]> code.delx.au - pulseaudio/blob - src/module-oss-mmap.c
add name registrar
[pulseaudio] / src / module-oss-mmap.c
1 #include <sys/soundcard.h>
2 #include <sys/ioctl.h>
3 #include <stdlib.h>
4 #include <sys/stat.h>
5 #include <stdio.h>
6 #include <assert.h>
7 #include <errno.h>
8 #include <string.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <limits.h>
12 #include <sys/mman.h>
13
14 #include "iochannel.h"
15 #include "sink.h"
16 #include "source.h"
17 #include "module.h"
18 #include "oss-util.h"
19 #include "sample-util.h"
20
21 struct userdata {
22 struct sink *sink;
23 struct source *source;
24 struct core *core;
25 struct pa_sample_spec sample_spec;
26
27 size_t in_fragment_size, out_fragment_size, in_fragments, out_fragments, out_fill;
28
29 int fd;
30
31 void *in_mmap, *out_mmap;
32 size_t in_mmap_length, out_mmap_length;
33
34 struct mainloop_source *mainloop_source;
35
36 struct memblock **in_memblocks, **out_memblocks;
37 unsigned out_current, in_current;
38 };
39
40 void module_done(struct core *c, struct module*m);
41
42 static void out_clear_memblocks(struct userdata*u, unsigned n) {
43 unsigned i = u->out_current;
44 assert(u && u->out_memblocks);
45
46 if (n > u->out_fragments)
47 n = u->out_fragments;
48
49 while (n > 0) {
50 if (u->out_memblocks[i]) {
51 memblock_unref_fixed(u->out_memblocks[i]);
52 u->out_memblocks[i] = NULL;
53 }
54
55 i++;
56 while (i >= u->out_fragments)
57 i -= u->out_fragments;
58
59 n--;
60 }
61 }
62
63 static void out_fill_memblocks(struct userdata *u, unsigned n) {
64 assert(u && u->out_memblocks);
65
66 while (n > 0) {
67 struct memchunk chunk;
68
69 if (!u->out_memblocks[u->out_current]) {
70
71 u->out_memblocks[u->out_current] = chunk.memblock = memblock_new_fixed(u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size);
72 chunk.length = chunk.memblock->length;
73 chunk.index = 0;
74
75 sink_render_into_full(u->sink, &chunk);
76 }
77
78 u->out_current++;
79 while (u->out_current >= u->out_fragments)
80 u->out_current -= u->out_fragments;
81
82 n--;
83 }
84 }
85
86 static void do_write(struct userdata *u) {
87 struct count_info info;
88 assert(u && u->sink);
89
90 if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) {
91 fprintf(stderr, "SNDCTL_DSP_GETOPTR: %s\n", strerror(errno));
92 return;
93 }
94
95 u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size);
96
97 if (!info.blocks)
98 return;
99
100 out_clear_memblocks(u, info.blocks);
101 out_fill_memblocks(u, info.blocks);
102
103 }
104
105 static void in_post_memblocks(struct userdata *u, unsigned n) {
106 assert(u && u->in_memblocks);
107
108 while (n > 0) {
109 struct memchunk chunk;
110
111 if (!u->in_memblocks[u->in_current]) {
112 u->in_memblocks[u->in_current] = chunk.memblock = memblock_new_fixed(u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size);
113 chunk.length = chunk.memblock->length;
114 chunk.index = 0;
115
116 source_post(u->source, &chunk);
117 }
118
119 u->in_current++;
120 while (u->in_current >= u->in_fragments)
121 u->in_current -= u->in_fragments;
122
123 n--;
124 }
125 }
126
127 static void in_clear_memblocks(struct userdata*u, unsigned n) {
128 unsigned i = u->in_current;
129 assert(u && u->in_memblocks);
130
131 if (n > u->in_fragments)
132 n = u->in_fragments;
133
134 while (n > 0) {
135 if (u->in_memblocks[i]) {
136 memblock_unref_fixed(u->in_memblocks[i]);
137 u->in_memblocks[i] = NULL;
138 }
139
140 i++;
141 while (i >= u->in_fragments)
142 i -= u->in_fragments;
143
144 n--;
145 }
146 }
147
148 static void do_read(struct userdata *u) {
149 struct count_info info;
150 assert(u && u->source);
151
152 if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) {
153 fprintf(stderr, "SNDCTL_DSP_GETIPTR: %s\n", strerror(errno));
154 return;
155 }
156
157 if (!info.blocks)
158 return;
159
160 in_post_memblocks(u, info.blocks);
161 in_clear_memblocks(u, u->in_fragments/2);
162 };
163
164 static void io_callback(struct pa_mainloop_api *m, void *id, int fd, enum pa_mainloop_api_io_events events, void *userdata) {
165 struct userdata *u = userdata;
166
167 assert (u && u->core->mainloop == m && u->mainloop_source == id);
168
169 if (events & PA_MAINLOOP_API_IO_EVENT_INPUT)
170 do_read(u);
171 if (events & PA_MAINLOOP_API_IO_EVENT_OUTPUT)
172 do_write(u);
173 }
174
175 static uint32_t sink_get_latency_cb(struct sink *s) {
176 struct userdata *u = s->userdata;
177 assert(s && u);
178
179 do_write(u);
180 return pa_samples_usec(u->out_fill, &s->sample_spec);
181 }
182
183 int module_init(struct core *c, struct module*m) {
184 struct audio_buf_info info;
185 struct userdata *u = NULL;
186 char *p;
187 int frag_size;
188 int mode, caps, caps_read = 0;
189 int enable_bits = 0, zero = 0;
190 assert(c && m);
191
192 m->userdata = u = malloc(sizeof(struct userdata));
193 assert(u);
194 memset(u, 0, sizeof(struct userdata));
195 u->fd = -1;
196 u->core = c;
197
198 p = m->argument ? m->argument : "/dev/dsp";
199 if ((u->fd = open(p, (mode = O_RDWR)|O_NDELAY)) >= 0) {
200 ioctl(u->fd, SNDCTL_DSP_SETDUPLEX, 0);
201
202 if (ioctl(u->fd, SNDCTL_DSP_GETCAPS, &caps) < 0) {
203 fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
204 goto fail;
205 }
206
207 if (!(caps & DSP_CAP_DUPLEX)) {
208 close(u->fd);
209 u->fd = -1;
210 } else
211 caps_read = 1;
212 }
213
214 if (u->fd < 0) {
215 if ((u->fd = open(p, (mode = O_WRONLY)|O_NDELAY)) < 0) {
216 if ((u->fd = open(p, (mode = O_RDONLY)|O_NDELAY)) < 0) {
217 fprintf(stderr, "open('%s'): %s\n", p, strerror(errno));
218 goto fail;
219 }
220 }
221 }
222
223 if (!caps_read) {
224 if (ioctl(u->fd, SNDCTL_DSP_GETCAPS, &caps) < 0) {
225 fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
226 goto fail;
227 }
228 }
229
230 if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) {
231 fprintf(stderr, "OSS device not mmap capable.\n");
232 goto fail;
233 }
234
235 fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
236
237 frag_size = ((int) 12 << 16) | 10; /* nfrags = 12; frag_size = 2^10 */
238 if (ioctl(u->fd, SNDCTL_DSP_SETFRAGMENT, &frag_size) < 0) {
239 fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
240 goto fail;
241 }
242
243 if (oss_auto_format(u->fd, &u->sample_spec) < 0)
244 goto fail;
245
246 if (mode != O_WRONLY) {
247 if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
248 fprintf(stderr, "SNDCTL_DSP_GETISPACE: %s\n", strerror(errno));
249 goto fail;
250 }
251
252 fprintf(stderr, "module-oss-mmap: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
253 u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal);
254
255 if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
256 if (mode == O_RDWR) {
257 fprintf(stderr, "module-oss-mmap: mmap failed for input. Changing to O_WRONLY mode.\n");
258 mode = O_WRONLY;
259 } else {
260 fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno));
261 goto fail;
262 }
263 } else {
264
265 u->source = source_new(c, "dsp", 0, &u->sample_spec);
266 assert(u->source);
267 u->source->userdata = u;
268
269 u->in_memblocks = malloc(sizeof(struct memblock *)*u->in_fragments);
270 memset(u->in_memblocks, 0, sizeof(struct memblock *)*u->in_fragments);
271
272 enable_bits |= PCM_ENABLE_INPUT;
273 }
274 }
275
276 if (m != O_RDONLY) {
277 if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
278 fprintf(stderr, "SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno));
279 goto fail;
280 }
281
282 fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
283 u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal);
284
285 if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) {
286 if (mode == O_RDWR) {
287 fprintf(stderr, "module-oss-mmap: mmap filed for output. Changing to O_RDONLY mode.\n");
288 mode = O_RDONLY;
289 } else {
290 fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno));
291 goto fail;
292 }
293 } else {
294 silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec);
295
296 u->sink = sink_new(c, "dsp", 0, &u->sample_spec);
297 assert(u->sink);
298 u->sink->get_latency = sink_get_latency_cb;
299 u->sink->userdata = u;
300
301 u->out_memblocks = malloc(sizeof(struct memblock *)*u->out_fragments);
302 memset(u->out_memblocks, 0, sizeof(struct memblock *)*u->out_fragments);
303
304 enable_bits |= PCM_ENABLE_OUTPUT;
305 }
306 }
307
308 zero = 0;
309 if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) {
310 fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
311 goto fail;
312 }
313
314 if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) {
315 fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno));
316 goto fail;
317 }
318
319 assert(u->source || u->sink);
320
321 u->mainloop_source = c->mainloop->source_io(c->mainloop, u->fd, (u->source ? PA_MAINLOOP_API_IO_EVENT_INPUT : 0) | (u->sink ? PA_MAINLOOP_API_IO_EVENT_OUTPUT : 0), io_callback, u);
322 assert(u->mainloop_source);
323
324 return 0;
325
326 fail:
327 module_done(c, m);
328
329 return -1;
330 }
331
332 void module_done(struct core *c, struct module*m) {
333 struct userdata *u;
334 assert(c && m);
335
336 u = m->userdata;
337 assert(u);
338
339 if (u->out_memblocks) {
340 out_clear_memblocks(u, u->out_fragments);
341 free(u->out_memblocks);
342 }
343
344 if (u->in_memblocks) {
345 in_clear_memblocks(u, u->in_fragments);
346 free(u->in_memblocks);
347 }
348
349 if (u->in_mmap && u->in_mmap != MAP_FAILED)
350 munmap(u->in_mmap, u->in_mmap_length);
351
352 if (u->out_mmap && u->out_mmap != MAP_FAILED)
353 munmap(u->out_mmap, u->out_mmap_length);
354
355 if (u->sink)
356 sink_free(u->sink);
357
358 if (u->source)
359 source_free(u->source);
360
361 if (u->mainloop_source)
362 u->core->mainloop->cancel_io(u->core->mainloop, u->mainloop_source);
363
364 if (u->fd >= 0)
365 close(u->fd);
366
367 free(u);
368 }