Linux-USB驱动笔记(五)--主机控制器驱动框架
1、前言
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()即可。