Loading drivers/usb/gadget/function/f_cdev.c +155 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/debugfs.h> #include <linux/cdev.h> #include <linux/spinlock.h> #include <linux/usb/gadget.h> Loading Loading @@ -126,6 +127,9 @@ struct f_cdev { unsigned long nbytes_to_host; unsigned long nbytes_to_port_bridge; unsigned long nbytes_from_port_bridge; /* To test remote wakeup using debugfs */ u8 debugfs_rw_enable; }; struct f_cdev_opts { Loading @@ -135,6 +139,12 @@ struct f_cdev_opts { u8 port_num; }; struct usb_cser_debugfs { struct dentry *debugfs_root; }; static struct usb_cser_debugfs debugfs; static int major, minors; struct class *fcdev_classp; static DEFINE_IDA(chardev_ida); Loading @@ -147,6 +157,7 @@ static int usb_cser_connect(struct f_cdev *port); static void usb_cser_disconnect(struct f_cdev *port); static struct f_cdev *f_cdev_alloc(char *func_name, int portno); static void usb_cser_free_req(struct usb_ep *ep, struct usb_request *req); static void usb_cser_debugfs_exit(void); static struct usb_interface_descriptor cser_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, Loading Loading @@ -505,6 +516,32 @@ static int usb_cser_set_alt(struct usb_function *f, unsigned int intf, return rc; } static int usb_cser_func_suspend(struct usb_function *f, u8 options) { bool func_wakeup_allowed; func_wakeup_allowed = ((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0); f->func_wakeup_allowed = func_wakeup_allowed; if (options & FUNC_SUSPEND_OPT_SUSP_MASK) { if (!f->func_is_suspended) f->func_is_suspended = true; } else { if (f->func_is_suspended) f->func_is_suspended = false; } return 0; } static int usb_cser_get_status(struct usb_function *f) { bool remote_wakeup_en_status = f->func_wakeup_allowed ? 1 : 0; return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) | (1 << FUNC_WAKEUP_CAPABLE_SHIFT); } static void usb_cser_disable(struct usb_function *f) { struct f_cdev *port = func_to_port(f); Loading Loading @@ -823,6 +860,7 @@ static void cser_free_inst(struct usb_function_instance *fi) cdev_del(&opts->port->fcdev_cdev); } usb_cser_chardev_deinit(); usb_cser_debugfs_exit(); kfree(opts->func_name); kfree(opts->port); kfree(opts); Loading Loading @@ -1500,6 +1538,119 @@ static const struct file_operations f_cdev_fops = { .compat_ioctl = f_cdev_ioctl, }; static ssize_t cser_rw_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct seq_file *s = file->private_data; struct f_cdev *port = s->private; u8 input; struct cserial *cser; struct usb_function *func; struct usb_gadget *gadget; int ret; cser = &port->port_usb; if (!cser) { pr_err("cser is NULL\n"); return -EINVAL; } if (!port->is_connected) { pr_debug("port disconnected\n"); return -ENODEV; } func = &cser->func; if (!func) { pr_err("func is NULL\n"); return -EINVAL; } if (ubuf == NULL) { pr_debug("buffer is Null.\n"); goto err; } ret = kstrtou8_from_user(ubuf, count, 0, &input); if (ret) { pr_err("Invalid value. err:%d\n", ret); goto err; } if (port->debugfs_rw_enable == !!input) { if (!!input) pr_debug("RW already enabled\n"); else pr_debug("RW already disabled\n"); goto err; } port->debugfs_rw_enable = !!input; if (port->debugfs_rw_enable) { gadget = cser->func.config->cdev->gadget; if (gadget->speed == USB_SPEED_SUPER && func->func_is_suspended) { pr_debug("Calling usb_func_wakeup\n"); ret = usb_func_wakeup(func); } else { pr_debug("Calling usb_gadget_wakeup\n"); ret = usb_gadget_wakeup(gadget); } if ((ret == -EBUSY) || (ret == -EAGAIN)) pr_debug("RW delayed due to LPM exit.\n"); else if (ret) pr_err("wakeup failed. ret=%d.\n", ret); } else { pr_debug("RW disabled.\n"); } err: return count; } static int usb_cser_rw_show(struct seq_file *s, void *unused) { struct f_cdev *port = s->private; if (!port) { pr_err("port is null\n"); return 0; } seq_printf(s, "%d\n", port->debugfs_rw_enable); return 0; } static int debug_cdev_rw_open(struct inode *inode, struct file *f) { return single_open(f, usb_cser_rw_show, inode->i_private); } static const struct file_operations cser_rem_wakeup_fops = { .open = debug_cdev_rw_open, .read = seq_read, .write = cser_rw_write, .owner = THIS_MODULE, .llseek = seq_lseek, .release = seq_release, }; static void usb_cser_debugfs_init(struct f_cdev *port) { debugfs.debugfs_root = debugfs_create_dir(port->name, NULL); if (IS_ERR(debugfs.debugfs_root)) return; debugfs_create_file("remote_wakeup", 0600, debugfs.debugfs_root, port, &cser_rem_wakeup_fops); } static void usb_cser_debugfs_exit(void) { debugfs_remove_recursive(debugfs.debugfs_root); } static struct f_cdev *f_cdev_alloc(char *func_name, int portno) { int ret; Loading Loading @@ -1567,6 +1718,8 @@ static struct f_cdev *f_cdev_alloc(char *func_name, int portno) goto err_create_dev; } usb_cser_debugfs_init(port); pr_info("port_name:%s (%pK) portno:(%d)\n", port->name, port, port->port_num); return port; Loading Loading @@ -1829,6 +1982,8 @@ static struct usb_function *cser_alloc(struct usb_function_instance *fi) port->port_usb.func.set_alt = usb_cser_set_alt; port->port_usb.func.disable = usb_cser_disable; port->port_usb.func.setup = usb_cser_setup; port->port_usb.func.func_suspend = usb_cser_func_suspend; port->port_usb.func.get_status = usb_cser_get_status; port->port_usb.func.free_func = usb_cser_free_func; return &port->port_usb.func; Loading Loading
drivers/usb/gadget/function/f_cdev.c +155 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/debugfs.h> #include <linux/cdev.h> #include <linux/spinlock.h> #include <linux/usb/gadget.h> Loading Loading @@ -126,6 +127,9 @@ struct f_cdev { unsigned long nbytes_to_host; unsigned long nbytes_to_port_bridge; unsigned long nbytes_from_port_bridge; /* To test remote wakeup using debugfs */ u8 debugfs_rw_enable; }; struct f_cdev_opts { Loading @@ -135,6 +139,12 @@ struct f_cdev_opts { u8 port_num; }; struct usb_cser_debugfs { struct dentry *debugfs_root; }; static struct usb_cser_debugfs debugfs; static int major, minors; struct class *fcdev_classp; static DEFINE_IDA(chardev_ida); Loading @@ -147,6 +157,7 @@ static int usb_cser_connect(struct f_cdev *port); static void usb_cser_disconnect(struct f_cdev *port); static struct f_cdev *f_cdev_alloc(char *func_name, int portno); static void usb_cser_free_req(struct usb_ep *ep, struct usb_request *req); static void usb_cser_debugfs_exit(void); static struct usb_interface_descriptor cser_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, Loading Loading @@ -505,6 +516,32 @@ static int usb_cser_set_alt(struct usb_function *f, unsigned int intf, return rc; } static int usb_cser_func_suspend(struct usb_function *f, u8 options) { bool func_wakeup_allowed; func_wakeup_allowed = ((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0); f->func_wakeup_allowed = func_wakeup_allowed; if (options & FUNC_SUSPEND_OPT_SUSP_MASK) { if (!f->func_is_suspended) f->func_is_suspended = true; } else { if (f->func_is_suspended) f->func_is_suspended = false; } return 0; } static int usb_cser_get_status(struct usb_function *f) { bool remote_wakeup_en_status = f->func_wakeup_allowed ? 1 : 0; return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) | (1 << FUNC_WAKEUP_CAPABLE_SHIFT); } static void usb_cser_disable(struct usb_function *f) { struct f_cdev *port = func_to_port(f); Loading Loading @@ -823,6 +860,7 @@ static void cser_free_inst(struct usb_function_instance *fi) cdev_del(&opts->port->fcdev_cdev); } usb_cser_chardev_deinit(); usb_cser_debugfs_exit(); kfree(opts->func_name); kfree(opts->port); kfree(opts); Loading Loading @@ -1500,6 +1538,119 @@ static const struct file_operations f_cdev_fops = { .compat_ioctl = f_cdev_ioctl, }; static ssize_t cser_rw_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { struct seq_file *s = file->private_data; struct f_cdev *port = s->private; u8 input; struct cserial *cser; struct usb_function *func; struct usb_gadget *gadget; int ret; cser = &port->port_usb; if (!cser) { pr_err("cser is NULL\n"); return -EINVAL; } if (!port->is_connected) { pr_debug("port disconnected\n"); return -ENODEV; } func = &cser->func; if (!func) { pr_err("func is NULL\n"); return -EINVAL; } if (ubuf == NULL) { pr_debug("buffer is Null.\n"); goto err; } ret = kstrtou8_from_user(ubuf, count, 0, &input); if (ret) { pr_err("Invalid value. err:%d\n", ret); goto err; } if (port->debugfs_rw_enable == !!input) { if (!!input) pr_debug("RW already enabled\n"); else pr_debug("RW already disabled\n"); goto err; } port->debugfs_rw_enable = !!input; if (port->debugfs_rw_enable) { gadget = cser->func.config->cdev->gadget; if (gadget->speed == USB_SPEED_SUPER && func->func_is_suspended) { pr_debug("Calling usb_func_wakeup\n"); ret = usb_func_wakeup(func); } else { pr_debug("Calling usb_gadget_wakeup\n"); ret = usb_gadget_wakeup(gadget); } if ((ret == -EBUSY) || (ret == -EAGAIN)) pr_debug("RW delayed due to LPM exit.\n"); else if (ret) pr_err("wakeup failed. ret=%d.\n", ret); } else { pr_debug("RW disabled.\n"); } err: return count; } static int usb_cser_rw_show(struct seq_file *s, void *unused) { struct f_cdev *port = s->private; if (!port) { pr_err("port is null\n"); return 0; } seq_printf(s, "%d\n", port->debugfs_rw_enable); return 0; } static int debug_cdev_rw_open(struct inode *inode, struct file *f) { return single_open(f, usb_cser_rw_show, inode->i_private); } static const struct file_operations cser_rem_wakeup_fops = { .open = debug_cdev_rw_open, .read = seq_read, .write = cser_rw_write, .owner = THIS_MODULE, .llseek = seq_lseek, .release = seq_release, }; static void usb_cser_debugfs_init(struct f_cdev *port) { debugfs.debugfs_root = debugfs_create_dir(port->name, NULL); if (IS_ERR(debugfs.debugfs_root)) return; debugfs_create_file("remote_wakeup", 0600, debugfs.debugfs_root, port, &cser_rem_wakeup_fops); } static void usb_cser_debugfs_exit(void) { debugfs_remove_recursive(debugfs.debugfs_root); } static struct f_cdev *f_cdev_alloc(char *func_name, int portno) { int ret; Loading Loading @@ -1567,6 +1718,8 @@ static struct f_cdev *f_cdev_alloc(char *func_name, int portno) goto err_create_dev; } usb_cser_debugfs_init(port); pr_info("port_name:%s (%pK) portno:(%d)\n", port->name, port, port->port_num); return port; Loading Loading @@ -1829,6 +1982,8 @@ static struct usb_function *cser_alloc(struct usb_function_instance *fi) port->port_usb.func.set_alt = usb_cser_set_alt; port->port_usb.func.disable = usb_cser_disable; port->port_usb.func.setup = usb_cser_setup; port->port_usb.func.func_suspend = usb_cser_func_suspend; port->port_usb.func.get_status = usb_cser_get_status; port->port_usb.func.free_func = usb_cser_free_func; return &port->port_usb.func; Loading