1 /* Extended Module Player
2  * Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
3  *
4  * This file is part of the Extended Module Player and is distributed
5  * under the terms of the GNU General Public License. See the COPYING
6  * file for more information.
7  */
8 
9 /* CS4231 code tested on Sparc 20 and Ultra 1 running Solaris 2.5.1
10  * with mono/stereo, 16 bit, 22.05 kHz and 44.1 kHz using the internal
11  * speaker and headphones.
12  *
13  * AMD 7930 code tested on Axil 240 running Solaris 2.5.1 and an Axil
14  * 220 running Linux 2.0.33.
15  */
16 
17 /* Fixed and improved by Keith Hargrove <Keith.Hargrove@Eng.Sun.COM>
18  * Wed, 30 Jun 1999 14:23:52 -0700 (PDT)
19  */
20 
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 
29 #if defined(HAVE_SYS_AUDIOIO_H)
30 #include <sys/audioio.h>
31 #elif defined(HAVE_SYS_AUDIO_IO_H)
32 #include <sys/audio.io.h>
33 #elif defined(HAVE_SUN_AUDIOIO_H)
34 #include <sun/audioio.h>
35 #endif
36 #include <sys/stropts.h>
37 
38 /* This is for Linux on Sparc */
39 #if defined(HAVE_SBUS_AUDIO_AUDIO_H)
40 #include <sbus/audio/audio.h>
41 #endif
42 
43 #include "sound.h"
44 
45 static int audio_fd;
46 static int audioctl_fd;
47 
init(struct options * options)48 static int init(struct options *options)
49 {
50 	char **parm = options->driver_parm;
51 	audio_info_t ainfo, ainfo2;
52 	int gain;
53 	int bsize = 32 * 1024;
54 	int port;
55 	AUDIO_INITINFO(&ainfo);
56 
57 	if ((audio_fd = open("/dev/audio", O_WRONLY)) == -1)
58 		return -1;
59 
60 	/* try to open audioctl device */
61 	if ((audioctl_fd = open("/dev/audioctl", O_RDWR)) < 0) {
62 		fprintf(stderr, "couldn't open audioctl device\n");
63 		close(audio_fd);
64 		return -1;
65 	}
66 
67 	/* sleep(2); -- Really needed? */
68 
69 	/* empty buffers before change config */
70 	ioctl(audio_fd, AUDIO_DRAIN, 0);	/* drain everything out */
71 	ioctl(audio_fd, I_FLUSH, FLUSHRW);	/* flush everything     */
72 	ioctl(audioctl_fd, I_FLUSH, FLUSHRW);	/* flush everything */
73 
74 	/* get audio parameters. */
75 	if (ioctl(audioctl_fd, AUDIO_GETINFO, &ainfo) < 0) {
76 		fprintf(stderr, "AUDIO_GETINFO failed!\n");
77 		close(audio_fd);
78 		close(audioctl_fd);
79 		return -1;
80 	}
81 
82 	close(audioctl_fd);
83 
84 	/* KH: Sun Dbri doesn't support 8bits linear. I dont muck with the gain
85 	 *     or the port setting. I hate when a program does that. There is
86 	 *     nothing more frustrating then having a program change your volume
87 	 *     and change from external speakers to the tiny one
88 	 */
89 
90 	gain = ainfo.play.gain;
91 	port = ainfo.play.port;
92 
93 	parm_init(parm);
94 	chkparm1("gain", gain = strtoul(token, NULL, 0));
95 	chkparm1("buffer", bsize = strtoul(token, NULL, 0));
96 	chkparm1("port", port = (int)*token)
97 	parm_end();
98 
99 	switch (port) {
100 	case 'h':
101 		port = AUDIO_HEADPHONE;
102 		break;
103 	case 'l':
104 		port = AUDIO_LINE_OUT;
105 		break;
106 	case 's':
107 		port = AUDIO_SPEAKER;
108 	}
109 
110 	if (gain < AUDIO_MIN_GAIN)
111 		gain = AUDIO_MIN_GAIN;
112 	if (gain > AUDIO_MAX_GAIN)
113 		gain = AUDIO_MAX_GAIN;
114 
115 	AUDIO_INITINFO(&ainfo);	/* For CS4231 */
116 	AUDIO_INITINFO(&ainfo2);	/* For AMD 7930 if needed */
117 
118 	ainfo.play.sample_rate = options->rate;
119 	ainfo.play.channels = options->format & XMP_FORMAT_MONO ? 1 : 2;
120 	ainfo.play.precision = options->format & XMP_FORMAT_8BIT ? 8 : 16;
121 	ainfo.play.encoding = AUDIO_ENCODING_LINEAR;
122 	ainfo2.play.gain = ainfo.play.gain = gain;
123 	ainfo2.play.port = ainfo.play.port = port;
124 	ainfo2.play.buffer_size = ainfo.play.buffer_size = bsize;
125 
126 	if (ioctl(audio_fd, AUDIO_SETINFO, &ainfo) == -1) {
127 		/* CS4231 initialization Failed, perhaps we have an AMD 7930 */
128 		if (ioctl(audio_fd, AUDIO_SETINFO, &ainfo2) == -1) {
129 			close(audio_fd);
130 			return -1;
131 		}
132 
133 		/* Sorry, AMD7930 uLaw not supported anymore */
134 		/* sound_solaris.description = "Solaris AMD7930 PCM audio"; */
135 		return -1;
136 	} else {
137 		/* sound_solaris.description = "Solaris CS4231 PCM audio"; */
138 	}
139 
140 	return 0;
141 }
142 
play(void * b,int i)143 static void play(void *b, int i)
144 {
145 	int j;
146 
147 	while (i) {
148 		if ((j = write(audio_fd, b, i)) > 0) {
149 			i -= j;
150 			b = (char *)b + j;
151 		} else
152 			break;
153 	}
154 }
155 
deinit(void)156 static void deinit(void)
157 {
158 	close(audio_fd);
159 }
160 
flush(void)161 static void flush(void)
162 {
163 }
164 
onpause(void)165 static void onpause(void)
166 {
167 }
168 
onresume(void)169 static void onresume(void)
170 {
171 }
172 
173 static const char *const help[] = {
174 	"gain=val", "Audio output gain (0 to 255)",
175 	"port={s|h|l}", "Audio port (s[peaker], h[eadphones], l[ineout])",
176 	"buffer=val", "Audio buffer size (default is 32768)",
177 	NULL
178 };
179 
180 struct sound_driver sound_solaris = {
181 	"solaris",
182 	"Solaris PCM audio",
183 	help,
184 	init,
185 	deinit,
186 	play,
187 	flush,
188 	onpause,
189 	onresume
190 };
191 
192