From ac6439fec5c8be4a7872818fc4c4912c1707c5c3 Mon Sep 17 00:00:00 2001 From: James Smith Date: Mon, 5 Dec 2022 17:03:05 -0700 Subject: [PATCH 1/6] 29: Minor README corrections --- README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7c8710a..252bfbb 100644 --- a/README.md +++ b/README.md @@ -354,7 +354,7 @@ Below defines a location word which is used to address blocks of memory in some ### CRC -CRC byte transmits last, just before the end sequence is transmitted. It is the value after starting with 0 and applying XOR to each other byte in the packet. +CRC byte transmits last, just before the end sequence is transmitted. It is the value after starting with 0 and applying XOR against each byte in the packet. --- @@ -380,7 +380,7 @@ All button bits are 0 when pressed and 1 when released. ## Screen -The "block write" command (0x0C) with screen function code and 48 data words is used to write monochrome images to the screen. A screen is 48 bits wide and 32 bits tall. For each bit in the 48 data words, a value of 1 means the pixel is on (black) and 0 means the pixel is off (white). Data is written from left to right and top to bottom. The most significant bit of the first word sets the pixel on the top, left of the screen. The two most significant bytes write to the 33rd through 48th bit of the first row. The next two bytes write to the 1st through 16th bits of the second row. This is repeated for the right of the 48 words like pictured below. +The "block write" command (0x0C) with screen function code and 48 data words is used to write monochrome images to the screen. A screen is 48 bits wide and 32 bits tall. For each bit in the 48 data words, a value of 1 means the pixel is on (black) and 0 means the pixel is off (white). Data is written from left to right and top to bottom. The most significant bit of the first word sets the pixel on the top, left of the screen. The two most significant bytes write to the 33rd through 48th bit of the first row. The next two bytes write to the 1st through 16th bits of the second row. This is repeated for the rest of the 48 words like pictured below.

Screen Words @@ -432,7 +432,7 @@ Specifically, the text `Fm:4 - 30Hz`. This correlates to `(value + 1) / 2` and m - `0xX0`: single stable vibration (i.e. no inclination) at power X - `0xX8`: positive inclination (ramp up) from power X up to max - `0x8X`: negative inclination (ramp down) from power X down to min -- A values of `0x00`, `0x08`, or `0x80` immediately stops the currently executing vibration sequence +- A value of `0x00`, `0x08`, or `0x80` immediately stops the currently executing vibration sequence There is a very noticeable change from one vibration power to the next when inclination is used and a long cycle period is selected. @@ -468,8 +468,16 @@ Bit 0 may be set to 1 to augment duration, but the meaning is not completely und **Maple Bus Resources** +https://archive.org/details/MaplePatent/page/n7/mode/1up + http://mc.pp.se/dc/maplebus.html and http://mc.pp.se/dc/controller.html https://tech-en.netlify.app/articles/en540236/index.html https://www.raphnet.net/programmation/dreamcast_usb/index_en.php + +https://web.archive.org/web/20100425041817/http://www.maushammer.com/vmu.html + +https://hackaday.io/project/170365-blueretro/log/180790-evolution-of-segas-io-interface-from-sg-1000-to-saturn + +https://segaretro.org/History_of_the_Sega_Dreamcast/Development From 08f33b81511ce069e06ac550662aa8c0b611a4be Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 7 Dec 2022 19:30:46 -0700 Subject: [PATCH 2/6] 29: Very, very, very rough implementation of raw communication over cdc --- inc/configuration.h | 3 + inc/hal/System/LockGuard.hpp | 20 ++- inc/hal/Usb/usb_interface.hpp | 7 +- src/coreLib/DreamcastMainNode.cpp | 6 +- src/hal/Usb/Common/cdc.cpp | 69 ++++---- src/hal/Usb/Common/cdc.hpp | 3 +- src/hal/Usb/Hid/usb_descriptors.c | 4 +- src/hal/Usb/Hid/usb_descriptors.h | 8 +- src/hal/Usb/Hid/usb_execution.cpp | 8 +- src/hal/Usb/Xinput/CMakeLists.txt | 26 +++ src/hal/Usb/Xinput/tusb_config.h | 96 +++++++++++ src/hal/Usb/Xinput/usb_descriptors.c | 160 +++++++++++++++++++ src/hal/Usb/Xinput/usb_descriptors.h | 24 +++ src/hal/Usb/Xinput/usb_execution.cpp | 58 +++++++ src/hal/Usb/Xinput/xinput_device.cpp | 229 +++++++++++++++++++++++++++ src/hal/Usb/Xinput/xinput_device.hpp | 29 ++++ src/main/main.cpp | 155 +++++++++++++++++- 17 files changed, 852 insertions(+), 53 deletions(-) create mode 100644 src/hal/Usb/Xinput/CMakeLists.txt create mode 100644 src/hal/Usb/Xinput/tusb_config.h create mode 100644 src/hal/Usb/Xinput/usb_descriptors.c create mode 100644 src/hal/Usb/Xinput/usb_descriptors.h create mode 100644 src/hal/Usb/Xinput/usb_execution.cpp create mode 100644 src/hal/Usb/Xinput/xinput_device.cpp create mode 100644 src/hal/Usb/Xinput/xinput_device.hpp diff --git a/inc/configuration.h b/inc/configuration.h index 3d8fbb3..88269ad 100644 --- a/inc/configuration.h +++ b/inc/configuration.h @@ -5,6 +5,9 @@ // Warning: enabling debug messages drastically degrades communication performance #define SHOW_DEBUG_MESSAGES false +// true to enable USB CDC (serial) interface to directly control the maple bus +#define USB_CDC_ENABLED true + // Adjust the CPU clock frequency here (133 MHz is maximum documented stable frequency) #define CPU_FREQ_KHZ 133000 diff --git a/inc/hal/System/LockGuard.hpp b/inc/hal/System/LockGuard.hpp index 2282608..7522298 100644 --- a/inc/hal/System/LockGuard.hpp +++ b/inc/hal/System/LockGuard.hpp @@ -15,21 +15,29 @@ class LockGuard LockGuard() = delete; //! Initializes data and locks mutex as long as it wouldn't cause a deadlock - inline LockGuard(MutexInterface& mutex) : + inline LockGuard(MutexInterface& mutex, bool allowDeadlock = false) : mMutex(mutex), mIsLocked(false) { - int8_t lockValue = mMutex.tryLock(); - if (lockValue == 0) + if (allowDeadlock) { mMutex.lock(); mIsLocked = true; } - else if (lockValue > 0) + else { - mIsLocked = true; + int8_t lockValue = mMutex.tryLock(); + if (lockValue == 0) + { + mMutex.lock(); + mIsLocked = true; + } + else if (lockValue > 0) + { + mIsLocked = true; + } + // Else: not locked, would cause deadlock - due to simultaneous IRQ access on same core } - // Else: not locked, would cause deadlock } //! Unlocks mutex if it was previously locked diff --git a/inc/hal/Usb/usb_interface.hpp b/inc/hal/Usb/usb_interface.hpp index 857ee43..a26e854 100644 --- a/inc/hal/Usb/usb_interface.hpp +++ b/inc/hal/Usb/usb_interface.hpp @@ -4,11 +4,16 @@ #include "UsbFileSystem.hpp" #include "DreamcastControllerObserver.hpp" #include "hal/System/MutexInterface.hpp" +#include //! @returns array of the USB controller observers DreamcastControllerObserver** get_usb_controller_observers(); //! USB initialization -void usb_init(MutexInterface* mscMutex, MutexInterface* cdcMutex); +void usb_init( + MutexInterface* mscMutex, + MutexInterface* cdcStdioMutex, + MutexInterface* cdcRxMutex, + std::vector* cdcRx); //! USB task that needs to be called constantly by main() void usb_task(); //! @returns number of USB controllers diff --git a/src/coreLib/DreamcastMainNode.cpp b/src/coreLib/DreamcastMainNode.cpp index 92ace10..d71369f 100644 --- a/src/coreLib/DreamcastMainNode.cpp +++ b/src/coreLib/DreamcastMainNode.cpp @@ -4,9 +4,9 @@ #include "DreamcastController.hpp" #include "EndpointTxScheduler.hpp" -const uint8_t DreamcastMainNode::MAIN_TRANSMISSION_PRIORITY = 0; -const uint8_t DreamcastMainNode::SUB_TRANSMISSION_PRIORITY = 1; -const uint8_t DreamcastMainNode::MAX_PRIORITY = 1; +const uint8_t DreamcastMainNode::MAIN_TRANSMISSION_PRIORITY = 1; +const uint8_t DreamcastMainNode::SUB_TRANSMISSION_PRIORITY = 2; +const uint8_t DreamcastMainNode::MAX_PRIORITY = 2; DreamcastMainNode::DreamcastMainNode(MapleBusInterface& bus, PlayerData playerData, diff --git a/src/hal/Usb/Common/cdc.cpp b/src/hal/Usb/Common/cdc.cpp index 98f38d5..16600e1 100644 --- a/src/hal/Usb/Common/cdc.cpp +++ b/src/hal/Usb/Common/cdc.cpp @@ -18,9 +18,9 @@ #if CFG_TUD_CDC -static MutexInterface* cdcMutex = nullptr; - -#if SHOW_DEBUG_MESSAGES +static MutexInterface* stdioMutex = nullptr; +static MutexInterface* rxMutex; +static std::vector* rx; // Can't use stdio_usb_init() because it checks tud_cdc_connected(), and that doesn't always return // true when a connection is made. Not all terminal client set this when making connection. @@ -32,7 +32,7 @@ extern "C" { static void stdio_usb_out_chars2(const char *buf, int length) { static uint64_t last_avail_time; - LockGuard lockGuard(*cdcMutex); + LockGuard lockGuard(*stdioMutex); if (!lockGuard.isLocked()) { return; // would deadlock otherwise @@ -65,7 +65,7 @@ static void stdio_usb_out_chars2(const char *buf, int length) { int stdio_usb_in_chars2(char *buf, int length) { - LockGuard lockGuard(*cdcMutex); + LockGuard lockGuard(*stdioMutex); if (!lockGuard.isLocked()) { return PICO_ERROR_NO_DATA; // would deadlock otherwise @@ -92,39 +92,52 @@ struct stdio_driver stdio_usb2 = } // extern "C" -#endif // #if SHOW_DEBUG_MESSAGES - -void cdc_init(MutexInterface* mutex) +void cdc_init( + MutexInterface* cdcStdioMutex, + MutexInterface* cdcRxMutex, + std::vector* cdcRx) { - cdcMutex = mutex; -#if SHOW_DEBUG_MESSAGES + stdioMutex = cdcStdioMutex; + rxMutex = cdcRxMutex; + rx = cdcRx; stdio_set_driver_enabled(&stdio_usb2, true); -#endif } void cdc_task() { - // This interface is only currently used when SHOW_DEBUG_MESSAGES is true, so no need to do - // anything otherwise -#if CDC_ENABLED - // connected() check for DTR bit - // Most but not all terminal client set this when making connection - // if ( tud_cdc_connected() ) +#if USB_CDC_ENABLED + // connected and there are data available + if ( tud_cdc_available() ) { - // connected and there are data available - if ( tud_cdc_available() ) + char buf[64]; + uint32_t count = 0; + { - // TODO: This is where CDC data may be read - tud_cdc_read_flush(); + LockGuard lockGuard(*stdioMutex); + if (!lockGuard.isLocked()) + { + return; // would deadlock otherwise; just wait for next loop + } - // // read datas - // char buf[64]; - // uint32_t count = tud_cdc_read(buf, sizeof(buf)); - // (void) count; + // read data + count = tud_cdc_read(buf, sizeof(buf)); - // // Echo back - // tud_cdc_write(buf, count); - // tud_cdc_write_flush(); + if (count > 0) + { + // Echo back + tud_cdc_write(buf, count); + tud_cdc_write_flush(); + } + } + + if (count > 0) + { + LockGuard lockGuard(*stdioMutex, true); + rx->reserve(rx->size() + count); + for (uint32_t i = 0; i < count; ++i) + { + rx->push_back(buf[i]); + } } } #endif diff --git a/src/hal/Usb/Common/cdc.hpp b/src/hal/Usb/Common/cdc.hpp index 3da87ab..5eb65e1 100644 --- a/src/hal/Usb/Common/cdc.hpp +++ b/src/hal/Usb/Common/cdc.hpp @@ -1,8 +1,9 @@ #include "tusb_config.h" #include "hal/System/MutexInterface.hpp" #include "hal/System/LockGuard.hpp" +#include // CDC is used to create a debug serial interface -void cdc_init(MutexInterface* cdcMutex); +void cdc_init(MutexInterface* cdcStdioMutex, MutexInterface* cdcRxMutex, std::vector* rx); void cdc_task(); diff --git a/src/hal/Usb/Hid/usb_descriptors.c b/src/hal/Usb/Hid/usb_descriptors.c index 1c1ac67..9749b07 100644 --- a/src/hal/Usb/Hid/usb_descriptors.c +++ b/src/hal/Usb/Hid/usb_descriptors.c @@ -112,7 +112,7 @@ uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) // Configuration Descriptor //--------------------------------------------------------------------+ -#if CDC_ENABLED +#if USB_CDC_ENABLED #define DEBUG_CONFIG_LEN TUD_CDC_DESC_LEN #else #define DEBUG_CONFIG_LEN 0 @@ -171,7 +171,7 @@ uint8_t const desc_configuration[] = // * Communication Device Descriptor (for debug messaging) * // ************************************************************************* -#if CDC_ENABLED +#if USB_CDC_ENABLED // Interface number, string index, EP notification address and size, EP data address (out, in) and size. TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 9, EPIN_CDC_NOTIF, 8, EPOUT_CDC, EPIN_CDC, 64), #endif diff --git a/src/hal/Usb/Hid/usb_descriptors.h b/src/hal/Usb/Hid/usb_descriptors.h index d971c4d..9b194de 100644 --- a/src/hal/Usb/Hid/usb_descriptors.h +++ b/src/hal/Usb/Hid/usb_descriptors.h @@ -3,12 +3,6 @@ #include "configuration.h" -#if SHOW_DEBUG_MESSAGES -#define CDC_ENABLED true -#else -#define CDC_ENABLED false -#endif - // Going in reverse order because the host seems to usually enumerate the highest value first enum { // Gamepads @@ -19,7 +13,7 @@ enum { // For mass storage device ITF_NUM_MSC, -#if CDC_ENABLED +#if USB_CDC_ENABLED ITF_NUM_CDC, ITF_NUM_CDC_DATA, #endif diff --git a/src/hal/Usb/Hid/usb_execution.cpp b/src/hal/Usb/Hid/usb_execution.cpp index 2ad2d65..a824e22 100644 --- a/src/hal/Usb/Hid/usb_execution.cpp +++ b/src/hal/Usb/Hid/usb_execution.cpp @@ -125,13 +125,17 @@ void led_task() } -void usb_init(MutexInterface* mscMutex, MutexInterface* cdcMutex) +void usb_init( + MutexInterface* mscMutex, + MutexInterface* cdcStdioMutex, + MutexInterface* cdcRxMutex, + std::vector* cdcRx) { set_usb_devices(devices, sizeof(devices) / sizeof(devices[1])); board_init(); tusb_init(); msc_init(mscMutex); - cdc_init(cdcMutex); + cdc_init(cdcStdioMutex, cdcRxMutex, cdcRx); #if USB_LED_PIN >= 0 gpio_init(USB_LED_PIN); diff --git a/src/hal/Usb/Xinput/CMakeLists.txt b/src/hal/Usb/Xinput/CMakeLists.txt new file mode 100644 index 0000000..c1ef8b5 --- /dev/null +++ b/src/hal/Usb/Xinput/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.12) + +set(CMAKE_VERBOSE_MAKEFILE ON) + +file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c*") + +add_library(hal-Usb-Xinput STATIC ${SRC}) +target_link_libraries(hal-Usb-Xinput + PUBLIC + pico_stdlib + pico_unique_id + tinyusb_device + tinyusb_board + tinyusb_device_base +) +target_compile_options(hal-Usb-Xinput PRIVATE + -Wall + -Werror + -O3 +) + +target_include_directories(hal-Usb-Xinput + PUBLIC + "$" + "$" + "${PROJECT_SOURCE_DIR}/inc") diff --git a/src/hal/Usb/Xinput/tusb_config.h b/src/hal/Usb/Xinput/tusb_config.h new file mode 100644 index 0000000..3ef6cbc --- /dev/null +++ b/src/hal/Usb/Xinput/tusb_config.h @@ -0,0 +1,96 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#include "usb_descriptors.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ + CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#else +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_PICO +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +#define CFG_TUD_XINPUT_EP_BUFSIZE 64 + +//------------- CLASS -------------// +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 +#define CFG_TUD_XINPUT 1 + + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/src/hal/Usb/Xinput/usb_descriptors.c b/src/hal/Usb/Xinput/usb_descriptors.c new file mode 100644 index 0000000..66d2a0c --- /dev/null +++ b/src/hal/Usb/Xinput/usb_descriptors.c @@ -0,0 +1,160 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + * + */ + +#include "tusb.h" +#include "usb_descriptors.h" +#include "class/hid/hid_device.h" +#include "pico/unique_id.h" +#include + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + // Faking out Microsoft device + .idVendor = 0x045E, + .idProduct = 0x028E, + .bcdDevice = 0x0572, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const *tud_descriptor_device_cb(void) { + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + (NUMBER_OF_GAMEPADS * TUD_XINPUT_DESC_LEN)) + +#define EPNUM_XINPUT1 (ITF_NUM_XINPUT1 + 1) + +uint8_t const desc_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, NUMBER_OF_GAMEPADS, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 400), + + // Interface number, string index, EP In address, EP Out address + TUD_XINPUT_DESCRIPTOR(ITF_NUM_XINPUT1, 4, 0x80 | EPNUM_XINPUT1, EPNUM_XINPUT1) +}; + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { + (void) index; // for multiple configurations + return desc_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const *string_desc_arr[] = +{ + (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409) + "GENERIC" , // 1: Manufacturer + "XINPUT CONTROLLER", // 2: Product + NULL, // 3: Serial (special case; get pico serial) + "P1", // 4: Device 1 + "P2", // 5: Device 2 + "P3", // 6: Device 3 + "P4" // 7: Device 4 +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + (void) langid; + + uint8_t chr_count; + char buffer[32] = {0}; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + } else { + // Convert ASCII string into UTF-16 + + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL; + + const char *str = string_desc_arr[index]; + + if (str == NULL) + { + if (index == 3) + { + // Special case: try to get pico serial number + pico_get_unique_board_id_string(buffer, sizeof(buffer)); + if (buffer[0] != '\0') + { + str = buffer; + } + else + { + // Something failed, have host assign serial + return NULL; + } + } + else + { + return NULL; + } + } + + // Cap at max char + chr_count = strlen(str); + if (chr_count > 31) chr_count = 31; + + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); + + return _desc_str; +} diff --git a/src/hal/Usb/Xinput/usb_descriptors.h b/src/hal/Usb/Xinput/usb_descriptors.h new file mode 100644 index 0000000..d59580d --- /dev/null +++ b/src/hal/Usb/Xinput/usb_descriptors.h @@ -0,0 +1,24 @@ +#ifndef __USB_DESCRIPTORS_H__ +#define __USB_DESCRIPTORS_H__ + +// Going in reverse order because the host seems to usually enumerate the highest value first +enum { + ITF_NUM_XINPUT1 = 0 +}; + +#define NUMBER_OF_GAMEPADS 1 + +#define TUD_XINPUT_DESC_LEN (9 + 17 + 7 + 7) +#define TUD_XINPUT_NUM_ENDPOINTS 2 + +#define TUD_XINPUT_DESCRIPTOR(_itfnum, _stridx, _epin, _epout) \ + /* Interface */\ + 9, TUSB_DESC_INTERFACE, _itfnum, 0, TUD_XINPUT_NUM_ENDPOINTS, TUSB_CLASS_VENDOR_SPECIFIC, 0x5D, 0x01, _stridx,\ + /* Unknown */ \ + 17, TUSB_DESC_FUNCTIONAL, 0x00, 0x01, 0x01, 0x25, _epin, 0x14, 0x00, 0x00, 0x00, 0x00, 0x13, _epout, 0x08, 0x00, 0x00, \ + /* Button device output */ \ + 7, TUSB_DESC_ENDPOINT, _epin, 0x03, 0x20, 0x00, 0x04, \ + /* Force feedback and LED device input */ \ + 7, TUSB_DESC_ENDPOINT, _epout, 0x03, 0x20, 0x00, 0x08 + +#endif // __USB_DESCRIPTORS_H__ diff --git a/src/hal/Usb/Xinput/usb_execution.cpp b/src/hal/Usb/Xinput/usb_execution.cpp new file mode 100644 index 0000000..849f659 --- /dev/null +++ b/src/hal/Usb/Xinput/usb_execution.cpp @@ -0,0 +1,58 @@ +#include "hal/Usb/DreamcastControllerObserver.hpp" + +#include "configuration.h" +#include +#include +#include + +#include "bsp/board.h" +#include "pico/stdlib.h" +#include "tusb.h" +#include "device/dcd.h" +#include "usb_descriptors.h" +#include "class/hid/hid_device.h" +#include "xinput_device.hpp" + +static void sendReportData(void) +{ + + // Poll every 1ms + const uint32_t interval_ms = 1; + static uint32_t start_ms = 0; + + if (board_millis() - start_ms < interval_ms) return; // not enough time + start_ms += interval_ms; + + // Remote wakeup + if (tud_suspended()) { + // Wake up host if we are in suspend mode + // and REMOTE_WAKEUP feature is enabled by host + tud_remote_wakeup(); + } + if (tud_xinput_n_ready(0)) + { + tud_xinput_n_gamepad_report(0, 1, 0, 0, 0, 0, 0, 0, 0); + } +} + +DreamcastControllerObserver** get_usb_controller_observers() +{ + return NULL; +} + +void usb_init() +{ + board_init(); + tusb_init(); +} + +void usb_task() +{ + sendReportData(); + tud_task(); // tinyusb device task +} + +uint32_t get_num_usb_controllers() +{ + return 1; +} diff --git a/src/hal/Usb/Xinput/xinput_device.cpp b/src/hal/Usb/Xinput/xinput_device.cpp new file mode 100644 index 0000000..048feac --- /dev/null +++ b/src/hal/Usb/Xinput/xinput_device.cpp @@ -0,0 +1,229 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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. + */ + +#include "tusb_option.h" +#include "tusb_config.h" + +#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XINPUT) + +//--------------------------------------------------------------------+ +// INCLUDE +//--------------------------------------------------------------------+ +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +#include "xinput_device.hpp" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +typedef struct +{ + uint8_t itf_num; + uint8_t ep_in; + uint8_t ep_out; // optional Out endpoint + + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_XINPUT_EP_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_XINPUT_EP_BUFSIZE]; +} xinput_interface_t; + +CFG_TUSB_MEM_SECTION static xinput_interface_t _xinput_itf[CFG_TUD_XINPUT]; + +/*------------- Helpers -------------*/ +static inline uint8_t get_index_by_itfnum(uint8_t itf_num) +{ + for (uint8_t i=0; i < CFG_TUD_XINPUT; i++ ) + { + if ( itf_num == _xinput_itf[i].itf_num ) return i; + } + + return 0xFF; +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +bool tud_xinput_n_ready(uint8_t instance) +{ + uint8_t const ep_in = _xinput_itf[instance].ep_in; + return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(TUD_OPT_RHPORT, ep_in); +} + +bool tud_xinput_n_report(uint8_t instance, uint8_t report_id, void const* report, uint8_t len) +{ + uint8_t const rhport = 0; + xinput_interface_t * p_xinput = &_xinput_itf[instance]; + + // claim endpoint + TU_VERIFY( usbd_edpt_claim(rhport, p_xinput->ep_in) ); + + // prepare data + if (report_id) + { + len = tu_min8(len, CFG_TUD_XINPUT_EP_BUFSIZE-1); + + p_xinput->epin_buf[0] = report_id; + p_xinput->epin_buf[1] = len; + memcpy(p_xinput->epin_buf+2, report, len); + len++; + }else + { + // If report id = 0, skip ID and len fields + len = tu_min8(len, CFG_TUD_XINPUT_EP_BUFSIZE); + memcpy(p_xinput->epin_buf, report, len); + } + + return usbd_edpt_xfer(TUD_OPT_RHPORT, p_xinput->ep_in, p_xinput->epin_buf, len); +} + +bool tud_xinput_n_gamepad_report(uint8_t instance, uint8_t report_id, + uint16_t buttons, uint8_t lt, uint8_t rt, int lx, int ly, int rx, int ry) +{ + xinput_gamepad_report_t report = + { + .buttons = buttons, + .lt = lt, + .rt = rt, + .lx = lx, + .ly = ly, + .rx = rx, + .ry = ry + }; + + return tud_xinput_n_report(instance, report_id, &report, sizeof(report)); +} + +//--------------------------------------------------------------------+ +// USBD-CLASS API +//--------------------------------------------------------------------+ +void xinput_reset(uint8_t rhport) +{ + (void) rhport; + tu_memclr(_xinput_itf, sizeof(_xinput_itf)); +} + +void xinput_init(void) +{ + xinput_reset(TUD_OPT_RHPORT); +} + +uint16_t xinput_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len) +{ + //+16 is for the unknown descriptor + uint16_t const drv_len = sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t) + 17; + TU_VERIFY(max_len >= drv_len, 0); + + // Find available interface + xinput_interface_t * p_xinput = NULL; + uint8_t hid_id; + for(hid_id=0; hid_idbNumEndpoints, TUSB_XFER_BULK, &p_xinput->ep_out, &p_xinput->ep_in), 0); + + // Prepare for output endpoint + if (p_xinput->ep_out) + { + if ( !usbd_edpt_xfer(rhport, p_xinput->ep_out, p_xinput->epout_buf, sizeof(p_xinput->epout_buf)) ) + { + TU_LOG_FAILED(); + TU_BREAKPOINT(); + } + } + + return drv_len; +} + +// Invoked when a control transfer occurred on an interface of this class +// Driver response accordingly to the request and the transfer stage (setup/data/ack) +// return false to stall control endpoint (e.g unsupported request) +bool xinput_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + return true; +} + +bool xinput_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) result; + + uint8_t instance = 0; + xinput_interface_t * p_xinput = _xinput_itf; + + // Identify which interface to use + for (instance = 0; instance < CFG_TUD_XINPUT; instance++) + { + p_xinput = &_xinput_itf[instance]; + if ( (ep_addr == p_xinput->ep_out) || (ep_addr == p_xinput->ep_in) ) break; + } + TU_ASSERT(instance < CFG_TUD_XINPUT); + + // Sent report successfully + if (ep_addr == p_xinput->ep_in) + { + // TODO: add hook + } + // Received report + else if (ep_addr == p_xinput->ep_out) + { + // TODO + //tud_xinput_set_report_cb(instance, p_xinput->epout_buf, xferred_bytes); + TU_ASSERT(usbd_edpt_xfer(rhport, p_xinput->ep_out, p_xinput->epout_buf, sizeof(p_xinput->epout_buf))); + } + + return true; +} + +static usbd_class_driver_t const xinput_driver = +{ + #if CFG_TUSB_DEBUG >= 2 + .name = "XINPUT", +#endif + .init = xinput_init, + .reset = xinput_reset, + .open = xinput_open, + .control_xfer_cb = xinput_control_xfer_cb, + .xfer_cb = xinput_xfer_cb, + .sof = NULL +}; + +usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) +{ + *driver_count = 1; + return &xinput_driver; +} + +#endif diff --git a/src/hal/Usb/Xinput/xinput_device.hpp b/src/hal/Usb/Xinput/xinput_device.hpp new file mode 100644 index 0000000..40b463c --- /dev/null +++ b/src/hal/Usb/Xinput/xinput_device.hpp @@ -0,0 +1,29 @@ +#ifndef __XINPUT_DEVICE_H__ +#define __XINPUT_DEVICE_H__ + +#include + +#include "common/tusb_common.h" + +typedef struct TU_ATTR_PACKED +{ + uint16_t buttons; + uint8_t lt; + uint8_t rt; + int lx; + int ly; + int rx; + int ry; +} xinput_gamepad_report_t; + +bool tud_xinput_n_ready(uint8_t instance); + +bool tud_xinput_n_report(uint8_t instance, uint8_t report_id, void const* report, uint8_t len); + +bool tud_xinput_n_gamepad_report(uint8_t instance, uint8_t report_id, + uint16_t buttons, uint8_t lt, uint8_t rt, int lx, int ly, int rx, int ry); + +void tud_xinput_set_report_cb(uint8_t instance, const uint8_t* buf, uint32_t xferred_bytes); + + +#endif // __XINPUT_DEVICE_H__ diff --git a/src/main/main.cpp b/src/main/main.cpp index b287dbd..fccd703 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -11,6 +11,7 @@ #include "Mutex.hpp" #include "Clock.hpp" +#include "hal/System/LockGuard.hpp" #include "hal/MapleBus/MapleBusInterface.hpp" #include "hal/Usb/usb_interface.hpp" @@ -21,6 +22,43 @@ const uint8_t MAPLE_HOST_ADDRESSES[MAX_DEVICES] = {0x00, 0x40, 0x80, 0xC0}; +Mutex cdcRxMutex; +std::vector cdcRx; + +class EchoTransmitter : public Transmitter +{ + virtual void txStarted(std::shared_ptr tx) final + {} + + virtual void txFailed(bool writeFailed, + bool readFailed, + std::shared_ptr tx) final + { + if (writeFailed) + { + printf("%lu: failed write\n", tx->transmissionId); + } + else + { + printf("%lu: failed read\n", tx->transmissionId); + } + } + + virtual void txComplete(std::shared_ptr packet, + std::shared_ptr tx) final + { + printf("%lu: complete {", tx->transmissionId); + printf("%08lX", packet->frameWord); + for (std::vector::const_iterator iter = packet->payload.begin(); + iter != packet->payload.end(); + ++iter) + { + printf(" %08lX", *iter); + } + printf("}\n"); + } +} echoTransmitter; + // Second Core Process // The second core is in charge of handling communication with Dreamcast peripherals void core1() @@ -42,6 +80,7 @@ void core1() DreamcastControllerObserver** observers = get_usb_controller_observers(); std::shared_ptr buses[numDevices]; std::shared_ptr dreamcastMainNodes[numDevices]; + std::shared_ptr schedulers[numDevices]; Clock clock; for (uint32_t i = 0; i < numDevices; ++i) { @@ -52,12 +91,14 @@ void core1() clock, usb_msc_get_file_system()); buses[i] = create_maple_bus(maplePins[i], MAPLE_HOST_ADDRESSES[i]); + schedulers[i] = std::make_shared(DreamcastMainNode::MAX_PRIORITY); dreamcastMainNodes[i] = std::make_shared( *buses[i], *playerData[i], - std::make_shared(DreamcastMainNode::MAX_PRIORITY)); + schedulers[i]); } + uint32_t lastSize = 0; while(true) { for (uint32_t i = 0; i < numDevices; ++i) @@ -65,6 +106,114 @@ void core1() // Worst execution duration of below is ~350 us at 133 MHz when debug print is disabled dreamcastMainNodes[i]->task(time_us_64()); } + +#if 1 + { + LockGuard lockGuard(cdcRxMutex, true); + + if (lastSize != cdcRx.size()) + { + std::vector::iterator eol = std::find(cdcRx.begin(), cdcRx.end(), '\n'); + if (eol != cdcRx.end()) + { + bool valid = false; + std::vector words; + std::vector::iterator iter = cdcRx.begin(); + while(iter != eol) + { + uint32_t word = 0; + uint32_t i = 0; + while (i < 8 && iter != eol) + { + char v = *iter; + if (v >= '0' && v <= '9') + { + word |= ((v - '0') << ((8 - i) * 4 - 4)); + ++i; + } + else if (v >= 'a' && v <= 'f') + { + word |= ((v - 'a' + 10) << ((8 - i) * 4 - 4)); + ++i; + } + else if (v >= 'A' && v <= 'F') + { + word |= ((v - 'A' + 10) << ((8 - i) * 4 - 4)); + ++i; + } + ++iter; + } + valid = ((i == 8) || (i == 0)); + + if (i == 8) + { + words.push_back(word); + } + } + + if (valid) + { + MaplePacket packet(&words[0], words.size()); + if (packet.isValid()) + { + uint8_t sender = packet.getFrameSenderAddr(); + int32_t idx = -1; + if (sender == 0 && numDevices > 0) + { + idx = 0; + } + else if (sender == 0x40 && numDevices > 1) + { + idx = 1; + } + else if (sender == 0x80 && numDevices > 2) + { + idx = 2; + } + else if (sender == 0xC0 && numDevices > 3) + { + idx = 3; + } + + if (idx >= 0) + { + uint32_t id = schedulers[idx]->add( + 0, + PrioritizedTxScheduler::TX_TIME_ASAP, + &echoTransmitter, + packet, + true); + std::vector::iterator iter = words.begin(); + printf("%lu: added {%08lX", id, *iter++); + for(; iter < words.end(); ++iter) + { + printf(" %08lX", *iter); + } + printf("}\n"); + } + else + { + printf("0: failed invalid sender\n"); + } + } + else + { + printf("0: failed packet invalid\n"); + } + } + else + { + printf("0: failed missing data\n"); + } + + cdcRx.erase(cdcRx.begin(), eol + 1); + } + + + lastSize = cdcRx.size(); + } + } +#endif } } @@ -81,8 +230,8 @@ int main() multicore_launch_core1(core1); Mutex fileMutex; - Mutex cdcMutex; - usb_init(&fileMutex, &cdcMutex); + Mutex cdcStdioMutex; + usb_init(&fileMutex, &cdcStdioMutex, &cdcRxMutex, &cdcRx); while(true) { From 08da213fabb78d97454d0dd63c7749a3b2cc302f Mon Sep 17 00:00:00 2001 From: James Smith Date: Wed, 7 Dec 2022 20:43:47 -0700 Subject: [PATCH 3/6] 29: Corrections and necessary assertions --- src/coreLib/PrioritizedTxScheduler.cpp | 6 ++++++ src/coreLib/PrioritizedTxScheduler.hpp | 2 ++ src/test/mocks/MockMutex.hpp | 2 ++ 3 files changed, 10 insertions(+) diff --git a/src/coreLib/PrioritizedTxScheduler.cpp b/src/coreLib/PrioritizedTxScheduler.cpp index f671eab..0f451e9 100644 --- a/src/coreLib/PrioritizedTxScheduler.cpp +++ b/src/coreLib/PrioritizedTxScheduler.cpp @@ -2,6 +2,8 @@ #include "configuration.h" #include "utils.h" +#include + // STL #include @@ -16,6 +18,7 @@ PrioritizedTxScheduler::~PrioritizedTxScheduler() {} uint32_t PrioritizedTxScheduler::add(std::shared_ptr tx) { + assert(tx->priority < mSchedule.size()); std::list>& schedule = mSchedule[tx->priority]; // Keep iterating until correct position is found std::list>::const_iterator iter = schedule.cbegin(); @@ -46,6 +49,9 @@ uint32_t PrioritizedTxScheduler::add(uint8_t priority, uint32_t pktDurationUs = INT_DIVIDE_CEILING(pktDurationNs, 1000); + // This will happen if minimal communication is made constantly for 20 days + assert(mNextId != INVALID_TX_ID); + std::shared_ptr tx = std::make_shared(mNextId++, priority, diff --git a/src/coreLib/PrioritizedTxScheduler.hpp b/src/coreLib/PrioritizedTxScheduler.hpp index f1ccd6e..331c097 100644 --- a/src/coreLib/PrioritizedTxScheduler.hpp +++ b/src/coreLib/PrioritizedTxScheduler.hpp @@ -78,6 +78,8 @@ class PrioritizedTxScheduler public: //! Use this for txTime if the packet needs to be sent ASAP static const uint64_t TX_TIME_ASAP = 0; + //! Transmission ID to use in order to flag no ID + static const uint32_t INVALID_TX_ID = 0; protected: //! The next transmission ID to set diff --git a/src/test/mocks/MockMutex.hpp b/src/test/mocks/MockMutex.hpp index 1a303d3..7ebb01b 100644 --- a/src/test/mocks/MockMutex.hpp +++ b/src/test/mocks/MockMutex.hpp @@ -11,4 +11,6 @@ class MockMutex : public MutexInterface MOCK_METHOD(void, lock, (), (override)); MOCK_METHOD(void, unlock, (), (override)); + + MOCK_METHOD(int8_t, tryLock, (), (override)); }; From f89d28c99e25ef091c7a87f6cb5eab82bc4e3683 Mon Sep 17 00:00:00 2001 From: James Smith Date: Thu, 8 Dec 2022 16:11:55 -0700 Subject: [PATCH 4/6] 29: Didn't mean to commit xinput code here --- src/hal/Usb/Xinput/CMakeLists.txt | 26 --- src/hal/Usb/Xinput/tusb_config.h | 96 ----------- src/hal/Usb/Xinput/usb_descriptors.c | 160 ------------------- src/hal/Usb/Xinput/usb_descriptors.h | 24 --- src/hal/Usb/Xinput/usb_execution.cpp | 58 ------- src/hal/Usb/Xinput/xinput_device.cpp | 229 --------------------------- src/hal/Usb/Xinput/xinput_device.hpp | 29 ---- 7 files changed, 622 deletions(-) delete mode 100644 src/hal/Usb/Xinput/CMakeLists.txt delete mode 100644 src/hal/Usb/Xinput/tusb_config.h delete mode 100644 src/hal/Usb/Xinput/usb_descriptors.c delete mode 100644 src/hal/Usb/Xinput/usb_descriptors.h delete mode 100644 src/hal/Usb/Xinput/usb_execution.cpp delete mode 100644 src/hal/Usb/Xinput/xinput_device.cpp delete mode 100644 src/hal/Usb/Xinput/xinput_device.hpp diff --git a/src/hal/Usb/Xinput/CMakeLists.txt b/src/hal/Usb/Xinput/CMakeLists.txt deleted file mode 100644 index c1ef8b5..0000000 --- a/src/hal/Usb/Xinput/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required(VERSION 3.12) - -set(CMAKE_VERBOSE_MAKEFILE ON) - -file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c*") - -add_library(hal-Usb-Xinput STATIC ${SRC}) -target_link_libraries(hal-Usb-Xinput - PUBLIC - pico_stdlib - pico_unique_id - tinyusb_device - tinyusb_board - tinyusb_device_base -) -target_compile_options(hal-Usb-Xinput PRIVATE - -Wall - -Werror - -O3 -) - -target_include_directories(hal-Usb-Xinput - PUBLIC - "$" - "$" - "${PROJECT_SOURCE_DIR}/inc") diff --git a/src/hal/Usb/Xinput/tusb_config.h b/src/hal/Usb/Xinput/tusb_config.h deleted file mode 100644 index 3ef6cbc..0000000 --- a/src/hal/Usb/Xinput/tusb_config.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * 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. - * - */ - -#ifndef _TUSB_CONFIG_H_ -#define _TUSB_CONFIG_H_ - -#include "usb_descriptors.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//-------------------------------------------------------------------- -// COMMON CONFIGURATION -//-------------------------------------------------------------------- - -// defined by compiler flags for flexibility -#ifndef CFG_TUSB_MCU -#error CFG_TUSB_MCU must be defined -#endif - -#if CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ - CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) -#else -#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE -#endif - -#ifndef CFG_TUSB_OS -#define CFG_TUSB_OS OPT_OS_PICO -#endif - -// CFG_TUSB_DEBUG is defined by compiler in DEBUG build -// #define CFG_TUSB_DEBUG 0 - -/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. - * Tinyusb use follows macros to declare transferring memory so that they can be put - * into those specific section. - * e.g - * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) - * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) - */ -#ifndef CFG_TUSB_MEM_SECTION -#define CFG_TUSB_MEM_SECTION -#endif - -#ifndef CFG_TUSB_MEM_ALIGN -#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) -#endif - -//-------------------------------------------------------------------- -// DEVICE CONFIGURATION -//-------------------------------------------------------------------- - -#ifndef CFG_TUD_ENDPOINT0_SIZE -#define CFG_TUD_ENDPOINT0_SIZE 64 -#endif - -#define CFG_TUD_XINPUT_EP_BUFSIZE 64 - -//------------- CLASS -------------// -#define CFG_TUD_CDC 0 -#define CFG_TUD_MSC 0 -#define CFG_TUD_HID 0 -#define CFG_TUD_MIDI 0 -#define CFG_TUD_VENDOR 0 -#define CFG_TUD_XINPUT 1 - - -#ifdef __cplusplus -} -#endif - -#endif /* _TUSB_CONFIG_H_ */ diff --git a/src/hal/Usb/Xinput/usb_descriptors.c b/src/hal/Usb/Xinput/usb_descriptors.c deleted file mode 100644 index 66d2a0c..0000000 --- a/src/hal/Usb/Xinput/usb_descriptors.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * 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. - * - */ - -#include "tusb.h" -#include "usb_descriptors.h" -#include "class/hid/hid_device.h" -#include "pico/unique_id.h" -#include - -//--------------------------------------------------------------------+ -// Device Descriptors -//--------------------------------------------------------------------+ -tusb_desc_device_t const desc_device = -{ - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = 0x00, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - - // Faking out Microsoft device - .idVendor = 0x045E, - .idProduct = 0x028E, - .bcdDevice = 0x0572, - - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - - .bNumConfigurations = 0x01 -}; - -// Invoked when received GET DEVICE DESCRIPTOR -// Application return pointer to descriptor -uint8_t const *tud_descriptor_device_cb(void) { - return (uint8_t const *) &desc_device; -} - -//--------------------------------------------------------------------+ -// Configuration Descriptor -//--------------------------------------------------------------------+ - -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + (NUMBER_OF_GAMEPADS * TUD_XINPUT_DESC_LEN)) - -#define EPNUM_XINPUT1 (ITF_NUM_XINPUT1 + 1) - -uint8_t const desc_configuration[] = -{ - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, NUMBER_OF_GAMEPADS, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 400), - - // Interface number, string index, EP In address, EP Out address - TUD_XINPUT_DESCRIPTOR(ITF_NUM_XINPUT1, 4, 0x80 | EPNUM_XINPUT1, EPNUM_XINPUT1) -}; - -// Invoked when received GET CONFIGURATION DESCRIPTOR -// Application return pointer to descriptor -// Descriptor contents must exist long enough for transfer to complete -uint8_t const *tud_descriptor_configuration_cb(uint8_t index) { - (void) index; // for multiple configurations - return desc_configuration; -} - -//--------------------------------------------------------------------+ -// String Descriptors -//--------------------------------------------------------------------+ - -// array of pointer to string descriptors -char const *string_desc_arr[] = -{ - (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409) - "GENERIC" , // 1: Manufacturer - "XINPUT CONTROLLER", // 2: Product - NULL, // 3: Serial (special case; get pico serial) - "P1", // 4: Device 1 - "P2", // 5: Device 2 - "P3", // 6: Device 3 - "P4" // 7: Device 4 -}; - -static uint16_t _desc_str[32]; - -// Invoked when received GET STRING DESCRIPTOR request -// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete -uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - (void) langid; - - uint8_t chr_count; - char buffer[32] = {0}; - - if (index == 0) { - memcpy(&_desc_str[1], string_desc_arr[0], 2); - chr_count = 1; - } else { - // Convert ASCII string into UTF-16 - - if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL; - - const char *str = string_desc_arr[index]; - - if (str == NULL) - { - if (index == 3) - { - // Special case: try to get pico serial number - pico_get_unique_board_id_string(buffer, sizeof(buffer)); - if (buffer[0] != '\0') - { - str = buffer; - } - else - { - // Something failed, have host assign serial - return NULL; - } - } - else - { - return NULL; - } - } - - // Cap at max char - chr_count = strlen(str); - if (chr_count > 31) chr_count = 31; - - for (uint8_t i = 0; i < chr_count; i++) { - _desc_str[1 + i] = str[i]; - } - } - - // first byte is length (including header), second byte is string type - _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); - - return _desc_str; -} diff --git a/src/hal/Usb/Xinput/usb_descriptors.h b/src/hal/Usb/Xinput/usb_descriptors.h deleted file mode 100644 index d59580d..0000000 --- a/src/hal/Usb/Xinput/usb_descriptors.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef __USB_DESCRIPTORS_H__ -#define __USB_DESCRIPTORS_H__ - -// Going in reverse order because the host seems to usually enumerate the highest value first -enum { - ITF_NUM_XINPUT1 = 0 -}; - -#define NUMBER_OF_GAMEPADS 1 - -#define TUD_XINPUT_DESC_LEN (9 + 17 + 7 + 7) -#define TUD_XINPUT_NUM_ENDPOINTS 2 - -#define TUD_XINPUT_DESCRIPTOR(_itfnum, _stridx, _epin, _epout) \ - /* Interface */\ - 9, TUSB_DESC_INTERFACE, _itfnum, 0, TUD_XINPUT_NUM_ENDPOINTS, TUSB_CLASS_VENDOR_SPECIFIC, 0x5D, 0x01, _stridx,\ - /* Unknown */ \ - 17, TUSB_DESC_FUNCTIONAL, 0x00, 0x01, 0x01, 0x25, _epin, 0x14, 0x00, 0x00, 0x00, 0x00, 0x13, _epout, 0x08, 0x00, 0x00, \ - /* Button device output */ \ - 7, TUSB_DESC_ENDPOINT, _epin, 0x03, 0x20, 0x00, 0x04, \ - /* Force feedback and LED device input */ \ - 7, TUSB_DESC_ENDPOINT, _epout, 0x03, 0x20, 0x00, 0x08 - -#endif // __USB_DESCRIPTORS_H__ diff --git a/src/hal/Usb/Xinput/usb_execution.cpp b/src/hal/Usb/Xinput/usb_execution.cpp deleted file mode 100644 index 849f659..0000000 --- a/src/hal/Usb/Xinput/usb_execution.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "hal/Usb/DreamcastControllerObserver.hpp" - -#include "configuration.h" -#include -#include -#include - -#include "bsp/board.h" -#include "pico/stdlib.h" -#include "tusb.h" -#include "device/dcd.h" -#include "usb_descriptors.h" -#include "class/hid/hid_device.h" -#include "xinput_device.hpp" - -static void sendReportData(void) -{ - - // Poll every 1ms - const uint32_t interval_ms = 1; - static uint32_t start_ms = 0; - - if (board_millis() - start_ms < interval_ms) return; // not enough time - start_ms += interval_ms; - - // Remote wakeup - if (tud_suspended()) { - // Wake up host if we are in suspend mode - // and REMOTE_WAKEUP feature is enabled by host - tud_remote_wakeup(); - } - if (tud_xinput_n_ready(0)) - { - tud_xinput_n_gamepad_report(0, 1, 0, 0, 0, 0, 0, 0, 0); - } -} - -DreamcastControllerObserver** get_usb_controller_observers() -{ - return NULL; -} - -void usb_init() -{ - board_init(); - tusb_init(); -} - -void usb_task() -{ - sendReportData(); - tud_task(); // tinyusb device task -} - -uint32_t get_num_usb_controllers() -{ - return 1; -} diff --git a/src/hal/Usb/Xinput/xinput_device.cpp b/src/hal/Usb/Xinput/xinput_device.cpp deleted file mode 100644 index 048feac..0000000 --- a/src/hal/Usb/Xinput/xinput_device.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * - * 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. - */ - -#include "tusb_option.h" -#include "tusb_config.h" - -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_XINPUT) - -//--------------------------------------------------------------------+ -// INCLUDE -//--------------------------------------------------------------------+ -#include "device/usbd.h" -#include "device/usbd_pvt.h" - -#include "xinput_device.hpp" - -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF -//--------------------------------------------------------------------+ -typedef struct -{ - uint8_t itf_num; - uint8_t ep_in; - uint8_t ep_out; // optional Out endpoint - - CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_XINPUT_EP_BUFSIZE]; - CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_XINPUT_EP_BUFSIZE]; -} xinput_interface_t; - -CFG_TUSB_MEM_SECTION static xinput_interface_t _xinput_itf[CFG_TUD_XINPUT]; - -/*------------- Helpers -------------*/ -static inline uint8_t get_index_by_itfnum(uint8_t itf_num) -{ - for (uint8_t i=0; i < CFG_TUD_XINPUT; i++ ) - { - if ( itf_num == _xinput_itf[i].itf_num ) return i; - } - - return 0xFF; -} - -//--------------------------------------------------------------------+ -// APPLICATION API -//--------------------------------------------------------------------+ -bool tud_xinput_n_ready(uint8_t instance) -{ - uint8_t const ep_in = _xinput_itf[instance].ep_in; - return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(TUD_OPT_RHPORT, ep_in); -} - -bool tud_xinput_n_report(uint8_t instance, uint8_t report_id, void const* report, uint8_t len) -{ - uint8_t const rhport = 0; - xinput_interface_t * p_xinput = &_xinput_itf[instance]; - - // claim endpoint - TU_VERIFY( usbd_edpt_claim(rhport, p_xinput->ep_in) ); - - // prepare data - if (report_id) - { - len = tu_min8(len, CFG_TUD_XINPUT_EP_BUFSIZE-1); - - p_xinput->epin_buf[0] = report_id; - p_xinput->epin_buf[1] = len; - memcpy(p_xinput->epin_buf+2, report, len); - len++; - }else - { - // If report id = 0, skip ID and len fields - len = tu_min8(len, CFG_TUD_XINPUT_EP_BUFSIZE); - memcpy(p_xinput->epin_buf, report, len); - } - - return usbd_edpt_xfer(TUD_OPT_RHPORT, p_xinput->ep_in, p_xinput->epin_buf, len); -} - -bool tud_xinput_n_gamepad_report(uint8_t instance, uint8_t report_id, - uint16_t buttons, uint8_t lt, uint8_t rt, int lx, int ly, int rx, int ry) -{ - xinput_gamepad_report_t report = - { - .buttons = buttons, - .lt = lt, - .rt = rt, - .lx = lx, - .ly = ly, - .rx = rx, - .ry = ry - }; - - return tud_xinput_n_report(instance, report_id, &report, sizeof(report)); -} - -//--------------------------------------------------------------------+ -// USBD-CLASS API -//--------------------------------------------------------------------+ -void xinput_reset(uint8_t rhport) -{ - (void) rhport; - tu_memclr(_xinput_itf, sizeof(_xinput_itf)); -} - -void xinput_init(void) -{ - xinput_reset(TUD_OPT_RHPORT); -} - -uint16_t xinput_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len) -{ - //+16 is for the unknown descriptor - uint16_t const drv_len = sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t) + 17; - TU_VERIFY(max_len >= drv_len, 0); - - // Find available interface - xinput_interface_t * p_xinput = NULL; - uint8_t hid_id; - for(hid_id=0; hid_idbNumEndpoints, TUSB_XFER_BULK, &p_xinput->ep_out, &p_xinput->ep_in), 0); - - // Prepare for output endpoint - if (p_xinput->ep_out) - { - if ( !usbd_edpt_xfer(rhport, p_xinput->ep_out, p_xinput->epout_buf, sizeof(p_xinput->epout_buf)) ) - { - TU_LOG_FAILED(); - TU_BREAKPOINT(); - } - } - - return drv_len; -} - -// Invoked when a control transfer occurred on an interface of this class -// Driver response accordingly to the request and the transfer stage (setup/data/ack) -// return false to stall control endpoint (e.g unsupported request) -bool xinput_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) -{ - return true; -} - -bool xinput_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) -{ - (void) result; - - uint8_t instance = 0; - xinput_interface_t * p_xinput = _xinput_itf; - - // Identify which interface to use - for (instance = 0; instance < CFG_TUD_XINPUT; instance++) - { - p_xinput = &_xinput_itf[instance]; - if ( (ep_addr == p_xinput->ep_out) || (ep_addr == p_xinput->ep_in) ) break; - } - TU_ASSERT(instance < CFG_TUD_XINPUT); - - // Sent report successfully - if (ep_addr == p_xinput->ep_in) - { - // TODO: add hook - } - // Received report - else if (ep_addr == p_xinput->ep_out) - { - // TODO - //tud_xinput_set_report_cb(instance, p_xinput->epout_buf, xferred_bytes); - TU_ASSERT(usbd_edpt_xfer(rhport, p_xinput->ep_out, p_xinput->epout_buf, sizeof(p_xinput->epout_buf))); - } - - return true; -} - -static usbd_class_driver_t const xinput_driver = -{ - #if CFG_TUSB_DEBUG >= 2 - .name = "XINPUT", -#endif - .init = xinput_init, - .reset = xinput_reset, - .open = xinput_open, - .control_xfer_cb = xinput_control_xfer_cb, - .xfer_cb = xinput_xfer_cb, - .sof = NULL -}; - -usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) -{ - *driver_count = 1; - return &xinput_driver; -} - -#endif diff --git a/src/hal/Usb/Xinput/xinput_device.hpp b/src/hal/Usb/Xinput/xinput_device.hpp deleted file mode 100644 index 40b463c..0000000 --- a/src/hal/Usb/Xinput/xinput_device.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __XINPUT_DEVICE_H__ -#define __XINPUT_DEVICE_H__ - -#include - -#include "common/tusb_common.h" - -typedef struct TU_ATTR_PACKED -{ - uint16_t buttons; - uint8_t lt; - uint8_t rt; - int lx; - int ly; - int rx; - int ry; -} xinput_gamepad_report_t; - -bool tud_xinput_n_ready(uint8_t instance); - -bool tud_xinput_n_report(uint8_t instance, uint8_t report_id, void const* report, uint8_t len); - -bool tud_xinput_n_gamepad_report(uint8_t instance, uint8_t report_id, - uint16_t buttons, uint8_t lt, uint8_t rt, int lx, int ly, int rx, int ry); - -void tud_xinput_set_report_cb(uint8_t instance, const uint8_t* buf, uint32_t xferred_bytes); - - -#endif // __XINPUT_DEVICE_H__ From 2eea8e8c37758714a723b4e4aceaf0997924aa76 Mon Sep 17 00:00:00 2001 From: James Smith Date: Sat, 17 Dec 2022 16:55:51 -0700 Subject: [PATCH 5/6] 29: Added TTY parser for the CDC interface --- inc/hal/Usb/CommandParser.hpp | 25 +++ inc/hal/Usb/TtyParser.hpp | 19 +++ inc/hal/Usb/usb_interface.hpp | 4 +- src/coreLib/CMakeLists.txt | 7 +- .../parsers/MaplePassthroughCommandParser.cpp | 155 +++++++++++++++++ .../parsers/MaplePassthroughCommandParser.hpp | 32 ++++ src/hal/Usb/Common/UsbCdcTtyParser.cpp | 154 +++++++++++++++++ src/hal/Usb/Common/UsbCdcTtyParser.hpp | 44 +++++ src/hal/Usb/Common/cdc.cpp | 69 ++++---- src/hal/Usb/Common/cdc.hpp | 2 +- src/hal/Usb/Hid/usb_execution.cpp | 6 +- src/main/main.cpp | 161 ++---------------- 12 files changed, 492 insertions(+), 186 deletions(-) create mode 100644 inc/hal/Usb/CommandParser.hpp create mode 100644 inc/hal/Usb/TtyParser.hpp create mode 100644 src/coreLib/parsers/MaplePassthroughCommandParser.cpp create mode 100644 src/coreLib/parsers/MaplePassthroughCommandParser.hpp create mode 100644 src/hal/Usb/Common/UsbCdcTtyParser.cpp create mode 100644 src/hal/Usb/Common/UsbCdcTtyParser.hpp diff --git a/inc/hal/Usb/CommandParser.hpp b/inc/hal/Usb/CommandParser.hpp new file mode 100644 index 0000000..9124b73 --- /dev/null +++ b/inc/hal/Usb/CommandParser.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +#include "hal/System/MutexInterface.hpp" + +// Command structure: [whitespace][command]<\n> + +//! Command parser for processing commands from a TTY stream +class CommandParser +{ +public: + virtual ~CommandParser() {} + + //! @returns the string of command characters this parser handles + virtual const char* getCommandChars() = 0; + + //! Called when newline reached; submit command and reset + virtual void submit(const char* chars, uint32_t len) = 0; + + //! Prints help message for this command + virtual void printHelp() = 0; +}; diff --git a/inc/hal/Usb/TtyParser.hpp b/inc/hal/Usb/TtyParser.hpp new file mode 100644 index 0000000..455d17e --- /dev/null +++ b/inc/hal/Usb/TtyParser.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "hal/Usb/CommandParser.hpp" +#include "hal/System/MutexInterface.hpp" + +//! Command parser for processing commands from a TTY stream +class TtyParser +{ +public: + //! Virtual destructor + virtual ~TtyParser() {} + //! Adds a command parser to my list of parsers - must be done before any other function called + virtual void addCommandParser(std::shared_ptr parser) = 0; + //! Called from the process handling maple bus execution + virtual void process() = 0; +}; + +TtyParser* usb_cdc_create_parser(MutexInterface* m, char helpChar); diff --git a/inc/hal/Usb/usb_interface.hpp b/inc/hal/Usb/usb_interface.hpp index a26e854..3bf3c6c 100644 --- a/inc/hal/Usb/usb_interface.hpp +++ b/inc/hal/Usb/usb_interface.hpp @@ -11,9 +11,7 @@ DreamcastControllerObserver** get_usb_controller_observers(); //! USB initialization void usb_init( MutexInterface* mscMutex, - MutexInterface* cdcStdioMutex, - MutexInterface* cdcRxMutex, - std::vector* cdcRx); + MutexInterface* cdcStdioMutex); //! USB task that needs to be called constantly by main() void usb_task(); //! @returns number of USB controllers diff --git a/src/coreLib/CMakeLists.txt b/src/coreLib/CMakeLists.txt index ab08877..3d5ce98 100644 --- a/src/coreLib/CMakeLists.txt +++ b/src/coreLib/CMakeLists.txt @@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.12) set(CMAKE_VERBOSE_MAKEFILE ON) -file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c*" "${CMAKE_CURRENT_SOURCE_DIR}/peripherals/*.c*") +file(GLOB SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.c*" + "${CMAKE_CURRENT_SOURCE_DIR}/peripherals/*.c*" + "${CMAKE_CURRENT_SOURCE_DIR}/parsers/*.c*") add_library(coreLib STATIC ${SRC}) @@ -19,4 +21,5 @@ target_include_directories(coreLib "$" "$" "${PROJECT_SOURCE_DIR}/inc" - "${CMAKE_CURRENT_SOURCE_DIR}/peripherals") + "${CMAKE_CURRENT_SOURCE_DIR}/peripherals" + "${CMAKE_CURRENT_SOURCE_DIR}/parsers") diff --git a/src/coreLib/parsers/MaplePassthroughCommandParser.cpp b/src/coreLib/parsers/MaplePassthroughCommandParser.cpp new file mode 100644 index 0000000..92fdc8d --- /dev/null +++ b/src/coreLib/parsers/MaplePassthroughCommandParser.cpp @@ -0,0 +1,155 @@ +#include "MaplePassthroughCommandParser.hpp" +#include "hal/MapleBus/MaplePacket.hpp" + +#include + +// Simple definition of a transmitter which just echos status and received data +class EchoTransmitter : public Transmitter +{ +public: + virtual void txStarted(std::shared_ptr tx) final + {} + + virtual void txFailed(bool writeFailed, + bool readFailed, + std::shared_ptr tx) final + { + if (writeFailed) + { + printf("%lu: failed write\n", tx->transmissionId); + } + else + { + printf("%lu: failed read\n", tx->transmissionId); + } + } + + virtual void txComplete(std::shared_ptr packet, + std::shared_ptr tx) final + { + printf("%lu: complete {", tx->transmissionId); + printf("%08lX", packet->frameWord); + for (std::vector::const_iterator iter = packet->payload.begin(); + iter != packet->payload.end(); + ++iter) + { + printf(" %08lX", *iter); + } + printf("}\n"); + } +} echoTransmitter; + +MaplePassthroughCommandParser::MaplePassthroughCommandParser(std::shared_ptr* schedulers, + const uint8_t* senderAddresses, + uint32_t numSenders) : + mSchedulers(schedulers), + mSenderAddresses(senderAddresses), + mNumSenders(numSenders) +{} + +const char* MaplePassthroughCommandParser::getCommandChars() +{ + // Anything beginning with a hex character should be considered a passthrough command + return "0123456789ABCDEFabcdef"; +} + +void MaplePassthroughCommandParser::submit(const char* chars, uint32_t len) +{ + bool valid = false; + const char* const eol = chars + len; + std::vector words; + const char* iter = chars; + while(iter < eol) + { + uint32_t word = 0; + uint32_t i = 0; + while (i < 8 && iter < eol) + { + char v = *iter++; + uint_fast8_t value = 0; + + if (v >= '0' && v <= '9') + { + value = v - '0'; + } + else if (v >= 'a' && v <= 'f') + { + value = v - 'a' + 0xa; + } + else if (v >= 'A' && v <= 'F') + { + value = v - 'A' + 0xA; + } + else + { + // Ignore this character + continue; + } + + // Apply value into current word + word |= (value << ((8 - i) * 4 - 4)); + ++i; + } + + // Invalid if a partial word was given + valid = ((i == 8) || (i == 0)); + + if (i == 8) + { + words.push_back(word); + } + } + + if (valid) + { + MaplePacket packet(&words[0], words.size()); + if (packet.isValid()) + { + uint8_t sender = packet.getFrameSenderAddr(); + int32_t idx = -1; + const uint8_t* senderAddress = mSenderAddresses; + + for (uint32_t i = 0; i < mNumSenders && idx < 0; ++i, ++senderAddress) + { + if (sender == *senderAddress) + { + idx = i; + } + } + + if (idx >= 0) + { + uint32_t id = mSchedulers[idx]->add( + 0, + PrioritizedTxScheduler::TX_TIME_ASAP, + &echoTransmitter, + packet, + true); + std::vector::iterator iter = words.begin(); + printf("%lu: added {%08lX", id, *iter++); + for(; iter < words.end(); ++iter) + { + printf(" %08lX", *iter); + } + printf("}\n"); + } + else + { + printf("0: failed invalid sender\n"); + } + } + else + { + printf("0: failed packet invalid\n"); + } + } + else + { + printf("0: failed missing data\n"); + } +} + +void MaplePassthroughCommandParser::printHelp() +{ + printf("0-1 a-f A-F: the beginning of a hex value to send to maple bus without CRC\n"); +} diff --git a/src/coreLib/parsers/MaplePassthroughCommandParser.hpp b/src/coreLib/parsers/MaplePassthroughCommandParser.hpp new file mode 100644 index 0000000..eeef38f --- /dev/null +++ b/src/coreLib/parsers/MaplePassthroughCommandParser.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "hal/Usb/CommandParser.hpp" + +#include "PrioritizedTxScheduler.hpp" + +#include + +// Command structure: [whitespace][command]<\n> + +//! Command parser for processing commands from a TTY stream +class MaplePassthroughCommandParser : public CommandParser +{ +public: + MaplePassthroughCommandParser(std::shared_ptr* schedulers, + const uint8_t* senderAddresses, + uint32_t numSenders); + + //! @returns the string of command characters this parser handles + virtual const char* getCommandChars() final; + + //! Called when newline reached; submit command and reset + virtual void submit(const char* chars, uint32_t len) final; + + //! Prints help message for this command + virtual void printHelp() final; + +private: + std::shared_ptr* const mSchedulers; + const uint8_t* const mSenderAddresses; + const uint32_t mNumSenders; +}; diff --git a/src/hal/Usb/Common/UsbCdcTtyParser.cpp b/src/hal/Usb/Common/UsbCdcTtyParser.cpp new file mode 100644 index 0000000..d519a4d --- /dev/null +++ b/src/hal/Usb/Common/UsbCdcTtyParser.cpp @@ -0,0 +1,154 @@ +#include "UsbCdcTtyParser.hpp" +#include "hal/System/LockGuard.hpp" + +#include +#include +#include + +const char* UsbCdcTtyParser::WHITESPACE_CHARS = "\n\r\t "; + +UsbCdcTtyParser::UsbCdcTtyParser(MutexInterface& m, char helpChar) : + mParserRx(), + mParserMutex(m), + mCommandReady(false), + mHelpChar(helpChar), + mParsers(), + mOverflowDetected(false) +{} + +void UsbCdcTtyParser::addCommandParser(std::shared_ptr parser) +{ + mParsers.push_back(parser); +} + +void UsbCdcTtyParser::addChars(const char* chars, uint32_t len) +{ + // Entire function is locked + LockGuard lockGuard(mParserMutex); + + // Reserve space for new characters + uint32_t newCapacity = mParserRx.size() + len; + if (newCapacity > MAX_QUEUE_SIZE) + { + newCapacity = MAX_QUEUE_SIZE; + } + mParserRx.reserve(newCapacity); + + for (uint32_t i = 0; i < len; ++i, ++chars) + { + // Flag overflow - next command will be ignored + if (mParserRx.size() >= MAX_QUEUE_SIZE && *chars != 0x08) + { + mOverflowDetected = true; + } + + if (mOverflowDetected) + { + if (*chars == '\n') + { + printf("Error: Command input overflow\n"); + // Remove only command that overflowed + std::vector::reverse_iterator lastEnd = + std::find(mParserRx.rbegin(), mParserRx.rend(), '\n'); + if (lastEnd == mParserRx.rend()) + { + mParserRx.clear(); + } + else + { + // We still captured a full command, so at least leave that for processing + mParserRx.erase((lastEnd + 1).base(), mParserRx.end()); + } + mOverflowDetected = false; + } + } + else if (*chars == 0x08) + { + if (!mParserRx.empty()) + { + // Backspace + mParserRx.pop_back(); + } + } + else + { + if (*chars == '\n') + { + mCommandReady = true; + } + mParserRx.push_back(*chars); + } + } +} + +void UsbCdcTtyParser::process() +{ + // Only do something if a command is ready + if (mCommandReady) + { + // Begin lock guard context + LockGuard lockGuard(mParserMutex); + + mCommandReady = false; + + // End of command is at the new line character + std::vector::iterator eol = + std::find(mParserRx.begin(), mParserRx.end(), '\n'); + + if (eol != mParserRx.end()) + { + // Just in case it gets parsed as a string, changed EOL to NULL + *eol = '\0'; + // Move past whitespace characters + const char* ptr = &mParserRx[0]; + uint32_t len = eol - mParserRx.begin(); + while (len > 0 && strchr(WHITESPACE_CHARS, *ptr) != NULL) + { + --len; + ++ptr; + } + + if (len > 0) + { + if (*ptr == mHelpChar) + { + printf("HELP\n" + "Command structure: [whitespace][command]<\\n>\n" + "\n" + "COMMANDS:\n"); + printf("%c: Prints this help\n", mHelpChar); + // Print help for all commands + for (std::vector>::iterator iter = mParsers.begin(); + iter != mParsers.end(); + ++iter) + { + (*iter)->printHelp(); + } + } + else + { + // Find command parser that can process this command + bool processed = false; + for (std::vector>::iterator iter = mParsers.begin(); + iter != mParsers.end() && !processed; + ++iter) + { + if (strchr((*iter)->getCommandChars(), *ptr) != NULL) + { + (*iter)->submit(ptr, len); + processed = true; + } + } + + if (!processed) + { + printf("Error: Invalid command\n"); + } + } + } + // Else: empty string - do nothing + + mParserRx.erase(mParserRx.begin(), eol + 1); + } + } // End lock guard context +} diff --git a/src/hal/Usb/Common/UsbCdcTtyParser.hpp b/src/hal/Usb/Common/UsbCdcTtyParser.hpp new file mode 100644 index 0000000..a1c1ea3 --- /dev/null +++ b/src/hal/Usb/Common/UsbCdcTtyParser.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include + +#include "hal/System/MutexInterface.hpp" +#include "hal/Usb/TtyParser.hpp" +#include "hal/Usb/CommandParser.hpp" + +// Command structure: [whitespace][command]<\n> + +//! Command parser for processing commands from a TTY stream +class UsbCdcTtyParser : public TtyParser +{ +public: + //! Constructor + UsbCdcTtyParser(MutexInterface& m, char helpChar); + //! Adds a command parser to my list of parsers - must be done before any other function called + virtual void addCommandParser(std::shared_ptr parser) final; + //! Called from the process receiving characters on the TTY + void addChars(const char* chars, uint32_t len); + //! Called from the process handling maple bus execution + virtual void process() final; + +private: + //! Max of 2 KB of memory to use for tty RX queue + static const uint32_t MAX_QUEUE_SIZE = 2048; + //! String of characters that are considered whitespace + static const char* WHITESPACE_CHARS; + //! Receive queue + std::vector mParserRx; + //! Mutex used to serialize addChars and process + MutexInterface& mParserMutex; + //! Flag when end of line detected on add + std::atomic mCommandReady; + //! The command character which prints help for all commands + const char mHelpChar; + //! Parsers that may handle data + std::vector> mParsers; + //! true when overflow in mParserRx + bool mOverflowDetected; +}; diff --git a/src/hal/Usb/Common/cdc.cpp b/src/hal/Usb/Common/cdc.cpp index 16600e1..697f636 100644 --- a/src/hal/Usb/Common/cdc.cpp +++ b/src/hal/Usb/Common/cdc.cpp @@ -16,11 +16,24 @@ #include "class/hid/hid_device.h" #include "class/cdc/cdc_device.h" +#include "UsbCdcTtyParser.hpp" + + +UsbCdcTtyParser* ttyParser = nullptr; + +TtyParser* usb_cdc_create_parser(MutexInterface* m, char helpChar) +{ + if (ttyParser == nullptr) + { + ttyParser = new UsbCdcTtyParser(*m, helpChar); + } + return ttyParser; +} + + #if CFG_TUD_CDC static MutexInterface* stdioMutex = nullptr; -static MutexInterface* rxMutex; -static std::vector* rx; // Can't use stdio_usb_init() because it checks tud_cdc_connected(), and that doesn't always return // true when a connection is made. Not all terminal client set this when making connection. @@ -92,14 +105,9 @@ struct stdio_driver stdio_usb2 = } // extern "C" -void cdc_init( - MutexInterface* cdcStdioMutex, - MutexInterface* cdcRxMutex, - std::vector* cdcRx) +void cdc_init(MutexInterface* cdcStdioMutex) { stdioMutex = cdcStdioMutex; - rxMutex = cdcRxMutex; - rx = cdcRx; stdio_set_driver_enabled(&stdio_usb2, true); } @@ -107,37 +115,40 @@ void cdc_task() { #if USB_CDC_ENABLED // connected and there are data available - if ( tud_cdc_available() ) + if (tud_cdc_available()) { - char buf[64]; - uint32_t count = 0; - + if (ttyParser) { - LockGuard lockGuard(*stdioMutex); - if (!lockGuard.isLocked()) + char buf[64]; + uint32_t count = 0; + { - return; // would deadlock otherwise; just wait for next loop + LockGuard lockGuard(*stdioMutex); + if (!lockGuard.isLocked()) + { + return; // would deadlock otherwise; just wait for next loop + } + + // read data + count = tud_cdc_read(buf, sizeof(buf)); + + if (count > 0) + { + // Echo back + tud_cdc_write(buf, count); + tud_cdc_write_flush(); + } } - // read data - count = tud_cdc_read(buf, sizeof(buf)); - if (count > 0) { - // Echo back - tud_cdc_write(buf, count); - tud_cdc_write_flush(); + ttyParser->addChars(buf, count); } } - - if (count > 0) + else { - LockGuard lockGuard(*stdioMutex, true); - rx->reserve(rx->size() + count); - for (uint32_t i = 0; i < count; ++i) - { - rx->push_back(buf[i]); - } + // Parser not created yet + tud_cdc_read_flush(); } } #endif diff --git a/src/hal/Usb/Common/cdc.hpp b/src/hal/Usb/Common/cdc.hpp index 5eb65e1..f5908ed 100644 --- a/src/hal/Usb/Common/cdc.hpp +++ b/src/hal/Usb/Common/cdc.hpp @@ -5,5 +5,5 @@ // CDC is used to create a debug serial interface -void cdc_init(MutexInterface* cdcStdioMutex, MutexInterface* cdcRxMutex, std::vector* rx); +void cdc_init(MutexInterface* cdcStdioMutex); void cdc_task(); diff --git a/src/hal/Usb/Hid/usb_execution.cpp b/src/hal/Usb/Hid/usb_execution.cpp index a824e22..bbafa6a 100644 --- a/src/hal/Usb/Hid/usb_execution.cpp +++ b/src/hal/Usb/Hid/usb_execution.cpp @@ -127,15 +127,13 @@ void led_task() void usb_init( MutexInterface* mscMutex, - MutexInterface* cdcStdioMutex, - MutexInterface* cdcRxMutex, - std::vector* cdcRx) + MutexInterface* cdcStdioMutex) { set_usb_devices(devices, sizeof(devices) / sizeof(devices[1])); board_init(); tusb_init(); msc_init(mscMutex); - cdc_init(cdcStdioMutex, cdcRxMutex, cdcRx); + cdc_init(cdcStdioMutex); #if USB_LED_PIN >= 0 gpio_init(USB_LED_PIN); diff --git a/src/main/main.cpp b/src/main/main.cpp index fccd703..e3f6c68 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -7,6 +7,8 @@ #include "DreamcastMainNode.hpp" #include "PlayerData.hpp" +#include "MaplePassthroughCommandParser.hpp" + #include "CriticalSectionMutex.hpp" #include "Mutex.hpp" #include "Clock.hpp" @@ -14,6 +16,7 @@ #include "hal/System/LockGuard.hpp" #include "hal/MapleBus/MapleBusInterface.hpp" #include "hal/Usb/usb_interface.hpp" +#include "hal/Usb/TtyParser.hpp" #include #include @@ -22,43 +25,6 @@ const uint8_t MAPLE_HOST_ADDRESSES[MAX_DEVICES] = {0x00, 0x40, 0x80, 0xC0}; -Mutex cdcRxMutex; -std::vector cdcRx; - -class EchoTransmitter : public Transmitter -{ - virtual void txStarted(std::shared_ptr tx) final - {} - - virtual void txFailed(bool writeFailed, - bool readFailed, - std::shared_ptr tx) final - { - if (writeFailed) - { - printf("%lu: failed write\n", tx->transmissionId); - } - else - { - printf("%lu: failed read\n", tx->transmissionId); - } - } - - virtual void txComplete(std::shared_ptr packet, - std::shared_ptr tx) final - { - printf("%lu: complete {", tx->transmissionId); - printf("%08lX", packet->frameWord); - for (std::vector::const_iterator iter = packet->payload.begin(); - iter != packet->payload.end(); - ++iter) - { - printf(" %08lX", *iter); - } - printf("}\n"); - } -} echoTransmitter; - // Second Core Process // The second core is in charge of handling communication with Dreamcast peripherals void core1() @@ -98,122 +64,23 @@ void core1() schedulers[i]); } - uint32_t lastSize = 0; + // Initialize CDC to Maple Bus interfaces + Mutex ttyParserMutex; + TtyParser* ttyParser = usb_cdc_create_parser(&ttyParserMutex, 'h'); + ttyParser->addCommandParser( + std::make_shared( + &schedulers[0], MAPLE_HOST_ADDRESSES, numDevices)); + while(true) { + // Process each main node for (uint32_t i = 0; i < numDevices; ++i) { // Worst execution duration of below is ~350 us at 133 MHz when debug print is disabled dreamcastMainNodes[i]->task(time_us_64()); } - -#if 1 - { - LockGuard lockGuard(cdcRxMutex, true); - - if (lastSize != cdcRx.size()) - { - std::vector::iterator eol = std::find(cdcRx.begin(), cdcRx.end(), '\n'); - if (eol != cdcRx.end()) - { - bool valid = false; - std::vector words; - std::vector::iterator iter = cdcRx.begin(); - while(iter != eol) - { - uint32_t word = 0; - uint32_t i = 0; - while (i < 8 && iter != eol) - { - char v = *iter; - if (v >= '0' && v <= '9') - { - word |= ((v - '0') << ((8 - i) * 4 - 4)); - ++i; - } - else if (v >= 'a' && v <= 'f') - { - word |= ((v - 'a' + 10) << ((8 - i) * 4 - 4)); - ++i; - } - else if (v >= 'A' && v <= 'F') - { - word |= ((v - 'A' + 10) << ((8 - i) * 4 - 4)); - ++i; - } - ++iter; - } - valid = ((i == 8) || (i == 0)); - - if (i == 8) - { - words.push_back(word); - } - } - - if (valid) - { - MaplePacket packet(&words[0], words.size()); - if (packet.isValid()) - { - uint8_t sender = packet.getFrameSenderAddr(); - int32_t idx = -1; - if (sender == 0 && numDevices > 0) - { - idx = 0; - } - else if (sender == 0x40 && numDevices > 1) - { - idx = 1; - } - else if (sender == 0x80 && numDevices > 2) - { - idx = 2; - } - else if (sender == 0xC0 && numDevices > 3) - { - idx = 3; - } - - if (idx >= 0) - { - uint32_t id = schedulers[idx]->add( - 0, - PrioritizedTxScheduler::TX_TIME_ASAP, - &echoTransmitter, - packet, - true); - std::vector::iterator iter = words.begin(); - printf("%lu: added {%08lX", id, *iter++); - for(; iter < words.end(); ++iter) - { - printf(" %08lX", *iter); - } - printf("}\n"); - } - else - { - printf("0: failed invalid sender\n"); - } - } - else - { - printf("0: failed packet invalid\n"); - } - } - else - { - printf("0: failed missing data\n"); - } - - cdcRx.erase(cdcRx.begin(), eol + 1); - } - - - lastSize = cdcRx.size(); - } - } -#endif + // Process any waiting commands in the TTY parser + ttyParser->process(); } } @@ -231,7 +98,7 @@ int main() Mutex fileMutex; Mutex cdcStdioMutex; - usb_init(&fileMutex, &cdcStdioMutex, &cdcRxMutex, &cdcRx); + usb_init(&fileMutex, &cdcStdioMutex); while(true) { From f6b6d8cb056965f199f1d18d0c15c3b8ba22efb6 Mon Sep 17 00:00:00 2001 From: James Smith Date: Sat, 17 Dec 2022 17:20:17 -0700 Subject: [PATCH 6/6] 29: Cleaned up priority selection --- src/coreLib/DreamcastMainNode.cpp | 8 ++------ src/coreLib/DreamcastMainNode.hpp | 6 ------ src/coreLib/PrioritizedTxScheduler.cpp | 4 ++-- src/coreLib/PrioritizedTxScheduler.hpp | 16 +++++++++++++++- .../parsers/MaplePassthroughCommandParser.cpp | 2 +- src/main/main.cpp | 2 +- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/coreLib/DreamcastMainNode.cpp b/src/coreLib/DreamcastMainNode.cpp index d71369f..b2f67a0 100644 --- a/src/coreLib/DreamcastMainNode.cpp +++ b/src/coreLib/DreamcastMainNode.cpp @@ -4,17 +4,13 @@ #include "DreamcastController.hpp" #include "EndpointTxScheduler.hpp" -const uint8_t DreamcastMainNode::MAIN_TRANSMISSION_PRIORITY = 1; -const uint8_t DreamcastMainNode::SUB_TRANSMISSION_PRIORITY = 2; -const uint8_t DreamcastMainNode::MAX_PRIORITY = 2; - DreamcastMainNode::DreamcastMainNode(MapleBusInterface& bus, PlayerData playerData, std::shared_ptr prioritizedTxScheduler) : DreamcastNode(DreamcastPeripheral::MAIN_PERIPHERAL_ADDR_MASK, std::make_shared( prioritizedTxScheduler, - MAIN_TRANSMISSION_PRIORITY, + PrioritizedTxScheduler::MAIN_TRANSMISSION_PRIORITY, DreamcastPeripheral::getRecipientAddress( playerData.playerIndex, DreamcastPeripheral::MAIN_PERIPHERAL_ADDR_MASK) ), @@ -32,7 +28,7 @@ DreamcastMainNode::DreamcastMainNode(MapleBusInterface& bus, addr, std::make_shared( prioritizedTxScheduler, - SUB_TRANSMISSION_PRIORITY, + PrioritizedTxScheduler::SUB_TRANSMISSION_PRIORITY, DreamcastPeripheral::getRecipientAddress(playerData.playerIndex, addr)), mPlayerData)); } diff --git a/src/coreLib/DreamcastMainNode.hpp b/src/coreLib/DreamcastMainNode.hpp index d6c4c61..16eb0b2 100644 --- a/src/coreLib/DreamcastMainNode.hpp +++ b/src/coreLib/DreamcastMainNode.hpp @@ -65,12 +65,6 @@ class DreamcastMainNode : public DreamcastNode public: //! Number of microseconds in between each info request when no peripheral is detected static const uint32_t US_PER_CHECK = 16000; - //! Main node has highest priority - static const uint8_t MAIN_TRANSMISSION_PRIORITY; - //! Sub nodes have lower priority - static const uint8_t SUB_TRANSMISSION_PRIORITY; - //! Maximum allowed priority - static const uint8_t MAX_PRIORITY; protected: //! The sub nodes under this node diff --git a/src/coreLib/PrioritizedTxScheduler.cpp b/src/coreLib/PrioritizedTxScheduler.cpp index 0f451e9..27db2c6 100644 --- a/src/coreLib/PrioritizedTxScheduler.cpp +++ b/src/coreLib/PrioritizedTxScheduler.cpp @@ -7,11 +7,11 @@ // STL #include -PrioritizedTxScheduler::PrioritizedTxScheduler(uint8_t maxPriority) : +PrioritizedTxScheduler::PrioritizedTxScheduler() : mNextId(1), mSchedule() { - mSchedule.resize(maxPriority + 1); + mSchedule.resize(PRIORITY_COUNT); } PrioritizedTxScheduler::~PrioritizedTxScheduler() {} diff --git a/src/coreLib/PrioritizedTxScheduler.hpp b/src/coreLib/PrioritizedTxScheduler.hpp index 331c097..024bef9 100644 --- a/src/coreLib/PrioritizedTxScheduler.hpp +++ b/src/coreLib/PrioritizedTxScheduler.hpp @@ -9,9 +9,23 @@ class PrioritizedTxScheduler { +public: + //! Enumerates available priorities to be used with add() + enum Priority : uint8_t + { + //! Priority for external entity taking control of the bus (max) + EXTERNAL_TRANSMISSION_PRIORITY = 0, + //! Priority for main peripheral + MAIN_TRANSMISSION_PRIORITY, + //! Priority for sub peripheral (min) + SUB_TRANSMISSION_PRIORITY, + //! Any selected priority must be less than PRIORITY_COUNT + PRIORITY_COUNT + }; + public: //! Default constructor - PrioritizedTxScheduler(uint8_t maxPriority); + PrioritizedTxScheduler(); //! Virtual destructor virtual ~PrioritizedTxScheduler(); diff --git a/src/coreLib/parsers/MaplePassthroughCommandParser.cpp b/src/coreLib/parsers/MaplePassthroughCommandParser.cpp index 92fdc8d..345c119 100644 --- a/src/coreLib/parsers/MaplePassthroughCommandParser.cpp +++ b/src/coreLib/parsers/MaplePassthroughCommandParser.cpp @@ -120,7 +120,7 @@ void MaplePassthroughCommandParser::submit(const char* chars, uint32_t len) if (idx >= 0) { uint32_t id = mSchedulers[idx]->add( - 0, + PrioritizedTxScheduler::EXTERNAL_TRANSMISSION_PRIORITY, PrioritizedTxScheduler::TX_TIME_ASAP, &echoTransmitter, packet, diff --git a/src/main/main.cpp b/src/main/main.cpp index e3f6c68..0ed4e54 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -57,7 +57,7 @@ void core1() clock, usb_msc_get_file_system()); buses[i] = create_maple_bus(maplePins[i], MAPLE_HOST_ADDRESSES[i]); - schedulers[i] = std::make_shared(DreamcastMainNode::MAX_PRIORITY); + schedulers[i] = std::make_shared(); dreamcastMainNodes[i] = std::make_shared( *buses[i], *playerData[i],