From 8d2a02b24d66aa359e83eebc1ed3c0f85367a1cb Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Thu, 16 May 2024 03:11:33 +0000 Subject: [PATCH] AX88772C_eeprom and ax8872c build together --- 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