4 This file is part of PulseAudio.
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, write to the Free Software
18 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>
38 #include "pdispatch.h"
40 /*#define DEBUG_OPCODES */
44 static const char *command_names
[PA_COMMAND_MAX
] = {
45 [PA_COMMAND_ERROR
] = "ERROR",
46 [PA_COMMAND_TIMEOUT
] = "TIMEOUT",
47 [PA_COMMAND_REPLY
] = "REPLY",
48 [PA_COMMAND_CREATE_PLAYBACK_STREAM
] = "CREATE_PLAYBACK_STREAM",
49 [PA_COMMAND_DELETE_PLAYBACK_STREAM
] = "DELETE_PLAYBACK_STREAM",
50 [PA_COMMAND_CREATE_RECORD_STREAM
] = "CREATE_RECORD_STREAM",
51 [PA_COMMAND_DELETE_RECORD_STREAM
] = "DELETE_RECORD_STREAM",
52 [PA_COMMAND_AUTH
] = "AUTH",
53 [PA_COMMAND_REQUEST
] = "REQUEST",
54 [PA_COMMAND_EXIT
] = "EXIT",
55 [PA_COMMAND_SET_CLIENT_NAME
] = "SET_CLIENT_NAME",
56 [PA_COMMAND_LOOKUP_SINK
] = "LOOKUP_SINK",
57 [PA_COMMAND_LOOKUP_SOURCE
] = "LOOKUP_SOURCE",
58 [PA_COMMAND_DRAIN_PLAYBACK_STREAM
] = "DRAIN_PLAYBACK_STREAM",
59 [PA_COMMAND_PLAYBACK_STREAM_KILLED
] = "PLAYBACK_STREAM_KILLED",
60 [PA_COMMAND_RECORD_STREAM_KILLED
] = "RECORD_STREAM_KILLED",
61 [PA_COMMAND_STAT
] = "STAT",
62 [PA_COMMAND_GET_PLAYBACK_LATENCY
] = "PLAYBACK_LATENCY",
63 [PA_COMMAND_CREATE_UPLOAD_STREAM
] = "CREATE_UPLOAD_STREAM",
64 [PA_COMMAND_DELETE_UPLOAD_STREAM
] = "DELETE_UPLOAD_STREAM",
65 [PA_COMMAND_FINISH_UPLOAD_STREAM
] = "FINISH_UPLOAD_STREAM",
66 [PA_COMMAND_PLAY_SAMPLE
] = "PLAY_SAMPLE",
67 [PA_COMMAND_REMOVE_SAMPLE
] = "REMOVE_SAMPLE",
68 [PA_COMMAND_GET_SERVER_INFO
] = "GET_SERVER_INFO",
69 [PA_COMMAND_GET_SINK_INFO
] = "GET_SINK_INFO",
70 [PA_COMMAND_GET_SINK_INFO_LIST
] = "GET_SINK_INFO_LIST",
71 [PA_COMMAND_GET_SOURCE_INFO
] = "GET_SOURCE_INFO",
72 [PA_COMMAND_GET_SOURCE_INFO_LIST
] = "GET_SOURCE_INFO_LIST",
73 [PA_COMMAND_GET_MODULE_INFO
] = "GET_MODULE_INFO",
74 [PA_COMMAND_GET_MODULE_INFO_LIST
] = "GET_MODULE_INFO_LIST",
75 [PA_COMMAND_GET_CLIENT_INFO
] = "GET_CLIENT_INFO",
76 [PA_COMMAND_GET_CLIENT_INFO_LIST
] = "GET_CLIENT_INFO_LIST",
77 [PA_COMMAND_GET_SAMPLE_INFO
] = "GET_SAMPLE_INFO",
78 [PA_COMMAND_GET_SAMPLE_INFO_LIST
] = "GET_SAMPLE_INFO_LIST",
79 [PA_COMMAND_GET_SINK_INPUT_INFO
] = "GET_SINK_INPUT_INFO",
80 [PA_COMMAND_GET_SINK_INPUT_INFO_LIST
] = "GET_SINK_INPUT_INFO_LIST",
81 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO
] = "GET_SOURCE_OUTPUT_INFO",
82 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST
] = "GET_SOURCE_OUTPUT_INFO_LIST",
83 [PA_COMMAND_SUBSCRIBE
] = "SUBSCRIBE",
84 [PA_COMMAND_SUBSCRIBE_EVENT
] = "SUBSCRIBE_EVENT",
85 [PA_COMMAND_SET_SINK_VOLUME
] = "SET_SINK_VOLUME",
86 [PA_COMMAND_SET_SINK_INPUT_VOLUME
] = "SET_SINK_INPUT_VOLUME",
87 [PA_COMMAND_SET_SOURCE_VOLUME
] = "SET_SOURCE_VOLME",
88 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM
] = "TRIGGER_PLAYBACK_STREAM",
89 [PA_COMMAND_FLUSH_PLAYBACK_STREAM
] = "FLUSH_PLAYBACK_STREAM",
90 [PA_COMMAND_CORK_PLAYBACK_STREAM
] = "CORK_PLAYBACK_STREAM",
91 [PA_COMMAND_GET_AUTOLOAD_INFO
] = "GET_AUTOLOAD_INFO",
92 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST
] = "GET_AUTOLOAD_INFO_LIST",
98 pa_pdispatch
*pdispatch
;
99 PA_LLIST_FIELDS(struct reply_info
);
100 pa_pdispatch_cb_t callback
;
102 pa_free_cb_t free_cb
;
104 pa_time_event
*time_event
;
107 struct pa_pdispatch
{
109 pa_mainloop_api
*mainloop
;
110 const pa_pdispatch_cb_t
*callback_table
;
112 PA_LLIST_HEAD(struct reply_info
, replies
);
113 pa_pdispatch_drain_callback drain_callback
;
114 void *drain_userdata
;
115 const pa_creds
*creds
;
118 static void reply_info_free(struct reply_info
*r
) {
119 assert(r
&& r
->pdispatch
&& r
->pdispatch
->mainloop
);
122 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
124 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
129 pa_pdispatch
* pa_pdispatch_new(pa_mainloop_api
*mainloop
, const pa_pdispatch_cb_t
*table
, unsigned entries
) {
133 assert((entries
&& table
) || (!entries
&& !table
));
135 pd
= pa_xmalloc(sizeof(pa_pdispatch
));
137 pd
->mainloop
= mainloop
;
138 pd
->callback_table
= table
;
139 pd
->n_commands
= entries
;
140 PA_LLIST_HEAD_INIT(struct reply_info
, pd
->replies
);
141 pd
->drain_callback
= NULL
;
142 pd
->drain_userdata
= NULL
;
148 static void pdispatch_free(pa_pdispatch
*pd
) {
151 while (pd
->replies
) {
152 if (pd
->replies
->free_cb
)
153 pd
->replies
->free_cb(pd
->replies
->userdata
);
155 reply_info_free(pd
->replies
);
161 static void run_action(pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, pa_tagstruct
*ts
) {
162 pa_pdispatch_cb_t callback
;
167 pa_pdispatch_ref(pd
);
169 callback
= r
->callback
;
170 userdata
= r
->userdata
;
175 callback(pd
, command
, tag
, ts
, userdata
);
177 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
178 pd
->drain_callback(pd
, pd
->drain_userdata
);
180 pa_pdispatch_unref(pd
);
183 int pa_pdispatch_run(pa_pdispatch
*pd
, pa_packet
*packet
, const pa_creds
*creds
, void *userdata
) {
184 uint32_t tag
, command
;
185 pa_tagstruct
*ts
= NULL
;
187 assert(pd
&& packet
&& packet
->data
);
189 pa_pdispatch_ref(pd
);
191 if (packet
->length
<= 8)
194 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
197 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
198 pa_tagstruct_getu32(ts
, &tag
) < 0)
205 if (!(p
= command_names
[command
]))
206 snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
208 pa_log("Recieved opcode <%s>", p
);
214 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
215 struct reply_info
*r
;
217 for (r
= pd
->replies
; r
; r
= r
->next
)
222 run_action(pd
, r
, command
, ts
);
224 } else if (pd
->callback_table
&& (command
< pd
->n_commands
) && pd
->callback_table
[command
]) {
225 const pa_pdispatch_cb_t
*c
= pd
->callback_table
+command
;
227 (*c
)(pd
, command
, tag
, ts
, userdata
);
229 pa_log("Recieved unsupported command %u", command
);
239 pa_tagstruct_free(ts
);
241 pa_pdispatch_unref(pd
);
246 static void timeout_callback(pa_mainloop_api
*m
, pa_time_event
*e
, PA_GCC_UNUSED
const struct timeval
*tv
, void *userdata
) {
247 struct reply_info
*r
= userdata
;
248 assert(r
&& r
->time_event
== e
&& r
->pdispatch
&& r
->pdispatch
->mainloop
== m
&& r
->callback
);
250 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
253 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
) {
254 struct reply_info
*r
;
256 assert(pd
&& pd
->ref
>= 1 && cb
);
258 r
= pa_xmalloc(sizeof(struct reply_info
));
261 r
->userdata
= userdata
;
262 r
->free_cb
= free_cb
;
265 pa_gettimeofday(&tv
);
266 tv
.tv_sec
+= timeout
;
268 r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, &tv
, timeout_callback
, r
);
269 assert(r
->time_event
);
271 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
274 int pa_pdispatch_is_pending(pa_pdispatch
*pd
) {
277 return !!pd
->replies
;
280 void pa_pdispatch_set_drain_callback(pa_pdispatch
*pd
, void (*cb
)(pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
282 assert(!cb
|| pa_pdispatch_is_pending(pd
));
284 pd
->drain_callback
= cb
;
285 pd
->drain_userdata
= userdata
;
288 void pa_pdispatch_unregister_reply(pa_pdispatch
*pd
, void *userdata
) {
289 struct reply_info
*r
, *n
;
292 for (r
= pd
->replies
; r
; r
= n
) {
295 if (r
->userdata
== userdata
)
300 void pa_pdispatch_unref(pa_pdispatch
*pd
) {
301 assert(pd
&& pd
->ref
>= 1);
307 pa_pdispatch
* pa_pdispatch_ref(pa_pdispatch
*pd
) {
308 assert(pd
&& pd
->ref
>= 1);
313 const pa_creds
* pa_pdispatch_creds(pa_pdispatch
*pd
) {
315 assert(pd
->ref
>= 1);