]> code.delx.au - pulseaudio/blob - src/modules/alsa-util.c
Handle ALSA file descriptors more correctly. This means a bit more overhead,
[pulseaudio] / src / modules / alsa-util.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
6 polypaudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 polypaudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <sys/types.h>
27 #include <asoundlib.h>
28
29 #include <polyp/sample.h>
30 #include <polypcore/xmalloc.h>
31 #include <polypcore/log.h>
32
33 #include "alsa-util.h"
34
35 struct pa_alsa_fdlist {
36 int num_fds;
37 struct pollfd *fds;
38 /* This is a temporary buffer used to avoid lots of mallocs */
39 struct pollfd *work_fds;
40
41 snd_pcm_t *pcm;
42
43 pa_mainloop_api *m;
44 pa_defer_event *defer;
45 pa_io_event **ios;
46
47 int polled;
48
49 void (*cb)(void *userdata);
50 void *userdata;
51 };
52
53 static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) {
54 struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata;
55 int err, i;
56 unsigned short revents;
57
58 assert(a && fdl && fdl->pcm);
59
60 if (fdl->polled)
61 return;
62
63 fdl->polled = 1;
64
65 memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds);
66
67 for (i = 0;i < fdl->num_fds;i++) {
68 if (e == fdl->ios[i]) {
69 if (events & PA_IO_EVENT_INPUT)
70 fdl->work_fds[i].revents |= POLLIN;
71 if (events & PA_IO_EVENT_OUTPUT)
72 fdl->work_fds[i].revents |= POLLOUT;
73 if (events & PA_IO_EVENT_ERROR)
74 fdl->work_fds[i].revents |= POLLERR;
75 if (events & PA_IO_EVENT_HANGUP)
76 fdl->work_fds[i].revents |= POLLHUP;
77 break;
78 }
79 }
80
81 assert(i != fdl->num_fds);
82
83 err = snd_pcm_poll_descriptors_revents(fdl->pcm, fdl->work_fds, fdl->num_fds, &revents);
84 if (err < 0) {
85 pa_log_error(__FILE__": Unable to get poll revent: %s",
86 snd_strerror(err));
87 a->defer_enable(fdl->defer, 0);
88 return;
89 }
90
91 if (revents)
92 fdl->cb(fdl->userdata);
93 }
94
95 static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) {
96 struct pa_alsa_fdlist *fdl = (struct pa_alsa_fdlist*)userdata;
97 int num_fds, i, err;
98 struct pollfd *temp;
99
100 assert(a && fdl && fdl->pcm);
101
102 num_fds = snd_pcm_poll_descriptors_count(fdl->pcm);
103 assert(num_fds > 0);
104
105 if (num_fds != fdl->num_fds) {
106 if (fdl->fds)
107 pa_xfree(fdl->fds);
108 if (fdl->work_fds)
109 pa_xfree(fdl->work_fds);
110 fdl->fds = pa_xmalloc(sizeof(struct pollfd) * num_fds);
111 fdl->work_fds = pa_xmalloc(sizeof(struct pollfd) * num_fds);
112 }
113
114 memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds);
115 err = snd_pcm_poll_descriptors(fdl->pcm, fdl->work_fds, num_fds);
116 if (err < 0) {
117 pa_log_error(__FILE__": Unable to get poll descriptors: %s",
118 snd_strerror(err));
119 a->defer_enable(fdl->defer, 0);
120 return;
121 }
122
123 fdl->polled = 0;
124
125 if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0)
126 return;
127
128 if (fdl->ios) {
129 for (i = 0;i < fdl->num_fds;i++)
130 a->io_free(fdl->ios[i]);
131 if (num_fds != fdl->num_fds) {
132 pa_xfree(fdl->ios);
133 fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds);
134 assert(fdl->ios);
135 }
136 } else {
137 fdl->ios = pa_xmalloc(sizeof(pa_io_event*) * num_fds);
138 assert(fdl->ios);
139 }
140
141 /* Swap pointers */
142 temp = fdl->work_fds;
143 fdl->work_fds = fdl->fds;
144 fdl->fds = temp;
145
146 fdl->num_fds = num_fds;
147
148 for (i = 0;i < num_fds;i++) {
149 fdl->ios[i] = a->io_new(a, fdl->fds[i].fd,
150 ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) |
151 ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0),
152 io_cb, fdl);
153 assert(fdl->ios[i]);
154 }
155 }
156
157 struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) {
158 struct pa_alsa_fdlist *fdl;
159
160 fdl = pa_xmalloc(sizeof(struct pa_alsa_fdlist));
161 assert(fdl);
162
163 fdl->num_fds = 0;
164 fdl->fds = NULL;
165 fdl->work_fds = NULL;
166
167 fdl->pcm = NULL;
168
169 fdl->m = NULL;
170 fdl->defer = NULL;
171 fdl->ios = NULL;
172
173 fdl->polled = 0;
174
175 return fdl;
176 }
177
178 void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) {
179 assert(fdl);
180
181 if (fdl->defer) {
182 assert(fdl->m);
183 fdl->m->defer_free(fdl->defer);
184 }
185
186 if (fdl->ios) {
187 int i;
188 assert(fdl->m);
189 for (i = 0;i < fdl->num_fds;i++)
190 fdl->m->io_free(fdl->ios[0]);
191 pa_xfree(fdl->ios);
192 }
193
194 if (fdl->fds)
195 pa_xfree(fdl->fds);
196 if (fdl->work_fds)
197 pa_xfree(fdl->work_fds);
198
199 pa_xfree(fdl);
200 }
201
202 int pa_alsa_fdlist_init_pcm(struct pa_alsa_fdlist *fdl, snd_pcm_t *pcm_handle, pa_mainloop_api* m, void (*cb)(void *userdata), void *userdata) {
203 assert(fdl && pcm_handle && m && !fdl->m && cb);
204
205 fdl->pcm = pcm_handle;
206 fdl->m = m;
207
208 fdl->defer = m->defer_new(m, defer_cb, fdl);
209 assert(fdl->defer);
210
211 fdl->cb = cb;
212 fdl->userdata = userdata;
213
214 return 0;
215 }
216
217 /* Set the hardware parameters of the given ALSA device. Returns the
218 * selected fragment settings in *period and *period_size */
219 int pa_alsa_set_hw_params(snd_pcm_t *pcm_handle, const pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size) {
220 int ret = -1;
221 snd_pcm_uframes_t buffer_size;
222 snd_pcm_hw_params_t *hwparams = NULL;
223 unsigned int r = ss->rate;
224
225 static const snd_pcm_format_t format_trans[] = {
226 [PA_SAMPLE_U8] = SND_PCM_FORMAT_U8,
227 [PA_SAMPLE_ALAW] = SND_PCM_FORMAT_A_LAW,
228 [PA_SAMPLE_ULAW] = SND_PCM_FORMAT_MU_LAW,
229 [PA_SAMPLE_S16LE] = SND_PCM_FORMAT_S16_LE,
230 [PA_SAMPLE_S16BE] = SND_PCM_FORMAT_S16_BE,
231 [PA_SAMPLE_FLOAT32LE] = SND_PCM_FORMAT_FLOAT_LE,
232 [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
233 };
234 assert(pcm_handle && ss && periods && period_size);
235
236 if (snd_pcm_hw_params_malloc(&hwparams) < 0 ||
237 snd_pcm_hw_params_any(pcm_handle, hwparams) < 0 ||
238 snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 ||
239 snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[ss->format]) < 0 ||
240 snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL) < 0 ||
241 snd_pcm_hw_params_set_channels(pcm_handle, hwparams, ss->channels) < 0 ||
242 (*periods > 0 && snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, periods, NULL) < 0) ||
243 (*period_size > 0 && snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL) < 0) ||
244 snd_pcm_hw_params(pcm_handle, hwparams) < 0)
245 goto finish;
246
247 if (ss->rate != r)
248 pa_log_info(__FILE__": device doesn't support %u Hz, changed to %u Hz.", ss->rate, r);
249
250 if (snd_pcm_prepare(pcm_handle) < 0)
251 goto finish;
252
253 if (snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size) < 0 ||
254 snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL) < 0)
255 goto finish;
256
257 assert(buffer_size > 0);
258 assert(*period_size > 0);
259 *periods = buffer_size / *period_size;
260 assert(*periods > 0);
261
262 ret = 0;
263
264 finish:
265 if (hwparams)
266 snd_pcm_hw_params_free(hwparams);
267
268 return ret;
269 }
270
271 int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
272 int err;
273
274 assert(mixer && dev);
275
276 if ((err = snd_mixer_attach(mixer, dev)) < 0) {
277 pa_log_warn(__FILE__": Unable to attach to mixer %s: %s", dev, snd_strerror(err));
278 return -1;
279 }
280
281 if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
282 pa_log_warn(__FILE__": Unable to register mixer: %s", snd_strerror(err));
283 return -1;
284 }
285
286 if ((err = snd_mixer_load(mixer)) < 0) {
287 pa_log_warn(__FILE__": Unable to load mixer: %s", snd_strerror(err));
288 return -1;
289 }
290
291 return 0;
292 }
293
294 snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name) {
295 snd_mixer_elem_t *elem;
296 snd_mixer_selem_id_t *sid;
297 snd_mixer_selem_id_alloca(&sid);
298
299 assert(mixer && name);
300
301 snd_mixer_selem_id_set_name(sid, name);
302
303 elem = snd_mixer_find_selem(mixer, sid);
304 if (!elem)
305 pa_log_warn(__FILE__": Cannot find mixer control %s", snd_mixer_selem_id_get_name(sid));
306
307 return elem;
308 }