Hi all, The patch format still have some problem since I copied it from the html mail. I fix it in this mail. :) Aixiong. >From 636a5ed9fbdab1a90945b1555ae765fc8d0232e6 Mon Sep 17 00:00:00 2001 From: Li Aixiong <aixiong.li@xxxxxxxxx> Date: Fri, 7 Nov 2014 12:53:01 +0800 Subject: [PATCH] USB: enable all functions remote wakeup for USB3 composite device In current usb driver, it only enable remote wakeup for the first function of USB3 device. It is found some USB3 composite devices cannot trigger remote wakeup in the 2nd function, 3rd function etc. since remote wakeup not enabled for those functions. This patch enable remote wakeup for all functions. The composite device may or may not use the associate interface. If associated interface is used, enable remote wakeup for the first interface of function is enough. For CDC device, only enable the CDC-control interfaces. For other multi-interface device, enable every interface. Change-Id: Ifa9c1783e807868cecd1c0978b6441ea360b8d55 Signed-off-by: Li Aixiong <aixiong.li@xxxxxxxxx> --- drivers/usb/core/hub.c | 133 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 115 insertions(+), 18 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1dcf9a5..8efb1da 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2898,30 +2898,91 @@ void usb_enable_ltm(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_enable_ltm); +static struct usb_interface_assoc_descriptor *is_assoc_interface( + struct usb_device *dev, + struct usb_host_config *config, + u8 inum) +{ + struct usb_interface_assoc_descriptor *intf_assoc; + int first_intf; + int last_intf; + int i; + + for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) { + intf_assoc = config->intf_assoc[i]; + if (intf_assoc->bInterfaceCount == 0) + continue; + + first_intf = intf_assoc->bFirstInterface; + last_intf = first_intf + (intf_assoc->bInterfaceCount - 1); + if (inum >= first_intf && inum <= last_intf) + return intf_assoc; + } + + return NULL; +} + + /* * usb_enable_remote_wakeup - enable remote wakeup for a device * @udev: target device * * For USB-2 devices: Set the device's remote wakeup feature. * - * For USB-3 devices: Assume there's only one function on the device and - * enable remote wake for the first interface. FIXME if the interface - * association descriptor shows there's more than one function. + * For USB-3 devices: enable remote wakeup for all functions. */ static int usb_enable_remote_wakeup(struct usb_device *udev) { + struct usb_host_config *config = udev->actconfig; + struct usb_interface_assoc_descriptor *intf_assoc; + struct usb_interface *intf; + int intf_num; + int intf_class; + int i; + int ret = 0; + if (udev->speed < USB_SPEED_SUPER) return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - else - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE, - USB_INTRF_FUNC_SUSPEND, - USB_INTRF_FUNC_SUSPEND_RW | - USB_INTRF_FUNC_SUSPEND_LP, - NULL, 0, USB_CTRL_SET_TIMEOUT); + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + intf = config->interface[i]; + intf_class = intf->cur_altsetting->desc.bInterfaceClass; + intf_num = intf->cur_altsetting->desc.bInterfaceNumber; + + if (intf_class == USB_CLASS_CDC_DATA) + continue; + + intf_assoc = is_assoc_interface(udev, config, intf_num); + if (intf_assoc && intf_num != intf_assoc->bFirstInterface) + continue; + + /* the lower byte of wIndex shall be set to the first + * interface that is part of that function (USB 3.0 spec + * section 9.4.9) + */ + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, + USB_INTRF_FUNC_SUSPEND_RW | + USB_INTRF_FUNC_SUSPEND_LP | + intf_num, + NULL, 0, USB_CTRL_SET_TIMEOUT); + + if (ret) { + dev_warn(&udev->dev, + "Can't enable remote wakeup interface %d\n", + intf_num); + + return ret; + } + + dev_dbg(&udev->dev, "interface %d remote wakeup enabled\n", + intf_num); + } + return 0; } /* @@ -2930,22 +2991,58 @@ static int usb_enable_remote_wakeup(struct usb_device *udev) * * For USB-2 devices: Clear the device's remote wakeup feature. * - * For USB-3 devices: Assume there's only one function on the device and - * disable remote wake for the first interface. FIXME if the interface - * association descriptor shows there's more than one function. + * For USB-3 devices: clear remote wakeup for all functions */ static int usb_disable_remote_wakeup(struct usb_device *udev) { + struct usb_host_config *config = udev->actconfig; + struct usb_interface_assoc_descriptor *intf_assoc; + struct usb_interface *intf; + int intf_num; + int intf_class; + int i; + int ret = 0; + if (udev->speed < USB_SPEED_SUPER) return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - else - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE, - USB_INTRF_FUNC_SUSPEND, 0, NULL, 0, - USB_CTRL_SET_TIMEOUT); + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + intf = config->interface[i]; + intf_class = intf->cur_altsetting->desc.bInterfaceClass; + intf_num = intf->cur_altsetting->desc.bInterfaceNumber; + + if (intf_class == USB_CLASS_CDC_DATA) + continue; + + intf_assoc = is_assoc_interface(udev, config, intf_num); + if (intf_assoc && intf_num != intf_assoc->bFirstInterface) + continue; + + /* the lower byte of wIndex shall be set to the first + * interface that is part of that function (USB 3.0 spec + * section 9.4.9) + */ + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, + intf_num, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + + if (ret) { + dev_warn(&udev->dev, + "Can't disable remote wakeup interface %d\n", + intf_num); + return ret; + } + dev_dbg(&udev->dev, "interface %d remotewakeup disabled\n", + intf_num); + + } + return 0; } /* Count of wakeup-enabled devices at or below udev */ -- 1.7.9.5 -----Original Message----- From: Li, Aixiong Sent: Sunday, November 30, 2014 11:44 AM To: stern@xxxxxxxxxxxxxxxxxxx; gregkh@xxxxxxxxxxxxxxxxxxx Cc: linux-usb@xxxxxxxxxxxxxxx; Li, Aixiong Subject: [PATCH] USB: enable all functions remote wakeup for USB3 composite device Hi USB maintainers, I found one problem for remote wakeup of USB3 composite device when I tested remote wakeup functionality for Intel LTE modem. This LTE modem is USB3 composite device, it cannot generate remote wakeup except the 1st function. After check the USB 3.0 spec and current LINUX driver, I wrote the following patch. After I applied this patch, this LTE modem can generate remote wakeup for all functions. So please help to review it. (Since my last email is rejected due to html format, I re-send it using plain text) Regards. Aixiong Li >From 636a5ed9fbdab1a90945b1555ae765fc8d0232e6 Mon Sep 17 00:00:00 2001 From: Li Aixiong <aixiong.li@xxxxxxxxx> Date: Fri, 7 Nov 2014 12:53:01 +0800 Subject: [PATCH] USB: enable all functions remote wakeup for USB3 composite device In current usb driver, it only enable remote wakeup for the first function of USB3 device. It is found some USB3 composite devices cannot trigger remote wakeup in the 2nd function, 3rd function etc., since remote wakeup not enabled for those functions. This patch enable remote wakeup for all functions. The composite device may or may not use the associate interface. If associated interface is used, enable remote wakeup for the first interface of function is enough. For CDC device, only enable the CDC-control interfaces. For other multi-interface device, enable every interface. Change-Id: Ifa9c1783e807868cecd1c0978b6441ea360b8d55 Signed-off-by: Li Aixiong <aixiong.li@xxxxxxxxx> --- drivers/usb/core/hub.c | 133 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 115 insertions(+), 18 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1dcf9a5..8efb1da 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2898,30 +2898,91 @@ void usb_enable_ltm(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_enable_ltm); +static struct usb_interface_assoc_descriptor *is_assoc_interface( + struct usb_device *dev, + struct usb_host_config *config, + u8 inum) { + struct usb_interface_assoc_descriptor *intf_assoc; + int first_intf; + int last_intf; + int i; + + for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) { + intf_assoc = config->intf_assoc[i]; + if (intf_assoc->bInterfaceCount == 0) + continue; + + first_intf = intf_assoc->bFirstInterface; + last_intf = first_intf + (intf_assoc->bInterfaceCount - 1); + if (inum >= first_intf && inum <= last_intf) + return intf_assoc; + } + + return NULL; +} + + /* * usb_enable_remote_wakeup - enable remote wakeup for a device * @udev: target device * * For USB-2 devices: Set the device's remote wakeup feature. * - * For USB-3 devices: Assume there's only one function on the device and - * enable remote wake for the first interface. FIXME if the interface - * association descriptor shows there's more than one function. + * For USB-3 devices: enable remote wakeup for all functions. */ static int usb_enable_remote_wakeup(struct usb_device *udev) { + struct usb_host_config *config = udev->actconfig; + struct usb_interface_assoc_descriptor *intf_assoc; + struct usb_interface *intf; + int intf_num; + int intf_class; + int i; + int ret = 0; + if (udev->speed < USB_SPEED_SUPER) return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - else - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE, - USB_INTRF_FUNC_SUSPEND, - USB_INTRF_FUNC_SUSPEND_RW | - USB_INTRF_FUNC_SUSPEND_LP, - NULL, 0, USB_CTRL_SET_TIMEOUT); + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + intf = config->interface[i]; + intf_class = intf->cur_altsetting->desc.bInterfaceClass; + intf_num = intf->cur_altsetting->desc.bInterfaceNumber; + + if (intf_class == USB_CLASS_CDC_DATA) + continue; + + intf_assoc = is_assoc_interface(udev, config, intf_num); + if (intf_assoc && intf_num != intf_assoc->bFirstInterface) + continue; + + /* the lower byte of wIndex shall be set to the first + * interface that is part of that function (USB 3.0 spec + * section 9.4.9) + */ + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, + USB_INTRF_FUNC_SUSPEND_RW | + USB_INTRF_FUNC_SUSPEND_LP | + intf_num, + NULL, 0, USB_CTRL_SET_TIMEOUT); + + if (ret) { + dev_warn(&udev->dev, + "Can't enable remote wakeup interface %d\n", + intf_num); + + return ret; + } + + dev_dbg(&udev->dev, "interface %d remote wakeup enabled\n", + intf_num); + } + return 0; } /* @@ -2930,22 +2991,58 @@ static int usb_enable_remote_wakeup(struct usb_device *udev) * * For USB-2 devices: Clear the device's remote wakeup feature. * - * For USB-3 devices: Assume there's only one function on the device and - * disable remote wake for the first interface. FIXME if the interface - * association descriptor shows there's more than one function. + * For USB-3 devices: clear remote wakeup for all functions */ static int usb_disable_remote_wakeup(struct usb_device *udev) { + struct usb_host_config *config = udev->actconfig; + struct usb_interface_assoc_descriptor *intf_assoc; + struct usb_interface *intf; + int intf_num; + int intf_class; + int i; + int ret = 0; + if (udev->speed < USB_SPEED_SUPER) return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - else - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE, - USB_INTRF_FUNC_SUSPEND, 0, NULL, 0, - USB_CTRL_SET_TIMEOUT); + + for (i = 0; i < config->desc.bNumInterfaces; i++) { + intf = config->interface[i]; + intf_class = intf->cur_altsetting->desc.bInterfaceClass; + intf_num = intf->cur_altsetting->desc.bInterfaceNumber; + + if (intf_class == USB_CLASS_CDC_DATA) + continue; + + intf_assoc = is_assoc_interface(udev, config, intf_num); + if (intf_assoc && intf_num != intf_assoc->bFirstInterface) + continue; + + /* the lower byte of wIndex shall be set to the first + * interface that is part of that function (USB 3.0 spec + * section 9.4.9) + */ + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, + intf_num, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + + if (ret) { + dev_warn(&udev->dev, + "Can't disable remote wakeup interface %d\n", + intf_num); + return ret; + } + dev_dbg(&udev->dev, "interface %d remotewakeup disabled\n", + intf_num); + + } + return 0; } /* Count of wakeup-enabled devices at or below udev */ -- 1.7.9.5 -- 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