S3C2440 USB 設備控制器
s3c2440
soc集成了一個usb1.1設備控制器,,可以進行全速/低速的控制,中斷與批量傳輸,。除了端點0,具有四個端點,,每個端點都可以作為中斷與批量的端點,
每個端點具有128
byte的FIFO,,所以端點最大packet可以設置成128byte,。并且支持DMA傳輸。任何一種設備控制器對于軟件來說都是一組寄存器:數(shù)據(jù),,狀
態(tài),,控制。usb
設備控制器也不例外,。設置好相應的控制寄存器,,并且在數(shù)據(jù)來時讀取數(shù)據(jù)寄存器,需要發(fā)送數(shù)據(jù)的時候將數(shù)據(jù)寫入輸出寄存器,。而這種數(shù)據(jù)的通信建立在對狀態(tài)寄
存器的讀取上,,往往還會有中斷與DMA的操作。s3c2440
usb設備控制器的寄存器分為以下幾組:
(1):電源管理寄存器
PWR_REG 負責USB設備掛起等電源設置
(2):地址寄存器
存儲USB設備的地址,,當主機枚舉設備設備的時候設置
(3):中斷控制寄存器
EP_INT_REG 端點中斷狀態(tài)寄存器,,每當一個端點事件發(fā)生的時候,相應的位就會置1
USB_INT_REG 設備中斷狀態(tài)寄存器,,主要有三個中斷:喚醒,,復位,掛起
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傳輸細節(jié),,并產生相應的中斷,。所以對USB設備控制器的編程,最關鍵的部分就是中斷處理的部分,。比如端點每收到一個
token以及后面的數(shù)據(jù)包,,就會產生相應的中斷。軟件只需要在中斷處理程序中讀取數(shù)據(jù),,并且清除中斷標志,。USB設備控制器就會自動發(fā)送應答包。現(xiàn)在網
上比較流行的操作s3c2440 usb
設備控制器的程序就是移植到u-boot里面的usb下載程序,。因為在u-boot的環(huán)境下所以調試起來沒有在裸機上來的方便,,所以我將這個程序移植到了
裸機上,編譯環(huán)境arm-linux-gcc,。下面大體介紹以下這個程序的流程:
這個程序主要是完成了USB設備的枚舉,,與批量OUT傳輸,,并且開啟了DMA,,OUT傳輸用的端點是端點3,。這個程序首先從
init_usb_slave() 開始:這個函數(shù)主要是設置usb slave的引腳以及控制寄存器,設置中斷處理程序的入口,,以及開啟中斷,。
- void usb_init_slave(void)
- {
- struct s3c24x0_gpio * const gpioregs = s3c24x0_get_base_gpio();
- char *mode;
-
- Delay(10);
-
- Usb_Isr_Init();
-
- writel((readl(&gpioregs->MISCCR) & ~((1<<3) | (1<<13))), &gpioregs->MISCCR);
-
-
-
-
-
- isUsbdSetConfiguration=0;
-
- UsbdMain();
- Delay(10);
-
- writel((readl(&gpioregs->GPCDAT) | (1<<5)), &gpioregs->GPCDAT);
-
-
-
- #if USBDMA
- mode="DMA";
- #else
- mode="Int";
- #endif
- download_run=0;
- printk("USB slave is enable!\n");
-
- }
在配置完設備后,給GPC5一個高電平,,主機就開始枚舉設備的過程了,。能不能開始這個過程關鍵是看設備是否配置正確。這就是要看UsbMain()這個函數(shù)了,。他在usbmain.c中,,如下:
- void UsbdMain(void)
- {
- InitDescriptorTable();
- ConfigUsbd();
- PrepareEp1Fifo();
- }
這個函數(shù)挺簡單,但是調用了三個函數(shù)可不簡單,。第一個就是初始化一些USB描述符,,比如設備描述符,,配置描述符等,,在usb控制傳輸?shù)臅r候返回給設備。這
個函數(shù)在usbsetup.c中。PrepareEp1Fifo()是配置端點1的,,這里沒有用到,。關鍵的是ConfigUsbd()函數(shù),這個函數(shù)是最
主要的配置函數(shù),。在usblib.c中:
- void ConfigUsbd(void)
- {
- struct s3c24x0_interrupt * intregs = s3c24x0_get_base_interrupt();
- ReconfigUsbd();
- writel((readl(&intregs->INTMSK) & ~(BIT_USBD)), &intregs->INTMSK);
- }
這個函數(shù)是個封裝函數(shù),,第一次配置調用他,并且開啟中斷,。在重置USB的過程中調用的是ReconfigUsbd(),,這個函數(shù)也在usblib.c中:
- void ReconfigUsbd(void)
- {
-
-
-
-
-
-
- struct s3c24x0_usb_device * const usbdevregs = s3c24x0_get_base_usb_device();
-
- writeb(PWR_REG_DEFAULT_VALUE, &usbdevregs->PWR_REG);
- writeb(0, &usbdevregs->INDEX_REG);
- writeb(FIFO_SIZE_8, &usbdevregs->MAXP_REG);
- writeb((EP0_SERVICED_OUT_PKT_RDY | EP0_SERVICED_SETUP_END), & usbdevregs->EP0_CSR_IN_CSR1_REG);
- writeb(1, &usbdevregs->INDEX_REG);
-
- #if (EP1_PKT_SIZE==32)
- writeb(FIFO_SIZE_32, &usbdevregs->MAXP_REG);
- #else
- writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG);
- #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);
- 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);
- 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);
- 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);
- #else
- writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG);
- #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);
- writeb(EPO_CDT, &usbdevregs->OUT_CSR1_REG);
-
-
- writeb((EPO_BULK | EPO_OUT_DMA_INT_MASK), &usbdevregs->OUT_CSR2_REG);
-
- writeb(4, &usbdevregs->INDEX_REG);
- writeb(FIFO_SIZE_64, &usbdevregs->MAXP_REG);
- 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);
- writeb(EPO_CDT, &usbdevregs->OUT_CSR1_REG);
-
-
- 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);
-
-
- 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因為其他三個端點沒有用到。最后清除所有的中斷狀態(tài)寄存器的標志,。
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);
-
- if(usbdIntpnd&SUSPEND_INT)
- {
- writeb(SUSPEND_INT, &usbdevregs->USB_INT_REG);
-
- }
- if(usbdIntpnd&RESUME_INT)
- {
- writeb(RESUME_INT, &usbdevregs->USB_INT_REG);
-
- }
- if(usbdIntpnd&RESET_INT)
- {
-
-
-
- ReconfigUsbd();
- writeb(RESET_INT, &usbdevregs->USB_INT_REG);
- PrepareEp1Fifo();
- }
-
- if(epIntpnd&EP0_INT)
- {
- writeb(EP0_INT, &usbdevregs->EP_INT_REG);
- Ep0Handler();
- }
-
- if(epIntpnd&EP1_INT)
- {
- writeb(EP1_INT, &usbdevregs->EP_INT_REG);
- Ep1Handler();
- }
-
- if(epIntpnd&EP2_INT)
- {
- writeb(EP2_INT, &usbdevregs->EP_INT_REG);
-
-
- }
-
- 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);
-
-
- }
-
- ClearPending_my((int)BIT_USBD);
- writeb(saveIndexReg, &usbdevregs->INDEX_REG);
- }
USB
最重要要的是時序,,打印函數(shù)是很費時間的,所以如果在USB中斷處理程序中加入打印函數(shù),,有可能造成總線超時,,從而使得USB傳輸不能成功。下面針對
USB設備枚舉來說明以下控制傳輸,,相應的中斷處理程序Ep0Handler(),,在usbsetup.c中:
這個函數(shù)比較長,我就不貼代碼了,。這個函數(shù)主要就是針對中斷的類型類進行相應的處理:
writeb(0, &usbdevregs->INDEX_REG);
ep0_csr = readb(&usbdevregs->EP0_CSR_IN_CSR1_REG);
EP0_CSR_IN_CSR1_REG寄存器里面有相應端點的相應中斷類型,。其中有兩種中斷是不正常狀態(tài),EP0_SETUP_END這個標志表示,,控
制傳輸,,SETUP階段在數(shù)據(jù)包到來前,,傳輸就結束了。造成這個原因有可能是總線超時,,如果主機在送出SETUP包以及數(shù)據(jù)包之后,,USB設備遲遲不發(fā)送
應答包。EP0_SENT_STALL 當設備不正常的時候這個標志會置位,。
EP0_OUT_PKT_READY
這個標志是正常的狀態(tài),,在USB設備接收到了一個正確的Token以及數(shù)據(jù)包是,這個標志就置位,,從而程序可以從FIFO中讀取數(shù)據(jù)根據(jù)setup包中根
據(jù)請求類型來進行相應的處理,。然后清除這個標志。USB設備控制器就會自動發(fā)送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);
- downPt+=fifoCnt-8;
-
- #if USBDMA
-
-
- writel((readl(&intregs->INTMSK) | BIT_USBD), &intregs->INTMSK);
- return;
- #endif
- }
- else
- {
- #if USBDMA
- printk("<ERROR>");
- #endif
- RdPktEp3_CheckSum((U8 *)downPt,fifoCnt);
- downPt+=fifoCnt;
- }
- #endif
- CLR_EP3_OUT_PKT_READY();
- return;
- }
-
-
-
- if(out_csr3 & EPO_SENT_STALL)
- {
- printk("[STALL]");
- CLR_EP3_SENT_STALL();
- return;
- }
- }
可以看出中斷狀態(tài)EPO_OUT_PKT_READY標志置位就代表著一個數(shù)據(jù)包的到來,這里是處理第一個數(shù)據(jù)包,,由于端點3配置的大小為32個字節(jié),,所
以一個包的數(shù)據(jù)就是32個字節(jié)。因為主機軟件DNW在每個文件的頭部加了8個字節(jié)的信息來標示下載地址與文件長度,,所以這個就是處理下載地址與文件長度的
信息的,。最后下載地址保存在downloadAddress里,文件長度保存在downloadFileSize里,。處理完第一個包后,,首先禁止USBD
中斷這個為了設置端點3的DMA功能。在中斷禁止的時候,,主機發(fā)送的數(shù)據(jù)包都會被USB設備忽略,,從而主機會從新發(fā)送。貌似這個中斷處理函數(shù)只處理一個包
的數(shù)據(jù)就關了中斷,,USB批量傳輸就進行不下去了,。但是別急,程序中還有一個函數(shù)是進行批量傳輸先運行的,,這就是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;
- dwUSBBufWritePtr = dwUSBBufBase;
- bDMAPending = 0;
-
-
- tempDownloadAddress = dwUSBBufBase;
-
- downloadAddress=(U32)tempMem;
- downPt=(unsigned char *)downloadAddress;
-
- downloadFileSize=0;
-
-
-
-
-
- if(isUsbdSetConfiguration==0)
- {
- printk("USB host is not connected yet.\n");
- }
-
- while(downloadFileSize==0)
- {
- if(first==1 && isUsbdSetConfiguration!=0)
- {
- printk("USB host is connected. Waiting a download.\n");
- first=0;
- }
- }
-
- printk("get downloadFileSize = %d !!\n",downloadFileSize);
-
-
- 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();
-
-
-
- if(downloadFileSize>EP3_PKT_SIZE)
- {
- if(downloadFileSize - EP3_PKT_SIZE<=(0x80000))
- {
-
- dwUSBBufWritePtr = downloadAddress + EP3_PKT_SIZE-8;
- dwWillDMACnt = downloadFileSize - EP3_PKT_SIZE;
- }
- else
- {
- dwUSBBufWritePtr = downloadAddress + EP3_PKT_SIZE - 8;
-
-
-
-
-
-
-
-
-
- dwWillDMACnt = 0x80000 + 8 - EP3_PKT_SIZE;
- }
- totalDmaCount = 0;
- ConfigEp3DmaMode(dwUSBBufWritePtr, dwWillDMACnt);
- }
- else
- {
- 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函數(shù)緊接著usb_init_slave運行,,如果主機枚舉成功設備,,那么就會設置downloadFileSize =
1,”USB host is connected. Waiting a
download“就會輸出到終端,從而執(zhí)行以下的代碼,,否則就會等待USB設備配置完畢。頭文件中是定義了USBDMA的,,所以#if USBDMA
下面的代碼就會執(zhí)行,。在說這段代碼之前首先說一下Timer_InitEx(); Timer_StartEx();這兩個函數(shù)的作用還有
if
(wait)下面代碼的作用,起始這些都是為了顯示現(xiàn)在進度,,以及下載平均耗時的,,這里主要用了看門狗定時器作為定時硬件,如果定義了wait,,那么程序
會將下載信息顯示在終端,,然后還統(tǒng)計每秒傳輸?shù)淖止?jié)數(shù)。下面重點說明#if USBDMA下面的代碼,。
這段代碼的主要作用就是針對傳輸文件的大小來決定DMA的使用情況,,如果使用DMA,則配置端點3為DMA模式,,設置DMA傳輸?shù)慕K點地址以及每次DMA
傳輸?shù)拇笮?,開啟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);
-
-
-
-
- if (!totalDmaCount)
- totalDmaCount = dwWillDMACnt + EP3_PKT_SIZE;
- else
- totalDmaCount+=dwWillDMACnt;
-
-
- dwUSBBufWritePtr = ((dwUSBBufWritePtr + dwWillDMACnt - dwUSBBufBase) % dwUSBBufSize) + dwUSBBufBase;
-
- if(totalDmaCount>=downloadFileSize)
- {
- totalDmaCount=downloadFileSize;
-
- ConfigEp3IntMode();
-
- if(out_csr3& EPO_OUT_PKT_READY)
- {
- CLR_EP3_OUT_PKT_READY();
- }
- writel(((readl(&intregs->INTMSK) | BIT_DMA2) & ~(BIT_USBD)), &intregs->INTMSK);
-
- }
- else
- {
- if((totalDmaCount+0x80000)<downloadFileSize)
- {
- dwWillDMACnt = 0x80000;
- }
- else
- {
- dwWillDMACnt = downloadFileSize - totalDmaCount;
- }
-
- dwEmptyCnt = (dwUSBBufReadPtr - dwUSBBufWritePtr - 1 + dwUSBBufSize) % dwUSBBufSize;
- if (dwEmptyCnt >= dwWillDMACnt)
- {
- ConfigEp3DmaMode(dwUSBBufWritePtr, dwWillDMACnt);
- }
- else
- {
- bDMAPending = 1;
- }
- }
- writeb(saveIndexReg, &usbdevregs->INDEX_REG);
- }
DMA中斷處理程序在每次DMA傳輸完成后進入,,根據(jù)還剩文件的字節(jié)數(shù)來決定是否繼續(xù)傳輸,以及怎樣傳輸,。當傳輸完成后,,禁止DMA中斷,將端點3配置成中斷模式,,好進行下一次的批量傳輸,。
程序源代碼在 http://download.csdn.net/detail/yaozhenguo2006/4182352