@@ -75,6 +75,7 @@ struct f_hidg {
/* recv report */
spinlock_t read_spinlock;
wait_queue_head_t read_queue;
+ bool disabled;
/* recv report - interrupt out only (use_out_ep == 1) */
struct list_head completed_out_req;
unsigned int qlen;
@@ -329,7 +330,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
spin_lock_irqsave(&hidg->read_spinlock, flags);
-#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req))
+#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req) || hidg->disabled)
/* wait for at least one buffer to complete */
while (!READ_COND_INTOUT) {
@@ -343,6 +344,11 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
spin_lock_irqsave(&hidg->read_spinlock, flags);
}
+ if (hidg->disabled) {
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+ return -ESHUTDOWN;
+ }
+
/* pick the first one */
list = list_first_entry(&hidg->completed_out_req,
struct f_hidg_req_list, list);
@@ -387,7 +393,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
return count;
}
-#define READ_COND_SSREPORT (hidg->set_report_buf != NULL)
+#define READ_COND_SSREPORT (hidg->set_report_buf != NULL || hidg->disabled)
static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer,
size_t count, loff_t *ptr)
@@ -1012,6 +1018,11 @@ static void hidg_disable(struct usb_function *f)
}
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
+ spin_lock_irqsave(&hidg->read_spinlock, flags);
+ hidg->disabled = true;
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+ wake_up(&hidg->read_queue);
+
spin_lock_irqsave(&hidg->write_spinlock, flags);
if (!hidg->write_pending) {
free_ep_req(hidg->in_ep, hidg->req);
@@ -1097,6 +1108,10 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
}
}
+ spin_lock_irqsave(&hidg->read_spinlock, flags);
+ hidg->disabled = false;
+ spin_unlock_irqrestore(&hidg->read_spinlock, flags);
+
if (hidg->in_ep != NULL) {
spin_lock_irqsave(&hidg->write_spinlock, flags);
hidg->req = req_in;
Similar to how it is done in the write path. Add a disabled flag to track the function state and use it to exit the read loops to ensure no readers get stuck when the function is disabled/unbound, protecting against corruption when the waitq and spinlocks are reinitialized in hidg_bind(). Signed-off-by: Peter Korsgaard <peter@korsgaard.com> --- drivers/usb/gadget/function/f_hid.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)