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