1 /*
2  * Copyright 2008-2013 Various Authors
3  * Copyright 2004-2005 Timo Hirvonen
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "../op.h"
20 #include "../sf.h"
21 #include "../xmalloc.h"
22 #include "../debug.h"
23 #include "../utils.h"
24 
25 #if defined(__OpenBSD__)
26 #include <soundcard.h>
27 #else
28 #include <sys/soundcard.h>
29 #endif
30 #include <sys/ioctl.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 
36 static sample_format_t oss_sf;
37 static int oss_fd = -1;
38 
39 /* configuration */
40 static char *oss_dsp_device = NULL;
41 
42 static int oss_close(void);
43 
oss_reset(void)44 static int oss_reset(void)
45 {
46 	if (fcntl(oss_fd, SNDCTL_DSP_RESET, 0) == -1) {
47 		return -1;
48 	}
49 	return 0;
50 }
51 
52 #if defined(__linux__)
53 /* defined only in OSSv4, but seem to work in OSSv3 (Linux) */
54 #ifndef AFMT_S32_LE
55 #define AFMT_S32_LE	0x00001000
56 #endif
57 #ifndef AFMT_S32_BE
58 #define AFMT_S32_BE	0x00002000
59 #endif
60 #ifndef AFMT_S24_PACKED
61 #define AFMT_S24_PACKED	0x00040000
62 #endif
63 #endif
64 
65 struct oss_fmt {
66 	int fmt, bits, sig, be;
67 };
68 static struct oss_fmt oss_fmts[] = {
69 	{ AFMT_S16_BE, 16, 1, 1 },
70 	{ AFMT_S16_LE, 16, 1, 0 },
71 #ifdef AFMT_S24_PACKED
72 	{ AFMT_S24_PACKED, 24, 1, 0 },
73 #endif
74 #ifdef AFMT_S24_BE
75 	{ AFMT_S24_BE, 24, 1, 1 },
76 #endif
77 #ifdef AFMT_S24_LE
78 	{ AFMT_S24_LE, 24, 1, 0 },
79 #endif
80 #ifdef AFMT_S32_BE
81 	{ AFMT_S32_BE, 32, 1, 1 },
82 #endif
83 #ifdef AFMT_S32_LE
84 	{ AFMT_S32_LE, 32, 1, 0 },
85 #endif
86 
87 	{ AFMT_U16_BE, 16, 0, 1 },
88 	{ AFMT_U16_LE, 16, 0, 0 },
89 #ifdef AFMT_U24_BE
90 	{ AFMT_U24_BE, 24, 0, 1 },
91 #endif
92 #ifdef AFMT_U24_LE
93 	{ AFMT_U24_LE, 24, 0, 0 },
94 #endif
95 #ifdef AFMT_U32_BE
96 	{ AFMT_U32_BE, 32, 0, 1 },
97 #endif
98 #ifdef AFMT_U32_LE
99 	{ AFMT_U32_LE, 32, 0, 0 },
100 #endif
101 	{ AFMT_S8, 8, 1, 0 },
102 	{ AFMT_S8, 8, 1, 1 },
103 	{ AFMT_U8, 8, 0, 0 },
104 	{ AFMT_U8, 8, 0, 1 },
105 };
106 
oss_set_sf(sample_format_t sf)107 static int oss_set_sf(sample_format_t sf)
108 {
109 	int found, tmp, log2_fragment_size, nr_fragments, bytes_per_second;
110 	size_t i;
111 
112 	oss_reset();
113 	oss_sf = sf;
114 
115 #ifdef SNDCTL_DSP_CHANNELS
116 	tmp = sf_get_channels(oss_sf);
117 	if (ioctl(oss_fd, SNDCTL_DSP_CHANNELS, &tmp) == -1)
118 		return -1;
119 #else
120 	tmp = sf_get_channels(oss_sf) - 1;
121 	if (ioctl(oss_fd, SNDCTL_DSP_STEREO, &tmp) == -1)
122 		return -1;
123 #endif
124 
125 	found = 0;
126 	for (i = 0; i < N_ELEMENTS(oss_fmts); i++) {
127 		if (sf_get_bits(oss_sf) == oss_fmts[i].bits &&
128 		    sf_get_signed(oss_sf) == oss_fmts[i].sig &&
129 		    sf_get_bigendian(oss_sf) == oss_fmts[i].be) {
130 			found = 1;
131 			tmp = oss_fmts[i].fmt;
132 			break;
133 		}
134 	}
135 	if (!found) {
136 		d_print("unsupported sample format: %c%u_%s\n",
137 			sf_get_signed(oss_sf) ? 'S' : 'U', sf_get_bits(oss_sf),
138 			sf_get_bigendian(oss_sf) ? "BE" : "LE");
139 		return -1;
140 	}
141 	if (ioctl(oss_fd, SNDCTL_DSP_SAMPLESIZE, &tmp) == -1)
142 		return -1;
143 
144 	tmp = sf_get_rate(oss_sf);
145 	if (ioctl(oss_fd, SNDCTL_DSP_SPEED, &tmp) == -1)
146 		return -1;
147 
148 	bytes_per_second = sf_get_second_size(oss_sf);
149 	log2_fragment_size = 0;
150 	while (1 << log2_fragment_size < bytes_per_second / 25)
151 		log2_fragment_size++;
152 	log2_fragment_size--;
153 	nr_fragments = 32;
154 
155 	/* bits 0..15 = size of fragment, 16..31 = log2(number of fragments) */
156 	tmp = (nr_fragments << 16) + log2_fragment_size;
157 	if (ioctl(oss_fd, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
158 		return -1;
159 	return 0;
160 }
161 
oss_device_exists(const char * device)162 static int oss_device_exists(const char *device)
163 {
164 	struct stat s;
165 
166 	if (stat(device, &s))
167 		return 0;
168 	return 1;
169 }
170 
oss_init(void)171 static int oss_init(void)
172 {
173 	const char *new_dsp_dev = "/dev/sound/dsp";
174 	const char *dsp_dev = "/dev/dsp";
175 
176 	if (oss_dsp_device) {
177 		if (oss_device_exists(oss_dsp_device))
178 			return 0;
179 		free(oss_dsp_device);
180 		oss_dsp_device = NULL;
181 		return -1;
182 	}
183 	if (oss_device_exists(new_dsp_dev)) {
184 		oss_dsp_device = xstrdup(new_dsp_dev);
185 		return 0;
186 	}
187 	if (oss_device_exists(dsp_dev)) {
188 		oss_dsp_device = xstrdup(dsp_dev);
189 		return 0;
190 	}
191 	return -1;
192 }
193 
oss_exit(void)194 static int oss_exit(void)
195 {
196 	free(oss_dsp_device);
197 	oss_dsp_device = NULL;
198 	return 0;
199 }
200 
oss_open(sample_format_t sf,const channel_position_t * channel_map)201 static int oss_open(sample_format_t sf, const channel_position_t *channel_map)
202 {
203 	int oss_version = 0;
204 	oss_fd = open(oss_dsp_device, O_WRONLY);
205 	if (oss_fd == -1)
206 		return -1;
207 	ioctl(oss_fd, OSS_GETVERSION, &oss_version);
208 	d_print("oss version: %#08x\n", oss_version);
209 	if (oss_set_sf(sf) == -1) {
210 		oss_close();
211 		return -1;
212 	}
213 	return 0;
214 }
215 
oss_close(void)216 static int oss_close(void)
217 {
218 	close(oss_fd);
219 	oss_fd = -1;
220 	return 0;
221 }
222 
oss_write(const char * buffer,int count)223 static int oss_write(const char *buffer, int count)
224 {
225 	int rc;
226 
227 	count -= count % sf_get_frame_size(oss_sf);
228 	rc = write(oss_fd, buffer, count);
229 	return rc;
230 }
231 
oss_pause(void)232 static int oss_pause(void)
233 {
234 	if (ioctl(oss_fd, SNDCTL_DSP_POST, NULL) == -1)
235 		return -1;
236 	return 0;
237 }
238 
oss_unpause(void)239 static int oss_unpause(void)
240 {
241 	return 0;
242 }
243 
oss_buffer_space(void)244 static int oss_buffer_space(void)
245 {
246 	audio_buf_info info;
247 	int space;
248 
249 	if (ioctl(oss_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
250 		return -1;
251 	space = (info.fragments - 1) * info.fragsize;
252 	return space;
253 }
254 
op_oss_set_device(const char * val)255 static int op_oss_set_device(const char *val)
256 {
257 	free(oss_dsp_device);
258 	oss_dsp_device = xstrdup(val);
259 	return 0;
260 }
261 
op_oss_get_device(char ** val)262 static int op_oss_get_device(char **val)
263 {
264 	if (oss_dsp_device)
265 		*val = xstrdup(oss_dsp_device);
266 	return 0;
267 }
268 
269 const struct output_plugin_ops op_pcm_ops = {
270 	.init = oss_init,
271 	.exit = oss_exit,
272 	.open = oss_open,
273 	.close = oss_close,
274 	.write = oss_write,
275 	.pause = oss_pause,
276 	.unpause = oss_unpause,
277 	.buffer_space = oss_buffer_space,
278 };
279 
280 const struct output_plugin_opt op_pcm_options[] = {
281 	OPT(op_oss, device),
282 	{ NULL },
283 };
284 
285 const int op_priority = 1;
286 const unsigned op_abi_version = OP_ABI_VERSION;
287