mirror of
https://github.com/No-Chicken/Power-Pico.git
synced 2026-04-03 13:02:36 +08:00
update
This commit is contained in:
599
software/Power_Pico/BSP/PD/PD_UFP_Protocol.c
Normal file
599
software/Power_Pico/BSP/PD/PD_UFP_Protocol.c
Normal file
@@ -0,0 +1,599 @@
|
||||
|
||||
/**
|
||||
* PD_UFP_Protocol.c
|
||||
*
|
||||
* Updated on: Jan 25, 2021
|
||||
* Author: Ryan Ma
|
||||
*
|
||||
* Minimalist USB PD implement with only UFP(device) sink only functionality
|
||||
* Requires PD PHY to do automatic GoodCRC response on valid SOP messages.
|
||||
* Requires only stdint.h, stdbool.h and string.h
|
||||
* No use of bit-field for better cross-platform compatibility
|
||||
*
|
||||
* Support PD3.0 PPS
|
||||
* Do not support extended message. Not necessary for PD trigger and PPS.
|
||||
*
|
||||
* Reference: USB_PD_R2_0 V1.3 - 20170112
|
||||
* USB_PD_R3_0 V2.0 20190829 + ECNs 2020-12-10
|
||||
* - Chapter 6. Protocol Layer
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "PD_UFP_Protocol.h"
|
||||
|
||||
#define PD_SPECIFICATION_REVISION 0x2
|
||||
|
||||
#define PD_CONTROL_MSG_TYPE_ACCEPT 0x3
|
||||
#define PD_CONTROL_MSG_TYPE_REJECT 0x4
|
||||
#define PD_CONTROL_MSG_TYPE_GET_SRC_CAP 0x7
|
||||
#define PD_CONTROL_MSG_TYPE_NOT_SUPPORT 0x10
|
||||
#define PD_CONTROL_MSG_TYPE_GET_PPS_STATUS 0x14
|
||||
|
||||
#define PD_DATA_MSG_TYPE_REQUEST 0x2
|
||||
#define PD_DATA_MSG_TYPE_SINK_CAP 0x4
|
||||
#define PD_DATA_MSG_TYPE_VENDOR_DEFINED 0xF
|
||||
|
||||
#define PD_EXT_MSG_TYPE_SINK_CAP_EXT 0xF
|
||||
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t spec_rev;
|
||||
uint8_t id;
|
||||
uint8_t num_of_obj;
|
||||
} PD_msg_header_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t limit;
|
||||
uint8_t use_voltage;
|
||||
uint8_t use_current;
|
||||
} PD_power_option_setting_t;
|
||||
|
||||
struct PD_msg_state_t {
|
||||
const char * name;
|
||||
void (*handler)(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
bool (*responder)(PD_protocol_t * p, uint16_t * header, uint32_t * obj);
|
||||
};
|
||||
|
||||
/* Optimize RAM usage on AVR MCU by allocate const in PROGMEM */
|
||||
#if defined(__AVR__)
|
||||
#include <avr/pgmspace.h>
|
||||
#define SET_MSG_STAGE(d, s) do { static struct PD_msg_state_t m; memcpy_P(&m, s, sizeof(struct PD_msg_state_t)); d = &m; } while (0)
|
||||
#define SET_MSG_NAME(d, s) do { static char n[16]; strncpy_P(n, s, 15); d = n; } while (0)
|
||||
#define COPY_PDO(d, s) do { memcpy_P(&d, &s, 4); } while (0)
|
||||
#else
|
||||
#define PROGMEM
|
||||
#define SET_MSG_STAGE(d, s) do { d = s; } while (0)
|
||||
#define SET_MSG_NAME(d, s) do { d = s; } while (0)
|
||||
#define COPY_PDO(d, s) do { d = s; } while (0)
|
||||
#endif
|
||||
|
||||
#define T(name) static const char str_ ## name [] PROGMEM = #name
|
||||
|
||||
static void handler_good_crc (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
static void handler_goto_min (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
static void handler_accept (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
static void handler_reject (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
static void handler_ps_rdy (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
static void handler_source_cap (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
static void handler_BIST (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
static void handler_alert (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
static void handler_vender_def (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
static void handler_PPS_Status (PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events);
|
||||
|
||||
static bool responder_get_sink_cap (PD_protocol_t * p, uint16_t * header, uint32_t * obj);
|
||||
static bool responder_reject (PD_protocol_t * p, uint16_t * header, uint32_t * obj);
|
||||
static bool responder_soft_reset (PD_protocol_t * p, uint16_t * header, uint32_t * obj);
|
||||
static bool responder_source_cap (PD_protocol_t * p, uint16_t * header, uint32_t * obj);
|
||||
static bool responder_vender_def (PD_protocol_t * p, uint16_t * header, uint32_t * obj);
|
||||
static bool responder_sink_cap_ext (PD_protocol_t * p, uint16_t * header, uint32_t * obj);
|
||||
static bool responder_not_support (PD_protocol_t * p, uint16_t * header, uint32_t * obj);
|
||||
|
||||
T(C0); T(GoodCRC); T(GotoMin); T(Accept); T(Reject); T(Ping); T(PS_RDY); T(Get_Src_Cap);
|
||||
T(Get_Sink_Cap); T(DR_Swap); T(PR_Swap); T(VCONN_Swap); T(Wait); T(Soft_Rst); T(Dat_Rst); T(Dat_Rst_Cpt);
|
||||
T(NS); T(Get_Src_Ext); T(Get_Stat); T(FR_Swap); T(Get_PPS_Stat); T(Get_CC); T(Get_Sink_Ext); T(C_R);
|
||||
|
||||
static const struct PD_msg_state_t ctrl_msg_list[] PROGMEM = {
|
||||
{.name = str_C0, .handler = 0, .responder = 0},
|
||||
{.name = str_GoodCRC, .handler = handler_good_crc, .responder = 0},
|
||||
{.name = str_GotoMin, .handler = handler_goto_min, .responder = 0},
|
||||
{.name = str_Accept, .handler = handler_accept, .responder = 0},
|
||||
{.name = str_Reject, .handler = handler_reject, .responder = 0},
|
||||
{.name = str_Ping, .handler = 0, .responder = 0},
|
||||
{.name = str_PS_RDY, .handler = handler_ps_rdy, .responder = 0},
|
||||
{.name = str_Get_Src_Cap, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Get_Sink_Cap, .handler = 0, .responder = responder_get_sink_cap},
|
||||
{.name = str_DR_Swap, .handler = 0, .responder = responder_reject},
|
||||
{.name = str_PR_Swap, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_VCONN_Swap, .handler = 0, .responder = responder_reject},
|
||||
{.name = str_Wait, .handler = 0, .responder = 0},
|
||||
{.name = str_Soft_Rst, .handler = 0, .responder = responder_soft_reset},
|
||||
{.name = str_Dat_Rst, .handler = 0, .responder = 0},
|
||||
{.name = str_Dat_Rst_Cpt, .handler = 0, .responder = 0},
|
||||
|
||||
{.name = str_NS, .handler = 0, .responder = 0},
|
||||
{.name = str_Get_Src_Ext, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Get_Stat, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_FR_Swap, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Get_PPS_Stat, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Get_CC, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Get_Sink_Ext, .handler = 0, .responder = responder_sink_cap_ext},
|
||||
{.name = str_C_R, .handler = 0, .responder = responder_not_support},
|
||||
};
|
||||
|
||||
T(D0); T(Src_Cap); T(Request); T(BIST); T(Sink_Cap); T(Bat_Stat); T(Alert); T(Get_CI);
|
||||
T(Enter_USB); T(D9); T(D10); T(D11); T(D12); T(D13); T(D14); T(VDM);
|
||||
T(D_R);
|
||||
|
||||
static const struct PD_msg_state_t data_msg_list[] PROGMEM = {
|
||||
{.name = str_D0, .handler = 0, .responder = 0},
|
||||
{.name = str_Src_Cap, .handler = handler_source_cap, .responder = responder_source_cap},
|
||||
{.name = str_Request, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_BIST, .handler = handler_BIST, .responder = 0},
|
||||
{.name = str_Sink_Cap, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Bat_Stat, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Alert, .handler = handler_alert, .responder = 0},
|
||||
{.name = str_Get_CI, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Enter_USB, .handler = 0, .responder = 0},
|
||||
{.name = str_D9, .handler = 0, .responder = 0},
|
||||
{.name = str_D10, .handler = 0, .responder = 0},
|
||||
{.name = str_D11, .handler = 0, .responder = 0},
|
||||
{.name = str_D12, .handler = 0, .responder = 0},
|
||||
{.name = str_D13, .handler = 0, .responder = 0},
|
||||
{.name = str_D14, .handler = 0, .responder = 0},
|
||||
{.name = str_VDM, .handler = handler_vender_def, .responder = responder_vender_def},
|
||||
|
||||
{.name = str_D_R, .handler = 0, .responder = responder_not_support},
|
||||
};
|
||||
|
||||
T(E0); T(Src_Cap_Ext); T(Status); T(Get_Bat_cap); T(Get_Bat_Stat); T(Bat_Cap); T(Get_Mfg_Info); T(Mfg_Info);
|
||||
T(Sec_Request); T(Sec_Response); T(FU_request); T(FU_Response); T(PPS_Stat); T(Country_Info); T(Country_Code); T(Sink_Cap_Ext);
|
||||
T(E_R);
|
||||
|
||||
static const struct PD_msg_state_t ext_msg_list[] PROGMEM = {
|
||||
{.name = str_E0, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Src_Cap_Ext, .handler = 0, .responder = 0},
|
||||
{.name = str_Status, .handler = 0, .responder = 0},
|
||||
{.name = str_Get_Bat_cap, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Get_Bat_Stat, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Bat_Cap, .handler = 0, .responder = 0},
|
||||
{.name = str_Get_Mfg_Info, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Mfg_Info, .handler = 0, .responder = 0},
|
||||
{.name = str_Sec_Request, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_Sec_Response, .handler = 0, .responder = 0},
|
||||
{.name = str_FU_request, .handler = 0, .responder = responder_not_support},
|
||||
{.name = str_FU_Response, .handler = 0, .responder = 0},
|
||||
{.name = str_PPS_Stat, .handler = handler_PPS_Status, .responder = 0},
|
||||
{.name = str_Country_Info, .handler = 0, .responder = 0},
|
||||
{.name = str_Country_Code, .handler = 0, .responder = 0},
|
||||
{.name = str_Sink_Cap_Ext, .handler = 0, .responder = responder_not_support},
|
||||
|
||||
{.name = str_E_R, .handler = 0, .responder = responder_not_support},
|
||||
};
|
||||
|
||||
static const PD_power_option_setting_t power_option_setting[8] = {
|
||||
{.limit = 25, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_5V */
|
||||
{.limit = 45, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_9V */
|
||||
{.limit = 60, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_12V */
|
||||
{.limit = 75, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_15V */
|
||||
{.limit = 100, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_20V */
|
||||
{.limit = 100, .use_voltage = 1, .use_current = 0}, /* PD_POWER_OPTION_MAX_VOLTAGE */
|
||||
{.limit = 125, .use_voltage = 0, .use_current = 1}, /* PD_POWER_OPTION_MAX_CURRENT */
|
||||
{.limit = 12500,.use_voltage = 1, .use_current = 1}, /* PD_POWER_OPTION_MAX_POWER */
|
||||
};
|
||||
|
||||
static uint8_t evaluate_src_cap(PD_protocol_t * p, uint16_t PPS_voltage, uint8_t PPS_current)
|
||||
{
|
||||
const PD_power_option_setting_t * setting;
|
||||
PD_power_info_t info;
|
||||
uint8_t option = p->power_option;
|
||||
uint8_t selected = 0;
|
||||
|
||||
/* If selected option is not available, use first PDO. Reference: 6.4.1 Capabilities Message
|
||||
The vSafe5V Fixed Supply Object Shall always be the first object. */
|
||||
if (option >= sizeof(power_option_setting) / sizeof(power_option_setting[0])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
setting = &power_option_setting[option];
|
||||
for (uint8_t n = 0; PD_protocol_get_power_info(p, n, &info); n++) {
|
||||
if (info.type == PD_PDO_TYPE_AUGMENTED_PDO) {
|
||||
uint16_t pps_v = PPS_voltage * 2; /* Voltage in 20mV units */
|
||||
uint16_t pps_i = PPS_current * 5; /* Current in 50mA units */
|
||||
/* PD_power_info_t: Voltage in 50mV units, Current in 10mA units */
|
||||
if (info.min_v * 5 <= pps_v && pps_v <= info.max_v * 5 && pps_i <= info.max_i) {
|
||||
return n;
|
||||
}
|
||||
} else {
|
||||
uint8_t v = setting->use_voltage ? info.max_v >> 2 : 1;
|
||||
uint8_t i = setting->use_current ? info.max_i >> 2 : 1;
|
||||
uint16_t power = (uint16_t)v * i; /* reduce 10-bit power info to 8-bit and use 8-bit x 8-bit multiplication */
|
||||
if (power <= setting->limit) {
|
||||
selected = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
static void parse_header(PD_msg_header_info_t * info, uint16_t header)
|
||||
{
|
||||
/* Reference: 6.2.1.1 Message Header */
|
||||
info->type = (header >> 0) & 0x1F; /* 4...0 Message Type */
|
||||
info->spec_rev = (header >> 6) & 0x3; /* 7...6 Specification Revision */
|
||||
info->id = (header >> 9) & 0x7; /* 11...9 MessageID */
|
||||
info->num_of_obj = (header >> 12) & 0x7; /* 14...12 Number of Data Objects */
|
||||
}
|
||||
|
||||
static uint16_t generate_header(PD_protocol_t * p, uint8_t type, uint8_t obj_count)
|
||||
{
|
||||
/* Reference: 6.2.1.1 Message Header */
|
||||
uint16_t h = ((uint16_t)type << 0) | /* 4...0 Message Type */
|
||||
((uint16_t)PD_SPECIFICATION_REVISION << 6) | /* 7...6 Specification Revision */
|
||||
((uint16_t)p->message_id << 9) | /* 11...9 MessageID */
|
||||
((uint16_t)obj_count << 12); /* 14...12 Number of Data Objects */
|
||||
p->tx_msg_header = h;
|
||||
return h;
|
||||
}
|
||||
|
||||
static uint16_t generate_header_ext(PD_protocol_t * p, uint8_t type, uint8_t data_size, uint32_t * obj)
|
||||
{
|
||||
uint16_t h = generate_header(p, type, (data_size + 5) >> 2); /* set obj_count to fit ext header and data */
|
||||
h |= (uint16_t)1 << 15; /* Set extended field */
|
||||
/* Reference: 6.2.1.2 Extended Message Headerr */
|
||||
obj[0] |= ((uint16_t)data_size << 0) | /* 8...0 ata Size */
|
||||
/* Assume short message, set Chunk Number and Request Chunk to 0 */
|
||||
((uint16_t)1 << 15); /* 15 Chunked */
|
||||
p->tx_msg_header = h;
|
||||
return h;
|
||||
}
|
||||
|
||||
static void handler_good_crc(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
/* Reference: 6.2.1.3 Message ID
|
||||
MessageIDCounter Shall be initialized to zero at power-on / reset, increment when receive GoodCRC Message */
|
||||
uint8_t message_id = p->message_id;
|
||||
if (++message_id > 7) {
|
||||
message_id = 0;
|
||||
}
|
||||
p->message_id = message_id;
|
||||
}
|
||||
|
||||
static void handler_goto_min(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
// Not implemented
|
||||
}
|
||||
|
||||
static void handler_accept(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
if (events) {
|
||||
*events |= PD_PROTOCOL_EVENT_ACCEPT;
|
||||
}
|
||||
}
|
||||
|
||||
static void handler_reject(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
if (events) {
|
||||
*events |= PD_PROTOCOL_EVENT_PS_RDY;
|
||||
}
|
||||
}
|
||||
|
||||
static void handler_ps_rdy(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
if (events) {
|
||||
*events |= PD_PROTOCOL_EVENT_PS_RDY;
|
||||
}
|
||||
}
|
||||
|
||||
static void handler_source_cap(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
PD_msg_header_info_t h;
|
||||
parse_header(&h, header);
|
||||
p->power_data_obj_count = h.num_of_obj;
|
||||
for (uint8_t i = 0; i < h.num_of_obj; i++) {
|
||||
p->power_data_obj[i] = obj[i];
|
||||
}
|
||||
p->power_data_obj_selected = evaluate_src_cap(p, p->PPS_voltage, p->PPS_current);
|
||||
if (events) {
|
||||
*events |= PD_PROTOCOL_EVENT_SRC_CAP;
|
||||
}
|
||||
}
|
||||
|
||||
static void handler_BIST(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
// TODO: implement BIST
|
||||
}
|
||||
|
||||
static void handler_alert(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
// TODO: implement alert
|
||||
}
|
||||
|
||||
static void handler_vender_def(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
// TODO: implement VDM parsing
|
||||
}
|
||||
|
||||
static void handler_PPS_Status(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
/* Handle chunked Extended message, Offset 2 byte for Extended Message Header */
|
||||
p->PPSSDB[0] = (obj[0] >> 16) & 0xFF;
|
||||
p->PPSSDB[1] = (obj[0] >> 24) & 0xFF;
|
||||
p->PPSSDB[2] = (obj[1] >> 0) & 0xFF;
|
||||
p->PPSSDB[3] = (obj[1] >> 8) & 0xFF;
|
||||
if (events) {
|
||||
*events |= PD_PROTOCOL_EVENT_PPS_STATUS;
|
||||
}
|
||||
}
|
||||
|
||||
static bool responder_get_sink_cap(PD_protocol_t * p, uint16_t * header, uint32_t * obj)
|
||||
{
|
||||
/* Reference: 6.4.1.2.3 Sink Fixed Supply Power Data Object */
|
||||
uint32_t data = ((uint32_t)100 << 0) | /* B9...0 Operational Current in 10mA units */
|
||||
((uint32_t)100 << 10) | /* B19...10 Voltage in 50mV units */
|
||||
((uint32_t)1 << 26) | /* B26 USB Communications Capable */
|
||||
((uint32_t)1 << 28) | /* B28 Higher Capability */
|
||||
((uint32_t)PD_PDO_TYPE_FIXED_SUPPLY << 30); /* B31...30 Fixed supply */
|
||||
*obj = data; /* Only implement 5V 1A Fix supply PDO. Source rarely request sink cap */
|
||||
*header = generate_header(p, PD_DATA_MSG_TYPE_SINK_CAP, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool responder_sink_cap_ext(PD_protocol_t * p, uint16_t * header, uint32_t * obj)
|
||||
{
|
||||
/* Reference: 6.5.13 Sink_Capabilities_Extended Message
|
||||
6.12.3 Applicability of Extended Messages (Normative; Shall be supported) */
|
||||
#define SINK_CAP_VID 0
|
||||
#define SINK_CAP_PID 0
|
||||
#define SINK_CAP_XID 0 /* If the vendor does not have an XID, then it Shall return zero */
|
||||
#define SINK_CAP_FW_Version 1
|
||||
#define SINK_CAP_HW_Version 1
|
||||
#define SINK_CAP_SKEDB_Version 1
|
||||
#define SINK_CAP_SINK_MODE 0x3 /* Bit 0: PPS charging supported, Bit 1: VBUS powered */
|
||||
#define SINK_CAP_SINK_MIN_PDP 5 /* Minimum PD Power in Watt */
|
||||
#define SINK_CAP_SINK_OP_PDP 5 /* Operational PD Power in Watt */
|
||||
#define SINK_CAP_SINK_MAX_PDP 100 /* Maximum PD Power in Watt */
|
||||
static const uint32_t SKEDB[6] PROGMEM = { /* 2-byte header + 21-byte data, chunked to 6 PDO */
|
||||
/* PDO[0], data byte 0...1 */
|
||||
/* 16-bit LSB is reserved for Extended Message Header */
|
||||
((uint32_t)SINK_CAP_VID << 16), /* Byte 0...1 VID */
|
||||
/* PDO[1], data byte 2...5 */
|
||||
((uint32_t)SINK_CAP_PID << 0) | /* Byte 2...3 PID */
|
||||
(((uint32_t)SINK_CAP_XID & 0xFF) << 16), /* Byte 4...5 XID */
|
||||
/* PDO[2], data byte 6...9 */
|
||||
(((uint32_t)SINK_CAP_XID >> 16) << 0) | /* Byte 6...7 XID */
|
||||
((uint32_t)SINK_CAP_FW_Version << 16) | /* Byte 8 FW Version */
|
||||
((uint32_t)SINK_CAP_HW_Version << 24), /* Byte 9 HW Version */
|
||||
/* PDO[3], data byte 10...13 */
|
||||
((uint32_t)SINK_CAP_SKEDB_Version << 0), /* Byte 10 SKEDB Version */
|
||||
/* Not set Byte 11 Load Step, Byte 13..12 Sink Load Characteristics */
|
||||
/* PDO[4], data byte 14...17 */
|
||||
/* Not set Byte 14 Compliance, Byte 15 Touch Temp, Byte 16 Battery Info */
|
||||
((uint32_t)SINK_CAP_SINK_MODE << 24), /* Byte 17 Sink Modes */
|
||||
/* PDO[5], data byte 18...20 */
|
||||
((uint32_t)SINK_CAP_SINK_MIN_PDP << 0) | /* Byte 18 Minimum PDP */
|
||||
((uint32_t)SINK_CAP_SINK_OP_PDP << 8) | /* Byte 19 Operational PDP */
|
||||
((uint32_t)SINK_CAP_SINK_MAX_PDP << 16) /* Byte 20 Maximum PDP */
|
||||
};
|
||||
uint8_t i;
|
||||
for (i = 0; i < 6; i++) {
|
||||
COPY_PDO(obj[i], SKEDB[i]);
|
||||
}
|
||||
*header = generate_header_ext(p, PD_EXT_MSG_TYPE_SINK_CAP_EXT, 21, obj);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool responder_reject(PD_protocol_t * p, uint16_t * header, uint32_t * obj)
|
||||
{
|
||||
*header = generate_header(p, PD_CONTROL_MSG_TYPE_REJECT, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool responder_not_support(PD_protocol_t * p, uint16_t * header, uint32_t * obj)
|
||||
{
|
||||
*header = generate_header(p, PD_CONTROL_MSG_TYPE_NOT_SUPPORT, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool responder_soft_reset(PD_protocol_t * p, uint16_t * header, uint32_t * obj)
|
||||
{
|
||||
*header = generate_header(p, PD_CONTROL_MSG_TYPE_ACCEPT, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool responder_source_cap(PD_protocol_t * p, uint16_t * header, uint32_t * obj)
|
||||
{
|
||||
PD_power_info_t info;
|
||||
uint32_t data, pos = p->power_data_obj_selected + 1;
|
||||
PD_protocol_get_power_info(p, p->power_data_obj_selected, &info);
|
||||
/* Reference: 6.4.2 Request Message */
|
||||
if (info.type == PD_PDO_TYPE_AUGMENTED_PDO) {
|
||||
/* NOTE: To compatible PD2.0 PHY, do not set Unchunked Extended Messages Supported */
|
||||
data = ((uint32_t)p->PPS_current << 0) | /* B6 ...0 Operating Current 50mA units */
|
||||
((uint32_t)p->PPS_voltage << 9) | /* B19...9 Output Voltage in 20mV units */
|
||||
((uint32_t)1 << 25) | /* B25 USB Communication Capable */
|
||||
((uint32_t)pos << 28); /* B30...28 Object position (000b is Reserved and Shall Not be used) */
|
||||
} else {
|
||||
uint32_t req = info.max_i ? info.max_i : info.max_p;
|
||||
data = ((uint32_t)req << 0) | /* B9 ...0 Max Operating Current 10mA units / Max Operating Power in 250mW units */
|
||||
((uint32_t)req << 10) | /* B19...10 Operating Current 10mA units / Operating Power in 250mW units */
|
||||
((uint32_t)1 << 25) | /* B25 USB Communication Capable */
|
||||
((uint32_t)pos << 28); /* B30...28 Object position (000b is Reserved and Shall Not be used) */
|
||||
}
|
||||
*obj = data;
|
||||
*header = generate_header(p, PD_DATA_MSG_TYPE_REQUEST, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool responder_vender_def(PD_protocol_t * p, uint16_t * header, uint32_t * obj)
|
||||
{
|
||||
// TODO: implement VDM respond
|
||||
return false;
|
||||
}
|
||||
|
||||
void PD_protocol_handle_msg(PD_protocol_t * p, uint16_t header, uint32_t * obj, PD_protocol_event_t * events)
|
||||
{
|
||||
#define EXT_MSG_LIMIT (sizeof(ext_msg_list) / sizeof(ext_msg_list[0]) - 1)
|
||||
#define DATA_MSG_LIMIT (sizeof(data_msg_list) / sizeof(data_msg_list[0]) - 1)
|
||||
#define CTRL_MSG_LIMIT (sizeof(ctrl_msg_list) / sizeof(ctrl_msg_list[0]) - 1)
|
||||
|
||||
const struct PD_msg_state_t * state;
|
||||
PD_msg_header_info_t h;
|
||||
parse_header(&h, header);
|
||||
p->rx_msg_header = header;
|
||||
if ((header >> 15) & 0x1) {
|
||||
state = &ext_msg_list[h.type > EXT_MSG_LIMIT ? EXT_MSG_LIMIT : h.type];
|
||||
} else if (h.num_of_obj) {
|
||||
state = &data_msg_list[h.type > DATA_MSG_LIMIT ? DATA_MSG_LIMIT : h.type];
|
||||
} else {
|
||||
state =&ctrl_msg_list[h.type > CTRL_MSG_LIMIT ? CTRL_MSG_LIMIT : h.type];
|
||||
}
|
||||
SET_MSG_STAGE(p->msg_state, state);
|
||||
if (p->msg_state->handler) {
|
||||
p->msg_state->handler(p, header, obj, events);
|
||||
}
|
||||
}
|
||||
|
||||
bool PD_protocol_respond(PD_protocol_t * p, uint16_t * header, uint32_t * obj)
|
||||
{
|
||||
if (p && p->msg_state && p->msg_state->responder && header && obj) {
|
||||
return p->msg_state->responder(p, (uint16_t *)header, obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PD_protocol_create_get_src_cap(PD_protocol_t * p, uint16_t * header)
|
||||
{
|
||||
*header = generate_header(p, PD_CONTROL_MSG_TYPE_GET_SRC_CAP, 0);
|
||||
}
|
||||
|
||||
void PD_protocol_create_get_PPS_status(PD_protocol_t *p, uint16_t *header)
|
||||
{
|
||||
*header = generate_header(p, PD_CONTROL_MSG_TYPE_GET_PPS_STATUS, 0);
|
||||
}
|
||||
|
||||
void PD_protocol_create_request(PD_protocol_t * p, uint16_t * header, uint32_t * obj)
|
||||
{
|
||||
responder_source_cap(p, header, obj);
|
||||
}
|
||||
|
||||
bool PD_protocol_get_power_info(PD_protocol_t * p, uint8_t index, PD_power_info_t * power_info)
|
||||
{
|
||||
if (p && index < p->power_data_obj_count && power_info) {
|
||||
uint32_t obj = p->power_data_obj[index];
|
||||
power_info->type = obj >> 30;
|
||||
switch (power_info->type) {
|
||||
case PD_PDO_TYPE_FIXED_SUPPLY:
|
||||
/* Reference: 6.4.1.2.3 Source Fixed Supply Power Data Object */
|
||||
power_info->min_v = 0;
|
||||
power_info->max_v = (obj >> 10) & 0x3FF; /* B19...10 Voltage in 50mV units */
|
||||
power_info->max_i = (obj >> 0) & 0x3FF; /* B9 ...0 Max Current in 10mA units */
|
||||
power_info->max_p = 0;
|
||||
break;
|
||||
case PD_PDO_TYPE_BATTERY:
|
||||
/* Reference: 6.4.1.2.5 Battery Supply Power Data Object */
|
||||
power_info->min_v = (obj >> 10) & 0x3FF; /* B19...10 Min Voltage in 50mV units */
|
||||
power_info->max_v = (obj >> 20) & 0x3FF; /* B29...20 Max Voltage in 50mV units */
|
||||
power_info->max_i = 0;
|
||||
power_info->max_p = (obj >> 0) & 0x3FF; /* B9 ...0 Max Allowable Power in 250mW units */
|
||||
break;
|
||||
case PD_PDO_TYPE_VARIABLE_SUPPLY:
|
||||
/* Reference: 6.4.1.2.4 Variable Supply (non-Battery) Power Data Object */
|
||||
power_info->min_v = (obj >> 10) & 0x3FF; /* B19...10 Min Voltage in 50mV units */
|
||||
power_info->max_v = (obj >> 20) & 0x3FF; /* B29...20 Max Voltage in 50mV units */
|
||||
power_info->max_i = (obj >> 0) & 0x3FF; /* B9 ...0 Max Current in 10mA units */
|
||||
power_info->max_p = 0;
|
||||
break;
|
||||
case PD_PDO_TYPE_AUGMENTED_PDO:
|
||||
/* Reference: 6.4.1.3.4 Programmable Power Supply Augmented Power Data Object */
|
||||
power_info->max_v = ((obj >> 17) & 0xFF) * 2; /* B24...17 Max Voltage in 100mV units */
|
||||
power_info->min_v = ((obj >> 8) & 0xFF) * 2; /* B15...8 Min Voltage in 100mV units */
|
||||
power_info->max_i = ((obj >> 0) & 0x7F) * 5; /* B6 ...0 Max Current in 50mA units */
|
||||
power_info->max_p = 0;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PD_protocol_get_msg_info(uint16_t header, PD_msg_info_t * msg_info)
|
||||
{
|
||||
PD_msg_header_info_t h;
|
||||
parse_header(&h, header);
|
||||
if (msg_info) {
|
||||
const char * name;
|
||||
const struct PD_msg_state_t * state;
|
||||
uint8_t type = h.type;
|
||||
SET_MSG_STAGE(state, header & 0x8000 ? &ext_msg_list[type] :
|
||||
h.num_of_obj ? &data_msg_list[type] : &ctrl_msg_list[type]);
|
||||
SET_MSG_NAME(name, state->name);
|
||||
msg_info->name = name;
|
||||
msg_info->id = h.id;
|
||||
msg_info->spec_rev = h.spec_rev;
|
||||
msg_info->num_of_obj = h.num_of_obj;
|
||||
msg_info->extended = header >> 15;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PD_protocol_get_PPS_status(PD_protocol_t *p, PPS_status_t * PPS_status)
|
||||
{
|
||||
if (p && PPS_status) {
|
||||
/* Reference: 6.5.10 PPS_Status Message */
|
||||
PPS_status->output_voltage = ((uint16_t)p->PPSSDB[1] << 8) | p->PPSSDB[0];
|
||||
PPS_status->output_current = p->PPSSDB[2];
|
||||
PPS_status->flag_PTF = (p->PPSSDB[3] >> 1) & 0x3; /* Bit 1 ... 2 */
|
||||
PPS_status->flag_OMF = (p->PPSSDB[3] >> 3) & 0x1; /* Bit 3 */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PD_protocol_set_power_option(PD_protocol_t * p, enum PD_power_option_t option)
|
||||
{
|
||||
p->power_option = option;
|
||||
p->PPS_voltage = 0;
|
||||
p->PPS_current = 0;
|
||||
if (p->power_data_obj_count > 0) {
|
||||
p->power_data_obj_selected = evaluate_src_cap(p, p->PPS_voltage, p->PPS_current);
|
||||
return true; /* need to re-send request */
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PD_protocol_select_power(PD_protocol_t * p, uint8_t index)
|
||||
{
|
||||
if (index < p->power_data_obj_count) {
|
||||
p->power_data_obj_selected = index;
|
||||
return true; /* need to re-send request */
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PD_protocol_set_PPS(PD_protocol_t * p, uint16_t PPS_voltage, uint8_t PPS_current, bool strict)
|
||||
{
|
||||
if (p->PPS_voltage != PPS_voltage || p->PPS_current != PPS_current) {
|
||||
uint8_t selected = evaluate_src_cap(p, PPS_voltage, PPS_current);
|
||||
if (selected || !strict) {
|
||||
p->PPS_voltage = PPS_voltage;
|
||||
p->PPS_current = PPS_current;
|
||||
p->power_data_obj_selected = selected;
|
||||
return true; /* need to re-send request */
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PD_protocol_reset(PD_protocol_t * p)
|
||||
{
|
||||
p->msg_state = &ctrl_msg_list[0];
|
||||
p->message_id = 0;
|
||||
}
|
||||
|
||||
void PD_protocol_init(PD_protocol_t * p)
|
||||
{
|
||||
memset(p, 0, sizeof(PD_protocol_t));
|
||||
p->msg_state = &ctrl_msg_list[0];
|
||||
}
|
||||
Reference in New Issue
Block a user