tizen 2.3.1 release
[framework/connectivity/bluez.git] / profiles / audio / source.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2006-2010  Nokia Corporation
6  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
7  *  Copyright (C) 2009  Joao Paulo Rechi Vita
8  *
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  *
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <stdint.h>
31 #include <stdbool.h>
32 #include <errno.h>
33
34 #include <glib.h>
35 #include <dbus/dbus.h>
36
37 #include "lib/bluetooth.h"
38 #include "lib/sdp.h"
39
40 #include "gdbus/gdbus.h"
41
42 #include "src/log.h"
43 #include "src/adapter.h"
44 #include "src/device.h"
45 #include "src/service.h"
46 #include "src/error.h"
47 #include "src/dbus-common.h"
48 #include "src/shared/queue.h"
49
50 #include "avdtp.h"
51 #include "media.h"
52 #include "a2dp.h"
53 #include "source.h"
54
55 struct source {
56         struct btd_service *service;
57         struct avdtp *session;
58         struct avdtp_stream *stream;
59         unsigned int cb_id;
60         avdtp_session_state_t session_state;
61         avdtp_state_t stream_state;
62         source_state_t state;
63         unsigned int connect_id;
64         unsigned int disconnect_id;
65         unsigned int avdtp_callback_id;
66 };
67
68 struct source_state_callback {
69         source_state_cb cb;
70         struct btd_service *service;
71         void *user_data;
72         unsigned int id;
73 };
74
75 static GSList *source_callbacks = NULL;
76
77 static char *str_state[] = {
78         "SOURCE_STATE_DISCONNECTED",
79         "SOURCE_STATE_CONNECTING",
80         "SOURCE_STATE_CONNECTED",
81         "SOURCE_STATE_PLAYING",
82 };
83
84 static void source_set_state(struct source *source, source_state_t new_state)
85 {
86         struct btd_device *dev = btd_service_get_device(source->service);
87         source_state_t old_state = source->state;
88         GSList *l;
89
90         source->state = new_state;
91
92         DBG("State changed %s: %s -> %s", device_get_path(dev),
93                                 str_state[old_state], str_state[new_state]);
94
95         for (l = source_callbacks; l != NULL; l = l->next) {
96                 struct source_state_callback *cb = l->data;
97
98                 if (cb->service != source->service)
99                         continue;
100
101                 cb->cb(source->service, old_state, new_state, cb->user_data);
102         }
103
104         if (new_state != SOURCE_STATE_DISCONNECTED)
105                 return;
106
107         if (source->session) {
108                 avdtp_unref(source->session);
109                 source->session = NULL;
110         }
111 }
112
113 static void avdtp_state_callback(struct btd_device *dev, struct avdtp *session,
114                                         avdtp_session_state_t old_state,
115                                         avdtp_session_state_t new_state,
116                                         void *user_data)
117 {
118         struct source *source = user_data;
119
120         switch (new_state) {
121         case AVDTP_SESSION_STATE_DISCONNECTED:
122                 source_set_state(source, SOURCE_STATE_DISCONNECTED);
123                 break;
124         case AVDTP_SESSION_STATE_CONNECTING:
125                 source_set_state(source, SOURCE_STATE_CONNECTING);
126                 break;
127         case AVDTP_SESSION_STATE_CONNECTED:
128                 break;
129         }
130
131         source->session_state = new_state;
132 }
133
134 static void stream_state_changed(struct avdtp_stream *stream,
135                                         avdtp_state_t old_state,
136                                         avdtp_state_t new_state,
137                                         struct avdtp_error *err,
138                                         void *user_data)
139 {
140         struct btd_service *service = user_data;
141         struct source *source = btd_service_get_user_data(service);
142
143         if (err)
144                 return;
145
146         switch (new_state) {
147         case AVDTP_STATE_IDLE:
148                 btd_service_disconnecting_complete(source->service, 0);
149
150                 if (source->disconnect_id > 0) {
151                         a2dp_cancel(source->disconnect_id);
152                         source->disconnect_id = 0;
153                 }
154
155                 if (source->session) {
156                         avdtp_unref(source->session);
157                         source->session = NULL;
158                 }
159                 source->stream = NULL;
160                 source->cb_id = 0;
161                 break;
162         case AVDTP_STATE_OPEN:
163                 btd_service_connecting_complete(source->service, 0);
164                 source_set_state(source, SOURCE_STATE_CONNECTED);
165                 break;
166         case AVDTP_STATE_STREAMING:
167                 source_set_state(source, SOURCE_STATE_PLAYING);
168                 break;
169         case AVDTP_STATE_CONFIGURED:
170         case AVDTP_STATE_CLOSING:
171         case AVDTP_STATE_ABORTING:
172         default:
173                 break;
174         }
175
176         source->stream_state = new_state;
177 }
178
179 static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
180                                         struct avdtp_stream *stream,
181                                         struct avdtp_error *err, void *user_data)
182 {
183         struct source *source = user_data;
184
185         source->connect_id = 0;
186
187         if (stream)
188                 return;
189
190         avdtp_unref(source->session);
191         source->session = NULL;
192         if (avdtp_error_category(err) == AVDTP_ERRNO
193                                 && avdtp_error_posix_errno(err) != EHOSTDOWN)
194                 btd_service_connecting_complete(source->service, -EAGAIN);
195         else
196                 btd_service_connecting_complete(source->service, -EIO);
197 }
198
199 static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
200                         GSList *caps, void *user_data)
201 {
202         struct source *source = user_data;
203         int id;
204
205         source->connect_id = 0;
206
207         if (caps == NULL)
208                 goto failed;
209
210         id = a2dp_config(session, sep, stream_setup_complete, caps, source);
211         if (id == 0)
212                 goto failed;
213
214         source->connect_id = id;
215         return;
216
217 failed:
218         btd_service_connecting_complete(source->service, -EIO);
219
220         avdtp_unref(source->session);
221         source->session = NULL;
222 }
223
224 static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
225                                 void *user_data)
226 {
227         struct source *source = user_data;
228         int id, perr;
229
230         if (err) {
231                 avdtp_unref(source->session);
232                 source->session = NULL;
233
234                 perr = -avdtp_error_posix_errno(err);
235                 if (perr != -EHOSTDOWN) {
236                         if (avdtp_error_category(err) == AVDTP_ERRNO)
237                                 perr = -EAGAIN;
238                         else
239                                 perr = -EIO;
240                 }
241                 goto failed;
242         }
243
244         DBG("Discovery complete");
245
246         id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL,
247                                                 select_complete, source);
248         if (id == 0) {
249                 perr = -EIO;
250                 goto failed;
251         }
252
253         source->connect_id = id;
254         return;
255
256 failed:
257         btd_service_connecting_complete(source->service, perr);
258         avdtp_unref(source->session);
259         source->session = NULL;
260 }
261
262 gboolean source_setup_stream(struct btd_service *service,
263                                                         struct avdtp *session)
264 {
265         struct source *source = btd_service_get_user_data(service);
266
267         if (source->connect_id > 0 || source->disconnect_id > 0)
268                 return FALSE;
269
270         if (session && !source->session)
271                 source->session = avdtp_ref(session);
272
273         if (!source->session)
274                 return FALSE;
275
276         if (avdtp_discover(source->session, discovery_complete, source) < 0)
277                 return FALSE;
278
279         return TRUE;
280 }
281
282 int source_connect(struct btd_service *service)
283 {
284         struct source *source = btd_service_get_user_data(service);
285
286         if (!source->session)
287                 source->session = a2dp_avdtp_get(btd_service_get_device(service));
288
289         if (!source->session) {
290                 DBG("Unable to get a session");
291                 return -EIO;
292         }
293
294         if (source->connect_id > 0 || source->disconnect_id > 0)
295                 return -EBUSY;
296
297         if (source->state == SOURCE_STATE_CONNECTING)
298                 return -EBUSY;
299
300         if (source->stream_state >= AVDTP_STATE_OPEN)
301                 return -EALREADY;
302
303         if (!source_setup_stream(service, NULL)) {
304                 DBG("Failed to create a stream");
305                 return -EIO;
306         }
307
308         DBG("stream creation in progress");
309
310         return 0;
311 }
312
313 static void source_free(struct btd_service *service)
314 {
315         struct source *source = btd_service_get_user_data(service);
316
317         if (source->cb_id)
318                 avdtp_stream_remove_cb(source->session, source->stream,
319                                         source->cb_id);
320
321         if (source->session)
322                 avdtp_unref(source->session);
323
324         if (source->connect_id > 0) {
325                 btd_service_connecting_complete(source->service, -ECANCELED);
326                 a2dp_cancel(source->connect_id);
327                 source->connect_id = 0;
328         }
329
330         if (source->disconnect_id > 0) {
331                 btd_service_disconnecting_complete(source->service, -ECANCELED);
332                 a2dp_cancel(source->disconnect_id);
333                 source->disconnect_id = 0;
334         }
335
336         avdtp_remove_state_cb(source->avdtp_callback_id);
337         btd_service_unref(source->service);
338
339         g_free(source);
340 }
341
342 void source_unregister(struct btd_service *service)
343 {
344         struct btd_device *dev = btd_service_get_device(service);
345
346         DBG("%s", device_get_path(dev));
347
348         source_free(service);
349 }
350
351 int source_init(struct btd_service *service)
352 {
353         struct btd_device *dev = btd_service_get_device(service);
354         struct source *source;
355
356         DBG("%s", device_get_path(dev));
357
358         source = g_new0(struct source, 1);
359
360         source->service = btd_service_ref(service);
361
362         source->avdtp_callback_id = avdtp_add_state_cb(dev,
363                                                         avdtp_state_callback,
364                                                         source);
365
366         btd_service_set_user_data(service, source);
367
368         return 0;
369 }
370
371 gboolean source_new_stream(struct btd_service *service, struct avdtp *session,
372                                 struct avdtp_stream *stream)
373 {
374         struct source *source = btd_service_get_user_data(service);
375
376         if (source->stream)
377                 return FALSE;
378
379         if (!source->session)
380                 source->session = avdtp_ref(session);
381
382         source->stream = stream;
383
384         source->cb_id = avdtp_stream_add_cb(session, stream,
385                                                 stream_state_changed, service);
386
387         return TRUE;
388 }
389
390 int source_disconnect(struct btd_service *service)
391 {
392         struct source *source = btd_service_get_user_data(service);
393
394         if (!source->session)
395                 return -ENOTCONN;
396
397         /* cancel pending connect */
398         if (source->connect_id > 0) {
399                 a2dp_cancel(source->connect_id);
400                 source->connect_id = 0;
401                 btd_service_connecting_complete(source->service, -ECANCELED);
402
403                 avdtp_unref(source->session);
404                 source->session = NULL;
405
406                 return 0;
407         }
408
409         /* disconnect already ongoing */
410         if (source->disconnect_id > 0)
411                 return -EBUSY;
412
413         if (!source->stream)
414                 return -ENOTCONN;
415
416         return avdtp_close(source->session, source->stream, FALSE);
417 }
418
419 unsigned int source_add_state_cb(struct btd_service *service,
420                                         source_state_cb cb, void *user_data)
421 {
422         struct source_state_callback *state_cb;
423         static unsigned int id = 0;
424
425         state_cb = g_new(struct source_state_callback, 1);
426         state_cb->cb = cb;
427         state_cb->service = service;
428         state_cb->user_data = user_data;
429         state_cb->id = ++id;
430
431         source_callbacks = g_slist_append(source_callbacks, state_cb);
432
433         return state_cb->id;
434 }
435
436 gboolean source_remove_state_cb(unsigned int id)
437 {
438         GSList *l;
439
440         for (l = source_callbacks; l != NULL; l = l->next) {
441                 struct source_state_callback *cb = l->data;
442                 if (cb && cb->id == id) {
443                         source_callbacks = g_slist_remove(source_callbacks, cb);
444                         g_free(cb);
445                         return TRUE;
446                 }
447         }
448
449         return FALSE;
450 }