dnw 是 bootloader 中一个比较实用的工具,使用 usb 线就可以下载文件到指定的内存,那么它是如何实现的呢?其实原理并不复杂,以2440为例,它有三个USB口,其中两个是 usb host ,另一个为usb slave相当于一个普通的USB设备,因此主机可以像操作U盘一样来给我们的设备发送文件。
USB 通过D-,D+ 信号的状态判断设备的插入,如下图所示,D+ 接上拉电阻为全速设备,D- 接下拉为低速设备。
对于2440来说,GPC5输出高电平时相当于上拉,USB使能。
下面开始分析代码,对于 uboot 来说,它是一个裸机程序,因此 usb 设备与主机的交互也非常底层,对于理解 usb 协议也是非常有帮助的。先来设想一下,我们肯定要配置我们的 2440 的 usb 设备控制寄存器,让它作为一个 usb 设备来工作,那么对于一个 usb 设备来说,它得具备以下的信息和能力:
- 它应该有各种描述符,设备描述符、配置描述符等等
- 它得有端点和主机通信,最起码的端点0得有
- 它得有和主机交互的能力,因为主机要通过控制传输来读取描述符,配置它,以及进行后续的数据传输
dnw 的源码也正是围绕以上的三点进行展开,大致工作:
- 配置端点0和端点3,端点0用于控制,端点3用于批量传输
- “伪造”一大堆描述符,主机请求的时候返回给主机
- 三个重要的中断处理
- 端点0的中断处理函数,这个就是标准的usb协议第九章的内容了,给设备设置地址、配置设备、请求描述符等等等等。
- 端点3的中断处理函数,端点3是一个批量端点,第一包数据到来的时候我们进入这里,分析包头里的信息,传输地址和传输数据总长度,然后配置DMA控制器后续就使用DMA来传输。
- DMA中断处理函数,收到数据判断是否继续传输~
补充一点,dnw传输时,会在数据包前面加上8个字节的包头在数据包后面加上2个字节的校验和,包头前4个字节为想要下载的地址,后4个字节为数据包的总长度(包括包头和校验)。下面开始分析代码:
int main(void)
{
MMU_Init();
Uart_Init();
Port_Init(); //管脚初始化,没必要
Isr_Init(); //中断初始化
Uart_SendByte('\n');
//gpc5 输出 0,禁用usb
rGPCCON = 0xAAAAA6AA;
rGPCDAT &= ~(1<<5);
//cpsr bit7 清零 使能irq
enable_irq();
usb_init_slave();
usb_receive();
while(1);
}
前面罗里吧嗦的mmu uart port 初始化不是重点,重点来看 usb_init_slave 函数和 usb_receivce 函数。
void usb_init_slave(void)
{
struct s3c24x0_gpio * const gpioregs = s3c24x0_get_base_gpio();
char *mode;
Delay(10);
//注册中断处理函数
Usb_Isr_Init();
//usb1 bit3 bit13 清零 将usb设置为设备模式,端口未挂起
writel((readl(&gpioregs->MISCCR) & ~((1<<3) | (1<<13))), &gpioregs->MISCCR);
isUsbdSetConfiguration = 0;
UsbdMain();
Delay(10);
//使能usb设备
writel((readl(&gpioregs->GPCDAT) | (1<<5)), &gpioregs->GPCDAT);
/* enable USB Device, thisway.diy */
#if USBDMA
mode="DMA";
#else
mode="Int";
#endif
printk("USB slave is enable!\n");
}
在这里有两个重要的函数,一个是 Usb_Isr_Init ,它注册了前面我们提到的那几个重要的中断处理函数。另一个是 UsbdMain 它的工作就是伪造描述符和配置USB设备的那些寄存器。
void Usb_Isr_Init(void)
{
isr_handle_array[ISR_TIMER4_OFT] = IsrTimer4;
isr_handle_array[ISR_WDT_OFT] = IsrWatchdog;
isr_handle_array[ISR_USBD_OFT] = IsrUsbd;
isr_handle_array[ISR_DMA2_OFT] = IsrDma2;
ClearPending_my(BIT_DMA2);
ClearPending_my(BIT_USBD);
}
重点是 usbd 函数,里面包括了端点0和端点3的中断处理。DMA2 函数,也就是前面提到的 dma 中断处理函数。这里只是注册,我们到用到的时候再来分析。
void UsbdMain(void)
{
//usb 设备描述符设置
InitDescriptorTable();
//配置usb设备寄存器,使能usb设备中断
ConfigUsbd();
}
void InitDescriptorTable(void)
{
//Standard device descriptor
descDev.bLength=0x12; //EP0_DEV_DESC_SIZE=0x12 bytes
descDev.bDescriptorType=DEVICE_TYPE;
descDev.bcdUSBL=0x10;
descDev.bcdUSBH=0x01; //Ver 1.10
descDev.bDeviceClass=0xFF; //0x0
descDev.bDeviceSubClass=0x0;
descDev.bDeviceProtocol=0x0;
descDev.bMaxPacketSize0=0x8;
descDev.idVendorL=0x45;
descDev.idVendorH=0x53;
descDev.idProductL=0x34;
descDev.idProductH=0x12;
descDev.bcdDeviceL=0x00;
descDev.bcdDeviceH=0x01;
descDev.iManufacturer=0x1; //index of string descriptor
descDev.iProduct=0x2; //index of string descriptor
descDev.iSerialNumber=0x0;
descDev.bNumConfigurations=0x1;
//Standard configuration descriptor
descConf.bLength=0x9;
descConf.bDescriptorType=CONFIGURATION_TYPE;
descConf.wTotalLengthL=0x20; //<cfg desc>+<if desc>+<endp0 desc>+<endp1 desc>
descConf.wTotalLengthH=0;
descConf.bNumInterfaces=1;
//dbg descConf.bConfigurationValue=2; //why 2? There's no reason.
descConf.bConfigurationValue=1;
descConf.iConfiguration=0;
descConf.bmAttributes=CONF_ATTR_DEFAULT|CONF_ATTR_SELFPOWERED; //bus powered only.
descConf.maxPower=25; //draws 50mA current from the USB bus.
//Standard interface descriptor
descIf.bLength=0x9;
descIf.bDescriptorType=INTERFACE_TYPE;
descIf.bInterfaceNumber=0x0;
descIf.bAlternateSetting=0x0; //?
descIf.bNumEndpoints=2; //# of endpoints except EP0
descIf.bInterfaceClass=0xff; //0x0 ?
descIf.bInterfaceSubClass=0x0;
descIf.bInterfaceProtocol=0x0;
descIf.iInterface=0x0;
//Standard endpoint0 descriptor
descEndpt0.bLength=0x7;
descEndpt0.bDescriptorType=ENDPOINT_TYPE;
descEndpt0.bEndpointAddress=1|EP_ADDR_IN; // 2400Xendpoint 1 is IN endpoint.
descEndpt0.bmAttributes=EP_ATTR_BULK;
descEndpt0.wMaxPacketSizeL=EP1_PKT_SIZE; //64
descEndpt0.wMaxPacketSizeH=0x0;
descEndpt0.bInterval=0x0; //not used
//Standard endpoint1 descriptor
descEndpt1.bLength=0x7;
descEndpt1.bDescriptorType=ENDPOINT_TYPE;
descEndpt1.bEndpointAddress=3|EP_ADDR_OUT; // 2400X endpoint 3 is OUT endpoint.
descEndpt1.bmAttributes=EP_ATTR_BULK;
descEndpt1.wMaxPacketSizeL=EP3_PKT_SIZE; //64
descEndpt1.wMaxPacketSizeH=0x0;
descEndpt1.bInterval=0x0; //not used
}
简单分析一下,
descDev.idVendorL=0x45;
descDev.idVendorH=0x53;
descDev.idProductL=0x34;
descDev.idProductH=0x12;
如果你看过 linux 下的 dnw 上层源码的话,它的 idtable 就是这个 {5345:1234},此外里边有1个配置1个接口两个端点描述符。一个是批量输入端点对应2440的端点1,另一个是批量输出端点对应2440的端点3,端点1应该是没有用到。
void ConfigUsbd(void)
{
struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
ReconfigUsbd();
//使能usbd中断
writel((readl(&intregs->INTMSK) & ~(BIT_USBD)), &intregs->INTMSK);
}
配置端点0和端点3并使能中断。
void ReconfigUsbd(void)
{
// *** End point information ***
// EP0: control
// EP1: not used
// EP2: not used
// EP3: bulk out end point
// EP4: not used
struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
/* 禁止挂起模式 */
writeb(PWR_REG_DEFAULT_VALUE, &usbdevregs->PWR_REG);
/* 端点0 */
writeb(0, &usbdevregs->INDEX_REG);
writeb(FIFO_SIZE_8, &usbdevregs->MAXP_REG); //最大包 8
//清除SETUP_END 和 POT_PKT_RDY
writeb((EP0_SERVICED_OUT_PKT_RDY | EP0_SERVICED_SETUP_END), & usbdevregs->EP0_CSR_IN_CSR1_REG); //EP0:clear OUT_PKT_RDY & SETUP_END
/* 端点3 */
writeb(3, &usbdevregs->INDEX_REG);
#if (EP3_PKT_SIZE==32)
writeb(FIFO_SIZE_32, &usbdevregs->MAXP_REG); //EP3:max packit size = 32
#else
writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG); //EP3:max packit size = 64
#endif
writeb((EPI_FIFO_FLUSH | EPI_CDT | EPI_BULK), &usbdevregs->EP0_CSR_IN_CSR1_REG);
//批量输出端点,屏蔽dma中断
writeb((EPI_MODE_OUT | EPI_IN_DMA_INT_MASK), &usbdevregs->IN_CSR2_REG); //OUT mode, IN_DMA_INT=masked
writeb(EPO_CDT, &usbdevregs->OUT_CSR1_REG);
writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG);
//Clear all usbd pending bits
//使能端点0 1 3 中断
//writeb((EP0_INT | EP1_INT | EP3_INT), &usbdevregs->EP_INT_EN_REG);
//使能端点0 3 中断
writeb((EP0_INT | EP3_INT), &usbdevregs->EP_INT_EN_REG);
//使能usb中断
writeb(RESET_INT, &usbdevregs->USB_INT_EN_REG);
ep0State = EP0_STATE_INIT;
}
端点0的中断处理函数
void Ep0Handler(void)
{
static int ep0SubState;
U8 ep0_csr;
struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
writeb(0, &usbdevregs->INDEX_REG);
ep0_csr = readb(&usbdevregs->EP0_CSR_IN_CSR1_REG);
//DATAEND interrupt(ep0_csr==0x0) will be ignored
//because ep0State==EP0_STATE_INIT when the DATAEND interrupt is issued.
if(ep0_csr & EP0_SETUP_END)
{
// Host may end GET_DESCRIPTOR operation without completing the IN data stage.
// If host does that, SETUP_END bit will be set.
// OUT_PKT_RDY has to be also cleared because status stage sets OUT_PKT_RDY to 1.
// DbgPrintf("[SETUPEND]");
CLR_EP0_SETUP_END();
if(ep0_csr & EP0_OUT_PKT_READY)
{
FLUSH_EP0_FIFO(); //(???)
//I think this isn't needed because EP0 flush is done automatically.
CLR_EP0_OUT_PKT_RDY();
}
ep0State=EP0_STATE_INIT;
return;
}
//I think that EP0_SENT_STALL will not be set to 1.
if(ep0_csr & EP0_SENT_STALL)
{
CLR_EP0_SENT_STALL();
if(ep0_csr & EP0_OUT_PKT_READY)
{
CLR_EP0_OUT_PKT_RDY();
}
ep0State=EP0_STATE_INIT;
return;
}
if((ep0_csr & EP0_OUT_PKT_READY)) // && (ep0State==EP0_STATE_INIT))
{
//读8个字节的令牌包
RdPktEp0((U8 *)&descSetup,EP0_PKT_SIZE);//PrintEp0Pkt((U8 *)(&descSetup)); //DEBUG
switch(descSetup.bRequest)
{
/* 请求描述符 */
case GET_DESCRIPTOR:
switch(descSetup.bValueH)
{
case DEVICE_TYPE: //设备描述符
CLR_EP0_OUT_PKT_RDY();
ep0State=EP0_STATE_GD_DEV_0;
break;
case CONFIGURATION_TYPE: //配置描述符,两种情况,1中1次全读,1中分次读
CLR_EP0_OUT_PKT_RDY();
if((descSetup.bLengthL+(descSetup.bLengthH<<8))>0x9)
//bLengthH should be used for bLength=0x209 at WIN2K.
ep0State=EP0_STATE_GD_CFG_0; //for WIN98,WIN2K
else
ep0State=EP0_STATE_GD_CFG_ONLY_0; //for WIN2K
break;
case STRING_TYPE: //字符串描述符
CLR_EP0_OUT_PKT_RDY();
switch(descSetup.bValueL)
{
case 0:
ep0State=EP0_STATE_GD_STR_I0;
break;
case 1:
ep0State=EP0_STATE_GD_STR_I1;
break;
case 2:
ep0State=EP0_STATE_GD_STR_I2;
break;
default:
//DbgPrintf("[UE:STRI?]");
break;
}
ep0SubState=0;
break;
case INTERFACE_TYPE: //接口描述符
CLR_EP0_OUT_PKT_RDY();
ep0State=EP0_STATE_GD_IF_ONLY_0; //for WIN98
break;
case ENDPOINT_TYPE: //端点描述符
CLR_EP0_OUT_PKT_RDY();
switch(descSetup.bValueL&0xf)
{
case 0:
ep0State=EP0_STATE_GD_EP0_ONLY_0;
break;
case 1:
ep0State=EP0_STATE_GD_EP1_ONLY_0;
break;
default:
break;
}
break;
default:
break;
}
break;
/* 设置地址 */
case SET_ADDRESS:
writeb((descSetup.bValueL | 0x80), &usbdevregs->FUNC_ADDR_REG);
CLR_EP0_OUTPKTRDY_DATAEND(); //Because of no data control transfers.
ep0State=EP0_STATE_INIT;
break;
/* 设置配置 */
case SET_CONFIGURATION:
ConfigSet.ConfigurationValue=descSetup.bValueL;
CLR_EP0_OUTPKTRDY_DATAEND(); //Because of no data control transfers.
ep0State=EP0_STATE_INIT;
isUsbdSetConfiguration=1;
break;
/* 请求特性,远程唤醒等,一般不用 */
case CLEAR_FEATURE:
switch (descSetup.bmRequestType)
{
case DEVICE_RECIPIENT:
if (descSetup.bValueL == 1)
Rwuen = FALSE;
break;
case ENDPOINT_RECIPIENT:
if (descSetup.bValueL == 0)
{
if((descSetup.bIndexL & 0x7f) == 0x00){
StatusGet.Endpoint0= 0;
}
if((descSetup.bIndexL & 0x8f) == 0x81){ // IN Endpoint 1
StatusGet.Endpoint1= 0;
}
if((descSetup.bIndexL & 0x8f) == 0x03){ // OUT Endpoint 3
StatusGet.Endpoint3= 0;
}
}
break;
default:
break;
}
CLR_EP0_OUTPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
/* 请求配置 */
case GET_CONFIGURATION:
CLR_EP0_OUT_PKT_RDY();
ep0State=EP0_CONFIG_SET;
break;
/* 请求接口 */
case GET_INTERFACE:
CLR_EP0_OUT_PKT_RDY();
ep0State=EP0_INTERFACE_GET;
break;
/* 请求状态 */
case GET_STATUS:
switch(descSetup.bmRequestType)
{
case (0x80):
CLR_EP0_OUT_PKT_RDY();
StatusGet.Device=((U8)Rwuen<<1)|(U8)Selfpwr;
ep0State=EP0_GET_STATUS0;
break;
case (0x81):
CLR_EP0_OUT_PKT_RDY();
StatusGet.Interface=0;
ep0State=EP0_GET_STATUS1;
break;
case (0x82):
CLR_EP0_OUT_PKT_RDY();
if((descSetup.bIndexL & 0x7f) == 0x00){
ep0State=EP0_GET_STATUS2;
}
if((descSetup.bIndexL & 0x8f) == 0x81){
ep0State=EP0_GET_STATUS3;
}
if((descSetup.bIndexL & 0x8f) == 0x03){
ep0State=EP0_GET_STATUS4;
}
break;
default:
break;
}
break;
/* 设置描述符 */
case SET_DESCRIPTOR:
CLR_EP0_OUTPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
/* 设置特性 */
case SET_FEATURE:
switch (descSetup.bmRequestType)
{
case DEVICE_RECIPIENT:
if (descSetup.bValueL == 1)
Rwuen = TRUE;
break;
case ENDPOINT_RECIPIENT:
if (descSetup.bValueL == 0)
{
if((descSetup.bIndexL & 0x7f) == 0x00){
StatusGet.Endpoint0= 1;
}
if((descSetup.bIndexL & 0x8f) == 0x81){
StatusGet.Endpoint1= 1;
}
if((descSetup.bIndexL & 0x8f) == 0x03){
StatusGet.Endpoint3= 1;
}
}
break;
default:
break;
}
CLR_EP0_OUTPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
/* 设置接口 */
case SET_INTERFACE:
InterfaceGet.AlternateSetting= descSetup.bValueL;
CLR_EP0_OUTPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
/* 同步frame */
case SYNCH_FRAME:
ep0State=EP0_STATE_INIT;
break;
default:
CLR_EP0_OUTPKTRDY_DATAEND(); //Because of no data control transfers.
ep0State=EP0_STATE_INIT;
break;
}
}
switch(ep0State)
{
case EP0_STATE_INIT:
break;
/*
发送设备描述符,总长 8+8+2 个字节
端点0 最大包大小8字节,因此要分三次发送
每次发送完成,mcu 置位 IN_PKT_RDY(packet ready)
USB发送完成时,USB将IN_PKT_RDY清零
如果是最后一个数据包,IN_PKT_RDY置位的同时还要将 DATA_END 置位
*/
case EP0_STATE_GD_DEV_0:
WrPktEp0((U8 *)&descDev+0,8); //EP0_PKT_SIZE
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_DEV_1;
break;
case EP0_STATE_GD_DEV_1:
WrPktEp0((U8 *)&descDev+0x8,8);
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_DEV_2;
break;
case EP0_STATE_GD_DEV_2:
WrPktEp0((U8 *)&descDev+0x10,2);//8+8+2=0x12
SET_EP0_INPKTRDY_DATAEND(); //包含了SET_EP0_IN_PKT_RDY()
ep0State=EP0_STATE_INIT;
break;
//=== GET_DESCRIPTOR:CONFIGURATION+INTERFACE+ENDPOINT0+ENDPOINT1 ===
//Windows98 gets these 4 descriptors all together by issuing only a request.
//Windows2000 gets each descriptor seperately.
/*
读取配置描述符,一次全部读取(包括该配置下的接口设置端点描述符)
如果请求的长度(wLength)大于实际返回的数据长度(SendLength),
并且返回的数据包长度是端点最大包长的整数倍时,需要返回0长度数据包,否则不返回0长度数据包
这个问题很容易理解的,你从 host 的角度来看,要求了 18 字节,这是在 setup 阶段,
而实际的数据传输在 data 阶段,我们来分析一下 data 阶段:
transcation 1:
host 发出 in token,device 收到后发出 8 字节数据(第 0 ~ 7 字节)。
transcation 2:
host 收到数据后,继续发出 in token,device 收到后发出 8 字节数据(第 8 ~ 15 字节)。
到此,device 已把 16 字节数据全部发送完毕,但 host 要求的是 18 字节,而收到了 16 字节,
显然 host 认为数据没有传输完,于是:
transcation 3:
host 继续发出 in token,device 收到 in token,好,我们在此停住。
你认为应该现在 device 应该如何响应?忽略这个 in token?好,先这样假设吧,那么 host 会重试数次,
然后报错。这样显然不行。如何通知 host 数据已经完成呢?为此,USB 协议规定,device 必须发一个
zero length packet,host 收到后,就会知道没有更多的数据了。
*/
case EP0_STATE_GD_CFG_0:
WrPktEp0((U8 *)&descConf+0,8); //EP0_PKT_SIZE
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_CFG_1;
break;
case EP0_STATE_GD_CFG_1:
WrPktEp0((U8 *)&descConf+8,1);
WrPktEp0((U8 *)&descIf+0,7);
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_CFG_2;
break;
case EP0_STATE_GD_CFG_2:
WrPktEp0((U8 *)&descIf+7,2);
WrPktEp0((U8 *)&descEndpt0+0,6);
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_CFG_3;
break;
case EP0_STATE_GD_CFG_3:
WrPktEp0((U8 *)&descEndpt0+6,1);
WrPktEp0((U8 *)&descEndpt1+0,7); //2440的端点3,看地址
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_CFG_4;
break;
case EP0_STATE_GD_CFG_4:
//zero length data packit,个人认为不是很恰当
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
//=== GET_DESCRIPTOR:CONFIGURATION ONLY===
/*
多次少量的获取配置描述符
*/
case EP0_STATE_GD_CFG_ONLY_0:
WrPktEp0((U8 *)&descConf+0,8); //EP0_PKT_SIZE
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_CFG_ONLY_1;
break;
case EP0_STATE_GD_CFG_ONLY_1:
WrPktEp0((U8 *)&descConf+8,1);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
//=== GET_DESCRIPTOR:INTERFACE ONLY===
case EP0_STATE_GD_IF_ONLY_0:
WrPktEp0((U8 *)&descIf+0,8);
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_IF_ONLY_1;
break;
case EP0_STATE_GD_IF_ONLY_1:
WrPktEp0((U8 *)&descIf+8,1);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
//=== GET_DESCRIPTOR:ENDPOINT 0 ONLY===
case EP0_STATE_GD_EP0_ONLY_0:
WrPktEp0((U8 *)&descEndpt0+0,7);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
//=== GET_DESCRIPTOR:ENDPOINT 1 ONLY===
case EP0_STATE_GD_EP1_ONLY_0:
WrPktEp0((U8 *)&descEndpt1+0,7); //2440的端点3,看地址
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
case EP0_INTERFACE_GET:
WrPktEp0((U8 *)&InterfaceGet+0,1);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
/*
字符串描述符
*/
case EP0_STATE_GD_STR_I0:
WrPktEp0((U8 *)descStr0, 4 );
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
ep0SubState=0;
break;
case EP0_STATE_GD_STR_I1:
if( (ep0SubState*EP0_PKT_SIZE+EP0_PKT_SIZE)<sizeof(descStr1) )
{
WrPktEp0((U8 *)descStr1+(ep0SubState*EP0_PKT_SIZE),EP0_PKT_SIZE);
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_STR_I1;
ep0SubState++;
}
else
{
WrPktEp0((U8 *)descStr1+(ep0SubState*EP0_PKT_SIZE),
sizeof(descStr1)-(ep0SubState*EP0_PKT_SIZE));
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
ep0SubState=0;
}
break;
case EP0_STATE_GD_STR_I2:
if( (ep0SubState*EP0_PKT_SIZE+EP0_PKT_SIZE)<sizeof(descStr2) )
{
WrPktEp0((U8 *)descStr2+(ep0SubState*EP0_PKT_SIZE),EP0_PKT_SIZE);
SET_EP0_IN_PKT_RDY();
ep0State=EP0_STATE_GD_STR_I2;
ep0SubState++;
}
else
{
WrPktEp0((U8 *)descStr2+(ep0SubState*EP0_PKT_SIZE),
sizeof(descStr2)-(ep0SubState*EP0_PKT_SIZE));
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
ep0SubState=0;
}
break;
/*
发送配置
*/
case EP0_CONFIG_SET:
WrPktEp0((U8 *)&ConfigSet+0,1);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
/*
状态
*/
case EP0_GET_STATUS0:
WrPktEp0((U8 *)&StatusGet+0,1);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
case EP0_GET_STATUS1:
WrPktEp0((U8 *)&StatusGet+1,1);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
case EP0_GET_STATUS2:
WrPktEp0((U8 *)&StatusGet+2,1);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
case EP0_GET_STATUS3:
WrPktEp0((U8 *)&StatusGet+3,1);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
case EP0_GET_STATUS4:
WrPktEp0((U8 *)&StatusGet+4,1);
SET_EP0_INPKTRDY_DATAEND();
ep0State=EP0_STATE_INIT;
break;
default:
break;
}
}
端点0为控制传输,上述都是标准的 usb request ,可以参考 usb 协议的第九章来分析。
这时,我们再来看看 main 函数中的 usb_receive 函数
u32 usb_receive()
{
int first = 1;
U8 tempMem[8];
U32 j;
unsigned int dwRecvTimeSec = 0;
struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
//先指向临时buffer存放前8byte,根据前8字节的内容决定目的地址
downloadAddress = (U32)tempMem; //_RAM_STARTADDRESS;
downPt = (unsigned char *)downloadAddress;
downloadFileSize = 0;
if(isUsbdSetConfiguration == 0)
{
printk("USB host is not connected yet.\n");
}
//等待配置以及文件传输,downloadFileSize 在中断3的处理函数中赋值
while(downloadFileSize == 0) /* wait until send a file */
{
if(first == 1 && isUsbdSetConfiguration != 0)
{
printk("USB host is connected. Waiting a download.\n");
first = 0;
}
}
/* 后面的先不分析 */
}
这里是接收函数,downloadFileSize 刚开始为 0,因此会停留在 while 循环那里,那么 downloadFileSize 在哪里赋值呢?数据包大小是在第一个数据包到来时得到,也就是在端点3的中断处理函数中。
void Ep3Handler(void)
{
struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
U8 out_csr3;
int fifoCnt;
writeb(3, &usbdevregs->INDEX_REG);
out_csr3 = readb(&usbdevregs->OUT_CSR1_REG);
if(out_csr3 & EPO_OUT_PKT_READY)
{
fifoCnt = readb(&usbdevregs->OUT_FIFO_CNT1_REG);
if(downloadFileSize == 0)//开始默认为0
{
//将前8字节读到16字节临时数组中(此时未开启DMA)
RdPktEp3((U8 *)downPt,8);
/*
dnw 传输1-4字节为下载地址
dnw 传输5-8字节为数据长度
dnw 每次传输32字节
*/
downloadAddress = *((U8 *)(downPt+0)) +
(*((U8 *)(downPt+1))<<8) +
(*((U8 *)(downPt+2))<<16)+
(*((U8 *)(downPt+3))<<24);
dwUSBBufWritePtr = downloadAddress;
//数据大小
downloadFileSize = *((U8 *)(downPt+4)) +
(*((U8 *)(downPt+5)) << 8 )+
(*((U8 *)(downPt+6)) << 16)+
(*((U8 *)(downPt+7)) << 24);
checkSum = 0;
downPt = (U8 *)downloadAddress;
//读取FIFO中剩下的数据,并计算校验和
RdPktEp3_CheckSum((U8 *)downPt, fifoCnt - 8); //The first 8-bytes are deleted.
downPt += fifoCnt-8;
#if USBDMA
//屏蔽USBD中断,此时先返回去初始化DMA,然后在 CLR_EP3_OUT_PKT_READY()进行下一次传输
writel((readl(&intregs->INTMSK) | BIT_USBD), &intregs->INTMSK);
return;
#endif
}
else
{
#if USBDMA
printk("<ERROR>");
#endif
RdPktEp3_CheckSum((U8 *)downPt,fifoCnt);
downPt+=fifoCnt; //fifoCnt=64
}
CLR_EP3_OUT_PKT_READY();
return;
}
//I think that EPO_SENT_STALL will not be set to 1.
if(out_csr3 & EPO_SENT_STALL)
{
printk("[STALL]");
CLR_EP3_SENT_STALL();
return;
}
}
下面继续看 usb_receive 函数
u32 usb_receive()
{
....
//等待配置以及文件传输,downloadFileSize 在中断3的处理函数中赋值
while(downloadFileSize == 0) /* wait until send a file */
{
if(first == 1 && isUsbdSetConfiguration != 0)
{
printk("USB host is connected. Waiting a download.\n");
first = 0;
}
}
printk("get downloadFileSize = %d !!\n", downloadFileSize);
Timer_InitEx();
Timer_StartEx();
#if USBDMA
//清除dma2通道中断屏蔽
writel((readl(&intregs->INTMSK) & ~(BIT_DMA2)), &intregs->INTMSK);
if(downloadFileSize > EP3_PKT_SIZE)
{
if(downloadFileSize - EP3_PKT_SIZE <= (0x80000))
{
/* set the source and length */
dwUSBBufWritePtr = downloadAddress + EP3_PKT_SIZE - 8;
dwWillDMACnt = downloadFileSize - EP3_PKT_SIZE;
}
else
{
dwUSBBufWritePtr = downloadAddress + EP3_PKT_SIZE - 8;
// dwWillDMACnt = 0x80000 - EP3_PKT_SIZE;
//第一次DMA传输小于512K,后面的传输为整512K
dwWillDMACnt = 0x80000 + 8 - EP3_PKT_SIZE;
}
totalDmaCount = 0;
printk("current addr %x len %d\n", dwUSBBufWritePtr, dwWillDMACnt);
ConfigEp3DmaMode(dwUSBBufWritePtr, dwWillDMACnt);
ClearEp3OutPktReady();
}
else
{
dwUSBBufWritePtr = downloadAddress + downloadFileSize - 8;
totalDmaCount = downloadFileSize;
}
#endif
printk("\nNow, Downloading [ADDRESS:%xh,TOTAL:%d]\n", downloadAddress, downloadFileSize);
while (totalDmaCount != downloadFileSize)
{
;//等待传输完成
}
printk("totalDmaCount %d \n", totalDmaCount);
dwRecvTimeSec = Timer_StopEx();
if (dwRecvTimeSec == 0)
{
dwRecvTimeSec = 1;
}
printk("(%dKB/S, %dS)\n", (downloadFileSize/dwRecvTimeSec/1024), dwRecvTimeSec);
return downloadFileSize - 10;
}
重点是里边的 ConfigEp3DmaMode 函数,配置dma并启动dma传输,来接收后续的数据包。同时屏蔽了 usbd 中断,因为我们不需要再使用端点3的中断处理函数自己读fifo了嘛,dma帮我们处理,我们需要关心的就是 dma 的中断处理函数了。
void ConfigEp3DmaMode(U32 bufAddr, U32 count)
{
char j;
int i;
struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
struct s3c24x0_dmas * const dmaregs = s3c24x0_get_base_dmas();
//端点3
writeb(3, &usbdevregs->INDEX_REG);
count &= 0xfffff; //transfer size should be <1MB
//配置DMA2,数据源位于外部总线,每次传输完地址保持不变,TC到达0时才发生中断
writel(((1<<1) | (1<<0)), &dmaregs->dma[2].DISRCC);
//数据源起始地址为 ENP3 FIFO
writel(ADDR_EP3_FIFO, &dmaregs->dma[2].DISRC);
//配置DMA2,数据目的位于内部,每次传输完成地址增加
writel(((0<<1) | (0<<0)), &dmaregs->dma[2].DIDSTC);
//数据目的地 bufAddr
writel(bufAddr, &dmaregs->dma[2].DIDST); //dst=AHB,increase,dst=bufAddr
#if USBDMA_DEMAND
writel(((count)|(0<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(4<<24)|(1<<23)|(0<<22)|(0<<20)), &dmaregs->dma[2].DCON);
//demand,requestor=APB,CURR_TC int enable,unit transfer,
//single service,src=USBD,H/W request,autoreload,byte,CURR_TC
#else
/* changed by thisway.diy to disable autoreload */
writel(((count)|(1<<31)|(0<<30)|(1<<29)|(0<<28)|(0<<27)|(4<<24)|(1<<23)|(1<<22)|(0<<20)), &dmaregs->dma[2].DCON);
//handshake,requestor=APB,CURR_TC int enable,unit transfer,
//single service,src=USBD,H/W request,autoreload,byte,CURR_TC
#endif
//打开dm2通道
writel((1<<1), &dmaregs->dma[2].DMASKTRIG);
//共计传输1M
writeb(0xff, &usbdevregs->ep3.EP_DMA_TTC_L);
writeb(0xff, &usbdevregs->ep3.EP_DMA_TTC_M);
writeb(0x0f, &usbdevregs->ep3.EP_DMA_TTC_H);
//设置 OUT_PKT_RDY 自动清除,开 dma 中断
writeb((readb(&usbdevregs->OUT_CSR2_REG) | EPO_AUTO_CLR | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG);
#if USBDMA_DEMAND
writeb(EP3_PKT_SIZE, &usbdevregs->ep3.EP_DMA_TTC_H);
writeb((UDMA_DEMAND_MODE | UDMA_OUT_DMA_RUN | UDMA_DMA_MODE_EN), &usbdevregs->ep3.EP_DMA_CON);
// deamnd enable,out_dma_run=run,in_dma_run=stop,DMA mode enable
#else
//启动DMA传输,传输完成通道关闭
writeb(0x01, &usbdevregs->ep3.EP_DMA_UNIT);
writeb((UDMA_OUT_DMA_RUN | UDMA_DMA_MODE_EN), &usbdevregs->ep3.EP_DMA_CON);
#endif
//wait until DMA_CON is effective.
j = readb(&usbdevregs->ep3.EP_DMA_CON);
for(i=0;i<10;i++);
/* 在dma中断处理函数中如果数据未传输完毕,还会调用该函数重新配置DMA进行传输512K */
}
void IsrDma2(void)
{
struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
U8 out_csr3;
U32 dwEmptyCnt;
U8 saveIndexReg = readb(&usbdevregs->INDEX_REG);
writeb(3, &usbdevregs->INDEX_REG);
out_csr3 = readb(&usbdevregs->OUT_CSR1_REG);
ClearPending_my((int)BIT_DMA2);
/* thisway.diy, 2006.06.22
* When the first DMA interrupt happened, it has received max (0x80000 + EP3_PKT_SIZE) bytes data from PC
*/
if (!totalDmaCount) //第一次执行这个分支
totalDmaCount = dwWillDMACnt + EP3_PKT_SIZE;
else
totalDmaCount += dwWillDMACnt;
//不考虑8M限制的话,就是 dwUSBBufWritePtr = dwUSBBufWritePtr + dwWillDMACnt
//dwUSBBufWritePtr = ((dwUSBBufWritePtr + dwWillDMACnt - dwUSBBufBase) % dwUSBBufSize) + dwUSBBufBase;
dwUSBBufWritePtr = dwUSBBufWritePtr + dwWillDMACnt;
if(totalDmaCount >= downloadFileSize) // is last?
{
totalDmaCount = downloadFileSize;
//做些收尾工作,并不是进入init模式
ConfigEp3IntMode();
if(out_csr3 & EPO_OUT_PKT_READY)
{
CLR_EP3_OUT_PKT_READY();
}
//屏蔽DMA中断,开启USBD中断
writel(((readl(&intregs->INTMSK) | BIT_DMA2) & ~(BIT_USBD)), &intregs->INTMSK);
}
else
{
if((totalDmaCount + 0x80000 )< downloadFileSize)
{
dwWillDMACnt = 0x80000; //再传512K
}
else
{
dwWillDMACnt = downloadFileSize - totalDmaCount;
}
//继续传输
printk("current addr %x len %d\n", dwUSBBufWritePtr, dwWillDMACnt);
ConfigEp3DmaMode(dwUSBBufWritePtr, dwWillDMACnt);
}
writeb(saveIndexReg, &usbdevregs->INDEX_REG);
}