4 This file is part of polypaudio.
6 polypaudio 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 polypaudio 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 polypaudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
30 #include "pdispatch.h"
31 #include "native-common.h"
36 /* #define DEBUG_OPCODES */
40 static const char *command_names
[PA_COMMAND_MAX
] = {
41 [PA_COMMAND_ERROR
] = "ERROR",
42 [PA_COMMAND_TIMEOUT
] = "TIMEOUT",
43 [PA_COMMAND_REPLY
] = "REPLY",
44 [PA_COMMAND_CREATE_PLAYBACK_STREAM
] = "CREATE_PLAYBACK_STREAM",
45 [PA_COMMAND_DELETE_PLAYBACK_STREAM
] = "DELETE_PLAYBACK_STREAM",
46 [PA_COMMAND_CREATE_RECORD_STREAM
] = "CREATE_RECORD_STREAM",
47 [PA_COMMAND_DELETE_RECORD_STREAM
] = "DELETE_RECORD_STREAM",
48 [PA_COMMAND_AUTH
] = "AUTH",
49 [PA_COMMAND_REQUEST
] = "REQUEST",
50 [PA_COMMAND_EXIT
] = "EXIT",
51 [PA_COMMAND_SET_CLIENT_NAME
] = "SET_CLIENT_NAME",
52 [PA_COMMAND_LOOKUP_SINK
] = "LOOKUP_SINK",
53 [PA_COMMAND_LOOKUP_SOURCE
] = "LOOKUP_SOURCE",
54 [PA_COMMAND_DRAIN_PLAYBACK_STREAM
] = "DRAIN_PLAYBACK_STREAM",
55 [PA_COMMAND_PLAYBACK_STREAM_KILLED
] = "PLAYBACK_STREAM_KILLED",
56 [PA_COMMAND_RECORD_STREAM_KILLED
] = "RECORD_STREAM_KILLED",
57 [PA_COMMAND_STAT
] = "STAT",
58 [PA_COMMAND_GET_PLAYBACK_LATENCY
] = "PLAYBACK_LATENCY",
59 [PA_COMMAND_CREATE_UPLOAD_STREAM
] = "CREATE_UPLOAD_STREAM",
60 [PA_COMMAND_DELETE_UPLOAD_STREAM
] = "DELETE_UPLOAD_STREAM",
61 [PA_COMMAND_FINISH_UPLOAD_STREAM
] = "FINISH_UPLOAD_STREAM",
62 [PA_COMMAND_PLAY_SAMPLE
] = "PLAY_SAMPLE",
63 [PA_COMMAND_REMOVE_SAMPLE
] = "REMOVE_SAMPLE",
64 [PA_COMMAND_GET_SERVER_INFO
] = "GET_SERVER_INFO",
65 [PA_COMMAND_GET_SINK_INFO
] = "GET_SINK_INFO",
66 [PA_COMMAND_GET_SINK_INFO_LIST
] = "GET_SINK_INFO_LIST",
67 [PA_COMMAND_GET_SOURCE_INFO
] = "GET_SOURCE_INFO",
68 [PA_COMMAND_GET_SOURCE_INFO_LIST
] = "GET_SOURCE_INFO_LIST",
69 [PA_COMMAND_GET_MODULE_INFO
] = "GET_MODULE_INFO",
70 [PA_COMMAND_GET_MODULE_INFO_LIST
] = "GET_MODULE_INFO_LIST",
71 [PA_COMMAND_GET_CLIENT_INFO
] = "GET_CLIENT_INFO",
72 [PA_COMMAND_GET_CLIENT_INFO_LIST
] = "GET_CLIENT_INFO_LIST",
73 [PA_COMMAND_GET_SAMPLE_INFO
] = "GET_SAMPLE_INFO",
74 [PA_COMMAND_GET_SAMPLE_INFO_LIST
] = "GET_SAMPLE_INFO_LIST",
75 [PA_COMMAND_GET_SINK_INPUT_INFO
] = "GET_SINK_INPUT_INFO",
76 [PA_COMMAND_GET_SINK_INPUT_INFO_LIST
] = "GET_SINK_INPUT_INFO_LIST",
77 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO
] = "GET_SOURCE_OUTPUT_INFO",
78 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST
] = "GET_SOURCE_OUTPUT_INFO_LIST",
79 [PA_COMMAND_SUBSCRIBE
] = "SUBSCRIBE",
80 [PA_COMMAND_SUBSCRIBE_EVENT
] = "SUBSCRIBE_EVENT",
81 [PA_COMMAND_SET_SINK_VOLUME
] = "SET_SINK_VOLUME",
82 [PA_COMMAND_SET_SINK_INPUT_VOLUME
] = "SET_SINK_INPUT_VOLUME",
83 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM
] = "TRIGGER_PLAYBACK_STREAM",
84 [PA_COMMAND_FLUSH_PLAYBACK_STREAM
] = "FLUSH_PLAYBACK_STREAM",
85 [PA_COMMAND_CORK_PLAYBACK_STREAM
] = "CORK_PLAYBACK_STREAM",
86 [PA_COMMAND_GET_AUTOLOAD_INFO
] = "GET_AUTOLOAD_INFO",
87 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST
] = "GET_AUTOLOAD_INFO_LIST",
93 struct pa_pdispatch
*pdispatch
;
94 PA_LLIST_FIELDS(struct reply_info
);
95 void (*callback
)(struct pa_pdispatch
*pd
, uint32_t command
, uint32_t tag
, struct pa_tagstruct
*t
, void *userdata
);
98 struct pa_time_event
*time_event
;
101 struct pa_pdispatch
{
103 struct pa_mainloop_api
*mainloop
;
104 const struct pa_pdispatch_command
*command_table
;
106 PA_LLIST_HEAD(struct reply_info
, replies
);
107 void (*drain_callback
)(struct pa_pdispatch
*pd
, void *userdata
);
108 void *drain_userdata
;
111 static void reply_info_free(struct reply_info
*r
) {
112 assert(r
&& r
->pdispatch
&& r
->pdispatch
->mainloop
);
115 r
->pdispatch
->mainloop
->time_free(r
->time_event
);
117 PA_LLIST_REMOVE(struct reply_info
, r
->pdispatch
->replies
, r
);
122 struct pa_pdispatch
* pa_pdispatch_new(struct pa_mainloop_api
*mainloop
, const struct pa_pdispatch_command
*table
, unsigned entries
) {
123 struct pa_pdispatch
*pd
;
126 assert((entries
&& table
) || (!entries
&& !table
));
128 pd
= pa_xmalloc(sizeof(struct pa_pdispatch
));
130 pd
->mainloop
= mainloop
;
131 pd
->command_table
= table
;
132 pd
->n_commands
= entries
;
133 PA_LLIST_HEAD_INIT(struct pa_reply_info
, pd
->replies
);
134 pd
->drain_callback
= NULL
;
135 pd
->drain_userdata
= NULL
;
140 void pdispatch_free(struct pa_pdispatch
*pd
) {
144 reply_info_free(pd
->replies
);
149 static void run_action(struct pa_pdispatch
*pd
, struct reply_info
*r
, uint32_t command
, struct pa_tagstruct
*ts
) {
150 void (*callback
)(struct pa_pdispatch
*pd
, uint32_t command
, uint32_t tag
, struct pa_tagstruct
*t
, void *userdata
);
155 pa_pdispatch_ref(pd
);
157 callback
= r
->callback
;
158 userdata
= r
->userdata
;
163 callback(pd
, command
, tag
, ts
, userdata
);
165 if (pd
->drain_callback
&& !pa_pdispatch_is_pending(pd
))
166 pd
->drain_callback(pd
, pd
->drain_userdata
);
168 pa_pdispatch_unref(pd
);
171 int pa_pdispatch_run(struct pa_pdispatch
*pd
, struct pa_packet
*packet
, void *userdata
) {
172 uint32_t tag
, command
;
173 struct pa_tagstruct
*ts
= NULL
;
175 assert(pd
&& packet
&& packet
->data
);
177 pa_pdispatch_ref(pd
);
179 if (packet
->length
<= 8)
182 ts
= pa_tagstruct_new(packet
->data
, packet
->length
);
185 if (pa_tagstruct_getu32(ts
, &command
) < 0 ||
186 pa_tagstruct_getu32(ts
, &tag
) < 0)
193 if (!(p
= command_names
[command
]))
194 snprintf((char*) (p
= t
), sizeof(t
), "%u", command
);
196 pa_log(__FILE__
": Recieved opcode <%s>\n", p
);
200 if (command
== PA_COMMAND_ERROR
|| command
== PA_COMMAND_REPLY
) {
201 struct reply_info
*r
;
203 for (r
= pd
->replies
; r
; r
= r
->next
)
208 run_action(pd
, r
, command
, ts
);
210 } else if (pd
->command_table
&& (command
< pd
->n_commands
) && pd
->command_table
[command
].proc
) {
211 const struct pa_pdispatch_command
*c
= pd
->command_table
+command
;
213 c
->proc(pd
, command
, tag
, ts
, userdata
);
215 pa_log(__FILE__
": Recieved unsupported command %u\n", command
);
223 pa_tagstruct_free(ts
);
225 pa_pdispatch_unref(pd
);
230 static void timeout_callback(struct pa_mainloop_api
*m
, struct pa_time_event
*e
, const struct timeval
*tv
, void *userdata
) {
231 struct reply_info
*r
= userdata
;
232 assert(r
&& r
->time_event
== e
&& r
->pdispatch
&& r
->pdispatch
->mainloop
== m
&& r
->callback
);
234 run_action(r
->pdispatch
, r
, PA_COMMAND_TIMEOUT
, NULL
);
237 void pa_pdispatch_register_reply(struct pa_pdispatch
*pd
, uint32_t tag
, int timeout
, void (*cb
)(struct pa_pdispatch
*pd
, uint32_t command
, uint32_t tag
, struct pa_tagstruct
*t
, void *userdata
), void *userdata
) {
238 struct reply_info
*r
;
240 assert(pd
&& pd
->ref
>= 1 && cb
);
242 r
= pa_xmalloc(sizeof(struct reply_info
));
245 r
->userdata
= userdata
;
248 gettimeofday(&tv
, NULL
);
249 tv
.tv_sec
+= timeout
;
251 r
->time_event
= pd
->mainloop
->time_new(pd
->mainloop
, &tv
, timeout_callback
, r
);
252 assert(r
->time_event
);
254 PA_LLIST_PREPEND(struct reply_info
, pd
->replies
, r
);
257 int pa_pdispatch_is_pending(struct pa_pdispatch
*pd
) {
260 return !!pd
->replies
;
263 void pa_pdispatch_set_drain_callback(struct pa_pdispatch
*pd
, void (*cb
)(struct pa_pdispatch
*pd
, void *userdata
), void *userdata
) {
265 assert(!cb
|| pa_pdispatch_is_pending(pd
));
267 pd
->drain_callback
= cb
;
268 pd
->drain_userdata
= userdata
;
271 void pa_pdispatch_unregister_reply(struct pa_pdispatch
*pd
, void *userdata
) {
272 struct reply_info
*r
, *n
;
275 for (r
= pd
->replies
; r
; r
= n
) {
278 if (r
->userdata
== userdata
)
283 void pa_pdispatch_unref(struct pa_pdispatch
*pd
) {
284 assert(pd
&& pd
->ref
>= 1);
290 struct pa_pdispatch
* pa_pdispatch_ref(struct pa_pdispatch
*pd
) {
291 assert(pd
&& pd
->ref
>= 1);