Hi Heikki > -----Original Message----- > From: linux-usb-ow...@vger.kernel.org <linux-usb-ow...@vger.kernel.org> On > Behalf Of Heikki Krogerus > Sent: Friday, February 1, 2019 2:48 AM > To: Greg Kroah-Hartman <gre...@linuxfoundation.org>; Ajay Gupta > <aj...@nvidia.com>; Michael Hsu <m...@nvidia.com> > Cc: linux-usb@vger.kernel.org > Subject: [PATCH 4/5] usb: typec: ucsi: Preliminary support for alternate modes > > With UCSI the alternate modes, just like everything else related to USB Type-C > connectors, are handled in firmware. > The operating system can see the status and is allowed to request certain > things, > for example entering and exiting the modes, but the support for alternate > modes is very limited in UCSI. The feature is also optional, which means that > even when the platform supports alternate modes, the operating system may > not be even made aware of them. > > UCSI does not support direct VDM reading or writing. > Instead, alternate modes can be entered and exited using a single custom > command which takes also an optional SVID specific configuration value as > parameter. That means every supported alternate mode has to be handled > separately in UCSI driver. > > This commit does not include support for any specific alternate mode. The > discovered alternate modes are now registered, but binding a driver to an > alternate mode will not be possible until support for that alternate mode is > added to the UCSI driver. > > Signed-off-by: Heikki Krogerus <heikki.kroge...@linux.intel.com> > --- > drivers/usb/typec/ucsi/trace.c | 12 ++ drivers/usb/typec/ucsi/trace.h | > 26 +++ > drivers/usb/typec/ucsi/ucsi.c | 351 +++++++++++++++++++++++++++------ > drivers/usb/typec/ucsi/ucsi.h | 72 +++++++ > 4 files changed, 396 insertions(+), 65 deletions(-) > > diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c > index > ffa3b4c3f338..1dabafb74320 100644 > --- a/drivers/usb/typec/ucsi/trace.c > +++ b/drivers/usb/typec/ucsi/trace.c > @@ -60,3 +60,15 @@ const char *ucsi_cci_str(u32 cci) > > return ""; > } > + > +static const char * const ucsi_recipient_strs[] = { > + [UCSI_RECIPIENT_CON] = "port", > + [UCSI_RECIPIENT_SOP] = "partner", > + [UCSI_RECIPIENT_SOP_P] = "plug (prime)", > + [UCSI_RECIPIENT_SOP_PP] = "plug (double prime)", > +}; > + > +const char *ucsi_recipient_str(u8 recipient) { > + return ucsi_recipient_strs[recipient]; } > diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h > index > 5e2906df2db7..783ec9c72055 100644 > --- a/drivers/usb/typec/ucsi/trace.h > +++ b/drivers/usb/typec/ucsi/trace.h > @@ -7,6 +7,7 @@ > #define __UCSI_TRACE_H > > #include <linux/tracepoint.h> > +#include <linux/usb/typec_altmode.h> > > const char *ucsi_cmd_str(u64 raw_cmd); > const char *ucsi_ack_str(u8 ack); > @@ -134,6 +135,31 @@ DEFINE_EVENT(ucsi_log_connector_status, > ucsi_register_port, > TP_ARGS(port, status) > ); > > +DECLARE_EVENT_CLASS(ucsi_log_register_altmode, > + TP_PROTO(u8 recipient, struct typec_altmode *alt), > + TP_ARGS(recipient, alt), > + TP_STRUCT__entry( > + __field(u8, recipient) > + __field(u16, svid) > + __field(u8, mode) > + __field(u32, vdo) > + ), > + TP_fast_assign( > + __entry->recipient = recipient; > + __entry->svid = alt->svid; > + __entry->mode = alt->mode; > + __entry->vdo = alt->vdo; > + ), > + TP_printk("%s alt mode: svid %04x, mode %d vdo %x", > + ucsi_recipient_str(__entry->recipient), __entry->svid, > + __entry->mode, __entry->vdo) > +); > + > +DEFINE_EVENT(ucsi_log_register_altmode, ucsi_register_altmode, > + TP_PROTO(u8 recipient, struct typec_altmode *alt), > + TP_ARGS(recipient, alt) > +); > + > #endif /* __UCSI_TRACE_H */ > > /* This part must be outside protection */ diff --git > a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index > 8d0a6fe748bd..5190f8dd4548 100644 > --- a/drivers/usb/typec/ucsi/ucsi.c > +++ b/drivers/usb/typec/ucsi/ucsi.c > @@ -12,7 +12,7 @@ > #include <linux/module.h> > #include <linux/delay.h> > #include <linux/slab.h> > -#include <linux/usb/typec.h> > +#include <linux/usb/typec_altmode.h> > > #include "ucsi.h" > #include "trace.h" > @@ -45,22 +45,6 @@ enum ucsi_status { > UCSI_ERROR, > }; > > -struct ucsi_connector { > - int num; > - > - struct ucsi *ucsi; > - struct work_struct work; > - struct completion complete; > - > - struct typec_port *port; > - struct typec_partner *partner; > - > - struct typec_capability typec_cap; > - > - struct ucsi_connector_status status; > - struct ucsi_connector_capability cap; > -}; > - > struct ucsi { > struct device *dev; > struct ucsi_ppm *ppm; > @@ -238,8 +222,201 @@ static int ucsi_run_command(struct ucsi *ucsi, struct > ucsi_control *ctrl, > return ret; > } > > +int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, > + void *retval, size_t size) > +{ > + int ret; > + > + mutex_lock(&ucsi->ppm_lock); > + ret = ucsi_run_command(ucsi, ctrl, retval, size); > + mutex_unlock(&ucsi->ppm_lock); > + > + return ret; > +} > + > /* > -------------------------------------------------------------------------- */ > > +void ucsi_altmode_update_active(struct ucsi_connector *con) { > + struct typec_altmode *altmode; > + struct ucsi_control ctrl; > + int ret; > + u8 cur; > + int i; > + > + UCSI_CMD_GET_CURRENT_CAM(ctrl, con->num); > + ret = ucsi_run_command(con->ucsi, &ctrl, &cur, sizeof(cur)); > + if (ret < 0) { > + dev_err(con->ucsi->dev, "GET_CURRENT_CAM command > failed\n"); > + return; > + } > + > + altmode = typec_match_altmode(con->partner_altmode, > UCSI_MAX_ALTMODES, > + con->port_altmode[cur]->svid, > + con->port_altmode[cur]->mode); > + > + for (i = 0; con->partner_altmode[i]; i++) > + typec_altmode_update_active(con->partner_altmode[i], > + con->partner_altmode[i] == > altmode); } > + > +static u8 ucsi_altmode_next_mode(struct typec_altmode **alt, u16 svid) > +{ > + u8 mode = 1; > + int i; > + > + for (i = 0; alt[i]; i++) > + if (alt[i]->svid == svid) > + mode++; > + > + return mode; > +} > + > +static int ucsi_next_altmode(struct typec_altmode **alt) { > + int i = 0; > + > + for (i = 0; i < UCSI_MAX_ALTMODES; i++) > + if (!alt[i]) > + return i; > + > + return -ENOENT; > +} > + > +static int ucsi_register_altmode(struct ucsi_connector *con, > + struct typec_altmode_desc *desc, > + u8 recipient) > +{ > + struct typec_altmode *alt; > + int ret; > + int i; > + > + switch (recipient) { > + case UCSI_RECIPIENT_CON: > + i = ucsi_next_altmode(con->port_altmode); > + if (i < 0) { > + ret = i; > + goto err; > + } > + > + desc->mode = ucsi_altmode_next_mode(con->port_altmode, > + desc->svid); > + > + alt = typec_port_register_altmode(con->port, desc); > + if (IS_ERR(alt)) { > + ret = PTR_ERR(alt); > + goto err; > + } > + > + con->port_altmode[i] = alt; > + break; > + case UCSI_RECIPIENT_SOP: > + i = ucsi_next_altmode(con->partner_altmode); We are seeing duplicate partner altmode devices getting created when we set "active" file from 1->0->1 Please add a check here to see if altmode device already exists.
[...] case UCSI_RECIPIENT_SOP: /* check to see if partner altmode already exists */ if (ucsi_altmode_found(con->partner_altmode, desc)) break; i = ucsi_next_altmode(con->partner_altmode); if (i < 0) { [...] static bool ucsi_altmode_found(struct typec_altmode **alt, struct typec_altmode_desc *desc) { int i; for (i = 0; i < UCSI_MAX_ALTMODES; i++) { if (!alt[i]) return false; if (alt[i]->svid == desc->svid && alt[i]->vdo == desc->vdo) return true; } return false; } thanks > nvpublic > + if (i < 0) { > + ret = i; > + goto err; > + } > + > + desc->mode = ucsi_altmode_next_mode(con- > >partner_altmode, > + desc->svid); > + > + alt = typec_partner_register_altmode(con->partner, desc); > + if (IS_ERR(alt)) { > + ret = PTR_ERR(alt); > + goto err; > + } > + > + con->partner_altmode[i] = alt; > + break; > + default: > + return -EINVAL; > + } > + > + trace_ucsi_register_altmode(recipient, alt); > + > + return 0; > + > +err: > + dev_err(con->ucsi->dev, "failed to registers svid 0x%04x mode %d\n", > + desc->svid, desc->mode); > + > + return ret; > +} > + > +static int ucsi_register_altmodes(struct ucsi_connector *con, u8 > +recipient) { > + int max_altmodes = UCSI_MAX_ALTMODES; > + struct typec_altmode_desc desc; > + struct ucsi_altmode alt[2]; > + struct ucsi_control ctrl; > + int num = 1; > + int ret; > + int len; > + int j; > + int i; > + > + if (!(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_DETAILS)) > + return 0; > + > + if (recipient == UCSI_RECIPIENT_CON) > + max_altmodes = con->ucsi->cap.num_alt_modes; > + > + for (i = 0; i < max_altmodes;) { > + memset(alt, 0, sizeof(alt)); > + UCSI_CMD_GET_ALTERNATE_MODES(ctrl, recipient, con->num, > i, 1); > + len = ucsi_run_command(con->ucsi, &ctrl, alt, sizeof(alt)); > + if (len <= 0) > + return len; > + > + /* > + * This code is requesting one alt mode at a time, but some > PPMs > + * may still return two. If that happens both alt modes need be > + * registered and the offset for the next alt mode has to be > + * incremented. > + */ > + num = len / sizeof(alt[0]); > + i += num; > + > + for (j = 0; j < num; j++) { > + if (!alt[j].svid) > + return 0; > + > + memset(&desc, 0, sizeof(desc)); > + desc.vdo = alt[j].mid; > + desc.svid = alt[j].svid; > + desc.roles = TYPEC_PORT_DRD; > + > + ret = ucsi_register_altmode(con, &desc, recipient); > + if (ret) > + return ret; > + } > + } > + > + return 0; > +} > + > +static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 > +recipient) { > + struct typec_altmode **adev; > + int i = 0; > + > + switch (recipient) { > + case UCSI_RECIPIENT_CON: > + adev = con->port_altmode; > + break; > + case UCSI_RECIPIENT_SOP: > + adev = con->partner_altmode; > + break; > + default: > + return; > + } > + > + while (adev[i]) { > + typec_unregister_altmode(adev[i]); > + adev[i++] = NULL; > + } > +} > + > static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { > switch (con->status.pwr_op_mode) { > @@ -299,10 +476,43 @@ static void ucsi_unregister_partner(struct > ucsi_connector *con) > if (!con->partner) > return; > > + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP); > typec_unregister_partner(con->partner); > con->partner = NULL; > } > > +static void ucsi_partner_change(struct ucsi_connector *con) { > + int ret; > + > + if (!con->partner) > + return; > + > + switch (con->status.partner_type) { > + case UCSI_CONSTAT_PARTNER_TYPE_UFP: > + typec_set_data_role(con->port, TYPEC_HOST); > + break; > + case UCSI_CONSTAT_PARTNER_TYPE_DFP: > + typec_set_data_role(con->port, TYPEC_DEVICE); > + break; > + default: > + break; > + } > + > + /* Complete pending data role swap */ > + if (!completion_done(&con->complete)) > + complete(&con->complete); > + > + /* Can't rely on Partner Flags field. Always checking the alt modes. */ > + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); > + if (ret) > + dev_err(con->ucsi->dev, > + "con%d: failed to register partner alternate modes\n", > + con->num); > + else > + ucsi_altmode_update_active(con); > +} > + > static void ucsi_connector_change(struct work_struct *work) { > struct ucsi_connector *con = container_of(work, struct ucsi_connector, > @@ -311,10 +521,10 @@ static void ucsi_connector_change(struct work_struct > *work) > struct ucsi_control ctrl; > int ret; > > - mutex_lock(&ucsi->ppm_lock); > + mutex_lock(&con->lock); > > UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num); > - ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status)); > + ret = ucsi_send_command(ucsi, &ctrl, &con->status, > +sizeof(con->status)); > if (ret < 0) { > dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed > (%d)\n", > __func__, ret); > @@ -332,23 +542,6 @@ static void ucsi_connector_change(struct work_struct > *work) > complete(&con->complete); > } > > - if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) { > - switch (con->status.partner_type) { > - case UCSI_CONSTAT_PARTNER_TYPE_UFP: > - typec_set_data_role(con->port, TYPEC_HOST); > - break; > - case UCSI_CONSTAT_PARTNER_TYPE_DFP: > - typec_set_data_role(con->port, TYPEC_DEVICE); > - break; > - default: > - break; > - } > - > - /* Complete pending data role swap */ > - if (!completion_done(&con->complete)) > - complete(&con->complete); > - } > - > if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) { > typec_set_pwr_role(con->port, con->status.pwr_dir); > > @@ -369,6 +562,19 @@ static void ucsi_connector_change(struct work_struct > *work) > ucsi_unregister_partner(con); > } > > + if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) { > + /* > + * We don't need to know the currently supported alt modes > here. > + * Running GET_CAM_SUPPORTED command just to make sure > the PPM > + * does not get stuck in case it assumes we do so. > + */ > + UCSI_CMD_GET_CAM_SUPPORTED(ctrl, con->num); > + ucsi_run_command(con->ucsi, &ctrl, NULL, 0); > + } > + > + if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) > + ucsi_partner_change(con); > + > ret = ucsi_ack(ucsi, UCSI_ACK_EVENT); > if (ret) > dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); @@ - > 377,7 +583,7 @@ static void ucsi_connector_change(struct work_struct *work) > > out_unlock: > clear_bit(EVENT_PENDING, &ucsi->flags); > - mutex_unlock(&ucsi->ppm_lock); > + mutex_unlock(&con->lock); > } > > /** > @@ -427,7 +633,7 @@ static int ucsi_reset_connector(struct ucsi_connector > *con, bool hard) > > UCSI_CMD_CONNECTOR_RESET(ctrl, con, hard); > > - return ucsi_run_command(con->ucsi, &ctrl, NULL, 0); > + return ucsi_send_command(con->ucsi, &ctrl, NULL, 0); > } > > static int ucsi_reset_ppm(struct ucsi *ucsi) @@ -481,15 +687,17 @@ static int > ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) { > int ret; > > - ret = ucsi_run_command(con->ucsi, ctrl, NULL, 0); > + ret = ucsi_send_command(con->ucsi, ctrl, NULL, 0); > if (ret == -ETIMEDOUT) { > struct ucsi_control c; > > /* PPM most likely stopped responding. Resetting everything. */ > + mutex_lock(&con->ucsi->ppm_lock); > ucsi_reset_ppm(con->ucsi); > + mutex_unlock(&con->ucsi->ppm_lock); > > UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL); > - ucsi_run_command(con->ucsi, &c, NULL, 0); > + ucsi_send_command(con->ucsi, &c, NULL, 0); > > ucsi_reset_connector(con, true); > } > @@ -504,10 +712,12 @@ ucsi_dr_swap(const struct typec_capability *cap, > enum typec_data_role role) > struct ucsi_control ctrl; > int ret = 0; > > - if (!con->partner) > - return -ENOTCONN; > + mutex_lock(&con->lock); > > - mutex_lock(&con->ucsi->ppm_lock); > + if (!con->partner) { > + ret = -ENOTCONN; > + goto out_unlock; > + } > > if ((con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP > && > role == TYPEC_DEVICE) || > @@ -520,18 +730,14 @@ ucsi_dr_swap(const struct typec_capability *cap, > enum typec_data_role role) > if (ret < 0) > goto out_unlock; > > - mutex_unlock(&con->ucsi->ppm_lock); > - > if (!wait_for_completion_timeout(&con->complete, > > msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) > - return -ETIMEDOUT; > - > - return 0; > + ret = -ETIMEDOUT; > > out_unlock: > - mutex_unlock(&con->ucsi->ppm_lock); > + mutex_unlock(&con->lock); > > - return ret; > + return ret < 0 ? ret : 0; > } > > static int > @@ -541,10 +747,12 @@ ucsi_pr_swap(const struct typec_capability *cap, > enum typec_role role) > struct ucsi_control ctrl; > int ret = 0; > > - if (!con->partner) > - return -ENOTCONN; > + mutex_lock(&con->lock); > > - mutex_lock(&con->ucsi->ppm_lock); > + if (!con->partner) { > + ret = -ENOTCONN; > + goto out_unlock; > + } > > if (con->status.pwr_dir == role) > goto out_unlock; > @@ -554,13 +762,11 @@ ucsi_pr_swap(const struct typec_capability *cap, > enum typec_role role) > if (ret < 0) > goto out_unlock; > > - mutex_unlock(&con->ucsi->ppm_lock); > - > if (!wait_for_completion_timeout(&con->complete, > - > msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) > - return -ETIMEDOUT; > - > - mutex_lock(&con->ucsi->ppm_lock); > + msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) { > + ret = -ETIMEDOUT; > + goto out_unlock; > + } > > /* Something has gone wrong while swapping the role */ > if (con->status.pwr_op_mode != UCSI_CONSTAT_PWR_OPMODE_PD) { > @@ -569,7 +775,7 @@ ucsi_pr_swap(const struct typec_capability *cap, enum > typec_role role) > } > > out_unlock: > - mutex_unlock(&con->ucsi->ppm_lock); > + mutex_unlock(&con->lock); > > return ret; > } > @@ -595,6 +801,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int > index) > > INIT_WORK(&con->work, ucsi_connector_change); > init_completion(&con->complete); > + mutex_init(&con->lock); > con->num = index + 1; > con->ucsi = ucsi; > > @@ -636,6 +843,12 @@ static int ucsi_register_port(struct ucsi *ucsi, int > index) > if (IS_ERR(con->port)) > return PTR_ERR(con->port); > > + /* Alternate modes */ > + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON); > + if (ret) > + dev_err(ucsi->dev, "con%d: failed to register alt modes\n", > + con->num); > + > /* Get the status */ > UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num); > ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status)); > @@ -662,6 +875,16 @@ static int ucsi_register_port(struct ucsi *ucsi, int > index) > if (con->status.connected) > ucsi_register_partner(con); > > + if (con->partner) { > + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); > + if (ret) > + dev_err(ucsi->dev, > + "con%d: failed to register alternate modes\n", > + con->num); > + else > + ucsi_altmode_update_active(con); > + } > + > trace_ucsi_register_port(con->num, &con->status); > > return 0; > @@ -788,17 +1011,15 @@ void ucsi_unregister_ppm(struct ucsi *ucsi) > /* Make sure that we are not in the middle of driver initialization */ > cancel_work_sync(&ucsi->work); > > - mutex_lock(&ucsi->ppm_lock); > - > /* Disable everything except command complete notification */ > UCSI_CMD_SET_NTFY_ENABLE(ctrl, > UCSI_ENABLE_NTFY_CMD_COMPLETE) > - ucsi_run_command(ucsi, &ctrl, NULL, 0); > - > - mutex_unlock(&ucsi->ppm_lock); > + ucsi_send_command(ucsi, &ctrl, NULL, 0); > > for (i = 0; i < ucsi->cap.num_connectors; i++) { > cancel_work_sync(&ucsi->connector[i].work); > ucsi_unregister_partner(&ucsi->connector[i]); > + ucsi_unregister_altmodes(&ucsi->connector[i], > + UCSI_RECIPIENT_CON); > typec_unregister_port(ucsi->connector[i].port); > } > > diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h > index > 53b80f40a908..c416bae4b5ca 100644 > --- a/drivers/usb/typec/ucsi/ucsi.h > +++ b/drivers/usb/typec/ucsi/ucsi.h > @@ -6,6 +6,7 @@ > #include <linux/bitops.h> > #include <linux/device.h> > #include <linux/types.h> > +#include <linux/usb/typec.h> > > /* > -------------------------------------------------------------------------- */ > > @@ -60,6 +61,20 @@ struct ucsi_uor_cmd { > u16:6; /* reserved */ > } __packed; > > +/* Get Alternate Modes Command structure */ struct ucsi_altmode_cmd { > + u8 cmd; > + u8 length; > + u8 recipient; > +#define UCSI_RECIPIENT_CON 0 > +#define UCSI_RECIPIENT_SOP 1 > +#define UCSI_RECIPIENT_SOP_P 2 > +#define UCSI_RECIPIENT_SOP_PP 3 > + u8 con_num; > + u8 offset; > + u8 num_altmodes; > +} __packed; > + > struct ucsi_control { > union { > u64 raw_cmd; > @@ -67,6 +82,7 @@ struct ucsi_control { > struct ucsi_uor_cmd uor; > struct ucsi_ack_cmd ack; > struct ucsi_con_rst con_rst; > + struct ucsi_altmode_cmd alt; > }; > }; > > @@ -112,6 +128,30 @@ struct ucsi_control { > (_ctrl_).cmd.data = _con_; \ > } > > +/* Helper for preparing ucsi_control for GET_ALTERNATE_MODES command. > +*/ #define UCSI_CMD_GET_ALTERNATE_MODES(_ctrl_, _r_, _con_num_, _o_, > _num_)\ > +{ \ > + __UCSI_CMD((_ctrl_), UCSI_GET_ALTERNATE_MODES) > \ > + _ctrl_.alt.recipient = (_r_); \ > + _ctrl_.alt.con_num = (_con_num_); \ > + _ctrl_.alt.offset = (_o_); \ > + _ctrl_.alt.num_altmodes = (_num_) - 1; \ > +} > + > +/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */ > +#define UCSI_CMD_GET_CAM_SUPPORTED(_ctrl_, _con_) > \ > +{ \ > + __UCSI_CMD((_ctrl_), UCSI_GET_CAM_SUPPORTED) > \ > + _ctrl_.cmd.data = (_con_); \ > +} > + > +/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */ > +#define UCSI_CMD_GET_CURRENT_CAM(_ctrl_, _con_) \ > +{ \ > + __UCSI_CMD((_ctrl_), UCSI_GET_CURRENT_CAM) > \ > + _ctrl_.cmd.data = (_con_); \ > +} > + > /* Helper for preparing ucsi_control for GET_CONNECTOR_STATUS command. > */ > #define UCSI_CMD_GET_CONNECTOR_STATUS(_ctrl_, _con_) > \ > { \ > @@ -334,4 +374,36 @@ struct ucsi *ucsi_register_ppm(struct device *dev, > struct ucsi_ppm *ppm); void ucsi_unregister_ppm(struct ucsi *ucsi); void > ucsi_notify(struct ucsi *ucsi); > > +/* > +----------------------------------------------------------------------- > +--- */ > + > +struct ucsi; > + > +#define UCSI_MAX_SVID 5 > +#define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6) > + > +struct ucsi_connector { > + int num; > + > + struct ucsi *ucsi; > + struct mutex lock; /* port lock */ > + struct work_struct work; > + struct completion complete; > + > + struct typec_port *port; > + struct typec_partner *partner; > + > + struct typec_altmode *port_altmode[UCSI_MAX_ALTMODES]; > + struct typec_altmode *partner_altmode[UCSI_MAX_ALTMODES]; > + > + struct typec_capability typec_cap; > + > + struct ucsi_connector_status status; > + struct ucsi_connector_capability cap; > +}; > + > +int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, > + void *retval, size_t size); > + > +void ucsi_altmode_update_active(struct ucsi_connector *con); > + > #endif /* __DRIVER_USB_TYPEC_UCSI_H */ > -- > 2.20.1