Commit 3c857757 authored by Johan Hedberg's avatar Johan Hedberg Committed by Marcel Holtmann

Bluetooth: Add directed advertising support through connect()

When we're in peripheral mode (HCI_ADVERTISING flag is set) the most
natural mapping of connect() is to perform directed advertising to the
peer device.

This patch does the necessary changes to enable directed advertising and
keeps the hci_conn state as BT_CONNECT in a similar way as is done for
central or BR/EDR connection initiation.
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 5d2e9fad
...@@ -367,6 +367,7 @@ enum { ...@@ -367,6 +367,7 @@ enum {
#define HCI_ERROR_REMOTE_POWER_OFF 0x15 #define HCI_ERROR_REMOTE_POWER_OFF 0x15
#define HCI_ERROR_LOCAL_HOST_TERM 0x16 #define HCI_ERROR_LOCAL_HOST_TERM 0x16
#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18 #define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18
#define HCI_ERROR_ADVERTISING_TIMEOUT 0x3c
/* Flow control modes */ /* Flow control modes */
#define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00 #define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00
......
...@@ -367,9 +367,23 @@ static void le_conn_timeout(struct work_struct *work) ...@@ -367,9 +367,23 @@ static void le_conn_timeout(struct work_struct *work)
{ {
struct hci_conn *conn = container_of(work, struct hci_conn, struct hci_conn *conn = container_of(work, struct hci_conn,
le_conn_timeout.work); le_conn_timeout.work);
struct hci_dev *hdev = conn->hdev;
BT_DBG(""); BT_DBG("");
/* We could end up here due to having done directed advertising,
* so clean up the state if necessary. This should however only
* happen with broken hardware or if low duty cycle was used
* (which doesn't have a timeout of its own).
*/
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
u8 enable = 0x00;
hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
&enable);
hci_le_conn_failed(conn, HCI_ERROR_ADVERTISING_TIMEOUT);
return;
}
hci_le_create_connection_cancel(conn); hci_le_create_connection_cancel(conn);
} }
...@@ -549,6 +563,11 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status) ...@@ -549,6 +563,11 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
* favor of connection establishment, we should restart it. * favor of connection establishment, we should restart it.
*/ */
hci_update_background_scan(hdev); hci_update_background_scan(hdev);
/* Re-enable advertising in case this was a failed connection
* attempt as a peripheral.
*/
mgmt_reenable_advertising(hdev);
} }
static void create_le_conn_complete(struct hci_dev *hdev, u8 status) static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
...@@ -609,6 +628,45 @@ static void hci_req_add_le_create_conn(struct hci_request *req, ...@@ -609,6 +628,45 @@ static void hci_req_add_le_create_conn(struct hci_request *req,
conn->state = BT_CONNECT; conn->state = BT_CONNECT;
} }
static void hci_req_directed_advertising(struct hci_request *req,
struct hci_conn *conn)
{
struct hci_dev *hdev = req->hdev;
struct hci_cp_le_set_adv_param cp;
u8 own_addr_type;
u8 enable;
enable = 0x00;
hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
/* Clear the HCI_ADVERTISING bit temporarily so that the
* hci_update_random_address knows that it's safe to go ahead
* and write a new random address. The flag will be set back on
* as soon as the SET_ADV_ENABLE HCI command completes.
*/
clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
/* Set require_privacy to false so that the remote device has a
* chance of identifying us.
*/
if (hci_update_random_address(req, false, &own_addr_type) < 0)
return;
memset(&cp, 0, sizeof(cp));
cp.type = LE_ADV_DIRECT_IND;
cp.own_address_type = own_addr_type;
cp.direct_addr_type = conn->dst_type;
bacpy(&cp.direct_addr, &conn->dst);
cp.channel_map = hdev->le_adv_channel_map;
hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
enable = 0x01;
hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
conn->state = BT_CONNECT;
}
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level, u8 auth_type) u8 dst_type, u8 sec_level, u8 auth_type)
{ {
...@@ -618,9 +676,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -618,9 +676,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
struct hci_request req; struct hci_request req;
int err; int err;
if (test_bit(HCI_ADVERTISING, &hdev->flags))
return ERR_PTR(-ENOTSUPP);
/* Some devices send ATT messages as soon as the physical link is /* Some devices send ATT messages as soon as the physical link is
* established. To be able to handle these ATT messages, the user- * established. To be able to handle these ATT messages, the user-
* space first establishes the connection and then starts the pairing * space first establishes the connection and then starts the pairing
...@@ -668,13 +723,20 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -668,13 +723,20 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
conn->dst_type = dst_type; conn->dst_type = dst_type;
conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
conn->sec_level = BT_SECURITY_LOW; conn->sec_level = BT_SECURITY_LOW;
conn->pending_sec_level = sec_level; conn->pending_sec_level = sec_level;
conn->auth_type = auth_type; conn->auth_type = auth_type;
hci_req_init(&req, hdev);
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
hci_req_directed_advertising(&req, conn);
goto create_conn;
}
conn->out = true;
conn->link_mode |= HCI_LM_MASTER;
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (params) { if (params) {
conn->le_conn_min_interval = params->conn_min_interval; conn->le_conn_min_interval = params->conn_min_interval;
...@@ -684,8 +746,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -684,8 +746,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
conn->le_conn_max_interval = hdev->le_conn_max_interval; conn->le_conn_max_interval = hdev->le_conn_max_interval;
} }
hci_req_init(&req, hdev);
/* If controller is scanning, we stop it since some controllers are /* If controller is scanning, we stop it since some controllers are
* not able to scan and connect at the same time. Also set the * not able to scan and connect at the same time. Also set the
* HCI_LE_SCAN_INTERRUPTED flag so that the command complete * HCI_LE_SCAN_INTERRUPTED flag so that the command complete
...@@ -699,6 +759,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -699,6 +759,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
hci_req_add_le_create_conn(&req, conn); hci_req_add_le_create_conn(&req, conn);
create_conn:
err = hci_req_run(&req, create_le_conn_complete); err = hci_req_run(&req, create_le_conn_complete);
if (err) { if (err) {
hci_conn_del(conn); hci_conn_del(conn);
......
...@@ -991,10 +991,25 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -991,10 +991,25 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
if (!sent) if (!sent)
return; return;
if (status)
return;
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (!status) /* If we're doing connection initation as peripheral. Set a
mgmt_advertising(hdev, *sent); * timeout in case something goes wrong.
*/
if (*sent) {
struct hci_conn *conn;
conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
if (conn)
queue_delayed_work(hdev->workqueue,
&conn->le_conn_timeout,
HCI_LE_CONN_TIMEOUT);
}
mgmt_advertising(hdev, *sent);
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment