Loading drivers/usb/gadget/function/f_accessory.c +3 −0 Original line number Diff line number Diff line Loading @@ -780,6 +780,9 @@ static const struct file_operations acc_fops = { .read = acc_read, .write = acc_write, .unlocked_ioctl = acc_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = acc_ioctl, #endif .open = acc_open, .release = acc_release, }; Loading drivers/usb/gadget/function/f_mtp.c +242 −91 Original line number Diff line number Diff line Loading @@ -56,7 +56,7 @@ #define STATE_ERROR 4 /* error from completion routine */ /* number of tx and rx requests to allocate */ #define TX_REQ_MAX 4 #define MTP_TX_REQ_MAX 8 #define RX_REQ_MAX 2 #define INTR_REQ_MAX 5 Loading @@ -74,6 +74,15 @@ #define MTP_RESPONSE_DEVICE_BUSY 0x2019 #define DRIVER_NAME "mtp" unsigned int mtp_rx_req_len = MTP_BULK_BUFFER_SIZE; module_param(mtp_rx_req_len, uint, 0644); unsigned int mtp_tx_req_len = MTP_BULK_BUFFER_SIZE; module_param(mtp_tx_req_len, uint, 0644); unsigned int mtp_tx_reqs = MTP_TX_REQ_MAX; module_param(mtp_tx_reqs, uint, 0644); static const char mtp_shortname[] = DRIVER_NAME "_usb"; struct mtp_dev { Loading Loading @@ -504,18 +513,46 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, ep->driver_data = dev; /* claim the endpoint */ dev->ep_intr = ep; retry_tx_alloc: if (mtp_tx_req_len > MTP_BULK_BUFFER_SIZE) mtp_tx_reqs = 4; /* now allocate requests for our endpoints */ for (i = 0; i < TX_REQ_MAX; i++) { req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE); if (!req) for (i = 0; i < mtp_tx_reqs; i++) { req = mtp_request_new(dev->ep_in, mtp_tx_req_len); if (!req) { if (mtp_tx_req_len <= MTP_BULK_BUFFER_SIZE) goto fail; while ((req = mtp_req_get(dev, &dev->tx_idle))) mtp_request_free(req, dev->ep_in); mtp_tx_req_len = MTP_BULK_BUFFER_SIZE; mtp_tx_reqs = MTP_TX_REQ_MAX; goto retry_tx_alloc; } req->complete = mtp_complete_in; mtp_req_put(dev, &dev->tx_idle, req); } /* * The RX buffer should be aligned to EP max packet for * some controllers. At bind time, we don't know the * operational speed. Hence assuming super speed max * packet size. */ if (mtp_rx_req_len % 1024) mtp_rx_req_len = MTP_BULK_BUFFER_SIZE; retry_rx_alloc: for (i = 0; i < RX_REQ_MAX; i++) { req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE); if (!req) req = mtp_request_new(dev->ep_out, mtp_rx_req_len); if (!req) { if (mtp_rx_req_len <= MTP_BULK_BUFFER_SIZE) goto fail; for (--i; i >= 0; i--) mtp_request_free(dev->rx_req[i], dev->ep_out); mtp_rx_req_len = MTP_BULK_BUFFER_SIZE; goto retry_rx_alloc; } req->complete = mtp_complete_out; dev->rx_req[i] = req; } Loading @@ -540,13 +577,15 @@ static ssize_t mtp_read(struct file *fp, char __user *buf, struct mtp_dev *dev = fp->private_data; struct usb_composite_dev *cdev = dev->cdev; struct usb_request *req; ssize_t r = count; unsigned xfer; ssize_t r = count, xfer, len; int ret = 0; size_t len = 0; DBG(cdev, "mtp_read(%zu)\n", count); len = ALIGN(count, dev->ep_out->maxpacket); if (len > mtp_rx_req_len) return -EINVAL; /* we will block until we're online */ DBG(cdev, "mtp_read: waiting for online state\n"); ret = wait_event_interruptible(dev->read_wq, Loading Loading @@ -597,7 +636,17 @@ static ssize_t mtp_read(struct file *fp, char __user *buf, } /* wait for a request to complete */ ret = wait_event_interruptible(dev->read_wq, dev->rx_done); ret = wait_event_interruptible(dev->read_wq, dev->rx_done || dev->state != STATE_BUSY); if (dev->state == STATE_CANCELED) { r = -ECANCELED; if (!dev->rx_done) usb_ep_dequeue(dev->ep_out, req); spin_lock_irq(&dev->lock); dev->state = STATE_CANCELED; spin_unlock_irq(&dev->lock); goto done; } if (ret < 0) { r = ret; usb_ep_dequeue(dev->ep_out, req); Loading Loading @@ -808,6 +857,7 @@ static void send_file_work(struct work_struct *data) ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); if (ret < 0) { DBG(cdev, "send_file_work: xfer error %d\n", ret); if (dev->state != STATE_OFFLINE) dev->state = STATE_ERROR; r = -EIO; break; Loading Loading @@ -848,6 +898,9 @@ static void receive_file_work(struct work_struct *data) count = dev->xfer_file_length; DBG(cdev, "receive_file_work(%lld)\n", count); if (!IS_ALIGNED(count, dev->ep_out->maxpacket)) DBG(cdev, "%s- count(%lld) not multiple of mtu(%d)\n", __func__, count, dev->ep_out->maxpacket); while (count > 0 || write_req) { if (count > 0) { Loading @@ -855,12 +908,14 @@ static void receive_file_work(struct work_struct *data) read_req = dev->rx_req[cur_buf]; cur_buf = (cur_buf + 1) % RX_REQ_MAX; read_req->length = (count > MTP_BULK_BUFFER_SIZE ? MTP_BULK_BUFFER_SIZE : count); /* some h/w expects size to be aligned to ep's MTU */ read_req->length = mtp_rx_req_len; dev->rx_done = 0; ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); if (ret < 0) { r = -EIO; if (dev->state != STATE_OFFLINE) dev->state = STATE_ERROR; break; } Loading @@ -873,6 +928,7 @@ static void receive_file_work(struct work_struct *data) DBG(cdev, "vfs_write %d\n", ret); if (ret != write_req->actual) { r = -EIO; if (dev->state != STATE_OFFLINE) dev->state = STATE_ERROR; break; } Loading @@ -883,7 +939,11 @@ static void receive_file_work(struct work_struct *data) /* wait for our last read to complete */ ret = wait_event_interruptible(dev->read_wq, dev->rx_done || dev->state != STATE_BUSY); if (dev->state == STATE_CANCELED) { if (dev->state == STATE_CANCELED || dev->state == STATE_OFFLINE) { if (dev->state == STATE_OFFLINE) r = -EIO; else r = -ECANCELED; if (!dev->rx_done) usb_ep_dequeue(dev->ep_out, read_req); Loading @@ -893,6 +953,11 @@ static void receive_file_work(struct work_struct *data) r = read_req->status; break; } /* Check if we aligned the size due to MTU constraint */ if (count < read_req->length) read_req->actual = (read_req->actual > count ? count : read_req->actual); /* if xfer_file_length is 0xFFFFFFFF, then we read until * we get a zero length packet */ Loading Loading @@ -949,26 +1014,20 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) return ret; } static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) static long mtp_send_receive_ioctl(struct file *fp, unsigned int code, struct mtp_file_range *mfr) { struct mtp_dev *dev = fp->private_data; struct file *filp = NULL; struct work_struct *work; int ret = -EINVAL; if (mtp_lock(&dev->ioctl_excl)) return -EBUSY; switch (code) { case MTP_SEND_FILE: case MTP_RECEIVE_FILE: case MTP_SEND_FILE_WITH_HEADER: { struct mtp_file_range mfr; struct work_struct *work; spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) { /* report cancelation to userspace */ /* report cancellation to userspace */ dev->state = STATE_READY; spin_unlock_irq(&dev->lock); ret = -ECANCELED; Loading @@ -982,12 +1041,8 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) dev->state = STATE_BUSY; spin_unlock_irq(&dev->lock); if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { ret = -EFAULT; goto fail; } /* hold a reference to the file while we are working with it */ filp = fget(mfr.fd); filp = fget(mfr->fd); if (!filp) { ret = -EBADF; goto fail; Loading @@ -995,15 +1050,16 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) /* write the parameters */ dev->xfer_file = filp; dev->xfer_file_offset = mfr.offset; dev->xfer_file_length = mfr.length; dev->xfer_file_offset = mfr->offset; dev->xfer_file_length = mfr->length; /* make sure write is done before parameters are read */ smp_wmb(); if (code == MTP_SEND_FILE_WITH_HEADER) { work = &dev->send_file_work; dev->xfer_send_header = 1; dev->xfer_command = mfr.command; dev->xfer_transaction_id = mfr.transaction_id; dev->xfer_command = mfr->command; dev->xfer_transaction_id = mfr->transaction_id; } else if (code == MTP_SEND_FILE) { work = &dev->send_file_work; dev->xfer_send_header = 0; Loading @@ -1023,11 +1079,40 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) /* read the result */ smp_rmb(); ret = dev->xfer_result; break; fail: spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) ret = -ECANCELED; else if (dev->state != STATE_OFFLINE) dev->state = STATE_READY; spin_unlock_irq(&dev->lock); out: mtp_unlock(&dev->ioctl_excl); DBG(dev->cdev, "ioctl returning %d\n", ret); return ret; } case MTP_SEND_EVENT: static long mtp_ioctl(struct file *fp, unsigned int code, unsigned long value) { struct mtp_dev *dev = fp->private_data; struct mtp_file_range mfr; struct mtp_event event; int ret = -EINVAL; switch (code) { case MTP_SEND_FILE: case MTP_RECEIVE_FILE: case MTP_SEND_FILE_WITH_HEADER: if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { ret = -EFAULT; goto fail; } ret = mtp_send_receive_ioctl(fp, code, &mfr); break; case MTP_SEND_EVENT: if (mtp_lock(&dev->ioctl_excl)) return -EBUSY; /* return here so we don't change dev->state below, * which would interfere with bulk transfer state. */ Loading @@ -1035,22 +1120,85 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) ret = -EFAULT; else ret = mtp_send_event(dev, &event); goto out; mtp_unlock(&dev->ioctl_excl); break; default: DBG(dev->cdev, "unknown ioctl code: %d\n", code); } fail: return ret; } fail: spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) ret = -ECANCELED; else if (dev->state != STATE_OFFLINE) dev->state = STATE_READY; spin_unlock_irq(&dev->lock); out: /* * 32 bit userspace calling into 64 bit kernel. handle ioctl code * and userspace pointer */ #ifdef CONFIG_COMPAT static long compat_mtp_ioctl(struct file *fp, unsigned int code, unsigned long value) { struct mtp_dev *dev = fp->private_data; struct mtp_file_range mfr; struct __compat_mtp_file_range cmfr; struct mtp_event event; struct __compat_mtp_event cevent; unsigned int cmd; bool send_file = false; int ret = -EINVAL; switch (code) { case COMPAT_MTP_SEND_FILE: cmd = MTP_SEND_FILE; send_file = true; break; case COMPAT_MTP_RECEIVE_FILE: cmd = MTP_RECEIVE_FILE; send_file = true; break; case COMPAT_MTP_SEND_FILE_WITH_HEADER: cmd = MTP_SEND_FILE_WITH_HEADER; send_file = true; break; case COMPAT_MTP_SEND_EVENT: cmd = MTP_SEND_EVENT; break; default: DBG(dev->cdev, "unknown compat_ioctl code: %d\n", code); ret = -ENOIOCTLCMD; goto fail; } if (send_file) { if (copy_from_user(&cmfr, (void __user *)value, sizeof(cmfr))) { ret = -EFAULT; goto fail; } mfr.fd = cmfr.fd; mfr.offset = cmfr.offset; mfr.length = cmfr.length; mfr.command = cmfr.command; mfr.transaction_id = cmfr.transaction_id; ret = mtp_send_receive_ioctl(fp, cmd, &mfr); } else { if (mtp_lock(&dev->ioctl_excl)) return -EBUSY; /* return here so we don't change dev->state below, * which would interfere with bulk transfer state. */ if (copy_from_user(&cevent, (void __user *)value, sizeof(cevent))) { ret = -EFAULT; goto fail; } event.length = cevent.length; event.data = compat_ptr(cevent.data); ret = mtp_send_event(dev, &event); mtp_unlock(&dev->ioctl_excl); DBG(dev->cdev, "ioctl returning %d\n", ret); } fail: return ret; } #endif static int mtp_open(struct inode *ip, struct file *fp) { Loading Loading @@ -1080,6 +1228,9 @@ static const struct file_operations mtp_fops = { .read = mtp_read, .write = mtp_write, .unlocked_ioctl = mtp_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_mtp_ioctl, #endif .open = mtp_open, .release = mtp_release, }; Loading include/linux/usb/f_mtp.h +30 −0 Original line number Diff line number Diff line Loading @@ -19,5 +19,35 @@ #define __LINUX_USB_F_MTP_H #include <uapi/linux/usb/f_mtp.h> #include <linux/ioctl.h> #ifdef CONFIG_COMPAT #include <linux/compat.h> #endif #ifdef __KERNEL__ #ifdef CONFIG_COMPAT struct __compat_mtp_file_range { compat_int_t fd; compat_loff_t offset; int64_t length; uint16_t command; uint32_t transaction_id; }; struct __compat_mtp_event { compat_size_t length; compat_caddr_t data; }; #define COMPAT_MTP_SEND_FILE _IOW('M', 0, \ struct __compat_mtp_file_range) #define COMPAT_MTP_RECEIVE_FILE _IOW('M', 1, \ struct __compat_mtp_file_range) #define COMPAT_MTP_SEND_EVENT _IOW('M', 3, \ struct __compat_mtp_event) #define COMPAT_MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, \ struct __compat_mtp_file_range) #endif #endif #endif /* __LINUX_USB_F_MTP_H */ Loading
drivers/usb/gadget/function/f_accessory.c +3 −0 Original line number Diff line number Diff line Loading @@ -780,6 +780,9 @@ static const struct file_operations acc_fops = { .read = acc_read, .write = acc_write, .unlocked_ioctl = acc_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = acc_ioctl, #endif .open = acc_open, .release = acc_release, }; Loading
drivers/usb/gadget/function/f_mtp.c +242 −91 Original line number Diff line number Diff line Loading @@ -56,7 +56,7 @@ #define STATE_ERROR 4 /* error from completion routine */ /* number of tx and rx requests to allocate */ #define TX_REQ_MAX 4 #define MTP_TX_REQ_MAX 8 #define RX_REQ_MAX 2 #define INTR_REQ_MAX 5 Loading @@ -74,6 +74,15 @@ #define MTP_RESPONSE_DEVICE_BUSY 0x2019 #define DRIVER_NAME "mtp" unsigned int mtp_rx_req_len = MTP_BULK_BUFFER_SIZE; module_param(mtp_rx_req_len, uint, 0644); unsigned int mtp_tx_req_len = MTP_BULK_BUFFER_SIZE; module_param(mtp_tx_req_len, uint, 0644); unsigned int mtp_tx_reqs = MTP_TX_REQ_MAX; module_param(mtp_tx_reqs, uint, 0644); static const char mtp_shortname[] = DRIVER_NAME "_usb"; struct mtp_dev { Loading Loading @@ -504,18 +513,46 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, ep->driver_data = dev; /* claim the endpoint */ dev->ep_intr = ep; retry_tx_alloc: if (mtp_tx_req_len > MTP_BULK_BUFFER_SIZE) mtp_tx_reqs = 4; /* now allocate requests for our endpoints */ for (i = 0; i < TX_REQ_MAX; i++) { req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE); if (!req) for (i = 0; i < mtp_tx_reqs; i++) { req = mtp_request_new(dev->ep_in, mtp_tx_req_len); if (!req) { if (mtp_tx_req_len <= MTP_BULK_BUFFER_SIZE) goto fail; while ((req = mtp_req_get(dev, &dev->tx_idle))) mtp_request_free(req, dev->ep_in); mtp_tx_req_len = MTP_BULK_BUFFER_SIZE; mtp_tx_reqs = MTP_TX_REQ_MAX; goto retry_tx_alloc; } req->complete = mtp_complete_in; mtp_req_put(dev, &dev->tx_idle, req); } /* * The RX buffer should be aligned to EP max packet for * some controllers. At bind time, we don't know the * operational speed. Hence assuming super speed max * packet size. */ if (mtp_rx_req_len % 1024) mtp_rx_req_len = MTP_BULK_BUFFER_SIZE; retry_rx_alloc: for (i = 0; i < RX_REQ_MAX; i++) { req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE); if (!req) req = mtp_request_new(dev->ep_out, mtp_rx_req_len); if (!req) { if (mtp_rx_req_len <= MTP_BULK_BUFFER_SIZE) goto fail; for (--i; i >= 0; i--) mtp_request_free(dev->rx_req[i], dev->ep_out); mtp_rx_req_len = MTP_BULK_BUFFER_SIZE; goto retry_rx_alloc; } req->complete = mtp_complete_out; dev->rx_req[i] = req; } Loading @@ -540,13 +577,15 @@ static ssize_t mtp_read(struct file *fp, char __user *buf, struct mtp_dev *dev = fp->private_data; struct usb_composite_dev *cdev = dev->cdev; struct usb_request *req; ssize_t r = count; unsigned xfer; ssize_t r = count, xfer, len; int ret = 0; size_t len = 0; DBG(cdev, "mtp_read(%zu)\n", count); len = ALIGN(count, dev->ep_out->maxpacket); if (len > mtp_rx_req_len) return -EINVAL; /* we will block until we're online */ DBG(cdev, "mtp_read: waiting for online state\n"); ret = wait_event_interruptible(dev->read_wq, Loading Loading @@ -597,7 +636,17 @@ static ssize_t mtp_read(struct file *fp, char __user *buf, } /* wait for a request to complete */ ret = wait_event_interruptible(dev->read_wq, dev->rx_done); ret = wait_event_interruptible(dev->read_wq, dev->rx_done || dev->state != STATE_BUSY); if (dev->state == STATE_CANCELED) { r = -ECANCELED; if (!dev->rx_done) usb_ep_dequeue(dev->ep_out, req); spin_lock_irq(&dev->lock); dev->state = STATE_CANCELED; spin_unlock_irq(&dev->lock); goto done; } if (ret < 0) { r = ret; usb_ep_dequeue(dev->ep_out, req); Loading Loading @@ -808,6 +857,7 @@ static void send_file_work(struct work_struct *data) ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); if (ret < 0) { DBG(cdev, "send_file_work: xfer error %d\n", ret); if (dev->state != STATE_OFFLINE) dev->state = STATE_ERROR; r = -EIO; break; Loading Loading @@ -848,6 +898,9 @@ static void receive_file_work(struct work_struct *data) count = dev->xfer_file_length; DBG(cdev, "receive_file_work(%lld)\n", count); if (!IS_ALIGNED(count, dev->ep_out->maxpacket)) DBG(cdev, "%s- count(%lld) not multiple of mtu(%d)\n", __func__, count, dev->ep_out->maxpacket); while (count > 0 || write_req) { if (count > 0) { Loading @@ -855,12 +908,14 @@ static void receive_file_work(struct work_struct *data) read_req = dev->rx_req[cur_buf]; cur_buf = (cur_buf + 1) % RX_REQ_MAX; read_req->length = (count > MTP_BULK_BUFFER_SIZE ? MTP_BULK_BUFFER_SIZE : count); /* some h/w expects size to be aligned to ep's MTU */ read_req->length = mtp_rx_req_len; dev->rx_done = 0; ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL); if (ret < 0) { r = -EIO; if (dev->state != STATE_OFFLINE) dev->state = STATE_ERROR; break; } Loading @@ -873,6 +928,7 @@ static void receive_file_work(struct work_struct *data) DBG(cdev, "vfs_write %d\n", ret); if (ret != write_req->actual) { r = -EIO; if (dev->state != STATE_OFFLINE) dev->state = STATE_ERROR; break; } Loading @@ -883,7 +939,11 @@ static void receive_file_work(struct work_struct *data) /* wait for our last read to complete */ ret = wait_event_interruptible(dev->read_wq, dev->rx_done || dev->state != STATE_BUSY); if (dev->state == STATE_CANCELED) { if (dev->state == STATE_CANCELED || dev->state == STATE_OFFLINE) { if (dev->state == STATE_OFFLINE) r = -EIO; else r = -ECANCELED; if (!dev->rx_done) usb_ep_dequeue(dev->ep_out, read_req); Loading @@ -893,6 +953,11 @@ static void receive_file_work(struct work_struct *data) r = read_req->status; break; } /* Check if we aligned the size due to MTU constraint */ if (count < read_req->length) read_req->actual = (read_req->actual > count ? count : read_req->actual); /* if xfer_file_length is 0xFFFFFFFF, then we read until * we get a zero length packet */ Loading Loading @@ -949,26 +1014,20 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) return ret; } static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) static long mtp_send_receive_ioctl(struct file *fp, unsigned int code, struct mtp_file_range *mfr) { struct mtp_dev *dev = fp->private_data; struct file *filp = NULL; struct work_struct *work; int ret = -EINVAL; if (mtp_lock(&dev->ioctl_excl)) return -EBUSY; switch (code) { case MTP_SEND_FILE: case MTP_RECEIVE_FILE: case MTP_SEND_FILE_WITH_HEADER: { struct mtp_file_range mfr; struct work_struct *work; spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) { /* report cancelation to userspace */ /* report cancellation to userspace */ dev->state = STATE_READY; spin_unlock_irq(&dev->lock); ret = -ECANCELED; Loading @@ -982,12 +1041,8 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) dev->state = STATE_BUSY; spin_unlock_irq(&dev->lock); if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { ret = -EFAULT; goto fail; } /* hold a reference to the file while we are working with it */ filp = fget(mfr.fd); filp = fget(mfr->fd); if (!filp) { ret = -EBADF; goto fail; Loading @@ -995,15 +1050,16 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) /* write the parameters */ dev->xfer_file = filp; dev->xfer_file_offset = mfr.offset; dev->xfer_file_length = mfr.length; dev->xfer_file_offset = mfr->offset; dev->xfer_file_length = mfr->length; /* make sure write is done before parameters are read */ smp_wmb(); if (code == MTP_SEND_FILE_WITH_HEADER) { work = &dev->send_file_work; dev->xfer_send_header = 1; dev->xfer_command = mfr.command; dev->xfer_transaction_id = mfr.transaction_id; dev->xfer_command = mfr->command; dev->xfer_transaction_id = mfr->transaction_id; } else if (code == MTP_SEND_FILE) { work = &dev->send_file_work; dev->xfer_send_header = 0; Loading @@ -1023,11 +1079,40 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) /* read the result */ smp_rmb(); ret = dev->xfer_result; break; fail: spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) ret = -ECANCELED; else if (dev->state != STATE_OFFLINE) dev->state = STATE_READY; spin_unlock_irq(&dev->lock); out: mtp_unlock(&dev->ioctl_excl); DBG(dev->cdev, "ioctl returning %d\n", ret); return ret; } case MTP_SEND_EVENT: static long mtp_ioctl(struct file *fp, unsigned int code, unsigned long value) { struct mtp_dev *dev = fp->private_data; struct mtp_file_range mfr; struct mtp_event event; int ret = -EINVAL; switch (code) { case MTP_SEND_FILE: case MTP_RECEIVE_FILE: case MTP_SEND_FILE_WITH_HEADER: if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) { ret = -EFAULT; goto fail; } ret = mtp_send_receive_ioctl(fp, code, &mfr); break; case MTP_SEND_EVENT: if (mtp_lock(&dev->ioctl_excl)) return -EBUSY; /* return here so we don't change dev->state below, * which would interfere with bulk transfer state. */ Loading @@ -1035,22 +1120,85 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) ret = -EFAULT; else ret = mtp_send_event(dev, &event); goto out; mtp_unlock(&dev->ioctl_excl); break; default: DBG(dev->cdev, "unknown ioctl code: %d\n", code); } fail: return ret; } fail: spin_lock_irq(&dev->lock); if (dev->state == STATE_CANCELED) ret = -ECANCELED; else if (dev->state != STATE_OFFLINE) dev->state = STATE_READY; spin_unlock_irq(&dev->lock); out: /* * 32 bit userspace calling into 64 bit kernel. handle ioctl code * and userspace pointer */ #ifdef CONFIG_COMPAT static long compat_mtp_ioctl(struct file *fp, unsigned int code, unsigned long value) { struct mtp_dev *dev = fp->private_data; struct mtp_file_range mfr; struct __compat_mtp_file_range cmfr; struct mtp_event event; struct __compat_mtp_event cevent; unsigned int cmd; bool send_file = false; int ret = -EINVAL; switch (code) { case COMPAT_MTP_SEND_FILE: cmd = MTP_SEND_FILE; send_file = true; break; case COMPAT_MTP_RECEIVE_FILE: cmd = MTP_RECEIVE_FILE; send_file = true; break; case COMPAT_MTP_SEND_FILE_WITH_HEADER: cmd = MTP_SEND_FILE_WITH_HEADER; send_file = true; break; case COMPAT_MTP_SEND_EVENT: cmd = MTP_SEND_EVENT; break; default: DBG(dev->cdev, "unknown compat_ioctl code: %d\n", code); ret = -ENOIOCTLCMD; goto fail; } if (send_file) { if (copy_from_user(&cmfr, (void __user *)value, sizeof(cmfr))) { ret = -EFAULT; goto fail; } mfr.fd = cmfr.fd; mfr.offset = cmfr.offset; mfr.length = cmfr.length; mfr.command = cmfr.command; mfr.transaction_id = cmfr.transaction_id; ret = mtp_send_receive_ioctl(fp, cmd, &mfr); } else { if (mtp_lock(&dev->ioctl_excl)) return -EBUSY; /* return here so we don't change dev->state below, * which would interfere with bulk transfer state. */ if (copy_from_user(&cevent, (void __user *)value, sizeof(cevent))) { ret = -EFAULT; goto fail; } event.length = cevent.length; event.data = compat_ptr(cevent.data); ret = mtp_send_event(dev, &event); mtp_unlock(&dev->ioctl_excl); DBG(dev->cdev, "ioctl returning %d\n", ret); } fail: return ret; } #endif static int mtp_open(struct inode *ip, struct file *fp) { Loading Loading @@ -1080,6 +1228,9 @@ static const struct file_operations mtp_fops = { .read = mtp_read, .write = mtp_write, .unlocked_ioctl = mtp_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = compat_mtp_ioctl, #endif .open = mtp_open, .release = mtp_release, }; Loading
include/linux/usb/f_mtp.h +30 −0 Original line number Diff line number Diff line Loading @@ -19,5 +19,35 @@ #define __LINUX_USB_F_MTP_H #include <uapi/linux/usb/f_mtp.h> #include <linux/ioctl.h> #ifdef CONFIG_COMPAT #include <linux/compat.h> #endif #ifdef __KERNEL__ #ifdef CONFIG_COMPAT struct __compat_mtp_file_range { compat_int_t fd; compat_loff_t offset; int64_t length; uint16_t command; uint32_t transaction_id; }; struct __compat_mtp_event { compat_size_t length; compat_caddr_t data; }; #define COMPAT_MTP_SEND_FILE _IOW('M', 0, \ struct __compat_mtp_file_range) #define COMPAT_MTP_RECEIVE_FILE _IOW('M', 1, \ struct __compat_mtp_file_range) #define COMPAT_MTP_SEND_EVENT _IOW('M', 3, \ struct __compat_mtp_event) #define COMPAT_MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, \ struct __compat_mtp_file_range) #endif #endif #endif /* __LINUX_USB_F_MTP_H */