package/swconfig: add sanity checks to prevent a segfault
[openwrt.git] / package / lqtapi / src / tapi / tapi-stream.c
1 #include <linux/cdev.h>
2 #include <linux/device.h>
3 #include <linux/fs.h>
4 #include <linux/list.h>
5 #include <linux/kernel.h>
6 #include <linux/module.h>
7 #include <linux/slab.h>
8 #include <linux/signal.h>
9 #include <linux/sched.h>
10 #include <linux/poll.h>
11
12 #include <linux/tapi/tapi.h>
13 #include <linux/tapi/tapi-ioctl.h>
14
15
16 struct tapi_stream_file {
17 struct tapi_device *tdev;
18 struct tapi_stream *stream;
19 };
20
21 static inline struct tapi_device *inode_to_tdev(struct inode *inode)
22 {
23 return container_of(inode->i_cdev, struct tapi_char_device, cdev)->tdev;
24 }
25
26 static int tapi_stream_open(struct inode *inode, struct file *file)
27 {
28 int ret;
29 struct tapi_device *tdev = inode_to_tdev(inode);
30 struct tapi_stream_file *stream;
31
32 get_device(&tdev->dev);
33
34 stream = kzalloc(sizeof(*stream), GFP_KERNEL);
35 if (!stream) {
36 ret = -ENOMEM;
37 goto err_put;
38 }
39
40 stream->stream = tapi_stream_alloc(tdev);
41 if (IS_ERR(stream->stream)) {
42 ret = PTR_ERR(stream->stream);
43 goto err_free;
44 }
45 stream->tdev = tdev;
46
47 init_waitqueue_head(&stream->stream->recv_wait);
48 skb_queue_head_init(&stream->stream->recv_queue);
49
50 file->private_data = stream;
51
52 return 0;
53
54 err_free:
55 kfree(stream);
56 err_put:
57 put_device(&tdev->dev);
58 return ret;
59 }
60
61 static int tapi_stream_release(struct inode *inode, struct file *file)
62 {
63 struct tapi_stream_file *stream = file->private_data;
64
65 if (stream) {
66 tapi_stream_free(stream->tdev, stream->stream);
67 put_device(&stream->tdev->dev);
68 kfree(stream);
69 }
70
71 return 0;
72 }
73
74 static long tapi_stream_ioctl(struct file *file, unsigned int cmd,
75 unsigned long arg)
76 {
77 int ret = 0;
78 struct tapi_stream_file *stream = file->private_data;
79 struct tapi_device *tdev = stream->tdev;
80
81 switch (cmd) {
82 case TAPI_STREAM_IOCTL_GET_ENDPOINT:
83 ret = stream->stream->ep.id;
84 break;
85 case TAPI_STREAM_IOCTL_CONFIGURE:
86 break;
87 case TAPI_STREAM_IOCTL_START:
88 ret = tapi_stream_start(tdev, stream->stream);
89 break;
90 case TAPI_STREAM_IOCTL_STOP:
91 ret = tapi_stream_stop(tdev, stream->stream);
92 break;
93 default:
94 ret = -EINVAL;
95 break;
96 }
97
98 return ret;
99 }
100
101 static unsigned int tapi_stream_poll(struct file *file, struct poll_table_struct *wait)
102 {
103 struct tapi_stream_file *stream = file->private_data;
104 int ret;
105
106 poll_wait(file, &stream->stream->recv_wait, wait);
107
108 ret = POLLOUT;
109
110 if (!skb_queue_empty(&stream->stream->recv_queue))
111 ret |= POLLIN;
112
113 return ret;
114 }
115
116 static ssize_t tapi_stream_read(struct file *file, char __user *buffer,
117 size_t count, loff_t *offset)
118 {
119 struct tapi_stream_file *stream = file->private_data;
120 struct sk_buff *skb;
121
122 skb = skb_dequeue(&stream->stream->recv_queue);
123 if (!skb) {
124 if (file->f_flags & O_NONBLOCK)
125 return -EAGAIN;
126
127 do {
128 interruptible_sleep_on(&stream->stream->recv_wait);
129 skb = skb_dequeue(&stream->stream->recv_queue);
130 } while (skb == NULL && !signal_pending(current));
131
132 if (skb == NULL)
133 return -ERESTARTNOHAND;
134 }
135
136 if (skb->len > count) {
137 skb_queue_head(&stream->stream->recv_queue, skb);
138 return -EMSGSIZE;
139 }
140
141 if (copy_to_user(buffer, skb->data, skb->len)) {
142 skb_queue_head(&stream->stream->recv_queue, skb);
143 return -EFAULT;
144 }
145
146 count = skb->len;
147
148 kfree_skb(skb);
149
150 return count;
151 }
152
153 static ssize_t tapi_stream_write(struct file *file, const char __user *buffer,
154 size_t count, loff_t *ppos)
155 {
156 struct tapi_stream_file *stream = file->private_data;
157 struct tapi_device *tdev = stream->tdev;
158 struct sk_buff *skb;
159
160 if (count == 0)
161 return 0;
162
163 skb = alloc_skb(count, GFP_USER);
164 if (!skb)
165 return -ENOMEM;
166
167 if (copy_from_user(skb_put(skb, count), buffer, count)) {
168 kfree_skb(skb);
169 return -EFAULT;
170 }
171
172 tdev->ops->stream_send(tdev, stream->stream, skb);
173
174 return count;
175 }
176
177 static const struct file_operations tapi_stream_file_ops = {
178 .owner = THIS_MODULE,
179 .read = tapi_stream_read,
180 .write = tapi_stream_write,
181 .open = tapi_stream_open,
182 .release = tapi_stream_release,
183 .poll = tapi_stream_poll,
184 .unlocked_ioctl = tapi_stream_ioctl,
185 };
186
187 int tapi_register_stream_device(struct tapi_device* tdev)
188 {
189 dev_set_name(&tdev->stream_dev.dev, "tapi%uS", tdev->id);
190 return tapi_char_device_register(tdev, &tdev->stream_dev, &tapi_stream_file_ops);
191 }
192
193 int tapi_stream_recv(struct tapi_device *tdev, struct tapi_stream * stream,
194 struct sk_buff *skb)
195 {
196 skb_queue_tail(&stream->recv_queue, skb);
197 wake_up(&stream->recv_wait);
198
199 return 0;
200 }
201 EXPORT_SYMBOL_GPL(tapi_stream_recv);
This page took 0.057125 seconds and 5 git commands to generate.