diff --git a/drivers/hid/hid-lenovo-go-s.c b/drivers/hid/hid-lenovo-go-s.c index 68301d4c762a..36505d8402ff 100644 --- a/drivers/hid/hid-lenovo-go-s.c +++ b/drivers/hid/hid-lenovo-go-s.c @@ -37,6 +37,7 @@ static struct hid_gos_cfg { struct completion send_cmd_complete; struct led_classdev *led_cdev; struct hid_device *hdev; + bool orphan_ack_pending; struct mutex cfg_mutex; /*ensure single synchronous output report*/ int cmd_status; u8 gp_auto_sleep_time; @@ -454,6 +455,19 @@ static int mcu_property_out(struct hid_device *hdev, u8 command, u8 index, return -EINVAL; guard(mutex)(&drvdata.cfg_mutex); + + /* + * A reply to the previous command may still be in flight. Give it a + * short window to arrive and be consumed before this call reinits the + * completion, so a late reply can't be mistaken for this command's. + */ + if (drvdata.orphan_ack_pending) { + wait_for_completion_timeout(&drvdata.send_cmd_complete, msecs_to_jiffies(25)); + drvdata.orphan_ack_pending = false; + drvdata.cmd_status = -ETIMEDOUT; + } + reinit_completion(&drvdata.send_cmd_complete); + /* We can't use a devm_alloc reusable buffer without side effects during suspend */ dmabuf = kzalloc(GO_S_PACKET_SIZE, GFP_KERNEL); if (!dmabuf) @@ -479,7 +493,9 @@ static int mcu_property_out(struct hid_device *hdev, u8 command, u8 index, msecs_to_jiffies(timeout)); ret = ret > 0 ? drvdata.cmd_status : ret ?: -EBUSY; - reinit_completion(&drvdata.send_cmd_complete); + if (ret) + drvdata.orphan_ack_pending = true; + return ret; }