//------------------------------------------------------------------- // ajayecho.cpp // // This program illustrates our programming of data transfers // using a Hi-Speed USB Debug Cable made by Ajays Technology: // the data received will get transferred back to its sender. // It relies upon the services of our 'ajay.c' kernel module, // and it expects data to be sent by our 'ajayxfer.cpp' demo. // // to compile: $ g++ ajayecho.cpp -o ajayecho // to execute: $ ./ajayecho // // // Reference: // "USB2 Debug Device: A Functional Specification (Rev 0.9)" // online at: // // programmer: ALLAN CRUSE // written on: 31 JAN 2010 // revised on: 07 FEB 2010 -- added -C signal-handler //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for lseek(), usleep() #include // for signal() #include // for mmap() #define AJAYBASE 0x80000 // suitable address for io-mapping #define BULKSIZE 512 // maximum size of 'bulk' transfer unsigned int *lp = (unsigned int*)AJAYBASE; // registers array char devname[] = "/dev/ajay"; // name for our driver device-node void my_sigint_handler( int signo ) { exit(1); } int initiate_debug_port_transaction( unsigned int *lp ) { lp[ 0xA0 >> 2 ] = lp[ 0xA0 >> 2 ] | (1 << 5); // set the GO bit while ( ( lp[ 0xA0 >> 2 ] & (1 << 16)) == 0 ); // await DONE bit return ( ( lp[ 0xA0 >> 2 ] >> 6 )&0xF ); // GOOD or ERROR } void display_controller_registers( unsigned int *lp ) { printf( " USB2CMD=%08X ", lp[ 0x20 >> 2 ] ); printf( " USB2STS=%08X ", lp[ 0x24 >> 2 ] ); printf( " PORT0SC=%08X ", lp[ 0x64 >> 2 ] ); printf( " CNTL_STS=%08X ", lp[ 0xA0 >> 2 ] ); printf( "\n" ); } void release_debug_port_ownership( void ) { printf( "\n\n Quitting... \n" ); lp[ 0xA0 >> 2 ] = 0x00010000; // OWNER_CTL=0, ENABLE_CTL=0 display_controller_registers( lp ); } void exhibit_debug_port_registers( unsigned int *lp ) { unsigned int token_pid = (lp[ 0xA4 >> 2 ] >> 0)&0xFF; unsigned int recv_pid = (lp[ 0xA4 >> 2 ] >> 16)&0xFF; switch( token_pid ) { case 0x2D: printf( "\r SETUP: " ); break; case 0x69: printf( "\r IN: " ); break; case 0xE1: printf( "\r OUT: " ); break; default: printf( "\r " ); break; } printf( " CNTL_STS=%08X ", lp[ 0xA0 >> 2 ] ); printf( " USBPID=%06X ", lp[ 0xA4 >> 2 ] ); printf( " DATABUF=%08X-%08X ", lp[ 0xA8 >> 2 ], lp[ 0xAC >> 2 ] ); switch ( recv_pid ) { case 0xD2: printf( "(ACK) " ); break; case 0x5A: printf( "(NAK) " ); break; case 0x96: printf( "(NYET) " ); break; case 0x1E: printf( "(STALL)" ); break; case 0xC3: printf( "(DATA0)" ); break; case 0x4B: printf( "(DATA1)" ); break; default: printf( "(--?--)" ); break; } } int main( int argc, char **argv ) { int err; // open the device-file for reading and writing int fd = open( devname, O_RDWR ); if ( fd < 0 ) { perror( devname ); exit(1); } // map the EHCI registers to user-space int size = lseek( fd, 0, SEEK_END ); int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; void *mm = (void*)AJAYBASE; if ( mmap( mm, size, prot, flag, fd, 0 ) == MAP_FAILED ) { perror( "mmap" ); exit(1); } // insure that debug port 'ownership' will get released atexit( release_debug_port_ownership ); signal( SIGINT, my_sigint_handler ); // show state of the relevant EHCI controller registers printf( "\n Detecting... \n" ); printf( " CONFIGFLAG=%X ", lp[ 0x60 >> 2 ] ); printf( " USB2INTR=%08X ", lp[ 0x28 >> 2 ] ); printf( "\n" ); display_controller_registers( lp ); printf( "\n" ); // interpret readiness of the EHCI for Debug Port usage unsigned int port0sc = lp[ 0x64 >> 2 ]; unsigned int cfgflag = lp[ 0x60 >> 2 ]; unsigned int dbgcmd = lp[ 0xA0 >> 2 ]; unsigned int usbpid = lp[ 0xA4 >> 2 ]; unsigned int config = lp[ 0xB0 >> 2 ]; if ( cfgflag == 1 ) printf( " EHCI initialized" ); else { printf( "\n EHCI controller is uninitialized " ); exit(1); } if ( (port0sc & 1)==1 ) printf( ", Port 0 connected" ); else { printf( "\n Port 0 is not connected " ); exit(1); } if ( ( (port0sc >> 13)&1 )==0 ) printf( ", but not owned" ); else { printf( "\n Port 0 is already owned " ); exit(1); } if ( ( (dbgcmd >> 30)&1 )==0 ) printf( ", Debug Port available" ); else { printf( "\n Debug Port is already owned " ); exit(1); } printf( "\n\n" ); // initiate Port 0 reset port0sc = lp[ 0x64 >> 2 ]; // read current PORT0SC bits port0sc &= ~(1 << 13); // clear Port0 'owned' bit port0sc &= ~(1 << 2); // clear Port0 'enabled' bit port0sc |= (1 << 8 ); // set the Port0 'reset' bit lp[ 0x64 >> 2 ] = port0sc; // write revised PORT0SC bits usleep( 50000 ); // delay for 50 milliseconds // terminate Port 0 reset port0sc = lp[ 0x64 >> 2 ]; // read current PORT0SC bits port0sc &= ~(1 << 8 ); // clear the Port0 'reset' bit lp[ 0x64 >> 2 ] = port0sc; // write revised PORT0SC bits usleep( 2000 ); // delay for 2 milliseconds // configure the Debug Port printf( " Configuring... \n" ); lp[ 0xA0 >> 2 ] = 0x50010000; printf( " USB2CMD=%08X ", lp[ 0x20 >> 2 ] ); printf( " USB2STS=%08X ", lp[ 0x24 >> 2 ] ); printf( " PORT0SC=%08X ", lp[ 0x64 >> 2 ] ); printf( " CNTL_STS=%08X ", lp[ 0xA0 >> 2 ] ); printf( "\n" ); // implement the 'Get_Descriptor' control-transfer printf( "\n Initiating our 'GET_DESCRIPTOR' control-transfer: \n" ); // 1. the SETUP stage (Get_Descriptor) lp[ 0xB0 >> 2 ] = 0x00000000; // CONFIG: device=0, endpoint=0 lp[ 0xA8 >> 2 ] = 0x0A000680; // DATA_31: bRequest=6, wValue=10 lp[ 0xAC >> 2 ] = 0x00080000; // DATA_74: wIndex=0, wLength=8 lp[ 0xA4 >> 2 ] = 0x0000C32D; // USBPID: SETUP, DATA0 lp[ 0xA0 >> 2 ] = 0x50010018; // CNTL_STS: transmit 8 bytes err = initiate_debug_port_transaction( lp ); exhibit_debug_port_registers( lp ); printf( "\n"); if ( err ) exit( 1 ); // 2. the DATA stage (Get_Descriptor) lp[ 0xA8 >> 2 ] = 0x00000000; // DATABUF_31: all zeros lp[ 0xAC >> 2 ] = 0x00000000; // DATABUF_74: all zeros lp[ 0xA4 >> 2 ] = 0x0000C369; // USBPID: IN, DATA0 lp[ 0xA0 >> 2 ] = 0x50010008; // CNTL_STS: receive 4 bytes err = initiate_debug_port_transaction( lp ); exhibit_debug_port_registers( lp ); printf( "\n"); if ( err ) exit( 1 ); // 3. the HANDSHAKE (Get_Descriptor) lp[ 0xA4 >> 2 ] = 0x0000C3E1; // USBPID: OUT, DATA0 lp[ 0xA0 >> 2 ] = 0x50010010; // CNTL_STS: transmit 0 bytes err = initiate_debug_port_transaction( lp ); exhibit_debug_port_registers( lp ); printf( "\n"); if ( err ) exit( 1 ); // check for an unexpected peripheral attached to Port 0 usbpid = lp[ 0xA4 >> 2 ]; if ( (usbpid >> 16) != 0xD2 ) { printf( " Handshake was not 'ACK' " ); printf( "(i.e., Port 0 not attached to Debug Peripheral) " ); exit(1); } // show the Debug Descriptor unsigned char descriptor[ 8 ]; *(unsigned int *)( descriptor+0 ) = lp[ 0xA8 >> 2 ]; *(unsigned int *)( descriptor+4 ) = lp[ 0xAC >> 2 ]; printf( "\n Debug Descriptor: " ); for (int i = 0; i < 4; i++) printf( " %02X", descriptor[ i ] ); printf( "\n" ); // Now implement the 'Set_Feature' control-transfer printf( "\n Initiating our 'SET_FEATURE' control-transfer: \n" ); // 1. the SETUP stage (Set_Feature) lp[ 0xA8 >> 2 ] = 0x00060300; // DATABUF_30: bRequest=3, wValue=6 lp[ 0xAC >> 2 ] = 0x00000000; // DATABUF_73: wIndex=0, wLength=0 lp[ 0xA4 >> 2 ] = 0x0000C32D; // USBPID: SETUP, DATA0 lp[ 0xA0 >> 2 ] = 0x50010018; // CNTL_STS: transmit 8 bytes err = initiate_debug_port_transaction( lp ); exhibit_debug_port_registers( lp ); printf( "\n"); if ( err ) exit( 1 ); // 2. the optional DATA phase (Set_Feature) lp[ 0xA4 >> 2 ] = 0x0000C369; // USBPID: IN, DATA0 lp[ 0xA0 >> 2 ] = 0x50010000; // CNTL_STS: receive 0 bytes err = initiate_debug_port_transaction( lp ); exhibit_debug_port_registers( lp ); printf( "\n"); if ( err ) exit( 1 ); // 3. the HANDSHAKE phase (Set_Feature) lp[ 0xA4 >> 2 ] = 0x00004BE1; // USBPID: OUT, DATA1 lp[ 0xA0 >> 2 ] = 0x50010010; // CNTL_STS: transmit 0 bytes err = initiate_debug_port_transaction( lp ); exhibit_debug_port_registers( lp ); printf( "\n"); if ( err ) exit( 1 ); //========================================================= // LOOP TO PERFORM THE 'IN' TRANSFERS //========================================================= printf( "\n\n Now awaiting data from link-partner... \n" ); config = 0x00000002; // CONFIG: device=0, endpoint=2 usbpid = 0x0000C369; // USBPID: IN, DATA0 dbgcmd = 0x50010008; // CNTL_STS: receive 8 bytes char databuf[ BULKSIZE + 8 ] = { 0 }; int rxbytes = 0; int where = 0; do { lp[ 0xA8 >> 2 ] = 0x00000000; // DATABUF_30 lp[ 0xAC >> 2 ] = 0x00000000; // DATABUF_74 lp[ 0xB0 >> 2 ] = config; // CONFIG: lp[ 0xA4 >> 2 ] = usbpid; // USBPID: lp[ 0xA0 >> 2 ] = dbgcmd; // CNTL_SYS err = initiate_debug_port_transaction( lp ); exhibit_debug_port_registers( lp ); if ( err ) exit( 1 ); dbgcmd = lp[ 0xA0 >> 2 ]; usbpid = lp[ 0xA4 >> 2 ]; if ( (usbpid >> 16) != 0x5A ) printf( "\n" ); else continue; // status was 'NAK' *(unsigned int *)( databuf + where + 0 ) = lp[ 0xA8 >> 2 ]; *(unsigned int *)( databuf + where + 4 ) = lp[ 0xAC >> 2 ]; where += 8; usbpid ^= 0x00008800; rxbytes += ( dbgcmd & 0x0F ); if ( ( dbgcmd & 0x0F ) < 8 ) break; else dbgcmd = 0x50010008; } while ( where < BULKSIZE ); printf( "\n OK, received %d bytes from link partner \n\n", rxbytes ); databuf[ rxbytes ] = '\0'; printf( "%s\n", databuf ); //========================================================== // LOOP TO PERFORM THE 'OUT' TRANSFERS //========================================================== printf( "\n Now returning %d bytes to link partner... \n", rxbytes ); config = 0x00000001; // CONFIG: device=0, endpoint=1 usbpid = 0x0000C3E1; // USBPID: OUT, DATA0 dbgcmd = 0x50010018; // CNTL_STS: transmit 8 bytes int out_count = 0; int to_do = rxbytes; where = 0; do { if ( ( out_count % 3 ) == 0 ) usleep( 1000 ); ++out_count; if ( to_do < 8 ) dbgcmd = 0x50010010 | to_do; lp[ 0xA8 >> 2 ] = *(unsigned int *)( databuf + where + 0 ); lp[ 0xAC >> 2 ] = *(unsigned int *)( databuf + where + 4 ); lp[ 0xB0 >> 2 ] = config; // CONFIG lp[ 0xA4 >> 2 ] = usbpid; // USBPID lp[ 0xA0 >> 2 ] = dbgcmd; // CNTL_STS err = initiate_debug_port_transaction( lp ); exhibit_debug_port_registers( lp ); printf( "\n"); if ( err ) exit( 1 ); // NOTE: The Debug Device specification says: // "NYET responses from the OUT endpoints // must be equivalent to an ACK response." // see 'Device Requirements' on page 2 dbgcmd = lp[ 0xA0 >> 2 ]; usbpid = lp[ 0xA4 >> 2 ]; if ( (usbpid >> 16) == 0x5A ) // status 'NAK' { usleep( 1000 ); continue; } usbpid ^= 0x00008800; where += 8; to_do -= 8; } while ( to_do >= 0 ); }