device.c revision 61d29a1847169342dd82713468e528224b54925c
1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2006-2007  Nokia Corporation
6 *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
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.
13 *
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.
18 *
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
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <stdio.h>
30#include <errno.h>
31#include <unistd.h>
32#include <sys/stat.h>
33#include <sys/param.h>
34#include <netinet/in.h>
35
36#include <bluetooth/bluetooth.h>
37#include <bluetooth/hci.h>
38#include <bluetooth/hci_lib.h>
39#include <bluetooth/sdp.h>
40#include <bluetooth/sdp_lib.h>
41
42#include <glib.h>
43#include <dbus/dbus.h>
44#include <gdbus.h>
45
46#include "logging.h"
47#include "textfile.h"
48#include "../src/adapter.h"
49#include "../src/device.h"
50
51#include "error.h"
52#include "ipc.h"
53#include "dbus-common.h"
54#include "device.h"
55#include "unix.h"
56#include "avdtp.h"
57#include "control.h"
58#include "headset.h"
59#include "gateway.h"
60#include "sink.h"
61#include "source.h"
62
63#define AUDIO_INTERFACE "org.bluez.Audio"
64
65#define CONTROL_CONNECT_TIMEOUT 2
66#define AVDTP_CONNECT_TIMEOUT 1
67#define HEADSET_CONNECT_TIMEOUT 1
68
69typedef enum {
70	AUDIO_STATE_DISCONNECTED,
71	AUDIO_STATE_CONNECTING,
72	AUDIO_STATE_CONNECTED,
73} audio_state_t;
74
75struct service_auth {
76	service_auth_cb cb;
77	void *user_data;
78};
79
80struct dev_priv {
81	audio_state_t state;
82
83	headset_state_t hs_state;
84	sink_state_t sink_state;
85	avctp_state_t avctp_state;
86	GSList *auths;
87
88	DBusMessage *conn_req;
89	DBusMessage *dc_req;
90
91	guint control_timer;
92	guint avdtp_timer;
93	guint headset_timer;
94
95	gboolean authorized;
96};
97
98static unsigned int sink_callback_id = 0;
99static unsigned int avctp_callback_id = 0;
100static unsigned int avdtp_callback_id = 0;
101static unsigned int headset_callback_id = 0;
102
103static void device_free(struct audio_device *dev)
104{
105	struct dev_priv *priv = dev->priv;
106
107	if (dev->conn)
108		dbus_connection_unref(dev->conn);
109
110	btd_device_unref(dev->btd_dev);
111
112	if (priv) {
113		if (priv->control_timer)
114			g_source_remove(priv->control_timer);
115		if (priv->avdtp_timer)
116			g_source_remove(priv->avdtp_timer);
117		if (priv->headset_timer)
118			g_source_remove(priv->headset_timer);
119		if (priv->dc_req)
120			dbus_message_unref(priv->dc_req);
121		if (priv->conn_req)
122			dbus_message_unref(priv->conn_req);
123		g_free(priv);
124	}
125
126	g_free(dev->path);
127	g_free(dev);
128}
129
130static const char *state2str(audio_state_t state)
131{
132	switch (state) {
133	case AUDIO_STATE_DISCONNECTED:
134		return "disconnected";
135	case AUDIO_STATE_CONNECTING:
136		return "connecting";
137	case AUDIO_STATE_CONNECTED:
138		return "connected";
139	default:
140		error("Invalid audio state %d", state);
141		return NULL;
142	}
143}
144
145static void device_set_state(struct audio_device *dev, audio_state_t new_state)
146{
147	struct dev_priv *priv = dev->priv;
148	const char *state_str;
149	DBusMessage *reply = NULL;
150
151	state_str = state2str(new_state);
152	if (!state_str)
153		return;
154
155	if (new_state == AUDIO_STATE_DISCONNECTED)
156		priv->authorized = FALSE;
157
158	if (dev->priv->state == new_state) {
159		debug("state change attempted from %s to %s",
160							state_str, state_str);
161		return;
162	}
163
164	dev->priv->state = new_state;
165
166	if (priv->dc_req && new_state == AUDIO_STATE_DISCONNECTED) {
167		reply = dbus_message_new_method_return(priv->dc_req);
168		dbus_message_unref(priv->dc_req);
169		priv->dc_req = NULL;
170		g_dbus_send_message(dev->conn, reply);
171	}
172
173	if (priv->conn_req && new_state != AUDIO_STATE_CONNECTING) {
174		if (new_state == AUDIO_STATE_CONNECTED)
175			reply = dbus_message_new_method_return(priv->conn_req);
176		else
177			reply = g_dbus_create_error(priv->conn_req,
178							ERROR_INTERFACE
179							".ConnectFailed",
180							"Connecting failed");
181		dbus_message_unref(priv->conn_req);
182		priv->conn_req = NULL;
183		g_dbus_send_message(dev->conn, reply);
184	}
185
186	emit_property_changed(dev->conn, dev->path,
187				AUDIO_INTERFACE, "State",
188				DBUS_TYPE_STRING, &state_str);
189}
190
191static gboolean control_connect_timeout(gpointer user_data)
192{
193	struct audio_device *dev = user_data;
194
195	dev->priv->control_timer = 0;
196
197	if (dev->control)
198		avrcp_connect(dev);
199
200	return FALSE;
201}
202
203static gboolean device_set_control_timer(struct audio_device *dev)
204{
205	struct dev_priv *priv = dev->priv;
206
207	if (!dev->control)
208		return FALSE;
209
210	if (priv->control_timer)
211		return FALSE;
212
213	priv->control_timer = g_timeout_add_seconds(CONTROL_CONNECT_TIMEOUT,
214							control_connect_timeout,
215							dev);
216
217	return TRUE;
218}
219
220static void device_remove_control_timer(struct audio_device *dev)
221{
222	if (dev->priv->control_timer)
223		g_source_remove(dev->priv->control_timer);
224	dev->priv->control_timer = 0;
225}
226
227static gboolean avdtp_connect_timeout(gpointer user_data)
228{
229	struct audio_device *dev = user_data;
230
231	dev->priv->avdtp_timer = 0;
232
233	if (dev->sink) {
234		struct avdtp *session = avdtp_get(&dev->src, &dev->dst);
235
236		if (!session)
237			return FALSE;
238
239		sink_setup_stream(dev->sink, session);
240		avdtp_unref(session);
241	}
242
243	return FALSE;
244}
245
246static gboolean device_set_avdtp_timer(struct audio_device *dev)
247{
248	struct dev_priv *priv = dev->priv;
249
250	if (!dev->sink)
251		return FALSE;
252
253	if (priv->avdtp_timer)
254		return FALSE;
255
256	priv->avdtp_timer = g_timeout_add_seconds(AVDTP_CONNECT_TIMEOUT,
257							avdtp_connect_timeout,
258							dev);
259
260	return TRUE;
261}
262
263static void device_remove_avdtp_timer(struct audio_device *dev)
264{
265	if (dev->priv->avdtp_timer)
266		g_source_remove(dev->priv->avdtp_timer);
267	dev->priv->avdtp_timer = 0;
268}
269
270static gboolean headset_connect_timeout(gpointer user_data)
271{
272	struct audio_device *dev = user_data;
273	struct dev_priv *priv = dev->priv;
274
275	dev->priv->headset_timer = 0;
276
277	if (dev->headset == NULL)
278		return FALSE;
279
280	if (headset_config_stream(dev, FALSE, NULL, NULL) == 0) {
281		if (priv->state != AUDIO_STATE_CONNECTED &&
282				(priv->sink_state == SINK_STATE_CONNECTED ||
283				priv->sink_state == SINK_STATE_PLAYING))
284			device_set_state(dev, AUDIO_STATE_CONNECTED);
285	}
286
287	return FALSE;
288}
289
290static gboolean device_set_headset_timer(struct audio_device *dev)
291{
292	struct dev_priv *priv = dev->priv;
293
294	if (!dev->headset)
295		return FALSE;
296
297	if (priv->headset_timer)
298		return FALSE;
299
300	priv->headset_timer = g_timeout_add_seconds(HEADSET_CONNECT_TIMEOUT,
301						headset_connect_timeout, dev);
302
303	return TRUE;
304}
305
306static void device_remove_headset_timer(struct audio_device *dev)
307{
308	if (dev->priv->headset_timer)
309		g_source_remove(dev->priv->headset_timer);
310	dev->priv->headset_timer = 0;
311}
312
313static void device_avdtp_cb(struct audio_device *dev, struct avdtp *session,
314				avdtp_session_state_t old_state,
315				avdtp_session_state_t new_state,
316				void *user_data)
317{
318	if (!dev->sink || !dev->control)
319		return;
320
321	if (new_state == AVDTP_SESSION_STATE_CONNECTED) {
322		if (avdtp_stream_setup_active(session))
323			device_set_control_timer(dev);
324		else
325			avrcp_connect(dev);
326	}
327}
328
329static void device_sink_cb(struct audio_device *dev,
330				sink_state_t old_state,
331				sink_state_t new_state,
332				void *user_data)
333{
334	struct dev_priv *priv = dev->priv;
335
336	if (!dev->sink)
337		return;
338
339	priv->sink_state = new_state;
340
341	switch (new_state) {
342	case SINK_STATE_DISCONNECTED:
343		if (dev->control) {
344			device_remove_control_timer(dev);
345			avrcp_disconnect(dev);
346		}
347		if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
348			device_set_state(dev, AUDIO_STATE_DISCONNECTED);
349		else if (old_state == SINK_STATE_CONNECTING) {
350			switch (priv->hs_state) {
351			case HEADSET_STATE_CONNECTED:
352			case HEADSET_STATE_PLAY_IN_PROGRESS:
353			case HEADSET_STATE_PLAYING:
354				device_set_state(dev, AUDIO_STATE_CONNECTED);
355			default:
356				break;
357			}
358		}
359		break;
360	case SINK_STATE_CONNECTING:
361		device_remove_avdtp_timer(dev);
362		if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
363			device_set_state(dev, AUDIO_STATE_CONNECTING);
364		break;
365	case SINK_STATE_CONNECTED:
366		if (old_state == SINK_STATE_PLAYING)
367			break;
368		if (dev->auto_connect) {
369			if (!dev->headset)
370				device_set_state(dev, AUDIO_STATE_CONNECTED);
371			else if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
372				device_set_headset_timer(dev);
373			else if (priv->hs_state == HEADSET_STATE_CONNECTED ||
374					priv->hs_state == HEADSET_STATE_PLAY_IN_PROGRESS ||
375					priv->hs_state == HEADSET_STATE_PLAYING)
376				device_set_state(dev, AUDIO_STATE_CONNECTED);
377		} else if (priv->hs_state == HEADSET_STATE_DISCONNECTED ||
378				priv->hs_state == HEADSET_STATE_CONNECTING)
379			device_set_state(dev, AUDIO_STATE_CONNECTED);
380		break;
381	case SINK_STATE_PLAYING:
382		break;
383	}
384}
385
386static void device_avctp_cb(struct audio_device *dev,
387				avctp_state_t old_state,
388				avctp_state_t new_state,
389				void *user_data)
390{
391	if (!dev->control)
392		return;
393
394	dev->priv->avctp_state = new_state;
395
396	switch (new_state) {
397	case AVCTP_STATE_DISCONNECTED:
398		break;
399	case AVCTP_STATE_CONNECTING:
400		device_remove_control_timer(dev);
401		break;
402	case AVCTP_STATE_CONNECTED:
403		break;
404	}
405}
406
407static void device_headset_cb(struct audio_device *dev,
408				headset_state_t old_state,
409				headset_state_t new_state,
410				void *user_data)
411{
412	struct dev_priv *priv = dev->priv;
413
414	if (!dev->headset)
415		return;
416
417	priv->hs_state = new_state;
418
419	switch (new_state) {
420	case HEADSET_STATE_DISCONNECTED:
421		device_remove_avdtp_timer(dev);
422		if (priv->sink_state != SINK_STATE_DISCONNECTED &&
423						dev->sink && priv->dc_req) {
424			sink_shutdown(dev->sink);
425			break;
426		}
427		if (priv->sink_state == SINK_STATE_DISCONNECTED)
428			device_set_state(dev, AUDIO_STATE_DISCONNECTED);
429		else if (old_state == HEADSET_STATE_CONNECTING &&
430				(priv->sink_state == SINK_STATE_CONNECTED ||
431				priv->sink_state == SINK_STATE_PLAYING))
432			device_set_state(dev, AUDIO_STATE_CONNECTED);
433		break;
434	case HEADSET_STATE_CONNECTING:
435		device_remove_headset_timer(dev);
436		if (priv->sink_state == SINK_STATE_DISCONNECTED)
437			device_set_state(dev, AUDIO_STATE_CONNECTING);
438		break;
439	case HEADSET_STATE_CONNECTED:
440		if (old_state == HEADSET_STATE_CONNECTED ||
441				old_state == HEADSET_STATE_PLAY_IN_PROGRESS ||
442				old_state == HEADSET_STATE_PLAYING)
443			break;
444		if (dev->auto_connect) {
445			if (!dev->sink)
446				device_set_state(dev, AUDIO_STATE_CONNECTED);
447			else if (priv->sink_state == SINK_STATE_DISCONNECTED)
448				device_set_avdtp_timer(dev);
449			else if (priv->sink_state == SINK_STATE_CONNECTED ||
450					priv->sink_state == SINK_STATE_PLAYING)
451				device_set_state(dev, AUDIO_STATE_CONNECTED);
452		} else if (priv->sink_state == SINK_STATE_DISCONNECTED ||
453				priv->sink_state == SINK_STATE_CONNECTING)
454			device_set_state(dev, AUDIO_STATE_CONNECTED);
455		break;
456	case HEADSET_STATE_PLAY_IN_PROGRESS:
457		break;
458	case HEADSET_STATE_PLAYING:
459		break;
460	}
461}
462
463static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
464								void *data)
465{
466	struct audio_device *dev = data;
467	struct dev_priv *priv = dev->priv;
468
469	if (priv->state == AUDIO_STATE_CONNECTING)
470		return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress",
471						"Connect in Progress");
472	else if (priv->state == AUDIO_STATE_CONNECTED)
473		return g_dbus_create_error(msg, ERROR_INTERFACE
474						".AlreadyConnected",
475						"Already Connected");
476
477	dev->auto_connect = TRUE;
478
479	if (dev->headset)
480		headset_config_stream(dev, FALSE, NULL, NULL);
481
482	if (priv->state != AUDIO_STATE_CONNECTING && dev->sink) {
483		struct avdtp *session = avdtp_get(&dev->src, &dev->dst);
484
485		if (!session)
486			return g_dbus_create_error(msg, ERROR_INTERFACE
487					".Failed",
488					"Failed to get AVDTP session");
489
490		sink_setup_stream(dev->sink, session);
491		avdtp_unref(session);
492	}
493
494	/* The previous calls should cause a call to the state callback to
495	 * indicate AUDIO_STATE_CONNECTING */
496	if (priv->state != AUDIO_STATE_CONNECTING)
497		return g_dbus_create_error(msg, ERROR_INTERFACE
498				".ConnectFailed",
499				"Headset connect failed");
500
501	priv->conn_req = dbus_message_ref(msg);
502
503	return NULL;
504}
505
506static DBusMessage *dev_disconnect(DBusConnection *conn, DBusMessage *msg,
507								void *data)
508{
509	struct audio_device *dev = data;
510	struct dev_priv *priv = dev->priv;
511
512	if (priv->state == AUDIO_STATE_DISCONNECTED)
513		return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected",
514						"Not connected");
515
516	if (priv->dc_req)
517		return dbus_message_new_method_return(msg);
518
519	priv->dc_req = dbus_message_ref(msg);
520
521	if (priv->hs_state != HEADSET_STATE_DISCONNECTED)
522		headset_shutdown(dev);
523	else if (dev->sink && priv->sink_state != SINK_STATE_DISCONNECTED)
524		sink_shutdown(dev->sink);
525	else {
526		dbus_message_unref(priv->dc_req);
527		priv->dc_req = NULL;
528		return dbus_message_new_method_return(msg);
529	}
530
531	return NULL;
532}
533
534static DBusMessage *dev_get_properties(DBusConnection *conn, DBusMessage *msg,
535								void *data)
536{
537	struct audio_device *device = data;
538	DBusMessage *reply;
539	DBusMessageIter iter;
540	DBusMessageIter dict;
541	const char *state;
542
543	reply = dbus_message_new_method_return(msg);
544	if (!reply)
545		return NULL;
546
547	dbus_message_iter_init_append(reply, &iter);
548
549	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
550			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
551			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
552			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
553
554	/* State */
555	state = state2str(device->priv->state);
556	if (state)
557		dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
558
559	dbus_message_iter_close_container(&iter, &dict);
560
561	return reply;
562}
563
564static GDBusMethodTable dev_methods[] = {
565	{ "Connect",		"",	"",	dev_connect,
566						G_DBUS_METHOD_FLAG_ASYNC },
567	{ "Disconnect",		"",	"",	dev_disconnect },
568	{ "GetProperties",	"",	"a{sv}",dev_get_properties },
569	{ NULL, NULL, NULL, NULL }
570};
571
572static GDBusSignalTable dev_signals[] = {
573	{ "PropertyChanged",		"sv"	},
574	{ NULL, NULL }
575};
576
577struct audio_device *audio_device_register(DBusConnection *conn,
578					struct btd_device *device,
579					const char *path, const bdaddr_t *src,
580					const bdaddr_t *dst)
581{
582	struct audio_device *dev;
583
584	if (!conn || !path)
585		return NULL;
586
587	dev = g_new0(struct audio_device, 1);
588
589	dev->btd_dev = btd_device_ref(device);
590	dev->path = g_strdup(path);
591	bacpy(&dev->dst, dst);
592	bacpy(&dev->src, src);
593	dev->conn = dbus_connection_ref(conn);
594	dev->priv = g_new0(struct dev_priv, 1);
595	dev->priv->state = AUDIO_STATE_DISCONNECTED;
596
597	if (!g_dbus_register_interface(dev->conn, dev->path,
598					AUDIO_INTERFACE,
599					dev_methods, dev_signals, NULL,
600					dev, NULL)) {
601		error("Unable to register %s on %s", AUDIO_INTERFACE,
602								dev->path);
603		device_free(dev);
604		return NULL;
605	}
606
607	debug("Registered interface %s on path %s", AUDIO_INTERFACE,
608								dev->path);
609
610	if (sink_callback_id == 0)
611		sink_callback_id = sink_add_state_cb(device_sink_cb, NULL);
612
613	if (avdtp_callback_id == 0)
614		avdtp_callback_id = avdtp_add_state_cb(device_avdtp_cb, NULL);
615	if (avctp_callback_id == 0)
616		avctp_callback_id = avctp_add_state_cb(device_avctp_cb, NULL);
617
618	if (headset_callback_id == 0)
619		headset_callback_id = headset_add_state_cb(device_headset_cb,
620									NULL);
621
622	return dev;
623}
624
625gboolean audio_device_is_active(struct audio_device *dev,
626						const char *interface)
627{
628	if (!interface) {
629		if ((dev->sink || dev->source) &&
630			avdtp_is_connected(&dev->src, &dev->dst))
631			return TRUE;
632
633		if (dev->headset && headset_is_active(dev))
634			return TRUE;
635	}
636	else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink &&
637			avdtp_is_connected(&dev->src, &dev->dst))
638		return TRUE;
639	else if (!strcmp(interface, AUDIO_SOURCE_INTERFACE) && dev->source &&
640			avdtp_is_connected(&dev->src, &dev->dst))
641		return TRUE;
642	else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset &&
643			headset_is_active(dev))
644		return TRUE;
645	else if (!strcmp(interface, AUDIO_CONTROL_INTERFACE) && dev->control &&
646			control_is_active(dev))
647		return TRUE;
648	else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway &&
649			gateway_is_connected(dev))
650		return TRUE;
651
652	return FALSE;
653}
654
655void audio_device_unregister(struct audio_device *device)
656{
657	unix_device_removed(device);
658
659	if (device->hs_preauth_id) {
660		g_source_remove(device->hs_preauth_id);
661		device->hs_preauth_id = 0;
662	}
663
664	if (device->headset)
665		headset_unregister(device);
666
667	if (device->sink)
668		sink_unregister(device);
669
670	if (device->source)
671		source_unregister(device);
672
673	if (device->control)
674		control_unregister(device);
675
676	g_dbus_unregister_interface(device->conn, device->path,
677						AUDIO_INTERFACE);
678
679	device_free(device);
680}
681
682static void auth_cb(DBusError *derr, void *user_data)
683{
684	struct audio_device *dev = user_data;
685	struct dev_priv *priv = dev->priv;
686
687	if (derr == NULL)
688		priv->authorized = TRUE;
689
690	while (priv->auths) {
691		struct service_auth *auth = priv->auths->data;
692
693		auth->cb(derr, auth->user_data);
694		priv->auths = g_slist_remove(priv->auths, auth);
695		g_free(auth);
696	}
697}
698
699static gboolean auth_idle_cb(gpointer user_data)
700{
701	auth_cb(NULL, user_data);
702	return FALSE;
703}
704
705static gboolean audio_device_is_connected(struct audio_device *dev)
706{
707	if (dev->headset) {
708		headset_state_t state = headset_get_state(dev);
709
710		if (state == HEADSET_STATE_CONNECTED ||
711				state == HEADSET_STATE_PLAY_IN_PROGRESS ||
712				state == HEADSET_STATE_PLAYING)
713			return TRUE;
714	}
715
716	if (dev->sink) {
717		sink_state_t state = sink_get_state(dev);
718
719		if (state == SINK_STATE_CONNECTED ||
720				state == SINK_STATE_PLAYING)
721			return TRUE;
722	}
723
724	if (dev->source) {
725		source_state_t state = source_get_state(dev);
726
727		if (state == SOURCE_STATE_CONNECTED ||
728				state == SOURCE_STATE_PLAYING)
729			return TRUE;
730	}
731
732	return FALSE;
733}
734
735int audio_device_request_authorization(struct audio_device *dev,
736					const char *uuid, service_auth_cb cb,
737					void *user_data)
738{
739	struct dev_priv *priv = dev->priv;
740	struct service_auth *auth;
741	int err;
742
743	auth = g_try_new0(struct service_auth, 1);
744	if (!auth)
745		return -ENOMEM;
746
747	auth->cb = cb;
748	auth->user_data = user_data;
749
750	priv->auths = g_slist_append(priv->auths, auth);
751	if (g_slist_length(priv->auths) > 1)
752		return 0;
753
754	if (priv->authorized || audio_device_is_connected(dev)) {
755		g_idle_add(auth_idle_cb, dev);
756		return 0;
757	}
758
759	err = btd_request_authorization(&dev->src, &dev->dst, uuid, auth_cb,
760					dev);
761	if (err < 0) {
762		priv->auths = g_slist_remove(priv->auths, auth);
763		g_free(auth);
764	}
765
766	return err;
767}
768
769int audio_device_cancel_authorization(struct audio_device *dev,
770					authorization_cb cb, void *user_data)
771{
772	struct dev_priv *priv = dev->priv;
773	GSList *l, *next;
774
775	for (l = priv->auths; l != NULL; l = next) {
776		struct service_auth *auth = priv->auths->data;
777
778		next = g_slist_next(l);
779
780		if (cb && auth->cb != cb)
781			continue;
782
783		if (user_data && auth->user_data != user_data)
784			continue;
785
786		priv->auths = g_slist_remove(priv->auths, auth);
787		g_free(auth);
788	}
789
790	if (g_slist_length(priv->auths) == 0)
791		btd_cancel_authorization(&dev->src, &dev->dst);
792
793	return 0;
794}
795