blob: 0c9e42838844f8b443cf3450d7b75aa79475dda7 [file] [log] [blame]
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -07001/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved.
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -08002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12#include <linux/kfifo.h>
13#include <linux/list.h>
14#include <linux/sched.h>
15#include <linux/uaccess.h>
16#include <linux/wait.h>
17#include "esoc.h"
18
19/**
20 * struct esoc_udev: Userspace char interface
21 * @dev: interface device.
22 * @req_fifio: fifo for clink requests.
23 * @req_wait: signal availability of request from clink
24 * @req_fifo_lock: serialize access to req fifo
25 * @evt_fito: fifo for clink events
26 * @evt_wait: signal availability of clink event
27 * @evt_fifo_lock: serialize access to event fifo
28 * @list: entry in esoc dev list.
29 * @clink: reference to contorl link
30 */
31struct esoc_udev {
32 struct device *dev;
33 struct kfifo req_fifo;
34 wait_queue_head_t req_wait;
35 spinlock_t req_fifo_lock;
36 struct kfifo evt_fifo;
37 wait_queue_head_t evt_wait;
38 spinlock_t evt_fifo_lock;
39 struct list_head list;
40 struct esoc_clink *clink;
41};
42
43/**
44 * struct esoc_uhandle: Userspace handle of esoc
45 * @esoc_clink: esoc control link.
46 * @eng: esoc engine for commands/ requests.
47 * @esoc_udev: user interface device.
48 * @req_eng_reg: indicates if engine is registered as request eng
49 * @cmd_eng_reg: indicates if engine is registered as cmd eng
50 */
51struct esoc_uhandle {
52 struct esoc_clink *esoc_clink;
53 struct esoc_eng eng;
54 struct esoc_udev *esoc_udev;
55 bool req_eng_reg;
56 bool cmd_eng_reg;
57};
58
59#define ESOC_MAX_MINOR 256
60#define ESOC_MAX_REQ 8
61#define ESOC_MAX_EVT 4
62
63static LIST_HEAD(esoc_udev_list);
64static DEFINE_SPINLOCK(esoc_udev_list_lock);
65struct class *esoc_class;
66static int esoc_major;
67
68static struct esoc_udev *get_free_esoc_udev(struct esoc_clink *esoc_clink)
69{
70 struct esoc_udev *esoc_udev;
71 int err;
72
73 if (esoc_clink->id > ESOC_MAX_MINOR) {
74 pr_err("too many esoc devices\n");
75 return ERR_PTR(-ENODEV);
76 }
77 esoc_udev = kzalloc(sizeof(*esoc_udev), GFP_KERNEL);
78 if (!esoc_udev)
79 return ERR_PTR(-ENOMEM);
80 err = kfifo_alloc(&esoc_udev->req_fifo, (sizeof(u32)) * ESOC_MAX_REQ,
81 GFP_KERNEL);
82 if (err) {
83 pr_err("unable to allocate request fifo for %s\n",
84 esoc_clink->name);
85 goto req_fifo_fail;
86 }
87 err = kfifo_alloc(&esoc_udev->evt_fifo, (sizeof(u32)) * ESOC_MAX_EVT,
88 GFP_KERNEL);
89 if (err) {
90 pr_err("unable to allocate evt fifo for %s\n",
91 esoc_clink->name);
92 goto evt_fifo_fail;
93 }
94 init_waitqueue_head(&esoc_udev->req_wait);
95 init_waitqueue_head(&esoc_udev->evt_wait);
96 spin_lock_init(&esoc_udev->req_fifo_lock);
97 spin_lock_init(&esoc_udev->evt_fifo_lock);
98 esoc_udev->clink = esoc_clink;
99 spin_lock(&esoc_udev_list_lock);
100 list_add_tail(&esoc_udev->list, &esoc_udev_list);
101 spin_unlock(&esoc_udev_list_lock);
102 return esoc_udev;
103evt_fifo_fail:
104 kfifo_free(&esoc_udev->req_fifo);
105req_fifo_fail:
106 kfree(esoc_udev);
107 return ERR_PTR(-ENODEV);
108}
109
110static void return_esoc_udev(struct esoc_udev *esoc_udev)
111{
112 spin_lock(&esoc_udev_list_lock);
113 list_del(&esoc_udev->list);
114 spin_unlock(&esoc_udev_list_lock);
115 kfifo_free(&esoc_udev->req_fifo);
116 kfifo_free(&esoc_udev->evt_fifo);
117 kfree(esoc_udev);
118}
119
120static struct esoc_udev *esoc_udev_get_by_minor(unsigned int index)
121{
122 struct esoc_udev *esoc_udev;
123
124 spin_lock(&esoc_udev_list_lock);
125 list_for_each_entry(esoc_udev, &esoc_udev_list, list) {
126 if (esoc_udev->clink->id == index)
127 goto found;
128 }
129 esoc_udev = NULL;
130found:
131 spin_unlock(&esoc_udev_list_lock);
132 return esoc_udev;
133}
134
135void esoc_udev_handle_clink_req(enum esoc_req req, struct esoc_eng *eng)
136{
137 int err;
138 u32 clink_req;
139 struct esoc_clink *esoc_clink = eng->esoc_clink;
140 struct esoc_udev *esoc_udev = esoc_udev_get_by_minor(esoc_clink->id);
141
142 if (!esoc_udev)
143 return;
144 clink_req = (u32)req;
145 err = kfifo_in_spinlocked(&esoc_udev->req_fifo, &clink_req,
146 sizeof(clink_req),
147 &esoc_udev->req_fifo_lock);
148 if (err != sizeof(clink_req)) {
149 pr_err("unable to queue request for %s\n", esoc_clink->name);
150 return;
151 }
152 wake_up_interruptible(&esoc_udev->req_wait);
153}
154
155void esoc_udev_handle_clink_evt(enum esoc_evt evt, struct esoc_eng *eng)
156{
157 int err;
158 u32 clink_evt;
159 struct esoc_clink *esoc_clink = eng->esoc_clink;
160 struct esoc_udev *esoc_udev = esoc_udev_get_by_minor(esoc_clink->id);
161
162 if (!esoc_udev)
163 return;
164 clink_evt = (u32)evt;
165 err = kfifo_in_spinlocked(&esoc_udev->evt_fifo, &clink_evt,
166 sizeof(clink_evt),
167 &esoc_udev->evt_fifo_lock);
168 if (err != sizeof(clink_evt)) {
169 pr_err("unable to queue event for %s\n", esoc_clink->name);
170 return;
171 }
172 wake_up_interruptible(&esoc_udev->evt_wait);
173}
174
175static long esoc_dev_ioctl(struct file *file, unsigned int cmd,
176 unsigned long arg)
177{
178 int err;
179 u32 esoc_cmd, status, req, evt;
180 struct esoc_uhandle *uhandle = file->private_data;
181 struct esoc_udev *esoc_udev = uhandle->esoc_udev;
182 struct esoc_clink *esoc_clink = uhandle->esoc_clink;
183 const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
184 void __user *uarg = (void __user *)arg;
185
186 switch (cmd) {
187 case ESOC_REG_REQ_ENG:
188 err = esoc_clink_register_req_eng(esoc_clink, &uhandle->eng);
189 if (err)
190 return err;
191 uhandle->req_eng_reg = true;
192 break;
193 case ESOC_REG_CMD_ENG:
194 err = esoc_clink_register_cmd_eng(esoc_clink, &uhandle->eng);
195 if (err)
196 return err;
197 uhandle->cmd_eng_reg = true;
198 break;
199 case ESOC_CMD_EXE:
200 if (esoc_clink->cmd_eng != &uhandle->eng)
201 return -EACCES;
202 get_user(esoc_cmd, (u32 __user *)arg);
203 return clink_ops->cmd_exe(esoc_cmd, esoc_clink);
204 case ESOC_WAIT_FOR_REQ:
205 if (esoc_clink->req_eng != &uhandle->eng)
206 return -EACCES;
207 err = wait_event_interruptible(esoc_udev->req_wait,
208 !kfifo_is_empty(&esoc_udev->req_fifo));
209 if (!err) {
210 err = kfifo_out_spinlocked(&esoc_udev->req_fifo, &req,
211 sizeof(req),
212 &esoc_udev->req_fifo_lock);
213 if (err != sizeof(req)) {
214 pr_err("read from clink %s req q failed\n",
215 esoc_clink->name);
216 return -EIO;
217 }
Arun KS7bffb1f2017-02-07 18:41:25 +0530218 put_user(req, (unsigned int __user *)uarg);
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800219
220 }
221 return err;
222 case ESOC_NOTIFY:
223 get_user(esoc_cmd, (u32 __user *)arg);
224 clink_ops->notify(esoc_cmd, esoc_clink);
225 break;
226 case ESOC_GET_STATUS:
227 err = clink_ops->get_status(&status, esoc_clink);
228 if (err)
229 return err;
Arun KS7bffb1f2017-02-07 18:41:25 +0530230 put_user(status, (unsigned int __user *)uarg);
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800231 break;
232 case ESOC_WAIT_FOR_CRASH:
233 err = wait_event_interruptible(esoc_udev->evt_wait,
234 !kfifo_is_empty(&esoc_udev->evt_fifo));
235 if (!err) {
236 err = kfifo_out_spinlocked(&esoc_udev->evt_fifo, &evt,
237 sizeof(evt),
238 &esoc_udev->evt_fifo_lock);
239 if (err != sizeof(evt)) {
240 pr_err("read from clink %s evt q failed\n",
241 esoc_clink->name);
242 return -EIO;
243 }
Arun KS7bffb1f2017-02-07 18:41:25 +0530244 put_user(evt, (unsigned int __user *)uarg);
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800245 }
246 return err;
247 default:
248 return -EINVAL;
249 };
250 return 0;
251}
252
253static int esoc_dev_open(struct inode *inode, struct file *file)
254{
255 struct esoc_uhandle *uhandle;
256 struct esoc_udev *esoc_udev;
257 struct esoc_clink *esoc_clink;
258 struct esoc_eng *eng;
259 unsigned int minor = iminor(inode);
260
261 esoc_udev = esoc_udev_get_by_minor(minor);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700262 if (!esoc_udev) {
263 pr_err("failed to get udev\n");
264 return -ENOMEM;
265 }
266
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800267 esoc_clink = get_esoc_clink(esoc_udev->clink->id);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700268 if (!esoc_clink) {
269 pr_err("failed to get clink\n");
270 return -ENOMEM;
271 }
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800272
273 uhandle = kzalloc(sizeof(*uhandle), GFP_KERNEL);
274 if (!uhandle) {
275 put_esoc_clink(esoc_clink);
276 return -ENOMEM;
277 }
278 uhandle->esoc_udev = esoc_udev;
279 uhandle->esoc_clink = esoc_clink;
280 eng = &uhandle->eng;
281 eng->handle_clink_req = esoc_udev_handle_clink_req;
282 eng->handle_clink_evt = esoc_udev_handle_clink_evt;
283 file->private_data = uhandle;
284 return 0;
285}
286
287static int esoc_dev_release(struct inode *inode, struct file *file)
288{
289 struct esoc_clink *esoc_clink;
290 struct esoc_uhandle *uhandle = file->private_data;
291
292 esoc_clink = uhandle->esoc_clink;
293 if (uhandle->req_eng_reg)
294 esoc_clink_unregister_req_eng(esoc_clink, &uhandle->eng);
295 if (uhandle->cmd_eng_reg)
296 esoc_clink_unregister_cmd_eng(esoc_clink, &uhandle->eng);
297 uhandle->req_eng_reg = false;
298 uhandle->cmd_eng_reg = false;
299 put_esoc_clink(esoc_clink);
300 kfree(uhandle);
301 return 0;
302}
303static const struct file_operations esoc_dev_fops = {
304 .owner = THIS_MODULE,
305 .open = esoc_dev_open,
306 .unlocked_ioctl = esoc_dev_ioctl,
307 .release = esoc_dev_release,
308};
309
310int esoc_clink_add_device(struct device *dev, void *dummy)
311{
312 struct esoc_udev *esoc_udev;
313 struct esoc_clink *esoc_clink = to_esoc_clink(dev);
314
315 esoc_udev = get_free_esoc_udev(esoc_clink);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700316 if (IS_ERR_OR_NULL(esoc_udev))
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800317 return PTR_ERR(esoc_udev);
318 esoc_udev->dev = device_create(esoc_class, &esoc_clink->dev,
319 MKDEV(esoc_major, esoc_clink->id),
320 esoc_clink, "esoc-%d", esoc_clink->id);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700321 if (IS_ERR_OR_NULL(esoc_udev->dev)) {
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800322 pr_err("failed to create user device\n");
323 goto dev_err;
324 }
325 return 0;
326dev_err:
327 return_esoc_udev(esoc_udev);
328 return -ENODEV;
329}
330
331int esoc_clink_del_device(struct device *dev, void *dummy)
332{
333 struct esoc_udev *esoc_udev;
334 struct esoc_clink *esoc_clink = to_esoc_clink(dev);
335
336 esoc_udev = esoc_udev_get_by_minor(esoc_clink->id);
337 if (!esoc_udev)
338 return 0;
339 return_esoc_udev(esoc_udev);
340 device_destroy(esoc_class, MKDEV(esoc_major, esoc_clink->id));
341 return_esoc_udev(esoc_udev);
342 return 0;
343}
344
345static int esoc_dev_notifier_call(struct notifier_block *nb,
346 unsigned long action,
347 void *data)
348{
349 struct device *dev = data;
350
351 switch (action) {
352 case BUS_NOTIFY_ADD_DEVICE:
353 return esoc_clink_add_device(dev, NULL);
354 case BUS_NOTIFY_DEL_DEVICE:
355 return esoc_clink_del_device(dev, NULL);
356 };
357 return 0;
358}
359
360static struct notifier_block esoc_dev_notifier = {
361 .notifier_call = esoc_dev_notifier_call,
362};
363
364int __init esoc_dev_init(void)
365{
366 int ret = 0;
367
368 esoc_class = class_create(THIS_MODULE, "esoc-dev");
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700369 if (IS_ERR_OR_NULL(esoc_class)) {
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800370 pr_err("coudn't create class");
371 return PTR_ERR(esoc_class);
372 }
373 esoc_major = register_chrdev(0, "esoc", &esoc_dev_fops);
374 if (esoc_major < 0) {
375 pr_err("failed to allocate char dev\n");
376 ret = esoc_major;
377 goto class_unreg;
378 }
379 ret = bus_register_notifier(&esoc_bus_type, &esoc_dev_notifier);
380 if (ret)
381 goto chrdev_unreg;
382 esoc_for_each_dev(NULL, esoc_clink_add_device);
383 return ret;
384chrdev_unreg:
385 unregister_chrdev(esoc_major, "esoc");
386class_unreg:
387 class_destroy(esoc_class);
388 return 0;
389}
390
391void __exit esoc_dev_exit(void)
392{
393 bus_unregister_notifier(&esoc_bus_type, &esoc_dev_notifier);
394 class_destroy(esoc_class);
395 unregister_chrdev(esoc_major, "esoc-dev");
396}
397
398MODULE_LICENSE("GPL v2");
399module_init(esoc_dev_init);
400module_exit(esoc_dev_exit);