4 This file is part of PulseAudio.
6 Copyright 2004-2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as
11 published by the Free Software Foundation; either version 2.1 of the
12 License, or (at your option) any later version.
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public
20 License along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
32 #include <pulse/timeval.h>
33 #include <pulse/xmalloc.h>
35 #include <pulsecore/native-common.h>
36 #include <pulsecore/llist.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/core-util.h>
39 #include <pulsecore/macro.h>
40 #include <pulsecore/refcnt.h>
41 #include <pulsecore/flist.h>
43 #include "pdispatch.h"
45 /*#define DEBUG_OPCODES */
49 static const char *command_names
[PA_COMMAND_MAX
] = {
50 [PA_COMMAND_ERROR
] = "ERROR",
51 [PA_COMMAND_TIMEOUT
] = "TIMEOUT",
52 [PA_COMMAND_REPLY
] = "REPLY",
53 [PA_COMMAND_CREATE_PLAYBACK_STREAM
] = "CREATE_PLAYBACK_STREAM",
54 [PA_COMMAND_DELETE_PLAYBACK_STREAM
] = "DELETE_PLAYBACK_STREAM",
55 [PA_COMMAND_CREATE_RECORD_STREAM
] = "CREATE_RECORD_STREAM",
56 [PA_COMMAND_DELETE_RECORD_STREAM
] = "DELETE_RECORD_STREAM",
57 [PA_COMMAND_AUTH
] = "AUTH",
58 [PA_COMMAND_REQUEST
] = "REQUEST",
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_PLAYBACK_STREAM_KILLED
] = "PLAYBACK_STREAM_KILLED",
65 [PA_COMMAND_RECORD_STREAM_KILLED
] = "RECORD_STREAM_KILLED",
66 [PA_COMMAND_STAT
] = "STAT",
67 [PA_COMMAND_GET_PLAYBACK_LATENCY
] = "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",
73 [PA_COMMAND_GET_SERVER_INFO
] = "GET_SERVER_INFO",
74 [PA_COMMAND_GET_SINK_INFO
] = "GET_SINK_INFO",
75 [PA_COMMAND_GET_SINK_INFO_LIST
] = "GET_SINK_INFO_LIST",
76 [PA_COMMAND_GET_SOURCE_INFO
] = "GET_SOURCE_INFO",
77 [PA_COMMAND_GET_SOURCE_INFO_LIST
] = "GET_SOURCE_INFO_LIST",
78 [PA_COMMAND_GET_MODULE_INFO
] = "GET_MODULE_INFO",
79 [PA_COMMAND_GET_MODULE_INFO_LIST
] = "GET_MODULE_INFO_LIST",
80 [PA_COMMAND_GET_CLIENT_INFO
] = "GET_CLIENT_INFO",
81 [PA_COMMAND_GET_CLIENT_INFO_LIST
] = "GET_CLIENT_INFO_LIST",
82 [PA_COMMAND_GET_SAMPLE_INFO
] = "GET_SAMPLE_INFO",
83 [PA_COMMAND_GET_SAMPLE_INFO_LIST
] = "GET_SAMPLE_INFO_LIST",
84 [PA_COMMAND_GET_SINK_INPUT_INFO
] = "GET_SINK_INPUT_INFO",
85 [PA_COMMAND_GET_SINK_INPUT_INFO_LIST
] = "GET_SINK_INPUT_INFO_LIST",
86 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO
] = "GET_SOURCE_OUTPUT_INFO",
87 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST
] = "GET_SOURCE_OUTPUT_INFO_LIST",
88 [PA_COMMAND_SUBSCRIBE
] = "SUBSCRIBE",
89 [PA_COMMAND_SUBSCRIBE_EVENT
] = "SUBSCRIBE_EVENT",
90 [PA_COMMAND_SET_SINK_VOLUME
] = "SET_SINK_VOLUME",
91 [PA_COMMAND_SET_SINK_INPUT_VOLUME
] = "SET_SINK_INPUT_VOLUME",
92 [PA_COMMAND_SET_SOURCE_VOLUME
] = "SET_SOURCE_VOLME",
93 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM
] = "TRIGGER_PLAYBACK_STREAM",
94 [PA_COMMAND_FLUSH_PLAYBACK_STREAM
] = "FLUSH_PLAYBACK_STREAM",
95 [PA_COMMAND_CORK_PLAYBACK_STREAM
] = "CORK_PLAYBACK_STREAM",
96 [PA_COMMAND_GET_AUTOLOAD_INFO
] = "GET_AUTOLOAD_INFO",
97 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST
] = "GET_AUTOLOAD_INFO_LIST",
102 PA_STATIC_FLIST_DECLARE(reply_infos
, 0, pa_xfree
);
105 pa_pdispatch
*pdispatch
;
106 PA_LLIST_FIELDS(struct reply_info
);
107 pa_pdispatch_cb_t callback
;
109 pa_free_cb_t free_cb
;
111 pa_time_event
*time_event
;
114 struct pa_pdispatch
{
116 pa_mainloop_api
*mainloop
;
117 const pa_pdispatch_cb_t
*callback_table
;
119 PA_LLIST_HEAD(struct reply_info
, replies
);
120 pa_pdispatch_drain_callback drain_callback
;
121 void *drain_userdata
;
122 const pa_creds
*creds
;
125 static void reply_info_free(struct reply_info
*r
) {
127 pa_assert(r
->pdispatch
);
128 pa_assert(r
->pdispatch
->mainloop
);
131 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
133 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
135 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos
), r
) < 0)
139 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
143 pa_assert((entries
&& table
) || (!entries
&& !table
));
145 pd
= pa_xnew(pa_pdispatch
, 1);
147 pd
->mainloop
= mainloop
;
148 pd
->callback_table
= table
;
149 pd
->n_commands
= entries
;
150 PA_LLIST_HEAD_INIT(struct reply_info
, pd
->replies
);
151 pd
->drain_callback
= NULL
;
152 pd
->drain_userdata
= NULL
;
158 static void pdispatch_free(pa_pdispatch
*pd
) {
161 while (pd
->replies
) {
162 if (pd
->replies
->free_cb
)
163 pd
->replies
->free_cb(pd
->replies
->userdata
);
165 reply_info_free(pd
->replies
);
171 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
172 pa_pdispatch_cb_t callback
;
177 pa_pdispatch_ref(pd
);
179 callback
= r
->callback
;
180 userdata
= r
->userdata
;
185 callback(pd
, command
, tag
, ts
, userdata
);
187 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
188 pd
->drain_callback(pd
, pd
->drain_userdata
);
190 pa_pdispatch_unref(pd
);
193 int pa_pdispatch_run(pa_pdispatch
*pd
, pa_packet
*packet
, const pa_creds
*creds
, void *userdata
) {
194 uint32_t tag
, command
;
195 pa_tagstruct
*ts
= NULL
;
199 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
201 pa_assert(PA_REFCNT_VALUE(packet
) >= 1);
202 pa_assert(packet
->data
);
204 pa_pdispatch_ref(pd
);
206 if (packet
->length
<= 8)
209 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
211 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
212 pa_tagstruct_getu32(ts
, &tag
) < 0)
219 if (!(p
= command_names
[command
]))
220 pa_snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
222 pa_log("Recieved opcode <%s>", p
);
228 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
229 struct reply_info
*r
;
231 for (r
= pd
->replies
; r
; r
= r
->next
)
236 run_action(pd
, r
, command
, ts
);
238 } else if (pd
->callback_table
&& (command
< pd
->n_commands
) && pd
->callback_table
[command
]) {
239 const pa_pdispatch_cb_t
*c
= pd
->callback_table
+command
;
241 (*c
)(pd
, command
, tag
, ts
, userdata
);
243 pa_log("Recieved unsupported command %u", command
);
253 pa_tagstruct_free(ts
);
255 pa_pdispatch_unref(pd
);
260 static void timeout_callback(pa_mainloop_api
*m
, pa_time_event
*e
, PA_GCC_UNUSED
const struct timeval
*tv
, void *userdata
) {
261 struct reply_info
*r
= userdata
;
264 pa_assert(r
->time_event
== e
);
265 pa_assert(r
->pdispatch
);
266 pa_assert(r
->pdispatch
->mainloop
== m
);
267 pa_assert(r
->callback
);
269 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
272 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
) {
273 struct reply_info
*r
;
277 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
280 if (!(r
= pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos
))))
281 r
= pa_xnew(struct reply_info
, 1);
285 r
->userdata
= userdata
;
286 r
->free_cb
= free_cb
;
289 pa_gettimeofday(&tv
);
290 tv
.tv_sec
+= timeout
;
292 pa_assert_se(r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, &tv
, timeout_callback
, r
));
294 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
297 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
299 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
301 return !!pd
->replies
;
304 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, void (*cb
)(pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
306 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
307 pa_assert(!cb
|| pa_pdispatch_is_pending(pd
));
309 pd
->drain_callback
= cb
;
310 pd
->drain_userdata
= userdata
;
313 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
314 struct reply_info
*r
, *n
;
317 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
319 for (r
= pd
->replies
; r
; r
= n
) {
322 if (r
->userdata
== userdata
)
327 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
329 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
331 if (PA_REFCNT_DEC(pd
) <= 0)
335 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
337 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
343 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
345 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);