Adds the usb code that we got in ECE 385. It will not work now, and parts that involve the timer are disabled. It does compile though, with a few warnings. The goal will be to add USB MSD support, which is not actually given to us so I will have to do myself or find some other code to base it off of.
476 lines
18 KiB
C
476 lines
18 KiB
C
|
|
/* USB transfers */
|
|
#define _transfer_c_
|
|
|
|
#include "project_config.h"
|
|
#include <stdio.h>
|
|
|
|
EP_RECORD dev0ep = {{ 0 }}; //Endpoint data structure for uninitialized device during enumeration
|
|
EP_RECORD msd_ep[ 3 ] = {{ 0 }}; //Mass storage bulk-only transport endpoints: 1 control and 2 bulk, IN and OUT
|
|
//ep records for other classes are defined in class-specific modules
|
|
|
|
/* macros to aid filling in TPL */
|
|
#define INIT_VID_PID(v,p) 0x##p##v
|
|
#define INIT_CL_SC_P(c,s,p) 0x##00##p##s##c
|
|
|
|
//const rom USB_TPL_ENTRY TplTable[ USB_NUMTARGETS + 1 ] = {
|
|
//// VID & PID or Client
|
|
//// Class, Subclass & Protocol Config Numep Eprecord Driver
|
|
//{ INIT_VID_PID( 0000, 0000 ), 0, 1, &dev0ep, 0, "Uninitialized" },
|
|
//{ INIT_VID_PID( 0781, 5406 ), 0, 3, msd_ep, MSD_DRIVER, "Mass storage" }, //Sandisk U3 Cruzer Micro
|
|
////{ INIT_VID_PID( 0CF2, 6220 ), 0, 0 }, //ENE UB6220
|
|
//{ INIT_CL_SC_P( 03, 01, 02 ), 0, 3, hid_ep, HIDM_DRIVER, "HID Mouse with Boot protocol" }, //
|
|
//{ INIT_VID_PID( aaaa, 5555 ), 0, 1, NULL, 0, NULL }, //
|
|
//{ INIT_CL_SC_P( 08, 06, 50 ), 0, 3, msd_ep, MSD_DRIVER, "Mass storage" } //Mass storage bulk only class
|
|
//};
|
|
|
|
/* control transfers function pointers */
|
|
const CTRL_XFER ctrl_xfers[ 2 ] = {
|
|
XferCtrlND,
|
|
XferCtrlData
|
|
};
|
|
|
|
/* device table. Filled during enumeration */
|
|
/* index corresponds to device address */
|
|
/* each entry contains pointer to endpoint structure */
|
|
/* and device class to use in various places */
|
|
DEV_RECORD devtable[ USB_NUMDEVICES + 1 ];
|
|
|
|
/* Client Driver Function Pointer Table */
|
|
CLASS_CALLBACK_TABLE ClientDriverTable[ USB_NUMCLASSES ] = {
|
|
{
|
|
MSDProbe, //Mass storage class device init
|
|
MSDEventHandler,
|
|
0
|
|
},
|
|
{
|
|
HIDMProbe, //HID class device init
|
|
HIDMEventHandler,
|
|
0
|
|
},
|
|
{
|
|
HIDKProbe,
|
|
HIDKEventHandler,
|
|
0
|
|
},
|
|
{
|
|
DummyProbe,
|
|
DummyEventHandler,
|
|
0
|
|
}
|
|
};
|
|
/* Control transfer stages function pointer table */
|
|
|
|
|
|
/* USB state machine related variables */
|
|
|
|
BYTE usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE;
|
|
BYTE usb_error;
|
|
BYTE last_usb_task_state = 0;
|
|
|
|
/* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer, */
|
|
/* depending on request. Actual requests are defined as macros */
|
|
/* return codes: */
|
|
/* 00 = success */
|
|
/* 01-0f = non-zero HRSLT */
|
|
BYTE XferCtrlReq( BYTE addr, BYTE ep, BYTE bmReqType, BYTE bRequest, BYTE wValLo, BYTE wValHi, WORD wInd, WORD nbytes, BYTE* dataptr )
|
|
{
|
|
BOOL direction = FALSE; //request direction, IN or OUT
|
|
BYTE datastage = 1; //request data stage present or absent
|
|
BYTE rcode;
|
|
SETUP_PKT setup_pkt;
|
|
if( dataptr == NULL ) {
|
|
datastage = 0;
|
|
}
|
|
MAXreg_wr( rPERADDR, addr ); //set peripheral address
|
|
/* fill in setup packet */
|
|
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;
|
|
MAXbytes_wr( rSUDFIFO, 8, (BYTE *)&setup_pkt ); //transfer to setup packet FIFO
|
|
rcode = XferDispatchPkt( tokSETUP, ep ); //dispatch packet
|
|
if( rcode ) { //return HRSLT if not zero
|
|
return( rcode );
|
|
}
|
|
rcode = ctrl_xfers[ datastage ]( addr, ep, nbytes, dataptr, direction ); //call data stage or no data stage transfer
|
|
return( rcode );
|
|
}
|
|
/* Control transfer with data stage */
|
|
BYTE XferCtrlData( BYTE addr, BYTE ep, WORD nbytes, BYTE* dataptr, BOOL direction )
|
|
{
|
|
BYTE rcode;
|
|
|
|
//MAXreg_wr( rHCTL, bmRCVTOG1 ); //set toggle to DATA1
|
|
if( direction ) { //IN transfer
|
|
devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG1;
|
|
rcode = XferInTransfer( addr, ep, nbytes, dataptr, devtable[ addr ].epinfo[ ep ].MaxPktSize );
|
|
if( rcode ) {
|
|
return( rcode );
|
|
}
|
|
rcode = XferDispatchPkt( tokOUTHS, ep );
|
|
return( rcode );
|
|
}
|
|
else { //OUT not implemented
|
|
return( 0xff );
|
|
}
|
|
}
|
|
/* Control transfer with status stage and no data stage */
|
|
BYTE XferCtrlND( BYTE addr, BYTE ep, WORD nbytes, BYTE* dataptr, BOOL direction )
|
|
{
|
|
BYTE rcode;
|
|
|
|
(void) addr;
|
|
(void) nbytes;
|
|
(void) dataptr;
|
|
|
|
if( direction ) { //GET
|
|
rcode = XferDispatchPkt( tokOUTHS, ep );
|
|
}
|
|
else {
|
|
rcode = XferDispatchPkt( tokINHS, ep );
|
|
}
|
|
return( rcode );
|
|
}
|
|
/* Dispatch a packet. Assumes peripheral address is set and, if necessary, sudFIFO-sendFIFO loaded. */
|
|
/* Result code: 0 success, nonzero = error condition */
|
|
/* If NAK, tries to re-send up to USB_NAK_LIMIT times */
|
|
/* If bus timeout, re-sends up to USB_RETRY_LIMIT times */
|
|
/* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout */
|
|
BYTE XferDispatchPkt( BYTE token, BYTE ep )
|
|
{
|
|
DWORD timeout = 0;//(alt_nticks()*1000)/alt_ticks_per_second() + USB_XFER_TIMEOUT;
|
|
BYTE tmpdata;
|
|
BYTE rcode;
|
|
char retry_count = 0;
|
|
BYTE nak_count = 0;
|
|
|
|
while( 1 ) {
|
|
MAXreg_wr( rHXFR, ( token|ep )); //launch the transfer
|
|
rcode = 0xff;
|
|
/*
|
|
while( (alt_nticks()*1000)/alt_ticks_per_second() < timeout ) {
|
|
tmpdata = MAXreg_rd( rHIRQ );
|
|
if( tmpdata & bmHXFRDNIRQ ) {
|
|
MAXreg_wr( rHIRQ, bmHXFRDNIRQ ); //clear the interrupt
|
|
rcode = 0x00;
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
if( rcode != 0x00 ) { //exit if timeout
|
|
return( rcode );
|
|
}
|
|
rcode = ( MAXreg_rd( rHRSL ) & 0x0f );
|
|
if( rcode == hrNAK ) {
|
|
nak_count++;
|
|
if( nak_count == USB_NAK_LIMIT ) {
|
|
break;
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
}
|
|
if( rcode == hrTIMEOUT ) {
|
|
retry_count++;
|
|
if( retry_count == USB_RETRY_LIMIT ) {
|
|
break;
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
}
|
|
else break;
|
|
}//while( 1 )
|
|
return( rcode );
|
|
}
|
|
/* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes.
|
|
Keep sending INs and writes data to memory area pointed by 'data' */
|
|
/* rcode 0 if no errors. rcode 01-0f is relayed from prvXferDispatchPkt(). Rcode f0 means RCVDAVIRQ error,
|
|
fe USB xfer timeout */
|
|
BYTE XferInTransfer( BYTE addr/* not sure if it's necessary */, BYTE ep, WORD nbytes, BYTE* data, BYTE maxpktsize )
|
|
{
|
|
BYTE rcode;
|
|
//BYTE i;
|
|
//BYTE tmpbyte;
|
|
BYTE pktsize;
|
|
WORD xfrlen = 0;
|
|
MAXreg_wr( rHCTL, devtable[ addr ].epinfo[ ep ].rcvToggle ); //set toggle value
|
|
while( 1 ) { // use a 'return' to exit this loop
|
|
rcode = XferDispatchPkt( tokIN, ep ); //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 absence of RCVDAVIRQ makes sense is when toggle error occurred. Need to add handling for that */
|
|
if(( MAXreg_rd( rHIRQ ) & bmRCVDAVIRQ ) == 0 ) {
|
|
return ( 0xf0 ); //receive error
|
|
}
|
|
pktsize = MAXreg_rd( rRCVBC ); //number of received bytes
|
|
//printf ("pktsize: %d\n", pktsize);
|
|
data = MAXbytes_rd( rRCVFIFO, pktsize, data );
|
|
MAXreg_wr( 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( MAXreg_rd( rHRSL ) & bmRCVTOGRD ) { //save toggle value
|
|
devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG1;
|
|
}
|
|
else {
|
|
devtable[ addr ].epinfo[ ep ].rcvToggle = bmRCVTOG0;
|
|
}
|
|
return( 0 );
|
|
}
|
|
}//while( 1 )
|
|
}
|
|
/* initialization of USB data structures */
|
|
void USB_init( void )
|
|
{
|
|
BYTE i;
|
|
for( i = 0; i < ( USB_NUMDEVICES + 1 ); i++ ) {
|
|
devtable[ i ].epinfo = NULL; //clear device table
|
|
devtable[ i ].devclass = 0;
|
|
}
|
|
devtable[ 0 ].epinfo = &dev0ep; //set single ep for uninitialized device
|
|
dev0ep.MaxPktSize = 0;
|
|
dev0ep.sndToggle = bmSNDTOG0; //set DATA0/1 toggles to 0
|
|
dev0ep.rcvToggle = bmRCVTOG0;
|
|
}
|
|
|
|
|
|
USB_DEVICE_DESCRIPTOR buf = {0};
|
|
USB_STRING_DESCRIPTOR strDesc = {0};
|
|
|
|
/* USB state machine. Connect/disconnect, enumeration, initialization */
|
|
/* error codes: 01-0f HRSLT */
|
|
/* ff - unsupported device */
|
|
/* fe - no address available */
|
|
/* fd - no client driver available */
|
|
void USB_Task( void )
|
|
{
|
|
static DWORD usb_delay = 0;
|
|
static BYTE tmp_addr;
|
|
|
|
BYTE rcode, tmpdata;
|
|
BYTE i;
|
|
|
|
switch( usb_task_state & USB_STATE_MASK ) {
|
|
/* Detached state - when nothing is connected to ( or just disconnected from) USB bus */
|
|
case( USB_STATE_DETACHED ):
|
|
switch( usb_task_state ) {
|
|
case( USB_DETACHED_SUBSTATE_INITIALIZE ):
|
|
/* cleanup device data structures */
|
|
USB_init();
|
|
usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE;
|
|
break;
|
|
case( USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE ):
|
|
/* Do nothing */
|
|
MAXreg_wr(rHCTL,bmSAMPLEBUS);
|
|
break;
|
|
case( USB_DETACHED_SUBSTATE_ILLEGAL ):
|
|
/* don't know what to do yet */
|
|
break;
|
|
}//switch( usb_task_state )
|
|
break;//( USB_STATE_DETACHED ):
|
|
/**/
|
|
case( USB_STATE_ATTACHED ): //prepare for enumeration
|
|
switch( usb_task_state ) {
|
|
case( USB_STATE_ATTACHED ):
|
|
//TODO
|
|
//usb_delay = (alt_nticks()*1000)/alt_ticks_per_second() + 200; //initial settle 200ms
|
|
usb_delay = 0;
|
|
usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE;
|
|
break;//case( USB_STATE_ATTACHED )
|
|
case( USB_ATTACHED_SUBSTATE_SETTLE ): //waiting for settle timer to expire
|
|
//TODO
|
|
/*
|
|
if( (alt_nticks()*1000)/alt_ticks_per_second() > usb_delay ) {
|
|
usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE;
|
|
}
|
|
*/
|
|
break;//case( USB_ATTACHED_SUBSTATE_SETTLE )
|
|
case( USB_ATTACHED_SUBSTATE_RESET_DEVICE ):
|
|
MAXreg_wr( rHIRQ, bmBUSEVENTIRQ ); //clear bus event IRQ
|
|
MAXreg_wr( rHCTL, bmBUSRST ); //issue bus reset
|
|
usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE;
|
|
break;//case( USB_ATTACHED_SUBSTATE_RESET_DEVICE )
|
|
case( USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE ): //wait for bus reset and first SOF
|
|
if(( MAXreg_rd( rHCTL ) & bmBUSRST ) == 0 ) {
|
|
tmpdata = MAXreg_rd( rMODE ) | bmSOFKAENAB; //start SOF generation
|
|
MAXreg_wr( rMODE, tmpdata );
|
|
usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF;
|
|
}
|
|
break;//case( USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE )
|
|
case( USB_ATTACHED_SUBSTATE_WAIT_SOF ):
|
|
if( MAXreg_rd( rHIRQ ) | bmFRAMEIRQ ) { //when first SOF received we can continue
|
|
usb_task_state = USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE;
|
|
}
|
|
break;//case( USB_ATTACHED_SUBSTATE_WAIT_SOF )
|
|
case( USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE ): //send request for first 8 bytes of device descriptor
|
|
devtable[ 0 ].epinfo->MaxPktSize = 0x0008; //fill max packet size with minimum allowed
|
|
rcode = XferGetDevDescr( 0, 0, 8, (BYTE *)&buf ); //get device descriptor size
|
|
if( rcode == 0 ) {
|
|
devtable[ 0 ].epinfo->MaxPktSize = buf.bMaxPacketSize0;
|
|
|
|
rcode = XferGetDevDescr( 0, 0, buf.bLength, (BYTE *)&buf ); //get full descriptor
|
|
//pull the string descriptor for the product if it exists
|
|
//hackish, store this somewhere
|
|
if (buf.iManufacturer != 0)
|
|
{
|
|
rcode = XferGetStrDescr( 0, 0, 2, buf.iManufacturer, LANG_EN_US, (BYTE *)&strDesc);
|
|
rcode = XferGetStrDescr( 0, 0, strDesc.bLength, buf.iManufacturer, LANG_EN_US, (BYTE *)&strDesc);
|
|
//printf ("Mfgr string(%i): %s\n", buf.iManufacturer, ConvUTF8ToStr(strDesc.bString, (strDesc.bLength>>1)-1));
|
|
}
|
|
if (buf.iProduct != 0)
|
|
{
|
|
rcode = XferGetStrDescr( 0, 0, 2, buf.iProduct, LANG_EN_US, (BYTE *)&strDesc);
|
|
rcode = XferGetStrDescr( 0, 0, strDesc.bLength, buf.iProduct, LANG_EN_US, (BYTE *)&strDesc);
|
|
//printf ("Product string(%i): %s\n", buf.iProduct, ConvUTF8ToStr(strDesc.bString, (strDesc.bLength>>1)-1));
|
|
}
|
|
usb_task_state = USB_STATE_ADDRESSING;
|
|
|
|
}
|
|
else {
|
|
usb_error = rcode;
|
|
last_usb_task_state = usb_task_state;
|
|
usb_task_state = USB_STATE_ERROR;
|
|
}
|
|
break;//case( USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE ):
|
|
}//switch( usb_task_state )
|
|
break;//case ( USB_STATE_ATTACHED )
|
|
case( USB_STATE_ADDRESSING ): //give device an address
|
|
for( i = 1; i < USB_NUMDEVICES; i++ ) {
|
|
if( devtable[ i ].epinfo == NULL ) {
|
|
devtable[ i ].epinfo = devtable[ 0 ].epinfo; //set correct MaxPktSize
|
|
//devtable[ i ].epinfo->MaxPktSize = devtable[ 0 ].epinfo->MaxPktSize; //copy uninitialized device record to have correct MaxPktSize
|
|
rcode = XferSetAddr( 0, 0, i );
|
|
if( rcode == 0 ) {
|
|
tmp_addr = i;
|
|
usb_task_state = USB_STATE_CONFIGURING;
|
|
}
|
|
else {
|
|
usb_error = rcode; //set address error
|
|
last_usb_task_state = usb_task_state;
|
|
usb_task_state = USB_STATE_ERROR;
|
|
}
|
|
break; //break if address assigned or error occurred during address assignment attempt
|
|
}
|
|
}
|
|
if( usb_task_state == USB_STATE_ADDRESSING ) {
|
|
usb_error = 0xfe;
|
|
last_usb_task_state = usb_task_state;
|
|
usb_task_state = USB_STATE_ERROR;
|
|
}
|
|
break;//case ( USB_STATE_ADDRESSING )
|
|
case( USB_STATE_CONFIGURING ): //checking for driver
|
|
//run device class probes until one returns TRUE
|
|
for( i = 0; i < USB_NUMCLASSES; i++ ) {
|
|
rcode = ClientDriverTable[ i ].Initialize( tmp_addr, 0 );
|
|
if( rcode == TRUE ) {
|
|
usb_task_state = USB_STATE_RUNNING;
|
|
break;
|
|
}
|
|
}
|
|
if( usb_task_state == USB_STATE_CONFIGURING ) {
|
|
usb_error = 0xfd;
|
|
last_usb_task_state = usb_task_state;
|
|
usb_task_state = USB_STATE_ERROR;
|
|
}
|
|
break;//( USB_STATE_CONFIGURING )
|
|
case( USB_STATE_RUNNING ):
|
|
//vTaskDelay( LED_RATE );
|
|
break;//( USB_STATE_RUNNING )
|
|
case( USB_STATE_ERROR ):
|
|
//vTaskDelay( LED_RATE ); //stay here if error
|
|
break;//( USB_STATE_ERROR )
|
|
default:
|
|
//Should never get here
|
|
break;
|
|
}//switch( usb_task_state & STATE_MASK )
|
|
}
|
|
|
|
//place-holders for MSD (mass-storage device) drivers, we don't have them ported.
|
|
//returns TRUE if device is successfully identified and configured, otherwise returns FALSE
|
|
BOOL MSDProbe( BYTE addr, DWORD flags )
|
|
{
|
|
(void) addr;
|
|
(void) flags;
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOL MSDEventHandler( BYTE address, BYTE event, void *data, DWORD size )
|
|
{
|
|
(void) address;
|
|
(void) event;
|
|
(void) data;
|
|
(void) size;
|
|
return( FALSE );
|
|
|
|
}
|
|
//CDC (communication device class also not supported)
|
|
BOOL CDCProbe( BYTE address, DWORD flags )
|
|
{
|
|
(void) address;
|
|
(void) flags;
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
BOOL CDCEventHandler( BYTE address, BYTE event, void *data, DWORD size )
|
|
{
|
|
(void) address;
|
|
(void) event;
|
|
(void) data;
|
|
(void) size;
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOL DummyProbe( BYTE address , DWORD flags )
|
|
{
|
|
(void) address;
|
|
(void) flags;
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOL DummyEventHandler( BYTE address, BYTE event, void *data, DWORD size )
|
|
{
|
|
(void) address;
|
|
(void) event;
|
|
(void) data;
|
|
(void) size;
|
|
|
|
return( FALSE );
|
|
}
|
|
/* Function to access usb_task_state variable from outside */
|
|
BYTE GetUsbTaskState( void )
|
|
{
|
|
return( usb_task_state );
|
|
}
|
|
/* Function to access devtable[] from outside */
|
|
DEV_RECORD* GetDevtable( BYTE index )
|
|
{
|
|
return( &devtable[ index ] );
|
|
}
|
|
|
|
char* ConvUTF8ToStr(BYTE* utf8, BYTE length)
|
|
{
|
|
BYTE i;
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
utf8[i] = utf8[2*i];
|
|
}
|
|
utf8[length] = 0x00;
|
|
return (char*)utf8;
|
|
}
|