1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2012-2022 Hans Petter Selasky
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/queue.h>
29 #include <sys/types.h>
30 #include <sys/filio.h>
31 #include <sys/linker.h>
32 #include <sys/rtprio.h>
33 #include <sys/nv.h>
34 #include <sys/sndstat.h>
35 #include <sys/soundcard.h>
36 #include <sys/sysctl.h>
37
38 #include <dlfcn.h>
39 #include <errno.h>
40 #include <grp.h>
41 #include <stdio.h>
42 #include <stdint.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <err.h>
47 #include <sysexits.h>
48 #include <signal.h>
49 #include <fcntl.h>
50 #include <paths.h>
51
52 #include <cuse.h>
53 #include <pthread.h>
54
55 #include "backend.h"
56 #include "int.h"
57 #include "virtual_oss.h"
58
59 #define SYSCTL_BASECLONE "hw.snd.basename_clone"
60
61 pthread_mutex_t atomic_mtx;
62 pthread_cond_t atomic_cv;
63
64 static void
atomic_init(void)65 atomic_init(void)
66 {
67 if (pthread_mutex_init(&atomic_mtx, NULL) != 0)
68 err(1, "pthread_mutex_init");
69 if (pthread_cond_init(&atomic_cv, NULL) != 0)
70 err(1, "pthread_cond_init");
71 }
72
73 uint32_t
vclient_sample_bytes(vclient_t * pvc)74 vclient_sample_bytes(vclient_t *pvc)
75 {
76 uint32_t fmt = pvc->format;
77
78 if (fmt & AFMT_16BIT)
79 return (2);
80 else if (fmt & AFMT_24BIT)
81 return (3);
82 else if (fmt & AFMT_32BIT)
83 return (4);
84 else if (fmt & AFMT_8BIT)
85 return (1);
86 else
87 return (0);
88 /* TODO AFMT_BPS */
89 }
90
91 static uint32_t
vclient_output_delay(vclient_t * pvc)92 vclient_output_delay(vclient_t *pvc)
93 {
94 uint64_t size;
95 uint64_t mod;
96
97 if (pvc->tx_busy == 0)
98 vclient_import_write_locked(pvc);
99
100 mod = pvc->channels * vclient_sample_bytes(pvc);
101
102 size = vring_total_read_len(&pvc->tx_ring[0]);
103 size = (size / 8) * vclient_sample_bytes(pvc);
104
105 size = (size * (uint64_t)pvc->sample_rate) /
106 (uint64_t)voss_dsp_sample_rate;
107 size += vring_total_read_len(&pvc->tx_ring[1]);
108 size -= size % mod;
109
110 return (size);
111 }
112
113 static uint32_t
vclient_input_delay(vclient_t * pvc)114 vclient_input_delay(vclient_t *pvc)
115 {
116 if (pvc->rx_busy == 0)
117 vclient_export_read_locked(pvc);
118 return (vring_total_read_len(&pvc->rx_ring[1]));
119 }
120
121 uint32_t
vclient_bufsize_scaled(vclient_t * pvc)122 vclient_bufsize_scaled(vclient_t *pvc)
123 {
124 uint32_t samples_scaled = ((uint64_t)voss_dsp_samples *
125 (uint64_t)pvc->sample_rate) / (uint64_t)voss_dsp_sample_rate;
126 if (samples_scaled == 0)
127 samples_scaled = 1;
128 return (pvc->channels * samples_scaled * vclient_sample_bytes(pvc));
129 }
130
131 static uint64_t
vclient_bufsize_consumed(vclient_t * pvc,uint64_t ts)132 vclient_bufsize_consumed(vclient_t *pvc, uint64_t ts)
133 {
134 int64_t delta;
135 int64_t samples_scaled;
136 int64_t retval;
137
138 delta = virtual_oss_timestamp() - ts;
139 if (delta < 0)
140 delta = 0;
141 samples_scaled = (delta * (uint64_t)pvc->sample_rate) / 1000000000ULL;
142 if (samples_scaled < 0)
143 samples_scaled = 0;
144 retval = pvc->channels * samples_scaled * vclient_sample_bytes(pvc);
145 if (retval < 0)
146 retval = 0;
147 return (retval);
148 }
149
150 /*
151 * VLC and some other audio player use this value for jitter
152 * computations and expect it to be very accurate. VirtualOSS is block
153 * based and does not have sample accuracy. Use the system clock to
154 * update this value as we go along instead:
155 */
156 static uint32_t
vclient_output_delay_adjusted(vclient_t * pvc)157 vclient_output_delay_adjusted(vclient_t *pvc)
158 {
159 int64_t retval = vclient_output_delay(pvc) -
160 vclient_bufsize_consumed(pvc, pvc->tx_timestamp);
161 if (retval < 0)
162 retval = 0;
163 return (retval);
164 }
165
166 vmonitor_t *
vmonitor_alloc(int * pid,vmonitor_head_t * phead)167 vmonitor_alloc(int *pid, vmonitor_head_t *phead)
168 {
169 int id = 0;
170 vmonitor_t *pvm;
171
172 TAILQ_FOREACH(pvm, phead, entry)
173 id++;
174
175 if (id >= 64) {
176 *pid = 0;
177 return (NULL);
178 }
179 pvm = malloc(sizeof(*pvm));
180 if (pvm == NULL) {
181 *pid = 0;
182 return (NULL);
183 }
184 memset(pvm, 0, sizeof(*pvm));
185
186 pvm->mute = 1;
187
188 TAILQ_INSERT_TAIL(phead, pvm, entry);
189
190 *pid = id;
191 return (pvm);
192 }
193
194 int64_t
vclient_noise(uint32_t * pnoise,int64_t volume,int8_t shift)195 vclient_noise(uint32_t *pnoise, int64_t volume, int8_t shift)
196 {
197 const uint32_t prime = 0xFFFF1DU;
198 int64_t temp;
199
200 /* compute next noise sample */
201 temp = *pnoise;
202 if (temp & 1)
203 temp += prime;
204 temp /= 2;
205 *pnoise = temp;
206
207 /* unsigned to signed conversion */
208 temp ^= 0x800000ULL;
209 if (temp & 0x800000U)
210 temp |= -0x800000ULL;
211
212 /* properly amplify */
213 temp *= volume;
214
215 /* bias shift */
216 shift -= 23 + VVOLUME_UNIT_SHIFT;
217
218 /* range check and shift noise */
219 if (__predict_false(shift < -63 || shift > 63))
220 temp = 0;
221 else if (shift < 0)
222 temp >>= -shift;
223 else
224 temp <<= shift;
225
226 return (temp);
227 }
228
229 static void
vresample_free(vresample_t * pvr)230 vresample_free(vresample_t *pvr)
231 {
232 if (pvr->state != NULL)
233 src_delete(pvr->state);
234 free(pvr->data_in);
235 free(pvr->data_out);
236 memset(pvr, 0, sizeof(*pvr));
237 }
238
239 static int
vresample_setup(vclient_t * pvc,vresample_t * pvr,int samples)240 vresample_setup(vclient_t *pvc, vresample_t *pvr, int samples)
241 {
242 int code = 0;
243
244 if (pvr->state != NULL)
245 return (0);
246 pvr->state = src_new(voss_libsamplerate_quality, pvc->channels, &code);
247 if (pvr->state == NULL)
248 goto error;
249 pvr->data_in = malloc(sizeof(float) * samples);
250 if (pvr->data_in == NULL)
251 goto error;
252 pvr->data_out = malloc(sizeof(float) * samples);
253 if (pvr->data_out == NULL)
254 goto error;
255 pvr->data.data_in = pvr->data_in;
256 pvr->data.data_out = pvr->data_out;
257 return (0);
258 error:
259 vresample_free(pvr);
260 return (CUSE_ERR_NO_MEMORY);
261 }
262
263 void
vclient_free(vclient_t * pvc)264 vclient_free(vclient_t *pvc)
265 {
266 vresample_free(&pvc->rx_resample);
267 vresample_free(&pvc->tx_resample);
268
269 /* free equalizer */
270 vclient_eq_free(pvc);
271
272 /* free ring buffers */
273 vring_free(&pvc->rx_ring[0]);
274 vring_free(&pvc->rx_ring[1]);
275 vring_free(&pvc->tx_ring[0]);
276 vring_free(&pvc->tx_ring[1]);
277
278 free(pvc);
279 }
280
281 vclient_t *
vclient_alloc(void)282 vclient_alloc(void)
283 {
284 vclient_t *pvc;
285
286 pvc = malloc(sizeof(*pvc));
287 if (pvc == NULL)
288 return (NULL);
289
290 memset(pvc, 0, sizeof(*pvc));
291
292 pvc->rx_noise_rem = 1;
293 pvc->tx_noise_rem = 1;
294 pvc->rx_volume = 1 << VVOLUME_UNIT_SHIFT;
295 pvc->tx_volume = 1 << VVOLUME_UNIT_SHIFT;
296
297 return (pvc);
298 }
299
300 int
vclient_get_default_fmt(vprofile_t * pvp,int type)301 vclient_get_default_fmt(vprofile_t *pvp, int type)
302 {
303 int retval;
304
305 if (type == VTYPE_WAV_HDR) {
306 switch (pvp->bits) {
307 case 16:
308 retval = AFMT_S16_LE;
309 break;
310 case 24:
311 retval = AFMT_S24_LE;
312 break;
313 case 32:
314 retval = AFMT_S32_LE;
315 break;
316 default:
317 retval = AFMT_S8;
318 break;
319 }
320 } else {
321 switch (pvp->bits) {
322 case 16:
323 retval = AFMT_S16_NE;
324 break;
325 case 24:
326 retval = AFMT_S24_NE;
327 break;
328 case 32:
329 retval = AFMT_S32_NE;
330 break;
331 default:
332 retval = AFMT_S8;
333 break;
334 }
335 }
336 return (retval);
337 }
338
339 int
vclient_setup_buffers(vclient_t * pvc,int size,int frags,int channels,int format,int sample_rate)340 vclient_setup_buffers(vclient_t *pvc, int size, int frags,
341 int channels, int format, int sample_rate)
342 {
343 size_t bufsize_internal;
344 size_t bufsize_min;
345 size_t mod_internal;
346 size_t mod;
347 uint64_t ts;
348 int bufsize;
349
350 /* check we are not busy */
351 if (pvc->rx_busy || pvc->tx_busy)
352 return (CUSE_ERR_BUSY);
353
354 /* free equalizer */
355 vclient_eq_free(pvc);
356
357 /* free existing ring buffers */
358 vring_free(&pvc->rx_ring[0]);
359 vring_free(&pvc->rx_ring[1]);
360 vring_free(&pvc->tx_ring[0]);
361 vring_free(&pvc->tx_ring[1]);
362
363 /* reset resampler */
364 vresample_free(&pvc->rx_resample);
365 vresample_free(&pvc->tx_resample);
366
367 if (sample_rate > 0)
368 pvc->sample_rate = sample_rate;
369 if (format != 0)
370 pvc->format = format;
371 if (channels > 0)
372 pvc->channels = channels;
373
374 mod = pvc->channels * vclient_sample_bytes(pvc);
375 mod_internal = pvc->channels * 8;
376
377 if (size > 0) {
378 size += mod - 1;
379 size -= size % mod;
380
381 pvc->buffer_size = size;
382 pvc->buffer_size_set = 1;
383 } else if (pvc->buffer_size_set == 0)
384 pvc->buffer_size = vclient_bufsize_scaled(pvc);
385
386 pvc->low_water = pvc->buffer_size;
387
388 if (frags > 0) {
389 pvc->buffer_frags = frags;
390 pvc->buffer_frags_set = 1;
391 } else if (pvc->buffer_frags_set == 0)
392 pvc->buffer_frags = 2;
393
394 /* sanity checks */
395 if (frags < 0 || size < 0)
396 return (CUSE_ERR_INVALID);
397 if (pvc->format == 0)
398 return (CUSE_ERR_INVALID);
399 if (pvc->buffer_frags <= 0 || pvc->buffer_frags >= 1024)
400 return (CUSE_ERR_INVALID);
401 if (pvc->buffer_size <= 0 || pvc->buffer_size >= (1024 * 1024))
402 return (CUSE_ERR_INVALID);
403 if ((pvc->buffer_size * pvc->buffer_frags) >= (128 * 1024 * 1024))
404 return (CUSE_ERR_INVALID);
405 if (pvc->channels <= 0 || pvc->channels > pvc->profile->channels)
406 return (CUSE_ERR_INVALID);
407
408 /* get buffer sizes */
409 bufsize = pvc->buffer_frags * pvc->buffer_size;
410 bufsize_internal = ((uint64_t)bufsize * (uint64_t)voss_dsp_sample_rate * 8ULL) /
411 ((uint64_t)pvc->sample_rate * (uint64_t)vclient_sample_bytes(pvc));
412
413 bufsize_min = voss_dsp_samples * pvc->channels * 8;
414
415 /* check for too small buffer size */
416 if (bufsize_internal < bufsize_min)
417 return (CUSE_ERR_INVALID);
418
419 /* allow for jitter */
420 bufsize_internal *= 2ULL;
421
422 /* align buffer size */
423 bufsize_internal += (mod_internal - 1);
424 bufsize_internal -= (bufsize_internal % mod_internal);
425
426 /* allocate new buffers */
427 if (vring_alloc(&pvc->rx_ring[0], bufsize_internal))
428 goto err_0;
429 if (vring_alloc(&pvc->rx_ring[1], bufsize))
430 goto err_1;
431 if (vring_alloc(&pvc->tx_ring[0], bufsize_internal))
432 goto err_2;
433 if (vring_alloc(&pvc->tx_ring[1], bufsize))
434 goto err_3;
435 if (vclient_eq_alloc(pvc))
436 goto err_4;
437
438 ts = virtual_oss_timestamp();
439
440 pvc->rx_samples = 0;
441 pvc->tx_samples = 0;
442 pvc->tx_timestamp = ts;
443 pvc->rx_timestamp = ts;
444
445 return (0);
446
447 err_4:
448 vring_free(&pvc->tx_ring[1]);
449 err_3:
450 vring_free(&pvc->tx_ring[0]);
451 err_2:
452 vring_free(&pvc->rx_ring[1]);
453 err_1:
454 vring_free(&pvc->rx_ring[0]);
455 err_0:
456 return (CUSE_ERR_NO_MEMORY);
457 }
458
459 static int
vclient_open_sub(struct cuse_dev * pdev,int fflags __unused,int type)460 vclient_open_sub(struct cuse_dev *pdev, int fflags __unused, int type)
461 {
462 vclient_t *pvc;
463 vprofile_t *pvp;
464 int error;
465
466 pvp = cuse_dev_get_priv0(pdev);
467
468 pvc = vclient_alloc();
469 if (pvc == NULL)
470 return (CUSE_ERR_NO_MEMORY);
471
472 pvc->profile = pvp;
473
474 /* setup buffers */
475 error = vclient_setup_buffers(pvc, 0, 0, pvp->channels,
476 vclient_get_default_fmt(pvp, type), voss_dsp_sample_rate);
477 if (error != 0) {
478 vclient_free(pvc);
479 return (error);
480 }
481
482 pvc->type = type;
483
484 cuse_dev_set_per_file_handle(pdev, pvc);
485
486 atomic_lock();
487 /* only allow one synchronization source at a time */
488 if (pvc->profile->synchronized) {
489 if (voss_has_synchronization != 0)
490 error = CUSE_ERR_BUSY;
491 else
492 voss_has_synchronization++;
493 }
494 if (error == 0)
495 TAILQ_INSERT_TAIL(&pvc->profile->head, pvc, entry);
496 atomic_unlock();
497
498 return (error);
499 }
500
501 static int
vclient_open_wav(struct cuse_dev * pdev,int fflags)502 vclient_open_wav(struct cuse_dev *pdev, int fflags)
503 {
504 return (vclient_open_sub(pdev, fflags, VTYPE_WAV_HDR));
505 }
506
507 static int
vclient_open_oss(struct cuse_dev * pdev,int fflags)508 vclient_open_oss(struct cuse_dev *pdev, int fflags)
509 {
510 return (vclient_open_sub(pdev, fflags, VTYPE_OSS_DAT));
511 }
512
513 static int
vclient_close(struct cuse_dev * pdev,int fflags __unused)514 vclient_close(struct cuse_dev *pdev, int fflags __unused)
515 {
516 vclient_t *pvc;
517
518 pvc = cuse_dev_get_per_file_handle(pdev);
519 if (pvc == NULL)
520 return (CUSE_ERR_INVALID);
521
522 atomic_lock();
523 if (pvc->profile->synchronized) {
524 voss_has_synchronization--;
525
526 /* wait for virtual_oss_process(), if any */
527 while (pvc->sync_busy) {
528 pvc->sync_wakeup = 1;
529 atomic_wakeup();
530 atomic_wait();
531 }
532 }
533 TAILQ_REMOVE(&pvc->profile->head, pvc, entry);
534 atomic_unlock();
535
536 vclient_free(pvc);
537
538 return (0);
539 }
540
541 static int
vclient_read_silence_locked(vclient_t * pvc)542 vclient_read_silence_locked(vclient_t *pvc)
543 {
544 size_t size;
545 int delta_in;
546
547 delta_in = pvc->profile->rec_delay - pvc->rec_delay;
548 if (delta_in < 1)
549 return (0);
550
551 size = delta_in * pvc->channels * 8;
552 size = vring_write_zero(&pvc->rx_ring[0], size);
553 pvc->rec_delay += size / (pvc->channels * 8);
554
555 delta_in = pvc->profile->rec_delay - pvc->rec_delay;
556 if (delta_in < 1)
557 return (0);
558
559 return (1);
560 }
561
562 static int
vclient_generate_wav_header_locked(vclient_t * pvc)563 vclient_generate_wav_header_locked(vclient_t *pvc)
564 {
565 uint8_t *ptr;
566 size_t mod;
567 size_t len;
568
569 vring_get_write(&pvc->rx_ring[1], &ptr, &len);
570
571 mod = pvc->channels * vclient_sample_bytes(pvc);
572
573 if (mod == 0 || len < (44 + mod - 1))
574 return (CUSE_ERR_INVALID);
575
576 /* align to next sample */
577 len = 44 + mod - 1;
578 len -= len % mod;
579
580 /* pre-advance write pointer */
581 vring_inc_write(&pvc->rx_ring[1], len);
582
583 /* clear block */
584 memset(ptr, 0, len);
585
586 /* fill out data header */
587 ptr[len - 8] = 'd';
588 ptr[len - 7] = 'a';
589 ptr[len - 6] = 't';
590 ptr[len - 5] = 'a';
591
592 /* magic for unspecified length */
593 ptr[len - 4] = 0x00;
594 ptr[len - 3] = 0xF0;
595 ptr[len - 2] = 0xFF;
596 ptr[len - 1] = 0x7F;
597
598 /* fill out header */
599 *ptr++ = 'R';
600 *ptr++ = 'I';
601 *ptr++ = 'F';
602 *ptr++ = 'F';
603
604 /* total chunk size - unknown */
605
606 *ptr++ = 0;
607 *ptr++ = 0;
608 *ptr++ = 0;
609 *ptr++ = 0;
610
611 *ptr++ = 'W';
612 *ptr++ = 'A';
613 *ptr++ = 'V';
614 *ptr++ = 'E';
615 *ptr++ = 'f';
616 *ptr++ = 'm';
617 *ptr++ = 't';
618 *ptr++ = ' ';
619
620 /* make sure header fits in PCM block */
621 len -= 28;
622
623 *ptr++ = len;
624 *ptr++ = len >> 8;
625 *ptr++ = len >> 16;
626 *ptr++ = len >> 24;
627
628 /* audioformat = PCM */
629
630 *ptr++ = 0x01;
631 *ptr++ = 0x00;
632
633 /* number of channels */
634
635 len = pvc->channels;
636
637 *ptr++ = len;
638 *ptr++ = len >> 8;
639
640 /* sample rate */
641
642 len = pvc->sample_rate;
643
644 *ptr++ = len;
645 *ptr++ = len >> 8;
646 *ptr++ = len >> 16;
647 *ptr++ = len >> 24;
648
649 /* byte rate */
650
651 len = pvc->sample_rate * pvc->channels * vclient_sample_bytes(pvc);
652
653 *ptr++ = len;
654 *ptr++ = len >> 8;
655 *ptr++ = len >> 16;
656 *ptr++ = len >> 24;
657
658 /* block align */
659
660 len = pvc->channels * vclient_sample_bytes(pvc);
661
662 *ptr++ = len;
663 *ptr++ = len >> 8;
664
665 /* bits per sample */
666
667 len = vclient_sample_bytes(pvc) * 8;
668
669 *ptr++ = len;
670 *ptr++ = len >> 8;
671
672 return (0);
673 }
674
675 int
vclient_export_read_locked(vclient_t * pvc)676 vclient_export_read_locked(vclient_t *pvc) __requires_exclusive(atomic_mtx)
677 {
678 enum { MAX_FRAME = 1024 };
679 size_t dst_mod;
680 size_t src_mod;
681 int error;
682
683 if (pvc->type == VTYPE_WAV_HDR) {
684 error = vclient_generate_wav_header_locked(pvc);
685 if (error != 0)
686 return (error);
687 /* only write header once */
688 pvc->type = VTYPE_WAV_DAT;
689 }
690 error = vclient_read_silence_locked(pvc);
691 if (error != 0)
692 return (0);
693
694 dst_mod = pvc->channels * vclient_sample_bytes(pvc);
695 src_mod = pvc->channels * 8;
696
697 if (pvc->sample_rate == (int)voss_dsp_sample_rate) {
698 while (1) {
699 uint8_t *src_ptr;
700 size_t src_len;
701 uint8_t *dst_ptr;
702 size_t dst_len;
703
704 vring_get_read(&pvc->rx_ring[0], &src_ptr, &src_len);
705 vring_get_write(&pvc->rx_ring[1], &dst_ptr, &dst_len);
706
707 src_len /= src_mod;
708 dst_len /= dst_mod;
709
710 /* compare number of samples */
711 if (dst_len > src_len)
712 dst_len = src_len;
713 else
714 src_len = dst_len;
715
716 if (dst_len == 0)
717 break;
718
719 src_len *= src_mod;
720 dst_len *= dst_mod;
721
722 format_export(pvc->format,
723 (const int64_t *)(uintptr_t)src_ptr,
724 dst_ptr, dst_len);
725
726 vring_inc_read(&pvc->rx_ring[0], src_len);
727 vring_inc_write(&pvc->rx_ring[1], dst_len);
728 }
729 } else {
730 vresample_t *pvr = &pvc->rx_resample;
731
732 if (vresample_setup(pvc, pvr, MAX_FRAME * pvc->channels) != 0)
733 return (CUSE_ERR_NO_MEMORY);
734
735 while (1) {
736 uint8_t *src_ptr;
737 size_t src_len;
738 uint8_t *dst_ptr;
739 size_t dst_len;
740 int64_t temp[MAX_FRAME * pvc->channels];
741 size_t samples;
742 size_t y;
743
744 vring_get_read(&pvc->rx_ring[0], &src_ptr, &src_len);
745 vring_get_write(&pvc->rx_ring[1], &dst_ptr, &dst_len);
746
747 src_len /= src_mod;
748 dst_len /= dst_mod;
749
750 /* compare number of samples */
751 if (dst_len > src_len)
752 dst_len = src_len;
753 else
754 src_len = dst_len;
755
756 if (dst_len > MAX_FRAME)
757 dst_len = src_len = MAX_FRAME;
758
759 if (dst_len == 0)
760 break;
761
762 src_len *= src_mod;
763 dst_len *= dst_mod;
764
765 for (y = 0; y != src_len; y += 8) {
766 pvr->data_in[y / 8] =
767 *(int64_t *)(uintptr_t)(src_ptr + y);
768 }
769
770 /* setup parameters for transform */
771 pvr->data.input_frames = src_len / src_mod;
772 pvr->data.output_frames = dst_len / dst_mod;
773 pvr->data.src_ratio = (float)pvc->sample_rate / (float)voss_dsp_sample_rate;
774
775 pvc->rx_busy = 1;
776 atomic_unlock();
777 error = src_process(pvr->state, &pvr->data);
778 atomic_lock();
779 pvc->rx_busy = 0;
780
781 if (error != 0)
782 break;
783
784 src_len = pvr->data.input_frames_used * src_mod;
785 dst_len = pvr->data.output_frames_gen * dst_mod;
786
787 samples = pvr->data.output_frames_gen * pvc->channels;
788
789 for (y = 0; y != samples; y++)
790 temp[y] = pvr->data_out[y];
791
792 format_export(pvc->format, temp, dst_ptr, dst_len);
793
794 vring_inc_read(&pvc->rx_ring[0], src_len);
795 vring_inc_write(&pvc->rx_ring[1], dst_len);
796
797 /* check if no data was moved */
798 if (src_len == 0 && dst_len == 0)
799 break;
800 }
801 }
802 if (pvc->sync_busy)
803 atomic_wakeup();
804 return (0);
805 }
806
807 static int
vclient_read(struct cuse_dev * pdev,int fflags,void * peer_ptr,int len)808 vclient_read(struct cuse_dev *pdev, int fflags,
809 void *peer_ptr, int len)
810 {
811 vclient_t *pvc;
812
813 int error;
814 int retval;
815
816 pvc = cuse_dev_get_per_file_handle(pdev);
817 if (pvc == NULL)
818 return (CUSE_ERR_INVALID);
819
820 atomic_lock();
821
822 if (pvc->rx_busy) {
823 atomic_unlock();
824 return (CUSE_ERR_BUSY);
825 }
826 pvc->rx_enabled = 1;
827
828 retval = 0;
829
830 while (len > 0) {
831 uint8_t *buf_ptr;
832 size_t buf_len;
833
834 error = vclient_export_read_locked(pvc);
835 if (error != 0) {
836 retval = error;
837 break;
838 }
839
840 vring_get_read(&pvc->rx_ring[1], &buf_ptr, &buf_len);
841
842 if (buf_len == 0) {
843 /* out of data */
844 if (fflags & CUSE_FFLAG_NONBLOCK) {
845 if (retval == 0)
846 retval = CUSE_ERR_WOULDBLOCK;
847 break;
848 }
849 pvc->rx_busy = 1;
850 atomic_wait();
851 pvc->rx_busy = 0;
852 if (cuse_got_peer_signal() == 0) {
853 if (retval == 0)
854 retval = CUSE_ERR_SIGNAL;
855 break;
856 }
857 continue;
858 }
859 if ((int)buf_len > len)
860 buf_len = len;
861
862 pvc->rx_busy = 1;
863 atomic_unlock();
864 error = cuse_copy_out(buf_ptr, peer_ptr, buf_len);
865 atomic_lock();
866 pvc->rx_busy = 0;
867
868 if (error != 0) {
869 retval = error;
870 break;
871 }
872 peer_ptr = ((uint8_t *)peer_ptr) + buf_len;
873 retval += buf_len;
874 len -= buf_len;
875
876 vring_inc_read(&pvc->rx_ring[1], buf_len);
877 }
878 atomic_unlock();
879
880 return (retval);
881 }
882
883 void
vclient_import_write_locked(vclient_t * pvc)884 vclient_import_write_locked(vclient_t *pvc) __requires_exclusive(atomic_mtx)
885 {
886 enum { MAX_FRAME = 1024 };
887 size_t dst_mod;
888 size_t src_mod;
889
890 dst_mod = pvc->channels * 8;
891 src_mod = pvc->channels * vclient_sample_bytes(pvc);
892
893 if (pvc->sample_rate == (int)voss_dsp_sample_rate) {
894 while (1) {
895 uint8_t *src_ptr;
896 size_t src_len;
897 uint8_t *dst_ptr;
898 size_t dst_len;
899
900 vring_get_read(&pvc->tx_ring[1], &src_ptr, &src_len);
901 vring_get_write(&pvc->tx_ring[0], &dst_ptr, &dst_len);
902
903 src_len /= src_mod;
904 dst_len /= dst_mod;
905
906 /* compare number of samples */
907 if (dst_len > src_len)
908 dst_len = src_len;
909 else
910 src_len = dst_len;
911
912 if (dst_len == 0)
913 break;
914
915 src_len *= src_mod;
916 dst_len *= dst_mod;
917
918 format_import(pvc->format, src_ptr, src_len,
919 (int64_t *)(uintptr_t)dst_ptr);
920
921 vring_inc_read(&pvc->tx_ring[1], src_len);
922 vring_inc_write(&pvc->tx_ring[0], dst_len);
923 }
924 } else {
925 vresample_t *pvr = &pvc->tx_resample;
926
927 if (vresample_setup(pvc, pvr, MAX_FRAME * pvc->channels) != 0)
928 return;
929
930 while (1) {
931 uint8_t *src_ptr;
932 size_t src_len;
933 uint8_t *dst_ptr;
934 size_t dst_len;
935 int64_t temp[MAX_FRAME * pvc->channels];
936 size_t samples;
937 size_t y;
938 int error;
939
940 vring_get_read(&pvc->tx_ring[1], &src_ptr, &src_len);
941 vring_get_write(&pvc->tx_ring[0], &dst_ptr, &dst_len);
942
943 src_len /= src_mod;
944 dst_len /= dst_mod;
945
946 /* compare number of samples */
947 if (dst_len > src_len)
948 dst_len = src_len;
949 else
950 src_len = dst_len;
951
952 if (dst_len > MAX_FRAME)
953 dst_len = src_len = MAX_FRAME;
954
955 if (dst_len == 0)
956 break;
957
958 src_len *= src_mod;
959 dst_len *= dst_mod;
960
961 format_import(pvc->format, src_ptr, src_len, temp);
962
963 src_len /= vclient_sample_bytes(pvc);
964
965 for (y = 0; y != src_len; y++)
966 pvr->data_in[y] = temp[y];
967
968 src_len *= vclient_sample_bytes(pvc);
969
970 /* setup parameters for transform */
971 pvr->data.input_frames = src_len / src_mod;
972 pvr->data.output_frames = dst_len / dst_mod;
973 pvr->data.src_ratio = (float)voss_dsp_sample_rate / (float)pvc->sample_rate;
974
975 pvc->tx_busy = 1;
976 atomic_unlock();
977 error = src_process(pvr->state, &pvr->data);
978 atomic_lock();
979 pvc->tx_busy = 0;
980
981 if (error != 0)
982 break;
983
984 src_len = pvr->data.input_frames_used * src_mod;
985 dst_len = pvr->data.output_frames_gen * dst_mod;
986
987 samples = pvr->data.output_frames_gen * pvc->channels;
988
989 for (y = 0; y != samples; y++) {
990 ((int64_t *)(uintptr_t)dst_ptr)[y] =
991 pvr->data_out[y];
992 }
993
994 vring_inc_read(&pvc->tx_ring[1], src_len);
995 vring_inc_write(&pvc->tx_ring[0], dst_len);
996
997 /* check if no data was moved */
998 if (src_len == 0 && dst_len == 0)
999 break;
1000 }
1001 }
1002 if (pvc->sync_busy)
1003 atomic_wakeup();
1004 }
1005
1006 static int
vclient_write_oss(struct cuse_dev * pdev,int fflags,const void * peer_ptr,int len)1007 vclient_write_oss(struct cuse_dev *pdev, int fflags,
1008 const void *peer_ptr, int len)
1009 {
1010 vclient_t *pvc;
1011
1012 int error;
1013 int retval;
1014
1015 pvc = cuse_dev_get_per_file_handle(pdev);
1016 if (pvc == NULL)
1017 return (CUSE_ERR_INVALID);
1018
1019 retval = 0;
1020
1021 atomic_lock();
1022
1023 if (pvc->tx_busy) {
1024 atomic_unlock();
1025 return (CUSE_ERR_BUSY);
1026 }
1027 pvc->tx_enabled = 1;
1028
1029 while (1) {
1030 uint8_t *buf_ptr;
1031 size_t buf_len;
1032
1033 vclient_import_write_locked(pvc);
1034
1035 if (len < 1)
1036 break;
1037
1038 vring_get_write(&pvc->tx_ring[1], &buf_ptr, &buf_len);
1039
1040 if (buf_len == 0) {
1041 /* out of data */
1042 if (fflags & CUSE_FFLAG_NONBLOCK) {
1043 if (retval == 0)
1044 retval = CUSE_ERR_WOULDBLOCK;
1045 break;
1046 }
1047 pvc->tx_busy = 1;
1048 atomic_wait();
1049 pvc->tx_busy = 0;
1050 if (cuse_got_peer_signal() == 0) {
1051 if (retval == 0)
1052 retval = CUSE_ERR_SIGNAL;
1053 break;
1054 }
1055 continue;
1056 }
1057 if ((int)buf_len > len)
1058 buf_len = len;
1059
1060 pvc->tx_busy = 1;
1061 atomic_unlock();
1062 error = cuse_copy_in(peer_ptr, buf_ptr, buf_len);
1063 atomic_lock();
1064 pvc->tx_busy = 0;
1065
1066 if (error != 0) {
1067 retval = error;
1068 break;
1069 }
1070 peer_ptr = ((const uint8_t *)peer_ptr) + buf_len;
1071 retval += buf_len;
1072 len -= buf_len;
1073
1074 vring_inc_write(&pvc->tx_ring[1], buf_len);
1075 }
1076 atomic_unlock();
1077
1078 return (retval);
1079 }
1080
1081 static int
vclient_write_wav(struct cuse_dev * pdev __unused,int fflags __unused,const void * peer_ptr __unused,int len __unused)1082 vclient_write_wav(struct cuse_dev *pdev __unused, int fflags __unused,
1083 const void *peer_ptr __unused, int len __unused)
1084 {
1085 return (CUSE_ERR_INVALID);
1086 }
1087
1088 static int
vclient_set_channels(vclient_t * pvc,int channels)1089 vclient_set_channels(vclient_t *pvc, int channels)
1090 {
1091 if (pvc->channels == channels)
1092 return (0);
1093 return (vclient_setup_buffers(pvc, 0, 0, channels, 0, 0));
1094 }
1095
1096 /* greatest common divisor, Euclid equation */
1097 static uint64_t
vclient_gcd_64(uint64_t a,uint64_t b)1098 vclient_gcd_64(uint64_t a, uint64_t b)
1099 {
1100 uint64_t an;
1101 uint64_t bn;
1102
1103 while (b != 0) {
1104 an = b;
1105 bn = a % b;
1106 a = an;
1107 b = bn;
1108 }
1109 return (a);
1110 }
1111
1112 static uint64_t
vclient_scale(uint64_t value,uint64_t mul,uint64_t div)1113 vclient_scale(uint64_t value, uint64_t mul, uint64_t div)
1114 {
1115 uint64_t gcd = vclient_gcd_64(mul, div);
1116
1117 mul /= gcd;
1118 div /= gcd;
1119
1120 return ((value * mul) / div);
1121 }
1122
1123 static int
vclient_ioctl_oss(struct cuse_dev * pdev,int fflags __unused,unsigned long cmd,void * peer_data)1124 vclient_ioctl_oss(struct cuse_dev *pdev, int fflags __unused,
1125 unsigned long cmd, void *peer_data)
1126 {
1127 union {
1128 int val;
1129 unsigned long long lval;
1130 oss_sysinfo sysinfo;
1131 oss_card_info card_info;
1132 oss_audioinfo audioinfo;
1133 audio_buf_info buf_info;
1134 oss_count_t oss_count;
1135 count_info oss_count_info;
1136 audio_errinfo errinfo;
1137 oss_label_t label;
1138 oss_longname_t longname;
1139 } data;
1140
1141 vclient_t *pvc;
1142
1143 uint64_t bytes;
1144
1145 int len;
1146 int error;
1147 int temp;
1148
1149 pvc = cuse_dev_get_per_file_handle(pdev);
1150 if (pvc == NULL)
1151 return (CUSE_ERR_INVALID);
1152
1153 len = IOCPARM_LEN(cmd);
1154
1155 if (len < 0 || len > (int)sizeof(data))
1156 return (CUSE_ERR_INVALID);
1157
1158 if (cmd & IOC_IN) {
1159 error = cuse_copy_in(peer_data, &data, len);
1160 if (error)
1161 return (error);
1162 } else {
1163 error = 0;
1164 }
1165
1166 atomic_lock();
1167
1168 switch (cmd) {
1169 case OSS_GETVERSION:
1170 data.val = SOUND_VERSION;
1171 break;
1172 case SNDCTL_SYSINFO:
1173 memset(&data.sysinfo, 0, sizeof(data.sysinfo));
1174 strcpy(data.sysinfo.product, "VOSS");
1175 strcpy(data.sysinfo.version, "1.0");
1176 data.sysinfo.versionnum = SOUND_VERSION;
1177 data.sysinfo.numaudios = 1;
1178 data.sysinfo.numcards = 1;
1179 data.sysinfo.numaudioengines = 1;
1180 strcpy(data.sysinfo.license, "BSD");
1181 memset(data.sysinfo.filler, -1, sizeof(data.sysinfo.filler));
1182 break;
1183 case SNDCTL_CARDINFO:
1184 memset(&data.card_info, 0, sizeof(data.card_info));
1185 strlcpy(data.card_info.shortname, pvc->profile->oss_name,
1186 sizeof(data.card_info.shortname));
1187 break;
1188 case SNDCTL_AUDIOINFO:
1189 case SNDCTL_AUDIOINFO_EX:
1190 case SNDCTL_ENGINEINFO:
1191 memset(&data.audioinfo, 0, sizeof(data.audioinfo));
1192 strlcpy(data.audioinfo.name, pvc->profile->oss_name,
1193 sizeof(data.audioinfo.name));
1194 snprintf(data.audioinfo.devnode, sizeof(data.audioinfo.devnode),
1195 "/dev/%s", pvc->profile->oss_name);
1196 data.audioinfo.caps = DSP_CAP_INPUT | DSP_CAP_OUTPUT;
1197 data.audioinfo.iformats = VSUPPORTED_AFMT;
1198 data.audioinfo.oformats = VSUPPORTED_AFMT;
1199 data.audioinfo.enabled = 1;
1200 data.audioinfo.min_rate = (int)8000;
1201 data.audioinfo.max_rate = (int)voss_dsp_sample_rate;
1202 data.audioinfo.max_channels = pvc->profile->channels;
1203 /* range check */
1204 if (voss_libsamplerate_enable == 0 ||
1205 data.audioinfo.min_rate > data.audioinfo.max_rate)
1206 data.audioinfo.min_rate = data.audioinfo.max_rate;
1207 data.audioinfo.nrates = 1;
1208 data.audioinfo.rates[0] = (int)voss_dsp_sample_rate;
1209 if (voss_libsamplerate_enable != 0 &&
1210 96000 != voss_dsp_sample_rate)
1211 data.audioinfo.rates[data.audioinfo.nrates++] = 96000;
1212 if (voss_libsamplerate_enable != 0 &&
1213 48000 != voss_dsp_sample_rate)
1214 data.audioinfo.rates[data.audioinfo.nrates++] = 48000;
1215 if (voss_libsamplerate_enable != 0 &&
1216 44100 != voss_dsp_sample_rate)
1217 data.audioinfo.rates[data.audioinfo.nrates++] = 44100;
1218 if (voss_libsamplerate_enable != 0 &&
1219 24000 != voss_dsp_sample_rate)
1220 data.audioinfo.rates[data.audioinfo.nrates++] = 24000;
1221 if (voss_libsamplerate_enable != 0 &&
1222 16000 != voss_dsp_sample_rate)
1223 data.audioinfo.rates[data.audioinfo.nrates++] = 16000;
1224 if (voss_libsamplerate_enable != 0 &&
1225 8000 != voss_dsp_sample_rate)
1226 data.audioinfo.rates[data.audioinfo.nrates++] = 8000;
1227 data.audioinfo.latency = -1;
1228 break;
1229 case FIONREAD:
1230 data.val = vclient_input_delay(pvc);
1231 break;
1232 case FIONWRITE:
1233 data.val = vring_total_read_len(&pvc->tx_ring[1]);
1234 break;
1235 case FIOASYNC:
1236 case SNDCTL_DSP_NONBLOCK:
1237 case FIONBIO:
1238 break;
1239 case SNDCTL_DSP_SETBLKSIZE:
1240 case _IOWR('P', 4, int):
1241 error = vclient_setup_buffers(pvc, data.val, 0, 0, 0, 0);
1242 /* FALLTHROUGH */
1243 case SNDCTL_DSP_GETBLKSIZE:
1244 data.val = pvc->buffer_size;
1245 break;
1246 case SNDCTL_DSP_SETFRAGMENT:
1247 if ((data.val & 0xFFFF) < 4) {
1248 /* need at least 16 bytes of buffer */
1249 data.val &= ~0xFFFF;
1250 data.val |= 4;
1251 } else if ((data.val & 0xFFFF) > 24) {
1252 /* no more than 16MBytes of buffer */
1253 data.val &= ~0xFFFF;
1254 data.val |= 24;
1255 }
1256 error = vclient_setup_buffers(pvc,
1257 (1 << (data.val & 0xFFFF)), (data.val >> 16), 0, 0, 0);
1258 if (error) {
1259 /* fallback to defaults */
1260 pvc->buffer_size_set = 0;
1261 pvc->buffer_frags_set = 0;
1262 error = vclient_setup_buffers(pvc, 0, 0, 0, 0, 0);
1263 if (error)
1264 break;
1265 /* figure out log2() of actual buffer size */
1266 for (data.val = 0;
1267 data.val < 24 && (1U << data.val) < pvc->buffer_size;
1268 data.val++)
1269 ;
1270 /* or in the actual number of fragments */
1271 data.val |= (pvc->buffer_frags << 16);
1272 }
1273 break;
1274 case SNDCTL_DSP_RESET:
1275 error = vclient_setup_buffers(pvc, 0, 0, 0, 0, 0);
1276 break;
1277 case SNDCTL_DSP_SYNC:
1278 break;
1279 case SNDCTL_DSP_SPEED:
1280 if (data.val >= 8000 && data.val <= 96000 &&
1281 voss_libsamplerate_enable != 0) {
1282 error = vclient_setup_buffers(pvc, 0, 0, 0, 0, data.val);
1283 }
1284 /* return current speed */
1285 data.val = (int)pvc->sample_rate;
1286 break;
1287 case SOUND_PCM_READ_RATE:
1288 data.val = (int)pvc->sample_rate;
1289 break;
1290 case SNDCTL_DSP_STEREO:
1291 if (data.val != 0) {
1292 error = vclient_set_channels(pvc, 2);
1293 } else {
1294 error = vclient_set_channels(pvc, 1);
1295 }
1296 data.val = (pvc->channels == 2);
1297 break;
1298 case SOUND_PCM_WRITE_CHANNELS:
1299 if (data.val < 0) {
1300 data.val = 0;
1301 error = CUSE_ERR_INVALID;
1302 break;
1303 }
1304 if (data.val == 0) {
1305 data.val = pvc->channels;
1306 } else {
1307 error = vclient_set_channels(pvc, data.val);
1308 }
1309 break;
1310 case SOUND_PCM_READ_CHANNELS:
1311 data.val = pvc->channels;
1312 break;
1313 case AIOGFMT:
1314 case SNDCTL_DSP_GETFMTS:
1315 data.val = VSUPPORTED_AFMT | AFMT_FULLDUPLEX |
1316 (pvc->profile->channels > 1 ? AFMT_STEREO : 0);
1317 break;
1318 case AIOSFMT:
1319 case SNDCTL_DSP_SETFMT:
1320 if (data.val != AFMT_QUERY) {
1321 temp = data.val & VSUPPORTED_AFMT;
1322 if (temp == 0 || (temp & (temp - 1)) != 0) {
1323 error = CUSE_ERR_INVALID;
1324 } else {
1325 error = vclient_setup_buffers(pvc, 0, 0, 0, temp, 0);
1326 }
1327 } else {
1328 data.val = pvc->format;
1329 }
1330 break;
1331 case SNDCTL_DSP_GETISPACE:
1332 memset(&data.buf_info, 0, sizeof(data.buf_info));
1333 data.buf_info.fragsize = pvc->buffer_size;
1334 data.buf_info.fragstotal = pvc->buffer_frags;
1335 bytes = (pvc->buffer_size * pvc->buffer_frags);
1336 temp = vclient_input_delay(pvc);
1337 if (temp < 0 || (uint64_t)temp > bytes)
1338 temp = bytes;
1339 data.buf_info.fragments = temp / pvc->buffer_size;
1340 data.buf_info.bytes = temp;
1341 break;
1342 case SNDCTL_DSP_GETOSPACE:
1343 memset(&data.buf_info, 0, sizeof(data.buf_info));
1344 data.buf_info.fragsize = pvc->buffer_size;
1345 data.buf_info.fragstotal = pvc->buffer_frags;
1346 bytes = (pvc->buffer_size * pvc->buffer_frags);
1347 temp = vclient_output_delay(pvc);
1348 if (temp < 0 || (uint64_t)temp >= bytes) {
1349 /* buffer is full */
1350 data.buf_info.fragments = 0;
1351 data.buf_info.bytes = 0;
1352 } else {
1353 /* buffer is not full */
1354 bytes -= temp;
1355 data.buf_info.fragments = bytes / pvc->buffer_size;
1356 data.buf_info.bytes = bytes;
1357 }
1358 break;
1359 case SNDCTL_DSP_GETCAPS:
1360 data.val = PCM_CAP_REALTIME | PCM_CAP_DUPLEX |
1361 PCM_CAP_INPUT | PCM_CAP_OUTPUT | PCM_CAP_TRIGGER |
1362 PCM_CAP_VIRTUAL;
1363 break;
1364 case SOUND_PCM_READ_BITS:
1365 data.val = vclient_sample_bytes(pvc) * 8;
1366 break;
1367 case SNDCTL_DSP_SETTRIGGER:
1368 if (data.val & PCM_ENABLE_INPUT) {
1369 pvc->rx_enabled = 1;
1370 } else {
1371 pvc->rx_enabled = 0;
1372 vring_reset(&pvc->rx_ring[1]);
1373 }
1374
1375 if (data.val & PCM_ENABLE_OUTPUT) {
1376 pvc->tx_enabled = 1;
1377 } else {
1378 pvc->tx_enabled = 0;
1379 vring_reset(&pvc->tx_ring[1]);
1380 }
1381 break;
1382 case SNDCTL_DSP_GETTRIGGER:
1383 data.val = 0;
1384 if (pvc->rx_enabled)
1385 data.val |= PCM_ENABLE_INPUT;
1386 if (pvc->tx_enabled)
1387 data.val |= PCM_ENABLE_OUTPUT;
1388 break;
1389 case SNDCTL_DSP_GETODELAY:
1390 data.val = vclient_output_delay_adjusted(pvc);
1391 break;
1392 case SNDCTL_DSP_POST:
1393 break;
1394 case SNDCTL_DSP_SETDUPLEX:
1395 break;
1396 case SNDCTL_DSP_GETRECVOL:
1397 temp = (pvc->rx_volume * 100) >> VVOLUME_UNIT_SHIFT;
1398 data.val = (temp & 0x00FF) |
1399 ((temp << 8) & 0xFF00);
1400 break;
1401 case SNDCTL_DSP_SETRECVOL:
1402 pvc->rx_volume = ((data.val & 0xFF) << VVOLUME_UNIT_SHIFT) / 100;
1403 break;
1404 case SNDCTL_DSP_GETPLAYVOL:
1405 temp = (pvc->tx_volume * 100) >> VVOLUME_UNIT_SHIFT;
1406 data.val = (temp & 0x00FF) |
1407 ((temp << 8) & 0xFF00);
1408 break;
1409 case SNDCTL_DSP_SETPLAYVOL:
1410 pvc->tx_volume = ((data.val & 0xFF) << VVOLUME_UNIT_SHIFT) / 100;
1411 break;
1412 case SNDCTL_DSP_CURRENT_IPTR:
1413 memset(&data.oss_count, 0, sizeof(data.oss_count));
1414 /* compute input samples per channel */
1415 data.oss_count.samples =
1416 vclient_scale(pvc->rx_samples, pvc->sample_rate, voss_dsp_sample_rate);
1417 data.oss_count.samples /= pvc->channels;
1418 data.oss_count.fifo_samples =
1419 vclient_input_delay(pvc) / (pvc->channels * vclient_sample_bytes(pvc));
1420 break;
1421 case SNDCTL_DSP_CURRENT_OPTR:
1422 memset(&data.oss_count, 0, sizeof(data.oss_count));
1423 /* compute output samples per channel */
1424 data.oss_count.samples =
1425 vclient_scale(pvc->tx_samples, pvc->sample_rate, voss_dsp_sample_rate);
1426 data.oss_count.samples /= pvc->channels;
1427 data.oss_count.fifo_samples =
1428 vclient_output_delay(pvc) / (pvc->channels * vclient_sample_bytes(pvc));
1429 break;
1430 case SNDCTL_DSP_GETIPTR:
1431 memset(&data.oss_count_info, 0, sizeof(data.oss_count_info));
1432 /* compute input bytes */
1433 bytes =
1434 vclient_scale(pvc->rx_samples, pvc->sample_rate, voss_dsp_sample_rate) *
1435 vclient_sample_bytes(pvc);
1436 data.oss_count_info.bytes = bytes;
1437 data.oss_count_info.blocks = bytes / pvc->buffer_size;
1438 data.oss_count_info.ptr = bytes % (pvc->buffer_size * pvc->buffer_frags);
1439 break;
1440 case SNDCTL_DSP_GETOPTR:
1441 memset(&data.oss_count_info, 0, sizeof(data.oss_count_info));
1442 /* compute output bytes */
1443 bytes =
1444 vclient_scale(pvc->tx_samples, pvc->sample_rate, voss_dsp_sample_rate) *
1445 vclient_sample_bytes(pvc);
1446 data.oss_count_info.bytes = bytes;
1447 data.oss_count_info.blocks = bytes / pvc->buffer_size;
1448 data.oss_count_info.ptr = bytes % (pvc->buffer_size * pvc->buffer_frags);
1449 break;
1450 case SNDCTL_DSP_HALT_OUTPUT:
1451 pvc->tx_enabled = 0;
1452 break;
1453 case SNDCTL_DSP_HALT_INPUT:
1454 pvc->rx_enabled = 0;
1455 break;
1456 case SNDCTL_DSP_LOW_WATER:
1457 if (data.val > 0 && data.val <
1458 (int)(pvc->buffer_frags * pvc->buffer_size)) {
1459 pvc->low_water = data.val;
1460 } else {
1461 error = CUSE_ERR_INVALID;
1462 }
1463 break;
1464 case SNDCTL_DSP_GETERROR:
1465 memset(&data.errinfo, 0, sizeof(data.errinfo));
1466 break;
1467 case SNDCTL_DSP_SYNCGROUP:
1468 case SNDCTL_DSP_SYNCSTART:
1469 break;
1470 case SNDCTL_DSP_POLICY:
1471 break;
1472 case SNDCTL_DSP_COOKEDMODE:
1473 break;
1474 case SNDCTL_DSP_GET_CHNORDER:
1475 data.lval = CHNORDER_NORMAL;
1476 break;
1477 case SNDCTL_DSP_GETCHANNELMASK:
1478 data.val = DSP_BIND_FRONT;
1479 break;
1480 case SNDCTL_DSP_BIND_CHANNEL:
1481 break;
1482 case SNDCTL_GETLABEL:
1483 memset(&data.label, 0, sizeof(data.label));
1484 break;
1485 case SNDCTL_SETLABEL:
1486 break;
1487 case SNDCTL_GETSONG:
1488 memset(&data.longname, 0, sizeof(data.longname));
1489 break;
1490 case SNDCTL_SETSONG:
1491 break;
1492 case SNDCTL_SETNAME:
1493 break;
1494 default:
1495 error = CUSE_ERR_INVALID;
1496 break;
1497 }
1498 atomic_unlock();
1499
1500 if (error == 0) {
1501 if (cmd & IOC_OUT)
1502 error = cuse_copy_out(&data, peer_data, len);
1503 }
1504 return (error);
1505 }
1506
1507 static int
vclient_ioctl_wav(struct cuse_dev * pdev,int fflags __unused,unsigned long cmd,void * peer_data)1508 vclient_ioctl_wav(struct cuse_dev *pdev, int fflags __unused,
1509 unsigned long cmd, void *peer_data)
1510 {
1511 union {
1512 int val;
1513 } data;
1514
1515 vclient_t *pvc;
1516 int len;
1517 int error;
1518
1519 pvc = cuse_dev_get_per_file_handle(pdev);
1520 if (pvc == NULL)
1521 return (CUSE_ERR_INVALID);
1522
1523 len = IOCPARM_LEN(cmd);
1524
1525 if (len < 0 || len > (int)sizeof(data))
1526 return (CUSE_ERR_INVALID);
1527
1528 if (cmd & IOC_IN) {
1529 error = cuse_copy_in(peer_data, &data, len);
1530 if (error)
1531 return (error);
1532 } else {
1533 error = 0;
1534 }
1535
1536 atomic_lock();
1537 switch (cmd) {
1538 case FIONREAD:
1539 data.val = vclient_input_delay(pvc);
1540 break;
1541 case FIOASYNC:
1542 case SNDCTL_DSP_NONBLOCK:
1543 case FIONBIO:
1544 break;
1545 default:
1546 error = CUSE_ERR_INVALID;
1547 break;
1548 }
1549 atomic_unlock();
1550
1551 if (error == 0) {
1552 if (cmd & IOC_OUT)
1553 error = cuse_copy_out(&data, peer_data, len);
1554 }
1555 return (error);
1556 }
1557
1558 static int
vclient_poll(struct cuse_dev * pdev,int fflags,int events)1559 vclient_poll(struct cuse_dev *pdev, int fflags, int events)
1560 {
1561 vclient_t *pvc;
1562
1563 int retval = CUSE_POLL_NONE;
1564
1565 pvc = cuse_dev_get_per_file_handle(pdev);
1566 if (pvc == NULL)
1567 return (retval);
1568
1569 atomic_lock();
1570 if ((events & CUSE_POLL_READ) && (fflags & CUSE_FFLAG_READ)) {
1571 pvc->rx_enabled = 1;
1572 if (vclient_input_delay(pvc) >= pvc->low_water)
1573 retval |= CUSE_POLL_READ;
1574 }
1575 if ((events & CUSE_POLL_WRITE) && (fflags & CUSE_FFLAG_WRITE)) {
1576 const uint32_t out_dly = vclient_output_delay(pvc);
1577 const uint32_t out_buf = (pvc->buffer_frags * pvc->buffer_size);
1578
1579 if (out_dly < out_buf && (out_buf - out_dly) >= pvc->low_water)
1580 retval |= CUSE_POLL_WRITE;
1581 }
1582 atomic_unlock();
1583
1584 return (retval);
1585 }
1586
1587 static const struct cuse_methods vclient_oss_methods = {
1588 .cm_open = vclient_open_oss,
1589 .cm_close = vclient_close,
1590 .cm_read = vclient_read,
1591 .cm_write = vclient_write_oss,
1592 .cm_ioctl = vclient_ioctl_oss,
1593 .cm_poll = vclient_poll,
1594 };
1595
1596 static const struct cuse_methods vclient_wav_methods = {
1597 .cm_open = vclient_open_wav,
1598 .cm_close = vclient_close,
1599 .cm_read = vclient_read,
1600 .cm_write = vclient_write_wav,
1601 .cm_ioctl = vclient_ioctl_wav,
1602 .cm_poll = vclient_poll,
1603 };
1604
1605 vprofile_head_t virtual_profile_client_head;
1606 vprofile_head_t virtual_profile_loopback_head;
1607
1608 vmonitor_head_t virtual_monitor_input;
1609 vmonitor_head_t virtual_monitor_output;
1610 vmonitor_head_t virtual_monitor_local;
1611
1612 uint32_t voss_max_channels;
1613 uint32_t voss_mix_channels;
1614 uint32_t voss_dsp_samples;
1615 uint32_t voss_dsp_max_channels;
1616 uint32_t voss_dsp_sample_rate;
1617 uint32_t voss_dsp_bits;
1618 uint8_t voss_libsamplerate_enable;
1619 uint8_t voss_libsamplerate_quality = SRC_SINC_FASTEST;
1620 int voss_is_recording = 1;
1621 int voss_has_synchronization;
1622 volatile sig_atomic_t voss_exit = 0;
1623
1624 static int voss_do_background;
1625 static int voss_baseclone = 0;
1626 static const char *voss_pid_path;
1627
1628 uint32_t voss_dsp_rx_refresh;
1629 uint32_t voss_dsp_tx_refresh;
1630 char voss_dsp_rx_device[VMAX_STRING];
1631 char voss_dsp_tx_device[VMAX_STRING];
1632 char voss_ctl_device[VMAX_STRING];
1633
1634 uint32_t voss_jitter_up;
1635 uint32_t voss_jitter_down;
1636
1637 struct voss_backend *voss_rx_backend;
1638 struct voss_backend *voss_tx_backend;
1639
1640 static int voss_dups;
1641 static int voss_ntds;
1642 static pthread_t *voss_tds;
1643 static int voss_fd_sta = -1;
1644
1645 static gid_t voss_gid;
1646 static int voss_perm = 0660;
1647
1648 /* XXX I do not like the prefix argument... */
1649 static struct voss_backend *
voss_load_backend(const char * prefix,const char * name,const char * dir)1650 voss_load_backend(const char *prefix, const char *name, const char *dir)
1651 {
1652 struct voss_backend *backend;
1653 void *hdl;
1654 char lpath[64], bsym[64];
1655
1656 snprintf(lpath, sizeof(lpath), "%s/lib/virtual_oss/voss_%s.so",
1657 prefix, name);
1658 snprintf(bsym, sizeof(bsym), "voss_backend_%s_%s", name, dir);
1659
1660 if ((hdl = dlopen(lpath, RTLD_NOW)) == NULL)
1661 errx(1, "%s", dlerror());
1662 if ((backend = dlsym(hdl, bsym)) == NULL) {
1663 warnx("%s", dlerror());
1664 dlclose(hdl);
1665 exit(EXIT_FAILURE);
1666 }
1667
1668 return (backend);
1669 }
1670
1671 static void
voss_rx_backend_refresh(void)1672 voss_rx_backend_refresh(void)
1673 {
1674 /* setup RX backend */
1675 if (strcmp(voss_dsp_rx_device, "/dev/null") == 0) {
1676 voss_rx_backend = voss_load_backend("/usr", "null", "rec");
1677 } else if (strstr(voss_dsp_rx_device, "/dev/bluetooth/") == voss_dsp_rx_device) {
1678 voss_rx_backend = voss_load_backend("/usr/local", "bt", "rec");
1679 } else if (strstr(voss_dsp_rx_device, "/dev/sndio/") == voss_dsp_rx_device) {
1680 voss_rx_backend = voss_load_backend("/usr/local", "sndio", "rec");
1681 } else {
1682 voss_rx_backend = voss_load_backend("/usr", "oss", "rec");
1683 }
1684 }
1685
1686 static void
voss_tx_backend_refresh(void)1687 voss_tx_backend_refresh(void)
1688 {
1689 /* setup TX backend */
1690 if (strcmp(voss_dsp_tx_device, "/dev/null") == 0) {
1691 voss_tx_backend = voss_load_backend("/usr", "null", "play");
1692 } else if (strstr(voss_dsp_tx_device, "/dev/bluetooth/") == voss_dsp_tx_device) {
1693 voss_tx_backend = voss_load_backend("/usr/local", "bt", "play");
1694 } else if (strstr(voss_dsp_tx_device, "/dev/sndio/") == voss_dsp_tx_device) {
1695 voss_tx_backend = voss_load_backend("/usr/local", "sndio", "play");
1696 } else {
1697 voss_tx_backend = voss_load_backend("/usr", "oss", "play");
1698 }
1699 }
1700
1701 static void
usage(void)1702 usage(void)
1703 {
1704 fprintf(stderr, "Usage: virtual_oss [options...] [device] \\\n"
1705 "\t" "-C 2 -c 2 -r 48000 -b 16 -s 100.0ms -f /dev/dsp3 \\\n"
1706 "\t" "-P /dev/dsp3 -R /dev/dsp1 \\\n"
1707 "\t" "-O /dev/dsp3 -R /dev/null \\\n"
1708 "\t" "-c 1 -m 0,0 [-w wav.0] -d dsp100.0 \\\n"
1709 "\t" "-c 1 -m 0,0 [-w wav.0] -d vdsp.0 \\\n"
1710 "\t" "-c 2 -m 0,0,1,1 [-w wav.1] -d vdsp.1 \\\n"
1711 "\t" "-c 2 -m 0,0,1,1 [-w wav.loopback] -l vdsp.loopback \\\n"
1712 "\t" "-c 2 -m 0,0,1,1 [-w wav.loopback] -L vdsp.loopback \\\n"
1713 "\t" "-B # run in background \\\n"
1714 "\t" "-s <samples> or <milliseconds>ms \\\n"
1715 "\t" "-S # enable automatic resampling using libsamplerate \\\n"
1716 "\t" "-Q <0,1,2> # quality of resampling 0=best,1=medium,2=fastest (default) \\\n"
1717 "\t" "-b <bits> \\\n"
1718 "\t" "-r <rate> \\\n"
1719 "\t" "-i <rtprio> \\\n"
1720 "\t" "-a <amp -63..63> \\\n"
1721 "\t" "-a i,<rx_amp -63..63> \\\n"
1722 "\t" "-a o,<tx_amp -63..63> \\\n"
1723 "\t" "-g <knee,attack,decay> # enable device RX compressor\\\n"
1724 "\t" "-x <knee,attack,decay> # enable output compressor\\\n"
1725 "\t" "-p <pol 0..1> \\\n"
1726 "\t" "-e <rxtx_mute 0..1> \\\n"
1727 "\t" "-e <rx_mute 0..1>,<tx_mute 0..1> \\\n"
1728 "\t" "-m <mapping> \\\n"
1729 "\t" "-m <rx0,tx0,rx1,tx1...rxN,txN> \\\n"
1730 "\t" "-C <mixchans>\\\n"
1731 "\t" "-c <dspchans> \\\n"
1732 "\t" "-M <monitorfilter> \\\n"
1733 "\t" "-M i,<src>,<dst>,<pol>,<mute>,<amp> \\\n"
1734 "\t" "-M o,<src>,<dst>,<pol>,<mute>,<amp> \\\n"
1735 "\t" "-M x,<src>,<dst>,<pol>,<mute>,<amp> \\\n"
1736 "\t" "-F <rx_filter_samples> or <milliseconds>ms \\\n"
1737 "\t" "-G <tx_filter_samples> or <milliseconds>ms \\\n"
1738 "\t" "-E <enable_recording, 0 or 1> \\\n"
1739 "\t" "-N <max HTTP connections, default is 1> \\\n"
1740 "\t" "-H <bind HTTP server to this host> \\\n"
1741 "\t" "-o <bind HTTP server to this port, default is 80> \\\n"
1742 "\t" "-J <bind RTP server to this network interface> \\\n"
1743 "\t" "-k <bind RTP server to this port, default is 8080> \\\n"
1744 "\t" "-t vdsp.ctl \n"
1745 "\t" "Left channel = 0\n"
1746 "\t" "Right channel = 1\n"
1747 "\t" "Max channels = %d\n", VMAX_CHAN);
1748
1749 exit(EX_USAGE);
1750 }
1751
1752 /*
1753 * Restore hw.snd.basename_clone if it was disabled by us.
1754 */
1755 static void
restore_baseclone(void)1756 restore_baseclone(void)
1757 {
1758 if (voss_baseclone) {
1759 if (sysctlbyname(SYSCTL_BASECLONE, NULL, NULL, &voss_baseclone,
1760 sizeof(int)) < 0)
1761 warn("Could not enable " SYSCTL_BASECLONE);
1762 printf(SYSCTL_BASECLONE ": 0 -> %d\n", voss_baseclone);
1763 }
1764 }
1765
1766 static void
init_compressor(struct virtual_profile * pvp)1767 init_compressor(struct virtual_profile *pvp)
1768 {
1769 int x;
1770
1771 memset(&pvp->rx_compressor_param, 0, sizeof(pvp->rx_compressor_param));
1772
1773 pvp->rx_compressor_param.knee = 85;
1774 pvp->rx_compressor_param.attack = 3;
1775 pvp->rx_compressor_param.decay = 20;
1776
1777 for (x = 0; x != VMAX_CHAN; x++)
1778 pvp->rx_compressor_gain[x] = 1.0;
1779 }
1780
1781 static void
init_mapping(struct virtual_profile * pvp)1782 init_mapping(struct virtual_profile *pvp)
1783 {
1784 int x;
1785
1786 for (x = 0; x != VMAX_CHAN; x++) {
1787 pvp->rx_src[x] = x;
1788 pvp->tx_dst[x] = x;
1789 }
1790 }
1791
1792 static void
init_sndstat(vprofile_t * ptr)1793 init_sndstat(vprofile_t *ptr)
1794 {
1795 int err;
1796 nvlist_t *nvl;
1797 nvlist_t *di = NULL, *dichild = NULL;
1798 struct sndstioc_nv_arg arg;
1799 unsigned int min_rate, max_rate;
1800
1801 nvl = nvlist_create(0);
1802 if (nvl == NULL) {
1803 warn("Failed to create nvlist");
1804 goto done;
1805 }
1806
1807 di = nvlist_create(0);
1808 if (di == NULL) {
1809 warn("Failed to create nvlist");
1810 goto done;
1811 }
1812
1813 dichild = nvlist_create(0);
1814 if (dichild == NULL) {
1815 warn("Failed to create nvlist");
1816 goto done;
1817 }
1818
1819 nvlist_add_string(di, SNDST_DSPS_PROVIDER, "virtual_oss");
1820 nvlist_add_string(di, SNDST_DSPS_DESC, "virtual_oss device");
1821 nvlist_add_number(di, SNDST_DSPS_PCHAN, 1);
1822 nvlist_add_number(di, SNDST_DSPS_RCHAN, 1);
1823 min_rate = 8000;
1824 max_rate = voss_dsp_sample_rate;
1825 if (voss_libsamplerate_enable == 0 ||
1826 min_rate > max_rate)
1827 min_rate = max_rate;
1828 if (voss_libsamplerate_enable != 0 && max_rate < 96000)
1829 max_rate = 96000;
1830 nvlist_add_number(dichild, SNDST_DSPS_INFO_MIN_RATE, min_rate);
1831 nvlist_add_number(dichild, SNDST_DSPS_INFO_MAX_RATE, max_rate);
1832 nvlist_add_number(dichild, SNDST_DSPS_INFO_FORMATS, VSUPPORTED_AFMT);
1833 nvlist_add_number(dichild, SNDST_DSPS_INFO_MIN_CHN, ptr->channels);
1834 nvlist_add_number(dichild, SNDST_DSPS_INFO_MAX_CHN, ptr->channels);
1835 nvlist_add_nvlist(di, SNDST_DSPS_INFO_PLAY, dichild);
1836 nvlist_add_nvlist(di, SNDST_DSPS_INFO_REC, dichild);
1837
1838 nvlist_add_string(di, SNDST_DSPS_DEVNODE,
1839 ptr->oss_name);
1840 nvlist_append_nvlist_array(nvl, SNDST_DSPS, di);
1841
1842 if (nvlist_error(nvl)) {
1843 warn("Failed building nvlist");
1844 goto done;
1845 }
1846
1847 arg.buf = nvlist_pack(nvl, &arg.nbytes);
1848 if (arg.buf == NULL) {
1849 warn("Failed to pack nvlist");
1850 goto done;
1851 }
1852 err = ioctl(voss_fd_sta, SNDSTIOC_ADD_USER_DEVS, &arg);
1853 free(arg.buf);
1854 if (err != 0) {
1855 warn("Failed to issue ioctl(SNDSTIOC_ADD_USER_DEVS)");
1856 goto done;
1857 }
1858
1859 done:
1860 nvlist_destroy(di);
1861 nvlist_destroy(dichild);
1862 nvlist_destroy(nvl);
1863 }
1864
1865 static void
cleanup_profile(vprofile_t * pvp)1866 cleanup_profile(vprofile_t *pvp)
1867 {
1868 if (pvp->oss_dev != NULL)
1869 cuse_dev_destroy(pvp->oss_dev);
1870 if (pvp->wav_dev != NULL)
1871 cuse_dev_destroy(pvp->wav_dev);
1872 }
1873
1874 static const char *
dup_profile(vprofile_t * pvp,int * pamp,int pol,int rx_mute,int tx_mute,int synchronized,int is_client)1875 dup_profile(vprofile_t *pvp, int *pamp, int pol, int rx_mute,
1876 int tx_mute, int synchronized, int is_client)
1877 {
1878 vprofile_t *ptr;
1879 struct cuse_dev *pdev;
1880 const char *errstr;
1881 int x;
1882
1883 rx_mute = rx_mute ? 1 : 0;
1884 tx_mute = tx_mute ? 1 : 0;
1885 pol = pol ? 1 : 0;
1886
1887 /* Range check amplitude argument. */
1888 for (x = 0; x != 2; x++) {
1889 if (pamp[x] < -63)
1890 pamp[x] = -63;
1891 else if (pamp[x] > 63)
1892 pamp[x] = 63;
1893 }
1894
1895 ptr = malloc(sizeof(*ptr));
1896 if (ptr == NULL)
1897 return ("Out of memory");
1898
1899 memcpy(ptr, pvp, sizeof(*ptr));
1900
1901 ptr->synchronized = synchronized;
1902 TAILQ_INIT(&ptr->head);
1903
1904 for (x = 0; x != ptr->channels; x++) {
1905 ptr->tx_mute[x] = tx_mute;
1906 ptr->rx_mute[x] = rx_mute;
1907 ptr->tx_shift[x] = pamp[1];
1908 ptr->rx_shift[x] = pamp[0];
1909 ptr->tx_pol[x] = pol;
1910 ptr->rx_pol[x] = pol;
1911 }
1912
1913 /* create DSP device */
1914 if (ptr->oss_name[0] != 0) {
1915 /*
1916 * Detect /dev/dsp creation and try to disable system
1917 * basename cloning automatically:
1918 */
1919 if (strcmp(ptr->oss_name, "dsp") == 0) {
1920 size_t size;
1921
1922 x = 0;
1923 size = sizeof(int);
1924 if (sysctlbyname(SYSCTL_BASECLONE, &voss_baseclone,
1925 &size, &x, size) < 0)
1926 return ("Could not disable " SYSCTL_BASECLONE);
1927 printf(SYSCTL_BASECLONE ": %d -> 0\n", voss_baseclone);
1928 if (atexit(restore_baseclone) < 0)
1929 return ("Could not set atexit callback");
1930 }
1931
1932 /* create DSP character device */
1933 pdev = cuse_dev_create(&vclient_oss_methods, ptr, NULL,
1934 0, voss_gid, voss_perm, ptr->oss_name);
1935 if (pdev == NULL) {
1936 errstr = "Could not create CUSE DSP device";
1937 goto err;
1938 }
1939 ptr->oss_dev = pdev;
1940
1941 /* register to sndstat */
1942 if (voss_fd_sta < 0) {
1943 if ((voss_fd_sta = open("/dev/sndstat", O_WRONLY)) < 0) {
1944 errstr = "Could not open /dev/sndstat";
1945 goto err;
1946 }
1947 }
1948 init_sndstat(ptr);
1949 }
1950 /* create WAV device */
1951 if (ptr->wav_name[0] != 0) {
1952 pdev = cuse_dev_create(&vclient_wav_methods, ptr, NULL,
1953 0, voss_gid, voss_perm, ptr->wav_name);
1954 if (pdev == NULL) {
1955 errstr = "Could not create CUSE WAV device";
1956 goto err;
1957 }
1958 ptr->wav_dev = pdev;
1959 }
1960
1961 atomic_lock();
1962 if (is_client)
1963 TAILQ_INSERT_TAIL(&virtual_profile_client_head, ptr, entry);
1964 else
1965 TAILQ_INSERT_TAIL(&virtual_profile_loopback_head, ptr, entry);
1966 atomic_unlock();
1967
1968 voss_dups++;
1969
1970 /* need new names next time */
1971 memset(pvp->oss_name, 0, sizeof(pvp->oss_name));
1972 memset(pvp->wav_name, 0, sizeof(pvp->wav_name));
1973
1974 /* need to set new filter sizes */
1975 pvp->rx_filter_size = 0;
1976 pvp->tx_filter_size = 0;
1977
1978 /* need to specify new HTTP parameters next time */
1979 pvp->http.host = NULL;
1980 pvp->http.port = NULL;
1981 pvp->http.nstate = 0;
1982 pvp->http.rtp_ifname = NULL;
1983 pvp->http.rtp_port = NULL;
1984
1985 /* need to specify new amplification next time */
1986 pamp[0] = 0;
1987 pamp[1] = 0;
1988
1989 /* need to set new compressor parameters next time */
1990 init_compressor(pvp);
1991
1992 return (voss_httpd_start(ptr));
1993
1994 err:
1995 cleanup_profile(ptr);
1996 free(ptr);
1997
1998 return (errstr);
1999
2000 }
2001
2002 static void
virtual_pipe(int sig __unused)2003 virtual_pipe(int sig __unused)
2004 {
2005 voss_dsp_tx_refresh = 1;
2006 voss_dsp_rx_refresh = 1;
2007 }
2008
2009 static void
virtual_cuse_hup(int sig __unused)2010 virtual_cuse_hup(int sig __unused)
2011 {
2012 atomic_wakeup();
2013 }
2014
2015 static void *
virtual_cuse_process(void * arg __unused)2016 virtual_cuse_process(void *arg __unused)
2017 {
2018 signal(SIGHUP, &virtual_cuse_hup);
2019
2020 while (1) {
2021 if (cuse_wait_and_process() != 0)
2022 break;
2023 }
2024 return (NULL);
2025 }
2026
2027 static void
virtual_cuse_init_profile(struct virtual_profile * pvp)2028 virtual_cuse_init_profile(struct virtual_profile *pvp)
2029 {
2030 memset(pvp, 0, sizeof(*pvp));
2031
2032 init_compressor(pvp);
2033 init_mapping(pvp);
2034 }
2035
2036 static void
virtual_sig_exit(int sig __unused)2037 virtual_sig_exit(int sig __unused)
2038 {
2039 voss_exit = 1;
2040 }
2041
2042 static const char *
parse_options(int narg,char ** pparg,int is_main)2043 parse_options(int narg, char **pparg, int is_main)
2044 {
2045 const char *ptr;
2046 int a, b, c;
2047 int val;
2048 int idx;
2049 int type;
2050 int opt_mute[2] = {0, 0};
2051 int opt_amp[2] = {0, 0};
2052 int opt_pol = 0;
2053 const char *optstr;
2054 struct virtual_profile profile;
2055 struct rtprio rtp;
2056 float samples_ms;
2057
2058 if (is_main)
2059 optstr = "N:J:k:H:o:F:G:w:e:p:a:C:c:r:b:f:g:x:i:m:M:d:l:L:s:t:h?O:P:Q:R:SBD:E:";
2060 else
2061 optstr = "F:G:w:e:p:a:c:b:f:m:M:d:l:L:s:O:P:R:E:";
2062
2063 virtual_cuse_init_profile(&profile);
2064
2065 /* reset getopt parsing */
2066 optreset = 1;
2067 optind = 1;
2068
2069 while ((c = getopt(narg, pparg, optstr)) != -1) {
2070 switch (c) {
2071 case 'B':
2072 voss_do_background = 1;
2073 break;
2074 case 'D':
2075 voss_pid_path = optarg;
2076 break;
2077 case 'C':
2078 if (voss_mix_channels != 0) {
2079 return ("The -C argument may only be used once");
2080 }
2081 voss_mix_channels = atoi(optarg);
2082 if (voss_mix_channels >= VMAX_CHAN) {
2083 return ("Number of mixing channels is too high");
2084 }
2085 break;
2086 case 'a':
2087 switch (optarg[0]) {
2088 case '-':
2089 case '0':
2090 case '1':
2091 case '2':
2092 case '3':
2093 case '4':
2094 case '5':
2095 case '6':
2096 case '7':
2097 case '8':
2098 case '9':
2099 opt_amp[0] = -(opt_amp[1] = atoi(optarg));
2100 break;
2101 case 'i':
2102 if (optarg[1] != ',')
2103 return ("Expected comma after 'i'");
2104 opt_amp[0] = atoi(optarg + 2);
2105 break;
2106 case 'o':
2107 if (optarg[1] != ',')
2108 return ("Expected comma after 'o'");
2109 opt_amp[1] = atoi(optarg + 2);
2110 break;
2111 default:
2112 return ("Invalid syntax for amplitude argument");
2113 }
2114 break;
2115 case 'E':
2116 voss_is_recording = (atoi(optarg) != 0);
2117 break;
2118 case 'e':
2119 idx = 0;
2120 ptr = optarg;
2121 memset(opt_mute, 0, sizeof(opt_mute));
2122 while (1) {
2123 c = *ptr++;
2124 if (c == ',' || c == 0) {
2125 idx++;
2126 if (c == 0)
2127 break;
2128 continue;
2129 }
2130 if (idx < 2 && c >= '0' && c <= '1') {
2131 opt_mute[idx] = c - '0';
2132 } else {
2133 return ("Invalid -e parameter");
2134 }
2135 }
2136 switch (idx) {
2137 case 1:
2138 opt_mute[1] = opt_mute[0];
2139 break;
2140 case 2:
2141 break;
2142 default:
2143 return ("Invalid -e parameter");
2144 }
2145 break;
2146 case 'p':
2147 opt_pol = atoi(optarg);
2148 break;
2149 case 'c':
2150 profile.channels = atoi(optarg);
2151 if (profile.channels == 0)
2152 return ("Number of channels is zero");
2153 if (profile.channels > VMAX_CHAN)
2154 return ("Number of channels is too high");
2155 break;
2156 case 'r':
2157 voss_dsp_sample_rate = atoi(optarg);
2158 if (voss_dsp_sample_rate < 8000)
2159 return ("Sample rate is too low, 8000 Hz");
2160 if (voss_dsp_sample_rate > 0xFFFFFF)
2161 return ("Sample rate is too high");
2162 break;
2163 case 'i':
2164 memset(&rtp, 0, sizeof(rtp));
2165 rtp.type = RTP_PRIO_REALTIME;
2166 rtp.prio = atoi(optarg);
2167 if (rtprio(RTP_SET, getpid(), &rtp) != 0)
2168 printf("Cannot set realtime priority\n");
2169 break;
2170 case 'b':
2171 profile.bits = atoi(optarg);
2172 switch (profile.bits) {
2173 case 8:
2174 case 16:
2175 case 24:
2176 case 32:
2177 break;
2178 default:
2179 return ("Invalid number of sample bits");
2180 }
2181 break;
2182 case 'g':
2183 if (profile.rx_compressor_param.enabled)
2184 return ("Compressor already enabled for this device");
2185 if (sscanf(optarg, "%d,%d,%d", &a, &b, &c) != 3 ||
2186 a < VIRTUAL_OSS_KNEE_MIN ||
2187 a > VIRTUAL_OSS_KNEE_MAX ||
2188 b < VIRTUAL_OSS_ATTACK_MIN ||
2189 b > VIRTUAL_OSS_ATTACK_MAX ||
2190 c < VIRTUAL_OSS_DECAY_MIN ||
2191 c > VIRTUAL_OSS_DECAY_MAX)
2192 return ("Invalid device compressor argument(s)");
2193 profile.rx_compressor_param.enabled = 1;
2194 profile.rx_compressor_param.knee = a;
2195 profile.rx_compressor_param.attack = b;
2196 profile.rx_compressor_param.decay = c;
2197 break;
2198 case 'x':
2199 if (voss_output_compressor_param.enabled)
2200 return ("Compressor already enabled for output");
2201 if (sscanf(optarg, "%d,%d,%d", &a, &b, &c) != 3 ||
2202 a < VIRTUAL_OSS_KNEE_MIN ||
2203 a > VIRTUAL_OSS_KNEE_MAX ||
2204 b < VIRTUAL_OSS_ATTACK_MIN ||
2205 b > VIRTUAL_OSS_ATTACK_MAX ||
2206 c < VIRTUAL_OSS_DECAY_MIN ||
2207 c > VIRTUAL_OSS_DECAY_MAX)
2208 return ("Invalid output compressor argument(s)");
2209 voss_output_compressor_param.enabled = 1;
2210 voss_output_compressor_param.knee = a;
2211 voss_output_compressor_param.attack = b;
2212 voss_output_compressor_param.decay = c;
2213 break;
2214 case 'f':
2215 case 'O':
2216 case 'P':
2217 case 'R':
2218 if (voss_dsp_sample_rate == 0 || voss_dsp_samples == 0)
2219 return ("Missing -r or -s parameters");
2220 if (voss_dsp_bits == 0) {
2221 if (profile.bits == 0)
2222 return ("Missing -b parameter");
2223 voss_dsp_bits = profile.bits;
2224 }
2225 if (voss_dsp_max_channels == 0) {
2226 if (profile.channels == 0)
2227 return ("Missing -c parameter");
2228 voss_dsp_max_channels = profile.channels;
2229 }
2230 if (c == 'f' || c == 'R') {
2231 if (strlen(optarg) > VMAX_STRING - 1)
2232 return ("Device name too long");
2233 strncpy(voss_dsp_rx_device, optarg, sizeof(voss_dsp_rx_device));
2234 voss_rx_backend_refresh();
2235 voss_dsp_rx_refresh = 1;
2236 }
2237 if (c == 'f' || c == 'P' || c == 'O') {
2238 if (strlen(optarg) > VMAX_STRING - 1)
2239 return ("Device name too long");
2240 strncpy(voss_dsp_tx_device, optarg, sizeof(voss_dsp_tx_device));
2241 voss_tx_backend_refresh();
2242 voss_dsp_tx_refresh = 1;
2243
2244 if (c == 'O' && voss_has_synchronization == 0)
2245 voss_has_synchronization++;
2246 }
2247 break;
2248 case 'w':
2249 if (strlen(optarg) > VMAX_STRING - 1)
2250 return ("Device name too long");
2251 strncpy(profile.wav_name, optarg, sizeof(profile.wav_name));
2252 break;
2253 case 'd':
2254 case 'L':
2255 case 'l':
2256 if (strlen(optarg) > VMAX_STRING - 1)
2257 return ("Device name too long");
2258 strncpy(profile.oss_name, optarg, sizeof(profile.oss_name));
2259
2260 if (profile.bits == 0 || voss_dsp_sample_rate == 0 ||
2261 profile.channels == 0 || voss_dsp_samples == 0)
2262 return ("Missing -b, -r, -c or -s parameters");
2263
2264 val = (voss_dsp_samples *
2265 profile.bits * profile.channels) / 8;
2266 if (val <= 0 || val >= (1024 * 1024))
2267 return ("-s option value is too big");
2268
2269 ptr = dup_profile(&profile, opt_amp, opt_pol,
2270 opt_mute[0], opt_mute[1], c == 'L', c == 'd');
2271 if (ptr != NULL)
2272 return (ptr);
2273 break;
2274 case 'S':
2275 voss_libsamplerate_enable = 1;
2276 break;
2277 case 'Q':
2278 c = atoi(optarg);
2279 switch (c) {
2280 case 0:
2281 voss_libsamplerate_quality = SRC_SINC_BEST_QUALITY;
2282 break;
2283 case 1:
2284 voss_libsamplerate_quality = SRC_SINC_MEDIUM_QUALITY;
2285 break;
2286 default:
2287 voss_libsamplerate_quality = SRC_SINC_FASTEST;
2288 break;
2289 }
2290 break;
2291 case 's':
2292 if (voss_dsp_samples != 0)
2293 return ("-s option may only be used once");
2294 if (profile.bits == 0 || profile.channels == 0)
2295 return ("-s option requires -b and -c options");
2296 if (strlen(optarg) > 2 &&
2297 sscanf(optarg, "%f", &samples_ms) == 1 &&
2298 strcmp(optarg + strlen(optarg) - 2, "ms") == 0) {
2299 if (voss_dsp_sample_rate == 0)
2300 return ("-s <X>ms option requires -r option");
2301 if (samples_ms < 0.125 || samples_ms >= 1000.0)
2302 return ("-s <X>ms option has invalid value");
2303 voss_dsp_samples = voss_dsp_sample_rate * samples_ms / 1000.0;
2304 } else {
2305 voss_dsp_samples = atoi(optarg);
2306 }
2307 if (voss_dsp_samples >= (1U << 24))
2308 return ("-s option requires a non-zero positive value");
2309 break;
2310 case 't':
2311 if (voss_ctl_device[0])
2312 return ("-t parameter may only be used once");
2313
2314 strlcpy(voss_ctl_device, optarg, sizeof(voss_ctl_device));
2315 break;
2316 case 'm':
2317 ptr = optarg;
2318 val = 0;
2319 idx = 0;
2320 init_mapping(&profile);
2321 while (1) {
2322 c = *ptr++;
2323 if (c == ',' || c == 0) {
2324 if (idx >= (2 * VMAX_CHAN))
2325 return ("Too many channels in mask");
2326 if (idx & 1)
2327 profile.tx_dst[idx / 2] = val;
2328 else
2329 profile.rx_src[idx / 2] = val;
2330 if (c == 0)
2331 break;
2332 val = 0;
2333 idx++;
2334 continue;
2335 }
2336 if (c >= '0' && c <= '9') {
2337 val *= 10;
2338 val += c - '0';
2339 }
2340 }
2341 break;
2342 case 'M':
2343 ptr = optarg;
2344 type = *ptr;
2345 if (type == 'i' || type == 'o' || type == 'x') {
2346 vmonitor_t *pvm;
2347
2348 int src = 0;
2349 int dst = 0;
2350 int pol = 0;
2351 int mute = 0;
2352 int amp = 0;
2353 int neg;
2354
2355 ptr++;
2356 if (*ptr == ',')
2357 ptr++;
2358 else if (type == 'i')
2359 return ("Expected comma after 'i'");
2360 else if (type == 'o')
2361 return ("Expected comma after 'o'");
2362 else
2363 return ("Expected comma after 'x'");
2364
2365 val = 0;
2366 neg = 0;
2367 idx = 0;
2368 while (1) {
2369 c = *ptr++;
2370 if (c == '-') {
2371 neg = 1;
2372 continue;
2373 }
2374 if (c == ',' || c == 0) {
2375 switch (idx) {
2376 case 0:
2377 src = val;
2378 break;
2379 case 1:
2380 dst = val;
2381 break;
2382 case 2:
2383 pol = val ? 1 : 0;
2384 break;
2385 case 3:
2386 mute = val ? 1 : 0;
2387 break;
2388 case 4:
2389 if (val > 31) {
2390 return ("Absolute amplitude "
2391 "for -M parameter "
2392 "cannot exceed 31");
2393 }
2394 amp = neg ? -val : val;
2395 break;
2396 default:
2397 break;
2398 }
2399 if (c == 0)
2400 break;
2401 val = 0;
2402 neg = 0;
2403 idx++;
2404 continue;
2405 }
2406 if (c >= '0' && c <= '9') {
2407 val *= 10;
2408 val += c - '0';
2409 }
2410 }
2411 if (idx < 4)
2412 return ("Too few parameters for -M");
2413
2414 pvm = vmonitor_alloc(&idx,
2415 (type == 'i') ? &virtual_monitor_input :
2416 (type == 'x') ? &virtual_monitor_local :
2417 &virtual_monitor_output);
2418
2419 if (pvm == NULL)
2420 return ("Out of memory");
2421
2422 pvm->src_chan = src;
2423 pvm->dst_chan = dst;
2424 pvm->pol = pol;
2425 pvm->mute = mute;
2426 pvm->shift = amp;
2427 } else {
2428 return ("Invalid -M parameter");
2429 }
2430 break;
2431 case 'F':
2432 if (strlen(optarg) > 2 &&
2433 sscanf(optarg, "%f", &samples_ms) == 1 &&
2434 strcmp(optarg + strlen(optarg) - 2, "ms") == 0) {
2435 if (voss_dsp_sample_rate == 0)
2436 return ("-F <X>ms option requires -r option");
2437 if (samples_ms < 0.125 || samples_ms >= 1000.0)
2438 return ("-F <X>ms option has invalid value");
2439 profile.rx_filter_size = voss_dsp_sample_rate * samples_ms / 1000.0;
2440 } else {
2441 profile.rx_filter_size = atoi(optarg);
2442 }
2443 /* make value power of two */
2444 while ((profile.rx_filter_size - 1) & profile.rx_filter_size)
2445 profile.rx_filter_size += ~(profile.rx_filter_size - 1) & profile.rx_filter_size;
2446 /* range check */
2447 if (profile.rx_filter_size > VIRTUAL_OSS_FILTER_MAX)
2448 return ("Invalid -F parameter is out of range");
2449 break;
2450 case 'G':
2451 if (strlen(optarg) > 2 &&
2452 sscanf(optarg, "%f", &samples_ms) == 1 &&
2453 strcmp(optarg + strlen(optarg) - 2, "ms") == 0) {
2454 if (voss_dsp_sample_rate == 0)
2455 return ("-G <X>ms option requires -r option");
2456 if (samples_ms < 0.125 || samples_ms >= 1000.0)
2457 return ("-G <X>ms option has invalid value");
2458 profile.tx_filter_size = voss_dsp_sample_rate * samples_ms / 1000.0;
2459 } else {
2460 profile.tx_filter_size = atoi(optarg);
2461 }
2462 /* make value power of two */
2463 while ((profile.tx_filter_size - 1) & profile.tx_filter_size)
2464 profile.tx_filter_size += ~(profile.tx_filter_size - 1) & profile.tx_filter_size;
2465 /* range check */
2466 if (profile.tx_filter_size > VIRTUAL_OSS_FILTER_MAX)
2467 return ("Invalid -F parameter is out of range");
2468 break;
2469 case 'N':
2470 profile.http.nstate = atoi(optarg);
2471 break;
2472 case 'H':
2473 profile.http.host = optarg;
2474 if (profile.http.port == NULL)
2475 profile.http.port = "80";
2476 if (profile.http.nstate == 0)
2477 profile.http.nstate = 1;
2478 break;
2479 case 'o':
2480 profile.http.port = optarg;
2481 break;
2482 case 'J':
2483 profile.http.rtp_ifname = optarg;
2484 if (profile.http.rtp_port == NULL)
2485 profile.http.rtp_port = "8080";
2486 break;
2487 case 'k':
2488 profile.http.rtp_port = optarg;
2489 break;
2490 default:
2491 if (is_main)
2492 usage();
2493 else
2494 return ("Invalid option detected");
2495 break;
2496 }
2497 }
2498 return (NULL);
2499 }
2500
2501 static void
create_threads(void)2502 create_threads(void)
2503 {
2504 int idx;
2505
2506 /* Give each DSP device 4 threads */
2507 voss_ntds = voss_dups * 4;
2508 voss_tds = malloc(voss_ntds * sizeof(pthread_t));
2509 if (voss_tds == NULL)
2510 err(1, "malloc");
2511
2512 for (idx = 0; idx < voss_ntds; idx++) {
2513 if (pthread_create(&voss_tds[idx], NULL, &virtual_cuse_process,
2514 NULL) != 0)
2515 err(1, "pthread_create");
2516 }
2517
2518 /* Reset until next time called */
2519 voss_dups = 0;
2520 }
2521
2522 static void
destroy_threads(void)2523 destroy_threads(void)
2524 {
2525 int idx;
2526
2527 for (idx = 0; idx < voss_ntds; idx++)
2528 pthread_cancel(voss_tds[idx]);
2529 free(voss_tds);
2530 }
2531
2532 void
voss_add_options(char * str)2533 voss_add_options(char *str)
2534 {
2535 static char name[] = { "virtual_oss" };
2536 const char sep[] = "\t ";
2537 const char *ptrerr;
2538 char *parg[64];
2539 char *word;
2540 char *brkt;
2541 int narg = 0;
2542
2543 parg[narg++] = name;
2544
2545 for (word = strtok_r(str, sep, &brkt); word != NULL;
2546 word = strtok_r(NULL, sep, &brkt)) {
2547 if (narg >= 64) {
2548 ptrerr = "Too many arguments";
2549 goto done;
2550 }
2551 parg[narg++] = word;
2552 }
2553 ptrerr = parse_options(narg, parg, 0);
2554 done:
2555 if (ptrerr != NULL) {
2556 strlcpy(str, ptrerr, VIRTUAL_OSS_OPTIONS_MAX);
2557 } else {
2558 str[0] = 0;
2559 create_threads();
2560 }
2561 }
2562
2563 int
main(int argc,char ** argv)2564 main(int argc, char **argv)
2565 {
2566 const char *ptrerr;
2567 struct sigaction sa;
2568 struct cuse_dev *pdev = NULL;
2569 struct virtual_profile *pvp;
2570 struct group *gr;
2571
2572 /* Devices can be used only by users who part of the audio group. */
2573 if ((gr = getgrnam("audio")) == NULL)
2574 err(EX_USAGE, "getgrnam");
2575 voss_gid = gr->gr_gid;
2576
2577 TAILQ_INIT(&virtual_profile_client_head);
2578 TAILQ_INIT(&virtual_profile_loopback_head);
2579
2580 TAILQ_INIT(&virtual_monitor_input);
2581 TAILQ_INIT(&virtual_monitor_output);
2582 TAILQ_INIT(&virtual_monitor_local);
2583
2584 atomic_init();
2585
2586 if (kldload("cuse.ko") < 0 && errno != EEXIST)
2587 err(1, "Failed to load cuse kernel module");
2588
2589 if (cuse_init() != 0)
2590 errx(EX_USAGE, "Could not connect to cuse module");
2591
2592 signal(SIGPIPE, &virtual_pipe);
2593
2594 memset(&sa, 0, sizeof(sa));
2595 sigfillset(&sa.sa_mask);
2596 sa.sa_handler = virtual_sig_exit;
2597 if (sigaction(SIGINT, &sa, NULL) < 0)
2598 err(1, "sigaction(SIGINT)");
2599 if (sigaction(SIGTERM, &sa, NULL) < 0)
2600 err(1, "sigaction(SIGTERM)");
2601
2602 ptrerr = parse_options(argc, argv, 1);
2603 if (ptrerr != NULL)
2604 errx(EX_USAGE, "%s", ptrerr);
2605
2606 if (voss_dsp_rx_device[0] == 0 || voss_dsp_tx_device[0] == 0)
2607 errx(EX_USAGE, "Missing -f argument");
2608
2609 /* use DSP channels as default */
2610 if (voss_mix_channels == 0)
2611 voss_mix_channels = voss_dsp_max_channels;
2612
2613 if (voss_mix_channels > voss_dsp_max_channels)
2614 voss_max_channels = voss_mix_channels;
2615 else
2616 voss_max_channels = voss_dsp_max_channels;
2617
2618 if (voss_dsp_samples > (voss_dsp_sample_rate / 4))
2619 errx(EX_USAGE, "Too many buffer samples given by -s argument");
2620
2621 /* check if daemon mode is requested */
2622 if (voss_do_background != 0 && daemon(0, 0) != 0)
2623 errx(EX_SOFTWARE, "Cannot become daemon");
2624
2625 if (voss_pid_path != NULL) {
2626 int pidfile = open(voss_pid_path, O_RDWR | O_CREAT | O_TRUNC, 0600);
2627 pid_t mypid = getpid();
2628 char mypidstr[8];
2629 snprintf(mypidstr, sizeof(mypidstr), "%d\n", mypid);
2630 if (pidfile < 0)
2631 errx(EX_SOFTWARE, "Cannot create PID file '%s'", voss_pid_path);
2632 if (write(pidfile, mypidstr, strlen(mypidstr)) !=
2633 (ssize_t)strlen(mypidstr))
2634 errx(EX_SOFTWARE, "Cannot write PID file");
2635 close(pidfile);
2636 }
2637
2638 /* setup audio delay unit */
2639 voss_ad_init(voss_dsp_sample_rate);
2640
2641 /* Create CTL device */
2642
2643 if (voss_ctl_device[0] != 0) {
2644 pdev = cuse_dev_create(&vctl_methods, NULL, NULL,
2645 0, voss_gid, voss_perm, voss_ctl_device);
2646 if (pdev == NULL)
2647 errx(EX_USAGE, "Could not create '/dev/%s'", voss_ctl_device);
2648
2649 voss_dups++;
2650 }
2651
2652 /* Create worker threads */
2653 create_threads();
2654
2655 /* Run DSP threads */
2656
2657 virtual_oss_process(NULL);
2658
2659 destroy_threads();
2660
2661 /* Destroy CUSE devices */
2662
2663 if (voss_ctl_device[0] != 0)
2664 cuse_dev_destroy(pdev);
2665
2666 TAILQ_FOREACH(pvp, &virtual_profile_client_head, entry) {
2667 cleanup_profile(pvp);
2668 }
2669 TAILQ_FOREACH(pvp, &virtual_profile_loopback_head, entry) {
2670 cleanup_profile(pvp);
2671 }
2672
2673 cuse_uninit();
2674 close(voss_fd_sta);
2675
2676 return (0);
2677 }
2678