diff options
author | Daniel Mack <daniel@caiaq.de> | 2010-03-04 13:46:17 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2010-03-05 02:19:17 -0500 |
commit | 767d75ad1c08c31646498a13837a5c59db90ccad (patch) | |
tree | 2f9196d65f63e7da72904f98a46039946e575401 /sound/usb/pcm.c | |
parent | 29088fef3e3f62147c1dd53d764da4f04bf3188d (diff) |
ALSA: usb-audio: add support for samplerate setting on v2 devices
Sample rate setting is done with a 4-byte long class request that
addresses the interface.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r-- | sound/usb/pcm.c | 173 |
1 files changed, 127 insertions, 46 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index bd0f84f3a9d2..e0f3f87f99a0 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c | |||
@@ -107,69 +107,150 @@ static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned | |||
107 | return found; | 107 | return found; |
108 | } | 108 | } |
109 | 109 | ||
110 | static int init_pitch_v1(struct snd_usb_audio *chip, int iface, | ||
111 | struct usb_host_interface *alts, | ||
112 | struct audioformat *fmt) | ||
113 | { | ||
114 | struct usb_device *dev = chip->dev; | ||
115 | unsigned int ep; | ||
116 | unsigned char data[1]; | ||
117 | int err; | ||
118 | |||
119 | ep = get_endpoint(alts, 0)->bEndpointAddress; | ||
120 | |||
121 | /* if endpoint doesn't have pitch control, bail out */ | ||
122 | if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL)) | ||
123 | return 0; | ||
124 | |||
125 | data[0] = 1; | ||
126 | if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, | ||
127 | USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, | ||
128 | UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, | ||
129 | data, sizeof(data), 1000)) < 0) { | ||
130 | snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", | ||
131 | dev->devnum, iface, ep); | ||
132 | return err; | ||
133 | } | ||
134 | |||
135 | return 0; | ||
136 | } | ||
110 | 137 | ||
111 | /* | 138 | /* |
112 | * initialize the picth control and sample rate | 139 | * initialize the picth control and sample rate |
113 | */ | 140 | */ |
114 | int snd_usb_init_pitch(struct usb_device *dev, int iface, | 141 | int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, |
115 | struct usb_host_interface *alts, | 142 | struct usb_host_interface *alts, |
116 | struct audioformat *fmt) | 143 | struct audioformat *fmt) |
117 | { | 144 | { |
145 | struct usb_interface_descriptor *altsd = get_iface_desc(alts); | ||
146 | |||
147 | switch (altsd->bInterfaceProtocol) { | ||
148 | case UAC_VERSION_1: | ||
149 | return init_pitch_v1(chip, iface, alts, fmt); | ||
150 | |||
151 | case UAC_VERSION_2: | ||
152 | /* not implemented yet */ | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | return -EINVAL; | ||
157 | } | ||
158 | |||
159 | static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, | ||
160 | struct usb_host_interface *alts, | ||
161 | struct audioformat *fmt, int rate) | ||
162 | { | ||
163 | struct usb_device *dev = chip->dev; | ||
118 | unsigned int ep; | 164 | unsigned int ep; |
119 | unsigned char data[1]; | 165 | unsigned char data[3]; |
120 | int err; | 166 | int err, crate; |
121 | 167 | ||
122 | ep = get_endpoint(alts, 0)->bEndpointAddress; | 168 | ep = get_endpoint(alts, 0)->bEndpointAddress; |
123 | /* if endpoint has pitch control, enable it */ | 169 | /* if endpoint doesn't have sampling rate control, bail out */ |
124 | if (fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL) { | 170 | if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) { |
125 | data[0] = 1; | 171 | snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n", |
126 | if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, | 172 | dev->devnum, iface, fmt->altsetting); |
127 | USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, | 173 | return 0; |
128 | UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) { | 174 | } |
129 | snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", | 175 | |
130 | dev->devnum, iface, ep); | 176 | data[0] = rate; |
131 | return err; | 177 | data[1] = rate >> 8; |
132 | } | 178 | data[2] = rate >> 16; |
179 | if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, | ||
180 | USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, | ||
181 | UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, | ||
182 | data, sizeof(data), 1000)) < 0) { | ||
183 | snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n", | ||
184 | dev->devnum, iface, fmt->altsetting, rate, ep); | ||
185 | return err; | ||
133 | } | 186 | } |
187 | if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, | ||
188 | USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, | ||
189 | UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, | ||
190 | data, sizeof(data), 1000)) < 0) { | ||
191 | snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n", | ||
192 | dev->devnum, iface, fmt->altsetting, ep); | ||
193 | return 0; /* some devices don't support reading */ | ||
194 | } | ||
195 | crate = data[0] | (data[1] << 8) | (data[2] << 16); | ||
196 | if (crate != rate) { | ||
197 | snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); | ||
198 | // runtime->rate = crate; | ||
199 | } | ||
200 | |||
134 | return 0; | 201 | return 0; |
135 | } | 202 | } |
136 | 203 | ||
137 | int snd_usb_init_sample_rate(struct usb_device *dev, int iface, | 204 | static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, |
205 | struct usb_host_interface *alts, | ||
206 | struct audioformat *fmt, int rate) | ||
207 | { | ||
208 | struct usb_device *dev = chip->dev; | ||
209 | unsigned char data[4]; | ||
210 | int err, crate; | ||
211 | |||
212 | data[0] = rate; | ||
213 | data[1] = rate >> 8; | ||
214 | data[2] = rate >> 16; | ||
215 | data[3] = rate >> 24; | ||
216 | if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, | ||
217 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | ||
218 | 0x0100, chip->clock_id << 8, | ||
219 | data, sizeof(data), 1000)) < 0) { | ||
220 | snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n", | ||
221 | dev->devnum, iface, fmt->altsetting, rate); | ||
222 | return err; | ||
223 | } | ||
224 | if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, | ||
225 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
226 | 0x0100, chip->clock_id << 8, | ||
227 | data, sizeof(data), 1000)) < 0) { | ||
228 | snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n", | ||
229 | dev->devnum, iface, fmt->altsetting); | ||
230 | return err; | ||
231 | } | ||
232 | crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); | ||
233 | if (crate != rate) | ||
234 | snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, | ||
138 | struct usb_host_interface *alts, | 240 | struct usb_host_interface *alts, |
139 | struct audioformat *fmt, int rate) | 241 | struct audioformat *fmt, int rate) |
140 | { | 242 | { |
141 | unsigned int ep; | 243 | struct usb_interface_descriptor *altsd = get_iface_desc(alts); |
142 | unsigned char data[3]; | ||
143 | int err; | ||
144 | 244 | ||
145 | ep = get_endpoint(alts, 0)->bEndpointAddress; | 245 | switch (altsd->bInterfaceProtocol) { |
146 | /* if endpoint has sampling rate control, set it */ | 246 | case UAC_VERSION_1: |
147 | if (fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE) { | 247 | return set_sample_rate_v1(chip, iface, alts, fmt, rate); |
148 | int crate; | 248 | |
149 | data[0] = rate; | 249 | case UAC_VERSION_2: |
150 | data[1] = rate >> 8; | 250 | return set_sample_rate_v2(chip, iface, alts, fmt, rate); |
151 | data[2] = rate >> 16; | ||
152 | if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, | ||
153 | USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, | ||
154 | UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) { | ||
155 | snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n", | ||
156 | dev->devnum, iface, fmt->altsetting, rate, ep); | ||
157 | return err; | ||
158 | } | ||
159 | if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, | ||
160 | USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, | ||
161 | UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) { | ||
162 | snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n", | ||
163 | dev->devnum, iface, fmt->altsetting, ep); | ||
164 | return 0; /* some devices don't support reading */ | ||
165 | } | ||
166 | crate = data[0] | (data[1] << 8) | (data[2] << 16); | ||
167 | if (crate != rate) { | ||
168 | snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); | ||
169 | // runtime->rate = crate; | ||
170 | } | ||
171 | } | 251 | } |
172 | return 0; | 252 | |
253 | return -EINVAL; | ||
173 | } | 254 | } |
174 | 255 | ||
175 | /* | 256 | /* |
@@ -280,7 +361,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) | |||
280 | if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX) | 361 | if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX) |
281 | subs->fill_max = 1; | 362 | subs->fill_max = 1; |
282 | 363 | ||
283 | if ((err = snd_usb_init_pitch(dev, subs->interface, alts, fmt)) < 0) | 364 | if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0) |
284 | return err; | 365 | return err; |
285 | 366 | ||
286 | subs->cur_audiofmt = fmt; | 367 | subs->cur_audiofmt = fmt; |
@@ -343,7 +424,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, | |||
343 | struct usb_interface *iface; | 424 | struct usb_interface *iface; |
344 | iface = usb_ifnum_to_if(subs->dev, fmt->iface); | 425 | iface = usb_ifnum_to_if(subs->dev, fmt->iface); |
345 | alts = &iface->altsetting[fmt->altset_idx]; | 426 | alts = &iface->altsetting[fmt->altset_idx]; |
346 | ret = snd_usb_init_sample_rate(subs->dev, subs->interface, alts, fmt, rate); | 427 | ret = snd_usb_init_sample_rate(subs->stream->chip, subs->interface, alts, fmt, rate); |
347 | if (ret < 0) | 428 | if (ret < 0) |
348 | return ret; | 429 | return ret; |
349 | subs->cur_rate = rate; | 430 | subs->cur_rate = rate; |