summaryrefslogtreecommitdiffstats
path: root/package/lqtapi/src/tapi
diff options
context:
space:
mode:
Diffstat (limited to 'package/lqtapi/src/tapi')
-rw-r--r--package/lqtapi/src/tapi/Makefile7
-rw-r--r--package/lqtapi/src/tapi/tapi-control.c193
-rw-r--r--package/lqtapi/src/tapi/tapi-core.c250
-rw-r--r--package/lqtapi/src/tapi/tapi-input.c99
-rw-r--r--package/lqtapi/src/tapi/tapi-nl.c62
-rw-r--r--package/lqtapi/src/tapi/tapi-port.c82
-rw-r--r--package/lqtapi/src/tapi/tapi-stream.c201
-rw-r--r--package/lqtapi/src/tapi/tapi-sysfs-port.c107
8 files changed, 1001 insertions, 0 deletions
diff --git a/package/lqtapi/src/tapi/Makefile b/package/lqtapi/src/tapi/Makefile
new file mode 100644
index 000000000..2992d4b3a
--- /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 000000000..a4f245e92
--- /dev/null
+++ b/package/lqtapi/src/tapi/tapi-control.c
@@ -0,0 +1,193 @@
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/tapi/tapi.h>
+#include <linux/tapi/tapi-ioctl.h>
+
+/* 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 000000000..2e112a376
--- /dev/null
+++ b/package/lqtapi/src/tapi/tapi-core.c
@@ -0,0 +1,250 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/list.h>
+
+#include <linux/cdev.h>
+#include <linux/err.h>
+
+#include <linux/tapi/tapi.h>
+
+
+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 <lars@metafoo.de>");
+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 000000000..999aaefa9
--- /dev/null
+++ b/package/lqtapi/src/tapi/tapi-input.c
@@ -0,0 +1,99 @@
+
+#include <linux/tapi/tapi.h>
+
+#include <linux/input.h>
+
+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 000000000..c066742b6
--- /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 000000000..0ef955567
--- /dev/null
+++ b/package/lqtapi/src/tapi/tapi-port.c
@@ -0,0 +1,82 @@
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/tapi/tapi.h>
+#include <linux/tapi/tapi-ioctl.h>
+
+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 000000000..7277a5c61
--- /dev/null
+++ b/package/lqtapi/src/tapi/tapi-stream.c
@@ -0,0 +1,201 @@
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+
+#include <linux/tapi/tapi.h>
+#include <linux/tapi/tapi-ioctl.h>
+
+
+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 000000000..f6e5ee066
--- /dev/null
+++ b/package/lqtapi/src/tapi/tapi-sysfs-port.c
@@ -0,0 +1,107 @@
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+
+#include <linux/err.h>
+#include <linux/tapi/tapi.h>
+
+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);
+}