1 /*
2 * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3 */
4
5 #include <stddef.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <errno.h>
9
10 #if AOS_COMP_CLI
11 #include "aos/cli.h"
12 #endif
13 #include "aos/list.h"
14
15 #include <drivers/u_ld.h>
16 #include <drivers/spinlock.h>
17 #include <drivers/char/u_device.h>
18 #include <drivers/char/u_driver.h>
19 #include <drivers/char/u_bus.h>
20
21 static int u_bus_dump(struct u_bus *bus);
22 static int u_device_unbind_driver(struct u_device *dev);
23 static int u_bus_detach_device(struct u_device *dev);
24
25 /**
26 * all buses is connected via g_bus_list_head,
27 * used to enumerate bus/device/driver,
28 * either for info dump or for power management function
29 *
30 */
31 static dlist_t g_bus_list_head;
32 static spinlock_t g_bus_list_lock;
33
34 /**
35 * dump all bus informations
36 *
37 * @return always returns 0
38 */
u_bus_dump_all(void)39 int u_bus_dump_all(void) {
40 u_bus_t *bus = NULL;
41 dlist_t *next = NULL;
42 u_bus_private_t *p = NULL;
43
44 dlist_for_each_entry_safe(&g_bus_list_head, next, p, u_bus_private_t, bus_node) {
45 ddkc_info("p:%p, bus_node:%p, next:%p, prev:%p\r\n", p, &p->bus_node, p->bus_node.next, p->bus_node.prev);
46 bus = p->bus;
47 u_bus_dump(bus);
48 }
49
50 return 0;
51 }
52
53 /**
54 * dump single bus's information, all devices, drivers connected to this bus is included
55 *
56 * @param bus - pointer to specified bus
57 * @return
58 */
u_bus_dump(struct u_bus * bus)59 static int u_bus_dump(struct u_bus *bus) {
60 u_device_t *dev = NULL;
61 u_driver_t *drv = NULL;
62 struct u_bus_private *p = NULL;
63 u_device_private_t *dev_p = NULL;
64 u_driver_private_t *drv_p = NULL;
65
66 struct dlist_s *next = NULL;
67
68 if (!bus)
69 return -EINVAL;
70
71 ddkc_info("bus:%p, name:%s, p:%p\r\n", bus, bus->name, bus->p);
72
73 p = bus->p;
74
75 /* dump information of all devices, who is connected into current bus */
76 if (dlist_empty(&p->dev_list)) {
77 ddkc_warn("bus[%s]'s device list is empty\r\n", bus->name);
78 } else {
79 dlist_for_each_entry_safe(&p->dev_list, next, dev_p, u_device_private_t, bus_node) {
80 dev = dev_p->dev;
81 u_device_dump(dev);
82 }
83 }
84
85 /* dump information of all drivers, who is connected into current bus */
86 if (dlist_empty(&p->drv_list)) {
87 ddkc_warn("bus[%s]'s driver list is empty\r\n", bus->name);
88 } else {
89 dlist_for_each_entry_safe(&p->drv_list, next, drv_p, u_driver_private_t, bus_node) {
90 drv = drv_p->drv;
91 u_driver_dump(drv);
92 }
93 }
94
95 return 0;
96 }
97
98 /**
99 * connect dev into target bus(dev->bus)
100 * low level operations, driver is not aware of this operation
101 *
102 * @param dev - pointer to target device
103 * @return always returns 0
104 */
u_bus_add_device(struct u_device * dev)105 int u_bus_add_device(struct u_device *dev) {
106 unsigned long flags;
107 u_bus_t *bus = dev->bus;
108
109 /* add dev->p->bus_node into dev->bus's p->dev_list */
110 spin_lock_irqsave(&bus->p->lock, flags);
111 dlist_add_tail(&dev->p->bus_node, &bus->p->dev_list);
112 spin_unlock_irqrestore(&bus->p->lock, flags);
113
114 return 0;
115 }
116
117 /**
118 * disconnect device with its driver and delete it from target bus(dev->bus)
119 *
120 * @param dev - pointer to target device
121 * @return always return 0
122 */
u_bus_del_device(struct u_device * dev)123 int u_bus_del_device(struct u_device *dev) {
124 u_bus_t *bus = NULL;
125
126 if (!dev) {
127 ddkc_err("invalid dev:%p to delete\r\n", dev);
128 return -EINVAL;
129 }
130
131 bus = dev->bus;
132
133 /* if device is already attached with specified driver, detach it */
134 if (dev && dev->drv) {
135 u_bus_detach_device(dev);
136 u_device_unbind_driver(dev);
137 }
138
139 /* delete dev from it's bus */
140 if (dev && dev->bus && !dlist_empty(&dev->bus->p->dev_list)) {
141 //TODO: should notify driver about this device is to be deleted?
142 unsigned long flags;
143
144 spin_lock_irqsave(&bus->p->lock, flags);
145 dlist_del(&dev->p->bus_node);
146 dlist_init(&dev->p->bus_node);
147 spin_unlock_irqrestore(&bus->p->lock, flags);
148 }
149
150 return 0;
151 }
152
153 /**
154 * bind device with target driver
155 *
156 * @param drv - pointer to the driver to be binded
157 * @param dev - pointer to the device to be binded
158 * @return always return 0
159 */
u_driver_bind_device(struct u_driver * drv,struct u_device * dev)160 static int u_driver_bind_device(struct u_driver *drv, struct u_device *dev) {
161 unsigned long flags;
162 u_driver_private_t *drv_p = drv->p;
163 u_device_private_t *dev_p = dev->p;
164
165 spin_lock_irqsave(&drv_p->lock, flags);
166 dlist_add_tail(&dev_p->drv_node, &drv_p->dev_list);
167 spin_unlock_irqrestore(&drv_p->lock, flags);
168
169 return 0;
170 }
171
172 /**
173 * unbind device with the binded driver
174 *
175 * @param dev - pointer to target device
176 * @return always returns 0
177 */
u_device_unbind_driver(struct u_device * dev)178 static int u_device_unbind_driver(struct u_device *dev) {
179 unsigned long flags;
180 u_device_private_t *dev_p = dev->p;
181
182 spin_lock_irqsave(&dev_p->lock, flags);
183 dlist_del(&dev_p->drv_node);
184 spin_unlock_irqrestore(&dev_p->lock, flags);
185
186 return 0;
187 }
188
189 /**
190 * Try to init dev with drv's ops
191 *
192 * @param drv - target driver to be used for device initialization
193 * @param dev - target device to be initialized
194 * @return return 1 if init success; otherwise return 0
195 */
u_driver_init_device(struct u_driver * drv,struct u_device * dev)196 static int u_driver_init_device(struct u_driver *drv, struct u_device *dev)
197 {
198 int r = -1;
199
200 /* attach to drv for the moment, will detach if init/probe fails */
201 dev->drv = drv;
202 ddkc_dbg("bus[%s] try to init dev[%s] with drv[%s]\r\n", dev->bus->name, dev->name, drv->name);
203
204 ddkc_dbg("dev->bus->init:%p, drv->init:%p\r\n", dev->bus->init, drv->init);
205 ddkc_dbg("dev->bus->probe:%p, drv->probe:%p\r\n", dev->bus->probe, drv->probe);
206 do {
207 /* try legacy ops, bus init cb is prioritized */
208 if (dev->bus->init) {
209 r = dev->bus->init(dev);
210 break;
211 } else if (drv->init) {
212 r = drv->init(dev);
213 break;
214 }
215
216 /* try probe ops - for linux-like drivers, bus probe cb is prioritized */
217 if (dev->bus->probe) {
218 r = dev->bus->probe(dev);
219 break;
220 } else if (drv->probe) {
221 r = drv->probe(dev);
222 break;
223 }
224 } while(0);
225
226 if (!r) {
227 ddkc_dbg("bus[%s] drv[%s] init dev[%s] success\r\n", dev->bus->name, drv->name, dev->p->name);
228 u_driver_bind_device(drv, dev);
229 } else {
230 ddkc_warn("bus[%s] drv[%s] init dev[%s] fails\r\n", dev->bus->name, drv->name, dev->name);
231 dev->drv = NULL;
232 }
233
234 return r;
235 }
236
237 /**
238 * Try whether drv matches dev with bus match function
239 *
240 * @param drv - target driver to be matched with dev
241 * @param dev - target device to be matched with drv
242 * @return return 1 if match success; otherwise return 0
243 */
u_driver_match_device(struct u_driver * drv,struct u_device * dev)244 static int u_driver_match_device(struct u_driver *drv, struct u_device *dev)
245 {
246 if (!drv || !dev) {
247 ddkc_err("invalid drv:%p or dev:%p\r\n", drv, dev);
248 return 0;
249 }
250
251 ddkc_dbg("driver match device, drv:%p, drv->bus:%p, dev:%p\r\n", drv, drv->bus, dev);
252 if (drv->bus) {
253 ddkc_dbg("driver match device, drv->bus->match:%p\r\n", drv->bus->match);
254 return drv->bus->match ? drv->bus->match(dev, drv) : 1;
255 } else {
256 ddkc_err("invalid drv->bus:%p\r\n", drv->bus);
257 return 0;
258 }
259 }
260
261 /**
262 * enumerate driver list and try to init the device with the drivers
263 *
264 * @param dev - pointer to the deviice to be initialized
265 *
266 * @return 0 if device initialization success; negative if device initialization fails
267 */
u_bus_try_init_device(struct u_device * dev)268 int u_bus_try_init_device(struct u_device *dev) {
269 int r = 0;
270 u_bus_t *bus = dev->bus;
271 u_driver_t *drv = NULL;
272 u_driver_private_t *drv_p = NULL;
273 struct dlist_s *next = NULL;
274
275 if (dev->drv) {
276 ddkc_warn("dev[%s]'s is busy, is now driven by drv[%s]\r\n", dev->name, dev->drv->name);
277 return 0;
278 }
279 // whenever device is created, try match and init it if match returns true
280 if (dlist_empty(&bus->p->drv_list)) {
281 ddkc_dbg("bus[%s]'s drv_list is empty\r\n", bus->name);
282 return 0;
283 }
284
285 dlist_for_each_entry_safe(&bus->p->drv_list, next, drv_p, u_driver_private_t, bus_node) {
286 drv = drv_p->drv;
287
288 r = u_driver_match_device(drv, dev);
289 if (!r) {
290 ddkc_dbg("drv[%s] does not match dev[%s]\r\n", drv->name, dev->name);
291 continue;
292 }
293
294 r = u_driver_init_device(drv, dev);
295 if (r) {
296 ddkc_err("drv[%s] init dev[%s] fails\r\n", drv->name, dev->name);
297 } else {
298 ddkc_info("drv[%s] init dev[%s] success\r\n", drv->name, dev->name);
299 }
300 }
301
302 return 0;
303 }
304
305 /**
306 * add a driver into bus's driver list
307 *
308 * @param drv - the driver to be added
309 * @return always return 0
310 */
u_bus_add_driver(struct u_driver * drv)311 int u_bus_add_driver(struct u_driver *drv) {
312 u_bus_t *bus = drv->bus;
313 unsigned long flags;
314
315 spin_lock_irqsave(&bus->p->lock, flags);
316 dlist_add_tail(&drv->p->bus_node, &bus->p->drv_list);
317 spin_unlock_irqrestore(&bus->p->lock, flags);
318
319 return 0;
320 }
321
322 /**
323 * attach a driver into bus's driver list,
324 * try to init free devices(not drived by any driver) if device is matchd
325 *
326 * @drv: the driver to be added
327 * @return always return 0
328 */
u_bus_attach_driver(struct u_driver * drv)329 int u_bus_attach_driver(struct u_driver *drv) {
330 int r = 0;
331 u_bus_t *bus = drv->bus;
332 u_device_t *dev = NULL;
333 u_device_private_t *dev_p = NULL;
334 dlist_t *next = NULL;
335
336 // whenever device is created, try init it if match returns success
337 if (dlist_empty(&bus->p->dev_list)) {
338 ddkc_dbg("bus[%s]'s dev_list is empty\r\n", bus->name);
339 return 0;
340 }
341
342 /* try to match the device in bus device list with driver */
343
344 dlist_for_each_entry_safe(&bus->p->dev_list, next, dev_p, u_device_private_t, bus_node) {
345 dev = dev_p->dev;
346 /* exclude the devices who is already attached by driver */
347 if (dev->drv) {
348 ddkc_dbg("bus[%s] dev[%s] is already probed by drv[%s]\r\n",
349 bus->name, dev->p->name, dev->drv->name);
350 continue;
351 }
352
353 r = u_driver_match_device(drv, dev);
354 if (!r) {
355 ddkc_dbg("drv[%s] does not match dev[%s]\r\n", drv->name, dev->name);
356 continue;
357 }
358
359 r = u_driver_init_device(drv, dev);
360 ddkc_dbg("drv[%s] init dev[%s] %s, r:%d\r\n", drv->name, dev->name, r ? "fail" : "success", r);
361 }
362
363 return 0;
364 }
365
u_bus_get_by_name(char * name)366 u_bus_t * u_bus_get_by_name(char *name) {
367 dlist_t *next = NULL;
368 u_bus_private_t *p = NULL;
369 u_bus_t *bus = NULL;
370
371 if (!name)
372 return NULL;
373
374
375 dlist_for_each_entry_safe(&g_bus_list_head, next, p, u_bus_private_t, bus_node) {
376 u_bus_dump(p->bus);
377 bus = p->bus;
378 ddkc_dbg("bus->name:%s, name_len:%d\r\n", bus->name, strlen(bus->name));
379 if (!strcmp(bus->name, name)) {
380 ddkc_dbg("name:%s, bus:%p\r\n", name, bus);
381 break;
382 }
383 bus = NULL;
384 }
385
386 return bus;
387 }
388
389 /**
390 * init global bus list, this is module entry API of u_bus
391 * @return always return 0
392 */
u_bus_list_init(void)393 int u_bus_list_init(void) {
394 ddkc_dbg("bus list init\r\n");
395 dlist_init(&g_bus_list_head);
396 spin_lock_init(&g_bus_list_lock);
397 return 0;
398 }
399
400 /**
401 * add new bus into global bus list
402 *
403 * @param bus - pointer to target bus to be added
404 * @return always return 0
405 */
u_bus_add(struct u_bus * bus)406 int u_bus_add(struct u_bus *bus) {
407 unsigned long flags;
408
409 ddkc_dbg("add bus[%s] into global bus list\r\n", bus->name);
410 spin_lock_irqsave(&g_bus_list_lock, flags);
411 dlist_add_tail(&bus->p->bus_node, &g_bus_list_head);
412 spin_unlock_irqrestore(&g_bus_list_lock, flags);
413 return 0;
414 }
415
416 /**
417 * init bus node struct
418 *
419 * @param bus - target bus to be initialized
420 * @return 0 for success; negative for failure
421 */
u_bus_node_initialize(struct u_bus * bus)422 int u_bus_node_initialize(struct u_bus *bus) {
423 u_bus_private_t *p = NULL;
424
425 p = (u_bus_private_t *)malloc(sizeof(u_bus_private_t));
426 if (!p) {
427 ddkc_err("malloc for u_bus_private_t fails\r\n");
428 return -ENOMEM;
429 }
430
431 spin_lock_init(&p->lock);
432 dlist_init(&p->dev_list);
433 dlist_init(&p->drv_list);
434 dlist_init(&p->bus_node);
435 p->bus = bus;
436 bus->p = p;
437
438 return 0;
439 }
440
441 /**
442 * register bus to system
443 *
444 * @param bus - pointer to the target bus to be registered
445 * @return 0 for success; negative for failure
446 */
u_bus_register(struct u_bus * bus)447 int u_bus_register(struct u_bus *bus) {
448 int r = 0;
449
450 if(!bus) {
451 ddkc_err("invalid bus:%p\r\n", bus);
452 return -EINVAL;
453 }
454
455 if (bus->p || !bus->name) {
456 ddkc_err("invalid bus priv data:%p or bus name:%p, ignore\r\n", bus->p, bus->name);
457 return -EINVAL;
458 }
459
460 r = u_bus_node_initialize(bus);
461 if (r) {
462 ddkc_err("bus node init fails, error:%d\r\n", r);
463 return -1;
464 }
465
466 return u_bus_add(bus);
467 }
468
469 /**
470 * unregister bus from system
471 *
472 * @param bus - pointer to the target bus to be unregistered
473 * @return
474 */
u_bus_unregister(struct u_bus * bus)475 int u_bus_unregister(struct u_bus *bus) {
476 unsigned long flags;
477
478 // TODO: all bus related driver/device list should be clean up before calling u_bus_unregister
479 ddkc_info("add bus[%s] into global bus list\r\n", bus->name);
480 spin_lock_irqsave(&g_bus_list_lock, flags);
481 dlist_del(&bus->p->bus_node);
482 spin_unlock_irqrestore(&g_bus_list_lock, flags);
483 return 0;
484 }
485
486 #if 0
487 struct u_device *u_bus_find_dev_by_id(struct u_bus *bus, dev_t devid) {
488 u_device_t *dev = NULL;
489 struct u_driver_t *drv = NULL;
490 struct u_bus_private *p = NULL;
491 u_device_private_t *dev_p = NULL;
492
493 struct dlist_s *next = NULL;
494
495 if (!bus)
496 return -EINVAL;
497 ddkc_info("bus:%p, name:%s, p:%p, devid:0x%x\r\n", bus, bus->name, bus->p, devid);
498
499 p = bus->p;
500
501 if (dlist_empty(&p->dev_list)) {
502 ddkc_dbg("bus[%s]'s device list is empty\r\n", bus->name);
503 }
504 dlist_for_each_entry_safe(&p->dev_list, next, dev_p, u_device_private_t, bus_node) {
505 dev = dev_p->dev;
506 if (dev->dev_id == devid) {
507 ddkc_loud("dev:%p found with devid:0x%x\r\n", dev, devid);
508 break;
509 }
510 ddkc_loud("dev->dev_id:%d\r\n", dev->dev_id);
511 dev = NULL;
512 }
513
514 return dev;
515 }
516
517 struct u_device *u_device_find_by_devid(dev_t devid) {
518 u_bus_t *bus = NULL;
519 dlist_t *pos = NULL;
520 dlist_t *next = NULL;
521 u_bus_private_t *p = NULL;
522 struct u_device *dev = NULL;
523
524 ddkc_dbg ("%s\r\n", __func__);
525 dlist_for_each_entry_safe(&g_bus_list_head, next, p, u_bus_private_t, bus_node) {
526 bus = p->bus;
527 dev = u_bus_find_dev_by_id(bus, devid);
528 if (dev)
529 break;
530 }
531
532 ddkc_err("u_device[%p] found by devid[0x%x]\r\n", dev, devid);
533 return dev;
534 }
535
536 #endif
537
538
539 /**
540 * delete a driver from bus's driver list
541 *
542 * @param drv - pointer to the driver to be deleted
543 * @return always return 0
544 */
u_bus_del_driver(struct u_driver * drv)545 int u_bus_del_driver(struct u_driver *drv) {
546 u_bus_t *bus = NULL;
547 unsigned long flags;
548
549 if (!drv) {
550 ddkc_err("invalid drv:%p to delete\r\n", drv);
551 return -EINVAL;
552 }
553
554 bus = drv->bus;
555 spin_lock_irqsave(&bus->p->lock, flags);
556 dlist_del(&drv->p->bus_node);
557 spin_unlock_irqrestore(&bus->p->lock, flags);
558
559 return 0;
560 }
561
562 /**
563 * Try to deinit a device
564 *
565 * @param drv - pointer to target device's driver
566 * @param dev - pointer to target device
567 * @return 0 if deinit success; otherwise return negative
568 */
u_driver_deinit_device(struct u_driver * drv,struct u_device * dev)569 static int u_driver_deinit_device(struct u_driver *drv, struct u_device *dev)
570 {
571 int r = -1;
572
573 /* attach to drv for the moment, will detach if init fails */
574 ddkc_dbg("bus[%s] drv[%s] removing dev[%s]\r\n", dev->bus->name, drv->name, dev->name);
575
576 ddkc_dbg("dev->bus->deinit:%p, drv->deinit:%p\r\n", dev->bus->deinit, drv->deinit);
577 ddkc_dbg("dev->bus->remove:%p, drv->remove:%p\r\n", dev->bus->remove, drv->remove);
578
579 /* try legacy ops */
580 if (dev->bus->deinit) {
581 r = dev->bus->deinit(dev);
582 if (r)
583 goto remove_fail;
584 } else if (drv->deinit) {
585 r = drv->deinit(dev);
586 if (r)
587 goto remove_fail;
588 }
589
590 if (dev->bus->remove) {
591 r = dev->bus->remove(dev);
592 if (r)
593 goto remove_fail;
594 } else if (drv->remove) {
595 r = drv->remove(dev);
596 if (r)
597 goto remove_fail;
598 }
599
600 dev->drv = NULL;
601 ddkc_dbg("bus[%s] drv[%s] remove dev[%s] success\r\n", dev->bus->name, drv->name, dev->p->name);
602
603 return 0;
604
605 remove_fail:
606 dev->drv = NULL;
607 ddkc_dbg("bus[%s] drv[%s] remove dev[%s] fails\r\n", dev->bus->name, drv->name, dev->name);
608 return -1;
609 }
610
611 /**
612 * deattch a driver into bus's driver list
613 *
614 * @param drv - pointer to target driver to be deatched
615 * @return always return 0
616 */
u_bus_detach_driver(struct u_driver * drv)617 int u_bus_detach_driver(struct u_driver *drv) {
618 int r = 0;
619 u_device_t *dev = NULL;
620 u_device_private_t *dev_p = NULL;
621 dlist_t *next = NULL;
622
623 if (dlist_empty(&drv->p->dev_list)) {
624 ddkc_dbg("drv[%s]'s dev_list is empty\r\n", drv->name);
625 return 0;
626 }
627 ddkc_dbg("drv:%p, drv->p:%p, &drv->p->dev_list:%p\r\n", drv, drv->p, &drv->p->dev_list);
628
629 /* try to remove the devices which is already attached with the driver */
630 dlist_for_each_entry_safe(&drv->p->dev_list, next, dev_p, u_device_private_t, drv_node) {
631 ddkc_dbg("dev:%p, dev_p:%p\r\n", dev, dev_p);
632 dev = dev_p->dev;
633 ddkc_dbg("dev:%p, dev_p:%p\r\n", dev, dev_p);
634 r = u_driver_deinit_device(drv, dev);
635 ddkc_dbg("drv[%s] remove dev[%s] %s, r:%d\r\n", drv->name, dev->name, r ? "fail" : "success", r);
636 }
637
638 return 0;
639 }
640
641 /**
642 * detach a device into bus's driver list
643 *
644 * @param dev - target device to be detached
645 * @return always return 0
646 */
u_bus_detach_device(struct u_device * dev)647 int u_bus_detach_device(struct u_device *dev) {
648 int r = 0;
649 u_driver_t *drv = dev->drv;
650
651 if (!drv) {
652 ddkc_dbg("dev[%s] is not attached to driver\r\n", dev->name);
653 return 0;
654 }
655
656 r = u_driver_deinit_device(drv, dev);
657 if (r) {
658 ddkc_err("dev[%s] deatch with driver[%s] failed\r\n", dev->name, drv->name);
659 return 0;
660 }
661 ddkc_dbg("dev[%s] deatch with driver[%s] succeed\r\n", dev->name, drv->name);
662
663 return 0;
664 }
665
666 #if AOS_COMP_CLI
667 /**
668 * command service handler - dump information of all bus/device/driver
669 *
670 */
u_bus_dump_cmd(char * wbuf,int len,int argc,char ** argv)671 static void u_bus_dump_cmd(char *wbuf, int len, int argc, char **argv) {
672
673 ddkc_info("dumping bus/device/driver information\r\n");
674 u_bus_dump_all();
675 ddkc_info("dump bus/device/driver information done\r\n");
676
677 return;
678 }
679
680 struct cli_command u_bus_test_cmds[] = {
681 {"ubusdump", "dump system's bus/device/driver information", u_bus_dump_cmd},
682 };
683
u_bus_test_cmd_init(void)684 static int u_bus_test_cmd_init(void) {
685 ddkc_dbg("registering udevice commands\r\n");
686 return aos_cli_register_commands(&u_bus_test_cmds[0],
687 sizeof(u_bus_test_cmds)/sizeof(u_bus_test_cmds[0]));
688 }
689 #endif /* AOS_COMP_CLI */
690
691 /**
692 * u_bus module init entry
693 * declared with CORE_DRIVER_ENTRY, which is 1st level driver init entry
694 * @return 0
695 */
u_bus_init(void)696 int u_bus_init(void) {
697 #if AOS_COMP_CLI
698 u_bus_test_cmd_init();
699 #endif
700 return u_bus_list_init();
701 }
702
703 CORE_DRIVER_ENTRY(u_bus_init)
704