USB主机控制器驱动

Posted laoyaodada

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了USB主机控制器驱动相关的知识,希望对你有一定的参考价值。

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 }

 

以上是关于USB主机控制器驱动的主要内容,如果未能解决你的问题,请参考以下文章

Linux:主机USB设备驱动简析

WIN10以上平台实现UCX框架的USB虚拟总线驱动(USB主机控制端驱动)

WIN10以上平台实现UCX框架的USB虚拟总线驱动(USB主机控制端驱动)

USB 控制器初始化失败。未找到一个已支持的主机 USB 驱动程序。如果你已在主机系统中安装了 USB 通信监控

usb3.0驱动在哪里下载

Linux usb子系统 _写一个usb鼠标驱动