1 了解USB识别的过程

eg:在Windows系统下的一个现象:把手机的USB设备接到PC
  1. 右下角弹出"发现android phone"
  2. 跳出一个对话框,提示你安装驱动程序

问1. 既然还没有"驱动程序",为何能知道是"android phone"
答1. windows里已经有了USB的总线驱动程序,接入USB设备后,是"总线驱动程序"知道你是"android phone"
     提示你安装的是"设备驱动程序"。

问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有新设备接入

2 USB的其他概念

2.1 USB是主从结构的
   所有的USB传输,都是从USB主机这方发起;USB设备没有"主动"通知USB主机的能力。
   例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来读数据,只能被动地等得PC机来读。

2.2 USB的传输类型:
a. 控制传输:可靠,时间有保证,比如:USB设备的识别过程
b. 批量传输: 可靠, 时间没有保证, 比如:U盘
c. 中断传输:可靠,实时,比如:USB鼠标
d. 实时传输:不可靠,实时,比如:USB摄像头

2.3 USB传输的对象:端点(endpoint)
   我们说"读U盘"、"写U盘",可以细化为:把数据写到U盘的端点1,从U盘的端点2里读出数据
   除了端点0外,每一个端点只支持一个方向的数据传输
   端点0用于控制传输,既能输出也能输入
   
2.4 每一个端点都有传输类型,传输方向

2.5 术语里、程序里说的输入(IN)、输出(OUT) "都是" 基于USB主机的立场说的。
   比如鼠标的数据是从鼠标传到PC机, 对应的端点称为"输入端点"
     
2.6 USB总线驱动程序的作用
a. 识别USB设备
b. 查找并安装对应的设备驱动程序
c. 提供USB读写函数

3 USB驱动程序框架

app:   
-------------------------------------------
          USB设备驱动程序      // 知道数据含义
内核 --------------------------------------
          USB总线驱动程序      // 1. 识别, 2. 找到匹配的设备驱动, 3. 提供USB读写函数 (它不知道数据含义)
-------------------------------------------
           USB主机控制器
           UHCI OHCI EHCI
硬件        -----------
              USB设备

UHCI: intel,     低速(1.5Mbps)/全速(12Mbps)
OHCI: microsoft  低速/全速
EHCI:            高速(480Mbps)

小结:

USB总线驱动程序的作用
1. 识别USB设备
1.1 分配地址
1.2 并告诉USB设备(set address)
1.3 发出命令获取描述符
描述符的信息可以在include\linux\usb\Ch9.h看到
2. 查找并安装对应的设备驱动程序
3. 提供USB读写函数

4 实践并分析USB的识别过程

把手机设备插入USB到开发板,看输出结果。

输出结果:

usb 1-1: new full speed USB device using s3c2410-ohci and address 2
usb 1-1: configuration #1 chosen from 1 choice
scsi0 : SCSI emulation for USB Mass Storage devices
scsi 0:0:0:0: Direct-Access     Linux    File-Stor Gadget 0310 PQ: 0 ANSI: 2
sd 0:0:0:0: [sda] 5586 2048-byte hardware sectors (11 MB)
sd 0:0:0:0: [sda] Write Protect is on
sd 0:0:0:0: [sda] Assuming drive cache: write through
sd 0:0:0:0: [sda] 5586 2048-byte hardware sectors (11 MB)
sd 0:0:0:0: [sda] Write Protect is on
sd 0:0:0:0: [sda] Assuming drive cache: write through
 sda: unknown partition table
sd 0:0:0:0: [sda] Attached SCSI removable disk

拔掉后,串口输出的结果:
usb 1-1: USB disconnect, address 2

在内核目录下:grep "USB device using" * -nR查看调用过程

drivers/usb/core/hub.c:2186:              "%s %s speed %sUSB device using %s and address %d\n",

hub_irq
    kick_khubd
        hub_thread
            hub_events
                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 2
                        
                        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

5 分析可知USB设备的驱动框架

(1)分配usb_driver结构体

(2)设置usb_driver结构体

  .id_table

  .probe

  .disconnect

(3)注册

6 写代码

参考usbmouse.c

------------------------------------------------------------编辑于2017-01-12 00:46:47

6.1 分析usbmouse.c

(1)usb_mouse_init函数:注册usb_mouse_driver结构体

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

usb_mouse_driver结构体的原型:

 static struct usb_driver usb_mouse_driver = {
.name = "usbmouse",
.probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table,
};

如果id_table相匹配,则调用probe函数。

(2)probe函数

 static int usb_mouse_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;//端点描述符
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
int error = -ENOMEM; interface = intf->cur_altsetting; //接口定义 if (interface->desc.bNumEndpoints != )
return -ENODEV; endpoint = &interface->endpoint[].desc; //端点定义
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV; pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); //源
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));//数据包的大小,即长度 mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
input_dev = input_allocate_device();//注册input_dev结构体
if (!mouse || !input_dev)
goto fail1; mouse->data = usb_buffer_alloc(dev, , GFP_ATOMIC, &mouse->data_dma);//目的
if (!mouse->data)
goto fail1; mouse->irq = usb_alloc_urb(, GFP_KERNEL);//分配一个urb结构体
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))
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结构体*/
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(EV_KEY) | BIT(EV_REL);
input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
input_dev->relbit[] = BIT(REL_X) | BIT(REL_Y);
input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
input_dev->relbit[] |= BIT(REL_WHEEL); input_set_drvdata(input_dev, mouse); input_dev->open = usb_mouse_open;
input_dev->close = usb_mouse_close;
    /*设置urb结构体,并提交urb结构体*/
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > ? : maxp),
usb_mouse_irq, mouse, endpoint->bInterval);
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
  /*注册input_dev结构体*/
error = input_register_device(mouse->dev);
if (error)
goto fail3; usb_set_intfdata(intf, mouse);
return ; fail3:
usb_free_urb(mouse->irq);
fail2:
usb_buffer_free(dev, , mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}

6.2 写代码

写一个USB鼠标作为按键,左键为"l",右键为"s",滚轮为"ENTER",命名为usbmouse_as_key.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 struct urb *uk_urb;
static int len; static void usbmouse_as_key_irq(struct urb *urb)
{
static unsigned char pre_val;
if((pre_val & (<<)) != (usb_buf[] & (<<)))
{
input_report_key(uk_dev,KEY_L,(usb_buf[] & (<<)) );
input_sync(uk_dev);
}
if((pre_val & (<<)) != (usb_buf[] & (<<)))
{
input_report_key(uk_dev,KEY_S,(usb_buf[] & (<<)) );
input_sync(uk_dev);
}
if((pre_val & (<<)) != (usb_buf[] & (<<)))
{
input_report_key(uk_dev,KEY_ENTER,(usb_buf[] & (<<)) );
input_sync(uk_dev);
}
pre_val = usb_buf[]; 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; uk_dev = input_allocate_device(); set_bit(EV_KEY,uk_dev->evbit);
set_bit(EV_REP,uk_dev->evbit); set_bit(KEY_L,uk_dev->keybit);
set_bit(KEY_S,uk_dev->keybit);
set_bit(KEY_ENTER,uk_dev->keybit); input_register_device(uk_dev); pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
usb_buf = usb_buffer_alloc(dev,len, GFP_ATOMIC, &usb_buf_phys);
len = endpoint->wMaxPacketSize; uk_urb = usb_alloc_urb(, GFP_KERNEL);
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;
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);
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);
} 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) },
{ } /* Terminating entry */
}; 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)
{
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_as_key.c

调试方法:cat /dev/tty1

---------------------------------------------------------------编辑于2017-01-13 00:25:38

驱动07.USB驱动程序的更多相关文章

  1. linux驱动之USB驱动程序

    1. USB是主从结构的 所有的USB传输,都是从USB主机这方发起:USB设备没有"主动"通知USB主机的能力. 例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来 ...

  2. Linux驱动之USB总线驱动程序框架简析

    通用串行总线(USB)是主机和外围设备之间的一种连接.USB总线规范有1.1版和2.0版,当然现在已经有了3.0版本.USB1.1支持两种传输速度:低速为1.5Mbps,高速为12Mbps.USB2. ...

  3. 【驱动】USB驱动·入门

    [驱动]USB驱动·入门 Preface USB是目前最流行的系统总线之一.随着计算机周围硬件的不断扩展,各种设备使用不同的总线接口,导致计算机外部总线种类繁多,管理困难.USB总线正是因此而诞生的. ...

  4. 用DriverStudio开发USB驱动程序

    很多写Windows Device Driver的开发人员基本上都是使用Windows DDK进行开发的.但是,现在也有不少人都开始借助一些辅助工具.笔者去年开始接触到DriverStudio,发现它 ...

  5. usb驱动程序小结(六)

    title: usb驱动程序小结 tags: linux date: 2018/12/20/ 17:59:51 toc: true --- usb驱动程序小结 linux中为usb驱动也提供了一套总线 ...

  6. Linux驱动之USB鼠标驱动编写

    本篇博客分以下几部分讲解 1.介绍USB四大描述 2.介绍USB鼠标驱动程序功能及框架 3.介绍程序用到的结构体 4.介绍程序用到的函数 5.编写程序 6.测试程序 1.介绍USB四大描述符 USB设 ...

  7. 乾坤合一~Linux设备驱动之USB主机和设备驱动

    如果不能陪你到最后 是否后悔当初我们牵手 如果当初没能遇见你 现在的我 在哪里逗留 所有的爱都是冒险 那就心甘情愿 等待我们一生中 所有悬念 我一往情深的恋人 她是我的爱人 她给我的爱就像是 带着露水 ...

  8. 获取 Google USB 驱动程序

    获取 Google USB 驱动程序       另请参阅 安装 USB 驱动程序 使用硬件设备 使用任何 Google Nexus 设备进行 ADB 调试时,只有 Windows 需要 Google ...

  9. Linux usb 驱动程序范例

                     linxu_usb驱动之框架 USB骨架程序可以被看做一个最简单的USB设备驱动的实例. 首先看看USB骨架程序的usb_driver的定义 [cpp] view p ...

随机推荐

  1. leetcode[61] Unique Paths

    题目:给定一个m*n的矩阵,从头开始,只能往右边和下边走,一次走一格,知道走到最后一个(右下角)为止.总共有多少种走法. 典型的动态规划吧.其实从头走到尾部,和从尾部开始走到头是一样的次数.我们用一个 ...

  2. Mysql 使用 select into outfile

    Mysql支持将查询结果到处 默认语法 select .. from table  into outfile "filepath\filename.txt"; 如果在执行的过程中遇 ...

  3. cocos2d的-X- luaproject的LUA脚本加密

    2014/1/26 更新 近期又发现了一个非常easy的方法,事实上coco2dx已经给我们提供设置loader的方法. 注意:有个局限性,在非android平台下调用pEngine->exec ...

  4. 开发环境准备:Ruby on Rails开发环境配置

    开发环境准备:Ruby on Rails开发环境配置 前情回顾 上次讲到Vmware虚拟机的安装配置以及Scientific Linux 6.X系统的安装.这回我们的主要任务是在Linux操作系统上完 ...

  5. SQLServer 分组查询相邻两条记录的时间差

    原文:SQLServer 分组查询相邻两条记录的时间差 首先,我们通过数据库中表的两条记录来引出问题,如下图 以上为一个记录操作记录的表数据.OrderID为自增长列,后面依次为操作类型,操作时间,操 ...

  6. class 添加样式,删除,开关 【选择】addClass,removeClass,toggleClass

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

  7. php和cookie

    cookie常用于用户识别,是服务器留在用户计算机中的小文件. cookie在浏览器端和服务器端的通信过程大致是这样: 浏览器向服务器作出请求(如果浏览器有cookie,将在request heade ...

  8. Javascript多线程引擎(一)

    Javascript多线程引擎(一) Javascript 天生是单线程的语言, 不支持synchronized等线程操作, 但是这便不妨碍Javascript作为web语言中最具有魅力语言之一. 虽 ...

  9. 根据Eclipse SVN changelog使用ANT自动打增量包

    1.获取changeLog 用eclipseSVN的插件功能查看history. 将日志文件导出到本地文件svn_change.log,格式如下 r63 | xiaodaoshi | 2014-08- ...

  10. 自定义MVP .net框架

      一个自定义MVP .net框架 AngleFrame   摘要:本篇是本人在完成.net平台下一个项目时,对于MVP框架引发的一些思考,以及开发了一个小型的配置型框架,名字叫作AngleFrame ...