diff options
| -rw-r--r-- | include/linux/usb/audio-v2.h | 47 | ||||
| -rw-r--r-- | include/linux/usb/audio.h | 71 | ||||
| -rw-r--r-- | sound/usb/mixer.c | 322 | ||||
| -rw-r--r-- | sound/usb/mixer.h | 3 |
4 files changed, 343 insertions, 100 deletions
diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index 3b8560d233bd..0952231e6c3f 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h | |||
| @@ -43,6 +43,53 @@ struct uac_clock_selector_descriptor { | |||
| 43 | __u8 baCSourceID[]; | 43 | __u8 baCSourceID[]; |
| 44 | } __attribute__((packed)); | 44 | } __attribute__((packed)); |
| 45 | 45 | ||
| 46 | /* 4.7.2.4 Input terminal descriptor */ | ||
| 47 | |||
| 48 | struct uac2_input_terminal_descriptor { | ||
| 49 | __u8 bLength; | ||
| 50 | __u8 bDescriptorType; | ||
| 51 | __u8 bDescriptorSubtype; | ||
| 52 | __u8 bTerminalID; | ||
| 53 | __u16 wTerminalType; | ||
| 54 | __u8 bAssocTerminal; | ||
| 55 | __u8 bCSourceID; | ||
| 56 | __u8 bNrChannels; | ||
| 57 | __u32 bmChannelConfig; | ||
| 58 | __u8 iChannelNames; | ||
| 59 | __u16 bmControls; | ||
| 60 | __u8 iTerminal; | ||
| 61 | } __attribute__((packed)); | ||
| 62 | |||
| 63 | /* 4.7.2.5 Output terminal descriptor */ | ||
| 64 | |||
| 65 | struct uac2_output_terminal_descriptor { | ||
| 66 | __u8 bLength; | ||
| 67 | __u8 bDescriptorType; | ||
| 68 | __u8 bDescriptorSubtype; | ||
| 69 | __u8 bTerminalID; | ||
| 70 | __u16 wTerminalType; | ||
| 71 | __u8 bAssocTerminal; | ||
| 72 | __u8 bSourceID; | ||
| 73 | __u8 bCSourceID; | ||
| 74 | __u16 bmControls; | ||
| 75 | __u8 iTerminal; | ||
| 76 | } __attribute__((packed)); | ||
| 77 | |||
| 78 | |||
| 79 | |||
| 80 | /* 4.7.2.8 Feature Unit Descriptor */ | ||
| 81 | |||
| 82 | struct uac2_feature_unit_descriptor { | ||
| 83 | __u8 bLength; | ||
| 84 | __u8 bDescriptorType; | ||
| 85 | __u8 bDescriptorSubtype; | ||
| 86 | __u8 bUnitID; | ||
| 87 | __u8 bSourceID; | ||
| 88 | /* bmaControls is actually u32, | ||
| 89 | * but u8 is needed for the hybrid parser */ | ||
| 90 | __u8 bmaControls[0]; /* variable length */ | ||
| 91 | } __attribute__((packed)); | ||
| 92 | |||
| 46 | /* 4.9.2 Class-Specific AS Interface Descriptor */ | 93 | /* 4.9.2 Class-Specific AS Interface Descriptor */ |
| 47 | 94 | ||
| 48 | struct uac_as_header_descriptor_v2 { | 95 | struct uac_as_header_descriptor_v2 { |
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h index bc78a83d0f48..905a87caf3fb 100644 --- a/include/linux/usb/audio.h +++ b/include/linux/usb/audio.h | |||
| @@ -196,20 +196,33 @@ static inline __u8 uac_mixer_unit_bNrChannels(struct uac_mixer_unit_descriptor * | |||
| 196 | return desc->baSourceID[desc->bNrInPins]; | 196 | return desc->baSourceID[desc->bNrInPins]; |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | static inline __u16 uac_mixer_unit_wChannelConfig(struct uac_mixer_unit_descriptor *desc) | 199 | static inline __u32 uac_mixer_unit_wChannelConfig(struct uac_mixer_unit_descriptor *desc, |
| 200 | int protocol) | ||
| 200 | { | 201 | { |
| 201 | return (desc->baSourceID[desc->bNrInPins + 2] << 8) | | 202 | if (protocol == UAC_VERSION_1) |
| 202 | desc->baSourceID[desc->bNrInPins + 1]; | 203 | return (desc->baSourceID[desc->bNrInPins + 2] << 8) | |
| 204 | desc->baSourceID[desc->bNrInPins + 1]; | ||
| 205 | else | ||
| 206 | return (desc->baSourceID[desc->bNrInPins + 4] << 24) | | ||
| 207 | (desc->baSourceID[desc->bNrInPins + 3] << 16) | | ||
| 208 | (desc->baSourceID[desc->bNrInPins + 2] << 8) | | ||
| 209 | (desc->baSourceID[desc->bNrInPins + 1]); | ||
| 203 | } | 210 | } |
| 204 | 211 | ||
| 205 | static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor *desc) | 212 | static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor *desc, |
| 213 | int protocol) | ||
| 206 | { | 214 | { |
| 207 | return desc->baSourceID[desc->bNrInPins + 3]; | 215 | return (protocol == UAC_VERSION_1) ? |
| 216 | desc->baSourceID[desc->bNrInPins + 3] : | ||
| 217 | desc->baSourceID[desc->bNrInPins + 5]; | ||
| 208 | } | 218 | } |
| 209 | 219 | ||
| 210 | static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc) | 220 | static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc, |
| 221 | int protocol) | ||
| 211 | { | 222 | { |
| 212 | return &desc->baSourceID[desc->bNrInPins + 4]; | 223 | return (protocol == UAC_VERSION_1) ? |
| 224 | &desc->baSourceID[desc->bNrInPins + 4] : | ||
| 225 | &desc->baSourceID[desc->bNrInPins + 6]; | ||
| 213 | } | 226 | } |
| 214 | 227 | ||
| 215 | static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc) | 228 | static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc) |
| @@ -267,36 +280,54 @@ static inline __u8 uac_processing_unit_bNrChannels(struct uac_processing_unit_de | |||
| 267 | return desc->baSourceID[desc->bNrInPins]; | 280 | return desc->baSourceID[desc->bNrInPins]; |
| 268 | } | 281 | } |
| 269 | 282 | ||
| 270 | static inline __u16 uac_processing_unit_wChannelConfig(struct uac_processing_unit_descriptor *desc) | 283 | static inline __u32 uac_processing_unit_wChannelConfig(struct uac_processing_unit_descriptor *desc, |
| 284 | int protocol) | ||
| 271 | { | 285 | { |
| 272 | return (desc->baSourceID[desc->bNrInPins + 2] << 8) | | 286 | if (protocol == UAC_VERSION_1) |
| 273 | desc->baSourceID[desc->bNrInPins + 1]; | 287 | return (desc->baSourceID[desc->bNrInPins + 2] << 8) | |
| 288 | desc->baSourceID[desc->bNrInPins + 1]; | ||
| 289 | else | ||
| 290 | return (desc->baSourceID[desc->bNrInPins + 4] << 24) | | ||
| 291 | (desc->baSourceID[desc->bNrInPins + 3] << 16) | | ||
| 292 | (desc->baSourceID[desc->bNrInPins + 2] << 8) | | ||
| 293 | (desc->baSourceID[desc->bNrInPins + 1]); | ||
| 274 | } | 294 | } |
| 275 | 295 | ||
| 276 | static inline __u8 uac_processing_unit_iChannelNames(struct uac_processing_unit_descriptor *desc) | 296 | static inline __u8 uac_processing_unit_iChannelNames(struct uac_processing_unit_descriptor *desc, |
| 297 | int protocol) | ||
| 277 | { | 298 | { |
| 278 | return desc->baSourceID[desc->bNrInPins + 3]; | 299 | return (protocol == UAC_VERSION_1) ? |
| 300 | desc->baSourceID[desc->bNrInPins + 3] : | ||
| 301 | desc->baSourceID[desc->bNrInPins + 5]; | ||
| 279 | } | 302 | } |
| 280 | 303 | ||
| 281 | static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_descriptor *desc) | 304 | static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_descriptor *desc, |
| 305 | int protocol) | ||
| 282 | { | 306 | { |
| 283 | return desc->baSourceID[desc->bNrInPins + 4]; | 307 | return (protocol == UAC_VERSION_1) ? |
| 308 | desc->baSourceID[desc->bNrInPins + 4] : | ||
| 309 | desc->baSourceID[desc->bNrInPins + 6]; | ||
| 284 | } | 310 | } |
| 285 | 311 | ||
| 286 | static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc) | 312 | static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc, |
| 313 | int protocol) | ||
| 287 | { | 314 | { |
| 288 | return &desc->baSourceID[desc->bNrInPins + 5]; | 315 | return (protocol == UAC_VERSION_1) ? |
| 316 | &desc->baSourceID[desc->bNrInPins + 5] : | ||
| 317 | &desc->baSourceID[desc->bNrInPins + 7]; | ||
| 289 | } | 318 | } |
| 290 | 319 | ||
| 291 | static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc) | 320 | static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc, |
| 321 | int protocol) | ||
| 292 | { | 322 | { |
| 293 | __u8 control_size = uac_processing_unit_bControlSize(desc); | 323 | __u8 control_size = uac_processing_unit_bControlSize(desc, protocol); |
| 294 | return desc->baSourceID[desc->bNrInPins + control_size]; | 324 | return desc->baSourceID[desc->bNrInPins + control_size]; |
| 295 | } | 325 | } |
| 296 | 326 | ||
| 297 | static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_descriptor *desc) | 327 | static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_descriptor *desc, |
| 328 | int protocol) | ||
| 298 | { | 329 | { |
| 299 | __u8 control_size = uac_processing_unit_bControlSize(desc); | 330 | __u8 control_size = uac_processing_unit_bControlSize(desc, protocol); |
| 300 | return &desc->baSourceID[desc->bNrInPins + control_size + 1]; | 331 | return &desc->baSourceID[desc->bNrInPins + control_size + 1]; |
| 301 | } | 332 | } |
| 302 | 333 | ||
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 994b0385235c..1deef623c081 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | #include <linux/string.h> | 33 | #include <linux/string.h> |
| 34 | #include <linux/usb.h> | 34 | #include <linux/usb.h> |
| 35 | #include <linux/usb/audio.h> | 35 | #include <linux/usb/audio.h> |
| 36 | #include <linux/usb/audio-v2.h> | ||
| 36 | 37 | ||
| 37 | #include <sound/core.h> | 38 | #include <sound/core.h> |
| 38 | #include <sound/control.h> | 39 | #include <sound/control.h> |
| @@ -197,6 +198,7 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid, | |||
| 197 | 198 | ||
| 198 | /* | 199 | /* |
| 199 | * find an audio control unit with the given unit id | 200 | * find an audio control unit with the given unit id |
| 201 | * this doesn't return any clock related units, so they need to be handled elsewhere | ||
| 200 | */ | 202 | */ |
| 201 | static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit) | 203 | static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit) |
| 202 | { | 204 | { |
| @@ -205,7 +207,7 @@ static void *find_audio_control_unit(struct mixer_build *state, unsigned char un | |||
| 205 | p = NULL; | 207 | p = NULL; |
| 206 | while ((p = snd_usb_find_desc(state->buffer, state->buflen, p, | 208 | while ((p = snd_usb_find_desc(state->buffer, state->buflen, p, |
| 207 | USB_DT_CS_INTERFACE)) != NULL) { | 209 | USB_DT_CS_INTERFACE)) != NULL) { |
| 208 | if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC_EXTENSION_UNIT_V1 && p[3] == unit) | 210 | if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC2_EXTENSION_UNIT_V2 && p[3] == unit) |
| 209 | return p; | 211 | return p; |
| 210 | } | 212 | } |
| 211 | return NULL; | 213 | return NULL; |
| @@ -302,7 +304,7 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val) | |||
| 302 | * retrieve a mixer value | 304 | * retrieve a mixer value |
| 303 | */ | 305 | */ |
| 304 | 306 | ||
| 305 | static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) | 307 | static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) |
| 306 | { | 308 | { |
| 307 | unsigned char buf[2]; | 309 | unsigned char buf[2]; |
| 308 | int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; | 310 | int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; |
| @@ -324,6 +326,58 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali | |||
| 324 | return -EINVAL; | 326 | return -EINVAL; |
| 325 | } | 327 | } |
| 326 | 328 | ||
| 329 | static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) | ||
| 330 | { | ||
| 331 | unsigned char buf[14]; /* enough space for one range of 4 bytes */ | ||
| 332 | unsigned char *val; | ||
| 333 | int ret; | ||
| 334 | __u8 bRequest; | ||
| 335 | |||
| 336 | bRequest = (request == UAC_GET_CUR) ? | ||
| 337 | UAC2_CS_CUR : UAC2_CS_RANGE; | ||
| 338 | |||
| 339 | ret = snd_usb_ctl_msg(cval->mixer->chip->dev, | ||
| 340 | usb_rcvctrlpipe(cval->mixer->chip->dev, 0), | ||
| 341 | bRequest, | ||
| 342 | USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, | ||
| 343 | validx, cval->mixer->ctrlif | (cval->id << 8), | ||
| 344 | buf, sizeof(buf), 1000); | ||
| 345 | |||
| 346 | if (ret < 0) { | ||
| 347 | snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", | ||
| 348 | request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type); | ||
| 349 | return ret; | ||
| 350 | } | ||
| 351 | |||
| 352 | switch (request) { | ||
| 353 | case UAC_GET_CUR: | ||
| 354 | val = buf; | ||
| 355 | break; | ||
| 356 | case UAC_GET_MIN: | ||
| 357 | val = buf + sizeof(__u16); | ||
| 358 | break; | ||
| 359 | case UAC_GET_MAX: | ||
| 360 | val = buf + sizeof(__u16) * 2; | ||
| 361 | break; | ||
| 362 | case UAC_GET_RES: | ||
| 363 | val = buf + sizeof(__u16) * 3; | ||
| 364 | break; | ||
| 365 | default: | ||
| 366 | return -EINVAL; | ||
| 367 | } | ||
| 368 | |||
| 369 | *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(val, sizeof(__u16))); | ||
| 370 | |||
| 371 | return 0; | ||
| 372 | } | ||
| 373 | |||
| 374 | static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) | ||
| 375 | { | ||
| 376 | return (cval->mixer->protocol == UAC_VERSION_1) ? | ||
| 377 | get_ctl_value_v1(cval, request, validx, value_ret) : | ||
| 378 | get_ctl_value_v2(cval, request, validx, value_ret); | ||
| 379 | } | ||
| 380 | |||
| 327 | static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value) | 381 | static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value) |
| 328 | { | 382 | { |
| 329 | return get_ctl_value(cval, UAC_GET_CUR, validx, value); | 383 | return get_ctl_value(cval, UAC_GET_CUR, validx, value); |
| @@ -348,8 +402,7 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval, | |||
| 348 | err = get_cur_mix_raw(cval, channel, value); | 402 | err = get_cur_mix_raw(cval, channel, value); |
| 349 | if (err < 0) { | 403 | if (err < 0) { |
| 350 | if (!cval->mixer->ignore_ctl_error) | 404 | if (!cval->mixer->ignore_ctl_error) |
| 351 | snd_printd(KERN_ERR "cannot get current value for " | 405 | snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", |
| 352 | "control %d ch %d: err = %d\n", | ||
| 353 | cval->control, channel, err); | 406 | cval->control, channel, err); |
| 354 | return err; | 407 | return err; |
| 355 | } | 408 | } |
| @@ -367,8 +420,22 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, | |||
| 367 | int request, int validx, int value_set) | 420 | int request, int validx, int value_set) |
| 368 | { | 421 | { |
| 369 | unsigned char buf[2]; | 422 | unsigned char buf[2]; |
| 370 | int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; | 423 | int val_len, timeout = 10; |
| 371 | int timeout = 10; | 424 | |
| 425 | if (cval->mixer->protocol == UAC_VERSION_1) { | ||
| 426 | val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; | ||
| 427 | } else { /* UAC_VERSION_2 */ | ||
| 428 | /* audio class v2 controls are always 2 bytes in size */ | ||
| 429 | val_len = sizeof(__u16); | ||
| 430 | |||
| 431 | /* FIXME */ | ||
| 432 | if (request != UAC_SET_CUR) { | ||
| 433 | snd_printdd(KERN_WARNING "RANGE setting not yet supported\n"); | ||
| 434 | return -EINVAL; | ||
| 435 | } | ||
| 436 | |||
| 437 | request = UAC2_CS_CUR; | ||
| 438 | } | ||
| 372 | 439 | ||
| 373 | value_set = convert_bytes_value(cval, value_set); | 440 | value_set = convert_bytes_value(cval, value_set); |
| 374 | buf[0] = value_set & 0xff; | 441 | buf[0] = value_set & 0xff; |
| @@ -564,46 +631,65 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm | |||
| 564 | */ | 631 | */ |
| 565 | static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) | 632 | static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) |
| 566 | { | 633 | { |
| 567 | unsigned char *p1; | 634 | void *p1; |
| 568 | 635 | ||
| 569 | memset(term, 0, sizeof(*term)); | 636 | memset(term, 0, sizeof(*term)); |
| 570 | while ((p1 = find_audio_control_unit(state, id)) != NULL) { | 637 | while ((p1 = find_audio_control_unit(state, id)) != NULL) { |
| 638 | unsigned char *hdr = p1; | ||
| 571 | term->id = id; | 639 | term->id = id; |
| 572 | switch (p1[2]) { | 640 | switch (hdr[2]) { |
| 573 | case UAC_INPUT_TERMINAL: | 641 | case UAC_INPUT_TERMINAL: |
| 574 | term->type = combine_word(p1 + 4); | 642 | if (state->mixer->protocol == UAC_VERSION_1) { |
| 575 | term->channels = p1[7]; | 643 | struct uac_input_terminal_descriptor *d = p1; |
| 576 | term->chconfig = combine_word(p1 + 8); | 644 | term->type = le16_to_cpu(d->wTerminalType); |
| 577 | term->name = p1[11]; | 645 | term->channels = d->bNrChannels; |
| 646 | term->chconfig = le16_to_cpu(d->wChannelConfig); | ||
| 647 | term->name = d->iTerminal; | ||
| 648 | } else { /* UAC_VERSION_2 */ | ||
| 649 | struct uac2_input_terminal_descriptor *d = p1; | ||
| 650 | term->type = le16_to_cpu(d->wTerminalType); | ||
| 651 | term->channels = d->bNrChannels; | ||
| 652 | term->chconfig = le32_to_cpu(d->bmChannelConfig); | ||
| 653 | term->name = d->iTerminal; | ||
| 654 | } | ||
| 578 | return 0; | 655 | return 0; |
| 579 | case UAC_FEATURE_UNIT: | 656 | case UAC_FEATURE_UNIT: { |
| 580 | id = p1[4]; | 657 | /* the header is the same for v1 and v2 */ |
| 658 | struct uac_feature_unit_descriptor *d = p1; | ||
| 659 | id = d->bUnitID; | ||
| 581 | break; /* continue to parse */ | 660 | break; /* continue to parse */ |
| 582 | case UAC_MIXER_UNIT: | 661 | } |
| 583 | term->type = p1[2] << 16; /* virtual type */ | 662 | case UAC_MIXER_UNIT: { |
| 584 | term->channels = p1[5 + p1[4]]; | 663 | struct uac_mixer_unit_descriptor *d = p1; |
| 585 | term->chconfig = combine_word(p1 + 6 + p1[4]); | 664 | term->type = d->bDescriptorSubtype << 16; /* virtual type */ |
| 586 | term->name = p1[p1[0] - 1]; | 665 | term->channels = uac_mixer_unit_bNrChannels(d); |
| 666 | term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->protocol); | ||
| 667 | term->name = uac_mixer_unit_iMixer(d); | ||
| 587 | return 0; | 668 | return 0; |
| 588 | case UAC_SELECTOR_UNIT: | 669 | } |
| 670 | case UAC_SELECTOR_UNIT: { | ||
| 671 | struct uac_selector_unit_descriptor *d = p1; | ||
| 589 | /* call recursively to retrieve the channel info */ | 672 | /* call recursively to retrieve the channel info */ |
| 590 | if (check_input_term(state, p1[5], term) < 0) | 673 | if (check_input_term(state, d->baSourceID[0], term) < 0) |
| 591 | return -ENODEV; | 674 | return -ENODEV; |
| 592 | term->type = p1[2] << 16; /* virtual type */ | 675 | term->type = d->bDescriptorSubtype << 16; /* virtual type */ |
| 593 | term->id = id; | 676 | term->id = id; |
| 594 | term->name = p1[9 + p1[0] - 1]; | 677 | term->name = uac_selector_unit_iSelector(d); |
| 595 | return 0; | 678 | return 0; |
| 679 | } | ||
| 596 | case UAC_PROCESSING_UNIT_V1: | 680 | case UAC_PROCESSING_UNIT_V1: |
| 597 | case UAC_EXTENSION_UNIT_V1: | 681 | case UAC_EXTENSION_UNIT_V1: { |
| 598 | if (p1[6] == 1) { | 682 | struct uac_processing_unit_descriptor *d = p1; |
| 599 | id = p1[7]; | 683 | if (d->bNrInPins) { |
| 684 | id = d->baSourceID[0]; | ||
| 600 | break; /* continue to parse */ | 685 | break; /* continue to parse */ |
| 601 | } | 686 | } |
| 602 | term->type = p1[2] << 16; /* virtual type */ | 687 | term->type = d->bDescriptorSubtype << 16; /* virtual type */ |
| 603 | term->channels = p1[7 + p1[6]]; | 688 | term->channels = uac_processing_unit_bNrChannels(d); |
| 604 | term->chconfig = combine_word(p1 + 8 + p1[6]); | 689 | term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol); |
| 605 | term->name = p1[12 + p1[6] + p1[11 + p1[6]]]; | 690 | term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol); |
| 606 | return 0; | 691 | return 0; |
| 692 | } | ||
| 607 | default: | 693 | default: |
| 608 | return -ENODEV; | 694 | return -ENODEV; |
| 609 | } | 695 | } |
| @@ -850,6 +936,15 @@ static struct snd_kcontrol_new usb_feature_unit_ctl = { | |||
| 850 | .put = mixer_ctl_feature_put, | 936 | .put = mixer_ctl_feature_put, |
| 851 | }; | 937 | }; |
| 852 | 938 | ||
| 939 | /* the read-only variant */ | ||
| 940 | static struct snd_kcontrol_new usb_feature_unit_ctl_ro = { | ||
| 941 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 942 | .name = "", /* will be filled later manually */ | ||
| 943 | .info = mixer_ctl_feature_info, | ||
| 944 | .get = mixer_ctl_feature_get, | ||
| 945 | .put = NULL, | ||
| 946 | }; | ||
| 947 | |||
| 853 | 948 | ||
| 854 | /* | 949 | /* |
| 855 | * build a feature control | 950 | * build a feature control |
| @@ -862,7 +957,8 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str) | |||
| 862 | 957 | ||
| 863 | static void build_feature_ctl(struct mixer_build *state, void *raw_desc, | 958 | static void build_feature_ctl(struct mixer_build *state, void *raw_desc, |
| 864 | unsigned int ctl_mask, int control, | 959 | unsigned int ctl_mask, int control, |
| 865 | struct usb_audio_term *iterm, int unitid) | 960 | struct usb_audio_term *iterm, int unitid, |
| 961 | int read_only) | ||
| 866 | { | 962 | { |
| 867 | struct uac_feature_unit_descriptor *desc = raw_desc; | 963 | struct uac_feature_unit_descriptor *desc = raw_desc; |
| 868 | unsigned int len = 0; | 964 | unsigned int len = 0; |
| @@ -906,7 +1002,11 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, | |||
| 906 | /* get min/max values */ | 1002 | /* get min/max values */ |
| 907 | get_min_max(cval, 0); | 1003 | get_min_max(cval, 0); |
| 908 | 1004 | ||
| 909 | kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); | 1005 | if (read_only) |
| 1006 | kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval); | ||
| 1007 | else | ||
| 1008 | kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); | ||
| 1009 | |||
| 910 | if (! kctl) { | 1010 | if (! kctl) { |
| 911 | snd_printk(KERN_ERR "cannot malloc kcontrol\n"); | 1011 | snd_printk(KERN_ERR "cannot malloc kcontrol\n"); |
| 912 | kfree(cval); | 1012 | kfree(cval); |
| @@ -1016,24 +1116,34 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void | |||
| 1016 | struct usb_audio_term iterm; | 1116 | struct usb_audio_term iterm; |
| 1017 | unsigned int master_bits, first_ch_bits; | 1117 | unsigned int master_bits, first_ch_bits; |
| 1018 | int err, csize; | 1118 | int err, csize; |
| 1019 | struct uac_feature_unit_descriptor *ftr = _ftr; | 1119 | struct uac_feature_unit_descriptor *hdr = _ftr; |
| 1120 | __u8 *bmaControls; | ||
| 1121 | |||
| 1122 | if (state->mixer->protocol == UAC_VERSION_1) { | ||
| 1123 | csize = hdr->bControlSize; | ||
| 1124 | channels = (hdr->bLength - 7) / csize - 1; | ||
| 1125 | bmaControls = hdr->bmaControls; | ||
| 1126 | } else { | ||
| 1127 | struct uac2_feature_unit_descriptor *ftr = _ftr; | ||
| 1128 | csize = 4; | ||
| 1129 | channels = (hdr->bLength - 6) / 4; | ||
| 1130 | bmaControls = ftr->bmaControls; | ||
| 1131 | } | ||
| 1020 | 1132 | ||
| 1021 | if (ftr->bLength < 7 || ! (csize = ftr->bControlSize) || ftr->bLength < 7 + csize) { | 1133 | if (hdr->bLength < 7 || !csize || hdr->bLength < 7 + csize) { |
| 1022 | snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid); | 1134 | snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid); |
| 1023 | return -EINVAL; | 1135 | return -EINVAL; |
| 1024 | } | 1136 | } |
| 1025 | 1137 | ||
| 1026 | /* parse the source unit */ | 1138 | /* parse the source unit */ |
| 1027 | if ((err = parse_audio_unit(state, ftr->bSourceID)) < 0) | 1139 | if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0) |
| 1028 | return err; | 1140 | return err; |
| 1029 | 1141 | ||
| 1030 | /* determine the input source type and name */ | 1142 | /* determine the input source type and name */ |
| 1031 | if (check_input_term(state, ftr->bSourceID, &iterm) < 0) | 1143 | if (check_input_term(state, hdr->bSourceID, &iterm) < 0) |
| 1032 | return -EINVAL; | 1144 | return -EINVAL; |
| 1033 | 1145 | ||
| 1034 | channels = (ftr->bLength - 7) / csize - 1; | 1146 | master_bits = snd_usb_combine_bytes(bmaControls, csize); |
| 1035 | |||
| 1036 | master_bits = snd_usb_combine_bytes(ftr->bmaControls, csize); | ||
| 1037 | /* master configuration quirks */ | 1147 | /* master configuration quirks */ |
| 1038 | switch (state->chip->usb_id) { | 1148 | switch (state->chip->usb_id) { |
| 1039 | case USB_ID(0x08bb, 0x2702): | 1149 | case USB_ID(0x08bb, 0x2702): |
| @@ -1044,21 +1154,54 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void | |||
| 1044 | break; | 1154 | break; |
| 1045 | } | 1155 | } |
| 1046 | if (channels > 0) | 1156 | if (channels > 0) |
| 1047 | first_ch_bits = snd_usb_combine_bytes(ftr->bmaControls + csize, csize); | 1157 | first_ch_bits = snd_usb_combine_bytes(bmaControls + csize, csize); |
| 1048 | else | 1158 | else |
| 1049 | first_ch_bits = 0; | 1159 | first_ch_bits = 0; |
| 1050 | /* check all control types */ | 1160 | |
| 1051 | for (i = 0; i < 10; i++) { | 1161 | if (state->mixer->protocol == UAC_VERSION_1) { |
| 1052 | unsigned int ch_bits = 0; | 1162 | /* check all control types */ |
| 1053 | for (j = 0; j < channels; j++) { | 1163 | for (i = 0; i < 10; i++) { |
| 1054 | unsigned int mask = snd_usb_combine_bytes(ftr->bmaControls + csize * (j+1), csize); | 1164 | unsigned int ch_bits = 0; |
| 1055 | if (mask & (1 << i)) | 1165 | for (j = 0; j < channels; j++) { |
| 1056 | ch_bits |= (1 << j); | 1166 | unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize); |
| 1167 | if (mask & (1 << i)) | ||
| 1168 | ch_bits |= (1 << j); | ||
| 1169 | } | ||
| 1170 | /* audio class v1 controls are never read-only */ | ||
| 1171 | if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ | ||
| 1172 | build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, 0); | ||
| 1173 | if (master_bits & (1 << i)) | ||
| 1174 | build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0); | ||
| 1175 | } | ||
| 1176 | } else { /* UAC_VERSION_2 */ | ||
| 1177 | for (i = 0; i < 30/2; i++) { | ||
| 1178 | /* From the USB Audio spec v2.0: | ||
| 1179 | bmaControls() is a (ch+1)-element array of 4-byte bitmaps, | ||
| 1180 | each containing a set of bit pairs. If a Control is present, | ||
| 1181 | it must be Host readable. If a certain Control is not | ||
| 1182 | present then the bit pair must be set to 0b00. | ||
| 1183 | If a Control is present but read-only, the bit pair must be | ||
| 1184 | set to 0b01. If a Control is also Host programmable, the bit | ||
| 1185 | pair must be set to 0b11. The value 0b10 is not allowed. */ | ||
| 1186 | unsigned int ch_bits = 0; | ||
| 1187 | unsigned int ch_read_only = 0; | ||
| 1188 | |||
| 1189 | for (j = 0; j < channels; j++) { | ||
| 1190 | unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize); | ||
| 1191 | if (mask & (1 << (i * 2))) { | ||
| 1192 | ch_bits |= (1 << j); | ||
| 1193 | if (~mask & (1 << ((i * 2) + 1))) | ||
| 1194 | ch_read_only |= (1 << j); | ||
| 1195 | } | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | /* FIXME: the whole unit is read-only if any of the channels is marked read-only */ | ||
| 1199 | if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ | ||
| 1200 | build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, !!ch_read_only); | ||
| 1201 | if (master_bits & (1 << i * 2)) | ||
| 1202 | build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, | ||
| 1203 | ~master_bits & (1 << ((i * 2) + 1))); | ||
| 1057 | } | 1204 | } |
| 1058 | if (ch_bits & 1) /* the first channel must be set (for ease of programming) */ | ||
| 1059 | build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid); | ||
| 1060 | if (master_bits & (1 << i)) | ||
| 1061 | build_feature_ctl(state, _ftr, 0, i, &iterm, unitid); | ||
| 1062 | } | 1205 | } |
| 1063 | 1206 | ||
| 1064 | return 0; | 1207 | return 0; |
| @@ -1100,7 +1243,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, | |||
| 1100 | cval->control = in_ch + 1; /* based on 1 */ | 1243 | cval->control = in_ch + 1; /* based on 1 */ |
| 1101 | cval->val_type = USB_MIXER_S16; | 1244 | cval->val_type = USB_MIXER_S16; |
| 1102 | for (i = 0; i < num_outs; i++) { | 1245 | for (i = 0; i < num_outs; i++) { |
| 1103 | if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc), in_ch, i, num_outs)) { | 1246 | if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol), in_ch, i, num_outs)) { |
| 1104 | cval->cmask |= (1 << i); | 1247 | cval->cmask |= (1 << i); |
| 1105 | cval->channels++; | 1248 | cval->channels++; |
| 1106 | } | 1249 | } |
| @@ -1164,7 +1307,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r | |||
| 1164 | int och, ich_has_controls = 0; | 1307 | int och, ich_has_controls = 0; |
| 1165 | 1308 | ||
| 1166 | for (och = 0; och < num_outs; ++och) { | 1309 | for (och = 0; och < num_outs; ++och) { |
| 1167 | if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc), | 1310 | if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol), |
| 1168 | ich, och, num_outs)) { | 1311 | ich, och, num_outs)) { |
| 1169 | ich_has_controls = 1; | 1312 | ich_has_controls = 1; |
| 1170 | break; | 1313 | break; |
| @@ -1343,7 +1486,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw | |||
| 1343 | 0, NULL, default_value_info | 1486 | 0, NULL, default_value_info |
| 1344 | }; | 1487 | }; |
| 1345 | 1488 | ||
| 1346 | if (desc->bLength < 13 || desc->bLength < 13 + num_ins || desc->bLength < num_ins + uac_processing_unit_bControlSize(desc)) { | 1489 | if (desc->bLength < 13 || desc->bLength < 13 + num_ins || |
| 1490 | desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) { | ||
| 1347 | snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid); | 1491 | snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid); |
| 1348 | return -EINVAL; | 1492 | return -EINVAL; |
| 1349 | } | 1493 | } |
| @@ -1361,7 +1505,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw | |||
| 1361 | info = &default_info; | 1505 | info = &default_info; |
| 1362 | 1506 | ||
| 1363 | for (valinfo = info->values; valinfo->control; valinfo++) { | 1507 | for (valinfo = info->values; valinfo->control; valinfo++) { |
| 1364 | __u8 *controls = uac_processing_unit_bmControls(desc); | 1508 | __u8 *controls = uac_processing_unit_bmControls(desc, state->mixer->protocol); |
| 1365 | 1509 | ||
| 1366 | if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1)))) | 1510 | if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1)))) |
| 1367 | continue; | 1511 | continue; |
| @@ -1381,7 +1525,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw | |||
| 1381 | 1525 | ||
| 1382 | /* get min/max values */ | 1526 | /* get min/max values */ |
| 1383 | if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) { | 1527 | if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) { |
| 1384 | __u8 *control_spec = uac_processing_unit_specific(desc); | 1528 | __u8 *control_spec = uac_processing_unit_specific(desc, state->mixer->protocol); |
| 1385 | /* FIXME: hard-coded */ | 1529 | /* FIXME: hard-coded */ |
| 1386 | cval->min = 1; | 1530 | cval->min = 1; |
| 1387 | cval->max = control_spec[0]; | 1531 | cval->max = control_spec[0]; |
| @@ -1414,7 +1558,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw | |||
| 1414 | else if (info->name) | 1558 | else if (info->name) |
| 1415 | strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); | 1559 | strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); |
| 1416 | else { | 1560 | else { |
| 1417 | nameid = uac_processing_unit_iProcessing(desc); | 1561 | nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol); |
| 1418 | len = 0; | 1562 | len = 0; |
| 1419 | if (nameid) | 1563 | if (nameid) |
| 1420 | len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); | 1564 | len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); |
| @@ -1676,9 +1820,17 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) | |||
| 1676 | case UAC_FEATURE_UNIT: | 1820 | case UAC_FEATURE_UNIT: |
| 1677 | return parse_audio_feature_unit(state, unitid, p1); | 1821 | return parse_audio_feature_unit(state, unitid, p1); |
| 1678 | case UAC_PROCESSING_UNIT_V1: | 1822 | case UAC_PROCESSING_UNIT_V1: |
| 1679 | return parse_audio_processing_unit(state, unitid, p1); | 1823 | /* UAC2_EFFECT_UNIT has the same value */ |
| 1824 | if (state->mixer->protocol == UAC_VERSION_1) | ||
| 1825 | return parse_audio_processing_unit(state, unitid, p1); | ||
| 1826 | else | ||
| 1827 | return 0; /* FIXME - effect units not implemented yet */ | ||
| 1680 | case UAC_EXTENSION_UNIT_V1: | 1828 | case UAC_EXTENSION_UNIT_V1: |
| 1681 | return parse_audio_extension_unit(state, unitid, p1); | 1829 | /* UAC2_PROCESSING_UNIT_V2 has the same value */ |
| 1830 | if (state->mixer->protocol == UAC_VERSION_1) | ||
| 1831 | return parse_audio_extension_unit(state, unitid, p1); | ||
| 1832 | else /* UAC_VERSION_2 */ | ||
| 1833 | return parse_audio_processing_unit(state, unitid, p1); | ||
| 1682 | default: | 1834 | default: |
| 1683 | snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); | 1835 | snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); |
| 1684 | return -EINVAL; | 1836 | return -EINVAL; |
| @@ -1711,11 +1863,11 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) | |||
| 1711 | */ | 1863 | */ |
| 1712 | static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) | 1864 | static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) |
| 1713 | { | 1865 | { |
| 1714 | struct uac_output_terminal_descriptor_v1 *desc; | ||
| 1715 | struct mixer_build state; | 1866 | struct mixer_build state; |
| 1716 | int err; | 1867 | int err; |
| 1717 | const struct usbmix_ctl_map *map; | 1868 | const struct usbmix_ctl_map *map; |
| 1718 | struct usb_host_interface *hostif; | 1869 | struct usb_host_interface *hostif; |
| 1870 | void *p; | ||
| 1719 | 1871 | ||
| 1720 | hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; | 1872 | hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0]; |
| 1721 | memset(&state, 0, sizeof(state)); | 1873 | memset(&state, 0, sizeof(state)); |
| @@ -1734,18 +1886,35 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) | |||
| 1734 | } | 1886 | } |
| 1735 | } | 1887 | } |
| 1736 | 1888 | ||
| 1737 | desc = NULL; | 1889 | p = NULL; |
| 1738 | while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, UAC_OUTPUT_TERMINAL)) != NULL) { | 1890 | while ((p = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) { |
| 1739 | if (desc->bLength < 9) | 1891 | if (mixer->protocol == UAC_VERSION_1) { |
| 1740 | continue; /* invalid descriptor? */ | 1892 | struct uac_output_terminal_descriptor_v1 *desc = p; |
| 1741 | set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */ | 1893 | |
| 1742 | state.oterm.id = desc->bTerminalID; | 1894 | if (desc->bLength < sizeof(*desc)) |
| 1743 | state.oterm.type = le16_to_cpu(desc->wTerminalType); | 1895 | continue; /* invalid descriptor? */ |
| 1744 | state.oterm.name = desc->iTerminal; | 1896 | set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */ |
| 1745 | err = parse_audio_unit(&state, desc->bSourceID); | 1897 | state.oterm.id = desc->bTerminalID; |
| 1746 | if (err < 0) | 1898 | state.oterm.type = le16_to_cpu(desc->wTerminalType); |
| 1747 | return err; | 1899 | state.oterm.name = desc->iTerminal; |
| 1900 | err = parse_audio_unit(&state, desc->bSourceID); | ||
| 1901 | if (err < 0) | ||
| 1902 | return err; | ||
| 1903 | } else { /* UAC_VERSION_2 */ | ||
| 1904 | struct uac2_output_terminal_descriptor *desc = p; | ||
| 1905 | |||
| 1906 | if (desc->bLength < sizeof(*desc)) | ||
| 1907 | continue; /* invalid descriptor? */ | ||
| 1908 | set_bit(desc->bTerminalID, state.unitbitmap); /* mark terminal ID as visited */ | ||
| 1909 | state.oterm.id = desc->bTerminalID; | ||
| 1910 | state.oterm.type = le16_to_cpu(desc->wTerminalType); | ||
| 1911 | state.oterm.name = desc->iTerminal; | ||
| 1912 | err = parse_audio_unit(&state, desc->bSourceID); | ||
| 1913 | if (err < 0) | ||
| 1914 | return err; | ||
| 1915 | } | ||
| 1748 | } | 1916 | } |
| 1917 | |||
| 1749 | return 0; | 1918 | return 0; |
| 1750 | } | 1919 | } |
| 1751 | 1920 | ||
| @@ -1868,7 +2037,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, | |||
| 1868 | struct usb_mixer_interface *mixer; | 2037 | struct usb_mixer_interface *mixer; |
| 1869 | struct snd_info_entry *entry; | 2038 | struct snd_info_entry *entry; |
| 1870 | struct usb_host_interface *host_iface; | 2039 | struct usb_host_interface *host_iface; |
| 1871 | int err, protocol; | 2040 | int err; |
| 1872 | 2041 | ||
| 1873 | strcpy(chip->card->mixername, "USB Mixer"); | 2042 | strcpy(chip->card->mixername, "USB Mixer"); |
| 1874 | 2043 | ||
| @@ -1886,14 +2055,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, | |||
| 1886 | } | 2055 | } |
| 1887 | 2056 | ||
| 1888 | host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0]; | 2057 | host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0]; |
| 1889 | protocol = host_iface->desc.bInterfaceProtocol; | 2058 | mixer->protocol = host_iface->desc.bInterfaceProtocol; |
| 1890 | |||
| 1891 | /* FIXME! */ | ||
| 1892 | if (protocol != UAC_VERSION_1) { | ||
| 1893 | snd_printk(KERN_WARNING "mixer interface protocol 0x%02x not yet supported\n", | ||
| 1894 | protocol); | ||
| 1895 | return 0; | ||
| 1896 | } | ||
| 1897 | 2059 | ||
| 1898 | if ((err = snd_usb_mixer_controls(mixer)) < 0 || | 2060 | if ((err = snd_usb_mixer_controls(mixer)) < 0 || |
| 1899 | (err = snd_usb_mixer_status_create(mixer)) < 0) | 2061 | (err = snd_usb_mixer_status_create(mixer)) < 0) |
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 63101ae201cc..130123854a6c 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h | |||
| @@ -10,6 +10,9 @@ struct usb_mixer_interface { | |||
| 10 | /* array[MAX_ID_ELEMS], indexed by unit id */ | 10 | /* array[MAX_ID_ELEMS], indexed by unit id */ |
| 11 | struct usb_mixer_elem_info **id_elems; | 11 | struct usb_mixer_elem_info **id_elems; |
| 12 | 12 | ||
| 13 | /* the usb audio specification version this interface complies to */ | ||
| 14 | int protocol; | ||
| 15 | |||
| 13 | /* Sound Blaster remote control stuff */ | 16 | /* Sound Blaster remote control stuff */ |
| 14 | const struct rc_config *rc_cfg; | 17 | const struct rc_config *rc_cfg; |
| 15 | u32 rc_code; | 18 | u32 rc_code; |
