Bluetooth: Add support for Get Advertising Size Information command
[linux-2.6-block.git] / net / bluetooth / mgmt.c
index 05370e76feb0db84aba98c4995277ffcbcc97fa1..dc8e428050d93e353ddcfbf5e8e0cd92669537f7 100644 (file)
@@ -102,6 +102,7 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_READ_ADV_FEATURES,
        MGMT_OP_ADD_ADVERTISING,
        MGMT_OP_REMOVE_ADVERTISING,
+       MGMT_OP_GET_ADV_SIZE_INFO,
 };
 
 static const u16 mgmt_events[] = {
@@ -7059,6 +7060,62 @@ unlock:
        return err;
 }
 
+static u8 tlv_data_max_len(u32 adv_flags, bool is_adv_data)
+{
+       u8 max_len = HCI_MAX_AD_LENGTH;
+
+       if (is_adv_data) {
+               if (adv_flags & (MGMT_ADV_FLAG_DISCOV |
+                                MGMT_ADV_FLAG_LIMITED_DISCOV |
+                                MGMT_ADV_FLAG_MANAGED_FLAGS))
+                       max_len -= 3;
+
+               if (adv_flags & MGMT_ADV_FLAG_TX_POWER)
+                       max_len -= 3;
+       }
+
+       return max_len;
+}
+
+static int get_adv_size_info(struct sock *sk, struct hci_dev *hdev,
+                            void *data, u16 data_len)
+{
+       struct mgmt_cp_get_adv_size_info *cp = data;
+       struct mgmt_rp_get_adv_size_info rp;
+       u32 flags, supported_flags;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+                                      MGMT_STATUS_REJECTED);
+
+       if (cp->instance < 1 || cp->instance > HCI_MAX_ADV_INSTANCES)
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+                                      MGMT_STATUS_INVALID_PARAMS);
+
+       flags = __le32_to_cpu(cp->flags);
+
+       /* The current implementation only supports a subset of the specified
+        * flags.
+        */
+       supported_flags = get_supported_adv_flags(hdev);
+       if (flags & ~supported_flags)
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+                                      MGMT_STATUS_INVALID_PARAMS);
+
+       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);
+
+       err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO,
+                               MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+
+       return err;
+}
+
 static const struct hci_mgmt_handler mgmt_handlers[] = {
        { NULL }, /* 0x0000 (no command) */
        { read_version,            MGMT_READ_VERSION_SIZE,
@@ -7146,6 +7203,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
        { add_advertising,         MGMT_ADD_ADVERTISING_SIZE,
                                                HCI_MGMT_VAR_LEN },
        { remove_advertising,      MGMT_REMOVE_ADVERTISING_SIZE },
+       { get_adv_size_info,       MGMT_GET_ADV_SIZE_INFO_SIZE },
 };
 
 void mgmt_index_added(struct hci_dev *hdev)