USB驱动

版权声明:转载请声明 https://blog.csdn.net/qq_40732350/article/details/83588914

1 概述   ######

1.1 USB总线拓扑结构

USB设备的连接如图19.1所示,对于每个PC来说,都有一个或者多个称为主机(Host)控制器的设备,该主机控制器和一个根集线器(Hub)作为一个整体。这个根Hub下可以接多级的Hub,每个子Hub又可以接子Hub,每个USB设备作为一个结点接在不同级别的Hub上

\

1  USB主机控制器(Host Control)

USB Host控制器:每个PC的主板上都会有多个Host控制器,每个Host控制器其实就是一个PCI设备,挂载在PCI总线上,嵌入式设备也如此。在Linux系统中,驱动开发人员应该给Host控制器提供驱动程序,用usbhcd结构来表示。值得注意的是,目前Host控制器驱动主要有两种,一种是1.0,另一种是2.0,分别对应着USB协议1.0和USB协议2.0。

2   USB集线器(USB Hub)

USB Hub:每个USB Host控制器都会自带一个USB Hub,被称为根(Root) Hub。这个根Hub可以接子(Sub) Hub,每个Hub上挂载USB设备。一般PC有8个USB口,通过外接USB Hub,可以插更多的USB设备。当USB设备插入到USB Hub或从上面拔出时,都会发出电信号通知系统。这样可以枚举USB设备。

3  USB设备USB设备:

USB设备就是插在USB总线上工作的设备,广义地讲USB Hub也算是 USB设备。每个根USB Hub下可以直接或间接地连接127个设备,并且彼此不会干扰。对于用户来说,可以看成是USB设备和USB控制器直接相连,之间通信需要满足USB的通信协议。

https://www.cnblogs.com/mahj/p/8489186.html

1.2  USB驱动总体架构

  1. USB驱动由USB主机控制器驱动和USB设备驱动组成。
  2. USB主机控制器驱动,主要用来驱动芯片上的主机控制器硬件。
  3. USB设备驱动是指具体的例如USB摄像头等设备驱动。

1 USB主机控制器

如图19.2所示,在Linux驱动中, USB驱动处于最底层是USB主机控制器硬件。主机控制器硬件用来实现USB协议规定的相关操作,完成与USB设备之间的通信。在嵌入式系统中, USB主机控制器硬件一般集成在CPU芯片中。事实上,在USB的世界里,要使USB设备正常工作,除了有USB设备本身外,在计算机系统中,还需要USB主机控制器才能使USB设备工作。

顾名思义,主机控制器就是用来控制USB设备与CPU之间通信的。通常计算机的CPU并不是直接和USB设备通信,而是和主机控制器通信。CPU要对设备做什么操作,会先通知主机控制器,而不是直接发送指令给USB设备。主机控制器接收到CPU的命令后,会去指挥USB设备完成相应的任务。这样, CPU把命令传给主机控制器后,就不用管余下的工作了, CPU转向处理其他事情。

2. USB主机控制器驱动

USB主机控制器硬件必须由USB主机控制器驱动程序驱动才能运行。USB主机控制器驱动用he driver表示,在计算机系统中的每一个主机控制器都有一个对应的hc_driver结构体,该结构体在/drivers/usb/core/hcd.h文件中定义,代码如下:

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) */
#define	HCD_LOCAL_MEM	0x0002		/* HC needs local memory */
#define	HCD_USB11	0x0010		/* USB 1.1 */
#define	HCD_USB2	0x0020		/* USB 2.0 */
#define	HCD_USB3	0x0040		/* USB 3.0 */
#define	HCD_MASK	0x0070

	/* 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);

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

3 USB核心

再往上一层是USB核心, USB核心负责对USB设备的整体控制,包括实现USB主机控制器到USB设备之间的数据通信。本质上, USB核心是为设备驱动程序提供服务的程序,包含内存分配和一些设备驱动公用的函数,例如初始化Hub、初始化主机控制器等。USB核心的代码存放在drivers/usb/core目录下。

4 USB设备驱动程序

最上一层是USB设备驱动程序,用来驱动相应的USB设备。USB设备驱动用usb-driver表示,它主要用来将USB设备挂接到USB核心中,并启动USB设备,让其正常工作。对 USB设备的具体读写操作由放在usb driver设备中的usb class drivers成员来实现,该成员中定义了一个file-operations结构体,用来对设备进行读写操作。关于usbdriver结构体的详细说明将在下面介绍。


5 USB设备与USB驱动之间的通信

要理解USB设备和USB驱动之间是怎样进行通信的,需要知道两个概念。一是USB设备固件,二是USB协议。这里的USB驱动包括USB主机控制器驱动和USB设备驱动。

固件(Firmware)就是写入EROM或EPROM (可编程只读存储器)中的程序,通俗地理解就是“固化的软件”。更简单地说,固件就是BIOS (基本输入输出软件)的软件,但又与普通软件完全不同,它是固化在集成电路内部的程序代码,负责控制和协调集成电路的功能。USB固件中包含了USB设备的出厂信息,标识该设备的厂商ID、产品ID、主版本号和次版本号等。

另外固件中还包含一组程序,这组程序主要完成两个任务: USB协议的处理和设备的!读写操作。例如将数据从设备发送到总线上,或从总线中将数据读取到设备存储器中。对设备的读写需要固件程序来完成,所以固件程序应该了解对设备读写的方法。驱动程序只是将USB规范定义的请求发送给固件程序,固件程序负责将数据写入设备的存储器中。现在的一些U盘病毒,例如exe文件夹图标病毒,可以破坏USB固件中的程序,导致U盘损害,在使用U盘时,需要引起读者的注意。

USB设备固件和USB驱动之间通信的规范是通过USB协议来完成的。通俗地讲,USB协议规定了USB设备之间是如何通信的。如图19.3是USB设备固件和USB驱动通信的简易关系图。

2 USB设备驱动模型   ########

1 USB驱动初探  @@@@@@

Linux操作系统提供了大量的默认驱动程序。一般来说,这些驱动程序适用于大多数硬件,但也有许多特殊功能的硬件不能在操作系统中找到相应的驱动程序。这时,驱动开发人员一般在内核中找到一份相似的驱动代码,再根据实际的硬件情况进行修改。所以通过什么样的方法找到相似的驱动程序非常重要。

幸好Linux内核源码具有好的分类目录, drivers/usb/storage/目录便是常见的USB设备驱动程序目录。该目录中实现了一个重要的usb-storage模块,该模块支持常用的USB存储设备。本文将对这种设备驱动进行分析。找到了USB驱动目录后,哪些才是USB驱动相关的重要文件呢,请看下面的分析。

1.1 如何找到我们想要的驱动文件?

在USB目录下,有一个重要的storage目录,这里面的代码就是实际需要讲解的USB "设备驱动"的代码。我们日常生活中频繁使用的U盘的驱动,就被放在这个目录中。由于USB设备非常复杂, storage目录中的代码也与其他目录中的代码有千丝万缕的联系,在以后的学习中将逐步讲解,希望引起读者的注意。storgae目录中的主要文件可以用ls命令查看,如下所示。

但是由于storage.o文件的生成很复杂,所有直接给出以后参考的文件是

文件为:\drivers\usb\storage\usb.c

2 USB设备驱动模型  @@@@@

2.1 总线,设备和驱动

Linux设备驱动模型中有3个重要的概念,分别是总线(bus)、设备(device)和驱动(driver) 。

这3个数据结构在Linux内核源码中分别对应struct_bus_type, struct_device和struct_device_driver.

Linux系统中总线的概念与实际的物理主机中总线的概念是不同的。物理主机中的总线是实际的物理线路,例如数据总线、地址总线。而在Linux系统中,总线是一种用来管理设备和驱动程序的数据结构,它与实际的物理总线相对应。在计算机系统中,总线有很多种。例如USB总线、SCSI总线、PCI总线等,在内核代码中,分别对应usb-bus_type,scsibustype和pcibus type变量,这些变量的类型是bus_type.在此处需要关注bus type结构体中的bus type private成员, bus type结构体和 bus type private结构体的省略定义如下:

struct bus_type {
	//......

	struct bus_type_private *p;
};

struct bus_type_private {
	struct kset subsys;
	struct kset *drivers_kset;
	struct kset *devices_kset;
	struct klist klist_devices;
	struct klist klist_drivers;
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;
	struct bus_type *bus;
};

bus_type_private结构体表示总线拥有的私有数据,其中drivers kset和devices kset这两个数据非常重要,其他成员在此处可以忽略。内核设计者将总线与两个链表联系起来,一个是drivers_kset,另一个是devices_kset, drivers_set链表表示连接到该总线上的所有驱动程序, devices_kset链表表示连接到该总线上的所有设备。这种关系如图19.4所示。

在内核中总线、驱动、设备三者之间是通过指针互相联系的。知道其中任何一个结构都可以通过指针获得其他结构。

设备与驱动的绑定

在内核中总线、驱动、设备三者之间是通过指针互相联系的。知道其中任何一个结构都可以通过指针获得其他结构绑定。设备与驱动的绑定,只能在同一总线上的设备与驱动之间进行。

在设备模型中,当知道一条总线的数据结构时,就可以找到这条总线所连接的设备和驱动程序。要实现这个关系,就要求当每次总线上有新设备出现时,系统就要向总线汇报,告知有新设备添加到系统中。系统为设备分配一个struct device数据结构,并将其挂接到 devices kset链表中。特别是在开机时,系统会扫描连接了哪些设备,并为每一个设备分配个 struct device数据结构,同样将其挂接在总线的devices kset链表中。

当驱动开发者申请了一条总线,用bus type来表示,这时总线并不知道连在总线上的设备有哪些,驱动程序有哪些。总线与设备和驱动的连接,需要相应总线的核心代码来实现。对USB总线,实现总线与驱动和设备的连接,是通过USB核心(USB core)来完成的。USB core会完成总线的初始化工作,然后再扫描USB总线,看USB总线上连接了哪些设备。

当USB core发现设备时,会为其分配一个struct device结构体,并将其连到总线上。当发现所有设备后, USB总线上的设备链表就建立好了。

相比设备的连接,将驱动连接到总线上就容易多了。每当驱动注册时,会将自己在总线上注册,并连接到总线的驱动链表中。这时,驱动会遍历总线的设备链表,寻找自己适的设备,并将其通过内部指针连接起来。

3 USB设备驱动结构体 usb_driver  @@@@@

在USB设备驱动模型中, USB设备驱动使用usb driver结构体来表示。该结构体中包含了与具体设备相关的核心函数,对于不同的USB设备,驱动开发人员需要实现不同功能的函数, USB核心通过在框架中调用这些自定义的函数完成相应的功能。下面对usb driver结构体进行简要的介绍。

挂接在usb总线上的驱动程序,使用usb_driver结构体来表示。这个结构在系统驱动注册时,将加载到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 (*ioctl) (struct usb_interface *intf, unsigned int code,
			void *buf);//I/O控制函数

	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;//USB驱动所支持的设备列表

	struct usb_dynids dynids;
	struct usbdrv_wrap drvwrap;
	unsigned int no_dynamic_id:1;//是否允许动态加载驱动
	unsigned int supports_autosuspend:1;//是否支持自动挂起驱动
	unsigned int soft_unbind:1;
};

13~17:驱动开发用不着关心

前面已经说过,一个设备只能绑定到一个驱动程序,但是一个驱动程序却可以支持很多设备。例如,用户插入两块不同厂商的U盘,但是他们都符合USB 2.0协议,那么只需要一个支持USB 2.0协议的驱动程序即可。也就是说,不论插入多少个同类型U盘,系统都只使用一个驱动程序。这样就有效地减少了模块的引用,节省了系统的内存开销。

既然一个驱动可以支持多个设备,那么怎样知道驱动支持哪些设备呢?通过usb driver结构中的id table成员就可以完成这个功能。id table成员描述了一个USB设备所支持的所有USB设备列表,它指向一个usb_deviceid数组。usb-device id结构体包含了USB设备的制造商ID、产品ID、产品版本、结构类等信息。

usb-deviceid结构体就像一张实名制火车票,票上有姓名、车次、车厢号、座位。旅客上车时,乘务员将检查这些信息,只有当这些信息都相同时,乘务员才允许旅客上车。USB设备驱动也一样,在USB设备中有一个固件程序,固件程序中包含了这些信息。当 USB设备中的信息和总线上驱动的id table信息中的一项相同时,就将USB设备与驱动绑定。由于一个驱动可以适用与多个设备,所以id table表项中可能有很多项。usb device id结构体定义如下:

struct usb_device_id {
	/* which fields to match against? */
	__u16		match_flags;//匹配标志,定义下面哪些项应该被匹配

	/* Used for product specific matches; range is inclusive */
	__u16		idVendor;//制造商ID
	__u16		idProduct;//产品ID
	__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;

	/* not matched against */
	kernel_ulong_t	driver_info;
};

第02行的match-flags字段表示设备的固件信息与usb deviceid的哪些字段相匹配,才能认为驱动适合于该设备。这个标志可以取下列标志的组合。

/* Some useful macros to use to create struct usb_device_id */
#define USB_DEVICE_ID_MATCH_VENDOR		0x0001
#define USB_DEVICE_ID_MATCH_PRODUCT		0x0002
#define USB_DEVICE_ID_MATCH_DEV_LO		0x0004
#define USB_DEVICE_ID_MATCH_DEV_HI		0x0008
#define USB_DEVICE_ID_MATCH_DEV_CLASS		0x0010
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS	0x0020
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL	0x0040
#define USB_DEVICE_ID_MATCH_INT_CLASS		0x0080
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS	0x0100
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL	0x0200

例如一个驱动只需要比较厂商ID和产品ID,那么如下

.mach_flages = (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)

USB注册驱动函数usb_register()

static inline int usb_register(struct usb_driver *driver)
{
	return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
}
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
			const char *mod_name)
{
	int retval = 0;

	if (usb_disabled())//禁用USB设备
		return -ENODEV;

	new_driver->drvwrap.for_devices = 0;
	new_driver->drvwrap.driver.name = (char *) 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);
	if (retval)
		goto out;

	usbfs_update_special();

	retval = usb_create_newid_file(new_driver);
	if (retval)
		goto out_newid;

	retval = usb_create_removeid_file(new_driver);
	if (retval)
		goto out_removeid;

	pr_info("%s: registered new interface driver %s\n",
			usbcore_name, new_driver->name);
}

3 USB设备驱动程序   ########

文件为:\drivers\usb\storage\usb.c

3.1 USB设备驱动加载和卸载函数  @@@@@

USB设备驱动程序对应一个usb_driver结构体,这个结构体相当于Linux设备驱动模型中的driver结构体。下面对usb_driver结构进行详细的介绍。
1, usb_driver结构

作为USB设备驱动的实现,首先需要定义一个usb_driver结构变量作为要注册到USB核心的设备驱动。这里定义了一个变量usb storage_driver进行注册,变量usb_storage_driver是由USB设备模块的加载模块中的usb register)函数加入系统的,这个函数已经详细讲述。usb_storage_driver变量的定义如下:

static struct usb_driver usb_storage_driver = {
	.name =		"usb-storage",
	.probe =	storage_probe,
	.disconnect =	usb_stor_disconnect,
	.suspend =	usb_stor_suspend,
	.resume =	usb_stor_resume,
	.reset_resume =	usb_stor_reset_resume,
	.pre_reset =	usb_stor_pre_reset,
	.post_reset =	usb_stor_post_reset,
	.id_table =	usb_storage_usb_ids,
	.soft_unbind =	1,
};

安装和卸载驱动

static int __init usb_stor_init(void)
{
	int retval;

	pr_info("Initializing USB Mass Storage driver...\n");

	/* register the driver, return usb_register return code if error */
	retval = usb_register(&usb_storage_driver);//注册USB驱动
	if (retval == 0) {
		pr_info("USB Mass Storage support registered.\n");
		usb_usual_set_present(USB_US_TYPE_STOR);//设置模块的状态
	}
	return retval;
}

static void __exit usb_stor_exit(void)
{
	US_DEBUGP("usb_stor_exit() called\n");

	/* Deregister the driver
	 * This will cause disconnect() to be called for each
	 * attached unit
	 */
	US_DEBUGP("-- calling usb_deregister()\n");
	usb_deregister(&usb_storage_driver) ;//卸载驱动程序

	usb_usual_clear_present(USB_US_TYPE_STOR);
}

3.2 探测函数probe()的参数usb_interface

前面已经讲了USB设备驱动加载函数usb stor init。从代码中可以看出,该函数的执行流已经结束,此时,我们几乎不知道程序会从哪里开始执行。事实上,当usb_stor_init()函数执行完后,就没有代码会执行了,除非有事件触发使USB驱动开始工作。

触发事件是什么呢?当USB设备插入USB插槽时,会引起一个电信号的变化,主机控制器捕获这个电信号,并命令USB核心处理对设备的加载工作。USB核心读取USB设备固件中关于设备的信息,并与挂接在USB总线上的驱动程序相比较,如果找到合适的驱动程序usb_driver,就会调用驱动程序的probe)函数。本节讲解的代码中的probe)函数就 storage probe), probe)函数的原型是:

static int storage_probe(struct usb_interface *intf,
			 const struct usb_device_id *id)

该函数的第一个参数usb_interface是USB驱动中最重要的一个结构体了。它代表着设备中的一种功能,与一个usb driver相对应。usb interface在USB驱动中只有一个,有USB核心负责维护,所以需要注意的是,以后提到的usb_interface指的都是同一个.usb_interface。要了解usb interface就需要先了解一些USB协议中的内容了。这里,先从 USB协议中的设备开始。

3.3 USB协议中的设备   @@@@@@

设备:一个设备就表示一个真实的物理设备,(如U盘,USB鼠标)

配置:多个功能的组合(如打印机的第一个配置:扫描功能,第二个配置:打印和复印功能)

接口:一个接口对应一个单一的功能,如(打印机的打印功能)

端点:端点是USB的最基本的通信方式,只能通过端点传输数据(一般一个接口有输入端点,输出端点,控制端点)

端点描述符:是端点的一些配置

综合上面的内容,设备、配置、接口、端点之间的关系是:

设备通常有一个或者多个配置。

配置通常有一个或者多个接口。

接口通常有一个或者多个端点。

4种描述符的关系

一个接口必须有一个控制端点的描述符

端点就是用来传输数据的

端点传输方式:

1 控制传输:

控制传输可以访问一个设备的不同部分。其主要用于向设备发送配置信息、获取设备 ,信息、发送命令到设备,或者获取设备的状态报告。控制传输是任何USB设备都应该支持的一种传输方式。

2 中断传输

以一个固定的时间周期传输一些数据,不能传输大量数据,因为这是周期传输

3 批量传输

无周期,能传输大量的数据,如U盘

4 等时传输

用于传输达量数据,但是可以接收数据的一些丢失或者错误,但是不能接受数据传输的延时,如视频传输,音频传输

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/83588914