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