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
33 #include <pulse/timeval.h>
34 #include <pulse/xmalloc.h>
36 #include <pulsecore/native-common.h>
37 #include <pulsecore/llist.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/core-util.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",
101 pa_pdispatch
*pdispatch
;
102 PA_LLIST_FIELDS(struct reply_info
);
103 pa_pdispatch_cb_t callback
;
105 pa_free_cb_t free_cb
;
107 pa_time_event
*time_event
;
110 struct pa_pdispatch
{
112 pa_mainloop_api
*mainloop
;
113 const pa_pdispatch_cb_t
*callback_table
;
115 PA_LLIST_HEAD(struct reply_info
, replies
);
116 pa_pdispatch_drain_callback drain_callback
;
117 void *drain_userdata
;
118 const pa_creds
*creds
;
121 static void reply_info_free(struct reply_info
*r
) {
122 assert(r
&& r
->pdispatch
&& r
->pdispatch
->mainloop
);
125 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
127 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
132 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
136 assert((entries
&& table
) || (!entries
&& !table
));
138 pd
= pa_xmalloc(sizeof(pa_pdispatch
));
140 pd
->mainloop
= mainloop
;
141 pd
->callback_table
= table
;
142 pd
->n_commands
= entries
;
143 PA_LLIST_HEAD_INIT(struct reply_info
, pd
->replies
);
144 pd
->drain_callback
= NULL
;
145 pd
->drain_userdata
= NULL
;
151 static void pdispatch_free(pa_pdispatch
*pd
) {
154 while (pd
->replies
) {
155 if (pd
->replies
->free_cb
)
156 pd
->replies
->free_cb(pd
->replies
->userdata
);
158 reply_info_free(pd
->replies
);
164 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
165 pa_pdispatch_cb_t callback
;
170 pa_pdispatch_ref(pd
);
172 callback
= r
->callback
;
173 userdata
= r
->userdata
;
178 callback(pd
, command
, tag
, ts
, userdata
);
180 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
181 pd
->drain_callback(pd
, pd
->drain_userdata
);
183 pa_pdispatch_unref(pd
);
186 int pa_pdispatch_run(pa_pdispatch
*pd
, pa_packet
*packet
, const pa_creds
*creds
, void *userdata
) {
187 uint32_t tag
, command
;
188 pa_tagstruct
*ts
= NULL
;
190 assert(pd
&& packet
&& packet
->data
);
192 pa_pdispatch_ref(pd
);
194 if (packet
->length
<= 8)
197 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
200 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
201 pa_tagstruct_getu32(ts
, &tag
) < 0)
208 if (!(p
= command_names
[command
]))
209 snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
211 pa_log("Recieved opcode <%s>", p
);
217 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
218 struct reply_info
*r
;
220 for (r
= pd
->replies
; r
; r
= r
->next
)
225 run_action(pd
, r
, command
, ts
);
227 } else if (pd
->callback_table
&& (command
< pd
->n_commands
) && pd
->callback_table
[command
]) {
228 const pa_pdispatch_cb_t
*c
= pd
->callback_table
+command
;
230 (*c
)(pd
, command
, tag
, ts
, userdata
);
232 pa_log("Recieved unsupported command %u", command
);
242 pa_tagstruct_free(ts
);
244 pa_pdispatch_unref(pd
);
249 static void timeout_callback(pa_mainloop_api
*m
, pa_time_event
*e
, PA_GCC_UNUSED
const struct timeval
*tv
, void *userdata
) {
250 struct reply_info
*r
= userdata
;
251 assert(r
&& r
->time_event
== e
&& r
->pdispatch
&& r
->pdispatch
->mainloop
== m
&& r
->callback
);
253 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
256 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
) {
257 struct reply_info
*r
;
259 assert(pd
&& pd
->ref
>= 1 && cb
);
261 r
= pa_xnew(struct reply_info
, 1);
264 r
->userdata
= userdata
;
265 r
->free_cb
= free_cb
;
268 pa_gettimeofday(&tv
);
269 tv
.tv_sec
+= timeout
;
271 r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, &tv
, timeout_callback
, r
);
272 assert(r
->time_event
);
274 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
277 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
280 return !!pd
->replies
;
283 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, void (*cb
)(pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
285 assert(!cb
|| pa_pdispatch_is_pending(pd
));
287 pd
->drain_callback
= cb
;
288 pd
->drain_userdata
= userdata
;
291 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
292 struct reply_info
*r
, *n
;
295 for (r
= pd
->replies
; r
; r
= n
) {
298 if (r
->userdata
== userdata
)
303 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
304 assert(pd
&& pd
->ref
>= 1);
310 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
311 assert(pd
&& pd
->ref
>= 1);
316 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
318 assert(pd
->ref
>= 1);