linux usb驱动详解
linux-dash
A beautiful web dashboard for Linux
项目地址:https://gitcode.com/gh_mirrors/li/linux-dash
免费下载资源
·
1 数据结构说明
1.1
struct usb_device_id {
/* which fields to match against? */
__u16 match_flags;
/* Used for product specific matches; range is inclusive */
__u16 idVendor;
__u16 idProduct;
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
/* Used for device class matches */
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/* Used for interface class matches */
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
/* Used for vendor-specific interface matches */
__u8 bInterfaceNumber;
/* not matched against */
kernel_ulong_t driver_info
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
成员变量说明:
match_flags :需要匹配的类型(可以多个或起来),数据定义如下:
#define USB_DEVICE_ID_MATCH_VENDOR 0x0001 //匹配PID
#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 //匹配VID
#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 //匹配产品编号低位
#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 //匹配产品编号高位
#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 //匹配设备描述符的class
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 //匹配设备描述符的subclass
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 //匹配设备描述符的protocol
#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 //匹配接口描述符的class
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 //匹配接口描述符的subclass
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 //匹配接口描述符的protocol
#define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400 //匹配接口的数量
driver_info :用来保存用户的私有数据
1.2
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
int (*reset_resume)(struct usb_interface *intf);
int (*pre_reset)(struct usb_interface *intf);
int (*post_reset)(struct usb_interface *intf);
const struct usb_device_id *id_table;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int disable_hub_initiated_lpm:1;
unsigned int soft_unbind:1;
};
name:独一无二的设备的名字
probe:回调函数指针,在USB设备插入,且符合struct usb_device_id 定义的匹配条件是被调用。这是如果匹配成功返回0,
disconnect:回调函数指针,当设备拔出时被调用
unlocked_ioctl:
suspend : 设备休眠的时候被调用
resume:设备唤醒的时候被调用
reset_resume:设备在休眠的时候已经reset,唤醒的时候调用
pre_reset:在调用usb_reset_device()的时候这个回调函数被调用,这个函数在reset前调用,该函数必须等到没有有效的URBs才能返回。
post_reset:在调用usb_reset_device()的时候这个回调函数被调用,这个函数在reset完成后调用
id_table:需要匹配的设备信息
dynids:用来保持动态添加的设备列表
drvwrap:Driver-model core structure wrapper
no_dynamic_id:如果设置为1,内核将不允许添加动态设备列表
supports_autosuspend:如果设置为0 内核将不允许设备自动休眠,不知道为什么这个参数设置为0,有时会造成设备访问被拒绝
soft_unbind:如果设置为1 ,在设备拔出前,不允许禁用端点,杀掉URBs
disable_hub_initiated_lpm:
注意:这个结构必须要实现name、probe、disconnect、id_table,其他成员可以根据情况选择是否实现
1.3
struct usb_class_driver {
char *name;
char *(*devnode)(struct device *dev, umode_t *mode);
const struct file_operations *fops;
int minor_base;
};
name:设备节点的名称,也就是在dev目录下的设备,可以直接Open这个设备进行读写
devnode:??
fops:设备文件操作结构
minor_base:次设备号的基准值,
1.4
struct urb {
/* private: usb core and host controller only fields in the urb */
struct kref kref; /* reference count of the URB */
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
atomic_t reject; /* submissions will fail */
int unlinked; /* unlink error code */
/* public: documented fields in the urb that can be used by drivers */
struct list_head urb_list; /* list head for use by the urb's * current owner */
struct list_head anchor_list; /* the URB may be anchored */
struct usb_anchor *anchor;
struct usb_device *dev; /* (in) pointer to associated device */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe; /* (in) pipe information */
unsigned int stream_id; /* (in) stream ID */
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
struct scatterlist *sg; /* (in) scatter gather buffer list */
int num_mapped_sgs; /* (internal) mapped sg entries */
int num_sgs; /* (in) number of entries in the sg list */
u32 transfer_buffer_length; /* (in) data buffer length */
u32 actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
int start_frame; /* (modify) start frame (ISO) */
int number_of_packets; /* (in) number of ISO packets */
int interval; /* (modify) transfer interval
* (INT/ISO) */
int error_count; /* (return) number of ISO errors */
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
};
以下的成员变量是私有的,只能由usb核心和主机控制器访问的字段
kref : urb引用计数 ,当计数引用为0时,系统释放urb
hcpriv : 主机控制器私有数据
use_count : 并发传输计数
reject : 传输将失败
以下的成员变量是公共的,可以被驱动使用的字段
urb_list :链表头
dev :关联的usb设备
pipe :端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.
要设置这个结构的成员, 驱动可以使用下面的函数来设置. 注意每个端点只可以是一个类型.
unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)
指定一个控制 OUT 端点给特定的带有特定端点号的 USB 设备.
unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)
指定一个控制 IN 端点给带有特定端点号的特定 USB 设备.
unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)
指定一个块传输 OUT 端点给带有特定端点号的特定 USB 设备
unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)
指定一个块传输 IN 端点给带有特定端点号的特定 USB 设备
unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)
指定一个中断传输 OUT 端点给带有特定端点号的特定 USB 设备
unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)
指定一个中断传输 IN 端点给带有特定端点号的特定 USB 设备
unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)
指定一个同步传输 OUT 端点给带有特定端点号的特定 USB 设备
unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)
指定一个同步传输 IN 端点给带有特定端点号的特定 USB 设备
status : urb的当前状态
0 urb 传送是成功的.
-ENOENT 这个 urb 被 usb_kill_urb调用停止.
-ECONNRESET urb 被usb_unlink_urb 调用去链, 并且 transfer_flags 变量被设置为 URB_ASYNC_UNLINK.
-EINPROGRESS 这个 urb 仍然在被 USB 主机控制器处理中. 如果你的驱动曾见到这个值, 它是一个你的驱动中的 bug.
-EPROTO 一个 bitstuff 错误在传送中发生. 硬件没有及时收到响应帧.
-EILSEQ 在这个 urb 传送中 CRC 不匹配.
-EPIPE 这个端点现在被停止. 如果这个包含的端点不是一个控制端点, 这个错误可被清除通过一个对函数 usb_clear_halt 的调用.
-ECOMM 在传送中数据接收快于能被写入系统内存. 这个错误值只对 IN urb.
-ENOSR 在传送中数据不能从系统内存中获取得足够快, 以便可跟上请求的 USB 数据速率. 这个错误只对 OUT urb.
-EOVERFLOW 这个 urb 发生一个"babble"错误. 一个"babble"错误发生当端点接受数据多于端点的特定最大报文大小.
-EREMOTEIO 只发生在当 URB_SHORT_NOT_OK 标志被设置在 urb 的 transfer_flags 变量, 并且意味着 urb 请求的完整数量的数据没有收到.
-ENODEV 这个 USB 设备现在从系统中消失.
-EXDEV 只对同步 urb 发生, 并且意味着传送只部分完成. 为了决定传送什么, 驱动必须看单独的帧状态.
-EINVAL 这个 urb 发生了非常坏的事情. USB 内核文档描述了这个值意味着什么:ISO 疯了, 如果发生这个: 退出并回家.
它也可发生, 如果一个参数在 urb 结构中被不正确地设置了, 或者如果在提交这个 urb 给 USB 核心的 usb_submit_urb 调用中, 有一个不正确的函数参数.
-ESHUTDOWN 这个 USB 主机控制器驱动有严重的错误; 它现在已被禁止, 或者设备和系统去掉连接, 并且这个urb 在设备被去除后被提交. 它也可发生当这个设备的配置改变, 而这个 urb 被提交给设备.
通常, 错误值 -EPROTO, -EILSEQ, 和 -EOVERFLOW 指示设备的硬件问题, 设备固件, 或者连接设备到计算机的线缆.
transfer_flags:这个变量可被设置为不同位值, 根据这个 USB 驱动想这个 urb 发生什么. 可用的值如下:
URB_SHORT_NOT_OK 当置位, 它指出任何在一个 IN 端点上可能发生的短读, 应当被 USB 核心当作一个错误. 这个值只对从 USB 设备读的 urb 有用, 不是写 urbs.
URB_ISO_ASAP 如果这个 urb 是同步的, 这个位可被置位如果驱动想这个 urb 被调度, 只要带宽允许它这样, 并且在此点设置这个 urb 中的 start_frame 变量. 如果对于同步 urb 这个位没有被置位, 驱动必须指定 start_frame 值并且必须能够正确恢复, 如果没有在那个时刻启动. 见下面的章节关于同步 urb 更多的消息.
URB_NO_TRANSFER_DMA_MAP 应当被置位, 当 urb 包含一个要被发送的 DMA 缓冲. USB 核心使用这个被 transfer_dma 变量指向的缓冲, 不是被 transfer_buffer 变量指向的缓冲.
URB_NO_SETUP_DMA_MAP 象 URB_NO_TRANSFER_DMA_MAP 位, 这个位用来控制有一个 DMA 缓冲已经建立的 urb. 如果它被置位, USB 核心使用这个被 setup_dma 变量而不是 setup_packet 变量指向的缓冲.
URB_ASYNC_UNLINK 如果置位, 给这个 urb 的对 usb_unlink_urb 的调用几乎立刻返回, 并且这个 urb 在后面被解除连接. 否则, 这个函数等待直到 urb 完全被去链并且在返回前结束. 小心使用这个位, 因为它可有非常难于调试的同步问题.
URB_NO_FSBR 只有 UHCI USB 主机控制器驱动使用, 并且告诉它不要试图做 Front Side Bus Reclamation 逻辑. 这个位通常应当不设置, 因为有 UHCI 主机控制器的机器创建了许多 CPU 负担, 并且 PCI 总线被等待设置了这个位的 urb 所饱和.
URB_ZERO_PACKET 如果置位, 一个块 OUT urb 通过发送不包含数据的短报文而结束, 当数据对齐到一个端点报文边界. 这被一些坏掉的 USB 设备所需要(例如一些 USB 到 IR 的设备) 为了正确的工作..
URB_NO_INTERRUPT 如果置位, 硬件当 urb 结束时可能不产生一个中断. 这个位应当小心使用并且只在排队多个到相同端点的 urb 时使用. USB 核心函数使用这个为了做 DMA 缓冲传送.
transfer_buffer : 发送数据到设备或从设备接收数据的缓冲区,这个缓冲区它必须被使用 kmalloc来创建, 不是在堆栈或者静态地址,所以不能定义一个大数组来作为缓冲区.
transfer_dma : 用来以dma方式向设备传输数据的缓冲区
transfer_buffer_length : transfer_buffer或transfer_dma 指向缓冲区的大小
actual_length : urb结束后,发送或接收数据的实际长度
setup_packet : 指向控制urb的设置数据包的指针
setup_dma :控制urb的设置数据包的dma缓冲区
start_frame :等时传输中用于设置或返回初始帧
number_of_packets :只对同步 urb 有效, 并且指定这个 urb 要处理的同步传送缓冲的编号. 这个值必须被 USB 驱动设置给同步 urb, 在这个 urb 发送给 USB 核心之前.
interval : urb被轮询到的时间间隔(对中断和等时urb有效)
error_count : 等时传输错误数量
context :completion函数上下文 ,用来保存用户数据的指针
complete : 回调函数指针,当urb被完全传输或发生错误时,被调用
usb_complete_t 类型定义 :typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
iso_frame_desc[0] : 单个urb一次可定义多个等时传输时,描述各个等时传输
注意:urb 不能静态创建, 或者在另一个结构中, 必须使用 usb_alloc_urb 函数来创建,使用usb_free_urb来释放 。
2 函数说明
int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name)
函数功能:注册一个USB驱动
参数说明:new_driver :struct usb_driver的结构指针
owner :模块的所有者名
mod_name :模块名称
返回值:0 成功 非0 失败
说明 :这个函数通常在驱动的__init函数中调用注册一个USB驱动。为简化调用的参数,我们通常直接用 usb_register这个宏
usb_register定义如下
#define usb_register(driver) usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
void usb_deregister(struct usb_driver * driver);
函数功能:注销一个USB驱动,这个函数通常在驱动的__exit函数中调用
参数说明:driver : struct usb_driver的结构指针
返回值:无
int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver)
函数功能:注册一个USB设备,注册成功后可以在/dev目录下看到相应的设备节点,这个函数通常是在struct usb_driver中定义的probe回调函数中被调用
参数说明:intf :USB接口描述符
class_driver : struct usb_class_driver 这个结构将指定设备的名称和 file_operations结构
返回值:0成功 非0 失败
void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver)
函数功能: 注销一个USB设备,这个函数通常是在struct usb_driver中定义的disconnect回调函数中被调用
参数说明:intf :USB接口描述符
class_driver : struct usb_class_driver 这个结构将指定设备的名称和 file_operations结构
返回值: 无
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
函数功能:动态分配一个urb,并初始化
参数说明:iso_packets : usb_iso_packet_descriptor的个数,这个参数只对同步传输有效。对于控制、中断、块传输,这个参数必须为0
mem_flags : 和kmalloc 函数调用来从内核分配内存的标志类型相同
返回值:成功返回分配的urb的指针,失败返回NULL
void usb_free_urb(struct urb *urb)
函数功能:释放usb_alloc_urb分配的urb
参数说明:urb : 要释放的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_fn, void *context)
void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context, int interval)
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_fn,void *context)
函数功能:用来填充用于块传输、中断传输、控制传输的urb
参数说明:Urb:是要初始化的urb
Dev:表示消息要被发送到的USB设备
Pipe:表示消息被发送到的端点
transfer_buffer:表示发送或接收数据的缓冲区
length:就是transfer_buffer所表示的缓冲区大小
context:完成处理函数的上下文
complete_fn:传输完了之后的回调函数.
interval:中断传输被调度的间隔
setup_packet:将被发送到端点的设置数据包
返回值:无
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
函数功能:用来递交urb,该函数以非阻塞的方式发送,也就是这个函数将不等发送完成就返回。
参数说明:urb :指向urb的指针
mem_flags :可以有下面三种取值
GFP_ATOMIC:在中断处理函数、底半部、tasklet、定时器处理函数以及urb完成函数中,在调用者持有自旋锁或者读写锁时以及当驱动将current->state修改为非 TASK_ RUNNING时,应使用此标志。
GFP_NOIO:在存储设备的块I/O和错误处理路径中,应使用此标志;
GFP_KERNEL:如果没有任何理由使用GFP_ATOMIC和GFP_NOIO,就使用GFP_ KERNEL。
返回值:0成功 非0 失败
void usb_kill_urb(struct urb *urb)
函数功能:取消一个urb传输,并且等待他结束
参数说明:要取消的urb的指针
返回值:无
int usb_unlink_urb(struct urb *urb)
函数功能:停止 urb传输. 这个函数 不等待这个 urb 完全停止就返回
参数说明:要取消的urb的指针
返回值:无
int usb_control_msg(struct usb_device *dev, unsigned int pipe, u8 request, u8 requesttype, u16 value, u16 index, void *data, u16 size, int timeout)
函数功能:以阻塞的方式进行控制传输,直到超时或urb传输完成函数才返回
参数说明:Dev:表示消息要被发送到的USB设备
Pipe:表示消息被发送到的端点
request:命令的序号(其实就是命令)
requesttype:D7=0主机到设备, =1设备到主机;
D6D5 =00标准请求命令, 01 类请求命令,10用户定义的命令,11保留值
D4D3D2D1D0= 0表示接收者为设备,1表示接收者为接口,2表示接收者为端点,3表示接收者为其他
value:2个字节,用来传送当前请求的参数,随请求不同而变。
index:索引字段同样是2个字节,描述的是接口号
data :数据缓冲区的指针
size:数据缓冲区的长度
timeout:超时时间
返回值:0成功,非0 失败
int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
函数功能:以阻塞的方式进行中断或者块传输,直到超时或urb传输完成函数才返回
参数说明:Dev:表示消息要被发送到的USB设备
Pipe:表示消息被发送到的端点
data :数据缓冲区的指针
len :数据缓冲区的长度
actual_length :实际发送或者接收的数据长度
timeout:超时时间
返回值:0成功,非0 失败
void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
函数功能:当urb在处理的时候锁定该urb ,urb被使用时是不能被删除的。在删除一个urb时需要调用usb_wait_anchor_empty_timeout来等待urb传输完全结束
参数说明:urb:要锁定的urb
anchor:
返回值:无
void usb_unanchor_urb(struct urb *urb)
函数功能:解锁一个urb
参数说明:urb:要解锁的urb
返回值:无
int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,unsigned int timeout)
函数功能:等待urb传输完全结束
参数说明:urb:要等待的urb
anchor:
timeout :超时设置
返回值:0传输完成,非0 超时
void usb_kill_anchored_urbs(struct usb_anchor *anchor)
函数功能:撤销一个urb传输
参数说明:anchor:
3注册一个USB驱动的流程
1 定义一个 struct usb_device_id的结构。
2 定义一个 struct usb_driver,并实现probe和disconnect函数
3 在模块的init函数中调用usb_register函数注册一个驱动
4 定义一个usb_class_driver
5 在probe函数中调用usb_register_dev注册一个USB设备,这样在dev目录下就可以看到我们的USB设备了
下面是示例代码:
。。。。
static const struct file_operations usbdev_fops = {
.owner = THIS_MODULE,
.read = usbdev_read,
.write = usbdev_write,
.open = usbdev_open,
.release = usbdev_release,
.flush = usbdev_flush,
.llseek = noop_llseek,
};
static struct usb_class_driver usbdev_class = {
.name = "USBTestDevice%d",//这里一次插上多个USB时候,将在dev目录下看到USBTestDevice0、USBTestDevice1 ....
.fops = &usbdev_fops,
.minor_base = USB_DEV_MINOR_BASE,
};
static int usbdev_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
......
retval = usb_register_dev(interface, &usbdev_class);
......
}
static void usbdev_disconnect(struct usb_interface *interface)
{
................
usb_deregister_dev(interface, &usbdev_class);
................
}
static const struct usb_device_id usbdev_table[] = {
{ USB_DEVICE(USB_MATCH_VENDOR_ID, USB_MATCH_PRODUCT_ID) },
{ USB_DEVICE(USB_MATCH_VENDOR_ID, USB_MATCH_PRODUCT_ID_1) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usbdev_table);
static struct usb_driver usbdev_driver = {
.name = "usbtest",
.probe = usbdev_probe,
.disconnect = usbdev_disconnect,
.id_table = usbdev_table,
};
static int __init usbdev_init(void)
{
return usb_register(&usbdev_driver);
}
module_init(usbdev_init);
static void __exit usbdev_exit(void)
{
usb_deregister(&usbdev_driver);
}
module_exit(usbdev_exit);
4 urb的操作流程:
1 调用usb_alloc_urb分配一个urb
2 调用kmalloc分配缓冲区
3 调用usb_fill_XXX_urb初始化urb
4 调用usb_submit_urb提交
5 在回调函数(complete)中处理,接收的数据,判断是否还要继续接收,如果还要接收,重复第三步。
如果是发送数据,判断是否还有数据要发送,填充数据
6 如果传输完成调用usb_free_urb释放urb,释放缓冲区
下面是示例代码
//回调函数
static void async_ctrl_callback(struct urb *urb)
{
struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
int status = urb->status;
if (status < 0)
{
printk("error\n");
}
kfree(req);
kfree(urb->transfer_buffer);
usb_free_urb(urb);
}
struct urb *urb;
struct usb_ctrlrequest *req;
void *data;
urb = usb_alloc_urb(0, GFP_KERNEL);
req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
/*根据协议设置reg*/
req->bRequestType = RequestType;
req->bRequest = Request;
req->wValue = Value;
req->wIndex = Index;
req->wLength = Length;
data = kmalloc(Length, GFP_KERNEL);
usb_fill_control_urb(urb, dev, usb_sndctrlpipe(dev, 0),reg, data, Length, usb_ctrl_complete, req);
usb_submit_urb(urb, GFP_ATOMIC);
对于其他的传输,方法差不多。
最后需要注意的是中断传输接收,不需要每次调用usb_submit_urb,调用一次usb_submit_urb之后,系统会按照指定的间隔,不断接收数据的。
5 下面是完成的驱动的示例代码
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/moduleparam.h>
#define BUF_PER_PAGE 10240
#define MAX_PAGE 5
typedef struct
{
volatile int iReadIndex;
volatile int iWriteIndex;
unsigned char *PackgeBuf[MAX_PAGE];//[BUF_PER_PAGE];
int iLen[MAX_PAGE];
}cachebuf ;
/* Define these values to match your devices */
#define USB_MATCH_VENDOR_ID 0x1234
#define USB_MATCH_PRODUCT_ID 0x5678
/* table of devices that work with this driver */
static const struct usb_device_id usbdev_table[] = {
{ USB_DEVICE(USB_MATCH_VENDOR_ID, USB_MATCH_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usbdev_table);
/* Get a minor range for your devices from the usb maintainer */
#define USB_DEV_MINOR_BASE 192
/* our private defines. if this grows any larger, use your own .h file */
#define MAX_TRANSFER (PAGE_SIZE - 512)
/* MAX_TRANSFER is chosen so that the VM is not stressed by
allocations > PAGE_SIZE and the number of packets in a page
is an integer 512 is the largest possible packet on EHCI */
#define WRITES_IN_FLIGHT 8
/* arbitrarily chosen */
/* Structure to hold all of our device specific stuff */
struct usb_devinfo {
struct usb_device *udev; /* the usb device for this device */
struct usb_interface *interface; /* the interface for this device */
struct semaphore limit_sem; /* limiting the number of writes in progress */
struct usb_anchor submitted; /* in case we need to retract our submissions */
struct urb *bulk_in_urb; /* the urb to read data with */
unsigned char *bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
size_t bulk_in_filled; /* number of bytes in the buffer */
size_t bulk_in_copied; /* already copied to user space */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
int errors; /* the last request tanked */
spinlock_t err_lock; /* lock for errors */
struct kref kref;
struct mutex io_mutex; /* synchronize I/O with disconnect */
cachebuf UsbBuffer;
wait_queue_head_t wqReadOK; /* buf is not empty*/
struct work_struct work;
struct mutex read_mutex;
int isOpen;
};
static struct usb_driver usbdev_driver;
static void usbdev_emptyurb(struct usb_devinfo *dev);
static int devusb_do_read_io(struct usb_devinfo *dev, size_t count);
static void work_func(struct work_struct *work)
{
struct usb_devinfo *dev = container_of(work, struct usb_devinfo, work);
if(dev->isOpen==0)
{
return ;
}
if(dev->bulk_in_filled>0)
{
int iWriteIndex = dev->UsbBuffer.iWriteIndex;
mutex_lock(&dev->read_mutex);
//memcpy(dev->UsbBuffer.PackgeBuf[iWriteIndex],dev->bulk_in_buffer ,dev->bulk_in_filled);
dev->UsbBuffer.iLen[iWriteIndex] = dev->bulk_in_filled;
mutex_unlock(&dev->read_mutex);
iWriteIndex++;
if(iWriteIndex>=MAX_PAGE)
{
iWriteIndex = 0;
}
while(iWriteIndex ==dev->UsbBuffer.iReadIndex)
{
if(iRetry==0)
printk(KERN_ALERT"usbdev_overflow(%d %d) \n",dev->UsbBuffer.iReadIndex,dev->UsbBuffer.iWriteIndex);
}
mutex_lock(&dev->read_mutex);
dev->UsbBuffer.iWriteIndex = iWriteIndex;
mutex_unlock(&dev->read_mutex);
dev->bulk_in_filled = 0;
//printk("write w=%d r=%d \n",dev->UsbBuffer.iWriteIndex,dev->UsbBuffer.iReadIndex);
devusb_do_read_io(dev,BUF_PER_PAGE);
wake_up_interruptible(&dev->wqReadOK);
}
}
static void usbdev_delete(struct kref *kref)
{
struct usb_devinfo *dev = container_of(kref, struct usb_devinfo, kref);
int i;
usb_free_urb(dev->bulk_in_urb);
usb_put_dev(dev->udev);
kfree(dev->bulk_in_buffer);
for(i=0;i<MAX_PAGE;i++)
{
kfree(dev->UsbBuffer.PackgeBuf[i]);
}
kfree(dev);
}
static int usbdev_open(struct inode *inode, struct file *file)
{
struct usb_devinfo *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;
printk(KERN_ALERT"usbdev_open \n");
subminor = iminor(inode);
interface = usb_find_interface(&usbdev_driver, subminor);
if (!interface) {
pr_err("%s - error, can't find device for minor %d\n",
__func__, subminor);
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface);
if (!dev) {
retval = -ENODEV;
goto exit;
}
retval = usb_autopm_get_interface(interface);
if (retval)
goto exit;
/* increment our usage count for the device */
kref_get(&dev->kref);
/* save our object in the file's private structure */
file->private_data = dev;
dev->UsbBuffer.iReadIndex=dev->UsbBuffer.iWriteIndex=0;
devusb_do_read_io(dev,BUF_PER_PAGE);
dev->isOpen = 1 ;
exit:
return retval;
}
static int usbdev_release(struct inode *inode, struct file *file)
{
struct usb_devinfo *dev;
dev = file->private_data;
if (dev == NULL)
return -ENODEV;
/* allow the device to be autosuspended */
mutex_lock(&dev->io_mutex);
usbdev_emptyurb(dev);
if (dev->interface)
usb_autopm_put_interface(dev->interface);
mutex_unlock(&dev->io_mutex);
dev->isOpen = 0 ;
wake_up_interruptible(&dev->wqReadOK);
/* decrement the count on our device */
kref_put(&dev->kref, usbdev_delete);
return 0;
}
static int usbdev_flush(struct file *file, fl_owner_t id)
{
struct usb_devinfo *dev;
int res;
dev = file->private_data;
if (dev == NULL)
return -ENODEV;
/* wait for io to stop */
mutex_lock(&dev->io_mutex);
usbdev_emptyurb(dev);
/* read out errors, leave subsequent opens a clean slate */
spin_lock_irq(&dev->err_lock);
res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0;
dev->errors = 0;
spin_unlock_irq(&dev->err_lock);
mutex_unlock(&dev->io_mutex);
return res;
}
static void usbdev_read_callback(struct urb *urb)
{
struct usb_devinfo *dev;
dev = urb->context;
//printk(KERN_ALERT"usbdev_read_callback\n");
spin_lock(&dev->err_lock);
/* sync/async unlink faults aren't errors */
if (urb->status) {
if (!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
dev_err(&dev->interface->dev,
"%s - nonzero write bulk status received: %d\n",
__func__, urb->status);
dev->errors = urb->status;
} else {
dev->bulk_in_filled = urb->actual_length;
if(dev->isOpen==1)
{
schedule_work(&(dev->work));
}
}
spin_unlock(&dev->err_lock);
}
static int devusb_do_read_io(struct usb_devinfo *dev, size_t count)
{
int rv;
/* prepare a read */
usb_fill_bulk_urb(dev->bulk_in_urb,
dev->udev,
usb_rcvbulkpipe(dev->udev,
dev->bulk_in_endpointAddr),
dev->UsbBuffer.PackgeBuf[dev->UsbBuffer.iWriteIndex],//dev->bulk_in_buffer,//
min(dev->bulk_in_size, count),
usbdev_read_callback,
dev);
/* tell everybody to leave the URB alone */
//spin_lock_irq(&dev->err_lock);
//spin_unlock_irq(&dev->err_lock);
/* submit bulk in urb, which means no data to deliver */
dev->bulk_in_filled = 0;
dev->bulk_in_copied = 0;
/* do it */
rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
if (rv < 0) {
//dev_err(&dev->interface->dev,
// "%s - failed submitting read urb, error %d\n",
// __func__, rv);
rv = (rv == -ENOMEM) ? rv : -EIO;
spin_lock_irq(&dev->err_lock);
spin_unlock_irq(&dev->err_lock);
}
//printk(KERN_ALERT"devusb_do_read_io\n");
return rv;
}
static ssize_t usbdev_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct usb_devinfo *dev;
int copyed=0;
dev = file->private_data;
if(dev->isOpen != 1 )
{
//printk(KERN_ALERT"close read err\n");
return -1;
}
if ( !count)
return 0;
if(dev->UsbBuffer.iReadIndex==dev->UsbBuffer.iWriteIndex)
{
if (file->f_flags & O_NONBLOCK)
{
return copyed;
}
else
{
wait_event_interruptible(dev->wqReadOK, (dev->UsbBuffer.iReadIndex!=dev->UsbBuffer.iWriteIndex)||(dev->isOpen != 1 ) );
}
}
if(dev->isOpen != 1 )
{
//printk(KERN_ALERT"close read err1\n");
return -1;
}
//下一步加锁
mutex_lock(&dev->read_mutex);
while( dev->UsbBuffer.iReadIndex!=dev->UsbBuffer.iWriteIndex )
{
int iLen = dev->UsbBuffer.iLen[dev->UsbBuffer.iReadIndex];
if( count>=iLen )
{
if(copy_to_user(buffer+copyed, &dev->UsbBuffer.PackgeBuf[dev->UsbBuffer.iReadIndex][0], iLen)!=0)
{
copyed = -EFAULT;
break;
}
dev->UsbBuffer.iReadIndex++;
if(dev->UsbBuffer.iReadIndex>=MAX_PAGE)
{
dev->UsbBuffer.iReadIndex = 0;
}
count-=iLen;
copyed+=iLen;
//printk("read r=%d w=%d\n",dev->UsbBuffer.iReadIndex,dev->UsbBuffer.iWriteIndex );
if(iLen<BUF_PER_PAGE)
{
break;
}
}
else
{
break;
}
}
mutex_unlock(&dev->read_mutex);
return copyed;
}
static void usbdev_write_callback(struct urb *urb)
{
struct usb_devinfo *dev;
dev = urb->context;
/* sync/async unlink faults aren't errors */
if (urb->status) {
if (!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
dev_err(&dev->interface->dev,
"%s - nonzero write bulk status received: %d\n",
__func__, urb->status);
spin_lock(&dev->err_lock);
dev->errors = urb->status;
spin_unlock(&dev->err_lock);
}
/* free up our allocated buffer */
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
up(&dev->limit_sem);
}
static ssize_t usbdev_write(struct file *file, const char *user_buffer,
size_t count, loff_t *ppos)
{
struct usb_devinfo *dev;
int retval = 0;
struct urb *urb = NULL;
char *buf = NULL;
size_t writesize = min(count, (size_t)MAX_TRANSFER);
dev = file->private_data;
if(dev->isOpen != 1 )
{
return -1;
}
/* verify that we actually have some data to write */
if (count == 0)
goto exit;
/*
* limit the number of URBs in flight to stop a user from using up all
* RAM
*/
if (!(file->f_flags & O_NONBLOCK)) {
int ret = down_timeout(&dev->limit_sem,10);
if (ret!=0) {
retval = -ERESTARTSYS;
goto exit;
}
} else {
if (down_trylock(&dev->limit_sem)) {
retval = -EAGAIN;
goto exit;
}
}
spin_lock_irq(&dev->err_lock);
retval = dev->errors;
if (retval < 0) {
/* any error is reported once */
dev->errors = 0;
/* to preserve notifications about reset */
retval = (retval == -EPIPE) ? retval : -EIO;
}
spin_unlock_irq(&dev->err_lock);
if (retval < 0)
goto error;
/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto error;
}
buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
&urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
if (copy_from_user(buf, user_buffer, writesize)) {
retval = -EFAULT;
goto error;
}
/* this lock makes sure we don't submit URBs to gone devices */
mutex_lock(&dev->io_mutex);
if (!dev->interface) { /* disconnect() was called */
mutex_unlock(&dev->io_mutex);
retval = -ENODEV;
goto error;
}
/* initialize the urb properly */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, writesize, usbdev_write_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &dev->submitted);
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
mutex_unlock(&dev->io_mutex);
if (retval) {
dev_err(&dev->interface->dev,
"%s - failed submitting write urb, error %d\n",
__func__, retval);
goto error_unanchor;
}
/*
* release our reference to this urb, the USB core will eventually free
* it entirely
*/
usb_free_urb(urb);
return writesize;
error_unanchor:
usb_unanchor_urb(urb);
error:
if (urb) {
usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
}
up(&dev->limit_sem);
exit:
return retval;
}
static const struct file_operations usbdev_fops = {
.owner = THIS_MODULE,
.read = usbdev_read,
.write = usbdev_write,
.open = usbdev_open,
.release = usbdev_release,
.flush = usbdev_flush,
.llseek = noop_llseek,
};
/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with the driver core
*/
static struct usb_class_driver usbdev_class = {
.name = "USBDev%d",
.fops = &usbdev_fops,
.minor_base = USB_DEV_MINOR_BASE,
};
static struct usb_class_driver usbdevother_class = {
.name = "USBOther%d",
.fops = &usbdev_fops,
.minor_base = USB_DEV_MINOR_BASE,
};
static int usbdev_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_devinfo *dev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
int i;
int retval = -ENOMEM;
int iInterfaceType=0;
if(interface->cur_altsetting->desc.bInterfaceClass==0xff)
{
if(interface->cur_altsetting->desc.bInterfaceSubClass==0xff)
{
iInterfaceType=1;
}
else
{
iInterfaceType=2;
}
}
if(iInterfaceType==0)
{
return ;
}
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
dev_err(&interface->dev, "Out of memory\n");
goto error;
}
kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
mutex_init(&dev->io_mutex);
spin_lock_init(&dev->err_lock);
init_usb_anchor(&dev->submitted);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
buffer_size = usb_endpoint_maxp(endpoint);
dev->bulk_in_size = BUF_PER_PAGE;//buffer_size*100;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
dev_err(&interface->dev,
"Could not allocate bulk_in_buffer\n");
goto error;
}
dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->bulk_in_urb) {
dev_err(&interface->dev,
"Could not allocate bulk_in_urb\n");
goto error;
}
}
if (!dev->bulk_out_endpointAddr &&
usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
dev_err(&interface->dev,
"Could not find both bulk-in and bulk-out endpoints\n");
goto error;
}
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
/* we can register the device now, as it is ready */
if(iInterfaceType==1)
{
retval = usb_register_dev(interface, &usbdev_class);
}
else
{
retval = usb_register_dev(interface, &usbdevother_class);
}
if (retval) {
/* something prevented us from registering this driver */
dev_err(&interface->dev,
"Not able to get a minor for this device.\n");
usb_set_intfdata(interface, NULL);
goto error;
}
memset(&dev->UsbBuffer,0,sizeof(cachebuf));
init_waitqueue_head(&dev->wqReadOK);
INIT_WORK(&(dev->work), work_func);
mutex_init(&dev->read_mutex);
dev->isOpen = 0 ;
for(i=0;i< MAX_PAGE;i++)
{
dev->UsbBuffer.PackgeBuf[i] = kmalloc(dev->bulk_in_size, GFP_KERNEL);
}
/* let the user know what node this device is now attached to */
dev_info(&interface->dev,
"USB Skeleton device now attached to USBSkel-%d",
interface->minor);
return 0;
error:
if (dev)
/* this frees allocated memory */
kref_put(&dev->kref, usbdev_delete);
return retval;
}
static void usbdev_disconnect(struct usb_interface *interface)
{
struct usb_devinfo *dev;
int minor = interface->minor;
int iInterfaceType=0;
if(interface->cur_altsetting->desc.bInterfaceClass==0xff)
{
if(interface->cur_altsetting->desc.bInterfaceSubClass==0xff)
{
iInterfaceType=1;
}
else
{
iInterfaceType=2;
}
}
if(iInterfaceType==0)
{
return ;
}
dev = usb_get_intfdata(interface);
dev->isOpen = 0 ;
wake_up_interruptible(&dev->wqReadOK);
usb_set_intfdata(interface, NULL);
/* give back our minor */
if(iInterfaceType==1)
{
usb_deregister_dev(interface, &usbdev_class);
}
else
{
usb_deregister_dev(interface, &usbdevother_class);
}
/* prevent more I/O from starting */
mutex_lock(&dev->io_mutex);
dev->interface = NULL;
mutex_unlock(&dev->io_mutex);
usb_kill_anchored_urbs(&dev->submitted);
/* decrement our usage count */
kref_put(&dev->kref, usbdev_delete);
dev_info(&interface->dev, "USB #%d now disconnected", minor);
}
static void usbdev_emptyurb(struct usb_devinfo *dev)
{
int time;
time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
if (!time)
usb_kill_anchored_urbs(&dev->submitted);
usb_kill_urb(dev->bulk_in_urb);
}
static int usbdev_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_devinfo *dev = usb_get_intfdata(intf);
if (!dev)
return 0;
usbdev_emptyurb(dev);
return 0;
}
static int usbdev_resume(struct usb_interface *intf)
{
return 0;
}
static int usbdev_pre_reset(struct usb_interface *intf)
{
struct usb_devinfo *dev = usb_get_intfdata(intf);
mutex_lock(&dev->io_mutex);
usbdev_emptyurb(dev);
return 0;
}
static int usbdev_post_reset(struct usb_interface *intf)
{
struct usb_devinfo *dev = usb_get_intfdata(intf);
/* we are sure no URBs are active - no locking needed */
dev->errors = -EPIPE;
mutex_unlock(&dev->io_mutex);
return 0;
}
static struct usb_driver usbdev_driver = {
.name = "usbtest",
.probe = usbdev_probe,
.disconnect = usbdev_disconnect,
.suspend = usbdev_suspend,
.resume = usbdev_resume,
.pre_reset = usbdev_pre_reset,
.post_reset = usbdev_post_reset,
.id_table = usbdev_table,
.supports_autosuspend = 1,
};
static int __init usbdev_init(void)
{
return usb_register(&usbdev_driver);
}
module_init(usbdev_init);
static void __exit usbdev_exit(void)
{
usb_deregister(&usbdev_driver);
}
module_exit(usbdev_exit);
MODULE_LICENSE("GPL");
1.1
struct usb_device_id {
/* which fields to match against? */
__u16 match_flags;
/* Used for product specific matches; range is inclusive */
__u16 idVendor;
__u16 idProduct;
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
/* Used for device class matches */
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/* Used for interface class matches */
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
/* Used for vendor-specific interface matches */
__u8 bInterfaceNumber;
/* not matched against */
kernel_ulong_t driver_info
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
成员变量说明:
match_flags :需要匹配的类型(可以多个或起来),数据定义如下:
#define USB_DEVICE_ID_MATCH_VENDOR 0x0001 //匹配PID
#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 //匹配VID
#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 //匹配产品编号低位
#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 //匹配产品编号高位
#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 //匹配设备描述符的class
#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 //匹配设备描述符的subclass
#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 //匹配设备描述符的protocol
#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 //匹配接口描述符的class
#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 //匹配接口描述符的subclass
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 //匹配接口描述符的protocol
#define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400 //匹配接口的数量
driver_info :用来保存用户的私有数据
1.2
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
int (*reset_resume)(struct usb_interface *intf);
int (*pre_reset)(struct usb_interface *intf);
int (*post_reset)(struct usb_interface *intf);
const struct usb_device_id *id_table;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int disable_hub_initiated_lpm:1;
unsigned int soft_unbind:1;
};
name:独一无二的设备的名字
probe:回调函数指针,在USB设备插入,且符合struct usb_device_id 定义的匹配条件是被调用。这是如果匹配成功返回0,
disconnect:回调函数指针,当设备拔出时被调用
unlocked_ioctl:
suspend : 设备休眠的时候被调用
resume:设备唤醒的时候被调用
reset_resume:设备在休眠的时候已经reset,唤醒的时候调用
pre_reset:在调用usb_reset_device()的时候这个回调函数被调用,这个函数在reset前调用,该函数必须等到没有有效的URBs才能返回。
post_reset:在调用usb_reset_device()的时候这个回调函数被调用,这个函数在reset完成后调用
id_table:需要匹配的设备信息
dynids:用来保持动态添加的设备列表
drvwrap:Driver-model core structure wrapper
no_dynamic_id:如果设置为1,内核将不允许添加动态设备列表
supports_autosuspend:如果设置为0 内核将不允许设备自动休眠,不知道为什么这个参数设置为0,有时会造成设备访问被拒绝
soft_unbind:如果设置为1 ,在设备拔出前,不允许禁用端点,杀掉URBs
disable_hub_initiated_lpm:
注意:这个结构必须要实现name、probe、disconnect、id_table,其他成员可以根据情况选择是否实现
1.3
struct usb_class_driver {
char *name;
char *(*devnode)(struct device *dev, umode_t *mode);
const struct file_operations *fops;
int minor_base;
};
name:设备节点的名称,也就是在dev目录下的设备,可以直接Open这个设备进行读写
devnode:??
fops:设备文件操作结构
minor_base:次设备号的基准值,
1.4
struct urb {
/* private: usb core and host controller only fields in the urb */
struct kref kref; /* reference count of the URB */
void *hcpriv; /* private data for host controller */
atomic_t use_count; /* concurrent submissions counter */
atomic_t reject; /* submissions will fail */
int unlinked; /* unlink error code */
/* public: documented fields in the urb that can be used by drivers */
struct list_head urb_list; /* list head for use by the urb's * current owner */
struct list_head anchor_list; /* the URB may be anchored */
struct usb_anchor *anchor;
struct usb_device *dev; /* (in) pointer to associated device */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe; /* (in) pipe information */
unsigned int stream_id; /* (in) stream ID */
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
struct scatterlist *sg; /* (in) scatter gather buffer list */
int num_mapped_sgs; /* (internal) mapped sg entries */
int num_sgs; /* (in) number of entries in the sg list */
u32 transfer_buffer_length; /* (in) data buffer length */
u32 actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
int start_frame; /* (modify) start frame (ISO) */
int number_of_packets; /* (in) number of ISO packets */
int interval; /* (modify) transfer interval
* (INT/ISO) */
int error_count; /* (return) number of ISO errors */
void *context; /* (in) context for completion */
usb_complete_t complete; /* (in) completion routine */
struct usb_iso_packet_descriptor iso_frame_desc[0];
/* (in) ISO ONLY */
};
以下的成员变量是私有的,只能由usb核心和主机控制器访问的字段
kref : urb引用计数 ,当计数引用为0时,系统释放urb
hcpriv : 主机控制器私有数据
use_count : 并发传输计数
reject : 传输将失败
以下的成员变量是公共的,可以被驱动使用的字段
urb_list :链表头
dev :关联的usb设备
pipe :端点消息,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化.
要设置这个结构的成员, 驱动可以使用下面的函数来设置. 注意每个端点只可以是一个类型.
unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)
指定一个控制 OUT 端点给特定的带有特定端点号的 USB 设备.
unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)
指定一个控制 IN 端点给带有特定端点号的特定 USB 设备.
unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)
指定一个块传输 OUT 端点给带有特定端点号的特定 USB 设备
unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)
指定一个块传输 IN 端点给带有特定端点号的特定 USB 设备
unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)
指定一个中断传输 OUT 端点给带有特定端点号的特定 USB 设备
unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)
指定一个中断传输 IN 端点给带有特定端点号的特定 USB 设备
unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)
指定一个同步传输 OUT 端点给带有特定端点号的特定 USB 设备
unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)
指定一个同步传输 IN 端点给带有特定端点号的特定 USB 设备
status : urb的当前状态
0 urb 传送是成功的.
-ENOENT 这个 urb 被 usb_kill_urb调用停止.
-ECONNRESET urb 被usb_unlink_urb 调用去链, 并且 transfer_flags 变量被设置为 URB_ASYNC_UNLINK.
-EINPROGRESS 这个 urb 仍然在被 USB 主机控制器处理中. 如果你的驱动曾见到这个值, 它是一个你的驱动中的 bug.
-EPROTO 一个 bitstuff 错误在传送中发生. 硬件没有及时收到响应帧.
-EILSEQ 在这个 urb 传送中 CRC 不匹配.
-EPIPE 这个端点现在被停止. 如果这个包含的端点不是一个控制端点, 这个错误可被清除通过一个对函数 usb_clear_halt 的调用.
-ECOMM 在传送中数据接收快于能被写入系统内存. 这个错误值只对 IN urb.
-ENOSR 在传送中数据不能从系统内存中获取得足够快, 以便可跟上请求的 USB 数据速率. 这个错误只对 OUT urb.
-EOVERFLOW 这个 urb 发生一个"babble"错误. 一个"babble"错误发生当端点接受数据多于端点的特定最大报文大小.
-EREMOTEIO 只发生在当 URB_SHORT_NOT_OK 标志被设置在 urb 的 transfer_flags 变量, 并且意味着 urb 请求的完整数量的数据没有收到.
-ENODEV 这个 USB 设备现在从系统中消失.
-EXDEV 只对同步 urb 发生, 并且意味着传送只部分完成. 为了决定传送什么, 驱动必须看单独的帧状态.
-EINVAL 这个 urb 发生了非常坏的事情. USB 内核文档描述了这个值意味着什么:ISO 疯了, 如果发生这个: 退出并回家.
它也可发生, 如果一个参数在 urb 结构中被不正确地设置了, 或者如果在提交这个 urb 给 USB 核心的 usb_submit_urb 调用中, 有一个不正确的函数参数.
-ESHUTDOWN 这个 USB 主机控制器驱动有严重的错误; 它现在已被禁止, 或者设备和系统去掉连接, 并且这个urb 在设备被去除后被提交. 它也可发生当这个设备的配置改变, 而这个 urb 被提交给设备.
通常, 错误值 -EPROTO, -EILSEQ, 和 -EOVERFLOW 指示设备的硬件问题, 设备固件, 或者连接设备到计算机的线缆.
transfer_flags:这个变量可被设置为不同位值, 根据这个 USB 驱动想这个 urb 发生什么. 可用的值如下:
URB_SHORT_NOT_OK 当置位, 它指出任何在一个 IN 端点上可能发生的短读, 应当被 USB 核心当作一个错误. 这个值只对从 USB 设备读的 urb 有用, 不是写 urbs.
URB_ISO_ASAP 如果这个 urb 是同步的, 这个位可被置位如果驱动想这个 urb 被调度, 只要带宽允许它这样, 并且在此点设置这个 urb 中的 start_frame 变量. 如果对于同步 urb 这个位没有被置位, 驱动必须指定 start_frame 值并且必须能够正确恢复, 如果没有在那个时刻启动. 见下面的章节关于同步 urb 更多的消息.
URB_NO_TRANSFER_DMA_MAP 应当被置位, 当 urb 包含一个要被发送的 DMA 缓冲. USB 核心使用这个被 transfer_dma 变量指向的缓冲, 不是被 transfer_buffer 变量指向的缓冲.
URB_NO_SETUP_DMA_MAP 象 URB_NO_TRANSFER_DMA_MAP 位, 这个位用来控制有一个 DMA 缓冲已经建立的 urb. 如果它被置位, USB 核心使用这个被 setup_dma 变量而不是 setup_packet 变量指向的缓冲.
URB_ASYNC_UNLINK 如果置位, 给这个 urb 的对 usb_unlink_urb 的调用几乎立刻返回, 并且这个 urb 在后面被解除连接. 否则, 这个函数等待直到 urb 完全被去链并且在返回前结束. 小心使用这个位, 因为它可有非常难于调试的同步问题.
URB_NO_FSBR 只有 UHCI USB 主机控制器驱动使用, 并且告诉它不要试图做 Front Side Bus Reclamation 逻辑. 这个位通常应当不设置, 因为有 UHCI 主机控制器的机器创建了许多 CPU 负担, 并且 PCI 总线被等待设置了这个位的 urb 所饱和.
URB_ZERO_PACKET 如果置位, 一个块 OUT urb 通过发送不包含数据的短报文而结束, 当数据对齐到一个端点报文边界. 这被一些坏掉的 USB 设备所需要(例如一些 USB 到 IR 的设备) 为了正确的工作..
URB_NO_INTERRUPT 如果置位, 硬件当 urb 结束时可能不产生一个中断. 这个位应当小心使用并且只在排队多个到相同端点的 urb 时使用. USB 核心函数使用这个为了做 DMA 缓冲传送.
transfer_buffer : 发送数据到设备或从设备接收数据的缓冲区,这个缓冲区它必须被使用 kmalloc来创建, 不是在堆栈或者静态地址,所以不能定义一个大数组来作为缓冲区.
transfer_dma : 用来以dma方式向设备传输数据的缓冲区
transfer_buffer_length : transfer_buffer或transfer_dma 指向缓冲区的大小
actual_length : urb结束后,发送或接收数据的实际长度
setup_packet : 指向控制urb的设置数据包的指针
setup_dma :控制urb的设置数据包的dma缓冲区
start_frame :等时传输中用于设置或返回初始帧
number_of_packets :只对同步 urb 有效, 并且指定这个 urb 要处理的同步传送缓冲的编号. 这个值必须被 USB 驱动设置给同步 urb, 在这个 urb 发送给 USB 核心之前.
interval : urb被轮询到的时间间隔(对中断和等时urb有效)
error_count : 等时传输错误数量
context :completion函数上下文 ,用来保存用户数据的指针
complete : 回调函数指针,当urb被完全传输或发生错误时,被调用
usb_complete_t 类型定义 :typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
iso_frame_desc[0] : 单个urb一次可定义多个等时传输时,描述各个等时传输
注意:urb 不能静态创建, 或者在另一个结构中, 必须使用 usb_alloc_urb 函数来创建,使用usb_free_urb来释放 。
2 函数说明
int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name)
函数功能:注册一个USB驱动
参数说明:new_driver :struct usb_driver的结构指针
owner :模块的所有者名
mod_name :模块名称
返回值:0 成功 非0 失败
说明 :这个函数通常在驱动的__init函数中调用注册一个USB驱动。为简化调用的参数,我们通常直接用 usb_register这个宏
usb_register定义如下
#define usb_register(driver) usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
void usb_deregister(struct usb_driver * driver);
函数功能:注销一个USB驱动,这个函数通常在驱动的__exit函数中调用
参数说明:driver : struct usb_driver的结构指针
返回值:无
int usb_register_dev(struct usb_interface *intf, struct usb_class_driver *class_driver)
函数功能:注册一个USB设备,注册成功后可以在/dev目录下看到相应的设备节点,这个函数通常是在struct usb_driver中定义的probe回调函数中被调用
参数说明:intf :USB接口描述符
class_driver : struct usb_class_driver 这个结构将指定设备的名称和 file_operations结构
返回值:0成功 非0 失败
void usb_deregister_dev(struct usb_interface *intf, struct usb_class_driver *class_driver)
函数功能: 注销一个USB设备,这个函数通常是在struct usb_driver中定义的disconnect回调函数中被调用
参数说明:intf :USB接口描述符
class_driver : struct usb_class_driver 这个结构将指定设备的名称和 file_operations结构
返回值: 无
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
函数功能:动态分配一个urb,并初始化
参数说明:iso_packets : usb_iso_packet_descriptor的个数,这个参数只对同步传输有效。对于控制、中断、块传输,这个参数必须为0
mem_flags : 和kmalloc 函数调用来从内核分配内存的标志类型相同
返回值:成功返回分配的urb的指针,失败返回NULL
void usb_free_urb(struct urb *urb)
函数功能:释放usb_alloc_urb分配的urb
参数说明:urb : 要释放的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_fn, void *context)
void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length, usb_complete_t complete_fn, void *context, int interval)
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_fn,void *context)
函数功能:用来填充用于块传输、中断传输、控制传输的urb
参数说明:Urb:是要初始化的urb
Dev:表示消息要被发送到的USB设备
Pipe:表示消息被发送到的端点
transfer_buffer:表示发送或接收数据的缓冲区
length:就是transfer_buffer所表示的缓冲区大小
context:完成处理函数的上下文
complete_fn:传输完了之后的回调函数.
interval:中断传输被调度的间隔
setup_packet:将被发送到端点的设置数据包
返回值:无
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
函数功能:用来递交urb,该函数以非阻塞的方式发送,也就是这个函数将不等发送完成就返回。
参数说明:urb :指向urb的指针
mem_flags :可以有下面三种取值
GFP_ATOMIC:在中断处理函数、底半部、tasklet、定时器处理函数以及urb完成函数中,在调用者持有自旋锁或者读写锁时以及当驱动将current->state修改为非 TASK_ RUNNING时,应使用此标志。
GFP_NOIO:在存储设备的块I/O和错误处理路径中,应使用此标志;
GFP_KERNEL:如果没有任何理由使用GFP_ATOMIC和GFP_NOIO,就使用GFP_ KERNEL。
返回值:0成功 非0 失败
void usb_kill_urb(struct urb *urb)
函数功能:取消一个urb传输,并且等待他结束
参数说明:要取消的urb的指针
返回值:无
int usb_unlink_urb(struct urb *urb)
函数功能:停止 urb传输. 这个函数 不等待这个 urb 完全停止就返回
参数说明:要取消的urb的指针
返回值:无
int usb_control_msg(struct usb_device *dev, unsigned int pipe, u8 request, u8 requesttype, u16 value, u16 index, void *data, u16 size, int timeout)
函数功能:以阻塞的方式进行控制传输,直到超时或urb传输完成函数才返回
参数说明:Dev:表示消息要被发送到的USB设备
Pipe:表示消息被发送到的端点
request:命令的序号(其实就是命令)
requesttype:D7=0主机到设备, =1设备到主机;
D6D5 =00标准请求命令, 01 类请求命令,10用户定义的命令,11保留值
D4D3D2D1D0= 0表示接收者为设备,1表示接收者为接口,2表示接收者为端点,3表示接收者为其他
value:2个字节,用来传送当前请求的参数,随请求不同而变。
index:索引字段同样是2个字节,描述的是接口号
data :数据缓冲区的指针
size:数据缓冲区的长度
timeout:超时时间
返回值:0成功,非0 失败
int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
函数功能:以阻塞的方式进行中断或者块传输,直到超时或urb传输完成函数才返回
参数说明:Dev:表示消息要被发送到的USB设备
Pipe:表示消息被发送到的端点
data :数据缓冲区的指针
len :数据缓冲区的长度
actual_length :实际发送或者接收的数据长度
timeout:超时时间
返回值:0成功,非0 失败
void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
函数功能:当urb在处理的时候锁定该urb ,urb被使用时是不能被删除的。在删除一个urb时需要调用usb_wait_anchor_empty_timeout来等待urb传输完全结束
参数说明:urb:要锁定的urb
anchor:
返回值:无
void usb_unanchor_urb(struct urb *urb)
函数功能:解锁一个urb
参数说明:urb:要解锁的urb
返回值:无
int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,unsigned int timeout)
函数功能:等待urb传输完全结束
参数说明:urb:要等待的urb
anchor:
timeout :超时设置
返回值:0传输完成,非0 超时
void usb_kill_anchored_urbs(struct usb_anchor *anchor)
函数功能:撤销一个urb传输
参数说明:anchor:
3注册一个USB驱动的流程
1 定义一个 struct usb_device_id的结构。
2 定义一个 struct usb_driver,并实现probe和disconnect函数
3 在模块的init函数中调用usb_register函数注册一个驱动
4 定义一个usb_class_driver
5 在probe函数中调用usb_register_dev注册一个USB设备,这样在dev目录下就可以看到我们的USB设备了
下面是示例代码:
。。。。
static const struct file_operations usbdev_fops = {
.owner = THIS_MODULE,
.read = usbdev_read,
.write = usbdev_write,
.open = usbdev_open,
.release = usbdev_release,
.flush = usbdev_flush,
.llseek = noop_llseek,
};
static struct usb_class_driver usbdev_class = {
.name = "USBTestDevice%d",//这里一次插上多个USB时候,将在dev目录下看到USBTestDevice0、USBTestDevice1 ....
.fops = &usbdev_fops,
.minor_base = USB_DEV_MINOR_BASE,
};
static int usbdev_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
......
retval = usb_register_dev(interface, &usbdev_class);
......
}
static void usbdev_disconnect(struct usb_interface *interface)
{
................
usb_deregister_dev(interface, &usbdev_class);
................
}
static const struct usb_device_id usbdev_table[] = {
{ USB_DEVICE(USB_MATCH_VENDOR_ID, USB_MATCH_PRODUCT_ID) },
{ USB_DEVICE(USB_MATCH_VENDOR_ID, USB_MATCH_PRODUCT_ID_1) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usbdev_table);
static struct usb_driver usbdev_driver = {
.name = "usbtest",
.probe = usbdev_probe,
.disconnect = usbdev_disconnect,
.id_table = usbdev_table,
};
static int __init usbdev_init(void)
{
return usb_register(&usbdev_driver);
}
module_init(usbdev_init);
static void __exit usbdev_exit(void)
{
usb_deregister(&usbdev_driver);
}
module_exit(usbdev_exit);
4 urb的操作流程:
1 调用usb_alloc_urb分配一个urb
2 调用kmalloc分配缓冲区
3 调用usb_fill_XXX_urb初始化urb
4 调用usb_submit_urb提交
5 在回调函数(complete)中处理,接收的数据,判断是否还要继续接收,如果还要接收,重复第三步。
如果是发送数据,判断是否还有数据要发送,填充数据
6 如果传输完成调用usb_free_urb释放urb,释放缓冲区
下面是示例代码
//回调函数
static void async_ctrl_callback(struct urb *urb)
{
struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
int status = urb->status;
if (status < 0)
{
printk("error\n");
}
kfree(req);
kfree(urb->transfer_buffer);
usb_free_urb(urb);
}
struct urb *urb;
struct usb_ctrlrequest *req;
void *data;
urb = usb_alloc_urb(0, GFP_KERNEL);
req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
/*根据协议设置reg*/
req->bRequestType = RequestType;
req->bRequest = Request;
req->wValue = Value;
req->wIndex = Index;
req->wLength = Length;
data = kmalloc(Length, GFP_KERNEL);
usb_fill_control_urb(urb, dev, usb_sndctrlpipe(dev, 0),reg, data, Length, usb_ctrl_complete, req);
usb_submit_urb(urb, GFP_ATOMIC);
对于其他的传输,方法差不多。
最后需要注意的是中断传输接收,不需要每次调用usb_submit_urb,调用一次usb_submit_urb之后,系统会按照指定的间隔,不断接收数据的。
5 下面是完成的驱动的示例代码
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/moduleparam.h>
#define BUF_PER_PAGE 10240
#define MAX_PAGE 5
typedef struct
{
volatile int iReadIndex;
volatile int iWriteIndex;
unsigned char *PackgeBuf[MAX_PAGE];//[BUF_PER_PAGE];
int iLen[MAX_PAGE];
}cachebuf ;
/* Define these values to match your devices */
#define USB_MATCH_VENDOR_ID 0x1234
#define USB_MATCH_PRODUCT_ID 0x5678
/* table of devices that work with this driver */
static const struct usb_device_id usbdev_table[] = {
{ USB_DEVICE(USB_MATCH_VENDOR_ID, USB_MATCH_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, usbdev_table);
/* Get a minor range for your devices from the usb maintainer */
#define USB_DEV_MINOR_BASE 192
/* our private defines. if this grows any larger, use your own .h file */
#define MAX_TRANSFER (PAGE_SIZE - 512)
/* MAX_TRANSFER is chosen so that the VM is not stressed by
allocations > PAGE_SIZE and the number of packets in a page
is an integer 512 is the largest possible packet on EHCI */
#define WRITES_IN_FLIGHT 8
/* arbitrarily chosen */
/* Structure to hold all of our device specific stuff */
struct usb_devinfo {
struct usb_device *udev; /* the usb device for this device */
struct usb_interface *interface; /* the interface for this device */
struct semaphore limit_sem; /* limiting the number of writes in progress */
struct usb_anchor submitted; /* in case we need to retract our submissions */
struct urb *bulk_in_urb; /* the urb to read data with */
unsigned char *bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
size_t bulk_in_filled; /* number of bytes in the buffer */
size_t bulk_in_copied; /* already copied to user space */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
int errors; /* the last request tanked */
spinlock_t err_lock; /* lock for errors */
struct kref kref;
struct mutex io_mutex; /* synchronize I/O with disconnect */
cachebuf UsbBuffer;
wait_queue_head_t wqReadOK; /* buf is not empty*/
struct work_struct work;
struct mutex read_mutex;
int isOpen;
};
static struct usb_driver usbdev_driver;
static void usbdev_emptyurb(struct usb_devinfo *dev);
static int devusb_do_read_io(struct usb_devinfo *dev, size_t count);
static void work_func(struct work_struct *work)
{
struct usb_devinfo *dev = container_of(work, struct usb_devinfo, work);
if(dev->isOpen==0)
{
return ;
}
if(dev->bulk_in_filled>0)
{
int iWriteIndex = dev->UsbBuffer.iWriteIndex;
mutex_lock(&dev->read_mutex);
//memcpy(dev->UsbBuffer.PackgeBuf[iWriteIndex],dev->bulk_in_buffer ,dev->bulk_in_filled);
dev->UsbBuffer.iLen[iWriteIndex] = dev->bulk_in_filled;
mutex_unlock(&dev->read_mutex);
iWriteIndex++;
if(iWriteIndex>=MAX_PAGE)
{
iWriteIndex = 0;
}
while(iWriteIndex ==dev->UsbBuffer.iReadIndex)
{
if(iRetry==0)
printk(KERN_ALERT"usbdev_overflow(%d %d) \n",dev->UsbBuffer.iReadIndex,dev->UsbBuffer.iWriteIndex);
}
mutex_lock(&dev->read_mutex);
dev->UsbBuffer.iWriteIndex = iWriteIndex;
mutex_unlock(&dev->read_mutex);
dev->bulk_in_filled = 0;
//printk("write w=%d r=%d \n",dev->UsbBuffer.iWriteIndex,dev->UsbBuffer.iReadIndex);
devusb_do_read_io(dev,BUF_PER_PAGE);
wake_up_interruptible(&dev->wqReadOK);
}
}
static void usbdev_delete(struct kref *kref)
{
struct usb_devinfo *dev = container_of(kref, struct usb_devinfo, kref);
int i;
usb_free_urb(dev->bulk_in_urb);
usb_put_dev(dev->udev);
kfree(dev->bulk_in_buffer);
for(i=0;i<MAX_PAGE;i++)
{
kfree(dev->UsbBuffer.PackgeBuf[i]);
}
kfree(dev);
}
static int usbdev_open(struct inode *inode, struct file *file)
{
struct usb_devinfo *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;
printk(KERN_ALERT"usbdev_open \n");
subminor = iminor(inode);
interface = usb_find_interface(&usbdev_driver, subminor);
if (!interface) {
pr_err("%s - error, can't find device for minor %d\n",
__func__, subminor);
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface);
if (!dev) {
retval = -ENODEV;
goto exit;
}
retval = usb_autopm_get_interface(interface);
if (retval)
goto exit;
/* increment our usage count for the device */
kref_get(&dev->kref);
/* save our object in the file's private structure */
file->private_data = dev;
dev->UsbBuffer.iReadIndex=dev->UsbBuffer.iWriteIndex=0;
devusb_do_read_io(dev,BUF_PER_PAGE);
dev->isOpen = 1 ;
exit:
return retval;
}
static int usbdev_release(struct inode *inode, struct file *file)
{
struct usb_devinfo *dev;
dev = file->private_data;
if (dev == NULL)
return -ENODEV;
/* allow the device to be autosuspended */
mutex_lock(&dev->io_mutex);
usbdev_emptyurb(dev);
if (dev->interface)
usb_autopm_put_interface(dev->interface);
mutex_unlock(&dev->io_mutex);
dev->isOpen = 0 ;
wake_up_interruptible(&dev->wqReadOK);
/* decrement the count on our device */
kref_put(&dev->kref, usbdev_delete);
return 0;
}
static int usbdev_flush(struct file *file, fl_owner_t id)
{
struct usb_devinfo *dev;
int res;
dev = file->private_data;
if (dev == NULL)
return -ENODEV;
/* wait for io to stop */
mutex_lock(&dev->io_mutex);
usbdev_emptyurb(dev);
/* read out errors, leave subsequent opens a clean slate */
spin_lock_irq(&dev->err_lock);
res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0;
dev->errors = 0;
spin_unlock_irq(&dev->err_lock);
mutex_unlock(&dev->io_mutex);
return res;
}
static void usbdev_read_callback(struct urb *urb)
{
struct usb_devinfo *dev;
dev = urb->context;
//printk(KERN_ALERT"usbdev_read_callback\n");
spin_lock(&dev->err_lock);
/* sync/async unlink faults aren't errors */
if (urb->status) {
if (!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
dev_err(&dev->interface->dev,
"%s - nonzero write bulk status received: %d\n",
__func__, urb->status);
dev->errors = urb->status;
} else {
dev->bulk_in_filled = urb->actual_length;
if(dev->isOpen==1)
{
schedule_work(&(dev->work));
}
}
spin_unlock(&dev->err_lock);
}
static int devusb_do_read_io(struct usb_devinfo *dev, size_t count)
{
int rv;
/* prepare a read */
usb_fill_bulk_urb(dev->bulk_in_urb,
dev->udev,
usb_rcvbulkpipe(dev->udev,
dev->bulk_in_endpointAddr),
dev->UsbBuffer.PackgeBuf[dev->UsbBuffer.iWriteIndex],//dev->bulk_in_buffer,//
min(dev->bulk_in_size, count),
usbdev_read_callback,
dev);
/* tell everybody to leave the URB alone */
//spin_lock_irq(&dev->err_lock);
//spin_unlock_irq(&dev->err_lock);
/* submit bulk in urb, which means no data to deliver */
dev->bulk_in_filled = 0;
dev->bulk_in_copied = 0;
/* do it */
rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
if (rv < 0) {
//dev_err(&dev->interface->dev,
// "%s - failed submitting read urb, error %d\n",
// __func__, rv);
rv = (rv == -ENOMEM) ? rv : -EIO;
spin_lock_irq(&dev->err_lock);
spin_unlock_irq(&dev->err_lock);
}
//printk(KERN_ALERT"devusb_do_read_io\n");
return rv;
}
static ssize_t usbdev_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct usb_devinfo *dev;
int copyed=0;
dev = file->private_data;
if(dev->isOpen != 1 )
{
//printk(KERN_ALERT"close read err\n");
return -1;
}
if ( !count)
return 0;
if(dev->UsbBuffer.iReadIndex==dev->UsbBuffer.iWriteIndex)
{
if (file->f_flags & O_NONBLOCK)
{
return copyed;
}
else
{
wait_event_interruptible(dev->wqReadOK, (dev->UsbBuffer.iReadIndex!=dev->UsbBuffer.iWriteIndex)||(dev->isOpen != 1 ) );
}
}
if(dev->isOpen != 1 )
{
//printk(KERN_ALERT"close read err1\n");
return -1;
}
//下一步加锁
mutex_lock(&dev->read_mutex);
while( dev->UsbBuffer.iReadIndex!=dev->UsbBuffer.iWriteIndex )
{
int iLen = dev->UsbBuffer.iLen[dev->UsbBuffer.iReadIndex];
if( count>=iLen )
{
if(copy_to_user(buffer+copyed, &dev->UsbBuffer.PackgeBuf[dev->UsbBuffer.iReadIndex][0], iLen)!=0)
{
copyed = -EFAULT;
break;
}
dev->UsbBuffer.iReadIndex++;
if(dev->UsbBuffer.iReadIndex>=MAX_PAGE)
{
dev->UsbBuffer.iReadIndex = 0;
}
count-=iLen;
copyed+=iLen;
//printk("read r=%d w=%d\n",dev->UsbBuffer.iReadIndex,dev->UsbBuffer.iWriteIndex );
if(iLen<BUF_PER_PAGE)
{
break;
}
}
else
{
break;
}
}
mutex_unlock(&dev->read_mutex);
return copyed;
}
static void usbdev_write_callback(struct urb *urb)
{
struct usb_devinfo *dev;
dev = urb->context;
/* sync/async unlink faults aren't errors */
if (urb->status) {
if (!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
dev_err(&dev->interface->dev,
"%s - nonzero write bulk status received: %d\n",
__func__, urb->status);
spin_lock(&dev->err_lock);
dev->errors = urb->status;
spin_unlock(&dev->err_lock);
}
/* free up our allocated buffer */
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
up(&dev->limit_sem);
}
static ssize_t usbdev_write(struct file *file, const char *user_buffer,
size_t count, loff_t *ppos)
{
struct usb_devinfo *dev;
int retval = 0;
struct urb *urb = NULL;
char *buf = NULL;
size_t writesize = min(count, (size_t)MAX_TRANSFER);
dev = file->private_data;
if(dev->isOpen != 1 )
{
return -1;
}
/* verify that we actually have some data to write */
if (count == 0)
goto exit;
/*
* limit the number of URBs in flight to stop a user from using up all
* RAM
*/
if (!(file->f_flags & O_NONBLOCK)) {
int ret = down_timeout(&dev->limit_sem,10);
if (ret!=0) {
retval = -ERESTARTSYS;
goto exit;
}
} else {
if (down_trylock(&dev->limit_sem)) {
retval = -EAGAIN;
goto exit;
}
}
spin_lock_irq(&dev->err_lock);
retval = dev->errors;
if (retval < 0) {
/* any error is reported once */
dev->errors = 0;
/* to preserve notifications about reset */
retval = (retval == -EPIPE) ? retval : -EIO;
}
spin_unlock_irq(&dev->err_lock);
if (retval < 0)
goto error;
/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto error;
}
buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
&urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
if (copy_from_user(buf, user_buffer, writesize)) {
retval = -EFAULT;
goto error;
}
/* this lock makes sure we don't submit URBs to gone devices */
mutex_lock(&dev->io_mutex);
if (!dev->interface) { /* disconnect() was called */
mutex_unlock(&dev->io_mutex);
retval = -ENODEV;
goto error;
}
/* initialize the urb properly */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, writesize, usbdev_write_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &dev->submitted);
/* send the data out the bulk port */
retval = usb_submit_urb(urb, GFP_KERNEL);
mutex_unlock(&dev->io_mutex);
if (retval) {
dev_err(&dev->interface->dev,
"%s - failed submitting write urb, error %d\n",
__func__, retval);
goto error_unanchor;
}
/*
* release our reference to this urb, the USB core will eventually free
* it entirely
*/
usb_free_urb(urb);
return writesize;
error_unanchor:
usb_unanchor_urb(urb);
error:
if (urb) {
usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
}
up(&dev->limit_sem);
exit:
return retval;
}
static const struct file_operations usbdev_fops = {
.owner = THIS_MODULE,
.read = usbdev_read,
.write = usbdev_write,
.open = usbdev_open,
.release = usbdev_release,
.flush = usbdev_flush,
.llseek = noop_llseek,
};
/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with the driver core
*/
static struct usb_class_driver usbdev_class = {
.name = "USBDev%d",
.fops = &usbdev_fops,
.minor_base = USB_DEV_MINOR_BASE,
};
static struct usb_class_driver usbdevother_class = {
.name = "USBOther%d",
.fops = &usbdev_fops,
.minor_base = USB_DEV_MINOR_BASE,
};
static int usbdev_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_devinfo *dev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
int i;
int retval = -ENOMEM;
int iInterfaceType=0;
if(interface->cur_altsetting->desc.bInterfaceClass==0xff)
{
if(interface->cur_altsetting->desc.bInterfaceSubClass==0xff)
{
iInterfaceType=1;
}
else
{
iInterfaceType=2;
}
}
if(iInterfaceType==0)
{
return ;
}
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
dev_err(&interface->dev, "Out of memory\n");
goto error;
}
kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
mutex_init(&dev->io_mutex);
spin_lock_init(&dev->err_lock);
init_usb_anchor(&dev->submitted);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
buffer_size = usb_endpoint_maxp(endpoint);
dev->bulk_in_size = BUF_PER_PAGE;//buffer_size*100;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
dev_err(&interface->dev,
"Could not allocate bulk_in_buffer\n");
goto error;
}
dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->bulk_in_urb) {
dev_err(&interface->dev,
"Could not allocate bulk_in_urb\n");
goto error;
}
}
if (!dev->bulk_out_endpointAddr &&
usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
}
if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
dev_err(&interface->dev,
"Could not find both bulk-in and bulk-out endpoints\n");
goto error;
}
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
/* we can register the device now, as it is ready */
if(iInterfaceType==1)
{
retval = usb_register_dev(interface, &usbdev_class);
}
else
{
retval = usb_register_dev(interface, &usbdevother_class);
}
if (retval) {
/* something prevented us from registering this driver */
dev_err(&interface->dev,
"Not able to get a minor for this device.\n");
usb_set_intfdata(interface, NULL);
goto error;
}
memset(&dev->UsbBuffer,0,sizeof(cachebuf));
init_waitqueue_head(&dev->wqReadOK);
INIT_WORK(&(dev->work), work_func);
mutex_init(&dev->read_mutex);
dev->isOpen = 0 ;
for(i=0;i< MAX_PAGE;i++)
{
dev->UsbBuffer.PackgeBuf[i] = kmalloc(dev->bulk_in_size, GFP_KERNEL);
}
/* let the user know what node this device is now attached to */
dev_info(&interface->dev,
"USB Skeleton device now attached to USBSkel-%d",
interface->minor);
return 0;
error:
if (dev)
/* this frees allocated memory */
kref_put(&dev->kref, usbdev_delete);
return retval;
}
static void usbdev_disconnect(struct usb_interface *interface)
{
struct usb_devinfo *dev;
int minor = interface->minor;
int iInterfaceType=0;
if(interface->cur_altsetting->desc.bInterfaceClass==0xff)
{
if(interface->cur_altsetting->desc.bInterfaceSubClass==0xff)
{
iInterfaceType=1;
}
else
{
iInterfaceType=2;
}
}
if(iInterfaceType==0)
{
return ;
}
dev = usb_get_intfdata(interface);
dev->isOpen = 0 ;
wake_up_interruptible(&dev->wqReadOK);
usb_set_intfdata(interface, NULL);
/* give back our minor */
if(iInterfaceType==1)
{
usb_deregister_dev(interface, &usbdev_class);
}
else
{
usb_deregister_dev(interface, &usbdevother_class);
}
/* prevent more I/O from starting */
mutex_lock(&dev->io_mutex);
dev->interface = NULL;
mutex_unlock(&dev->io_mutex);
usb_kill_anchored_urbs(&dev->submitted);
/* decrement our usage count */
kref_put(&dev->kref, usbdev_delete);
dev_info(&interface->dev, "USB #%d now disconnected", minor);
}
static void usbdev_emptyurb(struct usb_devinfo *dev)
{
int time;
time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
if (!time)
usb_kill_anchored_urbs(&dev->submitted);
usb_kill_urb(dev->bulk_in_urb);
}
static int usbdev_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_devinfo *dev = usb_get_intfdata(intf);
if (!dev)
return 0;
usbdev_emptyurb(dev);
return 0;
}
static int usbdev_resume(struct usb_interface *intf)
{
return 0;
}
static int usbdev_pre_reset(struct usb_interface *intf)
{
struct usb_devinfo *dev = usb_get_intfdata(intf);
mutex_lock(&dev->io_mutex);
usbdev_emptyurb(dev);
return 0;
}
static int usbdev_post_reset(struct usb_interface *intf)
{
struct usb_devinfo *dev = usb_get_intfdata(intf);
/* we are sure no URBs are active - no locking needed */
dev->errors = -EPIPE;
mutex_unlock(&dev->io_mutex);
return 0;
}
static struct usb_driver usbdev_driver = {
.name = "usbtest",
.probe = usbdev_probe,
.disconnect = usbdev_disconnect,
.suspend = usbdev_suspend,
.resume = usbdev_resume,
.pre_reset = usbdev_pre_reset,
.post_reset = usbdev_post_reset,
.id_table = usbdev_table,
.supports_autosuspend = 1,
};
static int __init usbdev_init(void)
{
return usb_register(&usbdev_driver);
}
module_init(usbdev_init);
static void __exit usbdev_exit(void)
{
usb_deregister(&usbdev_driver);
}
module_exit(usbdev_exit);
MODULE_LICENSE("GPL");
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e
added ecosystem file for PM2 4 年前
5def40a3
Add host customization support for the NodeJS version 4 年前
更多推荐
已为社区贡献6条内容
所有评论(0)