3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2006-2010 Nokia Corporation
6 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
34 #include <dbus/dbus.h>
36 #include "lib/bluetooth.h"
39 #include "gdbus/gdbus.h"
42 #include "src/adapter.h"
43 #include "src/device.h"
44 #include "src/service.h"
45 #include "src/error.h"
46 #include "src/dbus-common.h"
47 #include "src/shared/queue.h"
54 #define STREAM_SETUP_RETRY_TIMER 2
57 struct btd_service *service;
58 struct avdtp *session;
59 struct avdtp_stream *stream;
61 avdtp_session_state_t session_state;
62 avdtp_state_t stream_state;
64 unsigned int connect_id;
65 unsigned int disconnect_id;
66 unsigned int avdtp_callback_id;
69 struct sink_state_callback {
71 struct btd_service *service;
76 static GSList *sink_callbacks = NULL;
78 static char *str_state[] = {
79 "SINK_STATE_DISCONNECTED",
80 "SINK_STATE_CONNECTING",
81 "SINK_STATE_CONNECTED",
85 static void sink_set_state(struct sink *sink, sink_state_t new_state)
87 struct btd_service *service = sink->service;
88 struct btd_device *dev = btd_service_get_device(service);
89 sink_state_t old_state = sink->state;
92 sink->state = new_state;
94 DBG("State changed %s: %s -> %s", device_get_path(dev),
95 str_state[old_state], str_state[new_state]);
97 for (l = sink_callbacks; l != NULL; l = l->next) {
98 struct sink_state_callback *cb = l->data;
100 if (cb->service != service)
103 cb->cb(service, old_state, new_state, cb->user_data);
106 if (new_state != SINK_STATE_DISCONNECTED)
109 #ifdef __TIZEN_PATCH__
110 btd_service_disconnecting_complete(service, 0);
114 avdtp_unref(sink->session);
115 sink->session = NULL;
116 #ifdef __TIZEN_PATCH__
117 sink->connect_id = 0;
118 sink->disconnect_id = 0;
123 static void avdtp_state_callback(struct btd_device *dev,
124 struct avdtp *session,
125 avdtp_session_state_t old_state,
126 avdtp_session_state_t new_state,
129 struct sink *sink = user_data;
132 case AVDTP_SESSION_STATE_DISCONNECTED:
133 sink_set_state(sink, SINK_STATE_DISCONNECTED);
135 case AVDTP_SESSION_STATE_CONNECTING:
136 sink_set_state(sink, SINK_STATE_CONNECTING);
138 case AVDTP_SESSION_STATE_CONNECTED:
142 sink->session_state = new_state;
145 static void stream_state_changed(struct avdtp_stream *stream,
146 avdtp_state_t old_state,
147 avdtp_state_t new_state,
148 struct avdtp_error *err,
151 struct btd_service *service = user_data;
152 struct sink *sink = btd_service_get_user_data(service);
158 case AVDTP_STATE_IDLE:
159 #ifndef __TIZEN_PATCH__
160 btd_service_disconnecting_complete(sink->service, 0);
163 if (sink->disconnect_id > 0) {
164 a2dp_cancel(sink->disconnect_id);
165 sink->disconnect_id = 0;
169 avdtp_unref(sink->session);
170 sink->session = NULL;
175 case AVDTP_STATE_OPEN:
176 btd_service_connecting_complete(sink->service, 0);
177 sink_set_state(sink, SINK_STATE_CONNECTED);
179 case AVDTP_STATE_STREAMING:
180 sink_set_state(sink, SINK_STATE_PLAYING);
182 case AVDTP_STATE_CONFIGURED:
183 case AVDTP_STATE_CLOSING:
184 case AVDTP_STATE_ABORTING:
189 sink->stream_state = new_state;
192 static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
193 struct avdtp_stream *stream,
194 struct avdtp_error *err, void *user_data)
196 struct sink *sink = user_data;
198 sink->connect_id = 0;
203 avdtp_unref(sink->session);
204 sink->session = NULL;
205 #ifdef __TIZEN_PATCH__
206 if (err && avdtp_error_category(err) == AVDTP_ERRNO
207 && avdtp_error_posix_errno(err) != EHOSTDOWN)
209 if (avdtp_error_category(err) == AVDTP_ERRNO
210 && avdtp_error_posix_errno(err) != EHOSTDOWN)
212 btd_service_connecting_complete(sink->service, -EAGAIN);
214 btd_service_connecting_complete(sink->service, -EIO);
217 static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
218 GSList *caps, void *user_data)
220 struct sink *sink = user_data;
223 sink->connect_id = 0;
225 id = a2dp_config(session, sep, stream_setup_complete, caps, sink);
229 sink->connect_id = id;
233 btd_service_connecting_complete(sink->service, -EIO);
235 avdtp_unref(sink->session);
236 sink->session = NULL;
239 static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
242 struct sink *sink = user_data;
245 sink->connect_id = 0;
248 avdtp_unref(sink->session);
249 sink->session = NULL;
251 perr = -avdtp_error_posix_errno(err);
252 if (perr != -EHOSTDOWN) {
253 if (avdtp_error_category(err) == AVDTP_ERRNO)
261 DBG("Discovery complete");
263 id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL,
264 select_complete, sink);
270 sink->connect_id = id;
274 btd_service_connecting_complete(sink->service, perr);
275 avdtp_unref(sink->session);
276 sink->session = NULL;
279 gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session)
281 struct sink *sink = btd_service_get_user_data(service);
283 if (sink->connect_id > 0 || sink->disconnect_id > 0)
286 if (session && !sink->session)
287 sink->session = avdtp_ref(session);
292 sink->connect_id = a2dp_discover(sink->session, discovery_complete,
294 if (sink->connect_id == 0)
300 int sink_connect(struct btd_service *service)
302 struct sink *sink = btd_service_get_user_data(service);
304 #ifndef __TIZEN_PATCH__
306 sink->session = a2dp_avdtp_get(btd_service_get_device(service));
308 if (!sink->session) {
309 DBG("Unable to get a session");
314 if (sink->connect_id > 0 || sink->disconnect_id > 0)
317 if (sink->state == SINK_STATE_CONNECTING)
320 if (sink->stream_state >= AVDTP_STATE_OPEN)
323 #ifdef __TIZEN_PATCH__
325 sink->session = a2dp_avdtp_get(btd_service_get_device(service));
327 if (!sink->session) {
328 DBG("Unable to get a session");
333 if (!sink_setup_stream(service, NULL)) {
334 DBG("Failed to create a stream");
338 DBG("stream creation in progress");
343 static void sink_free(struct btd_service *service)
345 struct sink *sink = btd_service_get_user_data(service);
348 avdtp_stream_remove_cb(sink->session, sink->stream,
351 #ifdef __TIZEN_PATCH__
353 /* We need to clear the avdtp discovery procedure */
354 finalize_discovery(sink->session, ECANCELED);
355 avdtp_unref(sink->session);
359 avdtp_unref(sink->session);
362 if (sink->connect_id > 0) {
363 btd_service_connecting_complete(sink->service, -ECANCELED);
364 a2dp_cancel(sink->connect_id);
365 sink->connect_id = 0;
368 if (sink->disconnect_id > 0) {
369 btd_service_disconnecting_complete(sink->service, -ECANCELED);
370 a2dp_cancel(sink->disconnect_id);
371 sink->disconnect_id = 0;
374 avdtp_remove_state_cb(sink->avdtp_callback_id);
375 btd_service_unref(sink->service);
380 void sink_unregister(struct btd_service *service)
382 struct btd_device *dev = btd_service_get_device(service);
384 DBG("%s", device_get_path(dev));
389 int sink_init(struct btd_service *service)
391 struct btd_device *dev = btd_service_get_device(service);
394 DBG("%s", device_get_path(dev));
396 sink = g_new0(struct sink, 1);
398 sink->service = btd_service_ref(service);
400 sink->avdtp_callback_id = avdtp_add_state_cb(dev, avdtp_state_callback,
403 btd_service_set_user_data(service, sink);
408 gboolean sink_is_active(struct btd_service *service)
410 struct sink *sink = btd_service_get_user_data(service);
418 gboolean sink_new_stream(struct btd_service *service, struct avdtp *session,
419 struct avdtp_stream *stream)
421 struct sink *sink = btd_service_get_user_data(service);
427 sink->session = avdtp_ref(session);
429 sink->stream = stream;
431 sink->cb_id = avdtp_stream_add_cb(session, stream,
432 stream_state_changed, service);
437 int sink_disconnect(struct btd_service *service)
439 struct sink *sink = btd_service_get_user_data(service);
444 /* cancel pending connect */
445 if (sink->connect_id > 0) {
446 a2dp_cancel(sink->connect_id);
447 sink->connect_id = 0;
448 btd_service_disconnecting_complete(sink->service, 0);
450 avdtp_unref(sink->session);
451 sink->session = NULL;
456 /* disconnect already ongoing */
457 if (sink->disconnect_id > 0)
463 return avdtp_close(sink->session, sink->stream, FALSE);
466 unsigned int sink_add_state_cb(struct btd_service *service, sink_state_cb cb,
469 struct sink_state_callback *state_cb;
470 static unsigned int id = 0;
472 state_cb = g_new(struct sink_state_callback, 1);
474 state_cb->service = service;
475 state_cb->user_data = user_data;
478 sink_callbacks = g_slist_append(sink_callbacks, state_cb);
483 gboolean sink_remove_state_cb(unsigned int id)
487 for (l = sink_callbacks; l != NULL; l = l->next) {
488 struct sink_state_callback *cb = l->data;
489 if (cb && cb->id == id) {
490 sink_callbacks = g_slist_remove(sink_callbacks, cb);