3 * BlueZ - Bluetooth protocol stack for Linux
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
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.
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.
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
35 #include <dbus/dbus.h>
37 #include "lib/bluetooth.h"
40 #include "gdbus/gdbus.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"
56 struct btd_service *service;
57 struct avdtp *session;
58 struct avdtp_stream *stream;
60 avdtp_session_state_t session_state;
61 avdtp_state_t stream_state;
63 unsigned int connect_id;
64 unsigned int disconnect_id;
65 unsigned int avdtp_callback_id;
68 struct source_state_callback {
70 struct btd_service *service;
75 static GSList *source_callbacks = NULL;
77 static char *str_state[] = {
78 "SOURCE_STATE_DISCONNECTED",
79 "SOURCE_STATE_CONNECTING",
80 "SOURCE_STATE_CONNECTED",
81 "SOURCE_STATE_PLAYING",
84 static void source_set_state(struct source *source, source_state_t new_state)
86 struct btd_device *dev = btd_service_get_device(source->service);
87 source_state_t old_state = source->state;
90 source->state = new_state;
92 DBG("State changed %s: %s -> %s", device_get_path(dev),
93 str_state[old_state], str_state[new_state]);
95 for (l = source_callbacks; l != NULL; l = l->next) {
96 struct source_state_callback *cb = l->data;
98 if (cb->service != source->service)
101 cb->cb(source->service, old_state, new_state, cb->user_data);
104 if (new_state != SOURCE_STATE_DISCONNECTED)
107 if (source->session) {
108 avdtp_unref(source->session);
109 source->session = NULL;
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,
118 struct source *source = user_data;
121 case AVDTP_SESSION_STATE_DISCONNECTED:
122 source_set_state(source, SOURCE_STATE_DISCONNECTED);
124 case AVDTP_SESSION_STATE_CONNECTING:
125 source_set_state(source, SOURCE_STATE_CONNECTING);
127 case AVDTP_SESSION_STATE_CONNECTED:
131 source->session_state = new_state;
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,
140 struct btd_service *service = user_data;
141 struct source *source = btd_service_get_user_data(service);
147 case AVDTP_STATE_IDLE:
148 btd_service_disconnecting_complete(source->service, 0);
150 if (source->disconnect_id > 0) {
151 a2dp_cancel(source->disconnect_id);
152 source->disconnect_id = 0;
155 if (source->session) {
156 avdtp_unref(source->session);
157 source->session = NULL;
159 source->stream = NULL;
162 case AVDTP_STATE_OPEN:
163 btd_service_connecting_complete(source->service, 0);
164 source_set_state(source, SOURCE_STATE_CONNECTED);
166 case AVDTP_STATE_STREAMING:
167 source_set_state(source, SOURCE_STATE_PLAYING);
169 case AVDTP_STATE_CONFIGURED:
170 case AVDTP_STATE_CLOSING:
171 case AVDTP_STATE_ABORTING:
176 source->stream_state = new_state;
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)
183 struct source *source = user_data;
185 source->connect_id = 0;
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);
196 btd_service_connecting_complete(source->service, -EIO);
199 static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
200 GSList *caps, void *user_data)
202 struct source *source = user_data;
205 source->connect_id = 0;
210 id = a2dp_config(session, sep, stream_setup_complete, caps, source);
214 source->connect_id = id;
218 btd_service_connecting_complete(source->service, -EIO);
220 avdtp_unref(source->session);
221 source->session = NULL;
224 static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
227 struct source *source = user_data;
231 avdtp_unref(source->session);
232 source->session = NULL;
234 perr = -avdtp_error_posix_errno(err);
235 if (perr != -EHOSTDOWN) {
236 if (avdtp_error_category(err) == AVDTP_ERRNO)
244 DBG("Discovery complete");
246 id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL,
247 select_complete, source);
253 source->connect_id = id;
257 btd_service_connecting_complete(source->service, perr);
258 avdtp_unref(source->session);
259 source->session = NULL;
262 gboolean source_setup_stream(struct btd_service *service,
263 struct avdtp *session)
265 struct source *source = btd_service_get_user_data(service);
267 if (source->connect_id > 0 || source->disconnect_id > 0)
270 if (session && !source->session)
271 source->session = avdtp_ref(session);
273 if (!source->session)
276 if (avdtp_discover(source->session, discovery_complete, source) < 0)
282 int source_connect(struct btd_service *service)
284 struct source *source = btd_service_get_user_data(service);
286 if (!source->session)
287 source->session = a2dp_avdtp_get(btd_service_get_device(service));
289 if (!source->session) {
290 DBG("Unable to get a session");
294 if (source->connect_id > 0 || source->disconnect_id > 0)
297 if (source->state == SOURCE_STATE_CONNECTING)
300 if (source->stream_state >= AVDTP_STATE_OPEN)
303 if (!source_setup_stream(service, NULL)) {
304 DBG("Failed to create a stream");
308 DBG("stream creation in progress");
313 static void source_free(struct btd_service *service)
315 struct source *source = btd_service_get_user_data(service);
318 avdtp_stream_remove_cb(source->session, source->stream,
322 avdtp_unref(source->session);
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;
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;
336 avdtp_remove_state_cb(source->avdtp_callback_id);
337 btd_service_unref(source->service);
342 void source_unregister(struct btd_service *service)
344 struct btd_device *dev = btd_service_get_device(service);
346 DBG("%s", device_get_path(dev));
348 source_free(service);
351 int source_init(struct btd_service *service)
353 struct btd_device *dev = btd_service_get_device(service);
354 struct source *source;
356 DBG("%s", device_get_path(dev));
358 source = g_new0(struct source, 1);
360 source->service = btd_service_ref(service);
362 source->avdtp_callback_id = avdtp_add_state_cb(dev,
363 avdtp_state_callback,
366 btd_service_set_user_data(service, source);
371 gboolean source_new_stream(struct btd_service *service, struct avdtp *session,
372 struct avdtp_stream *stream)
374 struct source *source = btd_service_get_user_data(service);
379 if (!source->session)
380 source->session = avdtp_ref(session);
382 source->stream = stream;
384 source->cb_id = avdtp_stream_add_cb(session, stream,
385 stream_state_changed, service);
390 int source_disconnect(struct btd_service *service)
392 struct source *source = btd_service_get_user_data(service);
394 if (!source->session)
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);
403 avdtp_unref(source->session);
404 source->session = NULL;
409 /* disconnect already ongoing */
410 if (source->disconnect_id > 0)
416 return avdtp_close(source->session, source->stream, FALSE);
419 unsigned int source_add_state_cb(struct btd_service *service,
420 source_state_cb cb, void *user_data)
422 struct source_state_callback *state_cb;
423 static unsigned int id = 0;
425 state_cb = g_new(struct source_state_callback, 1);
427 state_cb->service = service;
428 state_cb->user_data = user_data;
431 source_callbacks = g_slist_append(source_callbacks, state_cb);
436 gboolean source_remove_state_cb(unsigned int id)
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);