USB驅動框架(二)

2021-02-14 嵌入式軟體開發交流




    前面簡單聊了一下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()即可。




長按識別圖中二維碼關注

相關焦點

  • Linux USB-Gadget 驅動框架
    Linux-USB Gadget 驅動框架(以下簡稱 Gadget)實現了USB 協議定義的設備端的軟體功能。相對於 Linux USB 主機端(Host) 驅動而言, Gadget 驅動出現較晚,它出現在2.4.23 以後。
  • Linux USB總線驅動框架分析
    當USB總線驅動程序識別出設備後,會為其找到該USB設備驅動程序,如鍵盤,滑鼠,U盤。USB通信過程均為主從結構,USB主機發起通信請求,設備進行數據回復,USB設備不具備主動向主機通信的能力。2、USB總線驅動框架如上,我們大致了解了USB的簡單通信過程,那麼什麼是USB總線驅動程序?什麼是USB設備驅動呢?
  • 一文搞懂 USB 設備端驅動框架
    USB 設備驅動 2.1 gadget 驅動框架拆解1我們將USB 設備端驅動拆解一下,其驅動框架如下:usb 驅動框架之所以複雜,除了需要研究各種複雜的協議,還融合了各種驅動,對於初學者來說,理解起來有點困難。事實上,光是legacy這裡也包含其他驅動,比如webcam裡有大名鼎鼎的 v4l2 驅動框架。所以當我學習USB驅動框架的時候,一定要抓大放小,【把握主要脈絡,忽略細節】。
  • USB主機控制器驅動分析
    前面我們介紹了USB的基礎知識和USB驅動框架
  • 關於USB驅動說明
    從一開始學習PLC,就有很多朋友遇到軟體驅動問題,那麼再此說明一下三菱PLC-USB轉232驅動說明:市面上USB轉232線品類眾多
  • Linux USB滑鼠驅動程序詳解
    一個 USB 總線引出兩個重要的鍊表,一個為 USB 設備鍊表,一個為 USB 驅動鍊表。設備鍊表包含各種系統中的 USB 設備以及這些設備的所有接口,驅動鍊表包含 USB 設備驅動程序(usb device driver)和 USB 驅動程序(usb driver)。
  • 重裝系統必備神器:usb萬能驅動
    其實我們可以下載安裝一個usb設備試試,為此,小編就跟大夥嘮嘮這個usb萬能驅動。相信不少人在使用usb或者usb設備的時候,都遇到過無法識別usb的問題。這個問題是比較常見的,有些人是因為電腦的某個程序或者硬體損壞了而無法識別,那麼如果是由於usb驅動發生故障時,該怎麼辦呢?下面,小編就跟大家介紹usb萬能驅動,可解此毒。
  • USB設備控制器(UDC)驅動分析
    內核版本:4.20.12驅動路徑:/drivers/usb/chipidea/core.c                /drivers/usb/chipidea/udc.cUSB IP核:ChipideaChipidea的UDC驅動和主機控制器驅動都是放在core.c中,裡面根據不同的配置來選擇初始化角色(主機,外設,OTG等)。
  • USB驅動設備無法啟動!
    然後換了usb口不行;換滑鼠不行。想來麻煩大了,想來用慣滑鼠的手,只用tab鍵大概是有些不適應的。抓狂的時候,想用手指去屏幕亂點,結果肯定沒什麼卵用。分析問題應該出在新安的軟體上,所以第一反應是去卸載軟體,想從360軟體管理界面進入,但從程序用tab打開軟體後,就無法進行操作,看來windows只默認應用系統裡的東西。
  • 安裝win7系統怎麼注入USB3.0驅動和NVME驅動
    win7注入usb3.0和nvme固態硬碟驅動的方法。 以往我們安裝win7系統或原版win7系統時,都是先下載相關的對應的驅動,然後在安裝時手動注入usb3.0和nvme固態硬碟等驅動,但這種方法操作起來相對小白來說操作起來比較困難,而且還要下載對驅動,要不就會出現問題,今天就教大家一個簡單的方法自動注入usb3.0和nvme驅動。
  • uart驅動框架分析(二)uart_add_one_port
    作者:lizuobin (也是百問網論壇兼職答疑助手)原文:https://blog.csdn.net/lizuobin2/article/details/51801183 (所用開發板:mini2440, jz2440類似,代碼很多,需要你建個si工程跟著代碼分析)在前面的一篇文章中:   tty初探 — uart驅動框架分析
  • 嵌入式Linux USB驅動開發之教你一步步編寫USB驅動程序
    usb_complete_t complete_fn,  7.                  void *context,  8.                  int interval);  9.
  • Windows下USB音視頻類驅動程序堆棧架構-USB中文網
    一個USB接口的UVC攝像頭接入Windows系統後,Windows會使用系統自帶的驅動進行硬體設備驅動,從而實現「免驅」的效果。
  • usb無線網卡怎麼用 桌上型電腦usb無線網卡驅動安裝
    今天小編通過親身實踐,分享一下usb無線網卡怎麼用,希望對有需要的朋友有所幫助。桌上型電腦安裝usb無線網卡,無線上網其實並不難,難點主要在驅動安裝部分,以下是桌上型電腦usb無線網卡驅動安裝使用教程。桌上型電腦安裝USB無線網卡驅動主要有2種方式:1、通過驅動光碟安裝小編買的是300Mbps騰達U1 USB無線網卡,包裝中自帶了驅動光碟,無奈桌上型電腦沒有配備光碟機,因此小編只能選擇下面的方法。
  • 「圖文」深入了解Linux USB驅動工作的整個流程
    (USB 核心將其傳遞給USB設備驅動,並由USB設備驅動負責後續的控制。ep_dev; /* For sysfs info */    unsigned char*extra;/* Extra descriptors */    int extralen;    int enabled;};當調用USB設備驅動調用usb_submit_urb提交urb請求時,將調用int usb_hcd_link_urb_to_ep(struct
  • USB大容量存儲設備無法啟動--這個設備(服務)的驅動程序已被禁用.另一個驅動程序可以提供這個功能. (代碼 32)
    (代碼 1)----   win10 usb功能被禁用的解決方法:  1.插上u盤。通過以上的4個步驟,就可以解決Windows10 usb功能被被禁用的問題。方法二問題描述如下:這個設備(服務)的驅動程序已被禁用。另一個驅動程序可以提供這個功能。
  • win7驅動打不上?黑科技H310/B360 USB核顯驅動安裝教程
    得益於科技一直在進步,新硬體每年都在發布更新(雖然是擠牙膏式升級),英特爾酷睿9代之後,Intel在b360 z390h370 h310(14nm工藝)晶片組上,不再支持windows7作業系統,表現為:系統安裝好以後,不能識別到相關的usb設備,沒有相關的usb驅動。
  • 帶你遨遊USB世界
    4、USB gadget驅動分析4.2、USB gadget功能框架5)f_mtp.cmtp驅動文件映射到文件節點/dev/mtp_usb :5.2、滑鼠驅動在Linuxkernel中,drivers\hid\usbhid\hiddev.c和drivers\hid\usbhid\usbmouse.c兩個驅動文件均可以支持USB滑鼠
  • U盤怎麼安裝USB3.0驅動?
    現在usb3.0是一種USB規範,USB3.0的速度是USB2.0的十倍,並且比USB2.0更加節能。那麼怎麼在U盤中加入USB3.0驅動呢?
  • Linux 下使用USB 網絡
    這種網絡有相當強的實用價值,而且完整實現的Linux USB Slave驅動和Linux USB Gadget兩種驅動,因此有很強演示效果.g_ether.ko驅動。  二、增加第二個有線網絡.   在教室上課時,很多時候伺服器和學生用機只有一個網絡口接口,我把網線接到區域網時,我的開發板就無法用NFS接我的機器,學生機也是如此,現在伺服器上把有線網絡接入區域網,而我的開發板與伺服器則直接相連.