2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include <pulse/timeval.h>
31 #include <pulse/xmalloc.h>
33 #include <pulsecore/native-common.h>
34 #include <pulsecore/llist.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/core-util.h>
37 #include <pulsecore/macro.h>
38 #include <pulsecore/refcnt.h>
39 #include <pulsecore/flist.h>
41 #include "pdispatch.h"
43 /* #define DEBUG_OPCODES */
47 static const char *command_names
[PA_COMMAND_MAX
] = {
48 /* Generic commands */
49 [PA_COMMAND_ERROR
] = "ERROR",
50 [PA_COMMAND_TIMEOUT
] = "TIMEOUT",
51 [PA_COMMAND_REPLY
] = "REPLY",
54 [PA_COMMAND_CREATE_PLAYBACK_STREAM
] = "CREATE_PLAYBACK_STREAM",
55 [PA_COMMAND_DELETE_PLAYBACK_STREAM
] = "DELETE_PLAYBACK_STREAM",
56 [PA_COMMAND_CREATE_RECORD_STREAM
] = "CREATE_RECORD_STREAM",
57 [PA_COMMAND_DELETE_RECORD_STREAM
] = "DELETE_RECORD_STREAM",
58 [PA_COMMAND_AUTH
] = "AUTH",
59 [PA_COMMAND_EXIT
] = "EXIT",
60 [PA_COMMAND_SET_CLIENT_NAME
] = "SET_CLIENT_NAME",
61 [PA_COMMAND_LOOKUP_SINK
] = "LOOKUP_SINK",
62 [PA_COMMAND_LOOKUP_SOURCE
] = "LOOKUP_SOURCE",
63 [PA_COMMAND_DRAIN_PLAYBACK_STREAM
] = "DRAIN_PLAYBACK_STREAM",
64 [PA_COMMAND_STAT
] = "STAT",
65 [PA_COMMAND_GET_PLAYBACK_LATENCY
] = "GET_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",
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",
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",
93 [PA_COMMAND_SET_SINK_MUTE
] = "SET_SINK_MUTE",
94 [PA_COMMAND_SET_SOURCE_MUTE
] = "SET_SOURCE_MUTE",
96 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM
] = "TRIGGER_PLAYBACK_STREAM",
97 [PA_COMMAND_FLUSH_PLAYBACK_STREAM
] = "FLUSH_PLAYBACK_STREAM",
98 [PA_COMMAND_CORK_PLAYBACK_STREAM
] = "CORK_PLAYBACK_STREAM",
100 [PA_COMMAND_SET_DEFAULT_SINK
] = "SET_DEFAULT_SINK",
101 [PA_COMMAND_SET_DEFAULT_SOURCE
] = "SET_DEFAULT_SOURCE",
103 [PA_COMMAND_SET_PLAYBACK_STREAM_NAME
] = "SET_PLAYBACK_STREAM_NAME",
104 [PA_COMMAND_SET_RECORD_STREAM_NAME
] = "SET_RECORD_STREAM_NAME",
106 [PA_COMMAND_KILL_CLIENT
] = "KILL_CLIENT",
107 [PA_COMMAND_KILL_SINK_INPUT
] = "KILL_SINK_INPUT",
108 [PA_COMMAND_KILL_SOURCE_OUTPUT
] = "SOURCE_OUTPUT",
110 [PA_COMMAND_LOAD_MODULE
] = "LOAD_MODULE",
111 [PA_COMMAND_UNLOAD_MODULE
] = "UNLOAD_MODULE",
113 [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE
] = "ADD_AUTOLOAD (obsolete)",
114 [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE
] = "REMOVE_AUTOLOAD (obsolete)",
115 [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE
] = "GET_AUTOLOAD_INFO (obsolete)",
116 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE
] = "GET_AUTOLOAD_INFO_LIST (obsolete)",
118 [PA_COMMAND_GET_RECORD_LATENCY
] = "GET_RECORD_LATENCY",
119 [PA_COMMAND_CORK_RECORD_STREAM
] = "CORK_RECORD_STREAM",
120 [PA_COMMAND_FLUSH_RECORD_STREAM
] = "FLUSH_RECORD_STREAM",
121 [PA_COMMAND_PREBUF_PLAYBACK_STREAM
] = "PREBUF_PLAYBACK_STREAM",
124 [PA_COMMAND_REQUEST
] = "REQUEST",
125 [PA_COMMAND_OVERFLOW
] = "OVERFLOW",
126 [PA_COMMAND_UNDERFLOW
] = "UNDERFLOW",
127 [PA_COMMAND_PLAYBACK_STREAM_KILLED
] = "PLAYBACK_STREAM_KILLED",
128 [PA_COMMAND_RECORD_STREAM_KILLED
] = "RECORD_STREAM_KILLED",
129 [PA_COMMAND_SUBSCRIBE_EVENT
] = "SUBSCRIBE_EVENT",
131 /* A few more client->server commands */
133 /* Supported since protocol v10 (0.9.5) */
134 [PA_COMMAND_MOVE_SINK_INPUT
] = "MOVE_SINK_INPUT",
135 [PA_COMMAND_MOVE_SOURCE_OUTPUT
] = "MOVE_SOURCE_OUTPUT",
137 /* Supported since protocol v11 (0.9.7) */
138 [PA_COMMAND_SET_SINK_INPUT_MUTE
] = "SET_SINK_INPUT_MUTE",
140 [PA_COMMAND_SUSPEND_SINK
] = "SUSPEND_SINK",
141 [PA_COMMAND_SUSPEND_SOURCE
] = "SUSPEND_SOURCE",
143 /* Supported since protocol v12 (0.9.8) */
144 [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR
] = "SET_PLAYBACK_STREAM_BUFFER_ATTR",
145 [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR
] = "SET_RECORD_STREAM_BUFFER_ATTR",
147 [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE
] = "UPDATE_PLAYBACK_STREAM_SAMPLE_RATE",
148 [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE
] = "UPDATE_RECORD_STREAM_SAMPLE_RATE",
151 [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED
] = "PLAYBACK_STREAM_SUSPENDED",
152 [PA_COMMAND_RECORD_STREAM_SUSPENDED
] = "RECORD_STREAM_SUSPENDED",
153 [PA_COMMAND_PLAYBACK_STREAM_MOVED
] = "PLAYBACK_STREAM_MOVED",
154 [PA_COMMAND_RECORD_STREAM_MOVED
] = "RECORD_STREAM_MOVED",
156 /* Supported since protocol v13 (0.9.11) */
157 [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST
] = "UPDATE_RECORD_STREAM_PROPLIST",
158 [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST
] = "UPDATE_RECORD_STREAM_PROPLIST",
159 [PA_COMMAND_UPDATE_CLIENT_PROPLIST
] = "UPDATE_CLIENT_PROPLIST",
160 [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST
] = "REMOVE_RECORD_STREAM_PROPLIST",
161 [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST
] = "REMOVE_PLAYBACK_STREAM_PROPLIST",
162 [PA_COMMAND_REMOVE_CLIENT_PROPLIST
] = "REMOVE_CLIENT_PROPLIST",
165 [PA_COMMAND_STARTED
] = "STARTED",
167 /* Supported since protocol v14 (0.9.12) */
168 [PA_COMMAND_EXTENSION
] = "EXTENSION"
173 PA_STATIC_FLIST_DECLARE(reply_infos
, 0, pa_xfree
);
176 pa_pdispatch
*pdispatch
;
177 PA_LLIST_FIELDS(struct reply_info
);
178 pa_pdispatch_cb_t callback
;
180 pa_free_cb_t free_cb
;
182 pa_time_event
*time_event
;
185 struct pa_pdispatch
{
187 pa_mainloop_api
*mainloop
;
188 const pa_pdispatch_cb_t
*callback_table
;
190 PA_LLIST_HEAD(struct reply_info
, replies
);
191 pa_pdispatch_drain_callback drain_callback
;
192 void *drain_userdata
;
193 const pa_creds
*creds
;
196 static void reply_info_free(struct reply_info
*r
) {
198 pa_assert(r
->pdispatch
);
199 pa_assert(r
->pdispatch
->mainloop
);
202 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
204 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
206 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos
), r
) < 0)
210 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
214 pa_assert((entries
&& table
) || (!entries
&& !table
));
216 pd
= pa_xnew(pa_pdispatch
, 1);
218 pd
->mainloop
= mainloop
;
219 pd
->callback_table
= table
;
220 pd
->n_commands
= entries
;
221 PA_LLIST_HEAD_INIT(struct reply_info
, pd
->replies
);
222 pd
->drain_callback
= NULL
;
223 pd
->drain_userdata
= NULL
;
229 static void pdispatch_free(pa_pdispatch
*pd
) {
232 while (pd
->replies
) {
233 if (pd
->replies
->free_cb
)
234 pd
->replies
->free_cb(pd
->replies
->userdata
);
236 reply_info_free(pd
->replies
);
242 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
243 pa_pdispatch_cb_t callback
;
248 pa_pdispatch_ref(pd
);
250 callback
= r
->callback
;
251 userdata
= r
->userdata
;
256 callback(pd
, command
, tag
, ts
, userdata
);
258 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
259 pd
->drain_callback(pd
, pd
->drain_userdata
);
261 pa_pdispatch_unref(pd
);
264 int pa_pdispatch_run(pa_pdispatch
*pd
, pa_packet
*packet
, const pa_creds
*creds
, void *userdata
) {
265 uint32_t tag
, command
;
266 pa_tagstruct
*ts
= NULL
;
270 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
272 pa_assert(PA_REFCNT_VALUE(packet
) >= 1);
273 pa_assert(packet
->data
);
275 pa_pdispatch_ref(pd
);
277 if (packet
->length
<= 8)
280 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
282 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
283 pa_tagstruct_getu32(ts
, &tag
) < 0)
289 char const *p
= NULL
;
291 if (command
>= PA_COMMAND_MAX
|| !(p
= command_names
[command
]))
292 pa_snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
294 pa_log("[%p] Recieved opcode <%s>", pd
, p
);
300 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
301 struct reply_info
*r
;
303 for (r
= pd
->replies
; r
; r
= r
->next
)
308 run_action(pd
, r
, command
, ts
);
310 } else if (pd
->callback_table
&& (command
< pd
->n_commands
) && pd
->callback_table
[command
]) {
311 const pa_pdispatch_cb_t
*c
= pd
->callback_table
+command
;
313 (*c
)(pd
, command
, tag
, ts
, userdata
);
315 pa_log("Recieved unsupported command %u", command
);
325 pa_tagstruct_free(ts
);
327 pa_pdispatch_unref(pd
);
332 static void timeout_callback(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*tv
, void *userdata
) {
333 struct reply_info
*r
= userdata
;
336 pa_assert(r
->time_event
== e
);
337 pa_assert(r
->pdispatch
);
338 pa_assert(r
->pdispatch
->mainloop
== m
);
339 pa_assert(r
->callback
);
341 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
344 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
) {
345 struct reply_info
*r
;
349 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
352 if (!(r
= pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos
))))
353 r
= pa_xnew(struct reply_info
, 1);
357 r
->userdata
= userdata
;
358 r
->free_cb
= free_cb
;
361 pa_gettimeofday(&tv
);
362 tv
.tv_sec
+= timeout
;
364 pa_assert_se(r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, &tv
, timeout_callback
, r
));
366 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
369 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
371 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
373 return !!pd
->replies
;
376 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, void (*cb
)(pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
378 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
379 pa_assert(!cb
|| pa_pdispatch_is_pending(pd
));
381 pd
->drain_callback
= cb
;
382 pd
->drain_userdata
= userdata
;
385 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
386 struct reply_info
*r
, *n
;
389 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
391 for (r
= pd
->replies
; r
; r
= n
) {
394 if (r
->userdata
== userdata
)
399 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
401 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
403 if (PA_REFCNT_DEC(pd
) <= 0)
407 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
409 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
415 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
417 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);