Linux_USB驱动程序设计学习
来源:互联网 发布:语音速记软件 编辑:程序博客网 时间:2024/05/16 02:35
Linux_USB软件系统架构构成:
usb主控制驱动(USB Host Controller Driver):为USB主控制器提供驱动
usb核心(USB Core):为usb主控制器和usb设备驱动连接起桥梁作用
usb设备驱动(USB Client Driver):为连接到usb主机的设备提供驱动
USB设备组成结构:
usb设备包括配置(configuration)、 接口( interface) 和端点(endpoint), 一个USB设备驱动程序对应一个USB接口 , 而非整个USB设备。
usb设备可能由一个或者多个配置构成,一个配置可能为多个接口构成,一个接口可能由一个或者多个端点构成。
usb的一个接口就是usb设备的一个基本功能。如果一个usb设备拥有多个功能,那就需要多个接口构成。
主机与usb设备通信方式:
主机首先要发送请求给设备,设备才能给出相应的响应。
usb设备驱动(发起请求的源,发起usb请求)->usb核心->usb主控制器驱动(使用usb总线把指令、信息发给usb设备)
usb设备响应指令(返回信息)usb总线->usb主控制器->usb核心->usb设备驱动
usb请求信息使用urb结构体描述:
USB请求块( USB request block-URB)是USB设备驱动中用来与USB设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的sk_buff结构体, 是USB主机与设备通信的“电波”。
urb使用步奏:
1. USB 设备驱动程序创建并初始化一个访问特定端点的 urb,并提交给USB core;
2. USB core提交该urb到 USB主控制器驱动程序;
3. USB 主控制器驱动程序根据该urb描述的信息,来访问 USB设备;
4. 当设备访问结束后,USB 主控制器驱动程序通知USB 设备驱动程序。
创建urb:
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
参数:
iso_packets: urb所包含的等时数据包的个数,创建非批量urb填写0。
mem_flags:内 存分配标识(如GFP_KERNEL), 参考kmalloc。
对于批量urb,使用 usb_fill_bulk_urb函数来初始化
对于控制urb,使用 usb_fill_control_urb函数来初始化
对于等时urb, 只能手动地初始化urb。
static inline void usb_fill_int_urb(struct urb *urb, //待初始化的urbstruct usb_device *dev, //urb所要访问的设备unsigned int pipe, //要访问的端点所对应的管道,void *transfer_buffer, //保存传输数据的bufferint buffer_length, //buffer长度usb_complete_t complete_fn, //urb完成时调用的函数void *context, //赋值到urb->context的数据int interval) //urb被调度的时间间隔
提交urb:
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
参数:
urb: 要提交urb的指针
mem_flags: 内存分配标识(如GFP_KERNEL), 参考kmallocURB被提交到 USB核心后, USB核心指定usb主控制器驱动程序来处理该urb, 处理完之后, urb完成函数将被调用。
在Linux内核中,使用 struct usb_driver结构描述一个USB驱动。
struct usb_driver {const char *name; /*驱动程序名 *//* 当 USB核心发现了 该驱动能够处理的 USB接口时, 调用该函数 */int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);/* 当相应的 USB接口被移除时, 调用该函数 */void (*disconnect) (struct usb_interface *intf);/* USB驱动能够处理的设备列表 */const struct usb_device_id *id_table;}
struct usb_device_id *id_table:保存这个驱动所支持的设备的ID,由制造商ID和编号ID构成,查看usb设备的两个ID号使用lsusb命令查看,前一个为制造商ID后一个为编号ID。当有usb加载时,驱动首先会检查是否有usb设备的设备ID与usb驱动的id_table里面的设备ID相同,如果有则调用此驱动的probe函数。
当设备拔出的时候就会调用disconnect函数。
HID协议:HID(Human Interface Device), 属于人机交互类的设备,如USB鼠标, USB键盘, USB游戏操纵杆等。 该类设备必须遵循HID设计规范。
USB下载线驱动设计步奏:
1、实现struct usb_driver结构体:
struct usb_driver dnw_driver = {.name= "dnw", /* 驱动名 */.probe= dnw_probe, /* 捕获函数 */.disconnect= dnw_disconnect, /* 卸载函数 */.id_table= dnw_id_table, /* 设备列表 */};2、使用lsusb命令查看usb设备号,并实现id_table参数:
static struct usb_device_id dnw_id_table [] = {{ USB_DEVICE(0x5345, 0x1234) },{ }};3、实现probe函数:
3.1、(应用程序需要读写操作)初始化字符设备,usb总线的字符设备用struct usb_class_driver结构体和usb_register_dev函数创建,与一般字符设备创建函数cdev实现效果一样:
static struct usb_class_driver dnw_class = {.name ="secbulk%d",/*创建的设备名,打开设备时的名字*/.fops =&dnw_ops,/*设备的操作函数集名字*/.minor_base =100,/*需要创建的设备的次设备号,从100开始*/};
static struct file_operations dnw_ops ={.owner =THIS_MODULE,.write =dnw_write,.open =dnw_open,.release =dnw_release,};
3.2、创建usb设备struct usb_device *udev,并利用probe函数中的struct usb_interface *intf接口信息转换成usb对对应的设备,转换函数:interface_to_usbdev,所需参数为struct usb_interface *intf,再使用usb_get_dev包含interface_to_usbdev函数,返回值为usb设备。
利用struct usb_interface *intf获取端点地址:
3.3.1、创建接口描述:
struct usb_host_interface *interface;
3.3.2、获取当前接口设置:
interface = intf->cur_altsetting;
3.3.3、利用接口设置拿到接口描述符,在接口描述符里面就有端点的数量:
端点描述符初始化:struct usb_endpoint_descriptor *endpoint;
interface->desc.bNumEndpoints接口下端点的数量,利用for循环找出需要的端点,使用usb_endpoint_is_bulk_out函数判断端点是否为bulk_out端点。
static int dnw_probe(struct usb_interface *intf, const struct usb_device_id *id){ /* 接口设置描述 */ struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; int i; interface = intf->cur_altsetting; for(i=0;i<interface->desc.bNumEndpoints;i++) { endpoint = &interface->endpoint[i].desc; if(usb_endpoint_is_bulk_out(endpoint)) { bulk_out_endaddr = endpoint->bEndpointAddress; break; } } usb_register_dev(intf,&dnw_class); udev = usb_get_dev(interface_to_usbdev(intf));}
4、实现write函数:
4.1、(分配urb、初始化urb、提交urb)可以使用usb_bulk_msg函数代替之前三个步奏
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)函数参数说明
第一个参数:usb设备名;
第二个参数:usb管道;
usb创建管道函数有usb_sndbulkpipe、和usb_rcvbulkpipe两种,分别是创建发送和接收函数
这两个函数的第一个参数为设备名,第二个参数为端点地址
第三个参数:需要发送的数据;
第四个参数:需要发送的数据的数量;
第五个参数:(返回的)实际传输的数据数量
第六个参数:时间间隔
4.2、(下载为大数据传输)usb端点选择为批量型
static ssize_t dnw_write(struct file *file, const char __user *buf, size_t len, loff_t *pos){ size_t to_write; size_t total_write = 0; size_t act_len; while(len>0) { to_write = min(len,(size_t)BULKOUT_BUFFER_SIZE); copy_from_user(bulkout_buffer,buf+total_write,to_write);usb_bulk_msg(udev,usb_sndbulkpipe(udev,bulk_out_endaddr),bulkout_buffer,to_write,&act_len,3*HZ);len -= to_write;total_write += to_write; } return total_write;}
示例代码:
#include <linux/module.h>#include <linux/kernel.h>#include <linux/usb.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/slab.h>#define BULKOUT_BUFFER_SIZE 512char *bulkout_buffer;struct usb_device *udev;__u8 bulk_out_endaddr;static struct usb_device_id dnw_id_table [] = {{ USB_DEVICE(0x5345, 0x1234) },{ }};static int dnw_open(struct inode* inode, struct file *file){ bulkout_buffer = kmalloc(BULKOUT_BUFFER_SIZE,GFP_KERNEL); return 0;}static int dnw_release (struct inode* inode, struct file *file){ kfree(bulkout_buffer); return 0;}static ssize_t dnw_write(struct file *file, const char __user *buf, size_t len, loff_t *pos){ size_t to_write; size_t total_write = 0; size_t act_len; while(len>0) { to_write = min(len,(size_t)BULKOUT_BUFFER_SIZE); copy_from_user(bulkout_buffer,buf+total_write,to_write);usb_bulk_msg(udev,usb_sndbulkpipe(udev,bulk_out_endaddr),bulkout_buffer,to_write,&act_len,3*HZ);len -= to_write;total_write += to_write; } return total_write;}static struct file_operations dnw_ops ={.owner =THIS_MODULE,.write =dnw_write,.open =dnw_open,.release =dnw_release,};static struct usb_class_driver dnw_class = {.name ="secbulk%d",.fops =&dnw_ops,.minor_base =100,};static int dnw_probe(struct usb_interface *intf, const struct usb_device_id *id){ /* 接口设置描述 */ struct usb_host_interface *interface; struct usb_endpoint_descriptor *endpoint; int i; interface = intf->cur_altsetting; for(i=0;i<interface->desc.bNumEndpoints;i++) { endpoint = &interface->endpoint[i].desc; if(usb_endpoint_is_bulk_out(endpoint)) { bulk_out_endaddr = endpoint->bEndpointAddress; break; } } usb_register_dev(intf,&dnw_class); udev = usb_get_dev(interface_to_usbdev(intf));}static void dnw_disconnect(struct usb_interface *intf){usb_deregister_dev(intf,&dnw_class);}struct usb_driver dnw_driver = {.name= "dnw", /* 驱动名 */.probe= dnw_probe, /* 捕获函数 */.disconnect= dnw_disconnect, /* 卸载函数 */.id_table= dnw_id_table, /* 设备列表 */};int dnw_init(){ usb_register(&dnw_driver); return 0;}void dnw_exit(){ usb_deregister(&dnw_driver);}module_init(dnw_init);module_exit(dnw_exit);MODULE_LICENSE("GPL");
- Linux_USB驱动程序设计学习
- 我想学习驱动程序设计
- linux_usb驱动
- 事件驱动程序设计学习笔记
- 字符驱动程序设计学习笔记4-1
- 字符驱动程序设计学习笔记4-2
- Java学习笔记----事件驱动程序设计
- 嵌入式系统开发学习(三)--驱动程序的设计
- ok6410学习笔记(18.linux串口驱动程序设计)
- ok6410学习笔记(19.块设备驱动程序设计)
- ok6410学习笔记(18.linux串口驱动程序设计)
- java丨事件驱动程序设计学习笔记(一)
- java丨事件驱动程序设计学习笔记(二)
- linux驱动程序设计
- Win2000驱动程序设计初步
- Linux设备驱动程序设计
- WDM驱动程序设计
- Linux设备驱动程序设计
- java 通过元数据改写jdbcUtil访问jar
- Eclipse搭建Spring开发环境
- C++ Primer 第五版 练习9.52 解答
- 待重写
- poj 2632
- Linux_USB驱动程序设计学习
- CSS3结构性伪类
- 面试题2:实现Singleton
- 计蒜客 难题题库 027 三值排序
- 快快快!27个提升效率的iOS开源库推荐
- 读书观点
- atitit.html编辑器的设计要点与框架选型 attilax总结
- gstreamer ugly插件x264enc不能编译的原因
- poj1573