ec4541909aaf2a4025bcdd202214eccb01dc12f5
[profile/ivi/pulseaudio.git] / src / pdispatch.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <assert.h>
4 #include "pdispatch.h"
5 #include "protocol-native-spec.h"
6
7 static const char *command_names[PA_COMMAND_MAX] = {
8     [PA_COMMAND_ERROR] = "ERROR",
9     [PA_COMMAND_TIMEOUT] = "TIMEOUT",
10     [PA_COMMAND_REPLY] = "REPLY",
11     [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
12     [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
13     [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
14     [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
15     [PA_COMMAND_AUTH] = "AUTH",
16     [PA_COMMAND_REQUEST] = "REQUEST",
17     [PA_COMMAND_EXIT] = "EXIT",
18     [PA_COMMAND_SET_NAME] = "SET_NAME",
19     [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
20     [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
21 };
22
23 struct reply_info {
24     struct pa_pdispatch *pdispatch;
25     struct reply_info *next, *previous;
26     void (*callback)(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata);
27     void *userdata;
28     uint32_t tag;
29     void *mainloop_timeout;
30 };
31
32 struct pa_pdispatch {
33     struct pa_mainloop_api *mainloop;
34     const struct pa_pdispatch_command *command_table;
35     unsigned n_commands;
36     struct reply_info *replies;
37     void (*drain_callback)(struct pa_pdispatch *pd, void *userdata);
38     void *drain_userdata;
39     int in_use, shall_free;
40 };
41
42 static void reply_info_free(struct reply_info *r) {
43     assert(r && r->pdispatch && r->pdispatch->mainloop);
44
45     if (r->pdispatch)
46         r->pdispatch->mainloop->cancel_time(r->pdispatch->mainloop, r->mainloop_timeout);
47
48     if (r->previous)
49         r->previous->next = r->next;
50     else
51         r->pdispatch->replies = r->next;
52
53     if (r->next)
54         r->next->previous = r->previous;
55     
56     free(r);
57 }
58
59 struct pa_pdispatch* pa_pdispatch_new(struct pa_mainloop_api *mainloop, const struct pa_pdispatch_command*table, unsigned entries) {
60     struct pa_pdispatch *pd;
61     assert(mainloop);
62
63     assert((entries && table) || (!entries && !table));
64     
65     pd = malloc(sizeof(struct pa_pdispatch));
66     assert(pd);
67     pd->mainloop = mainloop;
68     pd->command_table = table;
69     pd->n_commands = entries;
70     pd->replies = NULL;
71     pd->drain_callback = NULL;
72     pd->drain_userdata = NULL;
73
74     pd->in_use = pd->shall_free = 0;
75     return pd;
76 }
77
78 void pa_pdispatch_free(struct pa_pdispatch *pd) {
79     assert(pd);
80
81     if (pd->in_use) {
82         pd->shall_free = 1;
83         return;
84     }
85     
86     while (pd->replies)
87         reply_info_free(pd->replies);
88     free(pd);
89 }
90
91 int pa_pdispatch_run(struct pa_pdispatch *pd, struct pa_packet*packet, void *userdata) {
92     uint32_t tag, command;
93     struct pa_tagstruct *ts = NULL;
94     int ret = -1;
95     assert(pd && packet && packet->data && !pd->in_use);
96
97     if (packet->length <= 8)
98         goto finish;
99
100     ts = pa_tagstruct_new(packet->data, packet->length);
101     assert(ts);
102     
103     if (pa_tagstruct_getu32(ts, &command) < 0 ||
104         pa_tagstruct_getu32(ts, &tag) < 0)
105         goto finish;
106
107     /*fprintf(stderr, __FILE__": Recieved opcode <%s>\n", command_names[command]);*/
108
109     if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
110         struct reply_info *r;
111
112         for (r = pd->replies; r; r = r->next) {
113             if (r->tag != tag)
114                 continue;
115             
116             pd->in_use = 1;
117             assert(r->callback);
118             r->callback(r->pdispatch, command, tag, ts, r->userdata);
119             pd->in_use = 0;
120             reply_info_free(r);
121             
122             if (pd->shall_free) {
123                 pa_pdispatch_free(pd);
124                 break;
125             }
126
127             if (pd->drain_callback && !pa_pdispatch_is_pending(r->pdispatch))
128                 pd->drain_callback(r->pdispatch, r->pdispatch->drain_userdata);
129
130             break;
131         }
132
133     } else if (pd->command_table && command < pd->n_commands) {
134         const struct pa_pdispatch_command *c = pd->command_table+command;
135
136         if (c->proc)
137             c->proc(pd, command, tag, ts, userdata);
138     } else
139         goto finish;
140
141     ret = 0;
142         
143 finish:
144     if (ts)
145         pa_tagstruct_free(ts);    
146
147     return ret;
148 }
149
150 static void timeout_callback(struct pa_mainloop_api*m, void *id, const struct timeval *tv, void *userdata) {
151     struct reply_info*r = userdata;
152     assert (r && r->mainloop_timeout == id && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
153
154     r->callback(r->pdispatch, PA_COMMAND_TIMEOUT, r->tag, NULL, r->userdata);
155     reply_info_free(r);
156
157     if (r->pdispatch->drain_callback && !pa_pdispatch_is_pending(r->pdispatch))
158         r->pdispatch->drain_callback(r->pdispatch, r->pdispatch->drain_userdata);
159 }
160
161 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) {
162     struct reply_info *r;
163     struct timeval tv;
164     assert(pd && cb);
165
166     r = malloc(sizeof(struct reply_info));
167     assert(r);
168     r->pdispatch = pd;
169     r->callback = cb;
170     r->userdata = userdata;
171     r->tag = tag;
172
173     gettimeofday(&tv, NULL);
174     tv.tv_sec += timeout;
175
176     r->mainloop_timeout = pd->mainloop->source_time(pd->mainloop, &tv, timeout_callback, r);
177     assert(r->mainloop_timeout);
178
179     r->previous = NULL;
180     r->next = pd->replies;
181     if (r->next)
182         r->next->previous = r;
183     pd->replies = r;
184 }
185
186 int pa_pdispatch_is_pending(struct pa_pdispatch *pd) {
187     assert(pd);
188
189     return !!pd->replies;
190 }
191
192 void pa_pdispatch_set_drain_callback(struct pa_pdispatch *pd, void (*cb)(struct pa_pdispatch *pd, void *userdata), void *userdata) {
193     assert(pd);
194     assert(!cb || pa_pdispatch_is_pending(pd));
195
196     pd->drain_callback = cb;
197     pd->drain_userdata = userdata;
198 }