It's connecting!!
This commit is contained in:
parent
bb5e51330a
commit
2225e70e5e
7 changed files with 322 additions and 45 deletions
|
|
@ -23,6 +23,7 @@ if (EXISTS ${picoVscode})
|
|||
include(${picoVscode})
|
||||
endif()
|
||||
# ====================================================================================
|
||||
option(SWITCH_PICO_AUTOTEST "Auto-play grip-screen connection sequence on startup" OFF)
|
||||
set(PICO_BOARD pico CACHE STRING "Board type")
|
||||
|
||||
# Pull in Raspberry Pi Pico SDK (must be before project)
|
||||
|
|
@ -44,7 +45,8 @@ pico_set_program_name(switch-pico "switch-pico")
|
|||
pico_set_program_version(switch-pico "0.1")
|
||||
|
||||
# Modify the below lines to enable/disable output over UART/USB
|
||||
pico_enable_stdio_uart(switch-pico 0)
|
||||
# UART0 is enabled for debug logging; USB stdio remains off.
|
||||
pico_enable_stdio_uart(switch-pico 1)
|
||||
pico_enable_stdio_usb(switch-pico 0)
|
||||
|
||||
# Add the standard library to the build
|
||||
|
|
@ -56,6 +58,10 @@ target_link_libraries(switch-pico
|
|||
pico_rand
|
||||
)
|
||||
|
||||
if (SWITCH_PICO_AUTOTEST)
|
||||
target_compile_definitions(switch-pico PRIVATE SWITCH_PICO_AUTOTEST=1)
|
||||
endif()
|
||||
|
||||
# Add the standard include files to the build
|
||||
target_include_directories(switch-pico PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
|
|
|
|||
33
host_uart_logger.py
Normal file
33
host_uart_logger.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import argparse
|
||||
import datetime
|
||||
import sys
|
||||
|
||||
try:
|
||||
import serial
|
||||
except ImportError:
|
||||
print("pyserial not installed. Install with: pip install pyserial", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Read debug logs from Pico UART")
|
||||
parser.add_argument("-p", "--port", required=True, help="Serial port (e.g. /dev/ttyUSB0 or COM3)")
|
||||
parser.add_argument("-b", "--baud", type=int, default=115200, help="Baud rate (default: 115200)")
|
||||
args = parser.parse_args()
|
||||
|
||||
with serial.Serial(args.port, args.baud, timeout=1) as ser:
|
||||
print(f"Opened {args.port} @ {args.baud}")
|
||||
while True:
|
||||
line = ser.readline()
|
||||
if not line:
|
||||
continue
|
||||
ts = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
||||
try:
|
||||
text = line.decode(errors="replace").rstrip()
|
||||
except Exception:
|
||||
text = repr(line)
|
||||
print(f"{ts} | {text}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
126
switch-pico.cpp
126
switch-pico.cpp
|
|
@ -11,6 +11,20 @@
|
|||
#define UART_TX_PIN 4
|
||||
#define UART_RX_PIN 5
|
||||
|
||||
static bool g_last_mounted = false;
|
||||
static bool g_last_ready = false;
|
||||
|
||||
// Track the latest state provided by UART or the autopilot.
|
||||
static SwitchInputState g_user_state;
|
||||
|
||||
#ifdef SWITCH_PICO_AUTOTEST
|
||||
static bool g_autopilot_active = true;
|
||||
static uint32_t g_autopilot_counter = 0;
|
||||
static absolute_time_t g_autopilot_last_tick = {0};
|
||||
static bool g_uart_activity = false;
|
||||
static bool g_ready_logged = false;
|
||||
#endif
|
||||
|
||||
static void init_uart_input() {
|
||||
uart_init(UART_ID, BAUD_RATE);
|
||||
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
|
||||
|
|
@ -52,12 +66,104 @@ static void poll_uart_frames() {
|
|||
|
||||
buffer[index++] = byte;
|
||||
if (index >= sizeof(buffer)) {
|
||||
switch_pro_apply_uart_packet(buffer, sizeof(buffer));
|
||||
SwitchInputState parsed{};
|
||||
if (switch_pro_apply_uart_packet(buffer, sizeof(buffer), &parsed)) {
|
||||
g_user_state = parsed;
|
||||
printf("[UART] packet buttons=0x%04x hat=%u lx=%u ly=%u rx=%u ry=%u\n",
|
||||
(parsed.button_a ? SWITCH_PRO_MASK_A : 0) |
|
||||
(parsed.button_b ? SWITCH_PRO_MASK_B : 0) |
|
||||
(parsed.button_x ? SWITCH_PRO_MASK_X : 0) |
|
||||
(parsed.button_y ? SWITCH_PRO_MASK_Y : 0) |
|
||||
(parsed.button_l ? SWITCH_PRO_MASK_L : 0) |
|
||||
(parsed.button_r ? SWITCH_PRO_MASK_R : 0) |
|
||||
(parsed.button_zl ? SWITCH_PRO_MASK_ZL : 0) |
|
||||
(parsed.button_zr ? SWITCH_PRO_MASK_ZR : 0) |
|
||||
(parsed.button_plus? SWITCH_PRO_MASK_PLUS: 0) |
|
||||
(parsed.button_minus?SWITCH_PRO_MASK_MINUS:0) |
|
||||
(parsed.button_home?SWITCH_PRO_MASK_HOME:0) |
|
||||
(parsed.button_capture?SWITCH_PRO_MASK_CAPTURE:0) |
|
||||
(parsed.button_l3 ? SWITCH_PRO_MASK_L3 : 0) |
|
||||
(parsed.button_r3 ? SWITCH_PRO_MASK_R3 : 0),
|
||||
parsed.dpad_up ? SWITCH_PRO_HAT_UP :
|
||||
parsed.dpad_down ? SWITCH_PRO_HAT_DOWN :
|
||||
parsed.dpad_left ? SWITCH_PRO_HAT_LEFT :
|
||||
parsed.dpad_right ? SWITCH_PRO_HAT_RIGHT : SWITCH_PRO_HAT_NOTHING,
|
||||
parsed.lx >> 8, parsed.ly >> 8, parsed.rx >> 8, parsed.ry >> 8);
|
||||
}
|
||||
#ifdef SWITCH_PICO_AUTOTEST
|
||||
g_uart_activity = true;
|
||||
#endif
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SWITCH_PICO_AUTOTEST
|
||||
// Replays the Switch-Fightstick grip-screen sequence: press L+R twice, then A twice.
|
||||
static SwitchInputState autopilot_state(const SwitchInputState& fallback) {
|
||||
if (!g_autopilot_active || g_uart_activity) {
|
||||
g_autopilot_active = false;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
if (!tud_mounted()) {
|
||||
g_autopilot_counter = 0;
|
||||
g_autopilot_last_tick = {0};
|
||||
return fallback;
|
||||
}
|
||||
|
||||
absolute_time_t now = get_absolute_time();
|
||||
if (!to_us_since_boot(g_autopilot_last_tick)) {
|
||||
g_autopilot_last_tick = now;
|
||||
}
|
||||
|
||||
// Run at ~1ms cadence similar to the LUFA fightstick timing.
|
||||
if (absolute_time_diff_us(g_autopilot_last_tick, now) < 1000) {
|
||||
return fallback;
|
||||
}
|
||||
g_autopilot_last_tick = now;
|
||||
|
||||
SwitchInputState state = fallback;
|
||||
state.lx = SWITCH_PRO_JOYSTICK_MID;
|
||||
state.ly = SWITCH_PRO_JOYSTICK_MID;
|
||||
state.rx = SWITCH_PRO_JOYSTICK_MID;
|
||||
state.ry = SWITCH_PRO_JOYSTICK_MID;
|
||||
|
||||
// Fire L+R twice then A twice, loop every ~300ms to keep trying.
|
||||
uint32_t step = g_autopilot_counter % 300;
|
||||
if (step == 25 || step == 50) {
|
||||
state.button_l = true;
|
||||
state.button_r = true;
|
||||
} else if (step == 75 || step == 100) {
|
||||
state.button_a = true;
|
||||
}
|
||||
|
||||
g_autopilot_counter++;
|
||||
|
||||
return state;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void log_usb_state() {
|
||||
bool mounted = tud_mounted();
|
||||
bool ready = switch_pro_is_ready();
|
||||
|
||||
if (mounted != g_last_mounted) {
|
||||
g_last_mounted = mounted;
|
||||
printf("[USB] %s\n", mounted ? "mounted" : "unmounted");
|
||||
}
|
||||
if (ready != g_last_ready) {
|
||||
g_last_ready = ready;
|
||||
printf("[SWITCH] driver %s\n", ready ? "ready (handshake OK)" : "not ready");
|
||||
}
|
||||
#ifdef SWITCH_PICO_AUTOTEST
|
||||
if (ready && !g_ready_logged) {
|
||||
g_ready_logged = true;
|
||||
printf("[AUTO] ready -> autopilot active=%s\n", g_autopilot_active ? "true" : "false");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main() {
|
||||
board_init();
|
||||
stdio_init_all();
|
||||
|
|
@ -66,11 +172,27 @@ int main() {
|
|||
|
||||
tusb_init();
|
||||
switch_pro_init();
|
||||
switch_pro_set_input(neutral_input());
|
||||
g_user_state = neutral_input();
|
||||
switch_pro_set_input(g_user_state);
|
||||
|
||||
printf("[BOOT] switch-pico starting (UART0 log @ 115200)\n");
|
||||
printf("[INFO] AUTOTEST=%s UART1 pins TX=%d RX=%d baud=%d\n",
|
||||
#ifdef SWITCH_PICO_AUTOTEST
|
||||
"ON"
|
||||
#else
|
||||
"OFF"
|
||||
#endif
|
||||
, UART_TX_PIN, UART_RX_PIN, BAUD_RATE);
|
||||
|
||||
while (true) {
|
||||
tud_task(); // USB device tasks
|
||||
poll_uart_frames(); // Pull controller state from UART1
|
||||
SwitchInputState state = g_user_state;
|
||||
#ifdef SWITCH_PICO_AUTOTEST
|
||||
state = autopilot_state(state);
|
||||
#endif
|
||||
switch_pro_set_input(state);
|
||||
switch_pro_task(); // Push state to the Switch host
|
||||
log_usb_state();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,8 +322,10 @@ typedef struct
|
|||
} SwitchProOutReport;
|
||||
|
||||
static const uint8_t switch_pro_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community";
|
||||
static const uint8_t switch_pro_string_product[] = "GP2040-CE (Pro Controller)";
|
||||
static const uint8_t switch_pro_string_manufacturer[] = "Nintendo Co., Ltd.";
|
||||
static const uint8_t switch_pro_string_product[] = "Pro Controller";
|
||||
// static const uint8_t switch_pro_string_manufacturer[] = "Open Stick Community";
|
||||
// static const uint8_t switch_pro_string_product[] = "GP2040-CE (Pro Controller)";
|
||||
static const uint8_t switch_pro_string_version[] = "000000000001";
|
||||
|
||||
static const uint8_t *switch_pro_string_descriptors[] __attribute__((unused)) =
|
||||
|
|
@ -340,8 +342,8 @@ static const uint8_t switch_pro_device_descriptor[] =
|
|||
0x01, // bDescriptorType (Device)
|
||||
0x00, 0x02, // bcdUSB 2.00
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x7E, 0x05, // idVendor 0x057E
|
||||
0x09, 0x20, // idProduct 0x2009
|
||||
|
|
@ -394,7 +396,7 @@ static const uint8_t switch_pro_configuration_descriptor[] =
|
|||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x05, // bEndpointAddress (IN/D2H)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x08, // bInterval 8 (unit depends on device speed)
|
||||
|
|
|
|||
|
|
@ -22,11 +22,14 @@ static uint8_t last_report[SWITCH_PRO_ENDPOINT_SIZE] = {};
|
|||
static SwitchProReport switch_report{};
|
||||
static uint8_t last_report_counter = 0;
|
||||
static uint32_t last_report_timer = 0;
|
||||
static uint32_t last_host_activity_ms = 0;
|
||||
static bool is_ready = false;
|
||||
static bool is_initialized = false;
|
||||
static bool is_report_queued = false;
|
||||
static bool report_sent = false;
|
||||
static uint8_t queued_report_id = 0;
|
||||
static bool forced_ready = false;
|
||||
static bool host_sent_out = false;
|
||||
static uint8_t handshake_counter = 0;
|
||||
|
||||
static SwitchDeviceInfo device_info{};
|
||||
|
|
@ -47,31 +50,31 @@ static const uint8_t factory_config_data[0xEFF] = {
|
|||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
|
||||
0xFF, 0xFF,
|
||||
0xFF, 0xFF,
|
||||
|
||||
// device type
|
||||
SWITCH_TYPE_PRO_CONTROLLER,
|
||||
SWITCH_TYPE_PRO_CONTROLLER,
|
||||
|
||||
// unknown
|
||||
0xA0,
|
||||
0xA0,
|
||||
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
|
||||
// color options
|
||||
0x02,
|
||||
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF,
|
||||
|
||||
// config & calibration 1
|
||||
0xE3, 0xFF, 0x39, 0xFF, 0xED, 0x01, 0x00, 0x40,
|
||||
0x00, 0x40, 0x00, 0x40, 0x09, 0x00, 0xEA, 0xFF,
|
||||
0xA1, 0xFF, 0x3B, 0x34, 0x3B, 0x34, 0x3B, 0x34,
|
||||
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
|
||||
// config & calibration 2
|
||||
// left stick
|
||||
0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46,
|
||||
0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80, 0xa4, 0x46,
|
||||
0x6a,
|
||||
|
||||
// right stick
|
||||
|
|
@ -92,32 +95,32 @@ static const uint8_t factory_config_data[0xEFF] = {
|
|||
// right grip color
|
||||
0xEC, 0x00, 0x8C,
|
||||
|
||||
0x01,
|
||||
0x01,
|
||||
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF,
|
||||
|
||||
0x50, 0xFD, 0x00, 0x00, 0xC6, 0x0F,
|
||||
0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14,
|
||||
0x54, 0x41, 0x15, 0x54, 0xC7, 0x79, 0x9C, 0x33,
|
||||
0x36, 0x63,
|
||||
|
||||
0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14,
|
||||
|
||||
0x50, 0xFD, 0x00, 0x00, 0xC6, 0x0F,
|
||||
0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14,
|
||||
0x54, 0x41, 0x15, 0x54, 0xC7, 0x79, 0x9C, 0x33,
|
||||
0x36, 0x63,
|
||||
|
||||
0x0F, 0x30, 0x61, 0xAE, 0x90, 0xD9, 0xD4, 0x14,
|
||||
0x54, 0x41, 0x15, 0x54,
|
||||
|
||||
|
||||
0xC7,
|
||||
|
||||
0x79,
|
||||
|
||||
0x9C,
|
||||
0x79,
|
||||
|
||||
0x9C,
|
||||
|
||||
0x33,
|
||||
|
||||
0x36,
|
||||
|
||||
0x33,
|
||||
|
||||
0x36,
|
||||
|
||||
0x63, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
|
@ -125,9 +128,9 @@ static const uint8_t factory_config_data[0xEFF] = {
|
|||
static const uint8_t user_calibration_data[0x3F] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
|
||||
|
||||
// Left Stick
|
||||
0xB2, 0xA1, 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80,
|
||||
0xB2, 0xA1, 0xa4, 0x46, 0x6a, 0x00, 0x08, 0x80,
|
||||
0xa4, 0x46, 0x6a,
|
||||
|
||||
// Right Stick
|
||||
|
|
@ -176,6 +179,9 @@ static bool send_report(uint8_t reportID, const void* reportData, uint16_t repor
|
|||
} else {
|
||||
last_report_counter = 0;
|
||||
}
|
||||
if (!result) {
|
||||
printf("[HID] send_report failed id=%u len=%u\n", reportID, reportLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -194,21 +200,26 @@ static void read_spi_flash(uint8_t* dest, uint32_t address, uint8_t size) {
|
|||
|
||||
static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSubID, const uint8_t *reportData, uint16_t reportLength) {
|
||||
bool canSend = false;
|
||||
last_host_activity_ms = to_ms_since_boot(get_absolute_time());
|
||||
host_sent_out = true;
|
||||
|
||||
switch (switchReportSubID) {
|
||||
case IDENTIFY:
|
||||
send_identify();
|
||||
canSend = true;
|
||||
printf("[HID] CONFIG IDENTIFY\n");
|
||||
break;
|
||||
case HANDSHAKE:
|
||||
report_buffer[0] = REPORT_USB_INPUT_81;
|
||||
report_buffer[1] = HANDSHAKE;
|
||||
canSend = true;
|
||||
printf("[HID] CONFIG HANDSHAKE\n");
|
||||
break;
|
||||
case BAUD_RATE:
|
||||
report_buffer[0] = REPORT_USB_INPUT_81;
|
||||
report_buffer[1] = BAUD_RATE;
|
||||
canSend = true;
|
||||
printf("[HID] CONFIG BAUD_RATE\n");
|
||||
break;
|
||||
case DISABLE_USB_TIMEOUT:
|
||||
report_buffer[0] = REPORT_OUTPUT_30;
|
||||
|
|
@ -219,16 +230,19 @@ static void handle_config_report(uint8_t switchReportID, uint8_t switchReportSub
|
|||
is_ready = true;
|
||||
//}
|
||||
canSend = true;
|
||||
printf("[HID] CONFIG DISABLE_USB_TIMEOUT -> ready\n");
|
||||
break;
|
||||
case ENABLE_USB_TIMEOUT:
|
||||
report_buffer[0] = REPORT_OUTPUT_30;
|
||||
report_buffer[1] = switchReportSubID;
|
||||
canSend = true;
|
||||
printf("[HID] CONFIG ENABLE_USB_TIMEOUT\n");
|
||||
break;
|
||||
default:
|
||||
report_buffer[0] = REPORT_OUTPUT_30;
|
||||
report_buffer[1] = switchReportSubID;
|
||||
canSend = true;
|
||||
printf("[HID] CONFIG unknown subid=0x%02x\n", switchReportSubID);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -240,6 +254,8 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu
|
|||
uint32_t spiReadAddress = 0;
|
||||
uint8_t spiReadSize = 0;
|
||||
bool canSend = false;
|
||||
last_host_activity_ms = to_ms_since_boot(get_absolute_time());
|
||||
host_sent_out = true;
|
||||
|
||||
report_buffer[0] = REPORT_OUTPUT_21;
|
||||
report_buffer[1] = last_report_counter;
|
||||
|
|
@ -251,18 +267,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu
|
|||
report_buffer[14] = commandID;
|
||||
report_buffer[15] = 0x03;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE GET_CONTROLLER_STATE\n");
|
||||
break;
|
||||
case BLUETOOTH_PAIR_REQUEST:
|
||||
report_buffer[13] = 0x81;
|
||||
report_buffer[14] = commandID;
|
||||
report_buffer[15] = 0x03;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE BLUETOOTH_PAIR_REQUEST\n");
|
||||
break;
|
||||
case REQUEST_DEVICE_INFO:
|
||||
report_buffer[13] = 0x82;
|
||||
report_buffer[14] = 0x02;
|
||||
memcpy(&report_buffer[15], &device_info, sizeof(device_info));
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE REQUEST_DEVICE_INFO\n");
|
||||
break;
|
||||
case SET_MODE:
|
||||
input_mode = reportData[11];
|
||||
|
|
@ -270,16 +289,19 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu
|
|||
report_buffer[14] = 0x03;
|
||||
report_buffer[15] = input_mode;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE SET_MODE 0x%02x\n", input_mode);
|
||||
break;
|
||||
case TRIGGER_BUTTONS:
|
||||
report_buffer[13] = 0x83;
|
||||
report_buffer[14] = 0x04;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE TRIGGER_BUTTONS\n");
|
||||
break;
|
||||
case SET_SHIPMENT:
|
||||
report_buffer[13] = 0x80;
|
||||
report_buffer[14] = commandID;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE SET_SHIPMENT\n");
|
||||
break;
|
||||
case SPI_READ:
|
||||
spiReadAddress = (reportData[14] << 24) | (reportData[13] << 16) | (reportData[12] << 8) | (reportData[11]);
|
||||
|
|
@ -293,22 +315,26 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu
|
|||
report_buffer[19] = reportData[15];
|
||||
read_spi_flash(&report_buffer[20], spiReadAddress, spiReadSize);
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE SPI_READ addr=0x%08lx size=%u\n", (unsigned long)spiReadAddress, spiReadSize);
|
||||
break;
|
||||
case SET_NFC_IR_CONFIG:
|
||||
report_buffer[13] = 0x80;
|
||||
report_buffer[14] = commandID;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE SET_NFC_IR_CONFIG\n");
|
||||
break;
|
||||
case SET_NFC_IR_STATE:
|
||||
report_buffer[13] = 0x80;
|
||||
report_buffer[14] = commandID;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE SET_NFC_IR_STATE\n");
|
||||
break;
|
||||
case SET_PLAYER_LIGHTS:
|
||||
player_id = reportData[11];
|
||||
report_buffer[13] = 0x80;
|
||||
report_buffer[14] = commandID;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE SET_PLAYER_LIGHTS player=%u\n", player_id);
|
||||
break;
|
||||
case GET_PLAYER_LIGHTS:
|
||||
player_id = reportData[11];
|
||||
|
|
@ -316,18 +342,21 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu
|
|||
report_buffer[14] = commandID;
|
||||
report_buffer[15] = player_id;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE GET_PLAYER_LIGHTS player=%u\n", player_id);
|
||||
break;
|
||||
case COMMAND_UNKNOWN_33:
|
||||
report_buffer[13] = 0x80;
|
||||
report_buffer[14] = commandID;
|
||||
report_buffer[15] = 0x03;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE COMMAND_UNKNOWN_33\n");
|
||||
break;
|
||||
case SET_HOME_LIGHT:
|
||||
report_buffer[13] = 0x80;
|
||||
report_buffer[14] = commandID;
|
||||
report_buffer[15] = 0x00;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE SET_HOME_LIGHT\n");
|
||||
break;
|
||||
case TOGGLE_IMU:
|
||||
is_imu_enabled = reportData[11];
|
||||
|
|
@ -335,11 +364,13 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu
|
|||
report_buffer[14] = commandID;
|
||||
report_buffer[15] = 0x00;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE TOGGLE_IMU %u\n", is_imu_enabled);
|
||||
break;
|
||||
case IMU_SENSITIVITY:
|
||||
report_buffer[13] = 0x80;
|
||||
report_buffer[14] = commandID;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE IMU_SENSITIVITY\n");
|
||||
break;
|
||||
case ENABLE_VIBRATION:
|
||||
is_vibration_enabled = reportData[11];
|
||||
|
|
@ -347,6 +378,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu
|
|||
report_buffer[14] = commandID;
|
||||
report_buffer[15] = 0x00;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE ENABLE_VIBRATION %u\n", is_vibration_enabled);
|
||||
break;
|
||||
case READ_IMU:
|
||||
report_buffer[13] = 0xC0;
|
||||
|
|
@ -354,6 +386,7 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu
|
|||
report_buffer[15] = reportData[11];
|
||||
report_buffer[16] = reportData[12];
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE READ_IMU addr=%u size=%u\n", reportData[11], reportData[12]);
|
||||
break;
|
||||
case GET_VOLTAGE:
|
||||
report_buffer[13] = 0xD0;
|
||||
|
|
@ -361,12 +394,14 @@ static void handle_feature_report(uint8_t switchReportID, uint8_t switchReportSu
|
|||
report_buffer[15] = 0x83;
|
||||
report_buffer[16] = 0x06;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE GET_VOLTAGE\n");
|
||||
break;
|
||||
default:
|
||||
report_buffer[13] = 0x80;
|
||||
report_buffer[14] = commandID;
|
||||
report_buffer[15] = 0x03;
|
||||
canSend = true;
|
||||
printf("[HID] FEATURE unknown cmd=0x%02x\n", commandID);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -404,7 +439,7 @@ static void update_switch_report_from_state() {
|
|||
uint16_t scaleLeftStickY = scale16To12(g_input_state.ly);
|
||||
uint16_t scaleRightStickX = scale16To12(g_input_state.rx);
|
||||
uint16_t scaleRightStickY = scale16To12(g_input_state.ry);
|
||||
|
||||
|
||||
switch_report.inputs.leftStick.setX(std::min(std::max(scaleLeftStickX,leftMinX), leftMaxX));
|
||||
switch_report.inputs.leftStick.setY(-std::min(std::max(scaleLeftStickY,leftMinY), leftMaxY));
|
||||
switch_report.inputs.rightStick.setX(std::min(std::max(scaleRightStickX,rightMinX), rightMaxX));
|
||||
|
|
@ -421,6 +456,8 @@ void switch_pro_init() {
|
|||
is_initialized = false;
|
||||
is_report_queued = false;
|
||||
report_sent = false;
|
||||
forced_ready = false;
|
||||
host_sent_out = false;
|
||||
|
||||
device_info = {
|
||||
.majorVersion = 0x04,
|
||||
|
|
@ -437,8 +474,8 @@ void switch_pro_init() {
|
|||
.timestamp = 0,
|
||||
|
||||
.inputs {
|
||||
.connectionInfo = 0,
|
||||
.batteryLevel = 0x08,
|
||||
.connectionInfo = 0x08, // wired connection
|
||||
.batteryLevel = 0x0F, // full battery
|
||||
|
||||
.buttonY = 0,
|
||||
.buttonX = 0,
|
||||
|
|
@ -475,6 +512,7 @@ void switch_pro_init() {
|
|||
};
|
||||
|
||||
last_report_timer = to_ms_since_boot(get_absolute_time());
|
||||
last_host_activity_ms = last_report_timer;
|
||||
|
||||
factory_config->leftStickCalibration.getRealMin(leftMinX, leftMinY);
|
||||
factory_config->leftStickCalibration.getCenter(leftCenX, leftCenY);
|
||||
|
|
@ -508,7 +546,8 @@ void switch_pro_task() {
|
|||
report_sent = true;
|
||||
}
|
||||
|
||||
if (is_ready && !report_sent) {
|
||||
// If the host never sends feature/config reports (seen on Switch), force readiness after a timeout.
|
||||
if (is_ready && !report_sent && host_sent_out) {
|
||||
if ((now - last_report_timer) > SWITCH_PRO_KEEPALIVE_TIMER) {
|
||||
switch_report.timestamp = last_report_counter;
|
||||
void * inputReport = &switch_report;
|
||||
|
|
@ -535,7 +574,7 @@ void switch_pro_task() {
|
|||
}
|
||||
}
|
||||
|
||||
bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length) {
|
||||
bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchInputState* out_state) {
|
||||
// Packet format: 0xAA, buttons(2 LE), hat, lx, ly, rx, ry
|
||||
if (length < 8 || packet[0] != 0xAA) {
|
||||
return false;
|
||||
|
|
@ -587,18 +626,33 @@ bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length) {
|
|||
state.rx = expand_axis(out.rx);
|
||||
state.ry = expand_axis(out.ry);
|
||||
|
||||
switch_pro_set_input(state);
|
||||
if (out_state) {
|
||||
*out_state = state;
|
||||
} else {
|
||||
switch_pro_set_input(state);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool switch_pro_is_ready() {
|
||||
return is_ready;
|
||||
}
|
||||
|
||||
void switch_pro_mark_host_active() {
|
||||
host_sent_out = true;
|
||||
}
|
||||
|
||||
// HID callbacks
|
||||
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) {
|
||||
(void)instance;
|
||||
(void)report_id;
|
||||
(void)report_type;
|
||||
(void)buffer;
|
||||
(void)reqlen;
|
||||
return 0;
|
||||
printf("[HID] get_report id=%u type=%u len=%u\n", report_id, report_type, reqlen);
|
||||
if (!buffer) return 0;
|
||||
|
||||
// Serve the current input report for any GET_REPORT request.
|
||||
uint16_t report_size = sizeof(switch_report);
|
||||
if (reqlen < report_size) report_size = reqlen;
|
||||
memcpy(buffer, &switch_report, report_size);
|
||||
return report_size;
|
||||
}
|
||||
|
||||
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {
|
||||
|
|
@ -609,7 +663,12 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
|
|||
|
||||
uint8_t switchReportID = buffer[0];
|
||||
uint8_t switchReportSubID = buffer[1];
|
||||
printf("[HID] set_report type=%d id=%u switchRID=0x%02x sub=0x%02x len=%u\n",
|
||||
report_type, report_id, switchReportID, switchReportSubID, bufsize);
|
||||
host_sent_out = true;
|
||||
if (switchReportID == REPORT_OUTPUT_00) {
|
||||
// No-op, just acknowledge to clear any stalls.
|
||||
return;
|
||||
} else if (switchReportID == REPORT_FEATURE) {
|
||||
queued_report_id = report_id;
|
||||
handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize);
|
||||
|
|
@ -620,6 +679,26 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
|
|||
}
|
||||
}
|
||||
|
||||
void tud_hid_report_received_cb(uint8_t instance, uint8_t report_id, uint8_t const* buffer, uint16_t bufsize) {
|
||||
(void)instance;
|
||||
// Host sent data on interrupt OUT; mirror the control path handling.
|
||||
memset(report_buffer, 0x00, bufsize);
|
||||
uint8_t switchReportID = buffer[0];
|
||||
uint8_t switchReportSubID = buffer[1];
|
||||
printf("[HID] report_received id=%u switchRID=0x%02x sub=0x%02x len=%u\n",
|
||||
report_id, switchReportID, switchReportSubID, bufsize);
|
||||
host_sent_out = true;
|
||||
if (switchReportID == REPORT_OUTPUT_00) {
|
||||
return;
|
||||
} else if (switchReportID == REPORT_FEATURE) {
|
||||
queued_report_id = report_id;
|
||||
handle_feature_report(switchReportID, switchReportSubID, buffer, bufsize);
|
||||
} else if (switchReportID == REPORT_CONFIGURATION) {
|
||||
queued_report_id = report_id;
|
||||
handle_config_report(switchReportID, switchReportSubID, buffer, bufsize);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) {
|
||||
(void)itf;
|
||||
return switch_pro_report_descriptor;
|
||||
|
|
@ -634,6 +713,30 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
|
|||
return switch_pro_configuration_descriptor;
|
||||
}
|
||||
|
||||
bool tud_control_request_cb(uint8_t rhport, tusb_control_request_t const * request) {
|
||||
(void)rhport;
|
||||
printf("[CTRL] bmReq=0x%02x bReq=0x%02x wValue=0x%04x wIndex=0x%04x wLen=%u\n",
|
||||
request->bmRequestType, request->bRequest, request->wValue, request->wIndex, request->wLength);
|
||||
return false; // let TinyUSB handle it normally
|
||||
}
|
||||
|
||||
void tud_mount_cb(void) {
|
||||
printf("[USB] mount_cb\n");
|
||||
last_host_activity_ms = to_ms_since_boot(get_absolute_time());
|
||||
forced_ready = false;
|
||||
is_ready = false;
|
||||
is_initialized = false;
|
||||
host_sent_out = false;
|
||||
}
|
||||
|
||||
void tud_umount_cb(void) {
|
||||
printf("[USB] umount_cb\n");
|
||||
forced_ready = false;
|
||||
is_ready = false;
|
||||
is_initialized = false;
|
||||
host_sent_out = false;
|
||||
}
|
||||
|
||||
static uint16_t desc_str[32];
|
||||
|
||||
uint16_t const * tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||
|
|
|
|||
|
|
@ -47,4 +47,11 @@ void switch_pro_set_input(const SwitchInputState& state);
|
|||
void switch_pro_task();
|
||||
|
||||
// Convert a packed UART message into controller state (returns true if parsed).
|
||||
bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length);
|
||||
// If out_state is null the parsed state is written directly to the driver.
|
||||
bool switch_pro_apply_uart_packet(const uint8_t* packet, uint8_t length, SwitchInputState* out_state = nullptr);
|
||||
|
||||
// Driver state helpers
|
||||
bool switch_pro_is_ready();
|
||||
|
||||
// Mark that the host has sent any OUT traffic (allows starting IN reports).
|
||||
void switch_pro_mark_host_active();
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ extern "C" {
|
|||
#define CFG_TUD_MSC 0
|
||||
#define CFG_TUD_MIDI 0
|
||||
#define CFG_TUD_VENDOR 0
|
||||
#ifdef CFG_TUSB_DEBUG
|
||||
#undef CFG_TUSB_DEBUG
|
||||
#endif
|
||||
#define CFG_TUSB_DEBUG 2
|
||||
|
||||
#define CFG_TUD_HID_EP_BUFSIZE 64
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue