2024年最全《Linux驱动:USB设备驱动看这一篇就够了》_linux usb驱动,2024年最新干货整理

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

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;

}


##### 3.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);


hub\_probe 在后面向USB总线注册一个和hub驱动匹配的hub设备时会被调用。


#### 3.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,
};


#### 3.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;

}


#### 3.1.6 总结


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


### 3.2 USB主机控制器驱动(HCD)


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


#### 3.2.1 USB主机控制器-设备



// linux-2.6.22.6/arch/arm/mach-s3c2440/mach-smdk2440.c
// 这个流程和之前分析的lcd、tp一致。
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
}
};


#### 3.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”,
},
};


#### 3.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)


#### 3.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)


##### 3.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函数。


##### 3.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。


## 四,USB设备驱动 – USB鼠标


用于和枚举到的USB设备进行绑定,完成特定的功能。  
 比如USB鼠标设备,驱动开发主要是这一块代码的coding。


### 4.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;
}


### 4.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

### 4.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 鼠标设备的目的。


### 4.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));


### 4.5 分配urb


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



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


### 4.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;


### 4.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鼠标设备驱动还涉及输入子系统的内容,之前已经分析过,不在此再次记录。


### 4.8 总结


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


* 根据期望适用的USB设备信息构建一个id\_table。
* 根据需要的数据传输类型,调用相应的接口创建数据传输管道。
* 分配一个urb(USB请求块)。
* 根据需要的数据传输类型,调用相应的接口进行urb数据结构初始化。
* 提交urb


## 五,实现一个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;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *uk_urb;

static struct usb_device_id usbmouse_as_key_id_table [] = {
// 匹配HID,鼠标设备
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
//{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-松开

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

dev;
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *uk_urb;

static struct usb_device_id usbmouse_as_key_id_table [] = {
// 匹配HID,鼠标设备
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
//{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-松开

[外链图片转存中…(img-H5dU8cZW-1715634454279)]
[外链图片转存中…(img-entHPCGw-1715634454280)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值