// Copyright (c) 2010, Peter Barrett /* ** Permission to use, copy, modify, and/or distribute this software for ** any purpose with or without fee is hereby granted, provided that the ** above copyright notice and this permission notice appear in all copies. ** ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ** SOFTWARE. */ #include "Arduino.h" #include "USBAPI.h" #include "Reset.h" #include #include "PluggableUSB.h" #include //#define TRACE_CORE(x) x #define TRACE_CORE(x) uint32_t EndPoints[] = { EP_TYPE_CONTROL, #ifdef CDC_ENABLED EP_TYPE_INTERRUPT_IN, // CDC_ENDPOINT_ACM EP_TYPE_BULK_OUT, // CDC_ENDPOINT_OUT EP_TYPE_BULK_IN, // CDC_ENDPOINT_IN #endif #ifdef PLUGGABLE_USB_ENABLED //allocate 6 endpoints and remove const so they can be changed by the user 0, 0, 0, 0, 0, 0, #endif }; /** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */ #define TX_RX_LED_PULSE_MS 100 volatile uint8_t TxLEDPulse; /**< Milliseconds remaining for data Tx LED pulse */ volatile uint8_t RxLEDPulse; /**< Milliseconds remaining for data Rx LED pulse */ static char isRemoteWakeUpEnabled = 0; static char isEndpointHalt = 0; //================================================================== //================================================================== extern const uint16_t STRING_LANGUAGE[]; extern const uint8_t STRING_PRODUCT[]; extern const uint8_t STRING_MANUFACTURER[]; extern const DeviceDescriptor USB_DeviceDescriptor; extern const DeviceDescriptor USB_DeviceDescriptorA; const uint16_t STRING_LANGUAGE[2] = { (3<<8) | (2+2), 0x0409 // English }; #ifndef USB_PRODUCT #define USB_PRODUCT "Arduino Due" #endif const uint8_t STRING_PRODUCT[] = USB_PRODUCT; #ifndef USB_MANUFACTURER #define USB_MANUFACTURER "Arduino LLC" #endif const uint8_t STRING_MANUFACTURER[] = USB_MANUFACTURER; #ifdef CDC_ENABLED #define DEVICE_CLASS 0x02 #else #define DEVICE_CLASS 0x00 #endif // DEVICE DESCRIPTOR const DeviceDescriptor USB_DeviceDescriptor = D_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,ISERIAL,1); const DeviceDescriptor USB_DeviceDescriptorA = D_DEVICE(0xEF,0x02,0x01,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,ISERIAL,1); const QualifierDescriptor USB_DeviceQualifier = D_QUALIFIER(0x00,0x00,0x00,64,1); //! 7.1.20 Test Mode Support static const unsigned char test_packet_buffer[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // JKJKJKJK * 9 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, // JJKKJJKK * 8 0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE, // JJJJKKKK * 8 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, // JJJJJJJKKKKKKK * 8 0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD, // JJJJJJJK * 8 0xFC,0x7E,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0x7E // {JKKKKKKK * 10}, JK }; //================================================================== //================================================================== volatile uint32_t _usbConfiguration = 0; volatile uint32_t _usbInitialized = 0; uint32_t _usbSetInterface = 0; uint32_t _cdcComposite = 0; //================================================================== //================================================================== #define USB_RECV_TIMEOUT class LockEP { irqflags_t flags; public: LockEP(uint32_t ep __attribute__ ((unused))) : flags(cpu_irq_save()) { } ~LockEP() { cpu_irq_restore(flags); } }; // Number of bytes, assumes a rx endpoint uint32_t USBD_Available(uint32_t ep) { LockEP lock(ep); return UDD_FifoByteCount(ep & 0xF); } // Non Blocking receive // Return number of bytes read uint32_t USBD_Recv(uint32_t ep, void* d, uint32_t len) { if (!_usbConfiguration) return -1; LockEP lock(ep); uint32_t n = UDD_FifoByteCount(ep & 0xF); len = min(n,len); n = len; uint8_t* dst = (uint8_t*)d; while (n--) *dst++ = UDD_Recv8(ep & 0xF); if (len && !UDD_FifoByteCount(ep & 0xF)) // release empty buffer UDD_ReleaseRX(ep & 0xF); return len; } // Recv 1 byte if ready uint32_t USBD_Recv(uint32_t ep) { uint8_t c; if (USBD_Recv(ep & 0xF, &c, 1) != 1) return -1; else return c; } // Space in send EP //uint32_t USBD_SendSpace(uint32_t ep) //{ //LockEP lock(ep); //// if (!UDD_ReadWriteAllowed(ep & 0xF)) ////{ ////printf("pb "); // UOTGHS->UOTGHS_DEVEPTISR[%d]=0x%X\n\r", ep, UOTGHS->UOTGHS_DEVEPTISR[ep]); ////return 0; ////} //if(ep==0) return 64 - UDD_FifoByteCount(ep & 0xF); // EP0_SIZE jcb //else return 512 - UDD_FifoByteCount(ep & 0xF); // EPX_SIZE jcb //} // Blocking Send of data to an endpoint uint32_t USBD_Send(uint32_t ep, const void* d, uint32_t len) { uint32_t n; int r = len; const uint8_t* data = (const uint8_t*)d; if (!_usbConfiguration) { TRACE_CORE(printf("pb conf\n\r");) return -1; } while (len) { if(ep==0) n = EP0_SIZE; else n = EPX_SIZE; if (n > len) n = len; len -= n; //USB PATCH to avoid dead loop int count=0; while( UOTGHS_DEVEPTISR_TXINI != (UOTGHS->UOTGHS_DEVEPTISR[ep & 0xF] & UOTGHS_DEVEPTISR_TXINI )) { count++; if (count>10000) return len; } UDD_Send(ep & 0xF, data, n); data += n; } //TXLED1; // light the TX LED //TxLEDPulse = TX_RX_LED_PULSE_MS; return r; } uint16_t _cmark; uint16_t _cend; void USBD_InitControl(int end) { _cmark = 0; _cend = end; } // Clipped by _cmark/_cend int USBD_SendControl(uint8_t flags __attribute__ ((unused)), const void* d, uint32_t len) { const uint8_t* data = (const uint8_t*)d; uint32_t length = len; uint32_t sent = 0; uint32_t pos = 0; TRACE_CORE(printf("=> USBD_SendControl TOTAL len=%lu\r\n", len);) if (_cmark < _cend) { while (len > 0) { sent = UDD_Send(EP0, data + pos, len); TRACE_CORE(printf("=> USBD_SendControl sent=%lu\r\n", sent);) pos += sent; len -= sent; } } _cmark += length; return length; } // Send a USB descriptor string. The string is stored as a // plain ASCII string but is sent out as UTF-16 with the // correct 2-byte prefix static bool USB_SendStringDescriptor(const uint8_t *string, int wLength) { if (wLength < 2) return false; uint8_t buffer[wLength]; buffer[0] = strlen((const char*)string) * 2 + 2; buffer[1] = 0x03; uint8_t i; for (i = 2; i < wLength && *string; i++) { buffer[i++] = *string++; if (i == wLength) break; buffer[i] = 0; } return USBD_SendControl(0, (uint8_t*)buffer, i); } // Does not timeout or cross fifo boundaries // Will only work for transfers <= 64 bytes // TODO int USBD_RecvControl(void* d, uint32_t len) { UDD_WaitOUT(); UDD_Recv(EP0, (uint8_t*)d, len); UDD_ClearOUT(); return len; } // Handle CLASS_INTERFACE requests bool USBD_ClassInterfaceRequest(USBSetup& setup) { uint8_t i = setup.wIndex; TRACE_CORE(printf("=> USBD_ClassInterfaceRequest\r\n");) #ifdef CDC_ENABLED if (CDC_ACM_INTERFACE == i) { return CDC_Setup(setup); } #endif #ifdef PLUGGABLE_USB_ENABLED return PluggableUSB().setup(setup); #endif return false; } uint8_t USBD_SendInterfaces(void) { uint8_t interfaces = 0; #ifdef CDC_ENABLED CDC_GetInterface(&interfaces); #endif #ifdef PLUGGABLE_USB_ENABLED PluggableUSB().getInterface(&interfaces); #endif TRACE_CORE(printf("=> USBD_SendInterfaces, interfaces=%d\r\n", interfaces);) return interfaces; } uint8_t USBD_SendOtherInterfaces(void) { uint8_t interfaces = 0; #ifdef CDC_ENABLED CDC_GetOtherInterface(&interfaces); #endif #ifdef PLUGGABLE_USB_ENABLED PluggableUSB().getInterface(&interfaces); #endif TRACE_CORE(printf("=> USBD_SendInterfaces, interfaces=%d\r\n", interfaces);) return interfaces; } // Construct a dynamic configuration descriptor // This really needs dynamic endpoint allocation etc // TODO static bool USBD_SendConfiguration(int maxlen) { // Count and measure interfaces USBD_InitControl(0); //TRACE_CORE(printf("=> USBD_SendConfiguration _cmark1=%d\r\n", _cmark);) uint8_t interfaces = USBD_SendInterfaces(); //TRACE_CORE(printf("=> USBD_SendConfiguration _cmark2=%d\r\n", _cmark);) //TRACE_CORE(printf("=> USBD_SendConfiguration sizeof=%d\r\n", sizeof(ConfigDescriptor));) _Pragma("pack(1)") ConfigDescriptor config = D_CONFIG(_cmark + sizeof(ConfigDescriptor),interfaces); _Pragma("pack()") //TRACE_CORE(printf("=> USBD_SendConfiguration clen=%d\r\n", config.clen);) //TRACE_CORE(printf("=> USBD_SendConfiguration maxlen=%d\r\n", maxlen);) // Now send them USBD_InitControl(maxlen); USBD_SendControl(0,&config,sizeof(ConfigDescriptor)); USBD_SendInterfaces(); return true; } static bool USBD_SendOtherConfiguration(int maxlen) { // Count and measure interfaces USBD_InitControl(0); //TRACE_CORE(printf("=> USBD_SendConfiguration _cmark1=%d\r\n", _cmark);) uint8_t interfaces = USBD_SendOtherInterfaces(); //TRACE_CORE(printf("=> USBD_SendConfiguration _cmark2=%d\r\n", _cmark);) //TRACE_CORE(printf("=> USBD_SendConfiguration sizeof=%d\r\n", sizeof(ConfigDescriptor));) _Pragma("pack(1)") ConfigDescriptor config = D_OTHERCONFIG(_cmark + sizeof(ConfigDescriptor),interfaces); _Pragma("pack()") //TRACE_CORE(printf("=> USBD_SendConfiguration clen=%d\r\n", config.clen);) //TRACE_CORE(printf("=> USBD_SendConfiguration maxlen=%d\r\n", maxlen);) // Now send them USBD_InitControl(maxlen); USBD_SendControl(0,&config,sizeof(ConfigDescriptor)); USBD_SendOtherInterfaces(); return true; } static bool USBD_SendDescriptor(USBSetup& setup) { uint8_t t = setup.wValueH; uint8_t desc_length = 0; int ret = 0; const uint8_t* desc_addr = 0; if (USB_CONFIGURATION_DESCRIPTOR_TYPE == t) { TRACE_CORE(printf("=> USBD_SendDescriptor : USB_CONFIGURATION_DESCRIPTOR_TYPE length=%d\r\n", setup.wLength);) return USBD_SendConfiguration(setup.wLength); } USBD_InitControl(setup.wLength); #ifdef PLUGGABLE_USB_ENABLED ret = PluggableUSB().getDescriptor(setup); if (ret != 0) { return (ret > 0 ? true : false); } #endif if (USB_DEVICE_DESCRIPTOR_TYPE == t) { TRACE_CORE(puts("=> USBD_SendDescriptor : USB_DEVICE_DESCRIPTOR_TYPE\r\n");) if (setup.wLength == 8) { _cdcComposite = 1; } desc_addr = _cdcComposite ? (const uint8_t*)&USB_DeviceDescriptorA : (const uint8_t*)&USB_DeviceDescriptor; if( *desc_addr > setup.wLength ) { desc_length = setup.wLength; } } else if (USB_STRING_DESCRIPTOR_TYPE == t) { TRACE_CORE(puts("=> USBD_SendDescriptor : USB_STRING_DESCRIPTOR_TYPE\r\n");) if (setup.wValueL == 0) { desc_addr = (const uint8_t*)&STRING_LANGUAGE; } else if (setup.wValueL == IPRODUCT) { return USB_SendStringDescriptor(STRING_PRODUCT, setup.wLength); } else if (setup.wValueL == IMANUFACTURER) { return USB_SendStringDescriptor(STRING_MANUFACTURER, setup.wLength); } else if (setup.wValueL == ISERIAL) { #ifdef PLUGGABLE_USB_ENABLED char name[ISERIAL_MAX_LEN]; PluggableUSB().getShortName(name); return USB_SendStringDescriptor((uint8_t*)name, setup.wLength); #endif } else { return false; } if( *desc_addr > setup.wLength ) { desc_length = setup.wLength; } } else if (USB_DEVICE_QUALIFIER == t) { // Device qualifier descriptor requested desc_addr = (const uint8_t*)&USB_DeviceQualifier; if( *desc_addr > setup.wLength ) { desc_length = setup.wLength; } } else if (USB_OTHER_SPEED_CONFIGURATION == t) { // Other configuration descriptor requested return USBD_SendOtherConfiguration(setup.wLength); } else { //printf("Device ERROR"); } if (desc_addr == 0) { return false; } if (desc_length == 0) { desc_length = *desc_addr; } TRACE_CORE(printf("=> USBD_SendDescriptor : desc_addr=%p desc_length=%d\r\n", desc_addr, desc_length);) USBD_SendControl(0, desc_addr, desc_length); return true; } static void USB_SendZlp( void ) { while( UOTGHS_DEVEPTISR_TXINI != (UOTGHS->UOTGHS_DEVEPTISR[0] & UOTGHS_DEVEPTISR_TXINI ) ) { if((UOTGHS->UOTGHS_DEVISR & UOTGHS_DEVISR_SUSP) == UOTGHS_DEVISR_SUSP) { return; } } UOTGHS->UOTGHS_DEVEPTICR[0] = UOTGHS_DEVEPTICR_TXINIC; } static void Test_Mode_Support( uint8_t wIndex ) { uint8_t i; uint8_t *ptr_dest = (uint8_t *) &udd_get_endpoint_fifo_access8(2); switch( wIndex ) { case 4: //Test mode Test_Packet: //Upon command, a port must repetitively transmit the following test packet until //the exit action is taken. This enables the testing of rise and fall times, eye //patterns, jitter, and any other dynamic waveform specifications. //The test packet is made up by concatenating the following strings. //(Note: For J/K NRZI data, and for NRZ data, the bit on the left is the first one //transmitted. "S" indicates that a bit stuff occurs, which inserts an "extra" NRZI data bit. //"* N" is used to indicate N occurrences of a string of bits or symbols.) //A port in Test_Packet mode must send this packet repetitively. The inter-packet timing //must be no less than the minimum allowable inter-packet gap as defined in Section 7.1.18 and //no greater than 125 us. // Send ZLP USB_SendZlp(); UOTGHS->UOTGHS_DEVDMA[0].UOTGHS_DEVDMACONTROL = 0; // raz UOTGHS->UOTGHS_DEVDMA[1].UOTGHS_DEVDMACONTROL = 0; // raz // Configure endpoint 2, 64 bytes, direction IN, type BULK, 1 bank UOTGHS->UOTGHS_DEVEPTCFG[2] = UOTGHS_DEVEPTCFG_EPSIZE_64_BYTE | UOTGHS_DEVEPTCFG_EPDIR_IN | UOTGHS_DEVEPTCFG_EPTYPE_BLK | UOTGHS_DEVEPTCFG_EPBK_1_BANK; // Check if the configuration is ok UOTGHS->UOTGHS_DEVEPTCFG[2] |= UOTGHS_DEVEPTCFG_ALLOC; while((UOTGHS->UOTGHS_DEVEPTISR[2]&UOTGHS_DEVEPTISR_CFGOK)==0) {} UOTGHS->UOTGHS_DEVEPT |= UOTGHS_DEVEPT_EPEN2; // Write FIFO for( i=0; iUOTGHS_DEVCTRL |= UOTGHS_DEVCTRL_TSTPCKT; // Send packet UOTGHS->UOTGHS_DEVEPTICR[2] = UOTGHS_DEVEPTICR_TXINIC; UOTGHS->UOTGHS_DEVEPTIDR[2] = UOTGHS_DEVEPTIDR_FIFOCONC; for(;;); // break; case 1: //Test mode Test_J: //Upon command, a port's transceiver must enter the high-speed J state and remain in that //state until the exit action is taken. This enables the testing of the high output drive //level on the D+ line. // Send a ZLP USB_SendZlp(); UOTGHS->UOTGHS_DEVCTRL |= UOTGHS_DEVCTRL_TSTJ; for(;;); // break; case 2: //Test mode Test_K: //Upon command, a port's transceiver must enter the high-speed K state and remain in //that state until the exit action is taken. This enables the testing of the high output drive //level on the D- line. // Send a ZLP USB_SendZlp(); UOTGHS->UOTGHS_DEVCTRL |= UOTGHS_DEVCTRL_TSTK; for(;;); // break; case 3: //Test mode Test_SE0_NAK: //Upon command, a port's transceiver must enter the high-speed receive mode //and remain in that mode until the exit action is taken. This enables the testing //of output impedance, low level output voltage, and loading characteristics. //In addition, while in this mode, upstream facing ports (and only upstream facing ports) //must respond to any IN token packet with a NAK handshake (only if the packet CRC is //determined to be correct) within the normal allowed device response time. This enables testing of //the device squelch level circuitry and, additionally, provides a general purpose stimulus/response //test for basic functional testing. // Send a ZLP USB_SendZlp(); UOTGHS->UOTGHS_DEVIDR = UOTGHS_DEVIDR_SUSPEC | UOTGHS_DEVIDR_MSOFEC | UOTGHS_DEVIDR_SOFEC | UOTGHS_DEVIDR_EORSTEC | UOTGHS_DEVIDR_WAKEUPEC | UOTGHS_DEVIDR_EORSMEC | UOTGHS_DEVIDR_UPRSMEC | UOTGHS_DEVIDR_PEP_0 | UOTGHS_DEVIDR_PEP_1 | UOTGHS_DEVIDR_PEP_2 | UOTGHS_DEVIDR_PEP_3 | UOTGHS_DEVIDR_PEP_4 | UOTGHS_DEVIDR_PEP_5 | UOTGHS_DEVIDR_PEP_6 | UOTGHS_DEVIDR_DMA_1 | UOTGHS_DEVIDR_DMA_2 | UOTGHS_DEVIDR_DMA_3 | UOTGHS_DEVIDR_DMA_4 | UOTGHS_DEVIDR_DMA_5 | UOTGHS_DEVIDR_DMA_6; for(;;); // break; } } //unsigned int iii=0; // Endpoint 0 interrupt static void USB_ISR(void) { // printf("ISR=0x%X\n\r", UOTGHS->UOTGHS_DEVISR); // jcb // if( iii++ > 1500 ) while(1); // jcb // End of bus reset if (Is_udd_reset()) { TRACE_CORE(printf(">>> End of Reset\r\n");) // Reset USB address to 0 udd_configure_address(0); udd_enable_address(); // Configure EP 0 UDD_InitEP(0, EP_TYPE_CONTROL); udd_enable_setup_received_interrupt(0); udd_enable_endpoint_interrupt(0); _usbConfiguration = 0; udd_ack_reset(); } #ifdef CDC_ENABLED if (Is_udd_endpoint_interrupt(CDC_RX)) { udd_ack_out_received(CDC_RX); // Handle received bytes if (USBD_Available(CDC_RX)) SerialUSB.accept(); } if (Is_udd_sof()) { udd_ack_sof(); // USBD_Flush(CDC_TX); // jcb } #endif // EP 0 Interrupt if (Is_udd_endpoint_interrupt(0) ) { if (!UDD_ReceivedSetupInt()) { return; } USBSetup setup; UDD_Recv(EP0, (uint8_t*)&setup, 8); UDD_ClearSetupInt(); uint8_t requestType = setup.bmRequestType; if (requestType & REQUEST_DEVICETOHOST) { TRACE_CORE(puts(">>> EP0 Int: IN Request\r\n");) UDD_WaitIN(); } else { TRACE_CORE(puts(">>> EP0 Int: OUT Request\r\n");) UDD_ClearIN(); } bool ok = true; if (REQUEST_STANDARD == (requestType & REQUEST_TYPE)) { // Standard Requests uint8_t r = setup.bRequest; if (GET_STATUS == r) { if( setup.bmRequestType == 0 ) // device { // Send the device status TRACE_CORE(puts(">>> EP0 Int: GET_STATUS\r\n");) // Check current configuration for power mode (if device is configured) // TODO // Check if remote wake-up is enabled // TODO UDD_Send8(EP0, 0); // TODO UDD_Send8(EP0, 0); } // if( setup.bmRequestType == 2 ) // Endpoint: else { // Send the endpoint status // Check if the endpoint if currently halted if( isEndpointHalt == 1 ) UDD_Send8(EP0, 1); // TODO else UDD_Send8(EP0, 0); // TODO UDD_Send8(EP0, 0); } } else if (CLEAR_FEATURE == r) { // Check which is the selected feature if( setup.wValueL == 1) // DEVICEREMOTEWAKEUP { // Enable remote wake-up and send a ZLP if( isRemoteWakeUpEnabled == 1 ) UDD_Send8(EP0, 1); else UDD_Send8(EP0, 0); UDD_Send8(EP0, 0); } else // if( setup.wValueL == 0) // ENDPOINTHALT { isEndpointHalt = 0; // TODO UDD_Send8(EP0, 0); UDD_Send8(EP0, 0); } } else if (SET_FEATURE == r) { // Check which is the selected feature if( setup.wValueL == 1) // DEVICEREMOTEWAKEUP { // Enable remote wake-up and send a ZLP isRemoteWakeUpEnabled = 1; UDD_Send8(EP0, 0); } if( setup.wValueL == 0) // ENDPOINTHALT { // Halt endpoint isEndpointHalt = 1; //USBD_Halt(USBGenericRequest_GetEndpointNumber(pRequest)); UDD_Send8(EP0, 0); } if( setup.wValueL == 2) // TEST_MODE { // 7.1.20 Test Mode Support, 9.4.9 SetFeature if( (setup.bmRequestType == 0 /*USBGenericRequest_DEVICE*/) && ((setup.wIndex & 0x000F) == 0) ) { // the lower byte of wIndex must be zero // the most significant byte of wIndex is used to specify the specific test mode UOTGHS->UOTGHS_DEVIDR &= ~UOTGHS_DEVIDR_SUSPEC; UOTGHS->UOTGHS_DEVCTRL |= UOTGHS_DEVCTRL_SPDCONF_HIGH_SPEED; // remove suspend ? Test_Mode_Support( (setup.wIndex & 0xFF00)>>8 ); } } } else if (SET_ADDRESS == r) { TRACE_CORE(puts(">>> EP0 Int: SET_ADDRESS\r\n");) UDD_WaitIN(); UDD_SetAddress(setup.wValueL); } else if (GET_DESCRIPTOR == r) { TRACE_CORE(puts(">>> EP0 Int: GET_DESCRIPTOR\r\n");) ok = USBD_SendDescriptor(setup); } else if (SET_DESCRIPTOR == r) { TRACE_CORE(puts(">>> EP0 Int: SET_DESCRIPTOR\r\n");) ok = false; } else if (GET_CONFIGURATION == r) { TRACE_CORE(puts(">>> EP0 Int: GET_CONFIGURATION\r\n");) UDD_Send8(EP0, _usbConfiguration); } else if (SET_CONFIGURATION == r) { if (REQUEST_DEVICE == (requestType & REQUEST_RECIPIENT)) { TRACE_CORE(printf(">>> EP0 Int: SET_CONFIGURATION REQUEST_DEVICE %d\r\n", setup.wValueL);) uint32_t num_endpoints = 0; while (EndPoints[num_endpoints] != 0) { num_endpoints++; } UDD_InitEndpoints(EndPoints, num_endpoints); _usbConfiguration = setup.wValueL; #ifdef CDC_ENABLED // Enable interrupt for CDC reception from host (OUT packet) udd_enable_out_received_interrupt(CDC_RX); udd_enable_endpoint_interrupt(CDC_RX); #endif } else { TRACE_CORE(puts(">>> EP0 Int: SET_CONFIGURATION failed!\r\n");) ok = false; } } else if (GET_INTERFACE == r) { TRACE_CORE(puts(">>> EP0 Int: GET_INTERFACE\r\n");) UDD_Send8(EP0, _usbSetInterface); } else if (SET_INTERFACE == r) { _usbSetInterface = setup.wValueL; TRACE_CORE(puts(">>> EP0 Int: SET_INTERFACE\r\n");) } } else { TRACE_CORE(puts(">>> EP0 Int: ClassInterfaceRequest\r\n");) UDD_WaitIN(); // Workaround: need tempo here, else CDC serial won't open correctly USBD_InitControl(setup.wLength); // Max length of transfer ok = USBD_ClassInterfaceRequest(setup); } if (ok) { TRACE_CORE(puts(">>> EP0 Int: Send packet\r\n");) UDD_ClearIN(); } else { TRACE_CORE(puts(">>> EP0 Int: Stall\r\n");) UDD_Stall(); } } } void USBD_Flush(uint32_t ep) { if (UDD_FifoByteCount(ep)) UDD_ReleaseTX(ep); } // VBUS or counting frames // Any frame counting? uint32_t USBD_Connected(void) { uint8_t f = UDD_GetFrameNumber(); delay(3); return f != UDD_GetFrameNumber(); } //======================================================================= //======================================================================= USBDevice_ USBDevice; USBDevice_::USBDevice_() { UDD_SetStack(&USB_ISR); if (UDD_Init() == 0UL) { _usbInitialized=1UL; } } bool USBDevice_::attach(void) { if (_usbInitialized != 0UL) { UDD_Attach(); _usbConfiguration = 0; return true; } else { return false; } } bool USBDevice_::detach(void) { if (_usbInitialized != 0UL) { UDD_Detach(); return true; } else { return false; } } // Check for interrupts // TODO: VBUS detection bool USBDevice_::configured() { return _usbConfiguration; } void USBDevice_::poll() { }