summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/gadget/function/f_mtp.c35
1 files changed, 35 insertions, 0 deletions
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index 972ea68b16e4..de648b0ae2b8 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -137,6 +137,7 @@ struct mtp_dev {
unsigned dbg_read_index;
unsigned dbg_write_index;
bool is_ptp;
+ struct mutex read_mutex;
};
static struct usb_interface_descriptor mtp_interface_desc = {
@@ -640,11 +641,18 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
dev->state = STATE_BUSY;
spin_unlock_irq(&dev->lock);
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ goto done;
+ }
requeue_req:
/* queue a request */
req = dev->rx_req[0];
req->length = len;
dev->rx_done = 0;
+ mutex_unlock(&dev->read_mutex);
ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
if (ret < 0) {
r = -EIO;
@@ -670,6 +678,7 @@ requeue_req:
usb_ep_dequeue(dev->ep_out, req);
goto done;
}
+ mutex_lock(&dev->read_mutex);
if (dev->state == STATE_BUSY) {
/* If we got a 0-len packet, throw it back and try again. */
if (req->actual == 0)
@@ -683,6 +692,7 @@ requeue_req:
} else
r = -EIO;
+ mutex_unlock(&dev->read_mutex);
done:
spin_lock_irq(&dev->lock);
if (dev->state == STATE_CANCELED)
@@ -937,6 +947,12 @@ static void receive_file_work(struct work_struct *data)
while (count > 0 || write_req) {
if (count > 0) {
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ break;
+ }
/* queue a request */
read_req = dev->rx_req[cur_buf];
cur_buf = (cur_buf + 1) % RX_REQ_MAX;
@@ -945,6 +961,7 @@ static void receive_file_work(struct work_struct *data)
read_req->length = mtp_rx_req_len;
dev->rx_done = 0;
+ mutex_unlock(&dev->read_mutex);
ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
if (ret < 0) {
r = -EIO;
@@ -957,15 +974,23 @@ static void receive_file_work(struct work_struct *data)
if (write_req) {
DBG(cdev, "rx %pK %d\n", write_req, write_req->actual);
start_time = ktime_get();
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ break;
+ }
ret = vfs_write(filp, write_req->buf, write_req->actual,
&offset);
DBG(cdev, "vfs_write %d\n", ret);
if (ret != write_req->actual) {
r = -EIO;
+ mutex_unlock(&dev->read_mutex);
if (dev->state != STATE_OFFLINE)
dev->state = STATE_ERROR;
break;
}
+ mutex_unlock(&dev->read_mutex);
dev->perf[dev->dbg_write_index].vfs_wtime =
ktime_to_us(ktime_sub(ktime_get(), start_time));
dev->perf[dev->dbg_write_index].vfs_wbytes = ret;
@@ -989,6 +1014,12 @@ static void receive_file_work(struct work_struct *data)
break;
}
+ mutex_lock(&dev->read_mutex);
+ if (dev->state == STATE_OFFLINE) {
+ r = -EIO;
+ mutex_unlock(&dev->read_mutex);
+ break;
+ }
/* Check if we aligned the size due to MTU constraint */
if (count < read_req->length)
read_req->actual = (read_req->actual > count ?
@@ -1009,6 +1040,7 @@ static void receive_file_work(struct work_struct *data)
write_req = read_req;
read_req = NULL;
+ mutex_unlock(&dev->read_mutex);
}
}
@@ -1469,12 +1501,14 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
struct usb_request *req;
int i;
+ mutex_lock(&dev->read_mutex);
while ((req = mtp_req_get(dev, &dev->tx_idle)))
mtp_request_free(req, dev->ep_in);
for (i = 0; i < RX_REQ_MAX; i++)
mtp_request_free(dev->rx_req[i], dev->ep_out);
while ((req = mtp_req_get(dev, &dev->intr_idle)))
mtp_request_free(req, dev->ep_intr);
+ mutex_unlock(&dev->read_mutex);
dev->state = STATE_OFFLINE;
dev->is_ptp = false;
kfree(f->os_desc_table);
@@ -1877,6 +1911,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi,
dev->is_ptp = !mtp_config;
fi->f = &dev->function;
+ mutex_init(&dev->read_mutex);
return &dev->function;
}
EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp);