1 /* Sysdep Solaris sound dsp driver
2 
3    Copyright 2000 Hans de Goede, Mathis Rosenhauer
4 
5    This file and the acompanying files in this directory are free software;
6    you can redistribute them and/or modify them under the terms of the GNU
7    Library General Public License as published by the Free Software Foundation;
8    either version 2 of the License, or (at your option) any later version.
9 
10    These files are distributed in the hope that they will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with these files; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.
19 */
20 /* Changelog
21 Version 0.1, February 2000
22 -initial release, based on oss.c and on the old xmame sound driver done by
23  Juan Antonio Martinez, Keith Hargrove, Mathis Rosenhauer and others.
24  (Mathis Rosenhauer)
25 */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/types.h>
32 #include <sys/conf.h>
33 #include <sys/stat.h>
34 #include <sys/ioctl.h>
35 #include <sys/stropts.h>
36 #include <sys/audioio.h>
37 #include "sysdep/sysdep_dsp.h"
38 #include "sysdep/sysdep_dsp_priv.h"
39 #include "sysdep/plugin_manager.h"
40 
41 /* our per instance private data struct */
42 struct sol_dsp_priv_data {
43 	int fd;
44 	unsigned int samples_written;
45 	unsigned int buffer_samples;
46 };
47 
48 /* public methods prototypes (static but exported through the sysdep_dsp or
49    plugin struct) */
50 static void *sol_dsp_create(const void *flags);
51 static void sol_dsp_destroy(struct sysdep_dsp_struct *dsp);
52 static int sol_dsp_get_freespace(struct sysdep_dsp_struct *dsp);
53 static int sol_dsp_write(struct sysdep_dsp_struct *dsp, unsigned char *data,
54 						 int count);
55 
56 /* public variables */
57 const struct plugin_struct sysdep_dsp_solaris = {
58 	"solaris",
59 	"sysdep_dsp",
60 	"Solaris DSP plugin",
61 	NULL, /* no options */
62 	NULL, /* no init */
63 	NULL, /* no exit */
64 	sol_dsp_create,
65 	3	  /* high priority */
66 };
67 
68 /* private variables */
69 static int sol_dsp_bytes_per_sample[4] = SYSDEP_DSP_BYTES_PER_SAMPLE;
70 
71 /* public methods (static but exported through the sysdep_dsp or plugin
72    struct) */
sol_dsp_create(const void * flags)73 static void *sol_dsp_create(const void *flags)
74 {
75 	int i,j;
76 	audio_info_t info;   /* info about audio settings */
77 	audio_device_t dev;  /* info about audio hardware */
78 	struct sol_dsp_priv_data *priv = NULL;
79 	struct sysdep_dsp_struct *dsp = NULL;
80 	const struct sysdep_dsp_create_params *params = flags;
81 	const char *device = params->device;
82 
83 	/* allocate the dsp struct */
84 	if (!(dsp = calloc(1, sizeof(struct sysdep_dsp_struct))))
85 	{
86 		perror("error malloc failed for struct sysdep_dsp_struct\n");
87 		return NULL;
88 	}
89 
90 	/* alloc private data */
91 	if(!(priv = calloc(1, sizeof(struct sol_dsp_priv_data))))
92 	{
93 		perror("error malloc failed for struct sol_dsp_priv_data\n");
94 		sol_dsp_destroy(dsp);
95 		return NULL;
96 	}
97 
98 	/* fill in the functions and some data */
99 	priv->fd = -1;
100 	priv->samples_written = 0;
101 	dsp->_priv = priv;
102 	dsp->get_freespace = sol_dsp_get_freespace;
103 	dsp->write = sol_dsp_write;
104 	dsp->destroy = sol_dsp_destroy;
105 	dsp->hw_info.type = params->type;
106 	dsp->hw_info.samplerate = params->samplerate;
107 
108 	/* open the sound device */
109 	if (!device)
110 		device = getenv("AUDIODEV");
111 	if (!device)
112 		device = "/dev/audio";
113 
114 	if((priv->fd = open(device, O_WRONLY)) < 0)
115 	{
116 		fprintf(stderr, "error: opening %s\n", device);
117 		sol_dsp_destroy(dsp);
118 		return NULL;
119 	}
120 
121 	/* empty buffers before change config */
122 	ioctl(priv->fd, AUDIO_DRAIN, 0);	/* drain everything out */
123 	ioctl(priv->fd, I_FLUSH, FLUSHRW);	/* flush everything		*/
124 
125 	/* identify audio device. */
126 	if(ioctl(priv->fd, AUDIO_GETDEV, &dev) < 0)
127 	{
128 		perror("error: cannot get sound device type\n");
129 		sol_dsp_destroy(dsp);
130 		return NULL;
131 	}
132 
133 	fprintf(stderr, "info: sound device is a %s %s version %s\n",dev.config,dev.name,dev.version);
134 
135 	/* get audio parameters. */
136 	if (ioctl(priv->fd, AUDIO_GETINFO, &info) < 0)
137 	{
138 		perror("AUDIO_GETINFO failed!\nRun with -nosound\n");
139 		sol_dsp_destroy(dsp);
140 		return NULL;
141 	}
142 
143 	/* set the number of bits */
144 	AUDIO_INITINFO(&info);
145 
146 	if (dsp->hw_info.type & SYSDEP_DSP_16BIT)
147 	{
148 		info.play.encoding = i = AUDIO_ENCODING_LINEAR;
149 		info.play.precision = j = 16;
150 	}
151 	else
152 	{
153 		info.play.encoding = i = AUDIO_ENCODING_LINEAR8;
154 		info.play.precision = j = 8;
155 	}
156 
157 	if (ioctl(priv->fd, AUDIO_SETINFO, &info) < 0)
158 	{
159 		perror("error: AUDIO_SETINFO\n");
160 		sol_dsp_destroy(dsp);
161 		return NULL;
162 	}
163 	if ((info.play.encoding != i) || (info.play.precision != j))
164 	{
165 		if(dsp->hw_info.type & SYSDEP_DSP_16BIT)
166 		{
167 			fprintf(stderr, "warning: couldn't set sound to 16 bits,\ntrying again with 8 bits: ");
168 		}
169 		else
170 		{
171 			fprintf(stderr, "error: couldn't set sound to 8 bits,\n");
172 			sol_dsp_destroy(dsp);
173 			return NULL;
174 		}
175 		dsp->hw_info.type &= ~SYSDEP_DSP_16BIT;
176 		info.play.precision = 8;
177 		info.play.encoding = AUDIO_ENCODING_LINEAR8;
178 		if (ioctl(priv->fd, AUDIO_SETINFO, &info) < 0)
179 		{
180 			perror("error: AUDIO_SETINFO\n");
181 			sol_dsp_destroy(dsp);
182 			return NULL;
183 		}
184 		if (info.play.precision != 8 || info.play.encoding != AUDIO_ENCODING_LINEAR8)
185 		{
186 			fprintf(stderr, "failed\n");
187 			sol_dsp_destroy(dsp);
188 			return NULL;
189 		}
190 		fprintf(stderr, "success\n");
191 	}
192 
193    /* set the number of channels */
194     info.play.channels = (dsp->hw_info.type & SYSDEP_DSP_STEREO)? 2:1;
195 	if (ioctl(priv->fd, AUDIO_SETINFO, &info) < 0)
196 	{
197 		perror("error: AUDIO_SETINFO\n");
198 		sol_dsp_destroy(dsp);
199 		return NULL;
200 	}
201 	if(info.play.channels == 2)
202 		dsp->hw_info.type |= SYSDEP_DSP_STEREO;
203 	else
204 		dsp->hw_info.type &= ~SYSDEP_DSP_STEREO;
205 
206 	/* set the samplerate and buffer size*/
207 	info.play.sample_rate = dsp->hw_info.samplerate;
208 	priv->buffer_samples = dsp->hw_info.samplerate * params->bufsize;
209 	info.play.buffer_size = dsp->hw_info.bufsize = priv->buffer_samples * sol_dsp_bytes_per_sample[dsp->hw_info.type]; /* this seems to have no effect */
210 	if (ioctl(priv->fd, AUDIO_SETINFO, &info) < 0)
211 	{
212 		perror("error: AUDIO_SETINFO\n");
213 		sol_dsp_destroy(dsp);
214 		return NULL;
215 	}
216 
217 	dsp->hw_info.samplerate = info.play.sample_rate;
218 	fprintf(stderr, "info: audiodevice %s set to %dbit linear %s %dHz\n",
219 			dev.name, info.play.precision,
220 			(info.play.channels & 2)? "stereo":"mono",
221 			info.play.sample_rate);
222 
223 	return dsp;
224 }
225 
sol_dsp_destroy(struct sysdep_dsp_struct * dsp)226 static void sol_dsp_destroy(struct sysdep_dsp_struct *dsp)
227 {
228 	struct sol_dsp_priv_data *priv = dsp->_priv;
229 
230 	if(priv)
231 	{
232 		if(priv->fd >= 0)
233 			close(priv->fd);
234 		free(priv);
235 	}
236 	free(dsp);
237 }
238 
sol_dsp_get_freespace(struct sysdep_dsp_struct * dsp)239 static int sol_dsp_get_freespace(struct sysdep_dsp_struct *dsp)
240 {
241 	audio_info_t info;
242 	struct sol_dsp_priv_data *priv = dsp->_priv;
243 
244 	if (ioctl(priv->fd, AUDIO_GETINFO, &info) < 0)
245 	{
246 		perror("error: AUDIO_GETINFO\n");
247 		return -1;
248 	}
249 	return priv->buffer_samples - (priv->samples_written - info.play.samples);
250 }
251 
sol_dsp_write(struct sysdep_dsp_struct * dsp,unsigned char * data,int count)252 static int sol_dsp_write(struct sysdep_dsp_struct *dsp, unsigned char *data, int count)
253 {
254 	int result;
255 	struct sol_dsp_priv_data *priv = dsp->_priv;
256 	result = write(priv->fd, data, count * sol_dsp_bytes_per_sample[dsp->hw_info.type]);
257 
258 	if (result < 0)
259 		return -1;
260 
261 	result /= sol_dsp_bytes_per_sample[dsp->hw_info.type];
262 	priv->samples_written += result;
263 	return result;
264 }
265