USB总线驱动

SOC通过AHB总线连接USB控制器,USB控制器,USB控制器内部集了一个根集线器(root hub),可以直接连接USB设备,也可以连接集线器,然后接不同的USB设备.

UHCI/OHCI/EHCI/XHCI
1.0低速:1.5M
2.0全速:12M
2.0高速:480M
USB3.0:5G

USB热插拔的硬件实现:
USB集线器的每个端口的D+/D-上,分别接了一个15K的电阻到地,这样,在集线器的端口悬空时,就被这两个下拉电阻拉到了低电平,而在USB设备端,在D+/D-上接了1.5k欧姆上拉电阻,对于全速和高速设备,上拉电阻是接在D+上,低速设备是接在D-上,这样,当设备插入到集线器时,由1.5K的上拉电阻和15K下拉电阻分压,结果就将差分数据线中的一条拉高了,集线器检测到这个状态后,它就报告给USB主控制器,这样就检测到了硬件的插入.
USB高速设备先被识别成全速设备,然后通过HOST和DEVICE两者之间的确认,再切换到高速模式的.
热插拔软件实现:当USB控制器检测到有外部设备插入时,控制器会发出往地址0(新设备的默认地址)发出读设备控制符命令,设备收到请求后,会回复设备描述符给总线驱动程序,之后,控制器会reset该外部设备,并充分分配地址.此后一直用该新地址通信.
在这里插入图片描述

USB通信分主从模式,USB主机发起通信请求,设备进行数据回复.USB设备不具备主动向主机通信的能力.
USB设备分为如下模块:
USB主设备<---->USB主控制驱动HCD(root hub)<---->USB core<---->USB设备.
在这里插入图片描述
USB CORE是真个软件部分的核心,是独立与硬件的协议栈.为设备驱动提供服务,提供一个用于访问和控制USB硬件的接口,而不用考虑系统当前使用的哪种HOST controller.USB core将用户的请求映射到相关的HCD,(host controller driver)用户不能直接访问HCD.USB core就是HCD 与USB 设备的桥梁.
USB主机控制器HCD:完成主机控制器的初始化以及数据的传输,并其内核线程监测外部设备的插入,完成设备枚举.

USB设备由很多:比如鼠标键盘,U盘,声卡,打印机等
根据其设备类型的不同,其传输方式也不同.
USB传输方式:
控制: 可靠,时间有保证. 获取/配置设备,比如USB设备的识别过程.
中断:可靠,实时,例如USB鼠标
批量:可靠,时间没保证.用于大容量数据传输,没有固定的传输速率.例如打印机/扫描仪/U盘等
实时:不可靠,实时.可以传输大批量数据,但是对数据是否到达没有保证,对实时性要求很高,例如音频声卡/USB摄像头

那么,当一个USB设备插入到机器时,主控制是怎么知道它属于哪种设备的呢?
答案就是,主控器会读取USB设备的描述符(与PCI控制器读取PCI设备的配置信息一样),USB设备里面也相当于有一个存储单元.里面存放有该设备的描述符等信息.
USB的描述符有:
设备描述符:struct usb_device_descriptor
配置描述符:struct usb_config_descriptor
接口描述符:struct usb_interface_descriptor
端点描述符:struct usb_endpoint_descriptor

USB传输的对象为端点(通过管道),没一个端点都有传输类型/传输方向,除了端点0外,每一个端点只支持一个方向的数据传输,端点0用于控制传输,既能输入,也能输出.

注意一点:USB设备驱动是和USB接口绑定,而不是设备.

lsusb可以列出所有的设备,
lsusb -v可以列出USB设备的所有描述符.

对于USB PCI这种可以动态探测的设备,是不需要在dts中描述的.但是对于i2c等不能动态探测的是,是需要在dts中显示描述

USB core分析:kernel/driver/usb/core/usb.c
subsys_initcall(usb_init);
bus_register(&usb_bus_type);//总线初始化,注册.
bus_register_notifier(&usb_bus_type, &usb_bus_nb);
usb_major_init();–>register_chrdev(USB_MAJOR, “usb”, &usb_fops);
usb_register(&usbfs_driver);
usb_hub_init();//root hub相关,注册hub driver.probe中起工作队列work内核线程检测新设备,hub_events
usb_register_device_driver(&usb_generic_driver, THIS_MODULE);//注册USB通用驱动,用于USB设备插入后 设备驱动枚举.

USB主机控制器驱动HCD(以ehci为例)
dts定义:
usb_host0_ehci: usb@ff500000 {
compatible = “generic-ehci”;
reg = <0x0 0xff500000 0x0 0x20000>;
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru HCLK_USBHOST0>, <&usbphy1>;
clock-names = “usbhost”, “utmi”;
phys = <&usbphy1>;
phy-names = “usb”;
status = “disabled”;
};

在这里插入图片描述
控制器上内嵌了一个特殊的USB设备,称为root hub,根集线器提供的连接点称为port.主机控制器驱动(HCD)需要完成的工作
1.完成控制器硬件的初始化,使得USB PHY工作在主状态下.
2.提供并实现控制器驱动的操作函数,用于与从设备数据交互.
3.既然root hub是一个USB设备,在驱动中,需要创建并注册一个USB device来表示这个root hub.
4.外部设备插在root hub上,软件需要完成对root hub的监控,以及USB新设备的插入检测以及插入后的枚举识别初始化.

Linux讲ehci作为一个platform_device注册,用struct usb_hcd抽象主控制器驱动.
主控制器驱动分为两大部分:1.平台硬件相关初始化.2.内核通用的USB HCD软件初始化流程.
ehci_platform_probe()中,定义了ehci的power_on/off函数先是设置了DMA的标识,然后platform_get_irq,然后调用usb_create_hcd申请了hcd(定义了定时器与wakeup_work),platform_get_resource/devm_ioremap_resource.然后,调用usb_add_hcd()讲hcd加到USB bus中,并tasklet_init连个tasklet,还申请了一个usb device,指向给控制器的root hub.并把该root hub注册到bus总线.

dwc2 主控制器的相关代码:linux/driver/usb/core/dwc2/platform.c

usb_host1: usb@ff540000 {
	compatible = "rockchip,rk3288-usb", "rockchip,rk3066-usb",
			"snps,dwc2";
	reg = <0x0 0xff540000 0x0 0x40000>;
	interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&cru HCLK_USBHOST1>;
	clock-names = "otg";
	dr_mode = "host";
	phys = <&usbphy2>;
	phy-names = "usb2-phy";
	status = "disabled";
};

usb_otg: usb@ff580000 {
	compatible = "rockchip,rk3288-usb", "rockchip,rk3066-usb",
			"snps,dwc2";
	reg = <0x0 0xff580000 0x0 0x40000>;
	interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&cru HCLK_OTG0>;
	clock-names = "otg";
	dr_mode = "otg";
	g-np-tx-fifo-size = <16>;
	g-rx-fifo-size = <280>;
	g-tx-fifo-size = <256 128 128 64 32 16>;
	g-use-dma;
	phys = <&usbphy0>;
	phy-names = "usb2-phy";
	status = "disabled";
};
dwc2.c中完成usb_crearte_hcd/usb_add_hcd完成hub驱动的注册.并usb_alloc_device申请usb dev,并指向root hub.而且注册这个root hub.这个root hub注册,会导致usb core中usb_hub_init中注册的通用usb驱动usb_generic_driver的generic_probe调用.generic_probe中usb_choose_configuration(),usb_set_configuration()读取设置usb 设备配置,根据读取配置的bDeviceClass,并发出USB_ADD_DEV的notification,如果是USB_CLASS_HUB类型的,调用hub.c中的hub_probe.在hub probe中读取主控器中断,起工作队列.然后会读取hub这个usb device的endpoint端点信息,并构建urb,然后回周期性的往端点发送信息,读取配置.

新USB设备插入的时候,电平电话会产生hub中断,hub_irq–>kick_hub_wq–>queue_work(hub_wq, &hub->events)–>hub_event->hub_event里面会检测每个port的状态位,并调用port_event–>hub_port_connect_change—>hub_port_connect(这里面会重新读取当前设备的设备描述符,usb_alloc_dev,usb_new_device,device_add)–>device_add讲当前设备加载到usb_bus,.bus的match函数会匹配设备的vid,pid,然后和驱动的id_table match,matchOK后,调用驱动的probe.(usb的总线match,会一次比较bInterfaceClass,bInterfaceSubClass,)

一个主控制器对应一条USB总线,一个主控制器绑定着一个root hub,一个 root hub对应于一个usb _device.然后注册root hub.每个USB设备(usb_device)有一种或多种配置,每种配置有一个或多个接口,一个接口有一种或多种设置(功能),一种设置有一个或多个端点.

USB设备驱动.
以USB鼠标驱动为例:kernel\drivers\hid\usbhid\usbmouse.c
https://blog.csdn.net/kehyuanyu/article/details/100983179
简单来说,usbmouse.c的probe中,显示对端口进行了判断,然后申请管道(pipi)用于和端点通信,然后申请了dma空间,然后调用usb_alloc_urb()构建了一个urb.然后使用usb_fill_int_urb完善这个rub结构变量,并传入urb完成回调函数.usb_mouse_irq.
在usbmouse的input->open函数中调用usb_submit_urb().提交urb,当读取到数据之后,会调用usb_mouse_irq回调函数,在回调中解析数据并再次调用usb_submit_urb.然后一直欢快的跑下去.当disconnect的时候,调用usb_free_urb()讲urb清除掉.

https://blog.csdn.net/boazheng/article/details/88950725(Linux USB总线驱动框架分析)
https://xingxingzhihuo.blog.csdn.net/article/details/104259648

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九月天-深圳专业软硬件开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值