summaryrefslogtreecommitdiff
path: root/zephyr/emul/tcpc/emul_tcpci_partner_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/emul/tcpc/emul_tcpci_partner_common.c')
-rw-r--r--zephyr/emul/tcpc/emul_tcpci_partner_common.c578
1 files changed, 484 insertions, 94 deletions
diff --git a/zephyr/emul/tcpc/emul_tcpci_partner_common.c b/zephyr/emul/tcpc/emul_tcpci_partner_common.c
index 49c5278908..4d6467378e 100644
--- a/zephyr/emul/tcpc/emul_tcpci_partner_common.c
+++ b/zephyr/emul/tcpc/emul_tcpci_partner_common.c
@@ -1,4 +1,4 @@
-/* Copyright 2021 The Chromium OS Authors. All rights reserved.
+/* Copyright 2021 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
@@ -8,18 +8,21 @@ LOG_MODULE_REGISTER(tcpci_partner, CONFIG_TCPCI_EMUL_LOG_LEVEL);
#include <stdlib.h>
#include <zephyr/sys/byteorder.h>
-#include <zephyr/zephyr.h>
-#include <ztest.h>
+#include <zephyr/kernel.h>
+#include <zephyr/ztest.h>
#include "common.h"
#include "emul/tcpc/emul_tcpci_partner_common.h"
#include "emul/tcpc/emul_tcpci.h"
#include "usb_pd.h"
+#include "util.h"
/** Length of PDO, RDO and BIST request object in SOP message in bytes */
-#define TCPCI_MSG_DO_LEN 4
+#define TCPCI_MSG_DO_LEN 4
/** Length of header in SOP message in bytes */
-#define TCPCI_MSG_HEADER_LEN 2
+#define TCPCI_MSG_HEADER_LEN 2
+/** Length of extended header in bytes */
+#define TCPCI_MSG_EXT_HEADER_LEN 2
void tcpci_partner_common_hard_reset_as_role(struct tcpci_partner_data *data,
enum pd_power_role power_role)
@@ -27,33 +30,104 @@ void tcpci_partner_common_hard_reset_as_role(struct tcpci_partner_data *data,
data->power_role = power_role;
data->data_role = power_role == PD_ROLE_SOURCE ? PD_ROLE_DFP :
PD_ROLE_UFP;
+ data->displayport_configured = false;
+ data->entered_svid = 0;
+ atomic_clear(&data->mode_enter_attempts);
}
-struct tcpci_partner_msg *tcpci_partner_alloc_msg(int data_objects)
+/**
+ * @brief Allocate space for a PD message. Do not call directly; use
+ * tcpci_partner_alloc_standard_msg() or
+ * tcpci_partner_alloc_extended_msg() depending on the type of message.
+ *
+ * @param size Size of the message in bytes, including header(s)
+ *
+ * @return Pointer to new message on success
+ * @return NULL on error
+ */
+static struct tcpci_partner_msg *tcpci_partner_alloc_msg_helper(size_t size)
{
struct tcpci_partner_msg *new_msg;
- size_t size = TCPCI_MSG_HEADER_LEN + TCPCI_MSG_DO_LEN * data_objects;
- new_msg = malloc(sizeof(struct tcpci_partner_msg));
+ new_msg = calloc(1, sizeof(struct tcpci_partner_msg));
if (new_msg == NULL) {
return NULL;
}
- new_msg->msg.buf = malloc(size);
+ new_msg->msg.buf = calloc(1, size);
if (new_msg->msg.buf == NULL) {
free(new_msg);
return NULL;
}
/* Set default message type to SOP */
- new_msg->msg.type = TCPCI_MSG_SOP;
+ new_msg->msg.sop_type = TCPCI_MSG_SOP;
new_msg->msg.cnt = size;
- new_msg->data_objects = data_objects;
return new_msg;
}
/**
+ * @brief Allocate space for a standard (non-extended) message, containing the
+ * specified number of data objects.
+ *
+ * @param num_data_objects Number of 32-bit DOs this message contains, if data
+ * message. Pass 0 if control message.
+ * @return struct tcpci_partner_msg* if successful
+ * @return NULL in case of error
+ */
+static struct tcpci_partner_msg *
+tcpci_partner_alloc_standard_msg(int num_data_objects)
+{
+ struct tcpci_partner_msg *msg = tcpci_partner_alloc_msg_helper(
+ TCPCI_MSG_HEADER_LEN + TCPCI_MSG_DO_LEN * num_data_objects);
+
+ if (msg) {
+ msg->data_objects = num_data_objects;
+ }
+
+ return msg;
+}
+
+/**
+ * @brief Allocate space for an extended message, containing a payload of
+ * specified size
+ *
+ * @param payload_size Size of extended message payload. Do not count either
+ * message header.
+ * @return struct tcpci_partner_msg* if successful
+ * @return NULL in case of error
+ */
+static struct tcpci_partner_msg *
+tcpci_partner_alloc_extended_msg(size_t payload_size)
+{
+ /* Currently, the emulators only support extended messages that can fit
+ * into a single chunk. Enforce that here.
+ */
+
+ __ASSERT(payload_size <= PD_MAX_EXTENDED_MSG_CHUNK_LEN,
+ "Message must fit into a single chunk");
+
+ struct tcpci_partner_msg *msg = tcpci_partner_alloc_msg_helper(
+ TCPCI_MSG_HEADER_LEN + TCPCI_MSG_EXT_HEADER_LEN + payload_size);
+
+ if (msg) {
+ msg->extended = true;
+
+ /* Update the number of data objects with the number of 4-byte
+ * words in the payload, rounding up. This includes the 2-byte
+ * Extended Message Header (USB-PD spec Rev 3.0, V1.1,
+ * section 6.2.1.2.1)
+ */
+
+ msg->data_objects = DIV_ROUND_UP(
+ payload_size + TCPCI_MSG_EXT_HEADER_LEN, 4);
+ }
+
+ return msg;
+}
+
+/**
* @brief Alloc and append message to log if collect_msg_log flag is set
*
* @param data Pointer to TCPCI partner emulator
@@ -64,10 +138,8 @@ struct tcpci_partner_msg *tcpci_partner_alloc_msg(int data_objects)
* @return Pointer to message status
*/
static enum tcpci_emul_tx_status *tcpci_partner_log_msg(
- struct tcpci_partner_data *data,
- const struct tcpci_emul_msg *msg,
- enum tcpci_partner_msg_sender sender,
- enum tcpci_emul_tx_status status)
+ struct tcpci_partner_data *data, const struct tcpci_emul_msg *msg,
+ enum tcpci_partner_msg_sender sender, enum tcpci_emul_tx_status status)
{
struct tcpci_partner_log_msg *log_msg;
int cnt;
@@ -91,7 +163,7 @@ static enum tcpci_emul_tx_status *tcpci_partner_log_msg(
}
log_msg->cnt = cnt;
- log_msg->sop = msg->type;
+ log_msg->sop = msg->sop_type;
log_msg->time = k_uptime_get();
log_msg->sender = sender;
log_msg->status = status;
@@ -121,12 +193,24 @@ void tcpci_partner_free_msg(struct tcpci_partner_msg *msg)
void tcpci_partner_set_header(struct tcpci_partner_data *data,
struct tcpci_partner_msg *msg)
{
+ uint16_t msg_id;
+ uint16_t header;
+
/* Header msg id has only 3 bits and wraps around after 8 messages */
- uint16_t msg_id = data->msg_id & 0x7;
- uint16_t header = PD_HEADER(msg->type, data->power_role,
- data->data_role, msg_id, msg->data_objects,
- data->rev, 0 /* ext */);
- data->msg_id++;
+ if (msg->msg.sop_type == TCPCI_MSG_SOP) {
+ msg_id = data->sop_msg_id & 0x7;
+ header = PD_HEADER(msg->type, data->power_role, data->data_role,
+ msg_id, msg->data_objects, data->rev,
+ msg->extended);
+ data->sop_msg_id++;
+ } else if (msg->msg.sop_type == TCPCI_MSG_SOP_PRIME) {
+ msg_id = data->sop_prime_msg_id & 0x7;
+ header = PD_HEADER(msg->type, PD_PLUG_FROM_CABLE, 0, msg_id,
+ msg->data_objects, data->rev, msg->extended);
+ data->sop_prime_msg_id++;
+ } else {
+ return;
+ }
msg->msg.buf[1] = (header >> 8) & 0xff;
msg->msg.buf[0] = header & 0xff;
@@ -276,8 +360,8 @@ int tcpci_partner_send_msg(struct tcpci_partner_data *data,
return ret;
}
- prev_msg = SYS_SLIST_PEEK_HEAD_CONTAINER(&data->to_send, prev_msg,
- node);
+ prev_msg =
+ SYS_SLIST_PEEK_HEAD_CONTAINER(&data->to_send, prev_msg, node);
/* Current message should be sent first */
if (prev_msg == NULL || prev_msg->time > msg->time) {
sys_slist_prepend(&data->to_send, &msg->node);
@@ -287,7 +371,8 @@ int tcpci_partner_send_msg(struct tcpci_partner_data *data,
}
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&data->to_send, prev_msg, next_msg,
- node) {
+ node)
+ {
/*
* If we reach tail or next message should be sent after new
* message, insert new message to the list.
@@ -306,12 +391,11 @@ int tcpci_partner_send_msg(struct tcpci_partner_data *data,
}
int tcpci_partner_send_control_msg(struct tcpci_partner_data *data,
- enum pd_ctrl_msg_type type,
- uint64_t delay)
+ enum pd_ctrl_msg_type type, uint64_t delay)
{
struct tcpci_partner_msg *msg;
- msg = tcpci_partner_alloc_msg(0);
+ msg = tcpci_partner_alloc_standard_msg(0);
if (msg == NULL) {
return -ENOMEM;
}
@@ -329,14 +413,13 @@ int tcpci_partner_send_control_msg(struct tcpci_partner_data *data,
}
int tcpci_partner_send_data_msg(struct tcpci_partner_data *data,
- enum pd_data_msg_type type,
- uint32_t *data_obj, int data_obj_num,
- uint64_t delay)
+ enum pd_data_msg_type type, uint32_t *data_obj,
+ int data_obj_num, uint64_t delay)
{
struct tcpci_partner_msg *msg;
int addr;
- msg = tcpci_partner_alloc_msg(data_obj_num);
+ msg = tcpci_partner_alloc_standard_msg(data_obj_num);
if (msg == NULL) {
return -ENOMEM;
}
@@ -352,6 +435,36 @@ int tcpci_partner_send_data_msg(struct tcpci_partner_data *data,
return tcpci_partner_send_msg(data, msg, delay);
}
+/* Note: Cables can send from both SOP' and SOP'', so accept a type argument */
+int tcpci_cable_send_data_msg(struct tcpci_partner_data *data,
+ enum pd_data_msg_type type, uint32_t *data_obj,
+ int data_obj_num, enum tcpci_msg_type sop_type,
+ uint64_t delay)
+{
+ struct tcpci_partner_msg *msg;
+ int addr;
+
+ /* TODO(b/243151272): Add SOP'' support */
+ if (sop_type != TCPCI_MSG_SOP_PRIME)
+ return -EINVAL;
+
+ msg = tcpci_partner_alloc_standard_msg(data_obj_num);
+ if (msg == NULL) {
+ return -ENOMEM;
+ }
+
+ for (int i = 0; i < data_obj_num; i++) {
+ /* Address of given data object in message buffer */
+ addr = TCPCI_MSG_HEADER_LEN + i * TCPCI_MSG_DO_LEN;
+ sys_put_le32(data_obj[i], msg->msg.buf + addr);
+ }
+
+ msg->msg.sop_type = sop_type;
+ msg->type = type;
+
+ return tcpci_partner_send_msg(data, msg, delay);
+}
+
int tcpci_partner_clear_msg_queue(struct tcpci_partner_data *data)
{
struct tcpci_partner_msg *msg;
@@ -384,8 +497,10 @@ int tcpci_partner_clear_msg_queue(struct tcpci_partner_data *data)
static void tcpci_partner_common_reset(struct tcpci_partner_data *data)
{
tcpci_partner_clear_msg_queue(data);
- data->msg_id = 0;
- data->recv_msg_id = -1;
+ data->sop_msg_id = 0;
+ data->sop_prime_msg_id = 0;
+ data->sop_recv_msg_id = -1;
+ data->sop_prime_recv_msg_id = -1;
data->in_soft_reset = false;
data->vconn_role = PD_ROLE_VCONN_OFF;
tcpci_partner_stop_sender_response_timer(data);
@@ -416,8 +531,8 @@ void tcpci_partner_common_send_hard_reset(struct tcpci_partner_data *data)
tcpci_partner_common_hard_reset(data);
- msg = tcpci_partner_alloc_msg(0);
- msg->msg.type = TCPCI_MSG_TX_HARD_RESET;
+ msg = tcpci_partner_alloc_standard_msg(0);
+ msg->msg.sop_type = TCPCI_MSG_TX_HARD_RESET;
tcpci_partner_send_msg(data, msg, 0);
}
@@ -425,8 +540,10 @@ void tcpci_partner_common_send_hard_reset(struct tcpci_partner_data *data)
void tcpci_partner_common_send_soft_reset(struct tcpci_partner_data *data)
{
/* Reset counters */
- data->msg_id = 0;
- data->recv_msg_id = -1;
+ data->sop_msg_id = 0;
+ data->sop_prime_msg_id = 0;
+ data->sop_recv_msg_id = -1;
+ data->sop_prime_recv_msg_id = -1;
tcpci_partner_common_clear_ams_ctrl_msg(data);
@@ -437,6 +554,55 @@ void tcpci_partner_common_send_soft_reset(struct tcpci_partner_data *data)
tcpci_partner_start_sender_response_timer(data);
}
+int tcpci_partner_send_extended_msg(struct tcpci_partner_data *data,
+ enum pd_ext_msg_type type, uint64_t delay,
+ uint8_t *payload, size_t payload_size)
+{
+ struct tcpci_partner_msg *msg;
+
+ msg = tcpci_partner_alloc_extended_msg(payload_size);
+ if (msg == NULL) {
+ return -ENOMEM;
+ }
+
+ msg->type = type;
+
+ /* Apply extended message header. We currently do not support
+ * multiple chunks.
+ */
+
+ sys_put_le16(PD_EXT_HEADER(0, 0, payload_size), &msg->msg.buf[2]);
+
+ /* Copy in payload */
+ memcpy(&msg->msg.buf[4], payload, payload_size);
+
+ return tcpci_partner_send_msg(data, msg, delay);
+}
+
+/** Check description in emul_common_tcpci_partner.h */
+void tcpci_partner_common_send_get_battery_capabilities(
+ struct tcpci_partner_data *data, int battery_index)
+{
+ __ASSERT(battery_index >= 0 && battery_index < PD_BATT_MAX,
+ "Battery index out of range");
+ __ASSERT(data->battery_capabilities.index < 0,
+ "Get Battery Capabilities request already in progress");
+
+ LOG_INF("Send battery cap request");
+
+ /* Get_Battery_Cap message payload */
+ uint8_t payload[1] = { [0] = battery_index };
+
+ /* Keep track which battery we requested capabilities for */
+ data->battery_capabilities.index = battery_index;
+ int ret = tcpci_partner_send_extended_msg(data, PD_EXT_GET_BATTERY_CAP,
+ 0, payload, sizeof(payload));
+ if (ret) {
+ LOG_ERR("Send battery capacity result: %d", ret);
+ }
+ tcpci_partner_start_sender_response_timer(data);
+}
+
/**
* @brief Handler for response timeout
*
@@ -445,9 +611,8 @@ void tcpci_partner_common_send_soft_reset(struct tcpci_partner_data *data)
static void tcpci_partner_sender_response_timeout(struct k_work *work)
{
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
- struct tcpci_partner_data *data =
- CONTAINER_OF(dwork, struct tcpci_partner_data,
- sender_response_timeout);
+ struct tcpci_partner_data *data = CONTAINER_OF(
+ dwork, struct tcpci_partner_data, sender_response_timeout);
if (k_mutex_lock(&data->transmit_mutex, K_NO_WAIT) != 0) {
/*
@@ -520,13 +685,180 @@ tcpci_partner_common_vdm_handler(struct tcpci_partner_data *data,
data->modes_vdos, 0);
}
return TCPCI_PARTNER_COMMON_MSG_HANDLED;
- /* TODO(b/219562077): Support DP mode entry. */
+ case CMD_ENTER_MODE:
+ /* Partner emulator only supports entering one mode */
+ if (data->enter_mode_vdos > 0 &&
+ (PD_VDO_VID(vdm_header) ==
+ PD_VDO_VID(data->enter_mode_vdm[0]))) {
+ /* Squirrel away the SVID if we're sending ACK */
+ if (PD_VDO_CMDT(data->enter_mode_vdm[0]) ==
+ CMDT_RSP_ACK)
+ data->entered_svid = PD_VDO_VID(vdm_header);
+
+ tcpci_partner_send_data_msg(data, PD_DATA_VENDOR_DEF,
+ data->enter_mode_vdm,
+ data->enter_mode_vdos, 0);
+ }
+ atomic_inc(&data->mode_enter_attempts);
+ return TCPCI_PARTNER_COMMON_MSG_HANDLED;
+ case CMD_EXIT_MODE:
+ /* Only exit a SVID we know we entered */
+ if (PD_VDO_VID(vdm_header) == data->entered_svid) {
+ uint32_t response_vdm_header;
+
+ response_vdm_header =
+ VDO(PD_VDO_VID(vdm_header), true,
+ VDO_CMDT(CMDT_RSP_ACK) | CMD_EXIT_MODE);
+ tcpci_partner_send_data_msg(data, PD_DATA_VENDOR_DEF,
+ &response_vdm_header, 1, 0);
+ } else {
+ uint32_t response_vdm_header;
+
+ response_vdm_header =
+ VDO(PD_VDO_VID(vdm_header), true,
+ VDO_CMDT(CMDT_RSP_NAK) | CMD_EXIT_MODE);
+ tcpci_partner_send_data_msg(data, PD_DATA_VENDOR_DEF,
+ &response_vdm_header, 1, 0);
+ }
+ data->displayport_configured = false;
+ return TCPCI_PARTNER_COMMON_MSG_HANDLED;
+ case CMD_DP_STATUS:
+ if (data->dp_status_vdos > 0 &&
+ (PD_VDO_VID(vdm_header) == USB_SID_DISPLAYPORT)) {
+ tcpci_partner_send_data_msg(data, PD_DATA_VENDOR_DEF,
+ data->dp_status_vdm,
+ data->dp_status_vdos, 0);
+ }
+ return TCPCI_PARTNER_COMMON_MSG_HANDLED;
+ case CMD_DP_CONFIG:
+ if (data->dp_config_vdos > 0 &&
+ (PD_VDO_VID(vdm_header) == USB_SID_DISPLAYPORT)) {
+ tcpci_partner_send_data_msg(data, PD_DATA_VENDOR_DEF,
+ data->dp_config_vdm,
+ data->dp_config_vdos, 0);
+ data->displayport_configured = true;
+ }
+ return TCPCI_PARTNER_COMMON_MSG_HANDLED;
default:
/* TCPCI r. 2.0: Ignore unsupported commands. */
return TCPCI_PARTNER_COMMON_MSG_HANDLED;
}
}
+static enum tcpci_partner_handler_res
+tcpci_partner_common_cable_handler(struct tcpci_partner_data *data,
+ const struct tcpci_emul_msg *message,
+ enum tcpci_msg_type sop_type)
+{
+ uint32_t vdm_header = sys_get_le32(message->buf + TCPCI_MSG_HEADER_LEN);
+ uint32_t response_vdm_header;
+ uint16_t header = sys_get_le16(&message->buf[0]);
+
+ /* TODO(b/243151272): Add soft reset support */
+ /* Ensure we are replying to a VDM */
+ if (PD_HEADER_CNT(header) == 0 ||
+ PD_HEADER_TYPE(header) != PD_DATA_VENDOR_DEF ||
+ PD_HEADER_EXT(header) != 0)
+ return TCPCI_PARTNER_COMMON_MSG_NOT_HANDLED;
+
+ /*
+ * Ignore any VDMs which are not sent by an initiator. As a cable, we
+ * never expect to be the initiator processing ACKs.
+ * TODO(b/225397796): Validate VDM fields more thoroughly.
+ */
+ if (PD_VDO_CMDT(vdm_header) != CMDT_INIT || !PD_VDO_SVDM(vdm_header)) {
+ return TCPCI_PARTNER_COMMON_MSG_HANDLED;
+ }
+
+ /* If we have no cable, we must not GoodCRC */
+ if (data->cable == NULL)
+ return TCPCI_PARTNER_COMMON_MSG_NO_GOODCRC;
+
+ /* TODO(b/243151272): Add SOP'' support */
+ if (sop_type == TCPCI_MSG_SOP_PRIME_PRIME) {
+ return TCPCI_PARTNER_COMMON_MSG_NOT_HANDLED;
+ }
+
+ switch (PD_VDO_CMD(vdm_header)) {
+ case CMD_DISCOVER_IDENT:
+ if (data->cable->identity_vdos > 0) {
+ tcpci_cable_send_data_msg(data, PD_DATA_VENDOR_DEF,
+ data->cable->identity_vdm,
+ data->cable->identity_vdos,
+ TCPCI_MSG_SOP_PRIME, 0);
+ return TCPCI_PARTNER_COMMON_MSG_HANDLED;
+ }
+ /* A cable with no identity shouldn't GoodCRC */
+ return TCPCI_PARTNER_COMMON_MSG_NO_GOODCRC;
+ default:
+ /*
+ * Cable must support VDMs, so generate a NAK on unfamiliar
+ * commands
+ */
+ response_vdm_header =
+ VDO(PD_VDO_VID(vdm_header), true,
+ VDO_CMDT(CMDT_RSP_NAK) | PD_VDO_CMD(vdm_header));
+ tcpci_cable_send_data_msg(data, PD_DATA_VENDOR_DEF,
+ &response_vdm_header, 1, sop_type, 0);
+
+ return TCPCI_PARTNER_COMMON_MSG_HANDLED;
+ }
+}
+
+/**
+ * @brief Handle a received Battery Capability message from the TCPC. Save the
+ * contents to the emulator data struct for analysis.
+ *
+ * @param data Emulator state
+ * @param message Received PD message
+ * @return enum tcpci_partner_handler_res
+ */
+static enum tcpci_partner_handler_res
+tcpci_partner_common_battery_capability_handler(
+ struct tcpci_partner_data *data, const struct tcpci_emul_msg *message)
+{
+ uint16_t header = sys_get_le16(&message->buf[0]);
+ uint16_t ext_header = sys_get_le16(&message->buf[2]);
+
+ /* Validate message header */
+ __ASSERT(PD_HEADER_TYPE(header) == PD_EXT_BATTERY_CAP,
+ "wrong message type");
+ __ASSERT(PD_EXT_HEADER_DATA_SIZE(ext_header) == 9,
+ "Data size mismatch");
+
+ int index = data->battery_capabilities.index;
+
+ data->battery_capabilities.index = -1;
+
+ if (index < 0) {
+ LOG_ERR("Received a Battery Capability message but it was "
+ "never requested");
+ return TCPCI_PARTNER_COMMON_MSG_NOT_HANDLED;
+ }
+
+ __ASSERT(index < PD_BATT_MAX, "Battery index out of range");
+
+ data->battery_capabilities.bcdb[index] = (struct pd_bcdb){
+ .vid = sys_get_le16(&message->buf[4]),
+ .pid = sys_get_le16(&message->buf[6]),
+ .design_cap = sys_get_le16(&message->buf[8]),
+ .last_full_charge_cap = sys_get_le16(&message->buf[10]),
+ .battery_type = message->buf[12],
+ };
+
+ data->battery_capabilities.have_response[index] = true;
+
+ LOG_INF("Saved data for battery index (%d): vid=%04x, pid=%04x, "
+ "cap=%u, last_cap=%u, type=%02x",
+ index, data->battery_capabilities.bcdb[index].vid,
+ data->battery_capabilities.bcdb[index].pid,
+ data->battery_capabilities.bcdb[index].design_cap,
+ data->battery_capabilities.bcdb[index].last_full_charge_cap,
+ data->battery_capabilities.bcdb[index].battery_type);
+
+ return TCPCI_PARTNER_COMMON_MSG_HANDLED;
+}
+
static void tcpci_partner_common_set_vconn(struct tcpci_partner_data *data,
enum pd_vconn_role role)
{
@@ -641,9 +973,9 @@ tcpi_partner_common_handle_accept(struct tcpci_partner_data *data)
* @param TCPCI_PARTNER_COMMON_MSG_HARD_RESET Message was handled by sending
* hard reset
*/
-static enum tcpci_partner_handler_res tcpci_partner_common_sop_msg_handler(
- struct tcpci_partner_data *data,
- const struct tcpci_emul_msg *tx_msg)
+static enum tcpci_partner_handler_res
+tcpci_partner_common_sop_msg_handler(struct tcpci_partner_data *data,
+ const struct tcpci_emul_msg *tx_msg)
{
struct tcpci_partner_extension *ext;
uint16_t header;
@@ -655,15 +987,41 @@ static enum tcpci_partner_handler_res tcpci_partner_common_sop_msg_handler(
header = sys_get_le16(tx_msg->buf);
msg_type = PD_HEADER_TYPE(header);
- if (PD_HEADER_ID(header) == data->recv_msg_id &&
+ if (PD_HEADER_ID(header) == data->sop_recv_msg_id &&
msg_type != PD_CTRL_SOFT_RESET) {
/* Repeated message mark as handled */
return TCPCI_PARTNER_COMMON_MSG_HANDLED;
}
+ data->sop_recv_msg_id = PD_HEADER_ID(header);
+
+ if (PD_HEADER_EXT(header)) {
+ /* Extended message */
+
+ if (PD_HEADER_REV(header) < PD_REV30) {
+ LOG_ERR("Received extended message but current PD rev "
+ "(0x%x) does not support them.",
+ PD_HEADER_REV(header));
+ return TCPCI_PARTNER_COMMON_MSG_NOT_HANDLED;
+ }
+
+ switch (PD_HEADER_TYPE(header)) {
+ case PD_EXT_GET_BATTERY_CAP:
+ /* Not implemented */
+ LOG_INF("Got PD_EXT_GET_BATTERY_CAP");
+ return TCPCI_PARTNER_COMMON_MSG_NOT_HANDLED;
+ case PD_EXT_BATTERY_CAP:
+ /* Received a Battery Capabilities response */
+ LOG_INF("Got PD_EXT_BATTERY_CAP");
- data->recv_msg_id = PD_HEADER_ID(header);
+ return tcpci_partner_common_battery_capability_handler(
+ data, tx_msg);
+ default:
+ return TCPCI_PARTNER_COMMON_MSG_NOT_HANDLED;
+ }
+ }
if (PD_HEADER_CNT(header)) {
+ /* Data message */
switch (PD_HEADER_TYPE(header)) {
case PD_DATA_VENDOR_DEF:
return tcpci_partner_common_vdm_handler(data, tx_msg);
@@ -681,7 +1039,7 @@ static enum tcpci_partner_handler_res tcpci_partner_common_sop_msg_handler(
/* Handle control message */
switch (PD_HEADER_TYPE(header)) {
case PD_CTRL_SOFT_RESET:
- data->msg_id = 0;
+ data->sop_msg_id = 0;
tcpci_partner_send_control_msg(data, PD_CTRL_ACCEPT, 0);
for (ext = data->extensions; ext != NULL; ext = ext->next) {
@@ -781,6 +1139,9 @@ void tcpci_partner_common_disconnect(struct tcpci_partner_data *data)
tcpci_partner_clear_msg_queue(data);
tcpci_partner_stop_sender_response_timer(data);
data->tcpci_emul = NULL;
+ data->displayport_configured = false;
+ data->entered_svid = 0;
+ atomic_clear(&data->mode_enter_attempts);
}
int tcpci_partner_common_enable_pd_logging(struct tcpci_partner_data *data,
@@ -816,8 +1177,10 @@ static char *tcpci_partner_sender_names[] = {
*
* @return Number of written bytes
*/
-static __printf_like(4, 5) int tcpci_partner_print_to_buf(
- char *buf, const int buf_len, int start, const char *fmt, ...)
+static __printf_like(4, 5) int tcpci_partner_print_to_buf(char *buf,
+ const int buf_len,
+ int start,
+ const char *fmt, ...)
{
va_list ap;
int ret;
@@ -853,7 +1216,8 @@ void tcpci_partner_common_print_logged_msgs(struct tcpci_partner_data *data)
chars_in += tcpci_partner_print_to_buf(buf, buf_len, chars_in,
"===PD messages log:\n");
- SYS_SLIST_FOR_EACH_CONTAINER(&data->msg_log, msg, node) {
+ SYS_SLIST_FOR_EACH_CONTAINER(&data->msg_log, msg, node)
+ {
/*
* If there is too many messages to keep them in local buffer,
* accept possibility of lines interleaving on console and print
@@ -863,27 +1227,27 @@ void tcpci_partner_common_print_logged_msgs(struct tcpci_partner_data *data)
LOG_PRINTK("%s", buf);
chars_in = 0;
}
- chars_in += tcpci_partner_print_to_buf(buf, buf_len, chars_in,
- "\tAt %lld Msg SOP %d from %s (status 0x%x):\n",
- msg->time, msg->sop,
- tcpci_partner_sender_names[msg->sender],
- msg->status);
+ chars_in += tcpci_partner_print_to_buf(
+ buf, buf_len, chars_in,
+ "\tAt %lld Msg SOP %d from %s (status 0x%x):\n",
+ msg->time, msg->sop,
+ tcpci_partner_sender_names[msg->sender], msg->status);
header = sys_get_le16(msg->buf);
+ chars_in += tcpci_partner_print_to_buf(
+ buf, buf_len, chars_in,
+ "\t\text=%d;cnt=%d;id=%d;pr=%d;dr=%d;rev=%d;type=%d\n",
+ PD_HEADER_EXT(header), PD_HEADER_CNT(header),
+ PD_HEADER_ID(header), PD_HEADER_PROLE(header),
+ PD_HEADER_DROLE(header), PD_HEADER_REV(header),
+ PD_HEADER_TYPE(header));
chars_in += tcpci_partner_print_to_buf(buf, buf_len, chars_in,
- "\t\text=%d;cnt=%d;id=%d;pr=%d;dr=%d;rev=%d;type=%d\n",
- PD_HEADER_EXT(header), PD_HEADER_CNT(header),
- PD_HEADER_ID(header), PD_HEADER_PROLE(header),
- PD_HEADER_DROLE(header), PD_HEADER_REV(header),
- PD_HEADER_TYPE(header));
- chars_in += tcpci_partner_print_to_buf(buf, buf_len, chars_in,
- "\t\t");
+ "\t\t");
for (i = 0; i < msg->cnt; i++) {
chars_in += tcpci_partner_print_to_buf(
- buf, buf_len, chars_in,
- "%02x ", msg->buf[i]);
+ buf, buf_len, chars_in, "%02x ", msg->buf[i]);
}
chars_in += tcpci_partner_print_to_buf(buf, buf_len, chars_in,
- "\n");
+ "\n");
}
LOG_PRINTK("%s===\n", buf);
@@ -920,11 +1284,10 @@ void tcpci_partner_common_set_ams_ctrl_msg(struct tcpci_partner_data *data,
enum pd_ctrl_msg_type msg_type)
{
/* Make sure we handle one CTRL request at a time */
- zassert_equal(
- data->cur_ams_ctrl_req, PD_CTRL_INVALID,
- "More than one CTRL msg handled in parallel"
- " cur_ams_ctrl_req=%d, msg_type=%d",
- data->cur_ams_ctrl_req, msg_type);
+ zassert_equal(data->cur_ams_ctrl_req, PD_CTRL_INVALID,
+ "More than one CTRL msg handled in parallel"
+ " cur_ams_ctrl_req=%d, msg_type=%d",
+ data->cur_ams_ctrl_req, msg_type);
data->cur_ams_ctrl_req = msg_type;
}
@@ -955,7 +1318,6 @@ void tcpci_partner_received_msg_status(struct tcpci_partner_data *data,
LOG_WRN("Changing status of received message more than once");
}
*data->received_msg_status = status;
-
}
/**
@@ -971,8 +1333,7 @@ void tcpci_partner_received_msg_status(struct tcpci_partner_data *data,
static void tcpci_partner_transmit_op(const struct emul *emul,
const struct tcpci_emul_partner_ops *ops,
const struct tcpci_emul_msg *tx_msg,
- enum tcpci_msg_type type,
- int retry)
+ enum tcpci_msg_type type, int retry)
{
struct tcpci_partner_data *data =
CONTAINER_OF(ops, struct tcpci_partner_data, ops);
@@ -980,9 +1341,8 @@ static void tcpci_partner_transmit_op(const struct emul *emul,
struct tcpci_partner_extension *ext;
int ret;
- data->received_msg_status =
- tcpci_partner_log_msg(data, tx_msg, TCPCI_PARTNER_SENDER_TCPM,
- TCPCI_EMUL_TX_UNKNOWN);
+ data->received_msg_status = tcpci_partner_log_msg(
+ data, tx_msg, TCPCI_PARTNER_SENDER_TCPM, TCPCI_EMUL_TX_UNKNOWN);
ret = k_mutex_lock(&data->transmit_mutex, K_FOREVER);
if (ret) {
@@ -1002,8 +1362,8 @@ static void tcpci_partner_transmit_op(const struct emul *emul,
goto message_handled;
}
- /* Skip handling of none SOP messages */
- if (type != TCPCI_MSG_SOP) {
+ /* Skip handling of non-SOP/SOP'/SOP'' messages */
+ if (type > TCPCI_MSG_SOP_PRIME_PRIME) {
/* Never send GoodCRC for cable reset */
if (data->send_goodcrc && type != TCPCI_MSG_CABLE_RESET) {
tcpci_partner_received_msg_status(
@@ -1013,10 +1373,22 @@ static void tcpci_partner_transmit_op(const struct emul *emul,
}
/* Call common SOP handler */
- processed = tcpci_partner_common_sop_msg_handler(data, tx_msg);
- /* Always send GoodCRC for messages handled by common handler */
- if (data->send_goodcrc ||
- processed != TCPCI_PARTNER_COMMON_MSG_NOT_HANDLED) {
+ if (type == TCPCI_MSG_SOP) {
+ processed = tcpci_partner_common_sop_msg_handler(data, tx_msg);
+ } else {
+ processed =
+ tcpci_partner_common_cable_handler(data, tx_msg, type);
+ }
+ if (processed == TCPCI_PARTNER_COMMON_MSG_NO_GOODCRC) {
+ /*
+ * Fail message send if common handler knows message shouldn't
+ * transit successfully.
+ */
+ tcpci_partner_received_msg_status(data, TCPCI_EMUL_TX_FAILED);
+ goto message_handled;
+ } else if (data->send_goodcrc ||
+ processed != TCPCI_PARTNER_COMMON_MSG_NOT_HANDLED) {
+ /* Always send GoodCRC for messages handled by common handler */
tcpci_partner_received_msg_status(data, TCPCI_EMUL_TX_SUCCESS);
}
@@ -1036,8 +1408,7 @@ static void tcpci_partner_transmit_op(const struct emul *emul,
}
/* Send reject for not handled messages (PD rev 2.0) */
- tcpci_partner_send_control_msg(data,
- PD_CTRL_REJECT, 0);
+ tcpci_partner_send_control_msg(data, PD_CTRL_REJECT, 0);
message_handled:
k_mutex_unlock(&data->transmit_mutex);
@@ -1051,14 +1422,13 @@ message_handled:
* @param ops Pointer to partner operations structure
* @param rx_msg Message that was consumed by TCPM
*/
-static void tcpci_partner_rx_consumed_op(
- const struct emul *emul,
- const struct tcpci_emul_partner_ops *ops,
- const struct tcpci_emul_msg *rx_msg)
+static void
+tcpci_partner_rx_consumed_op(const struct emul *emul,
+ const struct tcpci_emul_partner_ops *ops,
+ const struct tcpci_emul_msg *rx_msg)
{
- struct tcpci_partner_msg *msg = CONTAINER_OF(rx_msg,
- struct tcpci_partner_msg,
- msg);
+ struct tcpci_partner_msg *msg =
+ CONTAINER_OF(rx_msg, struct tcpci_partner_msg, msg);
tcpci_partner_free_msg(msg);
}
@@ -1069,9 +1439,9 @@ static void tcpci_partner_rx_consumed_op(
* @param emul Pointer to TCPCI emulator
* @param ops Pointer to partner operations structure
*/
-static void tcpci_partner_disconnect_op(
- const struct emul *emul,
- const struct tcpci_emul_partner_ops *ops)
+static void
+tcpci_partner_disconnect_op(const struct emul *emul,
+ const struct tcpci_emul_partner_ops *ops)
{
struct tcpci_partner_data *data =
CONTAINER_OF(ops, struct tcpci_partner_data, ops);
@@ -1113,9 +1483,20 @@ int tcpci_partner_connect_to_tcpci(struct tcpci_partner_data *data,
data->tcpci_emul = NULL;
}
+ /* Clear any received battery capability info */
+ tcpci_partner_reset_battery_capability_state(data);
+
return ret;
}
+void tcpci_partner_reset_battery_capability_state(
+ struct tcpci_partner_data *data)
+{
+ memset(&data->battery_capabilities, 0,
+ sizeof(data->battery_capabilities));
+ data->battery_capabilities.index = -1;
+}
+
void tcpci_partner_init(struct tcpci_partner_data *data, enum pd_rev_type rev)
{
k_timer_init(&data->delayed_send, tcpci_partner_delayed_send_timer,
@@ -1144,4 +1525,13 @@ void tcpci_partner_init(struct tcpci_partner_data *data, enum pd_rev_type rev)
data->ops.rx_consumed = tcpci_partner_rx_consumed_op;
data->ops.control_change = NULL;
data->ops.disconnect = tcpci_partner_disconnect_op;
+ data->displayport_configured = false;
+ data->entered_svid = 0;
+ atomic_clear(&data->mode_enter_attempts);
+
+ /* Reset the data structure used to store battery capability responses
+ */
+ tcpci_partner_reset_battery_capability_state(data);
+
+ data->cable = NULL;
}