Linux-USB驱动笔记(五)--主机控制器驱动框架

26 篇文章 31 订阅

1、前言

Linux-USB驱动笔记一

Linux-USB驱动笔记二

Linux-USB驱动笔记三

Linux-USB驱动笔记(四)–USB整体框架

2、主机控制器框架

在这里插入图片描述
USB核心(USBD)是整个USB驱动的核心部分,一方面USBD对从USB主机控制器接收到的数据进行处理,然后传递给上层的设备端驱动;同时也接收来自上层的非USB格式数据流,进行相应的数据处理后传递给USB主机控制器驱动。所以它起了一个承上启下的作用。

3、重要结构体

3.1、usb_hcd – 主机控制器驱动

//include/linux/usb/hcd.h
struct 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))));
};

3.2、hc_driver – 控制器操作函数


//include/linux/usb/hcd.h
struct 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()函数。

3.3、ehci_hcd – ECHI主机控制器


//drivers/usb/host/echi.h
struct 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) 。

4、API函数


//创建和初始化一个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_hcd
static 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.c
void 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_driver
static 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()即可。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值