/** * @file * Analogy for Linux, descriptor related features * * @note Copyright (C) 1997-2000 David A. Schleef * @note Copyright (C) 2008 Alexis Berlemont * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include "internal.h" #include "root_leaf.h" #ifndef DOXYGEN_CPP static void a4l_root_setup(a4l_root_t * rt, unsigned long gsize, unsigned long rsize) { /* Common init */ rt->offset = ((void *)rt + sizeof(a4l_root_t)); rt->gsize = gsize; rt->id = 0xffffffff; rt->nb_leaf = 0; rt->lfnxt = NULL; rt->lfchd = NULL; /* Specific init */ rt->data = rt->offset; rt->offset += rsize; } static int a4l_leaf_add(a4l_root_t * rt, a4l_leaf_t * lf, a4l_leaf_t ** lfchild, unsigned long lfsize) { /* Basic checking */ if (rt->offset + sizeof(a4l_leaf_t) + lfsize > ((void *)rt) + rt->gsize) return -ENOMEM; if (lf->nb_leaf != 0) { int i; a4l_leaf_t *lflst = lf->lfchd; for (i = 0; i < (lf->nb_leaf - 1); i++) { if (lflst == NULL) return -EFAULT; else lflst = lflst->lfnxt; } lflst->lfnxt = (a4l_leaf_t *) rt->offset; } else lf->lfchd = (a4l_leaf_t *) rt->offset; /* Inits parent leaf */ lf->nb_leaf++; *lfchild = (a4l_leaf_t *) rt->offset; rt->offset += sizeof(a4l_leaf_t); /* Performs child leaf init */ (*lfchild)->id = lf->nb_leaf - 1; (*lfchild)->nb_leaf = 0; (*lfchild)->lfnxt = NULL; (*lfchild)->lfchd = NULL; (*lfchild)->data = (void *)rt->offset; /* Performs root modifications */ rt->offset += lfsize; return 0; } static inline a4l_leaf_t *a4l_leaf_get(a4l_leaf_t * lf, unsigned int id) { int i; a4l_leaf_t *lflst = lf->lfchd; for (i = 0; i < id && lflst != NULL; i++) lflst = lflst->lfnxt; return lflst; } static int __a4l_get_sbsize(int fd, a4l_desc_t * dsc) { unsigned int i, j, nb_chan, nb_rng; int ret, res = dsc->nb_subd * (sizeof(a4l_sbinfo_t) + sizeof(a4l_leaf_t)); for (i = 0; i < dsc->nb_subd; i++) { if ((ret = a4l_sys_nbchaninfo(fd, i, &nb_chan)) < 0) return ret; res += nb_chan * (sizeof(a4l_chinfo_t) + sizeof(a4l_leaf_t)); for (j = 0; j < nb_chan; j++) { if ((ret = a4l_sys_nbrnginfo(fd, i, j, &nb_rng)) < 0) return ret; res += nb_rng * (sizeof(a4l_rnginfo_t) + sizeof(a4l_leaf_t)); } } return res; } static int __a4l_fill_desc(int fd, a4l_desc_t * dsc) { int ret; unsigned int i, j; a4l_sbinfo_t *sbinfo; a4l_root_t *rt = (a4l_root_t *) dsc->sbdata; a4l_root_setup(rt, dsc->sbsize, dsc->nb_subd * sizeof(a4l_sbinfo_t)); sbinfo = (a4l_sbinfo_t *) rt->data; if ((ret = a4l_sys_subdinfo(fd, sbinfo)) < 0) return ret; for (i = 0; i < dsc->nb_subd; i++) { a4l_leaf_t *lfs; a4l_chinfo_t *chinfo; /* For each subd, add a leaf for the channels even if the subd does not own any channel */ ret = a4l_leaf_add(rt, (a4l_leaf_t *) rt, &lfs, sbinfo[i].nb_chan * sizeof(a4l_chinfo_t)); if (ret < 0) return ret; /* If there is no channel, no need to go further */ if(sbinfo[i].nb_chan == 0) continue; chinfo = (a4l_chinfo_t *) lfs->data; if ((ret = a4l_sys_chaninfo(fd, i, chinfo)) < 0) return ret; for (j = 0; j < sbinfo[i].nb_chan; j++) { a4l_leaf_t *lfc; a4l_rnginfo_t *rnginfo; /* For each channel, add a leaf for the ranges even if no range descriptor is available */ ret = a4l_leaf_add(rt, lfs, &lfc, chinfo[j].nb_rng * sizeof(a4l_rnginfo_t)); if (ret < 0) return ret; /* If there is no range, no need to go further */ if(chinfo[j].nb_rng ==0) continue; rnginfo = (a4l_rnginfo_t *) lfc->data; if ((ret = a4l_sys_rnginfo(fd, i, j, rnginfo)) < 0) return ret; } } return 0; } #endif /* !DOXYGEN_CPP */ /*! * @ingroup analogy_lib_syscall * @defgroup analogy_lib_descriptor Descriptor Syscall API * @{ */ /** * @brief Get a descriptor on an attached device * * Once the device has been attached, the function a4l_get_desc() * retrieves various information on the device (subdevices, channels, * ranges, etc.). * The function a4l_get_desc() can be called twice: * - The first time, almost all the fields, except sbdata, are set * (board_name, nb_subd, idx_read_subd, idx_write_subd, magic, * sbsize); the last field , sbdata, is supposed to be a pointer on * a buffer, which size is defined by the field sbsize. * - The second time, the buffer pointed by sbdata is filled with data * about the subdevices, the channels and the ranges. * * Between the two calls, an allocation must be performed in order to * recover a buffer large enough to contain all the data. These data * are set up according a root-leaf organization (device -> subdevice * -> channel -> range). They cannot be accessed directly; specific * functions are available so as to retrieve them: * - a4l_get_subdinfo() to get some subdevice's characteristics. * - a4l_get_chaninfo() to get some channel's characteristics. * - a4l_get_rnginfo() to get some range's characteristics. * * @param[in] fd Driver file descriptor * @param[out] dsc Device descriptor * @param[in] pass Description level to retrieve: * - A4L_BSC_DESC to get the basic descriptor (notably the size of * the data buffer to allocate). * - A4L_CPLX_DESC to get the complex descriptor, the data buffer * is filled with characteristics about the subdevices, the channels * and the ranges. * * @return 0 on success. Otherwise: * * - -EINVAL is returned if some argument is missing or wrong; the * pass argument should be checked; check also the kernel log * ("dmesg") * - -EFAULT is returned if a user <-> kernel transfer went wrong * - -ENODEV is returned if the descriptor is incoherent (the device * may be unattached) * */ int a4l_sys_desc(int fd, a4l_desc_t * dsc, int pass) { int ret = 0; if (dsc == NULL || (pass != A4L_BSC_DESC && dsc->magic != MAGIC_BSC_DESC)) return -EINVAL; if (pass == A4L_BSC_DESC) { ret = a4l_sys_devinfo(fd, (a4l_dvinfo_t *) dsc); if (ret < 0) goto out_a4l_sys_desc; dsc->sbsize = __a4l_get_sbsize(fd, dsc); dsc->sbdata = NULL; dsc->magic = MAGIC_BSC_DESC; } else { if (!dsc->sbsize) { ret = -ENODEV; goto out_a4l_sys_desc; } ret = __a4l_fill_desc(fd, dsc); if (ret < 0) goto out_a4l_sys_desc; dsc->magic = MAGIC_CPLX_DESC; } out_a4l_sys_desc: return ret; } /*! @} Descriptor Syscall API */ /*! * @ingroup analogy_lib_level1 * @defgroup analogy_lib_descriptor1 Descriptor API * * This is the API interface used to fill and use Analogy device * descriptor structure * @{ */ /** * @brief Open an Analogy device and basically fill the descriptor * * @param[out] dsc Device descriptor * @param[in] fname Device name * * @return 0 on success. Otherwise: * * - -EINVAL is returned if some argument is missing or wrong; the * fname and the dsc pointer should be checked; check also the * kernel log ("dmesg") * - -EFAULT is returned if a user <-> kernel transfer went wrong * */ int a4l_open(a4l_desc_t *dsc, const char *fname) { int ret; /* Basic checking */ if (dsc == NULL) return -EINVAL; /* Initializes the descriptor */ memset(dsc, 0, sizeof(a4l_desc_t)); /* Opens the driver */ dsc->fd = a4l_sys_open(fname); if (dsc->fd < 0) return dsc->fd; /* Basically fills the descriptor */ ret = a4l_sys_desc(dsc->fd, dsc, A4L_BSC_DESC); if (ret < 0) { a4l_sys_close(dsc->fd); } return ret; } /** * @brief Close the Analogy device related with the descriptor * * The file descriptor is associated with a context. The context is * one of the enabler of asynchronous transfers. So, by closing the * file descriptor, the programer must keep in mind that the currently * occuring asynchronous transfer will cancelled. * * @param[in] dsc Device descriptor * * @return 0 on success. Otherwise: * * - -EINVAL is returned if some argument is missing or wrong; the * the dsc pointer should be checked; check also the kernel log * ("dmesg") * */ int a4l_close(a4l_desc_t * dsc) { /* Basic checking */ if (dsc == NULL) return -EINVAL; return a4l_sys_close(dsc->fd); } /** * @brief Fill the descriptor with subdevices, channels and ranges * data * * @param[in] dsc Device descriptor partly filled by a4l_open(). * * @return 0 on success. Otherwise: * * - -EINVAL is returned if some argument is missing or wrong; the * the dsc pointer should be checked; check also the kernel log * ("dmesg") * - -EFAULT is returned if a user <-> kernel transfer went wrong * - -ENODEV is returned if the descriptor is incoherent (the device * may be unattached) * */ int a4l_fill_desc(a4l_desc_t * dsc) { /* Basic checking */ if (dsc == NULL || dsc->fd < 0) return -EINVAL; /* Checks the descriptor has been basically filled */ if (dsc->magic != MAGIC_BSC_DESC) return -EINVAL; return a4l_sys_desc(dsc->fd, dsc, A4L_CPLX_DESC); } /** * @brief Get an information structure on a specified subdevice * * @param[in] dsc Device descriptor filled by a4l_open() and * a4l_fill_desc() * @param[in] subd Subdevice index * @param[out] info Subdevice information structure * * @return 0 on success. Otherwise: * * - -EINVAL is returned if some argument is missing or wrong; subd * and the dsc pointer should be checked; check also the kernel log * ("dmesg"); WARNING: a4l_fill_desc() should be called before * using a4l_get_subdinfo(). * */ int a4l_get_subdinfo(a4l_desc_t * dsc, unsigned int subd, a4l_sbinfo_t ** info) { a4l_leaf_t *tmp; if (dsc == NULL || info == NULL) return -EINVAL; if (dsc->magic != MAGIC_CPLX_DESC) return -EINVAL; if (subd >= dsc->nb_subd) return -EINVAL; tmp = (a4l_leaf_t *) dsc->sbdata; *info = &(((a4l_sbinfo_t *) tmp->data)[subd]); return 0; } /** * @brief Get an information structure on a specified channel * * @param[in] dsc Device descriptor filled by a4l_open() and * a4l_fill_desc() * @param[in] subd Subdevice index * @param[in] chan Channel index * @param[out] info Channel information structure * * @return 0 on success. Otherwise: * * - -EINVAL is returned if some argument is missing or wrong; subd, * chan and the dsc pointer should be checked; check also the * kernel log ("dmesg"); WARNING: a4l_fill_desc() should be called * before using a4l_get_chinfo() * */ int a4l_get_chinfo(a4l_desc_t * dsc, unsigned int subd, unsigned int chan, a4l_chinfo_t ** info) { a4l_leaf_t *tmp; if (dsc == NULL || info == NULL) return -EINVAL; if (dsc->magic != MAGIC_CPLX_DESC) return -EINVAL; if (subd >= dsc->nb_subd) return -EINVAL; tmp = (a4l_leaf_t *) dsc->sbdata; if (chan >= ((a4l_sbinfo_t *) tmp->data)[subd].nb_chan) return -EINVAL; tmp = a4l_leaf_get(tmp, subd); *info = &(((a4l_chinfo_t *) tmp->data)[chan]); return 0; } /** * @brief Get an information structure on a specified range * * @param[in] dsc Device descriptor filled by a4l_open() and * a4l_fill_desc() * @param[in] subd Subdevice index * @param[in] chan Channel index * @param[in] rng Range index * @param[out] info Range information structure * * @return 0 on success. Otherwise: * * - -EINVAL is returned if some argument is missing or wrong; subd, * chan, rng and the dsc pointer should be checked; check also the * kernel log ("dmesg"); WARNING: a4l_fill_desc() should be called * before using a4l_get_rnginfo() * */ int a4l_get_rnginfo(a4l_desc_t * dsc, unsigned int subd, unsigned int chan, unsigned int rng, a4l_rnginfo_t ** info) { a4l_leaf_t *tmp; if (dsc == NULL || info == NULL) return -EINVAL; if (dsc->magic != MAGIC_CPLX_DESC) return -EINVAL; if (subd >= dsc->nb_subd) return -EINVAL; tmp = (a4l_leaf_t *) dsc->sbdata; if (chan >= ((a4l_sbinfo_t *) tmp->data)[subd].nb_chan) return -EINVAL; tmp = a4l_leaf_get(tmp, subd); if (rng >= ((a4l_chinfo_t *) tmp->data)[chan].nb_rng) return -EINVAL; tmp = a4l_leaf_get(tmp, chan); *info = &(((a4l_rnginfo_t *) tmp->data)[rng]); return 0; } /*! @} Descriptor API */