RE: [PATCH v1] Bluetooth: btintel: Add support to configure TX power

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Luiz, 

Appreciate your comments.

> Subject: Re: [PATCH v1] Bluetooth: btintel: Add support to configure TX power
> 
> Hi Vijay,
> 
> On Fri, Oct 25, 2024 at 8:17 AM Vijay Satija <vijay.satija@xxxxxxxxx> wrote:
> >
> > Bluetooth has regulatory limitations which prohibit or allow usage
> > of certain bands or channels or limiting Tx power. The Tx power
> > values can be configured in ACPI table. This patch reads from ACPI entry
> > and configures the TX power to be used accordingly.
> >
> > Signed-off-by: Vijay Satija <vijay.satija@xxxxxxxxx>
> > Signed-off-by: Kiran K <kiran.k@xxxxxxxxx>
> > ---
> >  drivers/bluetooth/btintel.c | 313
> ++++++++++++++++++++++++++++++++++++
> >  drivers/bluetooth/btintel.h |  35 ++++
> >  2 files changed, 348 insertions(+)
> >
> > diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
> > index d496cf2c3411..aa4ebe230cc5 100644
> > --- a/drivers/bluetooth/btintel.c
> > +++ b/drivers/bluetooth/btintel.c
> > @@ -29,6 +29,11 @@
> >
> >  #define BTINTEL_EFI_DSBR       L"UefiCnvCommonDSBR"
> >
> > +#define BTINTEL_BT_DOMAIN              0x12
> > +#define BTINTEL_SAR_LEGACY             0
> > +#define BTINTEL_SAR_INC_PWR            1
> > +#define BTINTEL_SAR_INC_PWR_SUPPORTED  0
> > +
> >  enum {
> >         DSM_SET_WDISABLE2_DELAY = 1,
> >         DSM_SET_RESET_METHOD = 3,
> > @@ -2799,6 +2804,311 @@ static int btintel_set_dsbr(struct hci_dev *hdev,
> struct intel_version_tlv *ver)
> >         return 0;
> >  }
> >
> > +#ifdef CONFIG_ACPI
> > +static acpi_status btintel_evaluate_acpi_method(struct hci_dev *hdev,
> > +                                               acpi_string method,
> > +                                               union acpi_object **ptr,
> > +                                               u8 pkg_size)
> > +{
> > +       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> > +       union acpi_object *p;
> > +       acpi_status status;
> > +       acpi_handle handle;
> > +
> > +       handle = ACPI_HANDLE(GET_HCIDEV_DEV(hdev));
> > +       if (!handle) {
> > +               bt_dev_dbg(hdev, "ACPI-BT: No ACPI support for Bluetooth
> device");
> > +               return AE_NOT_EXIST;
> > +       }
> > +
> > +       status = acpi_evaluate_object(handle, method, NULL, &buffer);
> > +
> > +       if (ACPI_FAILURE(status)) {
> > +               bt_dev_dbg(hdev, "ACPI-BT: ACPI Failure: %s method: %s",
> > +                          acpi_format_exception(status), method);
> > +               return status;
> > +       }
> > +
> > +       p = buffer.pointer;
> > +
> > +       if (p->type != ACPI_TYPE_PACKAGE || p->package.count < pkg_size) {
> > +               bt_dev_warn(hdev, "ACPI-BT: Invalid object type: %d or package
> count: %d",
> > +                           p->type, p->package.count);
> > +               kfree(buffer.pointer);
> > +               return AE_ERROR;
> > +       }
> > +
> > +       *ptr = buffer.pointer;
> > +       return 0;
> > +}
> > +
> > +static union acpi_object *btintel_acpi_get_bt_pkg(union acpi_object
> *buffer)
> > +{
> > +       union acpi_object *domain, *bt_pkg;
> > +       int i;
> > +
> > +       for (i = 1; i < buffer->package.count; i++) {
> > +               bt_pkg = &buffer->package.elements[i];
> > +               domain = &bt_pkg->package.elements[0];
> > +               if (domain->type == ACPI_TYPE_INTEGER &&
> > +                   domain->integer.value == BTINTEL_BT_DOMAIN)
> > +                       return bt_pkg;
> > +       }
> > +       return ERR_PTR(-ENOENT);
> > +}
> > +
> > +static int btintel_send_sar_ddc(struct hci_dev *hdev, struct
> btintel_cp_ddc_write *data, u8 len)
> > +{
> > +       struct sk_buff *skb;
> > +
> > +       skb = __hci_cmd_sync(hdev, 0xfc8b, len, data, HCI_CMD_TIMEOUT);
> > +       if (IS_ERR(skb)) {
> > +               bt_dev_warn(hdev, "Failed to send sar ddc id:0x%4.4x (%ld)",
> > +                           le16_to_cpu(data->id), PTR_ERR(skb));
> > +               return PTR_ERR(skb);
> > +       }
> > +       kfree_skb(skb);
> > +       return 0;
> > +}
> > +
> > +static int btintel_set_legacy_sar(struct hci_dev *hdev,
> > +                                 struct btintel_sar_inc_pwr *sar)
> > +{
> > +       struct btintel_cp_ddc_write *cmd;
> > +       u8 buffer[64];
> > +       int ret;
> > +
> > +       cmd = (void *)buffer;
> > +       cmd->len = 3;
> > +       cmd->id = cpu_to_le16(0x0131);
> > +       cmd->data[0] = sar->br >> 3;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 4);
> > +       if (ret)
> > +               return ret;
> 
> It might be a good idea to handle each command on its own function and
> then name it according to what the command would be doing.
Ack
> 
> > +
> > +       cmd->len = 3;
> > +       cmd->id = cpu_to_le16(0x0132);
> > +       cmd->data[0] = sar->br >> 3;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 4);
> > +       if (ret)
> > +               return ret;
> > +
> > +       cmd->len = 3;
> > +       cmd->id = cpu_to_le16(0x0133);
> > +       cmd->data[0] = min3(sar->le, sar->le_lr, sar->le_2mhz) >> 3;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 4);
> > +       if (ret)
> > +               return ret;
> > +
> > +       cmd->len = 5;
> > +       cmd->id = cpu_to_le16(0x0137);
> > +       cmd->data[0] = sar->br >> 3;
> > +       cmd->data[1] = sar->edr2 >> 3;
> > +       cmd->data[2] = sar->edr3 >> 3;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 6);
> > +       if (ret)
> > +               return ret;
> > +
> > +       cmd->len = 5;
> > +       cmd->id = cpu_to_le16(0x0138);
> > +       cmd->data[0] = sar->br >> 3;
> > +       cmd->data[1] = sar->edr2 >> 3;
> > +       cmd->data[2] = sar->edr3 >> 3;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 6);
> > +       if (ret)
> > +               return ret;
> > +
> > +       cmd->len = 5;
> > +       cmd->id = cpu_to_le16(0x013b);
> > +       cmd->data[0] = sar->br >> 3;
> > +       cmd->data[1] = sar->edr2 >> 3;
> > +       cmd->data[2] = sar->edr3 >> 3;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 6);
> > +       if (ret)
> > +               return ret;
> > +
> > +       cmd->len = 5;
> > +       cmd->id = cpu_to_le16(0x013c);
> > +       cmd->data[0] = sar->br >> 3;
> > +       cmd->data[1] = sar->edr2 >> 3;
> > +       cmd->data[2] = sar->edr3 >> 3;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 6);
> > +
> > +       return ret;
> > +}
> > +
> > +static int btintel_set_mutual_sar(struct hci_dev *hdev,
> > +                                 struct btintel_sar_inc_pwr *sar)
> > +{
> > +       struct btintel_cp_ddc_write *cmd;
> > +       struct sk_buff *skb;
> > +       u8 buffer[64];
> > +       bool enable;
> > +       int ret;
> > +
> > +       cmd = (void *)buffer;
> > +
> > +       cmd->len = 3;
> > +       cmd->id = cpu_to_le16(0x019e);
> > +
> > +       if (sar->revision == BTINTEL_SAR_INC_PWR &&
> > +           sar->inc_power_mode == BTINTEL_SAR_INC_PWR_SUPPORTED)
> > +               cmd->data[0] = 0x01;
> > +       else
> > +               cmd->data[0] = 0x00;
> > +
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 4);
> > +       if (ret)
> > +               return ret;
> > +
> > +       if (sar->revision == BTINTEL_SAR_INC_PWR &&
> > +           sar->inc_power_mode == BTINTEL_SAR_INC_PWR_SUPPORTED) {
> > +               cmd->len = 3;
> > +               cmd->id = cpu_to_le16(0x019f);
> > +               cmd->data[0] = sar->sar_2400_chain_a;
> > +
> > +               ret = btintel_send_sar_ddc(hdev, cmd, 4);
> > +               if (ret)
> > +                       return ret;
> > +       }
> > +
> > +       cmd->len = 3;
> > +       cmd->id = cpu_to_le16(0x01a0);
> > +       cmd->data[0] = sar->br;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 4);
> > +       if (ret)
> > +               return ret;
> 
> Ditto, hopefully if we have dedicated functions for each of these
> commands we can reuse some of them.
Ack
> 
> > +       cmd->len = 3;
> > +       cmd->id = cpu_to_le16(0x01a1);
> > +       cmd->data[0] = sar->edr2;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 4);
> > +       if (ret)
> > +               return ret;
> > +
> > +       cmd->len = 3;
> > +       cmd->id = cpu_to_le16(0x01a2);
> > +       cmd->data[0] = sar->edr3;
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 4);
> > +       if (ret)
> > +               return ret;
> > +
> > +       cmd->len = 3;
> > +       cmd->id = cpu_to_le16(0x01a3);
> > +       cmd->data[0] = min3(sar->le, sar->le_lr, sar->le_2mhz);
> > +       ret = btintel_send_sar_ddc(hdev, cmd, 4);
> > +       if (ret)
> > +               return ret;
> > +
> > +       enable = true;
> > +       skb = __hci_cmd_sync(hdev, 0xfe25, 1, &enable, HCI_CMD_TIMEOUT);
> > +       if (IS_ERR(skb)) {
> > +               bt_dev_warn(hdev, "Failed to send Intel SAR Enable (%ld)",
> PTR_ERR(skb));
> > +               return PTR_ERR(skb);
> > +       }
> > +
> > +       kfree_skb(skb);
> > +       return 0;
> > +}
> > +
> > +static int btintel_sar_send_to_device(struct hci_dev *hdev, struct
> btintel_sar_inc_pwr *sar,
> > +                                     struct intel_version_tlv *ver)
> > +{
> > +       u16 cnvi, cnvr;
> > +       int ret;
> > +
> > +       cnvi = ver->cnvi_top & 0xfff;
> > +       cnvr = ver->cnvr_top & 0xfff;
> > +
> > +       if (cnvi < BTINTEL_CNVI_BLAZARI && cnvr < BTINTEL_CNVR_FMP2) {
> > +               bt_dev_info(hdev, "Applying legacy Bluetooth SAR");
> > +               ret = btintel_set_legacy_sar(hdev, sar);
> > +       } else if (cnvi == BTINTEL_CNVI_GAP || cnvr == BTINTEL_CNVR_FMP2) {
> > +               bt_dev_info(hdev, "Applying  Mutual Bluetooth SAR");
> > +               ret = btintel_set_mutual_sar(hdev, sar);
> > +       } else {
> > +               ret = -EOPNOTSUPP;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static int btintel_acpi_set_sar(struct hci_dev *hdev, struct intel_version_tlv
> *ver)
> > +{
> > +       union acpi_object *bt_pkg, *buffer = NULL;
> > +       struct btintel_sar_inc_pwr sar;
> > +       acpi_status status;
> > +       u8 revision;
> > +       int ret;
> > +
> > +       status = btintel_evaluate_acpi_method(hdev, "BRDS", &buffer, 2);
> > +       if (ACPI_FAILURE(status))
> > +               return -ENOENT;
> > +
> > +       bt_pkg = btintel_acpi_get_bt_pkg(buffer);
> > +
> > +       if (IS_ERR(bt_pkg)) {
> > +               ret = PTR_ERR(bt_pkg);
> > +               goto error;
> > +       }
> > +
> > +       revision = buffer->package.elements[0].integer.value;
> > +
> > +       if (revision > BTINTEL_SAR_INC_PWR) {
> > +               bt_dev_dbg(hdev, "BT_SAR: revision: 0x%2.2x not supported",
> revision);
> > +               ret = -EOPNOTSUPP;
> > +               goto error;
> > +       }
> > +
> > +       if (revision == BTINTEL_SAR_LEGACY && bt_pkg->package.count != 7) {
> > +               sar.revision = revision;
> > +               sar.bt_sar_bios = (u32)bt_pkg->package.elements[1].integer.value;
> > +               sar.br = (u8)bt_pkg->package.elements[2].integer.value;
> > +               sar.edr2 = (u8)bt_pkg->package.elements[3].integer.value;
> > +               sar.edr3 = (u8)bt_pkg->package.elements[4].integer.value;
> > +               sar.le = (u8)bt_pkg->package.elements[5].integer.value;
> > +               sar.le_2mhz = (u8)bt_pkg->package.elements[6].integer.value;
> > +               sar.le_lr  = (u8)bt_pkg->package.elements[7].integer.value;
> 
> Hmm, are you sure the check is correct? It is != 7 but then it goes
> ahead and access element[7]? Are these values aligned and are the acpi
> values guaranteed to be in the same endianness as the host? Even if
> they are, I'd say using the likes of le32_to_cpu is probably a good
> idea anyway.
> 
I will fix in V2 version of the patch
> > +
> > +       } else if (revision == BTINTEL_SAR_INC_PWR && bt_pkg-
> >package.count != 9) {
> > +               sar.revision = revision;
> > +               sar.bt_sar_bios = (u32)bt_pkg->package.elements[1].integer.value;
> > +               sar.inc_power_mode = (u32)bt_pkg-
> >package.elements[2].integer.value;
> > +               sar.sar_2400_chain_a = (u8)bt_pkg-
> >package.elements[3].integer.value;
> > +               sar.br = (u8)bt_pkg->package.elements[4].integer.value;
> > +               sar.edr2 = (u8)bt_pkg->package.elements[5].integer.value;
> > +               sar.edr3 = (u8)bt_pkg->package.elements[6].integer.value;
> > +               sar.le = (u8)bt_pkg->package.elements[7].integer.value;
> > +               sar.le_2mhz = (u8)bt_pkg->package.elements[8].integer.value;
> > +               sar.le_lr  = (u8)bt_pkg->package.elements[9].integer.value;
> 
> Ditto, != 9 then it attempts to access element[9]
Ack
> 
> > +       } else {
> > +               ret = -EINVAL;
> > +               goto error;
> > +       }
> > +
> > +       /* Apply only if it is enabled in BIOS */
> > +       if (sar.bt_sar_bios != 1) {
> > +               bt_dev_dbg(hdev, "Bluetooth SAR is not enabled");
> > +               ret = -EOPNOTSUPP;
> > +               goto error;
> > +       }
> > +
> > +       ret = btintel_sar_send_to_device(hdev, &sar, ver);
> > +error:
> > +       kfree(buffer);
> > +       return ret;
> > +}
> > +#endif /* CONFIG_ACPI */
> > +
> > +static int btintel_set_specific_absorption_rate(struct hci_dev *hdev,
> > +                                               struct intel_version_tlv *ver)
> > +{
> > +#ifdef CONFIG_ACPI
> > +       return btintel_acpi_set_sar(hdev, ver);
> > +#endif
> > +       return 0;
> > +}
> > +
> >  int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
> >                                  struct intel_version_tlv *ver)
> >  {
> > @@ -2876,6 +3186,9 @@ int btintel_bootloader_setup_tlv(struct hci_dev
> *hdev,
> >
> >         hci_dev_clear_flag(hdev, HCI_QUALITY_REPORT);
> >
> > +       /* Send sar values to controller */
> > +       btintel_set_specific_absorption_rate(hdev, ver);
> > +
> >         /* Set PPAG feature */
> >         btintel_set_ppag(hdev, ver);
> >
> > diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
> > index fa43eb137821..0b355cb49836 100644
> > --- a/drivers/bluetooth/btintel.h
> > +++ b/drivers/bluetooth/btintel.h
> > @@ -56,7 +56,10 @@ struct intel_tlv {
> >  #define BTINTEL_CNVI_BLAZARIW          0x901
> >  #define BTINTEL_CNVI_GAP               0x910
> >  #define BTINTEL_CNVI_BLAZARU           0x930
> > +#define BTINTEL_CNVI_GAP               0x910
> >
> > +/* CNVR */
> > +#define BTINTEL_CNVR_FMP2              0x910
> >  #define BTINTEL_IMG_BOOTLOADER         0x01    /* Bootloader image */
> >  #define BTINTEL_IMG_IML                        0x02    /* Intermediate image */
> >  #define BTINTEL_IMG_OP                 0x03    /* Operational image */
> > @@ -164,6 +167,38 @@ struct hci_ppag_enable_cmd {
> >  #define INTEL_TLV_DEBUG_EXCEPTION      0x02
> >  #define INTEL_TLV_TEST_EXCEPTION       0xDE
> >
> > +struct btintel_cp_ddc_write {
> > +       u8      len;
> > +       __le16  id;
> > +       u8      data[];
> > +} __packed;
> > +
> > +/* Bluetooth SAR feature (BRDS), Revision 0 */
> > +struct btintel_sar {
> > +       u8      revision;
> > +       u32     bt_sar_bios; /* Mode of SAR control to be used, 1:enabled in
> bios */
> > +       u8      br;
> > +       u8      edr2;
> > +       u8      edr3;
> > +       u8      le;
> > +       u8      le_2mhz;
> > +       u8      le_lr;
> > +} __packed;
> > +
> > +/* Bluetooth SAR feature (BRDS), Revision 1 */
> > +struct btintel_sar_inc_pwr {
> > +       u8      revision;
> > +       u32     bt_sar_bios;
> > +       u32     inc_power_mode;  /* Increased power mode */
> 
> For a packed multi-byte field I think it is always a good idea to have
> it as a specific byte order rather than expecting it to be the same as
> the host order.
It is not required to be a packed structure, This structure is populated from ACPI Table. I will remove the __packed. 
> 
> > +       u8      sar_2400_chain_a; /* Sar power restriction LB */
> > +       u8      br;
> > +       u8      edr2;
> > +       u8      edr3;
> > +       u8      le;
> > +       u8      le_2mhz;
> > +       u8      le_lr;
> > +} __packed;
> > +
> >  #define INTEL_HW_PLATFORM(cnvx_bt)     ((u8)(((cnvx_bt) & 0x0000ff00) >>
> 8))
> >  #define INTEL_HW_VARIANT(cnvx_bt)      ((u8)(((cnvx_bt) & 0x003f0000) >>
> 16))
> >  #define INTEL_CNVX_TOP_TYPE(cnvx_top)  ((cnvx_top) & 0x00000fff)
> > --
> > 2.40.1
> >
> >
> 
> 
> --
> Luiz Augusto von Dentz

Thanks,
Vijay Satija




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux