/* 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 }
/* 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 ); }
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 }
/* 01-0f = non-zero HRSLT */ uint8_t USB::ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi, uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p) { bool direction = false; //request direction, IN or OUT uint8_t rcode; SETUP_PKT setup_pkt; EpInfo *pep = NULL; uint16_t nak_limit = 0; rcode = SetAddress(addr, ep, &pep, nak_limit); if(rcode) return rcode; direction = ((bmReqType & 0x80) > 0); /* 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 = total; bytesWr(rSUDFIFO, 8, (uint8_t*) & setup_pkt); //transfer to setup packet FIFO rcode = dispatchPkt(tokSETUP, ep, nak_limit); //dispatch packet if(rcode) //return HRSLT if not zero return ( rcode); if(dataptr != NULL) //data stage, if present { if(direction) //IN transfer { uint16_t left = total; pep->bmRcvToggle = 1; //bmRCVTOG1; while(left) { // Bytes read into buffer uint16_t read = nbytes; //uint16_t read = (left<nbytes) ? left : nbytes; rcode = InTransfer(pep, nak_limit, &read, dataptr); if(rcode == hrTOGERR) { // yes, we flip it wrong here so that next time it is actually correct! pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1; continue; } if(rcode) return rcode; // Invoke callback function if inTransfer completed successfully and callback function pointer is specified if(!rcode && p) ((USBReadParser*)p)->Parse(read, dataptr, total - left); left -= read; if(read < nbytes) break; } } else //OUT transfer { pep->bmSndToggle = 1; //bmSNDTOG1; rcode = OutTransfer(pep, nak_limit, nbytes, dataptr); } if(rcode) //return error return ( rcode); } // Status stage return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit); //GET if direction }