From 1eafe69a3c6020036af1f6ec3960ca8dc26f8c3f Mon Sep 17 00:00:00 2001 From: Levi Gillis Date: Mon, 2 Mar 2026 11:46:27 +0100 Subject: [PATCH 1/5] feat(usb): integrate PluggableUSB-based USB stack for STM32 Co-authored-by: Aymane Bahssain Signed-off-by: Aymane Bahssain --- boards.txt | 2 + libraries/USBDevice/inc/PluggableUSB/HID.h | 124 +++ .../USBDevice/inc/PluggableUSB/PacketBuffer.h | 230 +++++ .../USBDevice/inc/PluggableUSB/PluggableUSB.h | 113 +++ libraries/USBDevice/inc/PluggableUSB/USBAPI.h | 153 ++++ libraries/USBDevice/inc/PluggableUSB/USBCDC.h | 158 ++++ .../USBDevice/inc/PluggableUSB/USBCore.h | 205 +++++ .../inc/PluggableUSB/USBCore_stm32.h | 57 ++ .../USBDevice/inc/PluggableUSB/USBOptions.h | 77 ++ .../inc/PluggableUSB/USBOptionsDefaults.h | 28 + libraries/USBDevice/inc/PluggableUSB/USB_EP.h | 33 + .../USBDevice/inc/PluggableUSB/USB_EP_conf.h | 73 ++ libraries/USBDevice/inc/usbd_conf.h | 6 +- libraries/USBDevice/inc/usbd_desc.h | 2 +- libraries/USBDevice/inc/usbd_ep_conf.h | 4 +- libraries/USBDevice/inc/usbd_if.h | 2 +- libraries/USBDevice/src/PluggableUSB/HID.cpp | 174 ++++ .../src/PluggableUSB/PluggableUSB.cpp | 145 +++ .../USBDevice/src/PluggableUSB/USBCDC.cpp | 344 ++++++++ .../USBDevice/src/PluggableUSB/USBCore.cpp | 823 ++++++++++++++++++ .../src/PluggableUSB/USB_EP_conf.cpp | 51 ++ libraries/USBDevice/src/USBSerial.cpp | 2 +- libraries/USBDevice/src/cdc/cdc_queue.c | 2 +- libraries/USBDevice/src/cdc/usbd_cdc.c | 2 +- libraries/USBDevice/src/cdc/usbd_cdc_if.c | 2 +- .../USBDevice/src/hid/usbd_hid_composite.c | 3 +- .../USBDevice/src/hid/usbd_hid_composite.h | 2 +- .../USBDevice/src/hid/usbd_hid_composite_if.c | 2 +- libraries/USBDevice/src/usbd_conf.c | 71 +- libraries/USBDevice/src/usbd_desc.c | 288 ++++-- libraries/USBDevice/src/usbd_ep_conf.c | 2 +- libraries/USBDevice/src/usbd_if.c | 6 +- platform.txt | 2 +- 33 files changed, 3094 insertions(+), 94 deletions(-) create mode 100644 libraries/USBDevice/inc/PluggableUSB/HID.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/PacketBuffer.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/PluggableUSB.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/USBAPI.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/USBCDC.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/USBCore.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/USBCore_stm32.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/USBOptions.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/USBOptionsDefaults.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/USB_EP.h create mode 100644 libraries/USBDevice/inc/PluggableUSB/USB_EP_conf.h create mode 100644 libraries/USBDevice/src/PluggableUSB/HID.cpp create mode 100644 libraries/USBDevice/src/PluggableUSB/PluggableUSB.cpp create mode 100644 libraries/USBDevice/src/PluggableUSB/USBCDC.cpp create mode 100644 libraries/USBDevice/src/PluggableUSB/USBCore.cpp create mode 100644 libraries/USBDevice/src/PluggableUSB/USB_EP_conf.cpp diff --git a/boards.txt b/boards.txt index 0efc8f1aa2..32cc607bfd 100644 --- a/boards.txt +++ b/boards.txt @@ -15438,6 +15438,8 @@ Nucleo_32.menu.xusb.HSFS=High Speed in Full Speed mode Nucleo_32.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS Disco.menu.usb.none=None +Disco.menu.usb.pluggable=PluggableUSB +Disco.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED Disco.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) Disco.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC Disco.menu.usb.CDC=CDC (no generic 'Serial') diff --git a/libraries/USBDevice/inc/PluggableUSB/HID.h b/libraries/USBDevice/inc/PluggableUSB/HID.h new file mode 100644 index 0000000000..f8ba9e695a --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/HID.h @@ -0,0 +1,124 @@ +/* + Copyright (c) 2015, Arduino LLC + Original code (pre-library): Copyright (c) 2011, Peter Barrett + * Modified by Levi Gillis @ 2022-2025 + Changes can be found in the git repo https://github.com/Levi--G/USBLibrarySTM32 + + 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. + */ + +#ifndef HID_STM32_h +#define HID_STM32_h + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "PluggableUSB.h" +#include +#include + +#define _USING_HID + +// HID 'Driver' +// ------------ +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +#define HID_HID_DESCRIPTOR_TYPE 0x21 +#define HID_REPORT_DESCRIPTOR_TYPE 0x22 +#define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23 + +// HID subclass HID1.11 Page 8 4.2 Subclass +#define HID_SUBCLASS_NONE 0 +#define HID_SUBCLASS_BOOT_INTERFACE 1 + +// HID Keyboard/Mouse bios compatible protocols HID1.11 Page 9 4.3 Protocols +#define HID_PROTOCOL_NONE 0 +#define HID_PROTOCOL_KEYBOARD 1 +#define HID_PROTOCOL_MOUSE 2 + +// Normal or bios protocol (Keyboard/Mouse) HID1.11 Page 54 7.2.5 Get_Protocol Request +// "protocol" variable is used for this purpose. +#define HID_BOOT_PROTOCOL 0 +#define HID_REPORT_PROTOCOL 1 + +// HID Request Type HID1.11 Page 51 7.2.1 Get_Report Request +#define HID_REPORT_TYPE_INPUT 1 +#define HID_REPORT_TYPE_OUTPUT 2 +#define HID_REPORT_TYPE_FEATURE 3 + +typedef struct { + uint8_t len; // 9 + uint8_t dtype; // 0x21 + uint8_t addr; + uint8_t versionL; // 0x101 + uint8_t versionH; // 0x101 + uint8_t country; + uint8_t desctype; // 0x22 report + uint8_t descLenL; + uint8_t descLenH; +} HIDDescDescriptor; + +typedef struct { + InterfaceDescriptor hid; + HIDDescDescriptor desc; + EndpointDescriptor in; +} HIDDescriptor; + +class HIDSubDescriptor { + public: + HIDSubDescriptor *next = NULL; + HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) {} + + const void *data; + const uint16_t length; +}; + +class HID_ : public PluggableUSBModule { + public: + HID_(void); + int begin(void); + int SendReport(uint8_t id, const void *data, int len); + void AppendDescriptor(HIDSubDescriptor *node); + + protected: + // Implementation of the PluggableUSBModule + int getInterface(uint8_t *interfaceCount); + int getDescriptor(USBSetup &setup); + bool setup(USBSetup &setup); + uint8_t getShortName(char *name); + + private: + uint8_t epType[1]; + + HIDSubDescriptor *rootNode; + uint16_t descriptorSize; + + uint8_t protocol; + uint8_t idle; +}; + +// Replacement for global singleton. +// This function prevents static-initialization-order-fiasco +// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use +HID_ &HID(); + +#define D_HIDREPORT(length) {9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length)} + +#endif // USBCON PLUGGABLE_USB_ENABLED + +#endif // HID_STM32_h diff --git a/libraries/USBDevice/inc/PluggableUSB/PacketBuffer.h b/libraries/USBDevice/inc/PluggableUSB/PacketBuffer.h new file mode 100644 index 0000000000..c9a517b333 --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/PacketBuffer.h @@ -0,0 +1,230 @@ +/* + * PacketBuffer.h + * Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the GNU Lesser General Public License v3.0 license. + */ + +#ifndef PACKET_BUFFER_H_ +#define PACKET_BUFFER_H_ + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "USBOptions.h" +#include +#include + +#if PACKETBUFFER_COUNT < 2 + #warning "PacketBuffer is likely too small, expect issues" +#endif + +class PacketBuffer { + public: + virtual uint8_t *PrepareWrite(uint32_t &len) = 0; + virtual void CommitWrite(uint32_t len) = 0; + virtual uint8_t *PrepareRead(uint32_t &len) = 0; + virtual void CommitRead(uint32_t len) = 0; + virtual uint32_t Read(uint8_t *data, uint32_t len) = 0; + virtual uint32_t Write(uint8_t *data, uint32_t len) = 0; + virtual bool isEmpty() = 0; + virtual bool isFull() = 0; + virtual void clear() = 0; + virtual uint32_t available() = 0; + virtual uint32_t availableToWrite() = 0; + virtual ~PacketBuffer() = default; +}; + +template +struct USBD_HID_BufferItem { + uint32_t len = 0; + uint32_t pos = 0; + uint8_t buf[size]; + + uint32_t Read(uint8_t *data, uint32_t length) + { + uint8_t read = min(Remaining(), length); + if (read) { + memcpy(data, buf + pos, read); + pos += read; + } + return read; + } + uint32_t Write(uint8_t *data, uint32_t length) + { + uint8_t write = min(size, length); + if (write) { + memcpy(buf, data, write); + len = write; + pos = 0; + } + return write; + } + uint32_t Remaining() + { + return len - pos; + } + bool Empty() + { + return Remaining() <= 0; + } + void Clear() + { + len = 0; + pos = 0; + } + void WriteLength(uint32_t length) + { + len = length; + pos = 0; + } + void ReadLength(uint32_t length) + { + pos = min(pos + length, size); + } +}; + +template +class SplitPacketBuffer : public PacketBuffer { + public: + virtual uint8_t *PrepareWrite(uint32_t &len) + { + isPrepared = true; + if (writeHead == readHead) { + // overwrite last + writeHead = prevUnsafeHead(writeHead); + } + len = min(buffersize, len); + return buffer[writeHead].buf; + } + virtual void CommitWrite(uint32_t len) + { + if (isPrepared) { + buffer[writeHead].WriteLength(len); + writeHead = newWriteHead(); + } + } + virtual uint8_t *PrepareRead(uint32_t &len) + { + isPrepared = true; + if (!buffer[readHead].Remaining()) { + readHead = newReadHead(); + } + len = min(len, buffer[readHead].Remaining()); + return buffer[readHead].buf; + } + virtual void CommitRead(uint32_t len) + { + if (isPrepared) { + buffer[readHead].ReadLength(len); + if (buffer[readHead].Empty()) { + readHead = newReadHead(); + } + } + } + uint32_t Read(uint8_t *data, uint32_t len) + { + if (!buffer[readHead].Remaining()) { + readHead = newReadHead(); + } + uint32_t read = 0; + if (buffer[readHead].Remaining()) { + read = buffer[readHead].Read(data, len); + if (buffer[readHead].Empty()) { + readHead = newReadHead(); + } + } + return read; + } + virtual uint32_t Write(uint8_t *data, uint32_t len) + { + uint32_t write = 0; + if (writeHead == readHead) { + // overwrite last + writeHead = prevUnsafeHead(writeHead); + } + write = buffer[writeHead].Write(data, len); + writeHead = newWriteHead(); + return write; + } + virtual bool isEmpty() + { + return available() == 0; + } + virtual bool isFull() + { +#if PACKETBUFFER_ALLOW_OVERWRITE + return false; +#else + return readHead == writeHead; +#endif + } + void clear() + { + readHead = 0; + writeHead = 1; + } + virtual uint32_t available() + { + if (!buffer[readHead].Remaining()) { + readHead = newReadHead(); + } + return getAvailable(readHead, writeHead); + } + virtual uint32_t availableToWrite() + { + return readHead == writeHead ? 0 : capacity; + } + + uint32_t getAvailable(int head, int otherhead) + { +#if PACKETBUFFER_USE_FAST_AVAILABLE + return buffer[head].Remaining(); +#else + uint32_t16_t total = 0; + auto ptr = head; + auto endptr = otherhead; + for (size_t i = 0; i != endptr; i++) { + total += buffer[i].Remaining(); + i = newUnsafeHead(ptr); + } + return total; +#endif + } + + private: + USBD_HID_BufferItem buffer[capacity]; + int readHead = 0; + int writeHead = 1; + bool isPrepared = false; + int newReadHead() + { + auto result = nextUnsafeHead(readHead); + if (result == writeHead) { + return readHead; + } + return result; + } + int newWriteHead() + { + return nextUnsafeHead(writeHead); + } + int nextUnsafeHead(int current) + { + auto result = current + 1; + if (result >= capacity) { + result = 0; + } + return result; + } + int prevUnsafeHead(int current) + { + auto result = current - 1; + if (result < 0) { + result = capacity - 1; + } + return result; + } +}; + +#endif +#endif // PACKET_BUFFER_H_ diff --git a/libraries/USBDevice/inc/PluggableUSB/PluggableUSB.h b/libraries/USBDevice/inc/PluggableUSB/PluggableUSB.h new file mode 100644 index 0000000000..7146e6fa4e --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/PluggableUSB.h @@ -0,0 +1,113 @@ +/* + PluggableUSB.h + Copyright (c) 2015 Arduino LLC + * Modified by Levi Gillis @ 2022-2025 + Changes can be found in the git repo https://github.com/Levi--G/USBLibrarySTM32 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef PUSB_STM32_h +#define PUSB_STM32_h + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "USBAPI.h" +#include + +class PluggableUSBModule { + public: + PluggableUSBModule(uint8_t numEps, uint8_t numIfs, uint8_t *epType) : numEndpoints(numEps), numInterfaces(numIfs), endpointType(epType), pluggedEndpoint(1) + { + } + + protected: + virtual bool setup(USBSetup &setup) = 0; + virtual int getInterface(uint8_t *interfaceCount) = 0; + virtual int getDescriptor(USBSetup &setup) = 0; + virtual uint8_t getShortName(char *name) + { + name[0] = 'A' + pluggedInterface; + return 1; + } + + uint8_t pluggedInterface; + uint8_t pluggedEndpoint; + + const uint8_t numEndpoints; + const uint8_t numInterfaces; + const uint8_t *endpointType; + + virtual uint8_t getNumEndpoints() + { + return numEndpoints; + } + + virtual uint8_t getNumInterfaces() + { + return numInterfaces; + } + + virtual uint8_t getEndpointTypes(uint8_t *types) + { + for (size_t i = 0; i < numEndpoints; i++) { + types[i] = endpointType[i]; + } + return numEndpoints; + } + + PluggableUSBModule *next = NULL; + + friend class PluggableUSB_; + friend int PLUG_GetInterface(uint8_t *interfaceCount); + friend int PLUG_GetDescriptor(USBSetup &setup); + friend bool PLUG_Setup(USBSetup &setup); + friend uint8_t PLUG_GetNumEndpoints(); + friend uint8_t PLUG_GetNumInterfaces(); + friend uint8_t PLUG_GetEndpointTypes(uint8_t *types); +}; + +class PluggableUSB_ : PluggableUSBModule { + public: + PluggableUSB_(); + bool plug(PluggableUSBModule *node); + int getInterface(uint8_t *interfaceCount); + int getDescriptor(USBSetup &setup); + bool setup(USBSetup &setup); + uint8_t getShortName(char *name); + + protected: + virtual uint8_t getNumEndpoints(); + virtual uint8_t getNumInterfaces(); + virtual uint8_t getEndpointTypes(uint8_t *types); + + private: + uint8_t lastIf; + uint8_t lastEp; + PluggableUSBModule *rootNode; +}; + +// Replacement for global singleton. +// This function prevents static-initialization-order-fiasco +// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use +PluggableUSB_ &PluggableUSB(); + +/// @brief Allows installing a single pluggable module without the overhead of multiple module support +/// @param root The module to install, will replace any already present +void USB_PlugRoot(PluggableUSBModule *root); + +#endif + +#endif /*PUSB_STM32_h*/ diff --git a/libraries/USBDevice/inc/PluggableUSB/USBAPI.h b/libraries/USBDevice/inc/PluggableUSB/USBAPI.h new file mode 100644 index 0000000000..0152d19cb2 --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/USBAPI.h @@ -0,0 +1,153 @@ +/* + USBAPI.h + Copyright (c) 2005-2014 Arduino. All right reserved. + * Modified by Levi Gillis @ 2022-2025 + Changes can be found in the git repo https://github.com/Levi--G/USBLibrarySTM32 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __USBAPI__ +#define __USBAPI__ + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; + +#include "Arduino.h" +#include "USB_EP.h" + +#define EPX_SIZE USB_EP_SIZE + +#include "USBCore.h" + +//================================================================================ +//================================================================================ +// USB + +#define EP_TYPE_CONTROL (USB_ENDPOINT_TYPE_CONTROL) +#define EP_TYPE_BULK_IN (USB_ENDPOINT_IN(USB_ENDPOINT_TYPE_BULK)) +#define EP_TYPE_BULK_OUT (USB_ENDPOINT_TYPE_BULK) +#define EP_TYPE_INTERRUPT_IN (USB_ENDPOINT_IN(USB_ENDPOINT_TYPE_INTERRUPT)) +#define EP_TYPE_INTERRUPT_OUT (USB_ENDPOINT_TYPE_INTERRUPT) +#define EP_TYPE_ISOCHRONOUS_IN (USB_ENDPOINT_IN(USB_ENDPOINT_TYPE_ISOCHRONOUS)) +#define EP_TYPE_ISOCHRONOUS_OUT (USB_ENDPOINT_TYPE_ISOCHRONOUS) + +//================================================================================ +//================================================================================ +// Low level API + +typedef struct { + uint8_t bmRequestType; + uint8_t bRequest; + uint8_t wValueL; + uint8_t wValueH; + uint16_t wIndex; + uint16_t wLength; +} USBSetup; + +//================================================================================ +//================================================================================ + +#define TRANSFER_PGM 0x80 +#define TRANSFER_RELEASE 0x40 +#define TRANSFER_ZERO 0x20 + +/// @brief Sends a message over the CTRL 0 endpoint +/// @param flags ignored on STM32 +/// @param d data buffer +/// @param len length of data +/// @return length of data written, can be smaller than len +int USB_SendControl(uint8_t flags, const void *d, int len); +/// @brief Reads data from the CTRL 0 endpoint +/// @param d data buffer +/// @param len length of data +/// @return length read into buffer, can be smaller than len +int USB_RecvControl(void *d, int len); +/// @brief Gives the available read length on an RX (OUT) endpoint +/// @param ep RX (OUT) endpoint +/// @return length in bytes +uint8_t USB_Available(uint8_t ep); +/// @brief The amount of free space on a TX (IN) ep +/// @param ep TX (IN) endpoint +/// @return free space in bytes +uint8_t USB_SendSpace(uint8_t ep); +/// @brief Sends a zero length packet, blocking for compatibility +/// @param ep TX (IN) endpoint +/// @return 0 +int USB_SendZLP(uint8_t ep); +/// @brief Sends data to an endpoint, blocking for compatibility +/// @param ep TX (IN) endpoint +/// @param data data buffer +/// @param len data length +/// @return length written to EP, can be smaller than len +int USB_Send(uint8_t ep, const void *data, int len); +/// @brief Sends data to an endpoint, non blocking +/// unless previous data is still being sent out +/// when not using TX buffers make sure to use a buffer allocated on heap +/// @param ep TX (IN) endpoint +/// @param data data buffer +/// @param len data length +/// @return length written to EP, can be smaller than len +int USB_SendQuick(uint8_t ep, const void *data, int len); +/// @brief Returns if sending is possible on this endpoint +/// @param ep TX (IN) endpoint +/// @return true if sending is possible +bool USB_SendAvailable(uint8_t ep); +/// @brief Receives data on a RX (OUT) endpoint +/// @param ep RX (OUT) endpoint +/// @param data data buffer +/// @param len data length +/// @return length read into buffer +int USB_Recv(uint8_t ep, void *data, int len); +/// @brief Reads a single byte from an endpoint, +/// not very performant, prefer using a buffer instead +/// @param ep RX (OUT) endpoint +/// @return 1 if reading was successful +int USB_Recv(uint8_t ep); +/// @brief Flushes an endpoint +/// @param ep RX or TX endpoint +void USB_Flush(uint8_t ep); +/// @brief Starts the USB and allocates needed resources +/// @return true if successful +bool USB_Begin(); +/// @brief Checks if the USB is allocated and ready to connect. +/// +/// Sending to buffer is already possible but there might be no host listening yet +/// @return true if ready +bool USB_Running(); +/// @brief Checks if the USB has active communication with a host +/// +/// This can be used to react to usb connection/disconnection +/// @return true if active +bool USB_Connected(); +/// @brief Stops all USB activity and cleans up used resources +void USB_End(); + +// for pluggableusb support +int PLUG_GetInterface(uint8_t *interfaceCount); +int PLUG_GetDescriptor(USBSetup &setup); +bool PLUG_Setup(USBSetup &setup); +uint8_t PLUG_GetNumEndpoints(); +uint8_t PLUG_GetNumInterfaces(); +uint8_t PLUG_GetEndpointTypes(uint8_t *types); + +#endif + +#endif /* if defined(USBCON) */ diff --git a/libraries/USBDevice/inc/PluggableUSB/USBCDC.h b/libraries/USBDevice/inc/PluggableUSB/USBCDC.h new file mode 100644 index 0000000000..29f43a6df9 --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/USBCDC.h @@ -0,0 +1,158 @@ +/* + * USBCDC.h + * Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the GNU Lesser General Public License v3.0 license. + */ + +#ifndef __CDC_H__ +#define __CDC_H__ + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "PluggableUSB.h" +#include "USBAPI.h" + +#define CDC_V1_10 0x0110 +#define CDC_COMMUNICATION_INTERFACE_CLASS 0x02 + +#define CDC_CALL_MANAGEMENT 0x01 +#define CDC_ABSTRACT_CONTROL_MODEL 0x02 +#define CDC_HEADER 0x00 +#define CDC_ABSTRACT_CONTROL_MANAGEMENT 0x02 +#define CDC_UNION 0x06 +#define CDC_CS_INTERFACE 0x24 +#define CDC_CS_ENDPOINT 0x25 +#define CDC_DATA_INTERFACE_CLASS 0x0A + +#if USB_SERIAL_USE_ACM_EP + #define USB_SERIAL_EP_NUM 3 +#else + #define USB_SERIAL_EP_NUM 2 +#endif + +// CDC CS interface descriptor +typedef struct { + uint8_t len; // 5 + uint8_t dtype; // 0x24 + uint8_t subtype; + uint8_t d0; + uint8_t d1; +} CDCCSInterfaceDescriptor; + +typedef struct { + uint8_t len; // 4 + uint8_t dtype; // 0x24 + uint8_t subtype; + uint8_t d0; +} CDCCSInterfaceDescriptor4; + +typedef struct { + uint8_t len; + uint8_t dtype; // 0x24 + uint8_t subtype; // 1 + uint8_t bmCapabilities; + uint8_t bDataInterface; +} CMFunctionalDescriptor; + +typedef struct { + uint8_t len; + uint8_t dtype; // 0x24 + uint8_t subtype; // 1 + uint8_t bmCapabilities; +} ACMFunctionalDescriptor; + +typedef struct { + // IAD + IADDescriptor iad; // Only needed on compound device + // Control + InterfaceDescriptor cif; + CDCCSInterfaceDescriptor header; + ACMFunctionalDescriptor controlManagement; // ACM + CDCCSInterfaceDescriptor functionalDescriptor; // CDC_UNION + CMFunctionalDescriptor callManagement; // Call Management +#if USB_SERIAL_USE_ACM_EP + EndpointDescriptor cifin; +#endif + // Data + InterfaceDescriptor dif; + EndpointDescriptor in; + EndpointDescriptor out; +} CDCDescriptor; + +class USBCDC : public Stream, public PluggableUSBModule { + public: + USBCDC(); + void begin() {} + void begin(uint32_t baud_count); + void begin(unsigned long, uint8_t); + void end(void); + + virtual int available(void); + virtual int availableForWrite(void); + virtual int peek(void); + virtual int read(void); + virtual void flush(void); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buffer, size_t size); + using Print::write; // pull in write(str) from Print + operator bool(); + + size_t readBytes(char *buffer, size_t length); + + // This method allows processing "SEND_BREAK" requests sent by + // the USB host. Those requests indicate that the host wants to + // send a BREAK signal and are accompanied by a single uint16_t + // value, specifying the duration of the break. The value 0 + // means to end any current break, while the value 0xffff means + // to start an indefinite break. + // readBreak() will return the value of the most recent break + // request, but will return it at most once, returning -1 when + // readBreak() is called again (until another break request is + // received, which is again returned once). + // This also mean that if two break requests are received + // without readBreak() being called in between, the value of the + // first request is lost. + // Note that the value returned is a long, so it can return + // 0-0xffff as well as -1. + int32_t readBreak(); + + // These return the settings specified by the USB host for the + // serial port. These aren't really used, but are offered here + // in case a sketch wants to act on these settings. + uint32_t baud(); + uint8_t stopbits(); + uint8_t paritytype(); + uint8_t numbits(); + bool dtr(); + bool rts(); + enum { + ONE_STOP_BIT = 0, + ONE_AND_HALF_STOP_BIT = 1, + TWO_STOP_BITS = 2, + }; + enum { + NO_PARITY = 0, + ODD_PARITY = 1, + EVEN_PARITY = 2, + MARK_PARITY = 3, + SPACE_PARITY = 4, + }; + + protected: + // Implementation of the PUSBListNode + int getInterface(uint8_t *interfaceNum); + int getDescriptor(USBSetup &setup); + bool setup(USBSetup &setup); + uint8_t getShortName(char *name); + void handleEndpoint(int ep); + + private: + int availableForStore(void); + bool stalled; + uint8_t epType[USB_SERIAL_EP_NUM]; +}; + +#endif /* USBCON */ + +#endif diff --git a/libraries/USBDevice/inc/PluggableUSB/USBCore.h b/libraries/USBDevice/inc/PluggableUSB/USBCore.h new file mode 100644 index 0000000000..42170e0f90 --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/USBCore.h @@ -0,0 +1,205 @@ + +// Copyright (c) 2010, Peter Barrett +// * Modified by Levi Gillis @ 2022-2025 +// * Changes can be found in the git repo https://github.com/Levi--G/USBLibrarySTM32 +/* +** 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. +*/ + +#ifndef __USBCORE_H__ +#define __USBCORE_H__ + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "USBAPI.h" + +// Standard requests +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 + +// bmRequestType +#define REQUEST_HOSTTODEVICE 0x00 +#define REQUEST_DEVICETOHOST 0x80 +#define REQUEST_DIRECTION 0x80 + +#define REQUEST_STANDARD 0x00 +#define REQUEST_CLASS 0x20 +#define REQUEST_VENDOR 0x40 +#define REQUEST_TYPE 0x60 + +#define REQUEST_DEVICE 0x00 +#define REQUEST_INTERFACE 0x01 +#define REQUEST_ENDPOINT 0x02 +#define REQUEST_OTHER 0x03 +#define REQUEST_RECIPIENT 0x03 + +#define REQUEST_DEVICETOHOST_CLASS_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_CLASS | REQUEST_INTERFACE) +#define REQUEST_HOSTTODEVICE_CLASS_INTERFACE (REQUEST_HOSTTODEVICE | REQUEST_CLASS | REQUEST_INTERFACE) +#define REQUEST_DEVICETOHOST_STANDARD_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_INTERFACE) + +// Class requests + +#define CDC_SET_LINE_CODING 0x20 +#define CDC_GET_LINE_CODING 0x21 +#define CDC_SET_CONTROL_LINE_STATE 0x22 +#define CDC_SEND_BREAK 0x23 + +#define MSC_RESET 0xFF +#define MSC_GET_MAX_LUN 0xFE + +// Descriptors + +#define USB_DEVICE_DESC_SIZE 18 +#define USB_CONFIGUARTION_DESC_SIZE 9 +#define USB_INTERFACE_DESC_SIZE 9 +#define USB_ENDPOINT_DESC_SIZE 7 + +#define USB_DEVICE_DESCRIPTOR_TYPE 1 +#define USB_CONFIGURATION_DESCRIPTOR_TYPE 2 +#define USB_STRING_DESCRIPTOR_TYPE 3 +#define USB_INTERFACE_DESCRIPTOR_TYPE 4 +#define USB_ENDPOINT_DESCRIPTOR_TYPE 5 + +// usb_20.pdf Table 9.6 Standard Feature Selectors +#define DEVICE_REMOTE_WAKEUP 1 +#define ENDPOINT_HALT 2 +#define TEST_MODE 3 + +// usb_20.pdf Figure 9-4. Information Returned by a GetStatus() Request to a Device +#define FEATURE_SELFPOWERED_ENABLED (1 << 0) +#define FEATURE_REMOTE_WAKEUP_ENABLED (1 << 1) + +#define USB_DEVICE_CLASS_COMMUNICATIONS 0x02 +#define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03 +#define USB_DEVICE_CLASS_STORAGE 0x08 +#define USB_DEVICE_CLASS_VENDOR_SPECIFIC 0xFF + +#define USB_CONFIG_POWERED_MASK 0x40 +#define USB_CONFIG_BUS_POWERED 0x80 +// #define USB_CONFIG_BUS_POWERED 0xC0 +// #ifndef USB_CONFIG_SELF_POWERED +// #define USB_CONFIG_SELF_POWERED 0xC0 +// #define USB_CONFIG_REMOTE_WAKEUP 0x20 +// #endif + +// bMaxPower in Configuration Descriptor +#define USB_CONFIG_POWER_MA(mA) ((mA) / 2) +#ifndef USB_CONFIG_POWER + #define USB_CONFIG_POWER (500) + // #define USB_CONFIG_POWER (100) +#endif + +// Device +typedef struct { + u8 len; // 18 + u8 dtype; // 1 USB_DEVICE_DESCRIPTOR_TYPE + u16 usbVersion; // 0x200 or 0x210 + u8 deviceClass; + u8 deviceSubClass; + u8 deviceProtocol; + u8 packetSize0; // Packet 0 + u16 idVendor; + u16 idProduct; + u16 deviceVersion; // 0x100 + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; +} DeviceDescriptor; + +// Config +typedef struct { + u8 len; // 9 + u8 dtype; // 2 + u16 clen; // total length + u8 numInterfaces; + u8 config; + u8 iconfig; + u8 attributes; + u8 maxPower; +} ConfigDescriptor; + +// String + +// Interface +typedef struct { + u8 len; // 9 + u8 dtype; // 4 + u8 number; + u8 alternate; + u8 numEndpoints; + u8 interfaceClass; + u8 interfaceSubClass; + u8 protocol; + u8 iInterface; +} InterfaceDescriptor; + +// Endpoint +typedef struct { + u8 len; // 7 + u8 dtype; // 5 + u8 addr; + u8 attr; + u16 packetSize; + u8 interval; +} EndpointDescriptor; + +// Interface Association Descriptor +// Used to bind 2 interfaces together in CDC composite device +typedef struct { + u8 len; // 8 + u8 dtype; // 11 + u8 firstInterface; + u8 interfaceCount; + u8 functionClass; + u8 funtionSubClass; + u8 functionProtocol; + u8 iInterface; +} IADDescriptor; + +typedef struct { + InterfaceDescriptor msc; + EndpointDescriptor in; + EndpointDescriptor out; +} MSCDescriptor; + +#define D_DEVICE(_class, _subClass, _proto, _packetSize0, _vid, _pid, _version, _im, _ip, _is, _configs) \ + {18, 1, USB_VERSION, _class, _subClass, _proto, _packetSize0, _vid, _pid, _version, _im, _ip, _is, _configs} + +#define D_CONFIG(_totalLength, _interfaces) \ + {9, 2, _totalLength, _interfaces, 1, 0, USB_CONFIG_BUS_POWERED, USB_CONFIG_POWER_MA(USB_CONFIG_POWER)} + +#define D_INTERFACE(_n, _numEndpoints, _class, _subClass, _protocol) \ + {9, 4, _n, 0, _numEndpoints, _class, _subClass, _protocol, 0} + +#define D_ENDPOINT(_addr, _attr, _packetSize, _interval) \ + {7, 5, _addr, _attr, _packetSize, _interval} + +#define D_IAD(_firstInterface, _count, _class, _subClass, _protocol) \ + {8, 11, _firstInterface, _count, _class, _subClass, _protocol, 0} + +#define D_CDCCS(_subtype, _d0, _d1) {5, 0x24, _subtype, _d0, _d1} +#define D_CDCCS4(_subtype, _d0) {4, 0x24, _subtype, _d0} + +#endif + +#endif diff --git a/libraries/USBDevice/inc/PluggableUSB/USBCore_stm32.h b/libraries/USBDevice/inc/PluggableUSB/USBCore_stm32.h new file mode 100644 index 0000000000..9eaea74fd8 --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/USBCore_stm32.h @@ -0,0 +1,57 @@ +/* + * USBCore_stm32.h + * Template generated with Stm32CubeMX "usbd_customhid.h": + * Copyright (c) 2015 STMicroelectronics. + * All rights reserved. + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + * Implementation/modification: + * Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the GNU Lesser General Public License v3.0 license. + */ + +#ifndef __USBCORE_STM32_H__ +#define __USBCORE_STM32_H__ + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "USB_EP.h" +#include "usbd_ioreq.h" + +#define HID_DESCRIPTOR_TYPE 0x21 +#define HID_REPORT_DESC 0x22 + +#ifndef HID_HS_BINTERVAL + #define HID_HS_BINTERVAL 0x07U +#endif /* HID_HS_BINTERVAL */ + +#ifndef HID_FS_BINTERVAL + #define HID_FS_BINTERVAL 0x0AU +#endif /* HID_FS_BINTERVAL */ + +#define HID_REQ_SET_PROTOCOL 0x0BU +#define HID_REQ_GET_PROTOCOL 0x03U + +#define HID_REQ_SET_IDLE 0x0AU +#define HID_REQ_GET_IDLE 0x02U + +#define HID_REQ_SET_REPORT 0x09U +#define HID_REQ_GET_REPORT 0x01U + +typedef enum { + HID_IDLE = 0, + HID_BUSY, +} HID_StateTypeDef; + +typedef struct { + uint32_t Protocol; + uint32_t IdleState; + uint32_t AltSetting; + HID_StateTypeDef EPstate[USB_MAX_EPS]; +} USBD_HID_HandleTypeDef; + +#endif /* USBCON */ +#endif /* __USBCORE_STM32_H__ */ diff --git a/libraries/USBDevice/inc/PluggableUSB/USBOptions.h b/libraries/USBDevice/inc/PluggableUSB/USBOptions.h new file mode 100644 index 0000000000..920bfeeb7c --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/USBOptions.h @@ -0,0 +1,77 @@ +/* + * USBOptions.h + * Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the GNU Lesser General Public License v3.0 license. + */ + +#pragma once + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + + #include "USBOptionsDefaults.h" + + // These options can be changed by adding -D Option=value to your compiler arguments + /* + For PIO, in platformio.ini: + build_flags = + -D USBCON + -D HAL_PCD_MODULE_ENABLED + -D Option=value + */ + + #ifndef USB_EP0_SIZE + // This should really stay at max size, but in theory can be lowered + #define USB_EP0_SIZE MAX_USB_EP_SIZE + #endif + #ifndef USB_EP_SIZE + // This definitions is useful if you want to reduce the EP_SIZE to save ram + // Don't forget to also make the epsize smaller in the interface definition + #define USB_EP_SIZE 64 + #endif + + #ifndef EP0_PACKETBUFFER_COUNT + // This should stay at 2 + #define EP0_PACKETBUFFER_COUNT 2 + #endif + + #ifndef PACKETBUFFER_COUNT + // Packetbuffer count determines the buffer size, + // this can be lowered to 2 to save ram, but more missed data may occur. + // 2 should be stable on USB_OTG_FS, 3 on USB. + // If you have missing packets, increase the size + #define PACKETBUFFER_COUNT PACKETBUFFER_COUNT_DEFAULT + #endif + + #ifndef PACKETBUFFER_ALLOW_OVERWRITE + // Allows buffers to be overwritten in case of data overload, + // this prevents hangs but might cause data loss if polling rate is too low + // recommended for stm32 with "classic" USB, not needed for stm32 with USB_OTF_FS + #define PACKETBUFFER_ALLOW_OVERWRITE PACKETBUFFER_ALLOW_OVERWRITE_DEFAULT + #endif + + #ifndef PACKETBUFFER_USE_FAST_AVAILABLE + // Allows a faster version of available calculations, + // some libraries might not work and not see incoming data + #define PACKETBUFFER_USE_FAST_AVAILABLE true + #endif + + #ifndef PACKETBUFFER_USE_TX_BUFFERS + // Adds TX buffers that buffer sent data, + // this prevents hangs but uses more ram + #define PACKETBUFFER_USE_TX_BUFFERS true + #endif + + #ifndef USB_WRITE_TIMEOUT + // Sets a timeout for the USB_Send and USB_Flush function + #define USB_WRITE_TIMEOUT 100 + #endif + + #ifndef USB_SERIAL_USE_ACM_EP + // Enables the third ACM endpoint of USB serial which is in this library normally + // disabled to use the EP for other libraries but might not be compatible with some OSses + // or programs that might need it to be enabled + #define USB_SERIAL_USE_ACM_EP false + #endif + +#endif diff --git a/libraries/USBDevice/inc/PluggableUSB/USBOptionsDefaults.h b/libraries/USBDevice/inc/PluggableUSB/USBOptionsDefaults.h new file mode 100644 index 0000000000..d0ca6e8344 --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/USBOptionsDefaults.h @@ -0,0 +1,28 @@ +/* + * USBOptionsDefaults.h + * Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the GNU Lesser General Public License v3.0 license. + */ + +#pragma once + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + + #include + + // These defaults are for values that depend on a certain board + + #if defined(USB) + #define PACKETBUFFER_COUNT_DEFAULT 3 + #elif defined(USB_OTG_FS) + #define PACKETBUFFER_COUNT_DEFAULT 2 + #endif + + #if defined(USB) + #define PACKETBUFFER_ALLOW_OVERWRITE_DEFAULT true + #elif defined(USB_OTG_FS) + #define PACKETBUFFER_ALLOW_OVERWRITE_DEFAULT false + #endif + +#endif diff --git a/libraries/USBDevice/inc/PluggableUSB/USB_EP.h b/libraries/USBDevice/inc/PluggableUSB/USB_EP.h new file mode 100644 index 0000000000..db8b4afd63 --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/USB_EP.h @@ -0,0 +1,33 @@ +/* + * USBEP.h + * Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the GNU Lesser General Public License v3.0 license. + */ + +#pragma once + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + + #include "USBOptions.h" + + #define MAX_USB_EP_SIZE 64 + + #if defined(USB_OTG_FS) + #define USB_MAX_EPS 3 + #else + #define USB_MAX_EPS 7 + #endif + #define USB_MAX_EPS_SLOTS (USB_MAX_EPS - 1) + + #define USB_ENDPOINT_DIRECTION_MASK 0x80 + #define USB_ENDPOINT_OUT(addr) (lowByte((addr) | 0x00)) + #define USB_ENDPOINT_IN(addr) (lowByte((addr) | 0x80)) + + #define USB_ENDPOINT_TYPE_MASK 0x03 + #define USB_ENDPOINT_TYPE_CONTROL 0x00 + #define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01 + #define USB_ENDPOINT_TYPE_BULK 0x02 + #define USB_ENDPOINT_TYPE_INTERRUPT 0x03 + +#endif diff --git a/libraries/USBDevice/inc/PluggableUSB/USB_EP_conf.h b/libraries/USBDevice/inc/PluggableUSB/USB_EP_conf.h new file mode 100644 index 0000000000..ba6da8df8c --- /dev/null +++ b/libraries/USBDevice/inc/PluggableUSB/USB_EP_conf.h @@ -0,0 +1,73 @@ +/* + * USBEP_conf.h + * Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the GNU Lesser General Public License v3.0 license. + */ + +#pragma once + +#include "USB_EP.h" +#include +#include + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#define SMALL_EP(Ep) (Ep & 0xFU) +#define IN_EP(Ep) (lowByte((Ep) | 0x80)) +#define IS_IN_EP(Ep) ((Ep & 0x80) == 0x80) + +#ifndef PCD_USE_DBL_BUF + #define PCD_USE_DBL_BUF 0 +#endif + +#ifndef PCD_DEF_BUF + #if PCD_USE_DBL_BUF + #define PCD_DEF_BUF PCD_DBL_BUF + #else + #define PCD_DEF_BUF PCD_SNG_BUF + #endif +#endif + +#if defined(USB_OTG_FS) + // words not bytes + #define USB_ABS_EP0_SIZE (11 + ((USB_EP0_SIZE / 4) + 1) * 2) + #define USB_ABS_EP0_TX_SIZE (USB_EP0_SIZE / 4) + #define USB_ABS_EP_SIZE (USB_EP_SIZE / 4) +#elif defined(USB) + #define USB_ABS_EP0_SIZE (PCD_USE_DBL_BUF ? USB_EP0_SIZE * 2 : USB_EP0_SIZE) + #define USB_ABS_EP0_TX_SIZE (PCD_USE_DBL_BUF ? USB_EP0_SIZE * 2 : USB_EP0_SIZE) + #define USB_ABS_EP_SIZE (PCD_USE_DBL_BUF ? USB_EP_SIZE * 2 : USB_EP_SIZE) +#else + #error "unsupported USB" +#endif + +#if defined(USB_OTG_FS) + #define PMA_MAX_SIZE 320 +#elif defined(USB) + #define PMA_BASE_ADDR (4 * 2 * USB_MAX_EPS) + #ifndef PMA_MAX_SIZE + #define PMA_MAX_SIZE (512 - PMA_BASE_ADDR) + #endif +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct { + uint32_t ep_num; /* Endpoint number+direction */ + uint32_t ep_type; /* Endpoint type */ +} ep_desc_t; + +void USB_EP_ClearEndpoints(); +uint8_t USB_EP_AddEndpoint(uint32_t ep_type); +uint_fast8_t USB_EP_GetNumEndpoints(); +const ep_desc_t *USB_EP_GetEndpointsSlots(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/USBDevice/inc/usbd_conf.h b/libraries/USBDevice/inc/usbd_conf.h index a6bac515dd..77b9872e11 100644 --- a/libraries/USBDevice/inc/usbd_conf.h +++ b/libraries/USBDevice/inc/usbd_conf.h @@ -35,7 +35,7 @@ extern "C" { #if defined(USE_USB_HS) && !defined(USB_OTG_HS) #error "This board does not support USB High Speed! Select 'Full Speed' in the 'Tools->USB interface' menu" #endif -#if !defined(USB_BASE) && !defined(USB_OTG_FS) && defined(USB_OTG_HS) && !defined(USE_USB_HS) +#if !defined(USB_BASE) && !defined(USB_OTG_FS) && defined(USB_OTG_HS) && !defined(USE_USB_HS) && !defined(PLUGGABLE_USB_ENABLED) #error "This board support only USB High Speed! Select 'High Speed' or 'High Speed in Full Speed mode' in the 'Tools->USB interface' menu" #endif @@ -96,7 +96,11 @@ extern "C" { #endif #ifndef USBD_MAX_NUM_INTERFACES +#if defined(PLUGGABLE_USB_ENABLED) +#define USBD_MAX_NUM_INTERFACES 5U +#else #define USBD_MAX_NUM_INTERFACES 2U +#endif #endif /* USBD_MAX_NUM_INTERFACES */ #ifndef USBD_MAX_NUM_CONFIGURATION diff --git a/libraries/USBDevice/inc/usbd_desc.h b/libraries/USBDevice/inc/usbd_desc.h index aa832b2772..6eb972c733 100644 --- a/libraries/USBDevice/inc/usbd_desc.h +++ b/libraries/USBDevice/inc/usbd_desc.h @@ -20,7 +20,7 @@ /* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __USBD_DESC_H #define __USBD_DESC_H -#ifdef USBCON +#if defined(USBCON) /* Includes ------------------------------------------------------------------*/ #include "usbd_def.h" diff --git a/libraries/USBDevice/inc/usbd_ep_conf.h b/libraries/USBDevice/inc/usbd_ep_conf.h index 354b6f4d0c..7d78486798 100644 --- a/libraries/USBDevice/inc/usbd_ep_conf.h +++ b/libraries/USBDevice/inc/usbd_ep_conf.h @@ -20,7 +20,7 @@ #ifndef __USBD_EP_CONF_H #define __USBD_EP_CONF_H -#ifdef USBCON +#if defined(USBCON) && !defined(PLUGGABLE_USB_ENABLED) #include #include "usbd_def.h" @@ -86,4 +86,4 @@ extern const ep_desc_t ep_def[DEV_NUM_EP + 1]; #endif /* USBCON */ #endif /* __USBD_EP_CONF_H */ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ \ No newline at end of file +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/libraries/USBDevice/inc/usbd_if.h b/libraries/USBDevice/inc/usbd_if.h index b21a328db0..7da0b5d1e1 100644 --- a/libraries/USBDevice/inc/usbd_if.h +++ b/libraries/USBDevice/inc/usbd_if.h @@ -24,7 +24,7 @@ extern "C" { #endif void USBD_reenumerate(void); -#ifdef USBD_USE_CDC +#if defined(USBD_USE_CDC) && !defined(PLUGGABLE_USB_ENABLED) void USBD_CDC_init(void); #endif diff --git a/libraries/USBDevice/src/PluggableUSB/HID.cpp b/libraries/USBDevice/src/PluggableUSB/HID.cpp new file mode 100644 index 0000000000..f654011fb3 --- /dev/null +++ b/libraries/USBDevice/src/PluggableUSB/HID.cpp @@ -0,0 +1,174 @@ +/* + Copyright (c) 2015, Arduino LLC + Original code (pre-library): Copyright (c) 2011, Peter Barrett + * Modified by Levi Gillis @ 2022-2025 + Changes can be found in the git repo https://github.com/Levi--G/USBLibrarySTM32 + + 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. + */ + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "HID.h" + +HID_ &HID() +{ + static HID_ obj; + return obj; +} + +int HID_::getInterface(uint8_t *interfaceCount) +{ + *interfaceCount += 1; // uses 1 + HIDDescriptor hidInterface = { + D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE), + D_HIDREPORT(descriptorSize), + D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) + }; + return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); +} + +int HID_::getDescriptor(USBSetup &setup) +{ + // Check if this is a HID Class Descriptor request + if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { + return 0; + } + if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { + return 0; + } + + // In a HID Class Descriptor wIndex contains the interface number + if (setup.wIndex != pluggedInterface) { + return 0; + } + + int total = 0; + HIDSubDescriptor *node; + for (node = rootNode; node; node = node->next) { + int res = USB_SendControl(TRANSFER_PGM, node->data, node->length); + if (res == -1) { + return -1; + } + total += res; + } + + // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol + // due to the USB specs, but Windows and Linux just assumes its in report mode. + protocol = HID_REPORT_PROTOCOL; + + return total; +} + +uint8_t HID_::getShortName(char *name) +{ + name[0] = 'H'; + name[1] = 'I'; + name[2] = 'D'; + name[3] = 'A' + (descriptorSize & 0x0F); + name[4] = 'A' + ((descriptorSize >> 4) & 0x0F); + return 5; +} + +void HID_::AppendDescriptor(HIDSubDescriptor *node) +{ + if (!rootNode) { + rootNode = node; + } else { + HIDSubDescriptor *current = rootNode; + while (current->next) { + current = current->next; + } + current->next = node; + } + descriptorSize += node->length; +} + +int HID_::SendReport(uint8_t id, const void *data, int len) +{ + // auto ret = USB_Send(pluggedEndpoint, &id, 1); + // if (ret < 0) + // return ret; + // auto ret2 = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, len); + // if (ret2 < 0) + // return ret2; + // return ret + ret2; + uint8_t p[len + 1]; + p[0] = id; + memcpy(&p[1], data, len); + return USB_Send(pluggedEndpoint | TRANSFER_RELEASE, p, len + 1); +} + +bool HID_::setup(USBSetup &setup) +{ + if (pluggedInterface != setup.wIndex) { + return false; + } + + uint8_t request = setup.bRequest; + uint8_t requestType = setup.bmRequestType; + + if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) { + if (request == HID_GET_REPORT) { + // TODO: HID_GetReport(); + return true; + } + if (request == HID_GET_PROTOCOL) { + // TODO: Send8(protocol); + return true; + } + if (request == HID_GET_IDLE) { + // TODO: Send8(idle); + } + } + + if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { + if (request == HID_SET_PROTOCOL) { + // The USB Host tells us if we are in boot or report mode. + // This only works with a real boot compatible device. + protocol = setup.wValueL; + return true; + } + if (request == HID_SET_IDLE) { + idle = setup.wValueL; + return true; + } + if (request == HID_SET_REPORT) { + // uint8_t reportID = setup.wValueL; + // uint16_t length = setup.wLength; + // uint8_t data[length]; + // Make sure to not read more data than USB_EP_SIZE. + // You can read multiple times through a loop. + // The first byte (may!) contain the reportID on a multreport. + // USB_RecvControl(data, length); + } + } + + return false; +} + +HID_::HID_(void) : PluggableUSBModule(1, 1, epType), + rootNode(NULL), descriptorSize(0), + protocol(HID_REPORT_PROTOCOL), idle(1) +{ + epType[0] = EP_TYPE_INTERRUPT_IN; + PluggableUSB().plug(this); +} + +int HID_::begin(void) +{ + return 0; +} + +#endif /* if defined(USBCON) */ diff --git a/libraries/USBDevice/src/PluggableUSB/PluggableUSB.cpp b/libraries/USBDevice/src/PluggableUSB/PluggableUSB.cpp new file mode 100644 index 0000000000..08db13ad01 --- /dev/null +++ b/libraries/USBDevice/src/PluggableUSB/PluggableUSB.cpp @@ -0,0 +1,145 @@ +/* + PluggableUSB.cpp + Copyright (c) 2015 Arduino LLC + * Modified by Levi Gillis @ 2022-2025 + Changes can be found in the git repo https://github.com/Levi--G/USBLibrarySTM32 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "PluggableUSB.h" +#include "USBAPI.h" +#include "USB_EP_conf.h" + +int PluggableUSB_::getInterface(uint8_t *interfaceCount) +{ + int sent = 0; + PluggableUSBModule *node; + for (node = rootNode; node; node = node->next) { + int res = node->getInterface(interfaceCount); + if (res < 0) { + return -1; + } + sent += res; + } + return sent; +} + +int PluggableUSB_::getDescriptor(USBSetup &setup) +{ + PluggableUSBModule *node; + for (node = rootNode; node; node = node->next) { + int ret = node->getDescriptor(setup); + // ret!=0 -> request has been processed + if (ret) { + return ret; + } + } + return 0; +} + +uint8_t PluggableUSB_::getShortName(char *iSerialNum) +{ + uint8_t total = 0; + PluggableUSBModule *node; + for (node = rootNode; node; node = node->next) { + total += node->getShortName(iSerialNum + total); + } + *iSerialNum = 0; + return total; +} + +bool PluggableUSB_::setup(USBSetup &setup) +{ + PluggableUSBModule *node; + for (node = rootNode; node; node = node->next) { + if (node->setup(setup)) { + return true; + } + } + return false; +} + +bool PluggableUSB_::plug(PluggableUSBModule *node) +{ + if (!rootNode) { + rootNode = node; + } else { + PluggableUSBModule *current = rootNode; + while (current->next) { + current = current->next; + } + current->next = node; + } + + node->pluggedInterface = lastIf; + node->pluggedEndpoint = lastEp; + lastIf += node->getNumInterfaces(); + lastEp += node->getNumEndpoints(); + USB_PlugRoot(this); + if (USB_Running()) { + USB_End(); + USB_Begin(); + } + return true; +} + +PluggableUSB_ &PluggableUSB() +{ + static PluggableUSB_ obj; + return obj; +} + +PluggableUSB_::PluggableUSB_() : PluggableUSBModule(0, 0, nullptr), + lastIf(0), + lastEp(1), + rootNode(NULL) +{ + // Empty +} + +uint8_t PluggableUSB_::getNumEndpoints() +{ + uint8_t total = 0; + PluggableUSBModule *node; + for (node = rootNode; node; node = node->next) { + total += node->getNumEndpoints(); + } + return total; +} + +uint8_t PluggableUSB_::getNumInterfaces() +{ + uint8_t total = 0; + PluggableUSBModule *node; + for (node = rootNode; node; node = node->next) { + total += node->getNumInterfaces(); + } + return total; +} + +uint8_t PluggableUSB_::getEndpointTypes(uint8_t *types) +{ + uint8_t total = 0; + PluggableUSBModule *node; + for (node = rootNode; node; node = node->next) { + total += node->getEndpointTypes(types + total); + } + return total; +} + +#endif /* if defined(USBCON) */ diff --git a/libraries/USBDevice/src/PluggableUSB/USBCDC.cpp b/libraries/USBDevice/src/PluggableUSB/USBCDC.cpp new file mode 100644 index 0000000000..f4f5a3d14d --- /dev/null +++ b/libraries/USBDevice/src/PluggableUSB/USBCDC.cpp @@ -0,0 +1,344 @@ +/* + Copyright (c) 2015 Arduino LLC. All right reserved. + * Modified by Levi Gillis @ 2022-2025 + Changes can be found in the git repo https://github.com/Levi--G/USBLibrarySTM32 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "USBCDC.h" +#include "USBAPI.h" +#include + +#include +#include +#include + +// #include "core_cm3.h" + +#define CDC_SERIAL_BUFFER_SIZE 256 + +/* For information purpose only since RTS is not always handled by the terminal application */ +#define CDC_LINESTATE_DTR 0x01 // Data Terminal Ready +#define CDC_LINESTATE_RTS 0x02 // Ready to Send + +#define CDC_LINESTATE_READY (CDC_LINESTATE_RTS | CDC_LINESTATE_DTR) + +typedef struct { + uint32_t dwDTERate; + uint8_t bCharFormat; + uint8_t bParityType; + uint8_t bDataBits; + uint8_t lineState; +} LineInfo; + +static volatile LineInfo _usbLineInfo = { + 115200, // dWDTERate + 0x00, // bCharFormat + 0x00, // bParityType + 0x08, // bDataBits + 0x00 // lineState +}; + +static volatile int32_t breakValue = -1; + +// CDC +#define CDC_ACM_INTERFACE pluggedInterface // CDC ACM +#define CDC_DATA_INTERFACE uint8_t(pluggedInterface + 1) // CDC Data + +#if USB_SERIAL_USE_ACM_EP + #define CDC_ENDPOINT_ACM pluggedEndpoint + 2 +#endif +#define CDC_ENDPOINT_OUT pluggedEndpoint + 1 +#define CDC_ENDPOINT_IN pluggedEndpoint + +#define CDC_RX CDC_ENDPOINT_OUT +#define CDC_TX CDC_ENDPOINT_IN + +int USBCDC::getInterface(uint8_t *interfaceNum) +{ + interfaceNum[0] += 2; // uses 2 + CDCDescriptor _cdcInterface = { + D_IAD(pluggedInterface, 2, CDC_COMMUNICATION_INTERFACE_CLASS, CDC_ABSTRACT_CONTROL_MODEL, 0), + + // CDC communication interface +#if USB_SERIAL_USE_ACM_EP + D_INTERFACE(CDC_ACM_INTERFACE, 1, CDC_COMMUNICATION_INTERFACE_CLASS, CDC_ABSTRACT_CONTROL_MODEL, 0), +#else + D_INTERFACE(CDC_ACM_INTERFACE, 0, CDC_COMMUNICATION_INTERFACE_CLASS, CDC_ABSTRACT_CONTROL_MODEL, 0), +#endif + D_CDCCS(CDC_HEADER, CDC_V1_10 & 0xFF, (CDC_V1_10 >> 8) & 0x0FF), // Header (1.10 bcd) + + D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT, 6), // SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported + D_CDCCS(CDC_UNION, CDC_ACM_INTERFACE, CDC_DATA_INTERFACE), // Communication interface is master, data interface is slave 0 + D_CDCCS(CDC_CALL_MANAGEMENT, 1, 1), // Device handles call management (not) +#if USB_SERIAL_USE_ACM_EP + D_ENDPOINT(USB_ENDPOINT_IN(CDC_ENDPOINT_ACM), USB_ENDPOINT_TYPE_INTERRUPT, 0x10, 0x10), +#endif + // CDC data interface + D_INTERFACE(CDC_DATA_INTERFACE, 2, CDC_DATA_INTERFACE_CLASS, 0, 0), + D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT), USB_ENDPOINT_TYPE_BULK, EPX_SIZE, 0), + D_ENDPOINT(USB_ENDPOINT_IN(CDC_ENDPOINT_IN), USB_ENDPOINT_TYPE_BULK, EPX_SIZE, 0) + }; + + return USB_SendControl(0, &_cdcInterface, sizeof(_cdcInterface)); +} + +int USBCDC::getDescriptor(USBSetup & /* setup */) +{ + return 0; +} + +static void utox8(uint32_t val, char *s) +{ + for (int i = 0; i < 8; i++) { + int d = val & 0XF; + val = (val >> 4); + + s[7 - i] = d > 9 ? 'A' + d - 10 : '0' + d; + } +} + +uint8_t USBCDC::getShortName(char *name) +{ + // from section 9.3.3 of the datasheet +#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t *)(0x0080A00C) +#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t *)(0x0080A040) +#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t *)(0x0080A044) +#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t *)(0x0080A048) + + utox8(SERIAL_NUMBER_WORD_0, &name[0]); + utox8(SERIAL_NUMBER_WORD_1, &name[8]); + utox8(SERIAL_NUMBER_WORD_2, &name[16]); + utox8(SERIAL_NUMBER_WORD_3, &name[24]); + return 32; +} + +void USBCDC::handleEndpoint(int /* ep */) +{ +} + +bool USBCDC::setup(USBSetup &setup) +{ + uint8_t requestType = setup.bmRequestType; + uint8_t r = setup.bRequest; + uint8_t i = setup.wIndex; + + if (CDC_ACM_INTERFACE != i) { + return false; + } + + if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) { + if (r == CDC_GET_LINE_CODING) { + USB_SendControl(0, (void *)&_usbLineInfo, 7); + return true; + } + } + + if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { + + if (r == CDC_SET_LINE_CODING) { + USB_RecvControl((void *)&_usbLineInfo, 7); + } + + if (r == CDC_SET_CONTROL_LINE_STATE) { + _usbLineInfo.lineState = setup.wValueL; + } + + if (r == CDC_SET_LINE_CODING || r == CDC_SET_CONTROL_LINE_STATE) { + // TODO reset + // NVIC_SystemReset(); + // USB_SendZLP(0); + } + + if (CDC_SEND_BREAK == r) { + breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; + USB_SendZLP(0); + } + return true; + } + return false; +} + +USBCDC::USBCDC() : PluggableUSBModule(2, 2, epType), stalled(false) +{ + epType[0] = USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN(0); + epType[1] = USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_OUT(0); +#if USB_SERIAL_USE_ACM_EP + epType[2] = USB_ENDPOINT_TYPE_INTERRUPT | USB_ENDPOINT_IN(0); +#endif + PluggableUSB().plug(this); +} + +void USBCDC::begin(uint32_t /* baud_count */) +{ + // uart config is ignored in USB-CDC +} + +void USBCDC::begin(uint32_t /* baud_count */, uint8_t /* config */) +{ + // uart config is ignored in USB-CDC +} + +void USBCDC::end(void) +{ + memset((void *)&_usbLineInfo, 0, sizeof(_usbLineInfo)); +} + +int _serialPeek = -1; + +int USBCDC::available(void) +{ + return USB_Available(CDC_ENDPOINT_OUT) + (_serialPeek != -1); +} + +int USBCDC::availableForWrite(void) +{ + return USB_SendAvailable(CDC_ENDPOINT_OUT) ? USB_EP_SIZE : 0; +} + +int USBCDC::peek(void) +{ + if (_serialPeek != -1) { + return _serialPeek; + } + _serialPeek = read(); + return _serialPeek; +} + +int USBCDC::read(void) +{ + if (_serialPeek != -1) { + int res = _serialPeek; + _serialPeek = -1; + return res; + } + return USB_Recv(CDC_ENDPOINT_OUT); +} + +size_t USBCDC::readBytes(char *buffer, size_t length) +{ + size_t count = 0; + _startMillis = millis(); + while (count < length) { + uint32_t n = USB_Recv(CDC_ENDPOINT_OUT, buffer + count, length - count); + if (n == 0 && (millis() - _startMillis) >= _timeout) { + break; + } + count += n; + } + return count; +} + +void USBCDC::flush(void) +{ + USB_Flush(CDC_ENDPOINT_IN); +} + +size_t USBCDC::write(const uint8_t *buffer, size_t size) +{ + uint32_t r = USB_Send(CDC_ENDPOINT_IN, buffer, size); + + if (r > 0) { + return r; + } else { + setWriteError(); + return 0; + } +} + +size_t USBCDC::write(uint8_t c) +{ + return write(&c, 1); +} + +// This operator is a convenient way for a sketch to check whether the +// port has actually been configured and opened by the host (as opposed +// to just being connected to the host). It can be used, for example, in +// setup() before printing to ensure that an application on the host is +// actually ready to receive and display the data. +// We add a short delay before returning to fix a bug observed by Federico +// where the port is configured (lineState != 0) but not quite opened. +USBCDC::operator bool() +{ + // this is here to avoid spurious opening after upload + if (millis() < 500) { + return false; + } + + bool result = false; + + if (_usbLineInfo.lineState > 0) { + result = true; + } + + delay(10); + return result; +} + +int32_t USBCDC::readBreak() +{ + uint8_t enableInterrupts = ((__get_PRIMASK() & 0x1) == 0); + + // disable interrupts, + // to avoid clearing a breakValue that might occur + // while processing the current break value + __disable_irq(); + + int32_t ret = breakValue; + + breakValue = -1; + + if (enableInterrupts) { + // re-enable the interrupts + __enable_irq(); + } + + return ret; +} + +unsigned long USBCDC::baud() +{ + return _usbLineInfo.dwDTERate; +} + +uint8_t USBCDC::stopbits() +{ + return _usbLineInfo.bCharFormat; +} + +uint8_t USBCDC::paritytype() +{ + return _usbLineInfo.bParityType; +} + +uint8_t USBCDC::numbits() +{ + return _usbLineInfo.bDataBits; +} + +bool USBCDC::dtr() +{ + return ((_usbLineInfo.lineState & CDC_LINESTATE_DTR) == CDC_LINESTATE_DTR); +} + +bool USBCDC::rts() +{ + return ((_usbLineInfo.lineState & CDC_LINESTATE_RTS) == CDC_LINESTATE_RTS); +} + +#endif /* USBCON */ diff --git a/libraries/USBDevice/src/PluggableUSB/USBCore.cpp b/libraries/USBDevice/src/PluggableUSB/USBCore.cpp new file mode 100644 index 0000000000..c734a1a50d --- /dev/null +++ b/libraries/USBDevice/src/PluggableUSB/USBCore.cpp @@ -0,0 +1,823 @@ +/* + * USBCore.c + * Template generated with Stm32CubeMX "usbd_customhid.c": + * Copyright (c) 2015 STMicroelectronics. + * All rights reserved. + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + * Implementation/modification: + * Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the GNU Lesser General Public License v3.0 license. + */ + +#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +/* Includes ------------------------------------------------------------------*/ +#include "Arduino.h" +#include "PacketBuffer.h" +#include "PluggableUSB.h" +#include "USBAPI.h" +#include "USBCore_stm32.h" +#include "USB_EP_conf.h" +#include "usbd_desc.h" + +static uint8_t USBD_HID_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_HID_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_HID_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static bool Init_Endpoints(); + +static uint8_t *USBD_HID_GetFSCfgDesc(uint16_t *length); +static uint8_t *USBD_HID_GetHSCfgDesc(uint16_t *length); +static uint8_t *USBD_HID_GetOtherSpeedCfgDesc(uint16_t *length); +static uint8_t *USBD_HID_GetDeviceQualifierDesc(uint16_t *length); +static uint8_t USBD_HID_EP0_RxReady(USBD_HandleTypeDef *pdev); +static uint8_t USBD_HID_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_HID_DataOut(USBD_HandleTypeDef *pdev, uint8_t endp); + +static bool PrepareReceive(USBD_HandleTypeDef *pdev, USBD_HID_HandleTypeDef *hhid, uint8_t ep); + +USBD_ClassTypeDef USBD_HID_CLASS = { + USBD_HID_Init, + USBD_HID_DeInit, + USBD_HID_Setup, + NULL, /* EP0_TxSent */ + USBD_HID_EP0_RxReady, /* EP0_RxReady */ + USBD_HID_DataIn, /* DataIn */ + USBD_HID_DataOut, /* DataOut */ + NULL, /* SOF */ + NULL, + NULL, + USBD_HID_GetHSCfgDesc, + USBD_HID_GetFSCfgDesc, + USBD_HID_GetOtherSpeedCfgDesc, + USBD_HID_GetDeviceQualifierDesc, +}; + +/* USB Standard Device Descriptor */ +__ALIGN_BEGIN static uint8_t USBD_HID_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END = { + USB_LEN_DEV_QUALIFIER_DESC, + USB_DESC_TYPE_DEVICE_QUALIFIER, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x40, + 0x01, + 0x00, +}; + +/* Buffers for properly handling interface and report descriptors */ +#ifndef USB_CFGBUFFER_LEN + #define USB_CFGBUFFER_LEN 128 +#endif +__ALIGN_BEGIN uint8_t tempcfgBuffer[USB_CFGBUFFER_LEN] __ALIGN_END; +uint8_t tempcfgbufferpos = 0; +bool cfgBufferMode = false; +__ALIGN_BEGIN uint8_t tempdescBuffer[USB_CFGBUFFER_LEN] __ALIGN_END; +uint8_t tempdescbufferpos = 0; +bool descBufferMode = false; + +/* RX buffers */ +PacketBuffer *EP_Buffers[USB_MAX_EPS]; +uint16_t Recv_EP0 = false; +USBD_SetupReqTypedef EP0Setup; + +/* USB Device Core HID composite handle declaration */ +USBD_HandleTypeDef hUSBD_Device_HID_Handle; + +bool HID_initialized = false; +PluggableUSBModule *rootModule; + +bool USB_Begin() +{ + if (HID_initialized || rootModule == NULL) { + return false; + } + + if (!Init_Endpoints()) { + return false; + } + + /* Init Device Library */ + if (USBD_Init(&hUSBD_Device_HID_Handle, &USBD_Desc, 0) != USBD_OK) { + return false; + } + /* Add Supported Class */ + if (USBD_RegisterClass(&hUSBD_Device_HID_Handle, &USBD_HID_CLASS) != USBD_OK) { + return false; + } + /* Start Device Process */ + if (USBD_Start(&hUSBD_Device_HID_Handle) != USBD_OK) { + return false; + } + HID_initialized = true; + return true; +} + +bool GetHHID(USBD_HID_HandleTypeDef *&hhid) +{ + hhid = (USBD_HID_HandleTypeDef *)hUSBD_Device_HID_Handle.pClassData; + return hhid != NULL; +} + +bool USB_Running() +{ + USBD_HID_HandleTypeDef *hhid; + return GetHHID(hhid); +} + +bool USB_Connected() +{ + return hUSBD_Device_HID_Handle.dev_state == USBD_STATE_CONFIGURED; +} + +void USB_End() +{ + if (HID_initialized) { + /* Stop Device Process */ + USBD_Stop(&hUSBD_Device_HID_Handle); + /* DeInit Device Library */ + USBD_DeInit(&hUSBD_Device_HID_Handle); + HID_initialized = false; + } +} + +void USB_PlugRoot(PluggableUSBModule *root) +{ + bool wasinit = HID_initialized; + if (wasinit) { + USB_End(); + } + rootModule = root; + if (wasinit) { + USB_Begin(); + } +} +int PLUG_GetInterface(uint8_t *interfaceCount) +{ + return rootModule->getInterface(interfaceCount); +} +int PLUG_GetDescriptor(USBSetup &setup) +{ + return rootModule->getDescriptor(setup); +} +bool PLUG_Setup(USBSetup &setup) +{ + return rootModule->setup(setup); +} +uint8_t PLUG_GetNumEndpoints() +{ + return rootModule->getNumEndpoints(); +} +uint8_t PLUG_GetNumInterfaces() +{ + return rootModule->getNumInterfaces(); +} +uint8_t PLUG_GetEndpointTypes(uint8_t *types) +{ + return rootModule->getEndpointTypes(types); +} + +static bool Init_Endpoints() +{ + USB_EP_ClearEndpoints(); + uint8_t eps = PLUG_GetNumEndpoints(); + if (eps > USB_MAX_EPS) { + return false; + } + uint8_t epstypes[eps]; + if (eps != PLUG_GetEndpointTypes(epstypes)) { + return false; + } + for (uint_fast8_t i = 0; i < eps; i++) { + if (!USB_EP_AddEndpoint(epstypes[i])) { + return false; + } + } + return true; +} + +USBD_EndpointTypeDef *GetEPTypeDef(uint8_t ep, bool in) +{ + if (in) { + return &hUSBD_Device_HID_Handle.ep_in[SMALL_EP(ep)]; + } + return &hUSBD_Device_HID_Handle.ep_out[SMALL_EP(ep)]; +} + +static bool USB_SendAvailable_Internal(USBD_HID_HandleTypeDef *&hhid, uint8_t endp) +{ + uint8_t ep = SMALL_EP(endp); +#if PACKETBUFFER_USE_TX_BUFFERS + if (EP_Buffers[ep] != NULL && (!EP_Buffers[ep]->isFull() || PACKETBUFFER_ALLOW_OVERWRITE)) { + return true; + } +#endif + return hUSBD_Device_HID_Handle.dev_state == USBD_STATE_CONFIGURED && hhid->EPstate[ep] != HID_BUSY; +} + +static bool USB_Flush_Internal(USBD_HID_HandleTypeDef *&hhid, uint8_t endp) +{ + if (USB_SendAvailable_Internal(hhid, endp)) { + return true; + } + uint8_t ep = SMALL_EP(endp); + uint8_t EP = IN_EP(endp); +#if USB_WRITE_TIMEOUT + uint32_t start = millis(); + while (hhid->EPstate[ep] == HID_BUSY && millis() - start < USB_WRITE_TIMEOUT) { + delay(1); + } +#else + while (hhid->EPstate[ep] == HID_BUSY) { + delay(1); + } +#endif + USBD_LL_FlushEP(&hUSBD_Device_HID_Handle, EP); + return USB_SendAvailable_Internal(hhid, endp); +} + +bool USB_SendAvailable(uint8_t endp) +{ + USBD_HID_HandleTypeDef *hhid; + if (!GetHHID(hhid)) { + return false; + } + return USB_SendAvailable_Internal(hhid, endp); +} + +#if PACKETBUFFER_USE_TX_BUFFERS +static bool USB_SendTXBufferUnsafe(USBD_HandleTypeDef *pdev, USBD_HID_HandleTypeDef *hhid, uint8_t ep) +{ + if (EP_Buffers[ep] && !EP_Buffers[ep]->isEmpty()) { + hhid->EPstate[ep] = HID_BUSY; + uint32_t len = USB_EP_SIZE; + auto buf = EP_Buffers[ep]->PrepareRead(len); + pdev->ep_in[ep].total_length = len; + (void)USBD_LL_Transmit(pdev, ep, buf, len); + return true; + } + return false; +} +#endif + +int USB_SendQuick(uint8_t endp, const void *data, int len) +{ + USBD_HID_HandleTypeDef *hhid; + if (!GetHHID(hhid)) { + return 0; + } + if (!USB_SendAvailable_Internal(hhid, endp) && !USB_Flush_Internal(hhid, endp)) { + return 0; + } + uint8_t ep = SMALL_EP(endp); + uint8_t EP = IN_EP(endp); +#if PACKETBUFFER_USE_TX_BUFFERS + auto buffer = EP_Buffers[ep]; + if (buffer == NULL) { + return 0; + } + auto write = buffer->Write((uint8_t *)data, len); + if (hhid->EPstate[ep] == HID_IDLE) { + USB_SendTXBufferUnsafe(&hUSBD_Device_HID_Handle, hhid, ep); + } + return write; +#else + if (hhid->EPstate[ep] == HID_IDLE) { + hhid->EPstate[ep] = HID_BUSY; + hUSBD_Device_HID_Handle.ep_in[ep].total_length = len; + (void)USBD_LL_Transmit(&hUSBD_Device_HID_Handle, EP, (uint8_t *)data, len); + return len; + } + return 0; +#endif +} + +int USB_Send(uint8_t endp, const void *data, int len) +{ + USBD_HID_HandleTypeDef *hhid; + if (GetHHID(hhid)) { + int ret = USB_SendQuick(endp, data, len); + // USB_Send is blocking in the original avr implementation + USB_Flush_Internal(hhid, endp); + return ret; + } + return 0; +} + +int USB_SendControl(uint8_t flags, const void *d, int len) +{ + if (cfgBufferMode) { + if (tempcfgbufferpos + len < USB_CFGBUFFER_LEN) { + uint8_t cpy = 0; + uint8_t *ptr = (uint8_t *)d; + for (uint8_t i = 0; i < len;) { + if (ptr[i] == 0) { + i++; + } else if (ptr[i] + i <= len) { + memcpy(tempcfgBuffer + tempcfgbufferpos, ptr + i, ptr[i]); + tempcfgbufferpos += ptr[i]; + cpy += ptr[i]; + i += ptr[i]; + } + } + return cpy; + } + return 0; + } + if (descBufferMode) { + if (tempdescbufferpos + len < USB_CFGBUFFER_LEN) { + memcpy(tempdescBuffer + tempdescbufferpos, d, len); + tempdescbufferpos += len; + return len; + } + return 0; + } + if (!USB_Running()) { + return 0; + } + return (USBD_CtlSendData(&hUSBD_Device_HID_Handle, (uint8_t *)d, len) == USBD_OK) ? len : 0; +} + +uint8_t USB_SendSpace(uint8_t endp) +{ + return USB_SendAvailable(endp) ? (endp == 0 ? USB_EP0_SIZE : USB_EP_SIZE) : 0; +} + +int USB_SendZLP(uint8_t endp) +{ + return USB_Send(endp, NULL, 0); +} + +void USB_Flush(uint8_t endp) +{ + USBD_HID_HandleTypeDef *hhid; + if (GetHHID(hhid)) { + USB_Flush_Internal(hhid, endp); + } +} + +uint8_t USB_Available(uint8_t endp) +{ + auto buffer = EP_Buffers[SMALL_EP(endp)]; + if (!buffer) { + return 0; + } + return buffer->available(); +} + +int USB_Recv(uint8_t endp, void *data, int len) +{ + if (len <= 0) { + return 0; + } + uint8_t ep = SMALL_EP(endp); + if (ep == 0) { + return USB_RecvControl(data, len); + } + USBD_HID_HandleTypeDef *hhid; + if (!GetHHID(hhid)) { + return 0; + } + auto buffer = EP_Buffers[ep]; + if (buffer == NULL) { + return 0; + } + uint8_t read = buffer->Read((uint8_t *)data, len); + PrepareReceive(&hUSBD_Device_HID_Handle, hhid, ep); + return read; +} + +int USB_Recv(uint8_t endp) +{ + uint8_t c; + if (USB_Recv(endp, &c, 1) != 1) { + return -1; + } + return c; +} + +int USB_RecvControl(void *data, int len) +{ + if (len <= 0) { + return 0; + } + auto buffer = EP_Buffers[0]; + if (buffer == NULL) { + return 0; + } + return buffer->Read((uint8_t *)data, len); +} + +/** + * @brief USBD_HID_Init + * Initialize the HID interface + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static uint8_t USBD_HID_Init(USBD_HandleTypeDef *pdev, + uint8_t cfgidx) +{ + UNUSED(cfgidx); + + USBD_HID_HandleTypeDef *hhid; + + hhid = (USBD_HID_HandleTypeDef *)USBD_malloc(sizeof(USBD_HID_HandleTypeDef)); + + if (hhid == NULL) { + pdev->pClassData = NULL; + return (uint8_t)USBD_EMEM; + } + + (void)USBD_memset(hhid, 0, sizeof(USBD_HID_HandleTypeDef)); + + pdev->pClassData = (void *)hhid; + + EP_Buffers[0] = new SplitPacketBuffer(); + + uint_fast8_t eps = USB_EP_GetNumEndpoints(); + const ep_desc_t *epdefs = USB_EP_GetEndpointsSlots(); + + for (uint_fast8_t i = 0; i < eps; i++) { + uint8_t EP = epdefs[i].ep_num; + uint8_t ep = SMALL_EP(EP); + USBD_EndpointTypeDef *eptdef = GetEPTypeDef(EP, IS_IN_EP(EP)); + eptdef->bInterval = ((pdev->dev_speed == USBD_SPEED_HIGH) ? HID_HS_BINTERVAL : HID_FS_BINTERVAL); + USBD_LL_OpenEP(pdev, EP, epdefs[i].ep_type, USB_EP_SIZE); + eptdef->is_used = 1U; + hhid->EPstate[ep] = HID_IDLE; + if (IS_IN_EP(EP)) { +#if PACKETBUFFER_USE_TX_BUFFERS + EP_Buffers[ep] = new SplitPacketBuffer(); +#endif + } else { + EP_Buffers[ep] = new SplitPacketBuffer(); + if (!PrepareReceive(pdev, hhid, ep)) { + return (uint8_t)USBD_EMEM; + } + } + } + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_HID_DeInit + * DeInitialize the HID layer + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static uint8_t USBD_HID_DeInit(USBD_HandleTypeDef *pdev, + uint8_t cfgidx) +{ + UNUSED(cfgidx); + + if (EP_Buffers[0]) { + delete EP_Buffers[0]; + EP_Buffers[0] = NULL; + } + + uint_fast8_t eps = USB_EP_GetNumEndpoints(); + const ep_desc_t *epdefs = USB_EP_GetEndpointsSlots(); + + for (uint_fast8_t i = 0; i < eps; i++) { + uint8_t EP = epdefs[i].ep_num; + uint8_t ep = SMALL_EP(EP); + USBD_EndpointTypeDef *epdef = GetEPTypeDef(ep, IS_IN_EP(EP)); + USBD_LL_CloseEP(pdev, EP); + epdef->is_used = 0U; + epdef->bInterval = 0U; + if (EP_Buffers[ep]) { + delete EP_Buffers[ep]; + EP_Buffers[ep] = NULL; + } + } + + /* Free allocated memory */ + if (pdev->pClassData != NULL) { + (void)USBD_free(pdev->pClassData); + pdev->pClassData = NULL; + } + + return (uint8_t)USBD_OK; +} + +USBD_StatusTypeDef USBD_HID_GetDescriptor(USBD_HandleTypeDef *pdev, + USBD_SetupReqTypedef *req) +{ + USBSetup setup = {req->bmRequest, req->bRequest, lowByte(req->wValue), highByte(req->wValue), req->wIndex, req->wLength}; + tempdescbufferpos = 0; + descBufferMode = true; + auto result = PLUG_GetDescriptor(setup); + descBufferMode = false; + + if (result && tempdescbufferpos > 0) { + USB_SendControl(0, tempdescBuffer, min((uint16_t)tempdescbufferpos, req->wLength)); + } + return USBD_OK; +} + +uint8_t HandleSetup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) +{ + USBD_HID_HandleTypeDef *hhid = (USBD_HID_HandleTypeDef *)pdev->pClassData; + USBD_StatusTypeDef ret = USBD_OK; + uint16_t len = 0U; + uint8_t *pbuf = NULL; + uint16_t status_info = 0U; + + switch (req->bmRequest & USB_REQ_TYPE_MASK) { + case USB_REQ_TYPE_CLASS: { + USBSetup setup = {req->bmRequest, req->bRequest, lowByte(req->wValue), highByte(req->wValue), req->wIndex, req->wLength}; + bool result = PLUG_Setup(setup); + if (result) { + ret = USBD_OK; + } else { + switch (req->bRequest) { + case HID_REQ_SET_PROTOCOL: + hhid->Protocol = (uint8_t)(req->wValue); + break; + + case HID_REQ_GET_PROTOCOL: + (void)USBD_CtlSendData(pdev, (uint8_t *)&hhid->Protocol, 1U); + break; + + case HID_REQ_SET_IDLE: + hhid->IdleState = (uint8_t)(req->wValue >> 8); + break; + + case HID_REQ_GET_IDLE: + (void)USBD_CtlSendData(pdev, (uint8_t *)&hhid->IdleState, 1U); + break; + + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + break; + } + } + break; + } + case USB_REQ_TYPE_STANDARD: + switch (req->bRequest) { + case USB_REQ_GET_STATUS: + if (pdev->dev_state == USBD_STATE_CONFIGURED) { + (void)USBD_CtlSendData(pdev, (uint8_t *)&status_info, 2U); + } else { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + case USB_REQ_GET_DESCRIPTOR: + ret = USBD_HID_GetDescriptor(pdev, req); + break; + + case USB_REQ_GET_INTERFACE: + if (pdev->dev_state == USBD_STATE_CONFIGURED) { + (void)USBD_CtlSendData(pdev, (uint8_t *)&hhid->AltSetting, 1U); + } else { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_SET_INTERFACE: + if (pdev->dev_state == USBD_STATE_CONFIGURED) { + hhid->AltSetting = (uint8_t)(req->wValue); + } else { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_CLEAR_FEATURE: + break; + + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + break; + } + break; + + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + break; + } + + return (uint8_t)ret; +} + +/** + * @brief USBD_HID_Setup + * Handle the HID specific requests + * @param pdev: instance + * @param req: usb requests + * @retval status + */ +static uint8_t USBD_HID_Setup(USBD_HandleTypeDef *pdev, + USBD_SetupReqTypedef *req) +{ + USBD_HID_HandleTypeDef *hhid = (USBD_HID_HandleTypeDef *)pdev->pClassData; + if (hhid == NULL) { + return (uint8_t)USBD_FAIL; + } + if (req->wLength && ((req->bmRequest & 0x80) == 0)) { + if (Recv_EP0) { + // transfer still in progress + return (uint8_t)USBD_FAIL; + } + EP0Setup = *req; + EP_Buffers[0]->clear(); + Recv_EP0 = min(req->wLength, (uint16_t)(PACKETBUFFER_COUNT * USB_EP0_SIZE)); + uint32_t len = Recv_EP0; + auto buf = EP_Buffers[0]->PrepareWrite(len); + USBD_CtlPrepareRx(&hUSBD_Device_HID_Handle, buf, len); + return (uint8_t)USBD_OK; + } + return HandleSetup(pdev, req); +} + +/** + * @brief USBD_HID_GetCfgFSDesc + * return FS configuration descriptor + * @param speed : current device speed + * @param length : pointer data length + * @retval pointer to descriptor buffer + */ +static uint8_t *USBD_HID_GetFSCfgDesc(uint16_t *length) +{ + /* Since this needs to be returned as pointer to an aligned array + we will store the "sent" configuration and send the filled buffer instead */ + auto confsize = USB_CONFIGUARTION_DESC_SIZE; + tempcfgbufferpos = confsize; + cfgBufferMode = true; + u8 interfaces = 0; + *length = PLUG_GetInterface(&interfaces); + *length += confsize; + cfgBufferMode = false; + ConfigDescriptor config = D_CONFIG(*length, interfaces); + memcpy(tempcfgBuffer, &config, confsize); + return tempcfgBuffer; +} + +/** + * @brief USBD_HID_GetCfgHSDesc + * return HS configuration descriptor + * @param speed : current device speed + * @param length : pointer data length + * @retval pointer to descriptor buffer + */ +static uint8_t *USBD_HID_GetHSCfgDesc(uint16_t *length) +{ + // should not happen/matter => return FS + return USBD_HID_GetFSCfgDesc(length); +} + +/** + * @brief USBD_HID_GetOtherSpeedCfgDesc + * return other speed configuration descriptor + * @param speed : current device speed + * @param length : pointer data length + * @retval pointer to descriptor buffer + */ +static uint8_t *USBD_HID_GetOtherSpeedCfgDesc(uint16_t *length) +{ + // should not happen/matter => return FS + return USBD_HID_GetFSCfgDesc(length); +} + +static uint8_t USBD_HID_HandleDataOut(USBD_HandleTypeDef *pdev, uint8_t endp) +{ + USBD_HID_HandleTypeDef *hhid; + if (!GetHHID(hhid)) { + return USBD_FAIL; + } + uint8_t ep = SMALL_EP(endp); + auto buffer = EP_Buffers[ep]; + if (buffer == NULL) { + // this should never happen; + return (uint8_t)USBD_FAIL; + } + buffer->CommitWrite(USBD_LL_GetRxDataSize(pdev, endp)); + hhid->EPstate[ep] = HID_IDLE; + if (ep != 0) { + PrepareReceive(pdev, hhid, ep); + } + return (uint8_t)USBD_OK; +} + +static uint8_t USBD_HID_EP0_RxReady(USBD_HandleTypeDef *pdev) +{ + if (Recv_EP0) { + if (USBD_HID_HandleDataOut(pdev, 0) != USBD_OK) { + return (uint8_t)USBD_FAIL; + } + auto read = EP_Buffers[0]->available(); + if (Recv_EP0 > read) { + uint32_t len = Recv_EP0 - read; + auto buf = EP_Buffers[0]->PrepareWrite(len); + USBD_CtlPrepareRx(pdev, buf, len); + return (uint8_t)USBD_OK; + } + Recv_EP0 = 0; + return (uint8_t)HandleSetup(pdev, &EP0Setup); + } + // this should not happen? + return (uint8_t)USBD_FAIL; +} + +/** + * @brief USBD_HID_DataIn + * handle data IN Stage + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t USBD_HID_DataIn(USBD_HandleTypeDef *pdev, + uint8_t endp) +{ + /* Ensure that the FIFO is empty before a new transfer, this condition could + be caused by a new transfer before the end of the previous transfer */ + + uint8_t ep = SMALL_EP(endp); + + USBD_HID_HandleTypeDef *hhid; + if (!GetHHID(hhid)) { + return (uint8_t)USBD_FAIL; + } + +#if PACKETBUFFER_USE_TX_BUFFERS + EP_Buffers[ep]->CommitRead(pdev->ep_in[ep].total_length); +#endif + + if (pdev->ep_in[ep].total_length > 0U && (pdev->ep_in[ep].total_length % pdev->ep_in[ep].maxpacket) == 0U) { + /* Update the packet total length */ + pdev->ep_in[ep].total_length = 0U; + + /* Send ZLP */ + (void)USBD_LL_Transmit(pdev, endp, NULL, 0U); + return (uint8_t)USBD_OK; + } +#if PACKETBUFFER_USE_TX_BUFFERS + if (!USB_SendTXBufferUnsafe(pdev, hhid, ep)) { + hhid->EPstate[ep] = HID_IDLE; + } + return (uint8_t)USBD_OK; +#else + hhid->EPstate[ep] = HID_IDLE; + return (uint8_t)USBD_OK; +#endif +} + +/** + * @brief USBD_CDC_DataOut + * Data received on non-control Out endpoint + * @param pdev: device instance + * @param epnum: endpoint number + * @retval status + */ +static uint8_t USBD_HID_DataOut(USBD_HandleTypeDef *pdev, uint8_t endp) +{ + return USBD_HID_HandleDataOut(pdev, endp); +} + +/// @param ep SMALL_EP(endp) +static bool PrepareReceive(USBD_HandleTypeDef *pdev, USBD_HID_HandleTypeDef *hhid, uint8_t ep) +{ + if (ep == 0) { + // this should never happen + return false; + } + if (hhid->EPstate[ep] == HID_BUSY) { + return true; + } + if (EP_Buffers[ep]->isFull() && !PACKETBUFFER_ALLOW_OVERWRITE) { + return false; + } + hhid->EPstate[ep] = HID_BUSY; + uint32_t len = USB_EP_SIZE; + auto buf = EP_Buffers[ep]->PrepareWrite(len); + USBD_LL_PrepareReceive(pdev, ep, buf, len); + return true; +} + +/** + * @brief DeviceQualifierDescriptor + * return Device Qualifier descriptor + * @param length : pointer data length + * @retval pointer to descriptor buffer + */ +static uint8_t *USBD_HID_GetDeviceQualifierDesc(uint16_t *length) +{ + // Serial1.println("DeviceQ"); + *length = (uint16_t)sizeof(USBD_HID_DeviceQualifierDesc); + return USBD_HID_DeviceQualifierDesc; +} +#endif /* USBCON */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/libraries/USBDevice/src/PluggableUSB/USB_EP_conf.cpp b/libraries/USBDevice/src/PluggableUSB/USB_EP_conf.cpp new file mode 100644 index 0000000000..9a274f9bcf --- /dev/null +++ b/libraries/USBDevice/src/PluggableUSB/USB_EP_conf.cpp @@ -0,0 +1,51 @@ +/* + * USB_EP_conf.cpp + * Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the GNU Lesser General Public License v3.0 license. + */ + +#if defined(HAL_PCD_MODULE_ENABLED) && defined(USBCON) && defined(PLUGGABLE_USB_ENABLED) + +#include "USB_EP_conf.h" + +ep_desc_t ep_def[USB_MAX_EPS_SLOTS]; +uint_fast8_t ep_num = 0; + +void USB_EP_ClearEndpoints() +{ + ep_desc_t epdef = {0, 0}; + for (uint_fast8_t i = 0; i < USB_MAX_EPS_SLOTS; i++) { + ep_def[i] = epdef; + } + ep_num = 0; +} + +int USB_EP_GetEndpointsSize() +{ + return (USB_ABS_EP0_SIZE + USB_ABS_EP0_TX_SIZE + (ep_num * USB_ABS_EP_SIZE)); +} + +uint8_t USB_EP_AddEndpoint(uint32_t ep_type) +{ + if (ep_num >= USB_MAX_EPS_SLOTS || USB_EP_GetEndpointsSize() + USB_ABS_EP_SIZE > PMA_MAX_SIZE) { + return 0; + } + uint8_t ep = ep_num + 1; + ep_def[ep_num].ep_num = ep | (ep_type & (uint32_t)USB_ENDPOINT_DIRECTION_MASK); + ep_def[ep_num].ep_type = (ep_type & (uint32_t)USB_ENDPOINT_TYPE_MASK); + ep_num++; + return ep; +} + +uint_fast8_t USB_EP_GetNumEndpoints() +{ + return ep_num; +} + +const ep_desc_t *USB_EP_GetEndpointsSlots() +{ + return ep_def; +} + +#endif diff --git a/libraries/USBDevice/src/USBSerial.cpp b/libraries/USBDevice/src/USBSerial.cpp index e23250d007..4dd01a2ff0 100644 --- a/libraries/USBDevice/src/USBSerial.cpp +++ b/libraries/USBDevice/src/USBSerial.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#if defined (USBCON) && defined(USBD_USE_CDC) +#if defined (USBCON) && defined(USBD_USE_CDC) && !defined(PLUGGABLE_USB_ENABLED) #include "USBSerial.h" #include "usbd_cdc.h" diff --git a/libraries/USBDevice/src/cdc/cdc_queue.c b/libraries/USBDevice/src/cdc/cdc_queue.c index 2d2330c195..73a33ddaca 100644 --- a/libraries/USBDevice/src/cdc/cdc_queue.c +++ b/libraries/USBDevice/src/cdc/cdc_queue.c @@ -35,7 +35,7 @@ ****************************************************************************** */ -#ifdef USBCON +#if defined(USBCON) && !defined(PLUGGABLE_USB_ENABLED) #ifdef USBD_USE_CDC #include "cdc_queue.h" diff --git a/libraries/USBDevice/src/cdc/usbd_cdc.c b/libraries/USBDevice/src/cdc/usbd_cdc.c index e1c9a5085e..e7e07c907f 100644 --- a/libraries/USBDevice/src/cdc/usbd_cdc.c +++ b/libraries/USBDevice/src/cdc/usbd_cdc.c @@ -50,7 +50,7 @@ ****************************************************************************** */ -#ifdef USBCON +#if defined(USBCON) && !defined(PLUGGABLE_USB_ENABLED) #ifdef USBD_USE_CDC /* Includes ------------------------------------------------------------------*/ diff --git a/libraries/USBDevice/src/cdc/usbd_cdc_if.c b/libraries/USBDevice/src/cdc/usbd_cdc_if.c index a04cc2b218..cb1f7b42f2 100644 --- a/libraries/USBDevice/src/cdc/usbd_cdc_if.c +++ b/libraries/USBDevice/src/cdc/usbd_cdc_if.c @@ -17,7 +17,7 @@ ****************************************************************************** */ -#ifdef USBCON +#if defined(USBCON) && !defined(PLUGGABLE_USB_ENABLED) #ifdef USBD_USE_CDC /* Includes ------------------------------------------------------------------*/ diff --git a/libraries/USBDevice/src/hid/usbd_hid_composite.c b/libraries/USBDevice/src/hid/usbd_hid_composite.c index 75fee40ea1..cee1424052 100644 --- a/libraries/USBDevice/src/hid/usbd_hid_composite.c +++ b/libraries/USBDevice/src/hid/usbd_hid_composite.c @@ -38,7 +38,8 @@ ****************************************************************************** */ -#ifdef USBCON +#if defined(USBCON) && !defined(PLUGGABLE_USB_ENABLED) + #ifdef USBD_USE_HID_COMPOSITE /* Includes ------------------------------------------------------------------*/ diff --git a/libraries/USBDevice/src/hid/usbd_hid_composite.h b/libraries/USBDevice/src/hid/usbd_hid_composite.h index c5d4aaab4c..0443e8cf9b 100644 --- a/libraries/USBDevice/src/hid/usbd_hid_composite.h +++ b/libraries/USBDevice/src/hid/usbd_hid_composite.h @@ -21,7 +21,7 @@ #ifndef __USB_HID_COMPOSITE_H #define __USB_HID_COMPOSITE_H -#ifdef USBCON +#if defined(USBCON) && !defined(PLUGGABLE_USB_ENABLED) #ifdef USBD_USE_HID_COMPOSITE #ifdef __cplusplus diff --git a/libraries/USBDevice/src/hid/usbd_hid_composite_if.c b/libraries/USBDevice/src/hid/usbd_hid_composite_if.c index 9355bbac91..9404dd5aeb 100644 --- a/libraries/USBDevice/src/hid/usbd_hid_composite_if.c +++ b/libraries/USBDevice/src/hid/usbd_hid_composite_if.c @@ -16,7 +16,7 @@ * ****************************************************************************** */ -#ifdef USBCON +#if defined(USBCON) && !defined(PLUGGABLE_USB_ENABLED) #ifdef USBD_USE_HID_COMPOSITE #include #include "usbd_desc.h" diff --git a/libraries/USBDevice/src/usbd_conf.c b/libraries/USBDevice/src/usbd_conf.c index 8b0d8184aa..e37d59c894 100644 --- a/libraries/USBDevice/src/usbd_conf.c +++ b/libraries/USBDevice/src/usbd_conf.c @@ -20,7 +20,11 @@ /* Includes ------------------------------------------------------------------*/ #include "usbd_core.h" #include "usbd_if.h" -#include "usbd_ep_conf.h" +#if defined(PLUGGABLE_USB_ENABLED) + #include "USB_EP_conf.h" +#else + #include "usbd_ep_conf.h" +#endif #include "stm32yyxx_ll_pwr.h" #ifndef HAL_PCD_MODULE_ENABLED @@ -467,7 +471,12 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev) { USBD_reenumerate(); /* Set common LL Driver parameters */ +#if defined(PLUGGABLE_USB_ENABLED) + g_hpcd.Init.dev_endpoints = USB_EP_GetNumEndpoints(); +#else g_hpcd.Init.dev_endpoints = DEV_NUM_EP; +#endif + #ifdef DEP0CTL_MPS_64 g_hpcd.Init.ep0_mps = DEP0CTL_MPS_64; #else @@ -520,9 +529,66 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev) Error_Handler(); } +#if defined(PLUGGABLE_USB_ENABLED) +#if !defined(USB) + uint8_t eps = USB_EP_GetNumEndpoints(); + int32_t freemem = PMA_MAX_SIZE - USB_ABS_EP0_SIZE - USB_ABS_EP0_TX_SIZE - (USB_ABS_EP_SIZE * eps); + if (freemem < 0) { + // no PMA + return USBD_EMEM; + } + + /* configure EPs FIFOs : + EP0 RX = USB_ABS_EP0_SIZE + all the extra free memory + EP0 TX = USB_ABS_EP0_TX_SIZE + other TX = USB_ABS_EP_SIZE si IN + */ + HAL_PCDEx_SetRxFiFo(&g_hpcd, USB_ABS_EP0_SIZE + freemem); + HAL_PCDEx_SetTxFiFo(&g_hpcd, 0, USB_ABS_EP0_TX_SIZE); + + const ep_desc_t *epdefs = USB_EP_GetEndpointsSlots(); + for (uint32_t i = 0; i < eps; i++) { + if (IS_IN_EP(epdefs[i].ep_num)) { + HAL_PCDEx_SetTxFiFo(&g_hpcd, epdefs[i].ep_num & 0xF, USB_ABS_EP_SIZE); + } + } + +#else + + uint32_t currentaddress = PMA_BASE_ADDR; + uint32_t address = currentaddress; + +#if PCD_USE_DBL_BUF + address = (currentaddress << 16U) | (currentaddress + USB_EP0_SIZE); +#endif + HAL_PCDEx_PMAConfig(&g_hpcd, 0x00, PCD_DEF_BUF, address); + currentaddress += USB_ABS_EP0_SIZE; + +#if PCD_USE_DBL_BUF + address = (currentaddress << 16U) | (currentaddress + USB_EP0_SIZE); +#else + address = currentaddress; +#endif + HAL_PCDEx_PMAConfig(&g_hpcd, 0x80, PCD_DEF_BUF, address); + currentaddress += USB_ABS_EP0_TX_SIZE; + + uint8_t eps = USB_EP_GetNumEndpoints(); + const ep_desc_t *epdefs = USB_EP_GetEndpointsSlots(); + + for (uint32_t i = 0; i < eps; i++) { +#if PCD_USE_DBL_BUF + address = (currentaddress << 16U) | (currentaddress + USB_EP_SIZE); +#else + address = currentaddress; +#endif + HAL_PCDEx_PMAConfig(&g_hpcd, epdefs[i].ep_num, PCD_DEF_BUF, address); + currentaddress += USB_ABS_EP_SIZE; + } +#endif /* USE_USB_HS */ + +#else // !PLUGGABLE_USB_ENABLED #if !defined (USB) - /* configure EPs FIFOs */ HAL_PCDEx_SetRxFiFo(&g_hpcd, ep_def[0].ep_size); for (uint32_t i = 1; i < (DEV_NUM_EP + 1); i++) { HAL_PCDEx_SetTxFiFo(&g_hpcd, ep_def[i].ep_adress & 0xF, ep_def[i].ep_size); @@ -532,6 +598,7 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev) HAL_PCDEx_PMAConfig(&g_hpcd, ep_def[i].ep_adress, ep_def[i].ep_kind, ep_def[i].ep_size); } #endif /* USE_USB_HS */ +#endif // PLUGGABLE_USB_ENABLED return USBD_OK; } diff --git a/libraries/USBDevice/src/usbd_desc.c b/libraries/USBDevice/src/usbd_desc.c index 11681c8b04..3e5b93b68c 100644 --- a/libraries/USBDevice/src/usbd_desc.c +++ b/libraries/USBDevice/src/usbd_desc.c @@ -23,93 +23,151 @@ #include "utils.h" #include -/* Private typedef -----------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ - -/* USB VID and PID have to be specified to correct values. - * They can be defined thanks: - * - boards.txt: *.build.vid or *.build.pid - * - build_opt.h: define CUSTOM_USBD_VID or CUSTOM_USBD_PID - * Else if not defined or specified, default to the ST VID, - * with PID assigned to HID or CDC devices. - */ -#if !defined(USBD_VID) || USBD_VID == 0 - // Undef the default definition - #undef USBD_VID - #if defined(CUSTOM_USBD_VID) - #define USBD_VID CUSTOM_USBD_VID +#if defined(PLUGGABLE_USB_ENABLED) + + #include "USB_EP.h" + + /* Private typedef -----------------------------------------------------------*/ + /* Private define ------------------------------------------------------------*/ + + /* USB VID and PID: Either both or neither must be specified. If not + * specified, default to the ST VID, with a PID assigned to HID or a PID + * assigned to CDC devices. */ + #if !USBD_PID && !USBD_VID + // Undef the default zero values + #undef USBD_PID + #undef USBD_VID + // Define default values, based on the USB class used + #define USBD_VID 0x0483 + #define USBD_PID 0x5711 + #endif /* !USBD_PID && !USBD_VID */ + + #if !USBD_VID || !USBD_PID + #error "USB VID or PID not specified" + #endif + + /* Manufacturer string: Use the specified string if specified, guess + based on VID otherwise */ + #if defined(USB_MANUFACTURER_STRING) + #define USBD_MANUFACTURER_STRING USB_MANUFACTURER_STRING + #elif USBD_VID == 0x2341 + #define USBD_MANUFACTURER_STRING "Arduino LLC" + #elif USBD_VID == 0x2A03 + #define USBD_MANUFACTURER_STRING "Arduino srl" + #elif USBD_VID == 0x0483 + #define USBD_MANUFACTURER_STRING "STMicroelectronics" #else - // Define default values - #define USBD_VID 0x0483 + #define USBD_MANUFACTURER_STRING "Unknown" #endif -#endif /* USBD_VID */ -#if !defined(USBD_PID) || USBD_PID == -1 - // Undef the default definition - #undef USBD_PID - #if defined(CUSTOM_USBD_PID) - #define USBD_PID CUSTOM_USBD_PID + #define USBD_LANGID_STRING 0x409 /* 1033 US.S English */ + + /* Product string: Use the specified string if specified, construct + based on BOARD_NAME and class otherwise. */ + #if defined(USB_PRODUCT_STRING) + #define USBD_CLASS_PRODUCT_HS_STRING USB_PRODUCT_STRING + #define USBD_CLASS_PRODUCT_FS_STRING USB_PRODUCT_STRING #else - // Define default values, based on the USB class used - #if defined(USBD_USE_HID_COMPOSITE) - #define USBD_PID 0x5711 - #elif defined(USBD_USE_CDC) - #define USBD_PID 0x5740 - #else - #error "USB PID not specified" - #endif + #define USBD_CLASS_PRODUCT_HS_STRING CONCATS(BOARD_NAME, "HID in HS Mode") + #define USBD_CLASS_PRODUCT_FS_STRING CONCATS(BOARD_NAME, "HID in FS Mode") #endif -#endif /* USBD_VID */ -#if USBD_VID == 0 - #error "USB VID not properly specified" -#endif + #define USBD_CLASS_CONFIGURATION_HS_STRING CONCATS(BOARD_NAME, "HID Config") + #define USBD_CLASS_INTERFACE_HS_STRING CONCATS(BOARD_NAME, "HID Interface") + #define USBD_CLASS_CONFIGURATION_FS_STRING CONCATS(BOARD_NAME, "HID Config") + #define USBD_CLASS_INTERFACE_FS_STRING CONCATS(BOARD_NAME, "HID Interface") -/* Manufacturer string: Use the specified string if specified, guess - based on VID otherwise */ -#if defined(USB_MANUFACTURER_STRING) - #define USBD_MANUFACTURER_STRING USB_MANUFACTURER_STRING -#elif USBD_VID == 0x2341 - #define USBD_MANUFACTURER_STRING "Arduino LLC" -#elif USBD_VID == 0x2A03 - #define USBD_MANUFACTURER_STRING "Arduino srl" -#elif USBD_VID == 0x0483 - #define USBD_MANUFACTURER_STRING "STMicroelectronics" #else - #define USBD_MANUFACTURER_STRING "Unknown" -#endif -#define USBD_LANGID_STRING 0x409 /* 1033 US.S English */ - -/* Product string: Use the specified string if specified, construct - based on BOARD_NAME and class otherwise. */ -#if defined(USB_PRODUCT_STRING) - #define USBD_CLASS_PRODUCT_HS_STRING USB_PRODUCT_STRING - #define USBD_CLASS_PRODUCT_FS_STRING USB_PRODUCT_STRING -#elif defined(USBD_USE_HID_COMPOSITE) - #define USBD_CLASS_PRODUCT_HS_STRING CONCATS(BOARD_NAME, "HID in HS Mode") - #define USBD_CLASS_PRODUCT_FS_STRING CONCATS(BOARD_NAME, "HID in FS Mode") -#elif defined(USBD_USE_CDC) - #define USBD_CLASS_PRODUCT_HS_STRING CONCATS(BOARD_NAME, "CDC in HS Mode") - #define USBD_CLASS_PRODUCT_FS_STRING CONCATS(BOARD_NAME, "CDC in FS Mode") -#else - #define USBD_CLASS_PRODUCT_HS_STRING CONCATS(BOARD_NAME, "in HS Mode") - #define USBD_CLASS_PRODUCT_FS_STRING CONCATS(BOARD_NAME, "in FS Mode") -#endif -#ifdef USBD_USE_HID_COMPOSITE - #define USBD_CLASS_CONFIGURATION_HS_STRING CONCATS(BOARD_NAME, "HID Config") - #define USBD_CLASS_INTERFACE_HS_STRING CONCATS(BOARD_NAME, "HID Interface") - #define USBD_CLASS_CONFIGURATION_FS_STRING CONCATS(BOARD_NAME, "HID Config") - #define USBD_CLASS_INTERFACE_FS_STRING CONCATS(BOARD_NAME, "HID Interface") -#endif /* USBD_USE_HID_COMPOSITE */ + /* Private typedef -----------------------------------------------------------*/ + /* Private define ------------------------------------------------------------*/ -#ifdef USBD_USE_CDC - #define USBD_CLASS_CONFIGURATION_HS_STRING CONCATS(BOARD_NAME, "CDC Config") - #define USBD_CLASS_INTERFACE_HS_STRING CONCATS(BOARD_NAME, "CDC Interface") - #define USBD_CLASS_CONFIGURATION_FS_STRING CONCATS(BOARD_NAME, "CDC Config") - #define USBD_CLASS_INTERFACE_FS_STRING CONCATS(BOARD_NAME, "CDC Interface") -#endif /* USBD_USE_CDC */ + /* USB VID and PID have to be specified to correct values. + * They can be defined thanks: + * - boards.txt: *.build.vid or *.build.pid + * - build_opt.h: define CUSTOM_USBD_VID or CUSTOM_USBD_PID + * Else if not defined or specified, default to the ST VID, + * with PID assigned to HID or CDC devices. + */ + #if !defined(USBD_VID) || USBD_VID == 0 + // Undef the default definition + #undef USBD_VID + #if defined(CUSTOM_USBD_VID) + #define USBD_VID CUSTOM_USBD_VID + #else + // Define default values + #define USBD_VID 0x0483 + #endif + #endif /* USBD_VID */ + + #if !defined(USBD_PID) || USBD_PID == -1 + // Undef the default definition + #undef USBD_PID + #if defined(CUSTOM_USBD_PID) + #define USBD_PID CUSTOM_USBD_PID + #else + // Define default values, based on the USB class used + #if defined(USBD_USE_HID_COMPOSITE) + #define USBD_PID 0x5711 + #elif defined(USBD_USE_CDC) + #define USBD_PID 0x5740 + #else + #error "USB PID not specified" + #endif + #endif + #endif /* USBD_VID */ + + #if USBD_VID == 0 + #error "USB VID not properly specified" + #endif + + /* Manufacturer string: Use the specified string if specified, guess + based on VID otherwise */ + #if defined(USB_MANUFACTURER_STRING) + #define USBD_MANUFACTURER_STRING USB_MANUFACTURER_STRING + #elif USBD_VID == 0x2341 + #define USBD_MANUFACTURER_STRING "Arduino LLC" + #elif USBD_VID == 0x2A03 + #define USBD_MANUFACTURER_STRING "Arduino srl" + #elif USBD_VID == 0x0483 + #define USBD_MANUFACTURER_STRING "STMicroelectronics" + #else + #define USBD_MANUFACTURER_STRING "Unknown" + #endif + + #define USBD_LANGID_STRING 0x409 /* 1033 US.S English */ + + /* Product string: Use the specified string if specified, construct + based on BOARD_NAME and class otherwise. */ + #if defined(USB_PRODUCT_STRING) + #define USBD_CLASS_PRODUCT_HS_STRING USB_PRODUCT_STRING + #define USBD_CLASS_PRODUCT_FS_STRING USB_PRODUCT_STRING + #elif defined(USBD_USE_HID_COMPOSITE) + #define USBD_CLASS_PRODUCT_HS_STRING CONCATS(BOARD_NAME, "HID in HS Mode") + #define USBD_CLASS_PRODUCT_FS_STRING CONCATS(BOARD_NAME, "HID in FS Mode") + #elif defined(USBD_USE_CDC) + #define USBD_CLASS_PRODUCT_HS_STRING CONCATS(BOARD_NAME, "CDC in HS Mode") + #define USBD_CLASS_PRODUCT_FS_STRING CONCATS(BOARD_NAME, "CDC in FS Mode") + #else + #define USBD_CLASS_PRODUCT_HS_STRING CONCATS(BOARD_NAME, "in HS Mode") + #define USBD_CLASS_PRODUCT_FS_STRING CONCATS(BOARD_NAME, "in FS Mode") + #endif + + #ifdef USBD_USE_HID_COMPOSITE + #define USBD_CLASS_CONFIGURATION_HS_STRING CONCATS(BOARD_NAME, "HID Config") + #define USBD_CLASS_INTERFACE_HS_STRING CONCATS(BOARD_NAME, "HID Interface") + #define USBD_CLASS_CONFIGURATION_FS_STRING CONCATS(BOARD_NAME, "HID Config") + #define USBD_CLASS_INTERFACE_FS_STRING CONCATS(BOARD_NAME, "HID Interface") + #endif /* USBD_USE_HID_COMPOSITE */ + + #ifdef USBD_USE_CDC + #define USBD_CLASS_CONFIGURATION_HS_STRING CONCATS(BOARD_NAME, "CDC Config") + #define USBD_CLASS_INTERFACE_HS_STRING CONCATS(BOARD_NAME, "CDC Interface") + #define USBD_CLASS_CONFIGURATION_FS_STRING CONCATS(BOARD_NAME, "CDC Config") + #define USBD_CLASS_INTERFACE_FS_STRING CONCATS(BOARD_NAME, "CDC Interface") + #endif /* USBD_USE_CDC */ +#endif /* Private macro -------------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ @@ -147,6 +205,35 @@ USBD_DescriptorsTypeDef USBD_Desc = { USBD_USR_BOSDescriptor, #endif /* (USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1) */ }; +#if defined(PLUGGABLE_USB_ENABLED) + +__ALIGN_BEGIN uint8_t USBD_Class_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = { + 0x12, /* bLength */ + USB_DESC_TYPE_DEVICE, /* bDescriptorType */ +#if ((USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1)) + 0x01, /*bcdUSB */ /* changed to USB version 2.01 + in order to support BOS Desc */ +#else + 0x00, /* bcdUSB */ +#endif + 0x02, + 0x00, /* bDeviceClass */ + 0x00, /* bDeviceSubClass */ + 0x00, /* bDeviceProtocol */ + USB_EP0_SIZE, /* bMaxPacketSize */ + LOBYTE(USBD_VID), /* idVendor */ + HIBYTE(USBD_VID), /* idVendor */ + LOBYTE(USBD_PID), /* idProduct */ + HIBYTE(USBD_PID), /* idProduct */ + 0x00, /* bcdDevice rel. 0.00 */ + 0x00, + USBD_IDX_MFC_STR, /* Index of manufacturer string */ + USBD_IDX_PRODUCT_STR, /* Index of product string */ + USBD_IDX_SERIAL_STR, /* Index of serial number string */ + USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations */ +}; /* USB_DeviceDescriptor */ + +#else #ifdef USBD_USE_HID_COMPOSITE /* USB Standard Device Descriptor */ @@ -206,6 +293,8 @@ __ALIGN_BEGIN uint8_t USBD_Class_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = { }; /* USB_DeviceDescriptor */ #endif /* USBD_USE_CDC */ +#endif // PLUGGABLE_USB_ENABLED + /* USB Device LPM BOS descriptor */ #if (USBD_LPM_ENABLED == 1) __ALIGN_BEGIN uint8_t USBD_BOSDesc[USB_SIZ_BOS_DESC] __ALIGN_END = { @@ -321,11 +410,24 @@ __ALIGN_BEGIN uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = { HIBYTE(USBD_LANGID_STRING), }; +#if defined(PLUGGABLE_USB_ENABLED) + +# if !defined(USB_SERIAL_STRING) +uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] = { + USB_SIZ_STRING_SERIAL, + USB_DESC_TYPE_STRING, +}; +# endif + +#else + uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] = { USB_SIZ_STRING_SERIAL, USB_DESC_TYPE_STRING, }; +#endif + __ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END; /* Private functions ---------------------------------------------------------*/ @@ -397,12 +499,23 @@ uint8_t *USBD_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) { UNUSED(speed); +#if defined(PLUGGABLE_USB_ENABLED) +# if !defined(USB_SERIAL_STRING) + *length = USB_SIZ_STRING_SERIAL; + Get_SerialNum(); + return (uint8_t *)USBD_StringSerial; +# else + USBD_GetString((uint8_t *)USB_SERIAL_STRING, USBD_StrDesc, length); + return USBD_StrDesc; +# endif +#else *length = USB_SIZ_STRING_SERIAL; /* Update the serial number string descriptor with the data from the unique ID*/ Get_SerialNum(); return (uint8_t *)USBD_StringSerial; +#endif } /** @@ -437,6 +550,28 @@ uint8_t *USBD_Class_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *le return USBD_StrDesc; } +#if defined(PLUGGABLE_USB_ENABLED) +# if !defined(USB_SERIAL_STRING) +/** + * @brief Create the serial number string descriptor + */ +static void Get_SerialNum(void) +{ + uint32_t deviceserial0, deviceserial1, deviceserial2; + + deviceserial0 = *(uint32_t *)DEVICE_ID1; + deviceserial1 = *(uint32_t *)DEVICE_ID2; + deviceserial2 = *(uint32_t *)DEVICE_ID3; + + deviceserial0 += deviceserial2; + + if (deviceserial0 != 0U) { + IntToUnicode(deviceserial0, &USBD_StringSerial[2], 8U); + IntToUnicode(deviceserial1, &USBD_StringSerial[18], 4U); + } +} +# endif +#else /** * @brief Create the serial number string descriptor * @param None @@ -459,6 +594,7 @@ static void Get_SerialNum(void) IntToUnicode(deviceserial1, &USBD_StringSerial[18], 4U); } } +#endif // PLUGGABLE_USB_ENABLED #if ((USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1)) diff --git a/libraries/USBDevice/src/usbd_ep_conf.c b/libraries/USBDevice/src/usbd_ep_conf.c index d872acca5f..4cf96a9439 100644 --- a/libraries/USBDevice/src/usbd_ep_conf.c +++ b/libraries/USBDevice/src/usbd_ep_conf.c @@ -15,7 +15,7 @@ * ****************************************************************************** */ -#if defined(HAL_PCD_MODULE_ENABLED) && defined(USBCON) +#if defined(HAL_PCD_MODULE_ENABLED) && defined(USBCON) && !defined(PLUGGABLE_USB_ENABLED) /* Includes ------------------------------------------------------------------*/ #include "usbd_ep_conf.h" diff --git a/libraries/USBDevice/src/usbd_if.c b/libraries/USBDevice/src/usbd_if.c index d0a87a79c5..00b573a370 100644 --- a/libraries/USBDevice/src/usbd_if.c +++ b/libraries/USBDevice/src/usbd_if.c @@ -8,7 +8,9 @@ #ifdef USBCON #include "usbd_if.h" -#include "usbd_cdc_if.h" +#if !defined(PLUGGABLE_USB_ENABLED) + #include "usbd_cdc_if.h" +#endif #include "stm32yyxx_ll_system.h" #if !defined(USBD_REENUM_DISABLED) @@ -169,7 +171,7 @@ WEAK void USBD_reenumerate(void) WEAK void USBD_reenumerate(void) { } #endif -#ifdef USBD_USE_CDC +#if defined(USBD_USE_CDC) && !defined(PLUGGABLE_USB_ENABLED) void USBD_CDC_init(void) { CDC_init(); diff --git a/platform.txt b/platform.txt index 5b3ad21016..679dc9d1ee 100644 --- a/platform.txt +++ b/platform.txt @@ -38,7 +38,7 @@ USBDevice_include_dir={builtin_library_dir}/USBDevice/inc # STM compile variables # ---------------------- -compiler.stm.extra_include="-I{build.source.path}" "-I{build.core.path}/avr" "-I{core_stm32_dir}" "-I{SrcWrapper_include_dir}" "-I{SrcWrapper_include_dir}/LL" "-I{hal_dir}/Inc" "-I{hal_dir}/Src" "-I{build.system.path}/{build.series}" "-I{USBDevice_include_dir}" "-I{usbd_core_dir}/Inc" "-I{usbd_core_dir}/Src" "-I{VirtIO_include_dir}" {build.virtio_extra_include} +compiler.stm.extra_include="-I{build.source.path}" "-I{build.core.path}/avr" "-I{core_stm32_dir}" "-I{SrcWrapper_include_dir}" "-I{SrcWrapper_include_dir}/LL" "-I{hal_dir}/Inc" "-I{hal_dir}/Src" "-I{build.system.path}/{build.series}" "-I{USBDevice_include_dir}" "-I{USBDevice_include_dir}/PluggableUSB" "-I{usbd_core_dir}/Inc" "-I{usbd_core_dir}/Src" "-I{VirtIO_include_dir}" {build.virtio_extra_include} compiler.arm.cmsis.c.flags="-I{cmsis_dir}/Core/Include/" "-I{cmsis_dev_dir}/Include/" "-I{cmsis_dev_dir}/Source/Templates/gcc/" "-I{cmsis_dsp}/Include" "-I{cmsis_dsp}/PrivateInclude" compiler.warning_flags=-w From 0de99fbc08ae3d1f9f9de37667217bb69c39d2ce Mon Sep 17 00:00:00 2001 From: Aymane Bahssain Date: Mon, 2 Mar 2026 17:12:42 +0100 Subject: [PATCH 2/5] fix(mouse): rename builtin Mouse library to Mouse_STM32 to avoid conflict Signed-off-by: Aymane Bahssain Rename and update includes for the builtin STM32 mouse library to `Mouse_STM32` to avoid name conflicts with the official Arduino `Mouse` library --- libraries/CMakeLists.txt | 2 +- libraries/Mouse/CMakeLists.txt | 30 ------------------- libraries/Mouse_STM32/CMakeLists.txt | 30 +++++++++++++++++++ libraries/{Mouse => Mouse_STM32}/README.adoc | 0 .../ButtonMouseControl/ButtonMouseControl.ino | 2 +- libraries/{Mouse => Mouse_STM32}/keywords.txt | 2 +- .../{Mouse => Mouse_STM32}/library.properties | 2 +- .../src/Mouse_STM32.cpp} | 2 +- .../Mouse.h => Mouse_STM32/src/Mouse_STM32.h} | 0 9 files changed, 35 insertions(+), 35 deletions(-) delete mode 100644 libraries/Mouse/CMakeLists.txt create mode 100644 libraries/Mouse_STM32/CMakeLists.txt rename libraries/{Mouse => Mouse_STM32}/README.adoc (100%) rename libraries/{Mouse => Mouse_STM32}/examples/ButtonMouseControl/ButtonMouseControl.ino (98%) rename libraries/{Mouse => Mouse_STM32}/keywords.txt (96%) rename libraries/{Mouse => Mouse_STM32}/library.properties (95%) rename libraries/{Mouse/src/Mouse.cpp => Mouse_STM32/src/Mouse_STM32.cpp} (98%) rename libraries/{Mouse/src/Mouse.h => Mouse_STM32/src/Mouse_STM32.h} (100%) diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index ff0a3b0557..3e9036e388 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -4,7 +4,7 @@ add_subdirectory(CMSIS_DSP) add_subdirectory(EEPROM) add_subdirectory(IWatchdog) add_subdirectory(Keyboard) -add_subdirectory(Mouse) +add_subdirectory(Mouse_STM32) add_subdirectory(RGB_LED_TLC59731) add_subdirectory(SPI) add_subdirectory(Servo) diff --git a/libraries/Mouse/CMakeLists.txt b/libraries/Mouse/CMakeLists.txt deleted file mode 100644 index a544f07a8a..0000000000 --- a/libraries/Mouse/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -# v3.21 implemented semantic changes regarding $ -# See https://cmake.org/cmake/help/v3.21/command/target_link_libraries.html#linking-object-libraries-via-target-objects -cmake_minimum_required(VERSION 3.21) - -add_library(Mouse INTERFACE) -add_library(Mouse_usage INTERFACE) - -target_include_directories(Mouse_usage INTERFACE - src -) - - -target_link_libraries(Mouse_usage INTERFACE - base_config -) - -target_link_libraries(Mouse INTERFACE Mouse_usage) - - - -add_library(Mouse_bin OBJECT EXCLUDE_FROM_ALL - src/Mouse.cpp -) -target_link_libraries(Mouse_bin PUBLIC Mouse_usage) - -target_link_libraries(Mouse INTERFACE - Mouse_bin - $ -) - diff --git a/libraries/Mouse_STM32/CMakeLists.txt b/libraries/Mouse_STM32/CMakeLists.txt new file mode 100644 index 0000000000..83c7654d50 --- /dev/null +++ b/libraries/Mouse_STM32/CMakeLists.txt @@ -0,0 +1,30 @@ +# v3.21 implemented semantic changes regarding $ +# See https://cmake.org/cmake/help/v3.21/command/target_link_libraries.html#linking-object-libraries-via-target-objects +cmake_minimum_required(VERSION 3.21) + +add_library(Mouse_STM32 INTERFACE) +add_library(Mouse_STM32_usage INTERFACE) + +target_include_directories(Mouse_STM32_usage INTERFACE + src +) + + +target_link_libraries(Mouse_STM32_usage INTERFACE + base_config +) + +target_link_libraries(Mouse_STM32 INTERFACE Mouse_STM32_usage) + + + +add_library(Mouse_STM32_bin OBJECT EXCLUDE_FROM_ALL + src/Mouse_STM32.cpp +) +target_link_libraries(Mouse_STM32_bin PUBLIC Mouse_STM32_usage) + +target_link_libraries(Mouse_STM32 INTERFACE + Mouse_STM32_bin + $ +) + diff --git a/libraries/Mouse/README.adoc b/libraries/Mouse_STM32/README.adoc similarity index 100% rename from libraries/Mouse/README.adoc rename to libraries/Mouse_STM32/README.adoc diff --git a/libraries/Mouse/examples/ButtonMouseControl/ButtonMouseControl.ino b/libraries/Mouse_STM32/examples/ButtonMouseControl/ButtonMouseControl.ino similarity index 98% rename from libraries/Mouse/examples/ButtonMouseControl/ButtonMouseControl.ino rename to libraries/Mouse_STM32/examples/ButtonMouseControl/ButtonMouseControl.ino index 3fc95e5411..34a4c37366 100644 --- a/libraries/Mouse/examples/ButtonMouseControl/ButtonMouseControl.ino +++ b/libraries/Mouse_STM32/examples/ButtonMouseControl/ButtonMouseControl.ino @@ -23,7 +23,7 @@ https://www.arduino.cc/en/Tutorial/BuiltInExamples/ButtonMouseControl */ -#include "Mouse.h" +#include "Mouse_STM32.h" // set pin numbers for the five buttons: const int upButton = 2; diff --git a/libraries/Mouse/keywords.txt b/libraries/Mouse_STM32/keywords.txt similarity index 96% rename from libraries/Mouse/keywords.txt rename to libraries/Mouse_STM32/keywords.txt index fcccd0d412..d0b86f0f23 100644 --- a/libraries/Mouse/keywords.txt +++ b/libraries/Mouse_STM32/keywords.txt @@ -6,7 +6,7 @@ # Datatypes (KEYWORD1) ####################################### -Mouse KEYWORD1 +Mouse_STM32 KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) diff --git a/libraries/Mouse/library.properties b/libraries/Mouse_STM32/library.properties similarity index 95% rename from libraries/Mouse/library.properties rename to libraries/Mouse_STM32/library.properties index da59d05041..0a93961529 100644 --- a/libraries/Mouse/library.properties +++ b/libraries/Mouse_STM32/library.properties @@ -1,4 +1,4 @@ -name=Mouse +name=Mouse_STM32 version=1.1.0 author=Arduino, stm32duino maintainer=stm32duino diff --git a/libraries/Mouse/src/Mouse.cpp b/libraries/Mouse_STM32/src/Mouse_STM32.cpp similarity index 98% rename from libraries/Mouse/src/Mouse.cpp rename to libraries/Mouse_STM32/src/Mouse_STM32.cpp index 56f23be295..1780eba6ae 100644 --- a/libraries/Mouse/src/Mouse.cpp +++ b/libraries/Mouse_STM32/src/Mouse_STM32.cpp @@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "Mouse.h" +#include "Mouse_STM32.h" #if defined(USBCON) #include "usbd_hid_composite_if.h" diff --git a/libraries/Mouse/src/Mouse.h b/libraries/Mouse_STM32/src/Mouse_STM32.h similarity index 100% rename from libraries/Mouse/src/Mouse.h rename to libraries/Mouse_STM32/src/Mouse_STM32.h From d3b4852ad42207ca5f3a8aa1a926033a2172c669 Mon Sep 17 00:00:00 2001 From: Aymane Bahssain Date: Mon, 2 Mar 2026 17:14:22 +0100 Subject: [PATCH 3/5] chore(ci): update codespell ignore list Signed-off-by: Aymane Bahssain --- CI/codespell/.codespellignore | 1 + 1 file changed, 1 insertion(+) diff --git a/CI/codespell/.codespellignore b/CI/codespell/.codespellignore index dcc3b118c8..c659fd4443 100644 --- a/CI/codespell/.codespellignore +++ b/CI/codespell/.codespellignore @@ -8,3 +8,4 @@ nwe ore shiftin socio-economic +clen From a7e7cb06e63970eb0a876ec9456bd4a0e2c63a7c Mon Sep 17 00:00:00 2001 From: Aymane Bahssain Date: Tue, 3 Mar 2026 13:50:34 +0100 Subject: [PATCH 4/5] feat(usb): add PluggableUSB option for various boards Signed-off-by: Aymane Bahssain --- boards.txt | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/boards.txt b/boards.txt index 32cc607bfd..c6a26618db 100644 --- a/boards.txt +++ b/boards.txt @@ -15399,6 +15399,8 @@ STeaMi.menu.xserial.disabled.build.xSerial= # USB connectivity Nucleo_144.menu.usb.none=None +Nucleo_144.menu.usb.pluggable=PluggableUSB +Nucleo_144.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED Nucleo_144.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) Nucleo_144.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC Nucleo_144.menu.usb.CDC=CDC (no generic 'Serial') @@ -15412,6 +15414,8 @@ Nucleo_144.menu.xusb.HSFS=High Speed in Full Speed mode Nucleo_144.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS Nucleo_64.menu.usb.none=None +Nucleo_64.menu.usb.pluggable=PluggableUSB +Nucleo_64.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED Nucleo_64.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) Nucleo_64.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC Nucleo_64.menu.usb.CDC=CDC (no generic 'Serial') @@ -15425,6 +15429,8 @@ Nucleo_64.menu.xusb.HSFS=High Speed in Full Speed mode Nucleo_64.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS Nucleo_32.menu.usb.none=None +Nucleo_32.menu.usb.pluggable=PluggableUSB +Nucleo_32.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED Nucleo_32.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) Nucleo_32.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC Nucleo_32.menu.usb.CDC=CDC (no generic 'Serial') @@ -15453,6 +15459,8 @@ Disco.menu.xusb.HSFS=High Speed in Full Speed mode Disco.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS Eval.menu.usb.none=None +Eval.menu.usb.pluggable=PluggableUSB +Eval.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED Eval.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) Eval.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC Eval.menu.usb.CDC=CDC (no generic 'Serial') @@ -15466,6 +15474,8 @@ Eval.menu.xusb.HSFS=High Speed in Full Speed mode Eval.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenC0.menu.usb.none=None +GenC0.menu.usb.pluggable=PluggableUSB +GenC0.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenC0.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenC0.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenC0.menu.usb.CDC=CDC (no generic 'Serial') @@ -15474,6 +15484,8 @@ GenC0.menu.usb.HID=HID (keyboard and mouse) GenC0.menu.usb.HID.build.enable_usb={build.usb_flags} -DUSBD_USE_HID_COMPOSITE GenF0.menu.usb.none=None +GenF0.menu.usb.pluggable=PluggableUSB +GenF0.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenF0.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenF0.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenF0.menu.usb.CDC=CDC (no generic 'Serial') @@ -15482,6 +15494,8 @@ GenF0.menu.usb.HID=HID (keyboard and mouse) GenF0.menu.usb.HID.build.enable_usb={build.usb_flags} -DUSBD_USE_HID_COMPOSITE GenF1.menu.usb.none=None +GenF1.menu.usb.pluggable=PluggableUSB +GenF1.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenF1.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenF1.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenF1.menu.usb.CDC=CDC (no generic 'Serial') @@ -15495,6 +15509,8 @@ GenF1.menu.xusb.HSFS=High Speed in Full Speed mode GenF1.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenF2.menu.usb.none=None +GenF2.menu.usb.pluggable=PluggableUSB +GenF2.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenF2.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenF2.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenF2.menu.usb.CDC=CDC (no generic 'Serial') @@ -15508,6 +15524,8 @@ GenF2.menu.xusb.HSFS=High Speed in Full Speed mode GenF2.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenF3.menu.usb.none=None +GenF3.menu.usb.pluggable=PluggableUSB +GenF3.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenF3.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenF3.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenF3.menu.usb.CDC=CDC (no generic 'Serial') @@ -15521,6 +15539,8 @@ GenF3.menu.xusb.HSFS=High Speed in Full Speed mode GenF3.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenF4.menu.usb.none=None +GenF4.menu.usb.pluggable=PluggableUSB +GenF4.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenF4.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenF4.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenF4.menu.usb.CDC=CDC (no generic 'Serial') @@ -15534,6 +15554,8 @@ GenF4.menu.xusb.HSFS=High Speed in Full Speed mode GenF4.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenF7.menu.usb.none=None +GenF7.menu.usb.pluggable=PluggableUSB +GenF7.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenF7.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenF7.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenF7.menu.usb.CDC=CDC (no generic 'Serial') @@ -15547,6 +15569,8 @@ GenF7.menu.xusb.HSFS=High Speed in Full Speed mode GenF7.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenG0.menu.usb.none=None +GenG0.menu.usb.pluggable=PluggableUSB +GenG0.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenG0.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenG0.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenG0.menu.usb.CDC=CDC (no generic 'Serial') @@ -15555,6 +15579,8 @@ GenG0.menu.usb.HID=HID (keyboard and mouse) GenG0.menu.usb.HID.build.enable_usb={build.usb_flags} -DUSBD_USE_HID_COMPOSITE GenG4.menu.usb.none=None +GenG4.menu.usb.pluggable=PluggableUSB +GenG4.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenG4.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenG4.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenG4.menu.usb.CDC=CDC (no generic 'Serial') @@ -15568,6 +15594,8 @@ GenG4.menu.xusb.HSFS=High Speed in Full Speed mode GenG4.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenH5.menu.usb.none=None +GenH5.menu.usb.pluggable=PluggableUSB +GenH5.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenH5.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenH5.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenH5.menu.usb.CDC=CDC (no generic 'Serial') @@ -15581,6 +15609,8 @@ GenH5.menu.xusb.HSFS=High Speed in Full Speed mode GenH5.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenH7.menu.usb.none=None +GenH7.menu.usb.pluggable=PluggableUSB +GenH7.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenH7.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenH7.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenH7.menu.usb.CDC=CDC (no generic 'Serial') @@ -15594,6 +15624,8 @@ GenH7.menu.xusb.HSFS=High Speed in Full Speed mode GenH7.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenL0.menu.usb.none=None +GenL0.menu.usb.pluggable=PluggableUSB +GenL0.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenL0.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenL0.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenL0.menu.usb.CDC=CDC (no generic 'Serial') @@ -15602,6 +15634,8 @@ GenL0.menu.usb.HID=HID (keyboard and mouse) GenL0.menu.usb.HID.build.enable_usb={build.usb_flags} -DUSBD_USE_HID_COMPOSITE GenL1.menu.usb.none=None +GenL1.menu.usb.pluggable=PluggableUSB +GenL1.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenL1.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenL1.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenL1.menu.usb.CDC=CDC (no generic 'Serial') @@ -15610,6 +15644,8 @@ GenL1.menu.usb.HID=HID (keyboard and mouse) GenL1.menu.usb.HID.build.enable_usb={build.usb_flags} -DUSBD_USE_HID_COMPOSITE GenL4.menu.usb.none=None +GenL4.menu.usb.pluggable=PluggableUSB +GenL4.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenL4.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenL4.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenL4.menu.usb.CDC=CDC (no generic 'Serial') @@ -15623,6 +15659,8 @@ GenL4.menu.xusb.HSFS=High Speed in Full Speed mode GenL4.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenL5.menu.usb.none=None +GenL5.menu.usb.pluggable=PluggableUSB +GenL5.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenL5.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenL5.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenL5.menu.usb.CDC=CDC (no generic 'Serial') @@ -15636,6 +15674,8 @@ GenL5.menu.xusb.HSFS=High Speed in Full Speed mode GenL5.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenU0.menu.usb.none=None +GenU0.menu.usb.pluggable=PluggableUSB +GenU0.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenU0.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenU0.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenU0.menu.usb.CDC=CDC (no generic 'Serial') @@ -15644,6 +15684,8 @@ GenU0.menu.usb.HID=HID (keyboard and mouse) GenU0.menu.usb.HID.build.enable_usb={build.usb_flags} -DUSBD_USE_HID_COMPOSITE GenU3.menu.usb.none=None +GenU3.menu.usb.pluggable=PluggableUSB +GenU3.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenU3.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenU3.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenU3.menu.usb.CDC=CDC (no generic 'Serial') @@ -15652,6 +15694,8 @@ GenU3.menu.usb.HID=HID (keyboard and mouse) GenU3.menu.usb.HID.build.enable_usb={build.usb_flags} -DUSBD_USE_HID_COMPOSITE GenU5.menu.usb.none=None +GenU5.menu.usb.pluggable=PluggableUSB +GenU5.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenU5.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenU5.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenU5.menu.usb.CDC=CDC (no generic 'Serial') @@ -15665,6 +15709,8 @@ GenU5.menu.xusb.HSFS=High Speed in Full Speed mode GenU5.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS GenWB.menu.usb.none=None +GenWB.menu.usb.pluggable=PluggableUSB +GenWB.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenWB.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenWB.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenWB.menu.usb.CDC=CDC (no generic 'Serial') @@ -15678,6 +15724,8 @@ GenWB.menu.xusb.HSFS=High Speed in Full Speed mode GenWB.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS 3dprinter.menu.usb.none=None +3dprinter.menu.usb.pluggable=PluggableUSB +3dprinter.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED 3dprinter.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) 3dprinter.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC 3dprinter.menu.usb.CDC=CDC (no generic 'Serial') @@ -15695,6 +15743,8 @@ Blues.menu.usb.CDC.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC -DDISABLE_G Blues.menu.usb.HID=HID (keyboard and mouse) Blues.menu.usb.HID.build.enable_usb={build.usb_flags} -DUSBD_USE_HID_COMPOSITE Blues.menu.usb.none=None +Blues.menu.usb.pluggable=PluggableUSB +Blues.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED Blues.menu.xusb.FS=Low/Full Speed Blues.menu.xusb.HS=High Speed Blues.menu.xusb.HS.build.usb_speed=-DUSE_USB_HS @@ -15702,6 +15752,8 @@ Blues.menu.xusb.HSFS=High Speed in Full Speed mode Blues.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS Elecgator.menu.usb.none=None +Elecgator.menu.usb.pluggable=PluggableUSB +Elecgator.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED Elecgator.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) Elecgator.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC Elecgator.menu.usb.CDC=CDC (no generic 'Serial') @@ -15715,6 +15767,8 @@ Elecgator.menu.xusb.HSFS=High Speed in Full Speed mode Elecgator.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS ESC_board.menu.usb.none=None +ESC_board.menu.usb.pluggable=PluggableUSB +ESC_board.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED ESC_board.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) ESC_board.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC ESC_board.menu.usb.CDC=CDC (no generic 'Serial') @@ -15726,6 +15780,8 @@ ESC_board.menu.xusb.HSFS=High Speed in Full Speed mode ESC_board.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS Garatronic.menu.usb.none=None +Garatronic.menu.usb.pluggable=PluggableUSB +Garatronic.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED Garatronic.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) Garatronic.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC Garatronic.menu.usb.CDC=CDC (no generic 'Serial') @@ -15734,6 +15790,8 @@ Garatronic.menu.usb.HID=HID (keyboard and mouse) Garatronic.menu.usb.HID.build.enable_usb={build.usb_flags} -DUSBD_USE_HID_COMPOSITE GenFlight.menu.usb.none=None +GenFlight.menu.usb.pluggable=PluggableUSB +GenFlight.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED GenFlight.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) GenFlight.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC GenFlight.menu.usb.CDC=CDC (no generic 'Serial') @@ -15747,6 +15805,8 @@ GenFlight.menu.xusb.HSFS=High Speed in Full Speed mode GenFlight.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS Midatronics.menu.usb.none=None +Midatronics.menu.usb.pluggable=PluggableUSB +Midatronics.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED Midatronics.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) Midatronics.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC Midatronics.menu.usb.CDC=CDC (no generic 'Serial') @@ -15760,6 +15820,8 @@ Midatronics.menu.xusb.HSFS=High Speed in Full Speed mode Midatronics.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS SparkFun.menu.usb.none=None +SparkFun.menu.usb.pluggable=PluggableUSB +SparkFun.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED SparkFun.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART) SparkFun.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC SparkFun.menu.usb.CDC=CDC (no generic 'Serial') From 5cb44f202429300194bc3448e257417c461b265f Mon Sep 17 00:00:00 2001 From: Levi Gillis Date: Tue, 3 Mar 2026 14:01:53 +0100 Subject: [PATCH 5/5] feat(usb): add PluggableUSB examples for JoystickMouse and SerialUSB Signed-off-by: Aymane Bahssain --- .../PluggableUSB-JoystickMouse/main.cpp | 62 +++++++++++++++++++ .../examples/PluggableUSB-SerialUSB/main.cpp | 28 +++++++++ 2 files changed, 90 insertions(+) create mode 100644 libraries/USBDevice/examples/PluggableUSB-JoystickMouse/main.cpp create mode 100644 libraries/USBDevice/examples/PluggableUSB-SerialUSB/main.cpp diff --git a/libraries/USBDevice/examples/PluggableUSB-JoystickMouse/main.cpp b/libraries/USBDevice/examples/PluggableUSB-JoystickMouse/main.cpp new file mode 100644 index 0000000000..021632b34f --- /dev/null +++ b/libraries/USBDevice/examples/PluggableUSB-JoystickMouse/main.cpp @@ -0,0 +1,62 @@ +#include +#include "Mouse.h" +#include "Joystick.h" + +#define LED PC13 +#define HIGHPIN A7 +#define ADCPIN A6 +#define LOWPIN A5 + +Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, + JOYSTICK_TYPE_JOYSTICK, 0, 0, + true, false, false, false, false, false, + false, false, false, false, false); + +int analogvalue = 0; + +void setup() +{ + Joystick.setXAxisRange(0, 255); + Joystick.begin(); + Mouse.begin(); + USB_Begin(); + pinMode(HIGHPIN, OUTPUT); + pinMode(ADCPIN, INPUT); + pinMode(LOWPIN, OUTPUT); + digitalWrite(HIGHPIN, HIGH); + digitalWrite(LOWPIN, LOW); + pinMode(LED, OUTPUT); +} + +bool high = false; +int loops = 0; + +void loop() +{ + auto last = analogvalue; + auto val = analogRead(ADCPIN); + analogvalue <<= 1; + analogvalue += val; + analogvalue /= 3; + if (analogvalue >> 2 != last >> 2) + { + Joystick.setXAxis(analogvalue >> 2); + } + delay(1); + loops++; + if (loops >= 500) + { + // every 500ms + loops = 0; + high = !high; + digitalWrite(LED, high ? HIGH : LOW); + if (high) + { + Mouse.move(10, 0); + } + else + { + Mouse.move(-10, 0); + } + } +} diff --git a/libraries/USBDevice/examples/PluggableUSB-SerialUSB/main.cpp b/libraries/USBDevice/examples/PluggableUSB-SerialUSB/main.cpp new file mode 100644 index 0000000000..457577d376 --- /dev/null +++ b/libraries/USBDevice/examples/PluggableUSB-SerialUSB/main.cpp @@ -0,0 +1,28 @@ +#include +#include "USBCDC.h" + +USBCDC USBSerial; + +void setup() +{ + USBSerial.begin(115200); + USB_Begin(); + while (!USB_Running()) + { + // wait until usb connected + delay(5); + } + while (!USBSerial) + { + //(optional) wait until Serial port is connected + delay(5); + } +} + +void loop() +{ + if (USBSerial.available()) + { + USBSerial.print((char)USBSerial.read()); + } +} \ No newline at end of file