/* * Analogy for Linux, subdevice, channel and range related features * * 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 #include #include #include /* --- Common ranges declarations --- */ struct a4l_rngtab rng_bipolar10 = { 1, { RANGE_V(-10, 10), }}; struct a4l_rngdesc a4l_range_bipolar10 = RNG_GLOBAL(rng_bipolar10); struct a4l_rngtab rng_bipolar5 = { 1, { RANGE_V(-5, 5), }}; struct a4l_rngdesc a4l_range_bipolar5 = RNG_GLOBAL(rng_bipolar5); struct a4l_rngtab rng_unipolar10 = { 1, { RANGE_V(0, 10), }}; struct a4l_rngdesc a4l_range_unipolar10 = RNG_GLOBAL(rng_unipolar10); struct a4l_rngtab rng_unipolar5 = { 1, { RANGE_V(0, 5), }}; struct a4l_rngdesc a4l_range_unipolar5 = RNG_GLOBAL(rng_unipolar5); struct a4l_rngtab rng_unknown = { 1, { RANGE(0, 1), }}; struct a4l_rngdesc a4l_range_unknown = RNG_GLOBAL(rng_unknown); struct a4l_rngtab rng_fake = { 0, { RANGE(0, 0), }}; struct a4l_rngdesc a4l_range_fake = RNG_GLOBAL(rng_fake); /* --- Basic channel / range management functions --- */ struct a4l_channel *a4l_get_chfeat(struct a4l_subdevice *sb, int idx) { int i = (sb->chan_desc->mode != A4L_CHAN_GLOBAL_CHANDESC) ? idx : 0; return &(sb->chan_desc->chans[i]); } struct a4l_range *a4l_get_rngfeat(struct a4l_subdevice *sb, int chidx, int rngidx) { int i = (sb->rng_desc->mode != A4L_RNG_GLOBAL_RNGDESC) ? chidx : 0; return &(sb->rng_desc->rngtabs[i]->rngs[rngidx]); } int a4l_check_chanlist(struct a4l_subdevice *subd, unsigned char nb_chan, unsigned int *chans) { int i, j; if (nb_chan > subd->chan_desc->length) return -EINVAL; for (i = 0; i < nb_chan; i++) { j = (subd->chan_desc->mode != A4L_CHAN_GLOBAL_CHANDESC) ? i : 0; if (CR_CHAN(chans[i]) >= subd->chan_desc->length) { __a4l_err("a4l_check_chanlist: " "chan idx out_of range (%u>=%lu)\n", CR_CHAN(chans[i]), subd->chan_desc->length); return -EINVAL; } if (CR_AREF(chans[i]) != 0 && (CR_AREF(chans[i]) & subd->chan_desc->chans[j].flags) == 0) { __a4l_err("a4l_check_chanlist: " "bad channel type\n"); return -EINVAL; } } if (subd->rng_desc == NULL) return 0; for (i = 0; i < nb_chan; i++) { j = (subd->rng_desc->mode != A4L_RNG_GLOBAL_RNGDESC) ? i : 0; if (CR_RNG(chans[i]) > subd->rng_desc->rngtabs[j]->length) { __a4l_err("a4l_check_chanlist: " "rng idx out_of range (%u>=%u)\n", CR_RNG(chans[i]), subd->rng_desc->rngtabs[j]->length); return -EINVAL; } } return 0; } /* --- Upper layer functions --- */ struct a4l_subdevice * a4l_alloc_subd(int sizeof_priv, void (*setup)(struct a4l_subdevice *)) { struct a4l_subdevice *subd; subd = rtdm_malloc(sizeof(struct a4l_subdevice) + sizeof_priv); if(subd != NULL) { memset(subd, 0 , sizeof(struct a4l_subdevice) + sizeof_priv); if(setup != NULL) setup(subd); } return subd; } int a4l_add_subd(struct a4l_device * dev, struct a4l_subdevice * subd) { struct list_head *this; int i = 0; /* Basic checking */ if (dev == NULL || subd == NULL) return -EINVAL; list_add_tail(&subd->list, &dev->subdvsq); subd->dev = dev; list_for_each(this, &dev->subdvsq) { i++; } subd->idx = --i; return i; } struct a4l_subdevice *a4l_get_subd(struct a4l_device *dev, int idx) { int i = 0; struct a4l_subdevice *subd = NULL; struct list_head *this; /* This function is not optimized as we do not go through the transfer structure */ list_for_each(this, &dev->subdvsq) { if(idx == i++) subd = list_entry(this, struct a4l_subdevice, list); } return subd; } /* --- IOCTL / FOPS functions --- */ int a4l_ioctl_subdinfo(struct a4l_device_context * cxt, void *arg) { struct rtdm_fd *fd = rtdm_private_to_fd(cxt); struct a4l_device *dev = a4l_get_dev(cxt); int i, ret = 0; a4l_sbinfo_t *subd_info; /* Basic checking */ if (!test_bit(A4L_DEV_ATTACHED_NR, &dev->flags)) { __a4l_err("a4l_ioctl_subdinfo: unattached device\n"); return -EINVAL; } subd_info = rtdm_malloc(dev->transfer.nb_subd * sizeof(a4l_sbinfo_t)); if (subd_info == NULL) return -ENOMEM; for (i = 0; i < dev->transfer.nb_subd; i++) { subd_info[i].flags = dev->transfer.subds[i]->flags; subd_info[i].status = dev->transfer.subds[i]->status; subd_info[i].nb_chan = (dev->transfer.subds[i]->chan_desc != NULL) ? dev->transfer.subds[i]->chan_desc->length : 0; } if (rtdm_safe_copy_to_user(fd, arg, subd_info, dev->transfer.nb_subd * sizeof(a4l_sbinfo_t)) != 0) ret = -EFAULT; rtdm_free(subd_info); return ret; } int a4l_ioctl_nbchaninfo(struct a4l_device_context * cxt, void *arg) { struct rtdm_fd *fd = rtdm_private_to_fd(cxt); struct a4l_device *dev = a4l_get_dev(cxt); a4l_chinfo_arg_t inarg; /* Basic checking */ if (!dev->flags & A4L_DEV_ATTACHED_NR) { __a4l_err("a4l_ioctl_nbchaninfo: unattached device\n"); return -EINVAL; } if (rtdm_safe_copy_from_user(fd, &inarg, arg, sizeof(a4l_chinfo_arg_t)) != 0) return -EFAULT; if (inarg.idx_subd >= dev->transfer.nb_subd) { __a4l_err("a4l_ioctl_nbchaninfo: subdevice index " "out of range\n"); return -EINVAL; } if(dev->transfer.subds[inarg.idx_subd]->chan_desc == NULL) inarg.info = (void *)0; else inarg.info = (void *)(unsigned long) dev->transfer.subds[inarg.idx_subd]->chan_desc->length; if (rtdm_safe_copy_to_user(fd, arg, &inarg, sizeof(a4l_chinfo_arg_t)) != 0) return -EFAULT; return 0; } int a4l_ioctl_chaninfo(struct a4l_device_context * cxt, void *arg) { struct rtdm_fd *fd = rtdm_private_to_fd(cxt); int i, ret = 0; struct a4l_device *dev = a4l_get_dev(cxt); a4l_chinfo_t *chan_info; a4l_chinfo_arg_t inarg; struct a4l_channels_desc *chan_desc; struct a4l_rngdesc *rng_desc; /* Basic checking */ if (!test_bit(A4L_DEV_ATTACHED_NR, &dev->flags)) { __a4l_err("a4l_ioctl_chaninfo: unattached device\n"); return -EINVAL; } if (rtdm_safe_copy_from_user(fd, &inarg, arg, sizeof(a4l_chinfo_arg_t)) != 0) return -EFAULT; if (inarg.idx_subd >= dev->transfer.nb_subd) { __a4l_err("a4l_ioctl_chaninfo: bad subdevice index\n"); return -EINVAL; } chan_desc = dev->transfer.subds[inarg.idx_subd]->chan_desc; rng_desc = dev->transfer.subds[inarg.idx_subd]->rng_desc; if (chan_desc == NULL) { __a4l_err("a4l_ioctl_chaninfo: no channel descriptor " "for subdevice %d\n", inarg.idx_subd); return -EINVAL; } if(rng_desc == NULL) rng_desc = &a4l_range_fake; chan_info = rtdm_malloc(chan_desc->length * sizeof(a4l_chinfo_t)); if (chan_info == NULL) return -ENOMEM; /* If the channel descriptor is global, the fields are filled with the same instance of channel descriptor */ for (i = 0; i < chan_desc->length; i++) { int j = (chan_desc->mode != A4L_CHAN_GLOBAL_CHANDESC) ? i : 0; int k = (rng_desc->mode != A4L_RNG_GLOBAL_RNGDESC) ? i : 0; chan_info[i].chan_flags = chan_desc->chans[j].flags; chan_info[i].nb_bits = chan_desc->chans[j].nb_bits; chan_info[i].nb_rng = rng_desc->rngtabs[k]->length; if (chan_desc->mode == A4L_CHAN_GLOBAL_CHANDESC) chan_info[i].chan_flags |= A4L_CHAN_GLOBAL; } if (rtdm_safe_copy_to_user(fd, inarg.info, chan_info, chan_desc->length * sizeof(a4l_chinfo_t)) != 0) return -EFAULT; rtdm_free(chan_info); return ret; } int a4l_ioctl_nbrnginfo(struct a4l_device_context * cxt, void *arg) { int i; struct rtdm_fd *fd = rtdm_private_to_fd(cxt); struct a4l_device *dev = a4l_get_dev(cxt); a4l_rnginfo_arg_t inarg; struct a4l_rngdesc *rng_desc; /* Basic checking */ if (!test_bit(A4L_DEV_ATTACHED_NR, &dev->flags)) { __a4l_err("a4l_ioctl_nbrnginfo: unattached device\n"); return -EINVAL; } if (rtdm_safe_copy_from_user(fd, &inarg, arg, sizeof(a4l_rnginfo_arg_t)) != 0) return -EFAULT; if (inarg.idx_subd >= dev->transfer.nb_subd) { __a4l_err("a4l_ioctl_nbrnginfo: bad subdevice index\n"); return -EINVAL; } if (dev->transfer.subds[inarg.idx_subd]->chan_desc == NULL) { __a4l_err("a4l_ioctl_nbrnginfo: no channel descriptor " "for subdevice %d\n", inarg.idx_subd); return -EINVAL; } if (inarg.idx_chan >= dev->transfer.subds[inarg.idx_subd]->chan_desc->length) { __a4l_err("a4l_ioctl_nbrnginfo: bad channel index\n"); return -EINVAL; } rng_desc = dev->transfer.subds[inarg.idx_subd]->rng_desc; if (rng_desc != NULL) { i = (rng_desc->mode != A4L_RNG_GLOBAL_RNGDESC) ? inarg.idx_chan : 0; inarg.info = (void *)(unsigned long) rng_desc->rngtabs[i]->length; } else inarg.info = (void *)0; if (rtdm_safe_copy_to_user(fd, arg, &inarg, sizeof(a4l_rnginfo_arg_t)) != 0) return -EFAULT; return 0; } int a4l_ioctl_rnginfo(struct a4l_device_context * cxt, void *arg) { struct rtdm_fd *fd = rtdm_private_to_fd(cxt); int i, ret = 0; unsigned int tmp; struct a4l_device *dev = a4l_get_dev(cxt); struct a4l_rngdesc *rng_desc; a4l_rnginfo_t *rng_info; a4l_rnginfo_arg_t inarg; /* Basic checking */ if (!test_bit(A4L_DEV_ATTACHED_NR, &dev->flags)) { __a4l_err("a4l_ioctl_rnginfo: unattached device\n"); return -EINVAL; } if (rtdm_safe_copy_from_user(fd, &inarg, arg, sizeof(a4l_rnginfo_arg_t)) != 0) return -EFAULT; if (inarg.idx_subd >= dev->transfer.nb_subd) { __a4l_err("a4l_ioctl_rnginfo: bad subdevice index\n"); return -EINVAL; } if (dev->transfer.subds[inarg.idx_subd]->chan_desc == NULL) { __a4l_err("a4l_ioctl_rnginfo: no channel descriptor " "for subdevice %d\n", inarg.idx_subd); return -EINVAL; } if (inarg.idx_chan >= dev->transfer.subds[inarg.idx_subd]->chan_desc->length) { __a4l_err("a4l_ioctl_rnginfo: bad channel index\n"); return -EINVAL; } rng_desc = dev->transfer.subds[inarg.idx_subd]->rng_desc; if (rng_desc == NULL) { __a4l_err("a4l_ioctl_rnginfo: no range descriptor " "for channel %d\n", inarg.idx_chan); return -EINVAL; } /* If the range descriptor is global, we take the first instance */ tmp = (rng_desc->mode != A4L_RNG_GLOBAL_RNGDESC) ? inarg.idx_chan : 0; rng_info = rtdm_malloc(rng_desc->rngtabs[tmp]->length * sizeof(a4l_rnginfo_t)); if (rng_info == NULL) return -ENOMEM; for (i = 0; i < rng_desc->rngtabs[tmp]->length; i++) { rng_info[i].min = rng_desc->rngtabs[tmp]->rngs[i].min; rng_info[i].max = rng_desc->rngtabs[tmp]->rngs[i].max; rng_info[i].flags = rng_desc->rngtabs[tmp]->rngs[i].flags; if (rng_desc->mode == A4L_RNG_GLOBAL_RNGDESC) rng_info[i].flags |= A4L_RNG_GLOBAL; } if (rtdm_safe_copy_to_user(fd, inarg.info, rng_info, rng_desc->rngtabs[tmp]->length * sizeof(a4l_rnginfo_t)) != 0) return -EFAULT; rtdm_free(rng_info); return ret; }