/* * -------------------------------------------------------------------------------- * Using libusb to access a PIC18 device. We presume a fairly simple device which * has only three endpoints : Control, interrupt-Input, interrupt-Output. * Leave the Control endpoint alone for the kernel to talk to, * that leaves us only two endpoints, one for each direction of data transfer. * * This is designed mostly for debugging and experimentation because it works * interractively. The PIC runs bytecode commands via USB data transfer and sends * back the results of those commands. The user can then observe the opcodes * and test various features. Generally the opcode interpreter will not require * any changes, merely the implementation of additional opcodes. Any unimplemented * opcode will be a noop and harmlessly echo the character. * -------------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include struct usb_bus *busses; usb_dev_handle *devhandle; int ifno; int idVendor = 0xDEAD; int idProduct = 0xBEEF; int idle_counter; int setup( void ) { struct usb_bus *bus; int e; devhandle = 0; usb_init(); usb_find_busses(); usb_find_devices(); busses = usb_get_busses(); for( bus = busses; bus; bus = bus->next ) { struct usb_device *dev; for( dev = bus->devices; dev; dev = dev->next ) { if( dev->descriptor.idVendor == idVendor && dev->descriptor.idProduct == idProduct ) { devhandle = usb_open( dev ); if( !devhandle ) { fprintf( stderr, "Failed to open %s\n", dev->filename ); perror( "Failed to open USB device\n" ); return( -1 ); } /* * Presume a simple device with only one configuration * * In theory, all sorts of complicated things might be possible. * In practice, it seems unlikely anyone would bother. */ if( !dev->config ) { fprintf( stderr, "Broken device config... not operational\n" ); goto release_and_fail; } ifno = dev->config[ 0 ].interface[ 0 ].altsetting[ 0 ].bInterfaceNumber; e = usb_claim_interface( devhandle, ifno ); if( e < 0 ) { perror( "Failed to claim USB device (possibly in use elsewhere)\n" ); goto release_and_fail; } /* * Setting the interface seems to be REQUIRED because * otherwise the OS will not accept that the endpoints exist */ e = usb_set_altinterface( devhandle, 0 ); if( e < 0 ) { perror( "Cannot set interface to 0\n" ); goto release_and_fail; } } } } if( !devhandle ) { return( -1 ); } return( 0 ); release_and_fail: usb_release_interface( devhandle, ifno ); devhandle = 0; return( -1 ); } int main( int argc, char *argv[] ) { int e, i; unsigned char buf[ 64 ] = {0}; struct termios tio_start = {0}; struct termios tio_raw = {0}; int last_char = 0; /* * FIXME: do some option parsing here */ e = setup(); if( e < 0 ) { exit( -1 ); } e = tcgetattr( fileno( stdin ), &tio_start ); if( e < 0 ) { perror( "Failed to get terminal settings" ); goto finished; } memcpy( &tio_raw, &tio_start, sizeof( tio_raw )); cfmakeraw( &tio_raw ); e = tcsetattr( fileno( stdin ), TCSANOW, &tio_raw ); if( e < 0 ) { perror( "Failed to put terminal into raw mode" ); goto finished; } e = fcntl( fileno( stdin ), F_GETFL ); e |= O_NONBLOCK; e = fcntl( fileno( stdin ), F_SETFL, e ); if( e < 0 ) { perror( "Failed to go into nonblocking mode" ); goto finished; } /* * Do a few useless writes at the start, try to sync the * toggle bit on the endpoints, ignore the results */ usb_interrupt_write( devhandle, 0x01, " ", 1, 500 ); usb_interrupt_write( devhandle, 0x01, " ", 1, 500 ); printf( " > " ); fflush( stdout ); fflush( stderr ); /* * Our main objective is to run a continuous polling loop such * that keystrokes are entered and we send the keystroke data * directly to the USB device. Return results from the USB device * are inspected and printed as the result. Special case for a * CR or LF from the keyboard... we generate a prompt containing * the idle counter (approx number of idle ticks per minute). */ for(;;) { int j; /* * Attempt to read the keyboard and if we do get chars * then send them to the USB device */ e = read( fileno( stdin ), buf, 64 ); if( e < 0 ) { if( errno != EAGAIN ) { tcsetattr( fileno( stdin ), TCSANOW, &tio_start ); perror( "\n\nError reading keyboard" ); break; } } else if( e == 0 ) { tcsetattr( fileno( stdin ), TCSANOW, &tio_start ); fprintf( stderr, "\n\nEOF\n" ); break; } else { e = usb_interrupt_write( devhandle, 0x01, buf, e, 500 ); if( e < 0 ) { tcsetattr( fileno( stdin ), TCSANOW, &tio_start ); perror( "\n\nError writing to USB" ); break; } // No local echo... USB device will remote echo } /* * Attempt to read the USB, should always succeed in a * relatively short space of time (although the read may * contain no resulting data). */ e = usb_interrupt_read( devhandle, 0x01, buf, 64, 500 ); if( e < 0 ) { tcsetattr( fileno( stdin ), TCSANOW, &tio_start ); perror( "\n\nError reading USB" ); break; } for( j = 0; j < e && buf[ j ]; j++ ) { switch( buf[ j ]) { case '.': idle_counter++; break; case 3: case 4: goto finished; case '\n': /* * Annoying lack of TTY standards */ if( last_char == '\r' ) break; case '\r': printf( "\r\n" ); printf( "%4d> ", idle_counter ); break; default: printf( "%c", buf[ j ]); } last_char = buf[ j ]; } fflush( stdout ); fflush( stderr ); } finished: e = tcsetattr( fileno( stdin ), TCSANOW, &tio_start ); if( devhandle ) { usb_release_interface( devhandle, ifno ); } return( 0 ); }