#include #include #include #include #include #include #include #include #include /* FIXME Does it acutally make sense to allow more then one application at a * time to open the control device? For example calling sync from one app will * also sync all others. */ struct tapi_control_file { struct tapi_device *tdev; struct list_head links; }; static struct tapi_endpoint *tapi_lookup_endpoint(struct tapi_device *tdev, unsigned int ep_id) { struct tapi_stream *stream; if (ep_id < tdev->num_ports) return &tdev->ports[ep_id].ep; list_for_each_entry(stream, &tdev->streams, head) { if (stream->ep.id == ep_id) return &stream->ep; } return ERR_PTR(-ENOENT); } static inline struct tapi_device *inode_to_tdev(struct inode *inode) { return container_of(inode->i_cdev, struct tapi_char_device, cdev)->tdev; } static int tapi_control_open(struct inode *inode, struct file *file) { int ret; struct tapi_device *tdev = inode_to_tdev(inode); struct tapi_control_file *tctrl; get_device(&tdev->dev); tctrl = kzalloc(sizeof(*tctrl), GFP_KERNEL); if (!tctrl) { ret = -ENOMEM; goto err_put_device; } INIT_LIST_HEAD(&tctrl->links); tctrl->tdev = tdev; file->private_data = tctrl; return 0; err_put_device: put_device(&tdev->dev); return ret; } static int tapi_control_release(struct inode *inode, struct file *file) { struct tapi_control_file *tctrl = file->private_data; struct tapi_link *link; if (tctrl) { list_for_each_entry(link, &tctrl->links, head) tapi_link_free(tctrl->tdev, link); put_device(&tctrl->tdev->dev); } return 0; } static long tapi_control_ioctl_link_alloc(struct tapi_control_file *tctrl, unsigned long arg) { struct tapi_link *link; struct tapi_endpoint *ep1, *ep2; ep1 = tapi_lookup_endpoint(tctrl->tdev, arg >> 16); ep2 = tapi_lookup_endpoint(tctrl->tdev, arg & 0xffff); link = tapi_link_alloc(tctrl->tdev, ep1, ep2); if (IS_ERR(link)) return PTR_ERR(link); list_add_tail(&link->head, &tctrl->links); return link->id; } struct tapi_link *tapi_control_lookup_link(struct tapi_control_file *tctrl, unsigned int id) { struct tapi_link *link; list_for_each_entry(link, &tctrl->links, head) { if (link->id == id) return link; } return NULL; } static long tapi_control_ioctl_link_free(struct tapi_control_file *tctrl, unsigned long arg) { struct tapi_link *link = tapi_control_lookup_link(tctrl, arg); if (!link) return -ENOENT; tapi_link_free(tctrl->tdev, link); list_del(&link->head); return 0; } static long tapi_control_ioctl_link_enable(struct tapi_control_file *tctrl, unsigned long arg) { struct tapi_link *link = tapi_control_lookup_link(tctrl, arg); if (!link) return -ENOENT; return tapi_link_enable(tctrl->tdev, link); } static long tapi_control_ioctl_link_disable(struct tapi_control_file *tctrl, unsigned long arg) { struct tapi_link *link = tapi_control_lookup_link(tctrl, arg); if (!link) return -ENOENT; return tapi_link_disable(tctrl->tdev, link); } static long tapi_control_ioctl_sync(struct tapi_control_file *tctrl) { return tapi_sync(tctrl->tdev); } static long tapi_control_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret; struct tapi_control_file *tctrl = file->private_data; switch (cmd) { case TAPI_CONTROL_IOCTL_LINK_ALLOC: ret = tapi_control_ioctl_link_alloc(tctrl, arg); break; case TAPI_CONTROL_IOCTL_LINK_FREE: ret = tapi_control_ioctl_link_free(tctrl, arg); break; case TAPI_CONTROL_IOCTL_LINK_ENABLE: ret = tapi_control_ioctl_link_enable(tctrl, arg); break; case TAPI_CONTROL_IOCTL_LINK_DISABLE: ret = tapi_control_ioctl_link_disable(tctrl, arg); break; case TAPI_CONTROL_IOCTL_SYNC: ret = tapi_control_ioctl_sync(tctrl); break; default: return -EINVAL; } return ret; } static const struct file_operations tapi_control_file_ops = { .owner = THIS_MODULE, .open = tapi_control_open, .release = tapi_control_release, .unlocked_ioctl = tapi_control_ioctl, }; int tapi_register_control_device(struct tapi_device* tdev) { dev_set_name(&tdev->control_dev.dev, "tapi%uC", tdev->id); return tapi_char_device_register(tdev, &tdev->control_dev, &tapi_control_file_ops); }