From a8b2a07f375edefec242de9f20d4aefafa927517 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 3 Nov 2010 19:12:34 +0000 Subject: * adds a rewrite of the tapi drivers + sip app. this is the result of lars' gsoc 2010 project, Thanks ! SVN-Revision: 23840 --- package/lqtapi/src/tapi/Makefile | 7 + package/lqtapi/src/tapi/tapi-control.c | 193 +++++++++++++++++++++++ package/lqtapi/src/tapi/tapi-core.c | 250 ++++++++++++++++++++++++++++++ package/lqtapi/src/tapi/tapi-input.c | 99 ++++++++++++ package/lqtapi/src/tapi/tapi-nl.c | 62 ++++++++ package/lqtapi/src/tapi/tapi-port.c | 82 ++++++++++ package/lqtapi/src/tapi/tapi-stream.c | 201 ++++++++++++++++++++++++ package/lqtapi/src/tapi/tapi-sysfs-port.c | 107 +++++++++++++ 8 files changed, 1001 insertions(+) create mode 100644 package/lqtapi/src/tapi/Makefile create mode 100644 package/lqtapi/src/tapi/tapi-control.c create mode 100644 package/lqtapi/src/tapi/tapi-core.c create mode 100644 package/lqtapi/src/tapi/tapi-input.c create mode 100644 package/lqtapi/src/tapi/tapi-nl.c create mode 100644 package/lqtapi/src/tapi/tapi-port.c create mode 100644 package/lqtapi/src/tapi/tapi-stream.c create mode 100644 package/lqtapi/src/tapi/tapi-sysfs-port.c (limited to 'package/lqtapi/src/tapi') diff --git a/package/lqtapi/src/tapi/Makefile b/package/lqtapi/src/tapi/Makefile new file mode 100644 index 0000000000..2992d4b3a3 --- /dev/null +++ b/package/lqtapi/src/tapi/Makefile @@ -0,0 +1,7 @@ + +tapi-objs := tapi-core.o tapi-port.o tapi-input.o +tapi-objs += tapi-control.o +tapi-objs += tapi-stream.o +tapi-objs += tapi-sysfs-port.o + +obj-m += tapi.o diff --git a/package/lqtapi/src/tapi/tapi-control.c b/package/lqtapi/src/tapi/tapi-control.c new file mode 100644 index 0000000000..a4f245e92f --- /dev/null +++ b/package/lqtapi/src/tapi/tapi-control.c @@ -0,0 +1,193 @@ +#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); +} diff --git a/package/lqtapi/src/tapi/tapi-core.c b/package/lqtapi/src/tapi/tapi-core.c new file mode 100644 index 0000000000..2e112a3769 --- /dev/null +++ b/package/lqtapi/src/tapi/tapi-core.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include + +#include +#include + +#include + + +void tapi_alloc_input(struct tapi_device *tdev, struct tapi_port *port); +int tapi_register_port_device(struct tapi_device* tdev, struct tapi_port *port); +int tapi_register_stream_device(struct tapi_device* tdev); +int tapi_register_control_device(struct tapi_device* tdev); + +static struct class *tapi_class; +static int tapi_major; + +#define TAPI_MAX_MINORS 255 + +static bool tapi_minors[TAPI_MAX_MINORS]; + +static int tapi_get_free_minor(void) +{ + int i; + for (i = 0; i < TAPI_MAX_MINORS; ++i) { + if (!tapi_minors[i]) { + tapi_minors[i] = true; + return i; + } + } + + return -1; +} + +/* +int tapi_port_send_dtmf_events(struct tapi_device *tdev, unsigned int port, struct tapi_dtmf *, size_t num_events, unsigned int dealy) +{ +} +EXPORT_SYMBOL_GPL(tapi_port_send_dtmf_events); +*/ + +void tapi_report_hook_event(struct tapi_device *tdev, struct tapi_port *port, + bool on) +{ + struct tapi_event event; + event.type = TAPI_EVENT_TYPE_HOOK; + event.port = port->id; + event.hook.on = on; + + tapi_report_event(tdev, &event); +} +EXPORT_SYMBOL_GPL(tapi_report_hook_event); + +void tapi_report_dtmf_event(struct tapi_device *tdev, struct tapi_port *port, + unsigned char code) +{ + struct tapi_event event; + event.type = TAPI_EVENT_TYPE_DTMF; + event.port = port->id; + event.dtmf.code = code; + + tapi_report_event(tdev, &event); +} +EXPORT_SYMBOL_GPL(tapi_report_dtmf_event); + +struct tapi_stream *tapi_stream_alloc(struct tapi_device *tdev) +{ + struct tapi_stream *stream; + printk("tdev %p\n", tdev); + + if (!tdev->ops || !tdev->ops->stream_alloc) + return ERR_PTR(-ENOSYS); + + stream = tdev->ops->stream_alloc(tdev); + printk("stream %p\n", stream); + if (IS_ERR(stream)) + return stream; + + stream->id = atomic_inc_return(&tdev->stream_id) - 1; + stream->ep.id = stream->id; + +/* mutex_lock(&tdev->lock);*/ + list_add_tail(&stream->head, &tdev->streams); +/* mutex_unlock(&tdev->lock);*/ + + return stream; +} +EXPORT_SYMBOL_GPL(tapi_stream_alloc); + +void tapi_stream_free(struct tapi_device *tdev, struct tapi_stream *stream) +{ + mutex_lock(&tdev->lock); + list_del(&stream->head); + mutex_unlock(&tdev->lock); + + tdev->ops->stream_free(tdev, stream); +} +EXPORT_SYMBOL_GPL(tapi_stream_free); + +struct tapi_link *tapi_link_alloc(struct tapi_device *tdev, + struct tapi_endpoint *ep1, struct tapi_endpoint *ep2) +{ + struct tapi_link *link; + + if (!tdev->ops || !tdev->ops->link_alloc) + return ERR_PTR(-ENOSYS); + + link = tdev->ops->link_alloc(tdev, ep1, ep2); + if (IS_ERR(link)) + return link; + + link->id = atomic_inc_return(&tdev->link_id) - 1; + +/* + mutex_lock(&tdev->lock); + list_add_tail(&link->head, &tdev->links); + mutex_unlock(&tdev->lock); +*/ + return link; +} +EXPORT_SYMBOL_GPL(tapi_link_alloc); + +void tapi_link_free(struct tapi_device *tdev, struct tapi_link *link) +{ +/* + mutex_lock(&tdev->lock); + list_del(&link->head); + mutex_unlock(&tdev->lock); +*/ + tdev->ops->link_free(tdev, link); +} +EXPORT_SYMBOL_GPL(tapi_link_free); + +int tapi_char_device_register(struct tapi_device *tdev, + struct tapi_char_device *tchrdev, const struct file_operations *fops) +{ + int ret; + struct device *dev = &tchrdev->dev; + dev_t devt; + int minor = tapi_get_free_minor(); + + devt = MKDEV(tapi_major, minor); + + dev->devt = devt; + dev->class = tapi_class; + dev->parent = &tdev->dev; + + tchrdev->tdev = tdev; + + cdev_init(&tchrdev->cdev, fops); + tchrdev->cdev.owner = THIS_MODULE; + ret = cdev_add(&tchrdev->cdev, devt, 1); + if (ret) + return ret; + + ret = device_register(&tchrdev->dev); + if (ret) + goto err_cdev_del; + + return 0; + +err_cdev_del: + cdev_del(&tchrdev->cdev); + + return ret; +} + +int tapi_device_register(struct tapi_device *tdev, const char *name, + struct device *parent) +{ + static atomic_t tapi_device_id = ATOMIC_INIT(0); + int ret, i; + + tdev->dev.class = tapi_class; + tdev->dev.parent = parent; + dev_set_name(&tdev->dev, "%s", name); + + ret = device_register(&tdev->dev); + if (ret) + return ret; + + tdev->id = atomic_inc_return(&tapi_device_id) - 1; + + mutex_init(&tdev->lock); + INIT_LIST_HEAD(&tdev->streams); + INIT_LIST_HEAD(&tdev->links); + atomic_set(&tdev->link_id, 0); + atomic_set(&tdev->stream_id, tdev->num_ports); + + tapi_register_stream_device(tdev); + tapi_register_control_device(tdev); + + for (i = 0; i < tdev->num_ports; ++i) { + tapi_port_alloc(tdev, i); + tapi_alloc_input(tdev, &tdev->ports[i]); + tapi_register_port_device(tdev, &tdev->ports[i]); + tdev->ports[i].id = i; + tdev->ports[i].ep.id = i; + } + + return 0; +} +EXPORT_SYMBOL_GPL(tapi_device_register); + +void tapi_device_unregister(struct tapi_device *tdev) +{ + device_unregister(&tdev->dev); +} +EXPORT_SYMBOL_GPL(tapi_device_unregister); + +static int __init tapi_class_init(void) +{ + int ret; + dev_t dev; + + tapi_class = class_create(THIS_MODULE, "tapi"); + + if (IS_ERR(tapi_class)) { + ret = PTR_ERR(tapi_class); + printk(KERN_ERR "tapi: Failed to create device class: %d\n", ret); + goto err; + } + + ret = alloc_chrdev_region(&dev, 0, TAPI_MAX_MINORS, "tapi"); + if (ret) { + printk(KERN_ERR "tapi: Failed to allocate chrdev region: %d\n", ret); + goto err_class_destory; + } + tapi_major = MAJOR(dev); + + return 0; +err_class_destory: + class_destroy(tapi_class); +err: + return ret; +} +subsys_initcall(tapi_class_init); + +static void __exit tapi_class_exit(void) +{ + unregister_chrdev_region(MKDEV(tapi_major, 0), TAPI_MAX_MINORS); + class_destroy(tapi_class); +} +module_exit(tapi_class_exit); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("TAPI class"); +MODULE_LICENSE("GPL"); diff --git a/package/lqtapi/src/tapi/tapi-input.c b/package/lqtapi/src/tapi/tapi-input.c new file mode 100644 index 0000000000..999aaefa98 --- /dev/null +++ b/package/lqtapi/src/tapi/tapi-input.c @@ -0,0 +1,99 @@ + +#include + +#include + +static unsigned short tapi_keycodes[] = { + [0] = KEY_NUMERIC_0, + [1] = KEY_NUMERIC_1, + [2] = KEY_NUMERIC_2, + [3] = KEY_NUMERIC_3, + [4] = KEY_NUMERIC_4, + [5] = KEY_NUMERIC_5, + [6] = KEY_NUMERIC_6, + [7] = KEY_NUMERIC_7, + [8] = KEY_NUMERIC_8, + [9] = KEY_NUMERIC_9, + [10] = KEY_NUMERIC_STAR, + [11] = KEY_NUMERIC_POUND, + [12] = KEY_ENTER, + [13] = KEY_ESC, +}; + +static int tapi_input_event(struct input_dev *input, unsigned int type, + unsigned int code, int value) +{ + struct tapi_device *tdev = dev_to_tapi(input->dev.parent); + struct tapi_port *port = input_get_drvdata(input); + + + if (type != EV_SND || code != SND_BELL) + return -EINVAL; + + tapi_port_set_ring(tdev, port, value); + + return 0; +} + +void tapi_alloc_input(struct tapi_device *tdev, struct tapi_port *port) +{ + struct input_dev *input; + int i; + char *phys; + + input = input_allocate_device(); + + phys = kzalloc(sizeof("tapi/input000"), GFP_KERNEL); + sprintf(phys, "tapi/input%d", port->id); + + input->name = "tapi"; + input->phys = phys; + input->id.bustype = BUS_HOST; + input->dev.parent = &tdev->dev; + input->evbit[0] = BIT(EV_KEY) | BIT(EV_SND); + input->sndbit[0] = BIT(SND_BELL); + + input->event = tapi_input_event; + + input->keycodesize = sizeof(unsigned short); + input->keycodemax = ARRAY_SIZE(tapi_keycodes); + input->keycode = tapi_keycodes; + + port->input = input; + + for (i = 0; i < ARRAY_SIZE(tapi_keycodes); ++i) + __set_bit(tapi_keycodes[i], input->keybit); + + input_set_drvdata(input, port); + input_register_device(input); +} + +void tapi_report_event(struct tapi_device *tdev, + struct tapi_event *event) +{ + unsigned short key_code; + struct input_dev *input; + + if (!tdev || !tdev->ports) + return; + + switch (event->type) { + case TAPI_EVENT_TYPE_HOOK: + if (event->hook.on) + key_code = KEY_ENTER; + else + key_code = KEY_ESC; + break; + case TAPI_EVENT_TYPE_DTMF: + key_code = tapi_keycodes[event->dtmf.code]; + break; + default: + return; + } + + input = tdev->ports[event->port].input; + input_report_key(input, key_code, 1); + input_sync(input); + input_report_key(input, key_code, 0); + input_sync(input); +} diff --git a/package/lqtapi/src/tapi/tapi-nl.c b/package/lqtapi/src/tapi/tapi-nl.c new file mode 100644 index 0000000000..c066742b65 --- /dev/null +++ b/package/lqtapi/src/tapi/tapi-nl.c @@ -0,0 +1,62 @@ + +static struct tapi_attr default_port[] = { + [PORTS] = { + .type = TAPI_TYPE_PORTS, + .name = "ports", + .description = "foobar", + .set = tapi_set_ports, + .get = tapi_get_ports, + }, +}; + +static const struct nla_policy tapi_policy[] = { + [TAPI_ATTR_ID] = { .type = NLA_U32 }, + [TAPI_ATTR_PORT] = { .type = NLA_U32 }, + [TAPI_ATTR_ENDPOINT] = { .type = NLA_U32 }, + [TAPI_ATTR_STREAM] = { .type = NLA_U32 } +}; + +static const struct nla_policy tapi_port_policy[] = { + [TAPI_PORT_ID] = { .type = NLA_U32 }, +}; + +static const struct nla_policy tapi_endpoint_policy[] = { + [TAPI_ENDPOINT_ID] = { .type = NLA_U32 }, +}; + +static const struct nla_policy tapi_stream_policy[] = { + [TAPI_STREAM_ID] = { .type = NLA_U32 }, +}; + +static struct genl_family tapi_nl_family = { + .id = GENL_ID_GENERATE, + .name = "tapi", + .hdrsize = 0, + .version = 1, + .maxattr = ARRAY_SIZE(tapi_policy), +}; + + + +static struct genl_ops tapi_nl_ops[] = { + TAPI_NL_OP(TAPI_CMD_LIST, list_attr), + +}; + +static int __init tapi_nl_init(void) +{ + ret = genl_unregister_family(&tapi_nl_family); + if (ret) + return ret; + + genl_register_ops(&tapi_nl_family, tapi_nl_ops); + + return 0; +} +module_init(tapi_nl_init); + +static void __exit tapi_nl_exit(void) +{ + genl_unregister_family(&tapi_nl_family); +} + diff --git a/package/lqtapi/src/tapi/tapi-port.c b/package/lqtapi/src/tapi/tapi-port.c new file mode 100644 index 0000000000..0ef9555676 --- /dev/null +++ b/package/lqtapi/src/tapi/tapi-port.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +static inline struct tapi_port *tapi_char_device_to_port(struct tapi_char_device *chrdev) +{ + return container_of(chrdev, struct tapi_port, chrdev); +} + +static int tapi_port_open(struct inode *inode, struct file *file) +{ + struct tapi_device *tdev = cdev_to_tapi_char_device(inode->i_cdev)->tdev; + + get_device(&tdev->dev); + file->private_data = cdev_to_tapi_char_device(inode->i_cdev); + + return 0; +} + +static int tapi_port_release(struct inode *inode, struct file *file) +{ + struct tapi_device *tdev = cdev_to_tapi_char_device(inode->i_cdev)->tdev; + + put_device(&tdev->dev); + + return 0; +} + +static long tapi_port_ioctl_get_endpoint(struct tapi_device *tdev, + struct tapi_port *port, unsigned long arg) +{ + return port->ep.id; +} + +static long tapi_port_ioctl_set_ring(struct tapi_device *tdev, + struct tapi_port *port, unsigned long arg) +{ + tapi_port_set_ring(tdev, port, arg); + return 0; +} + +static long tapi_port_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + struct tapi_char_device *tchrdev = file->private_data; + struct tapi_device *tdev = tchrdev->tdev; + struct tapi_port *port = tapi_char_device_to_port(tchrdev); + + switch (cmd) { + case TAPI_PORT_IOCTL_GET_ENDPOINT: + ret = tapi_port_ioctl_get_endpoint(tdev, port, arg); + break; + case TAPI_PORT_IOCTL_SET_RING: + ret = tapi_port_ioctl_set_ring(tdev, port, arg); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct file_operations tapi_port_file_ops = { + .owner = THIS_MODULE, + .open = tapi_port_open, + .release = tapi_port_release, + .unlocked_ioctl = tapi_port_ioctl, +}; + +int tapi_register_port_device(struct tapi_device* tdev, struct tapi_port *port) +{ + dev_set_name(&port->chrdev.dev, "tapi%uP%u", tdev->id, port->id); + return tapi_char_device_register(tdev, &port->chrdev, &tapi_port_file_ops); +} diff --git a/package/lqtapi/src/tapi/tapi-stream.c b/package/lqtapi/src/tapi/tapi-stream.c new file mode 100644 index 0000000000..7277a5c61c --- /dev/null +++ b/package/lqtapi/src/tapi/tapi-stream.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +struct tapi_stream_file { + struct tapi_device *tdev; + struct tapi_stream *stream; +}; + +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_stream_open(struct inode *inode, struct file *file) +{ + int ret; + struct tapi_device *tdev = inode_to_tdev(inode); + struct tapi_stream_file *stream; + + get_device(&tdev->dev); + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) { + ret = -ENOMEM; + goto err_put; + } + + stream->stream = tapi_stream_alloc(tdev); + if (IS_ERR(stream->stream)) { + ret = PTR_ERR(stream->stream); + goto err_free; + } + stream->tdev = tdev; + + init_waitqueue_head(&stream->stream->recv_wait); + skb_queue_head_init(&stream->stream->recv_queue); + + file->private_data = stream; + + return 0; + +err_free: + kfree(stream); +err_put: + put_device(&tdev->dev); + return ret; +} + +static int tapi_stream_release(struct inode *inode, struct file *file) +{ + struct tapi_stream_file *stream = file->private_data; + + if (stream) { + tapi_stream_free(stream->tdev, stream->stream); + put_device(&stream->tdev->dev); + kfree(stream); + } + + return 0; +} + +static long tapi_stream_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct tapi_stream_file *stream = file->private_data; + struct tapi_device *tdev = stream->tdev; + + switch (cmd) { + case TAPI_STREAM_IOCTL_GET_ENDPOINT: + ret = stream->stream->ep.id; + break; + case TAPI_STREAM_IOCTL_CONFIGURE: + break; + case TAPI_STREAM_IOCTL_START: + ret = tapi_stream_start(tdev, stream->stream); + break; + case TAPI_STREAM_IOCTL_STOP: + ret = tapi_stream_stop(tdev, stream->stream); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static unsigned int tapi_stream_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tapi_stream_file *stream = file->private_data; + int ret; + + poll_wait(file, &stream->stream->recv_wait, wait); + + ret = POLLOUT; + + if (!skb_queue_empty(&stream->stream->recv_queue)) + ret |= POLLIN; + + return ret; +} + +static ssize_t tapi_stream_read(struct file *file, char __user *buffer, + size_t count, loff_t *offset) +{ + struct tapi_stream_file *stream = file->private_data; + struct sk_buff *skb; + + skb = skb_dequeue(&stream->stream->recv_queue); + if (!skb) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + do { + interruptible_sleep_on(&stream->stream->recv_wait); + skb = skb_dequeue(&stream->stream->recv_queue); + } while (skb == NULL && !signal_pending(current)); + + if (skb == NULL) + return -ERESTARTNOHAND; + } + + if (skb->len > count) { + skb_queue_head(&stream->stream->recv_queue, skb); + return -EMSGSIZE; + } + + if (copy_to_user(buffer, skb->data, skb->len)) { + skb_queue_head(&stream->stream->recv_queue, skb); + return -EFAULT; + } + + count = skb->len; + + kfree_skb(skb); + + return count; +} + +static ssize_t tapi_stream_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct tapi_stream_file *stream = file->private_data; + struct tapi_device *tdev = stream->tdev; + struct sk_buff *skb; + + if (count == 0) + return 0; + + skb = alloc_skb(count, GFP_USER); + if (!skb) + return -ENOMEM; + + if (copy_from_user(skb_put(skb, count), buffer, count)) { + kfree_skb(skb); + return -EFAULT; + } + + tdev->ops->stream_send(tdev, stream->stream, skb); + + return count; +} + +static const struct file_operations tapi_stream_file_ops = { + .owner = THIS_MODULE, + .read = tapi_stream_read, + .write = tapi_stream_write, + .open = tapi_stream_open, + .release = tapi_stream_release, + .poll = tapi_stream_poll, + .unlocked_ioctl = tapi_stream_ioctl, +}; + +int tapi_register_stream_device(struct tapi_device* tdev) +{ + dev_set_name(&tdev->stream_dev.dev, "tapi%uS", tdev->id); + return tapi_char_device_register(tdev, &tdev->stream_dev, &tapi_stream_file_ops); +} + +int tapi_stream_recv(struct tapi_device *tdev, struct tapi_stream * stream, + struct sk_buff *skb) +{ + skb_queue_tail(&stream->recv_queue, skb); + wake_up(&stream->recv_wait); + + return 0; +} +EXPORT_SYMBOL_GPL(tapi_stream_recv); diff --git a/package/lqtapi/src/tapi/tapi-sysfs-port.c b/package/lqtapi/src/tapi/tapi-sysfs-port.c new file mode 100644 index 0000000000..f6e5ee0661 --- /dev/null +++ b/package/lqtapi/src/tapi/tapi-sysfs-port.c @@ -0,0 +1,107 @@ +#include +#include +#include + +#include +#include + +struct tapi_sysfs_port { + struct tapi_device *tdev; + unsigned int id; + struct kobject kobj; +}; + +struct tapi_sysfs_entry { + ssize_t (*show)(struct tapi_device *, unsigned int port, char *); + ssize_t (*store)(struct tapi_device *, unsigned int port, const char *, size_t); + struct attribute attr; +}; + +static ssize_t tapi_port_store(struct kobject *kobj, struct attribute *attr, + const char *s, size_t len) +{ + struct tapi_sysfs_port *port = container_of(kobj, struct tapi_sysfs_port, kobj); + struct tapi_sysfs_entry *entry = container_of(attr, struct tapi_sysfs_entry, + attr); + + if (!entry->store) + return -ENOSYS; + + return entry->store(port->tdev, port->id, s, len); +} + +static ssize_t tapi_port_show(struct kobject *kobj, struct attribute *attr, + char *s) +{ + return -ENOSYS; +} + +#define TAPI_PORT_ATTR(_name, _mode, _show, _store) \ + struct tapi_sysfs_entry tapi_port_ ## _name ## _attr = \ + __ATTR(_name, _mode, _show, _store) + +static int tapi_port_store_ring(struct tapi_device *tdev, unsigned int port, + const char *s, size_t len) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(s, 10, &val); + + if (ret) + return ret; + + ret = tapi_port_set_ring(tdev, &tdev->ports[port], val); + if (ret) + return ret; + return len; +} + +static TAPI_PORT_ATTR(ring, 0644, NULL, tapi_port_store_ring); + +static struct attribute *tapi_port_default_attrs[] = { + &tapi_port_ring_attr.attr, + NULL, +}; + +static void tapi_port_free(struct kobject *kobj) +{ + struct tapi_sysfs_port *port = container_of(kobj, struct tapi_sysfs_port, kobj); + kfree(port); +} + +static struct sysfs_ops tapi_port_sysfs_ops = { + .show = tapi_port_show, + .store = tapi_port_store, +}; + +static struct kobj_type tapi_port_ktype = { + .release = tapi_port_free, + .sysfs_ops = &tapi_port_sysfs_ops, + .default_attrs = tapi_port_default_attrs, +}; + +struct tapi_sysfs_port *tapi_port_alloc(struct tapi_device *tdev, unsigned int id) +{ + struct tapi_sysfs_port *port; + int ret; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + port->tdev = tdev; + port->id = id; + + ret = kobject_init_and_add(&port->kobj, &tapi_port_ktype, &tdev->dev.kobj, + "port%d", id); + if (ret) { + kfree(port); + return ERR_PTR(ret); + } + + return port; +} + +void tapi_port_delete(struct tapi_sysfs_port *port) +{ + kobject_del(&port->kobj); + kobject_put(&port->kobj); +} -- cgit v1.2.3