Added support for USB remote wakeup

This commit is contained in:
某杰同学
2024-06-19 03:04:18 -07:00
parent a4c7ee1b6c
commit 2d2bfb11f9
5 changed files with 95 additions and 1 deletions

View File

@@ -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,
};
/**

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 ******************************/

View File

@@ -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;