Module: xenomai-forge
Branch: next
Commit: 6d8886c30d4b981d9a84380e289daa6325865516
URL:    
http://git.xenomai.org/?p=xenomai-forge.git;a=commit;h=6d8886c30d4b981d9a84380e289daa6325865516

Author: Philippe Gerum <r...@xenomai.org>
Date:   Fri Aug 15 13:07:18 2014 +0200

drivers/udd: expose file descriptor to ancillary I/O handlers

The file descriptor may be needed for accessing context data, such as
the device minor information.

At this chance, the UDD core is also documented.

---

 include/cobalt/kernel/rtdm/udd.h |  245 ++++++++++++++++++++++++++++++++++++--
 include/rtdm/uapi/udd.h          |   60 +++++++++-
 kernel/cobalt/rtdm/device.c      |    4 +-
 kernel/drivers/udd/udd.c         |  159 ++++++++++++++++++++++---
 4 files changed, 436 insertions(+), 32 deletions(-)

diff --git a/include/cobalt/kernel/rtdm/udd.h b/include/cobalt/kernel/rtdm/udd.h
index 5aab27f..1222f9a 100644
--- a/include/cobalt/kernel/rtdm/udd.h
+++ b/include/cobalt/kernel/rtdm/udd.h
@@ -1,4 +1,5 @@
-/*
+/**
+ * @file
  * Copyright (C) 2014 Philippe Gerum <r...@xenomai.org>
  *
  * Xenomai is free software; you can redistribute it and/or
@@ -22,40 +23,262 @@
 #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.
+ *
+ * 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 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_kem() 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 to
+ * reach the individual regions, which the application can open, for
+ * mapping the corresponding region to their address space via the
+ * mmap(2) system call.
+ *
+ * For instance, declaring a region of physical memory at index #2 of
+ * the memory region array as follows:
+ *
+ * @code
+ * static int foocard_pci_probe(struct pci_dev *dev, const struct 
pci_device_id *id)
+ * {
+ *      struct udd_device udd;
+ *
+ *      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
+ *
+ * will make such region accessible via the mapper device using the
+ * following sequence of code, via the default ->mmap() handler from
+ * the UDD core:
+ *
+ * @code
+ * int fd, fdm;
+ * void *p;
+ *
+ * fd = open("/dev/foocard", O_RDWR);
+ * fdm = open("/dev/foocard,mapper@2", O_RDWR);
+ * p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, 0, fdm, 0);
+ * @endcode
+ *
+ * @note No mapper device is created unless a valid region has been
+ * declared in the udd_device.mem_regions[] array.
+ */
 struct udd_memregion {
+       /** Name of the region (informational but required) */
        const char *name;
-       phys_addr_t addr;
+       /**
+        * 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. */
        const char *device_name;
+       /**
+        * Textual description of the device managed by the
+        * mini-driver.
+        */
        const char *device_description;
+       /**
+        * 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 forced to
+        * RTDM_CLASS_UDD.
+        */
        int device_subclass;
+       /** @ref drv_versioning "Driver version. */
        int driver_version;
+       /** Driver author/provider (exposed via /proc/xenomai/rtdm) */
        const char *driver_author;
        struct {
-               int (*open)(struct udd_device *dev, int oflags);
-               void (*close)(struct udd_device *dev);
-               int (*ioctl)(struct udd_device *dev,
+               /**
+                * Ancillary open() handler, optional. See
+                * rtdm_open_handler().
+                */
+               int (*open)(struct rtdm_fd *fd, int oflags);
+               /**
+                * Ancillary close() handler, optional. See
+                * rtdm_close_handler().
+                */
+               void (*close)(struct rtdm_fd *fd);
+               /**
+                * Ancillary ioctl() handler, optional. See
+                * rtdm_ioctl_handler().
+                */
+               int (*ioctl)(struct rtdm_fd *fd,
                             unsigned int request, void *arg);
-               int (*mmap)(struct udd_device *dev,
+               /**
+                * 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.
+                */
+               int (*mmap)(struct rtdm_fd *fd,
                            struct vm_area_struct *vma);
-               int (*interrupt)(struct udd_device *dev);
+               /**
+                * @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).
+                */
+               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;
                atomic_t event;
@@ -68,15 +291,19 @@ struct udd_device {
        } __reserved;
 };
 
-int udd_register_device(struct udd_device *dev);
+int udd_register_device(struct udd_device *udd);
 
-int udd_unregister_device(struct udd_device *dev,
+int udd_unregister_device(struct udd_device *udd,
                          unsigned int poll_delay);
 
+struct udd_device *udd_get_device(struct rtdm_fd *fd);
+
 void udd_notify_event(struct udd_device *udd);
 
 void udd_post_irq_enable(int irq);
 
 void udd_post_irq_disable(int irq);
 
+/** @} */
+
 #endif /* !_COBALT_RTDM_UDD_H */
diff --git a/include/rtdm/uapi/udd.h b/include/rtdm/uapi/udd.h
index d5bafdd..210f0ce 100644
--- a/include/rtdm/uapi/udd.h
+++ b/include/rtdm/uapi/udd.h
@@ -1,7 +1,8 @@
-/*
+/**
+ * @file
  * This file is part of the Xenomai project.
  *
- * Copyright (C) 2014 Philippe Gerum <r...@xenomai.org>
+ * @author Copyright (C) 2014 Philippe Gerum <r...@xenomai.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,13 +21,68 @@
 #ifndef _RTDM_UAPI_UDD_H
 #define _RTDM_UAPI_UDD_H
 
+/**
+ * @addtogroup rtdm_udd
+ *
+ * @{
+ */
+
+/**
+ * @anchor udd_signotify
+ * @brief UDD event notification descriptor
+ *
+ * This structure shall be used to pass the information required to
+ * enable/disable the notification by signal upon interrupt receipt.
+ *
+ * If PID is zero or negative, the notification is disabled.
+ * Otherwise, the Cobalt thread whose PID is given will receive the
+ * Cobalt signal also mentioned, along with the count of interrupts at
+ * the time of the receipt stored in siginfo.si_int. A Cobalt thread
+ * must explicitly wait for notifications using the sigwaitinfo() or
+ * sigtimedwait() services (no asynchronous mode available).
+ */
 struct udd_signotify {
+       /**
+        * PID of the Cobalt thread to notify upon interrupt
+        * receipt. If @a pid is zero or negative, the notification is
+        * disabled.
+        */
        pid_t pid;
+       /**
+        * Signal number to send to PID for notifying, which must be
+        * in the range [SIGRTMIN .. SIGRTMAX] inclusive. This value
+        * is not considered if @a pid is zero or negative.
+        */
        int sig;
 };
 
+/**
+ * @anchor udd_ioctl_codes @name UDD_IOCTL
+ * IOCTL requests
+ *
+ * @{
+ */
+
+/**
+ * Enable the interrupt line. The UDD-class mini-driver in kernel
+ * space should act upon this request appropriately when received via
+ * its ->ioctl() handler.
+ */
 #define UDD_RTIOC_IRQEN                _IO(RTDM_CLASS_UDD, 0)
+/**
+ * Disable the interrupt line. The UDD-class mini-driver in kernel
+ * should act upon this request appropriately when received via its
+ * ->ioctl() handler.
+ */
 #define UDD_RTIOC_IRQDIS       _IO(RTDM_CLASS_UDD, 1)
+/**
+ * Enable/Disable signal notification upon interrupt event. A valid
+ * @ref udd_signotify "notification descriptor" must be passed along
+ * with this request, which is handled by the UDD core directly.
+ */
 #define UDD_RTIOC_IRQSIG       _IOW(RTDM_CLASS_UDD, 2, struct udd_signotify)
 
+/** @} */
+/** @} */
+
 #endif /* !_RTDM_UAPI_UDD_H */
diff --git a/kernel/cobalt/rtdm/device.c b/kernel/cobalt/rtdm/device.c
index 9c9ef5e..4c6f4c8 100644
--- a/kernel/cobalt/rtdm/device.c
+++ b/kernel/cobalt/rtdm/device.c
@@ -148,8 +148,8 @@ __rtdm_get_protocol_device(int protocol_family, int 
socket_type)
  * - -EINVAL is returned if the device structure contains invalid entries.
  * Check kernel log in this case.
  *
- * - -ENOMEM is returned if the context for an exclusive device cannot be
- * allocated.
+ * - -ENOMEM is returned if a memory allocation failed in the process
+ * of registering the device.
  *
  * - -EEXIST is returned if the specified device name of protocol ID is
  * already in use.
diff --git a/kernel/drivers/udd/udd.c b/kernel/drivers/udd/udd.c
index 332bd5a..37b4db9 100644
--- a/kernel/drivers/udd/udd.c
+++ b/kernel/drivers/udd/udd.c
@@ -37,7 +37,7 @@ static int udd_open(struct rtdm_fd *fd, int oflags)
 
        udd = container_of(rtdm_fd_device(fd), struct udd_device, 
__reserved.device);
        if (udd->ops.open) {
-               ret = udd->ops.open(udd, oflags);
+               ret = udd->ops.open(fd, oflags);
                if (ret)
                        return ret;
        }
@@ -54,7 +54,7 @@ static void udd_close(struct rtdm_fd *fd)
 
        udd = container_of(rtdm_fd_device(fd), struct udd_device, 
__reserved.device);
        if (udd->ops.close)
-               udd->ops.close(udd);
+               udd->ops.close(fd);
 }
 
 static int udd_ioctl_rt(struct rtdm_fd *fd,
@@ -67,7 +67,7 @@ static int udd_ioctl_rt(struct rtdm_fd *fd,
 
        udd = container_of(rtdm_fd_device(fd), struct udd_device, 
__reserved.device);
        if (udd->ops.ioctl) {
-               ret = udd->ops.ioctl(udd, request, arg);
+               ret = udd->ops.ioctl(fd, request, arg);
                if (ret != -ENOSYS)
                        return ret;
        }
@@ -230,7 +230,7 @@ static int mapper_mmap(struct rtdm_fd *fd, struct 
vm_area_struct *vma)
        udd = container_of(rtdm_fd_device(fd), struct udd_device, 
__reserved.mapper);
        if (udd->ops.mmap)
                /* Offload to client driver if handler is present. */
-               return udd->ops.mmap(udd, vma);
+               return udd->ops.mmap(fd, vma);
 
        /* Otherwise DIY using the RTDM helpers. */
 
@@ -257,21 +257,6 @@ static int mapper_mmap(struct rtdm_fd *fd, struct 
vm_area_struct *vma)
        return ret;
 }
 
-void udd_notify_event(struct udd_device *udd)
-{
-       struct udd_reserved *ur = &udd->__reserved;
-       union sigval sival;
-
-       atomic_inc(&ur->event);
-       rtdm_event_signal(&ur->pulse);
-
-       if (ur->signfy.pid > 0) {
-               sival.sival_int = atomic_read(&ur->event);
-               cobalt_sigqueue(ur->signfy.pid, ur->signfy.sig, &sival);
-       }
-}
-EXPORT_SYMBOL_GPL(udd_notify_event);
-
 static inline int check_memregion(struct udd_device *udd,
                                  struct udd_memregion *rn)
 {
@@ -323,6 +308,32 @@ static inline int register_mapper(struct udd_device *udd)
        return rtdm_dev_register(dev);
 }
 
+/**
+ * @brief Register a UDD device
+ *
+ * This routine registers a mini-driver at the UDD core.
+ *
+ * @param udd The @ref udd_device "UDD device descriptor" which should
+ * describe the new device properties.
+ *
+ * @return Zero is returned upon success, otherwise a negative error
+ * code is received, from the set of error codes defined by
+ * rtdm_dev_register(). In addition, the following error codes can be
+ * returned:
+ *
+ * - -EINVAL, some of the memory regions declared in the
+ *   udd_device.mem_regions[] array have invalid properties, i.e. bad
+ *   type, NULL name, zero length or address. Any undeclared region
+ *   entry from the array must bear the UDD_MEM_NONE type.
+ *
+ * - -EINVAL, if udd_device.irq is different from UDD_IRQ_CUSTOM and
+ * UDD_IRQ_NONE but invalid, causing rtdm_irq_request() to fail.
+ *
+ * - -ENXIO can be received if this service is called while the Cobalt
+ * kernel is disabled.
+ *
+ * @coretags{secondary-only}
+ */
 int udd_register_device(struct udd_device *udd)
 {
        struct rtdm_device *dev = &udd->__reserved.device;
@@ -397,6 +408,23 @@ fail_irq_request:
 }
 EXPORT_SYMBOL_GPL(udd_register_device);
 
+/**
+ * @brief Unregister a UDD device
+ *
+ * This routine unregisters a mini-driver from the UDD core.
+ *
+ * @param udd The UDD device descriptor
+ *
+ * @param poll_delay Polling delay in milliseconds to check repeatedly
+ * for open instances of @a udd, or 0 for non-blocking mode.
+ *
+ * @return Zero is returned upon success, otherwise a negative error
+ * code is received, from the set of error codes defined by
+ * rtdm_dev_unregister(). In addition, -ENXIO can be received if this
+ * service is called while the Cobalt kernel is disabled.
+ *
+ * @coretags{secondary-only}
+ */
 int udd_unregister_device(struct udd_device *udd,
                          unsigned int poll_delay)
 {
@@ -419,6 +447,42 @@ int udd_unregister_device(struct udd_device *udd,
 }
 EXPORT_SYMBOL_GPL(udd_unregister_device);
 
+/**
+ * @brief Notify an IRQ event for an unmanaged interrupt
+ *
+ * When the UDD core shall hand over the interrupt management for a
+ * device to the mini-driver (see UDD_IRQ_CUSTOM), the latter should
+ * notify the UDD core when IRQ events are received by calling this
+ * service.
+ *
+ * As a result, the UDD core wakes up any Cobalt thread waiting for
+ * interrupts on the device via a read(2) or select(2) call.
+ *
+ * @param udd The UDD device descriptor receiving the IRQ.
+ *
+ * @coretags{coreirq-only}
+ *
+ * @note In case the ref udd_irq_handler "IRQ handler" from the
+ * mini-driver requested the UDD core not to re-enable the interrupt
+ * line, the application may later request the unmasking by issuing
+ * the UDD_RTIOC_IRQEN ioctl(2) command. Writing a non-zero integer to
+ * the device via the write(2) system call has the same effect.
+ */
+void udd_notify_event(struct udd_device *udd)
+{
+       struct udd_reserved *ur = &udd->__reserved;
+       union sigval sival;
+
+       atomic_inc(&ur->event);
+       rtdm_event_signal(&ur->pulse);
+
+       if (ur->signfy.pid > 0) {
+               sival.sival_int = atomic_read(&ur->event);
+               cobalt_sigqueue(ur->signfy.pid, ur->signfy.sig, &sival);
+       }
+}
+EXPORT_SYMBOL_GPL(udd_notify_event);
+
 struct irqswitch_work {
        struct ipipe_work_header work; /* Must be first. */
        int irq;
@@ -460,16 +524,73 @@ static void switch_irq_line(int irq, int enable)
        ipipe_post_work_root(&switchwork, work);
 }
 
+/**
+ * @brief Post a request for enabling an IRQ line
+ *
+ * This service issues a request to the regular kernel for enabling
+ * the IRQ line mentioned. If the caller runs in primary mode, the
+ * request is scheduled but deferred until the current CPU leaves the
+ * real-time domain. Otherwise, the request is immediately handled.
+ *
+ * @param irq The IRQ line to enable.
+ *
+ * @coretags{unrestricted}
+ *
+ * @note The deferral is required as some interrupt management code
+ * involved in enabling interrupt lines may not be safely executed
+ * from primary mode.
+ */
 void udd_post_irq_enable(int irq)
 {
        switch_irq_line(irq, 1);
 }
 EXPORT_SYMBOL_GPL(udd_post_irq_enable);
 
+/**
+ * @brief Post a request for disabling an IRQ line
+ *
+ * This service issues a request to the regular kernel for disabling
+ * the IRQ line mentioned. If the caller runs in primary mode, the
+ * request is scheduled but deferred until the current CPU leaves the
+ * real-time domain. Otherwise, the request is immediately handled.
+ *
+ * @param irq The IRQ line to enable.
+ *
+ * @coretags{unrestricted}
+ *
+ * @note The deferral is required as some interrupt management code
+ * involved in disable interrupt lines may not be safely executed from
+ * primary mode.
+ */
 void udd_post_irq_disable(int irq)
 {
        switch_irq_line(irq, 0);
 }
 EXPORT_SYMBOL_GPL(udd_post_irq_disable);
 
+/**
+ * @brief Retrieve the UDD device descriptor from a file descriptor
+ *
+ * @param fd The file descriptor received by an ancillary I/O handler
+ * from a mini-driver based on the UDD core.
+ *
+ * @return A pointer to the UDD device to which @a fd refers to.
+ *
+ * @note This service is intended for use by mini-drivers based on the
+ * UDD core exclusively. Passing file descriptors referring to other
+ * RTDM devices will certainly lead to invalid results.
+ *
+ * @coretags{mode-unrestricted}
+ */
+struct udd_device *udd_get_device(struct rtdm_fd *fd)
+{
+       struct rtdm_device *dev = rtdm_fd_device(fd);
+
+       if (dev->device_class == RTDM_CLASS_MEMORY)
+               return container_of(dev, struct udd_device, __reserved.mapper);
+
+       return container_of(dev, struct udd_device, __reserved.device);
+}
+EXPORT_SYMBOL_GPL(udd_get_device);
+
 MODULE_LICENSE("GPL");


_______________________________________________
Xenomai-git mailing list
Xenomai-git@xenomai.org
http://www.xenomai.org/mailman/listinfo/xenomai-git

Reply via email to