Commit | Line | Data |
---|---|---|
705ececd MG |
1 | /* |
2 | * Line6 Linux USB driver - 0.8.0 | |
3 | * | |
4 | * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at) | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation, version 2. | |
9 | * | |
10 | */ | |
11 | ||
12 | #include "driver.h" | |
13 | ||
14 | #include <sound/core.h> | |
15 | #include <sound/pcm.h> | |
16 | #include <sound/pcm_params.h> | |
17 | ||
18 | #include "audio.h" | |
19 | #include "pcm.h" | |
20 | #include "pod.h" | |
21 | ||
22 | ||
23 | /* | |
24 | Software stereo volume control. | |
25 | */ | |
26 | static void change_volume(struct urb *urb_out, int volume[], int bytes_per_frame) | |
27 | { | |
28 | int chn = 0; | |
29 | ||
30 | if(volume[0] == 256 && volume[1] == 256) | |
31 | return; /* maximum volume - no change */ | |
32 | ||
33 | if(bytes_per_frame == 4) { | |
34 | short *p, *buf_end; | |
35 | p = (short *)urb_out->transfer_buffer; | |
36 | buf_end = p + urb_out->transfer_buffer_length / sizeof(*p); | |
37 | ||
38 | for(; p < buf_end; ++p) { | |
39 | *p = (*p * volume[chn & 1]) >> 8; | |
40 | ++chn; | |
41 | } | |
42 | } | |
43 | else if(bytes_per_frame == 6) { | |
44 | unsigned char *p, *buf_end; | |
45 | p = (unsigned char *)urb_out->transfer_buffer; | |
46 | buf_end = p + urb_out->transfer_buffer_length; | |
47 | ||
48 | for(; p < buf_end; p += 3) { | |
49 | int val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16); | |
50 | val = (val * volume[chn & 1]) >> 8; | |
51 | p[0] = val; | |
52 | p[1] = val >> 8; | |
53 | p[2] = val >> 16; | |
54 | ++chn; | |
55 | } | |
56 | } | |
57 | } | |
58 | ||
59 | /* | |
60 | Find a free URB, prepare audio data, and submit URB. | |
61 | */ | |
62 | static int submit_audio_out_urb(struct snd_pcm_substream *substream) | |
63 | { | |
64 | int index; | |
65 | unsigned long flags; | |
66 | int i, urb_size, urb_frames; | |
67 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | |
68 | const int bytes_per_frame = line6pcm->properties->bytes_per_frame; | |
69 | const int frame_increment = line6pcm->properties->snd_line6_rates.rats[0].num_min; | |
70 | const int frame_factor = line6pcm->properties->snd_line6_rates.rats[0].den * (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); | |
71 | struct snd_pcm_runtime *runtime = substream->runtime; | |
72 | struct urb *urb_out; | |
73 | ||
74 | spin_lock_irqsave(&line6pcm->lock_audio_out, flags); | |
75 | index = find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS); | |
76 | ||
77 | if(index < 0 || index >= LINE6_ISO_BUFFERS) { | |
78 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | |
79 | dev_err(s2m(substream), "no free URB found\n"); | |
80 | return -EINVAL; | |
81 | } | |
82 | ||
83 | urb_out = line6pcm->urb_audio_out[index]; | |
84 | urb_size = 0; | |
85 | ||
86 | for(i = 0; i < LINE6_ISO_PACKETS; ++i) { | |
87 | /* compute frame size for given sampling rate */ | |
88 | int n, fs; | |
89 | struct usb_iso_packet_descriptor *fout = &urb_out->iso_frame_desc[i]; | |
90 | line6pcm->count_out += frame_increment; | |
91 | n = line6pcm->count_out / frame_factor; | |
92 | line6pcm->count_out -= n * frame_factor; | |
93 | fs = n * bytes_per_frame; | |
94 | fout->offset = urb_size; | |
95 | fout->length = fs; | |
96 | urb_size += fs; | |
97 | } | |
98 | ||
99 | urb_frames = urb_size / bytes_per_frame; | |
100 | ||
101 | if(test_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags)) { | |
102 | urb_out->transfer_buffer = line6pcm->wrap_out; | |
103 | memset(line6pcm->wrap_out, 0, urb_size); | |
104 | } | |
105 | else { | |
106 | if(line6pcm->pos_out + urb_frames > runtime->buffer_size) { | |
107 | /* | |
108 | The transferred area goes over buffer boundary, | |
109 | copy the data to the temp buffer. | |
110 | */ | |
111 | int len; | |
112 | len = runtime->buffer_size - line6pcm->pos_out; | |
113 | urb_out->transfer_buffer = line6pcm->wrap_out; | |
114 | ||
115 | if(len > 0) { | |
116 | memcpy(line6pcm->wrap_out, runtime->dma_area + line6pcm->pos_out * bytes_per_frame, len * bytes_per_frame); | |
117 | memcpy(line6pcm->wrap_out + len * bytes_per_frame, runtime->dma_area, (urb_frames - len) * bytes_per_frame); | |
118 | } | |
119 | else | |
120 | dev_err(s2m(substream), "driver bug: len = %d\n", len); /* this is somewhat paranoid */ | |
121 | } | |
122 | else { | |
123 | /* set the buffer pointer */ | |
124 | urb_out->transfer_buffer = runtime->dma_area + line6pcm->pos_out * bytes_per_frame; | |
125 | } | |
126 | } | |
127 | ||
128 | if((line6pcm->pos_out += urb_frames) >= runtime->buffer_size) | |
129 | line6pcm->pos_out -= runtime->buffer_size; | |
130 | ||
131 | urb_out->transfer_buffer_length = urb_size; | |
132 | urb_out->context = substream; | |
133 | change_volume(urb_out, line6pcm->volume, bytes_per_frame); | |
134 | ||
135 | #if DO_DUMP_PCM_SEND | |
136 | for(i = 0; i < LINE6_ISO_PACKETS; ++i) { | |
137 | struct usb_iso_packet_descriptor *fout = &urb_out->iso_frame_desc[i]; | |
138 | line6_write_hexdump(line6pcm->line6, 'P', urb_out->transfer_buffer + fout->offset, fout->length); | |
139 | } | |
140 | #endif | |
141 | ||
142 | if(usb_submit_urb(urb_out, GFP_ATOMIC) == 0) | |
143 | set_bit(index, &line6pcm->active_urb_out); | |
144 | else | |
145 | dev_err(s2m(substream), "URB out #%d submission failed\n", index); | |
146 | ||
147 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | |
148 | return 0; | |
149 | } | |
150 | ||
151 | /* | |
152 | Submit all currently available playback URBs. | |
153 | */ | |
154 | static int submit_audio_out_all_urbs(struct snd_pcm_substream *substream) | |
155 | { | |
156 | int ret, i; | |
157 | ||
158 | for(i = 0; i < LINE6_ISO_BUFFERS; ++i) | |
159 | if((ret = submit_audio_out_urb(substream)) < 0) | |
160 | return ret; | |
161 | ||
162 | return 0; | |
163 | } | |
164 | ||
165 | /* | |
166 | Unlink all currently active playback URBs. | |
167 | */ | |
168 | static void unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) | |
169 | { | |
170 | unsigned int i; | |
171 | ||
172 | for(i = LINE6_ISO_BUFFERS; i--;) { | |
173 | if(test_bit(i, &line6pcm->active_urb_out)) { | |
174 | if(!test_and_set_bit(i, &line6pcm->unlink_urb_out)) { | |
175 | struct urb *u = line6pcm->urb_audio_out[i]; | |
176 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | |
177 | u->transfer_flags |= URB_ASYNC_UNLINK; | |
178 | #endif | |
179 | usb_unlink_urb(u); | |
180 | } | |
181 | } | |
182 | } | |
183 | } | |
184 | ||
185 | /* | |
186 | Wait until unlinking of all currently active playback URBs has been finished. | |
187 | */ | |
188 | static void wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) | |
189 | { | |
190 | int timeout = HZ; | |
191 | unsigned int i; | |
192 | int alive; | |
193 | ||
194 | do { | |
195 | alive = 0; | |
196 | for (i = LINE6_ISO_BUFFERS; i--;) { | |
197 | if (test_bit(i, &line6pcm->active_urb_out)) | |
198 | alive++; | |
199 | } | |
200 | if (! alive) | |
201 | break; | |
202 | set_current_state(TASK_UNINTERRUPTIBLE); | |
203 | schedule_timeout(1); | |
204 | } while (--timeout > 0); | |
205 | if (alive) | |
206 | snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); | |
207 | ||
208 | line6pcm->active_urb_out = 0; | |
209 | line6pcm->unlink_urb_out = 0; | |
210 | } | |
211 | ||
212 | /* | |
213 | Unlink all currently active playback URBs, and wait for finishing. | |
214 | */ | |
215 | void unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) | |
216 | { | |
217 | unlink_audio_out_urbs(line6pcm); | |
218 | wait_clear_audio_out_urbs(line6pcm); | |
219 | } | |
220 | ||
221 | /* | |
222 | Callback for completed playback URB. | |
223 | */ | |
224 | static void audio_out_callback(struct urb *urb PT_REGS) | |
225 | { | |
226 | int i, index, length = 0, shutdown = 0; | |
227 | unsigned long flags; | |
228 | ||
229 | struct snd_pcm_substream *substream = (struct snd_pcm_substream *)urb->context; | |
230 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | |
231 | struct snd_pcm_runtime *runtime = substream->runtime; | |
232 | ||
233 | /* find index of URB */ | |
234 | for(index = LINE6_ISO_BUFFERS; index--;) | |
235 | if(urb == line6pcm->urb_audio_out[index]) | |
236 | break; | |
237 | ||
238 | if(index < 0) | |
239 | return; /* URB has been unlinked asynchronously */ | |
240 | ||
241 | for(i = LINE6_ISO_PACKETS; i--;) | |
242 | length += urb->iso_frame_desc[i].length; | |
243 | ||
244 | spin_lock_irqsave(&line6pcm->lock_audio_out, flags); | |
245 | line6pcm->pos_out_done += length / line6pcm->properties->bytes_per_frame; | |
246 | ||
247 | if(line6pcm->pos_out_done >= runtime->buffer_size) | |
248 | line6pcm->pos_out_done -= runtime->buffer_size; | |
249 | ||
250 | clear_bit(index, &line6pcm->active_urb_out); | |
251 | ||
252 | for(i = LINE6_ISO_PACKETS; i--;) | |
253 | if(urb->iso_frame_desc[i].status == -ESHUTDOWN) { | |
254 | shutdown = 1; | |
255 | break; | |
256 | } | |
257 | ||
258 | if(test_bit(index, &line6pcm->unlink_urb_out)) | |
259 | shutdown = 1; | |
260 | ||
261 | spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); | |
262 | ||
263 | if(!shutdown) { | |
264 | submit_audio_out_urb(substream); | |
265 | ||
266 | if((line6pcm->bytes_out += length) >= line6pcm->period_out) { | |
267 | line6pcm->bytes_out -= line6pcm->period_out; | |
268 | snd_pcm_period_elapsed(substream); | |
269 | } | |
270 | } | |
271 | } | |
272 | ||
273 | /* open playback callback */ | |
274 | static int snd_line6_playback_open(struct snd_pcm_substream *substream) | |
275 | { | |
276 | int err; | |
277 | struct snd_pcm_runtime *runtime = substream->runtime; | |
278 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | |
279 | ||
280 | if((err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | |
281 | (&line6pcm->properties->snd_line6_rates))) < 0) | |
282 | return err; | |
283 | ||
284 | runtime->hw = line6pcm->properties->snd_line6_playback_hw; | |
285 | return 0; | |
286 | } | |
287 | ||
288 | /* close playback callback */ | |
289 | static int snd_line6_playback_close(struct snd_pcm_substream *substream) | |
290 | { | |
291 | return 0; | |
292 | } | |
293 | ||
294 | /* hw_params playback callback */ | |
295 | static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) | |
296 | { | |
297 | int ret; | |
298 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | |
299 | ||
300 | /* -- Florian Demski [FD] */ | |
301 | /* don't ask me why, but this fixes the bug on my machine */ | |
302 | if ( line6pcm == NULL ) { | |
303 | if ( substream->pcm == NULL ) | |
304 | return -ENOMEM; | |
305 | if ( substream->pcm->private_data == NULL ) | |
306 | return -ENOMEM; | |
307 | substream->private_data = substream->pcm->private_data; | |
308 | line6pcm = snd_pcm_substream_chip( substream ); | |
309 | } | |
310 | /* -- [FD] end */ | |
311 | ||
312 | if((ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) | |
313 | return ret; | |
314 | ||
315 | line6pcm->period_out = params_period_bytes(hw_params); | |
316 | line6pcm->wrap_out = kmalloc(2 * LINE6_ISO_PACKET_SIZE_MAX, GFP_KERNEL); | |
317 | ||
318 | if(!line6pcm->wrap_out) { | |
319 | dev_err(s2m(substream), "cannot malloc wrap_out\n"); | |
320 | return -ENOMEM; | |
321 | } | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
326 | /* hw_free playback callback */ | |
327 | static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) | |
328 | { | |
329 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | |
330 | unlink_wait_clear_audio_out_urbs(line6pcm); | |
331 | ||
332 | if(line6pcm->wrap_out) { | |
333 | kfree(line6pcm->wrap_out); | |
334 | line6pcm->wrap_out = 0; | |
335 | } | |
336 | ||
337 | return snd_pcm_lib_free_pages(substream); | |
338 | } | |
339 | ||
340 | /* trigger playback callback */ | |
341 | int snd_line6_playback_trigger(struct snd_pcm_substream *substream, int cmd) | |
342 | { | |
343 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | |
344 | int err; | |
345 | line6pcm->count_out = 0; | |
346 | ||
347 | switch(cmd) { | |
348 | case SNDRV_PCM_TRIGGER_START: | |
349 | if(!test_and_set_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) { | |
350 | err = submit_audio_out_all_urbs(substream); | |
351 | ||
352 | if(err < 0) { | |
353 | clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags); | |
354 | return err; | |
355 | } | |
356 | } | |
357 | ||
358 | break; | |
359 | ||
360 | case SNDRV_PCM_TRIGGER_STOP: | |
361 | if(test_and_clear_bit(BIT_RUNNING_PLAYBACK, &line6pcm->flags)) | |
362 | unlink_audio_out_urbs(line6pcm); | |
363 | ||
364 | break; | |
365 | ||
366 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
367 | set_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags); | |
368 | break; | |
369 | ||
370 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
371 | clear_bit(BIT_PAUSE_PLAYBACK, &line6pcm->flags); | |
372 | break; | |
373 | ||
374 | default: | |
375 | return -EINVAL; | |
376 | } | |
377 | ||
378 | return 0; | |
379 | } | |
380 | ||
381 | /* playback pointer callback */ | |
382 | static snd_pcm_uframes_t | |
383 | snd_line6_playback_pointer(struct snd_pcm_substream *substream) | |
384 | { | |
385 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | |
386 | return line6pcm->pos_out_done; | |
387 | } | |
388 | ||
389 | /* playback operators */ | |
390 | struct snd_pcm_ops snd_line6_playback_ops = { | |
391 | .open = snd_line6_playback_open, | |
392 | .close = snd_line6_playback_close, | |
393 | .ioctl = snd_pcm_lib_ioctl, | |
394 | .hw_params = snd_line6_playback_hw_params, | |
395 | .hw_free = snd_line6_playback_hw_free, | |
396 | .prepare = snd_line6_prepare, | |
397 | .trigger = snd_line6_trigger, | |
398 | .pointer = snd_line6_playback_pointer, | |
399 | }; | |
400 | ||
401 | int create_audio_out_urbs(struct snd_line6_pcm *line6pcm) | |
402 | { | |
403 | int i; | |
404 | ||
405 | /* create audio URBs and fill in constant values: */ | |
406 | for(i = 0; i < LINE6_ISO_BUFFERS; ++i) { | |
407 | struct urb *urb; | |
408 | ||
409 | /* URB for audio out: */ | |
410 | urb = line6pcm->urb_audio_out[i] = usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); | |
411 | ||
412 | if(urb == NULL) { | |
413 | dev_err(line6pcm->line6->ifcdev, "Out of memory\n"); | |
414 | return -ENOMEM; | |
415 | } | |
416 | ||
417 | urb->dev = line6pcm->line6->usbdev; | |
418 | urb->pipe = usb_sndisocpipe(line6pcm->line6->usbdev, line6pcm->ep_audio_write & USB_ENDPOINT_NUMBER_MASK); | |
419 | urb->transfer_flags = URB_ISO_ASAP; | |
420 | urb->start_frame = -1; | |
421 | urb->number_of_packets = LINE6_ISO_PACKETS; | |
422 | urb->interval = LINE6_ISO_INTERVAL; | |
423 | urb->error_count = 0; | |
424 | urb->complete = audio_out_callback; | |
425 | } | |
426 | ||
427 | return 0; | |
428 | } |