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 [PA_COMMAND_ERROR
] = "ERROR",
49 [PA_COMMAND_TIMEOUT
] = "TIMEOUT",
50 [PA_COMMAND_REPLY
] = "REPLY",
51 [PA_COMMAND_CREATE_PLAYBACK_STREAM
] = "CREATE_PLAYBACK_STREAM",
52 [PA_COMMAND_DELETE_PLAYBACK_STREAM
] = "DELETE_PLAYBACK_STREAM",
53 [PA_COMMAND_CREATE_RECORD_STREAM
] = "CREATE_RECORD_STREAM",
54 [PA_COMMAND_DELETE_RECORD_STREAM
] = "DELETE_RECORD_STREAM",
55 [PA_COMMAND_AUTH
] = "AUTH",
56 [PA_COMMAND_REQUEST
] = "REQUEST",
57 [PA_COMMAND_EXIT
] = "EXIT",
58 [PA_COMMAND_SET_CLIENT_NAME
] = "SET_CLIENT_NAME",
59 [PA_COMMAND_LOOKUP_SINK
] = "LOOKUP_SINK",
60 [PA_COMMAND_LOOKUP_SOURCE
] = "LOOKUP_SOURCE",
61 [PA_COMMAND_DRAIN_PLAYBACK_STREAM
] = "DRAIN_PLAYBACK_STREAM",
62 [PA_COMMAND_PLAYBACK_STREAM_KILLED
] = "PLAYBACK_STREAM_KILLED",
63 [PA_COMMAND_RECORD_STREAM_KILLED
] = "RECORD_STREAM_KILLED",
64 [PA_COMMAND_STAT
] = "STAT",
65 [PA_COMMAND_GET_PLAYBACK_LATENCY
] = "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",
71 [PA_COMMAND_GET_SERVER_INFO
] = "GET_SERVER_INFO",
72 [PA_COMMAND_GET_SINK_INFO
] = "GET_SINK_INFO",
73 [PA_COMMAND_GET_SINK_INFO_LIST
] = "GET_SINK_INFO_LIST",
74 [PA_COMMAND_GET_SOURCE_INFO
] = "GET_SOURCE_INFO",
75 [PA_COMMAND_GET_SOURCE_INFO_LIST
] = "GET_SOURCE_INFO_LIST",
76 [PA_COMMAND_GET_MODULE_INFO
] = "GET_MODULE_INFO",
77 [PA_COMMAND_GET_MODULE_INFO_LIST
] = "GET_MODULE_INFO_LIST",
78 [PA_COMMAND_GET_CLIENT_INFO
] = "GET_CLIENT_INFO",
79 [PA_COMMAND_GET_CLIENT_INFO_LIST
] = "GET_CLIENT_INFO_LIST",
80 [PA_COMMAND_GET_SAMPLE_INFO
] = "GET_SAMPLE_INFO",
81 [PA_COMMAND_GET_SAMPLE_INFO_LIST
] = "GET_SAMPLE_INFO_LIST",
82 [PA_COMMAND_GET_SINK_INPUT_INFO
] = "GET_SINK_INPUT_INFO",
83 [PA_COMMAND_GET_SINK_INPUT_INFO_LIST
] = "GET_SINK_INPUT_INFO_LIST",
84 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO
] = "GET_SOURCE_OUTPUT_INFO",
85 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST
] = "GET_SOURCE_OUTPUT_INFO_LIST",
86 [PA_COMMAND_SUBSCRIBE
] = "SUBSCRIBE",
87 [PA_COMMAND_SUBSCRIBE_EVENT
] = "SUBSCRIBE_EVENT",
88 [PA_COMMAND_SET_SINK_VOLUME
] = "SET_SINK_VOLUME",
89 [PA_COMMAND_SET_SINK_INPUT_VOLUME
] = "SET_SINK_INPUT_VOLUME",
90 [PA_COMMAND_SET_SOURCE_VOLUME
] = "SET_SOURCE_VOLME",
91 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM
] = "TRIGGER_PLAYBACK_STREAM",
92 [PA_COMMAND_FLUSH_PLAYBACK_STREAM
] = "FLUSH_PLAYBACK_STREAM",
93 [PA_COMMAND_CORK_PLAYBACK_STREAM
] = "CORK_PLAYBACK_STREAM",
94 [PA_COMMAND_GET_AUTOLOAD_INFO
] = "GET_AUTOLOAD_INFO",
95 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST
] = "GET_AUTOLOAD_INFO_LIST",
100 PA_STATIC_FLIST_DECLARE(reply_infos
, 0, pa_xfree
);
103 pa_pdispatch
*pdispatch
;
104 PA_LLIST_FIELDS(struct reply_info
);
105 pa_pdispatch_cb_t callback
;
107 pa_free_cb_t free_cb
;
109 pa_time_event
*time_event
;
112 struct pa_pdispatch
{
114 pa_mainloop_api
*mainloop
;
115 const pa_pdispatch_cb_t
*callback_table
;
117 PA_LLIST_HEAD(struct reply_info
, replies
);
118 pa_pdispatch_drain_callback drain_callback
;
119 void *drain_userdata
;
120 const pa_creds
*creds
;
123 static void reply_info_free(struct reply_info
*r
) {
125 pa_assert(r
->pdispatch
);
126 pa_assert(r
->pdispatch
->mainloop
);
129 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
131 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
133 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos
), r
) < 0)
137 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
141 pa_assert((entries
&& table
) || (!entries
&& !table
));
143 pd
= pa_xnew(pa_pdispatch
, 1);
145 pd
->mainloop
= mainloop
;
146 pd
->callback_table
= table
;
147 pd
->n_commands
= entries
;
148 PA_LLIST_HEAD_INIT(struct reply_info
, pd
->replies
);
149 pd
->drain_callback
= NULL
;
150 pd
->drain_userdata
= NULL
;
156 static void pdispatch_free(pa_pdispatch
*pd
) {
159 while (pd
->replies
) {
160 if (pd
->replies
->free_cb
)
161 pd
->replies
->free_cb(pd
->replies
->userdata
);
163 reply_info_free(pd
->replies
);
169 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
170 pa_pdispatch_cb_t callback
;
175 pa_pdispatch_ref(pd
);
177 callback
= r
->callback
;
178 userdata
= r
->userdata
;
183 callback(pd
, command
, tag
, ts
, userdata
);
185 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
186 pd
->drain_callback(pd
, pd
->drain_userdata
);
188 pa_pdispatch_unref(pd
);
191 int pa_pdispatch_run(pa_pdispatch
*pd
, pa_packet
*packet
, const pa_creds
*creds
, void *userdata
) {
192 uint32_t tag
, command
;
193 pa_tagstruct
*ts
= NULL
;
197 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
199 pa_assert(PA_REFCNT_VALUE(packet
) >= 1);
200 pa_assert(packet
->data
);
202 pa_pdispatch_ref(pd
);
204 if (packet
->length
<= 8)
207 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
209 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
210 pa_tagstruct_getu32(ts
, &tag
) < 0)
217 if (!(p
= command_names
[command
]))
218 pa_snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
220 pa_log("Recieved opcode <%s>", p
);
226 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
227 struct reply_info
*r
;
229 for (r
= pd
->replies
; r
; r
= r
->next
)
234 run_action(pd
, r
, command
, ts
);
236 } else if (pd
->callback_table
&& (command
< pd
->n_commands
) && pd
->callback_table
[command
]) {
237 const pa_pdispatch_cb_t
*c
= pd
->callback_table
+command
;
239 (*c
)(pd
, command
, tag
, ts
, userdata
);
241 pa_log("Recieved unsupported command %u", command
);
251 pa_tagstruct_free(ts
);
253 pa_pdispatch_unref(pd
);
258 static void timeout_callback(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*tv
, void *userdata
) {
259 struct reply_info
*r
= userdata
;
262 pa_assert(r
->time_event
== e
);
263 pa_assert(r
->pdispatch
);
264 pa_assert(r
->pdispatch
->mainloop
== m
);
265 pa_assert(r
->callback
);
267 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
270 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
) {
271 struct reply_info
*r
;
275 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
278 if (!(r
= pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos
))))
279 r
= pa_xnew(struct reply_info
, 1);
283 r
->userdata
= userdata
;
284 r
->free_cb
= free_cb
;
287 pa_gettimeofday(&tv
);
288 tv
.tv_sec
+= timeout
;
290 pa_assert_se(r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, &tv
, timeout_callback
, r
));
292 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
295 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
297 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
299 return !!pd
->replies
;
302 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, void (*cb
)(pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
304 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
305 pa_assert(!cb
|| pa_pdispatch_is_pending(pd
));
307 pd
->drain_callback
= cb
;
308 pd
->drain_userdata
= userdata
;
311 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
312 struct reply_info
*r
, *n
;
315 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
317 for (r
= pd
->replies
; r
; r
= n
) {
320 if (r
->userdata
== userdata
)
325 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
327 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
329 if (PA_REFCNT_DEC(pd
) <= 0)
333 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
335 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);
341 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
343 pa_assert(PA_REFCNT_VALUE(pd
) >= 1);