⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usbh_hw.c

📁 USB Host在嵌入式系统中的实现.包括所有源代码.
💻 C
字号:
/*2005/12/15 jessefanchiang 
	bug: Bulk In Second data will save to First Bulk IN Last one Buffer position
	from:
		1.HISR token Done Enable BulkIN Last one transaction.
		2.SOF Interrupt will Find BulkIN transaction to Enable
*/
#include "standard.h"
#include "nucleus.h"
#include "usbhost.h"

//#define HOSTHW_PRINTF logprintf
#define HOSTHW_PRINTF if(0) printf

#define HISR_STACKSIZE  800
#define USB_BDT_PAGE    ( (Uint32) USB10BDT0REG0 )

static Uint32 pHISRStack[HISR_STACKSIZE];
static NU_HISR usbh_hisr;

EP_DATA *Ctrl_ep;
DEV_INFO *device_info;
HOST_QUEUE_HEAD	ISO_QH,INT_QH,CONTROL_QH,BULK_QH;
void QH_init(void)
{
    HOST_QUEUE_HEAD *curr_QH;
    
//	HOSTHW_PRINTF("BULK QH init\n");		
	BULK_QH.TD_num=4;
	BULK_QH.TD_head=NULL;
	BULK_QH.TD_tail=NULL;	
	
//	HOSTHW_PRINTF("CONTROL QH init\n");		
	CONTROL_QH.TD_num=3;
	CONTROL_QH.TD_head=NULL;
	CONTROL_QH.TD_tail=NULL;
		
//	HOSTHW_PRINTF("INT QH init\n");		
	INT_QH.TD_num=2;
	INT_QH.TD_head=NULL;
	INT_QH.TD_tail=NULL;
	
//	HOSTHW_PRINTF("ISO QH init\n");
	ISO_QH.TD_num=1;
	ISO_QH.TD_head=NULL;
	ISO_QH.TD_tail=NULL;
	
//	HOSTHW_PRINTF("QH Link\n");	
	ISO_QH.QH_next		=	&INT_QH;
	INT_QH.QH_next		=	&CONTROL_QH;
	CONTROL_QH.QH_next	=	&BULK_QH;
	BULK_QH.QH_next		=	&ISO_QH;
	
	//verify QH link
	HOSTHW_PRINTF("USBH: ISO QH Addr=0x%x, next QH=0x%x\n",&ISO_QH,ISO_QH.QH_next);
		HOSTHW_PRINTF("USBH: INT QH Addr=0x%x, next QH=0x%x\n",&INT_QH,INT_QH.QH_next);
			HOSTHW_PRINTF("USBH: CONTROL QH Addr=0x%x, next QH=0x%x\n",&CONTROL_QH,CONTROL_QH.QH_next);
				HOSTHW_PRINTF("USBH: BULK QH Addr=0x%x, next QH=0x%x\n",&BULK_QH,BULK_QH.QH_next);

}

/*Transfer Setting Fill to Register*/
void Fill2REG_FCT(TRANSACTION_STRUCT *currTD)
{
    BDT_STRUCT *bdt;
    
    //Select Data IN/OUT
    if((currTD->state&0x0F)==PROCESS_IN)
    {
	    //Data IN Setting
	    //1.Get Data IN BDT
	    //2.Link to Txf
	    //3.BDT Buffer Setting
	    //4.COntrol Register Setting
	    
	    bdt = (BDT_STRUCT *)((Uint32)USB_BDT_PAGE | USBHOST_EVEN_ODD_IN_GET());//usb_host.even_odd_IN);
	    currTD->bdt = bdt;
	    
	    USBHOST_EVEN_ODD_IN_SET();
	    //usb_host.even_odd_IN ^= BDT_ODD_EVEN_BIT;
	}
	else if(((currTD->state&0x0F)==PROCESS_OUT)||
			((currTD->state&0x0f)==CNTRL_SETUP)||
			((currTD->state&0x0f)==CNTRL_LAST))
	{
	    //Data OUT Setting
	    //1.Get Data OUT BDT
	    //2.Link to Txf
	    //3.BDT Buffer Setting
	    //4.COntrol Register Setting
	    bdt = (BDT_STRUCT *)((Uint32)USB_BDT_PAGE | USBHOST_EVEN_ODD_OUT_GET());//usb_host.even_odd_OUT);
	    currTD->bdt = bdt;
	    USBHOST_EVEN_ODD_OUT_SET();
	    //usb_host.even_odd_OUT ^= BDT_ODD_EVEN_BIT;
	}
	
    *regBUFBASEL = currTD->BUFBASEL;
    *regBUFBASEH = currTD->BUFBASEH;
    
	/* Enable the token done, error, STALL and RESET interrupts */
//	*regINT_ENB = INTR_TOK_DNE | INTR_ERROR | INTR_STALL | INTR_USB_RST | INTR_SOF_TOK;	
	        
   	/* Ship it */
	bdt->PID   = currTD->bdt_PID;
	bdt->BC    = currTD->bdt_BC;
	bdt->ADDRL = currTD->bdt_ADDRL;
	bdt->ADDRH = currTD->bdt_ADDRH;
	
	*regEP0CTL	= currTD->EP0CTL;
	*regADDR 	= currTD->ADDR;
	*regTOKEN	= currTD->TOKEN;
}


TRANSACTION_STRUCT *SearchQHsTD(HOST_QUEUE_HEAD *QH)
{
    Uint8 i;
    TRANSACTION_STRUCT *TD;
    HOST_QUEUE_HEAD	*CurrQH, *NextQH;

    

	CurrQH=QH;
	NextQH=QH->QH_next;
	
	for(i=1;i<=4;i++){
		if(CurrQH->TD_head!=NULL){	//Check QH 's Has TD or Not

			return CurrQH->TD_head;
		}
		else{			
			CurrQH=NextQH;			//Get Next QH
			NextQH=CurrQH->QH_next;
		}
	}
	return NULL;
}

void QH_RemoveTD(HOST_QUEUE_HEAD *QH, TRANSACTION_STRUCT *CurrTD)
{
	TRANSACTION_STRUCT *head, *prve, *next;
	
	head=QH->TD_head;
	next=CurrTD->TD_next;
	/*Case 1. QH First TD*/
	if(head==CurrTD){
		/*Case1.1 TD Tail*/
		if(next==NULL){	
			QH->TD_head=NULL;
			QH->TD_tail=NULL;
		}
		/*Case1.2 Link to Next TD*/
		else
			QH->TD_head=next;
	}
	/*Case 2. Multi TD*/
	else{		
		//prve=usb_host.prve_TD;	/*Get Prve TD*/
		prve=(TRANSACTION_STRUCT *)USBHOST_PRVE_TD_GET();
		/*Case2.1 TD Tail*/
		if(next==NULL){
			prve->TD_next=NULL;	/*Init Prve TD_next=NULL*/
			QH->TD_tail=prve;
		}
		/*Case2.2 Link to Next TD*/
		else
			prve->TD_next=next;	/*Prve Link to TD->TD_next*/	
	}
	free(CurrTD);
}

void SOF_ISR(void)
{
    TRANSACTION_STRUCT *currTD;
    	
	currTD=SearchQHsTD(&ISO_QH);	
	if(currTD==NULL){/*No Any More TD Link to QH*/
//		usb_host.curr_QH=NULL;
//		usb_host.prve_TD=NULL;
//		usb_host.curr_TD=NULL;
		USBHOST_CURR_QH_RESET();
		USBHOST_CURR_TD_RESET();
		USBHOST_PRVE_TD_RESET();
		return;
	}
//	//2005/12/14 jessefanchiang
//	//Check Curr TD BDT OWN Setting
//	if((currTD->bdt->PID&&BDT_PID_OWN)==BDT_PID_OWN)
//		return;
	//2.QH Init
//	usb_host.curr_QH=(HOST_QUEUE_HEAD*)currTD->QH;
	USBHOST_CURR_QH_SET((HOST_QUEUE_HEAD*)currTD->QH);
	//3.TD Init
//	usb_host.prve_TD=currTD;
//	usb_host.curr_TD=currTD;
	USBHOST_CURR_TD_SET(currTD);
	USBHOST_PRVE_TD_SET(currTD);
	

	//4.Setting
	Fill2REG_FCT(currTD);
}

//Get Curr TD Data
//Set Next TD
//Remove Finish TD
Uint8	debug_tokendone_log[500];
Uint16	Debug_tokendone_count=0;
Uint32	Debug_tokendone_step=0;
void Debug_TokenDone_record(Uint32 data)
{
	if(Debug_tokendone_count>499)
		Debug_tokendone_count=0;
	debug_tokendone_log[Debug_tokendone_count]=(Uint8)data;
	Debug_tokendone_count++;
}

void TokenDone_ISR(Uint32 rstatus)
{
    Uint8 status, tmp;
    Uint32 PROCESS_ID, new_Addr, remain_byte, bytecnt;
    EP_DATA *ep;
    BDT_STRUCT *bdt;
    HOST_QUEUE_HEAD *this_qh;
	TRANSACTION_STRUCT *this_td, *nextTD;
	Uint32	dbg_log=0;

	this_qh		= (HOST_QUEUE_HEAD *)USBHOST_CURR_QH_GET();
	this_td		= (TRANSACTION_STRUCT *)USBHOST_CURR_TD_GET();
	nextTD		= this_td->TD_next;
	PROCESS_ID	= (Uint32)this_td->state;
	ep			= this_td->curr_EP;
	
	
    status = this_td->bdt->PID & BDT_PID_MASKS;	/* Get the status from BDT */
    if (!status || (status == BDT_NAK_PID))		/*When TD NAK From USB Device*/
    {
    	dbg_log=0x30000000;
    	/*Interrupt IN NAK*/
    	if((this_td->state&0xF0) == INT_TRANSACTION){
    		QH_RemoveTD(this_qh, this_td);	/*Remove Curr TD*/
    		USBH_work_report(USBH_INTNAK, PROCESS_ID, rstatus);
    		dbg_log |=0x1000000;
    	}
    	/*Others NAK*/
    	else{
    		dbg_log |=0x2000000;
			if (++this_td->NAKcount > 30000) {
				QH_RemoveTD(this_qh, this_td);	/*Remove Curr TD*/
	            USBH_work_report(USBH_NAK, PROCESS_ID, rstatus);	//Notify to Call Bake FCT
				this_td->NAKcount = 0;
				dbg_log |=0x400000;
				goto next_td_setting;
			}
			USBHOST_PRVE_TD_SET(this_td);
			dbg_log |=0x300000;
		}
		goto next_td_setting;
    }
    

	if(((this_td->state&0x0f)==CNTRL_SETUP)||((this_td->state&0x0f)==CNTRL_LAST)){
	    QH_RemoveTD(this_qh, this_td);/*Remove Curr TD*/
		USBH_work_report(USBH_DONE, PROCESS_ID, rstatus);
		dbg_log=0x20000000;
//		HOSTHW_PRINTF("log 0x%x\n",dbg_log);
		return;
	}
	
	dbg_log=0x10000000;
	/* Endpoint Data Toggle*/
	ep->DATAx ^= BDT_PID_DATA01;

	bdt 			= this_td->bdt;	/*Host Buffer Descriptor Table*/
	bytecnt 		= bdt->BC;		/*Data Bytes Count*/	    
	ep->curr_count += bytecnt;
	/*--------------------------------Debug Log Message-----------------------------------------*/
//  temp to disable 2005/12/19 jessefanchiang debug for USBD 1.1
//	HOSTHW_PRINTF("ByteCount %d InOdd:%d InEven:%d OutOdd:%d OutEven:%d\n",
//	bytecnt,*(Uint8 *)0xE0480031,*(Uint8 *)0xE0480035,*(Uint8 *)0xE0480039,*(Uint8 *)0xE048003D);
//	HOSTHW_PRINTF("DATAx %x Curraddr %x%x%x%x\n",
//	ep->DATAx,this_td->BUFBASEH,this_td->BUFBASEL,this_td->bdt_ADDRH,this_td->bdt_ADDRL);
//	bdt->BC=0;
	/*--------------------------------Debug Log Message-----------------------------------------*/
	
	/*Get Remain Bytes*/
	remain_byte = this_td->length - ep->curr_count;
	
	this_td->bdt_BC = ep->pksize;	/*Set BDT Byte Count*/
	if (remain_byte < ep->pksize) 	/*Set BDT Byte Count if Short Packet*/
		this_td->bdt_BC = (Uint8)remain_byte;
	
	/*2.Done Condition*/
	if((this_td->length==ep->curr_count)||(bytecnt != this_td->curr_EP->pksize)){
		dbg_log |=0x3000000;
		//Bulk OUT Zero Packet
		//1.total length mod 64 ==0
		//2.transaction ID is Bulk OUT
		//3.curr byte count=64
		//4.Finish Send Zero Packet byte count=0
	    if(((this_td->length%64)==0)&&((this_td->state&0x0F)==PROCESS_OUT)&&(bytecnt)){
	    	this_td->bdt_BC=0;
	    	HOSTHW_PRINTF("BULK OUT Send Zero Packet\n");
	    	dbg_log |=0x500000;
	    }
	    else{
		    QH_RemoveTD(this_qh, this_td);/*Remove Curr TD*/
			USBH_work_report(USBH_DONE, PROCESS_ID, rstatus);
			dbg_log |=0x400000;
			goto next_td_setting;
		}
	}
	dbg_log |=0x60000;
	/*Init Next Packet Data Toggle*/
	this_td->bdt_PID	= BDT_PID_OWN | ep->DATAx;
	/*init Next Packet Buffer head*/
	new_Addr=(Uint32)(this_td->io_bf+ep->curr_count);	//Get Next Data Buffer Start
	if(new_Addr >(Uint32)(this_td->io_bf+this_td->length)){
		HOSTHW_PRINTF("USBH IO Buffer Error\n");
		while(1);
	}
	IObf_2_BDT(this_td, (Uint8 *)new_Addr);				//TD Buffer Address Converter
	HOSTHW_PRINTF("NextAddr %x\n",new_Addr);
	
next_td_setting:
	return;//Direct EXit,Don't Enable Next transaction Descriptor 

	//SOF Interrupt Check
	tmp=(Uint8)(rstatus>>16);
	if((tmp&INTR_SOF_TOK)==INTR_SOF_TOK){
		dbg_log |=0x1000;
//		HOSTHW_PRINTF("log 0x%x BC %d\n",dbg_log,bytecnt);
		return;
	}
	dbg_log |=0x2000;										
	USBHOST_PRVE_TD_SET(this_td);		//Change Next TD to Do,Remeber Curr TD
	/*QH TD Tail Get Next QH's TD*/
	if(nextTD==NULL){
		dbg_log |=0x100;
		nextTD=SearchQHsTD(this_qh->QH_next);
		if(nextTD==NULL){/*No Any More TD Link to QH*/
			USBHOST_CURR_QH_RESET();
			USBHOST_CURR_TD_RESET();
			USBHOST_PRVE_TD_RESET();
			dbg_log |=0x10;
//			HOSTHW_PRINTF("log 0x%x BC %d\n",dbg_log,bytecnt);
			return;
		}
		dbg_log |=0x20;
		USBHOST_CURR_QH_SET((HOST_QUEUE_HEAD*)nextTD->QH);
		USBHOST_PRVE_TD_SET(nextTD);
	}
	dbg_log |=1;
	USBHOST_CURR_TD_SET(nextTD);
	Fill2REG_FCT(nextTD);
//	HOSTHW_PRINTF("log 0x%x BC %d\n",dbg_log,bytecnt);
}

static void USBH_hisr(void)
{
    Uint8 istatus, status, error;
    Uint32 rstatus;
    Uint32 PROCESS_ID=0;
    
        
    /* While any of the enabled interrupts are asserted... */
    istatus = *regINT_STAT & *regINT_ENB;

    
    while (istatus) 
    {
        error = *regERR_STAT;
        status = *regSTATUS;
        
        /*Action Status+INT Status+Error Status+Transaction Status*/
        /*Action Status is Current Action Status		*/
        /*INT Status is Interrupt Status Register Value	*/
        /*ERROR Status is Error Occurred Status Value	*/
        /*Transation Status is HOST Controller Transaction Status */
		rstatus = ((Uint32)istatus << 16) | ((Uint32)error << 8) | status;
        
        /* Clear error bits */
        *regERR_STAT = error & *regERR_ENB;
        /* Previous token transmission should already be done */
        if (*regCTL & CTRL_TOKEN_BUSY) {
//            usb_host.state = NO_REQUEST;
//            usb_host.reset_required = TRUE;
//            usb_host.DeviceReady = NOT_READY;
//            Start_USB_Host_Task(USBH_ERROR, rstatus);

            /* Clear all enabled interrupts */
            *regINT_STAT = istatus;

            /* Check for another interrupt pending */
            istatus = *regINT_STAT & *regINT_ENB;


            /* Clear bit in exception register. */
            *(unsigned long *)EXMSK1A |= (1 << USB1_INTERRUPT_BIT);
            *(Uint32 *)EXCLRA = (1 << USB1_INTERRUPT_BIT);
            return;
        }
		
        /* Check if STALL interrupt */
        if (istatus & INTR_STALL) 
        {
            /* A bad-CRC16 error is OK with STALL, clear error */
            if (error & VUSB_ERR_STAT_CRC16) {
                error ^= VUSB_ERR_STAT_CRC16;
                if (!error) {
                    istatus     ^= VUSB_INT_STAT_ERROR;
                    *regINT_STAT = VUSB_INT_STAT_ERROR;
                }
            }
            rstatus = ((Uint32)istatus << 16) | ((Uint32)error << 8) | status;
        	USBH_work_report(USBH_STALL, PROCESS_ID, rstatus);
        }
        /* Check if ATTACH interrupt */
        else if (istatus & INTR_ATTACH)
        {
			*regINT_ENB=0;
			USBH_work_report(USBH_ATTACH, PROCESS_ID, rstatus);
        }
        else if (istatus & INTR_TOK_DNE){
       		
        	*regINT_STAT = istatus;					/* Clear all enabled interrupts */
        	TokenDone_ISR(rstatus);
       		istatus = *regINT_STAT & *regINT_ENB;	/* Check for another interrupt pending */
       		continue;
        }
        else if (istatus & INTR_SOF_TOK)     	
			SOF_ISR();	/*SOF 1ms do one time start from ISO transfer*/
        else if (istatus & INTR_ERROR)
			USBH_work_report(USBH_ERROR, PROCESS_ID, rstatus);
        /* Check if RESET interrupt */
        else if (istatus & INTR_USB_RST) 
        {
            *regCTL = 0;
            /* Let polling routine know that device went bye-bye */
            USBHOST_DEVICEREADY_SET(NOT_READY);
            //usb_host.DeviceReady = NOT_READY;
            USBH_work_report(USBH_RESET, PROCESS_ID, rstatus);
        }
		
        /* Clear all enabled interrupts */
        *regINT_STAT = istatus;

        /* Check for another interrupt pending */
        istatus = *regINT_STAT & *regINT_ENB;
    }

    /* Clear bit in exception register. */
    *(unsigned long *)EXMSK1A |= (1 << USB1_INTERRUPT_BIT);
    *(Uint32 *)EXCLRA = (1 << USB1_INTERRUPT_BIT);
}

/***********************************************************************/
/*  Function Name  : USBH_isr                                          */
/*  Returned Value : None                                              */
/*                                                                     */
/*  Service all the interrupts in the VUSB1.1 host hardware            */
/***********************************************************************/
static void USBH_isr(int vector)
{
    /* Mask off USB interrupts because we're moving to HISR */
    *(unsigned long *)EXMSK1A &= ~(1 << USB1_INTERRUPT_BIT);

    NU_Activate_HISR(&usbh_hisr);

    /* Clear bit in exception register. */
    *(Uint32 *)EXCLRA = (1 << USB1_INTERRUPT_BIT);
}


void usbh_hw_init(void)
{
	DEV_INFO *DEV;
	STATUS status;
    void (*old_lisr)(int);
    
    
	//Default Pipe Create
	Ctrl_ep=(EP_DATA *)calloc(sizeof(EP_DATA),1);
	
	//Device Info Struct
	device_info=(DEV_INFO *)calloc(sizeof(DEV_INFO)*DEVID_MAX,1);
	
  	QH_init();
  	
    /* Register HISR */
    status = NU_Create_HISR(&usbh_hisr, "HostHISR", USBH_hisr, 2, pHISRStack,
                            HISR_STACKSIZE);
    ASSERT(status == NU_SUCCESS);

    /* Install the LISR (Low Interrupt Service Routine). */
    status = NU_Register_LISR(USB1_INTERRUPT_BIT, USBH_isr, &old_lisr);
    ASSERT(status == NU_SUCCESS);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -