oss_plugin.c 13.3 KB
Newer Older
1 2 3
/*
 * Copyright (C) 2003-2009 The Music Player Daemon Project
 * http://www.musicpd.org
4 5 6 7 8 9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
14 15 16 17
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 19
 */

20
#include "../output_api.h"
21
#include "mixer_list.h"
22
#include "mixer_control.h"
23

24
#include <glib.h>
25

26 27 28
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
29 30 31 32 33 34
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "oss"
35

36 37
#if defined(__OpenBSD__) || defined(__NetBSD__)
# include <soundcard.h>
Avuton Olrich's avatar
Avuton Olrich committed
38
#else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
39
# include <sys/soundcard.h>
Avuton Olrich's avatar
Avuton Olrich committed
40
#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
41

Max Kellermann's avatar
Max Kellermann committed
42
#if G_BYTE_ORDER == G_BIG_ENDIAN
43 44 45
# define	AFMT_S16_MPD	 AFMT_S16_BE
#else
# define	AFMT_S16_MPD	 AFMT_S16_LE
46
#endif
47

Max Kellermann's avatar
Max Kellermann committed
48
struct oss_data {
49
	int fd;
50
	const char *device;
51
	struct audio_format audio_format;
52
	int bitFormat;
Avuton Olrich's avatar
Avuton Olrich committed
53
	int *supported[3];
54
	unsigned num_supported[3];
Avuton Olrich's avatar
Avuton Olrich committed
55
	int *unsupported[3];
56
	unsigned num_unsupported[3];
57 58 59

	/** the mixer object associated with this output */
	struct mixer *mixer;
Max Kellermann's avatar
Max Kellermann committed
60
};
61

62 63 64 65 66 67 68 69 70 71 72 73
enum oss_support {
	OSS_SUPPORTED = 1,
	OSS_UNSUPPORTED = 0,
	OSS_UNKNOWN = -1,
};

enum oss_param {
	OSS_RATE = 0,
	OSS_CHANNELS = 1,
	OSS_BITS = 2,
};

74 75 76 77 78 79 80 81 82
/**
 * The quark used for GError.domain.
 */
static inline GQuark
oss_output_quark(void)
{
	return g_quark_from_static_string("oss_output");
}

83
static enum oss_param
Max Kellermann's avatar
Max Kellermann committed
84
oss_param_from_ioctl(unsigned param)
Avuton Olrich's avatar
Avuton Olrich committed
85
{
86
	enum oss_param idx = OSS_RATE;
Avuton Olrich's avatar
Avuton Olrich committed
87 88

	switch (param) {
89
	case SNDCTL_DSP_SPEED:
Max Kellermann's avatar
Max Kellermann committed
90
		idx = OSS_RATE;
91 92
		break;
	case SNDCTL_DSP_CHANNELS:
Max Kellermann's avatar
Max Kellermann committed
93
		idx = OSS_CHANNELS;
94 95
		break;
	case SNDCTL_DSP_SAMPLESIZE:
Max Kellermann's avatar
Max Kellermann committed
96
		idx = OSS_BITS;
97 98 99
		break;
	}

Max Kellermann's avatar
Max Kellermann committed
100
	return idx;
101 102
}

103
static bool
Max Kellermann's avatar
Max Kellermann committed
104
oss_find_supported_param(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
105
{
Max Kellermann's avatar
Max Kellermann committed
106
	enum oss_param idx = oss_param_from_ioctl(param);
Avuton Olrich's avatar
Avuton Olrich committed
107

108
	for (unsigned i = 0; i < od->num_supported[idx]; i++)
Max Kellermann's avatar
Max Kellermann committed
109
		if (od->supported[idx][i] == val)
110
			return true;
111

112
	return false;
113 114
}

115
static bool
Max Kellermann's avatar
Max Kellermann committed
116
oss_can_convert(int idx, int val)
Avuton Olrich's avatar
Avuton Olrich committed
117
{
Max Kellermann's avatar
Max Kellermann committed
118
	switch (idx) {
119
	case OSS_BITS:
Avuton Olrich's avatar
Avuton Olrich committed
120
		if (val != 16)
121
			return false;
122 123
		break;
	case OSS_CHANNELS:
Avuton Olrich's avatar
Avuton Olrich committed
124
		if (val != 2)
125
			return false;
126 127 128
		break;
	}

129
	return true;
130 131
}

Max Kellermann's avatar
Max Kellermann committed
132 133
static int
oss_get_supported_param(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
134
{
Max Kellermann's avatar
Max Kellermann committed
135
	enum oss_param idx = oss_param_from_ioctl(param);
136 137 138
	int ret = -1;
	int least = val;
	int diff;
Avuton Olrich's avatar
Avuton Olrich committed
139

140
	for (unsigned i = 0; i < od->num_supported[idx]; i++) {
Max Kellermann's avatar
Max Kellermann committed
141
		diff = od->supported[idx][i] - val;
Avuton Olrich's avatar
Avuton Olrich committed
142 143 144
		if (diff < 0)
			diff = -diff;
		if (diff < least) {
Max Kellermann's avatar
Max Kellermann committed
145
			if (!oss_can_convert(idx, od->supported[idx][i]))
146
				continue;
Max Kellermann's avatar
Max Kellermann committed
147

148
			least = diff;
Max Kellermann's avatar
Max Kellermann committed
149
			ret = od->supported[idx][i];
150 151 152 153 154 155
		}
	}

	return ret;
}

156
static bool
Max Kellermann's avatar
Max Kellermann committed
157
oss_find_unsupported_param(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
158
{
Max Kellermann's avatar
Max Kellermann committed
159
	enum oss_param idx = oss_param_from_ioctl(param);
Avuton Olrich's avatar
Avuton Olrich committed
160

161
	for (unsigned i = 0; i < od->num_unsupported[idx]; i++) {
Max Kellermann's avatar
Max Kellermann committed
162
		if (od->unsupported[idx][i] == val)
163
			return true;
164 165
	}

166
	return false;
167 168
}

Max Kellermann's avatar
Max Kellermann committed
169 170
static void
oss_add_supported_param(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
171
{
Max Kellermann's avatar
Max Kellermann committed
172
	enum oss_param idx = oss_param_from_ioctl(param);
173

Max Kellermann's avatar
Max Kellermann committed
174
	od->num_supported[idx]++;
175
	od->supported[idx] = g_realloc(od->supported[idx],
Max Kellermann's avatar
Max Kellermann committed
176 177
				       od->num_supported[idx] * sizeof(int));
	od->supported[idx][od->num_supported[idx] - 1] = val;
178 179
}

Max Kellermann's avatar
Max Kellermann committed
180 181
static void
oss_add_unsupported_param(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
182
{
Max Kellermann's avatar
Max Kellermann committed
183
	enum oss_param idx = oss_param_from_ioctl(param);
184

Max Kellermann's avatar
Max Kellermann committed
185
	od->num_unsupported[idx]++;
186
	od->unsupported[idx] = g_realloc(od->unsupported[idx],
Max Kellermann's avatar
Max Kellermann committed
187
					 od->num_unsupported[idx] *
188
					 sizeof(int));
Max Kellermann's avatar
Max Kellermann committed
189
	od->unsupported[idx][od->num_unsupported[idx] - 1] = val;
190 191
}

Max Kellermann's avatar
Max Kellermann committed
192 193
static void
oss_remove_supported_param(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
194
{
195
	unsigned j = 0;
Max Kellermann's avatar
Max Kellermann committed
196
	enum oss_param idx = oss_param_from_ioctl(param);
197

198
	for (unsigned i = 0; i < od->num_supported[idx] - 1; i++) {
Max Kellermann's avatar
Max Kellermann committed
199
		if (od->supported[idx][i] == val)
Avuton Olrich's avatar
Avuton Olrich committed
200
			j = 1;
Max Kellermann's avatar
Max Kellermann committed
201
		od->supported[idx][i] = od->supported[idx][i + j];
202 203
	}

Max Kellermann's avatar
Max Kellermann committed
204
	od->num_supported[idx]--;
205
	od->supported[idx] = g_realloc(od->supported[idx],
Max Kellermann's avatar
Max Kellermann committed
206
				       od->num_supported[idx] * sizeof(int));
207 208
}

Max Kellermann's avatar
Max Kellermann committed
209 210
static void
oss_remove_unsupported_param(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
211
{
212
	unsigned j = 0;
Max Kellermann's avatar
Max Kellermann committed
213
	enum oss_param idx = oss_param_from_ioctl(param);
214

215
	for (unsigned i = 0; i < od->num_unsupported[idx] - 1; i++) {
Max Kellermann's avatar
Max Kellermann committed
216
		if (od->unsupported[idx][i] == val)
Avuton Olrich's avatar
Avuton Olrich committed
217
			j = 1;
Max Kellermann's avatar
Max Kellermann committed
218
		od->unsupported[idx][i] = od->unsupported[idx][i + j];
219 220
	}

Max Kellermann's avatar
Max Kellermann committed
221
	od->num_unsupported[idx]--;
222
	od->unsupported[idx] = g_realloc(od->unsupported[idx],
Max Kellermann's avatar
Max Kellermann committed
223
					 od->num_unsupported[idx] *
224
					 sizeof(int));
225 226
}

227
static enum oss_support
Max Kellermann's avatar
Max Kellermann committed
228
oss_param_is_supported(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
229
{
Max Kellermann's avatar
Max Kellermann committed
230
	if (oss_find_supported_param(od, param, val))
Avuton Olrich's avatar
Avuton Olrich committed
231
		return OSS_SUPPORTED;
Max Kellermann's avatar
Max Kellermann committed
232
	if (oss_find_unsupported_param(od, param, val))
Avuton Olrich's avatar
Avuton Olrich committed
233
		return OSS_UNSUPPORTED;
234 235 236
	return OSS_UNKNOWN;
}

Max Kellermann's avatar
Max Kellermann committed
237 238
static void
oss_set_supported(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
239
{
Max Kellermann's avatar
Max Kellermann committed
240
	enum oss_support supported = oss_param_is_supported(od, param, val);
241

Avuton Olrich's avatar
Avuton Olrich committed
242 243
	if (supported == OSS_SUPPORTED)
		return;
244

Max Kellermann's avatar
Max Kellermann committed
245 246
	if (supported == OSS_UNSUPPORTED)
		oss_remove_unsupported_param(od, param, val);
247

Max Kellermann's avatar
Max Kellermann committed
248
	oss_add_supported_param(od, param, val);
249 250
}

Max Kellermann's avatar
Max Kellermann committed
251 252
static void
oss_set_unsupported(struct oss_data *od, unsigned param, int val)
Avuton Olrich's avatar
Avuton Olrich committed
253
{
Max Kellermann's avatar
Max Kellermann committed
254
	enum oss_support supported = oss_param_is_supported(od, param, val);
255

Avuton Olrich's avatar
Avuton Olrich committed
256 257
	if (supported == OSS_UNSUPPORTED)
		return;
258

Max Kellermann's avatar
Max Kellermann committed
259 260
	if (supported == OSS_SUPPORTED)
		oss_remove_supported_param(od, param, val);
261

Max Kellermann's avatar
Max Kellermann committed
262
	oss_add_unsupported_param(od, param, val);
263 264
}

Max Kellermann's avatar
Max Kellermann committed
265 266
static struct oss_data *
oss_data_new(void)
Avuton Olrich's avatar
Avuton Olrich committed
267
{
Max Kellermann's avatar
Max Kellermann committed
268
	struct oss_data *ret = g_new(struct oss_data, 1);
269

270 271
	ret->device = NULL;
	ret->fd = -1;
272

273 274 275 276 277 278 279
	ret->supported[OSS_RATE] = NULL;
	ret->supported[OSS_CHANNELS] = NULL;
	ret->supported[OSS_BITS] = NULL;
	ret->unsupported[OSS_RATE] = NULL;
	ret->unsupported[OSS_CHANNELS] = NULL;
	ret->unsupported[OSS_BITS] = NULL;

Max Kellermann's avatar
Max Kellermann committed
280 281 282 283 284 285
	ret->num_supported[OSS_RATE] = 0;
	ret->num_supported[OSS_CHANNELS] = 0;
	ret->num_supported[OSS_BITS] = 0;
	ret->num_unsupported[OSS_RATE] = 0;
	ret->num_unsupported[OSS_CHANNELS] = 0;
	ret->num_unsupported[OSS_BITS] = 0;
286

Max Kellermann's avatar
Max Kellermann committed
287 288 289 290
	oss_set_supported(ret, SNDCTL_DSP_SPEED, 48000);
	oss_set_supported(ret, SNDCTL_DSP_SPEED, 44100);
	oss_set_supported(ret, SNDCTL_DSP_CHANNELS, 2);
	oss_set_supported(ret, SNDCTL_DSP_SAMPLESIZE, 16);
291

292 293 294
	return ret;
}

Max Kellermann's avatar
Max Kellermann committed
295 296
static void
oss_data_free(struct oss_data *od)
Avuton Olrich's avatar
Avuton Olrich committed
297
{
298 299 300 301 302 303
	g_free(od->supported[OSS_RATE]);
	g_free(od->supported[OSS_CHANNELS]);
	g_free(od->supported[OSS_BITS]);
	g_free(od->unsupported[OSS_RATE]);
	g_free(od->unsupported[OSS_CHANNELS]);
	g_free(od->unsupported[OSS_BITS]);
304

305
	mixer_free(od->mixer);
306

307
	g_free(od);
308 309
}

310 311 312 313 314 315 316
enum oss_stat {
	OSS_STAT_NO_ERROR = 0,
	OSS_STAT_NOT_CHAR_DEV = -1,
	OSS_STAT_NO_PERMS = -2,
	OSS_STAT_DOESN_T_EXIST = -3,
	OSS_STAT_OTHER = -4,
};
317

318
static enum oss_stat
Max Kellermann's avatar
Max Kellermann committed
319
oss_stat_device(const char *device, int *errno_r)
Avuton Olrich's avatar
Avuton Olrich committed
320
{
321
	struct stat st;
Avuton Olrich's avatar
Avuton Olrich committed
322 323 324

	if (0 == stat(device, &st)) {
		if (!S_ISCHR(st.st_mode)) {
325 326
			return OSS_STAT_NOT_CHAR_DEV;
		}
Avuton Olrich's avatar
Avuton Olrich committed
327
	} else {
Max Kellermann's avatar
Max Kellermann committed
328
		*errno_r = errno;
329

Avuton Olrich's avatar
Avuton Olrich committed
330
		switch (errno) {
331 332 333 334 335 336 337 338 339 340
		case ENOENT:
		case ENOTDIR:
			return OSS_STAT_DOESN_T_EXIST;
		case EACCES:
			return OSS_STAT_NO_PERMS;
		default:
			return OSS_STAT_OTHER;
		}
	}

341
	return OSS_STAT_NO_ERROR;
342 343
}

344 345
static const char *default_devices[] = { "/dev/sound/dsp", "/dev/dsp" };

Max Kellermann's avatar
Max Kellermann committed
346 347
static bool
oss_output_test_default_device(void)
Avuton Olrich's avatar
Avuton Olrich committed
348
{
349
	int fd, i;
350

351
	for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
352
		if ((fd = open(default_devices[i], O_WRONLY)) >= 0) {
353
			close(fd);
354
			return true;
355
		}
356 357
		g_warning("Error opening OSS device \"%s\": %s\n",
			  default_devices[i], strerror(errno));
358 359
	}

360
	return false;
361
}
362

Max Kellermann's avatar
Max Kellermann committed
363
static void *
364
oss_open_default(GError **error)
365 366
{
	int i;
367
	int err[G_N_ELEMENTS(default_devices)];
368
	enum oss_stat ret[G_N_ELEMENTS(default_devices)];
369

370
	for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
Max Kellermann's avatar
Max Kellermann committed
371
		ret[i] = oss_stat_device(default_devices[i], &err[i]);
372
		if (ret[i] == OSS_STAT_NO_ERROR) {
Max Kellermann's avatar
Max Kellermann committed
373
			struct oss_data *od = oss_data_new();
374
			od->device = default_devices[i];
375
			od->mixer = mixer_new(&oss_mixer, NULL);
376
			return od;
377
		}
378 379
	}

380
	for (i = G_N_ELEMENTS(default_devices); --i >= 0; ) {
381 382
		const char *dev = default_devices[i];
		switch(ret[i]) {
383 384 385
		case OSS_STAT_NO_ERROR:
			/* never reached */
			break;
386
		case OSS_STAT_DOESN_T_EXIST:
387
			g_warning("%s not found\n", dev);
388 389
			break;
		case OSS_STAT_NOT_CHAR_DEV:
390
			g_warning("%s is not a character device\n", dev);
391 392
			break;
		case OSS_STAT_NO_PERMS:
393
			g_warning("%s: permission denied\n", dev);
394
			break;
395
		case OSS_STAT_OTHER:
396 397
			g_warning("Error accessing %s: %s\n",
				  dev, strerror(err[i]));
398 399
		}
	}
400 401 402 403

	g_set_error(error, oss_output_quark(), 0,
		    "error trying to open default OSS device");
	return NULL;
404 405
}

406
static void *
Max Kellermann's avatar
Max Kellermann committed
407
oss_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
408 409
		const struct config_param *param,
		GError **error)
Avuton Olrich's avatar
Avuton Olrich committed
410
{
Max Kellermann's avatar
Max Kellermann committed
411 412
	const char *device = config_get_block_string(param, "device", NULL);
	if (device != NULL) {
Max Kellermann's avatar
Max Kellermann committed
413
		struct oss_data *od = oss_data_new();
Max Kellermann's avatar
Max Kellermann committed
414
		od->device = device;
415
		od->mixer = mixer_new(&oss_mixer, param);
Max Kellermann's avatar
Max Kellermann committed
416
		return od;
417
	}
Max Kellermann's avatar
Max Kellermann committed
418

419
	return oss_open_default(error);
420 421
}

Max Kellermann's avatar
Max Kellermann committed
422 423
static void
oss_output_finish(void *data)
Avuton Olrich's avatar
Avuton Olrich committed
424
{
Max Kellermann's avatar
Max Kellermann committed
425
	struct oss_data *od = data;
426

Max Kellermann's avatar
Max Kellermann committed
427
	oss_data_free(od);
428 429
}

430 431 432
static struct mixer *
oss_get_mixer(void *data)
{
Max Kellermann's avatar
Max Kellermann committed
433
	struct oss_data *od = data;
434 435 436 437

	return od->mixer;
}

Max Kellermann's avatar
Max Kellermann committed
438 439
static int
oss_set_param(struct oss_data *od, unsigned param, int *value)
Avuton Olrich's avatar
Avuton Olrich committed
440
{
441 442
	int val = *value;
	int copy;
Max Kellermann's avatar
Max Kellermann committed
443
	enum oss_support supported = oss_param_is_supported(od, param, val);
444 445

	do {
Avuton Olrich's avatar
Avuton Olrich committed
446
		if (supported == OSS_UNSUPPORTED) {
Max Kellermann's avatar
Max Kellermann committed
447
			val = oss_get_supported_param(od, param, val);
Avuton Olrich's avatar
Avuton Olrich committed
448 449
			if (copy < 0)
				return -1;
450 451
		}
		copy = val;
Avuton Olrich's avatar
Avuton Olrich committed
452
		if (ioctl(od->fd, param, &copy)) {
Max Kellermann's avatar
Max Kellermann committed
453
			oss_set_unsupported(od, param, val);
454
			supported = OSS_UNSUPPORTED;
Avuton Olrich's avatar
Avuton Olrich committed
455 456
		} else {
			if (supported == OSS_UNKNOWN) {
Max Kellermann's avatar
Max Kellermann committed
457
				oss_set_supported(od, param, val);
458 459 460 461
				supported = OSS_SUPPORTED;
			}
			val = copy;
		}
Avuton Olrich's avatar
Avuton Olrich committed
462
	} while (supported == OSS_UNSUPPORTED);
463 464 465 466 467 468

	*value = val;

	return 0;
}

Max Kellermann's avatar
Max Kellermann committed
469 470
static void
oss_close(struct oss_data *od)
471
{
Avuton Olrich's avatar
Avuton Olrich committed
472 473
	if (od->fd >= 0)
		while (close(od->fd) && errno == EINTR) ;
474 475 476
	od->fd = -1;
}

477 478 479
/**
 * Sets up the OSS device which was opened before.
 */
Max Kellermann's avatar
Max Kellermann committed
480
static bool
481
oss_setup(struct oss_data *od, GError **error)
Avuton Olrich's avatar
Avuton Olrich committed
482
{
483
	int tmp;
484

485
	tmp = od->audio_format.channels;
Max Kellermann's avatar
Max Kellermann committed
486
	if (oss_set_param(od, SNDCTL_DSP_CHANNELS, &tmp)) {
487 488 489 490
		g_set_error(error, oss_output_quark(), errno,
			    "OSS device \"%s\" does not support %u channels: %s",
			    od->device, od->audio_format.channels,
			    strerror(errno));
491
		return false;
492
	}
493
	od->audio_format.channels = tmp;
494

495
	tmp = od->audio_format.sample_rate;
Max Kellermann's avatar
Max Kellermann committed
496
	if (oss_set_param(od, SNDCTL_DSP_SPEED, &tmp)) {
497 498 499 500
		g_set_error(error, oss_output_quark(), errno,
			    "OSS device \"%s\" does not support %u Hz audio: %s",
			    od->device, od->audio_format.sample_rate,
			    strerror(errno));
501
		return false;
502
	}
503
	od->audio_format.sample_rate = tmp;
504

505
	switch (od->audio_format.bits) {
506 507 508 509 510
	case 8:
		tmp = AFMT_S8;
		break;
	case 16:
		tmp = AFMT_S16_MPD;
511 512 513 514 515 516 517
		break;

	default:
		/* not supported by OSS - fall back to 16 bit */
		od->audio_format.bits = 16;
		tmp = AFMT_S16_MPD;
		break;
518 519
	}

Max Kellermann's avatar
Max Kellermann committed
520
	if (oss_set_param(od, SNDCTL_DSP_SAMPLESIZE, &tmp)) {
521 522 523
		g_set_error(error, oss_output_quark(), errno,
			    "OSS device \"%s\" does not support %u bit audio: %s",
			    od->device, tmp, strerror(errno));
524
		return false;
525
	}
526

527
	return true;
528
}
529

530
static bool
531
oss_open(struct oss_data *od, GError **error)
532 533 534 535
{
	bool success;

	if ((od->fd = open(od->device, O_WRONLY)) < 0) {
536 537 538
		g_set_error(error, oss_output_quark(), errno,
			    "Error opening OSS device \"%s\": %s",
			    od->device, strerror(errno));
539 540 541
		return false;
	}

542
	success = oss_setup(od, error);
543 544 545 546 547 548
	if (!success) {
		oss_close(od);
		return false;
	}

	return true;
549 550
}

551
static bool
552
oss_output_open(void *data, struct audio_format *audio_format, GError **error)
553
{
554
	bool ret;
Max Kellermann's avatar
Max Kellermann committed
555
	struct oss_data *od = data;
556

Max Kellermann's avatar
Max Kellermann committed
557
	od->audio_format = *audio_format;
558

559
	ret = oss_open(od, error);
560 561
	if (!ret)
		return false;
562

Max Kellermann's avatar
Max Kellermann committed
563
	*audio_format = od->audio_format;
564

565
	mixer_open(od->mixer);
566

567
	return ret;
568 569
}

Max Kellermann's avatar
Max Kellermann committed
570 571
static void
oss_output_close(void *data)
Avuton Olrich's avatar
Avuton Olrich committed
572
{
Max Kellermann's avatar
Max Kellermann committed
573
	struct oss_data *od = data;
574

575
	oss_close(od);
576
	mixer_close(od->mixer);
577 578
}

Max Kellermann's avatar
Max Kellermann committed
579 580
static void
oss_output_cancel(void *data)
Avuton Olrich's avatar
Avuton Olrich committed
581
{
Max Kellermann's avatar
Max Kellermann committed
582
	struct oss_data *od = data;
583

Avuton Olrich's avatar
Avuton Olrich committed
584
	if (od->fd >= 0) {
585
		ioctl(od->fd, SNDCTL_DSP_RESET, 0);
586
		oss_close(od);
587 588 589
	}
}

590
static size_t
591
oss_output_play(void *data, const void *chunk, size_t size, GError **error)
592
{
Max Kellermann's avatar
Max Kellermann committed
593
	struct oss_data *od = data;
594
	ssize_t ret;
595

596
	/* reopen the device since it was closed by dropBufferedAudio */
597 598
	if (od->fd < 0 && !oss_open(od, error))
		return 0;
599

600
	while (true) {
601
		ret = write(od->fd, chunk, size);
602 603 604 605
		if (ret > 0)
			return (size_t)ret;

		if (ret < 0 && errno != EINTR) {
606 607 608
			g_set_error(error, oss_output_quark(), errno,
				    "Write error on %s: %s",
				    od->device, strerror(errno));
609
			return 0;
610 611 612 613
		}
	}
}

Max Kellermann's avatar
Max Kellermann committed
614
const struct audio_output_plugin oss_output_plugin = {
615
	.name = "oss",
Max Kellermann's avatar
Max Kellermann committed
616 617 618
	.test_default_device = oss_output_test_default_device,
	.init = oss_output_init,
	.finish = oss_output_finish,
619
	.get_mixer = oss_get_mixer,
Max Kellermann's avatar
Max Kellermann committed
620 621 622 623
	.open = oss_output_open,
	.close = oss_output_close,
	.play = oss_output_play,
	.cancel = oss_output_cancel,
624
};