a4f245e92fc13b2716bf58adf60b69c2d5af6df1
[openwrt.git] / package / lqtapi / src / tapi / tapi-control.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
9 #include <linux/tapi/tapi.h>
10 #include <linux/tapi/tapi-ioctl.h>
11
12 /* FIXME Does it acutally make sense to allow more then one application at a
13  * time to open the control device? For example calling sync from one app will
14  * also sync all others. */
15
16 struct tapi_control_file {
17         struct tapi_device *tdev;
18         struct list_head links;
19 };
20
21 static struct tapi_endpoint *tapi_lookup_endpoint(struct tapi_device *tdev,
22         unsigned int ep_id)
23 {
24         struct tapi_stream *stream;
25
26         if (ep_id < tdev->num_ports)
27                 return &tdev->ports[ep_id].ep;
28
29         list_for_each_entry(stream, &tdev->streams, head) {
30                 if (stream->ep.id == ep_id)
31                         return &stream->ep;
32         }
33
34         return ERR_PTR(-ENOENT);
35 }
36
37 static inline struct tapi_device *inode_to_tdev(struct inode *inode)
38 {
39         return container_of(inode->i_cdev, struct tapi_char_device, cdev)->tdev;
40 }
41
42 static int tapi_control_open(struct inode *inode, struct file *file)
43 {
44         int ret;
45         struct tapi_device *tdev = inode_to_tdev(inode);
46         struct tapi_control_file *tctrl;
47
48         get_device(&tdev->dev);
49
50         tctrl = kzalloc(sizeof(*tctrl), GFP_KERNEL);
51         if (!tctrl) {
52                 ret = -ENOMEM;
53                 goto err_put_device;
54         }
55
56         INIT_LIST_HEAD(&tctrl->links);
57         tctrl->tdev = tdev;
58
59         file->private_data = tctrl;
60
61         return 0;
62
63 err_put_device:
64         put_device(&tdev->dev);
65
66         return ret;
67 }
68
69 static int tapi_control_release(struct inode *inode, struct file *file)
70 {
71         struct tapi_control_file *tctrl = file->private_data;
72         struct tapi_link *link;
73
74         if (tctrl) {
75                 list_for_each_entry(link, &tctrl->links, head)
76                         tapi_link_free(tctrl->tdev, link);
77
78                 put_device(&tctrl->tdev->dev);
79         }
80
81         return 0;
82 }
83
84 static long tapi_control_ioctl_link_alloc(struct tapi_control_file *tctrl,
85         unsigned long arg)
86 {
87         struct tapi_link *link;
88         struct tapi_endpoint *ep1, *ep2;
89
90         ep1 = tapi_lookup_endpoint(tctrl->tdev, arg >> 16);
91         ep2 = tapi_lookup_endpoint(tctrl->tdev, arg & 0xffff);
92
93         link = tapi_link_alloc(tctrl->tdev, ep1, ep2);
94         if (IS_ERR(link))
95                 return PTR_ERR(link);
96
97         list_add_tail(&link->head, &tctrl->links);
98
99         return link->id;
100 }
101
102 struct tapi_link *tapi_control_lookup_link(struct tapi_control_file *tctrl,
103         unsigned int id)
104 {
105         struct tapi_link *link;
106
107         list_for_each_entry(link, &tctrl->links, head) {
108                 if (link->id == id)
109                         return link;
110         }
111
112         return NULL;
113 }
114
115 static long tapi_control_ioctl_link_free(struct tapi_control_file *tctrl,
116         unsigned long arg)
117 {
118         struct tapi_link *link = tapi_control_lookup_link(tctrl, arg);
119         if (!link)
120                 return -ENOENT;
121
122         tapi_link_free(tctrl->tdev, link);
123         list_del(&link->head);
124
125         return 0;
126 }
127
128 static long tapi_control_ioctl_link_enable(struct tapi_control_file *tctrl,
129         unsigned long arg)
130 {
131         struct tapi_link *link = tapi_control_lookup_link(tctrl, arg);
132         if (!link)
133                 return -ENOENT;
134
135         return tapi_link_enable(tctrl->tdev, link);
136 }
137
138 static long tapi_control_ioctl_link_disable(struct tapi_control_file *tctrl,
139         unsigned long arg)
140 {
141         struct tapi_link *link = tapi_control_lookup_link(tctrl, arg);
142         if (!link)
143                 return -ENOENT;
144
145         return tapi_link_disable(tctrl->tdev, link);
146 }
147
148 static long tapi_control_ioctl_sync(struct tapi_control_file *tctrl)
149 {
150         return tapi_sync(tctrl->tdev);
151 }
152
153 static long tapi_control_ioctl(struct file *file, unsigned int cmd,
154         unsigned long arg)
155 {
156         int ret;
157         struct tapi_control_file *tctrl = file->private_data;
158
159         switch (cmd) {
160         case TAPI_CONTROL_IOCTL_LINK_ALLOC:
161                 ret = tapi_control_ioctl_link_alloc(tctrl, arg);
162                 break;
163         case TAPI_CONTROL_IOCTL_LINK_FREE:
164                 ret = tapi_control_ioctl_link_free(tctrl, arg);
165                 break;
166         case TAPI_CONTROL_IOCTL_LINK_ENABLE:
167                 ret = tapi_control_ioctl_link_enable(tctrl, arg);
168                 break;
169         case TAPI_CONTROL_IOCTL_LINK_DISABLE:
170                 ret = tapi_control_ioctl_link_disable(tctrl, arg);
171                 break;
172         case TAPI_CONTROL_IOCTL_SYNC:
173                 ret = tapi_control_ioctl_sync(tctrl);
174                 break;
175         default:
176                 return -EINVAL;
177         }
178
179         return ret;
180 }
181
182 static const struct file_operations tapi_control_file_ops = {
183         .owner = THIS_MODULE,
184         .open = tapi_control_open,
185         .release = tapi_control_release,
186         .unlocked_ioctl = tapi_control_ioctl,
187 };
188
189 int tapi_register_control_device(struct tapi_device* tdev)
190 {
191         dev_set_name(&tdev->control_dev.dev, "tapi%uC", tdev->id);
192         return tapi_char_device_register(tdev, &tdev->control_dev, &tapi_control_file_ops);
193 }