#ifdef CONFIG_XENO_DRIVERS_ANALOGY
/**
 * @file
 * Analogy for Linux, driver facilities
 *
 * @note Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
 * @note Copyright (C) 2008 Alexis Berlemont <alexis.berlemont@free.fr>
 *
 * 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.
 */

/*!
 * @defgroup Analogy4Linux Analogy API.
 *
 * This is the API interface of Analogy (kernel layer and user
 * layer)
 *
 */

/*!
 * @ingroup Analogy4Linux
 * @defgroup driverfacilities Driver API.
 *
 * This is the API interface of Analogy provided to device drivers.
 *
 */

#include <linux/module.h>
#include <linux/fs.h>

#include <analogy/context.h>
#include <analogy/device.h>
#include <analogy/analogy_driver.h>

/* --- Driver section --- */

/*!
 * @ingroup driverfacilities
 * @defgroup 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(a4l_drv_t * 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(a4l_drv_t * drv);
EXPORT_SYMBOL_GPL(a4l_unregister_drv);

/** @} */

/* --- Subdevice section --- */

/*!
 * @ingroup driverfacilities
 * @defgroup 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(range_bipolar10);
EXPORT_SYMBOL_GPL(range_bipolar5);
EXPORT_SYMBOL_GPL(range_unipolar10);
EXPORT_SYMBOL_GPL(range_unipolar5);
EXPORT_SYMBOL_GPL(range_unknown);
EXPORT_SYMBOL_GPL(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.
 *
 */
a4l_subd_t * a4l_alloc_subd(int sizeof_priv,
				  void (*setup)(a4l_subd_t *));
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(a4l_dev_t *dev, a4l_subd_t *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.
 *
 */
a4l_subd_t *a4l_get_subd(a4l_dev_t *dev, int idx);
EXPORT_SYMBOL_GPL(a4l_get_subd);

/** @} */

/* --- Buffer section --- */

/*!
 * @ingroup driverfacilities
 * @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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *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(a4l_subd_t *subd);
EXPORT_SYMBOL_GPL(a4l_buf_count);

/**
 * @brief Get the current Analogy command descriptor
 *
 * @param[in] subd Subdevice descriptor structure
 *
 * @return the command descriptor.
 *
 */
a4l_cmd_t *a4l_get_cmd(a4l_subd_t * subd);
EXPORT_SYMBOL_GPL(a4l_get_cmd);


/**
 * @brief Get the channel index according to its type
 *
 * @param[in] subd Subdevice descriptor structure
 *
 * @return the channel index.
 *
 */
int a4l_get_chan(a4l_subd_t *subd);
EXPORT_SYMBOL_GPL(a4l_get_chan);

/** @} */

/* --- IRQ handling section --- */

/*!
 * @ingroup driverfacilities
 * @defgroup analogy_interrupt 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(a4l_dev_t * 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:
 * - A4L_IRQ_SHARED: enable IRQ-sharing with other drivers
 *   (Warning: real-time drivers and non-real-time drivers cannot
 *   share an interrupt line).
 * - A4L_IRQ_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(a4l_dev_t * 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(a4l_dev_t * dev, unsigned int irq);
EXPORT_SYMBOL_GPL(a4l_free_irq);

/** @} */

/* --- Misc section --- */

/*!
 * @ingroup driverfacilities
 * @defgroup misc Misc services
 * @{
 */

#ifdef DOXYGEN_CPP		/* Only used for doxygen doc generation */

/**
 * @brief Intialise and start an Analogy task
 *
 * This function belongs to a minimal set of task management services
 * (with a4l_task_destroy() and a4l_task_sleep()). Such features
 * are not critical for Analogy driver development.
 *
 * @param[in,out] task Task handle
 * @param[in] name Optional task name
 * @param[in] proc Procedure to be executed by the task
 * @param[in] arg Custom argument passed to @c proc() on entry
 * @param[in] priority Priority of the task
 *
 * @return 0 on success, otherwise negative error code
 *
 */
int a4l_task_init(a4l_task_t * task,
		  const char *name,
		  a4l_task_proc_t proc, void *arg, int priority);

/**
 * @brief Destroy an Analogy task
 *
 * This function belongs to a minimal set of task management services
 * (with a4l_task_init() and a4l_task_sleep()). Such features
 * are not critical for Analogy driver development.
 *
 * @param[in,out] task Task handle
 *
 */
void a4l_task_destroy(a4l_task_t * task);

/**
 * @brief Make the current Analogy task passively wait a defined delay
 *
 * This function belongs to a minimal set of task management services
 * (with a4l_task_init() and a4l_task_destroy()). Such features
 * are not critical for Analogy driver development.
 *
 * @param[in] nsdelay Amount of time expressed in nanoseconds during
 * which the Analogy task must be sleeping.
 *
 * @return 0 on success, otherwise negative error code
 *
 */
int a4l_task_sleep(unsigned long long nsdelay);

#endif /* DOXYGEN_CPP */

/**
 * @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);

/** @} */
#endif
