// Non Blocking receive // Return number of bytes read uint32_t USBDeviceClass::recv(uint32_t ep, void *_data, uint32_t len) { if (!_usbConfiguration) return -1; if (available(ep) < len) len = available(ep); #ifdef PIN_LED_RXL digitalWrite(PIN_LED_RXL, LOW); rxLEDPulse = TX_RX_LED_PULSE_MS; #endif armRecv(ep); usbd.epBank0DisableTransferComplete(ep); memcpy(_data, udd_ep_out_cache_buffer[ep], len); // release empty buffer if (len && !available(ep)) { // The RAM Buffer is empty: we can receive data usbd.epBank0ResetReady(ep); // Clear Transfer complete 0 flag usbd.epBank0AckTransferComplete(ep); } return len; }
// Non Blocking receive // Return number of bytes read uint32_t USBDeviceClass::recv(uint32_t ep, void *_data, uint32_t len) { if (!_usbConfiguration) return -1; if (available(ep) < len) len = available(ep); armRecv(ep); usbd.epBank0DisableTransferComplete(ep); memcpy(_data, udd_ep_out_cache_buffer[ep], len); // release empty buffer if (len && !available(ep)) { // The RAM Buffer is empty: we can receive data usbd.epBank0ResetReady(ep); // Clear Transfer complete 0 flag usbd.epBank0AckTransferComplete(ep); } return len; }
void USBDeviceClass::handleEndpoint(uint8_t ep) { #if defined(CDC_ENABLED) if (ep == CDC_ENDPOINT_OUT) { // The RAM Buffer is empty: we can receive data //usbd.epBank0ResetReady(CDC_ENDPOINT_OUT); // Handle received bytes if (available(CDC_ENDPOINT_OUT)) SerialUSB.accept(); } if (ep == CDC_ENDPOINT_IN) { // NAK on endpoint IN, the bank is not yet filled in. usbd.epBank1ResetReady(CDC_ENDPOINT_IN); usbd.epBank1AckTransferComplete(CDC_ENDPOINT_IN); } if (ep == CDC_ENDPOINT_ACM) { // NAK on endpoint IN, the bank is not yet filled in. usbd.epBank1ResetReady(CDC_ENDPOINT_ACM); usbd.epBank1AckTransferComplete(CDC_ENDPOINT_ACM); } #endif #if defined(PLUGGABLE_USB_ENABLED) // Empty #endif }
uint8_t USBDeviceClass::armRecv(uint32_t ep) { uint16_t count = usbd.epBank0ByteCount(ep); if (count >= 64) { usbd.epBank0SetByteCount(ep, count - 64); } else { usbd.epBank0SetByteCount(ep, 0); } return usbd.epBank0ByteCount(ep); }
void USBDeviceClass::flush(uint32_t ep) { if (available(ep)) { // RAM buffer is full, we can send data (IN) usbd.epBank1SetReady(ep); // Clear the transfer complete flag usbd.epBank1AckTransferComplete(ep); } }
uint32_t USBDeviceClass::armSend(uint32_t ep, const void* data, uint32_t len) { memcpy(&udd_ep_in_cache_buffer[ep], data, len); // Get endpoint configuration from setting register usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[ep]); usbd.epBank1SetMultiPacketSize(ep, 0); usbd.epBank1SetByteCount(ep, len); return len; }
bool USBDeviceClass::attach() { if (!initialized) return false; usbd.attach(); usbd.enableEndOfResetInterrupt(); usbd.enableStartOfFrameInterrupt(); _usbConfiguration = 0; return true; }
void USBDeviceClass::setAddress(uint32_t addr) { usbd.epBank1SetByteCount(0, 0); usbd.epBank1AckTransferComplete(0); // RAM buffer is full, we can send data (IN) usbd.epBank1SetReady(0); // Wait for transfer to complete while (!usbd.epBank1IsTransferComplete(0)) {} // Set USB address to addr USB->DEVICE.DADD.bit.DADD = addr; // Address USB->DEVICE.DADD.bit.ADDEN = 1; // Enable }
bool USBDeviceClass::detach() { if (!initialized) return false; usbd.detach(); return true; }
uint8_t USBDeviceClass::armRecvCtrlOUT(uint32_t ep) { // Get endpoint configuration from setting register usbd.epBank0SetAddress(ep, &udd_ep_out_cache_buffer[ep]); usbd.epBank0SetMultiPacketSize(ep, 8); usbd.epBank0SetByteCount(ep, 0); usbd.epBank0ResetReady(ep); // Wait OUT while (!usbd.epBank0IsReady(ep)) {} while (!usbd.epBank0IsTransferComplete(ep)) {} return usbd.epBank0ByteCount(ep); }
uint32_t USBDeviceClass::recvControl(void *_data, uint32_t len) { uint8_t *data = reinterpret_cast<uint8_t *>(_data); // The RAM Buffer is empty: we can receive data usbd.epBank0ResetReady(0); //usbd.epBank0AckSetupReceived(0); uint32_t read = armRecvCtrlOUT(0); if (read > len) read = len; //while (!usbd.epBank0AckTransferComplete(0)) {} uint8_t *buffer = udd_ep_out_cache_buffer[0]; for (uint32_t i=0; i<len; i++) { data[i] = buffer[i]; } return read; }
void USBDeviceClass::init() { #ifdef PIN_LED_TXL txLEDPulse = 0; pinMode(PIN_LED_TXL, OUTPUT); digitalWrite(PIN_LED_TXL, HIGH); #endif #ifdef PIN_LED_RXL rxLEDPulse = 0; pinMode(PIN_LED_RXL, OUTPUT); digitalWrite(PIN_LED_RXL, HIGH); #endif // Enable USB clock PM->APBBMASK.reg |= PM_APBBMASK_USB; // Set up the USB DP/DN pins PORT->Group[0].PINCFG[PIN_PA24G_USB_DM].bit.PMUXEN = 1; PORT->Group[0].PMUX[PIN_PA24G_USB_DM/2].reg &= ~(0xF << (4 * (PIN_PA24G_USB_DM & 0x01u))); PORT->Group[0].PMUX[PIN_PA24G_USB_DM/2].reg |= MUX_PA24G_USB_DM << (4 * (PIN_PA24G_USB_DM & 0x01u)); PORT->Group[0].PINCFG[PIN_PA25G_USB_DP].bit.PMUXEN = 1; PORT->Group[0].PMUX[PIN_PA25G_USB_DP/2].reg &= ~(0xF << (4 * (PIN_PA25G_USB_DP & 0x01u))); PORT->Group[0].PMUX[PIN_PA25G_USB_DP/2].reg |= MUX_PA25G_USB_DP << (4 * (PIN_PA25G_USB_DP & 0x01u)); // Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference) GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(6) | // Generic Clock Multiplexer 6 GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source GCLK_CLKCTRL_CLKEN; while (GCLK->STATUS.bit.SYNCBUSY) ; USB_SetHandler(&UDD_Handler); // Reset USB Device usbd.reset(); usbd.calibrate(); usbd.setUSBDeviceMode(); usbd.runInStandby(); usbd.setFullSpeed(); // Configure interrupts NVIC_SetPriority((IRQn_Type) USB_IRQn, 0UL); NVIC_EnableIRQ((IRQn_Type) USB_IRQn); usbd.enable(); initialized = true; }
void USBDeviceClass::sendZlp(uint32_t ep) { // Set the byte count as zero usbd.epBank1SetByteCount(ep, 0); }
// Blocking Send of data to an endpoint uint32_t USBDeviceClass::send(uint32_t ep, const void *data, uint32_t len) { uint32_t length = 0; if (!_usbConfiguration) return -1; if (len > 16384) return -1; #if 0 // This shortcut has some issues: // - sometimes it fails when sending an odd number of bytes (may be // due to memory alignment?) // - the data pointer should point to "stable" data (and this is not // guaranteed by caller, it may be some sort of temporary buffer) // - the SRAM is not guaranteed to start at 0x20000000 // All the above problems must be properly fixed before reenabling // this part if ((unsigned int)data > 0x20000000) { // Buffer in RAM usbd.epBank1SetAddress(ep, (void *)data); usbd.epBank1SetMultiPacketSize(ep, 0); usbd.epBank1SetByteCount(ep, len); // Clear the transfer complete flag usbd.epBank1AckTransferComplete(ep); // RAM buffer is full, we can send data (IN) usbd.epBank1SetReady(ep); // Wait for transfer to complete while (!usbd.epBank1IsTransferComplete(ep)) { ; // need fire exit. } return 0; } #endif // Flash area while (len != 0) { if (len >= 64) { length = 64; } else { length = len; } /* memcopy could be safer in multi threaded environment */ memcpy(&udd_ep_in_cache_buffer[ep], data, length); usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[ep]); usbd.epBank1SetByteCount(ep, length); // Clear the transfer complete flag usbd.epBank1AckTransferComplete(ep); // RAM buffer is full, we can send data (IN) usbd.epBank1SetReady(ep); // Wait for transfer to complete while (!usbd.epBank1IsTransferComplete(ep)) { ; // need fire exit. } len -= length; data += length; } return len; }
void USBDeviceClass::initEP(uint32_t ep, uint32_t config) { if (config == (USB_ENDPOINT_TYPE_INTERRUPT | USB_ENDPOINT_IN(0))) { usbd.epBank1SetSize(ep, 64); usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[ep]); usbd.epBank1SetType(ep, 4); // INTERRUPT IN } else if (config == (USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_OUT(0))) { usbd.epBank0SetSize(ep, 64); usbd.epBank0SetAddress(ep, &udd_ep_out_cache_buffer[ep]); usbd.epBank0SetType(ep, 3); // BULK OUT // Release OUT EP usbd.epBank0SetMultiPacketSize(ep, 64); usbd.epBank0SetByteCount(ep, 0); } else if (config == (USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN(0))) { usbd.epBank1SetSize(ep, 64); usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[ep]); // NAK on endpoint IN, the bank is not yet filled in. usbd.epBank1ResetReady(ep); usbd.epBank1SetType(ep, 3); // BULK IN } else if (config == USB_ENDPOINT_TYPE_CONTROL) { // XXX: Needed? // usbd.epBank0DisableAutoZLP(ep); // usbd.epBank1DisableAutoZLP(ep); // Setup Control OUT usbd.epBank0SetSize(ep, 64); usbd.epBank0SetAddress(ep, &udd_ep_out_cache_buffer[ep]); usbd.epBank0SetType(ep, 1); // CONTROL OUT / SETUP // Setup Control IN usbd.epBank1SetSize(ep, 64); usbd.epBank1SetAddress(ep, &udd_ep_in_cache_buffer[0]); usbd.epBank1SetType(ep, 1); // CONTROL IN // Release OUT EP usbd.epBank0SetMultiPacketSize(ep, 64); usbd.epBank0SetByteCount(ep, 0); // NAK on endpoint OUT, the bank is full. usbd.epBank0SetReady(ep); } }
bool USBDeviceClass::handleStandardSetup(USBSetup &setup) { switch (setup.bRequest) { case GET_STATUS: if (setup.bmRequestType == 0) // device { // Send the device status // TODO: Check current configuration for power mode (if device is configured) // TODO: Check if remote wake-up is enabled uint8_t buff[] = { 0, 0 }; armSend(0, buff, 2); return true; } // if( setup.bmRequestType == 2 ) // Endpoint: else { // Send the endpoint status // Check if the endpoint if currently halted uint8_t buff[] = { 0, 0 }; if (isEndpointHalt == 1) buff[0] = 1; armSend(0, buff, 2); return true; } case CLEAR_FEATURE: // Check which is the selected feature if (setup.wValueL == 1) // DEVICEREMOTEWAKEUP { // Enable remote wake-up and send a ZLP uint8_t buff[] = { 0, 0 }; if (isRemoteWakeUpEnabled == 1) buff[0] = 1; armSend(0, buff, 2); return true; } else // if( setup.wValueL == 0) // ENDPOINTHALT { isEndpointHalt = 0; sendZlp(0); return true; } case SET_FEATURE: // Check which is the selected feature if (setup.wValueL == 1) // DEVICEREMOTEWAKEUP { // Enable remote wake-up and send a ZLP isRemoteWakeUpEnabled = 1; uint8_t buff[] = { 0 }; armSend(0, buff, 1); return true; } if (setup.wValueL == 0) // ENDPOINTHALT { // Halt endpoint isEndpointHalt = 1; sendZlp(0); return true; } case SET_ADDRESS: setAddress(setup.wValueL); return true; case GET_DESCRIPTOR: return sendDescriptor(setup); case SET_DESCRIPTOR: return false; case GET_CONFIGURATION: armSend(0, (void*)&_usbConfiguration, 1); return true; case SET_CONFIGURATION: if (REQUEST_DEVICE == (setup.bmRequestType & REQUEST_RECIPIENT)) { initEndpoints(); _usbConfiguration = setup.wValueL; #if defined(CDC_ENABLED) // Enable interrupt for CDC reception from host (OUT packet) usbd.epBank1EnableTransferComplete(CDC_ENDPOINT_ACM); usbd.epBank0EnableTransferComplete(CDC_ENDPOINT_OUT); #endif sendZlp(0); return true; } else { return false; } case GET_INTERFACE: armSend(0, (void*)&_usbSetInterface, 1); return true; case SET_INTERFACE: _usbSetInterface = setup.wValueL; sendZlp(0); return true; default: return true; } }
void USBDeviceClass::ISRHandler() { if (_pack_message == true) { return; } // End-Of-Reset if (usbd.isEndOfResetInterrupt()) { // Configure EP 0 initEP(0, USB_ENDPOINT_TYPE_CONTROL); // Enable Setup-Received interrupt usbd.epBank0EnableSetupReceived(0); _usbConfiguration = 0; usbd.ackEndOfResetInterrupt(); } // Start-Of-Frame if (usbd.isStartOfFrameInterrupt()) { usbd.ackStartOfFrameInterrupt(); } // Endpoint 0 Received Setup interrupt if (usbd.epBank0IsSetupReceived(0)) { usbd.epBank0AckSetupReceived(0); USBSetup *setup = reinterpret_cast<USBSetup *>(udd_ep_out_cache_buffer[0]); /* Clear the Bank 0 ready flag on Control OUT */ // The RAM Buffer is empty: we can receive data usbd.epBank0ResetReady(0); bool ok; if (REQUEST_STANDARD == (setup->bmRequestType & REQUEST_TYPE)) { // Standard Requests ok = handleStandardSetup(*setup); } else { // Class Interface Requests ok = handleClassInterfaceSetup(*setup); } if (ok) { usbd.epBank1SetReady(0); } else { stall(0); } if (usbd.epBank1IsStalled(0)) { usbd.epBank1AckStalled(0); // Remove stall request usbd.epBank1DisableStalled(0); } } // end Received Setup handler uint8_t i=0; uint8_t ept_int = usbd.epInterruptSummary() & 0xFE; // Remove endpoint number 0 (setup) while (ept_int != 0) { // Check if endpoint has a pending interrupt if ((ept_int & (1 << i)) != 0) { // Endpoint Transfer Complete (0/1) Interrupt if (usbd.epBank0IsTransferComplete(i) || usbd.epBank1IsTransferComplete(i)) { handleEndpoint(i); } ept_int &= ~(1 << i); } i++; if (i > USB_EPT_NUM) break; // fire exit } }
// Number of bytes, assumes a rx endpoint uint32_t USBDeviceClass::available(uint32_t ep) { return usbd.epBank0ByteCount(ep); }