/********************************************************************\ Name: usb.c Created by: Stefan Ritt Contents: USB routines for Cygnal USB sub-master SUBM250 running on Cygnal C8051F320 $Id$ \********************************************************************/ #include "mscbemb.h" #include "usb.h" extern void watchdog_refresh(unsigned char from_interrupt) reentrant; /*---- Globals -----------------------------------------------------*/ DEVICE_STATUS idata gDeviceStatus; EP0_COMMAND idata gEp0Command; EP_STATUS idata gEp0Status; EP_STATUS idata gEp1InStatus; EP_STATUS idata gEp2OutStatus; PIF_STATUS idata pIfStatus; BYTE *prx_buf; BYTE *pn_rx; /*------------------------------------------------------------------*/ //--------------------------- // Descriptor Declarations //--------------------------- // All descriptors are contained in the global structure . // This structure contains BYTE arrays for the standard device descriptor // and all configurations. The lengths of the configuration arrays are // defined by the number of interface and endpoint descriptors required // for the particular configuration (these constants are named // CFG1_IF_DSC and CFG1_EP_DSC for configuration1). // // The entire gDescriptorMap structure is initialized below in // codespace. DESCRIPTORS code gDescriptorMap = { //--------------------------- // Begin Standard Device Descriptor (structure element stddevdsc) //--------------------------- 18, // bLength 0x01, // bDescriptorType 0x00, 0x02, // bcdUSB (lsb first) 0x00, // bDeviceClass 0x00, // bDeviceSubClass 0x00, // bDeviceProtocol EP0_PACKET_SIZE, // bMaxPacketSize0 0xC4, 0x10, // idVendor (lsb first) 0x75, 0x11, // idProduct (lsb first) 0x00, 0x00, // bcdDevice (lsb first) 0x01, // iManufacturer 0x02, // iProduct 0x00, // iSerialNumber 0x01, // bNumConfigurations //--------------------------- // Begin Configuration 1 (structure element cfg1) //--------------------------- // Begin Descriptor: Configuration 1 0x09, // Length 0x02, // Type 0x20, 0x00, // TotalLength (lsb first) 0x01, // NumInterfaces 0x01, // bConfigurationValue 0x00, // iConfiguration 0x80, // bmAttributes (no remote wakeup) 0x0F, // MaxPower (*2mA) // Begin Descriptor: Interface0, Alternate0 0x09, // bLength 0x04, // bDescriptorType 0x00, // bInterfaceNumber 0x00, // bAlternateSetting 0x02, // bNumEndpoints 0x00, // bInterfaceClass 0x00, // bInterfaceSubClass 0x00, // bInterfaceProcotol 0x00, // iInterface // Begin Descriptor: Endpoint1, Interface0, Alternate0 0x07, // bLength 0x05, // bDescriptorType 0x81, // bEndpointAddress (ep1, IN) 0x02, // bmAttributes (Bulk) EP1_PACKET_SIZE, 0x00, // wMaxPacketSize (lsb first) 0x20, // bInterval // Begin Descriptor: Endpoint2, Interface0, Alternate0 0x07, // bLength 0x05, // bDescriptorType 0x02, // bEndpointAddress (ep2, OUT) 0x02, // bmAttributes (Bulk) EP2_PACKET_SIZE, 0x00, // wMaxPacketSize (lsb first) 0x01, // bInterval //--------------------------- // End Configuration 1 //--------------------------- }; //--------------------------- // String Descriptors //--------------------------- code const BYTE String0Desc[4] = { 0x04, 0x03, 0x09, 0x04 }; #define STR1LEN sizeof("PSI S. Ritt")*2 code const BYTE String1Desc[STR1LEN] = { STR1LEN, 0x03, 'P', 0, 'S', 0, 'I', 0, ' ', 0, 'S', 0, '.', 0, ' ', 0, 'R', 0, 'i', 0, 't', 0, 't', 0, }; #define STR2LEN sizeof("MSCB Submaster SCS-250")*2 code const BYTE String2Desc[STR2LEN] = { STR2LEN, 0x03, 'M', 0, 'S', 0, 'C', 0, 'B', 0, ' ', 0, 'S', 0, 'u', 0, 'b', 0, 'm', 0, 'a', 0, 's', 0, 't', 0, 'e', 0, 'r', 0, ' ', 0, 'S', 0, 'C', 0, 'S', 0, '-', 0, '2', 0, '5', 0, '0', 0, }; code BYTE* const StringDescTable[] = { String0Desc, String1Desc, String2Desc }; /*---- Prototypes --------------------------------------------------*/ void USBReset(void); void Endpoint0(void); void BulkOrInterruptIn(PEP_STATUS pEpInStatus); void BulkOrInterruptOut(PEP_STATUS pEpOutStatus); void FIFORead(BYTE bEp, UINT uNumBytes, BYTE * pData); void FIFOWrite(BYTE bEp, UINT uNumBytes, BYTE * pData) reentrant; void SetAddressRequest(void); void SetFeatureRequest(void); void ClearFeatureRequest(void); void SetConfigurationRequest(void); void SetInterfaceRequest(void); void GetStatusRequest(void); void GetDescriptorRequest(void); void GetConfigurationRequest(void); void GetInterfaceRequest(void); BYTE HaltEndpoint(UINT uEp); BYTE EnableEndpoint(UINT uEp); BYTE GetEpStatus(UINT uEp); BYTE SetConfiguration(BYTE SelectConfig); BYTE SetInterface(PIF_STATUS pIfStatus); /*------------------------------------------------------------------*/ /* USB Initialization - Initialize USB0 - Enable USB0 interrupts - Enable USB0 transceiver - Enable USB0 */ void usb_init(unsigned char *buffer, unsigned char *psize) { UWRITE_BYTE(POWER, 0x08); // Asynch. reset UWRITE_BYTE(IN1IE, 0x0F); // Enable Endpoint0 Interrupt UWRITE_BYTE(OUT1IE, 0x0F); UWRITE_BYTE(CMIE, 0x04); // Enable Reset interrupt USB0XCN = 0xC0; // Enable transceiver USB0XCN |= FULL_SPEED; // Select device speed UWRITE_BYTE(CLKREC, 0x80); // Enable clock recovery, // single-step mode disabled EIE1 |= 0x02; // Enable USB0 Interrupts EA = 1; // Enable global interrupts UWRITE_BYTE(POWER, 0x00); // Enable USB0 by clearing the // USB Inhibit bit // Suspend mode disabled /* save pointers */ prx_buf = buffer; pn_rx = psize; } /*------------------------------------------------------------------*/ /* This is the top level USB ISR. All endpoint interrupt/request handlers are called from this function. Handler routines for any configured interrupts should be added in the appropriate endpoint handler call slots. */ void USB_ISR() interrupt 8 { BYTE bCommonInt, bInInt, bOutInt; // Read interrupt registers UREAD_BYTE(CMINT, bCommonInt); UREAD_BYTE(IN1INT, bInInt); UREAD_BYTE(OUT1INT, bOutInt); // Check for reset interrupt if (bCommonInt & rbRSTINT) USBReset(); // Check for Endpoint0 interrupt if (bInInt & rbEP0) Endpoint0(); // Endpoint1 IN // SR: outcommented, gets handled by usb_send // if (bInInt & rbIN1) // BulkOrInterruptIn(&gEp1InStatus); // Endpoint2 OUT if (bOutInt & rbOUT2) BulkOrInterruptOut(&gEp2OutStatus); } /*------------------------------------------------------------------*/ /* - Initialize the global Device Status structure (all zeros) - Resets all endpoints */ void USBReset() { BYTE i, bPower = 0; BYTE * pDevStatus; // Reset device status structure to all zeros (undefined) pDevStatus = (BYTE *)&gDeviceStatus; for (i=0;i gDescriptorMap.bStdDevDsc[std_bNumConfigurations]) { bEpState = EP_ERROR; } // Handle zero configuration assignment else if (gEp0Command.wValue.c[1] == 0) gDeviceStatus.bDevState = DEV_ADDRESS; // Select the assigned configuration else bEpState = SetConfiguration(gEp0Command.wValue.c[1]); } gEp0Status.bEpState = bEpState; } /*------------------------------------------------------------------*/ void SetInterfaceRequest() { BYTE bEpState; // Length field must be zero if ((gEp0Command.wLength.i) || (gDeviceStatus.bDevState != DEV_CONFIG)) bEpState = EP_ERROR; else { // Check that target interface exists for this configuration if(gEp0Command.wIndex.i > gDeviceStatus.bNumInterf - 1) bEpState = EP_ERROR; else { // Get pointer to interface status structure pIfStatus = (PIF_STATUS)&gDeviceStatus.IfStatus; // Check that alternate setting exists for the interface if (gEp0Command.wValue.i > pIfStatus->bNumAlts) bEpState = EP_ERROR; // Assign alternate setting else { pIfStatus->bCurrentAlt = gEp0Command.wValue.i; bEpState = SetInterface(pIfStatus); } } } gEp0Status.bEpState = bEpState; } /*------------------------------------------------------------------*/ void GetStatusRequest() { BYTE bEpState; // Value field must be zero; Length field must be 2 if ((gEp0Command.wValue.i != 0) || (gEp0Command.wLength.i != 0x02) || (gDeviceStatus.bDevState == DEV_DEFAULT) || (gDeviceStatus.bDevState == DEV_ADDRESS && gEp0Command.wIndex.i != 0)) { bEpState = EP_ERROR; } else { // Check for desired status (device, interface, endpoint) switch (gEp0Command.bmRequestType & CMD_MASK_RECIP) { // Device case CMD_RECIP_DEV: // Index must be zero for a Device status request if (gEp0Command.wIndex.i != 0) bEpState = EP_ERROR; else { // Prepare data_out for transmission gEp0Status.wData.c[1] = 0; gEp0Status.wData.c[0] = gDeviceStatus.bRemoteWakeupStatus; gEp0Status.wData.c[0] |= gDeviceStatus.bSelfPoweredStatus; } break; // Interface case CMD_RECIP_IF: // Prepare data_out for transmission gEp0Status.wData.i = 0; break; // Endpoint case CMD_RECIP_EP: // Prepare data_out for transmission gEp0Status.wData.i = 0; if (GetEpStatus(gEp0Command.wIndex.i) == EP_HALTED) gEp0Status.wData.c[0] |= 0x01; break; // Other cases unsupported default: bEpState = EP_ERROR; break; } // Endpoint0 state assignment bEpState = EP_TX; // Point ep0 data pointer to transmit data_out gEp0Status.pData = (BYTE *)&gEp0Status.wData.i; gEp0Status.uNumBytes = 2; } gEp0Status.bEpState = bEpState; } /*------------------------------------------------------------------*/ void GetDescriptorRequest() { WORD wTempInt; UINT uNumBytes; BYTE bEpState; // This request is valid in all device states // Switch on requested descriptor (Value field) switch (gEp0Command.wValue.c[0]) { // Device Descriptor Request case DSC_DEVICE: // Get size of the requested descriptor uNumBytes = STD_DSC_SIZE; // Prep to send the requested length if (uNumBytes > gEp0Command.wLength.i) { uNumBytes = gEp0Command.wLength.i; } // Point data pointer to the requested descriptor gEp0Status.pData = (void*)&gDescriptorMap.bStdDevDsc; bEpState = EP_TX; break; // Configuration Descriptor Request case DSC_CONFIG: // Make sure requested descriptor exists if (gEp0Command.wValue.c[1] > gDescriptorMap.bStdDevDsc[std_bNumConfigurations]) { bEpState = EP_ERROR; } else { // Get total length of this configuration descriptor // (includes all associated interface and endpoints) wTempInt.c[1] = gDescriptorMap.bCfg1[cfg_wTotalLength_lsb]; wTempInt.c[0] = gDescriptorMap.bCfg1[cfg_wTotalLength_msb]; uNumBytes = wTempInt.i; // Prep to transmit the requested length if (uNumBytes > gEp0Command.wLength.i) { uNumBytes = gEp0Command.wLength.i; } // Point data pointer to requested descriptor gEp0Status.pData = &gDescriptorMap.bCfg1; bEpState = EP_TX; } break; // String Descriptor Request case DSC_STRING: gEp0Status.pData = (void*)StringDescTable[gEp0Command.wValue.c[1]]; uNumBytes = *((BYTE *)gEp0Status.pData); bEpState = EP_TX; break; } gEp0Status.uNumBytes = uNumBytes; gEp0Status.bEpState = bEpState; } /*------------------------------------------------------------------*/ void GetConfigurationRequest() { BYTE bEpState; // Length field must be 1; Index field must be 0; // Value field must be 0 if ((gEp0Command.wLength.i != 1) || (gEp0Command.wIndex.i) || (gEp0Command.wValue.i) || (gDeviceStatus.bDevState == DEV_DEFAULT)) { bEpState = EP_ERROR; } else if (gDeviceStatus.bDevState == DEV_ADDRESS) { // Prepare data_out for transmission gEp0Status.wData.i = 0; // Point ep0 data pointer to transmit data_out gEp0Status.pData = (BYTE *)&gEp0Status.wData.i; // ep0 state assignment bEpState = EP_TX; } else { // Index to desired field gEp0Status.pData = (void *)&gDescriptorMap.bCfg1[cfg_bConfigurationValue]; // ep0 state assignment bEpState = EP_TX; } gEp0Status.uNumBytes = 1; gEp0Status.bEpState = bEpState; } /*------------------------------------------------------------------*/ void GetInterfaceRequest() { BYTE bEpState; // Value field must be 0; Length field must be 1 if ((gEp0Command.wValue.i) || (gEp0Command.wLength.i != 1) || (gDeviceStatus.bDevState != DEV_CONFIG)) { bEpState = EP_ERROR; } else { // Make sure requested interface exists if (gEp0Command.wIndex.i > gDeviceStatus.bNumInterf - 1) bEpState = EP_ERROR; else { // Get current interface setting gEp0Status.pData = (void *)&gDeviceStatus.IfStatus.bCurrentAlt; // Length must be 1 gEp0Status.uNumBytes = 1; bEpState = EP_TX; } } gEp0Status.bEpState = bEpState; } /*------------------------------------------------------------------*/ BYTE HaltEndpoint(UINT uEp) { BYTE bReturnState, bIndex; // Save current INDEX value and target selected endpoint UREAD_BYTE (INDEX, bIndex); UWRITE_BYTE (INDEX, (BYTE)uEp & 0x00EF); // Halt selected endpoint and update its status flag switch (uEp) { case EP1_IN: UWRITE_BYTE (EINCSRL, rbInSDSTL); gEp1InStatus.bEpState = EP_HALTED; bReturnState = EP_IDLE; // Return success flag break; case EP2_OUT: UWRITE_BYTE (EOUTCSRL, rbOutSDSTL); gEp2OutStatus.bEpState = EP_HALTED; bReturnState = EP_IDLE; // Return success flag break; default: bReturnState = EP_ERROR; // Return error flag // if endpoint not found break; } UWRITE_BYTE (INDEX, bIndex); // Restore saved INDEX return bReturnState; } /*------------------------------------------------------------------*/ BYTE EnableEndpoint(UINT uEp) { BYTE bReturnState, bIndex; // Save current INDEX value and target selected endpoint UREAD_BYTE (INDEX, bIndex); UWRITE_BYTE (INDEX, (BYTE)uEp & 0x00EF); // Flag selected endpoint has HALTED switch (uEp) { case EP1_IN: // Disable STALL condition and clear the data toggle UWRITE_BYTE (EINCSRL, rbInCLRDT); gEp1InStatus.bEpState = EP_IDLE; // Return success bReturnState = EP_IDLE; break; case EP2_OUT: // Disable STALL condition and clear the data toggle UWRITE_BYTE (EOUTCSRL, rbOutCLRDT); gEp2OutStatus.bEpState = EP_IDLE;// Return success bReturnState = EP_IDLE; break; default: bReturnState = EP_ERROR; // Return error // if no endpoint found break; } UWRITE_BYTE (INDEX, bIndex); // Restore INDEX return bReturnState; } /*------------------------------------------------------------------*/ BYTE GetEpStatus(UINT uEp) { BYTE bReturnState; // Get selected endpoint status switch (uEp) { case EP1_IN: bReturnState = gEp1InStatus.bEpState; break; case EP2_OUT: bReturnState = gEp2OutStatus.bEpState; break; default: bReturnState = EP_ERROR; break; } return bReturnState; } /*------------------------------------------------------------------*/ BYTE SetConfiguration(BYTE SelectConfig) { BYTE bReturnState = EP_IDLE; // Endpoint state return value PIF_STATUS pIfStatus; // Pointer to interface status // structure // Store address of selected config desc gDeviceStatus.pConfig = &gDescriptorMap.bCfg1; // Confirm that this configuration descriptor matches the requested // configuration value if (gDeviceStatus.pConfig[cfg_bConfigurationValue] != SelectConfig) { bReturnState = EP_ERROR; } else { // Store number of interfaces for this configuration gDeviceStatus.bNumInterf = gDeviceStatus.pConfig[cfg_bNumInterfaces]; // Store total number of interface descriptors for this configuration gDeviceStatus.bTotalInterfDsc = MAX_IF; // Get pointer to the interface status structure pIfStatus = (PIF_STATUS)&gDeviceStatus.IfStatus[0]; // Build Interface status structure for Interface0 pIfStatus->bIfNumber = 0; // Set interface number pIfStatus->bCurrentAlt = 0; // Select alternate number zero pIfStatus->bNumAlts = 0; // No other alternates SetInterface(pIfStatus); // Configure Interface0, Alternate0 gDeviceStatus.bDevState = DEV_CONFIG;// Set device state to configured gDeviceStatus.bCurrentConfig = SelectConfig;// Store current config } return bReturnState; } /*------------------------------------------------------------------*/ BYTE SetInterface(PIF_STATUS pIfStatus) { BYTE bReturnState = EP_IDLE; BYTE bIndex; // Save current INDEX value UREAD_BYTE (INDEX, bIndex); // Add actions for each possible interface alternate selections switch(pIfStatus->bIfNumber) { // Configure endpoints for interface0 case 0: // Configure Endpoint1 IN UWRITE_BYTE(INDEX, 1); // Index to Endpoint1 registers UWRITE_BYTE(EINCSRH, 0x20); // FIFO split disabled, // direction = IN UWRITE_BYTE(EOUTCSRH, 0); // Double-buffering disabled gEp1InStatus.uNumBytes = 0; // Reset byte counter gEp1InStatus.uMaxP = EP1_PACKET_SIZE;// Set maximum packet size gEp1InStatus.bEp = EP1_IN; // Set endpoint address gEp1InStatus.bEpState = EP_IDLE; // Set endpoint state // Endpoint2 OUT UWRITE_BYTE(INDEX, 2); // Index to Endpoint2 registers UWRITE_BYTE(EINCSRH, 0x00); // FIFO split disabled, // direction = OUT gEp2OutStatus.uNumBytes = 0; // Reset byte counter gEp2OutStatus.uMaxP = EP2_PACKET_SIZE;// Set maximum packet size gEp2OutStatus.bEp = EP2_OUT; // Set endpoint number gEp2OutStatus.bEpState = EP_IDLE;// Set endpoint state UWRITE_BYTE(INDEX, 0); // Return to index 0 break; // Configure endpoints for interface1 case 1: // Configure endpoints for interface2 case 2: // Default (error) default: bReturnState = EP_ERROR; } UWRITE_BYTE (INDEX, bIndex); // Restore INDEX return bReturnState; } /*------------------------------------------------------------------*/ void BulkOrInterruptOut(PEP_STATUS pEpOutStatus) { UINT uBytes; BYTE bTemp = 0; BYTE bCsrL, bCsrH; UWRITE_BYTE(INDEX, pEpOutStatus->bEp); // Index to current endpoint UREAD_BYTE(EOUTCSRL, bCsrL); UREAD_BYTE(EOUTCSRH, bCsrH); // Make sure this endpoint is not halted if (pEpOutStatus->bEpState != EP_HALTED) { // Handle STALL condition sent if (bCsrL & rbOutSTSTL) { // Clear Send Stall, Sent Stall, and data toggle UWRITE_BYTE(EOUTCSRL, rbOutCLRDT); } // Read received packet(s) // If double-buffering is enabled, multiple packets may be ready while(bCsrL & rbOutOPRDY) { // Get packet length UREAD_BYTE(EOUTCNTL, bTemp); // Low byte uBytes = (UINT)bTemp & 0x00FF; UREAD_BYTE(EOUTCNTH, bTemp); // High byte uBytes |= (UINT)bTemp << 8; if (uBytes <= EP2_PACKET_SIZE) { // Read FIFO FIFORead(pEpOutStatus->bEp, uBytes, (BYTE*)prx_buf); pEpOutStatus->uNumBytes = uBytes; // Signal new data *pn_rx = uBytes; } // If a data packet of anything but 8 bytes is received, ignore // and flush the FIFO else { UWRITE_BYTE(EOUTCSRL, rbOutFLUSH); } // Clear out-packet-ready UWRITE_BYTE(INDEX, pEpOutStatus->bEp); UWRITE_BYTE(EOUTCSRL, 0); // Read updated status register UREAD_BYTE(EOUTCSRL, bCsrL); } } } /*------------------------------------------------------------------*/ void BulkOrInterruptIn(PEP_STATUS pEpInStatus) { BYTE bCsrL, bCsrH; UWRITE_BYTE(INDEX, pEpInStatus->bEp); // Index to current endpoint UREAD_BYTE(EINCSRL, bCsrL); UREAD_BYTE(EINCSRH, bCsrH); // Make sure this endpoint is not halted if (pEpInStatus->bEpState != EP_HALTED) { // Handle STALL condition sent if (bCsrL & rbInSTSTL) { UWRITE_BYTE(EINCSRL, rbInCLRDT); // Clear Send Stall and Sent Stall, // and clear data toggle } // If a FIFO slot is open, write a new packet to the IN FIFO if (!(bCsrL & rbInINPRDY)) { led_blink(0, 1, 300); pEpInStatus->uNumBytes = 8; pEpInStatus->pData = (BYTE*)prx_buf; // Write bytes to the FIFO FIFOWrite(pEpInStatus->bEp, pEpInStatus->uNumBytes, (BYTE*)pEpInStatus->pData); // Set Packet Ready bit (INPRDY) UWRITE_BYTE(EINCSRL, rbInINPRDY); // Check updated endopint status UREAD_BYTE(EINCSRL, bCsrL); } } } /*------------------------------------------------------------------*/ void usb_send(unsigned char *buffer, unsigned char size) { PEP_STATUS pEpInStatus; BYTE bCsrL, bCsrH; if (size == 0) return; pEpInStatus = &gEp1InStatus; UWRITE_BYTE(INDEX, pEpInStatus->bEp); // Index to current endpoint UREAD_BYTE(EINCSRL, bCsrL); UREAD_BYTE(EINCSRH, bCsrH); // Make sure this endpoint is not halted if (pEpInStatus->bEpState != EP_HALTED) { // Handle STALL condition sent if (bCsrL & rbInSTSTL) { UWRITE_BYTE(EINCSRL, rbInCLRDT); // Clear Send Stall and Sent Stall, // and clear data toggle } // Wait until FIFO slot is open while (bCsrL & rbInINPRDY) { watchdog_refresh(0); UREAD_BYTE(EINCSRL, bCsrL); } // write a new packet to the IN FIFO pEpInStatus->uNumBytes = size; pEpInStatus->pData = (BYTE*)buffer; // Write bytes to the FIFO FIFOWrite(pEpInStatus->bEp, pEpInStatus->uNumBytes, (BYTE*)pEpInStatus->pData); // Set Packet Ready bit (INPRDY) UWRITE_BYTE(EINCSRL, rbInINPRDY); // Check updated endopint status UREAD_BYTE(EINCSRL, bCsrL); } } /*------------------------------------------------------------------*/ /* Read from the selected endpoint FIFO Inputs: bEp: target endpoint uNumBytes: number of bytes to unload pData: read data destination */ void FIFORead(BYTE bEp, UINT uNumBytes, BYTE * pData) { BYTE TargetReg; UINT i; // If >0 bytes requested, if (uNumBytes) { TargetReg = FIFO_EP0 + bEp; // Find address for target // endpoint FIFO USB0ADR = (TargetReg & 0x3F); // Set address (mask out bits7-6) USB0ADR |= 0xC0; // Set auto-read and initiate // first read // Unload from the selected FIFO for(i=0; i<(uNumBytes-1); i++) { while(USB0ADR & 0x80); // Wait for BUSY->'0' (data ready) pData[i] = USB0DAT; // Copy data byte } // Disable auto read and copy last byte USB0ADR = (TargetReg & 0x3F); // Set address (mask out bits7-6) while(USB0ADR & 0x80); // Wait for BUSY->'0' (data ready) pData[i] = USB0DAT; // Copy final data byte } } /*------------------------------------------------------------------*/ /* Write to the selected endpoint FIFO Inputs: bEp: target endpoint uNumBytes: number of bytes to write pData: location of source data */ void FIFOWrite(BYTE bEp, UINT uNumBytes, BYTE * pData) reentrant { BYTE TargetReg; UINT i; // If >0 bytes requested, if (uNumBytes) { TargetReg = FIFO_EP0 + bEp; // Find address for target // endpoint FIFO while(USB0ADR & 0x80); // Wait for BUSY->'0' // (register available) USB0ADR = (TargetReg & 0x3F); // Set address (mask out bits7-6) // Write to the selected FIFO for(i=0;i'0' (data ready) } } }