USB主机控制器接口标准由OHCI、UHCI、EHCI三种,其中OHCI主要用于嵌入式系统。
1 主机控制器驱动
在Linux内核中,用usb_hcd结构体描述USB主机控制器驱动,它包含USB主机控制器信息、硬件信息和状态以及用于操作主机控制器的hc_driver等。
1 /* include/linux/usb/hcd.h */ 2 struct usb_hcd { 3 4 struct usb_bus self; /* hcd is-a bus */ 5 struct kref kref; /* reference counter */ 6 7 const char *product_desc; /* product/vendor string */ 8 int speed; /* Speed for this roothub. 9 * May be different from 10 * hcd->driver->flags & HCD_MASK 11 */ 12 char irq_descr[24]; /* driver + bus # */ 13 14 struct timer_list rh_timer; /* drives root-hub polling */ 15 struct urb *status_urb; /* the current status urb */ 16 #ifdef CONFIG_PM_RUNTIME 17 struct work_struct wakeup_work; /* for remote wakeup */ 18 #endif 19 20 const struct hc_driver *driver; /* hw-specific hooks */ 21 22 struct usb_phy *phy; 23 24 unsigned long flags; 25 ... 26 27 /* The HC driver‘s private data is stored at the end of 28 * this structure. 29 */ 30 unsigned long hcd_priv[0] 31 __attribute__ ((aligned(sizeof(s64)))); 32 };
usb_hcd结构体中的hc_driver成员很重要,它包括具体的用于操作主机控制器的钩子函数,即“hw-specific hooks”。
1 /* include/linux/usb/hcd.h */ 2 struct hc_driver { 3 const char *description; /* "ehci-hcd" etc */ 4 const char *product_desc; /* product/vendor string */ 5 size_t hcd_priv_size; /* size of private data */ 6 7 /* irq handler */ 8 irqreturn_t (*irq) (struct usb_hcd *hcd); 9 10 int flags; 11 ... 12 /* called to init HCD and root hub */ 13 int (*reset) (struct usb_hcd *hcd); 14 int (*start) (struct usb_hcd *hcd); 15 ... 16 void (*stop) (struct usb_hcd *hcd); 17 18 /* shutdown HCD */ 19 void (*shutdown) (struct usb_hcd *hcd); 20 21 /* return current frame number */ 22 int (*get_frame_number) (struct usb_hcd *hcd); 23 24 /* manage i/o requests, device state */ 25 int (*urb_enqueue)(struct usb_hcd *hcd, 26 struct urb *urb, gfp_t mem_flags); 27 int (*urb_dequeue)(struct usb_hcd *hcd, 28 struct urb *urb, int status); 29 30 ... 31 /* Allocate endpoint resources and add them to a new schedule */ 32 int (*add_endpoint)(struct usb_hcd *, struct usb_device *, 33 struct usb_host_endpoint *); 34 /* Drop an endpoint from a new schedule */ 35 int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, 36 struct usb_host_endpoint *); 37 38 int (*check_bandwidth)(struct usb_hcd *, struct usb_device *); 39 40 void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); 41 /* Returns the hardware-chosen device address */ 42 int (*address_device)(struct usb_hcd *, struct usb_device *udev); 43 /* prepares the hardware to send commands to the device */ 44 int (*enable_device)(struct usb_hcd *, struct usb_device *udev); 45 /* Notifies the HCD after a hub descriptor is fetched. 46 * Will block. 47 */ 48 int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, 49 struct usb_tt *tt, gfp_t mem_flags); 50 int (*reset_device)(struct usb_hcd *, struct usb_device *); 51 /* Notifies the HCD after a device is connected and its 52 * address is set 53 */ 54 int (*update_device)(struct usb_hcd *, struct usb_device *); 55 int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int); 56 /* USB 3.0 Link Power Management */ 57 /* Returns the USB3 hub-encoded value for the U1/U2 timeout. */ 58 int (*enable_usb3_lpm_timeout)(struct usb_hcd *, 59 struct usb_device *, enum usb3_link_state state); 60 /* The xHCI host controller can still fail the command to 61 * disable the LPM timeouts, so this can return an error code. 62 */ 63 int (*disable_usb3_lpm_timeout)(struct usb_hcd *, 64 struct usb_device *, enum usb3_link_state state); 65 int (*find_raw_port_number)(struct usb_hcd *, int); 66 };
上述代码的urb_enqueue()函数很重要,上层通过调用usb_submit_urb()提交一个USB请求后,该函数调用usb_hcd_submit_urb(),并最终调用至usb_hcd的driver成员(hc_driver类型)的urb_enqueue()函数。
Linux内核中,使用如下函数来创建HCD:
函数原形 |
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name); |
返回值 |
成功:返回对应的usb_hcd结构体;失败:返回NULL |
如下函数被用来增加和移除HCD:
函数原形 |
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags); void usb_remove_hcd(struct usb_hcd *hcd); |
2 EHCI主机控制器驱动
EHCI HCD驱动属于HCD驱动的实例,它定义了一个ehci_hcd结构体,通常作为usb_hcd结构体的私有数据(hcd_priv)。
1 /* drivers/usb/host/ehci.h */ 2 struct ehci_hcd { /* one per controller */ 3 /* timing support */ 4 enum ehci_hrtimer_event next_hrtimer_event; 5 unsigned enabled_hrtimer_events; 6 ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS]; 7 struct hrtimer hrtimer; 8 9 ... 10 /* general schedule support */ 11 bool scanning:1; 12 bool need_rescan:1; 13 bool intr_unlinking:1; 14 bool iaa_in_progress:1; 15 bool async_unlinking:1; 16 bool shutdown:1; 17 struct ehci_qh *qh_scan_next; 18 19 /* async schedule support */ 20 struct ehci_qh *async; 21 struct ehci_qh *dummy; /* For AMD quirk use */ 22 struct list_head async_unlink; 23 struct list_head async_idle; 24 unsigned async_unlink_cycle; 25 unsigned async_count; /* async activity count */ 26 27 /* periodic schedule support */ 28 #define DEFAULT_I_TDPS 1024 /* some HCs can do less */ 29 unsigned periodic_size; 30 __hc32 *periodic; /* hw periodic table */ 31 dma_addr_t periodic_dma; 32 struct list_head intr_qh_list; 33 unsigned i_thresh; /* uframes HC might cache */ 34 35 ... 36 37 /* bandwidth usage */ 38 #define EHCI_BANDWIDTH_SIZE 64 39 #define EHCI_BANDWIDTH_FRAMES (EHCI_BANDWIDTH_SIZE >> 3) 40 u8 bandwidth[EHCI_BANDWIDTH_SIZE]; 41 /* us allocated per uframe */ 42 u8 tt_budget[EHCI_BANDWIDTH_SIZE]; 43 /* us budgeted per uframe */ 44 struct list_head tt_list; 45 46 /* platform-specific data -- must come last */ 47 unsigned long priv[0] __aligned(sizeof(s64)); 48 };
使用如下函数可实现usb_hcd和ehci_hcd的相互传输:
函数原形 |
static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd); static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci); |
从usb_hcd得到ehci_hcd只是取得“私有”数据,而从ehci_hcd得到usb_hcd则是通过container_of()从结构体成员获得结构体指针。
使用如下函数可初始化EHCI主机控制器:
函数原形 |
static int ehci_run(struct usb_hcd *hcd); |
如下函数分别用于开启、停止及复位EHCI控制器:
函数原形 |
static int ehci_run (struct usb_hcd *hcd); static void ehci_stop (struct usb_hcd *hcd); static int ehci_reset (struct ehci_hcd *ehci); |
上述函数在drivers/usb/host/ehci-hcd.c文件中被填充给了一个hc_driver结构体的generic的实例ehci_hc_driver。
1 static const struct hc_driver ehci_hc_driver = { 2 ... 3 .reset = ehci_setup, 4 .start = ehci_run, 5 .stop = ehci_stop, 6 .shutdown = ehci_shutdown, 7 ... 8 };
drivers/usb/host/ehci-hcd.c实现了绝大多数EHCI主机驱动的工作,具体的EHCI实例简单地调用:
函数原形 |
void ehci_init_driver(struct hc_driver *drv, const struct ehci_driver_overrides *over); |
初始化hc_driver即可,这个函数会被generic的ehci_hc_driver实例复制给每个具体底层驱动的实例,当然底层驱动可以通过第2个参数,即ehci_driver_overrides重写中间层的reset()、port_power()这2个函数,另外也可以填充一些额外的私有数据。
1 void ehci_init_driver(struct hc_driver *drv, 2 const struct ehci_driver_overrides *over) 3 { 4 /* Copy the generic table to drv and then apply the overrides */ 5 *drv = ehci_hc_driver; 6 7 if (over) { 8 drv->hcd_priv_size += over->extra_priv_size; 9 if (over->reset) 10 drv->reset = over->reset; 11 } 12 }
3 实例:Chipidea USB主机驱动
Chipidea USB主机驱动是EHCI主机控制器驱动的一个实例:位于drivers/usb/chipidea/下。
当Chipidea USB驱动的内核代码drivers/usb/chipidea/core.c中的ci_hdrc_probe()被执行后(匹配上了),它会调用drivers/usb/chipidea/host.c中的ci_hdrc_host_init()函数,该函数完成hc_driver的初始化并赋值一系列与Chipidea平台相关的私有数据。
1 int ci_hdrc_host_init(struct ci_hdrc *ci) { 2 struct ci_role_driver *rdrv; 3 4 if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC)) 5 return -ENXIO; 6 7 rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL); 8 if (!rdrv) 9 return -ENOMEM; 10 11 rdrv->start = host_start; 12 rdrv->stop = host_stop; 13 rdrv->irq = host_irq; 14 rdrv->name = "host"; 15 ci->roles[CI_ROLE_HOST] = rdrv; 16 17 ehci_init_driver(&ci_ehci_hc_driver, NULL); 18 19 return 0; 20 }