赞
踩
(1)控制传输(control)
是每一个USB设备必须支持的,通常用来获取设备描述符、设置设备的状态等等。一个USB设备从插入到最后的拔出这个过程一定会产生控制传输(即便这个USB设备不能被这个系统支持)。
(2)中断传输(interrupt)
支持中断传输的典型设备有USB鼠标、 USB键盘等等。中断传输不是说usb设备真正发出一个中断,然后主机会来读取数据。它其实是一种轮询的方式来完成数据的通信。USB设备会在设备驱动程序中设置一个参数叫做interval,它是endpoint的一个成员。 interval是间隔时间的意思,表示usb设备希望主机多长时间来轮询自己,只要这个值确定了之后,主机就会周期性的来查看有没有数据需要处理。
(3)批量传输(bulk)
支持批量传输最典型的设备就是U盘,它进行大数量的数据传输,能够保证数据的准确性,但是时间不是固定的。
(4)实时传输(isochronous)
USB摄像头就是实时传输设备的典型代表,它同样进行大数量的数据传输,数据的准确性无法保证,但是对传输延迟非常敏感,也就是说对实时性要求比较高。
USB分为主机侧和设备侧,以PC为例,PC为主机侧,插入的U盘为设备侧,当U盘插入电脑时,PC通过枚举过程,获取设备信息,并加载合适的驱动程序。在这个过程中:
(1)新接入的USB设备的默认地址(编号)是0,在未分配新编号前,PC主机使用0地址和它通信。当分配了地址后,以后通信就用分配的地址。
(2)所有的USB传输,都是从USB主机这方发起;USB设备没有"主动"通知USB主机的能力。
根据电气连接的不同,区分主机侧和设备侧。
区分低速设备和全速设备:
全速设备端,D+ 线上接上拉电阻 1.5K 欧姆 ±5%
低速设备端接,D- 线上接上拉电阻 1.5K 欧姆 ±5%
USB主机控制器规范:OHCI、UHCI、EHCI等
1)usb设备驱动:usb设备如何与主机通信。
2)usb核心:usb驱动管理和协议处理。
3)usb主机控制器:控制插入其中的USB设备,与硬件交互。
《Linux设备驱动开发详解》:
USB设备的逻辑组织中,包含设备、配置、接口和端点4个层次。每个USB设备的都提供不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需要从其中选定一个),配置由多个接口组成。
在USB协议中,接口由多个端点组成,代表一个基本的功能,是USB设备驱动程序控制的对象,一个功能复杂的USB设备可以具有多个接口。设备接口是端点的汇集(Collection)。例如,USB扬声器可以包含一个音频接口以及对旋钮和按钮的接口。一个配置中的所有接口可以同时有效,并可被不同的驱动程序连接。每个接口可以有备用接口,以提供不同质量的服务参数。
端点是USB通信的最基本形式,每一个USB设备接口在主机看来就是一个端点的集合。主机只能通过端点与设备进行通信,以使用设备的功能。在USB系统中每一个端点都有唯一的地址,这是由设备地址和端点号给出的。每个端点都有一定的属性,其中包括传输方式、总线访问频率、带宽、端点号和数据包的最大容量等。一个USB端点只能在一个方向上承载数据,从主机到设备(输出端点)或者从设备到主机(输入端点),因此端点可看作是一个单向的管道。端点0
通常为控制端点,用于设备初始化参数等。只要设备连接到USB上并且上电,端点0就可以被访问。端点1、2等一般用作数据端点,存放主机与设备间往来的数据。
设备描述符:关于设备的通用信息,如供应商 ID、产品 ID 和修订 ID,支持的设备类、子类和适用的协议以及默认端点的最大包大小等。
配置描述符:此配置中的接口数、支持的挂起和恢复能力以及功率要求。
接口描述符:接口类、子类和适用的协议,接口备用配置的数目和端点数目。
端点描述符:端点地址、方向和类型,支持的最大包大小,如果是中断类型的端点则还包括轮询频率。
usb总线驱动程序的作用:
1)识别usb
2)查找并安装驱动程序
3)提供usb读写函数
插入usb设备到开发板上,会打印一些log,根据log信息,找到drivers/usb/core/hub.c的hub_port_init函数,看这个函数是如何被调用的
hub_thread --->
hub_events ---->
hub_port_connect_change --->
hub_port_init
hub_thread函数。
static int hub_thread(void *__unused) { set_freezable(); do { hub_events(); wait_event_freezable(khubd_wait, !list_empty(&hub_event_list) || kthread_should_stop()); } while (!kthread_should_stop() || !list_empty(&hub_event_list)); pr_debug("%s: khubd exiting\n", usbcore_name); return 0; }
hub_thread会休眠,等待被唤醒。
哪里被唤醒:kick_khubd
static void kick_khubd(struct usb_hub *hub)
{
unsigned long flags;
/* Suppress autosuspend until khubd runs */
atomic_set(&to_usb_interface(hub->intfdev)->pm_usage_cnt, 1);
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
wake_up(&khubd_wait);
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
当接上USB设备时,产生hub_irq中断,调用kick_khubd,唤醒hub_thread。
static void hub_port_connect_change(struct usb_hub *hub, int port1,
u16 portstatus, u16 portchange)
{
...
udev = usb_alloc_dev(hdev, hdev->bus, port1); //分配usb_device
usb_set_device_state(udev, USB_STATE_POWERED);//设置状态
choose_address(udev); //分配地址
status = hub_port_init(hub, udev, port1, i);//初始化
status = usb_new_device(udev);//创建设备
...
}
(1)usb_alloc_dev
struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) { ... dev = kzalloc(sizeof(*dev), GFP_KERNEL);//分配结构体 .. device_initialize(&dev->dev);//初始化 dev->dev.bus = &usb_bus_type;//usb总线 dev->dev.type = &usb_device_type; dev->dev.groups = usb_device_groups; dev->dev.dma_mask = bus->controller->dma_mask; set_dev_node(&dev->dev, dev_to_node(bus->controller)); dev->state = USB_STATE_ATTACHED; ... }
(2)choose_address
找到要分配的地址,此时还没有分配。
static void choose_address(struct usb_device *udev) { int devnum; struct usb_bus *bus = udev->bus; if (udev->wusb) { devnum = udev->portnum + 1; BUG_ON(test_bit(devnum, bus->devmap.devicemap)); } else { devnum = find_next_zero_bit(bus->devmap.devicemap, 128, bus->devnum_next);//查找非0的编号 if (devnum >= 128)//没找到,再找一次 devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1); bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1); } if (devnum < 128) { set_bit(devnum, bus->devmap.devicemap); udev->devnum = devnum; } }
usb最大支持127个设备。
(3)hub_port_init
static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int retry_counter)
{
...
retval = hub_set_address(udev, devnum); //设置地址
retval = usb_get_device_descriptor(udev, 8);//获取设备描述符,只读了前面8个字节,需要先获取总容量
...
}
hub_set_address设置地址,通过usb_control_msg发送报文给usb设备。
(4)usb_new_device
int usb_new_device(struct usb_device *udev)
{
...
err = usb_configure_device(udev); //把所有的描述符都读出来
...
err = device_add(&udev->dev);//添加设备
...
}
类似platform总线,usb也有总线。当添加设备时,就会调用usb_device_match,与id_table进行匹配,匹配成功则调用probe函数。
以/drivers/hid/usbhid/usbmouse.c中的usb_mouse_id_table为例,就是要设置usb支持的信息。
#define USB_INTERFACE_INFO(cl, sc, pr) \
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
.bInterfaceClass = (cl), \
.bInterfaceSubClass = (sc), \
.bInterfaceProtocol = (pr)
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
上述:USB_INTERFACE_CLASS_HID,接口类型为HID;USB_INTERFACE_SUBCLASS_BOOT,接口子类型为启动设备;USB_INTERFACE_PROTOCOL_MOUSE,接口协议为USB鼠标的协议。如果usb设备与id_table中描述一致,那么就可以支持这个设备。
上述流程:
1)hub_thread–>hub_events,休眠
2)中断–>hub_irq,调用hub_port_connect_change
3)在hub_port_connect_change中依次调用了:usb_alloc_dev、choose_address、hub_port_init、usb_new_device
实验目的:USB鼠标,左键——L,右键——S,滚轮——enter。
步骤:
1)分配/设置usb_driver
2)注册
参考:/drivers/hid/usbhid/usbmouse.c
#include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/init.h> #include <linux/usb/input.h> #include <linux/hid.h> static struct input_dev *uk_dev; static char *usb_buf; static dma_addr_t usb_buf_phys; static int len; static struct urb *uk_urb; static struct usb_device_id usbmouse_as_key_id_table [] = { { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) }, //{USB_DEVICE(0x1234,0x5678)}, { } /* Terminating entry */ }; static void usbmouse_as_key_irq(struct urb *urb) { static unsigned char pre_val; #if 0 int i; static int cnt = 0; printk("data cnt %d: ", ++cnt); for (i = 0; i < len; i++) { printk("%02x ", usb_buf[i]); } printk("\n"); #endif /* USB鼠标数据含义 * data[0]: bit0-左键, 1-按下, 0-松开 * bit1-右键, 1-按下, 0-松开 * bit2-中键, 1-按下, 0-松开 * */ if ((pre_val & (1<<0)) != (usb_buf[0] & (1<<0))) { /* 左键发生了变化 */ input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0)) ? 1 : 0); input_sync(uk_dev); } if ((pre_val & (1<<1)) != (usb_buf[0] & (1<<1))) { /* 右键发生了变化 */ input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1)) ? 1 : 0); input_sync(uk_dev); } if ((pre_val & (1<<2)) != (usb_buf[0] & (1<<2))) { /* 中键发生了变化 */ input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2)) ? 1 : 0); input_sync(uk_dev); } pre_val = usb_buf[0]; /* 重新提交urb */ usb_submit_urb(uk_urb, GFP_KERNEL); } static 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; interface = intf->cur_altsetting; endpoint = &interface->endpoint[0].desc; /* a. 分配一个input_dev */ uk_dev = input_allocate_device(); /* b. 设置 */ /* b.1 能产生哪类事件 */ set_bit(EV_KEY, uk_dev->evbit); set_bit(EV_REP, uk_dev->evbit); /* b.2 能产生哪些事件 */ set_bit(KEY_L, uk_dev->keybit); set_bit(KEY_S, uk_dev->keybit); set_bit(KEY_ENTER, uk_dev->keybit); /* c. 注册 */ input_register_device(uk_dev); /* d. 硬件相关操作 */ /* 数据传输3要素: 源,目的,长度 */ /* 源: USB设备的某个端点 */ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /* 长度: */ len = endpoint->wMaxPacketSize; /* 目的: */ usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys); /* 使用"3要素" */ /* 分配usb request block */ uk_urb = usb_alloc_urb(0, GFP_KERNEL); /* 使用"3要素设置urb" */ usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_irq, NULL, endpoint->bInterval); uk_urb->transfer_dma = usb_buf_phys; uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* 使用URB */ usb_submit_urb(uk_urb, GFP_KERNEL); return 0; } static void usbmouse_as_key_disconnect(struct usb_interface *intf) { struct usb_device *dev = interface_to_usbdev(intf); //printk("disconnect usbmouse!\n"); usb_kill_urb(uk_urb); usb_free_urb(uk_urb); usb_buffer_free(dev, len, usb_buf, usb_buf_phys); input_unregister_device(uk_dev); input_free_device(uk_dev); } /* 1. 分配/设置usb_driver */ static struct usb_driver usbmouse_as_key_driver = { .name = "usbmouse_as_key_", .probe = usbmouse_as_key_probe, .disconnect = usbmouse_as_key_disconnect, .id_table = usbmouse_as_key_id_table, }; static int usbmouse_as_key_init(void) { /* 2. 注册 */ usb_register(&usbmouse_as_key_driver); return 0; } static void usbmouse_as_key_exit(void) { usb_deregister(&usbmouse_as_key_driver); } module_init(usbmouse_as_key_init); module_exit(usbmouse_as_key_exit); MODULE_LICENSE("GPL");
(1)内核
Device Drivers --->
HID Devices
<> USB Human Interface Device (full HID) support
(2)usb 1-1: device descriptor read/64, error -62
UPLLCON没有设置好。根本问题在uboot。但是可以修改内核来弥补。在drivers/usb/host/ohci-s3c2410.c 的函数 s3c2410_start_hc修改如下:
#include <mach/regs-clock.h> //添加的代码 static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd) { struct s3c2410_hcd_info *info = dev->dev.platform_data; unsigned long upllvalue = (0x38<< 12) | (0x02 << 4) | (0x01); // 添加的代码 unsigned long upllvalue1 = (0x38<< 12) | (0x02 << 4) | (0x02); // 添加的代码 dev_dbg(&dev->dev, "s3c2410_start_hc:\n"); __raw_writel(upllvalue, S3C2410_UPLLCON); // 添加的代码 mdelay(20); // 添加的代码 __raw_writel(upllvalue1, S3C2410_UPLLCON);// 添加的代码 mdelay(20); // 添加的代码 clk_enable(usb_clk); mdelay(2); /* let the bus clock stabilise */ clk_enable(clk); if (info != NULL) { info->hcd = hcd; info->report_oc = s3c2410_hcd_oc; if (info->enable_oc != NULL) { (info->enable_oc)(info, 1); } } }
(3)测试
用新的内核,并加载驱动,测试。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。