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

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

libhound: Move new API implementation to a new file

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