diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000..3a2c45c37553c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: CI + +on: [pull_request] + +jobs: + ci: + runs-on: ubuntu-latest + name: CI for Pull Request + steps: + - name: Checkout the source code + uses: actions/checkout@v3 + with: + path: src/src + + - name: CI + uses: tedd-an/bzcafe@main + with: + task: ci + base_folder: src + space: kernel + github_token: ${{ secrets.GITHUB_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} + diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000000000..3883d55a23267 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,43 @@ +name: Sync + +on: + schedule: + - cron: "*/30 * * * *" + +jobs: + sync_repo: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: master + + - name: Sync Repo + uses: tedd-an/bzcafe@main + with: + task: sync + upstream_repo: 'https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git' + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Cleanup PR + uses: tedd-an/bzcafe@main + with: + task: cleanup + github_token: ${{ secrets.ACTION_TOKEN }} + + sync_patchwork: + needs: sync_repo + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Sync Patchwork + uses: tedd-an/bzcafe@main + with: + task: patchwork + space: kernel + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + patchwork_user: ${{ secrets.PATCHWORK_USER }} + diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml new file mode 100644 index 0000000000000..c8e9c55c1afb4 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,bluetooth-common.yaml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,bluetooth-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Bluetooth Common Properties + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + firmware-name: + minItems: 1 + items: + - description: specify the name of nvm firmware to load + - description: specify the name of rampatch firmware to load + + qcom,local-bd-address-broken: + type: boolean + description: + boot firmware is incorrectly passing the address in big-endian order + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,qca2066-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca2066-bt.yaml new file mode 100644 index 0000000000000..d4f167c9b7e14 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca2066-bt.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,qca2066-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QCA2006 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,qca2066-bt + - qcom,qca6174-bt + + clocks: + items: + - description: External low-power 32.768 kHz clock input + + enable-gpios: + maxItems: 1 + +required: + - compatible + - clocks + - enable-gpios + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + serial { + bluetooth { + compatible = "qcom,qca6174-bt"; + clocks = <&divclk4>; + enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>; + firmware-name = "nvm_00440302.bin"; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,qca6390-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca6390-bt.yaml new file mode 100644 index 0000000000000..cffbc9e61cd6c --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca6390-bt.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,qca6390-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QCA6390 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,qca6390-bt + + vddaon-supply: + description: VDD_AON supply regulator handle + + vddbtcmx-supply: + description: VDD_BT_CMX supply regulator handle + + vddrfa0p8-supply: + description: VDD_RFA_0P8 supply regulator handle + + vddrfa1p2-supply: + description: VDD_RFA_1P2 supply regulator handle + + vddrfa1p7-supply: + description: VDD_RFA_1P7 supply regulator handle + + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + +required: + - compatible + - vddaon-supply + - vddbtcmx-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p7-supply + - vddrfacmn-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,qca6390-bt"; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,qca9377-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca9377-bt.yaml new file mode 100644 index 0000000000000..3fe9476c1d74c --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,qca9377-bt.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,qca9377-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QCA9377 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,qca9377-bt + + clocks: + items: + - description: External low-power 32.768 kHz clock input + + enable-gpios: + maxItems: 1 + + vddio-supply: + description: VDD_IO supply regulator handle + + vddxo-supply: + description: VDD_XO supply regulator handle + +required: + - compatible + - clocks + - enable-gpios + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + serial { + bluetooth { + compatible = "qcom,qca9377-bt"; + clocks = <&rk809 1>; + enable-gpios = <&gpio3 5 GPIO_ACTIVE_HIGH>; + max-speed = <2000000>; + pinctrl-names = "default"; + pinctrl-0 = <&bt_enable>; + vddio-supply = <&vcc_1v8>; + vddxo-supply = <&vcc3v3_sys>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3950-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3950-bt.yaml new file mode 100644 index 0000000000000..83382f3c90498 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3950-bt.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn3950-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCN3950/WCN3988 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,wcn3950-bt + - qcom,wcn3988-bt + + enable-gpios: + maxItems: 1 + + swctrl-gpios: + maxItems: 1 + description: gpio specifier is used to find status + of clock supply to SoC + + vddch0-supply: + description: VDD_CH0 supply regulator handle + + vddio-supply: + description: VDD_IO supply regulator handle + + vddrf-supply: + description: VDD_RF supply regulator handle + + vddxo-supply: + description: VDD_XO supply regulator handle + +required: + - compatible + - vddch0-supply + - vddio-supply + - vddrf-supply + - vddxo-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + serial { + bluetooth { + compatible = "qcom,wcn3950-bt"; + enable-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>; + max-speed = <3200000>; + vddch0-supply = <&pm4125_l22>; + vddio-supply = <&pm4125_l15>; + vddrf-supply = <&pm4125_l10>; + vddxo-supply = <&pm4125_l13>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3990-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3990-bt.yaml new file mode 100644 index 0000000000000..89ceb1f7def07 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn3990-bt.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn3990-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCN3990/WCN3991/WCN3998 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,wcn3990-bt + - qcom,wcn3991-bt + - qcom,wcn3998-bt + + clocks: + items: + - description: External low-power 32.768 kHz clock input + + vddch0-supply: + description: VDD_CH0 supply regulator handle + + vddch1-supply: + description: VDD_CH1 supply regulator handle + + vddio-supply: + description: VDD_IO supply regulator handle + + vddrf-supply: + description: VDD_RF supply regulator handle + + vddxo-supply: + description: VDD_XO supply regulator handle + +required: + - compatible + - vddch0-supply + - vddio-supply + - vddrf-supply + - vddxo-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,wcn3990-bt"; + firmware-name = "crnv21.bin"; + max-speed = <3200000>; + vddio-supply = <&vreg_s4a_1p8>; + vddch0-supply = <&vreg_l25a_3p3>; + vddch1-supply = <&vreg_l23a_3p3>; + vddrf-supply = <&vreg_l17a_1p3>; + vddxo-supply = <&vreg_l7a_1p8>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml new file mode 100644 index 0000000000000..8606a45ac9b97 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6750-bt.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn6750-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCN6750 Bluetooth + +maintainers: + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,wcn6750-bt + + enable-gpios: + maxItems: 1 + deprecated: true + + swctrl-gpios: + maxItems: 1 + description: gpio specifier is used to find status + of clock supply to SoC + deprecated: true + + vddaon-supply: + description: VDD_AON supply regulator handle + + vddasd-supply: + description: VDD_ASD supply regulator handle + deprecated: true + + vddbtcmx-supply: + description: VDD_BT_CMX supply regulator handle + + vddbtcxmx-supply: + description: VDD_BT_CXMX supply regulator handle + deprecated: true + + vddio-supply: + description: VDD_IO supply regulator handle + deprecated: true + + vddrfa0p8-supply: + description: VDD_RFA_0P8 supply regulator handle + + vddrfa1p2-supply: + description: VDD_RFA_1P2 supply regulator handle + + vddrfa1p7-supply: + description: VDD_RFA_1P7 supply regulator handle + + vddrfa2p2-supply: + description: VDD_RFA_2P2 supply regulator handle + + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + +required: + - compatible + - vddaon-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p7-supply + - vddrfacmn-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,wcn6750-bt"; + + firmware-name = "msnv11.bin"; + max-speed = <3200000>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p85>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml new file mode 100644 index 0000000000000..45630067d3c8e --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn6855-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCN6855 Bluetooth + +maintainers: + - Bartosz Golaszewski + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,wcn6855-bt + + enable-gpios: + maxItems: 1 + deprecated: true + + swctrl-gpios: + maxItems: 1 + description: gpio specifier is used to find status + of clock supply to SoC + deprecated: true + + vddaon-supply: + description: VDD_AON supply regulator handle + + vddbtcmx-supply: + description: VDD_BT_CMX supply regulator handle + + vddbtcxmx-supply: + description: VDD_BT_CXMX supply regulator handle + deprecated: true + + vddio-supply: + description: VDD_IO supply regulator handle + deprecated: true + + vddrfa0p8-supply: + description: VDD_RFA_0P8 supply regulator handle + + vddrfa1p2-supply: + description: VDD_RFA_1P2 supply regulator handle + + vddrfa1p7-supply: + description: VDD_RFA_1P7 supply regulator handle + deprecated: true + + vddrfa1p8-supply: + description: VDD_RFA_1P8 supply regulator handle + + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + + vddwlcx-supply: + description: VDD_WLCX supply regulator handle + + vddwlmx-supply: + description: VDD_WLMX supply regulator handle + +required: + - compatible + - vddaon-supply + - vddbtcmx-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p8-supply + - vddrfacmn-supply + - vddwlcx-supply + - vddwlmx-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,wcn6855-bt"; + + max-speed = <3000000>; + vddaon-supply = <&vreg_pmu_aon_0p8>; + vddbtcmx-supply = <&vreg_pmu_btcmx_0p8>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn_0p8>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p8>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml new file mode 100644 index 0000000000000..8108ef83e99b9 --- /dev/null +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn7850-bt.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/bluetooth/qcom,wcn7850-bt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm WCN7850 Bluetooth + +maintainers: + - Bartosz Golaszewski + - Balakrishna Godavarthi + - Rocky Liao + +properties: + compatible: + enum: + - qcom,wcn7850-bt + + enable-gpios: + maxItems: 1 + deprecated: true + + swctrl-gpios: + maxItems: 1 + description: gpio specifier is used to find status + of clock supply to SoC + deprecated: true + + vddaon-supply: + description: VDD_AON supply regulator handle + + vdddig-supply: + description: VDD_DIG supply regulator handle + deprecated: true + + vddio-supply: + description: VDD_IO supply regulator handle + deprecated: true + + vddrfa0p8-supply: + description: VDD_RFA_0P8 supply regulator handle + + vddrfa1p2-supply: + description: VDD_RFA_1P2 supply regulator handle + + vddrfa1p8-supply: + description: VDD_RFA_1P8 supply regulator handle + + vddrfa1p9-supply: + description: VDD_RFA_1P9 supply regulator handle + deprecated: true + + vddrfacmn-supply: + description: VDD_RFA_CMN supply regulator handle + + vddwlcx-supply: + description: VDD_WLCX supply regulator handle + + vddwlmx-supply: + description: VDD_WLMX supply regulator handle + +required: + - compatible + - vddrfacmn-supply + - vddaon-supply + - vddwlcx-supply + - vddwlmx-supply + - vddrfa0p8-supply + - vddrfa1p2-supply + - vddrfa1p8-supply + +allOf: + - $ref: bluetooth-controller.yaml# + - $ref: qcom,bluetooth-common.yaml + - $ref: /schemas/serial/serial-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + serial { + bluetooth { + compatible = "qcom,wcn7850-bt"; + + max-speed = <3200000>; + vddaon-supply = <&vreg_pmu_aon_0p59>; + vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>; + vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>; + vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>; + vddrfacmn-supply = <&vreg_pmu_rfa_cmn>; + vddwlcx-supply = <&vreg_pmu_wlcx_0p8>; + vddwlmx-supply = <&vreg_pmu_wlmx_0p85>; + }; + }; diff --git a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml b/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml deleted file mode 100644 index 6353a336f382e..0000000000000 --- a/Documentation/devicetree/bindings/net/bluetooth/qualcomm-bluetooth.yaml +++ /dev/null @@ -1,259 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/net/bluetooth/qualcomm-bluetooth.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Qualcomm Bluetooth Chips - -maintainers: - - Balakrishna Godavarthi - - Rocky Liao - -description: - This binding describes Qualcomm UART-attached bluetooth chips. - -properties: - compatible: - enum: - - qcom,qca2066-bt - - qcom,qca6174-bt - - qcom,qca9377-bt - - qcom,wcn3950-bt - - qcom,wcn3988-bt - - qcom,wcn3990-bt - - qcom,wcn3991-bt - - qcom,wcn3998-bt - - qcom,qca6390-bt - - qcom,wcn6750-bt - - qcom,wcn6855-bt - - qcom,wcn7850-bt - - enable-gpios: - maxItems: 1 - description: gpio specifier used to enable chip - - swctrl-gpios: - maxItems: 1 - description: gpio specifier is used to find status - of clock supply to SoC - - clocks: - maxItems: 1 - description: clock provided to the controller (SUSCLK_32KHZ) - - vddio-supply: - description: VDD_IO supply regulator handle - - vddxo-supply: - description: VDD_XO supply regulator handle - - vddrf-supply: - description: VDD_RF supply regulator handle - - vddch0-supply: - description: VDD_CH0 supply regulator handle - - vddch1-supply: - description: VDD_CH1 supply regulator handle - - vddaon-supply: - description: VDD_AON supply regulator handle - - vdddig-supply: - description: VDD_DIG supply regulator handle - - vddbtcmx-supply: - description: VDD_BT_CMX supply regulator handle - - vddbtcxmx-supply: - description: VDD_BT_CXMX supply regulator handle - - vddrfacmn-supply: - description: VDD_RFA_CMN supply regulator handle - - vddrfa0p8-supply: - description: VDD_RFA_0P8 supply regulator handle - - vddrfa1p7-supply: - description: VDD_RFA_1P7 supply regulator handle - - vddrfa1p8-supply: - description: VDD_RFA_1P8 supply regulator handle - - vddrfa1p2-supply: - description: VDD_RFA_1P2 supply regulator handle - - vddrfa1p9-supply: - description: VDD_RFA_1P9 supply regulator handle - - vddrfa2p2-supply: - description: VDD_RFA_2P2 supply regulator handle - - vddasd-supply: - description: VDD_ASD supply regulator handle - - vddwlcx-supply: - description: VDD_WLCX supply regulator handle - - vddwlmx-supply: - description: VDD_WLMX supply regulator handle - - max-speed: true - - firmware-name: - minItems: 1 - items: - - description: specify the name of nvm firmware to load - - description: specify the name of rampatch firmware to load - - local-bd-address: true - - qcom,local-bd-address-broken: - type: boolean - description: - boot firmware is incorrectly passing the address in big-endian order - -required: - - compatible - -additionalProperties: false - -allOf: - - $ref: bluetooth-controller.yaml# - - $ref: /schemas/serial/serial-peripheral-props.yaml# - - if: - properties: - compatible: - contains: - enum: - - qcom,qca2066-bt - - qcom,qca6174-bt - then: - required: - - enable-gpios - - clocks - - - if: - properties: - compatible: - contains: - enum: - - qcom,wcn3950-bt - - qcom,wcn3988-bt - - qcom,wcn3990-bt - - qcom,wcn3991-bt - - qcom,wcn3998-bt - then: - required: - - vddio-supply - - vddxo-supply - - vddrf-supply - - vddch0-supply - - - if: - properties: - compatible: - contains: - enum: - - qcom,wcn6750-bt - then: - required: - - vddaon-supply - - vddrfacmn-supply - - vddrfa0p8-supply - - vddrfa1p7-supply - - vddrfa1p2-supply - - if: - properties: - compatible: - contains: - enum: - - qcom,wcn6855-bt - then: - required: - - vddrfacmn-supply - - vddaon-supply - - vddwlcx-supply - - vddwlmx-supply - - vddbtcmx-supply - - vddrfa0p8-supply - - vddrfa1p2-supply - - vddrfa1p8-supply - - if: - properties: - compatible: - contains: - enum: - - qcom,wcn7850-bt - then: - required: - - vddrfacmn-supply - - vddaon-supply - - vddwlcx-supply - - vddwlmx-supply - - vddrfa0p8-supply - - vddrfa1p2-supply - - vddrfa1p8-supply - - if: - properties: - compatible: - contains: - enum: - - qcom,qca6390-bt - then: - required: - - vddrfacmn-supply - - vddaon-supply - - vddbtcmx-supply - - vddrfa0p8-supply - - vddrfa1p2-supply - - vddrfa1p7-supply - -examples: - - | - #include - serial { - - bluetooth { - compatible = "qcom,qca6174-bt"; - enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>; - clocks = <&divclk4>; - firmware-name = "nvm_00440302.bin"; - }; - }; - - | - serial { - - bluetooth { - compatible = "qcom,wcn3990-bt"; - vddio-supply = <&vreg_s4a_1p8>; - vddxo-supply = <&vreg_l7a_1p8>; - vddrf-supply = <&vreg_l17a_1p3>; - vddch0-supply = <&vreg_l25a_3p3>; - max-speed = <3200000>; - firmware-name = "crnv21.bin"; - }; - }; - - | - serial { - - bluetooth { - compatible = "qcom,wcn6750-bt"; - pinctrl-names = "default"; - pinctrl-0 = <&bt_en_default>; - enable-gpios = <&tlmm 85 GPIO_ACTIVE_HIGH>; - swctrl-gpios = <&tlmm 86 GPIO_ACTIVE_HIGH>; - vddio-supply = <&vreg_l19b_1p8>; - vddaon-supply = <&vreg_s7b_0p9>; - vddbtcxmx-supply = <&vreg_s7b_0p9>; - vddrfacmn-supply = <&vreg_s7b_0p9>; - vddrfa0p8-supply = <&vreg_s7b_0p9>; - vddrfa1p7-supply = <&vreg_s1b_1p8>; - vddrfa1p2-supply = <&vreg_s8b_1p2>; - vddrfa2p2-supply = <&vreg_s1c_2p2>; - vddasd-supply = <&vreg_l11c_2p8>; - max-speed = <3200000>; - firmware-name = "msnv11.bin"; - }; - }; diff --git a/MAINTAINERS b/MAINTAINERS index 4b2603ed45f4a..4c01e0eeba7de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21074,6 +21074,7 @@ QUALCOMM BLUETOOTH DRIVER M: Bartosz Golaszewski L: linux-arm-msm@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/net/bluetooth/qcom,* F: drivers/bluetooth/btqca.[ch] F: drivers/bluetooth/btqcomsmd.c F: drivers/bluetooth/hci_qca.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index c5d45cf91f882..ccbd2e13977e9 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -493,6 +493,13 @@ config BT_NXPUART select BT_HCIUART_H4 select CRC32 select CRC8 + select CRYPTO + select CRYPTO_ECDSA + select CRYPTO_ECDH + select CRYPTO_AES + select CRYPTO_GCM + select CRYPTO_SHA256 + select CRYPTO_HMAC help NXP is serial driver required for NXP Bluetooth devices with UART interface. diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index fba3ab6d30a5a..e986e5af51ae1 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -1472,7 +1472,6 @@ static void btmtksdio_remove(struct sdio_func *func) hci_free_dev(hdev); } -#ifdef CONFIG_PM static int btmtksdio_runtime_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); @@ -1542,18 +1541,13 @@ static const struct dev_pm_ops btmtksdio_pm_ops = { RUNTIME_PM_OPS(btmtksdio_runtime_suspend, btmtksdio_runtime_resume, NULL) }; -#define BTMTKSDIO_PM_OPS (&btmtksdio_pm_ops) -#else /* CONFIG_PM */ -#define BTMTKSDIO_PM_OPS NULL -#endif /* CONFIG_PM */ - static struct sdio_driver btmtksdio_driver = { .name = "btmtksdio", .probe = btmtksdio_probe, .remove = btmtksdio_remove, .id_table = btmtksdio_table, .drv = { - .pm = BTMTKSDIO_PM_OPS, + .pm = pm_ptr(&btmtksdio_pm_ops), } }; diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 3b1e9224e9657..115f727c2572b 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -15,12 +15,25 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include @@ -104,6 +117,8 @@ #define PS_STATE_SLEEP 1 /* NXP Vendor Commands. Refer user manual UM11628 on nxp.com */ +/* Get FW version */ +#define HCI_NXP_GET_FW_VERSION 0xfc0f /* Set custom BD Address */ #define HCI_NXP_SET_BD_ADDR 0xfc22 /* Set Auto-Sleep mode */ @@ -116,6 +131,8 @@ #define HCI_NXP_IND_RESET 0xfcfc /* Bluetooth vendor command: Trigger FW dump */ #define HCI_NXP_TRIGGER_DUMP 0xfe91 +/* Bluetooth vendor command: Secure Host Interface */ +#define HCI_NXP_SHI_ENCRYPT 0xfe9c /* Bluetooth Power State : Vendor cmd params */ #define BT_PS_ENABLE 0x02 @@ -134,6 +151,16 @@ #define BT_CTRL_WAKEUP_METHOD_EXT_BREAK 0x04 #define BT_CTRL_WAKEUP_METHOD_RTS 0x05 +/* FW Metadata */ +#define FW_METADATA_TLV_UUID 0x40 +#define FW_METADATA_TLV_ECDSA_KEY 0x50 +#define FW_METADATA_FLAG_BT 0x02 + +#define NXP_FW_UUID_SIZE 16 +#define NXP_FW_ECDH_PUBKEY_SIZE 64 +#define NXP_FW_ECDSA_PUBKEY_SIZE 65 +#define NXP_MAX_ENCRYPT_CMD_LEN 256 + struct ps_data { u8 target_ps_mode; /* ps mode to be set */ u8 cur_psmode; /* current ps_mode */ @@ -180,6 +207,33 @@ enum bootloader_param_change { changed }; +struct nxp_tls_traffic_keys { + u8 h2d_secret[SHA256_DIGEST_SIZE]; + u8 d2h_secret[SHA256_DIGEST_SIZE]; + /* These keys below should be used for message encryption/decryption */ + u8 h2d_iv[GCM_AES_IV_SIZE]; + u8 h2d_key[AES_KEYSIZE_128]; + u8 d2h_iv[GCM_AES_IV_SIZE]; + u8 d2h_key[AES_KEYSIZE_128]; +}; + +struct btnxpuart_crypto { + struct crypto_shash *tls_handshake_hash_tfm; + struct shash_desc *tls_handshake_hash_desc; + struct crypto_kpp *kpp; + u8 ecdh_public[NXP_FW_ECDH_PUBKEY_SIZE]; /* ECDH public key, Key negotiation */ + u8 ecdsa_public[NXP_FW_ECDSA_PUBKEY_SIZE]; /* ECDSA public key, Authentication*/ + u8 fw_uuid[NXP_FW_UUID_SIZE]; + u8 handshake_h2_hash[SHA256_DIGEST_SIZE]; + u8 handshake_secret[SHA256_DIGEST_SIZE]; + u8 master_secret[SHA256_DIGEST_SIZE]; + u64 enc_seq_no; + u64 dec_seq_no; + struct completion completion; + int decrypt_result; + struct nxp_tls_traffic_keys keys; +}; + struct btnxpuart_dev { struct hci_dev *hdev; struct serdev_device *serdev; @@ -213,6 +267,8 @@ struct btnxpuart_dev { struct btnxpuart_data *nxp_data; struct reset_control *pdn; struct hci_uart hu; + bool secure_interface; + struct btnxpuart_crypto crypto; }; #define NXP_V1_FW_REQ_PKT 0xa5 @@ -362,6 +418,152 @@ union nxp_set_bd_addr_payload { u8 buf[8]; }; +/* Secure Host Interface */ +#define NXP_TLS_MAGIC 0x43b826f3 +#define NXP_TLS_VERSION 1 + +#define NXP_TLS_ECDH_PUBLIC_KEY_SIZE 64 +#define NXP_DEVICE_UUID_LEN 16 +#define NXP_ENC_AUTH_TAG_SIZE 16 + +#define NXP_TLS_LABEL(str) str, strlen(str) +#define NXP_TLS_DEVICE_HS_TS_LABEL NXP_TLS_LABEL("D HS TS") +#define NXP_TLS_KEYING_IV_LABEL NXP_TLS_LABEL("iv") +#define NXP_TLS_KEYING_KEY_LABEL NXP_TLS_LABEL("key") +#define NXP_TLS_FINISHED_LABEL NXP_TLS_LABEL("finished") +#define NXP_TLS_DERIVED_LABEL NXP_TLS_LABEL("derived") +#define NXP_TLS_HOST_HS_TS_LABEL NXP_TLS_LABEL("H HS TS") +#define NXP_TLS_D_AP_TS_LABEL NXP_TLS_LABEL("D AP TS") +#define NXP_TLS_H_AP_TS_LABEL NXP_TLS_LABEL("H AP TS") + +enum nxp_tls_signature_algorithm { + NXP_TLS_ECDSA_SECP256R1_SHA256 = 0x0403, +}; + +enum nxp_tls_key_exchange_type { + NXP_TLS_ECDHE_SECP256R1 = 0x0017, +}; + +enum nxp_tls_cipher_suite { + NXP_TLS_AES_128_GCM_SHA256 = 0x1301, +}; + +enum nxp_tls_message_id { + NXP_TLS_HOST_HELLO = 1, + NXP_TLS_DEVICE_HELLO = 2, + NXP_TLS_HOST_FINISHED = 3, +}; + +struct nxp_tls_message_hdr { + __le32 magic; + __le16 len; + u8 message_id; + u8 protocol_version; +}; + +struct nxp_tls_host_hello { + struct nxp_tls_message_hdr hdr; + __le16 sig_alg; + __le16 key_exchange_type; + __le16 cipher_suite; + __le16 reserved; + u8 random[32]; + u8 pubkey[NXP_TLS_ECDH_PUBLIC_KEY_SIZE]; /* ECDHE */ +}; + +union nxp_tls_host_hello_payload { + struct { + u8 msg_type; + struct nxp_tls_host_hello host_hello; + } __packed; + u8 buf[113]; +}; + +struct nxp_tls_device_info { + __le16 chip_id; + __le16 device_flags; + u8 reserved[4]; + u8 uuid[NXP_DEVICE_UUID_LEN]; +}; + +struct nxp_tls_signature { + u8 sig[64]; /* P-256 ECDSA signature, two points */ +}; + +struct nxp_tls_finished { + u8 verify_data[32]; +}; + +struct nxp_tls_device_hello { + struct nxp_tls_message_hdr hdr; + __le32 reserved; + u8 random[32]; + u8 pubkey[NXP_TLS_ECDH_PUBLIC_KEY_SIZE]; + /* Encrypted portion */ + struct { + struct nxp_tls_device_info device_info; + struct nxp_tls_signature device_handshake_sig; /* TLS Certificate Verify */ + struct nxp_tls_finished device_finished; + } enc; + u8 auth_tag[NXP_ENC_AUTH_TAG_SIZE]; /* Auth tag for the encrypted portion */ +}; + +struct nxp_tls_data_add { + u8 version; /* NXP_TLS_VERSION */ + u8 reserved[5]; /* zeroes */ + __le16 len; +}; + +struct nxp_tls_host_finished { + struct nxp_tls_message_hdr hdr; + __le32 reserved; + /* Encrypted portion */ + struct { + struct nxp_tls_signature reserved2; + struct nxp_tls_finished host_finished; + } enc; + u8 auth_tag[NXP_ENC_AUTH_TAG_SIZE]; /* Auth tag for the encrypted portion */ +}; + +union nxp_tls_host_finished_payload { + struct { + u8 msg_type; + struct nxp_tls_host_finished host_finished; + } __packed; + u8 buf[125]; +}; + +#define DEVICE_HELLO_SIG_CUTOFF_POS \ + offsetof(struct nxp_tls_device_hello, enc) + +#define DEVICE_HELLO_FINISHED_ENC_CUTOFF_POS \ + (offsetof(struct nxp_tls_device_hello, enc.device_finished) - \ + DEVICE_HELLO_SIG_CUTOFF_POS) + + +#define HOST_FINISHED_CUTOFF_POS \ + offsetof(struct nxp_tls_host_finished, enc.host_finished) + +/* FW Meta Data */ +struct fw_metadata_hdr { + __le32 cmd; + __le32 addr; + __le32 len; + __le32 crc; +}; + +struct fw_metadata_tail { + __le32 len; + u8 magic[8]; + __le32 crc; +}; + +struct fw_metadata_tlv { + __le16 id; + __le16 flag; + __le32 len; +}; + static u8 crc8_table[CRC8_TABLE_SIZE]; /* Default configurations */ @@ -1190,6 +1392,85 @@ static void nxp_handle_fw_download_error(struct hci_dev *hdev, struct v3_data_re } } +static u32 nxp_process_fw_metadata_tlv(struct hci_dev *hdev, char **payload) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct fw_metadata_tlv *tlv = (struct fw_metadata_tlv *)(*payload); + u32 ret = sizeof(*tlv) + le32_to_cpu(tlv->len); + + /* Process only BT specific metadata TLVs */ + if (!(le16_to_cpu(tlv->flag) & FW_METADATA_FLAG_BT)) + goto align_and_return; + + switch (le16_to_cpu(tlv->id)) { + case FW_METADATA_TLV_UUID: + if (le32_to_cpu(tlv->len) == NXP_FW_UUID_SIZE) + memcpy(nxpdev->crypto.fw_uuid, + *payload + sizeof(*tlv), NXP_FW_UUID_SIZE); + break; + case FW_METADATA_TLV_ECDSA_KEY: + if (le32_to_cpu(tlv->len) == NXP_FW_ECDSA_PUBKEY_SIZE) + memcpy(nxpdev->crypto.ecdsa_public, + *payload + sizeof(*tlv), NXP_FW_ECDSA_PUBKEY_SIZE); + break; + default: + bt_dev_err(hdev, "Unknown metadata TLV ID: 0x%x", le16_to_cpu(tlv->id)); + break; + } + +align_and_return: + /* Align the pointer to 4 byte structure alignment */ + ret = round_up(ret, 4); + *payload += ret; + + return ret; +} + +static void nxp_process_fw_meta_data(struct hci_dev *hdev, const struct firmware *fw) +{ + const char *metamagc = "metamagc"; + struct fw_metadata_hdr *hdr = NULL; + struct fw_metadata_tail *tail; + u32 hdr_crc = 0; + u32 payload_crc = 0; + char *payload; + u32 payload_len = 0; + + /* FW metadata should contain at least header and tail */ + if (fw->size < (sizeof(*hdr) + sizeof(*tail))) + return; + + tail = (struct fw_metadata_tail *)&fw->data[fw->size - sizeof(*tail)]; + + /* If tail doesn't contain the string "metamagc", this is invalid FW metadata */ + if (memcmp(metamagc, tail->magic, strlen(metamagc))) + return; + + hdr = (struct fw_metadata_hdr *)&fw->data[fw->size - + sizeof(*tail) - + le32_to_cpu(tail->len)]; + + /* If metadata header isn't cmd24, this is invalid FW metadata */ + if (le32_to_cpu(hdr->cmd) != 24) + return; + + /* If header CRC doesn't match, this is invalid FW metadata */ + hdr_crc = crc32_be(0, (u8 *)hdr, offsetof(struct fw_metadata_hdr, crc)); + if (hdr_crc != le32_to_cpu(hdr->crc)) + return; + + /* If payload CRC doesn't match, this is invalid FW metadata */ + payload = (u8 *)hdr + sizeof(*hdr); + payload_crc = crc32_be(0, payload, le32_to_cpu(hdr->len) - 4); + if (payload_crc != le32_to_cpu(tail->crc)) + return; + + payload_len = le32_to_cpu(hdr->len) - sizeof(*tail); + + while (payload_len > sizeof(struct fw_metadata_tlv)) + payload_len -= nxp_process_fw_metadata_tlv(hdev, &payload); +} + static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) { struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); @@ -1248,14 +1529,6 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) goto free_skb; } - if (req->len == 0) { - bt_dev_info(hdev, "FW Download Complete: %zu bytes", - nxpdev->fw->size); - clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); - wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q); - goto free_skb; - } - offset = __le32_to_cpu(req->offset); if (offset < nxpdev->fw_v3_offset_correction) { /* This scenario should ideally never occur. But if it ever does, @@ -1267,6 +1540,17 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) } nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction; + + if (req->len == 0) { + if (nxpdev->fw_dnld_v3_offset < nxpdev->fw->size) + nxp_process_fw_meta_data(hdev, nxpdev->fw); + bt_dev_info(hdev, "FW Download Complete: %u bytes.", + req->offset - nxpdev->fw_v3_offset_correction); + clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q); + goto free_skb; + } + serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + nxpdev->fw_dnld_v3_offset, len); @@ -1437,6 +1721,1131 @@ static int nxp_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) return 0; } +static void nxp_handle_chip_specific_features(struct hci_dev *hdev, u8 *version) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + if (!version || strlen(version) == 0) + return; + + if (!strncmp(version, "aw693n-V1", strlen("aw693n-V1"))) + nxpdev->secure_interface = true; +} + +static void nxp_get_fw_version(struct hci_dev *hdev) +{ + struct sk_buff *skb; + u8 version[100] = {0}; + u8 cmd = 0; + u8 *status; + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_GET_FW_VERSION, 1, &cmd, true); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Failed to get firmware version (%ld)", + PTR_ERR(skb)); + return; + } + + status = skb_pull_data(skb, 1); + if (status) { + if (*status) { + bt_dev_err(hdev, "Error get FW version: %d", *status); + } else if (skb->len < 10 || skb->len >= 100) { + bt_dev_err(hdev, "Invalid FW version"); + } else { + memcpy(version, skb->data, skb->len); + bt_dev_info(hdev, "FW Version: %s", version); + nxp_handle_chip_specific_features(hdev, version); + } + } + + kfree_skb(skb); +} + +/* Secure Interface */ +static int nxp_get_pub_key(struct hci_dev *hdev, + const struct nxp_tls_device_info *device_info, + u8 ecdsa_pub_key[NXP_FW_ECDSA_PUBKEY_SIZE]) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + const char *fw_name; + + if (ecdsa_pub_key[0] == 0x04) + return 0; + + fw_name = nxp_get_fw_name_from_chipid(hdev, + le16_to_cpu(device_info->chip_id), + le16_to_cpu(device_info->device_flags)); + if (nxp_request_firmware(hdev, fw_name, NULL)) + return -ENOENT; + + nxp_process_fw_meta_data(hdev, nxpdev->fw); + release_firmware(nxpdev->fw); + memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name)); + + if (memcmp(nxpdev->crypto.fw_uuid, device_info->uuid, 16) || + nxpdev->crypto.ecdsa_public[0] != 0x04) { + bt_dev_err(hdev, + "UUID check failed while trying to read ECDSA public key from FW."); + return -EBADF; + } + + memcpy(ecdsa_pub_key, nxpdev->crypto.ecdsa_public, 65); + + return 0; +} + +static int nxp_generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64]) +{ + DECLARE_CRYPTO_WAIT(result); + struct kpp_request *req; + u8 *tmp; + struct scatterlist dst; + int err; + + tmp = kzalloc(64, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + req = kpp_request_alloc(tfm, GFP_KERNEL); + if (!req) { + err = -ENOMEM; + goto free_tmp; + } + + sg_init_one(&dst, tmp, 64); + kpp_request_set_input(req, NULL, 0); + kpp_request_set_output(req, &dst, 64); + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &result); + + err = crypto_kpp_generate_public_key(req); + err = crypto_wait_req(err, &result); + if (err < 0) + goto free_all; + + memcpy(public_key, tmp, 64); + +free_all: + kpp_request_free(req); +free_tmp: + kfree(tmp); + return err; +} + +static inline void nxp_tls_hdr_init(struct nxp_tls_message_hdr *hdr, size_t len, + enum nxp_tls_message_id id) +{ + hdr->magic = cpu_to_le32(NXP_TLS_MAGIC); + hdr->len = cpu_to_le16((u16)len); + hdr->message_id = (u8)id; + hdr->protocol_version = NXP_TLS_VERSION; +} + +static struct sk_buff *nxp_host_do_hello(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + union nxp_tls_host_hello_payload tls_hello; + struct nxp_tls_host_hello *host_hello = &tls_hello.host_hello; + struct ecdh p = {0}; + u8 *buf = NULL; + unsigned int buf_len; + struct sk_buff *skb; + int ret; + + nxp_tls_hdr_init(&host_hello->hdr, sizeof(*host_hello), NXP_TLS_HOST_HELLO); + + host_hello->sig_alg = cpu_to_le16(NXP_TLS_ECDSA_SECP256R1_SHA256); + host_hello->key_exchange_type = cpu_to_le16(NXP_TLS_ECDHE_SECP256R1); + host_hello->cipher_suite = cpu_to_le16(NXP_TLS_AES_128_GCM_SHA256); + + get_random_bytes(host_hello->random, sizeof(host_hello->random)); + + /* Generate random private key */ + p.key_size = 32; + p.key = kzalloc(p.key_size, GFP_KERNEL); + if (!p.key) + return ERR_PTR(-ENOMEM); + + get_random_bytes(p.key, p.key_size); + + buf_len = crypto_ecdh_key_len(&p); + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto free_key; + } + + ret = crypto_ecdh_encode_key(buf, buf_len, &p); + if (ret) { + bt_dev_err(hdev, "crypto_ecdh_encode_key() failed"); + goto free_buf; + } + + ret = crypto_kpp_set_secret(nxpdev->crypto.kpp, buf, buf_len); + if (ret) { + bt_dev_err(hdev, "crypto_kpp_set_secret() failed"); + goto free_buf; + } + + ret = nxp_generate_ecdh_public_key(nxpdev->crypto.kpp, host_hello->pubkey); + if (ret) { + bt_dev_err(hdev, "Failed to generate ECDH public key: %d", ret); + goto free_buf; + } + + ret = crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)host_hello, sizeof(*host_hello)); + if (ret) { + bt_dev_err(hdev, "Failed to update handshake hash: %d", ret); + goto free_buf; + } + + tls_hello.msg_type = 0; + + skb = __hci_cmd_sync(hdev, HCI_NXP_SHI_ENCRYPT, sizeof(tls_hello), + tls_hello.buf, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Host Hello command failed: %ld", PTR_ERR(skb)); + ret = PTR_ERR(skb); + } + +free_buf: + kfree(buf); +free_key: + memset(p.key, 0, p.key_size); + kfree(p.key); + if (ret) + return ERR_PTR(ret); + else + return skb; +} + +static int nxp_crypto_shash_final(struct shash_desc *desc, u8 *out) +{ + struct shash_desc *desc_tmp = kzalloc(sizeof(struct shash_desc) + + crypto_shash_descsize(desc->tfm), + GFP_KERNEL); + + if (!desc_tmp) + return -ENOMEM; + + crypto_shash_export(desc, desc_tmp); + crypto_shash_final(desc, out); + crypto_shash_import(desc, desc_tmp); + kfree(desc_tmp); + + return 0; +} + +static int nxp_compute_shared_secret(struct crypto_kpp *tfm, const u8 public_key[64], u8 secret[32]) +{ + DECLARE_CRYPTO_WAIT(result); + struct kpp_request *req; + struct scatterlist src, dst; + int err; + + req = kpp_request_alloc(tfm, GFP_KERNEL); + if (!req) { + pr_err("Failed to allocate memory for KPP request\n"); + return -ENOMEM; + } + + sg_init_one(&src, public_key, 64); + sg_init_one(&dst, secret, 32); + kpp_request_set_input(req, &src, 64); + kpp_request_set_output(req, &dst, 32); + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &result); + err = crypto_kpp_compute_shared_secret(req); + err = crypto_wait_req(err, &result); + if (err < 0) { + pr_err("alg: ecdh: compute shared secret failed. err %d\n", err); + goto free_all; + } + +free_all: + kpp_request_free(req); + return err; +} + +static int nxp_hkdf_sha256_extract(const void *salt, size_t salt_len, + const void *ikm, size_t ikm_len, + u8 result[SHA256_DIGEST_SIZE]) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + u8 zeroes[SHA256_DIGEST_SIZE] = {0}; + int ret = 0; + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + desc->tfm = tfm; + + /* RFC 5869: If salt is empty, use HashLen zero octets */ + if (salt_len == 0) + ret = crypto_shash_setkey(tfm, zeroes, SHA256_DIGEST_SIZE); + else + ret = crypto_shash_setkey(tfm, salt, salt_len); + + if (ret) + goto cleanup; + + ret = crypto_shash_init(desc); + if (ret) + goto cleanup; + + ret = crypto_shash_update(desc, ikm, ikm_len); + if (ret) + goto cleanup; + + ret = crypto_shash_final(desc, result); + +cleanup: + kfree(desc); + crypto_free_shash(tfm); + return ret; +} + +static int nxp_hkdf_expand_label(const u8 secret[SHA256_DIGEST_SIZE], + const char *label, size_t label_size, + u8 *context, size_t context_size, + void *output, size_t output_size) +{ + struct crypto_shash *tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + struct shash_desc *desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), + GFP_KERNEL); + u8 hmac_out[SHA256_DIGEST_SIZE]; + u16 length = output_size; + u8 one = 0x01; + + if (IS_ERR(tfm)) { + pr_err("Failed to alloc shash for HMAC\n"); + return -ENOMEM; + } + + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + crypto_shash_setkey(tfm, secret, SHA256_DIGEST_SIZE); + desc->tfm = tfm; + + crypto_shash_init(desc); + crypto_shash_update(desc, (u8 *)&length, sizeof(length)); + crypto_shash_update(desc, label, label_size); + + if (context && context_size > 0) + crypto_shash_update(desc, context, context_size); + + /* RFC 5869: HKDF-Expand counter starts at 0x01 */ + crypto_shash_update(desc, &one, sizeof(one)); + crypto_shash_final(desc, hmac_out); + + memcpy(output, hmac_out, output_size); + + kfree(desc); + crypto_free_shash(tfm); + return 0; +} + +static int nxp_hkdf_derive_secret(u8 secret[32], const char *label, size_t label_size, + u8 context[SHA256_DIGEST_SIZE], + u8 output[SHA256_DIGEST_SIZE]) +{ + return nxp_hkdf_expand_label(secret, label, label_size, context, SHA256_DIGEST_SIZE, + output, SHA256_DIGEST_SIZE); +} + +/* + * The digital signature is computed over the concatenation of: + * - A string that consists of octet 32 (0x20) repeated 64 times + * - The context string + * - A single 0 byte which serves as the separator + * - The content to be signed + */ +static int nxp_handshake_sig_hash(const u8 transcript_hash[SHA256_DIGEST_SIZE], + const char *context, size_t context_len, + u8 output_hash[SHA256_DIGEST_SIZE]) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + const u8 zero = 0; + + tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + desc->tfm = tfm; + + memset(output_hash, 0x20, SHA256_DIGEST_SIZE); + + crypto_shash_init(desc); + /* 2x hash size = block size of 0x20 */ + crypto_shash_update(desc, output_hash, SHA256_DIGEST_SIZE); + crypto_shash_update(desc, output_hash, SHA256_DIGEST_SIZE); + + crypto_shash_update(desc, context, context_len); + crypto_shash_update(desc, &zero, sizeof(zero)); + + crypto_shash_update(desc, transcript_hash, SHA256_DIGEST_SIZE); + crypto_shash_final(desc, output_hash); + + kfree(desc); + crypto_free_shash(tfm); + return 0; +} + + +static void nxp_aead_complete(void *req, int err) +{ + struct btnxpuart_crypto *crypto = req; + + crypto->decrypt_result = err; + complete(&crypto->completion); +} + +static int nxp_aes_gcm_decrypt(struct hci_dev *hdev, void *buf, size_t size, + u8 auth_tag[16], u8 key[AES_KEYSIZE_128], + u8 iv[GCM_AES_IV_SIZE]) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct crypto_aead *tfm; + struct aead_request *req; + struct scatterlist src, dst; + struct nxp_tls_data_add aad = { + .version = NXP_TLS_VERSION, + .len = cpu_to_le16((u16)size) + }; + u8 *ciphertext; + u8 *plaintext; + int ret = 0; + + ciphertext = kzalloc(sizeof(aad) + size + NXP_ENC_AUTH_TAG_SIZE, + GFP_KERNEL); + if (!ciphertext) + return -ENOMEM; + + plaintext = kzalloc(size + NXP_ENC_AUTH_TAG_SIZE, GFP_KERNEL); + if (!plaintext) { + ret = -ENOMEM; + goto free_ciphertext; + } + + memcpy(ciphertext, &aad, sizeof(aad)); + memcpy(ciphertext + sizeof(aad), buf, size); + memcpy(ciphertext + sizeof(aad) + size, auth_tag, NXP_ENC_AUTH_TAG_SIZE); + + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + goto free_plaintext; + } + + crypto_aead_setkey(tfm, key, AES_KEYSIZE_128); + crypto_aead_setauthsize(tfm, NXP_ENC_AUTH_TAG_SIZE); + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto free_tfm; + } + + sg_init_one(&src, ciphertext, sizeof(aad) + size + NXP_ENC_AUTH_TAG_SIZE); + sg_init_one(&dst, plaintext, size + NXP_ENC_AUTH_TAG_SIZE); + init_completion(&nxpdev->crypto.completion); + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + nxp_aead_complete, &nxpdev->crypto); + aead_request_set_crypt(req, &src, &dst, size + NXP_ENC_AUTH_TAG_SIZE, iv); + aead_request_set_ad(req, sizeof(aad)); + + ret = crypto_aead_decrypt(req); + if (ret == -EINPROGRESS || ret == -EBUSY) { + wait_for_completion(&nxpdev->crypto.completion); + ret = nxpdev->crypto.decrypt_result; + } + if (!ret) + memcpy(buf, plaintext + sizeof(aad), size); + + aead_request_free(req); +free_tfm: + crypto_free_aead(tfm); +free_plaintext: + kfree(plaintext); +free_ciphertext: + kfree(ciphertext); + return ret; +} + +static int nxp_aes_gcm_encrypt(struct hci_dev *hdev, void *buf, size_t size, u8 auth_tag[16], + u8 key[AES_KEYSIZE_128], u8 iv[GCM_AES_IV_SIZE]) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct crypto_aead *tfm; + struct aead_request *req; + struct scatterlist src, dst; + struct nxp_tls_data_add aad = { + .version = NXP_TLS_VERSION, + .len = cpu_to_le16((u16)size) + }; + u8 *ciphertext; + u8 *plaintext; + int ret = 0; + + ciphertext = kzalloc(sizeof(aad) + size + NXP_ENC_AUTH_TAG_SIZE, + GFP_KERNEL); + if (!ciphertext) + return -ENOMEM; + + plaintext = kzalloc(size + NXP_ENC_AUTH_TAG_SIZE, GFP_KERNEL); + if (!plaintext) { + ret = -ENOMEM; + goto free_ciphertext; + } + + memcpy(plaintext, &aad, sizeof(aad)); + memcpy(plaintext + sizeof(aad), buf, size); + + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + goto free_plaintext; + } + + crypto_aead_setkey(tfm, key, AES_KEYSIZE_128); + crypto_aead_setauthsize(tfm, NXP_ENC_AUTH_TAG_SIZE); + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto free_tfm; + } + + sg_init_one(&src, plaintext, size + NXP_ENC_AUTH_TAG_SIZE); + sg_init_one(&dst, ciphertext, sizeof(aad) + size + NXP_ENC_AUTH_TAG_SIZE); + init_completion(&nxpdev->crypto.completion); + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + nxp_aead_complete, &nxpdev->crypto); + aead_request_set_crypt(req, &src, &dst, size, iv); + aead_request_set_ad(req, sizeof(aad)); + + ret = crypto_aead_encrypt(req); + if (ret == -EINPROGRESS || ret == -EBUSY) { + wait_for_completion(&nxpdev->crypto.completion); + ret = nxpdev->crypto.decrypt_result; + } + if (!ret) { + memcpy(buf, ciphertext + sizeof(aad), size); + memcpy(auth_tag, ciphertext + size + sizeof(aad), NXP_ENC_AUTH_TAG_SIZE); + } + + aead_request_free(req); +free_tfm: + crypto_free_aead(tfm); +free_plaintext: + kfree(plaintext); +free_ciphertext: + kfree(ciphertext); + return ret; +} + +static int nxp_handshake_decrypt_verify(struct hci_dev *hdev, void *buf, size_t size, + u8 auth_tag[16], + u8 traffic_secret[SHA256_DIGEST_SIZE]) +{ + u8 key[AES_KEYSIZE_128] = {0}; + u8 iv[GCM_AES_IV_SIZE] = {0}; + + nxp_hkdf_expand_label(traffic_secret, NXP_TLS_KEYING_KEY_LABEL, NULL, 0, + key, AES_KEYSIZE_128); + nxp_hkdf_expand_label(traffic_secret, NXP_TLS_KEYING_IV_LABEL, NULL, 0, + iv, GCM_AES_IV_SIZE); + + return nxp_aes_gcm_decrypt(hdev, buf, size, auth_tag, key, iv); +} + +static int nxp_handshake_encrypt(struct hci_dev *hdev, void *buf, + size_t size, u8 auth_tag[16], + u8 traffic_secret[SHA256_DIGEST_SIZE]) +{ + u8 key[AES_KEYSIZE_128] = {0}; + u8 iv[GCM_AES_IV_SIZE] = {0}; + + nxp_hkdf_expand_label(traffic_secret, NXP_TLS_KEYING_KEY_LABEL, NULL, + 0, key, AES_KEYSIZE_128); + nxp_hkdf_expand_label(traffic_secret, NXP_TLS_KEYING_IV_LABEL, NULL, + 0, iv, GCM_AES_IV_SIZE); + + return nxp_aes_gcm_encrypt(hdev, buf, size, auth_tag, key, iv); +} + +static int nxp_p256_ecdsa_verify(const u8 sig[64], const u8 pub[65], + const u8 *hash, size_t hash_len) +{ + struct public_key_signature sig_info = {0}; + struct public_key pub_key = {0}; + int ret; + + sig_info.s = (u8 *)sig; + sig_info.s_size = 64; + sig_info.digest = (u8 *)hash; + sig_info.digest_size = hash_len; + sig_info.pkey_algo = "ecdsa"; + sig_info.hash_algo = "sha256"; + sig_info.encoding = "p1363"; + + pub_key.key = (void *)pub; + pub_key.keylen = 65; + pub_key.algo = OID_id_ecPublicKey; + pub_key.key_is_private = false; + pub_key.pkey_algo = "ecdsa-nist-p256"; + pub_key.id_type = NULL; + + ret = public_key_verify_signature(&pub_key, &sig_info); + if (ret) + pr_err("ECDSA signature verification failed: %d\n", ret); + + return ret; +} + +static int nxp_device_hello_sig_verify(struct hci_dev *hdev, struct nxp_tls_device_hello *msg) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + u8 hash_sig[SHA256_DIGEST_SIZE]; + + nxp_handshake_sig_hash(nxpdev->crypto.handshake_h2_hash, + "D HS SIG", 8, hash_sig); + return nxp_p256_ecdsa_verify(msg->enc.device_handshake_sig.sig, + nxpdev->crypto.ecdsa_public, + hash_sig, SHA256_DIGEST_SIZE); +} + +static int nxp_write_finished(struct hci_dev *hdev, + const u8 hs_traffic_secret[SHA256_DIGEST_SIZE], + u8 verify_data[SHA256_DIGEST_SIZE]) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + u8 transcript_hash[SHA256_DIGEST_SIZE]; + u8 finished_key[SHA256_DIGEST_SIZE]; + int ret = 0; + + ret = nxp_crypto_shash_final(nxpdev->crypto.tls_handshake_hash_desc, + transcript_hash); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(hs_traffic_secret, NXP_TLS_FINISHED_LABEL, + NULL, 0, finished_key, sizeof(finished_key)); + if (ret) + return ret; + + nxp_hkdf_sha256_extract(finished_key, SHA256_DIGEST_SIZE, transcript_hash, + SHA256_DIGEST_SIZE, verify_data); + + return 0; +} + +static int nxp_verify_device_finished(struct hci_dev *hdev, + struct nxp_tls_device_hello *msg, + const u8 hs_traffic_secret[SHA256_DIGEST_SIZE]) +{ + u8 verify_data[SHA256_DIGEST_SIZE] = {0}; + int ret = 0; + + ret = nxp_write_finished(hdev, hs_traffic_secret, verify_data); + if (ret) + return ret; + + if (memcmp(verify_data, msg->enc.device_finished.verify_data, + SHA256_DIGEST_SIZE)) + return -EBADMSG; + + return 0; +} + +static int nxp_process_device_hello(struct hci_dev *hdev, struct nxp_tls_device_hello *msg) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_tls_message_hdr *hdr; + u8 hs_traffic_secret[SHA256_DIGEST_SIZE]; + u8 *shared_secret = NULL; + int ret; + + if (!msg) + return -EINVAL; + + hdr = &msg->hdr; + + if (le32_to_cpu(hdr->magic) != NXP_TLS_MAGIC || + le16_to_cpu(hdr->len) != sizeof(*msg) || + hdr->message_id != NXP_TLS_DEVICE_HELLO || + hdr->protocol_version != NXP_TLS_VERSION) { + bt_dev_err(hdev, "Invalid device hello header"); + return -EINVAL; + } + + shared_secret = kzalloc(32, GFP_KERNEL); + if (!shared_secret) + return -ENOMEM; + + ret = crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, (u8 *)msg, + DEVICE_HELLO_SIG_CUTOFF_POS); + if (ret) + goto fail; + + ret = nxp_crypto_shash_final(nxpdev->crypto.tls_handshake_hash_desc, + nxpdev->crypto.handshake_h2_hash); + if (ret) + goto fail; + + memcpy(nxpdev->crypto.ecdh_public, msg->pubkey, NXP_FW_ECDH_PUBKEY_SIZE); + + ret = nxp_compute_shared_secret(nxpdev->crypto.kpp, nxpdev->crypto.ecdh_public, + shared_secret); + if (ret) + goto fail; + + ret = nxp_hkdf_sha256_extract(NULL, 0, shared_secret, 32, + nxpdev->crypto.handshake_secret); + if (ret) + goto fail; + + ret = nxp_hkdf_derive_secret(nxpdev->crypto.handshake_secret, + NXP_TLS_DEVICE_HS_TS_LABEL, + nxpdev->crypto.handshake_h2_hash, + hs_traffic_secret); + if (ret) + goto fail; + + ret = nxp_handshake_decrypt_verify(hdev, &msg->enc, sizeof(msg->enc), + msg->auth_tag, hs_traffic_secret); + if (ret) + goto fail; + + /* + * Verify ECDSA signature handshake_sig using Device's public key from FW metadata. + * + * This is the key point where Device authentication happens: + * - Host generates a random (HostHello.random) + * - Device signs the entire handshake (incl. Host's random) with its + * private key (DeviceHello.device_handshake_sig) + * - Host now verifies ECDSA signature generated by device using Device's + * public key + * + * Only the device that possesses the proper private key could sign the + * Host's random. + * If the device is an impostor and does not pose a valid private key, + * the handshake will fail at this point. + */ + ret = nxp_get_pub_key(hdev, &msg->enc.device_info, nxpdev->crypto.ecdsa_public); + if (ret) + goto fail; + + ret = nxp_device_hello_sig_verify(hdev, msg); + if (ret) + goto fail; + + ret = crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)&msg->enc, + DEVICE_HELLO_FINISHED_ENC_CUTOFF_POS); + if (ret) + goto fail; + + ret = nxp_verify_device_finished(hdev, msg, hs_traffic_secret); + if (ret) + goto fail; + + ret = crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)&msg->enc.device_finished, + sizeof(msg->enc.device_finished)); + if (ret) + goto fail; + + memset(hs_traffic_secret, 0, SHA256_DIGEST_SIZE); + +fail: + memset(shared_secret, 0, 32); + kfree(shared_secret); + return ret; +} + +static int nxp_host_do_finished(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + union nxp_tls_host_finished_payload finished; + struct nxp_tls_host_finished *msg = &finished.host_finished; + u8 hs_traffic_secret[SHA256_DIGEST_SIZE]; + struct sk_buff *skb; + u8 *status; + int ret = 0; + + memset(msg, 0, sizeof(*msg)); + nxp_tls_hdr_init(&msg->hdr, sizeof(*msg), NXP_TLS_HOST_FINISHED); + + crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)msg, HOST_FINISHED_CUTOFF_POS); + + ret = nxp_hkdf_derive_secret(nxpdev->crypto.handshake_secret, + NXP_TLS_HOST_HS_TS_LABEL, + nxpdev->crypto.handshake_h2_hash, + hs_traffic_secret); + if (ret) + return ret; + + ret = nxp_write_finished(hdev, hs_traffic_secret, + msg->enc.host_finished.verify_data); + if (ret) + return ret; + + crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, + (u8 *)&msg->enc.host_finished, sizeof(msg->enc.host_finished)); + + nxp_handshake_encrypt(hdev, &msg->enc, sizeof(msg->enc), + msg->auth_tag, hs_traffic_secret); + + finished.msg_type = 0x01; + + skb = __hci_cmd_sync(hdev, HCI_NXP_SHI_ENCRYPT, + sizeof(finished), finished.buf, + HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Host Finished error %ld", PTR_ERR(skb)); + return PTR_ERR(skb); + } + status = skb_pull_data(skb, 1); + if (!status) { + ret = -EIO; + goto fail; + } + if (*status) { + ret = -EIO; + bt_dev_err(hdev, "Host Finished status error: %d", *status); + } + +fail: + kfree_skb(skb); + return ret; +} + +static void nxp_handshake_derive_master_secret(u8 master_secret[SHA256_DIGEST_SIZE], + u8 handshake_secret[SHA256_DIGEST_SIZE]) +{ + u8 zeros[SHA256_DIGEST_SIZE] = {0}; + u8 dhs[SHA256_DIGEST_SIZE]; + + /* Derive intermediate secret */ + nxp_hkdf_expand_label(handshake_secret, NXP_TLS_DERIVED_LABEL, + NULL, 0, dhs, sizeof(dhs)); + /* Extract master secret from derived handshake secret */ + nxp_hkdf_sha256_extract(dhs, SHA256_DIGEST_SIZE, zeros, + sizeof(zeros), master_secret); + + memset(dhs, 0, sizeof(dhs)); +} + +static int nxp_handshake_derive_traffic_keys(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_tls_traffic_keys *keys = &nxpdev->crypto.keys; + u8 hash[SHA256_DIGEST_SIZE]; + int ret = 0; + + ret = crypto_shash_final(nxpdev->crypto.tls_handshake_hash_desc, hash); + if (ret) + return ret; + + ret = nxp_hkdf_derive_secret(nxpdev->crypto.master_secret, + NXP_TLS_D_AP_TS_LABEL, hash, keys->d2h_secret); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(keys->d2h_secret, + NXP_TLS_KEYING_KEY_LABEL, NULL, 0, + keys->d2h_key, AES_KEYSIZE_128); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(keys->d2h_secret, + NXP_TLS_KEYING_IV_LABEL, NULL, 0, + keys->d2h_iv, GCM_AES_IV_SIZE); + if (ret) + return ret; + + ret = nxp_hkdf_derive_secret(nxpdev->crypto.master_secret, + NXP_TLS_H_AP_TS_LABEL, hash, keys->h2d_secret); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(keys->h2d_secret, + NXP_TLS_KEYING_KEY_LABEL, NULL, 0, + keys->h2d_key, AES_KEYSIZE_128); + if (ret) + return ret; + + ret = nxp_hkdf_expand_label(keys->h2d_secret, + NXP_TLS_KEYING_IV_LABEL, NULL, 0, + keys->h2d_iv, GCM_AES_IV_SIZE); + if (ret) + return ret; + + memset(hash, 0, sizeof(hash)); + return ret; +} + +static int nxp_authenticate_device(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_tls_device_hello *device_hello; + size_t desc_size = 0; + struct sk_buff *skb; + u8 *status; + int ret = 0; + + nxpdev->crypto.tls_handshake_hash_tfm = crypto_alloc_shash("sha256", 0, 0); + if (IS_ERR(nxpdev->crypto.tls_handshake_hash_tfm)) + return PTR_ERR(nxpdev->crypto.tls_handshake_hash_tfm); + + desc_size = sizeof(struct shash_desc) + + crypto_shash_descsize(nxpdev->crypto.tls_handshake_hash_tfm); + nxpdev->crypto.tls_handshake_hash_desc = kzalloc(desc_size, GFP_KERNEL); + if (!nxpdev->crypto.tls_handshake_hash_desc) { + ret = -ENOMEM; + goto free_tfm; + } + + nxpdev->crypto.kpp = crypto_alloc_kpp("ecdh-nist-p256", 0, 0); + if (IS_ERR(nxpdev->crypto.kpp)) { + ret = PTR_ERR(nxpdev->crypto.kpp); + goto free_desc; + } + + nxpdev->crypto.tls_handshake_hash_desc->tfm = nxpdev->crypto.tls_handshake_hash_tfm; + crypto_shash_init(nxpdev->crypto.tls_handshake_hash_desc); + + skb = nxp_host_do_hello(hdev); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + goto free_kpp; + } + + status = skb_pull_data(skb, 1); + if (*status) + goto free_skb; + + if (skb->len != sizeof(struct nxp_tls_device_hello)) { + bt_dev_err(hdev, "Invalid Device Hello Length: %d", skb->len); + goto free_skb; + } + + device_hello = skb_pull_data(skb, sizeof(*device_hello)); + ret = nxp_process_device_hello(hdev, device_hello); + if (ret) + goto free_skb; + + ret = nxp_host_do_finished(hdev); + if (ret) + goto free_skb; + + nxp_handshake_derive_master_secret(nxpdev->crypto.master_secret, + nxpdev->crypto.handshake_secret); + + nxp_handshake_derive_traffic_keys(hdev); + +free_skb: + kfree_skb(skb); +free_kpp: + crypto_free_kpp(nxpdev->crypto.kpp); + nxpdev->crypto.kpp = NULL; +free_desc: + kfree(nxpdev->crypto.tls_handshake_hash_desc); + nxpdev->crypto.tls_handshake_hash_desc = NULL; +free_tfm: + crypto_free_shash(nxpdev->crypto.tls_handshake_hash_tfm); + nxpdev->crypto.tls_handshake_hash_tfm = NULL; + if (ret) + bt_dev_err(hdev, "Device Authentication failed: %d", ret); + + return ret; +} + +static void nxp_data_calc_nonce(u8 iv[GCM_AES_IV_SIZE], u64 seq_no, + u8 nonce[GCM_AES_IV_SIZE]) +{ + u64 tmp; + + /* XOR sequence number with IV to create unique nonce */ + memcpy(&tmp, iv, sizeof(tmp)); + tmp ^= seq_no; + memcpy(nonce, &tmp, sizeof(tmp)); + memcpy(nonce + sizeof(tmp), iv + sizeof(tmp), + GCM_AES_IV_SIZE - sizeof(tmp)); +} + +static struct sk_buff *nxp_crypto_encrypt_cmd(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + __le16 vendor_opcode = __cpu_to_le16(HCI_NXP_SHI_ENCRYPT); + u8 nonce[GCM_AES_IV_SIZE]; + u8 tag[NXP_ENC_AUTH_TAG_SIZE]; + u8 *enc_data; + u8 sub_opcode = 0x10; + int ret; + u32 plen, enc_data_len; + struct nxp_tls_traffic_keys *keys = &nxpdev->crypto.keys; + + if (skb->len > NXP_MAX_ENCRYPT_CMD_LEN) { + bt_dev_err(hdev, "Invalid skb->len: %d", skb->len); + return skb; + } + + nxp_data_calc_nonce(keys->h2d_iv, nxpdev->crypto.enc_seq_no, nonce); + + enc_data_len = skb->len; + enc_data = kmemdup(skb->data, skb->len, GFP_KERNEL); + if (!enc_data) + return skb; + + ret = nxp_aes_gcm_encrypt(hdev, enc_data, enc_data_len, tag, + keys->h2d_key, nonce); + if (ret) { + kfree(enc_data); + return skb; + } + + kfree_skb(skb); + + plen = enc_data_len + NXP_ENC_AUTH_TAG_SIZE + 1; + skb = bt_skb_alloc(plen, GFP_ATOMIC); + if (!skb) { + kfree(enc_data); + return ERR_PTR(-ENOMEM); + } + hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; + skb_put_data(skb, &vendor_opcode, 2); + skb_put_data(skb, &plen, 1); + skb_put_data(skb, &sub_opcode, 1); + skb_put_data(skb, enc_data, enc_data_len); + skb_put_data(skb, tag, NXP_ENC_AUTH_TAG_SIZE); + + nxpdev->crypto.enc_seq_no++; + kfree(enc_data); + return skb; +} + +static int nxp_crypto_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int ciphertext_size; + u8 *ciphertext; + u8 aes_gcm_tag[NXP_ENC_AUTH_TAG_SIZE]; + u8 nonce[GCM_AES_IV_SIZE]; + int ret; + struct sk_buff *event_skb; + struct nxp_tls_traffic_keys *keys = &nxpdev->crypto.keys; + + if (skb->len < NXP_ENC_AUTH_TAG_SIZE) { + bt_dev_err(hdev, "Encrypted event too short: %d", skb->len); + return -EINVAL; + } + ciphertext_size = skb->len - NXP_ENC_AUTH_TAG_SIZE; + ciphertext = kzalloc(ciphertext_size, GFP_KERNEL); + if (!ciphertext) + return -ENOMEM; + + memcpy(ciphertext, skb->data, ciphertext_size); + memcpy(aes_gcm_tag, skb->data + ciphertext_size, NXP_ENC_AUTH_TAG_SIZE); + + nxp_data_calc_nonce(keys->d2h_iv, nxpdev->crypto.dec_seq_no, nonce); + + ret = nxp_aes_gcm_decrypt(hdev, ciphertext, ciphertext_size, + aes_gcm_tag, keys->d2h_key, nonce); + if (ret) { + kfree(ciphertext); + return ret; + } + + event_skb = bt_skb_alloc(ciphertext_size, GFP_ATOMIC); + if (!event_skb) { + kfree(ciphertext); + return -ENOMEM; + } + + hci_skb_pkt_type(event_skb) = HCI_EVENT_PKT; + skb_put_data(event_skb, ciphertext, ciphertext_size); + + nxpdev->crypto.dec_seq_no++; + + kfree(ciphertext); + + /* Inject Decrypted Event to upper stack */ + return hci_recv_frame(hdev, event_skb); +} + +static int nxp_process_vendor_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct hci_event_hdr *vendor_event_hdr; + u8 *vendor_sub_event; + + vendor_event_hdr = (struct hci_event_hdr *)skb_pull_data(skb, + sizeof(*vendor_event_hdr)); + if (!vendor_event_hdr) + goto free_skb; + + if (!vendor_event_hdr->plen) + goto free_skb; + + vendor_sub_event = skb_pull_data(skb, 1); + if (!vendor_sub_event) + goto free_skb; + + switch (*vendor_sub_event) { + case 0x23: + break; // Power Save Enable/Disable vendor response. Can be ignored. + case 0xe3: + if (nxpdev->secure_interface) + nxp_crypto_event(hdev, skb); + else + bt_dev_warn(hdev, "Unexpected encrypted event"); + break; + default: + bt_dev_err(hdev, "Unknown vendor event subtype: %d", *vendor_sub_event); + break; + } + +free_skb: + kfree_skb(skb); + return 0; +} + +static int nxp_recv_event_frame(struct hci_dev *hdev, struct sk_buff *skb) +{ + u8 event = hci_event_hdr(skb)->evt; + + if (event == 0xff) + return nxp_process_vendor_event(hdev, skb); + else + return hci_recv_frame(hdev, skb); +} + /* NXP protocol */ static int nxp_setup(struct hci_dev *hdev) { @@ -1466,6 +2875,14 @@ static int nxp_setup(struct hci_dev *hdev) serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate); nxpdev->current_baudrate = nxpdev->fw_init_baudrate; + nxp_get_fw_version(hdev); + + if (nxpdev->secure_interface) { + err = nxp_authenticate_device(hdev); + if (err) + return -EACCES; + } + ps_init(hdev); if (test_and_clear_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state)) @@ -1632,6 +3049,20 @@ static int nxp_enqueue(struct hci_dev *hdev, struct sk_buff *skb) goto free_skb; } break; + case HCI_OP_LINK_KEY_REPLY: + case HCI_OP_LE_START_ENC: + case HCI_OP_LE_LTK_REPLY: + case HCI_OP_LE_ADD_TO_RESOLV_LIST: + if (nxpdev->secure_interface) { + /* Re-alloc skb and encrypt sensitive command + * and payload. Command complete event + * won't be encrypted. + */ + skb = nxp_crypto_encrypt_cmd(hdev, skb); + if (IS_ERR(skb)) + return PTR_ERR(skb); + } + break; default: break; } @@ -1742,7 +3173,7 @@ static int btnxpuart_flush(struct hci_dev *hdev) static const struct h4_recv_pkt nxp_recv_pkts[] = { { H4_RECV_ACL, .recv = nxp_recv_acl_pkt }, { H4_RECV_SCO, .recv = hci_recv_frame }, - { H4_RECV_EVENT, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = nxp_recv_event_frame }, { H4_RECV_ISO, .recv = hci_recv_frame }, { NXP_RECV_CHIP_VER_V1, .recv = nxp_recv_chip_ver_v1 }, { NXP_RECV_FW_REQ_V1, .recv = nxp_recv_fw_req_v1 }, diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 7c958d6065bec..74f820e89655e 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -581,28 +581,11 @@ static int qca_download_firmware(struct hci_dev *hdev, ret = request_firmware(&fw, config->fwname, &hdev->dev); if (ret) { - /* For WCN6750, if mbn file is not present then check for - * tlv file. - */ - if (soc_type == QCA_WCN6750 && config->type == ELF_TYPE_PATCH) { - bt_dev_dbg(hdev, "QCA Failed to request file: %s (%d)", - config->fwname, ret); - config->type = TLV_TYPE_PATCH; - snprintf(config->fwname, sizeof(config->fwname), - "qca/msbtfw%02x.tlv", rom_ver); - bt_dev_info(hdev, "QCA Downloading %s", config->fwname); - ret = request_firmware(&fw, config->fwname, &hdev->dev); - if (ret) { - bt_dev_err(hdev, "QCA Failed to request file: %s (%d)", - config->fwname, ret); - return ret; - } - } /* If the board-specific file is missing, try loading the default * one, unless that was attempted already. */ - else if (config->type == TLV_TYPE_NVM && - qca_get_alt_nvm_file(config->fwname, sizeof(config->fwname))) { + if (config->type == TLV_TYPE_NVM && + qca_get_alt_nvm_file(config->fwname, sizeof(config->fwname))) { bt_dev_info(hdev, "QCA Downloading %s", config->fwname); ret = request_firmware(&fw, config->fwname, &hdev->dev); if (ret) { @@ -847,8 +830,12 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, "qca/msbtfw%02x.mbn", rom_ver); break; case QCA_WCN6855: + /* Due to historical reasons, WCN685x chip has been using firmware + * without the "wcn" prefix. The mapping between the chip and its + * corresponding firmware has now been corrected. + */ snprintf(config.fwname, sizeof(config.fwname), - "qca/hpbtfw%02x.tlv", rom_ver); + "qca/wcnhpbtfw%02x.tlv", rom_ver); break; case QCA_WCN7850: snprintf(config.fwname, sizeof(config.fwname), @@ -861,8 +848,26 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, } err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + /* For WCN6750, if mbn file is not present then check for + * tlv file. + */ + if (err < 0 && soc_type == QCA_WCN6750) { + bt_dev_dbg(hdev, "QCA Failed to request file: %s (%d)", + config.fwname, err); + config.type = TLV_TYPE_PATCH; + snprintf(config.fwname, sizeof(config.fwname), + "qca/msbtfw%02x.tlv", rom_ver); + bt_dev_info(hdev, "QCA Downloading %s", config.fwname); + err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + } else if (err < 0 && !rampatch_name && soc_type == QCA_WCN6855) { + snprintf(config.fwname, sizeof(config.fwname), + "qca/hpbtfw%02x.tlv", rom_ver); + err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + } + if (err < 0) { - bt_dev_err(hdev, "QCA Failed to download patch (%d)", err); + bt_dev_err(hdev, "QCA Failed to request file: %s (%d)", + config.fwname, err); return err; } @@ -923,7 +928,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, case QCA_WCN6855: qca_read_fw_board_id(hdev, &boardid); qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname), - "hpnv", soc_type, ver, rom_ver, boardid); + "wcnhpnv", soc_type, ver, rom_ver, boardid); break; case QCA_WCN7850: qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname), @@ -936,8 +941,15 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, } err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + if (err < 0 && !firmware_name && soc_type == QCA_WCN6855) { + qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname), + "hpnv", soc_type, ver, rom_ver, boardid); + err = qca_download_firmware(hdev, &config, soc_type, rom_ver); + } + if (err < 0) { - bt_dev_err(hdev, "QCA Failed to download NVM (%d)", err); + bt_dev_err(hdev, "QCA Failed to request file: %s (%d)", + config.fwname, err); return err; } diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 8ed3883ab8eef..51278a0e4fd67 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -521,6 +521,8 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x0bda, 0xb850), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3600), .driver_info = BTUSB_REALTEK }, { USB_DEVICE(0x13d3, 0x3601), .driver_info = BTUSB_REALTEK }, + { USB_DEVICE(0x0489, 0xe112), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek 8851BU Bluetooth devices */ { USB_DEVICE(0x3625, 0x010b), .driver_info = BTUSB_REALTEK | @@ -637,6 +639,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3622), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe158), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional MediaTek MT7921 Bluetooth devices */ { USB_DEVICE(0x0489, 0xe0c8), .driver_info = BTUSB_MEDIATEK | @@ -4052,7 +4056,7 @@ static int btusb_probe(struct usb_interface *intf, return -ENODEV; } - data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -4075,8 +4079,10 @@ static int btusb_probe(struct usb_interface *intf, } } - if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) + if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) { + kfree(data); return -ENODEV; + } if (id->driver_info & BTUSB_AMP) { data->cmdreq_type = USB_TYPE_CLASS | 0x01; @@ -4131,8 +4137,10 @@ static int btusb_probe(struct usb_interface *intf, data->recv_acl = hci_recv_frame; hdev = hci_alloc_dev_priv(priv_size); - if (!hdev) + if (!hdev) { + kfree(data); return -ENOMEM; + } hdev->bus = HCI_USB; hci_set_drvdata(hdev, data); @@ -4406,6 +4414,7 @@ static int btusb_probe(struct usb_interface *intf, if (data->reset_gpio) gpiod_put(data->reset_gpio); hci_free_dev(hdev); + kfree(data); return err; } @@ -4454,6 +4463,7 @@ static void btusb_disconnect(struct usb_interface *intf) } hci_free_dev(hdev); + kfree(data); } #ifdef CONFIG_PM @@ -4463,10 +4473,11 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message) BT_DBG("intf %p", intf); - /* Don't auto-suspend if there are connections; external suspend calls - * shall never fail. + /* Don't auto-suspend if there are connections or discovery in + * progress; external suspend calls shall never fail. */ - if (PMSG_IS_AUTO(message) && hci_conn_count(data->hdev)) + if (PMSG_IS_AUTO(message) && + (hci_conn_count(data->hdev) || hci_discovery_active(data->hdev))) return -EBUSY; if (data->suspend_count++) @@ -4627,10 +4638,8 @@ static struct usb_driver btusb_driver = { .name = "btusb", .probe = btusb_probe, .disconnect = btusb_disconnect, -#ifdef CONFIG_PM - .suspend = btusb_suspend, - .resume = btusb_resume, -#endif + .suspend = pm_ptr(btusb_suspend), + .resume = pm_ptr(btusb_resume), .id_table = btusb_table, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c index 45e6d84224ee3..925d0a6359453 100644 --- a/drivers/bluetooth/hci_bcm4377.c +++ b/drivers/bluetooth/hci_bcm4377.c @@ -2416,8 +2416,9 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id) hdev); } -static int bcm4377_suspend(struct pci_dev *pdev, pm_message_t state) +static int bcm4377_suspend(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev); int ret; @@ -2431,8 +2432,9 @@ static int bcm4377_suspend(struct pci_dev *pdev, pm_message_t state) return 0; } -static int bcm4377_resume(struct pci_dev *pdev) +static int bcm4377_resume(struct device *dev) { + struct pci_dev *pdev = to_pci_dev(dev); struct bcm4377_data *bcm4377 = pci_get_drvdata(pdev); iowrite32(BCM4377_BAR0_SLEEP_CONTROL_UNQUIESCE, @@ -2441,6 +2443,8 @@ static int bcm4377_resume(struct pci_dev *pdev) return hci_resume_dev(bcm4377->hdev); } +static DEFINE_SIMPLE_DEV_PM_OPS(bcm4377_ops, bcm4377_suspend, bcm4377_resume); + static const struct dmi_system_id bcm4377_dmi_board_table[] = { { .matches = { @@ -2541,8 +2545,7 @@ static struct pci_driver bcm4377_pci_driver = { .name = "hci_bcm4377", .id_table = bcm4377_devid_table, .probe = bcm4377_probe, - .suspend = bcm4377_suspend, - .resume = bcm4377_resume, + .driver.pm = &bcm4377_ops, }; module_pci_driver(bcm4377_pci_driver); diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 888176b0faa90..a3c217571c3c4 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1653,6 +1653,39 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code) skb_queue_purge(&qca->rx_memdump_q); } + /* + * If the BT chip's bt_en pin is connected to a 3.3V power supply via + * hardware and always stays high, driver cannot control the bt_en pin. + * As a result, during SSR (SubSystem Restart), QCA_SSR_TRIGGERED and + * QCA_IBS_DISABLED flags cannot be cleared, which leads to a reset + * command timeout. + * Add an msleep delay to ensure controller completes the SSR process. + * + * Host will not download the firmware after SSR, controller to remain + * in the IBS_WAKE state, and the host needs to synchronize with it + * + * Since the bluetooth chip has been reset, clear the memdump state. + */ + if (!hci_test_quirk(hu->hdev, HCI_QUIRK_NON_PERSISTENT_SETUP)) { + /* + * When the SSR (SubSystem Restart) duration exceeds 2 seconds, + * it triggers host tx_idle_delay, which sets host TX state + * to sleep. Reset tx_idle_timer after SSR to prevent + * host enter TX IBS_Sleep mode. + */ + mod_timer(&qca->tx_idle_timer, jiffies + + msecs_to_jiffies(qca->tx_idle_delay)); + + /* Controller reset completion time is 50ms */ + msleep(50); + + clear_bit(QCA_SSR_TRIGGERED, &qca->flags); + clear_bit(QCA_IBS_DISABLED, &qca->flags); + + qca->tx_ibs_state = HCI_IBS_TX_AWAKE; + qca->memdump_state = QCA_MEMDUMP_IDLE; + } + clear_bit(QCA_HW_ERROR_EVENT, &qca->flags); } diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index d46ed9011ee5d..69eed69f7f265 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -130,21 +130,30 @@ struct bt_voice { #define BT_RCVMTU 13 #define BT_PHY 14 -#define BT_PHY_BR_1M_1SLOT 0x00000001 -#define BT_PHY_BR_1M_3SLOT 0x00000002 -#define BT_PHY_BR_1M_5SLOT 0x00000004 -#define BT_PHY_EDR_2M_1SLOT 0x00000008 -#define BT_PHY_EDR_2M_3SLOT 0x00000010 -#define BT_PHY_EDR_2M_5SLOT 0x00000020 -#define BT_PHY_EDR_3M_1SLOT 0x00000040 -#define BT_PHY_EDR_3M_3SLOT 0x00000080 -#define BT_PHY_EDR_3M_5SLOT 0x00000100 -#define BT_PHY_LE_1M_TX 0x00000200 -#define BT_PHY_LE_1M_RX 0x00000400 -#define BT_PHY_LE_2M_TX 0x00000800 -#define BT_PHY_LE_2M_RX 0x00001000 -#define BT_PHY_LE_CODED_TX 0x00002000 -#define BT_PHY_LE_CODED_RX 0x00004000 +#define BT_PHY_BR_1M_1SLOT BIT(0) +#define BT_PHY_BR_1M_3SLOT BIT(1) +#define BT_PHY_BR_1M_5SLOT BIT(2) +#define BT_PHY_EDR_2M_1SLOT BIT(3) +#define BT_PHY_EDR_2M_3SLOT BIT(4) +#define BT_PHY_EDR_2M_5SLOT BIT(5) +#define BT_PHY_EDR_3M_1SLOT BIT(6) +#define BT_PHY_EDR_3M_3SLOT BIT(7) +#define BT_PHY_EDR_3M_5SLOT BIT(8) +#define BT_PHY_LE_1M_TX BIT(9) +#define BT_PHY_LE_1M_RX BIT(10) +#define BT_PHY_LE_2M_TX BIT(11) +#define BT_PHY_LE_2M_RX BIT(12) +#define BT_PHY_LE_CODED_TX BIT(13) +#define BT_PHY_LE_CODED_RX BIT(14) + +#define BT_PHY_BREDR_MASK (BT_PHY_BR_1M_1SLOT | BT_PHY_BR_1M_3SLOT | \ + BT_PHY_BR_1M_5SLOT | BT_PHY_EDR_2M_1SLOT | \ + BT_PHY_EDR_2M_3SLOT | BT_PHY_EDR_2M_5SLOT | \ + BT_PHY_EDR_3M_1SLOT | BT_PHY_EDR_3M_3SLOT | \ + BT_PHY_EDR_3M_5SLOT) +#define BT_PHY_LE_MASK (BT_PHY_LE_1M_TX | BT_PHY_LE_1M_RX | \ + BT_PHY_LE_2M_TX | BT_PHY_LE_2M_RX | \ + BT_PHY_LE_CODED_TX | BT_PHY_LE_CODED_RX) #define BT_MODE 15 @@ -173,7 +182,7 @@ struct bt_iso_io_qos { __u32 interval; __u16 latency; __u16 sdu; - __u8 phy; + __u8 phys; __u8 rtn; }; @@ -212,9 +221,9 @@ struct bt_iso_qos { }; }; -#define BT_ISO_PHY_1M 0x01 -#define BT_ISO_PHY_2M 0x02 -#define BT_ISO_PHY_CODED 0x04 +#define BT_ISO_PHY_1M BIT(0) +#define BT_ISO_PHY_2M BIT(1) +#define BT_ISO_PHY_CODED BIT(2) #define BT_ISO_PHY_ANY (BT_ISO_PHY_1M | BT_ISO_PHY_2M | \ BT_ISO_PHY_CODED) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index a27cd3626b872..89ad9470fa71b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -654,6 +654,8 @@ enum { #define HCI_LE_ISO_BROADCASTER 0x40 #define HCI_LE_ISO_SYNC_RECEIVER 0x80 #define HCI_LE_LL_EXT_FEATURE 0x80 +#define HCI_LE_CS 0x40 +#define HCI_LE_CS_HOST 0x80 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 @@ -1883,6 +1885,15 @@ struct hci_cp_le_set_default_phy { #define HCI_LE_SET_PHY_2M 0x02 #define HCI_LE_SET_PHY_CODED 0x04 +#define HCI_OP_LE_SET_PHY 0x2032 +struct hci_cp_le_set_phy { + __le16 handle; + __u8 all_phys; + __u8 tx_phys; + __u8 rx_phys; + __le16 phy_opts; +} __packed; + #define HCI_OP_LE_SET_EXT_SCAN_PARAMS 0x2041 struct hci_cp_le_set_ext_scan_params { __u8 own_addr_type; @@ -2136,8 +2147,8 @@ struct hci_cis_params { __u8 cis_id; __le16 c_sdu; __le16 p_sdu; - __u8 c_phy; - __u8 p_phy; + __u8 c_phys; + __u8 p_phys; __u8 c_rtn; __u8 p_rtn; } __packed; @@ -2269,6 +2280,204 @@ struct hci_cp_le_read_all_remote_features { __u8 pages; } __packed; +/* Channel Sounding Commands */ +#define HCI_OP_LE_CS_RD_LOCAL_SUPP_CAP 0x2089 +struct hci_rp_le_cs_rd_local_supp_cap { + __u8 status; + __u8 num_config_supported; + __le16 max_consecutive_procedures_supported; + __u8 num_antennas_supported; + __u8 max_antenna_paths_supported; + __u8 roles_supported; + __u8 modes_supported; + __u8 rtt_capability; + __u8 rtt_aa_only_n; + __u8 rtt_sounding_n; + __u8 rtt_random_payload_n; + __le16 nadm_sounding_capability; + __le16 nadm_random_capability; + __u8 cs_sync_phys_supported; + __le16 subfeatures_supported; + __le16 t_ip1_times_supported; + __le16 t_ip2_times_supported; + __le16 t_fcs_times_supported; + __le16 t_pm_times_supported; + __u8 t_sw_time_supported; + __u8 tx_snr_capability; +} __packed; + +#define HCI_OP_LE_CS_RD_RMT_SUPP_CAP 0x208A +struct hci_cp_le_cs_rd_local_supp_cap { + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_WR_CACHED_RMT_SUPP_CAP 0x208B +struct hci_cp_le_cs_wr_cached_rmt_supp_cap { + __le16 handle; + __u8 num_config_supported; + __le16 max_consecutive_procedures_supported; + __u8 num_antennas_supported; + __u8 max_antenna_paths_supported; + __u8 roles_supported; + __u8 modes_supported; + __u8 rtt_capability; + __u8 rtt_aa_only_n; + __u8 rtt_sounding_n; + __u8 rtt_random_payload_n; + __le16 nadm_sounding_capability; + __le16 nadm_random_capability; + __u8 cs_sync_phys_supported; + __le16 subfeatures_supported; + __le16 t_ip1_times_supported; + __le16 t_ip2_times_supported; + __le16 t_fcs_times_supported; + __le16 t_pm_times_supported; + __u8 t_sw_time_supported; + __u8 tx_snr_capability; +} __packed; + +struct hci_rp_le_cs_wr_cached_rmt_supp_cap { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_SEC_ENABLE 0x208C +struct hci_cp_le_cs_sec_enable { + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_SET_DEFAULT_SETTINGS 0x208D +struct hci_cp_le_cs_set_default_settings { + __le16 handle; + __u8 role_enable; + __u8 cs_sync_ant_sel; + __s8 max_tx_power; +} __packed; + +struct hci_rp_le_cs_set_default_settings { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_RD_RMT_FAE_TABLE 0x208E +struct hci_cp_le_cs_rd_rmt_fae_table { + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_WR_CACHED_RMT_FAE_TABLE 0x208F +struct hci_cp_le_cs_wr_rmt_cached_fae_table { + __le16 handle; + __u8 remote_fae_table[72]; +} __packed; + +struct hci_rp_le_cs_wr_rmt_cached_fae_table { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_CREATE_CONFIG 0x2090 +struct hci_cp_le_cs_create_config { + __le16 handle; + __u8 config_id; + __u8 create_context; + __u8 main_mode_type; + __u8 sub_mode_type; + __u8 min_main_mode_steps; + __u8 max_main_mode_steps; + __u8 main_mode_repetition; + __u8 mode_0_steps; + __u8 role; + __u8 rtt_type; + __u8 cs_sync_phy; + __u8 channel_map[10]; + __u8 channel_map_repetition; + __u8 channel_selection_type; + __u8 ch3c_shape; + __u8 ch3c_jump; + __u8 reserved; +} __packed; + +#define HCI_OP_LE_CS_REMOVE_CONFIG 0x2091 +struct hci_cp_le_cs_remove_config { + __le16 handle; + __u8 config_id; +} __packed; + +#define HCI_OP_LE_CS_SET_CH_CLASSIFICATION 0x2092 +struct hci_cp_le_cs_set_ch_classification { + __u8 ch_classification[10]; +} __packed; + +struct hci_rp_le_cs_set_ch_classification { + __u8 status; +} __packed; + +#define HCI_OP_LE_CS_SET_PROC_PARAM 0x2093 +struct hci_cp_le_cs_set_proc_param { + __le16 handle; + __u8 config_id; + __le16 max_procedure_len; + __le16 min_procedure_interval; + __le16 max_procedure_interval; + __le16 max_procedure_count; + __u8 min_subevent_len[3]; + __u8 max_subevent_len[3]; + __u8 tone_antenna_config_selection; + __u8 phy; + __u8 tx_power_delta; + __u8 preferred_peer_antenna; + __u8 snr_control_initiator; + __u8 snr_control_reflector; +} __packed; + +struct hci_rp_le_cs_set_proc_param { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_CS_SET_PROC_ENABLE 0x2094 +struct hci_cp_le_cs_set_proc_enable { + __le16 handle; + __u8 config_id; + __u8 enable; +} __packed; + +#define HCI_OP_LE_CS_TEST 0x2095 +struct hci_cp_le_cs_test { + __u8 main_mode_type; + __u8 sub_mode_type; + __u8 main_mode_repetition; + __u8 mode_0_steps; + __u8 role; + __u8 rtt_type; + __u8 cs_sync_phy; + __u8 cs_sync_antenna_selection; + __u8 subevent_len[3]; + __le16 subevent_interval; + __u8 max_num_subevents; + __u8 transmit_power_level; + __u8 t_ip1_time; + __u8 t_ip2_time; + __u8 t_fcs_time; + __u8 t_pm_time; + __u8 t_sw_time; + __u8 tone_antenna_config_selection; + __u8 reserved; + __u8 snr_control_initiator; + __u8 snr_control_reflector; + __le16 drbg_nonce; + __u8 channel_map_repetition; + __le16 override_config; + __u8 override_parameters_length; + __u8 override_parameters_data[]; +} __packed; + +struct hci_rp_le_cs_test { + __u8 status; +} __packed; + +#define HCI_OP_LE_CS_TEST_END 0x2096 + /* ---- HCI Events ---- */ struct hci_ev_status { __u8 status; @@ -2960,6 +3169,129 @@ struct hci_evt_le_read_all_remote_features_complete { __u8 features[248]; } __packed; +/* Channel Sounding Events */ +#define HCI_EVT_LE_CS_READ_RMT_SUPP_CAP_COMPLETE 0x2C +struct hci_evt_le_cs_read_rmt_supp_cap_complete { + __u8 status; + __le16 handle; + __u8 num_configs_supp; + __le16 max_consec_proc_supp; + __u8 num_ant_supp; + __u8 max_ant_path_supp; + __u8 roles_supp; + __u8 modes_supp; + __u8 rtt_cap; + __u8 rtt_aa_only_n; + __u8 rtt_sounding_n; + __u8 rtt_rand_payload_n; + __le16 nadm_sounding_cap; + __le16 nadm_rand_cap; + __u8 cs_sync_phys_supp; + __le16 sub_feat_supp; + __le16 t_ip1_times_supp; + __le16 t_ip2_times_supp; + __le16 t_fcs_times_supp; + __le16 t_pm_times_supp; + __u8 t_sw_times_supp; + __u8 tx_snr_cap; +} __packed; + +#define HCI_EVT_LE_CS_READ_RMT_FAE_TABLE_COMPLETE 0x2D +struct hci_evt_le_cs_read_rmt_fae_table_complete { + __u8 status; + __le16 handle; + __u8 remote_fae_table[72]; +} __packed; + +#define HCI_EVT_LE_CS_SECURITY_ENABLE_COMPLETE 0x2E +struct hci_evt_le_cs_security_enable_complete { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_EVT_LE_CS_CONFIG_COMPLETE 0x2F +struct hci_evt_le_cs_config_complete { + __u8 status; + __le16 handle; + __u8 config_id; + __u8 action; + __u8 main_mode_type; + __u8 sub_mode_type; + __u8 min_main_mode_steps; + __u8 max_main_mode_steps; + __u8 main_mode_rep; + __u8 mode_0_steps; + __u8 role; + __u8 rtt_type; + __u8 cs_sync_phy; + __u8 channel_map[10]; + __u8 channel_map_rep; + __u8 channel_sel_type; + __u8 ch3c_shape; + __u8 ch3c_jump; + __u8 reserved; + __u8 t_ip1_time; + __u8 t_ip2_time; + __u8 t_fcs_time; + __u8 t_pm_time; +} __packed; + +#define HCI_EVT_LE_CS_PROCEDURE_ENABLE_COMPLETE 0x30 +struct hci_evt_le_cs_procedure_enable_complete { + __u8 status; + __le16 handle; + __u8 config_id; + __u8 state; + __u8 tone_ant_config_sel; + __s8 sel_tx_pwr; + __u8 sub_evt_len[3]; + __u8 sub_evts_per_evt; + __le16 sub_evt_intrvl; + __le16 evt_intrvl; + __le16 proc_intrvl; + __le16 proc_counter; + __le16 max_proc_len; +} __packed; + +#define HCI_EVT_LE_CS_SUBEVENT_RESULT 0x31 +struct hci_evt_le_cs_subevent_result { + __le16 handle; + __u8 config_id; + __le16 start_acl_conn_evt_counter; + __le16 proc_counter; + __le16 freq_comp; + __u8 ref_pwr_lvl; + __u8 proc_done_status; + __u8 subevt_done_status; + __u8 abort_reason; + __u8 num_ant_paths; + __u8 num_steps_reported; + __u8 step_mode[0]; /* depends on num_steps_reported */ + __u8 step_channel[0]; /* depends on num_steps_reported */ + __u8 step_data_length[0]; /* depends on num_steps_reported */ + __u8 step_data[0]; /* depends on num_steps_reported */ +} __packed; + +#define HCI_EVT_LE_CS_SUBEVENT_RESULT_CONTINUE 0x32 +struct hci_evt_le_cs_subevent_result_continue { + __le16 handle; + __u8 config_id; + __u8 proc_done_status; + __u8 subevt_done_status; + __u8 abort_reason; + __u8 num_ant_paths; + __u8 num_steps_reported; + __u8 step_mode[0]; /* depends on num_steps_reported */ + __u8 step_channel[0]; /* depends on num_steps_reported */ + __u8 step_data_length[0]; /* depends on num_steps_reported */ + __u8 step_data[0]; /* depends on num_steps_reported */ +} __packed; + +#define HCI_EVT_LE_CS_TEST_END_COMPLETE 0x33 +struct hci_evt_le_cs_test_end_complete { + __u8 status; +} __packed; + #define HCI_EV_VENDOR 0xff /* Internal events generated by Bluetooth stack */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4263e71a23efb..a7bffb908c1ec 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -730,6 +730,8 @@ struct hci_conn { __u16 le_per_adv_data_offset; __u8 le_adv_phy; __u8 le_adv_sec_phy; + __u8 le_tx_def_phys; + __u8 le_rx_def_phys; __u8 le_tx_phy; __u8 le_rx_phy; __s8 rssi; @@ -2071,6 +2073,12 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define ll_ext_feature_capable(dev) \ ((dev)->le_features[7] & HCI_LE_LL_EXT_FEATURE) +/* Channel sounding support */ +#define le_cs_capable(dev) \ + ((dev)->le_features[5] & HCI_LE_CS) +#define le_cs_host_capable(dev) \ + ((dev)->le_features[5] & HCI_LE_CS_HOST) + #define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \ (!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG))) @@ -2334,6 +2342,7 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); void *hci_recv_event_data(struct hci_dev *hdev, __u8 event); u32 hci_conn_get_phy(struct hci_conn *conn); +int hci_conn_set_phy(struct hci_conn *conn, u32 phys); /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h index 56076bbc981d9..73e494b2591de 100644 --- a/include/net/bluetooth/hci_sync.h +++ b/include/net/bluetooth/hci_sync.h @@ -191,3 +191,6 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn); int hci_past_sync(struct hci_conn *conn, struct hci_conn *le); int hci_le_read_remote_features(struct hci_conn *conn); + +int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type); +int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c3f7828bf9d54..0795818963a52 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1002,12 +1002,18 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, switch (type) { case ACL_LINK: conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; + conn->link_policy = hdev->link_policy; conn->mtu = hdev->acl_mtu; break; case LE_LINK: /* conn->src should reflect the local identity address */ hci_copy_identity_address(hdev, &conn->src, &conn->src_type); conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu; + /* Use the controller supported PHYS as default until the + * remote features are resolved. + */ + conn->le_tx_def_phys = hdev->le_tx_def_phys; + conn->le_rx_def_phys = hdev->le_tx_def_phys; break; case CIS_LINK: /* conn->src should reflect the local identity address */ @@ -1819,7 +1825,7 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) cp.bis.sdu = cpu_to_le16(qos->bcast.out.sdu); cp.bis.latency = cpu_to_le16(qos->bcast.out.latency); cp.bis.rtn = qos->bcast.out.rtn; - cp.bis.phy = qos->bcast.out.phy; + cp.bis.phy = qos->bcast.out.phys; cp.bis.packing = qos->bcast.packing; cp.bis.framing = qos->bcast.framing; cp.bis.encryption = qos->bcast.encryption; @@ -1869,10 +1875,10 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data) cis->cis_id = cis_id; cis->c_sdu = cpu_to_le16(conn->iso_qos.ucast.out.sdu); cis->p_sdu = cpu_to_le16(conn->iso_qos.ucast.in.sdu); - cis->c_phy = qos->ucast.out.phy ? qos->ucast.out.phy : - qos->ucast.in.phy; - cis->p_phy = qos->ucast.in.phy ? qos->ucast.in.phy : - qos->ucast.out.phy; + cis->c_phys = qos->ucast.out.phys ? qos->ucast.out.phys : + qos->ucast.in.phys; + cis->p_phys = qos->ucast.in.phys ? qos->ucast.in.phys : + qos->ucast.out.phys; cis->c_rtn = qos->ucast.out.rtn; cis->p_rtn = qos->ucast.in.rtn; } @@ -1974,8 +1980,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, return cis; /* Update LINK PHYs according to QoS preference */ - cis->le_tx_phy = qos->ucast.out.phy; - cis->le_rx_phy = qos->ucast.in.phy; + cis->le_tx_phy = qos->ucast.out.phys; + cis->le_rx_phy = qos->ucast.in.phys; /* If output interval is not set use the input interval as it cannot be * 0x000000. @@ -2090,15 +2096,15 @@ int hci_le_create_cis_pending(struct hci_dev *hdev) } static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn, - struct bt_iso_io_qos *qos, __u8 phy) + struct bt_iso_io_qos *qos, __u8 phys) { /* Only set MTU if PHY is enabled */ - if (!qos->sdu && qos->phy) + if (!qos->sdu && qos->phys) qos->sdu = conn->mtu; /* Use the same PHY as ACL if set to any */ - if (qos->phy == BT_ISO_PHY_ANY) - qos->phy = phy; + if (qos->phys == BT_ISO_PHY_ANY) + qos->phys = phys; /* Use LE ACL connection interval if not set */ if (!qos->interval) @@ -2118,7 +2124,7 @@ static int create_big_sync(struct hci_dev *hdev, void *data) u32 flags = 0; int err; - if (qos->bcast.out.phy == 0x02) + if (qos->bcast.out.phys == BIT(1)) flags |= MGMT_ADV_FLAG_SEC_2M; /* Align intervals */ @@ -2227,8 +2233,7 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, return conn; /* Update LINK PHYs according to QoS preference */ - conn->le_tx_phy = qos->bcast.out.phy; - conn->le_tx_phy = qos->bcast.out.phy; + conn->le_tx_def_phys = qos->bcast.out.phys; /* Add Basic Announcement into Peridic Adv Data if BASE is set */ if (base_len && base) { @@ -2237,7 +2242,7 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid, } hci_iso_qos_setup(hdev, conn, &qos->bcast.out, - conn->le_tx_phy ? conn->le_tx_phy : + conn->le_tx_def_phys ? conn->le_tx_def_phys : hdev->le_tx_def_phys); conn->iso_qos = *qos; @@ -2357,9 +2362,11 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, return le; hci_iso_qos_setup(hdev, le, &qos->ucast.out, - le->le_tx_phy ? le->le_tx_phy : hdev->le_tx_def_phys); + le->le_tx_def_phys ? le->le_tx_def_phys : + hdev->le_tx_def_phys); hci_iso_qos_setup(hdev, le, &qos->ucast.in, - le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys); + le->le_rx_def_phys ? le->le_rx_def_phys : + hdev->le_rx_def_phys); cis = hci_bind_cis(hdev, dst, dst_type, qos, timeout); if (IS_ERR(cis)) { @@ -2614,8 +2621,8 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) timer: if (hdev->idle_timeout > 0) - queue_delayed_work(hdev->workqueue, &conn->idle_work, - msecs_to_jiffies(hdev->idle_timeout)); + mod_delayed_work(hdev->workqueue, &conn->idle_work, + msecs_to_jiffies(hdev->idle_timeout)); } /* Drop all connection on the device */ @@ -2928,22 +2935,22 @@ u32 hci_conn_get_phy(struct hci_conn *conn) break; case LE_LINK: - if (conn->le_tx_phy & HCI_LE_SET_PHY_1M) + if (conn->le_tx_def_phys & HCI_LE_SET_PHY_1M) phys |= BT_PHY_LE_1M_TX; - if (conn->le_rx_phy & HCI_LE_SET_PHY_1M) + if (conn->le_rx_def_phys & HCI_LE_SET_PHY_1M) phys |= BT_PHY_LE_1M_RX; - if (conn->le_tx_phy & HCI_LE_SET_PHY_2M) + if (conn->le_tx_def_phys & HCI_LE_SET_PHY_2M) phys |= BT_PHY_LE_2M_TX; - if (conn->le_rx_phy & HCI_LE_SET_PHY_2M) + if (conn->le_rx_def_phys & HCI_LE_SET_PHY_2M) phys |= BT_PHY_LE_2M_RX; - if (conn->le_tx_phy & HCI_LE_SET_PHY_CODED) + if (conn->le_tx_def_phys & HCI_LE_SET_PHY_CODED) phys |= BT_PHY_LE_CODED_TX; - if (conn->le_rx_phy & HCI_LE_SET_PHY_CODED) + if (conn->le_rx_def_phys & HCI_LE_SET_PHY_CODED) phys |= BT_PHY_LE_CODED_RX; break; @@ -2952,6 +2959,111 @@ u32 hci_conn_get_phy(struct hci_conn *conn) return phys; } +static u16 bt_phy_pkt_type(struct hci_conn *conn, u32 phys) +{ + u16 pkt_type = conn->pkt_type; + + if (phys & BT_PHY_BR_1M_3SLOT) + pkt_type |= HCI_DM3 | HCI_DH3; + else + pkt_type &= ~(HCI_DM3 | HCI_DH3); + + if (phys & BT_PHY_BR_1M_5SLOT) + pkt_type |= HCI_DM5 | HCI_DH5; + else + pkt_type &= ~(HCI_DM5 | HCI_DH5); + + if (phys & BT_PHY_EDR_2M_1SLOT) + pkt_type &= ~HCI_2DH1; + else + pkt_type |= HCI_2DH1; + + if (phys & BT_PHY_EDR_2M_3SLOT) + pkt_type &= ~HCI_2DH3; + else + pkt_type |= HCI_2DH3; + + if (phys & BT_PHY_EDR_2M_5SLOT) + pkt_type &= ~HCI_2DH5; + else + pkt_type |= HCI_2DH5; + + if (phys & BT_PHY_EDR_3M_1SLOT) + pkt_type &= ~HCI_3DH1; + else + pkt_type |= HCI_3DH1; + + if (phys & BT_PHY_EDR_3M_3SLOT) + pkt_type &= ~HCI_3DH3; + else + pkt_type |= HCI_3DH3; + + if (phys & BT_PHY_EDR_3M_5SLOT) + pkt_type &= ~HCI_3DH5; + else + pkt_type |= HCI_3DH5; + + return pkt_type; +} + +static int bt_phy_le_phy(u32 phys, u8 *tx_phys, u8 *rx_phys) +{ + if (!tx_phys || !rx_phys) + return -EINVAL; + + *tx_phys = 0; + *rx_phys = 0; + + if (phys & BT_PHY_LE_1M_TX) + *tx_phys |= HCI_LE_SET_PHY_1M; + + if (phys & BT_PHY_LE_1M_RX) + *rx_phys |= HCI_LE_SET_PHY_1M; + + if (phys & BT_PHY_LE_2M_TX) + *tx_phys |= HCI_LE_SET_PHY_2M; + + if (phys & BT_PHY_LE_2M_RX) + *rx_phys |= HCI_LE_SET_PHY_2M; + + if (phys & BT_PHY_LE_CODED_TX) + *tx_phys |= HCI_LE_SET_PHY_CODED; + + if (phys & BT_PHY_LE_CODED_RX) + *rx_phys |= HCI_LE_SET_PHY_CODED; + + return 0; +} + +int hci_conn_set_phy(struct hci_conn *conn, u32 phys) +{ + u8 tx_phys, rx_phys; + + switch (conn->type) { + case SCO_LINK: + case ESCO_LINK: + return -EINVAL; + case ACL_LINK: + /* Only allow setting BR/EDR PHYs if link type is ACL */ + if (phys & ~BT_PHY_BREDR_MASK) + return -EINVAL; + + return hci_acl_change_pkt_type(conn, + bt_phy_pkt_type(conn, phys)); + case LE_LINK: + /* Only allow setting LE PHYs if link type is LE */ + if (phys & ~BT_PHY_LE_MASK) + return -EINVAL; + + if (bt_phy_le_phy(phys, &tx_phys, &rx_phys)) + return -EINVAL; + + return hci_le_set_phy(conn, tx_phys, rx_phys); + default: + return -EINVAL; + } +} + static int abort_conn_sync(struct hci_dev *hdev, void *data) { struct hci_conn *conn = data; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8ccec73dce45c..b069607b145b5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -117,6 +117,7 @@ bool hci_discovery_active(struct hci_dev *hdev) return false; } } +EXPORT_SYMBOL(hci_discovery_active); void hci_discovery_set_state(struct hci_dev *hdev, int state) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a9868f17ef40f..286529d2e554a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2869,6 +2869,31 @@ static void hci_cs_le_ext_create_conn(struct hci_dev *hdev, u8 status) hci_dev_unlock(hdev); } +static void hci_cs_le_set_phy(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_le_set_phy *cp; + struct hci_conn *conn; + + bt_dev_dbg(hdev, "status 0x%2.2x", status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PHY); + if (!cp) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); + if (conn) { + conn->le_tx_def_phys = cp->tx_phys; + conn->le_rx_def_phys = cp->rx_phys; + } + + hci_dev_unlock(hdev); +} + static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status) { struct hci_cp_le_read_remote_features *cp; @@ -4359,6 +4384,7 @@ static const struct hci_cs { HCI_CS(HCI_OP_LE_CREATE_CONN, hci_cs_le_create_conn), HCI_CS(HCI_OP_LE_READ_REMOTE_FEATURES, hci_cs_le_read_remote_features), HCI_CS(HCI_OP_LE_START_ENC, hci_cs_le_start_enc), + HCI_CS(HCI_OP_LE_SET_PHY, hci_cs_le_set_phy), HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn), HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis), HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big), @@ -6607,8 +6633,20 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data, conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { - if (!ev->status) - memcpy(conn->features[0], ev->features, 8); + if (!ev->status) { + memcpy(conn->le_features, ev->features, 8); + + /* Update supported PHYs */ + if (!(conn->le_features[1] & HCI_LE_PHY_2M)) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_2M; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_2M; + } + + if (!(conn->le_features[1] & HCI_LE_PHY_CODED)) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED; + } + } if (conn->state == BT_CONFIG) { __u8 status; @@ -6829,6 +6867,21 @@ static void hci_le_phy_update_evt(struct hci_dev *hdev, void *data, hci_dev_unlock(hdev); } +/* Convert LE PHY to QoS PHYs */ +static u8 le_phy_qos(u8 phy) +{ + switch (phy) { + case 0x01: + return HCI_LE_SET_PHY_1M; + case 0x02: + return HCI_LE_SET_PHY_2M; + case 0x03: + return HCI_LE_SET_PHY_CODED; + } + + return 0; +} + static void hci_le_cis_established_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb) { @@ -6890,8 +6943,8 @@ static void hci_le_cis_established_evt(struct hci_dev *hdev, void *data, 1000); qos->ucast.in.sdu = ev->c_bn ? le16_to_cpu(ev->c_mtu) : 0; qos->ucast.out.sdu = ev->p_bn ? le16_to_cpu(ev->p_mtu) : 0; - qos->ucast.in.phy = ev->c_phy; - qos->ucast.out.phy = ev->p_phy; + qos->ucast.in.phys = le_phy_qos(ev->c_phy); + qos->ucast.out.phys = le_phy_qos(ev->p_phy); break; case HCI_ROLE_MASTER: qos->ucast.in.interval = p_sdu_interval; @@ -6905,8 +6958,8 @@ static void hci_le_cis_established_evt(struct hci_dev *hdev, void *data, 1000); qos->ucast.out.sdu = ev->c_bn ? le16_to_cpu(ev->c_mtu) : 0; qos->ucast.in.sdu = ev->p_bn ? le16_to_cpu(ev->p_mtu) : 0; - qos->ucast.out.phy = ev->c_phy; - qos->ucast.in.phy = ev->p_phy; + qos->ucast.out.phys = le_phy_qos(ev->c_phy); + qos->ucast.in.phys = le_phy_qos(ev->p_phy); break; } @@ -7221,9 +7274,21 @@ static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev, if (!conn) goto unlock; - if (!ev->status) + if (!ev->status) { memcpy(conn->le_features, ev->features, 248); + /* Update supported PHYs */ + if (!(conn->le_features[1] & HCI_LE_PHY_2M)) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_2M; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_2M; + } + + if (!(conn->le_features[1] & HCI_LE_PHY_CODED)) { + conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED; + conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED; + } + } + if (conn->state == BT_CONFIG) { __u8 status; diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index a9f5b1a68356e..f04a90bce4a94 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -2948,8 +2948,8 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type, if (conn) { struct bt_iso_qos *qos = &conn->iso_qos; - if (qos->bcast.in.phy & BT_ISO_PHY_1M || - qos->bcast.in.phy & BT_ISO_PHY_2M) { + if (qos->bcast.in.phys & BT_ISO_PHY_1M || + qos->bcast.in.phys & BT_ISO_PHY_2M) { cp->scanning_phys |= LE_SCAN_PHY_1M; hci_le_scan_phy_params(phy, type, interval, @@ -2958,7 +2958,7 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type, phy++; } - if (qos->bcast.in.phy & BT_ISO_PHY_CODED) { + if (qos->bcast.in.phys & BT_ISO_PHY_CODED) { cp->scanning_phys |= LE_SCAN_PHY_CODED; hci_le_scan_phy_params(phy, type, interval * 3, @@ -4420,6 +4420,7 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev) if (bis_capable(hdev)) { events[1] |= 0x20; /* LE PA Report */ events[1] |= 0x40; /* LE PA Sync Established */ + events[1] |= 0x80; /* LE PA Sync Lost */ events[3] |= 0x04; /* LE Create BIG Complete */ events[3] |= 0x08; /* LE Terminate BIG Complete */ events[3] |= 0x10; /* LE BIG Sync Established */ @@ -4427,6 +4428,17 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev) events[4] |= 0x02; /* LE BIG Info Advertising Report */ } + if (le_cs_capable(hdev)) { + /* Channel Sounding events */ + events[5] |= 0x08; /* LE CS Read Remote Supported Cap Complete event */ + events[5] |= 0x10; /* LE CS Read Remote FAE Table Complete event */ + events[5] |= 0x20; /* LE CS Security Enable Complete event */ + events[5] |= 0x40; /* LE CS Config Complete event */ + events[5] |= 0x80; /* LE CS Procedure Enable Complete event */ + events[6] |= 0x01; /* LE CS Subevent Result event */ + events[6] |= 0x02; /* LE CS Subevent Result Continue event */ + events[6] |= 0x04; /* LE CS Test End Complete event */ + } return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK, sizeof(events), events, HCI_CMD_TIMEOUT); } @@ -4559,23 +4571,43 @@ static int hci_set_le_support_sync(struct hci_dev *hdev) } /* LE Set Host Feature */ -static int hci_le_set_host_feature_sync(struct hci_dev *hdev) +static int hci_le_set_host_feature_sync(struct hci_dev *hdev, u8 bit, u8 value) { struct hci_cp_le_set_host_feature cp; - if (!iso_capable(hdev)) - return 0; - memset(&cp, 0, sizeof(cp)); /* Connected Isochronous Channels (Host Support) */ - cp.bit_number = 32; - cp.bit_value = iso_enabled(hdev) ? 0x01 : 0x00; + cp.bit_number = bit; + cp.bit_value = value; return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_HOST_FEATURE, sizeof(cp), &cp, HCI_CMD_TIMEOUT); } +/* Set Host Features, each feature needs to be sent separately since + * HCI_OP_LE_SET_HOST_FEATURE doesn't support setting all of them at once. + */ +static int hci_le_set_host_features_sync(struct hci_dev *hdev) +{ + int err; + + if (iso_capable(hdev)) { + /* Connected Isochronous Channels (Host Support) */ + err = hci_le_set_host_feature_sync(hdev, 32, + (iso_enabled(hdev) ? 0x01 : + 0x00)); + if (err) + return err; + } + + if (le_cs_capable(hdev)) + /* Channel Sounding (Host Support) */ + err = hci_le_set_host_feature_sync(hdev, 47, 0x01); + + return err; +} + /* LE Controller init stage 3 command sequence */ static const struct hci_init_stage le_init3[] = { /* HCI_OP_LE_SET_EVENT_MASK */ @@ -4603,7 +4635,7 @@ static const struct hci_init_stage le_init3[] = { /* HCI_OP_WRITE_LE_HOST_SUPPORTED */ HCI_INIT(hci_set_le_support_sync), /* HCI_OP_LE_SET_HOST_FEATURE */ - HCI_INIT(hci_le_set_host_feature_sync), + HCI_INIT(hci_le_set_host_features_sync), {} }; @@ -6896,8 +6928,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data) conn->attempt++; - conn->link_policy = hdev->link_policy; - memset(&cp, 0, sizeof(cp)); bacpy(&cp.bdaddr, &conn->dst); cp.pscan_rep_mode = 0x02; @@ -7418,3 +7448,75 @@ int hci_le_read_remote_features(struct hci_conn *conn) return err; } + +static void pkt_type_changed(struct hci_dev *hdev, void *data, int err) +{ + struct hci_cp_change_conn_ptype *cp = data; + + bt_dev_dbg(hdev, "err %d", err); + + kfree(cp); +} + +static int hci_change_conn_ptype_sync(struct hci_dev *hdev, void *data) +{ + struct hci_cp_change_conn_ptype *cp = data; + + return __hci_cmd_sync_status_sk(hdev, HCI_OP_CHANGE_CONN_PTYPE, + sizeof(*cp), cp, + HCI_EV_PKT_TYPE_CHANGE, + HCI_CMD_TIMEOUT, NULL); +} + +int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_change_conn_ptype *cp; + + cp = kmalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + cp->handle = cpu_to_le16(conn->handle); + cp->pkt_type = cpu_to_le16(pkt_type); + + return hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp, + pkt_type_changed); +} + +static void le_phy_update_complete(struct hci_dev *hdev, void *data, int err) +{ + struct hci_cp_le_set_phy *cp = data; + + bt_dev_dbg(hdev, "err %d", err); + + kfree(cp); +} + +static int hci_le_set_phy_sync(struct hci_dev *hdev, void *data) +{ + struct hci_cp_le_set_phy *cp = data; + + return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_SET_PHY, + sizeof(*cp), cp, + HCI_EV_LE_PHY_UPDATE_COMPLETE, + HCI_CMD_TIMEOUT, NULL); +} + +int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_set_phy *cp; + + cp = kmalloc(sizeof(*cp), GFP_KERNEL); + if (!cp) + return -ENOMEM; + + memset(cp, 0, sizeof(*cp)); + cp->handle = cpu_to_le16(conn->handle); + cp->tx_phys = tx_phys; + cp->rx_phys = rx_phys; + + return hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp, + le_phy_update_complete); +} diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index e36d24a9098b9..1459ab161fd2a 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -361,7 +361,7 @@ static int iso_connect_bis(struct sock *sk) } /* Fail if out PHYs are marked as disabled */ - if (!iso_pi(sk)->qos.bcast.out.phy) { + if (!iso_pi(sk)->qos.bcast.out.phys) { err = -EINVAL; goto unlock; } @@ -458,7 +458,7 @@ static int iso_connect_cis(struct sock *sk) } /* Fail if either PHYs are marked as disabled */ - if (!iso_pi(sk)->qos.ucast.in.phy && !iso_pi(sk)->qos.ucast.out.phy) { + if (!iso_pi(sk)->qos.ucast.in.phys && !iso_pi(sk)->qos.ucast.out.phys) { err = -EINVAL; goto unlock; } @@ -894,7 +894,7 @@ static struct proto iso_proto = { .interval = 10000u, \ .latency = 10u, \ .sdu = 40u, \ - .phy = BT_ISO_PHY_2M, \ + .phys = BT_ISO_PHY_2M, \ .rtn = 2u, \ } @@ -1661,7 +1661,7 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg, static bool check_io_qos(struct bt_iso_io_qos *qos) { /* If no PHY is enable SDU must be 0 */ - if (!qos->phy && qos->sdu) + if (!qos->phys && qos->sdu) return false; if (qos->interval && (qos->interval < 0xff || qos->interval > 0xfffff)) @@ -1670,7 +1670,7 @@ static bool check_io_qos(struct bt_iso_io_qos *qos) if (qos->latency && (qos->latency < 0x05 || qos->latency > 0xfa0)) return false; - if (qos->phy > BT_ISO_PHY_ANY) + if (qos->phys > BT_ISO_PHY_ANY) return false; return true; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 9ee189c815d49..3ba3ce7eaa98a 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -885,7 +885,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, struct bt_power pwr; struct l2cap_conn *conn; int err = 0; - u32 opt; + u32 opt, phys; u16 mtu; u8 mode; @@ -1059,6 +1059,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; + case BT_PHY: + if (sk->sk_state != BT_CONNECTED) { + err = -ENOTCONN; + break; + } + + err = copy_safe_from_sockptr(&phys, sizeof(phys), optval, + optlen); + if (err) + break; + + if (!chan->conn) + break; + + conn = chan->conn; + err = hci_conn_set_phy(conn->hcon, phys); + break; + case BT_MODE: if (!enable_ecred) { err = -ENOPROTOOPT; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c11cdef42b6f6..5be9b8c919490 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -849,6 +849,12 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (cis_peripheral_capable(hdev)) settings |= MGMT_SETTING_CIS_PERIPHERAL; + if (bis_capable(hdev)) + settings |= MGMT_SETTING_ISO_BROADCASTER; + + if (sync_recv_capable(hdev)) + settings |= MGMT_SETTING_ISO_SYNC_RECEIVER; + if (ll_privacy_capable(hdev)) settings |= MGMT_SETTING_LL_PRIVACY; diff --git a/net/bluetooth/mgmt_config.c b/net/bluetooth/mgmt_config.c index c4063d200c0a6..fdcc752c6f13f 100644 --- a/net/bluetooth/mgmt_config.c +++ b/net/bluetooth/mgmt_config.c @@ -11,6 +11,12 @@ #include "mgmt_util.h" #include "mgmt_config.h" +#define HDEV_PARAM_U32(_param_name_) \ + struct {\ + struct mgmt_tlv_hdr entry; \ + __le32 value; \ + } __packed _param_name_ + #define HDEV_PARAM_U16(_param_name_) \ struct {\ struct mgmt_tlv_hdr entry; \ @@ -29,6 +35,12 @@ cpu_to_le16(hdev->_param_name_) \ } +#define TLV_SET_U32(_param_code_, _param_name_) \ + { \ + { cpu_to_le16(_param_code_), sizeof(__u32) }, \ + cpu_to_le32(hdev->_param_name_) \ + } + #define TLV_SET_U8(_param_code_, _param_name_) \ { \ { cpu_to_le16(_param_code_), sizeof(__u8) }, \ @@ -78,6 +90,7 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, HDEV_PARAM_U16(advmon_allowlist_duration); HDEV_PARAM_U16(advmon_no_filter_duration); HDEV_PARAM_U8(enable_advmon_interleave_scan); + HDEV_PARAM_U32(idle_timeout); } __packed rp = { TLV_SET_U16(0x0000, def_page_scan_type), TLV_SET_U16(0x0001, def_page_scan_int), @@ -111,6 +124,7 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, TLV_SET_U16(0x001d, advmon_allowlist_duration), TLV_SET_U16(0x001e, advmon_no_filter_duration), TLV_SET_U8(0x001f, enable_advmon_interleave_scan), + TLV_SET_U32(0x0020, idle_timeout), }; bt_dev_dbg(hdev, "sock %p", sk); @@ -122,6 +136,7 @@ int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, } #define TO_TLV(x) ((struct mgmt_tlv *)(x)) +#define TLV_GET_LE32(tlv) le32_to_cpu(*((__le32 *)(TO_TLV(tlv)->value))) #define TLV_GET_LE16(tlv) le16_to_cpu(*((__le16 *)(TO_TLV(tlv)->value))) #define TLV_GET_U8(tlv) (*((__u8 *)(TO_TLV(tlv)->value))) @@ -191,6 +206,9 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, case 0x001f: exp_type_len = sizeof(u8); break; + case 0x0020: + exp_type_len = sizeof(u32); + break; default: exp_type_len = 0; bt_dev_warn(hdev, "unsupported parameter %u", type); @@ -314,6 +332,9 @@ int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, case 0x0001f: hdev->enable_advmon_interleave_scan = TLV_GET_U8(buffer); break; + case 0x00020: + hdev->idle_timeout = TLV_GET_LE32(buffer); + break; default: bt_dev_warn(hdev, "unsupported parameter %u", type); break;