Skip to content

Commit

Permalink
WIP: USB-PD VDM for Apple devices, add debug shell over UART
Browse files Browse the repository at this point in the history
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
  • Loading branch information
noahwilliamsson committed Feb 1, 2021
1 parent c24b232 commit 3333575
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/extensions.json
.vscode/launch.json
.vscode/ipch
Artwork/
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
10 changes: 10 additions & 0 deletions include/pd_sink.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_; }

Expand Down
6 changes: 4 additions & 2 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions src/fusb302.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
62 changes: 55 additions & 7 deletions src/pd_debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

#if defined(PD_DEBUG)

#include "pd_sink.h"
extern usb_pd::pd_sink power_sink;

#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/stm32/gpio.h>
Expand Down Expand Up @@ -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)
Expand All @@ -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);
Expand All @@ -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 <sop|sop1|sop2|debug1|debug2> (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 <action ID> (send 0x11 Get Action Info)\r\n", 0);
DEBUG_LOG(" aaexec <action ID> [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;
}
Expand Down
99 changes: 95 additions & 4 deletions src/pd_sink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 3333575

Please sign in to comment.