mtk-usb代碼分析之枚舉過程


基於mt6750T,Android 7.0,kernel 3.18.35,本文主要簡述了USB的枚舉過程,主要是從host的角度來看。

一.USB的拓撲結構

簡單來說,USB由host和device兩部分組成,hub和function device統稱為device,最多支持128個設備。host和root hub是緊密聯系的。

二.USB設備的枚舉過程

  •  1.host和hub已經初始化完,device並未插入hub的port,此時device處於UNATTACHED狀態。

        

  • 2.host通過status change ep(屬於Interrupt類型)對hub進行輪詢,當設備插入port,hub的狀態發生改變,此時hub向host返回狀態變更信息,此時device處於ATTACHED狀態。

        

  • 3.host查詢hub的狀態變更並確認變更信息
  • 4.host已經確認有device插入,等待至少100ms等待device插好並且port口power保持穩定,此時device處於POWERED狀態。
  • 5.host對hub的port進行reset操作,port開始使能。device進入DEFAULT狀態並且能夠從port獲取不超過100mA的電流,此時device的所有register和state都進行復位。

        

  • 6.host給device分配一個特殊的地址(地址0,的確很特殊啊),此時device處於ADDRESSED狀態。

        

  • 7.device被分配為地址0,可以通過默認的控制管道(ep0)對device進行操作。host獲取device的設備描述符,確認ep0最大的數據包長度。
  • 8.host獲取device的配置信息(配置描述符,接口描述符和端點描述符),從0~n-1,n是device的配置數。然后選取configuration和interface進行配置。此時設備處於CONFIGURED狀態。

        

 

 三.USB的狀態變更圖

 

 

四.代碼分析

以下代碼分析從bus->host controller->hub->device的順序進行分析,代碼分析以流程為主,細節這里就不列出來了。

4.1 Bus

//usb/core/usb.c
static int __init usb_init(void)
{
    ......
//注冊usb總線 retval
= bus_register(&usb_bus_type); ......
//注冊hub driver並創建workqueue “usb_hub_wq” retval
= usb_hub_init(); ......
//注冊usb設備驅動usb_generic_driver retval
= usb_register_device_driver(&usb_generic_driver, THIS_MODULE); ...... }
  • usb_bus_type的match函數。usb里面區分設備和接口的概念,接口對應功能,一個usb設備可能包含多個接口,比如我們的android手機屬於usb設備,插入usb可以選擇usb模式,MTP、PTP、ADB等等,這些模式對應接口的概念。
    •   設備包含一個或多個配置
    •   配置包含一個或多個接口
    •   接口包含零個或多個端點
struct bus_type usb_bus_type = {
    .name =        "usb",
    .match =    usb_device_match,
    .uevent =    usb_uevent,
};
/*
* 將usb設備及設備驅動,usb接口及接口驅動區分對待
*/
static int usb_device_match(struct device *dev, struct device_driver *drv) { //屬於usb設備? if (is_usb_device(dev)) { //不是usb設備驅動則直接返回 if (!is_usb_device_driver(drv)) return 0; return 1;
//屬於usb接口? }
else if (is_usb_interface(dev)) { struct usb_interface *intf; struct usb_driver *usb_drv; const struct usb_device_id *id; //為usb設備類型則直接返回 if (is_usb_device_driver(drv)) return 0; intf = to_usb_interface(dev); usb_drv = to_usb_driver(drv); //接口和driver的id_table進行匹配 id = usb_match_id(intf, usb_drv->id_table); if (id) return 1; //動態匹配相關 id = usb_match_dynamic_id(intf, usb_drv); if (id) return 1; } return 0; }
  • usb_hub_init分析,hub_driver的函數等到hub這節再進行分析
int usb_hub_init(void)
{
//注冊hub驅動
    if (usb_register(&hub_driver) < 0) {
        printk(KERN_ERR "%s: can't register hub driver\n",
            usbcore_name);
        return -1;
    }
//創建workqueue
    hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);
        ......
}
  • usb_register
#define usb_register(driver) \
    usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
            const char *mod_name)
{
//for_devices為0表明這是interface driver,屬於接口范疇
    new_driver->drvwrap.for_devices = 0;
    new_driver->drvwrap.driver.name = new_driver->name;
    new_driver->drvwrap.driver.bus = &usb_bus_type;
//接口匹配probe函數,最終執行interface driver的probe
    new_driver->drvwrap.driver.probe = usb_probe_interface;
    new_driver->drvwrap.driver.remove = usb_unbind_interface;
    new_driver->drvwrap.driver.owner = owner;
    new_driver->drvwrap.driver.mod_name = mod_name;
    spin_lock_init(&new_driver->dynids.lock);
    INIT_LIST_HEAD(&new_driver->dynids.list);
//驅動注冊
    retval = driver_register(&new_driver->drvwrap.driver);
}
  • usb_register_device_driver
int usb_register_device_driver(struct usb_device_driver *new_udriver,
        struct module *owner)
{
//for_devices為1表明這是device driver,屬於設備范疇
    new_udriver->drvwrap.for_devices = 1;
    new_udriver->drvwrap.driver.name = new_udriver->name;
    new_udriver->drvwrap.driver.bus = &usb_bus_type;
//之前的match函數匹配上則執行drvwrap.driver.probe函數
//usb_probe_device最終調用driver的probe
   new_udriver->drvwrap.driver.probe = usb_probe_device; new_udriver->drvwrap.driver.remove = usb_unbind_device; new_udriver->drvwrap.driver.owner = owner; //驅動注冊 retval = driver_register(&new_udriver->drvwrap.driver); }

 

4.2 Host Controller

  • host controller主要圍繞usb_create_hcd&usb_add_hcd 展開
//usb/core/hcd.c
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
        struct device *dev, const char *bus_name)
{
//直接甩鍋給其他函數,主要就是hcd的分配空間及初始化,不細究了
    return usb_create_shared_hcd(driver, dev, bus_name, NULL);
}
//usb/core/hcd.c
/*
 * 完成hcd的初始化並進行注冊
 */
int usb_add_hcd(struct usb_hcd *hcd,
        unsigned int irqnum, unsigned long irqflags)
{
//從phy_bind_list中獲取phy device,並進行初始化
    usb_get_phy_dev(hcd->self.controller, 0);
    usb_phy_init(phy);
//初始化buffer pools,分兩種情況:DMA memory和非DMA memory
//DMA memory則調用dma_pool_create,后續調用dma_poll_alloc
//非DMA則后續直接調用kmalloc
    hcd_buffer_create(hcd);
//注冊usb host controller,一個host controller對應一條總線
    usb_register_bus(&hcd->self);
//申請root hub設備
        rhdev = usb_alloc_dev(NULL, &hcd->self, 0);
//hcd的reset
    if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
        goto err_hcd_driver_setup;
    }
//申請hcd的中斷處理函數,即hcd->driver->irq    
        usb_hcd_request_irqs(hcd, irqnum, irqflags);
//hcd start
        hcd->driver->start(hcd);
//注冊root hub設備
        register_root_hub(hcd);
}
  • usb_alloc_dev
struct usb_device *usb_alloc_dev(struct usb_device *parent,
                 struct usb_bus *bus, unsigned port1)
{
//申請設備內存空間
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
//調用usb_hcd->driver->alloc_dev
    if (usb_hcd->driver->alloc_dev && parent &&
        !usb_hcd->driver->alloc_dev(usb_hcd, dev)) {
        usb_put_hcd(bus_to_hcd(bus));
        kfree(dev);
        return NULL;
    }
//設備隸屬總線usb_bus_type,屬於usb設備類型而非接口類型
    device_initialize(&dev->dev);
    dev->dev.bus = &usb_bus_type;
    dev->dev.type = &usb_device_type;
    dev->dev.groups = usb_device_groups;
//設備狀態標記為ATTACHED
    dev->state = USB_STATE_ATTACHED;
//初始化並使能ep0
    INIT_LIST_HEAD(&dev->ep0.urb_list);
    dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
    dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
    usb_enable_endpoint(dev, &dev->ep0, false);
//設置portnum
    dev->portnum = port1;
}
  • register_root_hub
static int register_root_hub(struct usb_hcd *hcd)
{
//root hub的address設置為1
    const int devnum = 1;
    usb_dev->devnum = devnum;
//下一個注冊在該總線上的設備address從2開始
    usb_dev->bus->devnum_next = devnum + 1;
//標記設備狀態為ADDRESSED
    usb_set_device_state(usb_dev, USB_STATE_ADDRESS);
//ep0最大包長度
    usb_dev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
//獲取設備描述符,18個byte
    usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
//注冊root hub設備
    usb_new_device (usb_dev);
}
  • usb_new_device
int usb_new_device(struct usb_device *udev)
{
//獲取描述符(包括配置,接口,端點描述符)
    usb_enumerate_device(udev);    /* Read descriptors */

//注冊設備,加入統一設備模型
    device_add(&udev->dev);
}
  • usb_enumerate_device
static int usb_enumerate_device(struct usb_device *udev)
{

//獲取描述符(包括配置,接口,端點描述符),解析描述符的過程這里就不貼了^_^
    usb_get_configuration(udev);
//獲取產品序列,制造序列和序列號
    udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
    udev->manufacturer = usb_cache_string(udev,
                          udev->descriptor.iManufacturer);
    udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
//otg相關,偷個懶,暫時放着不考慮
    usb_enumerate_device_otg(udev);
}

 

4.3 Hub

  • 上面已經注冊了root hub設備,此時屬於usb設備的范疇,首先執行drvwrap_driver_probe函數(usb_probe_device),之后調用usb_generic_driver->probe函數(generic_probe)
static int generic_probe(struct usb_device *udev)
{
//前面添加root hub,通過usb_new_device已經獲取到了配置描述符,這里選擇配置描述符,大多數設備只有一種配置
    usb_choose_configuration(udev);
//創建接口設備,設置配置
    usb_set_configuration(udev, c);
    
    usb_notify_add_device(udev);
    return 0;
}
  • usb_set_configuration
int usb_set_configuration(struct usb_device *dev, int configuration)
{
    for (i = 0; i < nintf; ++i) {
//使用setting num為0的altsetting,這里setting num和數組下標並不存在一一對應關系 alt
= usb_altnum_to_altsetting(intf, 0);
//沒有num為0則使用第一個setting
if (!alt) alt = &intf->altsetting[0];
//使能接口,里面調用使能端點 usb_enable_interface(dev, intf,
true); //表明屬於接口設備類型 intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; intf->dev.groups = usb_interface_groups; device_initialize(&intf->dev); }
//控制傳輸,設置配置 ret
= usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
//usb設備狀態為CONFIGURED usb_set_device_state(dev, USB_STATE_CONFIGURED);
for (i = 0; i < nintf; ++i) {
//添加接口設備並創建端點設備 ret
= device_add(&intf->dev); create_intf_ep_devs(intf); } }
  •  注冊了接口設備,那必然要與總線上的接口驅動相match,與驅動的id_table或者動態id匹配。hub設備顧名思義就是與hub_driver匹配,執行hub_driver->probe函數
  • 整個以上過程可以用下面圖展示

 

 

static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
//判斷端點是否屬於中斷類型,前面提到host通過中斷端點進行輪詢hub
    if (!usb_endpoint_is_int_in(endpoint))
        goto descriptor_error;
//初始化hub_event任務,hub_event處理設備插入hub端口時的事件
    INIT_WORK(&hub->events, hub_event);
//hub配置
    hub_configure(hub, endpoint);
}
  •  hub_configure
static int hub_configure(struct usb_hub *hub,
    struct usb_endpoint_descriptor *endpoint)
{
//獲取hub描述符,之后對hub描述符進行分析,分析bNbrPorts,wHubCharacteristics,bDeviceProtocol這些字段
    ret = get_hub_descriptor(hdev, hub->descriptor);

//獲取USB設備狀態,這里屬於標准請求,主要是D0 bit的設備是否self powered
    ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
   
//獲取hub的狀態,屬於hub類型請求,由兩個2byte的字段組成,wHubStatus和wHubChange,跟供電狀態和是否過流相關
    ret = hub_hub_status(hub, &hubstatus, &hubchange);

//創建pipe
    pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
    maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));

//創建中斷urb,urb完成函數hub_irq,當hub有狀態發生變化時就調用kick_hub_wq將hub->events加入workqueue,這種針對熱插拔時USB枚舉的情況
    hub->urb = usb_alloc_urb(0, GFP_KERNEL);
    usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval);

//創建端口設備,有多少個端口就創建多少個端口設備
    usb_hub_create_port_device(hub, i + 1);

//hub初始化,分為三部曲
    hub_activate(hub, HUB_INIT);
}
  • hub初始化三部曲
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
//init第一部曲,hub上電
    if (type == HUB_INIT) {
            unsigned delay = hub_power_on_good_delay(hub);

            hub_power_on(hub, false);
            INIT_DELAYED_WORK(&hub->init_work, hub_init_func2);
            queue_delayed_work(system_power_efficient_wq,
                    &hub->init_work,
                    msecs_to_jiffies(delay));
            return;
    }
//初始化第二部曲,檢測端口狀態變化
init2:
    for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
        status = hub_port_status(hub, port1, &portstatus, &portchange);
        set_bit(port1, hub->change_bits);
    }
        if (need_debounce_delay) {
        delay = HUB_DEBOUNCE_STABLE;

        if (type == HUB_INIT2) {
            INIT_DELAYED_WORK(&hub->init_work, hub_init_func3);
            queue_delayed_work(system_power_efficient_wq,
                    &hub->init_work,
                    msecs_to_jiffies(delay));
            device_unlock(hub->intfdev);
            return;        /* Continues at init3: below */
        } else {
            msleep(delay);
        }
    }
//初始化第三部曲,hub->events加入workqueue,進行端口狀態變化的后續處理,這種針對hub初始化時候USB設備的枚舉過程
 init3:
    status = usb_submit_urb(hub->urb, GFP_NOIO);
    kick_hub_wq(hub);
}
  • hub_event
static void hub_event(struct work_struct *work)
{
    for (i = 1; i <= hdev->maxchild; i++) {
//hub端口狀態發生改變,調用port_event函數,里面主要為usb設備的枚舉過程
        port_event(hub, i);
    }
}

 

4.4 Device

  • port_event
static void port_event(struct usb_hub *hub, int port1)
        __must_hold(&port_dev->status_lock)
{
//主要調用下面函數
    hub_port_connect_change(hub, port1, portstatus, portchange);
}
  • hub_port_connect_change
static void hub_port_connect_change(struct usb_hub *hub, int port1,
                    u16 portstatus, u16 portchange)
        __must_hold(&port_dev->status_lock)
{
//繼續直接甩鍋
    hub_port_connect(hub, port1, portstatus, portchange);
}
  • 下面開始正戲了
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)
{
//創建usb設備,此時設備處於ATTACHED狀態
    udev = usb_alloc_dev(hdev, hdev->bus, port1);
//設備處於POWERED狀態
    usb_set_device_state(udev, USB_STATE_POWERED);
//選擇devicemap中第一個不為0的bit數作為設備number
    choose_devnum(udev);
//復位,分配地址,獲取設備描述符
    status = hub_port_init(hub, udev, port1, i);
//注冊usb設備,之后的流程參考前面root hub,設置configuration&interface
status = usb_new_device(udev);
}
  • hub_port_init
static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter)
{
//更新udev->devnum為0,最終調用hcd->driver->reset_device函數,復位后設備處於DEFAULT狀態,默認狀態下的設備地址為0
    hub_port_reset(hub, port1, udev, delay, false);
//根據設備類型,設置ep0最大傳輸包長度,高速和低速確定,但是全速不確定,需要從設備描述符中獲取
switch (udev->speed) {
    case USB_SPEED_SUPER:
    case USB_SPEED_WIRELESS:    /* fixed at 512 */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
        break;
    case USB_SPEED_HIGH:        /* fixed at 64 */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
        break;
    case USB_SPEED_FULL:        /* 8, 16, 32, or 64 */
        /* to determine the ep0 maxpacket size, try to read
         * the device descriptor to get bMaxPacketSize0 and
         * then correct our initial guess.
         */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
        break;
    case USB_SPEED_LOW:        /* fixed at 8 */
        udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
        break;
    default:
        goto fail;
    }
//獲取設備描述符,獲取長度為64byte,來確定ep0的最大傳輸包長度
    usb_control_msg(udev, usb_rcvaddr0pipe(),
                    USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                    USB_DT_DEVICE << 8, 0,
                    buf, GET_DESCRIPTOR_BUFSIZE,
                    initial_descriptor_timeout);


//參考choose_devnum定義的devnum,分配設備地址,此時設備處於ADDRESSED狀態
   hub_set_address(udev, devnum); 
//先獲取前8個字節,第一個byte即是該描述符的長度
    usb_get_device_descriptor(udev, 8);
//再獲取一次,得到完整的設備描述符
    usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM