2012-10-24 15:58:06

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 0/9] Add LE broadcaser and observer functionality

Hi all,

This patch series adds broadcaster and observer funcionality.

Yesterday, Johan Hedberg sent "Improved single-mode and LE peripheral support"
patch series, that has lots of conflicts with this implementation. Such patches
raised some discussions about LE roles interaction and advertising behavior.

The main intention of this submission is to show the implemenation progress of
broadcast and observer features and try to find the best way to merge them with
peripheral role implementation.

Aloisio Almeida Jr (5):
Bluetooth: Set advertising parameters on LE setup
Bluetooth: Add set controller data MGMT command
Bluetooth: Add set broadcaster MGMT command
Bluetooth: Refactor le scan helpers to enable observer support
Bluetooth: Add set observer MGMT command

Jefferson Delfes (4):
Bluetooth: Add HCI command to set advertising parameters
Bluetooth: Add HCI command to set advertising data
Bluetooth: Add unset controller data MGMT command
Bluetooth: Add HCI command to enable/disable advertising

include/net/bluetooth/hci.h | 31 ++++
include/net/bluetooth/hci_core.h | 33 +++-
include/net/bluetooth/mgmt.h | 21 +++
net/bluetooth/hci_core.c | 134 +++++++++++---
net/bluetooth/hci_event.c | 95 +++++++---
net/bluetooth/mgmt.c | 371 ++++++++++++++++++++++++++++++++++++--
6 files changed, 623 insertions(+), 62 deletions(-)

--
1.7.10.4



2012-10-24 15:58:15

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 9/9] Bluetooth: Add set observer MGMT command

This command will enable or disable observer mode.

If LE_ENABLED is set the HCI_OP_LE_SET_SCAN_PARAMS and HCI_OP_LE_SET_SCAN_ENABLE
commands will be sent. Observer will perform a passive scan with no timeout.

If discovery is required when observer is enabled, le scan is restarted in
active mode.

If observer is enabled when discovery finishes, passive le scan
is enabled.

If observer mode is required when discovery is on going, the le scan mode does
not change until the end of discovery process.

This command is available on the powered off state.

Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 5 ++
include/net/bluetooth/mgmt.h | 3 +
net/bluetooth/hci_core.c | 18 ++++-
net/bluetooth/hci_event.c | 50 ++++++------
net/bluetooth/mgmt.c | 165 ++++++++++++++++++++++++++++++++++----
6 files changed, 201 insertions(+), 41 deletions(-)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 591450c..78538c0 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -121,6 +121,7 @@ enum {
HCI_PENDING_CLASS,
HCI_PERIODIC_INQ,
HCI_BROADCASTER,
+ HCI_OBSERVER,
};

/* HCI ioctl defines */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 6ef8660..f6bab83 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -55,6 +55,8 @@ struct inquiry_entry {
struct inquiry_data data;
};

+#define hdev_is_in_discovery(hdev) (hdev->discovery.state != DISCOVERY_STOPPED)
+
struct discovery_state {
int type;
enum {
@@ -133,7 +135,9 @@ struct controller_data {
};

enum {
+ LE_SCAN_REQ_REASON_RESET,
LE_SCAN_REQ_REASON_DISCOVERY,
+ LE_SCAN_REQ_REASON_OBSERVER,
};

#define HCI_MAX_SHORT_NAME_LENGTH 10
@@ -1095,6 +1099,7 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
u8 *randomizer, u8 status);
int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
int mgmt_set_broadcaster_complete(struct hci_dev *hdev, u8 enable, u8 status);
+int mgmt_set_observer_complete(struct hci_dev *hdev, u8 enable, u8 status);
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
u8 ssp, u8 *eir, u16 eir_len);
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index bd64816..13936ab 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -93,6 +93,7 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_HS 0x00000100
#define MGMT_SETTING_LE 0x00000200
#define MGMT_SETTING_BROADCASTER 0x00000400
+#define MGMT_SETTING_OBSERVER 0x00000800

#define MGMT_OP_READ_INFO 0x0004
#define MGMT_READ_INFO_SIZE 0
@@ -368,6 +369,8 @@ struct mgmt_cp_unset_controller_data {

#define MGMT_OP_SET_BROADCASTER 0x002B

+#define MGMT_OP_SET_OBSERVER 0x002C
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index fbf0ba1..1482681 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1556,14 +1556,28 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,

BT_DBG("%s", hdev->name);

- if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+ if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+ reason == LE_SCAN_REQ_REASON_OBSERVER)
return -EINPROGRESS;

+ memset(&cp, 0, sizeof(cp));
+
+ if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+ reason == LE_SCAN_REQ_REASON_DISCOVERY) {
+ hci_dev_lock(hdev);
+ hdev->le_scan_req_reason = LE_SCAN_REQ_REASON_RESET;
+ hci_dev_unlock(hdev);
+
+ err = hci_request(hdev, le_scan_enable_req, (unsigned long) &cp,
+ timeo);
+ if (err)
+ return err;
+ }
+
param.type = type;
param.interval = interval;
param.window = window;

- memset(&cp, 0, sizeof(cp));
cp.enable = 1;
if (reason == LE_SCAN_REQ_REASON_DISCOVERY)
cp.filter_dup = 1;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 7ad2b0d..f31b694 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1212,42 +1212,42 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
if (!cp)
return;

- switch (cp->enable) {
- case LE_SCANNING_ENABLED:
- hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status);
+ hci_dev_lock(hdev);

- if (status) {
- hci_dev_lock(hdev);
- mgmt_start_discovery_failed(hdev, status);
- hci_dev_unlock(hdev);
- return;
- }
+ if (!status) {
+ if (cp->enable)
+ set_bit(HCI_LE_SCAN, &hdev->dev_flags);
+ else
+ clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
+ }

- set_bit(HCI_LE_SCAN, &hdev->dev_flags);
+ if (hdev->le_scan_req_reason == LE_SCAN_REQ_REASON_RESET)
+ goto unlock;

- hci_dev_lock(hdev);
- hci_discovery_set_state(hdev, DISCOVERY_FINDING);
- hci_dev_unlock(hdev);
+ if (hdev->le_scan_req_reason == LE_SCAN_REQ_REASON_OBSERVER) {
+ mgmt_set_observer_complete(hdev, cp->enable, status);
+ goto unlock;
+ }
+
+ switch (cp->enable) {
+ case LE_SCANNING_ENABLED:
+ if (status)
+ mgmt_start_discovery_failed(hdev, status);
+ else
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
break;

case LE_SCANNING_DISABLED:
if (status) {
- hci_dev_lock(hdev);
mgmt_stop_discovery_failed(hdev, status);
- hci_dev_unlock(hdev);
- return;
+ goto unlock;
}

- clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
-
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
- hdev->discovery.state == DISCOVERY_FINDING) {
+ hdev->discovery.state == DISCOVERY_FINDING)
mgmt_interleaved_discovery(hdev);
- } else {
- hci_dev_lock(hdev);
+ else
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
- hci_dev_unlock(hdev);
- }

break;

@@ -1255,6 +1255,10 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
BT_ERR("Used reserved LE_Scan_Enable param %d", cp->enable);
break;
}
+
+unlock:
+ hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status);
+ hci_dev_unlock(hdev);
}

static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 46a45e7..bd7b238 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -106,11 +106,13 @@ static const u16 mgmt_events[] = {
* These LE scan and inquiry parameters were chosen according to LE General
* Discovery Procedure specification.
*/
-#define LE_SCAN_TYPE 0x01
+#define LE_SCAN_TYPE_PASSIVE 0x00
+#define LE_SCAN_TYPE_ACTIVE 0x01
#define LE_SCAN_WIN 0x12
#define LE_SCAN_INT 0x12
#define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */
#define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */
+#define LE_SCAN_NO_TIMEOUT -1 /* Observer role */

#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
#define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */
@@ -381,6 +383,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_PAIRABLE;
settings |= MGMT_SETTING_BROADCASTER;
+ settings |= MGMT_SETTING_OBSERVER;

if (lmp_ssp_capable(hdev))
settings |= MGMT_SETTING_SSP;
@@ -424,6 +427,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (test_bit(HCI_BROADCASTER, &hdev->dev_flags))
settings |= MGMT_SETTING_BROADCASTER;

+ if (test_bit(HCI_OBSERVER, &hdev->dev_flags))
+ settings |= MGMT_SETTING_OBSERVER;
+
if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
settings |= MGMT_SETTING_LINK_SECURITY;

@@ -2305,14 +2311,10 @@ int mgmt_interleaved_discovery(struct hci_dev *hdev)

BT_DBG("%s", hdev->name);

- hci_dev_lock(hdev);
-
err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
if (err < 0)
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);

- hci_dev_unlock(hdev);
-
return err;
}

@@ -2339,7 +2341,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
goto failed;
}

- if (hdev->discovery.state != DISCOVERY_STOPPED) {
+ if (hdev_is_in_discovery(hdev)) {
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
MGMT_STATUS_BUSY);
goto failed;
@@ -2363,8 +2365,9 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,

case DISCOV_TYPE_LE:
if (lmp_host_le_capable(hdev))
- err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
- LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY,
+ err = hci_le_scan(hdev, LE_SCAN_TYPE_ACTIVE,
+ LE_SCAN_INT, LE_SCAN_WIN,
+ LE_SCAN_TIMEOUT_LE_ONLY,
LE_SCAN_REQ_REASON_DISCOVERY);
else
err = -ENOTSUPP;
@@ -2372,8 +2375,9 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,

case DISCOV_TYPE_INTERLEAVED:
if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev))
- err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
- LE_SCAN_WIN, LE_SCAN_TIMEOUT_BREDR_LE,
+ err = hci_le_scan(hdev, LE_SCAN_TYPE_ACTIVE,
+ LE_SCAN_INT, LE_SCAN_WIN,
+ LE_SCAN_TIMEOUT_BREDR_LE,
LE_SCAN_REQ_REASON_DISCOVERY);
else
err = -ENOTSUPP;
@@ -2850,6 +2854,89 @@ static int set_broadcaster(struct sock *sk, struct hci_dev *hdev, void *data,
return set_broadcaster_le(sk, hdev, cp->val);
}

+static int set_observer_le(struct sock *sk, struct hci_dev *hdev, u8 enable)
+{
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("%s enable:%i", hdev->name, enable);
+
+ hci_dev_lock(hdev);
+
+ if (!hdev_is_powered(hdev)) {
+ bool changed = false;
+
+ if (enable != test_bit(HCI_OBSERVER, &hdev->dev_flags)) {
+ change_bit(HCI_OBSERVER, &hdev->dev_flags);
+ changed = true;
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_OBSERVER, hdev);
+ if (err < 0)
+ goto unlock;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+ goto unlock;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_OBSERVER, hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_OBSERVER,
+ MGMT_STATUS_BUSY);
+ goto unlock;
+ }
+
+ if (enable == test_bit(HCI_OBSERVER, &hdev->dev_flags)) {
+ err = send_settings_rsp(sk, MGMT_OP_SET_OBSERVER, hdev);
+ goto unlock;
+ }
+
+ if (hdev_is_in_discovery(hdev)) {
+ change_bit(HCI_OBSERVER, &hdev->dev_flags);
+ err = send_settings_rsp(sk, MGMT_OP_SET_OBSERVER, hdev);
+ if (err < 0)
+ goto unlock;
+
+ err = new_settings(hdev, sk);
+ goto unlock;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_OBSERVER, hdev, NULL, 0);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ if (enable)
+ err = hci_le_scan(hdev, LE_SCAN_TYPE_PASSIVE, LE_SCAN_INT,
+ LE_SCAN_WIN, LE_SCAN_NO_TIMEOUT,
+ LE_SCAN_REQ_REASON_OBSERVER);
+ else
+ err = hci_cancel_le_scan(hdev, LE_SCAN_REQ_REASON_OBSERVER);
+
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static int set_observer(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
+{
+ struct mgmt_mode *cp = data;
+
+ BT_DBG("%s val:%i", hdev->name, cp->val);
+
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_OBSERVER,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ return set_observer_le(sk, hdev, cp->val);
+}
+
static const struct mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len);
@@ -2900,6 +2987,7 @@ static const struct mgmt_handler {
{ set_controller_data, true, MGMT_SET_CONTROLLER_DATA_SIZE },
{ unset_controller_data, false, MGMT_UNSET_CONTROLLER_DATA_SIZE },
{ set_broadcaster, false, MGMT_SETTING_SIZE },
+ { set_observer, false, MGMT_SETTING_SIZE },
};


@@ -3080,14 +3168,19 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
update_name(hdev, hdev->dev_name);
update_eir(hdev);

- if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
update_adv_data(hdev);

- if (test_bit(HCI_BROADCASTER, &hdev->dev_flags) &&
- test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
- u8 enable = 1;
- hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE,
- sizeof(enable), &enable);
+ if (test_bit(HCI_BROADCASTER, &hdev->dev_flags)) {
+ u8 enable = 1;
+ hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE,
+ sizeof(enable), &enable);
+ } else if (test_bit(HCI_OBSERVER, &hdev->dev_flags)) {
+ hci_le_scan(hdev, LE_SCAN_TYPE_PASSIVE,
+ LE_SCAN_INT,
+ LE_SCAN_WIN, LE_SCAN_NO_TIMEOUT,
+ LE_SCAN_REQ_REASON_OBSERVER);
+ }
}

} else {
@@ -3782,6 +3875,41 @@ int mgmt_set_broadcaster_complete(struct hci_dev *hdev, u8 enable, u8 status)
return err;
}

+int mgmt_set_observer_complete(struct hci_dev *hdev, u8 enable, u8 status)
+{
+ struct cmd_lookup match = { NULL, hdev };
+ bool changed = false;
+ int err = 0;
+
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+
+ mgmt_pending_foreach(MGMT_OP_SET_OBSERVER, hdev,
+ cmd_status_rsp, &mgmt_err);
+
+ return err;
+ }
+
+ if (enable) {
+ if (!test_and_set_bit(HCI_OBSERVER, &hdev->dev_flags))
+ changed = true;
+ } else {
+ if (test_and_clear_bit(HCI_OBSERVER, &hdev->dev_flags))
+ changed = true;
+ }
+
+ mgmt_pending_foreach(MGMT_OP_SET_OBSERVER, hdev, settings_rsp,
+ &match);
+
+ if (changed)
+ err = new_settings(hdev, match.sk);
+
+ if (match.sk)
+ sock_put(match.sk);
+
+ return err;
+}
+
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
ssp, u8 *eir, u16 eir_len)
@@ -3898,6 +4026,11 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
mgmt_pending_remove(cmd);
}

+ if (!discovering && test_bit(HCI_OBSERVER, &hdev->dev_flags))
+ hci_le_scan(hdev, LE_SCAN_TYPE_PASSIVE, LE_SCAN_INT,
+ LE_SCAN_WIN, LE_SCAN_NO_TIMEOUT,
+ LE_SCAN_REQ_REASON_OBSERVER);
+
memset(&ev, 0, sizeof(ev));
ev.type = hdev->discovery.type;
ev.discovering = discovering;
--
1.7.10.4


2012-10-24 15:58:14

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 8/9] Bluetooth: Refactor le scan helpers to enable observer support

It was added a parameter called 'reason' to le scan helpers (hci_do_le_scan and
hci_cancel_le_scan). Distinguishing the reason to enbale/disable le scan will be
important when both discovery and observer are implemented.

'reason' was also added do hdev structure (hdev.le_scan_req_reason) in order
to make hci_cc_le_set_scan_enable behave correctly.

Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
include/net/bluetooth/hci_core.h | 11 +++++--
net/bluetooth/hci_core.c | 63 +++++++++++++++++++++++---------------
net/bluetooth/mgmt.c | 10 +++---
3 files changed, 54 insertions(+), 30 deletions(-)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index fc26210..6ef8660 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -121,6 +121,7 @@ struct le_scan_params {
u16 interval;
u16 window;
int timeout;
+ u8 reason;
};

struct controller_data {
@@ -131,6 +132,10 @@ struct controller_data {
u8 data[0];
};

+enum {
+ LE_SCAN_REQ_REASON_DISCOVERY,
+};
+
#define HCI_MAX_SHORT_NAME_LENGTH 10

struct amp_assoc {
@@ -285,6 +290,7 @@ struct hci_dev {

struct work_struct le_scan;
struct le_scan_params le_scan_params;
+ __u8 le_scan_req_reason;

__s8 adv_tx_power;

@@ -1139,9 +1145,10 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
__u8 ltk[16]);
int hci_do_inquiry(struct hci_dev *hdev, u8 length);
int hci_cancel_inquiry(struct hci_dev *hdev);
+
int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
- int timeout);
-int hci_cancel_le_scan(struct hci_dev *hdev);
+ int timeout, u8 reason);
+int hci_cancel_le_scan(struct hci_dev *hdev, u8 reason);

u8 bdaddr_to_le(u8 bdaddr_type);

diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 2c2f3aa..fbf0ba1 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1538,20 +1538,20 @@ static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt)

static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt)
{
- struct hci_cp_le_set_scan_enable cp;
+ struct hci_cp_le_set_scan_enable *cp;

- memset(&cp, 0, sizeof(cp));
- cp.enable = 1;
- cp.filter_dup = 1;
+ cp = (struct hci_cp_le_set_scan_enable *) opt;

- hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
+ sizeof(struct hci_cp_le_set_scan_enable), cp);
}

static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
- u16 window, int timeout)
+ u16 window, int timeout, u8 reason)
{
long timeo = msecs_to_jiffies(3000);
struct le_scan_params param;
+ struct hci_cp_le_set_scan_enable cp;
int err;

BT_DBG("%s", hdev->name);
@@ -1563,38 +1563,52 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
param.interval = interval;
param.window = window;

+ memset(&cp, 0, sizeof(cp));
+ cp.enable = 1;
+ if (reason == LE_SCAN_REQ_REASON_DISCOVERY)
+ cp.filter_dup = 1;
+
+ hci_dev_lock(hdev);
+ hdev->le_scan_req_reason = reason;
+ hci_dev_unlock(hdev);
+
hci_req_lock(hdev);

err = __hci_request(hdev, le_scan_param_req, (unsigned long) &param,
timeo);
- if (!err)
- err = __hci_request(hdev, le_scan_enable_req, 0, timeo);
+ if (err)
+ goto unlock;

- hci_req_unlock(hdev);
-
- if (err < 0)
- return err;
+ err = __hci_request(hdev, le_scan_enable_req, (unsigned long) &cp,
+ timeo);
+ if (err)
+ goto unlock;

- schedule_delayed_work(&hdev->le_scan_disable,
- msecs_to_jiffies(timeout));
+ if (timeout > 0)
+ schedule_delayed_work(&hdev->le_scan_disable,
+ msecs_to_jiffies(timeout));

- return 0;
+unlock:
+ hci_req_unlock(hdev);
+ return err;
}

-int hci_cancel_le_scan(struct hci_dev *hdev)
+int hci_cancel_le_scan(struct hci_dev *hdev, u8 reason)
{
+ struct hci_cp_le_set_scan_enable cp;
+
BT_DBG("%s", hdev->name);

if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
return -EALREADY;

- if (cancel_delayed_work(&hdev->le_scan_disable)) {
- struct hci_cp_le_set_scan_enable cp;
+ if (reason == LE_SCAN_REQ_REASON_DISCOVERY)
+ if (!cancel_delayed_work(&hdev->le_scan_disable))
+ return 0;

- /* Send HCI command to disable LE Scan */
- memset(&cp, 0, sizeof(cp));
- hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
- }
+ /* Send HCI command to disable LE Scan */
+ memset(&cp, 0, sizeof(cp));
+ hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);

return 0;
}
@@ -1620,11 +1634,11 @@ static void le_scan_work(struct work_struct *work)
BT_DBG("%s", hdev->name);

hci_do_le_scan(hdev, param->type, param->interval, param->window,
- param->timeout);
+ param->timeout, param->reason);
}

int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
- int timeout)
+ int timeout, u8 reason)
{
struct le_scan_params *param = &hdev->le_scan_params;

@@ -1637,6 +1651,7 @@ int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
param->interval = interval;
param->window = window;
param->timeout = timeout;
+ param->reason = reason;

queue_work(system_long_wq, &hdev->le_scan);

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 705859f..46a45e7 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2364,7 +2364,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
case DISCOV_TYPE_LE:
if (lmp_host_le_capable(hdev))
err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
- LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
+ LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY,
+ LE_SCAN_REQ_REASON_DISCOVERY);
else
err = -ENOTSUPP;
break;
@@ -2372,8 +2373,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
case DISCOV_TYPE_INTERLEAVED:
if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev))
err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
- LE_SCAN_WIN,
- LE_SCAN_TIMEOUT_BREDR_LE);
+ LE_SCAN_WIN, LE_SCAN_TIMEOUT_BREDR_LE,
+ LE_SCAN_REQ_REASON_DISCOVERY);
else
err = -ENOTSUPP;
break;
@@ -2430,7 +2431,8 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
if (test_bit(HCI_INQUIRY, &hdev->flags))
err = hci_cancel_inquiry(hdev);
else
- err = hci_cancel_le_scan(hdev);
+ err = hci_cancel_le_scan(hdev,
+ LE_SCAN_REQ_REASON_DISCOVERY);

break;

--
1.7.10.4


2012-10-24 15:58:13

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 7/9] Bluetooth: Add set broadcaster MGMT command

This command will enable or disable broadcaster mode. Data can be added
with set_controller_data command.

If LE_ENABLED is set, the HCI_OP_LE_SET_ADV_ENABLE command will be sent.

This command is available on the powered off state.

Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
include/net/bluetooth/hci.h | 1 +
include/net/bluetooth/hci_core.h | 1 +
include/net/bluetooth/mgmt.h | 3 +
net/bluetooth/hci_event.c | 21 +++++++
net/bluetooth/mgmt.c | 116 ++++++++++++++++++++++++++++++++++++++
5 files changed, 142 insertions(+)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 1dadf3d..591450c 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -120,6 +120,7 @@ enum {
HCI_LINK_SECURITY,
HCI_PENDING_CLASS,
HCI_PERIODIC_INQ,
+ HCI_BROADCASTER,
};

/* HCI ioctl defines */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index d5d4dc4..fc26210 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1088,6 +1088,7 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
u8 *randomizer, u8 status);
int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
+int mgmt_set_broadcaster_complete(struct hci_dev *hdev, u8 enable, u8 status);
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
u8 ssp, u8 *eir, u16 eir_len);
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index bbb4b4a..bd64816 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -92,6 +92,7 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_BREDR 0x00000080
#define MGMT_SETTING_HS 0x00000100
#define MGMT_SETTING_LE 0x00000200
+#define MGMT_SETTING_BROADCASTER 0x00000400

#define MGMT_OP_READ_INFO 0x0004
#define MGMT_READ_INFO_SIZE 0
@@ -365,6 +366,8 @@ struct mgmt_cp_unset_controller_data {
} __packed;
#define MGMT_UNSET_CONTROLLER_DATA_SIZE 1

+#define MGMT_OP_SET_BROADCASTER 0x002B
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 3df6e67..7ad2b0d 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1330,6 +1330,23 @@ static void hci_cc_le_set_adv_params(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_LE_SET_ADV_PARAMS, status);
}

+static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+ __u8 enable;
+ void *sent;
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_ENABLE);
+ if (!sent)
+ return;
+
+ enable = *((__u8 *) sent);
+
+ if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
+ !test_bit(HCI_INIT, &hdev->flags))
+ mgmt_set_broadcaster_complete(hdev, enable, status);
+}
+
static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -2593,6 +2610,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_set_adv_params(hdev, skb);
break;

+ case HCI_OP_LE_SET_ADV_ENABLE:
+ hci_cc_le_set_adv_enable(hdev, skb);
+ break;
+
default:
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
break;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a094ef3..705859f 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -380,6 +380,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_FAST_CONNECTABLE;
settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_PAIRABLE;
+ settings |= MGMT_SETTING_BROADCASTER;

if (lmp_ssp_capable(hdev))
settings |= MGMT_SETTING_SSP;
@@ -420,6 +421,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
settings |= MGMT_SETTING_LE;

+ if (test_bit(HCI_BROADCASTER, &hdev->dev_flags))
+ settings |= MGMT_SETTING_BROADCASTER;
+
if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
settings |= MGMT_SETTING_LINK_SECURITY;

@@ -2776,6 +2780,74 @@ static int unset_controller_data(struct sock *sk, struct hci_dev *hdev,
NULL, 0);
}

+static int set_broadcaster_le(struct sock *sk, struct hci_dev *hdev, u8 enable)
+{
+ struct pending_cmd *cmd;
+ int err;
+
+ BT_DBG("%s enable:%i", hdev->name, enable);
+
+ hci_dev_lock(hdev);
+
+ if (!hdev_is_powered(hdev)) {
+ bool changed = false;
+
+ if (enable != test_bit(HCI_BROADCASTER, &hdev->dev_flags)) {
+ change_bit(HCI_BROADCASTER, &hdev->dev_flags);
+ changed = true;
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_BROADCASTER, hdev);
+ if (err < 0)
+ goto unlock;
+
+ if (changed)
+ err = new_settings(hdev, sk);
+
+ goto unlock;
+ }
+
+ if (enable == test_bit(HCI_BROADCASTER, &hdev->dev_flags)) {
+ err = send_settings_rsp(sk, MGMT_OP_SET_BROADCASTER, hdev);
+ goto unlock;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_BROADCASTER, hdev)) {
+ err = cmd_status(sk, hdev->id, MGMT_OP_SET_BROADCASTER,
+ MGMT_STATUS_BUSY);
+ goto unlock;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_BROADCASTER, hdev, NULL, 0);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ err = hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
+ &enable);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
+static int set_broadcaster(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
+{
+ struct mgmt_mode *cp = data;
+
+ BT_DBG("%s val:%i", hdev->name, cp->val);
+
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_BROADCASTER,
+ MGMT_STATUS_NOT_SUPPORTED);
+
+ return set_broadcaster_le(sk, hdev, cp->val);
+}
+
static const struct mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len);
@@ -2825,6 +2897,7 @@ static const struct mgmt_handler {
{ set_device_id, false, MGMT_SET_DEVICE_ID_SIZE },
{ set_controller_data, true, MGMT_SET_CONTROLLER_DATA_SIZE },
{ unset_controller_data, false, MGMT_UNSET_CONTROLLER_DATA_SIZE },
+ { set_broadcaster, false, MGMT_SETTING_SIZE },
};


@@ -3007,6 +3080,14 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)

if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
update_adv_data(hdev);
+
+ if (test_bit(HCI_BROADCASTER, &hdev->dev_flags) &&
+ test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ u8 enable = 1;
+ hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE,
+ sizeof(enable), &enable);
+ }
+
} else {
u8 status = MGMT_STATUS_NOT_POWERED;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
@@ -3664,6 +3745,41 @@ int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
return err;
}

+int mgmt_set_broadcaster_complete(struct hci_dev *hdev, u8 enable, u8 status)
+{
+ struct cmd_lookup match = { NULL, hdev };
+ bool changed = false;
+ int err = 0;
+
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+
+ mgmt_pending_foreach(MGMT_OP_SET_BROADCASTER, hdev,
+ cmd_status_rsp, &mgmt_err);
+
+ return err;
+ }
+
+ if (enable) {
+ if (!test_and_set_bit(HCI_BROADCASTER, &hdev->dev_flags))
+ changed = true;
+ } else {
+ if (test_and_clear_bit(HCI_BROADCASTER, &hdev->dev_flags))
+ changed = true;
+ }
+
+ mgmt_pending_foreach(MGMT_OP_SET_BROADCASTER, hdev, settings_rsp,
+ &match);
+
+ if (changed)
+ err = new_settings(hdev, match.sk);
+
+ if (match.sk)
+ sock_put(match.sk);
+
+ return err;
+}
+
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
ssp, u8 *eir, u16 eir_len)
--
1.7.10.4


2012-10-24 15:58:12

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 6/9] Bluetooth: Add HCI command to enable/disable advertising

From: Jefferson Delfes <[email protected]>

This command will be used when in discoverable mode or broadcaster role.

Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
include/net/bluetooth/hci.h | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index c0e9c3f..1dadf3d 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -963,6 +963,10 @@ struct hci_cp_le_set_adv_data {
__u8 data[HCI_MAX_ADV_LENGTH];
} __packed;

+#define HCI_OP_LE_SET_ADV_ENABLE 0x200a
+ #define ADVERTISING_DISABLE 0x00
+ #define ADVERTISING_ENABLE 0x01
+
#define HCI_OP_LE_SET_SCAN_PARAM 0x200b
struct hci_cp_le_set_scan_param {
__u8 type;
--
1.7.10.4


2012-10-24 15:58:11

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 5/9] Bluetooth: Add unset controller data MGMT command

From: Jefferson Delfes <[email protected]>

Unset controller data allows user to remove some data previously set to be
advertised via set controller data MGMT command.

If LE_ENABLED is set, the HCI_OP_LE_SET_ADV_DATA command will be sent.

This command is available on the powered off state.

Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
include/net/bluetooth/hci_core.h | 1 +
include/net/bluetooth/mgmt.h | 6 ++++++
net/bluetooth/hci_core.c | 19 +++++++++++++++++++
net/bluetooth/mgmt.c | 22 ++++++++++++++++++++++
4 files changed, 48 insertions(+)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 4046111..d5d4dc4 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -761,6 +761,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
int hci_controller_data_add(struct hci_dev *hdev, u8 flags, u8 type, u8 length,
u8 *data);
int hci_controller_data_clear(struct hci_dev *hdev);
+int hci_controller_data_remove(struct hci_dev *hdev, u8 type);

#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->dev.parent = (pdev))

diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index f99e0a8..bbb4b4a 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -359,6 +359,12 @@ struct mgmt_cp_set_controller_data {
} __packed;
#define MGMT_SET_CONTROLLER_DATA_SIZE 3

+#define MGMT_OP_UNSET_CONTROLLER_DATA 0x002A
+struct mgmt_cp_unset_controller_data {
+ __u8 type;
+} __packed;
+#define MGMT_UNSET_CONTROLLER_DATA_SIZE 1
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index e5ab961..2c2f3aa 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1439,6 +1439,25 @@ int hci_controller_data_clear(struct hci_dev *hdev)
return 0;
}

+int hci_controller_data_remove(struct hci_dev *hdev, u8 type)
+{
+ struct controller_data *match, *n;
+ int matches = 0;
+
+ list_for_each_entry_safe(match, n, &hdev->controller_data, list) {
+ if (type != match->type)
+ continue;
+
+ list_del(&match->list);
+ hdev->adv_data_len -= sizeof(match->length) +
+ sizeof(match->type) + match->length;
+ kfree(match);
+ matches++;
+ }
+
+ return matches;
+}
+
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct bdaddr_list *b;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 1ada019..a094ef3 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2755,6 +2755,27 @@ static int set_controller_data(struct sock *sk, struct hci_dev *hdev,
0);
}

+static int unset_controller_data(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_unset_controller_data *cp = data;
+ int removed;
+
+ BT_DBG("%s type:0x%02x", hdev->name, cp->type);
+
+ hci_dev_lock(hdev);
+
+ removed = hci_controller_data_remove(hdev, cp->type);
+
+ if (removed && test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ update_adv_data(hdev);
+
+ hci_dev_unlock(hdev);
+
+ return cmd_complete(sk, hdev->id, MGMT_OP_UNSET_CONTROLLER_DATA, 0,
+ NULL, 0);
+}
+
static const struct mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len);
@@ -2803,6 +2824,7 @@ static const struct mgmt_handler {
{ unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE },
{ set_device_id, false, MGMT_SET_DEVICE_ID_SIZE },
{ set_controller_data, true, MGMT_SET_CONTROLLER_DATA_SIZE },
+ { unset_controller_data, false, MGMT_UNSET_CONTROLLER_DATA_SIZE },
};


--
1.7.10.4


2012-10-24 15:58:10

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 4/9] Bluetooth: Add set controller data MGMT command

Set controller data allows user to set advertising data. The available data
types are 'service data' and 'manufacturer specific data'.

If LE_ENABLED is set, the HCI_OP_LE_SET_ADV_DATA command will be sent.

This command is available on the powered off state.

Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
include/net/bluetooth/hci.h | 3 ++
include/net/bluetooth/hci_core.h | 15 ++++++++
include/net/bluetooth/mgmt.h | 9 +++++
net/bluetooth/hci_core.c | 36 +++++++++++++++++++
net/bluetooth/mgmt.c | 74 ++++++++++++++++++++++++++++++++++++++
5 files changed, 137 insertions(+)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 75b7e58..c0e9c3f 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -333,6 +333,9 @@ enum {
#define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */
#define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */
#define EIR_DEVICE_ID 0x10 /* device ID */
+/* Advertising field types */
+#define ADV_SERVICE_DATA 0x16 /* Service Data */
+#define ADV_MANUFACTURER_DATA 0xFF /* Manufacturer Specific Data */

/* ----- HCI Commands ---- */
#define HCI_OP_NOP 0x0000
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index c885e54..4046111 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -123,6 +123,14 @@ struct le_scan_params {
int timeout;
};

+struct controller_data {
+ struct list_head list;
+ u8 flags;
+ u8 type;
+ u8 length;
+ u8 data[0];
+};
+
#define HCI_MAX_SHORT_NAME_LENGTH 10

struct amp_assoc {
@@ -280,6 +288,9 @@ struct hci_dev {

__s8 adv_tx_power;

+ struct list_head controller_data;
+ __u16 adv_data_len;
+
int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev);
@@ -747,6 +758,10 @@ void hci_conn_init_sysfs(struct hci_conn *conn);
void hci_conn_add_sysfs(struct hci_conn *conn);
void hci_conn_del_sysfs(struct hci_conn *conn);

+int hci_controller_data_add(struct hci_dev *hdev, u8 flags, u8 type, u8 length,
+ u8 *data);
+int hci_controller_data_clear(struct hci_dev *hdev);
+
#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->dev.parent = (pdev))

/* ----- LMP capabilities ----- */
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 22980a7..f99e0a8 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -350,6 +350,15 @@ struct mgmt_cp_set_device_id {
} __packed;
#define MGMT_SET_DEVICE_ID_SIZE 8

+#define MGMT_OP_SET_CONTROLLER_DATA 0x0029
+struct mgmt_cp_set_controller_data {
+ __u8 flags;
+ __u8 type;
+ __u8 length;
+ __u8 data[0];
+} __packed;
+#define MGMT_SET_CONTROLLER_DATA_SIZE 3
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 5a3400d..e5ab961 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1405,6 +1405,40 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
return 0;
}

+int hci_controller_data_add(struct hci_dev *hdev, u8 flags, u8 type, u8 length,
+ u8 *data)
+{
+ struct controller_data *c_data;
+
+ c_data = kmalloc(sizeof(*c_data) + length, GFP_KERNEL);
+ if (!c_data)
+ return -ENOMEM;
+
+ c_data->flags = flags;
+ c_data->type = type;
+ c_data->length = length;
+ memcpy(c_data->data, data, length);
+
+ list_add(&c_data->list, &hdev->controller_data);
+ hdev->adv_data_len += sizeof(length) + sizeof(type) + length;
+
+ return 0;
+}
+
+int hci_controller_data_clear(struct hci_dev *hdev)
+{
+ struct controller_data *c_data, *n;
+
+ list_for_each_entry_safe(c_data, n, &hdev->controller_data, list) {
+ list_del(&c_data->list);
+ kfree(c_data);
+ }
+
+ hdev->adv_data_len = 0;
+
+ return 0;
+}
+
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct bdaddr_list *b;
@@ -1617,6 +1651,7 @@ struct hci_dev *hci_alloc_dev(void)
INIT_LIST_HEAD(&hdev->long_term_keys);
INIT_LIST_HEAD(&hdev->remote_oob_data);
INIT_LIST_HEAD(&hdev->conn_hash.list);
+ INIT_LIST_HEAD(&hdev->controller_data);

INIT_WORK(&hdev->rx_work, hci_rx_work);
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
@@ -1781,6 +1816,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
hci_link_keys_clear(hdev);
hci_smp_ltks_clear(hdev);
hci_remote_oob_data_clear(hdev);
+ hci_controller_data_clear(hdev);
hci_dev_unlock(hdev);

hci_dev_put(hdev);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 399e502..1ada019 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2685,6 +2685,76 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
return 0;
}

+static void update_adv_data(struct hci_dev *hdev)
+{
+ struct hci_cp_le_set_adv_data cp;
+ struct controller_data *d;
+ u8 len, *ptr;
+
+ if (!hdev_is_powered(hdev))
+ return;
+
+ memset(&cp.data, 0, sizeof(cp.data));
+ ptr = cp.data;
+ len = 0;
+
+ list_for_each_entry(d, &hdev->controller_data, list) {
+ u8 entry_len = sizeof(d->length) + sizeof(d->type) + d->length;
+
+ if (len + entry_len > HCI_MAX_ADV_LENGTH) {
+ BT_DBG("Controller data bigger than adv data slot");
+ return;
+ }
+
+ ptr[0] = sizeof(d->type) + d->length;
+ ptr[1] = d->type;
+ memcpy(&ptr[2], d->data, d->length);
+
+ len += entry_len;
+ ptr += entry_len;
+ }
+
+ cp.data_len = len;
+
+ hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+}
+
+static int set_controller_data(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_set_controller_data *cp = data;
+ u8 room;
+
+ BT_DBG("%s", hdev->name);
+
+ if (cp->type != ADV_SERVICE_DATA && cp->type != ADV_MANUFACTURER_DATA)
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_CONTROLLER_DATA,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ hci_dev_lock(hdev);
+
+ room = HCI_MAX_ADV_LENGTH - hdev->adv_data_len;
+ if (sizeof(cp->length) + sizeof(cp->type) + cp->length > room) {
+ hci_dev_unlock(hdev);
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_CONTROLLER_DATA,
+ MGMT_STATUS_NO_RESOURCES);
+ }
+
+ BT_DBG("flags:0x%02x length:%i type:0x%02x", cp->flags, cp->length,
+ cp->type);
+
+ hci_controller_data_add(hdev, cp->flags, cp->type, cp->length,
+ cp->data);
+
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ update_adv_data(hdev);
+
+ hci_dev_unlock(hdev);
+
+ return cmd_complete(sk, hdev->id, MGMT_OP_SET_CONTROLLER_DATA, 0, NULL,
+ 0);
+}
+
static const struct mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len);
@@ -2732,6 +2802,7 @@ static const struct mgmt_handler {
{ block_device, false, MGMT_BLOCK_DEVICE_SIZE },
{ unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE },
{ set_device_id, false, MGMT_SET_DEVICE_ID_SIZE },
+ { set_controller_data, true, MGMT_SET_CONTROLLER_DATA_SIZE },
};


@@ -2911,6 +2982,9 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
update_class(hdev);
update_name(hdev, hdev->dev_name);
update_eir(hdev);
+
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ update_adv_data(hdev);
} else {
u8 status = MGMT_STATUS_NOT_POWERED;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
--
1.7.10.4


2012-10-24 15:58:09

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 3/9] Bluetooth: Add HCI command to set advertising data

From: Jefferson Delfes <[email protected]>

The information to be broadcasted must be set using this HCI command.

Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
include/net/bluetooth/hci.h | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index c539314..75b7e58 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -952,6 +952,14 @@ struct hci_cp_le_set_adv_params {
__u8 filter_policy;
} __packed;

+#define HCI_MAX_ADV_LENGTH 31
+
+#define HCI_OP_LE_SET_ADV_DATA 0x2008
+struct hci_cp_le_set_adv_data {
+ __u8 data_len;
+ __u8 data[HCI_MAX_ADV_LENGTH];
+} __packed;
+
#define HCI_OP_LE_SET_SCAN_PARAM 0x200b
struct hci_cp_le_set_scan_param {
__u8 type;
--
1.7.10.4


2012-10-24 15:58:07

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 1/9] Bluetooth: Add HCI command to set advertising parameters

From: Jefferson Delfes <[email protected]>

Add command to set advertising parameters before enable it.

Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
include/net/bluetooth/hci.h | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 348f4bf..c539314 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -938,6 +938,20 @@ struct hci_rp_le_read_adv_tx_power {
__s8 tx_power;
} __packed;

+#define ADV_USE_ALL_CHANNELS 0x07
+
+#define HCI_OP_LE_SET_ADV_PARAMS 0x2006
+struct hci_cp_le_set_adv_params {
+ __le16 interval_min;
+ __le16 interval_max;
+ __u8 type;
+ __u8 own_address_type;
+ __u8 direct_address_type;
+ __u8 direct_address[6];
+ __u8 channel_map;
+ __u8 filter_policy;
+} __packed;
+
#define HCI_OP_LE_SET_SCAN_PARAM 0x200b
struct hci_cp_le_set_scan_param {
__u8 type;
--
1.7.10.4


2012-10-24 15:58:08

by Aloisio Almeida

[permalink] [raw]
Subject: [RFC 2/9] Bluetooth: Set advertising parameters on LE setup

To enable broadcasting we need to set up the advertising parameters. As these
parameters are constant (only observer will use until now), they are set on LE
setup.

This patch makes the advertising non-connectable as default. To make connectable
advertising using 'hciconfig leadv' command, you must change these parameters
first.

Signed-off-by: Aloisio Almeida Jr <[email protected]>
---
net/bluetooth/hci_event.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 0b9e646..3df6e67 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -592,11 +592,22 @@ static void bredr_setup(struct hci_dev *hdev)

static void le_setup(struct hci_dev *hdev)
{
+ struct hci_cp_le_set_adv_params params;
+
/* Read LE Buffer Size */
hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);

/* Read LE Advertising Channel TX Power */
hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
+
+ /* Set ADV params */
+ memset(&params, 0, sizeof(params));
+ params.interval_min = __constant_cpu_to_le16(0x0800);
+ params.interval_max = __constant_cpu_to_le16(0x0800);
+ params.type = ADV_NONCONN_IND;
+ params.own_address_type = ADDR_LE_DEV_PUBLIC;
+ params.channel_map = ADV_USE_ALL_CHANNELS;
+ hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_PARAMS, sizeof(params), &params);
}

static void hci_setup(struct hci_dev *hdev)
@@ -1310,6 +1321,15 @@ static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
amp_write_rem_assoc_continue(hdev, rp->phy_handle);
}

+static void hci_cc_le_set_adv_params(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ hci_req_complete(hdev, HCI_OP_LE_SET_ADV_PARAMS, status);
+}
+
static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -2569,6 +2589,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_write_remote_amp_assoc(hdev, skb);
break;

+ case HCI_OP_LE_SET_ADV_PARAMS:
+ hci_cc_le_set_adv_params(hdev, skb);
+ break;
+
default:
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
break;
--
1.7.10.4