Add copyright notices to all relevant files. (based on svn log)
[profile/ivi/pulseaudio.git] / 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 #include <assert.h>
32
33 #include <pulse/timeval.h>
34 #include <pulse/xmalloc.h>
35
36 #include <pulsecore/native-common.h>
37 #include <pulsecore/llist.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/core-util.h>
40
41 #include "pdispatch.h"
42
43 /*#define DEBUG_OPCODES */
44
45 #ifdef DEBUG_OPCODES
46
47 static const char *command_names[PA_COMMAND_MAX] = {
48     [PA_COMMAND_ERROR] = "ERROR",
49     [PA_COMMAND_TIMEOUT] = "TIMEOUT",
50     [PA_COMMAND_REPLY] = "REPLY",
51     [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
52     [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
53     [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
54     [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
55     [PA_COMMAND_AUTH] = "AUTH",
56     [PA_COMMAND_REQUEST] = "REQUEST",
57     [PA_COMMAND_EXIT] = "EXIT",
58     [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
59     [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
60     [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
61     [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
62     [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
63     [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
64     [PA_COMMAND_STAT] = "STAT",
65     [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY",
66     [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
67     [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
68     [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
69     [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
70     [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
71     [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
72     [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
73     [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
74     [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
75     [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
76     [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
77     [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
78     [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
79     [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
80     [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
81     [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
82     [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
83     [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
84     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
85     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
86     [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
87     [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
88     [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
89     [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
90     [PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLME",
91     [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
92     [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
93     [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
94     [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO",
95     [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST",
96 };
97
98 #endif
99
100 struct reply_info {
101     pa_pdispatch *pdispatch;
102     PA_LLIST_FIELDS(struct reply_info);
103     pa_pdispatch_cb_t callback;
104     void *userdata;
105     pa_free_cb_t free_cb;
106     uint32_t tag;
107     pa_time_event *time_event;
108 };
109
110 struct pa_pdispatch {
111     int ref;
112     pa_mainloop_api *mainloop;
113     const pa_pdispatch_cb_t *callback_table;
114     unsigned n_commands;
115     PA_LLIST_HEAD(struct reply_info, replies);
116     pa_pdispatch_drain_callback drain_callback;
117     void *drain_userdata;
118     const pa_creds *creds;
119 };
120
121 static void reply_info_free(struct reply_info *r) {
122     assert(r && r->pdispatch && r->pdispatch->mainloop);
123
124     if (r->time_event)
125         r->pdispatch->mainloop->time_free(r->time_event);
126
127     PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
128
129     pa_xfree(r);
130 }
131
132 pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
133     pa_pdispatch *pd;
134     assert(mainloop);
135
136     assert((entries && table) || (!entries && !table));
137
138     pd = pa_xmalloc(sizeof(pa_pdispatch));
139     pd->ref = 1;
140     pd->mainloop = mainloop;
141     pd->callback_table = table;
142     pd->n_commands = entries;
143     PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
144     pd->drain_callback = NULL;
145     pd->drain_userdata = NULL;
146     pd->creds = NULL;
147
148     return pd;
149 }
150
151 static void pdispatch_free(pa_pdispatch *pd) {
152     assert(pd);
153
154     while (pd->replies) {
155         if (pd->replies->free_cb)
156             pd->replies->free_cb(pd->replies->userdata);
157
158         reply_info_free(pd->replies);
159     }
160
161     pa_xfree(pd);
162 }
163
164 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
165     pa_pdispatch_cb_t callback;
166     void *userdata;
167     uint32_t tag;
168     assert(r);
169
170     pa_pdispatch_ref(pd);
171
172     callback = r->callback;
173     userdata = r->userdata;
174     tag = r->tag;
175
176     reply_info_free(r);
177
178     callback(pd, command, tag, ts, userdata);
179
180     if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
181         pd->drain_callback(pd, pd->drain_userdata);
182
183     pa_pdispatch_unref(pd);
184 }
185
186 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, void *userdata) {
187     uint32_t tag, command;
188     pa_tagstruct *ts = NULL;
189     int ret = -1;
190     assert(pd && packet && packet->data);
191
192     pa_pdispatch_ref(pd);
193
194     if (packet->length <= 8)
195         goto finish;
196
197     ts = pa_tagstruct_new(packet->data, packet->length);
198     assert(ts);
199
200     if (pa_tagstruct_getu32(ts, &command) < 0 ||
201         pa_tagstruct_getu32(ts, &tag) < 0)
202         goto finish;
203
204 #ifdef DEBUG_OPCODES
205 {
206     char t[256];
207     char const *p;
208     if (!(p = command_names[command]))
209         snprintf((char*) (p = t), sizeof(t), "%u", command);
210
211     pa_log("Recieved opcode <%s>", p);
212 }
213 #endif
214
215     pd->creds = creds;
216
217     if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
218         struct reply_info *r;
219
220         for (r = pd->replies; r; r = r->next)
221             if (r->tag == tag)
222                 break;
223
224         if (r)
225             run_action(pd, r, command, ts);
226
227     } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
228         const pa_pdispatch_cb_t *c = pd->callback_table+command;
229
230         (*c)(pd, command, tag, ts, userdata);
231     } else {
232         pa_log("Recieved unsupported command %u", command);
233         goto finish;
234     }
235
236     ret = 0;
237
238 finish:
239     pd->creds = NULL;
240
241     if (ts)
242         pa_tagstruct_free(ts);
243
244     pa_pdispatch_unref(pd);
245
246     return ret;
247 }
248
249 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
250     struct reply_info*r = userdata;
251     assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
252
253     run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
254 }
255
256 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) {
257     struct reply_info *r;
258     struct timeval tv;
259     assert(pd && pd->ref >= 1 && cb);
260
261     r = pa_xmalloc(sizeof(struct reply_info));
262     r->pdispatch = pd;
263     r->callback = cb;
264     r->userdata = userdata;
265     r->free_cb = free_cb;
266     r->tag = tag;
267
268     pa_gettimeofday(&tv);
269     tv.tv_sec += timeout;
270
271     r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r);
272     assert(r->time_event);
273
274     PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
275 }
276
277 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
278     assert(pd);
279
280     return !!pd->replies;
281 }
282
283 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
284     assert(pd);
285     assert(!cb || pa_pdispatch_is_pending(pd));
286
287     pd->drain_callback = cb;
288     pd->drain_userdata = userdata;
289 }
290
291 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
292     struct reply_info *r, *n;
293     assert(pd);
294
295     for (r = pd->replies; r; r = n) {
296         n = r->next;
297
298         if (r->userdata == userdata)
299             reply_info_free(r);
300     }
301 }
302
303 void pa_pdispatch_unref(pa_pdispatch *pd) {
304     assert(pd && pd->ref >= 1);
305
306     if (!(--(pd->ref)))
307         pdispatch_free(pd);
308 }
309
310 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
311     assert(pd && pd->ref >= 1);
312     pd->ref++;
313     return pd;
314 }
315
316 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
317     assert(pd);
318     assert(pd->ref >= 1);
319
320     return pd->creds;
321 }