forked from Minki/linux
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Highlights: - Gustavo A. R. Silva keeps working on the implicit switch fallthru changes. - Support 802.11ax High-Efficiency wireless in cfg80211 et al, From Luca Coelho. - Re-enable ASPM in r8169, from Kai-Heng Feng. - Add virtual XFRM interfaces, which avoids all of the limitations of existing IPSEC tunnels. From Steffen Klassert. - Convert GRO over to use a hash table, so that when we have many flows active we don't traverse a long list during accumluation. - Many new self tests for routing, TC, tunnels, etc. Too many contributors to mention them all, but I'm really happy to keep seeing this stuff. - Hardware timestamping support for dpaa_eth/fsl-fman from Yangbo Lu. - Lots of cleanups and fixes in L2TP code from Guillaume Nault. - Add IPSEC offload support to netdevsim, from Shannon Nelson. - Add support for slotting with non-uniform distribution to netem packet scheduler, from Yousuk Seung. - Add UDP GSO support to mlx5e, from Boris Pismenny. - Support offloading of Team LAG in NFP, from John Hurley. - Allow to configure TX queue selection based upon RX queue, from Amritha Nambiar. - Support ethtool ring size configuration in aquantia, from Anton Mikaev. - Support DSCP and flowlabel per-transport in SCTP, from Xin Long. - Support list based batching and stack traversal of SKBs, this is very exciting work. From Edward Cree. - Busyloop optimizations in vhost_net, from Toshiaki Makita. - Introduce the ETF qdisc, which allows time based transmissions. IGB can offload this in hardware. From Vinicius Costa Gomes. - Add parameter support to devlink, from Moshe Shemesh. - Several multiplication and division optimizations for BPF JIT in nfp driver, from Jiong Wang. - Lots of prepatory work to make more of the packet scheduler layer lockless, when possible, from Vlad Buslov. - Add ACK filter and NAT awareness to sch_cake packet scheduler, from Toke Høiland-Jørgensen. - Support regions and region snapshots in devlink, from Alex Vesker. - Allow to attach XDP programs to both HW and SW at the same time on a given device, with initial support in nfp. From Jakub Kicinski. - Add TLS RX offload and support in mlx5, from Ilya Lesokhin. - Use PHYLIB in r8169 driver, from Heiner Kallweit. - All sorts of changes to support Spectrum 2 in mlxsw driver, from Ido Schimmel. - PTP support in mv88e6xxx DSA driver, from Andrew Lunn. - Make TCP_USER_TIMEOUT socket option more accurate, from Jon Maxwell. - Support for templates in packet scheduler classifier, from Jiri Pirko. - IPV6 support in RDS, from Ka-Cheong Poon. - Native tproxy support in nf_tables, from Máté Eckl. - Maintain IP fragment queue in an rbtree, but optimize properly for in-order frags. From Peter Oskolkov. - Improvde handling of ACKs on hole repairs, from Yuchung Cheng" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1996 commits) bpf: test: fix spelling mistake "REUSEEPORT" -> "REUSEPORT" hv/netvsc: Fix NULL dereference at single queue mode fallback net: filter: mark expected switch fall-through xen-netfront: fix warn message as irq device name has '/' cxgb4: Add new T5 PCI device ids 0x50af and 0x50b0 net: dsa: mv88e6xxx: missing unlock on error path rds: fix building with IPV6=m inet/connection_sock: prefer _THIS_IP_ to current_text_addr net: dsa: mv88e6xxx: bitwise vs logical bug net: sock_diag: Fix spectre v1 gadget in __sock_diag_cmd() ieee802154: hwsim: using right kind of iteration net: hns3: Add vlan filter setting by ethtool command -K net: hns3: Set tx ring' tc info when netdev is up net: hns3: Remove tx ring BD len register in hns3_enet net: hns3: Fix desc num set to default when setting channel net: hns3: Fix for phy link issue when using marvell phy driver net: hns3: Fix for information of phydev lost problem when down/up net: hns3: Fix for command format parsing error in hclge_is_all_function_id_zero net: hns3: Add support for serdes loopback selftest bnxt_en: take coredump_record structure off stack ...
This commit is contained in:
commit
9a76aba02a
@ -11,7 +11,7 @@ KernelVersion: v2.6.22
|
||||
Contact: linux-wireless@vger.kernel.org,
|
||||
Description: The rfkill class subsystem folder.
|
||||
Each registered rfkill driver is represented by an rfkillX
|
||||
subfolder (X being an integer > 0).
|
||||
subfolder (X being an integer >= 0).
|
||||
|
||||
|
||||
What: /sys/class/rfkill/rfkill[0-9]+/name
|
||||
@ -48,8 +48,8 @@ Contact: linux-wireless@vger.kernel.org
|
||||
Description: Current state of the transmitter.
|
||||
This file was scheduled to be removed in 2014, but due to its
|
||||
large number of users it will be sticking around for a bit
|
||||
longer. Despite it being marked as stabe, the newer "hard" and
|
||||
"soft" interfaces should be preffered, since it is not possible
|
||||
longer. Despite it being marked as stable, the newer "hard" and
|
||||
"soft" interfaces should be preferred, since it is not possible
|
||||
to express the 'soft and hard block' state of the rfkill driver
|
||||
through this interface. There will likely be another attempt to
|
||||
remove it in the future.
|
||||
|
@ -42,6 +42,17 @@ Description:
|
||||
network device transmit queue. Possible vaules depend on the
|
||||
number of available CPU(s) in the system.
|
||||
|
||||
What: /sys/class/<iface>/queues/tx-<queue>/xps_rxqs
|
||||
Date: June 2018
|
||||
KernelVersion: 4.18.0
|
||||
Contact: netdev@vger.kernel.org
|
||||
Description:
|
||||
Mask of the receive queue(s) currently enabled to participate
|
||||
into the Transmit Packet Steering packet processing flow for this
|
||||
network device transmit queue. Possible values depend on the
|
||||
number of available receive queue(s) in the network device.
|
||||
Default is disabled.
|
||||
|
||||
What: /sys/class/<iface>/queues/tx-<queue>/byte_queue_limits/hold_time
|
||||
Date: November 2011
|
||||
KernelVersion: 3.3
|
||||
|
@ -106,9 +106,9 @@ into the bpf-next tree will make their way into net-next tree. net and
|
||||
net-next are both run by David S. Miller. From there, they will go
|
||||
into the kernel mainline tree run by Linus Torvalds. To read up on the
|
||||
process of net and net-next being merged into the mainline tree, see
|
||||
the `netdev FAQ`_ under:
|
||||
the :ref:`netdev-FAQ`
|
||||
|
||||
|
||||
`Documentation/networking/netdev-FAQ.txt`_
|
||||
|
||||
Occasionally, to prevent merge conflicts, we might send pull requests
|
||||
to other trees (e.g. tracing) with a small subset of the patches, but
|
||||
@ -125,8 +125,8 @@ request)::
|
||||
Q: How do I indicate which tree (bpf vs. bpf-next) my patch should be applied to?
|
||||
---------------------------------------------------------------------------------
|
||||
|
||||
A: The process is the very same as described in the `netdev FAQ`_, so
|
||||
please read up on it. The subject line must indicate whether the
|
||||
A: The process is the very same as described in the :ref:`netdev-FAQ`,
|
||||
so please read up on it. The subject line must indicate whether the
|
||||
patch is a fix or rather "next-like" content in order to let the
|
||||
maintainers know whether it is targeted at bpf or bpf-next.
|
||||
|
||||
@ -184,7 +184,7 @@ ii) run extensive BPF test suite and
|
||||
Once the BPF pull request was accepted by David S. Miller, then
|
||||
the patches end up in net or net-next tree, respectively, and
|
||||
make their way from there further into mainline. Again, see the
|
||||
`netdev FAQ`_ for additional information e.g. on how often they are
|
||||
:ref:`netdev-FAQ` for additional information e.g. on how often they are
|
||||
merged to mainline.
|
||||
|
||||
Q: How long do I need to wait for feedback on my BPF patches?
|
||||
@ -208,7 +208,7 @@ Q: Are patches applied to bpf-next when the merge window is open?
|
||||
-----------------------------------------------------------------
|
||||
A: For the time when the merge window is open, bpf-next will not be
|
||||
processed. This is roughly analogous to net-next patch processing,
|
||||
so feel free to read up on the `netdev FAQ`_ about further details.
|
||||
so feel free to read up on the :ref:`netdev-FAQ` about further details.
|
||||
|
||||
During those two weeks of merge window, we might ask you to resend
|
||||
your patch series once bpf-next is open again. Once Linus released
|
||||
@ -372,7 +372,7 @@ netdev kernel mailing list in Cc and ask for the fix to be queued up:
|
||||
netdev@vger.kernel.org
|
||||
|
||||
The process in general is the same as on netdev itself, see also the
|
||||
`netdev FAQ`_ document.
|
||||
:ref:`netdev-FAQ`.
|
||||
|
||||
Q: Do you also backport to kernels not currently maintained as stable?
|
||||
----------------------------------------------------------------------
|
||||
@ -388,9 +388,7 @@ Q: The BPF patch I am about to submit needs to go to stable as well
|
||||
What should I do?
|
||||
|
||||
A: The same rules apply as with netdev patch submissions in general, see
|
||||
`netdev FAQ`_ under:
|
||||
|
||||
`Documentation/networking/netdev-FAQ.txt`_
|
||||
the :ref:`netdev-FAQ`.
|
||||
|
||||
Never add "``Cc: stable@vger.kernel.org``" to the patch description, but
|
||||
ask the BPF maintainers to queue the patches instead. This can be done
|
||||
@ -630,8 +628,7 @@ when:
|
||||
.. Links
|
||||
.. _Documentation/process/: https://www.kernel.org/doc/html/latest/process/
|
||||
.. _MAINTAINERS: ../../MAINTAINERS
|
||||
.. _Documentation/networking/netdev-FAQ.txt: ../networking/netdev-FAQ.txt
|
||||
.. _netdev FAQ: ../networking/netdev-FAQ.txt
|
||||
.. _netdev-FAQ: ../networking/netdev-FAQ.rst
|
||||
.. _samples/bpf/: ../../samples/bpf/
|
||||
.. _selftests: ../../tools/testing/selftests/bpf/
|
||||
.. _Documentation/dev-tools/kselftest.rst:
|
||||
|
@ -1,5 +1,5 @@
|
||||
=================
|
||||
BPF documentation
|
||||
BPF Documentation
|
||||
=================
|
||||
|
||||
This directory contains documentation for the BPF (Berkeley Packet
|
||||
@ -22,14 +22,14 @@ Frequently asked questions (FAQ)
|
||||
|
||||
Two sets of Questions and Answers (Q&A) are maintained.
|
||||
|
||||
* QA for common questions about BPF see: bpf_design_QA_
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
* QA for developers interacting with BPF subsystem: bpf_devel_QA_
|
||||
bpf_design_QA
|
||||
bpf_devel_QA
|
||||
|
||||
|
||||
.. Links:
|
||||
.. _bpf_design_QA: bpf_design_QA.rst
|
||||
.. _bpf_devel_QA: bpf_devel_QA.rst
|
||||
.. _Documentation/networking/filter.txt: ../networking/filter.txt
|
||||
.. _man-pages: https://www.kernel.org/doc/man-pages/
|
||||
.. _bpf(2): http://man7.org/linux/man-pages/man2/bpf.2.html
|
@ -13,14 +13,17 @@ MDIO multiplexer node:
|
||||
Every non-ethernet PHY requires a compatible so that it could be probed based
|
||||
on this compatible string.
|
||||
|
||||
Optional properties:
|
||||
- clocks: phandle of the core clock which drives the mdio block.
|
||||
|
||||
Additional information regarding generic multiplexer properties can be found
|
||||
at- Documentation/devicetree/bindings/net/mdio-mux.txt
|
||||
|
||||
|
||||
for example:
|
||||
mdio_mux_iproc: mdio-mux@6602023c {
|
||||
mdio_mux_iproc: mdio-mux@66020000 {
|
||||
compatible = "brcm,mdio-mux-iproc";
|
||||
reg = <0x6602023c 0x14>;
|
||||
reg = <0x66020000 0x250>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
|
@ -2,19 +2,25 @@ Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings
|
||||
---------------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "xlnx,zynq-can-1.0" for Zynq CAN
|
||||
controllers and "xlnx,axi-can-1.00.a" for Axi CAN
|
||||
controllers.
|
||||
- reg : Physical base address and size of the Axi CAN/Zynq
|
||||
CANPS registers map.
|
||||
- compatible : Should be:
|
||||
- "xlnx,zynq-can-1.0" for Zynq CAN controllers
|
||||
- "xlnx,axi-can-1.00.a" for Axi CAN controllers
|
||||
- "xlnx,canfd-1.0" for CAN FD controllers
|
||||
- reg : Physical base address and size of the controller
|
||||
registers map.
|
||||
- interrupts : Property with a value describing the interrupt
|
||||
number.
|
||||
- clock-names : List of input clock names - "can_clk", "pclk"
|
||||
(For CANPS), "can_clk" , "s_axi_aclk"(For AXI CAN)
|
||||
- clock-names : List of input clock names
|
||||
- "can_clk", "pclk" (For CANPS),
|
||||
- "can_clk", "s_axi_aclk" (For AXI CAN and CAN FD).
|
||||
(See clock bindings for details).
|
||||
- clocks : Clock phandles (see clock bindings for details).
|
||||
- tx-fifo-depth : Can Tx fifo depth.
|
||||
- rx-fifo-depth : Can Rx fifo depth.
|
||||
- tx-fifo-depth : Can Tx fifo depth (Zynq, Axi CAN).
|
||||
- rx-fifo-depth : Can Rx fifo depth (Zynq, Axi CAN, CAN FD in
|
||||
sequential Rx mode).
|
||||
- tx-mailbox-count : Can Tx mailbox buffer count (CAN FD).
|
||||
- rx-mailbox-count : Can Rx mailbox buffer count (CAN FD in mailbox Rx
|
||||
mode).
|
||||
|
||||
|
||||
Example:
|
||||
@ -41,3 +47,14 @@ For Axi CAN Dts file:
|
||||
tx-fifo-depth = <0x40>;
|
||||
rx-fifo-depth = <0x40>;
|
||||
};
|
||||
For CAN FD Dts file:
|
||||
canfd_0: canfd@40000000 {
|
||||
compatible = "xlnx,canfd-1.0";
|
||||
clocks = <&clkc 0>, <&clkc 1>;
|
||||
clock-names = "can_clk", "s_axi_aclk";
|
||||
reg = <0x40000000 0x2000>;
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <0 59 1>;
|
||||
tx-mailbox-count = <0x20>;
|
||||
rx-fifo-depth = <0x20>;
|
||||
};
|
||||
|
@ -24,6 +24,14 @@ Required properties:
|
||||
"brcm,bcm53018-srab"
|
||||
"brcm,bcm53019-srab" and the mandatory "brcm,bcm5301x-srab" string
|
||||
|
||||
For the BCM5831X/BCM1140x SoCs with an integrated switch, must be one of:
|
||||
"brcm,bcm11404-srab"
|
||||
"brcm,bcm11407-srab"
|
||||
"brcm,bcm11409-srab"
|
||||
"brcm,bcm58310-srab"
|
||||
"brcm,bcm58311-srab"
|
||||
"brcm,bcm58313-srab" and the mandatory "brcm,omega-srab" string
|
||||
|
||||
For the BCM585xx/586XX/88312 SoCs with an integrated switch, must be one of:
|
||||
"brcm,bcm58522-srab"
|
||||
"brcm,bcm58523-srab"
|
||||
|
153
Documentation/devicetree/bindings/net/dsa/realtek-smi.txt
Normal file
153
Documentation/devicetree/bindings/net/dsa/realtek-smi.txt
Normal file
@ -0,0 +1,153 @@
|
||||
Realtek SMI-based Switches
|
||||
==========================
|
||||
|
||||
The SMI "Simple Management Interface" is a two-wire protocol using
|
||||
bit-banged GPIO that while it reuses the MDIO lines MCK and MDIO does
|
||||
not use the MDIO protocol. This binding defines how to specify the
|
||||
SMI-based Realtek devices.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be exactly one of:
|
||||
"realtek,rtl8366"
|
||||
"realtek,rtl8366rb" (4+1 ports)
|
||||
"realtek,rtl8366s" (4+1 ports)
|
||||
"realtek,rtl8367"
|
||||
"realtek,rtl8367b"
|
||||
"realtek,rtl8368s" (8 port)
|
||||
"realtek,rtl8369"
|
||||
"realtek,rtl8370" (8 port)
|
||||
|
||||
Required properties:
|
||||
- mdc-gpios: GPIO line for the MDC clock line.
|
||||
- mdio-gpios: GPIO line for the MDIO data line.
|
||||
- reset-gpios: GPIO line for the reset signal.
|
||||
|
||||
Optional properties:
|
||||
- realtek,disable-leds: if the LED drivers are not used in the
|
||||
hardware design this will disable them so they are not turned on
|
||||
and wasting power.
|
||||
|
||||
Required subnodes:
|
||||
|
||||
- interrupt-controller
|
||||
|
||||
This defines an interrupt controller with an IRQ line (typically
|
||||
a GPIO) that will demultiplex and handle the interrupt from the single
|
||||
interrupt line coming out of one of the SMI-based chips. It most
|
||||
importantly provides link up/down interrupts to the PHY blocks inside
|
||||
the ASIC.
|
||||
|
||||
Required properties of interrupt-controller:
|
||||
|
||||
- interrupt: parent interrupt, see interrupt-controller/interrupts.txt
|
||||
- interrupt-controller: see interrupt-controller/interrupts.txt
|
||||
- #address-cells: should be <0>
|
||||
- #interrupt-cells: should be <1>
|
||||
|
||||
- mdio
|
||||
|
||||
This defines the internal MDIO bus of the SMI device, mostly for the
|
||||
purpose of being able to hook the interrupts to the right PHY and
|
||||
the right PHY to the corresponding port.
|
||||
|
||||
Required properties of mdio:
|
||||
|
||||
- compatible: should be set to "realtek,smi-mdio" for all SMI devices
|
||||
|
||||
See net/mdio.txt for additional MDIO bus properties.
|
||||
|
||||
See net/dsa/dsa.txt for a list of additional required and optional properties
|
||||
and subnodes of DSA switches.
|
||||
|
||||
Examples:
|
||||
|
||||
switch {
|
||||
compatible = "realtek,rtl8366rb";
|
||||
/* 22 = MDIO (has input reads), 21 = MDC (clock, output only) */
|
||||
mdc-gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>;
|
||||
mdio-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
|
||||
|
||||
switch_intc: interrupt-controller {
|
||||
/* GPIO 15 provides the interrupt */
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0>;
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan0";
|
||||
phy-handle = <&phy0>;
|
||||
};
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan1";
|
||||
phy-handle = <&phy1>;
|
||||
};
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan2";
|
||||
phy-handle = <&phy2>;
|
||||
};
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan3";
|
||||
phy-handle = <&phy3>;
|
||||
};
|
||||
port@4 {
|
||||
reg = <4>;
|
||||
label = "wan";
|
||||
phy-handle = <&phy4>;
|
||||
};
|
||||
port@5 {
|
||||
reg = <5>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac0>;
|
||||
phy-mode = "rgmii";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdio {
|
||||
compatible = "realtek,smi-mdio", "dsa-mdio";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
phy0: phy@0 {
|
||||
reg = <0>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <0>;
|
||||
};
|
||||
phy1: phy@1 {
|
||||
reg = <1>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <1>;
|
||||
};
|
||||
phy2: phy@2 {
|
||||
reg = <2>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <2>;
|
||||
};
|
||||
phy3: phy@3 {
|
||||
reg = <3>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <3>;
|
||||
};
|
||||
phy4: phy@4 {
|
||||
reg = <4>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <12>;
|
||||
};
|
||||
};
|
||||
};
|
@ -0,0 +1,81 @@
|
||||
Vitesse VSC73xx Switches
|
||||
========================
|
||||
|
||||
This defines device tree bindings for the Vitesse VSC73xx switch chips.
|
||||
The Vitesse company has been acquired by Microsemi and Microsemi in turn
|
||||
acquired by Microchip but retains this vendor branding.
|
||||
|
||||
The currently supported switch chips are:
|
||||
Vitesse VSC7385 SparX-G5 5+1-port Integrated Gigabit Ethernet Switch
|
||||
Vitesse VSC7388 SparX-G8 8-port Integrated Gigabit Ethernet Switch
|
||||
Vitesse VSC7395 SparX-G5e 5+1-port Integrated Gigabit Ethernet Switch
|
||||
Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch
|
||||
|
||||
The device tree node is an SPI device so it must reside inside a SPI bus
|
||||
device tree node, see spi/spi-bus.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be exactly one of:
|
||||
"vitesse,vsc7385"
|
||||
"vitesse,vsc7388"
|
||||
"vitesse,vsc7395"
|
||||
"vitesse,vsc7398"
|
||||
- gpio-controller: indicates that this switch is also a GPIO controller,
|
||||
see gpio/gpio.txt
|
||||
- #gpio-cells: this must be set to <2> and indicates that we are a twocell
|
||||
GPIO controller, see gpio/gpio.txt
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reset-gpios: a handle to a GPIO line that can issue reset of the chip.
|
||||
It should be tagged as active low.
|
||||
|
||||
Required subnodes:
|
||||
|
||||
See net/dsa/dsa.txt for a list of additional required and optional properties
|
||||
and subnodes of DSA switches.
|
||||
|
||||
Examples:
|
||||
|
||||
switch@0 {
|
||||
compatible = "vitesse,vsc7395";
|
||||
reg = <0>;
|
||||
/* Specified for 2.5 MHz or below */
|
||||
spi-max-frequency = <2500000>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan1";
|
||||
};
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan2";
|
||||
};
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan3";
|
||||
};
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan4";
|
||||
};
|
||||
vsc: port@6 {
|
||||
reg = <6>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac1>;
|
||||
phy-mode = "rgmii";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -356,30 +356,7 @@ ethernet@e0000 {
|
||||
============================================================================
|
||||
FMan IEEE 1588 Node
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
The FMan interface to support IEEE 1588
|
||||
|
||||
|
||||
PROPERTIES
|
||||
|
||||
- compatible
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: A standard property.
|
||||
Must include "fsl,fman-ptp-timer".
|
||||
|
||||
- reg
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: A standard property.
|
||||
|
||||
EXAMPLE
|
||||
|
||||
ptp-timer@fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0xfe000 0x1000>;
|
||||
};
|
||||
Refer to Documentation/devicetree/bindings/ptp/ptp-qoriq.txt
|
||||
|
||||
=============================================================================
|
||||
FMan MDIO Node
|
||||
|
35
Documentation/devicetree/bindings/net/mediatek-bluetooth.txt
Normal file
35
Documentation/devicetree/bindings/net/mediatek-bluetooth.txt
Normal file
@ -0,0 +1,35 @@
|
||||
MediaTek SoC built-in Bluetooth Devices
|
||||
==================================
|
||||
|
||||
This device is a serial attached device to BTIF device and thus it must be a
|
||||
child node of the serial node with BTIF. The dt-bindings details for BTIF
|
||||
device can be known via Documentation/devicetree/bindings/serial/8250.txt.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Must be
|
||||
"mediatek,mt7622-bluetooth": for MT7622 SoC
|
||||
- clocks: Should be the clock specifiers corresponding to the entry in
|
||||
clock-names property.
|
||||
- clock-names: Should contain "ref" entries.
|
||||
- power-domains: Phandle to the power domain that the device is part of
|
||||
|
||||
Example:
|
||||
|
||||
btif: serial@1100c000 {
|
||||
compatible = "mediatek,mt7622-btif",
|
||||
"mediatek,mtk-btif";
|
||||
reg = <0 0x1100c000 0 0x1000>;
|
||||
interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_LOW>;
|
||||
clocks = <&pericfg CLK_PERI_BTIF_PD>;
|
||||
clock-names = "main";
|
||||
reg-shift = <2>;
|
||||
reg-io-width = <4>;
|
||||
|
||||
bluetooth {
|
||||
compatible = "mediatek,mt7622-bluetooth";
|
||||
power-domains = <&scpsys MT7622_POWER_DOMAIN_WB>;
|
||||
clocks = <&clk25m>;
|
||||
clock-names = "ref";
|
||||
};
|
||||
};
|
@ -10,12 +10,25 @@ device the slave device is attached to.
|
||||
Required properties:
|
||||
- compatible: should contain one of the following:
|
||||
* "qcom,qca6174-bt"
|
||||
* "qcom,wcn3990-bt"
|
||||
|
||||
Optional properties for compatible string qcom,qca6174-bt:
|
||||
|
||||
Optional properties:
|
||||
- enable-gpios: gpio specifier used to enable chip
|
||||
- clocks: clock provided to the controller (SUSCLK_32KHZ)
|
||||
|
||||
Example:
|
||||
Required properties for compatible string qcom,wcn3990-bt:
|
||||
|
||||
- vddio-supply: VDD_IO supply regulator handle.
|
||||
- vddxo-supply: VDD_XO supply regulator handle.
|
||||
- vddrf-supply: VDD_RF supply regulator handle.
|
||||
- vddch0-supply: VDD_CH0 supply regulator handle.
|
||||
|
||||
Optional properties for compatible string qcom,wcn3990-bt:
|
||||
|
||||
- max-speed: see Documentation/devicetree/bindings/serial/slave-device.txt
|
||||
|
||||
Examples:
|
||||
|
||||
serial@7570000 {
|
||||
label = "BT-UART";
|
||||
@ -28,3 +41,15 @@ serial@7570000 {
|
||||
clocks = <&divclk4>;
|
||||
};
|
||||
};
|
||||
|
||||
serial@898000 {
|
||||
bluetooth {
|
||||
compatible = "qcom,wcn3990-bt";
|
||||
|
||||
vddio-supply = <&vreg_s4a_1p8>;
|
||||
vddxo-supply = <&vreg_l7a_1p8>;
|
||||
vddrf-supply = <&vreg_l17a_1p3>;
|
||||
vddch0-supply = <&vreg_l25a_3p3>;
|
||||
max-speed = <3200000>;
|
||||
};
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ The device node has following properties.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "rockchip,<name>-gamc"
|
||||
"rockchip,px30-gmac": found on PX30 SoCs
|
||||
"rockchip,rk3128-gmac": found on RK312x SoCs
|
||||
"rockchip,rk3228-gmac": found on RK322x SoCs
|
||||
"rockchip,rk3288-gmac": found on RK3288 SoCs
|
||||
|
@ -1,7 +1,8 @@
|
||||
* STMicroelectronics 10/100/1000 Ethernet driver (GMAC)
|
||||
* STMicroelectronics 10/100/1000/2500/10000 Ethernet (GMAC/XGMAC)
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "snps,dwmac-<ip_version>", "snps,dwmac"
|
||||
- compatible: Should be "snps,dwmac-<ip_version>", "snps,dwmac" or
|
||||
"snps,dwxgmac-<ip_version>", "snps,dwxgmac".
|
||||
For backwards compatibility: "st,spear600-gmac" is also supported.
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain the STMMAC interrupts
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
General Properties:
|
||||
|
||||
- compatible Should be "fsl,etsec-ptp"
|
||||
- compatible Should be "fsl,etsec-ptp" for eTSEC
|
||||
Should be "fsl,fman-ptp-timer" for DPAA FMan
|
||||
- reg Offset and length of the register set for the device
|
||||
- interrupts There should be at least two interrupts. Some devices
|
||||
have as many as four PTP related interrupts.
|
||||
@ -43,14 +44,22 @@ Clock Properties:
|
||||
value, which will be directly written in those bits, that is why,
|
||||
according to reference manual, the next clock sources can be used:
|
||||
|
||||
For eTSEC,
|
||||
<0> - external high precision timer reference clock (TSEC_TMR_CLK
|
||||
input is used for this purpose);
|
||||
<1> - eTSEC system clock;
|
||||
<2> - eTSEC1 transmit clock;
|
||||
<3> - RTC clock input.
|
||||
|
||||
When this attribute is not used, eTSEC system clock will serve as
|
||||
IEEE 1588 timer reference clock.
|
||||
For DPAA FMan,
|
||||
<0> - external high precision timer reference clock (TMR_1588_CLK)
|
||||
<1> - MAC system clock (1/2 FMan clock)
|
||||
<2> - reserved
|
||||
<3> - RTC clock oscillator
|
||||
|
||||
When this attribute is not used, the IEEE 1588 timer reference clock
|
||||
will use the eTSEC system clock (for Gianfar) or the MAC system
|
||||
clock (for DPAA).
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -397,6 +397,7 @@ v3 V3 Semiconductor
|
||||
variscite Variscite Ltd.
|
||||
via VIA Technologies, Inc.
|
||||
virtio Virtual I/O Device Specification, developed by the OASIS consortium
|
||||
vitesse Vitesse Semiconductor Corporation
|
||||
vivante Vivante Corporation
|
||||
vocore VoCore Studio
|
||||
voipac Voipac Technologies s.r.o.
|
||||
|
@ -92,6 +92,7 @@ needed).
|
||||
crypto/index
|
||||
filesystems/index
|
||||
vm/index
|
||||
bpf/index
|
||||
|
||||
Architecture-specific documentation
|
||||
-----------------------------------
|
||||
|
@ -18,8 +18,6 @@ README.ipw2200
|
||||
- README for the Intel PRO/Wireless 2915ABG and 2200BG driver.
|
||||
README.sb1000
|
||||
- info on General Instrument/NextLevel SURFboard1000 cable modem.
|
||||
alias.txt
|
||||
- info on using alias network devices.
|
||||
altera_tse.txt
|
||||
- Altera Triple-Speed Ethernet controller.
|
||||
arcnet-hardware.txt
|
||||
@ -140,8 +138,6 @@ multiqueue.txt
|
||||
- HOWTO for multiqueue network device support.
|
||||
netconsole.txt
|
||||
- The network console module netconsole.ko: configuration and notes.
|
||||
netdev-FAQ.txt
|
||||
- FAQ describing how to submit net changes to netdev mailing list.
|
||||
netdev-features.txt
|
||||
- Network interface features API description.
|
||||
netdevices.txt
|
||||
|
49
Documentation/networking/alias.rst
Normal file
49
Documentation/networking/alias.rst
Normal file
@ -0,0 +1,49 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
===========
|
||||
IP-Aliasing
|
||||
===========
|
||||
|
||||
IP-aliases are an obsolete way to manage multiple IP-addresses/masks
|
||||
per interface. Newer tools such as iproute2 support multiple
|
||||
address/prefixes per interface, but aliases are still supported
|
||||
for backwards compatibility.
|
||||
|
||||
An alias is formed by adding a colon and a string when running ifconfig.
|
||||
This string is usually numeric, but this is not a must.
|
||||
|
||||
|
||||
Alias creation
|
||||
==============
|
||||
|
||||
Alias creation is done by 'magic' interface naming: eg. to create a
|
||||
200.1.1.1 alias for eth0 ...
|
||||
::
|
||||
|
||||
# ifconfig eth0:0 200.1.1.1 etc,etc....
|
||||
~~ -> request alias #0 creation (if not yet exists) for eth0
|
||||
|
||||
The corresponding route is also set up by this command. Please note:
|
||||
The route always points to the base interface.
|
||||
|
||||
|
||||
Alias deletion
|
||||
==============
|
||||
|
||||
The alias is removed by shutting the alias down::
|
||||
|
||||
# ifconfig eth0:0 down
|
||||
~~~~~~~~~~ -> will delete alias
|
||||
|
||||
|
||||
Alias (re-)configuring
|
||||
======================
|
||||
|
||||
Aliases are not real devices, but programs should be able to configure
|
||||
and refer to them as usual (ifconfig, route, etc).
|
||||
|
||||
|
||||
Relationship with main device
|
||||
=============================
|
||||
|
||||
If the base device is shut down the added aliases will be deleted too.
|
@ -1,40 +0,0 @@
|
||||
|
||||
IP-Aliasing:
|
||||
============
|
||||
|
||||
IP-aliases are an obsolete way to manage multiple IP-addresses/masks
|
||||
per interface. Newer tools such as iproute2 support multiple
|
||||
address/prefixes per interface, but aliases are still supported
|
||||
for backwards compatibility.
|
||||
|
||||
An alias is formed by adding a colon and a string when running ifconfig.
|
||||
This string is usually numeric, but this is not a must.
|
||||
|
||||
o Alias creation.
|
||||
Alias creation is done by 'magic' interface naming: eg. to create a
|
||||
200.1.1.1 alias for eth0 ...
|
||||
|
||||
# ifconfig eth0:0 200.1.1.1 etc,etc....
|
||||
~~ -> request alias #0 creation (if not yet exists) for eth0
|
||||
|
||||
The corresponding route is also set up by this command.
|
||||
Please note: The route always points to the base interface.
|
||||
|
||||
|
||||
o Alias deletion.
|
||||
The alias is removed by shutting the alias down:
|
||||
|
||||
# ifconfig eth0:0 down
|
||||
~~~~~~~~~~ -> will delete alias
|
||||
|
||||
|
||||
o Alias (re-)configuring
|
||||
|
||||
Aliases are not real devices, but programs should be able to configure and
|
||||
refer to them as usual (ifconfig, route, etc).
|
||||
|
||||
|
||||
o Relationship with main device
|
||||
|
||||
If the base device is shut down the added aliases will be deleted
|
||||
too.
|
@ -1,3 +1,9 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=================
|
||||
Ethernet Bridging
|
||||
=================
|
||||
|
||||
In order to use the Ethernet bridging functionality, you'll need the
|
||||
userspace tools.
|
||||
|
332
Documentation/networking/can_ucan_protocol.rst
Normal file
332
Documentation/networking/can_ucan_protocol.rst
Normal file
@ -0,0 +1,332 @@
|
||||
=================
|
||||
The UCAN Protocol
|
||||
=================
|
||||
|
||||
UCAN is the protocol used by the microcontroller-based USB-CAN
|
||||
adapter that is integrated on System-on-Modules from Theobroma Systems
|
||||
and that is also available as a standalone USB stick.
|
||||
|
||||
The UCAN protocol has been designed to be hardware-independent.
|
||||
It is modeled closely after how Linux represents CAN devices
|
||||
internally. All multi-byte integers are encoded as Little Endian.
|
||||
|
||||
All structures mentioned in this document are defined in
|
||||
``drivers/net/can/usb/ucan.c``.
|
||||
|
||||
USB Endpoints
|
||||
=============
|
||||
|
||||
UCAN devices use three USB endpoints:
|
||||
|
||||
CONTROL endpoint
|
||||
The driver sends device management commands on this endpoint
|
||||
|
||||
IN endpoint
|
||||
The device sends CAN data frames and CAN error frames
|
||||
|
||||
OUT endpoint
|
||||
The driver sends CAN data frames on the out endpoint
|
||||
|
||||
|
||||
CONTROL Messages
|
||||
================
|
||||
|
||||
UCAN devices are configured using vendor requests on the control pipe.
|
||||
|
||||
To support multiple CAN interfaces in a single USB device all
|
||||
configuration commands target the corresponding interface in the USB
|
||||
descriptor.
|
||||
|
||||
The driver uses ``ucan_ctrl_command_in/out`` and
|
||||
``ucan_device_request_in`` to deliver commands to the device.
|
||||
|
||||
Setup Packet
|
||||
------------
|
||||
|
||||
================= =====================================================
|
||||
``bmRequestType`` Direction | Vendor | (Interface or Device)
|
||||
``bRequest`` Command Number
|
||||
``wValue`` Subcommand Number (16 Bit) or 0 if not used
|
||||
``wIndex`` USB Interface Index (0 for device commands)
|
||||
``wLength`` * Host to Device - Number of bytes to transmit
|
||||
* Device to Host - Maximum Number of bytes to
|
||||
receive. If the device send less. Commom ZLP
|
||||
semantics are used.
|
||||
================= =====================================================
|
||||
|
||||
Error Handling
|
||||
--------------
|
||||
|
||||
The device indicates failed control commands by stalling the
|
||||
pipe.
|
||||
|
||||
Device Commands
|
||||
---------------
|
||||
|
||||
UCAN_DEVICE_GET_FW_STRING
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Dev2Host; optional*
|
||||
|
||||
Request the device firmware string.
|
||||
|
||||
|
||||
Interface Commands
|
||||
------------------
|
||||
|
||||
UCAN_COMMAND_START
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Bring the CAN interface up.
|
||||
|
||||
Payload Format
|
||||
``ucan_ctl_payload_t.cmd_start``
|
||||
|
||||
==== ============================
|
||||
mode or mask of ``UCAN_MODE_*``
|
||||
==== ============================
|
||||
|
||||
UCAN_COMMAND_STOP
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Stop the CAN interface
|
||||
|
||||
Payload Format
|
||||
*empty*
|
||||
|
||||
UCAN_COMMAND_RESET
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Reset the CAN controller (including error counters)
|
||||
|
||||
Payload Format
|
||||
*empty*
|
||||
|
||||
UCAN_COMMAND_GET
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Get Information from the Device
|
||||
|
||||
Subcommands
|
||||
^^^^^^^^^^^
|
||||
|
||||
UCAN_COMMAND_GET_INFO
|
||||
Request the device information structure ``ucan_ctl_payload_t.device_info``.
|
||||
|
||||
See the ``device_info`` field for details, and
|
||||
``uapi/linux/can/netlink.h`` for an explanation of the
|
||||
``can_bittiming fields``.
|
||||
|
||||
Payload Format
|
||||
``ucan_ctl_payload_t.device_info``
|
||||
|
||||
UCAN_COMMAND_GET_PROTOCOL_VERSION
|
||||
|
||||
Request the device protocol version
|
||||
``ucan_ctl_payload_t.protocol_version``. The current protocol version is 3.
|
||||
|
||||
Payload Format
|
||||
``ucan_ctl_payload_t.protocol_version``
|
||||
|
||||
.. note:: Devices that do not implement this command use the old
|
||||
protocol version 1
|
||||
|
||||
UCAN_COMMAND_SET_BITTIMING
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; mandatory*
|
||||
|
||||
Setup bittiming by sending the the structure
|
||||
``ucan_ctl_payload_t.cmd_set_bittiming`` (see ``struct bittiming`` for
|
||||
details)
|
||||
|
||||
Payload Format
|
||||
``ucan_ctl_payload_t.cmd_set_bittiming``.
|
||||
|
||||
UCAN_SLEEP/WAKE
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; optional*
|
||||
|
||||
Configure sleep and wake modes. Not yet supported by the driver.
|
||||
|
||||
UCAN_FILTER
|
||||
~~~~~~~~~~~
|
||||
|
||||
*Host2Dev; optional*
|
||||
|
||||
Setup hardware CAN filters. Not yet supported by the driver.
|
||||
|
||||
Allowed interface commands
|
||||
--------------------------
|
||||
|
||||
================== =================== ==================
|
||||
Legal Device State Command New Device State
|
||||
================== =================== ==================
|
||||
stopped SET_BITTIMING stopped
|
||||
stopped START started
|
||||
started STOP or RESET stopped
|
||||
stopped STOP or RESET stopped
|
||||
started RESTART started
|
||||
any GET *no change*
|
||||
================== =================== ==================
|
||||
|
||||
IN Message Format
|
||||
=================
|
||||
|
||||
A data packet on the USB IN endpoint contains one or more
|
||||
``ucan_message_in`` values. If multiple messages are batched in a USB
|
||||
data packet, the ``len`` field can be used to jump to the next
|
||||
``ucan_message_in`` value (take care to sanity-check the ``len`` value
|
||||
against the actual data size).
|
||||
|
||||
.. _can_ucan_in_message_len:
|
||||
|
||||
``len`` field
|
||||
-------------
|
||||
|
||||
Each ``ucan_message_in`` must be aligned to a 4-byte boundary (relative
|
||||
to the start of the start of the data buffer). That means that there
|
||||
may be padding bytes between multiple ``ucan_message_in`` values:
|
||||
|
||||
.. code::
|
||||
|
||||
+----------------------------+ < 0
|
||||
| |
|
||||
| struct ucan_message_in |
|
||||
| |
|
||||
+----------------------------+ < len
|
||||
[padding]
|
||||
+----------------------------+ < round_up(len, 4)
|
||||
| |
|
||||
| struct ucan_message_in |
|
||||
| |
|
||||
+----------------------------+
|
||||
[...]
|
||||
|
||||
``type`` field
|
||||
--------------
|
||||
|
||||
The ``type`` field specifies the type of the message.
|
||||
|
||||
UCAN_IN_RX
|
||||
~~~~~~~~~~
|
||||
|
||||
``subtype``
|
||||
zero
|
||||
|
||||
Data received from the CAN bus (ID + payload).
|
||||
|
||||
UCAN_IN_TX_COMPLETE
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``subtype``
|
||||
zero
|
||||
|
||||
The CAN device has sent a message to the CAN bus. It answers with a
|
||||
list of of tuples <echo-ids, flags>.
|
||||
|
||||
The echo-id identifies the frame from (echos the id from a previous
|
||||
UCAN_OUT_TX message). The flag indicates the result of the
|
||||
transmission. Whereas a set Bit 0 indicates success. All other bits
|
||||
are reserved and set to zero.
|
||||
|
||||
Flow Control
|
||||
------------
|
||||
|
||||
When receiving CAN messages there is no flow control on the USB
|
||||
buffer. The driver has to handle inbound message quickly enough to
|
||||
avoid drops. I case the device buffer overflow the condition is
|
||||
reported by sending corresponding error frames (see
|
||||
:ref:`can_ucan_error_handling`)
|
||||
|
||||
|
||||
OUT Message Format
|
||||
==================
|
||||
|
||||
A data packet on the USB OUT endpoint contains one or more ``struct
|
||||
ucan_message_out`` values. If multiple messages are batched into one
|
||||
data packet, the device uses the ``len`` field to jump to the next
|
||||
ucan_message_out value. Each ucan_message_out must be aligned to 4
|
||||
bytes (relative to the start of the data buffer). The mechanism is
|
||||
same as described in :ref:`can_ucan_in_message_len`.
|
||||
|
||||
.. code::
|
||||
|
||||
+----------------------------+ < 0
|
||||
| |
|
||||
| struct ucan_message_out |
|
||||
| |
|
||||
+----------------------------+ < len
|
||||
[padding]
|
||||
+----------------------------+ < round_up(len, 4)
|
||||
| |
|
||||
| struct ucan_message_out |
|
||||
| |
|
||||
+----------------------------+
|
||||
[...]
|
||||
|
||||
``type`` field
|
||||
--------------
|
||||
|
||||
In protocol version 3 only ``UCAN_OUT_TX`` is defined, others are used
|
||||
only by legacy devices (protocol version 1).
|
||||
|
||||
UCAN_OUT_TX
|
||||
~~~~~~~~~~~
|
||||
``subtype``
|
||||
echo id to be replied within a CAN_IN_TX_COMPLETE message
|
||||
|
||||
Transmit a CAN frame. (parameters: ``id``, ``data``)
|
||||
|
||||
Flow Control
|
||||
------------
|
||||
|
||||
When the device outbound buffers are full it starts sending *NAKs* on
|
||||
the *OUT* pipe until more buffers are available. The driver stops the
|
||||
queue when a certain threshold of out packets are incomplete.
|
||||
|
||||
.. _can_ucan_error_handling:
|
||||
|
||||
CAN Error Handling
|
||||
==================
|
||||
|
||||
If error reporting is turned on the device encodes errors into CAN
|
||||
error frames (see ``uapi/linux/can/error.h``) and sends it using the
|
||||
IN endpoint. The driver updates its error statistics and forwards
|
||||
it.
|
||||
|
||||
Although UCAN devices can suppress error frames completely, in Linux
|
||||
the driver is always interested. Hence, the device is always started with
|
||||
the ``UCAN_MODE_BERR_REPORT`` set. Filtering those messages for the
|
||||
user space is done by the driver.
|
||||
|
||||
Bus OFF
|
||||
-------
|
||||
|
||||
- The device does not recover from bus of automatically.
|
||||
- Bus OFF is indicated by an error frame (see ``uapi/linux/can/error.h``)
|
||||
- Bus OFF recovery is started by ``UCAN_COMMAND_RESTART``
|
||||
- Once Bus OFF recover is completed the device sends an error frame
|
||||
indicating that it is on ERROR-ACTIVE state.
|
||||
- During Bus OFF no frames are sent by the device.
|
||||
- During Bus OFF transmission requests from the host are completed
|
||||
immediately with the success bit left unset.
|
||||
|
||||
Example Conversation
|
||||
====================
|
||||
|
||||
#) Device is connected to USB
|
||||
#) Host sends command ``UCAN_COMMAND_RESET``, subcmd 0
|
||||
#) Host sends command ``UCAN_COMMAND_GET``, subcmd ``UCAN_COMMAND_GET_INFO``
|
||||
#) Device sends ``UCAN_IN_DEVICE_INFO``
|
||||
#) Host sends command ``UCAN_OUT_SET_BITTIMING``
|
||||
#) Host sends command ``UCAN_COMMAND_START``, subcmd 0, mode ``UCAN_MODE_BERR_REPORT``
|
@ -6,15 +6,21 @@ Contents:
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
netdev-FAQ
|
||||
af_xdp
|
||||
batman-adv
|
||||
can
|
||||
can_ucan_protocol
|
||||
dpaa2/index
|
||||
e100
|
||||
e1000
|
||||
kapi
|
||||
z8530book
|
||||
msg_zerocopy
|
||||
failover
|
||||
net_failover
|
||||
alias
|
||||
bridge
|
||||
|
||||
.. only:: subproject
|
||||
|
||||
|
@ -81,6 +81,15 @@ fib_multipath_hash_policy - INTEGER
|
||||
0 - Layer 3
|
||||
1 - Layer 4
|
||||
|
||||
ip_forward_update_priority - INTEGER
|
||||
Whether to update SKB priority from "TOS" field in IPv4 header after it
|
||||
is forwarded. The new SKB priority is mapped from TOS field value
|
||||
according to an rt_tos2priority table (see e.g. man tc-prio).
|
||||
Default: 1 (Update priority.)
|
||||
Possible values:
|
||||
0 - Do not update priority.
|
||||
1 - Update priority.
|
||||
|
||||
route/max_size - INTEGER
|
||||
Maximum number of routes allowed in the kernel. Increase
|
||||
this when using large numbers of interfaces and/or routes.
|
||||
@ -733,11 +742,11 @@ tcp_limit_output_bytes - INTEGER
|
||||
Controls TCP Small Queue limit per tcp socket.
|
||||
TCP bulk sender tends to increase packets in flight until it
|
||||
gets losses notifications. With SNDBUF autotuning, this can
|
||||
result in a large amount of packets queued in qdisc/device
|
||||
on the local machine, hurting latency of other flows, for
|
||||
typical pfifo_fast qdiscs.
|
||||
tcp_limit_output_bytes limits the number of bytes on qdisc
|
||||
or device to reduce artificial RTT/cwnd and reduce bufferbloat.
|
||||
result in a large amount of packets queued on the local machine
|
||||
(e.g.: qdiscs, CPU backlog, or device) hurting latency of other
|
||||
flows, for typical pfifo_fast qdiscs. tcp_limit_output_bytes
|
||||
limits the number of bytes on qdisc or device to reduce artificial
|
||||
RTT/cwnd and reduce bufferbloat.
|
||||
Default: 262144
|
||||
|
||||
tcp_challenge_ack_limit - INTEGER
|
||||
@ -1834,6 +1843,16 @@ stable_secret - IPv6 address
|
||||
|
||||
By default the stable secret is unset.
|
||||
|
||||
addr_gen_mode - INTEGER
|
||||
Defines how link-local and autoconf addresses are generated.
|
||||
|
||||
0: generate address based on EUI64 (default)
|
||||
1: do no generate a link-local address, use EUI64 for addresses generated
|
||||
from autoconf
|
||||
2: generate stable privacy addresses, using the secret from
|
||||
stable_secret (RFC7217)
|
||||
3: generate stable privacy addresses, using a random secret if unset
|
||||
|
||||
drop_unicast_in_l2_multicast - BOOLEAN
|
||||
Drop any unicast IPv6 packets that are received in link-layer
|
||||
multicast (or broadcast) frames.
|
||||
@ -1863,6 +1882,11 @@ ratelimit - INTEGER
|
||||
otherwise the minimal space between responses in milliseconds.
|
||||
Default: 1000
|
||||
|
||||
echo_ignore_all - BOOLEAN
|
||||
If set non-zero, then the kernel will ignore all ICMP ECHO
|
||||
requests sent to it over the IPv6 protocol.
|
||||
Default: 0
|
||||
|
||||
xfrm6_gc_thresh - INTEGER
|
||||
The threshold at which we will start garbage collecting for IPv6
|
||||
destination cache entries. At twice this value the system will
|
||||
|
@ -36,37 +36,39 @@ 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.
|
||||
::
|
||||
|
||||
<interface type='network'>
|
||||
<mac address='52:54:00:00:12:53'/>
|
||||
<source network='enp66s0f0_br'/>
|
||||
<target dev='tap01'/>
|
||||
<model type='virtio'/>
|
||||
<driver name='vhost' queues='4'/>
|
||||
<link state='down'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/>
|
||||
</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'/>
|
||||
</interface>
|
||||
<interface type='network'>
|
||||
<mac address='52:54:00:00:12:53'/>
|
||||
<source network='enp66s0f0_br'/>
|
||||
<target dev='tap01'/>
|
||||
<model type='virtio'/>
|
||||
<driver name='vhost' queues='4'/>
|
||||
<link state='down'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x0a' function='0x0'/>
|
||||
</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'/>
|
||||
</interface>
|
||||
|
||||
Booting a VM with the above configuration will result in the following 3
|
||||
netdevs created in the VM.
|
||||
::
|
||||
|
||||
4: ens10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
|
||||
link/ether 52:54:00:00:12:53 brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.12.53/24 brd 192.168.12.255 scope global dynamic ens10
|
||||
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
|
||||
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
|
||||
4: ens10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
|
||||
link/ether 52:54:00:00:12:53 brd ff:ff:ff:ff:ff:ff
|
||||
inet 192.168.12.53/24 brd 192.168.12.255 scope global dynamic ens10
|
||||
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
|
||||
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.
|
||||
@ -80,37 +82,38 @@ 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.
|
||||
::
|
||||
|
||||
# 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'/>
|
||||
</interface>
|
||||
# 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'/>
|
||||
</interface>
|
||||
|
||||
# Source Hypervisor
|
||||
#!/bin/bash
|
||||
# Source Hypervisor
|
||||
#!/bin/bash
|
||||
|
||||
DOMAIN=fedora27-tap01
|
||||
PF=enp66s0f0
|
||||
VF_NUM=5
|
||||
TAP_IF=tap01
|
||||
VF_XML=
|
||||
DOMAIN=fedora27-tap01
|
||||
PF=enp66s0f0
|
||||
VF_NUM=5
|
||||
TAP_IF=tap01
|
||||
VF_XML=
|
||||
|
||||
MAC=52:54:00:00:12:53
|
||||
ZERO_MAC=00:00:00:00:00:00
|
||||
MAC=52:54:00:00:12:53
|
||||
ZERO_MAC=00:00:00:00:00:00
|
||||
|
||||
virsh domif-setlink $DOMAIN $TAP_IF up
|
||||
bridge fdb del $MAC dev $PF master
|
||||
virsh detach-device $DOMAIN $VF_XML
|
||||
ip link set $PF vf $VF_NUM mac $ZERO_MAC
|
||||
virsh domif-setlink $DOMAIN $TAP_IF up
|
||||
bridge fdb del $MAC dev $PF master
|
||||
virsh detach-device $DOMAIN $VF_XML
|
||||
ip link set $PF vf $VF_NUM mac $ZERO_MAC
|
||||
|
||||
virsh migrate --live $DOMAIN qemu+ssh://$REMOTE_HOST/system
|
||||
virsh migrate --live $DOMAIN qemu+ssh://$REMOTE_HOST/system
|
||||
|
||||
# Destination Hypervisor
|
||||
#!/bin/bash
|
||||
# Destination Hypervisor
|
||||
#!/bin/bash
|
||||
|
||||
virsh attach-device $DOMAIN $VF_XML
|
||||
virsh domif-setlink $DOMAIN $TAP_IF down
|
||||
virsh attach-device $DOMAIN $VF_XML
|
||||
virsh domif-setlink $DOMAIN $TAP_IF down
|
||||
|
259
Documentation/networking/netdev-FAQ.rst
Normal file
259
Documentation/networking/netdev-FAQ.rst
Normal file
@ -0,0 +1,259 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. _netdev-FAQ:
|
||||
|
||||
==========
|
||||
netdev FAQ
|
||||
==========
|
||||
|
||||
Q: What is netdev?
|
||||
------------------
|
||||
A: It is a mailing list for all network-related Linux stuff. This
|
||||
includes anything found under net/ (i.e. core code like IPv6) and
|
||||
drivers/net (i.e. hardware specific drivers) in the Linux source tree.
|
||||
|
||||
Note that some subsystems (e.g. wireless drivers) which have a high
|
||||
volume of traffic have their own specific mailing lists.
|
||||
|
||||
The netdev list is managed (like many other Linux mailing lists) through
|
||||
VGER (http://vger.kernel.org/) and archives can be found below:
|
||||
|
||||
- http://marc.info/?l=linux-netdev
|
||||
- http://www.spinics.net/lists/netdev/
|
||||
|
||||
Aside from subsystems like that mentioned above, all network-related
|
||||
Linux development (i.e. RFC, review, comments, etc.) takes place on
|
||||
netdev.
|
||||
|
||||
Q: How do the changes posted to netdev make their way into Linux?
|
||||
-----------------------------------------------------------------
|
||||
A: There are always two trees (git repositories) in play. Both are
|
||||
driven by David Miller, the main network maintainer. There is the
|
||||
``net`` tree, and the ``net-next`` tree. As you can probably guess from
|
||||
the names, the ``net`` tree is for fixes to existing code already in the
|
||||
mainline tree from Linus, and ``net-next`` is where the new code goes
|
||||
for the future release. You can find the trees here:
|
||||
|
||||
- https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git
|
||||
- https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git
|
||||
|
||||
Q: How often do changes from these trees make it to the mainline Linus tree?
|
||||
----------------------------------------------------------------------------
|
||||
A: To understand this, you need to know a bit of background information on
|
||||
the cadence of Linux development. Each new release starts off with a
|
||||
two week "merge window" where the main maintainers feed their new stuff
|
||||
to Linus for merging into the mainline tree. After the two weeks, the
|
||||
merge window is closed, and it is called/tagged ``-rc1``. No new
|
||||
features get mainlined after this -- only fixes to the rc1 content are
|
||||
expected. After roughly a week of collecting fixes to the rc1 content,
|
||||
rc2 is released. This repeats on a roughly weekly basis until rc7
|
||||
(typically; sometimes rc6 if things are quiet, or rc8 if things are in a
|
||||
state of churn), and a week after the last vX.Y-rcN was done, the
|
||||
official vX.Y is released.
|
||||
|
||||
Relating that to netdev: At the beginning of the 2-week merge window,
|
||||
the ``net-next`` tree will be closed - no new changes/features. The
|
||||
accumulated new content of the past ~10 weeks will be passed onto
|
||||
mainline/Linus via a pull request for vX.Y -- at the same time, the
|
||||
``net`` tree will start accumulating fixes for this pulled content
|
||||
relating to vX.Y
|
||||
|
||||
An announcement indicating when ``net-next`` has been closed is usually
|
||||
sent to netdev, but knowing the above, you can predict that in advance.
|
||||
|
||||
IMPORTANT: Do not send new ``net-next`` content to netdev during the
|
||||
period during which ``net-next`` tree is closed.
|
||||
|
||||
Shortly after the two weeks have passed (and vX.Y-rc1 is released), the
|
||||
tree for ``net-next`` reopens to collect content for the next (vX.Y+1)
|
||||
release.
|
||||
|
||||
If you aren't subscribed to netdev and/or are simply unsure if
|
||||
``net-next`` has re-opened yet, simply check the ``net-next`` git
|
||||
repository link above for any new networking-related commits. You may
|
||||
also check the following website for the current status:
|
||||
|
||||
http://vger.kernel.org/~davem/net-next.html
|
||||
|
||||
The ``net`` tree continues to collect fixes for the vX.Y content, and is
|
||||
fed back to Linus at regular (~weekly) intervals. Meaning that the
|
||||
focus for ``net`` is on stabilization and bug fixes.
|
||||
|
||||
Finally, the vX.Y gets released, and the whole cycle starts over.
|
||||
|
||||
Q: So where are we now in this cycle?
|
||||
|
||||
Load the mainline (Linus) page here:
|
||||
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
|
||||
|
||||
and note the top of the "tags" section. If it is rc1, it is early in
|
||||
the dev cycle. If it was tagged rc7 a week ago, then a release is
|
||||
probably imminent.
|
||||
|
||||
Q: How do I indicate which tree (net vs. net-next) my patch should be in?
|
||||
-------------------------------------------------------------------------
|
||||
A: Firstly, think whether you have a bug fix or new "next-like" content.
|
||||
Then once decided, assuming that you use git, use the prefix flag, i.e.
|
||||
::
|
||||
|
||||
git format-patch --subject-prefix='PATCH net-next' start..finish
|
||||
|
||||
Use ``net`` instead of ``net-next`` (always lower case) in the above for
|
||||
bug-fix ``net`` content. If you don't use git, then note the only magic
|
||||
in the above is just the subject text of the outgoing e-mail, and you
|
||||
can manually change it yourself with whatever MUA you are comfortable
|
||||
with.
|
||||
|
||||
Q: I sent a patch and I'm wondering what happened to it?
|
||||
--------------------------------------------------------
|
||||
Q: How can I tell whether it got merged?
|
||||
A: Start by looking at the main patchworks queue for netdev:
|
||||
|
||||
http://patchwork.ozlabs.org/project/netdev/list/
|
||||
|
||||
The "State" field will tell you exactly where things are at with your
|
||||
patch.
|
||||
|
||||
Q: The above only says "Under Review". How can I find out more?
|
||||
----------------------------------------------------------------
|
||||
A: Generally speaking, the patches get triaged quickly (in less than
|
||||
48h). So be patient. Asking the maintainer for status updates on your
|
||||
patch is a good way to ensure your patch is ignored or pushed to the
|
||||
bottom of the priority list.
|
||||
|
||||
Q: I submitted multiple versions of the patch series
|
||||
----------------------------------------------------
|
||||
Q: should I directly update patchwork for the previous versions of these
|
||||
patch series?
|
||||
A: No, please don't interfere with the patch status on patchwork, leave
|
||||
it to the maintainer to figure out what is the most recent and current
|
||||
version that should be applied. If there is any doubt, the maintainer
|
||||
will reply and ask what should be done.
|
||||
|
||||
Q: How can I tell what patches are queued up for backporting to the various stable releases?
|
||||
--------------------------------------------------------------------------------------------
|
||||
A: Normally Greg Kroah-Hartman collects stable commits himself, but for
|
||||
networking, Dave collects up patches he deems critical for the
|
||||
networking subsystem, and then hands them off to Greg.
|
||||
|
||||
There is a patchworks queue that you can see here:
|
||||
|
||||
http://patchwork.ozlabs.org/bundle/davem/stable/?state=*
|
||||
|
||||
It contains the patches which Dave has selected, but not yet handed off
|
||||
to Greg. If Greg already has the patch, then it will be here:
|
||||
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/stable/stable-queue.git
|
||||
|
||||
A quick way to find whether the patch is in this stable-queue is to
|
||||
simply clone the repo, and then git grep the mainline commit ID, e.g.
|
||||
::
|
||||
|
||||
stable-queue$ git grep -l 284041ef21fdf2e
|
||||
releases/3.0.84/ipv6-fix-possible-crashes-in-ip6_cork_release.patch
|
||||
releases/3.4.51/ipv6-fix-possible-crashes-in-ip6_cork_release.patch
|
||||
releases/3.9.8/ipv6-fix-possible-crashes-in-ip6_cork_release.patch
|
||||
stable/stable-queue$
|
||||
|
||||
Q: I see a network patch and I think it should be backported to stable.
|
||||
-----------------------------------------------------------------------
|
||||
Q: Should I request it via stable@vger.kernel.org like the references in
|
||||
the kernel's Documentation/process/stable-kernel-rules.rst file say?
|
||||
A: No, not for networking. Check the stable queues as per above first
|
||||
to see if it is already queued. If not, then send a mail to netdev,
|
||||
listing the upstream commit ID and why you think it should be a stable
|
||||
candidate.
|
||||
|
||||
Before you jump to go do the above, do note that the normal stable rules
|
||||
in :ref:`Documentation/process/stable-kernel-rules.rst <stable_kernel_rules>`
|
||||
still apply. So you need to explicitly indicate why it is a critical
|
||||
fix and exactly what users are impacted. In addition, you need to
|
||||
convince yourself that you *really* think it has been overlooked,
|
||||
vs. having been considered and rejected.
|
||||
|
||||
Generally speaking, the longer it has had a chance to "soak" in
|
||||
mainline, the better the odds that it is an OK candidate for stable. So
|
||||
scrambling to request a commit be added the day after it appears should
|
||||
be avoided.
|
||||
|
||||
Q: I have created a network patch and I think it should be backported to stable.
|
||||
--------------------------------------------------------------------------------
|
||||
Q: Should I add a Cc: stable@vger.kernel.org like the references in the
|
||||
kernel's Documentation/ directory say?
|
||||
A: No. See above answer. In short, if you think it really belongs in
|
||||
stable, then ensure you write a decent commit log that describes who
|
||||
gets impacted by the bug fix and how it manifests itself, and when the
|
||||
bug was introduced. If you do that properly, then the commit will get
|
||||
handled appropriately and most likely get put in the patchworks stable
|
||||
queue if it really warrants it.
|
||||
|
||||
If you think there is some valid information relating to it being in
|
||||
stable that does *not* belong in the commit log, then use the three dash
|
||||
marker line as described in
|
||||
:ref:`Documentation/process/submitting-patches.rst <the_canonical_patch_format>`
|
||||
to temporarily embed that information into the patch that you send.
|
||||
|
||||
Q: Are all networking bug fixes backported to all stable releases?
|
||||
------------------------------------------------------------------
|
||||
A: Due to capacity, Dave could only take care of the backports for the
|
||||
last two stable releases. For earlier stable releases, each stable
|
||||
branch maintainer is supposed to take care of them. If you find any
|
||||
patch is missing from an earlier stable branch, please notify
|
||||
stable@vger.kernel.org with either a commit ID or a formal patch
|
||||
backported, and CC Dave and other relevant networking developers.
|
||||
|
||||
Q: Is the comment style convention different for the networking content?
|
||||
------------------------------------------------------------------------
|
||||
A: Yes, in a largely trivial way. Instead of this::
|
||||
|
||||
/*
|
||||
* foobar blah blah blah
|
||||
* another line of text
|
||||
*/
|
||||
|
||||
it is requested that you make it look like this::
|
||||
|
||||
/* foobar blah blah blah
|
||||
* another line of text
|
||||
*/
|
||||
|
||||
Q: I am working in existing code that has the former comment style and not the latter.
|
||||
--------------------------------------------------------------------------------------
|
||||
Q: Should I submit new code in the former style or the latter?
|
||||
A: Make it the latter style, so that eventually all code in the domain
|
||||
of netdev is of this format.
|
||||
|
||||
Q: I found a bug that might have possible security implications or similar.
|
||||
---------------------------------------------------------------------------
|
||||
Q: Should I mail the main netdev maintainer off-list?**
|
||||
A: No. The current netdev maintainer has consistently requested that
|
||||
people use the mailing lists and not reach out directly. If you aren't
|
||||
OK with that, then perhaps consider mailing security@kernel.org or
|
||||
reading about http://oss-security.openwall.org/wiki/mailing-lists/distros
|
||||
as possible alternative mechanisms.
|
||||
|
||||
Q: What level of testing is expected before I submit my change?
|
||||
---------------------------------------------------------------
|
||||
A: If your changes are against ``net-next``, the expectation is that you
|
||||
have tested by layering your changes on top of ``net-next``. Ideally
|
||||
you will have done run-time testing specific to your change, but at a
|
||||
minimum, your changes should survive an ``allyesconfig`` and an
|
||||
``allmodconfig`` build without new warnings or failures.
|
||||
|
||||
Q: Any other tips to help ensure my net/net-next patch gets OK'd?
|
||||
-----------------------------------------------------------------
|
||||
A: Attention to detail. Re-read your own work as if you were the
|
||||
reviewer. You can start with using ``checkpatch.pl``, perhaps even with
|
||||
the ``--strict`` flag. But do not be mindlessly robotic in doing so.
|
||||
If your change is a bug fix, make sure your commit log indicates the
|
||||
end-user visible symptom, the underlying reason as to why it happens,
|
||||
and then if necessary, explain why the fix proposed is the best way to
|
||||
get things done. Don't mangle whitespace, and as is common, don't
|
||||
mis-indent function arguments that span multiple lines. If it is your
|
||||
first patch, mail it to yourself so you can test apply it to an
|
||||
unpatched tree to confirm infrastructure didn't mangle it.
|
||||
|
||||
Finally, go back and read
|
||||
:ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
|
||||
to be sure you are not repeating some common mistake documented there.
|
@ -1,244 +0,0 @@
|
||||
|
||||
Information you need to know about netdev
|
||||
-----------------------------------------
|
||||
|
||||
Q: What is netdev?
|
||||
|
||||
A: It is a mailing list for all network-related Linux stuff. This includes
|
||||
anything found under net/ (i.e. core code like IPv6) and drivers/net
|
||||
(i.e. hardware specific drivers) in the Linux source tree.
|
||||
|
||||
Note that some subsystems (e.g. wireless drivers) which have a high volume
|
||||
of traffic have their own specific mailing lists.
|
||||
|
||||
The netdev list is managed (like many other Linux mailing lists) through
|
||||
VGER ( http://vger.kernel.org/ ) and archives can be found below:
|
||||
|
||||
http://marc.info/?l=linux-netdev
|
||||
http://www.spinics.net/lists/netdev/
|
||||
|
||||
Aside from subsystems like that mentioned above, all network-related Linux
|
||||
development (i.e. RFC, review, comments, etc.) takes place on netdev.
|
||||
|
||||
Q: How do the changes posted to netdev make their way into Linux?
|
||||
|
||||
A: There are always two trees (git repositories) in play. Both are driven
|
||||
by David Miller, the main network maintainer. There is the "net" tree,
|
||||
and the "net-next" tree. As you can probably guess from the names, the
|
||||
net tree is for fixes to existing code already in the mainline tree from
|
||||
Linus, and net-next is where the new code goes for the future release.
|
||||
You can find the trees here:
|
||||
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git
|
||||
|
||||
Q: How often do changes from these trees make it to the mainline Linus tree?
|
||||
|
||||
A: To understand this, you need to know a bit of background information
|
||||
on the cadence of Linux development. Each new release starts off with
|
||||
a two week "merge window" where the main maintainers feed their new
|
||||
stuff to Linus for merging into the mainline tree. After the two weeks,
|
||||
the merge window is closed, and it is called/tagged "-rc1". No new
|
||||
features get mainlined after this -- only fixes to the rc1 content
|
||||
are expected. After roughly a week of collecting fixes to the rc1
|
||||
content, rc2 is released. This repeats on a roughly weekly basis
|
||||
until rc7 (typically; sometimes rc6 if things are quiet, or rc8 if
|
||||
things are in a state of churn), and a week after the last vX.Y-rcN
|
||||
was done, the official "vX.Y" is released.
|
||||
|
||||
Relating that to netdev: At the beginning of the 2-week merge window,
|
||||
the net-next tree will be closed - no new changes/features. The
|
||||
accumulated new content of the past ~10 weeks will be passed onto
|
||||
mainline/Linus via a pull request for vX.Y -- at the same time,
|
||||
the "net" tree will start accumulating fixes for this pulled content
|
||||
relating to vX.Y
|
||||
|
||||
An announcement indicating when net-next has been closed is usually
|
||||
sent to netdev, but knowing the above, you can predict that in advance.
|
||||
|
||||
IMPORTANT: Do not send new net-next content to netdev during the
|
||||
period during which net-next tree is closed.
|
||||
|
||||
Shortly after the two weeks have passed (and vX.Y-rc1 is released), the
|
||||
tree for net-next reopens to collect content for the next (vX.Y+1) release.
|
||||
|
||||
If you aren't subscribed to netdev and/or are simply unsure if net-next
|
||||
has re-opened yet, simply check the net-next git repository link above for
|
||||
any new networking-related commits. You may also check the following
|
||||
website for the current status:
|
||||
|
||||
http://vger.kernel.org/~davem/net-next.html
|
||||
|
||||
The "net" tree continues to collect fixes for the vX.Y content, and
|
||||
is fed back to Linus at regular (~weekly) intervals. Meaning that the
|
||||
focus for "net" is on stabilization and bugfixes.
|
||||
|
||||
Finally, the vX.Y gets released, and the whole cycle starts over.
|
||||
|
||||
Q: So where are we now in this cycle?
|
||||
|
||||
A: Load the mainline (Linus) page here:
|
||||
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
|
||||
|
||||
and note the top of the "tags" section. If it is rc1, it is early
|
||||
in the dev cycle. If it was tagged rc7 a week ago, then a release
|
||||
is probably imminent.
|
||||
|
||||
Q: How do I indicate which tree (net vs. net-next) my patch should be in?
|
||||
|
||||
A: Firstly, think whether you have a bug fix or new "next-like" content.
|
||||
Then once decided, assuming that you use git, use the prefix flag, i.e.
|
||||
|
||||
git format-patch --subject-prefix='PATCH net-next' start..finish
|
||||
|
||||
Use "net" instead of "net-next" (always lower case) in the above for
|
||||
bug-fix net content. If you don't use git, then note the only magic in
|
||||
the above is just the subject text of the outgoing e-mail, and you can
|
||||
manually change it yourself with whatever MUA you are comfortable with.
|
||||
|
||||
Q: I sent a patch and I'm wondering what happened to it. How can I tell
|
||||
whether it got merged?
|
||||
|
||||
A: Start by looking at the main patchworks queue for netdev:
|
||||
|
||||
http://patchwork.ozlabs.org/project/netdev/list/
|
||||
|
||||
The "State" field will tell you exactly where things are at with
|
||||
your patch.
|
||||
|
||||
Q: The above only says "Under Review". How can I find out more?
|
||||
|
||||
A: Generally speaking, the patches get triaged quickly (in less than 48h).
|
||||
So be patient. Asking the maintainer for status updates on your
|
||||
patch is a good way to ensure your patch is ignored or pushed to
|
||||
the bottom of the priority list.
|
||||
|
||||
Q: I submitted multiple versions of the patch series, should I directly update
|
||||
patchwork for the previous versions of these patch series?
|
||||
|
||||
A: No, please don't interfere with the patch status on patchwork, leave it to
|
||||
the maintainer to figure out what is the most recent and current version that
|
||||
should be applied. If there is any doubt, the maintainer will reply and ask
|
||||
what should be done.
|
||||
|
||||
Q: How can I tell what patches are queued up for backporting to the
|
||||
various stable releases?
|
||||
|
||||
A: Normally Greg Kroah-Hartman collects stable commits himself, but
|
||||
for networking, Dave collects up patches he deems critical for the
|
||||
networking subsystem, and then hands them off to Greg.
|
||||
|
||||
There is a patchworks queue that you can see here:
|
||||
http://patchwork.ozlabs.org/bundle/davem/stable/?state=*
|
||||
|
||||
It contains the patches which Dave has selected, but not yet handed
|
||||
off to Greg. If Greg already has the patch, then it will be here:
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/stable/stable-queue.git
|
||||
|
||||
A quick way to find whether the patch is in this stable-queue is
|
||||
to simply clone the repo, and then git grep the mainline commit ID, e.g.
|
||||
|
||||
stable-queue$ git grep -l 284041ef21fdf2e
|
||||
releases/3.0.84/ipv6-fix-possible-crashes-in-ip6_cork_release.patch
|
||||
releases/3.4.51/ipv6-fix-possible-crashes-in-ip6_cork_release.patch
|
||||
releases/3.9.8/ipv6-fix-possible-crashes-in-ip6_cork_release.patch
|
||||
stable/stable-queue$
|
||||
|
||||
Q: I see a network patch and I think it should be backported to stable.
|
||||
Should I request it via "stable@vger.kernel.org" like the references in
|
||||
the kernel's Documentation/process/stable-kernel-rules.rst file say?
|
||||
|
||||
A: No, not for networking. Check the stable queues as per above 1st to see
|
||||
if it is already queued. If not, then send a mail to netdev, listing
|
||||
the upstream commit ID and why you think it should be a stable candidate.
|
||||
|
||||
Before you jump to go do the above, do note that the normal stable rules
|
||||
in Documentation/process/stable-kernel-rules.rst still apply. So you need to
|
||||
explicitly indicate why it is a critical fix and exactly what users are
|
||||
impacted. In addition, you need to convince yourself that you _really_
|
||||
think it has been overlooked, vs. having been considered and rejected.
|
||||
|
||||
Generally speaking, the longer it has had a chance to "soak" in mainline,
|
||||
the better the odds that it is an OK candidate for stable. So scrambling
|
||||
to request a commit be added the day after it appears should be avoided.
|
||||
|
||||
Q: I have created a network patch and I think it should be backported to
|
||||
stable. Should I add a "Cc: stable@vger.kernel.org" like the references
|
||||
in the kernel's Documentation/ directory say?
|
||||
|
||||
A: No. See above answer. In short, if you think it really belongs in
|
||||
stable, then ensure you write a decent commit log that describes who
|
||||
gets impacted by the bugfix and how it manifests itself, and when the
|
||||
bug was introduced. If you do that properly, then the commit will
|
||||
get handled appropriately and most likely get put in the patchworks
|
||||
stable queue if it really warrants it.
|
||||
|
||||
If you think there is some valid information relating to it being in
|
||||
stable that does _not_ belong in the commit log, then use the three
|
||||
dash marker line as described in Documentation/process/submitting-patches.rst to
|
||||
temporarily embed that information into the patch that you send.
|
||||
|
||||
Q: Are all networking bug fixes backported to all stable releases?
|
||||
|
||||
A: Due to capacity, Dave could only take care of the backports for the last
|
||||
2 stable releases. For earlier stable releases, each stable branch maintainer
|
||||
is supposed to take care of them. If you find any patch is missing from an
|
||||
earlier stable branch, please notify stable@vger.kernel.org with either a
|
||||
commit ID or a formal patch backported, and CC Dave and other relevant
|
||||
networking developers.
|
||||
|
||||
Q: Someone said that the comment style and coding convention is different
|
||||
for the networking content. Is this true?
|
||||
|
||||
A: Yes, in a largely trivial way. Instead of this:
|
||||
|
||||
/*
|
||||
* foobar blah blah blah
|
||||
* another line of text
|
||||
*/
|
||||
|
||||
it is requested that you make it look like this:
|
||||
|
||||
/* foobar blah blah blah
|
||||
* another line of text
|
||||
*/
|
||||
|
||||
Q: I am working in existing code that has the former comment style and not the
|
||||
latter. Should I submit new code in the former style or the latter?
|
||||
|
||||
A: Make it the latter style, so that eventually all code in the domain of
|
||||
netdev is of this format.
|
||||
|
||||
Q: I found a bug that might have possible security implications or similar.
|
||||
Should I mail the main netdev maintainer off-list?
|
||||
|
||||
A: No. The current netdev maintainer has consistently requested that people
|
||||
use the mailing lists and not reach out directly. If you aren't OK with
|
||||
that, then perhaps consider mailing "security@kernel.org" or reading about
|
||||
http://oss-security.openwall.org/wiki/mailing-lists/distros
|
||||
as possible alternative mechanisms.
|
||||
|
||||
Q: What level of testing is expected before I submit my change?
|
||||
|
||||
A: If your changes are against net-next, the expectation is that you
|
||||
have tested by layering your changes on top of net-next. Ideally you
|
||||
will have done run-time testing specific to your change, but at a
|
||||
minimum, your changes should survive an "allyesconfig" and an
|
||||
"allmodconfig" build without new warnings or failures.
|
||||
|
||||
Q: Any other tips to help ensure my net/net-next patch gets OK'd?
|
||||
|
||||
A: Attention to detail. Re-read your own work as if you were the
|
||||
reviewer. You can start with using checkpatch.pl, perhaps even
|
||||
with the "--strict" flag. But do not be mindlessly robotic in
|
||||
doing so. If your change is a bug-fix, make sure your commit log
|
||||
indicates the end-user visible symptom, the underlying reason as
|
||||
to why it happens, and then if necessary, explain why the fix proposed
|
||||
is the best way to get things done. Don't mangle whitespace, and as
|
||||
is common, don't mis-indent function arguments that span multiple lines.
|
||||
If it is your first patch, mail it to yourself so you can test apply
|
||||
it to an unpatched tree to confirm infrastructure didn't mangle it.
|
||||
|
||||
Finally, go back and read Documentation/process/submitting-patches.rst to be
|
||||
sure you are not repeating some common mistake documented there.
|
@ -366,8 +366,13 @@ XPS: Transmit Packet Steering
|
||||
|
||||
Transmit Packet Steering is a mechanism for intelligently selecting
|
||||
which transmit queue to use when transmitting a packet on a multi-queue
|
||||
device. To accomplish this, a mapping from CPU to hardware queue(s) is
|
||||
recorded. The goal of this mapping is usually to assign queues
|
||||
device. This can be accomplished by recording two kinds of maps, either
|
||||
a mapping of CPU to hardware queue(s) or a mapping of receive queue(s)
|
||||
to hardware transmit queue(s).
|
||||
|
||||
1. XPS using CPUs map
|
||||
|
||||
The goal of this mapping is usually to assign queues
|
||||
exclusively to a subset of CPUs, where the transmit completions for
|
||||
these queues are processed on a CPU within this set. This choice
|
||||
provides two benefits. First, contention on the device queue lock is
|
||||
@ -377,15 +382,40 @@ transmit queue). Secondly, cache miss rate on transmit completion is
|
||||
reduced, in particular for data cache lines that hold the sk_buff
|
||||
structures.
|
||||
|
||||
XPS is configured per transmit queue by setting a bitmap of CPUs that
|
||||
may use that queue to transmit. The reverse mapping, from CPUs to
|
||||
transmit queues, is computed and maintained for each network device.
|
||||
When transmitting the first packet in a flow, the function
|
||||
get_xps_queue() is called to select a queue. This function uses the ID
|
||||
of the running CPU as a key into the CPU-to-queue lookup table. If the
|
||||
2. XPS using receive queues map
|
||||
|
||||
This mapping is used to pick transmit queue based on the receive
|
||||
queue(s) map configuration set by the administrator. A set of receive
|
||||
queues can be mapped to a set of transmit queues (many:many), although
|
||||
the common use case is a 1:1 mapping. This will enable sending packets
|
||||
on the same queue associations for transmit and receive. This is useful for
|
||||
busy polling multi-threaded workloads where there are challenges in
|
||||
associating a given CPU to a given application thread. The application
|
||||
threads are not pinned to CPUs and each thread handles packets
|
||||
received on a single queue. The receive queue number is cached in the
|
||||
socket for the connection. In this model, sending the packets on the same
|
||||
transmit queue corresponding to the associated receive queue has benefits
|
||||
in keeping the CPU overhead low. Transmit completion work is locked into
|
||||
the same queue-association that a given application is polling on. This
|
||||
avoids the overhead of triggering an interrupt on another CPU. When the
|
||||
application cleans up the packets during the busy poll, transmit completion
|
||||
may be processed along with it in the same thread context and so result in
|
||||
reduced latency.
|
||||
|
||||
XPS is configured per transmit queue by setting a bitmap of
|
||||
CPUs/receive-queues that may use that queue to transmit. The reverse
|
||||
mapping, from CPUs to transmit queues or from receive-queues to transmit
|
||||
queues, is computed and maintained for each network device. When
|
||||
transmitting the first packet in a flow, the function get_xps_queue() is
|
||||
called to select a queue. This function uses the ID of the receive queue
|
||||
for the socket connection for a match in the receive queue-to-transmit queue
|
||||
lookup table. Alternatively, this function can also use the ID of the
|
||||
running CPU as a key into the CPU-to-queue lookup table. If the
|
||||
ID matches a single queue, that is used for transmission. If multiple
|
||||
queues match, one is selected by using the flow hash to compute an index
|
||||
into the set.
|
||||
into the set. When selecting the transmit queue based on receive queue(s)
|
||||
map, the transmit device is not validated against the receive device as it
|
||||
requires expensive lookup operation in the datapath.
|
||||
|
||||
The queue chosen for transmitting a particular flow is saved in the
|
||||
corresponding socket structure for the flow (e.g. a TCP connection).
|
||||
@ -404,11 +434,15 @@ acknowledged.
|
||||
|
||||
XPS is only available if the kconfig symbol CONFIG_XPS is enabled (on by
|
||||
default for SMP). The functionality remains disabled until explicitly
|
||||
configured. To enable XPS, the bitmap of CPUs that may use a transmit
|
||||
queue is configured using the sysfs file entry:
|
||||
configured. To enable XPS, the bitmap of CPUs/receive-queues that may
|
||||
use a transmit queue is configured using the sysfs file entry:
|
||||
|
||||
For selection based on CPUs map:
|
||||
/sys/class/net/<dev>/queues/tx-<n>/xps_cpus
|
||||
|
||||
For selection based on receive-queues map:
|
||||
/sys/class/net/<dev>/queues/tx-<n>/xps_rxqs
|
||||
|
||||
== Suggested Configuration
|
||||
|
||||
For a network device with a single transmission queue, XPS configuration
|
||||
@ -421,6 +455,11 @@ best CPUs to share a given queue are probably those that share the cache
|
||||
with the CPU that processes transmit completions for that queue
|
||||
(transmit interrupts).
|
||||
|
||||
For transmit queue selection based on receive queue(s), XPS has to be
|
||||
explicitly configured mapping receive-queue(s) to transmit queue(s). If the
|
||||
user configuration for receive-queue map does not apply, then the transmit
|
||||
queue is selected based on the CPUs map.
|
||||
|
||||
Per TX Queue rate limitation:
|
||||
=============================
|
||||
|
||||
|
540
Documentation/networking/ti-cpsw.txt
Normal file
540
Documentation/networking/ti-cpsw.txt
Normal file
@ -0,0 +1,540 @@
|
||||
* Texas Instruments CPSW ethernet driver
|
||||
|
||||
Multiqueue & CBS & MQPRIO
|
||||
=====================================================================
|
||||
=====================================================================
|
||||
|
||||
The cpsw has 3 CBS shapers for each external ports. This document
|
||||
describes MQPRIO and CBS Qdisc offload configuration for cpsw driver
|
||||
based on examples. It potentially can be used in audio video bridging
|
||||
(AVB) and time sensitive networking (TSN).
|
||||
|
||||
The following examples were tested on AM572x EVM and BBB boards.
|
||||
|
||||
Test setup
|
||||
==========
|
||||
|
||||
Under consideration two examples with AM572x EVM running cpsw driver
|
||||
in dual_emac mode.
|
||||
|
||||
Several prerequisites:
|
||||
- TX queues must be rated starting from txq0 that has highest priority
|
||||
- Traffic classes are used starting from 0, that has highest priority
|
||||
- CBS shapers should be used with rated queues
|
||||
- The bandwidth for CBS shapers has to be set a little bit more then
|
||||
potential incoming rate, thus, rate of all incoming tx queues has
|
||||
to be a little less
|
||||
- Real rates can differ, due to discreetness
|
||||
- Map skb-priority to txq is not enough, also skb-priority to l2 prio
|
||||
map has to be created with ip or vconfig tool
|
||||
- Any l2/socket prio (0 - 7) for classes can be used, but for
|
||||
simplicity default values are used: 3 and 2
|
||||
- only 2 classes tested: A and B, but checked and can work with more,
|
||||
maximum allowed 4, but only for 3 rate can be set.
|
||||
|
||||
Test setup for examples
|
||||
=======================
|
||||
+-------------------------------+
|
||||
|--+ |
|
||||
| | Workstation0 |
|
||||
|E | MAC 18:03:73:66:87:42 |
|
||||
+-----------------------------+ +--|t | |
|
||||
| | 1 | E | | |h |./tsn_listener -d \ |
|
||||
| Target board: | 0 | t |--+ |0 | 18:03:73:66:87:42 -i eth0 \|
|
||||
| AM572x EVM | 0 | h | | | -s 1500 |
|
||||
| | 0 | 0 | |--+ |
|
||||
| Only 2 classes: |Mb +---| +-------------------------------+
|
||||
| class A, class B | |
|
||||
| | +---| +-------------------------------+
|
||||
| | 1 | E | |--+ |
|
||||
| | 0 | t | | | Workstation1 |
|
||||
| | 0 | h |--+ |E | MAC 20:cf:30:85:7d:fd |
|
||||
| |Mb | 1 | +--|t | |
|
||||
+-----------------------------+ |h |./tsn_listener -d \ |
|
||||
|0 | 20:cf:30:85:7d:fd -i eth0 \|
|
||||
| | -s 1500 |
|
||||
|--+ |
|
||||
+-------------------------------+
|
||||
|
||||
*********************************************************************
|
||||
*********************************************************************
|
||||
*********************************************************************
|
||||
Example 1: One port tx AVB configuration scheme for target board
|
||||
----------------------------------------------------------------------
|
||||
(prints and scheme for AM572x evm, applicable for single port boards)
|
||||
|
||||
tc - traffic class
|
||||
txq - transmit queue
|
||||
p - priority
|
||||
f - fifo (cpsw fifo)
|
||||
S - shaper configured
|
||||
|
||||
+------------------------------------------------------------------+ u
|
||||
| +---------------+ +---------------+ +------+ +------+ | s
|
||||
| | | | | | | | | | e
|
||||
| | App 1 | | App 2 | | Apps | | Apps | | r
|
||||
| | Class A | | Class B | | Rest | | Rest | |
|
||||
| | Eth0 | | Eth0 | | Eth0 | | Eth1 | | s
|
||||
| | VLAN100 | | VLAN100 | | | | | | | | p
|
||||
| | 40 Mb/s | | 20 Mb/s | | | | | | | | a
|
||||
| | SO_PRIORITY=3 | | SO_PRIORITY=2 | | | | | | | | c
|
||||
| | | | | | | | | | | | | | e
|
||||
| +---|-----------+ +---|-----------+ +---|--+ +---|--+ |
|
||||
+-----|------------------|------------------|--------|-------------+
|
||||
+-+ +------------+ | |
|
||||
| | +-----------------+ +--+
|
||||
| | | |
|
||||
+---|-------|-------------|-----------------------|----------------+
|
||||
| +----+ +----+ +----+ +----+ +----+ |
|
||||
| | p3 | | p2 | | p1 | | p0 | | p0 | | k
|
||||
| \ / \ / \ / \ / \ / | e
|
||||
| \ / \ / \ / \ / \ / | r
|
||||
| \/ \/ \/ \/ \/ | n
|
||||
| | | | | | e
|
||||
| | | +-----+ | | l
|
||||
| | | | | |
|
||||
| +----+ +----+ +----+ +----+ | s
|
||||
| |tc0 | |tc1 | |tc2 | |tc0 | | p
|
||||
| \ / \ / \ / \ / | a
|
||||
| \ / \ / \ / \ / | c
|
||||
| \/ \/ \/ \/ | e
|
||||
| | | +-----+ | |
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
| +----+ +----+ +----+ +----+ +----+ |
|
||||
| |txq0| |txq1| |txq2| |txq3| |txq4| |
|
||||
| \ / \ / \ / \ / \ / |
|
||||
| \ / \ / \ / \ / \ / |
|
||||
| \/ \/ \/ \/ \/ |
|
||||
| +-|------|------|------|--+ +--|--------------+ |
|
||||
| | | | | | | Eth0.100 | | Eth1 | |
|
||||
+---|------|------|------|------------------------|----------------+
|
||||
| | | | |
|
||||
p p p p |
|
||||
3 2 0-1, 4-7 <- L2 priority |
|
||||
| | | | |
|
||||
| | | | |
|
||||
+---|------|------|------|------------------------|----------------+
|
||||
| | | | | |----------+ |
|
||||
| +----+ +----+ +----+ +----+ +----+ |
|
||||
| |dma7| |dma6| |dma5| |dma4| |dma3| |
|
||||
| \ / \ / \ / \ / \ / | c
|
||||
| \S / \S / \ / \ / \ / | p
|
||||
| \/ \/ \/ \/ \/ | s
|
||||
| | | | +----- | | w
|
||||
| | | | | | |
|
||||
| | | | | | | d
|
||||
| +----+ +----+ +----+p p+----+ | r
|
||||
| | | | | | |o o| | | i
|
||||
| | f3 | | f2 | | f0 |r r| f0 | | v
|
||||
| |tc0 | |tc1 | |tc2 |t t|tc0 | | e
|
||||
| \CBS / \CBS / \CBS /1 2\CBS / | r
|
||||
| \S / \S / \ / \ / |
|
||||
| \/ \/ \/ \/ |
|
||||
+------------------------------------------------------------------+
|
||||
========================================Eth==========================>
|
||||
|
||||
1)
|
||||
// Add 4 tx queues, for interface Eth0, and 1 tx queue for Eth1
|
||||
$ ethtool -L eth0 rx 1 tx 5
|
||||
rx unmodified, ignoring
|
||||
|
||||
2)
|
||||
// Check if num of queues is set correctly:
|
||||
$ ethtool -l eth0
|
||||
Channel parameters for eth0:
|
||||
Pre-set maximums:
|
||||
RX: 8
|
||||
TX: 8
|
||||
Other: 0
|
||||
Combined: 0
|
||||
Current hardware settings:
|
||||
RX: 1
|
||||
TX: 5
|
||||
Other: 0
|
||||
Combined: 0
|
||||
|
||||
3)
|
||||
// TX queues must be rated starting from 0, so set bws for tx0 and tx1
|
||||
// Set rates 40 and 20 Mb/s appropriately.
|
||||
// Pay attention, real speed can differ a bit due to discreetness.
|
||||
// Leave last 2 tx queues not rated.
|
||||
$ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
|
||||
$ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
|
||||
|
||||
4)
|
||||
// Check maximum rate of tx (cpdma) queues:
|
||||
$ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
|
||||
40
|
||||
20
|
||||
0
|
||||
0
|
||||
0
|
||||
|
||||
5)
|
||||
// Map skb->priority to traffic class:
|
||||
// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
|
||||
// Map traffic class to transmit queue:
|
||||
// tc0 -> txq0, tc1 -> txq1, tc2 -> (txq2, txq3)
|
||||
$ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
|
||||
map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@2 hw 1
|
||||
|
||||
5a)
|
||||
// As two interface sharing same set of tx queues, assign all traffic
|
||||
// coming to interface Eth1 to separate queue in order to not mix it
|
||||
// with traffic from interface Eth0, so use separate txq to send
|
||||
// packets to Eth1, so all prio -> tc0 and tc0 -> txq4
|
||||
// Here hw 0, so here still default configuration for eth1 in hw
|
||||
$ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 1 \
|
||||
map 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 queues 1@4 hw 0
|
||||
|
||||
6)
|
||||
// Check classes settings
|
||||
$ tc -g class show dev eth0
|
||||
+---(100:ffe2) mqprio
|
||||
| +---(100:3) mqprio
|
||||
| +---(100:4) mqprio
|
||||
|
|
||||
+---(100:ffe1) mqprio
|
||||
| +---(100:2) mqprio
|
||||
|
|
||||
+---(100:ffe0) mqprio
|
||||
+---(100:1) mqprio
|
||||
|
||||
$ tc -g class show dev eth1
|
||||
+---(100:ffe0) mqprio
|
||||
+---(100:5) mqprio
|
||||
|
||||
7)
|
||||
// Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc
|
||||
// Set it +1 Mb for reserve (important!)
|
||||
// here only idle slope is important, others arg are ignored
|
||||
// Pay attention, real speed can differ a bit due to discreetness
|
||||
$ tc qdisc add dev eth0 parent 100:1 cbs locredit -1438 \
|
||||
hicredit 62 sendslope -959000 idleslope 41000 offload 1
|
||||
net eth0: set FIFO3 bw = 50
|
||||
|
||||
8)
|
||||
// Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc:
|
||||
// Set it +1 Mb for reserve (important!)
|
||||
$ tc qdisc add dev eth0 parent 100:2 cbs locredit -1468 \
|
||||
hicredit 65 sendslope -979000 idleslope 21000 offload 1
|
||||
net eth0: set FIFO2 bw = 30
|
||||
|
||||
9)
|
||||
// Create vlan 100 to map sk->priority to vlan qos
|
||||
$ ip link add link eth0 name eth0.100 type vlan id 100
|
||||
8021q: 802.1Q VLAN Support v1.8
|
||||
8021q: adding VLAN 0 to HW filter on device eth0
|
||||
8021q: adding VLAN 0 to HW filter on device eth1
|
||||
net eth0: Adding vlanid 100 to vlan filter
|
||||
|
||||
10)
|
||||
// Map skb->priority to L2 prio, 1 to 1
|
||||
$ ip link set eth0.100 type vlan \
|
||||
egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||
|
||||
11)
|
||||
// Check egress map for vlan 100
|
||||
$ cat /proc/net/vlan/eth0.100
|
||||
[...]
|
||||
INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0
|
||||
EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||
|
||||
12)
|
||||
// Run your appropriate tools with socket option "SO_PRIORITY"
|
||||
// to 3 for class A and/or to 2 for class B
|
||||
// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
|
||||
./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
|
||||
./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
|
||||
|
||||
13)
|
||||
// run your listener on workstation (should be in same vlan)
|
||||
// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
|
||||
./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39000 kbps
|
||||
|
||||
14)
|
||||
// Restore default configuration if needed
|
||||
$ ip link del eth0.100
|
||||
$ tc qdisc del dev eth1 root
|
||||
$ tc qdisc del dev eth0 root
|
||||
net eth0: Prev FIFO2 is shaped
|
||||
net eth0: set FIFO3 bw = 0
|
||||
net eth0: set FIFO2 bw = 0
|
||||
$ ethtool -L eth0 rx 1 tx 1
|
||||
|
||||
*********************************************************************
|
||||
*********************************************************************
|
||||
*********************************************************************
|
||||
Example 2: Two port tx AVB configuration scheme for target board
|
||||
----------------------------------------------------------------------
|
||||
(prints and scheme for AM572x evm, for dual emac boards only)
|
||||
|
||||
+------------------------------------------------------------------+ u
|
||||
| +----------+ +----------+ +------+ +----------+ +----------+ | s
|
||||
| | | | | | | | | | | | e
|
||||
| | App 1 | | App 2 | | Apps | | App 3 | | App 4 | | r
|
||||
| | Class A | | Class B | | Rest | | Class B | | Class A | |
|
||||
| | Eth0 | | Eth0 | | | | | Eth1 | | Eth1 | | s
|
||||
| | VLAN100 | | VLAN100 | | | | | VLAN100 | | VLAN100 | | p
|
||||
| | 40 Mb/s | | 20 Mb/s | | | | | 10 Mb/s | | 30 Mb/s | | a
|
||||
| | SO_PRI=3 | | SO_PRI=2 | | | | | SO_PRI=3 | | SO_PRI=2 | | c
|
||||
| | | | | | | | | | | | | | | | | e
|
||||
| +---|------+ +---|------+ +---|--+ +---|------+ +---|------+ |
|
||||
+-----|-------------|-------------|---------|-------------|--------+
|
||||
+-+ +-------+ | +----------+ +----+
|
||||
| | +-------+------+ | |
|
||||
| | | | | |
|
||||
+---|-------|-------------|--------------|-------------|-------|---+
|
||||
| +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+ |
|
||||
| | p3 | | p2 | | p1 | | p0 | | p0 | | p1 | | p2 | | p3 | | k
|
||||
| \ / \ / \ / \ / \ / \ / \ / \ / | e
|
||||
| \ / \ / \ / \ / \ / \ / \ / \ / | r
|
||||
| \/ \/ \/ \/ \/ \/ \/ \/ | n
|
||||
| | | | | | | | e
|
||||
| | | +----+ +----+ | | | l
|
||||
| | | | | | | |
|
||||
| +----+ +----+ +----+ +----+ +----+ +----+ | s
|
||||
| |tc0 | |tc1 | |tc2 | |tc2 | |tc1 | |tc0 | | p
|
||||
| \ / \ / \ / \ / \ / \ / | a
|
||||
| \ / \ / \ / \ / \ / \ / | c
|
||||
| \/ \/ \/ \/ \/ \/ | e
|
||||
| | | +-----+ +-----+ | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | E E | | | | |
|
||||
| +----+ +----+ +----+ +----+ t t +----+ +----+ +----+ +----+ |
|
||||
| |txq0| |txq1| |txq4| |txq5| h h |txq6| |txq7| |txq3| |txq2| |
|
||||
| \ / \ / \ / \ / 0 1 \ / \ / \ / \ / |
|
||||
| \ / \ / \ / \ / . . \ / \ / \ / \ / |
|
||||
| \/ \/ \/ \/ 1 1 \/ \/ \/ \/ |
|
||||
| +-|------|------|------|--+ 0 0 +-|------|------|------|--+ |
|
||||
| | | | | | | 0 0 | | | | | | |
|
||||
+---|------|------|------|---------------|------|------|------|----+
|
||||
| | | | | | | |
|
||||
p p p p p p p p
|
||||
3 2 0-1, 4-7 <-L2 pri-> 0-1, 4-7 2 3
|
||||
| | | | | | | |
|
||||
| | | | | | | |
|
||||
+---|------|------|------|---------------|------|------|------|----+
|
||||
| | | | | | | | | |
|
||||
| +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+ |
|
||||
| |dma7| |dma6| |dma3| |dma2| |dma1| |dma0| |dma4| |dma5| |
|
||||
| \ / \ / \ / \ / \ / \ / \ / \ / | c
|
||||
| \S / \S / \ / \ / \ / \ / \S / \S / | p
|
||||
| \/ \/ \/ \/ \/ \/ \/ \/ | s
|
||||
| | | | +----- | | | | | w
|
||||
| | | | | +----+ | | | |
|
||||
| | | | | | | | | | d
|
||||
| +----+ +----+ +----+p p+----+ +----+ +----+ | r
|
||||
| | | | | | |o o| | | | | | | i
|
||||
| | f3 | | f2 | | f0 |r CPSW r| f3 | | f2 | | f0 | | v
|
||||
| |tc0 | |tc1 | |tc2 |t t|tc0 | |tc1 | |tc2 | | e
|
||||
| \CBS / \CBS / \CBS /1 2\CBS / \CBS / \CBS / | r
|
||||
| \S / \S / \ / \S / \S / \ / |
|
||||
| \/ \/ \/ \/ \/ \/ |
|
||||
+------------------------------------------------------------------+
|
||||
========================================Eth==========================>
|
||||
|
||||
1)
|
||||
// Add 8 tx queues, for interface Eth0, but they are common, so are accessed
|
||||
// by two interfaces Eth0 and Eth1.
|
||||
$ ethtool -L eth1 rx 1 tx 8
|
||||
rx unmodified, ignoring
|
||||
|
||||
2)
|
||||
// Check if num of queues is set correctly:
|
||||
$ ethtool -l eth0
|
||||
Channel parameters for eth0:
|
||||
Pre-set maximums:
|
||||
RX: 8
|
||||
TX: 8
|
||||
Other: 0
|
||||
Combined: 0
|
||||
Current hardware settings:
|
||||
RX: 1
|
||||
TX: 8
|
||||
Other: 0
|
||||
Combined: 0
|
||||
|
||||
3)
|
||||
// TX queues must be rated starting from 0, so set bws for tx0 and tx1 for Eth0
|
||||
// and for tx2 and tx3 for Eth1. That is, rates 40 and 20 Mb/s appropriately
|
||||
// for Eth0 and 30 and 10 Mb/s for Eth1.
|
||||
// Real speed can differ a bit due to discreetness
|
||||
// Leave last 4 tx queues as not rated
|
||||
$ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
|
||||
$ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
|
||||
$ echo 30 > /sys/class/net/eth1/queues/tx-2/tx_maxrate
|
||||
$ echo 10 > /sys/class/net/eth1/queues/tx-3/tx_maxrate
|
||||
|
||||
4)
|
||||
// Check maximum rate of tx (cpdma) queues:
|
||||
$ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
|
||||
40
|
||||
20
|
||||
30
|
||||
10
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
|
||||
5)
|
||||
// Map skb->priority to traffic class for Eth0:
|
||||
// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
|
||||
// Map traffic class to transmit queue:
|
||||
// tc0 -> txq0, tc1 -> txq1, tc2 -> (txq4, txq5)
|
||||
$ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
|
||||
map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@4 hw 1
|
||||
|
||||
6)
|
||||
// Check classes settings
|
||||
$ tc -g class show dev eth0
|
||||
+---(100:ffe2) mqprio
|
||||
| +---(100:5) mqprio
|
||||
| +---(100:6) mqprio
|
||||
|
|
||||
+---(100:ffe1) mqprio
|
||||
| +---(100:2) mqprio
|
||||
|
|
||||
+---(100:ffe0) mqprio
|
||||
+---(100:1) mqprio
|
||||
|
||||
7)
|
||||
// Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc for Eth0
|
||||
// here only idle slope is important, others ignored
|
||||
// Real speed can differ a bit due to discreetness
|
||||
$ tc qdisc add dev eth0 parent 100:1 cbs locredit -1470 \
|
||||
hicredit 62 sendslope -959000 idleslope 41000 offload 1
|
||||
net eth0: set FIFO3 bw = 50
|
||||
|
||||
8)
|
||||
// Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc for Eth0
|
||||
$ tc qdisc add dev eth0 parent 100:2 cbs locredit -1470 \
|
||||
hicredit 65 sendslope -979000 idleslope 21000 offload 1
|
||||
net eth0: set FIFO2 bw = 30
|
||||
|
||||
9)
|
||||
// Create vlan 100 to map sk->priority to vlan qos for Eth0
|
||||
$ ip link add link eth0 name eth0.100 type vlan id 100
|
||||
net eth0: Adding vlanid 100 to vlan filter
|
||||
|
||||
10)
|
||||
// Map skb->priority to L2 prio for Eth0.100, one to one
|
||||
$ ip link set eth0.100 type vlan \
|
||||
egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||
|
||||
11)
|
||||
// Check egress map for vlan 100
|
||||
$ cat /proc/net/vlan/eth0.100
|
||||
[...]
|
||||
INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0
|
||||
EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||
|
||||
12)
|
||||
// Map skb->priority to traffic class for Eth1:
|
||||
// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
|
||||
// Map traffic class to transmit queue:
|
||||
// tc0 -> txq2, tc1 -> txq3, tc2 -> (txq6, txq7)
|
||||
$ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 3 \
|
||||
map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@2 1@3 2@6 hw 1
|
||||
|
||||
13)
|
||||
// Check classes settings
|
||||
$ tc -g class show dev eth1
|
||||
+---(100:ffe2) mqprio
|
||||
| +---(100:7) mqprio
|
||||
| +---(100:8) mqprio
|
||||
|
|
||||
+---(100:ffe1) mqprio
|
||||
| +---(100:4) mqprio
|
||||
|
|
||||
+---(100:ffe0) mqprio
|
||||
+---(100:3) mqprio
|
||||
|
||||
14)
|
||||
// Set rate for class A - 31 Mbit (tc0, txq2) using CBS Qdisc for Eth1
|
||||
// here only idle slope is important, others ignored
|
||||
// Set it +1 Mb for reserve (important!)
|
||||
$ tc qdisc add dev eth1 parent 100:3 cbs locredit -1453 \
|
||||
hicredit 47 sendslope -969000 idleslope 31000 offload 1
|
||||
net eth1: set FIFO3 bw = 31
|
||||
|
||||
15)
|
||||
// Set rate for class B - 11 Mbit (tc1, txq3) using CBS Qdisc for Eth1
|
||||
// Set it +1 Mb for reserve (important!)
|
||||
$ tc qdisc add dev eth1 parent 100:4 cbs locredit -1483 \
|
||||
hicredit 34 sendslope -989000 idleslope 11000 offload 1
|
||||
net eth1: set FIFO2 bw = 11
|
||||
|
||||
16)
|
||||
// Create vlan 100 to map sk->priority to vlan qos for Eth1
|
||||
$ ip link add link eth1 name eth1.100 type vlan id 100
|
||||
net eth1: Adding vlanid 100 to vlan filter
|
||||
|
||||
17)
|
||||
// Map skb->priority to L2 prio for Eth1.100, one to one
|
||||
$ ip link set eth1.100 type vlan \
|
||||
egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||
|
||||
18)
|
||||
// Check egress map for vlan 100
|
||||
$ cat /proc/net/vlan/eth1.100
|
||||
[...]
|
||||
INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0
|
||||
EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
|
||||
|
||||
19)
|
||||
// Run appropriate tools with socket option "SO_PRIORITY" to 3
|
||||
// for class A and to 2 for class B. For both interfaces
|
||||
./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
|
||||
./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
|
||||
./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p2 -s 1500&
|
||||
./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p3 -s 1500&
|
||||
|
||||
20)
|
||||
// run your listener on workstation (should be in same vlan)
|
||||
// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
|
||||
./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39012 kbps
|
||||
Receiving data rate: 39000 kbps
|
||||
|
||||
21)
|
||||
// Restore default configuration if needed
|
||||
$ ip link del eth1.100
|
||||
$ ip link del eth0.100
|
||||
$ tc qdisc del dev eth1 root
|
||||
net eth1: Prev FIFO2 is shaped
|
||||
net eth1: set FIFO3 bw = 0
|
||||
net eth1: set FIFO2 bw = 0
|
||||
$ tc qdisc del dev eth0 root
|
||||
net eth0: Prev FIFO2 is shaped
|
||||
net eth0: set FIFO3 bw = 0
|
||||
net eth0: set FIFO2 bw = 0
|
||||
$ ethtool -L eth0 rx 1 tx 1
|
@ -37,7 +37,7 @@ Procedure for submitting patches to the -stable tree
|
||||
|
||||
- If the patch covers files in net/ or drivers/net please follow netdev stable
|
||||
submission guidelines as described in
|
||||
Documentation/networking/netdev-FAQ.txt
|
||||
:ref:`Documentation/networking/netdev-FAQ.rst <netdev-FAQ>`
|
||||
- Security patches should not be handled (solely) by the -stable review
|
||||
process but should follow the procedures in
|
||||
:ref:`Documentation/admin-guide/security-bugs.rst <securitybugs>`.
|
||||
|
@ -611,6 +611,7 @@ which stable kernel versions should receive your fix. This is the preferred
|
||||
method for indicating a bug fixed by the patch. See :ref:`describe_changes`
|
||||
for more details.
|
||||
|
||||
.. _the_canonical_patch_format:
|
||||
|
||||
14) The canonical patch format
|
||||
------------------------------
|
||||
|
@ -9,7 +9,7 @@ rfkill - RF kill switch support
|
||||
Introduction
|
||||
============
|
||||
|
||||
The rfkill subsystem provides a generic interface to disabling any radio
|
||||
The rfkill subsystem provides a generic interface for disabling any radio
|
||||
transmitter in the system. When a transmitter is blocked, it shall not
|
||||
radiate any power.
|
||||
|
||||
@ -45,7 +45,7 @@ The rfkill subsystem is composed of three main components:
|
||||
* the rfkill drivers.
|
||||
|
||||
The rfkill core provides API for kernel drivers to register their radio
|
||||
transmitter with the kernel, methods for turning it on and off and, letting
|
||||
transmitter with the kernel, methods for turning it on and off, and letting
|
||||
the system know about hardware-disabled states that may be implemented on
|
||||
the device.
|
||||
|
||||
@ -54,7 +54,7 @@ ways for userspace to query the current states. See the "Userspace support"
|
||||
section below.
|
||||
|
||||
When the device is hard-blocked (either by a call to rfkill_set_hw_state()
|
||||
or from query_hw_block) set_block() will be invoked for additional software
|
||||
or from query_hw_block), set_block() will be invoked for additional software
|
||||
block, but drivers can ignore the method call since they can use the return
|
||||
value of the function rfkill_set_hw_state() to sync the software state
|
||||
instead of keeping track of calls to set_block(). In fact, drivers should
|
||||
@ -65,7 +65,6 @@ keeps track of soft and hard block separately.
|
||||
Kernel API
|
||||
==========
|
||||
|
||||
|
||||
Drivers for radio transmitters normally implement an rfkill driver.
|
||||
|
||||
Platform drivers might implement input devices if the rfkill button is just
|
||||
@ -75,14 +74,14 @@ a way to turn on/off the transmitter(s).
|
||||
|
||||
For some platforms, it is possible that the hardware state changes during
|
||||
suspend/hibernation, in which case it will be necessary to update the rfkill
|
||||
core with the current state is at resume time.
|
||||
core with the current state at resume time.
|
||||
|
||||
To create an rfkill driver, driver's Kconfig needs to have::
|
||||
|
||||
depends on RFKILL || !RFKILL
|
||||
|
||||
to ensure the driver cannot be built-in when rfkill is modular. The !RFKILL
|
||||
case allows the driver to be built when rfkill is not configured, which
|
||||
case allows the driver to be built when rfkill is not configured, in which
|
||||
case all rfkill API can still be used but will be provided by static inlines
|
||||
which compile to almost nothing.
|
||||
|
||||
@ -91,7 +90,7 @@ rfkill drivers that control devices that can be hard-blocked unless they also
|
||||
assign the poll_hw_block() callback (then the rfkill core will poll the
|
||||
device). Don't do this unless you cannot get the event in any other way.
|
||||
|
||||
RFKill provides per-switch LED triggers, which can be used to drive LEDs
|
||||
rfkill provides per-switch LED triggers, which can be used to drive LEDs
|
||||
according to the switch state (LED_FULL when blocked, LED_OFF otherwise).
|
||||
|
||||
|
||||
@ -114,7 +113,7 @@ a specified type) into a state which also updates the default state for
|
||||
hotplugged devices.
|
||||
|
||||
After an application opens /dev/rfkill, it can read the current state of all
|
||||
devices. Changes can be either obtained by either polling the descriptor for
|
||||
devices. Changes can be obtained by either polling the descriptor for
|
||||
hotplug or state change events or by listening for uevents emitted by the
|
||||
rfkill core framework.
|
||||
|
||||
@ -127,8 +126,7 @@ environment variables set::
|
||||
RFKILL_STATE
|
||||
RFKILL_TYPE
|
||||
|
||||
The contents of these variables corresponds to the "name", "state" and
|
||||
The content of these variables corresponds to the "name", "state" and
|
||||
"type" sysfs files explained above.
|
||||
|
||||
|
||||
For further details consult Documentation/ABI/stable/sysfs-class-rfkill.
|
||||
|
20
MAINTAINERS
20
MAINTAINERS
@ -9002,6 +9002,14 @@ F: include/uapi/linux/meye.h
|
||||
F: include/uapi/linux/ivtv*
|
||||
F: include/uapi/linux/uvcvideo.h
|
||||
|
||||
MEDIATEK BLUETOOTH DRIVER
|
||||
M: Sean Wang <sean.wang@mediatek.com>
|
||||
L: linux-bluetooth@vger.kernel.org
|
||||
L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/mediatek-bluetooth.txt
|
||||
F: drivers/bluetooth/btmtkuart.c
|
||||
|
||||
MEDIATEK CIR DRIVER
|
||||
M: Sean Wang <sean.wang@mediatek.com>
|
||||
S: Maintained
|
||||
@ -9174,6 +9182,7 @@ S: Supported
|
||||
W: http://www.mellanox.com
|
||||
Q: http://patchwork.ozlabs.org/project/netdev/list/
|
||||
F: drivers/net/ethernet/mellanox/mlxsw/
|
||||
F: tools/testing/selftests/drivers/net/mlxsw/
|
||||
|
||||
MELLANOX FIRMWARE FLASH LIBRARY (mlxfw)
|
||||
M: mlxsw@mellanox.com
|
||||
@ -12079,6 +12088,13 @@ S: Maintained
|
||||
F: sound/soc/codecs/rt*
|
||||
F: include/sound/rt*.h
|
||||
|
||||
REALTEK RTL83xx SMI DSA ROUTER CHIPS
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/net/dsa/realtek-smi.txt
|
||||
F: drivers/net/dsa/realtek-smi*
|
||||
F: drivers/net/dsa/rtl83*
|
||||
|
||||
REGISTER MAP ABSTRACTION
|
||||
M: Mark Brown <broonie@kernel.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
@ -12186,6 +12202,8 @@ S: Maintained
|
||||
F: Documentation/rfkill.txt
|
||||
F: Documentation/ABI/stable/sysfs-class-rfkill
|
||||
F: net/rfkill/
|
||||
F: include/linux/rfkill.h
|
||||
F: include/uapi/linux/rfkill.h
|
||||
|
||||
RHASHTABLE
|
||||
M: Thomas Graf <tgraf@suug.ch>
|
||||
@ -12193,7 +12211,9 @@ M: Herbert Xu <herbert@gondor.apana.org.au>
|
||||
L: netdev@vger.kernel.org
|
||||
S: Maintained
|
||||
F: lib/rhashtable.c
|
||||
F: lib/test_rhashtable.c
|
||||
F: include/linux/rhashtable.h
|
||||
F: include/linux/rhashtable-types.h
|
||||
|
||||
RICOH R5C592 MEMORYSTICK DRIVER
|
||||
M: Maxim Levitsky <maximlevitsky@gmail.com>
|
||||
|
@ -112,4 +112,7 @@
|
||||
|
||||
#define SO_ZEROCOPY 60
|
||||
|
||||
#define SO_TXTIME 61
|
||||
#define SCM_TXTIME SO_TXTIME
|
||||
|
||||
#endif /* _UAPI_ASM_SOCKET_H */
|
||||
|
@ -156,6 +156,100 @@
|
||||
};
|
||||
};
|
||||
|
||||
/* This is a RealTek RTL8366RB switch and PHY using SMI over GPIO */
|
||||
switch {
|
||||
compatible = "realtek,rtl8366rb";
|
||||
/* 22 = MDIO (has input reads), 21 = MDC (clock, output only) */
|
||||
mdc-gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>;
|
||||
mdio-gpios = <&gpio0 22 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
|
||||
realtek,disable-leds;
|
||||
|
||||
switch_intc: interrupt-controller {
|
||||
/* GPIO 15 provides the interrupt */
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <15 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan0";
|
||||
phy-handle = <&phy0>;
|
||||
};
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
label = "lan1";
|
||||
phy-handle = <&phy1>;
|
||||
};
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
label = "lan2";
|
||||
phy-handle = <&phy2>;
|
||||
};
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
label = "lan3";
|
||||
phy-handle = <&phy3>;
|
||||
};
|
||||
port@4 {
|
||||
reg = <4>;
|
||||
label = "wan";
|
||||
phy-handle = <&phy4>;
|
||||
};
|
||||
rtl8366rb_cpu_port: port@5 {
|
||||
reg = <5>;
|
||||
label = "cpu";
|
||||
ethernet = <&gmac0>;
|
||||
phy-mode = "rgmii";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
mdio {
|
||||
compatible = "realtek,smi-mdio";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
phy0: phy@0 {
|
||||
reg = <0>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <0>;
|
||||
};
|
||||
phy1: phy@1 {
|
||||
reg = <1>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <1>;
|
||||
};
|
||||
phy2: phy@2 {
|
||||
reg = <2>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <2>;
|
||||
};
|
||||
phy3: phy@3 {
|
||||
reg = <3>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <3>;
|
||||
};
|
||||
phy4: phy@4 {
|
||||
reg = <4>;
|
||||
interrupt-parent = <&switch_intc>;
|
||||
interrupts = <12>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
soc {
|
||||
flash@30000000 {
|
||||
/*
|
||||
@ -223,10 +317,12 @@
|
||||
* gpio0bgrp cover line 7 used by WPS LED
|
||||
* gpio0cgrp cover line 8, 13 used by keys
|
||||
* and 11, 12 used by the HD LEDs
|
||||
* and line 14, 15 used by RTL8366
|
||||
* RESET and phy ready
|
||||
* gpio0egrp cover line 16 used by VDISP
|
||||
* gpio0fgrp cover line 17 used by TK IRQ
|
||||
* gpio0ggrp cover line 20 used by panel CS
|
||||
* gpio0hgrp cover line 21,22 used by RTL8366RB
|
||||
* gpio0hgrp cover line 21,22 used by RTL8366RB MDIO
|
||||
*/
|
||||
gpio0_default_pins: pinctrl-gpio0 {
|
||||
mux {
|
||||
@ -250,6 +346,32 @@
|
||||
groups = "gpio1bgrp";
|
||||
};
|
||||
};
|
||||
pinctrl-gmii {
|
||||
mux {
|
||||
function = "gmii";
|
||||
groups = "gmii_gmac0_grp";
|
||||
};
|
||||
conf0 {
|
||||
pins = "V8 GMAC0 RXDV", "T10 GMAC1 RXDV",
|
||||
"Y7 GMAC0 RXC", "Y11 GMAC1 RXC",
|
||||
"T8 GMAC0 TXEN", "W11 GMAC1 TXEN",
|
||||
"U8 GMAC0 TXC", "V11 GMAC1 TXC",
|
||||
"W8 GMAC0 RXD0", "V9 GMAC0 RXD1",
|
||||
"Y8 GMAC0 RXD2", "U9 GMAC0 RXD3",
|
||||
"T7 GMAC0 TXD0", "U6 GMAC0 TXD1",
|
||||
"V7 GMAC0 TXD2", "U7 GMAC0 TXD3",
|
||||
"Y12 GMAC1 RXD0", "V12 GMAC1 RXD1",
|
||||
"T11 GMAC1 RXD2", "W12 GMAC1 RXD3",
|
||||
"U10 GMAC1 TXD0", "Y10 GMAC1 TXD1",
|
||||
"W10 GMAC1 TXD2", "T9 GMAC1 TXD3";
|
||||
skew-delay = <7>;
|
||||
};
|
||||
/* Set up drive strength on GMAC0 to 16 mA */
|
||||
conf1 {
|
||||
groups = "gmii_gmac0_grp";
|
||||
drive-strength = <16>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@ -291,6 +413,22 @@
|
||||
<0x6000 0 0 4 &pci_intc 2>;
|
||||
};
|
||||
|
||||
ethernet@60000000 {
|
||||
status = "okay";
|
||||
|
||||
ethernet-port@0 {
|
||||
phy-mode = "rgmii";
|
||||
fixed-link {
|
||||
speed = <1000>;
|
||||
full-duplex;
|
||||
pause;
|
||||
};
|
||||
};
|
||||
ethernet-port@1 {
|
||||
/* Not used in this platform */
|
||||
};
|
||||
};
|
||||
|
||||
ata@63000000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -77,11 +77,14 @@
|
||||
#define ARM_INST_EOR_R 0x00200000
|
||||
#define ARM_INST_EOR_I 0x02200000
|
||||
|
||||
#define ARM_INST_LDRB_I 0x05d00000
|
||||
#define ARM_INST_LDST__U 0x00800000
|
||||
#define ARM_INST_LDST__IMM12 0x00000fff
|
||||
#define ARM_INST_LDRB_I 0x05500000
|
||||
#define ARM_INST_LDRB_R 0x07d00000
|
||||
#define ARM_INST_LDRH_I 0x01d000b0
|
||||
#define ARM_INST_LDRD_I 0x014000d0
|
||||
#define ARM_INST_LDRH_I 0x015000b0
|
||||
#define ARM_INST_LDRH_R 0x019000b0
|
||||
#define ARM_INST_LDR_I 0x05900000
|
||||
#define ARM_INST_LDR_I 0x05100000
|
||||
#define ARM_INST_LDR_R 0x07900000
|
||||
|
||||
#define ARM_INST_LDM 0x08900000
|
||||
@ -124,9 +127,10 @@
|
||||
#define ARM_INST_SBC_R 0x00c00000
|
||||
#define ARM_INST_SBCS_R 0x00d00000
|
||||
|
||||
#define ARM_INST_STR_I 0x05800000
|
||||
#define ARM_INST_STRB_I 0x05c00000
|
||||
#define ARM_INST_STRH_I 0x01c000b0
|
||||
#define ARM_INST_STR_I 0x05000000
|
||||
#define ARM_INST_STRB_I 0x05400000
|
||||
#define ARM_INST_STRD_I 0x014000f0
|
||||
#define ARM_INST_STRH_I 0x014000b0
|
||||
|
||||
#define ARM_INST_TST_R 0x01100000
|
||||
#define ARM_INST_TST_I 0x03100000
|
||||
@ -183,17 +187,18 @@
|
||||
#define ARM_EOR_R(rd, rn, rm) _AL3_R(ARM_INST_EOR, rd, rn, rm)
|
||||
#define ARM_EOR_I(rd, rn, imm) _AL3_I(ARM_INST_EOR, rd, rn, imm)
|
||||
|
||||
#define ARM_LDR_I(rt, rn, off) (ARM_INST_LDR_I | (rt) << 12 | (rn) << 16 \
|
||||
| ((off) & 0xfff))
|
||||
#define ARM_LDR_R(rt, rn, rm) (ARM_INST_LDR_R | (rt) << 12 | (rn) << 16 \
|
||||
#define ARM_LDR_R(rt, rn, rm) (ARM_INST_LDR_R | ARM_INST_LDST__U \
|
||||
| (rt) << 12 | (rn) << 16 \
|
||||
| (rm))
|
||||
#define ARM_LDRB_I(rt, rn, off) (ARM_INST_LDRB_I | (rt) << 12 | (rn) << 16 \
|
||||
| (off))
|
||||
#define ARM_LDRB_R(rt, rn, rm) (ARM_INST_LDRB_R | (rt) << 12 | (rn) << 16 \
|
||||
#define ARM_LDR_R_SI(rt, rn, rm, type, imm) \
|
||||
(ARM_INST_LDR_R | ARM_INST_LDST__U \
|
||||
| (rt) << 12 | (rn) << 16 \
|
||||
| (imm) << 7 | (type) << 5 | (rm))
|
||||
#define ARM_LDRB_R(rt, rn, rm) (ARM_INST_LDRB_R | ARM_INST_LDST__U \
|
||||
| (rt) << 12 | (rn) << 16 \
|
||||
| (rm))
|
||||
#define ARM_LDRH_I(rt, rn, off) (ARM_INST_LDRH_I | (rt) << 12 | (rn) << 16 \
|
||||
| (((off) & 0xf0) << 4) | ((off) & 0xf))
|
||||
#define ARM_LDRH_R(rt, rn, rm) (ARM_INST_LDRH_R | (rt) << 12 | (rn) << 16 \
|
||||
#define ARM_LDRH_R(rt, rn, rm) (ARM_INST_LDRH_R | ARM_INST_LDST__U \
|
||||
| (rt) << 12 | (rn) << 16 \
|
||||
| (rm))
|
||||
|
||||
#define ARM_LDM(rn, regs) (ARM_INST_LDM | (rn) << 16 | (regs))
|
||||
@ -254,13 +259,6 @@
|
||||
#define ARM_SUBS_I(rd, rn, imm) _AL3_I(ARM_INST_SUBS, rd, rn, imm)
|
||||
#define ARM_SBC_I(rd, rn, imm) _AL3_I(ARM_INST_SBC, rd, rn, imm)
|
||||
|
||||
#define ARM_STR_I(rt, rn, off) (ARM_INST_STR_I | (rt) << 12 | (rn) << 16 \
|
||||
| ((off) & 0xfff))
|
||||
#define ARM_STRH_I(rt, rn, off) (ARM_INST_STRH_I | (rt) << 12 | (rn) << 16 \
|
||||
| (((off) & 0xf0) << 4) | ((off) & 0xf))
|
||||
#define ARM_STRB_I(rt, rn, off) (ARM_INST_STRB_I | (rt) << 12 | (rn) << 16 \
|
||||
| (((off) & 0xf0) << 4) | ((off) & 0xf))
|
||||
|
||||
#define ARM_TST_R(rn, rm) _AL3_R(ARM_INST_TST, 0, rn, rm)
|
||||
#define ARM_TST_I(rn, imm) _AL3_I(ARM_INST_TST, 0, rn, imm)
|
||||
|
||||
|
@ -482,9 +482,9 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
mdio_mux_iproc: mdio-mux@6602023c {
|
||||
mdio_mux_iproc: mdio-mux@66020000 {
|
||||
compatible = "brcm,mdio-mux-iproc";
|
||||
reg = <0x6602023c 0x14>;
|
||||
reg = <0x66020000 0x250>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
|
@ -278,9 +278,9 @@
|
||||
|
||||
#include "stingray-pinctrl.dtsi"
|
||||
|
||||
mdio_mux_iproc: mdio-mux@2023c {
|
||||
mdio_mux_iproc: mdio-mux@20000 {
|
||||
compatible = "brcm,mdio-mux-iproc";
|
||||
reg = <0x0002023c 0x14>;
|
||||
reg = <0x00020000 0x250>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
|
@ -11,13 +11,14 @@ fman0: fman@1a00000 {
|
||||
#size-cells = <1>;
|
||||
cell-index = <0>;
|
||||
compatible = "fsl,fman";
|
||||
ranges = <0x0 0x0 0x1a00000 0x100000>;
|
||||
reg = <0x0 0x1a00000 0x0 0x100000>;
|
||||
ranges = <0x0 0x0 0x1a00000 0xfe000>;
|
||||
reg = <0x0 0x1a00000 0x0 0xfe000>;
|
||||
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clockgen 3 0>;
|
||||
clock-names = "fmanclk";
|
||||
fsl,qman-channel-range = <0x800 0x10>;
|
||||
ptimer-handle = <&ptp_timer0>;
|
||||
|
||||
muram@0 {
|
||||
compatible = "fsl,fman-muram";
|
||||
@ -73,9 +74,11 @@ fman0: fman@1a00000 {
|
||||
compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
|
||||
reg = <0xfd000 0x1000>;
|
||||
};
|
||||
|
||||
ptp_timer0: ptp-timer@fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0xfe000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
ptp_timer0: ptp-timer@1afe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0x0 0x1afe000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clockgen 3 0>;
|
||||
};
|
||||
|
@ -114,4 +114,7 @@
|
||||
|
||||
#define SO_ZEROCOPY 60
|
||||
|
||||
#define SO_TXTIME 61
|
||||
#define SCM_TXTIME SO_TXTIME
|
||||
|
||||
#endif /* _ASM_IA64_SOCKET_H */
|
||||
|
@ -66,7 +66,6 @@ CONFIG_HW_RANDOM=y
|
||||
CONFIG_GPIO_SYSFS=y
|
||||
CONFIG_WATCHDOG=y
|
||||
CONFIG_BCM47XX_WDT=y
|
||||
CONFIG_SSB_DEBUG=y
|
||||
CONFIG_SSB_DRIVER_GIGE=y
|
||||
CONFIG_BCMA_DRIVER_GMAC_CMN=y
|
||||
CONFIG_USB=y
|
||||
|
@ -123,4 +123,7 @@
|
||||
|
||||
#define SO_ZEROCOPY 60
|
||||
|
||||
#define SO_TXTIME 61
|
||||
#define SCM_TXTIME SO_TXTIME
|
||||
|
||||
#endif /* _UAPI_ASM_SOCKET_H */
|
||||
|
@ -104,4 +104,7 @@
|
||||
|
||||
#define SO_ZEROCOPY 0x4035
|
||||
|
||||
#define SO_TXTIME 0x4036
|
||||
#define SCM_TXTIME SO_TXTIME
|
||||
|
||||
#endif /* _UAPI_ASM_SOCKET_H */
|
||||
|
@ -37,12 +37,13 @@ fman0: fman@400000 {
|
||||
#size-cells = <1>;
|
||||
cell-index = <0>;
|
||||
compatible = "fsl,fman";
|
||||
ranges = <0 0x400000 0x100000>;
|
||||
reg = <0x400000 0x100000>;
|
||||
ranges = <0 0x400000 0xfe000>;
|
||||
reg = <0x400000 0xfe000>;
|
||||
interrupts = <96 2 0 0>, <16 2 1 1>;
|
||||
clocks = <&clockgen 3 0>;
|
||||
clock-names = "fmanclk";
|
||||
fsl,qman-channel-range = <0x40 0xc>;
|
||||
ptimer-handle = <&ptp_timer0>;
|
||||
|
||||
muram@0 {
|
||||
compatible = "fsl,fman-muram";
|
||||
@ -93,9 +94,11 @@ fman0: fman@400000 {
|
||||
reg = <0x87000 0x1000>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ptp_timer0: ptp-timer@fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0xfe000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
ptp_timer0: ptp-timer@4fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0x4fe000 0x1000>;
|
||||
interrupts = <96 2 0 0>;
|
||||
clocks = <&clockgen 3 0>;
|
||||
};
|
||||
|
@ -37,12 +37,13 @@ fman1: fman@500000 {
|
||||
#size-cells = <1>;
|
||||
cell-index = <1>;
|
||||
compatible = "fsl,fman";
|
||||
ranges = <0 0x500000 0x100000>;
|
||||
reg = <0x500000 0x100000>;
|
||||
ranges = <0 0x500000 0xfe000>;
|
||||
reg = <0x500000 0xfe000>;
|
||||
interrupts = <97 2 0 0>, <16 2 1 0>;
|
||||
clocks = <&clockgen 3 1>;
|
||||
clock-names = "fmanclk";
|
||||
fsl,qman-channel-range = <0x60 0xc>;
|
||||
ptimer-handle = <&ptp_timer1>;
|
||||
|
||||
muram@0 {
|
||||
compatible = "fsl,fman-muram";
|
||||
@ -93,9 +94,11 @@ fman1: fman@500000 {
|
||||
reg = <0x87000 0x1000>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
ptp_timer1: ptp-timer@fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0xfe000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
ptp_timer1: ptp-timer@5fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0x5fe000 0x1000>;
|
||||
interrupts = <97 2 0 0>;
|
||||
clocks = <&clockgen 3 1>;
|
||||
};
|
||||
|
@ -37,12 +37,13 @@ fman0: fman@400000 {
|
||||
#size-cells = <1>;
|
||||
cell-index = <0>;
|
||||
compatible = "fsl,fman";
|
||||
ranges = <0 0x400000 0x100000>;
|
||||
reg = <0x400000 0x100000>;
|
||||
ranges = <0 0x400000 0xfe000>;
|
||||
reg = <0x400000 0xfe000>;
|
||||
interrupts = <96 2 0 0>, <16 2 1 1>;
|
||||
clocks = <&clockgen 3 0>;
|
||||
clock-names = "fmanclk";
|
||||
fsl,qman-channel-range = <0x800 0x10>;
|
||||
ptimer-handle = <&ptp_timer0>;
|
||||
|
||||
muram@0 {
|
||||
compatible = "fsl,fman-muram";
|
||||
@ -98,9 +99,11 @@ fman0: fman@400000 {
|
||||
compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
|
||||
reg = <0xfd000 0x1000>;
|
||||
};
|
||||
|
||||
ptp_timer0: ptp-timer@fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0xfe000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
ptp_timer0: ptp-timer@4fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0x4fe000 0x1000>;
|
||||
interrupts = <96 2 0 0>;
|
||||
clocks = <&clockgen 3 0>;
|
||||
};
|
||||
|
@ -37,12 +37,13 @@ fman1: fman@500000 {
|
||||
#size-cells = <1>;
|
||||
cell-index = <1>;
|
||||
compatible = "fsl,fman";
|
||||
ranges = <0 0x500000 0x100000>;
|
||||
reg = <0x500000 0x100000>;
|
||||
ranges = <0 0x500000 0xfe000>;
|
||||
reg = <0x500000 0xfe000>;
|
||||
interrupts = <97 2 0 0>, <16 2 1 0>;
|
||||
clocks = <&clockgen 3 1>;
|
||||
clock-names = "fmanclk";
|
||||
fsl,qman-channel-range = <0x820 0x10>;
|
||||
ptimer-handle = <&ptp_timer1>;
|
||||
|
||||
muram@0 {
|
||||
compatible = "fsl,fman-muram";
|
||||
@ -98,9 +99,11 @@ fman1: fman@500000 {
|
||||
compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
|
||||
reg = <0xfd000 0x1000>;
|
||||
};
|
||||
|
||||
ptp_timer1: ptp-timer@fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0xfe000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
ptp_timer1: ptp-timer@5fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0x5fe000 0x1000>;
|
||||
interrupts = <97 2 0 0>;
|
||||
clocks = <&clockgen 3 1>;
|
||||
};
|
||||
|
@ -37,12 +37,13 @@ fman0: fman@400000 {
|
||||
#size-cells = <1>;
|
||||
cell-index = <0>;
|
||||
compatible = "fsl,fman";
|
||||
ranges = <0 0x400000 0x100000>;
|
||||
reg = <0x400000 0x100000>;
|
||||
ranges = <0 0x400000 0xfe000>;
|
||||
reg = <0x400000 0xfe000>;
|
||||
interrupts = <96 2 0 0>, <16 2 1 1>;
|
||||
clocks = <&clockgen 3 0>;
|
||||
clock-names = "fmanclk";
|
||||
fsl,qman-channel-range = <0x800 0x10>;
|
||||
ptimer-handle = <&ptp_timer0>;
|
||||
|
||||
muram@0 {
|
||||
compatible = "fsl,fman-muram";
|
||||
@ -86,9 +87,11 @@ fman0: fman@400000 {
|
||||
compatible = "fsl,fman-memac-mdio", "fsl,fman-xmdio";
|
||||
reg = <0xfd000 0x1000>;
|
||||
};
|
||||
|
||||
ptp_timer0: ptp-timer@fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0xfe000 0x1000>;
|
||||
};
|
||||
};
|
||||
|
||||
ptp_timer0: ptp-timer@4fe000 {
|
||||
compatible = "fsl,fman-ptp-timer";
|
||||
reg = <0x4fe000 0x1000>;
|
||||
interrupts = <96 2 0 0>;
|
||||
clocks = <&clockgen 3 0>;
|
||||
};
|
||||
|
@ -78,7 +78,6 @@ CONFIG_GPIO_HLWD=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_GPIO=y
|
||||
# CONFIG_HWMON is not set
|
||||
CONFIG_SSB_DEBUG=y
|
||||
CONFIG_FB=y
|
||||
# CONFIG_VGA_CONSOLE is not set
|
||||
CONFIG_FRAMEBUFFER_CONSOLE=y
|
||||
|
@ -111,4 +111,7 @@
|
||||
|
||||
#define SO_ZEROCOPY 60
|
||||
|
||||
#define SO_TXTIME 61
|
||||
#define SCM_TXTIME SO_TXTIME
|
||||
|
||||
#endif /* _ASM_SOCKET_H */
|
||||
|
@ -101,6 +101,9 @@
|
||||
|
||||
#define SO_ZEROCOPY 0x003e
|
||||
|
||||
#define SO_TXTIME 0x003f
|
||||
#define SCM_TXTIME SO_TXTIME
|
||||
|
||||
/* Security levels - as per NRL IPv6 - don't actually do anything */
|
||||
#define SO_SECURITY_AUTHENTICATION 0x5001
|
||||
#define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002
|
||||
|
@ -204,6 +204,7 @@ static int rdtgroup_add_file(struct kernfs_node *parent_kn, struct rftype *rft)
|
||||
int ret;
|
||||
|
||||
kn = __kernfs_create_file(parent_kn, rft->name, rft->mode,
|
||||
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
|
||||
0, rft->kf_ops, rft, NULL, NULL);
|
||||
if (IS_ERR(kn))
|
||||
return PTR_ERR(kn);
|
||||
@ -2095,7 +2096,8 @@ static int mon_addfile(struct kernfs_node *parent_kn, const char *name,
|
||||
struct kernfs_node *kn;
|
||||
int ret = 0;
|
||||
|
||||
kn = __kernfs_create_file(parent_kn, name, 0444, 0,
|
||||
kn = __kernfs_create_file(parent_kn, name, 0444,
|
||||
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, 0,
|
||||
&kf_mondata_ops, priv, NULL, NULL);
|
||||
if (IS_ERR(kn))
|
||||
return PTR_ERR(kn);
|
||||
|
@ -116,4 +116,7 @@
|
||||
|
||||
#define SO_ZEROCOPY 60
|
||||
|
||||
#define SO_TXTIME 61
|
||||
#define SCM_TXTIME SO_TXTIME
|
||||
|
||||
#endif /* _XTENSA_SOCKET_H */
|
||||
|
@ -1071,7 +1071,7 @@ __poll_t af_alg_poll(struct file *file, struct socket *sock,
|
||||
struct af_alg_ctx *ctx = ask->private;
|
||||
__poll_t mask;
|
||||
|
||||
sock_poll_wait(file, sk_sleep(sk), wait);
|
||||
sock_poll_wait(file, wait);
|
||||
mask = 0;
|
||||
|
||||
if (!ctx->more || ctx->used)
|
||||
|
@ -1385,14 +1385,12 @@ static void zatm_close(struct atm_vcc *vcc)
|
||||
|
||||
static int zatm_open(struct atm_vcc *vcc)
|
||||
{
|
||||
struct zatm_dev *zatm_dev;
|
||||
struct zatm_vcc *zatm_vcc;
|
||||
short vpi = vcc->vpi;
|
||||
int vci = vcc->vci;
|
||||
int error;
|
||||
|
||||
DPRINTK(">zatm_open\n");
|
||||
zatm_dev = ZATM_DEV(vcc->dev);
|
||||
if (!test_bit(ATM_VF_PARTIAL,&vcc->flags))
|
||||
vcc->dev_data = NULL;
|
||||
if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
|
||||
|
@ -907,10 +907,19 @@ static const void *device_namespace(struct kobject *kobj)
|
||||
return ns;
|
||||
}
|
||||
|
||||
static void device_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
|
||||
if (dev->class && dev->class->get_ownership)
|
||||
dev->class->get_ownership(dev, uid, gid);
|
||||
}
|
||||
|
||||
static struct kobj_type device_ktype = {
|
||||
.release = device_release,
|
||||
.sysfs_ops = &dev_sysfs_ops,
|
||||
.namespace = device_namespace,
|
||||
.get_ownership = device_get_ownership,
|
||||
};
|
||||
|
||||
|
||||
|
@ -1633,7 +1633,7 @@ static int find_free_cb(int id, void *ptr, void *data)
|
||||
}
|
||||
|
||||
/* Netlink interface. */
|
||||
static struct nla_policy nbd_attr_policy[NBD_ATTR_MAX + 1] = {
|
||||
static const struct nla_policy nbd_attr_policy[NBD_ATTR_MAX + 1] = {
|
||||
[NBD_ATTR_INDEX] = { .type = NLA_U32 },
|
||||
[NBD_ATTR_SIZE_BYTES] = { .type = NLA_U64 },
|
||||
[NBD_ATTR_BLOCK_SIZE_BYTES] = { .type = NLA_U64 },
|
||||
@ -1645,14 +1645,14 @@ static struct nla_policy nbd_attr_policy[NBD_ATTR_MAX + 1] = {
|
||||
[NBD_ATTR_DEVICE_LIST] = { .type = NLA_NESTED},
|
||||
};
|
||||
|
||||
static struct nla_policy nbd_sock_policy[NBD_SOCK_MAX + 1] = {
|
||||
static const struct nla_policy nbd_sock_policy[NBD_SOCK_MAX + 1] = {
|
||||
[NBD_SOCK_FD] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
/* We don't use this right now since we don't parse the incoming list, but we
|
||||
* still want it here so userspace knows what to expect.
|
||||
*/
|
||||
static struct nla_policy __attribute__((unused))
|
||||
static const struct nla_policy __attribute__((unused))
|
||||
nbd_device_policy[NBD_DEVICE_ATTR_MAX + 1] = {
|
||||
[NBD_DEVICE_INDEX] = { .type = NLA_U32 },
|
||||
[NBD_DEVICE_CONNECTED] = { .type = NLA_U8 },
|
||||
|
@ -159,6 +159,7 @@ config BT_HCIUART_LL
|
||||
config BT_HCIUART_3WIRE
|
||||
bool "Three-wire UART (H5) protocol support"
|
||||
depends on BT_HCIUART
|
||||
depends on BT_HCIUART_SERDEV
|
||||
help
|
||||
The HCI Three-wire UART Transport Layer makes it possible to
|
||||
user the Bluetooth HCI over a serial port interface. The HCI
|
||||
@ -194,6 +195,19 @@ config BT_HCIUART_BCM
|
||||
|
||||
Say Y here to compile support for Broadcom protocol.
|
||||
|
||||
config BT_HCIUART_RTL
|
||||
bool "Realtek protocol support"
|
||||
depends on BT_HCIUART
|
||||
depends on BT_HCIUART_SERDEV
|
||||
depends on GPIOLIB
|
||||
select BT_HCIUART_3WIRE
|
||||
select BT_RTL
|
||||
help
|
||||
The Realtek protocol support enables Bluetooth HCI over 3-Wire
|
||||
serial port internface for Realtek Bluetooth controllers.
|
||||
|
||||
Say Y here to compile support for Realtek protocol.
|
||||
|
||||
config BT_HCIUART_QCA
|
||||
bool "Qualcomm Atheros protocol support"
|
||||
depends on BT_HCIUART
|
||||
@ -364,6 +378,17 @@ config BT_WILINK
|
||||
Say Y here to compile support for Texas Instrument's WiLink7 driver
|
||||
into the kernel or say M to compile it as module (btwilink).
|
||||
|
||||
config BT_MTKUART
|
||||
tristate "MediaTek HCI UART driver"
|
||||
depends on SERIAL_DEV_BUS
|
||||
help
|
||||
MediaTek Bluetooth HCI UART driver.
|
||||
This driver is required if you want to use MediaTek Bluetooth
|
||||
with serial interface.
|
||||
|
||||
Say Y here to compile support for MediaTek Bluetooth UART devices
|
||||
into the kernel or say M to compile it as module (btmtkuart).
|
||||
|
||||
config BT_QCOMSMD
|
||||
tristate "Qualcomm SMD based HCI support"
|
||||
depends on RPMSG || (COMPILE_TEST && RPMSG=n)
|
||||
|
@ -20,6 +20,7 @@ obj-$(CONFIG_BT_ATH3K) += ath3k.o
|
||||
obj-$(CONFIG_BT_MRVL) += btmrvl.o
|
||||
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
|
||||
obj-$(CONFIG_BT_WILINK) += btwilink.o
|
||||
obj-$(CONFIG_BT_MTKUART) += btmtkuart.o
|
||||
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
|
||||
obj-$(CONFIG_BT_BCM) += btbcm.o
|
||||
obj-$(CONFIG_BT_RTL) += btrtl.o
|
||||
|
@ -490,7 +490,7 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
count = skb->len;
|
||||
|
||||
/* Max HCI frame size seems to be 1511 + 1 */
|
||||
nskb = bt_skb_alloc(count + 32, GFP_ATOMIC);
|
||||
nskb = bt_skb_alloc(count + 32, GFP_KERNEL);
|
||||
if (!nskb) {
|
||||
BT_ERR("Can't allocate memory for new packet");
|
||||
return -ENOMEM;
|
||||
|
@ -565,7 +565,7 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
|
||||
/* Ericsson baud rate command */
|
||||
unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
|
||||
|
||||
skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
BT_ERR("Can't allocate mem for new packet");
|
||||
return -1;
|
||||
|
@ -289,7 +289,7 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
skb->dev = (void *) hdev;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -298,7 +298,7 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
case HCI_COMMAND_PKT:
|
||||
dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
|
||||
dr = kmalloc(sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr) {
|
||||
usb_free_urb(urb);
|
||||
return -ENOMEM;
|
||||
@ -343,7 +343,7 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
usb_anchor_urb(urb, &data->tx_anchor);
|
||||
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
err = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "urb %p submission failed", urb);
|
||||
kfree(urb->setup_packet);
|
||||
|
@ -718,7 +718,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
|
||||
}
|
||||
|
||||
/* Allocate buffer */
|
||||
skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
|
||||
skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
BT_ERR("No free skb");
|
||||
ret = -ENOMEM;
|
||||
|
629
drivers/bluetooth/btmtkuart.c
Normal file
629
drivers/bluetooth/btmtkuart.c
Normal file
@ -0,0 +1,629 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
|
||||
/*
|
||||
* Bluetooth support for MediaTek serial devices
|
||||
*
|
||||
* Author: Sean Wang <sean.wang@mediatek.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "h4_recv.h"
|
||||
|
||||
#define VERSION "0.1"
|
||||
|
||||
#define FIRMWARE_MT7622 "mediatek/mt7622pr2h.bin"
|
||||
|
||||
#define MTK_STP_TLR_SIZE 2
|
||||
|
||||
#define BTMTKUART_TX_STATE_ACTIVE 1
|
||||
#define BTMTKUART_TX_STATE_WAKEUP 2
|
||||
#define BTMTKUART_TX_WAIT_VND_EVT 3
|
||||
|
||||
enum {
|
||||
MTK_WMT_PATCH_DWNLD = 0x1,
|
||||
MTK_WMT_FUNC_CTRL = 0x6,
|
||||
MTK_WMT_RST = 0x7
|
||||
};
|
||||
|
||||
struct mtk_stp_hdr {
|
||||
u8 prefix;
|
||||
__be16 dlen;
|
||||
u8 cs;
|
||||
} __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 btmtkuart_dev {
|
||||
struct hci_dev *hdev;
|
||||
struct serdev_device *serdev;
|
||||
struct clk *clk;
|
||||
|
||||
struct work_struct tx_work;
|
||||
unsigned long tx_state;
|
||||
struct sk_buff_head txq;
|
||||
|
||||
struct sk_buff *rx_skb;
|
||||
|
||||
u8 stp_pad[6];
|
||||
u8 stp_cursor;
|
||||
u16 stp_dlen;
|
||||
};
|
||||
|
||||
static int mtk_hci_wmt_sync(struct hci_dev *hdev, u8 op, u8 flag, u16 plen,
|
||||
const void *param)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
|
||||
struct mtk_hci_wmt_cmd wc;
|
||||
struct mtk_wmt_hdr *hdr;
|
||||
u32 hlen;
|
||||
int err;
|
||||
|
||||
hlen = sizeof(*hdr) + plen;
|
||||
if (hlen > 255)
|
||||
return -EINVAL;
|
||||
|
||||
hdr = (struct mtk_wmt_hdr *)&wc;
|
||||
hdr->dir = 1;
|
||||
hdr->op = op;
|
||||
hdr->dlen = cpu_to_le16(plen + 1);
|
||||
hdr->flag = flag;
|
||||
memcpy(wc.data, param, plen);
|
||||
|
||||
set_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
|
||||
|
||||
err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc);
|
||||
if (err < 0) {
|
||||
clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* The vendor specific WMT commands are all answered by a vendor
|
||||
* specific event and will not have the Command Status or Command
|
||||
* Complete as with usual HCI command flow control.
|
||||
*
|
||||
* After sending the command, wait for BTMTKUART_TX_WAIT_VND_EVT
|
||||
* state to be cleared. The driver speicfic event receive routine
|
||||
* will clear that state and with that indicate completion of the
|
||||
* WMT command.
|
||||
*/
|
||||
err = wait_on_bit_timeout(&bdev->tx_state, BTMTKUART_TX_WAIT_VND_EVT,
|
||||
TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT);
|
||||
if (err == -EINTR) {
|
||||
bt_dev_err(hdev, "Execution of wmt command interrupted");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Execution of wmt command timed out");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_setup_fw(struct hci_dev *hdev)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
size_t fw_size;
|
||||
int err, dlen;
|
||||
u8 flag;
|
||||
|
||||
err = request_firmware(&fw, FIRMWARE_MT7622, &hdev->dev);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data;
|
||||
fw_size = fw->size;
|
||||
|
||||
/* The size of patch header is 30 bytes, should be skip */
|
||||
if (fw_size < 30)
|
||||
return -EINVAL;
|
||||
|
||||
fw_size -= 30;
|
||||
fw_ptr += 30;
|
||||
flag = 1;
|
||||
|
||||
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;
|
||||
|
||||
err = mtk_hci_wmt_sync(hdev, MTK_WMT_PATCH_DWNLD, flag, dlen,
|
||||
fw_ptr);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
break;
|
||||
}
|
||||
|
||||
fw_size -= dlen;
|
||||
fw_ptr += dlen;
|
||||
}
|
||||
|
||||
release_firmware(fw);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btmtkuart_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
|
||||
struct hci_event_hdr *hdr = (void *)skb->data;
|
||||
int err;
|
||||
|
||||
/* Fix up the vendor event id with 0xff for vendor specific instead
|
||||
* of 0xe4 so that event send via monitoring socket can be parsed
|
||||
* properly.
|
||||
*/
|
||||
if (hdr->evt == 0xe4)
|
||||
hdr->evt = HCI_EV_VENDOR;
|
||||
|
||||
err = hci_recv_frame(hdev, skb);
|
||||
|
||||
if (hdr->evt == HCI_EV_VENDOR) {
|
||||
if (test_and_clear_bit(BTMTKUART_TX_WAIT_VND_EVT,
|
||||
&bdev->tx_state)) {
|
||||
/* Barrier to sync with other CPUs */
|
||||
smp_mb__after_atomic();
|
||||
wake_up_bit(&bdev->tx_state, BTMTKUART_TX_WAIT_VND_EVT);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct h4_recv_pkt mtk_recv_pkts[] = {
|
||||
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||
{ H4_RECV_EVENT, .recv = btmtkuart_recv_event },
|
||||
};
|
||||
|
||||
static void btmtkuart_tx_work(struct work_struct *work)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = container_of(work, struct btmtkuart_dev,
|
||||
tx_work);
|
||||
struct serdev_device *serdev = bdev->serdev;
|
||||
struct hci_dev *hdev = bdev->hdev;
|
||||
|
||||
while (1) {
|
||||
clear_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state);
|
||||
|
||||
while (1) {
|
||||
struct sk_buff *skb = skb_dequeue(&bdev->txq);
|
||||
int len;
|
||||
|
||||
if (!skb)
|
||||
break;
|
||||
|
||||
len = serdev_device_write_buf(serdev, skb->data,
|
||||
skb->len);
|
||||
hdev->stat.byte_tx += len;
|
||||
|
||||
skb_pull(skb, len);
|
||||
if (skb->len > 0) {
|
||||
skb_queue_head(&bdev->txq, skb);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
case HCI_COMMAND_PKT:
|
||||
hdev->stat.cmd_tx++;
|
||||
break;
|
||||
case HCI_ACLDATA_PKT:
|
||||
hdev->stat.acl_tx++;
|
||||
break;
|
||||
case HCI_SCODATA_PKT:
|
||||
hdev->stat.sco_tx++;
|
||||
break;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
if (!test_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state))
|
||||
break;
|
||||
}
|
||||
|
||||
clear_bit(BTMTKUART_TX_STATE_ACTIVE, &bdev->tx_state);
|
||||
}
|
||||
|
||||
static void btmtkuart_tx_wakeup(struct btmtkuart_dev *bdev)
|
||||
{
|
||||
if (test_and_set_bit(BTMTKUART_TX_STATE_ACTIVE, &bdev->tx_state))
|
||||
set_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state);
|
||||
|
||||
schedule_work(&bdev->tx_work);
|
||||
}
|
||||
|
||||
static const unsigned char *
|
||||
mtk_stp_split(struct btmtkuart_dev *bdev, const unsigned char *data, int count,
|
||||
int *sz_h4)
|
||||
{
|
||||
struct mtk_stp_hdr *shdr;
|
||||
|
||||
/* The cursor is reset when all the data of STP is consumed out */
|
||||
if (!bdev->stp_dlen && bdev->stp_cursor >= 6)
|
||||
bdev->stp_cursor = 0;
|
||||
|
||||
/* Filling pad until all STP info is obtained */
|
||||
while (bdev->stp_cursor < 6 && count > 0) {
|
||||
bdev->stp_pad[bdev->stp_cursor] = *data;
|
||||
bdev->stp_cursor++;
|
||||
data++;
|
||||
count--;
|
||||
}
|
||||
|
||||
/* Retrieve STP info and have a sanity check */
|
||||
if (!bdev->stp_dlen && bdev->stp_cursor >= 6) {
|
||||
shdr = (struct mtk_stp_hdr *)&bdev->stp_pad[2];
|
||||
bdev->stp_dlen = be16_to_cpu(shdr->dlen) & 0x0fff;
|
||||
|
||||
/* Resync STP when unexpected data is being read */
|
||||
if (shdr->prefix != 0x80 || bdev->stp_dlen > 2048) {
|
||||
bt_dev_err(bdev->hdev, "stp format unexpect (%d, %d)",
|
||||
shdr->prefix, bdev->stp_dlen);
|
||||
bdev->stp_cursor = 2;
|
||||
bdev->stp_dlen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Directly quit when there's no data found for H4 can process */
|
||||
if (count <= 0)
|
||||
return NULL;
|
||||
|
||||
/* Tranlate to how much the size of data H4 can handle so far */
|
||||
*sz_h4 = min_t(int, count, bdev->stp_dlen);
|
||||
|
||||
/* Update the remaining size of STP packet */
|
||||
bdev->stp_dlen -= *sz_h4;
|
||||
|
||||
/* Data points to STP payload which can be handled by H4 */
|
||||
return data;
|
||||
}
|
||||
|
||||
static int btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
|
||||
const unsigned char *p_left = data, *p_h4;
|
||||
int sz_left = count, sz_h4, adv;
|
||||
int err;
|
||||
|
||||
while (sz_left > 0) {
|
||||
/* The serial data received from MT7622 BT controller is
|
||||
* at all time padded around with the STP header and tailer.
|
||||
*
|
||||
* A full STP packet is looking like
|
||||
* -----------------------------------
|
||||
* | STP header | H:4 | STP tailer |
|
||||
* -----------------------------------
|
||||
* but it doesn't guarantee to contain a full H:4 packet which
|
||||
* means that it's possible for multiple STP packets forms a
|
||||
* full H:4 packet that means extra STP header + length doesn't
|
||||
* indicate a full H:4 frame, things can fragment. Whose length
|
||||
* recorded in STP header just shows up the most length the
|
||||
* H:4 engine can handle currently.
|
||||
*/
|
||||
|
||||
p_h4 = mtk_stp_split(bdev, p_left, sz_left, &sz_h4);
|
||||
if (!p_h4)
|
||||
break;
|
||||
|
||||
adv = p_h4 - p_left;
|
||||
sz_left -= adv;
|
||||
p_left += adv;
|
||||
|
||||
bdev->rx_skb = h4_recv_buf(bdev->hdev, bdev->rx_skb, p_h4,
|
||||
sz_h4, mtk_recv_pkts,
|
||||
ARRAY_SIZE(mtk_recv_pkts));
|
||||
if (IS_ERR(bdev->rx_skb)) {
|
||||
err = PTR_ERR(bdev->rx_skb);
|
||||
bt_dev_err(bdev->hdev,
|
||||
"Frame reassembly failed (%d)", err);
|
||||
bdev->rx_skb = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
sz_left -= sz_h4;
|
||||
p_left += sz_h4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtkuart_receive_buf(struct serdev_device *serdev, const u8 *data,
|
||||
size_t count)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
|
||||
int err;
|
||||
|
||||
err = btmtkuart_recv(bdev->hdev, data, count);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
bdev->hdev->stat.byte_rx += count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void btmtkuart_write_wakeup(struct serdev_device *serdev)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
|
||||
|
||||
btmtkuart_tx_wakeup(bdev);
|
||||
}
|
||||
|
||||
static const struct serdev_device_ops btmtkuart_client_ops = {
|
||||
.receive_buf = btmtkuart_receive_buf,
|
||||
.write_wakeup = btmtkuart_write_wakeup,
|
||||
};
|
||||
|
||||
static int btmtkuart_open(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
|
||||
struct device *dev;
|
||||
int err;
|
||||
|
||||
err = serdev_device_open(bdev->serdev);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Unable to open UART device %s",
|
||||
dev_name(&bdev->serdev->dev));
|
||||
goto err_open;
|
||||
}
|
||||
|
||||
bdev->stp_cursor = 2;
|
||||
bdev->stp_dlen = 0;
|
||||
|
||||
dev = &bdev->serdev->dev;
|
||||
|
||||
/* Enable the power domain and clock the device requires */
|
||||
pm_runtime_enable(dev);
|
||||
err = pm_runtime_get_sync(dev);
|
||||
if (err < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
goto err_disable_rpm;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(bdev->clk);
|
||||
if (err < 0)
|
||||
goto err_put_rpm;
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_rpm:
|
||||
pm_runtime_put_sync(dev);
|
||||
err_disable_rpm:
|
||||
pm_runtime_disable(dev);
|
||||
err_open:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btmtkuart_close(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
|
||||
struct device *dev = &bdev->serdev->dev;
|
||||
|
||||
/* Shutdown the clock and power domain the device requires */
|
||||
clk_disable_unprepare(bdev->clk);
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
serdev_device_close(bdev->serdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtkuart_flush(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
|
||||
|
||||
/* Flush any pending characters */
|
||||
serdev_device_write_flush(bdev->serdev);
|
||||
skb_queue_purge(&bdev->txq);
|
||||
|
||||
cancel_work_sync(&bdev->tx_work);
|
||||
|
||||
kfree_skb(bdev->rx_skb);
|
||||
bdev->rx_skb = NULL;
|
||||
|
||||
bdev->stp_cursor = 2;
|
||||
bdev->stp_dlen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtkuart_setup(struct hci_dev *hdev)
|
||||
{
|
||||
u8 param = 0x1;
|
||||
int err = 0;
|
||||
|
||||
/* Setup a firmware which the device definitely requires */
|
||||
err = mtk_setup_fw(hdev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Activate function the firmware providing to */
|
||||
err = mtk_hci_wmt_sync(hdev, MTK_WMT_RST, 0x4, 0, 0);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Enable Bluetooth protocol */
|
||||
err = mtk_hci_wmt_sync(hdev, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param),
|
||||
¶m);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtkuart_shutdown(struct hci_dev *hdev)
|
||||
{
|
||||
u8 param = 0x0;
|
||||
int err;
|
||||
|
||||
/* Disable the device */
|
||||
err = mtk_hci_wmt_sync(hdev, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param),
|
||||
¶m);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtkuart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
|
||||
struct mtk_stp_hdr *shdr;
|
||||
int err, dlen, type = 0;
|
||||
|
||||
/* Prepend skb with frame type */
|
||||
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
|
||||
|
||||
/* Make sure that there is enough rooms for STP header and trailer */
|
||||
if (unlikely(skb_headroom(skb) < sizeof(*shdr)) ||
|
||||
(skb_tailroom(skb) < MTK_STP_TLR_SIZE)) {
|
||||
err = pskb_expand_head(skb, sizeof(*shdr), MTK_STP_TLR_SIZE,
|
||||
GFP_ATOMIC);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Add the STP header */
|
||||
dlen = skb->len;
|
||||
shdr = skb_push(skb, sizeof(*shdr));
|
||||
shdr->prefix = 0x80;
|
||||
shdr->dlen = cpu_to_be16((dlen & 0x0fff) | (type << 12));
|
||||
shdr->cs = 0; /* MT7622 doesn't care about checksum value */
|
||||
|
||||
/* Add the STP trailer */
|
||||
skb_put_zero(skb, MTK_STP_TLR_SIZE);
|
||||
|
||||
skb_queue_tail(&bdev->txq, skb);
|
||||
|
||||
btmtkuart_tx_wakeup(bdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btmtkuart_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct btmtkuart_dev *bdev;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
bdev = devm_kzalloc(&serdev->dev, sizeof(*bdev), GFP_KERNEL);
|
||||
if (!bdev)
|
||||
return -ENOMEM;
|
||||
|
||||
bdev->clk = devm_clk_get(&serdev->dev, "ref");
|
||||
if (IS_ERR(bdev->clk))
|
||||
return PTR_ERR(bdev->clk);
|
||||
|
||||
bdev->serdev = serdev;
|
||||
serdev_device_set_drvdata(serdev, bdev);
|
||||
|
||||
serdev_device_set_client_ops(serdev, &btmtkuart_client_ops);
|
||||
|
||||
INIT_WORK(&bdev->tx_work, btmtkuart_tx_work);
|
||||
skb_queue_head_init(&bdev->txq);
|
||||
|
||||
/* Initialize and register HCI device */
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev) {
|
||||
dev_err(&serdev->dev, "Can't allocate HCI device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bdev->hdev = hdev;
|
||||
|
||||
hdev->bus = HCI_UART;
|
||||
hci_set_drvdata(hdev, bdev);
|
||||
|
||||
hdev->open = btmtkuart_open;
|
||||
hdev->close = btmtkuart_close;
|
||||
hdev->flush = btmtkuart_flush;
|
||||
hdev->setup = btmtkuart_setup;
|
||||
hdev->shutdown = btmtkuart_shutdown;
|
||||
hdev->send = btmtkuart_send_frame;
|
||||
SET_HCIDEV_DEV(hdev, &serdev->dev);
|
||||
|
||||
hdev->manufacturer = 70;
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
dev_err(&serdev->dev, "Can't register HCI device\n");
|
||||
hci_free_dev(hdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btmtkuart_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
|
||||
struct hci_dev *hdev = bdev->hdev;
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
hci_free_dev(hdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mtk_of_match_table[] = {
|
||||
{ .compatible = "mediatek,mt7622-bluetooth"},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_of_match_table);
|
||||
#endif
|
||||
|
||||
static struct serdev_device_driver btmtkuart_driver = {
|
||||
.probe = btmtkuart_probe,
|
||||
.remove = btmtkuart_remove,
|
||||
.driver = {
|
||||
.name = "btmtkuart",
|
||||
.of_match_table = of_match_ptr(mtk_of_match_table),
|
||||
},
|
||||
};
|
||||
|
||||
module_serdev_device_driver(btmtkuart_driver);
|
||||
|
||||
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
|
||||
MODULE_DESCRIPTION("MediaTek Bluetooth Serial driver ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7622);
|
@ -27,7 +27,7 @@
|
||||
|
||||
#define VERSION "0.1"
|
||||
|
||||
static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version)
|
||||
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct edl_event_hdr *edl;
|
||||
@ -35,36 +35,35 @@ static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version)
|
||||
char cmd;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("%s: ROME Patch Version Request", hdev->name);
|
||||
bt_dev_dbg(hdev, "QCA Version Request");
|
||||
|
||||
cmd = EDL_PATCH_VER_REQ_CMD;
|
||||
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
|
||||
&cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
|
||||
&cmd, HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name,
|
||||
err);
|
||||
bt_dev_err(hdev, "Reading QCA version information failed (%d)",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*edl) + sizeof(*ver)) {
|
||||
BT_ERR("%s: Version size mismatch len %d", hdev->name,
|
||||
skb->len);
|
||||
bt_dev_err(hdev, "QCA Version size mismatch len %d", skb->len);
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
edl = (struct edl_event_hdr *)(skb->data);
|
||||
if (!edl) {
|
||||
BT_ERR("%s: TLV with no header", hdev->name);
|
||||
bt_dev_err(hdev, "QCA TLV with no header");
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
|
||||
edl->rtype != EDL_APP_VER_RES_EVT) {
|
||||
BT_ERR("%s: Wrong packet received %d %d", hdev->name,
|
||||
edl->cresp, edl->rtype);
|
||||
bt_dev_err(hdev, "QCA Wrong packet received %d %d", edl->cresp,
|
||||
edl->rtype);
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
@ -76,30 +75,35 @@ static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version)
|
||||
BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rome_ver));
|
||||
BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id));
|
||||
|
||||
/* ROME chipset version can be decided by patch and SoC
|
||||
/* QCA chipset version can be decided by patch and SoC
|
||||
* version, combination with upper 2 bytes from SoC
|
||||
* and lower 2 bytes from patch will be used.
|
||||
*/
|
||||
*rome_version = (le32_to_cpu(ver->soc_id) << 16) |
|
||||
*soc_version = (le32_to_cpu(ver->soc_id) << 16) |
|
||||
(le16_to_cpu(ver->rome_ver) & 0x0000ffff);
|
||||
if (*soc_version == 0)
|
||||
err = -EILSEQ;
|
||||
|
||||
out:
|
||||
kfree_skb(skb);
|
||||
if (err)
|
||||
bt_dev_err(hdev, "QCA Failed to get version (%d)", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qca_read_soc_version);
|
||||
|
||||
static int rome_reset(struct hci_dev *hdev)
|
||||
static int qca_send_reset(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s: ROME HCI_RESET", hdev->name);
|
||||
bt_dev_dbg(hdev, "QCA HCI_RESET");
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
BT_ERR("%s: Reset failed (%d)", hdev->name, err);
|
||||
bt_dev_err(hdev, "QCA Reset failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -108,7 +112,7 @@ static int rome_reset(struct hci_dev *hdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rome_tlv_check_data(struct rome_config *config,
|
||||
static void qca_tlv_check_data(struct rome_config *config,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
const u8 *data;
|
||||
@ -207,7 +211,7 @@ static void rome_tlv_check_data(struct rome_config *config,
|
||||
}
|
||||
}
|
||||
|
||||
static int rome_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||
static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||
const u8 *data, enum rome_tlv_dnld_mode mode)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
@ -225,22 +229,22 @@ static int rome_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||
cmd);
|
||||
|
||||
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
|
||||
HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
|
||||
HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
BT_ERR("%s: Failed to send TLV segment (%d)", hdev->name, err);
|
||||
bt_dev_err(hdev, "QCA Failed to send TLV segment (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*edl) + sizeof(*tlv_resp)) {
|
||||
BT_ERR("%s: TLV response size mismatch", hdev->name);
|
||||
bt_dev_err(hdev, "QCA TLV response size mismatch");
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
edl = (struct edl_event_hdr *)(skb->data);
|
||||
if (!edl) {
|
||||
BT_ERR("%s: TLV with no header", hdev->name);
|
||||
bt_dev_err(hdev, "TLV with no header");
|
||||
err = -EILSEQ;
|
||||
goto out;
|
||||
}
|
||||
@ -249,8 +253,8 @@ static int rome_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||
|
||||
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
|
||||
edl->rtype != EDL_TVL_DNLD_RES_EVT || tlv_resp->result != 0x00) {
|
||||
BT_ERR("%s: TLV with error stat 0x%x rtype 0x%x (0x%x)",
|
||||
hdev->name, edl->cresp, edl->rtype, tlv_resp->result);
|
||||
bt_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x (0x%x)",
|
||||
edl->cresp, edl->rtype, tlv_resp->result);
|
||||
err = -EIO;
|
||||
}
|
||||
|
||||
@ -260,23 +264,23 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rome_download_firmware(struct hci_dev *hdev,
|
||||
static int qca_download_firmware(struct hci_dev *hdev,
|
||||
struct rome_config *config)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
const u8 *segment;
|
||||
int ret, remain, i = 0;
|
||||
|
||||
bt_dev_info(hdev, "ROME Downloading %s", config->fwname);
|
||||
bt_dev_info(hdev, "QCA Downloading %s", config->fwname);
|
||||
|
||||
ret = request_firmware(&fw, config->fwname, &hdev->dev);
|
||||
if (ret) {
|
||||
BT_ERR("%s: Failed to request file: %s (%d)", hdev->name,
|
||||
config->fwname, ret);
|
||||
bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
|
||||
config->fwname, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rome_tlv_check_data(config, fw);
|
||||
qca_tlv_check_data(config, fw);
|
||||
|
||||
segment = fw->data;
|
||||
remain = fw->size;
|
||||
@ -290,7 +294,7 @@ static int rome_download_firmware(struct hci_dev *hdev,
|
||||
if (!remain || segsize < MAX_SIZE_PER_TLV_SEGMENT)
|
||||
config->dnld_mode = ROME_SKIP_EVT_NONE;
|
||||
|
||||
ret = rome_tlv_send_segment(hdev, segsize, segment,
|
||||
ret = qca_tlv_send_segment(hdev, segsize, segment,
|
||||
config->dnld_mode);
|
||||
if (ret)
|
||||
break;
|
||||
@ -314,11 +318,10 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
cmd[2] = sizeof(bdaddr_t); /* size */
|
||||
memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t));
|
||||
skb = __hci_cmd_sync_ev(hdev, EDL_NVM_ACCESS_OPCODE, sizeof(cmd), cmd,
|
||||
HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
|
||||
HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
BT_ERR("%s: Change address command failed (%d)",
|
||||
hdev->name, err);
|
||||
bt_dev_err(hdev, "QCA Change address command failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -328,57 +331,65 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
|
||||
|
||||
int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate)
|
||||
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver)
|
||||
{
|
||||
u32 rome_ver = 0;
|
||||
struct rome_config config;
|
||||
int err;
|
||||
u8 rom_ver;
|
||||
|
||||
BT_DBG("%s: ROME setup on UART", hdev->name);
|
||||
bt_dev_dbg(hdev, "QCA setup on UART");
|
||||
|
||||
config.user_baud_rate = baudrate;
|
||||
|
||||
/* Get ROME version information */
|
||||
err = rome_patch_ver_req(hdev, &rome_ver);
|
||||
if (err < 0 || rome_ver == 0) {
|
||||
BT_ERR("%s: Failed to get version 0x%x", hdev->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "ROME controller version 0x%08x", rome_ver);
|
||||
|
||||
/* Download rampatch file */
|
||||
config.type = TLV_TYPE_PATCH;
|
||||
snprintf(config.fwname, sizeof(config.fwname), "qca/rampatch_%08x.bin",
|
||||
rome_ver);
|
||||
err = rome_download_firmware(hdev, &config);
|
||||
if (soc_type == QCA_WCN3990) {
|
||||
/* Firmware files to download are based on ROM version.
|
||||
* ROM version is derived from last two bytes of soc_ver.
|
||||
*/
|
||||
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) |
|
||||
(soc_ver & 0x0000000f);
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/crbtfw%02x.tlv", rom_ver);
|
||||
} else {
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/rampatch_%08x.bin", soc_ver);
|
||||
}
|
||||
|
||||
err = qca_download_firmware(hdev, &config);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to download patch (%d)", hdev->name, err);
|
||||
bt_dev_err(hdev, "QCA Failed to download patch (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Download NVM configuration */
|
||||
config.type = TLV_TYPE_NVM;
|
||||
snprintf(config.fwname, sizeof(config.fwname), "qca/nvm_%08x.bin",
|
||||
rome_ver);
|
||||
err = rome_download_firmware(hdev, &config);
|
||||
if (soc_type == QCA_WCN3990)
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/crnv%02x.bin", rom_ver);
|
||||
else
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/nvm_%08x.bin", soc_ver);
|
||||
|
||||
err = qca_download_firmware(hdev, &config);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to download NVM (%d)", hdev->name, err);
|
||||
bt_dev_err(hdev, "QCA Failed to download NVM (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Perform HCI reset */
|
||||
err = rome_reset(hdev);
|
||||
err = qca_send_reset(hdev);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err);
|
||||
bt_dev_err(hdev, "QCA Failed to run HCI_RESET (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "ROME setup on UART is completed");
|
||||
bt_dev_info(hdev, "QCA setup on UART is completed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qca_uart_setup_rome);
|
||||
EXPORT_SYMBOL_GPL(qca_uart_setup);
|
||||
|
||||
MODULE_AUTHOR("Ben Young Tae Kim <ytkim@qca.qualcomm.com>");
|
||||
MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family ver " VERSION);
|
||||
|
@ -37,6 +37,9 @@
|
||||
#define EDL_TAG_ID_HCI (17)
|
||||
#define EDL_TAG_ID_DEEP_SLEEP (27)
|
||||
|
||||
#define QCA_WCN3990_POWERON_PULSE 0xFC
|
||||
#define QCA_WCN3990_POWEROFF_PULSE 0xC0
|
||||
|
||||
enum qca_bardrate {
|
||||
QCA_BAUDRATE_115200 = 0,
|
||||
QCA_BAUDRATE_57600,
|
||||
@ -124,10 +127,19 @@ struct tlv_type_hdr {
|
||||
__u8 data[0];
|
||||
} __packed;
|
||||
|
||||
enum qca_btsoc_type {
|
||||
QCA_INVALID = -1,
|
||||
QCA_AR3002,
|
||||
QCA_ROME,
|
||||
QCA_WCN3990
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_QCA)
|
||||
|
||||
int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||
int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate);
|
||||
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver);
|
||||
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version);
|
||||
|
||||
#else
|
||||
|
||||
@ -136,7 +148,13 @@ static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdad
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int qca_uart_setup_rome(struct hci_dev *hdev, int speed)
|
||||
static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -34,9 +34,12 @@
|
||||
#define RTL_ROM_LMP_8821A 0x8821
|
||||
#define RTL_ROM_LMP_8761A 0x8761
|
||||
#define RTL_ROM_LMP_8822B 0x8822
|
||||
#define RTL_CONFIG_MAGIC 0x8723ab55
|
||||
|
||||
#define IC_MATCH_FL_LMPSUBV (1 << 0)
|
||||
#define IC_MATCH_FL_HCIREV (1 << 1)
|
||||
#define IC_MATCH_FL_HCIVER (1 << 2)
|
||||
#define IC_MATCH_FL_HCIBUS (1 << 3)
|
||||
#define IC_INFO(lmps, hcir) \
|
||||
.match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV, \
|
||||
.lmp_subver = (lmps), \
|
||||
@ -46,49 +49,130 @@ struct id_table {
|
||||
__u16 match_flags;
|
||||
__u16 lmp_subver;
|
||||
__u16 hci_rev;
|
||||
__u8 hci_ver;
|
||||
__u8 hci_bus;
|
||||
bool config_needed;
|
||||
bool has_rom_version;
|
||||
char *fw_name;
|
||||
char *cfg_name;
|
||||
};
|
||||
|
||||
struct btrtl_device_info {
|
||||
const struct id_table *ic_info;
|
||||
u8 rom_version;
|
||||
u8 *fw_data;
|
||||
int fw_len;
|
||||
u8 *cfg_data;
|
||||
int cfg_len;
|
||||
};
|
||||
|
||||
static const struct id_table ic_id_table[] = {
|
||||
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8723A, 0x0,
|
||||
.config_needed = false,
|
||||
.has_rom_version = false,
|
||||
.fw_name = "rtl_bt/rtl8723a_fw.bin",
|
||||
.cfg_name = NULL },
|
||||
|
||||
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_3499, 0x0,
|
||||
.config_needed = false,
|
||||
.has_rom_version = false,
|
||||
.fw_name = "rtl_bt/rtl8723a_fw.bin",
|
||||
.cfg_name = NULL },
|
||||
|
||||
/* 8723BS */
|
||||
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
|
||||
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS,
|
||||
.lmp_subver = RTL_ROM_LMP_8723B,
|
||||
.hci_rev = 0xb,
|
||||
.hci_ver = 6,
|
||||
.hci_bus = HCI_UART,
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723bs_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723bs_config" },
|
||||
|
||||
/* 8723B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723b_config.bin" },
|
||||
.cfg_name = "rtl_bt/rtl8723b_config" },
|
||||
|
||||
/* 8723D */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723d_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723d_config.bin" },
|
||||
.cfg_name = "rtl_bt/rtl8723d_config" },
|
||||
|
||||
/* 8723DS */
|
||||
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
|
||||
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS,
|
||||
.lmp_subver = RTL_ROM_LMP_8723B,
|
||||
.hci_rev = 0xd,
|
||||
.hci_ver = 8,
|
||||
.hci_bus = HCI_UART,
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723ds_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723ds_config" },
|
||||
|
||||
/* 8821A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8821a_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8821a_config.bin" },
|
||||
.cfg_name = "rtl_bt/rtl8821a_config" },
|
||||
|
||||
/* 8821C */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc),
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8821c_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8821c_config.bin" },
|
||||
.cfg_name = "rtl_bt/rtl8821c_config" },
|
||||
|
||||
/* 8761A */
|
||||
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8761A, 0x0,
|
||||
.config_needed = false,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8761a_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8761a_config.bin" },
|
||||
.cfg_name = "rtl_bt/rtl8761a_config" },
|
||||
|
||||
/* 8822B */
|
||||
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8822b_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8822b_config.bin" },
|
||||
.cfg_name = "rtl_bt/rtl8822b_config" },
|
||||
};
|
||||
|
||||
static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
|
||||
u8 hci_ver, u8 hci_bus)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) {
|
||||
if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) &&
|
||||
(ic_id_table[i].lmp_subver != lmp_subver))
|
||||
continue;
|
||||
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) &&
|
||||
(ic_id_table[i].hci_rev != hci_rev))
|
||||
continue;
|
||||
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) &&
|
||||
(ic_id_table[i].hci_ver != hci_ver))
|
||||
continue;
|
||||
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) &&
|
||||
(ic_id_table[i].hci_bus != hci_bus))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
if (i >= ARRAY_SIZE(ic_id_table))
|
||||
return NULL;
|
||||
|
||||
return &ic_id_table[i];
|
||||
}
|
||||
|
||||
static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
|
||||
{
|
||||
struct rtl_rom_version_evt *rom_version;
|
||||
@ -97,20 +181,20 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
|
||||
/* Read RTL ROM version command */
|
||||
skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: Read ROM version failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
rtl_dev_err(hdev, "Read ROM version failed (%ld)\n",
|
||||
PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*rom_version)) {
|
||||
BT_ERR("%s: RTL version event length mismatch", hdev->name);
|
||||
rtl_dev_err(hdev, "RTL version event length mismatch\n");
|
||||
kfree_skb(skb);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rom_version = (struct rtl_rom_version_evt *)skb->data;
|
||||
bt_dev_info(hdev, "rom_version status=%x version=%x",
|
||||
rom_version->status, rom_version->version);
|
||||
rtl_dev_info(hdev, "rom_version status=%x version=%x\n",
|
||||
rom_version->status, rom_version->version);
|
||||
|
||||
*version = rom_version->version;
|
||||
|
||||
@ -118,16 +202,16 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
const struct firmware *fw,
|
||||
static int rtlbt_parse_firmware(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev,
|
||||
unsigned char **_buf)
|
||||
{
|
||||
const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
|
||||
struct rtl_epatch_header *epatch_info;
|
||||
unsigned char *buf;
|
||||
int i, ret, len;
|
||||
int i, len;
|
||||
size_t min_size;
|
||||
u8 opcode, length, data, rom_version = 0;
|
||||
u8 opcode, length, data;
|
||||
int project_id = -1;
|
||||
const unsigned char *fwptr, *chip_id_base;
|
||||
const unsigned char *patch_length_base, *patch_offset_base;
|
||||
@ -146,17 +230,13 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
{ RTL_ROM_LMP_8821A, 10 }, /* 8821C */
|
||||
};
|
||||
|
||||
ret = rtl_read_rom_version(hdev, &rom_version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
|
||||
if (fw->size < min_size)
|
||||
if (btrtl_dev->fw_len < min_size)
|
||||
return -EINVAL;
|
||||
|
||||
fwptr = fw->data + fw->size - sizeof(extension_sig);
|
||||
fwptr = btrtl_dev->fw_data + btrtl_dev->fw_len - sizeof(extension_sig);
|
||||
if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
|
||||
BT_ERR("%s: extension section signature mismatch", hdev->name);
|
||||
rtl_dev_err(hdev, "extension section signature mismatch\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -166,7 +246,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
* Once we have that, we double-check that that project_id is suitable
|
||||
* for the hardware we are working with.
|
||||
*/
|
||||
while (fwptr >= fw->data + (sizeof(struct rtl_epatch_header) + 3)) {
|
||||
while (fwptr >= btrtl_dev->fw_data + (sizeof(*epatch_info) + 3)) {
|
||||
opcode = *--fwptr;
|
||||
length = *--fwptr;
|
||||
data = *--fwptr;
|
||||
@ -177,8 +257,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
break;
|
||||
|
||||
if (length == 0) {
|
||||
BT_ERR("%s: found instruction with length 0",
|
||||
hdev->name);
|
||||
rtl_dev_err(hdev, "found instruction with length 0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -191,7 +270,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
}
|
||||
|
||||
if (project_id < 0) {
|
||||
BT_ERR("%s: failed to find version instruction", hdev->name);
|
||||
rtl_dev_err(hdev, "failed to find version instruction\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -202,19 +281,21 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) {
|
||||
BT_ERR("%s: unknown project id %d", hdev->name, project_id);
|
||||
rtl_dev_err(hdev, "unknown project id %d\n", project_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (lmp_subver != project_id_to_lmp_subver[i].lmp_subver) {
|
||||
BT_ERR("%s: firmware is for %x but this is a %x", hdev->name,
|
||||
project_id_to_lmp_subver[i].lmp_subver, lmp_subver);
|
||||
if (btrtl_dev->ic_info->lmp_subver !=
|
||||
project_id_to_lmp_subver[i].lmp_subver) {
|
||||
rtl_dev_err(hdev, "firmware is for %x but this is a %x\n",
|
||||
project_id_to_lmp_subver[i].lmp_subver,
|
||||
btrtl_dev->ic_info->lmp_subver);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
epatch_info = (struct rtl_epatch_header *)fw->data;
|
||||
epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data;
|
||||
if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) {
|
||||
BT_ERR("%s: bad EPATCH signature", hdev->name);
|
||||
rtl_dev_err(hdev, "bad EPATCH signature\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -229,16 +310,16 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
* Find the right patch for this chip.
|
||||
*/
|
||||
min_size += 8 * num_patches;
|
||||
if (fw->size < min_size)
|
||||
if (btrtl_dev->fw_len < min_size)
|
||||
return -EINVAL;
|
||||
|
||||
chip_id_base = fw->data + sizeof(struct rtl_epatch_header);
|
||||
chip_id_base = btrtl_dev->fw_data + sizeof(struct rtl_epatch_header);
|
||||
patch_length_base = chip_id_base + (sizeof(u16) * num_patches);
|
||||
patch_offset_base = patch_length_base + (sizeof(u16) * num_patches);
|
||||
for (i = 0; i < num_patches; i++) {
|
||||
u16 chip_id = get_unaligned_le16(chip_id_base +
|
||||
(i * sizeof(u16)));
|
||||
if (chip_id == rom_version + 1) {
|
||||
if (chip_id == btrtl_dev->rom_version + 1) {
|
||||
patch_length = get_unaligned_le16(patch_length_base +
|
||||
(i * sizeof(u16)));
|
||||
patch_offset = get_unaligned_le32(patch_offset_base +
|
||||
@ -248,21 +329,22 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
|
||||
}
|
||||
|
||||
if (!patch_offset) {
|
||||
BT_ERR("%s: didn't find patch for chip id %d",
|
||||
hdev->name, rom_version);
|
||||
rtl_dev_err(hdev, "didn't find patch for chip id %d",
|
||||
btrtl_dev->rom_version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
|
||||
min_size = patch_offset + patch_length;
|
||||
if (fw->size < min_size)
|
||||
if (btrtl_dev->fw_len < min_size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Copy the firmware into a new buffer and write the version at
|
||||
* the end.
|
||||
*/
|
||||
len = patch_length;
|
||||
buf = kmemdup(fw->data + patch_offset, patch_length, GFP_KERNEL);
|
||||
buf = kmemdup(btrtl_dev->fw_data + patch_offset, patch_length,
|
||||
GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -301,15 +383,14 @@ static int rtl_download_firmware(struct hci_dev *hdev,
|
||||
skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: download fw command failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
rtl_dev_err(hdev, "download fw command failed (%ld)\n",
|
||||
PTR_ERR(skb));
|
||||
ret = -PTR_ERR(skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(struct rtl_download_response)) {
|
||||
BT_ERR("%s: download fw event length mismatch",
|
||||
hdev->name);
|
||||
rtl_dev_err(hdev, "download fw event length mismatch\n");
|
||||
kfree_skb(skb);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
@ -324,12 +405,12 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff)
|
||||
static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
int ret;
|
||||
|
||||
bt_dev_info(hdev, "rtl: loading %s", name);
|
||||
rtl_dev_info(hdev, "rtl: loading %s\n", name);
|
||||
ret = request_firmware(&fw, name, &hdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -343,96 +424,37 @@ static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btrtl_setup_rtl8723a(struct hci_dev *hdev)
|
||||
static int btrtl_setup_rtl8723a(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
int ret;
|
||||
|
||||
bt_dev_info(hdev, "rtl: loading rtl_bt/rtl8723a_fw.bin");
|
||||
ret = request_firmware(&fw, "rtl_bt/rtl8723a_fw.bin", &hdev->dev);
|
||||
if (ret < 0) {
|
||||
BT_ERR("%s: Failed to load rtl_bt/rtl8723a_fw.bin", hdev->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fw->size < 8) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (btrtl_dev->fw_len < 8)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check that the firmware doesn't have the epatch signature
|
||||
* (which is only for RTL8723B and newer).
|
||||
*/
|
||||
if (!memcmp(fw->data, RTL_EPATCH_SIGNATURE, 8)) {
|
||||
BT_ERR("%s: unexpected EPATCH signature!", hdev->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = rtl_download_firmware(hdev, fw->data, fw->size);
|
||||
|
||||
out:
|
||||
release_firmware(fw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 hci_rev,
|
||||
u16 lmp_subver)
|
||||
{
|
||||
unsigned char *fw_data = NULL;
|
||||
const struct firmware *fw;
|
||||
int ret;
|
||||
int cfg_sz;
|
||||
u8 *cfg_buff = NULL;
|
||||
u8 *tbuff;
|
||||
char *cfg_name = NULL;
|
||||
char *fw_name = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) {
|
||||
if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) &&
|
||||
(ic_id_table[i].lmp_subver != lmp_subver))
|
||||
continue;
|
||||
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) &&
|
||||
(ic_id_table[i].hci_rev != hci_rev))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= ARRAY_SIZE(ic_id_table)) {
|
||||
BT_ERR("%s: unknown IC info, lmp subver %04x, hci rev %04x",
|
||||
hdev->name, lmp_subver, hci_rev);
|
||||
if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8)) {
|
||||
rtl_dev_err(hdev, "unexpected EPATCH signature!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cfg_name = ic_id_table[i].cfg_name;
|
||||
return rtl_download_firmware(hdev, btrtl_dev->fw_data,
|
||||
btrtl_dev->fw_len);
|
||||
}
|
||||
|
||||
if (cfg_name) {
|
||||
cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff);
|
||||
if (cfg_sz < 0) {
|
||||
cfg_sz = 0;
|
||||
if (ic_id_table[i].config_needed)
|
||||
BT_ERR("Necessary config file %s not found\n",
|
||||
cfg_name);
|
||||
}
|
||||
} else
|
||||
cfg_sz = 0;
|
||||
static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev)
|
||||
{
|
||||
unsigned char *fw_data = NULL;
|
||||
int ret;
|
||||
u8 *tbuff;
|
||||
|
||||
fw_name = ic_id_table[i].fw_name;
|
||||
bt_dev_info(hdev, "rtl: loading %s", fw_name);
|
||||
ret = request_firmware(&fw, fw_name, &hdev->dev);
|
||||
if (ret < 0) {
|
||||
BT_ERR("%s: Failed to load %s", hdev->name, fw_name);
|
||||
goto err_req_fw;
|
||||
}
|
||||
|
||||
ret = rtlbt_parse_firmware(hdev, lmp_subver, fw, &fw_data);
|
||||
ret = rtlbt_parse_firmware(hdev, btrtl_dev, &fw_data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (cfg_sz) {
|
||||
tbuff = kzalloc(ret + cfg_sz, GFP_KERNEL);
|
||||
if (btrtl_dev->cfg_len > 0) {
|
||||
tbuff = kzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
|
||||
if (!tbuff) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
@ -441,22 +463,18 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 hci_rev,
|
||||
memcpy(tbuff, fw_data, ret);
|
||||
kfree(fw_data);
|
||||
|
||||
memcpy(tbuff + ret, cfg_buff, cfg_sz);
|
||||
ret += cfg_sz;
|
||||
memcpy(tbuff + ret, btrtl_dev->cfg_data, btrtl_dev->cfg_len);
|
||||
ret += btrtl_dev->cfg_len;
|
||||
|
||||
fw_data = tbuff;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "cfg_sz %d, total size %d", cfg_sz, ret);
|
||||
rtl_dev_info(hdev, "cfg_sz %d, total sz %d\n", btrtl_dev->cfg_len, ret);
|
||||
|
||||
ret = rtl_download_firmware(hdev, fw_data, ret);
|
||||
|
||||
out:
|
||||
release_firmware(fw);
|
||||
kfree(fw_data);
|
||||
err_req_fw:
|
||||
if (cfg_sz)
|
||||
kfree(cfg_buff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -467,14 +485,13 @@ static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION failed (%ld)\n",
|
||||
PTR_ERR(skb));
|
||||
return skb;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(struct hci_rp_read_local_version)) {
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
|
||||
hdev->name);
|
||||
rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION event length mismatch\n");
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
@ -482,49 +499,264 @@ static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
|
||||
return skb;
|
||||
}
|
||||
|
||||
int btrtl_setup_realtek(struct hci_dev *hdev)
|
||||
void btrtl_free(struct btrtl_device_info *btrtl_dev)
|
||||
{
|
||||
kfree(btrtl_dev->fw_data);
|
||||
kfree(btrtl_dev->cfg_data);
|
||||
kfree(btrtl_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btrtl_free);
|
||||
|
||||
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
|
||||
const char *postfix)
|
||||
{
|
||||
struct btrtl_device_info *btrtl_dev;
|
||||
struct sk_buff *skb;
|
||||
struct hci_rp_read_local_version *resp;
|
||||
char cfg_name[40];
|
||||
u16 hci_rev, lmp_subver;
|
||||
u8 hci_ver;
|
||||
int ret;
|
||||
|
||||
btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
|
||||
if (!btrtl_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
skb = btrtl_read_local_version(hdev);
|
||||
if (IS_ERR(skb))
|
||||
return -PTR_ERR(skb);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
resp = (struct hci_rp_read_local_version *)skb->data;
|
||||
bt_dev_info(hdev, "rtl: examining hci_ver=%02x hci_rev=%04x "
|
||||
"lmp_ver=%02x lmp_subver=%04x",
|
||||
resp->hci_ver, resp->hci_rev,
|
||||
resp->lmp_ver, resp->lmp_subver);
|
||||
rtl_dev_info(hdev, "rtl: examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x\n",
|
||||
resp->hci_ver, resp->hci_rev,
|
||||
resp->lmp_ver, resp->lmp_subver);
|
||||
|
||||
hci_ver = resp->hci_ver;
|
||||
hci_rev = le16_to_cpu(resp->hci_rev);
|
||||
lmp_subver = le16_to_cpu(resp->lmp_subver);
|
||||
kfree_skb(skb);
|
||||
|
||||
btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
|
||||
hdev->bus);
|
||||
|
||||
if (!btrtl_dev->ic_info) {
|
||||
rtl_dev_err(hdev, "rtl: unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x",
|
||||
lmp_subver, hci_rev, hci_ver);
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (btrtl_dev->ic_info->has_rom_version) {
|
||||
ret = rtl_read_rom_version(hdev, &btrtl_dev->rom_version);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
btrtl_dev->fw_len = rtl_load_file(hdev, btrtl_dev->ic_info->fw_name,
|
||||
&btrtl_dev->fw_data);
|
||||
if (btrtl_dev->fw_len < 0) {
|
||||
rtl_dev_err(hdev, "firmware file %s not found\n",
|
||||
btrtl_dev->ic_info->fw_name);
|
||||
ret = btrtl_dev->fw_len;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (btrtl_dev->ic_info->cfg_name) {
|
||||
if (postfix) {
|
||||
snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin",
|
||||
btrtl_dev->ic_info->cfg_name, postfix);
|
||||
} else {
|
||||
snprintf(cfg_name, sizeof(cfg_name), "%s.bin",
|
||||
btrtl_dev->ic_info->cfg_name);
|
||||
}
|
||||
btrtl_dev->cfg_len = rtl_load_file(hdev, cfg_name,
|
||||
&btrtl_dev->cfg_data);
|
||||
if (btrtl_dev->ic_info->config_needed &&
|
||||
btrtl_dev->cfg_len <= 0) {
|
||||
rtl_dev_err(hdev, "mandatory config file %s not found\n",
|
||||
btrtl_dev->ic_info->cfg_name);
|
||||
ret = btrtl_dev->cfg_len;
|
||||
goto err_free;
|
||||
}
|
||||
}
|
||||
|
||||
return btrtl_dev;
|
||||
|
||||
err_free:
|
||||
btrtl_free(btrtl_dev);
|
||||
err_alloc:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btrtl_initialize);
|
||||
|
||||
int btrtl_download_firmware(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev)
|
||||
{
|
||||
/* Match a set of subver values that correspond to stock firmware,
|
||||
* which is not compatible with standard btusb.
|
||||
* If matched, upload an alternative firmware that does conform to
|
||||
* standard btusb. Once that firmware is uploaded, the subver changes
|
||||
* to a different value.
|
||||
*/
|
||||
switch (lmp_subver) {
|
||||
switch (btrtl_dev->ic_info->lmp_subver) {
|
||||
case RTL_ROM_LMP_8723A:
|
||||
case RTL_ROM_LMP_3499:
|
||||
return btrtl_setup_rtl8723a(hdev);
|
||||
return btrtl_setup_rtl8723a(hdev, btrtl_dev);
|
||||
case RTL_ROM_LMP_8723B:
|
||||
case RTL_ROM_LMP_8821A:
|
||||
case RTL_ROM_LMP_8761A:
|
||||
case RTL_ROM_LMP_8822B:
|
||||
return btrtl_setup_rtl8723b(hdev, hci_rev, lmp_subver);
|
||||
return btrtl_setup_rtl8723b(hdev, btrtl_dev);
|
||||
default:
|
||||
bt_dev_info(hdev, "rtl: assuming no firmware upload needed");
|
||||
rtl_dev_info(hdev, "rtl: assuming no firmware upload needed\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btrtl_download_firmware);
|
||||
|
||||
int btrtl_setup_realtek(struct hci_dev *hdev)
|
||||
{
|
||||
struct btrtl_device_info *btrtl_dev;
|
||||
int ret;
|
||||
|
||||
btrtl_dev = btrtl_initialize(hdev, NULL);
|
||||
if (IS_ERR(btrtl_dev))
|
||||
return PTR_ERR(btrtl_dev);
|
||||
|
||||
ret = btrtl_download_firmware(hdev, btrtl_dev);
|
||||
|
||||
btrtl_free(btrtl_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
|
||||
|
||||
static unsigned int btrtl_convert_baudrate(u32 device_baudrate)
|
||||
{
|
||||
switch (device_baudrate) {
|
||||
case 0x0252a00a:
|
||||
return 230400;
|
||||
|
||||
case 0x05f75004:
|
||||
return 921600;
|
||||
|
||||
case 0x00005004:
|
||||
return 1000000;
|
||||
|
||||
case 0x04928002:
|
||||
case 0x01128002:
|
||||
return 1500000;
|
||||
|
||||
case 0x00005002:
|
||||
return 2000000;
|
||||
|
||||
case 0x0000b001:
|
||||
return 2500000;
|
||||
|
||||
case 0x04928001:
|
||||
return 3000000;
|
||||
|
||||
case 0x052a6001:
|
||||
return 3500000;
|
||||
|
||||
case 0x00005001:
|
||||
return 4000000;
|
||||
|
||||
case 0x0252c014:
|
||||
default:
|
||||
return 115200;
|
||||
}
|
||||
}
|
||||
|
||||
int btrtl_get_uart_settings(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev,
|
||||
unsigned int *controller_baudrate,
|
||||
u32 *device_baudrate, bool *flow_control)
|
||||
{
|
||||
struct rtl_vendor_config *config;
|
||||
struct rtl_vendor_config_entry *entry;
|
||||
int i, total_data_len;
|
||||
bool found = false;
|
||||
|
||||
total_data_len = btrtl_dev->cfg_len - sizeof(*config);
|
||||
if (total_data_len <= 0) {
|
||||
rtl_dev_warn(hdev, "no config loaded\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config = (struct rtl_vendor_config *)btrtl_dev->cfg_data;
|
||||
if (le32_to_cpu(config->signature) != RTL_CONFIG_MAGIC) {
|
||||
rtl_dev_err(hdev, "invalid config magic\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (total_data_len < le16_to_cpu(config->total_len)) {
|
||||
rtl_dev_err(hdev, "config is too short\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < total_data_len; ) {
|
||||
entry = ((void *)config->entry) + i;
|
||||
|
||||
switch (le16_to_cpu(entry->offset)) {
|
||||
case 0xc:
|
||||
if (entry->len < sizeof(*device_baudrate)) {
|
||||
rtl_dev_err(hdev, "invalid UART config entry\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*device_baudrate = get_unaligned_le32(entry->data);
|
||||
*controller_baudrate = btrtl_convert_baudrate(
|
||||
*device_baudrate);
|
||||
|
||||
if (entry->len >= 13)
|
||||
*flow_control = !!(entry->data[12] & BIT(2));
|
||||
else
|
||||
*flow_control = false;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
rtl_dev_dbg(hdev, "skipping config entry 0x%x (len %u)\n",
|
||||
le16_to_cpu(entry->offset), entry->len);
|
||||
break;
|
||||
};
|
||||
|
||||
i += sizeof(*entry) + entry->len;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
rtl_dev_err(hdev, "no UART config entry found\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
rtl_dev_dbg(hdev, "device baudrate = 0x%08x\n", *device_baudrate);
|
||||
rtl_dev_dbg(hdev, "controller baudrate = %u\n", *controller_baudrate);
|
||||
rtl_dev_dbg(hdev, "flow control %d\n", *flow_control);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btrtl_get_uart_settings);
|
||||
|
||||
MODULE_AUTHOR("Daniel Drake <drake@endlessm.com>");
|
||||
MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8723a_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8761a_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin");
|
||||
MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin");
|
||||
|
@ -17,6 +17,13 @@
|
||||
|
||||
#define RTL_FRAG_LEN 252
|
||||
|
||||
#define rtl_dev_err(dev, fmt, ...) bt_dev_err(dev, "RTL: " fmt, ##__VA_ARGS__)
|
||||
#define rtl_dev_warn(dev, fmt, ...) bt_dev_warn(dev, "RTL: " fmt, ##__VA_ARGS__)
|
||||
#define rtl_dev_info(dev, fmt, ...) bt_dev_info(dev, "RTL: " fmt, ##__VA_ARGS__)
|
||||
#define rtl_dev_dbg(dev, fmt, ...) bt_dev_dbg(dev, "RTL: " fmt, ##__VA_ARGS__)
|
||||
|
||||
struct btrtl_device_info;
|
||||
|
||||
struct rtl_download_cmd {
|
||||
__u8 index;
|
||||
__u8 data[RTL_FRAG_LEN];
|
||||
@ -38,15 +45,61 @@ struct rtl_epatch_header {
|
||||
__le16 num_patches;
|
||||
} __packed;
|
||||
|
||||
struct rtl_vendor_config_entry {
|
||||
__le16 offset;
|
||||
__u8 len;
|
||||
__u8 data[0];
|
||||
} __packed;
|
||||
|
||||
struct rtl_vendor_config {
|
||||
__le32 signature;
|
||||
__le16 total_len;
|
||||
struct rtl_vendor_config_entry entry[0];
|
||||
} __packed;
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_RTL)
|
||||
|
||||
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
|
||||
const char *postfix);
|
||||
void btrtl_free(struct btrtl_device_info *btrtl_dev);
|
||||
int btrtl_download_firmware(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev);
|
||||
int btrtl_setup_realtek(struct hci_dev *hdev);
|
||||
int btrtl_get_uart_settings(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev,
|
||||
unsigned int *controller_baudrate,
|
||||
u32 *device_baudrate, bool *flow_control);
|
||||
|
||||
#else
|
||||
|
||||
static inline struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
|
||||
const char *postfix)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
static inline void btrtl_free(struct btrtl_device_info *btrtl_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int btrtl_download_firmware(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int btrtl_setup_realtek(struct hci_dev *hdev)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int btrtl_get_uart_settings(struct hci_dev *hdev,
|
||||
struct btrtl_device_info *btrtl_dev,
|
||||
unsigned int *controller_baudrate,
|
||||
u32 *device_baudrate,
|
||||
bool *flow_control)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -374,6 +374,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x7392, 0xa611), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Additional Realtek 8723DE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0bda, 0xb009), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x2ff8, 0xb011), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Additional Realtek 8821AE Bluetooth devices */
|
||||
@ -509,9 +510,10 @@ static inline void btusb_free_frags(struct btusb_data *data)
|
||||
static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
spin_lock(&data->rxlock);
|
||||
spin_lock_irqsave(&data->rxlock, flags);
|
||||
skb = data->evt_skb;
|
||||
|
||||
while (count) {
|
||||
@ -556,7 +558,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
||||
}
|
||||
|
||||
data->evt_skb = skb;
|
||||
spin_unlock(&data->rxlock);
|
||||
spin_unlock_irqrestore(&data->rxlock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -564,9 +566,10 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
||||
static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
spin_lock(&data->rxlock);
|
||||
spin_lock_irqsave(&data->rxlock, flags);
|
||||
skb = data->acl_skb;
|
||||
|
||||
while (count) {
|
||||
@ -613,7 +616,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
||||
}
|
||||
|
||||
data->acl_skb = skb;
|
||||
spin_unlock(&data->rxlock);
|
||||
spin_unlock_irqrestore(&data->rxlock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -621,9 +624,10 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
||||
static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
spin_lock(&data->rxlock);
|
||||
spin_lock_irqsave(&data->rxlock, flags);
|
||||
skb = data->sco_skb;
|
||||
|
||||
while (count) {
|
||||
@ -668,7 +672,7 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
|
||||
}
|
||||
|
||||
data->sco_skb = skb;
|
||||
spin_unlock(&data->rxlock);
|
||||
spin_unlock_irqrestore(&data->rxlock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1066,6 +1070,7 @@ static void btusb_tx_complete(struct urb *urb)
|
||||
struct sk_buff *skb = urb->context;
|
||||
struct hci_dev *hdev = (struct hci_dev *)skb->dev;
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
unsigned long flags;
|
||||
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
|
||||
urb->actual_length);
|
||||
@ -1079,9 +1084,9 @@ static void btusb_tx_complete(struct urb *urb)
|
||||
hdev->stat.err_tx++;
|
||||
|
||||
done:
|
||||
spin_lock(&data->txlock);
|
||||
spin_lock_irqsave(&data->txlock, flags);
|
||||
data->tx_in_flight--;
|
||||
spin_unlock(&data->txlock);
|
||||
spin_unlock_irqrestore(&data->txlock, flags);
|
||||
|
||||
kfree(urb->setup_packet);
|
||||
|
||||
@ -1593,13 +1598,13 @@ static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
|
||||
ret = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (ret < 0) {
|
||||
if (ret == -EINVAL) {
|
||||
BT_ERR("%s Intel firmware file request failed (%d)",
|
||||
hdev->name, ret);
|
||||
bt_dev_err(hdev, "Intel firmware file request failed (%d)",
|
||||
ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BT_ERR("%s failed to open Intel firmware file: %s(%d)",
|
||||
hdev->name, fwname, ret);
|
||||
bt_dev_err(hdev, "failed to open Intel firmware file: %s (%d)",
|
||||
fwname, ret);
|
||||
|
||||
/* If the correct firmware patch file is not found, use the
|
||||
* default firmware patch file instead
|
||||
@ -1607,8 +1612,8 @@ static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-hw-%x.%x.bseq",
|
||||
ver->hw_platform, ver->hw_variant);
|
||||
if (request_firmware(&fw, fwname, &hdev->dev) < 0) {
|
||||
BT_ERR("%s failed to open default Intel fw file: %s",
|
||||
hdev->name, fwname);
|
||||
bt_dev_err(hdev, "failed to open default fw file: %s",
|
||||
fwname);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@ -1637,7 +1642,7 @@ static int btusb_setup_intel_patching(struct hci_dev *hdev,
|
||||
* process.
|
||||
*/
|
||||
if (remain > HCI_COMMAND_HDR_SIZE && *fw_ptr[0] != 0x01) {
|
||||
BT_ERR("%s Intel fw corrupted: invalid cmd read", hdev->name);
|
||||
bt_dev_err(hdev, "Intel fw corrupted: invalid cmd read");
|
||||
return -EINVAL;
|
||||
}
|
||||
(*fw_ptr)++;
|
||||
@ -1651,7 +1656,7 @@ static int btusb_setup_intel_patching(struct hci_dev *hdev,
|
||||
* of command parameter. If not, the firmware file is corrupted.
|
||||
*/
|
||||
if (remain < cmd->plen) {
|
||||
BT_ERR("%s Intel fw corrupted: invalid cmd len", hdev->name);
|
||||
bt_dev_err(hdev, "Intel fw corrupted: invalid cmd len");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
@ -1684,8 +1689,7 @@ static int btusb_setup_intel_patching(struct hci_dev *hdev,
|
||||
remain -= sizeof(*evt);
|
||||
|
||||
if (remain < evt->plen) {
|
||||
BT_ERR("%s Intel fw corrupted: invalid evt len",
|
||||
hdev->name);
|
||||
bt_dev_err(hdev, "Intel fw corrupted: invalid evt len");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
@ -1699,15 +1703,15 @@ static int btusb_setup_intel_patching(struct hci_dev *hdev,
|
||||
* file is corrupted.
|
||||
*/
|
||||
if (!evt || !evt_param || remain < 0) {
|
||||
BT_ERR("%s Intel fw corrupted: invalid evt read", hdev->name);
|
||||
bt_dev_err(hdev, "Intel fw corrupted: invalid evt read");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
skb = __hci_cmd_sync_ev(hdev, le16_to_cpu(cmd->opcode), cmd->plen,
|
||||
cmd_param, evt->evt, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s sending Intel patch command (0x%4.4x) failed (%ld)",
|
||||
hdev->name, cmd->opcode, PTR_ERR(skb));
|
||||
bt_dev_err(hdev, "sending Intel patch command (0x%4.4x) failed (%ld)",
|
||||
cmd->opcode, PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
@ -1716,15 +1720,15 @@ static int btusb_setup_intel_patching(struct hci_dev *hdev,
|
||||
* the contents of the event.
|
||||
*/
|
||||
if (skb->len != evt->plen) {
|
||||
BT_ERR("%s mismatch event length (opcode 0x%4.4x)", hdev->name,
|
||||
le16_to_cpu(cmd->opcode));
|
||||
bt_dev_err(hdev, "mismatch event length (opcode 0x%4.4x)",
|
||||
le16_to_cpu(cmd->opcode));
|
||||
kfree_skb(skb);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (memcmp(skb->data, evt_param, evt->plen)) {
|
||||
BT_ERR("%s mismatch event parameter (opcode 0x%4.4x)",
|
||||
hdev->name, le16_to_cpu(cmd->opcode));
|
||||
bt_dev_err(hdev, "mismatch event parameter (opcode 0x%4.4x)",
|
||||
le16_to_cpu(cmd->opcode));
|
||||
kfree_skb(skb);
|
||||
return -EFAULT;
|
||||
}
|
||||
@ -1753,8 +1757,8 @@ static int btusb_setup_intel(struct hci_dev *hdev)
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s sending initial HCI reset command failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
bt_dev_err(hdev, "sending initial HCI reset command failed (%ld)",
|
||||
PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
@ -1890,7 +1894,7 @@ static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
|
||||
struct hci_event_hdr *hdr;
|
||||
struct hci_ev_cmd_complete *evt;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_ATOMIC);
|
||||
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -2084,8 +2088,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
* for now only accept this single value.
|
||||
*/
|
||||
if (ver.hw_platform != 0x37) {
|
||||
BT_ERR("%s: Unsupported Intel hardware platform (%u)",
|
||||
hdev->name, ver.hw_platform);
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware platform (%u)",
|
||||
ver.hw_platform);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -2104,8 +2108,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
case 0x14: /* QnJ, IcP */
|
||||
break;
|
||||
default:
|
||||
BT_ERR("%s: Unsupported Intel hardware variant (%u)",
|
||||
hdev->name, ver.hw_variant);
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)",
|
||||
ver.hw_variant);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -2134,8 +2138,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
* choice is to return an error and abort the device initialization.
|
||||
*/
|
||||
if (ver.fw_variant != 0x06) {
|
||||
BT_ERR("%s: Unsupported Intel firmware variant (%u)",
|
||||
hdev->name, ver.fw_variant);
|
||||
bt_dev_err(hdev, "Unsupported Intel firmware variant (%u)",
|
||||
ver.fw_variant);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -2151,8 +2155,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
* that this bootloader does not send them, then abort the setup.
|
||||
*/
|
||||
if (params.limited_cce != 0x00) {
|
||||
BT_ERR("%s: Unsupported Intel firmware loading method (%u)",
|
||||
hdev->name, params.limited_cce);
|
||||
bt_dev_err(hdev, "Unsupported Intel firmware loading method (%u)",
|
||||
params.limited_cce);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -2202,14 +2206,13 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
le16_to_cpu(ver.fw_revision));
|
||||
break;
|
||||
default:
|
||||
BT_ERR("%s: Unsupported Intel firmware naming", hdev->name);
|
||||
bt_dev_err(hdev, "Unsupported Intel firmware naming");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to load Intel firmware file (%d)",
|
||||
hdev->name, err);
|
||||
bt_dev_err(hdev, "Failed to load Intel firmware file (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -2235,13 +2238,13 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
le16_to_cpu(ver.fw_revision));
|
||||
break;
|
||||
default:
|
||||
BT_ERR("%s: Unsupported Intel firmware naming", hdev->name);
|
||||
bt_dev_err(hdev, "Unsupported Intel firmware naming");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fw->size < 644) {
|
||||
BT_ERR("%s: Invalid size of firmware file (%zu)",
|
||||
hdev->name, fw->size);
|
||||
bt_dev_err(hdev, "Invalid size of firmware file (%zu)",
|
||||
fw->size);
|
||||
err = -EBADF;
|
||||
goto done;
|
||||
}
|
||||
@ -2272,18 +2275,18 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
TASK_INTERRUPTIBLE,
|
||||
msecs_to_jiffies(5000));
|
||||
if (err == -EINTR) {
|
||||
BT_ERR("%s: Firmware loading interrupted", hdev->name);
|
||||
bt_dev_err(hdev, "Firmware loading interrupted");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
BT_ERR("%s: Firmware loading timeout", hdev->name);
|
||||
bt_dev_err(hdev, "Firmware loading timeout");
|
||||
err = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (test_bit(BTUSB_FIRMWARE_FAILED, &data->flags)) {
|
||||
BT_ERR("%s: Firmware loading failed", hdev->name);
|
||||
bt_dev_err(hdev, "Firmware loading failed");
|
||||
err = -ENOEXEC;
|
||||
goto done;
|
||||
}
|
||||
@ -2322,12 +2325,12 @@ done:
|
||||
msecs_to_jiffies(1000));
|
||||
|
||||
if (err == -EINTR) {
|
||||
BT_ERR("%s: Device boot interrupted", hdev->name);
|
||||
bt_dev_err(hdev, "Device boot interrupted");
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
BT_ERR("%s: Device boot timeout", hdev->name);
|
||||
bt_dev_err(hdev, "Device boot timeout");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
@ -2364,6 +2367,22 @@ static int btusb_shutdown_intel(struct hci_dev *hdev)
|
||||
struct sk_buff *skb;
|
||||
long ret;
|
||||
|
||||
/* In the shutdown sequence where Bluetooth is turned off followed
|
||||
* by WiFi being turned off, turning WiFi back on causes issue with
|
||||
* the RF calibration.
|
||||
*
|
||||
* To ensure that any RF activity has been stopped, issue HCI Reset
|
||||
* command to clear all ongoing activity including advertising,
|
||||
* scanning etc.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "HCI reset during shutdown failed");
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
/* 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.
|
||||
@ -2371,8 +2390,7 @@ static int btusb_shutdown_intel(struct hci_dev *hdev)
|
||||
skb = __hci_cmd_sync(hdev, 0xfc3f, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: turning off Intel device LED failed (%ld)",
|
||||
hdev->name, ret);
|
||||
bt_dev_err(hdev, "turning off Intel device LED failed");
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
@ -21,13 +21,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "btrtl.h"
|
||||
#include "hci_uart.h"
|
||||
|
||||
#define HCI_3WIRE_ACK_PKT 0
|
||||
@ -65,6 +70,9 @@ enum {
|
||||
};
|
||||
|
||||
struct h5 {
|
||||
/* Must be the first member, hci_serdev.c expects this. */
|
||||
struct hci_uart serdev_hu;
|
||||
|
||||
struct sk_buff_head unack; /* Unack'ed packets queue */
|
||||
struct sk_buff_head rel; /* Reliable packets queue */
|
||||
struct sk_buff_head unrel; /* Unreliable packets queue */
|
||||
@ -95,6 +103,19 @@ struct h5 {
|
||||
H5_SLEEPING,
|
||||
H5_WAKING_UP,
|
||||
} sleep;
|
||||
|
||||
const struct h5_vnd *vnd;
|
||||
const char *id;
|
||||
|
||||
struct gpio_desc *enable_gpio;
|
||||
struct gpio_desc *device_wake_gpio;
|
||||
};
|
||||
|
||||
struct h5_vnd {
|
||||
int (*setup)(struct h5 *h5);
|
||||
void (*open)(struct h5 *h5);
|
||||
void (*close)(struct h5 *h5);
|
||||
const struct acpi_gpio_mapping *acpi_gpio_map;
|
||||
};
|
||||
|
||||
static void h5_reset_rx(struct h5 *h5);
|
||||
@ -193,9 +214,13 @@ static int h5_open(struct hci_uart *hu)
|
||||
|
||||
BT_DBG("hu %p", hu);
|
||||
|
||||
h5 = kzalloc(sizeof(*h5), GFP_KERNEL);
|
||||
if (!h5)
|
||||
return -ENOMEM;
|
||||
if (hu->serdev) {
|
||||
h5 = serdev_device_get_drvdata(hu->serdev);
|
||||
} else {
|
||||
h5 = kzalloc(sizeof(*h5), GFP_KERNEL);
|
||||
if (!h5)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hu->priv = h5;
|
||||
h5->hu = hu;
|
||||
@ -210,6 +235,9 @@ static int h5_open(struct hci_uart *hu)
|
||||
|
||||
h5->tx_win = H5_TX_WIN_MAX;
|
||||
|
||||
if (h5->vnd && h5->vnd->open)
|
||||
h5->vnd->open(h5);
|
||||
|
||||
set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
|
||||
|
||||
/* Send initial sync request */
|
||||
@ -229,7 +257,21 @@ static int h5_close(struct hci_uart *hu)
|
||||
skb_queue_purge(&h5->rel);
|
||||
skb_queue_purge(&h5->unrel);
|
||||
|
||||
kfree(h5);
|
||||
if (h5->vnd && h5->vnd->close)
|
||||
h5->vnd->close(h5);
|
||||
|
||||
if (!hu->serdev)
|
||||
kfree(h5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int h5_setup(struct hci_uart *hu)
|
||||
{
|
||||
struct h5 *h5 = hu->priv;
|
||||
|
||||
if (h5->vnd && h5->vnd->setup)
|
||||
return h5->vnd->setup(h5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -744,18 +786,172 @@ static const struct hci_uart_proto h5p = {
|
||||
.name = "Three-wire (H5)",
|
||||
.open = h5_open,
|
||||
.close = h5_close,
|
||||
.setup = h5_setup,
|
||||
.recv = h5_recv,
|
||||
.enqueue = h5_enqueue,
|
||||
.dequeue = h5_dequeue,
|
||||
.flush = h5_flush,
|
||||
};
|
||||
|
||||
static int h5_serdev_probe(struct serdev_device *serdev)
|
||||
{
|
||||
const struct acpi_device_id *match;
|
||||
struct device *dev = &serdev->dev;
|
||||
struct h5 *h5;
|
||||
|
||||
h5 = devm_kzalloc(dev, sizeof(*h5), GFP_KERNEL);
|
||||
if (!h5)
|
||||
return -ENOMEM;
|
||||
|
||||
set_bit(HCI_UART_RESET_ON_INIT, &h5->serdev_hu.flags);
|
||||
|
||||
h5->hu = &h5->serdev_hu;
|
||||
h5->serdev_hu.serdev = serdev;
|
||||
serdev_device_set_drvdata(serdev, h5);
|
||||
|
||||
if (has_acpi_companion(dev)) {
|
||||
match = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
h5->vnd = (const struct h5_vnd *)match->driver_data;
|
||||
h5->id = (char *)match->id;
|
||||
|
||||
if (h5->vnd->acpi_gpio_map)
|
||||
devm_acpi_dev_add_driver_gpios(dev,
|
||||
h5->vnd->acpi_gpio_map);
|
||||
}
|
||||
|
||||
h5->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(h5->enable_gpio))
|
||||
return PTR_ERR(h5->enable_gpio);
|
||||
|
||||
h5->device_wake_gpio = devm_gpiod_get_optional(dev, "device-wake",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(h5->device_wake_gpio))
|
||||
return PTR_ERR(h5->device_wake_gpio);
|
||||
|
||||
return hci_uart_register_device(&h5->serdev_hu, &h5p);
|
||||
}
|
||||
|
||||
static void h5_serdev_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct h5 *h5 = serdev_device_get_drvdata(serdev);
|
||||
|
||||
hci_uart_unregister_device(&h5->serdev_hu);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_HCIUART_RTL
|
||||
static int h5_btrtl_setup(struct h5 *h5)
|
||||
{
|
||||
struct btrtl_device_info *btrtl_dev;
|
||||
struct sk_buff *skb;
|
||||
__le32 baudrate_data;
|
||||
u32 device_baudrate;
|
||||
unsigned int controller_baudrate;
|
||||
bool flow_control;
|
||||
int err;
|
||||
|
||||
btrtl_dev = btrtl_initialize(h5->hu->hdev, h5->id);
|
||||
if (IS_ERR(btrtl_dev))
|
||||
return PTR_ERR(btrtl_dev);
|
||||
|
||||
err = btrtl_get_uart_settings(h5->hu->hdev, btrtl_dev,
|
||||
&controller_baudrate, &device_baudrate,
|
||||
&flow_control);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
baudrate_data = cpu_to_le32(device_baudrate);
|
||||
skb = __hci_cmd_sync(h5->hu->hdev, 0xfc17, sizeof(baudrate_data),
|
||||
&baudrate_data, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
rtl_dev_err(h5->hu->hdev, "set baud rate command failed\n");
|
||||
err = PTR_ERR(skb);
|
||||
goto out_free;
|
||||
} else {
|
||||
kfree_skb(skb);
|
||||
}
|
||||
/* Give the device some time to set up the new baudrate. */
|
||||
usleep_range(10000, 20000);
|
||||
|
||||
serdev_device_set_baudrate(h5->hu->serdev, controller_baudrate);
|
||||
serdev_device_set_flow_control(h5->hu->serdev, flow_control);
|
||||
|
||||
err = btrtl_download_firmware(h5->hu->hdev, btrtl_dev);
|
||||
/* Give the device some time before the hci-core sends it a reset */
|
||||
usleep_range(10000, 20000);
|
||||
|
||||
out_free:
|
||||
btrtl_free(btrtl_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void h5_btrtl_open(struct h5 *h5)
|
||||
{
|
||||
/* Devices always start with these fixed parameters */
|
||||
serdev_device_set_flow_control(h5->hu->serdev, false);
|
||||
serdev_device_set_parity(h5->hu->serdev, SERDEV_PARITY_EVEN);
|
||||
serdev_device_set_baudrate(h5->hu->serdev, 115200);
|
||||
|
||||
/* The controller needs up to 500ms to wakeup */
|
||||
gpiod_set_value_cansleep(h5->enable_gpio, 1);
|
||||
gpiod_set_value_cansleep(h5->device_wake_gpio, 1);
|
||||
msleep(500);
|
||||
}
|
||||
|
||||
static void h5_btrtl_close(struct h5 *h5)
|
||||
{
|
||||
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
|
||||
gpiod_set_value_cansleep(h5->enable_gpio, 0);
|
||||
}
|
||||
|
||||
static const struct acpi_gpio_params btrtl_device_wake_gpios = { 0, 0, false };
|
||||
static const struct acpi_gpio_params btrtl_enable_gpios = { 1, 0, false };
|
||||
static const struct acpi_gpio_params btrtl_host_wake_gpios = { 2, 0, false };
|
||||
static const struct acpi_gpio_mapping acpi_btrtl_gpios[] = {
|
||||
{ "device-wake-gpios", &btrtl_device_wake_gpios, 1 },
|
||||
{ "enable-gpios", &btrtl_enable_gpios, 1 },
|
||||
{ "host-wake-gpios", &btrtl_host_wake_gpios, 1 },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct h5_vnd rtl_vnd = {
|
||||
.setup = h5_btrtl_setup,
|
||||
.open = h5_btrtl_open,
|
||||
.close = h5_btrtl_close,
|
||||
.acpi_gpio_map = acpi_btrtl_gpios,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id h5_acpi_match[] = {
|
||||
#ifdef CONFIG_BT_HCIUART_RTL
|
||||
{ "OBDA8723", (kernel_ulong_t)&rtl_vnd },
|
||||
#endif
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, h5_acpi_match);
|
||||
#endif
|
||||
|
||||
static struct serdev_device_driver h5_serdev_driver = {
|
||||
.probe = h5_serdev_probe,
|
||||
.remove = h5_serdev_remove,
|
||||
.driver = {
|
||||
.name = "hci_uart_h5",
|
||||
.acpi_match_table = ACPI_PTR(h5_acpi_match),
|
||||
},
|
||||
};
|
||||
|
||||
int __init h5_init(void)
|
||||
{
|
||||
serdev_device_driver_register(&h5_serdev_driver);
|
||||
return hci_uart_register_proto(&h5p);
|
||||
}
|
||||
|
||||
int __exit h5_deinit(void)
|
||||
{
|
||||
serdev_device_driver_unregister(&h5_serdev_driver);
|
||||
return hci_uart_unregister_proto(&h5p);
|
||||
}
|
||||
|
@ -458,7 +458,7 @@ static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
|
||||
struct hci_event_hdr *hdr;
|
||||
struct hci_ev_cmd_complete *evt;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_ATOMIC);
|
||||
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
* protocol extension to H4.
|
||||
*
|
||||
* Copyright (C) 2007 Texas Instruments, Inc.
|
||||
* Copyright (c) 2010, 2012 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2010, 2012, 2018 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* Acknowledgements:
|
||||
* This file is based on hci_ll.c, which was...
|
||||
@ -31,9 +31,14 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/serdev.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
@ -119,12 +124,51 @@ struct qca_data {
|
||||
u64 votes_off;
|
||||
};
|
||||
|
||||
enum qca_speed_type {
|
||||
QCA_INIT_SPEED = 1,
|
||||
QCA_OPER_SPEED
|
||||
};
|
||||
|
||||
/*
|
||||
* Voltage regulator information required for configuring the
|
||||
* QCA Bluetooth chipset
|
||||
*/
|
||||
struct qca_vreg {
|
||||
const char *name;
|
||||
unsigned int min_uV;
|
||||
unsigned int max_uV;
|
||||
unsigned int load_uA;
|
||||
};
|
||||
|
||||
struct qca_vreg_data {
|
||||
enum qca_btsoc_type soc_type;
|
||||
struct qca_vreg *vregs;
|
||||
size_t num_vregs;
|
||||
};
|
||||
|
||||
/*
|
||||
* Platform data for the QCA Bluetooth power driver.
|
||||
*/
|
||||
struct qca_power {
|
||||
struct device *dev;
|
||||
const struct qca_vreg_data *vreg_data;
|
||||
struct regulator_bulk_data *vreg_bulk;
|
||||
bool vregs_on;
|
||||
};
|
||||
|
||||
struct qca_serdev {
|
||||
struct hci_uart serdev_hu;
|
||||
struct gpio_desc *bt_en;
|
||||
struct clk *susclk;
|
||||
enum qca_btsoc_type btsoc_type;
|
||||
struct qca_power *bt_power;
|
||||
u32 init_speed;
|
||||
u32 oper_speed;
|
||||
};
|
||||
|
||||
static int qca_power_setup(struct hci_uart *hu, bool on);
|
||||
static void qca_power_shutdown(struct hci_dev *hdev);
|
||||
|
||||
static void __serial_clock_on(struct tty_struct *tty)
|
||||
{
|
||||
/* TODO: Some chipset requires to enable UART clock on client
|
||||
@ -402,10 +446,11 @@ static int qca_open(struct hci_uart *hu)
|
||||
{
|
||||
struct qca_serdev *qcadev;
|
||||
struct qca_data *qca;
|
||||
int ret;
|
||||
|
||||
BT_DBG("hu %p qca_open", hu);
|
||||
|
||||
qca = kzalloc(sizeof(struct qca_data), GFP_ATOMIC);
|
||||
qca = kzalloc(sizeof(struct qca_data), GFP_KERNEL);
|
||||
if (!qca)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -453,19 +498,32 @@ static int qca_open(struct hci_uart *hu)
|
||||
|
||||
hu->priv = qca;
|
||||
|
||||
if (hu->serdev) {
|
||||
serdev_device_open(hu->serdev);
|
||||
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
if (qcadev->btsoc_type != QCA_WCN3990) {
|
||||
gpiod_set_value_cansleep(qcadev->bt_en, 1);
|
||||
} else {
|
||||
hu->init_speed = qcadev->init_speed;
|
||||
hu->oper_speed = qcadev->oper_speed;
|
||||
ret = qca_power_setup(hu, true);
|
||||
if (ret) {
|
||||
destroy_workqueue(qca->workqueue);
|
||||
kfree_skb(qca->rx_skb);
|
||||
hu->priv = NULL;
|
||||
kfree(qca);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timer_setup(&qca->wake_retrans_timer, hci_ibs_wake_retrans_timeout, 0);
|
||||
qca->wake_retrans = IBS_WAKE_RETRANS_TIMEOUT_MS;
|
||||
|
||||
timer_setup(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, 0);
|
||||
qca->tx_idle_delay = IBS_TX_IDLE_TIMEOUT_MS;
|
||||
|
||||
if (hu->serdev) {
|
||||
serdev_device_open(hu->serdev);
|
||||
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
gpiod_set_value_cansleep(qcadev->bt_en, 1);
|
||||
}
|
||||
|
||||
BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u",
|
||||
qca->tx_idle_delay, qca->wake_retrans);
|
||||
|
||||
@ -549,10 +607,13 @@ static int qca_close(struct hci_uart *hu)
|
||||
qca->hu = NULL;
|
||||
|
||||
if (hu->serdev) {
|
||||
serdev_device_close(hu->serdev);
|
||||
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
gpiod_set_value_cansleep(qcadev->bt_en, 0);
|
||||
if (qcadev->btsoc_type == QCA_WCN3990)
|
||||
qca_power_shutdown(hu->hdev);
|
||||
else
|
||||
gpiod_set_value_cansleep(qcadev->bt_en, 0);
|
||||
|
||||
serdev_device_close(hu->serdev);
|
||||
}
|
||||
|
||||
kfree_skb(qca->rx_skb);
|
||||
@ -872,6 +933,8 @@ static uint8_t qca_get_baudrate_value(int speed)
|
||||
return QCA_BAUDRATE_2000000;
|
||||
case 3000000:
|
||||
return QCA_BAUDRATE_3000000;
|
||||
case 3200000:
|
||||
return QCA_BAUDRATE_3200000;
|
||||
case 3500000:
|
||||
return QCA_BAUDRATE_3500000;
|
||||
default:
|
||||
@ -884,19 +947,27 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
struct sk_buff *skb;
|
||||
struct qca_serdev *qcadev;
|
||||
u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 };
|
||||
|
||||
if (baudrate > QCA_BAUDRATE_3000000)
|
||||
if (baudrate > QCA_BAUDRATE_3200000)
|
||||
return -EINVAL;
|
||||
|
||||
cmd[4] = baudrate;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC);
|
||||
skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
|
||||
if (!skb) {
|
||||
bt_dev_err(hdev, "Failed to allocate baudrate packet");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Disabling hardware flow control is mandatory while
|
||||
* sending change baudrate request to wcn3990 SoC.
|
||||
*/
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
if (qcadev->btsoc_type == QCA_WCN3990)
|
||||
hci_uart_set_flow_control(hu, true);
|
||||
|
||||
/* Assign commands to change baudrate and packet type. */
|
||||
skb_put_data(skb, cmd, sizeof(cmd));
|
||||
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
|
||||
@ -912,6 +983,9 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
|
||||
schedule_timeout(msecs_to_jiffies(BAUDRATE_SETTLE_TIMEOUT_MS));
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
if (qcadev->btsoc_type == QCA_WCN3990)
|
||||
hci_uart_set_flow_control(hu, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -923,50 +997,195 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
||||
hci_uart_set_baudrate(hu, speed);
|
||||
}
|
||||
|
||||
static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* These power pulses are single byte command which are sent
|
||||
* at required baudrate to wcn3990. On wcn3990, we have an external
|
||||
* circuit at Tx pin which decodes the pulse sent at specific baudrate.
|
||||
* For example, wcn3990 supports RF COEX antenna for both Wi-Fi/BT
|
||||
* and also we use the same power inputs to turn on and off for
|
||||
* Wi-Fi/BT. Powering up the power sources will not enable BT, until
|
||||
* we send a power on pulse at 115200 bps. This algorithm will help to
|
||||
* save power. Disabling hardware flow control is mandatory while
|
||||
* sending power pulses to SoC.
|
||||
*/
|
||||
bt_dev_dbg(hdev, "sending power pulse %02x to SoC", cmd);
|
||||
|
||||
skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hci_uart_set_flow_control(hu, true);
|
||||
|
||||
skb_put_u8(skb, cmd);
|
||||
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
|
||||
|
||||
skb_queue_tail(&qca->txq, skb);
|
||||
hci_uart_tx_wakeup(hu);
|
||||
|
||||
/* Wait for 100 uS for SoC to settle down */
|
||||
usleep_range(100, 200);
|
||||
hci_uart_set_flow_control(hu, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int qca_get_speed(struct hci_uart *hu,
|
||||
enum qca_speed_type speed_type)
|
||||
{
|
||||
unsigned int speed = 0;
|
||||
|
||||
if (speed_type == QCA_INIT_SPEED) {
|
||||
if (hu->init_speed)
|
||||
speed = hu->init_speed;
|
||||
else if (hu->proto->init_speed)
|
||||
speed = hu->proto->init_speed;
|
||||
} else {
|
||||
if (hu->oper_speed)
|
||||
speed = hu->oper_speed;
|
||||
else if (hu->proto->oper_speed)
|
||||
speed = hu->proto->oper_speed;
|
||||
}
|
||||
|
||||
return speed;
|
||||
}
|
||||
|
||||
static int qca_check_speeds(struct hci_uart *hu)
|
||||
{
|
||||
struct qca_serdev *qcadev;
|
||||
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
if (qcadev->btsoc_type == QCA_WCN3990) {
|
||||
if (!qca_get_speed(hu, QCA_INIT_SPEED) &&
|
||||
!qca_get_speed(hu, QCA_OPER_SPEED))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (!qca_get_speed(hu, QCA_INIT_SPEED) ||
|
||||
!qca_get_speed(hu, QCA_OPER_SPEED))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
|
||||
{
|
||||
unsigned int speed, qca_baudrate;
|
||||
int ret;
|
||||
|
||||
if (speed_type == QCA_INIT_SPEED) {
|
||||
speed = qca_get_speed(hu, QCA_INIT_SPEED);
|
||||
if (speed)
|
||||
host_set_baudrate(hu, speed);
|
||||
} else {
|
||||
speed = qca_get_speed(hu, QCA_OPER_SPEED);
|
||||
if (!speed)
|
||||
return 0;
|
||||
|
||||
qca_baudrate = qca_get_baudrate_value(speed);
|
||||
bt_dev_dbg(hu->hdev, "Set UART speed to %d", speed);
|
||||
ret = qca_set_baudrate(hu->hdev, qca_baudrate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host_set_baudrate(hu, speed);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca_wcn3990_init(struct hci_uart *hu)
|
||||
{
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
int ret;
|
||||
|
||||
/* Forcefully enable wcn3990 to enter in to boot mode. */
|
||||
host_set_baudrate(hu, 2400);
|
||||
ret = qca_send_power_pulse(hdev, QCA_WCN3990_POWEROFF_PULSE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
qca_set_speed(hu, QCA_INIT_SPEED);
|
||||
ret = qca_send_power_pulse(hdev, QCA_WCN3990_POWERON_PULSE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait for 100 ms for SoC to boot */
|
||||
msleep(100);
|
||||
|
||||
/* Now the device is in ready state to communicate with host.
|
||||
* To sync host with device we need to reopen port.
|
||||
* Without this, we will have RTS and CTS synchronization
|
||||
* issues.
|
||||
*/
|
||||
serdev_device_close(hu->serdev);
|
||||
ret = serdev_device_open(hu->serdev);
|
||||
if (ret) {
|
||||
bt_dev_err(hu->hdev, "failed to open port");
|
||||
return ret;
|
||||
}
|
||||
|
||||
hci_uart_set_flow_control(hu, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca_setup(struct hci_uart *hu)
|
||||
{
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
struct qca_data *qca = hu->priv;
|
||||
unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200;
|
||||
struct qca_serdev *qcadev;
|
||||
int ret;
|
||||
int soc_ver = 0;
|
||||
|
||||
bt_dev_info(hdev, "ROME setup");
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
|
||||
ret = qca_check_speeds(hu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Patch downloading has to be done without IBS mode */
|
||||
clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
|
||||
|
||||
/* Setup initial baudrate */
|
||||
speed = 0;
|
||||
if (hu->init_speed)
|
||||
speed = hu->init_speed;
|
||||
else if (hu->proto->init_speed)
|
||||
speed = hu->proto->init_speed;
|
||||
|
||||
if (speed)
|
||||
host_set_baudrate(hu, speed);
|
||||
|
||||
/* Setup user speed if needed */
|
||||
speed = 0;
|
||||
if (hu->oper_speed)
|
||||
speed = hu->oper_speed;
|
||||
else if (hu->proto->oper_speed)
|
||||
speed = hu->proto->oper_speed;
|
||||
|
||||
if (speed) {
|
||||
qca_baudrate = qca_get_baudrate_value(speed);
|
||||
|
||||
bt_dev_info(hdev, "Set UART speed to %d", speed);
|
||||
ret = qca_set_baudrate(hdev, qca_baudrate);
|
||||
if (ret) {
|
||||
bt_dev_err(hdev, "Failed to change the baud rate (%d)",
|
||||
ret);
|
||||
if (qcadev->btsoc_type == QCA_WCN3990) {
|
||||
bt_dev_info(hdev, "setting up wcn3990");
|
||||
ret = qca_wcn3990_init(hu);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
host_set_baudrate(hu, speed);
|
||||
|
||||
ret = qca_read_soc_version(hdev, &soc_ver);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
bt_dev_info(hdev, "ROME setup");
|
||||
qca_set_speed(hu, QCA_INIT_SPEED);
|
||||
}
|
||||
|
||||
/* Setup user speed if needed */
|
||||
speed = qca_get_speed(hu, QCA_OPER_SPEED);
|
||||
if (speed) {
|
||||
ret = qca_set_speed(hu, QCA_OPER_SPEED);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
qca_baudrate = qca_get_baudrate_value(speed);
|
||||
}
|
||||
|
||||
if (qcadev->btsoc_type != QCA_WCN3990) {
|
||||
/* Get QCA version information */
|
||||
ret = qca_read_soc_version(hdev, &soc_ver);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver);
|
||||
/* Setup patch / NVM configurations */
|
||||
ret = qca_uart_setup_rome(hdev, qca_baudrate);
|
||||
ret = qca_uart_setup(hdev, qca_baudrate, qcadev->btsoc_type, soc_ver);
|
||||
if (!ret) {
|
||||
set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
|
||||
qca_debugfs_init(hdev);
|
||||
@ -1002,9 +1221,123 @@ static struct hci_uart_proto qca_proto = {
|
||||
.dequeue = qca_dequeue,
|
||||
};
|
||||
|
||||
static const struct qca_vreg_data qca_soc_data = {
|
||||
.soc_type = QCA_WCN3990,
|
||||
.vregs = (struct qca_vreg []) {
|
||||
{ "vddio", 1800000, 1900000, 15000 },
|
||||
{ "vddxo", 1800000, 1900000, 80000 },
|
||||
{ "vddrf", 1300000, 1350000, 300000 },
|
||||
{ "vddch0", 3300000, 3400000, 450000 },
|
||||
},
|
||||
.num_vregs = 4,
|
||||
};
|
||||
|
||||
static void qca_power_shutdown(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
|
||||
host_set_baudrate(hu, 2400);
|
||||
qca_send_power_pulse(hdev, QCA_WCN3990_POWEROFF_PULSE);
|
||||
qca_power_setup(hu, false);
|
||||
}
|
||||
|
||||
static int qca_enable_regulator(struct qca_vreg vregs,
|
||||
struct regulator *regulator)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regulator_set_voltage(regulator, vregs.min_uV,
|
||||
vregs.max_uV);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (vregs.load_uA)
|
||||
ret = regulator_set_load(regulator,
|
||||
vregs.load_uA);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regulator_enable(regulator);
|
||||
|
||||
}
|
||||
|
||||
static void qca_disable_regulator(struct qca_vreg vregs,
|
||||
struct regulator *regulator)
|
||||
{
|
||||
regulator_disable(regulator);
|
||||
regulator_set_voltage(regulator, 0, vregs.max_uV);
|
||||
if (vregs.load_uA)
|
||||
regulator_set_load(regulator, 0);
|
||||
|
||||
}
|
||||
|
||||
static int qca_power_setup(struct hci_uart *hu, bool on)
|
||||
{
|
||||
struct qca_vreg *vregs;
|
||||
struct regulator_bulk_data *vreg_bulk;
|
||||
struct qca_serdev *qcadev;
|
||||
int i, num_vregs, ret = 0;
|
||||
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
if (!qcadev || !qcadev->bt_power || !qcadev->bt_power->vreg_data ||
|
||||
!qcadev->bt_power->vreg_bulk)
|
||||
return -EINVAL;
|
||||
|
||||
vregs = qcadev->bt_power->vreg_data->vregs;
|
||||
vreg_bulk = qcadev->bt_power->vreg_bulk;
|
||||
num_vregs = qcadev->bt_power->vreg_data->num_vregs;
|
||||
BT_DBG("on: %d", on);
|
||||
if (on && !qcadev->bt_power->vregs_on) {
|
||||
for (i = 0; i < num_vregs; i++) {
|
||||
ret = qca_enable_regulator(vregs[i],
|
||||
vreg_bulk[i].consumer);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
BT_ERR("failed to enable regulator:%s", vregs[i].name);
|
||||
/* turn off regulators which are enabled */
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
qca_disable_regulator(vregs[i],
|
||||
vreg_bulk[i].consumer);
|
||||
} else {
|
||||
qcadev->bt_power->vregs_on = true;
|
||||
}
|
||||
} else if (!on && qcadev->bt_power->vregs_on) {
|
||||
/* turn off regulator in reverse order */
|
||||
i = qcadev->bt_power->vreg_data->num_vregs - 1;
|
||||
for ( ; i >= 0; i--)
|
||||
qca_disable_regulator(vregs[i], vreg_bulk[i].consumer);
|
||||
|
||||
qcadev->bt_power->vregs_on = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qca_init_regulators(struct qca_power *qca,
|
||||
const struct qca_vreg *vregs, size_t num_vregs)
|
||||
{
|
||||
int i;
|
||||
|
||||
qca->vreg_bulk = devm_kzalloc(qca->dev, num_vregs *
|
||||
sizeof(struct regulator_bulk_data),
|
||||
GFP_KERNEL);
|
||||
if (!qca->vreg_bulk)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_vregs; i++)
|
||||
qca->vreg_bulk[i].supply = vregs[i].name;
|
||||
|
||||
return devm_regulator_bulk_get(qca->dev, num_vregs, qca->vreg_bulk);
|
||||
}
|
||||
|
||||
static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct qca_serdev *qcadev;
|
||||
const struct qca_vreg_data *data;
|
||||
int err;
|
||||
|
||||
qcadev = devm_kzalloc(&serdev->dev, sizeof(*qcadev), GFP_KERNEL);
|
||||
@ -1012,47 +1345,84 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
return -ENOMEM;
|
||||
|
||||
qcadev->serdev_hu.serdev = serdev;
|
||||
data = of_device_get_match_data(&serdev->dev);
|
||||
serdev_device_set_drvdata(serdev, qcadev);
|
||||
if (data && data->soc_type == QCA_WCN3990) {
|
||||
qcadev->btsoc_type = QCA_WCN3990;
|
||||
qcadev->bt_power = devm_kzalloc(&serdev->dev,
|
||||
sizeof(struct qca_power),
|
||||
GFP_KERNEL);
|
||||
if (!qcadev->bt_power)
|
||||
return -ENOMEM;
|
||||
|
||||
qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(qcadev->bt_en)) {
|
||||
dev_err(&serdev->dev, "failed to acquire enable gpio\n");
|
||||
return PTR_ERR(qcadev->bt_en);
|
||||
qcadev->bt_power->dev = &serdev->dev;
|
||||
qcadev->bt_power->vreg_data = data;
|
||||
err = qca_init_regulators(qcadev->bt_power, data->vregs,
|
||||
data->num_vregs);
|
||||
if (err) {
|
||||
BT_ERR("Failed to init regulators:%d", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
qcadev->bt_power->vregs_on = false;
|
||||
|
||||
device_property_read_u32(&serdev->dev, "max-speed",
|
||||
&qcadev->oper_speed);
|
||||
if (!qcadev->oper_speed)
|
||||
BT_DBG("UART will pick default operating speed");
|
||||
|
||||
err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
|
||||
if (err) {
|
||||
BT_ERR("wcn3990 serdev registration failed");
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
qcadev->btsoc_type = QCA_ROME;
|
||||
qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(qcadev->bt_en)) {
|
||||
dev_err(&serdev->dev, "failed to acquire enable gpio\n");
|
||||
return PTR_ERR(qcadev->bt_en);
|
||||
}
|
||||
|
||||
qcadev->susclk = devm_clk_get(&serdev->dev, NULL);
|
||||
if (IS_ERR(qcadev->susclk)) {
|
||||
dev_err(&serdev->dev, "failed to acquire clk\n");
|
||||
return PTR_ERR(qcadev->susclk);
|
||||
}
|
||||
|
||||
err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(qcadev->susclk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
|
||||
if (err)
|
||||
clk_disable_unprepare(qcadev->susclk);
|
||||
}
|
||||
|
||||
qcadev->susclk = devm_clk_get(&serdev->dev, NULL);
|
||||
if (IS_ERR(qcadev->susclk)) {
|
||||
dev_err(&serdev->dev, "failed to acquire clk\n");
|
||||
return PTR_ERR(qcadev->susclk);
|
||||
}
|
||||
out: return err;
|
||||
|
||||
err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(qcadev->susclk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
|
||||
if (err)
|
||||
clk_disable_unprepare(qcadev->susclk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void qca_serdev_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
|
||||
|
||||
hci_uart_unregister_device(&qcadev->serdev_hu);
|
||||
if (qcadev->btsoc_type == QCA_WCN3990)
|
||||
qca_power_shutdown(qcadev->serdev_hu.hdev);
|
||||
else
|
||||
clk_disable_unprepare(qcadev->susclk);
|
||||
|
||||
clk_disable_unprepare(qcadev->susclk);
|
||||
hci_uart_unregister_device(&qcadev->serdev_hu);
|
||||
}
|
||||
|
||||
static const struct of_device_id qca_bluetooth_of_match[] = {
|
||||
{ .compatible = "qcom,qca6174-bt" },
|
||||
{ .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
|
||||
|
@ -19,6 +19,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
@ -239,7 +240,7 @@ void cn_del_callback(struct cb_id *id)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cn_del_callback);
|
||||
|
||||
static int cn_proc_show(struct seq_file *m, void *v)
|
||||
static int __maybe_unused cn_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct cn_queue_dev *dev = cdev.cbdev;
|
||||
struct cn_callback_entry *cbq;
|
||||
|
@ -1673,7 +1673,7 @@ static void chtls_timewait(struct sock *sk)
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
|
||||
tp->rcv_nxt++;
|
||||
tp->rx_opt.ts_recent_stamp = get_seconds();
|
||||
tp->rx_opt.ts_recent_stamp = ktime_get_seconds();
|
||||
tp->srtt_us = 0;
|
||||
tcp_time_wait(sk, TCP_TIME_WAIT, 0);
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ static void virtcrypto_clean_affinity(struct virtio_crypto *vi, long hcpu)
|
||||
|
||||
if (vi->affinity_hint_set) {
|
||||
for (i = 0; i < vi->max_data_queues; i++)
|
||||
virtqueue_set_affinity(vi->data_vq[i].vq, -1);
|
||||
virtqueue_set_affinity(vi->data_vq[i].vq, NULL);
|
||||
|
||||
vi->affinity_hint_set = false;
|
||||
}
|
||||
@ -173,7 +173,7 @@ static void virtcrypto_set_affinity(struct virtio_crypto *vcrypto)
|
||||
*
|
||||
*/
|
||||
for_each_online_cpu(cpu) {
|
||||
virtqueue_set_affinity(vcrypto->data_vq[i].vq, cpu);
|
||||
virtqueue_set_affinity(vcrypto->data_vq[i].vq, cpumask_of(cpu));
|
||||
if (++i >= vcrypto->max_data_queues)
|
||||
break;
|
||||
}
|
||||
|
@ -394,12 +394,16 @@ static const char * const hwmon_power_attr_templates[] = {
|
||||
[hwmon_power_cap_hyst] = "power%d_cap_hyst",
|
||||
[hwmon_power_cap_max] = "power%d_cap_max",
|
||||
[hwmon_power_cap_min] = "power%d_cap_min",
|
||||
[hwmon_power_min] = "power%d_min",
|
||||
[hwmon_power_max] = "power%d_max",
|
||||
[hwmon_power_lcrit] = "power%d_lcrit",
|
||||
[hwmon_power_crit] = "power%d_crit",
|
||||
[hwmon_power_label] = "power%d_label",
|
||||
[hwmon_power_alarm] = "power%d_alarm",
|
||||
[hwmon_power_cap_alarm] = "power%d_cap_alarm",
|
||||
[hwmon_power_min_alarm] = "power%d_min_alarm",
|
||||
[hwmon_power_max_alarm] = "power%d_max_alarm",
|
||||
[hwmon_power_lcrit_alarm] = "power%d_lcrit_alarm",
|
||||
[hwmon_power_crit_alarm] = "power%d_crit_alarm",
|
||||
};
|
||||
|
||||
|
@ -423,7 +423,7 @@ tx_finish:
|
||||
|
||||
static u16 hfi1_vnic_select_queue(struct net_device *netdev,
|
||||
struct sk_buff *skb,
|
||||
void *accel_priv,
|
||||
struct net_device *sb_dev,
|
||||
select_queue_fallback_t fallback)
|
||||
{
|
||||
struct hfi1_vnic_vport_info *vinfo = opa_vnic_dev_priv(netdev);
|
||||
|
@ -1,5 +1,5 @@
|
||||
config MLX5_INFINIBAND
|
||||
tristate "Mellanox Connect-IB HCA support"
|
||||
tristate "Mellanox 5th generation network adapters (ConnectX series) support"
|
||||
depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE
|
||||
depends on INFINIBAND_USER_ACCESS || INFINIBAND_USER_ACCESS=n
|
||||
---help---
|
||||
|
@ -32,6 +32,21 @@
|
||||
|
||||
#include "cmd.h"
|
||||
|
||||
int mlx5_cmd_dump_fill_mkey(struct mlx5_core_dev *dev, u32 *mkey)
|
||||
{
|
||||
u32 out[MLX5_ST_SZ_DW(query_special_contexts_out)] = {0};
|
||||
u32 in[MLX5_ST_SZ_DW(query_special_contexts_in)] = {0};
|
||||
int err;
|
||||
|
||||
MLX5_SET(query_special_contexts_in, in, opcode,
|
||||
MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS);
|
||||
err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
|
||||
if (!err)
|
||||
*mkey = MLX5_GET(query_special_contexts_out, out,
|
||||
dump_fill_mkey);
|
||||
return err;
|
||||
}
|
||||
|
||||
int mlx5_cmd_null_mkey(struct mlx5_core_dev *dev, u32 *null_mkey)
|
||||
{
|
||||
u32 out[MLX5_ST_SZ_DW(query_special_contexts_out)] = {};
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mlx5/driver.h>
|
||||
|
||||
int mlx5_cmd_dump_fill_mkey(struct mlx5_core_dev *dev, u32 *mkey);
|
||||
int mlx5_cmd_null_mkey(struct mlx5_core_dev *dev, u32 *null_mkey);
|
||||
int mlx5_cmd_query_cong_params(struct mlx5_core_dev *dev, int cong_point,
|
||||
void *out, int out_size);
|
||||
|
@ -95,7 +95,7 @@ static netdev_tx_t opa_netdev_start_xmit(struct sk_buff *skb,
|
||||
}
|
||||
|
||||
static u16 opa_vnic_select_queue(struct net_device *netdev, struct sk_buff *skb,
|
||||
void *accel_priv,
|
||||
struct net_device *sb_dev,
|
||||
select_queue_fallback_t fallback)
|
||||
{
|
||||
struct opa_vnic_adapter *adapter = opa_vnic_priv(netdev);
|
||||
@ -107,7 +107,7 @@ static u16 opa_vnic_select_queue(struct net_device *netdev, struct sk_buff *skb,
|
||||
mdata->entropy = opa_vnic_calc_entropy(skb);
|
||||
mdata->vl = opa_vnic_get_vl(adapter, skb);
|
||||
rc = adapter->rn_ops->ndo_select_queue(netdev, skb,
|
||||
accel_priv, fallback);
|
||||
sb_dev, fallback);
|
||||
skb_pull(skb, sizeof(*mdata));
|
||||
return rc;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -1321,7 +1322,7 @@ static inline void capinc_tty_exit(void) { }
|
||||
* /proc/capi/capi20:
|
||||
* minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
|
||||
*/
|
||||
static int capi20_proc_show(struct seq_file *m, void *v)
|
||||
static int __maybe_unused capi20_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct capidev *cdev;
|
||||
struct list_head *l;
|
||||
@ -1344,7 +1345,7 @@ static int capi20_proc_show(struct seq_file *m, void *v)
|
||||
* /proc/capi/capi20ncci:
|
||||
* applid ncci
|
||||
*/
|
||||
static int capi20ncci_proc_show(struct seq_file *m, void *v)
|
||||
static int __maybe_unused capi20ncci_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct capidev *cdev;
|
||||
struct capincci *np;
|
||||
|
@ -9,6 +9,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -2451,7 +2452,7 @@ lower_callback(struct notifier_block *nb, unsigned long val, void *v)
|
||||
* /proc/capi/capidrv:
|
||||
* nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
|
||||
*/
|
||||
static int capidrv_proc_show(struct seq_file *m, void *v)
|
||||
static int __maybe_unused capidrv_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
seq_printf(m, "%lu %lu %lu %lu\n",
|
||||
global.ap.nrecvctlpkt,
|
||||
|
@ -739,6 +739,7 @@ static void read_int_callback(struct urb *urb)
|
||||
|
||||
case HD_OPEN_B2CHANNEL_ACK:
|
||||
++channel;
|
||||
/* fall through */
|
||||
case HD_OPEN_B1CHANNEL_ACK:
|
||||
bcs = cs->bcs + channel;
|
||||
update_basstate(ucs, BS_B1OPEN << channel, 0);
|
||||
@ -752,6 +753,7 @@ static void read_int_callback(struct urb *urb)
|
||||
|
||||
case HD_CLOSE_B2CHANNEL_ACK:
|
||||
++channel;
|
||||
/* fall through */
|
||||
case HD_CLOSE_B1CHANNEL_ACK:
|
||||
bcs = cs->bcs + channel;
|
||||
update_basstate(ucs, 0, BS_B1OPEN << channel);
|
||||
@ -765,6 +767,7 @@ static void read_int_callback(struct urb *urb)
|
||||
|
||||
case HD_B2_FLOW_CONTROL:
|
||||
++channel;
|
||||
/* fall through */
|
||||
case HD_B1_FLOW_CONTROL:
|
||||
bcs = cs->bcs + channel;
|
||||
atomic_add((l - BAS_NORMFRAME) * BAS_CORRFRAMES,
|
||||
@ -972,16 +975,14 @@ static int starturbs(struct bc_state *bcs)
|
||||
rc = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
usb_fill_int_urb(urb, bcs->cs->hw.bas->udev,
|
||||
usb_rcvisocpipe(urb->dev, 3 + 2 * bcs->channel),
|
||||
ubc->isoinbuf + k * BAS_INBUFSIZE,
|
||||
BAS_INBUFSIZE, read_iso_callback, bcs,
|
||||
BAS_FRAMETIME);
|
||||
|
||||
urb->dev = bcs->cs->hw.bas->udev;
|
||||
urb->pipe = usb_rcvisocpipe(urb->dev, 3 + 2 * bcs->channel);
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
urb->transfer_buffer = ubc->isoinbuf + k * BAS_INBUFSIZE;
|
||||
urb->transfer_buffer_length = BAS_INBUFSIZE;
|
||||
urb->number_of_packets = BAS_NUMFRAMES;
|
||||
urb->interval = BAS_FRAMETIME;
|
||||
urb->complete = read_iso_callback;
|
||||
urb->context = bcs;
|
||||
for (j = 0; j < BAS_NUMFRAMES; j++) {
|
||||
urb->iso_frame_desc[j].offset = j * BAS_MAXFRAME;
|
||||
urb->iso_frame_desc[j].length = BAS_MAXFRAME;
|
||||
@ -1005,15 +1006,15 @@ static int starturbs(struct bc_state *bcs)
|
||||
rc = -EFAULT;
|
||||
goto error;
|
||||
}
|
||||
urb->dev = bcs->cs->hw.bas->udev;
|
||||
urb->pipe = usb_sndisocpipe(urb->dev, 4 + 2 * bcs->channel);
|
||||
usb_fill_int_urb(urb, bcs->cs->hw.bas->udev,
|
||||
usb_sndisocpipe(urb->dev, 4 + 2 * bcs->channel),
|
||||
ubc->isooutbuf->data,
|
||||
sizeof(ubc->isooutbuf->data),
|
||||
write_iso_callback, &ubc->isoouturbs[k],
|
||||
BAS_FRAMETIME);
|
||||
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
urb->transfer_buffer = ubc->isooutbuf->data;
|
||||
urb->transfer_buffer_length = sizeof(ubc->isooutbuf->data);
|
||||
urb->number_of_packets = BAS_NUMFRAMES;
|
||||
urb->interval = BAS_FRAMETIME;
|
||||
urb->complete = write_iso_callback;
|
||||
urb->context = &ubc->isoouturbs[k];
|
||||
for (j = 0; j < BAS_NUMFRAMES; ++j) {
|
||||
urb->iso_frame_desc[j].offset = BAS_OUTBUFSIZE;
|
||||
urb->iso_frame_desc[j].length = BAS_NORMFRAME;
|
||||
|
@ -361,6 +361,7 @@ modehdlc(struct bchannel *bch, int protocol)
|
||||
switch (protocol) {
|
||||
case -1: /* used for init */
|
||||
bch->state = -1;
|
||||
/* fall through */
|
||||
case ISDN_P_NONE:
|
||||
if (bch->state == ISDN_P_NONE)
|
||||
break;
|
||||
|
@ -1296,6 +1296,7 @@ mode_hfcpci(struct bchannel *bch, int bc, int protocol)
|
||||
case (-1): /* used for init */
|
||||
bch->state = -1;
|
||||
bch->nr = bc;
|
||||
/* fall through */
|
||||
case (ISDN_P_NONE):
|
||||
if (bch->state == ISDN_P_NONE)
|
||||
return 0;
|
||||
@ -2219,7 +2220,7 @@ hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
struct hfc_pci *card;
|
||||
struct _hfc_map *m = (struct _hfc_map *)ent->driver_data;
|
||||
|
||||
card = kzalloc(sizeof(struct hfc_pci), GFP_ATOMIC);
|
||||
card = kzalloc(sizeof(struct hfc_pci), GFP_KERNEL);
|
||||
if (!card) {
|
||||
printk(KERN_ERR "No kmem for HFC card\n");
|
||||
return err;
|
||||
|
@ -819,6 +819,7 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
|
||||
int fifon = fifo->fifonum;
|
||||
int i;
|
||||
int hdlc = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (debug & DBG_HFC_CALL_TRACE)
|
||||
printk(KERN_DEBUG "%s: %s: fifo(%i) len(%i) "
|
||||
@ -835,7 +836,7 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&hw->lock);
|
||||
spin_lock_irqsave(&hw->lock, flags);
|
||||
if (fifo->dch) {
|
||||
rx_skb = fifo->dch->rx_skb;
|
||||
maxlen = fifo->dch->maxlen;
|
||||
@ -844,7 +845,7 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
|
||||
if (fifo->bch) {
|
||||
if (test_bit(FLG_RX_OFF, &fifo->bch->Flags)) {
|
||||
fifo->bch->dropcnt += len;
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
return;
|
||||
}
|
||||
maxlen = bchannel_get_rxbuf(fifo->bch, len);
|
||||
@ -854,7 +855,7 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
|
||||
skb_trim(rx_skb, 0);
|
||||
pr_warning("%s.B%d: No bufferspace for %d bytes\n",
|
||||
hw->name, fifo->bch->nr, len);
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
return;
|
||||
}
|
||||
maxlen = fifo->bch->maxlen;
|
||||
@ -878,7 +879,7 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
|
||||
} else {
|
||||
printk(KERN_DEBUG "%s: %s: No mem for rx_skb\n",
|
||||
hw->name, __func__);
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -888,7 +889,7 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
|
||||
"for fifo(%d) HFCUSB_D_RX\n",
|
||||
hw->name, __func__, fifon);
|
||||
skb_trim(rx_skb, 0);
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -942,7 +943,7 @@ hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len,
|
||||
/* deliver transparent data to layer2 */
|
||||
recv_Bchannel(fifo->bch, MISDN_ID_ANY, false);
|
||||
}
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -979,18 +980,19 @@ rx_iso_complete(struct urb *urb)
|
||||
__u8 *buf;
|
||||
static __u8 eof[8];
|
||||
__u8 s0_state;
|
||||
unsigned long flags;
|
||||
|
||||
fifon = fifo->fifonum;
|
||||
status = urb->status;
|
||||
|
||||
spin_lock(&hw->lock);
|
||||
spin_lock_irqsave(&hw->lock, flags);
|
||||
if (fifo->stop_gracefull) {
|
||||
fifo->stop_gracefull = 0;
|
||||
fifo->active = 0;
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
|
||||
/*
|
||||
* ISO transfer only partially completed,
|
||||
@ -1096,15 +1098,16 @@ rx_int_complete(struct urb *urb)
|
||||
struct usb_fifo *fifo = (struct usb_fifo *) urb->context;
|
||||
struct hfcsusb *hw = fifo->hw;
|
||||
static __u8 eof[8];
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&hw->lock);
|
||||
spin_lock_irqsave(&hw->lock, flags);
|
||||
if (fifo->stop_gracefull) {
|
||||
fifo->stop_gracefull = 0;
|
||||
fifo->active = 0;
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
|
||||
fifon = fifo->fifonum;
|
||||
if ((!fifo->active) || (urb->status)) {
|
||||
@ -1172,12 +1175,13 @@ tx_iso_complete(struct urb *urb)
|
||||
int *tx_idx;
|
||||
int frame_complete, fifon, status, fillempty = 0;
|
||||
__u8 threshbit, *p;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock(&hw->lock);
|
||||
spin_lock_irqsave(&hw->lock, flags);
|
||||
if (fifo->stop_gracefull) {
|
||||
fifo->stop_gracefull = 0;
|
||||
fifo->active = 0;
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1195,7 +1199,7 @@ tx_iso_complete(struct urb *urb)
|
||||
} else {
|
||||
printk(KERN_DEBUG "%s: %s: neither BCH nor DCH\n",
|
||||
hw->name, __func__);
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1375,7 +1379,7 @@ tx_iso_complete(struct urb *urb)
|
||||
hw->name, __func__,
|
||||
symbolic(urb_errlist, status), status, fifon);
|
||||
}
|
||||
spin_unlock(&hw->lock);
|
||||
spin_unlock_irqrestore(&hw->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -887,6 +887,7 @@ release_card(struct inf_hw *card) {
|
||||
release_card(card->sc[i]);
|
||||
card->sc[i] = NULL;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
pci_disable_device(card->pdev);
|
||||
pci_set_drvdata(card->pdev, NULL);
|
||||
|
@ -972,6 +972,7 @@ isar_pump_statev_fax(struct isar_ch *ch, u8 devt) {
|
||||
break;
|
||||
case PCTRL_CMD_FTM:
|
||||
p1 = 2;
|
||||
/* fall through */
|
||||
case PCTRL_CMD_FTH:
|
||||
send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
|
||||
PCTRL_CMD_SILON, 1, &p1);
|
||||
@ -1177,6 +1178,7 @@ setup_pump(struct isar_ch *ch) {
|
||||
send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG,
|
||||
PMOD_DTMF, 1, param);
|
||||
}
|
||||
/* fall through */
|
||||
case ISDN_P_B_MODEM_ASYNC:
|
||||
ctrl = PMOD_DATAMODEM;
|
||||
if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) {
|
||||
@ -1268,6 +1270,7 @@ setup_iom2(struct isar_ch *ch) {
|
||||
case ISDN_P_B_MODEM_ASYNC:
|
||||
case ISDN_P_B_T30_FAX:
|
||||
cmsb |= IOM_CTRL_RCV;
|
||||
/* fall through */
|
||||
case ISDN_P_B_L2DTMF:
|
||||
if (test_bit(FLG_DTMFSEND, &ch->bch.Flags))
|
||||
cmsb |= IOM_CTRL_RCV;
|
||||
@ -1560,6 +1563,7 @@ isar_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)
|
||||
ich->is->name, hh->id);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
pr_info("%s: %s unknown prim(%x,%x)\n",
|
||||
ich->is->name, __func__, hh->prim, hh->id);
|
||||
|
@ -1084,7 +1084,7 @@ nj_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
card = kzalloc(sizeof(struct tiger_hw), GFP_ATOMIC);
|
||||
card = kzalloc(sizeof(struct tiger_hw), GFP_KERNEL);
|
||||
if (!card) {
|
||||
pr_info("No kmem for Netjet\n");
|
||||
return err;
|
||||
|
@ -207,6 +207,7 @@ modehdlc(struct BCState *bcs, int mode, int bc)
|
||||
bcs->mode = 1;
|
||||
bcs->channel = bc;
|
||||
bc = 0;
|
||||
/* fall through */
|
||||
case (L1_MODE_NULL):
|
||||
if (bcs->mode == L1_MODE_NULL)
|
||||
return;
|
||||
|
@ -1012,7 +1012,7 @@ dummy_pstack(struct PStack *st, int pr, void *arg) {
|
||||
|
||||
static int
|
||||
init_PStack(struct PStack **stp) {
|
||||
*stp = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
|
||||
*stp = kmalloc(sizeof(struct PStack), GFP_KERNEL);
|
||||
if (!*stp)
|
||||
return -ENOMEM;
|
||||
(*stp)->next = NULL;
|
||||
@ -1369,6 +1369,7 @@ leased_l1l2(struct PStack *st, int pr, void *arg)
|
||||
case (PH_ACTIVATE | INDICATION):
|
||||
case (PH_ACTIVATE | CONFIRM):
|
||||
event = EV_LEASED;
|
||||
/* fall through */
|
||||
case (PH_DEACTIVATE | INDICATION):
|
||||
case (PH_DEACTIVATE | CONFIRM):
|
||||
if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags))
|
||||
|
@ -1029,7 +1029,7 @@ static int hisax_cs_new(int cardnr, char *id, struct IsdnCard *card,
|
||||
|
||||
*cs_out = NULL;
|
||||
|
||||
cs = kzalloc(sizeof(struct IsdnCardState), GFP_ATOMIC);
|
||||
cs = kzalloc(sizeof(struct IsdnCardState), GFP_KERNEL);
|
||||
if (!cs) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: No memory for IsdnCardState(card %d)\n",
|
||||
@ -1059,12 +1059,12 @@ static int hisax_cs_new(int cardnr, char *id, struct IsdnCard *card,
|
||||
"HiSax: Card Type %d out of range\n", card->typ);
|
||||
goto outf_cs;
|
||||
}
|
||||
if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
|
||||
if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_KERNEL))) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: No memory for dlog(card %d)\n", cardnr + 1);
|
||||
goto outf_cs;
|
||||
}
|
||||
if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
|
||||
if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_KERNEL))) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: No memory for status_buf(card %d)\n",
|
||||
cardnr + 1);
|
||||
@ -1123,7 +1123,7 @@ static int hisax_cs_setup(int cardnr, struct IsdnCard *card,
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
|
||||
if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN_L1, GFP_KERNEL))) {
|
||||
printk(KERN_WARNING "HiSax: No memory for isac rcvbuf\n");
|
||||
ll_unload(cs);
|
||||
goto outf_cs;
|
||||
@ -1843,6 +1843,7 @@ static void hisax_b_l2l1(struct PStack *st, int pr, void *arg)
|
||||
case PH_DEACTIVATE | REQUEST:
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
skb_queue_purge(&bcs->squeue);
|
||||
/* fall through */
|
||||
default:
|
||||
B_L2L1(b_if, pr, arg);
|
||||
break;
|
||||
|
@ -108,6 +108,7 @@ ReadISAC(struct IsdnCardState *cs, u_char offset)
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
|
||||
/* fall through */
|
||||
case R685:
|
||||
return (readreg(cs->hw.gazel.isac, off2));
|
||||
case R753:
|
||||
@ -125,6 +126,7 @@ WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
|
||||
/* fall through */
|
||||
case R685:
|
||||
writereg(cs->hw.gazel.isac, off2, value);
|
||||
break;
|
||||
@ -203,6 +205,7 @@ ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
|
||||
/* fall through */
|
||||
case R685:
|
||||
return (readreg(cs->hw.gazel.hscx[hscx], off2));
|
||||
case R753:
|
||||
@ -220,6 +223,7 @@ WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
|
||||
/* fall through */
|
||||
case R685:
|
||||
writereg(cs->hw.gazel.hscx[hscx], off2, value);
|
||||
break;
|
||||
|
@ -432,16 +432,12 @@ fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,
|
||||
{
|
||||
int k;
|
||||
|
||||
urb->dev = dev;
|
||||
urb->pipe = pipe;
|
||||
urb->complete = complete;
|
||||
usb_fill_int_urb(urb, dev, pipe, buf, packet_size * num_packets,
|
||||
complete, context, interval);
|
||||
|
||||
urb->number_of_packets = num_packets;
|
||||
urb->transfer_buffer_length = packet_size * num_packets;
|
||||
urb->context = context;
|
||||
urb->transfer_buffer = buf;
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
urb->actual_length = 0;
|
||||
urb->interval = interval;
|
||||
for (k = 0; k < num_packets; k++) {
|
||||
urb->iso_frame_desc[k].offset = packet_size * k;
|
||||
urb->iso_frame_desc[k].length = packet_size;
|
||||
|
@ -1089,6 +1089,7 @@ isar_pump_statev_fax(struct BCState *bcs, u_char devt) {
|
||||
break;
|
||||
case PCTRL_CMD_FTM:
|
||||
p1 = 2;
|
||||
/* fall through */
|
||||
case PCTRL_CMD_FTH:
|
||||
sendmsg(cs, dps | ISAR_HIS_PUMPCTRL,
|
||||
PCTRL_CMD_SILON, 1, &p1);
|
||||
@ -1097,6 +1098,7 @@ isar_pump_statev_fax(struct BCState *bcs, u_char devt) {
|
||||
case PCTRL_CMD_FRM:
|
||||
if (frm_extra_delay)
|
||||
mdelay(frm_extra_delay);
|
||||
/* fall through */
|
||||
case PCTRL_CMD_FRH:
|
||||
p1 = bcs->hw.isar.mod = bcs->hw.isar.newmod;
|
||||
bcs->hw.isar.newmod = 0;
|
||||
|
@ -88,6 +88,7 @@ l3_1tr6_setup_req(struct l3_process *pc, u_char pr, void *arg)
|
||||
break;
|
||||
case 'C':
|
||||
channel = 0x08;
|
||||
/* fall through */
|
||||
case 'P':
|
||||
channel |= 0x80;
|
||||
teln++;
|
||||
|
@ -1282,6 +1282,7 @@ l3dss1_setup_req(struct l3_process *pc, u_char pr,
|
||||
switch (0x5f & *teln) {
|
||||
case 'C':
|
||||
channel = 0x08;
|
||||
/* fall through */
|
||||
case 'P':
|
||||
channel |= 0x80;
|
||||
teln++;
|
||||
|
@ -408,15 +408,10 @@ fill_isoc_urb(struct urb *urb, struct usb_device *dev,
|
||||
{
|
||||
int k;
|
||||
|
||||
urb->dev = dev;
|
||||
urb->pipe = pipe;
|
||||
urb->interval = 1;
|
||||
urb->transfer_buffer = buf;
|
||||
usb_fill_int_urb(urb, dev, pipe, buf, num_packets * packet_size,
|
||||
complete, context, 1);
|
||||
|
||||
urb->number_of_packets = num_packets;
|
||||
urb->transfer_buffer_length = num_packets * packet_size;
|
||||
urb->actual_length = 0;
|
||||
urb->complete = complete;
|
||||
urb->context = context;
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
for (k = 0; k < num_packets; k++) {
|
||||
urb->iso_frame_desc[k].offset = packet_size * k;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user