//------------------------------------------------------------------- // findajay.cpp // // This example loops through the sequence of valid USB device // numbers until it has obtained the Device Descriptor for the // 'NET20DC' USB 2.0 High-Speed Debug Cable (recognized by USB // identifier-numbers) or until the search has been exhausted. // // to compile: $ g++ findajay.cpp -o findajay // to execute: $ ./findajay // // NOTE: Requires installing our 'ehci.c' device-driver module. // // programmer: ALLAN CRUSE // written on: 30 MAR 2010 // revised on: 02 APR 2010 -- to 'reuse' QH and TD structures //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for lseek() #include // for memset() #include // for signal() #include // for mmap() #include // for ioctl() #define EHCI_BASE 0x80000 // suitable address for i/o mapping #define PAGE_SIZE 0x1000 // memory-granularity on x86 system #define VENDOR_ID 0x0525 // USB Vendor Identification-number #define DEVICE_ID 0x127A // USB Device Identification-number typedef struct { unsigned char bLength; unsigned char bDescriptorType; unsigned short bcdUSB; unsigned char bDeviceClass; unsigned char bDeviceSubClass; unsigned char bDeviceProtocol; unsigned char bMaxPacketSize0; unsigned short idVendor; unsigned short idProduct; unsigned short bcdDevice; unsigned char iManufacturer; unsigned char iProduct; unsigned char iSerialNumber; unsigned char bNumConfigurations; } USB_DD; // USB Device Descriptor typedef struct { unsigned char bmReqType; unsigned char bRequest; unsigned short wValue; unsigned short wIndex; unsigned short wLength; } USB_RB; // USB Request Block typedef struct { unsigned int qhHorizontalLink; unsigned int qhEndPtCharacteristics; unsigned int qhEndPtCapabilities; unsigned int qhCurrentTDPtr; unsigned int qhNextTDPtr; unsigned int qhAltNextTDPtr; unsigned char qhStatus; unsigned char qhAction; unsigned short qhTotalBytes; unsigned int qhBufferPtr[5]; unsigned int qhExtBufferPtr[5]; unsigned int qhPadding[15]; } EHCI_QH64; // EHCI Queue Head typedef struct { unsigned int tdNextTDPtr; unsigned int tdAltNextTDPtr; unsigned char tdStatus; unsigned char tdAction; unsigned short tdTotalBytes; unsigned int tdBufferPtr[5]; unsigned int tdExtBufferPtr[5]; unsigned int tdPadding[3]; } EHCI_TD64; // EHCI Transfer Descriptor void display_QH64( int index ); void display_TD64( int index ); char devname[] = "/dev/ehci"; unsigned long kmem_phys; unsigned int *lp = (unsigned int *)(EHCI_BASE + PAGE_SIZE * 0); EHCI_QH64 *qh = (EHCI_QH64 *)(EHCI_BASE + PAGE_SIZE * 1); EHCI_TD64 *td = (EHCI_TD64 *)(EHCI_BASE + PAGE_SIZE * 3); USB_RB *rb = (USB_RB *)(EHCI_BASE + PAGE_SIZE * 4); USB_DD *dd = (USB_DD *)(EHCI_BASE + PAGE_SIZE * 5); unsigned int usb2cmd, usb2sts, usb2intr, asynclistaddr; unsigned int qh_phys, td_phys, rb_phys, dd_phys; void restore_asynclist_address( void ) { lp[ 0x20 >> 2 ] &= ~(1<<5); // disable asynchronous schedule while( ( lp[ 0x24 >> 2 ] & (1<<15) ) != 0 ); // wait till done lp[ 0x38 >> 2 ] = asynclistaddr; // restore initial value usb2sts = lp[ 0x24 >> 2 ]; // read current status lp[ 0x24 >> 2 ] = usb2sts; // reset write-to-clears lp[ 0x28 >> 2 ] = usb2intr; // restore initial value lp[ 0x20 >> 2 ] = usb2cmd; // restore initial value } void my_sigint_handler( int signo ) { exit(1); } int main( int argc, char **argv ) { // open the device-file for reading and writing int fd = open( devname, O_RDWR ); if ( fd < 0 ) { perror( devname ); exit(1); } // initialize our physical region memory-addresses if ( ioctl( fd, 0, &kmem_phys ) < 0 ) { perror( "ioctl" ); exit(1); } qh_phys = kmem_phys + PAGE_SIZE * 0; td_phys = kmem_phys + PAGE_SIZE * 2; rb_phys = kmem_phys + PAGE_SIZE * 3; dd_phys = kmem_phys + PAGE_SIZE * 4; // map EHCI registers and kernel-memory regions into userspace int size = lseek( fd, 0, SEEK_END ); int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; void *mm = (void*)EHCI_BASE; if ( mmap( mm, size, prot, flag, fd, 0 ) == MAP_FAILED ) { perror( "mmap" ); exit(1); } // save some initial register-values to be restored later usb2cmd = lp[ 0x20 >> 2 ]; usb2sts = lp[ 0x24 >> 2 ]; usb2intr = lp[ 0x28 >> 2 ]; asynclistaddr = lp[ 0x38 >> 2 ]; // insure that 'asynclistaddr' will be restored upon exit atexit( restore_asynclist_address ); signal( SIGINT, my_sigint_handler ); // preinitialize a linked list of four Transfer Descriptors memset( &td[0], 0x00, 4 * sizeof( EHCI_TD64 ) ); td[0].tdNextTDPtr = 0x00000001; // invalid td[0].tdAltNextTDPtr = 0x00000001; // invalid td[0].tdStatus = 0x40; // Halted td[1].tdNextTDPtr = td_phys + 2 * sizeof( EHCI_TD64 ); td[1].tdAltNextTDPtr = 0x00000001; // invalid td[2].tdNextTDPtr = td_phys + 3 * sizeof( EHCI_TD64 ); td[2].tdAltNextTDPtr = 0x00000001; // invalid td[3].tdNextTDPtr = td_phys + 0 * sizeof( EHCI_TD64 ); td[3].tdAltNextTDPtr = 0x00000001; // invalid // setup a circular list containing a pair of queue-heads memset( &qh[0], 0x00, 2 * sizeof( EHCI_QH64 ) ); qh[0].qhHorizontalLink = (qh_phys + 1 * sizeof( EHCI_QH64 )) | (1<<1); qh[0].qhEndPtCharacteristics = (1<<15); // H=1 qh[0].qhEndPtCapabilities = 0x00000000; // MULT=0 qh[0].qhNextTDPtr = td_phys; // TD#0 qh[0].qhAltNextTDPtr = 0x00000001; // invalid qh[1].qhHorizontalLink = (qh_phys + 0 * sizeof( EHCI_QH64 )) | (1<<1); qh[1].qhEndPtCharacteristics = 0x40406000; // H=0 qh[1].qhEndPtCapabilities = 0x40000000; // MULT=1 qh[1].qhNextTDPtr = td_phys; // TD#0 qh[1].qhAltNextTDPtr = 0x00000001; // invalid // now activate this temporarily 'empty' asynchronous schedule lp[ 0x20 >> 2 ] |= (1<<0); // make sure the EHCI is running while ( ( lp[ 0x24 >> 2 ] & (1<<12) ) != 0 ); // spin till done lp[ 0x20 >> 2 ] &= ~(1<<5); // disable acynchronous schedule while ( ( lp[ 0x24 >> 2 ] & (1<<15) ) != 0 ); // spin till done lp[ 0x38 >> 2 ] = qh_phys | (1<<1); // our queue-head lp[ 0x20 >> 2 ] |= (1<<5); // reenable acynchronous schedule while ( ( lp[ 0x24 >> 2 ] & (1<<15) ) == 0 ); // spin till done // setup the 'Get_Device_Descriptor' request-block rb[0].bmReqType = 0x80; rb[0].bRequest = 0x06; rb[0].wValue = 0x0100; rb[0].wIndex = 0x0000; rb[0].wLength = 0x0012; // loop to discover the device-address of the NET20DC peripheral unsigned int dev = 0; do { // overwrite any previous data in the destination buffer memset( &dd[0], 0xFF, sizeof( USB_DD ) ); // (re)initialize volatile fields in linked-list of TDs td[1].tdStatus = 0x80; // Active td[1].tdAction = 0x0E; // SETUP td[1].tdTotalBytes = 0x0008 | (0<<15); // DATA0 td[1].tdBufferPtr[0] = rb_phys; // Request-Block td[2].tdStatus = 0x80; // Active td[2].tdAction = 0x0D; // IN td[2].tdTotalBytes = 0x0012 | (1<<15); // DATA1 td[2].tdBufferPtr[0] = dd_phys; // Descriptor-Buffer td[3].tdStatus = 0x80; // Active td[3].tdAction = 0x0C; // OUT td[3].tdTotalBytes = 0x0000 | (1<<15); // DATA1 // (re)attach list of TDs to Queue Head #1 in the schedule qh[1].qhEndPtCharacteristics = 0x40406000 | dev; // H=0 qh[1].qhNextTDPtr = td_phys + 1 * sizeof( EHCI_TD64) ; qh[1].qhStatus = 0x00; // not 'Halted' // wait until this Queue Head #1 is no longer 'active' while ( td[3].tdStatus & 0x80 ) // still 'Active'? if ( qh[1].qhStatus & 0x40 ) break; // Halted? // break if the NET20DC device-descriptor was received if (( dd[0].bDescriptorType == 1 ) &&( dd[0].idVendor == VENDOR_ID ) &&( dd[0].idProduct == DEVICE_ID )) break; } while ( ++dev < 128 ); if ( dev == 128 ) { printf( "\n NET20DC device not detected\n\n" ); exit(1); } printf( "\n NET20DC device has USB device-address %u \n\n", dev ); }