微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > ARM技术讨论 > 发一个用RP4412开发板调USB的技术文档

发一个用RP4412开发板调USB的技术文档

时间:10-02 整理:3721RD 点击:

这是使用的是RP4412开发板进行调的USB,以下是整理的一些相关的内容,希望可以给大家带来一些作用。

一、USB概述

当 USB设备接上或从USB设备移开的时候,主机启动一个被称作总线标识(bus enumeration)的进程,来标识并管理设备状态的改变,当USB设备接上一个加电端口时,系统当采取以下操作:

USB设备所连的集线器通过其通向主机的状态改变通道向主机汇报本USB设备已连接上。此时,USB设备处于加电状态,它所连接的端口是无效的。

  主机通过询问集线器决定此次状态改变的确切含义。

  主机一旦得知新设备已连上以后,它至少等待100ms以使得插入操作的完成以及设备电源稳定工作。然后主机发出端口使能及复位命令给那个端口。

  集线器将发向端口的复位信号持续10ms。当复位信号撤消后,端口已经有效了。这时USB设备处于缺省状态,并且可从VBUS汲取小于100mA的电能,所有设备寄存器及状态已经被复位,设备可对缺省地址产生响应。

主机给设备分配一个唯一的地址,设备转向编址状态。(Addressstate)。

在USB设备接受设备地址之前,它的缺省控制通道(Default Control Pipe)在缺省地址处自然是可寻址的,主机通过读取设备描述表,判决设备缺省通道的实际净数据负载。

主机从设备读取配置信息要从配置0读到配置n-1,其中n为配置个数,此操作须花费几个毫秒。

基于从设备取来的配置信息及设备如何被使用的信息,主机给设备一个配置值,此刻,设备就处于配置状态(Configured state)并此配置有关的所有端节点,都按照配置各就各位,USB设备现在可以从 VBUS得到描述中所要求的电量了。从设备的角度来讲,它已经准备就绪了。

  当USB设备被取走时,集线器同样会通知主机,断开一个设备连接会使得设备所连接的端口无效,一收到断开通知后,主机就会更新拓扑信息。

从上述USB上电的整个过程中,我们可以看到,为了实现USB设备能正确被主机识别,首先需要解决描述符的问题,只要描述符能够被主机正确识别,USB系统基本能够工作了,剩下的就是把用户协议添加到USB系统中。

描述符问题(descriptor),描述符包括report描述符、HID描述符、设备(device)描述符、配置(configuration)描述符、接口(interface)描述符、端点(endpoint)描述符;其中report描述符是不定长的,设备不同会不同,而其他描述符一般都有固定的格式。主要实现这些描述符的格式定义以及相应的数据初始化。

主机与设备间的通讯协议问题,使上位机(电脑)能够正确识别设备,并能实现正常的数据交换。主要包括USB设备能够正确回复上位机的查询(USB实现相应的中断处理,实现对上位机的数据的接收与解析以及正确回复等)。

用户协议问题,实现用户方的协议与USB搭配使用,并得到正确及时的数据响应

二、USB描述:

描述符多种多样,下表给出了各个描述符类型的对应编码:

描述符种类                     值

Device descriptor                 1

Configuration descriptor        2

String descriptor              3

Interface descriptor            4

Endpoint descriptor            5

实际在给描述符表赋值时,对应的描述符类型按照上表进行

1、设备描述:

struct usb_device_descriptor {

    __u8  bLength;    -------------------------------usb设备描述的长度(0x12)

    __u8  bDescriptorType;----------------------------设备描述类型一般都是0x1

    __le16bcdUSB;

    __u8  bDeviceClass;

    __u8  bDeviceSubClass;

    __u8  bDeviceProtocol;

    __u8  bMaxPacketSize0;

    __le16idVendor; ------------------------------------ Usb 设备版本ID

    __le16idProduct;-------------------------------------Usb 设备产品ID

    __le16bcdDevice;

    __u8  iManufacturer;

    __u8  iProduct;

    __u8  iSerialNumber;

    __u8  bNumConfigurations; -----------------------配置信息的个数

} __attribute__ ((packed));

2、配置描述:

struct usb_config_descriptor {

    __u8  bLength;------------------------------配置描述长度

    __u8  bDescriptorType; ------------------配置描述类型(0x02)

    __le16wTotalLength; -----------配置描述总长度(包括接口描述+端口描述)

    __u8  bNumInterfaces;------------------接口个数

    __u8  bConfigurationValue;

    __u8  iConfiguration;

    __u8  bmAttributes;

    __u8  bMaxPower;

} __attribute__ ((packed));

3、接口描述:

struct usb_interface_descriptor {

    __u8  bLength;-------------------------------接口长度

    __u8  bDescriptorType;---------------------接口类型为0x04

    __u8  bInterfaceNumber;--------------------替换接口数

    __u8  bAlternateSetting;

    __u8  bNumEndpoints;-----------------------端口个数

    __u8  bInterfaceClass;

    __u8  bInterfaceSubClass;

    __u8  bInterfaceProtocol;

    __u8  iInterface;

} __attribute__ ((packed));

4、端口描述:

struct usb_endpoint_descriptor {

    __u8  bLength;--------------------------------端口长度  

    __u8  bDescriptorType;----------------------端口类型0x05

    __u8  bEndpointAddress;--------------------端口号,最高位为1 IN ,0 OUT

    __u8  bmAttributes;---------------定义端口为INT,ISO,BULK,CONTROL

    __le16wMaxPacketSize;-----------端口支持的传输的数据大小

    __u8  bInterval;

    __u8  bRefresh;

    __u8  bSynchAddress;

} __attribute__ ((packed));

5、字符描述:

struct usb_string_descriptor {

    __u8  bLength;---------------------------------长度

    __u8  bDescriptorType;----------------------类型定义为0x03

    __le16wData[1];              ---------------------这个一般都是地址来读数据

} __attribute__ ((packed));

不管什么USB设备,都有这些配置信息,这些配置信息给出,就知道该usb设备支持什么样的数据传输类型及传输数据大小。

USB HID配置的描述和usb 设备有点出别,如果大家感兴趣的用户可以去多了解这方面的资料,网上都有这方面的资料描述。

三、USB状态

USB设备有若干可能的状态, 其中一些对于USB与主机(host)来说是外置的,而另外一些对 USB 设备来说是内置的。对于外置设备状态有以下几种:Attached(连接状态)、Powered(上电状态)、Default(缺省状态)、Address(地址状态)、Configured(配置状态)、Suspended(挂起状态)。

连接状态:USB 设备可被连接到 USB 接口上或从接口断开;

上电状态:USB 设备的电源可来自外部电源,也可从 USB 接口的集线器而来。电源来自外部电源的 USB 设备被称作自给电源式的(self-powered)。尽管自给电源式的USB 设备可能在连接上 USB 接口以前可能已经带电,但它们直到连线上 USB 接口后才能被看作是加电状态(Powered state)。而这时候 VBUS 已经对设备产生作用了。

缺省状态:设备加电以后,在它从总线接收到复位信号之前不会对总线传输发生响应。在接收到复位信号之后,设备才在缺省地址处变得可寻址。

地址状态:所有的 USB 设备在加电复位以后都使用缺省地址。每一设备在连接或复位后由主机分配一个唯一的地址。

配置状态:在USB设备正常工作以前,设备必须被正确配置。从设备的角度来看,配置包括一个将非零值写入设备配置寄存器的操作。配置一个设备或改变一个可变的设备设置会使得与这个相关接口的终端结点的所有的状态与配置值被设成缺省值。

挂起状态:为节省电源,USB 设备在探测不到总线传输时自动进入挂起状态。当中止时,USB设备保持本身的内部状态,包括它的地址及配置

四、USB标准请求

所有的 USB 设备在设备的缺省控制通道(Default Control Pipe)处对主机的请求发出响应。这些请求是通过使用控制传输来达到的,请求及请求的参数通过Setup包发向设备,由主机负责设置Setup包内的每个域的值。每个Setup包有8个字节。

Setup包由5个域所组成,包括请求类型(bmRequestType)、请求名称(bRequest)、wValue、wIndex、wLength。

bmRequestType的格式为D7D6D5D4D3D2D1D0B,D7表示传输方向(0表示主机到设备,1表示设备到主机),D6D5表示种类(0表示标准,1表示类,2表示厂商,3为保留),D4D3D2D1D0为接受者(0表示设备,1表示接口,2表示端点,3表示其他,4以后为保留)。

下表是标准请求表:

bmRequestType  bRequest  wValue  Windex wLength  Data

00000000B  CLEAR_FEATURE 1 特性选择符 零 零 无

00000001B  接口号

00000010B  端点号

10000000B   GET_CONFIGURATION 8 零零一配置值

10000000B   GET_DESCRIPTOR6 描述表种类索引 零或语言标志 描述表长 描述表

10000001B  GET_INTERFACE 10 零接口号一可选设置

10000000B   GET_STATUS 0 零 零二设备,接口或端点标志

10000001B   接口号

10000010B   端点号

00000000B   SET_ADDRESS 5 设备地址 零零无

00000000B   SET_CONFIGURATION 9 配置值零零无

00000000B   SET_DESCRIPTOR7 描述表种类索引 零或语言标志 描述表长 描述表

00000000B   SET_FEATURE 3 特性选择符 零零无

00000001B   接口号

00000010B   端点号

00000001B   SET_INTERFACE 11 可选设置 接口号 零 无

10000010B   SYNCH_FRAME 12 零 端点号二帧号

在USB完成配置之后,USB设备就处于就绪状态,这是USB标准请求表,一定要弄明白,在做usb 设备的固件程序和USB驱动必须要用到的!

五、介绍三星RP4412开发板 USB方面的驱动功能:

1、  在RP4412开发板 kernel下的arch/arm/mach-exynos/mach-smdk4x12.c,在这个文件里面有USB的EHCI,OCHI,GADGET,OTG设备加载及介绍

OHCI主要为非PC系统上以及带有SiS和 ALi芯片组的PC主板上的USB芯片
UHCI大多为Intel和Via主板上的USB控制器芯片。UHCI的硬件电路比OHCI简单,成本低,但驱动复杂,但他们都是由USB1.1规格的。
EHCI是有Intel等几个厂商研发,兼容OHCI UHCI 遵循USB2.0规范。
USB规范都是从寄存器级别规定好的,不过各个厂商可能有自己的几个专用的寄存器。

三星为了实现ECHI,OCHI,OTG,gadget三星提供了相关的驱动程序,

#ifdef CONFIG_USB_EHCI_S5P

       smdk4x12_ehci_init();

#endif

#ifdef CONFIG_USB_OHCI_S5P

       smdk4x12_ohci_init();

#endif

#ifdef CONFIG_USB_GADGET

       smdk4x12_usbgadget_init();

#endif

#ifdef CONFIG_USB_EXYNOS_SWITCH

       smdk4x12_usbswitch_init();

#endif

arch\arm\plat-s5p\ dev-ehci.c   此文件实现了ECHI,OCHI, OTG中断及IO内存声明

arch\arm\plat-s5p\dev-usbgadget.c  此文件定义了USB类型usb_mass_storage,adb,mtp,rndis等,里面同时也定义相关的类型的USB产品ID及adb序列号。

arch\arm\mach-exynos\setup-usb-phy.c 此文件实现USB OTG,ECHI,OCHI等寄存器配置功能

arch\arm\mach-exynos\ setup-sdhci-gpio.c此文件是配置4412 GPIO为USB 功能

smdk4x12_usbswitch_init();此函数定义了三星的OTG功能,OTG实现的原理就是利用,

OTG驱动关联在drivers\usb\misc\ exynos-usb-switch.c



USB_OTG实现,三星4412利用两个GPIO定义,一个GPIO为实现HOST中断,当插入设备是U 盘及键盘、鼠标,而GPIO(HOST_PW_EN)脚拉低,同时给VBUS线设置电源,给插入到usb host的设备供电  同理,另外一个GPIO定义是实现USB 线连接PC电脑设备等,这时MID是作为设备使用的功能,而这个USB_DET的IO脚拉高。 exynos-usb-switch.c文件主要实现上面两个功能的需求,根据其中的ID变化能识别插入到OTG设备是什么设备,自动加载相应的设备驱动模块,这个文件关键的函数是exynos_change_usb_mode(),此函数就实现了HOST,设备之间的切换。 不过三星实现的USB OTG功能还是比较复杂,三星OTG利用两个中断来实现host,设备切换,如果不修改此文件,三星exynos-usb-switch.c文件在待机有问题。  2、由于usb 实现的驱动比较庞大,主要usb 类设备太多,现在大都设备都可以利用usb接口来实现功能. EHCI驱动drivers\usb\host\ehci-hcd.c,此文件ehci_hcd_init()函数加载drivers\usb\host\ehci-s5p.c,ehci-s5p.c函数实现ECHI功能,初始配置三星USB寄存器功能配置及USB总线和USB root HUB,在USB_HCD中有一个重要的结构:

static const struct hc_driver s5p_ehci_hc_driver ,这个结构实现了USB 总线和跟usb hub的功能,下面有几个重要的函数介绍:



3、USB驱动中OCHI介绍,驱动路径, drivers\usb\host\ ohci-hcd.c, drivers\usb\host\ ohci-s5p.c关键是OCHI-s5p.c文件,此文件实现OCHI功能配置,其中下面几个重要的函数:



Usb_hcd在OCHI也有一个重要的结构static const struct hc_driverohci_s5p_hc_driver ,结构也实现了上面函数定义,也定义了USB hub及USB总线的实现。  4、gadget功能实现, 实现了 USB 协议定义的设备端的软件功能drivers\usb\ gadget\ s3c_udc_otg.c,此文件时三星实现功能,如果要了解gadget功能,请网上多搜索有关的GadGet文档了解,如果产品只有一个OTG接口,那么于OTG通讯都在在这个文件里面实现数据传输gadget功能驱动层是USB Gadget软件结构的最上层。主要是实现USB设备的功能,这一层通常与linux内核的其他层有密切的联系。模拟U盘的gadget就与文件系统层与块IO层有着联系。  5、usb hub介绍: 一个USB设备插上linux系统的PC后是如何一步一步调到我们的usb设备驱动的probe

函数的, 我们知道我们的USB驱动的probe函数中的一个参数是interface结构, 因此一般来说, 一个USB设备中的任何一个接口都应该有对应的一个驱动程序/*driver/usb/core/hub.c*/:usb_hub_init()-->hub_thread()-->hub_events()-->hub_port_connect_change()我们知道USB设备都是通过插入上层HUB的一个Port来连入系统并进而被系统发现的,当USB设备插入一个HUB时,该HUB的那个port的状态就会改变, 从而系统就会知道这个改变, 此时会调用hub_port_connect_change()/*driver/usb/core/hub.c*/函数创建一个usb_device的对象udev, 并初始化它,接着调用usb_new_device()来获取这个usb设备的各种描述符并为每个interface找到对用的driver.intusb_new_device(struct usb_device *udev){…. usb_enumerate_device()-àusb_get_configuration(udev);….device_add(&udev->dev);}该函数首先调用usb_get_configuration()来获取设备的各种描述符(设备描述符,配置描述符等),接着调用device_add()来把这个USB设备添加到USB系统中去, 也就是在这个过程中系统回去为这个设备找到相应的驱动.USB 功能驱动,一般都是用户自己编写代码,编写USB功能驱动一般利用usb_register()函数注册,卸载usb驱动利用usb_deregister()如果用户想自己编写usb驱动,可以参考drivers\usb\usb-skeleton.c











怎么一直在审核啊

不错,多陪几张图就更好了

不好意思哈哈

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top