diff options
Diffstat (limited to 'drivers/usb/gadget/f_mtp_samsung.c')
-rw-r--r-- | drivers/usb/gadget/f_mtp_samsung.c | 181 |
1 files changed, 172 insertions, 9 deletions
diff --git a/drivers/usb/gadget/f_mtp_samsung.c b/drivers/usb/gadget/f_mtp_samsung.c index 7f0dec8..304eca3 100644 --- a/drivers/usb/gadget/f_mtp_samsung.c +++ b/drivers/usb/gadget/f_mtp_samsung.c @@ -93,12 +93,12 @@ #endif /*-------------------------------------------------------------------------*/ -#define MTPG_BULK_BUFFER_SIZE 4096 +#define MTPG_BULK_BUFFER_SIZE 32768 #define MTPG_INTR_BUFFER_SIZE 28 /* number of rx and tx requests to allocate */ -#define MTPG_RX_REQ_MAX 4 -#define MTPG_MTPG_TX_REQ_MAX 4 +#define MTPG_RX_REQ_MAX 8 +#define MTPG_MTPG_TX_REQ_MAX 8 #define MTPG_INTR_REQ_MAX 5 /* ID for Microsoft MTP OS String */ @@ -137,13 +137,24 @@ struct mtpg_dev { struct usb_ep *bulk_in; struct usb_ep *bulk_out; struct usb_ep *int_in; + struct usb_request *notify_req; + struct workqueue_struct *wq; + struct work_struct read_send_work; + struct file *read_send_file; + + int64_t read_send_length; + + uint16_t read_send_cmd; + uint32_t read_send_id; + int read_send_result; atomic_t read_excl; atomic_t write_excl; atomic_t ioctl_excl; atomic_t open_excl; atomic_t wintfd_excl; char cancel_io_buf[USB_PTPREQUEST_CANCELIO_SIZE+1]; + int cancel_io; }; /* Global mtpg_dev Structure @@ -437,13 +448,14 @@ static int mtp_send_signal(int value) info.si_int = value; rcu_read_lock(); - if (!current->nsproxy) { + if (!current->nsproxy) { printk(KERN_DEBUG "process has gone\n"); rcu_read_unlock(); return -ENODEV; } t = pid_task(find_vpid(mtp_pid), PIDTYPE_PID); + if (t == NULL) { printk(KERN_DEBUG "no such pid\n"); rcu_read_unlock(); @@ -753,6 +765,108 @@ static ssize_t interrupt_write(struct file *fd, return ret; } +static void read_send_work(struct work_struct *work) +{ + struct mtpg_dev *dev = container_of(work, struct mtpg_dev, + read_send_work); + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = 0; + struct usb_container_header *hdr; + struct file *file; + loff_t file_pos = 0; + int64_t count = 0; + int xfer = 0; + int ret = -1; + int hdr_length = 0; + int r = 0; + int ZLP_flag = 0; + + /* read our parameters */ + smp_rmb(); + file = dev->read_send_file; + count = dev->read_send_length; + hdr_length = sizeof(struct usb_container_header); + count += hdr_length; + + printk(KERN_DEBUG "[%s:%d] offset=[%lld]\t leth+hder=[%lld]\n", + __func__, __LINE__, file_pos, count); + + /* Zero Length Packet should be sent if the last trasfer + * size is equals to the max packet size. + */ + if ((count & (dev->bulk_in->maxpacket - 1)) == 0) + ZLP_flag = 1; + + while (count > 0 || ZLP_flag) { + /*Breaking the loop after sending Zero Length Packet*/ + if (count == 0) + ZLP_flag = 0; + + if (dev->cancel_io == 1) { + dev->cancel_io = 0; /*reported to user space*/ + r = -EIO; + printk(KERN_DEBUG "[%s]\t%d ret = %d\n", + __func__, __LINE__, r); + break; + } + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = mtpg_req_get(dev, &dev->tx_idle)) + || dev->error)); + if (ret < 0) { + r = ret; + printk(KERN_DEBUG "[%s]\t%d ret = %d\n", + __func__, __LINE__, r); + break; + } + + if (count > MTPG_BULK_BUFFER_SIZE) + xfer = MTPG_BULK_BUFFER_SIZE; + else + xfer = count; + + if (hdr_length) { + hdr = (struct usb_container_header *)req->buf; + hdr->Length = __cpu_to_le32(count); + hdr->Type = __cpu_to_le16(2); + hdr->Code = __cpu_to_le16(dev->read_send_cmd); + hdr->TransactionID = __cpu_to_le32(dev->read_send_id); + } + + ret = vfs_read(file, req->buf + hdr_length, + xfer - hdr_length, &file_pos); + if (ret < 0) { + r = ret; + break; + } + xfer = ret + hdr_length; + hdr_length = 0; + + req->length = xfer; + ret = usb_ep_queue(dev->bulk_in, req, GFP_KERNEL); + if (ret < 0) { + dev->error = 1; + r = -EIO; + printk(KERN_DEBUG "[%s]\t%d ret = %d\n", + __func__, __LINE__, r); + break; + } + + count -= xfer; + + req = 0; + } + + if (req) + mtpg_req_put(dev, &dev->tx_idle, req); + + DEBUG_MTPB("[%s] \tline = [%d] \t r = [%d]\n", __func__, __LINE__, r); + + dev->read_send_result = r; + smp_wmb(); +} + static long mtpg_ioctl(struct file *fd, unsigned int code, unsigned long arg) { struct mtpg_dev *dev = fd->private_data; @@ -781,7 +895,7 @@ static long mtpg_ioctl(struct file *fd, unsigned int code, unsigned long arg) switch (code) { case MTP_ONLY_ENABLE: printk(KERN_DEBUG "[%s:%d] MTP_ONLY_ENABLE ioctl:\n", - __func__, __LINE__); + __func__, __LINE__); if (dev->cdev && dev->cdev->gadget) { usb_gadget_disconnect(cdev->gadget); printk(KERN_DEBUG "[%s:%d] B4 disconectng gadget\n", @@ -789,7 +903,7 @@ static long mtpg_ioctl(struct file *fd, unsigned int code, unsigned long arg) msleep(20); usb_gadget_connect(cdev->gadget); printk(KERN_DEBUG "[%s:%d] after usb_gadget_connect\n", - __func__, __LINE__); + __func__, __LINE__); } status = 10; printk(KERN_DEBUG "[%s:%d] MTP_ONLY_ENABLE clearing error 0\n", @@ -910,9 +1024,48 @@ static long mtpg_ioctl(struct file *fd, unsigned int code, unsigned long arg) else status = 512; break; + case SEND_FILE_WITH_HEADER: + { + struct read_send_info info; + struct work_struct *work; + struct file *file = NULL; + printk(KERN_DEBUG "[%s]SEND_FILE_WITH_HEADER line=[%d]\n", + __func__, __LINE__); + + if (copy_from_user(&info, (void __user *)arg, sizeof(info))) { + status = -EFAULT; + goto exit; + } + + file = fget(info.Fd); + if (!file) { + status = -EBADF; + printk(KERN_DEBUG "[%s] line=[%d] bad file number\n", + __func__, __LINE__); + goto exit; + } + + dev->read_send_file = file; + dev->read_send_length = info.Length; + smp_wmb(); + + work = &dev->read_send_work; + dev->read_send_cmd = info.Code; + dev->read_send_id = info.TransactionID; + queue_work(dev->wq, work); + /* Wait for the work to be complted on work queue */ + flush_workqueue(dev->wq); + + fput(file); + + smp_rmb(); + status = dev->read_send_result; + break; + } default: status = -ENOTTY; } +exit: return status; } @@ -1138,7 +1291,6 @@ mtpg_function_bind(struct usb_configuration *c, struct usb_function *f) fs_mtpg_out_desc.bEndpointAddress; int_hs_notify_desc.bEndpointAddress = int_fs_notify_desc.bEndpointAddress; - } mtpg->cdev = cdev; @@ -1208,6 +1360,7 @@ static int mtpg_function_set_alt(struct usb_function *f, dev->online = 1; dev->error = 0; dev->read_ready = 1; + dev->cancel_io = 0; /* readers may be blocked waiting for us to go online */ wake_up(&dev->read_wq); @@ -1259,6 +1412,7 @@ mtp_complete_cancel_io(struct usb_ep *ep, struct usb_request *req) memset(dev->cancel_io_buf, 0, USB_PTPREQUEST_CANCELIO_SIZE+1); memcpy(dev->cancel_io_buf, req->buf, USB_PTPREQUEST_CANCELIO_SIZE); + dev->cancel_io = 1; /*Debugging*/ for (i = 0; i < USB_PTPREQUEST_CANCELIO_SIZE; i++) DEBUG_MTPB("[%s]cancel_io_buf[%d]=%x\tline = [%d]\n", @@ -1299,7 +1453,8 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev, } return value; } else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { - if (ctrl->bRequest == 1 + if ((ctrl->bRequest == 1 || ctrl->bRequest == 0x54 || + ctrl->bRequest == 0x6F || ctrl->bRequest == 0xFE) && (ctrl->bRequestType & USB_DIR_IN) && (w_index == 4 || w_index == 5)) { value = (w_length < sizeof(mtpg_ext_config_desc) ? @@ -1443,6 +1598,14 @@ static int mtp_setup(void) INIT_LIST_HEAD(&mtpg->rx_done); INIT_LIST_HEAD(&mtpg->tx_idle); INIT_LIST_HEAD(&mtpg->intr_idle); + mtpg->wq = create_singlethread_workqueue("mtp_read_send"); + if (!mtpg->wq) { + printk(KERN_ERR "mtpg_dev_alloc work queue creation failed\n"); + rc = -ENOMEM; + goto err_work; + } + + INIT_WORK(&mtpg->read_send_work, read_send_work); /* the_mtpg must be set before calling usb_gadget_register_driver */ the_mtpg = mtpg; @@ -1454,7 +1617,7 @@ static int mtp_setup(void) } return 0; - +err_work: err_misc_register: the_mtpg = NULL; kfree(mtpg); |