Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc218641 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "sound: usb: Set SET_ALT control transfer timeout as 1 sec"

parents 3f0b1f99 85491426
Loading
Loading
Loading
Loading
+169 −0
Original line number Original line Diff line number Diff line
@@ -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
+2 −0
Original line number Original line Diff line number Diff line
@@ -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 */
+8 −3
Original line number Original line Diff line number Diff line
@@ -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)
@@ -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",
@@ -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",
@@ -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;