source: mainline/uspace/lib/hound/src/protocol.c@ 6606378

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6606378 was a9aae16e, checked in by Jan Vesely <jano.vesely@…>, 13 years ago

libhoud: Handle hangup in connection callback.

  • Property mode set to 100644
File size: 9.8 KB
Line 
1/*
2 * Copyright (c) 2012 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libhound
30 * @addtogroup audio
31 * @{
32 */
33/** @file
34 * Common USB functions.
35 */
36#include <errno.h>
37#include <loc.h>
38#include <str.h>
39#include <stdlib.h>
40
41#include "client.h"
42#include "server.h"
43
44const char *HOUND_SERVICE = "audio/hound";
45
46/***
47 * CLIENT SIDE
48 ***/
49
50typedef struct {
51 data_callback_t cb;
52 void *arg;
53} callback_t;
54
55
56static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
57 unsigned channels, unsigned rate, pcm_sample_format_t format,
58 data_callback_t data_callback, void *arg);
59static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name);
60static int hound_connection(hound_sess_t *sess, unsigned cmd,
61 const char *source, const char *sink);
62static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg);
63static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg);
64
65
66hound_sess_t *hound_get_session(void)
67{
68 service_id_t id = 0;
69 const int ret = loc_service_get_id(HOUND_SERVICE, &id, 0);
70 if (ret != EOK)
71 return NULL;
72 return loc_service_connect(EXCHANGE_SERIALIZE, id, 0);
73}
74void hound_release_session(hound_sess_t *sess)
75{
76 if (sess)
77 async_hangup(sess);
78}
79int hound_register_playback(hound_sess_t *sess, const char *name,
80 unsigned channels, unsigned rate, pcm_sample_format_t format,
81 data_callback_t data_callback, void *arg)
82{
83 return hound_register(sess, HOUND_REGISTER_PLAYBACK, name, channels,
84 rate, format, data_callback, arg);
85}
86int hound_register_recording(hound_sess_t *sess, const char *name,
87 unsigned channels, unsigned rate, pcm_sample_format_t format,
88 data_callback_t data_callback, void *arg)
89{
90 return hound_register(sess, HOUND_REGISTER_RECORDING, name, channels,
91 rate, format, data_callback, arg);
92
93}
94int hound_unregister_playback(hound_sess_t *sess, const char *name)
95{
96 return hound_unregister(sess, HOUND_UNREGISTER_PLAYBACK, name);
97}
98int hound_unregister_recording(hound_sess_t *sess, const char *name)
99{
100 return hound_unregister(sess, HOUND_UNREGISTER_RECORDING, name);
101}
102int hound_create_connection(hound_sess_t *sess, const char *source, const char *sink)
103{
104 return hound_connection(sess, HOUND_CONNECT, source, sink);
105}
106int hound_destroy_connection(hound_sess_t *sess, const char *source, const char *sink)
107{
108 return hound_connection(sess, HOUND_DISCONNECT, source, sink);
109}
110
111static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
112 unsigned channels, unsigned rate, pcm_sample_format_t format,
113 data_callback_t data_callback, void *arg)
114{
115 assert(cmd == HOUND_REGISTER_PLAYBACK || cmd == HOUND_REGISTER_RECORDING);
116 if (!name || !data_callback || !sess)
117 return EINVAL;
118
119 async_exch_t *exch = async_exchange_begin(sess);
120 if (!exch)
121 return ENOMEM;
122
123 aid_t id = async_send_0(exch, cmd, NULL);
124
125 int ret = async_data_write_start(exch, name, str_size(name));
126 if (ret != EOK) {
127 async_forget(id);
128 async_exchange_end(exch);
129 return ret;
130 }
131
132 callback_t *cb = malloc(sizeof(callback_t));
133 if (!cb) {
134 async_forget(id);
135 async_exchange_end(exch);
136 return ENOMEM;
137 }
138
139 cb->cb = data_callback;
140 cb->arg = arg;
141 async_client_conn_t callback =
142 (cmd == HOUND_REGISTER_PLAYBACK) ? callback_pb : callback_rec;
143
144 ret = async_connect_to_me(exch, channels, rate, format, callback, cb);
145 if (ret != EOK) {
146 async_forget(id);
147 async_exchange_end(exch);
148 free(cb);
149 return ret;
150 }
151
152 async_wait_for(id, (sysarg_t*)&ret);
153 if (ret != EOK) {
154 async_exchange_end(exch);
155 free(cb);
156 return ret;
157 }
158
159 async_exchange_end(exch);
160 return EOK;
161}
162
163static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name)
164{
165 assert(cmd == HOUND_UNREGISTER_PLAYBACK || cmd == HOUND_UNREGISTER_RECORDING);
166 if (!name || !sess)
167 return EINVAL;
168
169 async_exch_t *exch = async_exchange_begin(sess);
170 if (!exch)
171 return ENOMEM;
172 aid_t id = async_send_0(exch, cmd, NULL);
173 sysarg_t ret = async_data_write_start(exch, name, str_size(name));
174 if (ret != EOK) {
175 async_forget(id);
176 async_exchange_end(exch);
177 return ret;
178 }
179
180 async_wait_for(id, &ret);
181 async_exchange_end(exch);
182 return ret;
183}
184
185static int hound_connection(hound_sess_t *sess, unsigned cmd,
186 const char *source, const char *sink)
187{
188 assert(cmd == HOUND_CONNECT || cmd == HOUND_DISCONNECT);
189 if (!source || !sink || !sess)
190 return EINVAL;
191
192 async_exch_t *exch = async_exchange_begin(sess);
193 if (!exch)
194 return ENOMEM;
195
196 aid_t id = async_send_0(exch, cmd, NULL);
197 sysarg_t ret = async_data_write_start(exch, source, str_size(source));
198 if (ret != EOK) {
199 async_forget(id);
200 async_exchange_end(exch);
201 return ret;
202 }
203 ret = async_data_write_start(exch, sink, str_size(sink));
204 if (ret != EOK) {
205 async_forget(id);
206 async_exchange_end(exch);
207 return ret;
208 }
209 async_wait_for(id, &ret);
210 async_exchange_end(exch);
211 return ret;
212}
213static void callback_gen(ipc_callid_t iid, ipc_call_t *call, void *arg,
214 bool read)
215{
216 async_answer_0(iid, EOK);
217 callback_t *cb = arg;
218 assert(cb);
219 void *buffer = NULL;
220 size_t buffer_size = 0;
221
222 bool (*receive)(ipc_callid_t *, size_t *) = read ?
223 async_data_read_receive : async_data_write_receive;
224
225 while (1) {
226 size_t size = 0;
227 ipc_callid_t id = 0;
228 if (!receive(&id, &size)) {
229 ipc_call_t failed_call;
230 async_get_call(&failed_call);
231 /* Protocol error or hangup */
232 if (IPC_GET_IMETHOD(failed_call) != 0)
233 cb->cb(cb->arg, NULL, EIO);
234 free(cb);
235 return;
236 }
237
238 if (buffer_size < size) {
239 buffer = realloc(buffer, size);
240 if (!buffer) {
241 cb->cb(cb->arg, NULL, ENOMEM);
242 free(cb);
243 return;
244 }
245 buffer_size = size;
246 }
247 if (read)
248 cb->cb(cb->arg, buffer, size);
249 const int ret = read ?
250 async_data_read_finalize(id, buffer, size):
251 async_data_write_finalize(id, buffer, size);
252 if (ret != EOK) {
253 cb->cb(cb->arg, NULL, ret);
254 free(cb);
255 return;
256 }
257 if (!read)
258 cb->cb(cb->arg, buffer, size);
259 }
260}
261
262static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg)
263{
264 callback_gen(iid, call, arg, true);
265}
266
267static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg)
268{
269 callback_gen(iid, call, arg, false);
270}
271
272/***
273 * SERVER SIDE
274 ***/
275static const char * get_name(void);
276
277int hound_server_register(const char *name, service_id_t *id)
278{
279 if (!name || !id)
280 return EINVAL;
281
282 int ret = loc_server_register(name);
283 if (ret != EOK)
284 return ret;
285
286 return loc_service_register(HOUND_SERVICE, id);
287}
288
289void hound_server_unregister(service_id_t id)
290{
291 loc_service_unregister(id);
292}
293
294int hound_server_set_device_change_callback(dev_change_callback_t cb)
295{
296 return loc_register_cat_change_cb(cb);
297}
298
299int hound_server_devices_iterate(device_callback_t callback)
300{
301 if (!callback)
302 return EINVAL;
303 static bool resolved = false;
304 static category_id_t cat_id = 0;
305
306 if (!resolved) {
307 const int ret = loc_category_get_id("audio-pcm", &cat_id,
308 IPC_FLAG_BLOCKING);
309 if (ret != EOK)
310 return ret;
311 resolved = true;
312 }
313
314 service_id_t *svcs = NULL;
315 size_t count = 0;
316 const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
317 if (ret != EOK)
318 return ret;
319
320 for (unsigned i = 0; i < count; ++i) {
321 char *name = NULL;
322 loc_service_get_name(svcs[i], &name);
323 callback(svcs[i], name);
324 free(name);
325 }
326 free(svcs);
327 return EOK;
328}
329
330int hound_server_get_register_params(const char **name, async_sess_t **sess,
331 unsigned *channels, unsigned *rate, pcm_sample_format_t *format)
332{
333 if (!name || !sess || !channels || !rate || !format)
334 return EINVAL;
335
336 const char *n = get_name();
337 if (!n)
338 return ENOMEM;
339
340 ipc_call_t call;
341 ipc_callid_t callid = async_get_call(&call);
342
343 unsigned ch = IPC_GET_ARG1(call);
344 unsigned r = IPC_GET_ARG2(call);
345 pcm_sample_format_t f = IPC_GET_ARG3(call);
346
347 async_sess_t *s = async_callback_receive_start(EXCHANGE_ATOMIC, &call);
348 async_answer_0(callid, s ? EOK : ENOMEM);
349
350 *name = n;
351 *sess = s;
352 *channels = ch;
353 *rate = r;
354 *format = f;
355
356 return ENOTSUP;
357}
358
359int hound_server_get_unregister_params(const char **name)
360{
361 if (!name)
362 return EINVAL;
363 *name = get_name();
364 if (!*name)
365 return ENOMEM;
366 return EOK;
367}
368
369int hound_server_get_connection_params(const char **source, const char **sink)
370{
371 if (!source || !sink)
372 return EINVAL;
373
374 const char *source_name = get_name();
375 if (!source_name)
376 return ENOMEM;
377
378 const char *sink_name = get_name();
379 if (!sink_name)
380 return ENOMEM;
381
382 *source = source_name;
383 *sink = sink_name;
384 return EOK;
385}
386
387static const char * get_name(void)
388{
389 size_t size = 0;
390 ipc_callid_t callid;
391 async_data_write_receive(&callid, &size);
392 char *buffer = malloc(size + 1);
393 if (buffer) {
394 async_data_write_finalize(callid, buffer, size);
395 buffer[size] = 0;
396 }
397 return buffer;
398}
399
400/**
401 * @}
402 */
Note: See TracBrowser for help on using the repository browser.