liunx驱动----USB驱动

现象:把usb设备接入电脑

1.Windows发现设备

2.跳出一个对话框提示安装驱动程序

 

问1:既然没有驱动程序,为什么了够知道是什么驱动了??

答1:Windows里面已经有了usb总线驱动程序,接入usb设备后,是“总线驱动程序知道”是什么驱动。提示安装设备驱动程序

   usb总线驱动程序负责识别USB设备,给usb设备找到对应的驱动程序

 

 

问2.usb设备种类多,为什么接入电脑就能够识别出来了?

答2.PC和USB设备都的遵守一些规范。

  比如:USB接入电脑后PC机会发出,读取设备类型的命令(描述符)。然后USB设备就回答给PC机(描述符)。

 

问3.PC机上接有非常多的USB设备,怎么分辨?

答3.接在USB总线上的每个USB设备都有自己的编号(地址)PC机想访问某个USB设备的时候,发出的信息都有对方的编号(地址)

 

问4.USB设备刚接入PC的时候还没有编号,那么PC怎么把分配的编号告诉

答4.新接入的USB设备的默认编号为0,在没有分配新的编号前。PC使用0编号和USB通讯。

 

问5.为什么一接入USB设备,PC机就能发现USB设备

答5.PC的USB口内部。D-和D+接了下拉电阻,没有接入USB的时候为低电平,USB设备的USB口内部,D-或D+接了上啦电阻,接入时就有一个硬件的信号通知PC机有USB设备接入。  

其它概念:

1.USB是主从设备(主从结构)

  所有的USB传输都是从USB主机发起的。USB设备没有主动通知USB主机的能力。

  例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来读数据,只能被动的等待PC机来读取数据

2.USB的传输类型

a.控制传输:可靠,时间有保证,比如USB设备的识别过程

b.批量传输:可靠,时间没有保证,U盘

c.中断传输:可靠,实时,USB鼠标

d.实时传输:不可靠,实时,USB摄像头

 

 

3.USB传输的对象:端点(endpoint)

  读写U盘,可以细化为:把数据写到u盘的端点1,从u盘的端点2读出数据

  除了端点0以外,每个端点只支持一个方向的数据传输。

  端点0用于控制传输,既能输出也能输入

4.每一个端点都有传输类型,传输方向。

5.术语里,程序里说的输入(IN)输出(OUT)。都是基于USB主机的立场说的。

  比如说对于鼠标,数据是从鼠标传输到PC机的。对应的端点称为“输出端点”

 

USB驱动

设备驱动:          1.知道数据的含义。

 

USB总线驱动程序(的作用)----> 1.识别设备

               2.找到并安装对应的USB设备驱动

               3.提供USB读写函数

 

硬件:    USB主机控制器

USB驱动程框架:

APP:

---------------------------------------------

    USB设备驱动程序

内核--------------------------------------

    USB总线驱动程序

---------------------------------------------

     USB主机控制器

  
硬件   ---------------------

      USB设备

 

 

/*****************************USB总线驱动程序****************************************/

 USB设备驱动程序编写:

1.分配/设置usb_derver结构体

  .id_table  表示支持哪些usb设备

      .probe

  .disconnect  

2.注册

 

 

测试:

1.make munconfig 去掉内核中原来的usb鼠标驱动

-> Device Drivers 

-> HID Devices

   < > USB Human Interface Device (full HID) support 

 

2.make uImage  并使用新的内核启动

使用新内核启动:nfs 30000000 192.168.1.102:/home/book/work/nfs_root/first_fs/uImage_nohid; 30000000

挂接根文件系统:mount -t nfs -o nolock,vers=2 192.168.1.102:/home/book/work/nfs_root/first_fs  /mut

3.insmod usbmonus_as_key.ko



实验1:

目的:通过控制台将数据数据移动按下数据打印出来。

步骤1:得到  usb_host_interface  结构体

interface = intf->cur_altsetting;//interface结构体

步骤2:获取端点描述符

endpoint = &interface->endpoint[0].desc;//端点描述符

 步骤3://1.分配一个input_dev 结构体

uk_dev = input_allocate_device();

步骤4:设置uk_dev结构体

步骤5:注册input_dev结构体

步骤6:硬件相关的操作 (使用usb总线设备驱动来收发数据)

//数据传输的三要素: 源 、目的、长度
//源:USB的某个端点

代码分为:

usbmouse_as_key_probe函数(初始化需要操作鼠标的设置)     

 usbmouse_as_key_irq函数(当鼠标有数据变化时将调用这个函数)       

usbmouse_as_key_disconnect函数(鼠标的断开处理函数)

usbmouse_as_key_probe代码如下:

static int usbmouse_as_key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
    /*打印厂家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;//interface结构体  

    
    endpoint = &interface->endpoint[0].desc;//端点描述符
#if  0
    struct usb_device *dev = interface_to_usbdev(intf);//通过  interface_to_usbdev接口函数得到  usb_device          id
    printk("bcd_USB = %x\n",dev->descriptor.bcdUSB);
    printk("vid = 0x%x\n",dev->descriptor.idVendor);
    printk("pid = 0x%x\n",dev->descriptor.idProduct);
    printk("found usbmonuse!\n");
#endif

    //1.分配一个input_dev 结构体
    uk_dev = input_allocate_device();

    //2.设置分配的这个结构体
    //2.1能产生那类事件
    set_bit(EV_KEY, uk_dev->evbit);//设置能够产生按键类事件uk_dev->evbit是表示能够产生那类事件
    set_bit(EV_REP, uk_dev->evbit);//设置能够产生重复类事件uk_dev->evbit是表示能够产生那类事件
    //2.2能产生哪些事件
    set_bit(KEY_L,uk_dev->keybit);//通过设置uk_dev->keybit   实现产生一个KEY_L的事件  (相当于按键按下 L 键)
    set_bit(KEY_S,uk_dev->keybit);//通过设置uk_dev->keybit   实现产生一个KEY_S的事件  (相当于按键按下 S 键)
    set_bit(KEY_ENTER,uk_dev->keybit);//通过设置uk_dev->keybit   实现产生一个KEY_ENTER的事件  (相当于按键按下 ENTER 键)
    //3.注册input_dev结构体
    input_register_device(uk_dev);
    //4.硬件相关的操作   (使用usb总线设备驱动来收发数据)
    //数据传输的三要素:  源  、目的、长度
    //源:USB的某个端点
    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

    //长度:
    len =endpoint->wMaxPacketSize;//长度等于端点描述符的wMaxPacketSize(最大包大小)

    //目的:使用usb_buffer_alloc分配一个数据缓冲区
    usb_buf=usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_pyhs);


    //使用三要素  usb requset block
    uk_urb = usb_alloc_urb(0, GFP_KERNEL);

    //使用三要素  设置usb requset block
    /*             usb请求块, */
    usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval);
    uk_urb->transfer_dma = usb_buf_pyhs;//设置usb传输的目的 的物理地址
    uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//设置标记
    
    //使用urb
    usb_submit_urb (uk_urb, GFP_KERNEL);//提交数据 提交urb
    return 0;
}

 usbmouse_as_key_irq代码如下:(当鼠标数据发生变化时执行此函数)

static void usbmouse_as_key_irq(struct urb *urb)
{
    int i;
    static int cnt=0;
    /*将鼠标数据打印出来*/
    printk("usb data %d: ",++cnt);
    for(i=0;i<len;i++)
        {
            printk("%02x ",usb_buf[i]);
        }
    printk("\n");
    //重新提交urb    
    usb_submit_urb (uk_urb, GFP_KERNEL);//提交数据 提交urb
}

usbmouse_as_key_disconnect代码如下:其实就是usbmouse_as_key_probe的逆向操作

static void usbmouse_as_key_disconnect(struct usb_interface *intf)
{
    struct usb_device *dev = interface_to_usbdev(intf);

//printk("dsiconnect usbmonuse!\n");
    usb_kill_urb (uk_urb);//清除urb
    usb_free_urb(uk_urb);//释放urb
    usb_buffer_free(dev,len,usb_buf,usb_buf_pyhs);//去除  usb_buf
    input_unregister_device(uk_dev);
    input_free_device(uk_dev);
}

测试步骤: 

1、insmod usbmonus_as_key.ko  挂载驱动

2、ls /dev/event*        查看事件

 

 3、移动或者按下鼠标  

数据1:按键值

数据2:x方向位移 数据

数据3:y方向位移 数据

数据4:滚轮数据

 

 

实验2:

目的:使用鼠标上的按键实现  L  S 和ENTER 键值  在控制台上输出

通过实验一,已经将鼠标数据读了出来,所以要实现按键的话只关心第一个数据就可以了

所以只需要修改usbmouse_as_key_disconnect

static void usbmouse_as_key_irq(struct urb *urb)
{
    static int pre_val;//用来保存上次的数据
    
#if 0
    int i;
    static int cnt=0;
    
    /*将鼠标数据打印出来*/
    printk("usb data %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_event上报按键事件
            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_event上报按键事件
            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_event上报按键事件
            input_sync(uk_dev);//同步信号
        }
    pre_val = usb_buf[0];
    //重新提交urb    
    usb_submit_urb (uk_urb, GFP_KERNEL);//提交数据 提交urb
}

使用/dev/tty1 测试:

使用hexdump /dev/event1 测试:

 

posted @ 2019-04-15 23:10  Dipsyhu  阅读(380)  评论(0编辑  收藏  举报