1 /*
2 * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3 */
4
5 #include <stddef.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <stdlib.h>
9
10 #if AOS_COMP_CLI
11 #include "aos/cli.h"
12 #endif
13 #include "aos/list.h"
14
15 #include <drivers/char/u_device.h>
16 #include <drivers/char/u_driver.h>
17 #include <drivers/char/u_bus.h>
18 #include <drivers/spinlock.h>
19
20 #define G_ROOT_PATH "/dev"
21
22 int g_ddkc_log_level = DDKC_LOG_WARN;
23 static dlist_t g_dev_node_list;
24 static u_dev_node_t *g_root_dev_node = NULL;
25
26 char ddkc_level_tag[] = {
27 'L',
28 'D',
29 'I',
30 'W',
31 'E'
32 };
33
34 /**
35 * dump device's information, name, id, parent, bus, driver, drv_data included
36 * @param dev - pointer to target device
37 * @return 0 for success; netative for failure
38 */
u_device_dump(struct u_device * dev)39 int u_device_dump(struct u_device *dev) {
40
41 if (!dev) {
42 ddkc_err("dev is NULL\r\n");
43 return -1;
44 }
45
46 ddkc_info("dev:%p, dev->name:%s, id:%d, dev_id:%d, parent:%p, bus:%p, drv:%p, drv_data:%p\r\n",
47 dev, dev->name,
48 dev->id, dev->dev_id,
49 dev->parent, dev->bus, dev->drv, dev->driver_data);
50
51 return 0;
52 }
53
54 /**
55 * initialize u_device's private info
56 *
57 * @param dev - pointer to target device
58 * @return 0 for success; netative for failure
59 */
u_device_initialize(struct u_device * dev)60 int u_device_initialize(struct u_device *dev) {
61 u_device_private_t *p = NULL;
62
63 /* dev is not NULL, guaranteed by caller */
64 dev->drv = NULL;
65
66 /* device's parent and bus should be setup before call u_device_initialize */
67 //dev->parent = NULL;
68 //dev->bus = NULL;
69 p = (u_device_private_t *)malloc(sizeof(u_device_private_t));
70 if (!p) {
71 ddkc_err("malloc for u_device_private_t failed\r\n");
72 return -ENOMEM;
73 }
74
75 p->name = NULL;
76 spin_lock_init(&p->lock);
77 dlist_init(&p->bus_node);
78 dlist_init(&p->drv_node);
79 dlist_init(&p->child_head);
80 dlist_init(&p->parent_node);
81 p->dev = dev;
82 dev->p = p;
83
84 return 0;
85 }
86
87 /**
88 * add device into bus device list
89 * conditions to check in u_device_add:
90 * 1. dev is not NULL
91 * 2. dev->bus is assigned correctly
92 * 3. dev->p is allocated and initialized
93 *
94 * @param dev - pointer to target device
95 * @return 0 for success; netative for failure
96 */
u_device_add(struct u_device * dev)97 int u_device_add(struct u_device *dev) {
98 u_device_private_t *p = NULL;
99 int r = 0;
100 int len = 0;
101 /* dev is not NULL, guaranteed by caller */
102
103 if (!dev || !dev->p || !dev->bus) {
104 ddkc_err("invalid dev:%p or dev->p:%p or dev->bus:%p\r\n", dev, dev ? dev->p : NULL, dev ? dev->bus : NULL);
105 return -EINVAL;
106 }
107
108 if (!dev->bus->name || (dev->id >= MAX_DEV_ID_NUM)){
109 ddkc_err("invalid bus name:%p or dev->id:%d\r\n", dev->bus->name, dev->id);
110 return -EINVAL;
111 }
112
113 p = dev->p;
114 len = strlen(dev->bus->name) + MAX_DEV_ID_DIGS + 1; // 1 is for '\0'
115 p->name = (char *)malloc(len);
116 if (!p->name) {
117 ddkc_err("malloc for dev->p->name failed, len:%d\r\n", len);
118 return -ENOMEM;
119 }
120
121 /* name dev's name with <bus_name><dev_id> */
122 memset(p->name, 0, len);
123 snprintf(p->name, len, "%s%d", dev->bus->name, dev->id);
124
125 /* add device into its parent's child_head */
126 if (dev->parent) {
127 unsigned long flags;
128
129 spin_lock_irqsave(&dev->parent->p->lock, flags);
130 dlist_add_tail(&p->parent_node, &dev->parent->p->child_head);
131 spin_unlock_irqrestore(&dev->parent->p->lock, flags);
132 }
133
134 /* add this device into target bus */
135 r = u_bus_add_device(dev);
136 if (r)
137 goto dev_add_error;
138
139 /* it is not necessary to check u_bus_try_init_device's return value */
140 u_bus_try_init_device(dev);
141
142 return 0;
143
144 dev_add_error:
145 if (p->name) {
146 free(p->name);
147 p->name = NULL;
148 }
149
150 return r;
151 }
152
153 /**
154 * register a device into system
155 *
156 * @param dev - pointer to target device
157 * @return 0 for success; netative for failure
158 */
u_device_register(struct u_device * dev)159 int u_device_register(struct u_device *dev) {
160
161 if (!dev) {
162 ddkc_err("invalid dev:%p\r\n", dev);
163 return -EINVAL;
164 }
165
166 u_device_initialize(dev);
167 return u_device_add(dev);
168 }
169
170 /**
171 * check whether a device is registered or not
172 * @param dev - pointer to the device to be checked
173 *
174 * @return 0 for success; netative for failure
175 */
u_device_is_registered(struct u_device * dev)176 int u_device_is_registered(struct u_device *dev) {
177 u_device_private_t *devp = dev->p;
178
179 if (!devp || dlist_empty(&devp->bus_node))
180 return 0;
181 return 1;
182 }
183
u_dev_node_get_pathname_length(u_dev_node_t * dev_node)184 unsigned int u_dev_node_get_pathname_length(u_dev_node_t *dev_node) {
185 unsigned int length = 0;
186 #if 0
187 u_dev_node_t *parent = dev_node;
188
189 do {
190 if(!strlen(parent->name))
191 break;
192 length += strlen(parent->name) + 1;
193
194 parent = dev_node->parent;
195 } while (parent);
196 #else
197 /* 2 is for '/' and '\0' */
198 length = strlen(dev_node->name) + strlen(dev_node->parent->path_name) + 2;
199 #endif
200 return length;
201 }
202
u_dev_node_get_pathname(u_dev_node_t * dev_node)203 char *u_dev_node_get_pathname(u_dev_node_t *dev_node) {
204 char *pathname = NULL;
205 unsigned int len = 0;
206 unsigned int length = u_dev_node_get_pathname_length(dev_node);
207
208 pathname = malloc(length);
209 if (!pathname) {
210 ddkc_err("malloc for pathname failed, len[%d]\r\n", length);
211 return NULL;
212 }
213 memset(pathname, 0, length);
214 #if 0
215 u_dev_node_t *parent = dev_node;
216
217 length -= 2;
218
219 do {
220 len = strlen(parent->name);
221 if(!len)
222 break;
223 strncpy(pathname[length - len], parent->name, len);
224 length -= len;
225 pathname[length--] = '/';
226 parent = dev_node->parent;
227 } while (parent);
228 #else
229 ddkc_dbg("pathname length:%d\r\n", length);
230 ddkc_dbg("parent pathname:%s\r\n", dev_node->parent->path_name);
231 ddkc_dbg("node pathname:%p - %s\r\n", dev_node->name, dev_node->name);
232 len = strlen(dev_node->parent->path_name);
233 if (len) {
234 strncpy(pathname, dev_node->parent->path_name, len);
235 pathname[len++] = '/';
236 }
237 strncpy(pathname + len, dev_node->name, strlen(dev_node->name));
238 #endif
239 ddkc_info("pathname:%s\r\n", pathname);
240
241 return pathname;
242 }
243
u_device_node_create(struct u_dev_node * parent,unsigned int dev_id,void * drv_data,char * name)244 struct u_dev_node * u_device_node_create(struct u_dev_node *parent, unsigned int dev_id, void *drv_data, char *name) {
245 u_dev_node_t *dev_node = NULL;
246
247 if (!name) {
248 ddkc_err("name should not be NULL\r\n");
249 return NULL;
250 }
251
252 /**
253 * //TODO: need to do sanity check
254 * 1. whether dev_id is already binded with exist device node?
255 * 2. whether name is already binded to parent?
256 */
257
258 // manage all device, registered with u_device_node_create, into one list,
259 // <pathname, dev_id> is in this list
260 dev_node = malloc(sizeof(u_dev_node_t) + strlen(name) + 1);
261 memset(dev_node, 0, sizeof(u_dev_node_t) + strlen(name) + 1);
262 dlist_init(&dev_node->node);
263
264 dev_node->dev_id = dev_id;
265 if (parent)
266 dev_node->parent = parent;
267 else
268 dev_node->parent = g_root_dev_node;
269
270 if (drv_data)
271 dev_node->drv_data = drv_data;
272
273 strncpy(dev_node->name, name, strlen(name));
274 dev_node->name[strlen(name)] = '\0';
275
276 // compose device pathname
277 dev_node->path_name = u_dev_node_get_pathname(dev_node);
278
279 ddkc_info("path_name:%s added\r\n", dev_node->path_name);
280
281 // enqueue to global device node
282 dlist_add(&dev_node->node, &g_dev_node_list);
283
284 /* TODO: send IPC message to notify VFS new device node is created */
285
286 return dev_node;
287 }
288
u_device_node_delete(unsigned int dev_id)289 int u_device_node_delete(unsigned int dev_id) {
290 u_dev_node_t *dev_node = NULL;
291 struct dlist_s *node = NULL;
292
293 dlist_for_each_entry_safe(&g_dev_node_list, node, dev_node, u_dev_node_t, node) {
294 if (dev_node->dev_id == dev_id) {
295 dlist_del(&dev_node->node);
296 // free full path name memory
297 if (dev_node->path_name)
298 free(dev_node->path_name);
299 // free device node struct
300 free(dev_node);
301 }
302 }
303 return 0;
304 }
305
306 #if 0
307 struct u_dev_node * u_device_node_find_by_devid(dev_t dev_id) {
308 struct u_dev_node *parent;
309 u_dev_node_t *dev_node = NULL;
310 struct dlist_s *node = NULL;
311
312 dlist_for_each_entry_safe(&g_dev_node_list, node, dev_node, u_dev_node_t, node) {
313 if (dev_node->dev_id == dev_id) {
314 return dev_node;
315 }
316 }
317 return NULL;
318 }
319 #endif
320
u_device_node_find_by_pathname(char * path_name)321 struct u_dev_node * u_device_node_find_by_pathname(char *path_name) {
322 dlist_t *root = NULL;
323 u_dev_node_t *dev_node = NULL;
324
325 if (!path_name || !g_root_dev_node) {
326 ddkc_warn("invalid pathname[%p] or root dev node[%p]\r\n", path_name, g_root_dev_node);
327 return NULL;
328 }
329 root = &g_root_dev_node->node;
330 dlist_for_each_entry(root, dev_node, u_dev_node_t, node) {
331 if (!strcmp(path_name, dev_node->path_name)) {
332 ddkc_dbg("dev_node with pathname[%s] found\r\n", dev_node->path_name);
333 return dev_node;
334 }
335 }
336 ddkc_dbg("dev_node with pathname[%s] not found\r\n", path_name);
337 return NULL;
338 }
339
u_device_node_find_by_nodename(char * node_name)340 struct u_dev_node * u_device_node_find_by_nodename(char *node_name) {
341 dlist_t *root = NULL;
342 u_dev_node_t *dev_node = NULL;
343
344 if (!node_name || !g_root_dev_node) {
345 ddkc_warn("invalid node_name[%p] or root dev node[%p]\r\n", node_name, g_root_dev_node);
346 return NULL;
347 }
348 root = &g_root_dev_node->node;
349 dlist_for_each_entry(root, dev_node, u_dev_node_t, node) {
350 // exclude "/dev/" from dev_node->path_name
351 if (!strcmp(node_name, dev_node->path_name + sizeof(G_ROOT_PATH) + 1)) {
352 ddkc_dbg("dev_node with node_name[%s] found\r\n", dev_node->path_name);
353 return dev_node;
354 }
355 }
356 ddkc_dbg("dev_node with node_name[%s] not found\r\n", node_name);
357 return NULL;
358 }
359
u_device_node_find_by_dev(struct u_device * u_dev)360 struct u_dev_node * u_device_node_find_by_dev(struct u_device *u_dev) {
361 dlist_t *root = NULL;
362 u_dev_node_t *dev_node = NULL;
363
364 if (!u_dev || !g_root_dev_node) {
365 ddkc_warn("invalid dev[%p] or root dev node[%p]\r\n", u_dev, g_root_dev_node);
366 return NULL;
367 }
368
369 root = &g_dev_node_list;
370 dlist_for_each_entry(root, dev_node, u_dev_node_t, node) {
371 if (u_dev == dev_node->dev) {
372 ddkc_dbg("dev_node with u_dev[%p] found\r\n", u_dev);
373 return dev_node;
374 }
375 }
376 ddkc_dbg("dev_node with u_dev[%p] not found\r\n", u_dev);
377 return NULL;
378 }
379
u_device_root_node_init(void)380 int u_device_root_node_init(void) {
381 char *root_path = G_ROOT_PATH;
382 unsigned int len = sizeof(u_dev_node_t) + strlen(root_path) + 1;
383
384 dlist_init(&g_dev_node_list);
385
386 g_root_dev_node = malloc(len);
387 if (!g_root_dev_node) {
388 ddkc_err("fail to malloc for root device node\r\n");
389 return -ENOMEM;
390 }
391
392 memset(g_root_dev_node, 0, len);
393 dlist_init(&g_root_dev_node->node);
394
395 strncpy(g_root_dev_node->name, root_path, strlen(root_path) + 1);
396 g_root_dev_node->name[strlen(root_path)] = '\0';
397
398 g_root_dev_node->path_name = g_root_dev_node->name;
399
400 dlist_add(&g_root_dev_node->node, &g_dev_node_list);
401
402 ddkc_dbg("root device node initialized\r\n");
403
404 return 0;
405 }
406
407 /**
408 * delete device from system
409 *
410 * @param dev - pointer to device to be deleted
411 * @return 0 for success; negative for failure
412 */
u_device_del(struct u_device * dev)413 int u_device_del(struct u_device *dev) {
414 if (!dev)
415 return -EINVAL;
416
417 if (dev->p) {
418 unsigned long flags;
419 u_driver_t *drv = NULL;
420 u_device_private_t *p = dev->p;
421
422 //dlist_del(p->bus_node); /* already done in u_bus_del_device */
423 drv = dev->drv;
424 if (drv && drv->p) {
425 // remove drv_node from driver's device list
426 u_driver_private_t *drv_p = drv->p;
427
428 spin_lock_irqsave(&drv_p->lock, flags);
429 dlist_del(&p->drv_node);
430 spin_unlock_irqrestore(&drv_p->lock, flags);
431 }
432
433 spin_lock_irqsave(&dev->parent->p->lock, flags);
434 dlist_del(&p->parent_node);
435 // TODO: should detach all child in child_head
436 //dlist_del(p->child_head);
437 spin_unlock_irqrestore(&dev->parent->p->lock, flags);
438
439 if (p->name) {
440 free(p->name);
441 p->name = NULL;
442 }
443
444 free(p);
445 dev->p = NULL;
446 }
447
448 return 0;
449 }
450
451 /**
452 * unregister device from system
453 * @param dev - pointer to target device to be unregistered
454 * @return 0 for success; negative for failure
455 */
u_device_unregister(struct u_device * dev)456 int u_device_unregister(struct u_device *dev) {
457 return u_device_del(dev);
458 }
459
460 #if AOS_COMP_CLI
461 /**
462 * service handler - change log level of udevice
463 *
464 */
u_device_loglevel_cmd(char * wbuf,int len,int argc,char ** argv)465 static void u_device_loglevel_cmd(char *wbuf, int len, int argc, char **argv) {
466
467 if (argc < 1) {
468 ddkc_err("arg too short, current loglevel:%d\r\n", g_ddkc_log_level);
469 return;
470 }
471
472 switch (*argv[1]) {
473 case 'l':
474 case 'L':
475 g_ddkc_log_level = DDKC_LOG_LOUD;
476 break;
477 case 'd':
478 case 'D':
479 g_ddkc_log_level = DDKC_LOG_DEBUG;
480 break;
481 case 'i':
482 case 'I':
483 g_ddkc_log_level = DDKC_LOG_INFO;
484 break;
485 case 'w':
486 case 'W':
487 g_ddkc_log_level = DDKC_LOG_WARN;
488 break;
489 case 'e':
490 case 'E':
491 g_ddkc_log_level = DDKC_LOG_ERROR;
492 break;
493 default:
494 break;
495 }
496 ddkc_err("loglevel set to %d\r\n", g_ddkc_log_level);
497
498 return;
499 }
500
501 struct cli_command u_device_test_cmds[] = {
502 {"udev_loglevel", "adjust udevice log level", u_device_loglevel_cmd},
503 };
504
u_devicd_test_cmd_init(void)505 static int u_devicd_test_cmd_init(void) {
506 ddkc_dbg("registering udevice commands\r\n");
507 return aos_cli_register_commands(&u_device_test_cmds[0],
508 sizeof(u_device_test_cmds)/sizeof(u_device_test_cmds[0]));
509 }
510 #endif /* AOS_COMP_CLI */
511
u_device_init(void)512 int u_device_init(void) {
513 #if AOS_COMP_CLI
514 u_devicd_test_cmd_init();
515 #endif
516 return u_device_root_node_init();
517 }
518
519 #include "drivers/u_ld.h"
520 CORE_DRIVER_ENTRY(u_device_init)
521