概述
1、usb 设备识别枚举过程
当USB设备连接到或从USB中移除时,主机使用总线枚举过程来识别和管理接入的设备。当USB设备连接到一个已经被上电的端口,采取以下顺序行动:
1.1 设备上电
用户把USB设备插入USB端口(主机下的根hub或主机下行端口上的hub端口)或系统启动时设备上电。此时,USB设备处于加电状态(powered),它所连接的端口是无效的。
1.2 Hub检测电压变化,报告主机
hub会实时监测端口的电平变化,一旦HUB检测到端口有电压变化,hub将利用自己的中断端点hub->urb
将信息反馈给主控制器,告诉主机有设备连接hun_irq
。
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
1.3 主机了解连接设备
如果有连接/断开事件发生,那么主机会发送一个 Get_Port_Status
请求给 hub 以了解此次状态改变的确切含义。Get_Port_Status
等请求属于所有 hub 都要求支持的 hub 类标准请求。
== port_event(struct usb_hub *hub, int port1);
== hub_port_status(hub, port1, &portstatus, &portchange);
1.4 主机检测所插入的设备是全速还是低速
hub 通过检测 USB 总线空闲时的差分线的高低电压来判断所连接设备的速度类型,当 host 发来Get_Port_Status
请求时,hub 就可以将此设备的速度类型信息回复给 host。
USB 2.0 规范要求速度检测要先于复位(Reset)操作。根据是 D+ 还是 D- 被拉高来判断到底是什么设备(全速/低速)插入端口。
== hub_port_init(hub, udev, port1, i);
//复位设备, 分配地址, 获取设备描述符
== hub_port_reset(hub, port1, udev, delay, false); //reset device
== hub_port_wait_reset(hub, port1, udev, delay, warm);
//usb 设备speed
if (hub_is_wusb(hub))
udev->speed = USB_SPEED_WIRELESS;
else if (hub_is_superspeedplus(hub->hdev) &&
port_speed_is_ssp(hub->hdev, ext_portstatus & USB_EXT_PORT_STAT_RX_SPEED_ID))
udev->speed = USB_SPEED_SUPER_PLUS;
else if (hub_is_superspeed(hub->hdev))
udev->speed = USB_SPEED_SUPER;
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
udev->speed = USB_SPEED_LOW;
else
udev->speed = USB_SPEED_FULL;
1.5 主机通过hub复位设备
主机一旦得知新设备已连上以后,它至少等待100ms
以使得插入操作的完成以及设备电源稳定工作。然后主机控制器就向 hub 发出一个 Set_Port_Feature
请求让 hub 复位刚才设备插上的端口。hub 通过驱动数据线到复位状态(D+和D-全为低电平 ),并持续至少10ms。当然,hub 不会把这样的复位信号发送给其他已有设备连接的端口,所以其他连在该 hub 上的设备自然看不到复位信号,不会受影响。
== hub_port_init(hub, udev, port1, i);
//复位设备, 分配地址, 获取设备描述符
== hub_port_reset(hub, port1, udev, delay, false); //reset device
1.6 主机进一步检测全速设备是否是支持高速模式
因为根据 USB 2.0
协议,高速(High Speed)
设备在初始时是默认全速(Full Speed )
状态运行,所以对于一个支持 USB 2.0
的高速hub,当它发现它的端口连接的是一个全速设备时,会进行高速检测,看看目前这个设备是否还支持高速传输,如果是,那就切到高速信号模式,否则就一直在全速状态下工作。同样的,从设备的角度来看,如果是一个高速设备,在刚连接到 hub 时或上电只能用全速模式运行。随后hub会进行高速检测,之后这个设备才会切换到高速模式下工作。假如所连接的 hub 不支持USB 2.0
,即不是高速hub,不能进行高速检测,设备将一直以全速工作。
1.7 通过Hub建立主机和设备之间的信息通道
主机不停地向 hub 发送Get_Port_Status
请求,以查询设备是否复位成功。Hub 返回的报告信息中有专门的一位用来标志设备的复位状态。当 hub 撤销了复位信号,设备就处于默认/空闲状态(Default state)
,准备接收主机发来的请求。设备和主机之间的通信通过控制传输管道,默认管道为地址0、端点0。此时,设备能从总线上得到的最大电流是100mA
。此后主机就可以通过默认控制管道和设备进行控制传输。
== hub_port_init(hub, udev, port1, i);
//复位设备, 分配地址, 获取设备描述符
== hub_port_reset(hub, port1, udev, delay, false); //reset device
== set_port_feature(hub->hdev, port1, (warm ?
USB_PORT_FEAT_BH_PORT_RESET : USB_PORT_FEAT_RESET));
== usb_set_device_state(udev, USB_STATE_DEFAULT); //设置udev 的转态为 default
1.8 主机获取默认控制管道的最大数据包长度
默认管道其实连接到设备一端其实就是端点0。主机此时发送的请求是默认地址0,端点0,虽然所有未分配地址的设备都是通过地址0来获取主机发来的请求,但由于枚举过程不是多个设备并行处理,而是一次枚举一个设备的方式进行,所以不会发生多个设备同时响应主机发来的请求。
主机会发送Get_Descriptor
获取设备描述符,设备描述符的第8字节代表设备端点0的最大包大小,只有知道端点0 的最大包长度,才知道一次控制传输要从设备请求多少字节数据。
== hub_port_init(hub, udev, port1, i);
//复位设备, 分配地址, 获取设备描述符
== usb_get_device_descriptor(udev, 8);
//获取最大数据包长度
if (udev->descriptor.bMaxPacketSize0 == 0xff ||
udev->speed >= USB_SPEED_SUPER)
i = 512;
else
i = udev->descriptor.bMaxPacketSize0;
1.9 主机请求hub再次复位设备
当Get_Descriptor
获取设备描述符请求成功,系统会要求 hub 对设备进行再一次的复位操作(USB规范里面可没这要求),再次复位的目的是使设备进入一个确定的状态。
1.10 主机给设备分配一个新地址
主机控制器通过Set_Address
请求向设备分配一个唯一的地址。在完成这次传输之后,设备进入地址状态,之后就启用新地址与主机通信。这个地址对于设备来说是终生制的,设备在,地址在;设备消失(被拔出,复位,系统重启),地址被收回。同一个设备当再次被枚举后得到的地址不一定是上次那个了。
== hub_port_init(hub, udev, port1, i);
//复位设备, 分配地址, 获取设备描述符
== hub_set_address(udev, devnum);
//address device
== usb_control_msg(udev, usb_sndaddr0pipe(),
USB_REQ_SET_ADDRESS, 0, devnum, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
== usb_set_device_state(udev, USB_STATE_ADDRESS);
1.11 主机获取并解析设备描述符信息
主机发送Get_Descriptor
请求读取设备描述符,这次主机发送Get_Descriptor
请求使用新地址,它会解析设备描述符的每一项内容。设备描述符内信息包括端点0的最大包长度、设备所支持的配置个数、设备类型、VID、 PID、字符串索引等信息。
== hub_port_init(hub, udev, port1, i);
//复位设备, 分配地址, 获取设备描述符
== usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
//获取整个设备描述符
1.12 主机获取字符串描述符
根据如果有字符串描述符,主机会获取语言ID描述符和字符串描述符。
1.13 主机获取标准配置描述符
主机发送Get_Descriptor_Configuration
请求并解析标准配置描述符,标准配置描述符内信息包括配置描述符集合长度、接口数、设备属性、设备所需电流。
1.14 主机获取配置描述符集合
配置描述符集合包括标准配置描述符、接口描述符、端点描述符,如果是 HID 设备还会包括 HID 描述符,主机会根据上面得到的标准配置描述符的wTotalLength
,发送了一个Get_Descriptor_Configuration
请求获取到配置描述集合并解析所有描述符,从而知道设备到底是什么样的设备。
1.15 主机为设备挂载驱动并选择一个配置
主机通过解析描述符后对设备有了足够的了解,会选择一个最合适的驱动给设备,现在就将控制权交到设备驱动了。对于复合设备,通常应该是不同的接口配置给不同的驱动。
== usb_choose_configuration(udev);
//选择一个配置
主机发送Set_Configuration
请求来正式确定选择设备的哪个配置作为工作配置(对于大多数设备来说,一般只有一个配置被定义)。至此,设备处于配置状态(Configured)
。
== usb_set_configuration(udev, c);
//设置一个配置
== dev->actconfig = cp;
== usb_set_device_state(dev, USB_STATE_CONFIGURED);
2、code 流程
hub 检测到端口有 usb 设备连接时,hub_port_connect
枚举设备过程:
== usb_alloc_dev(hdev, hdev->bus, port1);
//构建udev
== usb_set_device_state(udev, USB_STATE_POWERED); //设置udev 的状态为 powered
== hub_port_init(hub, udev, port1, i);
//复位设备, 分配地址, 获取设备描述符
== hub_port_reset(hub, port1, udev, delay, false); //reset device
== set_port_feature(hub->hdev, port1, (warm ?
USB_PORT_FEAT_BH_PORT_RESET : USB_PORT_FEAT_RESET));
== usb_set_device_state(udev, USB_STATE_DEFAULT); //设置udev 的转态为 default
== hub_set_address(udev, devnum);
//address device
== usb_control_msg(udev, usb_sndaddr0pipe(),
USB_REQ_SET_ADDRESS, 0, devnum, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
== usb_set_device_state(udev, USB_STATE_ADDRESS);
== usb_get_device_descriptor(udev, 8);
//获取最大数据包长度
if (udev->descriptor.bMaxPacketSize0 == 0xff ||
udev->speed >= USB_SPEED_SUPER)
i = 512;
else
i = udev->descriptor.bMaxPacketSize0;
== usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
//获取整个设备描述符
== usb_new_device(udev);
//注册usb device
== usb_enumerate_device(udev);
//Read device configs/intfs/otg
== usb_get_configuration(udev);
//Get the USB config descriptors
== announce_device(udev);
== device_add(&udev->dev);
//注册usb device 设备
== usb_phy_notify_connect(hcd->usb_phy, udev->speed); //通知usb phy
== usb_choose_configuration(udev);
//选择一个配置
== usb_set_configuration(udev, c);
//设置一个配置
== dev->actconfig = cp;
== usb_set_device_state(dev, USB_STATE_CONFIGURED);
== device_add(&intf->dev);
//注册usb interface设备
== usb_notify_add_device(udev);
/* USB device state == configured ... usable */
最后
以上就是过时柚子为你收集整理的usb host 驱动 - device 插入枚举过程1、usb 设备识别枚举过程2、code 流程的全部内容,希望文章能够帮你解决usb host 驱动 - device 插入枚举过程1、usb 设备识别枚举过程2、code 流程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复