zoukankan      html  css  js  c++  java
  • 基于ok6410的韦东山驱动视频简要分析USB驱动 .

    注意:本篇讲的鼠标驱动仅能实现鼠标左右键跟滑轮这三个按键类似button的功能,按下左键则打出"l",右键打出“s”,滑轮打出“enter”。如果要实现正常的鼠标驱动,参考内核的鼠标驱动,修改input的一些参数即可。

    一、写驱动的步骤(新手稍微看下即可,内容有点搞)

    1、复制头文件;

    2、写入口函数,出口函数,再加上协议;

    3、分配注册usb_driver结构体(拷别人的),

    static struct usb_driver usb_mk_driver = {};

    在init中注册该结构体:usb_register(&usb_mk_driver);

    在exit中注销该结构体:usb_deregister(&usb_mk_driver);

    4、写id_table函数,static struct usb_device_id usb_mk_id_table [] = {},只有满足id_table中的各种类别,子类号,协议,才能调用;

    5、写probe函数:static int usb_mk_probe(struct usb_interface *intf, const struct usb_device_id *id);

    6、写disconnect函数:static void usb_mk_disconnect(struct usb_interface *intf)

    7、如果想在开发板插入usb设备的时候打印设备信息(可以不用),可以在probe中加入:

    printk("VID = 0x%x, PID = 0x%x\n", dev->descriptor.idVendor, dev->descriptor.idProduct);

    printk("USB VERS = 0x%x, PID = 0x%x\n", dev->descriptor.bcdUSB);

    8、probe之后要进一步判断你这个是不是鼠标:

    1、分配一个input_dev结构体:在头文件下定义;static struct input_dev       *mk_dev;  在probe内:mk_dev = input_allocate_device();

    2、设置:

    1、能产生哪类事件

    set_bit(EV_KEY, mk_dev->evbit);

    set_bit(EV_REP, mk_dev->evbit);

    2、能产生哪些事件

    set_bit(KEY_L, mk_dev->keybit);

    set_bit(KEY_S, mk_dev->keybit);

    set_bit(KEY_ENTER, mk_dev->keybit);

    3、注册:input_register_device(mk_dev);

    4、设置:数据传输3要素: 源, 目的, 长度

    先从usbmouse.c中拷入几行代码:

    struct usb_device *dev = interface_to_usbdev(intf);

    struct usb_host_interface *interface;

    struct usb_endpoint_descriptor *endpoint;

    int pipe;

    endpoint = &interface->endpoint[0].desc;

    源:usb设备的某个端点

    pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

    目的:需要设置缓冲区buffer:

    buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &buf_phys);

    在头文件下定义一个缓冲区:

    static char *buf; 

    static dma_addr_t buf_phys;

    长度:static  int len(作为全局变量,等会有用)

    len = endpoint->wMaxPacketSize;

    5、怎么用那三要素呢?

    在probe中;

    1、分配一个urb,mk_urb = usb_alloc_urb(0, GFP_KERNEL);

    在头文件下定义urb:static struct urb *mk_urb;

    2、使用三要素填充urb:

    usb_fill_int_urb(mk_urb, dev, pipe, buf,

     len,

     uk_callback, NULL, endpoint->bInterval);

    mk_urb->transfer_dma = buf_phys;

    mk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

    3、使用URB:usb_submit_urb(mk_urb, GFP_KERNEL);

    6、要写一个中断函数,原因是usb控制器收到从usb传来的数据后,需要一个中断来通知cpu有信号啦!static void uk_callback(struct urb *urb)

    在中断函数中定义一些唧唧歪歪的东西后看,重新提交urb:

    usb_submit_urb(mk_urb, GFP_KERNEL);

    7,提交urb后当然还要做杀掉urb,在disconnect中杀掉它!usb_kill_urb(mk_urb);

    顺便再disconnect中注销或卸载其他的函数;

    8、在中断函数里上报事件。

    **************************************************************************************************************

    二、驱动程序

    #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>
    #include <linux/input.h>
    
    /* 参考drivers\hid\usbhid\usbmouse.c
     */
    
    static struct input_dev *mk_dev;
    static int len;
    static char *buf;  //定义一个缓冲区
    static dma_addr_t buf_phys; //dma_addr实际上是一个物理地址
    static struct urb *mk_urb;
    
    static struct usb_device_id usb_mk_id_table [] = {
    	{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
    		USB_INTERFACE_PROTOCOL_MOUSE) },
    /*只要它的类是class_hid,子类是boot,协议是mouse,那么就可以匹配*/
     *{USB_DEVICE(0x46d, 0xc52f)或者直接指定自己的usb设备只支持这种厂家的设备。*/
    	{ }	/* Terminating entry */
    };
    
    /* 当USB主机控制器获得鼠标数据后,
     * 会调用这个函数
     */
    static void uk_callback(struct urb *urb)//这是一个中断函数!
    {
    	int i;
    	static char pre_val;
    #if 0	//这一段都用不到了,因为if = 0 ;这一段的作用就是用来看鼠标按下之后都显示什么数值,最后通过这些数值来设置上报事件。
    	printk("Get datas:\n");
    	for (i = 0; i < len; i++)
    	{
    		printk("%02x ", buf[i]);
    	}
    	printk("\n");
    #endif
    	/* 鼠标数据含义:
    	 * buf[0]: bit0-左键, 0-松开, 1-按下
    	 *         bit1-右键, 0-松开, 1-按下
    	 *         bit2-中键, 0-松开, 1-按下
    	 * buf[1],buf[2]构成一个整数, 表示X方向的相对位移
    	 *         >0 : 右移
    	 *         <0 : 左移
    	 * buf[3],buf[4]构成一个整数, 表示Y方向的相对位移
    	 *         >0 : 下移
    	 *         <0 : 上移
    	 * buf[6]: 滚轮
    	 */
    	 
    	/* 确定按键值 */
    	/* 上报数据 */
    	if ((pre_val & (1<<0)) != (buf[0] & (1<<0))) //如果上次数据的bit0不等于这次数据的bit0,那么就是左键发生变化
    	{
    		/* 左键按下或松开 */
    		input_event(mk_dev, EV_KEY, KEY_L, (buf[0] & (1<<0)) ? 1 : 0);//如果buf0=1的话那就是按下,既是1,否则为0;
    		input_sync(mk_dev);
    	}
    
    	if ((pre_val & (1<<1)) != (buf[0] & (1<<1)))
    	{
    		/* 右键按下或松开 */
    		input_event(mk_dev, EV_KEY, KEY_S, (buf[0] & (1<<1)) ? 1 : 0);
    		input_sync(mk_dev);
    	}
    
    	if ((pre_val & (1<<2)) != (buf[0] & (1<<2)))
    	{
    		/* 中键按下或松开 */
    		input_event(mk_dev, EV_KEY, KEY_ENTER, (buf[0] & (1<<2)) ? 1 : 0);
    		input_sync(mk_dev);
    	}
    
    	pre_val = buf[0];
    
    	/* 重新提交URB */
    	usb_submit_urb(mk_urb, GFP_KERNEL);
    }
    
    /*interface是指接口,一个usb设备可能有多个逻辑接口,这个逻辑接口就是用下面的usb_interface来表示的*/
    static int usb_mk_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;
    	static int first = 1;
    
    	if (!first)
    		return -EIO;
    	first = 0;
    
    	/* 每一个设备都有端点0
    	 * interface->endpoint[]数组里放"除了端点0外的其他端点"
    	 * interface->endpoint[0]表示"除端点0外的第1个端点"
    	 * interface->endpoint[1]表示"除端点0外的第2个端点"
    	 */
    	interface = intf->cur_altsetting;
    	endpoint = &interface->endpoint[0].desc;
    
    	/* 1. 分配inputd_dev */
    	mk_dev = input_allocate_device();
    	
    	/* 2. 设置 */
    	/* 2.1 能产生哪类事件 */
    	set_bit(EV_KEY, mk_dev->evbit);  //按键类事件
    	set_bit(EV_REP, mk_dev->evbit);  //重复类事件,例如一直按着L,则会显示LLLLLLL。。。
    	
    	/* 2.2 能产生这类事件里的哪些事件 */
    	set_bit(KEY_L, mk_dev->keybit);
    	set_bit(KEY_S, mk_dev->keybit);
    	set_bit(KEY_ENTER, mk_dev->keybit);
    	
    	/* 3. 注册 */
    	input_register_device(mk_dev);
    
    	/* 4. 硬件相关的操作: 
    	 *    对于GPIO按键, 是request_irq, 在中断处理函数里上报按键
    	 *    对于USB设备, 是使用"USB主机驱动程序提供的函数"发起USB传输获得数据
    	 */
    	/* 数据传输3要素: 源, 目的, 长度 */
    	
    	/* A. 源: USB设备的某个端点 */
    	/* ((PIPE_INTERRUPT << 30) | (dev->devnum << 8) | (endpoint << 15) | USB_DIR_IN) */
    	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//pipe是源,源是一个整数,这个整数里有端点的类型,端点的方向
    	
    	/* C. 长度: 这个端点描述符的wMaxPacketSize */
    	len = endpoint->wMaxPacketSize;
    
    	/* B. 目的: 分配buffer */
    	buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &buf_phys);//从usb_buffer_alloc换成usb_alloc_coherent
    
    	/* D. 怎么使用这3要素 ? */
    	/* 分配URB: USB Reqeust Block ,usb请求块*/
    	mk_urb = usb_alloc_urb(0, GFP_KERNEL);
    	/* 用3要素填充URB 
    	*实际上usb设备没有中断cpu的能力,但是电脑的usb主机有中断cpu的能力,所以usb主机不断查询,有信号便中断cpu。
    	*/
    	usb_fill_int_urb(mk_urb, dev, pipe, buf,
    			 len,
    			 uk_callback, NULL, endpoint->bInterval);//bInterval:查询频率,uk_callback是一个中断函数;
    	mk_urb->transfer_dma = buf_phys; //得到数据后要往某个内存里写,它没那么聪明,需要一个物理地址
    	mk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//还有设置某些标记,这标记什么意思伟哥也不懂
    	
    	/* 使用URB ,看看别人怎么做*/
    	usb_submit_urb(mk_urb, GFP_KERNEL);//提交urb
    
    	return 0;
    }
    
    static void usb_mk_disconnect(struct usb_interface *intf)
    {
    	struct usb_device *dev = interface_to_usbdev(intf);
    	
    	printk("disconnect usb mouse!!!!!\n");
    	usb_kill_urb(mk_urb);
    	usb_free_urb(mk_urb);
    	usb_free_coherent(dev,len, buf, buf_phys);//应该是usb_free_coherent
    	
    	input_unregister_device(mk_dev);
    	input_free_device(mk_dev);
    }
    
    /* 1. 分配usb_driver */
    /* 2. 设置 */
    static struct usb_driver usb_mk_driver = {
    	.name		= "usbmk",
    	.probe		= usb_mk_probe,
    	.disconnect	= usb_mk_disconnect,
    	.id_table	= usb_mk_id_table,
    };
    
    static int usb_mk_init(void)
    {
    	/* 3. 注册 */
    	usb_register(&usb_mk_driver);
    	return 0;
    }
    
    static void usb_mk_exit(void)
    {
    	usb_deregister(&usb_mk_driver);
    }
    
    module_init(usb_mk_init);
    module_exit(usb_mk_exit);
    
    MODULE_LICENSE("GPL");
    
    


     

  • 相关阅读:
    关于Cocos2d-x开发一个游戏的过程自述
    关于JDK环境变量的配置问题
    lnmp、lamp、lnmpa一键安装包(Updated: 2015-10-25)
    成大事者必备的九种能力、九种手段、九种心态
    燕十八mysql笔记
    cmake 安装 mysql5.5 版本
    IOS 中微信 网页授权报 key[也就是code]失效 解决办法
    js 复制 功能
    php 二维数组排序
    php获取前一天,前一个月,前一年的时间
  • 原文地址:https://www.cnblogs.com/start530/p/3834396.html
Copyright © 2011-2022 走看看