USB鼠标驱动开发流程

  • Post author:
  • Post category:其他


USB驱动开发,针对某一个USB设备的某个功能(接口)构建的驱动程序。USB驱动并不直接和USB设备进行数据交互,而是通过USB总线驱动程序(USB Core和USB HCD)来操作USB设备的。一般构建USB设备驱动的流程为:

(1) 根据期望适用的USB设备信息构建一个id_table。

(2) 根据需要的数据传输类型,调用相应的接口创建数据传输管道。

(3) 分配一个urb(USB请求块)。

(4) 根据需要的数据传输类型,调用相应的接口进行urb数据结构初始化。

(5) 提交urb。


1 USB子系统框架

在这里插入图片描述


2 USB总线驱动程序



2.1 USB Core


初始化内核USB总线及提供USB相关API,为设备驱动和HCD的交互提供桥梁。

Linux启动阶段,通过subsys_initcall会完成USB Core的加载。

subsys_initcall(usb_init);                

linux内核代码是以模块(module)和子系统(subsys)为单元的层次结构。在写模块代码时我们知道入口函数是module_init()或subsys_initcall(),在linux启动时会被调用。

usb_init函数如下:

static int __init usb_init(void) //usb子系统的入口函数,完成初始化动作,之后它所用到的资源就会被释放掉。
{
	int retval;
	if (usb_disabled()) {
		pr_info("%s: USB support disabled\n", usbcore_name);
		return 0;
	}
	usb_init_pool_max();

	usb_debugfs_init();

	usb_acpi_register();
	//使用bus_register接口注册USB总线,会创建出两条链表用来分别存放向USB总线注册的设备和驱动。
	retval = bus_register(&usb_bus_type);
	if (retval)
		goto bus_register_failed;
	retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
	if (retval)
		goto bus_notifier_failed;
	retval = usb_major_init();
	if (retval)
		goto major_init_failed;
	//在usb总线注册usb接口驱动,该驱动被放在usb总线的驱动链表中。一个设备可以有多个接口,每个接口对应着不同的功能。
	//usbfs为咱们提供了在用户空间直接访问usb硬件设备的接口,但它需要内核的大力支持,usbfs_driver就是用来完成这个光荣任务的。
	retval = usb_register(&usbfs_driver);
	if (retval)
		goto driver_register_failed;
	retval = usb_devio_init();
	if (retval)
		goto usb_devio_init_failed;
	//初始化一个usb设备集线器,用来检测usb设备的连接和断开。
	retval = usb_hub_init();
	if (retval)
		goto hub_init_failed;
	//在usb总线注册USB设备驱动,该驱动被放在usb总线的驱动链表中,generic代表基本的、通用的意思。
	retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
	if (!retval)
		goto out;

	usb_hub_cleanup();
hub_init_failed:
	usb_devio_cleanup();
usb_devio_init_failed:
	usb_deregister(&usbfs_driver);
driver_register_failed:
	usb_major_cleanup();
major_init_failed:
	bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_notifier_failed:
	bus_unregister(&usb_bus_type);
bus_register_failed:
	usb_acpi_unregister();
	usb_debugfs_cleanup();
out:
	return retval;
}


2.1.1 注册 USB 总线


USB是基于总线-驱动-设备模型的框架,其初始化阶段一个重点任务就是完成USB总线的创建。

retval = bus_register(&usb_bus_type);
if (retval) 
    goto bus_register_failed;

struct bus_type usb_bus_type = {
	.name =		"usb",
	.match =	usb_device_match,
	.uevent =	usb_uevent,
	.suspend =	usb_suspend,
	.resume =	usb_resume,
};

使用bus_register接口注册USB总线,会创建出两条链表用来分别存放向USB总线注册的设备和驱动。

klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&bus->klist_drivers, NULL, NULL);

usb_bus_type提供了驱动和设备匹配的匹配函数,后面注册设备和驱动时会调用到。

static int usb_device_match(struct device *dev, struct device_driver *drv)
{
	/* devices and interfaces are handled separately */
	if (is_usb_device(dev)) {
		struct usb_device *udev;
		struct usb_device_driver *udrv;

		/* interface drivers never match devices */
		if (!is_usb_device_driver(drv))
			return 0;

		udev = to_usb_device(dev);
		udrv = to_usb_device_driver(drv);

		/* If the device driver under consideration does not have a
		 * id_table or a match function, then let the driver's probe
		 * function decide.
		 */
		if (!udrv->id_table && !udrv->match)
			return 1;

		return usb_driver_applicable(udev, udrv);

	} else if (is_usb_interface(dev)) {
		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);
		if (id)
			return 1;

		id = usb_match_dynamic_id(intf, usb_drv);
		if (id)
			return 1;
	}

	return 0;
}


2.1.2 注册USB接口驱动


一个设备可以有多个接口,每个接口对应着不同的功能。因此每个接口都会对应一个驱动程序。

//在usb总线注册USB接口驱动,该驱动被放在usb总线的驱动链表中。
retval = usb_register(&usbfs_driver);
if (retval)
    goto driver_register_failed;
    
struct usb_driver usbfs_driver = {
	.name =		"usbfs",
	.probe =	driver_probe,
	.disconnect =	driver_disconnect,
	.suspend =	driver_suspend,
	.resume =	driver_resume,
	.supports_autosuspend = 1,
};


2.1.3 初始化USB Hub


初始化一个USB设备集线器,用来检测USB设备的connect和disconnect。

// linux-2.6.22.6/drivers/usb/core/usb.c
retval = usb_hub_init();
if (retval)
    goto hub_init_failed;


// linux-2.6.22.6/drivers/usb/core/hub.c
int usb_hub_init(void)
{
    // 在usb总线注册一个hub驱动,该驱动被放在usb总线的驱动链表中。
	if (usb_register(&hub_driver) < 0) {
		printk(KERN_ERR "%s: can't register hub driver\n",
			usbcore_name);
		return -1;
	}
    
    // 创建一个线程,用来处理USB设备的断开、连接等事件
	khubd_task = kthread_run(hub_thread, NULL, "khubd");
	if (!IS_ERR(khubd_task))
		return 0;

	/* Fall through if kernel_thread failed */
	usb_deregister(&hub_driver);
	printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);

	return -1;
}

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


static int hub_thread(void *__unused)
{
	do {
        /*
        hub_events()是一个死循环,其任务是解析hub_event_list,
        来一个一个处理发生在hub上的事件,比如插入,拔出。
        当hub_event_list事件被处理完后,break跳出while,
        通过wait_event_freezable使进程进入休眠态。
        一旦hub_event_list上有新事件需要处理,此处khubd_wait会在事件中断中被唤醒,
        重新执行到此处的hub_events()来遍历执行事件,完成处理。
        */
		hub_events();
		wait_event_interruptible(khubd_wait,
				!list_empty(&hub_event_list) ||
				kthread_should_stop());
		try_to_freeze();
	} while (!kthread_should_stop() || !list_empty(&hub_event_list));

	pr_debug("%s: khubd exiting\n", usbcore_name);
	return 0;
}


2.1.3.1 khubd_wait 的唤醒

hub_probe ->    
    hub_configure ->
        usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
                hub, endpoint->bInterval);
        // usb_fill_int_urb 接口创建了一个中断类型的 USB请求控制块
            hub_irq -> 
                kick_khubd(hub) ->
                    wake_up(&khubd_wait);
    

在usb_init函数中会调用usb_hub_init函数,而在usb_hub_init函数中会进行usb_register(&hub_driver),而在hub_driver这个结构体中就有一个成员为hub_probe。hub_probe 在后面向USB总线注册一个和hub驱动匹配的hub设备时会被调用。


2.1.4 注册USB设备驱动

// 在usb总线注册USB设备驱动,该驱动被放在usb总线的驱动链表中。
retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
if (!retval)
    goto out;

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


2.1.5 usb_register 和 usb_register_device_driver


usb_register 注册一个USB接口驱动,一个设备可以有多个接口,一个接口表示一种功能。比如USB声卡设备,有两个接口,一个播放接口,一个录音接口。

// linux-2.6.22.6/include/linux/usb.h
static inline int usb_register(struct usb_driver *driver)
{
	return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
}

// linux-2.6.22.6/drivers/usb/core/driver.c
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
			const char *mod_name)
{
	int retval = 0;

	if (usb_disabled())
		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;
    // 对应的usb接口“设备”被匹配时,首先会调用usb_probe_interface,然后在该接口中调用driver的probe
	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) {
		pr_info("%s: registered new interface driver %s\n",
			usbcore_name, new_driver->name);
		usbfs_update_special();
		usb_create_newid_file(new_driver);
	} else {
		printk(KERN_ERR "%s: error %d registering interface "
			"	driver %s\n",
			usbcore_name, retval, new_driver->name);
	}

	return retval;
}

usb_register_device_driver 注册一个通用USB设备驱动,而不是USB接口驱动。

// linux-2.6.22.6/drivers/usb/core/driver.c
int usb_register_device_driver(struct usb_device_driver *new_udriver,
		struct module *owner)
{
	int retval = 0;

	if (usb_disabled())
		return -ENODEV;
    // for_devices = 1 将和USB设备匹配成功
	new_udriver->drvwrap.for_devices = 1;
	new_udriver->drvwrap.driver.name = (char *) new_udriver->name;
	new_udriver->drvwrap.driver.bus = &usb_bus_type;
    // 对应的usb设备被匹配时,首先会调用usb_probe_device,然后在该接口中调用driver的probe
	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);

	if (!retval) {
		pr_info("%s: registered new device driver %s\n",
			usbcore_name, new_udriver->name);
		usbfs_update_special();
	} else {
		printk(KERN_ERR "%s: error %d registering device "
			"	driver %s\n",
			usbcore_name, retval, new_udriver->name);
	}

	return retval;
}


2.1.6 总结


USB core注册了一个USB总线,并向USB总线中注册了三个驱动,分别是USB接口驱动、HUB驱动、USB设备驱动。其中在注册HUB驱动前创建了一个hub_thread线程,用来处理hub上USB设备事件,比如插入和拔出;在HUB驱动的probe函数中,创建了一个urb并为其注册了一个中断处理函数hub_irq,用来唤醒hub_thread线程来处理USB设备事件。


2.2 USB主机控制器驱动(HCD)


USB HCD注册在平台总线上。用来处理主机控制器的初始化以及数据的传输,并监测外部设备插入、拔出,完成设备枚举。


2.2.1 USB主机控制器-设备

// linux-2.6.22.6/arch/arm/mach-s3c2440/mach-smdk2440.c 
MACHINE_START(S3C2440, "SMDK2440")
	/* Maintainer: Ben Dooks <ben@fluff.org> */
	.phys_io	= S3C2410_PA_UART,
	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
	.boot_params	= S3C2410_SDRAM_PA + 0x100,

	.init_irq	= s3c24xx_init_irq,
	.map_io		= smdk2440_map_io,
	.init_machine	= smdk2440_machine_init,
	.timer		= &s3c24xx_timer,
MACHINE_END

static void __init smdk2440_machine_init(void)
{
	s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);

	platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
	smdk_machine_init();
}

static struct platform_device *smdk2440_devices[] __initdata = {
	&s3c_device_usb,
	&s3c_device_lcd,
	&s3c_device_wdt,
	&s3c_device_i2c,
	&s3c_device_iis,
    &s3c2440_device_sdi,
};

// linux-2.6.22.6/arch/arm/plat-s3c24xx/devs.c
struct platform_device s3c_device_usb = {
	.name		  = "s3c2410-ohci",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_usb_resource),
	.resource	  = s3c_usb_resource,
	.dev              = {
		.dma_mask = &s3c_device_usb_dmamask,
		.coherent_dma_mask = 0xffffffffUL
	}
};


2.2.2 USB主机控制器-驱动

// linux-2.6.22.6/.config
CONFIG_ARCH_S3C2410=y

// linux-2.6.22.6/drivers/usb/host/ohci-hcd.c
#ifdef CONFIG_ARCH_S3C2410
#include "ohci-s3c2410.c"
#define PLATFORM_DRIVER		ohci_hcd_s3c2410_driver
#endif

static int __init ohci_hcd_mod_init(void)
{
	int retval = 0;

	if (usb_disabled())
		return -ENODEV;

	printk (KERN_DEBUG "%s: " DRIVER_INFO "\n", hcd_name);
	pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
		sizeof (struct ed), sizeof (struct td));

#ifdef PS3_SYSTEM_BUS_DRIVER
	if (firmware_has_feature(FW_FEATURE_PS3_LV1)) {
		retval = ps3_system_bus_driver_register(
				&PS3_SYSTEM_BUS_DRIVER);
		if (retval < 0)
			goto error_ps3;
	}
#endif

#ifdef PLATFORM_DRIVER
	retval = platform_driver_register(&PLATFORM_DRIVER);
	if (retval < 0)
		goto error_platform;
#endif

#ifdef OF_PLATFORM_DRIVER
	retval = of_register_platform_driver(&OF_PLATFORM_DRIVER);
	if (retval < 0)
		goto error_of_platform;
#endif

#ifdef SA1111_DRIVER
	retval = sa1111_driver_register(&SA1111_DRIVER);
	if (retval < 0)
		goto error_sa1111;
#endif

#ifdef PCI_DRIVER
	retval = pci_register_driver(&PCI_DRIVER);
	if (retval < 0)
		goto error_pci;
#endif

	return retval;

	/* Error path */
#ifdef PCI_DRIVER
 error_pci:
#endif
#ifdef SA1111_DRIVER
	sa1111_driver_unregister(&SA1111_DRIVER);
 error_sa1111:
#endif
#ifdef OF_PLATFORM_DRIVER
	of_unregister_platform_driver(&OF_PLATFORM_DRIVER);
 error_of_platform:
#endif
#ifdef PLATFORM_DRIVER
	platform_driver_unregister(&PLATFORM_DRIVER);
 error_platform:
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
	if (firmware_has_feature(FW_FEATURE_PS3_LV1))
		ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
 error_ps3:
#endif
	return retval;
}
module_init(ohci_hcd_mod_init);

// linux-2.6.22.6/drivers/usb/host/ohci-s3c2410.c
static struct platform_driver ohci_hcd_s3c2410_driver = {
	.probe		= ohci_hcd_s3c2410_drv_probe,
	.remove		= ohci_hcd_s3c2410_drv_remove,
	.shutdown	= usb_hcd_platform_shutdown,
	/*.suspend	= ohci_hcd_s3c2410_drv_suspend, */
	/*.resume	= ohci_hcd_s3c2410_drv_resume, */
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c2410-ohci",
	},
};


2.2.3 USB主机控制器设备和驱动的匹配

platform_driver_register->
	driver_register->
		bus_add_driver->
			driver_attach->
				bus_for_each_dev-> // 从平台总线的的设备链表中,取出每一项设备进行匹配
					__driver_attach->
                        driver_probe_device->
                            // 此总线类型为平台总线,其存在match函数,即调用platform_match进行匹配
                            if (drv->bus->match && !drv->bus->match(dev, drv))                            
    
// 平台总线                            
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.suspend	= platform_suspend,
	.suspend_late	= platform_suspend_late,
	.resume_early	= platform_resume_early,
	.resume		= platform_resume,
};              

static int platform_match(struct device * dev, struct device_driver * drv)
{
	struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    
    // 平台总线匹配设备和驱动的名称
	return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}

// ohci 设备   name = "s3c2410-ohci"
struct platform_device s3c_device_usb = {
	.name		  = "s3c2410-ohci",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_usb_resource),
	.resource	  = s3c_usb_resource,
	.dev              = {
		.dma_mask = &s3c_device_usb_dmamask,
		.coherent_dma_mask = 0xffffffffUL
	}
};

// ohci 驱动 name	= "s3c2410-ohci"
static struct platform_driver ohci_hcd_s3c2410_driver = {
	.probe		= ohci_hcd_s3c2410_drv_probe,
	.remove		= ohci_hcd_s3c2410_drv_remove,
	.shutdown	= usb_hcd_platform_shutdown,
	/*.suspend	= ohci_hcd_s3c2410_drv_suspend, */
	/*.resume	= ohci_hcd_s3c2410_drv_resume, */
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c2410-ohci",
	},
};                                

匹配成功调用驱动的probe函数。

driver_probe_device-> // 在此函数中匹配成功的话,就会去调用驱动的probe函数
    really_probe->
        drv->probe(dev)


2.2.4 USB主机控制器驱动的probe函数

ohci_hcd_s3c2410_drv_probe -> 
    usb_hcd_s3c2410_probe ->
        usb_add_hcd -> 
            rhdev = usb_alloc_dev
            hcd->self.root_hub = rhdev
            register_root_hub -> 
                usb_new_device ->
                    device_add ->     
                        bus_attach_device ->
                            device_attach -> 
                                bus_for_each_drv -> // 从usb总线的的驱动链表中,取出每一项驱动进行匹配
                                    __device_attach ->
                                        driver_probe_device ->
                                            // 此总线类型为USB总线,其存在match函数,即调用usb_device_match进行匹配
                                            if (drv->bus->match && !drv->bus->match(dev, drv)) 
                                                driver_probe_device-> // 在此函数中匹配成功的话,就会去调用驱动的probe函数
                                                    really_probe->
                                                        drv->probe(dev)

2.2.4.1 usb_device_match

static inline int is_usb_device(const struct device *dev)
{
	return dev->type == &usb_device_type;
}

/* Do the same for device drivers and interface drivers. */

static inline int is_usb_device_driver(struct device_driver *drv)
{
    // struct device_driver 中 struct usbdrv_wrap 中的for_devices变量为1,则为USB设备驱动
    // 上节USB Core中向USB总线注册的USB设备驱动中有将该变量设置为1(new_udriver->drvwrap.for_devices = 1;)
	return container_of(drv, struct usbdrv_wrap, driver)->
			for_devices;
}

static int usb_device_match(struct device *dev, struct device_driver *drv)
{
	// USB设备 和 USB接口“设备”分开处理 
	/* 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 {
        // 处理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);
		if (id)
			return 1;

		id = usb_match_dynamic_id(intf, usb_drv);
		if (id)
			return 1;
	}

	return 0;
}

probe 向USB总线注册一个root hub 设备,从usb总线的的驱动链表中,取出每一项驱动进行匹配。在USB Core中已经向总线注册了三个驱动(USB设备驱动、USB接口驱动、USB hub驱动),根据条件匹配到USB设备驱动,则去调用USB设备驱动的probe函数。


2.2.4.2 USB设备驱动的probe函数

generic_probe(struct usb_device *udev) -> // 从上分析流程知udev为USB root hub设备 
    usb_set_configuration ->
        device_add ->  // 创建USB接口设备,USB root hub接口设备被创建

之后匹配到USB Core中注册的USB hub驱动,执行USB hub驱动的probe函数,该probe函数中,创建了一个urb并为其注册了一个中断处理函数hub_irq,用来唤醒hub_thread线程来处理USB设备事件(插入、拔出)。至此,系统启动初始化时关于USB的内容分析完成。USB Core和USB HCD的成功建立联系,为之后的USB设备驱动提供API。


3 USB鼠标驱动


用于和枚举到的USB设备进行绑定,完成特定的功能。


3.1 注册一个USB接口驱动


向USB总线注册一个USB接口驱动。

// linux-2.6.22.6/drivers/hid/usbhid/usbmouse.c
static struct usb_driver usb_mouse_driver = {
	.name		= "usbmouse",
	.probe		= usb_mouse_probe,
	.disconnect	= usb_mouse_disconnect,
	.id_table	= usb_mouse_id_table,
};

static int __init usb_mouse_init(void)
{
	int retval = usb_register(&usb_mouse_driver);
	if (retval == 0)
		info(DRIVER_VERSION ":" DRIVER_DESC);
	return retval;
}


3.2 USB接口设备的创建


当一个USB 鼠标设备插入后,主机USB控制器检测到后,触发USB设备集线器中的”中断”处理函数hub_irq。在hub_irq中会获取USB鼠标设备的设备描述符,根据设备描述符创建USB接口设备,从而和这边的USB接口驱动匹配,调用其probe函数,通过USB总线驱动程序(USB Core和USB HCD)和USB鼠标设备建立联系,进而操作(读写控制)该设备。

hub_irq
	kick_khubd // 唤醒hub_thread线程
		hub_thread
			hub_events // 处理USB设备插入事件
				hub_port_connect_change
				
					udev = usb_alloc_dev(hdev, hdev->bus, port1);
								dev->dev.bus = &usb_bus_type;
				
					choose_address(udev); // 给新设备分配编号(地址)										
					hub_port_init   // usb 1-1: new full speed USB device using s3c2410-ohci and address 3
						
						hub_set_address  // 把编号(地址)告诉USB设备
						
						usb_get_device_descriptor(udev, 8); // 获取设备描述符
						retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
						
						usb_new_device(udev)   
							err = usb_get_configuration(udev); // 把所有的描述符都读出来,并解析
							usb_parse_configuration
							
							device_add  // 把device放入usb_bus_type的dev链表, 
							            // 从usb_bus_type的driver链表里取出usb_driver,
							            // 把usb_interface和usb_driver的id_table比较
							            // 如果能匹配,调用usb_driver的probe


3.3 USB接口驱动和USB接口设备的匹配


USB设备插入后根据获取到的设备描述符所创建的USB 接口设备和开发的USB接口驱动匹配:

对于设备:

将获取到的USB设备描述符信息保存在其id_table中。

对于驱动:

驱动的id_table中存放,期望该驱动适用的USB设备。

// linux-2.6.22.6/drivers/hid/usbhid/usbmouse.c
static struct usb_device_id usb_mouse_id_table [] = {  
    /*
    匹配 HID 设备
    USB 设备中有一大类就是 HID 设备,即 Human Interface Devices,人机接口设备。
    这类设备包括鼠标、键盘等,主要用于人与计算机进行交互。 
    它是 USB 协议最早支持的一种设备类。 
    HID 设备可以作为低速、全速、高速设备用。
    由于 HID 设备要求用户输入能得到及时响应,故其传输方式通常采用中断方式。
    */
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		USB_INTERFACE_PROTOCOL_MOUSE) },
	{ }	/* Terminating entry */
};

匹配成功后调用该驱动的probe函数,具体的过程和前面分析的差不多。接下来就是在probe函数中,和USB总线驱动程序建立联系,以达到操作USB 鼠标设备的目的。


3.4 创建数据传输管道


根据数据传输类型,有几个接口可供调用。

/* Create various pipes... */
// 控制传输
#define usb_sndctrlpipe(dev,endpoint)	\
	((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))
#define usb_rcvctrlpipe(dev,endpoint)	\
	((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
// 实时传输
#define usb_sndisocpipe(dev,endpoint)	\
	((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint))
#define usb_rcvisocpipe(dev,endpoint)	\
	((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
// 批量传输
#define usb_sndbulkpipe(dev,endpoint)	\
	((PIPE_BULK << 30) | __create_pipe(dev,endpoint))
#define usb_rcvbulkpipe(dev,endpoint)	\
	((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
// 中断传输
#define usb_sndintpipe(dev,endpoint)	\
	((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint))
#define usb_rcvintpipe(dev,endpoint)	\
	((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)

对于USB 鼠标设备,使用中断传输方式。

// linux-2.6.22.6/drivers/hid/usbhid/usbmouse.c

struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
int error = -ENOMEM;

interface = intf->cur_altsetting;

if (interface->desc.bNumEndpoints != 1)
    return -ENODEV;

endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
    return -ENODEV;

// 端点是USB设备数据传输对象
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));


3.5 分配urb


urb(USB Request Block)是Linux内核中USB驱动实现上的一个数据结构,用于组织每一次的USB设备驱动的数据传输请求。

mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!mouse->irq)
    goto fail2;


3.6 urb数据结构初始化


根据传输类型,有几个接口可供调用。

// 控制
static inline void usb_fill_control_urb(struct urb *urb,
					struct usb_device *dev,
					unsigned int pipe,
					unsigned char *setup_packet,
					void *transfer_buffer,
					int buffer_length,
					usb_complete_t complete_fn,
					void *context)
// 中断
static inline void usb_fill_int_urb(struct urb *urb,
				    struct usb_device *dev,
				    unsigned int pipe,
				    void *transfer_buffer,
				    int buffer_length,
				    usb_complete_t complete_fn,
				    void *context,
				    int interval)
// 批量

static inline void usb_fill_bulk_urb(struct urb *urb,
				     struct usb_device *dev,
				     unsigned int pipe,
				     void *transfer_buffer,
				     int buffer_length,
				     usb_complete_t complete_fn,
				     void *context)
    
// 实时   
// 实时urb 没有和中断、控制、批量urb 类似的初始化函数,因此它们在提交到USB核心之前,需要在驱动程序中手动的初始化.

对于USB鼠标设备,采用中断传输方式。

usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
			 (maxp > 8 ? 8 : maxp),
			 usb_mouse_irq, mouse, endpoint->bInterval);
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;


3.7 提交USB请求块


调用usb_submit_urb接口以获取USB设备数据。

// linux-2.6.22.6/drivers/hid/usbhid/usbmouse.c
static int usb_mouse_open(struct input_dev *dev)
{
	struct usb_mouse *mouse = input_get_drvdata(dev);

	mouse->irq->dev = mouse->usbdev;
	if (usb_submit_urb(mouse->irq, GFP_KERNEL))
		return -EIO;

	return 0;
}

该USB鼠标设备驱动还涉及输入子系统的内容。


5 实现一个USB设备驱动程序

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>

static struct input_dev *uk_dev;  //input_dev
static char *usb_buf;  //虚拟地址缓存区
static dma_addr_t usb_buf_phys;  //DMA缓存区
static int len;  //数据包长度
static struct urb *uk_urb;  //urb数据传输所用结构体

static struct usb_device_id usbmouse_as_key_id_table [] = {
	// 匹配HID,鼠标设备
	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,  //接口类:hid类
						 USB_INTERFACE_SUBCLASS_BOOT,  //子类:启动设备类
						USB_INTERFACE_PROTOCOL_MOUSE) },  //USB协议。鼠标协议
	//{USB_DEVICE(0x1234,0x5678)},
	{ }	/* Terminating entry */
};

static void usbmouse_as_key_irq(struct urb *urb)
{
	static unsigned char pre_val;

	/* USB鼠标数据含义
	 * data[0]: bit0-左键, 1-按下, 0-松开
	 *          bit1-右键, 1-按下, 0-松开
	 *          bit2-中键, 1-按下, 0-松开 
     */

	if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))
	{
		/* 左键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)) ? 1 : 0);
		input_sync(uk_dev);
	}

	if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))
	{
		/* 右键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)) ? 1 : 0);
		input_sync(uk_dev);
	}

	if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))
	{
		/* 中键发生了变化 */
		input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)) ? 1 : 0);
		input_sync(uk_dev);
	}
	
	pre_val = usb_buf[0];

	/* 重新提交urb */
	usb_submit_urb(uk_urb, GFP_KERNEL);
}

static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf); // 设备,通过usb_ interface接口获取usb_device设备,为后面设置USB数据传输用
	struct usb_host_interface *interface;   // 当前接口
	struct usb_endpoint_descriptor *endpoint;
	int pipe;  // 端点管道
	
	interface = intf->cur_altsetting;
	endpoint = &interface->endpoint[0].desc;   // 当前接口下的端点描述符

	/* a. 分配一个input_dev */
	uk_dev = input_allocate_device();
	
	/* b. 设置 */
	/* b.1 能产生哪类事件 */ //键盘变量定义在:include/linux/input.h, 比如: KEY_L(按键L)
	set_bit(EV_KEY, uk_dev->evbit);
	set_bit(EV_REP, uk_dev->evbit);
	
	/* b.2 能产生哪些事件 */
	set_bit(KEY_L, uk_dev->keybit);
	set_bit(KEY_S, uk_dev->keybit);
	set_bit(KEY_ENTER, uk_dev->keybit);
	
	/* c. 注册 */
	input_register_device(uk_dev);
	
	/* d. 硬件相关操作 */
	/* 数据传输3要素: 源,目的,长度 */
	/* 源: USB设备的某个端点 */

	// 创建数据传输管道
	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

	/* 长度: */
	len = endpoint->wMaxPacketSize;

	/* 目的: */
	usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);

	/* 使用"3要素" */
	/* 分配usb request block */
	uk_urb = usb_alloc_urb(0, GFP_KERNEL); //usb传输数据的urb结构体
	/* 使用"3要素设置urb" */
	// 初始化urb数据结构
	usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
	uk_urb->transfer_dma = usb_buf_phys;  //设置DMA地址
	uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;  //设置使用DMA地址

	/* 使用URB */
	// 提交urb,以读取usb设备数据。
	usb_submit_urb(uk_urb, GFP_KERNEL);
	
	return 0;
}

static void usbmouse_as_key_disconnect(struct usb_interface *intf)
{
	struct usb_device *dev = interface_to_usbdev(intf);

	//printk("disconnect usbmouse!\n");
	usb_kill_urb(uk_urb);
	usb_free_urb(uk_urb);

	usb_buffer_free(dev, len, usb_buf, usb_buf_phys);
	input_unregister_device(uk_dev);
	input_free_device(uk_dev);
}

/* 1. 分配/设置usb_driver */
static struct usb_driver usbmouse_as_key_driver = {
	.name		= "usbmouse_as_key_",
	.probe		= usbmouse_as_key_probe,
	.disconnect	= usbmouse_as_key_disconnect,
	.id_table	= usbmouse_as_key_id_table,
};

static int usbmouse_as_key_init(void)
{
	/* 2. 注册 */
	usb_register(&usbmouse_as_key_driver);
	return 0;
}

static void usbmouse_as_key_exit(void)
{
	usb_deregister(&usbmouse_as_key_driver);	
}

module_init(usbmouse_as_key_init);
module_exit(usbmouse_as_key_exit);

MODULE_LICENSE("GPL");



版权声明:本文为qq_41483419原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。