2016-09-07 20:40:35

by Szymon Janc

[permalink] [raw]
Subject: [PATCH 1/3] Bluetooth: Add support for local name in scan rsp

From: Michał Narajowski <[email protected]>

This patch enables appending local name to scan response data.

Signed-off-by: Michał Narajowski <[email protected]>
---
net/bluetooth/hci_request.c | 28 ++++++++++++++++++++--------
net/bluetooth/mgmt.c | 1 +
2 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 9968b1c..d1839d2 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -971,14 +971,14 @@ void __hci_req_enable_advertising(struct hci_request *req)
hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
}

-static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+static u8 append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
{
- u8 ad_len = 0;
size_t name_len;
+ int max_len;

+ max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
name_len = strlen(hdev->dev_name);
- if (name_len > 0) {
- size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+ if (name_len > 0 && max_len > 0) {

if (name_len > max_len) {
name_len = max_len;
@@ -997,22 +997,34 @@ static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
return ad_len;
}

+static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
+{
+ return append_local_name(hdev, ptr, 0);
+}
+
static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
u8 *ptr)
{
struct adv_info *adv_instance;
+ u32 instance_flags;
+ u8 scan_rsp_len = 0;

adv_instance = hci_find_adv_instance(hdev, instance);
if (!adv_instance)
return 0;

- /* TODO: Set the appropriate entries based on advertising instance flags
- * here once flags other than 0 are supported.
- */
+ instance_flags = adv_instance->flags;
+
memcpy(ptr, adv_instance->scan_rsp_data,
adv_instance->scan_rsp_len);

- return adv_instance->scan_rsp_len;
+ scan_rsp_len += adv_instance->scan_rsp_len;
+ ptr += adv_instance->scan_rsp_len;
+
+ if (instance_flags & MGMT_ADV_FLAG_LOCAL_NAME)
+ scan_rsp_len = append_local_name(hdev, ptr, scan_rsp_len);
+
+ return scan_rsp_len;
}

void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 74179b9..5f6942d 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -5887,6 +5887,7 @@ static u32 get_supported_adv_flags(struct hci_dev *hdev)
flags |= MGMT_ADV_FLAG_DISCOV;
flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
+ flags |= MGMT_ADV_FLAG_LOCAL_NAME;

if (hdev->adv_tx_power != HCI_TX_POWER_INVALID)
flags |= MGMT_ADV_FLAG_TX_POWER;
--
2.7.4



2016-09-08 05:47:43

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 1/3] Bluetooth: Add support for local name in scan rsp

Hi Szymon,

> This patch enables appending local name to scan response data.
>
> Signed-off-by: Michał Narajowski <[email protected]>
> ---
> net/bluetooth/hci_request.c | 28 ++++++++++++++++++++--------
> net/bluetooth/mgmt.c | 1 +
> 2 files changed, 21 insertions(+), 8 deletions(-)
>
> diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
> index 9968b1c..d1839d2 100644
> --- a/net/bluetooth/hci_request.c
> +++ b/net/bluetooth/hci_request.c
> @@ -971,14 +971,14 @@ void __hci_req_enable_advertising(struct hci_request *req)
> hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
> }
>
> -static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
> +static u8 append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
> {
> - u8 ad_len = 0;
> size_t name_len;
> + int max_len;
>
> + max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
> name_len = strlen(hdev->dev_name);
> - if (name_len > 0) {
> - size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
> + if (name_len > 0 && max_len > 0) {
>
> if (name_len > max_len) {
> name_len = max_len;
> @@ -997,22 +997,34 @@ static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
> return ad_len;
> }
>
> +static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
> +{
> + return append_local_name(hdev, ptr, 0);
> +}
> +
> static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
> u8 *ptr)
> {
> struct adv_info *adv_instance;
> + u32 instance_flags;
> + u8 scan_rsp_len = 0;
>
> adv_instance = hci_find_adv_instance(hdev, instance);
> if (!adv_instance)
> return 0;
>
> - /* TODO: Set the appropriate entries based on advertising instance flags
> - * here once flags other than 0 are supported.
> - */
> + instance_flags = adv_instance->flags;
> +
> memcpy(ptr, adv_instance->scan_rsp_data,
> adv_instance->scan_rsp_len);
>
> - return adv_instance->scan_rsp_len;
> + scan_rsp_len += adv_instance->scan_rsp_len;
> + ptr += adv_instance->scan_rsp_len;
> +
> + if (instance_flags & MGMT_ADV_FLAG_LOCAL_NAME)
> + scan_rsp_len = append_local_name(hdev, ptr, scan_rsp_len);
> +
> + return scan_rsp_len;
> }
>
> void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance)
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index 74179b9..5f6942d 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -5887,6 +5887,7 @@ static u32 get_supported_adv_flags(struct hci_dev *hdev)
> flags |= MGMT_ADV_FLAG_DISCOV;
> flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
> flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
> + flags |= MGMT_ADV_FLAG_LOCAL_NAME;
>
> if (hdev->adv_tx_power != HCI_TX_POWER_INVALID)
> flags |= MGMT_ADV_FLAG_TX_POWER;

the Get Advertising Size Information needs to work as well. This screams adding something into mgmt-tester to test it with different length of full and short names.

Regards

Marcel


2016-09-08 05:45:48

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 2/3] Bluetooth: Add support for appearance in scan rsp

Hi Szymon,

> This patch enables prepending appearance value to scan response data.
> It also adds support for setting appearance value through mgmt command.
>
> Signed-off-by: Michał Narajowski <[email protected]>
> ---
> include/net/bluetooth/hci_core.h | 1 +
> include/net/bluetooth/mgmt.h | 6 ++++++
> net/bluetooth/hci_request.c | 9 +++++++++
> net/bluetooth/mgmt.c | 40 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 56 insertions(+)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index a48f71d..f00bf66 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -211,6 +211,7 @@ struct hci_dev {
> __u8 dev_name[HCI_MAX_NAME_LENGTH];
> __u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
> __u8 eir[HCI_MAX_EIR_LENGTH];
> + __u16 appearance;
> __u8 dev_class[3];
> __u8 major_class;
> __u8 minor_class;
> diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
> index 611b243..72a456b 100644
> --- a/include/net/bluetooth/mgmt.h
> +++ b/include/net/bluetooth/mgmt.h
> @@ -598,6 +598,12 @@ struct mgmt_rp_read_ext_info {
> __u8 eir[0];
> } __packed;
>
> +#define MGMT_OP_SET_APPEARANCE 0x0043
> +struct mgmt_cp_set_appearance {
> + __u16 appearance;
> +} __packed;
> +#define MGMT_SET_APPEARANCE_SIZE 2
> +
> #define MGMT_EV_CMD_COMPLETE 0x0001
> struct mgmt_ev_cmd_complete {
> __le16 opcode;
> diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
> index d1839d2..ac683d1 100644
> --- a/net/bluetooth/hci_request.c
> +++ b/net/bluetooth/hci_request.c
> @@ -1015,6 +1015,15 @@ static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
>
> instance_flags = adv_instance->flags;
>
> + if (instance_flags & MGMT_ADV_FLAG_APPEARANCE &&
> + hdev->appearance != 0x0000) {
> + ptr[0] = 3;
> + ptr[1] = 2;

Lets use the proper constant for this.

> + memcpy(ptr + 2, &hdev->appearance, 2);

And this is broken. put_unaligned_le16.

> + scan_rsp_len += 4;
> + ptr += 4;
> + }
> +
> memcpy(ptr, adv_instance->scan_rsp_data,
> adv_instance->scan_rsp_len);
>
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index 5f6942d..5eb8716 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -105,6 +105,7 @@ static const u16 mgmt_commands[] = {
> MGMT_OP_GET_ADV_SIZE_INFO,
> MGMT_OP_START_LIMITED_DISCOVERY,
> MGMT_OP_READ_EXT_INFO,
> + MGMT_OP_SET_APPEARANCE,
> };
>
> static const u16 mgmt_events[] = {
> @@ -3112,6 +3113,40 @@ failed:
> return err;
> }
>
> +static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
> + u16 len)
> +{
> + struct mgmt_cp_set_appearance *cp = data;
> + int err;
> +
> + BT_DBG("");
> +
> + hci_dev_lock(hdev);
> +
> + /* If the old values are the same as the new ones just return a
> + * direct command complete event.
> + */
> + if (hdev->appearance == cp->appearance) {
> + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_APPEARANCE, 0,
> + data, len);
> + goto failed;
> + }
> +
> + if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
> + mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_APPEARANCE,
> + MGMT_STATUS_BUSY);
> + goto failed;

Why? Lets expire the advertising instance right away and not introduce broken code in the first place.

> + }
> +
> + hdev->appearance = cp->appearance;
> +
> + err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_APPEARANCE, 0,
> + data, len);
> +failed:

Use unlock: or done: as label name here.

> + hci_dev_unlock(hdev);
> + return err;
> +}
> +
> static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
> u16 opcode, struct sk_buff *skb)
> {
> @@ -5887,6 +5922,7 @@ static u32 get_supported_adv_flags(struct hci_dev *hdev)
> flags |= MGMT_ADV_FLAG_DISCOV;
> flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
> flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
> + flags |= MGMT_ADV_FLAG_APPEARANCE;
> flags |= MGMT_ADV_FLAG_LOCAL_NAME;
>
> if (hdev->adv_tx_power != HCI_TX_POWER_INVALID)
> @@ -5964,6 +6000,9 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
> tx_power_managed = true;
> max_len -= 3;
> }
> + } else {
> + if (adv_flags & MGMT_ADV_FLAG_APPEARANCE)
> + max_len -= 4;
> }

And I am missing the update to tlv_data_max_len function. Feel free to unify it if you can.

>
> if (len > max_len)
> @@ -6431,6 +6470,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
> { start_limited_discovery, MGMT_START_DISCOVERY_SIZE },
> { read_ext_controller_info,MGMT_READ_EXT_INFO_SIZE,
> HCI_MGMT_UNTRUSTED },
> + { set_appearance, MGMT_SET_APPEARANCE_SIZE },
> };

Regards

Marcel


2016-09-08 05:38:12

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 3/3] Bluetooth: Expire advertisement if name or appearance changed

Hi Szymon,

> Expire currently advertised instance if it contains appearance or
> name and one of those is being changed.
>
> Signed-off-by: Michał Narajowski <[email protected]>
> Signed-off-by: Szymon Janc <[email protected]>
> ---
> net/bluetooth/mgmt.c | 48 +++++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 41 insertions(+), 7 deletions(-)
>
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index 5eb8716..9be23b5 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -3015,6 +3015,39 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev,
> HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
> }
>
> +static void adv_expire(struct hci_dev *hdev, u32 flags)
> +{
> + struct adv_info *adv_instance, *n, *next_instance;
> + u8 schedule_instance = 0;
> + struct hci_request req;
> + int err;
> +
> + list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
> + if (hdev->cur_adv_instance != adv_instance->instance)
> + continue;
> +
> + /* stop if current instance doesn't need to be changed */
> + if (!(adv_instance->flags & flags))
> + break;
> +
> + cancel_adv_timeout(hdev);
> +
> + next_instance = hci_get_next_instance(hdev,
> + adv_instance->instance);
> + if (next_instance)
> + schedule_instance = next_instance->instance;
> + break;
> + }

this is a bit convoluted, isn't it? A break statement at the end of the loop execution block means you need to re-think that whole logic.

This whole list iteration is just to find current instance. Which we already know because of hdev->cur_adv_instance. So someone please explain to be what this thing is doing.

> +
> + if (!schedule_instance)
> + return;
> +
> + hci_req_init(&req, hdev);
> + err = __hci_req_schedule_adv_instance(&req, schedule_instance, true);
> + if (!err)
> + hci_req_run(&req, NULL);

If (err)
return;

> +}
> +
> static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)
> {
> struct mgmt_cp_set_local_name *cp;
> @@ -3030,13 +3063,17 @@ static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)
>
> cp = cmd->param;
>
> - if (status)
> + if (status) {
> mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
> mgmt_status(status));
> - else
> + } else {
> mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
> cp, sizeof(*cp));
>
> + if (hci_dev_test_flag(hdev, HCI_LE_ADV))
> + adv_expire(hdev, MGMT_ADV_FLAG_LOCAL_NAME);
> + }
> +
> mgmt_pending_remove(cmd);
>
> unlock:
> @@ -3132,11 +3169,8 @@ static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
> goto failed;
> }
>
> - if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
> - mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_APPEARANCE,
> - MGMT_STATUS_BUSY);
> - goto failed;
> - }
> + if (hci_dev_test_flag(hdev, HCI_LE_ADV))
> + adv_expire(hdev, MGMT_ADV_FLAG_APPEARANCE);

This change can not be correct. It would mean the previous patch is broken. So what is it?

Regards

Marcel


2016-09-07 20:40:37

by Szymon Janc

[permalink] [raw]
Subject: [PATCH 3/3] Bluetooth: Expire advertisement if name or appearance changed

From: Michał Narajowski <[email protected]>

Expire currently advertised instance if it contains appearance or
name and one of those is being changed.

Signed-off-by: Michał Narajowski <[email protected]>
Signed-off-by: Szymon Janc <[email protected]>
---
net/bluetooth/mgmt.c | 48 +++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 41 insertions(+), 7 deletions(-)

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 5eb8716..9be23b5 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3015,6 +3015,39 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev,
HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
}

+static void adv_expire(struct hci_dev *hdev, u32 flags)
+{
+ struct adv_info *adv_instance, *n, *next_instance;
+ u8 schedule_instance = 0;
+ struct hci_request req;
+ int err;
+
+ list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
+ if (hdev->cur_adv_instance != adv_instance->instance)
+ continue;
+
+ /* stop if current instance doesn't need to be changed */
+ if (!(adv_instance->flags & flags))
+ break;
+
+ cancel_adv_timeout(hdev);
+
+ next_instance = hci_get_next_instance(hdev,
+ adv_instance->instance);
+ if (next_instance)
+ schedule_instance = next_instance->instance;
+ break;
+ }
+
+ if (!schedule_instance)
+ return;
+
+ hci_req_init(&req, hdev);
+ err = __hci_req_schedule_adv_instance(&req, schedule_instance, true);
+ if (!err)
+ hci_req_run(&req, NULL);
+}
+
static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)
{
struct mgmt_cp_set_local_name *cp;
@@ -3030,13 +3063,17 @@ static void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode)

cp = cmd->param;

- if (status)
+ if (status) {
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
mgmt_status(status));
- else
+ } else {
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
cp, sizeof(*cp));

+ if (hci_dev_test_flag(hdev, HCI_LE_ADV))
+ adv_expire(hdev, MGMT_ADV_FLAG_LOCAL_NAME);
+ }
+
mgmt_pending_remove(cmd);

unlock:
@@ -3132,11 +3169,8 @@ static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}

- if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
- mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_APPEARANCE,
- MGMT_STATUS_BUSY);
- goto failed;
- }
+ if (hci_dev_test_flag(hdev, HCI_LE_ADV))
+ adv_expire(hdev, MGMT_ADV_FLAG_APPEARANCE);

hdev->appearance = cp->appearance;

--
2.7.4


2016-09-07 20:40:36

by Szymon Janc

[permalink] [raw]
Subject: [PATCH 2/3] Bluetooth: Add support for appearance in scan rsp

From: Michał Narajowski <[email protected]>

This patch enables prepending appearance value to scan response data.
It also adds support for setting appearance value through mgmt command.

Signed-off-by: Michał Narajowski <[email protected]>
---
include/net/bluetooth/hci_core.h | 1 +
include/net/bluetooth/mgmt.h | 6 ++++++
net/bluetooth/hci_request.c | 9 +++++++++
net/bluetooth/mgmt.c | 40 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 56 insertions(+)

diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index a48f71d..f00bf66 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -211,6 +211,7 @@ struct hci_dev {
__u8 dev_name[HCI_MAX_NAME_LENGTH];
__u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
__u8 eir[HCI_MAX_EIR_LENGTH];
+ __u16 appearance;
__u8 dev_class[3];
__u8 major_class;
__u8 minor_class;
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 611b243..72a456b 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -598,6 +598,12 @@ struct mgmt_rp_read_ext_info {
__u8 eir[0];
} __packed;

+#define MGMT_OP_SET_APPEARANCE 0x0043
+struct mgmt_cp_set_appearance {
+ __u16 appearance;
+} __packed;
+#define MGMT_SET_APPEARANCE_SIZE 2
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index d1839d2..ac683d1 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -1015,6 +1015,15 @@ static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,

instance_flags = adv_instance->flags;

+ if (instance_flags & MGMT_ADV_FLAG_APPEARANCE &&
+ hdev->appearance != 0x0000) {
+ ptr[0] = 3;
+ ptr[1] = 2;
+ memcpy(ptr + 2, &hdev->appearance, 2);
+ scan_rsp_len += 4;
+ ptr += 4;
+ }
+
memcpy(ptr, adv_instance->scan_rsp_data,
adv_instance->scan_rsp_len);

diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 5f6942d..5eb8716 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -105,6 +105,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_GET_ADV_SIZE_INFO,
MGMT_OP_START_LIMITED_DISCOVERY,
MGMT_OP_READ_EXT_INFO,
+ MGMT_OP_SET_APPEARANCE,
};

static const u16 mgmt_events[] = {
@@ -3112,6 +3113,40 @@ failed:
return err;
}

+static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
+{
+ struct mgmt_cp_set_appearance *cp = data;
+ int err;
+
+ BT_DBG("");
+
+ hci_dev_lock(hdev);
+
+ /* If the old values are the same as the new ones just return a
+ * direct command complete event.
+ */
+ if (hdev->appearance == cp->appearance) {
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_APPEARANCE, 0,
+ data, len);
+ goto failed;
+ }
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
+ mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_APPEARANCE,
+ MGMT_STATUS_BUSY);
+ goto failed;
+ }
+
+ hdev->appearance = cp->appearance;
+
+ err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_APPEARANCE, 0,
+ data, len);
+failed:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb)
{
@@ -5887,6 +5922,7 @@ static u32 get_supported_adv_flags(struct hci_dev *hdev)
flags |= MGMT_ADV_FLAG_DISCOV;
flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
+ flags |= MGMT_ADV_FLAG_APPEARANCE;
flags |= MGMT_ADV_FLAG_LOCAL_NAME;

if (hdev->adv_tx_power != HCI_TX_POWER_INVALID)
@@ -5964,6 +6000,9 @@ static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
tx_power_managed = true;
max_len -= 3;
}
+ } else {
+ if (adv_flags & MGMT_ADV_FLAG_APPEARANCE)
+ max_len -= 4;
}

if (len > max_len)
@@ -6431,6 +6470,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ start_limited_discovery, MGMT_START_DISCOVERY_SIZE },
{ read_ext_controller_info,MGMT_READ_EXT_INFO_SIZE,
HCI_MGMT_UNTRUSTED },
+ { set_appearance, MGMT_SET_APPEARANCE_SIZE },
};

void mgmt_index_added(struct hci_dev *hdev)
--
2.7.4