int a4l_check_cmddesc(struct a4l_device_context * cxt, struct a4l_cmd_desc * desc) { struct a4l_device *dev = a4l_get_dev(cxt); struct a4l_subdevice *subd; if (desc->idx_subd >= dev->transfer.nb_subd) { __a4l_err("a4l_check_cmddesc: " "subdevice index out of range (idx=%u)\n", desc->idx_subd); return -EINVAL; } subd = dev->transfer.subds[desc->idx_subd]; if ((subd->flags & A4L_SUBD_TYPES) == A4L_SUBD_UNUSED) { __a4l_err("a4l_check_cmddesc: " "subdevice type incoherent\n"); return -EIO; } if (!(subd->flags & A4L_SUBD_CMD)) { __a4l_err("a4l_check_cmddesc: operation not supported, " "synchronous only subdevice\n"); return -EIO; } if (test_bit(A4L_SUBD_BUSY, &subd->status)) { __a4l_err("a4l_check_cmddesc: subdevice busy\n"); return -EBUSY; } return a4l_check_chanlist(dev->transfer.subds[desc->idx_subd], desc->nb_chan, desc->chan_descs); }
int a4l_do_insn_trig(a4l_cxt_t * cxt, a4l_kinsn_t * dsc) { a4l_subd_t *subd; a4l_dev_t *dev = a4l_get_dev(cxt); unsigned int trignum; unsigned int *data = (unsigned int*)dsc->data; /* Basic checkings */ if (dsc->data_size > 1) { __a4l_err("a4l_do_insn_trig: data size should not be > 1\n"); return -EINVAL; } trignum = (dsc->data_size == sizeof(unsigned int)) ? data[0] : 0; if (dsc->idx_subd >= dev->transfer.nb_subd) { __a4l_err("a4l_do_insn_trig: " "subdevice index is out of range\n"); return -EINVAL; } subd = dev->transfer.subds[dsc->idx_subd]; /* Checks that the concerned subdevice is trigger-compliant */ if ((subd->flags & A4L_SUBD_CMD) == 0 || subd->trigger == NULL) { __a4l_err("a4l_do_insn_trig: subdevice does not support " "triggering or asynchronous acquisition\n"); return -EINVAL; } /* Performs the trigger */ return subd->trigger(subd, trignum); }
int a4l_do_special_insn(a4l_cxt_t * cxt, a4l_kinsn_t * dsc) { int ret = 0; switch (dsc->type) { case A4L_INSN_GTOD: ret = a4l_do_insn_gettime(dsc); break; case A4L_INSN_WAIT: ret = a4l_do_insn_wait(dsc); break; case A4L_INSN_INTTRIG: ret = a4l_do_insn_trig(cxt, dsc); break; default: __a4l_err("a4l_do_special_insn: " "incoherent instruction code\n"); return -EINVAL; } if (ret < 0) __a4l_err("a4l_do_special_insn: " "execution of the instruction failed (err=%d)\n", ret); return ret; }
int a4l_do_insn_wait(a4l_kinsn_t * dsc) { unsigned int us; unsigned int *data = (unsigned int *)dsc->data; /* Basic checkings */ if (dsc->data_size != sizeof(unsigned int)) { __a4l_err("a4l_do_insn_wait: data size should be 1\n"); return -EINVAL; } if (data[0] > A4L_INSN_WAIT_MAX) { __a4l_err("a4l_do_insn_wait: wait duration is out of range\n"); return -EINVAL; } /* As we use (a4l_)udelay, we have to convert the delay into microseconds */ us = data[0] / 1000; /* At least, the delay is rounded up to 1 microsecond */ if (us == 0) us = 1; /* Performs the busy waiting */ a4l_udelay(us); return 0; }
int a4l_check_specific_cmdcnt(struct a4l_device_context * cxt, struct a4l_cmd_desc * desc) { unsigned int tmp1, tmp2; struct a4l_device *dev = a4l_get_dev(cxt); struct a4l_cmd_desc *cmd_mask = dev->transfer.subds[desc->idx_subd]->cmd_mask; if (cmd_mask == NULL) return 0; if (cmd_mask->start_src != 0) { tmp1 = desc->start_src & ~(cmd_mask->start_src); tmp2 = desc->start_src & (cmd_mask->start_src); if (tmp1 != 0 || tmp2 == 0) { __a4l_err("a4l_check_cmddesc: start_src, " "trigger unsupported\n"); return -EINVAL; } } if (cmd_mask->scan_begin_src != 0) { tmp1 = desc->scan_begin_src & ~(cmd_mask->scan_begin_src); tmp2 = desc->scan_begin_src & (cmd_mask->scan_begin_src); if (tmp1 != 0 || tmp2 == 0) { __a4l_err("a4l_check_cmddesc: scan_begin_src, " "trigger unsupported\n"); return -EINVAL; } } if (cmd_mask->convert_src != 0) { tmp1 = desc->convert_src & ~(cmd_mask->convert_src); tmp2 = desc->convert_src & (cmd_mask->convert_src); if (tmp1 != 0 || tmp2 == 0) { __a4l_err("a4l_check_cmddesc: convert_src, " "trigger unsupported\n"); return -EINVAL; } } if (cmd_mask->scan_end_src != 0) { tmp1 = desc->scan_end_src & ~(cmd_mask->scan_end_src); if (tmp1 != 0) { __a4l_err("a4l_check_cmddesc: scan_end_src, " "trigger unsupported\n"); return -EINVAL; } } if (cmd_mask->stop_src != 0) { tmp1 = desc->stop_src & ~(cmd_mask->stop_src); tmp2 = desc->stop_src & (cmd_mask->stop_src); if (tmp1 != 0 || tmp2 == 0) { __a4l_err("a4l_check_cmddesc: stop_src, " "trigger unsupported\n"); return -EINVAL; } } return 0; }
/* --- Command descriptor management functions --- */ int a4l_fill_cmddesc(struct a4l_device_context *cxt, struct a4l_cmd_desc *desc, unsigned int **chan_descs, void *arg) { unsigned int *tmpchans = NULL; int ret = 0; ret = rtdm_safe_copy_from_user(rtdm_private_to_fd(cxt), desc, arg, sizeof(struct a4l_cmd_desc)); if (ret != 0) goto out_cmddesc; if (desc->nb_chan == 0) { ret = -EINVAL; goto out_cmddesc; } tmpchans = rtdm_malloc(desc->nb_chan * sizeof(unsigned int)); if (tmpchans == NULL) { ret = -ENOMEM; goto out_cmddesc; } ret = rtdm_safe_copy_from_user(rtdm_private_to_fd(cxt), tmpchans, desc->chan_descs, desc->nb_chan * sizeof(unsigned int)); if (ret != 0) { __a4l_err("%s invalid arguments \n", __FUNCTION__); goto out_cmddesc; } *chan_descs = desc->chan_descs; desc->chan_descs = tmpchans; __a4l_dbg(1, core_dbg, "desc dump: \n"); __a4l_dbg(1, core_dbg, "\t->idx_subd=%u\n", desc->idx_subd); __a4l_dbg(1, core_dbg, "\t->flags=%lu\n", desc->flags); __a4l_dbg(1, core_dbg, "\t->nb_chan=%u\n", desc->nb_chan); __a4l_dbg(1, core_dbg, "\t->chan_descs=0x%x\n", *desc->chan_descs); __a4l_dbg(1, core_dbg, "\t->data_len=%u\n", desc->data_len); __a4l_dbg(1, core_dbg, "\t->pdata=0x%p\n", desc->data); out_cmddesc: if (ret != 0) { __a4l_err("a4l_fill_cmddesc: %d \n", ret); if (tmpchans != NULL) rtdm_free(tmpchans); desc->chan_descs = NULL; } return ret; }
int a4l_assign_driver(a4l_cxt_t * cxt, a4l_drv_t * drv, a4l_lnkdesc_t * link_arg) { int ret = 0; a4l_dev_t *dev = a4l_get_dev(cxt); __a4l_dbg(1, core_dbg, "a4l_assign_driver: minor=%d\n", a4l_get_minor(cxt)); dev->driver = drv; if (drv->privdata_size == 0) __a4l_dbg(1, core_dbg, "a4l_assign_driver: warning! " "the field priv will not be usable\n"); else { INIT_LIST_HEAD(&dev->subdvsq); dev->priv = rtdm_malloc(drv->privdata_size); if (dev->priv == NULL && drv->privdata_size != 0) { __a4l_err("a4l_assign_driver: " "call(alloc) failed\n"); ret = -ENOMEM; goto out_assign_driver; } /* Initialize the private data even if it not our role (the driver should do it), that may prevent hard to find bugs */ memset(dev->priv, 0, drv->privdata_size); } if ((ret = drv->attach(dev, link_arg)) != 0) __a4l_err("a4l_assign_driver: " "call(drv->attach) failed (ret=%d)\n", ret); out_assign_driver: /* Increments module's count */ if (ret == 0 && (!try_module_get(drv->owner))) { __a4l_err("a4l_assign_driver: " "driver's owner field wrongly set\n"); ret = -ENODEV; } if (ret != 0 && dev->priv != NULL) { rtdm_free(dev->priv); dev->driver = NULL; } return ret; }
int a4l_ioctl_devcfg(a4l_cxt_t * cxt, void *arg) { int ret = 0; if (rtdm_in_rt_context()) return -ENOSYS; if (arg == NULL) { /* Basic checking */ if (!test_bit(A4L_DEV_ATTACHED_NR, &(a4l_get_dev(cxt)->flags))) { __a4l_err("a4l_ioctl_devcfg: " "free device, no driver to detach\n"); return -EINVAL; } /* Pre-cleanup of the transfer structure, we ensure that nothing is busy */ if ((ret = a4l_precleanup_transfer(cxt)) != 0) return ret; /* Remove the related proc file */ a4l_proc_detach(cxt); /* Free the transfer structure and its related data */ if ((ret = a4l_cleanup_transfer(cxt)) != 0) return ret; /* Free the device and the driver from each other */ if ((ret = a4l_device_detach(cxt)) == 0) clear_bit(A4L_DEV_ATTACHED_NR, &(a4l_get_dev(cxt)->flags)); } else { /* Basic checking */ if (test_bit (A4L_DEV_ATTACHED_NR, &(a4l_get_dev(cxt)->flags))) { __a4l_err("a4l_ioctl_devcfg: " "linked device, cannot attach more driver\n"); return -EINVAL; } /* Pre-initialization of the transfer structure */ a4l_presetup_transfer(cxt); /* Link the device with the driver */ if ((ret = a4l_device_attach(cxt, arg)) != 0) return ret; /* Create the transfer structure and the related proc file */ if ((ret = a4l_setup_transfer(cxt)) != 0 || (ret = a4l_proc_attach(cxt)) != 0) a4l_device_detach(cxt); else set_bit(A4L_DEV_ATTACHED_NR, &(a4l_get_dev(cxt)->flags)); } return ret; }
int a4l_ioctl_devcfg(a4l_cxt_t * cxt, void *arg) { int ret = 0; __a4l_dbg(1, core_dbg, "a4l_ioctl_devcfg: minor=%d\n", a4l_get_minor(cxt)); if (a4l_test_rt() != 0) return -EPERM; if (arg == NULL) { /* Basic checking */ if (!test_bit (A4L_DEV_ATTACHED, &(a4l_get_dev(cxt)->flags))) { __a4l_err("a4l_ioctl_devcfg: " "free device, no driver to detach\n"); return -EINVAL; } /* Removes the related proc file */ a4l_proc_detach(cxt); /* Frees the transfer structure and its related data */ if ((ret = a4l_cleanup_transfer(cxt)) != 0) return ret; /* Frees the device and the driver from each other */ if ((ret = a4l_device_detach(cxt)) == 0) clear_bit(A4L_DEV_ATTACHED, &(a4l_get_dev(cxt)->flags)); } else { /* Basic checking */ if (test_bit (A4L_DEV_ATTACHED, &(a4l_get_dev(cxt)->flags))) { __a4l_err("a4l_ioctl_devcfg: " "linked device, cannot attach more driver\n"); return -EINVAL; } /* Pre-initialization of the transfer structure */ a4l_presetup_transfer(cxt); /* Links the device with the driver */ if ((ret = a4l_device_attach(cxt, arg)) != 0) return ret; /* Creates the transfer structure and the related proc file */ if ((ret = a4l_setup_transfer(cxt)) != 0 || (ret = a4l_proc_attach(cxt)) != 0) a4l_device_detach(cxt); else set_bit(A4L_DEV_ATTACHED, &(a4l_get_dev(cxt)->flags)); } return ret; }
int a4l_init_proc(void) { int ret = 0; struct proc_dir_entry *entry; /* Creates the global directory */ a4l_proc_root = create_proc_entry("analogy", S_IFDIR, 0); if (a4l_proc_root == NULL) { __a4l_err("a4l_proc_init: " "failed to create /proc/analogy\n"); return -ENOMEM; } /* Creates the devices related file */ entry = create_proc_entry("devices", 0444, a4l_proc_root); if (entry == NULL) { __a4l_err("a4l_proc_init: " "failed to create /proc/analogy/devices\n"); ret = -ENOMEM; goto err_proc_init; } entry->nlink = 1; entry->data = NULL; entry->write_proc = NULL; entry->read_proc = a4l_rdproc_devs; wrap_proc_dir_entry_owner(entry); /* Creates the drivers related file */ entry = create_proc_entry("drivers", 0444, a4l_proc_root); if (entry == NULL) { __a4l_err("a4l_proc_init: " "failed to create /proc/analogy/drivers\n"); ret = -ENOMEM; goto err_proc_init; } entry->nlink = 1; entry->data = NULL; entry->write_proc = NULL; entry->read_proc = a4l_rdproc_drvs; wrap_proc_dir_entry_owner(entry); return 0; err_proc_init: remove_proc_entry("devices", a4l_proc_root); remove_proc_entry("analogy", NULL); return ret; }
int a4l_device_attach(a4l_cxt_t * cxt, void *arg) { int ret = 0; a4l_lnkdesc_t link_arg; a4l_drv_t *drv = NULL; __a4l_dbg(1, core_dbg, "a4l_device_attach: minor=%d\n", a4l_get_minor(cxt)); if ((ret = a4l_fill_lnkdesc(cxt, &link_arg, arg)) != 0) goto out_attach; if ((ret = a4l_lct_drv(link_arg.bname, &drv)) != 0) { __a4l_err("a4l_device_attach: " "cannot find board name %s\n", link_arg.bname); goto out_attach; } if ((ret = a4l_assign_driver(cxt, drv, &link_arg)) != 0) goto out_attach; out_attach: a4l_free_lnkdesc(cxt, &link_arg); return ret; }
/* This function is not optimized in terms of memory footprint and CPU charge; however, the whole analogy instruction system was not designed for performance issues */ int a4l_ioctl_insnlist(a4l_cxt_t * cxt, void *arg) { int i, ret = 0; a4l_kilst_t ilst; a4l_dev_t *dev = a4l_get_dev(cxt); /* Basic checking */ if (!test_bit(A4L_DEV_ATTACHED, &dev->flags)) { __a4l_err("a4l_ioctl_insnlist: unattached device\n"); return -EINVAL; } if ((ret = a4l_fill_ilstdsc(cxt, &ilst, arg)) < 0) return ret; /* Performs the instructions */ for (i = 0; i < ilst.count && ret == 0; i++) { if ((ilst.insns[i].type & A4L_INSN_MASK_SPECIAL) != 0) ret = a4l_do_special_insn(cxt, &ilst.insns[i]); else ret = a4l_do_insn(cxt, &ilst.insns[i]); } if (ret < 0) goto err_ioctl_ilst; return a4l_free_ilstdsc(cxt, &ilst); err_ioctl_ilst: a4l_free_ilstdsc(cxt, &ilst); return ret; }
int a4l_device_detach(a4l_cxt_t * cxt) { a4l_dev_t *dev = a4l_get_dev(cxt); if (dev->driver == NULL) { __a4l_err("a4l_device_detach: " "incoherent state, driver not reachable\n"); return -ENXIO; } return a4l_release_driver(cxt); }
int a4l_proc_attach(a4l_cxt_t * cxt) { int ret = 0; a4l_dev_t *dev = a4l_get_dev(cxt); struct proc_dir_entry *entry; char *entry_name, *p; /* Allocates the buffer for the file name */ entry_name = rtdm_malloc(A4L_NAMELEN + 4); if ((p = entry_name) == NULL) { __a4l_err("a4l_proc_attach: failed to allocate buffer\n"); return -ENOMEM; } /* Creates the proc file name */ p += sprintf(p, "%02d-", a4l_get_minor(cxt)); strncpy(p, dev->driver->board_name, A4L_NAMELEN); /* Creates the proc entry */ entry = create_proc_entry(entry_name, 0444, a4l_proc_root); if (entry == NULL) { __a4l_err("a4l_proc_attach: " "failed to create /proc/analogy/%s\n", entry_name); ret = -ENOMEM; goto out_setup_proc_transfer; } entry->nlink = 1; entry->data = &dev->transfer; entry->write_proc = NULL; entry->read_proc = a4l_rdproc_transfer; wrap_proc_dir_entry_owner(entry); out_setup_proc_transfer: /* Frees the file name buffer */ rtdm_free(entry_name); return ret; }
int a4l_fill_insndsc(a4l_cxt_t * cxt, a4l_kinsn_t * dsc, void *arg) { int ret = 0; void *tmp_data = NULL; ret = rtdm_safe_copy_from_user(cxt->user_info, dsc, arg, sizeof(a4l_insn_t)); if (ret != 0) goto out_insndsc; if (dsc->data_size != 0 && dsc->data == NULL) { __a4l_err("a4l_fill_insndsc: no data pointer specified\n"); ret = -EINVAL; goto out_insndsc; } if (dsc->data_size != 0 && dsc->data != NULL) { tmp_data = rtdm_malloc(dsc->data_size); if (tmp_data == NULL) { ret = -ENOMEM; goto out_insndsc; } if ((dsc->type & A4L_INSN_MASK_WRITE) != 0) { ret = rtdm_safe_copy_from_user(cxt->user_info, tmp_data, dsc->data, dsc->data_size); if (ret < 0) goto out_insndsc; } } dsc->__udata = dsc->data; dsc->data = tmp_data; out_insndsc: if (ret != 0 && tmp_data != NULL) rtdm_free(tmp_data); return ret; }
int a4l_fill_ilstdsc(a4l_cxt_t * cxt, a4l_kilst_t * dsc, void *arg) { int i, ret = 0; dsc->insns = NULL; /* Recovers the structure from user space */ ret = rtdm_safe_copy_from_user(cxt->user_info, dsc, arg, sizeof(a4l_insnlst_t)); if (ret < 0) return ret; /* Some basic checking */ if (dsc->count == 0) { __a4l_err("a4l_fill_ilstdsc: instruction list's count is 0\n"); return -EINVAL; } /* Keeps the user pointer in an opaque field */ dsc->__uinsns = (a4l_insn_t *)dsc->insns; dsc->insns = rtdm_malloc(dsc->count * sizeof(a4l_kinsn_t)); if (dsc->insns == NULL) return -ENOMEM; /* Recovers the instructions, one by one. This part is not optimized */ for (i = 0; i < dsc->count && ret == 0; i++) ret = a4l_fill_insndsc(cxt, &(dsc->insns[i]), &(dsc->__uinsns[i])); /* In case of error, frees the allocated memory */ if (ret < 0 && dsc->insns != NULL) rtdm_free(dsc->insns); return ret; }
int a4l_close(struct rtdm_dev_context *context, rtdm_user_info_t * user_info) { int err; a4l_cxt_t *cxt = (a4l_cxt_t *)rtdm_context_to_private(context); /* Cancel the maybe occuring asynchronous transfer */ err = a4l_cancel_buffer(cxt); if (err < 0) { __a4l_err("close: unable to stop the asynchronous transfer\n"); return err; } /* Free the buffer which was linked with this context and... */ a4l_free_buffer(cxt->buffer); /* ...free the other buffer resources (sync) and... */ a4l_cleanup_buffer(cxt->buffer); /* ...free the structure */ rtdm_free(cxt->buffer); return 0; }
void a4l_proc_detach(a4l_cxt_t * cxt) { char *entry_name, *p; a4l_dev_t *dev = a4l_get_dev(cxt); /* Allocate the buffer for the file name */ entry_name = rtdm_malloc(A4L_NAMELEN + 4); if ((p = entry_name) == NULL) { __a4l_err("a4l_proc_detach: " "failed to allocate filename buffer\n"); return; } /* Build the name */ p += sprintf(p, "%02d-", a4l_get_minor(cxt)); strncpy(p, dev->driver->board_name, A4L_NAMELEN); /* Remove the proc file */ remove_proc_entry(entry_name, a4l_proc_root); /* Free the temporary buffer */ rtdm_free(entry_name); }
int a4l_do_insn_gettime(a4l_kinsn_t * dsc) { nanosecs_abs_t ns; uint32_t ns2; unsigned int *data = (unsigned int *)dsc->data; /* Basic checkings */ if (dsc->data_size != 2 * sizeof(unsigned int)) { __a4l_err("a4l_do_insn_gettime: data size should be 2\n"); return -EINVAL; } /* Get a timestamp */ ns = a4l_get_time(); /* Perform the conversion */ ns2 = do_div(ns, 1000000000); data[0] = (unsigned int) ns; data[1] = (unsigned int) ns2 / 1000; return 0; }
int a4l_ioctl_insn(a4l_cxt_t * cxt, void *arg) { int ret = 0; a4l_kinsn_t insn; a4l_dev_t *dev = a4l_get_dev(cxt); /* Basic checking */ if (!test_bit(A4L_DEV_ATTACHED, &dev->flags)) { __a4l_err("a4l_ioctl_insn: unattached device\n"); return -EINVAL; } /* Recovers the instruction descriptor */ ret = a4l_fill_insndsc(cxt, &insn, arg); if (ret != 0) goto err_ioctl_insn; /* Performs the instruction */ if ((insn.type & A4L_INSN_MASK_SPECIAL) != 0) ret = a4l_do_special_insn(cxt, &insn); else ret = a4l_do_insn(cxt, &insn); if (ret < 0) goto err_ioctl_insn; /* Frees the used memory and sends back some data, if need be */ ret = a4l_free_insndsc(cxt, &insn); return ret; err_ioctl_insn: a4l_free_insndsc(cxt, &insn); return ret; }
int a4l_fill_lnkdesc(a4l_cxt_t * cxt, a4l_lnkdesc_t * link_arg, void *arg) { int ret; char *tmpname = NULL; void *tmpopts = NULL; ret = rtdm_safe_copy_from_user(cxt->user_info, link_arg, arg, sizeof(a4l_lnkdesc_t)); if (ret != 0) { __a4l_err("a4l_fill_lnkdesc: " "call1(copy_from_user) failed\n"); goto out_get_lnkdesc; } if (link_arg->bname_size != 0 && link_arg->bname != NULL) { tmpname = rtdm_malloc(link_arg->bname_size + 1); if (tmpname == NULL) { __a4l_err("a4l_fill_lnkdesc: " "call1(alloc) failed\n"); ret = -ENOMEM; goto out_get_lnkdesc; } tmpname[link_arg->bname_size] = 0; ret = rtdm_safe_copy_from_user(cxt->user_info, tmpname, link_arg->bname, link_arg->bname_size); if (ret != 0) { __a4l_err("a4l_fill_lnkdesc: " "call2(copy_from_user) failed\n"); goto out_get_lnkdesc; } } else { __a4l_err("a4l_fill_lnkdesc: board name missing\n"); ret = -EINVAL; goto out_get_lnkdesc; } if (link_arg->opts_size != 0 && link_arg->opts != NULL) { tmpopts = rtdm_malloc(link_arg->opts_size); if (tmpopts == NULL) { __a4l_err("a4l_fill_lnkdesc: " "call2(alloc) failed\n"); ret = -ENOMEM; goto out_get_lnkdesc; } ret = rtdm_safe_copy_from_user(cxt->user_info, tmpopts, link_arg->opts, link_arg->opts_size); if (ret != 0) { __a4l_err("a4l_fill_lnkdesc: " "call3(copy_from_user) failed\n"); goto out_get_lnkdesc; } } link_arg->bname = tmpname; link_arg->opts = tmpopts; out_get_lnkdesc: if (tmpname == NULL) { link_arg->bname = NULL; link_arg->bname_size = 0; } if (tmpopts == NULL) { link_arg->opts = NULL; link_arg->opts_size = 0; } return ret; }
int a4l_do_insn(a4l_cxt_t * cxt, a4l_kinsn_t * dsc) { int ret; a4l_subd_t *subd; a4l_dev_t *dev = a4l_get_dev(cxt); int (*hdlr) (a4l_subd_t *, a4l_kinsn_t *) = NULL; /* Checks the subdevice index */ if (dsc->idx_subd >= dev->transfer.nb_subd) { __a4l_err("a4l_do_insn: " "subdevice index out of range (idx=%d)\n", dsc->idx_subd); return -EINVAL; } /* Recovers pointers on the proper subdevice */ subd = dev->transfer.subds[dsc->idx_subd]; /* Checks the subdevice's characteristics */ if ((subd->flags & A4L_SUBD_TYPES) == A4L_SUBD_UNUSED) { __a4l_err("a4l_do_insn: wrong subdevice selected\n"); return -EINVAL; } /* Checks the channel descriptor */ ret = a4l_check_chanlist(dev->transfer.subds[dsc->idx_subd], 1, &dsc->chan_desc); if (ret < 0) return ret; /* Choose the proper handler, we can check the pointer because the subdevice was memset to 0 at allocation time */ switch (dsc->type) { case A4L_INSN_READ: hdlr = subd->insn_read; break; case A4L_INSN_WRITE: hdlr = subd->insn_write; break; case A4L_INSN_BITS: hdlr = subd->insn_bits; break; case A4L_INSN_CONFIG: hdlr = subd->insn_config; break; default: ret = -EINVAL; } /* We check the instruction type */ if (ret < 0) return ret; /* We check whether a handler is available */ if (hdlr == NULL) return -ENOSYS; /* Prevents the subdevice from being used during the following operations */ ret = a4l_reserve_transfer(cxt, dsc->idx_subd); if (ret < 0) goto out_do_insn; /* Let's the driver-specific code perform the instruction */ ret = hdlr(subd, dsc); if (ret < 0) __a4l_err("a4l_do_insn: " "execution of the instruction failed (err=%d)\n", ret); out_do_insn: /* Releases the subdevice from its reserved state */ a4l_cancel_transfer(cxt, dsc->idx_subd); return ret; }
int a4l_ioctl_cmd(struct a4l_device_context * ctx, void *arg) { int ret = 0, simul_flag = 0; struct a4l_cmd_desc *cmd_desc = NULL; struct a4l_device *dev = a4l_get_dev(ctx); unsigned int *chan_descs, *tmp; struct a4l_subdevice *subd; /* The command launching cannot be done in real-time because of some possible buffer allocations in the drivers */ if (rtdm_in_rt_context()) return -ENOSYS; /* Basically check the device */ if (!test_bit(A4L_DEV_ATTACHED_NR, &dev->flags)) { __a4l_err("a4l_ioctl_cmd: cannot command " "an unattached device\n"); return -EINVAL; } /* Allocates the command */ cmd_desc = (struct a4l_cmd_desc *) rtdm_malloc(sizeof(struct a4l_cmd_desc)); if (cmd_desc == NULL) return -ENOMEM; memset(cmd_desc, 0, sizeof(struct a4l_cmd_desc)); /* Gets the command */ ret = a4l_fill_cmddesc(ctx, cmd_desc, &chan_descs, arg); if (ret != 0) goto out_ioctl_cmd; /* Checks the command */ ret = a4l_check_cmddesc(ctx, cmd_desc); if (ret != 0) goto out_ioctl_cmd; ret = a4l_check_generic_cmdcnt(cmd_desc); if (ret != 0) goto out_ioctl_cmd; ret = a4l_check_specific_cmdcnt(ctx, cmd_desc); if (ret != 0) goto out_ioctl_cmd; __a4l_dbg(1, core_dbg,"1st cmd checks passed\n"); subd = dev->transfer.subds[cmd_desc->idx_subd]; /* Tests the command with the cmdtest function */ if (cmd_desc->flags & A4L_CMD_SIMUL) { simul_flag = 1; if (!subd->do_cmdtest) { __a4l_err("a4l_ioctl_cmd: driver's cmd_test NULL\n"); ret = -EINVAL; goto out_ioctl_cmd; } ret = subd->do_cmdtest(subd, cmd_desc); if (ret != 0) { __a4l_err("a4l_ioctl_cmd: driver's cmd_test failed\n"); goto out_ioctl_cmd; } __a4l_dbg(1, core_dbg, "driver's cmd checks passed\n"); goto out_ioctl_cmd; } /* Gets the transfer system ready */ ret = a4l_setup_buffer(ctx, cmd_desc); if (ret < 0) goto out_ioctl_cmd; /* Eventually launches the command */ ret = subd->do_cmd(subd, cmd_desc); if (ret != 0) { a4l_cancel_buffer(ctx); goto out_ioctl_cmd; } out_ioctl_cmd: if (simul_flag) { /* copy the kernel based descriptor */ tmp = cmd_desc->chan_descs; /* return the user based descriptor */ cmd_desc->chan_descs = chan_descs; rtdm_safe_copy_to_user(rtdm_private_to_fd(ctx), arg, cmd_desc, sizeof(struct a4l_cmd_desc)); /* make sure we release the memory associated to the kernel */ cmd_desc->chan_descs = tmp; } if (ret != 0 || simul_flag == 1) { a4l_free_cmddesc(cmd_desc); rtdm_free(cmd_desc); } return ret; }
int a4l_check_generic_cmdcnt(struct a4l_cmd_desc * desc) { unsigned int tmp1, tmp2; /* Makes sure trigger sources are trivially valid */ tmp1 = desc->start_src & ~(TRIG_NOW | TRIG_INT | TRIG_EXT | TRIG_FOLLOW); tmp2 = desc->start_src & (TRIG_NOW | TRIG_INT | TRIG_EXT | TRIG_FOLLOW); if (tmp1 != 0 || tmp2 == 0) { __a4l_err("a4l_check_cmddesc: start_src, weird trigger\n"); return -EINVAL; } tmp1 = desc->scan_begin_src & ~(TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW); tmp2 = desc->scan_begin_src & (TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW); if (tmp1 != 0 || tmp2 == 0) { __a4l_err("a4l_check_cmddesc: scan_begin_src, , weird trigger\n"); return -EINVAL; } tmp1 = desc->convert_src & ~(TRIG_TIMER | TRIG_EXT | TRIG_NOW); tmp2 = desc->convert_src & (TRIG_TIMER | TRIG_EXT | TRIG_NOW); if (tmp1 != 0 || tmp2 == 0) { __a4l_err("a4l_check_cmddesc: convert_src, weird trigger\n"); return -EINVAL; } tmp1 = desc->scan_end_src & ~(TRIG_COUNT); if (tmp1 != 0) { __a4l_err("a4l_check_cmddesc: scan_end_src, weird trigger\n"); return -EINVAL; } tmp1 = desc->stop_src & ~(TRIG_COUNT | TRIG_NONE); tmp2 = desc->stop_src & (TRIG_COUNT | TRIG_NONE); if (tmp1 != 0 || tmp2 == 0) { __a4l_err("a4l_check_cmddesc: stop_src, weird trigger\n"); return -EINVAL; } /* Makes sure trigger sources are unique */ if (desc->start_src != TRIG_NOW && desc->start_src != TRIG_INT && desc->start_src != TRIG_EXT && desc->start_src != TRIG_FOLLOW) { __a4l_err("a4l_check_cmddesc: start_src, " "only one trigger should be set\n"); return -EINVAL; } if (desc->scan_begin_src != TRIG_TIMER && desc->scan_begin_src != TRIG_EXT && desc->scan_begin_src != TRIG_FOLLOW) { __a4l_err("a4l_check_cmddesc: scan_begin_src, " "only one trigger should be set\n"); return -EINVAL; } if (desc->convert_src != TRIG_TIMER && desc->convert_src != TRIG_EXT && desc->convert_src != TRIG_NOW) { __a4l_err("a4l_check_cmddesc: convert_src, " "only one trigger should be set\n"); return -EINVAL; } if (desc->stop_src != TRIG_COUNT && desc->stop_src != TRIG_NONE) { __a4l_err("a4l_check_cmddesc: stop_src, " "only one trigger should be set\n"); return -EINVAL; } /* Makes sure arguments are trivially compatible */ tmp1 = desc->start_src & (TRIG_NOW | TRIG_FOLLOW | TRIG_INT); tmp2 = desc->start_arg; if (tmp1 != 0 && tmp2 != 0) { __a4l_err("a4l_check_cmddesc: no start_arg expected\n"); return -EINVAL; } tmp1 = desc->scan_begin_src & TRIG_FOLLOW; tmp2 = desc->scan_begin_arg; if (tmp1 != 0 && tmp2 != 0) { __a4l_err("a4l_check_cmddesc: no scan_begin_arg expected\n"); return -EINVAL; } tmp1 = desc->convert_src & TRIG_NOW; tmp2 = desc->convert_arg; if (tmp1 != 0 && tmp2 != 0) { __a4l_err("a4l_check_cmddesc: no convert_arg expected\n"); return -EINVAL; } tmp1 = desc->stop_src & TRIG_NONE; tmp2 = desc->stop_arg; if (tmp1 != 0 && tmp2 != 0) { __a4l_err("a4l_check_cmddesc: no stop_arg expected\n"); return -EINVAL; } return 0; }