From 2d2bfb11f9b6cb70caaa179da131c7168d2b0a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=90=E6=9D=B0=E5=90=8C=E5=AD=A6?= <1220627966@qq.com> Date: Wed, 19 Jun 2024 03:04:18 -0700 Subject: [PATCH] Added support for USB remote wakeup --- linux_5.10/drivers/usb/dwc2/gadget.c | 73 +++++++++++++++++++ .../drivers/usb/gadget/function/f_hid.c | 18 +++++ .../drivers/usb/gadget/function/u_hid.h | 1 + linux_5.10/drivers/usb/gadget/legacy/hid.c | 2 +- linux_5.10/drivers/usb/gadget/udc/core.c | 2 + 5 files changed, 95 insertions(+), 1 deletion(-) diff --git a/linux_5.10/drivers/usb/dwc2/gadget.c b/linux_5.10/drivers/usb/dwc2/gadget.c index 31af40c6f..f62b1c1fb 100644 --- a/linux_5.10/drivers/usb/dwc2/gadget.c +++ b/linux_5.10/drivers/usb/dwc2/gadget.c @@ -4306,6 +4306,78 @@ static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) return 0; } +/** + * dwc2_hsotg_wakeup - send wakeup signal to the host + * @gadget: The usb gadget state + * + * If the gadget is in device mode and in the L1 or L2 state, + * it sends a wakeup signal to the host. + */ +static int dwc2_hsotg_wakeup(struct usb_gadget *gadget) +{ + struct dwc2_hsotg *hsotg = to_hsotg(gadget); + int ret = -1; + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + + if (!hsotg->remote_wakeup_allowed) { + dev_info(hsotg->dev, + "wakeup: signalling skipped: is not allowed by host\n"); + goto skip; + } + if (hsotg->lx_state != DWC2_L1 && hsotg->lx_state != DWC2_L2) { + dev_info(hsotg->dev, + "wakeup: signalling skipped: gadget not in L1/L2 state: %d\n", hsotg->lx_state); + goto skip; + } + if (!dwc2_is_device_mode(hsotg)) { + dev_info(hsotg->dev, + "wakeup: signalling skipped: gadget not in device mode\n"); + goto skip; + } + + /*if (hsotg->in_ppd) { + if (dwc2_exit_partial_power_down(hsotg, 1, true)) + dev_err(hsotg->dev, "wakeup: exit partial_power_down failed\n"); + call_gadget(hsotg, resume); + }*/ + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) { + u32 pcgctl; + + dev_info(hsotg->dev, "wakeup: exiting device clock gating\n"); + + /* Clear the Gate hclk. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Phy Clock bit. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + hsotg->bus_suspended = false; + } + + dev_info(hsotg->dev, "wakeup: sending signal to the host"); + + dwc2_set_bit(hsotg, DCTL, DCTL_RMTWKUPSIG); + mdelay(10); + dwc2_clear_bit(hsotg, DCTL, DCTL_RMTWKUPSIG); + + /* After the signalling, the USB core wakes up to L0 */ + call_gadget(hsotg, resume); + hsotg->lx_state = DWC2_L0; + + ret = 0; +skip: + spin_unlock_irqrestore(&hsotg->lock, flags); + return ret; +} + /** * dwc2_hsotg_ep_sethalt - set halt on a given endpoint * @ep: The endpoint to set halt. @@ -4688,6 +4760,7 @@ static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = { .pullup = dwc2_hsotg_pullup, .vbus_session = dwc2_hsotg_vbus_session, .vbus_draw = dwc2_hsotg_vbus_draw, + .wakeup = dwc2_hsotg_wakeup, }; /** diff --git a/linux_5.10/drivers/usb/gadget/function/f_hid.c b/linux_5.10/drivers/usb/gadget/function/f_hid.c index 1125f4715..c21f0e55e 100644 --- a/linux_5.10/drivers/usb/gadget/function/f_hid.c +++ b/linux_5.10/drivers/usb/gadget/function/f_hid.c @@ -44,6 +44,8 @@ struct f_hidg { unsigned short report_desc_length; char *report_desc; unsigned short report_length; + bool use_out_ep; + bool wakeup_on_write; /* recv report */ struct list_head completed_out_req; @@ -332,10 +334,23 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, size_t count, loff_t *offp) { struct f_hidg *hidg = file->private_data; + struct usb_composite_dev *cdev = hidg->func.config->cdev; struct usb_request *req; unsigned long flags; ssize_t status = -ENOMEM; + /* + * remote wakeup is allowed only when the corresponding bit + * in config descriptor is set and wakeup_on_write is enabled. + * FIXME: cdev->config can be NULLed on disconnect. + */ + if (hidg->wakeup_on_write /*&& cdev->config->bmAttributes & 0x20*/){ + usb_gadget_wakeup(cdev->gadget); + ERROR(hidg->func.config->cdev, + "usb_gadget_wakeup\n"); + } + + spin_lock_irqsave(&hidg->write_spinlock, flags); #define WRITE_COND (!hidg->write_pending) @@ -918,6 +933,7 @@ CONFIGFS_ATTR(f_hid_opts_, name) F_HID_OPT(subclass, 8, 255); F_HID_OPT(protocol, 8, 255); F_HID_OPT(report_length, 16, 65535); +F_HID_OPT(wakeup_on_write, 8, 1); static ssize_t f_hid_opts_report_desc_show(struct config_item *item, char *page) { @@ -976,6 +992,7 @@ CONFIGFS_ATTR_RO(f_hid_opts_, dev); static struct configfs_attribute *hid_attrs[] = { &f_hid_opts_attr_subclass, &f_hid_opts_attr_protocol, + &f_hid_opts_attr_wakeup_on_write, &f_hid_opts_attr_report_length, &f_hid_opts_attr_report_desc, &f_hid_opts_attr_dev, @@ -1106,6 +1123,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOMEM); } } + hidg->wakeup_on_write = opts->wakeup_on_write; mutex_unlock(&opts->lock); diff --git a/linux_5.10/drivers/usb/gadget/function/u_hid.h b/linux_5.10/drivers/usb/gadget/function/u_hid.h index 84e6da302..a3e2f6053 100644 --- a/linux_5.10/drivers/usb/gadget/function/u_hid.h +++ b/linux_5.10/drivers/usb/gadget/function/u_hid.h @@ -20,6 +20,7 @@ struct f_hid_opts { int minor; unsigned char subclass; unsigned char protocol; + unsigned char wakeup_on_write; unsigned short report_length; unsigned short report_desc_length; unsigned char *report_desc; diff --git a/linux_5.10/drivers/usb/gadget/legacy/hid.c b/linux_5.10/drivers/usb/gadget/legacy/hid.c index c4eda7fe7..79b461ccd 100644 --- a/linux_5.10/drivers/usb/gadget/legacy/hid.c +++ b/linux_5.10/drivers/usb/gadget/legacy/hid.c @@ -123,7 +123,7 @@ static struct usb_configuration config_driver = { .label = "HID Gadget", .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ - .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER | USB_CONFIG_ATT_WAKEUP, }; /****************************** Gadget Bind ******************************/ diff --git a/linux_5.10/drivers/usb/gadget/udc/core.c b/linux_5.10/drivers/usb/gadget/udc/core.c index accdd8fb2..44bef2486 100644 --- a/linux_5.10/drivers/usb/gadget/udc/core.c +++ b/linux_5.10/drivers/usb/gadget/udc/core.c @@ -499,6 +499,8 @@ int usb_gadget_wakeup(struct usb_gadget *gadget) { int ret = 0; + // printf("usb_gadget_wakeup \r\n"); + // dev_err(dev, "usb_gadget_wakeup\n"); if (!gadget->ops->wakeup) { ret = -EOPNOTSUPP; goto out;