]> code.delx.au - pulseaudio/blob - src/pulsecore/pdispatch.c
remaining s/assert/pa_assert/ and refcnt.h modernizations
[pulseaudio] / src / pulsecore / pdispatch.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2004-2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
8
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as
11 published by the Free Software Foundation; either version 2.1 of the
12 License, or (at your option) any later version.
13
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 USA.
23 ***/
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include <pulse/timeval.h>
33 #include <pulse/xmalloc.h>
34
35 #include <pulsecore/native-common.h>
36 #include <pulsecore/llist.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/core-util.h>
39 #include <pulsecore/macro.h>
40 #include <pulsecore/refcnt.h>
41
42 #include "pdispatch.h"
43
44 /*#define DEBUG_OPCODES */
45
46 #ifdef DEBUG_OPCODES
47
48 static const char *command_names[PA_COMMAND_MAX] = {
49 [PA_COMMAND_ERROR] = "ERROR",
50 [PA_COMMAND_TIMEOUT] = "TIMEOUT",
51 [PA_COMMAND_REPLY] = "REPLY",
52 [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
53 [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
54 [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
55 [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
56 [PA_COMMAND_AUTH] = "AUTH",
57 [PA_COMMAND_REQUEST] = "REQUEST",
58 [PA_COMMAND_EXIT] = "EXIT",
59 [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
60 [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
61 [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
62 [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
63 [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
64 [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
65 [PA_COMMAND_STAT] = "STAT",
66 [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY",
67 [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
68 [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
69 [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
70 [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
71 [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
72 [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
73 [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
74 [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
75 [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
76 [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
77 [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
78 [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
79 [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
80 [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
81 [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
82 [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
83 [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
84 [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
85 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
86 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
87 [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
88 [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
89 [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
90 [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
91 [PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLME",
92 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
93 [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
94 [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
95 [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO",
96 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST",
97 };
98
99 #endif
100
101 struct reply_info {
102 pa_pdispatch *pdispatch;
103 PA_LLIST_FIELDS(struct reply_info);
104 pa_pdispatch_cb_t callback;
105 void *userdata;
106 pa_free_cb_t free_cb;
107 uint32_t tag;
108 pa_time_event *time_event;
109 };
110
111 struct pa_pdispatch {
112 PA_REFCNT_DECLARE;
113 pa_mainloop_api *mainloop;
114 const pa_pdispatch_cb_t *callback_table;
115 unsigned n_commands;
116 PA_LLIST_HEAD(struct reply_info, replies);
117 pa_pdispatch_drain_callback drain_callback;
118 void *drain_userdata;
119 const pa_creds *creds;
120 };
121
122 static void reply_info_free(struct reply_info *r) {
123 pa_assert(r);
124 pa_assert(r->pdispatch);
125 pa_assert(r->pdispatch->mainloop);
126
127 if (r->time_event)
128 r->pdispatch->mainloop->time_free(r->time_event);
129
130 PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
131
132 pa_xfree(r);
133 }
134
135 pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
136 pa_pdispatch *pd;
137 pa_assert(mainloop);
138
139 pa_assert((entries && table) || (!entries && !table));
140
141 pd = pa_xnew(pa_pdispatch, 1);
142 PA_REFCNT_INIT(pd);
143 pd->mainloop = mainloop;
144 pd->callback_table = table;
145 pd->n_commands = entries;
146 PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
147 pd->drain_callback = NULL;
148 pd->drain_userdata = NULL;
149 pd->creds = NULL;
150
151 return pd;
152 }
153
154 static void pdispatch_free(pa_pdispatch *pd) {
155 pa_assert(pd);
156
157 while (pd->replies) {
158 if (pd->replies->free_cb)
159 pd->replies->free_cb(pd->replies->userdata);
160
161 reply_info_free(pd->replies);
162 }
163
164 pa_xfree(pd);
165 }
166
167 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
168 pa_pdispatch_cb_t callback;
169 void *userdata;
170 uint32_t tag;
171 pa_assert(r);
172
173 pa_pdispatch_ref(pd);
174
175 callback = r->callback;
176 userdata = r->userdata;
177 tag = r->tag;
178
179 reply_info_free(r);
180
181 callback(pd, command, tag, ts, userdata);
182
183 if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
184 pd->drain_callback(pd, pd->drain_userdata);
185
186 pa_pdispatch_unref(pd);
187 }
188
189 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, void *userdata) {
190 uint32_t tag, command;
191 pa_tagstruct *ts = NULL;
192 int ret = -1;
193
194 pa_assert(pd);
195 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
196 pa_assert(packet);
197 pa_assert(PA_REFCNT_VALUE(packet) >= 1);
198 pa_assert(packet->data);
199
200 pa_pdispatch_ref(pd);
201
202 if (packet->length <= 8)
203 goto finish;
204
205 ts = pa_tagstruct_new(packet->data, packet->length);
206
207 if (pa_tagstruct_getu32(ts, &command) < 0 ||
208 pa_tagstruct_getu32(ts, &tag) < 0)
209 goto finish;
210
211 #ifdef DEBUG_OPCODES
212 {
213 char t[256];
214 char const *p;
215 if (!(p = command_names[command]))
216 pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
217
218 pa_log("Recieved opcode <%s>", p);
219 }
220 #endif
221
222 pd->creds = creds;
223
224 if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
225 struct reply_info *r;
226
227 for (r = pd->replies; r; r = r->next)
228 if (r->tag == tag)
229 break;
230
231 if (r)
232 run_action(pd, r, command, ts);
233
234 } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
235 const pa_pdispatch_cb_t *c = pd->callback_table+command;
236
237 (*c)(pd, command, tag, ts, userdata);
238 } else {
239 pa_log("Recieved unsupported command %u", command);
240 goto finish;
241 }
242
243 ret = 0;
244
245 finish:
246 pd->creds = NULL;
247
248 if (ts)
249 pa_tagstruct_free(ts);
250
251 pa_pdispatch_unref(pd);
252
253 return ret;
254 }
255
256 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
257 struct reply_info*r = userdata;
258
259 pa_assert(r);
260 pa_assert(r->time_event == e);
261 pa_assert(r->pdispatch);
262 pa_assert(r->pdispatch->mainloop == m);
263 pa_assert(r->callback);
264
265 run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
266 }
267
268 void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
269 struct reply_info *r;
270 struct timeval tv;
271
272 pa_assert(pd);
273 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
274 pa_assert(cb);
275
276 r = pa_xnew(struct reply_info, 1);
277 r->pdispatch = pd;
278 r->callback = cb;
279 r->userdata = userdata;
280 r->free_cb = free_cb;
281 r->tag = tag;
282
283 pa_gettimeofday(&tv);
284 tv.tv_sec += timeout;
285
286 pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r));
287
288 PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
289 }
290
291 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
292 pa_assert(pd);
293 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
294
295 return !!pd->replies;
296 }
297
298 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
299 pa_assert(pd);
300 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
301 pa_assert(!cb || pa_pdispatch_is_pending(pd));
302
303 pd->drain_callback = cb;
304 pd->drain_userdata = userdata;
305 }
306
307 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
308 struct reply_info *r, *n;
309
310 pa_assert(pd);
311 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
312
313 for (r = pd->replies; r; r = n) {
314 n = r->next;
315
316 if (r->userdata == userdata)
317 reply_info_free(r);
318 }
319 }
320
321 void pa_pdispatch_unref(pa_pdispatch *pd) {
322 pa_assert(pd);
323 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
324
325 if (PA_REFCNT_DEC(pd) <= 0)
326 pdispatch_free(pd);
327 }
328
329 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
330 pa_assert(pd);
331 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
332
333 PA_REFCNT_INC(pd);
334 return pd;
335 }
336
337 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
338 pa_assert(pd);
339 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
340
341 return pd->creds;
342 }