//------------------------------------------------------------------- // headmike.cpp // // This experimental application employs direct hardware-level // programming of a USB Open Host Controller Interface to find // and operate our Logitech H360 Headset's microphone in order // to record a 10-second sound interval and save it to a file. // The Linux 'aplay' application can be used for its playback. // // compile with: $ g++ headmike.cpp -o headmike // execute with: $ ./headmike // // You can then use the Linux 'aplay' application for playback // of the recorded sound in the 'headmike.wav' file: // // $ aplay headmike.wav // // NOTE: This program requires preloading our 'ohci.c' module. // // programmer: ALLAN CRUSE // date begun: 01 NOV 2011 // completion: 11 NOV 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 VENDOR_ID 0x046D // Logitech, Incorporated #define DEVICE_ID 0x0A02 // USB Stereo Headset H360 #define OHCI_BASE 0x70000 // suitable address for register-map #define PAGE_SIZE 0x01000 // memory-granularity on x86 systems #define KMEM_BASE 0x71000 // address for a kernel memory-arena typedef struct { unsigned int edControl; unsigned int edTailPtr; unsigned int edHeadPtr; unsigned int edNextED; } OHCI_ED; // OHCI Endpoint Descriptor typedef struct { unsigned int tdStatus; unsigned int tdBuffer; unsigned int tdNextTD; unsigned int tdBufEnd; unsigned short tdOffset[8]; } OHCI_TD; // OHCI Transfer 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 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 iMabufacturer; unsigned char iProduct; unsigned char iSerialNumber; unsigned char bNumConfigurations; } USB_DD; // USB Device Descriptor typedef struct { short left, right; } PCM; // stereo S16 format char devname[] = "/dev/ohci-"; const int sizeED = sizeof( OHCI_ED ); const int sizeTD = sizeof( OHCI_TD ); const int sizeRB = sizeof( USB_RB ); const int sizeDD = sizeof( USB_DD ); unsigned long kmem_phys, kmem_size; unsigned int *lp = (unsigned int *)OHCI_BASE; unsigned int *hcca = (unsigned int *)(KMEM_BASE + 0 * PAGE_SIZE); OHCI_ED *ed = (OHCI_ED *)(KMEM_BASE + 1 * PAGE_SIZE); OHCI_TD *td = (OHCI_TD *)(KMEM_BASE + 2 * PAGE_SIZE); USB_RB *rb = (USB_RB *)(KMEM_BASE + 3 * PAGE_SIZE); USB_DD *dd = (USB_DD *)(KMEM_BASE + 4 * PAGE_SIZE); PCM *bf = (PCM *)(KMEM_BASE + 5 * PAGE_SIZE); unsigned int hcca_phys, ed_phys, td_phys, rb_phys, dd_phys, bf_phys; unsigned int HcControl, HcInterruptEnable; unsigned int HcHCCA, HcControlHeadED, HcControlCurrentED; unsigned int hub, dev = 128; void my_sigint_handler( int signo ) { exit(1); } void restore_OHCI_registers( void ) { lp[ 0x0C >> 2 ] = 0x0000007F; // clear WC bits lp[ 0x10 >> 2 ] = HcInterruptEnable; // restore lp[ 0x04 >> 2 ] &= ~(0xF << 2); // list processing "off" lp[ 0x24 >> 2 ] = 0x00000000; // clear HcControlCurrentED lp[ 0x18 >> 2 ] = HcHCCA; // restore lp[ 0x20 >> 2 ] = HcControlHeadED; // restore lp[ 0x04 >> 2 ] = HcControl; // restore } int main( int argc, char **argv ) { // make sure prior OHCI register-values will get restored // when this program terminates, including the case where // the program 'hangs' and is terminated with -C atexit( restore_OHCI_registers ); signal( SIGINT, my_sigint_handler ); // to discover our peripheral's USB device-address, we // will iterate over our system's two OHCI controllers for (hub = 0; hub < 2; hub++) { // setup the controller's device-filename sprintf( devname + 9, "%c", hub + '1' ); printf( "\n Opening \'%s\' \n", devname ); // open this device-file for reading and writing int fd = open( devname, O_RDWR ); if ( fd < 0 ) { perror( devname ); exit(1); } // map the controller's registers and kernel buffer int size = lseek( fd, 0, SEEK_END ); int prot = PROT_READ | PROT_WRITE; int flag = MAP_FIXED | MAP_SHARED; void *mm = (void *)OHCI_BASE; if ( mmap( mm, size, prot, flag, fd, 0 ) == MAP_FAILED ) { perror( "mmap" ); exit(1); } // get the physical address of the kernel memory buffer if ( ioctl( fd, 0, &kmem_phys ) < 0 ) { perror( "ioctl" ); exit(1); } kmem_size = size; printf( " physical address-range of kernel memory:" ); printf( " 0x%08lX-0x%08lX \n", kmem_phys, kmem_phys + size ); // show the original OHCI register-values printf( "\n OHCI controller #%u registers: ", hub+1 ); int n_regs = 21 + (lp[ 0x48 >> 2 ] & 0xFF); for (int i = 0; i < n_regs; i++) { if ( (i % 8) == 0 ) printf( "\n 0x%02X: ", i*4 ); printf( "%08X ", lp[ i ] ); } printf( "\n" ); // save some initial register-values to be restored later HcControl = lp[ 0x04 >> 2 ]; HcInterruptEnable = lp[ 0x10 >> 2 ]; HcHCCA = lp[ 0x18 >> 2 ]; HcControlHeadED = lp[ 0x20 >> 2 ]; HcControlCurrentED = lp[ 0x24 >> 2 ]; // check that controller's functional state is 'OPERATIONAL' unsigned int HCFS = (HcControl >> 6) & 3; printf( " HcControl=%08X HCFS=%u ", HcControl, HCFS ); printf( " HC Functional State is " ); switch ( HCFS ) { case 0: printf( "\'RESET\' \n" ); break; case 1: printf( "\'RESUME\' \n" ); break; case 2: printf( "\'OPERATIONAL\' \n" ); break; case 3: printf( "\'SUSPEND\' \n" ); break; } if ( HCFS != 2 ) continue; // setup the physical addresses of our data arenas hcca_phys = kmem_phys + 0 * PAGE_SIZE; ed_phys = kmem_phys + 1 * PAGE_SIZE; td_phys = kmem_phys + 2 * PAGE_SIZE; rb_phys = kmem_phys + 3 * PAGE_SIZE; dd_phys = kmem_phys + 4 * PAGE_SIZE; bf_phys = kmem_phys + 5 * PAGE_SIZE; // setup an uninitialized Transfer Descriptor memset( &td[0], 0x00, sizeTD ); // setup an inactive Endpoint Descriptor ed[0].edControl = 0x00080000; // MPS=8, Full-Speed ed[0].edTailPtr = td_phys; ed[0].edHeadPtr = td_phys; ed[0].edNextED = ed_phys; // disable OHCI controller interrupts lp[ 0x14 >> 2 ] = 0x8000007F; // HcInterruptDisable lp[ 0x0C >> 2 ] = 0x8000007F; // HcInterruptStatus // activate our Control List lp[ 0x04 >> 2 ] &= ~(1 << 4); // HcControl.CLE "off" lp[ 0x20 >> 2 ] = ed_phys; // modify HcControlHeadED lp[ 0x24 >> 2 ] = 0x00000000; // clear HcControlCurrentED lp[ 0x04 >> 2 ] |= (1 << 4); // HcControl.CLE "on" lp[ 0x08 >> 2 ] |= (1 << 1); // set HcCommandStatus.CLF // clear prior contents of descriptor buffer memset( &dd[0], 0x00, sizeDD ); // build Request-Block for 'Get_Descriptor' rb[0].bmReqType = 0x80; // device-to-host rb[0].bRequest = 0x06; // Get_Descriptor rb[0].wValue = 0x0100; // device-descriptor rb[0].wIndex = 0x0000; // zero index rb[0].wLength = 0x0012; // length=18 // erase any prior data from the descriptor buffer memset( &dd[0], 0x00, sizeDD ); // loop to discover the peripheral's USB device-address for (dev = 0; dev < 128; dev++) { // set our Endpoint Descriptor's 'sKip' bit ed[0].edControl = 0x00084000 | dev; // MPS=8, FS, K ed[0].edTailPtr = td_phys + 5 * sizeTD; ed[0].edHeadPtr = td_phys + 0 * sizeTD; ed[0].edNextED = ed_phys; // setup linked-list of TDs for a Control-Transfer td[0].tdStatus = 0xF2E00000; // SETUP, DATA0 td[0].tdBuffer = rb_phys + 0; // Request-Block #0 td[0].tdNextTD = td_phys + 1 * sizeTD; td[0].tdBufEnd = rb_phys + 7; td[1].tdStatus = 0xF3F00000; // IN, DATA1 td[1].tdBuffer = dd_phys + 0; td[1].tdNextTD = td_phys + 2 * sizeTD; td[1].tdBufEnd = dd_phys + 7; td[2].tdStatus = 0xF2F00000; // IN, DATA0 td[2].tdBuffer = dd_phys + 8; td[2].tdNextTD = td_phys + 3 * sizeTD; td[2].tdBufEnd = dd_phys + 15; td[3].tdStatus = 0xF3F00000; // IN, DATA1 td[3].tdBuffer = dd_phys + 16; td[3].tdNextTD = td_phys + 4 * sizeTD; td[3].tdBufEnd = dd_phys + 17; td[4].tdStatus = 0xF3E80000; // OUT, DATA1 td[4].tdBuffer = 0x00000000; td[4].tdNextTD = td_phys + 5 * sizeTD; td[4].tdBufEnd = 0x00000000; td[5].tdStatus = 0x00000000; td[5].tdBuffer = 0x00000000; td[5].tdNextTD = 0x00000000; td[5].tdBufEnd = 0x00000000; // clear the ED's 'sKip' bit ed[0].edControl &= ~(1 << 14); // wait until the linked-list is processed while ( td[4].tdStatus >> 28 ) // completion-Code if ( ed[0].edHeadPtr & 1 ) break; //Halted? // check for 'Endpoint Halted' condition if ( ed[0].edHeadPtr & 1 ) continue; // check for peripheral's device-descriptor returned if (( dd[0].bDescriptorType == 1 ) &&( dd[0].idVendor == VENDOR_ID ) &&( dd[0].idProduct == DEVICE_ID )) break; } // stop searching if the peripheral was detected if ( dev < 128 ) break; else restore_OHCI_registers(); } // report results of our search for the attached peripheral-device if ( dev < 128 ) { printf( "\n Logitech Headset H360 " ); printf( "attached to OHCI controller #%u ", hub+1 ); printf( "with USB device-address %u \n\n", dev ); } else { printf( "\n The Logitech Stereo Headset H360 " ); printf( "peripheral-device was not detected \n\n" ); exit(1); } //------------------------------------------------ // Execute the SET_CONFIGURATION control-transfer //------------------------------------------------ // build the Request-Block for 'Set_Configuration' rb[1].bmReqType = 0x00; // host-to-device rb[1].bRequest = 0x09; // Set_Configuration rb[1].wValue = 0x0001; // configuration=1 rb[1].wIndex = 0x0000; // zero index rb[1].wLength = 0x0000; // zero length // setup revised Endpoint Descriptor with 'sKip' bit set ed[0].edControl = 0x00084000 | dev; // MPS=8, FS, K ed[0].edTailPtr = td_phys + 7 * sizeTD; ed[0].edHeadPtr = td_phys + 5 * sizeTD; ed[0].edNextED = ed_phys; // setup a fresh linked-list of Transfer Descriptors td[5].tdStatus = 0xF0000000 | (7 << 21); // SETUP, DATA0, DI=7 td[5].tdBuffer = rb_phys + 1 * sizeRB; // Request-Block #1 td[5].tdNextTD = td_phys + 6 * sizeTD; td[5].tdBufEnd = rb_phys + 1 * sizeRB + 7; td[6].tdStatus = 0xF0100000 | (0 << 21); // IN, DATA1, DI=0 td[6].tdBuffer = 0x00000000; td[6].tdNextTD = td_phys + 7 * sizeTD; td[6].tdBufEnd = 0x00000000; td[7].tdStatus = 0xF0000000; td[7].tdBuffer = 0x00000000; td[7].tdNextTD = 0x00000000; td[7].tdBufEnd = 0x00000000; lp[ 0x0C >> 2 ] = 0x0000007F; // reset WC bits // clear the Endpoint Descriptor's 'sKip' bit ed[0].edControl &= ~(1 << 14); // wait for the 'WritebackDoneHead' interrupt while ( (lp[ 0x0C >> 2 ] & (1 << 1)) == 0 ); // report outcome of the control-transfer if ( td[6].tdStatus >> 28 ) { printf( " SET_CONFIGURATION failed \n" ); exit(1); } printf( " Executed SET_CONFIGURATION control-transfer:" ); printf( " configuration=%d \n\n", rb[1].wValue ); //-------------------------------------------- // Execute the SET_INTERFACE control-transfer //-------------------------------------------- // build the Request-Block for 'Set_Interface' rb[2].bmReqType = 0x01; // host-to-device rb[2].bRequest = 0x0B; // Set_Interface rb[2].wValue = 0x0001; // interface=1 rb[2].wIndex = 0x0002; // index=2 rb[2].wLength = 0x0000; // zero length // setup revised Endpoint Descriptor with 'sKip' bit set ed[0].edControl = 0x00084000 | dev; // MPS=8, FS, K ed[0].edTailPtr = td_phys + 9 * sizeTD; ed[0].edHeadPtr = td_phys + 7 * sizeTD; ed[0].edNextED = ed_phys; // setup a fresh linked-list of Transfer Descriptors td[7].tdStatus = 0xF0000000 | (7 << 21); // SETUP, DATA0, DI=7 td[7].tdBuffer = rb_phys + 2 * sizeRB; // Request-Block #2 td[7].tdNextTD = td_phys + 8 * sizeTD; td[7].tdBufEnd = rb_phys + 2 * sizeRB + 7; td[8].tdStatus = 0xF0100000 | (0 << 21); // IN, DATA1, DI=0 td[8].tdBuffer = 0x00000000; td[8].tdNextTD = td_phys + 9 * sizeTD; td[8].tdBufEnd = 0x00000000; td[9].tdStatus = 0x00000000; td[9].tdBuffer = 0x00000000; td[9].tdNextTD = 0x00000000; td[9].tdBufEnd = 0x00000000; lp[ 0x0C >> 2 ] = 0x0000007F; // reset WC bits // clear the Endpoint Descriptor's 'sKip' bit ed[0].edControl &= ~(1 << 14); // wait for the 'WritebackDoneHead' interrupt while ( (lp[ 0x0C >> 2 ] & (1 << 1)) == 0 ); // report outcome of the control-transfer if ( td[8].tdStatus >> 28 ) { printf( " SET_INTERFACE failed \n" ); exit(1); } printf( " Executed SET_INTERFACE control-transfer:" ); printf( " interface=%d \n\n", rb[2].wValue ); //------------------------------------------ // Issue 'GET_SAMPLE_RATE' control-transfer //------------------------------------------ // initialize the parameter-block *(unsigned int *)bf = 0; // build the Request-Blocks rb[3].bmReqType = 0xA2; // device-to-host rb[3].bRequest = 0x81; // Get_Cur rb[3].wValue = 0x0100; // rb[3].wIndex = 0x0084; // endpoint=4 IN rb[3].wLength = 0x0003; // length=3 rb[4].bmReqType = 0x22; // host-to-device rb[4].bRequest = 0x01; // Set_Cur rb[4].wValue = 0x0100; // rb[4].wIndex = 0x0084; // endpoint=4 IN rb[4].wLength = 0x0003; // length=3 // setup revised Endpoint Descriptor with 'sKip' bit set ed[0].edControl = 0x00084000 | dev; // MPS=8, FS, K ed[0].edTailPtr = td_phys + 15 * sizeTD; ed[0].edHeadPtr = td_phys + 12 * sizeTD; ed[0].edNextED = ed_phys; // setup a fresh linked-list of Transfer Descriptors td[12].tdStatus = 0xF0000000 | (7 << 21); // SETUP, DATA0, DI=7 td[12].tdBuffer = rb_phys + 3 * sizeRB; // Request-Block #5 td[12].tdNextTD = td_phys + 13 * sizeTD; td[12].tdBufEnd = rb_phys + 3 * sizeRB + 7; td[13].tdStatus = 0xF0100000 | (7 << 21); // IN, DATA1, DI=7 td[13].tdBuffer = bf_phys; td[13].tdNextTD = td_phys + 14 * sizeTD; td[13].tdBufEnd = bf_phys + 2; td[14].tdStatus = 0xF3080000 | (0 << 21); // OUT, DATA1, DI=0 td[14].tdBuffer = 0x00000000; td[14].tdNextTD = td_phys + 15 * sizeTD; td[14].tdBufEnd = 0x00000000; td[15].tdStatus = 0x00000000; td[15].tdBuffer = 0x00000000; td[15].tdNextTD = 0x00000000; td[15].tdBufEnd = 0x00000000; lp[ 0x0C >> 2 ] = 0x0000007F; // reset WC bits // clear the Endpoint Descriptor's 'sKip' bit ed[0].edControl &= ~(1 << 14); // wait for the 'WritebackDoneHead' interrupt while ( (lp[ 0x0C >> 2 ] & (1 << 1)) == 0 ); // report outcome of the control-transfer if ( td[14].tdStatus >> 28 ) { printf( " GET_SAMPLE_RATE failed \n" ); exit(1); } printf( " Executed GET_SAMPLE_RATE control-transfer:" ); printf( " frequency=%u \n", *(unsigned int *)bf ); //------------------------------------------ // Issue 'SET_SAMPLE_RATE' control-transfer //------------------------------------------ // initialize the parameter-block *(unsigned int *)bf = 8000; // setup revised Endpoint Descriptor with 'sKip' bit set ed[0].edControl = 0x00084000 | dev; // MPS=8, FS, K ed[0].edTailPtr = td_phys + 19 * sizeTD; ed[0].edHeadPtr = td_phys + 16 * sizeTD; ed[0].edNextED = ed_phys; // setup a fresh linked-list of Transfer Descriptors td[16].tdStatus = 0xF0000000 | (7 << 21); // SETUP, DATA0, DI=7 td[16].tdBuffer = rb_phys + 4 * sizeRB; // Request-Block #5 td[16].tdNextTD = td_phys + 17 * sizeTD; td[16].tdBufEnd = rb_phys + 4 * sizeRB + 7; td[17].tdStatus = 0xF0080000 | (7 << 21); // OUT, DATA1, DI=7 td[17].tdBuffer = bf_phys; td[17].tdNextTD = td_phys + 18 * sizeTD; td[17].tdBufEnd = bf_phys + 2; td[18].tdStatus = 0xF3100000 | (0 << 21); // IN, DATA1, DI=0 td[18].tdBuffer = 0x00000000; td[18].tdNextTD = td_phys + 19 * sizeTD; td[18].tdBufEnd = 0x00000000; td[19].tdStatus = 0x00000000; td[19].tdBuffer = 0x00000000; td[19].tdNextTD = 0x00000000; td[19].tdBufEnd = 0x00000000; lp[ 0x0C >> 2 ] = 0x0000007F; // reset WC bits // clear the Endpoint Descriptor's 'sKip' bit ed[0].edControl &= ~(1 << 14); // wait for the 'WritebackDoneHead' interrupt while ( (lp[ 0x0C >> 2 ] & (1 << 1)) == 0 ); // report outcome of the control-transfer if ( td[18].tdStatus >> 28 ) { printf( " SET_SAMPLE_RATE failed \n" ); exit(1); } printf( " Executed SET_SAMPLE_RATE control-transfer:" ); printf( " frequency=%u \n", *(unsigned int *)bf ); //------------------------------------------ // Issue 'GET_SAMPLE_RATE' control-transfer //------------------------------------------ // initialize the parameter-block *(unsigned int *)bf = 0; // setup revised Endpoint Descriptor with 'sKip' bit set ed[0].edControl = 0x00084000 | dev; // MPS=8, FS, K ed[0].edTailPtr = td_phys + 15 * sizeTD; ed[0].edHeadPtr = td_phys + 12 * sizeTD; ed[0].edNextED = ed_phys; // setup a fresh linked-list of Transfer Descriptors td[12].tdStatus = 0xF0000000 | (7 << 21); // SETUP, DATA0, DI=7 td[12].tdBuffer = rb_phys + 3 * sizeRB; // Request-Block #5 td[12].tdNextTD = td_phys + 13 * sizeTD; td[12].tdBufEnd = rb_phys + 3 * sizeRB + 7; td[13].tdStatus = 0xF0100000 | (7 << 21); // IN, DATA1, DI=7 td[13].tdBuffer = bf_phys; td[13].tdNextTD = td_phys + 14 * sizeTD; td[13].tdBufEnd = bf_phys + 2; td[14].tdStatus = 0xF3080000 | (0 << 21); // OUT, DATA1, DI=0 td[14].tdBuffer = 0x00000000; td[14].tdNextTD = td_phys + 15 * sizeTD; td[14].tdBufEnd = 0x00000000; td[15].tdStatus = 0x00000000; td[15].tdBuffer = 0x00000000; td[15].tdNextTD = 0x00000000; td[15].tdBufEnd = 0x00000000; lp[ 0x0C >> 2 ] = 0x0000007F; // reset WC bits // clear the Endpoint Descriptor's 'sKip' bit ed[0].edControl &= ~(1 << 14); // wait for the 'WritebackDoneHead' interrupt while ( (lp[ 0x0C >> 2 ] & (1 << 1)) == 0 ); // report outcome of the control-transfer if ( td[14].tdStatus >> 28 ) { printf( " GET_SAMPLE_RATE failed \n" ); exit(1); } printf( " Executed GET_SAMPLE_RATE control-transfer:" ); printf( " frequency=%u \n", *(unsigned int *)bf ); printf( "\n" ); //===================================================== int inch = 0; printf( "\n Press -key to begin recording..." ); fflush( stdout ); read( STDIN_FILENO, &inch, sizeof( inch ) ); printf( " now recording... " ); fflush( stdout ); //===================================================== // Erase prior contents of destination buffer memset( &bf[0], 0x00, 0xA000 ); // 10 4KB-pages // setup a pair of inactive transfer-descriptors unsigned int itd_phys = td_phys + 10 * sizeTD; OHCI_TD *itd = &td[10]; memset( itd, 0x00, 2 * sizeTD ); // Setup an ED for the Isochronous IN endpoint (Endpoint 1) unsigned int dir = 2; // IN unsigned int isoc = 1; unsigned int endpt = 4; unsigned int maxpkt = 16; unsigned int control = (isoc << 15) | (dir << 11); control |= (maxpkt << 16) | (endpt << 7) | dev; ed[1].edControl = control | (1 << 14); // IN, ISOC, sKip=1 ed[1].edTailPtr = itd_phys + 1 * sizeTD; ed[1].edHeadPtr = itd_phys + 0 * sizeTD; ed[1].edNextED = 0x00000000; // initialize the Host Controller Communications Area for (int i = 0; i < 32; i++) hcca[ i ] = ed_phys + 1 * sizeED; // activate the Periodic List processing lp[ 0x18 >> 2 ] = hcca_phys; lp[ 0x04 >> 2 ] |= (3 << 2); // HcCommand.PLE and IE // Setup the circular-list of Isochronous Transfer Descriptors unsigned int buffer = bf_phys; unsigned int bufend = (bf_phys + 8 * maxpkt) - 1; unsigned short offset = 0xE000; unsigned short frnum = (lp[ 0x3C >> 2 ] | 7) + 1; frnum += 8; for (int pass = 0; pass < 320; pass++) { itd[0].tdStatus = 0xF7000000 | frnum; itd[0].tdBuffer = buffer; itd[0].tdBufEnd = bufend; itd[0].tdNextTD = itd_phys; for (int j = 0; j < 8; j++) itd[0].tdOffset[j] = offset + j * maxpkt; frnum += 8; offset += 8 * maxpkt; bufend += 8 * maxpkt; if ( offset & 0x1000 ) { buffer += 0x1000; bufend = (buffer + 8 * maxpkt) - 1; offset = 0xE000; } // clear the Endpoint Descriptor's 'sKip' bit ed[1].edHeadPtr = itd_phys; ed[1].edControl = control; if ( (pass % 32) == 0 ) write( STDOUT_FILENO, "* ", 2 ); while ( ( itd[0].tdStatus >> 28 ) == 0xF ) if ( ed[1].edHeadPtr & 1 ) break; } // dedactivate Periodic List processi9ng lp[ 0x04 >> 2 ] &= ~(3 << 2); printf( " recording done. \n\n" ); //-------------------------------------------- // Execute the SET_INTERFACE control-transfer //-------------------------------------------- // build the Request-Block for 'Set_Interface' rb[3].bmReqType = 0x01; // host-to-device rb[3].bRequest = 0x0B; // Set_Interface rb[3].wValue = 0x0000; // interface=0 rb[3].wIndex = 0x0001; // index=1 rb[3].wLength = 0x0000; // zero length // setup revised Endpoint Descriptor with 'sKip' bit set ed[0].edControl = 0x00084000 | dev; // MPS=8, FS, K ed[0].edTailPtr = td_phys + 9 * sizeTD; ed[0].edHeadPtr = td_phys + 7 * sizeTD; ed[0].edNextED = ed_phys; // setup a fresh linked-list of Transfer Descriptors td[7].tdStatus = 0xF0000000 | (7 << 21); // SETUP, DATA0, DI=7 td[7].tdBuffer = rb_phys + 3 * sizeRB; // Request-Block #3 td[7].tdNextTD = td_phys + 8 * sizeTD; td[7].tdBufEnd = rb_phys + 3 * sizeRB + 7; td[8].tdStatus = 0xF0100000 | (0 << 21); // IN, DATA1, DI=0 td[8].tdBuffer = 0x00000000; td[8].tdNextTD = td_phys + 9 * sizeTD; td[8].tdBufEnd = 0x00000000; td[9].tdStatus = 0x00000000; td[9].tdBuffer = 0x00000000; td[9].tdNextTD = 0x00000000; td[9].tdBufEnd = 0x00000000; lp[ 0x0C >> 2 ] = 0x0000007F; // reset WC bits // clear the Endpoint Descriptor's 'sKip' bit ed[0].edControl &= ~(1 << 14); // wait for the 'WritebackDoneHead' interrupt while ( (lp[ 0x0C >> 2 ] & (1 << 1)) == 0 ); // report outcome of the control-transfer if ( td[8].tdStatus >> 28 ) { printf( " SET_INTERFACE failed \n" ); exit(1); } printf( " Executed SET_INTERFACE control-transfer:" ); printf( " interface=%d \n\n", rb[3].wValue ); // Deactivate the Control List processing lp[ 0x04 >> 2 ] &= ~(1 << 4); // clear HcCommand.CLE printf( "\n Press key to view descriptors and save recording\n" ); getchar(); //====================================================== //========= DISPLAY THE TD AND ED DESCRIPTORS ========= //====================================================== // display the TDs and EDs printf( "\n" ); printf( " OHCI Transfer Descriptors \n" ); for (int i = 0; i <= 9; i++) { unsigned int *entry = (unsigned int *)&td[i]; printf( " TD%X <0x%08X>: ", i, td_phys + i * sizeTD ); for (int j = 0; j < 4; j++) printf( "%08X ", entry[j] ); printf( "\n" ); } for (int i = 10; i <= 10; i++) { unsigned int *entry = (unsigned int *)&td[i]; printf( " TD%X <0x%08X>: ", i, td_phys + i * sizeTD ); for (int j = 0; j < 4; j++) printf( "%08X ", entry[j] ); printf( "\n" ); printf( " " ); for (int j = 4; j < 8; j++) printf( "%08X ", entry[j] ); printf( "\n" ); } printf( "\n" ); printf( " OHCI Endpoint Descriptors \n" ); for (int i = 0; i < 2; i++) { unsigned int *entry = (unsigned int *)&ed[i]; printf( " ED%X <0x%08X>: ", i, ed_phys + i * sizeED ); for (int j = 0; j < 4; j++) printf( "%08X ", entry[j] ); printf( "\n" ); } printf( "\n" ); //----------------------------------- // Write the PCM data to a .wav file //----------------------------------- // define the .wav file's audio parameters short formatTag = 1; short nChannels = 1; int samplesPerSec = 8000; int avBytesPerSec = samplesPerSec * 2; short blockAlignmnt = 2; short bitsPerSample = 16; int dataChunkSize = 10 * 4096; int fmt_ChunkSize = 16; int riffChunkSize = 4 + (8 + fmt_ChunkSize) + (8 + dataChunkSize); // open the .wav .file for writing char wavname[] = "headmike.wav"; int wav = open( wavname, O_CREAT | O_RDWR | O_TRUNC, 0666 ); if ( wav < 0 ) { perror( wavname ); exit(1); } // output the RIFF chunk write( wav, "RIFF", 4 ); write( wav, &riffChunkSize, 4 ); write( wav, "WAVE", 4 ); // output the FORMAT chunk write( wav, "fmt ", 4 ); write( wav, &fmt_ChunkSize, 4 ); write( wav, &formatTag, 2 ); write( wav, &nChannels, 2 ); write( wav, &samplesPerSec, 4 ); write( wav, &avBytesPerSec, 4 ); write( wav, &blockAlignmnt, 2 ); write( wav, &bitsPerSample, 2 ); // write the DATA chunk write( wav, "data", 4 ); write( wav, &dataChunkSize, 4 ); char *wp = ((char *)&bf[0]); int bytes_to_file = lseek( wav, 0L, SEEK_CUR ); int bytes_to_save = dataChunkSize; while ( bytes_to_save > 0 ) { int wbytes = write( wav, wp, bytes_to_save ); if ( wbytes < 0 ) break; bytes_to_save -= wbytes; bytes_to_file += wbytes; wp += wbytes; } close( wav ); printf( " Written %d bytes to \'%s\' \n", bytes_to_file, wavname ); }