首页 > 系统相关 >Linux下编写USB驱动实例

Linux下编写USB驱动实例

时间:2023-01-30 09:00:18浏览次数:46  
标签:usb urb 实例 static Linux USB id struct

USB 是连接计算机系统与外部设备的一种串口总线标准,也是一种输入输出接口的技术规范,被广泛地应用于个人电脑和移动设备等信息通讯产品,USB 就是简写,中文叫通用串行总线。我们知道总线是用来通信的,所以USB总线就是一个种通信协议,你的设备支持什么协议就得使用哪一种总线协议与之通信。比如: EEPROM支持IIC协议,那么我们就得使用IIC总线与之通信,而EEPROM设备在制造过程中也必须遵循IIC协议设计。​

在学习驱动开发实例之前,先了解几个USB总线通信的问题:​

问题1: USB设备那么多,他们怎么分类的?​USB设备按照传输类型分,主要分为4类: 控制传输,中断传输,等时传输,批量传输。​
其中控制传输时每个USB设备都必须支持的,通常用来获取设备描述符,设置设备的状态等。从USB设备插入到拔出的过程中一定为产生控制传输,不管当前设备是否被主机支持。​
中断传输的经典代表是USB鼠标和USB键盘,这里说的中断不是真正硬件发出的中断,而是一种轮询机制,​
USB设备驱动程序里可以设置轮询时间的间隔,也就是主机可以按照这个间隔时间来轮询设备。​
批量传输的经典代表是U盘,数据可靠,时间不可靠。​
等时传输的经典代表是摄像头,数据不可靠,时间可靠。​
问题2: 当USB设备插入系统时(USB主机),系统怎么知道这是什么设备?当USB设备插入系统之后,根据硬件设计的特性,会被USB主机控制器第一时间知道,然后主机控制器就会问当前插入的设备是什么设备。这里就引入了一个概念叫做描述符。
描述符有很多种,最基本的有4种:设备描述符、配置描述符、接口描述符、端点描述符。一个∪SB设备必须同时支持这四大描述符,一般这些描述符都存放在USB设备的EEPROM里。设备描述符包含了设备遵循的 USB 的版本号、设备类、设备子类、制造商、产品编号等信息,主机会通过控制传输的方式获取这个设备描述符,通过这个设备描述符就能知道当前是什么设备了。
1.2 USB鼠标与键盘驱动编写实例​
Linux内核默认是支持鼠标驱动的,想要自己重新编写鼠标驱动,需要先将内核自带的鼠标驱动先去除掉。​

[root@wbyq linux-3.5]# make menuconfig​

 

Device Drivers ---> ​

HID support --->​

USB HID support --->​

< > USB HID transport layer //传输层​

Linux内核里自带的鼠标驱动源码: \drivers\hid\usbhid\usbmouse.c​

Linux内核里自带的键盘驱动:源码 \drivers/usb/input/usbkbd.c​

USB键盘和USB鼠标都属于HID人机交互类,都使用的是中断方式传输数据。代码区别只是匹配的类型不一样而已,其他处理代码通用。


下面是鼠标和键盘的模板:
#include <linux/init.h>​

#include <linux/module.h>​

#include <linux/usb.h>​

#include <linux/usb/input.h>​

#include <linux/hid.h>​

 

/*​

本程序为USB鼠标驱动程序,要安装本驱动,需要先将内核自带的USB驱动程序卸载掉​

*/​

 

//定义USB的IDTAB 24ae:2002​

static const struct usb_device_id tiny4412_usb_id[] = {​

{//148f:7601​

USB_DEVICE(0x148f,0x7601),/*360WIFI的制造商ID和产品ID */​

USB_DEVICE(0x1c4f,0x0051),/*当前鼠标的ID 1c4f:0051*/​

},​

};​

 

//USB鼠标的ID​

static struct usb_device_id usb_mouse_id[] = {​

{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,​

USB_INTERFACE_PROTOCOL_MOUSE) },​

{ }/* 终止进入 */​

};​

 

//USB键盘的ID​

static struct usb_device_id usb_kbd_id_table [] = {​

{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,​

USB_INTERFACE_PROTOCOL_KEYBOARD) },​

{ }​

};​

 

int size;​

static unsigned char *buf =NULL;​

static struct urb *myurb=NULL;​

dma_addr_t buf_phy;​

 

/*USB中断处理程序*/​

static void usb_complete(struct urb *urb)​

{​

int i;​

for(i=0;i<size;i++)​

{​

printk("0x%x ",buf[i]);​

}​

printk("\n");​

 

/* 重新提交异步请求*/​

usb_submit_urb(myurb, GFP_KERNEL);​

}​

 

//USB设备信息与驱动端匹配成功的时候调用。​

static int usb_probe(struct usb_interface *intf,const struct usb_device_id *id)​

{​

printk("USB驱动匹配成功! ID: 0x%X,0x%X\n",id->idVendor,id->idProduct);​

/*通过接口获取设备信息*/​

struct usb_device *dev = interface_to_usbdev(intf);​

/*获取当前接口设置*/​

struct usb_host_interface *interface=intf->cur_altsetting;​

/*获取端点描述符*/​

struct usb_endpoint_descriptor *endpoint = &interface->endpoint[0].desc;​

/*中断传输:创建输入管道*/​

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

/*从端点描述符中获取传输的数据大小 */​

size = endpoint->wMaxPacketSize;​

printk("设备传输数据包大小:%d\n",size);​

/*分配数据传输缓冲区*/​

buf = usb_alloc_coherent(dev,size,GFP_ATOMIC,&buf_phy);​

/*分配新的urb,urb是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构*/​

myurb = usb_alloc_urb(0,GFP_KERNEL);​

/*中断方式初始化urb*/​

usb_fill_int_urb(myurb,dev,pipe,buf,size,usb_complete,NULL,endpoint->bInterval);​

myurb->transfer_dma = buf_phy;​

myurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;​

/*为端点提交异步传输请求*/​

usb_submit_urb(myurb, GFP_KERNEL);​

return 0;​

}​

 

 

//USB断开的时候调用​

static void usb_disconnect(struct usb_interface *intf)​

{​

struct usb_device *dev = interface_to_usbdev(intf);​

usb_kill_urb(myurb);​

usb_free_urb(myurb);​

usb_free_coherent(dev,size,buf, buf_phy);​

printk("USB 设备释放成功!\n"); ​

}​

 

//定义USB驱动结构体 ​

static struct usb_driver tiny4412_usb_driver = {​

.name = "tiny4412_usb",​

.id_table = usb_kbd_id_table,​

.probe = usb_probe,​

.disconnect = usb_disconnect​

};​

 

static int __init tiny4412_usb_init(void)​

{​

//注册USB设备驱动​

usb_register(&tiny4412_usb_driver);​

return 0;​

}​

 

static void __exit tiny4412_usb_exit(void)​

{​

注销USB设备驱动​

usb_deregister(&tiny4412_usb_driver);​

}​

 

module_init(tiny4412_usb_init);​

module_exit(tiny4412_usb_exit);​

MODULE_AUTHOR("xiaolong");​

MODULE_LICENSE("GPL");​

1.3 USB电子扫码枪驱动编写实例​
USB电子扫码枪的驱动与USB键盘驱动通用,只是数据包的大小是64字节,匹配的类型也是使用键盘的类型。​

说明: USB电子扫码枪和USB键盘输出的数据都是以掩码的值输出。​

//USB键盘的ID​

static struct usb_device_id usb_kbd_id_table [] = {​

{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,​

USB_INTERFACE_PROTOCOL_KEYBOARD) },​

{ }​

};​

要测试电子扫码枪的驱动,也需要先将内核自带的USB键盘去掉先去掉在测试。​

#include <linux/init.h>​

#include <linux/module.h>​

#include <linux/usb.h>​

#include <linux/usb/input.h>​

#include <linux/hid.h>​

 

/*​

本程序为USB鼠标驱动程序,要安装本驱动,需要先将内核自带的USB驱动程序卸载掉​

*/​

 

//定义USB的IDTAB 24ae:2002​

static const struct usb_device_id tiny4412_usb_id[] = {​

{//148f:7601​

USB_DEVICE(0x148f,0x7601),/*360WIFI的制造商ID和产品ID */​

USB_DEVICE(0x1c4f,0x0051),/*鼠标的ID 1c4f:0051*/​

USB_DEVICE(0x0483,0x0011),/*电子扫描枪的ID 1c4f:0051*/​

},​

};​

 

//USB鼠标的ID​

static struct usb_device_id usb_mouse_id[] = {​

{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,​

USB_INTERFACE_PROTOCOL_MOUSE) },​

{ }/* 终止进入 */​

};​

 

//USB键盘的ID​

static struct usb_device_id usb_kbd_id_table [] = {​

{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,​

USB_INTERFACE_PROTOCOL_KEYBOARD) },​

{ }​

};​

 

int size;​

static unsigned char *buf =NULL;​

static struct urb *myurb=NULL;​

dma_addr_t buf_phy;​

 

/*USB中断处理程序*/​

static void usb_complete(struct urb *urb)​

{​

int i;​

/*​

for(i=0;i<size;i++)​

{​

if(buf[i]!=0)printk("%d,%d\n",buf[i],i);​

}​

printk("\n");​

*/​

//每包数据都是存放在buf[2]里,并且以掩码的形式存放,如果需要得到真实的​

//按键值,需要根据键盘的规则找到对应的码值​

if(buf[2]!=0)printk("0x%x\n",buf[2]);​

/* 重新提交异步请求*/​

usb_submit_urb(myurb, GFP_KERNEL);​

}​

 

//USB设备信息与驱动端匹配成功的时候调用。​

static int usb_probe(struct usb_interface *intf,const struct usb_device_id *id)​

{​

printk("USB驱动匹配成功! ID: 0x%X,0x%X\n",id->idVendor,id->idProduct);​

/*通过接口获取设备信息*/​

struct usb_device *dev = interface_to_usbdev(intf);​

/*获取当前接口设置*/​

struct usb_host_interface *interface=intf->cur_altsetting;​

/*获取端点描述符*/​

struct usb_endpoint_descriptor *endpoint = &interface->endpoint[0].desc;​

/*中断传输:创建输入管道*/​

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

/*从端点描述符中获取传输的数据大小 */​

size = endpoint->wMaxPacketSize;​

printk("设备传输数据包大小:%d\n",size);​

/*分配数据传输缓冲区*/​

buf = usb_alloc_coherent(dev,size,GFP_ATOMIC,&buf_phy);​

/*分配新的urb,urb是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构*/​

myurb = usb_alloc_urb(0,GFP_KERNEL);​

/*中断方式初始化urb*/​

usb_fill_int_urb(myurb,dev,pipe,buf,size,usb_complete,NULL,endpoint->bInterval);​

myurb->transfer_dma = buf_phy;​

myurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;​

/*为端点提交异步传输请求*/​

usb_submit_urb(myurb, GFP_KERNEL);​

return 0;​

}​

 

 

//USB断开的时候调用​

static void usb_disconnect(struct usb_interface *intf)​

{​

struct usb_device *dev = interface_to_usbdev(intf);​

usb_kill_urb(myurb);​

usb_free_urb(myurb);​

usb_free_coherent(dev,size,buf, buf_phy);​

printk("USB 设备释放成功!\n"); ​

}​

 

//定义USB驱动结构体 ​

static struct usb_driver tiny4412_usb_driver = {​

.name = "tiny4412_usb",​

.id_table =usb_kbd_id_table,//,​

.probe = usb_probe,​

.disconnect = usb_disconnect​

};​

 

static int __init tiny4412_usb_init(void)​

{​

//注册USB设备驱动​

usb_register(&tiny4412_usb_driver);​

return 0;​

}​

 

static void __exit tiny4412_usb_exit(void)​

{​

注销USB设备驱动​

usb_deregister(&tiny4412_usb_driver);​

}​

 

module_init(tiny4412_usb_init);​

module_exit(tiny4412_usb_exit);​

MODULE_AUTHOR("xiaolong");​

MODULE_LICENSE("GPL");​

1.4 USB摄像头编写实例​
要自己编写自己的UVC摄像头驱动,需要先将内核自带的驱动去掉。​

Device Drivers --->
<*> Multimedia support --->​

[*] Video capture adapters --->​

[] V4L USB devices ---> //将*号去掉即可​

示例:​


#include <linux/init.h>​

#include <linux/module.h>​

#include <linux/usb.h>​

#include <linux/usb/input.h>​

#include <linux/hid.h>​

 

#define UVC_MAX_STATUS_SIZE16​

static unsigned char *buf =NULL;​

 

static struct usb_device_id uvc_ids[] = {​

{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },​

{}​

};​

 

static struct urb *video_urb=NULL;​

 

 

/*USB中断处理程序*/​

static void usb_complete(struct urb *urb)​

{​

int len, ret;​

 

switch (urb->status) {​

case 0:​

break;​

 

case -ENOENT:/* usb_kill_urb() called. */​

case -ECONNRESET:/* usb_unlink_urb() called. */​

case -ESHUTDOWN:/* The endpoint is being disabled. */​

case -EPROTO:/* Device is disconnected (reported by some​

* host controller). */​

return;​

 

default:​

printk("Non-zero status (%d) in status completion handler.\n", urb->status);​

return;​

}​

 

len = urb->actual_length;​

printk("len=%d\n",len);​

//urb->interval = dev->int_ep->desc.bInterval;​

usb_submit_urb(urb, GFP_ATOMIC);​

}​

 

 

//USB设备信息与驱动端匹配成功的时候调用。​

static int usb_probe(struct usb_interface *intf,const struct usb_device_id *id)​

{​

struct usb_device *udev = interface_to_usbdev(intf);​

int ret;​

if (id->idVendor && id->idProduct)​

{​

printk("%s,%d,%d\n",udev->devpath, id->idVendor,id->idProduct);​

}​

else​

{​

printk("通用UVC设备:%s\n",udev->devpath);​

}​

 

/*分配新的urb,urb是usb设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构*/​

video_urb=usb_alloc_urb(0, GFP_KERNEL);​

 

/*通过接口获取设备信息*/​

struct usb_device *dev = interface_to_usbdev(intf);​

/*获取当前接口设置*/​

struct usb_host_interface *interface=intf->cur_altsetting;​

/*获取端点描述符*/​

struct usb_endpoint_descriptor *endpoint = &interface->endpoint[0].desc;​

/*中断传输:创建输入管道*/​

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

buf=kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);​

 

/*中断方式初始化urb*/​

usb_fill_int_urb(video_urb,dev,pipe,buf,UVC_MAX_STATUS_SIZE,usb_complete,NULL,endpoint->bInterval);​

 

/*使能自动暂停*/​

usb_submit_urb(video_urb, GFP_ATOMIC);​

//usb_enable_autosuspend(udev);​

return 0;​

}​

 

//USB断开的时候调用​

static void usb_disconnect(struct usb_interface *intf)​

{​

struct usb_device *dev = interface_to_usbdev(intf);​

usb_kill_urb(video_urb);​

usb_free_urb(video_urb);​

usb_set_intfdata(intf, NULL);​

kfree(buf);​

printk("USB 设备释放成功!\n"); ​

}​

 

 

//定义USB驱动结构体 ​

static struct usb_driver tiny4412_usb_driver = {​

.name = "tiny4412_usb",​

.id_table =uvc_ids,​

.probe = usb_probe,​

.disconnect = usb_disconnect​

};​

 

static int __init tiny4412_usb_init(void)​

{​

//注册USB设备驱动​

usb_register(&tiny4412_usb_driver);​

return 0;​

}​

 

static void __exit tiny4412_usb_exit(void)​

{​

注销USB设备驱动​

usb_deregister(&tiny4412_usb_driver);​

}​

 

module_init(tiny4412_usb_init);​

module_exit(tiny4412_usb_exit);​

MODULE_AUTHOR("xiaolong");​

MODULE_LICENSE("GPL");

标签:usb,urb,实例,static,Linux,USB,id,struct
From: https://www.cnblogs.com/kn-zheng/p/17074273.html

相关文章

  • Linux 标准分区扩容
    0x00 前提安装growpart工具yuminstall-ycloud-utils-growpartyuminstallxfsprogs0x01使用growpart扩容工具扩容growpart/dev/sda1#注意磁盘和序号之间......
  • Linux day1
    linux的文件系统/:跟目录bin:可执行文件etc:配置文件var/log:日志lib:库头文件,安装包proc:进程相关信息,里面有个cpuinfo路径切换绝对路径:/号开头相对路径......
  • Linux systemd学习
    Systemd 内容简介一、Systemd的定义二、Systemd的常用命令三、查看已有的Systemd配置文件四、编写一个简单的Systemd配置文件 一、Systemd的定义Systemd......
  • Linux unzip 解压后文件名乱码
    目录问题描述解决办法方法1:实践可用[来源于文末链接1]方法2:实践可用(recommend)[来源于文末链接1]问题描述直接使用unzipfileName.zip命令解压zip文件,解压出的文件名是......
  • QPython实例01-获取所有短信并生成词云
    一、QPython安装配置1.1.QPython介绍QPython是一个可以在安卓设备运行python的脚本引擎。版本有QPython3L和QPython3C,3L为官方版本,可以在应用市场搜索下载。3C版本为"......
  • react组件实例属性props
    propsprops简单使用classPersonextendsReact.Component{render(){return(<ul>......
  • linux驱动移植-linux网卡驱动基础
    一、OSI七层模型1.1、概念OSI七层模式是一个标准,规定了各种计算机在世界范围内互联成网的标准框架,OSI模型是一个分层的模型,每一个部分称为一层,每一层扮演固定的角色,互不......
  • Linux学习-DAY5
    一、文件目录管理命令1.touch命令touch命令用于创建空白文件或设置文件的时间,语法格式为“touch[参数] 文件名称”。2.mkdir命令mkdir命令用于创建空白的目录,英文全称为“......
  • Linux sudo: 无法执行 fork: 资源暂时不可用
    故障现象sudo:无法执行fork:资源暂时不可用  解决方案1:[2842072.342664]cgroup:forkrejectedbypidscontrollerin/user.slice/user-1003.slice/session......
  • linux 高效压缩工具之xz的压缩解压使用
    xz是什么高压缩率的工具,它使用LZMA2压缩算法,生成的压缩文件比传统使用的gzip、bzip2生成的压缩文件更小,不过xz也有一个坏处就是压缩时间比较长,比7z压缩时间还长一些......