udd.h 10.5 KB
Newer Older
1 2
/**
 * @file
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * Copyright (C) 2014 Philippe Gerum <rpm@xenomai.org>
 *
 * Xenomai is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#ifndef _COBALT_RTDM_UDD_H
#define _COBALT_RTDM_UDD_H

#include <linux/list.h>
#include <rtdm/driver.h>
#include <rtdm/uapi/udd.h>

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/**
 * @ingroup rtdm_profiles
 * @defgroup rtdm_udd User-space driver core
 *
 * This profile includes all mini-drivers sitting on top of the
 * User-space Device Driver framework (UDD). The generic UDD core
 * driver enables interrupt control and I/O memory access interfaces
 * to user-space device drivers, as defined by the mini-drivers when
 * registering.
 *
 * A mini-driver supplements the UDD core with ancillary functions for
 * dealing with @ref udd_memory_region "memory mappings" and @ref
 * udd_irq_handler "interrupt control" for a particular I/O
 * card/device.
 *
 * UDD-compliant mini-drivers only have to provide the basic support
 * for dealing with the interrupt sources present in the device, so
 * that most part of the device requests can be handled from a Xenomai
44 45 46
 * application running in user-space. Typically, a mini-driver would
 * handle the interrupt top-half, and the user-space application would
 * handle the bottom-half.
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
 *
 * This profile is reminiscent of the UIO framework available with the
 * Linux kernel, adapted to the dual kernel Cobalt environment.
 *
 * @{
 */

/**
 * @anchor udd_irq_special
 * Special IRQ values for udd_device.irq
 *
 * @{
 */
/**
 * No IRQ managed. Passing this code implicitly disables all
 * interrupt-related services, including control (disable/enable) and
 * notification.
 */
65
#define UDD_IRQ_NONE     0
66 67
/**
 * IRQ directly managed from the mini-driver on top of the UDD
68 69 70
 * core. The mini-driver is in charge of attaching the handler(s) to
 * the IRQ(s) it manages, notifying the Cobalt threads waiting for IRQ
 * events by calling the udd_notify_event() service.
71
 */
72
#define UDD_IRQ_CUSTOM   (-1)
73
/** @} */
74

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
/**
 * @anchor udd_memory_types  @name Memory types for mapping
 * Types of memory for mapping
 *
 * The UDD core implements a default ->mmap() handler which first
 * attempts to hand over the request to the corresponding handler
 * defined by the mini-driver. If not present, the UDD core
 * establishes the mapping automatically, depending on the memory
 * type defined for the region.
 *
 * @{
 */
/**
 * No memory region. Use this type code to disable an entry in the
 * array of memory mappings, i.e. udd_device.mem_regions[].
 */
91
#define UDD_MEM_NONE     0
92 93 94 95 96
/**
 * Physical I/O memory region. By default, the UDD core maps such
 * memory to a virtual user range by calling the rtdm_mmap_iomem()
 * service.
 */
97
#define UDD_MEM_PHYS     1
98 99 100
/**
 * Kernel logical memory region (e.g. kmalloc()). By default, the UDD
 * core maps such memory to a virtual user range by calling the
101
 * rtdm_mmap_kmem() service. */
102
#define UDD_MEM_LOGICAL  2
103 104 105 106 107
/**
 * Virtual memory region with no direct physical mapping
 * (e.g. vmalloc()). By default, the UDD core maps such memory to a
 * virtual user range by calling the rtdm_mmap_vmem() service.
 */
108
#define UDD_MEM_VIRTUAL  3
109
/** @} */
110 111 112

#define UDD_NR_MAPS  5

113 114 115 116 117 118 119 120 121 122 123 124 125
/**
 * @anchor udd_memory_region
 * UDD memory region descriptor.
 *
 * This descriptor defines the characteristics of a memory region
 * declared to the UDD core by the mini-driver. All valid regions
 * should be declared in the udd_device.mem_regions[] array,
 * invalid/unassigned ones should bear the UDD_MEM_NONE type.
 *
 * The UDD core exposes each region via the mmap(2) interface to the
 * application. To this end, a companion mapper device is created
 * automatically when registering the mini-driver.
 *
126 127 128
 * The mapper device creates special files in the RTDM namespace for
 * reaching the individual regions, which the application can open
 * then map to its address space via the mmap(2) system call.
129 130
 *
 * For instance, declaring a region of physical memory at index #2 of
131
 * the memory region array could be done as follows:
132 133
 *
 * @code
134 135
 * static struct udd_device udd;
 *
136 137 138 139 140 141 142 143 144 145 146 147 148
 * static int foocard_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 * {
 *      udd.device_name = "foocard";
 *      ...
 *      udd.mem_regions[2].name = "ADC";
 *      udd.mem_regions[2].addr = pci_resource_start(dev, 1);
 *      udd.mem_regions[2].len = pci_resource_len(dev, 1);
 *      udd.mem_regions[2].type = UDD_MEM_PHYS;
 *      ...
 *      return udd_register_device(&udd);
 * }
 * @endcode
 *
149
 * This will make such region accessible via the mapper device using
150 151
 * the following sequence of code (see note), via the default
 * ->mmap() handler from the UDD core:
152 153 154 155 156
 *
 * @code
 * int fd, fdm;
 * void *p;
 *
157 158 159
 * fd = open("/dev/rtdm/foocard", O_RDWR);
 * fdm = open("/dev/rtdm/foocard,mapper2", O_RDWR);
 * p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fdm, 0);
160 161
 * @endcode
 *
162 163 164 165 166 167
 * if no valid region has been declared in the
 * udd_device.mem_regions[] array, no mapper device is created.
 *
 * @note The example code assumes that @ref cobalt_api POSIX symbol
 * wrapping is in effect, so that RTDM performs the memory mapping
 * operation (not the regular kernel).
168
 */
169
struct udd_memregion {
170
	/** Name of the region (informational but required) */
171
	const char *name;
172 173 174 175 176 177 178 179 180 181
	/**
	 * Start address of the region. This may be a physical or
	 * virtual address, depending on the @ref udd_memory_types
	 * "memory type".
	 */
	unsigned long addr;
	/**
	 * Length (in bytes) of the region. This value must be
	 * PAGE_SIZE aligned.
	 */
182
	size_t len;
183 184 185 186
	/**
	 * Type of the region. See the discussion about @ref
	 * udd_memory_types "UDD memory types" for possible values.
	 */
187 188 189
	int type;
};

190 191 192 193 194 195 196
/**
 * @anchor udd_device
 * UDD device descriptor.
 *
 * This descriptor defines the characteristics of a UDD-based
 * mini-driver when registering via a call to udd_register_device().
 */
197
struct udd_device {
198 199
	/**
	 * Name of the device managed by the mini-driver, appears
200
	 * automatically in the /dev/rtdm namespace upon creation.
201
	 */
202
	const char *device_name;
203
	/**
204 205
	 * Additional device flags (e.g. RTDM_EXCLUSIVE)
	 * RTDM_NAMED_DEVICE may be omitted).
206 207
	 */
	int device_flags;
208 209 210
	/**
	 * Subclass code of the device managed by the mini-driver (see
	 * RTDM_SUBCLASS_xxx definition in the @ref rtdm_profiles
211
	 * "Device Profiles"). The main class code is pre-set to
212 213
	 * RTDM_CLASS_UDD.
	 */
214 215
	int device_subclass;
	struct {
216 217 218
		/**
		 * Ancillary open() handler, optional. See
		 * rtdm_open_handler().
219 220 221
		 *
		 * @note This handler is called from secondary mode
		 * only.
222 223 224 225 226
		 */
		int (*open)(struct rtdm_fd *fd, int oflags);
		/**
		 * Ancillary close() handler, optional. See
		 * rtdm_close_handler().
227 228 229
		 *
		 * @note This handler is called from secondary mode
		 * only.
230 231 232 233 234
		 */
		void (*close)(struct rtdm_fd *fd);
		/**
		 * Ancillary ioctl() handler, optional. See
		 * rtdm_ioctl_handler().
235 236 237 238 239 240 241 242
		 *
		 * If this routine returns -ENOSYS, the default action
		 * implemented by the UDD core for the corresponding
		 * request will be applied, as if no ioctl handler had
		 * been defined.
		 *
		 * @note This handler is called from primary mode
		 * only.
243 244
		 */
		int (*ioctl)(struct rtdm_fd *fd,
245
			     unsigned int request, void *arg);
246 247 248 249 250 251 252 253 254 255
		/**
		 * Ancillary mmap() handler for the mapper device,
		 * optional. See rtdm_mmap_handler(). The mapper
		 * device operates on a valid region defined in the @a
		 * mem_regions[] array. A pointer to the region 
		 * can be obtained by a call to udd_get_region().
		 *
		 * If this handler is NULL, the UDD core establishes
		 * the mapping automatically, depending on the memory
		 * type defined for the region.
256 257 258
		 *
		 * @note This handler is called from secondary mode
		 * only.
259 260
		 */
		int (*mmap)(struct rtdm_fd *fd,
261
			    struct vm_area_struct *vma);
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
		/**
		 * @anchor udd_irq_handler
		 *
		 * Ancillary handler for receiving interrupts. This
		 * handler must be provided if the mini-driver hands
		 * over IRQ handling to the UDD core, by setting the
		 * @a irq field to a valid value, different from
		 * UDD_IRQ_CUSTOM and UDD_IRQ_NONE.
		 *
		 * The ->interrupt() handler shall return one of the
		 * following status codes:
		 *
		 * - RTDM_IRQ_HANDLED, if the mini-driver successfully
		 * handled the IRQ. This flag can be combined with
		 * RTDM_IRQ_DISABLE to prevent the Cobalt kernel from
		 * re-enabling the interrupt line upon return,
		 * otherwise it is re-enabled automatically.
		 *
		 * - RTDM_IRQ_NONE, if the interrupt does not match
		 * any IRQ the mini-driver can handle.
		 *
		 * Once the ->interrupt() handler has returned, the
		 * UDD core notifies user-space Cobalt threads waiting
		 * for IRQ events (if any).
286 287 288
		 *
		 * @note This handler is called from primary mode
		 * only.
289 290
		 */
		int (*interrupt)(struct udd_device *udd);
291
	} ops;
292 293 294 295 296 297
	/**
	 * IRQ number. If valid, the UDD core manages the
	 * corresponding interrupt line, installing a base handler.
	 * Otherwise, a special value can be passed for declaring
	 * @ref udd_irq_special "unmanaged IRQs".
	 */
298
	int irq;
299 300 301 302 303 304
	/**
	 * Array of memory regions defined by the device. The array
	 * can be sparse, with some entries bearing the UDD_MEM_NONE
	 * type interleaved with valid ones.  See the discussion about
	 * @ref udd_memory_region "UDD memory regions".
	 */
305
	struct udd_memregion mem_regions[UDD_NR_MAPS];
306
	/** Reserved to the UDD core. */
307 308 309 310 311
	struct udd_reserved {
		rtdm_irq_t irqh;
		atomic_t event;
		struct udd_signotify signfy;
		struct rtdm_event pulse;
312
		struct rtdm_driver driver;
313
		struct rtdm_device device;
314
		struct rtdm_driver mapper_driver;
315 316 317 318
		struct udd_mapper {
			struct udd_device *udd;
			struct rtdm_device dev;
		} mapdev[UDD_NR_MAPS];
319 320 321 322 323
		char *mapper_name;
		int nr_maps;
	} __reserved;
};

324
int udd_register_device(struct udd_device *udd);
325

326
int udd_unregister_device(struct udd_device *udd);
327

328 329
struct udd_device *udd_get_device(struct rtdm_fd *fd);

330 331
void udd_notify_event(struct udd_device *udd);

332 333
void udd_enable_irq(struct udd_device *udd,
		    rtdm_event_t *done);
334

335 336
void udd_disable_irq(struct udd_device *udd,
		     rtdm_event_t *done);
337

338 339
/** @} */

340
#endif /* !_COBALT_RTDM_UDD_H */