GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libossaudio/ossaudio.c Lines: 0 195 0.0 %
Date: 2017-11-07 Branches: 0 140 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: ossaudio.c,v 1.18 2015/04/19 08:42:19 ratchov Exp $	*/
2
/*	$NetBSD: ossaudio.c,v 1.14 2001/05/10 01:53:48 augustss Exp $	*/
3
4
/*-
5
 * Copyright (c) 1997 The NetBSD Foundation, Inc.
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
 * POSSIBILITY OF SUCH DAMAGE.
28
 */
29
30
/*
31
 * This is an OSS (Linux) sound API emulator.
32
 * It provides the essentials of the API.
33
 */
34
35
/* XXX This file is essentially the same as sys/compat/ossaudio.c.
36
 * With some preprocessor magic it could be the same file.
37
 */
38
39
#include <stdarg.h>
40
#include <string.h>
41
#include <sys/types.h>
42
#include <sys/ioctl.h>
43
#include <sys/audioio.h>
44
#include <sys/stat.h>
45
#include <errno.h>
46
47
#include "soundcard.h"
48
#undef ioctl
49
50
#define GET_DEV(com) ((com) & 0xff)
51
52
#define TO_OSSVOL(x)	(((x) * 100 + 127) / 255)
53
#define FROM_OSSVOL(x)	((((x) > 100 ? 100 : (x)) * 255 + 50) / 100)
54
55
static struct audiodevinfo *getdevinfo(int);
56
57
static int mixer_ioctl(int, unsigned long, void *);
58
static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq);
59
static int enum_to_ord(struct audiodevinfo *di, int enm);
60
static int enum_to_mask(struct audiodevinfo *di, int enm);
61
62
#define INTARG (*(int*)argp)
63
64
int
65
_oss_ioctl(int fd, unsigned long com, ...)
66
{
67
	va_list ap;
68
	void *argp;
69
70
	va_start(ap, com);
71
	argp = va_arg(ap, void *);
72
	va_end(ap);
73
	if (IOCGROUP(com) == 'P')
74
		return ENOTTY;
75
	else if (IOCGROUP(com) == 'M')
76
		return mixer_ioctl(fd, com, argp);
77
	else
78
		return ioctl(fd, com, argp);
79
}
80
81
/* If the mixer device should have more than MAX_MIXER_DEVS devices
82
 * some will not be available to Linux */
83
#define MAX_MIXER_DEVS 64
84
struct audiodevinfo {
85
	int done;
86
	dev_t dev;
87
	ino_t ino;
88
	int16_t devmap[SOUND_MIXER_NRDEVICES],
89
	        rdevmap[MAX_MIXER_DEVS];
90
	char names[MAX_MIXER_DEVS][MAX_AUDIO_DEV_LEN];
91
	int enum2opaque[MAX_MIXER_DEVS];
92
        u_long devmask, recmask, stereomask;
93
	u_long caps, recsource;
94
};
95
96
static int
97
opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq)
98
{
99
	int i, o;
100
101
	for (i = 0; i < MAX_MIXER_DEVS; i++) {
102
		o = di->enum2opaque[i];
103
		if (o == opq)
104
			break;
105
		if (o == -1 && label != NULL &&
106
		    !strncmp(di->names[i], label->name, sizeof di->names[i])) {
107
			di->enum2opaque[i] = opq;
108
			break;
109
		}
110
	}
111
	if (i >= MAX_MIXER_DEVS)
112
		i = -1;
113
	/*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/
114
	return (i);
115
}
116
117
static int
118
enum_to_ord(struct audiodevinfo *di, int enm)
119
{
120
	if (enm >= MAX_MIXER_DEVS)
121
		return (-1);
122
123
	/*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/
124
	return (di->enum2opaque[enm]);
125
}
126
127
static int
128
enum_to_mask(struct audiodevinfo *di, int enm)
129
{
130
	int m;
131
	if (enm >= MAX_MIXER_DEVS)
132
		return (0);
133
134
	m = di->enum2opaque[enm];
135
	if (m == -1)
136
		m = 0;
137
	/*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/
138
	return (m);
139
}
140
141
/*
142
 * Collect the audio device information to allow faster
143
 * emulation of the Linux mixer ioctls.  Cache the information
144
 * to eliminate the overhead of repeating all the ioctls needed
145
 * to collect the information.
146
 */
147
static struct audiodevinfo *
148
getdevinfo(int fd)
149
{
150
	mixer_devinfo_t mi, cl;
151
	int i, j, e;
152
	static struct {
153
		char *name;
154
		int code;
155
	} *dp, devs[] = {
156
		{ AudioNmicrophone,	SOUND_MIXER_MIC },
157
		{ AudioNline,		SOUND_MIXER_LINE },
158
		{ AudioNcd,		SOUND_MIXER_CD },
159
		{ AudioNdac,		SOUND_MIXER_PCM },
160
		{ AudioNaux,		SOUND_MIXER_LINE1 },
161
		{ AudioNrecord,		SOUND_MIXER_IMIX },
162
		{ AudioNmaster,		SOUND_MIXER_VOLUME },
163
		{ AudioNtreble,		SOUND_MIXER_TREBLE },
164
		{ AudioNbass,		SOUND_MIXER_BASS },
165
		{ AudioNspeaker,	SOUND_MIXER_SPEAKER },
166
		{ AudioNoutput,		SOUND_MIXER_OGAIN },
167
		{ AudioNinput,		SOUND_MIXER_IGAIN },
168
		{ AudioNfmsynth,	SOUND_MIXER_SYNTH },
169
		{ AudioNmidi,		SOUND_MIXER_SYNTH },
170
		{ 0, -1 }
171
	};
172
	static struct audiodevinfo devcache = { 0 };
173
	struct audiodevinfo *di = &devcache;
174
	struct stat sb;
175
176
	/* Figure out what device it is so we can check if the
177
	 * cached data is valid.
178
	 */
179
	if (fstat(fd, &sb) < 0)
180
		return 0;
181
	if (di->done && (di->dev == sb.st_dev && di->ino == sb.st_ino))
182
		return di;
183
184
	di->done = 1;
185
	di->dev = sb.st_dev;
186
	di->ino = sb.st_ino;
187
	di->devmask = 0;
188
	di->recmask = 0;
189
	di->stereomask = 0;
190
	di->recsource = ~0;
191
	di->caps = 0;
192
	for(i = 0; i < SOUND_MIXER_NRDEVICES; i++)
193
		di->devmap[i] = -1;
194
	for(i = 0; i < MAX_MIXER_DEVS; i++) {
195
		di->rdevmap[i] = -1;
196
		di->names[i][0] = '\0';
197
		di->enum2opaque[i] = -1;
198
	}
199
	for(i = 0; i < MAX_MIXER_DEVS; i++) {
200
		mi.index = i;
201
		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
202
			break;
203
		switch(mi.type) {
204
		case AUDIO_MIXER_VALUE:
205
			for(dp = devs; dp->name; dp++)
206
		    		if (strcmp(dp->name, mi.label.name) == 0)
207
					break;
208
			if (dp->code >= 0) {
209
				di->devmap[dp->code] = i;
210
				di->rdevmap[i] = dp->code;
211
				di->devmask |= 1 << dp->code;
212
				if (mi.un.v.num_channels == 2)
213
					di->stereomask |= 1 << dp->code;
214
				strncpy(di->names[i], mi.label.name,
215
					sizeof di->names[i]);
216
			}
217
			break;
218
		}
219
	}
220
	for(i = 0; i < MAX_MIXER_DEVS; i++) {
221
		mi.index = i;
222
		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
223
			break;
224
		if (strcmp(mi.label.name, AudioNsource) != 0)
225
			continue;
226
		cl.index = mi.mixer_class;
227
		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &cl) < 0)
228
			break;
229
		if ((cl.type != AUDIO_MIXER_CLASS) ||
230
		    (strcmp(cl.label.name, AudioCrecord) != 0))
231
			continue;
232
		di->recsource = i;
233
		switch(mi.type) {
234
		case AUDIO_MIXER_ENUM:
235
			for(j = 0; j < mi.un.e.num_mem; j++) {
236
				e = opaque_to_enum(di,
237
						   &mi.un.e.member[j].label,
238
						   mi.un.e.member[j].ord);
239
				if (e >= 0)
240
					di->recmask |= 1 << di->rdevmap[e];
241
			}
242
			di->caps = SOUND_CAP_EXCL_INPUT;
243
			break;
244
		case AUDIO_MIXER_SET:
245
			for(j = 0; j < mi.un.s.num_mem; j++) {
246
				e = opaque_to_enum(di,
247
						   &mi.un.s.member[j].label,
248
						   mi.un.s.member[j].mask);
249
				if (e >= 0)
250
					di->recmask |= 1 << di->rdevmap[e];
251
			}
252
			break;
253
		}
254
	}
255
	return di;
256
}
257
258
int
259
mixer_ioctl(int fd, unsigned long com, void *argp)
260
{
261
	struct audiodevinfo *di;
262
	struct mixer_info *omi;
263
	struct audio_device adev;
264
	mixer_ctrl_t mc;
265
	int idat = 0;
266
	int i;
267
	int retval;
268
	int l, r, n, error, e;
269
270
	di = getdevinfo(fd);
271
	if (di == 0)
272
		return -1;
273
274
	switch (com) {
275
	case OSS_GETVERSION:
276
		idat = SOUND_VERSION;
277
		break;
278
	case SOUND_MIXER_INFO:
279
	case SOUND_OLD_MIXER_INFO:
280
		error = ioctl(fd, AUDIO_GETDEV, &adev);
281
		if (error)
282
			return (error);
283
		omi = argp;
284
		if (com == SOUND_MIXER_INFO)
285
			omi->modify_counter = 1;
286
		strncpy(omi->id, adev.name, sizeof omi->id);
287
		strncpy(omi->name, adev.name, sizeof omi->name);
288
		return 0;
289
	case SOUND_MIXER_READ_RECSRC:
290
		if (di->recsource == -1)
291
			return EINVAL;
292
		mc.dev = di->recsource;
293
		if (di->caps & SOUND_CAP_EXCL_INPUT) {
294
			mc.type = AUDIO_MIXER_ENUM;
295
			retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
296
			if (retval < 0)
297
				return retval;
298
			e = opaque_to_enum(di, NULL, mc.un.ord);
299
			if (e >= 0)
300
				idat = 1 << di->rdevmap[e];
301
		} else {
302
			mc.type = AUDIO_MIXER_SET;
303
			retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
304
			if (retval < 0)
305
				return retval;
306
			e = opaque_to_enum(di, NULL, mc.un.mask);
307
			if (e >= 0)
308
				idat = 1 << di->rdevmap[e];
309
		}
310
		break;
311
	case SOUND_MIXER_READ_DEVMASK:
312
		idat = di->devmask;
313
		break;
314
	case SOUND_MIXER_READ_RECMASK:
315
		idat = di->recmask;
316
		break;
317
	case SOUND_MIXER_READ_STEREODEVS:
318
		idat = di->stereomask;
319
		break;
320
	case SOUND_MIXER_READ_CAPS:
321
		idat = di->caps;
322
		break;
323
	case SOUND_MIXER_WRITE_RECSRC:
324
	case SOUND_MIXER_WRITE_R_RECSRC:
325
		if (di->recsource == -1)
326
			return EINVAL;
327
		mc.dev = di->recsource;
328
		idat = INTARG;
329
		if (di->caps & SOUND_CAP_EXCL_INPUT) {
330
			mc.type = AUDIO_MIXER_ENUM;
331
			for(i = 0; i < SOUND_MIXER_NRDEVICES; i++)
332
				if (idat & (1 << i))
333
					break;
334
			if (i >= SOUND_MIXER_NRDEVICES ||
335
			    di->devmap[i] == -1)
336
				return EINVAL;
337
			mc.un.ord = enum_to_ord(di, di->devmap[i]);
338
		} else {
339
			mc.type = AUDIO_MIXER_SET;
340
			mc.un.mask = 0;
341
			for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
342
				if (idat & (1 << i)) {
343
					if (di->devmap[i] == -1)
344
						return EINVAL;
345
					mc.un.mask |= enum_to_mask(di, di->devmap[i]);
346
				}
347
			}
348
		}
349
		return ioctl(fd, AUDIO_MIXER_WRITE, &mc);
350
	default:
351
		if (MIXER_READ(SOUND_MIXER_FIRST) <= com &&
352
		    com < MIXER_READ(SOUND_MIXER_NRDEVICES)) {
353
			n = GET_DEV(com);
354
			if (di->devmap[n] == -1)
355
				return EINVAL;
356
			mc.dev = di->devmap[n];
357
			mc.type = AUDIO_MIXER_VALUE;
358
		    doread:
359
			mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
360
			retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
361
			if (retval < 0)
362
				return retval;
363
			if (mc.type != AUDIO_MIXER_VALUE)
364
				return EINVAL;
365
			if (mc.un.value.num_channels != 2) {
366
				l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
367
			} else {
368
				l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
369
				r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
370
			}
371
			idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
372
			break;
373
		} else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com &&
374
			   com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) ||
375
			   (MIXER_WRITE(SOUND_MIXER_FIRST) <= com &&
376
			   com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) {
377
			n = GET_DEV(com);
378
			if (di->devmap[n] == -1)
379
				return EINVAL;
380
			idat = INTARG;
381
			l = FROM_OSSVOL( idat       & 0xff);
382
			r = FROM_OSSVOL((idat >> 8) & 0xff);
383
			mc.dev = di->devmap[n];
384
			mc.type = AUDIO_MIXER_VALUE;
385
			if (di->stereomask & (1<<n)) {
386
				mc.un.value.num_channels = 2;
387
				mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
388
				mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
389
			} else {
390
				mc.un.value.num_channels = 1;
391
				mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
392
			}
393
			retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc);
394
			if (retval < 0)
395
				return retval;
396
			if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com &&
397
			   com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))
398
				return 0;
399
			goto doread;
400
		} else {
401
			errno = EINVAL;
402
			return -1;
403
		}
404
	}
405
	INTARG = idat;
406
	return 0;
407
}