From 32c1661893c0007af70f5b2ce1b890a7b1449f20 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 15 Nov 2010 15:22:21 -0500 Subject: [PATCH 1/4] USB: gadget: f_mtp: Return zero result for successful file transfer. Returning number of bytes transfered does not work for files > 2 gig since ioctl() result is a signed integer. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_mtp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 81075f2bf080..81cc01868397 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -621,15 +621,16 @@ static void send_file_work(struct work_struct *data) { struct file *filp; loff_t offset; size_t count; - int r, xfer, ret; + int xfer, ret; + int r = 0; /* read our parameters */ smp_rmb(); filp = dev->xfer_file; offset = dev->xfer_file_offset; - r = count = dev->xfer_file_length; + count = dev->xfer_file_length; - DBG(cdev, "send_file_work(%lld %d)\n", offset, count); + DBG(cdev, "send_file_work(%lld %u)\n", offset, count); while (count > 0) { /* get an idle tx request to use */ @@ -686,15 +687,16 @@ static void receive_file_work(struct work_struct *data) struct file *filp; loff_t offset; size_t count; - int r, ret, cur_buf = 0; + int ret, cur_buf = 0; + int r = 0; /* read our parameters */ smp_rmb(); filp = dev->xfer_file; offset = dev->xfer_file_offset; - r = count = dev->xfer_file_length; + count = dev->xfer_file_length; - DBG(cdev, "receive_file_work(%d)\n", count); + DBG(cdev, "receive_file_work(%u)\n", count); while (count > 0 || write_req) { if (count > 0) { From 3482be62e48ddd2182873b25989270eb9088a65f Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 16 Nov 2010 17:14:32 -0500 Subject: [PATCH 2/4] USB: gadget: f_mtp: Support for file transfer length greater than 4 gigabytes For backward compatibility with PTP, MTP is limited to a 32-bit file size. When transferring files greater than 4 gig, MTP uses 0xFFFFFFFF as the file size and the receiver reads until it receives a short packet. Expanded size of mtp_file_range.length to 64 bits and added support for writing zero length packets. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_mtp.c | 46 +++++++++++++++++++++++++++++--------- include/linux/usb/f_mtp.h | 2 +- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 81cc01868397..03ea4a7066ca 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -107,7 +107,7 @@ struct mtp_dev { struct work_struct receive_file_work; struct file *xfer_file; loff_t xfer_file_offset; - size_t xfer_file_length; + int64_t xfer_file_length; int xfer_result; }; @@ -558,7 +558,8 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf, dev->state = STATE_BUSY; spin_unlock_irq(&dev->lock); - while (count > 0) { + /* condition check at bottom to allow zero length packet write */ + do { if (dev->state != STATE_BUSY) { DBG(cdev, "mtp_write dev->error\n"); r = -EIO; @@ -579,7 +580,7 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf, xfer = BULK_BUFFER_SIZE; else xfer = count; - if (copy_from_user(req->buf, buf, xfer)) { + if (xfer && copy_from_user(req->buf, buf, xfer)) { r = -EFAULT; break; } @@ -597,7 +598,7 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf, /* zero this so we don't try to free it on error exit */ req = 0; - } + } while (count > 0); if (req) req_put(dev, &dev->tx_idle, req); @@ -620,9 +621,10 @@ static void send_file_work(struct work_struct *data) { struct usb_request *req = 0; struct file *filp; loff_t offset; - size_t count; + int64_t count; int xfer, ret; int r = 0; + int sendZLP = 0; /* read our parameters */ smp_rmb(); @@ -630,9 +632,22 @@ static void send_file_work(struct work_struct *data) { offset = dev->xfer_file_offset; count = dev->xfer_file_length; - DBG(cdev, "send_file_work(%lld %u)\n", offset, count); + DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); + + /* we need to send a zero length packet to signal the end of transfer + * if the length is > 4 gig and the last packet is aligned to a + * packet boundary. + */ + if (dev->xfer_file_length >= 0xFFFFFFFF + && (dev->xfer_file_length & (dev->ep_in->maxpacket - 1)) == 0) { + sendZLP = 1; + } + + while (count > 0 || sendZLP) { + /* so we exit after sending ZLP */ + if (count == 0) + sendZLP = 0; - while (count > 0) { /* get an idle tx request to use */ req = 0; ret = wait_event_interruptible(dev->write_wq, @@ -686,7 +701,7 @@ static void receive_file_work(struct work_struct *data) struct usb_request *read_req = NULL, *write_req = NULL; struct file *filp; loff_t offset; - size_t count; + int64_t count; int ret, cur_buf = 0; int r = 0; @@ -696,7 +711,7 @@ static void receive_file_work(struct work_struct *data) offset = dev->xfer_file_offset; count = dev->xfer_file_length; - DBG(cdev, "receive_file_work(%u)\n", count); + DBG(cdev, "receive_file_work(%lld)\n", count); while (count > 0 || write_req) { if (count > 0) { @@ -713,7 +728,6 @@ static void receive_file_work(struct work_struct *data) dev->state = STATE_ERROR; break; } - count -= ret; } if (write_req) { @@ -737,7 +751,17 @@ static void receive_file_work(struct work_struct *data) r = ret; break; } - count -= read_req->actual; + /* if xfer_file_length is 0xFFFFFFFF, then we read until + * we get a zero length packet + */ + if (count != 0xFFFFFFFF) + count -= read_req->actual; + if (read_req->actual < read_req->length) { + /* short packet is used to signal EOF for sizes > 4 gig */ + DBG(cdev, "got short packet\n"); + count = 0; + } + write_req = read_req; read_req = NULL; } diff --git a/include/linux/usb/f_mtp.h b/include/linux/usb/f_mtp.h index 426c6b5cc06a..e4fd88066389 100644 --- a/include/linux/usb/f_mtp.h +++ b/include/linux/usb/f_mtp.h @@ -29,7 +29,7 @@ struct mtp_file_range { /* offset in file for start of transfer */ loff_t offset; /* number of bytes to transfer */ - size_t length; + int64_t length; }; struct mtp_event { From e069c6bf883aa3c7eda368dc923fe26c69150cc6 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 16 Nov 2010 15:39:43 -0800 Subject: [PATCH 3/4] misc: kernel_debugger: Add help command Signed-off-by: Dmitry Shmidt --- drivers/misc/kernel_debugger.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/misc/kernel_debugger.c b/drivers/misc/kernel_debugger.c index 20e1abbfbec7..4a9fef6244ed 100644 --- a/drivers/misc/kernel_debugger.c +++ b/drivers/misc/kernel_debugger.c @@ -65,6 +65,14 @@ static void do_sysrq(struct kdbg_ctxt *ctxt, char rq) } } +static void do_help(struct kdbg_ctxt *ctxt) +{ + dprintf("Kernel Debugger commands:\n"); + dprintf(" ps Process list\n"); + dprintf(" sysrq sysrq options\n"); + dprintf(" sysrq Execute sysrq with \n"); +} + int kernel_debugger(struct kdbg_ctxt *ctxt, char *cmd) { if (!strcmp(cmd, "ps")) @@ -73,6 +81,8 @@ int kernel_debugger(struct kdbg_ctxt *ctxt, char *cmd) do_sysrq(ctxt, 'h'); if (!strncmp(cmd, "sysrq ", 6)) do_sysrq(ctxt, cmd[6]); + if (!strcmp(cmd, "help")) + do_help(ctxt); return 0; } From f0dc37c24c579c343e4f64386306d57972cf0e0b Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 17 Nov 2010 11:16:35 -0500 Subject: [PATCH 4/4] USB: gadget: f_mtp: Zero length packet fixes Send zero length packets at end of transfers that are a multiple of the bulk max packet size as required by MTP. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_mtp.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 03ea4a7066ca..64fe3b3cd9e6 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -540,6 +540,7 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf, struct usb_composite_dev *cdev = dev->cdev; struct usb_request *req = 0; int r = count, xfer; + int sendZLP = 0; int ret; DBG(cdev, "mtp_write(%d)\n", count); @@ -558,8 +559,18 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf, dev->state = STATE_BUSY; spin_unlock_irq(&dev->lock); - /* condition check at bottom to allow zero length packet write */ - do { + /* 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 ((count & (dev->ep_in->maxpacket - 1)) == 0) { + sendZLP = 1; + } + + while (count > 0 || sendZLP) { + /* so we exit after sending ZLP */ + if (count == 0) + sendZLP = 0; + if (dev->state != STATE_BUSY) { DBG(cdev, "mtp_write dev->error\n"); r = -EIO; @@ -598,7 +609,7 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf, /* zero this so we don't try to free it on error exit */ req = 0; - } while (count > 0); + } if (req) req_put(dev, &dev->tx_idle, req); @@ -635,11 +646,9 @@ static void send_file_work(struct work_struct *data) { DBG(cdev, "send_file_work(%lld %lld)\n", offset, count); /* we need to send a zero length packet to signal the end of transfer - * if the length is > 4 gig and the last packet is aligned to a - * packet boundary. + * if the transfer size is aligned to a packet boundary. */ - if (dev->xfer_file_length >= 0xFFFFFFFF - && (dev->xfer_file_length & (dev->ep_in->maxpacket - 1)) == 0) { + if ((dev->xfer_file_length & (dev->ep_in->maxpacket - 1)) == 0) { sendZLP = 1; }