|
@@ -277,6 +277,13 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
|
|
/* pick the first one */
|
|
|
list = list_first_entry(&hidg->completed_out_req,
|
|
|
struct f_hidg_req_list, list);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Remove this from list to protect it from beign free()
|
|
|
+ * while host disables our function
|
|
|
+ */
|
|
|
+ list_del(&list->list);
|
|
|
+
|
|
|
req = list->req;
|
|
|
count = min_t(unsigned int, count, req->actual - list->pos);
|
|
|
spin_unlock_irqrestore(&hidg->spinlock, flags);
|
|
@@ -292,15 +299,20 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
|
|
|
* call, taking into account its current read position.
|
|
|
*/
|
|
|
if (list->pos == req->actual) {
|
|
|
- spin_lock_irqsave(&hidg->spinlock, flags);
|
|
|
- list_del(&list->list);
|
|
|
kfree(list);
|
|
|
- spin_unlock_irqrestore(&hidg->spinlock, flags);
|
|
|
|
|
|
req->length = hidg->report_length;
|
|
|
ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL);
|
|
|
- if (ret < 0)
|
|
|
+ if (ret < 0) {
|
|
|
+ free_ep_req(hidg->out_ep, req);
|
|
|
return ret;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ spin_lock_irqsave(&hidg->spinlock, flags);
|
|
|
+ list_add(&list->list, &hidg->completed_out_req);
|
|
|
+ spin_unlock_irqrestore(&hidg->spinlock, flags);
|
|
|
+
|
|
|
+ wake_up(&hidg->read_queue);
|
|
|
}
|
|
|
|
|
|
return count;
|
|
@@ -560,14 +572,18 @@ static void hidg_disable(struct usb_function *f)
|
|
|
{
|
|
|
struct f_hidg *hidg = func_to_hidg(f);
|
|
|
struct f_hidg_req_list *list, *next;
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
usb_ep_disable(hidg->in_ep);
|
|
|
usb_ep_disable(hidg->out_ep);
|
|
|
|
|
|
+ spin_lock_irqsave(&hidg->spinlock, flags);
|
|
|
list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
|
|
|
+ free_ep_req(hidg->out_ep, list->req);
|
|
|
list_del(&list->list);
|
|
|
kfree(list);
|
|
|
}
|
|
|
+ spin_unlock_irqrestore(&hidg->spinlock, flags);
|
|
|
}
|
|
|
|
|
|
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|