summaryrefslogtreecommitdiffstats
path: root/package/lqtapi/src/tapi/tapi-stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'package/lqtapi/src/tapi/tapi-stream.c')
-rw-r--r--package/lqtapi/src/tapi/tapi-stream.c201
1 files changed, 201 insertions, 0 deletions
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);