1. usb_alloc_dev中的 bus_type 中指定了匹配函数,和uevent中的环境参数。 ====>不同的bus_type的匹配函数可能是不同的,uevent的
环境变量参数也可能是不同的。

2. 推荐《LINUX内核源代码情景分析》,里面讲USB控制器源代码讲解的非常清晰。

3.当USB设备插入的时候会调用 usb_driver 的probe(),当USB设备拔出的时候会调用 disconnect()函数,和其它非支持热插拔的有些不
不同。所以usb_driver结构体里面至少要初始化id_table,probe,disconnect.

4.一个逻辑设备使用一个接口标识,比如声卡有录音和播放音乐的功能,因此有两个接口,两个驱动,分别负责录音和播放。

5.写驱动时先参考别人的,然后搞出一个最简单的框架,然后在逐渐丰满它。

6.唯一的dev->devnum用作了构建pipe的参数,应该可以使USB知道通过这个pipe的数据是发给哪个设备的。
int pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
/*1<<30|(dev->devnum << 8) | (endpoint << 15)
| 0x80*/
#define usb_rcvintpipe(dev, endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)

7.中断传输是USB主机控制器不断地查询,然后有数据了USB主机控制器就中断CPU,然后回调urb完成回调函数。回调函数类似中断服务函数
一样被调用。

8.若不明白鼠标数据的含义,可以先把数据打印出来,然后移动/点击鼠标对比查看数据的含义。

10.# cat /dev/tty1 用来查看串口打印。

11.# hexdump /dev/input0 查看设备节点接收到的原始的数据,测试是OK的

12.目的:把一个USB鼠标用作键盘,参考drivers/hid/usbhid/usbmouse.c
测试:
1.make menuconfig去掉之前的mouse驱动。若不去掉内核会匹配上它。
  -> Device Drivers
    -> HID Devices
      <> USB Human Interface Device (full HID) support
2.加载自己的驱动,插拔USB鼠标测试。

实验源码:

/*
* drivers\hid\usbhid\usbmouse.c
*/ #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 [] = {
{ 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;
#if 0
int i;
static int cnt = ;
printk("data cnt %d: ", ++cnt);
for (i = ; 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 & (<<)) != (usb_buf[] & (<<)))
{
/* 左键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[] & (<<)) ? : );
input_sync(uk_dev);
} if ((pre_val & (<<)) != (usb_buf[] & (<<)))
{
/* 右键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[] & (<<)) ? : );
input_sync(uk_dev);
} if ((pre_val & (<<)) != (usb_buf[] & (<<)))
{
/* 中键发生了变化 */
input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[] & (<<)) ? : );
input_sync(uk_dev);
} pre_val = usb_buf[]; /* 重新提交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[].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. 硬件相关操作 */
/* 数据传输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(, GFP_KERNEL);
/* 使用"3要素设置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;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* 使用URB */
usb_submit_urb(uk_urb, GFP_KERNEL); return ;
} 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 ;
} 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");

参考内核中的usbmouse.c:

#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> /* for apple IDs */
#ifdef CONFIG_USB_HID_MODULE
#include "../hid-ids.h"
#endif /*
* Version Information
*/
#define DRIVER_VERSION "v1.6"
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB HID Boot Protocol mouse driver" MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL"); struct usb_mouse {
char name[]; /*存储的是dev->manufacturer*/
char phys[];
struct usb_device *usbdev;
struct input_dev *dev;
struct urb *irq; /*中断urb,也就是轮询鼠标*/ signed char *data; /*存储DMA地址对应的虚拟地址*/
dma_addr_t data_dma; /*存储DMA地址*/
}; static void usb_mouse_irq(struct urb *urb)
{
struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data;
struct input_dev *dev = mouse->dev;
int status; switch (urb->status) {
case : /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
} /*是把所有的坐标上报一遍,按下和松开App都可以监听到了。若是没有任
何改变的话这个完成函数应该不会被调用的*/
input_report_key(dev, BTN_LEFT, data[] & 0x01); /*鼠标左键*/
input_report_key(dev, BTN_RIGHT, data[] & 0x02); /*鼠标右键*/
input_report_key(dev, BTN_MIDDLE, data[] & 0x04); /*鼠标中键*/
input_report_key(dev, BTN_SIDE, data[] & 0x08);
input_report_key(dev, BTN_EXTRA, data[] & 0x10); input_report_rel(dev, REL_X, data[]);
input_report_rel(dev, REL_Y, data[]);
input_report_rel(dev, REL_WHEEL, data[]); input_sync(dev);
resubmit:
/*open 输入设备节点的时候submit一次,之后每次urb完成后都重复submit已保持
一致处于工作状态*/
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
dev_err(&mouse->usbdev->dev,
"can't resubmit intr, %s-%s/input0, status %d\n",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
} static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev); /*probe中set进去的*/ mouse->irq->dev = mouse->usbdev;
if (usb_submit_urb(mouse->irq, GFP_KERNEL))
return -EIO; return ;
} static void usb_mouse_close(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev); usb_kill_urb(mouse->irq);
} static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
/*通过接口获取usb_device结构体*/
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; /* the currently active alternate setting */ /*进一步判断是不是鼠标,这里认为鼠标只有1个IN端点的*/
if (interface->desc.bNumEndpoints != )
return -ENODEV; endpoint = &interface->endpoint[].desc; /*只有1个端点,所以只能是endpoint[0]*/
if (!usb_endpoint_is_int_in(endpoint)) /*进一步判断是不是中断输入端点,无论是主从设备的端点名都是以host为参考起方向名字的*/
return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); /*传参(dev, pipe, 0),返回:epd->wMaxPacketSize & USB_ENDPOINT_MAXP_MASK;*/ /*构建usb鼠标这个设备*/
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1; /*mouse->data中存储虚拟地址,mouse->data_dma存储DMA地址*/
mouse->data = usb_alloc_coherent(dev, , GFP_ATOMIC, &mouse->data_dma);
if (!mouse->data)
goto fail1; /*看来host是在轮询鼠标读取鼠标中的数据*/
mouse->irq = usb_alloc_urb(, GFP_KERNEL);
if (!mouse->irq)
goto fail2; mouse->usbdev = dev;
mouse->dev = input_dev; if (dev->manufacturer)
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name)); if (dev->product) {
if (dev->manufacturer)
strlcat(mouse->name, " ", sizeof(mouse->name)); /*加一个空格*/
strlcat(mouse->name, dev->product, sizeof(mouse->name));
} if (!strlen(mouse->name)) /*如果这个usb设备没有指定manufacturer和product字符使用VID PID作为名字*/
snprintf(mouse->name, sizeof(mouse->name),
"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct)); usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
strlcat(mouse->phys, "/input0", sizeof(mouse->phys)); input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
usb_to_input_id(dev, &input_dev->id);
input_dev->dev.parent = &intf->dev; /*构建设备在设备树中的层次结构*/ input_dev->evbit[] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); /*对于鼠标有按键和滚轮和相对位移,所以是这两个*/
/*左键 中键 右键*/
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
/*只有x和y方向,没有z的*/
input_dev->relbit[] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
/*这两个是什么意思 ???? */
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
input_dev->relbit[] |= BIT_MASK(REL_WHEEL); input_set_drvdata(input_dev, mouse); /*所有的输入设备都必须要提供这两个函数*/
input_dev->open = usb_mouse_open; /*里面仅仅提交了一个urb*/
input_dev->close = usb_mouse_close; /*里面只做了kill urb的操作*/ /*完成回调函数usb_mouse_irq,当USB主机控制器收到数据后会产生一个中断,然后usb_mouse_irq会被调用*/
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > ? : maxp),
usb_mouse_irq, mouse, endpoint->bInterval); /*endpoint->bInterval: 这里查询的频率是由设备自己决定的*/
mouse->irq->transfer_dma = mouse->data_dma; /*上面usb_fill_int_urb中的赋值是传给不使用DMA的transfer_buffer的*/
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* urb->transfer_dma valid on submit */ error = input_register_device(mouse->dev);
if (error)
goto fail3; /*为了注销时使用mouse结构*/
usb_set_intfdata(intf, mouse);
return ; fail3:
usb_free_urb(mouse->irq);
fail2:
usb_free_coherent(dev, , mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
} static void usb_mouse_disconnect(struct usb_interface *intf)
{
struct usb_mouse *mouse = usb_get_intfdata (intf); usb_set_intfdata(intf, NULL);
if (mouse) {
usb_kill_urb(mouse->irq);
input_unregister_device(mouse->dev);
usb_free_urb(mouse->irq);
usb_free_coherent(interface_to_usbdev(intf), , mouse->data, mouse->data_dma);
kfree(mouse);
}
} static const struct usb_device_id usb_mouse_id_table[] = {
/*表示只要接口的class是hid,子class是boot,协议是mouse,都可以支持*/
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) },
{ USB_DEVICE(0x1234, 0x5678) }, /*I add it, 表示只支持这一款设备*/
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); static struct usb_driver usb_mouse_driver = {
.name = "usbmouse",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
}; module_usb_driver(usb_mouse_driver)

USB设备驱动_WDS的更多相关文章

  1. usb设备驱动描述,王明学learn

    usb设备驱动 本章主要内容包含以下:USB总线介绍,USB协议分析,USB系统架构 一.USB总线介绍 1.1USB发展史 USB(Universal Serial Bus)通用串行总线,是一种外部 ...

  2. USB设备驱动

    在Linux 内核中,使用usb_driver 结构体描述一个USB 设备驱动,usb_driver 结构体的定义如代码清单20.11 所示.代码清单20.11 usb_driver 结构体 stru ...

  3. USB设备驱动概述

    USB设备驱动 ·  )USB Hub:每个USBHost控制器都会自带一个USB Hub,被称为根(Root)Hub.这个根Hub可以接子(Sub)Hub,每个Hub上挂载USB设备.一般PC有8个 ...

  4. Linux下 USB设备驱动分析(原创)

    之前做过STM32的usb HID复合设备,闲来看看linux下USB设备驱动是怎么一回事, 参考资料基于韦东山JZ2440开发板,以下,有错误欢迎指出. 1.准备知识 1.1USB相关概念: USB ...

  5. Linux下usb设备驱动详解

    USB驱动分为两块,一块是USB的bus驱动,这个东西,Linux内核已经做好了,我们可以不管,我们只需要了解它的功能.形象的说,USB的bus驱动相当于铺出一条路来,让所有的信息都可以通过这条USB ...

  6. Linux USB驱动学习总结(二)---- USB设备驱动

    USB 设备驱动: 一.USB 描述符:(存在于USB 的E2PROM里面) 1.  设备描述符:struct usb_device_descriptor 2.  配置描述符:struct usb_c ...

  7. 嵌入式Linux驱动学习之路(二十)USB设备驱动

    USB在接入系统的时候,以0的设备ID和主机通信,然后由主机为其分配新的ID. 在主机端,D+和D-都是下拉接地的.而设备端的D-接上拉时,表明此设备为高速设备:12M/s. D+接上拉时则是全速设备 ...

  8. Linux usb子系统(二):USB设备驱动usb-skeleton.c

    usb驱动分为通过usbfs操作设备的用户空间驱动,内核空间的内核驱动.两者不能同时进行,否则容易引发对共享资源访问的问题,死锁!使用了内核驱动,就不能在usbfs里驱动该设备. 下面转载的一篇分析u ...

  9. USB2.0学习笔记连载(九):USB设备驱动的安装

    在第一次插入USB设备时(笔者用的是自己做的USB最小系统来测试),插入电脑后,在设备管理器中会显示 未知设备,如下图所示: 点击右键,选择属性,在详细信息中可以看到硬件ID以及PID等,如下图所示. ...

随机推荐

  1. English trip -- VC(情景课)5 C It's on Main Street 在主街上

    Grammar focus 语法点: on,   在...上 next to ,   旁边,周围 aross from ,  对面 between  在...之间 in front of  在…前面 ...

  2. Confluence 6 LDAP 用户组结构设置

    用户组对象类(Group Object Class) 这是在 LDAP 用户组对象中使用的类的名字.例如: groupOfUniqueNames group 用户组对象过滤器(Group Object ...

  3. Imbalance Value of a Tree CodeForces - 915F

    链接 大意: 给定树, 求树上所有链上最大值最小值之差 817D的树上版本, 用并查集维护即可. 817D由于是链的情况并查集不必压缩路径即可达到均摊$O(n)$, 该题必须压缩, 复杂度$O(nlo ...

  4. python-day43--多表查询

    一.多表连接查询:       #重点:外链接语法 准备表 #建表 create table department( id int, name varchar(20) ); create table ...

  5. spring boot 基础篇 -- 自带图片服务器

    我们平时在日常项目中经常会遇到图片的上传和访问的情景,平时我们可能习惯于把图片传到resource或者项项目中的某个位置,这样会有一个缺点,当我们重新项目打包时,这些图片会丢失.为了解决这一缺点,我们 ...

  6. 指定变形中心点CSS3

    <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head>    < ...

  7. spring cloud图形化dashboard是如何实现指标的收集展示的

    spring cloud图形化dashboard是如何实现指标的收集展示的 1.dashboard图形化界面入口 http://localhost:10000/hystrix.stream 说明:端口 ...

  8. iOS UI-常用控件

    #import "ViewController.h" @interface ViewController ()<UITextFieldDelegate> // 标题标签 ...

  9. haproxy配置示例

    1.最基础的的配置 下面的例子配置了一个监听在所有接口的80端口上HTTP proxy服务,它转发所有的请求至后端监听在127.0.0.1:8000上的"server". glob ...

  10. 用MyEclipse JPA创建项目(一)

    MyEclipse 3.15 Style——在线购买低至75折!火爆开抢>> [MyEclipse最新版下载] 本教程介绍了MyEclipse中的一些基于JPA的功能. 阅读本教程时,了解 ...