/* rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error, fe USB xfer timeout */ byte USB::inTransfer( byte addr, byte ep, unsigned int nbytes, char* data, unsigned int nak_limit ) { byte rcode; byte pktsize; byte maxpktsize = devtable[ addr ].epinfo[ ep ].MaxPktSize; unsigned int xfrlen = 0; regWr( rHCTL, devtable[ addr ].epinfo[ ep ].rcvToggle ); //set toggle value while( 1 ) { // use a 'return' to exit this loop rcode = dispatchPkt( tokIN, ep, nak_limit ); //IN packet to EP-'endpoint'. Function takes care of NAKS. if( rcode ) { return( rcode ); //should be 0, indicating ACK. Else return error code. } /* check for RCVDAVIRQ and generate error if not present */ /* the only case when absense of RCVDAVIRQ makes sense is when toggle error occured. Need to add handling for that */ if(( regRd( rHIRQ ) & bmRCVDAVIRQ ) == 0 ) { return ( 0xf0 ); //receive error } pktsize = regRd( rRCVBC ); //number of received bytes data = bytesRd( rRCVFIFO, pktsize, data ); regWr( rHIRQ, bmRCVDAVIRQ ); // Clear the IRQ & free the buffer xfrlen += pktsize; // add this packet's byte count to total transfer length /* The transfer is complete under two conditions: */ /* 1. The device sent a short packet (L.T. maxPacketSize) */ /* 2. 'nbytes' have been transferred. */ if (( pktsize < maxpktsize ) || (xfrlen >= nbytes )) { // have we transferred 'nbytes' bytes? if( regRd( rHRSL ) & bmRCVTOGRD ) { //save toggle value devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG1; } else { devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG0; } return( 0 ); } }//while( 1 ) }
uint8_t UsbHost_::SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t &nak_limit) { UsbDevice *p = addrPool.GetUsbDevicePtr(addr); if (!p) return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; if (!p->epinfo) return USB_ERROR_EPINFO_IS_NULL; *ppep = getEpInfoEntry(addr, ep); if (!*ppep) return USB_ERROR_EP_NOT_FOUND_IN_TBL; nak_limit = (0x0001UL << ( ( (*ppep)->bmNakPower > USB_NAK_MAX_POWER ) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower) ); nak_limit--; /* USBTRACE2("\r\nAddress: ", addr); USBTRACE2(" EP: ", ep); USBTRACE2(" NAK Power: ",(*ppep)->bmNakPower); USBTRACE2(" NAK Limit: ", nak_limit); USBTRACE("\r\n"); */ regWr( rPERADDR, addr ); //set peripheral address uint8_t mode = regRd( rMODE ); // Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise regWr( rMODE, (p->lowspeed) ? mode | bmLOWSPEED | bmHubPre : mode & ~(bmHUBPRE | bmLOWSPEED)); return 0; }
/* probe bus to determine device presense and speed and switch host to this speed */ void MAX3421E::busprobe( void ) { byte bus_sample; bus_sample = regRd( rHRSL ); //Get J,K status bus_sample &= ( bmJSTATUS|bmKSTATUS ); //zero the rest of the byte switch( bus_sample ) { //start full-speed or low-speed host case( bmJSTATUS ): if(( regRd( rMODE ) & bmLOWSPEED ) == 0 ) { regWr( rMODE, MODE_FS_HOST ); //start full-speed host vbusState = FSHOST; } else { regWr( rMODE, MODE_LS_HOST); //start low-speed host vbusState = LSHOST; } break; case( bmKSTATUS ): if(( regRd( rMODE ) & bmLOWSPEED ) == 0 ) { regWr( rMODE, MODE_LS_HOST ); //start low-speed host vbusState = LSHOST; } else { regWr( rMODE, MODE_FS_HOST ); //start full-speed host vbusState = FSHOST; } break; case( bmSE1 ): //illegal state vbusState = MAXSE1; break; case( bmSE0 ): //disconnected state regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmSEPIRQ); vbusState = MAXSE0; break; }//end switch( bus_sample ) }
/* upper 4 bits of IOPINS1, IOPINS2 are read-only, so no masking is necessary */ void MAX3421E::gpioWr( byte val ) { regWr( rIOPINS1, val ); val = val >>4; regWr( rIOPINS2, val ); return; }
uint8_t UsbHost_::InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t* data) { uint8_t rcode = 0; uint8_t pktsize; uint16_t nbytes = *nbytesptr; uint8_t maxpktsize = pep->maxPktSize; *nbytesptr = 0; regWr( rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0 ); //set toggle value while( 1 ) // use a 'return' to exit this loop { rcode = dispatchPkt( tokIN, pep->epAddr, nak_limit ); //IN packet to EP-'endpoint'. Function takes care of NAKS. if( rcode ) { #if 0 if ((rcode != 0x04) && (rcode != 0x0d)) { USBTRACE2("\ndispatchPkt error: ", rcode); } #endif return( rcode ); //should be 0, indicating ACK. Else return error code. } /* check for RCVDAVIRQ and generate error if not present */ /* the only case when absense of RCVDAVIRQ makes sense is when toggle error occured. Need to add handling for that */ if(( regRd( rHIRQ ) & bmRCVDAVIRQ ) == 0 ) return ( 0xf0 ); //receive error pktsize = regRd( rRCVBC ); //number of received bytes assert(pktsize <= nbytes); int16_t mem_left = (int16_t)nbytes - *((int16_t*)nbytesptr); if (mem_left < 0) mem_left = 0; data = bytesRd( rRCVFIFO, ((pktsize > mem_left) ? mem_left : pktsize), data ); regWr( rHIRQ, bmRCVDAVIRQ ); // Clear the IRQ & free the buffer *nbytesptr += pktsize; // add this packet's byte count to total transfer length /* The transfer is complete under two conditions: */ /* 1. The device sent a short packet (L.T. maxPacketSize) */ /* 2. 'nbytes' have been transferred. */ if (( pktsize < maxpktsize ) || (*nbytesptr >= nbytes )) // have we transferred 'nbytes' bytes? { // Save toggle value pep->bmRcvToggle = (( regRd( rHRSL ) & bmRCVTOGRD )) ? 1 : 0; return( 0 ); } // if } //while( 1 ) }
/* reset MAX3421E using chip reset bit. SPI configuration is not affected */ boolean MAX3421E::reset() { unsigned short tmp = 0; regWr( rUSBCTL, bmCHIPRES ); //Chip reset. This stops the oscillator regWr( rUSBCTL, 0x00 ); //Remove the reset while(!(regRd( rUSBIRQ ) & bmOSCOKIRQ )) { //wait until the PLL is stable tmp++; //timeout after 256 attempts if( tmp == 0 ) { return( false ); } } return( true ); }
/* major part of this function borrowed from code shared by Richard Ibbotson */ byte USB::outTransfer( byte addr, byte ep, unsigned int nbytes, char* data, unsigned int nak_limit ) { byte rcode, retry_count; char* data_p = data; //local copy of the data pointer unsigned int bytes_tosend, nak_count; unsigned int bytes_left = nbytes; byte maxpktsize = devtable[ addr ].epinfo[ ep ].MaxPktSize; unsigned long timeout = millis() + USB_XFER_TIMEOUT; if (!maxpktsize) { //todo: move this check close to epinfo init. Make it 1< pktsize <64 return 0xFE; } regWr( rHCTL, devtable[ addr ].epinfo[ ep ].sndToggle ); //set toggle value while( bytes_left ) { retry_count = 0; nak_count = 0; bytes_tosend = ( bytes_left >= maxpktsize ) ? maxpktsize : bytes_left; bytesWr( rSNDFIFO, bytes_tosend, data_p ); //filling output FIFO regWr( rSNDBC, bytes_tosend ); //set number of bytes regWr( rHXFR, ( tokOUT | ep )); //dispatch packet while(!(regRd( rHIRQ ) & bmHXFRDNIRQ )); //wait for the completion IRQ regWr( rHIRQ, bmHXFRDNIRQ ); //clear IRQ rcode = ( regRd( rHRSL ) & 0x0f ); while( rcode && ( timeout > millis())) { switch( rcode ) { case hrNAK: nak_count++; if( nak_limit && ( nak_count == USB_NAK_LIMIT )) { return( rcode); //return NAK } break; case hrTIMEOUT: retry_count++; if( retry_count == USB_RETRY_LIMIT ) { return( rcode ); //return TIMEOUT } break; default: return( rcode ); }//switch( rcode... /* process NAK according to Host out NAK bug */ regWr( rSNDBC, 0 ); regWr( rSNDFIFO, *data_p ); regWr( rSNDBC, bytes_tosend ); regWr( rHXFR, ( tokOUT | ep )); //dispatch packet while(!(regRd( rHIRQ ) & bmHXFRDNIRQ )); //wait for the completion IRQ regWr( rHIRQ, bmHXFRDNIRQ ); //clear IRQ rcode = ( regRd( rHRSL ) & 0x0f ); }//while( rcode && .... bytes_left -= bytes_tosend; data_p += bytes_tosend; }//while( bytes_left... devtable[ addr ].epinfo[ ep ].sndToggle = ( regRd( rHRSL ) & bmSNDTOGRD ) ? bmSNDTOG1 : bmSNDTOG0; //update toggle return( rcode ); //should be 0 in all cases }
/* return codes 0x00-0x0F are HRSLT( 0x00 being success ), 0xFF means timeout */ uint8_t USB::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit) { uint32_t timeout = (uint32_t)millis() + USB_XFER_TIMEOUT; uint8_t tmpdata; uint8_t rcode = hrSUCCESS; uint8_t retry_count = 0; uint16_t nak_count = 0; while ((int32_t)((uint32_t)millis() - timeout) < 0L) { #if defined(ESP8266) || defined(ESP32) yield(); // needed in order to reset the watchdog timer on the ESP8266 #endif regWr(rHXFR, (token | ep)); //launch the transfer rcode = USB_ERROR_TRANSFER_TIMEOUT; while ((int32_t)((uint32_t)millis() - timeout) < 0L) { //wait for transfer completion #if defined(ESP8266) || defined(ESP32) yield(); // needed to reset the watchdog timer on the ESP8266 #endif tmpdata = regRd(rHIRQ); if (tmpdata & bmHXFRDNIRQ) { regWr(rHIRQ, bmHXFRDNIRQ); //clear the interrupt rcode = 0x00; break; } } // while millis() < timeout //if (rcode != 0x00) //exit if timeout // return ( rcode); rcode = (regRd(rHRSL) & 0x0F); //analyze transfer result switch (rcode) { case hrNAK: nak_count++; if (nak_limit && (nak_count == nak_limit)) return (rcode); break; case hrTIMEOUT: retry_count++; if (retry_count == USB_RETRY_LIMIT) return (rcode); break; default: return (rcode); } } // while timeout > millis() return rcode; }
/* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout */ uint8_t UsbHost_::dispatchPkt( uint8_t token, uint8_t ep, uint16_t nak_limit ) { unsigned long timeout = millis() + USB_XFER_TIMEOUT; uint8_t tmpdata; uint8_t rcode; uint8_t retry_count = 0; uint16_t nak_count = 0; while( timeout > millis() ) { regWr( rHXFR, ( token|ep )); //launch the transfer rcode = USB_ERROR_TRANSFER_TIMEOUT; while( millis() < timeout ) //wait for transfer completion { tmpdata = regRd( rHIRQ ); if( tmpdata & bmHXFRDNIRQ ) { regWr( rHIRQ, bmHXFRDNIRQ ); //clear the interrupt rcode = 0x00; break; }//if( tmpdata & bmHXFRDNIRQ }//while ( millis() < timeout if( rcode != 0x00 ) //exit if timeout return( rcode ); rcode = ( regRd( rHRSL ) & 0x0f ); //analyze transfer result switch( rcode ) { case hrNAK: nak_count ++; if( nak_limit && ( nak_count == nak_limit )) return( rcode ); break; case hrTIMEOUT: retry_count ++; if( retry_count == USB_RETRY_LIMIT ) return( rcode ); break; default: return( rcode ); }//switch( rcode }//while( timeout > millis() return( rcode ); }
uint8_t USB::AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed) { //printf("AttemptConfig: parent = %i, port = %i\r\n", parent, port); uint8_t retries = 0; again: uint8_t rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed); if (rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET) { if (parent == 0) { // Send a bus reset on the root interface. regWr(rHCTL, bmBUSRST); //issue bus reset delay(102); // delay 102ms, compensate for clock inaccuracy. } else { // reset parent port devConfig[parent]->ResetHubPort(port); } } else if (rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works delay(100); retries++; goto again; } else if (rcode) return rcode; rcode = devConfig[driver]->Init(parent, port, lowspeed); if (rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works delay(100); retries++; goto again; } if (rcode) { // Issue a bus reset, because the device may be in a limbo state if (parent == 0) { // Send a bus reset on the root interface. regWr(rHCTL, bmBUSRST); //issue bus reset delay(102); // delay 102ms, compensate for clock inaccuracy. } else { // reset parent port devConfig[parent]->ResetHubPort(port); } } return rcode; }
/* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout */ byte USB::dispatchPkt( byte token, byte ep, unsigned int nak_limit ) { unsigned long timeout = millis() + USB_XFER_TIMEOUT; byte tmpdata; byte rcode; unsigned int nak_count = 0; char retry_count = 0; while( timeout > millis() ) { regWr( rHXFR, ( token|ep )); //launch the transfer rcode = 0xff; while( millis() < timeout ) { //wait for transfer completion tmpdata = regRd( rHIRQ ); if( tmpdata & bmHXFRDNIRQ ) { regWr( rHIRQ, bmHXFRDNIRQ ); //clear the interrupt rcode = 0x00; break; }//if( tmpdata & bmHXFRDNIRQ }//while ( millis() < timeout if( rcode != 0x00 ) { //exit if timeout return( rcode ); } rcode = ( regRd( rHRSL ) & 0x0f ); //analyze transfer result switch( rcode ) { case hrNAK: nak_count ++; if( nak_limit && ( nak_count == nak_limit )) { return( rcode ); } break; case hrTIMEOUT: retry_count ++; if( retry_count == USB_RETRY_LIMIT ) { return( rcode ); } break; default: return( rcode ); }//switch( rcode }//while( timeout > millis() return( rcode ); }
byte MAX3421E::IntHandler() { byte HIRQ; byte HIRQ_sendback = 0x00; HIRQ = regRd( rHIRQ ); //determine interrupt source //if( HIRQ & bmFRAMEIRQ ) { //->1ms SOF interrupt handler // HIRQ_sendback |= bmFRAMEIRQ; //}//end FRAMEIRQ handling if( HIRQ & bmCONDETIRQ ) { busprobe(); HIRQ_sendback |= bmCONDETIRQ; } /* End HIRQ interrupts handling, clear serviced IRQs */ regWr( rHIRQ, HIRQ_sendback ); return( HIRQ_sendback ); }
uint8_t USB::AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed) { uint8_t rcode = 0; //printf("AttemptConfig: parent = %i, port = %i\r\n", parent, port); rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed); if (rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET) { if (parent == 0) { // Send a bus reset on the root interface. regWr(rHCTL, bmBUSRST); //issue bus reset delay(102); // delay 102ms, compensate for clock inaccuracy. } else { // reset parent port devConfig[parent]->ResetHubPort(port); } } rcode = devConfig[driver]->Init(parent, port, lowspeed); return rcode; }
/* MAX3421E initialization after power-on */ void MAX3421E::powerOn() { /* Configure full-duplex SPI, interrupt pulse */ regWr( rPINCTL,( bmFDUPSPI + bmINTLEVEL + bmGPXB )); //Full-duplex SPI, level interrupt, GPX if( reset() == false ) { //stop/start the oscillator Serial.println("Error: OSCOKIRQ failed to assert"); } /* configure host operation */ regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmSEPIRQ ); // set pull-downs, Host, Separate GPIN IRQ on GPX regWr( rHIEN, bmCONDETIE|bmFRAMEIE ); //connection detection /* check if device is connected */ regWr( rHCTL,bmSAMPLEBUS ); // sample USB bus while(!(regRd( rHCTL ) & bmSAMPLEBUS )); //wait for sample operation to finish busprobe(); //check if anything is connected regWr( rHIRQ, bmCONDETIRQ ); //clear connection detect interrupt regWr( rCPUCTL, 0x01 ); //enable interrupt pin }
/* 01-0f = non-zero HRSLT */ byte USB::ctrlReq( byte addr, byte ep, byte bmReqType, byte bRequest, byte wValLo, byte wValHi, unsigned int wInd, unsigned int nbytes, char* dataptr, unsigned int nak_limit ) { boolean direction = false; //request direction, IN or OUT byte rcode; SETUP_PKT setup_pkt; regWr( rPERADDR, addr ); //set peripheral address if( bmReqType & 0x80 ) { direction = true; //determine request direction } /* fill in setup packet */ setup_pkt.ReqType_u.bmRequestType = bmReqType; setup_pkt.bRequest = bRequest; setup_pkt.wVal_u.wValueLo = wValLo; setup_pkt.wVal_u.wValueHi = wValHi; setup_pkt.wIndex = wInd; setup_pkt.wLength = nbytes; bytesWr( rSUDFIFO, 8, ( char *)&setup_pkt ); //transfer to setup packet FIFO rcode = dispatchPkt( tokSETUP, ep, nak_limit ); //dispatch packet //Serial.println("Setup packet"); //DEBUG if( rcode ) { //return HRSLT if not zero Serial.print("Setup packet error: "); Serial.print( rcode, HEX ); return( rcode ); } //Serial.println( direction, HEX ); if( dataptr != NULL ) { //data stage, if present rcode = ctrlData( addr, ep, nbytes, dataptr, direction ); } if( rcode ) { //return error Serial.print("Data packet error: "); Serial.print( rcode, HEX ); return( rcode ); } rcode = ctrlStatus( ep, direction ); //status stage return( rcode ); }
/* MAX3421E initialization after power-on */ void MAX3421E::powerOn() { /* Configure full-duplex SPI, interrupt pulse */ regWr( rPINCTL,( bmFDUPSPI + bmINTLEVEL + bmGPXB )); //Full-duplex SPI, level interrupt, GPX if( reset() == false ) { //stop/start the oscillator Serial.println("Error: OSCOKIRQ failed to assert"); } // /* configure power switch */ // vbusPwr( OFF ); //turn Vbus power off // regWr( rGPINIEN, bmGPINIEN7 ); //enable interrupt on GPIN7 (power switch overload flag) // if( vbusPwr( ON ) == false ) { // Serial.println("Error: Vbus overload"); // } /* configure host operation */ regWr( rMODE, bmDPPULLDN|bmDMPULLDN|bmHOST|bmSEPIRQ ); // set pull-downs, Host, Separate GPIN IRQ on GPX regWr( rHIEN, bmCONDETIE|bmFRAMEIE ); //connection detection regWr(rHCTL,bmSAMPLEBUS); // update the JSTATUS and KSTATUS bits busprobe(); //check if anything is connected regWr( rHIRQ, bmCONDETIRQ ); //clear connection detect interrupt regWr( rCPUCTL, 0x01 ); //enable interrupt pin }
/* USB main task. Performs enumeration/cleanup */ void USB::Task( void ) //USB state machine { byte i; byte rcode; static byte tmpaddr; byte tmpdata; static unsigned long delay = 0; USB_DEVICE_DESCRIPTOR buf; tmpdata = getVbusState(); /* modify USB task state if Vbus changed */ switch( tmpdata ) { case SE1: //illegal state usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL; break; case SE0: //disconnected if(( usb_task_state & USB_STATE_MASK ) != USB_STATE_DETACHED ) { usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; } break; case FSHOST: //attached case LSHOST: if(( usb_task_state & USB_STATE_MASK ) == USB_STATE_DETACHED ) { delay = millis() + USB_SETTLE_DELAY; usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; } break; }// switch( tmpdata //Serial.print("USB task state: "); //Serial.println( usb_task_state, HEX ); switch( usb_task_state ) { case USB_DETACHED_SUBSTATE_INITIALIZE: init(); usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE; break; case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here break; case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here break; case USB_ATTACHED_SUBSTATE_SETTLE: //setlle time for just attached device if( delay < millis() ) { usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE; } break; case USB_ATTACHED_SUBSTATE_RESET_DEVICE: regWr( rHCTL, bmBUSRST ); //issue bus reset usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE; break; case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE: if(( regRd( rHCTL ) & bmBUSRST ) == 0 ) { tmpdata = regRd( rMODE ) | bmSOFKAENAB; //start SOF generation regWr( rMODE, tmpdata ); // regWr( rMODE, bmSOFKAENAB ); usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF; delay = millis() + 20; //20ms wait after reset per USB spec } break; case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order if( regRd( rHIRQ ) & bmFRAMEIRQ ) { //when first SOF received we can continue if( delay < millis() ) { //20ms passed usb_task_state = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE; } } break; case USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE: // toggle( BPNT_0 ); devtable[ 0 ].epinfo->MaxPktSize = 8; //set max.packet size to min.allowed rcode = getDevDescr( 0, 0, 8, ( char* )&buf ); if( rcode == 0 ) { devtable[ 0 ].epinfo->MaxPktSize = buf.bMaxPacketSize0; usb_task_state = USB_STATE_ADDRESSING; } else { usb_error = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE; usb_task_state = USB_STATE_ERROR; } break; case USB_STATE_ADDRESSING: for( i = 1; i < USB_NUMDEVICES; i++ ) { if( devtable[ i ].epinfo == NULL ) { devtable[ i ].epinfo = devtable[ 0 ].epinfo; //set correct MaxPktSize //temporary record //until plugged with real device endpoint structure rcode = setAddr( 0, 0, i ); if( rcode == 0 ) { tmpaddr = i; usb_task_state = USB_STATE_CONFIGURING; } else { usb_error = USB_STATE_ADDRESSING; //set address error usb_task_state = USB_STATE_ERROR; } break; //break if address assigned or error occured during address assignment attempt } }//for( i = 1; i < USB_NUMDEVICES; i++ if( usb_task_state == USB_STATE_ADDRESSING ) { //no vacant place in devtable usb_error = 0xfe; usb_task_state = USB_STATE_ERROR; } break; case USB_STATE_CONFIGURING: break; case USB_STATE_RUNNING: break; case USB_STATE_ERROR: break; }// switch( usb_task_state }
/* USB main task. Performs enumeration/cleanup */ void USB::Task(void) //USB state machine { uint8_t rcode; uint8_t tmpdata; static unsigned long delay = 0; //USB_DEVICE_DESCRIPTOR buf; bool lowspeed = false; MAX3421E::Task(); tmpdata = getVbusState(); /* modify USB task state if Vbus changed */ switch(tmpdata) { case SE1: //illegal state usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL; lowspeed = false; break; case SE0: //disconnected if((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED) usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; lowspeed = false; break; case LSHOST: lowspeed = true; //intentional fallthrough case FSHOST: //attached if((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED) { delay = millis() + USB_SETTLE_DELAY; usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE; } break; }// switch( tmpdata for(uint8_t i = 0; i < USB_NUMDEVICES; i++) if(devConfig[i]) rcode = devConfig[i]->Poll(); switch(usb_task_state) { case USB_DETACHED_SUBSTATE_INITIALIZE: init(); for(uint8_t i = 0; i < USB_NUMDEVICES; i++) if(devConfig[i]) rcode = devConfig[i]->Release(); usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE; break; case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here break; case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here break; case USB_ATTACHED_SUBSTATE_SETTLE: //settle time for just attached device if(delay < millis()) usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE; else break; // don't fall through case USB_ATTACHED_SUBSTATE_RESET_DEVICE: regWr(rHCTL, bmBUSRST); //issue bus reset usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE; break; case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE: if((regRd(rHCTL) & bmBUSRST) == 0) { tmpdata = regRd(rMODE) | bmSOFKAENAB; //start SOF generation regWr(rMODE, tmpdata); usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF; //delay = millis() + 20; //20ms wait after reset per USB spec } break; case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order if(regRd(rHIRQ) & bmFRAMEIRQ) { //when first SOF received _and_ 20ms has passed we can continue /* if (delay < millis()) //20ms passed usb_task_state = USB_STATE_CONFIGURING; */ usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET; delay = millis() + 20; } break; case USB_ATTACHED_SUBSTATE_WAIT_RESET: if(delay < millis()) usb_task_state = USB_STATE_CONFIGURING; else break; // don't fall through case USB_STATE_CONFIGURING: //Serial.print("\r\nConf.LS: "); //Serial.println(lowspeed, HEX); rcode = Configuring(0, 0, lowspeed); if(rcode) { if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) { usb_error = rcode; usb_task_state = USB_STATE_ERROR; } } else usb_task_state = USB_STATE_RUNNING; break; case USB_STATE_RUNNING: break; case USB_STATE_ERROR: //MAX3421E::Init(); break; } // switch( usb_task_state ) }
uint8_t USB::OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data) { uint8_t rcode = hrSUCCESS, retry_count; uint8_t *data_p = data; //local copy of the data pointer uint16_t bytes_tosend, nak_count; uint16_t bytes_left = nbytes; uint8_t maxpktsize = pep->maxPktSize; if(maxpktsize < 1 || maxpktsize > 64) return USB_ERROR_INVALID_MAX_PKT_SIZE; unsigned long timeout = millis() + USB_XFER_TIMEOUT; regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value while(bytes_left) { retry_count = 0; nak_count = 0; bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left; bytesWr(rSNDFIFO, bytes_tosend, data_p); //filling output FIFO regWr(rSNDBC, bytes_tosend); //set number of bytes regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ rcode = (regRd(rHRSL) & 0x0f); while(rcode && (timeout > millis())) { switch(rcode) { case hrNAK: nak_count++; if(nak_limit && (nak_count == nak_limit)) goto breakout; //return ( rcode); break; case hrTIMEOUT: retry_count++; if(retry_count == USB_RETRY_LIMIT) goto breakout; //return ( rcode); break; case hrTOGERR: // yes, we flip it wrong here so that next time it is actually correct! pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value break; default: goto breakout; }//switch( rcode /* process NAK according to Host out NAK bug */ regWr(rSNDBC, 0); regWr(rSNDFIFO, *data_p); regWr(rSNDBC, bytes_tosend); regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ rcode = (regRd(rHRSL) & 0x0f); }//while( rcode && .... bytes_left -= bytes_tosend; data_p += bytes_tosend; }//while( bytes_left... breakout: pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0; //update toggle return ( rcode); //should be 0 in all cases }
uint8_t USB::InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t* data) { uint8_t rcode = 0; uint8_t pktsize; uint16_t nbytes = *nbytesptr; //printf("Requesting %i bytes ", nbytes); uint8_t maxpktsize = pep->maxPktSize; *nbytesptr = 0; regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value // use a 'break' to exit this loop while(1) { rcode = dispatchPkt(tokIN, pep->epAddr, nak_limit); //IN packet to EP-'endpoint'. Function takes care of NAKS. if(rcode == hrTOGERR) { // yes, we flip it wrong here so that next time it is actually correct! pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value continue; } if(rcode) { //printf(">>>>>>>> Problem! dispatchPkt %2.2x\r\n", rcode); break; //should be 0, indicating ACK. Else return error code. } /* check for RCVDAVIRQ and generate error if not present */ /* the only case when absence of RCVDAVIRQ makes sense is when toggle error occurred. Need to add handling for that */ if((regRd(rHIRQ) & bmRCVDAVIRQ) == 0) { //printf(">>>>>>>> Problem! NO RCVDAVIRQ!\r\n"); rcode = 0xf0; //receive error break; } pktsize = regRd(rRCVBC); //number of received bytes //printf("Got %i bytes \r\n", pktsize); // This would be OK, but... //assert(pktsize <= nbytes); if(pktsize > nbytes) { // This can happen. Use of assert on Arduino locks up the Arduino. // So I will trim the value, and hope for the best. //printf(">>>>>>>> Problem! Wanted %i bytes but got %i.\r\n", nbytes, pktsize); pktsize = nbytes; } int16_t mem_left = (int16_t)nbytes - *((int16_t*)nbytesptr); if(mem_left < 0) mem_left = 0; data = bytesRd(rRCVFIFO, ((pktsize > mem_left) ? mem_left : pktsize), data); regWr(rHIRQ, bmRCVDAVIRQ); // Clear the IRQ & free the buffer *nbytesptr += pktsize; // add this packet's byte count to total transfer length /* The transfer is complete under two conditions: */ /* 1. The device sent a short packet (L.T. maxPacketSize) */ /* 2. 'nbytes' have been transferred. */ if((pktsize < maxpktsize) || (*nbytesptr >= nbytes)) // have we transferred 'nbytes' bytes? { // Save toggle value pep->bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0; //printf("\r\n"); rcode = 0; break; } // if } //while( 1 ) return ( rcode); }