blob: 1d9e623c95f2e5c3e7e6538cb2a7d27232b58381 [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:
Arun KS55b33a42017-01-16 15:27:48 +0530227 clink_ops->get_status(&status, esoc_clink);
228 put_user(status, (unsigned int __user *)uarg);
229 break;
230 case ESOC_GET_ERR_FATAL:
231 clink_ops->get_err_fatal(&status, esoc_clink);
Arun KS7bffb1f2017-02-07 18:41:25 +0530232 put_user(status, (unsigned int __user *)uarg);
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800233 break;
234 case ESOC_WAIT_FOR_CRASH:
235 err = wait_event_interruptible(esoc_udev->evt_wait,
236 !kfifo_is_empty(&esoc_udev->evt_fifo));
237 if (!err) {
238 err = kfifo_out_spinlocked(&esoc_udev->evt_fifo, &evt,
239 sizeof(evt),
240 &esoc_udev->evt_fifo_lock);
241 if (err != sizeof(evt)) {
242 pr_err("read from clink %s evt q failed\n",
243 esoc_clink->name);
244 return -EIO;
245 }
Arun KS7bffb1f2017-02-07 18:41:25 +0530246 put_user(evt, (unsigned int __user *)uarg);
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800247 }
248 return err;
249 default:
250 return -EINVAL;
251 };
252 return 0;
253}
254
255static int esoc_dev_open(struct inode *inode, struct file *file)
256{
257 struct esoc_uhandle *uhandle;
258 struct esoc_udev *esoc_udev;
259 struct esoc_clink *esoc_clink;
260 struct esoc_eng *eng;
261 unsigned int minor = iminor(inode);
262
263 esoc_udev = esoc_udev_get_by_minor(minor);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700264 if (!esoc_udev) {
265 pr_err("failed to get udev\n");
266 return -ENOMEM;
267 }
268
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800269 esoc_clink = get_esoc_clink(esoc_udev->clink->id);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700270 if (!esoc_clink) {
271 pr_err("failed to get clink\n");
272 return -ENOMEM;
273 }
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800274
275 uhandle = kzalloc(sizeof(*uhandle), GFP_KERNEL);
276 if (!uhandle) {
277 put_esoc_clink(esoc_clink);
278 return -ENOMEM;
279 }
280 uhandle->esoc_udev = esoc_udev;
281 uhandle->esoc_clink = esoc_clink;
282 eng = &uhandle->eng;
283 eng->handle_clink_req = esoc_udev_handle_clink_req;
284 eng->handle_clink_evt = esoc_udev_handle_clink_evt;
285 file->private_data = uhandle;
286 return 0;
287}
288
289static int esoc_dev_release(struct inode *inode, struct file *file)
290{
291 struct esoc_clink *esoc_clink;
292 struct esoc_uhandle *uhandle = file->private_data;
293
294 esoc_clink = uhandle->esoc_clink;
295 if (uhandle->req_eng_reg)
296 esoc_clink_unregister_req_eng(esoc_clink, &uhandle->eng);
297 if (uhandle->cmd_eng_reg)
298 esoc_clink_unregister_cmd_eng(esoc_clink, &uhandle->eng);
299 uhandle->req_eng_reg = false;
300 uhandle->cmd_eng_reg = false;
301 put_esoc_clink(esoc_clink);
302 kfree(uhandle);
303 return 0;
304}
305static const struct file_operations esoc_dev_fops = {
306 .owner = THIS_MODULE,
307 .open = esoc_dev_open,
308 .unlocked_ioctl = esoc_dev_ioctl,
309 .release = esoc_dev_release,
310};
311
312int esoc_clink_add_device(struct device *dev, void *dummy)
313{
314 struct esoc_udev *esoc_udev;
315 struct esoc_clink *esoc_clink = to_esoc_clink(dev);
316
317 esoc_udev = get_free_esoc_udev(esoc_clink);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700318 if (IS_ERR_OR_NULL(esoc_udev))
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800319 return PTR_ERR(esoc_udev);
320 esoc_udev->dev = device_create(esoc_class, &esoc_clink->dev,
321 MKDEV(esoc_major, esoc_clink->id),
322 esoc_clink, "esoc-%d", esoc_clink->id);
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700323 if (IS_ERR_OR_NULL(esoc_udev->dev)) {
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800324 pr_err("failed to create user device\n");
325 goto dev_err;
326 }
327 return 0;
328dev_err:
329 return_esoc_udev(esoc_udev);
330 return -ENODEV;
331}
332
333int esoc_clink_del_device(struct device *dev, void *dummy)
334{
335 struct esoc_udev *esoc_udev;
336 struct esoc_clink *esoc_clink = to_esoc_clink(dev);
337
338 esoc_udev = esoc_udev_get_by_minor(esoc_clink->id);
339 if (!esoc_udev)
340 return 0;
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800341 device_destroy(esoc_class, MKDEV(esoc_major, esoc_clink->id));
342 return_esoc_udev(esoc_udev);
343 return 0;
344}
345
346static int esoc_dev_notifier_call(struct notifier_block *nb,
347 unsigned long action,
348 void *data)
349{
350 struct device *dev = data;
351
352 switch (action) {
353 case BUS_NOTIFY_ADD_DEVICE:
354 return esoc_clink_add_device(dev, NULL);
355 case BUS_NOTIFY_DEL_DEVICE:
356 return esoc_clink_del_device(dev, NULL);
357 };
358 return 0;
359}
360
361static struct notifier_block esoc_dev_notifier = {
362 .notifier_call = esoc_dev_notifier_call,
363};
364
365int __init esoc_dev_init(void)
366{
367 int ret = 0;
368
369 esoc_class = class_create(THIS_MODULE, "esoc-dev");
Satya Durga Srinivasu Prabhala377d32f2017-03-15 11:09:28 -0700370 if (IS_ERR_OR_NULL(esoc_class)) {
Abhimanyu Kapurc75b2e12016-02-22 18:15:13 -0800371 pr_err("coudn't create class");
372 return PTR_ERR(esoc_class);
373 }
374 esoc_major = register_chrdev(0, "esoc", &esoc_dev_fops);
375 if (esoc_major < 0) {
376 pr_err("failed to allocate char dev\n");
377 ret = esoc_major;
378 goto class_unreg;
379 }
380 ret = bus_register_notifier(&esoc_bus_type, &esoc_dev_notifier);
381 if (ret)
382 goto chrdev_unreg;
383 esoc_for_each_dev(NULL, esoc_clink_add_device);
384 return ret;
385chrdev_unreg:
386 unregister_chrdev(esoc_major, "esoc");
387class_unreg:
388 class_destroy(esoc_class);
389 return 0;
390}
391
392void __exit esoc_dev_exit(void)
393{
394 bus_unregister_notifier(&esoc_bus_type, &esoc_dev_notifier);
395 class_destroy(esoc_class);
396 unregister_chrdev(esoc_major, "esoc-dev");
397}
398
399MODULE_LICENSE("GPL v2");
400module_init(esoc_dev_init);
401module_exit(esoc_dev_exit);