diff options
author | Mike Lockwood <lockwood@android.com> | 2011-07-14 19:42:42 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2011-12-15 18:14:37 -0600 |
commit | 24647c785cfbf34f746427a00d0ab425ba050924 (patch) | |
tree | 81d878b0d0f16eabad61e4df5af2e2f58b957181 | |
parent | 6b917fce26f9464907bca88c9f79701876310926 (diff) |
USB: gadget: f_mtp: Add support for sending MTP header during file transfer
MTP_SEND_FILE_WITH_HEADER ioctl allows sending a file with the 12 byte header
prepended at the beginning.
This is to allow MTP to use a single packet for the data phase instead of two.
Signed-off-by: Mike Lockwood <lockwood@android.com>
-rw-r--r-- | drivers/usb/gadget/f_mtp.c | 46 | ||||
-rw-r--r-- | include/linux/usb/f_mtp.h | 28 |
2 files changed, 66 insertions, 8 deletions
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 275cd07c8335..4199b1766e5b 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -94,8 +94,8 @@ struct mtp_dev { struct usb_request *rx_req[RX_REQ_MAX]; int rx_done; - /* for processing MTP_SEND_FILE and MTP_RECEIVE_FILE - * ioctls on a work queue + /* for processing MTP_SEND_FILE, MTP_RECEIVE_FILE and + * MTP_SEND_FILE_WITH_HEADER ioctls on a work queue */ struct workqueue_struct *wq; struct work_struct send_file_work; @@ -103,6 +103,9 @@ struct mtp_dev { struct file *xfer_file; loff_t xfer_file_offset; int64_t xfer_file_length; + unsigned xfer_send_header; + uint16_t xfer_command; + uint32_t xfer_transaction_id; int xfer_result; }; @@ -629,10 +632,11 @@ static void send_file_work(struct work_struct *data) { struct mtp_dev *dev = container_of(data, struct mtp_dev, send_file_work); struct usb_composite_dev *cdev = dev->cdev; struct usb_request *req = 0; + struct mtp_data_header *header; struct file *filp; loff_t offset; int64_t count; - int xfer, ret; + int xfer, ret, hdr_size; int r = 0; int sendZLP = 0; @@ -644,10 +648,17 @@ static void send_file_work(struct work_struct *data) { DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); + if (dev->xfer_send_header) { + hdr_size = sizeof(struct mtp_data_header); + count += hdr_size; + } else { + hdr_size = 0; + } + /* we need to send a zero length packet to signal the end of transfer * if the transfer size is aligned to a packet boundary. */ - if ((dev->xfer_file_length & (dev->ep_in->maxpacket - 1)) == 0) { + if ((count & (dev->ep_in->maxpacket - 1)) == 0) { sendZLP = 1; } @@ -674,12 +685,23 @@ static void send_file_work(struct work_struct *data) { xfer = MTP_BULK_BUFFER_SIZE; else xfer = count; - ret = vfs_read(filp, req->buf, xfer, &offset); + + if (hdr_size) { + /* prepend MTP data header */ + header = (struct mtp_data_header *)req->buf; + header->length = __cpu_to_le32(count); + header->type = __cpu_to_le16(2); /* data packet */ + header->command = __cpu_to_le16(dev->xfer_command); + header->transaction_id = __cpu_to_le32(dev->xfer_transaction_id); + } + + ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size, &offset); if (ret < 0) { r = ret; break; } - xfer = ret; + xfer = ret + hdr_size; + hdr_size = 0; req->length = xfer; ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); @@ -828,6 +850,7 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) 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; @@ -865,10 +888,17 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value) dev->xfer_file_length = mfr.length; smp_wmb(); - if (code == MTP_SEND_FILE) + if (code == MTP_SEND_FILE_WITH_HEADER) { work = &dev->send_file_work; - else + dev->xfer_send_header = 1; + 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; + } else { work = &dev->receive_file_work; + } /* We do the file transfer on a work queue so it will run * in kernel context, which is necessary for vfs_read and diff --git a/include/linux/usb/f_mtp.h b/include/linux/usb/f_mtp.h index fdf828c49250..7422b17c6eb1 100644 --- a/include/linux/usb/f_mtp.h +++ b/include/linux/usb/f_mtp.h @@ -18,6 +18,22 @@ #ifndef __LINUX_USB_F_MTP_H #define __LINUX_USB_F_MTP_H +#include <linux/ioctl.h> + +#ifdef __KERNEL__ + +struct mtp_data_header { + /* length of packet, including this header */ + uint32_t length; + /* container type (2 for data packet) */ + uint16_t type; + /* MTP command code */ + uint16_t command; + /* MTP transaction ID */ + uint32_t transaction_id; +}; + +#endif /* __KERNEL__ */ struct mtp_file_range { /* file descriptor for file to transfer */ @@ -26,6 +42,14 @@ struct mtp_file_range { loff_t offset; /* number of bytes to transfer */ int64_t length; + /* MTP command ID for data header, + * used only for MTP_SEND_FILE_WITH_HEADER + */ + uint16_t command; + /* MTP transaction ID for data header, + * used only for MTP_SEND_FILE_WITH_HEADER + */ + uint32_t transaction_id; }; struct mtp_event { @@ -43,5 +67,9 @@ struct mtp_event { #define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range) /* Sends an event to the host via the interrupt endpoint */ #define MTP_SEND_EVENT _IOW('M', 3, struct mtp_event) +/* Sends the specified file range to the host, + * with a 12 byte MTP data packet header at the beginning. + */ +#define MTP_SEND_FILE_WITH_HEADER _IOW('M', 4, struct mtp_file_range) #endif /* __LINUX_USB_F_MTP_H */ |