guint pairable_timeout_id; /* pairable timeout id */
guint auth_idle_id; /* Pending authorization dequeue */
GQueue *auths; /* Ongoing and pending auths */
+ bool pincode_requested; /* PIN requested during last bonding */
GSList *connections; /* Connected devices */
GSList *devices; /* Devices structure pointers */
GSList *connect_list; /* Devices to connect when found */
adapter->dev_id = index;
adapter->mgmt = mgmt_ref(mgmt_master);
+ adapter->pincode_requested = false;
/*
* Setup default configuration values. These are either adapter
return;
}
+ /* Flag the request of a pincode to allow a bonding retry. */
+ adapter->pincode_requested = true;
+
memset(pin, 0, sizeof(pin));
iter = device_bonding_iter(device);
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 && adapter->pincode_requested) {
+ /* 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;
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;
}
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;
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);
+ /* Reset the pincode_requested flag for a new bonding attempt. */
+ adapter->pincode_requested = false;
+
memset(&cp, 0, sizeof(cp));
bacpy(&cp.addr.bdaddr, bdaddr);
cp.addr.type = addr_type;
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);
}
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,
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,
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 (btd_adapter_pin_cb_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;