单片机
返回首页

基于ok6410的韦东山驱动视频简要分析--USB驱动

2024-11-06 来源:cnblogs

注意:本篇讲的鼠标驱动仅能实现鼠标左右键跟滑轮这三个按键类似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%xn', dev->descriptor.idVendor, dev->descriptor.idProduct);


printk('USB VERS = 0x%x, PID = 0x%xn', 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

#include

#include

#include

#include

#include

#include


/* 参考drivershidusbhidusbmouse.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');


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 家用电源无载自动断电装置的设计与制作

  • PIC单片机控制的遥控防盗报警器电路

  • 用数字电路CD4069制作的万能遥控轻触开关

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 用NE555制作定时器

  • RS-485基础知识:处理空闲总线条件的两种常见方法

    相关电子头条文章