1、USB设备
物理特征:4条电缆,电源线、地线、数据线、脉冲线;
速度:低速1.5Mbps,全速12Mbps,高速480Mbps;
规范版本:1998年USB1.1,2000年USB2.0;
连接:PCI总线<->USB控制器(OHCI/UHCI/EHCI)<->USB设备;
单个USB控制器最大连接个数:127;
距离限制:USB单条线缆长度不能超过5m,通过hub可延长至30m。
重要概念:主机-USB设备采用master-slave方式分工,所有通信都是主机发起的;在某一时刻,只有一个设备与主机通信。
一个USB物理设备可以抽象为一个或多个逻辑设备。
USB逻辑设备层次:逻辑设备->配置(configuration)->接口(interface)->端点(end point); 端点是一个地址标识,是驱动和设备数据交换的一个终点,类似于网络编程中的客户短套接字或者服务器端套接字。在USBD client和特定的设备endpoint之间的通道被称作管道(pipe),一个从驱动到设备的数据传输管道包括以下几个要素:
1)设备的端点(通过读取/设置配置、接口得到);
2)设备号(设备动态插入后由USBD分配);
3)传输方向(从设备到主机、或从主机到设备);
4)带宽要求;
5)延迟要求。
6)主机与USB间有4种传输方式:
A.控制(小批量数据、保证到达);
B.同步(大批量数据、定时传输、不能保证到达);
C.批量(大批量数据、保证到达);
D.中断(小批量数据、不定时产生、保证到达);
控制用于读取/设置USB设备,所有USB设备的端口0默认(或者说强制)给控制管道使用;
同步主要用于视频设备如摄像头定时产生的批量数据,允许在带宽不足的情况下丢弃部分数据包;
批量用于一次性的大批量数据传输;
中断用于异步数据如键盘(或鼠标)按下事件发生等。
系统为不同传输类型分配不同的可使用带宽。因此,控制管道必须占有10%的可用带宽,而批量管道不能达到USB的理想速率,实际中USB设备与主机的传输速率比理想速率低很多。
2、VxWorsk下USB协议栈
下图提供了一个USB主驱动栈结构的简单概括,共四层。
数据发送路线及格式:客户驱动(USB_IRP包)->USBD(URB包)->HCD(HRB包)。>>>>在栈最底层的是硬件:USB主控制器(USB host controller),USB主控制器在嵌入式主机系统中控制USB,实现USB主机功能。目前,USB控制器主要有3类:
(1)(UHCI,universal host controller interface)通用的主控制器,由Intel最先设计并制定相应规范;
(2)(OHCI,open host controller interface)开放的主控制器,由Microsoft,Compaq和National Semicondtlctor最先设计并制定相应规范;
(3)(EHCI,enhanced host controller interface)增强的主控制器,该控制器支持USB2.0. >>>>每一主控制器在其上都有相对应的主控制器驱动程序(HCD),这些设备驱动程序与底层的硬件相关,为上层提供统一的功能接口。相应的驱动分别在usbHcdOhciLib.c和usbHcdUhciLib.c中定义。
USB主机驱动(USBD)与底层硬件无关,通过HCD与底层控制器通信。USBD管理连接到主机上的每一个USB连接,并提供高层与USB设备通信的通道。除此之外,USBD还自动对USB设备进行电源管理和分配带宽。在USB系统中,集线器(HUB)对USB系统的正常操作起到关键性作用,因此USBD直接对集线器进行控制。这就意味着USBD还具有控制USB设备和集线器动态插拔的能力。(USBD)是客户驱动和HCD之间的中介,接受客户发送来命令,发送给HCD,HCD在驱动硬件接受。USBD入口函数usbdCoreEntry(pURB_HEADER pUrb),所有请求函数都先进入此函数,根据pUrb->function 的值调用相应函数。
在协议栈的最顶层是USB设备驱动程序。设备驱动程序依赖USBD提供的通信通道,驱动连接到USB系统中的USB设备。
下图2显示了USB主驱动栈的各模块之间的功能联系。
通过图1/2的结构,可以看出USB主机栈的设计关键是USB主机驱动(USBD)的设计,USBD 为USB设备驱动程序提供标准的USBD API;为主机控制器驱动提供HCDAPI。
一个用户设置interface例子(仅仅说明在执行一个操作的过程中Stack的数据流):
A:usbdInterfaceSet() 生成URB_INTERFACE_GET_SET结构的变量Urb,结构中第一个变量是URB_HEADER;
B:调用urbExecBlock(&Urb.header) USB消息队列;
C:调用usbdCoreEntry(pUrb) 进行消息派发;
D由于pUrb->function=USBD_FNC_INTERFACE_SET,调用fncInterfaceSet(pUrb)->(USBD);E:调用controlRequest()->(USBD)生成USB_IRP,USB_SETUP包;
F:调用usbdTransfer()->(USBD)生成URB_TRANSFER结构的变量Urb;
G:调用urbExecBlock(&Urb.header)->usbdCoreEntry(pUrb);
H:由于pUrb->function=USBD_FNC_TRANSFER,调用fncTransfer()->(USBD)生成USB_IRP 包;
I:调用usbHcdIrpSubmit()->(HCD)生成HRB_IRP_SUBMIT结构hrb,
J:结构中第一个变量是HRB_HEADER,执行(*pNexus->hcdExecFunc) ((pVOID) &hrb); 这里HCD中也只有一个入口函数,在HCD注册时提供给系统,这里也就是hcdExecFunc所指向的函数。根据HCD类型选择注册usbHcdOhciExec()或者usbHcdUhciExec(),这两个函数都接受HRB数据,根据pHrb->function类型进行不同的处理。
上面例子中(*pNexus->hcdExecFunc) ((pVOID) &hrb);(在jx2410开发板采用ohciHCD)相于执行usbHcdOhciExec((pVOID) &hrb)->(HCD),由于hrb->function=HCD_FNC_IRP_SUBMIT,调用fncIrpSubmit()->(HCD)在fncIrpSubmit里真正完成数据传送:
步骤包括:
(根据数据块个数)USER_FLUSH();
if (pPipe->busAddress = = pHost->rootAddress)
rootIrpHandler() ;
else
busIrpHandler();
setIrpResult();
应用程序的关键在于发送合适的USB_IRP包,完成控制USB设备,获取数据的功能。
笔记2
3,USBD驱动详解
这一部分将要描述USBD(USB Host Driver)的典型应用。例如初始化,client注册,动态连接注册,设备配置,数据传输,同时还探讨了USBD内部设计的关键特性。这部分是VxWorks 下USB驱动的核心。
1 初始化USBD:分为两步
(1)必须至少调用一次函数usbdInitialize()。在一个给定的系统中,usbdlnifialize()初始化内部USBD数据结构,并依次调用其它USB驱动栈模块的入口。usbdinitialize()可以在启动时调用一次,也可以对每一个设备各调用一次。USBD 自己记录了调用usbdInitialize()(‘+’)和usbdShutDown()(‘—’)的次数。只有大于等于1时才是真正初始化了,而等于0是关闭/* Initialize the USBD. */
if (usbdInitialize () != OK)
return ERROR;
/* application code. */
/* Shut down the USBD. Note: we only execute this code if the return
* code from usbdInitialize() was OK. Not e also that there’s really no
* reason to check the result of usbdShutdown().*/
usbdShutdown ();
(2)用USBD 的lisbdHedAttaeh()函数来把至少一个HCD连接到USBD上。这一过程既可以在VxWorks启动时,也可以在运行时把HCD 连接到USBD 上去。后一种机制可以支持“热插拔”,而不用象前一种那样需要重新启动。
2 HCD的连接(attaching)与断开(detaching)
当HCD连接到USBD 时,调用者为usbdHedattaeh()函数传递HCD执行入口(表HCD_EXEC_FUNC)和HCD连接参数(HCD attach parameter)。USBD用HCD FNC ATY ACH
服务请求依次激活HCD的执行入口,传递同样的HCD attach参数。
需要强调的是虽然可以改变用HCD定义的参数,但是最好不应该有所改变。对于WindRiver提供的UHCI和OHCI的HCD,HCI attach参数是一个指向结构PCI_CFG_HEADER (定义在pciConstants.h) 的指针。
该结构用UHCI和OHCI主控制器的PCI配置头来初始化,而HCD用这个结构中的信息来定位,管理特定的主控制器。典型的,调用者用usbPeiClassFind ()和usbPciConfigHeaderGet()来得到想要的主控制器的PCI配置头,这两个函数定义在usbPciLib 中(stubUsbarchPciLib.h中)。如果有UHCI或OHCI要连接到USBD,就要调用这些函数来获得每一个主控制器的PCI_CFG_HEADER。然后利用usbdHedAttaeh来激活已鉴别出的每一个主控制器。
注意:底层BSP可能不支持USB的HCD断开,因为当中断向量表重新使能时,如果还应用的是过期的向量表,会导致错误。
//挂接过程
UINT8 busNo;
UINT8 deviceNo;
UINT8 funcNo;
PCI_CFG_HEADER pciCfgHdr; /* PCI_CFG_HEADER defined in pciConstants.h */ GENERIC_HANDLE uhciAttachToken; /* GENERIC_HANDLE defined in usbHandleLib.h */
/* Locate the first (0th) UHCI controller. UHCI_CLASS, etc., are defined
* in usbUhci.h. The functions usbPciClassFind() and
* usbPciConfigHeaderGet() are exported by usbPciLib.*/
if (!usbPciClassFind (UHCI_CLASS, UHCI_SUBCLASS, UHCI_PGMIF, 0,
&busNo, &deviceNo, &funcNo))
{
/* No UHCI controller was found. */
return ERROR;
}
usbPciConfigHeaderGet (busNo, deviceNo, funcNo, &pciCfgHdr);
/* Attach the UHCI HCD to the USBD. The function usbHcdUhciExec() is
* exported by usbHcdUhciLib.*/
if (usbdHcdAttach (usbHcdUhciExec, &pciCfgHdr, &uhciAttachToken) != OK)
{
/* USBD failed to attach to UHCI HCD. */
return ERROR;
}
/* Attachment is complete. */
//取消挂接
/* Detach the UHCI HCD from the HCD. */
usbdHcdDetach (uhciAttachToken);
/* Detach is complete! */
3 启动顺序
必须在所有USBD函数前执行函数usbdInitialize()。存在以下两种调用方式:
(1)传统的“启动”初始化。执行顺序与其意义如下:
a.usbdInitialize();
b.usbdPciClassFind():定位一个USB主控制器;
c.usbdPeiConfigHeaderGet():读USB主控制器配置头;
d.usbdHedAttaeh():连接HCD,将其作为特定的主控制器:
e.调用USB class driver初始化入口点;
f.USB class driver调用usbdlnitialize()。
(2)“热插拔”调用。执行顺序与其意义如下:
Boot Code里调用:
a.USB class driver初始化入口点;
b.USB class driver调用usbdlnitialize();
Hot-Swap code调用:
c.Hot-Swap 鉴别USB主控制器的连接或断开;
d.Usbdlnitialize();
e.UsbdPciConfigHeaderGet():读USB主控制器配置头;
f.UsbdHedAttaeh():连接HCD,将其作为特定的主控制器。
因为热插拔可以在任何时刻发生,所以USBD和其Client都必须被写成可以动态识别USB设备被插入还是被拔出。当主控制器连接到系统时,USBD 自动地鉴别与其相连的设备,并通知相关的client;同样,拔出设备时,也要通知相关设备。重要的是,USBD 的client,比如USB class driver,在client初始化时,从不设想特定的设备已经出现;而在其他时候,这些驱动随时检查设备是否已经连接到系统上。
4 总线任务
对每一个连接到USBD 的主控制器,例如插入或拔出设备,USBD都会产生一个总线任务,来监控总线事件。一般情况下,这些任务是休眠的(不消耗CPU),只有当USB hub 报告它的一个端口有变化时,它们才被唤醒。每一个USBD总线任务有VxWorks任务名:
UsbdBus。
虽然HCD委托USBD来管理,但有可能HCD 亲自监视主控制器事件。例如WindRiver 提供了UHCI和OHCI的HCD来创造这样的任务。对于WindRiver的UHCI模块(usbHcdUheiLib),后台任务只是被周期地唤醒,目的是为了检查超时IRP(用一个中断来通知OHCI根hub发生改变)。
用以在USBD和USB之间进行通信的client模块,除了调用usbdlnitialize()外,必须调用usbClientRegister()使其在USBD注册。当一个client注册到USBD时,USBD把每一个以后将要用到的client的数据结构定位,并跟踪那个client的请求。
对于每一个client,在client注册过程中,USBD还创建了一个callback任务。在成功注册client后,USBD返回一个句柄USBD_CLIENT_HANDLE。以下对USBD的调用,将会用到这个句柄。当所有句柄都不需要时,可以调用usbdClientUnregister()来释放每一个client的数据结构和callback任务。注意:此时所有client要求的任务都会被取消。
例如:注册一个叫USBD_TEST的client,再注销。
注册:usbdClientRegister("USBD_TEST,&usbdClientHandle);
注销:usbdClientUnregister(usbdClientHandle);
5 client回调(callback)任务
USB操作是严格遵守时序的。例如为使中断传输和同步传输正确工作.需要依靠时钟中断。在一个有几个不同client出现的主系统中.总是有可能出现一个client打断其它client 传输事件的发生。WindRiver USBD建议用client callback任务来解决这个问题。许多USB 事件可以导致一个USB client的callback任务。例如,每当USBD 完成USB IRP后,client 的IRP callback函数被激活。同样,当USBD识别出一个动态连接事件后,会激活一个或更多的动态attach callback操作。但不是马上激活这些回调操作,而是安排合适的相应的USBD client的回调任务来执行callback。
一般的情况下,每一个client的callback任务处于“休眠”态(阻塞态)。每一个client 的callback,继承了usbdClientRegister()产生的VxWorks任务优先级。这确保了每一个callback 按其client的任务优先级来执行,而且可以利用优先级来写client,保证对时间要求严格的USB传输。由于每一个client有它自己的callback任务,因此在callback期间,它们有很大的灵活性决定可以做什么。例如,允许在不破坏USBD或其它USBD client性能的条件下,使callback执行代码运行至阻塞态。
Client callback task有VxWorks任务名:tUsbdCln。
6 USBD内部Client
当第一次初始化USBD时,由USBD产生并注册一个内部client,以跟踪USB请求。USBD 可以产生什么类型的USB请求呢? 所有USBD与USB设备的传输,均利用调用USBD client的形式来完成。例如,当一个设备第一次连接到系统时.USBD用一个控制管道(control pipe) 自动地创建设备需要的所有的control pipe,即USBD client要用usbdPipeCreate()来创建一个与USB endpoint0通话的通道,然后所有USBD 内部、外部client 通过这个管道来发送诸如usbdDescriptorGet()或usbdFeatureGet()等的函数,进行操作。所以,USBD 的一个机制就是USBD 循环利用它自己的entry point,而内部chent跟踪这些请求。
7 动态连接的注册
每当一个特定类型的设备插入或拔出时,USBD client都通知上一层。利用调用usbdDynamicAttachRegister()操作,client可以指定一个callback操作,以便可以获取这样的通知。
USB设备类型用class,subclass,protocol来区别。标准的USB 类在usb.h 中定义为USB_CLASS_XXXX。Subclass和protocol根据class来定义,因此这些常数根据特定的class在头文件中定义。有时,一个client当利用usbdDynamicAttachRegister()进行注册时,只对特定的class,subclass,protocol感兴趣。例如,USB键盘类驱动usbkeyboardLib,注册了Human Device Interface (HID) 类,subclass 是USB_SUBCLASS_HID_ BOOT,protocol 是USB_PROTOCOL_HID_BOOT _KEYBOARD。通过callback机制的响应,每当一个设备完全符合这样的标准,从设备上插入或拔出时,SBD便通知给keyboard class driver。而在其它情况下,client关注的范围更广泛了。常量USBD_NOTIFY(定义在usbdLib.h)可以替代任意的class,subclass,protocol。例如,USB打印机USB驱动,usbPrinterLib,其class 等于USB_CLASS_PRINTER,subclass 等于USB_SUBCLASS_PRINTER (usbPrinter.h),protocol等于USBD_ NOTIFY_ ALL。典型的,当一个client只调用一次usbdDynamicAttachRegister()时,对一个client能拥有的并发通知请求数目没有限制。
8 Node ID
USB设备一般用USBD_NODE_ID来区别。从其作用来看,USBD_ NODE_ ID 是USBD 用来跟踪一个设备的句柄。它与USB设备真正的USB地址无关。这表明client并不真正关心想要了解设备是物理上与哪一个USB主控制器相连。应用为每个设备抽象定义的Node ID,使client可以不用考虑物理设备的连接细节以及USB地址分配,并允许USBD 在其内部对这些进行详细的管理。
当一个client通知有一个设备连接或断开时,USBD经常通过USBD_NODE_ID来定位设备。同样,当一个client想通过USBD与一个特定的设备通信时,它必须向USBD传递那个设备的USBD_NODE_ID。
9 总线编号(bus enumeration)操作
usbdLib模块提供了usbdBusCountGet(),usbdRootNodeldGet(),usbdHubPortCountGet(),usbdNodldGet()操作。它们被一起称作总线编号操作。它们使USBD Client对连接到每一个主控制器上的设备进行编号。
这些操作对于诊断程序和测试工具很有用,例如usbTool(WindRiver提供的一个测试工具)。但是,利用它们编号之后,调用者无法知道USB的拓扑结构是否变化。因此,建议USB class driver的开发者不要用这些操作。
10 数据传输
一旦client配置完成一个设备,就开始利用USBD提供的管道和传输功能与设备进行数据交换。为了和设备交换数据,client必须先创建管道。作为结果,USBD得到了一个USBD_PIPE_HANDLE,它被用于随后对这个管道的所有client操作。
当client企图创建一个管道时,USBD会检查是否有足够的可用带宽。对于中断和同步传输,带宽限制是必需的。USBD不允许把90%以上的可用带宽分配给中断和同步管道;而对于控制和块传输,则没有带宽的限制。同时,保证至少10%的带宽用于控制传输,对块传输则不保证会提供任何可用带宽。
数据传输的具体过程:
(1)创建pipe :usbdPipeCreate(usbdClient Handle,nodeld,endpoint,configvalue,interface,
USB_XFRTYPE_BULK,USB_ DIR_OUT,maxPacketSize,0,0,&outPipeHandle);
(2)定义callback:ourlrpCallback(pvoid P);
(3)初始化IRP的数据结构;
(4)发送IRP:usbdTransfer(usbdChentHandle,outPipeHandle,&irp)。
4、VxWorks下USB驱动编写流程
4.1 生成bootable工程,添加以下组件(根据不同硬件定制):
hardware->buses->USB Hosts->OHCI
hardware->buses->USB Hosts->USB Host Stack
hardware->buses->USB Hosts->USB Host Init->OHCI Init
hardware->buses->USB Hosts->USB Host Init->USB Host Stack Init
此时编译后的内核在启动时如果出现Attach OHCI...OK,表示USB协议栈加载成功。
4.2 修改和检查config.h
/* USB Stuff */
#define INCLUDE_USB
#define INCLUDE_OHCI
#define INCLUDE_OHCI_INIT
#define INCLUDE_USB_INIT
#define INCLUDE_USB_MOUSE
#define INCLUDE_USB_KEYBOARD
#define INCLUDE_USB_MS_BULKONL Y
#define INCLUDE_USB_MS_CBI
#define INCLUDE_USB_PRINTER
#define INCLUDE_USB_SPEAKER
//新增加,根据系统不同配制,可能不同
//IO 地址相关,该系统不采用动态PCI查找和影射
#define SL811H_IO_ADDR SL811H_MEMORY_START
#define SL811H_IO_ADDR_DATA ((SL811H_MEMORY_START) | 0x800000)
//中断相关
#define SL811H_INT_LVL INT_LVL_EXT_IRQ_0 /* PPC405GP UIC Interrupt 25 - External IRQ 0 */
#define SL811H_INT_VEC INT_VEC_EXT_IRQ_0 /* PPC405GP UIC Interrupt 25 - External IRQ 0 */
4.3 wrSbc405gp.h(特定系统配制文件)
#define SL811H_MEMORY_START 0x70000000
#define SL811H_MEMSIZE 0x10000000
4.4 sysLib.c,增加MMU属性配制
{
(void *) SL811H_MEMORY_START,
(void *) SL811H_MEMORY_START,
SL811H_MEMSIZE,
VM_STATE_MASK_V ALID | VM_STATE_MASK_WRITABLE | VM_STATE_MASK_CACHEABLE | VM_STATE_MASK_GUARDED,
VM_STATE_V ALID | VM_STATE_WRITABLE | VM_STATE_CACHEABLE_NOT | VM_STA TE_GUARDED
}
5.调用流程
5.1 USB Init
///////////////////////////////////////////////////////////////////////////////////////////
UINT16 cmdUsbInit() /*Initialize USBD */
{
UINT16 usbdVersion;
char usbdMfg [USBD_NAME_LEN+1];
UINT16 s;
/* if already initialized, just show a warning */
if (initialized)
{
printf( "Already initialized.\n");
return RET_CONTINUE;
}
/* Initialize the USBD */
s = usbdInitialize ();
printf( "usbdInitialize() returned %d\n", s);
if (s == OK)
{
/* Register ourselves with the USBD */
s = usbdClientRegister (PGM_NAME, &usbdClientHandle);
printf( "usbdClientRegister() returned %d\n", s);
if (s == OK)
{
printf( "usbdClientHandle = 0x%x\n", (UINT32) usbdClientHandle);
/* Display the USBD version */
if ((s = usbdVersionGet (&usbdVersion, usbdMfg)) != OK)
{
printf( "usbdVersionGet() returned %d\n", s);
}
else
{
printf( "USBD version = 0x%5.4x\n", usbdVersion);
printf( "USBD mfg = '%s'\n", usbdMfg);
}
if (s == OK)
initialized = TRUE;
}
}
if (s != OK)
{
printf( "initialization failed\n");
}
return RET_CONTINUE;
}
/////////////////////////////////////////////////////////////////////////////////////////////
5.2执行HCD挂接
SL811_IO_CFG sl811IOCfg = {
SL811H_IO_ADDR,
SL811H_IO_ADDR_DA TA,
SL811H_INT_VEC,
SL811H_INT_LVL
};
进入usbdHcdAttach,
5.3 setups the expansion bus access profile.(硬件单板的总线连接方式不同而不同)
5.4硬件相关寄存器初始化(因为硬件不同,所以相关过程不一样)
5.5初始化中断处理进程
(pHost->intThread =
taskSpawn ("tSl811Int", 0, 0, 0x4000,
(FUNCPTR) intThread, (int) pHost,
0, 0, 0, 0, 0, 0, 0, 0, 0))==ERROR
5.6 挂接中断
*pResult = intConnect ((VOIDFUNCPTR *)INUM_TO_IVEC (pHost->sl811CfgHdr.intVec), \
routine, (int)arg); \
开中断;
有必要的话,复位硬件(大部分时候如此)
6:数据发送和接受过程
6.1写数据过程
A:bulkWrite
LOCAL UINT16 bulkWrite
(
long bytes,
FILE *fin, /* stream for input (if any) */
FILE *fout /* stream for output (if any) */
)
{
/* Initialize the BULK class driver */
char filePath[1000];
char buffer[BULK_DRIVE_BUFFER_SIZE];
UINT8 *ptr;
int fd;
UINT32 i;
UINT8 j=1,k=1;
UINT32 len=0;
sprintf(filePath,"%sbulkFile",BULK_DRIVE_NAME);
remove(filePath);
fd = open(filePath, O_CREAT | O_WRONL Y, 0);
if (fd == ERROR)
fprintf (fout, "bulkWrite() error opening %s\n",filePath);
else
fprintf (fout, "bulkWrite() %s opened for write\n",filePath);
ptr = (UINT8 *) buffer;
for (i=1; i<=bytes; i++)
{
*ptr++ = j++;
len++;
if ( (i%BULK_DRIVE_BUFFER_SIZE)==0 || i==bytes )
{
if (len != write(fd,buffer,len))
{
fprintf (fout, "bulkWrite() error writing %u to %s\n",
i,filePath);
close(fd);
return ERROR;
}
if (i%BULK_DRIVE_DISPLAY_INTERV AL == 0)
{
fprintf (fout, "bulkWrite() wrote %u of %u bytes\n",
i,bytes);
}
len = 0;
k++;
j = k;
ptr = (UINT8 *) buffer;
}
}
close(fd);
fprintf (fout, "bulkWrite() wrote %u bytes to %s\n",i-1,filePath);
return RET_CONTINUE;
}
B:读数据过程
LOCAL UINT16 bulkRead
(
long bytes,
FILE *fin, /* stream for input (if any) */
FILE *fout /* stream for output (if any) */
)
{
/* Initialize the BULK class driver */
char filePath[1000];
char buffer[BULK_DRIVE_BUFFER_SIZE];
UINT8 *ptr;
int fd;
int i;
UINT8 j=1,k=1;
int len;
UINT32 totLen=0;
sprintf(filePath,"%sbulkFile",BULK_DRIVE_NAME);
fd = open(filePath, O_RDONL Y, 0);
if (fd == ERROR)
fprintf (fout, "bulkRead() error opening %s\n",filePath);
else
fprintf (fout, "bulkRead() %s opened for read\n",filePath);
ptr = (UINT8 *) buffer;
while ((len=read(fd,buffer,BULK_DRIVE_BUFFER_SIZE)) > 0) {
for (i=0; i { totLen++; if (*ptr++ != j++) { fprintf (fout, "bulkRead() error %u != %u at %u from %s\n", *(ptr-1),j-1,totLen,filePath); close(fd); return ERROR; } if (totLen%BULK_DRIVE_DISPLAY_INTERV AL == 0) { fprintf (fout, "bulkRead() verified %u of %u bytes\n", totLen,bytes); } } k++; j = k; ptr = (UINT8 *) buffer; } close(fd); if (totLen==bytes) { fprintf (fout, "bulkRead() verified %u bytes from %s\n", totLen,filePath); } else { fprintf (fout, "bulkRead() error reading at %u of %u in %s\n", totLen,bytes,filePath); return ERROR; } return RET_CONTINUE; } 7,USB代码的注意点: 这一周主要在研究USB代码的架构,发现一些问题: (1) 数据是以帧的方式传输的,数据包的开头是一段同步字段,同步字段的结尾是PID开始的标志,包结束是以EOP为结束的标志; (2)PE通过读出CSR以及Buffer里面的状态,来决定数据传输的可靠性,并不是PE来控制CSR; (3)仲裁器仲裁总线传输和DMA传输, CSR[15]是DMA使能位,但是发现找不到DMA request 的触发,Wishbone部分并没有对这个信号进行相应的处理; (4)从主机发过来的token会有两个token寄存器进行寄存,token的每个位都有相应的含义; (5)主机每秒发送的祯的个数,祯在Top层有一个祯的计数器; (6)在代码中,要保持信号的稳定性,往往都是采用计数器的方法,计数到一定时间后,再采样一次,这样来保持数据的稳定性。 VxWorks网络驱动配置及分析 VxWorks支持两种形式的网络驱动,一种是BSD驱动支持通用的 BSD4.4网络,API,结构等和大多数BSD网络的驱动类似.另一种是END 网络驱动,是VxWorks独有的,根据VxWorks MUX接口编程,不过END驱动在底层也要转换成BSD的形式. VxWorks网络驱动可参见VxWorks网络驱动 1.BSD4.4网络驱动设置. 网络设备驱动的调用主要在/target/src/config/usrNetwork.c文件中,下面说明BSD4.4驱动在VxWorks系统调用. 主要调用过程如下: VxWorks系统执行的第一个任务target\config\all\usrConfig.c文件中 usrRoot()=======>>target\src\config\usrNetwork.c文件中的usrNetInit ( )通过数组表netIf[]初始化相应的BSD网卡驱动. 在usrNetwork.c中的调用过程如下: usrNetInit()函数中调用usrNetIfAttach(): #ifdef INCLUDE_BSD if (!attached) { if ( (usrNetIfAttach (pNetDev, params.unitNum, pBootString) !=OK)) return (ERROR); attached = TRUE; } #endif /*INCLUDE_BSD*/ usrNetIfAttach ()函数中调用数组表usrNetIfTbl for (pNif = usrNetIfTbl; pNif->ifName != 0; pNif++) { if (strcmp (buf, pNif->ifName) == 0) break; } 网络BSD驱动数组表usrNetIfTbl在文件 /target/src/config/usrNetwork.c中的定义初始化: ... LOCAL NETIF netIf [] = /* 网络接口,定义网卡驱动 */ { /* 下面是定义包含的各种网络驱动 */ #ifdef INCLUDE_DC /* 从DEC 芯片启动,即系统有NVRAM存在,现在已过时*/ { "dc", dcattach, (char*)IO_ADRS_DC, INT_VEC_DC, INT_LVL_DC, DC_POOL_ADRS, DC_POOL_SIZE, DC_DATA_WIDTH, DC_RAM_PCI_ADRS, DC_MODE }, #endif /* INCLUDE_DC */ #ifdef INCLUDE_FEI /* 如果定义了INCLUDE_FEI,初始化Intel 网卡 */ { "fei", feiattach, (char*)FEI_POOL_ADRS, 0, 0, 0, 0}, #endif /* INCLUDE_FEI */ #ifdef INCLUDE_EX /* Excelan 网卡 */ { "ex", exattach, (char*)IO_ADRS_EX, INT_VEC_EX, INT_LVL_EX, IO_AM_EX_MASTER, IO_AM_EX }, #endif /* INCLUDE_EX */ #ifdef INCLUDE_ENP /* CMC 网卡 */ { "enp", enpattach, (char*)IO_ADRS_ENP, INT_VEC_ENP, INT_LVL_ENP, IO_AM_ENP }, #endif /* INCLUDE_ENP */ 基于vxBus的设备驱动模板 vxWorks6.x 设备驱动采用了新型的vxBus构架,该构架和原来的设备驱动有很大不同,初上手会有很多不方便。下面提供的模板有助于快速形成一个vxBus 设备驱动 设备驱动在3个地方体现: 头文件,代码文件,hwconfig.c,根据下面模板,把所有DEVICE换成自己的设备,并做相应操作即可,免去很多重复代码的苦恼,开发者仅需要关注功能即可。 这个模板处理了中断,但没有提供处理基本IO的方法,模板不是用来学习的,怎样完整地写一个vxbus的驱动,还是需要学习的。 头文件 #ifndef DEVICE_h #define DEVICE_h #define DEVICE_CARD_NAME "设备名" #define DEVICE_CARD_DESC "设备描述" #define DEVICE_CARD_VXBNAME "设备vxBus名" #define DEVICE_CARD_INT_PIN [中断管脚] 页脚内容1 #define DEVICE_CARD_INT_CORE_PIN [核中断管脚] #define DEVICE_CARD_INT_CORE_NUM [中断处理核] #define DEVICE_INT_DESC {DEVICE_INT_PIN,DEVICE_CARD_VXBNAME,0,0} #define DEVICE_XBAR_DESC {DEVICE_INT_PIN,DEVICE_INT_CORE_PIN} #define DEVICE_ROUTE_DESC {DEVICE_INT_PIN,DEVICE_INT_CORE_NUM} #define DEVICE_UNIT0_DEVICE_ID 0 /*假如有多个同型号设备begin*/ #define DEVICE_UNIT1_DEVICE_ID 1 ..... /*假如有多个同型号设备end*/ /*其他设备宏*/ #endif/*DEVICE_h*/ 页脚内容2 VxWorks Torando2.2 RTL81X9网卡驱动添加过程 Step 1. 下载RTL81X9驱动源代码。 Step 2. 复制一个BSP包,并且修改名称,例如,我复制了一个PC486的BSP包,改名为i486bsp,如图所示: Step 3. 修改复制的BSP里面的Makefile文件,修改文件内容的如下: TARGET_DIR = i486bsp MACH_EXTRA =rtl81x9.o ;添加rtl81x9.o到系统中 Step 4. 复制RTL81X9的驱动代码rtl81x9.h、 rtl81x9.c、sysRtl81x9End.c文件到你的bsp包中。Step 5. 修改BSP包中的Config.h文件。在文件如下部分添加黄色标记的内容 #define INCLUDE_PCI /* Network driver options */ #define INCLUDE_END /* Enhanced Network Driver Support */ #undef INCLUDE_DEC21X40_END /* (END) DEC 21x4x PCI interface */ #undef INCLUDE_EL_3C90X_END /* (END) 3Com Fast EtherLink XL PCI */ #undef INCLUDE_ELT_3C509_END /* (END) 3Com EtherLink III interface */ #undef INCLUDE_ENE_END /* (END) Eagle/Novell NE2000 interface */ #undef INCLUDE_FEI_END /* (END) Intel 8255[7/8/9] PCI interface */ #undef INCLUDE_GEI8254X_END /* (END) Intel 82543/82544 PCI interface */ #undef INCLUDE_LN_97X_END /* (END) AMD 79C97x PCI interface */ #undef INCLUDE_ULTRA_END /* (END) SMC Elite16 Ultra interface */ #define INCLUDE_RTL_81X9_END Step 6. 修改ConfigNet.h文件,在文件如下部分添加黄色标记的部分添加: /* Intel 82543/82544 PCI (gei) driver defines */ #ifdef INCLUDE_GEI8254X_END #define GEI8254X_LOAD_FUNC sysGei8254xEndLoad #define GEI8254X_BUFF_LOAN TRUE #define GEI8254X_LOAD_STR "" IMPORT END_OBJ * GEI8254X_LOAD_FUNC (char *, void *); #endif /* INCLUDE_GEI8254X_END */ #ifdef INCLUDE_RTL_81X9_END #define RTL_81X9_LOAD_FUNC sysRtl81x9EndLoad #define RTL_81X9_LOAD_LOAN TRUE #define RTL_81X9_LOAD_STRING "" IMPORT END_OBJ * RTL_81X9_LOAD_FUNC (char *, void *); #endif /* INCLUDE_RTL_81X9_END */ END_TBL_ENTRY endDevTbl [] = { #ifdef INCLUDE_EL_3C90X_END {0, EL_3C90X_LOAD_FUNC, EL_3C90X_LOAD_STR, EL_3C90X_BUFF_LOAN, NULL, FALSE}, #endif /* INCLUDE_EL_3C90X_END */ #ifdef INCLUDE_LN_97X_END {0, LN_97X_LOAD_FUNC, LN_97X_LOAD_STR, LN_97X_BUFF_LOAN, NULL, FALSE}, #endif /* INCLUDE_LN_97X_END */ 目录 第1章操作系统基本概念 (3) 1.1实时操作系统和分时操作系统的区别 (3) 1.2嵌入式操作系统VxWorks简介 (4) 1.3 VxWorks的特点 (5) 1.3.1可靠性 (5) 1.3.2实时性 (5) 1.3.3 可裁减性 (5) 1.3.4对一个实时核的要求 (6) 1.4 VxWorks的整体构架 (7) 第2章 vxWorks驱动开发预备 (7) 2.1 VxWorks映像 (8) 2.2 BSP主要文件目录的组成及作用: (11) 2.3系统启动顺序 (13) 2.4 Vxworks驱动概要 (17) 第3章 vxWorks 网络驱动开发 (21) 3.1驱动类别 (21) 3.2 Vxworks 下网络驱动开发 (24) 3.3 VxWorks网络驱动配置及分析 (58) 第4章 VxWorks系统中常见问题 (64) 前言 在通信,电子领域,目前主流的嵌入式操作系统为linux 和vxworks。Linux 多用于消费电子产品,像大家熟悉的andriod,原始系统就是linux,也就是说andriod也是一种linux系统。还有大家熟悉的MP3,也是使用linux操作系统的。Linux因为价格便宜(开源的,不需要付licence),稳定性好占据了一定的市场份额。 Vxworks是风河系统1983开发的一款嵌入式实时操作系统,多用于通信,导航,航天等领域。因为这些领域对实时性要求非常高,比如现在LTE 系统就是使用vxworks系统,因为是采用TDD模式,而这种模式,显然对实时性要求非常高。也就注定了vxwroks 在这些领域的特有优势。 选择linux还是vxworks看自己的专业方向,还有自己的兴趣所有。每个领域只要研究透彻,都是很有前途的。 既然两者都是主流的嵌入式操作系统,其还是有很多相似性。小编也初步接触过linux,发现具备一定的linux基础对日后深入学习vxworks很有好处,很多思想都是相似的。 另外,本人系小硕,目前的研究领域是vxworks驱动开发,主攻网络驱动开发。在平常的日常学习中,结合各位网友提供的资料(自己在百度上收集的),还有workbench 提供的document(自己翻译,特别是针对网络驱动部分),希望对广大嵌入式驱动开发者有所帮助。 声明: 此文档并不适合于vxworks初学者,因为这里,小编省去了很多vxworks 下的基本知识,包括核中常用的进程,信号量等等支持。也没有提及集成开发环境tornado/vworkbench. 这里的探索重点是vxwroks底层的东西,包括启动过程,BSP开发等。 附录: Vxworks的应用领域 嵌入式Vxworks系统的主要应用领域主要有以下几方面: 1.数据网络:如:以太网交换机、路由器、远程接入服务器等 2.远程通讯:如:电信用的专用分组交换机和自动呼叫分配器,蜂窝系统等 3.医疗设备:如:放射理疗设备 4.消费电子:如:个人数字助理等 5.交通运输:如:导航系统、高速火车控制系统等 6.工业:如:机器人 7.航空航天:如:卫星跟踪系统 8. 在网络初始化时加载END和subEND,并在其中初始化IP协议栈,在TMS初始化时绑定IP到SubEND,单播路由协议初始化在TMS初始化中,根据MIB配置确定初始化哪个单播路由协议,RIP初始化可以在网络初始化时,OSPF初始化在TMS 初始化中根据TMSMIB配置确定,END的IP地址在网络初始化时设置,SwEND的IP地址在网管配置时设置。 一、在configAll.H中定义了所有定置系统配置的宏 《INCLUDED SOFTWARE FACILITIES》:定义了基本组件; 《EXCLUDED FACILITIES》:定义了扩充组件,缺省不包括; 《KERNEL SOFTWARE CONFIGURATION》:内核运行的基本参数定义,包括文件个数、任务个数等等; 《"GENERIC" BOARD CONFIGURATION》:板通用属性定义;《"MISCELLANEOUS" CONSTANTS》:共享的信号量以及相关对象个数定义;《INCLUDED NETWORK FACILITIES》:包括的网络协议组件定义;《EXCLUDED NETWORK FACILITIES》:未包括的网络协议组件定义; 二、在bootConfig.C中的流程 usrInit()的流程,该例程被rom—start()中的boot代码调用。 调用intVecBaseSet ((FUNCPTR *) VEC_BASE_ADRS)设置中断向量; 调用excVecInit ()设置异常向量; 调用sysHwInit ()初始化硬件; 调用usrKernelInit ()配置VXWORK内核; 调用kernelInit ((FUNCPTR) usrRoot, ROOT_STACK_SIZE,启动usrRoot();usrRoot()有多个,一个在bootConfig。C中,一个在usrConfig。C中。 三、bootConfig.C中的usrRoot()流程(是BOOTROM用的) 调用 memInit (pMemPoolStart, memPoolSize);/* XXX select between memPartLibInit */初始化内存; 调用 sysClkConnect ((FUNCPTR) usrClock, 0);/* connect clock interrupt routine */ sysClkRateSet (60); /* set system clock rate */ sysClkEnable (); /* start it */ 初始化系统时钟; 调用 iosInit (NUM_DRIVERS, NUM_FILES, "/null");初始化文件系统; 根据配置的串口和控制台口个数,初始化串口; 调用 wdbConfig(); /* configure and initialize the WDB agent */初始化WDB; 调用 pipeDrv (); /* install pipe driver */ excInit (); /* init exception handling */ excHookAdd ((FUNCPTR) bootExcHandler); /* install exc handler */ logInit (consoleFd, 5); /* initialize logging */ 初始化相关功能模块; 调用 hashLibInit (); /* hashLib used by dosFS */初始化DOS文件系统;根据配置选择 VxWorks下网卡驱动程序的开发 摘要:本文首先从整体上分析VxWorks 网卡驱动程序的原理和结构,然后深入地分析Rtl8139C网卡驱动程序的结构以及实现,本文对于VxWorks下的同类开发也有一定参考作用。 关键词:网卡驱动程序;VxWorks;嵌入式系统;Tornado 1引言 VxWorks是当前应用十分广泛的嵌入式实时操作系统,而网卡在基于VxWorks的开发中有着极其重要的作用。Tornado 是VxWorks的开发工具,它本身支持的网卡不多。本文涉及到的网卡是Realtek 公司的8139C PCI网卡,相关的硬件资料有:RTL8139C(L) data sheet 和Rtl8139 Programming Guide 。需要实现的是RTL8139C END driver 。 2 VxWorks简介 VxWorks是由WRS(Wind River System Inc.)公司开发的一套具有微内核、高性能、可伸缩的实时操作系统,支持广泛的网络通信协议,并能够根据用户的需求进行组合。其开放式的结构和对工业标准的支持使开发者只需做最少的工作即可设计出有效的适合于不同用户要求的系统。 VxWorks是一种功能强大而且比较复杂的操作系统,VxWorks只占用了很小的存储空间,并可高度裁减,保证了系统能以较高的效率运行。所以仅仅依靠人工编程调试,很难发挥它的功能并设计出可靠、高效的嵌入式系统,必须要有与之相适应的开发工具。TornadoII是为开发VxWorks应用系统提供的集成开发环境。 VxWorks的优秀特性为编写应用程序和设备驱动程序提供了极大的便利。在VxWorks下,设备驱动程序既可以嵌入到内核中随系统一起启动,也可以作为可加载模块在系统启动之后运行。网卡驱动程序一般采用前一种方式。 3 VxWorks的网络模型 网卡在VxWorks开发中是极其重要的。在开发机-目标机模式中,通过网线进行下载调试是Tornado调试器最常用最方便的方式。VxWorks的优秀的实时性能使其在网络通信当中具有极广泛的应用,如网络交换机、路由器的软件系统等。 网卡驱动程序在整个VxWorks网络接口中的角色可以从下图中看出。VxWorks网络驱动配置及分析
vxWorks下设备驱动模板
VxWorks5.5 RTL81X9网卡驱动
小硕vxworks网络驱动开发笔记
VxWorks网络加载
VxWorks下网卡驱动程序的开发