diff --git a/hal/m258ke/descriptors.c b/hal/m258ke/descriptors.c new file mode 100644 index 0000000..97cf473 --- /dev/null +++ b/hal/m258ke/descriptors.c @@ -0,0 +1,194 @@ +/****************************************************************************//** + * @file descriptors.c + * @version V0.10 + * @brief USBD descriptors + * + * SPDX-License-Identifier: Apache-2.0 + * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. + ******************************************************************************/ + +#include "usbcdc_hw.h" + +/// VID and PID of our device +#define USB_VID 0x16D0 +#define USB_PID 0x06AA + +/// Device descriptor +const uint8_t deviceDescriptor[] = { + LEN_DEVICE, // bLength + DESC_DEVICE, // bDescriptorType + 0x10, 0x01, // bcdUSB + 0x02, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + EP0_MAX_PKT_SIZE, // bMaxPacketSize0 + // idVendor + USB_VID & 0x00FF, (USB_VID & 0xFF00) >> 8, + // idProduct + USB_PID & 0x00FF, (USB_PID & 0xFF00) >> 8, + 0x02, 0x00, // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x03, // iSerialNumber + 0x01 // bNumConfigurations +}; + +/// Config descriptor (includes interface, endpoint descriptors) +const uint8_t configDescriptor[] = +{ + LEN_CONFIG, // bLength + DESC_CONFIG, // bDescriptorType + 0x3E, 0x00, // wTotalLength + 0x02, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0xC0, // bmAttributes + 0xFA, // MaxPower + + // Interface descriptor: communication class interface + LEN_INTERFACE, // bLength + DESC_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x01, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0x00, // iInterface + + // Header functional descriptor + 0x05, // Size of the descriptor, in bytes + 0x24, // CS_INTERFACE descriptor type + 0x00, // Header functional descriptor subtype + 0x10, 0x01, // Communication device compliant to the communication spec. ver. 1.10 + + // Abstract control management functional descriptor + 0x04, // Size of the descriptor, in bytes + 0x24, // CS_INTERFACE descriptor type + 0x02, // Abstract control management functional descriptor subtype + 0x06, // bmCapabilities + + // Union functional descriptor + 0x05, // bLength + 0x24, // bDescriptorType: CS_INTERFACE descriptor type + 0x06, // bDescriptorSubType + 0x00, // bMasterInterface + 0x01, // bSlaveInterface0 + + // Endpoint descriptor + LEN_ENDPOINT, // bLength + DESC_ENDPOINT, // bDescriptorType + (EP_INPUT | INT_IN_EP_NUM), // bEndpointAddress + EP_INT, // bmAttributes + EP2_MAX_PKT_SIZE, 0x00, // wMaxPacketSize + 0xFF, // bInterval + + // Interface descriptor: data class interface + LEN_INTERFACE, // bLength + DESC_INTERFACE, // bDescriptorType + 0x01, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + + // Endpoint descriptor + LEN_ENDPOINT, // bLength + DESC_ENDPOINT, // bDescriptorType + (EP_OUTPUT | BULK_OUT_EP_NUM), // bEndpointAddress + EP_BULK, // bmAttributes + EP4_MAX_PKT_SIZE, 0x00, // wMaxPacketSize + 0x01, // bInterval + + // Endpoint descriptor + LEN_ENDPOINT, // bLength + DESC_ENDPOINT, // bDescriptorType + (EP_INPUT | BULK_IN_EP_NUM), // bEndpointAddress + EP_BULK, // bmAttributes + EP3_MAX_PKT_SIZE, 0x00, // wMaxPacketSize + 0x01, // bInterval +}; + + +/// Language descriptor +const uint8_t languageStringDescriptor[4] = +{ + 4, // bLength + DESC_STRING, // bDescriptorType + 0x09, 0x04 // English (United States) +}; + +/// Vendor string descriptor +const uint8_t vendorStringDescriptor[] = +{ + 22, + DESC_STRING, + 'D', 0, + 'o', 0, + 'u', 0, + 'g', 0, + ' ', 0, + 'B', 0, + 'r', 0, + 'o', 0, + 'w', 0, + 'n', 0, +}; + +/// Product string descriptor +const uint8_t productStringDescriptor[] = +{ + 48, // bLength + DESC_STRING, // bDescriptorType + 'M', 0, + 'a', 0, + 'c', 0, + ' ', 0, + 'R', 0, + 'O', 0, + 'M', 0, + ' ', 0, + 'S', 0, + 'I', 0, + 'M', 0, + 'M', 0, + ' ', 0, + 'P', 0, + 'r', 0, + 'o', 0, + 'g', 0, + 'r', 0, + 'a', 0, + 'm', 0, + 'm', 0, + 'e', 0, + 'r', 0, +}; + +/// Serial number string descriptor +const uint8_t serialStringDescriptor[] = +{ + 4, // bLength + DESC_STRING, // bDescriptorType + '0', 0, +}; + +/// Array of string descriptors +const uint8_t * const stringDescriptors[4] = +{ + languageStringDescriptor, + vendorStringDescriptor, + productStringDescriptor, + serialStringDescriptor +}; + +/// Descriptor info used by usbd.c +const S_USBD_INFO_T gsInfo = +{ + deviceDescriptor, + configDescriptor, + stringDescriptors, +}; + diff --git a/hal/m258ke/usbcdc.c b/hal/m258ke/usbcdc.c new file mode 100644 index 0000000..3a4a211 --- /dev/null +++ b/hal/m258ke/usbcdc.c @@ -0,0 +1,468 @@ +/* + * usbcdc.c + * + * Created on: Jun 19, 2023 + * Author: Doug + * + * Copyright (C) 2011-2023 Doug Brown + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Portions of this code also came from Nuvoton's BSP, originally + * licensed as Apache-2.0: + * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. + * + */ + +#include "usbcdc_hw.h" +#include + +// Undocumented register for HIRC trim from Nuvoton's samples +#define TRIM_INIT (SYS_BASE + 0x118) + +#define SET_LINE_CODE 0x20 +#define GET_LINE_CODE 0x21 +#define SET_CONTROL_LINE_STATE 0x22 + +/// Setup buffer uses first 8 bytes of USB SRAM +#define SETUP_BUF_BASE 0 +#define SETUP_BUF_LEN 8 +/// EP0 and EP1 share the same USB SRAM, 64 bytes of control +#define EP0_BUF_BASE (SETUP_BUF_BASE + SETUP_BUF_LEN) +#define EP0_BUF_LEN EP0_MAX_PKT_SIZE +#define EP1_BUF_BASE (SETUP_BUF_BASE + SETUP_BUF_LEN) +#define EP1_BUF_LEN EP1_MAX_PKT_SIZE +/// EP2 uses 8 bytes for interrupt IN (not really used though) +#define EP2_BUF_BASE (EP1_BUF_BASE + EP1_BUF_LEN) +#define EP2_BUF_LEN EP2_MAX_PKT_SIZE +/// EP3 uses 64 bytes for bulk IN +#define EP3_BUF_BASE (EP2_BUF_BASE + EP2_BUF_LEN) +#define EP3_BUF_LEN EP3_MAX_PKT_SIZE +/// EP4 uses 64 bytes for bulk OUT +#define EP4_BUF_BASE (EP3_BUF_BASE + EP3_BUF_LEN) +#define EP4_BUF_LEN EP4_MAX_PKT_SIZE + +/// Struct to represent the current line coding +typedef struct +{ + uint32_t baudRate; // Baud rate + uint8_t stopBits; // stop bit + uint8_t parity; // parity + uint8_t dataBits; // data bits +} CDCLineCoding; + +static void USBCDC_SendDataInBuffer(void); +static void USBCDC_InitEndpoints(void); +static void USBCDC_ClassRequest(void); + +/// Default HIRC trim value in case of errors +static uint32_t trimInit; +/// Toggle flag for ensuring the received endpoint 4 packet is the expected one +static volatile uint32_t ep4OutToggle = 0; +/// Line coding for CDC device, not used by this firmware +static CDCLineCoding cdcLineCoding = {0, 0, 0, 0}; +/// Control signal for CDC device, not used by this firmware +static uint16_t cdcCtrlSignal; + +/// Buffer to send to the CDC serial port +static uint8_t cdcTxBuf[EP3_MAX_PKT_SIZE]; +/// Current position in the TX buffer +static uint32_t cdcTxBufPos = 0; +/// Flag that is true if a TX is currently active +static volatile bool cdcTxActive = false; +/// Buffer we read into +static uint8_t cdcRxBuf[EP4_MAX_PKT_SIZE]; +/// Current length of RX buffer +static uint32_t cdcRxLen = 0; +/// Current position into RX buffer +static uint32_t cdcRxPos = 0; +/// Flag that is true if new RX data is ready to read +static volatile bool cdcRxReady = false; + +/** Initializes the USB CDC serial port + * + */ +void USBCDC_Init(void) +{ + // Set up USB + USBD_Open(&gsInfo, USBCDC_ClassRequest); + USBCDC_InitEndpoints(); + USBD_Start(); + NVIC_EnableIRQ(USBD_IRQn); + + // Backup the default trim value, go back to it if there's an error + trimInit = M32(TRIM_INIT); + + // Clear USB SOF flag; it will be used to detect if we have a USB + // signal available to use for HIRC trim + USBD_CLR_INT_FLAG(USBD_INTSTS_SOFIF_Msk); +} + +/** Performs any necessary periodic tasks for the USB CDC serial port + * + */ +void USBCDC_Check(void) +{ + // If we haven't enabled auto trim, and we have a USB signal available, + // then do it! + if (((SYS->HIRCTRIMCTL & SYS_HIRCTRIMCTL_FREQSEL_Msk) != 0x1) && + (USBD->INTSTS & USBD_INTSTS_SOFIF_Msk)) + { + // Clear SOF flag for next time + USBD_CLR_INT_FLAG(USBD_INTSTS_SOFIF_Msk); + + // Start USB trim: + // - HIRC trim reference is USB clock + // - Enable auto trim, and trim to 48 MHz + // - Use 4 cycles for trimming + // - Boundary enabled (value = 10) + SYS->HIRCTRIMCTL = (0x1UL << SYS_HIRCTRIMCTL_REFCKSEL_Pos) + | (0x1UL << SYS_HIRCTRIMCTL_FREQSEL_Pos) + | (0x0UL << SYS_HIRCTRIMCTL_LOOPSEL_Pos) + | (0x1UL << SYS_HIRCTRIMCTL_BOUNDEN_Pos) + | (10UL << SYS_HIRCTRIMCTL_BOUNDARY_Pos); + } + + // If we detect a clock error or trim failure, disable auto trim. We can try again later. + if (SYS->HIRCTRIMSTS & (SYS_HIRCTRIMSTS_CLKERIF_Msk | SYS_HIRCTRIMSTS_TFAILIF_Msk)) + { + // Restore original trim value we read at startup + M32(TRIM_INIT) = trimInit; + + // Disable USB trim + SYS->HIRCTRIMCTL = 0; + + // Clear the error flags + SYS->HIRCTRIMSTS = SYS_HIRCTRIMSTS_CLKERIF_Msk | SYS_HIRCTRIMSTS_TFAILIF_Msk; + + // Clear the SOF flag so the next time it's set we know we have a USB signal + USBD_CLR_INT_FLAG(USBD_INTSTS_SOFIF_Msk); + } + + // Flush the USB CDC port every main loop just like LUFA does + USBCDC_Flush(); +} + +/** Sends a byte out the USB serial port + * + * @param b The byte + */ +void USBCDC_SendByte(uint8_t b) +{ + // Fill up our buffer to send out the USB serial port + cdcTxBuf[cdcTxBufPos++] = b; + if (cdcTxBufPos == EP3_MAX_PKT_SIZE) + { + // If we reached a full packet size, send the data in the buffer + USBCDC_SendDataInBuffer(); + } +} + +/** Reads a byte from the USB serial port, if available + * + * @return The byte, or -1 if there is nothing available + */ +int16_t USBCDC_ReadByte(void) +{ + // Assume not ready + int16_t ret = -1; + + // If we have data ready to read out of the USB controller's endpoint buffer, grab it now + if (cdcRxLen == 0 && cdcRxReady) + { + // Flag that we handled the read event + cdcRxReady = false; + + // Reset our read pointer just in case + cdcRxPos = 0; + + // Read all of the data out from the USB controller + cdcRxLen = USBD_GET_PAYLOAD_LEN(EP4); + USBD_MemCopy(cdcRxBuf, (uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP4)), cdcRxLen); + + // We grabbed all of the packet data, so tell the USB controller we're done with it + USBD_SET_PAYLOAD_LEN(EP4, EP4_MAX_PKT_SIZE); + } + + // If we have something left in our buffer since the last read, use it + if (cdcRxLen > 0) + { + ret = cdcRxBuf[cdcRxPos++]; + + // If we finished reading from the buffer, mark it as finished. + if (cdcRxPos == cdcRxLen) + { + cdcRxPos = 0; + cdcRxLen = 0; + } + } + + return ret; +} + +/** Sends out any remaining data in the TX buffer + * + */ +void USBCDC_Flush(void) +{ + // If we have something waiting to send, send it out now + if (cdcTxBufPos > 0) + { + bool needsZLP = cdcTxBufPos == EP3_MAX_PKT_SIZE; + USBCDC_SendDataInBuffer(); + + // If we are flushing after sending a full packet of data, + // send a ZLP afterward to indicate end of transmit to host + if (needsZLP) + { + USBCDC_SendDataInBuffer(); + } + } +} + +/** Sends the data in the EP3 TX buffer to the host + * + */ +static void USBCDC_SendDataInBuffer(void) +{ + // Wait for any previous transmit to finish first + while (cdcTxActive); + + // Send out the packet + USBD_MemCopy((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP3)), cdcTxBuf, cdcTxBufPos); + cdcTxActive = true; + USBD_SET_PAYLOAD_LEN(EP3, cdcTxBufPos); + + // Reset our buffer position; we've given all the data to the USB controller + cdcTxBufPos = 0; +} + +/** IRQ handler called when USB endpoint 3 is ready (CDC TX data finished transferring) + * + */ +void EP3_Handler(void) +{ + // All we have to do is flag that we no longer have an active transmission. + // USBCDC_SendDataInBuffer() will handle the rest. + cdcTxActive = false; +} + +/** IRQ handler called when USB endpoint 4 is ready (CDC RX data ready to read) + * + */ +void EP4_Handler(void) +{ + // Verify the toggle has changed. If it's still the same, re-request the data. + if (ep4OutToggle == (USBD->EPSTS0 & USBD_EPSTS0_EPSTS4_Msk)) + { + USBD_SET_PAYLOAD_LEN(EP4, EP4_MAX_PKT_SIZE); + } + // Toggle changed, so we're good to go. Toggle for next time and flag that we + // have data ready to read. USBCDC_ReadByte() will handle the rest. + else + { + ep4OutToggle = USBD->EPSTS0 & USBD_EPSTS0_EPSTS4_Msk; + cdcRxReady = true; + } +} + +/** Main IRQ handler for USB device controller + * + */ +void USBD_IRQHandler(void) +{ + uint32_t intStatus = USBD_GET_INT_FLAG(); + uint32_t busState = USBD_GET_BUS_STATE(); + + // Bus interrupt + if (intStatus & USBD_INTSTS_BUSIF_Msk) + { + // Clear the flag + USBD_CLR_INT_FLAG(USBD_INTSTS_BUSIF_Msk); + + // The bus was reset - SE0 status for long enough + if (busState & USBD_STATE_USBRST) + { + // Enable USB, reset everything + USBD_ENABLE_USB(); + USBD_SwReset(); + ep4OutToggle = 0; + } + + // Entered suspend status (bus idle) + if (busState & USBD_STATE_SUSPEND) + { + // Disable PHY while we're suspended + USBD_DISABLE_PHY(); + } + + if (busState & USBD_STATE_RESUME) + { + // Re-enable USB (mainly just the PHY is what we want) + USBD_ENABLE_USB(); + } + } + + // USB event + if (intStatus & USBD_INTSTS_USBIF_Msk) + { + // Setup packet? + if (intStatus & USBD_INTSTS_SETUP_Msk) + { + // Clear the flag... + USBD_CLR_INT_FLAG(USBD_INTSTS_SETUP); + + // Stop EP0 and EP1 which are the control endpoints + USBD_STOP_TRANSACTION(EP0); + USBD_STOP_TRANSACTION(EP1); + + // Now handle the received setup packet + USBD_ProcessSetupPacket(); + } + + // EP0 - control in + if (intStatus & USBD_INTSTS_EPEVT0_Msk) + { + USBD_CLR_INT_FLAG(USBD_INTSTS_EPEVT0_Msk); + USBD_CtrlIn(); + } + + // EP1 - control out + if (intStatus & USBD_INTSTS_EPEVT1_Msk) + { + USBD_CLR_INT_FLAG(USBD_INTSTS_EPEVT1_Msk); + USBD_CtrlOut(); + } + + // Do nothing for EP2 for now + + // EP3 - bulk in for CDC data + if (intStatus & USBD_INTSTS_EPEVT3_Msk) + { + USBD_CLR_INT_FLAG(USBD_INTSTS_EPEVT3_Msk); + EP3_Handler(); + } + + // EP4 - bulk out for CDC data + if (intStatus & USBD_INTSTS_EPEVT4_Msk) + { + USBD_CLR_INT_FLAG(USBD_INTSTS_EPEVT4_Msk); + EP4_Handler(); + } + } +} + +/** Initializes endpoint buffers in USB SRAM + * + */ +void USBCDC_InitEndpoints(void) +{ + // First 8 bytes are for setup packets + USBD->STBUFSEG = SETUP_BUF_BASE; + + // EP0 = control IN, address 0, 64 bytes + USBD_CONFIG_EP(EP0, USBD_CFG_CSTALL | USBD_CFG_EPMODE_IN | 0); + USBD_SET_EP_BUF_ADDR(EP0, EP0_BUF_BASE); + + // EP1 = control OUT, address 0, uses same 64 bytes as control IN + USBD_CONFIG_EP(EP1, USBD_CFG_CSTALL | USBD_CFG_EPMODE_OUT | 0); + USBD_SET_EP_BUF_ADDR(EP1, EP1_BUF_BASE); + + // EP2 = interrupt IN, address 2, 8 bytes + USBD_CONFIG_EP(EP2, USBD_CFG_EPMODE_IN | INT_IN_EP_NUM); + USBD_SET_EP_BUF_ADDR(EP2, EP2_BUF_BASE); + + // EP3 = bulk IN, address 3, 64 bytes + USBD_CONFIG_EP(EP3, USBD_CFG_EPMODE_IN | BULK_IN_EP_NUM); + USBD_SET_EP_BUF_ADDR(EP3, EP3_BUF_BASE); + + // EP4 = bulk OUT, address 4, 64 bytes + USBD_CONFIG_EP(EP4, USBD_CFG_EPMODE_OUT | BULK_OUT_EP_NUM); + USBD_SET_EP_BUF_ADDR(EP4, EP4_BUF_BASE); + + // Mark that we are able to receive on EP4 + USBD_SET_PAYLOAD_LEN(EP4, EP4_MAX_PKT_SIZE); +} + +/** USB class request callback for CDC device + * + */ +void USBCDC_ClassRequest(void) +{ + uint8_t buf[8]; + + // Grab the setup packet... + USBD_GetSetupPacket(buf); + + // Device to host? + if (buf[0] & 0x80) + { + switch (buf[1]) + { + case GET_LINE_CODE: + if (buf[4] == 0) + { + USBD_MemCopy((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP0)), (uint8_t *)&cdcLineCoding, 7); + } + + // Data stage + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 7); + + // Status stage + USBD_PrepareCtrlOut(0, 0); + break; + + default: + // Stall + USBD_SET_EP_STALL(EP0); + USBD_SET_EP_STALL(EP1); + break; + } + } + // Host to device + else + { + switch (buf[1]) + { + case SET_CONTROL_LINE_STATE: + if (buf[4] == 0) + { + cdcCtrlSignal = (buf[3] << 8) | buf[2]; + } + + // Status stage + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 0); + break; + + case SET_LINE_CODE: + if (buf[4] == 0) + { + USBD_PrepareCtrlOut((uint8_t *)&cdcLineCoding, 7); + } + + // Status stage + USBD_SET_DATA1(EP0); + USBD_SET_PAYLOAD_LEN(EP0, 0); + break; + + default: + // Stall + USBD_SET_EP_STALL(EP0); + USBD_SET_EP_STALL(EP1); + break; + } + } +} diff --git a/hal/m258ke/usbcdc_hw.h b/hal/m258ke/usbcdc_hw.h new file mode 100644 index 0000000..ec9c713 --- /dev/null +++ b/hal/m258ke/usbcdc_hw.h @@ -0,0 +1,113 @@ +/* + * usbcdc_hw.h + * + * Created on: Jun 19, 2023 + * Author: Doug + * + * Copyright (C) 2011-2023 Doug Brown + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Portions of this code also came from Nuvoton's BSP, originally + * licensed as Apache-2.0: + * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. + * + */ + +#ifndef HAL_M258KE_USBCDC_HW_H_ +#define HAL_M258KE_USBCDC_HW_H_ + +#include +#include +#include "nuvoton/NuMicro.h" +#include "nuvoton/usbd.h" + +/// Maximum packet sizes of each endpoint +#define EP0_MAX_PKT_SIZE 64 +#define EP1_MAX_PKT_SIZE 64 +#define EP2_MAX_PKT_SIZE 8 +#define EP3_MAX_PKT_SIZE 64 +#define EP4_MAX_PKT_SIZE 64 + +/// Assigned endpoint numbers for CDC serial port +#define INT_IN_EP_NUM 2 +#define BULK_IN_EP_NUM 3 +#define BULK_OUT_EP_NUM 4 + +/** Initializes the USB CDC serial port + * + */ +void USBCDC_Init(void); + +/** Disconnects the USB CDC device from the host + * + */ +static inline void USBCDC_Disable(void) +{ + USBD_SET_SE0(); +} + +/** Performs any necessary periodic tasks for the USB CDC serial port + * + */ +void USBCDC_Check(void); + +/** Sends a byte out the USB serial port + * + * @param b The byte + */ +void USBCDC_SendByte(uint8_t b); + +/** Sends a block of data over the USB CDC serial port + * + * @param data The data to send + * @param len The number of bytes + * @return True on success, false on failure + */ +static inline bool USBCDC_SendData(uint8_t const *data, uint16_t len) +{ + while (len--) + { + USBCDC_SendByte(*data++); + } + + return true; +} + +/** Reads a byte from the USB serial port, if available + * + * @return The byte, or -1 if there is nothing available + */ +int16_t USBCDC_ReadByte(void); + +/** Reads a byte from the USB CDC serial port. Blocks until one is available. + * + * @return The byte read + */ +static inline uint8_t USBCDC_ReadByteBlocking(void) +{ + int16_t b; + do + { + b = USBCDC_ReadByte(); + } while (b < 0); + return (uint8_t)b; +} + +/** Flushes remaining data out to the USB serial port + * + */ +void USBCDC_Flush(void); + +#endif /* HAL_M258KE_USBCDC_HW_H_ */