linux usb 子系統(二)- host driver

Hacker_Albert發表於2020-10-18
  • 瞭解usb host driver.

1.USB Subsystem Framework

  The following chart shows the framework of the USB subsystem in Linux. Like i2c, the USB subsystem can be divided into three layers: ** Device Driver Layer - USB Core - Controller Driver Layer*

在這裡插入圖片描述

  The usb protocol is a complex protocol,The currently involved versions are usb1.0, usb2.0, usb3.0. If you open the kernel usb host directory, you will find that the following contains various forms of controller drivers such as ohci, uhci, ehci, xhci, and whci.

2.USB core 初始化

  usb_int 初始化整個usb系統的基礎部分。(drivers/usb/core/usb.c)

865 static int __init usb_init(void)
866 {
867   int retval;
868   if (nousb) {
869    pr_info("%s: USB support disabled/n", usbcore_name);
870    return 0;
871   }
872
873   retval = ksuspend_usb_init();
874   if (retval)
875    goto out;
876   retval = bus_register(&usb_bus_type);
877   if (retval) 
878    goto bus_register_failed;
879   retval = usb_host_init();
880   if (retval)
881    goto host_init_failed;
882   retval = usb_major_init();
883   if (retval)
884    goto major_init_failed;
885   retval = usb_register(&usbfs_driver);
886   if (retval)
887    goto driver_register_failed;
888   retval = usb_devio_init();
889   if (retval)
890    goto usb_devio_init_failed;
891   retval = usbfs_init();
892   if (retval)
893    goto fs_init_failed;
894   retval = usb_hub_init();
895   if (retval)
896    goto hub_init_failed;
897   retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
898   if (!retval)
899    goto out;
900 
901   usb_hub_cleanup();
902  hub_init_failed:
903   usbfs_cleanup();
904 fs_init_failed:
905   usb_devio_cleanup();
906 usb_devio_init_failed:
907   usb_deregister(&usbfs_driver);
908 driver_register_failed:
909   usb_major_cleanup();
910 major_init_failed:
911   usb_host_cleanup();
912 host_init_failed:
913   bus_unregister(&usb_bus_type);
914 bus_register_failed:
915   ksuspend_usb_cleanup();
916 out:
917   return retval;
918 } 
  • 註冊USB匯流排
    bus_register(&usb_bus_type);
  • 註冊usbfs驅動
    usb_register(&usbfs_driver);
  • 註冊usb hub驅動
    usb_hub_init -> usb_register(&hub_driver)
  • 註冊通用裝置驅動
    usb_register_device_driver(&usb_generic_driver, THIS_MODULE)

3.s3c2410 host driver

3.1.Makefile:

obj-$(config_usb_ohci_hcd_s3c2410) +=ohci-s3c2410.o
obj-$(config_usb_ohci_hcd) +=ohci-hcd.o

3.2.Kconfig

config usb_ohci_hcd_s3c2410
    tristate "ohci support for samsung s3c24xx/s3c64xx soc series"
    depends on usb_ohci_hcd&&(arch_s3c24xx || arch_s3c64xx)
    default y
    --- help ---
     enables support for the on-chip ohci controller on
     s3c24xx/s3c64xx chips.

config usb_ohci_hcd
  tristate "ohci hcd (usb 1.1) support"
  depends on has_dma && has_iomem

3.3.files

  • drivers/usb/host/ohci-hcd.c
  • ohci-s3c2410.c

4.程式碼分析

drivers/usb/host/ohci-s3c2410.c :

  468 static int __init ohci_s3c2410_init(void)
  469 {
  470     if (usb_disabled())
  471         return -ENODEV;
  472 
  473     pr_info("%s: " DRIVER_DESC "\n", hcd_name);
  474     ohci_init_driver(&ohci_s3c2410_hc_driver, NULL);
  475 
  476     /*
  477      * The Samsung HW has some unusual quirks, which require
  478      * Sumsung-specific workarounds. We override certain hc_driver
  479      * functions here to achieve that. We explicitly do not enhance
  480      * ohci_driver_overrides to allow this more easily, since this
  481      * is an unusual case, and we don't want to encourage others to
  482      * override these functions by making it too easy.
  483      */
  484 
  485     ohci_s3c2410_hc_driver.hub_status_data  = ohci_s3c2410_hub_status_data;
  486     ohci_s3c2410_hc_driver.hub_control  = ohci_s3c2410_hub_control;
  487                                                                                                        
  488     return platform_driver_register(&ohci_hcd_s3c2410_driver);
  489 }
  490 module_init(ohci_s3c2410_init);

  ohci_hcd_s3c2410_driver作為platform_driver例項,指定當與bus上相應的platform_device匹配後需要執行的probe函式。ohci的platform_device在arch/arm/plat-samsumg/devs.c中定義:

   939 struct platform_device s3c_device_ohci = {
   940     .name       = "s3c2410-ohci",                                                                     
   941     .id     = -1,
   942     .num_resources  = ARRAY_SIZE(s3c_usb_resource),
   943     .resource   = s3c_usb_resource,
   944     .dev        = {
   945         .dma_mask       = &samsung_device_dma_mask,
   946         .coherent_dma_mask  = DMA_BIT_MASK(32),
   947     }
   948 };

  bus_match成功後,執行ohci_hcd_s3c2410_probe函式進行驅動的載入。

static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
				  struct platform_device *dev)
{
	struct usb_hcd *hcd = NULL;
	struct s3c2410_hcd_info *info = dev_get_platdata(&dev->dev);
	int retval;
 
	s3c2410_usb_set_power(info, 1, 1);
	s3c2410_usb_set_power(info, 2, 1);
 
	hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx");
	if (hcd == NULL)
		return -ENOMEM;
 
	hcd->rsrc_start = dev->resource[0].start; // 指向hc 的暫存器首地址0x49000000
	hcd->rsrc_len	= resource_size(&dev->resource[0]); 
 
	hcd->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]); // 轉換為虛擬地址
	clk = devm_clk_get(&dev->dev, "usb-host");
	usb_clk = devm_clk_get(&dev->dev, "usb-bus-host");

	s3c2410_start_hc(dev, hcd);

	retval = usb_add_hcd(hcd, dev->resource[1].start, 0); //中斷號26
	device_wakeup_enable(hcd->self.controller);
	return 0;
 
 err_ioremap:
	s3c2410_stop_hc(dev);
 
 err_put:
	usb_put_hcd(hcd);
	return retval;
}

4.1.usb_create_hcd

struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
  struct device *dev, char *bus_name)
{
 struct usb_hcd *hcd;
 //申請空間,hcd_priv_size為動態的附加結構長度, 附加在usb_hcd的尾部
 // 這段空間用於主機控制器的私有結構,uhci的私有結構為uhci_hcd,用於描述uhci的屬性
 hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
 if (!hcd) 
 {
  dev_dbg (dev, "hcd alloc failed\n");
  return NULL;
 }
 //連線usb_hcd到pci_device
 dev_set_drvdata(dev, hcd);
 //初始化引用計數器
 kref_init(&hcd->kref);
 //初始化usb匯流排
 usb_bus_init(&hcd->self);
 //連線pci_device到usb_hcd的usb_bus上
 hcd->self.controller = dev;
 //設定usb_bus名稱
 hcd->self.bus_name = bus_name;
 //設定是否使用dma
 hcd->self.uses_dma = (dev->dma_mask != NULL);
 //hcd->rh_timer的註釋為drives root-hub polling
 //字面意思是一個用於記時執行某函式的結構,例如5ms後執行指定函式
 init_timer(&hcd->rh_timer);
 hcd->rh_timer.function = rh_timer_func;
 hcd->rh_timer.data = (unsigned long) hcd;
#ifdef CONFIG_PM
 INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
 //連線hc_driver到usb_hcd上
 hcd->driver = driver;
 //設定裝置名稱
 hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller";
 return hcd;
}

  rh_timer_func這個是root_hub輪詢計時器 ,控制器以輪詢的方式查詢埠變化狀態。 rh_timer_func呼叫usb_hcd_poll_rh_status,呼叫流程:

  ->usb_hcd_poll_rh_status                    //hcd.c
    ->hcd->driver->hub_status_data(hcd, buffer)
    ->usb_hcd_unlink_urb_from_ep(hcd, urb);
    ->usb_hcd_giveback_urb(hcd, urb, 0)
         ->usb_giveback_urb_bh();            //tasklet_hi_schedule(&bh->bh);
            ->__usb_hcd_giveback_urb(urb);
              ->urb->complete(urb);       //hub_irq
                ->hub_irq //hub.c  usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
                  ->kick_hub_wq(hub);
                    ->hub_event //INIT_WORK(&hub->events, hub_event);
                      ->port_event(hub, i);
                        ->hub_port_connect_change
                          ->hub_port_connect
                            ->hub_port_init
                            ->usb_new_device(udev);
                              ->usb_enumerate_device(udev);//開始列舉
                              ->device_add(&udev->dev);//列舉完畢後載入裝置驅動

  device_add函式會出發匯流排的通知鏈傳送通知,最終會呼叫匯流排的match方法。usb裝置和驅動一旦match,則會呼叫驅動的drvwrap.driver.probe方法:

  • 若是裝置則通過driver.c的usb_register_device_driver函式呼叫usb_probe_device方法
  • 若是介面則通過driver.c的usb_register_driver函式呼叫usb_probe_interface方法
  • 假設是U盤接入,則呼叫mass_storage驅動的probe,並在probe中使用usb_alloc_urb分配urb,最後usb_submit_urb提交urb。

4.2.usb_add_hcd 新增hcd到系統中

  • 向usb系統中註冊一條匯流排
    usb_register_bus(&hcd->self))

  • 建立一個USB裝置,作為根hub
    rhdev = usb_alloc_dev(NULL, &hcd->self, 0)
    hcd->self.root_hub = rhdev

  • 申請中斷並註冊中斷函式usb_hub_irq
    usb_hcd_request_irqs

  • 為該匯流排註冊根hub
    register_root_hub(hcd)

    • 獲取根hub的描述資訊
      usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE);
    • 向usb系統中新增了一個usb裝置
      usb_new_device (usb_dev);

相關文章