]> code.delx.au - pulseaudio/blob - src/module-oss.c
add name registrar
[pulseaudio] / src / module-oss.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
13 #include "iochannel.h"
14 #include "sink.h"
15 #include "source.h"
16 #include "module.h"
17 #include "oss-util.h"
18 #include "sample-util.h"
19
20 struct userdata {
21 struct sink *sink;
22 struct source *source;
23 struct iochannel *io;
24 struct core *core;
25
26 struct memchunk memchunk, silence;
27
28 uint32_t in_fragment_size, out_fragment_size, sample_size;
29
30 int fd;
31 };
32
33 static void do_write(struct userdata *u) {
34 struct memchunk *memchunk;
35 ssize_t r;
36 assert(u);
37
38 if (!u->sink || !iochannel_is_writable(u->io))
39 return;
40
41 if (!u->memchunk.length) {
42 if (sink_render(u->sink, u->out_fragment_size, &u->memchunk) < 0)
43 memchunk = &u->silence;
44 else
45 memchunk = &u->memchunk;
46 }
47
48 assert(memchunk->memblock && memchunk->length);
49
50 if ((r = iochannel_write(u->io, memchunk->memblock->data + memchunk->index, memchunk->length)) < 0) {
51 fprintf(stderr, "write() failed: %s\n", strerror(errno));
52 return;
53 }
54
55 if (memchunk == &u->silence)
56 assert(r % u->sample_size == 0);
57 else {
58 u->memchunk.index += r;
59 u->memchunk.length -= r;
60
61 if (u->memchunk.length <= 0) {
62 memblock_unref(u->memchunk.memblock);
63 u->memchunk.memblock = NULL;
64 }
65 }
66 }
67
68 static void do_read(struct userdata *u) {
69 struct memchunk memchunk;
70 ssize_t r;
71 assert(u);
72
73 if (!u->source || !iochannel_is_readable(u->io))
74 return;
75
76 memchunk.memblock = memblock_new(u->in_fragment_size);
77 assert(memchunk.memblock);
78 if ((r = iochannel_read(u->io, memchunk.memblock->data, memchunk.memblock->length)) < 0) {
79 memblock_unref(memchunk.memblock);
80 fprintf(stderr, "read() failed: %s\n", strerror(errno));
81 return;
82 }
83
84 assert(r <= (ssize_t) memchunk.memblock->length);
85 memchunk.length = memchunk.memblock->length = r;
86 memchunk.index = 0;
87
88 source_post(u->source, &memchunk);
89 memblock_unref(memchunk.memblock);
90 };
91
92 static void io_callback(struct iochannel *io, void*userdata) {
93 struct userdata *u = userdata;
94 assert(u);
95 do_write(u);
96 do_read(u);
97 }
98
99 static uint32_t sink_get_latency_cb(struct sink *s) {
100 int arg;
101 struct userdata *u = s->userdata;
102 assert(s && u && u->sink);
103
104 if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
105 fprintf(stderr, "module-oss: device doesn't support SNDCTL_DSP_GETODELAY.\n");
106 s->get_latency = NULL;
107 return 0;
108 }
109
110 return pa_samples_usec(arg, &s->sample_spec);
111 }
112
113 int module_init(struct core *c, struct module*m) {
114 struct audio_buf_info info;
115 struct userdata *u = NULL;
116 char *p;
117 int fd = -1;
118 int frag_size, in_frag_size, out_frag_size;
119 int mode;
120 struct pa_sample_spec ss;
121 assert(c && m);
122
123 p = m->argument ? m->argument : "/dev/dsp";
124 if ((fd = open(p, (mode = O_RDWR)|O_NDELAY)) >= 0) {
125 int caps;
126
127 ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
128
129 if (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) < 0) {
130 fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno));
131 goto fail;
132 }
133
134 if (!(caps & DSP_CAP_DUPLEX)) {
135 close(fd);
136 fd = -1;
137 }
138 }
139
140 if (fd < 0) {
141 if ((fd = open(p, (mode = O_WRONLY)|O_NDELAY)) < 0) {
142 if ((fd = open(p, (mode = O_RDONLY)|O_NDELAY)) < 0) {
143 fprintf(stderr, "open('%s'): %s\n", p, strerror(errno));
144 goto fail;
145 }
146 }
147 }
148
149 fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
150
151 frag_size = ((int) 12 << 16) | 10; /* nfrags = 12; frag_size = 2^10 */
152 if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag_size) < 0) {
153 fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno));
154 goto fail;
155 }
156
157 if (oss_auto_format(fd, &ss) < 0)
158 goto fail;
159
160 if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
161 fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno));
162 goto fail;
163 }
164 assert(frag_size);
165 in_frag_size = out_frag_size = frag_size;
166
167 if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
168 fprintf(stderr, "module-oss: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
169 in_frag_size = info.fragsize;
170 }
171
172 if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
173 fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize);
174 out_frag_size = info.fragsize;
175 }
176
177 u = malloc(sizeof(struct userdata));
178 assert(u);
179
180 u->core = c;
181
182 if (mode != O_RDONLY) {
183 u->sink = sink_new(c, "dsp", 0, &ss);
184 assert(u->sink);
185 u->sink->get_latency = sink_get_latency_cb;
186 u->sink->userdata = u;
187 } else
188 u->sink = NULL;
189
190 if (mode != O_WRONLY) {
191 u->source = source_new(c, "dsp", 0, &ss);
192 assert(u->source);
193 u->source->userdata = u;
194 } else
195 u->source = NULL;
196
197 assert(u->source || u->sink);
198
199 u->io = iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0);
200 assert(u->io);
201 iochannel_set_callback(u->io, io_callback, u);
202 u->fd = fd;
203
204 u->memchunk.memblock = NULL;
205 u->memchunk.length = 0;
206 u->sample_size = pa_sample_size(&ss);
207
208 u->out_fragment_size = out_frag_size;
209 u->in_fragment_size = in_frag_size;
210 u->silence.memblock = memblock_new(u->silence.length = u->out_fragment_size);
211 assert(u->silence.memblock);
212 silence_memblock(u->silence.memblock, &ss);
213 u->silence.index = 0;
214
215 m->userdata = u;
216
217 return 0;
218
219 fail:
220 if (fd >= 0)
221 close(fd);
222
223 return -1;
224 }
225
226 void module_done(struct core *c, struct module*m) {
227 struct userdata *u;
228 assert(c && m);
229
230 u = m->userdata;
231 assert(u);
232
233 if (u->memchunk.memblock)
234 memblock_unref(u->memchunk.memblock);
235 if (u->silence.memblock)
236 memblock_unref(u->silence.memblock);
237
238 if (u->sink)
239 sink_free(u->sink);
240 if (u->source)
241 source_free(u->source);
242 iochannel_free(u->io);
243 free(u);
244 }