1 /*
2 * cdda - CD Digital Audio support
3 *
4 * Copyright (C) 1993-2004 Ti Kan
5 * E-mail: xmcd@amb.org
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 /*
23 * Sun Solaris support
24 *
25 * This software fragment contains code that interfaces the
26 * application to the Solaris operating environment. The name "Sun"
27 * and "Solaris" are used here for identification purposes only.
28 *
29 * Contributing author: Darragh O'Brien <darragh.obrien@sun.com>
30 */
31 #ifndef lint
32 static char *_wr_sol_c_ident_ = "@(#)wr_sol.c 7.166 04/03/24";
33 #endif
34
35 #include "common_d/appenv.h"
36 #include "common_d/util.h"
37 #include "libdi_d/libdi.h"
38 #include "cdda_d/cdda.h"
39 #include "cdda_d/common.h"
40
41 #if defined(CDDA_WR_SOL) && defined(CDDA_SUPPORTED)
42
43 #include <stropts.h>
44 #include <sys/stream.h>
45 #include <sys/audioio.h>
46 #include "cdda_d/wr_gen.h"
47 #include "cdda_d/wr_sol.h"
48
49 extern appdata_t app_data;
50 extern FILE *errfp;
51
52 STATIC int sol_wsemid, /* Semaphores identifier */
53 sol_outport = 0; /* Output port */
54 STATIC pid_t sol_pipe_pid = -1; /* Pipe to program pid */
55 STATIC cd_state_t *sol_wcd; /* Shared memory pointer */
56 STATIC bool_t sol_write_dev, /* Write to audio device */
57 sol_write_file, /* Write to output file */
58 sol_write_pipe, /* Pipe to program */
59 sol_file_be = FALSE; /* Big endian output file */
60 STATIC gen_desc_t *sol_audio_desc = NULL, /* Audio device desc */
61 *sol_file_desc = NULL, /* Output file desc */
62 *sol_pipe_desc = NULL; /* Pipe to program desc */
63 STATIC unsigned int sol_file_mode, /* Output file mode */
64 sol_curr_vol, /* Current volume */
65 sol_curr_bal; /* Current balance */
66
67
68 /*
69 * solaud_scale_vol
70 * Scale volume from xmcd 0-100 to Solaris audio device 0-SOL_MAX_VOL
71 *
72 * Args:
73 * vol - xmcd volume 0-100
74 *
75 * Return:
76 * Solaris volume 0-SOL_MAX_VOL
77 */
78 STATIC unsigned int
solaud_scale_vol(int vol)79 solaud_scale_vol(int vol)
80 {
81 unsigned int ret;
82
83 ret = (vol * SOL_MAX_VOL) / 100;
84 if (((vol * SOL_MAX_VOL) % 100) >= 50)
85 ret++;
86
87 /* Range checking */
88 if (ret > SOL_MAX_VOL)
89 ret = SOL_MAX_VOL;
90
91 return (ret);
92 }
93
94
95 /*
96 * solaud_unscale_vol
97 * Scale volume from Solaris audio device 0-SOL_MAX_VOL to xmcd 0-100
98 *
99 * Args:
100 * vol - Solaris volume 0-SOL_MAX_VOL
101 *
102 * Return:
103 * xmcd volume 0-100
104 */
105 STATIC int
solaud_unscale_vol(unsigned int vol)106 solaud_unscale_vol(unsigned int vol)
107 {
108 int ret;
109
110 ret = (vol * 100) / SOL_MAX_VOL;
111 if (((vol * 100) % SOL_MAX_VOL) >= SOL_HALF_VOL)
112 ret++;
113
114 /* Range checking */
115 if (ret < 0)
116 ret = 0;
117 else if (ret > 100)
118 ret = 100;
119
120 return (ret);
121 }
122
123
124 /*
125 * solaud_scale_bal
126 * Convert xmcd individual channel volume percentage values to
127 * Solaris audio balance value.
128 *
129 * Args:
130 * vol_left - left volume percentage
131 * vol_right - right volume percentage
132 *
133 * Return:
134 * Solaris balance SOL_L_BAL-SOL_R_BAL
135 */
136 STATIC uchar_t
solaud_scale_bal(int vol_left,int vol_right)137 solaud_scale_bal(int vol_left, int vol_right)
138 {
139 uchar_t ret = SOL_M_BAL;
140
141 if (vol_left == 100 && vol_right == 100) {
142 ret = SOL_M_BAL;
143 }
144 else if (vol_right < 100) {
145 ret = (vol_right * SOL_M_BAL) / 100;
146 if (((vol_right * SOL_M_BAL) % 100) >= 50)
147 ret++;
148 }
149 else if (vol_left < 100) {
150 ret = ((vol_left * SOL_M_BAL) / 100) +
151 SOL_M_BAL;
152 if (((vol_left * SOL_M_BAL) % 100) >= 50)
153 ret++;
154 }
155
156 /* Range checking */
157 if (ret > SOL_R_BAL)
158 ret = SOL_R_BAL;
159
160 return (ret);
161 }
162
163
164 /*
165 * solaud_unscale_bal
166 * Convert Solaris audio balance value to xmcd individual channel
167 * volume percentage values.
168 *
169 * Args:
170 * bal - Solaris audio balance value
171 * vol_left - left volume percentage (return)
172 * vol_right - right volume percentage (return)
173 *
174 * Return:
175 * Nothing.
176 */
177 STATIC void
solaud_unscale_bal(uchar_t bal,int * vol_left,int * vol_right)178 solaud_unscale_bal(uchar_t bal, int *vol_left, int *vol_right)
179 {
180 if (bal > SOL_R_BAL)
181 bal = SOL_R_BAL;
182
183 if (bal == SOL_M_BAL) {
184 *vol_left = 100;
185 *vol_right = 100;
186 }
187 else if (bal < SOL_M_BAL) {
188 *vol_left = 100;
189 *vol_right = (bal * 100) / SOL_M_BAL;
190 if (((bal * 100) % SOL_M_BAL) >= SOL_HALF_VOL)
191 (*vol_right)++;
192 }
193 else if (bal > SOL_M_BAL) {
194 bal -= SOL_M_BAL;
195 *vol_left = (bal * 100) / SOL_M_BAL;
196 *vol_right = 100;
197 if (((bal * 100) % SOL_M_BAL) >= SOL_HALF_VOL)
198 (*vol_left)++;
199 }
200
201 /* Range checking */
202 if (*vol_left < 0)
203 *vol_left = 0;
204 else if (*vol_left > 100)
205 *vol_left = 100;
206
207 if (*vol_right < 0)
208 *vol_right = 0;
209 else if (*vol_right > 100)
210 *vol_right = 100;
211 }
212
213
214 /*
215 * solaud_get_info
216 * Get audio hardware settings.
217 *
218 * Args:
219 * info - Info to return
220 *
221 * Return:
222 * FALSE - failure
223 * TRUE - success
224 */
225 STATIC bool_t
solaud_get_info(audio_info_t * info)226 solaud_get_info(audio_info_t *info)
227 {
228 /* Retrieve settings */
229 if (!gen_ioctl(sol_audio_desc, AUDIO_GETINFO, info)) {
230 (void) sprintf(sol_wcd->i->msgbuf,
231 "solaud_get_info: AUDIO_GETINFO failed: %s",
232 strerror(errno));
233 DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
234 return FALSE;
235 }
236
237 return TRUE;
238 }
239
240
241 /*
242 * solaud_set_info
243 * Set audio hardware settings.
244 *
245 * Args:
246 * info - Info to set
247 *
248 * Return:
249 * FALSE - failure
250 * TRUE - success
251 */
252 STATIC bool_t
solaud_set_info(audio_info_t * info)253 solaud_set_info(audio_info_t *info)
254 {
255 /* Apply settings */
256 if (!gen_ioctl(sol_audio_desc, AUDIO_SETINFO, info)) {
257 (void) sprintf(sol_wcd->i->msgbuf,
258 "solaud_set_info: AUDIO_SETINFO failed: %s",
259 strerror(errno));
260 return FALSE;
261 }
262
263 return TRUE;
264 }
265
266
267 /*
268 * solaud_flush
269 * Wrapper for STREAMS I_FLUSH ioctl.
270 *
271 * Args:
272 * gdp - Pointer to the gen_desc_t structure
273 *
274 * Return:
275 * FALSE - failure
276 * TRUE - success
277 */
278 STATIC bool_t
solaud_flush(gen_desc_t * gdp)279 solaud_flush(gen_desc_t *gdp)
280 {
281 /* Apply settings */
282 if (!gen_ioctl(gdp, I_FLUSH, (void *) FLUSHRW)) {
283 (void) sprintf(sol_wcd->i->msgbuf,
284 "solaud_flush: I_FLUSH failed: %s",
285 strerror(errno));
286 DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
287 return FALSE;
288 }
289
290 return TRUE;
291 }
292
293
294 /*
295 * solaud_drain
296 * Wrapper for AUDIO_DRAIN ioctl.
297 *
298 * Args:
299 * gdp - Pointer to the gen_desc_t structure
300 *
301 * Return:
302 * FALSE - failure
303 * TRUE - success
304 */
305 STATIC bool_t
solaud_drain(gen_desc_t * gdp)306 solaud_drain(gen_desc_t *gdp)
307 {
308 /* Apply settings */
309 if (!gen_ioctl(gdp, AUDIO_DRAIN, NULL)) {
310 (void) sprintf(sol_wcd->i->msgbuf,
311 "solaud_drain: AUDIO_DRAIN failed: %s",
312 strerror(errno));
313 DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
314 return FALSE;
315 }
316
317 return TRUE;
318 }
319
320
321 /*
322 * solaud_open_dev
323 * Opens the audio device.
324 *
325 * Args:
326 * None.
327 *
328 * Return:
329 * FALSE - failure
330 * TRUE - success
331 */
332 STATIC bool_t
solaud_open_dev(void)333 solaud_open_dev(void)
334 {
335 char *cp;
336 struct stat stbuf;
337
338 if (!gen_set_eid(sol_wcd))
339 return FALSE;
340
341 /* Use environment variable if set */
342 if ((cp = getenv("AUDIODEV")) == NULL)
343 cp = DEFAULT_DEV_AUDIO;
344
345 /* Check to make sure it's a device */
346 if (stat(cp, &stbuf) < 0) {
347 (void) sprintf(sol_wcd->i->msgbuf,
348 "solaud_open_dev: stat of %s failed: %s",
349 cp, strerror(errno));
350 DBGPRN(DBG_GEN)(errfp, "%s\n", sol_wcd->i->msgbuf);
351 (void) gen_reset_eid(sol_wcd);
352 return FALSE;
353 }
354 if (!S_ISCHR(stbuf.st_mode)) {
355 (void) sprintf(sol_wcd->i->msgbuf,
356 "solaud_open_dev: %s is not a "
357 "character device",
358 cp);
359 DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
360 (void) gen_reset_eid(sol_wcd);
361 return FALSE;
362 }
363
364 /* Open audio device */
365 if ((sol_audio_desc = gen_open_file(sol_wcd, cp, O_WRONLY, 0,
366 FILEFMT_RAW, 0)) == NULL) {
367 (void) sprintf(sol_wcd->i->msgbuf,
368 "solaud_open_dev: open of %s failed: %s",
369 cp, strerror(errno));
370 DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
371 (void) gen_reset_eid(sol_wcd);
372 return FALSE;
373 }
374
375 (void) gen_reset_eid(sol_wcd);
376 return TRUE;
377 }
378
379
380 /*
381 * solaud_close_dev
382 * Closes the audio device.
383 *
384 * Args:
385 * None.
386 *
387 * Return:
388 * FALSE - failure
389 * TRUE - success
390 */
391 STATIC bool_t
solaud_close_dev(void)392 solaud_close_dev(void)
393 {
394 if (!gen_set_eid(sol_wcd))
395 return FALSE;
396
397 /* Close audio device */
398 if (!gen_close_file(sol_audio_desc)) {
399 DBGPRN(DBG_SND)(errfp,
400 "solaud_close_dev: close failed: %s\n",
401 strerror(errno));
402 (void) gen_reset_eid(sol_wcd);
403 return FALSE;
404 }
405
406 sol_audio_desc = NULL;
407
408 (void) gen_reset_eid(sol_wcd);
409 return TRUE;
410 }
411
412
413 /*
414 * solaud_config
415 * Sets up audio device for playing CD quality audio.
416 *
417 * Args:
418 * None.
419 *
420 * Return:
421 * FALSE - failure
422 * TRUE - success
423 */
424 STATIC bool_t
solaud_config(void)425 solaud_config(void)
426 {
427 audio_info_t solaud_info;
428
429 /* Initialize */
430 SOL_AUDIO_INIT(&solaud_info, sizeof(audio_info_t));
431
432 solaud_info.play.sample_rate = 44100;
433 solaud_info.play.channels = 2;
434 solaud_info.play.precision = 16;
435 solaud_info.play.encoding = AUDIO_ENCODING_LINEAR;
436
437 /* Select audio output port */
438 if (sol_wcd->i->outport != 0 && sol_wcd->i->outport != sol_outport) {
439 solaud_info.play.port = 0;
440 if ((sol_wcd->i->outport & CDDA_OUT_SPEAKER) != 0)
441 solaud_info.play.port |= AUDIO_SPEAKER;
442 if ((sol_wcd->i->outport & CDDA_OUT_HEADPHONE) != 0)
443 solaud_info.play.port |= AUDIO_HEADPHONE;
444 if ((sol_wcd->i->outport & CDDA_OUT_LINEOUT) != 0)
445 solaud_info.play.port |= AUDIO_LINE_OUT;
446
447 sol_outport = sol_wcd->i->outport;
448 }
449
450 /* Apply new settings */
451 if (!solaud_set_info(&solaud_info))
452 return FALSE;
453
454 /* Retrieve current settings */
455 if (!solaud_get_info(&solaud_info))
456 return FALSE;
457
458 sol_curr_vol = sol_curr_bal = -1;
459
460 /* Update volume */
461 sol_wcd->i->vol = util_untaper_vol(
462 solaud_unscale_vol(solaud_info.play.gain)
463 );
464
465 /* Update balance */
466 solaud_unscale_bal(solaud_info.play.balance,
467 &sol_wcd->i->vol_left, &sol_wcd->i->vol_right);
468
469 return TRUE;
470 }
471
472
473 /*
474 * solaud_write_eof
475 * Puts an eof marker in the audio stream.
476 *
477 * Args:
478 * gdp - Pointer to the gen_desc_t structure
479 *
480 * Return:
481 * FALSE - failure
482 * TRUE - success
483 */
484 STATIC bool_t
solaud_write_eof(gen_desc_t * gdp)485 solaud_write_eof(gen_desc_t *gdp)
486 {
487 if (!gen_set_eid(sol_wcd))
488 return FALSE;
489
490 if (!gen_write_chunk(gdp, NULL, 0)) {
491 (void) sprintf(sol_wcd->i->msgbuf,
492 "solaud_write_eof: write failed: %s",
493 strerror(errno));
494 DBGPRN(DBG_SND)(errfp, "%s\n", sol_wcd->i->msgbuf);
495
496 (void) gen_reset_eid(sol_wcd);
497 return FALSE;
498 }
499
500 (void) gen_reset_eid(sol_wcd);
501 return TRUE;
502 }
503
504
505 /*
506 * solaud_set_paused
507 * Update Solaris audio driver paused state based on current status.
508 *
509 * Args:
510 * state - The current paused state
511 *
512 * Return:
513 * The current paused state
514 */
515 STATIC int
solaud_paused(int state)516 solaud_paused(int state)
517 {
518 audio_info_t solaud_info;
519 static int prev_state = CDSTAT_NOSTATUS;
520
521 if (prev_state == state)
522 return (state); /* No change */
523
524 if (sol_write_dev) {
525 if (!solaud_get_info(&solaud_info))
526 return (state); /* Can't get info, just return */
527
528 /* Update paused */
529 solaud_info.play.pause = (state == CDSTAT_PAUSED) ? 1 : 0;
530
531 /* Apply new settings */
532 (void) solaud_set_info(&solaud_info);
533 }
534
535 prev_state = state;
536 return (state);
537 }
538
539
540 /*
541 * solaud_update
542 * Updates hardware volume and balance settings from GUI.
543 *
544 * Args:
545 * None.
546 *
547 * Return:
548 * Nothing.
549 */
550 STATIC void
solaud_update(void)551 solaud_update(void)
552 {
553 audio_info_t solaud_info;
554 int vol,
555 bal;
556 static int curr_vol = -1,
557 curr_bal = -1;
558
559 app_data.vol_taper = sol_wcd->i->vol_taper;
560
561 vol = solaud_scale_vol(util_taper_vol(sol_wcd->i->vol));
562 bal = solaud_scale_bal(sol_wcd->i->vol_left, sol_wcd->i->vol_right);
563
564 if (vol == curr_vol && bal == curr_bal &&
565 (sol_wcd->i->outport == 0 || sol_wcd->i->outport == sol_outport))
566 /* No change */
567 return;
568
569 /* Retrieve current settings */
570 if (!solaud_get_info(&solaud_info))
571 return;
572
573 /* Select audio output port */
574 if (sol_wcd->i->outport != 0 && sol_wcd->i->outport != sol_outport) {
575 solaud_info.play.port = 0;
576 if ((sol_wcd->i->outport & CDDA_OUT_SPEAKER) != 0)
577 solaud_info.play.port |= AUDIO_SPEAKER;
578 if ((sol_wcd->i->outport & CDDA_OUT_HEADPHONE) != 0)
579 solaud_info.play.port |= AUDIO_HEADPHONE;
580 if ((sol_wcd->i->outport & CDDA_OUT_LINEOUT) != 0)
581 solaud_info.play.port |= AUDIO_LINE_OUT;
582
583 sol_outport = sol_wcd->i->outport;
584 }
585
586 curr_vol = vol;
587 curr_bal = bal;
588
589 /* Update volume and balance */
590 solaud_info.play.gain = (unsigned int) vol;
591 solaud_info.play.balance = (unsigned int) bal;
592
593 /* Apply new settings */
594 (void) solaud_set_info(&solaud_info);
595 }
596
597
598 /*
599 * solaud_status
600 * Updates volume and balance settings from audio device.
601 *
602 * Args:
603 * None.
604 *
605 * Return:
606 * Nothing.
607 */
608 STATIC void
solaud_status(void)609 solaud_status(void)
610 {
611 audio_info_t solaud_info;
612
613 /* Retrieve current settings */
614 if (!solaud_get_info(&solaud_info))
615 return;
616
617 if (solaud_info.play.gain == sol_curr_vol &&
618 solaud_info.play.balance == sol_curr_bal)
619 /* No change */
620 return;
621
622 sol_curr_vol = solaud_info.play.gain;
623 sol_curr_bal = solaud_info.play.balance;
624
625 /* Update volume and balance */
626 sol_wcd->i->vol = util_untaper_vol(solaud_unscale_vol(sol_curr_vol));
627 solaud_unscale_bal(sol_curr_bal,
628 &sol_wcd->i->vol_left, &sol_wcd->i->vol_right);
629 }
630
631
632 /*
633 * solaud_write
634 * Function responsible for writing from the buffer to the audio
635 * device (and file if saving). Each chunk is followed by an eof
636 * marker.
637 *
638 * Args:
639 * s - Pointer to the curstat_t structure
640 *
641 * Return:
642 * FALSE - failure
643 * TRUE - success
644 */
645 STATIC bool_t
solaud_write(curstat_t * s)646 solaud_write(curstat_t *s)
647 {
648 byte_t *in_data,
649 *sw_data,
650 *le_data,
651 *be_data,
652 *dev_data,
653 *file_data,
654 *pipe_data;
655 char *fname = NULL;
656 int trk_idx = -1,
657 hbcnt = 1;
658 time_t start_time,
659 tot_time;
660 bool_t ret;
661
662 /* Get memory for audio device write buffer */
663 le_data = (byte_t *) MEM_ALLOC("le_data", sol_wcd->cds->chunk_bytes);
664 if (le_data == NULL) {
665 (void) sprintf(sol_wcd->i->msgbuf,
666 "solaud_write: out of memory");
667 DBGPRN(DBG_GEN)(errfp, "%s\n", sol_wcd->i->msgbuf);
668 return FALSE;
669 }
670
671 /* Get memory for audio file write buffer */
672 be_data = (byte_t *) MEM_ALLOC("be_data", sol_wcd->cds->chunk_bytes);
673 if (be_data == NULL) {
674 MEM_FREE(le_data);
675 (void) sprintf(sol_wcd->i->msgbuf,
676 "solaud_write: out of memory");
677 DBGPRN(DBG_GEN)(errfp, "%s\n", sol_wcd->i->msgbuf);
678 return FALSE;
679 }
680
681 (void) memset(le_data, 0, sol_wcd->cds->chunk_bytes);
682 (void) memset(be_data, 0, sol_wcd->cds->chunk_bytes);
683
684 in_data = app_data.cdda_bigendian ? be_data : le_data;
685 sw_data = app_data.cdda_bigendian ? le_data : be_data;
686
687 /* Start time */
688 start_time = time(NULL);
689
690 /* While not stopped */
691 ret = TRUE;
692 while (sol_wcd->i->state != CDSTAT_COMPLETED) {
693 /* Get lock */
694 cdda_waitsem(sol_wsemid, LOCK);
695
696 /* End if finished and nothing in the buffer */
697 if (sol_wcd->cdb->occupied == 0 && sol_wcd->i->cdda_done) {
698 if (sol_write_dev && !solaud_drain(sol_audio_desc)) {
699 ret = FALSE;
700 break;
701 }
702 sol_wcd->i->state = CDSTAT_COMPLETED;
703 cdda_postsem(sol_wsemid, LOCK);
704 break;
705 }
706
707 /* Wait until there is something there */
708 while (sol_wcd->cdb->occupied <= 0 &&
709 sol_wcd->i->state != CDSTAT_COMPLETED) {
710 cdda_postsem(sol_wsemid, LOCK);
711 cdda_waitsem(sol_wsemid, DATA);
712 cdda_waitsem(sol_wsemid, LOCK);
713 }
714
715 /* Break if stopped */
716 if (sol_wcd->i->state == CDSTAT_COMPLETED) {
717 cdda_postsem(sol_wsemid, LOCK);
718 break;
719 }
720
721 /* Set the current track and track length info */
722 cdda_wr_setcurtrk(sol_wcd);
723
724 /* Copy a chunk from the nextout slot into the data buffers */
725 (void) memcpy(
726 in_data,
727 &sol_wcd->cdb->b[
728 sol_wcd->cds->chunk_bytes * sol_wcd->cdb->nextout
729 ],
730 sol_wcd->cds->chunk_bytes
731 );
732
733 /* Update pointers */
734 sol_wcd->cdb->nextout++;
735 sol_wcd->cdb->nextout %= sol_wcd->cds->buffer_chunks;
736 sol_wcd->cdb->occupied--;
737
738 /* Set heartbeat and interval, update stats */
739 if (--hbcnt == 0) {
740 cdda_heartbeat(&sol_wcd->i->writer_hb, CDDA_HB_WRITER);
741
742 tot_time = time(NULL) - start_time;
743 if (tot_time > 0 && sol_wcd->i->frm_played > 0) {
744 sol_wcd->i->frm_per_sec = (int)
745 ((float) sol_wcd->i->frm_played /
746 (float) tot_time) + 0.5;
747 }
748
749 hbcnt = cdda_heartbeat_interval(
750 sol_wcd->i->frm_per_sec
751 );
752 }
753
754 /* Signal room available */
755 cdda_postsem(sol_wsemid, ROOM);
756
757 /* Release lock */
758 cdda_postsem(sol_wsemid, LOCK);
759
760 /* Change channel routing and attenuation if necessary */
761 gen_chroute_att(sol_wcd->i->chroute,
762 sol_wcd->i->att,
763 (sword16_t *)(void *) in_data,
764 (size_t) sol_wcd->cds->chunk_bytes);
765
766 /* Prepare byte-swapped version of the data */
767 gen_byteswap(in_data, sw_data,
768 (size_t) sol_wcd->cds->chunk_bytes);
769
770 /* Write to device */
771 if (sol_write_dev) {
772 /* Feed native endian data to the audio driver */
773 #if _BYTE_ORDER_ == _B_ENDIAN_
774 dev_data = be_data;
775 #else
776 dev_data = le_data;
777 #endif
778
779 if (!gen_write_chunk(sol_audio_desc, dev_data,
780 (size_t) sol_wcd->cds->chunk_bytes)) {
781 sol_wcd->i->state = CDSTAT_COMPLETED;
782 cdda_postsem(sol_wsemid, LOCK);
783 ret = FALSE;
784 break;
785 }
786
787 if (!solaud_write_eof(sol_audio_desc)) {
788 sol_wcd->i->state = CDSTAT_COMPLETED;
789 cdda_postsem(sol_wsemid, LOCK);
790 ret = FALSE;
791 break;
792 }
793
794 /* Update volume and balance settings */
795 solaud_update();
796
797 if ((sol_wcd->i->frm_played % FRAME_PER_SEC) == 0)
798 solaud_status();
799 }
800
801 /* Write to file */
802 if (sol_write_file) {
803 file_data = sol_file_be ? be_data : le_data;
804
805 /* Open new track file if necessary */
806 if (!app_data.cdda_trkfile) {
807 fname = s->trkinfo[0].outfile;
808 }
809 else if (trk_idx != sol_wcd->i->trk_idx) {
810 trk_idx = sol_wcd->i->trk_idx;
811
812 if (sol_file_desc != NULL &&
813 !gen_close_file(sol_file_desc)) {
814 sol_wcd->i->state = CDSTAT_COMPLETED;
815 cdda_postsem(sol_wsemid, LOCK);
816 ret = FALSE;
817 break;
818 }
819
820 fname = s->trkinfo[trk_idx].outfile;
821 sol_file_desc = gen_open_file(
822 sol_wcd,
823 fname,
824 O_WRONLY | O_TRUNC | O_CREAT,
825 (mode_t) sol_file_mode,
826 app_data.cdda_filefmt,
827 sol_wcd->i->trk_len
828 );
829 if (sol_file_desc == NULL) {
830 sol_wcd->i->state = CDSTAT_COMPLETED;
831 cdda_postsem(sol_wsemid, LOCK);
832 ret = FALSE;
833 break;
834 }
835 }
836
837 if (!gen_write_chunk(sol_file_desc, file_data,
838 (size_t) sol_wcd->cds->chunk_bytes)) {
839 sol_wcd->i->state = CDSTAT_COMPLETED;
840 cdda_postsem(sol_wsemid, LOCK);
841 ret = FALSE;
842 break;
843 }
844 }
845
846 /* Pipe to program */
847 if (sol_write_pipe) {
848 pipe_data = sol_file_be ? be_data : le_data;
849
850 if (!gen_write_chunk(sol_pipe_desc, pipe_data,
851 (size_t) sol_wcd->cds->chunk_bytes)) {
852 sol_wcd->i->state = CDSTAT_COMPLETED;
853 cdda_postsem(sol_wsemid, LOCK);
854 ret = FALSE;
855 break;
856 }
857 }
858
859 sol_wcd->i->frm_played += sol_wcd->cds->chunk_blocks;
860
861 while (solaud_paused(sol_wcd->i->state) == CDSTAT_PAUSED)
862 util_delayms(400);
863
864 /* Update debug level */
865 app_data.debug = sol_wcd->i->debug;
866 }
867
868 /* Wake up reader */
869 cdda_postsem(sol_wsemid, ROOM);
870
871 /* Free memory */
872 MEM_FREE(le_data);
873 MEM_FREE(be_data);
874
875 return (ret);
876 }
877
878
879 /*
880 * sol_winit
881 * Pre-playback support check function
882 *
883 * Args:
884 * None.
885 *
886 * Return:
887 * Bitmask of supported features
888 */
889 word32_t
sol_winit(void)890 sol_winit(void)
891 {
892 #if defined(i386) || defined(__i386__)
893 return (CDDA_WRITEDEV | CDDA_WRITEFILE | CDDA_WRITEPIPE);
894 #else
895 int arch = ((gethostid() >> 24) & ARCH_MASK);
896
897 switch (arch) {
898 case ARCH_SUN2:
899 case ARCH_SUN3:
900 case ARCH_SUN4C:
901 case ARCH_SUN4E:
902 DBGPRN(DBG_SND)(errfp, "sol_winit: %s\n%s\n",
903 "CD-quality stereo playback is not supported on",
904 "this machine architecture.");
905 return (CDDA_WRITEFILE | CDDA_WRITEPIPE);
906
907 case ARCH_SUN4M:
908 case ARCH_SUNX:
909 default:
910 return (CDDA_WRITEDEV | CDDA_WRITEFILE | CDDA_WRITEPIPE);
911 }
912 #endif
913 }
914
915
916 /*
917 * sol_write
918 * Opens audio device/file, attaches shared memory and semaphores.
919 * Continuously reads data from shared memory and writes it
920 * to the audio device/file.
921 *
922 * Args:
923 * s - Pointer to the curstat_t structure
924 *
925 * Return:
926 * FALSE - failure
927 * TRUE - success
928 */
929 bool_t
sol_write(curstat_t * s)930 sol_write(curstat_t *s)
931 {
932 size_t estlen;
933
934 if (!PLAYMODE_IS_CDDA(app_data.play_mode)) {
935 (void) fprintf(errfp, "sol_write: Nothing to do.\n");
936 return FALSE;
937 }
938
939 sol_write_dev = (bool_t) ((app_data.play_mode & PLAYMODE_CDDA) != 0);
940 sol_write_file = (bool_t) ((app_data.play_mode & PLAYMODE_FILE) != 0);
941 sol_write_pipe = (bool_t) ((app_data.play_mode & PLAYMODE_PIPE) != 0);
942
943 /* Generic services initialization */
944 gen_write_init();
945
946 /* Allocate memory */
947 sol_wcd = (cd_state_t *) MEM_ALLOC("cd_state_t", sizeof(cd_state_t));
948 if (sol_wcd == NULL) {
949 (void) fprintf(errfp, "sol_write: out of memory\n");
950 return FALSE;
951 }
952 (void) memset(sol_wcd, 0, sizeof(cd_state_t));
953
954 /* Initialize shared memory and semaphores */
955 if ((sol_wsemid = cdda_initipc(sol_wcd)) < 0)
956 return FALSE;
957
958 (void) sscanf(app_data.cdinfo_filemode, "%o", &sol_file_mode);
959 /* Make sure file is at least accessible by user */
960 sol_file_mode |= S_IRUSR | S_IWUSR;
961 sol_file_mode &= ~(S_ISUID | S_ISGID | S_IWGRP | S_IWOTH);
962
963 estlen = (sol_wcd->i->end_lba - sol_wcd->i->start_lba + 1) *
964 CDDA_BLKSZ;
965
966 /* Set heartbeat */
967 cdda_heartbeat(&sol_wcd->i->writer_hb, CDDA_HB_WRITER);
968
969 if (sol_write_file || sol_write_pipe) {
970 /* Open file and/or pipe */
971 if (!app_data.cdda_trkfile && sol_write_file) {
972 sol_file_desc = gen_open_file(
973 sol_wcd,
974 s->trkinfo[0].outfile,
975 O_WRONLY | O_TRUNC | O_CREAT,
976 (mode_t) sol_file_mode,
977 app_data.cdda_filefmt,
978 estlen
979 );
980 if (sol_file_desc == NULL)
981 return FALSE;
982 }
983
984 if (sol_write_pipe) {
985 sol_pipe_desc = gen_open_pipe(
986 sol_wcd,
987 app_data.pipeprog,
988 &sol_pipe_pid,
989 app_data.cdda_filefmt,
990 estlen
991 );
992 if (sol_pipe_desc == NULL)
993 return FALSE;
994
995 DBGPRN(DBG_SND)(errfp,
996 "\nsolaud_write: Pipe to [%s]: "
997 "chunk_blks=%d\n",
998 app_data.pipeprog,
999 sol_wcd->cds->chunk_blocks);
1000 }
1001
1002 /* Set endian */
1003 sol_file_be = gen_filefmt_be(app_data.cdda_filefmt);
1004 }
1005
1006 if (sol_write_dev) {
1007 /* Open audio device */
1008 if (!solaud_open_dev())
1009 return FALSE;
1010
1011 /* Configure audio */
1012 if (!solaud_config())
1013 return FALSE;
1014
1015 /* Initial settings */
1016 solaud_update();
1017 }
1018
1019 /* Do the writing */
1020 if (!solaud_write(s))
1021 return FALSE;
1022
1023 /* Flush audio */
1024 if (sol_write_dev && !solaud_flush(sol_audio_desc))
1025 return FALSE;
1026
1027 return TRUE;
1028 }
1029
1030
1031 /*
1032 * sol_wdone
1033 * Post-playback cleanup function
1034 *
1035 * Args:
1036 * killreader - whether to terminate the reader thread or process
1037 *
1038 * Return:
1039 * Nothing.
1040 */
1041 void
sol_wdone(bool_t killreader)1042 sol_wdone(bool_t killreader)
1043 {
1044 DBGPRN(DBG_GEN)(errfp, "\nsol_wdone: Cleaning up writer\n");
1045
1046 if (sol_audio_desc != NULL)
1047 (void) solaud_close_dev();
1048
1049 if (sol_file_desc != NULL) {
1050 (void) gen_close_file(sol_file_desc);
1051 sol_file_desc = NULL;
1052 }
1053
1054 if (sol_pipe_desc != NULL) {
1055 (void) gen_close_pipe(sol_pipe_desc);
1056 sol_pipe_desc = NULL;
1057 }
1058
1059 cdda_yield();
1060
1061 if (sol_pipe_pid > 0) {
1062 (void) kill(sol_pipe_pid, SIGTERM);
1063 sol_pipe_pid = -1;
1064 }
1065
1066 if (sol_wcd != NULL) {
1067 if (sol_wcd->i != NULL) {
1068 if (killreader && sol_wcd->i->reader != (thid_t) 0) {
1069 cdda_kill(sol_wcd->i->reader, SIGTERM);
1070 sol_wcd->i->state = CDSTAT_COMPLETED;
1071 }
1072
1073 /* Reset frames played counter */
1074 sol_wcd->i->frm_played = 0;
1075 }
1076
1077 MEM_FREE(sol_wcd);
1078 sol_wcd = NULL;
1079 }
1080
1081 sol_write_dev = sol_write_file = sol_write_pipe = FALSE;
1082 }
1083
1084
1085 /*
1086 * sol_winfo
1087 * Append method identification to informational string.
1088 *
1089 * Args:
1090 * str - The informational string
1091 *
1092 * Return:
1093 * Nothing.
1094 */
1095 void
sol_winfo(char * str)1096 sol_winfo(char *str)
1097 {
1098 (void) strcat(str, "Solaris audio driver\n");
1099 }
1100
1101 #endif /* CDDA_WR_SOL CDDA_SUPPORTED */
1102
1103