This patch splits the bonding process in an interative process consisting of 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 | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- src/adapter.h | 3 ++ src/device.c | 47 +++++++++++++++++++++++++++++++ src/device.h | 2 ++ 4 files changed, 127 insertions(+), 13 deletions(-) diff --git a/src/adapter.c b/src/adapter.c index 3007086..3250e13 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -4933,6 +4933,42 @@ 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) +{ + 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) { + if (device_bonding_attempt_retry(device) == 0) + return; + } + } + + /* Ignore disconnects during retry. */ + if (status == MGMT_STATUS_DISCONNECTED && + device && 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; @@ -4986,7 +5022,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; } @@ -4996,17 +5032,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; @@ -5014,6 +5046,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); @@ -5066,7 +5110,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); } @@ -5120,7 +5164,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, @@ -5720,14 +5765,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 proceeding, 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 26cea57..e579ca1 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 d9639b9..f260d47 100644 --- a/src/device.c +++ b/src/device.c @@ -3755,6 +3755,53 @@ 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; + uint8_t io_cap; + int err; + + if (!bonding) + return FALSE; + + DBG("retrying bonding"); + bonding->retry_timer = 0; + + if (bonding->agent) + io_cap = agent_get_io_capability(bonding->agent); + else + io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT; + + err = adapter_bonding_attempt(adapter, &device->bdaddr, + device->bdaddr_type, io_cap); + 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"); + 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; diff --git a/src/device.h b/src/device.h index fec214b..d19793c 100644 --- a/src/device.h +++ b/src/device.h @@ -76,8 +76,10 @@ gboolean device_is_connected(struct btd_device *device); bool 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.2.1 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html