USB子系统初始化

USB子系统初始化

重点结构体介绍

1. struct pci_driver xhci_pci_driver

/* pci driver glue; this is a "new style" PCI driver module */
static struct pci_driver xhci_pci_driver = {
	.name =		(char *) hcd_name,
	.id_table =	pci_ids,

	.probe =	xhci_pci_probe,
	.remove =	xhci_pci_remove,
	/* suspend and resume implemented later */

	.shutdown = 	usb_hcd_pci_shutdown,
#ifdef CONFIG_PM
	.driver = {
		.pm = &usb_hcd_pci_pm_ops
	},
#endif
};

2. struct pci_device_id pci_ids[]

/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids[] = { {
	/* handle any USB 3.0 xHCI controller */
	PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
	.driver_data =	(unsigned long) &xhci_pci_hc_driver,
	},
	{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, pci_ids);

3. struct hc_driver xhci_hc_driver

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,
	.alloc_streams =	xhci_alloc_streams,
	.free_streams =		xhci_free_streams,
	.add_endpoint =		xhci_add_endpoint,
	.drop_endpoint =	xhci_drop_endpoint,
	.endpoint_reset =	xhci_endpoint_reset,
	.check_bandwidth =	xhci_check_bandwidth,
	.reset_bandwidth =	xhci_reset_bandwidth,
	.address_device =	xhci_address_device,
	.enable_device =	xhci_enable_device,
	.update_hub_device =	xhci_update_hub_device,
	.reset_device =		xhci_discover_or_reset_device,

	/*
	 * 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,
	.get_resuming_ports =	xhci_get_resuming_ports,

	/*
	 * 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,
};

4. struct attribute_group *usb_device_groups[]

const struct attribute_group *usb_device_groups[] = {
	&dev_attr_grp, //属性组信息
	&dev_string_attr_grp, //属性字符串组
	NULL
};

5. struct attribute_group dev_attr_grp

static struct attribute_group dev_attr_grp = {
	.attrs = dev_attrs,
};

6. struct attribute *dev_attrs[]

static struct attribute *dev_attrs[] = {
	/* current configuration's attributes */
	&dev_attr_configuration.attr,
	&dev_attr_bNumInterfaces.attr,
	&dev_attr_bConfigurationValue.attr,
	&dev_attr_bmAttributes.attr,
	&dev_attr_bMaxPower.attr,
	/* device attributes */
	&dev_attr_urbnum.attr,
	&dev_attr_idVendor.attr,
	&dev_attr_idProduct.attr,
	&dev_attr_bcdDevice.attr,
	&dev_attr_bDeviceClass.attr,
	&dev_attr_bDeviceSubClass.attr,
	&dev_attr_bDeviceProtocol.attr,
	&dev_attr_bNumConfigurations.attr,
	&dev_attr_bMaxPacketSize0.attr,
	&dev_attr_speed.attr,
	&dev_attr_busnum.attr,
	&dev_attr_devnum.attr,
	&dev_attr_devpath.attr,
	&dev_attr_version.attr,
	&dev_attr_maxchild.attr,
	&dev_attr_quirks.attr,
	&dev_attr_avoid_reset_quirk.attr,
	&dev_attr_authorized.attr,
	&dev_attr_remove.attr,
	&dev_attr_removable.attr,
	&dev_attr_ltm_capable.attr,
	NULL,
};

7. attribute_group dev_string_attr_grp


static struct attribute_group dev_string_attr_grp = {
	.attrs =	dev_string_attrs,
	.is_visible =	dev_string_attrs_are_visible,
};

8. struct attribute *dev_string_attrs[]

static struct attribute *dev_string_attrs[] = {
	&dev_attr_manufacturer.attr,
	&dev_attr_product.attr,
	&dev_attr_serial.attr,
	NULL
};

*dev_string_attrs[]*dev_attrs[] 对应系统目录结构信息为:

[root@szclou /sys/devices/platform/nuc970-ehci/usb1]#ls
1-0:1.0              bmAttributes         maxchild
authorized           busnum               product
authorized_default   configuration        quirks
avoid_reset_quirk    descriptors          removable
bConfigurationValue  dev                  remove
bDeviceClass         devnum               serial
bDeviceProtocol      devpath              speed
bDeviceSubClass      driver               subsystem
bMaxPacketSize0      ep_00                uevent
bMaxPower            idProduct            urbnum
bNumConfigurations   idVendor             version
bNumInterfaces       ltm_capable
bcdDevice            manufacturer

9. struct usb_device_descriptor

在usb规范中,一个usb设备通过一些层级结构的描述符来描述。在linux内核中,通过usb_device结构来表示一个usb设备,通常一个usb设备有一个设备描述符,一个或多个配置描述符,以及一个或多个接口描述符。设备描述符用来描述设备的信息,包含厂家ID,产品ID等:

/* USB_DT_DEVICE: Device descriptor */
struct usb_device_descriptor {  
    __u8  bLength;//设备描述符的字节数大小,为0x12   
    __u8  bDescriptorType;//描述符类型编号,为0x01   
  
    __le16 bcdUSB;//USB版本号   
    __u8  bDeviceClass;//USB分配的设备类代码,0x01~0xfe为标准设备类,0xff为厂商自定义类型   
      
    //0x00不是在设备描述符中定义的,如HID   
    __u8  bDeviceSubClass;//usb分配的子类代码,同上,值由USB规定和分配的  
    __u8  bDeviceProtocol;//USB分配的设备协议代码,同上   
    __u8  bMaxPacketSize0;//端点0的最大包的大小   
    __le16 idVendor;//厂商编号   
    __le16 idProduct;//产品编号   
    __le16 bcdDevice;//设备出厂编号  
    __u8  iManufacturer;//描述厂商字符串的索引   
    __u8  iProduct;//描述产品字符串的索引  
    __u8  iSerialNumber;//描述设备序列号字符串的索引   
    __u8  bNumConfigurations;//可能的配置数量  
} __attribute__ ((packed));

10. struct bus_type usb_bus_type

drivers/usb/core/driver.c

struct bus_type usb_bus_type = {
	.name =		"usb",
	.match =	usb_device_match,
	.uevent =	usb_uevent,
	.need_parent_lock =	true,
};

11. struct device_driver

struct device_driver {
	const char		*name;  //与设备匹配时会用到的驱动名,一样就匹配成
	struct bus_type		*bus;  //在注册函数platform_driver_register中指定
 
	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */
 
	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
	enum probe_type probe_type;
 
	const struct of_device_id	*of_match_table;  //与设备树匹配,名称一样就匹配成功
	const struct acpi_device_id	*acpi_match_table;
 
	int (*probe) (struct device *dev);  //调用注册函数指定
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;
 
	const struct dev_pm_ops *pm;
 
	struct driver_private *p;
};

12. struct usb_device_driver usb_generic_driver

struct usb_device_driver usb_generic_driver = {
	.name =	"usb",
	.probe = generic_probe,
	.disconnect = generic_disconnect,
#ifdef	CONFIG_PM
	.suspend = generic_suspend,
	.resume = generic_resume,
#endif
	.supports_autosuspend = 1,
};

13. struct usb_driver hub_driver

static struct usb_driver hub_driver = {
	.name =		"hub",
	.probe =	hub_probe,//hub探测接口
	.disconnect =	hub_disconnect,
	.suspend =	hub_suspend,//hub挂起,涉及到PM电源管理驱动控制
	.resume =	hub_resume,//hub恢复
	.reset_resume =	hub_reset_resume,//通过对hub复位来引发复位
	.pre_reset =	hub_pre_reset,//与PM电源管理系统相关
	.post_reset =	hub_post_reset,//与PM电源管理系统相关
	.unlocked_ioctl = hub_ioctl,
	.id_table =	hub_id_table,//hub id表
	.supports_autosuspend =	1,
};

14. struct usb_device_id

/**
 * struct usb_device_id - identifies USB devices for probing and hotplugging
 * @match_flags: Bit mask controlling which of the other fields are used to
 *	match against new devices. Any field except for driver_info may be
 *	used, although some only make sense in conjunction with other fields.
 *	This is usually set by a USB_DEVICE_*() macro, which sets all
 *	other fields in this structure except for driver_info.
 * @idVendor: USB vendor ID for a device; numbers are assigned
 *	by the USB forum to its members.
 * @idProduct: Vendor-assigned product ID.
 * @bcdDevice_lo: Low end of range of vendor-assigned product version numbers.
 *	This is also used to identify individual product versions, for
 *	a range consisting of a single device.
 * @bcdDevice_hi: High end of version number range.  The range of product
 *	versions is inclusive.
 * @bDeviceClass: Class of device; numbers are assigned
 *	by the USB forum.  Products may choose to implement classes,
 *	or be vendor-specific.  Device classes specify behavior of all
 *	the interfaces on a device.
 * @bDeviceSubClass: Subclass of device; associated with bDeviceClass.
 * @bDeviceProtocol: Protocol of device; associated with bDeviceClass.
 * @bInterfaceClass: Class of interface; numbers are assigned
 *	by the USB forum.  Products may choose to implement classes,
 *	or be vendor-specific.  Interface classes specify behavior only
 *	of a given interface; other interfaces may support other classes.
 * @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass.
 * @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass.
 * @bInterfaceNumber: Number of interface; composite devices may use
 *	fixed interface numbers to differentiate between vendor-specific
 *	interfaces.
 * @driver_info: Holds information used by the driver.  Usually it holds
 *	a pointer to a descriptor understood by the driver, or perhaps
 *	device flags.
 *
 * In most cases, drivers will create a table of device IDs by using
 * USB_DEVICE(), or similar macros designed for that purpose.
 * They will then export it to userspace using MODULE_DEVICE_TABLE(),
 * and provide it to the USB core through their usb_driver structure.
 *
 * See the usb_match_id() function for information about how matches are
 * performed.  Briefly, you will normally use one of several macros to help
 * construct these entries.  Each entry you provide will either identify
 * one or more specific products, or will identify a class of products
 * which have agreed to behave the same.  You should put the more specific
 * matches towards the beginning of your table, so that driver_info can
 * record quirks of specific products.
 */
struct usb_device_id {
	/*表示下面哪些字段(产品、设备类别、接口类)可以进行匹配*/
	/* which fields to match against? */
	__u16		match_flags;
	
	/*用于产品的匹配*/
	/* Used for product specific matches; range is inclusive */
	__u16		idVendor;
	__u16		idProduct;
	__u16		bcdDevice_lo;
	__u16		bcdDevice_hi;

	/*用于设备类别的匹配*/
	/* Used for device class matches */
	__u8		bDeviceClass;
	__u8		bDeviceSubClass;
	__u8		bDeviceProtocol;

	/*用于接口类别的匹配*/
	/* Used for interface class matches */
	__u8		bInterfaceClass;
	__u8		bInterfaceSubClass;
	__u8		bInterfaceProtocol;

	/* Used for vendor-specific interface matches */
	__u8		bInterfaceNumber;

	/* not matched against */
	kernel_ulong_t	driver_info
		__attribute__((aligned(sizeof(kernel_ulong_t))));
};

15. struct usb_device_id hub_id_table[]

hub_id_table 分别初始化了三个usb_device_id结构体:

static const struct usb_device_id hub_id_table[] = {
    { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
			| USB_DEVICE_ID_MATCH_INT_CLASS,
      .idVendor = USB_VENDOR_GENESYS_LOGIC,
      .bInterfaceClass = USB_CLASS_HUB,
      .driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
    { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
      .bDeviceClass = USB_CLASS_HUB},
    { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
      .bInterfaceClass = USB_CLASS_HUB},
    { }						/* Terminating entry */
};

第1个结构体成员
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS, //表示需匹配的字段
.idVendor = USB_VENDOR_GENESYS_LOGIC,//匹配的字段
.bInterfaceClass = USB_CLASS_HUB //匹配的字段

第2个结构体成员
.match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, //表示需匹配的字段
.bDeviceClass = USB_CLASS_HUB //匹配的字段

第3个结构体成员
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, //表示需匹配的字段
.bInterfaceClass = USB_CLASS_HUB //匹配的字段

综上所述,match_flags 表示哪些字段匹配,而对应匹配的字段就是idVendorbInterfaceClassbDeviceClassbInterfaceClass ,这里在下面会分析如何使用。

16. struct usb_config_descriptor

在linux内核中,配置描述符和接口描述符用usb_host_config结构来表示,在设备描述符中bNumConfigurations的值为多少就有多少个usb_host_configusb_host_config中包含了usb设备的配置描述符,以及接口描述符。

usb设备的配置描述符用usb_config_descriptor结构来表示:

/* USB_DT_CONFIG: Configuration descriptor information.
 *
 * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the
 * descriptor type is different.  Highspeed-capable devices can look
 * different depending on what speed they're currently running.  Only
 * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG
 * descriptors.
 */
struct usb_config_descriptor {
	__u8  bLength;
	__u8  bDescriptorType;

	__le16 wTotalLength;
	__u8  bNumInterfaces;
	__u8  bConfigurationValue;
	__u8  iConfiguration;
	__u8  bmAttributes;
	__u8  bMaxPower;
} __attribute__ ((packed));

17. struct usb_interface_descriptor

在Linux内核中用usb_interface 结构来描述一个接口设备,配置描述符中的bNumInterfaces的值为多少就有多少个usb_interface。linux内核中用usb_host_interface结构表示接口描述,在该结构中包含了该接口的接口描述符,usb_host_interface结构位于usb_interface结构中。接口描述符定义如下:

/* USB_DT_INTERFACE: Interface descriptor */
struct usb_interface_descriptor {
	__u8  bLength;
	__u8  bDescriptorType;

	__u8  bInterfaceNumber;
	__u8  bAlternateSetting;
	__u8  bNumEndpoints;
	__u8  bInterfaceClass;
	__u8  bInterfaceSubClass;
	__u8  bInterfaceProtocol;
	__u8  iInterface;
} __attribute__ ((packed));

1. USB设备类型

1.1 usb设备

usb设备用usb_device结构来表示,它表示一个物理的usb设备。
通过下面的接口判断是否是usb设备:

static inline int is_usb_device(const struct device *dev)

1.2 usb接口

usb接口通过usb_interface结构来表示,表示一个usb设备的某个接口。
通过下面的接口判断设备是否为usb接口设备:

static inline int is_usb_interface(const struct device *dev)

2. USB驱动类型

2.1 驱动结构

和usb设备对应,在linux内核usb设备驱动模型中,usb驱动分为设备驱动和接口驱动,usb设备驱动用来解析和配置usb接口设备,以及注册usb的接口设备。

2.1.1 usb设备驱动

usb的设备驱动在内核中用usb_device_driver结构表示:

struct usb_device_driver {
        const char *name;
                
        int (*probe) (struct usb_device *udev);
        void (*disconnect) (struct usb_device *udev);
                
        int (*suspend) (struct usb_device *udev, pm_message_t message);
        int (*resume) (struct usb_device *udev, pm_message_t message);
        struct usbdrv_wrap drvwrap;
        unsigned int supports_autosuspend:1;
};
2.1.2 usb接口驱动

usb的接口驱动在内核中用usb_driver结构表示:

struct usb_driver {
        const char *name;

        int (*probe) (struct usb_interface *intf,
                      const struct usb_device_id *id);

        void (*disconnect) (struct usb_interface *intf);

        int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
                        void *buf);

        int (*suspend) (struct usb_interface *intf, pm_message_t message);
        int (*resume) (struct usb_interface *intf);
        int (*reset_resume)(struct usb_interface *intf);

        int (*pre_reset)(struct usb_interface *intf);
        int (*post_reset)(struct usb_interface *intf);

        const struct usb_device_id *id_table;

        struct usb_dynids dynids;
        struct usbdrv_wrap drvwrap;
        unsigned int no_dynamic_id:1;
        unsigned int supports_autosuspend:1;
        unsigned int disable_hub_initiated_lpm:1;
        unsigned int soft_unbind:1;
};

2.2 驱动类型的区分

usb_device_driverusb_driver结构中都包含一个usbdrv_wrap类型的drvwrap成员,该成员结构定义如下:

struct usbdrv_wrap {
        struct device_driver driver;
        int for_devices;
};

其中driver成员是usb驱动结构中内嵌的device_driver对象,是设备驱动模型的driver部件。for_device成员用来表示该驱动是usb设备驱动还是usb接口驱动。for_device的值为1表示usb设备驱动,为0表示usb接口驱动。

内核中通过下面的接口判断usb驱动是设备驱动还是接口驱动:

static inline int is_usb_device_driver(struct device_driver *drv)

2.3 驱动注册

2.3.1 usb设备驱动注册

usb设备驱动的注册和注销接口如下:

int usb_register_device_driver(struct usb_device_driver *new_udriver,
                struct module *owner)void usb_deregister_device_driver(struct usb_device_driver *udriver)

设备驱动注册函数中对drvwrap.for_devices成员赋值为1,表示注册的usb驱动是usb设备驱动,并且调用driver_register接口将drvwrap.driver注册到内核设备驱动模型中。代码如下:

new_udriver->drvwrap.for_devices = 1;
new_udriver->drvwrap.driver.name = new_udriver->name;
new_udriver->drvwrap.driver.bus = &usb_bus_type;
new_udriver->drvwrap.driver.probe = usb_probe_device;
new_udriver->drvwrap.driver.remove = usb_unbind_device;
new_udriver->drvwrap.driver.owner = owner;

retval = driver_register(&new_udriver->drvwrap.driver);
2.3.2 usb接口驱动注册

usb接口驱动的注册和注销接口如下:

/* use a define to avoid include chaining to get THIS_MODULE & friends */
#define usb_register(driver) \
        usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
extern void usb_deregister(struct usb_driver *);

接口驱动注册函数中对drvwrap.for_devices成员赋值为0,表示注册的是一个usb接口驱动,并且调用driver_register接口将drvwrap.driver注册到内核设备驱动模型中。代码如下:

new_driver->drvwrap.for_devices = 0;
new_driver->drvwrap.driver.name = new_driver->name;
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface;
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);

retval = driver_register(&new_driver->drvwrap.driver);

3. USB设备驱动模型

3.1 概述

usb设备驱动模型是内核设备驱动模型的应用,它包含usb总线,usb设备和usb驱动,以及用户空间视图的sysfs等。

usb总线的作用是维护所有的usb设备和usb驱动,以及为usb设备和usb驱动创建关联,以达到用户通过usb驱动操作关联的usb设备的目的。

3.2 USB总线

usb总线在内核中的定义如下:

drivers/usb/core/driver.c

struct bus_type usb_bus_type = {
	.name =		"usb",
	.match =	usb_device_match,
	.uevent =	usb_uevent,
	.need_parent_lock =	true,
};

总线名称为usb,即在/sys/bus/目录下存在usb目录,表示usb总线。

match函数指针用来进行设备和驱动的匹配,当向总线上添加一个usb设备(或usb驱动)时,linux内核设备驱动模型核心就会遍历usb总线上的驱动(或设备),并为其调用usb总线的match方法,以判断新插入的设备能否被某个驱动处理。match方法成功返回1,否则返回0。当内核设备驱动核心得到匹配成功的结果后就会为usb设备和usb驱动创建关联,并调用驱动的probe函数。

在usb总线的match方法中需要判断驱动和设备的类型,设备和驱动都分为usb设备和接口两个类型。对于设备类型和设备驱动类型的匹配,直接返回成功,而对于接口设备和接口驱动的匹配,则需要通过接口驱动的id_table成员中的信息和接口设备的信息进行匹配。id_table由usb接口驱动设置,用来描述该驱动能处理那些设备。

3.3 usb设备

usb设备分为usb设备和usb接口设备两种,通常一个usb物理设备关联一个usb设备对象,usb设备对象关联一个或多个usb接口对象。

当一个新的usb设备对象或者usb设备接口对象被注册到usb总线上的时候,内核设备驱动核心为其在usb总线上查找能处理其的驱动,并执行驱动的probe函数,以初始化设备和提供相关操作函数。

3.4 usb驱动

usb驱动也分为usb设备驱动和usb接口设备,usb设备驱动用来处理usb设备,usb接口驱动用来处理usb接口。通常大部分usb驱动都是usb接口驱动,比如usb存储,usb串口等。

在usb子系统初始化的时候会注册一个与设备相关的usb通用设备驱动,由usb总线match函数的代码可知该驱动能匹配所有usb设备。

当usb hub的事件线程监测到设备接入后,会向usb总线上注册新接入的设备,注册的设备首先将和usb通用驱动进行绑定,在usb通用驱动的probe函数中再申请注册usb接口设备。当usb接口设备注册到usb总线上时,再进行接口设备和接口驱动的绑定。

3.5 sysfs

sysfs是内核设备驱动模型的用户空间视图,usb子系统在sysfs中的路径是/sys/bus/usb目录。该目录下存在devices和drivers目录,用来呈现usb总线上的设备和驱动。

3.6 generic驱动

generic驱动在usb子系统初始化的时候由usb核心注册。该驱动负责处理所有的usb物理设备,当usb hub驱动监测到设备接入后,会向usb总线上注册新接入的设备,注册的设备首先将会和usb通用设备驱动关联,并调用usb_generic_driver的probe函数。

usb_generic_driver的probe函数中,将对设备进行配置和解析,并向usb总线注册该usb设备的设备接口设备。

当接口设备注册到usb总线上时,设备驱动核心将会为其在usb总线上查找和关联驱动,并调用关联驱动的probe函数。

```c
/*
 * Init
 */
static int __init usb_init(void)
{
	...
	retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
	...
}

```c
int usb_register_device_driver(struct usb_device_driver *new_udriver,
		struct module *owner)
{
	int retval = 0;
		...
	new_udriver->drvwrap.for_devices = 1;
	new_udriver->drvwrap.driver.name = new_udriver->name;
	new_udriver->drvwrap.driver.bus = &usb_bus_type;
	new_udriver->drvwrap.driver.probe = usb_probe_device;
	new_udriver->drvwrap.driver.remove = usb_unbind_device;
	new_udriver->drvwrap.driver.owner = owner;

	retval = driver_register(&new_udriver->drvwrap.driver);
	//klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	...
}
EXPORT_SYMBOL_GPL(usb_register_device_driver);

比如插入一个4G模块,将由usb_generic_driver配置4G模块的所有接口,并将接口注册到usb总线上,然后由设备驱动模型核心再为4G模块的所有接口在usb总线上查找和关联4G相关的驱动。

3.7 hub驱动

usb的hub驱动也是一个usb总线上的接口驱动,root hub设备由usb的host控制器驱动来创建和注册,当host驱动向usb总线注册了root hub设备的时候就会匹配到usb的hub驱动。

hub驱动会不停的监测新设备的接入。当hub驱动监测到新usb设备接入时会向内核设备驱动模型核心申请和注册usb设备,新设备所属总线为usb总线,所以当设备注册到usb总线上时,内核设备驱动模型核心会在usb总线上为其查找和绑定合适的usb驱动。

4. USB控制器驱动

usb的主控制器驱动结构为hc_driver,对应的usb主控制器设备的结构为usb_hcdhc_driver用来处理相应的usb_hcd。usb的控制器属于上一级的总线设备驱动模型的子节点,比如硬件上挂在pci总线上的usb控制器本身由pci核心来枚举,pci总线上的所有设备抽象为pci_dev结构,驱动抽象为pci_driver结构。当pci总线上枚举到pci_dev设备并注册设备后,将会调用pci_driverprobe函数并绑定pci_driver,在pci_driver的probe函数中,将创建和添加usb_hcd,同时从pci的pci_device_id中获取到hc_driver,并将hc_driverusb_hcd绑定。

4.1 usb主控制器(hcd)

硬件主机控制器Host Controller之上运行的是HCD,是对主机控制器硬件的一个抽象,实现核心层与控制器之间的对话接口,USB HCD包含多种USB接口规范:

(1)UHCI:Intel提供,通用主机控制接口,USB1.0/1.1;

(2)OHCI:微软提供,开放主机控制接口,USB1.0/1.1;

(3)EHCI:增强主机控制接口,USB2.0;

linux内核中用usb_hcd类型来描述一个usb主控制器,一个usb主控制器通常对应一个usb_bus类型的总线(此总线非设备驱动模型之总线)来维护该控制器上的设备树。

一个usb主控制器通常对应一个root hub设备,root hub设备是usb_device结构类型的。root hub作为usb总线(usb_bus)设备树上的其它usb设备的父设备,它被关联在usb_bus结构中。

root hub作为usb_device类型的对象,其关联的设备驱动模型的总线是usb总线,关联的设备驱动是usb hub驱动。在添加usb_hcd的时候,将会注册usb_bus以及root hub。root hub被初始化和注册后和usb hub驱动创建关联,之后的事就交给hub驱动和hub线程了。

4.3 控制器驱动(hc)

控制器驱动主要提供控制器的硬件操作。比如中断设置,提供控制器的初始化,开始停止,urb入队出队,usb端点屏蔽使能,hub状态获取,数据通信控制等功能。

5. USB core

USB Core这个模块是纯软件部分,并不代表一个设备,是独立于硬件的协议栈,它是所有USB设备赖以生存的模块,即USB子系统的核心。代码位于kernel/drivers/usb/core目录下。

USB Core为设备驱动程序提供服务,提供一个用于访问和控制USB硬件的接口,而不用考虑系统当前使用的哪种HOST Controller。USB Core将用户的请求映射到相关的HCD,用户不能直接访问HCD。USB Core就是HCD与USB设备的桥梁。

5.1 host driver

USB Subsystem Framework

The following chart shows the framework of the USB subsystem in Linux. Like i2c, the USB subsystem can be divided into three layers: ** Device Driver Layer - USB Core - Controller Driver Layer*
在这里插入图片描述
 The usb protocol is a complex protocol,The currently involved versions are usb1.0, usb2.0, usb3.0. If you open the kernel usb host directory, you will find that the following contains various forms of controller drivers such as ohci, uhci, ehci, xhci, and whci.

6. USB子系统初始化

USB的初始化函数在kernel/drivers/usb/core/usb.c中定义,主要完成bus_register(USB总线注册)、usb_major_init(注册usb主控器字符设备)、usb_register(注册usbfs驱动)、usb_hub_init(USB Hub初始化,注册hub驱动、创建内核守护线程来监测hub端口的状态变化)等工作。

6.1 总流程图

在这里插入图片描述

6.2 usb_init

usb_int 初始化整个usb系统的基础部分。(drivers/usb/core/usb.c

/*
 * Init
 */
static int __init usb_init(void)
{
	int retval;
	if (usb_disabled()) {
		pr_info("%s: USB support disabled\n", usbcore_name);
		return 0;
	}
	usb_init_pool_max();

	usb_debugfs_init();//初始化debugfs

	usb_acpi_register();//注册usb acpi总线
	retval = bus_register(&usb_bus_type);//注册总线,总线名称bus->name名字为 usb
	...
	retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);//注册usb总线通知链。
	...
	retval = usb_major_init();//注册usb主设备号
	...
	retval = usb_register(&usbfs_driver);//注册usbfs接口驱动
	...
	retval = usb_devio_init();//初始化usb字符设备io空间
	...
	retval = usb_hub_init();//初始化并注册usb hub驱动,申请hub工作队列
	...
	retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);//注册usb通用驱动。
	...
}
  • 注册USB总线:bus_register(&usb_bus_type);
  • 注册usb总线通知链:bus_register_notifier
  • 注册usb主设备号:usb_major_init()
  • 注册usbfs驱动:usb_register(&usbfs_driver);
  • 注册usb hub驱动:usb_hub_init -> usb_register(&hub_driver)
  • 注册通用设备驱动:usb_register_device_driver(&usb_generic_driver, THIS_MODULE)

由usb总线的定义可知,总线名称为“usb”,匹配方法为usb_device_match函数,uevent方法为usb_uevent
总线名称bus->name名字为usb

6.2.1 usb_init–>bus_register

注册名称bus->name名字为 usb 的总线
drivers/bash/bus.c

注意: 内核启动过程,在USB core加载之前也会有bus_register总线注册的调用

 [    0.238943]  drivers/base/bus.c:bus_register:bus: 'platform': registered
 [    0.246652]  drivers/base/bus.c:bus_register:bus: 'cpu': registered
 [    0.254096]  drivers/base/bus.c:bus_register:bus: 'memory': registered
 [    0.262313]  drivers/base/bus.c:bus_register:bus: 'container': registered
 [    0.279808]  drivers/base/bus.c:bus_register:bus: 'workqueue': registered
 [    0.486090]  drivers/base/bus.c:bus_register:bus: 'gpio': registered
 [    0.493597]  drivers/base/bus.c:bus_register:bus: 'virtio': registered
 [    0.511692]  drivers/base/bus.c:bus_register:bus: 'pci': registered
 [    0.527454]  drivers/base/bus.c:bus_register:bus: 'pci_express': registered
 [    0.535598]  drivers/base/bus.c:bus_register:bus: 'rapidio': registered
 [    0.543488]  drivers/base/bus.c:bus_register:bus: 'isa': registered
 [    0.550793]  drivers/base/bus.c:bus_register:bus: 'node': registered
 [    0.592103]  drivers/base/bus.c:bus_register:bus: 'spi': registered
 [    0.599533]  drivers/base/bus.c:bus_register:bus: 'i2c': registered
 [    1.296609]  drivers/base/bus.c:bus_register:bus: 'pnp': registered
 [    1.306813]  drivers/base/bus.c:bus_register:bus: 'parport': registered
 [    1.348520]  drivers/base/bus.c:bus_register:bus: 'scsi': registered
 [    1.430888]  drivers/base/bus.c:bus_register:bus: 'mdio_bus': registered
/**
 * bus_register - register a driver-core subsystem
 * @bus: bus to register
 *
 * Once we have that, we register the bus with the kobject
 * infrastructure, then register the children subsystems it has:
 * the devices and drivers that belong to the subsystem.
 */
int bus_register(struct bus_type *bus)
{
	int retval;
	struct subsys_private *priv;
	struct lock_class_key *key = &bus->lock_key;

	priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->bus = bus;
	bus->p = priv;

	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

	//初始化usb kset
	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset;
	priv->subsys.kobj.ktype = &bus_ktype;
	priv->drivers_autoprobe = 1;

	//注册usb kset
	retval = kset_register(&priv->subsys);
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent);
	...

	//初始化devices kset并注册在usb kset下
	priv->devices_kset = kset_create_and_add("devices", NULL,
						 &priv->subsys.kobj);
	...

	/初始化drivers kset并注册在usb kset下	
	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj);
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	INIT_LIST_HEAD(&priv->interfaces);
	__mutex_init(&priv->mutex, "subsys mutex", key);
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	klist_init(&priv->klist_drivers, NULL, NULL);

	retval = add_probe_files(bus);
	...
	retval = bus_add_groups(bus, bus->bus_groups);
	...
	...
}
EXPORT_SYMBOL_GPL(bus_register);

总结:注册usb类型的kset, 然后在usb类型的kset下添加devicesdrivers两个kset.

6.2.2 注册usb总线通知链
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);

通知链是linux内核用来实现异步通知的一种机制,通知链由一个通知链头结构和一些通知链上的block组成。usb核心会把一些事件发送到通知链,通知链核心将遍历通知链上的所有block,并调用block的回调函数以实现异步的效果。

总线上的通知链头结构包含在总线结构的p成员结构中。

usb总线通知链初始化的时候注册了一个通知块usb_bus_nb,该通知块定义如下:

static struct notifier_block usb_bus_nb = {
        .notifier_call = usb_bus_notify,
};

通知块回调函数为usb_bus_notify,该函数接收处理总线上添加或删除设备/接口设备的事件,当usb核心向内核设备驱动模型中添加或删除usb设备或接口设备后,向usb总线上发送通知,在usb_bus_notify通知处理函数中,向sysfs添加或删除设备或接口设备的属性文件。

6.2.3 注册usb主设备号
retval = usb_major_init();

usb_major_init函数中调用register_chrdev接口向内核注册了usb主设备号,usb的主设备号用USB_MAJOR宏定义,该宏定义的值为180

6.2.4 初始化usb字符设备io空间
retval = usb_devio_init();
6.2.4.1 在usb_devio_init函数中,首先按最大值为usb设备申请字符设备:
retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,
					"usb_device");
...
cdev_init(&usb_device_cdev, &usbdev_file_operations);
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
...
}
6.2.4.2 接着向usb内部通知链usb_notifier_list上注册通知块usbdev_nb
usb_register_notify(&usbdev_nb);

usbdev_nb定义如下:

static struct notifier_block usbdev_nb = {
	.notifier_call =	usbdev_notify,
};

usbdev_notify回调函数中判读如果有usb设备移除了,就会做一些相应的文件io回收释放等操作。

6.2.5 usb_init–>usb_register

在分析usb_register()函数前先看下hub_driver结构体:

static struct usb_driver hub_driver = {
	.name =		"hub",
	.probe =	hub_probe,//hub探测接口
	.disconnect =	hub_disconnect,
	.suspend =	hub_suspend,//hub挂起,涉及到PM电源管理驱动控制
	.resume =	hub_resume,//hub恢复
	.reset_resume =	hub_reset_resume,//通过对hub复位来引发复位
	.pre_reset =	hub_pre_reset,//与PM电源管理系统相关
	.post_reset =	hub_post_reset,//与PM电源管理系统相关
	.unlocked_ioctl = hub_ioctl,
	.id_table =	hub_id_table,//hub id表
	.supports_autosuspend =	1,
};
6.2.5.1 usb_device_id

hub_id_table 对应结构体 struct usb_device_id

/**
 * struct usb_device_id - identifies USB devices for probing and hotplugging
 * @match_flags: Bit mask controlling which of the other fields are used to
 *	match against new devices. Any field except for driver_info may be
 *	used, although some only make sense in conjunction with other fields.
 *	This is usually set by a USB_DEVICE_*() macro, which sets all
 *	other fields in this structure except for driver_info.
 * @idVendor: USB vendor ID for a device; numbers are assigned
 *	by the USB forum to its members.
 * @idProduct: Vendor-assigned product ID.
 * @bcdDevice_lo: Low end of range of vendor-assigned product version numbers.
 *	This is also used to identify individual product versions, for
 *	a range consisting of a single device.
 * @bcdDevice_hi: High end of version number range.  The range of product
 *	versions is inclusive.
 * @bDeviceClass: Class of device; numbers are assigned
 *	by the USB forum.  Products may choose to implement classes,
 *	or be vendor-specific.  Device classes specify behavior of all
 *	the interfaces on a device.
 * @bDeviceSubClass: Subclass of device; associated with bDeviceClass.
 * @bDeviceProtocol: Protocol of device; associated with bDeviceClass.
 * @bInterfaceClass: Class of interface; numbers are assigned
 *	by the USB forum.  Products may choose to implement classes,
 *	or be vendor-specific.  Interface classes specify behavior only
 *	of a given interface; other interfaces may support other classes.
 * @bInterfaceSubClass: Subclass of interface; associated with bInterfaceClass.
 * @bInterfaceProtocol: Protocol of interface; associated with bInterfaceClass.
 * @bInterfaceNumber: Number of interface; composite devices may use
 *	fixed interface numbers to differentiate between vendor-specific
 *	interfaces.
 * @driver_info: Holds information used by the driver.  Usually it holds
 *	a pointer to a descriptor understood by the driver, or perhaps
 *	device flags.
 *
 * In most cases, drivers will create a table of device IDs by using
 * USB_DEVICE(), or similar macros designed for that purpose.
 * They will then export it to userspace using MODULE_DEVICE_TABLE(),
 * and provide it to the USB core through their usb_driver structure.
 *
 * See the usb_match_id() function for information about how matches are
 * performed.  Briefly, you will normally use one of several macros to help
 * construct these entries.  Each entry you provide will either identify
 * one or more specific products, or will identify a class of products
 * which have agreed to behave the same.  You should put the more specific
 * matches towards the beginning of your table, so that driver_info can
 * record quirks of specific products.
 */
struct usb_device_id {
	/*表示下面哪些字段(产品、设备类别、接口类)可以进行匹配*/
	/* which fields to match against? */
	__u16		match_flags;
	
	/*用于产品的匹配*/
	/* Used for product specific matches; range is inclusive */
	__u16		idVendor;
	__u16		idProduct;
	__u16		bcdDevice_lo;
	__u16		bcdDevice_hi;

	/*用于设备类别的匹配*/
	/* Used for device class matches */
	__u8		bDeviceClass;
	__u8		bDeviceSubClass;
	__u8		bDeviceProtocol;

	/*用于接口类别的匹配*/
	/* Used for interface class matches */
	__u8		bInterfaceClass;
	__u8		bInterfaceSubClass;
	__u8		bInterfaceProtocol;

	/* Used for vendor-specific interface matches */
	__u8		bInterfaceNumber;

	/* not matched against */
	kernel_ulong_t	driver_info
		__attribute__((aligned(sizeof(kernel_ulong_t))));
};
6.2.5.2 hub_id_table

hub_id_table 分别初始化了三个usb_device_id结构体:

static const struct usb_device_id hub_id_table[] = {
    { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
			| USB_DEVICE_ID_MATCH_INT_CLASS,
      .idVendor = USB_VENDOR_GENESYS_LOGIC,
      .bInterfaceClass = USB_CLASS_HUB,
      .driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
    { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
      .bDeviceClass = USB_CLASS_HUB},
    { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
      .bInterfaceClass = USB_CLASS_HUB},
    { }						/* Terminating entry */
};

第1个结构体成员

.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS, //表示需匹配的字段
.idVendor = USB_VENDOR_GENESYS_LOGIC,//匹配的字段
.bInterfaceClass = USB_CLASS_HUB //匹配的字段

第2个结构体成员

.match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, //表示需匹配的字段
.bDeviceClass = USB_CLASS_HUB //匹配的字段

第3个结构体成员

.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, //表示需匹配的字段
.bInterfaceClass = USB_CLASS_HUB //匹配的字段

综上所述,match_flags 表示哪些字段匹配,而对应匹配的字段就是idVendorbInterfaceClassbDeviceClassbInterfaceClass ,这里在下面会分析如何使用。

6.2.5.3 usb_register

现在我们继续回到hub驱动注册:

#define usb_register(driver) \
	usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

此时串口一般会打印

[    1.623189] usbcore: registered new interface driver usbfs

表明此时注册的是一个接口类型的 usbfs

/**
 * usb_register_driver - register a USB interface driver
 * @new_driver: USB operations for the interface driver
 * @owner: module owner of this driver.
 * @mod_name: module name string
 *
 * Registers a USB interface driver with the USB core.  The list of
 * unattached interfaces will be rescanned whenever a new driver is
 * added, allowing the new driver to attach to any recognized interfaces.
 *
 * Return: A negative error code on failure and 0 on success.
 *
 * NOTE: if you want your driver to use the USB major number, you must call
 * usb_register_dev() to enable that functionality.  This function no longer
 * takes care of that.
 */
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
			const char *mod_name)
{
	int retval = 0;

	if (usb_disabled())
		return -ENODEV;

	//对drvwrap USB驱动包初始化
	new_driver->drvwrap.for_devices = 0;
	new_driver->drvwrap.driver.name = new_driver->name;//初始化驱动名称为“hub”
	new_driver->drvwrap.driver.bus = &usb_bus_type;//绑定总线类型为子系统usb
	new_driver->drvwrap.driver.probe = usb_probe_interface;//重要,是接口的探针函数!!!该函数内部会调用usb_serial_probe()
	new_driver->drvwrap.driver.remove = usb_unbind_interface;
	new_driver->drvwrap.driver.owner = owner;
	new_driver->drvwrap.driver.mod_name = mod_name;
	spin_lock_init(&new_driver->dynids.lock);
	INIT_LIST_HEAD(&new_driver->dynids.list);

	retval = driver_register(&new_driver->drvwrap.driver);//驱动注册
	...
	retval = usb_create_newid_files(new_driver);
	...

	pr_info("%s: registered new interface driver %s\n",
			usbcore_name, new_driver->name);//证明此时usbcore创建的新的hub是接口型设备

	...
}
EXPORT_SYMBOL_GPL(usb_register_driver);

usb_register_driver()函数内部主要涉及到两个函数,驱动注册driver_register()newid文件创建usb_create_newid_files(),接下来将逐个分析:

6.2.5.3.1 driver_register()

注意:在内核启动阶段,usb core初始化之前,依然有调用

[    0.558292]  drivers/base/driver.c:driver_register: Driver `syscon` fund by 'platform' 
[    0.606917]  drivers/base/driver.c:driver_register: Driver `dummy` fund by 'i2c' 
[    1.257772]  drivers/base/driver.c:driver_register: Driver `loongson-gpio` fund by 'platform' 
[    1.314486]  drivers/base/driver.c:driver_register: Driver `w25q16dvssig` fund by 'spi' 
[    1.363468]  drivers/base/driver.c:driver_register: Driver `ls-spi` fund by 'platform' 
[    1.397259]  drivers/base/driver.c:driver_register: Driver `ls-spi-pci` fund by 'pci' 
[    1.438725]  drivers/base/driver.c:driver_register: Driver `Generic 10G PHY` fund by 'mdio_bus' 
[    1.474069]  drivers/base/driver.c:driver_register: Driver `Generic PHY` fund by 'mdio_bus'

注意:other = driver_find(drv->name, drv->bus);//此时usb总线find的驱动名称是:usbfs

/**
 * driver_register - register driver with bus
 * @drv: driver to register
 *
 * We pass off most of the work to the bus_add_driver() call,
 * since most of the things we have to do deal with the bus
 * structures.
 */
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	if (!drv->bus->p) {
		pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
			   drv->name, drv->bus->name);
		return -EINVAL;
	}
	
	//检测总线的操作函数和驱动的操作函数是否同时存在
	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

	other = driver_find(drv->name, drv->bus);//这里查找drv->name="usbfs"驱动是否已经在drv->bus总线上注册,并增加引用计数,若已经注册,则返回提示信息
	if (other) {
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}

	ret = bus_add_driver(drv);//如果之前没注册,就在此注册
	...
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);
	...
}
EXPORT_SYMBOL_GPL(driver_register);
6.2.5.3.2 usb总线上添加hub驱动:bus_add_driver()

注意:在内核启动阶段,usb core初始化之前,依然有其他模块调用这个函数进行总线模块加载

[    0.567343]  drivers/base/bus.c:bus_add_driver,bus: 'platform': add driver syscon
[    0.615467]  drivers/base/bus.c:bus_add_driver,bus: 'i2c': add driver dummy
[    1.267419]  drivers/base/bus.c:bus_add_driver,bus: 'platform': add driver loongson-gpio
[    1.323639]  drivers/base/bus.c:bus_add_driver,bus: 'spi': add driver w25q16dvssig
[    1.372542]  drivers/base/bus.c:bus_add_driver,bus: 'platform': add driver ls-spi
[    1.406237]  drivers/base/bus.c:bus_add_driver,bus: 'pci': add driver ls-spi-pci
[    1.448573]  drivers/base/bus.c:bus_add_driver,bus: 'mdio_bus': add driver Generic 10G PHY
[    1.483567]  drivers/base/bus.c:bus_add_driver,bus: 'mdio_bus': add driver Generic PHY
/**
 * bus_add_driver - Add a driver to the bus.
 * @drv: driver.
 */
int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;

	//用于增加该bus所属的顶层bus的kobject的引用计数,返回的是其所属的顶层bus的指针。
	bus = bus_get(drv->bus);
	if (!bus)
		return -EINVAL;

	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);//分配一个驱动私有数据driver_private
	...
	klist_init(&priv->klist_devices, NULL, NULL);//初始化设备链表
	//以下相互绑定
	priv->driver = drv;//私有驱动数据绑定drv
	drv->p = priv;//drv->p驱动绑定priv
	priv->kobj.kset = bus->p->drivers_kset;//将priv->kobj.kset当前内核集合指向bus->p->drivers_kset内核集合(该集合在usb总线注册bus_register()描述)
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	...

	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);//将当前注册的priv->knode_bus节点加入到全局bus->p->klist_drivers链表中
	if (drv->bus->p->drivers_autoprobe) {
		if (driver_allows_async_probing(drv)) {//在usb总线注册时对该变量drivers_autoprobe初始化为1了
			pr_debug("bus: '%s': probing driver %s asynchronously\n",
				drv->bus->name, drv->name);
			async_schedule(driver_attach_async, drv);
		} else {
			error = driver_attach(drv);
			...
		}
	}
	module_add_driver(drv->owner, drv);//驱动模块注册时会用到,此时加载的模块名字是usbcore

	error = driver_create_file(drv, &driver_attr_uevent);
	...
	error = driver_add_groups(drv, bus->drv_groups);
	...

	if (!drv->suppress_bind_attrs) {
		error = add_bind_files(drv);
		...
}
6.2.5.3.3 hub内核对象初始化 kobject_init_and_add

这里重点分析下内核对象初始化的内部细节,

error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);

其中,driver_ktype为驱动的内核集合类型,主要用来对内核对象的操作

static struct kobj_type driver_ktype = {
	.sysfs_ops	= &driver_sysfs_ops,
	.release	= driver_release,
};
static const struct sysfs_ops driver_sysfs_ops = {
	.show	= drv_attr_show,
	.store	= drv_attr_store,
};
static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
			     char *buf)
{
	struct driver_attribute *drv_attr = to_drv_attr(attr);//获取驱动的属性结构体
	struct driver_private *drv_priv = to_driver(kobj);//获取驱动的私有数据
	ssize_t ret = -EIO;

	if (drv_attr->show)//驱动属性文件是否不为NULL
		ret = drv_attr->show(drv_priv->driver, buf);//显示驱动
	return ret;
}

static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,
			      const char *buf, size_t count)
{
	struct driver_attribute *drv_attr = to_drv_attr(attr);
	struct driver_private *drv_priv = to_driver(kobj);
	ssize_t ret = -EIO;

	if (drv_attr->store)
		ret = drv_attr->store(drv_priv->driver, buf, count);//存储
	return ret;
}

分析完了驱动内核对象类型操作,再回到内核对象的初始化

6.2.5.3.4 kobject_init_and_add
/**
 * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 * @parent: pointer to the parent of this kobject.
 * @fmt: the name of the kobject.
 *
 * This function combines the call to kobject_init() and
 * kobject_add().  The same type of error handling after a call to
 * kobject_add() and kobject lifetime rules are the same here.
 */
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
			 struct kobject *parent, const char *fmt, ...)
{
	va_list args;
	int retval;

	kobject_init(kobj, ktype);//初始化内核对象(绑定内核对象kobject的内核对象类型接口为ktype)

	va_start(args, fmt);
	retval = kobject_add_varg(kobj, parent, fmt, args);//内核对象增加,即创建hub目录
	va_end(args);

	return retval;
}
EXPORT_SYMBOL_GPL(kobject_init_and_add);

6.2.5.3.5 kobject_init
/**
 * kobject_init - initialize a kobject structure
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 *
 * This function will properly initialize a kobject such that it can then
 * be passed to the kobject_add() call.
 *
 * After this function is called, the kobject MUST be cleaned up by a call
 * to kobject_put(), not by a call to kfree directly to ensure that all of
 * the memory is cleaned up properly.
 */
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
	char *err_str;

	if (!kobj) {
		err_str = "invalid kobject pointer!";
		goto error;
	}
	if (!ktype) {
		err_str = "must have a ktype to be initialized properly!\n";
		goto error;
	}
	if (kobj->state_initialized) {
		/* do not error out as sometimes we can recover */
		pr_err("kobject (%p): tried to init an initialized object, something is seriously wrong.\n",
		       kobj);
		dump_stack();
	}

	kobject_init_internal(kobj);
	kobj->ktype = ktype;//绑定内核对象的内核类型(内核对象的操作接口)
	return;
	...
}
EXPORT_SYMBOL(kobject_init);
6.2.5.3.6 kobject_init_internal
static void kobject_init_internal(struct kobject *kobj)
{
	if (!kobj)
		return;
	kref_init(&kobj->kref);
	INIT_LIST_HEAD(&kobj->entry);//表示父内核对象下挂接的子内核对象
	kobj->state_in_sysfs = 0;//sysfs没有初始化(涉及到sysfs文件系统)
	kobj->state_add_uevent_sent = 0;//用户层事件是否发送
	kobj->state_remove_uevent_sent = 0;//用户层事件是否移除
	kobj->state_initialized = 1;//kobj对象初始化完成标识
}
6.2.5.3.7 kobject_init_and_add()->kobject_add_varg
static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,
					   struct kobject *parent,
					   const char *fmt, va_list vargs)
{
	int retval;

	retval = kobject_set_name_vargs(kobj, fmt, vargs);
	if (retval) {
		pr_err("kobject: can not set name properly!\n");
		return retval;
	}
	kobj->parent = parent;
	return kobject_add_internal(kobj);//内核对象增加,在sys文件系统下创建目录hub,绝对路径/sys/bus/usb/drivers/hub
}
6.2.5.3.8 kobject_add_internal
static int kobject_add_internal(struct kobject *kobj)
{
	int error = 0;
	struct kobject *parent;

	if (!kobj)
		return -ENOENT;

	...

	parent = kobject_get(kobj->parent);

	/* join kset if set, use it as parent if we do not already have one */
	if (kobj->kset) {//kset为usb集合(因为kset集合是被内核对象绑定,所以下面可以获取内核对象)
		if (!parent)
			parent = kobject_get(&kobj->kset->kobj);//即为usb内核对象
		kobj_kset_join(kobj);
		kobj->parent = parent;
	}

	pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
		 kobject_name(kobj), kobj, __func__,
		 parent ? kobject_name(parent) : "<NULL>",
		 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

	error = create_dir(kobj);
	if (error) {
		kobj_kset_leave(kobj);
		kobject_put(parent);
		kobj->parent = NULL;

		/* be noisy on error issues */
		if (error == -EEXIST)
			pr_err("%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory.\n",
			       __func__, kobject_name(kobj));
		else
			pr_err("%s failed for %s (error: %d parent: %s)\n",
			       __func__, kobject_name(kobj), error,
			       parent ? kobject_name(parent) : "'none'");
	} else
		kobj->state_in_sysfs = 1;//表示kobject对象在sysfs中的状态

	return error;
}

到这里完成了hub内核对象的初始化和添加,并将该节点添加到了usb总线的bus->p->klist_drivers驱动链表中,同时创建目录hub,绝对路径为/sys/bus/usb/hub;接下来将分析driver_attach()

6.2.5.3.8 驱动绑定driver_attach()

路径:bus_add_driver()-->driver_attach(drv)

/**
 * driver_attach - try to bind driver to devices.
 * @drv: driver.
 *
 * Walk the list of devices that the bus has on it and try to
 * match the driver with each one.  If driver_probe_device()
 * returns 0 and the @dev->driver is set, we've found a
 * compatible pair.
 */
int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);
/**
 * bus_for_each_dev - device iterator.
 * @bus: bus type.
 * @start: device to start iterating from.
 * @data: data for the callback.
 * @fn: function to be called for each device.
 *
 * Iterate over @bus's list of devices, and call @fn for each,
 * passing it @data. If @start is not NULL, we use that device to
 * begin iterating from.
 *
 * We check the return of @fn each time. If it returns anything
 * other than 0, we break out and return that value.
 *
 * NOTE: The device that returns a non-zero value is not retained
 * in any way, nor is its refcount incremented. If the caller needs
 * to retain this data, it should do so, and increment the reference
 * count in the supplied callback.
 */
int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus || !bus->p)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));//设备通过bus_add_device(...)添加时,最终会将设备加入到bus->p->klist_devices设备链表中
	while (!error && (dev = next_device(&i)))
		error = fn(dev, data);//回调接口函数__driver_attach
	klist_iter_exit(&i);
	return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);

bus_for_each_dev()函数内部主要是遍历usb总线的设备链表(由于本次注册的是hub驱动),需找与本次注册的hub驱动相匹配;

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
	int ret;

	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */
	//当设备和驱动的名字不匹配的时候返回的是0,然后就会调用下面的return 0;
	ret = driver_match_device(drv, dev);//驱动和设备是否匹配,不匹配将退出,bus_for_each_dev函数继续下一个设备来匹配-->usb_device_match(dev, drv)
	if (ret == 0) {
		/* no match */
		return 0;
	} else if (ret == -EPROBE_DEFER) {
		dev_dbg(dev, "Device match requests probe deferral\n");
		driver_deferred_probe_add(dev);
	} else if (ret < 0) {
		dev_dbg(dev, "Bus failed to match device: %d", ret);
		return ret;
	} /* ret > 0 means positive match */

	if (dev->parent && dev->bus->need_parent_lock)
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->p->dead && !dev->driver)
		driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的probe函数
	device_unlock(dev);
	if (dev->parent && dev->bus->need_parent_lock)
		device_unlock(dev->parent);
	...
}
6.2.5.3.9 driver_match_device()

static inline int driver_match_device(struct device_driver *drv, struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;//调用match函数,如果没用则默认返回1  -->usb_device_match(dev, drv)
}

通过上面的usb_register_driver()函数分析,得知这里的drvnew_driver->drvwrap.driver,再次贴上该代码

	new_driver->drvwrap.driver.name = (char *) new_driver->name; //初始化驱动名称为“hub”
	new_driver->drvwrap.driver.bus = &usb_bus_type; //绑定总线类型为子系统usb
	new_driver->drvwrap.driver.probe = usb_probe_interface; //重要,是接口的探针函数!!!该函数内部会调用usb_serial_probe()
	new_driver->drvwrap.driver.remove = usb_unbind_interface;
	new_driver->drvwrap.driver.owner = owner;
	new_driver->drvwrap.driver.mod_name = mod_name;

所以driver_match_device()函数内部drv->bus=&usb_bus_type,即会调用 drv->bus->match(dev, drv)

struct bus_type usb_bus_type = {
	.name =		"usb",
	.match =	usb_device_match,
	.uevent =	usb_uevent,
	.need_parent_lock =	true,
};
6.2.5.3.10 usb_bus_type 中的 usb_device_match
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
	/* devices and interfaces are handled separately */
	if (is_usb_device(dev)) { //是否是usb设备
 
		/* interface drivers never match devices */
		if (!is_usb_device_driver(drv))
			return 0;
 
		/* TODO: Add real matching code */
		return 1;
 
	} else if (is_usb_interface(dev)) { //是否是usb接口
		struct usb_interface *intf;
		struct usb_driver *usb_drv;
		const struct usb_device_id *id;
 
		/* device drivers never match interfaces */
		if (is_usb_device_driver(drv))
		...
		intf = to_usb_interface(dev);
		usb_drv = to_usb_driver(drv);
 
		id = usb_match_id(intf, usb_drv->id_table);
		...
 
		id = usb_match_dynamic_id(intf, usb_drv);
		...
	}
 
	return 0;
}

usb_device_match函数内部是usb设备或接口将执行不同的操作,我们在usb_register()函数注册时并未绑定类型,所以这里直接返回0,也就不会执行相应的probe探测函数了。

intf->dev.type = &usb_if_device_type 将在usb_set_configuration()内部初始化,dev->dev.type = &usb_device_type将在usb_alloc_dev()内部初始化

6.2.5.3.11 驱动模块增加:module_add_driver()

Hub驱动注册到usb总线上,并且这个时候usb总线上只有hub一个驱动,所以上面调用usb_device_match函数时将无法在对应的usb总线上匹配到设备,即该函数module_add_driver将不会被执行!不过为了后续再调用到该函数时不解,这里继续分析

module_add_driver(drv->owner, drv);
void module_add_driver(struct module *mod, struct device_driver *drv)
{
	char *driver_name;
	int no_warn;
	struct module_kobject *mk = NULL;
 
	if (!drv)
		return;
 
	if (mod) //mod为真
		mk = &mod->mkobj;
	else if (drv->mod_name) { //驱动名称,假设是usb主机控制器"ehci_hcd"
		struct kobject *mkobj;
 
		/* Lookup built-in module entry in /sys/modules */
		mkobj = kset_find_obj(module_kset, drv->mod_name); //根据mod_name,在module_kset内核集合上查找是否有相同的驱动
		if (mkobj) {
			mk = container_of(mkobj, struct module_kobject, kobj); //通过结构体成员mkobj,获取对应的module_kobject结构体指针
			/* remember our module structure */
			drv->p->mkobj = mk;
			/* kset_find_obj took a reference */
			kobject_put(mkobj);
		}
	}
 	...
	/* Don't check return codes; these calls are idempotent */
	no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module"); //创建module符号链接,即/sys/bus/usb/drivers/hub/module
	driver_name = make_driver_name(drv); //driver_name ="usb:hub"
	if (driver_name) {
		module_create_drivers_dir(mk); //创建目录/sys/bus/usb/drivers/hub/module/usb:hub
		no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
					    driver_name);
		kfree(driver_name);
	}
}
6.2.6 hub驱动初始化:usb_hub_init
6.2.5.1 usb hub流程图

在这里插入图片描述

6.2.5.2 hub驱动初始化:usb_hub_init

usb子系统中所有和hub相关的都交由一个内核线程表征, 通过 static struct workqueue_struct *hub_wq 来管理。

int usb_hub_init(void)
{
	//1. 注册usb hub驱动
	if (usb_register(&hub_driver) < 0) {//注册hub驱动到usb子系统总线上
		printk(KERN_ERR "%s: can't register hub driver\n",
			usbcore_name);
		return -1;
	}

	/*
	 * The workqueue needs to be freezable to avoid interfering with
	 * USB-PERSIST port handover. Otherwise it might see that a full-speed
	 * device was gone before the EHCI controller had handed its port
	 * over to the companion full-speed controller.
	 */
	 //
	hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);//创建hub内核线程
	...

	/* Fall through if kernel_thread failed */
	usb_deregister(&hub_driver);
	...
}

usb_hub_init()就做两件事:

  • 注册驱动–针对hub接口设备的驱动
  • 创建工作队列usb_hub_wq

hub_driver定义如下:

static struct usb_driver hub_driver = {
    .name =         "hub",
    .probe =        hub_probe,
    .disconnect =   hub_disconnect,
    .suspend =      hub_suspend,
    .resume =       hub_resume,
    .reset_resume = hub_reset_resume,
    .pre_reset =    hub_pre_reset,
    .post_reset =   hub_post_reset,
    .unlocked_ioctl = hub_ioctl,
    .id_table =     hub_id_table,
    .supports_autosuspend = 1,
};

由该定义可知,usb hub驱动是一个接口设备驱动,驱动名称为“hub”,id_table成员为hub_id_table,probe函数为hub_probe,当一个hub设备添加到内核设备驱动模型中的时候,如果hub设备测参数和hub_id_table中的参数匹配,就会调用到hub_probe函数。

hub本身也是个usb设备, 跟普通的U盘使用的都是同样的结构体, 当有hub设备被创建时,hub驱动的probe()将会match调用, 那问题来了,一个普通设备是被hub创建的, 那hub设备是谁创建的呢?很显然最初的root hub设备必须是静态创建的, 且这部分代码没放在hub.c, 而是放到了hcd.c, 可以看出一个Host必然有一个root hub, 是绑定的!

6.2.5.2.1 hub驱动注册:usb_hub_init–>usb_register
#define usb_register(driver) \
	usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

注意:retval = driver_register(&new_driver->drvwrap.driver);//此时usb总线find的驱动名称是:hub

/**
 * usb_register_driver - register a USB interface driver
 * @new_driver: USB operations for the interface driver
 * @owner: module owner of this driver.
 * @mod_name: module name string
 *
 * Registers a USB interface driver with the USB core.  The list of
 * unattached interfaces will be rescanned whenever a new driver is
 * added, allowing the new driver to attach to any recognized interfaces.
 *
 * Return: A negative error code on failure and 0 on success.
 *
 * NOTE: if you want your driver to use the USB major number, you must call
 * usb_register_dev() to enable that functionality.  This function no longer
 * takes care of that.
 */
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
			const char *mod_name)
{
	int retval = 0;

	if (usb_disabled())
		return -ENODEV;

	//对drvwrap USB驱动包初始化
	new_driver->drvwrap.for_devices = 0;
	new_driver->drvwrap.driver.name = new_driver->name;//初始化驱动名称为“hub”
	new_driver->drvwrap.driver.bus = &usb_bus_type;//绑定总线类型为子系统usb
	new_driver->drvwrap.driver.probe = usb_probe_interface;//重要,是接口的探针函数!!!该函数内部会调用usb_serial_probe()
	new_driver->drvwrap.driver.remove = usb_unbind_interface;
	new_driver->drvwrap.driver.owner = owner;
	new_driver->drvwrap.driver.mod_name = mod_name;
	spin_lock_init(&new_driver->dynids.lock);
	INIT_LIST_HEAD(&new_driver->dynids.list);

	retval = driver_register(&new_driver->drvwrap.driver);//此时usb总线find的驱动名称是:hub
	...

	retval = usb_create_newid_files(new_driver);
	...
	...
	...
}
EXPORT_SYMBOL_GPL(usb_register_driver);

usb_register_driver()函数内部主要涉及到两个函数,驱动注册driver_register()newid文件创建usb_create_newid_files()

6.2.5.2.2 usb_register_driver–>driver_register

此时串口一般会打印

[    1.715461] usbcore: registered new interface driver hub

表明此时注册的是一个接口类型的 hub

/**
 * driver_register - register driver with bus
 * @drv: driver to register
 *
 * We pass off most of the work to the bus_add_driver() call,
 * since most of the things we have to do deal with the bus
 * structures.
 */
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	...
	
	//检测总线的操作函数和驱动的操作函数是否同时存在
	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		...

	other = driver_find(drv->name, drv->bus);//这里查找drv->name="hub"驱动是否已经在drv->bus总线上注册,并增加引用计数,若已经注册,则返回提示信息
	...
	ret = bus_add_driver(drv);//如果之前没注册,就在此注册
	...
	ret = driver_add_groups(drv, drv->groups);
	...
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);
	....
}
EXPORT_SYMBOL_GPL(driver_register);
6.2.5.2.3 driver_register–>bus_add_driver()

usb总线上添加hub驱动

/**
 * bus_add_driver - Add a driver to the bus.
 * @drv: driver.
 */
int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;

	//用于增加该bus所属的顶层bus的kobject的引用计数,返回的是其所属的顶层bus的指针。
	bus = bus_get(drv->bus);
	if (!bus)
		return -EINVAL;

	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);//分配一个驱动私有数据driver_private
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
	klist_init(&priv->klist_devices, NULL, NULL);//初始化设备链表
	//以下相互绑定
	priv->driver = drv;//私有驱动数据绑定drv
	drv->p = priv;//drv->p驱动绑定priv
	priv->kobj.kset = bus->p->drivers_kset;//将priv->kobj.kset当前内核集合指向bus->p->drivers_kset内核集合(该集合在usb总线注册bus_register()描述)
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;

	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);//将当前注册的priv->knode_bus节点加入到全局bus->p->klist_drivers链表中
	if (drv->bus->p->drivers_autoprobe) {
		if (driver_allows_async_probing(drv)) {//在usb总线注册时对该变量drivers_autoprobe初始化为1了
			pr_debug("bus: '%s': probing driver %s asynchronously\n",
				drv->bus->name, drv->name);
			async_schedule(driver_attach_async, drv);
		} else {
			error = driver_attach(drv);//
			if (error)
				goto out_unregister;
		}
	}
	module_add_driver(drv->owner, drv);//驱动模块注册时会用到,此时加载的模块名字是usbcore

	error = driver_create_file(drv, &driver_attr_uevent);
	...
	error = driver_add_groups(drv, bus->drv_groups);
	...
	...
	...
}
6.2.5.2.4 bus_add_driver–>kobject_init_and_add

hub内核对象初始化,这里重点分析下内核对象初始化的内部细节,

error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);

其中,driver_ktype为驱动的内核集合类型,主要用来对内核对象的操作

static struct kobj_type driver_ktype = {
	.sysfs_ops	= &driver_sysfs_ops,
	.release	= driver_release,
};
static const struct sysfs_ops driver_sysfs_ops = {
	.show	= drv_attr_show,
	.store	= drv_attr_store,
};
static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
			     char *buf)
{
	struct driver_attribute *drv_attr = to_drv_attr(attr);//获取驱动的属性结构体
	struct driver_private *drv_priv = to_driver(kobj);//获取驱动的私有数据
	ssize_t ret = -EIO;

	if (drv_attr->show)//驱动属性文件是否不为NULL
		ret = drv_attr->show(drv_priv->driver, buf);//显示驱动
	return ret;
}

static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,
			      const char *buf, size_t count)
{
	struct driver_attribute *drv_attr = to_drv_attr(attr);
	struct driver_private *drv_priv = to_driver(kobj);
	ssize_t ret = -EIO;

	if (drv_attr->store)
		ret = drv_attr->store(drv_priv->driver, buf, count);//存储
	return ret;
}

分析完了驱动内核对象类型操作,再回到内核对象的初始化

/**
 * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 * @parent: pointer to the parent of this kobject.
 * @fmt: the name of the kobject.
 *
 * This function combines the call to kobject_init() and
 * kobject_add().  The same type of error handling after a call to
 * kobject_add() and kobject lifetime rules are the same here.
 */
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
			 struct kobject *parent, const char *fmt, ...)
{
	va_list args;
	int retval;

	kobject_init(kobj, ktype);//初始化内核对象(绑定内核对象kobject的内核对象类型接口为ktype)

	va_start(args, fmt);
	retval = kobject_add_varg(kobj, parent, fmt, args);//内核对象增加,即创建hub目录
	va_end(args);

	return retval;
}
EXPORT_SYMBOL_GPL(kobject_init_and_add);

/**
 * kobject_init - initialize a kobject structure
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 *
 * This function will properly initialize a kobject such that it can then
 * be passed to the kobject_add() call.
 *
 * After this function is called, the kobject MUST be cleaned up by a call
 * to kobject_put(), not by a call to kfree directly to ensure that all of
 * the memory is cleaned up properly.
 */
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
	char *err_str;

	if (!kobj) {
		err_str = "invalid kobject pointer!";
		goto error;
	}
	if (!ktype) {
		err_str = "must have a ktype to be initialized properly!\n";
		goto error;
	}
	if (kobj->state_initialized) {
		/* do not error out as sometimes we can recover */
		pr_err("kobject (%p): tried to init an initialized object, something is seriously wrong.\n",
		       kobj);
		dump_stack();
	}

	kobject_init_internal(kobj);
	kobj->ktype = ktype;//绑定内核对象的内核类型(内核对象的操作接口)
	return;

error:
	pr_err("kobject (%p): %s\n", kobj, err_str);
	dump_stack();
}
EXPORT_SYMBOL(kobject_init);
static void kobject_init_internal(struct kobject *kobj)
{
	if (!kobj)
		return;
	kref_init(&kobj->kref);
	INIT_LIST_HEAD(&kobj->entry);//表示父内核对象下挂接的子内核对象
	kobj->state_in_sysfs = 0;//sysfs没有初始化(涉及到sysfs文件系统)
	kobj->state_add_uevent_sent = 0;//用户层事件是否发送
	kobj->state_remove_uevent_sent = 0;//用户层事件是否移除
	kobj->state_initialized = 1;//kobj对象初始化完成标识
}
6.2.5.2.5 kobject_init_and_add()->kobject_add_varg
static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,
					   struct kobject *parent,
					   const char *fmt, va_list vargs)
{
	int retval;

	retval = kobject_set_name_vargs(kobj, fmt, vargs);
	if (retval) {
		pr_err("kobject: can not set name properly!\n");
		return retval;
	}
	kobj->parent = parent;
	return kobject_add_internal(kobj);//内核对象增加,在sys文件系统下创建目录hub,绝对路径/sys/bus/usb/drivers/hub
}
6.2.5.2.6 kobject_add_varg–>kobject_add_internal
static int kobject_add_internal(struct kobject *kobj)
{
	int error = 0;
	struct kobject *parent;

	...
	parent = kobject_get(kobj->parent);

	/* join kset if set, use it as parent if we do not already have one */
	if (kobj->kset) {//kset为usb集合(因为kset集合是被内核对象绑定,所以下面可以获取内核对象)
		if (!parent)
			parent = kobject_get(&kobj->kset->kobj);//即为usb内核对象
		kobj_kset_join(kobj);
		kobj->parent = parent;
	}

	pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
		 kobject_name(kobj), kobj, __func__,
		 parent ? kobject_name(parent) : "<NULL>",
		 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");

	error = create_dir(kobj);
	if (error) {
		kobj_kset_leave(kobj);
		kobject_put(parent);
		kobj->parent = NULL;

		/* be noisy on error issues */
		if (error == -EEXIST)
			pr_err("%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory.\n",
			       __func__, kobject_name(kobj));
		else
			pr_err("%s failed for %s (error: %d parent: %s)\n",
			       __func__, kobject_name(kobj), error,
			       parent ? kobject_name(parent) : "'none'");
	} else
		kobj->state_in_sysfs = 1;//表示kobject对象在sysfs中的状态

	return error;
}

到这里完成了hub内核对象的初始化和添加,并将该节点添加到了usb总线的bus->p->klist_drivers驱动链表中,同时创建目录hub,绝对路径为/sys/bus/usb/hub

6.2.5.2.8 驱动模块增加:module_add_driver()

Hub驱动注册到usb总线上,并且这个时候usb总线上只有hub一个驱动,所以上面调用usb_device_match函数时将无法在对应的usb总线上匹配到设备,即该函数module_add_driver将不会被执行!不过为了后续再调用到该函数时不解,这里继续分析

module_add_driver(drv->owner, drv);
void module_add_driver(struct module *mod, struct device_driver *drv)
{
	char *driver_name;
	int no_warn;
	struct module_kobject *mk = NULL;
 
	if (!drv)
		return;
 
	if (mod) //mod为真
		mk = &mod->mkobj;
	else if (drv->mod_name) { //驱动名称,假设是usb主机控制器"ehci_hcd"
		struct kobject *mkobj;
 
		/* Lookup built-in module entry in /sys/modules */
		mkobj = kset_find_obj(module_kset, drv->mod_name); //根据mod_name,在module_kset内核集合上查找是否有相同的驱动
		if (mkobj) {
			mk = container_of(mkobj, struct module_kobject, kobj); //通过结构体成员mkobj,获取对应的module_kobject结构体指针
			/* remember our module structure */
			drv->p->mkobj = mk;
			/* kset_find_obj took a reference */
			kobject_put(mkobj);
		}
	}
 	...
	/* Don't check return codes; these calls are idempotent */
	no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module"); //创建module符号链接,即/sys/bus/usb/drivers/hub/module
	driver_name = make_driver_name(drv); //driver_name ="usb:hub"
	if (driver_name) {
		module_create_drivers_dir(mk); //创建目录/sys/bus/usb/drivers/hub/module/usb:hub
		no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
					    driver_name);
		kfree(driver_name);
	}
}
6.2.5.2.8 申请hub工作队列

注册了hub_driver之后,申请hub工作队列。

hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0);

在hub的中断轮询的时候,在回调函数hub_irq中就会向hub_wq中添加任务。在该任务中再去做hub端口检查,新设备枚举等工作。

6.2.6 usb_init–>usb_register_device_driver

注意:retval = driver_register(&new_udriver->drvwrap.driver);//此时usb总线find的驱动名称是:usb

/**
 * usb_register_device_driver - register a USB device (not interface) driver
 * @new_udriver: USB operations for the device driver
 * @owner: module owner of this driver.
 *
 * Registers a USB device driver with the USB core.  The list of
 * unattached devices will be rescanned whenever a new driver is
 * added, allowing the new driver to attach to any recognized devices.
 *
 * Return: A negative error code on failure and 0 on success.
 */
int usb_register_device_driver(struct usb_device_driver *new_udriver,
		struct module *owner)
{
	int retval = 0;

	if (usb_disabled())
		...

	new_udriver->drvwrap.for_devices = 1;
	new_udriver->drvwrap.driver.name = new_udriver->name;
	new_udriver->drvwrap.driver.bus = &usb_bus_type;
	new_udriver->drvwrap.driver.probe = usb_probe_device;
	new_udriver->drvwrap.driver.remove = usb_unbind_device;
	new_udriver->drvwrap.driver.owner = owner;

	retval = driver_register(&new_udriver->drvwrap.driver);//此时usb总线find的驱动名称是:usb
	//klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	...
}
EXPORT_SYMBOL_GPL(usb_register_device_driver);

初始化usb_generic_driverdrvwrapdrivername, bus类型,以及probe函数,并注册到usb总线klist_drivers链表上(driver_register–>bus_add_driver–>klist_add_tail)。

6.2.6.1 driver_register()

注意:other = driver_find(drv->name, drv->bus);//此时usb总线find的驱动名称是:usb

此时串口一般会打印

[    1.793300] usbcore: registered new device driver usb

表明此时注册的是一个设备类型的 usb

/**
 * driver_register - register driver with bus
 * @drv: driver to register
 *
 * We pass off most of the work to the bus_add_driver() call,
 * since most of the things we have to do deal with the bus
 * structures.
 */
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	...
	//检测总线的操作函数和驱动的操作函数是否同时存在
	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

	other = driver_find(drv->name, drv->bus);//这里查找drv->name="usb"驱动是否已经在drv->bus总线上注册,并增加引用计数,若已经注册,则返回提示信息
	if (other) {
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}

	ret = bus_add_driver(drv);//如果之前没注册,就在此注册
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);

	return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
6.2.6.2 usb总线上添加hub驱动:bus_add_driver()
/**
 * bus_add_driver - Add a driver to the bus.
 * @drv: driver.
 */
int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;

	//用于增加该bus所属的顶层bus的kobject的引用计数,返回的是其所属的顶层bus的指针。
	bus = bus_get(drv->bus);
	...

	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);//分配一个驱动私有数据driver_private
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
	klist_init(&priv->klist_devices, NULL, NULL);//初始化设备链表
	//以下相互绑定
	priv->driver = drv;//私有驱动数据绑定drv
	drv->p = priv;//drv->p驱动绑定priv
	priv->kobj.kset = bus->p->drivers_kset;//将priv->kobj.kset当前内核集合指向bus->p->drivers_kset内核集合(该集合在usb总线注册bus_register()描述)
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	...

	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);//将当前注册的priv->knode_bus节点加入到全局bus->p->klist_drivers链表中
	if (drv->bus->p->drivers_autoprobe) {
		if (driver_allows_async_probing(drv)) {//在usb总线注册时对该变量drivers_autoprobe初始化为1了
			pr_debug("bus: '%s': probing driver %s asynchronously\n",
				drv->bus->name, drv->name);
			async_schedule(driver_attach_async, drv);
		} else {
			error = driver_attach(drv);
			...
		}
	}
	module_add_driver(drv->owner, drv);//**从该函数这开始,本次hub驱动将不会执行下面的函数了**

	error = driver_create_file(drv, &driver_attr_uevent);
	...
	error = driver_add_groups(drv, bus->drv_groups);
	...

	if (!drv->suppress_bind_attrs) {
		error = add_bind_files(drv);
		...
}
6.2.6.3 hub内核对象初始化 kobject_init_and_add

这里重点分析下内核对象初始化的内部细节,

error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);

其中,driver_ktype为驱动的内核集合类型,主要用来对内核对象的操作

static struct kobj_type driver_ktype = {
	.sysfs_ops	= &driver_sysfs_ops,
	.release	= driver_release,
};
static const struct sysfs_ops driver_sysfs_ops = {
	.show	= drv_attr_show,
	.store	= drv_attr_store,
};
static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
			     char *buf)
{
	struct driver_attribute *drv_attr = to_drv_attr(attr);//获取驱动的属性结构体
	struct driver_private *drv_priv = to_driver(kobj);//获取驱动的私有数据
	ssize_t ret = -EIO;

	if (drv_attr->show)//驱动属性文件是否不为NULL
		ret = drv_attr->show(drv_priv->driver, buf);//显示驱动
	return ret;
}

static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,
			      const char *buf, size_t count)
{
	struct driver_attribute *drv_attr = to_drv_attr(attr);
	struct driver_private *drv_priv = to_driver(kobj);
	ssize_t ret = -EIO;

	if (drv_attr->store)
		ret = drv_attr->store(drv_priv->driver, buf, count);//存储
	return ret;
}

分析完了驱动内核对象类型操作,再回到内核对象的初始化

6.2.6.4 kobject_init_and_add
/**
 * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 * @parent: pointer to the parent of this kobject.
 * @fmt: the name of the kobject.
 *
 * This function combines the call to kobject_init() and
 * kobject_add().  The same type of error handling after a call to
 * kobject_add() and kobject lifetime rules are the same here.
 */
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
			 struct kobject *parent, const char *fmt, ...)
{
	va_list args;
	int retval;

	kobject_init(kobj, ktype);//初始化内核对象(绑定内核对象kobject的内核对象类型接口为ktype)

	va_start(args, fmt);
	retval = kobject_add_varg(kobj, parent, fmt, args);//内核对象增加,即创建hub目录
	va_end(args);

	return retval;
}
EXPORT_SYMBOL_GPL(kobject_init_and_add);

6.2.6.5 kobject_init
/**
 * kobject_init - initialize a kobject structure
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 *
 * This function will properly initialize a kobject such that it can then
 * be passed to the kobject_add() call.
 *
 * After this function is called, the kobject MUST be cleaned up by a call
 * to kobject_put(), not by a call to kfree directly to ensure that all of
 * the memory is cleaned up properly.
 */
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
	char *err_str;

	...

	kobject_init_internal(kobj);
	kobj->ktype = ktype;//绑定内核对象的内核类型(内核对象的操作接口)
	return;
	...
}
EXPORT_SYMBOL(kobject_init);
6.2.6.6 kobject_init_internal
static void kobject_init_internal(struct kobject *kobj)
{
	if (!kobj)
		return;
	kref_init(&kobj->kref);
	INIT_LIST_HEAD(&kobj->entry);//表示父内核对象下挂接的子内核对象
	kobj->state_in_sysfs = 0;//sysfs没有初始化(涉及到sysfs文件系统)
	kobj->state_add_uevent_sent = 0;//用户层事件是否发送
	kobj->state_remove_uevent_sent = 0;//用户层事件是否移除
	kobj->state_initialized = 1;//kobj对象初始化完成标识
}
6.2.6.7 kobject_init_and_add()->kobject_add_varg
static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,
					   struct kobject *parent,
					   const char *fmt, va_list vargs)
{
	int retval;

	retval = kobject_set_name_vargs(kobj, fmt, vargs);
	if (retval) {
		pr_err("kobject: can not set name properly!\n");
		return retval;
	}
	kobj->parent = parent;
	return kobject_add_internal(kobj);//内核对象增加,在sys文件系统下创建目录hub,绝对路径/sys/bus/usb/drivers/hub
}
6.2.5.3.8 kobject_add_internal
static int kobject_add_internal(struct kobject *kobj)
{
	int error = 0;
	struct kobject *parent;

	...

	parent = kobject_get(kobj->parent);

	/* join kset if set, use it as parent if we do not already have one */
	if (kobj->kset) {//kset为usb集合(因为kset集合是被内核对象绑定,所以下面可以获取内核对象)
		if (!parent)
			parent = kobject_get(&kobj->kset->kobj);//即为usb内核对象
		kobj_kset_join(kobj);
		kobj->parent = parent;
	}

	...

	error = create_dir(kobj);
	if (error) {
		...
	} else
		kobj->state_in_sysfs = 1;//表示kobject对象在sysfs中的状态

	return error;
}

到这里完成了hub内核对象的初始化和添加,并将该节点添加到了usb总线的bus->p->klist_drivers驱动链表中,同时创建目录hub,绝对路径为/sys/bus/usb/hub;接下来将分析driver_attach()

6.2.5.3.9 驱动绑定driver_attach()

路径:bus_add_driver()-->driver_attach(drv)

/**
 * driver_attach - try to bind driver to devices.
 * @drv: driver.
 *
 * Walk the list of devices that the bus has on it and try to
 * match the driver with each one.  If driver_probe_device()
 * returns 0 and the @dev->driver is set, we've found a
 * compatible pair.
 */
int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);
/**
 * bus_for_each_dev - device iterator.
 * @bus: bus type.
 * @start: device to start iterating from.
 * @data: data for the callback.
 * @fn: function to be called for each device.
 *
 * Iterate over @bus's list of devices, and call @fn for each,
 * passing it @data. If @start is not NULL, we use that device to
 * begin iterating from.
 *
 * We check the return of @fn each time. If it returns anything
 * other than 0, we break out and return that value.
 *
 * NOTE: The device that returns a non-zero value is not retained
 * in any way, nor is its refcount incremented. If the caller needs
 * to retain this data, it should do so, and increment the reference
 * count in the supplied callback.
 */
int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus || !bus->p)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));//设备通过bus_add_device(...)添加时,最终会将设备加入到bus->p->klist_devices设备链表中
	while (!error && (dev = next_device(&i)))
		error = fn(dev, data);//回调接口函数__driver_attach
	klist_iter_exit(&i);
	return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);

bus_for_each_dev()函数内部主要是遍历usb总线的设备链表(由于本次注册的是hub驱动),需找与本次注册的hub驱动相匹配;

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
	int ret;

	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */
	//当设备和驱动的名字不匹配的时候返回的是0,然后就会调用下面的return 0;
	ret = driver_match_device(drv, dev);//驱动和设备是否匹配,不匹配将退出,bus_for_each_dev函数继续下一个设备来匹配-->usb_device_match(dev, drv)
	if (ret == 0) {
		/* no match */
		return 0;
	} else if (ret == -EPROBE_DEFER) {
		dev_dbg(dev, "Device match requests probe deferral\n");
		driver_deferred_probe_add(dev);
	} else if (ret < 0) {
		dev_dbg(dev, "Bus failed to match device: %d", ret);
		return ret;
	} /* ret > 0 means positive match */

	if (dev->parent && dev->bus->need_parent_lock)
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->p->dead && !dev->driver)
		driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的probe函数
	device_unlock(dev);
	if (dev->parent && dev->bus->need_parent_lock)
		device_unlock(dev->parent);

	return 0;
}
6.2.5.3.10 driver_match_device()

static inline int driver_match_device(struct device_driver *drv, struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;//调用match函数,如果没用则默认返回1  -->usb_device_match(dev, drv)
}

通过上面的usb_register_driver()函数分析,得知这里的drvnew_driver->drvwrap.driver,再次贴上该代码

	new_driver->drvwrap.driver.name = (char *) new_driver->name; //初始化驱动名称为“hub”
	new_driver->drvwrap.driver.bus = &usb_bus_type; //绑定总线类型为子系统usb
	new_driver->drvwrap.driver.probe = usb_probe_interface; //重要,是接口的探针函数!!!该函数内部会调用usb_serial_probe()
	new_driver->drvwrap.driver.remove = usb_unbind_interface;
	new_driver->drvwrap.driver.owner = owner;
	new_driver->drvwrap.driver.mod_name = mod_name;

所以driver_match_device()函数内部drv->bus=&usb_bus_type,即会调用 drv->bus->match(dev, drv)

struct bus_type usb_bus_type = {
	.name =		"usb",
	.match =	usb_device_match,
	.uevent =	usb_uevent,
	.need_parent_lock =	true,
};
6.2.5.3.11 usb_bus_type 中的 usb_device_match
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
	/* devices and interfaces are handled separately */
	if (is_usb_device(dev)) { //是否是usb设备
 
		/* interface drivers never match devices */
		if (!is_usb_device_driver(drv))
			return 0;
 
		/* TODO: Add real matching code */
		return 1;
 
	} else if (is_usb_interface(dev)) { //是否是usb接口
		struct usb_interface *intf;
		struct usb_driver *usb_drv;
		const struct usb_device_id *id;
 
		/* device drivers never match interfaces */
		if (is_usb_device_driver(drv))
			return 0;
 
		intf = to_usb_interface(dev);
		usb_drv = to_usb_driver(drv);
 
		id = usb_match_id(intf, usb_drv->id_table);
		...
 
		id = usb_match_dynamic_id(intf, usb_drv);
		...
	}
 
	return 0;
}

usb_device_match函数内部是usb设备或接口将执行不同的操作,我们在usb_register()函数注册时并未绑定类型,所以这里直接返回0,也就不会执行相应的probe探测函数了。

intf->dev.type = &usb_if_device_type 将在usb_set_configuration()内部初始化,dev->dev.type = &usb_device_type将在usb_alloc_dev()内部初始化

7. 编写Linux usb 键盘驱动的笔记

  • usb_write
    驱动将数据写到device(usb SD卡驱动)要构造urb,并且填充要写的dataurb,然后usb_submit_urb(skel->write_urb);启动写数据
  • usb_read: 驱动从device读数据到driver(输入设备,比如鼠标),读要分为两种:
    • 要不断的读数据,read动作的触发起源于硬件设备,比如鼠标移动就会自动read数据到driver;
    • 不需要不断的读数据,read动作的触发起源于驱动代码本身,驱动要读数据才去读,这种情况不需要使用urb,可使用函数usb_bulk_msg(...)进行单次的读数据;
    • 这段话摘自内核:
      usb_bulk_msg function can be very useful for doing single reads
      or writes to a device; however, if you need to read or write constantly to
      a device, it is recommended to set up your own urbs and submit them to
      the USB subsystem.
    • 实时读写数据:usb driver需要自己构造urb,并且填充,并且提交urb;
    • 非实时读写数据:usb driver可以使用函数usb_bulk_msg()来读写数据;
    • 写数据一般要构造urb,读分为两种:实时数据要构造urb,非实时调用函数usb_bulk_msg(...)进行读;
    • urb可以重复填充,也可以重复提交
    • usb读与usb写的最大不同是读跟写的端点不同,端点里包含的端点的具体地址也不同,具体表现为:
      • struct usb_endpoint_descriptor *bulk_in, *bulk_out;//输入端口,输出端口
      • usb_find_common_endpoints(interface->cur_altsetting,&bulk_in, &bulk_out, NULL, NULL);//分别获取输入,输出端点。
      • bulk_in->bEndpointAddress不同于bulk_out->bEndpointAddress,导致填充各自的urb时产生差异得以区分读写过程。
      • in_urb:创建一次,反复填充;
      • out_urb:每写一次创建一次并且填充一次且提交一次,即时创建即时销毁;
    • linux内核提供的usb设备驱动编写(包括读写过程)的框架为:drivers\usb\usb-skeleton.c
    • usb摄像头(视频,音频)是一个实时传输datausb设备,跟鼠标类似->单向实时usb输入设备,u盘属于非实时usb存贮设备(有读有写)。
    • usb设备分为两种
      • 设备发起读写请求,如usb鼠标,usb键盘;
      • 驱动程序发起读写请求,如usb磁盘;
      • 读要遵循一个规则,urb将数据从设备传输回来之后,要驱动程序处理完数据了,才可以再次提交urb,这个提交的动作可以发生在回掉函数里,也可以发生在回掉函数之外;反正就是要再次提交才可以再次运输新的数据回来;
      • 写要遵循的规则, drivers\usb\usb-skeleton.c ,这个是写usb磁盘的参考资料,但有很多细节可以参考; usb总线每传输完一次数据就会产生一次回调中断,这里面可以做数据处理,也可以重新提交urb;
    • usb设备驱动分为两种:
      • 一种是每次传输少量数据并且是硬件端发起的实时传输请求的如鼠标,键盘,触摸屏等,这类可以用中断型:usb_fill_int_urb
      • 另外一种是每次传输大量数据并且由驱动程序发起的非实时传输其你去如u盘,usb-lcd屏幕等,这类可以用块usb_fill_bulk_urb
      • 主要的区别是中断型的在fill的时候会配置dev->speed,中断型的传输速率会要求比较高;

测试usb键盘驱动

  Device Drivers > 
        HID support > 
            USB HID support > 
                < > USB HID transport layer//取消选中这个,
                //要不然就会编译linux内核自带的键盘驱动,轮不到自己写的键盘驱动进行控制键盘;

对于tty(输入输出)设备的理解

  tty0表示当前使用的控制终端
  tty1对应/dev/input/event0(输入设备0)
  tty2对应/dev/input/event1(输入设备1)
  tty3对应/dev/input/event2(输入设备2)

所以要确定自己注册的输入设备是event*几;

简单测试

  cat /dev/tty1  
  //这个测试按下了对应的字母按键之后,要再次按下enter键才可以显示之前按下的按键;

正式测试

 exec 0</dev/tty1   
 //将开发板的控制台改为自己的键盘以及自己开发的驱动,这样可以正确测试键盘的输入以及直接用控制台测试;

对exec测试命令的解释:

对于做驱动经常会使用exec来试验驱动,通过exec将-sh进程下的描述符指向我们的驱动,来实现调试
    -sh进程常用描述符号:
    0:标准输入
    1:标准输出
    2:错误信息
    5:中断服务
    exec命令使用:
    挂载:  exec [描述符号]<[设备节点路径] 
    卸载:  exec [描述符号]<&-

实例:

    exec 0</dev/tty1 //将本开发板的第1个输入设备(第一个注册的input_device,就是键盘驱动)挂接到-sh下的0号描述符,也就是标准输入来自/dev/tty1,也就是将标准输入从默认改为我们自己写的输入设备;
    exec 0<&-        //调用/dev/tty1驱动的卸载函数,系统的标准输入不要用我们自己注册的输入设备;

问2. USB设备种类非常多,为什么一接入电脑,就能识别出来?
答2. PCUSB设备都得遵守一些规范。
比如:
USB设备接入电脑后,PC机会发出"你是什么"?
USB设备就必须回答"我是xxx", 并且回答的语言必须是中文
USB总线驱动程序会发出某些命令想获取设备信息(描述符),
USB设备必须返回"描述符"给PC

问3. PC机上接有非常多的USB设备,怎么分辨它们?
USB接口只有4条线: 5V,GND,D-,D+
答3. 每一个USB设备接入PC时,USB总线驱动程序都会给它分配一个编号
接在USB总线上的每一个USB设备都有自己的编号(地址)
PC机想访问某个USB设备时,发出的命令都含有对应的编号(地址)

问4. USB设备刚接入PC时,还没有编号;那么PC怎么把"分配的编号"告诉它?
答4. 新接入的USB设备的默认编号是0,在未分配新编号前,PC使用0编号和它通信。

问5. 为什么一接入USB设备,PC机就能发现它?
答5. PCUSB口内部,D-D+接有15K的下拉电阻,未接USB设备时为低电平
USB设备的USB口内部,D-D+接有1.5K的上拉电阻;它一接入PC,就会把PC USB口的D-D+拉高,从硬件的角度通知PC有新设备接入

其他概念:

  1. USB是主从结构的
    所有的USB传输,都是从USB主机这方发起;USB设备没有"主动"通知USB主机的能力。
    例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来读数据,只能被动地等得PC机来读。

  2. USB的传输类型:
    a. 控制传输:可靠,时间有保证,比如:USB设备的识别过程
    b. 批量传输: 可靠, 时间没有保证, 比如:U
    c. 中断传输:可靠,实时,比如:USB鼠标
    d. 实时传输:不可靠,实时,比如:USB摄像头

  3. USB传输的对象:端点(endpoint)
    我们说"读U盘"、“写U盘”,可以细化为:把数据写到U盘的端点1,从U盘的端点2里读出数据
    除了端点0外,每一个端点只支持一个方向的数据传输
    端点0用于控制传输,既能输出也能输入

  4. 每一个端点都有传输类型,传输方向

  5. 术语里、程序里说的输入(IN)、输出(OUT) “都是” 基于USB主机的立场说的。
    比如鼠标的数据是从鼠标传到PC机, 对应的端点称为"输入端点"

  6. USB总线驱动程序的作用
    a. 识别USB设备
    b. 查找并安装对应的设备驱动程序
    c. 提供USB读写函数

  • USB总线驱动程序负责:识别USB设备, 给USB设备找到对应的驱动程序

USB驱动程序框架:

app:   
-------------------------------------------
          USB设备驱动程序      // 知道数据含义
内核 --------------------------------------
          USB总线驱动程序      // 1. 识别, 
          					 // 2. 找到匹配的设备驱动, 
          					 // 3. 提供USB读写函数 (它不知道数据含义)
-------------------------------------------
           USB主机控制器
           UHCI OHCI EHCI
硬件        -----------
              USB设备
UHCI: intel,     低速(1.5Mbps)/全速(12Mbps)
OHCI: microsoft  低速/全速
EHCI:            高速(480Mbps)

USB总线驱动程序的作用

  1. 识别USB设备
    1.1 分配地址
    1.2 并告诉USB设备(set address)
    1.3 发出命令获取描述符
    描述符的信息可以在include\linux\usb\Ch9.h看到

  2. 查找并安装对应的设备驱动程序

  3. 提供USB读写函数

hub_probe
    INIT_WORK(&hub->events, hub_event);    
    hub_configure
        hub_activate    
            kick_hub_wq
                queue_work(hub_wq, &hub->events)
                    hub_event
                        port_event
                            hub_port_connect_change
                                hub_port_connect
                                    hub_port_init
                                        if (udev->speed < USB_SPEED_SUPER)
                                            dev_info(&udev->dev,"%s %s USB device number %d using %s\n",(udev->config) ? "reset" : "new", speed,devnum, driver_name);

USB设备驱动的核心有两点

1.设备驱动的框架
usb_device: usb总线驱动根据插入的usb设备,1:从这个设备中读取设备描述符(包括设备类型是鼠标还是键盘,还有存贮芯片的大小,厂家id,设备id等等),
2:并且总线驱动给插入的usb设备分配编号(slave id),

总线驱动根据12两点信息为该设备构造了一个usb_device并且挂到usb_bus_type总线的dev链表上。

当我们驱动开发人员编写好usb_driver并且挂到usb_bus_typedrv链表上时,两者匹配成功会调用drvprobe函数。
usb_driver: 这个由设备开发人员编写,主要作用是处理:芯片的usb_controller传给usb_bus的数据再传给usb_driver的数据

2.处理usb总线驱动传给设备驱动的数据:

usb_controller -> usb_bus_driver -> usb_driver(处理硬件通过usb总线传上来的数据)

最重要的是处理数据,这也是USB设备驱动的核心工作。

usb设备是单向给usb_controller传输数据的,当usb设备插上接口,内核会自动识别此设备获取设备描述符,并且自动根据设备描述符创建一个usb_device,当操作usb设备时会产生各种数据(比如位移数据,坐标数据),并且将这些数据传输给usb总线驱动,usb总线驱动会有一个函数不断查询usb设备的接口是否传来数据,如果有就会将这些数据提取进总线并且传递给对应的设备驱动进行数据处理。

usb设备不可以产生中断,但是usb controller会根据设备驱动传给它的数据产生中断,中断到cpu

usb总线将来自usb设备的数据写到buf之后会产生一个中断,每刷新一次buf就产生一个中断,这个中断是usb controller产生的;

usb设备驱动使用usb总线提供的读写函数读写usb设备的核心是:端点描述符;
usb device传输过来的数据要先放在buf里面,然后再从buf里面提取出来填充urburb作为数据通讯的个单元(两岸通讯的小船:drvdev通讯的小船);

  • urb必须通过buf来填充;

  • usb_alloc_urb:申请urb;

  • usb_fill_int_urb(少量数据用中断)/usb_fill_bulk_urb(大量数据)/usb_fill_control_urb:填充urb(include\linux\usb.h)

这些urb被填充之后需要提交给一个usb_device;

dev->umk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;   //这点很重要

写鼠标驱动时,填充URB时,

  usb_fill_int_urb(dev->umk_urb, udev,
         usb_rcvintpipe(udev,endpoint->bEndpointAddress),
         dev->umk_urb_data, dev->buflen,
         usb_mouse_key_irq_trackpad, dev,endpoint->bInterval);    
//因为忘记添加这两句话,白忙活两天
dev->umk_urb->transfer_dma = dev->buf_phy_addr;
dev->umk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; //这点很重要

总结:USB子系统初始化完后,分别在总线上注册了名为"usb"和“hub"的两个驱动,分别对应着usb_register_device_driver中的usb_probe_deviceusb_register–>usb_register_driver中注册的usb_probe_interfaceprobe函数.

参考

Linux USB驱动分析(一)
usb设备的probe全过程
Linux内核usb子系统
Documentation\driver-api\usb\writing_usb_driver.rst
drivers\usb\usb-skeleton.c(usb设备驱动标准框架,这是一个usb磁盘驱动程序,用户app通过open,wrire,read读写磁盘(u盘))

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值