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" | |
32 | #include "easycap_debug.h" | |
33 | #include "easycap_sound.h" | |
34 | ||
35 | /*****************************************************************************/ | |
36 | /*---------------------------------------------------------------------------*/ | |
37 | /* | |
38 | * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE AUDIO BUFFERS | |
39 | * PROVIDED peasycap->audio_idle IS ZER0. REGARDLESS OF THIS BEING TRUE, | |
40 | * IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO. | |
41 | */ | |
42 | /*---------------------------------------------------------------------------*/ | |
43 | void | |
44 | easysnd_complete(struct urb *purb) | |
45 | { | |
46 | static int mt; | |
47 | struct easycap *peasycap; | |
48 | struct data_buffer *paudio_buffer; | |
49 | char errbuf[16]; | |
50 | __u8 *p1, *p2; | |
51 | __s16 s16; | |
52 | int i, j, more, much, leap, rc; | |
7ebc8760 MT |
53 | #if defined(UPSAMPLE) |
54 | int k; | |
55 | __s16 oldaudio, newaudio, delta; | |
56 | #endif /*UPSAMPLE*/ | |
702422bd T |
57 | |
58 | JOT(16, "\n"); | |
59 | ||
60 | if (NULL == purb) { | |
61 | SAY("ERROR: purb is NULL\n"); | |
62 | return; | |
63 | } | |
64 | peasycap = purb->context; | |
65 | if (NULL == peasycap) { | |
66 | SAY("ERROR: peasycap is NULL\n"); | |
67 | return; | |
68 | } | |
69 | much = 0; | |
70 | ||
71 | ||
72 | if (peasycap->audio_idle) { | |
73 | JOT(16, "%i=audio_idle %i=audio_isoc_streaming\n", \ | |
74 | peasycap->audio_idle, peasycap->audio_isoc_streaming); | |
75 | if (peasycap->audio_isoc_streaming) { | |
76 | rc = usb_submit_urb(purb, GFP_ATOMIC); | |
77 | if (0 != rc) { | |
78 | SAY("ERROR: while %i=audio_idle, " \ | |
79 | "usb_submit_urb() failed with rc:\n", \ | |
80 | peasycap->audio_idle); | |
81 | switch (rc) { | |
82 | case -ENOMEM: { | |
83 | SAY("ENOMEM\n"); break; | |
84 | } | |
85 | case -ENODEV: { | |
86 | SAY("ENODEV\n"); break; | |
87 | } | |
88 | case -ENXIO: { | |
89 | SAY("ENXIO\n"); break; | |
90 | } | |
91 | case -EINVAL: { | |
92 | SAY("EINVAL\n"); break; | |
93 | } | |
94 | case -EAGAIN: { | |
95 | SAY("EAGAIN\n"); break; | |
96 | } | |
97 | case -EFBIG: { | |
98 | SAY("EFBIG\n"); break; | |
99 | } | |
100 | case -EPIPE: { | |
101 | SAY("EPIPE\n"); break; | |
102 | } | |
103 | case -EMSGSIZE: { | |
104 | SAY("EMSGSIZE\n"); break; | |
105 | } | |
7ebc8760 MT |
106 | case -ENOSPC: { |
107 | SAY("ENOSPC\n"); break; | |
108 | } | |
702422bd T |
109 | default: { |
110 | SAY("0x%08X\n", rc); break; | |
111 | } | |
112 | } | |
113 | } | |
114 | } | |
115 | return; | |
116 | } | |
117 | /*---------------------------------------------------------------------------*/ | |
118 | if (purb->status) { | |
119 | if (-ESHUTDOWN == purb->status) { | |
120 | JOT(16, "immediate return because -ESHUTDOWN=purb->status\n"); | |
121 | return; | |
122 | } | |
123 | SAY("ERROR: non-zero urb status:\n"); | |
124 | switch (purb->status) { | |
125 | case -EINPROGRESS: { | |
126 | SAY("-EINPROGRESS\n"); break; | |
127 | } | |
128 | case -ENOSR: { | |
129 | SAY("-ENOSR\n"); break; | |
130 | } | |
131 | case -EPIPE: { | |
132 | SAY("-EPIPE\n"); break; | |
133 | } | |
134 | case -EOVERFLOW: { | |
135 | SAY("-EOVERFLOW\n"); break; | |
136 | } | |
137 | case -EPROTO: { | |
138 | SAY("-EPROTO\n"); break; | |
139 | } | |
140 | case -EILSEQ: { | |
141 | SAY("-EILSEQ\n"); break; | |
142 | } | |
143 | case -ETIMEDOUT: { | |
144 | SAY("-ETIMEDOUT\n"); break; | |
145 | } | |
146 | case -EMSGSIZE: { | |
147 | SAY("-EMSGSIZE\n"); break; | |
148 | } | |
149 | case -EOPNOTSUPP: { | |
150 | SAY("-EOPNOTSUPP\n"); break; | |
151 | } | |
152 | case -EPFNOSUPPORT: { | |
153 | SAY("-EPFNOSUPPORT\n"); break; | |
154 | } | |
155 | case -EAFNOSUPPORT: { | |
156 | SAY("-EAFNOSUPPORT\n"); break; | |
157 | } | |
158 | case -EADDRINUSE: { | |
159 | SAY("-EADDRINUSE\n"); break; | |
160 | } | |
161 | case -EADDRNOTAVAIL: { | |
162 | SAY("-EADDRNOTAVAIL\n"); break; | |
163 | } | |
164 | case -ENOBUFS: { | |
165 | SAY("-ENOBUFS\n"); break; | |
166 | } | |
167 | case -EISCONN: { | |
168 | SAY("-EISCONN\n"); break; | |
169 | } | |
170 | case -ENOTCONN: { | |
171 | SAY("-ENOTCONN\n"); break; | |
172 | } | |
173 | case -ESHUTDOWN: { | |
174 | SAY("-ESHUTDOWN\n"); break; | |
175 | } | |
176 | case -ENOENT: { | |
177 | SAY("-ENOENT\n"); break; | |
178 | } | |
179 | case -ECONNRESET: { | |
180 | SAY("-ECONNRESET\n"); break; | |
181 | } | |
7ebc8760 MT |
182 | case -ENOSPC: { |
183 | SAY("ENOSPC\n"); break; | |
184 | } | |
702422bd T |
185 | default: { |
186 | SAY("unknown error code 0x%08X\n", purb->status); break; | |
187 | } | |
188 | } | |
189 | /*---------------------------------------------------------------------------*/ | |
190 | /* | |
191 | * RESUBMIT THIS URB AFTER AN ERROR | |
192 | * | |
193 | * (THIS IS DUPLICATE CODE TO REDUCE INDENTATION OF THE NO-ERROR PATH) | |
194 | */ | |
195 | /*---------------------------------------------------------------------------*/ | |
196 | if (peasycap->audio_isoc_streaming) { | |
197 | rc = usb_submit_urb(purb, GFP_ATOMIC); | |
198 | if (0 != rc) { | |
199 | SAY("ERROR: while %i=audio_idle, usb_submit_urb() " | |
200 | "failed with rc:\n", peasycap->audio_idle); | |
201 | switch (rc) { | |
202 | case -ENOMEM: { | |
203 | SAY("ENOMEM\n"); break; | |
204 | } | |
205 | case -ENODEV: { | |
206 | SAY("ENODEV\n"); break; | |
207 | } | |
208 | case -ENXIO: { | |
209 | SAY("ENXIO\n"); break; | |
210 | } | |
211 | case -EINVAL: { | |
212 | SAY("EINVAL\n"); break; | |
213 | } | |
214 | case -EAGAIN: { | |
215 | SAY("EAGAIN\n"); break; | |
216 | } | |
217 | case -EFBIG: { | |
218 | SAY("EFBIG\n"); break; | |
219 | } | |
220 | case -EPIPE: { | |
221 | SAY("EPIPE\n"); break; | |
222 | } | |
223 | case -EMSGSIZE: { | |
224 | SAY("EMSGSIZE\n"); break; | |
225 | } | |
226 | default: { | |
227 | SAY("0x%08X\n", rc); break; | |
228 | } | |
229 | } | |
230 | } | |
231 | } | |
232 | return; | |
233 | } | |
234 | /*---------------------------------------------------------------------------*/ | |
235 | /* | |
236 | * PROCEED HERE WHEN NO ERROR | |
237 | */ | |
238 | /*---------------------------------------------------------------------------*/ | |
7ebc8760 MT |
239 | #if defined(UPSAMPLE) |
240 | oldaudio = peasycap->oldaudio; | |
241 | #endif /*UPSAMPLE*/ | |
242 | ||
702422bd T |
243 | for (i = 0; i < purb->number_of_packets; i++) { |
244 | switch (purb->iso_frame_desc[i].status) { | |
245 | case 0: { | |
246 | strcpy(&errbuf[0], "OK"); break; | |
247 | } | |
248 | case -ENOENT: { | |
249 | strcpy(&errbuf[0], "-ENOENT"); break; | |
250 | } | |
251 | case -EINPROGRESS: { | |
252 | strcpy(&errbuf[0], "-EINPROGRESS"); break; | |
253 | } | |
254 | case -EPROTO: { | |
255 | strcpy(&errbuf[0], "-EPROTO"); break; | |
256 | } | |
257 | case -EILSEQ: { | |
258 | strcpy(&errbuf[0], "-EILSEQ"); break; | |
259 | } | |
260 | case -ETIME: { | |
261 | strcpy(&errbuf[0], "-ETIME"); break; | |
262 | } | |
263 | case -ETIMEDOUT: { | |
264 | strcpy(&errbuf[0], "-ETIMEDOUT"); break; | |
265 | } | |
266 | case -EPIPE: { | |
267 | strcpy(&errbuf[0], "-EPIPE"); break; | |
268 | } | |
269 | case -ECOMM: { | |
270 | strcpy(&errbuf[0], "-ECOMM"); break; | |
271 | } | |
272 | case -ENOSR: { | |
273 | strcpy(&errbuf[0], "-ENOSR"); break; | |
274 | } | |
275 | case -EOVERFLOW: { | |
276 | strcpy(&errbuf[0], "-EOVERFLOW"); break; | |
277 | } | |
278 | case -EREMOTEIO: { | |
279 | strcpy(&errbuf[0], "-EREMOTEIO"); break; | |
280 | } | |
281 | case -ENODEV: { | |
282 | strcpy(&errbuf[0], "-ENODEV"); break; | |
283 | } | |
284 | case -EXDEV: { | |
285 | strcpy(&errbuf[0], "-EXDEV"); break; | |
286 | } | |
287 | case -EINVAL: { | |
288 | strcpy(&errbuf[0], "-EINVAL"); break; | |
289 | } | |
290 | case -ECONNRESET: { | |
291 | strcpy(&errbuf[0], "-ECONNRESET"); break; | |
292 | } | |
7ebc8760 MT |
293 | case -ENOSPC: { |
294 | strcpy(&errbuf[0], "-ENOSPC"); break; | |
295 | } | |
702422bd T |
296 | case -ESHUTDOWN: { |
297 | strcpy(&errbuf[0], "-ESHUTDOWN"); break; | |
298 | } | |
299 | default: { | |
300 | strcpy(&errbuf[0], "UNKNOWN"); break; | |
301 | } | |
302 | } | |
303 | if ((!purb->iso_frame_desc[i].status) && 0) { | |
304 | JOT(16, "frame[%2i]: %i=status{=%16s} " \ | |
305 | "%5i=actual " \ | |
306 | "%5i=length " \ | |
307 | "%3i=offset\n", \ | |
308 | i, purb->iso_frame_desc[i].status, &errbuf[0], | |
309 | purb->iso_frame_desc[i].actual_length, | |
310 | purb->iso_frame_desc[i].length, | |
311 | purb->iso_frame_desc[i].offset); | |
312 | } | |
313 | if (!purb->iso_frame_desc[i].status) { | |
314 | more = purb->iso_frame_desc[i].actual_length; | |
315 | ||
316 | #if defined(TESTTONE) | |
317 | if (!more) | |
318 | more = purb->iso_frame_desc[i].length; | |
319 | #endif | |
320 | ||
321 | if (!more) | |
322 | mt++; | |
323 | else { | |
324 | if (mt) { | |
325 | JOT(16, "%4i empty audio urb frames\n", mt); | |
326 | mt = 0; | |
327 | } | |
328 | ||
329 | p1 = (__u8 *)(purb->transfer_buffer + \ | |
330 | purb->iso_frame_desc[i].offset); | |
331 | ||
332 | leap = 0; | |
333 | p1 += leap; | |
334 | more -= leap; | |
335 | /*---------------------------------------------------------------------------*/ | |
336 | /* | |
337 | * COPY more BYTES FROM ISOC BUFFER TO AUDIO BUFFER, | |
7ebc8760 | 338 | * CONVERTING 8-BIT MONO TO 16-BIT SIGNED LITTLE-ENDIAN SAMPLES IF NECESSARY |
702422bd T |
339 | */ |
340 | /*---------------------------------------------------------------------------*/ | |
341 | while (more) { | |
342 | if (0 > more) { | |
343 | SAY("easysnd_complete: MISTAKE: " \ | |
344 | "more is negative\n"); | |
345 | return; | |
346 | } | |
3d423e91 | 347 | if (peasycap->audio_buffer_page_many <= \ |
702422bd T |
348 | peasycap->audio_fill) { |
349 | SAY("ERROR: bad " \ | |
350 | "peasycap->audio_fill\n"); | |
351 | return; | |
352 | } | |
353 | ||
354 | paudio_buffer = &peasycap->audio_buffer\ | |
355 | [peasycap->audio_fill]; | |
356 | if (PAGE_SIZE < (paudio_buffer->pto - \ | |
357 | paudio_buffer->pgo)) { | |
358 | SAY("ERROR: bad paudio_buffer->pto\n"); | |
359 | return; | |
360 | } | |
361 | if (PAGE_SIZE == (paudio_buffer->pto - \ | |
362 | paudio_buffer->pgo)) { | |
363 | ||
364 | #if defined(TESTTONE) | |
365 | easysnd_testtone(peasycap, \ | |
366 | peasycap->audio_fill); | |
367 | #endif /*TESTTONE*/ | |
368 | ||
369 | paudio_buffer->pto = \ | |
370 | paudio_buffer->pgo; | |
371 | (peasycap->audio_fill)++; | |
3d423e91 MT |
372 | if (peasycap->\ |
373 | audio_buffer_page_many <= \ | |
702422bd T |
374 | peasycap->audio_fill) |
375 | peasycap->audio_fill = 0; | |
376 | ||
377 | JOT(12, "bumped peasycap->" \ | |
378 | "audio_fill to %i\n", \ | |
379 | peasycap->audio_fill); | |
380 | ||
381 | paudio_buffer = &peasycap->\ | |
382 | audio_buffer\ | |
383 | [peasycap->audio_fill]; | |
384 | paudio_buffer->pto = \ | |
385 | paudio_buffer->pgo; | |
386 | ||
387 | if (!(peasycap->audio_fill % \ | |
3d423e91 | 388 | peasycap->\ |
702422bd T |
389 | audio_pages_per_fragment)) { |
390 | JOT(12, "wakeup call on wq_" \ | |
391 | "audio, %i=frag reading %i" \ | |
392 | "=fragment fill\n", \ | |
393 | (peasycap->audio_read / \ | |
3d423e91 | 394 | peasycap->\ |
702422bd T |
395 | audio_pages_per_fragment), \ |
396 | (peasycap->audio_fill / \ | |
3d423e91 | 397 | peasycap->\ |
702422bd T |
398 | audio_pages_per_fragment)); |
399 | wake_up_interruptible\ | |
400 | (&(peasycap->wq_audio)); | |
401 | } | |
402 | } | |
403 | ||
404 | much = PAGE_SIZE - (int)(paudio_buffer->pto -\ | |
405 | paudio_buffer->pgo); | |
702422bd T |
406 | |
407 | if (false == peasycap->microphone) { | |
408 | if (much > more) | |
409 | much = more; | |
410 | ||
411 | memcpy(paudio_buffer->pto, p1, much); | |
412 | p1 += much; | |
413 | more -= much; | |
414 | } else { | |
7ebc8760 MT |
415 | #if defined(UPSAMPLE) |
416 | if (much % 16) | |
417 | JOT(8, "MISTAKE? much" \ | |
418 | " is not divisible by 16\n"); | |
419 | if (much > (16 * \ | |
420 | more)) | |
421 | much = 16 * \ | |
422 | more; | |
423 | p2 = (__u8 *)paudio_buffer->pto; | |
424 | ||
425 | for (j = 0; j < (much/16); j++) { | |
426 | newaudio = ((int) *p1) - 128; | |
427 | newaudio = 128 * \ | |
428 | newaudio; | |
429 | ||
430 | delta = (newaudio - oldaudio) \ | |
431 | / 4; | |
432 | s16 = oldaudio + delta; | |
433 | ||
434 | for (k = 0; k < 4; k++) { | |
435 | *p2 = (0x00FF & s16); | |
436 | *(p2 + 1) = (0xFF00 & \ | |
437 | s16) >> 8; | |
438 | p2 += 2; | |
439 | *p2 = (0x00FF & s16); | |
440 | *(p2 + 1) = (0xFF00 & \ | |
441 | s16) >> 8; | |
442 | p2 += 2; | |
443 | ||
444 | s16 += delta; | |
445 | } | |
446 | p1++; | |
447 | more--; | |
448 | oldaudio = s16; | |
449 | } | |
450 | #else | |
702422bd T |
451 | if (much > (2 * more)) |
452 | much = 2 * more; | |
453 | p2 = (__u8 *)paudio_buffer->pto; | |
454 | ||
455 | for (j = 0; j < (much / 2); j++) { | |
456 | s16 = ((int) *p1) - 128; | |
7ebc8760 MT |
457 | s16 = 128 * \ |
458 | s16; | |
459 | *p2 = (0x00FF & s16); | |
460 | *(p2 + 1) = (0xFF00 & s16) >> \ | |
461 | 8; | |
702422bd T |
462 | p1++; p2 += 2; |
463 | more--; | |
464 | } | |
7ebc8760 | 465 | #endif /*UPSAMPLE*/ |
702422bd T |
466 | } |
467 | (paudio_buffer->pto) += much; | |
468 | } | |
469 | } | |
470 | } else { | |
471 | JOT(12, "discarding audio samples because " \ | |
472 | "%i=purb->iso_frame_desc[i].status\n", \ | |
473 | purb->iso_frame_desc[i].status); | |
474 | } | |
7ebc8760 MT |
475 | |
476 | #if defined(UPSAMPLE) | |
477 | peasycap->oldaudio = oldaudio; | |
478 | #endif /*UPSAMPLE*/ | |
479 | ||
702422bd T |
480 | } |
481 | /*---------------------------------------------------------------------------*/ | |
482 | /* | |
483 | * RESUBMIT THIS URB AFTER NO ERROR | |
484 | */ | |
485 | /*---------------------------------------------------------------------------*/ | |
486 | if (peasycap->audio_isoc_streaming) { | |
487 | rc = usb_submit_urb(purb, GFP_ATOMIC); | |
488 | if (0 != rc) { | |
489 | SAY("ERROR: while %i=audio_idle, usb_submit_urb() failed " \ | |
490 | "with rc:\n", peasycap->audio_idle); | |
491 | switch (rc) { | |
492 | case -ENOMEM: { | |
493 | SAY("ENOMEM\n"); break; | |
494 | } | |
495 | case -ENODEV: { | |
496 | SAY("ENODEV\n"); break; | |
497 | } | |
498 | case -ENXIO: { | |
499 | SAY("ENXIO\n"); break; | |
500 | } | |
501 | case -EINVAL: { | |
502 | SAY("EINVAL\n"); break; | |
503 | } | |
504 | case -EAGAIN: { | |
505 | SAY("EAGAIN\n"); break; | |
506 | } | |
507 | case -EFBIG: { | |
508 | SAY("EFBIG\n"); break; | |
509 | } | |
510 | case -EPIPE: { | |
511 | SAY("EPIPE\n"); break; | |
512 | } | |
513 | case -EMSGSIZE: { | |
514 | SAY("EMSGSIZE\n"); break; | |
515 | } | |
7ebc8760 MT |
516 | case -ENOSPC: { |
517 | SAY("ENOSPC\n"); break; | |
518 | } | |
702422bd T |
519 | default: { |
520 | SAY("0x%08X\n", rc); break; | |
521 | } | |
522 | } | |
523 | } | |
524 | } | |
525 | return; | |
526 | } | |
527 | /*****************************************************************************/ | |
528 | /*---------------------------------------------------------------------------*/ | |
529 | /* | |
530 | * THE AUDIO URBS ARE SUBMITTED AT THIS EARLY STAGE SO THAT IT IS POSSIBLE TO | |
531 | * STREAM FROM /dev/easysnd1 WITH SIMPLE PROGRAMS SUCH AS cat WHICH DO NOT | |
532 | * HAVE AN IOCTL INTERFACE. THE VIDEO URBS, BY CONTRAST, MUST BE SUBMITTED | |
533 | * MUCH LATER: SEE COMMENTS IN FILE easycap_main.c. | |
534 | */ | |
535 | /*---------------------------------------------------------------------------*/ | |
536 | int | |
537 | easysnd_open(struct inode *inode, struct file *file) | |
538 | { | |
539 | struct usb_interface *pusb_interface; | |
540 | struct easycap *peasycap; | |
541 | int subminor, rc; | |
542 | ||
543 | JOT(4, "begins.\n"); | |
544 | ||
545 | subminor = iminor(inode); | |
546 | ||
547 | pusb_interface = usb_find_interface(&easycap_usb_driver, subminor); | |
548 | if (NULL == pusb_interface) { | |
549 | SAY("ERROR: pusb_interface is NULL\n"); | |
550 | SAY("ending unsuccessfully\n"); | |
551 | return -1; | |
552 | } | |
553 | peasycap = usb_get_intfdata(pusb_interface); | |
554 | if (NULL == peasycap) { | |
555 | SAY("ERROR: peasycap is NULL\n"); | |
556 | SAY("ending unsuccessfully\n"); | |
557 | return -1; | |
558 | } | |
559 | ||
560 | file->private_data = peasycap; | |
561 | ||
562 | /*---------------------------------------------------------------------------*/ | |
563 | /* | |
564 | * INITIALIZATION. | |
565 | */ | |
566 | /*---------------------------------------------------------------------------*/ | |
567 | JOT(4, "starting initialization\n"); | |
568 | ||
569 | if ((struct usb_device *)NULL == peasycap->pusb_device) { | |
570 | SAY("ERROR: peasycap->pusb_device is NULL\n"); | |
571 | return -EFAULT; | |
572 | } else { | |
573 | JOT(16, "0x%08lX=peasycap->pusb_device\n", \ | |
574 | (long int)peasycap->pusb_device); | |
575 | } | |
576 | ||
577 | rc = audio_setup(peasycap); | |
578 | if (0 <= rc) | |
579 | JOT(8, "audio_setup() returned %i\n", rc); | |
580 | else | |
581 | JOT(8, "easysnd open(): ERROR: audio_setup() returned %i\n", rc); | |
582 | ||
583 | if ((struct usb_device *)NULL == peasycap->pusb_device) { | |
584 | SAY("ERROR: peasycap->pusb_device has become NULL\n"); | |
585 | return -EFAULT; | |
586 | } | |
587 | rc = adjust_volume(peasycap, -8192); | |
588 | if (0 != rc) { | |
589 | SAY("ERROR: adjust_volume(default) returned %i\n", rc); | |
590 | return -EFAULT; | |
591 | } | |
592 | /*---------------------------------------------------------------------------*/ | |
593 | if ((struct usb_device *)NULL == peasycap->pusb_device) { | |
594 | SAY("ERROR: peasycap->pusb_device has become NULL\n"); | |
595 | return -EFAULT; | |
596 | } | |
597 | rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface, \ | |
598 | peasycap->audio_altsetting_on); | |
599 | JOT(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface, \ | |
600 | peasycap->audio_altsetting_on, rc); | |
601 | ||
602 | if ((struct usb_device *)NULL == peasycap->pusb_device) { | |
603 | SAY("ERROR: peasycap->pusb_device has become NULL\n"); | |
604 | return -EFAULT; | |
605 | } | |
606 | rc = wakeup_device(peasycap->pusb_device); | |
607 | if (0 == rc) | |
608 | JOT(8, "wakeup_device() returned %i\n", rc); | |
609 | else | |
610 | JOT(8, "easysnd open(): ERROR: wakeup_device() returned %i\n", rc); | |
611 | ||
612 | if ((struct usb_device *)NULL == peasycap->pusb_device) { | |
613 | SAY("ERROR: peasycap->pusb_device has become NULL\n"); | |
614 | return -EFAULT; | |
615 | } | |
616 | submit_audio_urbs(peasycap); | |
617 | peasycap->audio_idle = 0; | |
618 | ||
619 | peasycap->timeval1.tv_sec = 0; | |
620 | peasycap->timeval1.tv_usec = 0; | |
621 | ||
622 | JOT(4, "finished initialization\n"); | |
623 | return 0; | |
624 | } | |
625 | /*****************************************************************************/ | |
626 | int | |
627 | easysnd_release(struct inode *inode, struct file *file) | |
628 | { | |
629 | struct easycap *peasycap; | |
630 | ||
631 | JOT(4, "begins\n"); | |
632 | ||
ba952d84 | 633 | peasycap = file->private_data; |
702422bd T |
634 | if (NULL == peasycap) { |
635 | SAY("ERROR: peasycap is NULL.\n"); | |
636 | return -EFAULT; | |
637 | } | |
638 | if (0 != kill_audio_urbs(peasycap)) { | |
639 | SAY("ERROR: kill_audio_urbs() failed\n"); | |
640 | return -EFAULT; | |
641 | } | |
642 | JOT(4, "ending successfully\n"); | |
643 | return 0; | |
644 | } | |
645 | /*****************************************************************************/ | |
646 | ssize_t | |
647 | easysnd_read(struct file *file, char __user *puserspacebuffer, \ | |
648 | size_t kount, loff_t *poff) | |
649 | { | |
650 | struct timeval timeval; | |
651 | static struct timeval timeval1; | |
652 | static long long int audio_bytes, above, below, mean; | |
653 | struct signed_div_result sdr; | |
654 | unsigned char *p0; | |
655 | long int kount1, more, rc, l0, lm; | |
656 | int fragment; | |
657 | struct easycap *peasycap; | |
658 | struct data_buffer *pdata_buffer; | |
659 | size_t szret; | |
660 | ||
661 | /*---------------------------------------------------------------------------*/ | |
662 | /* | |
663 | * DO A BLOCKING READ TO TRANSFER DATA TO USER SPACE. | |
664 | * | |
665 | ****************************************************************************** | |
666 | ***** N.B. IF THIS FUNCTION RETURNS 0, NOTHING IS SEEN IN USER SPACE. ****** | |
667 | ***** THIS CONDITION SIGNIFIES END-OF-FILE. ****** | |
668 | ****************************************************************************** | |
669 | */ | |
670 | /*---------------------------------------------------------------------------*/ | |
671 | ||
672 | JOT(8, "===== easysnd_read(): kount=%i, *poff=%i\n", (int)kount, (int)(*poff)); | |
673 | ||
674 | peasycap = (struct easycap *)(file->private_data); | |
675 | if (NULL == peasycap) { | |
676 | SAY("ERROR in easysnd_read(): peasycap is NULL\n"); | |
677 | return -EFAULT; | |
678 | } | |
679 | /*---------------------------------------------------------------------------*/ | |
680 | if ((0 > peasycap->audio_read) || \ | |
3d423e91 | 681 | (peasycap->audio_buffer_page_many <= peasycap->audio_read)) { |
702422bd T |
682 | SAY("ERROR: peasycap->audio_read out of range\n"); |
683 | return -EFAULT; | |
684 | } | |
685 | pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read]; | |
686 | if ((struct data_buffer *)NULL == pdata_buffer) { | |
687 | SAY("ERROR: pdata_buffer is NULL\n"); | |
688 | return -EFAULT; | |
689 | } | |
690 | JOT(12, "before wait, %i=frag read %i=frag fill\n", \ | |
3d423e91 MT |
691 | (peasycap->audio_read / peasycap->audio_pages_per_fragment), \ |
692 | (peasycap->audio_fill / peasycap->audio_pages_per_fragment)); | |
693 | fragment = (peasycap->audio_read / peasycap->audio_pages_per_fragment); | |
694 | while ((fragment == (peasycap->audio_fill / \ | |
695 | peasycap->audio_pages_per_fragment)) || \ | |
702422bd T |
696 | (0 == (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo)))) { |
697 | if (file->f_flags & O_NONBLOCK) { | |
698 | JOT(16, "returning -EAGAIN as instructed\n"); | |
699 | return -EAGAIN; | |
700 | } | |
701 | rc = wait_event_interruptible(peasycap->wq_audio, \ | |
702 | (peasycap->audio_idle || peasycap->audio_eof || \ | |
703 | ((fragment != (peasycap->audio_fill / \ | |
3d423e91 | 704 | peasycap->audio_pages_per_fragment)) && \ |
702422bd T |
705 | (0 < (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo)))))); |
706 | if (0 != rc) { | |
707 | SAY("aborted by signal\n"); | |
708 | return -ERESTARTSYS; | |
709 | } | |
710 | if (peasycap->audio_eof) { | |
711 | JOT(8, "returning 0 because %i=audio_eof\n", \ | |
712 | peasycap->audio_eof); | |
713 | kill_audio_urbs(peasycap); | |
714 | msleep(500); | |
715 | return 0; | |
716 | } | |
717 | if (peasycap->audio_idle) { | |
718 | JOT(16, "returning 0 because %i=audio_idle\n", \ | |
719 | peasycap->audio_idle); | |
720 | return 0; | |
721 | } | |
722 | if (!peasycap->audio_isoc_streaming) { | |
723 | JOT(16, "returning 0 because audio urbs not streaming\n"); | |
724 | return 0; | |
725 | } | |
726 | } | |
727 | JOT(12, "after wait, %i=frag read %i=frag fill\n", \ | |
3d423e91 MT |
728 | (peasycap->audio_read / peasycap->audio_pages_per_fragment), \ |
729 | (peasycap->audio_fill / peasycap->audio_pages_per_fragment)); | |
702422bd | 730 | szret = (size_t)0; |
3d423e91 MT |
731 | while (fragment == (peasycap->audio_read / \ |
732 | peasycap->audio_pages_per_fragment)) { | |
702422bd T |
733 | if (NULL == pdata_buffer->pgo) { |
734 | SAY("ERROR: pdata_buffer->pgo is NULL\n"); | |
735 | return -EFAULT; | |
736 | } | |
737 | if (NULL == pdata_buffer->pto) { | |
738 | SAY("ERROR: pdata_buffer->pto is NULL\n"); | |
739 | return -EFAULT; | |
740 | } | |
741 | kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo); | |
742 | if (0 > kount1) { | |
743 | SAY("easysnd_read: MISTAKE: kount1 is negative\n"); | |
744 | return -ERESTARTSYS; | |
745 | } | |
746 | if (!kount1) { | |
747 | (peasycap->audio_read)++; | |
3d423e91 | 748 | if (peasycap->audio_buffer_page_many <= peasycap->audio_read) |
702422bd T |
749 | peasycap->audio_read = 0; |
750 | JOT(12, "bumped peasycap->audio_read to %i\n", \ | |
751 | peasycap->audio_read); | |
752 | ||
753 | if (fragment != (peasycap->audio_read / \ | |
3d423e91 | 754 | peasycap->audio_pages_per_fragment)) |
702422bd T |
755 | break; |
756 | ||
757 | if ((0 > peasycap->audio_read) || \ | |
3d423e91 MT |
758 | (peasycap->audio_buffer_page_many <= \ |
759 | peasycap->audio_read)) { | |
702422bd T |
760 | SAY("ERROR: peasycap->audio_read out of range\n"); |
761 | return -EFAULT; | |
762 | } | |
763 | pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read]; | |
764 | if ((struct data_buffer *)NULL == pdata_buffer) { | |
765 | SAY("ERROR: pdata_buffer is NULL\n"); | |
766 | return -EFAULT; | |
767 | } | |
768 | if (NULL == pdata_buffer->pgo) { | |
769 | SAY("ERROR: pdata_buffer->pgo is NULL\n"); | |
770 | return -EFAULT; | |
771 | } | |
772 | if (NULL == pdata_buffer->pto) { | |
773 | SAY("ERROR: pdata_buffer->pto is NULL\n"); | |
774 | return -EFAULT; | |
775 | } | |
776 | kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo); | |
777 | } | |
778 | JOT(12, "ready to send %li bytes\n", (long int) kount1); | |
779 | JOT(12, "still to send %li bytes\n", (long int) kount); | |
780 | more = kount1; | |
781 | if (more > kount) | |
782 | more = kount; | |
783 | JOT(12, "agreed to send %li bytes from page %i\n", \ | |
784 | more, peasycap->audio_read); | |
785 | if (!more) | |
786 | break; | |
787 | ||
788 | /*---------------------------------------------------------------------------*/ | |
789 | /* | |
790 | * ACCUMULATE DYNAMIC-RANGE INFORMATION | |
791 | */ | |
792 | /*---------------------------------------------------------------------------*/ | |
793 | p0 = (unsigned char *)pdata_buffer->pgo; l0 = 0; lm = more/2; | |
794 | while (l0 < lm) { | |
795 | SUMMER(p0, &peasycap->audio_sample, &peasycap->audio_niveau, \ | |
796 | &peasycap->audio_square); l0++; p0 += 2; | |
797 | } | |
798 | /*---------------------------------------------------------------------------*/ | |
799 | rc = copy_to_user(puserspacebuffer, pdata_buffer->pto, more); | |
800 | if (0 != rc) { | |
801 | SAY("ERROR: copy_to_user() returned %li\n", rc); | |
802 | return -EFAULT; | |
803 | } | |
804 | *poff += (loff_t)more; | |
805 | szret += (size_t)more; | |
806 | pdata_buffer->pto += more; | |
807 | puserspacebuffer += more; | |
808 | kount -= (size_t)more; | |
809 | } | |
810 | JOT(12, "after read, %i=frag read %i=frag fill\n", \ | |
3d423e91 MT |
811 | (peasycap->audio_read / peasycap->audio_pages_per_fragment), \ |
812 | (peasycap->audio_fill / peasycap->audio_pages_per_fragment)); | |
702422bd T |
813 | if (kount < 0) { |
814 | SAY("MISTAKE: %li=kount %li=szret\n", \ | |
815 | (long int)kount, (long int)szret); | |
816 | } | |
817 | /*---------------------------------------------------------------------------*/ | |
818 | /* | |
819 | * CALCULATE DYNAMIC RANGE FOR (VAPOURWARE) AUTOMATIC VOLUME CONTROL | |
820 | */ | |
821 | /*---------------------------------------------------------------------------*/ | |
822 | if (peasycap->audio_sample) { | |
823 | below = peasycap->audio_sample; | |
824 | above = peasycap->audio_square; | |
825 | sdr = signed_div(above, below); | |
826 | above = sdr.quotient; | |
827 | mean = peasycap->audio_niveau; | |
828 | sdr = signed_div(mean, peasycap->audio_sample); | |
829 | ||
7ebc8760 | 830 | JOT(8, "%8lli=mean %8lli=meansquare after %lli samples, =>\n", \ |
702422bd T |
831 | sdr.quotient, above, peasycap->audio_sample); |
832 | ||
833 | sdr = signed_div(above, 32768); | |
834 | JOT(8, "audio dynamic range is roughly %lli\n", sdr.quotient); | |
835 | } | |
836 | /*---------------------------------------------------------------------------*/ | |
837 | /* | |
838 | * UPDATE THE AUDIO CLOCK | |
839 | */ | |
840 | /*---------------------------------------------------------------------------*/ | |
841 | do_gettimeofday(&timeval); | |
842 | if (!peasycap->timeval1.tv_sec) { | |
843 | audio_bytes = 0; | |
844 | timeval1 = timeval; | |
845 | ||
846 | if (mutex_lock_interruptible(&(peasycap->mutex_timeval1))) | |
847 | return -ERESTARTSYS; | |
848 | peasycap->timeval1 = timeval1; | |
849 | mutex_unlock(&(peasycap->mutex_timeval1)); | |
850 | sdr.quotient = 192000; | |
851 | } else { | |
852 | audio_bytes += (long long int) szret; | |
853 | below = ((long long int)(1000000)) * \ | |
854 | ((long long int)(timeval.tv_sec - timeval1.tv_sec)) + \ | |
855 | (long long int)(timeval.tv_usec - timeval1.tv_usec); | |
856 | above = 1000000 * ((long long int) audio_bytes); | |
857 | ||
858 | if (below) | |
859 | sdr = signed_div(above, below); | |
860 | else | |
861 | sdr.quotient = 192000; | |
862 | } | |
863 | JOT(8, "audio streaming at %lli bytes/second\n", sdr.quotient); | |
864 | if (mutex_lock_interruptible(&(peasycap->mutex_timeval1))) | |
865 | return -ERESTARTSYS; | |
866 | peasycap->dnbydt = sdr.quotient; | |
867 | mutex_unlock(&(peasycap->mutex_timeval1)); | |
868 | ||
869 | JOT(8, "returning %li\n", (long int)szret); | |
870 | return szret; | |
871 | } | |
872 | /*****************************************************************************/ | |
873 | /*---------------------------------------------------------------------------*/ | |
874 | /* | |
875 | * SUBMIT ALL AUDIO URBS. | |
876 | */ | |
877 | /*---------------------------------------------------------------------------*/ | |
878 | int | |
879 | submit_audio_urbs(struct easycap *peasycap) | |
880 | { | |
881 | struct data_urb *pdata_urb; | |
882 | struct urb *purb; | |
883 | struct list_head *plist_head; | |
884 | int j, isbad, m, rc; | |
885 | int isbuf; | |
886 | ||
887 | if ((struct list_head *)NULL == peasycap->purb_audio_head) { | |
888 | SAY("ERROR: peasycap->urb_audio_head uninitialized\n"); | |
889 | return -EFAULT; | |
890 | } | |
891 | if ((struct usb_device *)NULL == peasycap->pusb_device) { | |
892 | SAY("ERROR: peasycap->pusb_device is NULL\n"); | |
893 | return -EFAULT; | |
894 | } | |
895 | if (!peasycap->audio_isoc_streaming) { | |
896 | JOT(4, "initial submission of all audio urbs\n"); | |
897 | rc = usb_set_interface(peasycap->pusb_device, | |
898 | peasycap->audio_interface, \ | |
899 | peasycap->audio_altsetting_on); | |
900 | JOT(8, "usb_set_interface(.,%i,%i) returned %i\n", \ | |
901 | peasycap->audio_interface, \ | |
902 | peasycap->audio_altsetting_on, rc); | |
903 | ||
904 | isbad = 0; m = 0; | |
905 | list_for_each(plist_head, (peasycap->purb_audio_head)) { | |
906 | pdata_urb = list_entry(plist_head, struct data_urb, list_head); | |
907 | if (NULL != pdata_urb) { | |
908 | purb = pdata_urb->purb; | |
909 | if (NULL != purb) { | |
910 | isbuf = pdata_urb->isbuf; | |
911 | ||
912 | purb->interval = 1; | |
913 | purb->dev = peasycap->pusb_device; | |
914 | purb->pipe = \ | |
915 | usb_rcvisocpipe(peasycap->pusb_device,\ | |
916 | peasycap->audio_endpointnumber); | |
917 | purb->transfer_flags = URB_ISO_ASAP; | |
918 | purb->transfer_buffer = \ | |
919 | peasycap->audio_isoc_buffer[isbuf].pgo; | |
920 | purb->transfer_buffer_length = \ | |
921 | peasycap->audio_isoc_buffer_size; | |
922 | purb->complete = easysnd_complete; | |
923 | purb->context = peasycap; | |
924 | purb->start_frame = 0; | |
925 | purb->number_of_packets = \ | |
926 | peasycap->audio_isoc_framesperdesc; | |
927 | for (j = 0; j < peasycap->\ | |
928 | audio_isoc_framesperdesc; \ | |
929 | j++) { | |
930 | purb->iso_frame_desc[j].offset = j * \ | |
931 | peasycap->\ | |
932 | audio_isoc_maxframesize; | |
933 | purb->iso_frame_desc[j].length = \ | |
934 | peasycap->\ | |
935 | audio_isoc_maxframesize; | |
936 | } | |
937 | ||
938 | rc = usb_submit_urb(purb, GFP_KERNEL); | |
939 | if (0 != rc) { | |
940 | isbad++; | |
941 | SAY("ERROR: usb_submit_urb() failed" \ | |
942 | " for urb with rc:\n"); | |
943 | switch (rc) { | |
944 | case -ENOMEM: { | |
945 | SAY("ENOMEM\n"); break; | |
946 | } | |
947 | case -ENODEV: { | |
948 | SAY("ENODEV\n"); break; | |
949 | } | |
950 | case -ENXIO: { | |
951 | SAY("ENXIO\n"); break; | |
952 | } | |
953 | case -EINVAL: { | |
954 | SAY("EINVAL\n"); break; | |
955 | } | |
956 | case -EAGAIN: { | |
957 | SAY("EAGAIN\n"); break; | |
958 | } | |
959 | case -EFBIG: { | |
960 | SAY("EFBIG\n"); break; | |
961 | } | |
962 | case -EPIPE: { | |
963 | SAY("EPIPE\n"); break; | |
964 | } | |
965 | case -EMSGSIZE: { | |
966 | SAY("EMSGSIZE\n"); break; | |
967 | } | |
7ebc8760 MT |
968 | case -ENOSPC: { |
969 | SAY("ENOSPC\n"); break; | |
970 | } | |
702422bd T |
971 | default: { |
972 | SAY("unknown error code %i\n",\ | |
973 | rc); break; | |
974 | } | |
975 | } | |
976 | } else { | |
977 | m++; | |
978 | } | |
979 | } else { | |
980 | isbad++; | |
981 | } | |
982 | } else { | |
983 | isbad++; | |
984 | } | |
985 | } | |
986 | if (isbad) { | |
987 | JOT(4, "attempting cleanup instead of submitting\n"); | |
988 | list_for_each(plist_head, (peasycap->purb_audio_head)) { | |
989 | pdata_urb = list_entry(plist_head, struct data_urb, \ | |
990 | list_head); | |
991 | if (NULL != pdata_urb) { | |
992 | purb = pdata_urb->purb; | |
993 | if (NULL != purb) | |
994 | usb_kill_urb(purb); | |
995 | } | |
996 | } | |
997 | peasycap->audio_isoc_streaming = 0; | |
998 | } else { | |
999 | peasycap->audio_isoc_streaming = 1; | |
1000 | JOT(4, "submitted %i audio urbs\n", m); | |
1001 | } | |
1002 | } else | |
1003 | JOT(4, "already streaming audio urbs\n"); | |
1004 | ||
1005 | return 0; | |
1006 | } | |
1007 | /*****************************************************************************/ | |
1008 | /*---------------------------------------------------------------------------*/ | |
1009 | /* | |
1010 | * KILL ALL AUDIO URBS. | |
1011 | */ | |
1012 | /*---------------------------------------------------------------------------*/ | |
1013 | int | |
1014 | kill_audio_urbs(struct easycap *peasycap) | |
1015 | { | |
1016 | int m; | |
1017 | struct list_head *plist_head; | |
1018 | struct data_urb *pdata_urb; | |
1019 | ||
1020 | if (peasycap->audio_isoc_streaming) { | |
1021 | if ((struct list_head *)NULL != peasycap->purb_audio_head) { | |
1022 | peasycap->audio_isoc_streaming = 0; | |
1023 | JOT(4, "killing audio urbs\n"); | |
1024 | m = 0; | |
1025 | list_for_each(plist_head, (peasycap->purb_audio_head)) { | |
1026 | pdata_urb = list_entry(plist_head, struct data_urb, | |
1027 | list_head); | |
1028 | if ((struct data_urb *)NULL != pdata_urb) { | |
1029 | if ((struct urb *)NULL != pdata_urb->purb) { | |
1030 | usb_kill_urb(pdata_urb->purb); | |
1031 | m++; | |
1032 | } | |
1033 | } | |
1034 | } | |
1035 | JOT(4, "%i audio urbs killed\n", m); | |
1036 | } else { | |
1037 | SAY("ERROR: peasycap->purb_audio_head is NULL\n"); | |
1038 | return -EFAULT; | |
1039 | } | |
1040 | } else { | |
1041 | JOT(8, "%i=audio_isoc_streaming, no audio urbs killed\n", \ | |
1042 | peasycap->audio_isoc_streaming); | |
1043 | } | |
1044 | return 0; | |
1045 | } | |
1046 | /*****************************************************************************/ |