1 /**
2 * @file oss.c Open Sound System (OSS) driver
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6 #include <re.h>
7 #include <rem.h>
8 #include <baresip.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <pthread.h>
12 #include <fcntl.h>
13 #include <sys/ioctl.h>
14 #if defined(NETBSD) || defined(OPENBSD)
15 #include <soundcard.h>
16 #elif defined (LINUX)
17 #include <linux/soundcard.h>
18 #else
19 #include <sys/soundcard.h>
20 #endif
21 #ifdef SOLARIS
22 #include <sys/filio.h>
23 #endif
24
25
26 /**
27 * @defgroup oss oss
28 *
29 * Open Sound System (OSS) audio driver module
30 *
31 *
32 * References:
33 *
34 * http://www.4front-tech.com/linux.html
35 */
36
37
38 struct ausrc_st {
39 const struct ausrc *as; /* inheritance */
40 pthread_t thread;
41 bool run;
42 int fd;
43 int16_t *sampv;
44 size_t sampc;
45 ausrc_read_h *rh;
46 ausrc_error_h *errh;
47 void *arg;
48 };
49
50 struct auplay_st {
51 const struct auplay *ap; /* inheritance */
52 pthread_t thread;
53 bool run;
54 int fd;
55 int16_t *sampv;
56 size_t sampc;
57 auplay_write_h *wh;
58 void *arg;
59 };
60
61
62 static struct ausrc *ausrc;
63 static struct auplay *auplay;
64 static char oss_dev[64] = "/dev/dsp";
65
66
67 /*
68 * Automatically calculate the fragment size depending on sampling rate
69 * and number of channels. More entries can be added to the table below.
70 *
71 * NOTE. Powermac 8200 and linux 2.4.18 gives:
72 * SNDCTL_DSP_SETFRAGMENT: Invalid argument
73 */
set_fragment(int fd,uint32_t sampc)74 static int set_fragment(int fd, uint32_t sampc)
75 {
76 static const struct {
77 uint16_t max;
78 uint16_t size;
79 } fragv[] = {
80 {10, 7}, /* 10 x 2^7 = 1280 = 4 x 320 */
81 {15, 7}, /* 15 x 2^7 = 1920 = 6 x 320 */
82 {20, 7}, /* 20 x 2^7 = 2560 = 8 x 320 */
83 {25, 7}, /* 25 x 2^7 = 3200 = 10 x 320 */
84 {15, 8}, /* 15 x 2^8 = 3840 = 12 x 320 */
85 {20, 8}, /* 20 x 2^8 = 5120 = 16 x 320 */
86 {25, 8} /* 25 x 2^8 = 6400 = 20 x 320 */
87 };
88 size_t i;
89 const uint32_t buf_size = 2 * sampc;
90
91 for (i=0; i<ARRAY_SIZE(fragv); i++) {
92 const uint16_t frag_max = fragv[i].max;
93 const uint16_t frag_size = fragv[i].size;
94 const uint32_t fragment_size = frag_max * (1<<frag_size);
95
96 if (0 == (fragment_size%buf_size)) {
97 int fragment = (frag_max<<16) | frag_size;
98
99 if (0 == ioctl(fd, SNDCTL_DSP_SETFRAGMENT,
100 &fragment)) {
101 return 0;
102 }
103 }
104 }
105
106 return ENODEV;
107 }
108
109
oss_reset(int fd,uint32_t srate,uint8_t ch,int sampc,int nonblock)110 static int oss_reset(int fd, uint32_t srate, uint8_t ch, int sampc,
111 int nonblock)
112 {
113 int format = AFMT_S16_NE; /* native endian */
114 int speed = srate;
115 int channels = ch;
116 int blocksize = 0;
117 int err;
118
119 err = set_fragment(fd, sampc);
120 if (err)
121 return err;
122
123 if (0 != ioctl(fd, FIONBIO, &nonblock))
124 return errno;
125 if (0 != ioctl(fd, SNDCTL_DSP_SETFMT, &format))
126 return errno;
127 if (0 != ioctl(fd, SNDCTL_DSP_CHANNELS, &channels))
128 return errno;
129 if (2 == channels) {
130 int stereo = 1;
131 if (0 != ioctl(fd, SNDCTL_DSP_STEREO, &stereo))
132 return errno;
133 }
134 if (0 != ioctl(fd, SNDCTL_DSP_SPEED, &speed))
135 return errno;
136
137 (void)ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &blocksize);
138
139 info("oss: init: %d Hz %d ch, blocksize=%d\n",
140 speed, channels, blocksize);
141
142 return 0;
143 }
144
145
auplay_destructor(void * arg)146 static void auplay_destructor(void *arg)
147 {
148 struct auplay_st *st = arg;
149
150 if (st->run) {
151 st->run = false;
152 pthread_join(st->thread, NULL);
153 }
154
155 if (-1 != st->fd) {
156 (void)close(st->fd);
157 }
158
159 mem_deref(st->sampv);
160 }
161
162
ausrc_destructor(void * arg)163 static void ausrc_destructor(void *arg)
164 {
165 struct ausrc_st *st = arg;
166
167 if (st->run) {
168 st->run = false;
169 pthread_join(st->thread, NULL);
170 }
171
172 if (-1 != st->fd) {
173 (void)close(st->fd);
174 }
175
176 mem_deref(st->sampv);
177 }
178
179
record_thread(void * arg)180 static void *record_thread(void *arg)
181 {
182 struct ausrc_st *st = arg;
183 int n;
184
185 while (st->run) {
186
187 n = read(st->fd, st->sampv, st->sampc*2);
188 if (n <= 0)
189 continue;
190
191 st->rh(st->sampv, n/2, st->arg);
192 }
193
194 return NULL;
195 }
196
197
play_thread(void * arg)198 static void *play_thread(void *arg)
199 {
200 struct auplay_st *st = arg;
201 int n;
202
203 while (st->run) {
204
205 st->wh(st->sampv, st->sampc, st->arg);
206
207 n = write(st->fd, st->sampv, st->sampc*2);
208 if (n < 0) {
209 warning("oss: write: %m\n", errno);
210 break;
211 }
212 }
213
214 return NULL;
215 }
216
217
src_alloc(struct ausrc_st ** stp,const struct ausrc * as,struct media_ctx ** ctx,struct ausrc_prm * prm,const char * device,ausrc_read_h * rh,ausrc_error_h * errh,void * arg)218 static int src_alloc(struct ausrc_st **stp, const struct ausrc *as,
219 struct media_ctx **ctx,
220 struct ausrc_prm *prm, const char *device,
221 ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
222 {
223 struct ausrc_st *st;
224 int err;
225
226 (void)ctx;
227 (void)errh;
228
229 if (!stp || !as || !prm || prm->fmt != AUFMT_S16LE || !rh)
230 return EINVAL;
231
232 st = mem_zalloc(sizeof(*st), ausrc_destructor);
233 if (!st)
234 return ENOMEM;
235
236 st->fd = -1;
237 st->rh = rh;
238 st->errh = errh;
239 st->arg = arg;
240
241 if (!device)
242 device = oss_dev;
243
244 st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
245
246 st->sampv = mem_alloc(2 * st->sampc, NULL);
247 if (!st->sampv) {
248 err = ENOMEM;
249 goto out;
250 }
251
252 st->fd = open(device, O_RDONLY);
253 if (st->fd < 0) {
254 err = errno;
255 goto out;
256 }
257
258 err = oss_reset(st->fd, prm->srate, prm->ch, st->sampc, 0);
259 if (err)
260 goto out;
261
262 st->as = as;
263
264 st->run = true;
265 err = pthread_create(&st->thread, NULL, record_thread, st);
266 if (err) {
267 st->run = false;
268 goto out;
269 }
270
271 out:
272 if (err)
273 mem_deref(st);
274 else
275 *stp = st;
276
277 return err;
278 }
279
280
play_alloc(struct auplay_st ** stp,const struct auplay * ap,struct auplay_prm * prm,const char * device,auplay_write_h * wh,void * arg)281 static int play_alloc(struct auplay_st **stp, const struct auplay *ap,
282 struct auplay_prm *prm, const char *device,
283 auplay_write_h *wh, void *arg)
284 {
285 struct auplay_st *st;
286 int err;
287
288 if (!stp || !ap || !prm || prm->fmt != AUFMT_S16LE || !wh)
289 return EINVAL;
290
291 st = mem_zalloc(sizeof(*st), auplay_destructor);
292 if (!st)
293 return ENOMEM;
294
295 st->fd = -1;
296 st->wh = wh;
297 st->arg = arg;
298
299 if (!device)
300 device = oss_dev;
301
302 st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
303
304 st->sampv = mem_alloc(st->sampc * 2, NULL);
305 if (!st->sampv) {
306 err = ENOMEM;
307 goto out;
308 }
309
310 st->fd = open(device, O_WRONLY);
311 if (st->fd < 0) {
312 err = errno;
313 goto out;
314 }
315
316 err = oss_reset(st->fd, prm->srate, prm->ch, st->sampc, 0);
317 if (err)
318 goto out;
319
320 st->ap = ap;
321
322 st->run = true;
323 err = pthread_create(&st->thread, NULL, play_thread, st);
324 if (err) {
325 st->run = false;
326 goto out;
327 }
328
329 out:
330 if (err)
331 mem_deref(st);
332 else
333 *stp = st;
334
335 return err;
336 }
337
338
module_init(void)339 static int module_init(void)
340 {
341 int err;
342
343 err = ausrc_register(&ausrc, baresip_ausrcl(), "oss", src_alloc);
344 err |= auplay_register(&auplay, baresip_auplayl(), "oss", play_alloc);
345
346 return err;
347 }
348
349
module_close(void)350 static int module_close(void)
351 {
352 ausrc = mem_deref(ausrc);
353 auplay = mem_deref(auplay);
354
355 return 0;
356 }
357
358
359 EXPORT_SYM const struct mod_export DECL_EXPORTS(oss) = {
360 "oss",
361 "audio",
362 module_init,
363 module_close,
364 };
365