Files
OpenCellular/util/genvif.c
Vincent Palatin 600df851c5 Eve,Gru,Poppy,Reef: forbid DR_SWAP in RO firmware.
Currently, when we jump from RO to RW, we forget our USB PD state.
To recover from this, we send a SOFT_RESET (resetting the counters...),
then either the USB PD partner is happy about it and we can continue,
or it will issue a HARD_RESET to recover from our mismatched vision of
the current connection (e.g wrong role) resulting in a reset of VBUS.

The following use-case is still problematic:
if the system is not write-protected (ie it does USB PD negotiation in
RO EC) and we have no battery (or fully drained-one) as buffer, when we
are connected to a PD power supply, if it issues the HARD_RESET
mentioned above, we are going to brown-out.
It's happening with power-supplies supporting DR_SWAP, the RO EC will
negotiate a power-contract (as a sink), then try to reverse data role
(from UFP to DFP) to identify the power-supply. We end-up being
Sink/DFP, then when we sysjump to RW, we reset roles and send the
SOFT_RESET as Sink/DFP, the power-supply identifies the incorrect data
role and issues the HARD_RESET browning us out.
As a workaround, now we never ask for the DR_SWAP in RO firmware and
stays Sink/UFP.
This is not affecting regular write-protected machines (which are not
doing USB PD in RO EC). For developers, we are no longer doing the
DR_SWAP in RO mode, this is mostly innocuous for a regular power-supply,
but this would break the docking use-case. Normally, we will do it as
soon as we have jumped to RW, so the dock should still work unless the
developer is using the machine with RO EC (eg EC development with
soft-sync disabled).

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>

BRANCH=reef
BUG=b:35648282
TEST=Boot Snappy without battery. Verify RO image doesn't swap
data roles and soft reset issued by RW image as SNK/UFP is
accepted by the HP adapter.

Change-Id: Id184f0d24a006cd46212d04ceae02f640f5bda65
Reviewed-on: https://chromium-review.googlesource.com/461142
Commit-Ready: Daisuke Nojiri <dnojiri@chromium.org>
Tested-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-by: Sam Hurst <shurst@google.com>
2017-05-09 20:36:42 -07:00

553 lines
12 KiB
C

/* Copyright 2017 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#define _GNU_SOURCE /* for asprintf */
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <dirent.h>
#include <stdint.h>
#include <limits.h>
#include "config.h"
#include "usb_pd.h"
#include "usb_pd_tcpm.h"
#include "charge_manager.h"
#include "system.h"
#define PD_REV_2_0 1
#define PD_REV_3_0 2
#define VIF_SPEC "Revision 1.11, Version 1.0"
#define VENDOR_NAME "Google"
#define PD_SPEC_REV PD_REV_2_0
enum dtype {SNK = 0, SRC = 3, DRP = 4};
const uint32_t vdo_idh __attribute__((weak)) = 0;
const uint32_t *src_pdo;
uint32_t src_pdo_cnt;
char *yes_no(int val)
{
return val ? "YES" : "NO";
}
enum system_image_copy_t system_get_image_copy(void)
{
return SYSTEM_IMAGE_RW;
}
static void init_src_pdos(void)
{
#ifdef CONFIG_USB_PD_DYNAMIC_SRC_CAP
src_pdo_cnt = charge_manager_get_source_pdo(&src_pdo);
#else
src_pdo_cnt = pd_src_pdo_cnt;
src_pdo = pd_src_pdo;
#endif
}
static int is_src(void)
{
return src_pdo_cnt;
}
static int is_snk(void)
{
#ifdef CONFIG_USB_PD_DUAL_ROLE
return pd_snk_pdo_cnt;
#else
return 0;
#endif
}
static int is_extpwr(void)
{
if (is_src())
return !!(src_pdo[0] & PDO_FIXED_EXTERNAL);
else
return 0;
}
static int is_drp(void)
{
if (is_src())
return !!(src_pdo[0] & PDO_FIXED_DUAL_ROLE);
else
return 0;
}
#ifdef CONFIG_USB_PD_DUAL_ROLE
static char *giveback(void)
{
#ifdef CONFIG_USB_PD_GIVE_BACK
return "YES";
#else
return "NO";
#endif
}
#endif
static char *is_comms_cap(void)
{
if (is_src())
return yes_no(src_pdo[0] & PDO_FIXED_COMM_CAP);
else
return "NO";
}
static char *dr_swap_to_ufp_supported(void)
{
if (src_pdo[0] & PDO_FIXED_DATA_SWAP)
return yes_no(pd_check_data_swap(0, PD_ROLE_DFP));
return "NO";
}
static char *dr_swap_to_dfp_supported(void)
{
if (src_pdo[0] & PDO_FIXED_DATA_SWAP)
return yes_no(pd_check_data_swap(0, PD_ROLE_UFP));
return "NO";
}
static char *vconn_swap(void)
{
#ifdef CONFIG_USBC_VCONN_SWAP
return "YES";
#else
return "NO";
#endif
}
static char *try_src(void)
{
#ifdef CONFIG_USB_PD_TRY_SRC
return "YES";
#else
return "NO";
#endif
}
static char *can_act_as_host(void)
{
#ifdef CONFIG_VIF_TYPE_C_CAN_ACT_AS_HOST
return "YES";
#else
return "NO";
#endif
}
static char *can_act_as_device(void)
{
#ifdef CONFIG_USB
return "YES";
#else
return "NO";
#endif
}
static char *captive_cable(void)
{
#ifdef CONFIG_VIF_CAPTIVE_CABLE
return "YES";
#else
return "NO";
#endif
}
static char *sources_vconn(void)
{
#ifdef CONFIG_USBC_VCONN
return "YES";
#else
return "NO";
#endif
}
static char *battery_powered(void)
{
#if defined(CONFIG_BATTERY_BQ20Z453) || defined(CONFIG_BATTERY_BQ27541) || \
defined(CONFIG_BATTERY_BQ27621) || defined(CONFIG_BATTERY_RYU) || \
defined(CONFIG_BATTERY_SAMUS) || defined(CONFIG_BATTERY_SMART)
return "YES";
#else
return "NO";
#endif
}
static uint32_t product_type(void)
{
return PD_IDH_PTYPE(vdo_idh);
}
static uint32_t pid_sop(void)
{
#ifdef CONFIG_USB_PID
return CONFIG_USB_PID;
#else
return 0;
#endif
}
static uint32_t rp_value(void)
{
#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT
return CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT;
#else
return 0;
#endif
}
static char *attempts_discov_sop(enum dtype type)
{
#ifdef CONFIG_USB_PD_SIMPLE_DFP
if (type == SRC)
return "NO";
else
return "YES";
#else
return "YES";
#endif
}
static uint32_t bcddevice_sop(void)
{
#ifdef CONFIG_USB_BCD_DEV
return CONFIG_USB_BCD_DEV;
#else
return 0;
#endif
}
static uint32_t write_pdo_to_vif(FILE *vif, uint32_t pdo,
enum dtype type, uint32_t pnum)
{
uint32_t power;
if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_FIXED) {
uint32_t current = pdo & 0x3ff;
uint32_t voltage = (pdo >> 10) & 0x3ff;
power = ((current * 10) * (voltage * 50)) / 1000;
fprintf(vif, "%s_PDO_Supply_Type%d: 0\r\n",
(type == SRC) ? "Src" : "Snk", pnum);
if (type == SRC)
fprintf(vif, "Src_PDO_Peak_Current%d: 0\r\n", pnum);
fprintf(vif, "%s_PDO_Voltage%d: %d\r\n",
(type == SRC) ? "Src" : "Snk", pnum, voltage);
if (type == SRC)
fprintf(vif, "Src_PDO_Max_Current%d: %d\r\n",
pnum, current);
else
fprintf(vif, "Snk_PDO_Op_Current%d: %d\r\n",
pnum, current);
} else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_BATTERY) {
uint32_t max_voltage = (pdo >> 20) & 0x3ff;
uint32_t min_voltage = (pdo >> 10) & 0x3ff;
power = pdo & 0x3ff;
fprintf(vif, "%s_PDO_Supply_Type%d: 1\r\n",
(type == SRC) ? "Src" : "Snk", pnum);
fprintf(vif, "%s_PDO_Min_Voltage%d: %d\r\n",
(type == SRC) ? "Src" : "Snk", pnum, min_voltage);
fprintf(vif, "%s_PDO_Max_Voltage%d: %d\r\n",
(type == SRC) ? "Src" : "Snk", pnum, max_voltage);
if (type == SRC)
fprintf(vif, "Src_PDO_Max_Power%d: %d\r\n",
pnum, power);
else
fprintf(vif, "Snk_PDO_Op_Power%d: %d\r\n",
pnum, power);
} else if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_VARIABLE) {
uint32_t max_voltage = (pdo >> 20) & 0x3ff;
uint32_t min_voltage = (pdo >> 10) & 0x3ff;
uint32_t current = pdo & 0x3ff;
power = ((current * 10) * (max_voltage * 50)) / 1000;
fprintf(vif, "%s_PDO_Supply_Type%d: 2\r\n",
(type == SRC) ? "Src" : "Snk", pnum);
if (type == SRC)
fprintf(vif, "Src_PDO_Peak_Current%d: 0\r\n", pnum);
fprintf(vif, "%s_PDO_Min_Voltage%d: %d\r\n",
(type == SRC) ? "Src" : "Snk", pnum, min_voltage);
fprintf(vif, "%s_PDO_Max_Voltage%d: %d\r\n",
(type == SRC) ? "Src" : "Snk", pnum, max_voltage);
if (type == SRC)
fprintf(vif, "Src_PDO_Max_Current%d: %d\r\n",
pnum, current);
else
fprintf(vif, "Snk_PDO_Op_Current%d: %d\r\n",
pnum, current);
}
return power;
}
/**
* Carriage and line feed, '\r\n', is needed because the file is processed
* on a Windows machine.
*/
static int gen_vif(const char *name, const char *board,
const char *vif_producer)
{
FILE *vif;
enum dtype type;
if (is_drp())
type = DRP;
else if (is_src() && is_snk())
/* No DRP with SRC and SNK PDOs detected. So ignore. */
/* ie. Twinki or Plankton */
return 0;
else if (is_src())
type = SRC;
else if (is_snk())
type = SNK;
else
return 1;
/* Create VIF */
vif = fopen(name, "w+");
if (vif == NULL)
return 1;
/* Write VIF Header */
fprintf(vif, "$VIF_Specification: \"%s\"\r\n", VIF_SPEC);
fprintf(vif, "$VIF_Producer: \"%s\"\r\n", vif_producer);
fprintf(vif, "$Vendor_name: \"%s\"\r\n", VENDOR_NAME);
fprintf(vif, "$Product_Name: \"%s\"\r\n", board);
fprintf(vif, "PD_Specification_Revision: %d\r\n", PD_SPEC_REV);
fprintf(vif, "UUT_Device_Type: %d\r\n", type);
fprintf(vif, "USB_Comms_Capable: %s\r\n", is_comms_cap());
fprintf(vif, "DR_Swap_To_DFP_Supported: %s\r\n",
dr_swap_to_dfp_supported());
fprintf(vif, "DR_Swap_To_UFP_Supported: %s\r\n",
dr_swap_to_ufp_supported());
fprintf(vif, "Externally_Powered: %s\r\n", yes_no(is_extpwr()));
fprintf(vif, "VCONN_Swap_To_On_Supported: %s\r\n", vconn_swap());
fprintf(vif, "VCONN_Swap_To_Off_Supported: %s\r\n", vconn_swap());
fprintf(vif, "Responds_To_Discov_SOP: YES\r\n");
fprintf(vif, "Attempts_Discov_SOP: %s\r\n", attempts_discov_sop(type));
fprintf(vif, "SOP_Capable: YES\r\n");
fprintf(vif, "SOP_P_Capable: NO\r\n");
fprintf(vif, "SOP_PP_Capable: NO\r\n");
fprintf(vif, "SOP_P_Debug_Capable: NO\r\n");
fprintf(vif, "SOP_PP_Debug_Capable: NO\r\n");
/* Write Source Fields */
if (type == DRP || type == SRC) {
uint32_t max_power = 0;
fprintf(vif, "USB_Suspend_May_Be_Cleared: YES\r\n");
fprintf(vif, "Sends_Pings: NO\r\n");
fprintf(vif, "Num_Src_PDOs: %d\r\n", src_pdo_cnt);
/* Write Source PDOs */
{
int i;
uint32_t pwr;
for (i = 0; i < src_pdo_cnt; i++) {
pwr = write_pdo_to_vif(vif, src_pdo[i],
SRC, i+1);
if (pwr > max_power)
max_power = pwr;
}
}
fprintf(vif, "PD_Power_as_Source: %d\r\n", max_power);
}
/* Write Sink Fields */
#ifdef CONFIG_USB_PD_DUAL_ROLE
if (type == DRP || type == SNK) {
uint32_t max_power = 0;
uint32_t pwr;
int i;
fprintf(vif, "USB_Suspend_May_Be_Cleared: NO\r\n");
fprintf(vif, "GiveBack_May_Be_Set: %s\r\n", giveback());
fprintf(vif, "Higher_Capability_Set: NO\r\n");
fprintf(vif, "Num_Snk_PDOs: %d\r\n", pd_snk_pdo_cnt);
/* Write Sink PDOs */
for (i = 0; i < pd_snk_pdo_cnt; i++) {
pwr = write_pdo_to_vif(vif, pd_snk_pdo[i], SNK, i+1);
if (pwr > max_power)
max_power = pwr;
}
fprintf(vif, "PD_Power_as_Sink: %d\r\n", max_power);
}
/* Write DRP Fields */
if (type == DRP) {
fprintf(vif, "Accepts_PR_Swap_As_Src: YES\r\n");
fprintf(vif, "Accepts_PR_Swap_As_Snk: YES\r\n");
fprintf(vif, "Requests_PR_Swap_As_Src: YES\r\n");
fprintf(vif, "Requests_PR_Swap_As_Snk: YES\r\n");
}
#endif
/* SOP Discovery Fields */
fprintf(vif, "Structured_VDM_Version_SOP: 0\r\n");
fprintf(vif, "XID_SOP: 0\r\n");
fprintf(vif, "Data_Capable_as_USB_Host_SOP: %s\r\n",
can_act_as_host());
fprintf(vif, "Data_Capable_as_USB_Device_SOP: %s\r\n",
can_act_as_device());
fprintf(vif, "Product_Type_SOP: %d\r\n", product_type());
fprintf(vif, "Modal_Operation_Supported_SOP: YES\r\n");
fprintf(vif, "USB_VID_SOP: 0x%04x\r\n", USB_VID_GOOGLE);
fprintf(vif, "PID_SOP: 0x%04x\r\n", pid_sop());
fprintf(vif, "bcdDevice_SOP: 0x%04x\r\n", bcddevice_sop());
fprintf(vif, "SVID1_SOP: 0x%04x\r\n", USB_VID_GOOGLE);
fprintf(vif, "SVID1_num_modes_min_SOP: 1\r\n");
fprintf(vif, "SVID1_num_modes_max_SOP: 1\r\n");
fprintf(vif, "SVID1_num_modes_fixed_SOP: YES\r\n");
fprintf(vif, "SVID1_mode1_enter_SOP: YES\r\n");
#ifdef USB_SID_DISPLAYPORT
fprintf(vif, "SVID2_SOP: 0x%04x\r\n", USB_SID_DISPLAYPORT);
fprintf(vif, "SVID2_num_modes_min_SOP: 2\r\n");
fprintf(vif, "SVID2_num_modes_max_SOP: 2\r\n");
fprintf(vif, "SVID2_num_modes_fixed_SOP: YES\r\n");
fprintf(vif, "SVID2_mode1_enter_SOP: YES\r\n");
fprintf(vif, "SVID2_mode2_enter_SOP: YES\r\n");
fprintf(vif, "Num_SVIDs_min_SOP: 2\r\n");
fprintf(vif, "Num_SVIDs_max_SOP: 2\r\n");
fprintf(vif, "SVID_fixed_SOP: YES\r\n");
#else
fprintf(vif, "Num_SVIDs_min_SOP: 1\r\n");
fprintf(vif, "Num_SVIDs_max_SOP: 1\r\n");
fprintf(vif, "SVID_fixed_SOP: YES\r\n");
#endif
/* set Type_C_State_Machine */
{
int typec;
switch (type) {
case DRP:
typec = 2;
break;
case SNK:
typec = 1;
break;
default:
typec = 0;
}
fprintf(vif, "Type_C_State_Machine: %d\r\n", typec);
}
fprintf(vif, "Type_C_Implements_Try_SRC: %s\r\n", try_src());
fprintf(vif, "Type_C_Implements_Try_SNK: NO\r\n");
fprintf(vif, "Rp_Value: %d\r\n", rp_value());
/* None of the current devices send SOP' / SOP", so NO.*/
fprintf(vif, "Type_C_Supports_VCONN_Powered_Accessory: NO\r\n");
fprintf(vif, "Type_C_Is_VCONN_Powered_Accessory: NO\r\n");
fprintf(vif, "Type_C_Can_Act_As_Host: %s\r\n", can_act_as_host());
fprintf(vif, "Type_C_Host_Speed: 4\r\n");
fprintf(vif, "Type_C_Can_Act_As_Device: %s\r\n", can_act_as_device());
fprintf(vif, "Type_C_Device_Speed: 4\r\n");
fprintf(vif, "Type_C_Power_Source: 2\r\n");
fprintf(vif, "Type_C_BC_1_2_Support: 1\r\n");
fprintf(vif, "Type_C_Battery_Powered: %s\r\n", battery_powered());
fprintf(vif, "Type_C_Port_On_Hub: NO\r\n");
fprintf(vif, "Type_C_Supports_Audio_Accessory: NO\r\n");
fprintf(vif, "Captive_Cable: %s\r\n", captive_cable());
fprintf(vif, "Type_C_Source_Vconn: %s\r\n", sources_vconn());
fclose(vif);
return 0;
}
int main(int argc, char **argv)
{
int nopt;
int ret;
const char *out;
const char *board;
const char *vif_producer;
DIR *vifdir;
char *name;
int name_size;
const char * const short_opt = "hb:o:";
const struct option long_opts[] = {
{ "help", 0, NULL, 'h' },
{ "board", 1, NULL, 'b' },
{ "out", 1, NULL, 'o' },
{ NULL }
};
vif_producer = argv[0];
do {
nopt = getopt_long(argc, argv, short_opt, long_opts, NULL);
switch (nopt) {
case 'h': /* -h or --help */
printf("USAGE: %s -b <board name> -o <out directory>\n",
vif_producer);
return 1;
case 'b': /* -b or --board */
board = optarg;
break;
case 'o': /* -o or --out */
out = optarg;
break;
case -1:
break;
default:
abort();
}
} while (nopt != -1);
if (out == NULL || board == NULL)
return 1;
/* Make sure VIF directory exists */
vifdir = opendir(out);
if (vifdir == NULL) {
fprintf(stderr, "ERROR: %s directory does not exist.\n", out);
return 1;
}
closedir(vifdir);
init_src_pdos();
name_size = asprintf(&name, "%s/%s_vif.txt", out, board);
if (name_size < 0) {
fprintf(stderr, "ERROR: Out of memory.\n");
return 1;
}
ret = gen_vif(name, board, vif_producer);
free(name);
return ret;
}