//------------------------------------------------------------------- // ajayfsrx.cpp // // Here we try to receive data via the 'NET20DC' Debug Cable by // using the UHCI controller's 'Full-Speed' operation. Will it // work? Initially we were skeptical, but in fact it did work. // // to compile: $ g++ ajayfsrx.cpp -o ajayfsrx // to execute: $ ./ajayfsrx // // NOTE: Requires installing our 'uhci.c' device-driver module, // and may require removing the Linux 'ehci_hcd' device-driver. // // programmer: ALLAN CRUSE // date begun: 23 APR 2010 // completion: 28 APR 2010 // revised on: 11 MAY 2010 -- to reduce the memory 'footprint' //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for lseek() #include // for signal() #include // for memset() #include // for mmap() #include // for ioctl() #include // for inw(), outw() #define KMEM_BASE 0x70000 // suitable address for memory-map #define PAGE_SIZE 4096 // memory-granularity on x86 system #define BUFFER_SZ 40960 // maximum length for file-transfer #define QH (1<<1) // descriptor-flag for 'queue-head' #define VF (1<<2) // descriptor-flag for 'depth-first' #define VENDOR_ID 0x0525 // USB Vendor identification-number #define DEVICE_ID 0x127A // USB Device identification-number 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 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 int tdNextTD; unsigned int tdStatus; unsigned int tdAction; unsigned int tdBuffer; } UHCI_TD; // UHCI Transfer Descriptor typedef struct { unsigned int qhHorzLink; unsigned int qhVertLink; unsigned int qhPadding0; unsigned int qhPadding1; } UHCI_QH; // UHCI Queue Head char devname[] = "/dev/uhci"; const int sizeRB = sizeof( USB_RB ); const int sizeDD = sizeof( USB_DD ); const int sizeTD = sizeof( UHCI_TD ); const int sizeQH = sizeof( UHCI_QH ); unsigned long hub_count, port_addr, kmem_phys; unsigned int *framelist = (unsigned int *)(KMEM_BASE); UHCI_QH *qh = (UHCI_QH *)(KMEM_BASE + PAGE_SIZE * 1); UHCI_TD *td = (UHCI_TD *)(KMEM_BASE + PAGE_SIZE * 2); USB_RB *rb = (USB_RB *)(KMEM_BASE + PAGE_SIZE * 3); USB_DD *dd = (USB_DD *)(KMEM_BASE + PAGE_SIZE * 4); unsigned char *bf = (unsigned char *)(KMEM_BASE + PAGE_SIZE * 5); unsigned int fr_phys, qh_phys, td_phys, rb_phys, dd_phys, bf_phys; unsigned short usbcmd, usbsts, usbintr, frnum, portsc0, portsc1; unsigned int io, frbaseaddr, dev, hub; void display_UHCI_registers( void ) { printf( "\n" ); printf( " USBCMD=%04X ", inw( io + 0 ) ); printf( " USBSTS=%04X ", inw( io + 2 ) ); printf( " USBINTR=%04X ", inw( io + 4 ) ); printf( " FRNUM=%04X ", inw( io + 6 ) ); printf( "FRBASEADDR=%08X ", inl( io + 8 ) ); printf( "\n" ); printf( " PORTSC0=%04X ", inw( io + 16 ) ); printf( " PORTSC1=%04X ", inw( io + 18 ) ); printf( "\n" ); } void my_sigint_handler( int signo ) { // resume former schedule on this controller outw( inw( io + 0 ) & ~(1<<0), io + 0 ); // USBCMD R/S=0 while ( (inw( io + 2 ) & (1<<5)) == 0 ); // USBSTS HCHalted? outw( frnum, io + 6 ); // FRNUM prior outl( frbaseaddr, io + 8 ); // FRBASEADDR prior outw( usbcmd, io + 0 ); // USBCMD R/S=1 outw( usbintr, io + 4 ); // USBINTR prior outw( inw( io + 16 ), io + 16 ); // clear WC bits outw( inw( io + 18 ), io + 18 ); // clear WC bits outw( inw( io + 2 ), io + 2 ); // clear WC bits display_UHCI_registers(); 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); } int size = lseek( fd, 0, SEEK_END ); printf( "\n Opened the device-file \'%s\' ", devname ); printf( "(size=0x%X bytes) \n", size ); // initialize our physical region addresses if ( ioctl( fd, 0, &kmem_phys ) < 0 ) { perror( "ioctl" ); exit(1); } fr_phys = kmem_phys + PAGE_SIZE * 0; qh_phys = kmem_phys + PAGE_SIZE * 1; td_phys = kmem_phys + PAGE_SIZE * 2; rb_phys = kmem_phys + PAGE_SIZE * 3; dd_phys = kmem_phys + PAGE_SIZE * 4; bf_phys = kmem_phys + PAGE_SIZE * 5; printf( " Kernel memory region begins at " ); printf( "physical address 0x%08X \n", kmem_phys ); // map the kernel memory into userspace int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; void *mm = (void *)KMEM_BASE; if ( mmap( mm, size, prot, flag, fd, 0 ) == MAP_FAILED ) { perror( "mmap" ); exit(1); } // get the number of UHCI controllers we potentially must check if ( ioctl( fd, 1, &hub_count ) < 0 ) { perror( "ioctl" ); exit(1); } printf( " Number of UHCI controllers found = %lu \n", hub_count ); // build 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; // build the 'Set_Device_Configuration' Request Block rb[1].bmReqType = 0x00; rb[1].bRequest = 0x09; rb[1].wValue = 0x0001; rb[1].wIndex = 0x0000; rb[1].wLength = 0x0000; // build the 'Clear_Feature' Request Block rb[2].bmReqType = 0x02; rb[2].bRequest = 0x01; rb[2].wValue = 0x0000; rb[2].wIndex = 0x0082; rb[2].wLength = 0x0000; // build the initially empty 'Queue-Head' descriptor qh[0].qhHorzLink = 0x00000001; qh[0].qhVertLink = 0x00000001; qh[0].qhPadding0 = 0xFFFFFFFF; qh[0].qhPadding1 = 0xFFFFFFFF; // build the framelist array of Queue-Head pointers for (int i = 0; i < 1024; i++) framelist[i] = ( qh_phys + 0 * sizeQH ) | QH; // search for the current USB device-address of the NET20DC device for (hub = 1; hub <= hub_count; hub++) { // get this controller's i/o-port base-address port_addr = hub; printf( "\n UHCI #%u: ", hub ); if ( ioctl( fd, 1, &port_addr ) < 0 ) { perror( "ioctl" ); exit(1); } io = port_addr; printf( "I/O-Port Base Address = 0x%04lX ", io ); display_UHCI_registers(); // Check for Host Controller halted usbsts = inw( io + 2 ); if ( usbsts & (1<<5) ) // HCHalted? { printf( " Host Controller is halted \n" ); continue; } // check for any attached 'Full-Speed' devices int fs_count = 0; portsc0 = inw( io + 16 ); portsc1 = inw( io + 18 ); if ( (portsc0 & 0x101) == 0x0001 ) ++fs_count; if ( (portsc1 & 0x101) == 0x0001 ) ++fs_count; if ( fs_count > 0 ) { printf( " Checking \'Full-Speed\' device" ); printf( " found on UHCI #%u ... ", hub ); if (( portsc0 & 0x1000 )||( portsc1 & 0x1000 )) { printf( "device is suspended \n" ); continue; } else printf( "\n" ); } else { printf( " No \'Full-Speed\' device is detected" ); printf( " on UHCI #%u \n", hub ); continue; // no need to search any further } // overwrite any prior data in our destination buffers memset( dd, 0xFF, 128 * sizeof( USB_DD ) ); // save some UHCI registers to be restored later usbcmd = inw( io + 0 ); usbsts = inw( io + 2 ); usbintr = inw( io + 4 ); frnum = inw( io + 6 ); frbaseaddr = inl( io + 8 ); portsc0 = inw( io + 16 ); portsc1 = inw( io + 18 ); signal( SIGINT, my_sigint_handler ); // activate our schedule on this controller outw( inw( io + 0 ) & ~(1<<0), io + 0 ); // USBCMD R/S=0 while ( (inw( io + 2 ) & (1<<5)) == 0 ); // USBSTS HCHalted? frnum = inw( io + 6 ); // FRNUM saved frbaseaddr = inl( io + 8 ); // FRBASEADDR saved outw( 0x0000, io + 6 ); // FRNUM setup outl( fr_phys, io + 8 ); // FRBASEADDR setup outw( inw( io + 0 ) | (1<<0), io + 0 ); // USBCMD R/S=1 while ( (inw( io + 2) & (1 << 5)) != 0 ); // USBSTS HCHalted? // iterate over the potential device-addresses for (dev = 0; dev < 128; dev++) { // The SETUP stage (for 'Get_Descriptor') td[0].tdNextTD = (td_phys + 1 * sizeTD) | VF; td[0].tdStatus = 0x18800000; // Full-Speed, Active td[0].tdAction = 0x00E0002D; // SETUP, MaxLen=8 td[0].tdAction |= (dev << 8); // device-address td[0].tdAction |= (0 << 19); // data-toggle=DATA0 td[0].tdBuffer = rb_phys + 0 * sizeRB;// Request Block // The DATA stage (for 'Get_Descriptor') td[1].tdNextTD = (td_phys + 2 *sizeTD) | VF; td[1].tdStatus = 0x18800000; // Full-Speed, Active td[1].tdAction = 0x02200069; // IN, MaxLen=18 td[1].tdAction |= (dev << 8); // device-address td[1].tdAction |= (1 << 19); // data-toggle=DATA1 td[1].tdBuffer = dd_phys; // Descriptor buffer // The HANDSHAKE stage (for 'Get_Descriptor') td[2].tdNextTD = (td_phys + 3 *sizeTD) | VF; td[2].tdStatus = 0x18800000; // Full-Speed, Active td[2].tdAction = 0xFFE800E1; // OUT, MaxLen=0 td[2].tdAction |= (dev << 8); // device-address td[2].tdAction |= (1 << 19); // data-toggle=DATA1 td[2].tdBuffer = 0x00000000; // null address // The SETUP stage (for 'Set_Configuration') td[3].tdNextTD = (td_phys + 4 * sizeTD) | VF; td[3].tdStatus = 0x18800000; // Full-Speed, Active td[3].tdAction = 0x00E0002D; // SETUP, MaxLen=8 td[3].tdAction |= (dev << 8); // device-address td[3].tdAction |= (0 << 19); // data-toggle=DATA0 td[3].tdBuffer = rb_phys + 1 * sizeRB;// Request Block // The HANDSHAKE stage (for 'Set_Configuration') td[4].tdNextTD = (td_phys + 5 *sizeTD) | VF; td[4].tdStatus = 0x18800000; // Full-Speed, Active td[4].tdAction = 0xFFE00069; // IN, MaxLen=0 td[4].tdAction |= (dev << 8); // device-address td[4].tdAction |= (1 << 19); // data-toggle=DATA1 td[4].tdBuffer = 0x00000000; // null address // The SETUP stage (for 'Clear_Feature') td[5].tdNextTD = (td_phys + 6 * sizeTD) | VF; td[5].tdStatus = 0x18800000; // Full-Speed, Active td[5].tdAction = 0x00E0002D; // SETUP, MaxLen=8 td[5].tdAction |= (dev << 8); // device-address td[5].tdAction |= (0 << 19); // data-toggle=DATA0 td[5].tdBuffer = rb_phys + 2 * sizeRB;// Request Block // The HANDSHAKE stage (for 'Clear_Feature') td[6].tdNextTD = 0x00000001; // invalid pointer td[6].tdStatus = 0x18800000; // Full-Speed, Active td[6].tdAction = 0xFFE00069; // IN, MaxLen=0 td[6].tdAction |= (dev << 8); // device-address td[6].tdAction |= (1 << 19); // data-toggle=DATA1 td[6].tdBuffer = 0x00000000; // null address // attach this linked-list of TDs to our Queue-Head qh[0].qhVertLink = td_phys; // pointer to TD#1 // wait for Queue to become inactive or to stall while ( qh[0].qhVertLink != 0x00000001 ) if ( td[0].tdStatus & (1 << 22) ) break; // continue searching unless queue was traversed if ( qh[0].qhVertLink != 0x00000001 ) continue; // check for the 'NET20DC' device-descriptor if (( dd[0].bDescriptorType == 1 ) &&( dd[0].idVendor == VENDOR_ID ) &&( dd[0].idProduct == DEVICE_ID )) break; } // resume former schedule on this controller outw( inw( io + 0 ) & ~(1<<0), io + 0 ); // USBCMD R/S=0 while ( (inw( io + 2 ) & (1<<5)) == 0 ); // USBSTS HCHalted? outw( frnum, io + 6 ); // FRNUM prior outl( frbaseaddr, io + 8 ); // FRBASEADDR prior outw( usbcmd, io + 0 ); // USBCMD R/S=1 outw( usbintr, io + 4 ); // USBINTR prior outw( inw( io + 16 ), io + 16 ); // clear WC bits outw( inw( io + 18 ), io + 18 ); // clear WC bits outw( inw( io + 2 ), io + 2 ); // clear WC bits if ( dev < 128 ) break; } if (( dev == 128 )||( hub > hub_count )) { printf( "\n \'NET20DC\' peripheral could not be found\n\n" ); exit(1); } // report the peripheral's device-address and its attached controller printf( "\n \'NET20DC\' peripheral found: " ); printf( "UHCI hub-number = %d, ", hub ); printf( "USB device-address = %d \n", dev ); // Below we display the relevant QD and TDs printf( "\n" ); for (int i = 0; i <= 6; i++) { unsigned int *elem = (unsigned int *)&td[i]; printf( " td%X:", i ); for (int j = 0; j < 4; j++) printf( " %08X", elem[j] ); printf( "\n" ); } printf( "\n" ); for (int i = 0; i < 1; i++) { unsigned int *elem = (unsigned int *)&qh[i]; printf( " qh%X:", i ); for (int j = 0; j < 2; j++) printf( " %08X", elem[j] ); printf( "\n" ); } printf( "\n" ); //----------------------------------------------------- // Now we implement IN bulk-transfers, upon EndPoint 2 //----------------------------------------------------- // wipe any prior data from our buffer-area for (int i = 0; i < BUFFER_SZ; i++) bf[i] = (i == 63) ? '\n' : '-'; printf( "\n Waiting to receive data from our link-partner... \n" ); // reinitialize Queue-Head (to avoid a potential race-condition) qh[0].qhHorzLink = qh[0].qhVertLink = 0x00000001; // activate our schedule on this controller outw( inw( io + 0 ) & ~(1<<0), io + 0 ); // USBCMD R/S=0 while ( (inw( io + 2 ) & (1<<5)) == 0 ); // USBSTS HCHalted? frnum = inw( io + 6 ); // FRNUM saved frbaseaddr = inl( io + 8 ); // FRBASEADDR saved outw( 0x0000, io + 6 ); // FRNUM setup outl( fr_phys, io + 8 ); // FRBASEADDR setup outw( inw( io + 0 ) | (1<<0), io + 0 ); // USBCMD R/S=1 while ( (inw( io + 2) & (1 << 5)) != 0 ); // USBSTS HCHalted? int endpt = 2; // endpoint-address int rxbytes = 0; // number of bytes received int sizeBF = 64; // maximum packet-size int dt = 0; // initial data-toggle while ( rxbytes + sizeBF <= BUFFER_SZ ) { // reinitialize our list of Transfer-Descriptors int len = sizeBF; td[0].tdNextTD = 0x00000001; // invalid pointer td[0].tdStatus = 0x18800000; // Full-Speed, Active td[0].tdAction = 0x00000069; // IN td[0].tdAction |= (dev << 8); // device-address td[0].tdAction |= (endpt<<15); // endpoint-address td[0].tdAction |= (len-1)<<21; // MaxLen=64 td[0].tdAction |= (dt << 19); // Data-Toggle td[0].tdBuffer = bf_phys + rxbytes; // attach the linked-list of TDs to our Queue-Head qh[0].qhVertLink = td_phys + 0 * sizeTD; // wait until list is traversed, or until HC stalls while ( qh[0].qhVertLink != 0x00000001 ) if ( td[0].tdStatus & (1 << 22) ) break; // add the number of new bytes received to prior total len = (td[0].tdStatus + 1) & 0x7FF; rxbytes += len; // quit when packet has fewer bytes than the maximum if ( len < sizeBF ) break; // otherwise prepare to receive another data-packet dt = (1 + dt) % 2; // adjust data-toggle qh[0].qhVertLink = 0x00000001; // detach list of TDs } // resume former schedule on this controller outw( inw( io + 0 ) & ~(1<<0), io + 0 ); // USBCMD R/S=0 while ( (inw( io + 2 ) & (1<<5)) == 0 ); // USBSTS HCHalted? outw( frnum, io + 6 ); // FRNUM prior outl( frbaseaddr, io + 8 ); // FRBASEADDR prior outw( usbcmd, io + 0 ); // USBCMD prior outw( usbintr, io + 4 ); // USBINTR prior outw( inw( io + 16 ), io + 16 ); // clear WC bits outw( inw( io + 18 ), io + 18 ); // clear WC bits outw( inw( io + 2 ), io + 2 ); // clear WC bits // display the data received from our link-partner printf( "\n" ); for (int i = 0; i < rxbytes; i++) printf( "%c", bf[i] ); printf( "\n\n" ); printf( " Received %d bytes from our link-partner \n\n", rxbytes ); printf( "\n" ); }