Skip to content

Commit 395189a

Browse files
author
rechrtb
committed
Support switching between USB host and device
1 parent 11f5e89 commit 395189a

File tree

3 files changed

+226
-48
lines changed

3 files changed

+226
-48
lines changed

src/SerialCDC_tusb.cpp

+72-19
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#if SUPPORT_USB
99

10+
#include "TinyUsbInterface.h"
1011
#include "SerialCDC_tusb.h"
1112

1213
#if CORE_USES_TINYUSB
@@ -25,18 +26,35 @@ SerialCDC::SerialCDC() noexcept
2526

2627
void SerialCDC::Start(Pin p) noexcept
2728
{
29+
#if CFG_TUH_ENABLED
30+
if (CoreUsbIsHostMode())
31+
{
32+
return;
33+
}
34+
#endif
35+
vBusPin = p;
2836
while (!tud_inited()) { delay(10); }
2937
running = true;
3038
}
3139

3240
void SerialCDC::end() noexcept
3341
{
42+
#if CFG_TUH_ENABLED
43+
if (CoreUsbIsHostMode())
44+
{
45+
return;
46+
}
47+
#endif
3448
running = false;
3549
}
3650

3751
bool SerialCDC::IsConnected() const noexcept
3852
{
39-
return tud_cdc_connected();
53+
return
54+
#if CFG_TUH_ENABLED
55+
!CoreUsbIsHostMode() &&
56+
#endif
57+
tud_cdc_connected();
4058
}
4159

4260
// Overridden virtual functions
@@ -47,36 +65,57 @@ bool SerialCDC::IsConnected() const noexcept
4765
// available() returned nonzero bit read() never read it. Now we check neither when reading.
4866
int SerialCDC::read() noexcept
4967
{
50-
if (!running)
51-
{
52-
return -1;
53-
}
54-
55-
if (tud_cdc_available())
56-
{
57-
return tud_cdc_read_char();
58-
}
59-
return -1;
68+
if (!running
69+
#if CFG_TUH_ENABLED
70+
|| CoreUsbIsHostMode()
71+
#endif
72+
)
73+
{
74+
return -1;
75+
}
76+
77+
if (tud_cdc_available())
78+
{
79+
return tud_cdc_read_char();
80+
}
81+
return -1;
6082
}
6183

6284
int SerialCDC::available() noexcept
6385
{
64-
if (!running)
65-
{
66-
return 0;
67-
}
86+
if (!running
87+
#if CFG_TUH_ENABLED
88+
|| CoreUsbIsHostMode()
89+
#endif
90+
)
91+
{
92+
return 0;
93+
}
6894

69-
return tud_cdc_available();
95+
return tud_cdc_available();
7096
}
7197

7298
size_t SerialCDC::readBytes(char * _ecv_array buffer, size_t length) noexcept
7399
{
100+
if (!running
101+
#if CFG_TUH_ENABLED
102+
|| CoreUsbIsHostMode()
103+
#endif
104+
)
105+
{
106+
return 0;
107+
}
108+
74109
return tud_cdc_read (buffer, length);
75110
}
76111

77112
void SerialCDC::flush() noexcept
78113
{
79-
if (!running)
114+
if (!running
115+
#if CFG_TUH_ENABLED
116+
|| CoreUsbIsHostMode()
117+
#endif
118+
)
80119
{
81120
return;
82121
}
@@ -86,7 +125,11 @@ void SerialCDC::flush() noexcept
86125

87126
size_t SerialCDC::canWrite() noexcept
88127
{
89-
if (!running)
128+
if (!running
129+
#if CFG_TUH_ENABLED
130+
|| CoreUsbIsHostMode()
131+
#endif
132+
)
90133
{
91134
return 0;
92135
}
@@ -97,13 +140,23 @@ size_t SerialCDC::canWrite() noexcept
97140
// Write single character, blocking
98141
size_t SerialCDC::write(uint8_t c) noexcept
99142
{
143+
#if CFG_TUH_ENABLED
144+
if (CoreUsbIsHostMode())
145+
{
146+
return 0;
147+
}
148+
#endif
100149
return write(&c, 1);
101150
}
102151

103152
// Blocking write block
104153
size_t SerialCDC::write(const uint8_t *buf, size_t length) noexcept
105154
{
106-
if (!running)
155+
if (!running
156+
#if CFG_TUH_ENABLED
157+
|| CoreUsbIsHostMode()
158+
#endif
159+
)
107160
{
108161
return 0;
109162
}

src/TinyUsbInterface.cpp

+140-28
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include "class/hid/hid_device.h"
3030
#include "class/audio/audio.h"
3131
#include "class/midi/midi.h"
32+
#include "host/hcd.h"
33+
#include "device/dcd.h"
3234

3335
#if SAME70
3436

@@ -255,37 +257,41 @@ extern "C" const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t lang
255257
return desc_str;
256258
}
257259

260+
#if CFG_TUH_ENABLED
261+
static volatile bool isHostMode = false;
262+
static volatile bool changingMode = false;
263+
264+
static Pin UsbVbusDetect;
265+
static Pin UsbVbusOn;
266+
static Pin UsbModeSwitch;
267+
static Pin UsbModeDetect;
268+
#endif
269+
258270
// Call this to initialise the hardware
271+
#if CFG_TUH_ENABLED
272+
void CoreUsbInit(NvicPriority priority, Pin usbVbusDetect, Pin usbVbusOn, Pin usbModeSwitch, Pin usbModeDetect) noexcept
273+
#else
259274
void CoreUsbInit(NvicPriority priority) noexcept
275+
#endif
260276
{
261-
#if SAME70
262277

278+
#if CFG_TUH_ENABLED
279+
UsbVbusDetect = usbVbusDetect;
280+
UsbVbusOn = usbVbusOn;
281+
UsbModeSwitch = usbModeSwitch;
282+
UsbModeDetect = usbModeDetect;
283+
#endif
284+
285+
#if SAME70
263286
// Set the USB interrupt priority to a level that is allowed to make FreeRTOS calls
264287
NVIC_SetPriority(USBHS_IRQn, priority);
265288

266-
// Enable peripheral clock for USBHS
267-
pmc_enable_periph_clk(ID_USBHS);
268-
269289
// Start the UPLL clock. The default divider is 40 which is correct for 12MHz crystal.
270290
pmc_enable_upll_clock();
271-
272-
// From the datasheet:
273-
// "Before enabling the USB clock in the Power Management Controller, the USBHS must be enabled
274-
// (by writing a one to the USBHS_CTRL.USBE bit and a zero to the USBHS_CTRL.FRZCLK bit)"
275-
# if 0
276-
pmc_switch_udpck_to_upllck(1 - 1);
277-
USBHS->USBHS_DEVCTRL = USBHS_DEVCTRL_DETACH | USBHS_DEVCTRL_SPDCONF_FORCED_FS;
278-
# elif TUD_OPT_HIGH_SPEED
279-
pmc_switch_udpck_to_upllck(1 - 1);
280-
USBHS->USBHS_DEVCTRL = USBHS_DEVCTRL_DETACH;
281-
# else
282-
pmc_switch_udpck_to_upllck(10 - 1); // when high speed is disabled, tinyusb uses low power mode, which requires a 48MHz clock
283-
USBHS->USBHS_DEVCTRL = USBHS_DEVCTRL_SPDCONF_LOW_POWER | USBHS_DEVCTRL_DETACH;
284-
# endif
285-
USBHS->USBHS_CTRL = USBHS_CTRL_UIMOD_DEVICE | USBHS_CTRL_USBE;
291+
pmc_switch_udpck_to_upllck(CONFIG_USBCLK_DIV - 1);
286292
pmc_enable_udpck();
287-
288-
pmc_set_fast_startup_input(PMC_FSMR_USBAL);
293+
// Enable peripheral clock for USBHS
294+
pmc_enable_periph_clk(ID_USBHS);
289295

290296
#elif SAME5x
291297

@@ -322,24 +328,125 @@ void CoreUsbInit(NvicPriority priority) noexcept
322328
#endif
323329
}
324330

331+
#if CFG_TUH_ENABLED
332+
bool CoreUsbSetHostMode(bool hostMode, const StringRef& reply)
333+
{
334+
if (changingMode)
335+
{
336+
reply.printf("Previous USB mode change still in progress");
337+
return false;
338+
}
339+
340+
if (hostMode && digitalRead(UsbVbusDetect))
341+
{
342+
reply.printf("Unable to change to host mode, board plugged in to computer\n");
343+
return false;
344+
}
345+
346+
if (hostMode == CoreUsbIsHostMode())
347+
{
348+
reply.printf("Already in %s mode\n", hostMode ? "host" : "device");
349+
}
350+
else
351+
{
352+
CoreUsbStop();
353+
changingMode = true;
354+
}
355+
356+
return true;
357+
}
358+
359+
bool CoreUsbIsHostMode()
360+
{
361+
return isHostMode;
362+
}
363+
325364
// USB Device Driver task
326365
// This top level thread process all usb events and invoke callbacks
327366
extern "C" void CoreUsbDeviceTask(void* param) noexcept
328367
{
329368
(void)param;
330369

331-
// This should be called after scheduler/kernel is started.
332-
// Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
333-
tusb_init();
370+
while (true)
371+
{
372+
auto tusb_init = isHostMode ? tuh_init : tud_init;
373+
auto tusb_int_enable = isHostMode ? hcd_int_enable : dcd_int_enable;
374+
auto tusb_task = isHostMode ? tuh_task_ext : tud_task_ext;
375+
376+
digitalWrite(UsbVbusOn, isHostMode);
377+
tusb_init(0);
378+
if (changingMode)
379+
{
380+
if (isHostMode)
381+
{
382+
if (tuh_inited())
383+
{
384+
hcd_init(0);
385+
}
386+
}
387+
else
388+
{
389+
tud_connect();
390+
}
391+
}
392+
tusb_int_enable(0);
393+
394+
changingMode = false;
395+
while (!changingMode)
396+
{
397+
tusb_task(100, false);
398+
}
399+
400+
// Deinit current tinyUSB context.
401+
if (isHostMode)
402+
{
403+
hcd_event_device_remove(0, false);
404+
}
405+
else
406+
{
407+
tud_disconnect();
408+
}
409+
tusb_task(100, false);
410+
411+
// Reset USB hardware.
412+
USBHS->USBHS_CTRL &= ~USBHS_CTRL_USBE;
413+
for (int i = 9; i >= 0; i--)
414+
{
415+
if (isHostMode)
416+
{
417+
USBHS->USBHS_HSTPIPCFG[i] &= ~(USBHS_HSTPIPCFG_ALLOC);
418+
}
419+
else
420+
{
421+
USBHS->USBHS_DEVEPTCFG[i] &= ~(USBHS_DEVEPTCFG_ALLOC);
422+
}
423+
}
424+
425+
// Complete the mode change.
426+
isHostMode = !isHostMode;
427+
}
428+
}
429+
#else
430+
extern "C" void CoreUsbDeviceTask(void* param) noexcept
431+
{
432+
(void)param;
334433

335-
// RTOS forever loop
336-
while (1)
434+
tud_init(0);
435+
while (true)
337436
{
338-
// tinyusb device task
339437
tud_task();
340-
// tud_cdc_write_flush();
341438
}
342439
}
440+
#endif
441+
442+
void CoreUsbStop()
443+
{
444+
#if CFG_TUH_ENABLED
445+
digitalWrite(UsbVbusOn, false);
446+
#endif
447+
NVIC_DisableIRQ((IRQn_Type)ID_USBHS);
448+
USBHS->USBHS_CTRL &= ~USBHS_CTRL_USBE;
449+
}
343450

344451
#if RP2040 // RP2040 USB configuration has HID enabled by default
345452

@@ -389,7 +496,12 @@ uint32_t numUsbInterrupts = 0;
389496
extern "C" void USBHS_Handler() noexcept
390497
{
391498
++numUsbInterrupts;
499+
#if CFG_TUH_ENABLED
500+
auto tusb_handler = isHostMode ? tuh_int_handler : tud_int_handler;
501+
tusb_handler(0);
502+
#else
392503
tud_int_handler(0);
504+
#endif
393505
}
394506

395507
#elif SAME5x

0 commit comments

Comments
 (0)