• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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/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 
34 #include "base.h"
35 
36 static OSAL_LIST_HEAD(himedia_list);
37 static DEFINE_MUTEX(himedia_sem);
38 
39 /*
40  * Assigned numbers, used for dynamic minors
41  */
42 #define DYNAMIC_MINORS 64 /* like dynamic majors */
43 static unsigned char g_himedia_minors[DYNAMIC_MINORS / 8]; /* 8: bitmap 1Byte has 8 bit */
44 
himedia_open(struct inode * inode,struct file * file)45 static int himedia_open(struct inode *inode, struct file *file)
46 {
47     int minor = iminor(inode);
48     struct himedia_device *c = NULL;
49     int err = -ENODEV;
50     const struct file_operations *old_fops = NULL;
51     const struct file_operations *new_fops = NULL;
52 
53     mutex_lock(&himedia_sem);
54 
55     osal_list_for_each_entry(c, &himedia_list, list) {
56         if (c->minor == (unsigned int)minor) {
57             new_fops = fops_get(c->fops);
58             break;
59         }
60     }
61 
62     if (new_fops == NULL) {
63         mutex_unlock(&himedia_sem);
64         request_module("char-major-%d-%d", HIMEDIA_DEVICE_MAJOR, minor);
65         mutex_lock(&himedia_sem);
66 
67         osal_list_for_each_entry(c, &himedia_list, list) {
68             if (c->minor == (unsigned int)minor) {
69                 new_fops = fops_get(c->fops);
70                 break;
71             }
72         }
73 
74         if (new_fops == NULL) {
75             goto fail;
76         }
77     }
78 
79     err = 0;
80 
81     old_fops = file->f_op;
82     file->f_op = new_fops;
83     if (file->f_op->open) {
84         file->private_data = c;
85         err = file->f_op->open(inode, file);
86         if (err) {
87             fops_put(file->f_op);
88             file->private_data = NULL;
89             file->f_op = fops_get(old_fops);
90         }
91     }
92 
93     fops_put(old_fops);
94 fail:
95     mutex_unlock(&himedia_sem);
96     return err;
97 }
98 
99 static struct file_operations g_himedia_fops = {
100     .owner = THIS_MODULE,
101     .open = himedia_open,
102 };
103 
104 /*
105  * himedia_register - register a himedia device
106  * @himedia: device structure
107  *
108  * Register a himedia device with the kernel. If the minor
109  * number is set to %HIMEDIA_DYNAMIC_MINOR a minor number is assigned
110  * and placed in the minor field of the structure. For other cases
111  * the minor number requested is used.
112  *
113  * The structure passed is linked into the kernel and may not be
114  * destroyed until it has been unregistered.
115  *
116  * A zero is returned on success and a negative errno code for
117  * failure.
118  */
himedia_register(struct himedia_device * himedia)119 int himedia_register(struct himedia_device *himedia)
120 {
121     struct himedia_device *ptmp = NULL;
122     struct himedia_driver *pdrv = NULL;
123     int err;
124 
125     if (himedia == NULL) {
126         return -EBUSY;
127     }
128     mutex_lock(&himedia_sem);
129 
130     /* check if registered */
131     osal_list_for_each_entry(ptmp, &himedia_list, list) {
132         if (ptmp->minor == himedia->minor) {
133             mutex_unlock(&himedia_sem);
134             return -EBUSY;
135         }
136     }
137 
138     /* check minor */
139     if (himedia->minor == HIMEDIA_DYNAMIC_MINOR) {
140         int i = DYNAMIC_MINORS;
141 
142         while (--i >= 0)
143             if ((g_himedia_minors[(unsigned int)i >> 3] &  /* 3: left shift 3bit to locate the index of char array */
144                 (1 << ((unsigned int)i & 7))) == 0) { /* 7: locate the bit in bitmap */
145                 break;
146             }
147         if (i < 0) {
148             mutex_unlock(&himedia_sem);
149             return -EBUSY;
150         }
151 
152         himedia->minor = i;
153     }
154 
155     if (himedia->minor < DYNAMIC_MINORS) {
156         g_himedia_minors[himedia->minor >> 3] |= 1 << (himedia->minor & 7); /* 3, 7: bitmap write set bit 1 */
157     }
158 
159     /* device register */
160     err = himedia_device_register(himedia);
161     if (err < 0) {
162         g_himedia_minors[himedia->minor >> 3] &= ~(1 << (himedia->minor & 7)); /* 3, 7: bitmap write set bit 0 */
163         goto out;
164     }
165 
166     /* driver register */
167     pdrv = himedia_driver_register(himedia->devfs_name, himedia->owner, himedia->drvops);
168     if (IS_ERR(pdrv)) {
169         himedia_device_unregister(himedia);
170 
171         g_himedia_minors[himedia->minor >> 3] &= ~(1 << (himedia->minor & 7)); /* 3, 7: bitmap write set bit 0 */
172 
173         err = PTR_ERR(pdrv);
174         goto out;
175     }
176 
177     himedia->driver = pdrv;
178 
179     /*
180      * Add it to the front, so that later devices can "override"
181      * earlier defaults
182      */
183     osal_list_add(&himedia->list, &himedia_list);
184 
185 out:
186     mutex_unlock(&himedia_sem);
187     return err;
188 }
189 EXPORT_SYMBOL(himedia_register);
190 
191 /*
192  * himedia_unregister - unregister a himedia device
193  * @himedia: device to unregister
194  *
195  * Unregister a himedia device that was previously
196  * successfully registered with himedia_register(). Success
197  * is indicated by a zero return, a negative errno code
198  * indicates an error.
199  */
himedia_unregister(struct himedia_device * himedia)200 int himedia_unregister(struct himedia_device *himedia)
201 {
202     struct himedia_device *ptmp = NULL;
203     struct himedia_device *_ptmp = NULL;
204 
205     if (himedia == NULL) {
206         return -EINVAL;
207     }
208 
209     if (osal_list_empty(&himedia->list)) {
210         return -EINVAL;
211     }
212 
213     mutex_lock(&himedia_sem);
214 
215     osal_list_for_each_entry_safe(ptmp, _ptmp, &himedia_list, list) {
216         /* if found, unregister device & driver */
217         if (ptmp->minor == himedia->minor) {
218             osal_list_del(&himedia->list);
219 
220             himedia_driver_unregister(himedia->driver);
221 
222             himedia->driver = NULL;
223 
224             himedia_device_unregister(himedia);
225 
226             g_himedia_minors[himedia->minor >> 3] &= ~(1 << (himedia->minor & 7)); /* 3, 7: bitmap write set bit 0 */
227 
228             break;
229         }
230     }
231 
232     mutex_unlock(&himedia_sem);
233 
234     return 0;
235 }
236 EXPORT_SYMBOL(himedia_unregister);
237 
himedia_init(void)238 int himedia_init(void)
239 {
240     int ret;
241     ret = himedia_bus_init();
242     if (ret) {
243         goto err0;
244     }
245 
246     ret = -EIO;
247 
248     if (register_chrdev(HIMEDIA_DEVICE_MAJOR, "himedia", &g_himedia_fops)) {
249         goto err1;
250     }
251 
252     printk("Module himedia: init ok\n");
253 
254     return 0;
255 
256 err1:
257     himedia_bus_exit();
258 err0:
259     return ret;
260 }
261 
himedia_exit(void)262 void himedia_exit(void)
263 {
264     if (!osal_list_empty(&himedia_list)) {
265         printk("!!! Module himedia: sub module in list\n");
266         return;
267     }
268 
269     unregister_chrdev(HIMEDIA_DEVICE_MAJOR, "himedia");
270 
271     himedia_bus_exit();
272 
273     printk("!!! Module himedia: exit ok\n");
274 }
275