From 9d7e4940e9dd8cfb53adeda65f969f6262601fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Sun, 1 Mar 2020 23:22:49 +0100 Subject: [PATCH] application: add eMrker read out --- application.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 188 insertions(+), 12 deletions(-) diff --git a/application.c b/application.c index 7291e15..360146b 100644 --- a/application.c +++ b/application.c @@ -50,6 +50,9 @@ #include "oled_text.h" // OLED utilities to display text #include "usb_fusb302.h" // USB-C controller utilities +/* external library */ +#include "usb_pd.h" // USB Power Delivery definitions + /** watchdog period in ms */ #define WATCHDOG_PERIOD 10000 @@ -1235,6 +1238,94 @@ static void command_connections(void* argument) } } +/** print discovery identity response (e.g. eMarker information) + * param[in] packet FUSB302 packet containing VDM information + */ +static void print_emarker(uint8_t* packet) +{ + const uint16_t header = (packet[2] << 8) + packet[1]; + if (0 == PD_HEADER_CNT(header)) { // it's a control packet, not a data packet + return; + } + if (PD_DATA_VENDOR_DEF != PD_HEADER_TYPE(header)) { // not a VDM message + return; + } + + // get objects + uint32_t objects[7]; // maximum message length + for (uint8_t i = 0; i < PD_HEADER_CNT(header); i++) { + objects[i] = (packet[i * 4 + 3 + 3] << 24) + (packet[i * 4 + 3 + 2] << 16) + (packet[i * 4 + 3 + 1] << 8) + (packet[i * 4 + 3 + 0] << 0); + } + + puts("cable eMarker attributes:\n"); + // decode some messages + const uint32_t vdm_header = objects[0]; + if (vdm_header & VDO_SVDM_TYPE && VDO_CMDT(CMDT_RSP_ACK) == (vdm_header & VDO_CMDT_MASK) && CMD_DISCOVER_IDENT == (vdm_header & 0x1f)) { // we have a discover identity response + if (PD_HEADER_CNT(header) > 1) { // ID Header VDO (6.4.4.3.1.1) + //printf("- host %s enumerate\n", (objects[1] & (1 << 31)) ? "can" : "can't"); + //printf("- device %s be enumerated\n", (objects[1] & (1 << 30)) ? "can" : "can't"); + if (PD_HEADER_PROLE(header)) { // this is a plug + switch (PD_IDH_PTYPE(objects[1])) { + case 3: + puts("- cable: passive\n"); + break; + case 4: + puts("- cable: active\n"); + break; + default: + break; + } + } + printf("- VID: %04x\n", PD_IDH_VID(objects[1])); + } + if (PD_HEADER_CNT(header) > 2) { // Cert Stat VDO (6.4.4.3.1.2) + //printf("- cert stat: %08x\n", objects[2]); + } + if (PD_HEADER_CNT(header) > 3) { // Product VDO (6.4.4.3.1.3) + //printf("- product ID: %04x, bcdDevice: %04x\n", objects[3] >> 16, objects[3] & 0xffff); + } + if (PD_HEADER_CNT(header) > 3 && PD_HEADER_PROLE(header) && 3 == PD_IDH_PTYPE(objects[1])) { // Passive Cable VDO (6.4.4.3.1.4) + union cable_vdo cable; + memset(&cable, 0, sizeof(cable)); + cable.raw_value = objects[4]; + printf("- HW version: %u\n", cable.p_rev30.hw_version); + printf("- FW version: %u\n", cable.p_rev30.fw_version); + printf("- cable length: ~%um\n", ((cable.raw_value >> 13) & 0x7)); + printf("- VCONN: %srequired\n", ((cable.raw_value >> 11) & 0x3) ? "" : "not "); + printf("- maximum VBUS voltage: %uV\n", ((cable.raw_value >> 9) & 0x3) * 10 + 20); + puts("- maximum VBUS current: "); + switch (((cable.raw_value >> 5) & 0x3)) { + case 1: + putc('3'); + break; + case 2: + putc('5'); + break; + default: + putc('?'); + break; + } + puts("A\n"); + puts("- SuperSpeed: USB "); + switch (cable.p_rev30.ss) { + case 0: + puts("2.0"); + break; + case 1: + puts("3.2 Gen 1"); + break; + case 2: + puts("3.2 Gen 2"); + break; + default: + puts("???"); + break; + } + putc('\n'); + } + } +} + /** test USB-C plug * @param[in] argument no argument required */ @@ -1324,12 +1415,14 @@ static void command_cplug(void* argument) printf("- CC2 %sconnected to GND (", cc2 ? "" : "not "); print_connection(&cc2_connection); puts(")\n"); - if (cc1_connection.tx_drive_pull && cc1_connection.rx_pull_float && cc2_connection.tx_drive_pull && cc2_connection.rx_pull_float) { - puts("> powered cable to be connected to a sink on the other end (B, mini-B, or micro-B plug)\n"); - } else if ((cc1_connection.tx_drive_pull && cc1_connection.rx_pull_float) || (cc2_connection.tx_drive_pull && cc2_connection.rx_pull_float)) { - puts("> powered cable, or to be connected to a sink on the other end (B, mini-B, or micro-B plug)\n"); - } else { - puts("> unpowered cable\n"); + if (gnd_all || vbus_all || gnd_any || vbus_any) { + if (cc1_connection.tx_drive_pull && cc1_connection.rx_pull_float && cc2_connection.tx_drive_pull && cc2_connection.rx_pull_float) { + puts("> powered cable to be connected to a sink on the other end (B, mini-B, or micro-B plug)\n"); + } else if ((cc1_connection.tx_drive_pull && cc1_connection.rx_pull_float) || (cc2_connection.tx_drive_pull && cc2_connection.rx_pull_float)) { + puts("> powered cable, or to be connected to a sink on the other end (B, mini-B, or micro-B plug)\n"); + } else { + puts("> unpowered cable\n"); + } } // check if it should be connected to a source cc1 = usb_cables_test_pins(&usb_pins[connector->pins[5]], &usb_pins[connector->pins[4]], &cc1_connection); // A5 CC1 - A4 VBUS @@ -1340,10 +1433,12 @@ static void command_cplug(void* argument) printf("- CC2 %sconnected to VBUS (", cc2 ? "" : "not "); print_connection(&cc2_connection); puts(")\n"); - if (cc1 || cc2) { - puts("> to be connected to a source on the other end (A plug)\n"); - } else { - puts("> not to be connected to a source on the other end (A plug)\n"); + if (gnd_all || vbus_all || gnd_any || vbus_any) { + if (cc1 || cc2) { + puts("> to be connected to a source on the other end (A plug)\n"); + } else { + puts("> not to be connected to a source on the other end (A plug)\n"); + } } // find out if a resistor is present using USB-C controller if (present_fusb302 && USB_CONNECTOR_C_DEVICE == connectors[connector_id]) { // HW v1 (modified for a v2 prototype) has only a FUSB302 on the C device port @@ -1361,6 +1456,7 @@ static void command_cplug(void* argument) gpio_set_mode(pin.port, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, pin.pin); } // figure out resistors on CC lines + uint8_t cc_pd = 0; // line used for PD communication for (uint8_t cc = 1; cc < 3; cc++) { const char* r_values[] = {"short to ground", "Ra", "Rd", "Rp", "open"}; int16_t r = usb_fusb302_r(cc); @@ -1372,13 +1468,93 @@ static void command_cplug(void* argument) } putc('\n'); if (1 == r) { - printf("> powered cable (CC%u is VCONN)\n", (1 == cc) ? 2 : 1); + printf("> powered cable (CC%u is CC, CC%u is VCONN)\n", (1 == cc) ? 2 : 1, cc); + cc_pd = ((1 == cc) ? 2 : 1); // remember this is the CC line to communicate with eMarker } else if (2 == r) { puts("> to be connected to a sink on the other end (B, mini-B, or micro-B plug)\n"); } else if (3 == r) { puts("> to be connected to a source on the other end (A plug)\n"); } } + if (cc_pd) { // read cable eMarker + uint8_t stage = 0; + int16_t rc = usb_fusb302_pd(cc_pd); // configure CC channel for PD communication (also flushes data) + if (rc < 0) { + stage = 1; + goto pd_end; + } + rc = usb_fusb302_discover_identity_request(); // send discover identity request + if (rc < 0) { + stage = 2; + goto pd_end; + } + sleep_ms(2); // wait tReceive = 1.1 ms for goodCRC response (PD3.0 section 6.6.1) + bool expected_message = false; + uint8_t packet[35]; + while (!expected_message) { // wait got goodCRC + // I don't know why, after the first this action is called, the TX packet is also in the RX FIFO + rc = usb_fusb302_packet_read(packet); + if (0 == rc) { // RX FIFO is empty + stage = 3; + goto pd_end; + } else if (rc < 0) { + stage = 4; + goto pd_end; + } + if (6 != ((packet[0] >> 5) & 0x7)) { // not a SOP' response + continue; + } + uint16_t header = *(uint16_t*)(&packet[1]); + if (0 != PD_HEADER_CNT(header)) { // not a control message + continue; + } + if (PD_CTRL_GOOD_CRC != PD_HEADER_TYPE(header)) { // not a goodCRC message + continue; + } + expected_message = true; + } + if (!usb_fusb302_packet_checksum(packet)) { // verify CRC + stage = 5; + goto pd_end; + } + sleep_ms(15); // wait tReceiverResponse = 15 ms for request response (PD3.0 section 6.6.2) + expected_message = false; + while (!expected_message) { // wait got identity response + // I don't know why, after the first this action is called, the TX packet is also in the RX FIFO + rc = usb_fusb302_packet_read(packet); + if (0 == rc) { // RX FIFO is empty + stage = 6; + goto pd_end; + } else if (rc < 0) { + stage = 7; + goto pd_end; + } + if (6 != ((packet[0] >> 5) & 0x7)) { // not a SOP' response + continue; + } + uint16_t header = *(uint16_t*)(&packet[1]); + if (0 == PD_HEADER_CNT(header)) { // not a data message + continue; + } + if (PD_DATA_VENDOR_DEF != PD_HEADER_TYPE(header)) { // not a VDM message + continue; + } + expected_message = true; + } + if (!usb_fusb302_packet_checksum(packet)) { // verify CRC + stage = 8; + goto pd_end; + } +pd_end: + if (0 == stage) { + print_emarker(packet); + sleep_ms(2); // wait a bit for the goodCRC to be received by the eMarker + } else { + //puts("could not read eMarker\n"); + printf("could not read eMarker (stage=%u, error=%d)\n", stage, rc); + } + usb_fusb302_disconnect(); // remove all CC connections + } // put ground back to floating usb_cables_pins_float(); // not the most efficient way, but time is not critical } @@ -1390,7 +1566,7 @@ static void command_cplug(void* argument) if (NULL == c1 || NULL == c2) { return; } - puts("cable interconnection\n"); + puts("cable interconnection (C host to C device)\n"); struct usb_connection_t connection; bool connected; connected = usb_cables_test_pins(&usb_pins[c1->pins[5]], &usb_pins[c2->pins[5]], &connection); // A5 CC1 - A5 CC1