mirror of https://github.com/OpenIPC/firmware.git
200 lines
4.8 KiB
Diff
200 lines
4.8 KiB
Diff
--- linux-4.9.37/drivers/usb/gadget/function/f_hid.c 2017-07-12 16:42:41.000000000 +0300
|
|
+++ linux-4.9.y/drivers/usb/gadget/function/f_hid.c 2021-06-07 13:01:34.000000000 +0300
|
|
@@ -284,6 +284,7 @@
|
|
size_t count, loff_t *offp)
|
|
{
|
|
struct f_hidg *hidg = file->private_data;
|
|
+ struct usb_request *req;
|
|
unsigned long flags;
|
|
ssize_t status = -ENOMEM;
|
|
|
|
@@ -293,7 +294,7 @@
|
|
spin_lock_irqsave(&hidg->write_spinlock, flags);
|
|
|
|
#define WRITE_COND (!hidg->write_pending)
|
|
-
|
|
+try_again:
|
|
/* write queue */
|
|
while (!WRITE_COND) {
|
|
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
|
|
@@ -308,6 +309,7 @@
|
|
}
|
|
|
|
hidg->write_pending = 1;
|
|
+ req = hidg->req;
|
|
count = min_t(unsigned, count, hidg->report_length);
|
|
|
|
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
|
|
@@ -320,24 +322,38 @@
|
|
goto release_write_pending;
|
|
}
|
|
|
|
- hidg->req->status = 0;
|
|
- hidg->req->zero = 0;
|
|
- hidg->req->length = count;
|
|
- hidg->req->complete = f_hidg_req_complete;
|
|
- hidg->req->context = hidg;
|
|
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
|
|
+
|
|
+ /* we our function has been disabled by host */
|
|
+ if (!hidg->req) {
|
|
+ free_ep_req(hidg->in_ep, hidg->req);
|
|
+ /*
|
|
+ * TODO
|
|
+ * Should we fail with error here?
|
|
+ */
|
|
+ goto try_again;
|
|
+ }
|
|
+
|
|
+ req->status = 0;
|
|
+ req->zero = 0;
|
|
+ req->length = count;
|
|
+ req->complete = f_hidg_req_complete;
|
|
+ req->context = hidg;
|
|
|
|
status = usb_ep_queue(hidg->in_ep, hidg->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;
|
|
+ goto release_write_pending_unlocked;
|
|
} else {
|
|
status = count;
|
|
}
|
|
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
|
|
|
|
return status;
|
|
release_write_pending:
|
|
spin_lock_irqsave(&hidg->write_spinlock, flags);
|
|
+release_write_pending_unlocked:
|
|
hidg->write_pending = 0;
|
|
spin_unlock_irqrestore(&hidg->write_spinlock, flags);
|
|
|
|
@@ -541,12 +557,23 @@
|
|
kfree(list);
|
|
}
|
|
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
|
+
|
|
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
|
|
+ if (!hidg->write_pending) {
|
|
+ free_ep_req(hidg->in_ep, hidg->req);
|
|
+ hidg->write_pending = 1;
|
|
+ }
|
|
+
|
|
+ hidg->req = NULL;
|
|
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
|
|
}
|
|
|
|
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|
{
|
|
struct usb_composite_dev *cdev = f->config->cdev;
|
|
struct f_hidg *hidg = func_to_hidg(f);
|
|
+ struct usb_request *req_in = NULL;
|
|
+ unsigned long flags;
|
|
int i, status = 0;
|
|
|
|
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
|
|
@@ -567,6 +594,12 @@
|
|
goto fail;
|
|
}
|
|
hidg->in_ep->driver_data = hidg;
|
|
+
|
|
+ req_in = hidg_alloc_ep_req(hidg->in_ep, hidg->report_length);
|
|
+ if (!req_in) {
|
|
+ status = -ENOMEM;
|
|
+ goto disable_ep_in;
|
|
+ }
|
|
}
|
|
|
|
|
|
@@ -578,12 +611,12 @@
|
|
hidg->out_ep);
|
|
if (status) {
|
|
ERROR(cdev, "config_ep_by_speed FAILED!\n");
|
|
- goto fail;
|
|
+ goto free_req_in;
|
|
}
|
|
status = usb_ep_enable(hidg->out_ep);
|
|
if (status < 0) {
|
|
- ERROR(cdev, "Enable IN endpoint FAILED!\n");
|
|
- goto fail;
|
|
+ ERROR(cdev, "Enable OUT endpoint FAILED!\n");
|
|
+ goto free_req_in;
|
|
}
|
|
hidg->out_ep->driver_data = hidg;
|
|
|
|
@@ -599,17 +632,37 @@
|
|
req->context = hidg;
|
|
status = usb_ep_queue(hidg->out_ep, req,
|
|
GFP_ATOMIC);
|
|
- if (status)
|
|
+ if (status) {
|
|
ERROR(cdev, "%s queue req --> %d\n",
|
|
hidg->out_ep->name, status);
|
|
+ free_ep_req(hidg->out_ep, req);
|
|
+ }
|
|
} else {
|
|
- usb_ep_disable(hidg->out_ep);
|
|
status = -ENOMEM;
|
|
- goto fail;
|
|
+ goto disable_out_ep;
|
|
}
|
|
}
|
|
}
|
|
|
|
+ if (hidg->in_ep != NULL) {
|
|
+ spin_lock_irqsave(&hidg->write_spinlock, flags);
|
|
+ hidg->req = req_in;
|
|
+ hidg->write_pending = 0;
|
|
+ spin_unlock_irqrestore(&hidg->write_spinlock, flags);
|
|
+
|
|
+ wake_up(&hidg->write_queue);
|
|
+ }
|
|
+ return 0;
|
|
+disable_out_ep:
|
|
+ usb_ep_disable(hidg->out_ep);
|
|
+free_req_in:
|
|
+ if (req_in)
|
|
+ free_ep_req(hidg->in_ep, req_in);
|
|
+
|
|
+disable_ep_in:
|
|
+ if (hidg->in_ep)
|
|
+ usb_ep_disable(hidg->in_ep);
|
|
+
|
|
fail:
|
|
return status;
|
|
}
|
|
@@ -658,12 +711,6 @@
|
|
goto fail;
|
|
hidg->out_ep = ep;
|
|
|
|
- /* preallocate request and buffer */
|
|
- status = -ENOMEM;
|
|
- hidg->req = alloc_ep_req(hidg->in_ep, hidg->report_length);
|
|
- if (!hidg->req)
|
|
- goto fail;
|
|
-
|
|
/* set descriptor dynamic values */
|
|
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
|
|
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
|
|
@@ -690,6 +737,8 @@
|
|
goto fail;
|
|
|
|
spin_lock_init(&hidg->write_spinlock);
|
|
+ hidg->write_pending = 1;
|
|
+ hidg->req = NULL;
|
|
spin_lock_init(&hidg->read_spinlock);
|
|
init_waitqueue_head(&hidg->write_queue);
|
|
init_waitqueue_head(&hidg->read_queue);
|
|
@@ -954,10 +1003,6 @@
|
|
device_destroy(hidg_class, MKDEV(major, hidg->minor));
|
|
cdev_del(&hidg->cdev);
|
|
|
|
- /* disable/free request and end point */
|
|
- usb_ep_disable(hidg->in_ep);
|
|
- free_ep_req(hidg->in_ep, hidg->req);
|
|
-
|
|
usb_free_all_descriptors(f);
|
|
}
|
|
|