Disconnecting and reconnecting the USB cable can lead to crashes and a variety of kernel log spam. The problem was found and reproduced on the Raspberry Pi. The patch was created in Raspberry's own fork [1]. Since I was one of those who discovered and diagnosed the problem [2], I propose this patch for adoption to the kernel upstream with the consent of the author. It has been tested since January 2021 and is guaranteed to fix the described problem without breaking anything. Signed-off-by: Maxim Devaev <mdevaev@xxxxxxxxx> Signed-off-by: Phil Elwell <phil@xxxxxxxxxxxxxxx> Link: https://github.com/raspberrypi/linux/commit/a6e47d5f4efbd2ea6a0b6565cd2f9b7bb217ded5 [1] Link: https://github.com/raspberrypi/linux/issues/3870 [2] --- drivers/usb/gadget/function/f_hid.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 02683ac07..4975bbf02 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -338,6 +338,11 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, spin_lock_irqsave(&hidg->write_spinlock, flags); + if (!hidg->req) { + spin_unlock_irqrestore(&hidg->write_spinlock, flags); + return -ESHUTDOWN; + } + #define WRITE_COND (!hidg->write_pending) try_again: /* write queue */ @@ -358,7 +363,13 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, count = min_t(unsigned, count, hidg->report_length); spin_unlock_irqrestore(&hidg->write_spinlock, flags); - status = copy_from_user(req->buf, buffer, count); + if (req) { + status = copy_from_user(req->buf, buffer, count); + } else { + ERROR(hidg->func.config->cdev, "hidg->req is NULL\n"); + status = -ESHUTDOWN; + goto release_write_pending; + } if (status != 0) { ERROR(hidg->func.config->cdev, @@ -387,10 +398,13 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, spin_unlock_irqrestore(&hidg->write_spinlock, flags); + if (!hidg->in_ep->enabled) { + ERROR(hidg->func.config->cdev, "in_ep is disabled\n"); + status = -ESHUTDOWN; + goto release_write_pending; + } status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC); if (status < 0) { - ERROR(hidg->func.config->cdev, - "usb_ep_queue error on int endpoint %zd\n", status); goto release_write_pending; } else { status = count; -- 2.32.0