1. Fix dummy_hcd to let the function handle GET_STATUS(Interface) request. 2. Fix a bug in f_loopback Gadget which updated the bmAttribute of f_sourcesink instead of f_loopback. 3. Update f_sourcesink Gadget to support function suspend & wakeup. This is done by adding get_status() & func_suspend() handlers. The current status of the function is controlable via debug FS: (wakeup capable, wakeup enabled, suspended). Signed-off-by: Amit Blay <ablay@xxxxxxxxxxxxxx> --- drivers/usb/gadget/dummy_hcd.c | 3 +- drivers/usb/gadget/f_loopback.c | 2 +- drivers/usb/gadget/f_sourcesink.c | 162 +++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index e755a9d..7f6a2d8 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -1496,7 +1496,8 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb, Dev_InRequest) { buf[0] = (u8)dum->devstatus; } else - buf[0] = 0; + /* Let the function handle this */ + break; } if (urb->transfer_buffer_length > 1) buf[1] = 0; diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index ca660d4..75bac6d 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -427,7 +427,7 @@ int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume) /* support autoresume for remote wakeup testing */ if (autoresume) - sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; /* support OTG systems */ if (gadget_is_otg(cdev->gadget)) { diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index 5247f07..5c5da19 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -59,6 +59,12 @@ struct f_sourcesink { struct usb_ep *in_ep; struct usb_ep *out_ep; + + /* Function Power Management */ + bool func_suspended; + bool func_wakeup_capable; + bool func_wakeup_enabled; + struct device dev; }; static inline struct f_sourcesink *func_to_ss(struct usb_function *f) @@ -191,6 +197,79 @@ static struct usb_gadget_strings *sourcesink_strings[] = { NULL, }; +/*************************** DEVICE ATTRIBUTES ***************************/ + +static ssize_t f_sourcesink_show_func_suspend(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f_sourcesink *ss = container_of(dev, struct f_sourcesink, + dev); + return sprintf(buf, "%d\n", ss->func_suspended); +} + +static ssize_t f_sourcesink_show_func_wakeup_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f_sourcesink *ss = container_of(dev, struct f_sourcesink, + dev); + return sprintf(buf, "%d\n", ss->func_wakeup_enabled); +} + +static ssize_t f_sourcesink_show_func_wakeup_capable(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct f_sourcesink *ss = container_of(dev, struct f_sourcesink, + dev); + return sprintf(buf, "%d\n", ss->func_wakeup_capable); +} + +static ssize_t f_sourcesink_store_func_wakeup_capable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct f_sourcesink *ss = container_of(dev, struct f_sourcesink, dev); + unsigned long func_wakeup_capable; + + /* Allows changing function wakeup capable field from the file system */ + if (strict_strtoul(buf, 2, &func_wakeup_capable)) + return -EINVAL; + ss->func_wakeup_capable = (bool)func_wakeup_capable; + return count; +} + +static ssize_t f_sourcesink_store_func_wakeup_trigger(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct f_sourcesink *ss = container_of(dev, struct f_sourcesink, dev); + + /* Allows trigerring function wakeup from the file system */ + if (!ss->func_wakeup_capable || !ss->func_wakeup_enabled) + return -EINVAL; + + if (usb_gadget_wakeup(ss->function.config->cdev->gadget, + source_sink_intf.bInterfaceNumber) < 0) + return -EINVAL; + return count; +} + +static DEVICE_ATTR(func_suspend, 0444, f_sourcesink_show_func_suspend, NULL); +static DEVICE_ATTR(func_wakeup_enabled, 0444, + f_sourcesink_show_func_wakeup_enabled, NULL); +static DEVICE_ATTR(func_wakeup_capable, 0666, + f_sourcesink_show_func_wakeup_capable, + f_sourcesink_store_func_wakeup_capable); +static DEVICE_ATTR(func_wakeup_trigger, 0666, NULL, + f_sourcesink_store_func_wakeup_trigger); + +/*-------------------------------------------------------------------------*/ + +static void sourcesink_release(struct device *dev) +{ + /* Nothing needs to be done */ +} + /*-------------------------------------------------------------------------*/ static int __init @@ -199,6 +278,7 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) struct usb_composite_dev *cdev = c->cdev; struct f_sourcesink *ss = func_to_ss(f); int id; + int result = 0; /* allocate interface ID(s) */ id = usb_interface_id(c, f); @@ -243,12 +323,66 @@ autoconf_fail: (gadget_is_superspeed(c->cdev->gadget) ? "super" : (gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")), f->name, ss->in_ep->name, ss->out_ep->name); + + ss->dev.parent = &cdev->gadget->dev; + ss->dev.release = sourcesink_release; + dev_set_name(&ss->dev, "sourcesink"); + + result = device_register(&ss->dev); + if (result) { + ERROR(cdev, "failed to register: %d\n", result); + goto err_device_register; + } + + result = device_create_file(&ss->dev, &dev_attr_func_suspend); + if (result) { + ERROR(cdev, "device_create_file failed\n", result); + goto err_func_suspend_file; + } + + result = device_create_file(&ss->dev, &dev_attr_func_wakeup_enabled); + if (result) { + ERROR(cdev, "device_create_file failed\n", result); + goto err_func_wake_enabled_file; + } + + result = device_create_file(&ss->dev, &dev_attr_func_wakeup_capable); + if (result) { + ERROR(cdev, "device_create_file failed\n", result); + goto err_func_wake_capable_file; + } + + result = device_create_file(&ss->dev, &dev_attr_func_wakeup_trigger); + if (result) { + ERROR(cdev, "device_create_file failed\n", result); + goto err_func_wake_trigger_file; + } + return 0; + +err_func_wake_trigger_file: + device_remove_file(&ss->dev, &dev_attr_func_wakeup_capable); +err_func_wake_capable_file: + device_remove_file(&ss->dev, &dev_attr_func_wakeup_enabled); +err_func_wake_enabled_file: + device_remove_file(&ss->dev, &dev_attr_func_suspend); +err_func_suspend_file: + device_unregister(&ss->dev); +err_device_register: + return -ENODEV; } static void sourcesink_unbind(struct usb_configuration *c, struct usb_function *f) { + struct f_sourcesink *ss = func_to_ss(f); + + device_remove_file(&ss->dev, &dev_attr_func_suspend); + device_remove_file(&ss->dev, &dev_attr_func_wakeup_capable); + device_remove_file(&ss->dev, &dev_attr_func_wakeup_enabled); + device_remove_file(&ss->dev, &dev_attr_func_wakeup_trigger); + device_unregister(&ss->dev); + kfree(func_to_ss(f)); } @@ -383,6 +517,32 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in) return status; } +static int sourcesink_func_suspend(struct usb_function *f, + u8 suspend_opt) +{ + struct f_sourcesink *ss = func_to_ss(f); + struct usb_composite_dev *cdev; + + cdev = ss->function.config->cdev; + + /* Parse suspend options */ + ss->func_suspended = suspend_opt & USB_INTR_FUNC_SUSPEND_SUSP; + ss->func_wakeup_enabled = suspend_opt & USB_INTR_FUNC_SUSPEND_RWAKE_EN; + + return 1; +} + +static int sourcesink_get_status(struct usb_function *f) +{ + struct f_sourcesink *ss = func_to_ss(f); + struct usb_composite_dev *cdev; + + cdev = ss->function.config->cdev; + + return ss->func_wakeup_capable << USB_INTR_STAT_RWAKE_CAP + | (ss->func_wakeup_enabled << USB_INTR_STAT_RWAKE_EN); +} + static void disable_source_sink(struct f_sourcesink *ss) { struct usb_composite_dev *cdev; @@ -474,6 +634,8 @@ static int __init sourcesink_bind_config(struct usb_configuration *c) ss->function.unbind = sourcesink_unbind; ss->function.set_alt = sourcesink_set_alt; ss->function.disable = sourcesink_disable; + ss->function.func_suspend = sourcesink_func_suspend; + ss->function.get_status = sourcesink_get_status; status = usb_add_function(c, &ss->function); if (status) -- 1.7.3.3 -- Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum. -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html