aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/gadget/f_fs.c47
-rw-r--r--include/uapi/linux/usb/functionfs.h5
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__ */