void a4l_set_dev(a4l_cxt_t *cxt) { /* Retrieve the minor index */ const int minor = a4l_get_minor(cxt); /* Fill the dev fields accordingly */ cxt->dev = &(a4l_devs[minor]); }
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; }
int a4l_ioctl_devinfo(a4l_cxt_t * cxt, void *arg) { a4l_dvinfo_t info; a4l_dev_t *dev = a4l_get_dev(cxt); __a4l_dbg(1, core_dbg, "a4l_ioctl_devinfo: minor=%d\n", a4l_get_minor(cxt)); memset(&info, 0, sizeof(a4l_dvinfo_t)); if (test_bit(A4L_DEV_ATTACHED, &dev->flags)) { int len = (strlen(dev->driver->board_name) > A4L_NAMELEN) ? A4L_NAMELEN : strlen(dev->driver->board_name); memcpy(info.board_name, dev->driver->board_name, len); info.nb_subd = dev->transfer.nb_subd; info.idx_read_subd = dev->transfer.idx_read_subd; info.idx_write_subd = dev->transfer.idx_write_subd; } if (rtdm_safe_copy_to_user(cxt->user_info, arg, &info, sizeof(a4l_dvinfo_t)) != 0) return -EFAULT; return 0; }
int a4l_release_driver(a4l_cxt_t * cxt) { int ret = 0; a4l_dev_t *dev = a4l_get_dev(cxt); __a4l_dbg(1, core_dbg, "a4l_release_driver: minor=%d\n", a4l_get_minor(cxt)); if ((ret = dev->driver->detach(dev)) != 0) goto out_release_driver; /* Decrease module's count so as to allow module unloading */ module_put(dev->driver->owner); /* In case, the driver developer did not free the subdevices */ while (&dev->subdvsq != dev->subdvsq.next) { struct list_head *this = dev->subdvsq.next; a4l_subd_t *tmp = list_entry(this, a4l_subd_t, list); list_del(this); rtdm_free(tmp); } /* Free the private field */ rtdm_free(dev->priv); dev->driver = NULL; out_release_driver: 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; }
void a4l_free_lnkdesc(a4l_cxt_t * cxt, a4l_lnkdesc_t * link_arg) { __a4l_dbg(1, core_dbg, "a4l_free_lnkdesc: minor=%d\n", a4l_get_minor(cxt)); if (link_arg->bname != NULL) rtdm_free(link_arg->bname); if (link_arg->opts != NULL) rtdm_free(link_arg->opts); }
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_device_detach(a4l_cxt_t * cxt) { a4l_dev_t *dev = a4l_get_dev(cxt); __a4l_dbg(1, core_dbg, "a4l_device_detach: minor=%d\n", a4l_get_minor(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; }
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); }
void a4l_set_dev(a4l_cxt_t * cxt) { cxt->dev = &(a4l_devs[a4l_get_minor(cxt)]); }
int a4l_fill_lnkdesc(a4l_cxt_t * cxt, a4l_lnkdesc_t * link_arg, void *arg) { int ret; char *tmpname = NULL; void *tmpopts = NULL; __a4l_dbg(1, core_dbg, "a4l_fill_lnkdesc: minor=%d\n", a4l_get_minor(cxt)); 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; }