Thanks Heikki ! I am making another version addressing your nits as well. Guenter, Are you fine with the patch as well ? Regards, Badhri On Tue, Nov 7, 2017 at 3:00 AM, Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> wrote: > On Sat, Nov 04, 2017 at 12:22:12PM -0700, Badhri Jagan Sridharan wrote: >> The source and sink caps should follow the following rules. >> This patch validates whether the src_caps/snk_caps adheres >> to it. >> >> 6.4.1 Capabilities Message >> A Capabilities message (Source Capabilities message or Sink >> Capabilities message) shall have at least one Power >> Data Object for vSafe5V. The Capabilities message shall also >> contain the sending Port???s information followed by up to >> 6 additional Power Data Objects. Power Data Objects in a >> Capabilities message shall be sent in the following order: >> >> 1. The vSafe5V Fixed Supply Object shall always be the first object. >> 2. The remaining Fixed Supply Objects, if present, shall be sent >> in voltage order; lowest to highest. >> 3. The Battery Supply Objects, if present shall be sent in Minimum >> Voltage order; lowest to highest. >> 4. The Variable Supply (non-battery) Objects, if present, shall be >> sent in Minimum Voltage order; lowest to highest. >> >> Errors in source/sink_caps of the local port will prevent >> the port registration. Whereas, errors in source caps of partner >> device would only log them. >> >> Signed-off-by: Badhri Jagan Sridharan <Badhri@xxxxxxxxxx> > > It would have been nice to have enum for the return values used in > tcpm_cap_err() IMHO, but never mind. Is Guenter OK with these? > > FWIW: > > Acked-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> > > > Thanks, > >> --- >> hangelog since v1: >> - rebased on top drivers/usb/typec/tcpm.c as suggested by >> gregkh@xxxxxxxxxxxxxxxxxxx >> - renamed nr_snk_*_pdo as suggested by dan.carpenter@xxxxxxxxxx >> - removed stale comment as suggested by linux@xxxxxxxxxxxx >> - removed the tests for nr_snk_*_pdo as suggested by >> dan.carpenter@xxxxxxxxxx >> - Fixed sytling as suggested by dan.carpenter@xxxxxxxxxx >> - renamed tcpm_get_nr_type_pdos to nr_type_pdos as suggested by >> dan.carpenter@xxxxxxxxxx >> - fixed nr_type_pdos as suggested by dan.carpenter@xxxxxxxxxx >> - tcpm_pd_select_pdo now checks for all matching variable/batt pdos >> instead of the first matching one. >> >> Changelog since v2: >> - refactored error messages as suggested by >> heikki.krogerus@xxxxxxxxxxxxxxx >> >> Changelog since v3: >> - fixed formatting errors as suggested by >> heikki.krogerus@xxxxxxxxxxxxxxx >> >> Changelog since v4: >> - Reusing the macro >> >> drivers/usb/typec/tcpm.c | 115 +++++++++++++++++++++++++++++++++++++++++++---- >> include/linux/usb/pd.h | 2 + >> include/linux/usb/tcpm.h | 16 +++---- >> 3 files changed, 116 insertions(+), 17 deletions(-) >> >> diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c >> index 2b735f3e5765..8b30ab69ed79 100644 >> --- a/drivers/usb/typec/tcpm.c >> +++ b/drivers/usb/typec/tcpm.c >> @@ -1256,6 +1256,85 @@ static void vdm_state_machine_work(struct work_struct *work) >> mutex_unlock(&port->lock); >> } >> >> +static const char * const pdo_err[] = { >> + "", >> + " err: source/sink caps should atleast have vSafe5V", >> + " err: vSafe5V Fixed Supply Object Shall always be the first object", >> + " err: PDOs should be in the following order: Fixed; Battery; Variable", >> + " err: Fixed supply pdos should be in increasing order of their fixed voltage", >> + " err: Variable/Battery supply pdos should be in increasing order of their minimum voltage", >> + " err: Variable/Batt supply pdos cannot have same min/max voltage", >> +}; >> + >> +static int tcpm_caps_err(struct tcpm_port *port, const u32 *pdo, >> + unsigned int nr_pdo) >> +{ >> + unsigned int i; >> + >> + /* Should at least contain vSafe5v */ >> + if (nr_pdo < 1) >> + return 1; >> + >> + /* The vSafe5V Fixed Supply Object Shall always be the first object */ >> + if (pdo_type(pdo[0]) != PDO_TYPE_FIXED || >> + pdo_fixed_voltage(pdo[0]) != VSAFE5V) >> + return 2; >> + >> + for (i = 1; i < nr_pdo; i++) { >> + if (pdo_type(pdo[i]) < pdo_type(pdo[i - 1])) { >> + return 3; >> + } else if (pdo_type(pdo[i]) == pdo_type(pdo[i - 1])) { >> + enum pd_pdo_type type = pdo_type(pdo[i]); >> + >> + switch (type) { >> + /* >> + * The remaining Fixed Supply Objects, if >> + * present, shall be sent in voltage order; >> + * lowest to highest. >> + */ >> + case PDO_TYPE_FIXED: >> + if (pdo_fixed_voltage(pdo[i]) <= >> + pdo_fixed_voltage(pdo[i - 1])) >> + return 4; >> + break; >> + /* >> + * The Battery Supply Objects and Variable >> + * supply, if present shall be sent in Minimum >> + * Voltage order; lowest to highest. >> + */ >> + case PDO_TYPE_VAR: >> + case PDO_TYPE_BATT: >> + if (pdo_min_voltage(pdo[i]) < >> + pdo_min_voltage(pdo[i - 1])) >> + return 5; >> + else if ((pdo_min_voltage(pdo[i]) == >> + pdo_min_voltage(pdo[i - 1])) && >> + (pdo_max_voltage(pdo[i]) == >> + pdo_min_voltage(pdo[i - 1]))) >> + return 6; >> + break; >> + default: >> + tcpm_log_force(port, " Unknown pdo type"); >> + } >> + } >> + } >> + >> + return 0; >> +} >> + >> +static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo, >> + unsigned int nr_pdo) >> +{ >> + int err_index = tcpm_caps_err(port, pdo, nr_pdo); >> + >> + if (err_index) { >> + tcpm_log_force(port, " %s", pdo_err[err_index]); >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> /* >> * PD (data, control) command handling functions >> */ >> @@ -1278,6 +1357,9 @@ static void tcpm_pd_data_request(struct tcpm_port *port, >> >> tcpm_log_source_caps(port); >> >> + tcpm_validate_caps(port, port->source_caps, >> + port->nr_source_caps); >> + >> /* >> * This message may be received even if VBUS is not >> * present. This is quite unexpected; see USB PD >> @@ -3444,9 +3526,12 @@ static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo, >> return nr_vdo; >> } >> >> -void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, >> - unsigned int nr_pdo) >> +int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, >> + unsigned int nr_pdo) >> { >> + if (tcpm_validate_caps(port, pdo, nr_pdo)) >> + return -EINVAL; >> + >> mutex_lock(&port->lock); >> port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, pdo, nr_pdo); >> switch (port->state) { >> @@ -3466,16 +3551,20 @@ void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, >> break; >> } >> mutex_unlock(&port->lock); >> + return 0; >> } >> EXPORT_SYMBOL_GPL(tcpm_update_source_capabilities); >> >> -void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, >> - unsigned int nr_pdo, >> - unsigned int max_snk_mv, >> - unsigned int max_snk_ma, >> - unsigned int max_snk_mw, >> - unsigned int operating_snk_mw) >> +int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, >> + unsigned int nr_pdo, >> + unsigned int max_snk_mv, >> + unsigned int max_snk_ma, >> + unsigned int max_snk_mw, >> + unsigned int operating_snk_mw) >> { >> + if (tcpm_validate_caps(port, pdo, nr_pdo)) >> + return -EINVAL; >> + >> mutex_lock(&port->lock); >> port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo); >> port->max_snk_mv = max_snk_mv; >> @@ -3494,6 +3583,7 @@ void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, >> break; >> } >> mutex_unlock(&port->lock); >> + return 0; >> } >> EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities); >> >> @@ -3529,7 +3619,15 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) >> >> init_completion(&port->tx_complete); >> init_completion(&port->swap_complete); >> + tcpm_debugfs_init(port); >> >> + if (tcpm_validate_caps(port, tcpc->config->src_pdo, >> + tcpc->config->nr_src_pdo) || >> + tcpm_validate_caps(port, tcpc->config->snk_pdo, >> + tcpc->config->nr_snk_pdo)) { >> + err = -EINVAL; >> + goto out_destroy_wq; >> + } >> port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo, >> tcpc->config->nr_src_pdo); >> port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo, >> @@ -3584,7 +3682,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) >> } >> } >> >> - tcpm_debugfs_init(port); >> mutex_lock(&port->lock); >> tcpm_init(port); >> mutex_unlock(&port->lock); >> diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h >> index e00051ced806..b3d41d7409b3 100644 >> --- a/include/linux/usb/pd.h >> +++ b/include/linux/usb/pd.h >> @@ -148,6 +148,8 @@ enum pd_pdo_type { >> (PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \ >> PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma)) >> >> +#define VSAFE5V 5000 /* mv units */ >> + >> #define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */ >> #define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */ >> #define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */ >> diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h >> index 073197f0d2bb..ca1c0b57f03f 100644 >> --- a/include/linux/usb/tcpm.h >> +++ b/include/linux/usb/tcpm.h >> @@ -183,14 +183,14 @@ struct tcpm_port; >> struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc); >> void tcpm_unregister_port(struct tcpm_port *port); >> >> -void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, >> - unsigned int nr_pdo); >> -void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, >> - unsigned int nr_pdo, >> - unsigned int max_snk_mv, >> - unsigned int max_snk_ma, >> - unsigned int max_snk_mw, >> - unsigned int operating_snk_mw); >> +int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, >> + unsigned int nr_pdo); >> +int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, >> + unsigned int nr_pdo, >> + unsigned int max_snk_mv, >> + unsigned int max_snk_ma, >> + unsigned int max_snk_mw, >> + unsigned int operating_snk_mw); >> >> void tcpm_vbus_change(struct tcpm_port *port); >> void tcpm_cc_change(struct tcpm_port *port); >> -- >> 2.15.0.403.gc27cc4dac6-goog > > -- > heikki -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html