Bluetooth: btintel: Add infrastructure to read controller information
authorKiran K <kiraank@gmail.com>
Mon, 14 Sep 2020 13:25:05 +0000 (18:55 +0530)
committerMarcel Holtmann <marcel@holtmann.org>
Mon, 14 Sep 2020 18:30:36 +0000 (20:30 +0200)
New generation Intel controllers returns version information in TLV
format. This patch adds,

1) Enums, structures for TLV

2) function to read controller information, parse TLV data and populate
   intel_version_tlv structure

3) function to print version information

Signed-off-by: Kiran K <kiran.k@intel.com>
Signed-off-by: Amit K Bag <amit.k.bag@intel.com>
Signed-off-by: Raghuram Hegde <raghuram.hegde@intel.com>
Reviewed-by: Chethan T N <chethan.tumkur.narayan@intel.com>
Reviewed-by: Srivatsa Ravishankar <ravishankar.srivatsa@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
drivers/bluetooth/btintel.c
drivers/bluetooth/btintel.h

index dc6f1a9..e99fc6e 100644 (file)
@@ -361,6 +361,144 @@ int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)
 }
 EXPORT_SYMBOL_GPL(btintel_read_version);
 
+void btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)
+{
+       const char *variant;
+
+       switch (version->img_type) {
+       case 0x01:
+               variant = "Bootloader";
+               bt_dev_info(hdev, "Device revision is %u", version->dev_rev_id);
+               bt_dev_info(hdev, "Secure boot is %s",
+                           version->secure_boot ? "enabled" : "disabled");
+               bt_dev_info(hdev, "OTP lock is %s",
+                           version->otp_lock ? "enabled" : "disabled");
+               bt_dev_info(hdev, "API lock is %s",
+                           version->api_lock ? "enabled" : "disabled");
+               bt_dev_info(hdev, "Debug lock is %s",
+                           version->debug_lock ? "enabled" : "disabled");
+               bt_dev_info(hdev, "Minimum firmware build %u week %u %u",
+                           version->min_fw_build_nn, version->min_fw_build_cw,
+                           2000 + version->min_fw_build_yy);
+               break;
+       case 0x03:
+               variant = "Firmware";
+               break;
+       default:
+               bt_dev_err(hdev, "Unsupported image type(%02x)", version->img_type);
+               goto done;
+       }
+
+       bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant,
+                   2000 + (version->timestamp >> 8), version->timestamp & 0xff,
+                   version->build_type, version->build_num);
+
+done:
+       return;
+}
+EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
+
+int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)
+{
+       struct sk_buff *skb;
+       const u8 param[1] = { 0xFF };
+
+       if (!version)
+               return -EINVAL;
+
+       skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT);
+       if (IS_ERR(skb)) {
+               bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
+                          PTR_ERR(skb));
+               return PTR_ERR(skb);
+       }
+
+       if (skb->data[0]) {
+               bt_dev_err(hdev, "Intel Read Version command failed (%02x)",
+                          skb->data[0]);
+               kfree_skb(skb);
+               return -EIO;
+       }
+
+       /* Consume Command Complete Status field */
+       skb_pull(skb, 1);
+
+       /* Event parameters contatin multiple TLVs. Read each of them
+        * and only keep the required data. Also, it use existing legacy
+        * version field like hw_platform, hw_variant, and fw_variant
+        * to keep the existing setup flow
+        */
+       while (skb->len) {
+               struct intel_tlv *tlv;
+
+               tlv = (struct intel_tlv *)skb->data;
+               switch (tlv->type) {
+               case INTEL_TLV_CNVI_TOP:
+                       version->cnvi_top = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_CNVR_TOP:
+                       version->cnvr_top = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_CNVI_BT:
+                       version->cnvi_bt = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_CNVR_BT:
+                       version->cnvr_bt = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_DEV_REV_ID:
+                       version->dev_rev_id = get_unaligned_le16(tlv->val);
+                       break;
+               case INTEL_TLV_IMAGE_TYPE:
+                       version->img_type = tlv->val[0];
+                       break;
+               case INTEL_TLV_TIME_STAMP:
+                       version->timestamp = get_unaligned_le16(tlv->val);
+                       break;
+               case INTEL_TLV_BUILD_TYPE:
+                       version->build_type = tlv->val[0];
+                       break;
+               case INTEL_TLV_BUILD_NUM:
+                       version->build_num = get_unaligned_le32(tlv->val);
+                       break;
+               case INTEL_TLV_SECURE_BOOT:
+                       version->secure_boot = tlv->val[0];
+                       break;
+               case INTEL_TLV_OTP_LOCK:
+                       version->otp_lock = tlv->val[0];
+                       break;
+               case INTEL_TLV_API_LOCK:
+                       version->api_lock = tlv->val[0];
+                       break;
+               case INTEL_TLV_DEBUG_LOCK:
+                       version->debug_lock = tlv->val[0];
+                       break;
+               case INTEL_TLV_MIN_FW:
+                       version->min_fw_build_nn = tlv->val[0];
+                       version->min_fw_build_cw = tlv->val[1];
+                       version->min_fw_build_yy = tlv->val[2];
+                       break;
+               case INTEL_TLV_LIMITED_CCE:
+                       version->limited_cce = tlv->val[0];
+                       break;
+               case INTEL_TLV_SBE_TYPE:
+                       version->sbe_type = tlv->val[0];
+                       break;
+               case INTEL_TLV_OTP_BDADDR:
+                       memcpy(&version->otp_bd_addr, tlv->val, tlv->len);
+                       break;
+               default:
+                       /* Ignore rest of information */
+                       break;
+               }
+               /* consume the current tlv and move to next*/
+               skb_pull(skb, tlv->len + sizeof(*tlv));
+       }
+
+       kfree_skb(skb);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_read_version_tlv);
+
 /* ------- REGMAP IBT SUPPORT ------- */
 
 #define IBT_REG_MODE_8BIT  0x00
index 08e2060..829e1fc 100644 (file)
@@ -6,6 +6,72 @@
  *  Copyright (C) 2015  Intel Corporation
  */
 
+/* List of tlv type */
+enum {
+       INTEL_TLV_CNVI_TOP = 0x10,
+       INTEL_TLV_CNVR_TOP,
+       INTEL_TLV_CNVI_BT,
+       INTEL_TLV_CNVR_BT,
+       INTEL_TLV_CNVI_OTP,
+       INTEL_TLV_CNVR_OTP,
+       INTEL_TLV_DEV_REV_ID,
+       INTEL_TLV_USB_VENDOR_ID,
+       INTEL_TLV_USB_PRODUCT_ID,
+       INTEL_TLV_PCIE_VENDOR_ID,
+       INTEL_TLV_PCIE_DEVICE_ID,
+       INTEL_TLV_PCIE_SUBSYSTEM_ID,
+       INTEL_TLV_IMAGE_TYPE,
+       INTEL_TLV_TIME_STAMP,
+       INTEL_TLV_BUILD_TYPE,
+       INTEL_TLV_BUILD_NUM,
+       INTEL_TLV_FW_BUILD_PRODUCT,
+       INTEL_TLV_FW_BUILD_HW,
+       INTEL_TLV_FW_STEP,
+       INTEL_TLV_BT_SPEC,
+       INTEL_TLV_MFG_NAME,
+       INTEL_TLV_HCI_REV,
+       INTEL_TLV_LMP_SUBVER,
+       INTEL_TLV_OTP_PATCH_VER,
+       INTEL_TLV_SECURE_BOOT,
+       INTEL_TLV_KEY_FROM_HDR,
+       INTEL_TLV_OTP_LOCK,
+       INTEL_TLV_API_LOCK,
+       INTEL_TLV_DEBUG_LOCK,
+       INTEL_TLV_MIN_FW,
+       INTEL_TLV_LIMITED_CCE,
+       INTEL_TLV_SBE_TYPE,
+       INTEL_TLV_OTP_BDADDR,
+       INTEL_TLV_UNLOCKED_STATE
+};
+
+struct intel_tlv {
+       u8 type;
+       u8 len;
+       u8 val[0];
+} __packed;
+
+struct intel_version_tlv {
+       u32     cnvi_top;
+       u32     cnvr_top;
+       u32     cnvi_bt;
+       u32     cnvr_bt;
+       u16     dev_rev_id;
+       u8      img_type;
+       u16     timestamp;
+       u8      build_type;
+       u32     build_num;
+       u8      secure_boot;
+       u8      otp_lock;
+       u8      api_lock;
+       u8      debug_lock;
+       u8      min_fw_build_nn;
+       u8      min_fw_build_cw;
+       u8      min_fw_build_yy;
+       u8      limited_cce;
+       u8      sbe_type;
+       bdaddr_t otp_bd_addr;
+};
+
 struct intel_version {
        u8 status;
        u8 hw_platform;
@@ -77,12 +143,14 @@ int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
 void btintel_hw_error(struct hci_dev *hdev, u8 code);
 
 void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver);
+void btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *version);
 int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
                        const void *param);
 int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
 int btintel_set_event_mask(struct hci_dev *hdev, bool debug);
 int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug);
 int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver);
+int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver);
 
 struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
                                   u16 opcode_write);
@@ -137,6 +205,11 @@ static inline void btintel_version_info(struct hci_dev *hdev,
 {
 }
 
+static inline void btintel_version_info_tlv(struct hci_dev *hdev,
+                                           struct intel_version_tlv *version)
+{
+}
+
 static inline int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type,
                                      u32 plen, const void *param)
 {
@@ -165,6 +238,12 @@ static inline int btintel_read_version(struct hci_dev *hdev,
        return -EOPNOTSUPP;
 }
 
+static inline int btintel_read_version_tlv(struct hci_dev *hdev,
+                                          struct intel_version_tlv *ver)
+{
+       return -EOPNOTSUPP;
+}
+
 static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
                                                 u16 opcode_read,
                                                 u16 opcode_write)