USB设备初识
USB的接口有4条线(5V、GND、D-、D+),其中PC上的D-和D+都接有15k的下拉电阻,未接入设备时未低电平,USB设备的D-或D+接有1.5k的上拉电阻,USB设备接入PC后,就会把PC上的USB口的D-或D+拉高,通过硬件方式通知PC机有新设备接入,新接入的USB设备的默认编号为0,在未分配新编号前,PC使用0编号和它通信。USB总线驱动程序向USB设备发出命令获取USB设备信息(描述符),USB设备回应返回(描述符)并且语言必须是中文,USB总线驱动程序识别设备后,找到对应设备驱动程序,如果已安装则自动启动,否则提示安装后才能使用。
目前,usb在PC端已发展到3.0,但是在嵌入式系统上主要还是使用2.0版本。
USB通信
USB为主从结构:数据的通信传输都是有USB主机发起,USB从设备不能主动发起请求。
通信方式:主机上的客户机软件通过缓冲区和USB逻辑设备接口中的某一端点进行数据传输,该缓冲区与端点间构成管道pipe。传输类型分为以下几种:
USB传输类型:
- 控制传输:可靠,实时,比如:USB设备的识别过程
- 批量传输:可靠,时间无保证,比如:U盘
- 类中断(主机循环读)传输:可靠,实时,比如:USB鼠标
- 等时传输:不可靠,实时 ,比如:USB摄像头
USB传输对象:端点(endpoint)
- 端点0用于控制传输,可输出可输入
- 非端点0只能支持一个方向的数据传输
所有的输入输出都时基于USB主机的立场说的,每个端点都有传输类型和传输方向,例如鼠标的数据传到主机称为“ 输入端点 ”。
协议对各种传输的最大包长都做了规定,根据具体设备而定,若一个数据包大于最大包长,需要分几次来传输,这些信息在枚举阶段获得(即端点0时未分配编号阶段)
USB驱动框架
USB设备驱动、USB总线驱动、USB主机控制器、USB设备
其中只需要完成USB设备驱动和USB设备与板子的连接信息,其他都在内核完成。
USB主机控制器
分为:UHCI、OHCI、EHCI三种。
UHCI:intel 低速(1.5Mbps)/ 全速(12Mbps)
OHCI:microsoft 低速/高速
EHCI:高速(480Mbps)
该部分实现由SoC厂家负责实现,linux中代表接口的结构为struct usb_interface,里面有一个成员cur_altsetting,指向了主机侧对接口的描述结构struct usb_host_interface,该结构描述了接口中所包含的端点个数和各端点的配置描述符的详细信息,而这些信息都是在枚举过程中获得。
USB总线驱动:
电平跳变产生hub_irq中断,kick_khubd唤醒hub_events等待队列,hub_port_connect_change再次判断是否电平跳变,是则choose_address(udev);给新设备分配编号,hub_port_init初始化USB驱动程序及端点等,hub_set_address把编号告诉USB设备,usb_new_device创建新的设备描述符,usb_get_device_descriptor获取设备描述符,读出所有描述符解析,device_add把device放入dev链表,从bus的driver链表遍历中取出匹配的driver,匹配成功则调用driver的probe函数。
USB设备驱动:
构建usb_driver对象,注册,设置id_table设备匹配信息,完成对应probe、release以及文件I/O接口。
需在make menuconfig中去掉默认支持的usb驱动,在驱动的HID类下,然后重新编译内核。
HID:Human Interface Device的缩写,由其名称可以了解HID设备是直接与人交互的设备,例如键盘、鼠标与游戏杆等。不过HID设备并不一定要有人机接口,只要符合HID类别规范的设备都是HID设备。
设备驱动的编写要点
若设备驱动匹配成功,将给驱动Probe函数传入设备编号以及struct usb_interface描述符对应匹配到的设备连接接口信息。
- 通过接口获取匹配到的设备:interface_to_usbdev
- 通过接口获取与设备通信的第一个端点信息:endpoint = interface->endpoint[0].desc
- 通过设备与该端点信息获取通信管道:pipe = usb_rcvintpipe
- 通过endpoint->wMaxPacketSize获取该设备的最大包长。
- 分配一个URB信息体usb_alloc_coherent,并填充URB中的数据,指定类中断函数(即主机会不停对USB进行读,若有数据发生改变则执行)。
- 在类中断函数中判断数据的变化,根据不同变更采取不同操作。
- usb_submit_urb(urb, GFP_KERNEL); 上交URB给内核,由内核对指定USB设备进行数据交互
- USB中平台匹配共享数据函数,保存data到intf接口:usb_set_intfdata,usb_get_intfdata
- 关于URB的释放,需调用usb_kill_urb和usb_free_urb。usb_kill_urb用于撤销一个提交的URB,等待上一次URB被终止后才返回,避免通信出错发生故障。
10.interruptible_sleep_on可用于等待类中断函数执行的唤醒。
static char *usb_buf; //缓冲区
dma_addr_t usb_buf_phys; //缓冲区的物理地址
static int len; //最大包长
struct urb *uk_urb; //URB信息体
void usbmouse_as_key_irq(struct urb *urb)
{
int i;
static int cnt = 0;
printk("cnt = %d\n",++cnt);
for(i=0;i<len;i++)
{
printk("%d",usb_buf[i]);
}
printk("\n");
//提交URB
usb_submit_urb(urb, GFP_KERNEL);
}
int usbmouse_as_key_probe(struct usb_interface *intf,const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;
//获取主机侧的USB接口
interface = intf->cur_altsetting;
//获取端点
endpoint = &interface->endpoint[0].desc;
//获取管道
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
//获取最大包大小
len = endpoint->wMaxPacketSize;
//申请USB交互信息描述符
usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);
//分配USB request block
uk_urb = usb_alloc_urb(int iso_packets, gfp_t mem_flags);
//填充URB
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf,(len > 8 ? 8 : len),
usbmouse_as_key_irq, NULL, endpoint->bInterval);
}