linux usb 系统 (6)

Linux中EHCI控制器驱动的架构

首 先,让我们来看看linux中是如何来定义这些寄存器的。

PCI系列的EHCI寄存器我们不关心,我们只关心Capability系列的和Controller系列的寄存器。

Host Controller Capability Registers

 

struct ehci_caps {

/* these fields are specified as 8 and 16 bit registers,

* but some hosts can't perform 8 or 16 bit PCI accesses.

*/

u32 hc_capbase;

#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */

#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */

u32 hcs_params; /* HCSPARAMS - offset 0x4 */

#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */

#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */

#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */

#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */

#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */

#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */

#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */

u32 hcc_params; /* HCCPARAMS - offset 0x8 */

#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */

#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */

#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */

#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */

#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/

#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */

u8 portroute [8]; /* nibbles for routing - offset 0xC */

} __attribute__ ((packed));

 

Host Controller Operational Registers

 

struct ehci_regs {

/* USBCMD: offset 0x00 */

u32 command;

/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */

#define CMD_PARK (1<<11) /* enable "park" on async qh */

#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */

#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */

#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */

#define CMD_ASE (1<<5) /* async schedule enable */

#define CMD_PSE (1<<4) /* periodic schedule enable */

/* 3:2 is periodic frame list size */

#define CMD_RESET (1<<1) /* reset HC not bus */

#define CMD_RUN (1<<0) /* start/stop HC */

/* USBSTS: offset 0x04 */

u32 status;

#define STS_ASS (1<<15) /* Async Schedule Status */

#define STS_PSS (1<<14) /* Periodic Schedule Status */

#define STS_RECL (1<<13) /* Reclamation */

#define STS_HALT (1<<12) /* Not running (any reason) */

/* some bits reserved */

/* these STS_* flags are also intr_enable bits (USBINTR) */

#define STS_IAA (1<<5) /* Interrupted on async advance */

#define STS_FATAL (1<<4) /* such as some PCI access errors */

#define STS_FLR (1<<3) /* frame list rolled over */

#define STS_PCD (1<<2) /* port change detect */

#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */

#define STS_INT (1<<0) /* "normal" completion (short, ...) */

/* USBINTR: offset 0x08 */

u32 intr_enable;

/* FRINDEX: offset 0x0C */

u32 frame_index; /* current microframe number */

/* CTRLDSSEGMENT: offset 0x10 */

u32 segment; /* address bits 63:32 if needed */

/* PERIODICLISTBASE: offset 0x14 */

u32 frame_list; /* points to periodic list */

/* ASYNCLISTADDR: offset 0x18 */

u32 async_next; /* address of next async queue head */

u32 reserved [9];

/* CONFIGFLAG: offset 0x40 */

u32 configured_flag;

#define FLAG_CF (1<<0) /* true: we'll support "high speed" */

/* PORTSC: offset 0x44 */

u32 port_status [0]; /* up to N_PORTS */

/* 31:23 reserved */

#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */

#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */

#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */

/* 19:16 for port testing */

#define PORT_LED_OFF (0<<14)

#define PORT_LED_AMBER (1<<14)

#define PORT_LED_GREEN (2<<14)

#define PORT_LED_MASK (3<<14)

#define PORT_OWNER (1<<13) /* true: companion hc owns this port */

#define PORT_POWER (1<<12) /* true: has power (see PPC) */

#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */

/* 11:10 for detecting lowspeed devices (reset vs release ownership) */

/* 9 reserved */

#define PORT_RESET (1<<8) /* reset port */

#define PORT_SUSPEND (1<<7) /* suspend port */

#define PORT_RESUME (1<<6) /* resume it */

#define PORT_OCC (1<<5) /* over current change */

#define PORT_OC (1<<4) /* over current active */

#define PORT_PEC (1<<3) /* port enable change */

#define PORT_PE (1<<2) /* port enable */

#define PORT_CSC (1<<1) /* connect status change */

#define PORT_CONNECT (1<<0) /* device connected */

#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)

} __attribute__ ((packed));

 

上面两个结构体就是linux对EHCI寄存器的定义,我们可以对照<>2.2和2.3节来详细了解各个字段的定义。即使没有spec,代码中的注释和宏也己经能让我们理解个八九成。

Linux要管理ehci,仅有寄存器的定义还不够,下面我们再来看看ehci的定义:

struct ehci_hcd { /* one per controller */

/* glue to PCI and HCD framework */

struct ehci_caps __iomem *caps;

struct ehci_regs __iomem *regs;

struct ehci_dbg_port __iomem *debug;

__u32 hcs_params; /* cached register copy */

spinlock_t lock;

/* async schedule support */

struct ehci_qh *async;

struct ehci_qh *reclaim;

unsigned scanning : 1;

/* periodic schedule support */

#define DEFAULT_I_TDPS 1024 /* some HCs can do less */

unsigned periodic_size;

__hc32 *periodic; /* hw periodic table */

dma_addr_t periodic_dma;

unsigned i_thresh; /* uframes HC might cache */

union ehci_shadow *pshadow; /* mirror hw periodic table */

int next_uframe; /* scan periodic, start here */

unsigned periodic_sched; /* periodic activity count */

/* list of itds completed while clock_frame was still active */

struct list_head cached_itd_list;

unsigned clock_frame;

/* per root hub port */

unsigned long reset_done [EHCI_MAX_ROOT_PORTS];

/* bit vectors (one bit per port) */

unsigned long bus_suspended; /* which ports were

already suspended at the start of a bus suspend */

unsigned long companion_ports; /* which ports are

dedicated to the companion controller */

unsigned long owned_ports; /* which ports are

owned by the companion during a bus suspend */

unsigned long port_c_suspend; /* which ports have

the change-suspend feature turned on */

unsigned long suspended_ports; /* which ports are

suspended */

/* per-HC memory pools (could be per-bus, but ...) */

struct dma_pool *qh_pool; /* qh per active urb */

struct dma_pool *qtd_pool; /* one or more per qh */

struct dma_pool *itd_pool; /* itd per iso urb */

struct dma_pool *sitd_pool; /* sitd per split iso urb */

struct timer_list iaa_watchdog;

struct timer_list watchdog;

unsigned long actions;

unsigned stamp;

unsigned long next_statechange;

u32 command;

/* SILICON QUIRKS */

unsigned no_selective_suspend:1;

unsigned has_fsl_port_bug:1; /* FreeScale */

unsigned big_endian_mmio:1;

unsigned big_endian_desc:1;

unsigned has_amcc_usb23:1;

/* required for usb32 quirk */

#define OHCI_CTRL_HCFS (3 << 6)

#define OHCI_USB_OPER (2 << 6)

#define OHCI_USB_SUSPEND (3 << 6)

#define OHCI_HCCTRL_OFFSET 0x4

#define OHCI_HCCTRL_LEN 0x4

__hc32 *ohci_hcctrl_reg;

u8 sbrn; /* packed release number */

/* irq statistics */

#ifdef EHCI_STATS

struct ehci_stats stats;

# define COUNT(x) do { (x)++; } while (0)

#else

# define COUNT(x) do {} while (0)

#endif

/* debug files */

#ifdef DEBUG

struct dentry *debug_dir;

struct dentry *debug_async;

struct dentry *debug_periodic;

struct dentry *debug_registers;

#endif

#ifdef CONFIG_USB_OTG

/*

* OTG controllers and transceivers need software interaction;

* other external transceivers should be software-transparent

*/

struct otg_transceiver *transceiver;

#endif

};

 

struct ehci_hcd就像是一个大杂汇,把所有与EHCI的东西都放了进来,在内核中,一个ehci_hcd 的定义 (分配了实际的内存)就描述一个硬件上的EHCI控制器。

我们看看ehci_hcd的主要成员:

struct ehci_caps __iomem *caps;

struct ehci_regs __iomem *regs;

struct ehci_dbg_port __iomem *debug;

__u32 hcs_params; /* cached register copy */

__hc32 *periodic;

硬件相关,这些结构体的成员都要指向寄存器的地址。

 

struct ehci_qh *async;

struct ehci_qh *reclaim;

unsigned scanning : 1;

unsigned periodic_size;

__hc32 *periodic;

dma_addr_t periodic_dma;

unsigned i_thresh;

union ehci_shadow *pshadow;

int next_uframe;

unsigned periodic_sched;

管理EHCI的等时调度和异性调度。

 

struct otg_transceiver *transceiver;

如果系统中有OTG,此成员负责OTG的操作和管理。

 

struct dma_pool *qh_pool; /

struct dma_pool *qtd_pool;

struct dma_pool *itd_pool;

struct dma_pool *sitd_pool;

struct timer_list iaa_watchdog;

struct timer_list watchdog;

unsigned long actions;

unsigned stamp;

unsigned long next_statechange;

struct list_head cached_itd_list;

unsigned clock_frame;

EHCI控制过程中所需要用到的操作系统的资源的管理。

 
   

我们用usb_create_hcd (const struct hc_driver *driver, struct device *dev, const char *bus_name)这个函数向内核中增加一个控制器,也就是创建一个usb_hcd结构,并把usb_hcd与 hc_driver 关联接起来。

Struct usb_hcd 定义的是一个抽象的HCD结构,但我们实际的HCD可能是EHCI,也可能是OHCI/UHCI,这两者之间如何转换呢。HCD中有一个成员是hcd_priv,解决问题的关键就是它。

我们先看两个关键的代码:

struct usb_hcd {

......

/* The HC driver's private data is stored at the end of

* this structure.

*/

unsigned long hcd_priv[0]

__attribute__ ((aligned(sizeof(unsigned long))));

};

 

struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,

struct device *dev, const char *bus_name)

{

......

hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);

......

}

 

unsigned long hcd_priv[0] __attribute__ ((aligned(sizeof(unsigned long)))); 这样的语句是GNUC语言特有的用法,定义一个指向本结构体末尾的指针。

usb_create_hcd在给hcd分配内存是会多分配 driver->hcd_priv_size 大小的内存,按HCD的定义,这段多分配的内存正是unsigned long hcd_priv[0]指向的地址。这段多分配的地址正是用来存放ehci_hcd 对象的,在hc_driver中,我们一定要把ehci_hcd的长度赋给hcd_priv_size,如下:

hcd_priv_size = sizeof(struct ehci_hcd),

这样usb_create_hcd 才能正确的创建usb_hcd对象,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);

}

 

linux中struct hc_driver 这个结构体来描述USB主控制器功能的接口,这算是linux对控制器驱动要求提供的基本接口,无论是EHCI还是OHCI/UHCI的驱动,要想和上层协调工作,就得实现这些接口,我们驱动EHCI控制器的工作就是要实现这些接口。

我们先研究一下这些接口:

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_MEMORY 0x0001 /* HC regs use memory (else I/O) */

#define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */

#define HCD_USB11 0x0010 /* USB 1.1 */

#define HCD_USB2 0x0020 /* USB 2.0 */

/* 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, pm_message_t message);

/* called after entering D0 (etc), before resuming the hub */

int (*pci_resume) (struct usb_hcd *hcd);

/* 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);

/* hw synch, freeing endpoint resources that urb_dequeue can't */

void (*endpoint_disable)(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);

int (*disconnect)(struct usb_hcd *);

int (*connect)(struct usb_hcd *, struct usb_device*);

/* 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);

};

 

看看omap 中对这些接口的实现

static const struct hc_driver ehci_omap_hc_driver = {

.description = hcd_name,

.product_desc = "OMAP-EHCI Host Controller",

.hcd_priv_size = sizeof(struct ehci_hcd),

/*

* generic hardware linkage

*/

.irq = ehci_irq,

.flags = HCD_MEMORY | HCD_USB2,

/*

* basic lifecycle operations

*/

.reset = ehci_init,

.start = ehci_run,

.stop = ehci_stop,

.shutdown = ehci_shutdown,

/*

* managing i/o requests and associated device resources

*/

.urb_enqueue = ehci_urb_enqueue,

.urb_dequeue = ehci_urb_dequeue,

.endpoint_disable = ehci_endpoint_disable,

.endpoint_reset = ehci_endpoint_reset,

/*

* scheduling support

*/

.get_frame_number = ehci_get_frame,

/*

* root hub support

*/

.hub_status_data = ehci_hub_status_data,

.hub_control = ehci_hub_control,

.bus_suspend = ehci_bus_suspend,

.bus_resume = ehci_bus_resume,

.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,

};

 

上表中红色标识的部份都是十分复杂的接口,而且实现起来很困难,但幸运的是,EHCI中对寄存器的接口有了标准,所以这我们有许多可以重用的代码,这些代码就在 drivers/usb/host/ehci-hcd.c中,这里己经实现了与ECHI相关的许许多多重要的接口,一般而言,我们可以使用这些实现而不会有任何问题,就像上表中做的一样。

当然,我们首先要传一些参数给 hc_driver,像EHCI寄存器的基址和irq,这些都无法标准化的东西。

如下:

hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);

if (!hcd->regs) {

dev_err(&pdev->dev, "EHCI ioremap failed/n");

ret = -ENOMEM;

goto err_ioremap;

}

/* we know this is the memory we want, no need to ioremap again */

omap->ehci->caps = hcd->regs;

omap->ehci_base = hcd->regs;

res = platform_get_resource(pdev, IORESOURCE_MEM, 1);

omap->uhh_base = ioremap(res->start, resource_size(res));

if (!omap->uhh_base) {

dev_err(&pdev->dev, "UHH ioremap failed/n");

ret = -ENOMEM;

goto err_uhh_ioremap;

}

 

上表中的代码很清晰,从此中我们可以看出标准化的威力,只要给内核传少许几个参数,再实现少许的接口,复杂的EHCI就可以工作起来,不用太花心思去理解我上面讲的调度了,因为大部份复杂的工作早就被先驱者完成了。

下面探讨一下先驱者是如何来尊守EHCI规范的。

Isochronous (High-Speed) Transfer Descriptor (iTD)

clip_image002
上图就是EHCI规范中的Isochronous (High-Speed) Transfer Descriptor (iTD),也就是等时传输数据结构,下面看看在linux kernel中如何定义的.

struct ehci_itd {

/* first part defined by EHCI spec */

__hc32 hw_next; /* see EHCI 3.3.1 */

__hc32 hw_transaction [8]; /* see EHCI 3.3.2 */

#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */

#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */

#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */

#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */

#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)

#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */

#define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE)

__hc32 hw_bufp [7]; /* see EHCI 3.3.3 */

__hc32 hw_bufp_hi [7]; /* Appendix B */

/* the rest is HCD-private */

dma_addr_t itd_dma; /* for this itd */

union ehci_shadow itd_next; /* ptr to periodic q entry */

struct urb *urb;

struct ehci_iso_stream *stream; /* endpoint's queue */

struct list_head itd_list; /* list of stream's itds */

/* any/all hw_transactions here may be used by that urb */

unsigned frame; /* where scheduled */

unsigned pg;

unsigned index[8]; /* in urb->iso_frame_desc */

} __attribute__ ((aligned (32)));

 

struct ehci_itd的定义和EHCI spec是能一一对应的,而且注释中也标明了各成员在EHCI sepc中对应的章节。

__hc32 hw_next;

指向下一个调度数据结构的指针。

 

__hc32 hw_transaction [8];

#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */

#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */

#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */

#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */

#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)

#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */

#define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE)

传输状态和控制字段

设置成1,EHCI会自动传输本itd数据(对此数据进行调度),完成后EHCI置此位为0,此数据以后不参与调度。

有传输错误是置1(overrun/underrun).

检测到 babble时EHCI会将此字段置1。

不能收到有效数据是EHCI置此位为1(超时,CRC错,PID错等)

传输数据长度,OUT传输时表示软件想要传送的数据长度,由软件写入,IN传输表示软件希望接收的数据长度,传输完成EHCI会写入实际的传输长度。

传输完成

使能ITD的宏。

Page Select (PG).Transaction X Offset这两个字段linux中没有使用。

 

__hc32 hw_bufp [7];

hw_bufp [0]的0-6位是设备地址,8-11位是端点号,12-31是buf指针。

 

__hc32 hw_bufp_hi [7];

64位时会用到此位,扩展指针位数

 
   

Split Transaction Isochronous Transfer Descriptor (siTD)

clip_image004

struct ehci_sitd {

/* first part defined by EHCI spec */

__hc32 hw_next;

/* uses bit field macros above - see EHCI 0.95 Table 3-8 */

__hc32 hw_fullspeed_ep; /* EHCI table 3-9 */

__hc32 hw_uframe; /* EHCI table 3-10 */

__hc32 hw_results; /* EHCI table 3-11 */

#define SITD_IOC (1 << 31) /* interrupt on completion */

#define SITD_PAGE (1 << 30) /* buffer 0/1 */

#define SITD_LENGTH(x) (0x3ff & ((x)>>16))

#define SITD_STS_ACTIVE (1 << 7) /* HC may execute this */

#define SITD_STS_ERR (1 << 6) /* error from TT */

#define SITD_STS_DBE (1 << 5) /* data buffer error (in HC) */

#define SITD_STS_BABBLE (1 << 4) /* device was babbling */

#define SITD_STS_XACT (1 << 3) /* illegal IN response */

#define SITD_STS_MMF (1 << 2) /* incomplete split transaction */

#define SITD_STS_STS (1 << 1) /* split transaction state */

#define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, SITD_STS_ACTIVE)

__hc32 hw_buf [2]; /* EHCI table 3-12 */

__hc32 hw_backpointer; /* EHCI table 3-13 */

__hc32 hw_buf_hi [2]; /* Appendix B */

/* the rest is HCD-private */

dma_addr_t sitd_dma;

union ehci_shadow sitd_next; /* ptr to periodic q entry */

struct urb *urb;

struct ehci_iso_stream *stream; /* endpoint's queue */

struct list_head sitd_list; /* list of stream's sitds */

unsigned frame;

unsigned index;

} __attribute__ ((aligned (32)));

 

__hc32 hw_next;

指向下一sitd结构的指针,第1-2位表示传输类型(itd/sitd/qt),5-31位表示指针。

 

__hc32 hw_fullspeed_ep;

31位位表示方向,24-30表示端口数,16-22表示hub地址,8-11位表端点号,0-6位表设备地址。

 

__hc32 hw_uframe;

  

__hc32 hw_results;

#define SITD_IOC (1 << 31) /* interrupt on completion */

#define SITD_PAGE (1 << 30) /* buffer 0/1 */

#define SITD_LENGTH(x) (0x3ff & ((x)>>16))

#define SITD_STS_ACTIVE (1 << 7) /* HC may execute this */

#define SITD_STS_ERR (1 << 6) /* error from TT */

#define SITD_STS_DBE (1 << 5) /* data buffer error (in HC) */

#define SITD_STS_BABBLE (1 << 4) /* device was babbling */

#define SITD_STS_XACT (1 << 3) /* illegal IN response */

#define SITD_STS_MMF (1 << 2) /* incomplete split transaction */

#define SITD_STS_STS (1 << 1) /* split transaction state */

  

__hc32 hw_buf [2];

12-31位表示buf地址,

 

__hc32 hw_backpointer;

  

__hc32 hw_buf_hi [2];

一般置0或指向一个sitd数据。

 

struct ehci_sitd中有hub的信息,当ehci配合hub支持低全速设备时,sitd就派上用场了,hub会把低速数据翻译成高速的sitd数据与ehci通讯。这样ehci在hub的帮助下也可以支持低全速设 备,而不需要兼容控制器(ohci/uhci)的参与了。

Queue Element Transfer Descriptor

clip_image006
linux中的定义

struct ehci_qtd {

/* first part defined by EHCI spec */

__hc32 hw_next; /* see EHCI 3.5.1 */

__hc32 hw_alt_next; /* see EHCI 3.5.2 */

__hc32 hw_token; /* see EHCI 3.5.3 */

#define QTD_TOGGLE (1 << 31) /* data toggle */

#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)

#define QTD_IOC (1 << 15) /* interrupt on complete */

#define QTD_CERR(tok) (((tok)>>10) & 0x3)

#define QTD_PID(tok) (((tok)>>8) & 0x3)

#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */

#define QTD_STS_HALT (1 << 6) /* halted on error */

#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */

#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */

#define QTD_STS_XACT (1 << 3) /* device gave illegal response */

#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */

#define QTD_STS_STS (1 << 1) /* split transaction state */

#define QTD_STS_PING (1 << 0) /* issue PING? */

#define ACTIVE_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_ACTIVE)

#define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT)

#define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS)

__hc32 hw_buf [5]; /* see EHCI 3.5.4 */

__hc32 hw_buf_hi [5]; /* Appendix B */

/* the rest is HCD-private */

dma_addr_t qtd_dma; /* qtd address */

struct list_head qtd_list; /* sw qtd list */

struct urb *urb; /* qtd's urb */

size_t length; /* length of buffer */

} __attribute__ ((aligned (32)));

 

hw_next

指向下一传输单元

 

hw_alt_next

也是指向一个传输单元,当处理当前传输时遇到一个短包此字段会有用。

 

hw_token

#define QTD_TOGGLE (1 << 31) /* data toggle */

#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)

#define QTD_IOC (1 << 15) /* interrupt on complete */

#define QTD_CERR(tok) (((tok)>>10) & 0x3)

#define QTD_PID(tok) (((tok)>>8) & 0x3)

#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */

#define QTD_STS_HALT (1 << 6) /* halted on error */

#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */

#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */

#define QTD_STS_XACT (1 << 3) /* device gave illegal response */

#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */

#define QTD_STS_STS (1 << 1) /* split transaction state */

#define QTD_STS_PING (1 << 0) /* issue PING? */

  

hw_buf [5]

buf指针

 

hw_buf_hi [5];

兼容64位的指针

 

Queue Head

clip_image008

队列头传输结构在linux中的定义

struct ehci_qh {

/* first part defined by EHCI spec */

__hc32 hw_next; /* see EHCI 3.6.1 */

__hc32 hw_info1; /* see EHCI 3.6.2 */

#define QH_HEAD 0x00008000

__hc32 hw_info2; /* see EHCI 3.6.2 */

#define QH_SMASK 0x000000ff

#define QH_CMASK 0x0000ff00

#define QH_HUBADDR 0x007f0000

#define QH_HUBPORT 0x3f800000

#define QH_MULT 0xc0000000

__hc32 hw_current; /* qtd list - see EHCI 3.6.4 */

/* qtd overlay (hardware parts of a struct ehci_qtd) */

__hc32 hw_qtd_next;

__hc32 hw_alt_next;

__hc32 hw_token;

__hc32 hw_buf [5];

__hc32 hw_buf_hi [5];

/* the rest is HCD-private */

dma_addr_t qh_dma; /* address of qh */

union ehci_shadow qh_next; /* ptr to qh; or periodic */

struct list_head qtd_list; /* sw qtd list */

struct ehci_qtd *dummy;

struct ehci_qh *reclaim; /* next to reclaim */

struct ehci_hcd *ehci;

/*

* Do NOT use atomic operations for QH refcounting. On some CPUs

* (PPC7448 for example), atomic operations cannot be performed on

* memory that is cache-inhibited (i.e. being used for DMA).

* Spinlocks are used to protect all QH fields.

*/

u32 refcount;

unsigned stamp;

u8 qh_state;

#define QH_STATE_LINKED 1 /* HC sees this */

#define QH_STATE_UNLINK 2 /* HC may still see this */

#define QH_STATE_IDLE 3 /* HC doesn't see this */

#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */

#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */

/* periodic schedule info */

u8 usecs; /* intr bandwidth */

u8 gap_uf; /* uframes split/csplit gap */

u8 c_usecs; /* ... split completion bw */

u16 tt_usecs; /* tt downstream bandwidth */

unsigned short period; /* polling interval */

unsigned short start; /* where polling starts */

#define NO_FRAME ((unsigned short)~0) /* pick new start */

struct usb_device *dev; /* access to TT */

} __attribute__ ((aligned (32)));

hw_next

队列头水平指向的指针

 

hw_info1

端点的特性

 

hw_current

当前指针

 

hw_qtd_next

下一传输块指针

 

hw_alt_next

  

hw_buf

buf指针

 

hw_buf_hi

兼容64位的指针

 

Periodic Frame Span Traversal Node

clip_image010
FSTN结构用来管理跨越几个主机时间帧( Host-frame boundary)的全速和低速传输,当 HCIVERSION寄存器指示的ehci版本低于0.96时,此结构不能使用,FSTN有点像CPU中的管理中断的栈,保存两个地址,一个指要处理的数据地址,一个指向返回地址。

struct ehci_fstn {

__hc32 hw_next; /* any periodic q entry */

__hc32 hw_prev; /* qh or EHCI_LIST_END */

/* the rest is HCD-private */

dma_addr_t fstn_dma;

union ehci_shadow fstn_next; /* ptr to periodic q entry */

} __attribute__ ((aligned (32)));

 

我们发现,linux中itd/sitd/qtd/qh 的描述除了有ehci规范中要求的字段外,还定义了其它的字段,这些字段与linux中的usb核心系统有密切的关系。

dma_addr_t

一个符合DMA访问要求的地址

 

urb *

指向URB的指针

 

ehci_iso_stream *

管理所有的sitd和itd传输单元中的信息

 

list_head*

链表指针

 
   

上面列举的几个数据结构就是EHCI的核心,软件要做的事就是把上层设备驱动传下来的数转换成一个个的符合itd/sitd/qh/qtd/fstn要求的数据并在内存中放好,利用itd/sitd/qh/qtd/fstn的中定义的指针把这些数据链成链表,这些数据就相当于CPU中指令的集合,CPU的运行要求把指令在内存中放好,然后在PC寄存器的帮助下按的指令执行,这就是程序。而在EHCI中,FRINDEX寄存器和AsyncListAddr寄存器就扮演 了PC寄存器的角色。

 注:转载请注明出处 datangsoc@hotmail.com

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值