diff options
| -rw-r--r-- | drivers/usb/gadget/f_fs.c | 47 | ||||
| -rw-r--r-- | include/uapi/linux/usb/functionfs.h | 5 |
2 files changed, 46 insertions, 6 deletions
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index dce917ed3..ef910d05e 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -332,6 +332,9 @@ struct ffs_epfile { unsigned char isoc; /* P: ffs->eps_lock */ unsigned char _pad; + + unsigned long buf_len; + char *buffer; }; static int __must_check ffs_epfiles_create(struct ffs_data *ffs); @@ -767,7 +770,7 @@ static ssize_t ffs_epfile_io(struct file *file, char *data = NULL; ssize_t ret; int halt; - int buffer_len = 0; + size_t buffer_len = 0; pr_debug("%s: len %lld, read %d\n", __func__, (u64)len, read); @@ -830,9 +833,13 @@ first_try: /* Allocate & copy */ if (!halt && !data) { #if defined(CONFIG_64BIT) && defined(CONFIG_MTK_LM_MODE) - data = kzalloc(buffer_len, GFP_KERNEL | GFP_DMA); + data = buffer_len > epfile->buf_len ? + kzalloc(buffer_len, GFP_KERNEL| GFP_DMA) : + epfile->buffer; #else - data = kzalloc(buffer_len, GFP_KERNEL); + data = buffer_len > epfile->buf_len ? + kzalloc(buffer_len, GFP_KERNEL) : + epfile->buffer; #endif if (unlikely(!data)) return -ENOMEM; @@ -922,7 +929,8 @@ first_try: mutex_unlock(&epfile->mutex); error: - kfree(data); + if (buffer_len > epfile->buf_len) + kfree(data); return ret; } @@ -985,7 +993,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, return -ENODEV; spin_lock_irq(&epfile->ffs->eps_lock); - if (likely(epfile->ep)) { + if (epfile->ep) { switch (code) { case FUNCTIONFS_FIFO_STATUS: ret = usb_ep_fifo_status(epfile->ep->ep); @@ -1023,11 +1031,28 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, ret = -EFAULT; return ret; } + case FUNCTIONFS_ENDPOINT_ALLOC: + kfree(epfile->buffer); + epfile->buffer = NULL; + epfile->buf_len = value; + if (epfile->buf_len) { + epfile->buffer = kzalloc(epfile->buf_len, + GFP_KERNEL); + if (!epfile->buffer) + ret = -ENOMEM; + } + break; default: ret = -ENOTTY; } } else { - ret = -ENODEV; + switch (code) { + case FUNCTIONFS_ENDPOINT_ALLOC: + epfile->buf_len = value; + break; + default: + ret = -ENODEV; + } } spin_unlock_irq(&epfile->ffs->eps_lock); @@ -1635,6 +1660,8 @@ static void ffs_func_eps_disable(struct ffs_function *func) do { atomic_set(&epfile->error, 1); epfile->ep = NULL; + kfree(epfile->buffer); + epfile->buffer = NULL; /* pending requests get nuked */ if (likely(ep->ep)) { @@ -1677,6 +1704,14 @@ static int ffs_func_eps_enable(struct ffs_function *func) ep->ep->driver_data = ep; ep->ep->desc = ds; + if (epfile->buf_len) { + epfile->buffer = kzalloc(epfile->buf_len, + GFP_KERNEL); + if (!epfile->buffer) { + ret = -ENOMEM; + break; + } + } ret = usb_ep_enable(ep->ep); if (likely(!ret)) { epfile->ep = ep; diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h index 8a17a23d0..94aba0243 100644 --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -175,6 +175,11 @@ struct usb_functionfs_event { #define FUNCTIONFS_ENDPOINT_DESC _IOR('g', 130, \ struct usb_endpoint_descriptor) +/* + * Sets a buffer length for which all r/w operations under that size use a + * preallocated buffer. Behavior of larger operations does not change. + */ +#define FUNCTIONFS_ENDPOINT_ALLOC _IOR('g', 131, __u32) #endif /* _UAPI__LINUX_FUNCTIONFS_H__ */ |
