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/seq_file.h>
20 #include <linux/slab.h>
21 #include <linux/miscdevice.h>
22 
23 #include "securec.h"
24 #include "hi_osal.h"
25 #include "hiproc.h"
26 
27 
28 typedef struct {
29     wait_queue_head_t wq_for_read;
30     wait_queue_head_t wq_for_write;
31     wait_queue_head_t wq_for_cmd;
32 } proc_k_para;
33 static hi_s32 g_cmd_condition = 0;
34 static hi_s32 g_read_condition = 0;
35 static hi_s32 g_write_condition = 0;
36 static hi_s32 g_cmd_exit_condition = 0;
37 
38 typedef struct entrylist {
39     hi_u32 private_data;
40     hi_char entry_name[MAX_PROC_NAME_LEN];
41     struct entrylist *next;
42 } hi_entry_list;
43 
44 static hi_entry_list *g_proc_head = HI_NULL;
45 
46 static osal_mutex g_mutex = {
47     .mutex = HI_NULL
48 };
49 
50 static hi_proc_para proc_wait;
51 static proc_k_para g_proc_k_para;
52 
53 static hi_s32 g_dbg_flag = 1;
54 #define hiproc_dbg(params...) do { \
55         if (g_dbg_flag) {         \
56             osal_printk(params);  \
57         }                         \
58     } while (0)
59 
60 hi_void osal_proc_exit(hi_void);
61 static hi_s32 proc_read(void *seqfile, void *private);
62 static hi_s32 proc_write(osal_proc_entry *entry, const hi_char *buf, hi_s32 count, hi_s64 *ppos);
63 
add_list(const hi_char *name, const hi_u32 private_data)64 static hi_entry_list *add_list(const hi_char *name, const hi_u32 private_data)
65 {
66     hi_entry_list *tmp;
67     tmp = (hi_entry_list *)osal_kmalloc(0, sizeof(hi_entry_list), OSAL_GFP_KERNEL);
68     if (tmp == NULL) {
69         return tmp;
70     }
71     (void)memset_s(tmp->entry_name, sizeof(tmp->entry_name), 0, sizeof(tmp->entry_name));
72     (void)memcpy_s(tmp->entry_name, sizeof(tmp->entry_name), name, strlen(name) + 1);
73     tmp->private_data = private_data;
74     tmp->next = g_proc_head;
75     g_proc_head = tmp;
76     return tmp;
77 }
78 
get_list_and_delete_node(const hi_u32 private_data)79 static hi_void get_list_and_delete_node(const hi_u32 private_data)
80 {
81     hi_entry_list *tmp = g_proc_head;
82     hi_entry_list *pre = g_proc_head;
83     hi_entry_list *tmp1 = HI_NULL;
84     while (tmp != HI_NULL) {
85         if (tmp->private_data == private_data) {
86             if (tmp == pre) { // del head
87                 g_proc_head = tmp->next;
88                 tmp1 = g_proc_head;
89                 pre = g_proc_head;
90             } else {
91                 pre->next = tmp->next;
92                 tmp1 = pre->next;
93             }
94             osal_proc_remove(tmp->entry_name, strlen(tmp->entry_name));
95             osal_kfree(0, tmp);
96             tmp = tmp1;
97             continue;
98         }
99         pre = tmp;
100         tmp = tmp->next;
101     }
102     return;
103 }
104 
del_list_node(const hi_u32 private_data, const hi_char *name)105 static hi_s32 del_list_node(const hi_u32 private_data, const hi_char *name)
106 {
107     hi_entry_list *tmp = g_proc_head;
108     hi_entry_list *pre = g_proc_head;
109     while (tmp != HI_NULL) {
110         if (tmp->private_data == private_data && (!strncmp(tmp->entry_name, name, strlen(name)))) {
111             if (tmp == pre) { // del head
112                 g_proc_head = tmp->next;
113             } else {
114                 pre->next = tmp->next;
115             }
116             osal_kfree(0, tmp);
117             tmp = HI_NULL;
118             return HI_SUCCESS;
119         }
120         pre = tmp;
121         tmp = tmp->next;
122     }
123     return HI_FAILURE;
124 }
125 
del_listnull126 static hi_void del_list(hi_void)
127 {
128     hi_entry_list *tmp = g_proc_head;
129     hi_entry_list *tmp2 = HI_NULL;
130 
131     while (tmp != HI_NULL) {
132         tmp2 = tmp->next;
133         osal_kfree(0, tmp);
134         tmp = tmp2;
135     }
136     g_proc_head = HI_NULL;
137 }
138 
hiproc_open(hi_void *private_data)139 static hi_s32 hiproc_open(hi_void *private_data)
140 {
141     hiproc_dbg("Enter hiproc_open\n");
142     osal_mutex_init(&g_mutex);
143     init_waitqueue_head(&g_proc_k_para.wq_for_read);
144     init_waitqueue_head(&g_proc_k_para.wq_for_write);
145     init_waitqueue_head(&g_proc_k_para.wq_for_cmd);
146     return HI_SUCCESS;
147 }
148 
hiproc_release(hi_void *private_data)149 static hi_s32 hiproc_release(hi_void *private_data)
150 {
151     hiproc_dbg("Enter hiproc_release\n");
152     get_list_and_delete_node((hi_u32)(uintptr_t)private_data);
153     return HI_SUCCESS;
154 }
155 
check_validate_name(char const *name)156 static int check_validate_name(char const *name)
157 {
158     unsigned int index;
159 
160     for (index = 0; (index < MAX_PROC_NAME_LEN - 1) && (*(name + index) != '\0'); index++) {
161         if ((*(name + index) >= 'a' && *(name + index) <= 'z') ||
162             (*(name + index) >= '0' && *(name + index) <= '9') || // number 0~9
163             *(name + index) == '_') {
164             continue;
165         } else {
166             return HI_FAILURE;
167         }
168     }
169     return HI_SUCCESS;
170 }
171 
hiproc_create_proc_entry(unsigned int cmd, hi_void *para, hi_void *private_data)172 static hi_s32 hiproc_create_proc_entry(unsigned int cmd, hi_void *para, hi_void *private_data)
173 {
174     hi_proc_name *proc_name = NULL;
175     osal_proc_entry *entry_info = HI_NULL;
176     proc_name = (hi_proc_name *)((uintptr_t)para);
177     proc_name->name[sizeof(proc_name->name) - 1] = '\0';
178     if (check_validate_name(proc_name->name) != HI_SUCCESS) {
179         osal_printk("[%s,line:%d]Error: invalied name.\n", HIPROC_PFX, __LINE__);
180         return HI_FAILURE;
181     }
182     entry_info = osal_proc_add(proc_name->name, strlen(proc_name->name));
183     if (entry_info == HI_NULL) {
184         osal_printk("[%s,line:%d]Error: can't create proc entry\n", HIPROC_PFX, __LINE__);
185         return HI_FAILURE;
186     }
187     add_list(entry_info->name, (hi_u32)(uintptr_t)private_data);
188     entry_info->read = proc_read;
189     entry_info->write = proc_write;
190     return HI_SUCCESS;
191 }
192 
hiproc_remove_proc_entry(unsigned int cmd, hi_void *para, hi_void *private_data)193 static hi_s32 hiproc_remove_proc_entry(unsigned int cmd, hi_void *para, hi_void *private_data)
194 {
195     hi_proc_name *proc_name = NULL;
196     hi_s32 ret;
197     proc_name = (hi_proc_name *)((uintptr_t)para);
198     proc_name->name[sizeof(proc_name->name) - 1] = '\0';
199     if (check_validate_name(proc_name->name) != HI_SUCCESS) {
200         osal_printk("[%s,line:%d]Error: invalied name.\n", HIPROC_PFX, __LINE__);
201         return HI_FAILURE;
202     }
203     osal_proc_remove(proc_name->name, strlen(proc_name->name));
204     ret = del_list_node((hi_u32)(uintptr_t)private_data, proc_name->name);
205     if (ret != 0) {
206         osal_printk("hiproc delete entry failed\n");
207         return HI_FAILURE;
208     }
209     return HI_SUCCESS;
210 }
211 
hiproc_get_cmd(unsigned int cmd, hi_void *para, hi_void *private_data)212 static hi_s32 hiproc_get_cmd(unsigned int cmd, hi_void *para, hi_void *private_data)
213 {
214     hi_proc_para *user_proc_info = HI_NULL;
215     wait_event_interruptible(g_proc_k_para.wq_for_cmd, g_cmd_condition != 0);
216     if (g_cmd_exit_condition == 1) {
217         g_cmd_condition = 0;
218         g_cmd_exit_condition = 0;
219         return HI_FAILURE;
220     }
221     g_cmd_condition = 0;
222 
223     user_proc_info = (hi_proc_para *)((uintptr_t)para);
224     if (((strlen(proc_wait.cmd) > 0) && (strlen(proc_wait.entry.name) > 0))) {
225         (void)memcpy_s(&(user_proc_info->cmd), sizeof(user_proc_info->cmd), proc_wait.cmd,
226             sizeof(proc_wait.cmd));
227         (void)memcpy_s(&(user_proc_info->entry), sizeof(osal_proc_entry), &(proc_wait.entry),
228             sizeof(osal_proc_entry));
229         (void)memcpy_s(&(user_proc_info->write_buf.buf), sizeof(user_proc_info->write_buf.buf),
230             proc_wait.write_buf.buf, sizeof(user_proc_info->write_buf.buf));
231         user_proc_info->write_buf.ppos = proc_wait.write_buf.ppos;
232         user_proc_info->write_buf.count = proc_wait.write_buf.count;
233     } else {
234         return HI_FAILURE;
235     }
236     return HI_SUCCESS;
237 }
238 
hiproc_wake_read_task(unsigned int cmd, hi_void *para, hi_void *private_data)239 static hi_s32 hiproc_wake_read_task(unsigned int cmd, hi_void *para, hi_void *private_data)
240 {
241     hi_proc_show_buf *show_buf = HI_NULL;
242     show_buf = (hi_proc_show_buf *)((uintptr_t)para);
243     if (strlen(proc_wait.entry.name) <= 0) {
244         osal_printk("entry info is HI_NULL\n");
245         return HI_FAILURE;
246     }
247     if (proc_wait.entry.read != HI_NULL) {
248         osal_printk("entry read is not NULL\n");
249         return HI_FAILURE;
250     }
251     if (show_buf == HI_NULL) {
252         osal_printk("show_buf is HI_NULL\n");
253         return HI_FAILURE;
254     }
255     if (show_buf->size > HI_PROC_BUF_SIZE) {
256         osal_printk("show_buf (size = %u) is overflow\n", show_buf->size);
257         return HI_FAILURE;
258     }
259     if (show_buf->buf == HI_NULL) {
260         osal_printk("show_buf is NULL\n");
261         return HI_FAILURE;
262     }
263     proc_wait.entry.read = osal_kmalloc(0, show_buf->size, OSAL_GFP_KERNEL);
264     if (proc_wait.entry.read) {
265         if (osal_copy_from_user(proc_wait.entry.read, (hi_void __user *)show_buf->buf, show_buf->size)) {
266             osal_kfree(0, proc_wait.entry.read);
267             proc_wait.entry.read = HI_NULL;
268         }
269     }
270     g_read_condition = 1;
271     wake_up_interruptible(&(g_proc_k_para.wq_for_read));
272     return HI_SUCCESS;
273 }
274 
hiproc_wake_write_task(unsigned int cmd, hi_void *para, hi_void *private_data)275 static hi_s32 hiproc_wake_write_task(unsigned int cmd, hi_void *para, hi_void *private_data)
276 {
277     g_write_condition = 1;
278     wake_up_interruptible(&(g_proc_k_para.wq_for_write));
279     return HI_SUCCESS;
280 }
281 
hiproc_wake_get_cmd(unsigned int cmd, hi_void *para, hi_void *private_data)282 static hi_s32 hiproc_wake_get_cmd(unsigned int cmd, hi_void *para, hi_void *private_data)
283 {
284     g_cmd_condition = 1;
285     g_cmd_exit_condition = 1;
286     wake_up_interruptible(&(g_proc_k_para.wq_for_cmd));
287     return HI_SUCCESS;
288 }
289 
290 static osal_ioctl_cmd g_hiproc_cmd[] = {
291     {USER_CREATE_PROC_ENTRY, hiproc_create_proc_entry},
292     {USER_REMOVE_PROC_ENTRY, hiproc_remove_proc_entry},
293     {USER_PROC_GET_CMD, hiproc_get_cmd},
294     {USER_PROC_WAKE_READ_TASK, hiproc_wake_read_task},
295     {USER_PROC_WAKE_WRITE_TASK, hiproc_wake_write_task},
296     {USER_PROC_WAKE_GET_CMD, hiproc_wake_get_cmd},
297 };
298 
299 static osal_fileops g_hiproc_fops = {
300     .open = hiproc_open,
301     .release = hiproc_release,
302     .cmd_list = g_hiproc_cmd,
303     .cmd_cnt = sizeof(g_hiproc_cmd) / sizeof(g_hiproc_cmd[0]),
304 };
305 
306 static osal_dev g_hiproc_dev = {
307     .name = HIPROC_DEVICE_NAME,
308     .minor = MISC_DYNAMIC_MINOR,
309     .fops = &g_hiproc_fops,
310 };
311 
hiproc_initnull312 static hi_s32 hiproc_init(hi_void)
313 {
314     if (osal_dev_register(&g_hiproc_dev) != 0) {
315         osal_printk("[%s,line:%d]Error: can't register\n", HIPROC_PFX, __LINE__);
316         return HI_FAILURE;
317     }
318 
319     hiproc_dbg("hi_proc init ok. ver=%s, %s.\n", __DATE__, __TIME__);
320     return HI_SUCCESS;
321 }
322 
proc_read(void *seqfile, void *private)323 static hi_s32 proc_read(void *seqfile, void *private)
324 {
325     struct seq_file *s = seqfile;
326     osal_proc_entry *entry_info = s->private;
327     DEFINE_WAIT(wait);
328 
329     osal_mutex_lock(&g_mutex);
330     /* only these two parameters are used */
331     (void)memcpy_s(&(proc_wait.entry.name), sizeof(proc_wait.entry.name), entry_info->name,
332         sizeof(proc_wait.entry.name));
333     (void)memcpy_s(&(proc_wait.cmd), sizeof(proc_wait.cmd), HI_USER_PROC_READ_CMD, strlen(HI_USER_PROC_READ_CMD) + 1);
334 
335     g_cmd_condition = 1;
336     wake_up_interruptible(&(g_proc_k_para.wq_for_cmd));
337     wait_event_interruptible(g_proc_k_para.wq_for_read, g_read_condition != 0);
338     g_read_condition = 0;
339 
340     if (proc_wait.entry.read != HI_NULL) {
341         osal_proc_print(seqfile, "%s", (hi_char *)proc_wait.entry.read);
342         osal_kfree(0, proc_wait.entry.read);
343         proc_wait.entry.read = HI_NULL;
344     }
345     (void)memset_s(&(proc_wait.entry), sizeof(osal_proc_entry), 0, sizeof(osal_proc_entry));
346     (void)memset_s(&(proc_wait.cmd), sizeof(proc_wait.cmd), 0, sizeof(proc_wait.cmd));
347     osal_mutex_unlock(&g_mutex);
348     return HI_SUCCESS;
349 }
350 
proc_write(osal_proc_entry *entry, const hi_char *buf, hi_s32 count, hi_s64 *ppos)351 static hi_s32 proc_write(osal_proc_entry *entry, const hi_char *buf, hi_s32 count, hi_s64 *ppos)
352 {
353     DEFINE_WAIT(wait);
354     osal_mutex_lock(&g_mutex);
355     (void)memcpy_s(&(proc_wait.cmd), sizeof(proc_wait.cmd), HI_USER_PROC_WRITE_CMD, strlen(HI_USER_PROC_WRITE_CMD) + 1);
356     (void)memcpy_s(&(proc_wait.entry), sizeof(osal_proc_entry), entry, sizeof(osal_proc_entry));
357     osal_copy_from_user(proc_wait.write_buf.buf, buf, sizeof(proc_wait.write_buf.buf));
358     proc_wait.write_buf.ppos = *ppos;
359     proc_wait.write_buf.count = count;
360 
361     g_cmd_condition = 1;
362     wake_up_interruptible(&(g_proc_k_para.wq_for_cmd));
363     wait_event_interruptible(g_proc_k_para.wq_for_write, g_write_condition != 0);
364     g_write_condition = 0;
365 
366     (void)memset_s(&(proc_wait.entry), sizeof(osal_proc_entry), 0, sizeof(osal_proc_entry));
367     (void)memset_s(&(proc_wait.cmd), sizeof(proc_wait.cmd), 0, sizeof(proc_wait.cmd));
368     (void)memset_s(proc_wait.write_buf.buf, sizeof(proc_wait.write_buf.buf), 0, sizeof(proc_wait.write_buf.buf));
369     proc_wait.write_buf.ppos = 0;
370     proc_wait.write_buf.count = 0;
371     osal_mutex_unlock(&g_mutex);
372     return count;
373 }
374 
drv_common_module_initnull375 static hi_s32 drv_common_module_init(hi_void)
376 {
377     hi_s32 ret;
378 
379     hiproc_dbg("Enter drv_common_module_init\n");
380     ret = hiproc_init();
381     if (ret != 0) {
382         osal_printk("%s - drv_common_module_init error!\n", __FUNCTION__);
383     }
384     return ret;
385 }
386 
drv_common_module_exitnull387 static hi_void drv_common_module_exit(hi_void)
388 {
389     hiproc_dbg("Enter drv_common_module_exit\n");
390     osal_mutex_destory(&g_mutex);
391     osal_dev_unregister(&g_hiproc_dev);
392     del_list();
393     return;
394 }
395 
396 module_init(drv_common_module_init);
397 module_exit(drv_common_module_exit);
398