tiny4412 linux-4.2 移植(八)USB 2.0 host框架(3)hub

简介

tiny4412有两个usb hub,一个是芯片内部的root hub,一个是usb4604,usb4604挂载在hsic0上。

tiny4412 linux-4.2 移植(八)USB 2.0 host框架(3)hub_第1张图片

tiny4412 linux-4.2 移植(八)USB 2.0 host框架(3)hub_第2张图片

在这里插入图片描述

框架图

tiny4412 linux-4.2 移植(八)USB 2.0 host框架(3)hub_第3张图片

hub

上一节,我们知道了ehci主机控制器的相关驱动,在它的probe函数中会调用到usb_add_hcd进而调用root hub的注册函数register_root_hub。这个函数是用来注册root hub的,它会调用usb_new_device创建usb设备,因为hub本质是一个usb device,所以会调用这个函数。随后匹配到hub驱动调用hub_probe。

static int hub_configure(struct usb_hub *hub,
	struct usb_endpoint_descriptor *endpoint){
	....
	maxchild = hub->descriptor->bNbrPorts;	//由hub的描述符决定
	usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,	//注册中断处理函数,当usb端口状态发生改变时会调用它
		hub, endpoint->bInterval);
	for (i = 0; i < maxchild; i++) {	//hub 2有5个port
		ret = usb_hub_create_port_device(hub, i + 1);	//创建usb port
	}
}
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id){
	....
	INIT_WORK(&hub->events, hub_event);	//会在中断函数中调用到
	if (hub_configure(hub, &desc->endpoint[0].desc) >= 0)
		return 0;
}

上一节我们说了,由于存在root hub(hub 1)和usb4604(hub 2),所以会匹配到两个hub,调用两次hub_probe。调用顺序是这样的:开机起来在exynos_ehci_probe中调用了register_root_hub注册了hub 1,然后调用hub_probe;hub_probe初始化,随后由于有USB4604挂在hub 1上所以会有一个port change(调用hub_irq),所以会创建usb device(usb_new_device),usb4604生成的usb device挂在hub 1的 usb_port 2上,由于它的本质是一个usb hub,所以会创建一个hub 2。在创建的时候会调用device_add,它会调用hub 2的hub_probe,随后由于USB4604上有usb设备,所以会有port change(调用hub_irq),然后遍历各个usb port,挂载对应的设备。
来看下hub 1 hub 2对应的端口

hub 1-1    root hub
    usb_port   1
        usb_port   2    0424:4604        Microchip Technology, Inc.   USB4604  
        usb_port   3
hub 1-2    usb hub(ubs4604)
    usb_port 1
    usb_port 2   tiny4412sdk板左边的usb口,未插入设备时,他的usb_device是空的,插入U盘后,U盘的usb device会绑定到这个口
    usb_port 3   tiny4412sdk板右边的usb口,未插入设备时,他的usb_device是空的,插入U盘后,U盘的usb device会绑定到这个口
    usb_port 4    跟网口相关的usb口
    usb_port 5    Microchip Technology, Inc.

接下来我们来分析hub 1-2的hub_probe,由于调用层次太多,所以我这里简化代码,下一级的调用以缩进的形式表示,同一级调用保持同样的缩进。

插上U盘后的调用:
hub_irq
    kick_hub_wq
        queue_work(hub_wq, &hub->events)---->
        --->hub_event
           {
        	 //5个usb port,插右边的usb的话,只有port3进入port_event,插左边usb 只有port2进入port_event
              for (i = 1; i <= hdev->maxchild; i++) 
                 //查看是哪个usb port发生的status change,只处理发生状态改变的usb port
                 if (test_bit(i, hub->event_bits)|| test_bit(i, hub->change_bits)|| test_bit(i, hub->wakeup_bits))
                 {
                    port_event(hub, i);  --->
                        --->port_event(...)
                        	{
                                hub_port_status
                                    hub_ext_port_status
                                        get_port_status
                                            usb_control_msg
                                                usb_internal_control_msg
                                                    usb_start_wait_urb
                                                        usb_submit_urb
                                                            return usb_hcd_submit_urb(urb, mem_flags);
                                                            	//调用ehci_hc_driver的函数
                                                                status = hcd->driver->urb_enqueue(hcd, urb, mem_flags); 
                                                                   ehci_urb_enqueue 
                                                                   		 //将urb携带的信息整合到一个qtd_list链表中
                                                                         qh_urb_transaction    
                                                                          //提交数据,接下来就是DMA的事情了
                                                                         intr_submit                                                                                                                                                                                                         		
                                hub_port_connect_change
                                    hub_port_connect
                                        hub_port_init    //usb 1-2.3: new high-speed USB device number 5 using exynos-ehci
                                            usb_new_device
                                                announce_device    //usb 1-2.3: New USB device found, idVendor=05e3, 
                                                					//idProduct=0751, bcdDevice=14.02 
                                                                   //usb 1-2.3: New USB device strings: Mfr=3, Product=4, 
                                                                   //SerialNumber=0
                                                device_add(&udev->dev);    //调用 U盘的probe函数
                                                //struct usb_driver usb_storage_driver
                            }
           			}
           	  }

在调用port_event处理事件的时候会通过urb相关操作函数去获取usb状态,进过层层调用悔调用好hcd的相关函数,hcd->driver->urb_enqueue,这个接口在上一节已经说过了,指向struct hc_driver ehci_hc_driver 的ehci_urb_enqueue。这个函数会把将urb携带的信息整合到一个qtd_list链表中,提交完数据后,接下来就是DMA去帮你把数据送给usb设备了,当主机控制器处理完数据后会以中断的方式告诉CPU。
接下来会调用hub_port_connect_change去处理usb设备,创建usb device,调用device_add然后会匹配到U盘的probe函数,进入storage_probe处理usb storage。

你可能感兴趣的:(Tiny4412,Linux_4.2移植)