a28458a4091299f873235d2addd6d1d2e01c33e2
[profile/ivi/pulseaudio-panda.git] / src / pdispatch.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 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 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 <stdio.h>
27 #include <stdlib.h>
28 #include <assert.h>
29
30 #include "pdispatch.h"
31 #include "native-common.h"
32
33 #ifdef DEBUG_OPCODES
34
35 static const char *command_names[PA_COMMAND_MAX] = {
36     [PA_COMMAND_ERROR] = "ERROR",
37     [PA_COMMAND_TIMEOUT] = "TIMEOUT",
38     [PA_COMMAND_REPLY] = "REPLY",
39     [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
40     [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
41     [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
42     [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
43     [PA_COMMAND_AUTH] = "AUTH",
44     [PA_COMMAND_REQUEST] = "REQUEST",
45     [PA_COMMAND_EXIT] = "EXIT",
46     [PA_COMMAND_SET_NAME] = "SET_NAME",
47     [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
48     [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
49     [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
50     [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
51     [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
52     [PA_COMMAND_STAT] = "STAT",
53     [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY",
54 };
55
56 #endif
57
58 struct reply_info {
59     struct pa_pdispatch *pdispatch;
60     struct reply_info *next, *previous;
61     void (*callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
62     void *userdata;
63     uint32_t tag;
64     void *mainloop_timeout;
65     int callback_is_running;
66 };
67
68 struct pa_pdispatch {
69     struct pa_mainloop_api *mainloop;
70     const struct pa_pdispatch_command *command_table;
71     unsigned n_commands;
72     struct reply_info *replies;
73     void (*drain_callback)(struct pa_pdispatch *pd, void *userdata);
74     void *drain_userdata;
75     int in_use, shall_free;
76 };
77
78 static void reply_info_free(struct reply_info *r) {
79     assert(r && r->pdispatch && r->pdispatch->mainloop);
80
81     if (r->pdispatch)
82         r->pdispatch->mainloop->cancel_time(r->pdispatch->mainloop, r->mainloop_timeout);
83
84     if (r->previous)
85         r->previous->next = r->next;
86     else
87         r->pdispatch->replies = r->next;
88
89     if (r->next)
90         r->next->previous = r->previous;
91     
92     free(r);
93 }
94
95 struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *mainloop, const struct pa_pdispatch_command*table, unsigned entries) {
96     struct pa_pdispatch *pd;
97     assert(mainloop);
98
99     assert((entries && table) || (!entries && !table));
100     
101     pd = malloc(sizeof(struct pa_pdispatch));
102     assert(pd);
103     pd->mainloop = mainloop;
104     pd->command_table = table;
105     pd->n_commands = entries;
106     pd->replies = NULL;
107     pd->drain_callback = NULL;
108     pd->drain_userdata = NULL;
109
110     pd->in_use = pd->shall_free = 0;
111     return pd;
112 }
113
114 void pa_pdispatch_free(struct pa_pdispatch *pd) {
115     assert(pd);
116     
117     if (pd->in_use) {
118         pd->shall_free = 1;
119         return;
120     }
121     
122     while (pd->replies)
123         reply_info_free(pd->replies);
124     free(pd);
125 }
126
127 int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*packet, void *userdata) {
128     uint32_t tag, command;
129     struct pa_tagstruct *ts = NULL;
130     int ret = -1;
131     assert(pd && packet && packet->data && !pd->in_use);
132
133     if (packet->length <= 8)
134         goto finish;
135
136     ts = pa_tagstruct_new(packet->data, packet->length);
137     assert(ts);
138     
139     if (pa_tagstruct_getu32(ts, &command) < 0 ||
140         pa_tagstruct_getu32(ts, &tag) < 0)
141         goto finish;
142
143 #ifdef DEBUG_OPCODES
144     fprintf(stderr, __FILE__": Recieved opcode <%s>\n", command_names[command]);
145 #endif
146
147     if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
148         struct reply_info *r;
149
150         for (r = pd->replies; r; r = r->next)
151             if (r->tag == tag)
152                 break;
153
154         if (r) {
155             pd->in_use = r->callback_is_running = 1;
156             assert(r->callback);
157             r->callback(r->pdispatch, command, tag, ts, r->userdata);
158             pd->in_use = r->callback_is_running = 0;
159             reply_info_free(r);
160             
161             if (pd->shall_free)
162                 pa_pdispatch_free(pd);
163             else {
164                 if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
165                     pd->drain_callback(pd, pd->drain_userdata);
166             }
167         }
168
169     } else if (pd->command_table && command < pd->n_commands) {
170         const struct pa_pdispatch_command *c = pd->command_table+command;
171
172         if (c->proc)
173             c->proc(pd, command, tag, ts, userdata);
174     } else
175         goto finish;
176
177     ret = 0;
178         
179 finish:
180     if (ts)
181         pa_tagstruct_free(ts);    
182
183     return ret;
184 }
185
186 static void timeout_callback(struct pa_mainloop_api*m, void *id, const struct timeval *tv, void *userdata) {
187     struct reply_info*r = userdata;
188     assert (r && r->mainloop_timeout == id && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
189
190     r->callback(r->pdispatch, PA_COMMAND_TIMEOUT, r->tag, NULL, r->userdata);
191     reply_info_free(r);
192
193     if (r->pdispatch->drain_callback && !pa_pdispatch_is_pending(r->pdispatch))
194         r->pdispatch->drain_callback(r->pdispatch, r->pdispatch->drain_userdata);
195 }
196
197 void pa_pdispatch_register_reply(struct pa_pdispatch *pd, uint32_t tag, int timeout, void (*cb)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata), void *userdata) {
198     struct reply_info *r;
199     struct timeval tv;
200     assert(pd && cb);
201
202     r = malloc(sizeof(struct reply_info));
203     assert(r);
204     r->pdispatch = pd;
205     r->callback = cb;
206     r->userdata = userdata;
207     r->tag = tag;
208     r->callback_is_running = 0;
209     
210     gettimeofday(&tv, NULL);
211     tv.tv_sec += timeout;
212
213     r->mainloop_timeout = pd->mainloop->source_time(pd->mainloop, &tv, timeout_callback, r);
214     assert(r->mainloop_timeout);
215
216     r->previous = NULL;
217     r->next = pd->replies;
218     if (r->next)
219         r->next->previous = r;
220     pd->replies = r;
221 }
222
223 int pa_pdispatch_is_pending(struct pa_pdispatch *pd) {
224     assert(pd);
225
226     return !!pd->replies;
227 }
228
229 void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct pa_pdispatch *pd, void *userdata), void *userdata) {
230     assert(pd);
231     assert(!cb || pa_pdispatch_is_pending(pd));
232
233     pd->drain_callback = cb;
234     pd->drain_userdata = userdata;
235 }
236
237 void pa_pdispatch_unregister_reply(struct pa_pdispatch *pd, void *userdata) {
238     struct reply_info *r, *n;
239     assert(pd);
240
241     for (r = pd->replies; r; r = n) {
242         n = r->next;
243
244         if (!r->callback_is_running && r->userdata == userdata) /* when this item's callback is currently running it is destroyed anyway in the very near future */
245             reply_info_free(r);
246     }
247 }