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