Merge tag 'spi-fix-v4.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
[cascardo/linux.git] / net / bluetooth / mgmt.c
index 2b6fe10..1fba2a0 100644 (file)
@@ -867,17 +867,6 @@ static int read_controller_info(struct sock *sk, struct hci_dev *hdev,
                                 sizeof(rp));
 }
 
-static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
-                                 u8 data_len)
-{
-       eir[eir_len++] = sizeof(type) + data_len;
-       eir[eir_len++] = type;
-       memcpy(&eir[eir_len], data, data_len);
-       eir_len += data_len;
-
-       return eir_len;
-}
-
 static u16 append_eir_data_to_buf(struct hci_dev *hdev, u8 *eir)
 {
        u16 eir_len = 0;
@@ -887,6 +876,10 @@ static u16 append_eir_data_to_buf(struct hci_dev *hdev, u8 *eir)
                eir_len = eir_append_data(eir, eir_len, EIR_CLASS_OF_DEV,
                                          hdev->dev_class, 3);
 
+       if (hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+               eir_len = eir_append_le16(eir, eir_len, EIR_APPEARANCE,
+                                         hdev->appearance);
+
        name_len = strlen(hdev->dev_name);
        eir_len = eir_append_data(eir, eir_len, EIR_NAME_COMPLETE,
                                  hdev->dev_name, name_len);
@@ -940,12 +933,18 @@ static int read_ext_controller_info(struct sock *sk, struct hci_dev *hdev,
 
 static int ext_info_changed(struct hci_dev *hdev, struct sock *skip)
 {
-       struct mgmt_ev_ext_info_changed ev;
+       char buf[512];
+       struct mgmt_ev_ext_info_changed *ev = (void *)buf;
+       u16 eir_len;
 
-       ev.eir_len = cpu_to_le16(0);
+       memset(buf, 0, sizeof(buf));
+
+       eir_len = append_eir_data_to_buf(hdev, ev->eir);
+       ev->eir_len = cpu_to_le16(eir_len);
 
-       return mgmt_limited_event(MGMT_EV_EXT_INFO_CHANGED, hdev, &ev,
-                                 sizeof(ev), HCI_MGMT_EXT_INFO_EVENTS, skip);
+       return mgmt_limited_event(MGMT_EV_EXT_INFO_CHANGED, hdev, ev,
+                                 sizeof(*ev) + eir_len,
+                                 HCI_MGMT_EXT_INFO_EVENTS, skip);
 }
 
 static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
@@ -2521,6 +2520,8 @@ static int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev,
        if (!cmd)
                return -ENOMEM;
 
+       cmd->cmd_complete = addr_cmd_complete;
+
        err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
                           sizeof(cp->addr.bdaddr), &cp->addr.bdaddr);
        if (err < 0)
@@ -3137,7 +3138,7 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
        /* The name is stored in the scan response data and so
         * no need to udpate the advertising data here.
         */
-       if (lmp_le_capable(hdev))
+       if (lmp_le_capable(hdev) && hci_dev_test_flag(hdev, HCI_ADVERTISING))
                __hci_req_update_scan_rsp_data(&req, hdev->cur_adv_instance);
 
        err = hci_req_run(&req, set_name_complete);
@@ -3158,6 +3159,10 @@ static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("");
 
+       if (!lmp_le_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_APPEARANCE,
+                                      MGMT_STATUS_NOT_SUPPORTED);
+
        apperance = le16_to_cpu(cp->appearance);
 
        hci_dev_lock(hdev);
@@ -3167,6 +3172,8 @@ static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
 
                if (hci_dev_test_flag(hdev, HCI_LE_ADV))
                        adv_expire(hdev, MGMT_ADV_FLAG_APPEARANCE);
+
+               ext_info_changed(hdev, sk);
        }
 
        err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_APPEARANCE, 0, NULL,
@@ -6010,7 +6017,15 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
        return err;
 }
 
-static u8 tlv_data_max_len(u32 adv_flags, bool is_adv_data)
+static u8 calculate_name_len(struct hci_dev *hdev)
+{
+       u8 buf[HCI_MAX_SHORT_NAME_LENGTH + 3];
+
+       return append_local_name(hdev, buf, 0);
+}
+
+static u8 tlv_data_max_len(struct hci_dev *hdev, u32 adv_flags,
+                          bool is_adv_data)
 {
        u8 max_len = HCI_MAX_AD_LENGTH;
 
@@ -6023,9 +6038,8 @@ static u8 tlv_data_max_len(u32 adv_flags, bool is_adv_data)
                if (adv_flags & MGMT_ADV_FLAG_TX_POWER)
                        max_len -= 3;
        } else {
-               /* at least 1 byte of name should fit in */
                if (adv_flags & MGMT_ADV_FLAG_LOCAL_NAME)
-                       max_len -= 3;
+                       max_len -= calculate_name_len(hdev);
 
                if (adv_flags & (MGMT_ADV_FLAG_APPEARANCE))
                        max_len -= 4;
@@ -6056,12 +6070,13 @@ static bool appearance_managed(u32 adv_flags)
        return adv_flags & MGMT_ADV_FLAG_APPEARANCE;
 }
 
-static bool tlv_data_is_valid(u32 adv_flags, u8 *data, u8 len, bool is_adv_data)
+static bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data,
+                             u8 len, bool is_adv_data)
 {
        int i, cur_len;
        u8 max_len;
 
-       max_len = tlv_data_max_len(adv_flags, is_adv_data);
+       max_len = tlv_data_max_len(hdev, adv_flags, is_adv_data);
 
        if (len > max_len)
                return false;
@@ -6208,8 +6223,8 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
                goto unlock;
        }
 
-       if (!tlv_data_is_valid(flags, cp->data, cp->adv_data_len, true) ||
-           !tlv_data_is_valid(flags, cp->data + cp->adv_data_len,
+       if (!tlv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len, true) ||
+           !tlv_data_is_valid(hdev, flags, cp->data + cp->adv_data_len,
                               cp->scan_rsp_len, false)) {
                err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
                                      MGMT_STATUS_INVALID_PARAMS);
@@ -6422,8 +6437,8 @@ static int get_adv_size_info(struct sock *sk, struct hci_dev *hdev,
 
        rp.instance = cp->instance;
        rp.flags = cp->flags;
-       rp.max_adv_data_len = tlv_data_max_len(flags, true);
-       rp.max_scan_rsp_len = tlv_data_max_len(flags, false);
+       rp.max_adv_data_len = tlv_data_max_len(hdev, flags, true);
+       rp.max_scan_rsp_len = tlv_data_max_len(hdev, flags, false);
 
        err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
                                MGMT_STATUS_SUCCESS, &rp, sizeof(rp));