Files
openwrt_NSS_ACW/package/lqtapi/src/tapi/tapi-stream.c

202 lines
4.2 KiB
C

#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);