• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
8  *  Copyright (C) 2010  ProFUSION embedded systems
9  *
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24  *
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 
37 #include <glib.h>
38 #include <dbus/dbus.h>
39 #include <gdbus.h>
40 
41 #include <bluetooth/bluetooth.h>
42 #include <bluetooth/sdp.h>
43 #include <bluetooth/sdp_lib.h>
44 
45 #include "glib-helper.h"
46 #include "device.h"
47 #include "gateway.h"
48 #include "log.h"
49 #include "error.h"
50 #include "btio.h"
51 #include "dbus-common.h"
52 
53 #ifndef DBUS_TYPE_UNIX_FD
54 #define DBUS_TYPE_UNIX_FD -1
55 #endif
56 
57 struct hf_agent {
58 	char *name;	/* Bus id */
59 	char *path;	/* D-Bus path */
60 	guint watch;	/* Disconnect watch */
61 };
62 
63 struct gateway {
64 	gateway_state_t state;
65 	GIOChannel *rfcomm;
66 	GIOChannel *sco;
67 	gateway_stream_cb_t sco_start_cb;
68 	void *sco_start_cb_data;
69 	struct hf_agent *agent;
70 	DBusMessage *msg;
71 };
72 
73 int gateway_close(struct audio_device *device);
74 
state2str(gateway_state_t state)75 static const char *state2str(gateway_state_t state)
76 {
77 	switch (state) {
78 	case GATEWAY_STATE_DISCONNECTED:
79 		return "disconnected";
80 	case GATEWAY_STATE_CONNECTING:
81 		return "connecting";
82 	case GATEWAY_STATE_CONNECTED:
83 		return "connected";
84 	case GATEWAY_STATE_PLAYING:
85 		return "playing";
86 	default:
87 		return "";
88 	}
89 }
90 
agent_free(struct hf_agent * agent)91 static void agent_free(struct hf_agent *agent)
92 {
93 	if (!agent)
94 		return;
95 
96 	g_free(agent->name);
97 	g_free(agent->path);
98 	g_free(agent);
99 }
100 
change_state(struct audio_device * dev,gateway_state_t new_state)101 static void change_state(struct audio_device *dev, gateway_state_t new_state)
102 {
103 	struct gateway *gw = dev->gateway;
104 	const char *val;
105 
106 	if (gw->state == new_state)
107 		return;
108 
109 	val = state2str(new_state);
110 	gw->state = new_state;
111 
112 	emit_property_changed(dev->conn, dev->path,
113 			AUDIO_GATEWAY_INTERFACE, "State",
114 			DBUS_TYPE_STRING, &val);
115 }
116 
agent_disconnect(struct audio_device * dev,struct hf_agent * agent)117 static void agent_disconnect(struct audio_device *dev, struct hf_agent *agent)
118 {
119 	DBusMessage *msg;
120 
121 	msg = dbus_message_new_method_call(agent->name, agent->path,
122 			"org.bluez.HandsfreeAgent", "Release");
123 
124 	g_dbus_send_message(dev->conn, msg);
125 }
126 
agent_sendfd(struct hf_agent * agent,int fd,DBusPendingCallNotifyFunction notify,void * data)127 static gboolean agent_sendfd(struct hf_agent *agent, int fd,
128 		DBusPendingCallNotifyFunction notify, void *data)
129 {
130 	struct audio_device *dev = data;
131 	DBusMessage *msg;
132 	DBusPendingCall *call;
133 
134 	msg = dbus_message_new_method_call(agent->name, agent->path,
135 			"org.bluez.HandsfreeAgent", "NewConnection");
136 
137 	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
138 					DBUS_TYPE_INVALID);
139 
140 	if (dbus_connection_send_with_reply(dev->conn, msg, &call, -1) == FALSE)
141 		return FALSE;
142 
143 	dbus_pending_call_set_notify(call, notify, dev, NULL);
144 	dbus_pending_call_unref(call);
145 
146 	return TRUE;
147 }
148 
sco_io_cb(GIOChannel * chan,GIOCondition cond,struct audio_device * dev)149 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
150 			struct audio_device *dev)
151 {
152 	struct gateway *gw = dev->gateway;
153 
154 	if (cond & G_IO_NVAL)
155 		return FALSE;
156 
157 	if (cond & (G_IO_ERR | G_IO_HUP)) {
158 		DBG("sco connection is released");
159 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
160 		g_io_channel_unref(gw->sco);
161 		gw->sco = NULL;
162 		change_state(dev, GATEWAY_STATE_CONNECTED);
163 		return FALSE;
164 	}
165 
166 	return TRUE;
167 }
168 
sco_connect_cb(GIOChannel * chan,GError * err,gpointer user_data)169 static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
170 {
171 	struct audio_device *dev = (struct audio_device *) user_data;
172 	struct gateway *gw = dev->gateway;
173 
174 	DBG("at the begin of sco_connect_cb() in gateway.c");
175 
176 	gw->sco = g_io_channel_ref(chan);
177 
178 	if (gw->sco_start_cb)
179 		gw->sco_start_cb(dev, err, gw->sco_start_cb_data);
180 
181 	if (err) {
182 		error("sco_connect_cb(): %s", err->message);
183 		gateway_close(dev);
184 		return;
185 	}
186 
187 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
188 				(GIOFunc) sco_io_cb, dev);
189 }
190 
newconnection_reply(DBusPendingCall * call,void * data)191 static void newconnection_reply(DBusPendingCall *call, void *data)
192 {
193 	struct audio_device *dev = data;
194 	DBusMessage *reply = dbus_pending_call_steal_reply(call);
195 	DBusError derr;
196 
197 	if (!dev->gateway->rfcomm) {
198 		DBG("RFCOMM disconnected from server before agent reply");
199 		goto done;
200 	}
201 
202 	dbus_error_init(&derr);
203 	if (!dbus_set_error_from_message(&derr, reply)) {
204 		DBG("Agent reply: file descriptor passed successfully");
205 		change_state(dev, GATEWAY_STATE_CONNECTED);
206 		goto done;
207 	}
208 
209 	DBG("Agent reply: %s", derr.message);
210 
211 	dbus_error_free(&derr);
212 	gateway_close(dev);
213 
214 done:
215 	dbus_message_unref(reply);
216 }
217 
rfcomm_connect_cb(GIOChannel * chan,GError * err,gpointer user_data)218 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
219 				gpointer user_data)
220 {
221 	struct audio_device *dev = user_data;
222 	struct gateway *gw = dev->gateway;
223 	DBusMessage *reply;
224 	int sk, ret;
225 
226 	if (err) {
227 		error("connect(): %s", err->message);
228 		if (gw->sco_start_cb)
229 			gw->sco_start_cb(dev, err, gw->sco_start_cb_data);
230 		goto fail;
231 	}
232 
233 	if (!gw->agent) {
234 		error("Handsfree Agent not registered");
235 		goto fail;
236 	}
237 
238 	sk = g_io_channel_unix_get_fd(chan);
239 
240 	gw->rfcomm = g_io_channel_ref(chan);
241 
242 	ret = agent_sendfd(gw->agent, sk, newconnection_reply, dev);
243 
244 	if (!gw->msg)
245 		return;
246 
247 	if (ret)
248 		reply = dbus_message_new_method_return(gw->msg);
249 	else
250 		reply = btd_error_failed(gw->msg, "Can't pass file descriptor");
251 
252 	g_dbus_send_message(dev->conn, reply);
253 
254 	return;
255 
256 fail:
257 	if (gw->msg) {
258 		DBusMessage *reply;
259 		reply = btd_error_failed(gw->msg, "Connect failed");
260 		g_dbus_send_message(dev->conn, reply);
261 	}
262 
263 	change_state(dev, GATEWAY_STATE_DISCONNECTED);
264 }
265 
get_record_cb(sdp_list_t * recs,int err,gpointer user_data)266 static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
267 {
268 	struct audio_device *dev = user_data;
269 	struct gateway *gw = dev->gateway;
270 	int ch;
271 	sdp_list_t *protos, *classes;
272 	uuid_t uuid;
273 	GIOChannel *io;
274 	GError *gerr = NULL;
275 
276 	if (err < 0) {
277 		error("Unable to get service record: %s (%d)", strerror(-err),
278 					-err);
279 		goto fail;
280 	}
281 
282 	if (!recs || !recs->data) {
283 		error("No records found");
284 		err = -EIO;
285 		goto fail;
286 	}
287 
288 	if (sdp_get_service_classes(recs->data, &classes) < 0) {
289 		error("Unable to get service classes from record");
290 		err = -EINVAL;
291 		goto fail;
292 	}
293 
294 	if (sdp_get_access_protos(recs->data, &protos) < 0) {
295 		error("Unable to get access protocols from record");
296 		err = -ENODATA;
297 		goto fail;
298 	}
299 
300 	memcpy(&uuid, classes->data, sizeof(uuid));
301 	sdp_list_free(classes, free);
302 
303 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
304 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
305 		sdp_list_free(protos, NULL);
306 		error("Invalid service record or not HFP");
307 		err = -EIO;
308 		goto fail;
309 	}
310 
311 	ch = sdp_get_proto_port(protos, RFCOMM_UUID);
312 	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
313 	sdp_list_free(protos, NULL);
314 	if (ch <= 0) {
315 		error("Unable to extract RFCOMM channel from service record");
316 		err = -EIO;
317 		goto fail;
318 	}
319 
320 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &gerr,
321 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
322 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
323 				BT_IO_OPT_CHANNEL, ch,
324 				BT_IO_OPT_INVALID);
325 	if (!io) {
326 		error("Unable to connect: %s", gerr->message);
327 		gateway_close(dev);
328 		goto fail;
329 	}
330 
331 	g_io_channel_unref(io);
332 
333 	change_state(dev, GATEWAY_STATE_CONNECTING);
334 	return;
335 
336 fail:
337 	if (gw->msg) {
338 		DBusMessage *reply = btd_error_failed(gw->msg,
339 					gerr ? gerr->message : strerror(-err));
340 		g_dbus_send_message(dev->conn, reply);
341 	}
342 
343 	change_state(dev, GATEWAY_STATE_DISCONNECTED);
344 
345 	if (!gerr)
346 		g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED,
347 				"connect: %s (%d)", strerror(-err), -err);
348 
349 	if (gw->sco_start_cb)
350 		gw->sco_start_cb(dev, gerr, gw->sco_start_cb_data);
351 
352 	g_error_free(gerr);
353 }
354 
get_records(struct audio_device * device)355 static int get_records(struct audio_device *device)
356 {
357 	uuid_t uuid;
358 
359 	sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID);
360 	return bt_search_service(&device->src, &device->dst, &uuid,
361 				get_record_cb, device, NULL);
362 }
363 
ag_connect(DBusConnection * conn,DBusMessage * msg,void * data)364 static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
365 				void *data)
366 {
367 	struct audio_device *au_dev = (struct audio_device *) data;
368 	struct gateway *gw = au_dev->gateway;
369 	int err;
370 
371 	if (!gw->agent)
372 		return btd_error_agent_not_available(msg);
373 
374 	err = get_records(au_dev);
375 	if (err < 0)
376 		return btd_error_failed(msg, strerror(-err));
377 
378 	gw->msg = dbus_message_ref(msg);
379 
380 	return NULL;
381 }
382 
gateway_close(struct audio_device * device)383 int gateway_close(struct audio_device *device)
384 {
385 	struct gateway *gw = device->gateway;
386 	int sock;
387 
388 	if (gw->rfcomm) {
389 		sock = g_io_channel_unix_get_fd(gw->rfcomm);
390 		shutdown(sock, SHUT_RDWR);
391 
392 		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
393 		g_io_channel_unref(gw->rfcomm);
394 		gw->rfcomm = NULL;
395 	}
396 
397 	if (gw->sco) {
398 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
399 		g_io_channel_unref(gw->sco);
400 		gw->sco = NULL;
401 		gw->sco_start_cb = NULL;
402 		gw->sco_start_cb_data = NULL;
403 	}
404 
405 	change_state(device, GATEWAY_STATE_DISCONNECTED);
406 
407 	return 0;
408 }
409 
ag_disconnect(DBusConnection * conn,DBusMessage * msg,void * data)410 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
411 					void *data)
412 {
413 	struct audio_device *device = data;
414 	struct gateway *gw = device->gateway;
415 	DBusMessage *reply = NULL;
416 	char gw_addr[18];
417 
418 	if (!device->conn)
419 		return NULL;
420 
421 	reply = dbus_message_new_method_return(msg);
422 	if (!reply)
423 		return NULL;
424 
425 	if (!gw->rfcomm)
426 		return  btd_error_not_connected(msg);
427 
428 	gateway_close(device);
429 	ba2str(&device->dst, gw_addr);
430 	DBG("Disconnected from %s, %s", gw_addr, device->path);
431 
432 	return reply;
433 }
434 
agent_exited(DBusConnection * conn,void * data)435 static void agent_exited(DBusConnection *conn, void *data)
436 {
437 	struct gateway *gateway = data;
438 	struct hf_agent *agent = gateway->agent;
439 
440 	DBG("Agent %s exited", agent->name);
441 
442 	agent_free(agent);
443 	gateway->agent = NULL;
444 }
445 
ag_get_properties(DBusConnection * conn,DBusMessage * msg,void * data)446 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
447 					void *data)
448 {
449 	struct audio_device *device = data;
450 	struct gateway *gw = device->gateway;
451 	DBusMessage *reply;
452 	DBusMessageIter iter;
453 	DBusMessageIter dict;
454 	const char *value;
455 
456 
457 	reply = dbus_message_new_method_return(msg);
458 	if (!reply)
459 		return NULL;
460 
461 	dbus_message_iter_init_append(reply, &iter);
462 
463 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
464 			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
465 			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
466 			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
467 
468 	value = state2str(gw->state);
469 	dict_append_entry(&dict, "State",
470 			DBUS_TYPE_STRING, &value);
471 
472 	dbus_message_iter_close_container(&iter, &dict);
473 
474 	return reply;
475 }
476 
register_agent(DBusConnection * conn,DBusMessage * msg,void * data)477 static DBusMessage *register_agent(DBusConnection *conn,
478 					DBusMessage *msg, void *data)
479 {
480 	struct audio_device *device = data;
481 	struct gateway *gw = device->gateway;
482 	struct hf_agent *agent;
483 	const char *path, *name;
484 
485 	if (gw->agent)
486 		return btd_error_already_exists(msg);
487 
488 	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
489 						DBUS_TYPE_INVALID))
490 		return btd_error_invalid_args(msg);
491 
492 	name = dbus_message_get_sender(msg);
493 	agent = g_new0(struct hf_agent, 1);
494 
495 	agent->name = g_strdup(name);
496 	agent->path = g_strdup(path);
497 
498 	agent->watch = g_dbus_add_disconnect_watch(conn, name,
499 						agent_exited, gw, NULL);
500 
501 	gw->agent = agent;
502 
503 	return dbus_message_new_method_return(msg);
504 }
505 
unregister_agent(DBusConnection * conn,DBusMessage * msg,void * data)506 static DBusMessage *unregister_agent(DBusConnection *conn,
507 				DBusMessage *msg, void *data)
508 {
509 	struct audio_device *device = data;
510 	struct gateway *gw = device->gateway;
511 	const char *path;
512 
513 	if (!gw->agent)
514 		goto done;
515 
516 	if (strcmp(gw->agent->name, dbus_message_get_sender(msg)) != 0)
517 		return btd_error_not_authorized(msg);
518 
519 	if (!dbus_message_get_args(msg, NULL,
520 				DBUS_TYPE_OBJECT_PATH, &path,
521 				DBUS_TYPE_INVALID))
522 		return btd_error_invalid_args(msg);
523 
524 	if (strcmp(gw->agent->path, path) != 0)
525 		return btd_error_does_not_exist(msg);
526 
527 	g_dbus_remove_watch(device->conn, gw->agent->watch);
528 
529 	agent_free(gw->agent);
530 	gw->agent = NULL;
531 
532 done:
533 	return dbus_message_new_method_return(msg);
534 }
535 
536 static GDBusMethodTable gateway_methods[] = {
537 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
538 	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
539 	{ "GetProperties", "", "a{sv}", ag_get_properties },
540 	{ "RegisterAgent", "o", "", register_agent },
541 	{ "UnregisterAgent", "o", "", unregister_agent },
542 	{ NULL, NULL, NULL, NULL }
543 };
544 
545 static GDBusSignalTable gateway_signals[] = {
546 	{ "PropertyChanged", "sv" },
547 	{ NULL, NULL }
548 };
549 
path_unregister(void * data)550 static void path_unregister(void *data)
551 {
552 	struct audio_device *dev = data;
553 
554 	DBG("Unregistered interface %s on path %s",
555 		AUDIO_GATEWAY_INTERFACE, dev->path);
556 
557 	gateway_close(dev);
558 
559 	g_free(dev->gateway);
560 	dev->gateway = NULL;
561 }
562 
gateway_unregister(struct audio_device * dev)563 void gateway_unregister(struct audio_device *dev)
564 {
565 	if (dev->gateway->agent)
566 		agent_disconnect(dev, dev->gateway->agent);
567 
568 	g_dbus_unregister_interface(dev->conn, dev->path,
569 						AUDIO_GATEWAY_INTERFACE);
570 }
571 
gateway_init(struct audio_device * dev)572 struct gateway *gateway_init(struct audio_device *dev)
573 {
574 	if (DBUS_TYPE_UNIX_FD < 0)
575 		return NULL;
576 
577 	if (!g_dbus_register_interface(dev->conn, dev->path,
578 					AUDIO_GATEWAY_INTERFACE,
579 					gateway_methods, gateway_signals,
580 					NULL, dev, path_unregister))
581 		return NULL;
582 
583 	return g_new0(struct gateway, 1);
584 
585 }
586 
gateway_is_connected(struct audio_device * dev)587 gboolean gateway_is_connected(struct audio_device *dev)
588 {
589 	return (dev && dev->gateway &&
590 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
591 }
592 
gateway_connect_rfcomm(struct audio_device * dev,GIOChannel * io)593 int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
594 {
595 	if (!io)
596 		return -EINVAL;
597 
598 	dev->gateway->rfcomm = g_io_channel_ref(io);
599 
600 	return 0;
601 }
602 
gateway_connect_sco(struct audio_device * dev,GIOChannel * io)603 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
604 {
605 	struct gateway *gw = dev->gateway;
606 
607 	if (gw->sco)
608 		return -EISCONN;
609 
610 	gw->sco = g_io_channel_ref(io);
611 
612 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
613 						(GIOFunc) sco_io_cb, dev);
614 
615 	change_state(dev, GATEWAY_STATE_PLAYING);
616 
617 	return 0;
618 }
619 
gateway_start_service(struct audio_device * dev)620 void gateway_start_service(struct audio_device *dev)
621 {
622 	struct gateway *gw = dev->gateway;
623 	GError *err = NULL;
624 
625 	if (gw->rfcomm == NULL)
626 		return;
627 
628 	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL, &err)) {
629 		error("bt_io_accept: %s", err->message);
630 		g_error_free(err);
631 	}
632 }
633 
634 /* These are functions to be called from unix.c for audio system
635  * ifaces (alsa, gstreamer, etc.) */
gateway_request_stream(struct audio_device * dev,gateway_stream_cb_t cb,void * user_data)636 gboolean gateway_request_stream(struct audio_device *dev,
637 				gateway_stream_cb_t cb, void *user_data)
638 {
639 	struct gateway *gw = dev->gateway;
640 	GError *err = NULL;
641 	GIOChannel *io;
642 
643 	if (!gw->rfcomm) {
644 		gw->sco_start_cb = cb;
645 		gw->sco_start_cb_data = user_data;
646 		get_records(dev);
647 	} else if (!gw->sco) {
648 		gw->sco_start_cb = cb;
649 		gw->sco_start_cb_data = user_data;
650 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
651 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
652 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
653 				BT_IO_OPT_INVALID);
654 		if (!io) {
655 			error("%s", err->message);
656 			g_error_free(err);
657 			return FALSE;
658 		}
659 	} else if (cb)
660 		cb(dev, err, user_data);
661 
662 	return TRUE;
663 }
664 
gateway_config_stream(struct audio_device * dev,gateway_stream_cb_t sco_cb,void * user_data)665 int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t sco_cb,
666 				void *user_data)
667 {
668 	struct gateway *gw = dev->gateway;
669 
670 	if (!gw->rfcomm) {
671 		gw->sco_start_cb = sco_cb;
672 		gw->sco_start_cb_data = user_data;
673 		return get_records(dev);
674 	}
675 
676 	if (sco_cb)
677 		sco_cb(dev, NULL, user_data);
678 
679 	return 0;
680 }
681 
gateway_cancel_stream(struct audio_device * dev,unsigned int id)682 gboolean gateway_cancel_stream(struct audio_device *dev, unsigned int id)
683 {
684 	gateway_close(dev);
685 	return TRUE;
686 }
687 
gateway_get_sco_fd(struct audio_device * dev)688 int gateway_get_sco_fd(struct audio_device *dev)
689 {
690 	struct gateway *gw = dev->gateway;
691 
692 	if (!gw || !gw->sco)
693 		return -1;
694 
695 	return g_io_channel_unix_get_fd(gw->sco);
696 }
697 
gateway_suspend_stream(struct audio_device * dev)698 void gateway_suspend_stream(struct audio_device *dev)
699 {
700 	struct gateway *gw = dev->gateway;
701 
702 	if (!gw || !gw->sco)
703 		return;
704 
705 	g_io_channel_shutdown(gw->sco, TRUE, NULL);
706 	g_io_channel_unref(gw->sco);
707 	gw->sco = NULL;
708 	gw->sco_start_cb = NULL;
709 	gw->sco_start_cb_data = NULL;
710 	change_state(dev, GATEWAY_STATE_CONNECTED);
711 }
712