一直都是使用Usb 驱动程序,从来没有好好研读过。之前项目中碰到usb相关的也是usb register配置一下就好了。
至于Usb驱动如何工作,让我们慢慢来揭开它神秘的面纱。
对usb ehci 驱动的基本框架理解得益与understanding linux usb ehci device driver。这里还是借用这张经典的usb驱动框图,明确下EHCI在整个USB驱动中所处的地位。
EHCI驱动: Linux usb host controller driver(HCD )
usb host controller驱动,负责将usb_core发来的URB传输请求转化成HC可识别的格式并启动HC传输,直至完成传输. 整个usb subsystem只有该驱动直接操作硬件寄存器.该层也支持多种不同的host controller driver,比如UHCI,OHCI等.
在整个HCD的解读之前,需要了解ehci-specification-for-usb.pdf.这里跳过规范相关说明,用到时会加以引用。或请认真阅读understanding linux usb ehci device driver(1),是对EHCI规范的部分解读。
kernel/linux-3.10.y/drivers/usb$ ls -al
drwxr-xr-x 20 karry karry 4096 9月 7 14:46 .
drwxr-xr-x 117 karry karry 4096 9月 7 14:46 ..
drwxr-xr-x 2 karry karry 4096 9月 7 14:45 core
drwxr-xr-x 3 karry karry 4096 9月 7 14:57 gadget
drwxr-xr-x 3 karry karry 4096 9月 7 14:45 host
drwxr-xr-x 2 karry karry 4096 9月 7 14:46 storage
-rw-r--r-- 1 karry karry 2462 10月19 2015 usb-common.c
-rw-rw-r-- 1 karry karry 3372 10月19 2015 usb-common.o
-rw-rw-r-- 1 karry karry 33959 10月19 2015 .usb-common.o.cmd
usb host controller driver 只需关注host目录,结合core目录即可。
从数据结构来看,需要实现usbcore中struct hc_driver定义的接口。
struct hc_driver {
const char *description; /* "ehci-hcd" etc */
const char *product_desc; /* product/vendor string */
size_t hcd_priv_size; /* size of private data */
/* irq handler */
irqreturn_t (*irq) (struct usb_hcd *hcd);
int flags;
#define HCD_MEMORY0x0001/* HC regs use memory (else I/O) */
#define HCD_LOCAL_MEM0x0002/* HC needs local memory */
#define HCD_SHARED0x0004/* Two (or more) usb_hcds share HW */
#define HCD_USB110x0010/* USB 1.1 */
#define HCD_USB20x0020/* USB 2.0 */
#define HCD_USB30x0040/* USB 3.0 */
#define HCD_MASK0x0070
/* called to init HCD and root hub */
int (*reset) (struct usb_hcd *hcd);
int (*start) (struct usb_hcd *hcd);
/* NOTE: these suspend/resume calls relate to the HC as
* a whole, not just the root hub; they're for PCI bus glue.
*/
/* called after suspending the hub, before entering D3 etc */
int (*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);
/* called after entering D0 (etc), before resuming the hub */
int (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
/* cleanly make HCD stop writing memory and doing I/O */
void (*stop) (struct usb_hcd *hcd);
/* shutdown HCD */
void (*shutdown) (struct usb_hcd *hcd);
/* return current frame number */
int (*get_frame_number) (struct usb_hcd *hcd);
/* manage i/o requests, device state */
int (*urb_enqueue)(struct usb_hcd *hcd,
struct urb *urb, gfp_t mem_flags);
int (*urb_dequeue)(struct usb_hcd *hcd,
struct urb *urb, int status);
/*
* (optional) these hooks allow an HCD to override the default DMA
* mapping and unmapping routines. In general, they shouldn't be
* necessary unless the host controller has special DMA requirements,
* such as alignment contraints. If these are not specified, the
* general usb_hcd_(un)?map_urb_for_dma functions will be used instead
* (and it may be a good idea to call these functions in your HCD
* implementation)
*/
int (*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags);
void (*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb);
/* hw synch, freeing endpoint resources that urb_dequeue can't */
void (*endpoint_disable)(struct usb_hcd *hcd,
struct usb_host_endpoint *ep);
/* (optional) reset any endpoint state such as sequence number
and current window */
void (*endpoint_reset)(struct usb_hcd *hcd,
struct usb_host_endpoint *ep);
/* root hub support */
int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
int (*hub_control) (struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
int (*bus_suspend)(struct usb_hcd *);
int (*bus_resume)(struct usb_hcd *);
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
/* force handover of high-speed port to full-speed companion */
void (*relinquish_port)(struct usb_hcd *, int);
/* has a port been handed over to a companion? */
int (*port_handed_over)(struct usb_hcd *, int);
/* CLEAR_TT_BUFFER completion callback */
void (*clear_tt_buffer_complete)(struct usb_hcd *,
struct usb_host_endpoint *);
/* xHCI specific functions */
/* Called by usb_alloc_dev to alloc HC device structures */
int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
/* Called by usb_disconnect to free HC device structures */
void (*free_dev)(struct usb_hcd *, struct usb_device *);
/* Change a group of bulk endpoints to support multiple stream IDs */
int (*alloc_streams)(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
unsigned int num_streams, gfp_t mem_flags);
/* Reverts a group of bulk endpoints back to not using stream IDs.
* Can fail if we run out of memory.
*/
int (*free_streams)(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
gfp_t mem_flags);
/* Bandwidth computation functions */
/* Note that add_endpoint() can only be called once per endpoint before
* check_bandwidth() or reset_bandwidth() must be called.
* drop_endpoint() can only be called once per endpoint also.
* A call to xhci_drop_endpoint() followed by a call to
* xhci_add_endpoint() will add the endpoint to the schedule with
* possibly new parameters denoted by a different endpoint descriptor
* in usb_host_endpoint. A call to xhci_add_endpoint() followed by a
* call to xhci_drop_endpoint() is not allowed.
*/
/* Allocate endpoint resources and add them to a new schedule */
int (*add_endpoint)(struct usb_hcd *, struct usb_device *,
struct usb_host_endpoint *);
/* Drop an endpoint from a new schedule */
int (*drop_endpoint)(struct usb_hcd *, struct usb_device *,
struct usb_host_endpoint *);
/* Check that a new hardware configuration, set using
* endpoint_enable and endpoint_disable, does not exceed bus
* bandwidth. This must be called before any set configuration
* or set interface requests are sent to the device.
*/
int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
/* Reset the device schedule to the last known good schedule,
* which was set from a previous successful call to
* check_bandwidth(). This reverts any add_endpoint() and
* drop_endpoint() calls since that last successful call.
* Used for when a check_bandwidth() call fails due to resource
* or bandwidth constraints.
*/
void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
/* Returns the hardware-chosen device address */
int (*address_device)(struct usb_hcd *, struct usb_device *udev);
/* Notifies the HCD after a hub descriptor is fetched.
* Will block.
*/
int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev,
struct usb_tt *tt, gfp_t mem_flags);
int (*reset_device)(struct usb_hcd *, struct usb_device *);
/* Notifies the HCD after a device is connected and its
* address is set
*/
int (*update_device)(struct usb_hcd *, struct usb_device *);
int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int);
/* USB 3.0 Link Power Management */
/* Returns the USB3 hub-encoded value for the U1/U2 timeout. */
int (*enable_usb3_lpm_timeout)(struct usb_hcd *,
struct usb_device *, enum usb3_link_state state);
/* The xHCI host controller can still fail the command to
* disable the LPM timeouts, so this can return an error code.
*/
int (*disable_usb3_lpm_timeout)(struct usb_hcd *,
struct usb_device *, enum usb3_link_state state);
int (*find_raw_port_number)(struct usb_hcd *, int);
}
struct ehci_hcd 定义了ehci host controller driver的数据结构部分,,struct hc_driver则定义了基于struct usb_hcd的接口,该接口中的所有函数类型第一个参数都是struct usb_hcd*;可以通过hcd_to_ehci()从struct usb_hcd *获得对应的struct ehci_hcd *, 还可以通过ehci_to_hcd()从struct ehci_hcd *获得struct usb_hcd*.
/* convert between an HCD pointer and the corresponding EHCI_HCD */
static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
{
return (struct ehci_hcd *) (hcd->hcd_priv);
}
static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
{
return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
}
Ehci device driver的主要工作就是实现struct hc_driver中定义的主要接口,一般来说以下接口是必须要实现的:
irqreturn_t(*irq)(struct usb_hcd *hcd);//ehci hcd的irq handler
/* called to init HCD and root hub */
int(*reset)(struct usb_hcd *hcd);
int(*start)(struct usb_hcd *hcd); //启动HC
/* cleanly make HCD stop writing memory and doing I/O */
void (*stop)(struct usb_hcd *hcd); //停止HC
int (*get_frame_number)(struct usb_hcd *hcd);//获得当前的frame号
/*根据hcd和ep的信息,安排urb的schedule到EHCI,该URB的传输完成后,会调用urb->complete ()通知usbcore*/
int(*urb_enqueue)(struct usb_hcd *hcd,struct urb *urb, gfp_t mem_flags);
/*该接口用于用户取消已经enqueue的urb,主要为usbcore的unlink_urb()所调用*/
int(*urb_dequeue)(struct usb_hcd *hcd,struct urb *urb, int status);
/*disable the ep , 并且释放该ep上资源(unlink该ep上的qh)*/
void (*endpoint_disable)(struct usb_hcd *hcd,struct usb_host_endpoint *ep);
/*获取root hub port的状态信息*/
int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
/*操作root hub以及port*/
int (*hub_control)(struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
1. HCD加载( 分析海思3536平台 hiusb-ehci.c )
static struct platform_driver hiusb_ehci_hcd_driver = {
.probe = hiusb_ehci_hcd_drv_probe,
.remove = hiusb_ehci_hcd_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "hiusb-ehci",
.owner = THIS_MODULE,
.pm = HIUSB_EHCI_PMOPS,
}
};
static struct resource hiusb_ehci_res[] = {
[0] = {
.start = CONFIG_HIUSB_EHCI_IOBASE,
.end = CONFIG_HIUSB_EHCI_IOBASE
+ CONFIG_HIUSB_EHCI_IOSIZE - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = CONFIG_HIUSB_EHCI_IRQNUM,
.end = CONFIG_HIUSB_EHCI_IRQNUM,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device hiusb_ehci_platdev = {
.name = "hiusb-ehci",
.id = 0,
.dev = {
.platform_data = NULL,
.dma_mask = &usb_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.release = usb_ehci_platdev_release,
},
.num_resources = ARRAY_SIZE(hiusb_ehci_res),
.resource = hiusb_ehci_res,
};
再请看:Ehci-hcd.c
module_init(ehci_hcd_init);==>
static int __init ehci_hcd_init(void)==>
1. retval = platform_device_register(&hiusb_ehci_platdev);
pr_info("%s: " DRIVER_DESC "\n", hcd_name); //参见内核打印输出;
内核打印:
ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
before uhci_hcd and ohci_hcd, not after
hiusb-ehci hiusb-ehci.0: HIUSB EHCI
hiusb-ehci hiusb-ehci.0: new USB bus registered, assigned bus number 1
hiusb-ehci hiusb-ehci.0: irq 53, io mem 0x10040000
hiusb-ehci hiusb-ehci.0: USB 0.0 started, EHCI 1.00
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 2 ports detected
......2. retval = platform_driver_register(&PLATFORM_DRIVER);
#definePLATFORM_DRIVER hiusb_ehci_hcd_driver
这样系统在检测到usb host controller时会调用
static int hiusb_ehci_hcd_drv_probe(struct platform_device *pdev)==》
1. hcd = usb_create_hcd(&hiusb_ehci_hc_driver, &pdev->dev, "hiusb-ehci");//创建usb_hcd
2. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name);
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); //获取IO基地址;
3. hiusb_start_hcd(); //配置ustctrl和usb clk等。
4. ehci = hcd_to_ehci(hcd);
//初始化ehci寄存器基地址caps,regs
5. ehci->caps = hcd->regs;//capability Registers
6. ehci->regs = hcd->regs +HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase));//Operational Registers,之后hc_driver 很多操作的实现也是读写该寄存器。
4. /* cache this readonly data; minimize chip reads */ //将ehci的Capability Parameters读入到ehci->hcs_params缓冲起来
ehci->hcs_params = readl(&ehci->caps->hcs_params);
5. ret =usb_add_hcd(hcd, pdev->resource[1].start,IRQF_DISABLED | IRQF_SHARED);
6. platform_set_drvdata(pdev, hcd);
Capability Registers
Operational Registers
2.ehci接口实现
2.1 .reset= hiusb_ehci_setup 接口实现
usbcore 的API ---usb_add_hcd()会通过hcd->driver->reset(hcd)来调用.
hiusb_ehci_hcd_drv_probe()函数会调用 usb_add_hcd(). //见上面分析
static int hiusb_ehci_hcd_drv_probe(struct platform_device *pdev)调用:
ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED | IRQF_SHARED);调用:
1.if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {}
2. retval = hcd->driver->start(hcd);
下面分析hiusb_ehci_setup
hiusb_ehci_setup(struct usb_hcd *hcd)=》
ehci_init(hcd);=》//调用 ehci_init() 初始化ehci的数据结构:
1.spin_lock_init(&ehci->lock);
2. hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);//初始化watchdog timer,主要用于发现和处理irq lost的情况
3.hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
4.ehci->periodic_size = DEFAULT_I_TDPS; //1024
5 .ehci_mem_init(ehci, GFP_KERNEL)调用ehci_mem_init()分配并初始化HC schedule所需的数据结构,主要有
.预先分配一定数量的qtd,qh,itd以及sitd到ehci->qtd_pool, ehci->qh_pool, ehci->itd_pool和ehci->sitd_pool中作为cache;
/* QTDs for control/bulk/intr transfers */
ehci->qtd_pool = dma_pool_create ("ehci_qtd",...)
/* QHs for control/bulk/intr transfers */
ehci->qh_pool = dma_pool_create ("ehci_qh",...)
ehci->async = ehci_qh_alloc (ehci, flags);==>
// .从ehci->qh_pool分配一个qh, 并使得ehci->async指向该qh,这个qh用作asynchronous schedule的reclamation list head (H bit为1),实现Empty Asynchronous //Schedule Detection;
a.qh = kzalloc(sizeof *qh, GFP_ATOMIC);
b.qh->hw = (struct ehci_qh_hw *)dma_pool_alloc(ehci->qh_pool, flags, &dma);
c.qh->qh_dma = dma;
d.INIT_LIST_HEAD (&qh->qtd_list);
/* dummy td enables safe urb queuing */
e.qh->dummy = ehci_qtd_alloc(ehci, flags);==>
e.1 qtd = dma_pool_alloc (ehci->qtd_pool, flags, &dma);
e.2 ehci_qtd_init(ehci, qtd, dma);
/* ITD for high speed ISO transfers */
ehci->itd_pool = dma_pool_create ("ehci_itd",...)
/* SITD for full/low speed split ISO transfers */
ehci->sitd_pool = dma_pool_create ("ehci_sitd",...)
/* Hardware periodic table */// .调用dma_alloc_coherent()分配Hardware periodic table,并令ehci->periodic指向其,ehci->periodic_size设为 1024,ehci->periodic_dma返回表格对应的物理地址;初始化表格中每项初值为EHCI_LIST_END,即不包含 periodic schedule data structure;
ehci->periodic = (__le32 *)
dma_alloc_coherent (ehci_to_hcd(ehci)->self.controller,
ehci->periodic_size * sizeof(__le32),
&ehci->periodic_dma, 0);
ehci->periodic[i] = EHCI_LIST_END(ehci);
/* software shadow of hardware table */// .分配software shadow of hardware table, 令ehci->ehci->pshadow指向其,并初始化表格内容为全0;
ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags);
6. /* controllers may cache some of the periodic schedule ... */// 根据 ehci->caps->hcc_params 指向的参数初始化 ehci->i_thresh, 该参数代表了 HC 会预取多 少个 micro-frame 的 periodic schedule data structure;ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
7. 初始化asynchronous schedule data structure:
ehci->async->qh_next.qh = NULL;
hw = ehci->async->hw;
hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
#if defined(CONFIG_PPC_PS3)
hw->hw_info1 |= cpu_to_hc32(ehci, QH_INACTIVATE);
#endif
hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
hw->hw_qtd_next = EHCI_LIST_END(ehci);
ehci->async->qh_state = QH_STATE_LINKED;
hw->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
8. 依据irq_thresh, park mode, periodic size等信息构造ehci->command缺省值;
......
ehci->command = temp;
2.2 .start= ehci_run, 接口实现
usbcore 的API ---usb_add_hcd()在分配完root hub usbdevice后会通过hcd->driver->start(hcd)来调用.
/* start HC running; it's halted, ehci_init() has been run (once) *///写入periodic schedule list地址以及asynchronous schedule list地址到HC的相应寄存器:
Host Controller Operational Registers的定义参见:Table2.8
static int ehci_run (struct usb_hcd *hcd)==》
1. ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list);
/* PERIODICLISTBASE: offset 0x14 */
ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
/* ASYNCLISTADDR: offset 0x18 */
2. hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
if (HCC_64BIT_ADDR(hcc_params)) {
ehci_writel(ehci, 0, &ehci->regs->segment);
} //对64bit模式(HC作为bus master生成64bit地址)的处理:
3. 启动HC// Philips, Intel, and maybe others need CMD_RUN before the
// root hub will detect new devices (why?); NEC doesn't
ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
ehci->command |= CMD_RUN;
ehci_writel(ehci, ehci->command, &ehci->regs->command);
4. ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
2.3 .stop= ehci_stop, 接口实现1) hiusb_ehci_hcd_drv_remove()调用usb_remove_hcd()
2) usb_remove_hcd() 会通过hcd->driver->stop(hcd) 调用;
static int hiusb_ehci_hcd_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
hiusb_stop_hcd();
platform_set_drvdata(pdev, NULL);
return 0;
}
// Called when the ehci_hcd module is removed.
static void ehci_stop (struct usb_hcd *hcd)==》
1. 强制HC从running state进入idle状态,并复位HC chip, disable所有中断;
ehci_quiesce(ehci);
ehci_silence_controller(ehci);
ehci_reset (ehci);
/*
* Idle the controller (turn off the schedules).
* Must be called with interrupts enabled and the lock not held.
*/
static void ehci_quiesce (struct ehci_hcd *ehci)
{
u32 temp;
if (ehci->rh_state != EHCI_RH_RUNNING)
return;
/* wait for any schedule enables/disables to take effect */
temp = (ehci->command << 10) & (STS_ASS | STS_PSS);
handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125);
/* then disable anything that's still active */
spin_lock_irq(&ehci->lock);
ehci->command &= ~(CMD_ASE | CMD_PSE);
ehci_writel(ehci, ehci->command, &ehci->regs->command);
spin_unlock_irq(&ehci->lock);
/* hardware can take 16 microframes to turn off ... */
handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125);
}
/*
* Halt HC, turn off all ports, and let the BIOS use the companion controllers.
* Must be called with interrupts enabled and the lock not held.
*/
static void ehci_silence_controller(struct ehci_hcd *ehci)//将root hub的port 控制权交给companion HC;
{
ehci_halt(ehci);
spin_lock_irq(&ehci->lock);
ehci->rh_state = EHCI_RH_HALTED;
ehci_turn_off_all_ports(ehci);
/* make BIOS/etc use companion controller during reboot */
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
/* unblock posted writes */
ehci_readl(ehci, &ehci->regs->configured_flag);
spin_unlock_irq(&ehci->lock);
}
2.ehci->enabled_hrtimer_events = 0;,删除watchdog timer;
hrtimer_cancel(&ehci->hrtimer);
3.ehci_mem_cleanup (ehci);
2.4 .get_frame_number= ehci_get_frame,接口实现
hcd_get_frame_number():hcd.c, 该函数为struct usb_operations定义的接口;返回当前usb bus的frame number;
//未完待续