4 This file is part of PulseAudio.
6 Copyright 2004-2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
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
32 #include <pulse/timeval.h>
33 #include <pulse/xmalloc.h>
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>
42 #include "pdispatch.h"
44 /*#define DEBUG_OPCODES */
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",
102 pa_pdispatch
*pdispatch
;
103 PA_LLIST_FIELDS(struct reply_info
);
104 pa_pdispatch_cb_t callback
;
106 pa_free_cb_t free_cb
;
108 pa_time_event
*time_event
;
111 struct pa_pdispatch
{
113 pa_mainloop_api
*mainloop
;
114 const pa_pdispatch_cb_t
*callback_table
;
116 PA_LLIST_HEAD(struct reply_info
, replies
);
117 pa_pdispatch_drain_callback drain_callback
;
118 void *drain_userdata
;
119 const pa_creds
*creds
;
122 static void reply_info_free(struct reply_info
*r
) {
124 pa_assert(r
->pdispatch
);
125 pa_assert(r
->pdispatch
->mainloop
);
128 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
130 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
135 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
139 pa_assert((entries
&& table
) || (!entries
&& !table
));
141 pd
= pa_xnew(pa_pdispatch
, 1);
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
;
154 static void pdispatch_free(pa_pdispatch
*pd
) {
157 while (pd
->replies
) {
158 if (pd
->replies
->free_cb
)
159 pd
->replies
->free_cb(pd
->replies
->userdata
);
161 reply_info_free(pd
->replies
);
167 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
168 pa_pdispatch_cb_t callback
;
173 pa_pdispatch_ref(pd
);
175 callback
= r
->callback
;
176 userdata
= r
->userdata
;
181 callback(pd
, command
, tag
, ts
, userdata
);
183 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
184 pd
->drain_callback(pd
, pd
->drain_userdata
);
186 pa_pdispatch_unref(pd
);
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
;
195 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
197 pa_assert(PA_REFCNT_VALUE(packet
) >= 1);
198 pa_assert(packet
->data
);
200 pa_pdispatch_ref(pd
);
202 if (packet
->length
<= 8)
205 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
207 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
208 pa_tagstruct_getu32(ts
, &tag
) < 0)
215 if (!(p
= command_names
[command
]))
216 pa_snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
218 pa_log("Recieved opcode <%s>", p
);
224 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
225 struct reply_info
*r
;
227 for (r
= pd
->replies
; r
; r
= r
->next
)
232 run_action(pd
, r
, command
, ts
);
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
;
237 (*c
)(pd
, command
, tag
, ts
, userdata
);
239 pa_log("Recieved unsupported command %u", command
);
249 pa_tagstruct_free(ts
);
251 pa_pdispatch_unref(pd
);
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
;
260 pa_assert(r
->time_event
== e
);
261 pa_assert(r
->pdispatch
);
262 pa_assert(r
->pdispatch
->mainloop
== m
);
263 pa_assert(r
->callback
);
265 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
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
;
273 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
276 r
= pa_xnew(struct reply_info
, 1);
279 r
->userdata
= userdata
;
280 r
->free_cb
= free_cb
;
283 pa_gettimeofday(&tv
);
284 tv
.tv_sec
+= timeout
;
286 pa_assert_se(r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, &tv
, timeout_callback
, r
));
288 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
291 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
293 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
295 return !!pd
->replies
;
298 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, void (*cb
)(pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
300 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
301 pa_assert(!cb
|| pa_pdispatch_is_pending(pd
));
303 pd
->drain_callback
= cb
;
304 pd
->drain_userdata
= userdata
;
307 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
308 struct reply_info
*r
, *n
;
311 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
313 for (r
= pd
->replies
; r
; r
= n
) {
316 if (r
->userdata
== userdata
)
321 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
323 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
325 if (PA_REFCNT_DEC(pd
) <= 0)
329 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
331 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
337 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
339 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);