mirror of
https://github.com/commaai/agnos-kernel-sdm845.git
synced 2026-06-13 13:54:53 +08:00
Currently driver looks for the highest revision of usb audio class protocol version from the configuration summary descriptors. Instead, select first device preferred configuration not supporting full UAC 3.0 as full UAC 3.0 is not currently supported by audio class driver. Also, rename audio function subclass FULL_ADC_PROFILE to FULL_ADC_3_0 as defined in spec. Change-Id: I656e3f34e9f68530b886fd55ff2541318405fd2c Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
280 lines
7.8 KiB
C
280 lines
7.8 KiB
C
/*
|
|
* drivers/usb/generic.c - generic driver for USB devices (not interfaces)
|
|
*
|
|
* (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
|
|
*
|
|
* based on drivers/usb/usb.c which had the following copyrights:
|
|
* (C) Copyright Linus Torvalds 1999
|
|
* (C) Copyright Johannes Erdfelt 1999-2001
|
|
* (C) Copyright Andreas Gal 1999
|
|
* (C) Copyright Gregory P. Smith 1999
|
|
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
|
|
* (C) Copyright Randy Dunlap 2000
|
|
* (C) Copyright David Brownell 2000-2004
|
|
* (C) Copyright Yggdrasil Computing, Inc. 2000
|
|
* (usb_device_id matching changes by Adam J. Richter)
|
|
* (C) Copyright Greg Kroah-Hartman 2002-2003
|
|
*
|
|
*/
|
|
|
|
#include <linux/usb.h>
|
|
#include <linux/usb/hcd.h>
|
|
#include <linux/usb/audio.h>
|
|
#include <linux/usb/audio-v3.h>
|
|
#include "usb.h"
|
|
|
|
static inline const char *plural(int n)
|
|
{
|
|
return (n == 1 ? "" : "s");
|
|
}
|
|
|
|
static int is_rndis(struct usb_interface_descriptor *desc)
|
|
{
|
|
return desc->bInterfaceClass == USB_CLASS_COMM
|
|
&& desc->bInterfaceSubClass == 2
|
|
&& desc->bInterfaceProtocol == 0xff;
|
|
}
|
|
|
|
static int is_activesync(struct usb_interface_descriptor *desc)
|
|
{
|
|
return desc->bInterfaceClass == USB_CLASS_MISC
|
|
&& desc->bInterfaceSubClass == 1
|
|
&& desc->bInterfaceProtocol == 1;
|
|
}
|
|
|
|
static int usb_audio_max_rev_config(struct usb_host_bos *bos)
|
|
{
|
|
int desc_cnt, func_cnt, numfunc;
|
|
int num_cfg_desc;
|
|
struct usb_config_summary_descriptor *conf_summary;
|
|
|
|
if (!bos || !bos->config_summary)
|
|
goto done;
|
|
|
|
conf_summary = bos->config_summary;
|
|
num_cfg_desc = bos->num_config_summary_desc;
|
|
|
|
for (desc_cnt = 0; desc_cnt < num_cfg_desc; desc_cnt++) {
|
|
numfunc = conf_summary->bNumFunctions;
|
|
for (func_cnt = 0; func_cnt < numfunc; func_cnt++) {
|
|
/* honor device preferred config */
|
|
if (conf_summary->cs_info[func_cnt].bClass ==
|
|
USB_CLASS_AUDIO &&
|
|
conf_summary->cs_info[func_cnt].bSubClass !=
|
|
FULL_ADC_3_0)
|
|
return conf_summary->bConfigurationValue;
|
|
}
|
|
}
|
|
|
|
done:
|
|
return -EINVAL;
|
|
}
|
|
|
|
int usb_choose_configuration(struct usb_device *udev)
|
|
{
|
|
int i;
|
|
int num_configs;
|
|
int insufficient_power = 0;
|
|
struct usb_host_config *c, *best;
|
|
|
|
if (usb_device_is_owned(udev))
|
|
return 0;
|
|
|
|
best = NULL;
|
|
c = udev->config;
|
|
num_configs = udev->descriptor.bNumConfigurations;
|
|
for (i = 0; i < num_configs; (i++, c++)) {
|
|
struct usb_interface_descriptor *desc = NULL;
|
|
|
|
/* It's possible that a config has no interfaces! */
|
|
if (c->desc.bNumInterfaces > 0)
|
|
desc = &c->intf_cache[0]->altsetting->desc;
|
|
|
|
/*
|
|
* HP's USB bus-powered keyboard has only one configuration
|
|
* and it claims to be self-powered; other devices may have
|
|
* similar errors in their descriptors. If the next test
|
|
* were allowed to execute, such configurations would always
|
|
* be rejected and the devices would not work as expected.
|
|
* In the meantime, we run the risk of selecting a config
|
|
* that requires external power at a time when that power
|
|
* isn't available. It seems to be the lesser of two evils.
|
|
*
|
|
* Bugzilla #6448 reports a device that appears to crash
|
|
* when it receives a GET_DEVICE_STATUS request! We don't
|
|
* have any other way to tell whether a device is self-powered,
|
|
* but since we don't use that information anywhere but here,
|
|
* the call has been removed.
|
|
*
|
|
* Maybe the GET_DEVICE_STATUS call and the test below can
|
|
* be reinstated when device firmwares become more reliable.
|
|
* Don't hold your breath.
|
|
*/
|
|
#if 0
|
|
/* Rule out self-powered configs for a bus-powered device */
|
|
if (bus_powered && (c->desc.bmAttributes &
|
|
USB_CONFIG_ATT_SELFPOWER))
|
|
continue;
|
|
#endif
|
|
|
|
/*
|
|
* The next test may not be as effective as it should be.
|
|
* Some hubs have errors in their descriptor, claiming
|
|
* to be self-powered when they are really bus-powered.
|
|
* We will overestimate the amount of current such hubs
|
|
* make available for each port.
|
|
*
|
|
* This is a fairly benign sort of failure. It won't
|
|
* cause us to reject configurations that we should have
|
|
* accepted.
|
|
*/
|
|
|
|
/* Rule out configs that draw too much bus current */
|
|
if (usb_get_max_power(udev, c) > udev->bus_mA) {
|
|
insufficient_power++;
|
|
continue;
|
|
}
|
|
|
|
/* When the first config's first interface is one of Microsoft's
|
|
* pet nonstandard Ethernet-over-USB protocols, ignore it unless
|
|
* this kernel has enabled the necessary host side driver.
|
|
* But: Don't ignore it if it's the only config.
|
|
*/
|
|
if (i == 0 && num_configs > 1 && desc &&
|
|
(is_rndis(desc) || is_activesync(desc))) {
|
|
#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)
|
|
continue;
|
|
#else
|
|
best = c;
|
|
#endif
|
|
}
|
|
|
|
/* From the remaining configs, choose the first one whose
|
|
* first interface is for a non-vendor-specific class.
|
|
* Reason: Linux is more likely to have a class driver
|
|
* than a vendor-specific driver. */
|
|
else if (udev->descriptor.bDeviceClass !=
|
|
USB_CLASS_VENDOR_SPEC &&
|
|
(desc && desc->bInterfaceClass !=
|
|
USB_CLASS_VENDOR_SPEC)) {
|
|
best = c;
|
|
break;
|
|
}
|
|
/* If all the remaining configs are vendor-specific,
|
|
* choose the first one. */
|
|
else if (!best)
|
|
best = c;
|
|
}
|
|
|
|
if (insufficient_power > 0)
|
|
dev_info(&udev->dev, "rejected %d configuration%s "
|
|
"due to insufficient available bus power\n",
|
|
insufficient_power, plural(insufficient_power));
|
|
|
|
if (best) {
|
|
/* choose usb audio class preferred config if available */
|
|
i = usb_audio_max_rev_config(udev->bos);
|
|
if (i < 0)
|
|
i = best->desc.bConfigurationValue;
|
|
dev_dbg(&udev->dev,
|
|
"configuration #%d chosen from %d choice%s\n",
|
|
i, num_configs, plural(num_configs));
|
|
} else {
|
|
i = -1;
|
|
dev_warn(&udev->dev,
|
|
"no configuration chosen from %d choice%s\n",
|
|
num_configs, plural(num_configs));
|
|
}
|
|
return i;
|
|
}
|
|
EXPORT_SYMBOL_GPL(usb_choose_configuration);
|
|
|
|
static int generic_probe(struct usb_device *udev)
|
|
{
|
|
int err, c;
|
|
|
|
/* Choose and set the configuration. This registers the interfaces
|
|
* with the driver core and lets interface drivers bind to them.
|
|
*/
|
|
if (udev->authorized == 0)
|
|
dev_err(&udev->dev, "Device is not authorized for usage\n");
|
|
else {
|
|
c = usb_choose_configuration(udev);
|
|
if (c >= 0) {
|
|
err = usb_set_configuration(udev, c);
|
|
if (err && err != -ENODEV) {
|
|
dev_err(&udev->dev, "can't set config #%d, error %d\n",
|
|
c, err);
|
|
/* This need not be fatal. The user can try to
|
|
* set other configurations. */
|
|
}
|
|
}
|
|
}
|
|
/* USB device state == configured ... usable */
|
|
usb_notify_add_device(udev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void generic_disconnect(struct usb_device *udev)
|
|
{
|
|
usb_notify_remove_device(udev);
|
|
|
|
/* if this is only an unbind, not a physical disconnect, then
|
|
* unconfigure the device */
|
|
if (udev->actconfig)
|
|
usb_set_configuration(udev, -1);
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
static int generic_suspend(struct usb_device *udev, pm_message_t msg)
|
|
{
|
|
int rc;
|
|
|
|
/* Normal USB devices suspend through their upstream port.
|
|
* Root hubs don't have upstream ports to suspend,
|
|
* so we have to shut down their downstream HC-to-USB
|
|
* interfaces manually by doing a bus (or "global") suspend.
|
|
*/
|
|
if (!udev->parent)
|
|
rc = hcd_bus_suspend(udev, msg);
|
|
|
|
/* Non-root devices don't need to do anything for FREEZE or PRETHAW */
|
|
else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
|
|
rc = 0;
|
|
else
|
|
rc = usb_port_suspend(udev, msg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int generic_resume(struct usb_device *udev, pm_message_t msg)
|
|
{
|
|
int rc;
|
|
|
|
/* Normal USB devices resume/reset through their upstream port.
|
|
* Root hubs don't have upstream ports to resume or reset,
|
|
* so we have to start up their downstream HC-to-USB
|
|
* interfaces manually by doing a bus (or "global") resume.
|
|
*/
|
|
if (!udev->parent)
|
|
rc = hcd_bus_resume(udev, msg);
|
|
else
|
|
rc = usb_port_resume(udev, msg);
|
|
return rc;
|
|
}
|
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
struct usb_device_driver usb_generic_driver = {
|
|
.name = "usb",
|
|
.probe = generic_probe,
|
|
.disconnect = generic_disconnect,
|
|
#ifdef CONFIG_PM
|
|
.suspend = generic_suspend,
|
|
.resume = generic_resume,
|
|
#endif
|
|
.supports_autosuspend = 1,
|
|
};
|