• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18 
19 #include <linux/module.h>
20 #include <linux/kernel.h>
21 #include <linux/printk.h>
22 #include <linux/fs.h>
23 #include <linux/poll.h>
24 #include <linux/slab.h>
25 #include <linux/vmalloc.h>
26 #include <linux/mm_types.h>
27 #include <linux/mm.h>
28 #include <linux/kmod.h>
29 #include <linux/freezer.h>
30 #include <linux/rwlock.h>
31 #include <linux/rwlock_types.h>
32 #include "securec.h"
33 #include "hi_osal.h"
34 #include "himedia.h"
35 
36 typedef struct osal_dev_info_ {
37     osal_dev *dev;
38     pm_device media;
39     struct osal_list_head node;
40 } osal_dev_info;
41 
42 typedef struct osal_fileops_info_ {
43     void *private_data;
44     osal_fileops *fops;
45 }osal_fileops_info;
46 
47 static OSAL_LIST_HEAD(dev_list);
48 
49 static DEFINE_RWLOCK(g_dev_rwlock);
50 
osal_get_pmops(const pm_basedev * pm_base)51 osal_pmops *osal_get_pmops(const pm_basedev *pm_base)
52 {
53     osal_dev_info *dev_node = NULL;
54 
55     if (pm_base == NULL) {
56         return NULL;
57     }
58 
59     read_lock(&g_dev_rwlock);
60 
61     osal_list_for_each_entry(dev_node, &dev_list, node) {
62         if (osal_strncmp(dev_node->dev->name, strlen(dev_node->dev->name),
63             pm_base->name, strlen(dev_node->dev->name)) == 0)  {
64             read_unlock(&g_dev_rwlock);
65             return dev_node->dev->pmops;
66         }
67     }
68 
69     read_unlock(&g_dev_rwlock);
70 
71     return NULL;
72 }
73 
osal_open(struct inode * inode,struct file * file)74 static int osal_open(struct inode *inode, struct file *file)
75 {
76     int ret = 0;
77     osal_fileops *fops = NULL;
78     osal_dev *dev = NULL;
79     osal_fileops_info *fileops_node = NULL;
80 
81     dev = (osal_dev *)file->private_data;
82     if (dev == NULL) {
83         osal_printk("%s - get himedia device error!\n", __FUNCTION__);
84         return -1;
85     }
86 
87     fileops_node = (osal_fileops_info *)kmalloc(sizeof(osal_fileops_info), GFP_KERNEL);
88     if (fileops_node == NULL) {
89         osal_printk("%s - fileops_node kmalloc failed!\n", __FUNCTION__);
90         return -1;
91     }
92 
93     ret = memset_s(fileops_node, sizeof(osal_fileops_info), 0, sizeof(osal_fileops_info));
94     if (ret != 0) {
95         return -1;
96     }
97 
98     fops = dev->fops;
99     if ((fops != NULL) && (fops->open != NULL)) {
100         ret = fops->open((void *)&(fileops_node->private_data));
101     }
102 
103     if (ret != 0) {
104         kfree(fileops_node);
105         return ret;
106     }
107 
108     fileops_node->fops = fops;
109     file->private_data = fileops_node;
110 
111     return ret;
112 }
113 
osal_read(struct file * file,char __user * buf,size_t size,loff_t * offset)114 static ssize_t osal_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
115 {
116     int ret = 0;
117     osal_fileops *fops = NULL;
118     osal_fileops_info *fileops_node = NULL;
119 
120     fileops_node = file->private_data;
121     if (fileops_node == NULL) {
122         osal_printk("%s - fileops_node NULL!\n", __FUNCTION__);
123         return -1;
124     }
125 
126     fops = fileops_node->fops;
127     if (fops == NULL) {
128         osal_printk("%s - fops NULL!\n", __FUNCTION__);
129         return -1;
130     }
131 
132     if (fops->read != NULL) {
133         ret = fops->read(buf, (int)size, (long *)offset, (void *)&(fileops_node->private_data));
134     }
135 
136     return ret;
137 }
138 
osal_write(struct file * file,const char __user * buf,size_t size,loff_t * offset)139 static ssize_t osal_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
140 {
141     (void)file;
142     (void)buf;
143     (void)size;
144     (void)offset;
145     return 0;
146 }
147 
osal_llseek(struct file * file,loff_t offset,int whence)148 static loff_t osal_llseek(struct file *file, loff_t offset, int whence)
149 {
150     (void)file;
151     (void)offset;
152     (void)whence;
153     return 0;
154 }
155 
osal_release(struct inode * inode,struct file * file)156 static int osal_release(struct inode *inode, struct file *file)
157 {
158     int ret = 0;
159     osal_fileops *fops = NULL;
160     osal_fileops_info *fileops_node = NULL;
161 
162     fileops_node = file->private_data;
163     if (fileops_node == NULL) {
164         osal_printk("%s - fileops_node NULL!\n", __FUNCTION__);
165         return -1;
166     }
167 
168     fops = fileops_node->fops;
169     if (fops == NULL) {
170         osal_printk("%s - fops NULL!\n", __FUNCTION__);
171         return -1;
172     }
173 
174     if (fops->release != NULL) {
175         ret = fops->release((void *)&(fileops_node->private_data));
176     }
177 
178     kfree(fileops_node);
179     file->private_data = NULL;
180 
181     return ret;
182 }
183 
184 #define ARG_BUF_TEMP_SIZE 256
185 #define CMD_NUM_MAX       2048
186 
osal_ioctl_copy_from_user(unsigned int cmd,unsigned long arg,void ** ioctl_arg,unsigned int arg_size,char * arg_buf)187 static int osal_ioctl_copy_from_user(unsigned int cmd, unsigned long arg,
188     void **ioctl_arg, unsigned int arg_size, char *arg_buf)
189 {
190     int ret = -1;
191 
192     /*  Copy arguments into temp kernel buffer  */
193     switch (_IOC_DIR(cmd)) {
194         case _IOC_NONE:
195             *ioctl_arg = NULL;
196             break;
197         case _IOC_READ:
198         case _IOC_WRITE:
199         case (_IOC_WRITE | _IOC_READ):
200             if (arg_size <= ARG_BUF_TEMP_SIZE) {
201                 *ioctl_arg = arg_buf;
202             } else {
203                 /* too big to allocate from stack; <16K use kmalloc */
204                 *ioctl_arg = kmalloc(arg_size, GFP_KERNEL);
205                 if (*ioctl_arg == NULL) {
206                     printk("kmalloc cmd buffer failed\n");
207                     return -ENOMEM;
208                 }
209             }
210 
211             if (_IOC_DIR(cmd) & _IOC_WRITE) {
212                 ret = copy_from_user(*ioctl_arg, (void __user *)(uintptr_t)arg, arg_size);
213                 if (ret != 0) {
214                     printk("copy_from_user failed, cmd=0x%x\n", cmd);
215                     return -1;
216                 }
217             }
218 
219             break;
220         default:
221             break;
222     }
223 
224     return 0;
225 }
226 
osal_ioctl_call_fun(unsigned int cmd,void * ioctl_arg,osal_fileops * fops,void * private_data)227 static int osal_ioctl_call_fun(unsigned int cmd, void *ioctl_arg, osal_fileops *fops, void *private_data)
228 {
229     unsigned int i = 0;
230 
231     // call func; only used inosal_ioctl; no need check cmd, ioctl_arg, fops
232     for (i = 0; i < fops->cmd_cnt; i++) {
233         if (fops->cmd_list[i].cmd == cmd) {
234             if (fops->cmd_list[i].handler != NULL) {
235                 return fops->cmd_list[i].handler(cmd, ioctl_arg, private_data);
236             }
237         }
238     }
239 
240     return -1;
241 }
242 
osal_ioctl_copy_to_user(unsigned int cmd,unsigned long arg,const void * ioctl_arg,unsigned int arg_size)243 static int osal_ioctl_copy_to_user(unsigned int cmd, unsigned long arg, const void *ioctl_arg, unsigned int arg_size)
244 {
245     /*  Copy results into user buffer  */
246     switch (_IOC_DIR(cmd)) {
247         case _IOC_READ:
248         case (_IOC_WRITE | _IOC_READ):
249             {
250                 return copy_to_user((void __user *)(uintptr_t)arg, ioctl_arg, arg_size);
251             }
252             break;
253         default:
254             break;
255     }
256 
257     return 0;
258 }
259 
osal_ioctl(struct file * file,unsigned int cmd,unsigned long arg)260 static long osal_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
261 {
262     int ret;
263     unsigned int arg_size;
264     char arg_buf[ARG_BUF_TEMP_SIZE]; // temp to prevent tiny vmalloc
265     void *ioctl_arg = NULL;
266     osal_fileops *fops = NULL;
267     osal_fileops_info *fileops_node = NULL;
268 
269     if (file == NULL) {
270         osal_printk("%s - file NULL!\n", __FUNCTION__);
271         return -1;
272     }
273 
274     arg_size = _IOC_SIZE(cmd);
275 
276     if (_IOC_DIR(cmd) != _IOC_NONE) {
277         if ((arg_size == 0) || (((char *)(uintptr_t)arg) == NULL)) {
278             return -1; // arg_size max 0x4000, no need to check max
279         }
280     }
281 
282     fileops_node = file->private_data;
283     if (fileops_node == NULL) {
284         return -1;
285     }
286 
287     fops = fileops_node->fops;
288     if (fops == NULL) {
289         return -1;
290     }
291 
292     if ((fops->cmd_cnt > CMD_NUM_MAX) || (fops->cmd_cnt == 0) || (fops->cmd_list == NULL)) {
293         return -1;
294     }
295 
296     ret = osal_ioctl_copy_from_user(cmd, arg, &ioctl_arg, arg_size, arg_buf);
297     if (ret != 0) {
298         goto OUT;
299     }
300 
301     ret = osal_ioctl_call_fun(cmd, ioctl_arg, fops, (void *)&(fileops_node->private_data));
302     if (ret != 0) {
303         goto OUT;
304     }
305 
306     ret = osal_ioctl_copy_to_user(cmd, arg, ioctl_arg, arg_size);
307     if (ret != 0) {
308         goto OUT;
309     }
310 
311 OUT:
312     if ((ioctl_arg != NULL) && (arg_size > ARG_BUF_TEMP_SIZE)) {
313         kfree(ioctl_arg);
314     }
315 
316     return ret;
317 }
318 
__osal_poll(struct file * file,struct poll_table_struct * table)319 static unsigned int __osal_poll(struct file *file, struct poll_table_struct *table)
320 {
321     unsigned int ret = 0;
322     osal_poll t;
323     osal_fileops *fops = NULL;
324     osal_fileops_info *fileops_node = NULL;
325 
326     t.poll_table = table;
327     t.data = file;
328 
329     fileops_node = file->private_data;
330     if (fileops_node == NULL) {
331         osal_printk("%s - fileops_node NULL!\n", __FUNCTION__);
332         return -1;
333     }
334 
335     fops = fileops_node->fops;
336     if (fops == NULL) {
337         osal_printk("%s - fops NULL!\n", __FUNCTION__);
338         return -1;
339     }
340 
341     if (fops->poll != NULL) {
342         ret = fops->poll(&t, (void *)&(fileops_node->private_data));
343     }
344 
345     return ret;
346 }
347 
osal_valid_mmap_phys_addr_range(unsigned long pfn,size_t size)348 int osal_valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
349 {
350     /* check physical addr greater than the max addr supported by the system */
351     if ((pfn + (size >> PAGE_SHIFT)) > (1 + ((~0UL) >> PAGE_SHIFT))) {
352         osal_printk("--%s - %d--!\n", __FUNCTION__, __LINE__);
353         return 0;
354     }
355 
356     /* It's necessary for the variable "size" to align 4k(page_size). */
357 #define PAGE_SIZE_MASK 0xfffffffffffff000ULL
358     if ((unsigned long)size & (~PAGE_SIZE_MASK)) {
359         osal_printk("--%s - %d--!\n", __FUNCTION__, __LINE__);
360         return 0;
361     }
362 #undef PAGE_SIZE_MASK
363 
364     return 1;
365 }
366 
osal_mmap(struct file * file,struct vm_area_struct * vm)367 static int osal_mmap(struct file *file, struct vm_area_struct *vm)
368 {
369     int ret = 0;
370     osal_vm osal_vm;
371     osal_fileops *fops = NULL;
372     osal_fileops_info *fileops_node = NULL;
373 
374     if (!osal_valid_mmap_phys_addr_range(vm->vm_pgoff, vm->vm_end - vm->vm_start)) {
375         osal_printk("\n%s - invalid argument   size=%ld!!!\n", __FUNCTION__, vm->vm_end - vm->vm_start);
376         return -EINVAL;
377     }
378 
379     fileops_node = file->private_data;
380     if (fileops_node == NULL) {
381         osal_printk("%s - fileops_node NULL!\n", __FUNCTION__);
382         return -1;
383     }
384 
385     fops = fileops_node->fops;
386     if (fops == NULL) {
387         osal_printk("%s - fops NULL!\n", __FUNCTION__);
388         return -1;
389     }
390 
391     osal_vm.vm = vm;
392     if (fops->mmap != NULL) {
393         ret = fops->mmap(&osal_vm, vm->vm_start, vm->vm_end, vm->vm_pgoff, (void *)&(fileops_node->private_data));
394     }
395 
396     return ret;
397 }
398 
399 static struct file_operations g_osal_fops = {
400     .owner = THIS_MODULE,
401     .open = osal_open,
402     .read = osal_read,
403     .write = osal_write,
404     .llseek = osal_llseek,
405     .unlocked_ioctl = osal_ioctl,
406     .release = osal_release,
407     .poll = __osal_poll,
408     .mmap = osal_mmap,
409 #ifdef CONFIG_COMPAT
410     .compat_ioctl = osal_ioctl,
411 #endif
412 };
413 
osal_pm_shutdown(pm_basedev * pm_base)414 static void osal_pm_shutdown(pm_basedev *pm_base)
415 {
416     osal_pmops *pmops = NULL;
417 
418     pmops = osal_get_pmops(pm_base);
419     if (pmops == NULL) {
420         return;
421     }
422 
423     if (pmops->pm_poweroff != NULL) {
424         (void)pmops->pm_poweroff(pmops->private_data);
425     }
426 }
427 
osal_pm_suspend(pm_basedev * pm_base,pm_message_t state)428 static int osal_pm_suspend(pm_basedev *pm_base, pm_message_t state)
429 {
430     osal_pmops *pmops = NULL;
431 
432     pmops = osal_get_pmops(pm_base);
433     if (pmops == NULL) {
434         return 0;
435     }
436 
437     if (pmops->pm_suspend != NULL) {
438         return pmops->pm_suspend(pmops->private_data);
439     }
440 
441     return -1;
442 }
443 
osal_pm_resume(pm_basedev * pm_base)444 static int osal_pm_resume(pm_basedev *pm_base)
445 {
446     osal_pmops *pmops = NULL;
447 
448     pmops = osal_get_pmops(pm_base);
449     if (pmops == NULL) {
450         return 0;
451     }
452 
453     if (pmops->pm_resume != NULL) {
454         return pmops->pm_resume(pmops->private_data);
455     }
456 
457     return -1;
458 }
459 
osal_pm_lowpower_enter(void)460 void osal_pm_lowpower_enter(void)
461 {
462     osal_dev_info *dev_node = NULL;
463 
464     osal_list_for_each_entry(dev_node, &dev_list, node) {
465         if ((dev_node->dev != NULL) && (dev_node->dev->pmops != NULL)
466             && (dev_node->dev->pmops->pm_lowpower_enter != NULL)) {
467             dev_node->dev->pmops->pm_lowpower_enter(dev_node->dev->pmops->private_data);
468         }
469     }
470 }
471 EXPORT_SYMBOL(osal_pm_lowpower_enter);
472 
osal_pm_lowpower_exit(void)473 void osal_pm_lowpower_exit(void)
474 {
475     osal_dev_info *dev_node = NULL;
476 
477     osal_list_for_each_entry_reverse(dev_node, &dev_list, node) {
478         if ((dev_node->dev != NULL) && (dev_node->dev->pmops != NULL)
479             && (dev_node->dev->pmops->pm_lowpower_exit != NULL)) {
480             dev_node->dev->pmops->pm_lowpower_exit(dev_node->dev->pmops->private_data);
481         }
482     }
483 }
484 EXPORT_SYMBOL(osal_pm_lowpower_exit);
485 
486 static pm_baseops g_osal_pmops = {
487     .probe = NULL,
488     .remove = NULL,
489     .shutdown = osal_pm_shutdown,
490     .prepare = NULL,
491     .complete = NULL,
492     .suspend = osal_pm_suspend,
493     .suspend_late = NULL,
494     .resume_early = NULL,
495     .resume = osal_pm_resume,
496 };
497 
osal_dev_register(osal_dev * dev)498 int osal_dev_register(osal_dev *dev)
499 {
500     int ret;
501     osal_dev_info *dev_node = NULL;
502     pm_device *media = NULL;
503     errno_t err;
504 
505     if (dev == NULL) {
506         osal_printk("%s - dev invalid!\n", __FUNCTION__);
507         return -1;
508     }
509 
510     dev_node = (osal_dev_info *)kmalloc(sizeof(osal_dev_info), GFP_KERNEL);
511     if (dev_node == NULL) {
512         osal_printk("%s - kmalloc error!\n", __FUNCTION__);
513         return -1;
514     }
515 
516     err = memset_s(dev_node, sizeof(osal_dev_info), 0, sizeof(osal_dev_info));
517     if (err != EOK) {
518         kfree(dev_node);
519         dev_node = NULL;
520         osal_printk("memset_s is failed.\n");
521         return -1;
522     }
523     dev_node->dev = dev;
524     dev->dev = (void *)dev_node;
525 
526     media = &dev_node->media;
527 
528     if (dev->minor != 0) {
529         media->minor = dev->minor;
530     } else {
531         media->minor = HIMEDIA_DYNAMIC_MINOR;
532     }
533 
534     media->owner = THIS_MODULE;
535     media->app_ops = &g_osal_fops;
536     media->base_ops = &g_osal_pmops;
537     media->name = dev->name;
538     media->dev = dev;
539 
540     ret =  hi_drv_pm_register(media);
541     if (ret != 0) {
542         kfree(dev_node);
543         return -1;
544     }
545 
546     write_lock(&g_dev_rwlock);
547     osal_list_add(&dev_node->node, &dev_list);
548     write_unlock(&g_dev_rwlock);
549 
550     return 0;
551 }
552 EXPORT_SYMBOL(osal_dev_register);
553 
osal_dev_unregister(osal_dev * dev)554 void osal_dev_unregister(osal_dev *dev)
555 {
556     osal_dev_info *dev_node = NULL;
557 
558     if (dev == NULL || dev->dev == NULL) {
559         osal_printk("%s - dev invalid!\n", __FUNCTION__);
560         return;
561     }
562 
563     dev_node = (osal_dev_info *)(dev->dev);
564 
565     hi_drv_pm_un_register(&dev_node->media);
566 
567     write_lock(&g_dev_rwlock);
568     osal_list_del(&dev_node->node);
569     write_unlock(&g_dev_rwlock);
570 
571     kfree(dev_node);
572     dev->dev = NULL;
573 }
574 EXPORT_SYMBOL(osal_dev_unregister);
575 
osal_poll_wait(osal_poll * table,osal_wait * wait)576 void osal_poll_wait(osal_poll *table, osal_wait *wait)
577 {
578     if (table == NULL) {
579         osal_printk("%s - table(NULL) invalid!\n", __FUNCTION__);
580         return;
581     }
582 
583     if (wait == NULL) {
584         osal_printk("%s - wait(NULL) invalid!\n", __FUNCTION__);
585         return;
586     }
587 
588     poll_wait((struct file *)table->data, (wait_queue_head_t *)(wait->wait), table->poll_table);
589 }
590 EXPORT_SYMBOL(osal_poll_wait);
591 
osal_remap_pfn_range(osal_vm * vm,unsigned long addr,unsigned long pfn,unsigned long size,unsigned int cached)592 int osal_remap_pfn_range(osal_vm *vm, unsigned long addr, unsigned long pfn, unsigned long size, unsigned int cached)
593 {
594     struct vm_area_struct *v = NULL;
595 
596     if ((vm == NULL) || (vm->vm == NULL)) {
597         osal_printk("%s - vm(NULL) invalid!\n", __FUNCTION__);
598         return -1;
599     }
600 
601     if (size == 0) {
602         osal_printk("%s - size(0) invalid!\n", __FUNCTION__);
603         return -1;
604     }
605 
606     v = (struct vm_area_struct *)(vm->vm);
607 
608     if (cached == OSAL_NOCACHE) {
609         v->vm_page_prot = pgprot_noncached(v->vm_page_prot);
610     }
611 
612     return remap_pfn_range(v, addr, pfn, size, v->vm_page_prot);
613 }
614 EXPORT_SYMBOL(osal_remap_pfn_range);
615 
osal_try_to_freeze(void)616 int osal_try_to_freeze(void)
617 {
618     return try_to_freeze();
619 }
620 EXPORT_SYMBOL(osal_try_to_freeze);
621 
osal_set_freezable(void)622 int osal_set_freezable(void)
623 {
624     return set_freezable();
625 }
626 EXPORT_SYMBOL(osal_set_freezable);
627 
osal_irq_mmap(osal_vm * vm,void * ptr,unsigned long start,unsigned long sz)628 int osal_irq_mmap(osal_vm *vm, void *ptr, unsigned long start, unsigned long sz)
629 {
630     unsigned long pfn;
631     struct vm_area_struct *v = NULL;
632     struct page *page = NULL;
633     if (vm == NULL) {
634         return -EINVAL;
635     }
636     page = virt_to_head_page(ptr);
637     if (sz > (PAGE_SIZE << compound_order(page))) {
638         return -EINVAL;
639     }
640 
641     pfn = virt_to_phys(ptr) >> PAGE_SHIFT;
642     v = (struct vm_area_struct *)(vm->vm);
643     return remap_pfn_range(v, start, pfn, sz, v->vm_page_prot);
644 }
645 EXPORT_SYMBOL(osal_irq_mmap);
646