Networking changes for 5.17.
Core ---- - Defer freeing TCP skbs to the BH handler, whenever possible, or at least perform the freeing outside of the socket lock section to decrease cross-CPU allocator work and improve latency. - Add netdevice refcount tracking to locate sources of netdevice and net namespace refcount leaks. - Make Tx watchdog less intrusive - avoid pausing Tx and restarting all queues from a single CPU removing latency spikes. - Various small optimizations throughout the stack from Eric Dumazet. - Make netdev->dev_addr[] constant, force modifications to go via appropriate helpers to allow us to keep addresses in ordered data structures. - Replace unix_table_lock with per-hash locks, improving performance of bind() calls. - Extend skb drop tracepoint with a drop reason. - Allow SO_MARK and SO_PRIORITY setsockopt under CAP_NET_RAW. BPF --- - New helpers: - bpf_find_vma(), find and inspect VMAs for profiling use cases - bpf_loop(), runtime-bounded loop helper trading some execution time for much faster (if at all converging) verification - bpf_strncmp(), improve performance, avoid compiler flakiness - bpf_get_func_arg(), bpf_get_func_ret(), bpf_get_func_arg_cnt() for tracing programs, all inlined by the verifier - Support BPF relocations (CO-RE) in the kernel loader. - Further the support for BTF_TYPE_TAG annotations. - Allow access to local storage in sleepable helpers. - Convert verifier argument types to a composable form with different attributes which can be shared across types (ro, maybe-null). - Prepare libbpf for upcoming v1.0 release by cleaning up APIs, creating new, extensible ones where missing and deprecating those to be removed. Protocols --------- - WiFi (mac80211/cfg80211): - notify user space about long "come back in N" AP responses, allow it to react to such temporary rejections - allow non-standard VHT MCS 10/11 rates - use coarse time in airtime fairness code to save CPU cycles - Bluetooth: - rework of HCI command execution serialization to use a common queue and work struct, and improve handling errors reported in the middle of a batch of commands - rework HCI event handling to use skb_pull_data, avoiding packet parsing pitfalls - support AOSP Bluetooth Quality Report - SMC: - support net namespaces, following the RDMA model - improve connection establishment latency by pre-clearing buffers - introduce TCP ULP for automatic redirection to SMC - Multi-Path TCP: - support ioctls: SIOCINQ, OUTQ, and OUTQNSD - support socket options: IP_TOS, IP_FREEBIND, IP_TRANSPARENT, IPV6_FREEBIND, and IPV6_TRANSPARENT, TCP_CORK and TCP_NODELAY - support cmsgs: TCP_INQ - improvements in the data scheduler (assigning data to subflows) - support fastclose option (quick shutdown of the full MPTCP connection, similar to TCP RST in regular TCP) - MCTP (Management Component Transport) over serial, as defined by DMTF spec DSP0253 - "MCTP Serial Transport Binding". Driver API ---------- - Support timestamping on bond interfaces in active/passive mode. - Introduce generic phylink link mode validation for drivers which don't have any quirks and where MAC capability bits fully express what's supported. Allow PCS layer to participate in the validation. Convert a number of drivers. - Add support to set/get size of buffers on the Rx rings and size of the tx copybreak buffer via ethtool. - Support offloading TC actions as first-class citizens rather than only as attributes of filters, improve sharing and device resource utilization. - WiFi (mac80211/cfg80211): - support forwarding offload (ndo_fill_forward_path) - support for background radar detection hardware - SA Query Procedures offload on the AP side New hardware / drivers ---------------------- - tsnep - FPGA based TSN endpoint Ethernet MAC used in PLCs with real-time requirements for isochronous communication with protocols like OPC UA Pub/Sub. - Qualcomm BAM-DMUX WWAN - driver for data channels of modems integrated into many older Qualcomm SoCs, e.g. MSM8916 or MSM8974 (qcom_bam_dmux). - Microchip LAN966x multi-port Gigabit AVB/TSN Ethernet Switch driver with support for bridging, VLANs and multicast forwarding (lan966x). - iwlmei driver for co-operating between Intel's WiFi driver and Intel's Active Management Technology (AMT) devices. - mse102x - Vertexcom MSE102x Homeplug GreenPHY chips - Bluetooth: - MediaTek MT7921 SDIO devices - Foxconn MT7922A - Realtek RTL8852AE Drivers ------- - Significantly improve performance in the datapaths of: lan78xx, ax88179_178a, lantiq_xrx200, bnxt. - Intel Ethernet NICs: - igb: support PTP/time PEROUT and EXTTS SDP functions on 82580/i354/i350 adapters - ixgbevf: new PF -> VF mailbox API which avoids the risk of mailbox corruption with ESXi - iavf: support configuration of VLAN features of finer granularity, stacked tags and filtering - ice: PTP support for new E822 devices with sub-ns precision - ice: support firmware activation without reboot - Mellanox Ethernet NICs (mlx5): - expose control over IRQ coalescing mode (CQE vs EQE) via ethtool - support TC forwarding when tunnel encap and decap happen between two ports of the same NIC - dynamically size and allow disabling various features to save resources for running in embedded / SmartNIC scenarios - Broadcom Ethernet NICs (bnxt): - use page frag allocator to improve Rx performance - expose control over IRQ coalescing mode (CQE vs EQE) via ethtool - Other Ethernet NICs: - amd-xgbe: add Ryzen 6000 (Yellow Carp) Ethernet support - Microsoft cloud/virtual NIC (mana): - add XDP support (PASS, DROP, TX) - Mellanox Ethernet switches (mlxsw): - initial support for Spectrum-4 ASICs - VxLAN with IPv6 underlay - Marvell Ethernet switches (prestera): - support flower flow templates - add basic IP forwarding support - NXP embedded Ethernet switches (ocelot & felix): - support Per-Stream Filtering and Policing (PSFP) - enable cut-through forwarding between ports by default - support FDMA to improve packet Rx/Tx to CPU - Other embedded switches: - hellcreek: improve trapping management (STP and PTP) packets - qca8k: support link aggregation and port mirroring - Qualcomm 802.11ax WiFi (ath11k): - qca6390, wcn6855: enable 802.11 power save mode in station mode - BSS color change support - WCN6855 hw2.1 support - 11d scan offload support - scan MAC address randomization support - full monitor mode, only supported on QCN9074 - qca6390/wcn6855: report signal and tx bitrate - qca6390: rfkill support - qca6390/wcn6855: regdb.bin support - Intel WiFi (iwlwifi): - support SAR GEO Offset Mapping (SGOM) and Time-Aware-SAR (TAS) in cooperation with the BIOS - support for Optimized Connectivity Experience (OCE) scan - support firmware API version 68 - lots of preparatory work for the upcoming Bz device family - MediaTek WiFi (mt76): - Specific Absorption Rate (SAR) support - mt7921: 160 MHz channel support - RealTek WiFi (rtw88): - Specific Absorption Rate (SAR) support - scan offload - Other WiFi NICs - ath10k: support fetching (pre-)calibration data from nvmem - brcmfmac: configure keep-alive packet on suspend - wcn36xx: beacon filter support Signed-off-by: Jakub Kicinski <kuba@kernel.org> -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE6jPA+I1ugmIBA4hXMUZtbf5SIrsFAmHbkZAACgkQMUZtbf5S IruYkQ//XX7BggcwBfukPK83j0dONolClijqKcKR08g4vB5L8GXvv6OErKIWrh4k h8JanCH352ZkbCSw3MvFdm825UYQv8vPMd6Qks/LJ4aSKqCuy4MIlAo+yOw4Km3O i7++lRfma6DqHHI59wvLjWoxZSPu8lL+rI8UsZ5qMOlnNlGAOXsNrzRjaqQ3FddY AMxZeBUtrPqUCCQZFq3U8apkYzUp7CA/3XR9zRcja3uPbrtOV2G+4whRF90qGNWz Tm/QvJ9F/Ab292cbhxR4KuaQ3hUhaCQyDjbZk3+FZzZpAVhYTVqcNjny6+yXmbiP NXRtwemnl1NlWKMnJM8lEeY48u626tRIkxA/Wtd61uoO5uKUSxfGP+UpUi+DfXbF yIw50VQ7L2bpxXP/HjtmhVgZDaWKYyh22Zw4Hp/muMJz0hgUB0KODY3tf2jUWbjJ 0oEgocWyzhhwMQKqupTDCIaRgIs2ewYr4ZrFDhI3HnHC/vv1VjoPRUPIyxwppD2N cXvZb3B1sWK8iX5gCbISGzyU4bB7I0rvJSTU42ueti7n6NqRFZ79qHQpYnnY+JdO z1qOwY/d/yWfBoXVKRtRg2qz6CdEt5BQklwAgVEBgrFpf58gp694EwGMb1htY14J r/k9bVpmyIFpUnBH2CPMRfBVA3tUTqzyzzFV4AMw40NYLKmhLdo= =KLm3 -----END PGP SIGNATURE----- Merge tag '5.17-net-next' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next Pull networking updates from Jakub Kicinski: "Core ---- - Defer freeing TCP skbs to the BH handler, whenever possible, or at least perform the freeing outside of the socket lock section to decrease cross-CPU allocator work and improve latency. - Add netdevice refcount tracking to locate sources of netdevice and net namespace refcount leaks. - Make Tx watchdog less intrusive - avoid pausing Tx and restarting all queues from a single CPU removing latency spikes. - Various small optimizations throughout the stack from Eric Dumazet. - Make netdev->dev_addr[] constant, force modifications to go via appropriate helpers to allow us to keep addresses in ordered data structures. - Replace unix_table_lock with per-hash locks, improving performance of bind() calls. - Extend skb drop tracepoint with a drop reason. - Allow SO_MARK and SO_PRIORITY setsockopt under CAP_NET_RAW. BPF --- - New helpers: - bpf_find_vma(), find and inspect VMAs for profiling use cases - bpf_loop(), runtime-bounded loop helper trading some execution time for much faster (if at all converging) verification - bpf_strncmp(), improve performance, avoid compiler flakiness - bpf_get_func_arg(), bpf_get_func_ret(), bpf_get_func_arg_cnt() for tracing programs, all inlined by the verifier - Support BPF relocations (CO-RE) in the kernel loader. - Further the support for BTF_TYPE_TAG annotations. - Allow access to local storage in sleepable helpers. - Convert verifier argument types to a composable form with different attributes which can be shared across types (ro, maybe-null). - Prepare libbpf for upcoming v1.0 release by cleaning up APIs, creating new, extensible ones where missing and deprecating those to be removed. Protocols --------- - WiFi (mac80211/cfg80211): - notify user space about long "come back in N" AP responses, allow it to react to such temporary rejections - allow non-standard VHT MCS 10/11 rates - use coarse time in airtime fairness code to save CPU cycles - Bluetooth: - rework of HCI command execution serialization to use a common queue and work struct, and improve handling errors reported in the middle of a batch of commands - rework HCI event handling to use skb_pull_data, avoiding packet parsing pitfalls - support AOSP Bluetooth Quality Report - SMC: - support net namespaces, following the RDMA model - improve connection establishment latency by pre-clearing buffers - introduce TCP ULP for automatic redirection to SMC - Multi-Path TCP: - support ioctls: SIOCINQ, OUTQ, and OUTQNSD - support socket options: IP_TOS, IP_FREEBIND, IP_TRANSPARENT, IPV6_FREEBIND, and IPV6_TRANSPARENT, TCP_CORK and TCP_NODELAY - support cmsgs: TCP_INQ - improvements in the data scheduler (assigning data to subflows) - support fastclose option (quick shutdown of the full MPTCP connection, similar to TCP RST in regular TCP) - MCTP (Management Component Transport) over serial, as defined by DMTF spec DSP0253 - "MCTP Serial Transport Binding". Driver API ---------- - Support timestamping on bond interfaces in active/passive mode. - Introduce generic phylink link mode validation for drivers which don't have any quirks and where MAC capability bits fully express what's supported. Allow PCS layer to participate in the validation. Convert a number of drivers. - Add support to set/get size of buffers on the Rx rings and size of the tx copybreak buffer via ethtool. - Support offloading TC actions as first-class citizens rather than only as attributes of filters, improve sharing and device resource utilization. - WiFi (mac80211/cfg80211): - support forwarding offload (ndo_fill_forward_path) - support for background radar detection hardware - SA Query Procedures offload on the AP side New hardware / drivers ---------------------- - tsnep - FPGA based TSN endpoint Ethernet MAC used in PLCs with real-time requirements for isochronous communication with protocols like OPC UA Pub/Sub. - Qualcomm BAM-DMUX WWAN - driver for data channels of modems integrated into many older Qualcomm SoCs, e.g. MSM8916 or MSM8974 (qcom_bam_dmux). - Microchip LAN966x multi-port Gigabit AVB/TSN Ethernet Switch driver with support for bridging, VLANs and multicast forwarding (lan966x). - iwlmei driver for co-operating between Intel's WiFi driver and Intel's Active Management Technology (AMT) devices. - mse102x - Vertexcom MSE102x Homeplug GreenPHY chips - Bluetooth: - MediaTek MT7921 SDIO devices - Foxconn MT7922A - Realtek RTL8852AE Drivers ------- - Significantly improve performance in the datapaths of: lan78xx, ax88179_178a, lantiq_xrx200, bnxt. - Intel Ethernet NICs: - igb: support PTP/time PEROUT and EXTTS SDP functions on 82580/i354/i350 adapters - ixgbevf: new PF -> VF mailbox API which avoids the risk of mailbox corruption with ESXi - iavf: support configuration of VLAN features of finer granularity, stacked tags and filtering - ice: PTP support for new E822 devices with sub-ns precision - ice: support firmware activation without reboot - Mellanox Ethernet NICs (mlx5): - expose control over IRQ coalescing mode (CQE vs EQE) via ethtool - support TC forwarding when tunnel encap and decap happen between two ports of the same NIC - dynamically size and allow disabling various features to save resources for running in embedded / SmartNIC scenarios - Broadcom Ethernet NICs (bnxt): - use page frag allocator to improve Rx performance - expose control over IRQ coalescing mode (CQE vs EQE) via ethtool - Other Ethernet NICs: - amd-xgbe: add Ryzen 6000 (Yellow Carp) Ethernet support - Microsoft cloud/virtual NIC (mana): - add XDP support (PASS, DROP, TX) - Mellanox Ethernet switches (mlxsw): - initial support for Spectrum-4 ASICs - VxLAN with IPv6 underlay - Marvell Ethernet switches (prestera): - support flower flow templates - add basic IP forwarding support - NXP embedded Ethernet switches (ocelot & felix): - support Per-Stream Filtering and Policing (PSFP) - enable cut-through forwarding between ports by default - support FDMA to improve packet Rx/Tx to CPU - Other embedded switches: - hellcreek: improve trapping management (STP and PTP) packets - qca8k: support link aggregation and port mirroring - Qualcomm 802.11ax WiFi (ath11k): - qca6390, wcn6855: enable 802.11 power save mode in station mode - BSS color change support - WCN6855 hw2.1 support - 11d scan offload support - scan MAC address randomization support - full monitor mode, only supported on QCN9074 - qca6390/wcn6855: report signal and tx bitrate - qca6390: rfkill support - qca6390/wcn6855: regdb.bin support - Intel WiFi (iwlwifi): - support SAR GEO Offset Mapping (SGOM) and Time-Aware-SAR (TAS) in cooperation with the BIOS - support for Optimized Connectivity Experience (OCE) scan - support firmware API version 68 - lots of preparatory work for the upcoming Bz device family - MediaTek WiFi (mt76): - Specific Absorption Rate (SAR) support - mt7921: 160 MHz channel support - RealTek WiFi (rtw88): - Specific Absorption Rate (SAR) support - scan offload - Other WiFi NICs - ath10k: support fetching (pre-)calibration data from nvmem - brcmfmac: configure keep-alive packet on suspend - wcn36xx: beacon filter support" * tag '5.17-net-next' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2048 commits) tcp: tcp_send_challenge_ack delete useless param `skb` net/qla3xxx: Remove useless DMA-32 fallback configuration rocker: Remove useless DMA-32 fallback configuration hinic: Remove useless DMA-32 fallback configuration lan743x: Remove useless DMA-32 fallback configuration net: enetc: Remove useless DMA-32 fallback configuration cxgb4vf: Remove useless DMA-32 fallback configuration cxgb4: Remove useless DMA-32 fallback configuration cxgb3: Remove useless DMA-32 fallback configuration bnx2x: Remove useless DMA-32 fallback configuration et131x: Remove useless DMA-32 fallback configuration be2net: Remove useless DMA-32 fallback configuration vmxnet3: Remove useless DMA-32 fallback configuration bna: Simplify DMA setting net: alteon: Simplify DMA setting myri10ge: Simplify DMA setting qlcnic: Simplify DMA setting net: allwinner: Fix print format page_pool: remove spinlock in page_pool_refill_alloc_cache() amt: fix wrong return type of amt_send_membership_update() ...
This commit is contained in:
commit
8efd0d9c31
|
@ -3,7 +3,7 @@ BPF Type Format (BTF)
|
|||
=====================
|
||||
|
||||
1. Introduction
|
||||
***************
|
||||
===============
|
||||
|
||||
BTF (BPF Type Format) is the metadata format which encodes the debug info
|
||||
related to BPF program/map. The name BTF was used initially to describe data
|
||||
|
@ -30,7 +30,7 @@ sections are discussed in details in :ref:`BTF_Type_String`.
|
|||
.. _BTF_Type_String:
|
||||
|
||||
2. BTF Type and String Encoding
|
||||
*******************************
|
||||
===============================
|
||||
|
||||
The file ``include/uapi/linux/btf.h`` provides high-level definition of how
|
||||
types/strings are encoded.
|
||||
|
@ -57,13 +57,13 @@ little-endian target. The ``btf_header`` is designed to be extensible with
|
|||
generated.
|
||||
|
||||
2.1 String Encoding
|
||||
===================
|
||||
-------------------
|
||||
|
||||
The first string in the string section must be a null string. The rest of
|
||||
string table is a concatenation of other null-terminated strings.
|
||||
|
||||
2.2 Type Encoding
|
||||
=================
|
||||
-----------------
|
||||
|
||||
The type id ``0`` is reserved for ``void`` type. The type section is parsed
|
||||
sequentially and type id is assigned to each recognized type starting from id
|
||||
|
@ -86,6 +86,7 @@ sequentially and type id is assigned to each recognized type starting from id
|
|||
#define BTF_KIND_DATASEC 15 /* Section */
|
||||
#define BTF_KIND_FLOAT 16 /* Floating point */
|
||||
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
|
||||
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */
|
||||
|
||||
Note that the type section encodes debug info, not just pure types.
|
||||
``BTF_KIND_FUNC`` is not a type, and it represents a defined subprogram.
|
||||
|
@ -107,7 +108,7 @@ Each type contains the following common data::
|
|||
* "size" tells the size of the type it is describing.
|
||||
*
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
|
||||
* FUNC, FUNC_PROTO and DECL_TAG.
|
||||
* FUNC, FUNC_PROTO, DECL_TAG and TYPE_TAG.
|
||||
* "type" is a type_id referring to another type.
|
||||
*/
|
||||
union {
|
||||
|
@ -492,8 +493,18 @@ the attribute is applied to a ``struct``/``union`` member or
|
|||
a ``func`` argument, and ``btf_decl_tag.component_idx`` should be a
|
||||
valid index (starting from 0) pointing to a member or an argument.
|
||||
|
||||
2.2.17 BTF_KIND_TYPE_TAG
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``struct btf_type`` encoding requirement:
|
||||
* ``name_off``: offset to a non-empty string
|
||||
* ``info.kind_flag``: 0
|
||||
* ``info.kind``: BTF_KIND_TYPE_TAG
|
||||
* ``info.vlen``: 0
|
||||
* ``type``: the type with ``btf_type_tag`` attribute
|
||||
|
||||
3. BTF Kernel API
|
||||
*****************
|
||||
=================
|
||||
|
||||
The following bpf syscall command involves BTF:
|
||||
* BPF_BTF_LOAD: load a blob of BTF data into kernel
|
||||
|
@ -536,14 +547,14 @@ The workflow typically looks like:
|
|||
|
||||
|
||||
3.1 BPF_BTF_LOAD
|
||||
================
|
||||
----------------
|
||||
|
||||
Load a blob of BTF data into kernel. A blob of data, described in
|
||||
:ref:`BTF_Type_String`, can be directly loaded into the kernel. A ``btf_fd``
|
||||
is returned to a userspace.
|
||||
|
||||
3.2 BPF_MAP_CREATE
|
||||
==================
|
||||
------------------
|
||||
|
||||
A map can be created with ``btf_fd`` and specified key/value type id.::
|
||||
|
||||
|
@ -570,7 +581,7 @@ automatically.
|
|||
.. _BPF_Prog_Load:
|
||||
|
||||
3.3 BPF_PROG_LOAD
|
||||
=================
|
||||
-----------------
|
||||
|
||||
During prog_load, func_info and line_info can be passed to kernel with proper
|
||||
values for the following attributes:
|
||||
|
@ -620,7 +631,7 @@ For line_info, the line number and column number are defined as below:
|
|||
#define BPF_LINE_INFO_LINE_COL(line_col) ((line_col) & 0x3ff)
|
||||
|
||||
3.4 BPF_{PROG,MAP}_GET_NEXT_ID
|
||||
==============================
|
||||
------------------------------
|
||||
|
||||
In kernel, every loaded program, map or btf has a unique id. The id won't
|
||||
change during the lifetime of a program, map, or btf.
|
||||
|
@ -630,13 +641,13 @@ each command, to user space, for bpf program or maps, respectively, so an
|
|||
inspection tool can inspect all programs and maps.
|
||||
|
||||
3.5 BPF_{PROG,MAP}_GET_FD_BY_ID
|
||||
===============================
|
||||
-------------------------------
|
||||
|
||||
An introspection tool cannot use id to get details about program or maps.
|
||||
A file descriptor needs to be obtained first for reference-counting purpose.
|
||||
|
||||
3.6 BPF_OBJ_GET_INFO_BY_FD
|
||||
==========================
|
||||
--------------------------
|
||||
|
||||
Once a program/map fd is acquired, an introspection tool can get the detailed
|
||||
information from kernel about this fd, some of which are BTF-related. For
|
||||
|
@ -645,7 +656,7 @@ example, ``bpf_map_info`` returns ``btf_id`` and key/value type ids.
|
|||
bpf byte codes, and jited_line_info.
|
||||
|
||||
3.7 BPF_BTF_GET_FD_BY_ID
|
||||
========================
|
||||
------------------------
|
||||
|
||||
With ``btf_id`` obtained in ``bpf_map_info`` and ``bpf_prog_info``, bpf
|
||||
syscall command BPF_BTF_GET_FD_BY_ID can retrieve a btf fd. Then, with
|
||||
|
@ -657,10 +668,10 @@ tool has full btf knowledge and is able to pretty print map key/values, dump
|
|||
func signatures and line info, along with byte/jit codes.
|
||||
|
||||
4. ELF File Format Interface
|
||||
****************************
|
||||
============================
|
||||
|
||||
4.1 .BTF section
|
||||
================
|
||||
----------------
|
||||
|
||||
The .BTF section contains type and string data. The format of this section is
|
||||
same as the one describe in :ref:`BTF_Type_String`.
|
||||
|
@ -668,7 +679,7 @@ same as the one describe in :ref:`BTF_Type_String`.
|
|||
.. _BTF_Ext_Section:
|
||||
|
||||
4.2 .BTF.ext section
|
||||
====================
|
||||
--------------------
|
||||
|
||||
The .BTF.ext section encodes func_info and line_info which needs loader
|
||||
manipulation before loading into the kernel.
|
||||
|
@ -732,7 +743,7 @@ bpf_insn``. For ELF API, the ``insn_off`` is the byte offset from the
|
|||
beginning of section (``btf_ext_info_sec->sec_name_off``).
|
||||
|
||||
4.2 .BTF_ids section
|
||||
====================
|
||||
--------------------
|
||||
|
||||
The .BTF_ids section encodes BTF ID values that are used within the kernel.
|
||||
|
||||
|
@ -793,10 +804,10 @@ All the BTF ID lists and sets are compiled in the .BTF_ids section and
|
|||
resolved during the linking phase of kernel build by ``resolve_btfids`` tool.
|
||||
|
||||
5. Using BTF
|
||||
************
|
||||
============
|
||||
|
||||
5.1 bpftool map pretty print
|
||||
============================
|
||||
----------------------------
|
||||
|
||||
With BTF, the map key/value can be printed based on fields rather than simply
|
||||
raw bytes. This is especially valuable for large structure or if your data
|
||||
|
@ -838,7 +849,7 @@ bpftool is able to pretty print like below:
|
|||
]
|
||||
|
||||
5.2 bpftool prog dump
|
||||
=====================
|
||||
---------------------
|
||||
|
||||
The following is an example showing how func_info and line_info can help prog
|
||||
dump with better kernel symbol names, function prototypes and line
|
||||
|
@ -872,7 +883,7 @@ information.::
|
|||
[...]
|
||||
|
||||
5.3 Verifier Log
|
||||
================
|
||||
----------------
|
||||
|
||||
The following is an example of how line_info can help debugging verification
|
||||
failure.::
|
||||
|
@ -898,7 +909,7 @@ failure.::
|
|||
R2 offset is outside of the packet
|
||||
|
||||
6. BTF Generation
|
||||
*****************
|
||||
=================
|
||||
|
||||
You need latest pahole
|
||||
|
||||
|
@ -1005,6 +1016,6 @@ format.::
|
|||
.long 8206 # Line 8 Col 14
|
||||
|
||||
7. Testing
|
||||
**********
|
||||
==========
|
||||
|
||||
Kernel bpf selftest `test_btf.c` provides extensive set of BTF-related tests.
|
||||
|
|
|
@ -0,0 +1,376 @@
|
|||
|
||||
===================
|
||||
Classic BPF vs eBPF
|
||||
===================
|
||||
|
||||
eBPF is designed to be JITed with one to one mapping, which can also open up
|
||||
the possibility for GCC/LLVM compilers to generate optimized eBPF code through
|
||||
an eBPF backend that performs almost as fast as natively compiled code.
|
||||
|
||||
Some core changes of the eBPF format from classic BPF:
|
||||
|
||||
- Number of registers increase from 2 to 10:
|
||||
|
||||
The old format had two registers A and X, and a hidden frame pointer. The
|
||||
new layout extends this to be 10 internal registers and a read-only frame
|
||||
pointer. Since 64-bit CPUs are passing arguments to functions via registers
|
||||
the number of args from eBPF program to in-kernel function is restricted
|
||||
to 5 and one register is used to accept return value from an in-kernel
|
||||
function. Natively, x86_64 passes first 6 arguments in registers, aarch64/
|
||||
sparcv9/mips64 have 7 - 8 registers for arguments; x86_64 has 6 callee saved
|
||||
registers, and aarch64/sparcv9/mips64 have 11 or more callee saved registers.
|
||||
|
||||
Thus, all eBPF registers map one to one to HW registers on x86_64, aarch64,
|
||||
etc, and eBPF calling convention maps directly to ABIs used by the kernel on
|
||||
64-bit architectures.
|
||||
|
||||
On 32-bit architectures JIT may map programs that use only 32-bit arithmetic
|
||||
and may let more complex programs to be interpreted.
|
||||
|
||||
R0 - R5 are scratch registers and eBPF program needs spill/fill them if
|
||||
necessary across calls. Note that there is only one eBPF program (== one
|
||||
eBPF main routine) and it cannot call other eBPF functions, it can only
|
||||
call predefined in-kernel functions, though.
|
||||
|
||||
- Register width increases from 32-bit to 64-bit:
|
||||
|
||||
Still, the semantics of the original 32-bit ALU operations are preserved
|
||||
via 32-bit subregisters. All eBPF registers are 64-bit with 32-bit lower
|
||||
subregisters that zero-extend into 64-bit if they are being written to.
|
||||
That behavior maps directly to x86_64 and arm64 subregister definition, but
|
||||
makes other JITs more difficult.
|
||||
|
||||
32-bit architectures run 64-bit eBPF programs via interpreter.
|
||||
Their JITs may convert BPF programs that only use 32-bit subregisters into
|
||||
native instruction set and let the rest being interpreted.
|
||||
|
||||
Operation is 64-bit, because on 64-bit architectures, pointers are also
|
||||
64-bit wide, and we want to pass 64-bit values in/out of kernel functions,
|
||||
so 32-bit eBPF registers would otherwise require to define register-pair
|
||||
ABI, thus, there won't be able to use a direct eBPF register to HW register
|
||||
mapping and JIT would need to do combine/split/move operations for every
|
||||
register in and out of the function, which is complex, bug prone and slow.
|
||||
Another reason is the use of atomic 64-bit counters.
|
||||
|
||||
- Conditional jt/jf targets replaced with jt/fall-through:
|
||||
|
||||
While the original design has constructs such as ``if (cond) jump_true;
|
||||
else jump_false;``, they are being replaced into alternative constructs like
|
||||
``if (cond) jump_true; /* else fall-through */``.
|
||||
|
||||
- Introduces bpf_call insn and register passing convention for zero overhead
|
||||
calls from/to other kernel functions:
|
||||
|
||||
Before an in-kernel function call, the eBPF program needs to
|
||||
place function arguments into R1 to R5 registers to satisfy calling
|
||||
convention, then the interpreter will take them from registers and pass
|
||||
to in-kernel function. If R1 - R5 registers are mapped to CPU registers
|
||||
that are used for argument passing on given architecture, the JIT compiler
|
||||
doesn't need to emit extra moves. Function arguments will be in the correct
|
||||
registers and BPF_CALL instruction will be JITed as single 'call' HW
|
||||
instruction. This calling convention was picked to cover common call
|
||||
situations without performance penalty.
|
||||
|
||||
After an in-kernel function call, R1 - R5 are reset to unreadable and R0 has
|
||||
a return value of the function. Since R6 - R9 are callee saved, their state
|
||||
is preserved across the call.
|
||||
|
||||
For example, consider three C functions::
|
||||
|
||||
u64 f1() { return (*_f2)(1); }
|
||||
u64 f2(u64 a) { return f3(a + 1, a); }
|
||||
u64 f3(u64 a, u64 b) { return a - b; }
|
||||
|
||||
GCC can compile f1, f3 into x86_64::
|
||||
|
||||
f1:
|
||||
movl $1, %edi
|
||||
movq _f2(%rip), %rax
|
||||
jmp *%rax
|
||||
f3:
|
||||
movq %rdi, %rax
|
||||
subq %rsi, %rax
|
||||
ret
|
||||
|
||||
Function f2 in eBPF may look like::
|
||||
|
||||
f2:
|
||||
bpf_mov R2, R1
|
||||
bpf_add R1, 1
|
||||
bpf_call f3
|
||||
bpf_exit
|
||||
|
||||
If f2 is JITed and the pointer stored to ``_f2``. The calls f1 -> f2 -> f3 and
|
||||
returns will be seamless. Without JIT, __bpf_prog_run() interpreter needs to
|
||||
be used to call into f2.
|
||||
|
||||
For practical reasons all eBPF programs have only one argument 'ctx' which is
|
||||
already placed into R1 (e.g. on __bpf_prog_run() startup) and the programs
|
||||
can call kernel functions with up to 5 arguments. Calls with 6 or more arguments
|
||||
are currently not supported, but these restrictions can be lifted if necessary
|
||||
in the future.
|
||||
|
||||
On 64-bit architectures all register map to HW registers one to one. For
|
||||
example, x86_64 JIT compiler can map them as ...
|
||||
|
||||
::
|
||||
|
||||
R0 - rax
|
||||
R1 - rdi
|
||||
R2 - rsi
|
||||
R3 - rdx
|
||||
R4 - rcx
|
||||
R5 - r8
|
||||
R6 - rbx
|
||||
R7 - r13
|
||||
R8 - r14
|
||||
R9 - r15
|
||||
R10 - rbp
|
||||
|
||||
... since x86_64 ABI mandates rdi, rsi, rdx, rcx, r8, r9 for argument passing
|
||||
and rbx, r12 - r15 are callee saved.
|
||||
|
||||
Then the following eBPF pseudo-program::
|
||||
|
||||
bpf_mov R6, R1 /* save ctx */
|
||||
bpf_mov R2, 2
|
||||
bpf_mov R3, 3
|
||||
bpf_mov R4, 4
|
||||
bpf_mov R5, 5
|
||||
bpf_call foo
|
||||
bpf_mov R7, R0 /* save foo() return value */
|
||||
bpf_mov R1, R6 /* restore ctx for next call */
|
||||
bpf_mov R2, 6
|
||||
bpf_mov R3, 7
|
||||
bpf_mov R4, 8
|
||||
bpf_mov R5, 9
|
||||
bpf_call bar
|
||||
bpf_add R0, R7
|
||||
bpf_exit
|
||||
|
||||
After JIT to x86_64 may look like::
|
||||
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
sub $0x228,%rsp
|
||||
mov %rbx,-0x228(%rbp)
|
||||
mov %r13,-0x220(%rbp)
|
||||
mov %rdi,%rbx
|
||||
mov $0x2,%esi
|
||||
mov $0x3,%edx
|
||||
mov $0x4,%ecx
|
||||
mov $0x5,%r8d
|
||||
callq foo
|
||||
mov %rax,%r13
|
||||
mov %rbx,%rdi
|
||||
mov $0x6,%esi
|
||||
mov $0x7,%edx
|
||||
mov $0x8,%ecx
|
||||
mov $0x9,%r8d
|
||||
callq bar
|
||||
add %r13,%rax
|
||||
mov -0x228(%rbp),%rbx
|
||||
mov -0x220(%rbp),%r13
|
||||
leaveq
|
||||
retq
|
||||
|
||||
Which is in this example equivalent in C to::
|
||||
|
||||
u64 bpf_filter(u64 ctx)
|
||||
{
|
||||
return foo(ctx, 2, 3, 4, 5) + bar(ctx, 6, 7, 8, 9);
|
||||
}
|
||||
|
||||
In-kernel functions foo() and bar() with prototype: u64 (*)(u64 arg1, u64
|
||||
arg2, u64 arg3, u64 arg4, u64 arg5); will receive arguments in proper
|
||||
registers and place their return value into ``%rax`` which is R0 in eBPF.
|
||||
Prologue and epilogue are emitted by JIT and are implicit in the
|
||||
interpreter. R0-R5 are scratch registers, so eBPF program needs to preserve
|
||||
them across the calls as defined by calling convention.
|
||||
|
||||
For example the following program is invalid::
|
||||
|
||||
bpf_mov R1, 1
|
||||
bpf_call foo
|
||||
bpf_mov R0, R1
|
||||
bpf_exit
|
||||
|
||||
After the call the registers R1-R5 contain junk values and cannot be read.
|
||||
An in-kernel verifier.rst is used to validate eBPF programs.
|
||||
|
||||
Also in the new design, eBPF is limited to 4096 insns, which means that any
|
||||
program will terminate quickly and will only call a fixed number of kernel
|
||||
functions. Original BPF and eBPF are two operand instructions,
|
||||
which helps to do one-to-one mapping between eBPF insn and x86 insn during JIT.
|
||||
|
||||
The input context pointer for invoking the interpreter function is generic,
|
||||
its content is defined by a specific use case. For seccomp register R1 points
|
||||
to seccomp_data, for converted BPF filters R1 points to a skb.
|
||||
|
||||
A program, that is translated internally consists of the following elements::
|
||||
|
||||
op:16, jt:8, jf:8, k:32 ==> op:8, dst_reg:4, src_reg:4, off:16, imm:32
|
||||
|
||||
So far 87 eBPF instructions were implemented. 8-bit 'op' opcode field
|
||||
has room for new instructions. Some of them may use 16/24/32 byte encoding. New
|
||||
instructions must be multiple of 8 bytes to preserve backward compatibility.
|
||||
|
||||
eBPF is a general purpose RISC instruction set. Not every register and
|
||||
every instruction are used during translation from original BPF to eBPF.
|
||||
For example, socket filters are not using ``exclusive add`` instruction, but
|
||||
tracing filters may do to maintain counters of events, for example. Register R9
|
||||
is not used by socket filters either, but more complex filters may be running
|
||||
out of registers and would have to resort to spill/fill to stack.
|
||||
|
||||
eBPF can be used as a generic assembler for last step performance
|
||||
optimizations, socket filters and seccomp are using it as assembler. Tracing
|
||||
filters may use it as assembler to generate code from kernel. In kernel usage
|
||||
may not be bounded by security considerations, since generated eBPF code
|
||||
may be optimizing internal code path and not being exposed to the user space.
|
||||
Safety of eBPF can come from the verifier.rst. In such use cases as
|
||||
described, it may be used as safe instruction set.
|
||||
|
||||
Just like the original BPF, eBPF runs within a controlled environment,
|
||||
is deterministic and the kernel can easily prove that. The safety of the program
|
||||
can be determined in two steps: first step does depth-first-search to disallow
|
||||
loops and other CFG validation; second step starts from the first insn and
|
||||
descends all possible paths. It simulates execution of every insn and observes
|
||||
the state change of registers and stack.
|
||||
|
||||
opcode encoding
|
||||
===============
|
||||
|
||||
eBPF is reusing most of the opcode encoding from classic to simplify conversion
|
||||
of classic BPF to eBPF.
|
||||
|
||||
For arithmetic and jump instructions the 8-bit 'code' field is divided into three
|
||||
parts::
|
||||
|
||||
+----------------+--------+--------------------+
|
||||
| 4 bits | 1 bit | 3 bits |
|
||||
| operation code | source | instruction class |
|
||||
+----------------+--------+--------------------+
|
||||
(MSB) (LSB)
|
||||
|
||||
Three LSB bits store instruction class which is one of:
|
||||
|
||||
=================== ===============
|
||||
Classic BPF classes eBPF classes
|
||||
=================== ===============
|
||||
BPF_LD 0x00 BPF_LD 0x00
|
||||
BPF_LDX 0x01 BPF_LDX 0x01
|
||||
BPF_ST 0x02 BPF_ST 0x02
|
||||
BPF_STX 0x03 BPF_STX 0x03
|
||||
BPF_ALU 0x04 BPF_ALU 0x04
|
||||
BPF_JMP 0x05 BPF_JMP 0x05
|
||||
BPF_RET 0x06 BPF_JMP32 0x06
|
||||
BPF_MISC 0x07 BPF_ALU64 0x07
|
||||
=================== ===============
|
||||
|
||||
The 4th bit encodes the source operand ...
|
||||
|
||||
::
|
||||
|
||||
BPF_K 0x00
|
||||
BPF_X 0x08
|
||||
|
||||
* in classic BPF, this means::
|
||||
|
||||
BPF_SRC(code) == BPF_X - use register X as source operand
|
||||
BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
|
||||
|
||||
* in eBPF, this means::
|
||||
|
||||
BPF_SRC(code) == BPF_X - use 'src_reg' register as source operand
|
||||
BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
|
||||
|
||||
... and four MSB bits store operation code.
|
||||
|
||||
If BPF_CLASS(code) == BPF_ALU or BPF_ALU64 [ in eBPF ], BPF_OP(code) is one of::
|
||||
|
||||
BPF_ADD 0x00
|
||||
BPF_SUB 0x10
|
||||
BPF_MUL 0x20
|
||||
BPF_DIV 0x30
|
||||
BPF_OR 0x40
|
||||
BPF_AND 0x50
|
||||
BPF_LSH 0x60
|
||||
BPF_RSH 0x70
|
||||
BPF_NEG 0x80
|
||||
BPF_MOD 0x90
|
||||
BPF_XOR 0xa0
|
||||
BPF_MOV 0xb0 /* eBPF only: mov reg to reg */
|
||||
BPF_ARSH 0xc0 /* eBPF only: sign extending shift right */
|
||||
BPF_END 0xd0 /* eBPF only: endianness conversion */
|
||||
|
||||
If BPF_CLASS(code) == BPF_JMP or BPF_JMP32 [ in eBPF ], BPF_OP(code) is one of::
|
||||
|
||||
BPF_JA 0x00 /* BPF_JMP only */
|
||||
BPF_JEQ 0x10
|
||||
BPF_JGT 0x20
|
||||
BPF_JGE 0x30
|
||||
BPF_JSET 0x40
|
||||
BPF_JNE 0x50 /* eBPF only: jump != */
|
||||
BPF_JSGT 0x60 /* eBPF only: signed '>' */
|
||||
BPF_JSGE 0x70 /* eBPF only: signed '>=' */
|
||||
BPF_CALL 0x80 /* eBPF BPF_JMP only: function call */
|
||||
BPF_EXIT 0x90 /* eBPF BPF_JMP only: function return */
|
||||
BPF_JLT 0xa0 /* eBPF only: unsigned '<' */
|
||||
BPF_JLE 0xb0 /* eBPF only: unsigned '<=' */
|
||||
BPF_JSLT 0xc0 /* eBPF only: signed '<' */
|
||||
BPF_JSLE 0xd0 /* eBPF only: signed '<=' */
|
||||
|
||||
So BPF_ADD | BPF_X | BPF_ALU means 32-bit addition in both classic BPF
|
||||
and eBPF. There are only two registers in classic BPF, so it means A += X.
|
||||
In eBPF it means dst_reg = (u32) dst_reg + (u32) src_reg; similarly,
|
||||
BPF_XOR | BPF_K | BPF_ALU means A ^= imm32 in classic BPF and analogous
|
||||
src_reg = (u32) src_reg ^ (u32) imm32 in eBPF.
|
||||
|
||||
Classic BPF is using BPF_MISC class to represent A = X and X = A moves.
|
||||
eBPF is using BPF_MOV | BPF_X | BPF_ALU code instead. Since there are no
|
||||
BPF_MISC operations in eBPF, the class 7 is used as BPF_ALU64 to mean
|
||||
exactly the same operations as BPF_ALU, but with 64-bit wide operands
|
||||
instead. So BPF_ADD | BPF_X | BPF_ALU64 means 64-bit addition, i.e.:
|
||||
dst_reg = dst_reg + src_reg
|
||||
|
||||
Classic BPF wastes the whole BPF_RET class to represent a single ``ret``
|
||||
operation. Classic BPF_RET | BPF_K means copy imm32 into return register
|
||||
and perform function exit. eBPF is modeled to match CPU, so BPF_JMP | BPF_EXIT
|
||||
in eBPF means function exit only. The eBPF program needs to store return
|
||||
value into register R0 before doing a BPF_EXIT. Class 6 in eBPF is used as
|
||||
BPF_JMP32 to mean exactly the same operations as BPF_JMP, but with 32-bit wide
|
||||
operands for the comparisons instead.
|
||||
|
||||
For load and store instructions the 8-bit 'code' field is divided as::
|
||||
|
||||
+--------+--------+-------------------+
|
||||
| 3 bits | 2 bits | 3 bits |
|
||||
| mode | size | instruction class |
|
||||
+--------+--------+-------------------+
|
||||
(MSB) (LSB)
|
||||
|
||||
Size modifier is one of ...
|
||||
|
||||
::
|
||||
|
||||
BPF_W 0x00 /* word */
|
||||
BPF_H 0x08 /* half word */
|
||||
BPF_B 0x10 /* byte */
|
||||
BPF_DW 0x18 /* eBPF only, double word */
|
||||
|
||||
... which encodes size of load/store operation::
|
||||
|
||||
B - 1 byte
|
||||
H - 2 byte
|
||||
W - 4 byte
|
||||
DW - 8 byte (eBPF only)
|
||||
|
||||
Mode modifier is one of::
|
||||
|
||||
BPF_IMM 0x00 /* used for 32-bit mov in classic BPF and 64-bit in eBPF */
|
||||
BPF_ABS 0x20
|
||||
BPF_IND 0x40
|
||||
BPF_MEM 0x60
|
||||
BPF_LEN 0x80 /* classic BPF only, reserved in eBPF */
|
||||
BPF_MSH 0xa0 /* classic BPF only, reserved in eBPF */
|
||||
BPF_ATOMIC 0xc0 /* eBPF only, atomic operations */
|
|
@ -0,0 +1,11 @@
|
|||
================================
|
||||
Frequently asked questions (FAQ)
|
||||
================================
|
||||
|
||||
Two sets of Questions and Answers (Q&A) are maintained.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
bpf_design_QA
|
||||
bpf_devel_QA
|
|
@ -0,0 +1,7 @@
|
|||
Helper functions
|
||||
================
|
||||
|
||||
* `bpf-helpers(7)`_ maintains a list of helpers available to eBPF programs.
|
||||
|
||||
.. Links
|
||||
.. _bpf-helpers(7): https://man7.org/linux/man-pages/man7/bpf-helpers.7.html
|
|
@ -5,104 +5,33 @@ BPF Documentation
|
|||
This directory contains documentation for the BPF (Berkeley Packet
|
||||
Filter) facility, with a focus on the extended BPF version (eBPF).
|
||||
|
||||
This kernel side documentation is still work in progress. The main
|
||||
textual documentation is (for historical reasons) described in
|
||||
:ref:`networking-filter`, which describe both classical and extended
|
||||
BPF instruction-set.
|
||||
This kernel side documentation is still work in progress.
|
||||
The Cilium project also maintains a `BPF and XDP Reference Guide`_
|
||||
that goes into great technical depth about the BPF Architecture.
|
||||
|
||||
libbpf
|
||||
======
|
||||
|
||||
Documentation/bpf/libbpf/index.rst is a userspace library for loading and interacting with bpf programs.
|
||||
|
||||
BPF Type Format (BTF)
|
||||
=====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
instruction-set
|
||||
verifier
|
||||
libbpf/index
|
||||
btf
|
||||
|
||||
|
||||
Frequently asked questions (FAQ)
|
||||
================================
|
||||
|
||||
Two sets of Questions and Answers (Q&A) are maintained.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
bpf_design_QA
|
||||
bpf_devel_QA
|
||||
|
||||
Syscall API
|
||||
===========
|
||||
|
||||
The primary info for the bpf syscall is available in the `man-pages`_
|
||||
for `bpf(2)`_. For more information about the userspace API, see
|
||||
Documentation/userspace-api/ebpf/index.rst.
|
||||
|
||||
Helper functions
|
||||
================
|
||||
|
||||
* `bpf-helpers(7)`_ maintains a list of helpers available to eBPF programs.
|
||||
|
||||
|
||||
Program types
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
prog_cgroup_sockopt
|
||||
prog_cgroup_sysctl
|
||||
prog_flow_dissector
|
||||
bpf_lsm
|
||||
prog_sk_lookup
|
||||
|
||||
|
||||
Map types
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
map_cgroup_storage
|
||||
|
||||
|
||||
Testing and debugging BPF
|
||||
=========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
drgn
|
||||
s390
|
||||
|
||||
|
||||
Licensing
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
faq
|
||||
syscall_api
|
||||
helpers
|
||||
programs
|
||||
maps
|
||||
classic_vs_extended.rst
|
||||
bpf_licensing
|
||||
test_debug
|
||||
other
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
Other
|
||||
=====
|
||||
Indices
|
||||
=======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ringbuf
|
||||
llvm_reloc
|
||||
* :ref:`genindex`
|
||||
|
||||
.. Links:
|
||||
.. _networking-filter: ../networking/filter.rst
|
||||
.. _man-pages: https://www.kernel.org/doc/man-pages/
|
||||
.. _bpf(2): https://man7.org/linux/man-pages/man2/bpf.2.html
|
||||
.. _bpf-helpers(7): https://man7.org/linux/man-pages/man7/bpf-helpers.7.html
|
||||
.. _BPF and XDP Reference Guide: https://docs.cilium.io/en/latest/bpf/
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
|
||||
====================
|
||||
eBPF Instruction Set
|
||||
====================
|
||||
|
||||
Registers and calling convention
|
||||
================================
|
||||
|
||||
eBPF has 10 general purpose registers and a read-only frame pointer register,
|
||||
all of which are 64-bits wide.
|
||||
|
||||
The eBPF calling convention is defined as:
|
||||
|
||||
* R0: return value from function calls, and exit value for eBPF programs
|
||||
* R1 - R5: arguments for function calls
|
||||
* R6 - R9: callee saved registers that function calls will preserve
|
||||
* R10: read-only frame pointer to access stack
|
||||
|
||||
R0 - R5 are scratch registers and eBPF programs needs to spill/fill them if
|
||||
necessary across calls.
|
||||
|
||||
Instruction encoding
|
||||
====================
|
||||
|
||||
eBPF uses 64-bit instructions with the following encoding:
|
||||
|
||||
============= ======= =============== ==================== ============
|
||||
32 bits (MSB) 16 bits 4 bits 4 bits 8 bits (LSB)
|
||||
============= ======= =============== ==================== ============
|
||||
immediate offset source register destination register opcode
|
||||
============= ======= =============== ==================== ============
|
||||
|
||||
Note that most instructions do not use all of the fields.
|
||||
Unused fields shall be cleared to zero.
|
||||
|
||||
Instruction classes
|
||||
-------------------
|
||||
|
||||
The three LSB bits of the 'opcode' field store the instruction class:
|
||||
|
||||
========= ===== ===============================
|
||||
class value description
|
||||
========= ===== ===============================
|
||||
BPF_LD 0x00 non-standard load operations
|
||||
BPF_LDX 0x01 load into register operations
|
||||
BPF_ST 0x02 store from immediate operations
|
||||
BPF_STX 0x03 store from register operations
|
||||
BPF_ALU 0x04 32-bit arithmetic operations
|
||||
BPF_JMP 0x05 64-bit jump operations
|
||||
BPF_JMP32 0x06 32-bit jump operations
|
||||
BPF_ALU64 0x07 64-bit arithmetic operations
|
||||
========= ===== ===============================
|
||||
|
||||
Arithmetic and jump instructions
|
||||
================================
|
||||
|
||||
For arithmetic and jump instructions (BPF_ALU, BPF_ALU64, BPF_JMP and
|
||||
BPF_JMP32), the 8-bit 'opcode' field is divided into three parts:
|
||||
|
||||
============== ====== =================
|
||||
4 bits (MSB) 1 bit 3 bits (LSB)
|
||||
============== ====== =================
|
||||
operation code source instruction class
|
||||
============== ====== =================
|
||||
|
||||
The 4th bit encodes the source operand:
|
||||
|
||||
====== ===== ========================================
|
||||
source value description
|
||||
====== ===== ========================================
|
||||
BPF_K 0x00 use 32-bit immediate as source operand
|
||||
BPF_X 0x08 use 'src_reg' register as source operand
|
||||
====== ===== ========================================
|
||||
|
||||
The four MSB bits store the operation code.
|
||||
|
||||
|
||||
Arithmetic instructions
|
||||
-----------------------
|
||||
|
||||
BPF_ALU uses 32-bit wide operands while BPF_ALU64 uses 64-bit wide operands for
|
||||
otherwise identical operations.
|
||||
The code field encodes the operation as below:
|
||||
|
||||
======== ===== ==========================
|
||||
code value description
|
||||
======== ===== ==========================
|
||||
BPF_ADD 0x00 dst += src
|
||||
BPF_SUB 0x10 dst -= src
|
||||
BPF_MUL 0x20 dst \*= src
|
||||
BPF_DIV 0x30 dst /= src
|
||||
BPF_OR 0x40 dst \|= src
|
||||
BPF_AND 0x50 dst &= src
|
||||
BPF_LSH 0x60 dst <<= src
|
||||
BPF_RSH 0x70 dst >>= src
|
||||
BPF_NEG 0x80 dst = ~src
|
||||
BPF_MOD 0x90 dst %= src
|
||||
BPF_XOR 0xa0 dst ^= src
|
||||
BPF_MOV 0xb0 dst = src
|
||||
BPF_ARSH 0xc0 sign extending shift right
|
||||
BPF_END 0xd0 endianness conversion
|
||||
======== ===== ==========================
|
||||
|
||||
BPF_ADD | BPF_X | BPF_ALU means::
|
||||
|
||||
dst_reg = (u32) dst_reg + (u32) src_reg;
|
||||
|
||||
BPF_ADD | BPF_X | BPF_ALU64 means::
|
||||
|
||||
dst_reg = dst_reg + src_reg
|
||||
|
||||
BPF_XOR | BPF_K | BPF_ALU means::
|
||||
|
||||
src_reg = (u32) src_reg ^ (u32) imm32
|
||||
|
||||
BPF_XOR | BPF_K | BPF_ALU64 means::
|
||||
|
||||
src_reg = src_reg ^ imm32
|
||||
|
||||
|
||||
Jump instructions
|
||||
-----------------
|
||||
|
||||
BPF_JMP32 uses 32-bit wide operands while BPF_JMP uses 64-bit wide operands for
|
||||
otherwise identical operations.
|
||||
The code field encodes the operation as below:
|
||||
|
||||
======== ===== ========================= ============
|
||||
code value description notes
|
||||
======== ===== ========================= ============
|
||||
BPF_JA 0x00 PC += off BPF_JMP only
|
||||
BPF_JEQ 0x10 PC += off if dst == src
|
||||
BPF_JGT 0x20 PC += off if dst > src unsigned
|
||||
BPF_JGE 0x30 PC += off if dst >= src unsigned
|
||||
BPF_JSET 0x40 PC += off if dst & src
|
||||
BPF_JNE 0x50 PC += off if dst != src
|
||||
BPF_JSGT 0x60 PC += off if dst > src signed
|
||||
BPF_JSGE 0x70 PC += off if dst >= src signed
|
||||
BPF_CALL 0x80 function call
|
||||
BPF_EXIT 0x90 function / program return BPF_JMP only
|
||||
BPF_JLT 0xa0 PC += off if dst < src unsigned
|
||||
BPF_JLE 0xb0 PC += off if dst <= src unsigned
|
||||
BPF_JSLT 0xc0 PC += off if dst < src signed
|
||||
BPF_JSLE 0xd0 PC += off if dst <= src signed
|
||||
======== ===== ========================= ============
|
||||
|
||||
The eBPF program needs to store the return value into register R0 before doing a
|
||||
BPF_EXIT.
|
||||
|
||||
|
||||
Load and store instructions
|
||||
===========================
|
||||
|
||||
For load and store instructions (BPF_LD, BPF_LDX, BPF_ST and BPF_STX), the
|
||||
8-bit 'opcode' field is divided as:
|
||||
|
||||
============ ====== =================
|
||||
3 bits (MSB) 2 bits 3 bits (LSB)
|
||||
============ ====== =================
|
||||
mode size instruction class
|
||||
============ ====== =================
|
||||
|
||||
The size modifier is one of:
|
||||
|
||||
============= ===== =====================
|
||||
size modifier value description
|
||||
============= ===== =====================
|
||||
BPF_W 0x00 word (4 bytes)
|
||||
BPF_H 0x08 half word (2 bytes)
|
||||
BPF_B 0x10 byte
|
||||
BPF_DW 0x18 double word (8 bytes)
|
||||
============= ===== =====================
|
||||
|
||||
The mode modifier is one of:
|
||||
|
||||
============= ===== ====================================
|
||||
mode modifier value description
|
||||
============= ===== ====================================
|
||||
BPF_IMM 0x00 used for 64-bit mov
|
||||
BPF_ABS 0x20 legacy BPF packet access
|
||||
BPF_IND 0x40 legacy BPF packet access
|
||||
BPF_MEM 0x60 all normal load and store operations
|
||||
BPF_ATOMIC 0xc0 atomic operations
|
||||
============= ===== ====================================
|
||||
|
||||
BPF_MEM | <size> | BPF_STX means::
|
||||
|
||||
*(size *) (dst_reg + off) = src_reg
|
||||
|
||||
BPF_MEM | <size> | BPF_ST means::
|
||||
|
||||
*(size *) (dst_reg + off) = imm32
|
||||
|
||||
BPF_MEM | <size> | BPF_LDX means::
|
||||
|
||||
dst_reg = *(size *) (src_reg + off)
|
||||
|
||||
Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW.
|
||||
|
||||
Atomic operations
|
||||
-----------------
|
||||
|
||||
eBPF includes atomic operations, which use the immediate field for extra
|
||||
encoding::
|
||||
|
||||
.imm = BPF_ADD, .code = BPF_ATOMIC | BPF_W | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
|
||||
.imm = BPF_ADD, .code = BPF_ATOMIC | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
|
||||
|
||||
The basic atomic operations supported are::
|
||||
|
||||
BPF_ADD
|
||||
BPF_AND
|
||||
BPF_OR
|
||||
BPF_XOR
|
||||
|
||||
Each having equivalent semantics with the ``BPF_ADD`` example, that is: the
|
||||
memory location addresed by ``dst_reg + off`` is atomically modified, with
|
||||
``src_reg`` as the other operand. If the ``BPF_FETCH`` flag is set in the
|
||||
immediate, then these operations also overwrite ``src_reg`` with the
|
||||
value that was in memory before it was modified.
|
||||
|
||||
The more special operations are::
|
||||
|
||||
BPF_XCHG
|
||||
|
||||
This atomically exchanges ``src_reg`` with the value addressed by ``dst_reg +
|
||||
off``. ::
|
||||
|
||||
BPF_CMPXCHG
|
||||
|
||||
This atomically compares the value addressed by ``dst_reg + off`` with
|
||||
``R0``. If they match it is replaced with ``src_reg``. In either case, the
|
||||
value that was there before is zero-extended and loaded back to ``R0``.
|
||||
|
||||
Note that 1 and 2 byte atomic operations are not supported.
|
||||
|
||||
Clang can generate atomic instructions by default when ``-mcpu=v3`` is
|
||||
enabled. If a lower version for ``-mcpu`` is set, the only atomic instruction
|
||||
Clang can generate is ``BPF_ADD`` *without* ``BPF_FETCH``. If you need to enable
|
||||
the atomics features, while keeping a lower ``-mcpu`` version, you can use
|
||||
``-Xclang -target-feature -Xclang +alu32``.
|
||||
|
||||
You may encounter ``BPF_XADD`` - this is a legacy name for ``BPF_ATOMIC``,
|
||||
referring to the exclusive-add operation encoded when the immediate field is
|
||||
zero.
|
||||
|
||||
16-byte instructions
|
||||
--------------------
|
||||
|
||||
eBPF has one 16-byte instruction: ``BPF_LD | BPF_DW | BPF_IMM`` which consists
|
||||
of two consecutive ``struct bpf_insn`` 8-byte blocks and interpreted as single
|
||||
instruction that loads 64-bit immediate value into a dst_reg.
|
||||
|
||||
Packet access instructions
|
||||
--------------------------
|
||||
|
||||
eBPF has two non-generic instructions: (BPF_ABS | <size> | BPF_LD) and
|
||||
(BPF_IND | <size> | BPF_LD) which are used to access packet data.
|
||||
|
||||
They had to be carried over from classic BPF to have strong performance of
|
||||
socket filters running in eBPF interpreter. These instructions can only
|
||||
be used when interpreter context is a pointer to ``struct sk_buff`` and
|
||||
have seven implicit operands. Register R6 is an implicit input that must
|
||||
contain pointer to sk_buff. Register R0 is an implicit output which contains
|
||||
the data fetched from the packet. Registers R1-R5 are scratch registers
|
||||
and must not be used to store the data across BPF_ABS | BPF_LD or
|
||||
BPF_IND | BPF_LD instructions.
|
||||
|
||||
These instructions have implicit program exit condition as well. When
|
||||
eBPF program is trying to access the data beyond the packet boundary,
|
||||
the interpreter will abort the execution of the program. JIT compilers
|
||||
therefore must preserve this property. src_reg and imm32 fields are
|
||||
explicit inputs to these instructions.
|
||||
|
||||
For example, BPF_IND | BPF_W | BPF_LD means::
|
||||
|
||||
R0 = ntohl(*(u32 *) (((struct sk_buff *) R6)->data + src_reg + imm32))
|
||||
|
||||
and R1 - R5 are clobbered.
|
|
@ -3,8 +3,6 @@
|
|||
libbpf
|
||||
======
|
||||
|
||||
For API documentation see the `versioned API documentation site <https://libbpf.readthedocs.io/en/latest/api.html>`_.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
|
@ -14,6 +12,8 @@ For API documentation see the `versioned API documentation site <https://libbpf.
|
|||
This is documentation for libbpf, a userspace library for loading and
|
||||
interacting with bpf programs.
|
||||
|
||||
For API documentation see the `versioned API documentation site <https://libbpf.readthedocs.io/en/latest/api.html>`_.
|
||||
|
||||
All general BPF questions, including kernel functionality, libbpf APIs and
|
||||
their application, should be sent to bpf@vger.kernel.org mailing list.
|
||||
You can `subscribe <http://vger.kernel.org/vger-lists.html#bpf>`_ to the
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
=========
|
||||
eBPF maps
|
||||
=========
|
||||
|
||||
'maps' is a generic storage of different types for sharing data between kernel
|
||||
and userspace.
|
||||
|
||||
The maps are accessed from user space via BPF syscall, which has commands:
|
||||
|
||||
- create a map with given type and attributes
|
||||
``map_fd = bpf(BPF_MAP_CREATE, union bpf_attr *attr, u32 size)``
|
||||
using attr->map_type, attr->key_size, attr->value_size, attr->max_entries
|
||||
returns process-local file descriptor or negative error
|
||||
|
||||
- lookup key in a given map
|
||||
``err = bpf(BPF_MAP_LOOKUP_ELEM, union bpf_attr *attr, u32 size)``
|
||||
using attr->map_fd, attr->key, attr->value
|
||||
returns zero and stores found elem into value or negative error
|
||||
|
||||
- create or update key/value pair in a given map
|
||||
``err = bpf(BPF_MAP_UPDATE_ELEM, union bpf_attr *attr, u32 size)``
|
||||
using attr->map_fd, attr->key, attr->value
|
||||
returns zero or negative error
|
||||
|
||||
- find and delete element by key in a given map
|
||||
``err = bpf(BPF_MAP_DELETE_ELEM, union bpf_attr *attr, u32 size)``
|
||||
using attr->map_fd, attr->key
|
||||
|
||||
- to delete map: close(fd)
|
||||
Exiting process will delete maps automatically
|
||||
|
||||
userspace programs use this syscall to create/access maps that eBPF programs
|
||||
are concurrently updating.
|
||||
|
||||
maps can have different types: hash, array, bloom filter, radix-tree, etc.
|
||||
|
||||
The map is defined by:
|
||||
|
||||
- type
|
||||
- max number of elements
|
||||
- key size in bytes
|
||||
- value size in bytes
|
||||
|
||||
Map Types
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
map_*
|
|
@ -0,0 +1,9 @@
|
|||
=====
|
||||
Other
|
||||
=====
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ringbuf
|
||||
llvm_reloc
|
|
@ -0,0 +1,9 @@
|
|||
=============
|
||||
Program Types
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
prog_*
|
|
@ -0,0 +1,11 @@
|
|||
===========
|
||||
Syscall API
|
||||
===========
|
||||
|
||||
The primary info for the bpf syscall is available in the `man-pages`_
|
||||
for `bpf(2)`_. For more information about the userspace API, see
|
||||
Documentation/userspace-api/ebpf/index.rst.
|
||||
|
||||
.. Links:
|
||||
.. _man-pages: https://www.kernel.org/doc/man-pages/
|
||||
.. _bpf(2): https://man7.org/linux/man-pages/man2/bpf.2.html
|
|
@ -0,0 +1,9 @@
|
|||
=========================
|
||||
Testing and debugging BPF
|
||||
=========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
drgn
|
||||
s390
|
|
@ -0,0 +1,529 @@
|
|||
|
||||
=============
|
||||
eBPF verifier
|
||||
=============
|
||||
|
||||
The safety of the eBPF program is determined in two steps.
|
||||
|
||||
First step does DAG check to disallow loops and other CFG validation.
|
||||
In particular it will detect programs that have unreachable instructions.
|
||||
(though classic BPF checker allows them)
|
||||
|
||||
Second step starts from the first insn and descends all possible paths.
|
||||
It simulates execution of every insn and observes the state change of
|
||||
registers and stack.
|
||||
|
||||
At the start of the program the register R1 contains a pointer to context
|
||||
and has type PTR_TO_CTX.
|
||||
If verifier sees an insn that does R2=R1, then R2 has now type
|
||||
PTR_TO_CTX as well and can be used on the right hand side of expression.
|
||||
If R1=PTR_TO_CTX and insn is R2=R1+R1, then R2=SCALAR_VALUE,
|
||||
since addition of two valid pointers makes invalid pointer.
|
||||
(In 'secure' mode verifier will reject any type of pointer arithmetic to make
|
||||
sure that kernel addresses don't leak to unprivileged users)
|
||||
|
||||
If register was never written to, it's not readable::
|
||||
|
||||
bpf_mov R0 = R2
|
||||
bpf_exit
|
||||
|
||||
will be rejected, since R2 is unreadable at the start of the program.
|
||||
|
||||
After kernel function call, R1-R5 are reset to unreadable and
|
||||
R0 has a return type of the function.
|
||||
|
||||
Since R6-R9 are callee saved, their state is preserved across the call.
|
||||
|
||||
::
|
||||
|
||||
bpf_mov R6 = 1
|
||||
bpf_call foo
|
||||
bpf_mov R0 = R6
|
||||
bpf_exit
|
||||
|
||||
is a correct program. If there was R1 instead of R6, it would have
|
||||
been rejected.
|
||||
|
||||
load/store instructions are allowed only with registers of valid types, which
|
||||
are PTR_TO_CTX, PTR_TO_MAP, PTR_TO_STACK. They are bounds and alignment checked.
|
||||
For example::
|
||||
|
||||
bpf_mov R1 = 1
|
||||
bpf_mov R2 = 2
|
||||
bpf_xadd *(u32 *)(R1 + 3) += R2
|
||||
bpf_exit
|
||||
|
||||
will be rejected, since R1 doesn't have a valid pointer type at the time of
|
||||
execution of instruction bpf_xadd.
|
||||
|
||||
At the start R1 type is PTR_TO_CTX (a pointer to generic ``struct bpf_context``)
|
||||
A callback is used to customize verifier to restrict eBPF program access to only
|
||||
certain fields within ctx structure with specified size and alignment.
|
||||
|
||||
For example, the following insn::
|
||||
|
||||
bpf_ld R0 = *(u32 *)(R6 + 8)
|
||||
|
||||
intends to load a word from address R6 + 8 and store it into R0
|
||||
If R6=PTR_TO_CTX, via is_valid_access() callback the verifier will know
|
||||
that offset 8 of size 4 bytes can be accessed for reading, otherwise
|
||||
the verifier will reject the program.
|
||||
If R6=PTR_TO_STACK, then access should be aligned and be within
|
||||
stack bounds, which are [-MAX_BPF_STACK, 0). In this example offset is 8,
|
||||
so it will fail verification, since it's out of bounds.
|
||||
|
||||
The verifier will allow eBPF program to read data from stack only after
|
||||
it wrote into it.
|
||||
|
||||
Classic BPF verifier does similar check with M[0-15] memory slots.
|
||||
For example::
|
||||
|
||||
bpf_ld R0 = *(u32 *)(R10 - 4)
|
||||
bpf_exit
|
||||
|
||||
is invalid program.
|
||||
Though R10 is correct read-only register and has type PTR_TO_STACK
|
||||
and R10 - 4 is within stack bounds, there were no stores into that location.
|
||||
|
||||
Pointer register spill/fill is tracked as well, since four (R6-R9)
|
||||
callee saved registers may not be enough for some programs.
|
||||
|
||||
Allowed function calls are customized with bpf_verifier_ops->get_func_proto()
|
||||
The eBPF verifier will check that registers match argument constraints.
|
||||
After the call register R0 will be set to return type of the function.
|
||||
|
||||
Function calls is a main mechanism to extend functionality of eBPF programs.
|
||||
Socket filters may let programs to call one set of functions, whereas tracing
|
||||
filters may allow completely different set.
|
||||
|
||||
If a function made accessible to eBPF program, it needs to be thought through
|
||||
from safety point of view. The verifier will guarantee that the function is
|
||||
called with valid arguments.
|
||||
|
||||
seccomp vs socket filters have different security restrictions for classic BPF.
|
||||
Seccomp solves this by two stage verifier: classic BPF verifier is followed
|
||||
by seccomp verifier. In case of eBPF one configurable verifier is shared for
|
||||
all use cases.
|
||||
|
||||
See details of eBPF verifier in kernel/bpf/verifier.c
|
||||
|
||||
Register value tracking
|
||||
=======================
|
||||
|
||||
In order to determine the safety of an eBPF program, the verifier must track
|
||||
the range of possible values in each register and also in each stack slot.
|
||||
This is done with ``struct bpf_reg_state``, defined in include/linux/
|
||||
bpf_verifier.h, which unifies tracking of scalar and pointer values. Each
|
||||
register state has a type, which is either NOT_INIT (the register has not been
|
||||
written to), SCALAR_VALUE (some value which is not usable as a pointer), or a
|
||||
pointer type. The types of pointers describe their base, as follows:
|
||||
|
||||
|
||||
PTR_TO_CTX
|
||||
Pointer to bpf_context.
|
||||
CONST_PTR_TO_MAP
|
||||
Pointer to struct bpf_map. "Const" because arithmetic
|
||||
on these pointers is forbidden.
|
||||
PTR_TO_MAP_VALUE
|
||||
Pointer to the value stored in a map element.
|
||||
PTR_TO_MAP_VALUE_OR_NULL
|
||||
Either a pointer to a map value, or NULL; map accesses
|
||||
(see maps.rst) return this type, which becomes a
|
||||
PTR_TO_MAP_VALUE when checked != NULL. Arithmetic on
|
||||
these pointers is forbidden.
|
||||
PTR_TO_STACK
|
||||
Frame pointer.
|
||||
PTR_TO_PACKET
|
||||
skb->data.
|
||||
PTR_TO_PACKET_END
|
||||
skb->data + headlen; arithmetic forbidden.
|
||||
PTR_TO_SOCKET
|
||||
Pointer to struct bpf_sock_ops, implicitly refcounted.
|
||||
PTR_TO_SOCKET_OR_NULL
|
||||
Either a pointer to a socket, or NULL; socket lookup
|
||||
returns this type, which becomes a PTR_TO_SOCKET when
|
||||
checked != NULL. PTR_TO_SOCKET is reference-counted,
|
||||
so programs must release the reference through the
|
||||
socket release function before the end of the program.
|
||||
Arithmetic on these pointers is forbidden.
|
||||
|
||||
However, a pointer may be offset from this base (as a result of pointer
|
||||
arithmetic), and this is tracked in two parts: the 'fixed offset' and 'variable
|
||||
offset'. The former is used when an exactly-known value (e.g. an immediate
|
||||
operand) is added to a pointer, while the latter is used for values which are
|
||||
not exactly known. The variable offset is also used in SCALAR_VALUEs, to track
|
||||
the range of possible values in the register.
|
||||
|
||||
The verifier's knowledge about the variable offset consists of:
|
||||
|
||||
* minimum and maximum values as unsigned
|
||||
* minimum and maximum values as signed
|
||||
|
||||
* knowledge of the values of individual bits, in the form of a 'tnum': a u64
|
||||
'mask' and a u64 'value'. 1s in the mask represent bits whose value is unknown;
|
||||
1s in the value represent bits known to be 1. Bits known to be 0 have 0 in both
|
||||
mask and value; no bit should ever be 1 in both. For example, if a byte is read
|
||||
into a register from memory, the register's top 56 bits are known zero, while
|
||||
the low 8 are unknown - which is represented as the tnum (0x0; 0xff). If we
|
||||
then OR this with 0x40, we get (0x40; 0xbf), then if we add 1 we get (0x0;
|
||||
0x1ff), because of potential carries.
|
||||
|
||||
Besides arithmetic, the register state can also be updated by conditional
|
||||
branches. For instance, if a SCALAR_VALUE is compared > 8, in the 'true' branch
|
||||
it will have a umin_value (unsigned minimum value) of 9, whereas in the 'false'
|
||||
branch it will have a umax_value of 8. A signed compare (with BPF_JSGT or
|
||||
BPF_JSGE) would instead update the signed minimum/maximum values. Information
|
||||
from the signed and unsigned bounds can be combined; for instance if a value is
|
||||
first tested < 8 and then tested s> 4, the verifier will conclude that the value
|
||||
is also > 4 and s< 8, since the bounds prevent crossing the sign boundary.
|
||||
|
||||
PTR_TO_PACKETs with a variable offset part have an 'id', which is common to all
|
||||
pointers sharing that same variable offset. This is important for packet range
|
||||
checks: after adding a variable to a packet pointer register A, if you then copy
|
||||
it to another register B and then add a constant 4 to A, both registers will
|
||||
share the same 'id' but the A will have a fixed offset of +4. Then if A is
|
||||
bounds-checked and found to be less than a PTR_TO_PACKET_END, the register B is
|
||||
now known to have a safe range of at least 4 bytes. See 'Direct packet access',
|
||||
below, for more on PTR_TO_PACKET ranges.
|
||||
|
||||
The 'id' field is also used on PTR_TO_MAP_VALUE_OR_NULL, common to all copies of
|
||||
the pointer returned from a map lookup. This means that when one copy is
|
||||
checked and found to be non-NULL, all copies can become PTR_TO_MAP_VALUEs.
|
||||
As well as range-checking, the tracked information is also used for enforcing
|
||||
alignment of pointer accesses. For instance, on most systems the packet pointer
|
||||
is 2 bytes after a 4-byte alignment. If a program adds 14 bytes to that to jump
|
||||
over the Ethernet header, then reads IHL and addes (IHL * 4), the resulting
|
||||
pointer will have a variable offset known to be 4n+2 for some n, so adding the 2
|
||||
bytes (NET_IP_ALIGN) gives a 4-byte alignment and so word-sized accesses through
|
||||
that pointer are safe.
|
||||
The 'id' field is also used on PTR_TO_SOCKET and PTR_TO_SOCKET_OR_NULL, common
|
||||
to all copies of the pointer returned from a socket lookup. This has similar
|
||||
behaviour to the handling for PTR_TO_MAP_VALUE_OR_NULL->PTR_TO_MAP_VALUE, but
|
||||
it also handles reference tracking for the pointer. PTR_TO_SOCKET implicitly
|
||||
represents a reference to the corresponding ``struct sock``. To ensure that the
|
||||
reference is not leaked, it is imperative to NULL-check the reference and in
|
||||
the non-NULL case, and pass the valid reference to the socket release function.
|
||||
|
||||
Direct packet access
|
||||
====================
|
||||
|
||||
In cls_bpf and act_bpf programs the verifier allows direct access to the packet
|
||||
data via skb->data and skb->data_end pointers.
|
||||
Ex::
|
||||
|
||||
1: r4 = *(u32 *)(r1 +80) /* load skb->data_end */
|
||||
2: r3 = *(u32 *)(r1 +76) /* load skb->data */
|
||||
3: r5 = r3
|
||||
4: r5 += 14
|
||||
5: if r5 > r4 goto pc+16
|
||||
R1=ctx R3=pkt(id=0,off=0,r=14) R4=pkt_end R5=pkt(id=0,off=14,r=14) R10=fp
|
||||
6: r0 = *(u16 *)(r3 +12) /* access 12 and 13 bytes of the packet */
|
||||
|
||||
this 2byte load from the packet is safe to do, since the program author
|
||||
did check ``if (skb->data + 14 > skb->data_end) goto err`` at insn #5 which
|
||||
means that in the fall-through case the register R3 (which points to skb->data)
|
||||
has at least 14 directly accessible bytes. The verifier marks it
|
||||
as R3=pkt(id=0,off=0,r=14).
|
||||
id=0 means that no additional variables were added to the register.
|
||||
off=0 means that no additional constants were added.
|
||||
r=14 is the range of safe access which means that bytes [R3, R3 + 14) are ok.
|
||||
Note that R5 is marked as R5=pkt(id=0,off=14,r=14). It also points
|
||||
to the packet data, but constant 14 was added to the register, so
|
||||
it now points to ``skb->data + 14`` and accessible range is [R5, R5 + 14 - 14)
|
||||
which is zero bytes.
|
||||
|
||||
More complex packet access may look like::
|
||||
|
||||
|
||||
R0=inv1 R1=ctx R3=pkt(id=0,off=0,r=14) R4=pkt_end R5=pkt(id=0,off=14,r=14) R10=fp
|
||||
6: r0 = *(u8 *)(r3 +7) /* load 7th byte from the packet */
|
||||
7: r4 = *(u8 *)(r3 +12)
|
||||
8: r4 *= 14
|
||||
9: r3 = *(u32 *)(r1 +76) /* load skb->data */
|
||||
10: r3 += r4
|
||||
11: r2 = r1
|
||||
12: r2 <<= 48
|
||||
13: r2 >>= 48
|
||||
14: r3 += r2
|
||||
15: r2 = r3
|
||||
16: r2 += 8
|
||||
17: r1 = *(u32 *)(r1 +80) /* load skb->data_end */
|
||||
18: if r2 > r1 goto pc+2
|
||||
R0=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R1=pkt_end R2=pkt(id=2,off=8,r=8) R3=pkt(id=2,off=0,r=8) R4=inv(id=0,umax_value=3570,var_off=(0x0; 0xfffe)) R5=pkt(id=0,off=14,r=14) R10=fp
|
||||
19: r1 = *(u8 *)(r3 +4)
|
||||
|
||||
The state of the register R3 is R3=pkt(id=2,off=0,r=8)
|
||||
id=2 means that two ``r3 += rX`` instructions were seen, so r3 points to some
|
||||
offset within a packet and since the program author did
|
||||
``if (r3 + 8 > r1) goto err`` at insn #18, the safe range is [R3, R3 + 8).
|
||||
The verifier only allows 'add'/'sub' operations on packet registers. Any other
|
||||
operation will set the register state to 'SCALAR_VALUE' and it won't be
|
||||
available for direct packet access.
|
||||
|
||||
Operation ``r3 += rX`` may overflow and become less than original skb->data,
|
||||
therefore the verifier has to prevent that. So when it sees ``r3 += rX``
|
||||
instruction and rX is more than 16-bit value, any subsequent bounds-check of r3
|
||||
against skb->data_end will not give us 'range' information, so attempts to read
|
||||
through the pointer will give "invalid access to packet" error.
|
||||
|
||||
Ex. after insn ``r4 = *(u8 *)(r3 +12)`` (insn #7 above) the state of r4 is
|
||||
R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) which means that upper 56 bits
|
||||
of the register are guaranteed to be zero, and nothing is known about the lower
|
||||
8 bits. After insn ``r4 *= 14`` the state becomes
|
||||
R4=inv(id=0,umax_value=3570,var_off=(0x0; 0xfffe)), since multiplying an 8-bit
|
||||
value by constant 14 will keep upper 52 bits as zero, also the least significant
|
||||
bit will be zero as 14 is even. Similarly ``r2 >>= 48`` will make
|
||||
R2=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff)), since the shift is not sign
|
||||
extending. This logic is implemented in adjust_reg_min_max_vals() function,
|
||||
which calls adjust_ptr_min_max_vals() for adding pointer to scalar (or vice
|
||||
versa) and adjust_scalar_min_max_vals() for operations on two scalars.
|
||||
|
||||
The end result is that bpf program author can access packet directly
|
||||
using normal C code as::
|
||||
|
||||
void *data = (void *)(long)skb->data;
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
struct eth_hdr *eth = data;
|
||||
struct iphdr *iph = data + sizeof(*eth);
|
||||
struct udphdr *udp = data + sizeof(*eth) + sizeof(*iph);
|
||||
|
||||
if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*udp) > data_end)
|
||||
return 0;
|
||||
if (eth->h_proto != htons(ETH_P_IP))
|
||||
return 0;
|
||||
if (iph->protocol != IPPROTO_UDP || iph->ihl != 5)
|
||||
return 0;
|
||||
if (udp->dest == 53 || udp->source == 9)
|
||||
...;
|
||||
|
||||
which makes such programs easier to write comparing to LD_ABS insn
|
||||
and significantly faster.
|
||||
|
||||
Pruning
|
||||
=======
|
||||
|
||||
The verifier does not actually walk all possible paths through the program. For
|
||||
each new branch to analyse, the verifier looks at all the states it's previously
|
||||
been in when at this instruction. If any of them contain the current state as a
|
||||
subset, the branch is 'pruned' - that is, the fact that the previous state was
|
||||
accepted implies the current state would be as well. For instance, if in the
|
||||
previous state, r1 held a packet-pointer, and in the current state, r1 holds a
|
||||
packet-pointer with a range as long or longer and at least as strict an
|
||||
alignment, then r1 is safe. Similarly, if r2 was NOT_INIT before then it can't
|
||||
have been used by any path from that point, so any value in r2 (including
|
||||
another NOT_INIT) is safe. The implementation is in the function regsafe().
|
||||
Pruning considers not only the registers but also the stack (and any spilled
|
||||
registers it may hold). They must all be safe for the branch to be pruned.
|
||||
This is implemented in states_equal().
|
||||
|
||||
Understanding eBPF verifier messages
|
||||
====================================
|
||||
|
||||
The following are few examples of invalid eBPF programs and verifier error
|
||||
messages as seen in the log:
|
||||
|
||||
Program with unreachable instructions::
|
||||
|
||||
static struct bpf_insn prog[] = {
|
||||
BPF_EXIT_INSN(),
|
||||
BPF_EXIT_INSN(),
|
||||
};
|
||||
|
||||
Error:
|
||||
|
||||
unreachable insn 1
|
||||
|
||||
Program that reads uninitialized register::
|
||||
|
||||
BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (bf) r0 = r2
|
||||
R2 !read_ok
|
||||
|
||||
Program that doesn't initialize R0 before exiting::
|
||||
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_1),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (bf) r2 = r1
|
||||
1: (95) exit
|
||||
R0 !read_ok
|
||||
|
||||
Program that accesses stack out of bounds::
|
||||
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, 8, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (7a) *(u64 *)(r10 +8) = 0
|
||||
invalid stack off=8 size=8
|
||||
|
||||
Program that doesn't initialize stack before passing its address into function::
|
||||
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (bf) r2 = r10
|
||||
1: (07) r2 += -8
|
||||
2: (b7) r1 = 0x0
|
||||
3: (85) call 1
|
||||
invalid indirect read from stack off -8+0 size 8
|
||||
|
||||
Program that uses invalid map_fd=0 while calling to map_lookup_elem() function::
|
||||
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (7a) *(u64 *)(r10 -8) = 0
|
||||
1: (bf) r2 = r10
|
||||
2: (07) r2 += -8
|
||||
3: (b7) r1 = 0x0
|
||||
4: (85) call 1
|
||||
fd 0 is not pointing to valid bpf_map
|
||||
|
||||
Program that doesn't check return value of map_lookup_elem() before accessing
|
||||
map element::
|
||||
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (7a) *(u64 *)(r10 -8) = 0
|
||||
1: (bf) r2 = r10
|
||||
2: (07) r2 += -8
|
||||
3: (b7) r1 = 0x0
|
||||
4: (85) call 1
|
||||
5: (7a) *(u64 *)(r0 +0) = 0
|
||||
R0 invalid mem access 'map_value_or_null'
|
||||
|
||||
Program that correctly checks map_lookup_elem() returned value for NULL, but
|
||||
accesses the memory with incorrect alignment::
|
||||
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (7a) *(u64 *)(r10 -8) = 0
|
||||
1: (bf) r2 = r10
|
||||
2: (07) r2 += -8
|
||||
3: (b7) r1 = 1
|
||||
4: (85) call 1
|
||||
5: (15) if r0 == 0x0 goto pc+1
|
||||
R0=map_ptr R10=fp
|
||||
6: (7a) *(u64 *)(r0 +4) = 0
|
||||
misaligned access off 4 size 8
|
||||
|
||||
Program that correctly checks map_lookup_elem() returned value for NULL and
|
||||
accesses memory with correct alignment in one side of 'if' branch, but fails
|
||||
to do so in the other side of 'if' branch::
|
||||
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (7a) *(u64 *)(r10 -8) = 0
|
||||
1: (bf) r2 = r10
|
||||
2: (07) r2 += -8
|
||||
3: (b7) r1 = 1
|
||||
4: (85) call 1
|
||||
5: (15) if r0 == 0x0 goto pc+2
|
||||
R0=map_ptr R10=fp
|
||||
6: (7a) *(u64 *)(r0 +0) = 0
|
||||
7: (95) exit
|
||||
|
||||
from 5 to 8: R0=imm0 R10=fp
|
||||
8: (7a) *(u64 *)(r0 +0) = 1
|
||||
R0 invalid mem access 'imm'
|
||||
|
||||
Program that performs a socket lookup then sets the pointer to NULL without
|
||||
checking it::
|
||||
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -8),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 4),
|
||||
BPF_MOV64_IMM(BPF_REG_4, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_5, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (b7) r2 = 0
|
||||
1: (63) *(u32 *)(r10 -8) = r2
|
||||
2: (bf) r2 = r10
|
||||
3: (07) r2 += -8
|
||||
4: (b7) r3 = 4
|
||||
5: (b7) r4 = 0
|
||||
6: (b7) r5 = 0
|
||||
7: (85) call bpf_sk_lookup_tcp#65
|
||||
8: (b7) r0 = 0
|
||||
9: (95) exit
|
||||
Unreleased reference id=1, alloc_insn=7
|
||||
|
||||
Program that performs a socket lookup but does not NULL-check the returned
|
||||
value::
|
||||
|
||||
BPF_MOV64_IMM(BPF_REG_2, 0),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -8),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_MOV64_IMM(BPF_REG_3, 4),
|
||||
BPF_MOV64_IMM(BPF_REG_4, 0),
|
||||
BPF_MOV64_IMM(BPF_REG_5, 0),
|
||||
BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
Error::
|
||||
|
||||
0: (b7) r2 = 0
|
||||
1: (63) *(u32 *)(r10 -8) = r2
|
||||
2: (bf) r2 = r10
|
||||
3: (07) r2 += -8
|
||||
4: (b7) r3 = 4
|
||||
5: (b7) r4 = 0
|
||||
6: (b7) r5 = 0
|
||||
7: (85) call bpf_sk_lookup_tcp#65
|
||||
8: (95) exit
|
||||
Unreleased reference id=1, alloc_insn=7
|
|
@ -37,6 +37,20 @@ properties:
|
|||
should be named with the instance number of the NPE engine used for
|
||||
the crypto engine.
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
hss@[0-9]+$:
|
||||
$ref: /schemas/net/intel,ixp4xx-hss.yaml#
|
||||
type: object
|
||||
description: Optional node for the High Speed Serial link (HSS), the
|
||||
node should be named with the instance number of the NPE engine
|
||||
used for the HSS.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -45,9 +59,30 @@ additionalProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
npe: npe@c8006000 {
|
||||
compatible = "intel,ixp4xx-network-processing-engine";
|
||||
reg = <0xc8006000 0x1000>, <0xc8007000 0x1000>, <0xc8008000 0x1000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hss@0 {
|
||||
compatible = "intel,ixp4xx-hss";
|
||||
reg = <0>;
|
||||
intel,npe-handle = <&npe 0>;
|
||||
intel,queue-chl-rxtrig = <&qmgr 12>;
|
||||
intel,queue-chl-txready = <&qmgr 34>;
|
||||
intel,queue-pkt-rx = <&qmgr 13>;
|
||||
intel,queue-pkt-tx = <&qmgr 14>, <&qmgr 15>, <&qmgr 16>, <&qmgr 17>;
|
||||
intel,queue-pkt-rxfree = <&qmgr 18>, <&qmgr 19>, <&qmgr 20>, <&qmgr 21>;
|
||||
intel,queue-pkt-txdone = <&qmgr 22>;
|
||||
cts-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
|
||||
rts-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
|
||||
dcd-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
|
||||
dtr-gpios = <&gpio_74 2 GPIO_ACTIVE_LOW>;
|
||||
clk-internal-gpios = <&gpio_74 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
||||
crypto {
|
||||
compatible = "intel,ixp4xx-crypto";
|
||||
|
|
|
@ -17,6 +17,7 @@ properties:
|
|||
- const: allwinner,sun7i-a20-can
|
||||
- const: allwinner,sun4i-a10-can
|
||||
- const: allwinner,sun4i-a10-can
|
||||
- const: allwinner,sun8i-r40-can
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -27,6 +28,19 @@ properties:
|
|||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: allwinner,sun8i-r40-can
|
||||
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -47,5 +61,15 @@ examples:
|
|||
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&ccu CLK_APB1_CAN>;
|
||||
};
|
||||
- |
|
||||
#define RST_BUS_CAN 68
|
||||
#define CLK_BUS_CAN 91
|
||||
can1: can@1c2bc00 {
|
||||
compatible = "allwinner,sun8i-r40-can";
|
||||
reg = <0x01c2bc00 0x400>;
|
||||
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&ccu CLK_BUS_CAN>;
|
||||
resets = <&ccu RST_BUS_CAN>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/dsa/dsa-port.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ethernet Switch port Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Andrew Lunn <andrew@lunn.ch>
|
||||
- Florian Fainelli <f.fainelli@gmail.com>
|
||||
- Vivien Didelot <vivien.didelot@gmail.com>
|
||||
|
||||
description:
|
||||
Ethernet switch port Description
|
||||
|
||||
allOf:
|
||||
- $ref: "http://devicetree.org/schemas/net/ethernet-controller.yaml#"
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: Port number
|
||||
|
||||
label:
|
||||
description:
|
||||
Describes the label associated with this port, which will become
|
||||
the netdev name
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
link:
|
||||
description:
|
||||
Should be a list of phandles to other switch's DSA port. This
|
||||
port is used as the outgoing port towards the phandle ports. The
|
||||
full routing information must be given, not just the one hop
|
||||
routes to neighbouring switches
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
|
||||
ethernet:
|
||||
description:
|
||||
Should be a phandle to a valid Ethernet device node. This host
|
||||
device is what the switch port is connected to
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
dsa-tag-protocol:
|
||||
description:
|
||||
Instead of the default, the switch will use this tag protocol if
|
||||
possible. Useful when a device supports multiple protocols and
|
||||
the default is incompatible with the Ethernet device.
|
||||
enum:
|
||||
- dsa
|
||||
- edsa
|
||||
- ocelot
|
||||
- ocelot-8021q
|
||||
- seville
|
||||
|
||||
phy-handle: true
|
||||
|
||||
phy-mode: true
|
||||
|
||||
fixed-link: true
|
||||
|
||||
mac-address: true
|
||||
|
||||
sfp: true
|
||||
|
||||
managed: true
|
||||
|
||||
rx-internal-delay-ps: true
|
||||
|
||||
tx-internal-delay-ps: true
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
...
|
|
@ -46,65 +46,9 @@ patternProperties:
|
|||
type: object
|
||||
description: Ethernet switch ports
|
||||
|
||||
allOf:
|
||||
- $ref: "http://devicetree.org/schemas/net/ethernet-controller.yaml#"
|
||||
$ref: dsa-port.yaml#
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: Port number
|
||||
|
||||
label:
|
||||
description:
|
||||
Describes the label associated with this port, which will become
|
||||
the netdev name
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
link:
|
||||
description:
|
||||
Should be a list of phandles to other switch's DSA port. This
|
||||
port is used as the outgoing port towards the phandle ports. The
|
||||
full routing information must be given, not just the one hop
|
||||
routes to neighbouring switches
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
|
||||
ethernet:
|
||||
description:
|
||||
Should be a phandle to a valid Ethernet device node. This host
|
||||
device is what the switch port is connected to
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
dsa-tag-protocol:
|
||||
description:
|
||||
Instead of the default, the switch will use this tag protocol if
|
||||
possible. Useful when a device supports multiple protocols and
|
||||
the default is incompatible with the Ethernet device.
|
||||
enum:
|
||||
- dsa
|
||||
- edsa
|
||||
- ocelot
|
||||
- ocelot-8021q
|
||||
- seville
|
||||
|
||||
phy-handle: true
|
||||
|
||||
phy-mode: true
|
||||
|
||||
fixed-link: true
|
||||
|
||||
mac-address: true
|
||||
|
||||
sfp: true
|
||||
|
||||
managed: true
|
||||
|
||||
rx-internal-delay-ps: true
|
||||
|
||||
tx-internal-delay-ps: true
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
|
|
|
@ -99,40 +99,9 @@ patternProperties:
|
|||
type: object
|
||||
description: Ethernet switch ports
|
||||
|
||||
$ref: dsa-port.yaml#
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: Port number
|
||||
|
||||
label:
|
||||
description:
|
||||
Describes the label associated with this port, which will become
|
||||
the netdev name
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
|
||||
link:
|
||||
description:
|
||||
Should be a list of phandles to other switch's DSA port. This
|
||||
port is used as the outgoing port towards the phandle ports. The
|
||||
full routing information must be given, not just the one hop
|
||||
routes to neighbouring switches
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
|
||||
ethernet:
|
||||
description:
|
||||
Should be a phandle to a valid Ethernet device node. This host
|
||||
device is what the switch port is connected to
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
phy-handle: true
|
||||
|
||||
phy-mode: true
|
||||
|
||||
fixed-link: true
|
||||
|
||||
mac-address: true
|
||||
|
||||
sfp: true
|
||||
|
||||
qca,sgmii-rxclk-falling-edge:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
|
@ -154,10 +123,7 @@ patternProperties:
|
|||
SGMII on the QCA8337, it is advised to set this unless a communication
|
||||
issue is observed.
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/engleder,tsnep.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TSN endpoint Ethernet MAC binding
|
||||
|
||||
maintainers:
|
||||
- Gerhard Engleder <gerhard@engleder-embedded.com>
|
||||
|
||||
allOf:
|
||||
- $ref: ethernet-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: engleder,tsnep
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
local-mac-address: true
|
||||
|
||||
mac-address: true
|
||||
|
||||
nvmem-cells: true
|
||||
|
||||
nvmem-cells-names: true
|
||||
|
||||
phy-connection-type:
|
||||
enum:
|
||||
- mii
|
||||
- gmii
|
||||
- rgmii
|
||||
- rgmii-id
|
||||
|
||||
phy-mode: true
|
||||
|
||||
phy-handle: true
|
||||
|
||||
mdio:
|
||||
type: object
|
||||
$ref: "mdio.yaml#"
|
||||
description: optional node for embedded MDIO controller
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
axi {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
tnsep0: ethernet@a0000000 {
|
||||
compatible = "engleder,tsnep";
|
||||
reg = <0x0 0xa0000000 0x0 0x10000>;
|
||||
interrupts = <0 89 1>;
|
||||
interrupt-parent = <&gic>;
|
||||
local-mac-address = [00 00 00 00 00 00];
|
||||
phy-mode = "rgmii";
|
||||
phy-handle = <&phy0>;
|
||||
mdio {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
suppress-preamble;
|
||||
phy0: ethernet-phy@1 {
|
||||
reg = <1>;
|
||||
rxc-skew-ps = <1080>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,100 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2021 Linaro Ltd.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/net/intel,ixp4xx-hss.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: Intel IXP4xx V.35 WAN High Speed Serial Link (HSS)
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
description: |
|
||||
The Intel IXP4xx HSS makes use of the IXP4xx NPE (Network
|
||||
Processing Engine) and the IXP4xx Queue Manager to process
|
||||
V.35 Wideband Modem (WAN) links.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,ixp4xx-hss
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: The HSS instance
|
||||
|
||||
intel,npe-handle:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle-array'
|
||||
maxItems: 1
|
||||
description: phandle to the NPE this HSS instance is using
|
||||
and the instance to use in the second cell
|
||||
|
||||
intel,queue-chl-rxtrig:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle-array'
|
||||
maxItems: 1
|
||||
description: phandle to the RX trigger queue on the NPE
|
||||
|
||||
intel,queue-chl-txready:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle-array'
|
||||
maxItems: 1
|
||||
description: phandle to the TX ready queue on the NPE
|
||||
|
||||
intel,queue-pkt-rx:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle-array'
|
||||
maxItems: 1
|
||||
description: phandle to the packet RX queue on the NPE
|
||||
|
||||
intel,queue-pkt-tx:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle-array'
|
||||
maxItems: 4
|
||||
description: phandle to the packet TX0, TX1, TX2 and TX3 queues on the NPE
|
||||
|
||||
intel,queue-pkt-rxfree:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle-array'
|
||||
maxItems: 4
|
||||
description: phandle to the packet RXFREE0, RXFREE1, RXFREE2 and
|
||||
RXFREE3 queues on the NPE
|
||||
|
||||
intel,queue-pkt-txdone:
|
||||
$ref: '/schemas/types.yaml#/definitions/phandle-array'
|
||||
maxItems: 1
|
||||
description: phandle to the packet TXDONE queue on the NPE
|
||||
|
||||
cts-gpios:
|
||||
maxItems: 1
|
||||
description: Clear To Send (CTS) GPIO line
|
||||
|
||||
rts-gpios:
|
||||
maxItems: 1
|
||||
description: Ready To Send (RTS) GPIO line
|
||||
|
||||
dcd-gpios:
|
||||
maxItems: 1
|
||||
description: Data Carrier Detect (DCD) GPIO line
|
||||
|
||||
dtr-gpios:
|
||||
maxItems: 1
|
||||
description: Data Terminal Ready (DTR) GPIO line
|
||||
|
||||
clk-internal-gpios:
|
||||
maxItems: 1
|
||||
description: Clock internal GPIO line, driving this high will make the HSS
|
||||
use internal clocking as opposed to external clocking
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- intel,npe-handle
|
||||
- intel,queue-chl-rxtrig
|
||||
- intel,queue-chl-txready
|
||||
- intel,queue-pkt-rx
|
||||
- intel,queue-pkt-tx
|
||||
- intel,queue-pkt-rxfree
|
||||
- intel,queue-pkt-txdone
|
||||
- cts-gpios
|
||||
- rts-gpios
|
||||
- dcd-gpios
|
||||
- dtr-gpios
|
||||
- clk-internal-gpios
|
||||
|
||||
additionalProperties: false
|
|
@ -0,0 +1,169 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/microchip,lan966x-switch.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip Lan966x Ethernet switch controller
|
||||
|
||||
maintainers:
|
||||
- Horatiu Vultur <horatiu.vultur@microchip.com>
|
||||
|
||||
description: |
|
||||
The lan966x switch is a multi-port Gigabit AVB/TSN Ethernet Switch with
|
||||
two integrated 10/100/1000Base-T PHYs. In addition to the integrated PHYs,
|
||||
it supports up to 2RGMII/RMII, up to 3BASE-X/SERDES/2.5GBASE-X and up to
|
||||
2 Quad-SGMII/Quad-USGMII interfaces.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^switch@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
const: microchip,lan966x-switch
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: cpu target
|
||||
- description: general control block target
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: cpu
|
||||
- const: gcb
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: register based extraction
|
||||
- description: frame dma based extraction
|
||||
- description: analyzer interrupt
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: xtr
|
||||
- const: fdma
|
||||
- const: ana
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: Reset controller used for switch core reset (soft reset)
|
||||
- description: Reset controller used for releasing the phy from reset
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: switch
|
||||
- const: phy
|
||||
|
||||
ethernet-ports:
|
||||
type: object
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
patternProperties:
|
||||
"^port@[0-9a-f]+$":
|
||||
type: object
|
||||
|
||||
$ref: "/schemas/net/ethernet-controller.yaml#"
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
'#address-cells':
|
||||
const: 1
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
reg:
|
||||
description:
|
||||
Switch port number
|
||||
|
||||
phys:
|
||||
description:
|
||||
Phandle of a Ethernet SerDes PHY
|
||||
|
||||
phy-mode:
|
||||
description:
|
||||
This specifies the interface used by the Ethernet SerDes towards
|
||||
the PHY or SFP.
|
||||
enum:
|
||||
- gmii
|
||||
- sgmii
|
||||
- qsgmii
|
||||
- 1000base-x
|
||||
- 2500base-x
|
||||
|
||||
phy-handle:
|
||||
description:
|
||||
Phandle of a Ethernet PHY.
|
||||
|
||||
sfp:
|
||||
description:
|
||||
Phandle of an SFP.
|
||||
|
||||
managed: true
|
||||
|
||||
required:
|
||||
- reg
|
||||
- phys
|
||||
- phy-mode
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- phy-handle
|
||||
- required:
|
||||
- sfp
|
||||
- managed
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- resets
|
||||
- reset-names
|
||||
- ethernet-ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
switch: switch@e0000000 {
|
||||
compatible = "microchip,lan966x-switch";
|
||||
reg = <0xe0000000 0x0100000>,
|
||||
<0xe2000000 0x0800000>;
|
||||
reg-names = "cpu", "gcb";
|
||||
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "xtr";
|
||||
resets = <&switch_reset 0>, <&phy_reset 0>;
|
||||
reset-names = "switch", "phy";
|
||||
ethernet-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port0: port@0 {
|
||||
reg = <0>;
|
||||
phy-handle = <&phy0>;
|
||||
phys = <&serdes 0 0>;
|
||||
phy-mode = "gmii";
|
||||
};
|
||||
|
||||
port1: port@1 {
|
||||
reg = <1>;
|
||||
sfp = <&sfp_eth1>;
|
||||
managed = "in-band-status";
|
||||
phys = <&serdes 2 4>;
|
||||
phy-mode = "sgmii";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,92 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/net/qcom,bam-dmux.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm BAM Data Multiplexer
|
||||
|
||||
maintainers:
|
||||
- Stephan Gerhold <stephan@gerhold.net>
|
||||
|
||||
description: |
|
||||
The BAM Data Multiplexer provides access to the network data channels
|
||||
of modems integrated into many older Qualcomm SoCs, e.g. Qualcomm MSM8916
|
||||
or MSM8974. It is built using a simple protocol layer on top of a DMA engine
|
||||
(Qualcomm BAM DMA) and bidirectional interrupts to coordinate power control.
|
||||
|
||||
Note that this schema does not directly describe a hardware block but rather
|
||||
a firmware convention that combines several other hardware blocks (such as the
|
||||
DMA engine). As such it is specific to a firmware version, not a particular
|
||||
SoC or hardware version.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,bam-dmux
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
Interrupts used by the modem to signal the AP.
|
||||
Both interrupts must be declared as IRQ_TYPE_EDGE_BOTH.
|
||||
items:
|
||||
- description: Power control
|
||||
- description: Power control acknowledgment
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: pc
|
||||
- const: pc-ack
|
||||
|
||||
qcom,smem-states:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description: State bits used by the AP to signal the modem.
|
||||
items:
|
||||
- description: Power control
|
||||
- description: Power control acknowledgment
|
||||
|
||||
qcom,smem-state-names:
|
||||
description: Names for the state bits used by the AP to signal the modem.
|
||||
items:
|
||||
- const: pc
|
||||
- const: pc-ack
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: TX DMA channel phandle
|
||||
- description: RX DMA channel phandle
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- qcom,smem-states
|
||||
- qcom,smem-state-names
|
||||
- dmas
|
||||
- dma-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
mpss: remoteproc {
|
||||
bam-dmux {
|
||||
compatible = "qcom,bam-dmux";
|
||||
|
||||
interrupt-parent = <&modem_smsm>;
|
||||
interrupts = <1 IRQ_TYPE_EDGE_BOTH>, <11 IRQ_TYPE_EDGE_BOTH>;
|
||||
interrupt-names = "pc", "pc-ack";
|
||||
|
||||
qcom,smem-states = <&apps_smsm 1>, <&apps_smsm 11>;
|
||||
qcom,smem-state-names = "pc", "pc-ack";
|
||||
|
||||
dmas = <&bam_dmux_dma 4>, <&bam_dmux_dma 5>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
||||
};
|
|
@ -0,0 +1,71 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/net/vertexcom-mse102x.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: The Vertexcom MSE102x (SPI) Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Stefan Wahren <stefan.wahren@in-tech.com>
|
||||
|
||||
description:
|
||||
Vertexcom's MSE102x are a family of HomePlug GreenPHY chips.
|
||||
They can be connected either via RGMII, RMII or SPI to a host CPU.
|
||||
|
||||
In order to use a MSE102x chip as SPI device, it must be defined as
|
||||
a child of an SPI master device in the device tree.
|
||||
|
||||
More information can be found at
|
||||
http://www.vertexcom.com/doc/MSE1022%20Product%20Brief.pdf
|
||||
|
||||
allOf:
|
||||
- $ref: ethernet-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- vertexcom,mse1021
|
||||
- vertexcom,mse1022
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
spi-cpha: true
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
spi-max-frequency:
|
||||
minimum: 6000000
|
||||
maximum: 7142857
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- spi-cpha
|
||||
- spi-cpol
|
||||
- spi-max-frequency
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi0 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ethernet@0 {
|
||||
compatible = "vertexcom,mse1021";
|
||||
reg = <0>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <23 IRQ_TYPE_EDGE_RISING>;
|
||||
spi-cpha;
|
||||
spi-cpol;
|
||||
spi-max-frequency = <7142857>;
|
||||
};
|
||||
};
|
|
@ -32,6 +32,21 @@ properties:
|
|||
clock-names:
|
||||
const: rtc
|
||||
|
||||
enable-gpios:
|
||||
maxItems: 1
|
||||
description: Used by wilc1000-spi to determine the GPIO line
|
||||
connected to the ENABLE line. If specified, reset-gpios
|
||||
must be specified as well as otherwise the driver cannot
|
||||
ensure the timing required between asserting ENABLE
|
||||
and deasserting RESET. This should be declared as an
|
||||
active-high signal.
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
description: Used by wilc1000-spi to determine the GPIO line
|
||||
connected to the RESET line. This should be declared as an
|
||||
active-low signal.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
|
@ -40,6 +55,8 @@ additionalProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
@ -51,6 +68,8 @@ examples:
|
|||
interrupts = <27 0>;
|
||||
clocks = <&pck1>;
|
||||
clock-names = "rtc";
|
||||
enable-gpios = <&pioA 5 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&pioA 6 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -150,6 +150,12 @@ properties:
|
|||
string to uniquely identify variant of the calibration data in the
|
||||
board-2.bin for designs with colliding bus and device specific ids
|
||||
|
||||
memory-region:
|
||||
maxItems: 1
|
||||
description:
|
||||
phandle to a node describing reserved memory (System RAM memory)
|
||||
used by ath11k firmware (see bindings/reserved-memory/reserved-memory.txt)
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -279,3 +285,27 @@ examples:
|
|||
"tcl2host-status-ring";
|
||||
qcom,rproc = <&q6v5_wcss>;
|
||||
};
|
||||
|
||||
- |
|
||||
|
||||
reserved-memory {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
qcn9074_0: qcn9074_0@51100000 {
|
||||
no-map;
|
||||
reg = <0x0 0x51100000 0x0 0x03500000>;
|
||||
};
|
||||
};
|
||||
|
||||
pci {
|
||||
pcie0 {
|
||||
#size-cells = <2>;
|
||||
#address-cells = <3>;
|
||||
|
||||
wifi_0: wifi@0 {
|
||||
reg = <0 0 0 0 0>;
|
||||
memory-region = <&qcn9074_0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -381,6 +381,8 @@ patternProperties:
|
|||
description: Silicon Laboratories (formerly Energy Micro AS)
|
||||
"^engicam,.*":
|
||||
description: Engicam S.r.l.
|
||||
"^engleder,.*":
|
||||
description: Engleder
|
||||
"^epcos,.*":
|
||||
description: EPCOS AG
|
||||
"^epfl,.*":
|
||||
|
@ -1278,6 +1280,8 @@ patternProperties:
|
|||
description: Variscite Ltd.
|
||||
"^vdl,.*":
|
||||
description: Van der Laan b.v.
|
||||
"^vertexcom,.*":
|
||||
description: Vertexcom Technologies, Inc.
|
||||
"^via,.*":
|
||||
description: VIA Technologies, Inc.
|
||||
"^videostrong,.*":
|
||||
|
|
|
@ -422,6 +422,17 @@ arp_all_targets
|
|||
consider the slave up only when all of the arp_ip_targets
|
||||
are reachable
|
||||
|
||||
arp_missed_max
|
||||
|
||||
Specifies the number of arp_interval monitor checks that must
|
||||
fail in order for an interface to be marked down by the ARP monitor.
|
||||
|
||||
In order to provide orderly failover semantics, backup interfaces
|
||||
are permitted an extra monitor check (i.e., they must fail
|
||||
arp_missed_max + 1 times before being marked down).
|
||||
|
||||
The default value is 2, and the allowable range is 1 - 255.
|
||||
|
||||
downdelay
|
||||
|
||||
Specifies the time, in milliseconds, to wait before disabling
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
=============================
|
||||
Flexcan CAN Controller driver
|
||||
=============================
|
||||
|
||||
Authors: Marc Kleine-Budde <mkl@pengutronix.de>,
|
||||
Dario Binacchi <dario.binacchi@amarula.solutions.com>
|
||||
|
||||
On/off RTR frames reception
|
||||
===========================
|
||||
|
||||
For most flexcan IP cores the driver supports 2 RX modes:
|
||||
|
||||
- FIFO
|
||||
- mailbox
|
||||
|
||||
The older flexcan cores (integrated into the i.MX25, i.MX28, i.MX35
|
||||
and i.MX53 SOCs) only receive RTR frames if the controller is
|
||||
configured for RX-FIFO mode.
|
||||
|
||||
The RX FIFO mode uses a hardware FIFO with a depth of 6 CAN frames,
|
||||
while the mailbox mode uses a software FIFO with a depth of up to 62
|
||||
CAN frames. With the help of the bigger buffer, the mailbox mode
|
||||
performs better under high system load situations.
|
||||
|
||||
As reception of RTR frames is part of the CAN standard, all flexcan
|
||||
cores come up in a mode where RTR reception is possible.
|
||||
|
||||
With the "rx-rtr" private flag the ability to receive RTR frames can
|
||||
be waived at the expense of losing the ability to receive RTR
|
||||
messages. This trade off is beneficial in certain use cases.
|
||||
|
||||
"rx-rtr" on
|
||||
Receive RTR frames. (default)
|
||||
|
||||
The CAN controller can and will receive RTR frames.
|
||||
|
||||
On some IP cores the controller cannot receive RTR frames in the
|
||||
more performant "RX mailbox" mode and will use "RX FIFO" mode
|
||||
instead.
|
||||
|
||||
"rx-rtr" off
|
||||
|
||||
Waive ability to receive RTR frames. (not supported on all IP cores)
|
||||
|
||||
This mode activates the "RX mailbox mode" for better performance, on
|
||||
some IP cores RTR frames cannot be received anymore.
|
||||
|
||||
The setting can only be changed if the interface is down::
|
||||
|
||||
ip link set dev can0 down
|
||||
ethtool --set-priv-flags can0 rx-rtr {off|on}
|
||||
ip link set dev can0 up
|
|
@ -0,0 +1,20 @@
|
|||
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
|
||||
Controller Area Network (CAN) Device Drivers
|
||||
============================================
|
||||
|
||||
Device drivers for CAN devices.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
freescale/flexcan
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
Indices
|
||||
=======
|
||||
|
||||
* :ref:`genindex`
|
|
@ -135,7 +135,7 @@ The ENA driver supports two Queue Operation modes for Tx SQs:
|
|||
|
||||
- **Low Latency Queue (LLQ) mode or "push-mode":**
|
||||
In this mode the driver pushes the transmit descriptors and the
|
||||
first 128 bytes of the packet directly to the ENA device memory
|
||||
first 96 bytes of the packet directly to the ENA device memory
|
||||
space. The rest of the packet payload is fetched by the
|
||||
device. For this operation mode, the driver uses a dedicated PCI
|
||||
device memory BAR, which is mapped with write-combine capability.
|
||||
|
|
|
@ -11,6 +11,7 @@ Contents:
|
|||
appletalk/index
|
||||
atm/index
|
||||
cable/index
|
||||
can/index
|
||||
cellular/index
|
||||
ethernet/index
|
||||
fddi/index
|
||||
|
|
|
@ -109,14 +109,19 @@ own name.
|
|||
- Boolean
|
||||
- When enabled, the device driver will instantiate VDPA networking
|
||||
specific auxiliary device of the devlink device.
|
||||
* - ``enable_iwarp``
|
||||
- Boolean
|
||||
- Enable handling of iWARP traffic in the device.
|
||||
* - ``internal_err_reset``
|
||||
- Boolean
|
||||
- When enabled, the device driver will reset the device on internal
|
||||
errors.
|
||||
* - ``max_macs``
|
||||
- u32
|
||||
- Specifies the maximum number of MAC addresses per ethernet port of
|
||||
this device.
|
||||
- Typically macvlan, vlan net devices mac are also programmed in their
|
||||
parent netdevice's Function rx filter. This parameter limit the
|
||||
maximum number of unicast mac address filters to receive traffic from
|
||||
per ethernet port of this device.
|
||||
* - ``region_snapshot_enable``
|
||||
- Boolean
|
||||
- Enable capture of ``devlink-region`` snapshots.
|
||||
|
@ -126,3 +131,9 @@ own name.
|
|||
will NACK any attempt of other host to reset the device. This parameter
|
||||
is useful for setups where a device is shared by different hosts, such
|
||||
as multi-host setup.
|
||||
* - ``io_eq_size``
|
||||
- u32
|
||||
- Control the size of I/O completion EQs.
|
||||
* - ``event_eq_size``
|
||||
- u32
|
||||
- Control the size of asynchronous control events EQ.
|
||||
|
|
|
@ -26,8 +26,10 @@ The ``ice`` driver reports the following versions
|
|||
* - ``fw.mgmt``
|
||||
- running
|
||||
- 2.1.7
|
||||
- 3-digit version number of the management firmware that controls the
|
||||
PHY, link, etc.
|
||||
- 3-digit version number of the management firmware running on the
|
||||
Embedded Management Processor of the device. It controls the PHY,
|
||||
link, access to device resources, etc. Intel documentation refers to
|
||||
this as the EMP firmware.
|
||||
* - ``fw.mgmt.api``
|
||||
- running
|
||||
- 1.5.1
|
||||
|
@ -119,6 +121,24 @@ preserving settings, and thus ``DEVLINK_FLASH_OVERWRITE_IDENTIFIERS`` on its
|
|||
own will be rejected. If no overwrite mask is provided, the firmware will be
|
||||
instructed to preserve all settings and identifying fields when updating.
|
||||
|
||||
Reload
|
||||
======
|
||||
|
||||
The ``ice`` driver supports activating new firmware after a flash update
|
||||
using ``DEVLINK_CMD_RELOAD`` with the ``DEVLINK_RELOAD_ACTION_FW_ACTIVATE``
|
||||
action.
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ devlink dev reload pci/0000:01:00.0 reload action fw_activate
|
||||
|
||||
The new firmware is activated by issuing a device specific Embedded
|
||||
Management Processor reset which requests the device to reset and reload the
|
||||
EMP firmware image.
|
||||
|
||||
The driver does not currently support reloading the driver via
|
||||
``DEVLINK_RELOAD_ACTION_DRIVER_REINIT``.
|
||||
|
||||
Regions
|
||||
=======
|
||||
|
||||
|
|
|
@ -14,8 +14,19 @@ Parameters
|
|||
|
||||
* - Name
|
||||
- Mode
|
||||
- Validation
|
||||
* - ``enable_roce``
|
||||
- driverinit
|
||||
- Type: Boolean
|
||||
* - ``io_eq_size``
|
||||
- driverinit
|
||||
- The range is between 64 and 4096.
|
||||
* - ``event_eq_size``
|
||||
- driverinit
|
||||
- The range is between 64 and 4096.
|
||||
* - ``max_macs``
|
||||
- driverinit
|
||||
- The range is between 1 and 2^31. Only power of 2 values are supported.
|
||||
|
||||
The ``mlx5`` driver also implements the following driver-specific
|
||||
parameters.
|
||||
|
|
|
@ -849,7 +849,7 @@ Request contents:
|
|||
|
||||
Kernel response contents:
|
||||
|
||||
==================================== ====== ==========================
|
||||
==================================== ====== ===========================
|
||||
``ETHTOOL_A_RINGS_HEADER`` nested reply header
|
||||
``ETHTOOL_A_RINGS_RX_MAX`` u32 max size of RX ring
|
||||
``ETHTOOL_A_RINGS_RX_MINI_MAX`` u32 max size of RX mini ring
|
||||
|
@ -859,7 +859,8 @@ Kernel response contents:
|
|||
``ETHTOOL_A_RINGS_RX_MINI`` u32 size of RX mini ring
|
||||
``ETHTOOL_A_RINGS_RX_JUMBO`` u32 size of RX jumbo ring
|
||||
``ETHTOOL_A_RINGS_TX`` u32 size of TX ring
|
||||
==================================== ====== ==========================
|
||||
``ETHTOOL_A_RINGS_RX_BUF_LEN`` u32 size of buffers on the ring
|
||||
==================================== ====== ===========================
|
||||
|
||||
|
||||
RINGS_SET
|
||||
|
@ -869,13 +870,14 @@ Sets ring sizes like ``ETHTOOL_SRINGPARAM`` ioctl request.
|
|||
|
||||
Request contents:
|
||||
|
||||
==================================== ====== ==========================
|
||||
==================================== ====== ===========================
|
||||
``ETHTOOL_A_RINGS_HEADER`` nested reply header
|
||||
``ETHTOOL_A_RINGS_RX`` u32 size of RX ring
|
||||
``ETHTOOL_A_RINGS_RX_MINI`` u32 size of RX mini ring
|
||||
``ETHTOOL_A_RINGS_RX_JUMBO`` u32 size of RX jumbo ring
|
||||
``ETHTOOL_A_RINGS_TX`` u32 size of TX ring
|
||||
==================================== ====== ==========================
|
||||
``ETHTOOL_A_RINGS_RX_BUF_LEN`` u32 size of buffers on the ring
|
||||
==================================== ====== ===========================
|
||||
|
||||
Kernel checks that requested ring sizes do not exceed limits reported by
|
||||
driver. Driver may impose additional constraints and may not suspport all
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -35,7 +35,7 @@ To support this, the hypervisor needs to enable VIRTIO_NET_F_STANDBY
|
|||
feature on the virtio-net interface and assign the same MAC address to both
|
||||
virtio-net and VF interfaces.
|
||||
|
||||
Here is an example XML snippet that shows such configuration.
|
||||
Here is an example libvirt XML snippet that shows such configuration:
|
||||
::
|
||||
|
||||
<interface type='network'>
|
||||
|
@ -45,18 +45,32 @@ Here is an example XML snippet that shows such configuration.
|
|||
<model type='virtio'/>
|
||||
<driver name='vhost' queues='4'/>
|
||||
<link state='down'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/>
|
||||
<teaming type='persistent'/>
|
||||
<alias name='ua-backup0'/>
|
||||
</interface>
|
||||
<interface type='hostdev' managed='yes'>
|
||||
<mac address='52:54:00:00:12:53'/>
|
||||
<source>
|
||||
<address type='pci' domain='0x0000' bus='0x42' slot='0x02' function='0x5'/>
|
||||
</source>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x0b' function='0x0'/>
|
||||
<teaming type='transient' persistent='ua-backup0'/>
|
||||
</interface>
|
||||
|
||||
In this configuration, the first device definition is for the virtio-net
|
||||
interface and this acts as the 'persistent' device indicating that this
|
||||
interface will always be plugged in. This is specified by the 'teaming' tag with
|
||||
required attribute type having value 'persistent'. The link state for the
|
||||
virtio-net device is set to 'down' to ensure that the 'failover' netdev prefers
|
||||
the VF passthrough device for normal communication. The virtio-net device will
|
||||
be brought UP during live migration to allow uninterrupted communication.
|
||||
|
||||
The second device definition is for the VF passthrough interface. Here the
|
||||
'teaming' tag is provided with type 'transient' indicating that this device may
|
||||
periodically be unplugged. A second attribute - 'persistent' is provided and
|
||||
points to the alias name declared for the virtio-net device.
|
||||
|
||||
Booting a VM with the above configuration will result in the following 3
|
||||
netdevs created in the VM.
|
||||
interfaces created in the VM:
|
||||
::
|
||||
|
||||
4: ens10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
|
||||
|
@ -65,13 +79,36 @@ netdevs created in the VM.
|
|||
valid_lft 42482sec preferred_lft 42482sec
|
||||
inet6 fe80::97d8:db2:8c10:b6d6/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
5: ens10nsby: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master ens10 state UP group default qlen 1000
|
||||
5: ens10nsby: <BROADCAST,MULTICAST> mtu 1500 qdisc fq_codel master ens10 state DOWN group default qlen 1000
|
||||
link/ether 52:54:00:00:12:53 brd ff:ff:ff:ff:ff:ff
|
||||
7: ens11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master ens10 state UP group default qlen 1000
|
||||
link/ether 52:54:00:00:12:53 brd ff:ff:ff:ff:ff:ff
|
||||
|
||||
ens10 is the 'failover' master netdev, ens10nsby and ens11 are the slave
|
||||
'standby' and 'primary' netdevs respectively.
|
||||
Here, ens10 is the 'failover' master interface, ens10nsby is the slave 'standby'
|
||||
virtio-net interface, and ens11 is the slave 'primary' VF passthrough interface.
|
||||
|
||||
One point to note here is that some user space network configuration daemons
|
||||
like systemd-networkd, ifupdown, etc, do not understand the 'net_failover'
|
||||
device; and on the first boot, the VM might end up with both 'failover' device
|
||||
and VF accquiring IP addresses (either same or different) from the DHCP server.
|
||||
This will result in lack of connectivity to the VM. So some tweaks might be
|
||||
needed to these network configuration daemons to make sure that an IP is
|
||||
received only on the 'failover' device.
|
||||
|
||||
Below is the patch snippet used with 'cloud-ifupdown-helper' script found on
|
||||
Debian cloud images:
|
||||
|
||||
::
|
||||
@@ -27,6 +27,8 @@ do_setup() {
|
||||
local working="$cfgdir/.$INTERFACE"
|
||||
local final="$cfgdir/$INTERFACE"
|
||||
|
||||
+ if [ -d "/sys/class/net/${INTERFACE}/master" ]; then exit 0; fi
|
||||
+
|
||||
if ifup --no-act "$INTERFACE" > /dev/null 2>&1; then
|
||||
# interface is already known to ifupdown, no need to generate cfg
|
||||
log "Skipping configuration generation for $INTERFACE"
|
||||
|
||||
|
||||
Live Migration of a VM with SR-IOV VF & virtio-net in STANDBY mode
|
||||
==================================================================
|
||||
|
@ -80,40 +117,68 @@ net_failover also enables hypervisor controlled live migration to be supported
|
|||
with VMs that have direct attached SR-IOV VF devices by automatic failover to
|
||||
the paravirtual datapath when the VF is unplugged.
|
||||
|
||||
Here is a sample script that shows the steps to initiate live migration on
|
||||
the source hypervisor.
|
||||
Here is a sample script that shows the steps to initiate live migration from
|
||||
the source hypervisor. Note: It is assumed that the VM is connected to a
|
||||
software bridge 'br0' which has a single VF attached to it along with the vnet
|
||||
device to the VM. This is not the VF that was passthrough'd to the VM (seen in
|
||||
the vf.xml file).
|
||||
::
|
||||
|
||||
# cat vf_xml
|
||||
# cat vf.xml
|
||||
<interface type='hostdev' managed='yes'>
|
||||
<mac address='52:54:00:00:12:53'/>
|
||||
<source>
|
||||
<address type='pci' domain='0x0000' bus='0x42' slot='0x02' function='0x5'/>
|
||||
</source>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x0b' function='0x0'/>
|
||||
<teaming type='transient' persistent='ua-backup0'/>
|
||||
</interface>
|
||||
|
||||
# Source Hypervisor
|
||||
# Source Hypervisor migrate.sh
|
||||
#!/bin/bash
|
||||
|
||||
DOMAIN=fedora27-tap01
|
||||
PF=enp66s0f0
|
||||
VF_NUM=5
|
||||
TAP_IF=tap01
|
||||
VF_XML=
|
||||
DOMAIN=vm-01
|
||||
PF=ens6np0
|
||||
VF=ens6v1 # VF attached to the bridge.
|
||||
VF_NUM=1
|
||||
TAP_IF=vmtap01 # virtio-net interface in the VM.
|
||||
VF_XML=vf.xml
|
||||
|
||||
MAC=52:54:00:00:12:53
|
||||
ZERO_MAC=00:00:00:00:00:00
|
||||
|
||||
# Set the virtio-net interface up.
|
||||
virsh domif-setlink $DOMAIN $TAP_IF up
|
||||
bridge fdb del $MAC dev $PF master
|
||||
virsh detach-device $DOMAIN $VF_XML
|
||||
|
||||
# Remove the VF that was passthrough'd to the VM.
|
||||
virsh detach-device --live --config $DOMAIN $VF_XML
|
||||
|
||||
ip link set $PF vf $VF_NUM mac $ZERO_MAC
|
||||
|
||||
virsh migrate --live $DOMAIN qemu+ssh://$REMOTE_HOST/system
|
||||
# Add FDB entry for traffic to continue going to the VM via
|
||||
# the VF -> br0 -> vnet interface path.
|
||||
bridge fdb add $MAC dev $VF
|
||||
bridge fdb add $MAC dev $TAP_IF master
|
||||
|
||||
# Destination Hypervisor
|
||||
# Migrate the VM
|
||||
virsh migrate --live --persistent $DOMAIN qemu+ssh://$REMOTE_HOST/system
|
||||
|
||||
# Clean up FDB entries after migration completes.
|
||||
bridge fdb del $MAC dev $VF
|
||||
bridge fdb del $MAC dev $TAP_IF master
|
||||
|
||||
On the destination hypervisor, a shared bridge 'br0' is created before migration
|
||||
starts, and a VF from the destination PF is added to the bridge. Similarly an
|
||||
appropriate FDB entry is added.
|
||||
|
||||
The following script is executed on the destination hypervisor once migration
|
||||
completes, and it reattaches the VF to the VM and brings down the virtio-net
|
||||
interface.
|
||||
|
||||
::
|
||||
# reattach-vf.sh
|
||||
#!/bin/bash
|
||||
|
||||
virsh attach-device $DOMAIN $VF_XML
|
||||
virsh domif-setlink $DOMAIN $TAP_IF down
|
||||
bridge fdb del 52:54:00:00:12:53 dev ens36v0
|
||||
bridge fdb del 52:54:00:00:12:53 dev vmtap01 master
|
||||
virsh attach-device --config --live vm01 vf.xml
|
||||
virsh domif-setlink vm01 vmtap01 down
|
||||
|
|
|
@ -237,6 +237,11 @@ negotiation results.
|
|||
|
||||
Some of the interface modes are described below:
|
||||
|
||||
``PHY_INTERFACE_MODE_SMII``
|
||||
This is serial MII, clocked at 125MHz, supporting 100M and 10M speeds.
|
||||
Some details can be found in
|
||||
https://opencores.org/ocsvn/smii/smii/trunk/doc/SMII.pdf
|
||||
|
||||
``PHY_INTERFACE_MODE_1000BASEX``
|
||||
This defines the 1000BASE-X single-lane serdes link as defined by the
|
||||
802.3 standard section 36. The link operates at a fixed bit rate of
|
||||
|
|
17
MAINTAINERS
17
MAINTAINERS
|
@ -3577,7 +3577,7 @@ R: Florent Revest <revest@chromium.org>
|
|||
R: Brendan Jackman <jackmanb@chromium.org>
|
||||
L: bpf@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/bpf/bpf_lsm.rst
|
||||
F: Documentation/bpf/prog_lsm.rst
|
||||
F: include/linux/bpf_lsm.h
|
||||
F: kernel/bpf/bpf_lsm.c
|
||||
F: security/bpf/
|
||||
|
@ -12560,6 +12560,13 @@ L: netdev@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/net/ethernet/microchip/lan743x_*
|
||||
|
||||
MICROCHIP LAN966X ETHERNET DRIVER
|
||||
M: Horatiu Vultur <horatiu.vultur@microchip.com>
|
||||
M: UNGLinuxDriver@microchip.com
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/ethernet/microchip/lan966x/*
|
||||
|
||||
MICROCHIP LCDFB DRIVER
|
||||
M: Nicolas Ferre <nicolas.ferre@microchip.com>
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
|
@ -15769,6 +15776,14 @@ W: https://wireless.wiki.kernel.org/en/users/Drivers/ath9k
|
|||
F: Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml
|
||||
F: drivers/net/wireless/ath/ath9k/
|
||||
|
||||
QUALCOMM BAM-DMUX WWAN NETWORK DRIVER
|
||||
M: Stephan Gerhold <stephan@gerhold.net>
|
||||
L: netdev@vger.kernel.org
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
|
||||
F: drivers/net/wwan/qcom_bam_dmux.c
|
||||
|
||||
QUALCOMM CAMERA SUBSYSTEM DRIVER
|
||||
M: Robert Foss <robert.foss@linaro.org>
|
||||
M: Todor Tomov <todor.too@gmail.com>
|
||||
|
|
|
@ -369,12 +369,10 @@
|
|||
clocks = <&rpmhcc RPMH_IPA_CLK>;
|
||||
clock-names = "core";
|
||||
|
||||
interconnects = <&system_noc MASTER_IPA &system_noc SLAVE_SNOC_MEM_NOC_GC>,
|
||||
<&mem_noc MASTER_SNOC_GC_MEM_NOC &mc_virt SLAVE_EBI_CH0>,
|
||||
interconnects = <&system_noc MASTER_IPA &mc_virt SLAVE_EBI_CH0>,
|
||||
<&system_noc MASTER_IPA &system_noc SLAVE_OCIMEM>,
|
||||
<&mem_noc MASTER_AMPSS_M0 &system_noc SLAVE_IPA_CFG>;
|
||||
interconnect-names = "memory-a",
|
||||
"memory-b",
|
||||
interconnect-names = "memory",
|
||||
"imem",
|
||||
"config";
|
||||
|
||||
|
|
|
@ -511,6 +511,16 @@
|
|||
#interrupt-cells = <3>;
|
||||
#gpio-cells = <3>;
|
||||
|
||||
can_ph_pins: can-ph-pins {
|
||||
pins = "PH20", "PH21";
|
||||
function = "can";
|
||||
};
|
||||
|
||||
can_pa_pins: can-pa-pins {
|
||||
pins = "PA16", "PA17";
|
||||
function = "can";
|
||||
};
|
||||
|
||||
clk_out_a_pin: clk-out-a-pin {
|
||||
pins = "PI12";
|
||||
function = "clk_out_a";
|
||||
|
@ -926,6 +936,15 @@
|
|||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
can0: can@1c2bc00 {
|
||||
compatible = "allwinner,sun8i-r40-can";
|
||||
reg = <0x01c2bc00 0x400>;
|
||||
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&ccu CLK_BUS_CAN>;
|
||||
resets = <&ccu RST_BUS_CAN>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c4: i2c@1c2c000 {
|
||||
compatible = "allwinner,sun6i-a31-i2c";
|
||||
reg = <0x01c2c000 0x400>;
|
||||
|
|
|
@ -163,7 +163,7 @@ static const s8 bpf2a32[][2] = {
|
|||
[BPF_REG_9] = {STACK_OFFSET(BPF_R9_HI), STACK_OFFSET(BPF_R9_LO)},
|
||||
/* Read only Frame Pointer to access Stack */
|
||||
[BPF_REG_FP] = {STACK_OFFSET(BPF_FP_HI), STACK_OFFSET(BPF_FP_LO)},
|
||||
/* Temporary Register for internal BPF JIT, can be used
|
||||
/* Temporary Register for BPF JIT, can be used
|
||||
* for constant blindings and others.
|
||||
*/
|
||||
[TMP_REG_1] = {ARM_R7, ARM_R6},
|
||||
|
@ -1199,7 +1199,8 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx)
|
|||
|
||||
/* tmp2[0] = array, tmp2[1] = index */
|
||||
|
||||
/* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
|
||||
/*
|
||||
* if (tail_call_cnt >= MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
* tail_call_cnt++;
|
||||
*/
|
||||
|
@ -1208,7 +1209,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx)
|
|||
tc = arm_bpf_get_reg64(tcc, tmp, ctx);
|
||||
emit(ARM_CMP_I(tc[0], hi), ctx);
|
||||
_emit(ARM_COND_EQ, ARM_CMP_I(tc[1], lo), ctx);
|
||||
_emit(ARM_COND_HI, ARM_B(jmp_offset), ctx);
|
||||
_emit(ARM_COND_CS, ARM_B(jmp_offset), ctx);
|
||||
emit(ARM_ADDS_I(tc[1], tc[1], 1), ctx);
|
||||
emit(ARM_ADC_I(tc[0], tc[0], 0), ctx);
|
||||
arm_bpf_put_reg64(tcc, tmp, ctx);
|
||||
|
|
|
@ -33,15 +33,6 @@ do { \
|
|||
(b)->data = (tmp).data; \
|
||||
} while (0)
|
||||
|
||||
static inline bool in_bpf_jit(struct pt_regs *regs)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_BPF_JIT))
|
||||
return false;
|
||||
|
||||
return regs->pc >= BPF_JIT_REGION_START &&
|
||||
regs->pc < BPF_JIT_REGION_END;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BPF_JIT
|
||||
bool ex_handler_bpf(const struct exception_table_entry *ex,
|
||||
struct pt_regs *regs);
|
||||
|
|
|
@ -44,11 +44,8 @@
|
|||
#define _PAGE_OFFSET(va) (-(UL(1) << (va)))
|
||||
#define PAGE_OFFSET (_PAGE_OFFSET(VA_BITS))
|
||||
#define KIMAGE_VADDR (MODULES_END)
|
||||
#define BPF_JIT_REGION_START (_PAGE_END(VA_BITS_MIN))
|
||||
#define BPF_JIT_REGION_SIZE (SZ_128M)
|
||||
#define BPF_JIT_REGION_END (BPF_JIT_REGION_START + BPF_JIT_REGION_SIZE)
|
||||
#define MODULES_END (MODULES_VADDR + MODULES_VSIZE)
|
||||
#define MODULES_VADDR (BPF_JIT_REGION_END)
|
||||
#define MODULES_VADDR (_PAGE_END(VA_BITS_MIN))
|
||||
#define MODULES_VSIZE (SZ_128M)
|
||||
#define VMEMMAP_START (-(UL(1) << (VA_BITS - VMEMMAP_SHIFT)))
|
||||
#define VMEMMAP_END (VMEMMAP_START + VMEMMAP_SIZE)
|
||||
|
|
|
@ -994,7 +994,7 @@ static struct break_hook bug_break_hook = {
|
|||
static int reserved_fault_handler(struct pt_regs *regs, unsigned int esr)
|
||||
{
|
||||
pr_err("%s generated an invalid instruction at %pS!\n",
|
||||
in_bpf_jit(regs) ? "BPF JIT" : "Kernel text patching",
|
||||
"Kernel text patching",
|
||||
(void *)instruction_pointer(regs));
|
||||
|
||||
/* We cannot handle this */
|
||||
|
|
|
@ -41,8 +41,6 @@ static struct addr_marker address_markers[] = {
|
|||
{ 0 /* KASAN_SHADOW_START */, "Kasan shadow start" },
|
||||
{ KASAN_SHADOW_END, "Kasan shadow end" },
|
||||
#endif
|
||||
{ BPF_JIT_REGION_START, "BPF start" },
|
||||
{ BPF_JIT_REGION_END, "BPF end" },
|
||||
{ MODULES_VADDR, "Modules start" },
|
||||
{ MODULES_END, "Modules end" },
|
||||
{ VMALLOC_START, "vmalloc() area" },
|
||||
|
|
|
@ -44,7 +44,7 @@ static const int bpf2a64[] = {
|
|||
[BPF_REG_9] = A64_R(22),
|
||||
/* read-only frame pointer to access stack */
|
||||
[BPF_REG_FP] = A64_R(25),
|
||||
/* temporary registers for internal BPF JIT */
|
||||
/* temporary registers for BPF JIT */
|
||||
[TMP_REG_1] = A64_R(10),
|
||||
[TMP_REG_2] = A64_R(11),
|
||||
[TMP_REG_3] = A64_R(12),
|
||||
|
@ -287,13 +287,14 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx)
|
|||
emit(A64_CMP(0, r3, tmp), ctx);
|
||||
emit(A64_B_(A64_COND_CS, jmp_offset), ctx);
|
||||
|
||||
/* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
|
||||
/*
|
||||
* if (tail_call_cnt >= MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
* tail_call_cnt++;
|
||||
*/
|
||||
emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx);
|
||||
emit(A64_CMP(1, tcc, tmp), ctx);
|
||||
emit(A64_B_(A64_COND_HI, jmp_offset), ctx);
|
||||
emit(A64_B_(A64_COND_CS, jmp_offset), ctx);
|
||||
emit(A64_ADD_I(1, tcc, tcc, 1), ctx);
|
||||
|
||||
/* prog = array->ptrs[index];
|
||||
|
@ -791,7 +792,10 @@ emit_cond_jmp:
|
|||
u64 imm64;
|
||||
|
||||
imm64 = (u64)insn1.imm << 32 | (u32)imm;
|
||||
emit_a64_mov_i64(dst, imm64, ctx);
|
||||
if (bpf_pseudo_func(insn))
|
||||
emit_addr_mov_i64(dst, imm64, ctx);
|
||||
else
|
||||
emit_a64_mov_i64(dst, imm64, ctx);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1141,15 +1145,12 @@ out:
|
|||
|
||||
u64 bpf_jit_alloc_exec_limit(void)
|
||||
{
|
||||
return BPF_JIT_REGION_SIZE;
|
||||
return VMALLOC_END - VMALLOC_START;
|
||||
}
|
||||
|
||||
void *bpf_jit_alloc_exec(unsigned long size)
|
||||
{
|
||||
return __vmalloc_node_range(size, PAGE_SIZE, BPF_JIT_REGION_START,
|
||||
BPF_JIT_REGION_END, GFP_KERNEL,
|
||||
PAGE_KERNEL, 0, NUMA_NO_NODE,
|
||||
__builtin_return_address(0));
|
||||
return vmalloc(size);
|
||||
}
|
||||
|
||||
void bpf_jit_free_exec(void *addr)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#define LTQ_DMA_H__
|
||||
|
||||
#define LTQ_DESC_SIZE 0x08 /* each descriptor is 64bit */
|
||||
#define LTQ_DESC_NUM 0x40 /* 64 descriptors / channel */
|
||||
#define LTQ_DESC_NUM 0xC0 /* 192 descriptors / channel */
|
||||
|
||||
#define LTQ_DMA_OWN BIT(31) /* owner bit */
|
||||
#define LTQ_DMA_C BIT(30) /* complete bit */
|
||||
|
|
|
@ -1381,8 +1381,7 @@ void build_prologue(struct jit_context *ctx)
|
|||
* 16-byte area in the parent's stack frame. On a tail call, the
|
||||
* calling function jumps into the prologue after these instructions.
|
||||
*/
|
||||
emit(ctx, ori, MIPS_R_T9, MIPS_R_ZERO,
|
||||
min(MAX_TAIL_CALL_CNT + 1, 0xffff));
|
||||
emit(ctx, ori, MIPS_R_T9, MIPS_R_ZERO, min(MAX_TAIL_CALL_CNT, 0xffff));
|
||||
emit(ctx, sw, MIPS_R_T9, 0, MIPS_R_SP);
|
||||
|
||||
/*
|
||||
|
|
|
@ -552,7 +552,7 @@ void build_prologue(struct jit_context *ctx)
|
|||
* On a tail call, the calling function jumps into the prologue
|
||||
* after this instruction.
|
||||
*/
|
||||
emit(ctx, addiu, tc, MIPS_R_ZERO, min(MAX_TAIL_CALL_CNT + 1, 0xffff));
|
||||
emit(ctx, ori, tc, MIPS_R_ZERO, min(MAX_TAIL_CALL_CNT, 0xffff));
|
||||
|
||||
/* === Entry-point for tail calls === */
|
||||
|
||||
|
|
|
@ -221,13 +221,13 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
|
|||
PPC_BCC(COND_GE, out);
|
||||
|
||||
/*
|
||||
* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
|
||||
* if (tail_call_cnt >= MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
*/
|
||||
EMIT(PPC_RAW_CMPLWI(_R0, MAX_TAIL_CALL_CNT));
|
||||
/* tail_call_cnt++; */
|
||||
EMIT(PPC_RAW_ADDIC(_R0, _R0, 1));
|
||||
PPC_BCC(COND_GT, out);
|
||||
PPC_BCC(COND_GE, out);
|
||||
|
||||
/* prog = array->ptrs[index]; */
|
||||
EMIT(PPC_RAW_RLWINM(_R3, b2p_index, 2, 0, 29));
|
||||
|
|
|
@ -228,12 +228,12 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
|
|||
PPC_BCC(COND_GE, out);
|
||||
|
||||
/*
|
||||
* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
|
||||
* if (tail_call_cnt >= MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
*/
|
||||
PPC_BPF_LL(b2p[TMP_REG_1], 1, bpf_jit_stack_tailcallcnt(ctx));
|
||||
EMIT(PPC_RAW_CMPLWI(b2p[TMP_REG_1], MAX_TAIL_CALL_CNT));
|
||||
PPC_BCC(COND_GT, out);
|
||||
PPC_BCC(COND_GE, out);
|
||||
|
||||
/*
|
||||
* tail_call_cnt++;
|
||||
|
|
|
@ -799,11 +799,10 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx)
|
|||
emit_bcc(BPF_JGE, lo(idx_reg), RV_REG_T1, off, ctx);
|
||||
|
||||
/*
|
||||
* temp_tcc = tcc - 1;
|
||||
* if (tcc < 0)
|
||||
* if (--tcc < 0)
|
||||
* goto out;
|
||||
*/
|
||||
emit(rv_addi(RV_REG_T1, RV_REG_TCC, -1), ctx);
|
||||
emit(rv_addi(RV_REG_TCC, RV_REG_TCC, -1), ctx);
|
||||
off = ninsns_rvoff(tc_ninsn - (ctx->ninsns - start_insn));
|
||||
emit_bcc(BPF_JSLT, RV_REG_TCC, RV_REG_ZERO, off, ctx);
|
||||
|
||||
|
@ -829,7 +828,6 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx)
|
|||
if (is_12b_check(off, insn))
|
||||
return -1;
|
||||
emit(rv_lw(RV_REG_T0, off, RV_REG_T0), ctx);
|
||||
emit(rv_addi(RV_REG_TCC, RV_REG_T1, 0), ctx);
|
||||
/* Epilogue jumps to *(t0 + 4). */
|
||||
__build_epilogue(true, ctx);
|
||||
return 0;
|
||||
|
|
|
@ -327,12 +327,12 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx)
|
|||
off = ninsns_rvoff(tc_ninsn - (ctx->ninsns - start_insn));
|
||||
emit_branch(BPF_JGE, RV_REG_A2, RV_REG_T1, off, ctx);
|
||||
|
||||
/* if (TCC-- < 0)
|
||||
/* if (--TCC < 0)
|
||||
* goto out;
|
||||
*/
|
||||
emit_addi(RV_REG_T1, tcc, -1, ctx);
|
||||
emit_addi(RV_REG_TCC, tcc, -1, ctx);
|
||||
off = ninsns_rvoff(tc_ninsn - (ctx->ninsns - start_insn));
|
||||
emit_branch(BPF_JSLT, tcc, RV_REG_ZERO, off, ctx);
|
||||
emit_branch(BPF_JSLT, RV_REG_TCC, RV_REG_ZERO, off, ctx);
|
||||
|
||||
/* prog = array->ptrs[index];
|
||||
* if (!prog)
|
||||
|
@ -352,7 +352,6 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx)
|
|||
if (is_12b_check(off, insn))
|
||||
return -1;
|
||||
emit_ld(RV_REG_T3, off, RV_REG_T2, ctx);
|
||||
emit_mv(RV_REG_TCC, RV_REG_T1, ctx);
|
||||
__build_epilogue(true, ctx);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#define KMSG_COMPONENT "hugetlb"
|
||||
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
||||
|
||||
#include <asm/pgalloc.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/mman.h>
|
||||
|
|
|
@ -1369,7 +1369,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
jit->prg);
|
||||
|
||||
/*
|
||||
* if (tail_call_cnt++ > MAX_TAIL_CALL_CNT)
|
||||
* if (tail_call_cnt++ >= MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
*/
|
||||
|
||||
|
@ -1381,9 +1381,9 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
|||
EMIT4_IMM(0xa7080000, REG_W0, 1);
|
||||
/* laal %w1,%w0,off(%r15) */
|
||||
EMIT6_DISP_LH(0xeb000000, 0x00fa, REG_W1, REG_W0, REG_15, off);
|
||||
/* clij %w1,MAX_TAIL_CALL_CNT,0x2,out */
|
||||
/* clij %w1,MAX_TAIL_CALL_CNT-1,0x2,out */
|
||||
patch_2_clij = jit->prg;
|
||||
EMIT6_PCREL_RIEC(0xec000000, 0x007f, REG_W1, MAX_TAIL_CALL_CNT,
|
||||
EMIT6_PCREL_RIEC(0xec000000, 0x007f, REG_W1, MAX_TAIL_CALL_CNT - 1,
|
||||
2, jit->prg);
|
||||
|
||||
/*
|
||||
|
|
|
@ -227,7 +227,7 @@ static const int bpf2sparc[] = {
|
|||
|
||||
[BPF_REG_AX] = G7,
|
||||
|
||||
/* temporary register for internal BPF JIT */
|
||||
/* temporary register for BPF JIT */
|
||||
[TMP_REG_1] = G1,
|
||||
[TMP_REG_2] = G2,
|
||||
[TMP_REG_3] = G3,
|
||||
|
@ -867,7 +867,7 @@ static void emit_tail_call(struct jit_ctx *ctx)
|
|||
emit(LD32 | IMMED | RS1(SP) | S13(off) | RD(tmp), ctx);
|
||||
emit_cmpi(tmp, MAX_TAIL_CALL_CNT, ctx);
|
||||
#define OFFSET2 13
|
||||
emit_branch(BGU, ctx->idx, ctx->idx + OFFSET2, ctx);
|
||||
emit_branch(BGEU, ctx->idx, ctx->idx + OFFSET2, ctx);
|
||||
emit_nop(ctx);
|
||||
|
||||
emit_alu_K(ADD, tmp, 1, ctx);
|
||||
|
|
|
@ -1441,7 +1441,9 @@ flash_fail:
|
|||
}
|
||||
|
||||
static void vector_get_ringparam(struct net_device *netdev,
|
||||
struct ethtool_ringparam *ring)
|
||||
struct ethtool_ringparam *ring,
|
||||
struct kernel_ethtool_ringparam *kernel_ring,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct vector_private *vp = netdev_priv(netdev);
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* bpf_jit_comp.c: BPF JIT compiler
|
||||
* BPF JIT compiler
|
||||
*
|
||||
* Copyright (C) 2011-2013 Eric Dumazet (eric.dumazet@gmail.com)
|
||||
* Internal BPF Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
|
||||
* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
|
||||
*/
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/filter.h>
|
||||
|
@ -412,7 +412,7 @@ static void emit_indirect_jump(u8 **pprog, int reg, u8 *ip)
|
|||
* ... bpf_tail_call(void *ctx, struct bpf_array *array, u64 index) ...
|
||||
* if (index >= array->map.max_entries)
|
||||
* goto out;
|
||||
* if (++tail_call_cnt > MAX_TAIL_CALL_CNT)
|
||||
* if (tail_call_cnt++ >= MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
* prog = array->ptrs[index];
|
||||
* if (prog == NULL)
|
||||
|
@ -446,14 +446,14 @@ static void emit_bpf_tail_call_indirect(u8 **pprog, bool *callee_regs_used,
|
|||
EMIT2(X86_JBE, offset); /* jbe out */
|
||||
|
||||
/*
|
||||
* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
|
||||
* if (tail_call_cnt++ >= MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
*/
|
||||
EMIT2_off32(0x8B, 0x85, tcc_off); /* mov eax, dword ptr [rbp - tcc_off] */
|
||||
EMIT3(0x83, 0xF8, MAX_TAIL_CALL_CNT); /* cmp eax, MAX_TAIL_CALL_CNT */
|
||||
|
||||
offset = ctx->tail_call_indirect_label - (prog + 2 - start);
|
||||
EMIT2(X86_JA, offset); /* ja out */
|
||||
EMIT2(X86_JAE, offset); /* jae out */
|
||||
EMIT3(0x83, 0xC0, 0x01); /* add eax, 1 */
|
||||
EMIT2_off32(0x89, 0x85, tcc_off); /* mov dword ptr [rbp - tcc_off], eax */
|
||||
|
||||
|
@ -504,14 +504,14 @@ static void emit_bpf_tail_call_direct(struct bpf_jit_poke_descriptor *poke,
|
|||
int offset;
|
||||
|
||||
/*
|
||||
* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
|
||||
* if (tail_call_cnt++ >= MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
*/
|
||||
EMIT2_off32(0x8B, 0x85, tcc_off); /* mov eax, dword ptr [rbp - tcc_off] */
|
||||
EMIT3(0x83, 0xF8, MAX_TAIL_CALL_CNT); /* cmp eax, MAX_TAIL_CALL_CNT */
|
||||
|
||||
offset = ctx->tail_call_direct_label - (prog + 2 - start);
|
||||
EMIT2(X86_JA, offset); /* ja out */
|
||||
EMIT2(X86_JAE, offset); /* jae out */
|
||||
EMIT3(0x83, 0xC0, 0x01); /* add eax, 1 */
|
||||
EMIT2_off32(0x89, 0x85, tcc_off); /* mov dword ptr [rbp - tcc_off], eax */
|
||||
|
||||
|
@ -1976,7 +1976,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
void *orig_call)
|
||||
{
|
||||
int ret, i, nr_args = m->nr_args;
|
||||
int stack_size = nr_args * 8;
|
||||
int regs_off, ip_off, args_off, stack_size = nr_args * 8;
|
||||
struct bpf_tramp_progs *fentry = &tprogs[BPF_TRAMP_FENTRY];
|
||||
struct bpf_tramp_progs *fexit = &tprogs[BPF_TRAMP_FEXIT];
|
||||
struct bpf_tramp_progs *fmod_ret = &tprogs[BPF_TRAMP_MODIFY_RETURN];
|
||||
|
@ -1991,14 +1991,39 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
if (!is_valid_bpf_tramp_flags(flags))
|
||||
return -EINVAL;
|
||||
|
||||
/* Generated trampoline stack layout:
|
||||
*
|
||||
* RBP + 8 [ return address ]
|
||||
* RBP + 0 [ RBP ]
|
||||
*
|
||||
* RBP - 8 [ return value ] BPF_TRAMP_F_CALL_ORIG or
|
||||
* BPF_TRAMP_F_RET_FENTRY_RET flags
|
||||
*
|
||||
* [ reg_argN ] always
|
||||
* [ ... ]
|
||||
* RBP - regs_off [ reg_arg1 ] program's ctx pointer
|
||||
*
|
||||
* RBP - args_off [ args count ] always
|
||||
*
|
||||
* RBP - ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag
|
||||
*/
|
||||
|
||||
/* room for return value of orig_call or fentry prog */
|
||||
save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET);
|
||||
if (save_ret)
|
||||
stack_size += 8;
|
||||
|
||||
regs_off = stack_size;
|
||||
|
||||
/* args count */
|
||||
stack_size += 8;
|
||||
args_off = stack_size;
|
||||
|
||||
if (flags & BPF_TRAMP_F_IP_ARG)
|
||||
stack_size += 8; /* room for IP address argument */
|
||||
|
||||
ip_off = stack_size;
|
||||
|
||||
if (flags & BPF_TRAMP_F_SKIP_FRAME)
|
||||
/* skip patched call instruction and point orig_call to actual
|
||||
* body of the kernel function.
|
||||
|
@ -2012,23 +2037,25 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
EMIT4(0x48, 0x83, 0xEC, stack_size); /* sub rsp, stack_size */
|
||||
EMIT1(0x53); /* push rbx */
|
||||
|
||||
/* Store number of arguments of the traced function:
|
||||
* mov rax, nr_args
|
||||
* mov QWORD PTR [rbp - args_off], rax
|
||||
*/
|
||||
emit_mov_imm64(&prog, BPF_REG_0, 0, (u32) nr_args);
|
||||
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -args_off);
|
||||
|
||||
if (flags & BPF_TRAMP_F_IP_ARG) {
|
||||
/* Store IP address of the traced function:
|
||||
* mov rax, QWORD PTR [rbp + 8]
|
||||
* sub rax, X86_PATCH_SIZE
|
||||
* mov QWORD PTR [rbp - stack_size], rax
|
||||
* mov QWORD PTR [rbp - ip_off], rax
|
||||
*/
|
||||
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, 8);
|
||||
EMIT4(0x48, 0x83, 0xe8, X86_PATCH_SIZE);
|
||||
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -stack_size);
|
||||
|
||||
/* Continue with stack_size for regs storage, stack will
|
||||
* be correctly restored with 'leave' instruction.
|
||||
*/
|
||||
stack_size -= 8;
|
||||
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -ip_off);
|
||||
}
|
||||
|
||||
save_regs(m, &prog, nr_args, stack_size);
|
||||
save_regs(m, &prog, nr_args, regs_off);
|
||||
|
||||
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
||||
/* arg1: mov rdi, im */
|
||||
|
@ -2040,7 +2067,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
}
|
||||
|
||||
if (fentry->nr_progs)
|
||||
if (invoke_bpf(m, &prog, fentry, stack_size,
|
||||
if (invoke_bpf(m, &prog, fentry, regs_off,
|
||||
flags & BPF_TRAMP_F_RET_FENTRY_RET))
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -2050,7 +2077,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
if (!branches)
|
||||
return -ENOMEM;
|
||||
|
||||
if (invoke_bpf_mod_ret(m, &prog, fmod_ret, stack_size,
|
||||
if (invoke_bpf_mod_ret(m, &prog, fmod_ret, regs_off,
|
||||
branches)) {
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
|
@ -2058,7 +2085,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
}
|
||||
|
||||
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
||||
restore_regs(m, &prog, nr_args, stack_size);
|
||||
restore_regs(m, &prog, nr_args, regs_off);
|
||||
|
||||
/* call original function */
|
||||
if (emit_call(&prog, orig_call, prog)) {
|
||||
|
@ -2088,13 +2115,13 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|||
}
|
||||
|
||||
if (fexit->nr_progs)
|
||||
if (invoke_bpf(m, &prog, fexit, stack_size, false)) {
|
||||
if (invoke_bpf(m, &prog, fexit, regs_off, false)) {
|
||||
ret = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (flags & BPF_TRAMP_F_RESTORE_REGS)
|
||||
restore_regs(m, &prog, nr_args, stack_size);
|
||||
restore_regs(m, &prog, nr_args, regs_off);
|
||||
|
||||
/* This needs to be done regardless. If there were fmod_ret programs,
|
||||
* the return value is only updated on the stack and still needs to be
|
||||
|
|
|
@ -1323,7 +1323,7 @@ static void emit_bpf_tail_call(u8 **pprog, u8 *ip)
|
|||
EMIT2(IA32_JBE, jmp_label(jmp_label1, 2));
|
||||
|
||||
/*
|
||||
* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
|
||||
* if (tail_call_cnt++ >= MAX_TAIL_CALL_CNT)
|
||||
* goto out;
|
||||
*/
|
||||
lo = (u32)MAX_TAIL_CALL_CNT;
|
||||
|
@ -1337,7 +1337,7 @@ static void emit_bpf_tail_call(u8 **pprog, u8 *ip)
|
|||
/* cmp ecx,lo */
|
||||
EMIT3(0x83, add_1reg(0xF8, IA32_ECX), lo);
|
||||
|
||||
/* ja out */
|
||||
/* jae out */
|
||||
EMIT2(IA32_JAE, jmp_label(jmp_label1, 2));
|
||||
|
||||
/* add eax,0x1 */
|
||||
|
|
|
@ -876,6 +876,7 @@ struct regmap *__regmap_init(struct device *dev,
|
|||
if (!bus) {
|
||||
map->reg_read = config->reg_read;
|
||||
map->reg_write = config->reg_write;
|
||||
map->reg_update_bits = config->reg_update_bits;
|
||||
|
||||
map->defer_caching = false;
|
||||
goto skip_format_initialization;
|
||||
|
|
|
@ -19,6 +19,10 @@ config BT_QCA
|
|||
tristate
|
||||
select FW_LOADER
|
||||
|
||||
config BT_MTK
|
||||
tristate
|
||||
select FW_LOADER
|
||||
|
||||
config BT_HCIBTUSB
|
||||
tristate "HCI USB driver"
|
||||
depends on USB
|
||||
|
@ -55,6 +59,7 @@ config BT_HCIBTUSB_BCM
|
|||
config BT_HCIBTUSB_MTK
|
||||
bool "MediaTek protocol support"
|
||||
depends on BT_HCIBTUSB
|
||||
select BT_MTK
|
||||
default n
|
||||
help
|
||||
The MediaTek protocol support enables firmware download
|
||||
|
@ -383,6 +388,7 @@ config BT_ATH3K
|
|||
config BT_MTKSDIO
|
||||
tristate "MediaTek HCI SDIO driver"
|
||||
depends on MMC
|
||||
select BT_MTK
|
||||
help
|
||||
MediaTek Bluetooth HCI SDIO driver.
|
||||
This driver is required if you want to use MediaTek Bluetooth
|
||||
|
|
|
@ -25,6 +25,7 @@ obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
|
|||
obj-$(CONFIG_BT_BCM) += btbcm.o
|
||||
obj-$(CONFIG_BT_RTL) += btrtl.o
|
||||
obj-$(CONFIG_BT_QCA) += btqca.o
|
||||
obj-$(CONFIG_BT_MTK) += btmtk.o
|
||||
|
||||
obj-$(CONFIG_BT_VIRTIO) += virtio_bt.o
|
||||
|
||||
|
|
|
@ -628,6 +628,9 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
|
|||
data->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
|
||||
data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
|
||||
|
||||
if (!data->bulk_pkt_size)
|
||||
goto done;
|
||||
|
||||
rwlock_init(&data->lock);
|
||||
|
||||
data->reassembly = NULL;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
@ -343,6 +344,52 @@ static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev)
|
|||
return skb;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id disable_broken_read_transmit_power[] = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro16,4"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir8,1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir8,2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "iMac20,1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Apple Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "iMac20,2"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int btbcm_read_info(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
@ -363,6 +410,10 @@ static int btbcm_read_info(struct hci_dev *hdev)
|
|||
bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]);
|
||||
kfree_skb(skb);
|
||||
|
||||
/* Read DMI and disable broken Read LE Min/Max Tx Power */
|
||||
if (dmi_first_match(disable_broken_read_transmit_power))
|
||||
set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2081,14 +2081,16 @@ static int btintel_prepare_fw_download_tlv(struct hci_dev *hdev,
|
|||
if (ver->img_type == 0x03) {
|
||||
btintel_clear_flag(hdev, INTEL_BOOTLOADER);
|
||||
btintel_check_bdaddr(hdev);
|
||||
}
|
||||
|
||||
/* If the OTP has no valid Bluetooth device address, then there will
|
||||
* also be no valid address for the operational firmware.
|
||||
*/
|
||||
if (!bacmp(&ver->otp_bd_addr, BDADDR_ANY)) {
|
||||
bt_dev_info(hdev, "No device address configured");
|
||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||
} else {
|
||||
/*
|
||||
* Check for valid bd address in boot loader mode. Device
|
||||
* will be marked as unconfigured if empty bd address is
|
||||
* found.
|
||||
*/
|
||||
if (!bacmp(&ver->otp_bd_addr, BDADDR_ANY)) {
|
||||
bt_dev_info(hdev, "No device address configured");
|
||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||
}
|
||||
}
|
||||
|
||||
btintel_get_fw_name_tlv(ver, fwname, sizeof(fwname), "sfi");
|
||||
|
@ -2353,8 +2355,15 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
|||
* As a workaround, send HCI Reset command first which will reset the
|
||||
* number of completed commands and allow normal command processing
|
||||
* from now on.
|
||||
*
|
||||
* Regarding the INTEL_BROKEN_SHUTDOWN_LED flag, these devices maybe
|
||||
* in the SW_RFKILL ON state as a workaround of fixing LED issue during
|
||||
* the shutdown() procedure, and once the device is in SW_RFKILL ON
|
||||
* state, the only way to exit out of it is sending the HCI_Reset
|
||||
* command.
|
||||
*/
|
||||
if (btintel_test_flag(hdev, INTEL_BROKEN_INITIAL_NCMD)) {
|
||||
if (btintel_test_flag(hdev, INTEL_BROKEN_INITIAL_NCMD) ||
|
||||
btintel_test_flag(hdev, INTEL_BROKEN_SHUTDOWN_LED)) {
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
|
@ -2426,12 +2435,6 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
|||
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
|
||||
&hdev->quirks);
|
||||
|
||||
/* These devices have an issue with LED which doesn't
|
||||
* go off immediately during shutdown. Set the flag
|
||||
* here to send the LED OFF command during shutdown.
|
||||
*/
|
||||
btintel_set_flag(hdev, INTEL_BROKEN_LED);
|
||||
|
||||
err = btintel_legacy_rom_setup(hdev, &ver);
|
||||
break;
|
||||
case 0x0b: /* SfP */
|
||||
|
@ -2466,6 +2469,10 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
|||
goto exit_error;
|
||||
}
|
||||
|
||||
/* memset ver_tlv to start with clean state as few fields are exclusive
|
||||
* to bootloader mode and are not populated in operational mode
|
||||
*/
|
||||
memset(&ver_tlv, 0, sizeof(ver_tlv));
|
||||
/* For TLV type device, parse the tlv data */
|
||||
err = btintel_parse_version_tlv(hdev, &ver_tlv, skb);
|
||||
if (err) {
|
||||
|
@ -2492,10 +2499,14 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
|||
case 0x12: /* ThP */
|
||||
case 0x13: /* HrP */
|
||||
case 0x14: /* CcP */
|
||||
/* Some legacy bootloader devices from JfP supports both old
|
||||
* and TLV based HCI_Intel_Read_Version command. But we don't
|
||||
* want to use the TLV based setup routines for those legacy
|
||||
* bootloader device.
|
||||
/* Some legacy bootloader devices starting from JfP,
|
||||
* the operational firmware supports both old and TLV based
|
||||
* HCI_Intel_Read_Version command based on the command
|
||||
* parameter.
|
||||
*
|
||||
* For upgrading firmware case, the TLV based version cannot
|
||||
* be used because the firmware filename for legacy bootloader
|
||||
* is based on the old format.
|
||||
*
|
||||
* Also, it is not easy to convert TLV based version from the
|
||||
* legacy version format.
|
||||
|
@ -2507,6 +2518,20 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
|||
err = btintel_read_version(hdev, &ver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Apply the device specific HCI quirks
|
||||
*
|
||||
* All Legacy bootloader devices support WBS
|
||||
*/
|
||||
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
|
||||
|
||||
/* Valid LE States quirk for JfP/ThP familiy */
|
||||
if (ver.hw_variant == 0x11 || ver.hw_variant == 0x12)
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
|
||||
|
||||
/* Setup MSFT Extension support */
|
||||
btintel_set_msft_opcode(hdev, ver.hw_variant);
|
||||
|
||||
err = btintel_bootloader_setup(hdev, &ver);
|
||||
break;
|
||||
case 0x17:
|
||||
|
@ -2562,9 +2587,10 @@ static int btintel_shutdown_combined(struct hci_dev *hdev)
|
|||
|
||||
/* Some platforms have an issue with BT LED when the interface is
|
||||
* down or BT radio is turned off, which takes 5 seconds to BT LED
|
||||
* goes off. This command turns off the BT LED immediately.
|
||||
* goes off. As a workaround, sends HCI_Intel_SW_RFKILL to put the
|
||||
* device in the RFKILL ON state which turns off the BT LED immediately.
|
||||
*/
|
||||
if (btintel_test_flag(hdev, INTEL_BROKEN_LED)) {
|
||||
if (btintel_test_flag(hdev, INTEL_BROKEN_SHUTDOWN_LED)) {
|
||||
skb = __hci_cmd_sync(hdev, 0xfc3f, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
|
|
|
@ -150,7 +150,7 @@ enum {
|
|||
INTEL_FIRMWARE_FAILED,
|
||||
INTEL_BOOTING,
|
||||
INTEL_BROKEN_INITIAL_NCMD,
|
||||
INTEL_BROKEN_LED,
|
||||
INTEL_BROKEN_SHUTDOWN_LED,
|
||||
INTEL_ROM_LEGACY,
|
||||
|
||||
__INTEL_NUM_FLAGS,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*
|
||||
* Marvell Bluetooth driver
|
||||
*
|
||||
* Copyright (C) 2009, Marvell International Ltd.
|
||||
|
|
|
@ -0,0 +1,290 @@
|
|||
// SPDX-License-Identifier: ISC
|
||||
/* Copyright (C) 2021 MediaTek Inc.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "btmtk.h"
|
||||
|
||||
#define VERSION "0.1"
|
||||
|
||||
/* It is for mt79xx download rom patch*/
|
||||
#define MTK_FW_ROM_PATCH_HEADER_SIZE 32
|
||||
#define MTK_FW_ROM_PATCH_GD_SIZE 64
|
||||
#define MTK_FW_ROM_PATCH_SEC_MAP_SIZE 64
|
||||
#define MTK_SEC_MAP_COMMON_SIZE 12
|
||||
#define MTK_SEC_MAP_NEED_SEND_SIZE 52
|
||||
|
||||
struct btmtk_patch_header {
|
||||
u8 datetime[16];
|
||||
u8 platform[4];
|
||||
__le16 hwver;
|
||||
__le16 swver;
|
||||
__le32 magicnum;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_global_desc {
|
||||
__le32 patch_ver;
|
||||
__le32 sub_sys;
|
||||
__le32 feature_opt;
|
||||
__le32 section_num;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_section_map {
|
||||
__le32 sectype;
|
||||
__le32 secoffset;
|
||||
__le32 secsize;
|
||||
union {
|
||||
__le32 u4SecSpec[13];
|
||||
struct {
|
||||
__le32 dlAddr;
|
||||
__le32 dlsize;
|
||||
__le32 seckeyidx;
|
||||
__le32 alignlen;
|
||||
__le32 sectype;
|
||||
__le32 dlmodecrctype;
|
||||
__le32 crc;
|
||||
__le32 reserved[6];
|
||||
} bin_info_spec;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
|
||||
wmt_cmd_sync_func_t wmt_cmd_sync)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
struct btmtk_global_desc *globaldesc = NULL;
|
||||
struct btmtk_section_map *sectionmap;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
const u8 *fw_bin_ptr;
|
||||
int err, dlen, i, status;
|
||||
u8 flag, first_block, retry;
|
||||
u32 section_num, dl_size, section_offset;
|
||||
u8 cmd[64];
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
fw_bin_ptr = fw_ptr;
|
||||
globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
|
||||
section_num = le32_to_cpu(globaldesc->section_num);
|
||||
|
||||
for (i = 0; i < section_num; i++) {
|
||||
first_block = 1;
|
||||
fw_ptr = fw_bin_ptr;
|
||||
sectionmap = (struct btmtk_section_map *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
|
||||
MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
|
||||
|
||||
section_offset = le32_to_cpu(sectionmap->secoffset);
|
||||
dl_size = le32_to_cpu(sectionmap->bin_info_spec.dlsize);
|
||||
|
||||
if (dl_size > 0) {
|
||||
retry = 20;
|
||||
while (retry > 0) {
|
||||
cmd[0] = 0; /* 0 means legacy dl mode. */
|
||||
memcpy(cmd + 1,
|
||||
fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
|
||||
MTK_FW_ROM_PATCH_GD_SIZE +
|
||||
MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i +
|
||||
MTK_SEC_MAP_COMMON_SIZE,
|
||||
MTK_SEC_MAP_NEED_SEND_SIZE + 1);
|
||||
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = &status;
|
||||
wmt_params.flag = 0;
|
||||
wmt_params.dlen = MTK_SEC_MAP_NEED_SEND_SIZE + 1;
|
||||
wmt_params.data = &cmd;
|
||||
|
||||
err = wmt_cmd_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
if (status == BTMTK_WMT_PATCH_UNDONE) {
|
||||
break;
|
||||
} else if (status == BTMTK_WMT_PATCH_PROGRESS) {
|
||||
msleep(100);
|
||||
retry--;
|
||||
} else if (status == BTMTK_WMT_PATCH_DONE) {
|
||||
goto next_section;
|
||||
} else {
|
||||
bt_dev_err(hdev, "Failed wmt patch dwnld status (%d)",
|
||||
status);
|
||||
err = -EIO;
|
||||
goto err_release_fw;
|
||||
}
|
||||
}
|
||||
|
||||
fw_ptr += section_offset;
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
while (dl_size > 0) {
|
||||
dlen = min_t(int, 250, dl_size);
|
||||
if (first_block == 1) {
|
||||
flag = 1;
|
||||
first_block = 0;
|
||||
} else if (dl_size - dlen <= 0) {
|
||||
flag = 3;
|
||||
} else {
|
||||
flag = 2;
|
||||
}
|
||||
|
||||
wmt_params.flag = flag;
|
||||
wmt_params.dlen = dlen;
|
||||
wmt_params.data = fw_ptr;
|
||||
|
||||
err = wmt_cmd_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
dl_size -= dlen;
|
||||
fw_ptr += dlen;
|
||||
}
|
||||
}
|
||||
next_section:
|
||||
continue;
|
||||
}
|
||||
/* Wait a few moments for firmware activation done */
|
||||
usleep_range(100000, 120000);
|
||||
|
||||
err_release_fw:
|
||||
release_firmware(fw);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmtk_setup_firmware_79xx);
|
||||
|
||||
int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
|
||||
wmt_cmd_sync_func_t wmt_cmd_sync)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
size_t fw_size;
|
||||
int err, dlen;
|
||||
u8 flag, param;
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Power on data RAM the firmware relies on. */
|
||||
param = 1;
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 3;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
err = wmt_cmd_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to power on data RAM (%d)", err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
fw_size = fw->size;
|
||||
|
||||
/* The size of patch header is 30 bytes, should be skip */
|
||||
if (fw_size < 30) {
|
||||
err = -EINVAL;
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
fw_size -= 30;
|
||||
fw_ptr += 30;
|
||||
flag = 1;
|
||||
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
while (fw_size > 0) {
|
||||
dlen = min_t(int, 250, fw_size);
|
||||
|
||||
/* Tell device the position in sequence */
|
||||
if (fw_size - dlen <= 0)
|
||||
flag = 3;
|
||||
else if (fw_size < fw->size - 30)
|
||||
flag = 2;
|
||||
|
||||
wmt_params.flag = flag;
|
||||
wmt_params.dlen = dlen;
|
||||
wmt_params.data = fw_ptr;
|
||||
|
||||
err = wmt_cmd_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
fw_size -= dlen;
|
||||
fw_ptr += dlen;
|
||||
}
|
||||
|
||||
wmt_params.op = BTMTK_WMT_RST;
|
||||
wmt_params.flag = 4;
|
||||
wmt_params.dlen = 0;
|
||||
wmt_params.data = NULL;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
/* Activate funciton the firmware providing to */
|
||||
err = wmt_cmd_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
/* Wait a few moments for firmware activation done */
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
err_release_fw:
|
||||
release_firmware(fw);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmtk_setup_firmware);
|
||||
|
||||
int btmtk_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
long ret;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc1a, 6, bdaddr, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "changing Mediatek device address failed (%ld)",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmtk_set_bdaddr);
|
||||
|
||||
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
|
||||
MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>");
|
||||
MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7663);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7668);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7961);
|
|
@ -0,0 +1,111 @@
|
|||
/* SPDX-License-Identifier: ISC */
|
||||
/* Copyright (C) 2021 MediaTek Inc. */
|
||||
|
||||
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
|
||||
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
|
||||
#define FIRMWARE_MT7961 "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
|
||||
|
||||
#define HCI_WMT_MAX_EVENT_SIZE 64
|
||||
|
||||
#define BTMTK_WMT_REG_READ 0x2
|
||||
|
||||
enum {
|
||||
BTMTK_WMT_PATCH_DWNLD = 0x1,
|
||||
BTMTK_WMT_TEST = 0x2,
|
||||
BTMTK_WMT_WAKEUP = 0x3,
|
||||
BTMTK_WMT_HIF = 0x4,
|
||||
BTMTK_WMT_FUNC_CTRL = 0x6,
|
||||
BTMTK_WMT_RST = 0x7,
|
||||
BTMTK_WMT_REGISTER = 0x8,
|
||||
BTMTK_WMT_SEMAPHORE = 0x17,
|
||||
};
|
||||
|
||||
enum {
|
||||
BTMTK_WMT_INVALID,
|
||||
BTMTK_WMT_PATCH_UNDONE,
|
||||
BTMTK_WMT_PATCH_PROGRESS,
|
||||
BTMTK_WMT_PATCH_DONE,
|
||||
BTMTK_WMT_ON_UNDONE,
|
||||
BTMTK_WMT_ON_DONE,
|
||||
BTMTK_WMT_ON_PROGRESS,
|
||||
};
|
||||
|
||||
struct btmtk_wmt_hdr {
|
||||
u8 dir;
|
||||
u8 op;
|
||||
__le16 dlen;
|
||||
u8 flag;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_cmd {
|
||||
struct btmtk_wmt_hdr hdr;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt {
|
||||
struct hci_event_hdr hhdr;
|
||||
struct btmtk_wmt_hdr whdr;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt_funcc {
|
||||
struct btmtk_hci_wmt_evt hwhdr;
|
||||
__be16 status;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt_reg {
|
||||
struct btmtk_hci_wmt_evt hwhdr;
|
||||
u8 rsv[2];
|
||||
u8 num;
|
||||
__le32 addr;
|
||||
__le32 val;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_tci_sleep {
|
||||
u8 mode;
|
||||
__le16 duration;
|
||||
__le16 host_duration;
|
||||
u8 host_wakeup_pin;
|
||||
u8 time_compensation;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_params {
|
||||
u8 op;
|
||||
u8 flag;
|
||||
u16 dlen;
|
||||
const void *data;
|
||||
u32 *status;
|
||||
};
|
||||
|
||||
typedef int (*wmt_cmd_sync_func_t)(struct hci_dev *,
|
||||
struct btmtk_hci_wmt_params *);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_MTK)
|
||||
|
||||
int btmtk_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||
|
||||
int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
|
||||
wmt_cmd_sync_func_t wmt_cmd_sync);
|
||||
|
||||
int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
|
||||
wmt_cmd_sync_func_t wmt_cmd_sync);
|
||||
#else
|
||||
|
||||
static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
|
||||
const bdaddr_t *bdaddr)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
|
||||
wmt_cmd_sync_func_t wmt_cmd_sync)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
|
||||
wmt_cmd_sync_func_t wmt_cmd_sync)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -28,26 +27,32 @@
|
|||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "h4_recv.h"
|
||||
#include "btmtk.h"
|
||||
|
||||
#define VERSION "0.1"
|
||||
|
||||
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
|
||||
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
|
||||
|
||||
#define MTKBTSDIO_AUTOSUSPEND_DELAY 8000
|
||||
|
||||
static bool enable_autosuspend;
|
||||
|
||||
struct btmtksdio_data {
|
||||
const char *fwname;
|
||||
u16 chipid;
|
||||
};
|
||||
|
||||
static const struct btmtksdio_data mt7663_data = {
|
||||
.fwname = FIRMWARE_MT7663,
|
||||
.chipid = 0x7663,
|
||||
};
|
||||
|
||||
static const struct btmtksdio_data mt7668_data = {
|
||||
.fwname = FIRMWARE_MT7668,
|
||||
.chipid = 0x7668,
|
||||
};
|
||||
|
||||
static const struct btmtksdio_data mt7921_data = {
|
||||
.fwname = FIRMWARE_MT7961,
|
||||
.chipid = 0x7921,
|
||||
};
|
||||
|
||||
static const struct sdio_device_id btmtksdio_table[] = {
|
||||
|
@ -55,6 +60,8 @@ static const struct sdio_device_id btmtksdio_table[] = {
|
|||
.driver_data = (kernel_ulong_t)&mt7663_data },
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, SDIO_DEVICE_ID_MEDIATEK_MT7668),
|
||||
.driver_data = (kernel_ulong_t)&mt7668_data },
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, SDIO_DEVICE_ID_MEDIATEK_MT7961),
|
||||
.driver_data = (kernel_ulong_t)&mt7921_data },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(sdio, btmtksdio_table);
|
||||
|
@ -86,28 +93,13 @@ MODULE_DEVICE_TABLE(sdio, btmtksdio_table);
|
|||
|
||||
#define MTK_REG_CRDR 0x1c
|
||||
|
||||
#define MTK_REG_CRPLR 0x24
|
||||
|
||||
#define MTK_SDIO_BLOCK_SIZE 256
|
||||
|
||||
#define BTMTKSDIO_TX_WAIT_VND_EVT 1
|
||||
|
||||
enum {
|
||||
MTK_WMT_PATCH_DWNLD = 0x1,
|
||||
MTK_WMT_TEST = 0x2,
|
||||
MTK_WMT_WAKEUP = 0x3,
|
||||
MTK_WMT_HIF = 0x4,
|
||||
MTK_WMT_FUNC_CTRL = 0x6,
|
||||
MTK_WMT_RST = 0x7,
|
||||
MTK_WMT_SEMAPHORE = 0x17,
|
||||
};
|
||||
|
||||
enum {
|
||||
BTMTK_WMT_INVALID,
|
||||
BTMTK_WMT_PATCH_UNDONE,
|
||||
BTMTK_WMT_PATCH_DONE,
|
||||
BTMTK_WMT_ON_UNDONE,
|
||||
BTMTK_WMT_ON_DONE,
|
||||
BTMTK_WMT_ON_PROGRESS,
|
||||
};
|
||||
#define BTMTKSDIO_HW_TX_READY 2
|
||||
#define BTMTKSDIO_FUNC_ENABLED 3
|
||||
|
||||
struct mtkbtsdio_hdr {
|
||||
__le16 len;
|
||||
|
@ -115,50 +107,12 @@ struct mtkbtsdio_hdr {
|
|||
u8 bt_type;
|
||||
} __packed;
|
||||
|
||||
struct mtk_wmt_hdr {
|
||||
u8 dir;
|
||||
u8 op;
|
||||
__le16 dlen;
|
||||
u8 flag;
|
||||
} __packed;
|
||||
|
||||
struct mtk_hci_wmt_cmd {
|
||||
struct mtk_wmt_hdr hdr;
|
||||
u8 data[256];
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt {
|
||||
struct hci_event_hdr hhdr;
|
||||
struct mtk_wmt_hdr whdr;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt_funcc {
|
||||
struct btmtk_hci_wmt_evt hwhdr;
|
||||
__be16 status;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_tci_sleep {
|
||||
u8 mode;
|
||||
__le16 duration;
|
||||
__le16 host_duration;
|
||||
u8 host_wakeup_pin;
|
||||
u8 time_compensation;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_params {
|
||||
u8 op;
|
||||
u8 flag;
|
||||
u16 dlen;
|
||||
const void *data;
|
||||
u32 *status;
|
||||
};
|
||||
|
||||
struct btmtksdio_dev {
|
||||
struct hci_dev *hdev;
|
||||
struct sdio_func *func;
|
||||
struct device *dev;
|
||||
|
||||
struct work_struct tx_work;
|
||||
struct work_struct txrx_work;
|
||||
unsigned long tx_state;
|
||||
struct sk_buff_head txq;
|
||||
|
||||
|
@ -172,29 +126,35 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
|
|||
{
|
||||
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
|
||||
struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc;
|
||||
struct btmtk_hci_wmt_evt_reg *wmt_evt_reg;
|
||||
u32 hlen, status = BTMTK_WMT_INVALID;
|
||||
struct btmtk_hci_wmt_evt *wmt_evt;
|
||||
struct mtk_hci_wmt_cmd wc;
|
||||
struct mtk_wmt_hdr *hdr;
|
||||
struct btmtk_hci_wmt_cmd *wc;
|
||||
struct btmtk_wmt_hdr *hdr;
|
||||
int err;
|
||||
|
||||
/* Send the WMT command and wait until the WMT event returns */
|
||||
hlen = sizeof(*hdr) + wmt_params->dlen;
|
||||
if (hlen > 255)
|
||||
return -EINVAL;
|
||||
|
||||
hdr = (struct mtk_wmt_hdr *)&wc;
|
||||
wc = kzalloc(hlen, GFP_KERNEL);
|
||||
if (!wc)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = &wc->hdr;
|
||||
hdr->dir = 1;
|
||||
hdr->op = wmt_params->op;
|
||||
hdr->dlen = cpu_to_le16(wmt_params->dlen + 1);
|
||||
hdr->flag = wmt_params->flag;
|
||||
memcpy(wc.data, wmt_params->data, wmt_params->dlen);
|
||||
memcpy(wc->data, wmt_params->data, wmt_params->dlen);
|
||||
|
||||
set_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state);
|
||||
|
||||
err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc);
|
||||
err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc);
|
||||
if (err < 0) {
|
||||
clear_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state);
|
||||
return err;
|
||||
goto err_free_wc;
|
||||
}
|
||||
|
||||
/* The vendor specific WMT commands are all answered by a vendor
|
||||
|
@ -211,13 +171,14 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
|
|||
if (err == -EINTR) {
|
||||
bt_dev_err(hdev, "Execution of wmt command interrupted");
|
||||
clear_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state);
|
||||
return err;
|
||||
goto err_free_wc;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Execution of wmt command timed out");
|
||||
clear_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state);
|
||||
return -ETIMEDOUT;
|
||||
err = -ETIMEDOUT;
|
||||
goto err_free_wc;
|
||||
}
|
||||
|
||||
/* Parse and handle the return WMT event */
|
||||
|
@ -230,13 +191,13 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
|
|||
}
|
||||
|
||||
switch (wmt_evt->whdr.op) {
|
||||
case MTK_WMT_SEMAPHORE:
|
||||
case BTMTK_WMT_SEMAPHORE:
|
||||
if (wmt_evt->whdr.flag == 2)
|
||||
status = BTMTK_WMT_PATCH_UNDONE;
|
||||
else
|
||||
status = BTMTK_WMT_PATCH_DONE;
|
||||
break;
|
||||
case MTK_WMT_FUNC_CTRL:
|
||||
case BTMTK_WMT_FUNC_CTRL:
|
||||
wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
|
||||
if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
|
||||
status = BTMTK_WMT_ON_DONE;
|
||||
|
@ -245,6 +206,19 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
|
|||
else
|
||||
status = BTMTK_WMT_ON_UNDONE;
|
||||
break;
|
||||
case BTMTK_WMT_PATCH_DWNLD:
|
||||
if (wmt_evt->whdr.flag == 2)
|
||||
status = BTMTK_WMT_PATCH_DONE;
|
||||
else if (wmt_evt->whdr.flag == 1)
|
||||
status = BTMTK_WMT_PATCH_PROGRESS;
|
||||
else
|
||||
status = BTMTK_WMT_PATCH_UNDONE;
|
||||
break;
|
||||
case BTMTK_WMT_REGISTER:
|
||||
wmt_evt_reg = (struct btmtk_hci_wmt_evt_reg *)wmt_evt;
|
||||
if (le16_to_cpu(wmt_evt->whdr.dlen) == 12)
|
||||
status = le32_to_cpu(wmt_evt_reg->val);
|
||||
break;
|
||||
}
|
||||
|
||||
if (wmt_params->status)
|
||||
|
@ -253,6 +227,8 @@ static int mtk_hci_wmt_sync(struct hci_dev *hdev,
|
|||
err_free_skb:
|
||||
kfree_skb(bdev->evt_skb);
|
||||
bdev->evt_skb = NULL;
|
||||
err_free_wc:
|
||||
kfree(wc);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -279,6 +255,7 @@ static int btmtksdio_tx_packet(struct btmtksdio_dev *bdev,
|
|||
sdio_hdr->reserved = cpu_to_le16(0);
|
||||
sdio_hdr->bt_type = hci_skb_pkt_type(skb);
|
||||
|
||||
clear_bit(BTMTKSDIO_HW_TX_READY, &bdev->tx_state);
|
||||
err = sdio_writesb(bdev->func, MTK_REG_CTDR, skb->data,
|
||||
round_up(skb->len, MTK_SDIO_BLOCK_SIZE));
|
||||
if (err < 0)
|
||||
|
@ -301,32 +278,6 @@ static u32 btmtksdio_drv_own_query(struct btmtksdio_dev *bdev)
|
|||
return sdio_readl(bdev->func, MTK_REG_CHLPCR, NULL);
|
||||
}
|
||||
|
||||
static void btmtksdio_tx_work(struct work_struct *work)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = container_of(work, struct btmtksdio_dev,
|
||||
tx_work);
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
pm_runtime_get_sync(bdev->dev);
|
||||
|
||||
sdio_claim_host(bdev->func);
|
||||
|
||||
while ((skb = skb_dequeue(&bdev->txq))) {
|
||||
err = btmtksdio_tx_packet(bdev, skb);
|
||||
if (err < 0) {
|
||||
bdev->hdev->stat.err_tx++;
|
||||
skb_queue_head(&bdev->txq, skb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sdio_release_host(bdev->func);
|
||||
|
||||
pm_runtime_mark_last_busy(bdev->dev);
|
||||
pm_runtime_put_autosuspend(bdev->dev);
|
||||
}
|
||||
|
||||
static int btmtksdio_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
|
||||
|
@ -374,8 +325,29 @@ err_out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int btmtksdio_recv_acl(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
|
||||
u16 handle = le16_to_cpu(hci_acl_hdr(skb)->handle);
|
||||
|
||||
switch (handle) {
|
||||
case 0xfc6f:
|
||||
/* Firmware dump from device: when the firmware hangs, the
|
||||
* device can no longer suspend and thus disable auto-suspend.
|
||||
*/
|
||||
pm_runtime_forbid(bdev->dev);
|
||||
fallthrough;
|
||||
case 0x05ff:
|
||||
case 0x05fe:
|
||||
/* Firmware debug logging */
|
||||
return hci_recv_diag(hdev, skb);
|
||||
}
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static const struct h4_recv_pkt mtk_recv_pkts[] = {
|
||||
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||
{ H4_RECV_ACL, .recv = btmtksdio_recv_acl },
|
||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||
{ H4_RECV_EVENT, .recv = btmtksdio_recv_event },
|
||||
};
|
||||
|
@ -477,70 +449,90 @@ err_kfree_skb:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void btmtksdio_interrupt(struct sdio_func *func)
|
||||
static void btmtksdio_txrx_work(struct work_struct *work)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = sdio_get_drvdata(func);
|
||||
u32 int_status;
|
||||
u16 rx_size;
|
||||
|
||||
/* It is required that the host gets ownership from the device before
|
||||
* accessing any register, however, if SDIO host is not being released,
|
||||
* a potential deadlock probably happens in a circular wait between SDIO
|
||||
* IRQ work and PM runtime work. So, we have to explicitly release SDIO
|
||||
* host here and claim again after the PM runtime work is all done.
|
||||
*/
|
||||
sdio_release_host(bdev->func);
|
||||
struct btmtksdio_dev *bdev = container_of(work, struct btmtksdio_dev,
|
||||
txrx_work);
|
||||
unsigned long txrx_timeout;
|
||||
u32 int_status, rx_size;
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
pm_runtime_get_sync(bdev->dev);
|
||||
|
||||
sdio_claim_host(bdev->func);
|
||||
|
||||
/* Disable interrupt */
|
||||
sdio_writel(func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL);
|
||||
sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0);
|
||||
|
||||
int_status = sdio_readl(func, MTK_REG_CHISR, NULL);
|
||||
txrx_timeout = jiffies + 5 * HZ;
|
||||
|
||||
/* Ack an interrupt as soon as possible before any operation on
|
||||
* hardware.
|
||||
*
|
||||
* Note that we don't ack any status during operations to avoid race
|
||||
* condition between the host and the device such as it's possible to
|
||||
* mistakenly ack RX_DONE for the next packet and then cause interrupts
|
||||
* not be raised again but there is still pending data in the hardware
|
||||
* FIFO.
|
||||
*/
|
||||
sdio_writel(func, int_status, MTK_REG_CHISR, NULL);
|
||||
do {
|
||||
int_status = sdio_readl(bdev->func, MTK_REG_CHISR, NULL);
|
||||
|
||||
if (unlikely(!int_status))
|
||||
bt_dev_err(bdev->hdev, "CHISR is 0");
|
||||
/* Ack an interrupt as soon as possible before any operation on
|
||||
* hardware.
|
||||
*
|
||||
* Note that we don't ack any status during operations to avoid race
|
||||
* condition between the host and the device such as it's possible to
|
||||
* mistakenly ack RX_DONE for the next packet and then cause interrupts
|
||||
* not be raised again but there is still pending data in the hardware
|
||||
* FIFO.
|
||||
*/
|
||||
sdio_writel(bdev->func, int_status, MTK_REG_CHISR, NULL);
|
||||
|
||||
if (int_status & FW_OWN_BACK_INT)
|
||||
bt_dev_dbg(bdev->hdev, "Get fw own back");
|
||||
if (int_status & FW_OWN_BACK_INT)
|
||||
bt_dev_dbg(bdev->hdev, "Get fw own back");
|
||||
|
||||
if (int_status & TX_EMPTY)
|
||||
schedule_work(&bdev->tx_work);
|
||||
else if (unlikely(int_status & TX_FIFO_OVERFLOW))
|
||||
bt_dev_warn(bdev->hdev, "Tx fifo overflow");
|
||||
if (int_status & TX_EMPTY)
|
||||
set_bit(BTMTKSDIO_HW_TX_READY, &bdev->tx_state);
|
||||
|
||||
if (int_status & RX_DONE_INT) {
|
||||
rx_size = (int_status & RX_PKT_LEN) >> 16;
|
||||
else if (unlikely(int_status & TX_FIFO_OVERFLOW))
|
||||
bt_dev_warn(bdev->hdev, "Tx fifo overflow");
|
||||
|
||||
if (btmtksdio_rx_packet(bdev, rx_size) < 0)
|
||||
bdev->hdev->stat.err_rx++;
|
||||
}
|
||||
if (test_bit(BTMTKSDIO_HW_TX_READY, &bdev->tx_state)) {
|
||||
skb = skb_dequeue(&bdev->txq);
|
||||
if (skb) {
|
||||
err = btmtksdio_tx_packet(bdev, skb);
|
||||
if (err < 0) {
|
||||
bdev->hdev->stat.err_tx++;
|
||||
skb_queue_head(&bdev->txq, skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (int_status & RX_DONE_INT) {
|
||||
rx_size = sdio_readl(bdev->func, MTK_REG_CRPLR, NULL);
|
||||
rx_size = (rx_size & RX_PKT_LEN) >> 16;
|
||||
if (btmtksdio_rx_packet(bdev, rx_size) < 0)
|
||||
bdev->hdev->stat.err_rx++;
|
||||
}
|
||||
} while (int_status || time_is_before_jiffies(txrx_timeout));
|
||||
|
||||
/* Enable interrupt */
|
||||
sdio_writel(func, C_INT_EN_SET, MTK_REG_CHLPCR, NULL);
|
||||
sdio_writel(bdev->func, C_INT_EN_SET, MTK_REG_CHLPCR, 0);
|
||||
|
||||
sdio_release_host(bdev->func);
|
||||
|
||||
pm_runtime_mark_last_busy(bdev->dev);
|
||||
pm_runtime_put_autosuspend(bdev->dev);
|
||||
}
|
||||
|
||||
static void btmtksdio_interrupt(struct sdio_func *func)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = sdio_get_drvdata(func);
|
||||
|
||||
/* Disable interrupt */
|
||||
sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0);
|
||||
|
||||
schedule_work(&bdev->txrx_work);
|
||||
}
|
||||
|
||||
static int btmtksdio_open(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
|
||||
u32 status, val;
|
||||
int err;
|
||||
u32 status;
|
||||
|
||||
sdio_claim_host(bdev->func);
|
||||
|
||||
|
@ -548,6 +540,8 @@ static int btmtksdio_open(struct hci_dev *hdev)
|
|||
if (err < 0)
|
||||
goto err_release_host;
|
||||
|
||||
set_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state);
|
||||
|
||||
/* Get ownership from the device */
|
||||
sdio_writel(bdev->func, C_FW_OWN_REQ_CLR, MTK_REG_CHLPCR, &err);
|
||||
if (err < 0)
|
||||
|
@ -580,13 +574,22 @@ static int btmtksdio_open(struct hci_dev *hdev)
|
|||
/* SDIO CMD 5 allows the SDIO device back to idle state an
|
||||
* synchronous interrupt is supported in SDIO 4-bit mode
|
||||
*/
|
||||
sdio_writel(bdev->func, SDIO_INT_CTL | SDIO_RE_INIT_EN,
|
||||
MTK_REG_CSDIOCSR, &err);
|
||||
val = sdio_readl(bdev->func, MTK_REG_CSDIOCSR, &err);
|
||||
if (err < 0)
|
||||
goto err_release_irq;
|
||||
|
||||
/* Setup write-1-clear for CHISR register */
|
||||
sdio_writel(bdev->func, C_INT_CLR_CTRL, MTK_REG_CHCR, &err);
|
||||
val |= SDIO_INT_CTL;
|
||||
sdio_writel(bdev->func, val, MTK_REG_CSDIOCSR, &err);
|
||||
if (err < 0)
|
||||
goto err_release_irq;
|
||||
|
||||
/* Explitly set write-1-clear method */
|
||||
val = sdio_readl(bdev->func, MTK_REG_CHCR, &err);
|
||||
if (err < 0)
|
||||
goto err_release_irq;
|
||||
|
||||
val |= C_INT_CLR_CTRL;
|
||||
sdio_writel(bdev->func, val, MTK_REG_CHCR, &err);
|
||||
if (err < 0)
|
||||
goto err_release_irq;
|
||||
|
||||
|
@ -630,6 +633,8 @@ static int btmtksdio_close(struct hci_dev *hdev)
|
|||
|
||||
sdio_release_irq(bdev->func);
|
||||
|
||||
cancel_work_sync(&bdev->txrx_work);
|
||||
|
||||
/* Return ownership to the device */
|
||||
sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, NULL);
|
||||
|
||||
|
@ -638,6 +643,7 @@ static int btmtksdio_close(struct hci_dev *hdev)
|
|||
if (err < 0)
|
||||
bt_dev_err(bdev->hdev, "Cannot return ownership to device");
|
||||
|
||||
clear_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state);
|
||||
sdio_disable_func(bdev->func);
|
||||
|
||||
sdio_release_host(bdev->func);
|
||||
|
@ -651,7 +657,7 @@ static int btmtksdio_flush(struct hci_dev *hdev)
|
|||
|
||||
skb_queue_purge(&bdev->txq);
|
||||
|
||||
cancel_work_sync(&bdev->tx_work);
|
||||
cancel_work_sync(&bdev->txrx_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -663,7 +669,7 @@ static int btmtksdio_func_query(struct hci_dev *hdev)
|
|||
u8 param = 0;
|
||||
|
||||
/* Query whether the function is enabled */
|
||||
wmt_params.op = MTK_WMT_FUNC_CTRL;
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 4;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
|
@ -678,111 +684,16 @@ static int btmtksdio_func_query(struct hci_dev *hdev)
|
|||
return status;
|
||||
}
|
||||
|
||||
static int mtk_setup_firmware(struct hci_dev *hdev, const char *fwname)
|
||||
static int mt76xx_setup(struct hci_dev *hdev, const char *fwname)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
size_t fw_size;
|
||||
int err, dlen;
|
||||
u8 flag, param;
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Power on data RAM the firmware relies on. */
|
||||
param = 1;
|
||||
wmt_params.op = MTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 3;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
err = mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to power on data RAM (%d)", err);
|
||||
goto free_fw;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
fw_size = fw->size;
|
||||
|
||||
/* The size of patch header is 30 bytes, should be skip */
|
||||
if (fw_size < 30) {
|
||||
err = -EINVAL;
|
||||
goto free_fw;
|
||||
}
|
||||
|
||||
fw_size -= 30;
|
||||
fw_ptr += 30;
|
||||
flag = 1;
|
||||
|
||||
wmt_params.op = MTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
while (fw_size > 0) {
|
||||
dlen = min_t(int, 250, fw_size);
|
||||
|
||||
/* Tell device the position in sequence */
|
||||
if (fw_size - dlen <= 0)
|
||||
flag = 3;
|
||||
else if (fw_size < fw->size - 30)
|
||||
flag = 2;
|
||||
|
||||
wmt_params.flag = flag;
|
||||
wmt_params.dlen = dlen;
|
||||
wmt_params.data = fw_ptr;
|
||||
|
||||
err = mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto free_fw;
|
||||
}
|
||||
|
||||
fw_size -= dlen;
|
||||
fw_ptr += dlen;
|
||||
}
|
||||
|
||||
wmt_params.op = MTK_WMT_RST;
|
||||
wmt_params.flag = 4;
|
||||
wmt_params.dlen = 0;
|
||||
wmt_params.data = NULL;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
/* Activate funciton the firmware providing to */
|
||||
err = mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
|
||||
goto free_fw;
|
||||
}
|
||||
|
||||
/* Wait a few moments for firmware activation done */
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
free_fw:
|
||||
release_firmware(fw);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btmtksdio_setup(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
ktime_t calltime, delta, rettime;
|
||||
struct btmtk_tci_sleep tci_sleep;
|
||||
unsigned long long duration;
|
||||
struct sk_buff *skb;
|
||||
int err, status;
|
||||
u8 param = 0x1;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
/* Query whether the firmware is already download */
|
||||
wmt_params.op = MTK_WMT_SEMAPHORE;
|
||||
wmt_params.op = BTMTK_WMT_SEMAPHORE;
|
||||
wmt_params.flag = 1;
|
||||
wmt_params.dlen = 0;
|
||||
wmt_params.data = NULL;
|
||||
|
@ -800,7 +711,7 @@ static int btmtksdio_setup(struct hci_dev *hdev)
|
|||
}
|
||||
|
||||
/* Setup a firmware which the device definitely requires */
|
||||
err = mtk_setup_firmware(hdev, bdev->data->fwname);
|
||||
err = btmtk_setup_firmware(hdev, fwname, mtk_hci_wmt_sync);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -823,7 +734,7 @@ ignore_setup_fw:
|
|||
}
|
||||
|
||||
/* Enable Bluetooth protocol */
|
||||
wmt_params.op = MTK_WMT_FUNC_CTRL;
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 0;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
|
@ -852,6 +763,116 @@ ignore_func_on:
|
|||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt79xx_setup(struct hci_dev *hdev, const char *fwname)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
u8 param = 0x1;
|
||||
int err;
|
||||
|
||||
err = btmtk_setup_firmware_79xx(hdev, fwname, mtk_hci_wmt_sync);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to setup 79xx firmware (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Enable Bluetooth protocol */
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 0;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
err = mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
hci_set_msft_opcode(hdev, 0xFD30);
|
||||
hci_set_aosp_capable(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btsdio_mtk_reg_read(struct hci_dev *hdev, u32 reg, u32 *val)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
struct reg_read_cmd {
|
||||
u8 type;
|
||||
u8 rsv;
|
||||
u8 num;
|
||||
__le32 addr;
|
||||
} __packed reg_read = {
|
||||
.type = 1,
|
||||
.num = 1,
|
||||
};
|
||||
u32 status;
|
||||
int err;
|
||||
|
||||
reg_read.addr = cpu_to_le32(reg);
|
||||
wmt_params.op = BTMTK_WMT_REGISTER;
|
||||
wmt_params.flag = BTMTK_WMT_REG_READ;
|
||||
wmt_params.dlen = sizeof(reg_read);
|
||||
wmt_params.data = ®_read;
|
||||
wmt_params.status = &status;
|
||||
|
||||
err = mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to read reg(%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
*val = status;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btmtksdio_setup(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
|
||||
ktime_t calltime, delta, rettime;
|
||||
unsigned long long duration;
|
||||
char fwname[64];
|
||||
int err, dev_id;
|
||||
u32 fw_version = 0;
|
||||
|
||||
calltime = ktime_get();
|
||||
set_bit(BTMTKSDIO_HW_TX_READY, &bdev->tx_state);
|
||||
|
||||
switch (bdev->data->chipid) {
|
||||
case 0x7921:
|
||||
err = btsdio_mtk_reg_read(hdev, 0x70010200, &dev_id);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to get device id (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = btsdio_mtk_reg_read(hdev, 0x80021004, &fw_version);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to get fw version (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
snprintf(fwname, sizeof(fwname),
|
||||
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
|
||||
dev_id & 0xffff, (fw_version & 0xff) + 1);
|
||||
err = mt79xx_setup(hdev, fwname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case 0x7663:
|
||||
case 0x7668:
|
||||
err = mt76xx_setup(hdev, bdev->data->fwname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long)ktime_to_ns(delta) >> 10;
|
||||
|
@ -891,7 +912,7 @@ static int btmtksdio_shutdown(struct hci_dev *hdev)
|
|||
pm_runtime_get_sync(bdev->dev);
|
||||
|
||||
/* Disable the device */
|
||||
wmt_params.op = MTK_WMT_FUNC_CTRL;
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 0;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
|
@ -932,7 +953,7 @@ static int btmtksdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
skb_queue_tail(&bdev->txq, skb);
|
||||
|
||||
schedule_work(&bdev->tx_work);
|
||||
schedule_work(&bdev->txrx_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -955,7 +976,7 @@ static int btmtksdio_probe(struct sdio_func *func,
|
|||
bdev->dev = &func->dev;
|
||||
bdev->func = func;
|
||||
|
||||
INIT_WORK(&bdev->tx_work, btmtksdio_tx_work);
|
||||
INIT_WORK(&bdev->txrx_work, btmtksdio_txrx_work);
|
||||
skb_queue_head_init(&bdev->txq);
|
||||
|
||||
/* Initialize and register HCI device */
|
||||
|
@ -976,6 +997,8 @@ static int btmtksdio_probe(struct sdio_func *func,
|
|||
hdev->setup = btmtksdio_setup;
|
||||
hdev->shutdown = btmtksdio_shutdown;
|
||||
hdev->send = btmtksdio_send_frame;
|
||||
hdev->set_bdaddr = btmtk_set_bdaddr;
|
||||
|
||||
SET_HCIDEV_DEV(hdev, &func->dev);
|
||||
|
||||
hdev->manufacturer = 70;
|
||||
|
@ -1042,6 +1065,11 @@ static int btmtksdio_runtime_suspend(struct device *dev)
|
|||
if (!bdev)
|
||||
return 0;
|
||||
|
||||
if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state))
|
||||
return 0;
|
||||
|
||||
sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
|
||||
|
||||
sdio_claim_host(bdev->func);
|
||||
|
||||
sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, &err);
|
||||
|
@ -1069,6 +1097,9 @@ static int btmtksdio_runtime_resume(struct device *dev)
|
|||
if (!bdev)
|
||||
return 0;
|
||||
|
||||
if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state))
|
||||
return 0;
|
||||
|
||||
sdio_claim_host(bdev->func);
|
||||
|
||||
sdio_writel(bdev->func, C_FW_OWN_REQ_CLR, MTK_REG_CHLPCR, &err);
|
||||
|
@ -1112,5 +1143,3 @@ MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
|
|||
MODULE_DESCRIPTION("MediaTek Bluetooth SDIO driver ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7663);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7668);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
@ -141,6 +142,50 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int qca_send_patch_config_cmd(struct hci_dev *hdev)
|
||||
{
|
||||
const u8 cmd[] = { EDL_PATCH_CONFIG_CMD, 0x01, 0, 0, 0 };
|
||||
struct sk_buff *skb;
|
||||
struct edl_event_hdr *edl;
|
||||
int err;
|
||||
|
||||
bt_dev_dbg(hdev, "QCA Patch config");
|
||||
|
||||
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, sizeof(cmd),
|
||||
cmd, HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "Sending QCA Patch config failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (skb->len != 2) {
|
||||
bt_dev_err(hdev, "QCA Patch config cmd size mismatch len %d", skb->len);
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
edl = (struct edl_event_hdr *)(skb->data);
|
||||
if (!edl) {
|
||||
bt_dev_err(hdev, "QCA Patch config with no header");
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (edl->cresp != EDL_PATCH_CONFIG_RES_EVT || edl->rtype != EDL_PATCH_CONFIG_CMD) {
|
||||
bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
|
||||
edl->rtype);
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int qca_send_reset(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
@ -551,6 +596,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
|||
*/
|
||||
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
|
||||
|
||||
if (soc_type == QCA_WCN6750)
|
||||
qca_send_patch_config_cmd(hdev);
|
||||
|
||||
/* Download rampatch file */
|
||||
config.type = TLV_TYPE_PATCH;
|
||||
if (qca_is_wcn399x(soc_type)) {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define EDL_PATCH_TLV_REQ_CMD (0x1E)
|
||||
#define EDL_GET_BUILD_INFO_CMD (0x20)
|
||||
#define EDL_NVM_ACCESS_SET_REQ_CMD (0x01)
|
||||
#define EDL_PATCH_CONFIG_CMD (0x28)
|
||||
#define MAX_SIZE_PER_TLV_SEGMENT (243)
|
||||
#define QCA_PRE_SHUTDOWN_CMD (0xFC08)
|
||||
#define QCA_DISABLE_LOGGING (0xFC17)
|
||||
|
@ -24,6 +25,7 @@
|
|||
#define EDL_CMD_EXE_STATUS_EVT (0x00)
|
||||
#define EDL_SET_BAUDRATE_RSP_EVT (0x92)
|
||||
#define EDL_NVM_ACCESS_CODE_EVT (0x0B)
|
||||
#define EDL_PATCH_CONFIG_RES_EVT (0x00)
|
||||
#define QCA_DISABLE_LOGGING_SUB_OP (0x14)
|
||||
|
||||
#define EDL_TAG_ID_HCI (17)
|
||||
|
|
|
@ -295,6 +295,8 @@ static int btsdio_probe(struct sdio_func *func,
|
|||
switch (func->device) {
|
||||
case SDIO_DEVICE_ID_BROADCOM_43341:
|
||||
case SDIO_DEVICE_ID_BROADCOM_43430:
|
||||
case SDIO_DEVICE_ID_BROADCOM_4345:
|
||||
case SDIO_DEVICE_ID_BROADCOM_43455:
|
||||
case SDIO_DEVICE_ID_BROADCOM_4356:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
@ -24,13 +25,13 @@
|
|||
#include "btintel.h"
|
||||
#include "btbcm.h"
|
||||
#include "btrtl.h"
|
||||
#include "btmtk.h"
|
||||
|
||||
#define VERSION "0.8"
|
||||
|
||||
static bool disable_scofix;
|
||||
static bool force_scofix;
|
||||
static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
|
||||
|
||||
static bool reset = true;
|
||||
|
||||
static struct usb_driver btusb_driver;
|
||||
|
@ -59,6 +60,7 @@ static struct usb_driver btusb_driver;
|
|||
#define BTUSB_WIDEBAND_SPEECH 0x400000
|
||||
#define BTUSB_VALID_LE_STATES 0x800000
|
||||
#define BTUSB_QCA_WCN6855 0x1000000
|
||||
#define BTUSB_INTEL_BROKEN_SHUTDOWN_LED 0x2000000
|
||||
#define BTUSB_INTEL_BROKEN_INITIAL_NCMD 0x4000000
|
||||
|
||||
static const struct usb_device_id btusb_table[] = {
|
||||
|
@ -295,6 +297,24 @@ static const struct usb_device_id blacklist_table[] = {
|
|||
{ USB_DEVICE(0x0cf3, 0xe600), .driver_info = BTUSB_QCA_WCN6855 |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0cc), .driver_info = BTUSB_QCA_WCN6855 |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0d6), .driver_info = BTUSB_QCA_WCN6855 |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0e3), .driver_info = BTUSB_QCA_WCN6855 |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x10ab, 0x9309), .driver_info = BTUSB_QCA_WCN6855 |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x10ab, 0x9409), .driver_info = BTUSB_QCA_WCN6855 |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0d0), .driver_info = BTUSB_QCA_WCN6855 |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
|
||||
/* Broadcom BCM2035 */
|
||||
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
|
||||
|
@ -365,10 +385,13 @@ static const struct usb_device_id blacklist_table[] = {
|
|||
{ USB_DEVICE(0x8087, 0x0033), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
|
||||
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL_COMBINED |
|
||||
BTUSB_INTEL_BROKEN_INITIAL_NCMD },
|
||||
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
BTUSB_INTEL_BROKEN_INITIAL_NCMD |
|
||||
BTUSB_INTEL_BROKEN_SHUTDOWN_LED },
|
||||
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL_COMBINED |
|
||||
BTUSB_INTEL_BROKEN_SHUTDOWN_LED },
|
||||
{ USB_DEVICE(0x8087, 0x0a2b), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x0aa7), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
{ USB_DEVICE(0x8087, 0x0aa7), .driver_info = BTUSB_INTEL_COMBINED |
|
||||
BTUSB_INTEL_BROKEN_SHUTDOWN_LED },
|
||||
{ USB_DEVICE(0x8087, 0x0aaa), .driver_info = BTUSB_INTEL_COMBINED },
|
||||
|
||||
/* Other Intel Bluetooth devices */
|
||||
|
@ -384,6 +407,8 @@ static const struct usb_device_id blacklist_table[] = {
|
|||
/* Realtek 8852AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0bda, 0xc852), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0bda, 0x385a), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0bda, 0x4852), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04c5, 0x165c), .driver_info = BTUSB_REALTEK |
|
||||
|
@ -423,6 +448,14 @@ static const struct usb_device_id blacklist_table[] = {
|
|||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
|
||||
/* MediaTek MT7922A Bluetooth devices */
|
||||
{ USB_DEVICE(0x0489, 0xe0d8), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0d9), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
|
||||
/* Additional Realtek 8723AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK },
|
||||
|
@ -549,8 +582,13 @@ struct btusb_data {
|
|||
|
||||
unsigned long flags;
|
||||
|
||||
struct work_struct work;
|
||||
struct work_struct waker;
|
||||
bool poll_sync;
|
||||
int intr_interval;
|
||||
struct work_struct work;
|
||||
struct work_struct waker;
|
||||
struct delayed_work rx_work;
|
||||
|
||||
struct sk_buff_head acl_q;
|
||||
|
||||
struct usb_anchor deferred;
|
||||
struct usb_anchor tx_anchor;
|
||||
|
@ -715,6 +753,16 @@ static inline void btusb_free_frags(struct btusb_data *data)
|
|||
spin_unlock_irqrestore(&data->rxlock, flags);
|
||||
}
|
||||
|
||||
static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
|
||||
{
|
||||
if (data->intr_interval) {
|
||||
/* Trigger dequeue immediatelly if an event is received */
|
||||
schedule_delayed_work(&data->rx_work, 0);
|
||||
}
|
||||
|
||||
return data->recv_event(data->hdev, skb);
|
||||
}
|
||||
|
||||
static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
@ -760,7 +808,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
|||
|
||||
if (!hci_skb_expect(skb)) {
|
||||
/* Complete frame */
|
||||
data->recv_event(data->hdev, skb);
|
||||
btusb_recv_event(data, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -771,6 +819,20 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb)
|
||||
{
|
||||
/* Only queue ACL packet if intr_interval is set as it means
|
||||
* force_poll_sync has been enabled.
|
||||
*/
|
||||
if (!data->intr_interval)
|
||||
return data->recv_acl(data->hdev, skb);
|
||||
|
||||
skb_queue_tail(&data->acl_q, skb);
|
||||
schedule_delayed_work(&data->rx_work, data->intr_interval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
@ -818,7 +880,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
|||
|
||||
if (!hci_skb_expect(skb)) {
|
||||
/* Complete frame */
|
||||
data->recv_acl(data->hdev, skb);
|
||||
btusb_recv_acl(data, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -924,6 +986,8 @@ static void btusb_intr_complete(struct urb *urb)
|
|||
if (err != -EPERM && err != -ENODEV)
|
||||
bt_dev_err(hdev, "urb %p failed to resubmit (%d)",
|
||||
urb, -err);
|
||||
if (err != -EPERM)
|
||||
hci_cmd_sync_cancel(hdev, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
}
|
||||
|
@ -967,9 +1031,33 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
if (err != -EPERM && err != -ENODEV)
|
||||
bt_dev_err(hdev, "urb %p submission failed (%d)",
|
||||
urb, -err);
|
||||
if (err != -EPERM)
|
||||
hci_cmd_sync_cancel(hdev, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
||||
/* Only initialize intr_interval if URB poll sync is enabled */
|
||||
if (!data->poll_sync)
|
||||
goto done;
|
||||
|
||||
/* The units are frames (milliseconds) for full and low speed devices,
|
||||
* and microframes (1/8 millisecond) for highspeed and SuperSpeed
|
||||
* devices.
|
||||
*
|
||||
* This is done once on open/resume so it shouldn't change even if
|
||||
* force_poll_sync changes.
|
||||
*/
|
||||
switch (urb->dev->speed) {
|
||||
case USB_SPEED_SUPER_PLUS:
|
||||
case USB_SPEED_SUPER: /* units are 125us */
|
||||
data->intr_interval = usecs_to_jiffies(urb->interval * 125);
|
||||
break;
|
||||
default:
|
||||
data->intr_interval = msecs_to_jiffies(urb->interval);
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
usb_free_urb(urb);
|
||||
|
||||
return err;
|
||||
|
@ -1322,10 +1410,13 @@ static void btusb_tx_complete(struct urb *urb)
|
|||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||
goto done;
|
||||
|
||||
if (!urb->status)
|
||||
if (!urb->status) {
|
||||
hdev->stat.byte_tx += urb->transfer_buffer_length;
|
||||
else
|
||||
} else {
|
||||
if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT)
|
||||
hci_cmd_sync_cancel(hdev, -urb->status);
|
||||
hdev->stat.err_tx++;
|
||||
}
|
||||
|
||||
done:
|
||||
spin_lock_irqsave(&data->txlock, flags);
|
||||
|
@ -1429,9 +1520,12 @@ static int btusb_close(struct hci_dev *hdev)
|
|||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
cancel_delayed_work(&data->rx_work);
|
||||
cancel_work_sync(&data->work);
|
||||
cancel_work_sync(&data->waker);
|
||||
|
||||
skb_queue_purge(&data->acl_q);
|
||||
|
||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
||||
|
@ -1463,6 +1557,10 @@ static int btusb_flush(struct hci_dev *hdev)
|
|||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
cancel_delayed_work(&data->rx_work);
|
||||
|
||||
skb_queue_purge(&data->acl_q);
|
||||
|
||||
usb_kill_anchored_urbs(&data->tx_anchor);
|
||||
btusb_free_frags(data);
|
||||
|
||||
|
@ -1826,6 +1924,17 @@ static void btusb_waker(struct work_struct *work)
|
|||
usb_autopm_put_interface(data->intf);
|
||||
}
|
||||
|
||||
static void btusb_rx_work(struct work_struct *work)
|
||||
{
|
||||
struct btusb_data *data = container_of(work, struct btusb_data,
|
||||
rx_work.work);
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Dequeue ACL data received during the interval */
|
||||
while ((skb = skb_dequeue(&data->acl_q)))
|
||||
data->recv_acl(data->hdev, skb);
|
||||
}
|
||||
|
||||
static int btusb_setup_bcm92035(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
@ -2131,122 +2240,6 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
#define MTK_BT_RST_DONE 0x00000100
|
||||
#define MTK_BT_RESET_WAIT_MS 100
|
||||
#define MTK_BT_RESET_NUM_TRIES 10
|
||||
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
|
||||
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
|
||||
|
||||
#define HCI_WMT_MAX_EVENT_SIZE 64
|
||||
/* It is for mt79xx download rom patch*/
|
||||
#define MTK_FW_ROM_PATCH_HEADER_SIZE 32
|
||||
#define MTK_FW_ROM_PATCH_GD_SIZE 64
|
||||
#define MTK_FW_ROM_PATCH_SEC_MAP_SIZE 64
|
||||
#define MTK_SEC_MAP_COMMON_SIZE 12
|
||||
#define MTK_SEC_MAP_NEED_SEND_SIZE 52
|
||||
|
||||
enum {
|
||||
BTMTK_WMT_PATCH_DWNLD = 0x1,
|
||||
BTMTK_WMT_FUNC_CTRL = 0x6,
|
||||
BTMTK_WMT_RST = 0x7,
|
||||
BTMTK_WMT_SEMAPHORE = 0x17,
|
||||
};
|
||||
|
||||
enum {
|
||||
BTMTK_WMT_INVALID,
|
||||
BTMTK_WMT_PATCH_UNDONE,
|
||||
BTMTK_WMT_PATCH_PROGRESS,
|
||||
BTMTK_WMT_PATCH_DONE,
|
||||
BTMTK_WMT_ON_UNDONE,
|
||||
BTMTK_WMT_ON_DONE,
|
||||
BTMTK_WMT_ON_PROGRESS,
|
||||
};
|
||||
|
||||
struct btmtk_wmt_hdr {
|
||||
u8 dir;
|
||||
u8 op;
|
||||
__le16 dlen;
|
||||
u8 flag;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_cmd {
|
||||
struct btmtk_wmt_hdr hdr;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt {
|
||||
struct hci_event_hdr hhdr;
|
||||
struct btmtk_wmt_hdr whdr;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt_funcc {
|
||||
struct btmtk_hci_wmt_evt hwhdr;
|
||||
__be16 status;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_tci_sleep {
|
||||
u8 mode;
|
||||
__le16 duration;
|
||||
__le16 host_duration;
|
||||
u8 host_wakeup_pin;
|
||||
u8 time_compensation;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_params {
|
||||
u8 op;
|
||||
u8 flag;
|
||||
u16 dlen;
|
||||
const void *data;
|
||||
u32 *status;
|
||||
};
|
||||
|
||||
struct btmtk_patch_header {
|
||||
u8 datetime[16];
|
||||
u8 platform[4];
|
||||
__le16 hwver;
|
||||
__le16 swver;
|
||||
__le32 magicnum;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_global_desc {
|
||||
__le32 patch_ver;
|
||||
__le32 sub_sys;
|
||||
__le32 feature_opt;
|
||||
__le32 section_num;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_section_map {
|
||||
__le32 sectype;
|
||||
__le32 secoffset;
|
||||
__le32 secsize;
|
||||
union {
|
||||
__le32 u4SecSpec[13];
|
||||
struct {
|
||||
__le32 dlAddr;
|
||||
__le32 dlsize;
|
||||
__le32 seckeyidx;
|
||||
__le32 alignlen;
|
||||
__le32 sectype;
|
||||
__le32 dlmodecrctype;
|
||||
__le32 crc;
|
||||
__le32 reserved[6];
|
||||
} bin_info_spec;
|
||||
};
|
||||
} __packed;
|
||||
|
||||
static int btusb_set_bdaddr_mtk(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
long ret;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc1a, sizeof(bdaddr), bdaddr, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "changing Mediatek device address failed (%ld)",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btusb_mtk_wmt_recv(struct urb *urb)
|
||||
{
|
||||
|
@ -2265,6 +2258,7 @@ static void btusb_mtk_wmt_recv(struct urb *urb)
|
|||
skb = bt_skb_alloc(HCI_WMT_MAX_EVENT_SIZE, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
hdev->stat.err_rx++;
|
||||
kfree(urb->setup_packet);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2285,6 +2279,7 @@ static void btusb_mtk_wmt_recv(struct urb *urb)
|
|||
data->evt_skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!data->evt_skb) {
|
||||
kfree_skb(skb);
|
||||
kfree(urb->setup_packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2293,6 +2288,7 @@ static void btusb_mtk_wmt_recv(struct urb *urb)
|
|||
if (err < 0) {
|
||||
kfree_skb(data->evt_skb);
|
||||
data->evt_skb = NULL;
|
||||
kfree(urb->setup_packet);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2303,6 +2299,7 @@ static void btusb_mtk_wmt_recv(struct urb *urb)
|
|||
wake_up_bit(&data->flags,
|
||||
BTUSB_TX_WAIT_VND_EVT);
|
||||
}
|
||||
kfree(urb->setup_packet);
|
||||
return;
|
||||
} else if (urb->status == -ENOENT) {
|
||||
/* Avoid suspend failed when usb_kill_urb */
|
||||
|
@ -2323,6 +2320,7 @@ static void btusb_mtk_wmt_recv(struct urb *urb)
|
|||
usb_anchor_urb(urb, &data->ctrl_anchor);
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
kfree(urb->setup_packet);
|
||||
/* -EPERM: urb is being killed;
|
||||
* -ENODEV: device got disconnected
|
||||
*/
|
||||
|
@ -2497,209 +2495,6 @@ err_free_wc:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
struct btmtk_global_desc *globaldesc = NULL;
|
||||
struct btmtk_section_map *sectionmap;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
const u8 *fw_bin_ptr;
|
||||
int err, dlen, i, status;
|
||||
u8 flag, first_block, retry;
|
||||
u32 section_num, dl_size, section_offset;
|
||||
u8 cmd[64];
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
fw_bin_ptr = fw_ptr;
|
||||
globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
|
||||
section_num = le32_to_cpu(globaldesc->section_num);
|
||||
|
||||
for (i = 0; i < section_num; i++) {
|
||||
first_block = 1;
|
||||
fw_ptr = fw_bin_ptr;
|
||||
sectionmap = (struct btmtk_section_map *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
|
||||
MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
|
||||
|
||||
section_offset = le32_to_cpu(sectionmap->secoffset);
|
||||
dl_size = le32_to_cpu(sectionmap->bin_info_spec.dlsize);
|
||||
|
||||
if (dl_size > 0) {
|
||||
retry = 20;
|
||||
while (retry > 0) {
|
||||
cmd[0] = 0; /* 0 means legacy dl mode. */
|
||||
memcpy(cmd + 1,
|
||||
fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
|
||||
MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i +
|
||||
MTK_SEC_MAP_COMMON_SIZE,
|
||||
MTK_SEC_MAP_NEED_SEND_SIZE + 1);
|
||||
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = &status;
|
||||
wmt_params.flag = 0;
|
||||
wmt_params.dlen = MTK_SEC_MAP_NEED_SEND_SIZE + 1;
|
||||
wmt_params.data = &cmd;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
if (status == BTMTK_WMT_PATCH_UNDONE) {
|
||||
break;
|
||||
} else if (status == BTMTK_WMT_PATCH_PROGRESS) {
|
||||
msleep(100);
|
||||
retry--;
|
||||
} else if (status == BTMTK_WMT_PATCH_DONE) {
|
||||
goto next_section;
|
||||
} else {
|
||||
bt_dev_err(hdev, "Failed wmt patch dwnld status (%d)",
|
||||
status);
|
||||
goto err_release_fw;
|
||||
}
|
||||
}
|
||||
|
||||
fw_ptr += section_offset;
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
while (dl_size > 0) {
|
||||
dlen = min_t(int, 250, dl_size);
|
||||
if (first_block == 1) {
|
||||
flag = 1;
|
||||
first_block = 0;
|
||||
} else if (dl_size - dlen <= 0) {
|
||||
flag = 3;
|
||||
} else {
|
||||
flag = 2;
|
||||
}
|
||||
|
||||
wmt_params.flag = flag;
|
||||
wmt_params.dlen = dlen;
|
||||
wmt_params.data = fw_ptr;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
dl_size -= dlen;
|
||||
fw_ptr += dlen;
|
||||
}
|
||||
}
|
||||
next_section:
|
||||
continue;
|
||||
}
|
||||
/* Wait a few moments for firmware activation done */
|
||||
usleep_range(100000, 120000);
|
||||
|
||||
err_release_fw:
|
||||
release_firmware(fw);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_setup_firmware(struct hci_dev *hdev, const char *fwname)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
size_t fw_size;
|
||||
int err, dlen;
|
||||
u8 flag, param;
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Power on data RAM the firmware relies on. */
|
||||
param = 1;
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 3;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to power on data RAM (%d)", err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
fw_size = fw->size;
|
||||
|
||||
/* The size of patch header is 30 bytes, should be skip */
|
||||
if (fw_size < 30) {
|
||||
err = -EINVAL;
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
fw_size -= 30;
|
||||
fw_ptr += 30;
|
||||
flag = 1;
|
||||
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
while (fw_size > 0) {
|
||||
dlen = min_t(int, 250, fw_size);
|
||||
|
||||
/* Tell device the position in sequence */
|
||||
if (fw_size - dlen <= 0)
|
||||
flag = 3;
|
||||
else if (fw_size < fw->size - 30)
|
||||
flag = 2;
|
||||
|
||||
wmt_params.flag = flag;
|
||||
wmt_params.dlen = dlen;
|
||||
wmt_params.data = fw_ptr;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
fw_size -= dlen;
|
||||
fw_ptr += dlen;
|
||||
}
|
||||
|
||||
wmt_params.op = BTMTK_WMT_RST;
|
||||
wmt_params.flag = 4;
|
||||
wmt_params.dlen = 0;
|
||||
wmt_params.data = NULL;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
/* Activate funciton the firmware providing to */
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
/* Wait a few moments for firmware activation done */
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
err_release_fw:
|
||||
release_firmware(fw);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_func_query(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
|
@ -2857,7 +2652,12 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
|||
snprintf(fw_bin_name, sizeof(fw_bin_name),
|
||||
"mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
|
||||
dev_id & 0xffff, (fw_version & 0xff) + 1);
|
||||
err = btusb_mtk_setup_firmware_79xx(hdev, fw_bin_name);
|
||||
err = btmtk_setup_firmware_79xx(hdev, fw_bin_name,
|
||||
btusb_mtk_hci_wmt_sync);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to set up firmware (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* It's Device EndPoint Reset Option Register */
|
||||
btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
|
||||
|
@ -2877,6 +2677,7 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
|||
}
|
||||
|
||||
hci_set_msft_opcode(hdev, 0xFD30);
|
||||
hci_set_aosp_capable(hdev);
|
||||
goto done;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
|
||||
|
@ -2903,7 +2704,8 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
|
|||
}
|
||||
|
||||
/* Setup a firmware which the device definitely requires */
|
||||
err = btusb_mtk_setup_firmware(hdev, fwname);
|
||||
err = btmtk_setup_firmware(hdev, fwname,
|
||||
btusb_mtk_hci_wmt_sync);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -3064,9 +2866,6 @@ static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7663);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7668);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* Configure an out-of-band gpio as wake-up pin, if specified in device tree */
|
||||
static int marvell_config_oob_wake(struct hci_dev *hdev)
|
||||
|
@ -3190,11 +2989,15 @@ static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev,
|
|||
#define QCA_DFU_TIMEOUT 3000
|
||||
#define QCA_FLAG_MULTI_NVM 0x80
|
||||
|
||||
#define WCN6855_2_0_RAM_VERSION_GF 0x400c1200
|
||||
#define WCN6855_2_1_RAM_VERSION_GF 0x400c1211
|
||||
|
||||
struct qca_version {
|
||||
__le32 rom_version;
|
||||
__le32 patch_version;
|
||||
__le32 ram_version;
|
||||
__le16 board_id;
|
||||
__u8 chip_id;
|
||||
__u8 platform_id;
|
||||
__le16 flag;
|
||||
__u8 reserved[4];
|
||||
} __packed;
|
||||
|
@ -3221,6 +3024,7 @@ static const struct qca_device_info qca_devices_table[] = {
|
|||
{ 0x00000302, 28, 4, 16 }, /* Rome 3.2 */
|
||||
{ 0x00130100, 40, 4, 16 }, /* WCN6855 1.0 */
|
||||
{ 0x00130200, 40, 4, 16 }, /* WCN6855 2.0 */
|
||||
{ 0x00130201, 40, 4, 16 }, /* WCN6855 2.1 */
|
||||
};
|
||||
|
||||
static int btusb_qca_send_vendor_req(struct usb_device *udev, u8 request,
|
||||
|
@ -3375,6 +3179,50 @@ done:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void btusb_generate_qca_nvm_name(char *fwname, size_t max_size,
|
||||
const struct qca_version *ver)
|
||||
{
|
||||
u32 rom_version = le32_to_cpu(ver->rom_version);
|
||||
u16 flag = le16_to_cpu(ver->flag);
|
||||
|
||||
if (((flag >> 8) & 0xff) == QCA_FLAG_MULTI_NVM) {
|
||||
/* The board_id should be split into two bytes
|
||||
* The 1st byte is chip ID, and the 2nd byte is platform ID
|
||||
* For example, board ID 0x010A, 0x01 is platform ID. 0x0A is chip ID
|
||||
* we have several platforms, and platform IDs are continuously added
|
||||
* Platform ID:
|
||||
* 0x00 is for Mobile
|
||||
* 0x01 is for X86
|
||||
* 0x02 is for Automotive
|
||||
* 0x03 is for Consumer electronic
|
||||
*/
|
||||
u16 board_id = (ver->chip_id << 8) + ver->platform_id;
|
||||
const char *variant;
|
||||
|
||||
switch (le32_to_cpu(ver->ram_version)) {
|
||||
case WCN6855_2_0_RAM_VERSION_GF:
|
||||
case WCN6855_2_1_RAM_VERSION_GF:
|
||||
variant = "_gf";
|
||||
break;
|
||||
default:
|
||||
variant = "";
|
||||
break;
|
||||
}
|
||||
|
||||
if (board_id == 0) {
|
||||
snprintf(fwname, max_size, "qca/nvm_usb_%08x%s.bin",
|
||||
rom_version, variant);
|
||||
} else {
|
||||
snprintf(fwname, max_size, "qca/nvm_usb_%08x%s_%04x.bin",
|
||||
rom_version, variant, board_id);
|
||||
}
|
||||
} else {
|
||||
snprintf(fwname, max_size, "qca/nvm_usb_%08x.bin",
|
||||
rom_version);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int btusb_setup_qca_load_nvm(struct hci_dev *hdev,
|
||||
struct qca_version *ver,
|
||||
const struct qca_device_info *info)
|
||||
|
@ -3383,20 +3231,7 @@ static int btusb_setup_qca_load_nvm(struct hci_dev *hdev,
|
|||
char fwname[64];
|
||||
int err;
|
||||
|
||||
if (((ver->flag >> 8) & 0xff) == QCA_FLAG_MULTI_NVM) {
|
||||
/* if boardid equal 0, use default nvm without surfix */
|
||||
if (le16_to_cpu(ver->board_id) == 0x0) {
|
||||
snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x.bin",
|
||||
le32_to_cpu(ver->rom_version));
|
||||
} else {
|
||||
snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x_%04x.bin",
|
||||
le32_to_cpu(ver->rom_version),
|
||||
le16_to_cpu(ver->board_id));
|
||||
}
|
||||
} else {
|
||||
snprintf(fwname, sizeof(fwname), "qca/nvm_usb_%08x.bin",
|
||||
le32_to_cpu(ver->rom_version));
|
||||
}
|
||||
btusb_generate_qca_nvm_name(fwname, sizeof(fwname), ver);
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err) {
|
||||
|
@ -3661,6 +3496,49 @@ static int btusb_shutdown_qca(struct hci_dev *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t force_poll_sync_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btusb_data *data = file->private_data;
|
||||
char buf[3];
|
||||
|
||||
buf[0] = data->poll_sync ? 'Y' : 'N';
|
||||
buf[1] = '\n';
|
||||
buf[2] = '\0';
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
||||
}
|
||||
|
||||
static ssize_t force_poll_sync_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct btusb_data *data = file->private_data;
|
||||
bool enable;
|
||||
int err;
|
||||
|
||||
err = kstrtobool_from_user(user_buf, count, &enable);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Only allow changes while the adapter is down */
|
||||
if (test_bit(HCI_UP, &data->hdev->flags))
|
||||
return -EPERM;
|
||||
|
||||
if (data->poll_sync == enable)
|
||||
return -EALREADY;
|
||||
|
||||
data->poll_sync = enable;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations force_poll_sync_fops = {
|
||||
.open = simple_open,
|
||||
.read = force_poll_sync_read,
|
||||
.write = force_poll_sync_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int btusb_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
|
@ -3744,6 +3622,10 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
|
||||
INIT_WORK(&data->work, btusb_work);
|
||||
INIT_WORK(&data->waker, btusb_waker);
|
||||
INIT_DELAYED_WORK(&data->rx_work, btusb_rx_work);
|
||||
|
||||
skb_queue_head_init(&data->acl_q);
|
||||
|
||||
init_usb_anchor(&data->deferred);
|
||||
init_usb_anchor(&data->tx_anchor);
|
||||
spin_lock_init(&data->txlock);
|
||||
|
@ -3857,6 +3739,9 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
|
||||
if (id->driver_info & BTUSB_INTEL_BROKEN_INITIAL_NCMD)
|
||||
btintel_set_flag(hdev, INTEL_BROKEN_INITIAL_NCMD);
|
||||
|
||||
if (id->driver_info & BTUSB_INTEL_BROKEN_SHUTDOWN_LED)
|
||||
btintel_set_flag(hdev, INTEL_BROKEN_SHUTDOWN_LED);
|
||||
}
|
||||
|
||||
if (id->driver_info & BTUSB_MARVELL)
|
||||
|
@ -3868,7 +3753,7 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
hdev->shutdown = btusb_mtk_shutdown;
|
||||
hdev->manufacturer = 70;
|
||||
hdev->cmd_timeout = btusb_mtk_cmd_timeout;
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_mtk;
|
||||
hdev->set_bdaddr = btmtk_set_bdaddr;
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
data->recv_acl = btusb_recv_acl_mtk;
|
||||
}
|
||||
|
@ -4009,6 +3894,9 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
|
||||
usb_set_intfdata(intf, data);
|
||||
|
||||
debugfs_create_file("force_poll_sync", 0644, hdev->debugfs, data,
|
||||
&force_poll_sync_fops);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_dev:
|
||||
|
|
|
@ -1188,7 +1188,12 @@ static int bcm_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
dev->dev = &pdev->dev;
|
||||
dev->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev->irq = ret;
|
||||
|
||||
/* Initialize routing field to an unused value */
|
||||
dev->pcm_int_params[0] = 0xff;
|
||||
|
@ -1508,7 +1513,6 @@ static const struct of_device_id bcm_bluetooth_of_match[] = {
|
|||
{ .compatible = "brcm,bcm4330-bt" },
|
||||
{ .compatible = "brcm,bcm4334-bt" },
|
||||
{ .compatible = "brcm,bcm4345c5" },
|
||||
{ .compatible = "brcm,bcm4330-bt" },
|
||||
{ .compatible = "brcm,bcm43438-bt", .data = &bcm43438_device_data },
|
||||
{ .compatible = "brcm,bcm43540-bt", .data = &bcm4354_device_data },
|
||||
{ .compatible = "brcm,bcm4335a0" },
|
||||
|
|
|
@ -252,7 +252,7 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
|
|||
}
|
||||
|
||||
if (!dlen) {
|
||||
hu->padding = (skb->len - 1) % alignment;
|
||||
hu->padding = (skb->len + 1) % alignment;
|
||||
hu->padding = (alignment - hu->padding) % alignment;
|
||||
|
||||
/* No more data, complete frame */
|
||||
|
@ -260,7 +260,7 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
|
|||
skb = NULL;
|
||||
}
|
||||
} else {
|
||||
hu->padding = (skb->len - 1) % alignment;
|
||||
hu->padding = (skb->len + 1) % alignment;
|
||||
hu->padding = (alignment - hu->padding) % alignment;
|
||||
|
||||
/* Complete frame */
|
||||
|
|
|
@ -1928,6 +1928,9 @@ static int qca_power_off(struct hci_dev *hdev)
|
|||
hu->hdev->hw_error = NULL;
|
||||
hu->hdev->cmd_timeout = NULL;
|
||||
|
||||
del_timer_sync(&qca->wake_retrans_timer);
|
||||
del_timer_sync(&qca->tx_idle_timer);
|
||||
|
||||
/* Stop sending shutdown command if soc crashes. */
|
||||
if (soc_type != QCA_ROME
|
||||
&& qca->memdump_state == QCA_MEMDUMP_IDLE) {
|
||||
|
@ -2056,14 +2059,14 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
|||
|
||||
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (!qcadev->bt_en && data->soc_type == QCA_WCN6750) {
|
||||
if (IS_ERR_OR_NULL(qcadev->bt_en) && data->soc_type == QCA_WCN6750) {
|
||||
dev_err(&serdev->dev, "failed to acquire BT_EN gpio\n");
|
||||
power_ctrl_enabled = false;
|
||||
}
|
||||
|
||||
qcadev->sw_ctrl = devm_gpiod_get_optional(&serdev->dev, "swctrl",
|
||||
GPIOD_IN);
|
||||
if (!qcadev->sw_ctrl && data->soc_type == QCA_WCN6750)
|
||||
if (IS_ERR_OR_NULL(qcadev->sw_ctrl) && data->soc_type == QCA_WCN6750)
|
||||
dev_warn(&serdev->dev, "failed to acquire SW_CTRL gpio\n");
|
||||
|
||||
qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL);
|
||||
|
@ -2085,7 +2088,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
|||
|
||||
qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (!qcadev->bt_en) {
|
||||
if (IS_ERR_OR_NULL(qcadev->bt_en)) {
|
||||
dev_warn(&serdev->dev, "failed to acquire enable gpio\n");
|
||||
power_ctrl_enabled = false;
|
||||
}
|
||||
|
|
|
@ -38,9 +38,12 @@ struct vhci_data {
|
|||
|
||||
struct mutex open_mutex;
|
||||
struct delayed_work open_timeout;
|
||||
struct work_struct suspend_work;
|
||||
|
||||
bool suspended;
|
||||
bool wakeup;
|
||||
__u16 msft_opcode;
|
||||
bool aosp_capable;
|
||||
};
|
||||
|
||||
static int vhci_open_dev(struct hci_dev *hdev)
|
||||
|
@ -114,6 +117,17 @@ static ssize_t force_suspend_read(struct file *file, char __user *user_buf,
|
|||
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
||||
}
|
||||
|
||||
static void vhci_suspend_work(struct work_struct *work)
|
||||
{
|
||||
struct vhci_data *data = container_of(work, struct vhci_data,
|
||||
suspend_work);
|
||||
|
||||
if (data->suspended)
|
||||
hci_suspend_dev(data->hdev);
|
||||
else
|
||||
hci_resume_dev(data->hdev);
|
||||
}
|
||||
|
||||
static ssize_t force_suspend_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
|
@ -129,16 +143,10 @@ static ssize_t force_suspend_write(struct file *file,
|
|||
if (data->suspended == enable)
|
||||
return -EALREADY;
|
||||
|
||||
if (enable)
|
||||
err = hci_suspend_dev(data->hdev);
|
||||
else
|
||||
err = hci_resume_dev(data->hdev);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->suspended = enable;
|
||||
|
||||
schedule_work(&data->suspend_work);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -176,6 +184,8 @@ static ssize_t force_wakeup_write(struct file *file,
|
|||
if (data->wakeup == enable)
|
||||
return -EALREADY;
|
||||
|
||||
data->wakeup = enable;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -186,6 +196,88 @@ static const struct file_operations force_wakeup_fops = {
|
|||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int msft_opcode_set(void *data, u64 val)
|
||||
{
|
||||
struct vhci_data *vhci = data;
|
||||
|
||||
if (val > 0xffff || hci_opcode_ogf(val) != 0x3f)
|
||||
return -EINVAL;
|
||||
|
||||
if (vhci->msft_opcode)
|
||||
return -EALREADY;
|
||||
|
||||
vhci->msft_opcode = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msft_opcode_get(void *data, u64 *val)
|
||||
{
|
||||
struct vhci_data *vhci = data;
|
||||
|
||||
*val = vhci->msft_opcode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(msft_opcode_fops, msft_opcode_get, msft_opcode_set,
|
||||
"%llu\n");
|
||||
|
||||
static ssize_t aosp_capable_read(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct vhci_data *vhci = file->private_data;
|
||||
char buf[3];
|
||||
|
||||
buf[0] = vhci->aosp_capable ? 'Y' : 'N';
|
||||
buf[1] = '\n';
|
||||
buf[2] = '\0';
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
||||
}
|
||||
|
||||
static ssize_t aosp_capable_write(struct file *file,
|
||||
const char __user *user_buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct vhci_data *vhci = file->private_data;
|
||||
bool enable;
|
||||
int err;
|
||||
|
||||
err = kstrtobool_from_user(user_buf, count, &enable);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!enable)
|
||||
return -EINVAL;
|
||||
|
||||
if (vhci->aosp_capable)
|
||||
return -EALREADY;
|
||||
|
||||
vhci->aosp_capable = enable;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations aosp_capable_fops = {
|
||||
.open = simple_open,
|
||||
.read = aosp_capable_read,
|
||||
.write = aosp_capable_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int vhci_setup(struct hci_dev *hdev)
|
||||
{
|
||||
struct vhci_data *vhci = hci_get_drvdata(hdev);
|
||||
|
||||
if (vhci->msft_opcode)
|
||||
hci_set_msft_opcode(hdev, vhci->msft_opcode);
|
||||
|
||||
if (vhci->aosp_capable)
|
||||
hci_set_aosp_capable(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
|
@ -228,6 +320,8 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
|
|||
hdev->get_data_path_id = vhci_get_data_path_id;
|
||||
hdev->get_codec_config_data = vhci_get_codec_config_data;
|
||||
hdev->wakeup = vhci_wakeup;
|
||||
hdev->setup = vhci_setup;
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
|
||||
/* bit 6 is for external configuration */
|
||||
if (opcode & 0x40)
|
||||
|
@ -237,6 +331,8 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
|
|||
if (opcode & 0x80)
|
||||
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
||||
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
|
||||
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
BT_ERR("Can't register HCI device");
|
||||
hci_free_dev(hdev);
|
||||
|
@ -251,6 +347,14 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
|
|||
debugfs_create_file("force_wakeup", 0644, hdev->debugfs, data,
|
||||
&force_wakeup_fops);
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_MSFTEXT))
|
||||
debugfs_create_file("msft_opcode", 0644, hdev->debugfs, data,
|
||||
&msft_opcode_fops);
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_AOSPEXT))
|
||||
debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data,
|
||||
&aosp_capable_fops);
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
|
||||
|
||||
skb_put_u8(skb, 0xff);
|
||||
|
@ -440,6 +544,7 @@ static int vhci_open(struct inode *inode, struct file *file)
|
|||
|
||||
mutex_init(&data->open_mutex);
|
||||
INIT_DELAYED_WORK(&data->open_timeout, vhci_open_timeout);
|
||||
INIT_WORK(&data->suspend_work, vhci_suspend_work);
|
||||
|
||||
file->private_data = data;
|
||||
nonseekable_open(inode, file);
|
||||
|
@ -455,6 +560,7 @@ static int vhci_release(struct inode *inode, struct file *file)
|
|||
struct hci_dev *hdev;
|
||||
|
||||
cancel_delayed_work_sync(&data->open_timeout);
|
||||
flush_work(&data->suspend_work);
|
||||
|
||||
hdev = data->hdev;
|
||||
|
||||
|
|
|
@ -202,6 +202,9 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
|
|||
hci_skb_pkt_type(skb) = pkt_type;
|
||||
hci_recv_frame(vbt->hdev, skb);
|
||||
break;
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB
|
||||
/* Copyright (c) 2015 - 2021 Intel Corporation */
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "osdep.h"
|
||||
#include "status.h"
|
||||
#include "hmc.h"
|
||||
|
|
|
@ -228,7 +228,8 @@ static void irdma_fill_device_info(struct irdma_device *iwdev, struct ice_pf *pf
|
|||
rf->msix_count = pf->num_rdma_msix;
|
||||
rf->msix_entries = &pf->msix_entries[pf->rdma_base_vector];
|
||||
rf->default_vsi.vsi_idx = vsi->vsi_num;
|
||||
rf->protocol_used = IRDMA_ROCE_PROTOCOL_ONLY;
|
||||
rf->protocol_used = pf->rdma_mode & IIDC_RDMA_PROTOCOL_ROCEV2 ?
|
||||
IRDMA_ROCE_PROTOCOL_ONLY : IRDMA_IWARP_PROTOCOL_ONLY;
|
||||
rf->rdma_ver = IRDMA_GEN_2;
|
||||
rf->rsrc_profile = IRDMA_HMC_PROFILE_DEFAULT;
|
||||
rf->rst_to = IRDMA_RST_TIMEOUT_HZ;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB
|
||||
/* Copyright (c) 2016 - 2021 Intel Corporation */
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "osdep.h"
|
||||
#include "status.h"
|
||||
#include "hmc.h"
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <rdma/ib_umem.h>
|
||||
|
||||
#include "mlx5_ib.h"
|
||||
|
|
|
@ -1508,7 +1508,7 @@ _get_flow_table(struct mlx5_ib_dev *dev,
|
|||
!esw_encap)
|
||||
flags |= MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT;
|
||||
break;
|
||||
case MLX5_FLOW_NAMESPACE_FDB:
|
||||
case MLX5_FLOW_NAMESPACE_FDB_BYPASS:
|
||||
max_table_size = BIT(
|
||||
MLX5_CAP_ESW_FLOWTABLE_FDB(dev->mdev, log_max_ft_size));
|
||||
if (MLX5_CAP_ESW_FLOWTABLE_FDB(dev->mdev, decap) && esw_encap)
|
||||
|
@ -1517,7 +1517,7 @@ _get_flow_table(struct mlx5_ib_dev *dev,
|
|||
reformat_l3_tunnel_to_l2) &&
|
||||
esw_encap)
|
||||
flags |= MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT;
|
||||
priority = FDB_BYPASS_PATH;
|
||||
priority = fs_matcher->priority;
|
||||
break;
|
||||
case MLX5_FLOW_NAMESPACE_RDMA_RX:
|
||||
max_table_size = BIT(
|
||||
|
@ -1546,8 +1546,8 @@ _get_flow_table(struct mlx5_ib_dev *dev,
|
|||
case MLX5_FLOW_NAMESPACE_EGRESS:
|
||||
prio = &dev->flow_db->egress_prios[priority];
|
||||
break;
|
||||
case MLX5_FLOW_NAMESPACE_FDB:
|
||||
prio = &dev->flow_db->fdb;
|
||||
case MLX5_FLOW_NAMESPACE_FDB_BYPASS:
|
||||
prio = &dev->flow_db->fdb[priority];
|
||||
break;
|
||||
case MLX5_FLOW_NAMESPACE_RDMA_RX:
|
||||
prio = &dev->flow_db->rdma_rx[priority];
|
||||
|
@ -1937,7 +1937,7 @@ mlx5_ib_ft_type_to_namespace(enum mlx5_ib_uapi_flow_table_type table_type,
|
|||
*namespace = MLX5_FLOW_NAMESPACE_EGRESS;
|
||||
break;
|
||||
case MLX5_IB_UAPI_FLOW_TABLE_TYPE_FDB:
|
||||
*namespace = MLX5_FLOW_NAMESPACE_FDB;
|
||||
*namespace = MLX5_FLOW_NAMESPACE_FDB_BYPASS;
|
||||
break;
|
||||
case MLX5_IB_UAPI_FLOW_TABLE_TYPE_RDMA_RX:
|
||||
*namespace = MLX5_FLOW_NAMESPACE_RDMA_RX;
|
||||
|
@ -2029,8 +2029,8 @@ static int get_dests(struct uverbs_attr_bundle *attrs,
|
|||
}
|
||||
|
||||
/* Allow only DEVX object, drop as dest for FDB */
|
||||
if (fs_matcher->ns_type == MLX5_FLOW_NAMESPACE_FDB && !(dest_devx ||
|
||||
(*flags & MLX5_IB_ATTR_CREATE_FLOW_FLAGS_DROP)))
|
||||
if (fs_matcher->ns_type == MLX5_FLOW_NAMESPACE_FDB_BYPASS &&
|
||||
!(dest_devx || (*flags & MLX5_IB_ATTR_CREATE_FLOW_FLAGS_DROP)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Allow only DEVX object or QP as dest when inserting to RDMA_RX */
|
||||
|
@ -2050,7 +2050,7 @@ static int get_dests(struct uverbs_attr_bundle *attrs,
|
|||
if (!is_flow_dest(devx_obj, dest_id, dest_type))
|
||||
return -EINVAL;
|
||||
/* Allow only flow table as dest when inserting to FDB or RDMA_RX */
|
||||
if ((fs_matcher->ns_type == MLX5_FLOW_NAMESPACE_FDB ||
|
||||
if ((fs_matcher->ns_type == MLX5_FLOW_NAMESPACE_FDB_BYPASS ||
|
||||
fs_matcher->ns_type == MLX5_FLOW_NAMESPACE_RDMA_RX) &&
|
||||
*dest_type != MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE)
|
||||
return -EINVAL;
|
||||
|
@ -2320,7 +2320,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_FLOW_MATCHER_CREATE)(
|
|||
if (err)
|
||||
goto end;
|
||||
|
||||
if (obj->ns_type == MLX5_FLOW_NAMESPACE_FDB &&
|
||||
if (obj->ns_type == MLX5_FLOW_NAMESPACE_FDB_BYPASS &&
|
||||
mlx5_eswitch_mode(dev->mdev) != MLX5_ESWITCH_OFFLOADS) {
|
||||
err = -EINVAL;
|
||||
goto end;
|
||||
|
|
|
@ -232,6 +232,7 @@ enum {
|
|||
#define MLX5_IB_NUM_FLOW_FT (MLX5_IB_FLOW_LEFTOVERS_PRIO + 1)
|
||||
#define MLX5_IB_NUM_SNIFFER_FTS 2
|
||||
#define MLX5_IB_NUM_EGRESS_FTS 1
|
||||
#define MLX5_IB_NUM_FDB_FTS MLX5_BY_PASS_NUM_REGULAR_PRIOS
|
||||
struct mlx5_ib_flow_prio {
|
||||
struct mlx5_flow_table *flow_table;
|
||||
unsigned int refcount;
|
||||
|
@ -276,7 +277,7 @@ struct mlx5_ib_flow_db {
|
|||
struct mlx5_ib_flow_prio egress_prios[MLX5_IB_NUM_FLOW_FT];
|
||||
struct mlx5_ib_flow_prio sniffer[MLX5_IB_NUM_SNIFFER_FTS];
|
||||
struct mlx5_ib_flow_prio egress[MLX5_IB_NUM_EGRESS_FTS];
|
||||
struct mlx5_ib_flow_prio fdb;
|
||||
struct mlx5_ib_flow_prio fdb[MLX5_IB_NUM_FDB_FTS];
|
||||
struct mlx5_ib_flow_prio rdma_rx[MLX5_IB_NUM_FLOW_FT];
|
||||
struct mlx5_ib_flow_prio rdma_tx[MLX5_IB_NUM_FLOW_FT];
|
||||
struct mlx5_ib_flow_prio opfcs[MLX5_IB_OPCOUNTER_MAX];
|
||||
|
|
|
@ -1541,16 +1541,10 @@ int mlx5r_odp_create_eq(struct mlx5_ib_dev *dev, struct mlx5_ib_pf_eq *eq)
|
|||
|
||||
eq->irq_nb.notifier_call = mlx5_ib_eq_pf_int;
|
||||
param = (struct mlx5_eq_param) {
|
||||
.irq_index = MLX5_IRQ_EQ_CTRL,
|
||||
.nent = MLX5_IB_NUM_PF_EQE,
|
||||
};
|
||||
param.mask[0] = 1ull << MLX5_EVENT_TYPE_PAGE_FAULT;
|
||||
if (!zalloc_cpumask_var(¶m.affinity, GFP_KERNEL)) {
|
||||
err = -ENOMEM;
|
||||
goto err_wq;
|
||||
}
|
||||
eq->core = mlx5_eq_create_generic(dev->mdev, ¶m);
|
||||
free_cpumask_var(param.affinity);
|
||||
if (IS_ERR(eq->core)) {
|
||||
err = PTR_ERR(eq->core);
|
||||
goto err_wq;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <rdma/ib_umem.h>
|
||||
#include <rdma/ib_cache.h>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
static int showcapimsgs = 0;
|
||||
static int showcapimsgs;
|
||||
static struct workqueue_struct *kcapi_wq;
|
||||
|
||||
module_param(showcapimsgs, uint, 0);
|
||||
|
|
|
@ -643,6 +643,64 @@ static void mei_cl_bus_vtag_free(struct mei_cl_device *cldev)
|
|||
kfree(cl_vtag);
|
||||
}
|
||||
|
||||
void *mei_cldev_dma_map(struct mei_cl_device *cldev, u8 buffer_id, size_t size)
|
||||
{
|
||||
struct mei_device *bus;
|
||||
struct mei_cl *cl;
|
||||
int ret;
|
||||
|
||||
if (!cldev || !buffer_id || !size)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!IS_ALIGNED(size, MEI_FW_PAGE_SIZE)) {
|
||||
dev_err(&cldev->dev, "Map size should be aligned to %lu\n",
|
||||
MEI_FW_PAGE_SIZE);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
cl = cldev->cl;
|
||||
bus = cldev->bus;
|
||||
|
||||
mutex_lock(&bus->device_lock);
|
||||
if (cl->state == MEI_FILE_UNINITIALIZED) {
|
||||
ret = mei_cl_link(cl);
|
||||
if (ret)
|
||||
goto out;
|
||||
/* update pointers */
|
||||
cl->cldev = cldev;
|
||||
}
|
||||
|
||||
ret = mei_cl_dma_alloc_and_map(cl, NULL, buffer_id, size);
|
||||
out:
|
||||
mutex_unlock(&bus->device_lock);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
return cl->dma.vaddr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cldev_dma_map);
|
||||
|
||||
int mei_cldev_dma_unmap(struct mei_cl_device *cldev)
|
||||
{
|
||||
struct mei_device *bus;
|
||||
struct mei_cl *cl;
|
||||
int ret;
|
||||
|
||||
if (!cldev)
|
||||
return -EINVAL;
|
||||
|
||||
cl = cldev->cl;
|
||||
bus = cldev->bus;
|
||||
|
||||
mutex_lock(&bus->device_lock);
|
||||
ret = mei_cl_dma_unmap(cl, NULL);
|
||||
|
||||
mei_cl_flush_queues(cl, NULL);
|
||||
mei_cl_unlink(cl);
|
||||
mutex_unlock(&bus->device_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cldev_dma_unmap);
|
||||
|
||||
/**
|
||||
* mei_cldev_enable - enable me client device
|
||||
* create connection with me client
|
||||
|
@ -753,9 +811,11 @@ int mei_cldev_disable(struct mei_cl_device *cldev)
|
|||
dev_err(bus->dev, "Could not disconnect from the ME client\n");
|
||||
|
||||
out:
|
||||
/* Flush queues and remove any pending read */
|
||||
mei_cl_flush_queues(cl, NULL);
|
||||
mei_cl_unlink(cl);
|
||||
/* Flush queues and remove any pending read unless we have mapped DMA */
|
||||
if (!cl->dma_mapped) {
|
||||
mei_cl_flush_queues(cl, NULL);
|
||||
mei_cl_unlink(cl);
|
||||
}
|
||||
|
||||
mutex_unlock(&bus->device_lock);
|
||||
return err;
|
||||
|
@ -1052,6 +1112,7 @@ static void mei_cl_bus_dev_release(struct device *dev)
|
|||
if (!cldev)
|
||||
return;
|
||||
|
||||
mei_cl_flush_queues(cldev->cl, NULL);
|
||||
mei_me_cl_put(cldev->me_cl);
|
||||
mei_dev_bus_put(cldev->bus);
|
||||
mei_cl_unlink(cldev->cl);
|
||||
|
|
|
@ -700,6 +700,9 @@ int mei_cl_unlink(struct mei_cl *cl)
|
|||
|
||||
cl_dbg(dev, cl, "unlink client");
|
||||
|
||||
if (cl->state == MEI_FILE_UNINITIALIZED)
|
||||
return 0;
|
||||
|
||||
if (dev->open_handle_count > 0)
|
||||
dev->open_handle_count--;
|
||||
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
#define MEI_D0I3_TIMEOUT 5 /* D0i3 set/unset max response time */
|
||||
#define MEI_HBM_TIMEOUT 1 /* 1 second */
|
||||
|
||||
/*
|
||||
* FW page size for DMA allocations
|
||||
*/
|
||||
#define MEI_FW_PAGE_SIZE 4096UL
|
||||
|
||||
/*
|
||||
* MEI Version
|
||||
*/
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/net.h>
|
||||
#include <linux/igmp.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <net/sch_generic.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/udp.h>
|
||||
|
@ -1106,7 +1107,7 @@ static bool amt_send_membership_query(struct amt_dev *amt,
|
|||
rt = ip_route_output_key(amt->net, &fl4);
|
||||
if (IS_ERR(rt)) {
|
||||
netdev_dbg(amt->dev, "no route to %pI4\n", &tunnel->ip4);
|
||||
return -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
amtmq = skb_push(skb, sizeof(*amtmq));
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*/
|
||||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
|
|
|
@ -38,6 +38,13 @@ struct bareudp_net {
|
|||
struct list_head bareudp_list;
|
||||
};
|
||||
|
||||
struct bareudp_conf {
|
||||
__be16 ethertype;
|
||||
__be16 port;
|
||||
u16 sport_min;
|
||||
bool multi_proto_mode;
|
||||
};
|
||||
|
||||
/* Pseudo network device */
|
||||
struct bareudp_dev {
|
||||
struct net *net; /* netns for packet i/o */
|
||||
|
@ -602,7 +609,8 @@ static struct bareudp_dev *bareudp_find_dev(struct bareudp_net *bn,
|
|||
}
|
||||
|
||||
static int bareudp_configure(struct net *net, struct net_device *dev,
|
||||
struct bareudp_conf *conf)
|
||||
struct bareudp_conf *conf,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct bareudp_net *bn = net_generic(net, bareudp_net_id);
|
||||
struct bareudp_dev *t, *bareudp = netdev_priv(dev);
|
||||
|
@ -611,13 +619,17 @@ static int bareudp_configure(struct net *net, struct net_device *dev,
|
|||
bareudp->net = net;
|
||||
bareudp->dev = dev;
|
||||
t = bareudp_find_dev(bn, conf);
|
||||
if (t)
|
||||
if (t) {
|
||||
NL_SET_ERR_MSG(extack, "Another bareudp device using the same port already exists");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (conf->multi_proto_mode &&
|
||||
(conf->ethertype != htons(ETH_P_MPLS_UC) &&
|
||||
conf->ethertype != htons(ETH_P_IP)))
|
||||
conf->ethertype != htons(ETH_P_IP))) {
|
||||
NL_SET_ERR_MSG(extack, "Cannot set multiproto mode for this ethertype (only IPv4 and unicast MPLS are supported)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bareudp->port = conf->port;
|
||||
bareudp->ethertype = conf->ethertype;
|
||||
|
@ -664,7 +676,7 @@ static int bareudp_newlink(struct net *net, struct net_device *dev,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = bareudp_configure(net, dev, &conf);
|
||||
err = bareudp_configure(net, dev, &conf, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -721,40 +733,6 @@ static struct rtnl_link_ops bareudp_link_ops __read_mostly = {
|
|||
.fill_info = bareudp_fill_info,
|
||||
};
|
||||
|
||||
struct net_device *bareudp_dev_create(struct net *net, const char *name,
|
||||
u8 name_assign_type,
|
||||
struct bareudp_conf *conf)
|
||||
{
|
||||
struct nlattr *tb[IFLA_MAX + 1];
|
||||
struct net_device *dev;
|
||||
int err;
|
||||
|
||||
memset(tb, 0, sizeof(tb));
|
||||
dev = rtnl_create_link(net, name, name_assign_type,
|
||||
&bareudp_link_ops, tb, NULL);
|
||||
if (IS_ERR(dev))
|
||||
return dev;
|
||||
|
||||
err = bareudp_configure(net, dev, conf);
|
||||
if (err) {
|
||||
free_netdev(dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
err = dev_set_mtu(dev, IP_MAX_MTU - BAREUDP_BASE_HLEN);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
err = rtnl_configure_link(dev, NULL);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
return dev;
|
||||
err:
|
||||
bareudp_dellink(dev, NULL);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bareudp_dev_create);
|
||||
|
||||
static __net_init int bareudp_init_net(struct net *net)
|
||||
{
|
||||
struct bareudp_net *bn = net_generic(net, bareudp_net_id);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/ioport.h>
|
||||
|
@ -71,6 +72,7 @@
|
|||
#include <linux/ethtool.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/if_bonding.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <net/route.h>
|
||||
|
@ -1096,9 +1098,6 @@ static bool bond_should_notify_peers(struct bonding *bond)
|
|||
slave = rcu_dereference(bond->curr_active_slave);
|
||||
rcu_read_unlock();
|
||||
|
||||
netdev_dbg(bond->dev, "bond_should_notify_peers: slave %s\n",
|
||||
slave ? slave->dev->name : "NULL");
|
||||
|
||||
if (!slave || !bond->send_peer_notif ||
|
||||
bond->send_peer_notif %
|
||||
max(1, bond->params.peer_notif_delay) != 0 ||
|
||||
|
@ -1106,6 +1105,9 @@ static bool bond_should_notify_peers(struct bonding *bond)
|
|||
test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state))
|
||||
return false;
|
||||
|
||||
netdev_dbg(bond->dev, "bond_should_notify_peers: slave %s\n",
|
||||
slave ? slave->dev->name : "NULL");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1460,7 +1462,7 @@ done:
|
|||
bond_dev->hw_enc_features |= xfrm_features;
|
||||
#endif /* CONFIG_XFRM_OFFLOAD */
|
||||
bond_dev->mpls_features = mpls_features;
|
||||
bond_dev->gso_max_segs = gso_max_segs;
|
||||
netif_set_gso_max_segs(bond_dev, gso_max_segs);
|
||||
netif_set_gso_max_size(bond_dev, gso_max_size);
|
||||
|
||||
bond_dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
|
||||
|
@ -3129,8 +3131,8 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
|
|||
* when the source ip is 0, so don't take the link down
|
||||
* if we don't know our ip yet
|
||||
*/
|
||||
if (!bond_time_in_interval(bond, trans_start, 2) ||
|
||||
!bond_time_in_interval(bond, slave->last_rx, 2)) {
|
||||
if (!bond_time_in_interval(bond, trans_start, bond->params.missed_max) ||
|
||||
!bond_time_in_interval(bond, slave->last_rx, bond->params.missed_max)) {
|
||||
|
||||
bond_propose_link_state(slave, BOND_LINK_DOWN);
|
||||
slave_state_changed = 1;
|
||||
|
@ -3224,7 +3226,7 @@ static int bond_ab_arp_inspect(struct bonding *bond)
|
|||
|
||||
/* Backup slave is down if:
|
||||
* - No current_arp_slave AND
|
||||
* - more than 3*delta since last receive AND
|
||||
* - more than (missed_max+1)*delta since last receive AND
|
||||
* - the bond has an IP address
|
||||
*
|
||||
* Note: a non-null current_arp_slave indicates
|
||||
|
@ -3236,20 +3238,20 @@ static int bond_ab_arp_inspect(struct bonding *bond)
|
|||
*/
|
||||
if (!bond_is_active_slave(slave) &&
|
||||
!rcu_access_pointer(bond->current_arp_slave) &&
|
||||
!bond_time_in_interval(bond, last_rx, 3)) {
|
||||
!bond_time_in_interval(bond, last_rx, bond->params.missed_max + 1)) {
|
||||
bond_propose_link_state(slave, BOND_LINK_DOWN);
|
||||
commit++;
|
||||
}
|
||||
|
||||
/* Active slave is down if:
|
||||
* - more than 2*delta since transmitting OR
|
||||
* - (more than 2*delta since receive AND
|
||||
* - more than missed_max*delta since transmitting OR
|
||||
* - (more than missed_max*delta since receive AND
|
||||
* the bond has an IP address)
|
||||
*/
|
||||
trans_start = dev_trans_start(slave->dev);
|
||||
if (bond_is_active_slave(slave) &&
|
||||
(!bond_time_in_interval(bond, trans_start, 2) ||
|
||||
!bond_time_in_interval(bond, last_rx, 2))) {
|
||||
(!bond_time_in_interval(bond, trans_start, bond->params.missed_max) ||
|
||||
!bond_time_in_interval(bond, last_rx, bond->params.missed_max))) {
|
||||
bond_propose_link_state(slave, BOND_LINK_DOWN);
|
||||
commit++;
|
||||
}
|
||||
|
@ -4091,7 +4093,11 @@ static int bond_eth_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cm
|
|||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct mii_ioctl_data *mii = NULL;
|
||||
int res;
|
||||
const struct net_device_ops *ops;
|
||||
struct net_device *real_dev;
|
||||
struct hwtstamp_config cfg;
|
||||
struct ifreq ifrr;
|
||||
int res = 0;
|
||||
|
||||
netdev_dbg(bond_dev, "bond_eth_ioctl: cmd=%d\n", cmd);
|
||||
|
||||
|
@ -4117,7 +4123,42 @@ static int bond_eth_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cm
|
|||
mii->val_out = BMSR_LSTATUS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
break;
|
||||
case SIOCSHWTSTAMP:
|
||||
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!(cfg.flags & HWTSTAMP_FLAG_BONDED_PHC_INDEX))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
fallthrough;
|
||||
case SIOCGHWTSTAMP:
|
||||
rcu_read_lock();
|
||||
real_dev = bond_option_active_slave_get_rcu(bond);
|
||||
rcu_read_unlock();
|
||||
if (!real_dev)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
strscpy_pad(ifrr.ifr_name, real_dev->name, IFNAMSIZ);
|
||||
ifrr.ifr_ifru = ifr->ifr_ifru;
|
||||
|
||||
ops = real_dev->netdev_ops;
|
||||
if (netif_device_present(real_dev) && ops->ndo_eth_ioctl) {
|
||||
res = ops->ndo_eth_ioctl(real_dev, &ifrr, cmd);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
ifr->ifr_ifru = ifrr.ifr_ifru;
|
||||
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
|
||||
return -EFAULT;
|
||||
|
||||
/* Set the BOND_PHC_INDEX flag to notify user space */
|
||||
cfg.flags |= HWTSTAMP_FLAG_BONDED_PHC_INDEX;
|
||||
|
||||
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ?
|
||||
-EFAULT : 0;
|
||||
}
|
||||
fallthrough;
|
||||
default:
|
||||
res = -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -5319,10 +5360,40 @@ static void bond_ethtool_get_drvinfo(struct net_device *bond_dev,
|
|||
BOND_ABI_VERSION);
|
||||
}
|
||||
|
||||
static int bond_ethtool_get_ts_info(struct net_device *bond_dev,
|
||||
struct ethtool_ts_info *info)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
const struct ethtool_ops *ops;
|
||||
struct net_device *real_dev;
|
||||
struct phy_device *phydev;
|
||||
|
||||
rcu_read_lock();
|
||||
real_dev = bond_option_active_slave_get_rcu(bond);
|
||||
rcu_read_unlock();
|
||||
if (real_dev) {
|
||||
ops = real_dev->ethtool_ops;
|
||||
phydev = real_dev->phydev;
|
||||
|
||||
if (phy_has_tsinfo(phydev)) {
|
||||
return phy_ts_info(phydev, info);
|
||||
} else if (ops->get_ts_info) {
|
||||
return ops->get_ts_info(real_dev, info);
|
||||
}
|
||||
}
|
||||
|
||||
info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_SOFTWARE;
|
||||
info->phc_index = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops bond_ethtool_ops = {
|
||||
.get_drvinfo = bond_ethtool_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_link_ksettings = bond_ethtool_get_link_ksettings,
|
||||
.get_ts_info = bond_ethtool_get_ts_info,
|
||||
};
|
||||
|
||||
static const struct net_device_ops bond_netdev_ops = {
|
||||
|
@ -5822,6 +5893,7 @@ static int bond_check_params(struct bond_params *params)
|
|||
params->arp_interval = arp_interval;
|
||||
params->arp_validate = arp_validate_value;
|
||||
params->arp_all_targets = arp_all_targets_value;
|
||||
params->missed_max = 2;
|
||||
params->updelay = updelay;
|
||||
params->downdelay = downdelay;
|
||||
params->peer_notif_delay = 0;
|
||||
|
|
|
@ -110,6 +110,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = {
|
|||
.len = ETH_ALEN },
|
||||
[IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NLA_U8 },
|
||||
[IFLA_BOND_PEER_NOTIF_DELAY] = { .type = NLA_U32 },
|
||||
[IFLA_BOND_MISSED_MAX] = { .type = NLA_U8 },
|
||||
};
|
||||
|
||||
static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = {
|
||||
|
@ -453,6 +454,15 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[],
|
|||
return err;
|
||||
}
|
||||
|
||||
if (data[IFLA_BOND_MISSED_MAX]) {
|
||||
int missed_max = nla_get_u8(data[IFLA_BOND_MISSED_MAX]);
|
||||
|
||||
bond_opt_initval(&newval, missed_max);
|
||||
err = __bond_opt_set(bond, BOND_OPT_MISSED_MAX, &newval);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -515,6 +525,7 @@ static size_t bond_get_size(const struct net_device *bond_dev)
|
|||
nla_total_size(ETH_ALEN) + /* IFLA_BOND_AD_ACTOR_SYSTEM */
|
||||
nla_total_size(sizeof(u8)) + /* IFLA_BOND_TLB_DYNAMIC_LB */
|
||||
nla_total_size(sizeof(u32)) + /* IFLA_BOND_PEER_NOTIF_DELAY */
|
||||
nla_total_size(sizeof(u8)) + /* IFLA_BOND_MISSED_MAX */
|
||||
0;
|
||||
}
|
||||
|
||||
|
@ -650,6 +661,10 @@ static int bond_fill_info(struct sk_buff *skb,
|
|||
bond->params.tlb_dynamic_lb))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u8(skb, IFLA_BOND_MISSED_MAX,
|
||||
bond->params.missed_max))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct ad_info info;
|
||||
|
||||
|
|
|
@ -78,6 +78,8 @@ static int bond_option_ad_actor_system_set(struct bonding *bond,
|
|||
const struct bond_opt_value *newval);
|
||||
static int bond_option_ad_user_port_key_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval);
|
||||
static int bond_option_missed_max_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval);
|
||||
|
||||
|
||||
static const struct bond_opt_value bond_mode_tbl[] = {
|
||||
|
@ -213,6 +215,13 @@ static const struct bond_opt_value bond_ad_user_port_key_tbl[] = {
|
|||
{ NULL, -1, 0},
|
||||
};
|
||||
|
||||
static const struct bond_opt_value bond_missed_max_tbl[] = {
|
||||
{ "minval", 1, BOND_VALFLAG_MIN},
|
||||
{ "maxval", 255, BOND_VALFLAG_MAX},
|
||||
{ "default", 2, BOND_VALFLAG_DEFAULT},
|
||||
{ NULL, -1, 0},
|
||||
};
|
||||
|
||||
static const struct bond_option bond_opts[BOND_OPT_LAST] = {
|
||||
[BOND_OPT_MODE] = {
|
||||
.id = BOND_OPT_MODE,
|
||||
|
@ -270,6 +279,15 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = {
|
|||
.values = bond_intmax_tbl,
|
||||
.set = bond_option_arp_interval_set
|
||||
},
|
||||
[BOND_OPT_MISSED_MAX] = {
|
||||
.id = BOND_OPT_MISSED_MAX,
|
||||
.name = "arp_missed_max",
|
||||
.desc = "Maximum number of missed ARP interval",
|
||||
.unsuppmodes = BIT(BOND_MODE_8023AD) | BIT(BOND_MODE_TLB) |
|
||||
BIT(BOND_MODE_ALB),
|
||||
.values = bond_missed_max_tbl,
|
||||
.set = bond_option_missed_max_set
|
||||
},
|
||||
[BOND_OPT_ARP_TARGETS] = {
|
||||
.id = BOND_OPT_ARP_TARGETS,
|
||||
.name = "arp_ip_target",
|
||||
|
@ -1186,6 +1204,16 @@ static int bond_option_arp_all_targets_set(struct bonding *bond,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bond_option_missed_max_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval)
|
||||
{
|
||||
netdev_dbg(bond->dev, "Setting missed max to %s (%llu)\n",
|
||||
newval->string, newval->value);
|
||||
bond->params.missed_max = newval->value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bond_option_primary_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval)
|
||||
{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue