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",
171 [PA_COMMAND_GET_CARD_INFO
] = "GET_CARD_INFO",
172 [PA_COMMAND_GET_CARD_INFO_LIST
] = "GET_CARD_INFO_LIST",
173 [PA_COMMAND_SET_CARD_PROFILE
] = "SET_CARD_PROFILE",
175 [PA_COMMAND_CLIENT_EVENT
] = "GET_CLIENT_EVENT",
176 [PA_COMMAND_PLAYBACK_STREAM_EVENT
] = "PLAYBACK_STREAM_EVENT",
177 [PA_COMMAND_RECORD_STREAM_EVENT
] = "RECORD_STREAM_EVENT",
180 [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED
] = "PLAYBACK_BUFFER_ATTR_CHANGED",
181 [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED
] = "RECORD_BUFFER_ATTR_CHANGED"
186 PA_STATIC_FLIST_DECLARE(reply_infos
, 0, pa_xfree
);
189 pa_pdispatch
*pdispatch
;
190 PA_LLIST_FIELDS(struct reply_info
);
191 pa_pdispatch_cb_t callback
;
193 pa_free_cb_t free_cb
;
195 pa_time_event
*time_event
;
198 struct pa_pdispatch
{
200 pa_mainloop_api
*mainloop
;
201 const pa_pdispatch_cb_t
*callback_table
;
203 PA_LLIST_HEAD(struct reply_info
, replies
);
204 pa_pdispatch_drain_callback drain_callback
;
205 void *drain_userdata
;
206 const pa_creds
*creds
;
209 static void reply_info_free(struct reply_info
*r
) {
211 pa_assert(r
->pdispatch
);
212 pa_assert(r
->pdispatch
->mainloop
);
215 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
217 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
219 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos
), r
) < 0)
223 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
227 pa_assert((entries
&& table
) || (!entries
&& !table
));
229 pd
= pa_xnew(pa_pdispatch
, 1);
231 pd
->mainloop
= mainloop
;
232 pd
->callback_table
= table
;
233 pd
->n_commands
= entries
;
234 PA_LLIST_HEAD_INIT(struct reply_info
, pd
->replies
);
235 pd
->drain_callback
= NULL
;
236 pd
->drain_userdata
= NULL
;
242 static void pdispatch_free(pa_pdispatch
*pd
) {
245 while (pd
->replies
) {
246 if (pd
->replies
->free_cb
)
247 pd
->replies
->free_cb(pd
->replies
->userdata
);
249 reply_info_free(pd
->replies
);
255 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
256 pa_pdispatch_cb_t callback
;
261 pa_pdispatch_ref(pd
);
263 callback
= r
->callback
;
264 userdata
= r
->userdata
;
269 callback(pd
, command
, tag
, ts
, userdata
);
271 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
272 pd
->drain_callback(pd
, pd
->drain_userdata
);
274 pa_pdispatch_unref(pd
);
277 int pa_pdispatch_run(pa_pdispatch
*pd
, pa_packet
*packet
, const pa_creds
*creds
, void *userdata
) {
278 uint32_t tag
, command
;
279 pa_tagstruct
*ts
= NULL
;
283 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
285 pa_assert(PA_REFCNT_VALUE(packet
) >= 1);
286 pa_assert(packet
->data
);
288 pa_pdispatch_ref(pd
);
290 if (packet
->length
<= 8)
293 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
295 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
296 pa_tagstruct_getu32(ts
, &tag
) < 0)
302 char const *p
= NULL
;
304 if (command
>= PA_COMMAND_MAX
|| !(p
= command_names
[command
]))
305 pa_snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
307 pa_log("[%p] Received opcode <%s>", pd
, p
);
313 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
314 struct reply_info
*r
;
316 for (r
= pd
->replies
; r
; r
= r
->next
)
321 run_action(pd
, r
, command
, ts
);
323 } else if (pd
->callback_table
&& (command
< pd
->n_commands
) && pd
->callback_table
[command
]) {
324 const pa_pdispatch_cb_t
*c
= pd
->callback_table
+command
;
326 (*c
)(pd
, command
, tag
, ts
, userdata
);
328 pa_log("Received unsupported command %u", command
);
338 pa_tagstruct_free(ts
);
340 pa_pdispatch_unref(pd
);
345 static void timeout_callback(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*tv
, void *userdata
) {
346 struct reply_info
*r
= userdata
;
349 pa_assert(r
->time_event
== e
);
350 pa_assert(r
->pdispatch
);
351 pa_assert(r
->pdispatch
->mainloop
== m
);
352 pa_assert(r
->callback
);
354 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
357 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
) {
358 struct reply_info
*r
;
362 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
365 if (!(r
= pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos
))))
366 r
= pa_xnew(struct reply_info
, 1);
370 r
->userdata
= userdata
;
371 r
->free_cb
= free_cb
;
374 pa_gettimeofday(&tv
);
375 tv
.tv_sec
+= timeout
;
377 pa_assert_se(r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, &tv
, timeout_callback
, r
));
379 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
382 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
384 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
386 return !!pd
->replies
;
389 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, void (*cb
)(pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
391 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
392 pa_assert(!cb
|| pa_pdispatch_is_pending(pd
));
394 pd
->drain_callback
= cb
;
395 pd
->drain_userdata
= userdata
;
398 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
399 struct reply_info
*r
, *n
;
402 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
404 for (r
= pd
->replies
; r
; r
= n
) {
407 if (r
->userdata
== userdata
)
412 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
414 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
416 if (PA_REFCNT_DEC(pd
) <= 0)
420 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
422 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
428 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
430 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);