Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f558789a authored by Xu Yang's avatar Xu Yang Committed by Greg Kroah-Hartman
Browse files

usb: chipidea: core: fix possible concurrent when switch role



commit 451b15ed138ec15bffbebb58a00ebdd884c3e659 upstream.

The user may call role_store() when driver is handling
ci_handle_id_switch() which is triggerred by otg event or power lost
event. Unfortunately, the controller may go into chaos in this case.
Fix this by protecting it with mutex lock.

Fixes: a932a804 ("usb: chipidea: core: add sysfs group")
cc: <stable@vger.kernel.org>
Acked-by: default avatarPeter Chen <peter.chen@kernel.org>
Signed-off-by: default avatarXu Yang <xu.yang_2@nxp.com>
Link: https://lore.kernel.org/r/20230317061516.2451728-2-xu.yang_2@nxp.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 6b3287b1
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -203,6 +203,7 @@ struct hw_bank {
 * @in_lpm: if the core in low power mode
 * @wakeup_int: if wakeup interrupt occur
 * @rev: The revision number for controller
 * @mutex: protect code from concorrent running when doing role switch
 */
struct ci_hdrc {
	struct device			*dev;
@@ -256,6 +257,7 @@ struct ci_hdrc {
	bool				in_lpm;
	bool				wakeup_int;
	enum ci_revision		rev;
	struct mutex                    mutex;
};

static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
+7 −1
Original line number Diff line number Diff line
@@ -963,8 +963,12 @@ static ssize_t role_store(struct device *dev,
	if (role == CI_ROLE_END)
		return -EINVAL;

	if (role == ci->role)
	mutex_lock(&ci->mutex);

	if (role == ci->role) {
		mutex_unlock(&ci->mutex);
		return n;
	}

	pm_runtime_get_sync(dev);
	disable_irq(ci->irq);
@@ -974,6 +978,7 @@ static ssize_t role_store(struct device *dev,
		ci_handle_vbus_change(ci);
	enable_irq(ci->irq);
	pm_runtime_put_sync(dev);
	mutex_unlock(&ci->mutex);

	return (ret == 0) ? n : ret;
}
@@ -1009,6 +1014,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
		return -ENOMEM;

	spin_lock_init(&ci->lock);
	mutex_init(&ci->mutex);
	ci->dev = dev;
	ci->platdata = dev_get_platdata(dev);
	ci->imx28_write_fix = !!(ci->platdata->flags &
+4 −1
Original line number Diff line number Diff line
@@ -164,8 +164,10 @@ static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)

static void ci_handle_id_switch(struct ci_hdrc *ci)
{
	enum ci_role role = ci_otg_role(ci);
	enum ci_role role;

	mutex_lock(&ci->mutex);
	role = ci_otg_role(ci);
	if (role != ci->role) {
		dev_dbg(ci->dev, "switching from %s to %s\n",
			ci_role(ci)->name, ci->roles[role]->name);
@@ -188,6 +190,7 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
		if (role == CI_ROLE_GADGET)
			ci_handle_vbus_change(ci);
	}
	mutex_unlock(&ci->mutex);
}
/**
 * ci_otg_work - perform otg (vbus/id) event handle