| .. | .. |
|---|
| 21 | 21 | |
|---|
| 22 | 22 | #include <linux/usb.h> |
|---|
| 23 | 23 | #include <linux/usb/hcd.h> |
|---|
| 24 | +#include <uapi/linux/usb/audio.h> |
|---|
| 24 | 25 | #include "usb.h" |
|---|
| 25 | 26 | |
|---|
| 26 | 27 | static inline const char *plural(int n) |
|---|
| .. | .. |
|---|
| 42 | 43 | && desc->bInterfaceProtocol == 1; |
|---|
| 43 | 44 | } |
|---|
| 44 | 45 | |
|---|
| 45 | | -static int get_usb_audio_config(struct usb_host_bos *bos) |
|---|
| 46 | +static bool is_audio(struct usb_interface_descriptor *desc) |
|---|
| 46 | 47 | { |
|---|
| 47 | | - unsigned int desc_cnt, num_cfg_desc, len = 0; |
|---|
| 48 | | - unsigned char *buffer; |
|---|
| 49 | | - struct usb_config_summary_descriptor *conf_summary; |
|---|
| 48 | + return desc->bInterfaceClass == USB_CLASS_AUDIO; |
|---|
| 49 | +} |
|---|
| 50 | 50 | |
|---|
| 51 | | - if (!bos || !bos->config_summary) |
|---|
| 52 | | - goto done; |
|---|
| 53 | | - |
|---|
| 54 | | - num_cfg_desc = bos->num_config_summary_desc; |
|---|
| 55 | | - conf_summary = bos->config_summary; |
|---|
| 56 | | - buffer = (unsigned char *)conf_summary; |
|---|
| 57 | | - for (desc_cnt = 0; desc_cnt < num_cfg_desc; desc_cnt++) { |
|---|
| 58 | | - conf_summary = |
|---|
| 59 | | - (struct usb_config_summary_descriptor *)(buffer + len); |
|---|
| 60 | | - |
|---|
| 61 | | - len += conf_summary->bLength; |
|---|
| 62 | | - |
|---|
| 63 | | - if (conf_summary->bcdVersion != USB_CONFIG_SUMMARY_DESC_REV || |
|---|
| 64 | | - conf_summary->bClass != USB_CLASS_AUDIO) |
|---|
| 65 | | - continue; |
|---|
| 66 | | - |
|---|
| 67 | | - /* return 1st config as per device preference */ |
|---|
| 68 | | - return conf_summary->bConfigurationIndex[0]; |
|---|
| 69 | | - } |
|---|
| 70 | | - |
|---|
| 71 | | -done: |
|---|
| 72 | | - return -EINVAL; |
|---|
| 51 | +static bool is_uac3_config(struct usb_interface_descriptor *desc) |
|---|
| 52 | +{ |
|---|
| 53 | + return desc->bInterfaceProtocol == UAC_VERSION_3; |
|---|
| 73 | 54 | } |
|---|
| 74 | 55 | |
|---|
| 75 | 56 | int usb_choose_configuration(struct usb_device *udev) |
|---|
| .. | .. |
|---|
| 137 | 118 | continue; |
|---|
| 138 | 119 | } |
|---|
| 139 | 120 | |
|---|
| 121 | + /* |
|---|
| 122 | + * Select first configuration as default for audio so that |
|---|
| 123 | + * devices that don't comply with UAC3 protocol are supported. |
|---|
| 124 | + * But, still iterate through other configurations and |
|---|
| 125 | + * select UAC3 compliant config if present. |
|---|
| 126 | + */ |
|---|
| 127 | + if (desc && is_audio(desc)) { |
|---|
| 128 | + /* Always prefer the first found UAC3 config */ |
|---|
| 129 | + if (is_uac3_config(desc)) { |
|---|
| 130 | + best = c; |
|---|
| 131 | + break; |
|---|
| 132 | + } |
|---|
| 133 | + |
|---|
| 134 | + /* If there is no UAC3 config, prefer the first config */ |
|---|
| 135 | + else if (i == 0) |
|---|
| 136 | + best = c; |
|---|
| 137 | + |
|---|
| 138 | + /* Unconditional continue, because the rest of the code |
|---|
| 139 | + * in the loop is irrelevant for audio devices, and |
|---|
| 140 | + * because it can reassign best, which for audio devices |
|---|
| 141 | + * we don't want. |
|---|
| 142 | + */ |
|---|
| 143 | + continue; |
|---|
| 144 | + } |
|---|
| 145 | + |
|---|
| 140 | 146 | /* When the first config's first interface is one of Microsoft's |
|---|
| 141 | 147 | * pet nonstandard Ethernet-over-USB protocols, ignore it unless |
|---|
| 142 | 148 | * this kernel has enabled the necessary host side driver. |
|---|
| .. | .. |
|---|
| 175 | 181 | insufficient_power, plural(insufficient_power)); |
|---|
| 176 | 182 | |
|---|
| 177 | 183 | if (best) { |
|---|
| 178 | | - /* choose device preferred config */ |
|---|
| 179 | | - i = get_usb_audio_config(udev->bos); |
|---|
| 180 | | - if (i < 0) |
|---|
| 181 | | - i = best->desc.bConfigurationValue; |
|---|
| 184 | + i = best->desc.bConfigurationValue; |
|---|
| 182 | 185 | dev_dbg(&udev->dev, |
|---|
| 183 | 186 | "configuration #%d chosen from %d choice%s\n", |
|---|
| 184 | 187 | i, num_configs, plural(num_configs)); |
|---|
| .. | .. |
|---|
| 192 | 195 | } |
|---|
| 193 | 196 | EXPORT_SYMBOL_GPL(usb_choose_configuration); |
|---|
| 194 | 197 | |
|---|
| 195 | | -static int generic_probe(struct usb_device *udev) |
|---|
| 198 | +static int __check_for_non_generic_match(struct device_driver *drv, void *data) |
|---|
| 199 | +{ |
|---|
| 200 | + struct usb_device *udev = data; |
|---|
| 201 | + struct usb_device_driver *udrv; |
|---|
| 202 | + |
|---|
| 203 | + if (!is_usb_device_driver(drv)) |
|---|
| 204 | + return 0; |
|---|
| 205 | + udrv = to_usb_device_driver(drv); |
|---|
| 206 | + if (udrv == &usb_generic_driver) |
|---|
| 207 | + return 0; |
|---|
| 208 | + return usb_driver_applicable(udev, udrv); |
|---|
| 209 | +} |
|---|
| 210 | + |
|---|
| 211 | +static bool usb_generic_driver_match(struct usb_device *udev) |
|---|
| 212 | +{ |
|---|
| 213 | + if (udev->use_generic_driver) |
|---|
| 214 | + return true; |
|---|
| 215 | + |
|---|
| 216 | + /* |
|---|
| 217 | + * If any other driver wants the device, leave the device to this other |
|---|
| 218 | + * driver. |
|---|
| 219 | + */ |
|---|
| 220 | + if (bus_for_each_drv(&usb_bus_type, NULL, udev, __check_for_non_generic_match)) |
|---|
| 221 | + return false; |
|---|
| 222 | + |
|---|
| 223 | + return true; |
|---|
| 224 | +} |
|---|
| 225 | + |
|---|
| 226 | +int usb_generic_driver_probe(struct usb_device *udev) |
|---|
| 196 | 227 | { |
|---|
| 197 | 228 | int err, c; |
|---|
| 198 | 229 | |
|---|
| .. | .. |
|---|
| 219 | 250 | return 0; |
|---|
| 220 | 251 | } |
|---|
| 221 | 252 | |
|---|
| 222 | | -static void generic_disconnect(struct usb_device *udev) |
|---|
| 253 | +void usb_generic_driver_disconnect(struct usb_device *udev) |
|---|
| 223 | 254 | { |
|---|
| 224 | 255 | usb_notify_remove_device(udev); |
|---|
| 225 | 256 | |
|---|
| .. | .. |
|---|
| 231 | 262 | |
|---|
| 232 | 263 | #ifdef CONFIG_PM |
|---|
| 233 | 264 | |
|---|
| 234 | | -static int generic_suspend(struct usb_device *udev, pm_message_t msg) |
|---|
| 265 | +int usb_generic_driver_suspend(struct usb_device *udev, pm_message_t msg) |
|---|
| 235 | 266 | { |
|---|
| 236 | 267 | int rc; |
|---|
| 237 | 268 | |
|---|
| .. | .. |
|---|
| 254 | 285 | else |
|---|
| 255 | 286 | rc = usb_port_suspend(udev, msg); |
|---|
| 256 | 287 | |
|---|
| 288 | + if (rc == 0) |
|---|
| 289 | + usbfs_notify_suspend(udev); |
|---|
| 257 | 290 | return rc; |
|---|
| 258 | 291 | } |
|---|
| 259 | 292 | |
|---|
| 260 | | -static int generic_resume(struct usb_device *udev, pm_message_t msg) |
|---|
| 293 | +int usb_generic_driver_resume(struct usb_device *udev, pm_message_t msg) |
|---|
| 261 | 294 | { |
|---|
| 262 | 295 | int rc; |
|---|
| 263 | 296 | |
|---|
| .. | .. |
|---|
| 270 | 303 | rc = hcd_bus_resume(udev, msg); |
|---|
| 271 | 304 | else |
|---|
| 272 | 305 | rc = usb_port_resume(udev, msg); |
|---|
| 306 | + |
|---|
| 307 | + if (rc == 0) |
|---|
| 308 | + usbfs_notify_resume(udev); |
|---|
| 273 | 309 | return rc; |
|---|
| 274 | 310 | } |
|---|
| 275 | 311 | |
|---|
| .. | .. |
|---|
| 277 | 313 | |
|---|
| 278 | 314 | struct usb_device_driver usb_generic_driver = { |
|---|
| 279 | 315 | .name = "usb", |
|---|
| 280 | | - .probe = generic_probe, |
|---|
| 281 | | - .disconnect = generic_disconnect, |
|---|
| 316 | + .match = usb_generic_driver_match, |
|---|
| 317 | + .probe = usb_generic_driver_probe, |
|---|
| 318 | + .disconnect = usb_generic_driver_disconnect, |
|---|
| 282 | 319 | #ifdef CONFIG_PM |
|---|
| 283 | | - .suspend = generic_suspend, |
|---|
| 284 | | - .resume = generic_resume, |
|---|
| 320 | + .suspend = usb_generic_driver_suspend, |
|---|
| 321 | + .resume = usb_generic_driver_resume, |
|---|
| 285 | 322 | #endif |
|---|
| 286 | 323 | .supports_autosuspend = 1, |
|---|
| 287 | 324 | }; |
|---|