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. 20170405xlVBA快速录入

    Dim Rng As Range Dim Arr As Variant Dim LastCell As Range Dim FindText As String Dim ItemCount As Lo ...

  2. fabric 学习笔记

    fabric安装 目前,从PyPI可以搜索到主要的fabric库为“ Fabric 2.1.3 ”.“ fabric2 2.1.3 ”和“ Fabric3 1.14.post1 ”. Fabric:官 ...

  3. ES批量索引写入时的ID自动生成算法

    对bulk request的处理流程: 1.遍历所有的request,对其做一些加工,主要包括:获取routing(如果mapping里有的话).指定的timestamp(如果没有带timestamp ...

  4. 消息队列的创建与读写ftok,msgget,msgsnd,msgrcv,指令ipcs,ipcrm 查看,删除消息队列

    ipcs是Linux下显示进程间通信设施状态的工具.可以显示消息队列.共享内存和信号量的信息.对于程序员非常有用,普通的系统管理员一般用不到此指令. ipcs -q 查看系统使用的IPC队列资源 ip ...

  5. 管道pipe与dup结合使用,应用实例

    管道的一种常见用法:在父进程创建子进程后向子进程传递参数.例如,一个应用软件有一个主进程和很多个不同子进程. 主进程创建子进程后,在子进程调用exec函数执行一个新程序前,通过管道给即将执行的程序传递 ...

  6. 062——VUE中vue-router之命名视图的实例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. Oracle 11g新特性 Interval Partition

    分区(Partition)一直是Oracle数据库引以为傲的一项技术,正是分区的存在让Oracle高效的处理海量数据成为可能,在Oracle 11g中,分区技术在易用性和可扩展性上再次得到了增强.在1 ...

  8. Active MQ的初步探索

    参考链接: http://blog.csdn.net/jiuqiyuliang/article/details/46701559 JMS是jee规范,active MQ是该规范的实现 Active M ...

  9. css rem计算

    先抛出一个问题:为什么要选择rem? px:像素是相对于显示器屏幕分辨率而言的相对长度单位.pc端使用px倒也无所谓,可是在移动端,因为手机分辨率种类颇多,不可能一个个去适配,这时px就显得非常无力, ...

  10. (转载)SAPI 包含sphelper.h编译错误解决方案

    [转]SAPI 包含sphelper.h编译错误解决方案 在使用Microsoft Speech SDK 5.1开发语音识别程序时,包含了头文件“sphelper.h”和库文件“sapi.lib”.编 ...