技术背景
详细有学习过Linux驱动的小伙伴,都知道相应的总线驱动模型吧。学习驱动都是先从硬编码,到设备驱动模型的历程,这样能让代码尽可能少的改动,即可支持一系列的设备。在较新的Uboot中,已经有成熟的DM模型了,可以像开发Linux驱动一样,很容易的就能完成设备驱动的编写。
本专题也是基于DM_USB和DM_ETH模型,对iTop4412开发板的DM9621 USB to Ethernet芯片进行驱动程序的移植,借鉴了Linux内核里面的driver/net/usb/dm9601.c
和Uboot里面的类似网卡驱动driver/usb/eth/asix.c
,帮助很大!
前言
推荐大家阅读一下这篇比较优秀的博文,比较详细的介绍Uboot的DM模型,了解清楚DM模型,就知道为何我们这样写,就可以进行相应的操作,下面给出链接。
uboot (番外篇)uboot 驱动模型: https://blog.csdn.net/ooonebook/article/details/53234020
DM_USB模型简介
概览
根据Uboot的DM模型,需要有udevice_id
或UBOOT_DEVICE
描述设备信息,U_BOOT_DRIVER
驱动对应设备的实际硬件操作;UCLASS_USB
描述抽象的类信息,和与其对应的UCLASS_DRIVER
来描述对应的通用操作。如果一脸懵逼,请先阅读下前言中的博客链接,才能更好的理解Uboot中的驱动模型。
Host驱动简要分析
UCLASS驱动
源自文件drivers/usb/host/usb-uclass.c
。
792 UCLASS_DRIVER(usb) = {
793 .id = UCLASS_USB,
794 .name = "usb",
795 .flags = DM_UC_FLAG_SEQ_ALIAS,
796 .post_bind = dm_scan_fdt_dev,
797 .priv_auto_alloc_size = sizeof(struct usb_uclass_priv),
798 .per_child_auto_alloc_size = sizeof(struct usb_device),
799 .per_device_auto_alloc_size = sizeof(struct usb_bus_priv),
800 .child_post_bind = usb_child_post_bind,
801 .child_pre_probe = usb_child_pre_probe,
802 .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata),
803 };
804
设备驱动
源自文件drivers/usb/host/ehci-exynos.c
。
247 static const struct udevice_id ehci_usb_ids[] = {
248 { .compatible = "samsung,exynos-ehci" },
249 { }
250 };
251
252 U_BOOT_DRIVER(usb_ehci) = {
253 .name = "ehci_exynos",
254 .id = UCLASS_USB,
255 .of_match = ehci_usb_ids,
256 .ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
257 .probe = ehci_usb_probe,
258 .remove = ehci_usb_remove,
259 .ops = &ehci_usb_ops,
260 .priv_auto_alloc_size = sizeof(struct exynos_ehci),
261 .platdata_auto_alloc_size = sizeof(struct exynos_ehci_platdata),
262 .flags = DM_FLAG_ALLOC_PRIV_DMA,
263 };
设备驱动操作集
源自文件drivers/usb/host/ehci-exynos.c
。
1665 struct dm_usb_ops ehci_usb_ops = {
1666 .control = ehci_submit_control_msg,
1667 .bulk = ehci_submit_bulk_msg,
1668 .interrupt = ehci_submit_int_msg,
1669 .create_int_queue = ehci_create_int_queue,
1670 .poll_int_queue = ehci_poll_int_queue,
1671 .destroy_int_queue = ehci_destroy_int_queue,
1672 .get_max_xfer_size = ehci_get_max_xfer_size,
1673 };
工作流程
- udevice和对应uclass的创建(初始化DM时自动创建)
- udevice和对应uclass的绑定(初始化DM时自动绑定)
- 对应udevice的probe调用 (需要自己实现)
- uclass的接口调用(最终对应udevice driver中ops结构体保存的各种回调)
DM_ETH模型模型简要分析
概览
与DM_USB模型类似,会有UCLASS_ETH
抽象类和对应的UCLASS_DRIVER
,还有U_BOOT_USB_DEVICE
和对应的U_BOOT_DRIVER
。注意DM9621是USB转以太网芯片,所以这里有专门的宏U_BOOT_USB_DEVICE来描述USB设备。这里暂时以其他USB以太网网卡驱动用来分析模型,此处为ASIX AX8817X。
分析
UCLASS驱动
源自文件net/eth-uclass.c
。
544 UCLASS_DRIVER(eth) = {
545 .name = "eth",
546 .id = UCLASS_ETH,
547 .post_bind = eth_post_bind,
548 .pre_unbind = eth_pre_unbind,
549 .post_probe = eth_post_probe,
550 .pre_remove = eth_pre_remove,
551 .priv_auto_alloc_size = sizeof(struct eth_uclass_priv),
552 .per_device_auto_alloc_size = sizeof(struct eth_device_priv),
553 .flags = DM_UC_FLAG_SEQ_ALIAS,
554 };
设备驱动
源自文件drivers/usb/eth/asix.c
。
872 U_BOOT_DRIVER(asix_eth) = {
873 .name = "asix_eth",
874 .id = UCLASS_ETH,
875 .probe = asix_eth_probe,
876 .ops = &asix_eth_ops,
877 .priv_auto_alloc_size = sizeof(struct asix_private),
878 .platdata_auto_alloc_size = sizeof(struct eth_pdata),
879 };
880
881 static const struct usb_device_id asix_eth_id_table[] = {
882 /* Apple USB Ethernet Adapter */
883 { USB_DEVICE(0x05ac, 0x1402), .driver_info = FLAG_TYPE_AX88772 },
884 /* D-Link DUB-E100 H/W Ver B1 */
885 { USB_DEVICE(0x07d1, 0x3c05), .driver_info = FLAG_TYPE_AX88772 },
886 /* D-Link DUB-E100 H/W Ver C1 */
887 { USB_DEVICE(0x2001, 0x1a02), .driver_info = FLAG_TYPE_AX88772 },
888 /* Cables-to-Go USB Ethernet Adapter */
889 { USB_DEVICE(0x0b95, 0x772a), .driver_info = FLAG_TYPE_AX88772 },
890 /* Trendnet TU2-ET100 V3.0R */
891 { USB_DEVICE(0x0b95, 0x7720), .driver_info = FLAG_TYPE_AX88772 },
892 /* SMC */
893 { USB_DEVICE(0x0b95, 0x1720), .driver_info = FLAG_TYPE_AX88172 },
894 /* MSI - ASIX 88772a */
895 { USB_DEVICE(0x0db0, 0xa877), .driver_info = FLAG_TYPE_AX88772 },
896 /* Linksys 200M v2.1 */
897 { USB_DEVICE(0x13b1, 0x0018), .driver_info = FLAG_TYPE_AX88172 },
898 /* 0Q0 cable ethernet */
899 { USB_DEVICE(0x1557, 0x7720), .driver_info = FLAG_TYPE_AX88772 },
900 /* DLink DUB-E100 H/W Ver B1 Alternate */
901 { USB_DEVICE(0x2001, 0x3c05), .driver_info = FLAG_TYPE_AX88772 },
902 /* ASIX 88772B */
903 { USB_DEVICE(0x0b95, 0x772b),
904 .driver_info = FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC },
905 { USB_DEVICE(0x0b95, 0x7e2b), .driver_info = FLAG_TYPE_AX88772B },
906 { } /* Terminating entry */
907 };
设备驱动操作集
源自文件drivers/usb/eth/asix.c
。
863 static const struct eth_ops asix_eth_ops = {
864 .start = asix_eth_start,
865 .send = asix_eth_send,
866 .recv = asix_eth_recv,
867 .free_pkt = asix_free_pkt,
868 .stop = asix_eth_stop,
869 .write_hwaddr = asix_write_hwaddr,
870 };
设备
源自文件drivers/usb/eth/asix.c
。
909 U_BOOT_USB_DEVICE(asix_eth, asix_eth_id_table);
工作流程
- udevice和对应uclass的创建(初始化DM时自动创建)
- udevice和对应uclass的绑定(初始化DM时自动绑定)
- 对应udevice的probe调用 (需要自己实现)
- uclass的接口调用(最终对应udevice driver中ops结构体保存的各种回调)
网络命令工作流程
这里只写个简单的,就不分析了,太难写了。。直接给出简易版本,如下:
cmd/net.c:
shell中input合法的ping <IP>
---> do_ping(...)
---> net_loop(PING)
---> net_init()
---> eth_halt() --> eth_ops.stop -> asix_stop()
---> eth_init() --> eth_ops.start -> asix_start()
---> net_set_state(NETLOOP_CONTINUE)
---> ping_start()
---> net_set_timeout_handler(10000UL, ping_timeout_handler)
---> ping_send()
---> net_send_packet(ptr, len)
---> eth_send(ptr, len) --> eth_ops.send -> asix_send()
---> for(;;) --> eth_rx()
--> eth_ops.recv -> asix_recv()
--> net_process_received_packet()
--> eth_ops.free_pkt() --> asix_free_pkt()
--> jump to do recv, max 32 loops
--> if get crtl_c
--> eth_halt()
--> break for(;;)
--> if timeout or recv error
--> retry N times
--> break for(;;)
--> if OK
--> eth_halt()
--> break for(;;)
---> ret < 0, failed, else OK
---> ret CMD_RET_SUCCESS or CMD_RET_FAILURE