From 23fa18eaa71266feff7ba8d83022d9e1cc83c65a Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 10 May 2024 07:42:03 +0000
Subject: [PATCH] disable pwm7
---
kernel/drivers/usb/gadget/function/f_fs.c | 450 +++++++++++++++++++++++++++++++++++--------------------
1 files changed, 284 insertions(+), 166 deletions(-)
diff --git a/kernel/drivers/usb/gadget/function/f_fs.c b/kernel/drivers/usb/gadget/function/f_fs.c
index 6634dd6..bae40a7 100644
--- a/kernel/drivers/usb/gadget/function/f_fs.c
+++ b/kernel/drivers/usb/gadget/function/f_fs.c
@@ -17,17 +17,22 @@
#include <linux/blkdev.h>
#include <linux/pagemap.h>
#include <linux/export.h>
+#include <linux/fs_parser.h>
#include <linux/hid.h>
+#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/scatterlist.h>
#include <linux/sched/signal.h>
#include <linux/uio.h>
+#include <linux/vmalloc.h>
#include <asm/unaligned.h>
+#include <linux/usb/ccid.h>
#include <linux/usb/composite.h>
#include <linux/usb/functionfs.h>
#include <linux/aio.h>
-#include <linux/mmu_context.h>
+#include <linux/kthread.h>
#include <linux/poll.h>
#include <linux/eventfd.h>
@@ -117,8 +122,6 @@
struct usb_endpoint_descriptor *descs[3];
u8 num;
-
- int status; /* P: epfile->mutex */
};
struct ffs_epfile {
@@ -218,8 +221,13 @@
struct usb_ep *ep;
struct usb_request *req;
+ struct sg_table sgt;
+ bool use_sg;
struct ffs_data *ffs;
+
+ int status;
+ struct completion done;
};
struct ffs_desc_helper {
@@ -270,6 +278,11 @@
{
struct usb_request *req = ffs->ep0req;
int ret;
+
+ if (!req) {
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ return -EINVAL;
+ }
req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
@@ -561,7 +574,7 @@
spin_unlock_irq(&ffs->ev.waitq.lock);
if (likely(len)) {
- data = kmalloc(len, GFP_KERNEL);
+ data = kmalloc(ALIGN(len, cache_line_size()), GFP_KERNEL);
if (unlikely(!data)) {
ret = -ENOMEM;
goto done_mutex;
@@ -698,12 +711,15 @@
static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
{
+ struct ffs_io_data *io_data = req->context;
+
ENTER();
- if (likely(req->context)) {
- struct ffs_ep *ep = _ep->driver_data;
- ep->status = req->status ? req->status : req->actual;
- complete(req->context);
- }
+ if (req->status)
+ io_data->status = req->status;
+ else
+ io_data->status = req->actual;
+
+ complete(&io_data->done);
}
static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter)
@@ -749,6 +765,65 @@
return ret;
}
+/*
+ * allocate a virtually contiguous buffer and create a scatterlist describing it
+ * @sg_table - pointer to a place to be filled with sg_table contents
+ * @size - required buffer size
+ */
+static void *ffs_build_sg_list(struct sg_table *sgt, size_t sz)
+{
+ struct page **pages;
+ void *vaddr, *ptr;
+ unsigned int n_pages;
+ int i;
+
+ vaddr = vmalloc(sz);
+ if (!vaddr)
+ return NULL;
+
+ n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
+ pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
+ if (!pages) {
+ vfree(vaddr);
+
+ return NULL;
+ }
+ for (i = 0, ptr = vaddr; i < n_pages; ++i, ptr += PAGE_SIZE)
+ pages[i] = vmalloc_to_page(ptr);
+
+ if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) {
+ kvfree(pages);
+ vfree(vaddr);
+
+ return NULL;
+ }
+ kvfree(pages);
+
+ return vaddr;
+}
+
+static inline void *ffs_alloc_buffer(struct ffs_io_data *io_data,
+ size_t data_len)
+{
+ if (io_data->use_sg)
+ return ffs_build_sg_list(&io_data->sgt, data_len);
+
+ return kmalloc(ALIGN(data_len, cache_line_size()), GFP_KERNEL);
+}
+
+static inline void ffs_free_buffer(struct ffs_io_data *io_data)
+{
+ if (!io_data->buf)
+ return;
+
+ if (io_data->use_sg) {
+ sg_free_table(&io_data->sgt);
+ vfree(io_data->buf);
+ } else {
+ kfree(io_data->buf);
+ }
+}
+
static void ffs_user_copy_worker(struct work_struct *work)
{
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
@@ -758,13 +833,9 @@
bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD;
if (io_data->read && ret > 0) {
- mm_segment_t oldfs = get_fs();
-
- set_fs(USER_DS);
- use_mm(io_data->mm);
+ kthread_use_mm(io_data->mm);
ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data);
- unuse_mm(io_data->mm);
- set_fs(oldfs);
+ kthread_unuse_mm(io_data->mm);
}
io_data->kiocb->ki_complete(io_data->kiocb, ret, ret);
@@ -776,7 +847,7 @@
if (io_data->read)
kfree(io_data->to_free);
- kfree(io_data->buf);
+ ffs_free_buffer(io_data);
kfree(io_data);
}
@@ -946,9 +1017,11 @@
*/
if (io_data->read)
data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
+
+ io_data->use_sg = gadget->sg_supported && data_len > PAGE_SIZE;
spin_unlock_irq(&epfile->ffs->eps_lock);
- data = kmalloc(data_len, GFP_KERNEL);
+ data = ffs_alloc_buffer(io_data, data_len);
if (unlikely(!data)) {
ret = -ENOMEM;
goto error_mutex;
@@ -984,14 +1057,23 @@
WARN(1, "%s: data_len == -EINVAL\n", __func__);
ret = -EINVAL;
} else if (!io_data->aio) {
- DECLARE_COMPLETION_ONSTACK(done);
bool interrupted = false;
req = ep->req;
- req->buf = data;
- req->length = data_len;
+ if (io_data->use_sg) {
+ req->buf = NULL;
+ req->sg = io_data->sgt.sgl;
+ req->num_sgs = io_data->sgt.nents;
+ } else {
+ req->buf = data;
+ req->num_sgs = 0;
+ }
+ req->length = data_len;
- req->context = &done;
+ io_data->buf = data;
+
+ init_completion(&io_data->done);
+ req->context = io_data;
req->complete = ffs_epfile_io_complete;
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
@@ -1000,7 +1082,12 @@
spin_unlock_irq(&epfile->ffs->eps_lock);
- if (unlikely(wait_for_completion_interruptible(&done))) {
+ if (unlikely(wait_for_completion_interruptible(&io_data->done))) {
+ spin_lock_irq(&epfile->ffs->eps_lock);
+ if (epfile->ep != ep) {
+ ret = -ESHUTDOWN;
+ goto error_lock;
+ }
/*
* To avoid race condition with ffs_epfile_io_complete,
* dequeue the request first then check
@@ -1008,23 +1095,31 @@
* condition with req->complete callback.
*/
usb_ep_dequeue(ep->ep, req);
- wait_for_completion(&done);
- interrupted = ep->status < 0;
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ wait_for_completion(&io_data->done);
+ interrupted = io_data->status < 0;
}
if (interrupted)
ret = -EINTR;
- else if (io_data->read && ep->status > 0)
- ret = __ffs_epfile_read_data(epfile, data, ep->status,
+ else if (io_data->read && io_data->status > 0)
+ ret = __ffs_epfile_read_data(epfile, data, io_data->status,
&io_data->data);
else
- ret = ep->status;
+ ret = io_data->status;
goto error_mutex;
} else if (!(req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC))) {
ret = -ENOMEM;
} else {
- req->buf = data;
- req->length = data_len;
+ if (io_data->use_sg) {
+ req->buf = NULL;
+ req->sg = io_data->sgt.sgl;
+ req->num_sgs = io_data->sgt.nents;
+ } else {
+ req->buf = data;
+ req->num_sgs = 0;
+ }
+ req->length = data_len;
io_data->buf = data;
io_data->ep = ep->ep;
@@ -1054,7 +1149,8 @@
error_mutex:
mutex_unlock(&epfile->mutex);
error:
- kfree(data);
+ if (ret != -EIOCBQUEUED) /* don't free if there is iocb queued */
+ ffs_free_buffer(io_data);
return ret;
}
@@ -1274,14 +1370,6 @@
return ret;
}
-#ifdef CONFIG_COMPAT
-static long ffs_epfile_compat_ioctl(struct file *file, unsigned code,
- unsigned long value)
-{
- return ffs_epfile_ioctl(file, code, value);
-}
-#endif
-
static const struct file_operations ffs_epfile_operations = {
.llseek = no_llseek,
@@ -1290,9 +1378,7 @@
.read_iter = ffs_epfile_read_iter,
.release = ffs_epfile_release,
.unlocked_ioctl = ffs_epfile_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = ffs_epfile_compat_ioctl,
-#endif
+ .compat_ioctl = compat_ptr_ioctl,
};
@@ -1374,9 +1460,9 @@
struct ffs_data *ffs_data;
};
-static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
+static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc)
{
- struct ffs_sb_fill_data *data = _data;
+ struct ffs_sb_fill_data *data = fc->fs_private;
struct inode *inode;
struct ffs_data *ffs = data->ffs_data;
@@ -1409,143 +1495,145 @@
return 0;
}
-static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
+enum {
+ Opt_no_disconnect,
+ Opt_rmode,
+ Opt_fmode,
+ Opt_mode,
+ Opt_uid,
+ Opt_gid,
+};
+
+static const struct fs_parameter_spec ffs_fs_fs_parameters[] = {
+ fsparam_bool ("no_disconnect", Opt_no_disconnect),
+ fsparam_u32 ("rmode", Opt_rmode),
+ fsparam_u32 ("fmode", Opt_fmode),
+ fsparam_u32 ("mode", Opt_mode),
+ fsparam_u32 ("uid", Opt_uid),
+ fsparam_u32 ("gid", Opt_gid),
+ {}
+};
+
+static int ffs_fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
+ struct ffs_sb_fill_data *data = fc->fs_private;
+ struct fs_parse_result result;
+ int opt;
+
ENTER();
- if (!opts || !*opts)
- return 0;
+ opt = fs_parse(fc, ffs_fs_fs_parameters, param, &result);
+ if (opt < 0)
+ return opt;
- for (;;) {
- unsigned long value;
- char *eq, *comma;
+ switch (opt) {
+ case Opt_no_disconnect:
+ data->no_disconnect = result.boolean;
+ break;
+ case Opt_rmode:
+ data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
+ break;
+ case Opt_fmode:
+ data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
+ break;
+ case Opt_mode:
+ data->root_mode = (result.uint_32 & 0555) | S_IFDIR;
+ data->perms.mode = (result.uint_32 & 0666) | S_IFREG;
+ break;
- /* Option limit */
- comma = strchr(opts, ',');
- if (comma)
- *comma = 0;
+ case Opt_uid:
+ data->perms.uid = make_kuid(current_user_ns(), result.uint_32);
+ if (!uid_valid(data->perms.uid))
+ goto unmapped_value;
+ break;
+ case Opt_gid:
+ data->perms.gid = make_kgid(current_user_ns(), result.uint_32);
+ if (!gid_valid(data->perms.gid))
+ goto unmapped_value;
+ break;
- /* Value limit */
- eq = strchr(opts, '=');
- if (unlikely(!eq)) {
- pr_err("'=' missing in %s\n", opts);
- return -EINVAL;
- }
- *eq = 0;
-
- /* Parse value */
- if (kstrtoul(eq + 1, 0, &value)) {
- pr_err("%s: invalid value: %s\n", opts, eq + 1);
- return -EINVAL;
- }
-
- /* Interpret option */
- switch (eq - opts) {
- case 13:
- if (!memcmp(opts, "no_disconnect", 13))
- data->no_disconnect = !!value;
- else
- goto invalid;
- break;
- case 5:
- if (!memcmp(opts, "rmode", 5))
- data->root_mode = (value & 0555) | S_IFDIR;
- else if (!memcmp(opts, "fmode", 5))
- data->perms.mode = (value & 0666) | S_IFREG;
- else
- goto invalid;
- break;
-
- case 4:
- if (!memcmp(opts, "mode", 4)) {
- data->root_mode = (value & 0555) | S_IFDIR;
- data->perms.mode = (value & 0666) | S_IFREG;
- } else {
- goto invalid;
- }
- break;
-
- case 3:
- if (!memcmp(opts, "uid", 3)) {
- data->perms.uid = make_kuid(current_user_ns(), value);
- if (!uid_valid(data->perms.uid)) {
- pr_err("%s: unmapped value: %lu\n", opts, value);
- return -EINVAL;
- }
- } else if (!memcmp(opts, "gid", 3)) {
- data->perms.gid = make_kgid(current_user_ns(), value);
- if (!gid_valid(data->perms.gid)) {
- pr_err("%s: unmapped value: %lu\n", opts, value);
- return -EINVAL;
- }
- } else {
- goto invalid;
- }
- break;
-
- default:
-invalid:
- pr_err("%s: invalid option\n", opts);
- return -EINVAL;
- }
-
- /* Next iteration */
- if (!comma)
- break;
- opts = comma + 1;
+ default:
+ return -ENOPARAM;
}
return 0;
+
+unmapped_value:
+ return invalf(fc, "%s: unmapped value: %u", param->key, result.uint_32);
}
-/* "mount -t functionfs dev_name /dev/function" ends up here */
-
-static struct dentry *
-ffs_fs_mount(struct file_system_type *t, int flags,
- const char *dev_name, void *opts)
+/*
+ * Set up the superblock for a mount.
+ */
+static int ffs_fs_get_tree(struct fs_context *fc)
{
- struct ffs_sb_fill_data data = {
- .perms = {
- .mode = S_IFREG | 0600,
- .uid = GLOBAL_ROOT_UID,
- .gid = GLOBAL_ROOT_GID,
- },
- .root_mode = S_IFDIR | 0500,
- .no_disconnect = false,
- };
- struct dentry *rv;
- int ret;
+ struct ffs_sb_fill_data *ctx = fc->fs_private;
struct ffs_data *ffs;
+ int ret;
ENTER();
- ret = ffs_fs_parse_opts(&data, opts);
- if (unlikely(ret < 0))
- return ERR_PTR(ret);
+ if (!fc->source)
+ return invalf(fc, "No source specified");
- ffs = ffs_data_new(dev_name);
+ ffs = ffs_data_new(fc->source);
if (unlikely(!ffs))
- return ERR_PTR(-ENOMEM);
- ffs->file_perms = data.perms;
- ffs->no_disconnect = data.no_disconnect;
+ return -ENOMEM;
+ ffs->file_perms = ctx->perms;
+ ffs->no_disconnect = ctx->no_disconnect;
- ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
+ ffs->dev_name = kstrdup(fc->source, GFP_KERNEL);
if (unlikely(!ffs->dev_name)) {
ffs_data_put(ffs);
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
- ret = ffs_acquire_dev(dev_name, ffs);
+ ret = ffs_acquire_dev(ffs->dev_name, ffs);
if (ret) {
ffs_data_put(ffs);
- return ERR_PTR(ret);
+ return ret;
}
- data.ffs_data = ffs;
- rv = mount_nodev(t, flags, &data, ffs_sb_fill);
- if (IS_ERR(rv) && data.ffs_data)
- ffs_data_put(data.ffs_data);
- return rv;
+ ctx->ffs_data = ffs;
+ return get_tree_nodev(fc, ffs_sb_fill);
+}
+
+static void ffs_fs_free_fc(struct fs_context *fc)
+{
+ struct ffs_sb_fill_data *ctx = fc->fs_private;
+
+ if (ctx) {
+ if (ctx->ffs_data) {
+ ffs_data_put(ctx->ffs_data);
+ }
+
+ kfree(ctx);
+ }
+}
+
+static const struct fs_context_operations ffs_fs_context_ops = {
+ .free = ffs_fs_free_fc,
+ .parse_param = ffs_fs_parse_param,
+ .get_tree = ffs_fs_get_tree,
+};
+
+static int ffs_fs_init_fs_context(struct fs_context *fc)
+{
+ struct ffs_sb_fill_data *ctx;
+
+ ctx = kzalloc(sizeof(struct ffs_sb_fill_data), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->perms.mode = S_IFREG | 0600;
+ ctx->perms.uid = GLOBAL_ROOT_UID;
+ ctx->perms.gid = GLOBAL_ROOT_GID;
+ ctx->root_mode = S_IFDIR | 0500;
+ ctx->no_disconnect = false;
+
+ fc->fs_private = ctx;
+ fc->ops = &ffs_fs_context_ops;
+ return 0;
}
static void
@@ -1561,7 +1649,8 @@
static struct file_system_type ffs_fs_type = {
.owner = THIS_MODULE,
.name = "functionfs",
- .mount = ffs_fs_mount,
+ .init_fs_context = ffs_fs_init_fs_context,
+ .parameters = ffs_fs_fs_parameters,
.kill_sb = ffs_fs_kill_sb,
};
MODULE_ALIAS_FS("functionfs");
@@ -1807,10 +1896,14 @@
ENTER();
if (!WARN_ON(!ffs->gadget)) {
+ /* dequeue before freeing ep0req */
+ usb_ep_dequeue(ffs->gadget->ep0, ffs->ep0req);
+ mutex_lock(&ffs->mutex);
usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
ffs->ep0req = NULL;
ffs->gadget = NULL;
clear_bit(FFS_FL_BOUND, &ffs->flags);
+ mutex_unlock(&ffs->mutex);
ffs_data_put(ffs);
}
}
@@ -1964,7 +2057,7 @@
static int __must_check ffs_do_single_desc(char *data, unsigned len,
ffs_entity_callback entity,
- void *priv)
+ void *priv, int *current_class)
{
struct usb_descriptor_header *_ds = (void *)data;
u8 length;
@@ -2022,6 +2115,7 @@
__entity(INTERFACE, ds->bInterfaceNumber);
if (ds->iInterface)
__entity(STRING, ds->iInterface);
+ *current_class = ds->bInterfaceClass;
}
break;
@@ -2035,11 +2129,22 @@
}
break;
- case HID_DT_HID:
- pr_vdebug("hid descriptor\n");
- if (length != sizeof(struct hid_descriptor))
- goto inv_length;
- break;
+ case USB_TYPE_CLASS | 0x01:
+ if (*current_class == USB_INTERFACE_CLASS_HID) {
+ pr_vdebug("hid descriptor\n");
+ if (length != sizeof(struct hid_descriptor))
+ goto inv_length;
+ break;
+ } else if (*current_class == USB_INTERFACE_CLASS_CCID) {
+ pr_vdebug("ccid descriptor\n");
+ if (length != sizeof(struct ccid_descriptor))
+ goto inv_length;
+ break;
+ } else {
+ pr_vdebug("unknown descriptor: %d for class %d\n",
+ _ds->bDescriptorType, *current_class);
+ return -EINVAL;
+ }
case USB_DT_OTG:
if (length != sizeof(struct usb_otg_descriptor))
@@ -2096,6 +2201,7 @@
{
const unsigned _len = len;
unsigned long num = 0;
+ int current_class = -1;
ENTER();
@@ -2116,7 +2222,8 @@
if (!data)
return _len - len;
- ret = ffs_do_single_desc(data, len, entity, priv);
+ ret = ffs_do_single_desc(data, len, entity, priv,
+ ¤t_class);
if (unlikely(ret < 0)) {
pr_debug("%s returns %d\n", __func__, ret);
return ret;
@@ -2295,7 +2402,7 @@
return _len - len;
}
-/**
+/*
* Validate contents of the buffer from userspace related to OS descriptors.
*/
static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
@@ -2447,7 +2554,7 @@
os_descs_count = get_unaligned_le32(data);
data += 4;
len -= 4;
- };
+ }
/* Read descriptors */
raw_descs = data;
@@ -2670,7 +2777,7 @@
switch (type) {
case FUNCTIONFS_RESUME:
rem_type2 = FUNCTIONFS_SUSPEND;
- /* FALL THROUGH */
+ fallthrough;
case FUNCTIONFS_SUSPEND:
case FUNCTIONFS_SETUP:
rem_type1 = type;
@@ -2788,12 +2895,18 @@
struct usb_request *req;
struct usb_ep *ep;
u8 bEndpointAddress;
+ u16 wMaxPacketSize;
/*
* We back up bEndpointAddress because autoconfig overwrites
* it with physical endpoint address.
*/
bEndpointAddress = ds->bEndpointAddress;
+ /*
+ * We back up wMaxPacketSize because autoconfig treats
+ * endpoint descriptors as if they were full speed.
+ */
+ wMaxPacketSize = ds->wMaxPacketSize;
pr_vdebug("autoconfig\n");
ep = usb_ep_autoconfig(func->gadget, ds);
if (unlikely(!ep))
@@ -2815,6 +2928,11 @@
*/
if (func->ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
ds->bEndpointAddress = bEndpointAddress;
+ /*
+ * Restore wMaxPacketSize which was potentially
+ * overwritten by autoconfig.
+ */
+ ds->wMaxPacketSize = wMaxPacketSize;
}
ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
@@ -3449,7 +3567,7 @@
static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
{
- if (strlen(name) >= FIELD_SIZEOF(struct ffs_dev, name))
+ if (strlen(name) >= sizeof_field(struct ffs_dev, name))
return -ENAMETOOLONG;
return ffs_name_dev(to_f_fs_opts(fi)->dev, name);
}
@@ -3505,6 +3623,7 @@
/* Drain any pending AIO completions */
drain_workqueue(ffs->io_completion_wq);
+ ffs_event_add(ffs, FUNCTIONFS_UNBIND);
if (!--opts->refcnt)
functionfs_unbind(ffs);
@@ -3529,7 +3648,6 @@
func->function.ssp_descriptors = NULL;
func->interfaces_nums = NULL;
- ffs_event_add(ffs, FUNCTIONFS_UNBIND);
}
static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
--
Gitblit v1.6.2