当前位置:海洋目录网 » 站长资讯 » 站长资讯 » 文章详细 订阅RssFeed

13.USB驱动

来源:本站原创 浏览:17次 时间:2021-12-07

一、情景分析

现象:把USB设备接到PC

  1. 右下角弹出"发现android phone"
  2. 跳出一个对话框,提示你安装驱动程序

问1. 既然还没有"驱动程序",为何能知道是"android phone"
答1. windows里已经有了USB的总线驱动程序,接入USB设备后,是"总线驱动程序"知道你是"android phone"
提示你安装的是"设备驱动程序"
USB总线驱动程序负责:识别USB设备, 给USB设备找到对应的驱动程序

问2. USB设备种类非常多,为什么一接入电脑,就能识别出来?
答2. PC和USB设备都得遵守一些规范。
比如: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. PC的USB口内部,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读写函数
同样linux内核也自带了USB总线驱动程序,框架如下

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

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

3.提供USB读写函数
要想成为一个USB主机,硬件上就必须要有USB主机控制器,USB主机控制器又分为4种接口:

OHCI(Open Host Controller Inerface):微软主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps),OHCI接口的软件简单,硬件复杂

UHCI(Universal Host Controller Interface):Intel主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps),而UHCI接口的软件复杂,硬件简单

EHCI(Enhace Host Controller Interface):高速USB2.0(480Mbps)

xHCI(eXtensible Host Controller Interface):USB3.0(5.0Gbps),采用了9针脚设针,同时也支持USB2.0、1.1等

二、分析USB总线驱动

1、drivers/usb/core/hub.c的第2186行位于hub_port_init()函数里
它又是被谁调用的呢,如下图所示,我们搜索到它是通过hub_thread()函数调用的

hub_thread()函数如下:

static int hub_thread(void *__unused){do {hub_events();    //执行页次hub时间函数wait_event_interruptible(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop());//每次执行一次hub事件,都会进入一次等待事件中断函数try_to_freeze();} while (!kthread_should_stop() || !list_empty(&hub_event_list)); pr_debug("%s: khubd exiting\n", usbcore_name);return 0;}

从上面函数中得到,要想执行hub_event(),都要等待khubd_wait这个中断唤醒才行
2、我们搜索“khubd_wait”,看看是被谁唤醒
找到该中断在kick_khubd()函数中唤醒,代码如下:

static void kick_khubd(struct usb_hub *hub){unsigned longflags; /* Suppress autosuspend until khubd runs */to_usb_interface(hub->intfdev)->pm_usage_cnt = 1; spin_lock_irqsave(&hub_event_lock, flags);if (list_empty(&hub->event_list)) {list_add_tail(&hub->event_list, &hub_event_list);wake_up(&khubd_wait);//唤醒khubd_wait这个中断}spin_unlock_irqrestore(&hub_event_lock, flags);}

3、继续搜索kick_khubd,发现被hub_irq()函数中调用
显然,就是当USB设备插入后,D+ 或 D- 就会被拉高,然后USB主机控制器就会产生一个hub_irq中断

4、接下来我们直接分析hub_port_connect_chage()函数,如何连接端口的

static void hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange){... ...udev = usb_alloc_dev(hdev, hdev->bus, port1);//(1)注册usb_device,然后会放在usb总线上usb_set_device_state(udev, USB_STATE_POWERED);//设置注册的USB设备的状态标识... ...choose_address(udev);//(2)给新的设备飞培一个地址编号status = hub_port_init(hub, udev, port1, i)������,����׼;//(3)初始化端口,与USB设备建立连接... ... status = usb_new_device(udev);//(4)创建USB设备,与USB设备驱动连接}

(usb_device设备结构体参考:https://mp.csdn.net/postedit/81025137)
所以最终流程图如下:

5、我们进入hub_port_connect_change()->usb_alloc_dev(),来看看它是怎么设置usb_device的

struct usb_device * usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1){struct usb_device *dev; dev = kzalloc(sizeof(*dev), GFP_KERNEL);//分配一个usb_device设备... ... device_initialize(&dev->dev);//初始化usb_devicedev->dev.bus = &usb_bus_type;//(1)设置usb_device的成员device->bus等于usb_bus_type总线dev->dev.type = &usb_device_type;//(2)设置usb_device的成员device->type等于usb_device_type...return dev;//返回一个usb_device结构体}

(1)设置device成员bus,主要是二十小节,注册usb总线的device表上,

其中usb_bus_type是一个全局变量,它和我们之前学的platform平台总线相似,属于USB总线,是Linux中bus的一种。

如下图所示,每当创建一个USB设备,或者USB设备驱动时,USB总线都会调用match成员来匹配一次,使USB设备和USB驱动联系起来。

usb_bus_type结构体如下:

struct bus_type usb_bus_type = {.name ="usb",//总线名称,存在/sys/bus.match =usb_device_match,//匹配函数,匹配成功就会调用usb_driver驱动的probe函数成员.uevent =usb_uevent,//事件函数.suspend =usb_suspend,//休眠函数.resume =usb_resume,//唤醒函数};

6、我们进入hub_port_connect_change()->choose_address(),来看看它是怎么分配地址编号的

static void choose_address(struct usb_device *udev){intdevnum;struct usb_bus*bus = udev->bus; devnum = find_next_zero_bit(bus->devmap.devicemap, 128,bus->devnum_next);//在bus->devnum_next~128区间中,循环查找下一个非0(没有设备)的编号if (devnum >= 128)//若编号大于等于128,说明没有找到空余的地址编号,从头开始找devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1); bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);//设置下次寻址的区间+1 if (devnum < 128) {set_bit(devnum, bus->devmap.devicemap);//设置位udev->devnum = devnum;}}

从上面代码中分析每次的地址编号是连续加的,USB接口最大能接127个设备,我们连续插拔两次USB键盘,可以看出,如下图所示:

7、我们再来看看hub_port_connect_chage()->hub_port_init()函数是如何来实现连接USB设备的

static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter){...for (j = 0; j < SET_ADDRESS_TRIES; ++j) {retval = hub_set_address(udev);//(1)设置地址,告诉USB设备新的地址编号if (retval >= 0)break;msleep(200);}...retval = usb_get_device_descriptor(udev, 8);//(2)获得USB设备描述符前8个字节...retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);//重新获取设备描述符信息}

(1)hub_set_address()函数主要是用来告诉USB设备新的地址编号,
hub_set_address()函数如下:

static int hub_set_address(struct usb_device *udev){int retval; retval = usb_control_msg(udev, usb_sndaddr0pipe(),//(1)USB_REQ_SET_ADDRESS, 0, udev->devnum, 0,NULL, 0, USB_CTRL_SET_TIMEOUT);  if (retval == 0) {//设置新的地址,传输完成,返回0usb_set_device_state(udev, USB_STATE_ADDRESS);//设置状态标志ep0_reinit(udev);}return retval;}

usb_control_msg()函数就是用来让USB主机控制器把一个控制报文发给USB设备,如果传输完成就返回0,其中参数udev表示目标设备;使用管道为usb_sndaddr0pipe(),也就是默认的地址0加上控制端点号0;USB_REQ_SET_ADDRESS表示命令码,既设置地址;udev->devnum表示设置目标设备的设备号;允许等待传输完成的时间为5秒,因为USB_CTRL_SET_TIMEOUT定义为5000。

(2)usb_get_device_descriptor()函数主要是获取目标设备描述符前8个字节,为什么先只开始读取8个字节?是因为开始时还不知道对方说支持的信包容量,这8个字节是每个设备都有的,后面再根据设备的数据,通过usb_get_device_descriptor()重读一次目标设备的设备描述结构。

其中USB设备描述符结构体如下所示:

/* USB_DT_DEVICE: Device descriptor */struct usb_device_descriptor {__u8  bLength;    //本描述符的size__u8  bDescriptorType;//描述符的类型,这里是设备描述符DEVICE __le16 bcdUSB;//指名usb的版本,比如usb2.0__u8  bDeviceClass;//类__u8  bDeviceSubClass;//子类__u8  bDeviceProtocol;//指定协议__u8  bMaxPacketSize0;//端点0对应的最大包大小__le16 idVendor;//厂家ID__le16 idProduct;//产品ID__le16 bcdDevice;//设备的发布号__u8  iManufacturer;//字符串描述符中厂家ID的索引__u8  iProduct;//字符串描述符中产品ID的索引__u8  iSerialNumber;//字符串描述符中设备序列号的索引__u8  bNumConfigurations;//可能的配置的树木} __attribute__ ((packed));

8、我们来看看hub_port_connect_change()->usb_new_device()函数是如何来创建USB设备的

int usb_new_device(struct usb_device *udev){...err = usb_get_configuration(udev);//(1)获取配置描述块...err = device_add(&udev->dev);//(2)把device放入bus的dev链表中,并寻找对应的设备驱动...}

(1)其中usb_get_configuration()函数如下,就是获取各个配置

int usb_get_configuration(struct usb_device *dev){//USB_MAXCONFIG定义为8,表示设备描述块下最多不能超过8个配置描述块if (ncfg > USB_MAXCONFIG) {//nfs表示 设备描述块下 有多少个配置描述块dev_warn(ddev, "too many configurations: %d, ""using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;} for (cfgno = 0; cfgno < ncfg; cfgno++) //for循环,从USB设备里一次读入所有配置描述块{length = max((int) le16_to_cpu(desc->wTotalLength),USB_DT_CONFIG_SIZE);//通过wTotalLength,知道实际数据大小 bigbuffer = kmalloc(length, GFP_KERNEL);//然后分配足够大的空间...result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,bigbuffer, length);//在调用一次usb_get_descriptor,把整个配置描述块读出来,放到bifbuffer中...dev->rawdescriptors[cfgno] = bigbuffer;//再将bifbuffer地址放在ramdescriptor所指的指针数组中 result = usb_parse_configuration(&dev->dev, cfgno,&dev->config[cfgno], bigbuffer, length);//最后解析每个配置块}}

(2)其中device_add()函数如下:

int device_add(struct device *dev){...dev = get_device(dev);//使dev等于usb_device下的device成员...if ((error = bus_add_device(dev)))//把这个设备添加带dev->bus的device表中goto BusError;...bus_attach_device(dev);//来匹配对应的驱动程序}

当bus_attach_device()函数匹配成功,就会调用驱动的probe函数
9、我们再来看看usb_bus_type这个的成员usb_device_match函数,看看是如何匹配的

usb_device_match函数如下所示:

static int usb_device_match(struct device *dev, struct device_driver *drv){if (is_usb_device(dev)) {//判断是不是USB设备 if (!is_usb_device_driver(drv))return 0; return 1; } else {    //否则就是USB驱动或者USB设备的接口struct usb_interface *intf;struct usb_driver *usb_drv;const struct usb_device_id *id; if (is_usb_device_driver(drv))//如果是USB驱动,就不需要匹配,直接returnreturn 0; intf = to_usb_interface(dev);//获取USB设备的接口usb_drv = to_usb_driver(drv);//获取USB驱动 id = usb_match_id(intf, usb_drv->id_table);//匹配USB驱动的成员id_tableif (id)return 1; id = usb_match_dynamic_id(intf, usb_drv);if (id)return 1;} return 0;}

显然就是匹配USB驱动的id_table
10、那么USB驱动的id_table又该如何定义
id_table的结构体为usb_device_id,如下所示:

struct usb_device_id { __u16match_flags;//与usb设备匹配哪种类型?比较类型的宏如下://USB_DEVICE_ID_MATCH_INT_INFO:用于匹配设备的接口描述符的3个成员//USB_DEVICE_ID_MATCH_DEV_INFO:用于匹配设备描述符的3个成员//USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION:用于匹配特定的USB设备的4个成员//USB_DEVICE_ID_MATCH_DEVICE:用于匹配特定的USB设备的2个成员(idVendor和idProduct)  /* 以下4个用匹配描述特定的USB设备 */__u16idVendor;//厂家ID__u16idProduct;//产品ID__u16bcdDevice_lo;//设备的低版本号__u16bcdDevice_hi;//设备的高版本号 /* 以下3个就是用于比较设备描述符 */__u8bDeviceClass;//设备类__u8bDeviceSubClass;//设备子类__u8bDeviceProtocol;//设备协议 /* 以下3个就是用于比较设备的接口描述符的 */__u8bInterfaceClass;//接口类型__u8bInterfaceSubClass;//接口子类型__u8bInterfaceProtocol;//接口所遵循的协议 /* not matched against */kernel_ulong_tdriver_info;}

(设备描述符接口描述符结构体参考:https://mp.csdn.net/postedit/81025137)
我们参考/drivers/hid/usbhid/usbmouse.c(内核自带的USB鼠标驱动),是如何使用的,如下图所示:

发现它是通过USB_INTERFACE_INFO这个宏定义的,该宏如下所示:

#define USB_INTERFACE_INFO(cl,sc,pr) \.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, //设置id_table的,match_flags成员.bInterfaceClass = (cl), \.bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)//设置id_table的3个成员,用于匹配USB设备的3个成员

然后将上图的usb_mouse_id_table[]里的3个值代入宏
USB_INTERFACE_INFO(cl,sc,pr)中:

.bInterfaceClass =USB_INTERFACE_CLASS_HID;     //设置匹配USB的接口类型为HID类, 因为USB_INTERFACE_CLASS_HID=0x03   //HID类是属于人机交互的设备,比如:USB键盘,USB鼠标,USB触摸板,USB游戏操作杆都要填入0X03 .bInterfaceSubClass =USB_INTERFACE_SUBCLASS_BOOT;     //设置匹配USB的接口子类型为启动设备 .bInterfaceProtocol=USB_INTERFACE_PROTOCOL_MOUSE;  //设置匹配USB的接口协议为USB鼠标的协议,等于2  //当.bInterfaceProtocol=1也就是USB_INTERFACE_PROTOCOL_KEYBOARD时,表示USB键盘的协议

如下图,我们也可以通过windows下也可以找到鼠标的协议号,是1(Col01):

其中VID:表示厂家(vendor)ID

PID:表示产品(Product)ID

总结:当我们插上USB设备时,系统就会获取USB设备的配置、接口、端点的数据,并创建设备,所以我们的驱动就需要写id_table来匹配该USB设备

三、ch9.h描述符分析
Ch9.h/* USB_DT_DEVICE: Device descriptor */struct usb_device_descriptor {__u8  bLength;__u8  bDescriptorType;__le16 bcdUSB;/*usb版本号*/__u8  bDeviceClass;/*类*/__u8  bDeviceSubClass;/*子类*/__u8  bDeviceProtocol;/*协议*/__u8  bMaxPacketSize0;/*端点0  每个设备都有*/__le16 idVendor;/*厂家ID*/__le16 idProduct;/*产品ID*/__le16 bcdDevice;__u8  iManufacturer;__u8  iProduct;__u8  iSerialNumber;__u8  bNumConfigurations;/*改设备有多少种配置*/} __attribute__ ((packed));/*配置描述符*//* 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));/*接口描述符  写驱动就是给逻辑上的设备写的。一个usb硬件可能有多个逻辑设备,有多个逻辑设备就会安装多个驱动程序,多个端点*//* 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));/*端点描述符  一次性传输多大的数据,端点编号,方向,传输类型*//* USB_DT_ENDPOINT: Endpoint descriptor */struct usb_endpoint_descriptor {__u8  bLength;__u8  bDescriptorType;__u8  bEndpointAddress;/*端点地址,端点1 2 3 4*/__u8  bmAttributes;/*属性。表示哪一种端点。实时?批量?中断?*/__le16 wMaxPacketSize;/*最大包大小,一次性可以给多少数据*/__u8  bInterval;/*中断传输时 查询频率*//* NOTE:  these two are _only_ in audio endpoints. *//* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */__u8  bRefresh;__u8  bSynchAddress;} __attribute__ ((packed));

四、写程序

关键部分参考usbmouse.c




/* * drivers\hid\usbhid\usbmouse.c * 鼠标模拟键盘 * 左键:L 右键:S 滚轮按下:ENTER */#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;/*INTERFACE_INFO接口信息:只要类HID, 子类BOOT,协议为MOUSE 就可以支持*/static struct usb_device_id usbmouse_as_key_id_table [] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },//{USB_DEVICE(0x1234,0x5678)}, (厂家ID,设备ID)这么写就支持特定厂家的驱动。{ }/* Terminating entry */};static void usbmouse_as_key_irq(struct urb *urb){static unsigned char pre_val;#if 0int i;static int cnt = 0;printk("data cnt %d: ", ++cnt);for (i = 0; i < len; i++){printk("%02x ", usb_buf[i]);}printk("\n");#endif/* 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);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 能产生哪类事件 */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. 硬件相关操作 根据USB总线提供的驱动程序收发USB数据*//* 数据传输3要素: 源,目的,长度 *//* 源: USB设备的某个端点 */pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//usb_rcvintpipe中包含设备地址和端点地址pipe 为整数  包含了 二者//或运算合成一个整数/* 长度: */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);/* 使用"3要素设置urb" */usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);//bInterval 主机查询频率 ,主机得到数据产生中断调用usbmouse_as_key_irquk_urb->transfer_dma = usb_buf_phys;//USB控制器得到数据后要写的物理地址uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;/* 使用URB */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");

五、测试
  1. insmod usbmouse_as_key.ko
  2. ls /dev/event*
  3. 接上USB鼠标
  4. ls /dev/event*
  5. cat /dev/tty1 然后按鼠标键
  6. hexdump /dev/event0

  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net