Skip to content

Commit 11f96dd

Browse files
Levi--GAymane-ST
andcommitted
feat(usb): integrate PluggableUSB-based USB stack for STM32
Co-authored-by: Aymane Bahssain <aymane.bahssain@st.com> Signed-off-by: Aymane Bahssain <aymane.bahssain@st.com>
1 parent 8d61785 commit 11f96dd

33 files changed

+3044
-29
lines changed

boards.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15438,6 +15438,8 @@ Nucleo_32.menu.xusb.HSFS=High Speed in Full Speed mode
1543815438
Nucleo_32.menu.xusb.HSFS.build.usb_speed=-DUSE_USB_HS -DUSE_USB_HS_IN_FS
1543915439

1544015440
Disco.menu.usb.none=None
15441+
Disco.menu.usb.pluggable=PluggableUSB
15442+
Disco.menu.usb.pluggable.build.enable_usb=-DUSBCON -DPLUGGABLE_USB_ENABLED -DHAL_PCD_MODULE_ENABLED
1544115443
Disco.menu.usb.CDCgen=CDC (generic 'Serial' supersede U(S)ART)
1544215444
Disco.menu.usb.CDCgen.build.enable_usb={build.usb_flags} -DUSBD_USE_CDC
1544315445
Disco.menu.usb.CDC=CDC (no generic 'Serial')
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
Copyright (c) 2015, Arduino LLC
3+
Original code (pre-library): Copyright (c) 2011, Peter Barrett
4+
* Modified by Levi Gillis @ 2022-2025
5+
Changes can be found in the git repo https://github.com/Levi--G/USBLibrarySTM32
6+
7+
Permission to use, copy, modify, and/or distribute this software for
8+
any purpose with or without fee is hereby granted, provided that the
9+
above copyright notice and this permission notice appear in all copies.
10+
11+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
12+
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13+
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
14+
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
15+
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
16+
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
17+
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
18+
SOFTWARE.
19+
*/
20+
21+
#ifndef HID_STM32_h
22+
#define HID_STM32_h
23+
24+
#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED)
25+
26+
#include "PluggableUSB.h"
27+
#include <Arduino.h>
28+
#include <stdint.h>
29+
30+
#define _USING_HID
31+
32+
// HID 'Driver'
33+
// ------------
34+
#define HID_GET_REPORT 0x01
35+
#define HID_GET_IDLE 0x02
36+
#define HID_GET_PROTOCOL 0x03
37+
#define HID_SET_REPORT 0x09
38+
#define HID_SET_IDLE 0x0A
39+
#define HID_SET_PROTOCOL 0x0B
40+
41+
#define HID_HID_DESCRIPTOR_TYPE 0x21
42+
#define HID_REPORT_DESCRIPTOR_TYPE 0x22
43+
#define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23
44+
45+
// HID subclass HID1.11 Page 8 4.2 Subclass
46+
#define HID_SUBCLASS_NONE 0
47+
#define HID_SUBCLASS_BOOT_INTERFACE 1
48+
49+
// HID Keyboard/Mouse bios compatible protocols HID1.11 Page 9 4.3 Protocols
50+
#define HID_PROTOCOL_NONE 0
51+
#define HID_PROTOCOL_KEYBOARD 1
52+
#define HID_PROTOCOL_MOUSE 2
53+
54+
// Normal or bios protocol (Keyboard/Mouse) HID1.11 Page 54 7.2.5 Get_Protocol Request
55+
// "protocol" variable is used for this purpose.
56+
#define HID_BOOT_PROTOCOL 0
57+
#define HID_REPORT_PROTOCOL 1
58+
59+
// HID Request Type HID1.11 Page 51 7.2.1 Get_Report Request
60+
#define HID_REPORT_TYPE_INPUT 1
61+
#define HID_REPORT_TYPE_OUTPUT 2
62+
#define HID_REPORT_TYPE_FEATURE 3
63+
64+
typedef struct {
65+
uint8_t len; // 9
66+
uint8_t dtype; // 0x21
67+
uint8_t addr;
68+
uint8_t versionL; // 0x101
69+
uint8_t versionH; // 0x101
70+
uint8_t country;
71+
uint8_t desctype; // 0x22 report
72+
uint8_t descLenL;
73+
uint8_t descLenH;
74+
} HIDDescDescriptor;
75+
76+
typedef struct {
77+
InterfaceDescriptor hid;
78+
HIDDescDescriptor desc;
79+
EndpointDescriptor in;
80+
} HIDDescriptor;
81+
82+
class HIDSubDescriptor {
83+
public:
84+
HIDSubDescriptor *next = NULL;
85+
HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) {}
86+
87+
const void *data;
88+
const uint16_t length;
89+
};
90+
91+
class HID_ : public PluggableUSBModule {
92+
public:
93+
HID_(void);
94+
int begin(void);
95+
int SendReport(uint8_t id, const void *data, int len);
96+
void AppendDescriptor(HIDSubDescriptor *node);
97+
98+
protected:
99+
// Implementation of the PluggableUSBModule
100+
int getInterface(uint8_t *interfaceCount);
101+
int getDescriptor(USBSetup &setup);
102+
bool setup(USBSetup &setup);
103+
uint8_t getShortName(char *name);
104+
105+
private:
106+
uint8_t epType[1];
107+
108+
HIDSubDescriptor *rootNode;
109+
uint16_t descriptorSize;
110+
111+
uint8_t protocol;
112+
uint8_t idle;
113+
};
114+
115+
// Replacement for global singleton.
116+
// This function prevents static-initialization-order-fiasco
117+
// https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use
118+
HID_ &HID();
119+
120+
#define D_HIDREPORT(length) {9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length)}
121+
122+
#endif // USBCON PLUGGABLE_USB_ENABLED
123+
124+
#endif // HID_STM32_h
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* PacketBuffer.h
3+
* Copyright (C) 2022-2025 Levi Gillis - All Rights Reserved
4+
* You may use, distribute and modify this code under the
5+
* terms of the GNU Lesser General Public License v3.0 license.
6+
*/
7+
8+
#ifndef PACKET_BUFFER_H_
9+
#define PACKET_BUFFER_H_
10+
11+
#if defined(USBCON) && defined(PLUGGABLE_USB_ENABLED)
12+
13+
#include "USBOptions.h"
14+
#include <stddef.h>
15+
#include <stdint.h>
16+
17+
#if PACKETBUFFER_COUNT < 2
18+
#warning "PacketBuffer is likely too small, expect issues"
19+
#endif
20+
21+
class PacketBuffer {
22+
public:
23+
virtual uint8_t *PrepareWrite(uint32_t &len) = 0;
24+
virtual void CommitWrite(uint32_t len) = 0;
25+
virtual uint8_t *PrepareRead(uint32_t &len) = 0;
26+
virtual void CommitRead(uint32_t len) = 0;
27+
virtual uint32_t Read(uint8_t *data, uint32_t len) = 0;
28+
virtual uint32_t Write(uint8_t *data, uint32_t len) = 0;
29+
virtual bool isEmpty() = 0;
30+
virtual bool isFull() = 0;
31+
virtual void clear() = 0;
32+
virtual uint32_t available() = 0;
33+
virtual uint32_t availableToWrite() = 0;
34+
virtual ~PacketBuffer() = default;
35+
};
36+
37+
template <uint32_t size>
38+
struct USBD_HID_BufferItem {
39+
uint32_t len = 0;
40+
uint32_t pos = 0;
41+
uint8_t buf[size];
42+
43+
uint32_t Read(uint8_t *data, uint32_t length)
44+
{
45+
uint8_t read = min(Remaining(), length);
46+
if (read) {
47+
memcpy(data, buf + pos, read);
48+
pos += read;
49+
}
50+
return read;
51+
}
52+
uint32_t Write(uint8_t *data, uint32_t length)
53+
{
54+
uint8_t write = min(size, length);
55+
if (write) {
56+
memcpy(buf, data, write);
57+
len = write;
58+
pos = 0;
59+
}
60+
return write;
61+
}
62+
uint32_t Remaining()
63+
{
64+
return len - pos;
65+
}
66+
bool Empty()
67+
{
68+
return Remaining() <= 0;
69+
}
70+
void Clear()
71+
{
72+
len = 0;
73+
pos = 0;
74+
}
75+
void WriteLength(uint32_t length)
76+
{
77+
len = length;
78+
pos = 0;
79+
}
80+
void ReadLength(uint32_t length)
81+
{
82+
pos = min(pos + length, size);
83+
}
84+
};
85+
86+
template <uint32_t buffersize, uint32_t capacity>
87+
class SplitPacketBuffer : public PacketBuffer {
88+
public:
89+
virtual uint8_t *PrepareWrite(uint32_t &len)
90+
{
91+
isPrepared = true;
92+
if (writeHead == readHead) {
93+
// overwrite last
94+
writeHead = prevUnsafeHead(writeHead);
95+
}
96+
len = min(buffersize, len);
97+
return buffer[writeHead].buf;
98+
}
99+
virtual void CommitWrite(uint32_t len)
100+
{
101+
if (isPrepared) {
102+
buffer[writeHead].WriteLength(len);
103+
writeHead = newWriteHead();
104+
}
105+
}
106+
virtual uint8_t *PrepareRead(uint32_t &len)
107+
{
108+
isPrepared = true;
109+
if (!buffer[readHead].Remaining()) {
110+
readHead = newReadHead();
111+
}
112+
len = min(len, buffer[readHead].Remaining());
113+
return buffer[readHead].buf;
114+
}
115+
virtual void CommitRead(uint32_t len)
116+
{
117+
if (isPrepared) {
118+
buffer[readHead].ReadLength(len);
119+
if (buffer[readHead].Empty()) {
120+
readHead = newReadHead();
121+
}
122+
}
123+
}
124+
uint32_t Read(uint8_t *data, uint32_t len)
125+
{
126+
if (!buffer[readHead].Remaining()) {
127+
readHead = newReadHead();
128+
}
129+
uint32_t read = 0;
130+
if (buffer[readHead].Remaining()) {
131+
read = buffer[readHead].Read(data, len);
132+
if (buffer[readHead].Empty()) {
133+
readHead = newReadHead();
134+
}
135+
}
136+
return read;
137+
}
138+
virtual uint32_t Write(uint8_t *data, uint32_t len)
139+
{
140+
uint32_t write = 0;
141+
if (writeHead == readHead) {
142+
// overwrite last
143+
writeHead = prevUnsafeHead(writeHead);
144+
}
145+
write = buffer[writeHead].Write(data, len);
146+
writeHead = newWriteHead();
147+
return write;
148+
}
149+
virtual bool isEmpty()
150+
{
151+
return available() == 0;
152+
}
153+
virtual bool isFull()
154+
{
155+
#if PACKETBUFFER_ALLOW_OVERWRITE
156+
return false;
157+
#else
158+
return readHead == writeHead;
159+
#endif
160+
}
161+
void clear()
162+
{
163+
readHead = 0;
164+
writeHead = 1;
165+
}
166+
virtual uint32_t available()
167+
{
168+
if (!buffer[readHead].Remaining()) {
169+
readHead = newReadHead();
170+
}
171+
return getAvailable(readHead, writeHead);
172+
}
173+
virtual uint32_t availableToWrite()
174+
{
175+
return readHead == writeHead ? 0 : capacity;
176+
}
177+
178+
uint32_t getAvailable(int head, int otherhead)
179+
{
180+
#if PACKETBUFFER_USE_FAST_AVAILABLE
181+
return buffer[head].Remaining();
182+
#else
183+
uint32_t16_t total = 0;
184+
auto ptr = head;
185+
auto endptr = otherhead;
186+
for (size_t i = 0; i != endptr; i++) {
187+
total += buffer[i].Remaining();
188+
i = newUnsafeHead(ptr);
189+
}
190+
return total;
191+
#endif
192+
}
193+
194+
private:
195+
USBD_HID_BufferItem<buffersize> buffer[capacity];
196+
int readHead = 0;
197+
int writeHead = 1;
198+
bool isPrepared = false;
199+
int newReadHead()
200+
{
201+
auto result = nextUnsafeHead(readHead);
202+
if (result == writeHead) {
203+
return readHead;
204+
}
205+
return result;
206+
}
207+
int newWriteHead()
208+
{
209+
return nextUnsafeHead(writeHead);
210+
}
211+
int nextUnsafeHead(int current)
212+
{
213+
auto result = current + 1;
214+
if (result >= capacity) {
215+
result = 0;
216+
}
217+
return result;
218+
}
219+
int prevUnsafeHead(int current)
220+
{
221+
auto result = current - 1;
222+
if (result < 0) {
223+
result = capacity - 1;
224+
}
225+
return result;
226+
}
227+
};
228+
229+
#endif
230+
#endif // PACKET_BUFFER_H_

0 commit comments

Comments
 (0)