hc
2024-11-01 2f529f9b558ca1c1bd74be7437a84e4711743404
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/**
 * @file
 * 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>
 
/**
 * @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
 * 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.
 *
 * 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.
 */
#define UDD_IRQ_NONE     0
/**
 * IRQ directly managed from the mini-driver on top of the UDD
 * 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.
 */
#define UDD_IRQ_CUSTOM   (-1)
/** @} */
 
/**
 * @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[].
 */
#define UDD_MEM_NONE     0
/**
 * 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.
 */
#define UDD_MEM_PHYS     1
/**
 * Kernel logical memory region (e.g. kmalloc()). By default, the UDD
 * core maps such memory to a virtual user range by calling the
 * rtdm_mmap_kmem() service. */
#define UDD_MEM_LOGICAL  2
/**
 * 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.
 */
#define UDD_MEM_VIRTUAL  3
/** @} */
 
#define UDD_NR_MAPS  5
 
/**
 * @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.
 *
 * 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.
 *
 * For instance, declaring a region of physical memory at index #2 of
 * the memory region array could be done as follows:
 *
 * @code
 * static struct udd_device udd;
 *
 * 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
 *
 * This will make such region accessible via the mapper device using
 * the following sequence of code (see note), via the default
 * ->mmap() handler from the UDD core:
 *
 * @code
 * int fd, fdm;
 * void *p;
 *
 * 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);
 * @endcode
 *
 * 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).
 */
struct udd_memregion {
   /** Name of the region (informational but required) */
   const char *name;
   /**
    * 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.
    */
   size_t len;
   /**
    * Type of the region. See the discussion about @ref
    * udd_memory_types "UDD memory types" for possible values.
    */
   int type;
};
 
/**
 * @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().
 */
struct udd_device {
   /**
    * Name of the device managed by the mini-driver, appears
    * automatically in the /dev/rtdm namespace upon creation.
    */
   const char *device_name;
   /**
    * Additional device flags (e.g. RTDM_EXCLUSIVE)
    * RTDM_NAMED_DEVICE may be omitted).
    */
   int device_flags;
   /**
    * Subclass code of the device managed by the mini-driver (see
    * RTDM_SUBCLASS_xxx definition in the @ref rtdm_profiles
    * "Device Profiles"). The main class code is pre-set to
    * RTDM_CLASS_UDD.
    */
   int device_subclass;
   struct {
       /**
        * Ancillary open() handler, optional. See
        * rtdm_open_handler().
        *
        * @note This handler is called from secondary mode
        * only.
        */
       int (*open)(struct rtdm_fd *fd, int oflags);
       /**
        * Ancillary close() handler, optional. See
        * rtdm_close_handler().
        *
        * @note This handler is called from secondary mode
        * only.
        */
       void (*close)(struct rtdm_fd *fd);
       /**
        * Ancillary ioctl() handler, optional. See
        * rtdm_ioctl_handler().
        *
        * 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.
        */
       int (*ioctl)(struct rtdm_fd *fd,
                unsigned int request, void *arg);
       /**
        * 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.
        *
        * @note This handler is called from secondary mode
        * only.
        */
       int (*mmap)(struct rtdm_fd *fd,
               struct vm_area_struct *vma);
       /**
        * @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).
        *
        * @note This handler is called from primary mode
        * only.
        */
       int (*interrupt)(struct udd_device *udd);
   } ops;
   /**
    * 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".
    */
   int irq;
   /**
    * 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".
    */
   struct udd_memregion mem_regions[UDD_NR_MAPS];
   /** Reserved to the UDD core. */
   struct udd_reserved {
       rtdm_irq_t irqh;
       u32 event_count;
       struct udd_signotify signfy;
       struct rtdm_event pulse;
       struct rtdm_driver driver;
       struct rtdm_device device;
       struct rtdm_driver mapper_driver;
       struct udd_mapper {
           struct udd_device *udd;
           struct rtdm_device dev;
       } mapdev[UDD_NR_MAPS];
       char *mapper_name;
       int nr_maps;
   } __reserved;
};
 
int udd_register_device(struct udd_device *udd);
 
int udd_unregister_device(struct udd_device *udd);
 
struct udd_device *udd_get_device(struct rtdm_fd *fd);
 
void udd_notify_event(struct udd_device *udd);
 
void udd_enable_irq(struct udd_device *udd,
           rtdm_event_t *done);
 
void udd_disable_irq(struct udd_device *udd,
            rtdm_event_t *done);
 
/** @} */
 
#endif /* !_COBALT_RTDM_UDD_H */