xref: /inferno-os/emu/Linux/audio-oss.c (revision 79272b32d1a7e81e3c7c73b8f127b2b5f9322bed)
1 #include "dat.h"
2 #include "fns.h"
3 #include "error.h"
4 #include "audio.h"
5 #include <sys/ioctl.h>
6 #include <sys/soundcard.h>
7 
8 #define 	Audio_Mic_Val		SOUND_MIXER_MIC
9 #define 	Audio_Linein_Val	SOUND_MIXER_LINE
10 
11 #define		Audio_Speaker_Val	SOUND_MIXER_PCM // SOUND_MIXER_VOLUME
12 #define		Audio_Headphone_Val	SOUND_MIXER_ALTPCM
13 #define		Audio_Lineout_Val	SOUND_MIXER_CD
14 
15 #define 	Audio_Pcm_Val		AFMT_S16_LE
16 #define 	Audio_Ulaw_Val		AFMT_MU_LAW
17 #define 	Audio_Alaw_Val		AFMT_A_LAW
18 
19 #include "audio-tbls.c"
20 #define	min(a,b)	((a) < (b) ? (a) : (b))
21 
22 #define DEVAUDIO	"/dev/dsp"
23 #define DEVMIXER	"/dev/mixer"
24 
25 #define DPRINT if(1)print
26 
27 enum {
28 	A_Pause,
29 	A_UnPause,
30 	A_In,
31 	A_Out,
32 };
33 
34 static struct {
35 	int data;	/* dsp data fd */
36 	int ctl;	/* mixer fd */
37 	int pause;
38 	QLock lk;
39 } afd = {.data = -1, .ctl = -1, .pause =A_UnPause };
40 
41 static Audio_t av;
42 static QLock inlock;
43 static QLock outlock;
44 
45 static int audio_open(int);
46 static int audio_pause(int, int);
47 static int audio_set_info(int, Audio_d*, int);
48 
49 Audio_t*
getaudiodev(void)50 getaudiodev(void)
51 {
52 	return &av;
53 }
54 
55 void
audio_file_init(void)56 audio_file_init(void)
57 {
58 	audio_info_init(&av);
59 }
60 
61 void
audio_file_open(Chan * c,int omode)62 audio_file_open(Chan *c, int omode)
63 {
64 	USED(c);
65 	DPRINT("audio_file_open %d %d %x\n", afd.data, afd.ctl, omode);
66 	qlock(&afd.lk);
67 	if(waserror()){
68 		qunlock(&afd.lk);
69 		nexterror();
70 	}
71 	if(afd.data >= 0)
72 		error(Einuse);
73 	if(afd.ctl < 0){
74 		afd.ctl = open(DEVMIXER, ORDWR);
75 		if(afd.ctl < 0)
76 			oserror();
77 	}
78 	afd.data = audio_open(omode);
79 	if(afd.data < 0)
80 		oserror();
81 	poperror();
82 	qunlock(&afd.lk);
83 }
84 
85 void
audio_file_close(Chan * c)86 audio_file_close(Chan *c)
87 {
88 	USED(c);
89 	DPRINT("audio_file_close %d %d\n", afd.data, afd.ctl);
90 	qlock(&afd.lk);
91 	if(waserror()){
92 		qunlock(&afd.lk);
93 		nexterror();
94 	}
95 	close(afd.data);
96 	afd.data = -1;
97 	qunlock(&afd.lk);
98 	poperror();
99 }
100 
101 long
audio_file_read(Chan * c,void * va,long count,vlong offset)102 audio_file_read(Chan *c, void *va, long count, vlong offset)
103 {
104 	long ba, status, chunk, total;
105 
106 	USED(c);
107 	USED(offset);
108 	DPRINT("audio_file_read %d %d\n", afd.data, afd.ctl);
109 	qlock(&inlock);
110 	if(waserror()){
111 		qunlock(&inlock);
112 		nexterror();
113 	}
114 
115 	if(afd.data < 0)
116 		error(Eperm);
117 
118 	/* check block alignment */
119 	ba = av.in.bits * av.in.chan / Bits_Per_Byte;
120 
121 	if(count % ba)
122 		error(Ebadarg);
123 
124 	if(!audio_pause(afd.data, A_UnPause))
125 		error(Eio);
126 
127 	total = 0;
128 	while(total < count){
129 		chunk = count - total;
130 		status = read (afd.data, va + total, chunk);
131 		if(status < 0)
132 			error(Eio);
133 		total += status;
134 	}
135 
136 	if(total != count)
137 		error(Eio);
138 
139 	poperror();
140 	qunlock(&inlock);
141 
142 	return count;
143 }
144 
145 long
audio_file_write(Chan * c,void * va,long count,vlong offset)146 audio_file_write(Chan *c, void *va, long count, vlong offset)
147 {
148 	long status = -1;
149 	long ba, total, chunk, bufsz;
150 
151 	USED(c);
152 	USED(offset);
153 	DPRINT("audio_file_write %d %d\n", afd.data, afd.ctl);
154 	qlock(&outlock);
155 	if(waserror()){
156 		qunlock(&outlock);
157 		nexterror();
158 	}
159 
160 	if(afd.data < 0)
161 		error(Eperm);
162 
163 	/* check block alignment */
164 	ba = av.out.bits * av.out.chan / Bits_Per_Byte;
165 
166 	if(count % ba)
167 		error(Ebadarg);
168 
169 	total = 0;
170 	bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
171 
172 	if(bufsz == 0)
173 		error(Ebadarg);
174 
175 	while(total < count){
176 		chunk = min(bufsz, count - total);
177 		status = write(afd.data, va, chunk);
178 		if(status <= 0)
179 			error(Eio);
180 		total += status;
181 	}
182 
183 	poperror();
184 	qunlock(&outlock);
185 
186 	return count;
187 }
188 
189 long
audio_ctl_write(Chan * c,void * va,long count,vlong offset)190 audio_ctl_write(Chan *c, void *va, long count, vlong offset)
191 {
192 	Audio_t tmpav = av;
193 	int tfd;
194 
195 	USED(c);
196 	USED(offset);
197 	tmpav.in.flags = 0;
198 	tmpav.out.flags = 0;
199 
200 	DPRINT ("audio_ctl_write %X %X\n", afd.data, afd.ctl);
201 	if(!audioparse(va, count, &tmpav))
202 		error(Ebadarg);
203 
204 	if(!canqlock(&inlock))
205 		error("device busy");
206 	if(waserror()){
207 		qunlock(&inlock);
208 		nexterror();
209 	}
210 	if(!canqlock(&outlock))
211 		error("device busy");
212 	if(waserror()){
213 		qunlock(&outlock);
214 		nexterror();
215 	}
216 
217 	/* DEVAUDIO needs to be open to issue an ioctl */
218 	tfd = afd.data;
219 	if(tfd < 0){
220 		tfd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
221 		if(tfd < 0)
222 			oserror();
223 	}
224 	if(waserror()){
225 		if(tfd != afd.data)
226 			close(tfd);
227 		nexterror();
228 	}
229 
230 	if(tmpav.in.flags & AUDIO_MOD_FLAG){
231 		if(!audio_pause(tfd, A_Pause))
232 			error(Ebadarg);
233 		if(!audio_set_info(tfd, &tmpav.in, A_In))
234 			error(Ebadarg);
235 	}
236 
237 	poperror();
238 	if(tfd != afd.data)
239 		close(tfd);
240 
241 	tmpav.in.flags = 0;
242 	av = tmpav;
243 
244 	poperror();
245 	qunlock(&outlock);
246 	poperror();
247 	qunlock(&inlock);
248 	return count;
249 }
250 
251 /* Linux/OSS specific stuff */
252 
253 static int
choosefmt(Audio_d * i)254 choosefmt(Audio_d *i)
255 {
256 	switch(i->bits){
257 	case 8:
258 		switch(i->enc){
259 		case Audio_Alaw_Val:
260 			return AFMT_A_LAW;
261 		case Audio_Ulaw_Val:
262 			return AFMT_MU_LAW;
263 		case Audio_Pcm_Val:
264 			return AFMT_U8;
265 		}
266 		break;
267 	case 16:
268 		if(i->enc == Audio_Pcm_Val)
269 			return AFMT_S16_LE;
270 		break;
271 	}
272 	return -1;
273 }
274 
275 static int
setvolume(int fd,int what,int left,int right)276 setvolume(int fd, int what, int left, int right)
277 {
278 	int can, v;
279 
280 	if(ioctl(fd, SOUND_MIXER_READ_DEVMASK, &can) < 0)
281 		can = ~0;
282 
283 	DPRINT("setvolume fd%d %X can mix 0x%X (mask %X)\n", fd, what, (can & (1<<what)), can);
284 	if(!(can & (1<<what)))
285 		return 0;
286 	v = left | (right<<8);
287 	if(ioctl(afd.ctl, MIXER_WRITE(what), &v) < 0)
288 		return 0;
289 	return 1;
290 }
291 
292 static int
audio_set_info(int fd,Audio_d * i,int d)293 audio_set_info(int fd, Audio_d *i, int d)
294 {
295 	int status, arg;
296 	int oldfmt, newfmt;
297 
298 	USED(d);
299 	DPRINT("audio_set_info (%d) %d %d\n", fd, afd.data, afd.ctl);
300 	if(fd < 0)
301 		return 0;
302 
303 	/* sample rate */
304 	if(i->flags & AUDIO_RATE_FLAG){
305 		arg = i->rate;
306 		if(ioctl(fd, SNDCTL_DSP_SPEED, &arg) < 0)
307 			return 0;
308 	}
309 
310 	/* channels */
311 	if(i->flags & AUDIO_CHAN_FLAG){
312 		arg = i->chan;
313 		if(ioctl(fd, SNDCTL_DSP_CHANNELS, &arg) < 0)
314 			return 0;
315 	}
316 
317 	/* precision */
318 	if(i->flags & AUDIO_BITS_FLAG){
319 		arg = i->bits;
320 		if(ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &arg) < 0)
321 			return 0;
322 	}
323 
324 	/* encoding */
325 	if(i->flags & AUDIO_ENC_FLAG){
326 		ioctl(fd, SNDCTL_DSP_GETFMTS, &oldfmt);
327 
328 		newfmt = choosefmt(i);
329 		if(newfmt < 0)
330 			return 0;
331 		if(newfmt != oldfmt){
332 			status = ioctl(fd, SNDCTL_DSP_SETFMT, &arg);
333 			DPRINT ("enc oldfmt newfmt %x status %d\n", oldfmt, newfmt, status);
334 		}
335 	}
336 
337 	/* dev volume */
338 	if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG))
339 		return setvolume(afd.ctl, i->dev, i->left, i->right);
340 
341 	return 1;
342 }
343 
344 static int
audio_set_blocking(int fd)345 audio_set_blocking(int fd)
346 {
347 	int val;
348 
349 	if((val = fcntl(fd, F_GETFL, 0)) == -1)
350 		return 0;
351 
352 	val &= ~O_NONBLOCK;
353 
354 	if(fcntl(fd, F_SETFL, val) < 0)
355 		return 0;
356 
357 	return 1;
358 }
359 
360 static int
audio_open(int omode)361 audio_open(int omode)
362 {
363 	int fd;
364 
365 	/* open non-blocking in case someone already has it open */
366 	/* otherwise we would block until they close! */
367 	switch (omode){
368 	case OREAD:
369 		fd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK);
370 		break;
371 	case OWRITE:
372 		fd = open(DEVAUDIO, O_WRONLY|O_NONBLOCK);
373 		break;
374 	case ORDWR:
375 		fd = open(DEVAUDIO, O_RDWR|O_NONBLOCK);
376 		break;
377 	}
378 
379 	DPRINT("audio_open %d\n", fd);
380 	if(fd < 0)
381 		oserror();
382 
383 	/* change device to be blocking */
384 	if(!audio_set_blocking(fd)){
385 		close(fd);
386 		error("cannot set blocking mode");
387 	}
388 
389 	if(!audio_pause(fd, A_Pause)){
390 		close(fd);
391 		error(Eio);
392 	}
393 
394 	/* set audio info */
395 	av.in.flags = ~0;
396 	av.out.flags = ~0;
397 
398 	if(!audio_set_info(fd, &av.in, A_In)){
399 		close(fd);
400 		error(Ebadarg);
401 	}
402 
403 	av.in.flags = 0;
404 
405 	/* tada, we're open, blocking, paused and flushed */
406 	return fd;
407 }
408 
409 static int
dspsync(int fd)410 dspsync(int fd)
411 {
412 	return ioctl(fd, SNDCTL_DSP_RESET, NULL) >= 0 &&
413 		ioctl(fd, SNDCTL_DSP_SYNC, NULL) >= 0;
414 }
415 
416 static int
audio_pause(int fd,int f)417 audio_pause(int fd, int f)
418 {
419 	int status;
420 
421 //	DPRINT ("audio_pause (%d) %d %d\n", fd, afd.data, afd.ctl);
422 	if(fd < 0)
423 		return 0;
424 	if(fd != afd.data)
425 		return dspsync(fd);
426 	qlock(&afd.lk);
427 	if(afd.pause == f){
428 		qunlock(&afd.lk);
429 		return 1;
430 	}
431 	if(waserror()){
432 		qunlock(&afd.lk);
433 		nexterror();
434 	}
435 	status = dspsync(afd.data);
436 	if(status)
437 		afd.pause = f;
438 	poperror();
439 	qunlock(&afd.lk);
440 	return status;
441 }
442