//------------------------------------------------------------------- // tryadata.cpp // // This example loops through the sequence of valid USB device // numbers until it has obtained the Device Descriptor for the // ADATA N005 USB3 flash drive (recognized by its idVendor and // idProduct values), or until that search has been exhausted; // when found, its Configuration and Interface Descriptors are // displayed, along with information from the INQUIRY command. // // to compile: $ g++ tryadata.cpp -o tryadata // to execute: $ ./tryadata // // NOTE: Requires installing our 'ehci.c' device-driver module. // // programmer: ALLAN CRUSE // date begun: 08 JAN 2011 // completion: 13 JAN 2011 //------------------------------------------------------------------- #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 0x125F // USB Vendor Identification-number #define DEVICE_ID 0x105A // 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; } __attribute__((__packed__)) USB_DD; // USB Device Descriptor typedef struct { unsigned char bLength; unsigned char bDestriptorType; unsigned short wTotalLength; unsigned char bNumInterfaces; unsigned char ConfigurationValue; unsigned char iConfiguration; unsigned char bmAttributes; unsigned char bMaxPower; } __attribute__((__packed__)) USB_CD; // USB Configuration Descriptor typedef struct { unsigned char bLength; unsigned char bDescriptorType; unsigned char bInterfaceNumber; unsigned char bAlternateSetting; unsigned char bNumEndpoints; unsigned char bInterfaceClass; unsigned char bInterfaceSubClass; unsigned char bInterfaceProtocol; unsigned char iInterface; } __attribute__((__packed__)) USB_ID; // USB Interface Descriptor typedef struct { unsigned char bLength; unsigned char bDescriptorType; unsigned char bEndpointAddress; unsigned char bmAttributes; unsigned short wMaxPacketSize; unsigned char bInterval; } __attribute__((__packed__)) USB_ED; // USB Endpoint 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 typedef struct { unsigned int dCBWSignature; unsigned int dCBWTag; unsigned int dCBWDataTransferLength; unsigned char bCBWFlags; unsigned char bReserved; unsigned char bCBWLUN; unsigned char bCBWCD[ 16 ]; } __attribute__((__packed__)) SCSI_CBW; // Command Block Wrapper typedef struct { unsigned int dCSWSignature; unsigned int dCSWTag; unsigned int dSSWDataResidue; unsigned char bCSWStatus; } __attribute__((__packed__)) SCSI_CSW; // Command Status Wrapper typedef struct { unsigned char sector[ 512 ]; } DISK_BLOCK; void display_QH64( int index ); // used during debugging void display_TD64( int index ); // used during debugging 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); USB_CD *cd = (USB_CD *)(EHCI_BASE + PAGE_SIZE * 6); DISK_BLOCK *db = (DISK_BLOCK *)(EHCI_BASE + PAGE_SIZE * 7); SCSI_CBW *cb = (SCSI_CBW *)(EHCI_BASE + PAGE_SIZE * 8); SCSI_CSW *sb = (SCSI_CSW *)(EHCI_BASE + PAGE_SIZE * 9); unsigned int usb2cmd, usb2sts, usb2intr, asynclistaddr; unsigned int qh_phys, td_phys, rb_phys, dd_phys, cd_phys; unsigned int db_phys, cb_phys, sb_phys; const int sizeQH = sizeof( EHCI_QH64 ); const int sizeTD = sizeof( EHCI_TD64 ); const int sizeRB = sizeof( USB_RB ); const int sizeDD = sizeof( USB_DD ); const int sizeCD = sizeof( USB_CD ); const int sizeID = sizeof( USB_ID ); const int sizeED = sizeof( USB_ED ); const int sizeDB = sizeof( DISK_BLOCK ); const int sizeCB = sizeof( SCSI_CBW ); const int sizeSB = sizeof( SCSI_CSW ); 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; cd_phys = kmem_phys + PAGE_SIZE * 5; db_phys = kmem_phys + PAGE_SIZE * 6; cb_phys = kmem_phys + PAGE_SIZE * 7; sb_phys = kmem_phys + PAGE_SIZE * 8; // 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 ); // disable EHCI interrupts to prevent interference from // the Linux ehci-hcd device-driver's interrupt-handler lp[ 0x28 >> 2 ] = 0; // disable EHCI interrupts lp[ 0x24 >> 2 ] = ~0; // clear WC-bits in USB2STS //=========================================================== // setup an initial Queue Head for our Asynchronous Schedule // whose TD-list has a single 'inactive' transfer descriptor //=========================================================== // Transfer Descriptor #0 memset( &td[0], 0x00, sizeTD ); td[0].tdNextTDPtr = 1; // invalid td[0].tdAltNextTDPtr = 1; // invalid td[0].tdStatus = 0x40; // halted // Queue Head #0 (Head of the Reclamation List) memset( &qh[0], 0x00, sizeQH ); qh[0].qhHorizontalLink = qh_phys | (1<<1); // circular qh[0].qhEndPtCharacteristics |= (1<<15); // H=1 qh[0].qhEndPtCharacteristics |= (0<<14); // DT=0 qh[0].qhEndPtCharacteristics |= (0<<12); // EPS=0 qh[0].qhEndPtCapabilities = (0 << 30); // MULT=0 qh[0].qhNextTDPtr = 1;//td_phys; // to TD#0 qh[0].qhAltNextTDPtr = td_phys; // to TD#0 qh[0].qhStatus = 0x40; // halted // 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 printf( "\n Our Asynchronous Schedule has been enabled \n" ); //============================================================== // Now setup a second Queue Head with a linked-list of four TDs //============================================================== memset( &qh[1], 0x00, sizeQH ); qh[1].qhHorizontalLink = qh_phys | (1<<1); // to QH#0 qh[1].qhEndPtCharacteristics |= (0<<15); // H=0 qh[1].qhEndPtCharacteristics |= (1<<14); // DTC=1 qh[1].qhEndPtCharacteristics |= (2<<12); // Hi-Speed qh[1].qhEndPtCapabilities = (1 << 30); // MULT=1 qh[1].qhNextTDPtr = td_phys + 4 * sizeTD; // to TD#4 memset( &td[1], 0x00, sizeTD ); td[1].tdNextTDPtr = td_phys + 2 * sizeTD; // to TD#2 td[1].tdAltNextTDPtr = 1 ; // invalid td[1].tdStatus = 0x00; // inactive memset( &td[2], 0x00, sizeTD ); td[2].tdNextTDPtr = td_phys + 3 * sizeTD; // to TD#3 td[2].tdAltNextTDPtr = 1; // invalid td[2].tdStatus = 0x00; // inactive memset( &td[3], 0x00, sizeTD ); td[3].tdNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[3].tdAltNextTDPtr = 1; // invalid td[3].tdStatus = 0x00; // inactive // Then append Queue Head #1 to our Asynchronous Schedule qh[0].qhHorizontalLink = (qh_phys + 1 * sizeQH)|(1<<1); //================================================================ // Now try to discover the ADATA N0008 drive's device-address //================================================================ // setup the 'Get_Device_Descriptor' request-block rb[0].bmReqType = 0x80; // device-to-host rb[0].bRequest = 0x06; // Get_Descriptor rb[0].wValue = 0x0100; // for USB device rb[0].wIndex = 0x0000; // index is zero rb[0].wLength = 0x0012; // xfer 18-bytes // loop to discover the device-address for the ADATA peripheral unsigned int dev = 0; // initialize dev do { // make sure Queue Head #1 is 'halted' to avoid races qh[1].qhStatus = 0x40; // overwrite any previous data in the destination buffer memset( &dd[0], 0xFF, sizeDD ); // (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; // to RB#0 td[2].tdStatus = 0x80; // Active td[2].tdAction = 0x0D; // IN td[2].tdTotalBytes = 0x0012 | (1<<15); // DATA1 td[2].tdBufferPtr[0] = dd_phys; // to DD#0 td[3].tdStatus = 0x81; // Active, PING td[3].tdAction = 0x8C; // OUT, IOC=1 td[3].tdTotalBytes = 0x0000 | (1<<15); // DATA1 // modify the Queue Head's device-address qh[1].qhEndPtCharacteristics = 0x00406000 | dev; // (re)attach this linked-list of TDs to Queue Head #1 qh[1].qhNextTDPtr = td_phys + 1 * sizeTD; // to TD#1 qh[1].qhStatus = 0x00; // not 'Halted' // wait for transfer-completion or queue-halted lp[ 0x24 >> 2 ] = ~0; // reset WC-bits in USB2STS while ( (lp[ 0x24 >> 2 ] & 3) == 0 ); // USBINT or ERRINT? // break if the ADATA 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 ADATA Drive was not detected\n\n" ); exit(1); } printf( "\n ADATA N005 Drive using USB device-address %u\n", dev ); // display the Device Descriptor printf( "\n Device Descriptor: " ); unsigned short *wp = (unsigned short *)&dd[0]; for (int i = 0; i < 9; i++) printf( "%04X ", wp[i] ); printf( "\n" ); //================================================================== // Now get initial 9 bytes of the device's Configuration Descriptor //================================================================== // setup the 'Get_Configuration_Descriptor' request-block rb[1].bmReqType = 0x80; // device-to-host rb[1].bRequest = 0x06; // Get_Descriptor rb[1].wValue = 0x0200; // for configuration rb[1].wIndex = 0x0000; // index is zero rb[1].wLength = 0x0009; // xfer 9 bytes // clear the descriptor-buffer of any prior contents memset( &cd[0], 0xFF, 0x800 ); // make sure the Queue Head is 'halted' to avoid races qh[1].qhStatus = 0x40; // modify the Transfer Descriptors for this control-transfer td[1].tdNextTDPtr = td_phys + 2 * sizeTD; // to TD#2 td[1].tdAltNextTDPtr = 1; // invalid td[1].tdStatus = 0x80; // Active td[1].tdAction = 0x0E; // SETUP td[1].tdTotalBytes = 0x0008 | (0<<15); // DATA0 td[1].tdBufferPtr[0] = rb_phys + 1 * sizeRB; // to RB#1 td[2].tdNextTDPtr = td_phys + 3 * sizeTD; // to TD#3 td[2].tdAltNextTDPtr = td_phys + 3 * sizeTD; // to TD#3 td[2].tdStatus = 0x80; // Active td[2].tdAction = 0x0D; // IN td[2].tdTotalBytes = 0x0009 | (1<<15); // DATA1 td[2].tdBufferPtr[0] = cd_phys; // to cd#0 td[3].tdNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[3].tdAltNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[3].tdStatus = 0x81; // Active, PING td[3].tdAction = 0xFC; // OUT, IOC=1 td[3].tdTotalBytes = 0x0000 | (1<<15); // DATA1 // modify the Queue Head structure qh[1].qhNextTDPtr = td_phys + 1 * sizeTD; // to TD#1 qh[1].qhStatus = 0x00; // not 'halted' // wait for transfer-completion or queue-halted lp[ 0x24 >> 2 ] = ~0; // reset WC-bits in USB2STA while ( (lp[ 0x24 >> 2 ] & 3) == 0 ); // USBINT or ERRINT? printf( " Configuration Descriptor (Initial 8 bytes): " ); wp = (unsigned short *)&cd[0]; for (int i = 0; i < 0x04; i++) printf( "%04X ", wp[i] ); printf( "\n" ); unsigned short cd_len = cd[0].wTotalLength; printf( " Total Length of Configuration Descriptor: 0x%04X\n", cd_len ); unsigned int bNumConfigs = dd[0].bNumConfigurations; printf( " Number of possible configurations = %u \n", bNumConfigs ); //============================================================ // Then obtain the device's complete Configuration Descriptor //============================================================ // make sure Queue Head #1 is 'halted' to avoid races qh[1].qhStatus = 0x40; // modify 'wLength' in the Request Block rb[1].wLength = cd_len; // Total Length of descriptor // reinitialize volatile fields in the 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 + 1 * sizeRB; // to RB#1 td[2].tdStatus = 0x80; // Active td[2].tdAction = 0x0D; // IN td[2].tdTotalBytes = 0x1000 | (1<<15); // DATA1 td[2].tdBufferPtr[0] = cd_phys; // to CD#0 td[3].tdStatus = 0x81; // Active, POLL td[3].tdAction = 0x8C; // OUT, IOC=1 td[3].tdTotalBytes = 0x0000 | (1<<15); // DATA1 // reinitialize Queue Head #1 for this refreshed list of TDs qh[1].qhNextTDPtr = td_phys + 1 * sizeTD; // to TD#1 qh[1].qhStatus = 0x00; // not 'halted' // wait for transfer-completion or queue-halted lp[ 0x24 >> 2 ] = ~0; // reset WC-bits in USB2STA while ( (lp[ 0x24 >> 2 ] & 3) == 0 ); // USBINT or ERRINT? printf( "\n Configuration Descriptor " ); printf( "(Full %u bytes): ", cd_len ); unsigned char *cp = (unsigned char *)&cd[0]; for (int i = 0; i < cd_len; i++) { if ( (i % 16) == 0 ) printf( "\n 0x%04X: ", i ); printf( "%02X ", cp[i] ); } printf( "\n\n" ); printf( " Number of interfaces supported by this configuration" ); printf( " = %u \n\n", cd[0].bNumInterfaces ); cp = (unsigned char *)&cd[0]; cp += sizeCD; printf( " Interface Descriptor: " ); for (int i = 0; i < sizeID; i++) printf( "%02X ", cp[i] ); printf( "\n\n" ); USB_ID *id = (USB_ID *)cp; printf( " Number of Endpoints used by this interface" ); printf( " = %u \n\n", id->bNumEndpoints ); cp += sizeID; USB_ED *ed = (USB_ED *)cp; for (int i = 0; i < id->bNumEndpoints; i++) { unsigned int endpt = ed[i].bEndpointAddress & 0x7F; unsigned int maxpkt = ed[i].wMaxPacketSize & 0x3FF; unsigned int ptimes = ed[i].wMaxPacketSize >> 10; unsigned int attrib = ed[i].bmAttributes; printf( " Endpoint Descriptor #%u: ", endpt ); for (int j = 0; j < sizeED; j++) printf( "%02X ", cp[j] ); printf( "\n " ); switch ( attrib & 3 ) { case 0: printf( " Control " ); break; case 1: printf( " Isochronous " ); break; case 2: printf( " Bulk " ); break; case 3: printf( " Interrupt " ); break; } if ( ed[i].bEndpointAddress >> 7 ) printf( "IN " ); else printf( "OUT " ); printf( " MaxPacketSize=%ux%u-bytes ", ptimes, maxpkt ); printf( "\n" ); cp += sizeED; } printf( "\n\n" ); //================================================================== //===================================================================== // setup the 'Get_Device_Status' request-block rb[2].bmReqType = 0x80; // device-to-host rb[2].bRequest = 0x00; // Get_Status rb[2].wValue = 0x0000; // for device rb[2].wIndex = 0x0000; // index is zero rb[2].wLength = 0x0002; // xfer 2 bytes // make sure the Queue Head is 'halted' to avoid races qh[1].qhStatus = 0x40; // modify the Transfer Descriptors for this control-transfer td[1].tdNextTDPtr = td_phys + 2 * sizeTD; // to TD#2 td[1].tdAltNextTDPtr = 1; // invalid td[1].tdStatus = 0x80; // Active td[1].tdAction = 0x0E; // SETUP td[1].tdTotalBytes = 0x0008 | (0<<15); // DATA0 td[1].tdBufferPtr[0] = rb_phys + 2 * sizeRB; // to RB#2 td[2].tdNextTDPtr = td_phys + 3 * sizeTD; // to TD#3 td[2].tdAltNextTDPtr = td_phys + 3 * sizeTD; // to TD#3 td[2].tdStatus = 0x80; // Active td[2].tdAction = 0x0D; // IN td[2].tdTotalBytes = 0x0002 | (1<<15); // DATA1 td[2].tdBufferPtr[0] = dd_phys + 0x40; // to cd#0 td[3].tdNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[3].tdAltNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[3].tdStatus = 0x81; // Active, PING td[3].tdAction = 0xFC; // OUT, IOC=1 td[3].tdTotalBytes = 0x0000 | (1<<15); // DATA1 // modify the Queue Head structure qh[1].qhNextTDPtr = td_phys + 1 * sizeTD; // to TD#1 qh[1].qhStatus = 0x00; // not 'halted' // wait for transfer-completion or queue-halted lp[ 0x24 >> 2 ] = ~0; // reset WC-bits in USB2STA while ( (lp[ 0x24 >> 2 ] & 3) == 0 ); // USBINT or ERRINT? printf( " Device Status: " ); wp = (unsigned short *)&dd[0]; printf( "%04X ", wp[0x20] ); printf( " self-powered=%X ", wp[0x20] & 1 ); printf( " remote-wakeup=%X ", (wp[0x20]>>1)&1 ); printf( "\n\n" ); //============================================================ // setup the 'Get_Endpoint_Status' request-block rb[3].bmReqType = 0x82; // device-to-host rb[3].bRequest = 0x00; // Get_Status rb[3].wValue = 0x0000; // for endpoint rb[3].wIndex = 0x0000; // index is zero rb[3].wLength = 0x0002; // xfer 2 bytes for (int i = 0; i < id->bNumEndpoints; i++) { unsigned int endpt = ed[i].bEndpointAddress & 0x7F; // make sure the Queue Head is 'halted' to avoid races qh[1].qhStatus = 0x40; rb[3].wValue = endpt; // modify the Transfer Descriptors for this control-transfer td[1].tdNextTDPtr = td_phys + 2 * sizeTD; // to TD#2 td[1].tdAltNextTDPtr = 1; // invalid td[1].tdStatus = 0x80; // Active td[1].tdAction = 0x0E; // SETUP td[1].tdTotalBytes = 0x0008 | (0<<15); // DATA0 td[1].tdBufferPtr[0] = rb_phys + 3 * sizeRB; // to RB#3 td[2].tdNextTDPtr = td_phys + 3 * sizeTD; // to TD#3 td[2].tdAltNextTDPtr = td_phys + 3 * sizeTD; // to TD#3 td[2].tdStatus = 0x80; // Active td[2].tdAction = 0x0D; // IN td[2].tdTotalBytes = 0x0002 | (1<<15); // DATA1 td[2].tdBufferPtr[0] = dd_phys + 0x40; // to cd#0 td[3].tdNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[3].tdAltNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[3].tdStatus = 0x81; // Active, PING td[3].tdAction = 0x8C; // OUT, IOC=1 td[3].tdTotalBytes = 0x0000 | (1<<15); // DATA1 // modify the Queue Head structure qh[1].qhNextTDPtr = td_phys + 1 * sizeTD; // to TD#1 qh[1].qhStatus = 0x00; // not 'halted' // wait for transfer-completion or queue-halted lp[ 0x24 >> 2 ] = ~0; // reset WC-bits in USB2STA while ( (lp[ 0x24 >> 2 ] & 3) == 0 ); // USBINT or ERRINT? printf( " Endpoint Status for Endpoint #%u: ", endpt ); wp = (unsigned short *)&dd[0]; printf( "%04X ", wp[0x20] ); printf( " halted=%X ", wp[0x20] & 1 ); printf( "\n" ); } printf( "\n" ); //===================================================================== // setup the 'Clear_Feature( ENDPOINT_HALT )' request-block rb[5].bmReqType = 0x02; // host-to-device rb[5].bRequest = 0x01; // Clear_Feature rb[5].wValue = 0x0000; // value is zero rb[5].wIndex = 0x0001; // Endpoint 1 rb[5].wLength = 0x0000; // xfer 0 bytes // make sure the Queue Head is 'halted' to avoid races qh[1].qhStatus = 0x40; // modify the Transfer Descriptors for this control-transfer td[1].tdNextTDPtr = td_phys + 2 * sizeTD; // to TD#2 td[1].tdAltNextTDPtr = 1; // invalid td[1].tdStatus = 0x80; // Active td[1].tdAction = 0x0E; // SETUP td[1].tdTotalBytes = 0x0008 | (0<<15); // DATA0 td[1].tdBufferPtr[0] = rb_phys + 5 * sizeRB; // to RB#5 td[2].tdNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[2].tdAltNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[2].tdStatus = 0x80; // Active td[2].tdAction = 0x8D; // IN, IOC td[2].tdTotalBytes = 0x0000 | (1<<15); // DATA1 td[2].tdBufferPtr[0] = ~0; // unused // modify the Queue Head structure qh[1].qhNextTDPtr = td_phys + 1 * sizeTD; // to TD#1 qh[1].qhStatus = 0x00; // not 'halted' // wait for transfer-completion or queue-halted lp[ 0x24 >> 2 ] = ~0; // reset WC-bits in USB2STA while ( (lp[ 0x24 >> 2 ] & 3) == 0 ); // USBINT or ERRINT? printf( " CLEAR_FEATURE for Endpoint 1 \n" ); //===================================================================== // setup the 'Clear_Feature( ENDPOINT_HALT )' request-block rb[6].bmReqType = 0x02; // host-to-device rb[6].bRequest = 0x01; // Clear_Feature rb[6].wValue = 0x0000; // value is zero rb[6].wIndex = 0x0002; // Endpoint 2 rb[6].wLength = 0x0000; // xfer 0 bytes // make sure the Queue Head is 'halted' to avoid races qh[1].qhStatus = 0x40; // modify the Transfer Descriptors for this control-transfer td[1].tdNextTDPtr = td_phys + 2 * sizeTD; // to TD#2 td[1].tdAltNextTDPtr = 1; // invalid td[1].tdStatus = 0x80; // Active td[1].tdAction = 0x0E; // SETUP td[1].tdTotalBytes = 0x0008 | (0<<15); // DATA0 td[1].tdBufferPtr[0] = rb_phys + 6 * sizeRB; // to RB#6 td[2].tdNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[2].tdAltNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 td[2].tdStatus = 0x80; // Active td[2].tdAction = 0x8D; // IN, IOC td[2].tdTotalBytes = 0x0000 | (1<<15); // DATA1 td[2].tdBufferPtr[0] = ~0; // unused // modify the Queue Head structure qh[1].qhNextTDPtr = td_phys + 1 * sizeTD; // to TD#1 qh[1].qhStatus = 0x00; // not 'halted' // wait for transfer-completion or queue-halted lp[ 0x24 >> 2 ] = ~0; // reset WC-bits in USB2STA while ( (lp[ 0x24 >> 2 ] & 3) == 0 ); // USBINT or ERRINT? printf( " CLEAR_FEATURE for Endpoint 2 \n" ); //===================================================================== // setup Queue Heads for Endpoint #1 (IN) and Endpoint #2 (OUT) // circular-list: QH0 -> QH1 -> QH2 -> QH3 -> QH0 //===================================================================== memset( &qh[2], 0x00, sizeQH ); qh[2].qhHorizontalLink = (qh_phys + 3 * sizeQH) | (1<<1); // to QH#3 qh[2].qhEndPtCharacteristics = dev; // device-address qh[2].qhEndPtCharacteristics |= (1 << 8); // endpoint=1 (IN) qh[2].qhEndPtCharacteristics |= (512 << 16); // maxpacketlen=512 qh[2].qhEndPtCharacteristics |= (0<<15); // H=0 qh[2].qhEndPtCharacteristics |= (1<<14); // DTC=1 qh[2].qhEndPtCharacteristics |= (2<<12); // Hi-Speed qh[2].qhEndPtCapabilities = (1 << 30); // MULT=1 qh[2].qhNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 qh[2].qhAltNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 memset( &qh[3], 0x00, sizeQH ); qh[3].qhHorizontalLink = (qh_phys + 0 * sizeQH) | (1<<1); // to QH#0 qh[3].qhEndPtCharacteristics = dev; // device-address qh[3].qhEndPtCharacteristics |= (2 << 8); // endpoint=2 (OUT) qh[3].qhEndPtCharacteristics |= (512 << 16); // maxpacketlen=512 qh[3].qhEndPtCharacteristics |= (0<<15); // H=0 qh[3].qhEndPtCharacteristics |= (1<<14); // DTC=1 qh[3].qhEndPtCharacteristics |= (2<<12); // Hi-Speed qh[3].qhEndPtCapabilities = (1 << 30); // MULT=1 qh[3].qhNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 qh[3].qhAltNextTDPtr = td_phys + 0 * sizeTD; // to TD#0 // attach Queue Head #2 to the Asynchronous Schedule qh[1].qhHorizontalLink = (qh_phys + 2 * sizeQH) | (1<<1); //===================================================================== // Announce next command printf( "\n Issuing 'INQUIRY' command to USB device \n\n" ); // clear the destination buffer memset( &db[0], 0xFF, sizeDB ); // setup Command Status Wrapper structure memset( &sb[0], 0x00, sizeSB ); // setup Command Block Wrapper structure for 'INQUIRY' command memset( &cb[0], 0x00, sizeCB ); cb[0].dCBWSignature = 0x43425355; // "USBC" cb[0].dCBWTag = 0x00000001; // Tag = 1 cb[0].dCBWDataTransferLength = 36; // Length=36 cb[0].bCBWFlags = 0x80; // Flags= 0x80 cb[0].bCBWLUN = 12; // LUN=12 cb[0].bCBWCD[0] = 0x12; // INQUIRY = 12h cb[0].bCBWCD[4] = 0x24; // // setup a Transfer Descriptors for one OUT bulk-transfer memset( &td[8], 0x00, sizeTD ); td[8].tdNextTDPtr = 1; // invalid td[8].tdAltNextTDPtr = 1; // invalid td[8].tdStatus = 0x81; // Active, PING td[8].tdAction = 0x8C; // OUT, IOC td[8].tdTotalBytes = sizeCB | (0<<15); // DATA0 td[8].tdBufferPtr[0] = cb_phys; // to CB#0 memset( &td[9], 0x00, sizeTD ); td[9].tdNextTDPtr = td_phys + 10 * sizeTD; // to TD#10 td[9].tdAltNextTDPtr = td_phys + 10 * sizeTD; // to TD#10 td[9].tdStatus = 0x80; // Active td[9].tdAction = 0x0D; // IN td[9].tdTotalBytes = sizeDB | (0<<15); // DATA0 td[9].tdBufferPtr[0] = db_phys; // to DB#0 memset( &td[10], 0x00, sizeTD ); td[10].tdNextTDPtr = 1; // invalid td[10].tdAltNextTDPtr = 1; // invalid td[10].tdStatus = 0x80; // Active td[10].tdAction = 0x8D; // IN, IOC td[10].tdTotalBytes = sizeSB | (1<<15); // DATA1 td[10].tdBufferPtr[0] = sb_phys; // to SB#0 // modify the Queue Head #3 structure (OUT Endpoint) qh[3].qhNextTDPtr = td_phys + 8 * sizeTD; // to TD#8 qh[3].qhStatus = 0x00; // wait for transfer-completion or queue-halted lp[ 0x24 >> 2 ] = ~0; // reset WC-bits in USB2STA while ( (lp[ 0x24 >> 2 ] & 3) == 0 ); // USBINT or ERRINT? // report command progress unsigned char *p = (unsigned char *)&cb[0]; unsigned int pcount = td[8].tdBufferPtr[0] & 0xFFF; printf( " sent %u bytes: ", pcount ); for (int i = 0; i < pcount; i++) { if ( i == 15 ) printf( "\n " ); printf( " %02X", p[i] ); } printf( "\n\n" ); // modify the Queue Head #2 structure lp[ 0x24 >> 2 ] = ~0; // reset WC-bits in USB2STA qh[2].qhNextTDPtr = td_phys + 9 * sizeTD; // to TD#9 qh[2].qhStatus = 0x00; // wait for transfer-completion or queue-halted while ( (lp[ 0x24 >> 2 ] & 3) == 0 ); // USBINT or ERRINT? // report command progress p = (unsigned char *)&db[0]; pcount = td[9].tdBufferPtr[0] & 0xFFF; printf( " recv %u bytes: ", pcount ); for (int i = 0; i < pcount; i++) { if ( i == 15 ) printf( "\n " ); if ( i == 31 ) printf( "\n " ); printf( " %02X", p[i] ); } printf( "\n\n" ); // report command progress p = (unsigned char *)&sb[0]; pcount = td[10].tdBufferPtr[0] & 0xFFF; printf( " recv %u bytes: ", pcount ); for (int i = 0; i < pcount; i++) { if ( i == 15 ) printf( "\n " ); printf( " %02X", p[i] ); } printf( "\n\n" ); // Display some information from the INQUIRY response unsigned char *ir = (unsigned char *)&db[0]; printf( " %45s ", "Vendor identification: " ); for (int i = 8; i < 16; i++) printf( "%c", ir[i] ); printf( "\n" ); printf( " %46s ", "Product identification: " ); for (int i = 16; i < 32; i++) printf( "%c", ir[i] ); printf( "\n" ); printf( " %46s ", "Product revision-level: " ); for (int i = 32; i < 36; i++) printf( "%c", ir[i] ); printf( "\n\n" ); //===================================================================== } void display_QH64( int index ) { unsigned int *ep = (unsigned int *)&qh[ index ]; unsigned long addr = qh_phys + index * sizeof( EHCI_QH64 ); printf( " QH%X ", index ); printf( "<0x%08lX>: ", addr ); for (int i = 0; i < 9; i++) { if ( i == 4 ) printf( "\n " ); printf( "%08X ", ep[i] ); } printf( "\n" ); } void display_TD64( int index ) { unsigned int *ep = (unsigned int *)&td[ index ]; unsigned long addr = td_phys + index * sizeof( EHCI_TD64 ); printf( " TD%X ", index ); printf( "<0x%08lX>: ", addr ); for (int i = 0; i < 5; i++) printf( "%08X ", ep[i] ); printf( "\n" ); }