uboot dnw 源码分析

11 篇文章 11 订阅

  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);
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: u-boot是一个开源的引导加载程序,用于嵌入式系统的启动。它通常嵌入在芯片的ROM或闪存中,是系统的第一个执行程序,负责初始化硬件、加载操作系统和其他应用程序。 u-boot源码是以C语言编写的,具有高度可移植性。它提供了一系列的驱动程序和命令行工具,可以在开发板上进行硬件初始化和测试。 源码的结构分为几个重要的部分:启动代码、中断向量表、初始化代码以及其他功能模块。启动代码是u-boot执行的入口点,在这个阶段,它会初始化一些必要的硬件设备,例如串口、存储器等,同时也会设置中断向量表。 中断向量表是一个由硬件中断信号触发的函数指针数组,u-boot将中断信号与相应的函数关联起来,以便在发生中断时进行相应的处理。 初始化代码是u-boot执行的核心部分,它会通过配置文件或环境变量来读取系统设置,并进行相应的初始化。例如,它会加载并运行操作系统内核镜像,设置内存映射表,配置设备和网络接口等。 此外,u-boot还提供了一些功能模块,例如命令行解析器、文件系统支持、网络协议栈等。这些功能模块可以通过命令行进行操作,以便用户对嵌入式系统进行配置、调试和测试。 对于研究和分析u-boot源码,可以从以下几个方面入手: 1. 启动流程:了解u-boot是如何从ROM或闪存中加载到内存并执行的,包括启动代码和中断向量表的设置。 2. 硬件初始化:了解u-boot是如何初始化硬件设备的,包括串口、存储器、网络接口等。 3. 配置文件和环境变量:了解u-boot是如何通过配置文件或环境变量来读取系统设置的,以及如何进行相应的初始化。 4. 功能模块:了解u-boot提供的功能模块,例如命令行解析器、文件系统支持、网络协议栈等,以及它们的实现方式和使用方法。 通过对u-boot源码的详细分析,可以深入了解嵌入式系统的引导过程、硬件初始化和驱动程序的编写,从而提高嵌入式系统的开发和调试能力。 ### 回答2: Uboot是一种开源的引导加载程序,用于嵌入式系统的启动。它是一个简单而灵活的软件,可以在各种硬件平台上使用,并提供了许多功能和驱动程序。 首先,Uboot的主要功能是加载和运行操作系统。它通过读取存储介质上的引导扇区,将操作系统加载到内存中并启动。此外,Uboot还提供了命令行界面,用户可以在启动过程中进行配置和控制。 Uboot的源代码由若干模块组成,包括引导代码、设备驱动程序、命令行解析器等。其中,引导代码是最关键的部分,负责在硬件启动时初始化系统和设备,并在引导过程中进行加载和启动操作系统。设备驱动程序用于访问硬件设备,例如存储介质、串口等。命令行解析器则负责解析用户输入的命令,并执行相应的操作。 在Uboot的源代码中,可以找到各种初始化和设置函数,以及与硬件平台相关的代码。这些代码通常是与硬件设备的寄存器交互,进行硬件初始化和配置。此外,还有一些与引导过程和加载操作系统相关的代码,用于读取、解析和加载引导扇区以及操作系统镜像。 总的来说,Uboot源码详细分析涉及到引导代码、设备驱动程序和命令行解析器等多个模块。在分析过程中,需要理解硬件平台的相关知识和操作系统的启动流程,并深入了解Uboot的代码实现和功能。只有这样,才能对Uboot源码有一个全面的理解,并能根据需求进行相应的修改和定制。 ### 回答3: U-Boot是一款开源的引导加载程序,用于嵌入式系统中启动操作系统。它是最常用的引导加载程序之一,具有广泛的应用。下面,我将对U-Boot源码进行详细分析U-Boot源码位于一个git仓库中,可以通过clone仓库获取源码源码的结构清晰,主要分为三个部分:板级支持包(board support package,BSP),引导和命令。 BSP包含了与硬件相关的代码和配置文件,用于支持不同的硬件平台。其中,包括设备初始化、设备驱动程序和硬件设置等。这些代码主要包括处理器启动代码、时钟初始化、内存初始化以及设备和外设的配置等。 引导部分是U-Boot的核心,其中包括引导过程的各个阶段。首先,它加载引导扇区和主引导程序,其中包括引导加载器。引导加载器根据设备的启动模式选择适当的引导方式。然后,它会加载内核映像和根文件系统,并将控制权转移到内核。 最后,命令部分包含了一系列的命令,用于与用户进行交互。这些命令可以用于启动操作系统、进行系统设置和调试等。U-Boot提供了丰富的命令集,包括boot、setenv、saveenv、printenv等等。 在分析U-Boot源码时,需要了解硬件平台的特性和配置文件。可以根据目标硬件平台的手册和数据手册,对源码进行逐步分析和调试。在分析过程中,可以使用调试工具进行跟踪、断点和单步调试,以便更好地理解源码的执行过程。 总的来说,U-Boot源码的详细分析需要涉及到硬件平台的特性和配置文件,并对引导加载过程和命令解析进行深入研究。通过对U-Boot源码的理解和分析,可以为嵌入式系统的启动和操作提供更好的支持和定制化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值