S3C2440 USB 设备控制器

原创 2012年03月29日 10:04:12

S3C2440 USB 设备控制器

        s3c2440 soc集成了一个usb1.1设备控制器,可以进行全速/低速的控制,中断与批量传输。除了端点0,具有四个端点,每个端点都可以作为中断与批量的端点,每个端点具有128 byte的FIFO,所以端点最大packet可以设置成128byte。并且支持DMA传输。任何一种设备控制器对于软件来说都是一组寄存器:数据,状态,控制。usb 设备控制器也不例外。设置好相应的控制寄存器,并且在数据来时读取数据寄存器,需要发送数据的时候将数据写入输出寄存器。而这种数据的通信建立在对状态寄存器的读取上,往往还会有中断与DMA的操作。s3c2440 usb设备控制器的寄存器分为以下几组:

(1):电源管理寄存器
        PWR_REG 负责USB设备挂起等电源设置
(2):地址寄存器
        存储USB设备的地址,当主机枚举设备设备的时候设置
(3):中断控制寄存器
        EP_INT_REG    端点中断状态寄存器,每当一个端点事件发生的时候,相应的位就会置1
        USB_INT_REG   设备中断状态寄存器,主要有三个中断:唤醒,复位,挂起
        EP_INT_EN_REG  端点中断使能寄存器
        EP_INT_EN_REG  设备中断使能寄存器
(4):编号寄存器
        因为USB 设备控制器有五个端点,并且五个端点寄存器大同小异,所以硬件设计上使用了编号寄存器:名字相同但物理寄存器不同。有一个INDEX_REG寄存器,它里面的值指示了具体的哪组物理寄存器。这样的寄存器有七个分别是:
        MAXP_REG: 端点最大信息包大小
        IN_CSR1_REG
        IN_CSR2_REG
        OUT_CSR1_REG
        OUT_CSR2_REG
        OUT_FIFO_CNT1_REG
        OUT_FIFO_CNT2_REG
(5):FIFO寄存器
        EPO_FIFO_REG
        EP1_FIFO_REG
        EP2_FIFO_REG
        EP3_FIFO_REG
        EP4_FIFO_REG
(6):DMA寄存器
        端点1~4,每个端点六个,用于设置端点的DMA传输。
        USB设备控制器处理了大部分的USB传输细节,并产生相应的中断。所以对USB设备控制器的编程,最关键的部分就是中断处理的部分。比如端点每收到一个token以及后面的数据包,就会产生相应的中断。软件只需要在中断处理程序中读取数据,并且清除中断标志。USB设备控制器就会自动发送应答包。现在网上比较流行的操作s3c2440 usb 设备控制器的程序就是移植到u-boot里面的usb下载程序。因为在u-boot的环境下所以调试起来没有在裸机上来的方便,所以我将这个程序移植到了裸机上,编译环境arm-linux-gcc。下面大体介绍以下这个程序的流程:

        这个程序主要是完成了USB设备的枚举,与批量OUT传输,并且开启了DMA,OUT传输用的端点是端点3。这个程序首先从init_usb_slave() 开始:这个函数主要是设置usb slave的引脚以及控制寄存器,设置中断处理程序的入口,以及开启中断。

void usb_init_slave(void)
{
        struct s3c24x0_gpio * const gpioregs = s3c24x0_get_base_gpio();
        char *mode;

        Delay(10);

        Usb_Isr_Init();  
      //设置中断处理程序的入口,需要两个中断USBD与DMA2,USBD用于处理USB事务,DMA2用于处理DMA传输
         writel((readl(&gpioregs->MISCCR) & ~((1<<3) | (1<<13))), &gpioregs->MISCCR);
// USBD is selected instead of USBH1 
// USB port 1 is enabled.
//
//  USBD should be initialized first of all.
//
        isUsbdSetConfiguration=0;

        UsbdMain(); //主要配置函数
        Delay(10);

        writel((readl(&gpioregs->GPCDAT) | (1<<5)), &gpioregs->GPCDAT);
        //这个操作和Mini2440开发板有关,在USB 设备的信号线上D+上接上了一个GPC5的一个上拉电阻,当GPC5输出高电平的时候,主机的集线器才能检测到设备,从而给复位信号

 /* enable USB Device, thisway.diy */
#if USBDMA
        mode="DMA";   
#else
        mode="Int";
#endif
        download_run=0; //The default menu is the Download & Run mode.
        printk("USB slave is enable!\n");
        //printk是我自己编写的打印函数
}
        在配置完设备后,给GPC5一个高电平,主机就开始枚举设备的过程了。能不能开始这个过程关键是看设备是否配置正确。这就是要看UsbMain()这个函数了。他在usbmain.c中,如下:

void UsbdMain(void)
{
    InitDescriptorTable();
    ConfigUsbd(); 
    PrepareEp1Fifo(); 
}
        这个函数挺简单,但是调用了三个函数可不简单。第一个就是初始化一些USB描述符,比如设备描述符,配置描述符等,在usb控制传输的时候返回给设备。这个函数在usbsetup.c中。PrepareEp1Fifo()是配置端点1的,这里没有用到。关键的是ConfigUsbd()函数,这个函数是最主要的配置函数。在usblib.c中:

void ConfigUsbd(void)
{
        struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
        ReconfigUsbd();
        writel((readl(&intregs->INTMSK) & ~(BIT_USBD)), &intregs->INTMSK);
}
        这个函数是个封装函数,第一次配置调用他,并且开启中断。在重置USB的过程中调用的是ReconfigUsbd(),这个函数也在usblib.c中:
void ReconfigUsbd(void)
{
// *** End point information ***
//   EP0: control
//   EP1: bulk in end point
//   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); //disable suspend mode
	writeb(0, &usbdevregs->INDEX_REG); 
	writeb(FIFO_SIZE_8, &usbdevregs->MAXP_REG); //EP0 max packit size = 8 
	writeb((EP0_SERVICED_OUT_PKT_RDY | EP0_SERVICED_SETUP_END), & usbdevregs->EP0_CSR_IN_CSR1_REG); //EP0:clear OUT_PKT_RDY & SETUP_END
	writeb(1, &usbdevregs->INDEX_REG); 

#if (EP1_PKT_SIZE==32)
	writeb(FIFO_SIZE_32, &usbdevregs->MAXP_REG); //EP1:max packit size = 32
#else
	writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG); 	//EP1:max packit size = 64
#endif	
	writeb((EPI_FIFO_FLUSH | EPI_CDT), &usbdevregs->EP0_CSR_IN_CSR1_REG); 
	writeb((EPI_MODE_IN | EPI_IN_DMA_INT_MASK | EPI_BULK), &usbdevregs->IN_CSR2_REG);  //IN mode, IN_DMA_INT=masked    
	writeb(EPO_CDT, &usbdevregs->OUT_CSR1_REG); 
	writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG); 

	writeb(2, &usbdevregs->INDEX_REG); 
	writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG); //EP2:max packit size = 64
	writeb((EPI_FIFO_FLUSH | EPI_CDT | EPI_BULK), &usbdevregs->EP0_CSR_IN_CSR1_REG); 
	writeb((EPI_MODE_IN | EPI_IN_DMA_INT_MASK), &usbdevregs->IN_CSR2_REG);  //IN mode, IN_DMA_INT=masked    
	writeb(EPO_CDT, &usbdevregs->OUT_CSR1_REG); 
	writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG); 

	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); 	
	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); 
    	//clear OUT_PKT_RDY, data_toggle_bit.
	//The data toggle bit should be cleared when initialization.
	writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG); 

	writeb(4, &usbdevregs->INDEX_REG); 
	writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG); //EP4:max packit size = 64
	writeb((EPI_FIFO_FLUSH | EPI_CDT | EPI_BULK), &usbdevregs->EP0_CSR_IN_CSR1_REG); 
	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); 
    	//clear OUT_PKT_RDY, data_toggle_bit.
	//The data toggle bit should be cleared when initialization.
	writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG); 
    
	writeb((EP0_INT | EP1_INT | EP2_INT | EP3_INT | EP4_INT), &usbdevregs->EP_INT_REG); 
	writeb((RESET_INT | SUSPEND_INT | RESUME_INT), &usbdevregs->USB_INT_REG); 
    	//Clear all usbd pending bits
    	
	writeb((EP0_INT | EP1_INT | EP3_INT), &usbdevregs->EP_INT_EN_REG); 
	writeb(RESET_INT, &usbdevregs->USB_INT_EN_REG); 
        ep0State = EP0_STATE_INIT;
    
}
        可以看出首先是操作电源管理寄存器关闭自动挂起功能,然后就是针对每个端点来配置,主要设置端点的最大信息包的大小,端点类型,传输方向。以及是否支持DMA。最主要的是端点0与端点3因为其他三个端点没有用到。最后清除所有的中断状态寄存器的标志。
        usb 设备控制器最主要的部分就是中断处理程序,本程序中的USB中断处理程序是void IsrUsbd(void),在usbmain.c中:

void IsrUsbd(void)
{
    struct s3c24x0_usb_device * const usbdevregs	= s3c24x0_get_base_usb_device();
    U8 usbdIntpnd,epIntpnd;
    U8 saveIndexReg = readb(&usbdevregs->INDEX_REG);

    usbdIntpnd = readb(&usbdevregs->USB_INT_REG);
    epIntpnd = readb(&usbdevregs->EP_INT_REG);
//    printk( "[INT:EP_I=%x,USBI=%x]",epIntpnd,usbdIntpnd );
    if(usbdIntpnd&SUSPEND_INT)
    {
	writeb(SUSPEND_INT, &usbdevregs->USB_INT_REG);
    //	printk( "<SUS]");
    }
    if(usbdIntpnd&RESUME_INT)
    {
	writeb(RESUME_INT, &usbdevregs->USB_INT_REG);
    //	printk("<RSM]");
    }
    if(usbdIntpnd&RESET_INT)
    {
    //	printk( "<RST] ReconfigUsbd");  
    	
    	//ResetUsbd();
    	ReconfigUsbd();
	writeb(RESET_INT, &usbdevregs->USB_INT_REG); //RESET_INT should be cleared after ResetUsbd().   	
        PrepareEp1Fifo(); 
    }
    //以上三个是USB设备中断,主要用到的是复位中断,当GPC5引脚为高时,USB 插入主机,就会产生这个中断
    if(epIntpnd&EP0_INT)
    {
	writeb(EP0_INT, &usbdevregs->EP_INT_REG);
    	Ep0Handler();
    }
    //对于每个端点中断都有不同的处理程序,这个是端点0的处理程序。在设备枚举的过程中使用。
    if(epIntpnd&EP1_INT)
    {
	writeb(EP1_INT, &usbdevregs->EP_INT_REG);
    	Ep1Handler();
    }
    
    if(epIntpnd&EP2_INT)
    {
	writeb(EP2_INT, &usbdevregs->EP_INT_REG);
    //	printk("<2:TBD]\n");   //not implemented yet	
    	//Ep2Handler();
    }

    if(epIntpnd&EP3_INT)
    {
	writeb(EP3_INT, &usbdevregs->EP_INT_REG);
	printk("Ep3hander\n");
    	Ep3Handler();
    }

    if(epIntpnd&EP4_INT)
    {
	writeb(EP4_INT, &usbdevregs->EP_INT_REG);
  //  	printk("<4:TBD]\n");   //not implemented yet	
    	//Ep4Handler();
    }

    ClearPending_my((int)BIT_USBD);	 
	writeb(saveIndexReg, &usbdevregs->INDEX_REG);    
}
        USB 最重要要的是时序,打印函数是很费时间的,所以如果在USB中断处理程序中加入打印函数,有可能造成总线超时,从而使得USB传输不能成功。下面针对USB设备枚举来说明以下控制传输,相应的中断处理程序Ep0Handler(),在usbsetup.c中:
        这个函数比较长,我就不贴代码了。这个函数主要就是针对中断的类型类进行相应的处理:
        writeb(0, &usbdevregs->INDEX_REG);
        ep0_csr = readb(&usbdevregs->EP0_CSR_IN_CSR1_REG);
        EP0_CSR_IN_CSR1_REG寄存器里面有相应端点的相应中断类型。其中有两种中断是不正常状态,EP0_SETUP_END这个标志表示,控制传输,SETUP阶段在数据包到来前,传输就结束了。造成这个原因有可能是总线超时,如果主机在送出SETUP包以及数据包之后,USB设备迟迟不发送应答包。EP0_SENT_STALL 当设备不正常的时候这个标志会置位。
        EP0_OUT_PKT_READY 这个标志是正常的状态,在USB设备接收到了一个正确的Token以及数据包是,这个标志就置位,从而程序可以从FIFO中读取数据根据setup包中根据请求类型来进行相应的处理。然后清除这个标志。USB设备控制器就会自动发送ACK包给主机,从而结束一个事务。类似,程序用到用于下载的OUT批量传输端点3的中断处理程序Ep3Handler也是这样一个流程。但是不同的是这里还用到了DMA传输。
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);
    
    printk("<3:%x]",out_csr3);

    if(out_csr3 & EPO_OUT_PKT_READY)
    {  
	printk("EPO_OUT_PKT_READ\n"); 
	fifoCnt = readb(&usbdevregs->OUT_FIFO_CNT1_REG);
#if 0
	RdPktEp3(ep3Buf,fifoCnt);
	PrintEpoPkt(ep3Buf,fifoCnt);
#else

	if(downloadFileSize==0)
	{
   	    RdPktEp3((U8 *)downPt,8); 	
   	    
   	    if(download_run==0)
   	    {
		    downloadAddress=tempDownloadAddress;
	    }
	    else
	    {
	    	downloadAddress=
	    		*((U8 *)(downPt+0))+
			(*((U8 *)(downPt+1))<<8)+
			(*((U8 *)(downPt+2))<<16)+
			(*((U8 *)(downPt+3))<<24);
            
            dwUSBBufReadPtr = downloadAddress;
            dwUSBBufWritePtr = downloadAddress;
	    }
	    downloadFileSize=
	    	*((U8 *)(downPt+4))+
		(*((U8 *)(downPt+5))<<8)+
		(*((U8 *)(downPt+6))<<16)+
		(*((U8 *)(downPt+7))<<24);
	    checkSum=0;
	    downPt=(U8 *)downloadAddress;

  	    RdPktEp3_CheckSum((U8 *)downPt,fifoCnt-8); //The first 8-bytes are deleted.	    
  	    downPt+=fifoCnt-8;  
  	    
  	#if USBDMA
     	    //CLR_EP3_OUT_PKT_READY() is not executed. 
     	    //So, USBD may generate NAK until DMA2 is configured for USB_EP3;
		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
	}
#endif
   	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;
    }	
}
        可以看出中断状态EPO_OUT_PKT_READY标志置位就代表着一个数据包的到来,这里是处理第一个数据包,由于端点3配置的大小为32个字节,所以一个包的数据就是32个字节。因为主机软件DNW在每个文件的头部加了8个字节的信息来标示下载地址与文件长度,所以这个就是处理下载地址与文件长度的信息的。最后下载地址保存在downloadAddress里,文件长度保存在downloadFileSize里。处理完第一个包后,首先禁止USBD中断这个为了设置端点3的DMA功能。在中断禁止的时候,主机发送的数据包都会被USB设备忽略,从而主机会从新发送。貌似这个中断处理函数只处理一个包的数据就关了中断,USB批量传输就进行不下去了。但是别急,程序中还有一个函数是进行批量传输先运行的,这就是usb_receive:

u32 usb_receive(char *buf, size_t len, U32 wait)
{
	int first=1;
	U8 tempMem[16];
	U32 j;
	unsigned int dwRecvTimeSec = 0;
	char c;
	struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();

	dwUSBBufReadPtr = dwUSBBufBase; // USB_BUF_BASE; thiswa.diy, 2006.06.21
	dwUSBBufWritePtr = dwUSBBufBase; // USB_BUF_BASE; thiswa.diy, 2006.06.21
	bDMAPending = 0;

	/* add by thisway.diy */
	tempDownloadAddress = dwUSBBufBase; // USB_BUF_BASE; thiswa.diy, 2006.06.21 // RAM_BASE, changed by thisway.diy for wince, 2006.06.18

	downloadAddress=(U32)tempMem; //_RAM_STARTADDRESS; 
	downPt=(unsigned char *)downloadAddress;
	//This address is used for receiving first 8 byte.
	downloadFileSize=0;
    

    /*******************************/
    /*    File download    */
    /*******************************/
    if(isUsbdSetConfiguration==0)
    {
	    printk("USB host is not connected yet.\n");
    }

    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);

    /* add by thisway.diy */
    if (downloadFileSize - 10 > len)
    {
        printk("Length of file is too big : %d > %d\n", downloadFileSize - 10, len);
        return 0;
    }
    
    Timer_InitEx();
    Timer_StartEx();
        
#if USBDMA    

    writel((readl(&intregs->INTMSK) & ~(BIT_DMA2)), &intregs->INTMSK); 

    ClearEp3OutPktReady(); 
    	// indicate the first packit is processed.
    	// has been delayed for DMA2 cofiguration.

    if(downloadFileSize>EP3_PKT_SIZE) //如果下载文件的大小比端点FIFO大,得开启DMA功能
    {
        if(downloadFileSize - EP3_PKT_SIZE<=(0x80000)) //DMA每次传输的最大数据为1023kb,所以如果大于这个数就得分多次DMA传输
        {
            /* set the source and length */
            dwUSBBufWritePtr = downloadAddress + EP3_PKT_SIZE-8;
            dwWillDMACnt = downloadFileSize - EP3_PKT_SIZE;
	}
      	else //多次DMA传输
      	{
            dwUSBBufWritePtr = downloadAddress + EP3_PKT_SIZE - 8;
            // dwWillDMACnt = 0x80000 - EP3_PKT_SIZE;
            
            /* Changed by thisway.diy, 2006.06.22
             * We want When the first DMA interrupt happened, 
             * it has received (0x80000 + 8) bytes data from PC
             * The format of data PC send out is: <ADDR(DATA):4>+<SIZE(n+10):4>+<DATA:n>+<CS:2>
             * So, the first 8 bytes isn't the real data we want
             * We want the dwUSBBufWritePtr is always 0x80000 aligin
             */
            dwWillDMACnt = 0x80000 + 8 - EP3_PKT_SIZE;
    	}
     	totalDmaCount = 0;  //DMA传输的次数
  	ConfigEp3DmaMode(dwUSBBufWritePtr, dwWillDMACnt); //配置端点3DMA功能,然后设置地址,传输大小
    }
    else //如果下载文件的大小还没有端点FIFO大,那么没有必要开DMA了,加之第一个包已经处理了,所以直接更新写指针吧
    {
        dwUSBBufWritePtr = downloadAddress + downloadFileSize - 8;
	    totalDmaCount = downloadFileSize;
    }
#endif

    printk("\nNow, Downloading [ADDRESS:%xh,TOTAL:%d]\n",
    		downloadAddress,downloadFileSize);

    if (wait)
    {
        printk("RECEIVED FILE SIZE:%8d",0);

        j = totalDmaCount + 0x10000;
        while (totalDmaCount != downloadFileSize)
        {
            if (totalDmaCount > j)
            {
        	    printk("\b\b\b\b\b\b\b\b%8d", j);
                j = totalDmaCount + 0x10000;
            }
        }
	    printk("\b\b\b\b\b\b\b\b%8d ", totalDmaCount);
        dwRecvTimeSec = Timer_StopEx();
        if (dwRecvTimeSec == 0)
        {
            dwRecvTimeSec = 1;
        }
        printk("(%dKB/S, %dS)\n", (downloadFileSize/dwRecvTimeSec/1024), dwRecvTimeSec);
    }

    return downloadFileSize - 10;
}
        usb_receive函数紧接着usb_init_slave运行,如果主机枚举成功设备,那么就会设置downloadFileSize = 1,”USB host is connected. Waiting a download“就会输出到终端,从而执行以下的代码,否则就会等待USB设备配置完毕。头文件中是定义了USBDMA的,所以#if USBDMA 下面的代码就会执行。在说这段代码之前首先说一下Timer_InitEx(); Timer_StartEx();这两个函数的作用还有 if (wait)下面代码的作用,起始这些都是为了显示现在进度,以及下载平均耗时的,这里主要用了看门狗定时器作为定时硬件,如果定义了wait,那么程序会将下载信息显示在终端,然后还统计每秒传输的字节数。下面重点说明#if USBDMA下面的代码。
        这段代码的主要作用就是针对传输文件的大小来决定DMA的使用情况,如果使用DMA,则配置端点3为DMA模式,设置DMA传输的终点地址以及每次DMA传输的大小,开启DMA中断。当设置好DMA时,并打开了DMA中断,每传完一个设置的大小,就会进入DMA中断。所以如果传输文件的大小小于1023KB,那么只会进入一次DMA中断。下面就是DMA中断处理程序:
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; //第一次中断的时候,我们已经接收到了一个包才进入的DMA传输,所以要加一个包的大小
    else
        totalDmaCount+=dwWillDMACnt;


    dwUSBBufWritePtr = ((dwUSBBufWritePtr + dwWillDMACnt - dwUSBBufBase) % dwUSBBufSize) + dwUSBBufBase;
    //更新内存写指针
    if(totalDmaCount>=downloadFileSize)// 最后一次DMA传输完成
    {
    	totalDmaCount=downloadFileSize;
	
    	ConfigEp3IntMode(); //恢复端点3的设置	

    	if(out_csr3& EPO_OUT_PKT_READY)
    	{
       	    CLR_EP3_OUT_PKT_READY();
	}
	writel(((readl(&intregs->INTMSK) | BIT_DMA2) & ~(BIT_USBD)), &intregs->INTMSK);
        //禁止DMA中断,打开USBD中断
    }
    else
    {
    	if((totalDmaCount+0x80000)<downloadFileSize)	//下一次DMA不能传完文件
    	{
    	    dwWillDMACnt = 0x80000;
	}
    	else   //下一次DMA能够传完文件,那么就设置DMA传输的大小问剩下的字节数
    	{
    	    dwWillDMACnt = downloadFileSize - totalDmaCount;
    	}

        dwEmptyCnt = (dwUSBBufReadPtr - dwUSBBufWritePtr - 1 + dwUSBBufSize) % dwUSBBufSize;
        if (dwEmptyCnt >= dwWillDMACnt)
        {
    	    ConfigEp3DmaMode(dwUSBBufWritePtr, dwWillDMACnt);  //重新配置一下DMA传输大小以及地址
        }
        else
        {
            bDMAPending = 1;
        }
    }
	writeb(saveIndexReg, &usbdevregs->INDEX_REG);
}

        DMA中断处理程序在每次DMA传输完成后进入,根据还剩文件的字节数来决定是否继续传输,以及怎样传输。当传输完成后,禁止DMA中断,将端点3配置成中断模式,好进行下一次的批量传输。

程序源代码在 http://download.csdn.net/detail/yaozhenguo2006/4182352




相关文章推荐

C#下操作USB设备的方法

想必大家对LibUSB不陌生,没错,它就是很有名的开源usb驱动
  • cumtwys
  • cumtwys
  • 2014年08月04日 16:45
  • 5179

S3C2440 之USB设备篇

             S3C2440 之USB 设备篇S3C2440有2 个USB 主机接口和1 个USB 设备接口, 本篇讲述USB 设备接口。1 USB的分类及主机接口和设备...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

s3c2440 usb host device controller驱动分析(六)-----usb device 和 usb interface

前面分析了usb hub 和 generic driver,顺便将枚举的前一部分看完了。本来我们应该继续分析class driver的最上层,即利用usb通信实现具体功能的驱动。我们将以usb-ske...
  • a_jige
  • a_jige
  • 2013年10月19日 11:03
  • 1695

让mini2440成为U盘之linux gadget driver

如何使mini2440开发板插上电脑能被电脑识别为U盘,并能往里面拷贝东西呢? 这里就要学习USB gadget driver,USB gadget driver主要涉及s3c2440_udc.ko...

s3c2440的USB主机控制器

s3c2440提供了USB主机接口,它与OHCI v1.0完全兼容。要使用该功能,就必须熟悉OHCI v1.0规范;而要熟悉OHCI v1.0规范,那么还必须先熟悉USB v1.1协议。因此涉及到该部...
  • zhaocj
  • zhaocj
  • 2010年12月17日 21:48
  • 10611

uboot 中s3c2440 usb ohci 及u盘移植

因为boss的一个蛋疼项目,需要裸板运行。而且要求要有能够外接u盘的功能。最开始想自己写一个ohci 协议栈。看了两个月的usb协议的书,感觉看的懂,信心百赠。但是当实现起来时发现困难重重,个人认为无...

S3C2440板子模拟优盘

OS:Linux-2.6.34CPU:s3c24401、配置内核内核配置如下:Usb gadget Support 必须选中USB Peripheral Controller 选择 S3C2410 U...

S3C2440 之USB设备篇

             S3C2440 之USB 设备篇S3C2440有2 个USB 主机接口和1 个USB 设备接口, 本篇讲述USB 设备接口。1 USB的分类及主机接口和设备...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:S3C2440 USB 设备控制器
举报原因:
原因补充:

(最多只允许输入30个字)