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. .NET简单企业应用

    <.NET简单企业应用>项目开发环境 项目开始,开发团队需要构建一套开发环境,主要包含:开发工具.代码管理/版本控制系统.任务和Bug管理系统和持续集成(CI)系统.本文主要列举项目开发中 ...

  2. mysql查询字段值为数字

    原文:mysql查询字段值为数字 我想查询字段值为数字的sql如下:select * from tj_item_result where tj_value REGEXP '^[0-9]'

  3. TodoList开发笔记 – Part Ⅲ

    本节开始对TodoList项目的客户端进行开发 一.初步了解JQuery 其实我在学校时有接触过一段时间的Web开发,虽然代码量不多也不复杂,但也已经感受到了各浏览器对Web各项标准的恶意,Web界对 ...

  4. asp.net mvc 中 tempdata、viewdata、viewbag生命周期(转载)

                 TempData ViewData ViewBag都可以用来保存数据,它们之间的区别如下: TempData保存在Session中,Controller每次执行请求的时候,会 ...

  5. 泛型Hub

    SignalR循序渐进(二)泛型Hub 接上一篇,文章末尾抛出了2个问题: 能不能让客户端声明一个强类型的方法列表呢?这样首先不容易写错. 同样的,能不能让服务端声明一个强类型的方法列表给客户端调用呢 ...

  6. js 将long日期格式 转换为标准日期格式方法

    我们经常在操作的时候会发现从后台传递到view层的json中datetime类型变成了long型,当然你也可以从后台先转为string类型,但是如果是从和数据库对应的object中封装的话,就不能再去 ...

  7. 关于readonly

    当某个字段是引用类型,且该字段是readonly类型时,那么不可改变的是引用,而非引用的对象.如以下代码: public sealed class AType { public static read ...

  8. 浅谈DevExpress<二>:设计一个完整界面(2)

    下面来把剩下的工作做完,换肤功能昨天已近讨论过,今天就不重复了.首先建立三个全局变量,一个存放文件路径,一个存放数据,一个存放过滤条件. string DBFileName; DataView dat ...

  9. Hadoop 从URL中读取数据

    package com.hadoop; import java.io.IOException; import java.io.InputStream; import java.net.URL; imp ...

  10. 使用NPOI导出,读取EXCEL(可追加功能)

    使用NPOI导出,读取EXCEL,具有可追加功能 看代码 using System; using System.Collections.Generic; using System.Text; usin ...