From 33335756f2734b5bc8b39f2ae8760e0d21110f4e Mon Sep 17 00:00:00 2001 From: Noah Date: Sat, 30 Jan 2021 00:34:11 +0100 Subject: [PATCH] WIP: USB-PD VDM for Apple devices, add debug shell over UART This is an attempt to take advantage of the FUSB302 chip on the YZStudio ZY12PDN USB-PD Trigger to send custom VDM messages to Apple devices. See [Asahi Linux docs on USB-PD](https://github.com/AsahiLinux/docs/wiki/HW:USB-PD). - solder wires onto the STM32F0 chip's PA2 (USART1_TX) and PA3 (USART1_RX) pins (3.3V I/O) - use an USB Type-C breakout board to gain access to the D+/D- or SBU1/SBU2 signals Building: sudo apt install -y python3 openocd screen pio run pio run -t upload --- .gitignore | 1 + README.md | 20 ++++++++++ include/pd_sink.h | 10 +++++ platformio.ini | 6 ++- src/fusb302.cpp | 2 + src/main.cpp | 2 +- src/pd_debug.cpp | 62 +++++++++++++++++++++++++---- src/pd_sink.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 188 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 816ac91..581af01 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .pio .vscode/.browse.c_cpp.db* .vscode/c_cpp_properties.json +.vscode/extensions.json .vscode/launch.json .vscode/ipch Artwork/ diff --git a/README.md b/README.md index 215ef7c..649d531 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,23 @@ +# Overview + +This is an attempt to take advantage of the FUSB302 chip on the YZStudio ZY12PDN USB-PD Trigger to send custom VDM messages to Apple devices. See [Asahi Linux docs on USB-PD](https://github.com/AsahiLinux/docs/wiki/HW:USB-PD). + +- program the STM32F0 via the SWD pads on the back of the ZY12PDN board +- solder wires onto the STM32F0 chip's PA2 (USART1_TX) and PA3 (USART1_RX) pins (3.3V I/O) to gain access to the UART (the screw terminal must be desoldered first) +- use an USB Type-C breakout board to gain access to the D+/D- or SBU1/SBU2 signals + +Building: + + sudo apt install -y python3 openocd screen + vi platformio.ini # optionally change upload_protocol + pio run + pio run -t upload + +NOTE: [platformio.ini](platformio.ini) is currently setup for a J-Link EDU Mini SWD programmer. + +Below is the original README from https://github.com/manuelbl/zy12pdn-oss (on which this code is based). + + # Open-Source Firmware for ZY12PDN USB-PD Open-source firmware for USB Power Delivery trigger board based on an FUSB302B power delivery controller and a STM32F030F4 MCU. diff --git a/include/pd_sink.h b/include/pd_sink.h index aa7a612..136b841 100644 --- a/include/pd_sink.h +++ b/include/pd_sink.h @@ -87,6 +87,16 @@ struct pd_sink { */ void request_power(int voltage, int max_current = 0); +#ifdef PD_VDM_APPLE + /** + * Apple VDM: Get actions list + * https://github.com/AsahiLinux/docs/wiki/HW:USB-PD#0x10-get-action-list + */ + void vdm_apple_get_actions_list(sop_type sop); + void vdm_apple_get_action_info(sop_type sop, uint16_t action); + void vdm_apple_perform_action(sop_type sop, uint16_t action, uint16_t flags); +#endif + /// Active power delivery protocol pd_protocol protocol() { return protocol_; } diff --git a/platformio.ini b/platformio.ini index f2f1318..455d0a6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -3,5 +3,7 @@ platform = ststm32 board = demo_f030f4 framework = libopencm3 ;build_flags = -D PD_DEBUG -upload_protocol = stlink -debug_tool = stlink +;build_flags = -D PD_VDM_APPLE +build_flags = -D PD_DEBUG -D PD_VDM_APPLE +upload_protocol = jlink +debug_tool = jlink diff --git a/src/fusb302.cpp b/src/fusb302.cpp index 2c71ecf..9bb4ab3 100644 --- a/src/fusb302.cpp +++ b/src/fusb302.cpp @@ -52,6 +52,8 @@ void fusb302::init() write_register(reg::maska, *reg_maska::m_all); // Mask all interrupts (incl. good CRC sent) write_register(reg::maskb, *reg_maskb::m_all); + // Flush RX FIFO, enable SOP_DEBUG' and SOP_DEBUG'' packets + write_register(reg::control1, *(reg_control1::rx_flush | reg_control1::ensop1db | reg_control1::ensop2db)); next_message_id = 0; is_timeout_active = false; diff --git a/src/main.cpp b/src/main.cpp index e5d6dc8..2e4480e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,7 +22,7 @@ constexpr uint16_t nvs_voltage_key = 0; mcu_hal usb_pd::hal; -static pd_sink power_sink; +pd_sink power_sink; static eeprom nvs; diff --git a/src/pd_debug.cpp b/src/pd_debug.cpp index 685b022..ccfb93f 100644 --- a/src/pd_debug.cpp +++ b/src/pd_debug.cpp @@ -12,6 +12,9 @@ #if defined(PD_DEBUG) +#include "pd_sink.h" +extern usb_pd::pd_sink power_sink; + #include #include #include @@ -81,9 +84,10 @@ static uint8_t uart_read(uint8_t *buf, uint8_t len) void debug_uart_loop(void) { - static uint8_t data[64], dlen; - uint8_t len; - int i; + static sop_type sop = sop_type::SOP_TYPE_SOP_DEBUG1; + static uint8_t data[40], dlen; + uint8_t argc, i, j, len; + char *argv[6]; len = uart_available(); if (len == 0) @@ -96,7 +100,7 @@ void debug_uart_loop(void) restart: uart_transmit((const uint8_t *)"\r", 1); - for(i = 0; i < dlen; i++) { + for (i = 0; i < dlen; i++) { if (data[i] == 0x08 || data[i] == 0x7f) { if (i == 0) { memmove(data, &data[i+1], dlen - i - 1); @@ -119,15 +123,59 @@ void debug_uart_loop(void) uart_transmit((const uint8_t *)&data[i], 1); continue; } + data[i] = 0; uart_transmit((const uint8_t *)"\r\n", 2); - data[i] = 0; - dlen -= i + 1; + argc = 0; + argv[argc++] = (char *)data; + for (j = 0; j < i; j++) { + if (((char *)data)[j] == ' ') { + data[j] = 0; + if (j + 1 < i) + argv[argc++] = (char *)&data[j+1]; + } + } /* process data[0:i] */ - // TODO + if (!strcmp(argv[0], "help")) { + DEBUG_LOG("Usage:\r\n", 0); + DEBUG_LOG(" sop (send with given SOP)\r\n", 0); +#ifdef PD_VDM_APPLE + DEBUG_LOG("Apple VDM: https://github.com/AsahiLinux/docs/wiki/HW:USB-PD\r\n", 0); + DEBUG_LOG(" aalist (send 0x10 Get Actions List)\r\n", 0); + DEBUG_LOG(" aainfo (send 0x11 Get Action Info)\r\n", 0); + DEBUG_LOG(" aaexec [flags] (send 0x12 Perform Action)\r\n", 0); + DEBUG_LOG(" aareboot (reboot via 0x12)\r\n", 0); +#endif + } +#ifdef PD_VDM_APPLE + else if(!strcmp(argv[0], "sop") && argc > 1) { + if (!strcmp(argv[1], "sop")) sop = sop_type::SOP_TYPE_SOP; + else if (!strcmp(argv[1], "sop1")) sop = sop_type::SOP_TYPE_SOP1; + else if (!strcmp(argv[1], "sop2")) sop = sop_type::SOP_TYPE_SOP2; + else if (!strcmp(argv[1], "debug1")) sop = sop_type::SOP_TYPE_SOP_DEBUG1; + else if (!strcmp(argv[1], "debug2")) sop = sop_type::SOP_TYPE_SOP_DEBUG2; + } + else if (!strcmp(argv[0], "aalist")) { + power_sink.vdm_apple_get_actions_list(sop); + } + else if (!strcmp(argv[0], "aainfo") && argc > 1) { + uint16_t action = strtol(argv[1], NULL, 16) & 0xffff; + DEBUG_LOG("Info with action: %04x\r\n", action); + power_sink.vdm_apple_get_action_info(sop, action); + } + else if (!strcmp(argv[0], "aaexec") && argc > 1) { + uint16_t action = strtol(argv[1], NULL, 16) & 0xffff; + uint16_t flags = argc > 2? strtol(argv[2], NULL, 16): 0; + power_sink.vdm_apple_perform_action(sop, action, flags); + } + else if (!strcmp(argv[0], "aareboot")) { + power_sink.vdm_apple_perform_action(sop, 0x105, 0x8000); + } +#endif /* move remaining data */ + dlen -= i + 1; memmove(data, &data[i+1], dlen); break; } diff --git a/src/pd_sink.cpp b/src/pd_sink.cpp index 1db73bf..ac6cff6 100644 --- a/src/pd_sink.cpp +++ b/src/pd_sink.cpp @@ -153,14 +153,67 @@ void pd_sink::handle_src_cap_msg(uint16_t header, const uint8_t* payload) void pd_sink::handle_vd_msg(uint16_t header, const uint8_t* payload) { pd_msg_type type = pd_header::message_type(header); - int n = pd_header::num_data_objs(header); - uint32_t *msg = (uint32_t *)payload, *data = msg; + size_t n = pd_header::num_data_objs(header); + uint32_t *data = (uint32_t *)payload; DEBUG_LOG("RX--> PD: data_vendor_defined header: 0x%04x\r\n", header); DEBUG_LOG("RX--> type: 0x%02x\r\n", (uint8_t)type); DEBUG_LOG("RX--> objs: 0x%02x\r\n", n); - for (int i = 0; i < n; i++) { - DEBUG_LOG("RX--> data: 0x%08x\r\n", *data++); + for (size_t i = 0; i < n; i++) { + DEBUG_LOG("RX--> data: 0x%08x\r\n", data[i]); + } + + if (data[0] == 0xff008001) { + uint32_t vdm[] = { + data[0] | 0x40 /*ACK*/, + + (1L<<30) | // USB Device + (0L<<27) | // UFP Product Type = Undefined + (0L<<26) | // No modal operation + (0L<<23) | // DFP Product Type = Undefined + 0x5acL, // USB VID = Apple + + 0L, // XID + + (0x0001L<<16) | // USB PID, + 0x100L // bcdDevice + }; + + DEBUG_LOG("RX--> discover identity\r\n", 0); + DEBUG_LOG(" PAYLOAD IN :", 0); + for (size_t i = 0; i < n * sizeof(data[0]); i++) { + DEBUG_LOG("%02x", payload[i]); + } + DEBUG_LOG("\r\n", 0); + + DEBUG_LOG(" PAYLOAD OUT:", 0); + for (size_t i = 0; i < sizeof(vdm); i++){ + DEBUG_LOG("%02x", ((uint8_t *)vdm)[i]); + } + DEBUG_LOG("\r\n", 0); + + // Send reply + uint16_t h = pd_header::create_data(pd_msg_type::data_vendor_defined, 4); + pd_controller.send_message(h, (uint8_t *)vdm); + } + else if((data[0] & 0x05ac8000) == 0x05ac8000) { + switch(data[0] & 0xfff) { + case 0x10 | 0x40: + for(size_t i = 1; i < n; i++) { + DEBUG_LOG(" ACTION 0x%04x:", data[i] & 0xffff); + DEBUG_LOG("\r\n", 0); + DEBUG_LOG(" ACTION 0x%04x:", (data[i]>>16) & 0xffff); + DEBUG_LOG("\r\n", 0); + } + break; + + default: + DEBUG_LOG("RX--> Unsupported Apple VDM: 0x%06x\r\n", data[0] & 0xfff); + break; + } + } + else { + DEBUG_LOG("RX--> Unsupported VDM: 0x%06x\r\n", data[0] & 0xfff); } } @@ -219,6 +272,44 @@ void pd_sink::request_power(int voltage, int max_current) requested_max_current = max_current; } +#ifdef PD_VDM_APPLE +void pd_sink::vdm_apple_get_actions_list(sop_type sop) { + uint16_t header = pd_header::create_data(pd_msg_type::data_vendor_defined, 1); + uint32_t vdm[] = { + 0x05ac8000 | 0x10 + }; + + DEBUG_LOG("TX--> Apple VDM 0x10: 0x%08x\r\n", vdm[0]); + pd_controller.send_message(sop, header, (uint8_t *)vdm); +} + +void pd_sink::vdm_apple_get_action_info(sop_type sop, uint16_t action) { + uint16_t header = pd_header::create_data(pd_msg_type::data_vendor_defined, 2); + uint32_t vdm[] = { + 0x05ac8000 | 0x11, + (uint32_t)action, + }; + + DEBUG_LOG("TX--> Apple VDM 0x11: 0x%08x\r\n", vdm[0]); + DEBUG_LOG("TX--> action: 0x%04x\r\n", action); + pd_controller.send_message(sop, header, (uint8_t *)vdm); +} + +void pd_sink::vdm_apple_perform_action(sop_type sop, uint16_t action, uint16_t flags) { + uint16_t header = pd_header::create_data(pd_msg_type::data_vendor_defined, 3); + uint32_t vdm[] = { + 0x05ac8000 | 0x12, + (uint32_t)action, + (uint32_t)(flags << 16) | 0x0000 /* zero terminate args list */ + }; + + DEBUG_LOG("TX--> Apple VDM 0x12: 0x%08x\r\n", vdm[0]); + DEBUG_LOG("TX--> action: 0x%04x\r\n", action); + DEBUG_LOG("TX--> flags: 0x%04x\r\n", flags); + pd_controller.send_message(sop, header, (uint8_t *)vdm); +} +#endif + void pd_sink::notify(callback_event event) { if (event_callback_ == nullptr)