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_VOLUME",
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
] = "KILL_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_PLAYBACK_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
] = "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",
189 /* Supported since protocol v22 (1.0) */
190 [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME
] = "SET_SOURCE_OUTPUT_VOLUME",
191 [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE
] = "SET_SOURCE_OUTPUT_MUTE",
193 /* Supported since protocol v27 (3.0) */
194 [PA_COMMAND_SET_PORT_LATENCY_OFFSET
] = "SET_PORT_LATENCY_OFFSET",
199 PA_STATIC_FLIST_DECLARE(reply_infos
, 0, pa_xfree
);
202 pa_pdispatch
*pdispatch
;
203 PA_LLIST_FIELDS(struct reply_info
);
204 pa_pdispatch_cb_t callback
;
206 pa_free_cb_t free_cb
;
208 pa_time_event
*time_event
;
211 struct pa_pdispatch
{
213 pa_mainloop_api
*mainloop
;
214 const pa_pdispatch_cb_t
*callback_table
;
216 PA_LLIST_HEAD(struct reply_info
, replies
);
217 pa_pdispatch_drain_cb_t drain_callback
;
218 void *drain_userdata
;
219 const pa_creds
*creds
;
223 static void reply_info_free(struct reply_info
*r
) {
225 pa_assert(r
->pdispatch
);
226 pa_assert(r
->pdispatch
->mainloop
);
229 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
231 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
233 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos
), r
) < 0)
237 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, bool use_rtclock
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
241 pa_assert((entries
&& table
) || (!entries
&& !table
));
243 pd
= pa_xnew0(pa_pdispatch
, 1);
245 pd
->mainloop
= mainloop
;
246 pd
->callback_table
= table
;
247 pd
->n_commands
= entries
;
248 PA_LLIST_HEAD_INIT(struct reply_info
, pd
->replies
);
249 pd
->use_rtclock
= use_rtclock
;
254 static void pdispatch_free(pa_pdispatch
*pd
) {
257 while (pd
->replies
) {
258 if (pd
->replies
->free_cb
)
259 pd
->replies
->free_cb(pd
->replies
->userdata
);
261 reply_info_free(pd
->replies
);
267 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
268 pa_pdispatch_cb_t callback
;
273 pa_pdispatch_ref(pd
);
275 callback
= r
->callback
;
276 userdata
= r
->userdata
;
281 callback(pd
, command
, tag
, ts
, userdata
);
283 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
284 pd
->drain_callback(pd
, pd
->drain_userdata
);
286 pa_pdispatch_unref(pd
);
289 int pa_pdispatch_run(pa_pdispatch
*pd
, pa_packet
*packet
, const pa_creds
*creds
, void *userdata
) {
290 uint32_t tag
, command
;
291 pa_tagstruct
*ts
= NULL
;
295 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
297 pa_assert(PA_REFCNT_VALUE(packet
) >= 1);
298 pa_assert(packet
->data
);
300 pa_pdispatch_ref(pd
);
302 if (packet
->length
<= 8)
305 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
307 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
308 pa_tagstruct_getu32(ts
, &tag
) < 0)
314 char const *p
= NULL
;
316 if (command
>= PA_COMMAND_MAX
|| !(p
= command_names
[command
]))
317 pa_snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
319 pa_log("[%p] Received opcode <%s>", pd
, p
);
325 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
326 struct reply_info
*r
;
328 PA_LLIST_FOREACH(r
, pd
->replies
)
333 run_action(pd
, r
, command
, ts
);
335 } else if (pd
->callback_table
&& (command
< pd
->n_commands
) && pd
->callback_table
[command
]) {
336 const pa_pdispatch_cb_t
*cb
= pd
->callback_table
+command
;
338 (*cb
)(pd
, command
, tag
, ts
, userdata
);
340 pa_log("Received unsupported command %u", command
);
350 pa_tagstruct_free(ts
);
352 pa_pdispatch_unref(pd
);
357 static void timeout_callback(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
358 struct reply_info
*r
= userdata
;
361 pa_assert(r
->time_event
== e
);
362 pa_assert(r
->pdispatch
);
363 pa_assert(r
->pdispatch
->mainloop
== m
);
364 pa_assert(r
->callback
);
366 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
369 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
) {
370 struct reply_info
*r
;
374 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
377 if (!(r
= pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos
))))
378 r
= pa_xnew(struct reply_info
, 1);
382 r
->userdata
= userdata
;
383 r
->free_cb
= free_cb
;
386 pa_assert_se(r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
,
387 pa_timeval_rtstore(&tv
, pa_rtclock_now() + timeout
* PA_USEC_PER_SEC
, pd
->use_rtclock
),
388 timeout_callback
, r
));
390 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
393 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
395 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
397 return !!pd
->replies
;
400 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, pa_pdispatch_drain_cb_t cb
, void *userdata
) {
402 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
403 pa_assert(!cb
|| pa_pdispatch_is_pending(pd
));
405 pd
->drain_callback
= cb
;
406 pd
->drain_userdata
= userdata
;
409 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
410 struct reply_info
*r
, *n
;
413 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
415 PA_LLIST_FOREACH_SAFE(r
, n
, pd
->replies
)
416 if (r
->userdata
== userdata
)
420 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
422 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
424 if (PA_REFCNT_DEC(pd
) <= 0)
428 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
430 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
436 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
438 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);