diff options
author | Jack Pham <jackp@codeaurora.org> | 2017-01-26 13:28:19 -0800 |
---|---|---|
committer | Jack Pham <jackp@codeaurora.org> | 2017-02-10 17:42:06 -0800 |
commit | 47fdec0cf105986642b30e3e901db4930860996f (patch) | |
tree | 91e0717a85f3c7ce7a70218a09996559b59389c7 /drivers | |
parent | dd6494d94ef819c093157f73a314e9927305d305 (diff) |
usb: pd: Make select_pdo_store() wait until request goes through
When sending a request through the 'select_pdo' sysfs file,
make sure the request has gone through and is acknowledged
by the source before returning. This allows for checking if
the request was rejected so that an error can be returned.
Rename the 'swap_complete' completion variable to 'is_ready'
so it can be reused in this context. Also add a mutex so that
select_pdo will not contend with a dual_role swap.
Change-Id: Ie8b088faa15c67915e3bd96972b4f59f0cc66afe
Signed-off-by: Jack Pham <jackp@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/pd/policy_engine.c | 73 |
1 files changed, 58 insertions, 15 deletions
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 611750e209f9..43fbc5aab073 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -338,8 +338,10 @@ struct usbpd { enum power_role current_pr; bool in_pr_swap; bool pd_phy_opened; - struct completion swap_complete; + bool send_request; + struct completion is_ready; + struct mutex swap_lock; struct dual_role_phy_instance *dual_role; struct dual_role_phy_desc dr_desc; bool send_pr_swap; @@ -463,6 +465,9 @@ static inline void pd_reset_protocol(struct usbpd *pd) */ pd->rx_msgid = -1; pd->tx_msgid = 0; + pd->send_request = false; + pd->send_pr_swap = false; + pd->send_dr_swap = false; } static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data, @@ -842,7 +847,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); - complete(&pd->swap_complete); + complete(&pd->is_ready); dual_role_instance_changed(pd->dual_role); break; @@ -977,7 +982,7 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) case PE_SNK_READY: pd->in_explicit_contract = true; kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE); - complete(&pd->swap_complete); + complete(&pd->is_ready); dual_role_instance_changed(pd->dual_role); break; @@ -2075,6 +2080,9 @@ static void usbpd_sm(struct work_struct *w) vconn_swap(pd); } else if (IS_DATA(rx_msg, MSG_VDM)) { handle_vdm_rx(pd, rx_msg); + } else if (pd->send_request) { + pd->send_request = false; + usbpd_set_state(pd, PE_SNK_SELECT_CAPABILITY); } else if (pd->send_pr_swap && is_sink_tx_ok(pd)) { pd->send_pr_swap = false; ret = pd_send_msg(pd, MSG_PR_SWAP, NULL, 0, SOP_MSG); @@ -2540,17 +2548,21 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role, return -EAGAIN; } - reinit_completion(&pd->swap_complete); + mutex_lock(&pd->swap_lock); + reinit_completion(&pd->is_ready); pd->send_dr_swap = true; kick_sm(pd, 0); /* wait for operation to complete */ - if (!wait_for_completion_timeout(&pd->swap_complete, + if (!wait_for_completion_timeout(&pd->is_ready, msecs_to_jiffies(100))) { usbpd_err(&pd->dev, "data_role swap timed out\n"); + mutex_unlock(&pd->swap_lock); return -ETIMEDOUT; } + mutex_unlock(&pd->swap_lock); + if ((*val == DUAL_ROLE_PROP_DR_HOST && pd->current_dr != DR_DFP) || (*val == DUAL_ROLE_PROP_DR_DEVICE && @@ -2591,17 +2603,21 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role, return -EAGAIN; } - reinit_completion(&pd->swap_complete); + mutex_lock(&pd->swap_lock); + reinit_completion(&pd->is_ready); pd->send_pr_swap = true; kick_sm(pd, 0); /* wait for operation to complete */ - if (!wait_for_completion_timeout(&pd->swap_complete, + if (!wait_for_completion_timeout(&pd->is_ready, msecs_to_jiffies(2000))) { usbpd_err(&pd->dev, "power_role swap timed out\n"); + mutex_unlock(&pd->swap_lock); return -ETIMEDOUT; } + mutex_unlock(&pd->swap_lock); + if ((*val == DUAL_ROLE_PROP_PR_SRC && pd->current_pr != PR_SRC) || (*val == DUAL_ROLE_PROP_PR_SNK && @@ -2864,36 +2880,62 @@ static ssize_t select_pdo_store(struct device *dev, int pdo, uv = 0, ua = 0; int ret; + mutex_lock(&pd->swap_lock); + /* Only allowed if we are already in explicit sink contract */ if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { usbpd_err(&pd->dev, "select_pdo: Cannot select new PDO yet\n"); - return -EBUSY; + ret = -EBUSY; + goto out; } ret = sscanf(buf, "%d %d %d %d", &src_cap_id, &pdo, &uv, &ua); if (ret != 2 && ret != 4) { usbpd_err(&pd->dev, "select_pdo: Must specify <src cap id> <PDO> [<uV> <uA>]\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } if (src_cap_id != pd->src_cap_id) { usbpd_err(&pd->dev, "select_pdo: src_cap_id mismatch. Requested:%d, current:%d\n", src_cap_id, pd->src_cap_id); - return -EINVAL; + ret = -EINVAL; + goto out; } if (pdo < 1 || pdo > 7) { usbpd_err(&pd->dev, "select_pdo: invalid PDO:%d\n", pdo); - return -EINVAL; + ret = -EINVAL; + goto out; } ret = pd_select_pdo(pd, pdo, uv, ua); if (ret) - return ret; + goto out; - usbpd_set_state(pd, PE_SNK_SELECT_CAPABILITY); + reinit_completion(&pd->is_ready); + pd->send_request = true; + kick_sm(pd, 0); - return size; + /* wait for operation to complete */ + if (!wait_for_completion_timeout(&pd->is_ready, + msecs_to_jiffies(1000))) { + usbpd_err(&pd->dev, "select_pdo: request timed out\n"); + ret = -ETIMEDOUT; + goto out; + } + + /* determine if request was accepted/rejected */ + if (pd->selected_pdo != pd->requested_pdo || + pd->current_voltage != pd->requested_voltage) { + usbpd_err(&pd->dev, "select_pdo: request rejected\n"); + ret = -EINVAL; + } + +out: + pd->send_request = false; + mutex_unlock(&pd->swap_lock); + return ret ? ret : size; } static ssize_t select_pdo_show(struct device *dev, @@ -3123,6 +3165,7 @@ struct usbpd *usbpd_create(struct device *parent) INIT_WORK(&pd->sm_work, usbpd_sm); hrtimer_init(&pd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); pd->timer.function = pd_timeout; + mutex_init(&pd->swap_lock); pd->usb_psy = power_supply_get_by_name("usb"); if (!pd->usb_psy) { @@ -3233,7 +3276,7 @@ struct usbpd *usbpd_create(struct device *parent) spin_lock_init(&pd->rx_lock); INIT_LIST_HEAD(&pd->rx_q); INIT_LIST_HEAD(&pd->svid_handlers); - init_completion(&pd->swap_complete); + init_completion(&pd->is_ready); pd->psy_nb.notifier_call = psy_changed; ret = power_supply_reg_notifier(&pd->psy_nb); |