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_ */