Open FFBoard
Open source force feedback firmware
dcd_ch32_usbfs.c
Go to the documentation of this file.
1/*
2 * The MIT License (MIT)
3 *
4 * Copyright (c) 2024 Matthew Tran
5 * Copyright (c) 2024 hathach
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 * This file is part of the TinyUSB stack.
26 */
27
28#include "tusb_option.h"
29
30#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBFS) && CFG_TUD_WCH_USBIP_USBFS
31
32#include "device/dcd.h"
33#include "ch32_usbfs_reg.h"
34
35/* private defines */
36#define EP_MAX (8)
37
38#define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep])
39#define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep])
40#define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep])
41#define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep])
42
43/* private data */
44struct usb_xfer {
45 bool valid;
46 uint8_t* buffer;
47 size_t len;
49 size_t max_size;
50};
51
52static struct {
53 bool ep0_tog;
54 bool isochronous[EP_MAX];
55 struct usb_xfer xfer[EP_MAX][2];
56 TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64];
57 TU_ATTR_ALIGNED(4) struct {
58 // OUT transfers >64 bytes will overwrite queued IN data!
59 uint8_t out[64];
60 uint8_t in[1023];
61 uint8_t pad;
64
65/* private helpers */
66static void update_in(uint8_t rhport, uint8_t ep, bool force) {
67 struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_IN];
68 if (xfer->valid) {
69 if (force || xfer->len) {
70 size_t len = TU_MIN(xfer->max_size, xfer->len);
71 if (ep == 0) {
72 memcpy(data.buffer[ep][TUSB_DIR_OUT], xfer->buffer, len); // ep0 uses same chunk
73 } else if (ep == 3) {
74 memcpy(data.ep3_buffer.in, xfer->buffer, len);
75 } else {
76 memcpy(data.buffer[ep][TUSB_DIR_IN], xfer->buffer, len);
77 }
78 xfer->buffer += len;
79 xfer->len -= len;
81
82 EP_TX_LEN(ep) = len;
83 if (ep == 0) {
84 EP_TX_CTRL(0) = USBFS_EP_T_RES_ACK | (data.ep0_tog ? USBFS_EP_T_TOG : 0);
85 data.ep0_tog = !data.ep0_tog;
86 } else if (data.isochronous[ep]) {
87 EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NYET;
88 } else {
89 EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_ACK;
90 }
91 } else {
92 xfer->valid = false;
93 EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NAK;
95 rhport, ep | TUSB_DIR_IN_MASK, xfer->processed_len,
97 }
98 }
99}
100
101static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) {
102 struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_OUT];
103 if (xfer->valid) {
104 size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len));
105 if (ep == 3) {
106 memcpy(xfer->buffer, data.ep3_buffer.out, len);
107 } else {
108 memcpy(xfer->buffer, data.buffer[ep][TUSB_DIR_OUT], len);
109 }
110 xfer->buffer += len;
111 xfer->len -= len;
113
114 if (xfer->len == 0 || len < xfer->max_size) {
115 xfer->valid = false;
117 }
118
119 if (ep == 0) {
120 EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
121 }
122 }
123}
124
125/* public functions */
126bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
127 (void) rh_init;
128 // init registers
129 USBOTG_FS->BASE_CTRL = USBFS_CTRL_SYS_CTRL | USBFS_CTRL_INT_BUSY | USBFS_CTRL_DMA_EN;
130 USBOTG_FS->UDEV_CTRL = USBFS_UDEV_CTRL_PD_DIS | USBFS_UDEV_CTRL_PORT_EN;
131 USBOTG_FS->DEV_ADDR = 0x00;
132
133 USBOTG_FS->INT_FG = 0xFF;
134 USBOTG_FS->INT_EN = USBFS_INT_EN_BUS_RST | USBFS_INT_EN_TRANSFER | USBFS_INT_EN_SUSPEND;
135
136 // setup endpoint 0
137 EP_DMA(0) = (uint32_t) &data.buffer[0][0];
138 EP_TX_LEN(0) = 0;
139 EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
140 EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
141
142 // enable other endpoints but NAK everything
143 USBOTG_FS->UEP4_1_MOD = 0xCC;
144 USBOTG_FS->UEP2_3_MOD = 0xCC;
145 USBOTG_FS->UEP5_6_MOD = 0xCC;
146 USBOTG_FS->UEP7_MOD = 0x0C;
147
148 for (uint8_t ep = 1; ep < EP_MAX; ep++) {
149 EP_DMA(ep) = (uint32_t) &data.buffer[ep][0];
150 EP_TX_LEN(ep) = 0;
151 EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
152 EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK;
153 }
154 EP_DMA(3) = (uint32_t) &data.ep3_buffer.out[0];
155
156 dcd_connect(rhport);
157
158 return true;
159}
160
161void dcd_int_handler(uint8_t rhport) {
162 (void) rhport;
163 uint8_t status = USBOTG_FS->INT_FG;
164 if (status & USBFS_INT_FG_TRANSFER) {
165 uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(USBOTG_FS->INT_ST);
166 uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(USBOTG_FS->INT_ST);
167
168 switch (token) {
169 case PID_OUT: {
170 uint16_t rx_len = USBOTG_FS->RX_LEN;
171 update_out(rhport, ep, rx_len);
172 break;
173 }
174
175 case PID_IN:
176 update_in(rhport, ep, false);
177 break;
178
179 case PID_SETUP:
180 // setup clears stall
181 EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
182 EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
183
184 data.ep0_tog = true;
185 dcd_event_setup_received(rhport, &data.buffer[0][TUSB_DIR_OUT][0], true);
186 break;
187 }
188
189 USBOTG_FS->INT_FG = USBFS_INT_FG_TRANSFER;
190 } else if (status & USBFS_INT_FG_BUS_RST) {
191 data.ep0_tog = true;
192 data.xfer[0][TUSB_DIR_OUT].max_size = 64;
193 data.xfer[0][TUSB_DIR_IN].max_size = 64;
194
195 dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
196
197 USBOTG_FS->DEV_ADDR = 0x00;
198 EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
199
200 USBOTG_FS->INT_FG = USBFS_INT_FG_BUS_RST;
201 } else if (status & USBFS_INT_FG_SUSPEND) {
202 dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND};
203 dcd_event_handler(&event, true);
204 USBOTG_FS->INT_FG = USBFS_INT_FG_SUSPEND;
205 }
206}
207
208void dcd_int_enable(uint8_t rhport) {
209 (void) rhport;
210 NVIC_EnableIRQ(USBHD_IRQn);
211}
212
213void dcd_int_disable(uint8_t rhport) {
214 (void) rhport;
215 NVIC_DisableIRQ(USBHD_IRQn);
216}
217
218void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
219 (void) dev_addr;
220 dcd_edpt_xfer(rhport, 0x80, NULL, 0); // zlp status response
221}
222
223void dcd_remote_wakeup(uint8_t rhport) {
224 (void) rhport;
225 // TODO optional
226}
227
228void dcd_connect(uint8_t rhport) {
229 (void) rhport;
230 USBOTG_FS->BASE_CTRL |= USBFS_CTRL_DEV_PUEN;
231}
232
233void dcd_disconnect(uint8_t rhport) {
234 (void) rhport;
235 USBOTG_FS->BASE_CTRL &= ~USBFS_CTRL_DEV_PUEN;
236}
237
238void dcd_sof_enable(uint8_t rhport, bool en) {
239 (void) rhport;
240 (void) en;
241
242 // TODO implement later
243}
244
246 (void) rhport;
250 USBOTG_FS->DEV_ADDR = (uint8_t) request->wValue;
251 }
252 EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
253 EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
254}
255
256bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) {
257 (void) rhport;
258 uint8_t ep = tu_edpt_number(desc_ep->bEndpointAddress);
259 uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress);
260 TU_ASSERT(ep < EP_MAX);
261
262 data.isochronous[ep] = desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS;
263 data.xfer[ep][dir].max_size = tu_edpt_packet_size(desc_ep);
264
265 if (ep != 0) {
266 if (dir == TUSB_DIR_OUT) {
267 if (data.isochronous[ep]) {
268 EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET;
269 } else {
270 EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_ACK;
271 }
272 } else {
273 EP_TX_LEN(ep) = 0;
274 EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
275 }
276 }
277 return true;
278}
279
280void dcd_edpt_close_all(uint8_t rhport) {
281 (void) rhport;
282 // TODO optional
283}
284
285void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
286 (void) rhport;
287 (void) ep_addr;
288 // TODO optional
289}
290
291bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
292 (void) rhport;
293 uint8_t ep = tu_edpt_number(ep_addr);
294 uint8_t dir = tu_edpt_dir(ep_addr);
295
296 struct usb_xfer* xfer = &data.xfer[ep][dir];
297 dcd_int_disable(rhport);
298 xfer->valid = true;
299 xfer->buffer = buffer;
301 xfer->processed_len = 0;
302 dcd_int_enable(rhport);
303
304 if (dir == TUSB_DIR_IN) {
305 update_in(rhport, ep, true);
306 }
307 return true;
308}
309
310void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
311 (void) rhport;
312 uint8_t ep = tu_edpt_number(ep_addr);
313 uint8_t dir = tu_edpt_dir(ep_addr);
314 if (ep == 0) {
315 if (dir == TUSB_DIR_OUT) {
316 EP_RX_CTRL(0) = USBFS_EP_R_RES_STALL;
317 } else {
318 EP_TX_LEN(0) = 0;
319 EP_TX_CTRL(0) = USBFS_EP_T_RES_STALL;
320 }
321 } else {
322 if (dir == TUSB_DIR_OUT) {
323 EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~USBFS_EP_R_RES_MASK) | USBFS_EP_R_RES_STALL;
324 } else {
325 EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~USBFS_EP_T_RES_MASK) | USBFS_EP_T_RES_STALL;
326 }
327 }
328}
329
330void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
331 (void) rhport;
332 uint8_t ep = tu_edpt_number(ep_addr);
333 uint8_t dir = tu_edpt_dir(ep_addr);
334 if (ep == 0) {
335 if (dir == TUSB_DIR_OUT) {
336 EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
337 }
338 } else {
339 if (dir == TUSB_DIR_OUT) {
340 EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~(USBFS_EP_R_RES_MASK | USBFS_EP_R_TOG)) | USBFS_EP_R_RES_ACK;
341 } else {
342 EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK | USBFS_EP_T_TOG)) | USBFS_EP_T_RES_NAK;
343 }
344 }
345}
346
347#endif
static TU_ATTR_ALWAYS_INLINE void dcd_event_bus_signal(uint8_t rhport, dcd_eventid_t eid, bool in_isr)
Definition: dcd.h:196
static TU_ATTR_ALWAYS_INLINE void dcd_event_xfer_complete(uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
Definition: dcd.h:222
static TU_ATTR_ALWAYS_INLINE void dcd_event_setup_received(uint8_t rhport, uint8_t const *setup, bool in_isr)
Definition: dcd.h:213
dcd_event_t
Definition: dcd.h:86
void dcd_event_handler(dcd_event_t const *event, bool in_isr)
Definition: usbd.c:1153
struct TU_ATTR_ALIGNED(4)
Definition: dcd.h:55
static struct @612 data
static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len)
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr)
bool ep0_tog
void dcd_int_handler(uint8_t rhport)
void dcd_disconnect(uint8_t rhport)
static void update_in(uint8_t rhport, uint8_t ep, bool force)
void dcd_edpt_close_all(uint8_t rhport)
bool isochronous[EP_MAX]
ep3_buffer
void dcd_int_disable(uint8_t rhport)
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
struct usb_xfer xfer[EP_MAX][2]
void dcd_connect(uint8_t rhport)
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request)
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init)
void dcd_int_enable(uint8_t rhport)
void dcd_remote_wakeup(uint8_t rhport)
void dcd_sof_enable(uint8_t rhport, bool en)
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep)
uint16_t total_bytes
Definition: dcd_nuc505.c:113
uint8_t dev_addr
Definition: dcd_pic32mz.c:81
uint8_t const * buffer
Definition: midi_device.h:100
@ PID_IN
Definition: ohci.c:138
@ PID_SETUP
Definition: ohci.c:136
@ PID_OUT
Definition: ohci.c:137
static void * memcpy(void *dst, const void *src, size_t n)
Definition: ringbuffer.c:8
AUDIO Channel Cluster Descriptor (4.1)
Definition: audio.h:647
uint16_t wValue
Definition: audio.h:934
struct TU_ATTR_PACKED::@16::TU_ATTR_PACKED bmRequestType_bit
uint8_t bmAttributes
See: audio_clock_source_attribute_t.
Definition: audio.h:672
uint8_t bEndpointAddress
Definition: video.h:306
uint8_t bRequest
Request type audio_cs_req_t.
Definition: audio.h:831
size_t max_size
uint8_t * buffer
size_t len
size_t processed_len
@ TUSB_DIR_IN
Definition: tusb_types.h:67
@ TUSB_DIR_OUT
Definition: tusb_types.h:66
@ TUSB_DIR_IN_MASK
Definition: tusb_types.h:69
@ TUSB_REQ_SET_ADDRESS
Definition: tusb_types.h:127
static TU_ATTR_ALWAYS_INLINE uint8_t tu_edpt_number(uint8_t addr)
Definition: tusb_types.h:507
@ XFER_RESULT_SUCCESS
Definition: tusb_types.h:237
@ TUSB_REQ_RCPT_DEVICE
Definition: tusb_types.h:151
static TU_ATTR_ALWAYS_INLINE uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const *desc_ep)
Definition: tusb_types.h:515
@ TUSB_XFER_ISOCHRONOUS
Definition: tusb_types.h:60
TU_ATTR_PACKED_END TU_ATTR_BIT_FIELD_ORDER_END static TU_ATTR_ALWAYS_INLINE tusb_dir_t tu_edpt_dir(uint8_t addr)
Definition: tusb_types.h:502
@ TUSB_REQ_TYPE_STANDARD
Definition: tusb_types.h:144
CFG_TUH_MEM_ALIGN tusb_control_request_t request
Definition: usbh.c:259