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

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

Add libhound. This library implements hound protocol and helpers.

  • Property mode set to 100644
File size: 9.7 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}
74
75void hound_release_session(hound_sess_t *sess)
76{
77 if (sess)
78 async_hangup(sess);
79}
80
81int hound_register_playback(hound_sess_t *sess, const char *name,
82 unsigned channels, unsigned rate, pcm_sample_format_t format,
83 data_callback_t data_callback, void *arg)
84{
85 return hound_register(sess, HOUND_REGISTER_PLAYBACK, name, channels,
86 rate, format, data_callback, arg);
87}
88int hound_register_recording(hound_sess_t *sess, const char *name,
89 unsigned channels, unsigned rate, pcm_sample_format_t format,
90 data_callback_t data_callback, void *arg)
91{
92 return hound_register(sess, HOUND_REGISTER_RECORDING, name, channels,
93 rate, format, data_callback, arg);
94
95}
96int hound_unregister_playback(hound_sess_t *sess, const char *name)
97{
98 return hound_unregister(sess, HOUND_UNREGISTER_PLAYBACK, name);
99}
100int hound_unregister_recording(hound_sess_t *sess, const char *name)
101{
102 return hound_unregister(sess, HOUND_UNREGISTER_RECORDING, name);
103}
104int hound_create_connection(hound_sess_t *sess, const char *source, const char *sink)
105{
106 return hound_connection(sess, HOUND_CONNECT, source, sink);
107}
108int hound_destroy_connection(hound_sess_t *sess, const char *source, const char *sink)
109{
110 return hound_connection(sess, HOUND_DISCONNECT, source, sink);
111}
112
113static int hound_register(hound_sess_t *sess, unsigned cmd, const char *name,
114 unsigned channels, unsigned rate, pcm_sample_format_t format,
115 data_callback_t data_callback, void *arg)
116{
117 assert(cmd == HOUND_REGISTER_PLAYBACK || cmd == HOUND_REGISTER_RECORDING);
118 if (!name || !data_callback || !sess)
119 return EINVAL;
120
121 async_exch_t *exch = async_exchange_begin(sess);
122 if (!exch)
123 return ENOMEM;
124
125 aid_t id = async_send_0(exch, cmd, NULL);
126
127 int ret = async_data_write_start(exch, name, str_size(name));
128 if (ret != EOK) {
129 async_forget(id);
130 async_exchange_end(exch);
131 return ret;
132 }
133
134 callback_t *cb = malloc(sizeof(callback_t));
135 if (!cb) {
136 async_forget(id);
137 async_exchange_end(exch);
138 return ENOMEM;
139 }
140
141 cb->cb = data_callback;
142 cb->arg = arg;
143 async_client_conn_t callback =
144 (cmd == HOUND_REGISTER_PLAYBACK) ? callback_pb : callback_rec;
145
146 ret = async_connect_to_me(exch, channels, rate, format, callback, cb);
147 if (ret != EOK) {
148 async_forget(id);
149 async_exchange_end(exch);
150 free(cb);
151 return ret;
152 }
153
154 async_wait_for(id, (sysarg_t*)&ret);
155 if (ret != EOK) {
156 async_exchange_end(exch);
157 free(cb);
158 return ret;
159 }
160
161 async_exchange_end(exch);
162 return EOK;
163}
164
165static int hound_unregister(hound_sess_t *sess, unsigned cmd, const char *name)
166{
167 assert(cmd == HOUND_UNREGISTER_PLAYBACK || cmd == HOUND_UNREGISTER_RECORDING);
168 if (!name || !sess)
169 return EINVAL;
170
171 async_exch_t *exch = async_exchange_begin(sess);
172 if (!exch)
173 return ENOMEM;
174 aid_t id = async_send_0(exch, cmd, NULL);
175 sysarg_t ret = async_data_write_start(exch, name, str_size(name));
176 if (ret != EOK) {
177 async_forget(id);
178 async_exchange_end(exch);
179 return ret;
180 }
181
182 async_wait_for(id, &ret);
183 async_exchange_end(exch);
184 return ret;
185}
186
187static int hound_connection(hound_sess_t *sess, unsigned cmd,
188 const char *source, const char *sink)
189{
190 assert(cmd == HOUND_CONNECT || cmd == HOUND_DISCONNECT);
191 if (!source || !sink || !sess)
192 return EINVAL;
193
194 async_exch_t *exch = async_exchange_begin(sess);
195 if (!exch)
196 return ENOMEM;
197
198 aid_t id = async_send_0(exch, cmd, NULL);
199 sysarg_t ret = async_data_write_start(exch, source, str_size(source));
200 if (ret != EOK) {
201 async_forget(id);
202 async_exchange_end(exch);
203 return ret;
204 }
205 ret = async_data_write_start(exch, sink, str_size(sink));
206 if (ret != EOK) {
207 async_forget(id);
208 async_exchange_end(exch);
209 return ret;
210 }
211 async_wait_for(id, &ret);
212 async_exchange_end(exch);
213 return ret;
214}
215
216
217
218
219
220static void callback_gen(ipc_callid_t iid, ipc_call_t *call, void *arg,
221 bool read)
222{
223 async_answer_0(iid, EOK);
224 callback_t *cb = arg;
225 assert(cb);
226 void *buffer = NULL;
227 size_t buffer_size = 0;
228
229 bool (*receive)(ipc_callid_t *, size_t *) = read ?
230 async_data_read_receive : async_data_write_receive;
231
232 while (1) {
233 size_t size = 0;
234 ipc_callid_t id = 0;
235 if (!receive(&id, &size)) {
236 /* Protocol error */
237 cb->cb(cb->arg, NULL, EIO);
238 free(cb);
239 return;
240 }
241
242 if (buffer_size < size) {
243 buffer = realloc(buffer, size);
244 if (!buffer) {
245 cb->cb(cb->arg, NULL, ENOMEM);
246 free(cb);
247 return;
248 }
249 buffer_size = size;
250 }
251 if (read)
252 cb->cb(cb->arg, buffer, size);
253 const int ret = read ?
254 async_data_read_finalize(id, buffer, size):
255 async_data_write_finalize(id, buffer, size);
256 if (ret != EOK) {
257 cb->cb(cb->arg, NULL, ret);
258 free(cb);
259 return;
260 }
261 if (!read)
262 cb->cb(cb->arg, buffer, size);
263 }
264}
265
266static void callback_pb(ipc_callid_t iid, ipc_call_t *call, void *arg)
267{
268 callback_gen(iid, call, arg, true);
269}
270
271static void callback_rec(ipc_callid_t iid, ipc_call_t *call, void *arg)
272{
273 callback_gen(iid, call, arg, false);
274}
275
276/***
277 * SERVER SIDE
278 ***/
279static const char * get_name(void);
280
281int hound_server_register(const char *name, service_id_t *id)
282{
283 if (!name || !id)
284 return EINVAL;
285
286 int ret = loc_server_register(name);
287 if (ret != EOK)
288 return ret;
289
290 return loc_service_register(HOUND_SERVICE, id);
291}
292
293void hound_server_unregister(service_id_t id)
294{
295 loc_service_unregister(id);
296}
297
298int hound_server_set_device_change_callback(dev_change_callback_t cb)
299{
300 return loc_register_cat_change_cb(cb);
301}
302
303int hound_server_devices_iterate(device_callback_t callback)
304{
305 if (!callback)
306 return EINVAL;
307 static bool resolved = false;
308 static category_id_t cat_id = 0;
309
310 if (!resolved) {
311 const int ret = loc_category_get_id("audio-pcm", &cat_id,
312 IPC_FLAG_BLOCKING);
313 if (ret != EOK)
314 return ret;
315 resolved = true;
316 }
317
318 service_id_t *svcs = NULL;
319 size_t count = 0;
320 const int ret = loc_category_get_svcs(cat_id, &svcs, &count);
321 if (ret != EOK)
322 return ret;
323
324 for (unsigned i = 0; i < count; ++i) {
325 char *name = NULL;
326 loc_service_get_name(svcs[i], &name);
327 callback(svcs[i], name);
328 free(name);
329 }
330 free(svcs);
331 return EOK;
332}
333
334int hound_server_get_register_params(const char **name, async_sess_t **sess,
335 unsigned *channels, unsigned *rate, pcm_sample_format_t *format)
336{
337 if (!name || !sess || !channels || !rate || !format)
338 return EINVAL;
339
340 const char *n = get_name();
341 if (!n)
342 return ENOMEM;
343
344 ipc_call_t call;
345 ipc_callid_t callid = async_get_call(&call);
346
347 unsigned ch = IPC_GET_ARG1(call);
348 unsigned r = IPC_GET_ARG2(call);
349 pcm_sample_format_t f = IPC_GET_ARG3(call);
350
351 async_sess_t *s = async_callback_receive_start(EXCHANGE_ATOMIC, &call);
352 async_answer_0(callid, s ? EOK : ENOMEM);
353
354 *name = n;
355 *sess = s;
356 *channels = ch;
357 *rate = r;
358 *format = f;
359
360 return ENOTSUP;
361}
362
363int hound_server_get_unregister_params(const char **name)
364{
365 if (!name)
366 return EINVAL;
367 *name = get_name();
368 if (!*name)
369 return ENOMEM;
370 return EOK;
371}
372
373int hound_server_get_connection_params(const char **source, const char **sink)
374{
375 if (!source || !sink)
376 return EINVAL;
377
378 const char *source_name = get_name();
379 if (!source_name)
380 return ENOMEM;
381
382 const char *sink_name = get_name();
383 if (!sink_name)
384 return ENOMEM;
385
386 *source = source_name;
387 *sink = sink_name;
388 return EOK;
389}
390
391static const char * get_name(void)
392{
393 size_t size = 0;
394 ipc_callid_t callid;
395 async_data_write_receive(&callid, &size);
396 char *buffer = malloc(size + 1);
397 if (buffer) {
398 async_data_write_finalize(callid, buffer, size);
399 buffer[size] = 0;
400 }
401 return buffer;
402}
403
404/**
405 * @}
406 */
Note: See TracBrowser for help on using the repository browser.