• 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 #include <linux/module.h>
19 #include <linux/version.h>
20 #include <linux/fs.h>
21 #include <linux/errno.h>
22 #include <linux/kernel.h>
23 #include <linux/major.h>
24 #include <linux/slab.h>
25 #include <linux/mutex.h>
26 #include <linux/proc_fs.h>
27 #include <linux/seq_file.h>
28 #include <linux/stat.h>
29 #include <linux/init.h>
30 #include <linux/device.h>
31 #include <linux/tty.h>
32 #include <linux/kmod.h>
33 #include <linux/delay.h>
34 
35 #include "himedia.h"
36 #include "himedia_base.h"
37 
38 
39 #define mkstr(exp)        #exp
40 #define mk_marco_to_str(exp) mkstr(exp)
41 #define VERSION_STRING    ("SDK_VERSION:[" mk_marco_to_str(SDK_VERSION) "] Build Time:[" __DATE__ ", " __TIME__ "]")
42 
43 /*
44  * Head entry for the doubly linked himedia_device list
45  */
46 static LIST_HEAD(himedia_list);
47 static DEFINE_MUTEX(himedia_sem);
48 
49 /*
50  * Assigned numbers, used for dynamic minors
51  */
52 #define DYNAMIC_MINORS 128 /* like dynamic majors */
53 static unsigned char g_himedia_minors[DYNAMIC_MINORS / 8]; /* 8 表示8为粒度 */
54 
55 #ifdef CONFIG_PROC_FS
56 #if (1 == HI_PROC_SUPPORT)
himedia_seq_start(struct seq_file * seq,loff_t * pos)57 static void *himedia_seq_start(struct seq_file *seq, loff_t *pos)
58 {
59     mutex_lock(&himedia_sem);
60     return seq_list_start(&himedia_list, *pos);
61 }
62 
himedia_seq_next(struct seq_file * seq,void * v,loff_t * pos)63 static void *himedia_seq_next(struct seq_file *seq, void *v, loff_t *pos)
64 {
65     return seq_list_next(v, &himedia_list, pos);
66 }
67 
himedia_seq_stop(struct seq_file * seq,void * v)68 static void himedia_seq_stop(struct seq_file *seq, void *v)
69 {
70     mutex_unlock(&himedia_sem);
71 }
72 
himedia_seq_show(struct seq_file * seq,void * v)73 static int himedia_seq_show(struct seq_file *seq, void *v)
74 {
75     const pm_device *p = list_entry(v, pm_device, list);
76     if (p == NULL) {
77         return -1;
78     }
79     seq_printf(seq, "%3i %s\n", p->minor, (char *)p->name ? (char *)p->name : "");
80     return 0;
81 }
82 
83 static struct seq_operations g_himedia_seq_ops = {
84     .start = himedia_seq_start,
85     .next = himedia_seq_next,
86     .stop = himedia_seq_stop,
87     .show = himedia_seq_show,
88 };
89 
himedia_seq_open(struct inode * inode,struct file * file)90 static int himedia_seq_open(struct inode *inode, struct file *file)
91 {
92     return seq_open(file, &g_himedia_seq_ops);
93 }
94 
95 #if defined(LINUX_VERSION_CODE) && (LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0))
96 static struct file_operations g_himedia_proc_fops = {
97     .owner = THIS_MODULE,
98     .open = himedia_seq_open,
99     .read = seq_read,
100     .llseek = seq_lseek,
101     .release = seq_release,
102 };
103 #else
104 static struct proc_ops g_himedia_proc_fops = {
105     .proc_open = himedia_seq_open,
106     .proc_read = seq_read,
107     .proc_lseek = seq_lseek,
108     .proc_release = seq_release,
109 };
110 #endif
111 #endif
112 #endif
113 
himedia_open(struct inode * inode,struct file * file)114 static int himedia_open(struct inode *inode, struct file *file)
115 {
116     int minor = iminor(inode);
117     pm_device *c = NULL;
118     int err = -1;
119     const struct file_operations *old_fops = NULL;
120     const struct file_operations *new_fops = NULL;
121 
122     mutex_lock(&himedia_sem);
123 
124     list_for_each_entry(c, &himedia_list, list)
125     {
126         if (c->minor == minor) {
127             new_fops = fops_get(c->app_ops);
128             break;
129         }
130     }
131 
132     if (new_fops == NULL) {
133         goto fail;
134     }
135 
136     err = 0;
137     old_fops = file->f_op;
138     file->f_op = new_fops;
139     if (file->f_op->open) {
140         file->private_data = c->dev;
141         err = file->f_op->open(inode, file);
142         if (err) {
143             fops_put(file->f_op);
144             file->f_op = fops_get(old_fops);
145         }
146     }
147     fops_put(old_fops);
148 fail:
149     mutex_unlock(&himedia_sem);
150     return err;
151 }
152 
153 static struct class *g_himedia_class;
154 
155 static struct file_operations g_himedia_fops = {
156     .owner = THIS_MODULE,
157     .open = himedia_open,
158 };
159 
160 /**
161  *    hi_drv_pm_register    -    register a himedia device
162  *    @himedia: device structure
163  *
164  *    Register a himedia device with the kernel. If the minor
165  *    number is set to %HIMEDIA_DYNAMIC_MINOR a minor number is assigned
166  *    and placed in the minor field of the structure. For other cases
167  *    the minor number requested is used.
168  *
169  *    The structure passed is linked into the kernel and may not be
170  *    destroyed until it has been unregistered.
171  *
172  *    A zero is returned on success and a negative errno code for
173  *    failure.
174  */
pm_register_get_media_minor(pm_device * himedia)175 static hi_s32 pm_register_get_media_minor(pm_device *himedia)
176 {
177     pm_device *c = NULL;
178     hi_s32 i = DYNAMIC_MINORS;
179 
180     list_for_each_entry(c, &himedia_list, list) {
181         if (c->minor == himedia->minor) {
182         return -EBUSY;
183         }
184     }
185     if (himedia->minor == HIMEDIA_DYNAMIC_MINOR) {
186         while (--i >= 0)
187             if ((g_himedia_minors[(hi_u32)i >> 3] & (1 << ((hi_u32)i & 7))) == 0) { /* 3 1 7 表示偏移位数 */
188                 break;
189             }
190 
191         if (i < 0) {
192             return -EBUSY;
193         }
194         himedia->minor = i;
195     }
196 
197     if (himedia->minor < DYNAMIC_MINORS) {
198         g_himedia_minors[himedia->minor >> 3] |= 1 << (himedia->minor & 7);  /* 3 1 7 表示偏移位数 */
199     }
200     return HI_SUCCESS;
201 }
202 
pm_register_add_media_device(pm_device * himedia,pm_basedev ** bdev)203 static hi_s32 pm_register_add_media_device(pm_device *himedia, pm_basedev **bdev)
204 {
205     hi_s32 ret;
206 
207     if (bdev == NULL) {
208         return HI_FAILURE;
209     }
210 
211     *bdev = himedia_device_alloc(himedia->name, -1);
212     if (*bdev == NULL) {
213         return -ENOMEM;
214     }
215     ret = himedia_device_add(*bdev);
216     if (ret) {
217         himedia_device_put(*bdev);
218         return ret;
219     }
220     return HI_SUCCESS;
221 }
222 
223 
pm_register_device_create(pm_device * himedia,pm_basedev * bdev,dev_t * dev,struct device ** adev)224 static hi_s32 pm_register_device_create(pm_device *himedia,
225     pm_basedev *bdev, dev_t *dev, struct device **adev)
226 {
227     hi_s32 ret;
228 
229     *dev = MKDEV(HIMEDIA_DEVICE_MAJOR, himedia->minor);
230     *adev = device_create(g_himedia_class, &(bdev->dev), *dev, NULL,
231         "%s", himedia->name);
232     if (IS_ERR(*adev)) {
233         ret = PTR_ERR(*adev);
234         return ret;
235     }
236 
237     return HI_SUCCESS;
238 }
239 
240 
pm_register_device_register(pm_device * himedia,struct device * adev,pm_basedev * bdev)241 static hi_s32 pm_register_device_register(pm_device *himedia,
242     struct device *adev, pm_basedev *bdev)
243 {
244     pm_basedrv *bdrv = NULL;
245     hi_s32 ret;
246 
247     bdrv = himedia_driver_alloc(himedia->name, himedia->owner, himedia->base_ops);
248     if (bdrv == NULL) {
249         return -ENOMEM;
250     }
251 
252     ret = himedia_driver_register(bdrv);
253     if (ret) {
254         himedia_driver_release(bdrv);
255         return ret;
256     }
257 
258     himedia->app_device = adev;
259     himedia->base_device = bdev;
260     himedia->base_driver = bdrv;
261 
262     return HI_SUCCESS;
263 }
hi_drv_pm_register(pm_device * himedia)264 hi_s32 hi_drv_pm_register(pm_device *himedia)
265 {
266     int ret;
267     dev_t dev;
268     struct device *adev = NULL;
269     pm_basedev *bdev = NULL;
270 
271     if ((himedia == NULL) || (himedia->name == NULL) || (himedia->app_ops == NULL)) {
272         return -EINVAL;
273     }
274 
275 #ifdef MODULE
276     if (himedia->owner == NULL) {
277         return -EINVAL;
278     }
279 #endif
280     mutex_lock(&himedia_sem);
281     // 1
282     ret = pm_register_get_media_minor(himedia);
283     if (ret != HI_SUCCESS) {
284         goto out;
285     }
286 
287     // 2 base device, then class = NULL;
288     ret = pm_register_add_media_device(himedia, &bdev);
289     if (ret != HI_SUCCESS) {
290         goto err0;
291     }
292 
293     // 3 app class
294     ret = pm_register_device_create(himedia, bdev, &dev, &adev);
295     if (ret != HI_SUCCESS) {
296         goto err1;
297     }
298 
299     // 4 base driver
300     ret = pm_register_device_register(himedia, adev, bdev);
301     if (ret != HI_SUCCESS) {
302         goto err2;
303     }
304     /*
305      * Add it to the front, so that later devices can "override"
306      * earlier defaults
307      */
308     INIT_LIST_HEAD(&himedia->list);
309     list_add(&himedia->list, &himedia_list);
310     goto out;
311 
312 err2:
313     device_destroy(g_himedia_class, dev);
314 err1:
315     himedia_device_unregister(bdev);
316 err0:
317     g_himedia_minors[himedia->minor >> 3] &= ~(1 << (himedia->minor & 7)); /* 3 1 7 表示偏移位数 */
318 out:
319     mutex_unlock(&himedia_sem);
320     return ret;
321 }
322 
323 /**
324  *    hi_drv_pm_un_register - unregister a himedia device
325  *    @himedia: device to unregister
326  *
327  *    Unregister a himedia device that was previously
328  *    successfully registered with hi_drv_pm_register(). Success
329  *    is indicated by a zero return, a negative errno code
330  *    indicates an error.
331  */
hi_drv_pm_un_register(pm_device * himedia)332 hi_s32 hi_drv_pm_un_register(pm_device *himedia)
333 {
334     hi_u32 i;
335 
336     if (g_himedia_class == NULL || himedia == NULL || (himedia->name == NULL) || (himedia->app_ops == NULL)) {
337         return -EINVAL;
338     }
339 #ifdef MODULE
340     if (himedia->owner == NULL) {
341         return -EINVAL;
342     }
343 #endif
344     i = himedia->minor;
345     // 0
346     if (list_empty(&himedia->list)) {
347         return -EINVAL;
348     }
349     if ((i >= DYNAMIC_MINORS) || (i < 0)) {
350         return -EINVAL;
351     }
352     // 1
353     mutex_lock(&himedia_sem);
354     // 1.0
355     list_del(&himedia->list);
356     // 1.1
357     if (himedia->base_driver) {
358         himedia_driver_unregister(himedia->base_driver);
359         himedia_driver_release(himedia->base_driver);
360         himedia->base_driver = NULL;
361     }
362     // 1.2
363     if (himedia->app_device) {
364         device_destroy(g_himedia_class, MKDEV(HIMEDIA_DEVICE_MAJOR, himedia->minor));
365         himedia->app_device = NULL;
366     }
367     // 1.3
368     if (himedia->base_device) {
369         himedia_device_unregister(himedia->base_device);
370         himedia->base_device = NULL;
371     }
372     // 1.4
373     g_himedia_minors[i >> 3] &= ~(1 << (i & 7)); /* 3 1 7表示偏移位数 */
374     mutex_unlock(&himedia_sem);
375     return 0;
376 }
377 
drv_pm_mod_init(hi_void)378 hi_s32 drv_pm_mod_init(hi_void)
379 {
380     int ret;
381     // 0
382 #if !(0 == HI_PROC_SUPPORT)
383     proc_create("himedia", 0, NULL, &g_himedia_proc_fops);
384 #endif
385     // 1
386     ret = himedia_bus_init();
387     if (ret) {
388         goto err0;
389     }
390     // 2
391     g_himedia_class = class_create(THIS_MODULE, "himedia_class");
392     ret = PTR_ERR(g_himedia_class);
393     if (IS_ERR(g_himedia_class)) {
394         goto err1;
395     }
396     // 3
397     ret = -EIO;
398     if (register_chrdev(HIMEDIA_DEVICE_MAJOR, "himedia_char_dev", &g_himedia_fops)) {
399         goto err2;
400     }
401 #if defined(MODULE)
402     printk("Load hi_media.ko success.\t(%s)\n", VERSION_STRING);
403 #endif
404     return 0;
405     // 4
406 err2:
407 
408     printk("!!! Module himedia: unable to get major %d for himedia devices\n", HIMEDIA_DEVICE_MAJOR);
409 
410     class_destroy(g_himedia_class);
411 err1:
412     himedia_bus_exit();
413 err0:
414 #if !(0 == HI_PROC_SUPPORT)
415     remove_proc_entry("himedia", NULL);
416 #endif
417     return ret;
418 }
419 
drv_pm_mod_exit(hi_void)420 hi_void drv_pm_mod_exit(hi_void)
421 {
422     // 0
423     if (list_empty(&himedia_list) == 0) {
424         return;
425     }
426     // 1
427     unregister_chrdev(HIMEDIA_DEVICE_MAJOR, "himedia");
428     // 2
429     class_destroy(g_himedia_class);
430     // 3
431     himedia_bus_exit();
432     // 4
433 #if !(0 == HI_PROC_SUPPORT)
434     remove_proc_entry("himedia", NULL);
435 #endif
436 #if defined(MODULE)
437     printk("remove hi_media.ko success.\n");
438 #endif
439     return;
440 }
441 
442 #ifdef MODULE
443 module_init(drv_pm_mod_init);
444 module_exit(drv_pm_mod_exit);
445 #else
446 #ifdef CFG_HI_USER_DRV
447 subsys_initcall(drv_pm_mod_init);
448 module_exit(drv_pm_mod_exit);
449 #endif
450 #endif
451 
452 EXPORT_SYMBOL(hi_drv_pm_register);
453 EXPORT_SYMBOL(hi_drv_pm_un_register);
454 EXPORT_SYMBOL(drv_pm_mod_init);
455 EXPORT_SYMBOL(drv_pm_mod_exit);
456 
457 MODULE_LICENSE("GPL");
458