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;
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;
};
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;
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;
}
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);
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;
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
#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 */
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 {
#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 */