Loading drivers/usb/gadget/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -595,6 +595,17 @@ config USB_CONFIGFS_F_GSI related functionalities using GSI hardware accelerated data path and control path. config USB_FUNC_WAKEUP_SUPPORTED bool "USB Function Remote Wakeup support" depends on QGKI help USB 3.x allows functions to be independently suspended for better power management on a per-interface basis. Additionally a function can also be a remote wake source. Enable this option to add support for allowing a function to issue a remote wakeup to the composite driver, which in turn will issue a device notification packet request to the device controller driver. choice tristate "USB Gadget precomposed configurations" default USB_ETH Loading drivers/usb/gadget/composite.c +45 −0 Original line number Diff line number Diff line Loading @@ -426,6 +426,51 @@ int usb_interface_id(struct usb_configuration *config, } EXPORT_SYMBOL_GPL(usb_interface_id); #ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED int usb_func_wakeup(struct usb_function *func) { int ret, id; unsigned long flags; if (!func || !func->config || !func->config->cdev || !func->config->cdev->gadget) return -EINVAL; DBG(func->config->cdev, "%s function wakeup\n", func->name); spin_lock_irqsave(&func->config->cdev->lock, flags); for (id = 0; id < MAX_CONFIG_INTERFACES; id++) if (func->config->interface[id] == func) break; if (id == MAX_CONFIG_INTERFACES) { ERROR(func->config->cdev, "Invalid function id:%d\n", id); ret = -EINVAL; goto err; } ret = usb_gadget_func_wakeup(func->config->cdev->gadget, id); if (ret == -EAGAIN) { DBG(func->config->cdev, "Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n", func->name ? func->name : ""); ret = 0; } else if (ret < 0 && ret != -ENOTSUPP) { ERROR(func->config->cdev, "Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n", func->name ? func->name : "", ret); } err: spin_unlock_irqrestore(&func->config->cdev->lock, flags); return ret; } EXPORT_SYMBOL(usb_func_wakeup); #endif static u8 encode_bMaxPower(enum usb_device_speed speed, struct usb_configuration *c) { Loading drivers/usb/gadget/function/f_cdev.c +18 −17 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2011, 2013-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2011, 2013-2020, The Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 license terms, * and distributes only under these terms. * Loading Loading @@ -107,6 +107,10 @@ struct f_cdev { /* current USB RX buffer */ u8 *current_rx_buf; /* function suspend status */ bool func_is_suspended; bool func_wakeup_allowed; struct cserial port_usb; #define ACM_CTRL_DTR 0x01 Loading Loading @@ -514,28 +518,25 @@ static int usb_cser_set_alt(struct usb_function *f, unsigned int intf, static int usb_cser_func_suspend(struct usb_function *f, u8 options) { bool func_wakeup_allowed; struct f_cdev *port = func_to_port(f); func_wakeup_allowed = ((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0); port->func_wakeup_allowed = !!(options & (USB_INTRF_FUNC_SUSPEND_RW >> 8)); port->func_is_suspended = options & (USB_INTRF_FUNC_SUSPEND_LP >> 8); 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; #ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED struct f_cdev *port = func_to_port(f); return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) | (1 << FUNC_WAKEUP_CAPABLE_SHIFT); return (port->func_wakeup_allowed ? USB_INTRF_STAT_FUNC_RW : 0) | USB_INTRF_STAT_FUNC_RW_CAP; #else return 0; #endif } static void usb_cser_disable(struct usb_function *f) Loading Loading @@ -1584,8 +1585,8 @@ static ssize_t cser_rw_write(struct file *file, const char __user *ubuf, 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) { if (gadget->speed >= USB_SPEED_SUPER && port->func_is_suspended) { pr_debug("Calling usb_func_wakeup\n"); ret = usb_func_wakeup(func); } else { Loading drivers/usb/gadget/function/f_gsi.c +34 −26 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #include <linux/module.h> Loading Loading @@ -34,13 +34,11 @@ static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt); static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f) { bool remote_wakeup_allowed; bool remote_wakeup_allowed = true; struct f_gsi *gsi = func_to_gsi(f); if (f->config->cdev->gadget->speed >= USB_SPEED_SUPER) remote_wakeup_allowed = f->func_wakeup_allowed; else remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; remote_wakeup_allowed = gsi->func_wakeup_allowed; log_event_dbg("%s: remote_wakeup_allowed:%s", __func__, (remote_wakeup_allowed ? "true" : "false")); Loading Loading @@ -156,7 +154,7 @@ static int gsi_wakeup_host(struct f_gsi *gsi) * allowed to do so by the host. This is done in order to support non * fully USB 3.0 compatible hosts. */ if ((gadget->speed >= USB_SPEED_SUPER) && (func->func_is_suspended)) { if ((gadget->speed >= USB_SPEED_SUPER) && (gsi->func_is_suspended)) { log_event_dbg("%s: Calling usb_func_wakeup", __func__); ret = usb_func_wakeup(func); } else { Loading Loading @@ -730,7 +728,7 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port) struct f_gsi *gsi = d_port_to_gsi(d_port); struct usb_function *f = &gsi->function; f_suspend = f->func_wakeup_allowed; f_suspend = gsi->func_wakeup_allowed; log_event_dbg("%s: f_suspend:%d", __func__, f_suspend); if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend, Loading Loading @@ -1377,8 +1375,7 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf, return -ECONNRESET; } if (gsi->function.func_is_suspended && !gsi->function.func_wakeup_allowed) { if (gsi->func_is_suspended && !gsi->func_wakeup_allowed) { c_port->cpkt_drop_cnt++; log_event_err("drop ctrl pkt of len %zu", count); return -ENOTSUPP; Loading Loading @@ -1792,9 +1789,17 @@ static int queue_notification_request(struct f_gsi *gsi) int ret; unsigned long flags; ret = usb_func_ep_queue(&gsi->function, gsi->c_port.notify, if (!gsi->func_is_suspended) { ret = usb_ep_queue(gsi->c_port.notify, gsi->c_port.notify_req, GFP_ATOMIC); if (ret < 0) { } else { if (gsi->func_wakeup_allowed) ret = usb_func_wakeup(&gsi->function); else ret = -EOPNOTSUPP; } if (ret < 0 || gsi->func_is_suspended) { spin_lock_irqsave(&gsi->c_port.lock, flags); gsi->c_port.notify_req_queued = false; spin_unlock_irqrestore(&gsi->c_port.lock, flags); Loading Loading @@ -2435,7 +2440,7 @@ static void gsi_suspend(struct usb_function *f) struct f_gsi *gsi = func_to_gsi(f); /* Check if function is already suspended in gsi_func_suspend() */ if (f->func_is_suspended) { if (gsi->func_is_suspended) { log_event_dbg("%s: func already suspended, return\n", __func__); return; } Loading @@ -2460,7 +2465,7 @@ static void gsi_resume(struct usb_function *f) * canceled. In this case resume is done by a Function Resume request. */ if ((cdev->gadget->speed >= USB_SPEED_SUPER) && f->func_is_suspended) gsi->func_is_suspended) return; if (gsi->c_port.notify && !gsi->c_port.notify->desc) Loading Loading @@ -2488,10 +2493,14 @@ static void gsi_resume(struct usb_function *f) static int gsi_get_status(struct usb_function *f) { unsigned int remote_wakeup_en_status = f->func_wakeup_allowed ? 1 : 0; #ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED struct f_gsi *gsi = func_to_gsi(f); return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) | (1 << FUNC_WAKEUP_CAPABLE_SHIFT); return (gsi->func_wakeup_allowed ? USB_INTRF_STAT_FUNC_RW : 0) | USB_INTRF_STAT_FUNC_RW_CAP; #else return 0; #endif } static int gsi_func_suspend(struct usb_function *f, u8 options) Loading @@ -2502,21 +2511,20 @@ static int gsi_func_suspend(struct usb_function *f, u8 options) log_event_dbg("func susp %u cmd for %s", options, f->name ? f->name : ""); func_wakeup_allowed = ((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0); func_wakeup_allowed = !!(options & (USB_INTRF_FUNC_SUSPEND_RW >> 8)); if (options & FUNC_SUSPEND_OPT_SUSP_MASK) { f->func_wakeup_allowed = func_wakeup_allowed; if (!f->func_is_suspended) { if (options & (USB_INTRF_FUNC_SUSPEND_LP >> 8)) { gsi->func_wakeup_allowed = func_wakeup_allowed; if (!gsi->func_is_suspended) { gsi_suspend(f); f->func_is_suspended = true; gsi->func_is_suspended = true; } } else { if (f->func_is_suspended) { f->func_is_suspended = false; if (gsi->func_is_suspended) { gsi->func_is_suspended = false; gsi_resume(f); } f->func_wakeup_allowed = func_wakeup_allowed; gsi->func_wakeup_allowed = func_wakeup_allowed; } return 0; Loading drivers/usb/gadget/function/f_gsi.h +5 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #ifndef _F_GSI_H Loading Loading @@ -269,6 +269,10 @@ struct f_gsi { bool data_interface_up; enum rndis_class_id rndis_id; /* function suspend status */ bool func_is_suspended; bool func_wakeup_allowed; const struct usb_endpoint_descriptor *in_ep_desc_backup; const struct usb_endpoint_descriptor *out_ep_desc_backup; Loading Loading
drivers/usb/gadget/Kconfig +11 −0 Original line number Diff line number Diff line Loading @@ -595,6 +595,17 @@ config USB_CONFIGFS_F_GSI related functionalities using GSI hardware accelerated data path and control path. config USB_FUNC_WAKEUP_SUPPORTED bool "USB Function Remote Wakeup support" depends on QGKI help USB 3.x allows functions to be independently suspended for better power management on a per-interface basis. Additionally a function can also be a remote wake source. Enable this option to add support for allowing a function to issue a remote wakeup to the composite driver, which in turn will issue a device notification packet request to the device controller driver. choice tristate "USB Gadget precomposed configurations" default USB_ETH Loading
drivers/usb/gadget/composite.c +45 −0 Original line number Diff line number Diff line Loading @@ -426,6 +426,51 @@ int usb_interface_id(struct usb_configuration *config, } EXPORT_SYMBOL_GPL(usb_interface_id); #ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED int usb_func_wakeup(struct usb_function *func) { int ret, id; unsigned long flags; if (!func || !func->config || !func->config->cdev || !func->config->cdev->gadget) return -EINVAL; DBG(func->config->cdev, "%s function wakeup\n", func->name); spin_lock_irqsave(&func->config->cdev->lock, flags); for (id = 0; id < MAX_CONFIG_INTERFACES; id++) if (func->config->interface[id] == func) break; if (id == MAX_CONFIG_INTERFACES) { ERROR(func->config->cdev, "Invalid function id:%d\n", id); ret = -EINVAL; goto err; } ret = usb_gadget_func_wakeup(func->config->cdev->gadget, id); if (ret == -EAGAIN) { DBG(func->config->cdev, "Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n", func->name ? func->name : ""); ret = 0; } else if (ret < 0 && ret != -ENOTSUPP) { ERROR(func->config->cdev, "Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n", func->name ? func->name : "", ret); } err: spin_unlock_irqrestore(&func->config->cdev->lock, flags); return ret; } EXPORT_SYMBOL(usb_func_wakeup); #endif static u8 encode_bMaxPower(enum usb_device_speed speed, struct usb_configuration *c) { Loading
drivers/usb/gadget/function/f_cdev.c +18 −17 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2011, 2013-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2011, 2013-2020, The Linux Foundation. All rights reserved. * Linux Foundation chooses to take subject only to the GPLv2 license terms, * and distributes only under these terms. * Loading Loading @@ -107,6 +107,10 @@ struct f_cdev { /* current USB RX buffer */ u8 *current_rx_buf; /* function suspend status */ bool func_is_suspended; bool func_wakeup_allowed; struct cserial port_usb; #define ACM_CTRL_DTR 0x01 Loading Loading @@ -514,28 +518,25 @@ static int usb_cser_set_alt(struct usb_function *f, unsigned int intf, static int usb_cser_func_suspend(struct usb_function *f, u8 options) { bool func_wakeup_allowed; struct f_cdev *port = func_to_port(f); func_wakeup_allowed = ((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0); port->func_wakeup_allowed = !!(options & (USB_INTRF_FUNC_SUSPEND_RW >> 8)); port->func_is_suspended = options & (USB_INTRF_FUNC_SUSPEND_LP >> 8); 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; #ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED struct f_cdev *port = func_to_port(f); return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) | (1 << FUNC_WAKEUP_CAPABLE_SHIFT); return (port->func_wakeup_allowed ? USB_INTRF_STAT_FUNC_RW : 0) | USB_INTRF_STAT_FUNC_RW_CAP; #else return 0; #endif } static void usb_cser_disable(struct usb_function *f) Loading Loading @@ -1584,8 +1585,8 @@ static ssize_t cser_rw_write(struct file *file, const char __user *ubuf, 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) { if (gadget->speed >= USB_SPEED_SUPER && port->func_is_suspended) { pr_debug("Calling usb_func_wakeup\n"); ret = usb_func_wakeup(func); } else { Loading
drivers/usb/gadget/function/f_gsi.c +34 −26 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #include <linux/module.h> Loading Loading @@ -34,13 +34,11 @@ static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt); static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f) { bool remote_wakeup_allowed; bool remote_wakeup_allowed = true; struct f_gsi *gsi = func_to_gsi(f); if (f->config->cdev->gadget->speed >= USB_SPEED_SUPER) remote_wakeup_allowed = f->func_wakeup_allowed; else remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup; remote_wakeup_allowed = gsi->func_wakeup_allowed; log_event_dbg("%s: remote_wakeup_allowed:%s", __func__, (remote_wakeup_allowed ? "true" : "false")); Loading Loading @@ -156,7 +154,7 @@ static int gsi_wakeup_host(struct f_gsi *gsi) * allowed to do so by the host. This is done in order to support non * fully USB 3.0 compatible hosts. */ if ((gadget->speed >= USB_SPEED_SUPER) && (func->func_is_suspended)) { if ((gadget->speed >= USB_SPEED_SUPER) && (gsi->func_is_suspended)) { log_event_dbg("%s: Calling usb_func_wakeup", __func__); ret = usb_func_wakeup(func); } else { Loading Loading @@ -730,7 +728,7 @@ static int ipa_suspend_work_handler(struct gsi_data_port *d_port) struct f_gsi *gsi = d_port_to_gsi(d_port); struct usb_function *f = &gsi->function; f_suspend = f->func_wakeup_allowed; f_suspend = gsi->func_wakeup_allowed; log_event_dbg("%s: f_suspend:%d", __func__, f_suspend); if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend, Loading Loading @@ -1377,8 +1375,7 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf, return -ECONNRESET; } if (gsi->function.func_is_suspended && !gsi->function.func_wakeup_allowed) { if (gsi->func_is_suspended && !gsi->func_wakeup_allowed) { c_port->cpkt_drop_cnt++; log_event_err("drop ctrl pkt of len %zu", count); return -ENOTSUPP; Loading Loading @@ -1792,9 +1789,17 @@ static int queue_notification_request(struct f_gsi *gsi) int ret; unsigned long flags; ret = usb_func_ep_queue(&gsi->function, gsi->c_port.notify, if (!gsi->func_is_suspended) { ret = usb_ep_queue(gsi->c_port.notify, gsi->c_port.notify_req, GFP_ATOMIC); if (ret < 0) { } else { if (gsi->func_wakeup_allowed) ret = usb_func_wakeup(&gsi->function); else ret = -EOPNOTSUPP; } if (ret < 0 || gsi->func_is_suspended) { spin_lock_irqsave(&gsi->c_port.lock, flags); gsi->c_port.notify_req_queued = false; spin_unlock_irqrestore(&gsi->c_port.lock, flags); Loading Loading @@ -2435,7 +2440,7 @@ static void gsi_suspend(struct usb_function *f) struct f_gsi *gsi = func_to_gsi(f); /* Check if function is already suspended in gsi_func_suspend() */ if (f->func_is_suspended) { if (gsi->func_is_suspended) { log_event_dbg("%s: func already suspended, return\n", __func__); return; } Loading @@ -2460,7 +2465,7 @@ static void gsi_resume(struct usb_function *f) * canceled. In this case resume is done by a Function Resume request. */ if ((cdev->gadget->speed >= USB_SPEED_SUPER) && f->func_is_suspended) gsi->func_is_suspended) return; if (gsi->c_port.notify && !gsi->c_port.notify->desc) Loading Loading @@ -2488,10 +2493,14 @@ static void gsi_resume(struct usb_function *f) static int gsi_get_status(struct usb_function *f) { unsigned int remote_wakeup_en_status = f->func_wakeup_allowed ? 1 : 0; #ifdef CONFIG_USB_FUNC_WAKEUP_SUPPORTED struct f_gsi *gsi = func_to_gsi(f); return (remote_wakeup_en_status << FUNC_WAKEUP_ENABLE_SHIFT) | (1 << FUNC_WAKEUP_CAPABLE_SHIFT); return (gsi->func_wakeup_allowed ? USB_INTRF_STAT_FUNC_RW : 0) | USB_INTRF_STAT_FUNC_RW_CAP; #else return 0; #endif } static int gsi_func_suspend(struct usb_function *f, u8 options) Loading @@ -2502,21 +2511,20 @@ static int gsi_func_suspend(struct usb_function *f, u8 options) log_event_dbg("func susp %u cmd for %s", options, f->name ? f->name : ""); func_wakeup_allowed = ((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0); func_wakeup_allowed = !!(options & (USB_INTRF_FUNC_SUSPEND_RW >> 8)); if (options & FUNC_SUSPEND_OPT_SUSP_MASK) { f->func_wakeup_allowed = func_wakeup_allowed; if (!f->func_is_suspended) { if (options & (USB_INTRF_FUNC_SUSPEND_LP >> 8)) { gsi->func_wakeup_allowed = func_wakeup_allowed; if (!gsi->func_is_suspended) { gsi_suspend(f); f->func_is_suspended = true; gsi->func_is_suspended = true; } } else { if (f->func_is_suspended) { f->func_is_suspended = false; if (gsi->func_is_suspended) { gsi->func_is_suspended = false; gsi_resume(f); } f->func_wakeup_allowed = func_wakeup_allowed; gsi->func_wakeup_allowed = func_wakeup_allowed; } return 0; Loading
drivers/usb/gadget/function/f_gsi.h +5 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #ifndef _F_GSI_H Loading Loading @@ -269,6 +269,10 @@ struct f_gsi { bool data_interface_up; enum rndis_class_id rndis_id; /* function suspend status */ bool func_is_suspended; bool func_wakeup_allowed; const struct usb_endpoint_descriptor *in_ep_desc_backup; const struct usb_endpoint_descriptor *out_ep_desc_backup; Loading