USB主机控制器与设备驱动---主机侧
一、Linux USB驱动层次
1、USB驱动(主机侧)
NOTE:在Linux 系统中,USB驱动可以从两个角度去观察,一个角度是主机侧,一个角度是设备侧。
从主机侧角度看,USB驱动从底到上依次是:
USB主机控制器硬件层 --> USB主机控制器驱动层 --> USB核心层 --> USB设备驱动层
USB主机控制器驱动层:控制插入的USB设备;
USB核心层:负责USB驱动管理和协议处理。
---定义一些数据结构、宏和功能函数,分别向上层(USB设备驱动层)和下层(USB主机控制器驱动层) 提供编程接口;
---通过全局变量维护整个系统的USB信息;
---完成设备热拔插控制、总线数据传输控制等;
USB设备驱动层:控制USB设备和主机的通信;
2、USB的逻辑组合(4个层次)
设备(device):包含一个/多个配置。
设备描述符:struct usb_device_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdUSB;
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
__u8 bMaxPacketSize0;
__le16 idVendor;
__le16 idProduct;
__le16 bcdDevice;
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
__u8 bNumConfigurations;
} __attribute__ ((packed));
配置(config):不同的配置使设备表现出不同的功能组合。
---每个配置可以有多个接口,可由多个接口组成。
配置描述符:struct usb_config_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 wTotalLength;
__u8 bNumInterfaces;
__u8 bConfigurationValue;
__u8 iConfiguration;
__u8 bmAttributes;
__u8 bMaxPower;
} __attribute__ ((packed));
接口(interface):代表一个基本功能,是USB 设备驱动程序控制的对象。
---配置中的所有接口可以同时有效,并可被不同的驱动程序连接。
---接口可以有备用接口,以提供不同质量的服务参数。
---接口是端点的汇集。
接口描述符:struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber;
__u8 bAlternateSetting;
__u8 bNumEndpoints;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__ ((packed));
端点(endpoint):USB通信的基本形式(主机只能通过端点与设备进行通信,以使用设备的功能)。
---USB系统中每一个端点都有惟一的地址,由设备地址和端点号给出的。
---每个端点的属性包含传输方式、总线访问频率、带宽、端点号和数据包的最大容量等。
---每个USB 端点只能在一个方向承载数据,或主机到设备(输出端点),或设备到主机(输入端点)。
---端点0(控制端点,用于设备初始化参数),只要设备连接到USB并且上电端点0就可以被访问。
端点描述符:struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress;
__u8 bmAttributes;
__le16 wMaxPacketSize;
__u8 bInterval;
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
实例:在linux机器上插入U盘/读卡器,利用lsusb -v命令查看设备详细信息:
二、USB主机(控制器)驱动
1、主机控制器规格
OHCI:Open Host Controller Interface.
UHCI:Universal Host Controller Interface.
EHCI:Enhance(增强型),主要用于USB2.0,兼容OHCI和UHCI;
xHCI:eXtensible(可扩展的),主要用于USB3.0,兼容EHCI、UHCI及OHCI;
2、主机控制器的相关结构
usb_hcd结构体:描述USB主机控制器驱动。
struct usb_hcd {
/* housekeeping */
struct usb_bus self; /* hcd is-a bus */
struct kref kref; /* reference counter */
const char *product_desc; /* product/vendor string */
int speed; /* Speed for this roothub.
char irq_descr[24]; /* driver + bus # */
struct timer_list rh_timer; /* drives root-hub polling */
struct urb *status_urb; /* the current status urb */
/*
* hardware info/state
*/
const struct hc_driver *driver; /* hw-specific hooks(特定硬件的钩子函数) */
/*
* OTG and some Host controllers need software interaction with phys;
* other external phys should be software-transparent
*/
struct usb_phy *usb_phy;
struct phy *phy;
...
...
int state;
# define __ACTIVE 0x01
# define __SUSPEND 0x04
# define __TRANSIENT 0x80
...
/* The HC driver's private data is stored at the end of
* this structure.
*/
unsigned long hcd_priv[0]
__attribute__ ((aligned(sizeof(s64))));
};
hc_driver结构体:用于操作主机控制器。
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) */
...
/* 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, bool do_wakeup);
/* called after entering D0 (etc), before resuming the hub */
int (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
/* 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);
...
...
};
hcd的创建、添加和移除:
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name);
int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags);
void usb_remove_hcd(struct usb_hcd *hcd);
xhci主机控制器
1)xhci_hcd结构体:drivers/usb/host/xhci.h
struct xhci_hcd {
struct usb_hcd *main_hcd;
struct usb_hcd *shared_hcd;
/* glue to PCI and HCD framework */
struct xhci_cap_regs __iomem *cap_regs;
struct xhci_op_regs __iomem *op_regs;
struct xhci_run_regs __iomem *run_regs;
struct xhci_doorbell_array __iomem *dba;
/* Our HCD's current interrupter register set */
struct xhci_intr_reg __iomem *ir_set;
/* Cached register copies of read-only HC data */
__u32 hcs_params1;
...
spinlock_t lock;
/* packed release number */
u8 sbrn;
u16 hci_version;
...
...
...
};
2)usb_hcd和xhci_hcd的相互转换:
struct xhci_hcd *hcd_to_xhci(struct usb_hcd *hcd);
struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci);
3)xHCI主机控制器的初始化:
int xhci_init(struct usb_hcd *hcd);
4)xHCI主机控制器的开启、停止、复位:
int xhci_start(struct xhci_hcd *xhci);
void xhci_stop(struct usb_hcd *hcd);
int xhci_reset(struct xhci_hcd *xhci);
3、实例:xHCI主机控制器驱动
xHCI主机控制器驱动 drivers/usb/host/xhci.c
static const struct hc_driver xhci_hc_driver = {
.description = "xhci-hcd",
.product_desc = "xHCI Host Controller",
.hcd_priv_size = sizeof(struct xhci_hcd),
/*
* generic hardware linkage
*/
.irq = xhci_irq,
.flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED,
/*
* basic lifecycle operations
*/
.reset = NULL, /* set in xhci_init_driver() */
.start = xhci_run,
.stop = xhci_stop,
.shutdown = xhci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = xhci_urb_enqueue,
.urb_dequeue = xhci_urb_dequeue,
.alloc_dev = xhci_alloc_dev,
.free_dev = xhci_free_dev,
...
...
/*
* scheduling support
*/
.get_frame_number = xhci_get_frame,
/*
* root hub support
*/
.hub_control = xhci_hub_control,
.hub_status_data = xhci_hub_status_data,
.bus_suspend = xhci_bus_suspend,
.bus_resume = xhci_bus_resume,
/*
* call back when device connected and addressed
*/
.update_device = xhci_update_device,
.set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm,
.enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout,
.disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
.find_raw_port_number = xhci_find_raw_port_number,
.submit_single_step_set_feature = xhci_submit_single_step_set_feature,
};
int xhci_run(struct usb_hcd *hcd)
{
u32 temp;
u64 temp_64;
int ret;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
/* Start the xHCI host controller running only after the USB 2.0 roothub
* is setup.
*/
hcd->uses_new_polling = 1;
if (!usb_hcd_is_primary_hcd(hcd))
return xhci_run_finished(xhci);
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_run");
ret = xhci_try_enable_msi(hcd);
...
...
temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
temp_64 &= ~ERST_PTR_MASK;
...
temp = readl(&xhci->ir_set->irq_control);
temp &= ~ER_IRQ_INTERVAL_MASK;
/*
* the increment interval is 8 times as much as that defined
* in xHCI spec on MTK's controller
*/
temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160);
writel(temp, &xhci->ir_set->irq_control);
/* Set the HCD state before we enable the irqs */
temp = readl(&xhci->op_regs->command);
temp |= (CMD_EIE);
...
writel(temp, &xhci->op_regs->command);
temp = readl(&xhci->ir_set->irq_pending);
...
writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending);
xhci_print_ir_set(xhci, 0);
if (xhci->quirks & XHCI_NEC_HOST) {
struct xhci_command *command;
command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
...
xhci_queue_vendor_command(xhci, command, 0, 0, 0,
TRB_TYPE(TRB_NEC_GET_FW));
}
...
return 0;
}
三、USB设备驱动(详解)
四、实例
4.1 USB串口驱动:drivers/usb/serial/usb-serial.c
4.2 USB键盘驱动:drivers/hid/usbhid/usbkbd.c