2013-04-10 21:19:07

by Alex Deymo

[permalink] [raw]
Subject: [PATCH 0/6] Autopair plugin revisited

Hi
I went through this patch set again to fix some details. I may work in
some improvements for it in the future but think this is already a good
start.

As I tested, remote initiated bondings don't have a bonding_req and don't
go through these plugins anyway... but correct me if I'm missing something.

Repeated attempts errors are handled failing the pairing with an
AuthenticationCanceled error.

Please take a look, thanks!
Alex.

Alex Deymo (6):
core: Convert the pincode callback to an interable list.
plugins: Extend the pin code callback with the call number
core: Add support for retrying a bonding
core: retry bonding attempt until the iterator reachs the end.
core: Add device_get_class to the public interface.
autopair: Add the autopair plugin.

Makefile.plugins | 3 ++
plugins/autopair.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++
plugins/wiimote.c | 7 ++-
src/adapter.c | 143 ++++++++++++++++++++++++++++++++++++++++++++--------
src/adapter.h | 10 +++-
src/device.c | 79 ++++++++++++++++++++++++++++-
src/device.h | 5 ++
7 files changed, 366 insertions(+), 26 deletions(-)
create mode 100644 plugins/autopair.c

--
1.8.1.3



2013-04-10 21:19:13

by Alex Deymo

[permalink] [raw]
Subject: [PATCH 6/6] autopair: Add the autopair plugin.

The autopair plugin tries standard pincodes for different devices with dumb
pincodes. It also generates a random 6 digit pincode for keyboards that
support any pincode but fallbacks to the agent call in case the random
generated pincode didn't work.
---
Makefile.plugins | 3 ++
plugins/autopair.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 148 insertions(+)
create mode 100644 plugins/autopair.c

diff --git a/Makefile.plugins b/Makefile.plugins
index f497782..651a970 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -5,6 +5,9 @@ builtin_sources += plugins/hostname.c
builtin_modules += wiimote
builtin_sources += plugins/wiimote.c

+builtin_modules += autopair
+builtin_sources += plugins/autopair.c
+
if MAINTAINER_MODE
builtin_modules += gatt_example
builtin_sources += plugins/gatt-example.c
diff --git a/plugins/autopair.c b/plugins/autopair.c
new file mode 100644
index 0000000..40b8f8b
--- /dev/null
+++ b/plugins/autopair.c
@@ -0,0 +1,145 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012-2013 Google Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <glib.h>
+
+#include "plugin.h"
+#include "adapter.h"
+#include "device.h"
+#include "log.h"
+#include "storage.h"
+
+/*
+ * Plugin to handle automatic pairing of devices with reduced user
+ * interaction, including implementing the recommendation of the HID spec
+ * for keyboard devices.
+ *
+ * The plugin works by intercepting the PIN request for devices; if the
+ * device is a keyboard a random six-digit numeric PIN is generated and
+ * returned, flagged for displaying using DisplayPinCode.
+ *
+ */
+
+static ssize_t autopair_pincb(struct btd_adapter *adapter,
+ struct btd_device *device,
+ char *pinbuf, gboolean *display,
+ int count)
+{
+ char addr[18];
+ char pinstr[7];
+ uint32_t class;
+
+ ba2str(device_get_address(device), addr);
+
+ class = btd_device_get_class(device);
+
+ DBG("device %s 0x%x", addr, class);
+
+ /* This is a class-based pincode guesser. Ignore devices with an unknown
+ * class. */
+ if (class == 0)
+ return 0;
+
+ switch ((class & 0x1f00) >> 8) {
+ case 0x04: /* Audio/Video */
+ switch ((class & 0xfc) >> 2) {
+ case 0x01: /* Wearable Headset Device */
+ case 0x02: /* Hands-free Device */
+ case 0x06: /* Headphones */
+ case 0x07: /* Portable Audio */
+ case 0x0a: /* HiFi Audio Device */
+ if (count == 0)
+ memcpy(pinbuf, "0000", 4);
+ else if (count == 1)
+ memcpy(pinbuf, "1234", 4);
+ else
+ return 0;
+ return 4;
+ break;
+ }
+ break;
+ case 0x05: /* Peripheral */
+ switch ((class & 0xc0) >> 6) {
+ case 0x01: /* Keyboard */
+ case 0x03: /* Combo keyboard/pointing device */
+ if (count > 0)
+ return 0;
+ srand(time(NULL));
+ snprintf(pinstr, sizeof pinstr, "%06d",
+ rand() % 1000000);
+ *display = TRUE;
+ memcpy(pinbuf, pinstr, 6);
+ return 6;
+ break;
+ case 0x02: /* Pointing device */
+ if (count > 0)
+ return 0;
+ memcpy(pinbuf, "0000", 4);
+ return 4;
+ break;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
+static int autopair_probe(struct btd_adapter *adapter)
+{
+ btd_adapter_register_pin_cb(adapter, autopair_pincb);
+
+ return 0;
+}
+
+static void autopair_remove(struct btd_adapter *adapter)
+{
+ btd_adapter_unregister_pin_cb(adapter, autopair_pincb);
+}
+
+static struct btd_adapter_driver autopair_driver = {
+ .name = "autopair",
+ .probe = autopair_probe,
+ .remove = autopair_remove,
+};
+
+static int autopair_init(void)
+{
+ return btd_register_adapter_driver(&autopair_driver);
+}
+
+static void autopair_exit(void)
+{
+ btd_unregister_adapter_driver(&autopair_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(autopair, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, autopair_init, autopair_exit)
--
1.8.1.3

2013-04-10 21:19:12

by Alex Deymo

[permalink] [raw]
Subject: [PATCH 5/6] core: Add device_get_class to the public interface.

Exports the device class to plugins.
---
src/device.c | 5 +++++
src/device.h | 1 +
2 files changed, 6 insertions(+)

diff --git a/src/device.c b/src/device.c
index 032fc68..4ce7b8c 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2127,6 +2127,11 @@ void device_set_class(struct btd_device *device, uint32_t class)
DEVICE_INTERFACE, "Class");
}

+uint32_t btd_device_get_class(struct btd_device *device)
+{
+ return device->class;
+}
+
uint16_t btd_device_get_vendor(struct btd_device *device)
{
return device->vendor;
diff --git a/src/device.h b/src/device.h
index f81721a..ab243f9 100644
--- a/src/device.h
+++ b/src/device.h
@@ -37,6 +37,7 @@ void device_set_name(struct btd_device *device, const char *name);
void device_get_name(struct btd_device *device, char *name, size_t len);
bool device_name_known(struct btd_device *device);
void device_set_class(struct btd_device *device, uint32_t class);
+uint32_t btd_device_get_class(struct btd_device *device);
uint16_t btd_device_get_vendor(struct btd_device *device);
uint16_t btd_device_get_vendor_src(struct btd_device *device);
uint16_t btd_device_get_product(struct btd_device *device);
--
1.8.1.3

2013-04-10 21:19:11

by Alex Deymo

[permalink] [raw]
Subject: [PATCH 4/6] core: retry bonding attempt until the iterator reachs the end.

This patch splits the bonding process in an interative process consisting
on one or more "bonding attempts". The user/agent starts a new "bonding"
that may involve several rounds of "bonding attempts" with the device
before it gets back to the user/agent.

Some functions were split in two parts to reflect this change. When a
bonding attempt fails with an authentication error, a new bonding attempt
is initiated (after a short delay) to retry with the next function (or
next call to the same function) in the pincode callback list. This
effectively allows a plugin try different pincodes for the same device
during the same bonding.
---
src/adapter.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++---------
src/adapter.h | 3 ++
src/device.c | 45 ++++++++++++++++++++++++++++--
src/device.h | 2 ++
4 files changed, 125 insertions(+), 15 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index 17876b7..adeb047 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -4930,6 +4930,44 @@ static void bonding_complete(struct btd_adapter *adapter,
check_oob_bonding_complete(adapter, bdaddr, status);
}

+/* bonding_attempt_complete() handles the end of a "bonding attempt" checking if
+ * it should begin a new attempt or complete the bonding.
+ */
+static void bonding_attempt_complete(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t addr_type, uint8_t status)
+{
+ int err = 0;
+ struct btd_device *device;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%u bdaddr %s type %u status 0x%x", adapter->dev_id, addr,
+ addr_type, status);
+
+ if (status == 0)
+ device = adapter_get_device(adapter, bdaddr, addr_type);
+ else
+ device = adapter_find_device(adapter, bdaddr);
+
+ if (status == MGMT_STATUS_AUTH_FAILED) {
+
+ /* On faliure, issue a bonding_retry if possible. */
+ if (device != NULL) {
+ err = device_bonding_attempt_retry(device);
+ if (err == 0)
+ return;
+ }
+ }
+
+ /* Ignore disconnects during retry. */
+ if (status == MGMT_STATUS_DISCONNECTED && device_is_retrying(device))
+ return;
+
+ /* In any other case, finish the bonding. */
+ bonding_complete(adapter, bdaddr, addr_type, status);
+}
+
struct pair_device_data {
struct btd_adapter *adapter;
bdaddr_t bdaddr;
@@ -4983,7 +5021,7 @@ static void pair_device_complete(uint8_t status, uint16_t length,
error("Pair device failed: %s (0x%02x)",
mgmt_errstr(status), status);

- bonding_complete(adapter, &data->bdaddr,
+ bonding_attempt_complete(adapter, &data->bdaddr,
data->addr_type, status);
return;
}
@@ -4993,17 +5031,13 @@ static void pair_device_complete(uint8_t status, uint16_t length,
return;
}

- bonding_complete(adapter, &rp->addr.bdaddr, rp->addr.type, status);
+ bonding_attempt_complete(adapter, &rp->addr.bdaddr, rp->addr.type,
+ status);
}

int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
uint8_t addr_type, uint8_t io_cap)
{
- struct mgmt_cp_pair_device cp;
- char addr[18];
- struct pair_device_data *data;
- unsigned int id;
-
if (adapter->pair_device_id > 0) {
error("Unable pair since another pairing is in progress");
return -EBUSY;
@@ -5011,6 +5045,18 @@ int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,

suspend_discovery(adapter);

+ return adapter_bonding_attempt(adapter, bdaddr, addr_type, io_cap);
+}
+
+/* Starts a new bonding attempt in a fresh new bonding_req or a retried one. */
+int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+ uint8_t addr_type, uint8_t io_cap)
+{
+ struct mgmt_cp_pair_device cp;
+ char addr[18];
+ struct pair_device_data *data;
+ unsigned int id;
+
ba2str(bdaddr, addr);
DBG("hci%u bdaddr %s type %d io_cap 0x%02x",
adapter->dev_id, addr, addr_type, io_cap);
@@ -5063,7 +5109,7 @@ static void dev_disconnected(struct btd_adapter *adapter,
if (device)
adapter_remove_connection(adapter, device);

- bonding_complete(adapter, &addr->bdaddr, addr->type,
+ bonding_attempt_complete(adapter, &addr->bdaddr, addr->type,
MGMT_STATUS_DISCONNECTED);
}

@@ -5117,7 +5163,8 @@ static void auth_failed_callback(uint16_t index, uint16_t length,
return;
}

- bonding_complete(adapter, &ev->addr.bdaddr, ev->addr.type, ev->status);
+ bonding_attempt_complete(adapter, &ev->addr.bdaddr, ev->addr.type,
+ ev->status);
}

static void store_link_key(struct btd_adapter *adapter,
@@ -5717,14 +5764,31 @@ static void connect_failed_callback(uint16_t index, uint16_t length,

device = adapter_find_device(adapter, &ev->addr.bdaddr);
if (device) {
+ /* If the device is in a bonding process cancel any auth request
+ * sent to the agent before procedding, but keep the bonding
+ * request structure. */
if (device_is_bonding(device, NULL))
- device_bonding_failed(device, ev->status);
- if (device_is_temporary(device))
- adapter_remove_device(adapter, device, TRUE);
+ device_cancel_authentication(device, FALSE);
}

/* In the case of security mode 3 devices */
- bonding_complete(adapter, &ev->addr.bdaddr, ev->addr.type, ev->status);
+ bonding_attempt_complete(adapter, &ev->addr.bdaddr, ev->addr.type,
+ ev->status);
+
+ /* If the device is scheduled to retry the bonding wait until the retry
+ * happens. In other case, proceed with cancel the bondig.
+ */
+ if (device && device_is_bonding(device, NULL)
+ && !device_is_retrying(device)) {
+ device_cancel_authentication(device, TRUE);
+ device_bonding_failed(device, ev->status);
+ }
+
+ /* In the case the bonding was canceled or did exists, remove the device
+ * when it is temporary. */
+ if (device && !device_is_bonding(device, NULL)
+ && device_is_temporary(device))
+ adapter_remove_device(adapter, device, TRUE);
}

static void unpaired_callback(uint16_t index, uint16_t length,
diff --git a/src/adapter.h b/src/adapter.h
index 692963f..afc58c0 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -178,6 +178,9 @@ int btd_adapter_passkey_reply(struct btd_adapter *adapter,
int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
uint8_t addr_type, uint8_t io_cap);

+int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
+ uint8_t addr_type, uint8_t io_cap);
+
int adapter_cancel_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
uint8_t addr_type);

diff --git a/src/device.c b/src/device.c
index 0a39add..032fc68 100644
--- a/src/device.c
+++ b/src/device.c
@@ -3743,6 +3743,46 @@ gboolean device_is_bonding(struct btd_device *device, const char *sender)
return g_str_equal(sender, dbus_message_get_sender(bonding->msg));
}

+static gboolean device_bonding_retry(gpointer data)
+{
+ struct btd_device *device = data;
+ struct btd_adapter *adapter = device_get_adapter(device);
+ struct bonding_req *bonding = device->bonding;
+ int err;
+
+ if (!bonding)
+ return FALSE;
+
+ DBG("retrying bonding");
+ bonding->retry_timer = 0;
+
+ err = adapter_bonding_attempt(adapter, &device->bdaddr,
+ device->bdaddr_type, bonding->capability);
+ if (err < 0)
+ device_bonding_complete(device, bonding->status);
+
+ return FALSE;
+}
+
+int device_bonding_attempt_retry(struct btd_device *device) {
+ struct bonding_req *bonding = device->bonding;
+
+ /* Ignore other failure events while retrying */
+ if (device_is_retrying(device))
+ return 0;
+
+ if (!bonding)
+ return -EINVAL;
+
+ if (pincb_iter_end(bonding->cb_iter))
+ return -EINVAL;
+
+ DBG("scheduling retry with io_cap %u", bonding->capability);
+ bonding->retry_timer = g_timeout_add(3000,
+ device_bonding_retry, device);
+ return 0;
+}
+
void device_bonding_failed(struct btd_device *device, uint8_t status)
{
struct bonding_req *bonding = device->bonding;
@@ -3762,8 +3802,9 @@ void device_bonding_failed(struct btd_device *device, uint8_t status)
bonding_request_free(bonding);
}

-struct pincb_iter *device_bonding_iter(struct btd_device *device) {
- if (device->bonding == NULL)
+struct pincb_iter *device_bonding_iter(struct btd_device *device)
+{
+ if (!device->bonding)
return NULL;

return device->bonding->cb_iter;
diff --git a/src/device.h b/src/device.h
index 61a294b..f81721a 100644
--- a/src/device.h
+++ b/src/device.h
@@ -76,8 +76,10 @@ gboolean device_is_connected(struct btd_device *device);
gboolean device_is_retrying(struct btd_device *device);
void device_bonding_complete(struct btd_device *device, uint8_t status);
gboolean device_is_bonding(struct btd_device *device, const char *sender);
+void device_bonding_attempt_failed(struct btd_device *device, uint8_t status);
void device_bonding_failed(struct btd_device *device, uint8_t status);
struct pincb_iter *device_bonding_iter(struct btd_device *device);
+int device_bonding_attempt_retry(struct btd_device *device);
int device_request_pincode(struct btd_device *device, gboolean secure);
int device_request_passkey(struct btd_device *device);
int device_confirm_passkey(struct btd_device *device, uint32_t passkey,
--
1.8.1.3

2013-04-10 21:19:10

by Alex Deymo

[permalink] [raw]
Subject: [PATCH 3/6] core: Add support for retrying a bonding

In order to retry a bonding we need a timer that will perform the
retry, we need to stash the status and capability of the bonding
request so it can use them again. In the case of a retrying
bonding attempt we need to not tear down the temporary D-Bus device
object on the adapter.
---
src/adapter.c | 2 +-
src/device.c | 20 ++++++++++++++++++--
src/device.h | 1 +
3 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index f2193cf..17876b7 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -4253,7 +4253,7 @@ static void adapter_remove_connection(struct btd_adapter *adapter,
if (device_is_authenticating(device))
device_cancel_authentication(device, TRUE);

- if (device_is_temporary(device)) {
+ if (device_is_temporary(device) && !device_is_retrying(device)) {
const char *path = device_get_path(device);

DBG("Removing temporary device %s", path);
diff --git a/src/device.c b/src/device.c
index 8c4b98a..0a39add 100644
--- a/src/device.c
+++ b/src/device.c
@@ -86,6 +86,9 @@ struct bonding_req {
struct btd_device *device;
struct agent *agent;
struct pincb_iter *cb_iter;
+ uint8_t capability;
+ uint8_t status;
+ guint retry_timer;
};

typedef enum {
@@ -1400,7 +1403,8 @@ static void device_svc_resolved(struct btd_device *dev, int err)

static struct bonding_req *bonding_request_new(DBusMessage *msg,
struct btd_device *device,
- struct agent *agent)
+ struct agent *agent,
+ uint8_t io_cap)
{
struct bonding_req *bonding;
char addr[18];
@@ -1414,6 +1418,8 @@ static struct bonding_req *bonding_request_new(DBusMessage *msg,

bonding->cb_iter = pincb_iter_new(device->adapter);

+ bonding->capability = io_cap;
+
if (agent)
bonding->agent = agent_ref(agent);

@@ -1467,7 +1473,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
else
io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;

- bonding = bonding_request_new(msg, device, agent);
+ bonding = bonding_request_new(msg, device, agent, io_cap);

if (agent)
agent_unref(agent);
@@ -1548,6 +1554,9 @@ static void bonding_request_free(struct bonding_req *bonding)
bonding->agent = NULL;
}

+ if (bonding->retry_timer)
+ g_source_remove(bonding->retry_timer);
+
if (bonding->device)
bonding->device->bonding = NULL;

@@ -3594,6 +3603,13 @@ static void device_auth_req_free(struct btd_device *device)
device->authr = NULL;
}

+gboolean device_is_retrying(struct btd_device *device)
+{
+ struct bonding_req *bonding = device->bonding;
+
+ return bonding && bonding->retry_timer != 0;
+}
+
void device_bonding_complete(struct btd_device *device, uint8_t status)
{
struct bonding_req *bonding = device->bonding;
diff --git a/src/device.h b/src/device.h
index 725bd7a..61a294b 100644
--- a/src/device.h
+++ b/src/device.h
@@ -73,6 +73,7 @@ void device_set_bonded(struct btd_device *device, gboolean bonded);
void device_set_legacy(struct btd_device *device, bool legacy);
void device_set_rssi(struct btd_device *device, int8_t rssi);
gboolean device_is_connected(struct btd_device *device);
+gboolean device_is_retrying(struct btd_device *device);
void device_bonding_complete(struct btd_device *device, uint8_t status);
gboolean device_is_bonding(struct btd_device *device, const char *sender);
void device_bonding_failed(struct btd_device *device, uint8_t status);
--
1.8.1.3

2013-04-10 21:19:09

by Alex Deymo

[permalink] [raw]
Subject: [PATCH 2/6] plugins: Extend the pin code callback with the call number

The plugin's pin code callback doesn't know about the pairing process. It
just provides a pin code based on the information provided to this function.
Although limited state could be added through other new callbacks, this fix
achieves this by providing more information to the callback itself. The
new argument "count" states the pin callback attempt of the particular
plugin for the current pairing of the device. This allows a plugin to try
different pincodes for the same device in the same pairing process.

To signal that the plugin doesn't provide any pin code for the provided device
the current implementation returns 0 (an empty pin code). Analogously, with
this fix, a plugin should return 0 when it doesn't have any other pin code to
provide for the given device.
---
plugins/wiimote.c | 7 ++++++-
src/adapter.c | 2 +-
src/adapter.h | 3 ++-
3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/plugins/wiimote.c b/plugins/wiimote.c
index 90d6d7b..c8d024f 100644
--- a/plugins/wiimote.c
+++ b/plugins/wiimote.c
@@ -70,12 +70,17 @@ static const char *wii_names[] = {
};

static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device,
- char *pinbuf, gboolean *display)
+ char *pinbuf, gboolean *display, int count)
{
uint16_t vendor, product;
char addr[18], name[25];
unsigned int i;

+ /* Only try the pin code once per device. If it's not correct then it's
+ * an unknown device. */
+ if (count > 0)
+ return 0;
+
ba2str(device_get_address(device), addr);

vendor = btd_device_get_vendor(device);
diff --git a/src/adapter.c b/src/adapter.c
index 957f829..f2193cf 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -4802,7 +4802,7 @@ static ssize_t pincb_iter_next(struct pincb_iter *iter,

while (iter->it != NULL) {
cb = iter->it->data;
- ret = cb(adapter, device, pin_buf, display);
+ ret = cb(adapter, device, pin_buf, display, iter->count);
iter->count++;
if (ret > 0)
return ret;
diff --git a/src/adapter.h b/src/adapter.h
index d158334..692963f 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -132,7 +132,8 @@ int btd_cancel_authorization(guint id);
int btd_adapter_restore_powered(struct btd_adapter *adapter);

typedef ssize_t (*btd_adapter_pin_cb_t) (struct btd_adapter *adapter,
- struct btd_device *dev, char *out, gboolean *display);
+ struct btd_device *dev, char *out, gboolean *display,
+ int count);
void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
btd_adapter_pin_cb_t cb);
void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter,
--
1.8.1.3

2013-04-10 21:19:08

by Alex Deymo

[permalink] [raw]
Subject: [PATCH 1/6] core: Convert the pincode callback to an interable list.

The current pincode callback list on the adapter keeps track of all the
pincode callbacks registered by a plugin for that adapter and calls each
one until one provides a pincode for the current bonding. This mechanism
forgets about what happened with previous bonding attempts and pushes the
status track to the plugin side.

This patch creates an iterator struct (struct pincb_iter) that keeps track
of the last function called and the number of times called. This will
allow to provide more information about the bonding status to the pincode
callback.
---
src/adapter.c | 51 +++++++++++++++++++++++++++++++++++++++++++--------
src/adapter.h | 4 ++++
src/device.c | 13 +++++++++++++
src/device.h | 1 +
4 files changed, 61 insertions(+), 8 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index 6255da6..957f829 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -121,6 +121,12 @@ struct service_auth {
struct agent *agent; /* NULL for queued auths */
};

+struct pincb_iter {
+ GSList *it; /* current callback function */
+ int count; /* numer of times it() was called */
+ /* When the iterator reaches the end, it is NULL and count is -1 */
+};
+
struct btd_adapter {
int ref_count;

@@ -4769,22 +4775,43 @@ static void user_passkey_notify_callback(uint16_t index, uint16_t length,
error("device_notify_passkey: %s", strerror(-err));
}

-static ssize_t adapter_get_pin(struct btd_adapter *adapter,
- struct btd_device *dev, char *pin_buf,
+struct pincb_iter *pincb_iter_new(struct btd_adapter *adapter) {
+ struct pincb_iter *iter = g_new0(struct pincb_iter, 1);
+
+ iter->it = adapter->pin_callbacks;
+
+ return iter;
+}
+
+void pincb_iter_free(struct pincb_iter *iter) {
+ g_free(iter);
+}
+
+gboolean pincb_iter_end(struct pincb_iter *iter) {
+ return iter->it == NULL && iter->count == -1;
+}
+
+static ssize_t pincb_iter_next(struct pincb_iter *iter,
+ struct btd_adapter *adapter,
+ struct btd_device *device,
+ char *pin_buf,
gboolean *display)
{
btd_adapter_pin_cb_t cb;
ssize_t ret;
- GSList *l;

- for (l = adapter->pin_callbacks; l != NULL; l = g_slist_next(l)) {
- cb = l->data;
- ret = cb(adapter, dev, pin_buf, display);
+ while (iter->it != NULL) {
+ cb = iter->it->data;
+ ret = cb(adapter, device, pin_buf, display);
+ iter->count++;
if (ret > 0)
return ret;
+ iter->count = 0;
+ iter->it = g_slist_next(iter->it);
}
+ iter->count = -1;

- return -1;
+ return 0;
}

static void pin_code_request_callback(uint16_t index, uint16_t length,
@@ -4798,6 +4825,7 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
ssize_t pinlen;
char addr[18];
int err;
+ struct pincb_iter *iter;

if (length < sizeof(*ev)) {
error("Too small PIN code request event");
@@ -4815,7 +4843,14 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
}

memset(pin, 0, sizeof(pin));
- pinlen = adapter_get_pin(adapter, device, pin, &display);
+
+ iter = device_bonding_iter(device);
+
+ if (iter == NULL)
+ pinlen = 0;
+ else
+ pinlen = pincb_iter_next(iter, adapter, device, pin, &display);
+
if (pinlen > 0 && (!ev->secure || pinlen == 16)) {
if (display && device_is_bonding(device, NULL)) {
err = device_notify_pincode(device, ev->secure, pin);
diff --git a/src/adapter.h b/src/adapter.h
index 8d23a64..d158334 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -138,6 +138,10 @@ void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter,
btd_adapter_pin_cb_t cb);

+struct pincb_iter *pincb_iter_new(struct btd_adapter *adapter);
+void pincb_iter_free(struct pincb_iter *iter);
+gboolean pincb_iter_end(struct pincb_iter *iter);
+
/* If TRUE, enables fast connectabe, i.e. reduces page scan interval and changes
* type. If FALSE, disables fast connectable, i.e. sets page scan interval and
* type to default values. Valid for both connectable and discoverable modes. */
diff --git a/src/device.c b/src/device.c
index ee17514..8c4b98a 100644
--- a/src/device.c
+++ b/src/device.c
@@ -85,6 +85,7 @@ struct bonding_req {
guint listener_id;
struct btd_device *device;
struct agent *agent;
+ struct pincb_iter *cb_iter;
};

typedef enum {
@@ -1411,6 +1412,8 @@ static struct bonding_req *bonding_request_new(DBusMessage *msg,

bonding->msg = dbus_message_ref(msg);

+ bonding->cb_iter = pincb_iter_new(device->adapter);
+
if (agent)
bonding->agent = agent_ref(agent);

@@ -1536,6 +1539,9 @@ static void bonding_request_free(struct bonding_req *bonding)
if (bonding->msg)
dbus_message_unref(bonding->msg);

+ if (bonding->cb_iter)
+ g_free(bonding->cb_iter);
+
if (bonding->agent) {
agent_cancel(bonding->agent);
agent_unref(bonding->agent);
@@ -3740,6 +3746,13 @@ void device_bonding_failed(struct btd_device *device, uint8_t status)
bonding_request_free(bonding);
}

+struct pincb_iter *device_bonding_iter(struct btd_device *device) {
+ if (device->bonding == NULL)
+ return NULL;
+
+ return device->bonding->cb_iter;
+}
+
static void pincode_cb(struct agent *agent, DBusError *err, const char *pin,
void *data)
{
diff --git a/src/device.h b/src/device.h
index d072015..725bd7a 100644
--- a/src/device.h
+++ b/src/device.h
@@ -76,6 +76,7 @@ gboolean device_is_connected(struct btd_device *device);
void device_bonding_complete(struct btd_device *device, uint8_t status);
gboolean device_is_bonding(struct btd_device *device, const char *sender);
void device_bonding_failed(struct btd_device *device, uint8_t status);
+struct pincb_iter *device_bonding_iter(struct btd_device *device);
int device_request_pincode(struct btd_device *device, gboolean secure);
int device_request_passkey(struct btd_device *device);
int device_confirm_passkey(struct btd_device *device, uint32_t passkey,
--
1.8.1.3