Linux/drivers/staging/dream/qdsp5/adsp_driver.c

  1 /* arch/arm/mach-msm/qdsp5/adsp_driver.c
  2  *
  3  * Copyright (C) 2008 Google, Inc.
  4  * Author: Iliyan Malchev <ibm@android.com>
  5  *
  6  * This software is licensed under the terms of the GNU General Public
  7  * License version 2, as published by the Free Software Foundation, and
  8  * may be copied, distributed, and modified under those terms.
  9  *
 10  * This program is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  * GNU General Public License for more details.
 14  *
 15  */
 16 
 17 #include <linux/cdev.h>
 18 #include <linux/fs.h>
 19 #include <linux/list.h>
 20 #include <linux/platform_device.h>
 21 #include <linux/sched.h>
 22 #include <linux/uaccess.h>
 23 
 24 #include "adsp.h"
 25 
 26 #include <linux/msm_adsp.h>
 27 #include <linux/android_pmem.h>
 28 
 29 struct adsp_pmem_region {
 30         struct hlist_node list;
 31         void *vaddr;
 32         unsigned long paddr;
 33         unsigned long kvaddr;
 34         unsigned long len;
 35         struct file *file;
 36 };
 37 
 38 struct adsp_device {
 39         struct msm_adsp_module *module;
 40 
 41         spinlock_t event_queue_lock;
 42         wait_queue_head_t event_wait;
 43         struct list_head event_queue;
 44         int abort;
 45 
 46         const char *name;
 47         struct device *device;
 48         struct cdev cdev;
 49 };
 50 
 51 static struct adsp_device *inode_to_device(struct inode *inode);
 52 
 53 #define __CONTAINS(r, v, l) ({                                  \
 54         typeof(r) __r = r;                                      \
 55         typeof(v) __v = v;                                      \
 56         typeof(v) __e = __v + l;                                \
 57         int res = __v >= __r->vaddr &&                          \
 58                 __e <= __r->vaddr + __r->len;                   \
 59         res;                                                    \
 60 })
 61 
 62 #define CONTAINS(r1, r2) ({                                     \
 63         typeof(r2) __r2 = r2;                                   \
 64         __CONTAINS(r1, __r2->vaddr, __r2->len);                 \
 65 })
 66 
 67 #define IN_RANGE(r, v) ({                                       \
 68         typeof(r) __r = r;                                      \
 69         typeof(v) __vv = v;                                     \
 70         int res = ((__vv >= __r->vaddr) &&                      \
 71                 (__vv < (__r->vaddr + __r->len)));              \
 72         res;                                                    \
 73 })
 74 
 75 #define OVERLAPS(r1, r2) ({                                     \
 76         typeof(r1) __r1 = r1;                                   \
 77         typeof(r2) __r2 = r2;                                   \
 78         typeof(__r2->vaddr) __v = __r2->vaddr;                  \
 79         typeof(__v) __e = __v + __r2->len - 1;                  \
 80         int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
 81         res;                                                    \
 82 })
 83 
 84 static int adsp_pmem_check(struct msm_adsp_module *module,
 85                 void *vaddr, unsigned long len)
 86 {
 87         struct adsp_pmem_region *region_elt;
 88         struct hlist_node *node;
 89         struct adsp_pmem_region t = { .vaddr = vaddr, .len = len };
 90 
 91         hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
 92                 if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
 93                     OVERLAPS(region_elt, &t)) {
 94                         printk(KERN_ERR "adsp: module %s:"
 95                                 " region (vaddr %p len %ld)"
 96                                 " clashes with registered region"
 97                                 " (vaddr %p paddr %p len %ld)\n",
 98                                 module->name,
 99                                 vaddr, len,
100                                 region_elt->vaddr,
101                                 (void *)region_elt->paddr,
102                                 region_elt->len);
103                         return -EINVAL;
104                 }
105         }
106 
107         return 0;
108 }
109 
110 static int adsp_pmem_add(struct msm_adsp_module *module,
111                          struct adsp_pmem_info *info)
112 {
113         unsigned long paddr, kvaddr, len;
114         struct file *file;
115         struct adsp_pmem_region *region;
116         int rc = -EINVAL;
117 
118         mutex_lock(&module->pmem_regions_lock);
119         region = kmalloc(sizeof(*region), GFP_KERNEL);
120         if (!region) {
121                 rc = -ENOMEM;
122                 goto end;
123         }
124         INIT_HLIST_NODE(&region->list);
125         if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
126                 kfree(region);
127                 goto end;
128         }
129 
130         rc = adsp_pmem_check(module, info->vaddr, len);
131         if (rc < 0) {
132                 put_pmem_file(file);
133                 kfree(region);
134                 goto end;
135         }
136 
137         region->vaddr = info->vaddr;
138         region->paddr = paddr;
139         region->kvaddr = kvaddr;
140         region->len = len;
141         region->file = file;
142 
143         hlist_add_head(&region->list, &module->pmem_regions);
144 end:
145         mutex_unlock(&module->pmem_regions_lock);
146         return rc;
147 }
148 
149 static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr,
150                      unsigned long len, struct adsp_pmem_region **region)
151 {
152         struct hlist_node *node;
153         void *vaddr = *addr;
154         struct adsp_pmem_region *region_elt;
155 
156         int match_count = 0;
157 
158         *region = NULL;
159 
160         /* returns physical address or zero */
161         hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
162                 if (vaddr >= region_elt->vaddr &&
163                     vaddr < region_elt->vaddr + region_elt->len &&
164                     vaddr + len <= region_elt->vaddr + region_elt->len) {
165                         /* offset since we could pass vaddr inside a registerd
166                          * pmem buffer
167                          */
168 
169                         match_count++;
170                         if (!*region)
171                                 *region = region_elt;
172                 }
173         }
174 
175         if (match_count > 1) {
176                 printk(KERN_ERR "adsp: module %s: "
177                         "multiple hits for vaddr %p, len %ld\n",
178                         module->name, vaddr, len);
179                 hlist_for_each_entry(region_elt, node,
180                                 &module->pmem_regions, list) {
181                         if (vaddr >= region_elt->vaddr &&
182                             vaddr < region_elt->vaddr + region_elt->len &&
183                             vaddr + len <= region_elt->vaddr + region_elt->len)
184                                 printk(KERN_ERR "\t%p, %ld --> %p\n",
185                                         region_elt->vaddr,
186                                         region_elt->len,
187                                         (void *)region_elt->paddr);
188                 }
189         }
190 
191         return *region ? 0 : -1;
192 }
193 
194 int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
195                            unsigned long *kvaddr, unsigned long len)
196 {
197         struct adsp_pmem_region *region;
198         void *vaddr = *addr;
199         unsigned long *paddr = (unsigned long *)addr;
200         int ret;
201 
202         ret = adsp_pmem_lookup_vaddr(module, addr, len, &region);
203         if (ret) {
204                 printk(KERN_ERR "adsp: not patching %s (paddr & kvaddr),"
205                         " lookup (%p, %ld) failed\n",
206                         module->name, vaddr, len);
207                 return ret;
208         }
209         *paddr = region->paddr + (vaddr - region->vaddr);
210         *kvaddr = region->kvaddr + (vaddr - region->vaddr);
211         return 0;
212 }
213 
214 int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
215                     unsigned long len)
216 {
217         struct adsp_pmem_region *region;
218         void *vaddr = *addr;
219         unsigned long *paddr = (unsigned long *)addr;
220         int ret;
221 
222         ret = adsp_pmem_lookup_vaddr(module, addr, len, &region);
223         if (ret) {
224                 printk(KERN_ERR "adsp: not patching %s, lookup (%p, %ld) failed\n",
225                         module->name, vaddr, len);
226                 return ret;
227         }
228 
229         *paddr = region->paddr + (vaddr - region->vaddr);
230         return 0;
231 }
232 
233 static int adsp_verify_cmd(struct msm_adsp_module *module,
234                            unsigned int queue_id, void *cmd_data,
235                            size_t cmd_size)
236 {
237         /* call the per module verifier */
238         if (module->verify_cmd)
239                 return module->verify_cmd(module, queue_id, cmd_data,
240                                              cmd_size);
241         else
242                 printk(KERN_INFO "adsp: no packet verifying function "
243                                  "for task %s\n", module->name);
244         return 0;
245 }
246 
247 static long adsp_write_cmd(struct adsp_device *adev, void __user *arg)
248 {
249         struct adsp_command_t cmd;
250         unsigned char buf[256];
251         void *cmd_data;
252         long rc;
253 
254         if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)))
255                 return -EFAULT;
256 
257         if (cmd.len > 256) {
258                 cmd_data = kmalloc(cmd.len, GFP_USER);
259                 if (!cmd_data)
260                         return -ENOMEM;
261         } else {
262                 cmd_data = buf;
263         }
264 
265         if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) {
266                 rc = -EFAULT;
267                 goto end;
268         }
269 
270         mutex_lock(&adev->module->pmem_regions_lock);
271         if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) {
272                 printk(KERN_ERR "module %s: verify failed.\n",
273                         adev->module->name);
274                 rc = -EINVAL;
275                 goto end;
276         }
277         rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len);
278 end:
279         mutex_unlock(&adev->module->pmem_regions_lock);
280 
281         if (cmd.len > 256)
282                 kfree(cmd_data);
283 
284         return rc;
285 }
286 
287 static int adsp_events_pending(struct adsp_device *adev)
288 {
289         unsigned long flags;
290         int yes;
291         spin_lock_irqsave(&adev->event_queue_lock, flags);
292         yes = !list_empty(&adev->event_queue);
293         spin_unlock_irqrestore(&adev->event_queue_lock, flags);
294         return yes || adev->abort;
295 }
296 
297 static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr,
298                      struct adsp_pmem_region **region)
299 {
300         struct hlist_node *node;
301         unsigned long paddr = (unsigned long)(*addr);
302         struct adsp_pmem_region *region_elt;
303 
304         hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
305                 if (paddr >= region_elt->paddr &&
306                     paddr < region_elt->paddr + region_elt->len) {
307                         *region = region_elt;
308                         return 0;
309                 }
310         }
311         return -1;
312 }
313 
314 int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr)
315 {
316         struct adsp_pmem_region *region;
317         unsigned long paddr = (unsigned long)(*addr);
318         unsigned long *vaddr = (unsigned long *)addr;
319         int ret;
320 
321         ret = adsp_pmem_lookup_paddr(module, addr, &region);
322         if (ret) {
323                 printk(KERN_ERR "adsp: not patching %s, paddr %p lookup failed\n",
324                         module->name, vaddr);
325                 return ret;
326         }
327 
328         *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr);
329         return 0;
330 }
331 
332 static int adsp_patch_event(struct msm_adsp_module *module,
333                                 struct adsp_event *event)
334 {
335         /* call the per-module msg verifier */
336         if (module->patch_event)
337                 return module->patch_event(module, event);
338         return 0;
339 }
340 
341 static long adsp_get_event(struct adsp_device *adev, void __user *arg)
342 {
343         unsigned long flags;
344         struct adsp_event *data = NULL;
345         struct adsp_event_t evt;
346         int timeout;
347         long rc = 0;
348 
349         if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t)))
350                 return -EFAULT;
351 
352         timeout = (int)evt.timeout_ms;
353 
354         if (timeout > 0) {
355                 rc = wait_event_interruptible_timeout(
356                         adev->event_wait, adsp_events_pending(adev),
357                         msecs_to_jiffies(timeout));
358                 if (rc == 0)
359                         return -ETIMEDOUT;
360         } else {
361                 rc = wait_event_interruptible(
362                         adev->event_wait, adsp_events_pending(adev));
363         }
364         if (rc < 0)
365                 return rc;
366 
367         if (adev->abort)
368                 return -ENODEV;
369 
370         spin_lock_irqsave(&adev->event_queue_lock, flags);
371         if (!list_empty(&adev->event_queue)) {
372                 data = list_first_entry(&adev->event_queue,
373                                         struct adsp_event, list);
374                 list_del(&data->list);
375         }
376         spin_unlock_irqrestore(&adev->event_queue_lock, flags);
377 
378         if (!data)
379                 return -EAGAIN;
380 
381         /* DSP messages are type 0; they may contain physical addresses */
382         if (data->type == 0)
383                 adsp_patch_event(adev->module, data);
384 
385         /* map adsp_event --> adsp_event_t */
386         if (evt.len < data->size) {
387                 rc = -ETOOSMALL;
388                 goto end;
389         }
390         if (data->msg_id != EVENT_MSG_ID) {
391                 if (copy_to_user((void *)(evt.data), data->data.msg16,
392                                         data->size)) {
393                         rc = -EFAULT;
394                         goto end;
395         }
396         } else {
397                 if (copy_to_user((void *)(evt.data), data->data.msg32,
398                                         data->size)) {
399                         rc = -EFAULT;
400                         goto end;
401                 }
402         }
403 
404         evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */
405         evt.msg_id = data->msg_id;
406         evt.flags = data->is16;
407         evt.len = data->size;
408         if (copy_to_user(arg, &evt, sizeof(evt)))
409                 rc = -EFAULT;
410 end:
411         kfree(data);
412         return rc;
413 }
414 
415 static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
416 {
417         struct adsp_device *adev = filp->private_data;
418 
419         switch (cmd) {
420         case ADSP_IOCTL_ENABLE:
421                 return msm_adsp_enable(adev->module);
422 
423         case ADSP_IOCTL_DISABLE:
424                 return msm_adsp_disable(adev->module);
425 
426         case ADSP_IOCTL_DISABLE_EVENT_RSP:
427                 return 0;
428 
429         case ADSP_IOCTL_DISABLE_ACK:
430                 pr_err("adsp: ADSP_IOCTL_DISABLE_ACK is not implemented.\n");
431                 break;
432 
433         case ADSP_IOCTL_WRITE_COMMAND:
434                 return adsp_write_cmd(adev, (void __user *) arg);
435 
436         case ADSP_IOCTL_GET_EVENT:
437                 return adsp_get_event(adev, (void __user *) arg);
438 
439         case ADSP_IOCTL_SET_CLKRATE: {
440 #if CONFIG_MSM_AMSS_VERSION==6350
441                 unsigned long clk_rate;
442                 if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate)))
443                         return -EFAULT;
444                 return adsp_set_clkrate(adev->module, clk_rate);
445 #endif
446         }
447 
448         case ADSP_IOCTL_REGISTER_PMEM: {
449                 struct adsp_pmem_info info;
450                 if (copy_from_user(&info, (void *) arg, sizeof(info)))
451                         return -EFAULT;
452                 return adsp_pmem_add(adev->module, &info);
453         }
454 
455         case ADSP_IOCTL_ABORT_EVENT_READ:
456                 adev->abort = 1;
457                 wake_up(&adev->event_wait);
458                 break;
459 
460         default:
461                 break;
462         }
463         return -EINVAL;
464 }
465 
466 static int adsp_release(struct inode *inode, struct file *filp)
467 {
468         struct adsp_device *adev = filp->private_data;
469         struct msm_adsp_module *module = adev->module;
470         struct hlist_node *node, *tmp;
471         struct adsp_pmem_region *region;
472 
473         pr_info("adsp_release() '%s'\n", adev->name);
474 
475         /* clear module before putting it to avoid race with open() */
476         adev->module = NULL;
477 
478         mutex_lock(&module->pmem_regions_lock);
479         hlist_for_each_safe(node, tmp, &module->pmem_regions) {
480                 region = hlist_entry(node, struct adsp_pmem_region, list);
481                 hlist_del(node);
482                 put_pmem_file(region->file);
483                 kfree(region);
484         }
485         mutex_unlock(&module->pmem_regions_lock);
486         BUG_ON(!hlist_empty(&module->pmem_regions));
487 
488         msm_adsp_put(module);
489         return 0;
490 }
491 
492 static void adsp_event(void *driver_data, unsigned id, size_t len,
493                        void (*getevent)(void *ptr, size_t len))
494 {
495         struct adsp_device *adev = driver_data;
496         struct adsp_event *event;
497         unsigned long flags;
498 
499         if (len > ADSP_EVENT_MAX_SIZE) {
500                 pr_err("adsp_event: event too large (%d bytes)\n", len);
501                 return;
502         }
503 
504         event = kmalloc(sizeof(*event), GFP_ATOMIC);
505         if (!event) {
506                 pr_err("adsp_event: cannot allocate buffer\n");
507                 return;
508         }
509 
510         if (id != EVENT_MSG_ID) {
511                 event->type = 0;
512                 event->is16 = 0;
513                 event->msg_id = id;
514                 event->size = len;
515 
516                 getevent(event->data.msg16, len);
517         } else {
518                 event->type = 1;
519                 event->is16 = 1;
520                 event->msg_id = id;
521                 event->size = len;
522                 getevent(event->data.msg32, len);
523         }
524 
525         spin_lock_irqsave(&adev->event_queue_lock, flags);
526         list_add_tail(&event->list, &adev->event_queue);
527         spin_unlock_irqrestore(&adev->event_queue_lock, flags);
528         wake_up(&adev->event_wait);
529 }
530 
531 static struct msm_adsp_ops adsp_ops = {
532         .event = adsp_event,
533 };
534 
535 static int adsp_open(struct inode *inode, struct file *filp)
536 {
537         struct adsp_device *adev;
538         int rc;
539 
540         rc = nonseekable_open(inode, filp);
541         if (rc < 0)
542                 return rc;
543 
544         adev = inode_to_device(inode);
545         if (!adev)
546                 return -ENODEV;
547 
548         pr_info("adsp_open() name = '%s'\n", adev->name);
549 
550         rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
551         if (rc)
552                 return rc;
553 
554         pr_info("adsp_open() module '%s' adev %p\n", adev->name, adev);
555         filp->private_data = adev;
556         adev->abort = 0;
557         INIT_HLIST_HEAD(&adev->module->pmem_regions);
558         mutex_init(&adev->module->pmem_regions_lock);
559 
560         return 0;
561 }
562 
563 static unsigned adsp_device_count;
564 static struct adsp_device *adsp_devices;
565 
566 static struct adsp_device *inode_to_device(struct inode *inode)
567 {
568         unsigned n = MINOR(inode->i_rdev);
569         if (n < adsp_device_count) {
570                 if (adsp_devices[n].device)
571                         return adsp_devices + n;
572         }
573         return NULL;
574 }
575 
576 static dev_t adsp_devno;
577 static struct class *adsp_class;
578 
579 static struct file_operations adsp_fops = {
580         .owner = THIS_MODULE,
581         .open = adsp_open,
582         .unlocked_ioctl = adsp_ioctl,
583         .release = adsp_release,
584 };
585 
586 static void adsp_create(struct adsp_device *adev, const char *name,
587                         struct device *parent, dev_t devt)
588 {
589         struct device *dev;
590         int rc;
591 
592         dev = device_create(adsp_class, parent, devt, "%s", name);
593         if (IS_ERR(dev))
594                 return;
595 
596         init_waitqueue_head(&adev->event_wait);
597         INIT_LIST_HEAD(&adev->event_queue);
598         spin_lock_init(&adev->event_queue_lock);
599 
600         cdev_init(&adev->cdev, &adsp_fops);
601         adev->cdev.owner = THIS_MODULE;
602 
603         rc = cdev_add(&adev->cdev, devt, 1);
604         if (rc < 0) {
605                 device_destroy(adsp_class, devt);
606         } else {
607                 adev->device = dev;
608                 adev->name = name;
609         }
610 }
611 
612 void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n)
613 {
614         int rc;
615 
616         adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL);
617         if (!adsp_devices)
618                 return;
619 
620         adsp_class = class_create(THIS_MODULE, "adsp");
621         if (IS_ERR(adsp_class))
622                 goto fail_create_class;
623 
624         rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp");
625         if (rc < 0)
626                 goto fail_alloc_region;
627 
628         adsp_device_count = n;
629         for (n = 0; n < adsp_device_count; n++) {
630                 adsp_create(adsp_devices + n,
631                             modules[n].name, &modules[n].pdev.dev,
632                             MKDEV(MAJOR(adsp_devno), n));
633         }
634 
635         return;
636 
637 fail_alloc_region:
638         class_unregister(adsp_class);
639 fail_create_class:
640         kfree(adsp_devices);
641 }
642 

This page was automatically generated by LXR 0.3.1.  •  Linux is a registered trademark of Linus Torvalds