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;
*/
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,
}
kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
- complete(&pd->swap_complete);
+ complete(&pd->is_ready);
dual_role_instance_changed(pd->dual_role);
break;
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;
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);
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 &&
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 &&
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,
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) {
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);