/* * Analogy for Linux, driver facilities * * Copyright (C) 1997-2000 David A. Schleef * Copyright (C) 2008 Alexis Berlemont * * 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. * * Xenomai 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 Xenomai; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include /** * @ingroup cobalt * @defgroup analogy Analogy framework * A RTDM-based interface for implementing DAQ card drivers */ /** * @ingroup analogy * @defgroup analogy_driver_facilities Driver API * Programming interface provided to DAQ card drivers */ /* --- Driver section --- */ /** * @ingroup analogy_driver_facilities * @defgroup analogy_driver Driver management services * * Analogy driver registration / unregistration * * In a common Linux char driver, the developer has to register a fops * structure filled with callbacks for read / write / mmap / ioctl * operations. * * Analogy drivers do not have to implement read / write / mmap / * ioctl functions, these procedures are implemented in the Analogy * generic layer. Then, the transfers between user-space and * kernel-space are already managed. Analogy drivers work with commands * and instructions which are some kind of more dedicated read / write * operations. And, instead of registering a fops structure, a Analogy * driver must register some a4l_driver structure. * * @{ */ /** * @brief Register an Analogy driver * * After initialising a driver structure, the driver must be made * available so as to be attached. * * @param[in] drv Driver descriptor structure * * @return 0 on success, otherwise negative error code. * */ int a4l_register_drv(struct a4l_driver * drv); EXPORT_SYMBOL_GPL(a4l_register_drv); /** * @brief Unregister an Analogy driver * * This function removes the driver descriptor from the Analogy driver * list. The driver cannot be attached anymore. * * @param[in] drv Driver descriptor structure * * @return 0 on success, otherwise negative error code. * */ int a4l_unregister_drv(struct a4l_driver * drv); EXPORT_SYMBOL_GPL(a4l_unregister_drv); /** @} */ /* --- Subdevice section --- */ /** * @ingroup analogy_driver_facilities * @defgroup analogy_subdevice Subdevice management services * * Subdevice declaration in a driver * * The subdevice structure is the most complex one in the Analogy * driver layer. It contains some description fields to fill and some * callbacks to declare. * * The description fields are: * - flags: to define the subdevice type and its capabilities; * - chan_desc: to describe the channels which compose the subdevice; * - rng_desc: to declare the usable ranges; * * The functions callbakcs are: * - do_cmd() and do_cmdtest(): to performe asynchronous acquisitions * thanks to commands; * - cancel(): to abort a working asynchronous acquisition; * - munge(): to apply modifications on the data freshly acquired * during an asynchronous transfer. Warning: using this feature with * can significantly reduce the performances (if the munge operation * is complex, it will trigger high CPU charge and if the * acquisition device is DMA capable, many cache-misses and * cache-replaces will occur (the benefits of the DMA controller * will vanish); * - trigger(): optionnaly to launch an asynchronous acquisition; * - insn_read(), insn_write(), insn_bits(), insn_config(): to perform * synchronous acquisition operations. * * Once the subdevice is filled, it must be inserted into the driver * structure thanks to a4l_add_subd(). * * @{ */ EXPORT_SYMBOL_GPL(a4l_range_bipolar10); EXPORT_SYMBOL_GPL(a4l_range_bipolar5); EXPORT_SYMBOL_GPL(a4l_range_unipolar10); EXPORT_SYMBOL_GPL(a4l_range_unipolar5); EXPORT_SYMBOL_GPL(a4l_range_unknown); EXPORT_SYMBOL_GPL(a4l_range_fake); /** * @brief Allocate a subdevice descriptor * * This is a helper function so as to get a suitable subdevice * descriptor * * @param[in] sizeof_priv Size of the subdevice's private data * @param[in] setup Setup function to be called after the allocation * * @return the index with which the subdevice has been registered, in * case of error a negative error code is returned. * */ struct a4l_subdevice * a4l_alloc_subd(int sizeof_priv, void (*setup)(struct a4l_subdevice *)); EXPORT_SYMBOL_GPL(a4l_alloc_subd); /** * @brief Add a subdevice to the driver descriptor * * Once the driver descriptor structure is initialized, the function * a4l_add_subd() must be used so to add some subdevices to the * driver. * * @param[in] dev Device descriptor structure * @param[in] subd Subdevice descriptor structure * * @return the index with which the subdevice has been registered, in * case of error a negative error code is returned. * */ int a4l_add_subd(struct a4l_device *dev, struct a4l_subdevice *subd); EXPORT_SYMBOL_GPL(a4l_add_subd); /** * @brief Get a pointer to the subdevice descriptor referenced by its * registration index * * This function is scarcely useful as all the drivers callbacks get * the related subdevice descriptor as first argument. * This function is not optimized, it goes through a linked list to * get the proper pointer. So it must not be used in real-time context * but at initialization / cleanup time (attach / detach). * * @param[in] dev Device descriptor structure * @param[in] idx Subdevice index * * @return 0 on success, otherwise negative error code. * */ struct a4l_subdevice *a4l_get_subd(struct a4l_device *dev, int idx); EXPORT_SYMBOL_GPL(a4l_get_subd); /** @} */ /* --- Buffer section --- */ /** * @ingroup analogy_driver_facilities * @defgroup analogy_buffer Buffer management services * * Buffer management services * * The buffer is the key component of the Analogy infrastructure. It * manages transfers between the user-space and the Analogy drivers * thanks to generic functions which are described hereafter. Thanks * to the buffer subsystem, the driver developer does not have to care * about the way the user program retrieves or sends data. * * To write a classical char driver, the developer has to fill a fops * structure so as to provide transfer operations to the user program * (read, write, ioctl and mmap if need be). * * The Analogy infrastructure manages the whole interface with the * userspace; the common read, write, mmap, etc. callbacks are generic * Analogy functions. These functions manage (and perform, if need be) * tranfers between the user-space and an asynchronous buffer thanks * to lockless mechanisms. * * Consequently, the developer has to use the proper buffer functions * in order to write / read acquired data into / from the asynchronous * buffer. * * Here are listed the functions: * - a4l_buf_prepare_(abs)put() and a4l_buf_commit_(abs)put() * - a4l_buf_prepare_(abs)get() and a4l_buf_commit_(abs)get() * - a4l_buf_put() * - a4l_buf_get() * - a4l_buf_evt(). * * The functions count might seem high; however, the developer needs a * few of them to write a driver. Having so many functions enables to * manage any transfer cases: * - If some DMA controller is available, there is no need to make the * driver copy the acquired data into the asynchronous buffer, the * DMA controller must directly trigger DMA shots into / from the * buffer. In that case, a function a4l_buf_prepare_*() must be used * so as to set up the DMA transfer and a function * a4l_buf_commit_*() has to be called to complete the transfer(). * - For DMA controllers which need to work with global counter (the * transfered data count since the beginning of the acquisition), * the functions a4l_buf_*_abs_*() have been made available. * - If no DMA controller is available, the driver has to perform the * copy between the hardware component and the asynchronous * buffer. In such cases, the functions a4l_buf_get() and * a4l_buf_put() are useful. * * @{ */ /** * @brief Update the absolute count of data sent from the device to * the buffer since the start of the acquisition and after the next * DMA shot * * The functions a4l_buf_prepare_(abs)put(), * a4l_buf_commit_(abs)put(), a4l_buf_prepare_(abs)get() and * a4l_buf_commit_(absg)et() have been made available for DMA * transfers. In such situations, no data copy is needed between the * Analogy buffer and the device as some DMA controller is in charge * of performing data shots from / to the Analogy buffer. However, some * pointers still have to be updated so as to monitor the tranfers. * * @param[in] subd Subdevice descriptor structure * @param[in] count The data count to be transferred during the next * DMA shot plus the data count which have been copied since the start * of the acquisition * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_prepare_absput(struct a4l_subdevice *subd, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_prepare_absput); /** * @brief Set the absolute count of data which was sent from the * device to the buffer since the start of the acquisition and until * the last DMA shot * * The functions a4l_buf_prepare_(abs)put(), * a4l_buf_commit_(abs)put(), a4l_buf_prepare_(abs)get() and * a4l_buf_commit_(abs)get() have been made available for DMA * transfers. In such situations, no data copy is needed between the * Analogy buffer and the device as some DMA controller is in charge * of performing data shots from / to the Analogy buffer. However, * some pointers still have to be updated so as to monitor the * tranfers. * * @param[in] subd Subdevice descriptor structure * @param[in] count The data count transferred to the buffer during * the last DMA shot plus the data count which have been sent / * retrieved since the beginning of the acquisition * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_commit_absput(struct a4l_subdevice *subd, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_commit_absput); /** * @brief Set the count of data which is to be sent to the buffer at * the next DMA shot * * The functions a4l_buf_prepare_(abs)put(), * a4l_buf_commit_(abs)put(), a4l_buf_prepare_(abs)get() and * a4l_buf_commit_(abs)get() have been made available for DMA * transfers. In such situations, no data copy is needed between the * Analogy buffer and the device as some DMA controller is in charge * of performing data shots from / to the Analogy buffer. However, * some pointers still have to be updated so as to monitor the * tranfers. * * @param[in] subd Subdevice descriptor structure * @param[in] count The data count to be transferred * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_prepare_put(struct a4l_subdevice *subd, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_prepare_put); /** * @brief Set the count of data sent to the buffer during the last * completed DMA shots * * The functions a4l_buf_prepare_(abs)put(), * a4l_buf_commit_(abs)put(), a4l_buf_prepare_(abs)get() and * a4l_buf_commit_(abs)get() have been made available for DMA * transfers. In such situations, no data copy is needed between the * Analogy buffer and the device as some DMA controller is in charge * of performing data shots from / to the Analogy buffer. However, * some pointers still have to be updated so as to monitor the * tranfers. * * @param[in] subd Subdevice descriptor structure * @param[in] count The amount of data transferred * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_commit_put(struct a4l_subdevice *subd, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_commit_put); /** * @brief Copy some data from the device driver to the buffer * * The function a4l_buf_put() must copy data coming from some * acquisition device to the Analogy buffer. This ring-buffer is an * intermediate area between the device driver and the user-space * program, which is supposed to recover the acquired data. * * @param[in] subd Subdevice descriptor structure * @param[in] bufdata The data buffer to copy into the Analogy buffer * @param[in] count The amount of data to copy * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_put(struct a4l_subdevice *subd, void *bufdata, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_put); /** * @brief Update the absolute count of data sent from the buffer to * the device since the start of the acquisition and after the next * DMA shot * * The functions a4l_buf_prepare_(abs)put(), * a4l_buf_commit_(abs)put(), a4l_buf_prepare_(abs)get() and * a4l_buf_commit_(absg)et() have been made available for DMA * transfers. In such situations, no data copy is needed between the * Analogy buffer and the device as some DMA controller is in charge * of performing data shots from / to the Analogy buffer. However, * some pointers still have to be updated so as to monitor the * tranfers. * * @param[in] subd Subdevice descriptor structure * @param[in] count The data count to be transferred during the next * DMA shot plus the data count which have been copied since the start * of the acquisition * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_prepare_absget(struct a4l_subdevice *subd, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_prepare_absget); /** * @brief Set the absolute count of data which was sent from the * buffer to the device since the start of the acquisition and until * the last DMA shot * * The functions a4l_buf_prepare_(abs)put(), * a4l_buf_commit_(abs)put(), a4l_buf_prepare_(abs)get() and * a4l_buf_commit_(abs)get() have been made available for DMA * transfers. In such situations, no data copy is needed between the * Analogy buffer and the device as some DMA controller is in charge * of performing data shots from / to the Analogy buffer. However, * some pointers still have to be updated so as to monitor the * tranfers. * * @param[in] subd Subdevice descriptor structure * @param[in] count The data count transferred to the device during * the last DMA shot plus the data count which have been sent since * the beginning of the acquisition * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_commit_absget(struct a4l_subdevice *subd, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_commit_absget); /** * @brief Set the count of data which is to be sent from the buffer to * the device at the next DMA shot * * The functions a4l_buf_prepare_(abs)put(), * a4l_buf_commit_(abs)put(), a4l_buf_prepare_(abs)get() and * a4l_buf_commit_(abs)get() have been made available for DMA * transfers. In such situations, no data copy is needed between the * Analogy buffer and the device as some DMA controller is in charge * of performing data shots from / to the Analogy buffer. However, * some pointers still have to be updated so as to monitor the * tranfers. * * @param[in] subd Subdevice descriptor structure * @param[in] count The data count to be transferred * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_prepare_get(struct a4l_subdevice *subd, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_prepare_get); /** * @brief Set the count of data sent from the buffer to the device * during the last completed DMA shots * * The functions a4l_buf_prepare_(abs)put(), * a4l_buf_commit_(abs)put(), a4l_buf_prepare_(abs)get() and * a4l_buf_commit_(abs)get() have been made available for DMA * transfers. In such situations, no data copy is needed between the * Analogy buffer and the device as some DMA controller is in charge * of performing data shots from / to the Analogy buffer. However, * some pointers still have to be updated so as to monitor the * tranfers. * * @param[in] subd Subdevice descriptor structure * @param[in] count The amount of data transferred * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_commit_get(struct a4l_subdevice *subd, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_commit_get); /** * @brief Copy some data from the buffer to the device driver * * The function a4l_buf_get() must copy data coming from the Analogy * buffer to some acquisition device. This ring-buffer is an * intermediate area between the device driver and the user-space * program, which is supposed to provide the data to send to the * device. * * @param[in] subd Subdevice descriptor structure * @param[in] bufdata The data buffer to copy into the Analogy buffer * @param[in] count The amount of data to copy * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_get(struct a4l_subdevice *subd, void *bufdata, unsigned long count); EXPORT_SYMBOL_GPL(a4l_buf_get); /** * @brief Signal some event(s) to a user-space program involved in * some read / write operation * * The function a4l_buf_evt() is useful in many cases: * - To wake-up a process waiting for some data to read. * - To wake-up a process waiting for some data to write. * - To notify the user-process an error has occured during the * acquistion. * * @param[in] subd Subdevice descriptor structure * @param[in] evts Some specific event to notify: * - A4L_BUF_ERROR to indicate some error has occured during the * transfer * - A4L_BUF_EOA to indicate the acquisition is complete (this * event is automatically set, it should not be used). * * @return 0 on success, otherwise negative error code. * */ int a4l_buf_evt(struct a4l_subdevice *subd, unsigned long evts); EXPORT_SYMBOL_GPL(a4l_buf_evt); /** * @brief Get the data amount available in the Analogy buffer * * @param[in] subd Subdevice descriptor structure * * @return the amount of data available in the Analogy buffer. * */ unsigned long a4l_buf_count(struct a4l_subdevice *subd); EXPORT_SYMBOL_GPL(a4l_buf_count); #ifdef DOXYGEN_CPP /* Only used for doxygen doc generation */ /** * @brief Get the current Analogy command descriptor * * @param[in] subd Subdevice descriptor structure * * @return the command descriptor. * */ struct a4l_cmd_desc *a4l_get_cmd(struct a4l_subdevice * subd); #endif /* DOXYGEN_CPP */ /** * @brief Get the channel index according to its type * * @param[in] subd Subdevice descriptor structure * * @return the channel index. * */ int a4l_get_chan(struct a4l_subdevice *subd); EXPORT_SYMBOL_GPL(a4l_get_chan); /** @} */ /* --- IRQ handling section --- */ /** * @ingroup analogy_driver_facilities * @defgroup analogy_irq Interrupt management services * @{ */ /** * @brief Get the interrupt number in use for a specific device * * @param[in] dev Device descriptor structure * * @return the line number used or A4L_IRQ_UNUSED if no interrupt * is registered. * */ unsigned int a4l_get_irq(struct a4l_device * dev); EXPORT_SYMBOL_GPL(a4l_get_irq); /** * @brief Register an interrupt handler for a specific device * * @param[in] dev Device descriptor structure * @param[in] irq Line number of the addressed IRQ * @param[in] handler Interrupt handler * @param[in] flags Registration flags: * - RTDM_IRQTYPE_SHARED: enable IRQ-sharing with other drivers * (Warning: real-time drivers and non-real-time drivers cannot * share an interrupt line). * - RTDM_IRQTYPE_EDGE: mark IRQ as edge-triggered (Warning: this flag * is meaningless in RTDM-less context). * - A4L_IRQ_DISABLED: keep IRQ disabled when calling the action * handler (Warning: this flag is ignored in RTDM-enabled * configuration). * @param[in] cookie Pointer to be passed to the interrupt handler on * invocation * * @return 0 on success, otherwise negative error code. * */ int a4l_request_irq(struct a4l_device * dev, unsigned int irq, a4l_irq_hdlr_t handler, unsigned long flags, void *cookie); EXPORT_SYMBOL_GPL(a4l_request_irq); /** * @brief Release an interrupt handler for a specific device * * @param[in] dev Device descriptor structure * @param[in] irq Line number of the addressed IRQ * * @return 0 on success, otherwise negative error code. * */ int a4l_free_irq(struct a4l_device * dev, unsigned int irq); EXPORT_SYMBOL_GPL(a4l_free_irq); /** @} */ /* --- Misc section --- */ /** * @ingroup analogy_driver_facilities * @defgroup analogy_misc Misc services * @{ */ /** * @brief Get the absolute time in nanoseconds * * @return the absolute time expressed in nanoseconds * */ unsigned long long a4l_get_time(void); EXPORT_SYMBOL_GPL(a4l_get_time); /** @} */