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