USB设备驱动

1、USB设备

类型:

UHCI 低速/全速
OHCI 低速/全速
EHCI  高速(480Mbps)

设备插入识别:

在USB设备内部有四根线与主机端相连,分别是:VCC、GND、D+、D-
在USB端D+或者D-端会接有一个上拉电阻,阻值大概在1.5K左右
在主机端的D+与D-会接有下拉电阻,阻值在15K左右
因此在USB设备接入主机之后,由于上拉电阻的作用会导致主机端的D+或者D-电平拉高,此时在主机端会产生一个中断,主机以此识别USB设备的插入

设备类型识别:

在USB会有一个或者多个设备描述符,在描述符里面会有一个或者多个逻辑设备,在逻辑设备里面会有一个或者多个端点
逻辑设备:比如声卡可以分为录音和播放;比如网卡可以有多个虚拟网卡设备
在USB接入主机之后,主机默认其为0号编号,然后通过编号0进行通信

数据传输:

USB设备的数据传输都是由主机主动发出请求,USB设备没有通知主机进行数据传输的能力。主机查询

端点(endpoint):USB设备的通信是通过端点进行的,除了端点0之外每个端点只有读或者写的能力。端点类似于TCP/IP协议的端口号。

中断传输并不是真的中断,而是主机不断的进行查询

中断传输 鼠标
批量传输 U盘
实时传输 USB摄像头
控制传输 USB设备的识别过程

2、USB驱动程序

USB驱动程序是总线型的驱动
总线驱动程序作用: 识别USB设备;安装设备驱动程序;提供USB读写函数
框架:APP->设备驱动程序->总线驱动程序->设备(由上到下)
设备的识别:
usb 1-1: new low speed USB device using s3c2410-ohci and address 5
usb 1-1: configuration #1 chosen from 1 choice
设备驱动程序的安装(注册为输入子系统设备):
input: PixArt Lenovo USB Optical Mouse as /class/input/input1
input: USB HID v1.11 Mouse [PixArt Lenovo USB Optical Mouse] on usb-s3c24xx-1

3、USB驱动编写

构建usb_driver结构体(类似于platform_driver结构体/平台总线设备驱动)

static struct usb_driver usb2key_driver = {
	.name =		"usb2key",				//名字
	.probe =	usb2key_probe,			//成功匹配时执行
	.disconnect =	usb2key_disconnect,	//移除的时候执行,对应平台总线的.remove
	.id_table =	usb2key_id_table,		//设备标识,能支持哪些设备
};

构建模块装载卸载入口函数

static int usb2key_init(void)
{
	usb_register(&usb2key_driver);

	return 0;
}

static void usb2key_exit(void)
{
	usb_deregister(&usb2key_driver);
}

module_init(usb2key_init);
module_exit(usb2key_exit);
MODULE_LICENSE("GPL");

构建probe与disconnect函数,还有id_table

static int usb2key_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *endpoint;

	int pipe, maxp;
	int error = -ENOMEM;
	/* 通过逻辑设备接口结构体获得usb_device的地址 */
	usb_dev = interface_to_usbdev(intf);
	/* 最近活动的备选设置 */
	interface = intf->cur_altsetting;
	/* 只有一个端点 */
	if (interface->desc.bNumEndpoints != 1)
		return -ENODEV;
	/* 获取除了端点0之外的第一个端点描述符,USB鼠标没有输出端点,只有输入端点(相对于主机来说) */
	endpoint = &interface->endpoint[0].desc;
	if (!usb_endpoint_is_int_in(endpoint))	//中断类型端点、输入端点
		return -ENODEV;
	/* 端点地址以及最大传输包 */
	pipe = usb_rcvintpipe(usb_dev, endpoint->bEndpointAddress);
	maxp = usb_maxpacket(usb_dev, pipe, usb_pipeout(pipe));

	/* USB设备传输数据存放地址(虚拟地址) */
	usb_data = usb_buffer_alloc(usb_dev, 8, GFP_ATOMIC, &usb_dma);
	/* USB request block:USB请求块,0表示不需要私有空间,后者表示是内核空间 */
	usb2key_irq = usb_alloc_urb(0, GFP_KERNEL);

	usb2key_dev->name = "usb2key";
	usb_to_input_id(usb_dev, &usb2key_dev->id);	//输入id_table
	usb2key_dev->dev.parent = &intf->dev;
	/* 填充urb结构体:usb2key_irq-要填充的urb;pipe-端点地址;
	 *(maxp > 8 ? 8 : maxp)-数据长度;usb2key_mouse_irq-中断处理函数;  
	 *endpoint->bInterval-查询时间间隔
	 */
	usb_fill_int_urb(usb2key_irq, usb_dev, pipe, usb_data, (maxp > 8 ? 8 : maxp), usb2key_mouse_irq, NULL, endpoint->bInterval);
	usb2key_irq->transfer_dma = usb_dma;	//传输数据的物理地址
	usb2key_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;	//物理地址在传输时有效

	usb_submit_urb(usb2key_irq, GFP_KERNEL);	//提交urb到内核
	
	return 0;
}

void usb2key_disconnect(struct usb_interface *intf)
{
	input_unregister_device(usb2key_dev);
	usb_kill_urb(usb2key_irq);
	usb_free_urb(usb2key_irq);
	input_free_device(usb2key_dev);
	usb_buffer_free(usb_dev, 8, &usb_data, usb_dma);
}

/* 标识支持HID类(Human Interface Device),BOOT子类, MOUSE协议*/
static struct usb_device_id usb2key_id_table[] = {
    { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
		 USB_INTERFACE_PROTOCOL_MOUSE)},
    { }						/* 结束输入 */
};

构建中断处理函数(有数据的时候会产生中断)

static void usb2key_mouse_irq(struct urb *urb)
{
	int i;
	/* 打印接收到的数据 */
	for(i = 0; i < 8; i++)
	{
		printk("%02x\t", usb_data[i]);
	}
	/* 重新提交urb */
	error = usb_submit_urb(usb2key_irq, GFP_ATOMIC);
}

0002 0008 ffff ffff

0000 0000 0000 0000

鼠标下滚

0002 0008 0001 0000

0000 0000 0000 0000

上滚

0001 0110 0001 0000 

0000 0000 0000 0000

左键按下

0001 0110 0000 0000

0000 0000 0000 0000

左键松开

4、模块分析

设备的识别:

usb 1-1: new low speed USB device using s3c2410-ohci and address 5
usb 1-1: configuration #1 chosen from 1 choice

在hub.c里面:
usb_hub_init(void)
	usb_register(&hub_driver)	//注册hub_driver结构体
	kthread_run(hub_thread, NULL, "khubd");	//线程运行
	
hub_thread(void *__unused)
	hub_events();
		hub_port_connect_change(hub, i, portstatus, portchange);
			udev = usb_alloc_dev(hdev, hdev->bus, port1);
			hub_port_init(hub, udev, port1, i);
				//打印
				//usb 1-1: new low speed USB device using s3c2410-ohci and address 5
				//usb 1-1: configuration #1 chosen from 1 choice
			usb_new_device(udev);
				usb_control_msg	//询问设备
				MKDEV(USB_DEVICE_MAJOR, (((udev->bus->busnum-1) * 128) + (udev->devnum-1)));	//创建节点
				device_add(&udev->dev);	//添加设备,此时会由usb_bus_type的match函数查找到上面建立的driver进行匹配连接,并且传入设备描述
	wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list) || kthread_should_stop());	//等待中断,阻塞

hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
	hub_configure(hub, endpoint)
		usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval); //构建出hub_irq
hub_irq(struct urb *urb)
	kick_khubd(hub);
		wake_up(&khubd_wait);	//唤醒中断

USB驱动注册

usb_register(&usb2key_driver);
	new_driver->drvwrap.driver.bus = &usb_bus_type;	//总线类型
	new_driver->drvwrap.driver.probe = usb_probe_interface;	//驱动的probe函数
	new_driver->drvwrap.driver.remove = usb_unbind_interface;	//驱动的remove函数
	new_driver->drvwrap.driver.owner = owner;		//THIS_MODULE
	driver_register(&new_driver->drvwrap.driver);	//注册driver结构体,把此结构体挂入总线的链表里面,并且总线的match函数被调用
	

调用到usb设备驱动

Usb device connect!
usb2key_probe(struct usb_interface *intf, const struct usb_device_id *id)
	usb_fill_int_urb(usb2key_irq, usb_dev, pipe, usb_data, (maxp > 8 ? 8 : maxp), usb2key_mouse_irq, NULL, endpoint->bInterval);
static void usb2key_mouse_irq(struct urb *urb)	//等待数据传输

整体过程为:

1、由 usb_hub_init(void)函数创建 hub_driver(hub_driver里面包含有 hub_probe,hub_disconnect等函数);运行 hub_thread线程,线程里面查询 hub_event事件,没有就休眠;
2、在注册 hub_driver之后,由于系统里面会有对应的设备,调用hub设备总线的match函数之后就会找到相应的设备,然后调用到 hub_probe函数
3、 hub_thread线程先查询事件,如果没有退出,然后在 wait_event_interrupt处阻塞,等待被唤醒
4、在 hub_probe里面调用 hub_configure,在 hub_configure里面填充hub的urb结构体,并且构建 hub_irp函数,定时查询
5、当有USB设备插入的时候会中断数据产生,这个是硬件层面的,然后调用到 hub_irq,此时调用到 kick_khubd里面的 wake_up对上面的阻塞进行唤醒,同时重新提交hub的urb,等待新设备插入
6、执行 hub_event,调用 hub_port_connect_change里面的usb设备构建函数进行新插入设备对应的设备结构体构建,然后添加新设备
7、设备添加之后就会调用到前面的 usb_driver中的probe函数,完成usb的urb的构建
8、 usb_irq的中断查询函数等待数据传输的到来

5、整体介绍

两个总线设备驱动
hub总线:负责识别设备的插入并且注册添加相应的设备结构体,hub设备已经存在于主机上

usb总线:负责进行USB设备数据的处理,USB的设备结构体是在USB设备插入之后由hub总线的驱动获取USB设备信息之后才构建好的


usb总线的设备分支由hub总线的驱动构造

hub总线由编译器编译进内核,直接由内核构造

你可能感兴趣的:(linux,usb,ARM,s3c2440)