linux usb驱动
0.usb协议
usb的版本: 硬件
usb 1.0 OHCI 微软 硬件 > 软件
usb 1.1 UHCI intel 软件 > 硬件
usb 2.0 EHCI intel
usb 3.0 XHCI intel
12mpbs/s 480mpbs/s 5gbps/s 10gbps/s
usb传输类型:
(0)控制传输:在usb设备插入主机时,主机与usb通信获取usb设备信息进行相关资源分配,配置时;
(1)中断传输:数据传输量小,响应速度快,usb鼠标;【只是名称为中断,实际上其内部设置会有一个轮询时间,不断的去查询鼠标状态,是否有数据可上报】
(2)实时传输:数据量大,数据不可靠,其实时性还是比较强,usb摄像头;
(3)批量传输:数据量大,但是数据可靠,无实时性的限制:u盘;
传输--->事务---->包---->域
事务:输入(IN)事务处理、输出(OUT)事务处理、设置(SETUP)
包:USB协议定义在总线上传输的基本单位是包
1、令牌包阶段:启动一个输入、输出或设置的事务; start
2、数据包阶段:按输入、输出发送相应的数据; data
3、握手包阶段:返回数据接收情况,在同步传输的IN和OUT事务中没有这个阶段。 ack
域:同步字域(SYNC)、包标识符域(PID)、数据域、循环冗余校验域(CRC)和包结尾域(EOP),
usb端点是usb设备中的数据收发最小单元,usb数据传输的目的地/数据源是端点;
usb设备中除了端点 0 之外(in+out),其余的端点有且只有一个方向(in/out);
pipe:是usb主机 与 usb设备端点 之间通信的通道;
usb设备枚举过程:
(1)usb设备 --插入--> 主机 --->硬件 (主机 5v d-(15k--->gnd) d+(15k-->gnd) gnd)
(设备 5v d-(15k--->vcc) d+(15k-->vcc) gnd)
(2)usb设备上电:[1]主机供电 [2]外部独立供电
(3)usb设备reset:usb设备先复位,之后主机才能与设备通信;
(4)usb设备进入默认状态:主机与设备通信,首先该usb设备使用addr 0,端点0,控制管道与
主机进行通信,usb主机分配新地址;
(5)usb设备使用新地址:主机通过该新地址与usb设备进行通信,获取设备的相关信息;
(6)根据获取到的设备信息进行相关配置工作;
(7)如果有数据与该设备进行交互,则通信,反之挂起设备进入省电模式;
RZ编码(return zero)
正电平代表逻辑1,负电平代表逻辑0,每传输完一位数据,信号归0。[自同步]
NRZ编码(no return zero)
正电平代表逻辑1,负电平代表逻辑0,每传输完一位数据,信号不归0。
NRZI编码(no return zero in)
数据为0电平翻转,数据为1电平不翻转。
1.usb总线
1.1 传输模式
1.2 主机规范
2.linux下的usb框架
总线: 设备 驱动
usb struct bus_type usb_bus_type struct usb_device struct usb_driver
i2c struct bus_type i2c_bus_type struct i2c_client struct i2c_driver
platform struct bus_type platform_bus_type struct platform_device struct platform_driver
1.4 usb设备描述符
usb设备在逻辑上分成几个层次:
usb硬件连接:
[1.5.1]usb设备描述符:设备描述符给出了usb设备的一般信息。
[1.5.2]配置描述符
配置描述符给出了usb设备的配置信息,在一个配置下,一个端点不会再接口之间共享,
除非端点被统一接口的不同配置使用;
[1.5.3]接口描述符
[1.5.4]端点描述符
1.4 nrzi编码
1.0usb总线框架
app: open read write
-------------------------------------
kernel:
usb的设备驱动:struct usb_driver
drv_open drv_read drv_write ...
------------------------------
usb core:
提供结构体 + 注册注销函数 + 传输方法 + 设备识别
------------------------------
usb主机控制器驱动:struct hc_driver
-------------------------------------
hardware:
usb主机控制器:struct usb_hcd
------------------------------
usb的设备:struct usb_device
--------------------------------------
2.2 usb驱动
(2)总线
usb.c--->module--->装载
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
subsys_initcall(usb_init);
usb_init
retval = bus_register(&usb_bus_type);
#define subsys_initcall(fn) __define_initcall(fn, 4) //__define_initcall(usb_init, 4)
176 #define __define_initcall(usb_init, 4) \
177 static initcall_t __initcall_##usb_init##4 __used \
178 __attribute__((__section__(".initcall" #4 ".init"))) = usb_init
.initcall4.init = usb_init
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)
vmlinux.lds
(1)驱动结构体
struct usb_driver {
const char *name;/名称
int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);//设备与驱动匹配上自动执行
void (*disconnect) (struct usb_interface *intf);//当总线上设备或驱动任意一方移除时,自动执行
const struct usb_device_id *id_table;//usb驱动所支持的设备列表
};
(1)生成匹配设备指定<制造商ID + 产品ID>的usb_dev_id
USB_DEVICE(vendor , product);
/*
#define USB_DEVICE(vend, prod) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
.idVendor = (vend), \
.idProduct = (prod)
#define USB_DEVICE_ID_MATCH_DEVICE \
(USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
*/
(2) 生成匹配设备指定<制造商ID + 产品ID + 产品版本号最值区间>的usb_dev_id
USB_DEVICE_VER(vendor , product , lo ,hi);
(3)生成匹配设备指定<类 + 子类 + 协议>的usb_dev_id
USB_DEVICE_INFO(class , subclass , protocol);
(4)生成匹配接口指定<类 + 子类 + 协议>的usb_dev_id
USB_INTERFACE_INFO(class , subclass , protocol);
#define usb_register(driver) \
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
int usb_register(struct usb_driver *new_driver)
功能:usb_driver的注册
参数:new_driver:usb_driver的指针对象
返回值:成功: 0 失败:-ERRNO
void usb_deregister(struct usb_driver *driver)
功能:usb_driver的注销
参数:driver:usb_driver的指针对象
返回值:无
static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf)
功能:根据struct usb_interface获取struct usb_device
参数:intf:struct usb_interface
返回值:成功:struct usb_device指针对象 失败:NULL
想写一个usb鼠标的驱动:/driver/usb/usbmouse.c
流程:
(0)将该鼠标驱动程序对应的配置项删除
Device Drivers --->
HID support --->
USB HID support --->
<*> USB HID transport layer //该项“N”
cp arch/arm/boot/uImage ~/tftpboot
(1)模块三要素
(2)结构体填充
(3)注册注销
(3)设备
struct usb_device {
int devnum;//当前usb的设备地址编号
struct usb_bus *bus;//该设备挂载到的usb总线对象
struct usb_host_endpoint ep0;//usb设备的端点0
struct usb_device_descriptor descriptor;//usb设备描述符---->
struct usb_host_config *config;//usb主机配置【当前usb设备的若干种功能的集合】
struct usb_host_endpoint *ep_in[16];//usb 设备的输入端点集合
struct usb_host_endpoint *ep_out[16];////usb 设备的输出端点集合
struct device dev;//linux设备模型相关的
};
配置:
struct usb_host_config {
struct usb_config_descriptor desc;//配置描述符
struct usb_interface *interface[USB_MAXINTERFACES];//接口:具体的功能
};
接口:
struct usb_interface {
struct usb_host_interface *altsetting;//未使用的接口
struct usb_host_interface *cur_altsetting;//正在使用的接口
unsigned num_altsetting;//当前正在使用的接口的数量
int minor; //次设备号
struct device dev; //linux设备模型对象
};
设置:
struct usb_host_interface {
struct usb_interface_descriptor desc;//接口描述符
struct usb_host_endpoint *endpoint; //端点
};
端点:
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
};
lsusb -v:将系统中的所有usb设备的描述信息打印出来
创建pipe:
1、创建控制pipe(根据传输类型 以及 传输方向(针对于主机而言)的不同而不同 )
usb_sndctrlpipe(dev, endpoint)
usb_rcvctrlpipe(dev, endpoint)
2、创建中断pipe
usb_sndintpipe(dev, endpoint)
usb_rcvintpipe(dev, endpoint)
3、创建批量pipe
usb_sndbulkpipe(dev, endpoint)
usb_rcvbulkpipe(dev, endpoint)
4、创建等时pipe
usb_sndisocpipe(dev, endpoint)
usb_rcvisocpipe(dev, endpoint)
填充urb:
(1)使用usb_fill_control_urb函数初始化与控制端点通信的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,
);
(2)使用usb_fill_int_urb函数初始化与中断端点通信的urb。
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,
void *context,
int interval
);
(3)使用usb_fill_int_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,
void *context,
int interval
)
(4)等时传输的urb的填充采用固定步骤,具体参考相关数据设置
void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags,dma_addr_t *dma)
功能:给主机接收数据分配相关的地址空间
参数:dev:struct usb_device 的对象
size:分配空间的大小
mem_flags:分配空间的标志 GFP_KERNEL
dma:该返回值所对应的物理地址
返回值:成功:分配空间的首地址 失败: NULL
struct urb *usb_alloc_urb(int iso_packets, int mem_flags);
功能:分配struct urb结构体
参数:
iso_packet:urb包含的同步报文的数目。如果不是同步urb,设置为0,表示不创建等时数据包。
mem_flags: 内核分配内存的标志类型。
成功返回:urb的首地址 失败返回:NULL
释放urb结构体的函数:
void usb_free_urb(struct urb *urb);
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
功能:提交urb
参数:urb:待提交的urb对象
mem_flags:该urb所占内存空间的标志GFP_KERNEL
返回值:成功:0 失败: -errno
3.usb请求块
(1)urb结构体:
(2)urb相关函数
申请释放urb:
填充urb:
填充urb函数中关于pipe参数的设置:
提交urb
取消urb函数
4.usb骨架程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/mod_devicetable.h>
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/bitops.h>
#include <linux/gfp.h>
/*
流程:
(1)模块三要素
(2)结构体填充
(3)注册注销
(4)硬件相关的操作
[4.1]因为usb鼠标属于输入设备,其需要提供上报的数据 经过 usb 数据线 传输出去 故此时需要提供input 的数据
input_dev申请 + 填充 + 注册 + 注销
[4.2]usb相关
[4.2.1]得到通信的端点(usb 设备中)
[4.2.2]得到pipe (不同的传输类型的管道是不一样的 , 管道有传输方向)
[4.2.3]主机为了接收端点传进来的数据 需要分配一定的空间
[4.2.4]urb:申请到urb + fill + submit + 收到urb后再进行相关的数据解析
*/
struct usb_device *usb_key_dev = NULL;
struct input_dev *usb_input_dev = NULL;
char *usb_master_buf = NULL;
dma_addr_t usb_buf_phy;
struct urb *my_urb = NULL;
int length = 0;
void usb_key_complete(struct urb *urb)
{
int i = 0;
for(i = 0; i < length ; i++){
printk("usb_master_buf[%d]:%d\t",i ,usb_master_buf[i] );
}
printk("\n");
usb_submit_urb(my_urb,GFP_KERNEL);
}
int usb_key_probe(struct usb_interface *intf,const struct usb_device_id *id)
{
int ret = 0;
int pipe = 0;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
usb_key_dev = interface_to_usbdev(intf);
if(usb_key_dev == NULL){
printk("%s,%d usb_key_dev fail...\n",__func__,__LINE__);
return -ENOMEM;
}
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
/*input相关*/
usb_input_dev = input_allocate_device();
if(usb_input_dev == NULL){
printk("input_allocate_device ...\n");
return -ENOMEM;
}
//type
set_bit( EV_KEY, usb_input_dev->evbit);
set_bit( EV_REL, usb_input_dev->evbit);
//code
set_bit( KEY_L,usb_input_dev->keybit );
set_bit( KEY_R,usb_input_dev->keybit );
set_bit( KEY_ENTER,usb_input_dev->keybit);
ret = input_register_device(usb_input_dev);
if(ret < 0){
printk("input_register_device fail\n");
return -EINVAL;
}
/*usb相关 1. 确定数据传输的源 + 目的 + 长度*/
pipe= usb_rcvintpipe(usb_key_dev, endpoint->bEndpointAddress);
length = endpoint->wMaxPacketSize;
usb_master_buf = usb_alloc_coherent(usb_key_dev,length,GFP_KERNEL,&usb_buf_phy);
if(usb_master_buf == NULL){
printk("usb_alloc_coherent fail\n");
return -ENOMEM;
}
/*usb 数据的传输准备: 分配urb + 填充urb (跟传输类型相关)+ 提交urb*/
my_urb = usb_alloc_urb(0,GFP_KERNEL);
if(my_urb == NULL){
printk("usb_alloc_urb fail\n");
return -ENOMEM;
}
usb_fill_int_urb(my_urb,usb_key_dev,pipe,usb_master_buf,length,usb_key_complete,NULL,endpoint->bInterval);
my_urb->transfer_dma = usb_master_buf;
my_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_submit_urb(my_urb,GFP_KERNEL);
printk("%x,%x\n",usb_key_dev->descriptor.idVendor,usb_key_dev->descriptor.idProduct);//[ 21.545000] 93a,2510
printk("%s,%d\n",__func__,__LINE__);
return 0;
}
void usb_key_disconnect(struct usb_interface *intf)
{
usb_kill_urb(my_urb);
usb_free_urb(my_urb);
usb_free_coherent(usb_key_dev,length,usb_master_buf,usb_buf_phy);
input_unregister_device(usb_input_dev);
input_free_device(usb_input_dev);
printk("%s,%d\n",__func__,__LINE__);
}
const struct usb_device_id usb_key_table[] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE)},
//{USB_DEVICE(0x93a , 0x2510)},//匹配指定的厂商ID 产品ID为什么没有实现该功能?
};
struct usb_driver usb_key_drv = {
.name = "usb_key",
.probe = usb_key_probe,
.disconnect = usb_key_disconnect,
.id_table = usb_key_table,
};
static int __init usbkey_init(void)
{
int ret = 0;
ret = usb_register(&usb_key_drv);
if(ret < 0){
printk("%s,%d usb_register fail\n",__func__,__LINE__);
return -EINVAL;
}
printk("%s,%d\n",__func__,__LINE__);
return 0;
}
static void __exit usbkey_exit(void)
{
usb_deregister(&usb_key_drv);
printk("%s,%d\n",__func__,__LINE__);
}
module_init(usbkey_init);
module_exit(usbkey_exit);
MODULE_LICENSE("GPL");