Commit 29088fef3e3f62147c1dd53d764da4f04bf3188d
Committed by
Takashi Iwai
1 parent
015eb0b081
Exists in
master
and in
7 other branches
ALSA: usb-audio: support multiple formats with audio class v2 devices
Change the parser to correctly handle v2 descriptors with multiple format bits set. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Showing 2 changed files with 39 additions and 56 deletions Side-by-side Diff
sound/usb/format.c
... | ... | @@ -37,19 +37,20 @@ |
37 | 37 | * @format: the format tag (wFormatTag) |
38 | 38 | * @fmt: the format type descriptor |
39 | 39 | */ |
40 | -static int parse_audio_format_i_type(struct snd_usb_audio *chip, | |
40 | +static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, | |
41 | 41 | struct audioformat *fp, |
42 | 42 | int format, void *_fmt, |
43 | 43 | int protocol) |
44 | 44 | { |
45 | - int pcm_format, i; | |
46 | 45 | int sample_width, sample_bytes; |
46 | + u64 pcm_formats; | |
47 | 47 | |
48 | 48 | switch (protocol) { |
49 | 49 | case UAC_VERSION_1: { |
50 | 50 | struct uac_format_type_i_discrete_descriptor *fmt = _fmt; |
51 | 51 | sample_width = fmt->bBitResolution; |
52 | 52 | sample_bytes = fmt->bSubframeSize; |
53 | + format = 1 << format; | |
53 | 54 | break; |
54 | 55 | } |
55 | 56 | |
... | ... | @@ -57,24 +58,7 @@ |
57 | 58 | struct uac_format_type_i_ext_descriptor *fmt = _fmt; |
58 | 59 | sample_width = fmt->bBitResolution; |
59 | 60 | sample_bytes = fmt->bSubslotSize; |
60 | - | |
61 | - /* | |
62 | - * FIXME | |
63 | - * USB audio class v2 devices specify a bitmap of possible | |
64 | - * audio formats rather than one fix value. For now, we just | |
65 | - * pick one of them and report that as the only possible | |
66 | - * value for this setting. | |
67 | - * The bit allocation map is in fact compatible to the | |
68 | - * wFormatTag of the v1 AS streaming descriptors, which is why | |
69 | - * we can simply map the matrix. | |
70 | - */ | |
71 | - | |
72 | - for (i = 0; i < 5; i++) | |
73 | - if (format & (1UL << i)) { | |
74 | - format = i + 1; | |
75 | - break; | |
76 | - } | |
77 | - | |
61 | + format <<= 1; | |
78 | 62 | break; |
79 | 63 | } |
80 | 64 | |
81 | 65 | |
82 | 66 | |
... | ... | @@ -82,15 +66,15 @@ |
82 | 66 | return -EINVAL; |
83 | 67 | } |
84 | 68 | |
85 | - /* FIXME: correct endianess and sign? */ | |
86 | - pcm_format = -1; | |
69 | + pcm_formats = 0; | |
87 | 70 | |
88 | - switch (format) { | |
89 | - case UAC_FORMAT_TYPE_I_UNDEFINED: /* some devices don't define this correctly... */ | |
71 | + if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) { | |
72 | + /* some devices don't define this correctly... */ | |
90 | 73 | snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n", |
91 | 74 | chip->dev->devnum, fp->iface, fp->altsetting); |
92 | - /* fall-through */ | |
93 | - case UAC_FORMAT_TYPE_I_PCM: | |
75 | + format = 1 << UAC_FORMAT_TYPE_I_PCM; | |
76 | + } | |
77 | + if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) { | |
94 | 78 | if (sample_width > sample_bytes * 8) { |
95 | 79 | snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n", |
96 | 80 | chip->dev->devnum, fp->iface, fp->altsetting, |
97 | 81 | |
98 | 82 | |
99 | 83 | |
100 | 84 | |
101 | 85 | |
... | ... | @@ -99,22 +83,22 @@ |
99 | 83 | /* check the format byte size */ |
100 | 84 | switch (sample_bytes) { |
101 | 85 | case 1: |
102 | - pcm_format = SNDRV_PCM_FORMAT_S8; | |
86 | + pcm_formats |= SNDRV_PCM_FMTBIT_S8; | |
103 | 87 | break; |
104 | 88 | case 2: |
105 | 89 | if (snd_usb_is_big_endian_format(chip, fp)) |
106 | - pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */ | |
90 | + pcm_formats |= SNDRV_PCM_FMTBIT_S16_BE; /* grrr, big endian!! */ | |
107 | 91 | else |
108 | - pcm_format = SNDRV_PCM_FORMAT_S16_LE; | |
92 | + pcm_formats |= SNDRV_PCM_FMTBIT_S16_LE; | |
109 | 93 | break; |
110 | 94 | case 3: |
111 | 95 | if (snd_usb_is_big_endian_format(chip, fp)) |
112 | - pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */ | |
96 | + pcm_formats |= SNDRV_PCM_FMTBIT_S24_3BE; /* grrr, big endian!! */ | |
113 | 97 | else |
114 | - pcm_format = SNDRV_PCM_FORMAT_S24_3LE; | |
98 | + pcm_formats |= SNDRV_PCM_FMTBIT_S24_3LE; | |
115 | 99 | break; |
116 | 100 | case 4: |
117 | - pcm_format = SNDRV_PCM_FORMAT_S32_LE; | |
101 | + pcm_formats |= SNDRV_PCM_FMTBIT_S32_LE; | |
118 | 102 | break; |
119 | 103 | default: |
120 | 104 | snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n", |
121 | 105 | |
122 | 106 | |
123 | 107 | |
... | ... | @@ -122,30 +106,29 @@ |
122 | 106 | sample_width, sample_bytes); |
123 | 107 | break; |
124 | 108 | } |
125 | - break; | |
126 | - case UAC_FORMAT_TYPE_I_PCM8: | |
127 | - pcm_format = SNDRV_PCM_FORMAT_U8; | |
128 | - | |
109 | + } | |
110 | + if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) { | |
129 | 111 | /* Dallas DS4201 workaround: it advertises U8 format, but really |
130 | 112 | supports S8. */ |
131 | 113 | if (chip->usb_id == USB_ID(0x04fa, 0x4201)) |
132 | - pcm_format = SNDRV_PCM_FORMAT_S8; | |
133 | - break; | |
134 | - case UAC_FORMAT_TYPE_I_IEEE_FLOAT: | |
135 | - pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE; | |
136 | - break; | |
137 | - case UAC_FORMAT_TYPE_I_ALAW: | |
138 | - pcm_format = SNDRV_PCM_FORMAT_A_LAW; | |
139 | - break; | |
140 | - case UAC_FORMAT_TYPE_I_MULAW: | |
141 | - pcm_format = SNDRV_PCM_FORMAT_MU_LAW; | |
142 | - break; | |
143 | - default: | |
144 | - snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n", | |
114 | + pcm_formats |= SNDRV_PCM_FMTBIT_S8; | |
115 | + else | |
116 | + pcm_formats |= SNDRV_PCM_FMTBIT_U8; | |
117 | + } | |
118 | + if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) { | |
119 | + pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; | |
120 | + } | |
121 | + if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) { | |
122 | + pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW; | |
123 | + } | |
124 | + if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) { | |
125 | + pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW; | |
126 | + } | |
127 | + if (format & ~0x3f) { | |
128 | + snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n", | |
145 | 129 | chip->dev->devnum, fp->iface, fp->altsetting, format); |
146 | - break; | |
147 | 130 | } |
148 | - return pcm_format; | |
131 | + return pcm_formats; | |
149 | 132 | } |
150 | 133 | |
151 | 134 | |
152 | 135 | |
153 | 136 | |
... | ... | @@ -317,13 +300,13 @@ |
317 | 300 | default: |
318 | 301 | pcm_format = SNDRV_PCM_FORMAT_S16_LE; |
319 | 302 | } |
303 | + fp->formats = 1uLL << pcm_format; | |
320 | 304 | } else { |
321 | - pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol); | |
322 | - if (pcm_format < 0) | |
305 | + fp->formats = parse_audio_format_i_type(chip, fp, format, | |
306 | + fmt, protocol); | |
307 | + if (!fp->formats) | |
323 | 308 | return -1; |
324 | 309 | } |
325 | - | |
326 | - fp->formats = 1uLL << pcm_format; | |
327 | 310 | |
328 | 311 | /* gather possible sample rates */ |
329 | 312 | /* audio class v1 reports possible sample rates as part of the |
sound/usb/quirks-table.h
... | ... | @@ -2203,7 +2203,7 @@ |
2203 | 2203 | .ifnum = 1, |
2204 | 2204 | .type = QUIRK_AUDIO_FIXED_ENDPOINT, |
2205 | 2205 | .data = &(const struct audioformat) { |
2206 | - .format = SNDRV_PCM_FORMAT_S24_3BE, | |
2206 | + .formats = SNDRV_PCM_FMTBIT_S24_3BE, | |
2207 | 2207 | .channels = 2, |
2208 | 2208 | .iface = 1, |
2209 | 2209 | .altsetting = 1, |