[RFC/PATCH 3/3] usb:gadget: SuperSpeed function power management testing support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux