From 6941a5af81b53cd0df52632d403cccdb7d22cf30 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 12 Jun 2023 16:42:27 +0700 Subject: [PATCH] wrap up initial PD support for G4 --- examples/typec/power_delivery/src/main.c | 90 +++++++++++- src/typec/tcd.h | 15 +- src/typec/utcd.c | 180 ++++++++++------------- src/typec/utcd.h | 20 +++ 4 files changed, 196 insertions(+), 109 deletions(-) diff --git a/examples/typec/power_delivery/src/main.c b/examples/typec/power_delivery/src/main.c index 648345a55..42665ed1b 100644 --- a/examples/typec/power_delivery/src/main.c +++ b/examples/typec/power_delivery/src/main.c @@ -62,7 +62,8 @@ int main(void) while (1) { led_blinking_task(); -// tuc_task(); + // tinyusb typec task + tuc_task(); } } @@ -73,6 +74,93 @@ void app_main(void) } #endif +//--------------------------------------------------------------------+ +// TypeC PD callbacks +//--------------------------------------------------------------------+ + +bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end) { + switch (header->msg_type) { + case PD_DATA_SOURCE_CAP: { + printf("PD Source Capabilities\r\n"); + // Examine source capability and select a suitable PDO (starting from 1 with safe5v) + uint8_t obj_pos = 1; + + for(size_t i=0; in_data_obj; i++) { + TU_VERIFY(dobj < p_end); + uint32_t const pdo = tu_le32toh(tu_unaligned_read32(dobj)); + + switch ((pdo >> 30) & 0x03ul) { + case PD_PDO_TYPE_FIXED: { + pd_pdo_fixed_t const* fixed = (pd_pdo_fixed_t const*) &pdo; + printf("[Fixed] %u mV %u mA\r\n", fixed->voltage_50mv*50, fixed->current_max_10ma*10); + break; + } + + case PD_PDO_TYPE_BATTERY: + break; + + case PD_PDO_TYPE_VARIABLE: + break; + + case PD_PDO_TYPE_APDO: + break; + } + + dobj += 4; + } + + //------------- Response with selected PDO -------------// + // Be careful and make sure your board can withstand the selected PDO voltage other than safe5v e.g 12v or 20v + + // Send request with selected PDO position as response to Source Cap + pd_rdo_fixed_variable_t rdo = { + .current_extremum_10ma = 50, // max 500mA + .current_operate_10ma = 30, // 300mA + .reserved = 0, + .epr_mode_capable = 0, + .unchunked_ext_msg_support = 0, + .no_usb_suspend = 0, + .usb_comm_capable = 1, + .capability_mismatch = 0, + .give_back_flag = 0, // exteremum is max + .object_position = obj_pos, + }; + + tuc_msg_request(rhport, &rdo); + + break; + } + + default: break; + } + + return true; +} + +bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header) { + switch (header->msg_type) { + case PD_CTRL_ACCEPT: + printf("PD Request Accepted\r\n"); + // preparing for power transition + break; + + case PD_CTRL_REJECT: + printf("PD Request Rejected\r\n"); + // try to negotiate further power + break; + + case PD_CTRL_PS_READY: + printf("PD Power Ready\r\n"); + // Source is ready to supply power + break; + + default: + break; + } + + return true; +} + //--------------------------------------------------------------------+ // BLINKING TASK //--------------------------------------------------------------------+ diff --git a/src/typec/tcd.h b/src/typec/tcd.h index ee73943bd..86499c951 100644 --- a/src/typec/tcd.h +++ b/src/typec/tcd.h @@ -48,8 +48,7 @@ enum { TCD_EVENT_TX_COMPLETE, }; - -typedef struct { +typedef struct TU_ATTR_PACKED { uint8_t rhport; uint8_t event_id; @@ -58,10 +57,10 @@ typedef struct { uint8_t cc_state[2]; } cc_changed; - struct { - uint16_t xferred_bytes; - uint8_t result; - } rx_complete; + struct TU_ATTR_PACKED { + uint16_t result : 2; + uint16_t xferred_bytes : 14; + } xfer_complete; }; } tcd_event_t;; @@ -114,7 +113,7 @@ void tcd_event_rx_complete(uint8_t rhport, uint16_t xferred_bytes, uint8_t resul tcd_event_t event = { .rhport = rhport, .event_id = TCD_EVENT_RX_COMPLETE, - .rx_complete = { + .xfer_complete = { .xferred_bytes = xferred_bytes, .result = result } @@ -128,7 +127,7 @@ void tcd_event_tx_complete(uint8_t rhport, uint16_t xferred_bytes, uint8_t resul tcd_event_t event = { .rhport = rhport, .event_id = TCD_EVENT_TX_COMPLETE, - .rx_complete = { + .xfer_complete = { .xferred_bytes = xferred_bytes, .result = result } diff --git a/src/typec/utcd.c b/src/typec/utcd.c index 439fe89b9..831df1fe8 100644 --- a/src/typec/utcd.c +++ b/src/typec/utcd.c @@ -52,8 +52,12 @@ static bool _utcd_inited = false; static bool _port_inited[TUP_TYPEC_RHPORTS_NUM]; // Max possible PD size is 262 bytes -static uint8_t _rx_buf[262] TU_ATTR_ALIGNED(4); -static uint8_t _tx_buf[100] TU_ATTR_ALIGNED(4); +static uint8_t _rx_buf[64] TU_ATTR_ALIGNED(4); +static uint8_t _tx_buf[64] TU_ATTR_ALIGNED(4); + +bool utcd_msg_send(uint8_t rhport, pd_header_t const* header, void const* data); +bool parse_msg_data(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end); +bool parse_msg_control(uint8_t rhport, pd_header_t const* header); //--------------------------------------------------------------------+ // @@ -79,6 +83,7 @@ bool tuc_init(uint8_t rhport, uint32_t port_type) { } TU_LOG_UTCD("UTCD init on port %u\r\n", rhport); + TU_LOG_INT(UTCD_DEBUG, sizeof(tcd_event_t)); TU_ASSERT(tcd_init(rhport, port_type)); tcd_int_enable(rhport); @@ -87,6 +92,65 @@ bool tuc_init(uint8_t rhport, uint32_t port_type) { return true; } +void tuc_task_ext(uint32_t timeout_ms, bool in_isr) { + (void) in_isr; // not implemented yet + + // Skip if stack is not initialized + if (!_utcd_inited) return; + + // Loop until there is no more events in the queue + while (1) { + tcd_event_t event; + if (!osal_queue_receive(_utcd_q, &event, timeout_ms)) return; + + switch (event.event_id) { + case TCD_EVENT_CC_CHANGED: + break; + + case TCD_EVENT_RX_COMPLETE: + // TODO process message here in ISR, move to thread later + if (event.xfer_complete.result == XFER_RESULT_SUCCESS) { + pd_header_t const* header = (pd_header_t const*) _rx_buf; + + if (header->n_data_obj == 0) { + parse_msg_control(event.rhport, header); + + }else { + uint8_t const* p_end = _rx_buf + event.xfer_complete.xferred_bytes; + uint8_t const * dobj = _rx_buf + sizeof(pd_header_t); + + parse_msg_data(event.rhport, header, dobj, p_end); + } + } + + // prepare for next message + tcd_msg_receive(event.rhport, _rx_buf, sizeof(_rx_buf)); + break; + + case TCD_EVENT_TX_COMPLETE: + break; + + default: break; + } + } +} + +bool parse_msg_data(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end) { + if (tuc_pd_data_received_cb) { + tuc_pd_data_received_cb(rhport, header, dobj, p_end); + } + + return true; +} + +bool parse_msg_control(uint8_t rhport, pd_header_t const* header) { + if (tuc_pd_control_received_cb) { + tuc_pd_control_received_cb(rhport, header); + } + + return true; +} + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -104,94 +168,18 @@ bool utcd_msg_send(uint8_t rhport, pd_header_t const* header, void const* data) return tcd_msg_send(rhport, _tx_buf, sizeof(pd_header_t) + n_data_obj * 4); } -bool parse_message(uint8_t rhport, uint8_t const* buf, uint16_t len) { - (void) rhport; - uint8_t const* p_end = buf + len; - pd_header_t const* header = (pd_header_t const*) buf; - uint8_t const * ptr = buf + sizeof(pd_header_t); +bool tuc_msg_request(uint8_t rhport, void const* rdo) { + pd_header_t const header = { + .msg_type = PD_DATA_REQUEST, + .data_role = PD_DATA_ROLE_UFP, + .specs_rev = PD_REV_30, + .power_role = PD_POWER_ROLE_SINK, + .msg_id = 0, + .n_data_obj = 1, + .extended = 0, + }; - if (header->n_data_obj == 0) { - // control message - switch (header->msg_type) { - case PD_CTRL_GOOD_CRC: - break; - - case PD_CTRL_ACCEPT: - break; - - case PD_CTRL_REJECT: - break; - - case PD_CTRL_PS_READY: - break; - - default: break; - } - } else { - // data message - switch (header->msg_type) { - case PD_DATA_SOURCE_CAP: { - // Examine source capability and select a suitable PDO (starting from 1 with safe5v) - uint8_t obj_pos = 1; - - for(size_t i=0; in_data_obj; i++) { - TU_VERIFY(ptr < p_end); - uint32_t const pdo = tu_le32toh(tu_unaligned_read32(ptr)); - - switch ((pdo >> 30) & 0x03ul) { - case PD_PDO_TYPE_FIXED: { - pd_pdo_fixed_t const* fixed = (pd_pdo_fixed_t const*) &pdo; - TU_LOG3("[Fixed] %u mV %u mA\r\n", fixed->voltage_50mv*50, fixed->current_max_10ma*10); - break; - } - - case PD_PDO_TYPE_BATTERY: - break; - - case PD_PDO_TYPE_VARIABLE: - break; - - case PD_PDO_TYPE_APDO: - break; - } - - ptr += 4; - } - - // Send request with selected PDO position as response to Source Cap - pd_rdo_fixed_variable_t rdo = { - .current_extremum_10ma = 50, // max 500mA - .current_operate_10ma = 30, // 300mA - .reserved = 0, - .epr_mode_capable = 0, - .unchunked_ext_msg_support = 0, - .no_usb_suspend = 0, - .usb_comm_capable = 1, - .capability_mismatch = 0, - .give_back_flag = 0, // exteremum is max - .object_position = obj_pos, - }; - - pd_header_t const req_header = { - .msg_type = PD_DATA_REQUEST, - .data_role = PD_DATA_ROLE_UFP, - .specs_rev = PD_REV_20, - .power_role = PD_POWER_ROLE_SINK, - .msg_id = 0, - .n_data_obj = 1, - .extended = 0, - }; - - utcd_msg_send(rhport, &req_header, &rdo); - - break; - } - - default: break; - } - } - - return true; + return utcd_msg_send(rhport, &header, rdo); } void tcd_event_handler(tcd_event_t const * event, bool in_isr) { @@ -199,25 +187,17 @@ void tcd_event_handler(tcd_event_t const * event, bool in_isr) { switch(event->event_id) { case TCD_EVENT_CC_CHANGED: if (event->cc_changed.cc_state[0] || event->cc_changed.cc_state[1]) { - // Attach + // Attach, start receiving tcd_msg_receive(event->rhport, _rx_buf, sizeof(_rx_buf)); }else { // Detach } break; - case TCD_EVENT_RX_COMPLETE: - // TODO process message here in ISR, move to thread later - if (event->rx_complete.result == XFER_RESULT_SUCCESS) { - parse_message(event->rhport, _rx_buf, event->rx_complete.xferred_bytes); - } - - // start new rx - tcd_msg_receive(event->rhport, _rx_buf, sizeof(_rx_buf)); - break; - default: break; } + + osal_queue_send(_utcd_q, event, in_isr); } //--------------------------------------------------------------------+ diff --git a/src/typec/utcd.h b/src/typec/utcd.h index 740e477ac..9fbff9bc6 100644 --- a/src/typec/utcd.h +++ b/src/typec/utcd.h @@ -52,6 +52,17 @@ bool tuc_init(uint8_t rhport, uint32_t port_type); // Check if typec port is initialized bool tuc_inited(uint8_t rhport); +// Task function should be called in main/rtos loop, extended version of tud_task() +// - timeout_ms: millisecond to wait, zero = no wait, 0xFFFFFFFF = wait forever +// - in_isr: if function is called in ISR +void tuc_task_ext(uint32_t timeout_ms, bool in_isr); + +// Task function should be called in main/rtos loop +TU_ATTR_ALWAYS_INLINE static inline +void tuc_task (void) { + tuc_task_ext(UINT32_MAX, false); +} + #ifndef _TUSB_TCD_H_ extern void tcd_int_handler(uint8_t rhport); #endif @@ -59,10 +70,19 @@ extern void tcd_int_handler(uint8_t rhport); // Interrupt handler, name alias to TCD #define tuc_int_handler tcd_int_handler +//--------------------------------------------------------------------+ +// Callbacks +//--------------------------------------------------------------------+ + +TU_ATTR_WEAK bool tuc_pd_data_received_cb(uint8_t rhport, pd_header_t const* header, uint8_t const* dobj, uint8_t const* p_end); +TU_ATTR_WEAK bool tuc_pd_control_received_cb(uint8_t rhport, pd_header_t const* header); + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ +bool tuc_msg_request(uint8_t rhport, void const* rdo); + #ifdef __cplusplus }