Commit | Line | Data |
---|---|---|
702422bd T |
1 | /****************************************************************************** |
2 | * * | |
3 | * easycap_sound.c * | |
4 | * * | |
5 | * Audio driver for EasyCAP USB2.0 Video Capture Device DC60 * | |
6 | * * | |
7 | * * | |
8 | ******************************************************************************/ | |
9 | /* | |
10 | * | |
11 | * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org> | |
12 | * | |
13 | * | |
14 | * This is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License as published by | |
16 | * the Free Software Foundation; either version 2 of the License, or | |
17 | * (at your option) any later version. | |
18 | * | |
19 | * The software is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | * GNU General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License | |
25 | * along with this software; if not, write to the Free Software | |
26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
27 | * | |
28 | */ | |
29 | /*****************************************************************************/ | |
30 | ||
31 | #include "easycap.h" | |
702422bd | 32 | |
a9855917 MT |
33 | /*--------------------------------------------------------------------------*/ |
34 | /* | |
35 | * PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE | |
36 | */ | |
37 | /*--------------------------------------------------------------------------*/ | |
38 | static const struct snd_pcm_hardware alsa_hardware = { | |
39 | .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
40 | SNDRV_PCM_INFO_MMAP | | |
41 | SNDRV_PCM_INFO_INTERLEAVED | | |
42 | SNDRV_PCM_INFO_MMAP_VALID, | |
43 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
44 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, | |
45 | .rate_min = 32000, | |
46 | .rate_max = 48000, | |
47 | .channels_min = 2, | |
48 | .channels_max = 2, | |
a75af077 TW |
49 | .buffer_bytes_max = PAGE_SIZE * |
50 | PAGES_PER_AUDIO_FRAGMENT * | |
51 | AUDIO_FRAGMENT_MANY, | |
a9855917 MT |
52 | .period_bytes_min = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT, |
53 | .period_bytes_max = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT * 2, | |
54 | .periods_min = AUDIO_FRAGMENT_MANY, | |
55 | .periods_max = AUDIO_FRAGMENT_MANY * 2, | |
56 | }; | |
57 | ||
a9855917 | 58 | |
98680557 TW |
59 | /*---------------------------------------------------------------------------*/ |
60 | /* | |
61 | * SUBMIT ALL AUDIO URBS. | |
62 | */ | |
63 | /*---------------------------------------------------------------------------*/ | |
96bec7dd | 64 | static int easycap_audio_submit_urbs(struct easycap *peasycap) |
98680557 TW |
65 | { |
66 | struct data_urb *pdata_urb; | |
67 | struct urb *purb; | |
68 | struct list_head *plist_head; | |
69 | int j, isbad, nospc, m, rc; | |
70 | int isbuf; | |
71 | ||
98680557 TW |
72 | if (!peasycap->purb_audio_head) { |
73 | SAM("ERROR: peasycap->urb_audio_head uninitialized\n"); | |
74 | return -EFAULT; | |
75 | } | |
76 | if (!peasycap->pusb_device) { | |
77 | SAM("ERROR: peasycap->pusb_device is NULL\n"); | |
78 | return -EFAULT; | |
79 | } | |
80 | ||
81 | if (peasycap->audio_isoc_streaming) { | |
82 | JOM(4, "already streaming audio urbs\n"); | |
83 | return 0; | |
84 | } | |
85 | ||
86 | JOM(4, "initial submission of all audio urbs\n"); | |
87 | rc = usb_set_interface(peasycap->pusb_device, | |
88 | peasycap->audio_interface, | |
89 | peasycap->audio_altsetting_on); | |
90 | JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", | |
91 | peasycap->audio_interface, | |
92 | peasycap->audio_altsetting_on, rc); | |
93 | ||
94 | isbad = 0; | |
95 | nospc = 0; | |
96 | m = 0; | |
97 | list_for_each(plist_head, peasycap->purb_audio_head) { | |
98 | pdata_urb = list_entry(plist_head, struct data_urb, list_head); | |
99 | if (pdata_urb && pdata_urb->purb) { | |
100 | purb = pdata_urb->purb; | |
101 | isbuf = pdata_urb->isbuf; | |
102 | ||
103 | purb->interval = 1; | |
104 | purb->dev = peasycap->pusb_device; | |
105 | purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, | |
106 | peasycap->audio_endpointnumber); | |
107 | purb->transfer_flags = URB_ISO_ASAP; | |
108 | purb->transfer_buffer = peasycap->audio_isoc_buffer[isbuf].pgo; | |
109 | purb->transfer_buffer_length = peasycap->audio_isoc_buffer_size; | |
110 | purb->complete = easycap_alsa_complete; | |
111 | purb->context = peasycap; | |
112 | purb->start_frame = 0; | |
113 | purb->number_of_packets = peasycap->audio_isoc_framesperdesc; | |
114 | for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { | |
115 | purb->iso_frame_desc[j].offset = j * peasycap->audio_isoc_maxframesize; | |
116 | purb->iso_frame_desc[j].length = peasycap->audio_isoc_maxframesize; | |
117 | } | |
118 | ||
119 | rc = usb_submit_urb(purb, GFP_KERNEL); | |
120 | if (rc) { | |
121 | isbad++; | |
122 | SAM("ERROR: usb_submit_urb() failed" | |
123 | " for urb with rc: -%s: %d\n", | |
124 | strerror(rc), rc); | |
125 | } else { | |
126 | m++; | |
127 | } | |
128 | } else { | |
129 | isbad++; | |
130 | } | |
131 | } | |
132 | if (nospc) { | |
133 | SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc); | |
134 | SAM("..... possibly inadequate USB bandwidth\n"); | |
135 | peasycap->audio_eof = 1; | |
136 | } | |
8b1fad2f TW |
137 | |
138 | if (isbad) | |
139 | easycap_audio_kill_urbs(peasycap); | |
140 | else | |
98680557 | 141 | peasycap->audio_isoc_streaming = m; |
98680557 TW |
142 | |
143 | return 0; | |
144 | } | |
145 | /*---------------------------------------------------------------------------*/ | |
146 | /* | |
147 | * COMMON AUDIO INITIALIZATION | |
148 | */ | |
149 | /*---------------------------------------------------------------------------*/ | |
150 | static int easycap_sound_setup(struct easycap *peasycap) | |
151 | { | |
152 | int rc; | |
153 | ||
154 | JOM(4, "starting initialization\n"); | |
155 | ||
156 | if (!peasycap) { | |
157 | SAY("ERROR: peasycap is NULL.\n"); | |
158 | return -EFAULT; | |
159 | } | |
160 | if (!peasycap->pusb_device) { | |
161 | SAM("ERROR: peasycap->pusb_device is NULL\n"); | |
162 | return -ENODEV; | |
163 | } | |
164 | JOM(16, "0x%08lX=peasycap->pusb_device\n", (long int)peasycap->pusb_device); | |
165 | ||
96bec7dd | 166 | rc = easycap_audio_setup(peasycap); |
98680557 TW |
167 | JOM(8, "audio_setup() returned %i\n", rc); |
168 | ||
169 | if (!peasycap->pusb_device) { | |
170 | SAM("ERROR: peasycap->pusb_device has become NULL\n"); | |
171 | return -ENODEV; | |
172 | } | |
173 | /*---------------------------------------------------------------------------*/ | |
174 | if (!peasycap->pusb_device) { | |
175 | SAM("ERROR: peasycap->pusb_device has become NULL\n"); | |
176 | return -ENODEV; | |
177 | } | |
178 | rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface, | |
179 | peasycap->audio_altsetting_on); | |
180 | JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface, | |
181 | peasycap->audio_altsetting_on, rc); | |
182 | ||
96bec7dd | 183 | rc = easycap_wakeup_device(peasycap->pusb_device); |
98680557 TW |
184 | JOM(8, "wakeup_device() returned %i\n", rc); |
185 | ||
186 | peasycap->audio_eof = 0; | |
187 | peasycap->audio_idle = 0; | |
188 | ||
96bec7dd | 189 | easycap_audio_submit_urbs(peasycap); |
98680557 TW |
190 | |
191 | JOM(4, "finished initialization\n"); | |
192 | return 0; | |
193 | } | |
a9855917 MT |
194 | /*****************************************************************************/ |
195 | /*---------------------------------------------------------------------------*/ | |
196 | /* | |
197 | * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE DAM BUFFER | |
198 | * PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE, | |
199 | * IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO. | |
200 | */ | |
201 | /*---------------------------------------------------------------------------*/ | |
96bec7dd | 202 | void easycap_alsa_complete(struct urb *purb) |
a9855917 | 203 | { |
a75af077 TW |
204 | struct easycap *peasycap; |
205 | struct snd_pcm_substream *pss; | |
206 | struct snd_pcm_runtime *prt; | |
207 | int dma_bytes, fragment_bytes; | |
208 | int isfragment; | |
209 | u8 *p1, *p2; | |
210 | s16 tmp; | |
211 | int i, j, more, much, rc; | |
3fc0dae8 | 212 | #ifdef UPSAMPLE |
a75af077 TW |
213 | int k; |
214 | s16 oldaudio, newaudio, delta; | |
a9855917 MT |
215 | #endif /*UPSAMPLE*/ |
216 | ||
a75af077 | 217 | JOT(16, "\n"); |
a9855917 | 218 | |
6888393c | 219 | if (!purb) { |
a75af077 TW |
220 | SAY("ERROR: purb is NULL\n"); |
221 | return; | |
222 | } | |
223 | peasycap = purb->context; | |
6888393c | 224 | if (!peasycap) { |
a75af077 TW |
225 | SAY("ERROR: peasycap is NULL\n"); |
226 | return; | |
227 | } | |
a75af077 TW |
228 | much = 0; |
229 | if (peasycap->audio_idle) { | |
230 | JOM(16, "%i=audio_idle %i=audio_isoc_streaming\n", | |
231 | peasycap->audio_idle, peasycap->audio_isoc_streaming); | |
232 | if (peasycap->audio_isoc_streaming) | |
233 | goto resubmit; | |
234 | } | |
a9855917 | 235 | /*---------------------------------------------------------------------------*/ |
a75af077 | 236 | pss = peasycap->psubstream; |
6888393c | 237 | if (!pss) |
a75af077 TW |
238 | goto resubmit; |
239 | prt = pss->runtime; | |
6888393c | 240 | if (!prt) |
a75af077 TW |
241 | goto resubmit; |
242 | dma_bytes = (int)prt->dma_bytes; | |
243 | if (0 == dma_bytes) | |
244 | goto resubmit; | |
245 | fragment_bytes = 4 * ((int)prt->period_size); | |
246 | if (0 == fragment_bytes) | |
247 | goto resubmit; | |
a9855917 | 248 | /* -------------------------------------------------------------------------*/ |
a75af077 TW |
249 | if (purb->status) { |
250 | if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) { | |
251 | JOM(16, "urb status -ESHUTDOWN or -ENOENT\n"); | |
252 | return; | |
253 | } | |
254 | SAM("ERROR: non-zero urb status: -%s: %d\n", | |
255 | strerror(purb->status), purb->status); | |
256 | goto resubmit; | |
a9855917 | 257 | } |
a9855917 MT |
258 | /*---------------------------------------------------------------------------*/ |
259 | /* | |
260 | * PROCEED HERE WHEN NO ERROR | |
261 | */ | |
262 | /*---------------------------------------------------------------------------*/ | |
263 | ||
3fc0dae8 | 264 | #ifdef UPSAMPLE |
a75af077 | 265 | oldaudio = peasycap->oldaudio; |
a9855917 MT |
266 | #endif /*UPSAMPLE*/ |
267 | ||
a75af077 TW |
268 | for (i = 0; i < purb->number_of_packets; i++) { |
269 | if (purb->iso_frame_desc[i].status < 0) { | |
270 | SAM("-%s: %d\n", | |
271 | strerror(purb->iso_frame_desc[i].status), | |
272 | purb->iso_frame_desc[i].status); | |
273 | } | |
5917def5 TW |
274 | if (purb->iso_frame_desc[i].status) { |
275 | JOM(12, "discarding audio samples because " | |
276 | "%i=purb->iso_frame_desc[i].status\n", | |
277 | purb->iso_frame_desc[i].status); | |
278 | continue; | |
279 | } | |
280 | more = purb->iso_frame_desc[i].actual_length; | |
281 | if (more == 0) { | |
282 | peasycap->audio_mt++; | |
283 | continue; | |
284 | } | |
285 | if (0 > more) { | |
286 | SAM("MISTAKE: more is negative\n"); | |
287 | return; | |
288 | } | |
a9855917 | 289 | |
5917def5 TW |
290 | if (peasycap->audio_mt) { |
291 | JOM(12, "%4i empty audio urb frames\n", | |
292 | peasycap->audio_mt); | |
293 | peasycap->audio_mt = 0; | |
294 | } | |
295 | ||
296 | p1 = (u8 *)(purb->transfer_buffer + | |
297 | purb->iso_frame_desc[i].offset); | |
298 | ||
299 | /* | |
300 | * COPY more BYTES FROM ISOC BUFFER | |
301 | * TO THE DMA BUFFER, CONVERTING | |
302 | * 8-BIT MONO TO 16-BIT SIGNED | |
303 | * LITTLE-ENDIAN SAMPLES IF NECESSARY | |
304 | */ | |
305 | while (more) { | |
306 | much = dma_bytes - peasycap->dma_fill; | |
307 | if (0 > much) { | |
308 | SAM("MISTAKE: much is negative\n"); | |
309 | return; | |
310 | } | |
311 | if (0 == much) { | |
312 | peasycap->dma_fill = 0; | |
313 | peasycap->dma_next = fragment_bytes; | |
314 | JOM(8, "wrapped dma buffer\n"); | |
315 | } | |
316 | if (!peasycap->microphone) { | |
317 | if (much > more) | |
318 | much = more; | |
319 | memcpy(prt->dma_area + peasycap->dma_fill, | |
320 | p1, much); | |
321 | p1 += much; | |
322 | more -= much; | |
323 | } else { | |
3fc0dae8 | 324 | #ifdef UPSAMPLE |
5917def5 TW |
325 | if (much % 16) |
326 | JOM(8, "MISTAKE? much" | |
327 | " is not divisible by 16\n"); | |
328 | if (much > (16 * more)) | |
329 | much = 16 * more; | |
330 | p2 = (u8 *)(prt->dma_area + peasycap->dma_fill); | |
331 | ||
332 | for (j = 0; j < (much / 16); j++) { | |
333 | newaudio = ((int) *p1) - 128; | |
334 | newaudio = 128 * newaudio; | |
335 | ||
336 | delta = (newaudio - oldaudio) / 4; | |
337 | tmp = oldaudio + delta; | |
338 | ||
339 | for (k = 0; k < 4; k++) { | |
340 | *p2 = (0x00FF & tmp); | |
341 | *(p2 + 1) = (0xFF00 & tmp) >> 8; | |
342 | p2 += 2; | |
343 | *p2 = (0x00FF & tmp); | |
344 | *(p2 + 1) = (0xFF00 & tmp) >> 8; | |
345 | p2 += 2; | |
346 | tmp += delta; | |
347 | } | |
348 | p1++; | |
349 | more--; | |
350 | oldaudio = tmp; | |
351 | } | |
a75af077 | 352 | #else /*!UPSAMPLE*/ |
5917def5 TW |
353 | if (much > (2 * more)) |
354 | much = 2 * more; | |
355 | p2 = (u8 *)(prt->dma_area + peasycap->dma_fill); | |
356 | ||
357 | for (j = 0; j < (much / 2); j++) { | |
358 | tmp = ((int) *p1) - 128; | |
359 | tmp = 128 * tmp; | |
360 | *p2 = (0x00FF & tmp); | |
361 | *(p2 + 1) = (0xFF00 & tmp) >> 8; | |
362 | p1++; | |
363 | p2 += 2; | |
364 | more--; | |
365 | } | |
a9855917 | 366 | #endif /*UPSAMPLE*/ |
5917def5 TW |
367 | } |
368 | peasycap->dma_fill += much; | |
369 | if (peasycap->dma_fill >= peasycap->dma_next) { | |
370 | isfragment = peasycap->dma_fill / fragment_bytes; | |
371 | if (0 > isfragment) { | |
372 | SAM("MISTAKE: isfragment is negative\n"); | |
373 | return; | |
374 | } | |
375 | peasycap->dma_read = (isfragment - 1) * fragment_bytes; | |
376 | peasycap->dma_next = (isfragment + 1) * fragment_bytes; | |
377 | if (dma_bytes < peasycap->dma_next) | |
378 | peasycap->dma_next = fragment_bytes; | |
379 | ||
380 | if (0 <= peasycap->dma_read) { | |
381 | JOM(8, "snd_pcm_period_elapsed(), %i=" | |
382 | "isfragment\n", isfragment); | |
383 | snd_pcm_period_elapsed(pss); | |
a9855917 MT |
384 | } |
385 | } | |
386 | } | |
a9855917 | 387 | |
3fc0dae8 | 388 | #ifdef UPSAMPLE |
a75af077 | 389 | peasycap->oldaudio = oldaudio; |
a9855917 MT |
390 | #endif /*UPSAMPLE*/ |
391 | ||
a75af077 | 392 | } |
a9855917 MT |
393 | /*---------------------------------------------------------------------------*/ |
394 | /* | |
395 | * RESUBMIT THIS URB | |
396 | */ | |
397 | /*---------------------------------------------------------------------------*/ | |
398 | resubmit: | |
5917def5 TW |
399 | if (peasycap->audio_isoc_streaming == 0) |
400 | return; | |
401 | ||
402 | rc = usb_submit_urb(purb, GFP_ATOMIC); | |
403 | if (rc) { | |
404 | if ((-ENODEV != rc) && (-ENOENT != rc)) { | |
405 | SAM("ERROR: while %i=audio_idle, usb_submit_urb failed " | |
406 | "with rc: -%s :%d\n", | |
407 | peasycap->audio_idle, strerror(rc), rc); | |
a9855917 | 408 | } |
5917def5 TW |
409 | if (0 < peasycap->audio_isoc_streaming) |
410 | peasycap->audio_isoc_streaming--; | |
a9855917 | 411 | } |
a75af077 | 412 | return; |
a9855917 MT |
413 | } |
414 | /*****************************************************************************/ | |
68e4ccaa | 415 | static int easycap_alsa_open(struct snd_pcm_substream *pss) |
a9855917 | 416 | { |
a75af077 TW |
417 | struct snd_pcm *psnd_pcm; |
418 | struct snd_card *psnd_card; | |
419 | struct easycap *peasycap; | |
a9855917 | 420 | |
a75af077 | 421 | JOT(4, "\n"); |
6888393c | 422 | if (!pss) { |
a75af077 TW |
423 | SAY("ERROR: pss is NULL\n"); |
424 | return -EFAULT; | |
425 | } | |
426 | psnd_pcm = pss->pcm; | |
6888393c | 427 | if (!psnd_pcm) { |
a75af077 TW |
428 | SAY("ERROR: psnd_pcm is NULL\n"); |
429 | return -EFAULT; | |
430 | } | |
431 | psnd_card = psnd_pcm->card; | |
6888393c | 432 | if (!psnd_card) { |
a75af077 TW |
433 | SAY("ERROR: psnd_card is NULL\n"); |
434 | return -EFAULT; | |
435 | } | |
436 | ||
437 | peasycap = psnd_card->private_data; | |
6888393c | 438 | if (!peasycap) { |
a75af077 TW |
439 | SAY("ERROR: peasycap is NULL\n"); |
440 | return -EFAULT; | |
441 | } | |
a75af077 TW |
442 | if (peasycap->psnd_card != psnd_card) { |
443 | SAM("ERROR: bad peasycap->psnd_card\n"); | |
444 | return -EFAULT; | |
445 | } | |
6888393c | 446 | if (peasycap->psubstream) { |
a75af077 TW |
447 | SAM("ERROR: bad peasycap->psubstream\n"); |
448 | return -EFAULT; | |
449 | } | |
450 | pss->private_data = peasycap; | |
451 | peasycap->psubstream = pss; | |
452 | pss->runtime->hw = peasycap->alsa_hardware; | |
453 | pss->runtime->private_data = peasycap; | |
454 | pss->private_data = peasycap; | |
455 | ||
456 | if (0 != easycap_sound_setup(peasycap)) { | |
457 | JOM(4, "ending unsuccessfully\n"); | |
458 | return -EFAULT; | |
459 | } | |
460 | JOM(4, "ending successfully\n"); | |
461 | return 0; | |
a9855917 MT |
462 | } |
463 | /*****************************************************************************/ | |
68e4ccaa | 464 | static int easycap_alsa_close(struct snd_pcm_substream *pss) |
a9855917 | 465 | { |
a75af077 | 466 | struct easycap *peasycap; |
a9855917 | 467 | |
a75af077 | 468 | JOT(4, "\n"); |
6888393c | 469 | if (!pss) { |
a75af077 TW |
470 | SAY("ERROR: pss is NULL\n"); |
471 | return -EFAULT; | |
472 | } | |
473 | peasycap = snd_pcm_substream_chip(pss); | |
6888393c | 474 | if (!peasycap) { |
a75af077 TW |
475 | SAY("ERROR: peasycap is NULL\n"); |
476 | return -EFAULT; | |
477 | } | |
a75af077 TW |
478 | pss->private_data = NULL; |
479 | peasycap->psubstream = NULL; | |
480 | JOT(4, "ending successfully\n"); | |
481 | return 0; | |
a9855917 MT |
482 | } |
483 | /*****************************************************************************/ | |
68e4ccaa | 484 | static int easycap_alsa_vmalloc(struct snd_pcm_substream *pss, size_t sz) |
a9855917 | 485 | { |
a75af077 TW |
486 | struct snd_pcm_runtime *prt; |
487 | JOT(4, "\n"); | |
a9855917 | 488 | |
6888393c | 489 | if (!pss) { |
a75af077 TW |
490 | SAY("ERROR: pss is NULL\n"); |
491 | return -EFAULT; | |
492 | } | |
493 | prt = pss->runtime; | |
6888393c | 494 | if (!prt) { |
a75af077 TW |
495 | SAY("ERROR: substream.runtime is NULL\n"); |
496 | return -EFAULT; | |
497 | } | |
498 | if (prt->dma_area) { | |
499 | if (prt->dma_bytes > sz) | |
500 | return 0; | |
501 | vfree(prt->dma_area); | |
502 | } | |
503 | prt->dma_area = vmalloc(sz); | |
6888393c | 504 | if (!prt->dma_area) |
a75af077 TW |
505 | return -ENOMEM; |
506 | prt->dma_bytes = sz; | |
507 | return 0; | |
a9855917 MT |
508 | } |
509 | /*****************************************************************************/ | |
68e4ccaa | 510 | static int easycap_alsa_hw_params(struct snd_pcm_substream *pss, |
a75af077 | 511 | struct snd_pcm_hw_params *phw) |
68e4ccaa | 512 | { |
a75af077 | 513 | int rc; |
68e4ccaa | 514 | |
a75af077 | 515 | JOT(4, "%i\n", (params_buffer_bytes(phw))); |
6888393c | 516 | if (!pss) { |
a75af077 TW |
517 | SAY("ERROR: pss is NULL\n"); |
518 | return -EFAULT; | |
519 | } | |
520 | rc = easycap_alsa_vmalloc(pss, params_buffer_bytes(phw)); | |
521 | if (rc) | |
522 | return rc; | |
523 | return 0; | |
68e4ccaa TW |
524 | } |
525 | /*****************************************************************************/ | |
526 | static int easycap_alsa_hw_free(struct snd_pcm_substream *pss) | |
a9855917 | 527 | { |
a75af077 TW |
528 | struct snd_pcm_runtime *prt; |
529 | JOT(4, "\n"); | |
a9855917 | 530 | |
6888393c | 531 | if (!pss) { |
a75af077 TW |
532 | SAY("ERROR: pss is NULL\n"); |
533 | return -EFAULT; | |
534 | } | |
535 | prt = pss->runtime; | |
6888393c | 536 | if (!prt) { |
a75af077 TW |
537 | SAY("ERROR: substream.runtime is NULL\n"); |
538 | return -EFAULT; | |
539 | } | |
6888393c | 540 | if (prt->dma_area) { |
a75af077 TW |
541 | JOT(8, "prt->dma_area = %p\n", prt->dma_area); |
542 | vfree(prt->dma_area); | |
543 | prt->dma_area = NULL; | |
544 | } else | |
545 | JOT(8, "dma_area already freed\n"); | |
546 | return 0; | |
a9855917 MT |
547 | } |
548 | /*****************************************************************************/ | |
68e4ccaa | 549 | static int easycap_alsa_prepare(struct snd_pcm_substream *pss) |
a9855917 | 550 | { |
a75af077 TW |
551 | struct easycap *peasycap; |
552 | struct snd_pcm_runtime *prt; | |
a9855917 | 553 | |
a75af077 | 554 | JOT(4, "\n"); |
6888393c | 555 | if (!pss) { |
a75af077 TW |
556 | SAY("ERROR: pss is NULL\n"); |
557 | return -EFAULT; | |
558 | } | |
559 | prt = pss->runtime; | |
560 | peasycap = snd_pcm_substream_chip(pss); | |
6888393c | 561 | if (!peasycap) { |
a75af077 TW |
562 | SAY("ERROR: peasycap is NULL\n"); |
563 | return -EFAULT; | |
564 | } | |
a9855917 | 565 | |
a75af077 TW |
566 | JOM(16, "ALSA decides %8i Hz=rate\n", pss->runtime->rate); |
567 | JOM(16, "ALSA decides %8ld =period_size\n", pss->runtime->period_size); | |
568 | JOM(16, "ALSA decides %8i =periods\n", pss->runtime->periods); | |
569 | JOM(16, "ALSA decides %8ld =buffer_size\n", pss->runtime->buffer_size); | |
570 | JOM(16, "ALSA decides %8zd =dma_bytes\n", pss->runtime->dma_bytes); | |
571 | JOM(16, "ALSA decides %8ld =boundary\n", pss->runtime->boundary); | |
572 | JOM(16, "ALSA decides %8i =period_step\n", pss->runtime->period_step); | |
573 | JOM(16, "ALSA decides %8i =sample_bits\n", pss->runtime->sample_bits); | |
574 | JOM(16, "ALSA decides %8i =frame_bits\n", pss->runtime->frame_bits); | |
575 | JOM(16, "ALSA decides %8ld =min_align\n", pss->runtime->min_align); | |
576 | JOM(12, "ALSA decides %8ld =hw_ptr_base\n", pss->runtime->hw_ptr_base); | |
577 | JOM(12, "ALSA decides %8ld =hw_ptr_interrupt\n", | |
578 | pss->runtime->hw_ptr_interrupt); | |
579 | ||
580 | if (prt->dma_bytes != 4 * ((int)prt->period_size) * ((int)prt->periods)) { | |
581 | SAY("MISTAKE: unexpected ALSA parameters\n"); | |
582 | return -ENOENT; | |
583 | } | |
584 | return 0; | |
a9855917 MT |
585 | } |
586 | /*****************************************************************************/ | |
68e4ccaa | 587 | static int easycap_alsa_ack(struct snd_pcm_substream *pss) |
a9855917 | 588 | { |
68e4ccaa | 589 | return 0; |
a9855917 MT |
590 | } |
591 | /*****************************************************************************/ | |
68e4ccaa | 592 | static int easycap_alsa_trigger(struct snd_pcm_substream *pss, int cmd) |
a9855917 | 593 | { |
a75af077 | 594 | struct easycap *peasycap; |
a9855917 | 595 | |
a75af077 TW |
596 | JOT(4, "%i=cmd cf %i=START %i=STOP\n", cmd, SNDRV_PCM_TRIGGER_START, |
597 | SNDRV_PCM_TRIGGER_STOP); | |
6888393c | 598 | if (!pss) { |
a75af077 TW |
599 | SAY("ERROR: pss is NULL\n"); |
600 | return -EFAULT; | |
601 | } | |
602 | peasycap = snd_pcm_substream_chip(pss); | |
6888393c | 603 | if (!peasycap) { |
a75af077 TW |
604 | SAY("ERROR: peasycap is NULL\n"); |
605 | return -EFAULT; | |
606 | } | |
a75af077 TW |
607 | switch (cmd) { |
608 | case SNDRV_PCM_TRIGGER_START: { | |
609 | peasycap->audio_idle = 0; | |
610 | break; | |
611 | } | |
612 | case SNDRV_PCM_TRIGGER_STOP: { | |
613 | peasycap->audio_idle = 1; | |
614 | break; | |
615 | } | |
616 | default: | |
a6ff0a06 | 617 | return -EINVAL; |
a75af077 TW |
618 | } |
619 | return 0; | |
a9855917 MT |
620 | } |
621 | /*****************************************************************************/ | |
68e4ccaa | 622 | static snd_pcm_uframes_t easycap_alsa_pointer(struct snd_pcm_substream *pss) |
a9855917 | 623 | { |
a75af077 TW |
624 | struct easycap *peasycap; |
625 | snd_pcm_uframes_t offset; | |
a9855917 | 626 | |
a75af077 | 627 | JOT(16, "\n"); |
6888393c | 628 | if (!pss) { |
a75af077 TW |
629 | SAY("ERROR: pss is NULL\n"); |
630 | return -EFAULT; | |
631 | } | |
632 | peasycap = snd_pcm_substream_chip(pss); | |
6888393c | 633 | if (!peasycap) { |
a75af077 TW |
634 | SAY("ERROR: peasycap is NULL\n"); |
635 | return -EFAULT; | |
636 | } | |
a75af077 TW |
637 | if ((0 != peasycap->audio_eof) || (0 != peasycap->audio_idle)) { |
638 | JOM(8, "returning -EIO because " | |
639 | "%i=audio_idle %i=audio_eof\n", | |
640 | peasycap->audio_idle, peasycap->audio_eof); | |
641 | return -EIO; | |
642 | } | |
a9855917 | 643 | /*---------------------------------------------------------------------------*/ |
a75af077 TW |
644 | if (0 > peasycap->dma_read) { |
645 | JOM(8, "returning -EBUSY\n"); | |
646 | return -EBUSY; | |
647 | } | |
648 | offset = ((snd_pcm_uframes_t)peasycap->dma_read)/4; | |
649 | JOM(8, "ALSA decides %8i =hw_ptr_base\n", (int)pss->runtime->hw_ptr_base); | |
650 | JOM(8, "ALSA decides %8i =hw_ptr_interrupt\n", | |
651 | (int)pss->runtime->hw_ptr_interrupt); | |
652 | JOM(8, "%7i=offset %7i=dma_read %7i=dma_next\n", | |
653 | (int)offset, peasycap->dma_read, peasycap->dma_next); | |
654 | return offset; | |
a9855917 MT |
655 | } |
656 | /*****************************************************************************/ | |
a75af077 TW |
657 | static struct page * |
658 | easycap_alsa_page(struct snd_pcm_substream *pss, unsigned long offset) | |
a9855917 | 659 | { |
68e4ccaa | 660 | return vmalloc_to_page(pss->runtime->dma_area + offset); |
a9855917 MT |
661 | } |
662 | /*****************************************************************************/ | |
663 | ||
68e4ccaa TW |
664 | static struct snd_pcm_ops easycap_alsa_pcm_ops = { |
665 | .open = easycap_alsa_open, | |
666 | .close = easycap_alsa_close, | |
667 | .ioctl = snd_pcm_lib_ioctl, | |
668 | .hw_params = easycap_alsa_hw_params, | |
669 | .hw_free = easycap_alsa_hw_free, | |
670 | .prepare = easycap_alsa_prepare, | |
671 | .ack = easycap_alsa_ack, | |
672 | .trigger = easycap_alsa_trigger, | |
673 | .pointer = easycap_alsa_pointer, | |
674 | .page = easycap_alsa_page, | |
675 | }; | |
676 | ||
677 | /*****************************************************************************/ | |
678 | /*---------------------------------------------------------------------------*/ | |
679 | /* | |
680 | * THE FUNCTION snd_card_create() HAS THIS_MODULE AS AN ARGUMENT. THIS | |
681 | * MEANS MODULE easycap. BEWARE. | |
682 | */ | |
683 | /*---------------------------------------------------------------------------*/ | |
684 | int easycap_alsa_probe(struct easycap *peasycap) | |
685 | { | |
a75af077 TW |
686 | int rc; |
687 | struct snd_card *psnd_card; | |
688 | struct snd_pcm *psnd_pcm; | |
68e4ccaa | 689 | |
6888393c | 690 | if (!peasycap) { |
a75af077 TW |
691 | SAY("ERROR: peasycap is NULL\n"); |
692 | return -ENODEV; | |
693 | } | |
a75af077 TW |
694 | if (0 > peasycap->minor) { |
695 | SAY("ERROR: no minor\n"); | |
696 | return -ENODEV; | |
697 | } | |
68e4ccaa | 698 | |
a75af077 | 699 | peasycap->alsa_hardware = alsa_hardware; |
27d683ab | 700 | if (peasycap->microphone) { |
a75af077 TW |
701 | peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_32000; |
702 | peasycap->alsa_hardware.rate_min = 32000; | |
703 | peasycap->alsa_hardware.rate_max = 32000; | |
704 | } else { | |
705 | peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_48000; | |
706 | peasycap->alsa_hardware.rate_min = 48000; | |
707 | peasycap->alsa_hardware.rate_max = 48000; | |
708 | } | |
68e4ccaa TW |
709 | |
710 | if (0 != snd_card_create(SNDRV_DEFAULT_IDX1, "easycap_alsa", | |
a75af077 | 711 | THIS_MODULE, 0, &psnd_card)) { |
68e4ccaa TW |
712 | SAY("ERROR: Cannot do ALSA snd_card_create()\n"); |
713 | return -EFAULT; | |
714 | } | |
715 | ||
716 | sprintf(&psnd_card->id[0], "EasyALSA%i", peasycap->minor); | |
717 | strcpy(&psnd_card->driver[0], EASYCAP_DRIVER_DESCRIPTION); | |
718 | strcpy(&psnd_card->shortname[0], "easycap_alsa"); | |
719 | sprintf(&psnd_card->longname[0], "%s", &psnd_card->shortname[0]); | |
720 | ||
721 | psnd_card->dev = &peasycap->pusb_device->dev; | |
722 | psnd_card->private_data = peasycap; | |
723 | peasycap->psnd_card = psnd_card; | |
724 | ||
725 | rc = snd_pcm_new(psnd_card, "easycap_pcm", 0, 0, 1, &psnd_pcm); | |
6911e7e4 | 726 | if (rc) { |
68e4ccaa TW |
727 | SAM("ERROR: Cannot do ALSA snd_pcm_new()\n"); |
728 | snd_card_free(psnd_card); | |
729 | return -EFAULT; | |
730 | } | |
731 | ||
732 | snd_pcm_set_ops(psnd_pcm, SNDRV_PCM_STREAM_CAPTURE, | |
a75af077 | 733 | &easycap_alsa_pcm_ops); |
68e4ccaa TW |
734 | psnd_pcm->info_flags = 0; |
735 | strcpy(&psnd_pcm->name[0], &psnd_card->id[0]); | |
736 | psnd_pcm->private_data = peasycap; | |
737 | peasycap->psnd_pcm = psnd_pcm; | |
3c1fb66e | 738 | peasycap->psubstream = NULL; |
68e4ccaa TW |
739 | |
740 | rc = snd_card_register(psnd_card); | |
6911e7e4 | 741 | if (rc) { |
68e4ccaa TW |
742 | SAM("ERROR: Cannot do ALSA snd_card_register()\n"); |
743 | snd_card_free(psnd_card); | |
744 | return -EFAULT; | |
68e4ccaa | 745 | } |
5917def5 TW |
746 | |
747 | SAM("registered %s\n", &psnd_card->id[0]); | |
a75af077 | 748 | return 0; |
68e4ccaa | 749 | } |
a9855917 | 750 |