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/rtclock.h>
31 #include <pulse/timeval.h>
32 #include <pulse/xmalloc.h>
34 #include <pulsecore/native-common.h>
35 #include <pulsecore/llist.h>
36 #include <pulsecore/log.h>
37 #include <pulsecore/core-util.h>
38 #include <pulsecore/macro.h>
39 #include <pulsecore/refcnt.h>
40 #include <pulsecore/flist.h>
41 #include <pulsecore/core-rtclock.h>
43 #include "pdispatch.h"
45 /* #define DEBUG_OPCODES */
49 static const char *command_names
[PA_COMMAND_MAX
] = {
50 /* Generic commands */
51 [PA_COMMAND_ERROR
] = "ERROR",
52 [PA_COMMAND_TIMEOUT
] = "TIMEOUT",
53 [PA_COMMAND_REPLY
] = "REPLY",
56 [PA_COMMAND_CREATE_PLAYBACK_STREAM
] = "CREATE_PLAYBACK_STREAM",
57 [PA_COMMAND_DELETE_PLAYBACK_STREAM
] = "DELETE_PLAYBACK_STREAM",
58 [PA_COMMAND_CREATE_RECORD_STREAM
] = "CREATE_RECORD_STREAM",
59 [PA_COMMAND_DELETE_RECORD_STREAM
] = "DELETE_RECORD_STREAM",
60 [PA_COMMAND_AUTH
] = "AUTH",
61 [PA_COMMAND_EXIT
] = "EXIT",
62 [PA_COMMAND_SET_CLIENT_NAME
] = "SET_CLIENT_NAME",
63 [PA_COMMAND_LOOKUP_SINK
] = "LOOKUP_SINK",
64 [PA_COMMAND_LOOKUP_SOURCE
] = "LOOKUP_SOURCE",
65 [PA_COMMAND_DRAIN_PLAYBACK_STREAM
] = "DRAIN_PLAYBACK_STREAM",
66 [PA_COMMAND_STAT
] = "STAT",
67 [PA_COMMAND_GET_PLAYBACK_LATENCY
] = "GET_PLAYBACK_LATENCY",
68 [PA_COMMAND_CREATE_UPLOAD_STREAM
] = "CREATE_UPLOAD_STREAM",
69 [PA_COMMAND_DELETE_UPLOAD_STREAM
] = "DELETE_UPLOAD_STREAM",
70 [PA_COMMAND_FINISH_UPLOAD_STREAM
] = "FINISH_UPLOAD_STREAM",
71 [PA_COMMAND_PLAY_SAMPLE
] = "PLAY_SAMPLE",
72 [PA_COMMAND_REMOVE_SAMPLE
] = "REMOVE_SAMPLE",
74 [PA_COMMAND_GET_SERVER_INFO
] = "GET_SERVER_INFO",
75 [PA_COMMAND_GET_SINK_INFO
] = "GET_SINK_INFO",
76 [PA_COMMAND_GET_SINK_INFO_LIST
] = "GET_SINK_INFO_LIST",
77 [PA_COMMAND_GET_SOURCE_INFO
] = "GET_SOURCE_INFO",
78 [PA_COMMAND_GET_SOURCE_INFO_LIST
] = "GET_SOURCE_INFO_LIST",
79 [PA_COMMAND_GET_MODULE_INFO
] = "GET_MODULE_INFO",
80 [PA_COMMAND_GET_MODULE_INFO_LIST
] = "GET_MODULE_INFO_LIST",
81 [PA_COMMAND_GET_CLIENT_INFO
] = "GET_CLIENT_INFO",
82 [PA_COMMAND_GET_CLIENT_INFO_LIST
] = "GET_CLIENT_INFO_LIST",
83 [PA_COMMAND_GET_SAMPLE_INFO
] = "GET_SAMPLE_INFO",
84 [PA_COMMAND_GET_SAMPLE_INFO_LIST
] = "GET_SAMPLE_INFO_LIST",
85 [PA_COMMAND_GET_SINK_INPUT_INFO
] = "GET_SINK_INPUT_INFO",
86 [PA_COMMAND_GET_SINK_INPUT_INFO_LIST
] = "GET_SINK_INPUT_INFO_LIST",
87 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO
] = "GET_SOURCE_OUTPUT_INFO",
88 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST
] = "GET_SOURCE_OUTPUT_INFO_LIST",
89 [PA_COMMAND_SUBSCRIBE
] = "SUBSCRIBE",
91 [PA_COMMAND_SET_SINK_VOLUME
] = "SET_SINK_VOLUME",
92 [PA_COMMAND_SET_SINK_INPUT_VOLUME
] = "SET_SINK_INPUT_VOLUME",
93 [PA_COMMAND_SET_SOURCE_VOLUME
] = "SET_SOURCE_VOLME",
95 [PA_COMMAND_SET_SINK_MUTE
] = "SET_SINK_MUTE",
96 [PA_COMMAND_SET_SOURCE_MUTE
] = "SET_SOURCE_MUTE",
98 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM
] = "TRIGGER_PLAYBACK_STREAM",
99 [PA_COMMAND_FLUSH_PLAYBACK_STREAM
] = "FLUSH_PLAYBACK_STREAM",
100 [PA_COMMAND_CORK_PLAYBACK_STREAM
] = "CORK_PLAYBACK_STREAM",
102 [PA_COMMAND_SET_DEFAULT_SINK
] = "SET_DEFAULT_SINK",
103 [PA_COMMAND_SET_DEFAULT_SOURCE
] = "SET_DEFAULT_SOURCE",
105 [PA_COMMAND_SET_PLAYBACK_STREAM_NAME
] = "SET_PLAYBACK_STREAM_NAME",
106 [PA_COMMAND_SET_RECORD_STREAM_NAME
] = "SET_RECORD_STREAM_NAME",
108 [PA_COMMAND_KILL_CLIENT
] = "KILL_CLIENT",
109 [PA_COMMAND_KILL_SINK_INPUT
] = "KILL_SINK_INPUT",
110 [PA_COMMAND_KILL_SOURCE_OUTPUT
] = "SOURCE_OUTPUT",
112 [PA_COMMAND_LOAD_MODULE
] = "LOAD_MODULE",
113 [PA_COMMAND_UNLOAD_MODULE
] = "UNLOAD_MODULE",
115 [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE
] = "ADD_AUTOLOAD (obsolete)",
116 [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE
] = "REMOVE_AUTOLOAD (obsolete)",
117 [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE
] = "GET_AUTOLOAD_INFO (obsolete)",
118 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE
] = "GET_AUTOLOAD_INFO_LIST (obsolete)",
120 [PA_COMMAND_GET_RECORD_LATENCY
] = "GET_RECORD_LATENCY",
121 [PA_COMMAND_CORK_RECORD_STREAM
] = "CORK_RECORD_STREAM",
122 [PA_COMMAND_FLUSH_RECORD_STREAM
] = "FLUSH_RECORD_STREAM",
123 [PA_COMMAND_PREBUF_PLAYBACK_STREAM
] = "PREBUF_PLAYBACK_STREAM",
126 [PA_COMMAND_REQUEST
] = "REQUEST",
127 [PA_COMMAND_OVERFLOW
] = "OVERFLOW",
128 [PA_COMMAND_UNDERFLOW
] = "UNDERFLOW",
129 [PA_COMMAND_PLAYBACK_STREAM_KILLED
] = "PLAYBACK_STREAM_KILLED",
130 [PA_COMMAND_RECORD_STREAM_KILLED
] = "RECORD_STREAM_KILLED",
131 [PA_COMMAND_SUBSCRIBE_EVENT
] = "SUBSCRIBE_EVENT",
133 /* A few more client->server commands */
135 /* Supported since protocol v10 (0.9.5) */
136 [PA_COMMAND_MOVE_SINK_INPUT
] = "MOVE_SINK_INPUT",
137 [PA_COMMAND_MOVE_SOURCE_OUTPUT
] = "MOVE_SOURCE_OUTPUT",
139 /* Supported since protocol v11 (0.9.7) */
140 [PA_COMMAND_SET_SINK_INPUT_MUTE
] = "SET_SINK_INPUT_MUTE",
142 [PA_COMMAND_SUSPEND_SINK
] = "SUSPEND_SINK",
143 [PA_COMMAND_SUSPEND_SOURCE
] = "SUSPEND_SOURCE",
145 /* Supported since protocol v12 (0.9.8) */
146 [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR
] = "SET_PLAYBACK_STREAM_BUFFER_ATTR",
147 [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR
] = "SET_RECORD_STREAM_BUFFER_ATTR",
149 [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE
] = "UPDATE_PLAYBACK_STREAM_SAMPLE_RATE",
150 [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE
] = "UPDATE_RECORD_STREAM_SAMPLE_RATE",
153 [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED
] = "PLAYBACK_STREAM_SUSPENDED",
154 [PA_COMMAND_RECORD_STREAM_SUSPENDED
] = "RECORD_STREAM_SUSPENDED",
155 [PA_COMMAND_PLAYBACK_STREAM_MOVED
] = "PLAYBACK_STREAM_MOVED",
156 [PA_COMMAND_RECORD_STREAM_MOVED
] = "RECORD_STREAM_MOVED",
158 /* Supported since protocol v13 (0.9.11) */
159 [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST
] = "UPDATE_RECORD_STREAM_PROPLIST",
160 [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST
] = "UPDATE_RECORD_STREAM_PROPLIST",
161 [PA_COMMAND_UPDATE_CLIENT_PROPLIST
] = "UPDATE_CLIENT_PROPLIST",
162 [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST
] = "REMOVE_RECORD_STREAM_PROPLIST",
163 [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST
] = "REMOVE_PLAYBACK_STREAM_PROPLIST",
164 [PA_COMMAND_REMOVE_CLIENT_PROPLIST
] = "REMOVE_CLIENT_PROPLIST",
167 [PA_COMMAND_STARTED
] = "STARTED",
169 /* Supported since protocol v14 (0.9.12) */
170 [PA_COMMAND_EXTENSION
] = "EXTENSION",
173 [PA_COMMAND_GET_CARD_INFO
] = "GET_CARD_INFO",
174 [PA_COMMAND_GET_CARD_INFO_LIST
] = "GET_CARD_INFO_LIST",
175 [PA_COMMAND_SET_CARD_PROFILE
] = "SET_CARD_PROFILE",
177 [PA_COMMAND_CLIENT_EVENT
] = "GET_CLIENT_EVENT",
178 [PA_COMMAND_PLAYBACK_STREAM_EVENT
] = "PLAYBACK_STREAM_EVENT",
179 [PA_COMMAND_RECORD_STREAM_EVENT
] = "RECORD_STREAM_EVENT",
182 [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED
] = "PLAYBACK_BUFFER_ATTR_CHANGED",
183 [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED
] = "RECORD_BUFFER_ATTR_CHANGED"
188 PA_STATIC_FLIST_DECLARE(reply_infos
, 0, pa_xfree
);
191 pa_pdispatch
*pdispatch
;
192 PA_LLIST_FIELDS(struct reply_info
);
193 pa_pdispatch_cb_t callback
;
195 pa_free_cb_t free_cb
;
197 pa_time_event
*time_event
;
200 struct pa_pdispatch
{
202 pa_mainloop_api
*mainloop
;
203 const pa_pdispatch_cb_t
*callback_table
;
205 PA_LLIST_HEAD(struct reply_info
, replies
);
206 pa_pdispatch_drain_callback drain_callback
;
207 void *drain_userdata
;
208 const pa_creds
*creds
;
209 pa_bool_t use_rtclock
:1;
212 static void reply_info_free(struct reply_info
*r
) {
214 pa_assert(r
->pdispatch
);
215 pa_assert(r
->pdispatch
->mainloop
);
218 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
220 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
222 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos
), r
) < 0)
226 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, pa_bool_t use_rtclock
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
230 pa_assert((entries
&& table
) || (!entries
&& !table
));
232 pd
= pa_xnew(pa_pdispatch
, 1);
234 pd
->mainloop
= mainloop
;
235 pd
->callback_table
= table
;
236 pd
->n_commands
= entries
;
237 PA_LLIST_HEAD_INIT(struct reply_info
, pd
->replies
);
238 pd
->drain_callback
= NULL
;
239 pd
->drain_userdata
= NULL
;
241 pd
->use_rtclock
= use_rtclock
;
246 static void pdispatch_free(pa_pdispatch
*pd
) {
249 while (pd
->replies
) {
250 if (pd
->replies
->free_cb
)
251 pd
->replies
->free_cb(pd
->replies
->userdata
);
253 reply_info_free(pd
->replies
);
259 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
260 pa_pdispatch_cb_t callback
;
265 pa_pdispatch_ref(pd
);
267 callback
= r
->callback
;
268 userdata
= r
->userdata
;
273 callback(pd
, command
, tag
, ts
, userdata
);
275 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
276 pd
->drain_callback(pd
, pd
->drain_userdata
);
278 pa_pdispatch_unref(pd
);
281 int pa_pdispatch_run(pa_pdispatch
*pd
, pa_packet
*packet
, const pa_creds
*creds
, void *userdata
) {
282 uint32_t tag
, command
;
283 pa_tagstruct
*ts
= NULL
;
287 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
289 pa_assert(PA_REFCNT_VALUE(packet
) >= 1);
290 pa_assert(packet
->data
);
292 pa_pdispatch_ref(pd
);
294 if (packet
->length
<= 8)
297 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
299 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
300 pa_tagstruct_getu32(ts
, &tag
) < 0)
306 char const *p
= NULL
;
308 if (command
>= PA_COMMAND_MAX
|| !(p
= command_names
[command
]))
309 pa_snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
311 pa_log("[%p] Received opcode <%s>", pd
, p
);
317 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
318 struct reply_info
*r
;
320 for (r
= pd
->replies
; r
; r
= r
->next
)
325 run_action(pd
, r
, command
, ts
);
327 } else if (pd
->callback_table
&& (command
< pd
->n_commands
) && pd
->callback_table
[command
]) {
328 const pa_pdispatch_cb_t
*c
= pd
->callback_table
+command
;
330 (*c
)(pd
, command
, tag
, ts
, userdata
);
332 pa_log("Received unsupported command %u", command
);
342 pa_tagstruct_free(ts
);
344 pa_pdispatch_unref(pd
);
349 static void timeout_callback(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
350 struct reply_info
*r
= userdata
;
353 pa_assert(r
->time_event
== e
);
354 pa_assert(r
->pdispatch
);
355 pa_assert(r
->pdispatch
->mainloop
== m
);
356 pa_assert(r
->callback
);
358 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
361 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
) {
362 struct reply_info
*r
;
366 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
369 if (!(r
= pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos
))))
370 r
= pa_xnew(struct reply_info
, 1);
374 r
->userdata
= userdata
;
375 r
->free_cb
= free_cb
;
378 pa_assert_se(r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, pa_timeval_rtstore(&tv
, pa_rtclock_now() + timeout
* PA_USEC_PER_SEC
, pd
->use_rtclock
), timeout_callback
, r
));
380 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
383 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
385 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
387 return !!pd
->replies
;
390 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, void (*cb
)(pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
392 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
393 pa_assert(!cb
|| pa_pdispatch_is_pending(pd
));
395 pd
->drain_callback
= cb
;
396 pd
->drain_userdata
= userdata
;
399 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
400 struct reply_info
*r
, *n
;
403 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
405 for (r
= pd
->replies
; r
; r
= n
) {
408 if (r
->userdata
== userdata
)
413 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
415 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
417 if (PA_REFCNT_DEC(pd
) <= 0)
421 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
423 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
429 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
431 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);