USB: gadget: f_mtp: Add support for sending MTP header during file transfer
authorMike Lockwood <lockwood@android.com>
Thu, 14 Jul 2011 23:42:42 +0000 (19:42 -0400)
committerMike Lockwood <lockwood@android.com>
Fri, 15 Jul 2011 21:18:14 +0000 (17:18 -0400)
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>
drivers/usb/gadget/f_mtp.c
include/linux/usb/f_mtp.h

index a5b646394426974230b39958e55a7b4515e134df..a383bc570c61301351210baaeebf980d167b2260 100644 (file)
@@ -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);
@@ -829,6 +851,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;
@@ -866,10 +889,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
index fdf828c4925046815b2695427d539750961044a5..7422b17c6eb1cdc0a21d9857984b79dc9b41c315 100644 (file)
 #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 */