前面簡單聊了一下USB驅動的整體框架《USB驅動框架(一)》,接下來看一下主機控制器驅動框架。
網絡圖片
USB核心(USBD)是整個USB驅動的核心部分,一方面USBD對從USB主機控制器接收到的數據進行處理,然後傳遞給上層的設備端驅動;同時也接收來自上層的非USB格式數據流,進行相應的數據處理後傳遞給USB主機控制器驅動。所以它起了一個承上啟下的作用。
//include/linux/usb/hcd.hstruct usb_hcd {
struct usb_bus self; /* hcd是一個總線 */ struct kref kref; /* 引用計數 */
const char *product_desc; /* product/vendor字符創 */ int speed; /* roothub速率*/ char irq_descr[24]; /* driver + bus # */
struct timer_list rh_timer; /* 驅動root-hub輪詢 */ struct urb *status_urb; /* 當前狀態urb */#ifdef CONFIG_PM struct work_struct wakeup_work; /* 遠程喚醒 */#endif
//操作主機控制器的回調函數 const struct hc_driver *driver;
//OTG或外掛USB控制器 struct usb_phy *usb_phy; struct usb_phy_roothub *phy_roothub;
//狀態標誌 unsigned long flags; //省略宏定義....
//HCD註冊或移除時設置的標誌 unsigned rh_registered:1;/* is root hub registered? */ unsigned rh_pollable:1; /* may we poll the root hub? */ unsigned msix_enabled:1; /* driver has MSI-X enabled? */ unsigned msi_enabled:1; /* driver has MSI enabled? */
unsigned skip_phy_initialization:1; //省略..
//中斷號及內存資源 unsigned int irq; /* irq allocated */ void __iomem *regs; /* device memory/io */ resource_size_t rsrc_start; /* memory/io resource start */ resource_size_t rsrc_len; /* memory/io resource length */ unsigned power_budget; /* in mA, 0 = no limit */
struct giveback_urb_bh high_prio_bh; struct giveback_urb_bh low_prio_bh;
//互斥鎖 struct mutex *address0_mutex; struct mutex *bandwidth_mutex; struct usb_hcd *shared_hcd; struct usb_hcd *primary_hcd;
//緩衝區池#define HCD_BUFFER_POOLS 4 struct dma_pool *pool[HCD_BUFFER_POOLS];
int state; //省略宏定義....
//HCD私有數據存儲在該結構體的末尾 unsigned long hcd_priv[0] __attribute__ ((aligned(sizeof(s64))));};//include/linux/usb/hcd.hstruct hc_driver { const char *description; /* "ehci-hcd" etc */ const char *product_desc; /* product/vendor 字符串 */ size_t hcd_priv_size; /* 私有數據大小 */
/* 中斷處理函數 */ 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_SHARED 0x0004 /* Two (or more) usb_hcds share HW */#define HCD_USB11 0x0010 /* USB 1.1 */#define HCD_USB2 0x0020 /* USB 2.0 */#define HCD_USB25 0x0030 /* Wireless USB 1.0 (USB 2.5)*/#define HCD_USB3 0x0040 /* USB 3.0 */#define HCD_USB31 0x0050 /* USB 3.1 */#define HCD_USB32 0x0060 /* USB 3.2 */#define HCD_MASK 0x0070#define HCD_BH 0x0100 /* URB complete in BH context */
//初始化HCD和roothub時調用 int (*reset) (struct usb_hcd *hcd); int (*start) (struct usb_hcd *hcd);
//掛起hub時調用 int (*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);
//恢復hub調用 int (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
//停止HCD void (*stop) (struct usb_hcd *hcd);
//關閉HCD void (*shutdown) (struct usb_hcd *hcd);
//返回當前幀號 int (*get_frame_number) (struct usb_hcd *hcd);
//管理IO請求,設備狀態 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);
//重寫HCD默認的DMA映射和取消映射,非必須情況下,不要實現 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);
//硬體同步,釋放urb出列無法釋放的端點資源 void (*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
//復位端點 void (*endpoint_reset)(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
/* root hub支持 */ 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); unsigned long (*get_resuming_ports)(struct usb_hcd *);
//將高速埠切換為全速 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完成回調 */ void (*clear_tt_buffer_complete)(struct usb_hcd *, struct usb_host_endpoint *);
/* xHCI專用函數 */ //使用usb_alloc_dev分配HC設備結構體時回調 int (*alloc_dev)(struct usb_hcd *, struct usb_device *); //使用usb_disconnect釋放HC設備結構體時回調 void (*free_dev)(struct usb_hcd *, struct usb_device *); //更改一組批量端點以支持多個流id 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); //將一組批量端點恢復為不使用流id int (*free_streams)(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags);
/* 帶寬計算函數 */ //添加一個端點 int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); //刪除端點 int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *); //檢查一個新的硬體配置,必須在設置配置或設置接口請求前調用 int (*check_bandwidth)(struct usb_hcd *, struct usb_device *); //重置設備調度到最後已知的良好調度 void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); //返回設備地址 int (*address_device)(struct usb_hcd *, struct usb_device *udev); //準備硬體向設備發送命令 int (*enable_device)(struct usb_hcd *, struct usb_device *udev);
//hub描述符獲取到後通知HCD 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 *);
//設備的地址設置之後通知HCD int (*update_device)(struct usb_hcd *, struct usb_device *); int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int);
/* USB 3.0 線電源管理 */ int (*enable_usb3_lpm_timeout)(struct usb_hcd *, struct usb_device *, enum usb3_link_state state); 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); int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);};調用usb_submit_urb()提交一個USB請求之後,該函數調用usb_hcd_submit_urb() , 並最終調用usb_hcd的driver成員(hc_driver類型)的urb_enqueue()函數。
//drivers/usb/host/echi.hstruct ehci_hcd { /* one per controller */ /* 時序支持 */ enum ehci_hrtimer_event next_hrtimer_event; unsigned enabled_hrtimer_events; ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS]; struct hrtimer hrtimer;
int PSS_poll_count; int ASS_poll_count; int died_poll_count;
/* 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; /* 緩存寄存器拷貝 */ spinlock_t lock; enum ehci_rh_state rh_state;
/* 普通schedule支持*/ bool scanning:1; bool need_rescan:1; bool intr_unlinking:1; bool iaa_in_progress:1; bool async_unlinking:1; bool shutdown:1; struct ehci_qh *qh_scan_next;
/* 異步schedule支持 */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct list_head async_unlink; struct list_head async_idle; unsigned async_unlink_cycle; unsigned async_count; /* async activity count */ __hc32 old_current; /* Test for QH becoming */ __hc32 old_token; /* inactive during unlink */
/* 周期性schedule支持 */#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ unsigned periodic_size; __hc32 *periodic; /* hw periodic table */ dma_addr_t periodic_dma; struct list_head intr_qh_list; unsigned i_thresh; /* uframes HC might cache */
union ehci_shadow *pshadow; /* mirror hw periodic table */ struct list_head intr_unlink_wait; struct list_head intr_unlink; unsigned intr_unlink_wait_cycle; unsigned intr_unlink_cycle; unsigned now_frame; /* frame from HC hardware */ unsigned last_iso_frame; /* last frame scanned for iso */ unsigned intr_count; /* intr activity count */ unsigned isoc_count; /* isoc activity count */ unsigned periodic_count; /* periodic activity count */ unsigned uframe_periodic_max; /* max periodic time per uframe */ //省略 /* 每個root hub口 */ unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
/* bit vectors (每位代表一個port) */ unsigned long bus_suspended; /* 在總線開始時已經被掛起的port */ 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; /* 被掛起的port */ unsigned long resuming_ports; /* 恢復的port */
/* 每個主機控制器內存池 */ 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 */
unsigned random_frame; unsigned long next_statechange; ktime_t last_periodic_enable; u32 command;
//省略 __hc32 *ohci_hcctrl_reg; unsigned has_hostpc:1; unsigned has_tdi_phy_lpm:1; unsigned has_ppcd:1; /* support per-port change bits */ u8 sbrn; /* packed release number */
/* irq statistics */#ifdef EHCI_STATS struct ehci_stats stats;# define INCR(x) ((x)++)#else# define INCR(x) do {} while (0)#endif
/* debug文件 */#ifdef CONFIG_DYNAMIC_DEBUG struct dentry *debug_dir;#endif
/* 帶寬使用 */#define EHCI_BANDWIDTH_SIZE 64#define EHCI_BANDWIDTH_FRAMES (EHCI_BANDWIDTH_SIZE >> 3) u8 bandwidth[EHCI_BANDWIDTH_SIZE]; /* us allocated per uframe */ u8 tt_budget[EHCI_BANDWIDTH_SIZE]; /* us budgeted per uframe */ struct list_head tt_list;
//平臺數據 unsigned long priv[0] __aligned(sizeof(s64));};ECHI HCD驅動屬於HCD驅動的實例,用ehci_hcd結構體來表示,它通常會作為usb_hcd結構體的私有數據(hcd_priv) 。
//創建和初始化一個HCD結構體struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name)
//註冊HCD結構體 int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)//關閉HCD,斷開roothub連接 void usb_remove_hcd(struct usb_hcd *hcd)
/**********************EHCI********************************///初始化EHCI主機控制器static int ehci_init(struct usb_hcd *hcd)//開啟EHCI主機控制器static int ehci_run (struct usb_hcd *hcd)//停止EHCI主機控制器static void ehci_stop (struct usb_hcd *hcd)//復位EHCI主機控制器int ehci_reset(struct ehci_hcd *ehci)//設置EHCI主機控制器int ehci_setup(struct usb_hcd *hcd)//usb_hcd轉echi_hcd,內部實現就是從獲取usb_hcd的私有數據static inline struct ehci_hcd *hcd_to_ehci(struct usb_hcd *hcd)//echi_hcd轉usb_hcdstatic inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci)//EHCI驅動初始化void ehci_init_driver(struct hc_driver *drv, const struct ehci_driver_overrides *over)
/***********************XHCI**********************************///XHCI驅動初始化void xhci_init_driver(struct hc_driver *drv, const struct xhci_driver_overrides *over)drivers/usb/host/ehci-hcd.c 中實現了絕大多數EHCI主機驅動的工作,具體的驅動只要簡單調用ehci_init_driver()即可。看一下該函數具體實現:
//drivers/usb/host/ehci-hcd.cvoid ehci_init_driver(struct hc_driver *drv, const struct ehci_driver_overrides *over){ //拷貝通用hc_driver到drv,並重寫部分函數 *drv = ehci_hc_driver;
if (over) { drv->hcd_priv_size += over->extra_priv_size; if (over->reset) drv->reset = over->reset; if (over->port_power) drv->port_power = over->port_power; }}上面的函數就是初始化hc_driver, 這個函數會把通用的hc_driver實例ehci_hc_driver複製給每個具體底層驅動的實例,而第二個參數是重寫hc_driver的reset()、 port_power()兩個函數,另外可以填充一些額外的私有數據。
//ehci-hcd.c中實現的通用hc_driverstatic const struct hc_driver ehci_hc_driver = { .description = hcd_name, .product_desc = "EHCI Host Controller", .hcd_priv_size = sizeof(struct ehci_hcd),
/* * generic hardware linkage */ .irq = ehci_irq, .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
/* * basic lifecycle operations */ .reset = ehci_setup, .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, .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
/* * 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, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, .get_resuming_ports = ehci_get_resuming_ports,
/* * device support */ .free_dev = ehci_remove_device,};上面是Linux已經實現好的通用ehci接口操作函數,基本都是通用的,所以不需要自己再實現。
xhci主機控制器的實現方式和ehci基本一樣,在/drivers/usb/host/xhci.c中實現XHCI主機驅動的工作。具體的驅動只要簡單調用xhci_init_driver()即可。
長按識別圖中二維碼關注