summaryrefslogtreecommitdiff
path: root/src/portable/st/stm32_fsdev/fsdev_common.h
blob: 0c67ee0c7bf8882dbc61862ca62df166f95d0475 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*
 * The MIT License (MIT)
 *
 * Copyright (c) N Conrad
 * Copyright (c) 2024, hathach (tinyusb.org)
 * Copyright (c) 2025, HiFiPhile (Zixun LI)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * This file is part of the TinyUSB stack.
 */

#ifndef TUSB_FSDEV_COMMON_H
#define TUSB_FSDEV_COMMON_H

#include "common/tusb_common.h"

#if CFG_TUD_ENABLED
#include "device/dcd.h"
#endif

#if CFG_TUH_ENABLED
#include "host/hcd.h"
#endif

#if defined(TUP_USBIP_FSDEV_STM32)
  #include "fsdev_stm32.h"
#elif defined(TUP_USBIP_FSDEV_CH32)
  #include "fsdev_ch32.h"
#elif defined(TUP_USBIP_FSDEV_AT32)
  #include "fsdev_at32.h"
#else
  #error "Unknown USB IP"
#endif

#ifdef __cplusplus
extern "C" {
#endif

// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it
// Both of these MUST be a multiple of 2, and are in byte units.
#ifndef FSDEV_BTABLE_BASE
#define FSDEV_BTABLE_BASE 0U
#endif

TU_VERIFY_STATIC(FSDEV_BTABLE_BASE % 8 == 0, "BTABLE base must be aligned to 8 bytes");

// FSDEV_PMA_SIZE is PMA buffer size in bytes.
// - 512-byte devices, access with a stride of two words (use every other 16-bit address)
// - 1024-byte devices, access with a stride of one word (use every 16-bit address)
// - 2048-byte devices, access with 32-bit address

// For purposes of accessing the packet
#if FSDEV_PMA_SIZE == 512
  // 1x16 bit / word access scheme
  #define FSDEV_PMA_STRIDE  2
  #define pma_access_scheme TU_ATTR_ALIGNED(4)
#elif FSDEV_PMA_SIZE == 1024
  // 2x16 bit / word access scheme
  #define FSDEV_PMA_STRIDE  1
  #define pma_access_scheme
#elif FSDEV_PMA_SIZE == 2048
  // 32 bit access scheme
  #define FSDEV_BUS_32BIT
  #define FSDEV_PMA_STRIDE  1
  #define pma_access_scheme
#endif

// The fsdev_bus_t type can be used for both register and PMA access necessities
#ifdef FSDEV_BUS_32BIT
  typedef uint32_t fsdev_bus_t;
  #define fsdevbus_unaligned_read(_addr)           tu_unaligned_read32(_addr)
  #define fsdevbus_unaligned_write(_addr, _value)  tu_unaligned_write32(_addr, _value)
#else
  typedef uint16_t fsdev_bus_t;
  #define fsdevbus_unaligned_read(_addr)           tu_unaligned_read16(_addr)
  #define fsdevbus_unaligned_write(_addr, _value)  tu_unaligned_write16(_addr, _value)
#endif

enum {
  FSDEV_BUS_SIZE = sizeof(fsdev_bus_t),
};

//--------------------------------------------------------------------+
// BTable Typedef
//--------------------------------------------------------------------+
enum {
  BTABLE_BUF_TX = 0,
  BTABLE_BUF_RX = 1
};

// hardware limit endpoint
#define FSDEV_EP_COUNT 8

// Buffer Table is located in Packet Memory Area (PMA) and therefore its address access is forced to either
// 16-bit or 32-bit depending on FSDEV_BUS_32BIT.
// 0: TX (IN), 1: RX (OUT)
typedef union {
  // data is strictly 16-bit access (address could be 32-bit aligned)
  struct {
    volatile pma_access_scheme uint16_t addr;
    volatile pma_access_scheme uint16_t count;
  } ep16[FSDEV_EP_COUNT][2];

  // strictly 32-bit access
  struct {
    volatile uint32_t count_addr;
  } ep32[FSDEV_EP_COUNT][2];
} fsdev_btable_t;

TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT*8*FSDEV_PMA_STRIDE, "size is not correct");
TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT*8 <= FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM");

#define FSDEV_BTABLE ((volatile fsdev_btable_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(FSDEV_BTABLE_BASE)))

typedef struct {
  volatile pma_access_scheme fsdev_bus_t value;
} fsdev_pma_buf_t;

#define PMA_BUF_AT(_addr) ((fsdev_pma_buf_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(_addr)))

//--------------------------------------------------------------------+
// Registers Typedef
//--------------------------------------------------------------------+

// volatile 32-bit aligned
#define _va32     volatile TU_ATTR_ALIGNED(4)

typedef struct {
  struct {
    _va32 fsdev_bus_t reg;
  }ep[FSDEV_EP_COUNT];

  _va32 uint32_t RESERVED7[8];       // Reserved
  _va32 fsdev_bus_t CNTR;            // 40: Control register
  _va32 fsdev_bus_t ISTR;            // 44: Interrupt status register
  _va32 fsdev_bus_t FNR;             // 48: Frame number register
  _va32 fsdev_bus_t DADDR;           // 4C: Device address register
  _va32 fsdev_bus_t BTABLE;          // 50: Buffer Table address register (16-bit only)
  _va32 fsdev_bus_t LPMCSR;          // 54: LPM Control and Status Register (32-bit only)
  _va32 fsdev_bus_t BCDR;            // 58: Battery Charging Detector Register (32-bit only)
} fsdev_regs_t;

TU_VERIFY_STATIC(offsetof(fsdev_regs_t, CNTR) == 0x40, "Wrong offset");
TU_VERIFY_STATIC(sizeof(fsdev_regs_t) == 0x5C, "Size is not correct");

#define FSDEV_REG ((fsdev_regs_t*) FSDEV_REG_BASE)


#ifndef USB_EPTX_STAT
#define USB_EPTX_STAT 0x0030U
#endif

#ifndef USB_EPRX_STAT
#define USB_EPRX_STAT 0x3000U
#endif

#ifndef USB_EPTX_STAT_Pos
#define USB_EPTX_STAT_Pos    4u
#endif

#ifndef USB_EP_DTOG_TX_Pos
#define USB_EP_DTOG_TX_Pos   6u
#endif

#ifndef USB_EP_CTR_TX_Pos
#define USB_EP_CTR_TX_Pos    7u
#endif

typedef enum {
  EP_STAT_DISABLED = 0,
  EP_STAT_STALL = 1,
  EP_STAT_NAK = 2,
  EP_STAT_VALID = 3
}ep_stat_t;

#define EP_STAT_MASK(_dir)  (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
#define EP_DTOG_MASK(_dir)  (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))

#define CH_STAT_MASK(_dir)  (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 8 : 0)))
#define CH_DTOG_MASK(_dir)  (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 8 : 0)))

//--------------------------------------------------------------------+
// Endpoint Helper
// - CTR is write 0 to clear
// - DTOG and STAT are write 1 to toggle
//--------------------------------------------------------------------+

TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_read(uint32_t ep_id) {
  return FSDEV_REG->ep[ep_id].reg;
}

TU_ATTR_ALWAYS_INLINE static inline void ep_write(uint32_t ep_id, uint32_t value, bool need_exclusive) {
  if (need_exclusive) {
    fsdev_int_disable(0);
  }

  FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t) value;

  if (need_exclusive) {
    fsdev_int_enable(0);
  }
}

TU_ATTR_ALWAYS_INLINE static inline void ep_write_clear_ctr(uint32_t ep_id, tusb_dir_t dir) {
  uint32_t reg = FSDEV_REG->ep[ep_id].reg;
  reg |= USB_EP_CTR_TX | USB_EP_CTR_RX;
  reg &= USB_EPREG_MASK;
  reg &= ~(1 << (USB_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
  ep_write(ep_id, reg, false);
}

TU_ATTR_ALWAYS_INLINE static inline void ep_change_status(uint32_t* reg, tusb_dir_t dir, ep_stat_t state) {
  *reg ^= (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
}

TU_ATTR_ALWAYS_INLINE static inline void ep_change_dtog(uint32_t* reg, tusb_dir_t dir, uint8_t state) {
  *reg ^= (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
}

TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(uint32_t reg) {
  return (reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS;
}

//--------------------------------------------------------------------+
// Channel Helper
// - Direction is opposite to endpoint direction
//--------------------------------------------------------------------+

TU_ATTR_ALWAYS_INLINE static inline uint32_t ch_read(uint32_t ch_id) {
  return ep_read(ch_id);
}

TU_ATTR_ALWAYS_INLINE static inline void ch_write(uint32_t ch_id, uint32_t value, bool need_exclusive) {
  ep_write(ch_id, value, need_exclusive);
}

TU_ATTR_ALWAYS_INLINE static inline void ch_write_clear_ctr(uint32_t ch_id, tusb_dir_t dir) {
  uint32_t reg = FSDEV_REG->ep[ch_id].reg;
  reg |= USB_EP_CTR_TX | USB_EP_CTR_RX;
  reg &= USB_EPREG_MASK;
  reg &= ~(1 << (USB_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 8 : 0)));
  ep_write(ch_id, reg, false);
}

TU_ATTR_ALWAYS_INLINE static inline void ch_change_status(uint32_t* reg, tusb_dir_t dir, ep_stat_t state) {
  *reg ^= (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 8 : 0)));
}

TU_ATTR_ALWAYS_INLINE static inline void ch_change_dtog(uint32_t* reg, tusb_dir_t dir, uint8_t state) {
  *reg ^= (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 8 : 0)));
}

//--------------------------------------------------------------------+
// BTable Helper
//--------------------------------------------------------------------+

TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_addr(uint32_t ep_id, uint8_t buf_id) {
#ifdef FSDEV_BUS_32BIT
  return FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr & 0x0000FFFFu;
#else
  return FSDEV_BTABLE->ep16[ep_id][buf_id].addr;
#endif
}

TU_ATTR_ALWAYS_INLINE static inline void btable_set_addr(uint32_t ep_id, uint8_t buf_id, uint16_t addr) {
#ifdef FSDEV_BUS_32BIT
  uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
  count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu);
  FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
#else
  FSDEV_BTABLE->ep16[ep_id][buf_id].addr = addr;
#endif
}

TU_ATTR_ALWAYS_INLINE static inline uint16_t btable_get_count(uint32_t ep_id, uint8_t buf_id) {
  uint16_t count;
#ifdef FSDEV_BUS_32BIT
  count = (FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr >> 16);
#else
  count = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
#endif
  return count & 0x3FFU;
}

TU_ATTR_ALWAYS_INLINE static inline void btable_set_count(uint32_t ep_id, uint8_t buf_id, uint16_t byte_count) {
#ifdef FSDEV_BUS_32BIT
  uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
  count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16);
  FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
#else
  uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
  cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU);
  FSDEV_BTABLE->ep16[ep_id][buf_id].count = cnt;
#endif
}

// Reset the USB Core
void fsdev_core_reset(void);

// De-initialize the USB Core
void fsdev_deinit(void);

// Aligned buffer size according to hardware
uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block);

// Set RX buffer size
void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount);

//--------------------------------------------------------------------+
// PMA (Packet Memory Area) Access
//--------------------------------------------------------------------+

// Write to packet memory area (PMA) from user memory
// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT
// - Uses unaligned for RAM (since M0 cannot access unaligned address)
bool fsdev_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes);

// Read from packet memory area (PMA) to user memory.
// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT
// - Uses unaligned for RAM (since M0 cannot access unaligned address)
bool fsdev_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes);

// Write to PMA from FIFO
bool fsdev_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes);

// Read from PMA to FIFO
bool fsdev_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes);

#ifdef __cplusplus
}
#endif

#endif /* TUSB_FSDEV_COMMON_H */