uboot dnw 源码分析

    xiaoxiao2021-12-14  17

      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); }
    转载请注明原文地址: https://ju.6miu.com/read-971048.html

    最新回复(0)