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",
172 /* Supported since protocol v15 (0.9.15) */
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",
185 /* Supported since protocol v16 (0.9.16) */
186 [PA_COMMAND_SET_SINK_PORT
] = "SET_SINK_PORT",
187 [PA_COMMAND_SET_SOURCE_PORT
] = "SET_SOURCE_PORT"
192 PA_STATIC_FLIST_DECLARE(reply_infos
, 0, pa_xfree
);
195 pa_pdispatch
*pdispatch
;
196 PA_LLIST_FIELDS(struct reply_info
);
197 pa_pdispatch_cb_t callback
;
199 pa_free_cb_t free_cb
;
201 pa_time_event
*time_event
;
204 struct pa_pdispatch
{
206 pa_mainloop_api
*mainloop
;
207 const pa_pdispatch_cb_t
*callback_table
;
209 PA_LLIST_HEAD(struct reply_info
, replies
);
210 pa_pdispatch_drain_callback drain_callback
;
211 void *drain_userdata
;
212 const pa_creds
*creds
;
213 pa_bool_t use_rtclock
:1;
216 static void reply_info_free(struct reply_info
*r
) {
218 pa_assert(r
->pdispatch
);
219 pa_assert(r
->pdispatch
->mainloop
);
222 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
224 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
226 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos
), r
) < 0)
230 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, pa_bool_t use_rtclock
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
234 pa_assert((entries
&& table
) || (!entries
&& !table
));
236 pd
= pa_xnew(pa_pdispatch
, 1);
238 pd
->mainloop
= mainloop
;
239 pd
->callback_table
= table
;
240 pd
->n_commands
= entries
;
241 PA_LLIST_HEAD_INIT(struct reply_info
, pd
->replies
);
242 pd
->drain_callback
= NULL
;
243 pd
->drain_userdata
= NULL
;
245 pd
->use_rtclock
= use_rtclock
;
250 static void pdispatch_free(pa_pdispatch
*pd
) {
253 while (pd
->replies
) {
254 if (pd
->replies
->free_cb
)
255 pd
->replies
->free_cb(pd
->replies
->userdata
);
257 reply_info_free(pd
->replies
);
263 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
264 pa_pdispatch_cb_t callback
;
269 pa_pdispatch_ref(pd
);
271 callback
= r
->callback
;
272 userdata
= r
->userdata
;
277 callback(pd
, command
, tag
, ts
, userdata
);
279 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
280 pd
->drain_callback(pd
, pd
->drain_userdata
);
282 pa_pdispatch_unref(pd
);
285 int pa_pdispatch_run(pa_pdispatch
*pd
, pa_packet
*packet
, const pa_creds
*creds
, void *userdata
) {
286 uint32_t tag
, command
;
287 pa_tagstruct
*ts
= NULL
;
291 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
293 pa_assert(PA_REFCNT_VALUE(packet
) >= 1);
294 pa_assert(packet
->data
);
296 pa_pdispatch_ref(pd
);
298 if (packet
->length
<= 8)
301 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
303 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
304 pa_tagstruct_getu32(ts
, &tag
) < 0)
310 char const *p
= NULL
;
312 if (command
>= PA_COMMAND_MAX
|| !(p
= command_names
[command
]))
313 pa_snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
315 pa_log("[%p] Received opcode <%s>", pd
, p
);
321 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
322 struct reply_info
*r
;
324 for (r
= pd
->replies
; r
; r
= r
->next
)
329 run_action(pd
, r
, command
, ts
);
331 } else if (pd
->callback_table
&& (command
< pd
->n_commands
) && pd
->callback_table
[command
]) {
332 const pa_pdispatch_cb_t
*c
= pd
->callback_table
+command
;
334 (*c
)(pd
, command
, tag
, ts
, userdata
);
336 pa_log("Received unsupported command %u", command
);
346 pa_tagstruct_free(ts
);
348 pa_pdispatch_unref(pd
);
353 static void timeout_callback(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
354 struct reply_info
*r
= userdata
;
357 pa_assert(r
->time_event
== e
);
358 pa_assert(r
->pdispatch
);
359 pa_assert(r
->pdispatch
->mainloop
== m
);
360 pa_assert(r
->callback
);
362 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
365 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
) {
366 struct reply_info
*r
;
370 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
373 if (!(r
= pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos
))))
374 r
= pa_xnew(struct reply_info
, 1);
378 r
->userdata
= userdata
;
379 r
->free_cb
= free_cb
;
382 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
));
384 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
387 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
389 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
391 return !!pd
->replies
;
394 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, void (*cb
)(pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
396 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
397 pa_assert(!cb
|| pa_pdispatch_is_pending(pd
));
399 pd
->drain_callback
= cb
;
400 pd
->drain_userdata
= userdata
;
403 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
404 struct reply_info
*r
, *n
;
407 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
409 for (r
= pd
->replies
; r
; r
= n
) {
412 if (r
->userdata
== userdata
)
417 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
419 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
421 if (PA_REFCNT_DEC(pd
) <= 0)
425 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
427 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
433 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
435 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);