Loading drivers/usb/core/message.c +169 −0 Original line number Original line Diff line number Diff line Loading @@ -1493,6 +1493,175 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) } } EXPORT_SYMBOL_GPL(usb_set_interface); EXPORT_SYMBOL_GPL(usb_set_interface); /** * usb_set_interface_timeout - Makes a particular alternate setting be current * and allows to set a timeout value for this control transfer. * @dev: the device whose interface is being updated * @interface: the interface being updated * @alternate: the setting being chosen. * @timeout: timeout value in ms (non-zero), before which this transfer should * complete. * Context: !in_interrupt () * * This is used to enable data transfers on interfaces that may not * be enabled by default. Not all devices support such configurability. * Only the driver bound to an interface may change its setting. * * Within any given configuration, each interface may have several * alternative settings. These are often used to control levels of * bandwidth consumption. For example, the default setting for a high * speed interrupt endpoint may not send more than 64 bytes per microframe, * while interrupt transfers of up to 3KBytes per microframe are legal. * Also, isochronous endpoints may never be part of an * interface's default setting. To access such bandwidth, alternate * interface settings must be made current. * * Note that in the Linux USB subsystem, bandwidth associated with * an endpoint in a given alternate setting is not reserved until an URB * is submitted that needs that bandwidth. Some other operating systems * allocate bandwidth early, when a configuration is chosen. * * This call is synchronous, and may not be used in an interrupt context. * Also, drivers must not change altsettings while urbs are scheduled for * endpoints in that interface; all such urbs must first be completed * (perhaps forced by unlinking). * * Return: Zero on success, or else the status code returned by the * underlying usb_control_msg() call. */ int usb_set_interface_timeout(struct usb_device *dev, int interface, int alternate, unsigned long timeout_ms) { struct usb_interface *iface; struct usb_host_interface *alt; struct usb_hcd *hcd = bus_to_hcd(dev->bus); int i, ret, manual = 0; unsigned int epaddr; unsigned int pipe; if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; iface = usb_ifnum_to_if(dev, interface); if (!iface) { dev_dbg(&dev->dev, "selecting invalid interface %d\n", interface); return -EINVAL; } if (iface->unregistering) return -ENODEV; alt = usb_altnum_to_altsetting(iface, alternate); if (!alt) { dev_warn(&dev->dev, "selecting invalid altsetting %d\n", alternate); return -EINVAL; } /* Make sure we have enough bandwidth for this alternate interface. * Remove the current alt setting and add the new alt setting. */ mutex_lock(hcd->bandwidth_mutex); /* Disable LPM, and re-enable it once the new alt setting is installed, * so that the xHCI driver can recalculate the U1/U2 timeouts. */ if (usb_disable_lpm(dev)) { dev_err(&iface->dev, "%s Failed to disable LPM\n", __func__); mutex_unlock(hcd->bandwidth_mutex); return -ENOMEM; } /* Changing alt-setting also frees any allocated streams */ for (i = 0; i < iface->cur_altsetting->desc.bNumEndpoints; i++) iface->cur_altsetting->endpoint[i].streams = 0; ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt); if (ret < 0) { dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n", alternate); usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); return ret; } if (dev->quirks & USB_QUIRK_NO_SET_INTF) ret = -EPIPE; else ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate, interface, NULL, 0, timeout_ms); /* 9.4.10 says devices don't need this and are free to STALL the * request if the interface only has one alternate setting. */ if (ret == -EPIPE && iface->num_altsetting == 1) { dev_dbg(&dev->dev, "manual set_interface for iface %d, alt %d\n", interface, alternate); manual = 1; } else if (ret < 0) { /* Re-instate the old alt setting */ usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting); usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); return ret; } mutex_unlock(hcd->bandwidth_mutex); /* FIXME drivers shouldn't need to replicate/bugfix the logic here * when they implement async or easily-killable versions of this or * other "should-be-internal" functions (like clear_halt). * should hcd+usbcore postprocess control requests? */ /* prevent submissions using previous endpoint settings */ if (iface->cur_altsetting != alt) { remove_intf_ep_devs(iface); usb_remove_sysfs_intf_files(iface); } usb_disable_interface(dev, iface, true); iface->cur_altsetting = alt; /* Now that the interface is installed, re-enable LPM. */ usb_unlocked_enable_lpm(dev); /* If the interface only has one altsetting and the device didn't * accept the request, we attempt to carry out the equivalent action * by manually clearing the HALT feature for each endpoint in the * new altsetting. */ if (manual) { for (i = 0; i < alt->desc.bNumEndpoints; i++) { epaddr = alt->endpoint[i].desc.bEndpointAddress; pipe = __create_pipe(dev, USB_ENDPOINT_NUMBER_MASK & epaddr) | (usb_endpoint_out(epaddr) ? USB_DIR_OUT : USB_DIR_IN); usb_clear_halt(dev, pipe); } } /* 9.1.1.5: reset toggles for all endpoints in the new altsetting * * Note: * Despite EP0 is always present in all interfaces/AS, the list of * endpoints from the descriptor does not contain EP0. Due to its * omnipresence one might expect EP0 being considered "affected" by * any SetInterface request and hence assume toggles need to be reset. * However, EP0 toggles are re-synced for every individual transfer * during the SETUP stage - hence EP0 toggles are "don't care" here. * (Likewise, EP0 never "halts" on well designed devices.) */ usb_enable_interface(dev, iface, true); if (device_is_registered(&iface->dev)) { usb_create_sysfs_intf_files(iface); create_intf_ep_devs(iface); } return 0; } EXPORT_SYMBOL(usb_set_interface_timeout); /** /** * usb_reset_configuration - lightweight device reset * usb_reset_configuration - lightweight device reset * @dev: the device whose configuration is being reset * @dev: the device whose configuration is being reset Loading include/linux/usb.h +2 −0 Original line number Original line Diff line number Diff line Loading @@ -1832,6 +1832,8 @@ extern int usb_string(struct usb_device *dev, int index, extern int usb_clear_halt(struct usb_device *dev, int pipe); extern int usb_clear_halt(struct usb_device *dev, int pipe); extern int usb_reset_configuration(struct usb_device *dev); extern int usb_reset_configuration(struct usb_device *dev); extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); extern int usb_set_interface_timeout(struct usb_device *dev, int ifnum, int alternate, unsigned long timeout); extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr); extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr); /* this request isn't really synchronous, but it belongs with the others */ /* this request isn't really synchronous, but it belongs with the others */ Loading sound/usb/pcm.c +8 −3 Original line number Original line Diff line number Diff line Loading @@ -39,6 +39,8 @@ #define SUBSTREAM_FLAG_DATA_EP_STARTED 0 #define SUBSTREAM_FLAG_DATA_EP_STARTED 0 #define SUBSTREAM_FLAG_SYNC_EP_STARTED 1 #define SUBSTREAM_FLAG_SYNC_EP_STARTED 1 #define MAX_SETALT_TIMEOUT_MS 1000 /* return the estimated delay based on USB frame counters */ /* return the estimated delay based on USB frame counters */ snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, unsigned int rate) unsigned int rate) Loading Loading @@ -582,7 +584,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* close the old interface */ /* close the old interface */ if (subs->interface >= 0 && subs->interface != fmt->iface) { if (subs->interface >= 0 && subs->interface != fmt->iface) { if (!subs->stream->chip->keep_iface) { if (!subs->stream->chip->keep_iface) { err = usb_set_interface(subs->dev, subs->interface, 0); err = usb_set_interface_timeout(subs->dev, subs->interface, 0, MAX_SETALT_TIMEOUT_MS); if (err < 0) { if (err < 0) { dev_err(&dev->dev, dev_err(&dev->dev, "%d:%d: return to setting 0 failed (%d)\n", "%d:%d: return to setting 0 failed (%d)\n", Loading @@ -600,7 +603,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) if (err < 0) if (err < 0) return -EIO; return -EIO; err = usb_set_interface(dev, fmt->iface, fmt->altsetting); err = usb_set_interface_timeout(dev, fmt->iface, fmt->altsetting, MAX_SETALT_TIMEOUT_MS); if (err < 0) { if (err < 0) { dev_err(&dev->dev, dev_err(&dev->dev, "%d:%d: usb_set_interface failed (%d)\n", "%d:%d: usb_set_interface failed (%d)\n", Loading Loading @@ -646,7 +650,8 @@ int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, if (!enable) { if (!enable) { if (subs->interface >= 0) { if (subs->interface >= 0) { usb_set_interface(subs->dev, subs->interface, 0); usb_set_interface_timeout(subs->dev, subs->interface, 0, MAX_SETALT_TIMEOUT_MS); subs->altset_idx = 0; subs->altset_idx = 0; subs->interface = -1; subs->interface = -1; subs->cur_audiofmt = NULL; subs->cur_audiofmt = NULL; Loading Loading
drivers/usb/core/message.c +169 −0 Original line number Original line Diff line number Diff line Loading @@ -1493,6 +1493,175 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) } } EXPORT_SYMBOL_GPL(usb_set_interface); EXPORT_SYMBOL_GPL(usb_set_interface); /** * usb_set_interface_timeout - Makes a particular alternate setting be current * and allows to set a timeout value for this control transfer. * @dev: the device whose interface is being updated * @interface: the interface being updated * @alternate: the setting being chosen. * @timeout: timeout value in ms (non-zero), before which this transfer should * complete. * Context: !in_interrupt () * * This is used to enable data transfers on interfaces that may not * be enabled by default. Not all devices support such configurability. * Only the driver bound to an interface may change its setting. * * Within any given configuration, each interface may have several * alternative settings. These are often used to control levels of * bandwidth consumption. For example, the default setting for a high * speed interrupt endpoint may not send more than 64 bytes per microframe, * while interrupt transfers of up to 3KBytes per microframe are legal. * Also, isochronous endpoints may never be part of an * interface's default setting. To access such bandwidth, alternate * interface settings must be made current. * * Note that in the Linux USB subsystem, bandwidth associated with * an endpoint in a given alternate setting is not reserved until an URB * is submitted that needs that bandwidth. Some other operating systems * allocate bandwidth early, when a configuration is chosen. * * This call is synchronous, and may not be used in an interrupt context. * Also, drivers must not change altsettings while urbs are scheduled for * endpoints in that interface; all such urbs must first be completed * (perhaps forced by unlinking). * * Return: Zero on success, or else the status code returned by the * underlying usb_control_msg() call. */ int usb_set_interface_timeout(struct usb_device *dev, int interface, int alternate, unsigned long timeout_ms) { struct usb_interface *iface; struct usb_host_interface *alt; struct usb_hcd *hcd = bus_to_hcd(dev->bus); int i, ret, manual = 0; unsigned int epaddr; unsigned int pipe; if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; iface = usb_ifnum_to_if(dev, interface); if (!iface) { dev_dbg(&dev->dev, "selecting invalid interface %d\n", interface); return -EINVAL; } if (iface->unregistering) return -ENODEV; alt = usb_altnum_to_altsetting(iface, alternate); if (!alt) { dev_warn(&dev->dev, "selecting invalid altsetting %d\n", alternate); return -EINVAL; } /* Make sure we have enough bandwidth for this alternate interface. * Remove the current alt setting and add the new alt setting. */ mutex_lock(hcd->bandwidth_mutex); /* Disable LPM, and re-enable it once the new alt setting is installed, * so that the xHCI driver can recalculate the U1/U2 timeouts. */ if (usb_disable_lpm(dev)) { dev_err(&iface->dev, "%s Failed to disable LPM\n", __func__); mutex_unlock(hcd->bandwidth_mutex); return -ENOMEM; } /* Changing alt-setting also frees any allocated streams */ for (i = 0; i < iface->cur_altsetting->desc.bNumEndpoints; i++) iface->cur_altsetting->endpoint[i].streams = 0; ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt); if (ret < 0) { dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n", alternate); usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); return ret; } if (dev->quirks & USB_QUIRK_NO_SET_INTF) ret = -EPIPE; else ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate, interface, NULL, 0, timeout_ms); /* 9.4.10 says devices don't need this and are free to STALL the * request if the interface only has one alternate setting. */ if (ret == -EPIPE && iface->num_altsetting == 1) { dev_dbg(&dev->dev, "manual set_interface for iface %d, alt %d\n", interface, alternate); manual = 1; } else if (ret < 0) { /* Re-instate the old alt setting */ usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting); usb_enable_lpm(dev); mutex_unlock(hcd->bandwidth_mutex); return ret; } mutex_unlock(hcd->bandwidth_mutex); /* FIXME drivers shouldn't need to replicate/bugfix the logic here * when they implement async or easily-killable versions of this or * other "should-be-internal" functions (like clear_halt). * should hcd+usbcore postprocess control requests? */ /* prevent submissions using previous endpoint settings */ if (iface->cur_altsetting != alt) { remove_intf_ep_devs(iface); usb_remove_sysfs_intf_files(iface); } usb_disable_interface(dev, iface, true); iface->cur_altsetting = alt; /* Now that the interface is installed, re-enable LPM. */ usb_unlocked_enable_lpm(dev); /* If the interface only has one altsetting and the device didn't * accept the request, we attempt to carry out the equivalent action * by manually clearing the HALT feature for each endpoint in the * new altsetting. */ if (manual) { for (i = 0; i < alt->desc.bNumEndpoints; i++) { epaddr = alt->endpoint[i].desc.bEndpointAddress; pipe = __create_pipe(dev, USB_ENDPOINT_NUMBER_MASK & epaddr) | (usb_endpoint_out(epaddr) ? USB_DIR_OUT : USB_DIR_IN); usb_clear_halt(dev, pipe); } } /* 9.1.1.5: reset toggles for all endpoints in the new altsetting * * Note: * Despite EP0 is always present in all interfaces/AS, the list of * endpoints from the descriptor does not contain EP0. Due to its * omnipresence one might expect EP0 being considered "affected" by * any SetInterface request and hence assume toggles need to be reset. * However, EP0 toggles are re-synced for every individual transfer * during the SETUP stage - hence EP0 toggles are "don't care" here. * (Likewise, EP0 never "halts" on well designed devices.) */ usb_enable_interface(dev, iface, true); if (device_is_registered(&iface->dev)) { usb_create_sysfs_intf_files(iface); create_intf_ep_devs(iface); } return 0; } EXPORT_SYMBOL(usb_set_interface_timeout); /** /** * usb_reset_configuration - lightweight device reset * usb_reset_configuration - lightweight device reset * @dev: the device whose configuration is being reset * @dev: the device whose configuration is being reset Loading
include/linux/usb.h +2 −0 Original line number Original line Diff line number Diff line Loading @@ -1832,6 +1832,8 @@ extern int usb_string(struct usb_device *dev, int index, extern int usb_clear_halt(struct usb_device *dev, int pipe); extern int usb_clear_halt(struct usb_device *dev, int pipe); extern int usb_reset_configuration(struct usb_device *dev); extern int usb_reset_configuration(struct usb_device *dev); extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); extern int usb_set_interface_timeout(struct usb_device *dev, int ifnum, int alternate, unsigned long timeout); extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr); extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr); /* this request isn't really synchronous, but it belongs with the others */ /* this request isn't really synchronous, but it belongs with the others */ Loading
sound/usb/pcm.c +8 −3 Original line number Original line Diff line number Diff line Loading @@ -39,6 +39,8 @@ #define SUBSTREAM_FLAG_DATA_EP_STARTED 0 #define SUBSTREAM_FLAG_DATA_EP_STARTED 0 #define SUBSTREAM_FLAG_SYNC_EP_STARTED 1 #define SUBSTREAM_FLAG_SYNC_EP_STARTED 1 #define MAX_SETALT_TIMEOUT_MS 1000 /* return the estimated delay based on USB frame counters */ /* return the estimated delay based on USB frame counters */ snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, unsigned int rate) unsigned int rate) Loading Loading @@ -582,7 +584,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* close the old interface */ /* close the old interface */ if (subs->interface >= 0 && subs->interface != fmt->iface) { if (subs->interface >= 0 && subs->interface != fmt->iface) { if (!subs->stream->chip->keep_iface) { if (!subs->stream->chip->keep_iface) { err = usb_set_interface(subs->dev, subs->interface, 0); err = usb_set_interface_timeout(subs->dev, subs->interface, 0, MAX_SETALT_TIMEOUT_MS); if (err < 0) { if (err < 0) { dev_err(&dev->dev, dev_err(&dev->dev, "%d:%d: return to setting 0 failed (%d)\n", "%d:%d: return to setting 0 failed (%d)\n", Loading @@ -600,7 +603,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) if (err < 0) if (err < 0) return -EIO; return -EIO; err = usb_set_interface(dev, fmt->iface, fmt->altsetting); err = usb_set_interface_timeout(dev, fmt->iface, fmt->altsetting, MAX_SETALT_TIMEOUT_MS); if (err < 0) { if (err < 0) { dev_err(&dev->dev, dev_err(&dev->dev, "%d:%d: usb_set_interface failed (%d)\n", "%d:%d: usb_set_interface failed (%d)\n", Loading Loading @@ -646,7 +650,8 @@ int snd_usb_enable_audio_stream(struct snd_usb_substream *subs, if (!enable) { if (!enable) { if (subs->interface >= 0) { if (subs->interface >= 0) { usb_set_interface(subs->dev, subs->interface, 0); usb_set_interface_timeout(subs->dev, subs->interface, 0, MAX_SETALT_TIMEOUT_MS); subs->altset_idx = 0; subs->altset_idx = 0; subs->interface = -1; subs->interface = -1; subs->cur_audiofmt = NULL; subs->cur_audiofmt = NULL; Loading