diff options
author | Wim Taymans <wtaymans@redhat.com> | 2014-08-07 10:51:54 +0200 |
---|---|---|
committer | Wim Taymans <wtaymans@redhat.com> | 2014-08-07 10:51:54 +0200 |
commit | c0885c29f573a552605387c1d0e760ddd6311ed4 (patch) | |
tree | 7939a1873e1d0188a04db45bccfee98a79f4e239 /src | |
parent | 4ca6de134ead2aaa2280429df0ced0681c9ca523 (diff) |
remove transport
Move transport into headset
Diffstat (limited to 'src')
-rw-r--r-- | src/hsd-headset-transport.c | 339 | ||||
-rw-r--r-- | src/hsd-headset-transport.h | 42 | ||||
-rw-r--r-- | src/hsd-headset.c | 291 | ||||
-rw-r--r-- | src/hsd-headset.h | 11 |
4 files changed, 245 insertions, 438 deletions
diff --git a/src/hsd-headset-transport.c b/src/hsd-headset-transport.c deleted file mode 100644 index 05acf38..0000000 --- a/src/hsd-headset-transport.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) 2014 Wim Taymans <wim.taymans@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include <errno.h> -#include <sys/types.h> -#include <sys/socket.h> - -#include <bluetooth/bluetooth.h> -#include <bluetooth/sco.h> - -#include <gio/gio.h> -#include <gio/gunixfdlist.h> - -#include "hsd.h" -#include "hsd-headset.h" -#include "hsd-headset-transport.h" - -static GDBusNodeInfo *introspection_data = NULL; - -/* Introspection data for the service we are exporting */ -static const gchar introspection_xml[] = - "<node>" - " <interface name='org.freedesktop.HeadsetTransport'>" - " <method name='Acquire'>" - " <arg type='h' name='fd' direction='out'/>" - " <arg type='q' name='mtu_r' direction='out'/>" - " <arg type='q' name='mtu_w' direction='out'/>" - " </method>" - " <method name='Release'>" - " </method>" - " </interface>" - "</node>"; - -static GDBusInterfaceInfo * -get_headset_transport_interface_info (void) -{ - static GDBusNodeInfo *introspection_data = NULL; - - if (introspection_data == NULL) - introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); - - return introspection_data->interfaces[0]; -} - -static void -headset_transport_set_state (HsdHeadsetTransport *t, - HsdTransportState state) -{ - if (t->state == state) - return; - - t->state = state; -} - -static gboolean -transport_connect_cb (GIOChannel *io, GIOCondition cond, gpointer user_data) -{ - HsdHeadsetTransport *t = user_data; - GUnixFDList *fdlist; - gint sock; - GDBusMethodInvocation *invocation; - - invocation = t->invocation; - t->invocation = NULL; - - if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) - goto connect_failed; - - headset_transport_set_state (t, HSD_TRANSPORT_STATE_ACTIVE); - - g_debug ("connected"); - fdlist = g_unix_fd_list_new (); - g_unix_fd_list_append (fdlist, t->fd, NULL); - - g_dbus_method_invocation_return_value_with_unix_fd_list ( - invocation, g_variant_new ("(hqq)", 0, 48, 48), fdlist); - - return FALSE; - -connect_failed: - { - close (t->fd); - t->fd = -1; - headset_transport_set_state (t, HSD_TRANSPORT_STATE_IDLE); - g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.Failed", "failed to connect"); - return FALSE; - } -} - -static void -headset_transport_acquire (HsdHeadsetTransport *t, - GDBusConnection *connection, - GVariant *parameters, - GDBusMethodInvocation *invocation) -{ - GDBusMessage *reply; - GError *error; - guchar *blob; - gsize out_size; - struct sockaddr_sco addr; - int err, i; - GIOCondition cond; - GIOChannel *io; - bdaddr_t src; - bdaddr_t dst; - int voice = 0x60; - socklen_t len; - gchar *src_addr; - gchar *dst_addr; - - g_message ("transport Acquire"); - - if (t->state != HSD_TRANSPORT_STATE_IDLE) - goto not_idle; - - src_addr = t->headset->adapter_addr; - dst_addr = t->headset->device_addr; - - for (i = 5; i >= 0; i--, src_addr += 3) - src.b[i] = strtol(src_addr, NULL, 16); - for (i = 5; i >= 0; i--, dst_addr += 3) - dst.b[i] = strtol(dst_addr, NULL, 16); - - t->fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, BTPROTO_SCO); - if (t->fd < 0) - goto socket_failed; - - g_debug ("got fd %d", t->fd); - - memset(&addr, 0, sizeof(addr)); - addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, &src); - - if (bind(t->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) - goto bind_failed; - - if (voice) { - struct bt_voice opts; - - /* SCO voice setting */ - memset(&opts, 0, sizeof(opts)); - opts.setting = voice; - if (setsockopt(t->fd, SOL_BLUETOOTH, BT_VOICE, &opts, sizeof(opts)) < 0) - goto sockopt_failed; - } - - memset(&addr, 0, sizeof(addr)); - addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, &dst); - - g_debug ("doing connect"); - - err = connect(t->fd, (struct sockaddr *) &addr, sizeof(addr)); - if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) - goto connect_failed; - - headset_transport_set_state (t, HSD_TRANSPORT_STATE_PENDING); - - t->invocation = invocation; - - io = g_io_channel_unix_new(t->fd); - g_io_add_watch(io, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - transport_connect_cb, t); - g_io_channel_unref(io); - - return; - -not_idle: - { - g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.Failed", "transport not idle"); - return; - } -socket_failed: - { - g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.Failed", "failed to create socket"); - goto close_fd; - } -bind_failed: - { - g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.Failed", "failed to bind socket"); - goto close_fd; - } -sockopt_failed: - { - g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.Failed", "failed to setsockopt"); - goto close_fd; - } -connect_failed: - { - g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.Failed", "failed to connect"); - goto close_fd; - } -close_fd: - { - close (t->fd); - t->fd = -1; - return; - } -} - -static void -headset_transport_release (HsdHeadsetTransport *t, - GDBusConnection *connection, - GVariant *parameters, - GDBusMethodInvocation *invocation) -{ - g_message ("transport release"); - - if (t->fd != -1) { - shutdown (t->fd, SHUT_RDWR); - close (t->fd); - } - t->fd = -1; - - g_dbus_method_invocation_return_value (invocation, NULL); - - headset_transport_set_state (t, HSD_TRANSPORT_STATE_IDLE); -} - -static void -headset_transport_method_call (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - HsdHeadsetTransport *t = user_data; - - if (t->invocation) - goto busy; - - if (strcmp (t->owner, sender) != 0) - goto not_owner; - - if (g_strcmp0 (method_name, "Acquire") == 0) { - headset_transport_acquire (t, connection, parameters, invocation); - } else if (g_strcmp0 (method_name, "Release") == 0) { - headset_transport_release (t, connection, parameters, invocation); - } else - g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.NotImplemented", "no such method"); - - return; - -busy: - { - g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.Busy", "We have a pending operation"); - return; - } -not_owner: - { - g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.NotAuthorized", "not the transport owner"); - return; - } -} - -static const GDBusInterfaceVTable headset_transport_interface_vtable = -{ - headset_transport_method_call, - NULL, - NULL -}; - -HsdHeadsetTransport * -hsd_headset_transport_new (HsdHeadset *headset, gchar *name, const gchar *owner, GVariantIter *props, GError **error) -{ - GDBusConnection *conn = hsd_dbus_connection_get (); - HsdHeadsetTransport *transport; - - transport = g_new0 (HsdHeadsetTransport, 1); - transport->headset = headset; - transport->name = name; - transport->owner = g_strdup (owner); - transport->state = HSD_TRANSPORT_STATE_IDLE; - transport->fd = -1; - - /* FIXME, use props */ - if (props) - g_variant_iter_free (props); - - /* FIXME, watch owner */ - - g_debug ("Registering headset-transport object"); - transport->id = g_dbus_connection_register_object (conn, - name, - get_headset_transport_interface_info(), - &headset_transport_interface_vtable, - transport, - NULL, /* user_data_free_func */ - error); /* GError** */ - if (transport->id == 0) { - hsd_headset_transport_free (transport); - return NULL; - } - - return transport; -} - -void -hsd_headset_transport_free (HsdHeadsetTransport *transport) -{ - GDBusConnection *conn = hsd_dbus_connection_get (); - - if (transport->id) - g_dbus_connection_unregister_object (conn, transport->id); - if (transport->fd != -1) - close (transport->fd); - g_free (transport->name); - g_free (transport->owner); - g_free (transport); -} - diff --git a/src/hsd-headset-transport.h b/src/hsd-headset-transport.h deleted file mode 100644 index 9eb131e..0000000 --- a/src/hsd-headset-transport.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2014 Wim Taymans <wim.taymans@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -typedef enum { - HSD_TRANSPORT_STATE_IDLE, - HSD_TRANSPORT_STATE_PENDING, - HSD_TRANSPORT_STATE_ACTIVE, -} HsdTransportState; - -struct _HsdHeadsetTransport { - HsdHeadset *headset; - - gchar *name; - gchar *owner; - - guint id; - - gint fd; - HsdTransportState state; - GDBusMethodInvocation *invocation; -}; - -HsdHeadsetTransport * hsd_headset_transport_new (HsdHeadset *headset, gchar *name, - const gchar *owner, GVariantIter *props, - GError **error); -void hsd_headset_transport_free (HsdHeadsetTransport *transport); diff --git a/src/hsd-headset.c b/src/hsd-headset.c index 1af096c..3011272 100644 --- a/src/hsd-headset.c +++ b/src/hsd-headset.c @@ -17,11 +17,18 @@ * Boston, MA 02110-1301, USA. */ +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/sco.h> + #include <gio/gio.h> +#include <gio/gunixfdlist.h> #include "hsd.h" #include "hsd-headset.h" -#include "hsd-headset-transport.h" static guint count = 0; @@ -31,14 +38,14 @@ static GDBusNodeInfo *introspection_data = NULL; static const gchar introspection_xml[] = "<node>" " <interface name='org.freedesktop.Headset'>" - " <method name='GetTransport'>" + " <method name='Connect'>" " <arg type='a{sv}' name='properties' direction='in'/>" - " <arg type='o' name='transport' direction='out'/>" + " <arg type='a{sv}' name='results' direction='out'/>" " </method>" - " <method name='ReleaseTransport'>" - " <arg type='o' name='transport' direction='in'/>" + " <method name='Disconnect'>" " </method>" " <property type='o' name='Device' access='read'/>" + " <property type='s' name='State' access='read'/>" " </interface>" "</node>"; @@ -53,81 +60,220 @@ get_headset_interface_info (void) return introspection_data->interfaces[0]; } +static const gchar * +headset_state_to_string (HsdHeadsetState state) +{ + switch (state) { + case HSD_HEADSET_STATE_IDLE: + return "idle"; + case HSD_HEADSET_STATE_PENDING: + return "pending"; + case HSD_HEADSET_STATE_ACTIVE: + return "active"; + default: + return "invalid"; + } +} + static void -headset_get_transport (HsdHeadset *h, - GDBusConnection *connection, - const gchar *sender, - GVariant *parameters, - GDBusMethodInvocation *invocation) +headset_set_state (HsdHeadset *h, + HsdHeadsetState state) { - GVariantIter *props; - HsdHeadsetTransport *t; - GError *error = NULL; - gchar *tname; + if (h->state == state) + return; + + h->state = state; +} - g_debug ("headset GetTransport"); +static gboolean +headset_connect_cb (GIOChannel *io, GIOCondition cond, gpointer user_data) +{ + HsdHeadset *h = user_data; + GUnixFDList *fdlist; + gint sock; + GDBusMethodInvocation *invocation; + GVariantBuilder *b; - g_variant_get (parameters, "(a{sv})", &props); + invocation = h->invocation; + h->invocation = NULL; - /* FIXME, watch owner */ + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) + goto connect_failed; - tname = g_strdup_printf ("%s/fd%d", h->device, count++); - t = hsd_headset_transport_new (h, tname, sender, props, &error); - if (t == NULL) - goto transport_failed; + headset_set_state (h, HSD_HEADSET_STATE_ACTIVE); - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(o)", tname)); + g_debug ("connected"); + fdlist = g_unix_fd_list_new (); + g_unix_fd_list_append (fdlist, h->fd, NULL); - g_hash_table_insert (h->transports, tname, t); + b = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); + g_variant_builder_add (b, "{sv}", "fd", g_variant_new_handle (0)); + g_variant_builder_add (b, "{sv}", "mtu_r", g_variant_new_uint16 (48)); + g_variant_builder_add (b, "{sv}", "mtu_w", g_variant_new_uint16 (48)); + g_dbus_method_invocation_return_value_with_unix_fd_list ( + invocation, g_variant_new ("(a{sv})", b), fdlist); - return; + return FALSE; -transport_failed: +connect_failed: { - g_error ("failed to get transport: %s", error->message); - g_dbus_method_invocation_take_error (invocation, error); + close (h->fd); + h->fd = -1; + g_free (h->owner); + h->owner = NULL; + headset_set_state (h, HSD_HEADSET_STATE_IDLE); + g_dbus_method_invocation_return_dbus_error (invocation, + "org.freedesktop.Headset.Error.Failed", "failed to connect"); + return FALSE; } } static void -headset_release_transport (HsdHeadset *h, - GDBusConnection *connection, - const gchar *sender, - GVariant *parameters, - GDBusMethodInvocation *invocation) +headset_connect (HsdHeadset *h, + GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) { - HsdHeadsetTransport *t; - gchar *tname; - - g_debug ("headset ReleaseTransport"); + GDBusMessage *reply; + GError *error; + guchar *blob; + gsize out_size; + struct sockaddr_sco addr; + int err, i; + GIOCondition cond; + GIOChannel *io; + bdaddr_t src; + bdaddr_t dst; + int voice = 0x60; + socklen_t len; + gchar *src_addr; + gchar *dst_addr; + + g_message ("Connect headset"); + + if (h->state != HSD_HEADSET_STATE_IDLE) + goto not_idle; + + src_addr = h->adapter_addr; + dst_addr = h->device_addr; + + for (i = 5; i >= 0; i--, src_addr += 3) + src.b[i] = strtol(src_addr, NULL, 16); + for (i = 5; i >= 0; i--, dst_addr += 3) + dst.b[i] = strtol(dst_addr, NULL, 16); + + h->fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, BTPROTO_SCO); + if (h->fd < 0) + goto socket_failed; + + g_debug ("got fd %d", h->fd); + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + bacpy(&addr.sco_bdaddr, &src); + + if (bind(h->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + goto bind_failed; + + if (voice) { + struct bt_voice opts; + + /* SCO voice setting */ + memset(&opts, 0, sizeof(opts)); + opts.setting = voice; + if (setsockopt(h->fd, SOL_BLUETOOTH, BT_VOICE, &opts, sizeof(opts)) < 0) + goto sockopt_failed; + } - g_variant_get (parameters, "(&o)", &tname); + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + bacpy(&addr.sco_bdaddr, &dst); - t = g_hash_table_lookup (h->transports, tname); - if (t == NULL) - goto unknown_transport; + g_debug ("doing connect"); - if (strcmp (t->owner, sender) != 0) - goto not_owner; + err = connect(h->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) + goto connect_failed; - g_hash_table_remove (h->transports, tname); - hsd_headset_transport_free (t); + headset_set_state (h, HSD_HEADSET_STATE_PENDING); + h->owner = g_strdup (sender); + h->invocation = invocation; - g_dbus_method_invocation_return_value (invocation, NULL); + io = g_io_channel_unix_new(h->fd); + g_io_add_watch(io, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + headset_connect_cb, h); + g_io_channel_unref(io); return; -unknown_transport: +not_idle: { g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.DoesNotExist", "no such transport"); + "org.freedesktop.Headset.Error.Failed", "headset not idle"); return; } +socket_failed: + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.freedesktop.Headset.Error.Failed", "failed to create socket"); + goto close_fd; + } +bind_failed: + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.freedesktop.Headset.Error.Failed", "failed to bind socket"); + goto close_fd; + } +sockopt_failed: + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.freedesktop.Headset.Error.Failed", "failed to setsockopt"); + goto close_fd; + } +connect_failed: + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.freedesktop.Headset.Error.Failed", "failed to connect"); + goto close_fd; + } +close_fd: + { + close (h->fd); + h->fd = -1; + return; + } +} + +static void +headset_disconnect (HsdHeadset *h, + GDBusConnection *connection, + const gchar *sender, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + g_message ("headset disconnect"); + + if (h->owner != NULL && strcmp (h->owner, sender)) + goto not_owner; + + if (h->fd != -1) { + shutdown (h->fd, SHUT_RDWR); + close (h->fd); + } + h->fd = -1; + + h->owner = NULL; + headset_set_state (h, HSD_HEADSET_STATE_IDLE); + + g_dbus_method_invocation_return_value (invocation, NULL); + + return; + not_owner: { g_dbus_method_invocation_return_dbus_error (invocation, - "org.freedesktop.Headset.Error.NotAuthorized", "not the transport owner"); + "org.freedesktop.Headset.Error.NotAuthorized", "not the connection owner"); return; } } @@ -144,19 +290,53 @@ headset_method_call (GDBusConnection *connection, { HsdHeadset *h = user_data; - if (g_strcmp0 (method_name, "GetTransport") == 0) { - headset_get_transport (h, connection, sender, parameters, invocation); - } else if (g_strcmp0 (method_name, "ReleaseTransport") == 0) { - headset_release_transport (h, connection, sender, parameters, invocation); + if (h->invocation) + goto busy; + + if (g_strcmp0 (method_name, "Connect") == 0) { + headset_connect (h, connection, sender, parameters, invocation); + } else if (g_strcmp0 (method_name, "Disconnect") == 0) { + headset_disconnect (h, connection, sender, parameters, invocation); } else g_dbus_method_invocation_return_dbus_error (invocation, "org.freedesktop.Headset.Error.NotImplemented", "no such method"); + + return; + +busy: + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.freedesktop.Headset.Error.Busy", "We have a pending operation"); + return; + } } +static GVariant * +headset_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + HsdHeadset *h = user_data; + GVariant *ret; + + ret = NULL; + if (g_strcmp0 (property_name, "Device") == 0) { + ret = g_variant_new_object_path (h->device); + } else if (g_strcmp0 (property_name, "State") == 0) { + ret = g_variant_new_string (headset_state_to_string (h->state)); + } + return ret; +} + + static const GDBusInterfaceVTable headset_interface_vtable = { headset_method_call, - NULL, + headset_get_property, NULL }; @@ -244,7 +424,6 @@ hsd_headset_new (const gchar *device, GIOChannel *rfcomm, GError **error) headset = g_new0 (HsdHeadset, 1); headset->device = g_strdup (device); - headset->transports = g_hash_table_new (g_str_hash, g_str_equal); headset->rfcomm = rfcomm; headset->device_addr = dbus_get_property (conn, "org.bluez", device, "org.bluez.Device1", "Address", error); @@ -297,8 +476,8 @@ hsd_headset_free (HsdHeadset *headset) g_free (headset->device_addr); g_free (headset->adapter); g_free (headset->adapter_addr); + g_free (headset->owner); g_io_channel_unref (headset->rfcomm); - g_hash_table_unref (headset->transports); g_free (headset); } diff --git a/src/hsd-headset.h b/src/hsd-headset.h index c739a42..b30c01b 100644 --- a/src/hsd-headset.h +++ b/src/hsd-headset.h @@ -17,6 +17,12 @@ * Boston, MA 02110-1301, USA. */ +typedef enum { + HSD_HEADSET_STATE_IDLE, + HSD_HEADSET_STATE_PENDING, + HSD_HEADSET_STATE_ACTIVE, +} HsdHeadsetState; + struct _HsdHeadset { guint id; @@ -27,7 +33,10 @@ struct _HsdHeadset { GIOChannel *rfcomm; - GHashTable *transports; + HsdHeadsetState state; + gchar *owner; + gint fd; + GDBusMethodInvocation *invocation; }; HsdHeadset * hsd_headset_new (const gchar *device, GIOChannel *rfcomm, GError **error); |