怎样写linux下的USB设备驱动程序
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了怎样写linux下的USB设备驱动程序相关的知识,希望对你有一定的参考价值。
写一个USB的驱动程序最 基本的要做四件事:驱动程序要支持的设备、注册USB驱动程序、探测和断开、提交和控制urb(USB请求块)驱动程序支持的设备:有一个结构体struct usb_device_id,这个结构体提供了一列不同类型的该驱动程序支持的USB设备,对于一个只控制一个特定的USB设备的驱动程序来说,struct usb_device_id表被定义为:
/* 驱动程序支持的设备列表 */
static struct usb_device_id skel_table [] =
USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) ,
/* 终止入口 */
;
MODULE_DEVICE_TABLE (usb, skel_table);
对 于PC驱动程序,MODULE_DEVICE_TABLE是必需的,而且usb必需为该宏的第一个值,而USB_SKEL_VENDOR_ID和 USB_SKEL_PRODUCT_ID就是这个特殊设备的制造商和产品的ID了,我们在程序中把定义的值改为我们这款USB的,如:
/* 定义制造商和产品的ID号 */
#define USB_SKEL_VENDOR_ID 0x1234
#define USB_SKEL_PRODUCT_ID 0x2345
这两个值可以通过命令lsusb,当然你得先把USB设备先插到主机上了。或者查看厂商的USB设备的手册也能得到,在我机器上运行lsusb是这样的结果:
Bus 004 Device 001: ID 0000:0000
Bus 003 Device 002: ID 1234:2345 Abc Corp.
Bus 002 Device 001: ID 0000:0000
Bus 001 Device 001: ID 0000:0000
得到这两个值后把它定义到程序里就可以了。
注册USB驱动程序:所 有的USB驱动程序都必须创建的结构体是struct usb_driver。这个结构体必须由USB驱动程序来填写,包括许多回调函数和变量,它们向USB核心代码描述USB驱动程序。创建一个有效的 struct usb_driver结构体,只须要初始化五个字段就可以了,在框架程序中是这样的:
static struct usb_driver skel_driver =
.owner = THIS_MODULE,
.name = "skeleton",
.probe = skel_probe,
.disconnect = skel_disconnect,
.id_table = skel_table,
;
探测和断开:当 一个设备被安装而USB核心认为该驱动程序应该处理时,探测函数被调用,探测函数检查传递给它的设备信息,确定驱动程序是否真的适合该设备。当驱动程序因 为某种原因不应该控制设备时,断开函数被调用,它可以做一些清理工作。探测回调函数中,USB驱动程序初始化任何可能用于控制USB设备的局部结构体,它 还把所需的任何设备相关信息保存到一个局部结构体中,
提交和控制urb:当驱动程序有数据要发送到USB设备时(大多数情况是在驱动程序的写函数中),要分配一个urb来把数据传输给设备:
/* 创建一个urb,并且给它分配一个缓存*/
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
retval = -ENOMEM;
goto error;
当urb被成功分配后,还要创建一个DMA缓冲区来以高效的方式发送数据到设备,传递给驱动程序的数据要复制到这块缓冲中去:
buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
if (!buf)
retval = -ENOMEM;
goto error;
if (copy_from_user(buf, user_buffer, count))
retval = -EFAULT;
goto error;
当数据从用户空间正确复制到局部缓冲区后,urb必须在可以被提交给USB核心之前被正确初始化:
/* 初始化urb */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, count, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
然后urb就可以被提交给USB核心以传输到设备了:
/* 把数据从批量OUT端口发出 */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval)
err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
goto error;
当urb被成功传输到USB设备之后,urb回调函数将被USB核心调用,在我们的例子中,我们初始化urb,使它指向skel_write_bulk_callback函数,以下就是该函数:
static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
struct usb_skel *dev;
dev = (struct usb_skel *)urb->context;
if (urb->status &&
!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
dbg("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
/* 释放已分配的缓冲区 */
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
有时候USB驱动程序只是要发送或者接收一些简单的数据,驱动程序也可以不用urb来进行数据的传输,这是里涉及到两个简单的接口函数:usb_bulk_msg和usb_control_msg ,在这个USB框架程序里读操作就是这样的一个应用:
/* 进行阻塞的批量读以从设备获取数据 */
retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&count, HZ*10);
/*如果读成功,复制到用户空间 */
if (!retval)
if (copy_to_user(buffer, dev->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
usb_bulk_msg接口函数的定义如下:
int usb_bulk_msg(struct usb_device *usb_dev,unsigned int pipe,
void *data,int len,int *actual_length,int timeout);
其参数为:
struct usb_device *usb_dev:指向批量消息所发送的目标USB设备指针。
unsigned int pipe:批量消息所发送目标USB设备的特定端点,此值是调用usb_sndbulkpipe或者usb_rcvbulkpipe来创建的。
void *data:如果是一个OUT端点,它是指向即将发送到设备的数据的指针。如果是IN端点,它是指向从设备读取的数据应该存放的位置的指针。
int len:data参数所指缓冲区的大小。
int *actual_length:指向保存实际传输字节数的位置的指针,至于是传输到设备还是从设备接收取决于端点的方向。
int timeout:以Jiffies为单位的等待的超时时间,如果该值为0,该函数一直等待消息的结束。
如果该接口函数调用成功,返回值为0,否则返回一个负的错误值。
usb_control_msg接口函数定义如下:
int usb_control_msg(struct usb_device *dev,unsigned int pipe,__u8 request,__u8requesttype,__u16 value,__u16 index,void *data,__u16 size,int timeout)
除了允许驱动程序发送和接收USB控制消息之外,usb_control_msg函数的运作和usb_bulk_msg函数类似,其参数和usb_bulk_msg的参数有几个重要区别:
struct usb_device *dev:指向控制消息所发送的目标USB设备的指针。
unsigned int pipe:控制消息所发送的目标USB设备的特定端点,该值是调用usb_sndctrlpipe或usb_rcvctrlpipe来创建的。
__u8 request:控制消息的USB请求值。
__u8 requesttype:控制消息的USB请求类型值。
__u16 value:控制消息的USB消息值。
__u16 index:控制消息的USB消息索引值。
void *data:如果是一个OUT端点,它是指身即将发送到设备的数据的指针。如果是一个IN端点,它是指向从设备读取的数据应该存放的位置的指针。
__u16 size:data参数所指缓冲区的大小。
int timeout:以Jiffies为单位的应该等待的超时时间,如果为0,该函数将一直等待消息结束。
如果该接口函数调用成功,返回传输到设备或者从设备读取的字节数;如果不成功它返回一个负的错误值。
这两个接口函数都不能在一个中断上下文中或者持有自旋锁的情况下调用,同样,该函数也不能被任何其它函数取消,使用时要谨慎。
我们要给未知的USB设备写驱动程序,只需要把这个框架程序稍做修改就可以用了,前面我们已经说过要修改制造商和产品的ID号,把0xfff0这两个值改为未知USB的ID号。
#define USB_SKEL_VENDOR_ID 0xfff0
#define USB_SKEL_PRODUCT_ID 0xfff0
还 有就是在探测函数中把需要探测的接口端点类型写好,在这个框架程序中只探测了批量(USB_ENDPOINT_XFER_BULK)IN和OUT端点,可 以在此处使用掩码(USB_ENDPOINT_XFERTYPE_MASK)让其探测其它的端点类型,驱动程序会对USB设备的每一个接口进行一次探测, 当探测成功后,驱动程序就被绑定到这个接口上。再有就是urb的初始化问题,如果你只写简单的USB驱动,这块不用多加考虑,框架程序里的东西已经够用 了,这里我们简单介绍三个初始化urb的辅助函数:
usb_fill_int_urb :它的函数原型是这样的:
void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,
unsigned int pipe,void *transfer_buff,
int buffer_length,usb_complete_t complete,
void *context,int interval);
这个函数用来正确的初始化即将被发送到USB设备的中断端点的urb。
usb_fill_bulk_urb :它的函数原型是这样的:
void usb_fill_bulk_urb(struct urb *urb,struct usb_device *dev,
unsigned int pipe,void *transfer_buffer,
int buffer_length,usb_complete_t complete)
这个函数是用来正确的初始化批量urb端点的。
usb_fill_control_urb :它的函数原型是这样的:
void usb_fill_control_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,usb_complete_t complete,void *context);
这个函数是用来正确初始化控制urb端点的。
还有一个初始化等时urb的,它现在还没有初始化函数,所以它们在被提交到USB核心前,必须在驱动程序中手工地进行初始化,可以参考内核源代码树下的/usr/src/~/drivers/usb/media下的konicawc.c文件。 参考技术A
Linux内核提供了一个称为USB core的子系统来处理了大部分USB设备的复杂工作,所以这里所描述的是驱动程序和USB core之间的接口。
在USB设备组织结构中,从上到下分为设备(device)、配置(config)、接口(interface)和端点(endpoint)四个层次。
对于这四个层次的简单描述如下:
1、设备通常具有一个或多个的配置
2、配置经常具有一个或多个的接口
3、接口通常具有一个或多个的设置
4、接口没有或具有一个以上的端点
设备
代表了一个插入的USB设备,在内核使用数据结构 struct usb_device来描述整个USB设备。(include/linux/usb.h)
int devnum; //设备号,是在USB总线的地址
char devpath [16]; //用于消息的设备ID字符串
enum usb_device_state state; //设备状态:已配置、未连接等等
enum usb_device_speed speed; //设备速度:高速、全速、低速或错误
struct usb_tt *tt; //处理传输者信息;用于低速、全速设备和高速HUB
int ttport; //位于tt HUB的设备口
unsigned int toggle[2]; //每个端点的占一位,表明端点的方向([0] = IN, [1] = OUT)
struct usb_device *parent; //上一级HUB指针
struct usb_bus *bus; //总线指针
struct usb_host_endpoint ep0; //端点0数据
struct device dev; //一般的设备接口数据结构
struct usb_device_descriptor descriptor; //USB设备描述符
struct usb_host_config *config; //设备的所有配置
struct usb_host_config *actconfig; //被激活的设备配置
struct usb_host_endpoint *ep_in[16]; //输入端点数组
struct usb_host_endpoint *ep_out[16]; //输出端点数组
char **rawdescriptors; //每个配置的raw描述符
unsigned short bus_mA; //可使用的总线电流
u8 portnum;//父端口号
u8 level; //USB HUB的层数
unsigned can_submit:1; //URB可被提交标志
unsigned discon_suspended:1; //暂停时断开标志
unsigned persist_enabled:1; //USB_PERSIST使能标志
unsigned have_langid:1; //string_langid存在标志
unsigned authorized:1;
unsigned authenticated:1;
unsigned wusb:1; //无线USB标志
int string_langid; //字符串语言ID
/* static strings from the device */ //设备的静态字符串
char *product; //产品名
char *manufacturer; //厂商名
char *serial; //产品串号
struct list_head filelist; //此设备打开的usbfs文件
#ifdef CONFIG_USB_DEVICE_CLASS
struct device *usb_classdev; //用户空间访问的为usbfs设备创建的USB类设备
#endif
#ifdef CONFIG_USB_DEVICEFS
struct dentry *usbfs_dentry; //设备的usbfs入口
#endif
int maxchild; //(若为HUB)接口数
struct usb_device *children[USB_MAXCHILDREN];//连接在这个HUB上的子设备
int pm_usage_cnt; //自动挂起的使用计数
u32 quirks;
atomic_t urbnum; //这个设备所提交的URB计数
unsigned long active_duration; //激活后使用计时
#ifdef CONFIG_PM //电源管理相关
struct delayed_work autosuspend; //自动挂起的延时
struct work_struct autoresume; //(中断的)自动唤醒需求
struct mutex pm_mutex; //PM的互斥锁
unsigned long last_busy; //最后使用的时间
int autosuspend_delay;
unsigned long connect_time; //第一次连接的时间
unsigned auto_pm:1; //自动挂起/唤醒
unsigned do_remote_wakeup:1; //远程唤醒
unsigned reset_resume:1; //使用复位替代唤醒
unsigned autosuspend_disabled:1; //挂起关闭
unsigned autoresume_disabled:1; //唤醒关闭
unsigned skip_sys_resume:1; //跳过下个系统唤醒
#endif
struct wusb_dev *wusb_dev; //(如果为无线USB)连接到WUSB特定的数据结构
;
配置
一个USB设备可以有多个配置,并可在它们之间转换以改变设备的状态。比如一个设备可以通过下载固件(firmware)的方式改变设备的使用状态(我感觉类似FPGA或CPLD),那么USB设备就要切换配置,来完成这个工作。一个时刻只能有一个配置可以被激活。Linux使用结构 struct usb_host_config 来描述USB配置。我们编写的USB设备驱动通常不需要读写这些结构的任何值。可在内核源码的文件include/linux/usb.h中找到对它们的描述。
struct usb_config_descriptor desc; //配置描述符
char *string; /* 配置的字符串指针(如果存在) */
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; //配置的接口联合描述符链表
struct usb_interface *interface[USB_MAXINTERFACES]; //接口描述符链表
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
unsigned char *extra; /* 额外的描述符 */
int extralen;
;
接口
USB端点被绑为接口,USB接口只处理一种USB逻辑连接。一个USB接口代表一个基本功能,每个USB驱动控制一个接口。所以一个物理上的硬件设备可能需要一个以上的驱动程序。这可以在“晕到死 差屁”系统中看出,有时插入一个USB设备后,系统会识别出多个设备,并安装相应多个的驱动。
USB 接口可以有其他的设置,它是对接口参数的不同选择. 接口的初始化的状态是第一个设置,编号为0。 其他的设置可以以不同方式控制独立的端点。
USB接口在内核中使用 struct usb_interface 来描述。USB 核心将其传递给USB驱动,并由USB驱动负责后续的控制。
struct usb_host_interface *altsetting; /* 包含所有可用于该接口的可选设置的接口结构数组。每个 struct usb_host_interface 包含一套端点配置(即struct usb_host_endpoint结构所定义的端点配置。这些接口结构没有特别的顺序。*/
struct usb_host_interface *cur_altsetting; /* 指向altsetting内部的指针,表示当前激活的接口配置*/
unsigned num_altsetting; /* 可选设置的数量*/
/* If there is an interface association descriptor then it will list the associated interfaces */
struct usb_interface_assoc_descriptor *intf_assoc;
int minor; /* 如果绑定到这个接口的 USB 驱动使用 USB 主设备号, 这个变量包含由 USB 核心分配给接口的次设备号. 这只在一个成功的调用 usb_register_dev后才有效。*/
/*以下的数据在我们写的驱动中基本不用考虑,系统会自动设置*/
enum usb_interface_condition condition; /* state of binding */
unsigned is_active:1; /* the interface is not suspended */
unsigned sysfs_files_created:1; /* the sysfs attributes exist */
unsigned ep_devs_created:1; /* endpoint "devices" exist */
unsigned unregistering:1; /* unregistration is in progress */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */
unsigned needs_binding:1; /* needs delayed unbind/rebind */
unsigned reset_running:1;
struct device dev; /* 接口特定的设备信息 */
struct device *usb_dev;
int pm_usage_cnt; /* usage counter for autosuspend */
struct work_struct reset_ws; /* for resets in atomic context */
;
struct usb_host_interface
struct usb_interface_descriptor desc; //接口描述符
struct usb_host_endpoint *endpoint; /* 这个接口的所有端点结构体的联合数组*/
char *string; /* 接口描述字符串 */
unsigned char *extra; /* 额外的描述符 */
int extralen;
;
端点
USB 通讯的最基本形式是通过一个称为端点的东西。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))
端点在内核中使用结构 struct usb_host_endpoint 来描述,它所包含的真实端点信息在另一个结构中:struct usb_endpoint_descriptor(端点描述符,包含所有的USB特定数据)。
struct usb_host_endpointstruct usb_endpoint_descriptor desc; //端点描述符
struct list_head urb_list; //此端点的URB对列,由USB核心维护
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */
unsigned char *extra; /* Extra descriptors */
int extralen;
int enabled;
;
/*-------------------------------------------------------------------------*/
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress; /*这个特定端点的 USB 地址,这个8位数据包含端点的方向,结合位掩码 USB_DIR_OUT 和 USB_DIR_IN 使用, 确定这个端点的数据方向。*/
__u8 bmAttributes; //这是端点的类型,位掩码如下
__le16 wMaxPacketSize; /*端点可以一次处理的最大字节数。驱动可以发送比这个值大的数据量到端点, 但是当真正传送到设备时,数据会被分为 wMaxPakcetSize 大小的块。对于高速设备, 通过使用高位部分几个额外位,可用来支持端点的高带宽模式。*/
__u8 bInterval; //如果端点是中断类型,该值是端点的间隔设置,即端点的中断请求间的间隔时间,以毫秒为单位
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
__attribute__ ((packed));
#define USB_DT_ENDPOINT_SIZE 7
#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
/*
* Endpoints
*/
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress 端点的 USB 地址掩码 */
#define USB_ENDPOINT_DIR_MASK 0x80 /* in bEndpointAddress 数据方向掩码 */
#define USB_DIR_OUT 0 /* to device */
#define USB_DIR_IN 0x80 /* to host */
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* bmAttributes 的位掩码*/
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
/*-------------------------------------------------------------------------*/
Linux下编写USB驱动实例
1.1 Linux下USB驱动开发介绍
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 ---> [*] Video capture adapters ---> [] V4L USB devices ---> //将*号去掉即可 |
- 示例:
#include <linux/init.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/input.h> 怎样写linux下的USB设备驱动程序 |