《Linux设备驱动开发详解》读书笔记(6)

时间:2022-02-11 17:55:44

第十六章,USB主机、设备与Gadget驱动

USB的逻辑层次包括设备、配置、接口和端点4个层次,每层有各自的描述符descriptor。端口可以看作是一个单向的管道。端点0通常为控制端点,用于设备初始化。命令lsusb可以查看USB描述符。

主机控制器有OHCI, UHCI,EHCI。数据结构为structhc_driver,通过函数usb_create_hcd(), usb_add_hcd()来创建和添加。其成员函数有urb_enqueue(),用于发送数据。上层usb_submit_urb()提交1个请求后,最终就调到urb_enqueue()。HCI的特例为ehci,可以用hcd_to_ehci(),ehci_to_hcd()进行转换。操作函数包括ehci_init(),ehci_run(), ehci_init_driver()等。

USB设备信息可以在/sys/kernel/debug/usb/devices里面查看,或者用USBView查看。编写新的USB驱动,主要实现probe()和disconnect()。使用usb_regsiter()注册struct usb_driver。匹配时会用到id_table。USB请求块的数据结构为structurb,操作的函数有usb_alloc_urb(),usb_fill_int_urb(), usb_fill_control_urb(), usb_fill_bulk_urb()分别用于填充不同类型的数据(ISO URB需要手工创建)。创建后,通过usb_submit_urb()提交请求,其中数据buffer要指明是GFP_ATOMIC\GFP_KERNAL等。完成后,回调函数complete()将被调用。也可以用函数usb_unlink_urb()和usb_kill_urb()取消请求。对简单的消息,可以直接用usb_bulk_msg()或者usb_control_msg()来发送消息。

内核源码中的driver/usb/usb-skeleton.c提供了一个USB驱动程序的例子。

USB设备控制器(UDC)指的设备作为USB device(比如手机作为U盘),需要实现UDC驱动和function驱动,代码在drivers/usb/gadget下面。里面实现了一些常见的功能:USB以太网(CDC),U盘,串口,MIDI,USB视频(UVC)。关键的数据结构有struct usb_gadget, usb_ep, usb_gadget_ops, ep_ops。调用函数usb_agg_gadget_udc()添加UDC,函数usb_function_register()注册功能驱动。在Gadget驱动中,用struct usb_request做传输,类似于USB主机侧的URB。常用的API包括usb_ep_enable(), alloc_ep_req(), usb_ep_enqueue(),usb_ep_autoconfig()等。代码f_loopback.c给出了一个Gadget function驱动的例子。驱动中关键的函数包括bind(),set_alt()等。

USB OTG的支持是实现了主机通令协议(HNP)和对话请求协议(SRP)。在struct usb_gadget中增加了is_otg, b_hnp_enable等属性,还有几个函数usb_gadget_connect(),usb_gadget_wakeup()。在主机侧struct usb_bus中增加了otg_port, is_b_host, b_hnp_enable等属性。还有一个结构struct usb_otg用于OTG功能切换和协议描述。Usb_otg的代码一般在USB的PHY端实现。

第十七章,I2C、SPI、USB驱动架构类比。

都分成主机的数据接口,主机驱动传输函数,主机的枚举方法,控制器,描述传输协议的数据结构,传输API,外设的枚举方法,描述外设的数据结构,设备数中添加节或者热插拔。

第十八章,ARM设备树

设备树是一种描述硬件的数据结构,起源于OpenFirmware,所以大多函数以OF开头。

DTS经由DTC编译成DTB,在编译Linux内核时运行make dtbs即可。Bootloader读取DTB到内存中,并将地址传给kernel。编译Uboot时,设置CONFIG_LIBFDT,就可以使用FDT命令设置.dtb的地址。通过bootz kernel_addr initrd_addr dtb_addr启动内核。如果要将DTB附加到zImage后面,需要设置CONFIG_ARM_APPENDED_DTB。

对设备树中的节点和属性的描述文档在Documentation/devicetree/bindings目录下,如果添加了新的compatible字符串而没有相应的文档,DTC会报警。

定义一个新的板子,需要设置DT_MACHINE_START。其中.dt_compat定义了兼容的板类型,其他是一堆回调函数,比如.smp, .init_irq等。使用of_machine_is_compatible()来检查板子或者SoC的兼容性。

对于.dts中的每个设备节点,兼容属性用于设备和驱动的绑定。如对I2C和SPI,函数probe()依赖于of_match_table完成匹配。I2C/SPI也可以依赖于别名完成匹配。

除了of_device_is_compatible()之外,也可以利用填写of_device_id表中的.data成员的方式,然后通过of_match_node()取到其中的.data。这样可以把设备兼容的私有数据寻找出来。

可以给一个设备节点添加label,然后通过&label引用。

获取中断使用platform_get_irq(),获取gpio使用of_get_named_gpio(),获取时钟通过devm_clk_get()。调用of_platform_bus_type()即可自动展开所有的platform_device。

常用的OF API包括寻找节点of_find_compatible_node(),读取属性of_property_read_xxx(),内存映射of_iomap(),解析中断irq_of_parse_and_map(),获取设备of_find_device_by_node()。