|
29 | 29 | #include "class/hid/hid_device.h"
|
30 | 30 | #include "class/audio/audio.h"
|
31 | 31 | #include "class/midi/midi.h"
|
| 32 | +#include "host/hcd.h" |
| 33 | +#include "device/dcd.h" |
32 | 34 |
|
33 | 35 | #if SAME70
|
34 | 36 |
|
@@ -255,37 +257,41 @@ extern "C" const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t lang
|
255 | 257 | return desc_str;
|
256 | 258 | }
|
257 | 259 |
|
| 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 | + |
258 | 270 | // 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 |
259 | 274 | void CoreUsbInit(NvicPriority priority) noexcept
|
| 275 | +#endif |
260 | 276 | {
|
261 |
| -#if SAME70 |
262 | 277 |
|
| 278 | +#if CFG_TUH_ENABLED |
| 279 | + UsbVbusDetect = usbVbusDetect; |
| 280 | + UsbVbusOn = usbVbusOn; |
| 281 | + UsbModeSwitch = usbModeSwitch; |
| 282 | + UsbModeDetect = usbModeDetect; |
| 283 | +#endif |
| 284 | + |
| 285 | +#if SAME70 |
263 | 286 | // Set the USB interrupt priority to a level that is allowed to make FreeRTOS calls
|
264 | 287 | NVIC_SetPriority(USBHS_IRQn, priority);
|
265 | 288 |
|
266 |
| - // Enable peripheral clock for USBHS |
267 |
| - pmc_enable_periph_clk(ID_USBHS); |
268 |
| - |
269 | 289 | // Start the UPLL clock. The default divider is 40 which is correct for 12MHz crystal.
|
270 | 290 | 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); |
286 | 292 | 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); |
289 | 295 |
|
290 | 296 | #elif SAME5x
|
291 | 297 |
|
@@ -322,24 +328,125 @@ void CoreUsbInit(NvicPriority priority) noexcept
|
322 | 328 | #endif
|
323 | 329 | }
|
324 | 330 |
|
| 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 | + |
325 | 364 | // USB Device Driver task
|
326 | 365 | // This top level thread process all usb events and invoke callbacks
|
327 | 366 | extern "C" void CoreUsbDeviceTask(void* param) noexcept
|
328 | 367 | {
|
329 | 368 | (void)param;
|
330 | 369 |
|
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; |
334 | 433 |
|
335 |
| - // RTOS forever loop |
336 |
| - while (1) |
| 434 | + tud_init(0); |
| 435 | + while (true) |
337 | 436 | {
|
338 |
| - // tinyusb device task |
339 | 437 | tud_task();
|
340 |
| -// tud_cdc_write_flush(); |
341 | 438 | }
|
342 | 439 | }
|
| 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 | +} |
343 | 450 |
|
344 | 451 | #if RP2040 // RP2040 USB configuration has HID enabled by default
|
345 | 452 |
|
@@ -389,7 +496,12 @@ uint32_t numUsbInterrupts = 0;
|
389 | 496 | extern "C" void USBHS_Handler() noexcept
|
390 | 497 | {
|
391 | 498 | ++numUsbInterrupts;
|
| 499 | +#if CFG_TUH_ENABLED |
| 500 | + auto tusb_handler = isHostMode ? tuh_int_handler : tud_int_handler; |
| 501 | + tusb_handler(0); |
| 502 | +#else |
392 | 503 | tud_int_handler(0);
|
| 504 | +#endif |
393 | 505 | }
|
394 | 506 |
|
395 | 507 | #elif SAME5x
|
|
0 commit comments