From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Google-Smtp-Source: AIpwx4/ueFtr6Kl5P4jjNVH16noyR0+lqNVdU9+xJJ6dWfcwsQuHv+CawKUna52l385E6QCJZVi6 ARC-Seal: i=1; a=rsa-sha256; t=1524492658; cv=none; d=google.com; s=arc-20160816; b=UxX9EikI9RgxgV+lPpixfmQXt6J4CDlO+6XlMGBQE3bmbaZV1CB+2BRQ91NFFgPHSL O8+AE49TfzvFnrnA8jhKR4E2De4dXuda1yUiS7LigQuU49NhTiOXacojzNHCDuiEPaL+ SmYrqHHETAM0z6ss/GY67qqmKDPx3BoMT2Gzwa6QVz+yeZvynEvicwoCDQ6KiEj108OU t1bDp31m/awE0pkGKT0jDi86v1pUZXVPnpxc7O+rKHYVMy9venfHthef2jW2XhU5+s6T WrO5EA6GTKAJqUGa0jYKwbgFA5Rv/glKRxgZ4AMfhmTM9VL3X52N6lrczlZm6sKT2q5p zx3Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=mime-version:cc:to:subject:date:from:references:in-reply-to :message-id:arc-authentication-results; bh=rjgPhIazokcaMS6pEuPlauvFIQSxR0/cgrEJVjOZUS4=; b=txTn55nMyv4YOgsgkmFRougYJ+lf5IrmUgnjcy28tYOxj9mIEAvsqcgS88LYYHAZ1k xuGsM8vhUzwSPUudMoxVpSO4DDRZbL8WfalsLIHjiqY2dZEdeNhBDE5FZOioEa6oeIVw 0o+IrAtEUl9c/X5Nhc9C0aHDWPYxuXe8fn95JAZGBkPcBCfhUS+CKdijk0p1RqgxZe+i g8fej7iUNR6mTXoBG9POgdXO43fXWIHqt9gwfGsN4ChKVFQFO8fhxbA5hpsm4s3rXOas Y4dJtV5gLU88ZvwRtIE4X11nznpL1486JKVoBgKsBjGpvoPV+y3461FZ8oaqyAQzITD4 b3jQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of adam.thomson.opensource@diasemi.com designates 195.245.231.139 as permitted sender) smtp.mailfrom=Adam.Thomson.Opensource@diasemi.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=diasemi.com Authentication-Results: mx.google.com; spf=pass (google.com: domain of adam.thomson.opensource@diasemi.com designates 195.245.231.139 as permitted sender) smtp.mailfrom=Adam.Thomson.Opensource@diasemi.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=diasemi.com X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmplleJIrShJLcpLzFFi42KJ27nUWLfw5d0 og58H5S2aF69ns3hzfDqTRdfqnSwW1471Mllc3jWHzeJz7xFGi0XLWpktniw8w2RxeneJA6fH plWdbB7zTgZ67J+7ht1j47sdTB7v911l89j5vYHd4/MmuQD2KNbMvKT8igTWjL3PYgqmXGWs+ HPxNXsD4691jF2MXBxCAusZJWZNbQRyOIGcColTt9Yzgdi8ApkSj//NZgexOQXcJbonfWSDqH GTuNz2BqyeTcBCYvKJB0BxDg4WAVWJq1ecQcLCQOUti+ezg8wXEXjPKPHp8QcWkASzQJ1E7++ 3LBDzBSVOznwCFZeQOPjiBTPEfAOJ0wsaweISAvYS099fZQaZLyGgL9F4LBYibCjxfdY3qBJz ifuHu9gmMArOQjJ1FpKpCxiZVjFqFKcWlaUW6Rob6CUVZaZnlOQmZuboGhqY6uWmFhcnpqfmJ CYV6yXn525iBEZJPQMD4w7GCav8DjFKcjApifJanr8bJcSXlJ9SmZFYnBFfVJqTWnyIUYaDQ0 mCt+IFUE6wKDU9tSItMwcYrzBpCQ4eJRHeOJA0b3FBYm5xZjpE6hSjLse/m/t7mYVY8vLzUqX EeV1BigRAijJK8+BGwFLHJUZZKWFeRgYGBiGegtSi3MwSVPlXjOIcjErCvJtBpvBk5pXAbXoF dAQT0BEdkndAjihJREhJNTCaqWm11FVObnx+3v+CjY+7gOAR2W87P6TtjvPgjLt6NGP55/mb6 5Suq8xx3f1Ib+02djlPXtk4Ye73EbIdahcaJG/38f37U+vKq7Ytbxbz4+iZmR45S26s/nlo1p 40Hs6Npn5SYf0iBbus92eKeWZJvDsf26M6T1VQf/v6nzMumOi9yNrcxajEUpyRaKjFXFScCAA uLX6hGAMAAA== X-Env-Sender: Adam.Thomson.Opensource@diasemi.com X-Msg-Ref: server-16.tower-180.messagelabs.com!1524492656!99333180!1 X-Originating-IP: [94.185.165.51] X-SYMC-ESS-Client-Auth: outbound-route-from=pass X-StarScan-Received: X-StarScan-Version: 9.9.15; banners=-,-,- X-VirusChecked: Checked Message-ID: <34c58a40afac0b97375fc76a343d5fd41057fafa.1524490253.git.Adam.Thomson.Opensource@diasemi.com> In-Reply-To: References: From: Adam Thomson Date: Mon, 23 Apr 2018 15:10:56 +0100 Subject: [PATCH v8 1/6] typec: tcpm: Add core support for sink side PPS To: Heikki Krogerus , Guenter Roeck , Greg Kroah-Hartman , Sebastian Reichel , Hans de Goede , Jun Li CC: , , , MIME-Version: 1.0 Content-Type: text/plain X-KSE-AttachmentFiltering-Interceptor-Info: protection disabled X-KSE-ServerInfo: sw-ex-cashub01.diasemi.com, 9 X-KSE-Antivirus-Interceptor-Info: scan successful X-KSE-Antivirus-Info: Clean, bases: 23/04/2018 11:44:00 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: =?utf-8?q?1595722898650865988?= X-GMAIL-MSGID: =?utf-8?q?1598546413541538846?= X-Mailing-List: linux-kernel@vger.kernel.org List-ID: This commit adds code to handle requesting of PPS APDOs. Switching between standard PDOs and APDOs, and re-requesting an APDO to modify operating voltage/current will be triggered by an external call into TCPM. Signed-off-by: Adam Thomson Acked-by: Heikki Krogerus Reviewed-by: Guenter Roeck --- drivers/usb/typec/tcpm.c | 569 +++++++++++++++++++++++++++++++++++++++++++++-- include/linux/usb/pd.h | 4 +- include/linux/usb/tcpm.h | 1 + 3 files changed, 558 insertions(+), 16 deletions(-) diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 27192083..b160da3 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -48,6 +48,7 @@ S(SNK_DISCOVERY_DEBOUNCE_DONE), \ S(SNK_WAIT_CAPABILITIES), \ S(SNK_NEGOTIATE_CAPABILITIES), \ + S(SNK_NEGOTIATE_PPS_CAPABILITIES), \ S(SNK_TRANSITION_SINK), \ S(SNK_TRANSITION_SINK_VBUS), \ S(SNK_READY), \ @@ -167,6 +168,16 @@ struct pd_mode_data { struct typec_altmode_desc altmode_desc[SVID_DISCOVERY_MAX]; }; +struct pd_pps_data { + u32 min_volt; + u32 max_volt; + u32 max_curr; + u32 out_volt; + u32 op_curr; + bool supported; + bool active; +}; + struct tcpm_port { struct device *dev; @@ -235,6 +246,7 @@ struct tcpm_port { struct completion swap_complete; int swap_status; + unsigned int negotiated_rev; unsigned int message_id; unsigned int caps_count; unsigned int hard_reset_count; @@ -258,6 +270,7 @@ struct tcpm_port { unsigned int nr_snk_vdo; unsigned int operating_snk_mw; + bool update_sink_caps; /* Requested current / voltage */ u32 current_limit; @@ -274,8 +287,13 @@ struct tcpm_port { /* VDO to retry if UFP responder replied busy */ u32 vdo_retry; - /* Alternate mode data */ + /* PPS */ + struct pd_pps_data pps_data; + struct completion pps_complete; + bool pps_pending; + int pps_status; + /* Alternate mode data */ struct pd_mode_data mode_data; struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX]; struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX]; @@ -493,6 +511,16 @@ static void tcpm_log_source_caps(struct tcpm_port *port) pdo_max_voltage(pdo), pdo_max_power(pdo)); break; + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) + scnprintf(msg, sizeof(msg), + "%u-%u mV, %u mA", + pdo_pps_apdo_min_voltage(pdo), + pdo_pps_apdo_max_voltage(pdo), + pdo_pps_apdo_max_current(pdo)); + else + strcpy(msg, "undefined APDO"); + break; default: strcpy(msg, "undefined"); break; @@ -790,11 +818,13 @@ static int tcpm_pd_send_source_caps(struct tcpm_port *port) msg.header = PD_HEADER_LE(PD_CTRL_REJECT, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, 0); } else { msg.header = PD_HEADER_LE(PD_DATA_SOURCE_CAP, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, port->nr_src_pdo); } @@ -815,11 +845,13 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) msg.header = PD_HEADER_LE(PD_CTRL_REJECT, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, 0); } else { msg.header = PD_HEADER_LE(PD_DATA_SINK_CAP, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, port->nr_snk_pdo); } @@ -1186,6 +1218,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) msg.header = PD_HEADER_LE(PD_DATA_VENDOR_DEF, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, port->vdo_count); for (i = 0; i < port->vdo_count; i++) msg.payload[i] = cpu_to_le32(port->vdo_data[i]); @@ -1257,6 +1290,8 @@ enum pdo_err { PDO_ERR_FIXED_NOT_SORTED, PDO_ERR_VARIABLE_BATT_NOT_SORTED, PDO_ERR_DUPE_PDO, + PDO_ERR_PPS_APDO_NOT_SORTED, + PDO_ERR_DUPE_PPS_APDO, }; static const char * const pdo_err_msg[] = { @@ -1272,6 +1307,10 @@ enum pdo_err { " err: Variable/Battery supply pdos should be in increasing order of their minimum voltage", [PDO_ERR_DUPE_PDO] = " err: Variable/Batt supply pdos cannot have same min/max voltage", + [PDO_ERR_PPS_APDO_NOT_SORTED] = + " err: Programmable power supply apdos should be in increasing order of their maximum voltage", + [PDO_ERR_DUPE_PPS_APDO] = + " err: Programmable power supply apdos cannot have same min/max voltage and max current", }; static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo, @@ -1321,6 +1360,26 @@ static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo, pdo_min_voltage(pdo[i - 1]))) return PDO_ERR_DUPE_PDO; break; + /* + * The Programmable Power Supply APDOs, if present, + * shall be sent in Maximum Voltage order; + * lowest to highest. + */ + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo[i]) != APDO_TYPE_PPS) + break; + + if (pdo_pps_apdo_max_current(pdo[i]) < + pdo_pps_apdo_max_current(pdo[i - 1])) + return PDO_ERR_PPS_APDO_NOT_SORTED; + else if (pdo_pps_apdo_min_voltage(pdo[i]) == + pdo_pps_apdo_min_voltage(pdo[i - 1]) && + pdo_pps_apdo_max_voltage(pdo[i]) == + pdo_pps_apdo_max_voltage(pdo[i - 1]) && + pdo_pps_apdo_max_current(pdo[i]) == + pdo_pps_apdo_max_current(pdo[i - 1])) + return PDO_ERR_DUPE_PPS_APDO; + break; default: tcpm_log_force(port, " Unknown pdo type"); } @@ -1346,11 +1405,16 @@ static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo, /* * PD (data, control) command handling functions */ + +static int tcpm_pd_send_control(struct tcpm_port *port, + enum pd_ctrl_msg_type type); + static void tcpm_pd_data_request(struct tcpm_port *port, const struct pd_message *msg) { enum pd_data_msg_type type = pd_header_type_le(msg->header); unsigned int cnt = pd_header_cnt_le(msg->header); + unsigned int rev = pd_header_rev_le(msg->header); unsigned int i; switch (type) { @@ -1369,6 +1433,17 @@ static void tcpm_pd_data_request(struct tcpm_port *port, port->nr_source_caps); /* + * Adjust revision in subsequent message headers, as required, + * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't + * support Rev 1.0 so just do nothing in that scenario. + */ + if (rev == PD_REV10) + break; + + if (rev < PD_MAX_REV) + port->negotiated_rev = rev; + + /* * This message may be received even if VBUS is not * present. This is quite unexpected; see USB PD * specification, sections 8.3.3.6.3.1 and 8.3.3.6.3.2. @@ -1389,6 +1464,20 @@ static void tcpm_pd_data_request(struct tcpm_port *port, tcpm_queue_message(port, PD_MSG_CTRL_REJECT); break; } + + /* + * Adjust revision in subsequent message headers, as required, + * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't + * support Rev 1.0 so just reject in that scenario. + */ + if (rev == PD_REV10) { + tcpm_queue_message(port, PD_MSG_CTRL_REJECT); + break; + } + + if (rev < PD_MAX_REV) + port->negotiated_rev = rev; + port->sink_request = le32_to_cpu(msg->payload[0]); tcpm_set_state(port, SRC_NEGOTIATE_CAPABILITIES, 0); break; @@ -1413,6 +1502,15 @@ static void tcpm_pd_data_request(struct tcpm_port *port, } } +static void tcpm_pps_complete(struct tcpm_port *port, int result) +{ + if (port->pps_pending) { + port->pps_status = result; + port->pps_pending = false; + complete(&port->pps_complete); + } +} + static void tcpm_pd_ctrl_request(struct tcpm_port *port, const struct pd_message *msg) { @@ -1489,6 +1587,14 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, next_state = SNK_WAIT_CAPABILITIES; tcpm_set_state(port, next_state, 0); break; + case SNK_NEGOTIATE_PPS_CAPABILITIES: + /* Revert data back from any requested PPS updates */ + port->pps_data.out_volt = port->supply_voltage; + port->pps_data.op_curr = port->current_limit; + port->pps_status = (type == PD_CTRL_WAIT ? + -EAGAIN : -EOPNOTSUPP); + tcpm_set_state(port, SNK_READY, 0); + break; case DR_SWAP_SEND: port->swap_status = (type == PD_CTRL_WAIT ? -EAGAIN : -EOPNOTSUPP); @@ -1511,6 +1617,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, case PD_CTRL_ACCEPT: switch (port->state) { case SNK_NEGOTIATE_CAPABILITIES: + port->pps_data.active = false; + tcpm_set_state(port, SNK_TRANSITION_SINK, 0); + break; + case SNK_NEGOTIATE_PPS_CAPABILITIES: + port->pps_data.active = true; + port->supply_voltage = port->pps_data.out_volt; + port->current_limit = port->pps_data.op_curr; tcpm_set_state(port, SNK_TRANSITION_SINK, 0); break; case SOFT_RESET_SEND: @@ -1665,6 +1778,7 @@ static int tcpm_pd_send_control(struct tcpm_port *port, memset(&msg, 0, sizeof(msg)); msg.header = PD_HEADER_LE(type, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, 0); return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); @@ -1780,6 +1894,8 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, min_snk_mv = 0; int ret = -EINVAL; + port->pps_data.supported = false; + /* * Select the source PDO providing the most power which has a * matchig sink cap. @@ -1788,30 +1904,59 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, u32 pdo = port->source_caps[i]; enum pd_pdo_type type = pdo_type(pdo); - if (type == PDO_TYPE_FIXED) { + switch (type) { + case PDO_TYPE_FIXED: max_src_mv = pdo_fixed_voltage(pdo); min_src_mv = max_src_mv; - } else { + break; + case PDO_TYPE_BATT: + case PDO_TYPE_VAR: max_src_mv = pdo_max_voltage(pdo); min_src_mv = pdo_min_voltage(pdo); + break; + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) + port->pps_data.supported = true; + continue; + default: + tcpm_log(port, "Invalid source PDO type, ignoring"); + continue; } - if (type == PDO_TYPE_BATT) { - src_mw = pdo_max_power(pdo); - } else { + switch (type) { + case PDO_TYPE_FIXED: + case PDO_TYPE_VAR: src_ma = pdo_max_current(pdo); src_mw = src_ma * min_src_mv / 1000; + break; + case PDO_TYPE_BATT: + src_mw = pdo_max_power(pdo); + break; + case PDO_TYPE_APDO: + continue; + default: + tcpm_log(port, "Invalid source PDO type, ignoring"); + continue; } for (j = 0; j < port->nr_snk_pdo; j++) { pdo = port->snk_pdo[j]; - if (pdo_type(pdo) == PDO_TYPE_FIXED) { - min_snk_mv = pdo_fixed_voltage(pdo); + switch (pdo_type(pdo)) { + case PDO_TYPE_FIXED: max_snk_mv = pdo_fixed_voltage(pdo); - } else { - min_snk_mv = pdo_min_voltage(pdo); + min_snk_mv = max_snk_mv; + break; + case PDO_TYPE_BATT: + case PDO_TYPE_VAR: max_snk_mv = pdo_max_voltage(pdo); + min_snk_mv = pdo_min_voltage(pdo); + break; + case PDO_TYPE_APDO: + continue; + default: + tcpm_log(port, "Invalid sink PDO type, ignoring"); + continue; } if (max_src_mv <= max_snk_mv && @@ -1832,6 +1977,103 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, return ret; } +#define min_pps_apdo_current(x, y) \ + min(pdo_pps_apdo_max_current(x), pdo_pps_apdo_max_current(y)) + +static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) +{ + unsigned int i, j, max_mw = 0, max_mv = 0; + unsigned int min_src_mv, max_src_mv, src_ma, src_mw; + unsigned int min_snk_mv, max_snk_mv, snk_ma; + u32 pdo; + unsigned int src_pdo = 0, snk_pdo = 0; + + /* + * Select the source PPS APDO providing the most power while staying + * within the board's limits. We skip the first PDO as this is always + * 5V 3A. + */ + for (i = 1; i < port->nr_source_caps; ++i) { + pdo = port->source_caps[i]; + + switch (pdo_type(pdo)) { + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) { + tcpm_log(port, "Not PPS APDO (source), ignoring"); + continue; + } + + min_src_mv = pdo_pps_apdo_min_voltage(pdo); + max_src_mv = pdo_pps_apdo_max_voltage(pdo); + src_ma = pdo_pps_apdo_max_current(pdo); + src_mw = (src_ma * max_src_mv) / 1000; + + /* + * Now search through the sink PDOs to find a matching + * PPS APDO. Again skip the first sink PDO as this will + * always be 5V 3A. + */ + for (j = i; j < port->nr_snk_pdo; j++) { + pdo = port->snk_pdo[j]; + + switch (pdo_type(pdo)) { + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) { + tcpm_log(port, + "Not PPS APDO (sink), ignoring"); + continue; + } + + min_snk_mv = + pdo_pps_apdo_min_voltage(pdo); + max_snk_mv = + pdo_pps_apdo_max_voltage(pdo); + snk_ma = + pdo_pps_apdo_max_current(pdo); + break; + default: + tcpm_log(port, + "Not APDO type (sink), ignoring"); + continue; + } + + if (max_src_mv <= max_snk_mv && + min_src_mv >= min_snk_mv) { + /* Prefer higher voltages if available */ + if ((src_mw == max_mw && + min_src_mv > max_mv) || + src_mw > max_mw) { + src_pdo = i; + snk_pdo = j; + max_mw = src_mw; + max_mv = max_src_mv; + } + } + } + + break; + default: + tcpm_log(port, "Not APDO type (source), ignoring"); + continue; + } + } + + if (src_pdo) { + pdo = port->source_caps[src_pdo]; + + port->pps_data.min_volt = pdo_pps_apdo_min_voltage(pdo); + port->pps_data.max_volt = pdo_pps_apdo_max_voltage(pdo); + port->pps_data.max_curr = + min_pps_apdo_current(pdo, port->snk_pdo[snk_pdo]); + port->pps_data.out_volt = + min(pdo_pps_apdo_max_voltage(pdo), port->pps_data.out_volt); + port->pps_data.op_curr = + min(port->pps_data.max_curr, port->pps_data.op_curr); + } + + return src_pdo; +} + static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) { unsigned int mv, ma, mw, flags; @@ -1849,10 +2091,18 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) matching_snk_pdo = port->snk_pdo[snk_pdo_index]; type = pdo_type(pdo); - if (type == PDO_TYPE_FIXED) + switch (type) { + case PDO_TYPE_FIXED: mv = pdo_fixed_voltage(pdo); - else + break; + case PDO_TYPE_BATT: + case PDO_TYPE_VAR: mv = pdo_min_voltage(pdo); + break; + default: + tcpm_log(port, "Invalid PDO selected!"); + return -EINVAL; + } /* Select maximum available current within the sink pdo's limit */ if (type == PDO_TYPE_BATT) { @@ -1917,6 +2167,105 @@ static int tcpm_pd_send_request(struct tcpm_port *port) msg.header = PD_HEADER_LE(PD_DATA_REQUEST, port->pwr_role, port->data_role, + port->negotiated_rev, + port->message_id, 1); + msg.payload[0] = cpu_to_le32(rdo); + + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + +static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo) +{ + unsigned int out_mv, op_ma, op_mw, min_mv, max_mv, max_ma, flags; + enum pd_pdo_type type; + unsigned int src_pdo_index; + u32 pdo; + + src_pdo_index = tcpm_pd_select_pps_apdo(port); + if (!src_pdo_index) + return -EOPNOTSUPP; + + pdo = port->source_caps[src_pdo_index]; + type = pdo_type(pdo); + + switch (type) { + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) { + tcpm_log(port, "Invalid APDO selected!"); + return -EINVAL; + } + min_mv = port->pps_data.min_volt; + max_mv = port->pps_data.max_volt; + max_ma = port->pps_data.max_curr; + out_mv = port->pps_data.out_volt; + op_ma = port->pps_data.op_curr; + break; + default: + tcpm_log(port, "Invalid PDO selected!"); + return -EINVAL; + } + + flags = RDO_USB_COMM | RDO_NO_SUSPEND; + + op_mw = (op_ma * out_mv) / 1000; + if (op_mw < port->operating_snk_mw) { + /* + * Try raising current to meet power needs. If that's not enough + * then try upping the voltage. If that's still not enough + * then we've obviously chosen a PPS APDO which really isn't + * suitable so abandon ship. + */ + op_ma = (port->operating_snk_mw * 1000) / out_mv; + if ((port->operating_snk_mw * 1000) % out_mv) + ++op_ma; + op_ma += RDO_PROG_CURR_MA_STEP - (op_ma % RDO_PROG_CURR_MA_STEP); + + if (op_ma > max_ma) { + op_ma = max_ma; + out_mv = (port->operating_snk_mw * 1000) / op_ma; + if ((port->operating_snk_mw * 1000) % op_ma) + ++out_mv; + out_mv += RDO_PROG_VOLT_MV_STEP - + (out_mv % RDO_PROG_VOLT_MV_STEP); + + if (out_mv > max_mv) { + tcpm_log(port, "Invalid PPS APDO selected!"); + return -EINVAL; + } + } + } + + tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d", + port->cc_req, port->cc1, port->cc2, port->vbus_source, + port->vconn_role == TYPEC_SOURCE ? "source" : "sink", + port->polarity); + + *rdo = RDO_PROG(src_pdo_index + 1, out_mv, op_ma, flags); + + tcpm_log(port, "Requesting APDO %d: %u mV, %u mA", + src_pdo_index, out_mv, op_ma); + + port->pps_data.op_curr = op_ma; + port->pps_data.out_volt = out_mv; + + return 0; +} + +static int tcpm_pd_send_pps_request(struct tcpm_port *port) +{ + struct pd_message msg; + int ret; + u32 rdo; + + ret = tcpm_pd_build_pps_request(port, &rdo); + if (ret < 0) + return ret; + + memset(&msg, 0, sizeof(msg)); + msg.header = PD_HEADER_LE(PD_DATA_REQUEST, + port->pwr_role, + port->data_role, + port->negotiated_rev, port->message_id, 1); msg.payload[0] = cpu_to_le32(rdo); @@ -2103,6 +2452,7 @@ static void tcpm_reset_port(struct tcpm_port *port) tcpm_typec_disconnect(port); port->attached = false; port->pd_capable = false; + port->pps_data.supported = false; /* * First Rx ID should be 0; set this to a sentinel of -1 so that @@ -2120,6 +2470,8 @@ static void tcpm_reset_port(struct tcpm_port *port) tcpm_set_attached_state(port, false); port->try_src_count = 0; port->try_snk_count = 0; + port->supply_voltage = 0; + port->current_limit = 0; } static void tcpm_detach(struct tcpm_port *port) @@ -2364,6 +2716,7 @@ static void run_state_machine(struct tcpm_port *port) typec_set_pwr_opmode(port->typec_port, opmode); port->pwr_opmode = TYPEC_PWR_MODE_USB; port->caps_count = 0; + port->negotiated_rev = PD_MAX_REV; port->message_id = 0; port->rx_msgid = -1; port->explicit_contract = false; @@ -2424,6 +2777,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_swap_complete(port, 0); tcpm_typec_connect(port); + tcpm_check_send_discover(port); /* * 6.3.5 @@ -2447,6 +2801,7 @@ static void run_state_machine(struct tcpm_port *port) case SNK_UNATTACHED: if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); + tcpm_pps_complete(port, -ENOTCONN); tcpm_snk_detach(port); if (tcpm_start_drp_toggling(port)) { tcpm_set_state(port, DRP_TOGGLING, 0); @@ -2536,6 +2891,7 @@ static void run_state_machine(struct tcpm_port *port) port->cc2 : port->cc1); typec_set_pwr_opmode(port->typec_port, opmode); port->pwr_opmode = TYPEC_PWR_MODE_USB; + port->negotiated_rev = PD_MAX_REV; port->message_id = 0; port->rx_msgid = -1; port->explicit_contract = false; @@ -2606,6 +2962,24 @@ static void run_state_machine(struct tcpm_port *port) PD_T_SENDER_RESPONSE); } break; + case SNK_NEGOTIATE_PPS_CAPABILITIES: + ret = tcpm_pd_send_pps_request(port); + if (ret < 0) { + port->pps_status = ret; + /* + * If this was called due to updates to sink + * capabilities, and pps is no longer valid, we should + * safely fall back to a standard PDO. + */ + if (port->update_sink_caps) + tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); + else + tcpm_set_state(port, SNK_READY, 0); + } else { + tcpm_set_state_cond(port, hard_reset_state(port), + PD_T_SENDER_RESPONSE); + } + break; case SNK_TRANSITION_SINK: case SNK_TRANSITION_SINK_VBUS: tcpm_set_state(port, hard_reset_state(port), @@ -2613,6 +2987,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_READY: port->try_snk_count = 0; + port->update_sink_caps = false; if (port->explicit_contract) { typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_PD); @@ -2622,6 +2997,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_swap_complete(port, 0); tcpm_typec_connect(port); tcpm_check_send_discover(port); + tcpm_pps_complete(port, port->pps_status); + break; /* Accessory states */ @@ -2668,6 +3045,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; case SNK_HARD_RESET_SINK_OFF: + memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); tcpm_set_charge(port, false); tcpm_set_roles(port, false, TYPEC_SINK, TYPEC_DEVICE); @@ -2888,6 +3266,7 @@ static void run_state_machine(struct tcpm_port *port) break; case ERROR_RECOVERY: tcpm_swap_complete(port, -EPROTO); + tcpm_pps_complete(port, -EPROTO); tcpm_set_state(port, PORT_RESET, 0); break; case PORT_RESET: @@ -3470,6 +3849,162 @@ static int tcpm_try_role(const struct typec_capability *cap, int role) return ret; } +static int __maybe_unused tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr) +{ + unsigned int target_mw; + int ret; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (!port->pps_data.active) { + ret = -EOPNOTSUPP; + goto port_unlock; + } + + if (port->state != SNK_READY) { + ret = -EAGAIN; + goto port_unlock; + } + + if (op_curr > port->pps_data.max_curr) { + ret = -EINVAL; + goto port_unlock; + } + + target_mw = (op_curr * port->pps_data.out_volt) / 1000; + if (target_mw < port->operating_snk_mw) { + ret = -EINVAL; + goto port_unlock; + } + + reinit_completion(&port->pps_complete); + port->pps_data.op_curr = op_curr; + port->pps_status = 0; + port->pps_pending = true; + tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0); + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->pps_complete, + msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->pps_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + + return ret; +} + +static int __maybe_unused tcpm_pps_set_out_volt(struct tcpm_port *port, u16 out_volt) +{ + unsigned int target_mw; + int ret; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (!port->pps_data.active) { + ret = -EOPNOTSUPP; + goto port_unlock; + } + + if (port->state != SNK_READY) { + ret = -EAGAIN; + goto port_unlock; + } + + if (out_volt < port->pps_data.min_volt || + out_volt > port->pps_data.max_volt) { + ret = -EINVAL; + goto port_unlock; + } + + target_mw = (port->pps_data.op_curr * out_volt) / 1000; + if (target_mw < port->operating_snk_mw) { + ret = -EINVAL; + goto port_unlock; + } + + reinit_completion(&port->pps_complete); + port->pps_data.out_volt = out_volt; + port->pps_status = 0; + port->pps_pending = true; + tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0); + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->pps_complete, + msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->pps_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + + return ret; +} + +static int __maybe_unused tcpm_pps_activate(struct tcpm_port *port, bool activate) +{ + int ret = 0; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (!port->pps_data.supported) { + ret = -EOPNOTSUPP; + goto port_unlock; + } + + /* Trying to deactivate PPS when already deactivated so just bail */ + if (!port->pps_data.active && !activate) + goto port_unlock; + + if (port->state != SNK_READY) { + ret = -EAGAIN; + goto port_unlock; + } + + reinit_completion(&port->pps_complete); + port->pps_status = 0; + port->pps_pending = true; + + /* Trigger PPS request or move back to standard PDO contract */ + if (activate) { + port->pps_data.out_volt = port->supply_voltage; + port->pps_data.op_curr = port->current_limit; + tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0); + } else { + tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); + } + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->pps_complete, + msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->pps_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + + return ret; +} + static void tcpm_init(struct tcpm_port *port) { enum typec_cc_status cc1, cc2; @@ -3603,13 +4138,18 @@ int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, mutex_lock(&port->lock); port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo); port->operating_snk_mw = operating_snk_mw; + port->update_sink_caps = true; switch (port->state) { case SNK_NEGOTIATE_CAPABILITIES: + case SNK_NEGOTIATE_PPS_CAPABILITIES: case SNK_READY: case SNK_TRANSITION_SINK: case SNK_TRANSITION_SINK_VBUS: - tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); + if (port->pps_data.active) + tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0); + else + tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); break; default: break; @@ -3651,6 +4191,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) init_completion(&port->tx_complete); init_completion(&port->swap_complete); + init_completion(&port->pps_complete); tcpm_debugfs_init(port); if (tcpm_validate_caps(port, tcpc->config->src_pdo, @@ -3677,7 +4218,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.type = tcpc->config->type; port->typec_caps.data = tcpc->config->data; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ - port->typec_caps.pd_revision = 0x0200; /* USB-PD spec release 2.0 */ + port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ port->typec_caps.dr_set = tcpm_dr_set; port->typec_caps.pr_set = tcpm_pr_set; port->typec_caps.vconn_set = tcpm_vconn_set; diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index ff359bdf..09b570f 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -103,8 +103,8 @@ enum pd_ext_msg_type { (((cnt) & PD_HEADER_CNT_MASK) << PD_HEADER_CNT_SHIFT) | \ ((ext_hdr) ? PD_HEADER_EXT_HDR : 0)) -#define PD_HEADER_LE(type, pwr, data, id, cnt) \ - cpu_to_le16(PD_HEADER((type), (pwr), (data), PD_REV20, (id), (cnt), (0))) +#define PD_HEADER_LE(type, pwr, data, rev, id, cnt) \ + cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (0))) static inline unsigned int pd_header_cnt(u16 header) { diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index f5bda9a..b231b93 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -36,6 +36,7 @@ enum typec_cc_polarity { /* Time to wait for TCPC to complete transmit */ #define PD_T_TCPC_TX_TIMEOUT 100 /* in ms */ #define PD_ROLE_SWAP_TIMEOUT (MSEC_PER_SEC * 10) +#define PD_PPS_CTRL_TIMEOUT (MSEC_PER_SEC * 10) enum tcpm_transmit_status { TCPC_TX_SUCCESS = 0, -- 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Subject: [v8,1/6] typec: tcpm: Add core support for sink side PPS From: "Opensource \[Adam Thomson\]" Message-Id: <34c58a40afac0b97375fc76a343d5fd41057fafa.1524490253.git.Adam.Thomson.Opensource@diasemi.com> Date: Mon, 23 Apr 2018 15:10:56 +0100 To: Heikki Krogerus , Guenter Roeck , Greg Kroah-Hartman , Sebastian Reichel , Hans de Goede , Jun Li Cc: linux-usb@vger.kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, support.opensource@diasemi.com List-ID: VGhpcyBjb21taXQgYWRkcyBjb2RlIHRvIGhhbmRsZSByZXF1ZXN0aW5nIG9mIFBQUyBBUERPcy4g U3dpdGNoaW5nCmJldHdlZW4gc3RhbmRhcmQgUERPcyBhbmQgQVBET3MsIGFuZCByZS1yZXF1ZXN0 aW5nIGFuIEFQRE8gdG8KbW9kaWZ5IG9wZXJhdGluZyB2b2x0YWdlL2N1cnJlbnQgd2lsbCBiZSB0 cmlnZ2VyZWQgYnkgYW4KZXh0ZXJuYWwgY2FsbCBpbnRvIFRDUE0uCgpTaWduZWQtb2ZmLWJ5OiBB ZGFtIFRob21zb24gPEFkYW0uVGhvbXNvbi5PcGVuc291cmNlQGRpYXNlbWkuY29tPgpBY2tlZC1i eTogSGVpa2tpIEtyb2dlcnVzIDxoZWlra2kua3JvZ2VydXNAbGludXguaW50ZWwuY29tPgpSZXZp ZXdlZC1ieTogR3VlbnRlciBSb2VjayA8bGludXhAcm9lY2stdXMubmV0PgotLS0KIGRyaXZlcnMv dXNiL3R5cGVjL3RjcG0uYyB8IDU2OSArKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKystLQogaW5jbHVkZS9saW51eC91c2IvcGQuaCAgIHwgICA0ICstCiBpbmNsdWRl L2xpbnV4L3VzYi90Y3BtLmggfCAgIDEgKwogMyBmaWxlcyBjaGFuZ2VkLCA1NTggaW5zZXJ0aW9u cygrKSwgMTYgZGVsZXRpb25zKC0pCgpkaWZmIC0tZ2l0IGEvZHJpdmVycy91c2IvdHlwZWMvdGNw bS5jIGIvZHJpdmVycy91c2IvdHlwZWMvdGNwbS5jCmluZGV4IDI3MTkyMDgzLi5iMTYwZGEzIDEw MDY0NAotLS0gYS9kcml2ZXJzL3VzYi90eXBlYy90Y3BtLmMKKysrIGIvZHJpdmVycy91c2IvdHlw ZWMvdGNwbS5jCkBAIC00OCw2ICs0OCw3IEBACiAJUyhTTktfRElTQ09WRVJZX0RFQk9VTkNFX0RP TkUpLAkJXAogCVMoU05LX1dBSVRfQ0FQQUJJTElUSUVTKSwJCVwKIAlTKFNOS19ORUdPVElBVEVf Q0FQQUJJTElUSUVTKSwJCVwKKwlTKFNOS19ORUdPVElBVEVfUFBTX0NBUEFCSUxJVElFUyksCVwK IAlTKFNOS19UUkFOU0lUSU9OX1NJTkspLAkJCVwKIAlTKFNOS19UUkFOU0lUSU9OX1NJTktfVkJV UyksCQlcCiAJUyhTTktfUkVBRFkpLAkJCQlcCkBAIC0xNjcsNiArMTY4LDE2IEBAIHN0cnVjdCBw ZF9tb2RlX2RhdGEgewogCXN0cnVjdCB0eXBlY19hbHRtb2RlX2Rlc2MgYWx0bW9kZV9kZXNjW1NW SURfRElTQ09WRVJZX01BWF07CiB9OwogCitzdHJ1Y3QgcGRfcHBzX2RhdGEgeworCXUzMiBtaW5f dm9sdDsKKwl1MzIgbWF4X3ZvbHQ7CisJdTMyIG1heF9jdXJyOworCXUzMiBvdXRfdm9sdDsKKwl1 MzIgb3BfY3VycjsKKwlib29sIHN1cHBvcnRlZDsKKwlib29sIGFjdGl2ZTsKK307CisKIHN0cnVj dCB0Y3BtX3BvcnQgewogCXN0cnVjdCBkZXZpY2UgKmRldjsKIApAQCAtMjM1LDYgKzI0Niw3IEBA IHN0cnVjdCB0Y3BtX3BvcnQgewogCXN0cnVjdCBjb21wbGV0aW9uIHN3YXBfY29tcGxldGU7CiAJ aW50IHN3YXBfc3RhdHVzOwogCisJdW5zaWduZWQgaW50IG5lZ290aWF0ZWRfcmV2OwogCXVuc2ln bmVkIGludCBtZXNzYWdlX2lkOwogCXVuc2lnbmVkIGludCBjYXBzX2NvdW50OwogCXVuc2lnbmVk IGludCBoYXJkX3Jlc2V0X2NvdW50OwpAQCAtMjU4LDYgKzI3MCw3IEBAIHN0cnVjdCB0Y3BtX3Bv cnQgewogCXVuc2lnbmVkIGludCBucl9zbmtfdmRvOwogCiAJdW5zaWduZWQgaW50IG9wZXJhdGlu Z19zbmtfbXc7CisJYm9vbCB1cGRhdGVfc2lua19jYXBzOwogCiAJLyogUmVxdWVzdGVkIGN1cnJl bnQgLyB2b2x0YWdlICovCiAJdTMyIGN1cnJlbnRfbGltaXQ7CkBAIC0yNzQsOCArMjg3LDEzIEBA IHN0cnVjdCB0Y3BtX3BvcnQgewogCS8qIFZETyB0byByZXRyeSBpZiBVRlAgcmVzcG9uZGVyIHJl cGxpZWQgYnVzeSAqLwogCXUzMiB2ZG9fcmV0cnk7CiAKLQkvKiBBbHRlcm5hdGUgbW9kZSBkYXRh ICovCisJLyogUFBTICovCisJc3RydWN0IHBkX3Bwc19kYXRhIHBwc19kYXRhOworCXN0cnVjdCBj b21wbGV0aW9uIHBwc19jb21wbGV0ZTsKKwlib29sIHBwc19wZW5kaW5nOworCWludCBwcHNfc3Rh dHVzOwogCisJLyogQWx0ZXJuYXRlIG1vZGUgZGF0YSAqLwogCXN0cnVjdCBwZF9tb2RlX2RhdGEg bW9kZV9kYXRhOwogCXN0cnVjdCB0eXBlY19hbHRtb2RlICpwYXJ0bmVyX2FsdG1vZGVbU1ZJRF9E SVNDT1ZFUllfTUFYXTsKIAlzdHJ1Y3QgdHlwZWNfYWx0bW9kZSAqcG9ydF9hbHRtb2RlW1NWSURf RElTQ09WRVJZX01BWF07CkBAIC00OTMsNiArNTExLDE2IEBAIHN0YXRpYyB2b2lkIHRjcG1fbG9n X3NvdXJjZV9jYXBzKHN0cnVjdCB0Y3BtX3BvcnQgKnBvcnQpCiAJCQkJICBwZG9fbWF4X3ZvbHRh Z2UocGRvKSwKIAkJCQkgIHBkb19tYXhfcG93ZXIocGRvKSk7CiAJCQlicmVhazsKKwkJY2FzZSBQ RE9fVFlQRV9BUERPOgorCQkJaWYgKHBkb19hcGRvX3R5cGUocGRvKSA9PSBBUERPX1RZUEVfUFBT KQorCQkJCXNjbnByaW50Zihtc2csIHNpemVvZihtc2cpLAorCQkJCQkgICIldS0ldSBtViwgJXUg bUEiLAorCQkJCQkgIHBkb19wcHNfYXBkb19taW5fdm9sdGFnZShwZG8pLAorCQkJCQkgIHBkb19w cHNfYXBkb19tYXhfdm9sdGFnZShwZG8pLAorCQkJCQkgIHBkb19wcHNfYXBkb19tYXhfY3VycmVu dChwZG8pKTsKKwkJCWVsc2UKKwkJCQlzdHJjcHkobXNnLCAidW5kZWZpbmVkIEFQRE8iKTsKKwkJ CWJyZWFrOwogCQlkZWZhdWx0OgogCQkJc3RyY3B5KG1zZywgInVuZGVmaW5lZCIpOwogCQkJYnJl YWs7CkBAIC03OTAsMTEgKzgxOCwxMyBAQCBzdGF0aWMgaW50IHRjcG1fcGRfc2VuZF9zb3VyY2Vf Y2FwcyhzdHJ1Y3QgdGNwbV9wb3J0ICpwb3J0KQogCQltc2cuaGVhZGVyID0gUERfSEVBREVSX0xF KFBEX0NUUkxfUkVKRUNULAogCQkJCQkgIHBvcnQtPnB3cl9yb2xlLAogCQkJCQkgIHBvcnQtPmRh dGFfcm9sZSwKKwkJCQkJICBwb3J0LT5uZWdvdGlhdGVkX3JldiwKIAkJCQkJICBwb3J0LT5tZXNz YWdlX2lkLCAwKTsKIAl9IGVsc2UgewogCQltc2cuaGVhZGVyID0gUERfSEVBREVSX0xFKFBEX0RB VEFfU09VUkNFX0NBUCwKIAkJCQkJICBwb3J0LT5wd3Jfcm9sZSwKIAkJCQkJICBwb3J0LT5kYXRh X3JvbGUsCisJCQkJCSAgcG9ydC0+bmVnb3RpYXRlZF9yZXYsCiAJCQkJCSAgcG9ydC0+bWVzc2Fn ZV9pZCwKIAkJCQkJICBwb3J0LT5ucl9zcmNfcGRvKTsKIAl9CkBAIC04MTUsMTEgKzg0NSwxMyBA QCBzdGF0aWMgaW50IHRjcG1fcGRfc2VuZF9zaW5rX2NhcHMoc3RydWN0IHRjcG1fcG9ydCAqcG9y dCkKIAkJbXNnLmhlYWRlciA9IFBEX0hFQURFUl9MRShQRF9DVFJMX1JFSkVDVCwKIAkJCQkJICBw b3J0LT5wd3Jfcm9sZSwKIAkJCQkJICBwb3J0LT5kYXRhX3JvbGUsCisJCQkJCSAgcG9ydC0+bmVn b3RpYXRlZF9yZXYsCiAJCQkJCSAgcG9ydC0+bWVzc2FnZV9pZCwgMCk7CiAJfSBlbHNlIHsKIAkJ bXNnLmhlYWRlciA9IFBEX0hFQURFUl9MRShQRF9EQVRBX1NJTktfQ0FQLAogCQkJCQkgIHBvcnQt PnB3cl9yb2xlLAogCQkJCQkgIHBvcnQtPmRhdGFfcm9sZSwKKwkJCQkJICBwb3J0LT5uZWdvdGlh dGVkX3JldiwKIAkJCQkJICBwb3J0LT5tZXNzYWdlX2lkLAogCQkJCQkgIHBvcnQtPm5yX3Nua19w ZG8pOwogCX0KQEAgLTExODYsNiArMTIxOCw3IEBAIHN0YXRpYyB2b2lkIHZkbV9ydW5fc3RhdGVf bWFjaGluZShzdHJ1Y3QgdGNwbV9wb3J0ICpwb3J0KQogCQltc2cuaGVhZGVyID0gUERfSEVBREVS X0xFKFBEX0RBVEFfVkVORE9SX0RFRiwKIAkJCQkJICBwb3J0LT5wd3Jfcm9sZSwKIAkJCQkJICBw b3J0LT5kYXRhX3JvbGUsCisJCQkJCSAgcG9ydC0+bmVnb3RpYXRlZF9yZXYsCiAJCQkJCSAgcG9y dC0+bWVzc2FnZV9pZCwgcG9ydC0+dmRvX2NvdW50KTsKIAkJZm9yIChpID0gMDsgaSA8IHBvcnQt PnZkb19jb3VudDsgaSsrKQogCQkJbXNnLnBheWxvYWRbaV0gPSBjcHVfdG9fbGUzMihwb3J0LT52 ZG9fZGF0YVtpXSk7CkBAIC0xMjU3LDYgKzEyOTAsOCBAQCBlbnVtIHBkb19lcnIgewogCVBET19F UlJfRklYRURfTk9UX1NPUlRFRCwKIAlQRE9fRVJSX1ZBUklBQkxFX0JBVFRfTk9UX1NPUlRFRCwK IAlQRE9fRVJSX0RVUEVfUERPLAorCVBET19FUlJfUFBTX0FQRE9fTk9UX1NPUlRFRCwKKwlQRE9f RVJSX0RVUEVfUFBTX0FQRE8sCiB9OwogCiBzdGF0aWMgY29uc3QgY2hhciAqIGNvbnN0IHBkb19l cnJfbXNnW10gPSB7CkBAIC0xMjcyLDYgKzEzMDcsMTAgQEAgZW51bSBwZG9fZXJyIHsKIAkiIGVy cjogVmFyaWFibGUvQmF0dGVyeSBzdXBwbHkgcGRvcyBzaG91bGQgYmUgaW4gaW5jcmVhc2luZyBv cmRlciBvZiB0aGVpciBtaW5pbXVtIHZvbHRhZ2UiLAogCVtQRE9fRVJSX0RVUEVfUERPXSA9CiAJ IiBlcnI6IFZhcmlhYmxlL0JhdHQgc3VwcGx5IHBkb3MgY2Fubm90IGhhdmUgc2FtZSBtaW4vbWF4 IHZvbHRhZ2UiLAorCVtQRE9fRVJSX1BQU19BUERPX05PVF9TT1JURURdID0KKwkiIGVycjogUHJv Z3JhbW1hYmxlIHBvd2VyIHN1cHBseSBhcGRvcyBzaG91bGQgYmUgaW4gaW5jcmVhc2luZyBvcmRl ciBvZiB0aGVpciBtYXhpbXVtIHZvbHRhZ2UiLAorCVtQRE9fRVJSX0RVUEVfUFBTX0FQRE9dID0K KwkiIGVycjogUHJvZ3JhbW1hYmxlIHBvd2VyIHN1cHBseSBhcGRvcyBjYW5ub3QgaGF2ZSBzYW1l IG1pbi9tYXggdm9sdGFnZSBhbmQgbWF4IGN1cnJlbnQiLAogfTsKIAogc3RhdGljIGVudW0gcGRv X2VyciB0Y3BtX2NhcHNfZXJyKHN0cnVjdCB0Y3BtX3BvcnQgKnBvcnQsIGNvbnN0IHUzMiAqcGRv LApAQCAtMTMyMSw2ICsxMzYwLDI2IEBAIHN0YXRpYyBlbnVtIHBkb19lcnIgdGNwbV9jYXBzX2Vy cihzdHJ1Y3QgdGNwbV9wb3J0ICpwb3J0LCBjb25zdCB1MzIgKnBkbywKIAkJCQkJICBwZG9fbWlu X3ZvbHRhZ2UocGRvW2kgLSAxXSkpKQogCQkJCQlyZXR1cm4gUERPX0VSUl9EVVBFX1BETzsKIAkJ CQlicmVhazsKKwkJCS8qCisJCQkgKiBUaGUgUHJvZ3JhbW1hYmxlIFBvd2VyIFN1cHBseSBBUERP cywgaWYgcHJlc2VudCwKKwkJCSAqIHNoYWxsIGJlIHNlbnQgaW4gTWF4aW11bSBWb2x0YWdlIG9y ZGVyOworCQkJICogbG93ZXN0IHRvIGhpZ2hlc3QuCisJCQkgKi8KKwkJCWNhc2UgUERPX1RZUEVf QVBETzoKKwkJCQlpZiAocGRvX2FwZG9fdHlwZShwZG9baV0pICE9IEFQRE9fVFlQRV9QUFMpCisJ CQkJCWJyZWFrOworCisJCQkJaWYgKHBkb19wcHNfYXBkb19tYXhfY3VycmVudChwZG9baV0pIDwK KwkJCQkgICAgcGRvX3Bwc19hcGRvX21heF9jdXJyZW50KHBkb1tpIC0gMV0pKQorCQkJCQlyZXR1 cm4gUERPX0VSUl9QUFNfQVBET19OT1RfU09SVEVEOworCQkJCWVsc2UgaWYgKHBkb19wcHNfYXBk b19taW5fdm9sdGFnZShwZG9baV0pID09CisJCQkJCSAgcGRvX3Bwc19hcGRvX21pbl92b2x0YWdl KHBkb1tpIC0gMV0pICYmCisJCQkJCSBwZG9fcHBzX2FwZG9fbWF4X3ZvbHRhZ2UocGRvW2ldKSA9 PQorCQkJCQkgIHBkb19wcHNfYXBkb19tYXhfdm9sdGFnZShwZG9baSAtIDFdKSAmJgorCQkJCQkg cGRvX3Bwc19hcGRvX21heF9jdXJyZW50KHBkb1tpXSkgPT0KKwkJCQkJICBwZG9fcHBzX2FwZG9f bWF4X2N1cnJlbnQocGRvW2kgLSAxXSkpCisJCQkJCXJldHVybiBQRE9fRVJSX0RVUEVfUFBTX0FQ RE87CisJCQkJYnJlYWs7CiAJCQlkZWZhdWx0OgogCQkJCXRjcG1fbG9nX2ZvcmNlKHBvcnQsICIg VW5rbm93biBwZG8gdHlwZSIpOwogCQkJfQpAQCAtMTM0NiwxMSArMTQwNSwxNiBAQCBzdGF0aWMg aW50IHRjcG1fdmFsaWRhdGVfY2FwcyhzdHJ1Y3QgdGNwbV9wb3J0ICpwb3J0LCBjb25zdCB1MzIg KnBkbywKIC8qCiAgKiBQRCAoZGF0YSwgY29udHJvbCkgY29tbWFuZCBoYW5kbGluZyBmdW5jdGlv bnMKICAqLworCitzdGF0aWMgaW50IHRjcG1fcGRfc2VuZF9jb250cm9sKHN0cnVjdCB0Y3BtX3Bv cnQgKnBvcnQsCisJCQkJZW51bSBwZF9jdHJsX21zZ190eXBlIHR5cGUpOworCiBzdGF0aWMgdm9p ZCB0Y3BtX3BkX2RhdGFfcmVxdWVzdChzdHJ1Y3QgdGNwbV9wb3J0ICpwb3J0LAogCQkJCSBjb25z dCBzdHJ1Y3QgcGRfbWVzc2FnZSAqbXNnKQogewogCWVudW0gcGRfZGF0YV9tc2dfdHlwZSB0eXBl ID0gcGRfaGVhZGVyX3R5cGVfbGUobXNnLT5oZWFkZXIpOwogCXVuc2lnbmVkIGludCBjbnQgPSBw ZF9oZWFkZXJfY250X2xlKG1zZy0+aGVhZGVyKTsKKwl1bnNpZ25lZCBpbnQgcmV2ID0gcGRfaGVh ZGVyX3Jldl9sZShtc2ctPmhlYWRlcik7CiAJdW5zaWduZWQgaW50IGk7CiAKIAlzd2l0Y2ggKHR5 cGUpIHsKQEAgLTEzNjksNiArMTQzMywxNyBAQCBzdGF0aWMgdm9pZCB0Y3BtX3BkX2RhdGFfcmVx dWVzdChzdHJ1Y3QgdGNwbV9wb3J0ICpwb3J0LAogCQkJCSAgIHBvcnQtPm5yX3NvdXJjZV9jYXBz KTsKIAogCQkvKgorCQkgKiBBZGp1c3QgcmV2aXNpb24gaW4gc3Vic2VxdWVudCBtZXNzYWdlIGhl YWRlcnMsIGFzIHJlcXVpcmVkLAorCQkgKiB0byBjb21wbHkgd2l0aCA2LjIuMS4xLjUgb2YgdGhl IFVTQiBQRCAzLjAgc3BlYy4gV2UgZG9uJ3QKKwkJICogc3VwcG9ydCBSZXYgMS4wIHNvIGp1c3Qg ZG8gbm90aGluZyBpbiB0aGF0IHNjZW5hcmlvLgorCQkgKi8KKwkJaWYgKHJldiA9PSBQRF9SRVYx MCkKKwkJCWJyZWFrOworCisJCWlmIChyZXYgPCBQRF9NQVhfUkVWKQorCQkJcG9ydC0+bmVnb3Rp YXRlZF9yZXYgPSByZXY7CisKKwkJLyoKIAkJICogVGhpcyBtZXNzYWdlIG1heSBiZSByZWNlaXZl ZCBldmVuIGlmIFZCVVMgaXMgbm90CiAJCSAqIHByZXNlbnQuIFRoaXMgaXMgcXVpdGUgdW5leHBl Y3RlZDsgc2VlIFVTQiBQRAogCQkgKiBzcGVjaWZpY2F0aW9uLCBzZWN0aW9ucyA4LjMuMy42LjMu MSBhbmQgOC4zLjMuNi4zLjIuCkBAIC0xMzg5LDYgKzE0NjQsMjAgQEAgc3RhdGljIHZvaWQgdGNw bV9wZF9kYXRhX3JlcXVlc3Qoc3RydWN0IHRjcG1fcG9ydCAqcG9ydCwKIAkJCXRjcG1fcXVldWVf bWVzc2FnZShwb3J0LCBQRF9NU0dfQ1RSTF9SRUpFQ1QpOwogCQkJYnJlYWs7CiAJCX0KKworCQkv KgorCQkgKiBBZGp1c3QgcmV2aXNpb24gaW4gc3Vic2VxdWVudCBtZXNzYWdlIGhlYWRlcnMsIGFz IHJlcXVpcmVkLAorCQkgKiB0byBjb21wbHkgd2l0aCA2LjIuMS4xLjUgb2YgdGhlIFVTQiBQRCAz LjAgc3BlYy4gV2UgZG9uJ3QKKwkJICogc3VwcG9ydCBSZXYgMS4wIHNvIGp1c3QgcmVqZWN0IGlu IHRoYXQgc2NlbmFyaW8uCisJCSAqLworCQlpZiAocmV2ID09IFBEX1JFVjEwKSB7CisJCQl0Y3Bt X3F1ZXVlX21lc3NhZ2UocG9ydCwgUERfTVNHX0NUUkxfUkVKRUNUKTsKKwkJCWJyZWFrOworCQl9 CisKKwkJaWYgKHJldiA8IFBEX01BWF9SRVYpCisJCQlwb3J0LT5uZWdvdGlhdGVkX3JldiA9IHJl djsKKwogCQlwb3J0LT5zaW5rX3JlcXVlc3QgPSBsZTMyX3RvX2NwdShtc2ctPnBheWxvYWRbMF0p OwogCQl0Y3BtX3NldF9zdGF0ZShwb3J0LCBTUkNfTkVHT1RJQVRFX0NBUEFCSUxJVElFUywgMCk7 CiAJCWJyZWFrOwpAQCAtMTQxMyw2ICsxNTAyLDE1IEBAIHN0YXRpYyB2b2lkIHRjcG1fcGRfZGF0 YV9yZXF1ZXN0KHN0cnVjdCB0Y3BtX3BvcnQgKnBvcnQsCiAJfQogfQogCitzdGF0aWMgdm9pZCB0 Y3BtX3Bwc19jb21wbGV0ZShzdHJ1Y3QgdGNwbV9wb3J0ICpwb3J0LCBpbnQgcmVzdWx0KQorewor CWlmIChwb3J0LT5wcHNfcGVuZGluZykgeworCQlwb3J0LT5wcHNfc3RhdHVzID0gcmVzdWx0Owor CQlwb3J0LT5wcHNfcGVuZGluZyA9IGZhbHNlOworCQljb21wbGV0ZSgmcG9ydC0+cHBzX2NvbXBs ZXRlKTsKKwl9Cit9CisKIHN0YXRpYyB2b2lkIHRjcG1fcGRfY3RybF9yZXF1ZXN0KHN0cnVjdCB0 Y3BtX3BvcnQgKnBvcnQsCiAJCQkJIGNvbnN0IHN0cnVjdCBwZF9tZXNzYWdlICptc2cpCiB7CkBA IC0xNDg5LDYgKzE1ODcsMTQgQEAgc3RhdGljIHZvaWQgdGNwbV9wZF9jdHJsX3JlcXVlc3Qoc3Ry dWN0IHRjcG1fcG9ydCAqcG9ydCwKIAkJCQluZXh0X3N0YXRlID0gU05LX1dBSVRfQ0FQQUJJTElU SUVTOwogCQkJdGNwbV9zZXRfc3RhdGUocG9ydCwgbmV4dF9zdGF0ZSwgMCk7CiAJCQlicmVhazsK KwkJY2FzZSBTTktfTkVHT1RJQVRFX1BQU19DQVBBQklMSVRJRVM6CisJCQkvKiBSZXZlcnQgZGF0 YSBiYWNrIGZyb20gYW55IHJlcXVlc3RlZCBQUFMgdXBkYXRlcyAqLworCQkJcG9ydC0+cHBzX2Rh dGEub3V0X3ZvbHQgPSBwb3J0LT5zdXBwbHlfdm9sdGFnZTsKKwkJCXBvcnQtPnBwc19kYXRhLm9w X2N1cnIgPSBwb3J0LT5jdXJyZW50X2xpbWl0OworCQkJcG9ydC0+cHBzX3N0YXR1cyA9ICh0eXBl ID09IFBEX0NUUkxfV0FJVCA/CisJCQkJCSAgICAtRUFHQUlOIDogLUVPUE5PVFNVUFApOworCQkJ dGNwbV9zZXRfc3RhdGUocG9ydCwgU05LX1JFQURZLCAwKTsKKwkJCWJyZWFrOwogCQljYXNlIERS X1NXQVBfU0VORDoKIAkJCXBvcnQtPnN3YXBfc3RhdHVzID0gKHR5cGUgPT0gUERfQ1RSTF9XQUlU ID8KIAkJCQkJICAgICAtRUFHQUlOIDogLUVPUE5PVFNVUFApOwpAQCAtMTUxMSw2ICsxNjE3LDEz IEBAIHN0YXRpYyB2b2lkIHRjcG1fcGRfY3RybF9yZXF1ZXN0KHN0cnVjdCB0Y3BtX3BvcnQgKnBv cnQsCiAJY2FzZSBQRF9DVFJMX0FDQ0VQVDoKIAkJc3dpdGNoIChwb3J0LT5zdGF0ZSkgewogCQlj YXNlIFNOS19ORUdPVElBVEVfQ0FQQUJJTElUSUVTOgorCQkJcG9ydC0+cHBzX2RhdGEuYWN0aXZl ID0gZmFsc2U7CisJCQl0Y3BtX3NldF9zdGF0ZShwb3J0LCBTTktfVFJBTlNJVElPTl9TSU5LLCAw KTsKKwkJCWJyZWFrOworCQljYXNlIFNOS19ORUdPVElBVEVfUFBTX0NBUEFCSUxJVElFUzoKKwkJ CXBvcnQtPnBwc19kYXRhLmFjdGl2ZSA9IHRydWU7CisJCQlwb3J0LT5zdXBwbHlfdm9sdGFnZSA9 IHBvcnQtPnBwc19kYXRhLm91dF92b2x0OworCQkJcG9ydC0+Y3VycmVudF9saW1pdCA9IHBvcnQt PnBwc19kYXRhLm9wX2N1cnI7CiAJCQl0Y3BtX3NldF9zdGF0ZShwb3J0LCBTTktfVFJBTlNJVElP Tl9TSU5LLCAwKTsKIAkJCWJyZWFrOwogCQljYXNlIFNPRlRfUkVTRVRfU0VORDoKQEAgLTE2NjUs NiArMTc3OCw3IEBAIHN0YXRpYyBpbnQgdGNwbV9wZF9zZW5kX2NvbnRyb2woc3RydWN0IHRjcG1f cG9ydCAqcG9ydCwKIAltZW1zZXQoJm1zZywgMCwgc2l6ZW9mKG1zZykpOwogCW1zZy5oZWFkZXIg PSBQRF9IRUFERVJfTEUodHlwZSwgcG9ydC0+cHdyX3JvbGUsCiAJCQkJICBwb3J0LT5kYXRhX3Jv bGUsCisJCQkJICBwb3J0LT5uZWdvdGlhdGVkX3JldiwKIAkJCQkgIHBvcnQtPm1lc3NhZ2VfaWQs IDApOwogCiAJcmV0dXJuIHRjcG1fcGRfdHJhbnNtaXQocG9ydCwgVENQQ19UWF9TT1AsICZtc2cp OwpAQCAtMTc4MCw2ICsxODk0LDggQEAgc3RhdGljIGludCB0Y3BtX3BkX3NlbGVjdF9wZG8oc3Ry dWN0IHRjcG1fcG9ydCAqcG9ydCwgaW50ICpzaW5rX3BkbywKIAkJICAgICBtaW5fc25rX212ID0g MDsKIAlpbnQgcmV0ID0gLUVJTlZBTDsKIAorCXBvcnQtPnBwc19kYXRhLnN1cHBvcnRlZCA9IGZh bHNlOworCiAJLyoKIAkgKiBTZWxlY3QgdGhlIHNvdXJjZSBQRE8gcHJvdmlkaW5nIHRoZSBtb3N0 IHBvd2VyIHdoaWNoIGhhcyBhCiAJICogbWF0Y2hpZyBzaW5rIGNhcC4KQEAgLTE3ODgsMzAgKzE5 MDQsNTkgQEAgc3RhdGljIGludCB0Y3BtX3BkX3NlbGVjdF9wZG8oc3RydWN0IHRjcG1fcG9ydCAq cG9ydCwgaW50ICpzaW5rX3BkbywKIAkJdTMyIHBkbyA9IHBvcnQtPnNvdXJjZV9jYXBzW2ldOwog CQllbnVtIHBkX3Bkb190eXBlIHR5cGUgPSBwZG9fdHlwZShwZG8pOwogCi0JCWlmICh0eXBlID09 IFBET19UWVBFX0ZJWEVEKSB7CisJCXN3aXRjaCAodHlwZSkgeworCQljYXNlIFBET19UWVBFX0ZJ WEVEOgogCQkJbWF4X3NyY19tdiA9IHBkb19maXhlZF92b2x0YWdlKHBkbyk7CiAJCQltaW5fc3Jj X212ID0gbWF4X3NyY19tdjsKLQkJfSBlbHNlIHsKKwkJCWJyZWFrOworCQljYXNlIFBET19UWVBF X0JBVFQ6CisJCWNhc2UgUERPX1RZUEVfVkFSOgogCQkJbWF4X3NyY19tdiA9IHBkb19tYXhfdm9s dGFnZShwZG8pOwogCQkJbWluX3NyY19tdiA9IHBkb19taW5fdm9sdGFnZShwZG8pOworCQkJYnJl YWs7CisJCWNhc2UgUERPX1RZUEVfQVBETzoKKwkJCWlmIChwZG9fYXBkb190eXBlKHBkbykgPT0g QVBET19UWVBFX1BQUykKKwkJCQlwb3J0LT5wcHNfZGF0YS5zdXBwb3J0ZWQgPSB0cnVlOworCQkJ Y29udGludWU7CisJCWRlZmF1bHQ6CisJCQl0Y3BtX2xvZyhwb3J0LCAiSW52YWxpZCBzb3VyY2Ug UERPIHR5cGUsIGlnbm9yaW5nIik7CisJCQljb250aW51ZTsKIAkJfQogCi0JCWlmICh0eXBlID09 IFBET19UWVBFX0JBVFQpIHsKLQkJCXNyY19tdyA9IHBkb19tYXhfcG93ZXIocGRvKTsKLQkJfSBl bHNlIHsKKwkJc3dpdGNoICh0eXBlKSB7CisJCWNhc2UgUERPX1RZUEVfRklYRUQ6CisJCWNhc2Ug UERPX1RZUEVfVkFSOgogCQkJc3JjX21hID0gcGRvX21heF9jdXJyZW50KHBkbyk7CiAJCQlzcmNf bXcgPSBzcmNfbWEgKiBtaW5fc3JjX212IC8gMTAwMDsKKwkJCWJyZWFrOworCQljYXNlIFBET19U WVBFX0JBVFQ6CisJCQlzcmNfbXcgPSBwZG9fbWF4X3Bvd2VyKHBkbyk7CisJCQlicmVhazsKKwkJ Y2FzZSBQRE9fVFlQRV9BUERPOgorCQkJY29udGludWU7CisJCWRlZmF1bHQ6CisJCQl0Y3BtX2xv Zyhwb3J0LCAiSW52YWxpZCBzb3VyY2UgUERPIHR5cGUsIGlnbm9yaW5nIik7CisJCQljb250aW51 ZTsKIAkJfQogCiAJCWZvciAoaiA9IDA7IGogPCBwb3J0LT5ucl9zbmtfcGRvOyBqKyspIHsKIAkJ CXBkbyA9IHBvcnQtPnNua19wZG9bal07CiAKLQkJCWlmIChwZG9fdHlwZShwZG8pID09IFBET19U WVBFX0ZJWEVEKSB7Ci0JCQkJbWluX3Nua19tdiA9IHBkb19maXhlZF92b2x0YWdlKHBkbyk7CisJ CQlzd2l0Y2ggKHBkb190eXBlKHBkbykpIHsKKwkJCWNhc2UgUERPX1RZUEVfRklYRUQ6CiAJCQkJ bWF4X3Nua19tdiA9IHBkb19maXhlZF92b2x0YWdlKHBkbyk7Ci0JCQl9IGVsc2UgewotCQkJCW1p bl9zbmtfbXYgPSBwZG9fbWluX3ZvbHRhZ2UocGRvKTsKKwkJCQltaW5fc25rX212ID0gbWF4X3Nu a19tdjsKKwkJCQlicmVhazsKKwkJCWNhc2UgUERPX1RZUEVfQkFUVDoKKwkJCWNhc2UgUERPX1RZ UEVfVkFSOgogCQkJCW1heF9zbmtfbXYgPSBwZG9fbWF4X3ZvbHRhZ2UocGRvKTsKKwkJCQltaW5f c25rX212ID0gcGRvX21pbl92b2x0YWdlKHBkbyk7CisJCQkJYnJlYWs7CisJCQljYXNlIFBET19U WVBFX0FQRE86CisJCQkJY29udGludWU7CisJCQlkZWZhdWx0OgorCQkJCXRjcG1fbG9nKHBvcnQs ICJJbnZhbGlkIHNpbmsgUERPIHR5cGUsIGlnbm9yaW5nIik7CisJCQkJY29udGludWU7CiAJCQl9 CiAKIAkJCWlmIChtYXhfc3JjX212IDw9IG1heF9zbmtfbXYgJiYKQEAgLTE4MzIsNiArMTk3Nywx MDMgQEAgc3RhdGljIGludCB0Y3BtX3BkX3NlbGVjdF9wZG8oc3RydWN0IHRjcG1fcG9ydCAqcG9y dCwgaW50ICpzaW5rX3BkbywKIAlyZXR1cm4gcmV0OwogfQogCisjZGVmaW5lIG1pbl9wcHNfYXBk b19jdXJyZW50KHgsIHkpCVwKKwltaW4ocGRvX3Bwc19hcGRvX21heF9jdXJyZW50KHgpLCBwZG9f cHBzX2FwZG9fbWF4X2N1cnJlbnQoeSkpCisKK3N0YXRpYyB1bnNpZ25lZCBpbnQgdGNwbV9wZF9z ZWxlY3RfcHBzX2FwZG8oc3RydWN0IHRjcG1fcG9ydCAqcG9ydCkKK3sKKwl1bnNpZ25lZCBpbnQg aSwgaiwgbWF4X213ID0gMCwgbWF4X212ID0gMDsKKwl1bnNpZ25lZCBpbnQgbWluX3NyY19tdiwg bWF4X3NyY19tdiwgc3JjX21hLCBzcmNfbXc7CisJdW5zaWduZWQgaW50IG1pbl9zbmtfbXYsIG1h eF9zbmtfbXYsIHNua19tYTsKKwl1MzIgcGRvOworCXVuc2lnbmVkIGludCBzcmNfcGRvID0gMCwg c25rX3BkbyA9IDA7CisKKwkvKgorCSAqIFNlbGVjdCB0aGUgc291cmNlIFBQUyBBUERPIHByb3Zp ZGluZyB0aGUgbW9zdCBwb3dlciB3aGlsZSBzdGF5aW5nCisJICogd2l0aGluIHRoZSBib2FyZCdz IGxpbWl0cy4gV2Ugc2tpcCB0aGUgZmlyc3QgUERPIGFzIHRoaXMgaXMgYWx3YXlzCisJICogNVYg M0EuCisJICovCisJZm9yIChpID0gMTsgaSA8IHBvcnQtPm5yX3NvdXJjZV9jYXBzOyArK2kpIHsK KwkJcGRvID0gcG9ydC0+c291cmNlX2NhcHNbaV07CisKKwkJc3dpdGNoIChwZG9fdHlwZShwZG8p KSB7CisJCWNhc2UgUERPX1RZUEVfQVBETzoKKwkJCWlmIChwZG9fYXBkb190eXBlKHBkbykgIT0g QVBET19UWVBFX1BQUykgeworCQkJCXRjcG1fbG9nKHBvcnQsICJOb3QgUFBTIEFQRE8gKHNvdXJj ZSksIGlnbm9yaW5nIik7CisJCQkJY29udGludWU7CisJCQl9CisKKwkJCW1pbl9zcmNfbXYgPSBw ZG9fcHBzX2FwZG9fbWluX3ZvbHRhZ2UocGRvKTsKKwkJCW1heF9zcmNfbXYgPSBwZG9fcHBzX2Fw ZG9fbWF4X3ZvbHRhZ2UocGRvKTsKKwkJCXNyY19tYSA9IHBkb19wcHNfYXBkb19tYXhfY3VycmVu dChwZG8pOworCQkJc3JjX213ID0gKHNyY19tYSAqIG1heF9zcmNfbXYpIC8gMTAwMDsKKworCQkJ LyoKKwkJCSAqIE5vdyBzZWFyY2ggdGhyb3VnaCB0aGUgc2luayBQRE9zIHRvIGZpbmQgYSBtYXRj aGluZworCQkJICogUFBTIEFQRE8uIEFnYWluIHNraXAgdGhlIGZpcnN0IHNpbmsgUERPIGFzIHRo aXMgd2lsbAorCQkJICogYWx3YXlzIGJlIDVWIDNBLgorCQkJICovCisJCQlmb3IgKGogPSBpOyBq IDwgcG9ydC0+bnJfc25rX3BkbzsgaisrKSB7CisJCQkJcGRvID0gcG9ydC0+c25rX3Bkb1tqXTsK KworCQkJCXN3aXRjaCAocGRvX3R5cGUocGRvKSkgeworCQkJCWNhc2UgUERPX1RZUEVfQVBETzoK KwkJCQkJaWYgKHBkb19hcGRvX3R5cGUocGRvKSAhPSBBUERPX1RZUEVfUFBTKSB7CisJCQkJCQl0 Y3BtX2xvZyhwb3J0LAorCQkJCQkJCSAiTm90IFBQUyBBUERPIChzaW5rKSwgaWdub3JpbmciKTsK KwkJCQkJCWNvbnRpbnVlOworCQkJCQl9CisKKwkJCQkJbWluX3Nua19tdiA9CisJCQkJCQlwZG9f cHBzX2FwZG9fbWluX3ZvbHRhZ2UocGRvKTsKKwkJCQkJbWF4X3Nua19tdiA9CisJCQkJCQlwZG9f cHBzX2FwZG9fbWF4X3ZvbHRhZ2UocGRvKTsKKwkJCQkJc25rX21hID0KKwkJCQkJCXBkb19wcHNf YXBkb19tYXhfY3VycmVudChwZG8pOworCQkJCQlicmVhazsKKwkJCQlkZWZhdWx0OgorCQkJCQl0 Y3BtX2xvZyhwb3J0LAorCQkJCQkJICJOb3QgQVBETyB0eXBlIChzaW5rKSwgaWdub3JpbmciKTsK KwkJCQkJY29udGludWU7CisJCQkJfQorCisJCQkJaWYgKG1heF9zcmNfbXYgPD0gbWF4X3Nua19t diAmJgorCQkJCSAgICBtaW5fc3JjX212ID49IG1pbl9zbmtfbXYpIHsKKwkJCQkJLyogUHJlZmVy IGhpZ2hlciB2b2x0YWdlcyBpZiBhdmFpbGFibGUgKi8KKwkJCQkJaWYgKChzcmNfbXcgPT0gbWF4 X213ICYmCisJCQkJCSAgICAgbWluX3NyY19tdiA+IG1heF9tdikgfHwKKwkJCQkJICAgIHNyY19t dyA+IG1heF9tdykgeworCQkJCQkJc3JjX3BkbyA9IGk7CisJCQkJCQlzbmtfcGRvID0gajsKKwkJ CQkJCW1heF9tdyA9IHNyY19tdzsKKwkJCQkJCW1heF9tdiA9IG1heF9zcmNfbXY7CisJCQkJCX0K KwkJCQl9CisJCQl9CisKKwkJCWJyZWFrOworCQlkZWZhdWx0OgorCQkJdGNwbV9sb2cocG9ydCwg Ik5vdCBBUERPIHR5cGUgKHNvdXJjZSksIGlnbm9yaW5nIik7CisJCQljb250aW51ZTsKKwkJfQor CX0KKworCWlmIChzcmNfcGRvKSB7CisJCXBkbyA9IHBvcnQtPnNvdXJjZV9jYXBzW3NyY19wZG9d OworCisJCXBvcnQtPnBwc19kYXRhLm1pbl92b2x0ID0gcGRvX3Bwc19hcGRvX21pbl92b2x0YWdl KHBkbyk7CisJCXBvcnQtPnBwc19kYXRhLm1heF92b2x0ID0gcGRvX3Bwc19hcGRvX21heF92b2x0 YWdlKHBkbyk7CisJCXBvcnQtPnBwc19kYXRhLm1heF9jdXJyID0KKwkJCW1pbl9wcHNfYXBkb19j dXJyZW50KHBkbywgcG9ydC0+c25rX3Bkb1tzbmtfcGRvXSk7CisJCXBvcnQtPnBwc19kYXRhLm91 dF92b2x0ID0KKwkJCW1pbihwZG9fcHBzX2FwZG9fbWF4X3ZvbHRhZ2UocGRvKSwgcG9ydC0+cHBz X2RhdGEub3V0X3ZvbHQpOworCQlwb3J0LT5wcHNfZGF0YS5vcF9jdXJyID0KKwkJCW1pbihwb3J0 LT5wcHNfZGF0YS5tYXhfY3VyciwgcG9ydC0+cHBzX2RhdGEub3BfY3Vycik7CisJfQorCisJcmV0 dXJuIHNyY19wZG87Cit9CisKIHN0YXRpYyBpbnQgdGNwbV9wZF9idWlsZF9yZXF1ZXN0KHN0cnVj dCB0Y3BtX3BvcnQgKnBvcnQsIHUzMiAqcmRvKQogewogCXVuc2lnbmVkIGludCBtdiwgbWEsIG13 LCBmbGFnczsKQEAgLTE4NDksMTAgKzIwOTEsMTggQEAgc3RhdGljIGludCB0Y3BtX3BkX2J1aWxk X3JlcXVlc3Qoc3RydWN0IHRjcG1fcG9ydCAqcG9ydCwgdTMyICpyZG8pCiAJbWF0Y2hpbmdfc25r X3BkbyA9IHBvcnQtPnNua19wZG9bc25rX3Bkb19pbmRleF07CiAJdHlwZSA9IHBkb190eXBlKHBk byk7CiAKLQlpZiAodHlwZSA9PSBQRE9fVFlQRV9GSVhFRCkKKwlzd2l0Y2ggKHR5cGUpIHsKKwlj YXNlIFBET19UWVBFX0ZJWEVEOgogCQltdiA9IHBkb19maXhlZF92b2x0YWdlKHBkbyk7Ci0JZWxz ZQorCQlicmVhazsKKwljYXNlIFBET19UWVBFX0JBVFQ6CisJY2FzZSBQRE9fVFlQRV9WQVI6CiAJ CW12ID0gcGRvX21pbl92b2x0YWdlKHBkbyk7CisJCWJyZWFrOworCWRlZmF1bHQ6CisJCXRjcG1f bG9nKHBvcnQsICJJbnZhbGlkIFBETyBzZWxlY3RlZCEiKTsKKwkJcmV0dXJuIC1FSU5WQUw7CisJ fQogCiAJLyogU2VsZWN0IG1heGltdW0gYXZhaWxhYmxlIGN1cnJlbnQgd2l0aGluIHRoZSBzaW5r IHBkbydzIGxpbWl0ICovCiAJaWYgKHR5cGUgPT0gUERPX1RZUEVfQkFUVCkgewpAQCAtMTkxNyw2 ICsyMTY3LDEwNSBAQCBzdGF0aWMgaW50IHRjcG1fcGRfc2VuZF9yZXF1ZXN0KHN0cnVjdCB0Y3Bt X3BvcnQgKnBvcnQpCiAJbXNnLmhlYWRlciA9IFBEX0hFQURFUl9MRShQRF9EQVRBX1JFUVVFU1Qs CiAJCQkJICBwb3J0LT5wd3Jfcm9sZSwKIAkJCQkgIHBvcnQtPmRhdGFfcm9sZSwKKwkJCQkgIHBv cnQtPm5lZ290aWF0ZWRfcmV2LAorCQkJCSAgcG9ydC0+bWVzc2FnZV9pZCwgMSk7CisJbXNnLnBh eWxvYWRbMF0gPSBjcHVfdG9fbGUzMihyZG8pOworCisJcmV0dXJuIHRjcG1fcGRfdHJhbnNtaXQo cG9ydCwgVENQQ19UWF9TT1AsICZtc2cpOworfQorCitzdGF0aWMgaW50IHRjcG1fcGRfYnVpbGRf cHBzX3JlcXVlc3Qoc3RydWN0IHRjcG1fcG9ydCAqcG9ydCwgdTMyICpyZG8pCit7CisJdW5zaWdu ZWQgaW50IG91dF9tdiwgb3BfbWEsIG9wX213LCBtaW5fbXYsIG1heF9tdiwgbWF4X21hLCBmbGFn czsKKwllbnVtIHBkX3Bkb190eXBlIHR5cGU7CisJdW5zaWduZWQgaW50IHNyY19wZG9faW5kZXg7 CisJdTMyIHBkbzsKKworCXNyY19wZG9faW5kZXggPSB0Y3BtX3BkX3NlbGVjdF9wcHNfYXBkbyhw b3J0KTsKKwlpZiAoIXNyY19wZG9faW5kZXgpCisJCXJldHVybiAtRU9QTk9UU1VQUDsKKworCXBk byA9IHBvcnQtPnNvdXJjZV9jYXBzW3NyY19wZG9faW5kZXhdOworCXR5cGUgPSBwZG9fdHlwZShw ZG8pOworCisJc3dpdGNoICh0eXBlKSB7CisJY2FzZSBQRE9fVFlQRV9BUERPOgorCQlpZiAocGRv X2FwZG9fdHlwZShwZG8pICE9IEFQRE9fVFlQRV9QUFMpIHsKKwkJCXRjcG1fbG9nKHBvcnQsICJJ bnZhbGlkIEFQRE8gc2VsZWN0ZWQhIik7CisJCQlyZXR1cm4gLUVJTlZBTDsKKwkJfQorCQltaW5f bXYgPSBwb3J0LT5wcHNfZGF0YS5taW5fdm9sdDsKKwkJbWF4X212ID0gcG9ydC0+cHBzX2RhdGEu bWF4X3ZvbHQ7CisJCW1heF9tYSA9IHBvcnQtPnBwc19kYXRhLm1heF9jdXJyOworCQlvdXRfbXYg PSBwb3J0LT5wcHNfZGF0YS5vdXRfdm9sdDsKKwkJb3BfbWEgPSBwb3J0LT5wcHNfZGF0YS5vcF9j dXJyOworCQlicmVhazsKKwlkZWZhdWx0OgorCQl0Y3BtX2xvZyhwb3J0LCAiSW52YWxpZCBQRE8g c2VsZWN0ZWQhIik7CisJCXJldHVybiAtRUlOVkFMOworCX0KKworCWZsYWdzID0gUkRPX1VTQl9D T01NIHwgUkRPX05PX1NVU1BFTkQ7CisKKwlvcF9tdyA9IChvcF9tYSAqIG91dF9tdikgLyAxMDAw OworCWlmIChvcF9tdyA8IHBvcnQtPm9wZXJhdGluZ19zbmtfbXcpIHsKKwkJLyoKKwkJICogVHJ5 IHJhaXNpbmcgY3VycmVudCB0byBtZWV0IHBvd2VyIG5lZWRzLiBJZiB0aGF0J3Mgbm90IGVub3Vn aAorCQkgKiB0aGVuIHRyeSB1cHBpbmcgdGhlIHZvbHRhZ2UuIElmIHRoYXQncyBzdGlsbCBub3Qg ZW5vdWdoCisJCSAqIHRoZW4gd2UndmUgb2J2aW91c2x5IGNob3NlbiBhIFBQUyBBUERPIHdoaWNo IHJlYWxseSBpc24ndAorCQkgKiBzdWl0YWJsZSBzbyBhYmFuZG9uIHNoaXAuCisJCSAqLworCQlv cF9tYSA9IChwb3J0LT5vcGVyYXRpbmdfc25rX213ICogMTAwMCkgLyBvdXRfbXY7CisJCWlmICgo cG9ydC0+b3BlcmF0aW5nX3Nua19tdyAqIDEwMDApICUgb3V0X212KQorCQkJKytvcF9tYTsKKwkJ b3BfbWEgKz0gUkRPX1BST0dfQ1VSUl9NQV9TVEVQIC0gKG9wX21hICUgUkRPX1BST0dfQ1VSUl9N QV9TVEVQKTsKKworCQlpZiAob3BfbWEgPiBtYXhfbWEpIHsKKwkJCW9wX21hID0gbWF4X21hOwor CQkJb3V0X212ID0gKHBvcnQtPm9wZXJhdGluZ19zbmtfbXcgKiAxMDAwKSAvIG9wX21hOworCQkJ aWYgKChwb3J0LT5vcGVyYXRpbmdfc25rX213ICogMTAwMCkgJSBvcF9tYSkKKwkJCQkrK291dF9t djsKKwkJCW91dF9tdiArPSBSRE9fUFJPR19WT0xUX01WX1NURVAgLQorCQkJCSAgKG91dF9tdiAl IFJET19QUk9HX1ZPTFRfTVZfU1RFUCk7CisKKwkJCWlmIChvdXRfbXYgPiBtYXhfbXYpIHsKKwkJ CQl0Y3BtX2xvZyhwb3J0LCAiSW52YWxpZCBQUFMgQVBETyBzZWxlY3RlZCEiKTsKKwkJCQlyZXR1 cm4gLUVJTlZBTDsKKwkJCX0KKwkJfQorCX0KKworCXRjcG1fbG9nKHBvcnQsICJjYz0lZCBjYzE9 JWQgY2MyPSVkIHZidXM9JWQgdmNvbm49JXMgcG9sYXJpdHk9JWQiLAorCQkgcG9ydC0+Y2NfcmVx LCBwb3J0LT5jYzEsIHBvcnQtPmNjMiwgcG9ydC0+dmJ1c19zb3VyY2UsCisJCSBwb3J0LT52Y29u bl9yb2xlID09IFRZUEVDX1NPVVJDRSA/ICJzb3VyY2UiIDogInNpbmsiLAorCQkgcG9ydC0+cG9s YXJpdHkpOworCisJKnJkbyA9IFJET19QUk9HKHNyY19wZG9faW5kZXggKyAxLCBvdXRfbXYsIG9w X21hLCBmbGFncyk7CisKKwl0Y3BtX2xvZyhwb3J0LCAiUmVxdWVzdGluZyBBUERPICVkOiAldSBt ViwgJXUgbUEiLAorCQkgc3JjX3Bkb19pbmRleCwgb3V0X212LCBvcF9tYSk7CisKKwlwb3J0LT5w cHNfZGF0YS5vcF9jdXJyID0gb3BfbWE7CisJcG9ydC0+cHBzX2RhdGEub3V0X3ZvbHQgPSBvdXRf bXY7CisKKwlyZXR1cm4gMDsKK30KKworc3RhdGljIGludCB0Y3BtX3BkX3NlbmRfcHBzX3JlcXVl c3Qoc3RydWN0IHRjcG1fcG9ydCAqcG9ydCkKK3sKKwlzdHJ1Y3QgcGRfbWVzc2FnZSBtc2c7CisJ aW50IHJldDsKKwl1MzIgcmRvOworCisJcmV0ID0gdGNwbV9wZF9idWlsZF9wcHNfcmVxdWVzdChw b3J0LCAmcmRvKTsKKwlpZiAocmV0IDwgMCkKKwkJcmV0dXJuIHJldDsKKworCW1lbXNldCgmbXNn LCAwLCBzaXplb2YobXNnKSk7CisJbXNnLmhlYWRlciA9IFBEX0hFQURFUl9MRShQRF9EQVRBX1JF UVVFU1QsCisJCQkJICBwb3J0LT5wd3Jfcm9sZSwKKwkJCQkgIHBvcnQtPmRhdGFfcm9sZSwKKwkJ CQkgIHBvcnQtPm5lZ290aWF0ZWRfcmV2LAogCQkJCSAgcG9ydC0+bWVzc2FnZV9pZCwgMSk7CiAJ bXNnLnBheWxvYWRbMF0gPSBjcHVfdG9fbGUzMihyZG8pOwogCkBAIC0yMTAzLDYgKzI0NTIsNyBA QCBzdGF0aWMgdm9pZCB0Y3BtX3Jlc2V0X3BvcnQoc3RydWN0IHRjcG1fcG9ydCAqcG9ydCkKIAl0 Y3BtX3R5cGVjX2Rpc2Nvbm5lY3QocG9ydCk7CiAJcG9ydC0+YXR0YWNoZWQgPSBmYWxzZTsKIAlw b3J0LT5wZF9jYXBhYmxlID0gZmFsc2U7CisJcG9ydC0+cHBzX2RhdGEuc3VwcG9ydGVkID0gZmFs c2U7CiAKIAkvKgogCSAqIEZpcnN0IFJ4IElEIHNob3VsZCBiZSAwOyBzZXQgdGhpcyB0byBhIHNl bnRpbmVsIG9mIC0xIHNvIHRoYXQKQEAgLTIxMjAsNiArMjQ3MCw4IEBAIHN0YXRpYyB2b2lkIHRj cG1fcmVzZXRfcG9ydChzdHJ1Y3QgdGNwbV9wb3J0ICpwb3J0KQogCXRjcG1fc2V0X2F0dGFjaGVk X3N0YXRlKHBvcnQsIGZhbHNlKTsKIAlwb3J0LT50cnlfc3JjX2NvdW50ID0gMDsKIAlwb3J0LT50 cnlfc25rX2NvdW50ID0gMDsKKwlwb3J0LT5zdXBwbHlfdm9sdGFnZSA9IDA7CisJcG9ydC0+Y3Vy cmVudF9saW1pdCA9IDA7CiB9CiAKIHN0YXRpYyB2b2lkIHRjcG1fZGV0YWNoKHN0cnVjdCB0Y3Bt X3BvcnQgKnBvcnQpCkBAIC0yMzY0LDYgKzI3MTYsNyBAQCBzdGF0aWMgdm9pZCBydW5fc3RhdGVf bWFjaGluZShzdHJ1Y3QgdGNwbV9wb3J0ICpwb3J0KQogCQl0eXBlY19zZXRfcHdyX29wbW9kZShw b3J0LT50eXBlY19wb3J0LCBvcG1vZGUpOwogCQlwb3J0LT5wd3Jfb3Btb2RlID0gVFlQRUNfUFdS X01PREVfVVNCOwogCQlwb3J0LT5jYXBzX2NvdW50ID0gMDsKKwkJcG9ydC0+bmVnb3RpYXRlZF9y ZXYgPSBQRF9NQVhfUkVWOwogCQlwb3J0LT5tZXNzYWdlX2lkID0gMDsKIAkJcG9ydC0+cnhfbXNn aWQgPSAtMTsKIAkJcG9ydC0+ZXhwbGljaXRfY29udHJhY3QgPSBmYWxzZTsKQEAgLTI0MjQsNiAr Mjc3Nyw3IEBAIHN0YXRpYyB2b2lkIHJ1bl9zdGF0ZV9tYWNoaW5lKHN0cnVjdCB0Y3BtX3BvcnQg KnBvcnQpCiAKIAkJdGNwbV9zd2FwX2NvbXBsZXRlKHBvcnQsIDApOwogCQl0Y3BtX3R5cGVjX2Nv bm5lY3QocG9ydCk7CisKIAkJdGNwbV9jaGVja19zZW5kX2Rpc2NvdmVyKHBvcnQpOwogCQkvKgog CQkgKiA2LjMuNQpAQCAtMjQ0Nyw2ICsyODAxLDcgQEAgc3RhdGljIHZvaWQgcnVuX3N0YXRlX21h Y2hpbmUoc3RydWN0IHRjcG1fcG9ydCAqcG9ydCkKIAljYXNlIFNOS19VTkFUVEFDSEVEOgogCQlp ZiAoIXBvcnQtPm5vbl9wZF9yb2xlX3N3YXApCiAJCQl0Y3BtX3N3YXBfY29tcGxldGUocG9ydCwg LUVOT1RDT05OKTsKKwkJdGNwbV9wcHNfY29tcGxldGUocG9ydCwgLUVOT1RDT05OKTsKIAkJdGNw bV9zbmtfZGV0YWNoKHBvcnQpOwogCQlpZiAodGNwbV9zdGFydF9kcnBfdG9nZ2xpbmcocG9ydCkp IHsKIAkJCXRjcG1fc2V0X3N0YXRlKHBvcnQsIERSUF9UT0dHTElORywgMCk7CkBAIC0yNTM2LDYg KzI4OTEsNyBAQCBzdGF0aWMgdm9pZCBydW5fc3RhdGVfbWFjaGluZShzdHJ1Y3QgdGNwbV9wb3J0 ICpwb3J0KQogCQkJCQkgICAgICBwb3J0LT5jYzIgOiBwb3J0LT5jYzEpOwogCQl0eXBlY19zZXRf cHdyX29wbW9kZShwb3J0LT50eXBlY19wb3J0LCBvcG1vZGUpOwogCQlwb3J0LT5wd3Jfb3Btb2Rl ID0gVFlQRUNfUFdSX01PREVfVVNCOworCQlwb3J0LT5uZWdvdGlhdGVkX3JldiA9IFBEX01BWF9S RVY7CiAJCXBvcnQtPm1lc3NhZ2VfaWQgPSAwOwogCQlwb3J0LT5yeF9tc2dpZCA9IC0xOwogCQlw b3J0LT5leHBsaWNpdF9jb250cmFjdCA9IGZhbHNlOwpAQCAtMjYwNiw2ICsyOTYyLDI0IEBAIHN0 YXRpYyB2b2lkIHJ1bl9zdGF0ZV9tYWNoaW5lKHN0cnVjdCB0Y3BtX3BvcnQgKnBvcnQpCiAJCQkJ CSAgICBQRF9UX1NFTkRFUl9SRVNQT05TRSk7CiAJCX0KIAkJYnJlYWs7CisJY2FzZSBTTktfTkVH T1RJQVRFX1BQU19DQVBBQklMSVRJRVM6CisJCXJldCA9IHRjcG1fcGRfc2VuZF9wcHNfcmVxdWVz dChwb3J0KTsKKwkJaWYgKHJldCA8IDApIHsKKwkJCXBvcnQtPnBwc19zdGF0dXMgPSByZXQ7CisJ CQkvKgorCQkJICogSWYgdGhpcyB3YXMgY2FsbGVkIGR1ZSB0byB1cGRhdGVzIHRvIHNpbmsKKwkJ CSAqIGNhcGFiaWxpdGllcywgYW5kIHBwcyBpcyBubyBsb25nZXIgdmFsaWQsIHdlIHNob3VsZAor CQkJICogc2FmZWx5IGZhbGwgYmFjayB0byBhIHN0YW5kYXJkIFBETy4KKwkJCSAqLworCQkJaWYg KHBvcnQtPnVwZGF0ZV9zaW5rX2NhcHMpCisJCQkJdGNwbV9zZXRfc3RhdGUocG9ydCwgU05LX05F R09USUFURV9DQVBBQklMSVRJRVMsIDApOworCQkJZWxzZQorCQkJCXRjcG1fc2V0X3N0YXRlKHBv cnQsIFNOS19SRUFEWSwgMCk7CisJCX0gZWxzZSB7CisJCQl0Y3BtX3NldF9zdGF0ZV9jb25kKHBv cnQsIGhhcmRfcmVzZXRfc3RhdGUocG9ydCksCisJCQkJCSAgICBQRF9UX1NFTkRFUl9SRVNQT05T RSk7CisJCX0KKwkJYnJlYWs7CiAJY2FzZSBTTktfVFJBTlNJVElPTl9TSU5LOgogCWNhc2UgU05L X1RSQU5TSVRJT05fU0lOS19WQlVTOgogCQl0Y3BtX3NldF9zdGF0ZShwb3J0LCBoYXJkX3Jlc2V0 X3N0YXRlKHBvcnQpLApAQCAtMjYxMyw2ICsyOTg3LDcgQEAgc3RhdGljIHZvaWQgcnVuX3N0YXRl X21hY2hpbmUoc3RydWN0IHRjcG1fcG9ydCAqcG9ydCkKIAkJYnJlYWs7CiAJY2FzZSBTTktfUkVB RFk6CiAJCXBvcnQtPnRyeV9zbmtfY291bnQgPSAwOworCQlwb3J0LT51cGRhdGVfc2lua19jYXBz ID0gZmFsc2U7CiAJCWlmIChwb3J0LT5leHBsaWNpdF9jb250cmFjdCkgewogCQkJdHlwZWNfc2V0 X3B3cl9vcG1vZGUocG9ydC0+dHlwZWNfcG9ydCwKIAkJCQkJICAgICBUWVBFQ19QV1JfTU9ERV9Q RCk7CkBAIC0yNjIyLDYgKzI5OTcsOCBAQCBzdGF0aWMgdm9pZCBydW5fc3RhdGVfbWFjaGluZShz dHJ1Y3QgdGNwbV9wb3J0ICpwb3J0KQogCQl0Y3BtX3N3YXBfY29tcGxldGUocG9ydCwgMCk7CiAJ CXRjcG1fdHlwZWNfY29ubmVjdChwb3J0KTsKIAkJdGNwbV9jaGVja19zZW5kX2Rpc2NvdmVyKHBv cnQpOworCQl0Y3BtX3Bwc19jb21wbGV0ZShwb3J0LCBwb3J0LT5wcHNfc3RhdHVzKTsKKwogCQli cmVhazsKIAogCS8qIEFjY2Vzc29yeSBzdGF0ZXMgKi8KQEAgLTI2NjgsNiArMzA0NSw3IEBAIHN0 YXRpYyB2b2lkIHJ1bl9zdGF0ZV9tYWNoaW5lKHN0cnVjdCB0Y3BtX3BvcnQgKnBvcnQpCiAJCXRj cG1fc2V0X3N0YXRlKHBvcnQsIFNSQ19VTkFUVEFDSEVELCBQRF9UX1BTX1NPVVJDRV9PTik7CiAJ CWJyZWFrOwogCWNhc2UgU05LX0hBUkRfUkVTRVRfU0lOS19PRkY6CisJCW1lbXNldCgmcG9ydC0+ cHBzX2RhdGEsIDAsIHNpemVvZihwb3J0LT5wcHNfZGF0YSkpOwogCQl0Y3BtX3NldF92Y29ubihw b3J0LCBmYWxzZSk7CiAJCXRjcG1fc2V0X2NoYXJnZShwb3J0LCBmYWxzZSk7CiAJCXRjcG1fc2V0 X3JvbGVzKHBvcnQsIGZhbHNlLCBUWVBFQ19TSU5LLCBUWVBFQ19ERVZJQ0UpOwpAQCAtMjg4OCw2 ICszMjY2LDcgQEAgc3RhdGljIHZvaWQgcnVuX3N0YXRlX21hY2hpbmUoc3RydWN0IHRjcG1fcG9y dCAqcG9ydCkKIAkJYnJlYWs7CiAJY2FzZSBFUlJPUl9SRUNPVkVSWToKIAkJdGNwbV9zd2FwX2Nv bXBsZXRlKHBvcnQsIC1FUFJPVE8pOworCQl0Y3BtX3Bwc19jb21wbGV0ZShwb3J0LCAtRVBST1RP KTsKIAkJdGNwbV9zZXRfc3RhdGUocG9ydCwgUE9SVF9SRVNFVCwgMCk7CiAJCWJyZWFrOwogCWNh c2UgUE9SVF9SRVNFVDoKQEAgLTM0NzAsNiArMzg0OSwxNjIgQEAgc3RhdGljIGludCB0Y3BtX3Ry eV9yb2xlKGNvbnN0IHN0cnVjdCB0eXBlY19jYXBhYmlsaXR5ICpjYXAsIGludCByb2xlKQogCXJl dHVybiByZXQ7CiB9CiAKK3N0YXRpYyBpbnQgX19tYXliZV91bnVzZWQgdGNwbV9wcHNfc2V0X29w X2N1cnIoc3RydWN0IHRjcG1fcG9ydCAqcG9ydCwgdTE2IG9wX2N1cnIpCit7CisJdW5zaWduZWQg aW50IHRhcmdldF9tdzsKKwlpbnQgcmV0OworCisJbXV0ZXhfbG9jaygmcG9ydC0+c3dhcF9sb2Nr KTsKKwltdXRleF9sb2NrKCZwb3J0LT5sb2NrKTsKKworCWlmICghcG9ydC0+cHBzX2RhdGEuYWN0 aXZlKSB7CisJCXJldCA9IC1FT1BOT1RTVVBQOworCQlnb3RvIHBvcnRfdW5sb2NrOworCX0KKwor CWlmIChwb3J0LT5zdGF0ZSAhPSBTTktfUkVBRFkpIHsKKwkJcmV0ID0gLUVBR0FJTjsKKwkJZ290 byBwb3J0X3VubG9jazsKKwl9CisKKwlpZiAob3BfY3VyciA+IHBvcnQtPnBwc19kYXRhLm1heF9j dXJyKSB7CisJCXJldCA9IC1FSU5WQUw7CisJCWdvdG8gcG9ydF91bmxvY2s7CisJfQorCisJdGFy Z2V0X213ID0gKG9wX2N1cnIgKiBwb3J0LT5wcHNfZGF0YS5vdXRfdm9sdCkgLyAxMDAwOworCWlm ICh0YXJnZXRfbXcgPCBwb3J0LT5vcGVyYXRpbmdfc25rX213KSB7CisJCXJldCA9IC1FSU5WQUw7 CisJCWdvdG8gcG9ydF91bmxvY2s7CisJfQorCisJcmVpbml0X2NvbXBsZXRpb24oJnBvcnQtPnBw c19jb21wbGV0ZSk7CisJcG9ydC0+cHBzX2RhdGEub3BfY3VyciA9IG9wX2N1cnI7CisJcG9ydC0+ cHBzX3N0YXR1cyA9IDA7CisJcG9ydC0+cHBzX3BlbmRpbmcgPSB0cnVlOworCXRjcG1fc2V0X3N0 YXRlKHBvcnQsIFNOS19ORUdPVElBVEVfUFBTX0NBUEFCSUxJVElFUywgMCk7CisJbXV0ZXhfdW5s b2NrKCZwb3J0LT5sb2NrKTsKKworCWlmICghd2FpdF9mb3JfY29tcGxldGlvbl90aW1lb3V0KCZw b3J0LT5wcHNfY29tcGxldGUsCisJCQkJbXNlY3NfdG9famlmZmllcyhQRF9QUFNfQ1RSTF9USU1F T1VUKSkpCisJCXJldCA9IC1FVElNRURPVVQ7CisJZWxzZQorCQlyZXQgPSBwb3J0LT5wcHNfc3Rh dHVzOworCisJZ290byBzd2FwX3VubG9jazsKKworcG9ydF91bmxvY2s6CisJbXV0ZXhfdW5sb2Nr KCZwb3J0LT5sb2NrKTsKK3N3YXBfdW5sb2NrOgorCW11dGV4X3VubG9jaygmcG9ydC0+c3dhcF9s b2NrKTsKKworCXJldHVybiByZXQ7Cit9CisKK3N0YXRpYyBpbnQgX19tYXliZV91bnVzZWQgdGNw bV9wcHNfc2V0X291dF92b2x0KHN0cnVjdCB0Y3BtX3BvcnQgKnBvcnQsIHUxNiBvdXRfdm9sdCkK K3sKKwl1bnNpZ25lZCBpbnQgdGFyZ2V0X213OworCWludCByZXQ7CisKKwltdXRleF9sb2NrKCZw b3J0LT5zd2FwX2xvY2spOworCW11dGV4X2xvY2soJnBvcnQtPmxvY2spOworCisJaWYgKCFwb3J0 LT5wcHNfZGF0YS5hY3RpdmUpIHsKKwkJcmV0ID0gLUVPUE5PVFNVUFA7CisJCWdvdG8gcG9ydF91 bmxvY2s7CisJfQorCisJaWYgKHBvcnQtPnN0YXRlICE9IFNOS19SRUFEWSkgeworCQlyZXQgPSAt RUFHQUlOOworCQlnb3RvIHBvcnRfdW5sb2NrOworCX0KKworCWlmIChvdXRfdm9sdCA8IHBvcnQt PnBwc19kYXRhLm1pbl92b2x0IHx8CisJICAgIG91dF92b2x0ID4gcG9ydC0+cHBzX2RhdGEubWF4 X3ZvbHQpIHsKKwkJcmV0ID0gLUVJTlZBTDsKKwkJZ290byBwb3J0X3VubG9jazsKKwl9CisKKwl0 YXJnZXRfbXcgPSAocG9ydC0+cHBzX2RhdGEub3BfY3VyciAqIG91dF92b2x0KSAvIDEwMDA7CisJ aWYgKHRhcmdldF9tdyA8IHBvcnQtPm9wZXJhdGluZ19zbmtfbXcpIHsKKwkJcmV0ID0gLUVJTlZB TDsKKwkJZ290byBwb3J0X3VubG9jazsKKwl9CisKKwlyZWluaXRfY29tcGxldGlvbigmcG9ydC0+ cHBzX2NvbXBsZXRlKTsKKwlwb3J0LT5wcHNfZGF0YS5vdXRfdm9sdCA9IG91dF92b2x0OworCXBv cnQtPnBwc19zdGF0dXMgPSAwOworCXBvcnQtPnBwc19wZW5kaW5nID0gdHJ1ZTsKKwl0Y3BtX3Nl dF9zdGF0ZShwb3J0LCBTTktfTkVHT1RJQVRFX1BQU19DQVBBQklMSVRJRVMsIDApOworCW11dGV4 X3VubG9jaygmcG9ydC0+bG9jayk7CisKKwlpZiAoIXdhaXRfZm9yX2NvbXBsZXRpb25fdGltZW91 dCgmcG9ydC0+cHBzX2NvbXBsZXRlLAorCQkJCW1zZWNzX3RvX2ppZmZpZXMoUERfUFBTX0NUUkxf VElNRU9VVCkpKQorCQlyZXQgPSAtRVRJTUVET1VUOworCWVsc2UKKwkJcmV0ID0gcG9ydC0+cHBz X3N0YXR1czsKKworCWdvdG8gc3dhcF91bmxvY2s7CisKK3BvcnRfdW5sb2NrOgorCW11dGV4X3Vu bG9jaygmcG9ydC0+bG9jayk7Citzd2FwX3VubG9jazoKKwltdXRleF91bmxvY2soJnBvcnQtPnN3 YXBfbG9jayk7CisKKwlyZXR1cm4gcmV0OworfQorCitzdGF0aWMgaW50IF9fbWF5YmVfdW51c2Vk IHRjcG1fcHBzX2FjdGl2YXRlKHN0cnVjdCB0Y3BtX3BvcnQgKnBvcnQsIGJvb2wgYWN0aXZhdGUp Cit7CisJaW50IHJldCA9IDA7CisKKwltdXRleF9sb2NrKCZwb3J0LT5zd2FwX2xvY2spOworCW11 dGV4X2xvY2soJnBvcnQtPmxvY2spOworCisJaWYgKCFwb3J0LT5wcHNfZGF0YS5zdXBwb3J0ZWQp IHsKKwkJcmV0ID0gLUVPUE5PVFNVUFA7CisJCWdvdG8gcG9ydF91bmxvY2s7CisJfQorCisJLyog VHJ5aW5nIHRvIGRlYWN0aXZhdGUgUFBTIHdoZW4gYWxyZWFkeSBkZWFjdGl2YXRlZCBzbyBqdXN0 IGJhaWwgKi8KKwlpZiAoIXBvcnQtPnBwc19kYXRhLmFjdGl2ZSAmJiAhYWN0aXZhdGUpCisJCWdv dG8gcG9ydF91bmxvY2s7CisKKwlpZiAocG9ydC0+c3RhdGUgIT0gU05LX1JFQURZKSB7CisJCXJl dCA9IC1FQUdBSU47CisJCWdvdG8gcG9ydF91bmxvY2s7CisJfQorCisJcmVpbml0X2NvbXBsZXRp b24oJnBvcnQtPnBwc19jb21wbGV0ZSk7CisJcG9ydC0+cHBzX3N0YXR1cyA9IDA7CisJcG9ydC0+ cHBzX3BlbmRpbmcgPSB0cnVlOworCisJLyogVHJpZ2dlciBQUFMgcmVxdWVzdCBvciBtb3ZlIGJh Y2sgdG8gc3RhbmRhcmQgUERPIGNvbnRyYWN0ICovCisJaWYgKGFjdGl2YXRlKSB7CisJCXBvcnQt PnBwc19kYXRhLm91dF92b2x0ID0gcG9ydC0+c3VwcGx5X3ZvbHRhZ2U7CisJCXBvcnQtPnBwc19k YXRhLm9wX2N1cnIgPSBwb3J0LT5jdXJyZW50X2xpbWl0OworCQl0Y3BtX3NldF9zdGF0ZShwb3J0 LCBTTktfTkVHT1RJQVRFX1BQU19DQVBBQklMSVRJRVMsIDApOworCX0gZWxzZSB7CisJCXRjcG1f c2V0X3N0YXRlKHBvcnQsIFNOS19ORUdPVElBVEVfQ0FQQUJJTElUSUVTLCAwKTsKKwl9CisJbXV0 ZXhfdW5sb2NrKCZwb3J0LT5sb2NrKTsKKworCWlmICghd2FpdF9mb3JfY29tcGxldGlvbl90aW1l b3V0KCZwb3J0LT5wcHNfY29tcGxldGUsCisJCQkJbXNlY3NfdG9famlmZmllcyhQRF9QUFNfQ1RS TF9USU1FT1VUKSkpCisJCXJldCA9IC1FVElNRURPVVQ7CisJZWxzZQorCQlyZXQgPSBwb3J0LT5w cHNfc3RhdHVzOworCisJZ290byBzd2FwX3VubG9jazsKKworcG9ydF91bmxvY2s6CisJbXV0ZXhf dW5sb2NrKCZwb3J0LT5sb2NrKTsKK3N3YXBfdW5sb2NrOgorCW11dGV4X3VubG9jaygmcG9ydC0+ c3dhcF9sb2NrKTsKKworCXJldHVybiByZXQ7Cit9CisKIHN0YXRpYyB2b2lkIHRjcG1faW5pdChz dHJ1Y3QgdGNwbV9wb3J0ICpwb3J0KQogewogCWVudW0gdHlwZWNfY2Nfc3RhdHVzIGNjMSwgY2My OwpAQCAtMzYwMywxMyArNDEzOCwxOCBAQCBpbnQgdGNwbV91cGRhdGVfc2lua19jYXBhYmlsaXRp ZXMoc3RydWN0IHRjcG1fcG9ydCAqcG9ydCwgY29uc3QgdTMyICpwZG8sCiAJbXV0ZXhfbG9jaygm cG9ydC0+bG9jayk7CiAJcG9ydC0+bnJfc25rX3BkbyA9IHRjcG1fY29weV9wZG9zKHBvcnQtPnNu a19wZG8sIHBkbywgbnJfcGRvKTsKIAlwb3J0LT5vcGVyYXRpbmdfc25rX213ID0gb3BlcmF0aW5n X3Nua19tdzsKKwlwb3J0LT51cGRhdGVfc2lua19jYXBzID0gdHJ1ZTsKIAogCXN3aXRjaCAocG9y dC0+c3RhdGUpIHsKIAljYXNlIFNOS19ORUdPVElBVEVfQ0FQQUJJTElUSUVTOgorCWNhc2UgU05L X05FR09USUFURV9QUFNfQ0FQQUJJTElUSUVTOgogCWNhc2UgU05LX1JFQURZOgogCWNhc2UgU05L X1RSQU5TSVRJT05fU0lOSzoKIAljYXNlIFNOS19UUkFOU0lUSU9OX1NJTktfVkJVUzoKLQkJdGNw bV9zZXRfc3RhdGUocG9ydCwgU05LX05FR09USUFURV9DQVBBQklMSVRJRVMsIDApOworCQlpZiAo cG9ydC0+cHBzX2RhdGEuYWN0aXZlKQorCQkJdGNwbV9zZXRfc3RhdGUocG9ydCwgU05LX05FR09U SUFURV9QUFNfQ0FQQUJJTElUSUVTLCAwKTsKKwkJZWxzZQorCQkJdGNwbV9zZXRfc3RhdGUocG9y dCwgU05LX05FR09USUFURV9DQVBBQklMSVRJRVMsIDApOwogCQlicmVhazsKIAlkZWZhdWx0Ogog CQlicmVhazsKQEAgLTM2NTEsNiArNDE5MSw3IEBAIHN0cnVjdCB0Y3BtX3BvcnQgKnRjcG1fcmVn aXN0ZXJfcG9ydChzdHJ1Y3QgZGV2aWNlICpkZXYsIHN0cnVjdCB0Y3BjX2RldiAqdGNwYykKIAog CWluaXRfY29tcGxldGlvbigmcG9ydC0+dHhfY29tcGxldGUpOwogCWluaXRfY29tcGxldGlvbigm cG9ydC0+c3dhcF9jb21wbGV0ZSk7CisJaW5pdF9jb21wbGV0aW9uKCZwb3J0LT5wcHNfY29tcGxl dGUpOwogCXRjcG1fZGVidWdmc19pbml0KHBvcnQpOwogCiAJaWYgKHRjcG1fdmFsaWRhdGVfY2Fw cyhwb3J0LCB0Y3BjLT5jb25maWctPnNyY19wZG8sCkBAIC0zNjc3LDcgKzQyMTgsNyBAQCBzdHJ1 Y3QgdGNwbV9wb3J0ICp0Y3BtX3JlZ2lzdGVyX3BvcnQoc3RydWN0IGRldmljZSAqZGV2LCBzdHJ1 Y3QgdGNwY19kZXYgKnRjcGMpCiAJcG9ydC0+dHlwZWNfY2Fwcy50eXBlID0gdGNwYy0+Y29uZmln LT50eXBlOwogCXBvcnQtPnR5cGVjX2NhcHMuZGF0YSA9IHRjcGMtPmNvbmZpZy0+ZGF0YTsKIAlw b3J0LT50eXBlY19jYXBzLnJldmlzaW9uID0gMHgwMTIwOwkvKiBUeXBlLUMgc3BlYyByZWxlYXNl IDEuMiAqLwotCXBvcnQtPnR5cGVjX2NhcHMucGRfcmV2aXNpb24gPSAweDAyMDA7CS8qIFVTQi1Q RCBzcGVjIHJlbGVhc2UgMi4wICovCisJcG9ydC0+dHlwZWNfY2Fwcy5wZF9yZXZpc2lvbiA9IDB4 MDMwMDsJLyogVVNCLVBEIHNwZWMgcmVsZWFzZSAzLjAgKi8KIAlwb3J0LT50eXBlY19jYXBzLmRy X3NldCA9IHRjcG1fZHJfc2V0OwogCXBvcnQtPnR5cGVjX2NhcHMucHJfc2V0ID0gdGNwbV9wcl9z ZXQ7CiAJcG9ydC0+dHlwZWNfY2Fwcy52Y29ubl9zZXQgPSB0Y3BtX3Zjb25uX3NldDsKZGlmZiAt LWdpdCBhL2luY2x1ZGUvbGludXgvdXNiL3BkLmggYi9pbmNsdWRlL2xpbnV4L3VzYi9wZC5oCmlu ZGV4IGZmMzU5YmRmLi4wOWI1NzBmIDEwMDY0NAotLS0gYS9pbmNsdWRlL2xpbnV4L3VzYi9wZC5o CisrKyBiL2luY2x1ZGUvbGludXgvdXNiL3BkLmgKQEAgLTEwMyw4ICsxMDMsOCBAQCBlbnVtIHBk X2V4dF9tc2dfdHlwZSB7CiAJICgoKGNudCkgJiBQRF9IRUFERVJfQ05UX01BU0spIDw8IFBEX0hF QURFUl9DTlRfU0hJRlQpIHwJXAogCSAoKGV4dF9oZHIpID8gUERfSEVBREVSX0VYVF9IRFIgOiAw KSkKIAotI2RlZmluZSBQRF9IRUFERVJfTEUodHlwZSwgcHdyLCBkYXRhLCBpZCwgY250KSBcCi0J Y3B1X3RvX2xlMTYoUERfSEVBREVSKCh0eXBlKSwgKHB3ciksIChkYXRhKSwgUERfUkVWMjAsIChp ZCksIChjbnQpLCAoMCkpKQorI2RlZmluZSBQRF9IRUFERVJfTEUodHlwZSwgcHdyLCBkYXRhLCBy ZXYsIGlkLCBjbnQpIFwKKwljcHVfdG9fbGUxNihQRF9IRUFERVIoKHR5cGUpLCAocHdyKSwgKGRh dGEpLCAocmV2KSwgKGlkKSwgKGNudCksICgwKSkpCiAKIHN0YXRpYyBpbmxpbmUgdW5zaWduZWQg aW50IHBkX2hlYWRlcl9jbnQodTE2IGhlYWRlcikKIHsKZGlmZiAtLWdpdCBhL2luY2x1ZGUvbGlu dXgvdXNiL3RjcG0uaCBiL2luY2x1ZGUvbGludXgvdXNiL3RjcG0uaAppbmRleCBmNWJkYTlhLi5i MjMxYjkzIDEwMDY0NAotLS0gYS9pbmNsdWRlL2xpbnV4L3VzYi90Y3BtLmgKKysrIGIvaW5jbHVk ZS9saW51eC91c2IvdGNwbS5oCkBAIC0zNiw2ICszNiw3IEBAIGVudW0gdHlwZWNfY2NfcG9sYXJp dHkgewogLyogVGltZSB0byB3YWl0IGZvciBUQ1BDIHRvIGNvbXBsZXRlIHRyYW5zbWl0ICovCiAj ZGVmaW5lIFBEX1RfVENQQ19UWF9USU1FT1VUCTEwMAkJLyogaW4gbXMJKi8KICNkZWZpbmUgUERf Uk9MRV9TV0FQX1RJTUVPVVQJKE1TRUNfUEVSX1NFQyAqIDEwKQorI2RlZmluZSBQRF9QUFNfQ1RS TF9USU1FT1VUCShNU0VDX1BFUl9TRUMgKiAxMCkKIAogZW51bSB0Y3BtX3RyYW5zbWl0X3N0YXR1 cyB7CiAJVENQQ19UWF9TVUNDRVNTID0gMCwK From mboxrd@z Thu Jan 1 00:00:00 1970 From: Adam Thomson Subject: [PATCH v8 1/6] typec: tcpm: Add core support for sink side PPS Date: Mon, 23 Apr 2018 15:10:56 +0100 Message-ID: <34c58a40afac0b97375fc76a343d5fd41057fafa.1524490253.git.Adam.Thomson.Opensource@diasemi.com> References: Mime-Version: 1.0 Content-Type: text/plain Return-path: In-Reply-To: Sender: linux-kernel-owner@vger.kernel.org To: Heikki Krogerus , Guenter Roeck , Greg Kroah-Hartman , Sebastian Reichel , Hans de Goede , Jun Li Cc: linux-usb@vger.kernel.org, linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, support.opensource@diasemi.com List-Id: linux-pm@vger.kernel.org This commit adds code to handle requesting of PPS APDOs. Switching between standard PDOs and APDOs, and re-requesting an APDO to modify operating voltage/current will be triggered by an external call into TCPM. Signed-off-by: Adam Thomson Acked-by: Heikki Krogerus Reviewed-by: Guenter Roeck --- drivers/usb/typec/tcpm.c | 569 +++++++++++++++++++++++++++++++++++++++++++++-- include/linux/usb/pd.h | 4 +- include/linux/usb/tcpm.h | 1 + 3 files changed, 558 insertions(+), 16 deletions(-) diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 27192083..b160da3 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -48,6 +48,7 @@ S(SNK_DISCOVERY_DEBOUNCE_DONE), \ S(SNK_WAIT_CAPABILITIES), \ S(SNK_NEGOTIATE_CAPABILITIES), \ + S(SNK_NEGOTIATE_PPS_CAPABILITIES), \ S(SNK_TRANSITION_SINK), \ S(SNK_TRANSITION_SINK_VBUS), \ S(SNK_READY), \ @@ -167,6 +168,16 @@ struct pd_mode_data { struct typec_altmode_desc altmode_desc[SVID_DISCOVERY_MAX]; }; +struct pd_pps_data { + u32 min_volt; + u32 max_volt; + u32 max_curr; + u32 out_volt; + u32 op_curr; + bool supported; + bool active; +}; + struct tcpm_port { struct device *dev; @@ -235,6 +246,7 @@ struct tcpm_port { struct completion swap_complete; int swap_status; + unsigned int negotiated_rev; unsigned int message_id; unsigned int caps_count; unsigned int hard_reset_count; @@ -258,6 +270,7 @@ struct tcpm_port { unsigned int nr_snk_vdo; unsigned int operating_snk_mw; + bool update_sink_caps; /* Requested current / voltage */ u32 current_limit; @@ -274,8 +287,13 @@ struct tcpm_port { /* VDO to retry if UFP responder replied busy */ u32 vdo_retry; - /* Alternate mode data */ + /* PPS */ + struct pd_pps_data pps_data; + struct completion pps_complete; + bool pps_pending; + int pps_status; + /* Alternate mode data */ struct pd_mode_data mode_data; struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX]; struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX]; @@ -493,6 +511,16 @@ static void tcpm_log_source_caps(struct tcpm_port *port) pdo_max_voltage(pdo), pdo_max_power(pdo)); break; + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) + scnprintf(msg, sizeof(msg), + "%u-%u mV, %u mA", + pdo_pps_apdo_min_voltage(pdo), + pdo_pps_apdo_max_voltage(pdo), + pdo_pps_apdo_max_current(pdo)); + else + strcpy(msg, "undefined APDO"); + break; default: strcpy(msg, "undefined"); break; @@ -790,11 +818,13 @@ static int tcpm_pd_send_source_caps(struct tcpm_port *port) msg.header = PD_HEADER_LE(PD_CTRL_REJECT, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, 0); } else { msg.header = PD_HEADER_LE(PD_DATA_SOURCE_CAP, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, port->nr_src_pdo); } @@ -815,11 +845,13 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) msg.header = PD_HEADER_LE(PD_CTRL_REJECT, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, 0); } else { msg.header = PD_HEADER_LE(PD_DATA_SINK_CAP, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, port->nr_snk_pdo); } @@ -1186,6 +1218,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) msg.header = PD_HEADER_LE(PD_DATA_VENDOR_DEF, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, port->vdo_count); for (i = 0; i < port->vdo_count; i++) msg.payload[i] = cpu_to_le32(port->vdo_data[i]); @@ -1257,6 +1290,8 @@ enum pdo_err { PDO_ERR_FIXED_NOT_SORTED, PDO_ERR_VARIABLE_BATT_NOT_SORTED, PDO_ERR_DUPE_PDO, + PDO_ERR_PPS_APDO_NOT_SORTED, + PDO_ERR_DUPE_PPS_APDO, }; static const char * const pdo_err_msg[] = { @@ -1272,6 +1307,10 @@ enum pdo_err { " err: Variable/Battery supply pdos should be in increasing order of their minimum voltage", [PDO_ERR_DUPE_PDO] = " err: Variable/Batt supply pdos cannot have same min/max voltage", + [PDO_ERR_PPS_APDO_NOT_SORTED] = + " err: Programmable power supply apdos should be in increasing order of their maximum voltage", + [PDO_ERR_DUPE_PPS_APDO] = + " err: Programmable power supply apdos cannot have same min/max voltage and max current", }; static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo, @@ -1321,6 +1360,26 @@ static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo, pdo_min_voltage(pdo[i - 1]))) return PDO_ERR_DUPE_PDO; break; + /* + * The Programmable Power Supply APDOs, if present, + * shall be sent in Maximum Voltage order; + * lowest to highest. + */ + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo[i]) != APDO_TYPE_PPS) + break; + + if (pdo_pps_apdo_max_current(pdo[i]) < + pdo_pps_apdo_max_current(pdo[i - 1])) + return PDO_ERR_PPS_APDO_NOT_SORTED; + else if (pdo_pps_apdo_min_voltage(pdo[i]) == + pdo_pps_apdo_min_voltage(pdo[i - 1]) && + pdo_pps_apdo_max_voltage(pdo[i]) == + pdo_pps_apdo_max_voltage(pdo[i - 1]) && + pdo_pps_apdo_max_current(pdo[i]) == + pdo_pps_apdo_max_current(pdo[i - 1])) + return PDO_ERR_DUPE_PPS_APDO; + break; default: tcpm_log_force(port, " Unknown pdo type"); } @@ -1346,11 +1405,16 @@ static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo, /* * PD (data, control) command handling functions */ + +static int tcpm_pd_send_control(struct tcpm_port *port, + enum pd_ctrl_msg_type type); + static void tcpm_pd_data_request(struct tcpm_port *port, const struct pd_message *msg) { enum pd_data_msg_type type = pd_header_type_le(msg->header); unsigned int cnt = pd_header_cnt_le(msg->header); + unsigned int rev = pd_header_rev_le(msg->header); unsigned int i; switch (type) { @@ -1369,6 +1433,17 @@ static void tcpm_pd_data_request(struct tcpm_port *port, port->nr_source_caps); /* + * Adjust revision in subsequent message headers, as required, + * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't + * support Rev 1.0 so just do nothing in that scenario. + */ + if (rev == PD_REV10) + break; + + if (rev < PD_MAX_REV) + port->negotiated_rev = rev; + + /* * This message may be received even if VBUS is not * present. This is quite unexpected; see USB PD * specification, sections 8.3.3.6.3.1 and 8.3.3.6.3.2. @@ -1389,6 +1464,20 @@ static void tcpm_pd_data_request(struct tcpm_port *port, tcpm_queue_message(port, PD_MSG_CTRL_REJECT); break; } + + /* + * Adjust revision in subsequent message headers, as required, + * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't + * support Rev 1.0 so just reject in that scenario. + */ + if (rev == PD_REV10) { + tcpm_queue_message(port, PD_MSG_CTRL_REJECT); + break; + } + + if (rev < PD_MAX_REV) + port->negotiated_rev = rev; + port->sink_request = le32_to_cpu(msg->payload[0]); tcpm_set_state(port, SRC_NEGOTIATE_CAPABILITIES, 0); break; @@ -1413,6 +1502,15 @@ static void tcpm_pd_data_request(struct tcpm_port *port, } } +static void tcpm_pps_complete(struct tcpm_port *port, int result) +{ + if (port->pps_pending) { + port->pps_status = result; + port->pps_pending = false; + complete(&port->pps_complete); + } +} + static void tcpm_pd_ctrl_request(struct tcpm_port *port, const struct pd_message *msg) { @@ -1489,6 +1587,14 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, next_state = SNK_WAIT_CAPABILITIES; tcpm_set_state(port, next_state, 0); break; + case SNK_NEGOTIATE_PPS_CAPABILITIES: + /* Revert data back from any requested PPS updates */ + port->pps_data.out_volt = port->supply_voltage; + port->pps_data.op_curr = port->current_limit; + port->pps_status = (type == PD_CTRL_WAIT ? + -EAGAIN : -EOPNOTSUPP); + tcpm_set_state(port, SNK_READY, 0); + break; case DR_SWAP_SEND: port->swap_status = (type == PD_CTRL_WAIT ? -EAGAIN : -EOPNOTSUPP); @@ -1511,6 +1617,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, case PD_CTRL_ACCEPT: switch (port->state) { case SNK_NEGOTIATE_CAPABILITIES: + port->pps_data.active = false; + tcpm_set_state(port, SNK_TRANSITION_SINK, 0); + break; + case SNK_NEGOTIATE_PPS_CAPABILITIES: + port->pps_data.active = true; + port->supply_voltage = port->pps_data.out_volt; + port->current_limit = port->pps_data.op_curr; tcpm_set_state(port, SNK_TRANSITION_SINK, 0); break; case SOFT_RESET_SEND: @@ -1665,6 +1778,7 @@ static int tcpm_pd_send_control(struct tcpm_port *port, memset(&msg, 0, sizeof(msg)); msg.header = PD_HEADER_LE(type, port->pwr_role, port->data_role, + port->negotiated_rev, port->message_id, 0); return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); @@ -1780,6 +1894,8 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, min_snk_mv = 0; int ret = -EINVAL; + port->pps_data.supported = false; + /* * Select the source PDO providing the most power which has a * matchig sink cap. @@ -1788,30 +1904,59 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, u32 pdo = port->source_caps[i]; enum pd_pdo_type type = pdo_type(pdo); - if (type == PDO_TYPE_FIXED) { + switch (type) { + case PDO_TYPE_FIXED: max_src_mv = pdo_fixed_voltage(pdo); min_src_mv = max_src_mv; - } else { + break; + case PDO_TYPE_BATT: + case PDO_TYPE_VAR: max_src_mv = pdo_max_voltage(pdo); min_src_mv = pdo_min_voltage(pdo); + break; + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) + port->pps_data.supported = true; + continue; + default: + tcpm_log(port, "Invalid source PDO type, ignoring"); + continue; } - if (type == PDO_TYPE_BATT) { - src_mw = pdo_max_power(pdo); - } else { + switch (type) { + case PDO_TYPE_FIXED: + case PDO_TYPE_VAR: src_ma = pdo_max_current(pdo); src_mw = src_ma * min_src_mv / 1000; + break; + case PDO_TYPE_BATT: + src_mw = pdo_max_power(pdo); + break; + case PDO_TYPE_APDO: + continue; + default: + tcpm_log(port, "Invalid source PDO type, ignoring"); + continue; } for (j = 0; j < port->nr_snk_pdo; j++) { pdo = port->snk_pdo[j]; - if (pdo_type(pdo) == PDO_TYPE_FIXED) { - min_snk_mv = pdo_fixed_voltage(pdo); + switch (pdo_type(pdo)) { + case PDO_TYPE_FIXED: max_snk_mv = pdo_fixed_voltage(pdo); - } else { - min_snk_mv = pdo_min_voltage(pdo); + min_snk_mv = max_snk_mv; + break; + case PDO_TYPE_BATT: + case PDO_TYPE_VAR: max_snk_mv = pdo_max_voltage(pdo); + min_snk_mv = pdo_min_voltage(pdo); + break; + case PDO_TYPE_APDO: + continue; + default: + tcpm_log(port, "Invalid sink PDO type, ignoring"); + continue; } if (max_src_mv <= max_snk_mv && @@ -1832,6 +1977,103 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, return ret; } +#define min_pps_apdo_current(x, y) \ + min(pdo_pps_apdo_max_current(x), pdo_pps_apdo_max_current(y)) + +static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) +{ + unsigned int i, j, max_mw = 0, max_mv = 0; + unsigned int min_src_mv, max_src_mv, src_ma, src_mw; + unsigned int min_snk_mv, max_snk_mv, snk_ma; + u32 pdo; + unsigned int src_pdo = 0, snk_pdo = 0; + + /* + * Select the source PPS APDO providing the most power while staying + * within the board's limits. We skip the first PDO as this is always + * 5V 3A. + */ + for (i = 1; i < port->nr_source_caps; ++i) { + pdo = port->source_caps[i]; + + switch (pdo_type(pdo)) { + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) { + tcpm_log(port, "Not PPS APDO (source), ignoring"); + continue; + } + + min_src_mv = pdo_pps_apdo_min_voltage(pdo); + max_src_mv = pdo_pps_apdo_max_voltage(pdo); + src_ma = pdo_pps_apdo_max_current(pdo); + src_mw = (src_ma * max_src_mv) / 1000; + + /* + * Now search through the sink PDOs to find a matching + * PPS APDO. Again skip the first sink PDO as this will + * always be 5V 3A. + */ + for (j = i; j < port->nr_snk_pdo; j++) { + pdo = port->snk_pdo[j]; + + switch (pdo_type(pdo)) { + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) { + tcpm_log(port, + "Not PPS APDO (sink), ignoring"); + continue; + } + + min_snk_mv = + pdo_pps_apdo_min_voltage(pdo); + max_snk_mv = + pdo_pps_apdo_max_voltage(pdo); + snk_ma = + pdo_pps_apdo_max_current(pdo); + break; + default: + tcpm_log(port, + "Not APDO type (sink), ignoring"); + continue; + } + + if (max_src_mv <= max_snk_mv && + min_src_mv >= min_snk_mv) { + /* Prefer higher voltages if available */ + if ((src_mw == max_mw && + min_src_mv > max_mv) || + src_mw > max_mw) { + src_pdo = i; + snk_pdo = j; + max_mw = src_mw; + max_mv = max_src_mv; + } + } + } + + break; + default: + tcpm_log(port, "Not APDO type (source), ignoring"); + continue; + } + } + + if (src_pdo) { + pdo = port->source_caps[src_pdo]; + + port->pps_data.min_volt = pdo_pps_apdo_min_voltage(pdo); + port->pps_data.max_volt = pdo_pps_apdo_max_voltage(pdo); + port->pps_data.max_curr = + min_pps_apdo_current(pdo, port->snk_pdo[snk_pdo]); + port->pps_data.out_volt = + min(pdo_pps_apdo_max_voltage(pdo), port->pps_data.out_volt); + port->pps_data.op_curr = + min(port->pps_data.max_curr, port->pps_data.op_curr); + } + + return src_pdo; +} + static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) { unsigned int mv, ma, mw, flags; @@ -1849,10 +2091,18 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) matching_snk_pdo = port->snk_pdo[snk_pdo_index]; type = pdo_type(pdo); - if (type == PDO_TYPE_FIXED) + switch (type) { + case PDO_TYPE_FIXED: mv = pdo_fixed_voltage(pdo); - else + break; + case PDO_TYPE_BATT: + case PDO_TYPE_VAR: mv = pdo_min_voltage(pdo); + break; + default: + tcpm_log(port, "Invalid PDO selected!"); + return -EINVAL; + } /* Select maximum available current within the sink pdo's limit */ if (type == PDO_TYPE_BATT) { @@ -1917,6 +2167,105 @@ static int tcpm_pd_send_request(struct tcpm_port *port) msg.header = PD_HEADER_LE(PD_DATA_REQUEST, port->pwr_role, port->data_role, + port->negotiated_rev, + port->message_id, 1); + msg.payload[0] = cpu_to_le32(rdo); + + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + +static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo) +{ + unsigned int out_mv, op_ma, op_mw, min_mv, max_mv, max_ma, flags; + enum pd_pdo_type type; + unsigned int src_pdo_index; + u32 pdo; + + src_pdo_index = tcpm_pd_select_pps_apdo(port); + if (!src_pdo_index) + return -EOPNOTSUPP; + + pdo = port->source_caps[src_pdo_index]; + type = pdo_type(pdo); + + switch (type) { + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) != APDO_TYPE_PPS) { + tcpm_log(port, "Invalid APDO selected!"); + return -EINVAL; + } + min_mv = port->pps_data.min_volt; + max_mv = port->pps_data.max_volt; + max_ma = port->pps_data.max_curr; + out_mv = port->pps_data.out_volt; + op_ma = port->pps_data.op_curr; + break; + default: + tcpm_log(port, "Invalid PDO selected!"); + return -EINVAL; + } + + flags = RDO_USB_COMM | RDO_NO_SUSPEND; + + op_mw = (op_ma * out_mv) / 1000; + if (op_mw < port->operating_snk_mw) { + /* + * Try raising current to meet power needs. If that's not enough + * then try upping the voltage. If that's still not enough + * then we've obviously chosen a PPS APDO which really isn't + * suitable so abandon ship. + */ + op_ma = (port->operating_snk_mw * 1000) / out_mv; + if ((port->operating_snk_mw * 1000) % out_mv) + ++op_ma; + op_ma += RDO_PROG_CURR_MA_STEP - (op_ma % RDO_PROG_CURR_MA_STEP); + + if (op_ma > max_ma) { + op_ma = max_ma; + out_mv = (port->operating_snk_mw * 1000) / op_ma; + if ((port->operating_snk_mw * 1000) % op_ma) + ++out_mv; + out_mv += RDO_PROG_VOLT_MV_STEP - + (out_mv % RDO_PROG_VOLT_MV_STEP); + + if (out_mv > max_mv) { + tcpm_log(port, "Invalid PPS APDO selected!"); + return -EINVAL; + } + } + } + + tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d", + port->cc_req, port->cc1, port->cc2, port->vbus_source, + port->vconn_role == TYPEC_SOURCE ? "source" : "sink", + port->polarity); + + *rdo = RDO_PROG(src_pdo_index + 1, out_mv, op_ma, flags); + + tcpm_log(port, "Requesting APDO %d: %u mV, %u mA", + src_pdo_index, out_mv, op_ma); + + port->pps_data.op_curr = op_ma; + port->pps_data.out_volt = out_mv; + + return 0; +} + +static int tcpm_pd_send_pps_request(struct tcpm_port *port) +{ + struct pd_message msg; + int ret; + u32 rdo; + + ret = tcpm_pd_build_pps_request(port, &rdo); + if (ret < 0) + return ret; + + memset(&msg, 0, sizeof(msg)); + msg.header = PD_HEADER_LE(PD_DATA_REQUEST, + port->pwr_role, + port->data_role, + port->negotiated_rev, port->message_id, 1); msg.payload[0] = cpu_to_le32(rdo); @@ -2103,6 +2452,7 @@ static void tcpm_reset_port(struct tcpm_port *port) tcpm_typec_disconnect(port); port->attached = false; port->pd_capable = false; + port->pps_data.supported = false; /* * First Rx ID should be 0; set this to a sentinel of -1 so that @@ -2120,6 +2470,8 @@ static void tcpm_reset_port(struct tcpm_port *port) tcpm_set_attached_state(port, false); port->try_src_count = 0; port->try_snk_count = 0; + port->supply_voltage = 0; + port->current_limit = 0; } static void tcpm_detach(struct tcpm_port *port) @@ -2364,6 +2716,7 @@ static void run_state_machine(struct tcpm_port *port) typec_set_pwr_opmode(port->typec_port, opmode); port->pwr_opmode = TYPEC_PWR_MODE_USB; port->caps_count = 0; + port->negotiated_rev = PD_MAX_REV; port->message_id = 0; port->rx_msgid = -1; port->explicit_contract = false; @@ -2424,6 +2777,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_swap_complete(port, 0); tcpm_typec_connect(port); + tcpm_check_send_discover(port); /* * 6.3.5 @@ -2447,6 +2801,7 @@ static void run_state_machine(struct tcpm_port *port) case SNK_UNATTACHED: if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); + tcpm_pps_complete(port, -ENOTCONN); tcpm_snk_detach(port); if (tcpm_start_drp_toggling(port)) { tcpm_set_state(port, DRP_TOGGLING, 0); @@ -2536,6 +2891,7 @@ static void run_state_machine(struct tcpm_port *port) port->cc2 : port->cc1); typec_set_pwr_opmode(port->typec_port, opmode); port->pwr_opmode = TYPEC_PWR_MODE_USB; + port->negotiated_rev = PD_MAX_REV; port->message_id = 0; port->rx_msgid = -1; port->explicit_contract = false; @@ -2606,6 +2962,24 @@ static void run_state_machine(struct tcpm_port *port) PD_T_SENDER_RESPONSE); } break; + case SNK_NEGOTIATE_PPS_CAPABILITIES: + ret = tcpm_pd_send_pps_request(port); + if (ret < 0) { + port->pps_status = ret; + /* + * If this was called due to updates to sink + * capabilities, and pps is no longer valid, we should + * safely fall back to a standard PDO. + */ + if (port->update_sink_caps) + tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); + else + tcpm_set_state(port, SNK_READY, 0); + } else { + tcpm_set_state_cond(port, hard_reset_state(port), + PD_T_SENDER_RESPONSE); + } + break; case SNK_TRANSITION_SINK: case SNK_TRANSITION_SINK_VBUS: tcpm_set_state(port, hard_reset_state(port), @@ -2613,6 +2987,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_READY: port->try_snk_count = 0; + port->update_sink_caps = false; if (port->explicit_contract) { typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_PD); @@ -2622,6 +2997,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_swap_complete(port, 0); tcpm_typec_connect(port); tcpm_check_send_discover(port); + tcpm_pps_complete(port, port->pps_status); + break; /* Accessory states */ @@ -2668,6 +3045,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; case SNK_HARD_RESET_SINK_OFF: + memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); tcpm_set_charge(port, false); tcpm_set_roles(port, false, TYPEC_SINK, TYPEC_DEVICE); @@ -2888,6 +3266,7 @@ static void run_state_machine(struct tcpm_port *port) break; case ERROR_RECOVERY: tcpm_swap_complete(port, -EPROTO); + tcpm_pps_complete(port, -EPROTO); tcpm_set_state(port, PORT_RESET, 0); break; case PORT_RESET: @@ -3470,6 +3849,162 @@ static int tcpm_try_role(const struct typec_capability *cap, int role) return ret; } +static int __maybe_unused tcpm_pps_set_op_curr(struct tcpm_port *port, u16 op_curr) +{ + unsigned int target_mw; + int ret; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (!port->pps_data.active) { + ret = -EOPNOTSUPP; + goto port_unlock; + } + + if (port->state != SNK_READY) { + ret = -EAGAIN; + goto port_unlock; + } + + if (op_curr > port->pps_data.max_curr) { + ret = -EINVAL; + goto port_unlock; + } + + target_mw = (op_curr * port->pps_data.out_volt) / 1000; + if (target_mw < port->operating_snk_mw) { + ret = -EINVAL; + goto port_unlock; + } + + reinit_completion(&port->pps_complete); + port->pps_data.op_curr = op_curr; + port->pps_status = 0; + port->pps_pending = true; + tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0); + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->pps_complete, + msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->pps_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + + return ret; +} + +static int __maybe_unused tcpm_pps_set_out_volt(struct tcpm_port *port, u16 out_volt) +{ + unsigned int target_mw; + int ret; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (!port->pps_data.active) { + ret = -EOPNOTSUPP; + goto port_unlock; + } + + if (port->state != SNK_READY) { + ret = -EAGAIN; + goto port_unlock; + } + + if (out_volt < port->pps_data.min_volt || + out_volt > port->pps_data.max_volt) { + ret = -EINVAL; + goto port_unlock; + } + + target_mw = (port->pps_data.op_curr * out_volt) / 1000; + if (target_mw < port->operating_snk_mw) { + ret = -EINVAL; + goto port_unlock; + } + + reinit_completion(&port->pps_complete); + port->pps_data.out_volt = out_volt; + port->pps_status = 0; + port->pps_pending = true; + tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0); + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->pps_complete, + msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->pps_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + + return ret; +} + +static int __maybe_unused tcpm_pps_activate(struct tcpm_port *port, bool activate) +{ + int ret = 0; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (!port->pps_data.supported) { + ret = -EOPNOTSUPP; + goto port_unlock; + } + + /* Trying to deactivate PPS when already deactivated so just bail */ + if (!port->pps_data.active && !activate) + goto port_unlock; + + if (port->state != SNK_READY) { + ret = -EAGAIN; + goto port_unlock; + } + + reinit_completion(&port->pps_complete); + port->pps_status = 0; + port->pps_pending = true; + + /* Trigger PPS request or move back to standard PDO contract */ + if (activate) { + port->pps_data.out_volt = port->supply_voltage; + port->pps_data.op_curr = port->current_limit; + tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0); + } else { + tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); + } + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->pps_complete, + msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->pps_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + + return ret; +} + static void tcpm_init(struct tcpm_port *port) { enum typec_cc_status cc1, cc2; @@ -3603,13 +4138,18 @@ int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, mutex_lock(&port->lock); port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo); port->operating_snk_mw = operating_snk_mw; + port->update_sink_caps = true; switch (port->state) { case SNK_NEGOTIATE_CAPABILITIES: + case SNK_NEGOTIATE_PPS_CAPABILITIES: case SNK_READY: case SNK_TRANSITION_SINK: case SNK_TRANSITION_SINK_VBUS: - tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); + if (port->pps_data.active) + tcpm_set_state(port, SNK_NEGOTIATE_PPS_CAPABILITIES, 0); + else + tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); break; default: break; @@ -3651,6 +4191,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) init_completion(&port->tx_complete); init_completion(&port->swap_complete); + init_completion(&port->pps_complete); tcpm_debugfs_init(port); if (tcpm_validate_caps(port, tcpc->config->src_pdo, @@ -3677,7 +4218,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.type = tcpc->config->type; port->typec_caps.data = tcpc->config->data; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ - port->typec_caps.pd_revision = 0x0200; /* USB-PD spec release 2.0 */ + port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ port->typec_caps.dr_set = tcpm_dr_set; port->typec_caps.pr_set = tcpm_pr_set; port->typec_caps.vconn_set = tcpm_vconn_set; diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index ff359bdf..09b570f 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -103,8 +103,8 @@ enum pd_ext_msg_type { (((cnt) & PD_HEADER_CNT_MASK) << PD_HEADER_CNT_SHIFT) | \ ((ext_hdr) ? PD_HEADER_EXT_HDR : 0)) -#define PD_HEADER_LE(type, pwr, data, id, cnt) \ - cpu_to_le16(PD_HEADER((type), (pwr), (data), PD_REV20, (id), (cnt), (0))) +#define PD_HEADER_LE(type, pwr, data, rev, id, cnt) \ + cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (0))) static inline unsigned int pd_header_cnt(u16 header) { diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index f5bda9a..b231b93 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -36,6 +36,7 @@ enum typec_cc_polarity { /* Time to wait for TCPC to complete transmit */ #define PD_T_TCPC_TX_TIMEOUT 100 /* in ms */ #define PD_ROLE_SWAP_TIMEOUT (MSEC_PER_SEC * 10) +#define PD_PPS_CTRL_TIMEOUT (MSEC_PER_SEC * 10) enum tcpm_transmit_status { TCPC_TX_SUCCESS = 0, -- 1.9.1