Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: "Most notable changes in here: 1) By far the biggest accomplishment, thanks to a large range of contributors, is the addition of multi-send for transmit. This is the result of discussions back in Chicago, and the hard work of several individuals. Now, when the ->ndo_start_xmit() method of a driver sees skb->xmit_more as true, it can choose to defer the doorbell telling the driver to start processing the new TX queue entires. skb->xmit_more means that the generic networking is guaranteed to call the driver immediately with another SKB to send. There is logic added to the qdisc layer to dequeue multiple packets at a time, and the handling mis-predicted offloads in software is now done with no locks held. Finally, pktgen is extended to have a "burst" parameter that can be used to test a multi-send implementation. Several drivers have xmit_more support: i40e, igb, ixgbe, mlx4, virtio_net Adding support is almost trivial, so export more drivers to support this optimization soon. I want to thank, in no particular or implied order, Jesper Dangaard Brouer, Eric Dumazet, Alexander Duyck, Tom Herbert, Jamal Hadi Salim, John Fastabend, Florian Westphal, Daniel Borkmann, David Tat, Hannes Frederic Sowa, and Rusty Russell. 2) PTP and timestamping support in bnx2x, from Michal Kalderon. 3) Allow adjusting the rx_copybreak threshold for a driver via ethtool, and add rx_copybreak support to enic driver. From Govindarajulu Varadarajan. 4) Significant enhancements to the generic PHY layer and the bcm7xxx driver in particular (EEE support, auto power down, etc.) from Florian Fainelli. 5) Allow raw buffers to be used for flow dissection, allowing drivers to determine the optimal "linear pull" size for devices that DMA into pools of pages. The objective is to get exactly the necessary amount of headers into the linear SKB area pre-pulled, but no more. The new interface drivers use is eth_get_headlen(). From WANG Cong, with driver conversions (several had their own by-hand duplicated implementations) by Alexander Duyck and Eric Dumazet. 6) Support checksumming more smoothly and efficiently for encapsulations, and add "foo over UDP" facility. From Tom Herbert. 7) Add Broadcom SF2 switch driver to DSA layer, from Florian Fainelli. 8) eBPF now can load programs via a system call and has an extensive testsuite. Alexei Starovoitov and Daniel Borkmann. 9) Major overhaul of the packet scheduler to use RCU in several major areas such as the classifiers and rate estimators. From John Fastabend. 10) Add driver for Intel FM10000 Ethernet Switch, from Alexander Duyck. 11) Rearrange TCP_SKB_CB() to reduce cache line misses, from Eric Dumazet. 12) Add Datacenter TCP congestion control algorithm support, From Florian Westphal. 13) Reorganize sk_buff so that __copy_skb_header() is significantly faster. From Eric Dumazet" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1558 commits) netlabel: directly return netlbl_unlabel_genl_init() net: add netdev_txq_bql_{enqueue, complete}_prefetchw() helpers net: description of dma_cookie cause make xmldocs warning cxgb4: clean up a type issue cxgb4: potential shift wrapping bug i40e: skb->xmit_more support net: fs_enet: Add NAPI TX net: fs_enet: Remove non NAPI RX r8169:add support for RTL8168EP net_sched: copy exts->type in tcf_exts_change() wimax: convert printk to pr_foo() af_unix: remove 0 assignment on static ipv6: Do not warn for informational ICMP messages, regardless of type. Update Intel Ethernet Driver maintainers list bridge: Save frag_max_size between PRE_ROUTING and POST_ROUTING tipc: fix bug in multicast congestion handling net: better IFF_XMIT_DST_RELEASE support net/mlx4_en: remove NETDEV_TX_BUSY 3c59x: fix bad split of cpu_to_le32(pci_map_single()) net: bcmgenet: fix Tx ring priority programming ...
This commit is contained in:
commit
35a9ad8af0
1397 changed files with 153539 additions and 38332 deletions
32
Documentation/devicetree/bindings/bus/bcma.txt
Normal file
32
Documentation/devicetree/bindings/bus/bcma.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
Driver for ARM AXI Bus with Broadcom Plugins (bcma)
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : brcm,bus-axi
|
||||
|
||||
- reg : iomem address range of chipcommon core
|
||||
|
||||
The cores on the AXI bus are automatically detected by bcma with the
|
||||
memory ranges they are using and they get registered afterwards.
|
||||
|
||||
The top-level axi bus may contain children representing attached cores
|
||||
(devices). This is needed since some hardware details can't be auto
|
||||
detected (e.g. IRQ numbers). Also some of the cores may be responsible
|
||||
for extra things, e.g. ChipCommon providing access to the GPIO chip.
|
||||
|
||||
Example:
|
||||
|
||||
axi@18000000 {
|
||||
compatible = "brcm,bus-axi";
|
||||
reg = <0x18000000 0x1000>;
|
||||
ranges = <0x00000000 0x18000000 0x00100000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
chipcommon {
|
||||
reg = <0x00000000 0x1000>;
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,39 @@
|
|||
* Broadcom UniMAC MDIO bus controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should one from "brcm,genet-mdio-v1", "brcm,genet-mdio-v2",
|
||||
"brcm,genet-mdio-v3", "brcm,genet-mdio-v4" or "brcm,unimac-mdio"
|
||||
- reg: address and length of the regsiter set for the device, first one is the
|
||||
base register, and the second one is optional and for indirect accesses to
|
||||
larger than 16-bits MDIO transactions
|
||||
- reg-names: name(s) of the register must be "mdio" and optional "mdio_indir_rw"
|
||||
- #size-cells: must be 1
|
||||
- #address-cells: must be 0
|
||||
|
||||
Optional properties:
|
||||
- interrupts: must be one if the interrupt is shared with the Ethernet MAC or
|
||||
Ethernet switch this MDIO block is integrated from, or must be two, if there
|
||||
are two separate interrupts, first one must be "mdio done" and second must be
|
||||
for "mdio error"
|
||||
- interrupt-names: must be "mdio_done_error" when there is a share interrupt fed
|
||||
to this hardware block, or must be "mdio_done" for the first interrupt and
|
||||
"mdio_error" for the second when there are separate interrupts
|
||||
|
||||
Child nodes of this MDIO bus controller node are standard Ethernet PHY device
|
||||
nodes as described in Documentation/devicetree/bindings/net/phy.txt
|
||||
|
||||
Example:
|
||||
|
||||
mdio@403c0 {
|
||||
compatible = "brcm,unimac-mdio";
|
||||
reg = <0x403c0 0x8 0x40300 0x18>;
|
||||
reg-names = "mdio", "mdio_indir_rw";
|
||||
#size-cells = <1>;
|
||||
#address-cells = <0>;
|
||||
|
||||
...
|
||||
phy@0 {
|
||||
compatible = "ethernet-phy-ieee802.3-c22";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
78
Documentation/devicetree/bindings/net/broadcom-sf2.txt
Normal file
78
Documentation/devicetree/bindings/net/broadcom-sf2.txt
Normal file
|
@ -0,0 +1,78 @@
|
|||
* Broadcom Starfighter 2 integrated swich
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "brcm,bcm7445-switch-v4.0"
|
||||
- reg: addresses and length of the register sets for the device, must be 6
|
||||
pairs of register addresses and lengths
|
||||
- interrupts: interrupts for the devices, must be two interrupts
|
||||
- dsa,mii-bus: phandle to the MDIO bus controller, see dsa/dsa.txt
|
||||
- dsa,ethernet: phandle to the CPU network interface controller, see dsa/dsa.txt
|
||||
- #size-cells: must be 0
|
||||
- #address-cells: must be 2, see dsa/dsa.txt
|
||||
|
||||
Subnodes:
|
||||
|
||||
The integrated switch subnode should be specified according to the binding
|
||||
described in dsa/dsa.txt.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reg-names: litteral names for the device base register addresses, when present
|
||||
must be: "core", "reg", "intrl2_0", "intrl2_1", "fcb", "acb"
|
||||
|
||||
- interrupt-names: litternal names for the device interrupt lines, when present
|
||||
must be: "switch_0" and "switch_1"
|
||||
|
||||
- brcm,num-gphy: specify the maximum number of integrated gigabit PHYs in the
|
||||
switch
|
||||
|
||||
- brcm,num-rgmii-ports: specify the maximum number of RGMII interfaces supported
|
||||
by the switch
|
||||
|
||||
- brcm,fcb-pause-override: boolean property, if present indicates that the switch
|
||||
supports Failover Control Block pause override capability
|
||||
|
||||
- brcm,acb-packets-inflight: boolean property, if present indicates that the switch
|
||||
Admission Control Block supports reporting the number of packets in-flight in a
|
||||
switch queue
|
||||
|
||||
Example:
|
||||
|
||||
switch_top@f0b00000 {
|
||||
compatible = "simple-bus";
|
||||
#size-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
ranges = <0 0xf0b00000 0x40804>;
|
||||
|
||||
ethernet_switch@0 {
|
||||
compatible = "brcm,bcm7445-switch-v4.0";
|
||||
#size-cells = <0>;
|
||||
#address-cells = <2>;
|
||||
reg = <0x0 0x40000
|
||||
0x40000 0x110
|
||||
0x40340 0x30
|
||||
0x40380 0x30
|
||||
0x40400 0x34
|
||||
0x40600 0x208>;
|
||||
interrupts = <0 0x18 0
|
||||
0 0x19 0>;
|
||||
brcm,num-gphy = <1>;
|
||||
brcm,num-rgmii-ports = <2>;
|
||||
brcm,fcb-pause-override;
|
||||
brcm,acb-packets-inflight;
|
||||
|
||||
...
|
||||
switch@0 {
|
||||
reg = <0 0>;
|
||||
#size-cells = <0>;
|
||||
#address-cells <1>;
|
||||
|
||||
port@0 {
|
||||
label = "gphy";
|
||||
reg = <0>;
|
||||
};
|
||||
...
|
||||
};
|
||||
};
|
||||
};
|
67
Documentation/devicetree/bindings/net/can/m_can.txt
Normal file
67
Documentation/devicetree/bindings/net/can/m_can.txt
Normal file
|
@ -0,0 +1,67 @@
|
|||
Bosch MCAN controller Device Tree Bindings
|
||||
-------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "bosch,m_can" for M_CAN controllers
|
||||
- reg : physical base address and size of the M_CAN
|
||||
registers map and Message RAM
|
||||
- reg-names : Should be "m_can" and "message_ram"
|
||||
- interrupts : Should be the interrupt number of M_CAN interrupt
|
||||
line 0 and line 1, could be same if sharing
|
||||
the same interrupt.
|
||||
- interrupt-names : Should contain "int0" and "int1"
|
||||
- clocks : Clocks used by controller, should be host clock
|
||||
and CAN clock.
|
||||
- clock-names : Should contain "hclk" and "cclk"
|
||||
- pinctrl-<n> : Pinctrl states as described in bindings/pinctrl/pinctrl-bindings.txt
|
||||
- pinctrl-names : Names corresponding to the numbered pinctrl states
|
||||
- bosch,mram-cfg : Message RAM configuration data.
|
||||
Multiple M_CAN instances can share the same Message
|
||||
RAM and each element(e.g Rx FIFO or Tx Buffer and etc)
|
||||
number in Message RAM is also configurable,
|
||||
so this property is telling driver how the shared or
|
||||
private Message RAM are used by this M_CAN controller.
|
||||
|
||||
The format should be as follows:
|
||||
<offset sidf_elems xidf_elems rxf0_elems rxf1_elems
|
||||
rxb_elems txe_elems txb_elems>
|
||||
The 'offset' is an address offset of the Message RAM
|
||||
where the following elements start from. This is
|
||||
usually set to 0x0 if you're using a private Message
|
||||
RAM. The remain cells are used to specify how many
|
||||
elements are used for each FIFO/Buffer.
|
||||
|
||||
M_CAN includes the following elements according to user manual:
|
||||
11-bit Filter 0-128 elements / 0-128 words
|
||||
29-bit Filter 0-64 elements / 0-128 words
|
||||
Rx FIFO 0 0-64 elements / 0-1152 words
|
||||
Rx FIFO 1 0-64 elements / 0-1152 words
|
||||
Rx Buffers 0-64 elements / 0-1152 words
|
||||
Tx Event FIFO 0-32 elements / 0-64 words
|
||||
Tx Buffers 0-32 elements / 0-576 words
|
||||
|
||||
Please refer to 2.4.1 Message RAM Configuration in
|
||||
Bosch M_CAN user manual for details.
|
||||
|
||||
Example:
|
||||
SoC dtsi:
|
||||
m_can1: can@020e8000 {
|
||||
compatible = "bosch,m_can";
|
||||
reg = <0x020e8000 0x4000>, <0x02298000 0x4000>;
|
||||
reg-names = "m_can", "message_ram";
|
||||
interrupts = <0 114 0x04>,
|
||||
<0 114 0x04>;
|
||||
interrupt-names = "int0", "int1";
|
||||
clocks = <&clks IMX6SX_CLK_CANFD>,
|
||||
<&clks IMX6SX_CLK_CANFD>;
|
||||
clock-names = "hclk", "cclk";
|
||||
bosch,mram-cfg = <0x0 0 0 32 0 0 0 1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
Board dts:
|
||||
&m_can1 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_m_can1>;
|
||||
status = "enabled";
|
||||
};
|
43
Documentation/devicetree/bindings/net/can/rcar_can.txt
Normal file
43
Documentation/devicetree/bindings/net/can/rcar_can.txt
Normal file
|
@ -0,0 +1,43 @@
|
|||
Renesas R-Car CAN controller Device Tree Bindings
|
||||
-------------------------------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: "renesas,can-r8a7778" if CAN controller is a part of R8A7778 SoC.
|
||||
"renesas,can-r8a7779" if CAN controller is a part of R8A7779 SoC.
|
||||
"renesas,can-r8a7790" if CAN controller is a part of R8A7790 SoC.
|
||||
"renesas,can-r8a7791" if CAN controller is a part of R8A7791 SoC.
|
||||
- reg: physical base address and size of the R-Car CAN register map.
|
||||
- interrupts: interrupt specifier for the sole interrupt.
|
||||
- clocks: phandles and clock specifiers for 3 CAN clock inputs.
|
||||
- clock-names: 3 clock input name strings: "clkp1", "clkp2", "can_clk".
|
||||
- pinctrl-0: pin control group to be used for this controller.
|
||||
- pinctrl-names: must be "default".
|
||||
|
||||
Optional properties:
|
||||
- renesas,can-clock-select: R-Car CAN Clock Source Select. Valid values are:
|
||||
<0x0> (default) : Peripheral clock (clkp1)
|
||||
<0x1> : Peripheral clock (clkp2)
|
||||
<0x3> : Externally input clock
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
SoC common .dtsi file:
|
||||
|
||||
can0: can@e6e80000 {
|
||||
compatible = "renesas,can-r8a7791";
|
||||
reg = <0 0xe6e80000 0 0x1000>;
|
||||
interrupts = <0 186 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&mstp9_clks R8A7791_CLK_RCAN0>,
|
||||
<&cpg_clocks R8A7791_CLK_RCAN>, <&can_clk>;
|
||||
clock-names = "clkp1", "clkp2", "can_clk";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
Board specific .dts file:
|
||||
|
||||
&can0 {
|
||||
pinctrl-0 = <&can0_pins>;
|
||||
pinctrl-names = "default";
|
||||
status = "okay";
|
||||
};
|
|
@ -24,15 +24,17 @@ Optional properties:
|
|||
- ti,hwmods : Must be "cpgmac0"
|
||||
- no_bd_ram : Must be 0 or 1
|
||||
- dual_emac : Specifies Switch to act as Dual EMAC
|
||||
- syscon : Phandle to the system control device node, which is
|
||||
the control module device of the am33x
|
||||
|
||||
Slave Properties:
|
||||
Required properties:
|
||||
- phy_id : Specifies slave phy id
|
||||
- phy-mode : See ethernet.txt file in the same directory
|
||||
- mac-address : See ethernet.txt file in the same directory
|
||||
|
||||
Optional properties:
|
||||
- dual_emac_res_vlan : Specifies VID to be used to segregate the ports
|
||||
- mac-address : See ethernet.txt file in the same directory
|
||||
|
||||
Note: "ti,hwmods" field is used to fetch the base address and irq
|
||||
resources from TI, omap hwmod data base during device registration.
|
||||
|
@ -57,6 +59,7 @@ Examples:
|
|||
active_slave = <0>;
|
||||
cpts_clock_mult = <0x80000000>;
|
||||
cpts_clock_shift = <29>;
|
||||
syscon = <&cm>;
|
||||
cpsw_emac0: slave@0 {
|
||||
phy_id = <&davinci_mdio>, <0>;
|
||||
phy-mode = "rgmii-txid";
|
||||
|
@ -85,6 +88,7 @@ Examples:
|
|||
active_slave = <0>;
|
||||
cpts_clock_mult = <0x80000000>;
|
||||
cpts_clock_shift = <29>;
|
||||
syscon = <&cm>;
|
||||
cpsw_emac0: slave@0 {
|
||||
phy_id = <&davinci_mdio>, <0>;
|
||||
phy-mode = "rgmii-txid";
|
||||
|
|
|
@ -39,6 +39,22 @@ Optionnal property:
|
|||
This property is only used when switches are being
|
||||
chained/cascaded together.
|
||||
|
||||
- phy-handle : Phandle to a PHY on an external MDIO bus, not the
|
||||
switch internal one. See
|
||||
Documentation/devicetree/bindings/net/ethernet.txt
|
||||
for details.
|
||||
|
||||
- phy-mode : String representing the connection to the designated
|
||||
PHY node specified by the 'phy-handle' property. See
|
||||
Documentation/devicetree/bindings/net/ethernet.txt
|
||||
for details.
|
||||
|
||||
Optional subnodes:
|
||||
- fixed-link : Fixed-link subnode describing a link to a non-MDIO
|
||||
managed entity. See
|
||||
Documentation/devicetree/bindings/net/fixed-link.txt
|
||||
for details.
|
||||
|
||||
Example:
|
||||
|
||||
dsa@0 {
|
||||
|
@ -58,6 +74,7 @@ Example:
|
|||
port@0 {
|
||||
reg = <0>;
|
||||
label = "lan1";
|
||||
phy-handle = <&phy0>;
|
||||
};
|
||||
|
||||
port@1 {
|
||||
|
|
50
Documentation/devicetree/bindings/net/emac_rockchip.txt
Normal file
50
Documentation/devicetree/bindings/net/emac_rockchip.txt
Normal file
|
@ -0,0 +1,50 @@
|
|||
* ARC EMAC 10/100 Ethernet platform driver for Rockchip Rk3066/RK3188 SoCs
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "rockchip,rk3066-emac" or "rockchip,rk3188-emac"
|
||||
according to the target SoC.
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain the EMAC interrupts
|
||||
- rockchip,grf: phandle to the syscon grf used to control speed and mode
|
||||
for emac.
|
||||
- phy: see ethernet.txt file in the same directory.
|
||||
- phy-mode: see ethernet.txt file in the same directory.
|
||||
|
||||
Optional properties:
|
||||
- phy-supply: phandle to a regulator if the PHY needs one
|
||||
|
||||
Clock handling:
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
- clock-names: Shall be "hclk" for the host clock needed to calculate and set
|
||||
polling period of EMAC and "macref" for the reference clock needed to transfer
|
||||
data to and from the phy.
|
||||
|
||||
Child nodes of the driver are the individual PHY devices connected to the
|
||||
MDIO bus. They must have a "reg" property given the PHY address on the MDIO bus.
|
||||
|
||||
Examples:
|
||||
|
||||
ethernet@10204000 {
|
||||
compatible = "rockchip,rk3188-emac";
|
||||
reg = <0xc0fc2000 0x3c>;
|
||||
interrupts = <6>;
|
||||
mac-address = [ 00 11 22 33 44 55 ];
|
||||
|
||||
clocks = <&cru HCLK_EMAC>, <&cru SCLK_MAC>;
|
||||
clock-names = "hclk", "macref";
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&phy_int>;
|
||||
|
||||
rockchip,grf = <&grf>;
|
||||
|
||||
phy = <&phy0>;
|
||||
phy-mode = "rmii";
|
||||
phy-supply = <&vcc_rmii>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phy0: ethernet-phy@0 {
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
|
@ -16,6 +16,12 @@ Optional properties:
|
|||
- phy-handle : phandle to the PHY device connected to this device.
|
||||
- fixed-link : Assume a fixed link. See fixed-link.txt in the same directory.
|
||||
Use instead of phy-handle.
|
||||
- fsl,num-tx-queues : The property is valid for enet-avb IP, which supports
|
||||
hw multi queues. Should specify the tx queue number, otherwise set tx queue
|
||||
number to 1.
|
||||
- fsl,num-rx-queues : The property is valid for enet-avb IP, which supports
|
||||
hw multi queues. Should specify the rx queue number, otherwise set rx queue
|
||||
number to 1.
|
||||
|
||||
Optional subnodes:
|
||||
- mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
|
||||
|
|
36
Documentation/devicetree/bindings/net/marvell-pxa168.txt
Normal file
36
Documentation/devicetree/bindings/net/marvell-pxa168.txt
Normal file
|
@ -0,0 +1,36 @@
|
|||
* Marvell PXA168 Ethernet Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "marvell,pxa168-eth".
|
||||
- reg: address and length of the register set for the device.
|
||||
- interrupts: interrupt for the device.
|
||||
- clocks: pointer to the clock for the device.
|
||||
|
||||
Optional properties:
|
||||
- port-id: Ethernet port number. Should be '0','1' or '2'.
|
||||
- #address-cells: must be 1 when using sub-nodes.
|
||||
- #size-cells: must be 0 when using sub-nodes.
|
||||
- phy-handle: see ethernet.txt file in the same directory.
|
||||
- local-mac-address: see ethernet.txt file in the same directory.
|
||||
|
||||
Sub-nodes:
|
||||
Each PHY can be represented as a sub-node. This is not mandatory.
|
||||
|
||||
Sub-nodes required properties:
|
||||
- reg: the MDIO address of the PHY.
|
||||
|
||||
Example:
|
||||
|
||||
eth0: ethernet@f7b90000 {
|
||||
compatible = "marvell,pxa168-eth";
|
||||
reg = <0xf7b90000 0x10000>;
|
||||
clocks = <&chip CLKID_GETH0>;
|
||||
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phy-handle = <ðphy0>;
|
||||
|
||||
ethphy0: ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
25
Documentation/devicetree/bindings/net/meson-dwmac.txt
Normal file
25
Documentation/devicetree/bindings/net/meson-dwmac.txt
Normal file
|
@ -0,0 +1,25 @@
|
|||
* Amlogic Meson DWMAC Ethernet controller
|
||||
|
||||
The device inherits all the properties of the dwmac/stmmac devices
|
||||
described in the file net/stmmac.txt with the following changes.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "amlogic,meson6-dwmac" along with "snps,dwmac"
|
||||
and any applicable more detailed version number
|
||||
described in net/stmmac.txt
|
||||
|
||||
- reg: should contain a register range for the dwmac controller and
|
||||
another one for the Amlogic specific configuration
|
||||
|
||||
Example:
|
||||
|
||||
ethmac: ethernet@c9410000 {
|
||||
compatible = "amlogic,meson6-dwmac", "snps,dwmac";
|
||||
reg = <0xc9410000 0x10000
|
||||
0xc1108108 0x4>;
|
||||
interrupts = <0 8 1>;
|
||||
interrupt-names = "macirq";
|
||||
clocks = <&clk81>;
|
||||
clock-names = "stmmaceth";
|
||||
}
|
|
@ -26,7 +26,7 @@ Example (for ARM-based BeagleBoard xM with ST21NFCB on I2C2):
|
|||
clock-frequency = <400000>;
|
||||
|
||||
interrupt-parent = <&gpio5>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
reset-gpios = <&gpio5 29 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
|
|
|
@ -13,6 +13,11 @@ Optional SoC Specific Properties:
|
|||
- pinctrl-names: Contains only one value - "default".
|
||||
- pintctrl-0: Specifies the pin control groups used for this controller.
|
||||
- autosuspend-delay: Specify autosuspend delay in milliseconds.
|
||||
- vin-voltage-override: Specify voltage of VIN pin in microvolts.
|
||||
- irq-status-read-quirk: Specify that the trf7970a being used has the
|
||||
"IRQ Status Read" erratum.
|
||||
- en2-rf-quirk: Specify that the trf7970a being used has the "EN2 RF"
|
||||
erratum.
|
||||
|
||||
Example (for ARM-based BeagleBone with TRF7970A on SPI1):
|
||||
|
||||
|
@ -30,7 +35,10 @@ Example (for ARM-based BeagleBone with TRF7970A on SPI1):
|
|||
ti,enable-gpios = <&gpio2 2 GPIO_ACTIVE_LOW>,
|
||||
<&gpio2 5 GPIO_ACTIVE_LOW>;
|
||||
vin-supply = <&ldo3_reg>;
|
||||
vin-voltage-override = <5000000>;
|
||||
autosuspend-delay = <30000>;
|
||||
irq-status-read-quirk;
|
||||
en2-rf-quirk;
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
|
47
Documentation/devicetree/bindings/net/qca-qca7000-spi.txt
Normal file
47
Documentation/devicetree/bindings/net/qca-qca7000-spi.txt
Normal file
|
@ -0,0 +1,47 @@
|
|||
* Qualcomm QCA7000 (Ethernet over SPI protocol)
|
||||
|
||||
Note: The QCA7000 is useable as a SPI device. In this case it must be defined
|
||||
as a child of a SPI master in the device tree.
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "qca,qca7000"
|
||||
- reg : Should specify the SPI chip select
|
||||
- interrupts : The first cell should specify the index of the source interrupt
|
||||
and the second cell should specify the trigger type as rising edge
|
||||
- spi-cpha : Must be set
|
||||
- spi-cpol: Must be set
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent : Specify the pHandle of the source interrupt
|
||||
- spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at.
|
||||
Numbers smaller than 1000000 or greater than 16000000 are invalid. Missing
|
||||
the property will set the SPI frequency to 8000000 Hertz.
|
||||
- local-mac-address: 6 bytes, MAC address
|
||||
- qca,legacy-mode : Set the SPI data transfer of the QCA7000 to legacy mode.
|
||||
In this mode the SPI master must toggle the chip select between each data
|
||||
word. In burst mode these gaps aren't necessary, which is faster.
|
||||
This setting depends on how the QCA7000 is setup via GPIO pin strapping.
|
||||
If the property is missing the driver defaults to burst mode.
|
||||
|
||||
Example:
|
||||
|
||||
/* Freescale i.MX28 SPI master*/
|
||||
ssp2: spi@80014000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,imx28-spi";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi2_pins_a>;
|
||||
status = "okay";
|
||||
|
||||
qca7000: ethernet@0 {
|
||||
compatible = "qca,qca7000";
|
||||
reg = <0x0>;
|
||||
interrupt-parent = <&gpio3>; /* GPIO Bank 3 */
|
||||
interrupts = <25 0x1>; /* Index: 25, rising edge */
|
||||
spi-cpha; /* SPI mode: CPHA=1 */
|
||||
spi-cpol; /* SPI mode: CPOL=1 */
|
||||
spi-max-frequency = <8000000>; /* freq: 8 MHz */
|
||||
local-mac-address = [ A0 B0 C0 D0 E0 F0 ];
|
||||
};
|
||||
};
|
|
@ -12,6 +12,10 @@ Required properties:
|
|||
- altr,sysmgr-syscon : Should be the phandle to the system manager node that
|
||||
encompasses the glue register, the register offset, and the register shift.
|
||||
|
||||
Optional properties:
|
||||
altr,emac-splitter: Should be the phandle to the emac splitter soft IP node if
|
||||
DWMAC controller is connected emac splitter.
|
||||
|
||||
Example:
|
||||
|
||||
gmac0: ethernet@ff700000 {
|
||||
|
|
43
Documentation/networking/dctcp.txt
Normal file
43
Documentation/networking/dctcp.txt
Normal file
|
@ -0,0 +1,43 @@
|
|||
DCTCP (DataCenter TCP)
|
||||
----------------------
|
||||
|
||||
DCTCP is an enhancement to the TCP congestion control algorithm for data
|
||||
center networks and leverages Explicit Congestion Notification (ECN) in
|
||||
the data center network to provide multi-bit feedback to the end hosts.
|
||||
|
||||
To enable it on end hosts:
|
||||
|
||||
sysctl -w net.ipv4.tcp_congestion_control=dctcp
|
||||
|
||||
All switches in the data center network running DCTCP must support ECN
|
||||
marking and be configured for marking when reaching defined switch buffer
|
||||
thresholds. The default ECN marking threshold heuristic for DCTCP on
|
||||
switches is 20 packets (30KB) at 1Gbps, and 65 packets (~100KB) at 10Gbps,
|
||||
but might need further careful tweaking.
|
||||
|
||||
For more details, see below documents:
|
||||
|
||||
Paper:
|
||||
|
||||
The algorithm is further described in detail in the following two
|
||||
SIGCOMM/SIGMETRICS papers:
|
||||
|
||||
i) Mohammad Alizadeh, Albert Greenberg, David A. Maltz, Jitendra Padhye,
|
||||
Parveen Patel, Balaji Prabhakar, Sudipta Sengupta, and Murari Sridharan:
|
||||
"Data Center TCP (DCTCP)", Data Center Networks session
|
||||
Proc. ACM SIGCOMM, New Delhi, 2010.
|
||||
http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf
|
||||
http://www.sigcomm.org/ccr/papers/2010/October/1851275.1851192
|
||||
|
||||
ii) Mohammad Alizadeh, Adel Javanmard, and Balaji Prabhakar:
|
||||
"Analysis of DCTCP: Stability, Convergence, and Fairness"
|
||||
Proc. ACM SIGMETRICS, San Jose, 2011.
|
||||
http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp_analysis-full.pdf
|
||||
|
||||
IETF informational draft:
|
||||
|
||||
http://tools.ietf.org/html/draft-bensley-tcpm-dctcp-00
|
||||
|
||||
DCTCP site:
|
||||
|
||||
http://simula.stanford.edu/~alizade/Site/DCTCP.html
|
|
@ -951,7 +951,7 @@ Size modifier is one of ...
|
|||
|
||||
Mode modifier is one of:
|
||||
|
||||
BPF_IMM 0x00 /* classic BPF only, reserved in eBPF */
|
||||
BPF_IMM 0x00 /* used for 32-bit mov in classic BPF and 64-bit in eBPF */
|
||||
BPF_ABS 0x20
|
||||
BPF_IND 0x40
|
||||
BPF_MEM 0x60
|
||||
|
@ -995,6 +995,275 @@ BPF_XADD | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
|
|||
Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW. Note that 1 and
|
||||
2 byte atomic increments are not supported.
|
||||
|
||||
eBPF has one 16-byte instruction: BPF_LD | BPF_DW | BPF_IMM which consists
|
||||
of two consecutive 'struct bpf_insn' 8-byte blocks and interpreted as single
|
||||
instruction that loads 64-bit immediate value into a dst_reg.
|
||||
Classic BPF has similar instruction: BPF_LD | BPF_W | BPF_IMM which loads
|
||||
32-bit immediate value into a register.
|
||||
|
||||
eBPF verifier
|
||||
-------------
|
||||
The safety of the eBPF program is determined in two steps.
|
||||
|
||||
First step does DAG check to disallow loops and other CFG validation.
|
||||
In particular it will detect programs that have unreachable instructions.
|
||||
(though classic BPF checker allows them)
|
||||
|
||||
Second step starts from the first insn and descends all possible paths.
|
||||
It simulates execution of every insn and observes the state change of
|
||||
registers and stack.
|
||||
|
||||
At the start of the program the register R1 contains a pointer to context
|
||||
and has type PTR_TO_CTX.
|
||||
If verifier sees an insn that does R2=R1, then R2 has now type
|
||||
PTR_TO_CTX as well and can be used on the right hand side of expression.
|
||||
If R1=PTR_TO_CTX and insn is R2=R1+R1, then R2=UNKNOWN_VALUE,
|
||||
since addition of two valid pointers makes invalid pointer.
|
||||
(In 'secure' mode verifier will reject any type of pointer arithmetic to make
|
||||
sure that kernel addresses don't leak to unprivileged users)
|
||||
|
||||
If register was never written to, it's not readable:
|
||||
bpf_mov R0 = R2
|
||||
bpf_exit
|
||||
will be rejected, since R2 is unreadable at the start of the program.
|
||||
|
||||
After kernel function call, R1-R5 are reset to unreadable and
|
||||
R0 has a return type of the function.
|
||||
|
||||
Since R6-R9 are callee saved, their state is preserved across the call.
|
||||
bpf_mov R6 = 1
|
||||
bpf_call foo
|
||||
bpf_mov R0 = R6
|
||||
bpf_exit
|
||||
is a correct program. If there was R1 instead of R6, it would have
|
||||
been rejected.
|
||||
|
||||
load/store instructions are allowed only with registers of valid types, which
|
||||
are PTR_TO_CTX, PTR_TO_MAP, FRAME_PTR. They are bounds and alignment checked.
|
||||
For example:
|
||||
bpf_mov R1 = 1
|
||||
bpf_mov R2 = 2
|
||||
bpf_xadd *(u32 *)(R1 + 3) += R2
|
||||
bpf_exit
|
||||
will be rejected, since R1 doesn't have a valid pointer type at the time of
|
||||
execution of instruction bpf_xadd.
|
||||
|
||||
At the start R1 type is PTR_TO_CTX (a pointer to generic 'struct bpf_context')
|
||||
A callback is used to customize verifier to restrict eBPF program access to only
|
||||
certain fields within ctx structure with specified size and alignment.
|
||||
|
||||
For example, the following insn:
|
||||
bpf_ld R0 = *(u32 *)(R6 + 8)
|
||||
intends to load a word from address R6 + 8 and store it into R0
|
||||
If R6=PTR_TO_CTX, via is_valid_access() callback the verifier will know
|
||||
that offset 8 of size 4 bytes can be accessed for reading, otherwise
|
||||
the verifier will reject the program.
|
||||
If R6=FRAME_PTR, then access should be aligned and be within
|
||||
stack bounds, which are [-MAX_BPF_STACK, 0). In this example offset is 8,
|
||||
so it will fail verification, since it's out of bounds.
|
||||
|
||||
The verifier will allow eBPF program to read data from stack only after
|
||||
it wrote into it.
|
||||
Classic BPF verifier does similar check with M[0-15] memory slots.
|
||||
For example:
|
||||
bpf_ld R0 = *(u32 *)(R10 - 4)
|
||||
bpf_exit
|
||||
is invalid program.
|
||||
Though R10 is correct read-only register and has type FRAME_PTR
|
||||
and R10 - 4 is within stack bounds, there were no stores into that location.
|
||||
|
||||
Pointer register spill/fill is tracked as well, since four (R6-R9)
|
||||
callee saved registers may not be enough for some programs.
|
||||
|
||||
Allowed function calls are customized with bpf_verifier_ops->get_func_proto()
|
||||
The eBPF verifier will check that registers match argument constraints.
|
||||
After the call register R0 will be set to return type of the function.
|
||||
|
||||
Function calls is a main mechanism to extend functionality of eBPF programs.
|
||||
Socket filters may let programs to call one set of functions, whereas tracing
|
||||
filters may allow completely different set.
|
||||
|
||||
If a function made accessible to eBPF program, it needs to be thought through
|
||||
from safety point of view. The verifier will guarantee that the function is
|
||||
called with valid arguments.
|
||||
|
||||
seccomp vs socket filters have different security restrictions for classic BPF.
|
||||
Seccomp solves this by two stage verifier: classic BPF verifier is followed
|
||||
by seccomp verifier. In case of eBPF one configurable verifier is shared for
|
||||
all use cases.
|
||||
|
||||
See details of eBPF verifier in kernel/bpf/verifier.c
|
||||
|
||||
eBPF maps
|
||||
---------
|
||||
'maps' is a generic storage of different types for sharing data between kernel
|
||||
and userspace.
|
||||
|
||||
The maps are accessed from user space via BPF syscall, which has commands:
|
||||
- create a map with given type and attributes
|
||||
map_fd = bpf(BPF_MAP_CREATE, union bpf_attr *attr, u32 size)
|
||||
using attr->map_type, attr->key_size, attr->value_size, attr->max_entries
|
||||
returns process-local file descriptor or negative error
|
||||
|
||||
- lookup key in a given map
|
||||
err = bpf(BPF_MAP_LOOKUP_ELEM, union bpf_attr *attr, u32 size)
|
||||
using attr->map_fd, attr->key, attr->value
|
||||
returns zero and stores found elem into value or negative error
|
||||
|
||||
- create or update key/value pair in a given map
|
||||
err = bpf(BPF_MAP_UPDATE_ELEM, union bpf_attr *attr, u32 size)
|
||||
using attr->map_fd, attr->key, attr->value
|
||||
returns zero or negative error
|
||||
|
||||
- find and delete element by key in a given map
|
||||
err = bpf(BPF_MAP_DELETE_ELEM, union bpf_attr *attr, u32 size)
|
||||
using attr->map_fd, attr->key
|
||||
|
||||
- to delete map: close(fd)
|
||||
Exiting process will delete maps automatically
|
||||
|
||||
userspace programs use this syscall to create/access maps that eBPF programs
|
||||
are concurrently updating.
|
||||
|
||||
maps can have different types: hash, array, bloom filter, radix-tree, etc.
|
||||
|
||||
The map is defined by:
|
||||
. type
|
||||
. max number of elements
|
||||
. key size in bytes
|
||||
. value size in bytes
|
||||
|
||||
Understanding eBPF verifier messages
|
||||
------------------------------------
|
||||
|
||||
The following are few examples of invalid eBPF programs and verifier error
|
||||
messages as seen in the log:
|
||||
|
||||
Program with unreachable instructions:
|
||||
static struct bpf_insn prog[] = {
|
||||
BPF_EXIT_INSN(),
|
||||
BPF_EXIT_INSN(),
|
||||
};
|
||||
Error:
|
||||
unreachable insn 1
|
||||
|
||||
Program that reads uninitialized register:
|
||||
BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (bf) r0 = r2
|
||||
R2 !read_ok
|
||||
|
||||
Program that doesn't initialize R0 before exiting:
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_1),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (bf) r2 = r1
|
||||
1: (95) exit
|
||||
R0 !read_ok
|
||||
|
||||
Program that accesses stack out of bounds:
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, 8, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (7a) *(u64 *)(r10 +8) = 0
|
||||
invalid stack off=8 size=8
|
||||
|
||||
Program that doesn't initialize stack before passing its address into function:
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (bf) r2 = r10
|
||||
1: (07) r2 += -8
|
||||
2: (b7) r1 = 0x0
|
||||
3: (85) call 1
|
||||
invalid indirect read from stack off -8+0 size 8
|
||||
|
||||
Program that uses invalid map_fd=0 while calling to map_lookup_elem() function:
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (7a) *(u64 *)(r10 -8) = 0
|
||||
1: (bf) r2 = r10
|
||||
2: (07) r2 += -8
|
||||
3: (b7) r1 = 0x0
|
||||
4: (85) call 1
|
||||
fd 0 is not pointing to valid bpf_map
|
||||
|
||||
Program that doesn't check return value of map_lookup_elem() before accessing
|
||||
map element:
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (7a) *(u64 *)(r10 -8) = 0
|
||||
1: (bf) r2 = r10
|
||||
2: (07) r2 += -8
|
||||
3: (b7) r1 = 0x0
|
||||
4: (85) call 1
|
||||
5: (7a) *(u64 *)(r0 +0) = 0
|
||||
R0 invalid mem access 'map_value_or_null'
|
||||
|
||||
Program that correctly checks map_lookup_elem() returned value for NULL, but
|
||||
accesses the memory with incorrect alignment:
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (7a) *(u64 *)(r10 -8) = 0
|
||||
1: (bf) r2 = r10
|
||||
2: (07) r2 += -8
|
||||
3: (b7) r1 = 1
|
||||
4: (85) call 1
|
||||
5: (15) if r0 == 0x0 goto pc+1
|
||||
R0=map_ptr R10=fp
|
||||
6: (7a) *(u64 *)(r0 +4) = 0
|
||||
misaligned access off 4 size 8
|
||||
|
||||
Program that correctly checks map_lookup_elem() returned value for NULL and
|
||||
accesses memory with correct alignment in one side of 'if' branch, but fails
|
||||
to do so in the other side of 'if' branch:
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1),
|
||||
BPF_EXIT_INSN(),
|
||||
Error:
|
||||
0: (7a) *(u64 *)(r10 -8) = 0
|
||||
1: (bf) r2 = r10
|
||||
2: (07) r2 += -8
|
||||
3: (b7) r1 = 1
|
||||
4: (85) call 1
|
||||
5: (15) if r0 == 0x0 goto pc+2
|
||||
R0=map_ptr R10=fp
|
||||
6: (7a) *(u64 *)(r0 +0) = 0
|
||||
7: (95) exit
|
||||
|
||||
from 5 to 8: R0=imm0 R10=fp
|
||||
8: (7a) *(u64 *)(r0 +0) = 1
|
||||
R0 invalid mem access 'imm'
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
|
|
|
@ -65,6 +65,12 @@ neigh/default/gc_thresh1 - INTEGER
|
|||
purge entries if there are fewer than this number.
|
||||
Default: 128
|
||||
|
||||
neigh/default/gc_thresh2 - INTEGER
|
||||
Threshold when garbage collector becomes more aggressive about
|
||||
purging entries. Entries older than 5 seconds will be cleared
|
||||
when over this number.
|
||||
Default: 512
|
||||
|
||||
neigh/default/gc_thresh3 - INTEGER
|
||||
Maximum number of neighbor entries allowed. Increase this
|
||||
when using large numbers of interfaces and when communicating
|
||||
|
@ -757,8 +763,21 @@ icmp_ratelimit - INTEGER
|
|||
icmp_ratemask (see below) to specific targets.
|
||||
0 to disable any limiting,
|
||||
otherwise the minimal space between responses in milliseconds.
|
||||
Note that another sysctl, icmp_msgs_per_sec limits the number
|
||||
of ICMP packets sent on all targets.
|
||||
Default: 1000
|
||||
|
||||
icmp_msgs_per_sec - INTEGER
|
||||
Limit maximal number of ICMP packets sent per second from this host.
|
||||
Only messages whose type matches icmp_ratemask (see below) are
|
||||
controlled by this limit.
|
||||
Default: 1000
|
||||
|
||||
icmp_msgs_burst - INTEGER
|
||||
icmp_msgs_per_sec controls number of ICMP packets sent per second,
|
||||
while icmp_msgs_burst controls the burst size of these packets.
|
||||
Default: 50
|
||||
|
||||
icmp_ratemask - INTEGER
|
||||
Mask made of ICMP types for which rates are being limited.
|
||||
Significant bits: IHGFEDCBA9876543210
|
||||
|
@ -832,6 +851,11 @@ igmp_max_memberships - INTEGER
|
|||
|
||||
conf/all/* is special, changes the settings for all interfaces
|
||||
|
||||
igmp_qrv - INTEGER
|
||||
Controls the IGMP query robustness variable (see RFC2236 8.1).
|
||||
Default: 2 (as specified by RFC2236 8.1)
|
||||
Minimum: 1 (as specified by RFC6636 4.5)
|
||||
|
||||
log_martians - BOOLEAN
|
||||
Log packets with impossible addresses to kernel log.
|
||||
log_martians for the interface will be enabled if at least one of
|
||||
|
@ -935,14 +959,9 @@ accept_source_route - BOOLEAN
|
|||
FALSE (host)
|
||||
|
||||
accept_local - BOOLEAN
|
||||
Accept packets with local source addresses. In combination
|
||||
with suitable routing, this can be used to direct packets
|
||||
between two local interfaces over the wire and have them
|
||||
accepted properly.
|
||||
|
||||
rp_filter must be set to a non-zero value in order for
|
||||
accept_local to have an effect.
|
||||
|
||||
Accept packets with local source addresses. In combination with
|
||||
suitable routing, this can be used to direct packets between two
|
||||
local interfaces over the wire and have them accepted properly.
|
||||
default FALSE
|
||||
|
||||
route_localnet - BOOLEAN
|
||||
|
@ -1140,6 +1159,11 @@ anycast_src_echo_reply - BOOLEAN
|
|||
FALSE: disabled
|
||||
Default: FALSE
|
||||
|
||||
mld_qrv - INTEGER
|
||||
Controls the MLD query robustness variable (see RFC3810 9.1).
|
||||
Default: 2 (as specified by RFC3810 9.1)
|
||||
Minimum: 1 (as specified by RFC6636 4.5)
|
||||
|
||||
IPv6 Fragmentation:
|
||||
|
||||
ip6frag_high_thresh - INTEGER
|
||||
|
|
|
@ -99,6 +99,9 @@ Examples:
|
|||
|
||||
pgset "clone_skb 1" sets the number of copies of the same packet
|
||||
pgset "clone_skb 0" use single SKB for all transmits
|
||||
pgset "burst 8" uses xmit_more API to queue 8 copies of the same
|
||||
packet and update HW tx queue tail pointer once.
|
||||
"burst 1" is the default
|
||||
pgset "pkt_size 9014" sets packet size to 9014
|
||||
pgset "frags 5" packet will consist of 5 fragments
|
||||
pgset "count 200000" sets number of packets to send, set to zero
|
||||
|
|
|
@ -1,102 +1,307 @@
|
|||
The existing interfaces for getting network packages time stamped are:
|
||||
|
||||
1. Control Interfaces
|
||||
|
||||
The interfaces for receiving network packages timestamps are:
|
||||
|
||||
* SO_TIMESTAMP
|
||||
Generate time stamp for each incoming packet using the (not necessarily
|
||||
monotonous!) system time. Result is returned via recv_msg() in a
|
||||
control message as timeval (usec resolution).
|
||||
Generates a timestamp for each incoming packet in (not necessarily
|
||||
monotonic) system time. Reports the timestamp via recvmsg() in a
|
||||
control message as struct timeval (usec resolution).
|
||||
|
||||
* SO_TIMESTAMPNS
|
||||
Same time stamping mechanism as SO_TIMESTAMP, but returns result as
|
||||
timespec (nsec resolution).
|
||||
Same timestamping mechanism as SO_TIMESTAMP, but reports the
|
||||
timestamp as struct timespec (nsec resolution).
|
||||
|
||||
* IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]
|
||||
Only for multicasts: approximate send time stamp by receiving the looped
|
||||
packet and using its receive time stamp.
|
||||
Only for multicast:approximate transmit timestamp obtained by
|
||||
reading the looped packet receive timestamp.
|
||||
|
||||
The following interface complements the existing ones: receive time
|
||||
stamps can be generated and returned for arbitrary packets and much
|
||||
closer to the point where the packet is really sent. Time stamps can
|
||||
be generated in software (as before) or in hardware (if the hardware
|
||||
has such a feature).
|
||||
* SO_TIMESTAMPING
|
||||
Generates timestamps on reception, transmission or both. Supports
|
||||
multiple timestamp sources, including hardware. Supports generating
|
||||
timestamps for stream sockets.
|
||||
|
||||
SO_TIMESTAMPING:
|
||||
|
||||
Instructs the socket layer which kind of information should be collected
|
||||
and/or reported. The parameter is an integer with some of the following
|
||||
bits set. Setting other bits is an error and doesn't change the current
|
||||
state.
|
||||
1.1 SO_TIMESTAMP:
|
||||
|
||||
Four of the bits are requests to the stack to try to generate
|
||||
timestamps. Any combination of them is valid.
|
||||
This socket option enables timestamping of datagrams on the reception
|
||||
path. Because the destination socket, if any, is not known early in
|
||||
the network stack, the feature has to be enabled for all packets. The
|
||||
same is true for all early receive timestamp options.
|
||||
|
||||
SOF_TIMESTAMPING_TX_HARDWARE: try to obtain send time stamps in hardware
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE: try to obtain send time stamps in software
|
||||
SOF_TIMESTAMPING_RX_HARDWARE: try to obtain receive time stamps in hardware
|
||||
SOF_TIMESTAMPING_RX_SOFTWARE: try to obtain receive time stamps in software
|
||||
For interface details, see `man 7 socket`.
|
||||
|
||||
|
||||
1.2 SO_TIMESTAMPNS:
|
||||
|
||||
This option is identical to SO_TIMESTAMP except for the returned data type.
|
||||
Its struct timespec allows for higher resolution (ns) timestamps than the
|
||||
timeval of SO_TIMESTAMP (ms).
|
||||
|
||||
|
||||
1.3 SO_TIMESTAMPING:
|
||||
|
||||
Supports multiple types of timestamp requests. As a result, this
|
||||
socket option takes a bitmap of flags, not a boolean. In
|
||||
|
||||
err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, (void *) val, &val);
|
||||
|
||||
val is an integer with any of the following bits set. Setting other
|
||||
bit returns EINVAL and does not change the current state.
|
||||
|
||||
|
||||
1.3.1 Timestamp Generation
|
||||
|
||||
Some bits are requests to the stack to try to generate timestamps. Any
|
||||
combination of them is valid. Changes to these bits apply to newly
|
||||
created packets, not to packets already in the stack. As a result, it
|
||||
is possible to selectively request timestamps for a subset of packets
|
||||
(e.g., for sampling) by embedding an send() call within two setsockopt
|
||||
calls, one to enable timestamp generation and one to disable it.
|
||||
Timestamps may also be generated for reasons other than being
|
||||
requested by a particular socket, such as when receive timestamping is
|
||||
enabled system wide, as explained earlier.
|
||||
|
||||
SOF_TIMESTAMPING_RX_HARDWARE:
|
||||
Request rx timestamps generated by the network adapter.
|
||||
|
||||
SOF_TIMESTAMPING_RX_SOFTWARE:
|
||||
Request rx timestamps when data enters the kernel. These timestamps
|
||||
are generated just after a device driver hands a packet to the
|
||||
kernel receive stack.
|
||||
|
||||
SOF_TIMESTAMPING_TX_HARDWARE:
|
||||
Request tx timestamps generated by the network adapter.
|
||||
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE:
|
||||
Request tx timestamps when data leaves the kernel. These timestamps
|
||||
are generated in the device driver as close as possible, but always
|
||||
prior to, passing the packet to the network interface. Hence, they
|
||||
require driver support and may not be available for all devices.
|
||||
|
||||
SOF_TIMESTAMPING_TX_SCHED:
|
||||
Request tx timestamps prior to entering the packet scheduler. Kernel
|
||||
transmit latency is, if long, often dominated by queuing delay. The
|
||||
difference between this timestamp and one taken at
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE will expose this latency independent
|
||||
of protocol processing. The latency incurred in protocol
|
||||
processing, if any, can be computed by subtracting a userspace
|
||||
timestamp taken immediately before send() from this timestamp. On
|
||||
machines with virtual devices where a transmitted packet travels
|
||||
through multiple devices and, hence, multiple packet schedulers,
|
||||
a timestamp is generated at each layer. This allows for fine
|
||||
grained measurement of queuing delay.
|
||||
|
||||
SOF_TIMESTAMPING_TX_ACK:
|
||||
Request tx timestamps when all data in the send buffer has been
|
||||
acknowledged. This only makes sense for reliable protocols. It is
|
||||
currently only implemented for TCP. For that protocol, it may
|
||||
over-report measurement, because the timestamp is generated when all
|
||||
data up to and including the buffer at send() was acknowledged: the
|
||||
cumulative acknowledgment. The mechanism ignores SACK and FACK.
|
||||
|
||||
|
||||
1.3.2 Timestamp Reporting
|
||||
|
||||
The other three bits control which timestamps will be reported in a
|
||||
generated control message. If none of these bits are set or if none of
|
||||
the set bits correspond to data that is available, then the control
|
||||
message will not be generated:
|
||||
generated control message. Changes to the bits take immediate
|
||||
effect at the timestamp reporting locations in the stack. Timestamps
|
||||
are only reported for packets that also have the relevant timestamp
|
||||
generation request set.
|
||||
|
||||
SOF_TIMESTAMPING_SOFTWARE: report systime if available
|
||||
SOF_TIMESTAMPING_SYS_HARDWARE: report hwtimetrans if available (deprecated)
|
||||
SOF_TIMESTAMPING_RAW_HARDWARE: report hwtimeraw if available
|
||||
SOF_TIMESTAMPING_SOFTWARE:
|
||||
Report any software timestamps when available.
|
||||
|
||||
It is worth noting that timestamps may be collected for reasons other
|
||||
than being requested by a particular socket with
|
||||
SOF_TIMESTAMPING_[TR]X_(HARD|SOFT)WARE. For example, most drivers that
|
||||
can generate hardware receive timestamps ignore
|
||||
SOF_TIMESTAMPING_RX_HARDWARE. It is still a good idea to set that flag
|
||||
in case future drivers pay attention.
|
||||
SOF_TIMESTAMPING_SYS_HARDWARE:
|
||||
This option is deprecated and ignored.
|
||||
|
||||
If timestamps are reported, they will appear in a control message with
|
||||
cmsg_level==SOL_SOCKET, cmsg_type==SO_TIMESTAMPING, and a payload like
|
||||
this:
|
||||
SOF_TIMESTAMPING_RAW_HARDWARE:
|
||||
Report hardware timestamps as generated by
|
||||
SOF_TIMESTAMPING_TX_HARDWARE when available.
|
||||
|
||||
|
||||
1.3.3 Timestamp Options
|
||||
|
||||
The interface supports one option
|
||||
|
||||
SOF_TIMESTAMPING_OPT_ID:
|
||||
|
||||
Generate a unique identifier along with each packet. A process can
|
||||
have multiple concurrent timestamping requests outstanding. Packets
|
||||
can be reordered in the transmit path, for instance in the packet
|
||||
scheduler. In that case timestamps will be queued onto the error
|
||||
queue out of order from the original send() calls. This option
|
||||
embeds a counter that is incremented at send() time, to order
|
||||
timestamps within a flow.
|
||||
|
||||
This option is implemented only for transmit timestamps. There, the
|
||||
timestamp is always looped along with a struct sock_extended_err.
|
||||
The option modifies field ee_info to pass an id that is unique
|
||||
among all possibly concurrently outstanding timestamp requests for
|
||||
that socket. In practice, it is a monotonically increasing u32
|
||||
(that wraps).
|
||||
|
||||
In datagram sockets, the counter increments on each send call. In
|
||||
stream sockets, it increments with every byte.
|
||||
|
||||
|
||||
1.4 Bytestream Timestamps
|
||||
|
||||
The SO_TIMESTAMPING interface supports timestamping of bytes in a
|
||||
bytestream. Each request is interpreted as a request for when the
|
||||
entire contents of the buffer has passed a timestamping point. That
|
||||
is, for streams option SOF_TIMESTAMPING_TX_SOFTWARE will record
|
||||
when all bytes have reached the device driver, regardless of how
|
||||
many packets the data has been converted into.
|
||||
|
||||
In general, bytestreams have no natural delimiters and therefore
|
||||
correlating a timestamp with data is non-trivial. A range of bytes
|
||||
may be split across segments, any segments may be merged (possibly
|
||||
coalescing sections of previously segmented buffers associated with
|
||||
independent send() calls). Segments can be reordered and the same
|
||||
byte range can coexist in multiple segments for protocols that
|
||||
implement retransmissions.
|
||||
|
||||
It is essential that all timestamps implement the same semantics,
|
||||
regardless of these possible transformations, as otherwise they are
|
||||
incomparable. Handling "rare" corner cases differently from the
|
||||
simple case (a 1:1 mapping from buffer to skb) is insufficient
|
||||
because performance debugging often needs to focus on such outliers.
|
||||
|
||||
In practice, timestamps can be correlated with segments of a
|
||||
bytestream consistently, if both semantics of the timestamp and the
|
||||
timing of measurement are chosen correctly. This challenge is no
|
||||
different from deciding on a strategy for IP fragmentation. There, the
|
||||
definition is that only the first fragment is timestamped. For
|
||||
bytestreams, we chose that a timestamp is generated only when all
|
||||
bytes have passed a point. SOF_TIMESTAMPING_TX_ACK as defined is easy to
|
||||
implement and reason about. An implementation that has to take into
|
||||
account SACK would be more complex due to possible transmission holes
|
||||
and out of order arrival.
|
||||
|
||||
On the host, TCP can also break the simple 1:1 mapping from buffer to
|
||||
skbuff as a result of Nagle, cork, autocork, segmentation and GSO. The
|
||||
implementation ensures correctness in all cases by tracking the
|
||||
individual last byte passed to send(), even if it is no longer the
|
||||
last byte after an skbuff extend or merge operation. It stores the
|
||||
relevant sequence number in skb_shinfo(skb)->tskey. Because an skbuff
|
||||
has only one such field, only one timestamp can be generated.
|
||||
|
||||
In rare cases, a timestamp request can be missed if two requests are
|
||||
collapsed onto the same skb. A process can detect this situation by
|
||||
enabling SOF_TIMESTAMPING_OPT_ID and comparing the byte offset at
|
||||
send time with the value returned for each timestamp. It can prevent
|
||||
the situation by always flushing the TCP stack in between requests,
|
||||
for instance by enabling TCP_NODELAY and disabling TCP_CORK and
|
||||
autocork.
|
||||
|
||||
These precautions ensure that the timestamp is generated only when all
|
||||
bytes have passed a timestamp point, assuming that the network stack
|
||||
itself does not reorder the segments. The stack indeed tries to avoid
|
||||
reordering. The one exception is under administrator control: it is
|
||||
possible to construct a packet scheduler configuration that delays
|
||||
segments from the same stream differently. Such a setup would be
|
||||
unusual.
|
||||
|
||||
|
||||
2 Data Interfaces
|
||||
|
||||
Timestamps are read using the ancillary data feature of recvmsg().
|
||||
See `man 3 cmsg` for details of this interface. The socket manual
|
||||
page (`man 7 socket`) describes how timestamps generated with
|
||||
SO_TIMESTAMP and SO_TIMESTAMPNS records can be retrieved.
|
||||
|
||||
|
||||
2.1 SCM_TIMESTAMPING records
|
||||
|
||||
These timestamps are returned in a control message with cmsg_level
|
||||
SOL_SOCKET, cmsg_type SCM_TIMESTAMPING, and payload of type
|
||||
|
||||
struct scm_timestamping {
|
||||
struct timespec systime;
|
||||
struct timespec hwtimetrans;
|
||||
struct timespec hwtimeraw;
|
||||
struct timespec ts[3];
|
||||
};
|
||||
|
||||
recvmsg() can be used to get this control message for regular incoming
|
||||
packets. For send time stamps the outgoing packet is looped back to
|
||||
the socket's error queue with the send time stamp(s) attached. It can
|
||||
be received with recvmsg(flags=MSG_ERRQUEUE). The call returns the
|
||||
original outgoing packet data including all headers preprended down to
|
||||
and including the link layer, the scm_timestamping control message and
|
||||
a sock_extended_err control message with ee_errno==ENOMSG and
|
||||
ee_origin==SO_EE_ORIGIN_TIMESTAMPING. A socket with such a pending
|
||||
bounced packet is ready for reading as far as select() is concerned.
|
||||
If the outgoing packet has to be fragmented, then only the first
|
||||
fragment is time stamped and returned to the sending socket.
|
||||
The structure can return up to three timestamps. This is a legacy
|
||||
feature. Only one field is non-zero at any time. Most timestamps
|
||||
are passed in ts[0]. Hardware timestamps are passed in ts[2].
|
||||
|
||||
All three values correspond to the same event in time, but were
|
||||
generated in different ways. Each of these values may be empty (= all
|
||||
zero), in which case no such value was available. If the application
|
||||
is not interested in some of these values, they can be left blank to
|
||||
avoid the potential overhead of calculating them.
|
||||
ts[1] used to hold hardware timestamps converted to system time.
|
||||
Instead, expose the hardware clock device on the NIC directly as
|
||||
a HW PTP clock source, to allow time conversion in userspace and
|
||||
optionally synchronize system time with a userspace PTP stack such
|
||||
as linuxptp. For the PTP clock API, see Documentation/ptp/ptp.txt.
|
||||
|
||||
systime is the value of the system time at that moment. This
|
||||
corresponds to the value also returned via SO_TIMESTAMP[NS]. If the
|
||||
time stamp was generated by hardware, then this field is
|
||||
empty. Otherwise it is filled in if SOF_TIMESTAMPING_SOFTWARE is
|
||||
set.
|
||||
2.1.1 Transmit timestamps with MSG_ERRQUEUE
|
||||
|
||||
hwtimeraw is the original hardware time stamp. Filled in if
|
||||
SOF_TIMESTAMPING_RAW_HARDWARE is set. No assumptions about its
|
||||
relation to system time should be made.
|
||||
For transmit timestamps the outgoing packet is looped back to the
|
||||
socket's error queue with the send timestamp(s) attached. A process
|
||||
receives the timestamps by calling recvmsg() with flag MSG_ERRQUEUE
|
||||
set and with a msg_control buffer sufficiently large to receive the
|
||||
relevant metadata structures. The recvmsg call returns the original
|
||||
outgoing data packet with two ancillary messages attached.
|
||||
|
||||
hwtimetrans is always zero. This field is deprecated. It used to hold
|
||||
hw timestamps converted to system time. Instead, expose the hardware
|
||||
clock device on the NIC directly as a HW PTP clock source, to allow
|
||||
time conversion in userspace and optionally synchronize system time
|
||||
with a userspace PTP stack such as linuxptp. For the PTP clock API,
|
||||
see Documentation/ptp/ptp.txt.
|
||||
A message of cm_level SOL_IP(V6) and cm_type IP(V6)_RECVERR
|
||||
embeds a struct sock_extended_err. This defines the error type. For
|
||||
timestamps, the ee_errno field is ENOMSG. The other ancillary message
|
||||
will have cm_level SOL_SOCKET and cm_type SCM_TIMESTAMPING. This
|
||||
embeds the struct scm_timestamping.
|
||||
|
||||
|
||||
SIOCSHWTSTAMP, SIOCGHWTSTAMP:
|
||||
2.1.1.2 Timestamp types
|
||||
|
||||
The semantics of the three struct timespec are defined by field
|
||||
ee_info in the extended error structure. It contains a value of
|
||||
type SCM_TSTAMP_* to define the actual timestamp passed in
|
||||
scm_timestamping.
|
||||
|
||||
The SCM_TSTAMP_* types are 1:1 matches to the SOF_TIMESTAMPING_*
|
||||
control fields discussed previously, with one exception. For legacy
|
||||
reasons, SCM_TSTAMP_SND is equal to zero and can be set for both
|
||||
SOF_TIMESTAMPING_TX_HARDWARE and SOF_TIMESTAMPING_TX_SOFTWARE. It
|
||||
is the first if ts[2] is non-zero, the second otherwise, in which
|
||||
case the timestamp is stored in ts[0].
|
||||
|
||||
|
||||
2.1.1.3 Fragmentation
|
||||
|
||||
Fragmentation of outgoing datagrams is rare, but is possible, e.g., by
|
||||
explicitly disabling PMTU discovery. If an outgoing packet is fragmented,
|
||||
then only the first fragment is timestamped and returned to the sending
|
||||
socket.
|
||||
|
||||
|
||||
2.1.1.4 Packet Payload
|
||||
|
||||
The calling application is often not interested in receiving the whole
|
||||
packet payload that it passed to the stack originally: the socket
|
||||
error queue mechanism is just a method to piggyback the timestamp on.
|
||||
In this case, the application can choose to read datagrams with a
|
||||
smaller buffer, possibly even of length 0. The payload is truncated
|
||||
accordingly. Until the process calls recvmsg() on the error queue,
|
||||
however, the full packet is queued, taking up budget from SO_RCVBUF.
|
||||
|
||||
|
||||
2.1.1.5 Blocking Read
|
||||
|
||||
Reading from the error queue is always a non-blocking operation. To
|
||||
block waiting on a timestamp, use poll or select. poll() will return
|
||||
POLLERR in pollfd.revents if any data is ready on the error queue.
|
||||
There is no need to pass this flag in pollfd.events. This flag is
|
||||
ignored on request. See also `man 2 poll`.
|
||||
|
||||
|
||||
2.1.2 Receive timestamps
|
||||
|
||||
On reception, there is no reason to read from the socket error queue.
|
||||
The SCM_TIMESTAMPING ancillary data is sent along with the packet data
|
||||
on a normal recvmsg(). Since this is not a socket error, it is not
|
||||
accompanied by a message SOL_IP(V6)/IP(V6)_RECVERROR. In this case,
|
||||
the meaning of the three fields in struct scm_timestamping is
|
||||
implicitly defined. ts[0] holds a software timestamp if set, ts[1]
|
||||
is again deprecated and ts[2] holds a hardware timestamp if set.
|
||||
|
||||
|
||||
3. Hardware Timestamping configuration: SIOCSHWTSTAMP and SIOCGHWTSTAMP
|
||||
|
||||
Hardware time stamping must also be initialized for each device driver
|
||||
that is expected to do hardware time stamping. The parameter is defined in
|
||||
|
@ -167,8 +372,7 @@ enum {
|
|||
*/
|
||||
};
|
||||
|
||||
|
||||
DEVICE IMPLEMENTATION
|
||||
3.1 Hardware Timestamping Implementation: Device Drivers
|
||||
|
||||
A driver which supports hardware time stamping must support the
|
||||
SIOCSHWTSTAMP ioctl and update the supplied struct hwtstamp_config with
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
# To compile, from the source root
|
||||
#
|
||||
# make headers_install
|
||||
# make M=documentation
|
||||
|
||||
# List of programs to build
|
||||
hostprogs-y := hwtstamp_config timestamping
|
||||
hostprogs-y := hwtstamp_config timestamping txtimestamp
|
||||
|
||||
# Tell kbuild to always build the programs
|
||||
always := $(hostprogs-y)
|
||||
|
||||
HOSTCFLAGS_timestamping.o += -I$(objtree)/usr/include
|
||||
HOSTCFLAGS_txtimestamp.o += -I$(objtree)/usr/include
|
||||
HOSTCFLAGS_hwtstamp_config.o += -I$(objtree)/usr/include
|
||||
|
|
469
Documentation/networking/timestamping/txtimestamp.c
Normal file
469
Documentation/networking/timestamping/txtimestamp.c
Normal file
|
@ -0,0 +1,469 @@
|
|||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
* Author: willemb@google.com (Willem de Bruijn)
|
||||
*
|
||||
* Test software tx timestamping, including
|
||||
*
|
||||
* - SCHED, SND and ACK timestamps
|
||||
* - RAW, UDP and TCP
|
||||
* - IPv4 and IPv6
|
||||
* - various packet sizes (to test GSO and TSO)
|
||||
*
|
||||
* Consult the command line arguments for help on running
|
||||
* the various testcases.
|
||||
*
|
||||
* This test requires a dummy TCP server.
|
||||
* A simple `nc6 [-u] -l -p $DESTPORT` will do
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <asm/types.h>
|
||||
#include <error.h>
|
||||
#include <errno.h>
|
||||
#include <linux/errqueue.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <poll.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* command line parameters */
|
||||
static int cfg_proto = SOCK_STREAM;
|
||||
static int cfg_ipproto = IPPROTO_TCP;
|
||||
static int cfg_num_pkts = 4;
|
||||
static int do_ipv4 = 1;
|
||||
static int do_ipv6 = 1;
|
||||
static int cfg_payload_len = 10;
|
||||
static uint16_t dest_port = 9000;
|
||||
|
||||
static struct sockaddr_in daddr;
|
||||
static struct sockaddr_in6 daddr6;
|
||||
static struct timespec ts_prev;
|
||||
|
||||
static void __print_timestamp(const char *name, struct timespec *cur,
|
||||
uint32_t key, int payload_len)
|
||||
{
|
||||
if (!(cur->tv_sec | cur->tv_nsec))
|
||||
return;
|
||||
|
||||
fprintf(stderr, " %s: %lu s %lu us (seq=%u, len=%u)",
|
||||
name, cur->tv_sec, cur->tv_nsec / 1000,
|
||||
key, payload_len);
|
||||
|
||||
if ((ts_prev.tv_sec | ts_prev.tv_nsec)) {
|
||||
int64_t cur_ms, prev_ms;
|
||||
|
||||
cur_ms = (long) cur->tv_sec * 1000 * 1000;
|
||||
cur_ms += cur->tv_nsec / 1000;
|
||||
|
||||
prev_ms = (long) ts_prev.tv_sec * 1000 * 1000;
|
||||
prev_ms += ts_prev.tv_nsec / 1000;
|
||||
|
||||
fprintf(stderr, " (%+ld us)", cur_ms - prev_ms);
|
||||
}
|
||||
|
||||
ts_prev = *cur;
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
static void print_timestamp_usr(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timeval tv; /* avoid dependency on -lrt */
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
ts.tv_sec = tv.tv_sec;
|
||||
ts.tv_nsec = tv.tv_usec * 1000;
|
||||
|
||||
__print_timestamp(" USR", &ts, 0, 0);
|
||||
}
|
||||
|
||||
static void print_timestamp(struct scm_timestamping *tss, int tstype,
|
||||
int tskey, int payload_len)
|
||||
{
|
||||
const char *tsname;
|
||||
|
||||
switch (tstype) {
|
||||
case SCM_TSTAMP_SCHED:
|
||||
tsname = " ENQ";
|
||||
break;
|
||||
case SCM_TSTAMP_SND:
|
||||
tsname = " SND";
|
||||
break;
|
||||
case SCM_TSTAMP_ACK:
|
||||
tsname = " ACK";
|
||||
break;
|
||||
default:
|
||||
error(1, 0, "unknown timestamp type: %u",
|
||||
tstype);
|
||||
}
|
||||
__print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
|
||||
}
|
||||
|
||||
static void __poll(int fd)
|
||||
{
|
||||
struct pollfd pollfd;
|
||||
int ret;
|
||||
|
||||
memset(&pollfd, 0, sizeof(pollfd));
|
||||
pollfd.fd = fd;
|
||||
ret = poll(&pollfd, 1, 100);
|
||||
if (ret != 1)
|
||||
error(1, errno, "poll");
|
||||
}
|
||||
|
||||
static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
|
||||
{
|
||||
struct sock_extended_err *serr = NULL;
|
||||
struct scm_timestamping *tss = NULL;
|
||||
struct cmsghdr *cm;
|
||||
|
||||
for (cm = CMSG_FIRSTHDR(msg);
|
||||
cm && cm->cmsg_len;
|
||||
cm = CMSG_NXTHDR(msg, cm)) {
|
||||
if (cm->cmsg_level == SOL_SOCKET &&
|
||||
cm->cmsg_type == SCM_TIMESTAMPING) {
|
||||
tss = (void *) CMSG_DATA(cm);
|
||||
} else if ((cm->cmsg_level == SOL_IP &&
|
||||
cm->cmsg_type == IP_RECVERR) ||
|
||||
(cm->cmsg_level == SOL_IPV6 &&
|
||||
cm->cmsg_type == IPV6_RECVERR)) {
|
||||
|
||||
serr = (void *) CMSG_DATA(cm);
|
||||
if (serr->ee_errno != ENOMSG ||
|
||||
serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
|
||||
fprintf(stderr, "unknown ip error %d %d\n",
|
||||
serr->ee_errno,
|
||||
serr->ee_origin);
|
||||
serr = NULL;
|
||||
}
|
||||
} else
|
||||
fprintf(stderr, "unknown cmsg %d,%d\n",
|
||||
cm->cmsg_level, cm->cmsg_type);
|
||||
}
|
||||
|
||||
if (serr && tss)
|
||||
print_timestamp(tss, serr->ee_info, serr->ee_data, payload_len);
|
||||
}
|
||||
|
||||
static int recv_errmsg(int fd)
|
||||
{
|
||||
static char ctrl[1024 /* overprovision*/];
|
||||
static struct msghdr msg;
|
||||
struct iovec entry;
|
||||
static char *data;
|
||||
int ret = 0;
|
||||
|
||||
data = malloc(cfg_payload_len);
|
||||
if (!data)
|
||||
error(1, 0, "malloc");
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
memset(ctrl, 0, sizeof(ctrl));
|
||||
|
||||
entry.iov_base = data;
|
||||
entry.iov_len = cfg_payload_len;
|
||||
msg.msg_iov = &entry;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_control = ctrl;
|
||||
msg.msg_controllen = sizeof(ctrl);
|
||||
|
||||
ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
|
||||
if (ret == -1 && errno != EAGAIN)
|
||||
error(1, errno, "recvmsg");
|
||||
|
||||
__recv_errmsg_cmsg(&msg, ret);
|
||||
|
||||
free(data);
|
||||
return ret == -1;
|
||||
}
|
||||
|
||||
static void do_test(int family, unsigned int opt)
|
||||
{
|
||||
char *buf;
|
||||
int fd, i, val, total_len;
|
||||
|
||||
if (family == IPPROTO_IPV6 && cfg_proto != SOCK_STREAM) {
|
||||
/* due to lack of checksum generation code */
|
||||
fprintf(stderr, "test: skipping datagram over IPv6\n");
|
||||
return;
|
||||
}
|
||||
|
||||
total_len = cfg_payload_len;
|
||||
if (cfg_proto == SOCK_RAW) {
|
||||
total_len += sizeof(struct udphdr);
|
||||
if (cfg_ipproto == IPPROTO_RAW)
|
||||
total_len += sizeof(struct iphdr);
|
||||
}
|
||||
|
||||
buf = malloc(total_len);
|
||||
if (!buf)
|
||||
error(1, 0, "malloc");
|
||||
|
||||
fd = socket(family, cfg_proto, cfg_ipproto);
|
||||
if (fd < 0)
|
||||
error(1, errno, "socket");
|
||||
|
||||
if (cfg_proto == SOCK_STREAM) {
|
||||
val = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
|
||||
(char*) &val, sizeof(val)))
|
||||
error(1, 0, "setsockopt no nagle");
|
||||
|
||||
if (family == PF_INET) {
|
||||
if (connect(fd, (void *) &daddr, sizeof(daddr)))
|
||||
error(1, errno, "connect ipv4");
|
||||
} else {
|
||||
if (connect(fd, (void *) &daddr6, sizeof(daddr6)))
|
||||
error(1, errno, "connect ipv6");
|
||||
}
|
||||
}
|
||||
|
||||
opt |= SOF_TIMESTAMPING_SOFTWARE |
|
||||
SOF_TIMESTAMPING_OPT_ID;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
|
||||
(char *) &opt, sizeof(opt)))
|
||||
error(1, 0, "setsockopt timestamping");
|
||||
|
||||
for (i = 0; i < cfg_num_pkts; i++) {
|
||||
memset(&ts_prev, 0, sizeof(ts_prev));
|
||||
memset(buf, 'a' + i, total_len);
|
||||
buf[total_len - 2] = '\n';
|
||||
buf[total_len - 1] = '\0';
|
||||
|
||||
if (cfg_proto == SOCK_RAW) {
|
||||
struct udphdr *udph;
|
||||
int off = 0;
|
||||
|
||||
if (cfg_ipproto == IPPROTO_RAW) {
|
||||
struct iphdr *iph = (void *) buf;
|
||||
|
||||
memset(iph, 0, sizeof(*iph));
|
||||
iph->ihl = 5;
|
||||
iph->version = 4;
|
||||
iph->ttl = 2;
|
||||
iph->daddr = daddr.sin_addr.s_addr;
|
||||
iph->protocol = IPPROTO_UDP;
|
||||
/* kernel writes saddr, csum, len */
|
||||
|
||||
off = sizeof(*iph);
|
||||
}
|
||||
|
||||
udph = (void *) buf + off;
|
||||
udph->source = ntohs(9000); /* random spoof */
|
||||
udph->dest = ntohs(dest_port);
|
||||
udph->len = ntohs(sizeof(*udph) + cfg_payload_len);
|
||||
udph->check = 0; /* not allowed for IPv6 */
|
||||
}
|
||||
|
||||
print_timestamp_usr();
|
||||
if (cfg_proto != SOCK_STREAM) {
|
||||
if (family == PF_INET)
|
||||
val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr));
|
||||
else
|
||||
val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6));
|
||||
} else {
|
||||
val = send(fd, buf, cfg_payload_len, 0);
|
||||
}
|
||||
if (val != total_len)
|
||||
error(1, errno, "send");
|
||||
|
||||
/* wait for all errors to be queued, else ACKs arrive OOO */
|
||||
usleep(50 * 1000);
|
||||
|
||||
__poll(fd);
|
||||
|
||||
while (!recv_errmsg(fd)) {}
|
||||
}
|
||||
|
||||
if (close(fd))
|
||||
error(1, errno, "close");
|
||||
|
||||
free(buf);
|
||||
usleep(400 * 1000);
|
||||
}
|
||||
|
||||
static void __attribute__((noreturn)) usage(const char *filepath)
|
||||
{
|
||||
fprintf(stderr, "\nUsage: %s [options] hostname\n"
|
||||
"\nwhere options are:\n"
|
||||
" -4: only IPv4\n"
|
||||
" -6: only IPv6\n"
|
||||
" -h: show this message\n"
|
||||
" -l N: send N bytes at a time\n"
|
||||
" -r: use raw\n"
|
||||
" -R: use raw (IP_HDRINCL)\n"
|
||||
" -p N: connect to port N\n"
|
||||
" -u: use udp\n",
|
||||
filepath);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void parse_opt(int argc, char **argv)
|
||||
{
|
||||
int proto_count = 0;
|
||||
char c;
|
||||
|
||||
while ((c = getopt(argc, argv, "46hl:p:rRu")) != -1) {
|
||||
switch (c) {
|
||||
case '4':
|
||||
do_ipv6 = 0;
|
||||
break;
|
||||
case '6':
|
||||
do_ipv4 = 0;
|
||||
break;
|
||||
case 'r':
|
||||
proto_count++;
|
||||
cfg_proto = SOCK_RAW;
|
||||
cfg_ipproto = IPPROTO_UDP;
|
||||
break;
|
||||
case 'R':
|
||||
proto_count++;
|
||||
cfg_proto = SOCK_RAW;
|
||||
cfg_ipproto = IPPROTO_RAW;
|
||||
break;
|
||||
case 'u':
|
||||
proto_count++;
|
||||
cfg_proto = SOCK_DGRAM;
|
||||
cfg_ipproto = IPPROTO_UDP;
|
||||
break;
|
||||
case 'l':
|
||||
cfg_payload_len = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'p':
|
||||
dest_port = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cfg_payload_len)
|
||||
error(1, 0, "payload may not be nonzero");
|
||||
if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472)
|
||||
error(1, 0, "udp packet might exceed expected MTU");
|
||||
if (!do_ipv4 && !do_ipv6)
|
||||
error(1, 0, "pass -4 or -6, not both");
|
||||
if (proto_count > 1)
|
||||
error(1, 0, "pass -r, -R or -u, not multiple");
|
||||
|
||||
if (optind != argc - 1)
|
||||
error(1, 0, "missing required hostname argument");
|
||||
}
|
||||
|
||||
static void resolve_hostname(const char *hostname)
|
||||
{
|
||||
struct addrinfo *addrs, *cur;
|
||||
int have_ipv4 = 0, have_ipv6 = 0;
|
||||
|
||||
if (getaddrinfo(hostname, NULL, NULL, &addrs))
|
||||
error(1, errno, "getaddrinfo");
|
||||
|
||||
cur = addrs;
|
||||
while (cur && !have_ipv4 && !have_ipv6) {
|
||||
if (!have_ipv4 && cur->ai_family == AF_INET) {
|
||||
memcpy(&daddr, cur->ai_addr, sizeof(daddr));
|
||||
daddr.sin_port = htons(dest_port);
|
||||
have_ipv4 = 1;
|
||||
}
|
||||
else if (!have_ipv6 && cur->ai_family == AF_INET6) {
|
||||
memcpy(&daddr6, cur->ai_addr, sizeof(daddr6));
|
||||
daddr6.sin6_port = htons(dest_port);
|
||||
have_ipv6 = 1;
|
||||
}
|
||||
cur = cur->ai_next;
|
||||
}
|
||||
if (addrs)
|
||||
freeaddrinfo(addrs);
|
||||
|
||||
do_ipv4 &= have_ipv4;
|
||||
do_ipv6 &= have_ipv6;
|
||||
}
|
||||
|
||||
static void do_main(int family)
|
||||
{
|
||||
fprintf(stderr, "family: %s\n",
|
||||
family == PF_INET ? "INET" : "INET6");
|
||||
|
||||
fprintf(stderr, "test SND\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);
|
||||
|
||||
fprintf(stderr, "test ENQ\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SCHED);
|
||||
|
||||
fprintf(stderr, "test ENQ + SND\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SCHED |
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE);
|
||||
|
||||
if (cfg_proto == SOCK_STREAM) {
|
||||
fprintf(stderr, "\ntest ACK\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_ACK);
|
||||
|
||||
fprintf(stderr, "\ntest SND + ACK\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_TX_ACK);
|
||||
|
||||
fprintf(stderr, "\ntest ENQ + SND + ACK\n");
|
||||
do_test(family, SOF_TIMESTAMPING_TX_SCHED |
|
||||
SOF_TIMESTAMPING_TX_SOFTWARE |
|
||||
SOF_TIMESTAMPING_TX_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" };
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc == 1)
|
||||
usage(argv[0]);
|
||||
|
||||
parse_opt(argc, argv);
|
||||
resolve_hostname(argv[argc - 1]);
|
||||
|
||||
fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]);
|
||||
fprintf(stderr, "payload: %u\n", cfg_payload_len);
|
||||
fprintf(stderr, "server port: %u\n", dest_port);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
if (do_ipv4)
|
||||
do_main(PF_INET);
|
||||
if (do_ipv6)
|
||||
do_main(PF_INET6);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -241,6 +241,9 @@ address of the router (or Connected) for internal networks.
|
|||
6. TIPC
|
||||
-------------------------------------------------------
|
||||
|
||||
tipc_rmem
|
||||
----------
|
||||
|
||||
The TIPC protocol now has a tunable for the receive memory, similar to the
|
||||
tcp_rmem - i.e. a vector of 3 INTEGERs: (min, default, max)
|
||||
|
||||
|
@ -252,3 +255,16 @@ The max value is set to CONN_OVERLOAD_LIMIT, and the default and min values
|
|||
are scaled (shifted) versions of that same value. Note that the min value
|
||||
is not at this point in time used in any meaningful way, but the triplet is
|
||||
preserved in order to be consistent with things like tcp_rmem.
|
||||
|
||||
named_timeout
|
||||
--------------
|
||||
|
||||
TIPC name table updates are distributed asynchronously in a cluster, without
|
||||
any form of transaction handling. This means that different race scenarios are
|
||||
possible. One such is that a name withdrawal sent out by one node and received
|
||||
by another node may arrive after a second, overlapping name publication already
|
||||
has been accepted from a third node, although the conflicting updates
|
||||
originally may have been issued in the correct sequential order.
|
||||
If named_timeout is nonzero, failed topology updates will be placed on a defer
|
||||
queue until another event arrives that clears the error, or until the timeout
|
||||
expires. Value is in milliseconds.
|
||||
|
|
38
MAINTAINERS
38
MAINTAINERS
|
@ -152,8 +152,9 @@ F: drivers/scsi/53c700*
|
|||
|
||||
6LOWPAN GENERIC (BTLE/IEEE 802.15.4)
|
||||
M: Alexander Aring <alex.aring@gmail.com>
|
||||
L: linux-zigbee-devel@lists.sourceforge.net (moderated for non-subscribers)
|
||||
M: Jukka Rissanen <jukka.rissanen@linux.intel.com>
|
||||
L: linux-bluetooth@vger.kernel.org
|
||||
L: linux-wpan@vger.kernel.org
|
||||
S: Maintained
|
||||
F: net/6lowpan/
|
||||
F: include/net/6lowpan.h
|
||||
|
@ -1649,6 +1650,7 @@ L: wil6210@qca.qualcomm.com
|
|||
S: Supported
|
||||
W: http://wireless.kernel.org/en/users/Drivers/wil6210
|
||||
F: drivers/net/wireless/ath/wil6210/
|
||||
F: include/uapi/linux/wil6210_uapi.h
|
||||
|
||||
CARL9170 LINUX COMMUNITY WIRELESS DRIVER
|
||||
M: Christian Lamparter <chunkeey@googlemail.com>
|
||||
|
@ -3605,6 +3607,11 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kristoffer/linux-hpc.git
|
|||
F: drivers/video/fbdev/s1d13xxxfb.c
|
||||
F: include/video/s1d13xxxfb.h
|
||||
|
||||
ET131X NETWORK DRIVER
|
||||
M: Mark Einon <mark.einon@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/net/ethernet/agere/
|
||||
|
||||
ETHERNET BRIDGE
|
||||
M: Stephen Hemminger <stephen@networkplumber.org>
|
||||
L: bridge@lists.linux-foundation.org
|
||||
|
@ -4650,13 +4657,14 @@ F: drivers/idle/i7300_idle.c
|
|||
|
||||
IEEE 802.15.4 SUBSYSTEM
|
||||
M: Alexander Aring <alex.aring@gmail.com>
|
||||
L: linux-zigbee-devel@lists.sourceforge.net (moderated for non-subscribers)
|
||||
W: http://apps.sourceforge.net/trac/linux-zigbee
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/lowpan/lowpan.git
|
||||
L: linux-wpan@vger.kernel.org
|
||||
W: https://github.com/linux-wpan
|
||||
T: git git://github.com/linux-wpan/linux-wpan-next.git
|
||||
S: Maintained
|
||||
F: net/ieee802154/
|
||||
F: net/mac802154/
|
||||
F: drivers/net/ieee802154/
|
||||
F: Documentation/networking/ieee802154.txt
|
||||
|
||||
IGUANAWORKS USB IR TRANSCEIVER
|
||||
M: Sean Young <sean@mess.org>
|
||||
|
@ -4841,14 +4849,14 @@ M: Deepak Saxena <dsaxena@plexity.net>
|
|||
S: Maintained
|
||||
F: drivers/char/hw_random/ixp4xx-rng.c
|
||||
|
||||
INTEL ETHERNET DRIVERS (e100/e1000/e1000e/igb/igbvf/ixgb/ixgbe/ixgbevf/i40e/i40evf)
|
||||
INTEL ETHERNET DRIVERS (e100/e1000/e1000e/fm10k/igb/igbvf/ixgb/ixgbe/ixgbevf/i40e/i40evf)
|
||||
M: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
|
||||
M: Jesse Brandeburg <jesse.brandeburg@intel.com>
|
||||
M: Bruce Allan <bruce.w.allan@intel.com>
|
||||
M: Carolyn Wyborny <carolyn.wyborny@intel.com>
|
||||
M: Don Skidmore <donald.c.skidmore@intel.com>
|
||||
M: Greg Rose <gregory.v.rose@intel.com>
|
||||
M: Alex Duyck <alexander.h.duyck@intel.com>
|
||||
M: Matthew Vick <matthew.vick@intel.com>
|
||||
M: John Ronciak <john.ronciak@intel.com>
|
||||
M: Mitch Williams <mitch.a.williams@intel.com>
|
||||
M: Linux NICS <linux.nics@intel.com>
|
||||
|
@ -6429,7 +6437,7 @@ M: Lauro Ramos Venancio <lauro.venancio@openbossa.org>
|
|||
M: Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
|
||||
M: Samuel Ortiz <sameo@linux.intel.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
L: linux-nfc@lists.01.org (moderated for non-subscribers)
|
||||
L: linux-nfc@lists.01.org (subscribers-only)
|
||||
S: Supported
|
||||
F: net/nfc/
|
||||
F: include/net/nfc/
|
||||
|
@ -7435,15 +7443,15 @@ F: drivers/net/ethernet/qlogic/qla3xxx.*
|
|||
|
||||
QLOGIC QLCNIC (1/10)Gb ETHERNET DRIVER
|
||||
M: Shahed Shaikh <shahed.shaikh@qlogic.com>
|
||||
M: Dept-HSGLinuxNICDev@qlogic.com
|
||||
M: Dept-GELinuxNICDev@qlogic.com
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/net/ethernet/qlogic/qlcnic/
|
||||
|
||||
QLOGIC QLGE 10Gb ETHERNET DRIVER
|
||||
M: Shahed Shaikh <shahed.shaikh@qlogic.com>
|
||||
M: Jitendra Kalsaria <jitendra.kalsaria@qlogic.com>
|
||||
M: Ron Mercer <ron.mercer@qlogic.com>
|
||||
M: Harish Patil <harish.patil@qlogic.com>
|
||||
M: Sudarsana Kalluru <sudarsana.kalluru@qlogic.com>
|
||||
M: Dept-GELinuxNICDev@qlogic.com
|
||||
M: linux-driver@qlogic.com
|
||||
L: netdev@vger.kernel.org
|
||||
S: Supported
|
||||
|
@ -7546,13 +7554,12 @@ F: drivers/video/fbdev/aty/aty128fb.c
|
|||
|
||||
RALINK RT2X00 WIRELESS LAN DRIVER
|
||||
P: rt2x00 project
|
||||
M: Ivo van Doorn <IvDoorn@gmail.com>
|
||||
M: Stanislaw Gruszka <sgruszka@redhat.com>
|
||||
M: Helmut Schaa <helmut.schaa@googlemail.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
L: users@rt2x00.serialmonkey.com (moderated for non-subscribers)
|
||||
W: http://rt2x00.serialmonkey.com/
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/ivd/rt2x00.git
|
||||
F: drivers/net/wireless/rt2x00/
|
||||
|
||||
RAMDISK RAM BLOCK DEVICE DRIVER
|
||||
|
@ -8752,11 +8759,6 @@ M: H Hartley Sweeten <hsweeten@visionengravers.com>
|
|||
S: Odd Fixes
|
||||
F: drivers/staging/comedi/
|
||||
|
||||
STAGING - ET131X NETWORK DRIVER
|
||||
M: Mark Einon <mark.einon@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/et131x/
|
||||
|
||||
STAGING - FLARION FT1000 DRIVERS
|
||||
M: Marek Belisko <marek.belisko@gmail.com>
|
||||
S: Odd Fixes
|
||||
|
|
|
@ -132,6 +132,11 @@
|
|||
};
|
||||
};
|
||||
|
||||
cm: syscon@44e10000 {
|
||||
compatible = "ti,am33xx-controlmodule", "syscon";
|
||||
reg = <0x44e10000 0x800>;
|
||||
};
|
||||
|
||||
intc: interrupt-controller@48200000 {
|
||||
compatible = "ti,am33xx-intc";
|
||||
interrupt-controller;
|
||||
|
@ -699,6 +704,7 @@
|
|||
*/
|
||||
interrupts = <40 41 42 43>;
|
||||
ranges;
|
||||
syscon = <&cm>;
|
||||
status = "disabled";
|
||||
|
||||
davinci_mdio: mdio@4a101000 {
|
||||
|
|
|
@ -45,3 +45,7 @@
|
|||
&uart0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
ð0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -114,6 +114,23 @@
|
|||
#interrupt-cells = <3>;
|
||||
};
|
||||
|
||||
eth0: ethernet@b90000 {
|
||||
compatible = "marvell,pxa168-eth";
|
||||
reg = <0xb90000 0x10000>;
|
||||
clocks = <&chip CLKID_GETH0>;
|
||||
interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
|
||||
/* set by bootloader */
|
||||
local-mac-address = [00 00 00 00 00 00];
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phy-handle = <ðphy0>;
|
||||
status = "disabled";
|
||||
|
||||
ethphy0: ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
cpu-ctrl@dd0000 {
|
||||
compatible = "marvell,berlin-cpu-ctrl";
|
||||
reg = <0xdd0000 0x10000>;
|
||||
|
|
|
@ -779,6 +779,8 @@
|
|||
<&clks IMX6SX_CLK_ENET_PTP>;
|
||||
clock-names = "ipg", "ahb", "ptp",
|
||||
"enet_clk_ref", "enet_out";
|
||||
fsl,num-tx-queues=<3>;
|
||||
fsl,num-rx-queues=<3>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
|
|
@ -102,6 +102,22 @@
|
|||
};
|
||||
};
|
||||
|
||||
&emac {
|
||||
status = "okay";
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&phy_int>;
|
||||
|
||||
phy = <&phy0>;
|
||||
phy-supply = <&vcc_rmii>;
|
||||
|
||||
phy0: ethernet-phy@0 {
|
||||
reg = <0>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <26 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
||||
};
|
||||
|
||||
&i2c1 {
|
||||
status = "okay";
|
||||
clock-frequency = <400000>;
|
||||
|
@ -240,6 +256,12 @@
|
|||
};
|
||||
};
|
||||
|
||||
lan8720a {
|
||||
phy_int: phy-int {
|
||||
rockchip,pins = <RK_GPIO3 26 RK_FUNC_GPIO &pcfg_pull_up>;
|
||||
};
|
||||
};
|
||||
|
||||
ir-receiver {
|
||||
ir_recv_pin: ir-recv-pin {
|
||||
rockchip,pins = <RK_GPIO0 10 RK_FUNC_GPIO &pcfg_pull_none>;
|
||||
|
|
|
@ -168,6 +168,24 @@
|
|||
*/
|
||||
};
|
||||
|
||||
emac {
|
||||
emac_xfer: emac-xfer {
|
||||
rockchip,pins = <RK_GPIO3 16 RK_FUNC_2 &pcfg_pull_none>, /* tx_en */
|
||||
<RK_GPIO3 17 RK_FUNC_2 &pcfg_pull_none>, /* txd1 */
|
||||
<RK_GPIO3 18 RK_FUNC_2 &pcfg_pull_none>, /* txd0 */
|
||||
<RK_GPIO3 19 RK_FUNC_2 &pcfg_pull_none>, /* rxd0 */
|
||||
<RK_GPIO3 20 RK_FUNC_2 &pcfg_pull_none>, /* rxd1 */
|
||||
<RK_GPIO3 21 RK_FUNC_2 &pcfg_pull_none>, /* mac_clk */
|
||||
<RK_GPIO3 22 RK_FUNC_2 &pcfg_pull_none>, /* rx_err */
|
||||
<RK_GPIO3 23 RK_FUNC_2 &pcfg_pull_none>; /* crs_dvalid */
|
||||
};
|
||||
|
||||
emac_mdio: emac-mdio {
|
||||
rockchip,pins = <RK_GPIO3 24 RK_FUNC_2 &pcfg_pull_none>,
|
||||
<RK_GPIO3 25 RK_FUNC_2 &pcfg_pull_none>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c0 {
|
||||
i2c0_xfer: i2c0-xfer {
|
||||
rockchip,pins = <RK_GPIO1 24 RK_FUNC_1 &pcfg_pull_none>,
|
||||
|
@ -380,6 +398,10 @@
|
|||
};
|
||||
};
|
||||
|
||||
&emac {
|
||||
compatible = "rockchip,rk3188-emac";
|
||||
};
|
||||
|
||||
&global_timer {
|
||||
interrupts = <GIC_PPI 11 0xf04>;
|
||||
};
|
||||
|
|
|
@ -152,6 +152,23 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
emac: ethernet@10204000 {
|
||||
compatible = "snps,arc-emac";
|
||||
reg = <0x10204000 0x3c>;
|
||||
interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rockchip,grf = <&grf>;
|
||||
|
||||
clocks = <&cru HCLK_EMAC>, <&cru SCLK_MAC>;
|
||||
clock-names = "hclk", "macref";
|
||||
max-speed = <100>;
|
||||
phy-mode = "rmii";
|
||||
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
mmc0: dwmmc@10214000 {
|
||||
compatible = "rockchip,rk2928-dw-mshc";
|
||||
reg = <0x10214000 0x1000>;
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
#include <linux/compiler.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/hwcap.h>
|
||||
#include <asm/opcodes.h>
|
||||
|
@ -174,6 +174,14 @@ static inline bool is_load_to_a(u16 inst)
|
|||
}
|
||||
}
|
||||
|
||||
static void jit_fill_hole(void *area, unsigned int size)
|
||||
{
|
||||
u32 *ptr;
|
||||
/* We are guaranteed to have aligned memory. */
|
||||
for (ptr = area; size >= sizeof(u32); size -= sizeof(u32))
|
||||
*ptr++ = __opcode_to_mem_arm(ARM_INST_UDF);
|
||||
}
|
||||
|
||||
static void build_prologue(struct jit_ctx *ctx)
|
||||
{
|
||||
u16 reg_set = saved_regs(ctx);
|
||||
|
@ -859,9 +867,11 @@ b_epilogue:
|
|||
|
||||
void bpf_jit_compile(struct bpf_prog *fp)
|
||||
{
|
||||
struct bpf_binary_header *header;
|
||||
struct jit_ctx ctx;
|
||||
unsigned tmp_idx;
|
||||
unsigned alloc_size;
|
||||
u8 *target_ptr;
|
||||
|
||||
if (!bpf_jit_enable)
|
||||
return;
|
||||
|
@ -897,13 +907,15 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||
/* there's nothing after the epilogue on ARMv7 */
|
||||
build_epilogue(&ctx);
|
||||
#endif
|
||||
|
||||
alloc_size = 4 * ctx.idx;
|
||||
ctx.target = module_alloc(alloc_size);
|
||||
if (unlikely(ctx.target == NULL))
|
||||
header = bpf_jit_binary_alloc(alloc_size, &target_ptr,
|
||||
4, jit_fill_hole);
|
||||
if (header == NULL)
|
||||
goto out;
|
||||
|
||||
ctx.target = (u32 *) target_ptr;
|
||||
ctx.idx = 0;
|
||||
|
||||
build_prologue(&ctx);
|
||||
build_body(&ctx);
|
||||
build_epilogue(&ctx);
|
||||
|
@ -919,8 +931,9 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||
/* there are 2 passes here */
|
||||
bpf_jit_dump(fp->len, alloc_size, 2, ctx.target);
|
||||
|
||||
set_memory_ro((unsigned long)header, header->pages);
|
||||
fp->bpf_func = (void *)ctx.target;
|
||||
fp->jited = 1;
|
||||
fp->jited = true;
|
||||
out:
|
||||
kfree(ctx.offsets);
|
||||
return;
|
||||
|
@ -928,7 +941,15 @@ out:
|
|||
|
||||
void bpf_jit_free(struct bpf_prog *fp)
|
||||
{
|
||||
if (fp->jited)
|
||||
module_free(NULL, fp->bpf_func);
|
||||
kfree(fp);
|
||||
unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
|
||||
struct bpf_binary_header *header = (void *)addr;
|
||||
|
||||
if (!fp->jited)
|
||||
goto free_filter;
|
||||
|
||||
set_memory_rw(addr, header->pages);
|
||||
bpf_jit_binary_free(header);
|
||||
|
||||
free_filter:
|
||||
bpf_prog_unlock_free(fp);
|
||||
}
|
||||
|
|
|
@ -114,6 +114,20 @@
|
|||
|
||||
#define ARM_INST_UMULL 0x00800090
|
||||
|
||||
/*
|
||||
* Use a suitable undefined instruction to use for ARM/Thumb2 faulting.
|
||||
* We need to be careful not to conflict with those used by other modules
|
||||
* (BUG, kprobes, etc) and the register_undef_hook() system.
|
||||
*
|
||||
* The ARM architecture reference manual guarantees that the following
|
||||
* instruction space will produce an undefined instruction exception on
|
||||
* all CPUs:
|
||||
*
|
||||
* ARM: xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx ARMv7-AR, section A5.4
|
||||
* Thumb: 1101 1110 xxxx xxxx ARMv7-M, section A5.2.6
|
||||
*/
|
||||
#define ARM_INST_UDF 0xe7fddef1
|
||||
|
||||
/* register */
|
||||
#define _AL3_R(op, rd, rn, rm) ((op ## _R) | (rd) << 12 | (rn) << 16 | (rm))
|
||||
/* immediate */
|
||||
|
|
|
@ -499,7 +499,7 @@ void __init orion_ge00_switch_init(struct dsa_platform_data *d, int irq)
|
|||
|
||||
d->netdev = &orion_ge00.dev;
|
||||
for (i = 0; i < d->nr_chips; i++)
|
||||
d->chip[i].mii_bus = &orion_ge00_shared.dev;
|
||||
d->chip[i].host_dev = &orion_ge00_shared.dev;
|
||||
orion_switch_device.dev.platform_data = d;
|
||||
|
||||
platform_device_register(&orion_switch_device);
|
||||
|
|
|
@ -210,6 +210,10 @@ static void __init bcm47xx_register_bcma(void)
|
|||
pr_warn("bcm47xx: someone else already registered a bcma SPROM callback handler (err %d)\n", err);
|
||||
|
||||
err = bcma_host_soc_register(&bcm47xx_bus.bcma);
|
||||
if (err)
|
||||
panic("Failed to register BCMA bus (err %d)", err);
|
||||
|
||||
err = bcma_host_soc_init(&bcm47xx_bus.bcma);
|
||||
if (err)
|
||||
panic("Failed to initialize BCMA bus (err %d)", err);
|
||||
|
||||
|
|
|
@ -765,27 +765,6 @@ static u64 jit_get_skb_w(struct sk_buff *skb, unsigned offset)
|
|||
return (u64)err << 32 | ntohl(ret);
|
||||
}
|
||||
|
||||
#ifdef __BIG_ENDIAN_BITFIELD
|
||||
#define PKT_TYPE_MAX (7 << 5)
|
||||
#else
|
||||
#define PKT_TYPE_MAX 7
|
||||
#endif
|
||||
static int pkt_type_offset(void)
|
||||
{
|
||||
struct sk_buff skb_probe = {
|
||||
.pkt_type = ~0,
|
||||
};
|
||||
u8 *ct = (u8 *)&skb_probe;
|
||||
unsigned int off;
|
||||
|
||||
for (off = 0; off < sizeof(struct sk_buff); off++) {
|
||||
if (ct[off] == PKT_TYPE_MAX)
|
||||
return off;
|
||||
}
|
||||
pr_err_once("Please fix pkt_type_offset(), as pkt_type couldn't be found\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int build_body(struct jit_ctx *ctx)
|
||||
{
|
||||
void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w};
|
||||
|
@ -793,7 +772,6 @@ static int build_body(struct jit_ctx *ctx)
|
|||
const struct sock_filter *inst;
|
||||
unsigned int i, off, load_order, condt;
|
||||
u32 k, b_off __maybe_unused;
|
||||
int tmp;
|
||||
|
||||
for (i = 0; i < prog->len; i++) {
|
||||
u16 code;
|
||||
|
@ -1333,11 +1311,7 @@ jmp_cmp:
|
|||
case BPF_ANC | SKF_AD_PKTTYPE:
|
||||
ctx->flags |= SEEN_SKB;
|
||||
|
||||
tmp = off = pkt_type_offset();
|
||||
|
||||
if (tmp < 0)
|
||||
return -1;
|
||||
emit_load_byte(r_tmp, r_skb, off, ctx);
|
||||
emit_load_byte(r_tmp, r_skb, PKT_TYPE_OFFSET(), ctx);
|
||||
/* Keep only the last 3 bits */
|
||||
emit_andi(r_A, r_tmp, PKT_TYPE_MAX, ctx);
|
||||
#ifdef __BIG_ENDIAN_BITFIELD
|
||||
|
@ -1418,7 +1392,7 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||
bpf_jit_dump(fp->len, alloc_size, 2, ctx.target);
|
||||
|
||||
fp->bpf_func = (void *)ctx.target;
|
||||
fp->jited = 1;
|
||||
fp->jited = true;
|
||||
|
||||
out:
|
||||
kfree(ctx.offsets);
|
||||
|
@ -1428,5 +1402,6 @@ void bpf_jit_free(struct bpf_prog *fp)
|
|||
{
|
||||
if (fp->jited)
|
||||
module_free(NULL, fp->bpf_func);
|
||||
kfree(fp);
|
||||
|
||||
bpf_prog_unlock_free(fp);
|
||||
}
|
||||
|
|
|
@ -686,7 +686,7 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||
((u64 *)image)[0] = (u64)code_base;
|
||||
((u64 *)image)[1] = local_paca->kernel_toc;
|
||||
fp->bpf_func = (void *)image;
|
||||
fp->jited = 1;
|
||||
fp->jited = true;
|
||||
}
|
||||
out:
|
||||
kfree(addrs);
|
||||
|
@ -697,5 +697,6 @@ void bpf_jit_free(struct bpf_prog *fp)
|
|||
{
|
||||
if (fp->jited)
|
||||
module_free(NULL, fp->bpf_func);
|
||||
kfree(fp);
|
||||
|
||||
bpf_prog_unlock_free(fp);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,9 @@
|
|||
*
|
||||
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
|
||||
*/
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/facility.h>
|
||||
|
@ -148,6 +146,12 @@ struct bpf_jit {
|
|||
ret; \
|
||||
})
|
||||
|
||||
static void bpf_jit_fill_hole(void *area, unsigned int size)
|
||||
{
|
||||
/* Fill whole space with illegal instructions */
|
||||
memset(area, 0, size);
|
||||
}
|
||||
|
||||
static void bpf_jit_prologue(struct bpf_jit *jit)
|
||||
{
|
||||
/* Save registers and create stack frame if necessary */
|
||||
|
@ -223,37 +227,6 @@ static void bpf_jit_epilogue(struct bpf_jit *jit)
|
|||
EMIT2(0x07fe);
|
||||
}
|
||||
|
||||
/* Helper to find the offset of pkt_type in sk_buff
|
||||
* Make sure its still a 3bit field starting at the MSBs within a byte.
|
||||
*/
|
||||
#define PKT_TYPE_MAX 0xe0
|
||||
static int pkt_type_offset;
|
||||
|
||||
static int __init bpf_pkt_type_offset_init(void)
|
||||
{
|
||||
struct sk_buff skb_probe = {
|
||||
.pkt_type = ~0,
|
||||
};
|
||||
char *ct = (char *)&skb_probe;
|
||||
int off;
|
||||
|
||||
pkt_type_offset = -1;
|
||||
for (off = 0; off < sizeof(struct sk_buff); off++) {
|
||||
if (!ct[off])
|
||||
continue;
|
||||
if (ct[off] == PKT_TYPE_MAX)
|
||||
pkt_type_offset = off;
|
||||
else {
|
||||
/* Found non matching bit pattern, fix needed. */
|
||||
WARN_ON_ONCE(1);
|
||||
pkt_type_offset = -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
device_initcall(bpf_pkt_type_offset_init);
|
||||
|
||||
/*
|
||||
* make sure we dont leak kernel information to user
|
||||
*/
|
||||
|
@ -753,12 +726,10 @@ call_fn: /* lg %r1,<d(function)>(%r13) */
|
|||
}
|
||||
break;
|
||||
case BPF_ANC | SKF_AD_PKTTYPE:
|
||||
if (pkt_type_offset < 0)
|
||||
goto out;
|
||||
/* lhi %r5,0 */
|
||||
EMIT4(0xa7580000);
|
||||
/* ic %r5,<d(pkt_type_offset)>(%r2) */
|
||||
EMIT4_DISP(0x43502000, pkt_type_offset);
|
||||
EMIT4_DISP(0x43502000, PKT_TYPE_OFFSET());
|
||||
/* srl %r5,5 */
|
||||
EMIT4_DISP(0x88500000, 5);
|
||||
break;
|
||||
|
@ -780,38 +751,6 @@ out:
|
|||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: for security reasons, bpf code will follow a randomly
|
||||
* sized amount of illegal instructions.
|
||||
*/
|
||||
struct bpf_binary_header {
|
||||
unsigned int pages;
|
||||
u8 image[];
|
||||
};
|
||||
|
||||
static struct bpf_binary_header *bpf_alloc_binary(unsigned int bpfsize,
|
||||
u8 **image_ptr)
|
||||
{
|
||||
struct bpf_binary_header *header;
|
||||
unsigned int sz, hole;
|
||||
|
||||
/* Most BPF filters are really small, but if some of them fill a page,
|
||||
* allow at least 128 extra bytes for illegal instructions.
|
||||
*/
|
||||
sz = round_up(bpfsize + sizeof(*header) + 128, PAGE_SIZE);
|
||||
header = module_alloc(sz);
|
||||
if (!header)
|
||||
return NULL;
|
||||
memset(header, 0, sz);
|
||||
header->pages = sz / PAGE_SIZE;
|
||||
hole = min(sz - (bpfsize + sizeof(*header)), PAGE_SIZE - sizeof(*header));
|
||||
/* Insert random number of illegal instructions before BPF code
|
||||
* and make sure the first instruction starts at an even address.
|
||||
*/
|
||||
*image_ptr = &header->image[(prandom_u32() % hole) & -2];
|
||||
return header;
|
||||
}
|
||||
|
||||
void bpf_jit_compile(struct bpf_prog *fp)
|
||||
{
|
||||
struct bpf_binary_header *header = NULL;
|
||||
|
@ -850,7 +789,8 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||
size = prg_len + lit_len;
|
||||
if (size >= BPF_SIZE_MAX)
|
||||
goto out;
|
||||
header = bpf_alloc_binary(size, &jit.start);
|
||||
header = bpf_jit_binary_alloc(size, &jit.start,
|
||||
2, bpf_jit_fill_hole);
|
||||
if (!header)
|
||||
goto out;
|
||||
jit.prg = jit.mid = jit.start + prg_len;
|
||||
|
@ -869,7 +809,7 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||
if (jit.start) {
|
||||
set_memory_ro((unsigned long)header, header->pages);
|
||||
fp->bpf_func = (void *) jit.start;
|
||||
fp->jited = 1;
|
||||
fp->jited = true;
|
||||
}
|
||||
out:
|
||||
kfree(addrs);
|
||||
|
@ -884,8 +824,8 @@ void bpf_jit_free(struct bpf_prog *fp)
|
|||
goto free_filter;
|
||||
|
||||
set_memory_rw(addr, header->pages);
|
||||
module_free(NULL, header);
|
||||
bpf_jit_binary_free(header);
|
||||
|
||||
free_filter:
|
||||
kfree(fp);
|
||||
bpf_prog_unlock_free(fp);
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ struct vio_dring_register {
|
|||
u16 options;
|
||||
#define VIO_TX_DRING 0x0001
|
||||
#define VIO_RX_DRING 0x0002
|
||||
#define VIO_RX_DRING_DATA 0x0004
|
||||
u16 resv;
|
||||
u32 num_cookies;
|
||||
struct ldc_trans_cookie cookies[0];
|
||||
|
@ -80,6 +81,8 @@ struct vio_dring_unregister {
|
|||
#define VIO_PKT_MODE 0x01 /* Packet based transfer */
|
||||
#define VIO_DESC_MODE 0x02 /* In-band descriptors */
|
||||
#define VIO_DRING_MODE 0x03 /* Descriptor rings */
|
||||
/* in vers >= 1.2, VIO_DRING_MODE is 0x04 and transfer mode is a bitmask */
|
||||
#define VIO_NEW_DRING_MODE 0x04
|
||||
|
||||
struct vio_dring_data {
|
||||
struct vio_msg_tag tag;
|
||||
|
@ -205,10 +208,20 @@ struct vio_net_attr_info {
|
|||
u8 addr_type;
|
||||
#define VNET_ADDR_ETHERMAC 0x01
|
||||
u16 ack_freq;
|
||||
u32 resv1;
|
||||
u8 plnk_updt;
|
||||
#define PHYSLINK_UPDATE_NONE 0x00
|
||||
#define PHYSLINK_UPDATE_STATE 0x01
|
||||
#define PHYSLINK_UPDATE_STATE_ACK 0x02
|
||||
#define PHYSLINK_UPDATE_STATE_NACK 0x03
|
||||
u8 options;
|
||||
u16 resv1;
|
||||
u64 addr;
|
||||
u64 mtu;
|
||||
u64 resv2[3];
|
||||
u16 cflags;
|
||||
#define VNET_LSO_IPV4_CAPAB 0x0001
|
||||
u16 ipv4_lso_maxlen;
|
||||
u32 resv2;
|
||||
u64 resv3[2];
|
||||
};
|
||||
|
||||
#define VNET_NUM_MCAST 7
|
||||
|
@ -366,6 +379,33 @@ struct vio_driver_state {
|
|||
struct vio_driver_ops *ops;
|
||||
};
|
||||
|
||||
static inline bool vio_version_before(struct vio_driver_state *vio,
|
||||
u16 major, u16 minor)
|
||||
{
|
||||
u32 have = (u32)vio->ver.major << 16 | vio->ver.minor;
|
||||
u32 want = (u32)major << 16 | minor;
|
||||
|
||||
return have < want;
|
||||
}
|
||||
|
||||
static inline bool vio_version_after(struct vio_driver_state *vio,
|
||||
u16 major, u16 minor)
|
||||
{
|
||||
u32 have = (u32)vio->ver.major << 16 | vio->ver.minor;
|
||||
u32 want = (u32)major << 16 | minor;
|
||||
|
||||
return have > want;
|
||||
}
|
||||
|
||||
static inline bool vio_version_after_eq(struct vio_driver_state *vio,
|
||||
u16 major, u16 minor)
|
||||
{
|
||||
u32 have = (u32)vio->ver.major << 16 | vio->ver.minor;
|
||||
u32 want = (u32)major << 16 | minor;
|
||||
|
||||
return have >= want;
|
||||
}
|
||||
|
||||
#define viodbg(TYPE, f, a...) \
|
||||
do { if (vio->debug & VIO_DEBUG_##TYPE) \
|
||||
printk(KERN_INFO "vio: ID[%lu] " f, \
|
||||
|
|
|
@ -2159,7 +2159,7 @@ int ldc_map_single(struct ldc_channel *lp,
|
|||
state.pte_idx = (base - iommu->page_table);
|
||||
state.nc = 0;
|
||||
fill_cookies(&state, (pa & PAGE_MASK), (pa & ~PAGE_MASK), len);
|
||||
BUG_ON(state.nc != 1);
|
||||
BUG_ON(state.nc > ncookies);
|
||||
|
||||
return state.nc;
|
||||
}
|
||||
|
|
|
@ -426,6 +426,13 @@ static int process_dreg_info(struct vio_driver_state *vio,
|
|||
if (vio->dr_state & VIO_DR_STATE_RXREG)
|
||||
goto send_nack;
|
||||
|
||||
/* v1.6 and higher, ACK with desired, supported mode, or NACK */
|
||||
if (vio_version_after_eq(vio, 1, 6)) {
|
||||
if (!(pkt->options & VIO_TX_DRING))
|
||||
goto send_nack;
|
||||
pkt->options = VIO_TX_DRING;
|
||||
}
|
||||
|
||||
BUG_ON(vio->desc_buf);
|
||||
|
||||
vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC);
|
||||
|
@ -453,8 +460,11 @@ static int process_dreg_info(struct vio_driver_state *vio,
|
|||
pkt->tag.stype = VIO_SUBTYPE_ACK;
|
||||
pkt->dring_ident = ++dr->ident;
|
||||
|
||||
viodbg(HS, "SEND DRING_REG ACK ident[%llx]\n",
|
||||
(unsigned long long) pkt->dring_ident);
|
||||
viodbg(HS, "SEND DRING_REG ACK ident[%llx] "
|
||||
"ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n",
|
||||
(unsigned long long) pkt->dring_ident,
|
||||
pkt->num_descr, pkt->descr_size, pkt->options,
|
||||
pkt->num_cookies);
|
||||
|
||||
len = (sizeof(*pkt) +
|
||||
(dr->ncookies * sizeof(struct ldc_trans_cookie)));
|
||||
|
|
|
@ -585,16 +585,11 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||
case BPF_ANC | SKF_AD_PROTOCOL:
|
||||
emit_skb_load16(protocol, r_A);
|
||||
break;
|
||||
#if 0
|
||||
/* GCC won't let us take the address of
|
||||
* a bit field even though we very much
|
||||
* know what we are doing here.
|
||||
*/
|
||||
case BPF_ANC | SKF_AD_PKTTYPE:
|
||||
__emit_skb_load8(pkt_type, r_A);
|
||||
__emit_skb_load8(__pkt_type_offset, r_A);
|
||||
emit_andi(r_A, PKT_TYPE_MAX, r_A);
|
||||
emit_alu_K(SRL, 5);
|
||||
break;
|
||||
#endif
|
||||
case BPF_ANC | SKF_AD_IFINDEX:
|
||||
emit_skb_loadptr(dev, r_A);
|
||||
emit_cmpi(r_A, 0);
|
||||
|
@ -629,7 +624,12 @@ void bpf_jit_compile(struct bpf_prog *fp)
|
|||
emit_and(r_A, r_TMP, r_A);
|
||||
}
|
||||
break;
|
||||
|
||||
case BPF_LD | BPF_W | BPF_LEN:
|
||||
emit_skb_load32(len, r_A);
|
||||
break;
|
||||
case BPF_LDX | BPF_W | BPF_LEN:
|
||||
emit_skb_load32(len, r_X);
|
||||
break;
|
||||
case BPF_LD | BPF_IMM:
|
||||
emit_loadimm(K, r_A);
|
||||
break;
|
||||
|
@ -812,7 +812,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf];
|
|||
if (image) {
|
||||
bpf_flush_icache(image, image + proglen);
|
||||
fp->bpf_func = (void *)image;
|
||||
fp->jited = 1;
|
||||
fp->jited = true;
|
||||
}
|
||||
out:
|
||||
kfree(addrs);
|
||||
|
@ -823,5 +823,6 @@ void bpf_jit_free(struct bpf_prog *fp)
|
|||
{
|
||||
if (fp->jited)
|
||||
module_free(NULL, fp->bpf_func);
|
||||
kfree(fp);
|
||||
|
||||
bpf_prog_unlock_free(fp);
|
||||
}
|
||||
|
|
|
@ -8,12 +8,10 @@
|
|||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*/
|
||||
#include <linux/moduleloader.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/random.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
int bpf_jit_enable __read_mostly;
|
||||
|
||||
|
@ -109,39 +107,6 @@ static inline void bpf_flush_icache(void *start, void *end)
|
|||
#define CHOOSE_LOAD_FUNC(K, func) \
|
||||
((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset)
|
||||
|
||||
struct bpf_binary_header {
|
||||
unsigned int pages;
|
||||
/* Note : for security reasons, bpf code will follow a randomly
|
||||
* sized amount of int3 instructions
|
||||
*/
|
||||
u8 image[];
|
||||
};
|
||||
|
||||
static struct bpf_binary_header *bpf_alloc_binary(unsigned int proglen,
|
||||
u8 **image_ptr)
|
||||
{
|
||||
unsigned int sz, hole;
|
||||
struct bpf_binary_header *header;
|
||||
|
||||
/* Most of BPF filters are really small,
|
||||
* but if some of them fill a page, allow at least
|
||||
* 128 extra bytes to insert a random section of int3
|
||||
*/
|
||||
sz = round_up(proglen + sizeof(*header) + 128, PAGE_SIZE);
|
||||
header = module_alloc(sz);
|
||||
if (!header)
|
||||
return NULL;
|
||||
|
||||
memset(header, 0xcc, sz); /* fill whole space with int3 instructions */
|
||||
|
||||
header->pages = sz / PAGE_SIZE;
|
||||
hole = min(sz - (proglen + sizeof(*header)), PAGE_SIZE - sizeof(*header));
|
||||
|
||||
/* insert a random number of int3 instructions before BPF code */
|
||||
*image_ptr = &header->image[prandom_u32() % hole];
|
||||
return header;
|
||||
}
|
||||
|
||||
/* pick a register outside of BPF range for JIT internal work */
|
||||
#define AUX_REG (MAX_BPF_REG + 1)
|
||||
|
||||
|
@ -206,6 +171,12 @@ static inline u8 add_2reg(u8 byte, u32 dst_reg, u32 src_reg)
|
|||
return byte + reg2hex[dst_reg] + (reg2hex[src_reg] << 3);
|
||||
}
|
||||
|
||||
static void jit_fill_hole(void *area, unsigned int size)
|
||||
{
|
||||
/* fill whole space with int3 instructions */
|
||||
memset(area, 0xcc, size);
|
||||
}
|
||||
|
||||
struct jit_context {
|
||||
unsigned int cleanup_addr; /* epilogue code offset */
|
||||
bool seen_ld_abs;
|
||||
|
@ -393,6 +364,23 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
|||
EMIT1_off32(add_1reg(0xB8, dst_reg), imm32);
|
||||
break;
|
||||
|
||||
case BPF_LD | BPF_IMM | BPF_DW:
|
||||
if (insn[1].code != 0 || insn[1].src_reg != 0 ||
|
||||
insn[1].dst_reg != 0 || insn[1].off != 0) {
|
||||
/* verifier must catch invalid insns */
|
||||
pr_err("invalid BPF_LD_IMM64 insn\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* movabsq %rax, imm64 */
|
||||
EMIT2(add_1mod(0x48, dst_reg), add_1reg(0xB8, dst_reg));
|
||||
EMIT(insn[0].imm, 4);
|
||||
EMIT(insn[1].imm, 4);
|
||||
|
||||
insn++;
|
||||
i++;
|
||||
break;
|
||||
|
||||
/* dst %= src, dst /= src, dst %= imm32, dst /= imm32 */
|
||||
case BPF_ALU | BPF_MOD | BPF_X:
|
||||
case BPF_ALU | BPF_DIV | BPF_X:
|
||||
|
@ -515,6 +503,48 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
|
|||
EMIT3(0xC1, add_1reg(b3, dst_reg), imm32);
|
||||
break;
|
||||
|
||||
case BPF_ALU | BPF_LSH | BPF_X:
|
||||
case BPF_ALU | BPF_RSH | BPF_X:
|
||||
case BPF_ALU | BPF_ARSH | BPF_X:
|
||||
case BPF_ALU64 | BPF_LSH | BPF_X:
|
||||
case BPF_ALU64 | BPF_RSH | BPF_X:
|
||||
case BPF_ALU64 | BPF_ARSH | BPF_X:
|
||||
|
||||
/* check for bad case when dst_reg == rcx */
|
||||
if (dst_reg == BPF_REG_4) {
|
||||
/* mov r11, dst_reg */
|
||||
EMIT_mov(AUX_REG, dst_reg);
|
||||
dst_reg = AUX_REG;
|
||||
}
|
||||
|
||||
if (src_reg != BPF_REG_4) { /* common case */
|
||||
EMIT1(0x51); /* push rcx */
|
||||
|
||||
/* mov rcx, src_reg */
|
||||
EMIT_mov(BPF_REG_4, src_reg);
|
||||
}
|
||||
|
||||
/* shl %rax, %cl | shr %rax, %cl | sar %rax, %cl */
|
||||
if (BPF_CLASS(insn->code) == BPF_ALU64)
|
||||
EMIT1(add_1mod(0x48, dst_reg));
|
||||
else if (is_ereg(dst_reg))
|
||||
EMIT1(add_1mod(0x40, dst_reg));
|
||||
|
||||
switch (BPF_OP(insn->code)) {
|
||||
case BPF_LSH: b3 = 0xE0; break;
|
||||
case BPF_RSH: b3 = 0xE8; break;
|
||||
case BPF_ARSH: b3 = 0xF8; break;
|
||||
}
|
||||
EMIT2(0xD3, add_1reg(b3, dst_reg));
|
||||
|
||||
if (src_reg != BPF_REG_4)
|
||||
EMIT1(0x59); /* pop rcx */
|
||||
|
||||
if (insn->dst_reg == BPF_REG_4)
|
||||
/* mov dst_reg, r11 */
|
||||
EMIT_mov(insn->dst_reg, AUX_REG);
|
||||
break;
|
||||
|
||||
case BPF_ALU | BPF_END | BPF_FROM_BE:
|
||||
switch (imm32) {
|
||||
case 16:
|
||||
|
@ -900,7 +930,7 @@ void bpf_int_jit_compile(struct bpf_prog *prog)
|
|||
if (proglen <= 0) {
|
||||
image = NULL;
|
||||
if (header)
|
||||
module_free(NULL, header);
|
||||
bpf_jit_binary_free(header);
|
||||
goto out;
|
||||
}
|
||||
if (image) {
|
||||
|
@ -910,7 +940,8 @@ void bpf_int_jit_compile(struct bpf_prog *prog)
|
|||
break;
|
||||
}
|
||||
if (proglen == oldproglen) {
|
||||
header = bpf_alloc_binary(proglen, &image);
|
||||
header = bpf_jit_binary_alloc(proglen, &image,
|
||||
1, jit_fill_hole);
|
||||
if (!header)
|
||||
goto out;
|
||||
}
|
||||
|
@ -924,29 +955,23 @@ void bpf_int_jit_compile(struct bpf_prog *prog)
|
|||
bpf_flush_icache(header, image + proglen);
|
||||
set_memory_ro((unsigned long)header, header->pages);
|
||||
prog->bpf_func = (void *)image;
|
||||
prog->jited = 1;
|
||||
prog->jited = true;
|
||||
}
|
||||
out:
|
||||
kfree(addrs);
|
||||
}
|
||||
|
||||
static void bpf_jit_free_deferred(struct work_struct *work)
|
||||
void bpf_jit_free(struct bpf_prog *fp)
|
||||
{
|
||||
struct bpf_prog *fp = container_of(work, struct bpf_prog, work);
|
||||
unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
|
||||
struct bpf_binary_header *header = (void *)addr;
|
||||
|
||||
set_memory_rw(addr, header->pages);
|
||||
module_free(NULL, header);
|
||||
kfree(fp);
|
||||
}
|
||||
if (!fp->jited)
|
||||
goto free_filter;
|
||||
|
||||
void bpf_jit_free(struct bpf_prog *fp)
|
||||
{
|
||||
if (fp->jited) {
|
||||
INIT_WORK(&fp->work, bpf_jit_free_deferred);
|
||||
schedule_work(&fp->work);
|
||||
} else {
|
||||
kfree(fp);
|
||||
}
|
||||
set_memory_rw(addr, header->pages);
|
||||
bpf_jit_binary_free(header);
|
||||
|
||||
free_filter:
|
||||
bpf_prog_unlock_free(fp);
|
||||
}
|
||||
|
|
|
@ -363,3 +363,4 @@
|
|||
354 i386 seccomp sys_seccomp
|
||||
355 i386 getrandom sys_getrandom
|
||||
356 i386 memfd_create sys_memfd_create
|
||||
357 i386 bpf sys_bpf
|
||||
|
|
|
@ -327,6 +327,7 @@
|
|||
318 common getrandom sys_getrandom
|
||||
319 common memfd_create sys_memfd_create
|
||||
320 common kexec_file_load sys_kexec_file_load
|
||||
321 common bpf sys_bpf
|
||||
|
||||
#
|
||||
# x32-specific system call numbers start at 512 to avoid cache impact
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
bcma-y += main.o scan.o core.o sprom.o
|
||||
bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o
|
||||
bcma-y += driver_chipcommon_b.o
|
||||
bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o
|
||||
bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o
|
||||
bcma-y += driver_pci.o
|
||||
|
|
|
@ -50,6 +50,10 @@ void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
|
|||
extern struct platform_device bcma_pflash_dev;
|
||||
#endif /* CONFIG_BCMA_DRIVER_MIPS */
|
||||
|
||||
/* driver_chipcommon_b.c */
|
||||
int bcma_core_chipcommon_b_init(struct bcma_drv_cc_b *ccb);
|
||||
void bcma_core_chipcommon_b_free(struct bcma_drv_cc_b *ccb);
|
||||
|
||||
/* driver_chipcommon_pmu.c */
|
||||
u32 bcma_pmu_get_alp_clock(struct bcma_drv_cc *cc);
|
||||
u32 bcma_pmu_get_cpu_clock(struct bcma_drv_cc *cc);
|
||||
|
@ -84,6 +88,20 @@ extern int __init bcma_host_pci_init(void);
|
|||
extern void __exit bcma_host_pci_exit(void);
|
||||
#endif /* CONFIG_BCMA_HOST_PCI */
|
||||
|
||||
/* host_soc.c */
|
||||
#if defined(CONFIG_BCMA_HOST_SOC) && defined(CONFIG_OF)
|
||||
extern int __init bcma_host_soc_register_driver(void);
|
||||
extern void __exit bcma_host_soc_unregister_driver(void);
|
||||
#else
|
||||
static inline int __init bcma_host_soc_register_driver(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void __exit bcma_host_soc_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_BCMA_HOST_SOC && CONFIG_OF */
|
||||
|
||||
/* driver_pci.c */
|
||||
u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address);
|
||||
|
||||
|
|
61
drivers/bcma/driver_chipcommon_b.c
Normal file
61
drivers/bcma/driver_chipcommon_b.c
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Broadcom specific AMBA
|
||||
* ChipCommon B Unit driver
|
||||
*
|
||||
* Copyright 2014, Hauke Mehrtens <hauke@hauke-m.de>
|
||||
*
|
||||
* Licensed under the GNU/GPL. See COPYING for details.
|
||||
*/
|
||||
|
||||
#include "bcma_private.h"
|
||||
#include <linux/export.h>
|
||||
#include <linux/bcma/bcma.h>
|
||||
|
||||
static bool bcma_wait_reg(struct bcma_bus *bus, void __iomem *addr, u32 mask,
|
||||
u32 value, int timeout)
|
||||
{
|
||||
unsigned long deadline = jiffies + timeout;
|
||||
u32 val;
|
||||
|
||||
do {
|
||||
val = readl(addr);
|
||||
if ((val & mask) == value)
|
||||
return true;
|
||||
cpu_relax();
|
||||
udelay(10);
|
||||
} while (!time_after_eq(jiffies, deadline));
|
||||
|
||||
bcma_err(bus, "Timeout waiting for register %p\n", addr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void bcma_chipco_b_mii_write(struct bcma_drv_cc_b *ccb, u32 offset, u32 value)
|
||||
{
|
||||
struct bcma_bus *bus = ccb->core->bus;
|
||||
|
||||
writel(offset, ccb->mii + 0x00);
|
||||
bcma_wait_reg(bus, ccb->mii + 0x00, 0x0100, 0x0000, 100);
|
||||
writel(value, ccb->mii + 0x04);
|
||||
bcma_wait_reg(bus, ccb->mii + 0x00, 0x0100, 0x0000, 100);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_chipco_b_mii_write);
|
||||
|
||||
int bcma_core_chipcommon_b_init(struct bcma_drv_cc_b *ccb)
|
||||
{
|
||||
if (ccb->setup_done)
|
||||
return 0;
|
||||
|
||||
ccb->setup_done = 1;
|
||||
ccb->mii = ioremap_nocache(ccb->core->addr_s[1], BCMA_CORE_SIZE);
|
||||
if (!ccb->mii)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bcma_core_chipcommon_b_free(struct bcma_drv_cc_b *ccb)
|
||||
{
|
||||
if (ccb->mii)
|
||||
iounmap(ccb->mii);
|
||||
}
|
|
@ -76,7 +76,7 @@ static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio)
|
|||
bcma_chipco_gpio_pullup(cc, 1 << gpio, 0);
|
||||
}
|
||||
|
||||
#if IS_BUILTIN(CONFIG_BCMA_HOST_SOC)
|
||||
#if IS_BUILTIN(CONFIG_BCM47XX)
|
||||
static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
|
||||
{
|
||||
struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
|
||||
|
@ -215,8 +215,12 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
|
|||
chip->set = bcma_gpio_set_value;
|
||||
chip->direction_input = bcma_gpio_direction_input;
|
||||
chip->direction_output = bcma_gpio_direction_output;
|
||||
#if IS_BUILTIN(CONFIG_BCMA_HOST_SOC)
|
||||
#if IS_BUILTIN(CONFIG_BCM47XX)
|
||||
chip->to_irq = bcma_gpio_to_irq;
|
||||
#endif
|
||||
#if IS_BUILTIN(CONFIG_OF)
|
||||
if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
|
||||
chip->of_node = cc->core->dev.of_node;
|
||||
#endif
|
||||
switch (cc->core->bus->chipinfo.id) {
|
||||
case BCMA_CHIP_ID_BCM5357:
|
||||
|
|
|
@ -21,6 +21,14 @@
|
|||
#include <linux/serial_reg.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
enum bcma_boot_dev {
|
||||
BCMA_BOOT_DEV_UNK = 0,
|
||||
BCMA_BOOT_DEV_ROM,
|
||||
BCMA_BOOT_DEV_PARALLEL,
|
||||
BCMA_BOOT_DEV_SERIAL,
|
||||
BCMA_BOOT_DEV_NAND,
|
||||
};
|
||||
|
||||
static const char * const part_probes[] = { "bcm47xxpart", NULL };
|
||||
|
||||
static struct physmap_flash_data bcma_pflash_data = {
|
||||
|
@ -229,11 +237,51 @@ u32 bcma_cpu_clock(struct bcma_drv_mips *mcore)
|
|||
}
|
||||
EXPORT_SYMBOL(bcma_cpu_clock);
|
||||
|
||||
static enum bcma_boot_dev bcma_boot_dev(struct bcma_bus *bus)
|
||||
{
|
||||
struct bcma_drv_cc *cc = &bus->drv_cc;
|
||||
u8 cc_rev = cc->core->id.rev;
|
||||
|
||||
if (cc_rev == 42) {
|
||||
struct bcma_device *core;
|
||||
|
||||
core = bcma_find_core(bus, BCMA_CORE_NS_ROM);
|
||||
if (core) {
|
||||
switch (bcma_aread32(core, BCMA_IOST) &
|
||||
BCMA_NS_ROM_IOST_BOOT_DEV_MASK) {
|
||||
case BCMA_NS_ROM_IOST_BOOT_DEV_NOR:
|
||||
return BCMA_BOOT_DEV_SERIAL;
|
||||
case BCMA_NS_ROM_IOST_BOOT_DEV_NAND:
|
||||
return BCMA_BOOT_DEV_NAND;
|
||||
case BCMA_NS_ROM_IOST_BOOT_DEV_ROM:
|
||||
default:
|
||||
return BCMA_BOOT_DEV_ROM;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cc_rev == 38) {
|
||||
if (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT)
|
||||
return BCMA_BOOT_DEV_NAND;
|
||||
else if (cc->status & BIT(5))
|
||||
return BCMA_BOOT_DEV_ROM;
|
||||
}
|
||||
|
||||
if ((cc->capabilities & BCMA_CC_CAP_FLASHT) ==
|
||||
BCMA_CC_FLASHT_PARA)
|
||||
return BCMA_BOOT_DEV_PARALLEL;
|
||||
else
|
||||
return BCMA_BOOT_DEV_SERIAL;
|
||||
}
|
||||
|
||||
return BCMA_BOOT_DEV_SERIAL;
|
||||
}
|
||||
|
||||
static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
|
||||
{
|
||||
struct bcma_bus *bus = mcore->core->bus;
|
||||
struct bcma_drv_cc *cc = &bus->drv_cc;
|
||||
struct bcma_pflash *pflash = &cc->pflash;
|
||||
enum bcma_boot_dev boot_dev;
|
||||
|
||||
switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
|
||||
case BCMA_CC_FLASHT_STSER:
|
||||
|
@ -269,6 +317,20 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
|
|||
bcma_nflash_init(cc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine flash type this SoC boots from */
|
||||
boot_dev = bcma_boot_dev(bus);
|
||||
switch (boot_dev) {
|
||||
case BCMA_BOOT_DEV_PARALLEL:
|
||||
case BCMA_BOOT_DEV_SERIAL:
|
||||
/* TODO: Init NVRAM using BCMA_SOC_FLASH2 window */
|
||||
break;
|
||||
case BCMA_BOOT_DEV_NAND:
|
||||
/* TODO: Init NVRAM using BCMA_SOC_FLASH1 window */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bcma_core_mips_early_init(struct bcma_drv_mips *mcore)
|
||||
|
|
|
@ -208,6 +208,9 @@ static int bcma_host_pci_probe(struct pci_dev *dev,
|
|||
bus->boardinfo.vendor = bus->host_pci->subsystem_vendor;
|
||||
bus->boardinfo.type = bus->host_pci->subsystem_device;
|
||||
|
||||
/* Initialize struct, detect chip */
|
||||
bcma_init_bus(bus);
|
||||
|
||||
/* Register */
|
||||
err = bcma_bus_register(bus);
|
||||
if (err)
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
#include "bcma_private.h"
|
||||
#include "scan.h"
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/bcma/bcma_soc.h>
|
||||
|
||||
|
@ -134,12 +137,16 @@ static void bcma_host_soc_block_write(struct bcma_device *core,
|
|||
|
||||
static u32 bcma_host_soc_aread32(struct bcma_device *core, u16 offset)
|
||||
{
|
||||
if (WARN_ONCE(!core->io_wrap, "Accessed core has no wrapper/agent\n"))
|
||||
return ~0;
|
||||
return readl(core->io_wrap + offset);
|
||||
}
|
||||
|
||||
static void bcma_host_soc_awrite32(struct bcma_device *core, u16 offset,
|
||||
u32 value)
|
||||
{
|
||||
if (WARN_ONCE(!core->io_wrap, "Accessed core has no wrapper/agent\n"))
|
||||
return;
|
||||
writel(value, core->io_wrap + offset);
|
||||
}
|
||||
|
||||
|
@ -161,7 +168,6 @@ static const struct bcma_host_ops bcma_host_soc_ops = {
|
|||
int __init bcma_host_soc_register(struct bcma_soc *soc)
|
||||
{
|
||||
struct bcma_bus *bus = &soc->bus;
|
||||
int err;
|
||||
|
||||
/* iomap only first core. We have to read some register on this core
|
||||
* to scan the bus.
|
||||
|
@ -173,11 +179,100 @@ int __init bcma_host_soc_register(struct bcma_soc *soc)
|
|||
/* Host specific */
|
||||
bus->hosttype = BCMA_HOSTTYPE_SOC;
|
||||
bus->ops = &bcma_host_soc_ops;
|
||||
bus->host_pdev = NULL;
|
||||
|
||||
/* Register */
|
||||
/* Initialize struct, detect chip */
|
||||
bcma_init_bus(bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init bcma_host_soc_init(struct bcma_soc *soc)
|
||||
{
|
||||
struct bcma_bus *bus = &soc->bus;
|
||||
int err;
|
||||
|
||||
/* Scan bus and initialize it */
|
||||
err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
|
||||
if (err)
|
||||
iounmap(bus->mmio);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int bcma_host_soc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct bcma_bus *bus;
|
||||
int err;
|
||||
|
||||
/* Alloc */
|
||||
bus = devm_kzalloc(dev, sizeof(*bus), GFP_KERNEL);
|
||||
if (!bus)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Map MMIO */
|
||||
bus->mmio = of_iomap(np, 0);
|
||||
if (!bus->mmio)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Host specific */
|
||||
bus->hosttype = BCMA_HOSTTYPE_SOC;
|
||||
bus->ops = &bcma_host_soc_ops;
|
||||
bus->host_pdev = pdev;
|
||||
|
||||
/* Initialize struct, detect chip */
|
||||
bcma_init_bus(bus);
|
||||
|
||||
/* Register */
|
||||
err = bcma_bus_register(bus);
|
||||
if (err)
|
||||
goto err_unmap_mmio;
|
||||
|
||||
platform_set_drvdata(pdev, bus);
|
||||
|
||||
return err;
|
||||
|
||||
err_unmap_mmio:
|
||||
iounmap(bus->mmio);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bcma_host_soc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bcma_bus *bus = platform_get_drvdata(pdev);
|
||||
|
||||
bcma_bus_unregister(bus);
|
||||
iounmap(bus->mmio);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcma_host_soc_of_match[] = {
|
||||
{ .compatible = "brcm,bus-axi", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcma_host_soc_of_match);
|
||||
|
||||
static struct platform_driver bcma_host_soc_driver = {
|
||||
.driver = {
|
||||
.name = "bcma-host-soc",
|
||||
.of_match_table = bcma_host_soc_of_match,
|
||||
},
|
||||
.probe = bcma_host_soc_probe,
|
||||
.remove = bcma_host_soc_remove,
|
||||
};
|
||||
|
||||
int __init bcma_host_soc_register_driver(void)
|
||||
{
|
||||
return platform_driver_register(&bcma_host_soc_driver);
|
||||
}
|
||||
|
||||
void __exit bcma_host_soc_unregister_driver(void)
|
||||
{
|
||||
platform_driver_unregister(&bcma_host_soc_driver);
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -120,16 +121,103 @@ static void bcma_release_core_dev(struct device *dev)
|
|||
kfree(core);
|
||||
}
|
||||
|
||||
static int bcma_register_cores(struct bcma_bus *bus)
|
||||
static bool bcma_is_core_needed_early(u16 core_id)
|
||||
{
|
||||
switch (core_id) {
|
||||
case BCMA_CORE_NS_NAND:
|
||||
case BCMA_CORE_NS_QSPI:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct device_node *bcma_of_find_child_device(struct platform_device *parent,
|
||||
struct bcma_device *core)
|
||||
{
|
||||
struct device_node *node;
|
||||
u64 size;
|
||||
const __be32 *reg;
|
||||
|
||||
if (!parent || !parent->dev.of_node)
|
||||
return NULL;
|
||||
|
||||
for_each_child_of_node(parent->dev.of_node, node) {
|
||||
reg = of_get_address(node, 0, &size, NULL);
|
||||
if (!reg)
|
||||
continue;
|
||||
if (of_translate_address(node, reg) == core->addr)
|
||||
return node;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void bcma_of_fill_device(struct platform_device *parent,
|
||||
struct bcma_device *core)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
||||
node = bcma_of_find_child_device(parent, core);
|
||||
if (node)
|
||||
core->dev.of_node = node;
|
||||
}
|
||||
#else
|
||||
static void bcma_of_fill_device(struct platform_device *parent,
|
||||
struct bcma_device *core)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
|
||||
{
|
||||
int err;
|
||||
|
||||
core->dev.release = bcma_release_core_dev;
|
||||
core->dev.bus = &bcma_bus_type;
|
||||
dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
|
||||
|
||||
switch (bus->hosttype) {
|
||||
case BCMA_HOSTTYPE_PCI:
|
||||
core->dev.parent = &bus->host_pci->dev;
|
||||
core->dma_dev = &bus->host_pci->dev;
|
||||
core->irq = bus->host_pci->irq;
|
||||
break;
|
||||
case BCMA_HOSTTYPE_SOC:
|
||||
core->dev.dma_mask = &core->dev.coherent_dma_mask;
|
||||
if (bus->host_pdev) {
|
||||
core->dma_dev = &bus->host_pdev->dev;
|
||||
core->dev.parent = &bus->host_pdev->dev;
|
||||
bcma_of_fill_device(bus->host_pdev, core);
|
||||
} else {
|
||||
core->dma_dev = &core->dev;
|
||||
}
|
||||
break;
|
||||
case BCMA_HOSTTYPE_SDIO:
|
||||
break;
|
||||
}
|
||||
|
||||
err = device_register(&core->dev);
|
||||
if (err) {
|
||||
bcma_err(bus, "Could not register dev for core 0x%03X\n",
|
||||
core->id.id);
|
||||
put_device(&core->dev);
|
||||
return;
|
||||
}
|
||||
core->dev_registered = true;
|
||||
}
|
||||
|
||||
static int bcma_register_devices(struct bcma_bus *bus)
|
||||
{
|
||||
struct bcma_device *core;
|
||||
int err, dev_id = 0;
|
||||
int err;
|
||||
|
||||
list_for_each_entry(core, &bus->cores, list) {
|
||||
/* We support that cores ourself */
|
||||
switch (core->id.id) {
|
||||
case BCMA_CORE_4706_CHIPCOMMON:
|
||||
case BCMA_CORE_CHIPCOMMON:
|
||||
case BCMA_CORE_NS_CHIPCOMMON_B:
|
||||
case BCMA_CORE_PCI:
|
||||
case BCMA_CORE_PCIE:
|
||||
case BCMA_CORE_PCIE2:
|
||||
|
@ -138,39 +226,16 @@ static int bcma_register_cores(struct bcma_bus *bus)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Early cores were already registered */
|
||||
if (bcma_is_core_needed_early(core->id.id))
|
||||
continue;
|
||||
|
||||
/* Only first GMAC core on BCM4706 is connected and working */
|
||||
if (core->id.id == BCMA_CORE_4706_MAC_GBIT &&
|
||||
core->core_unit > 0)
|
||||
continue;
|
||||
|
||||
core->dev.release = bcma_release_core_dev;
|
||||
core->dev.bus = &bcma_bus_type;
|
||||
dev_set_name(&core->dev, "bcma%d:%d", bus->num, dev_id);
|
||||
|
||||
switch (bus->hosttype) {
|
||||
case BCMA_HOSTTYPE_PCI:
|
||||
core->dev.parent = &bus->host_pci->dev;
|
||||
core->dma_dev = &bus->host_pci->dev;
|
||||
core->irq = bus->host_pci->irq;
|
||||
break;
|
||||
case BCMA_HOSTTYPE_SOC:
|
||||
core->dev.dma_mask = &core->dev.coherent_dma_mask;
|
||||
core->dma_dev = &core->dev;
|
||||
break;
|
||||
case BCMA_HOSTTYPE_SDIO:
|
||||
break;
|
||||
}
|
||||
|
||||
err = device_register(&core->dev);
|
||||
if (err) {
|
||||
bcma_err(bus,
|
||||
"Could not register dev for core 0x%03X\n",
|
||||
core->id.id);
|
||||
put_device(&core->dev);
|
||||
continue;
|
||||
}
|
||||
core->dev_registered = true;
|
||||
dev_id++;
|
||||
bcma_register_core(bus, core);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BCMA_DRIVER_MIPS
|
||||
|
@ -247,6 +312,12 @@ int bcma_bus_register(struct bcma_bus *bus)
|
|||
bcma_core_chipcommon_early_init(&bus->drv_cc);
|
||||
}
|
||||
|
||||
/* Cores providing flash access go before SPROM init */
|
||||
list_for_each_entry(core, &bus->cores, list) {
|
||||
if (bcma_is_core_needed_early(core->id.id))
|
||||
bcma_register_core(bus, core);
|
||||
}
|
||||
|
||||
/* Try to get SPROM */
|
||||
err = bcma_sprom_get(bus);
|
||||
if (err == -ENOENT) {
|
||||
|
@ -261,6 +332,13 @@ int bcma_bus_register(struct bcma_bus *bus)
|
|||
bcma_core_chipcommon_init(&bus->drv_cc);
|
||||
}
|
||||
|
||||
/* Init CC core */
|
||||
core = bcma_find_core(bus, BCMA_CORE_NS_CHIPCOMMON_B);
|
||||
if (core) {
|
||||
bus->drv_cc_b.core = core;
|
||||
bcma_core_chipcommon_b_init(&bus->drv_cc_b);
|
||||
}
|
||||
|
||||
/* Init MIPS core */
|
||||
core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
|
||||
if (core) {
|
||||
|
@ -297,7 +375,7 @@ int bcma_bus_register(struct bcma_bus *bus)
|
|||
}
|
||||
|
||||
/* Register found cores */
|
||||
bcma_register_cores(bus);
|
||||
bcma_register_devices(bus);
|
||||
|
||||
bcma_info(bus, "Bus registered\n");
|
||||
|
||||
|
@ -315,6 +393,8 @@ void bcma_bus_unregister(struct bcma_bus *bus)
|
|||
else if (err)
|
||||
bcma_err(bus, "Can not unregister GPIO driver: %i\n", err);
|
||||
|
||||
bcma_core_chipcommon_b_free(&bus->drv_cc_b);
|
||||
|
||||
cores[0] = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
|
||||
cores[1] = bcma_find_core(bus, BCMA_CORE_PCIE);
|
||||
cores[2] = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON);
|
||||
|
@ -334,8 +414,6 @@ int __init bcma_bus_early_register(struct bcma_bus *bus,
|
|||
struct bcma_device *core;
|
||||
struct bcma_device_id match;
|
||||
|
||||
bcma_init_bus(bus);
|
||||
|
||||
match.manuf = BCMA_MANUF_BCM;
|
||||
match.id = bcma_cc_core_id(bus);
|
||||
match.class = BCMA_CL_SIM;
|
||||
|
@ -494,6 +572,11 @@ static int __init bcma_modinit(void)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
err = bcma_host_soc_register_driver();
|
||||
if (err) {
|
||||
pr_err("SoC host initialization failed\n");
|
||||
err = 0;
|
||||
}
|
||||
#ifdef CONFIG_BCMA_HOST_PCI
|
||||
err = bcma_host_pci_init();
|
||||
if (err) {
|
||||
|
@ -511,6 +594,7 @@ static void __exit bcma_modexit(void)
|
|||
#ifdef CONFIG_BCMA_HOST_PCI
|
||||
bcma_host_pci_exit();
|
||||
#endif
|
||||
bcma_host_soc_unregister_driver();
|
||||
bus_unregister(&bcma_bus_type);
|
||||
}
|
||||
module_exit(bcma_modexit)
|
||||
|
|
|
@ -276,7 +276,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
|
|||
struct bcma_device *core)
|
||||
{
|
||||
u32 tmp;
|
||||
u8 i, j;
|
||||
u8 i, j, k;
|
||||
s32 cia, cib;
|
||||
u8 ports[2], wrappers[2];
|
||||
|
||||
|
@ -314,6 +314,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
|
|||
/* Some specific cores don't need wrappers */
|
||||
switch (core->id.id) {
|
||||
case BCMA_CORE_4706_MAC_GBIT_COMMON:
|
||||
case BCMA_CORE_NS_CHIPCOMMON_B:
|
||||
/* Not used yet: case BCMA_CORE_OOB_ROUTER: */
|
||||
break;
|
||||
default:
|
||||
|
@ -367,6 +368,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
|
|||
core->addr = tmp;
|
||||
|
||||
/* get & parse slave ports */
|
||||
k = 0;
|
||||
for (i = 0; i < ports[1]; i++) {
|
||||
for (j = 0; ; j++) {
|
||||
tmp = bcma_erom_get_addr_desc(bus, eromptr,
|
||||
|
@ -376,9 +378,9 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
|
|||
/* pr_debug("erom: slave port %d "
|
||||
* "has %d descriptors\n", i, j); */
|
||||
break;
|
||||
} else {
|
||||
if (i == 0 && j == 0)
|
||||
core->addr1 = tmp;
|
||||
} else if (k < ARRAY_SIZE(core->addr_s)) {
|
||||
core->addr_s[k] = tmp;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -421,10 +423,13 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
|
|||
core->io_addr = ioremap_nocache(core->addr, BCMA_CORE_SIZE);
|
||||
if (!core->io_addr)
|
||||
return -ENOMEM;
|
||||
core->io_wrap = ioremap_nocache(core->wrap, BCMA_CORE_SIZE);
|
||||
if (!core->io_wrap) {
|
||||
iounmap(core->io_addr);
|
||||
return -ENOMEM;
|
||||
if (core->wrap) {
|
||||
core->io_wrap = ioremap_nocache(core->wrap,
|
||||
BCMA_CORE_SIZE);
|
||||
if (!core->io_wrap) {
|
||||
iounmap(core->io_addr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -434,9 +439,7 @@ void bcma_init_bus(struct bcma_bus *bus)
|
|||
{
|
||||
s32 tmp;
|
||||
struct bcma_chipinfo *chipinfo = &(bus->chipinfo);
|
||||
|
||||
if (bus->init_done)
|
||||
return;
|
||||
char chip_id[8];
|
||||
|
||||
INIT_LIST_HEAD(&bus->cores);
|
||||
bus->nr_cores = 0;
|
||||
|
@ -447,10 +450,11 @@ void bcma_init_bus(struct bcma_bus *bus)
|
|||
chipinfo->id = (tmp & BCMA_CC_ID_ID) >> BCMA_CC_ID_ID_SHIFT;
|
||||
chipinfo->rev = (tmp & BCMA_CC_ID_REV) >> BCMA_CC_ID_REV_SHIFT;
|
||||
chipinfo->pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT;
|
||||
bcma_info(bus, "Found chip with id 0x%04X, rev 0x%02X and package 0x%02X\n",
|
||||
chipinfo->id, chipinfo->rev, chipinfo->pkg);
|
||||
|
||||
bus->init_done = true;
|
||||
snprintf(chip_id, ARRAY_SIZE(chip_id),
|
||||
(chipinfo->id > 0x9999) ? "%d" : "0x%04X", chipinfo->id);
|
||||
bcma_info(bus, "Found chip with id %s, rev 0x%02X and package 0x%02X\n",
|
||||
chip_id, chipinfo->rev, chipinfo->pkg);
|
||||
}
|
||||
|
||||
int bcma_bus_scan(struct bcma_bus *bus)
|
||||
|
@ -460,8 +464,6 @@ int bcma_bus_scan(struct bcma_bus *bus)
|
|||
|
||||
int err, core_num = 0;
|
||||
|
||||
bcma_init_bus(bus);
|
||||
|
||||
erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
|
||||
if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
|
||||
eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
|
||||
|
|
|
@ -201,7 +201,7 @@ config BT_MRVL
|
|||
The core driver to support Marvell Bluetooth devices.
|
||||
|
||||
This driver is required if you want to support
|
||||
Marvell Bluetooth devices, such as 8688/8787/8797/8897.
|
||||
Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897.
|
||||
|
||||
Say Y here to compile Marvell Bluetooth driver
|
||||
into the kernel or say M to compile it as module.
|
||||
|
@ -214,7 +214,7 @@ config BT_MRVL_SDIO
|
|||
The driver for Marvell Bluetooth chipsets with SDIO interface.
|
||||
|
||||
This driver is required if you want to use Marvell Bluetooth
|
||||
devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8897
|
||||
devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897
|
||||
chipsets are supported.
|
||||
|
||||
Say Y here to compile support for Marvell BT-over-SDIO driver
|
||||
|
|
|
@ -88,6 +88,7 @@ static const struct usb_device_id ath3k_table[] = {
|
|||
{ USB_DEVICE(0x04CA, 0x300b) },
|
||||
{ USB_DEVICE(0x0930, 0x0219) },
|
||||
{ USB_DEVICE(0x0930, 0x0220) },
|
||||
{ USB_DEVICE(0x0930, 0x0227) },
|
||||
{ USB_DEVICE(0x0b05, 0x17d0) },
|
||||
{ USB_DEVICE(0x0CF3, 0x0036) },
|
||||
{ USB_DEVICE(0x0CF3, 0x3004) },
|
||||
|
@ -138,6 +139,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
|
|||
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
|
||||
|
|
|
@ -61,7 +61,7 @@ MODULE_LICENSE("GPL");
|
|||
/* ======================== Local structures ======================== */
|
||||
|
||||
|
||||
typedef struct bluecard_info_t {
|
||||
struct bluecard_info {
|
||||
struct pcmcia_device *p_dev;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
@ -78,7 +78,7 @@ typedef struct bluecard_info_t {
|
|||
|
||||
unsigned char ctrl_reg;
|
||||
unsigned long hw_state; /* Status of the hardware and LED control */
|
||||
} bluecard_info_t;
|
||||
};
|
||||
|
||||
|
||||
static int bluecard_config(struct pcmcia_device *link);
|
||||
|
@ -157,7 +157,7 @@ static void bluecard_detach(struct pcmcia_device *p_dev);
|
|||
|
||||
static void bluecard_activity_led_timeout(u_long arg)
|
||||
{
|
||||
bluecard_info_t *info = (bluecard_info_t *)arg;
|
||||
struct bluecard_info *info = (struct bluecard_info *)arg;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
|
||||
|
@ -173,7 +173,7 @@ static void bluecard_activity_led_timeout(u_long arg)
|
|||
}
|
||||
|
||||
|
||||
static void bluecard_enable_activity_led(bluecard_info_t *info)
|
||||
static void bluecard_enable_activity_led(struct bluecard_info *info)
|
||||
{
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
||||
|
@ -215,7 +215,7 @@ static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, i
|
|||
}
|
||||
|
||||
|
||||
static void bluecard_write_wakeup(bluecard_info_t *info)
|
||||
static void bluecard_write_wakeup(struct bluecard_info *info)
|
||||
{
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
|
@ -368,7 +368,8 @@ static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, in
|
|||
}
|
||||
|
||||
|
||||
static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
|
||||
static void bluecard_receive(struct bluecard_info *info,
|
||||
unsigned int offset)
|
||||
{
|
||||
unsigned int iobase;
|
||||
unsigned char buf[31];
|
||||
|
@ -497,7 +498,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
|
|||
|
||||
static irqreturn_t bluecard_interrupt(int irq, void *dev_inst)
|
||||
{
|
||||
bluecard_info_t *info = dev_inst;
|
||||
struct bluecard_info *info = dev_inst;
|
||||
unsigned int iobase;
|
||||
unsigned char reg;
|
||||
|
||||
|
@ -562,7 +563,7 @@ static irqreturn_t bluecard_interrupt(int irq, void *dev_inst)
|
|||
|
||||
static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
|
||||
{
|
||||
bluecard_info_t *info = hci_get_drvdata(hdev);
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Ericsson baud rate command */
|
||||
|
@ -611,7 +612,7 @@ static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
|
|||
|
||||
static int bluecard_hci_flush(struct hci_dev *hdev)
|
||||
{
|
||||
bluecard_info_t *info = hci_get_drvdata(hdev);
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
/* Drop TX queue */
|
||||
skb_queue_purge(&(info->txq));
|
||||
|
@ -622,7 +623,7 @@ static int bluecard_hci_flush(struct hci_dev *hdev)
|
|||
|
||||
static int bluecard_hci_open(struct hci_dev *hdev)
|
||||
{
|
||||
bluecard_info_t *info = hci_get_drvdata(hdev);
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
|
||||
bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
|
||||
|
@ -643,7 +644,7 @@ static int bluecard_hci_open(struct hci_dev *hdev)
|
|||
|
||||
static int bluecard_hci_close(struct hci_dev *hdev)
|
||||
{
|
||||
bluecard_info_t *info = hci_get_drvdata(hdev);
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
|
||||
return 0;
|
||||
|
@ -663,7 +664,7 @@ static int bluecard_hci_close(struct hci_dev *hdev)
|
|||
|
||||
static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
bluecard_info_t *info = hci_get_drvdata(hdev);
|
||||
struct bluecard_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
switch (bt_cb(skb)->pkt_type) {
|
||||
case HCI_COMMAND_PKT:
|
||||
|
@ -691,7 +692,7 @@ static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
/* ======================== Card services HCI interaction ======================== */
|
||||
|
||||
|
||||
static int bluecard_open(bluecard_info_t *info)
|
||||
static int bluecard_open(struct bluecard_info *info)
|
||||
{
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
struct hci_dev *hdev;
|
||||
|
@ -806,7 +807,7 @@ static int bluecard_open(bluecard_info_t *info)
|
|||
}
|
||||
|
||||
|
||||
static int bluecard_close(bluecard_info_t *info)
|
||||
static int bluecard_close(struct bluecard_info *info)
|
||||
{
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
struct hci_dev *hdev = info->hdev;
|
||||
|
@ -833,7 +834,7 @@ static int bluecard_close(bluecard_info_t *info)
|
|||
|
||||
static int bluecard_probe(struct pcmcia_device *link)
|
||||
{
|
||||
bluecard_info_t *info;
|
||||
struct bluecard_info *info;
|
||||
|
||||
/* Create new info device */
|
||||
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
|
||||
|
@ -857,7 +858,7 @@ static void bluecard_detach(struct pcmcia_device *link)
|
|||
|
||||
static int bluecard_config(struct pcmcia_device *link)
|
||||
{
|
||||
bluecard_info_t *info = link->priv;
|
||||
struct bluecard_info *info = link->priv;
|
||||
int i, n;
|
||||
|
||||
link->config_index = 0x20;
|
||||
|
@ -897,7 +898,7 @@ failed:
|
|||
|
||||
static void bluecard_release(struct pcmcia_device *link)
|
||||
{
|
||||
bluecard_info_t *info = link->priv;
|
||||
struct bluecard_info *info = link->priv;
|
||||
|
||||
bluecard_close(info);
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ MODULE_FIRMWARE("BT3CPCC.bin");
|
|||
/* ======================== Local structures ======================== */
|
||||
|
||||
|
||||
typedef struct bt3c_info_t {
|
||||
struct bt3c_info {
|
||||
struct pcmcia_device *p_dev;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
@ -80,7 +80,7 @@ typedef struct bt3c_info_t {
|
|||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
} bt3c_info_t;
|
||||
};
|
||||
|
||||
|
||||
static int bt3c_config(struct pcmcia_device *link);
|
||||
|
@ -175,7 +175,7 @@ static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
|
|||
}
|
||||
|
||||
|
||||
static void bt3c_write_wakeup(bt3c_info_t *info)
|
||||
static void bt3c_write_wakeup(struct bt3c_info *info)
|
||||
{
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
|
@ -214,7 +214,7 @@ static void bt3c_write_wakeup(bt3c_info_t *info)
|
|||
}
|
||||
|
||||
|
||||
static void bt3c_receive(bt3c_info_t *info)
|
||||
static void bt3c_receive(struct bt3c_info *info)
|
||||
{
|
||||
unsigned int iobase;
|
||||
int size = 0, avail;
|
||||
|
@ -336,7 +336,7 @@ static void bt3c_receive(bt3c_info_t *info)
|
|||
|
||||
static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
|
||||
{
|
||||
bt3c_info_t *info = dev_inst;
|
||||
struct bt3c_info *info = dev_inst;
|
||||
unsigned int iobase;
|
||||
int iir;
|
||||
irqreturn_t r = IRQ_NONE;
|
||||
|
@ -388,7 +388,7 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
|
|||
|
||||
static int bt3c_hci_flush(struct hci_dev *hdev)
|
||||
{
|
||||
bt3c_info_t *info = hci_get_drvdata(hdev);
|
||||
struct bt3c_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
/* Drop TX queue */
|
||||
skb_queue_purge(&(info->txq));
|
||||
|
@ -418,7 +418,7 @@ static int bt3c_hci_close(struct hci_dev *hdev)
|
|||
|
||||
static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
bt3c_info_t *info = hci_get_drvdata(hdev);
|
||||
struct bt3c_info *info = hci_get_drvdata(hdev);
|
||||
unsigned long flags;
|
||||
|
||||
switch (bt_cb(skb)->pkt_type) {
|
||||
|
@ -451,7 +451,8 @@ static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
/* ======================== Card services HCI interaction ======================== */
|
||||
|
||||
|
||||
static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware,
|
||||
static int bt3c_load_firmware(struct bt3c_info *info,
|
||||
const unsigned char *firmware,
|
||||
int count)
|
||||
{
|
||||
char *ptr = (char *) firmware;
|
||||
|
@ -536,7 +537,7 @@ error:
|
|||
}
|
||||
|
||||
|
||||
static int bt3c_open(bt3c_info_t *info)
|
||||
static int bt3c_open(struct bt3c_info *info)
|
||||
{
|
||||
const struct firmware *firmware;
|
||||
struct hci_dev *hdev;
|
||||
|
@ -603,7 +604,7 @@ error:
|
|||
}
|
||||
|
||||
|
||||
static int bt3c_close(bt3c_info_t *info)
|
||||
static int bt3c_close(struct bt3c_info *info)
|
||||
{
|
||||
struct hci_dev *hdev = info->hdev;
|
||||
|
||||
|
@ -620,7 +621,7 @@ static int bt3c_close(bt3c_info_t *info)
|
|||
|
||||
static int bt3c_probe(struct pcmcia_device *link)
|
||||
{
|
||||
bt3c_info_t *info;
|
||||
struct bt3c_info *info;
|
||||
|
||||
/* Create new info device */
|
||||
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
|
||||
|
@ -683,7 +684,7 @@ static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev,
|
|||
|
||||
static int bt3c_config(struct pcmcia_device *link)
|
||||
{
|
||||
bt3c_info_t *info = link->priv;
|
||||
struct bt3c_info *info = link->priv;
|
||||
int i;
|
||||
unsigned long try;
|
||||
|
||||
|
@ -724,7 +725,7 @@ failed:
|
|||
|
||||
static void bt3c_release(struct pcmcia_device *link)
|
||||
{
|
||||
bt3c_info_t *info = link->priv;
|
||||
struct bt3c_info *info = link->priv;
|
||||
|
||||
bt3c_close(info);
|
||||
|
||||
|
|
|
@ -84,7 +84,27 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = {
|
|||
.int_read_to_clear = false,
|
||||
};
|
||||
|
||||
static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
|
||||
static const struct btmrvl_sdio_card_reg btmrvl_reg_8887 = {
|
||||
.cfg = 0x00,
|
||||
.host_int_mask = 0x08,
|
||||
.host_intstatus = 0x0C,
|
||||
.card_status = 0x5C,
|
||||
.sq_read_base_addr_a0 = 0x6C,
|
||||
.sq_read_base_addr_a1 = 0x6D,
|
||||
.card_revision = 0xC8,
|
||||
.card_fw_status0 = 0x88,
|
||||
.card_fw_status1 = 0x89,
|
||||
.card_rx_len = 0x8A,
|
||||
.card_rx_unit = 0x8B,
|
||||
.io_port_0 = 0xE4,
|
||||
.io_port_1 = 0xE5,
|
||||
.io_port_2 = 0xE6,
|
||||
.int_read_to_clear = true,
|
||||
.host_int_rsr = 0x04,
|
||||
.card_misc_cfg = 0xD8,
|
||||
};
|
||||
|
||||
static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
|
||||
.cfg = 0x00,
|
||||
.host_int_mask = 0x02,
|
||||
.host_intstatus = 0x03,
|
||||
|
@ -128,10 +148,18 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
|
|||
.sd_blksz_fw_dl = 256,
|
||||
};
|
||||
|
||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = {
|
||||
.helper = NULL,
|
||||
.firmware = "mrvl/sd8887_uapsta.bin",
|
||||
.reg = &btmrvl_reg_8887,
|
||||
.support_pscan_win_report = true,
|
||||
.sd_blksz_fw_dl = 256,
|
||||
};
|
||||
|
||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
|
||||
.helper = NULL,
|
||||
.firmware = "mrvl/sd8897_uapsta.bin",
|
||||
.reg = &btmrvl_reg_88xx,
|
||||
.reg = &btmrvl_reg_8897,
|
||||
.support_pscan_win_report = true,
|
||||
.sd_blksz_fw_dl = 256,
|
||||
};
|
||||
|
@ -149,6 +177,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = {
|
|||
/* Marvell SD8797 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
|
||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
|
||||
/* Marvell SD8887 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
|
||||
/* Marvell SD8897 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
|
||||
.driver_data = (unsigned long) &btmrvl_sdio_sd8897 },
|
||||
|
@ -1280,4 +1311,5 @@ MODULE_FIRMWARE("mrvl/sd8688_helper.bin");
|
|||
MODULE_FIRMWARE("mrvl/sd8688.bin");
|
||||
MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
|
||||
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
|
||||
MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin");
|
||||
MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
|
||||
|
|
|
@ -62,7 +62,7 @@ MODULE_LICENSE("GPL");
|
|||
/* ======================== Local structures ======================== */
|
||||
|
||||
|
||||
typedef struct btuart_info_t {
|
||||
struct btuart_info {
|
||||
struct pcmcia_device *p_dev;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
@ -75,7 +75,7 @@ typedef struct btuart_info_t {
|
|||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
} btuart_info_t;
|
||||
};
|
||||
|
||||
|
||||
static int btuart_config(struct pcmcia_device *link);
|
||||
|
@ -127,7 +127,7 @@ static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
|
|||
}
|
||||
|
||||
|
||||
static void btuart_write_wakeup(btuart_info_t *info)
|
||||
static void btuart_write_wakeup(struct btuart_info *info)
|
||||
{
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
|
@ -172,7 +172,7 @@ static void btuart_write_wakeup(btuart_info_t *info)
|
|||
}
|
||||
|
||||
|
||||
static void btuart_receive(btuart_info_t *info)
|
||||
static void btuart_receive(struct btuart_info *info)
|
||||
{
|
||||
unsigned int iobase;
|
||||
int boguscount = 0;
|
||||
|
@ -286,7 +286,7 @@ static void btuart_receive(btuart_info_t *info)
|
|||
|
||||
static irqreturn_t btuart_interrupt(int irq, void *dev_inst)
|
||||
{
|
||||
btuart_info_t *info = dev_inst;
|
||||
struct btuart_info *info = dev_inst;
|
||||
unsigned int iobase;
|
||||
int boguscount = 0;
|
||||
int iir, lsr;
|
||||
|
@ -340,7 +340,8 @@ static irqreturn_t btuart_interrupt(int irq, void *dev_inst)
|
|||
}
|
||||
|
||||
|
||||
static void btuart_change_speed(btuart_info_t *info, unsigned int speed)
|
||||
static void btuart_change_speed(struct btuart_info *info,
|
||||
unsigned int speed)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase;
|
||||
|
@ -397,7 +398,7 @@ static void btuart_change_speed(btuart_info_t *info, unsigned int speed)
|
|||
|
||||
static int btuart_hci_flush(struct hci_dev *hdev)
|
||||
{
|
||||
btuart_info_t *info = hci_get_drvdata(hdev);
|
||||
struct btuart_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
/* Drop TX queue */
|
||||
skb_queue_purge(&(info->txq));
|
||||
|
@ -427,7 +428,7 @@ static int btuart_hci_close(struct hci_dev *hdev)
|
|||
|
||||
static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
btuart_info_t *info = hci_get_drvdata(hdev);
|
||||
struct btuart_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
switch (bt_cb(skb)->pkt_type) {
|
||||
case HCI_COMMAND_PKT:
|
||||
|
@ -455,7 +456,7 @@ static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
/* ======================== Card services HCI interaction ======================== */
|
||||
|
||||
|
||||
static int btuart_open(btuart_info_t *info)
|
||||
static int btuart_open(struct btuart_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
@ -521,7 +522,7 @@ static int btuart_open(btuart_info_t *info)
|
|||
}
|
||||
|
||||
|
||||
static int btuart_close(btuart_info_t *info)
|
||||
static int btuart_close(struct btuart_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
@ -550,7 +551,7 @@ static int btuart_close(btuart_info_t *info)
|
|||
|
||||
static int btuart_probe(struct pcmcia_device *link)
|
||||
{
|
||||
btuart_info_t *info;
|
||||
struct btuart_info *info;
|
||||
|
||||
/* Create new info device */
|
||||
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
|
||||
|
@ -613,7 +614,7 @@ static int btuart_check_config_notpicky(struct pcmcia_device *p_dev,
|
|||
|
||||
static int btuart_config(struct pcmcia_device *link)
|
||||
{
|
||||
btuart_info_t *info = link->priv;
|
||||
struct btuart_info *info = link->priv;
|
||||
int i;
|
||||
int try;
|
||||
|
||||
|
@ -654,7 +655,7 @@ failed:
|
|||
|
||||
static void btuart_release(struct pcmcia_device *link)
|
||||
{
|
||||
btuart_info_t *info = link->priv;
|
||||
struct btuart_info *info = link->priv;
|
||||
|
||||
btuart_close(info);
|
||||
|
||||
|
|
|
@ -165,6 +165,7 @@ static const struct usb_device_id blacklist_table[] = {
|
|||
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
|
||||
|
@ -267,20 +268,24 @@ struct btusb_data {
|
|||
struct usb_interface *intf;
|
||||
struct usb_interface *isoc;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
struct work_struct work;
|
||||
struct work_struct waker;
|
||||
|
||||
struct usb_anchor deferred;
|
||||
struct usb_anchor tx_anchor;
|
||||
int tx_in_flight;
|
||||
spinlock_t txlock;
|
||||
|
||||
struct usb_anchor intr_anchor;
|
||||
struct usb_anchor bulk_anchor;
|
||||
struct usb_anchor isoc_anchor;
|
||||
struct usb_anchor deferred;
|
||||
int tx_in_flight;
|
||||
spinlock_t txlock;
|
||||
spinlock_t rxlock;
|
||||
|
||||
struct sk_buff *evt_skb;
|
||||
struct sk_buff *acl_skb;
|
||||
struct sk_buff *sco_skb;
|
||||
|
||||
struct usb_endpoint_descriptor *intr_ep;
|
||||
struct usb_endpoint_descriptor *bulk_tx_ep;
|
||||
|
@ -295,18 +300,189 @@ struct btusb_data {
|
|||
int suspend_count;
|
||||
};
|
||||
|
||||
static int inc_tx(struct btusb_data *data)
|
||||
static inline void btusb_free_frags(struct btusb_data *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
int rv;
|
||||
|
||||
spin_lock_irqsave(&data->txlock, flags);
|
||||
rv = test_bit(BTUSB_SUSPENDING, &data->flags);
|
||||
if (!rv)
|
||||
data->tx_in_flight++;
|
||||
spin_unlock_irqrestore(&data->txlock, flags);
|
||||
spin_lock_irqsave(&data->rxlock, flags);
|
||||
|
||||
return rv;
|
||||
kfree_skb(data->evt_skb);
|
||||
data->evt_skb = NULL;
|
||||
|
||||
kfree_skb(data->acl_skb);
|
||||
data->acl_skb = NULL;
|
||||
|
||||
kfree_skb(data->sco_skb);
|
||||
data->sco_skb = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&data->rxlock, flags);
|
||||
}
|
||||
|
||||
static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err = 0;
|
||||
|
||||
spin_lock(&data->rxlock);
|
||||
skb = data->evt_skb;
|
||||
|
||||
while (count) {
|
||||
int len;
|
||||
|
||||
if (!skb) {
|
||||
skb = bt_skb_alloc(HCI_MAX_EVENT_SIZE, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
|
||||
bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE;
|
||||
}
|
||||
|
||||
len = min_t(uint, bt_cb(skb)->expect, count);
|
||||
memcpy(skb_put(skb, len), buffer, len);
|
||||
|
||||
count -= len;
|
||||
buffer += len;
|
||||
bt_cb(skb)->expect -= len;
|
||||
|
||||
if (skb->len == HCI_EVENT_HDR_SIZE) {
|
||||
/* Complete event header */
|
||||
bt_cb(skb)->expect = hci_event_hdr(skb)->plen;
|
||||
|
||||
if (skb_tailroom(skb) < bt_cb(skb)->expect) {
|
||||
kfree_skb(skb);
|
||||
skb = NULL;
|
||||
|
||||
err = -EILSEQ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_cb(skb)->expect == 0) {
|
||||
/* Complete frame */
|
||||
hci_recv_frame(data->hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
data->evt_skb = skb;
|
||||
spin_unlock(&data->rxlock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err = 0;
|
||||
|
||||
spin_lock(&data->rxlock);
|
||||
skb = data->acl_skb;
|
||||
|
||||
while (count) {
|
||||
int len;
|
||||
|
||||
if (!skb) {
|
||||
skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
|
||||
bt_cb(skb)->expect = HCI_ACL_HDR_SIZE;
|
||||
}
|
||||
|
||||
len = min_t(uint, bt_cb(skb)->expect, count);
|
||||
memcpy(skb_put(skb, len), buffer, len);
|
||||
|
||||
count -= len;
|
||||
buffer += len;
|
||||
bt_cb(skb)->expect -= len;
|
||||
|
||||
if (skb->len == HCI_ACL_HDR_SIZE) {
|
||||
__le16 dlen = hci_acl_hdr(skb)->dlen;
|
||||
|
||||
/* Complete ACL header */
|
||||
bt_cb(skb)->expect = __le16_to_cpu(dlen);
|
||||
|
||||
if (skb_tailroom(skb) < bt_cb(skb)->expect) {
|
||||
kfree_skb(skb);
|
||||
skb = NULL;
|
||||
|
||||
err = -EILSEQ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_cb(skb)->expect == 0) {
|
||||
/* Complete frame */
|
||||
hci_recv_frame(data->hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
data->acl_skb = skb;
|
||||
spin_unlock(&data->rxlock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int err = 0;
|
||||
|
||||
spin_lock(&data->rxlock);
|
||||
skb = data->sco_skb;
|
||||
|
||||
while (count) {
|
||||
int len;
|
||||
|
||||
if (!skb) {
|
||||
skb = bt_skb_alloc(HCI_MAX_SCO_SIZE, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
|
||||
bt_cb(skb)->expect = HCI_SCO_HDR_SIZE;
|
||||
}
|
||||
|
||||
len = min_t(uint, bt_cb(skb)->expect, count);
|
||||
memcpy(skb_put(skb, len), buffer, len);
|
||||
|
||||
count -= len;
|
||||
buffer += len;
|
||||
bt_cb(skb)->expect -= len;
|
||||
|
||||
if (skb->len == HCI_SCO_HDR_SIZE) {
|
||||
/* Complete SCO header */
|
||||
bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen;
|
||||
|
||||
if (skb_tailroom(skb) < bt_cb(skb)->expect) {
|
||||
kfree_skb(skb);
|
||||
skb = NULL;
|
||||
|
||||
err = -EILSEQ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_cb(skb)->expect == 0) {
|
||||
/* Complete frame */
|
||||
hci_recv_frame(data->hdev, skb);
|
||||
skb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
data->sco_skb = skb;
|
||||
spin_unlock(&data->rxlock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void btusb_intr_complete(struct urb *urb)
|
||||
|
@ -315,8 +491,8 @@ static void btusb_intr_complete(struct urb *urb)
|
|||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
int err;
|
||||
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
||||
urb, urb->status, urb->actual_length);
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
|
||||
urb->actual_length);
|
||||
|
||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||
return;
|
||||
|
@ -324,12 +500,14 @@ static void btusb_intr_complete(struct urb *urb)
|
|||
if (urb->status == 0) {
|
||||
hdev->stat.byte_rx += urb->actual_length;
|
||||
|
||||
if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
|
||||
urb->transfer_buffer,
|
||||
urb->actual_length) < 0) {
|
||||
if (btusb_recv_intr(data, urb->transfer_buffer,
|
||||
urb->actual_length) < 0) {
|
||||
BT_ERR("%s corrupted event packet", hdev->name);
|
||||
hdev->stat.err_rx++;
|
||||
}
|
||||
} else if (urb->status == -ENOENT) {
|
||||
/* Avoid suspend failed when usb_kill_urb */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
|
||||
|
@ -344,7 +522,7 @@ static void btusb_intr_complete(struct urb *urb)
|
|||
* -ENODEV: device got disconnected */
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p failed to resubmit (%d)",
|
||||
hdev->name, urb, -err);
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
}
|
||||
|
@ -377,8 +555,7 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
|
||||
|
||||
usb_fill_int_urb(urb, data->udev, pipe, buf, size,
|
||||
btusb_intr_complete, hdev,
|
||||
data->intr_ep->bInterval);
|
||||
btusb_intr_complete, hdev, data->intr_ep->bInterval);
|
||||
|
||||
urb->transfer_flags |= URB_FREE_BUFFER;
|
||||
|
||||
|
@ -388,7 +565,7 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
if (err < 0) {
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
||||
|
@ -403,8 +580,8 @@ static void btusb_bulk_complete(struct urb *urb)
|
|||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
int err;
|
||||
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
||||
urb, urb->status, urb->actual_length);
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
|
||||
urb->actual_length);
|
||||
|
||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||
return;
|
||||
|
@ -412,12 +589,14 @@ static void btusb_bulk_complete(struct urb *urb)
|
|||
if (urb->status == 0) {
|
||||
hdev->stat.byte_rx += urb->actual_length;
|
||||
|
||||
if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
|
||||
urb->transfer_buffer,
|
||||
urb->actual_length) < 0) {
|
||||
if (btusb_recv_bulk(data, urb->transfer_buffer,
|
||||
urb->actual_length) < 0) {
|
||||
BT_ERR("%s corrupted ACL packet", hdev->name);
|
||||
hdev->stat.err_rx++;
|
||||
}
|
||||
} else if (urb->status == -ENOENT) {
|
||||
/* Avoid suspend failed when usb_kill_urb */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
|
||||
|
@ -432,7 +611,7 @@ static void btusb_bulk_complete(struct urb *urb)
|
|||
* -ENODEV: device got disconnected */
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p failed to resubmit (%d)",
|
||||
hdev->name, urb, -err);
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
}
|
||||
|
@ -462,8 +641,8 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
|
||||
pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
|
||||
|
||||
usb_fill_bulk_urb(urb, data->udev, pipe,
|
||||
buf, size, btusb_bulk_complete, hdev);
|
||||
usb_fill_bulk_urb(urb, data->udev, pipe, buf, size,
|
||||
btusb_bulk_complete, hdev);
|
||||
|
||||
urb->transfer_flags |= URB_FREE_BUFFER;
|
||||
|
||||
|
@ -474,7 +653,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
if (err < 0) {
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
||||
|
@ -489,8 +668,8 @@ static void btusb_isoc_complete(struct urb *urb)
|
|||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
int i, err;
|
||||
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
||||
urb, urb->status, urb->actual_length);
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
|
||||
urb->actual_length);
|
||||
|
||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||
return;
|
||||
|
@ -505,13 +684,15 @@ static void btusb_isoc_complete(struct urb *urb)
|
|||
|
||||
hdev->stat.byte_rx += length;
|
||||
|
||||
if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
|
||||
urb->transfer_buffer + offset,
|
||||
length) < 0) {
|
||||
if (btusb_recv_isoc(data, urb->transfer_buffer + offset,
|
||||
length) < 0) {
|
||||
BT_ERR("%s corrupted SCO packet", hdev->name);
|
||||
hdev->stat.err_rx++;
|
||||
}
|
||||
}
|
||||
} else if (urb->status == -ENOENT) {
|
||||
/* Avoid suspend failed when usb_kill_urb */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
|
||||
|
@ -525,7 +706,7 @@ static void btusb_isoc_complete(struct urb *urb)
|
|||
* -ENODEV: device got disconnected */
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p failed to resubmit (%d)",
|
||||
hdev->name, urb, -err);
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
}
|
||||
|
@ -580,12 +761,12 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
|
||||
|
||||
usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_isoc_complete,
|
||||
hdev, data->isoc_rx_ep->bInterval);
|
||||
hdev, data->isoc_rx_ep->bInterval);
|
||||
|
||||
urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
|
||||
urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
|
||||
|
||||
__fill_isoc_descriptor(urb, size,
|
||||
le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
|
||||
le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
|
||||
|
||||
usb_anchor_urb(urb, &data->isoc_anchor);
|
||||
|
||||
|
@ -593,7 +774,7 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
if (err < 0) {
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
hdev->name, urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
||||
|
@ -605,11 +786,11 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
static void btusb_tx_complete(struct urb *urb)
|
||||
{
|
||||
struct sk_buff *skb = urb->context;
|
||||
struct hci_dev *hdev = (struct hci_dev *) skb->dev;
|
||||
struct hci_dev *hdev = (struct hci_dev *)skb->dev;
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
||||
urb, urb->status, urb->actual_length);
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
|
||||
urb->actual_length);
|
||||
|
||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||
goto done;
|
||||
|
@ -632,10 +813,10 @@ done:
|
|||
static void btusb_isoc_tx_complete(struct urb *urb)
|
||||
{
|
||||
struct sk_buff *skb = urb->context;
|
||||
struct hci_dev *hdev = (struct hci_dev *) skb->dev;
|
||||
struct hci_dev *hdev = (struct hci_dev *)skb->dev;
|
||||
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name,
|
||||
urb, urb->status, urb->actual_length);
|
||||
BT_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
|
||||
urb->actual_length);
|
||||
|
||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||
goto done;
|
||||
|
@ -719,6 +900,8 @@ static int btusb_close(struct hci_dev *hdev)
|
|||
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
||||
|
||||
btusb_stop_traffic(data);
|
||||
btusb_free_frags(data);
|
||||
|
||||
err = usb_autopm_get_interface(data->intf);
|
||||
if (err < 0)
|
||||
goto failed;
|
||||
|
@ -738,122 +921,181 @@ static int btusb_flush(struct hci_dev *hdev)
|
|||
BT_DBG("%s", hdev->name);
|
||||
|
||||
usb_kill_anchored_urbs(&data->tx_anchor);
|
||||
btusb_free_frags(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
static struct urb *alloc_ctrl_urb(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct usb_ctrlrequest *dr;
|
||||
struct urb *urb;
|
||||
unsigned int pipe;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dr = kmalloc(sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr) {
|
||||
usb_free_urb(urb);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
dr->bRequestType = data->cmdreq_type;
|
||||
dr->bRequest = 0;
|
||||
dr->wIndex = 0;
|
||||
dr->wValue = 0;
|
||||
dr->wLength = __cpu_to_le16(skb->len);
|
||||
|
||||
pipe = usb_sndctrlpipe(data->udev, 0x00);
|
||||
|
||||
usb_fill_control_urb(urb, data->udev, pipe, (void *)dr,
|
||||
skb->data, skb->len, btusb_tx_complete, skb);
|
||||
|
||||
skb->dev = (void *)hdev;
|
||||
|
||||
return urb;
|
||||
}
|
||||
|
||||
static struct urb *alloc_bulk_urb(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct urb *urb;
|
||||
unsigned int pipe;
|
||||
|
||||
if (!data->bulk_tx_ep)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pipe = usb_sndbulkpipe(data->udev, data->bulk_tx_ep->bEndpointAddress);
|
||||
|
||||
usb_fill_bulk_urb(urb, data->udev, pipe,
|
||||
skb->data, skb->len, btusb_tx_complete, skb);
|
||||
|
||||
skb->dev = (void *)hdev;
|
||||
|
||||
return urb;
|
||||
}
|
||||
|
||||
static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct urb *urb;
|
||||
unsigned int pipe;
|
||||
|
||||
if (!data->isoc_tx_ep)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress);
|
||||
|
||||
usb_fill_int_urb(urb, data->udev, pipe,
|
||||
skb->data, skb->len, btusb_isoc_tx_complete,
|
||||
skb, data->isoc_tx_ep->bInterval);
|
||||
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
|
||||
__fill_isoc_descriptor(urb, skb->len,
|
||||
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
|
||||
|
||||
skb->dev = (void *)hdev;
|
||||
|
||||
return urb;
|
||||
}
|
||||
|
||||
static int submit_tx_urb(struct hci_dev *hdev, struct urb *urb)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||
return -EBUSY;
|
||||
|
||||
skb->dev = (void *) hdev;
|
||||
|
||||
switch (bt_cb(skb)->pkt_type) {
|
||||
case HCI_COMMAND_PKT:
|
||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
|
||||
dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
|
||||
if (!dr) {
|
||||
usb_free_urb(urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dr->bRequestType = data->cmdreq_type;
|
||||
dr->bRequest = 0;
|
||||
dr->wIndex = 0;
|
||||
dr->wValue = 0;
|
||||
dr->wLength = __cpu_to_le16(skb->len);
|
||||
|
||||
pipe = usb_sndctrlpipe(data->udev, 0x00);
|
||||
|
||||
usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
|
||||
skb->data, skb->len, btusb_tx_complete, skb);
|
||||
|
||||
hdev->stat.cmd_tx++;
|
||||
break;
|
||||
|
||||
case HCI_ACLDATA_PKT:
|
||||
if (!data->bulk_tx_ep)
|
||||
return -ENODEV;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
|
||||
pipe = usb_sndbulkpipe(data->udev,
|
||||
data->bulk_tx_ep->bEndpointAddress);
|
||||
|
||||
usb_fill_bulk_urb(urb, data->udev, pipe,
|
||||
skb->data, skb->len, btusb_tx_complete, skb);
|
||||
|
||||
hdev->stat.acl_tx++;
|
||||
break;
|
||||
|
||||
case HCI_SCODATA_PKT:
|
||||
if (!data->isoc_tx_ep || hci_conn_num(hdev, SCO_LINK) < 1)
|
||||
return -ENODEV;
|
||||
|
||||
urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
|
||||
pipe = usb_sndisocpipe(data->udev,
|
||||
data->isoc_tx_ep->bEndpointAddress);
|
||||
|
||||
usb_fill_int_urb(urb, data->udev, pipe,
|
||||
skb->data, skb->len, btusb_isoc_tx_complete,
|
||||
skb, data->isoc_tx_ep->bInterval);
|
||||
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
|
||||
__fill_isoc_descriptor(urb, skb->len,
|
||||
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
|
||||
|
||||
hdev->stat.sco_tx++;
|
||||
goto skip_waking;
|
||||
|
||||
default:
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
err = inc_tx(data);
|
||||
if (err) {
|
||||
usb_anchor_urb(urb, &data->deferred);
|
||||
schedule_work(&data->waker);
|
||||
err = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
skip_waking:
|
||||
usb_anchor_urb(urb, &data->tx_anchor);
|
||||
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
err = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (err < 0) {
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
BT_ERR("%s urb %p submission failed (%d)",
|
||||
hdev->name, urb, -err);
|
||||
hdev->name, urb, -err);
|
||||
kfree(urb->setup_packet);
|
||||
usb_unanchor_urb(urb);
|
||||
} else {
|
||||
usb_mark_last_busy(data->udev);
|
||||
}
|
||||
|
||||
done:
|
||||
usb_free_urb(urb);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int submit_or_queue_tx_urb(struct hci_dev *hdev, struct urb *urb)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
unsigned long flags;
|
||||
bool suspending;
|
||||
|
||||
spin_lock_irqsave(&data->txlock, flags);
|
||||
suspending = test_bit(BTUSB_SUSPENDING, &data->flags);
|
||||
if (!suspending)
|
||||
data->tx_in_flight++;
|
||||
spin_unlock_irqrestore(&data->txlock, flags);
|
||||
|
||||
if (!suspending)
|
||||
return submit_tx_urb(hdev, urb);
|
||||
|
||||
usb_anchor_urb(urb, &data->deferred);
|
||||
schedule_work(&data->waker);
|
||||
|
||||
usb_free_urb(urb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct urb *urb;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||
return -EBUSY;
|
||||
|
||||
switch (bt_cb(skb)->pkt_type) {
|
||||
case HCI_COMMAND_PKT:
|
||||
urb = alloc_ctrl_urb(hdev, skb);
|
||||
if (IS_ERR(urb))
|
||||
return PTR_ERR(urb);
|
||||
|
||||
hdev->stat.cmd_tx++;
|
||||
return submit_or_queue_tx_urb(hdev, urb);
|
||||
|
||||
case HCI_ACLDATA_PKT:
|
||||
urb = alloc_bulk_urb(hdev, skb);
|
||||
if (IS_ERR(urb))
|
||||
return PTR_ERR(urb);
|
||||
|
||||
hdev->stat.acl_tx++;
|
||||
return submit_or_queue_tx_urb(hdev, urb);
|
||||
|
||||
case HCI_SCODATA_PKT:
|
||||
if (hci_conn_num(hdev, SCO_LINK) < 1)
|
||||
return -ENODEV;
|
||||
|
||||
urb = alloc_isoc_urb(hdev, skb);
|
||||
if (IS_ERR(urb))
|
||||
return PTR_ERR(urb);
|
||||
|
||||
hdev->stat.sco_tx++;
|
||||
return submit_tx_urb(hdev, urb);
|
||||
}
|
||||
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
|
@ -930,6 +1172,7 @@ static void btusb_work(struct work_struct *work)
|
|||
|
||||
if (hdev->voice_setting & 0x0020) {
|
||||
static const int alts[3] = { 2, 4, 5 };
|
||||
|
||||
new_alts = alts[data->sco_num - 1];
|
||||
} else {
|
||||
new_alts = data->sco_num;
|
||||
|
@ -1002,7 +1245,7 @@ static int btusb_setup_csr(struct hci_dev *hdev)
|
|||
return -PTR_ERR(skb);
|
||||
}
|
||||
|
||||
rp = (struct hci_rp_read_local_version *) skb->data;
|
||||
rp = (struct hci_rp_read_local_version *)skb->data;
|
||||
|
||||
if (!rp->status) {
|
||||
if (le16_to_cpu(rp->manufacturer) != 10) {
|
||||
|
@ -1040,7 +1283,7 @@ struct intel_version {
|
|||
} __packed;
|
||||
|
||||
static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
|
||||
struct intel_version *ver)
|
||||
struct intel_version *ver)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
char fwname[64];
|
||||
|
@ -1216,7 +1459,7 @@ static int btusb_check_bdaddr_intel(struct hci_dev *hdev)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
rp = (struct hci_rp_read_bd_addr *) skb->data;
|
||||
rp = (struct hci_rp_read_bd_addr *)skb->data;
|
||||
if (rp->status) {
|
||||
BT_ERR("%s Intel device address result failed (%02x)",
|
||||
hdev->name, rp->status);
|
||||
|
@ -1346,6 +1589,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
|
|||
|
||||
if (skb->data[0]) {
|
||||
u8 evt_status = skb->data[0];
|
||||
|
||||
BT_ERR("%s enable Intel manufacturer mode event failed (%02x)",
|
||||
hdev->name, evt_status);
|
||||
kfree_skb(skb);
|
||||
|
@ -1455,7 +1699,7 @@ static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
|||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: changing Intel device address failed (%ld)",
|
||||
hdev->name, ret);
|
||||
hdev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
@ -1530,19 +1774,19 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
|||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
|
||||
hdev->name, ret);
|
||||
hdev->name, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*ver)) {
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
|
||||
hdev->name);
|
||||
hdev->name);
|
||||
kfree_skb(skb);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ver = (struct hci_rp_read_local_version *) skb->data;
|
||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
||||
BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
|
||||
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
|
||||
ver->lmp_ver, ver->lmp_subver);
|
||||
|
@ -1553,7 +1797,7 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
|||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: BCM: Download Minidrv command failed (%ld)",
|
||||
hdev->name, ret);
|
||||
hdev->name, ret);
|
||||
goto reset_fw;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
@ -1565,13 +1809,13 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
|||
fw_size = fw->size;
|
||||
|
||||
while (fw_size >= sizeof(*cmd)) {
|
||||
cmd = (struct hci_command_hdr *) fw_ptr;
|
||||
cmd = (struct hci_command_hdr *)fw_ptr;
|
||||
fw_ptr += sizeof(*cmd);
|
||||
fw_size -= sizeof(*cmd);
|
||||
|
||||
if (fw_size < cmd->plen) {
|
||||
BT_ERR("%s: BCM: patch %s is corrupted",
|
||||
hdev->name, fw_name);
|
||||
hdev->name, fw_name);
|
||||
ret = -EINVAL;
|
||||
goto reset_fw;
|
||||
}
|
||||
|
@ -1587,7 +1831,7 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
|
|||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: BCM: patch command %04x failed (%ld)",
|
||||
hdev->name, opcode, ret);
|
||||
hdev->name, opcode, ret);
|
||||
goto reset_fw;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
@ -1612,19 +1856,19 @@ reset_fw:
|
|||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
|
||||
hdev->name, ret);
|
||||
hdev->name, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*ver)) {
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
|
||||
hdev->name);
|
||||
hdev->name);
|
||||
kfree_skb(skb);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ver = (struct hci_rp_read_local_version *) skb->data;
|
||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
||||
BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
|
||||
"lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev,
|
||||
ver->lmp_ver, ver->lmp_subver);
|
||||
|
@ -1636,19 +1880,19 @@ reset_fw:
|
|||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: HCI_OP_READ_BD_ADDR failed (%ld)",
|
||||
hdev->name, ret);
|
||||
hdev->name, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*bda)) {
|
||||
BT_ERR("%s: HCI_OP_READ_BD_ADDR event length mismatch",
|
||||
hdev->name);
|
||||
hdev->name);
|
||||
kfree_skb(skb);
|
||||
ret = -EIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
bda = (struct hci_rp_read_bd_addr *) skb->data;
|
||||
bda = (struct hci_rp_read_bd_addr *)skb->data;
|
||||
if (bda->status) {
|
||||
BT_ERR("%s: HCI_OP_READ_BD_ADDR error status (%02x)",
|
||||
hdev->name, bda->status);
|
||||
|
@ -1683,7 +1927,7 @@ static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
|||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: BCM: Change address command failed (%ld)",
|
||||
hdev->name, ret);
|
||||
hdev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
@ -1692,7 +1936,7 @@ static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
|||
}
|
||||
|
||||
static int btusb_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_endpoint_descriptor *ep_desc;
|
||||
struct btusb_data *data;
|
||||
|
@ -1707,6 +1951,7 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
|
||||
if (!id->driver_info) {
|
||||
const struct usb_device_id *match;
|
||||
|
||||
match = usb_match_id(intf, blacklist_table);
|
||||
if (match)
|
||||
id = match;
|
||||
|
@ -1755,17 +2000,16 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
data->udev = interface_to_usbdev(intf);
|
||||
data->intf = intf;
|
||||
|
||||
spin_lock_init(&data->lock);
|
||||
|
||||
INIT_WORK(&data->work, btusb_work);
|
||||
INIT_WORK(&data->waker, btusb_waker);
|
||||
init_usb_anchor(&data->deferred);
|
||||
init_usb_anchor(&data->tx_anchor);
|
||||
spin_lock_init(&data->txlock);
|
||||
|
||||
init_usb_anchor(&data->tx_anchor);
|
||||
init_usb_anchor(&data->intr_anchor);
|
||||
init_usb_anchor(&data->bulk_anchor);
|
||||
init_usb_anchor(&data->isoc_anchor);
|
||||
init_usb_anchor(&data->deferred);
|
||||
spin_lock_init(&data->rxlock);
|
||||
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev)
|
||||
|
@ -1857,7 +2101,7 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
|
||||
if (data->isoc) {
|
||||
err = usb_driver_claim_interface(&btusb_driver,
|
||||
data->isoc, data);
|
||||
data->isoc, data);
|
||||
if (err < 0) {
|
||||
hci_free_dev(hdev);
|
||||
return err;
|
||||
|
@ -1898,6 +2142,7 @@ static void btusb_disconnect(struct usb_interface *intf)
|
|||
else if (data->isoc)
|
||||
usb_driver_release_interface(&btusb_driver, data->isoc);
|
||||
|
||||
btusb_free_frags(data);
|
||||
hci_free_dev(hdev);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ MODULE_LICENSE("GPL");
|
|||
/* ======================== Local structures ======================== */
|
||||
|
||||
|
||||
typedef struct dtl1_info_t {
|
||||
struct dtl1_info {
|
||||
struct pcmcia_device *p_dev;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
|
@ -78,7 +78,7 @@ typedef struct dtl1_info_t {
|
|||
unsigned long rx_state;
|
||||
unsigned long rx_count;
|
||||
struct sk_buff *rx_skb;
|
||||
} dtl1_info_t;
|
||||
};
|
||||
|
||||
|
||||
static int dtl1_config(struct pcmcia_device *link);
|
||||
|
@ -94,11 +94,11 @@ static int dtl1_config(struct pcmcia_device *link);
|
|||
#define RECV_WAIT_DATA 1
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct nsh {
|
||||
u8 type;
|
||||
u8 zero;
|
||||
u16 len;
|
||||
} __packed nsh_t; /* Nokia Specific Header */
|
||||
} __packed; /* Nokia Specific Header */
|
||||
|
||||
#define NSHL 4 /* Nokia Specific Header Length */
|
||||
|
||||
|
@ -126,7 +126,7 @@ static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
|
|||
}
|
||||
|
||||
|
||||
static void dtl1_write_wakeup(dtl1_info_t *info)
|
||||
static void dtl1_write_wakeup(struct dtl1_info *info)
|
||||
{
|
||||
if (!info) {
|
||||
BT_ERR("Unknown device");
|
||||
|
@ -176,7 +176,7 @@ static void dtl1_write_wakeup(dtl1_info_t *info)
|
|||
}
|
||||
|
||||
|
||||
static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb)
|
||||
static void dtl1_control(struct dtl1_info *info, struct sk_buff *skb)
|
||||
{
|
||||
u8 flowmask = *(u8 *)skb->data;
|
||||
int i;
|
||||
|
@ -199,10 +199,10 @@ static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
|
||||
static void dtl1_receive(dtl1_info_t *info)
|
||||
static void dtl1_receive(struct dtl1_info *info)
|
||||
{
|
||||
unsigned int iobase;
|
||||
nsh_t *nsh;
|
||||
struct nsh *nsh;
|
||||
int boguscount = 0;
|
||||
|
||||
if (!info) {
|
||||
|
@ -227,7 +227,7 @@ static void dtl1_receive(dtl1_info_t *info)
|
|||
}
|
||||
|
||||
*skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
|
||||
nsh = (nsh_t *)info->rx_skb->data;
|
||||
nsh = (struct nsh *)info->rx_skb->data;
|
||||
|
||||
info->rx_count--;
|
||||
|
||||
|
@ -287,7 +287,7 @@ static void dtl1_receive(dtl1_info_t *info)
|
|||
|
||||
static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
|
||||
{
|
||||
dtl1_info_t *info = dev_inst;
|
||||
struct dtl1_info *info = dev_inst;
|
||||
unsigned int iobase;
|
||||
unsigned char msr;
|
||||
int boguscount = 0;
|
||||
|
@ -365,7 +365,7 @@ static int dtl1_hci_open(struct hci_dev *hdev)
|
|||
|
||||
static int dtl1_hci_flush(struct hci_dev *hdev)
|
||||
{
|
||||
dtl1_info_t *info = hci_get_drvdata(hdev);
|
||||
struct dtl1_info *info = hci_get_drvdata(hdev);
|
||||
|
||||
/* Drop TX queue */
|
||||
skb_queue_purge(&(info->txq));
|
||||
|
@ -387,9 +387,9 @@ static int dtl1_hci_close(struct hci_dev *hdev)
|
|||
|
||||
static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
dtl1_info_t *info = hci_get_drvdata(hdev);
|
||||
struct dtl1_info *info = hci_get_drvdata(hdev);
|
||||
struct sk_buff *s;
|
||||
nsh_t nsh;
|
||||
struct nsh nsh;
|
||||
|
||||
switch (bt_cb(skb)->pkt_type) {
|
||||
case HCI_COMMAND_PKT:
|
||||
|
@ -436,7 +436,7 @@ static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
/* ======================== Card services HCI interaction ======================== */
|
||||
|
||||
|
||||
static int dtl1_open(dtl1_info_t *info)
|
||||
static int dtl1_open(struct dtl1_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
@ -505,7 +505,7 @@ static int dtl1_open(dtl1_info_t *info)
|
|||
}
|
||||
|
||||
|
||||
static int dtl1_close(dtl1_info_t *info)
|
||||
static int dtl1_close(struct dtl1_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int iobase = info->p_dev->resource[0]->start;
|
||||
|
@ -534,7 +534,7 @@ static int dtl1_close(dtl1_info_t *info)
|
|||
|
||||
static int dtl1_probe(struct pcmcia_device *link)
|
||||
{
|
||||
dtl1_info_t *info;
|
||||
struct dtl1_info *info;
|
||||
|
||||
/* Create new info device */
|
||||
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
|
||||
|
@ -552,7 +552,7 @@ static int dtl1_probe(struct pcmcia_device *link)
|
|||
|
||||
static void dtl1_detach(struct pcmcia_device *link)
|
||||
{
|
||||
dtl1_info_t *info = link->priv;
|
||||
struct dtl1_info *info = link->priv;
|
||||
|
||||
dtl1_close(info);
|
||||
pcmcia_disable_device(link);
|
||||
|
@ -571,7 +571,7 @@ static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data)
|
|||
|
||||
static int dtl1_config(struct pcmcia_device *link)
|
||||
{
|
||||
dtl1_info_t *info = link->priv;
|
||||
struct dtl1_info *info = link->priv;
|
||||
int ret;
|
||||
|
||||
/* Look for a generic full-sized window */
|
||||
|
|
|
@ -237,7 +237,7 @@ static void h5_pkt_cull(struct h5 *h5)
|
|||
break;
|
||||
|
||||
to_remove--;
|
||||
seq = (seq - 1) % 8;
|
||||
seq = (seq - 1) & 0x07;
|
||||
}
|
||||
|
||||
if (seq != h5->rx_ack)
|
||||
|
|
|
@ -752,7 +752,7 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
|
|||
return ERR_PTR(-EINVAL);
|
||||
|
||||
entries = roundup_pow_of_two(entries + 1);
|
||||
if (entries > dev->mdev->caps.max_cqes)
|
||||
if (entries > dev->mdev->caps.gen.max_cqes)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
cq = kzalloc(sizeof(*cq), GFP_KERNEL);
|
||||
|
@ -919,7 +919,7 @@ int mlx5_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period)
|
|||
int err;
|
||||
u32 fsel;
|
||||
|
||||
if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_CQ_MODER))
|
||||
if (!(dev->mdev->caps.gen.flags & MLX5_DEV_CAP_FLAG_CQ_MODER))
|
||||
return -ENOSYS;
|
||||
|
||||
in = kzalloc(sizeof(*in), GFP_KERNEL);
|
||||
|
@ -1074,7 +1074,7 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata)
|
|||
int uninitialized_var(cqe_size);
|
||||
unsigned long flags;
|
||||
|
||||
if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_RESIZE_CQ)) {
|
||||
if (!(dev->mdev->caps.gen.flags & MLX5_DEV_CAP_FLAG_RESIZE_CQ)) {
|
||||
pr_info("Firmware does not support resize CQ\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
@ -1083,7 +1083,7 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata)
|
|||
return -EINVAL;
|
||||
|
||||
entries = roundup_pow_of_two(entries + 1);
|
||||
if (entries > dev->mdev->caps.max_cqes + 1)
|
||||
if (entries > dev->mdev->caps.gen.max_cqes + 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (entries == ibcq->cqe + 1)
|
||||
|
|
|
@ -129,7 +129,7 @@ int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, u8 port)
|
|||
|
||||
packet_error = be16_to_cpu(out_mad->status);
|
||||
|
||||
dev->mdev->caps.ext_port_cap[port - 1] = (!err && !packet_error) ?
|
||||
dev->mdev->caps.gen.ext_port_cap[port - 1] = (!err && !packet_error) ?
|
||||
MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO : 0;
|
||||
|
||||
out:
|
||||
|
|
|
@ -157,11 +157,13 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
|
|||
struct mlx5_ib_dev *dev = to_mdev(ibdev);
|
||||
struct ib_smp *in_mad = NULL;
|
||||
struct ib_smp *out_mad = NULL;
|
||||
struct mlx5_general_caps *gen;
|
||||
int err = -ENOMEM;
|
||||
int max_rq_sg;
|
||||
int max_sq_sg;
|
||||
u64 flags;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL);
|
||||
out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
|
||||
if (!in_mad || !out_mad)
|
||||
|
@ -183,7 +185,7 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
|
|||
IB_DEVICE_PORT_ACTIVE_EVENT |
|
||||
IB_DEVICE_SYS_IMAGE_GUID |
|
||||
IB_DEVICE_RC_RNR_NAK_GEN;
|
||||
flags = dev->mdev->caps.flags;
|
||||
flags = gen->flags;
|
||||
if (flags & MLX5_DEV_CAP_FLAG_BAD_PKEY_CNTR)
|
||||
props->device_cap_flags |= IB_DEVICE_BAD_PKEY_CNTR;
|
||||
if (flags & MLX5_DEV_CAP_FLAG_BAD_QKEY_CNTR)
|
||||
|
@ -213,30 +215,31 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
|
|||
memcpy(&props->sys_image_guid, out_mad->data + 4, 8);
|
||||
|
||||
props->max_mr_size = ~0ull;
|
||||
props->page_size_cap = dev->mdev->caps.min_page_sz;
|
||||
props->max_qp = 1 << dev->mdev->caps.log_max_qp;
|
||||
props->max_qp_wr = dev->mdev->caps.max_wqes;
|
||||
max_rq_sg = dev->mdev->caps.max_rq_desc_sz / sizeof(struct mlx5_wqe_data_seg);
|
||||
max_sq_sg = (dev->mdev->caps.max_sq_desc_sz - sizeof(struct mlx5_wqe_ctrl_seg)) /
|
||||
props->page_size_cap = gen->min_page_sz;
|
||||
props->max_qp = 1 << gen->log_max_qp;
|
||||
props->max_qp_wr = gen->max_wqes;
|
||||
max_rq_sg = gen->max_rq_desc_sz / sizeof(struct mlx5_wqe_data_seg);
|
||||
max_sq_sg = (gen->max_sq_desc_sz - sizeof(struct mlx5_wqe_ctrl_seg)) /
|
||||
sizeof(struct mlx5_wqe_data_seg);
|
||||
props->max_sge = min(max_rq_sg, max_sq_sg);
|
||||
props->max_cq = 1 << dev->mdev->caps.log_max_cq;
|
||||
props->max_cqe = dev->mdev->caps.max_cqes - 1;
|
||||
props->max_mr = 1 << dev->mdev->caps.log_max_mkey;
|
||||
props->max_pd = 1 << dev->mdev->caps.log_max_pd;
|
||||
props->max_qp_rd_atom = dev->mdev->caps.max_ra_req_qp;
|
||||
props->max_qp_init_rd_atom = dev->mdev->caps.max_ra_res_qp;
|
||||
props->max_cq = 1 << gen->log_max_cq;
|
||||
props->max_cqe = gen->max_cqes - 1;
|
||||
props->max_mr = 1 << gen->log_max_mkey;
|
||||
props->max_pd = 1 << gen->log_max_pd;
|
||||
props->max_qp_rd_atom = 1 << gen->log_max_ra_req_qp;
|
||||
props->max_qp_init_rd_atom = 1 << gen->log_max_ra_res_qp;
|
||||
props->max_srq = 1 << gen->log_max_srq;
|
||||
props->max_srq_wr = gen->max_srq_wqes - 1;
|
||||
props->local_ca_ack_delay = gen->local_ca_ack_delay;
|
||||
props->max_res_rd_atom = props->max_qp_rd_atom * props->max_qp;
|
||||
props->max_srq = 1 << dev->mdev->caps.log_max_srq;
|
||||
props->max_srq_wr = dev->mdev->caps.max_srq_wqes - 1;
|
||||
props->max_srq_sge = max_rq_sg - 1;
|
||||
props->max_fast_reg_page_list_len = (unsigned int)-1;
|
||||
props->local_ca_ack_delay = dev->mdev->caps.local_ca_ack_delay;
|
||||
props->local_ca_ack_delay = gen->local_ca_ack_delay;
|
||||
props->atomic_cap = IB_ATOMIC_NONE;
|
||||
props->masked_atomic_cap = IB_ATOMIC_NONE;
|
||||
props->max_pkeys = be16_to_cpup((__be16 *)(out_mad->data + 28));
|
||||
props->max_mcast_grp = 1 << dev->mdev->caps.log_max_mcg;
|
||||
props->max_mcast_qp_attach = dev->mdev->caps.max_qp_mcg;
|
||||
props->max_mcast_grp = 1 << gen->log_max_mcg;
|
||||
props->max_mcast_qp_attach = gen->max_qp_mcg;
|
||||
props->max_total_mcast_qp_attach = props->max_mcast_qp_attach *
|
||||
props->max_mcast_grp;
|
||||
props->max_map_per_fmr = INT_MAX; /* no limit in ConnectIB */
|
||||
|
@ -254,10 +257,12 @@ int mlx5_ib_query_port(struct ib_device *ibdev, u8 port,
|
|||
struct mlx5_ib_dev *dev = to_mdev(ibdev);
|
||||
struct ib_smp *in_mad = NULL;
|
||||
struct ib_smp *out_mad = NULL;
|
||||
struct mlx5_general_caps *gen;
|
||||
int ext_active_speed;
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (port < 1 || port > dev->mdev->caps.num_ports) {
|
||||
gen = &dev->mdev->caps.gen;
|
||||
if (port < 1 || port > gen->num_ports) {
|
||||
mlx5_ib_warn(dev, "invalid port number %d\n", port);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -288,8 +293,8 @@ int mlx5_ib_query_port(struct ib_device *ibdev, u8 port,
|
|||
props->phys_state = out_mad->data[33] >> 4;
|
||||
props->port_cap_flags = be32_to_cpup((__be32 *)(out_mad->data + 20));
|
||||
props->gid_tbl_len = out_mad->data[50];
|
||||
props->max_msg_sz = 1 << to_mdev(ibdev)->mdev->caps.log_max_msg;
|
||||
props->pkey_tbl_len = to_mdev(ibdev)->mdev->caps.port[port - 1].pkey_table_len;
|
||||
props->max_msg_sz = 1 << gen->log_max_msg;
|
||||
props->pkey_tbl_len = gen->port[port - 1].pkey_table_len;
|
||||
props->bad_pkey_cntr = be16_to_cpup((__be16 *)(out_mad->data + 46));
|
||||
props->qkey_viol_cntr = be16_to_cpup((__be16 *)(out_mad->data + 48));
|
||||
props->active_width = out_mad->data[31] & 0xf;
|
||||
|
@ -316,7 +321,7 @@ int mlx5_ib_query_port(struct ib_device *ibdev, u8 port,
|
|||
|
||||
/* If reported active speed is QDR, check if is FDR-10 */
|
||||
if (props->active_speed == 4) {
|
||||
if (dev->mdev->caps.ext_port_cap[port - 1] &
|
||||
if (gen->ext_port_cap[port - 1] &
|
||||
MLX_EXT_PORT_CAP_FLAG_EXTENDED_PORT_INFO) {
|
||||
init_query_mad(in_mad);
|
||||
in_mad->attr_id = MLX5_ATTR_EXTENDED_PORT_INFO;
|
||||
|
@ -470,6 +475,7 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
|
|||
struct mlx5_ib_alloc_ucontext_req_v2 req;
|
||||
struct mlx5_ib_alloc_ucontext_resp resp;
|
||||
struct mlx5_ib_ucontext *context;
|
||||
struct mlx5_general_caps *gen;
|
||||
struct mlx5_uuar_info *uuari;
|
||||
struct mlx5_uar *uars;
|
||||
int gross_uuars;
|
||||
|
@ -480,6 +486,7 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
|
|||
int i;
|
||||
size_t reqlen;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
if (!dev->ib_active)
|
||||
return ERR_PTR(-EAGAIN);
|
||||
|
||||
|
@ -512,14 +519,14 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
|
|||
|
||||
num_uars = req.total_num_uuars / MLX5_NON_FP_BF_REGS_PER_PAGE;
|
||||
gross_uuars = num_uars * MLX5_BF_REGS_PER_PAGE;
|
||||
resp.qp_tab_size = 1 << dev->mdev->caps.log_max_qp;
|
||||
resp.bf_reg_size = dev->mdev->caps.bf_reg_size;
|
||||
resp.qp_tab_size = 1 << gen->log_max_qp;
|
||||
resp.bf_reg_size = gen->bf_reg_size;
|
||||
resp.cache_line_size = L1_CACHE_BYTES;
|
||||
resp.max_sq_desc_sz = dev->mdev->caps.max_sq_desc_sz;
|
||||
resp.max_rq_desc_sz = dev->mdev->caps.max_rq_desc_sz;
|
||||
resp.max_send_wqebb = dev->mdev->caps.max_wqes;
|
||||
resp.max_recv_wr = dev->mdev->caps.max_wqes;
|
||||
resp.max_srq_recv_wr = dev->mdev->caps.max_srq_wqes;
|
||||
resp.max_sq_desc_sz = gen->max_sq_desc_sz;
|
||||
resp.max_rq_desc_sz = gen->max_rq_desc_sz;
|
||||
resp.max_send_wqebb = gen->max_wqes;
|
||||
resp.max_recv_wr = gen->max_wqes;
|
||||
resp.max_srq_recv_wr = gen->max_srq_wqes;
|
||||
|
||||
context = kzalloc(sizeof(*context), GFP_KERNEL);
|
||||
if (!context)
|
||||
|
@ -565,7 +572,7 @@ static struct ib_ucontext *mlx5_ib_alloc_ucontext(struct ib_device *ibdev,
|
|||
mutex_init(&context->db_page_mutex);
|
||||
|
||||
resp.tot_uuars = req.total_num_uuars;
|
||||
resp.num_ports = dev->mdev->caps.num_ports;
|
||||
resp.num_ports = gen->num_ports;
|
||||
err = ib_copy_to_udata(udata, &resp,
|
||||
sizeof(resp) - sizeof(resp.reserved));
|
||||
if (err)
|
||||
|
@ -967,9 +974,11 @@ static void mlx5_ib_event(struct mlx5_core_dev *dev, void *context,
|
|||
|
||||
static void get_ext_port_caps(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_general_caps *gen;
|
||||
int port;
|
||||
|
||||
for (port = 1; port <= dev->mdev->caps.num_ports; port++)
|
||||
gen = &dev->mdev->caps.gen;
|
||||
for (port = 1; port <= gen->num_ports; port++)
|
||||
mlx5_query_ext_port_caps(dev, port);
|
||||
}
|
||||
|
||||
|
@ -977,9 +986,11 @@ static int get_port_caps(struct mlx5_ib_dev *dev)
|
|||
{
|
||||
struct ib_device_attr *dprops = NULL;
|
||||
struct ib_port_attr *pprops = NULL;
|
||||
struct mlx5_general_caps *gen;
|
||||
int err = 0;
|
||||
int port;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
pprops = kmalloc(sizeof(*pprops), GFP_KERNEL);
|
||||
if (!pprops)
|
||||
goto out;
|
||||
|
@ -994,14 +1005,14 @@ static int get_port_caps(struct mlx5_ib_dev *dev)
|
|||
goto out;
|
||||
}
|
||||
|
||||
for (port = 1; port <= dev->mdev->caps.num_ports; port++) {
|
||||
for (port = 1; port <= gen->num_ports; port++) {
|
||||
err = mlx5_ib_query_port(&dev->ib_dev, port, pprops);
|
||||
if (err) {
|
||||
mlx5_ib_warn(dev, "query_port %d failed %d\n", port, err);
|
||||
break;
|
||||
}
|
||||
dev->mdev->caps.port[port - 1].pkey_table_len = dprops->max_pkeys;
|
||||
dev->mdev->caps.port[port - 1].gid_table_len = pprops->gid_tbl_len;
|
||||
gen->port[port - 1].pkey_table_len = dprops->max_pkeys;
|
||||
gen->port[port - 1].gid_table_len = pprops->gid_tbl_len;
|
||||
mlx5_ib_dbg(dev, "pkey_table_len %d, gid_table_len %d\n",
|
||||
dprops->max_pkeys, pprops->gid_tbl_len);
|
||||
}
|
||||
|
@ -1279,8 +1290,8 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
|
|||
strlcpy(dev->ib_dev.name, "mlx5_%d", IB_DEVICE_NAME_MAX);
|
||||
dev->ib_dev.owner = THIS_MODULE;
|
||||
dev->ib_dev.node_type = RDMA_NODE_IB_CA;
|
||||
dev->ib_dev.local_dma_lkey = mdev->caps.reserved_lkey;
|
||||
dev->num_ports = mdev->caps.num_ports;
|
||||
dev->ib_dev.local_dma_lkey = mdev->caps.gen.reserved_lkey;
|
||||
dev->num_ports = mdev->caps.gen.num_ports;
|
||||
dev->ib_dev.phys_port_cnt = dev->num_ports;
|
||||
dev->ib_dev.num_comp_vectors = dev->num_comp_vectors;
|
||||
dev->ib_dev.dma_device = &mdev->pdev->dev;
|
||||
|
@ -1355,7 +1366,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
|
|||
dev->ib_dev.free_fast_reg_page_list = mlx5_ib_free_fast_reg_page_list;
|
||||
dev->ib_dev.check_mr_status = mlx5_ib_check_mr_status;
|
||||
|
||||
if (mdev->caps.flags & MLX5_DEV_CAP_FLAG_XRC) {
|
||||
if (mdev->caps.gen.flags & MLX5_DEV_CAP_FLAG_XRC) {
|
||||
dev->ib_dev.alloc_xrcd = mlx5_ib_alloc_xrcd;
|
||||
dev->ib_dev.dealloc_xrcd = mlx5_ib_dealloc_xrcd;
|
||||
dev->ib_dev.uverbs_cmd_mask |=
|
||||
|
|
|
@ -158,11 +158,13 @@ static void mlx5_ib_qp_event(struct mlx5_core_qp *qp, int type)
|
|||
static int set_rq_size(struct mlx5_ib_dev *dev, struct ib_qp_cap *cap,
|
||||
int has_rq, struct mlx5_ib_qp *qp, struct mlx5_ib_create_qp *ucmd)
|
||||
{
|
||||
struct mlx5_general_caps *gen;
|
||||
int wqe_size;
|
||||
int wq_size;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
/* Sanity check RQ size before proceeding */
|
||||
if (cap->max_recv_wr > dev->mdev->caps.max_wqes)
|
||||
if (cap->max_recv_wr > gen->max_wqes)
|
||||
return -EINVAL;
|
||||
|
||||
if (!has_rq) {
|
||||
|
@ -182,10 +184,10 @@ static int set_rq_size(struct mlx5_ib_dev *dev, struct ib_qp_cap *cap,
|
|||
wq_size = roundup_pow_of_two(cap->max_recv_wr) * wqe_size;
|
||||
wq_size = max_t(int, wq_size, MLX5_SEND_WQE_BB);
|
||||
qp->rq.wqe_cnt = wq_size / wqe_size;
|
||||
if (wqe_size > dev->mdev->caps.max_rq_desc_sz) {
|
||||
if (wqe_size > gen->max_rq_desc_sz) {
|
||||
mlx5_ib_dbg(dev, "wqe_size %d, max %d\n",
|
||||
wqe_size,
|
||||
dev->mdev->caps.max_rq_desc_sz);
|
||||
gen->max_rq_desc_sz);
|
||||
return -EINVAL;
|
||||
}
|
||||
qp->rq.wqe_shift = ilog2(wqe_size);
|
||||
|
@ -266,9 +268,11 @@ static int calc_send_wqe(struct ib_qp_init_attr *attr)
|
|||
static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
|
||||
struct mlx5_ib_qp *qp)
|
||||
{
|
||||
struct mlx5_general_caps *gen;
|
||||
int wqe_size;
|
||||
int wq_size;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
if (!attr->cap.max_send_wr)
|
||||
return 0;
|
||||
|
||||
|
@ -277,9 +281,9 @@ static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
|
|||
if (wqe_size < 0)
|
||||
return wqe_size;
|
||||
|
||||
if (wqe_size > dev->mdev->caps.max_sq_desc_sz) {
|
||||
if (wqe_size > gen->max_sq_desc_sz) {
|
||||
mlx5_ib_dbg(dev, "wqe_size(%d) > max_sq_desc_sz(%d)\n",
|
||||
wqe_size, dev->mdev->caps.max_sq_desc_sz);
|
||||
wqe_size, gen->max_sq_desc_sz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -292,9 +296,9 @@ static int calc_sq_size(struct mlx5_ib_dev *dev, struct ib_qp_init_attr *attr,
|
|||
|
||||
wq_size = roundup_pow_of_two(attr->cap.max_send_wr * wqe_size);
|
||||
qp->sq.wqe_cnt = wq_size / MLX5_SEND_WQE_BB;
|
||||
if (qp->sq.wqe_cnt > dev->mdev->caps.max_wqes) {
|
||||
if (qp->sq.wqe_cnt > gen->max_wqes) {
|
||||
mlx5_ib_dbg(dev, "wqe count(%d) exceeds limits(%d)\n",
|
||||
qp->sq.wqe_cnt, dev->mdev->caps.max_wqes);
|
||||
qp->sq.wqe_cnt, gen->max_wqes);
|
||||
return -ENOMEM;
|
||||
}
|
||||
qp->sq.wqe_shift = ilog2(MLX5_SEND_WQE_BB);
|
||||
|
@ -309,11 +313,13 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev,
|
|||
struct mlx5_ib_qp *qp,
|
||||
struct mlx5_ib_create_qp *ucmd)
|
||||
{
|
||||
struct mlx5_general_caps *gen;
|
||||
int desc_sz = 1 << qp->sq.wqe_shift;
|
||||
|
||||
if (desc_sz > dev->mdev->caps.max_sq_desc_sz) {
|
||||
gen = &dev->mdev->caps.gen;
|
||||
if (desc_sz > gen->max_sq_desc_sz) {
|
||||
mlx5_ib_warn(dev, "desc_sz %d, max_sq_desc_sz %d\n",
|
||||
desc_sz, dev->mdev->caps.max_sq_desc_sz);
|
||||
desc_sz, gen->max_sq_desc_sz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -325,9 +331,9 @@ static int set_user_buf_size(struct mlx5_ib_dev *dev,
|
|||
|
||||
qp->sq.wqe_cnt = ucmd->sq_wqe_count;
|
||||
|
||||
if (qp->sq.wqe_cnt > dev->mdev->caps.max_wqes) {
|
||||
if (qp->sq.wqe_cnt > gen->max_wqes) {
|
||||
mlx5_ib_warn(dev, "wqe_cnt %d, max_wqes %d\n",
|
||||
qp->sq.wqe_cnt, dev->mdev->caps.max_wqes);
|
||||
qp->sq.wqe_cnt, gen->max_wqes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -803,16 +809,18 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd,
|
|||
struct mlx5_ib_resources *devr = &dev->devr;
|
||||
struct mlx5_ib_create_qp_resp resp;
|
||||
struct mlx5_create_qp_mbox_in *in;
|
||||
struct mlx5_general_caps *gen;
|
||||
struct mlx5_ib_create_qp ucmd;
|
||||
int inlen = sizeof(*in);
|
||||
int err;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
mutex_init(&qp->mutex);
|
||||
spin_lock_init(&qp->sq.lock);
|
||||
spin_lock_init(&qp->rq.lock);
|
||||
|
||||
if (init_attr->create_flags & IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK) {
|
||||
if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_BLOCK_MCAST)) {
|
||||
if (!(gen->flags & MLX5_DEV_CAP_FLAG_BLOCK_MCAST)) {
|
||||
mlx5_ib_dbg(dev, "block multicast loopback isn't supported\n");
|
||||
return -EINVAL;
|
||||
} else {
|
||||
|
@ -851,9 +859,9 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd,
|
|||
mlx5_ib_dbg(dev, "invalid rq params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ucmd.sq_wqe_count > dev->mdev->caps.max_wqes) {
|
||||
if (ucmd.sq_wqe_count > gen->max_wqes) {
|
||||
mlx5_ib_dbg(dev, "requested sq_wqe_count (%d) > max allowed (%d)\n",
|
||||
ucmd.sq_wqe_count, dev->mdev->caps.max_wqes);
|
||||
ucmd.sq_wqe_count, gen->max_wqes);
|
||||
return -EINVAL;
|
||||
}
|
||||
err = create_user_qp(dev, pd, qp, udata, &in, &resp, &inlen);
|
||||
|
@ -1144,6 +1152,7 @@ struct ib_qp *mlx5_ib_create_qp(struct ib_pd *pd,
|
|||
struct ib_qp_init_attr *init_attr,
|
||||
struct ib_udata *udata)
|
||||
{
|
||||
struct mlx5_general_caps *gen;
|
||||
struct mlx5_ib_dev *dev;
|
||||
struct mlx5_ib_qp *qp;
|
||||
u16 xrcdn = 0;
|
||||
|
@ -1161,11 +1170,12 @@ struct ib_qp *mlx5_ib_create_qp(struct ib_pd *pd,
|
|||
}
|
||||
dev = to_mdev(to_mxrcd(init_attr->xrcd)->ibxrcd.device);
|
||||
}
|
||||
gen = &dev->mdev->caps.gen;
|
||||
|
||||
switch (init_attr->qp_type) {
|
||||
case IB_QPT_XRC_TGT:
|
||||
case IB_QPT_XRC_INI:
|
||||
if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_XRC)) {
|
||||
if (!(gen->flags & MLX5_DEV_CAP_FLAG_XRC)) {
|
||||
mlx5_ib_dbg(dev, "XRC not supported\n");
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
|
@ -1272,6 +1282,9 @@ enum {
|
|||
|
||||
static int ib_rate_to_mlx5(struct mlx5_ib_dev *dev, u8 rate)
|
||||
{
|
||||
struct mlx5_general_caps *gen;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
if (rate == IB_RATE_PORT_CURRENT) {
|
||||
return 0;
|
||||
} else if (rate < IB_RATE_2_5_GBPS || rate > IB_RATE_300_GBPS) {
|
||||
|
@ -1279,7 +1292,7 @@ static int ib_rate_to_mlx5(struct mlx5_ib_dev *dev, u8 rate)
|
|||
} else {
|
||||
while (rate != IB_RATE_2_5_GBPS &&
|
||||
!(1 << (rate + MLX5_STAT_RATE_OFFSET) &
|
||||
dev->mdev->caps.stat_rate_support))
|
||||
gen->stat_rate_support))
|
||||
--rate;
|
||||
}
|
||||
|
||||
|
@ -1290,8 +1303,10 @@ static int mlx5_set_path(struct mlx5_ib_dev *dev, const struct ib_ah_attr *ah,
|
|||
struct mlx5_qp_path *path, u8 port, int attr_mask,
|
||||
u32 path_flags, const struct ib_qp_attr *attr)
|
||||
{
|
||||
struct mlx5_general_caps *gen;
|
||||
int err;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
path->fl = (path_flags & MLX5_PATH_FLAG_FL) ? 0x80 : 0;
|
||||
path->free_ar = (path_flags & MLX5_PATH_FLAG_FREE_AR) ? 0x80 : 0;
|
||||
|
||||
|
@ -1318,9 +1333,9 @@ static int mlx5_set_path(struct mlx5_ib_dev *dev, const struct ib_ah_attr *ah,
|
|||
path->port = port;
|
||||
|
||||
if (ah->ah_flags & IB_AH_GRH) {
|
||||
if (ah->grh.sgid_index >= dev->mdev->caps.port[port - 1].gid_table_len) {
|
||||
if (ah->grh.sgid_index >= gen->port[port - 1].gid_table_len) {
|
||||
pr_err(KERN_ERR "sgid_index (%u) too large. max is %d\n",
|
||||
ah->grh.sgid_index, dev->mdev->caps.port[port - 1].gid_table_len);
|
||||
ah->grh.sgid_index, gen->port[port - 1].gid_table_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1492,6 +1507,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
|
|||
struct mlx5_ib_qp *qp = to_mqp(ibqp);
|
||||
struct mlx5_ib_cq *send_cq, *recv_cq;
|
||||
struct mlx5_qp_context *context;
|
||||
struct mlx5_general_caps *gen;
|
||||
struct mlx5_modify_qp_mbox_in *in;
|
||||
struct mlx5_ib_pd *pd;
|
||||
enum mlx5_qp_state mlx5_cur, mlx5_new;
|
||||
|
@ -1500,6 +1516,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
|
|||
int mlx5_st;
|
||||
int err;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
in = kzalloc(sizeof(*in), GFP_KERNEL);
|
||||
if (!in)
|
||||
return -ENOMEM;
|
||||
|
@ -1539,7 +1556,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
|
|||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
context->mtu_msgmax = (attr->path_mtu << 5) | dev->mdev->caps.log_max_msg;
|
||||
context->mtu_msgmax = (attr->path_mtu << 5) | gen->log_max_msg;
|
||||
}
|
||||
|
||||
if (attr_mask & IB_QP_DEST_QPN)
|
||||
|
@ -1685,9 +1702,11 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
|
|||
struct mlx5_ib_dev *dev = to_mdev(ibqp->device);
|
||||
struct mlx5_ib_qp *qp = to_mqp(ibqp);
|
||||
enum ib_qp_state cur_state, new_state;
|
||||
struct mlx5_general_caps *gen;
|
||||
int err = -EINVAL;
|
||||
int port;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
mutex_lock(&qp->mutex);
|
||||
|
||||
cur_state = attr_mask & IB_QP_CUR_STATE ? attr->cur_qp_state : qp->state;
|
||||
|
@ -1699,21 +1718,21 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
|
|||
goto out;
|
||||
|
||||
if ((attr_mask & IB_QP_PORT) &&
|
||||
(attr->port_num == 0 || attr->port_num > dev->mdev->caps.num_ports))
|
||||
(attr->port_num == 0 || attr->port_num > gen->num_ports))
|
||||
goto out;
|
||||
|
||||
if (attr_mask & IB_QP_PKEY_INDEX) {
|
||||
port = attr_mask & IB_QP_PORT ? attr->port_num : qp->port;
|
||||
if (attr->pkey_index >= dev->mdev->caps.port[port - 1].pkey_table_len)
|
||||
if (attr->pkey_index >= gen->port[port - 1].pkey_table_len)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC &&
|
||||
attr->max_rd_atomic > dev->mdev->caps.max_ra_res_qp)
|
||||
attr->max_rd_atomic > (1 << gen->log_max_ra_res_qp))
|
||||
goto out;
|
||||
|
||||
if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC &&
|
||||
attr->max_dest_rd_atomic > dev->mdev->caps.max_ra_req_qp)
|
||||
attr->max_dest_rd_atomic > (1 << gen->log_max_ra_req_qp))
|
||||
goto out;
|
||||
|
||||
if (cur_state == new_state && cur_state == IB_QPS_RESET) {
|
||||
|
@ -2893,7 +2912,8 @@ static void to_ib_ah_attr(struct mlx5_ib_dev *ibdev, struct ib_ah_attr *ib_ah_at
|
|||
memset(ib_ah_attr, 0, sizeof(*ib_ah_attr));
|
||||
ib_ah_attr->port_num = path->port;
|
||||
|
||||
if (ib_ah_attr->port_num == 0 || ib_ah_attr->port_num > dev->caps.num_ports)
|
||||
if (ib_ah_attr->port_num == 0 ||
|
||||
ib_ah_attr->port_num > dev->caps.gen.num_ports)
|
||||
return;
|
||||
|
||||
ib_ah_attr->sl = path->sl & 0xf;
|
||||
|
@ -3011,10 +3031,12 @@ struct ib_xrcd *mlx5_ib_alloc_xrcd(struct ib_device *ibdev,
|
|||
struct ib_udata *udata)
|
||||
{
|
||||
struct mlx5_ib_dev *dev = to_mdev(ibdev);
|
||||
struct mlx5_general_caps *gen;
|
||||
struct mlx5_ib_xrcd *xrcd;
|
||||
int err;
|
||||
|
||||
if (!(dev->mdev->caps.flags & MLX5_DEV_CAP_FLAG_XRC))
|
||||
gen = &dev->mdev->caps.gen;
|
||||
if (!(gen->flags & MLX5_DEV_CAP_FLAG_XRC))
|
||||
return ERR_PTR(-ENOSYS);
|
||||
|
||||
xrcd = kmalloc(sizeof(*xrcd), GFP_KERNEL);
|
||||
|
|
|
@ -238,6 +238,7 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd,
|
|||
struct ib_udata *udata)
|
||||
{
|
||||
struct mlx5_ib_dev *dev = to_mdev(pd->device);
|
||||
struct mlx5_general_caps *gen;
|
||||
struct mlx5_ib_srq *srq;
|
||||
int desc_size;
|
||||
int buf_size;
|
||||
|
@ -247,11 +248,12 @@ struct ib_srq *mlx5_ib_create_srq(struct ib_pd *pd,
|
|||
int is_xrc;
|
||||
u32 flgs, xrcdn;
|
||||
|
||||
gen = &dev->mdev->caps.gen;
|
||||
/* Sanity check SRQ size before proceeding */
|
||||
if (init_attr->attr.max_wr >= dev->mdev->caps.max_srq_wqes) {
|
||||
if (init_attr->attr.max_wr >= gen->max_srq_wqes) {
|
||||
mlx5_ib_dbg(dev, "max_wr %d, cap %d\n",
|
||||
init_attr->attr.max_wr,
|
||||
dev->mdev->caps.max_srq_wqes);
|
||||
gen->max_srq_wqes);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
|
|
|
@ -1364,7 +1364,7 @@ void ipoib_setup(struct net_device *dev)
|
|||
dev->tx_queue_len = ipoib_sendq_size * 2;
|
||||
dev->features = (NETIF_F_VLAN_CHALLENGED |
|
||||
NETIF_F_HIGHDMA);
|
||||
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
|
||||
netif_keep_dst(dev);
|
||||
|
||||
memcpy(dev->broadcast, ipv4_bcast_addr, INFINIBAND_ALEN);
|
||||
|
||||
|
|
|
@ -205,11 +205,8 @@ static unsigned command_2_index(unsigned c, unsigned sc)
|
|||
{
|
||||
if (c & 0x80)
|
||||
c = 0x9 + (c & 0x0f);
|
||||
else if (c <= 0x0f);
|
||||
else if (c == 0x41)
|
||||
c = 0x9 + 0x1;
|
||||
else if (c == 0xff)
|
||||
c = 0x00;
|
||||
return (sc & 3) * (0x9 + 0x9) + c;
|
||||
}
|
||||
|
||||
|
|
|
@ -2365,7 +2365,7 @@ static int gigaset_probe(struct usb_interface *interface,
|
|||
endpoint = &hostif->endpoint[0].desc;
|
||||
usb_fill_int_urb(ucs->urb_int_in, udev,
|
||||
usb_rcvintpipe(udev,
|
||||
(endpoint->bEndpointAddress) & 0x0f),
|
||||
usb_endpoint_num(endpoint)),
|
||||
ucs->int_in_buf, IP_MSGSIZE, read_int_callback, cs,
|
||||
endpoint->bInterval);
|
||||
rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL);
|
||||
|
|
|
@ -1243,7 +1243,8 @@ static void do_action(int action, struct cardstate *cs,
|
|||
break;
|
||||
case ACT_FAILDLE0:
|
||||
cs->cur_at_seq = SEQ_NONE;
|
||||
dev_warn(cs->dev, "Could not leave DLE mode.\n");
|
||||
dev_warn(cs->dev, "Error leaving DLE mode.\n");
|
||||
cs->dle = 0;
|
||||
at_state2 = &cs->bcs[cs->curchannel].at_state;
|
||||
disconnect(&at_state2);
|
||||
schedule_init(cs, MS_RECOVER);
|
||||
|
|
|
@ -135,14 +135,13 @@ struct usb_cardstate {
|
|||
/* Output buffer */
|
||||
unsigned char *bulk_out_buffer;
|
||||
int bulk_out_size;
|
||||
__u8 bulk_out_endpointAddr;
|
||||
int bulk_out_epnum;
|
||||
struct urb *bulk_out_urb;
|
||||
|
||||
/* Input buffer */
|
||||
unsigned char *rcvbuf;
|
||||
int rcvbuf_size;
|
||||
struct urb *read_urb;
|
||||
__u8 int_in_endpointAddr;
|
||||
|
||||
char bchars[6]; /* for request 0x19 */
|
||||
};
|
||||
|
@ -466,7 +465,7 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb)
|
|||
|
||||
usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
|
||||
usb_sndbulkpipe(ucs->udev,
|
||||
ucs->bulk_out_endpointAddr & 0x0f),
|
||||
ucs->bulk_out_epnum),
|
||||
cb->buf + cb->offset, count,
|
||||
gigaset_write_bulk_callback, cs);
|
||||
|
||||
|
@ -628,8 +627,7 @@ static int write_modem(struct cardstate *cs)
|
|||
if (cs->connected) {
|
||||
usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
|
||||
usb_sndbulkpipe(ucs->udev,
|
||||
ucs->bulk_out_endpointAddr &
|
||||
0x0f),
|
||||
ucs->bulk_out_epnum),
|
||||
ucs->bulk_out_buffer, count,
|
||||
gigaset_write_bulk_callback, cs);
|
||||
ret = usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC);
|
||||
|
@ -714,7 +712,7 @@ static int gigaset_probe(struct usb_interface *interface,
|
|||
|
||||
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
|
||||
ucs->bulk_out_size = buffer_size;
|
||||
ucs->bulk_out_endpointAddr = endpoint->bEndpointAddress;
|
||||
ucs->bulk_out_epnum = usb_endpoint_num(endpoint);
|
||||
ucs->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
|
||||
if (!ucs->bulk_out_buffer) {
|
||||
dev_err(cs->dev, "Couldn't allocate bulk_out_buffer\n");
|
||||
|
@ -741,7 +739,6 @@ static int gigaset_probe(struct usb_interface *interface,
|
|||
}
|
||||
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
|
||||
ucs->rcvbuf_size = buffer_size;
|
||||
ucs->int_in_endpointAddr = endpoint->bEndpointAddress;
|
||||
ucs->rcvbuf = kmalloc(buffer_size, GFP_KERNEL);
|
||||
if (!ucs->rcvbuf) {
|
||||
dev_err(cs->dev, "Couldn't allocate rcvbuf\n");
|
||||
|
@ -750,8 +747,7 @@ static int gigaset_probe(struct usb_interface *interface,
|
|||
}
|
||||
/* Fill the interrupt urb and send it to the core */
|
||||
usb_fill_int_urb(ucs->read_urb, udev,
|
||||
usb_rcvintpipe(udev,
|
||||
endpoint->bEndpointAddress & 0x0f),
|
||||
usb_rcvintpipe(udev, usb_endpoint_num(endpoint)),
|
||||
ucs->rcvbuf, buffer_size,
|
||||
gigaset_read_int_callback,
|
||||
cs, endpoint->bInterval);
|
||||
|
|
|
@ -1454,66 +1454,63 @@ dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members)
|
|||
#ifdef CMX_CONF_DEBUG
|
||||
if (0) {
|
||||
#else
|
||||
if (members == 2) {
|
||||
if (members == 2) {
|
||||
#endif
|
||||
/* "other" becomes other party */
|
||||
other = (list_entry(conf->mlist.next,
|
||||
struct dsp_conf_member, list))->dsp;
|
||||
if (other == member)
|
||||
other = (list_entry(conf->mlist.prev,
|
||||
struct dsp_conf_member, list))->dsp;
|
||||
o_q = other->rx_buff; /* received data */
|
||||
o_rr = (other->rx_R + len) & CMX_BUFF_MASK;
|
||||
/* end of rx-pointer */
|
||||
o_r = (o_rr - rr + r) & CMX_BUFF_MASK;
|
||||
/* start rx-pointer at current read position*/
|
||||
/* -> if echo is NOT enabled */
|
||||
if (!dsp->echo.software) {
|
||||
/*
|
||||
* -> copy other member's rx-data,
|
||||
* if tx-data is available, mix
|
||||
*/
|
||||
while (o_r != o_rr && t != tt) {
|
||||
*d++ = dsp_audio_mix_law[(p[t] << 8) | o_q[o_r]];
|
||||
t = (t + 1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r + 1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while (o_r != o_rr) {
|
||||
*d++ = o_q[o_r];
|
||||
o_r = (o_r + 1) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* -> if echo is enabled */
|
||||
} else {
|
||||
/*
|
||||
* -> mix other member's rx-data with echo,
|
||||
* if tx-data is available, mix
|
||||
*/
|
||||
while (r != rr && t != tt) {
|
||||
sample = dsp_audio_law_to_s32[p[t]] +
|
||||
dsp_audio_law_to_s32[q[r]] +
|
||||
dsp_audio_law_to_s32[o_q[o_r]];
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
sample = 32767;
|
||||
*d++ = dsp_audio_s16_to_law[sample & 0xffff];
|
||||
/* tx-data + rx_data + echo */
|
||||
t = (t + 1) & CMX_BUFF_MASK;
|
||||
r = (r + 1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r + 1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while (r != rr) {
|
||||
*d++ = dsp_audio_mix_law[(q[r] << 8) | o_q[o_r]];
|
||||
r = (r + 1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r + 1) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* "other" becomes other party */
|
||||
other = (list_entry(conf->mlist.next,
|
||||
struct dsp_conf_member, list))->dsp;
|
||||
if (other == member)
|
||||
other = (list_entry(conf->mlist.prev,
|
||||
struct dsp_conf_member, list))->dsp;
|
||||
o_q = other->rx_buff; /* received data */
|
||||
o_rr = (other->rx_R + len) & CMX_BUFF_MASK;
|
||||
/* end of rx-pointer */
|
||||
o_r = (o_rr - rr + r) & CMX_BUFF_MASK;
|
||||
/* start rx-pointer at current read position*/
|
||||
/* -> if echo is NOT enabled */
|
||||
if (!dsp->echo.software) {
|
||||
/*
|
||||
* -> copy other member's rx-data,
|
||||
* if tx-data is available, mix
|
||||
*/
|
||||
while (o_r != o_rr && t != tt) {
|
||||
*d++ = dsp_audio_mix_law[(p[t] << 8) | o_q[o_r]];
|
||||
t = (t + 1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r + 1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while (o_r != o_rr) {
|
||||
*d++ = o_q[o_r];
|
||||
o_r = (o_r + 1) & CMX_BUFF_MASK;
|
||||
}
|
||||
/* -> if echo is enabled */
|
||||
} else {
|
||||
/*
|
||||
* -> mix other member's rx-data with echo,
|
||||
* if tx-data is available, mix
|
||||
*/
|
||||
while (r != rr && t != tt) {
|
||||
sample = dsp_audio_law_to_s32[p[t]] +
|
||||
dsp_audio_law_to_s32[q[r]] +
|
||||
dsp_audio_law_to_s32[o_q[o_r]];
|
||||
if (sample < -32768)
|
||||
sample = -32768;
|
||||
else if (sample > 32767)
|
||||
sample = 32767;
|
||||
*d++ = dsp_audio_s16_to_law[sample & 0xffff];
|
||||
/* tx-data + rx_data + echo */
|
||||
t = (t + 1) & CMX_BUFF_MASK;
|
||||
r = (r + 1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r + 1) & CMX_BUFF_MASK;
|
||||
}
|
||||
while (r != rr) {
|
||||
*d++ = dsp_audio_mix_law[(q[r] << 8) | o_q[o_r]];
|
||||
r = (r + 1) & CMX_BUFF_MASK;
|
||||
o_r = (o_r + 1) & CMX_BUFF_MASK;
|
||||
}
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
}
|
||||
#ifdef DSP_NEVER_DEFINED
|
||||
dsp->tx_R = t;
|
||||
goto send_packet;
|
||||
}
|
||||
#endif
|
||||
/* PROCESS DATA (three or more members) */
|
||||
/* -> if echo is NOT enabled */
|
||||
if (!dsp->echo.software) {
|
||||
|
|
|
@ -147,7 +147,6 @@ config MACVTAP
|
|||
config VXLAN
|
||||
tristate "Virtual eXtensible Local Area Network (VXLAN)"
|
||||
depends on INET
|
||||
select NET_IP_TUNNEL
|
||||
select NET_UDP_TUNNEL
|
||||
---help---
|
||||
This allows one to create vxlan virtual interfaces that provide
|
||||
|
|
|
@ -74,7 +74,7 @@ static struct net_device * __init ipddp_init(void)
|
|||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
|
||||
netif_keep_dst(dev);
|
||||
strcpy(dev->name, "ipddp%d");
|
||||
|
||||
if (version_printed++ == 0)
|
||||
|
|
|
@ -777,7 +777,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
|
|||
ACOMMAND(CFLAGScmd | RESETclear);
|
||||
AINTMASK(0);
|
||||
spin_unlock(&lp->lock);
|
||||
return IRQ_HANDLED;
|
||||
return retval;
|
||||
}
|
||||
|
||||
BUGMSG(D_DURING, "in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/arcdevice.h>
|
||||
#include <linux/com20020.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
|
@ -61,115 +62,317 @@ module_param(clockp, int, 0);
|
|||
module_param(clockm, int, 0);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static void com20020pci_remove(struct pci_dev *pdev);
|
||||
|
||||
static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct com20020_pci_card_info *ci;
|
||||
struct net_device *dev;
|
||||
struct arcnet_local *lp;
|
||||
int ioaddr, err;
|
||||
struct com20020_priv *priv;
|
||||
int i, ioaddr, ret;
|
||||
struct resource *r;
|
||||
|
||||
if (pci_enable_device(pdev))
|
||||
return -EIO;
|
||||
dev = alloc_arcdev(device);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->netdev_ops = &com20020_netdev_ops;
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct com20020_priv),
|
||||
GFP_KERNEL);
|
||||
ci = (struct com20020_pci_card_info *)id->driver_data;
|
||||
priv->ci = ci;
|
||||
|
||||
lp = netdev_priv(dev);
|
||||
INIT_LIST_HEAD(&priv->list_dev);
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
// SOHARD needs PCI base addr 4
|
||||
if (pdev->vendor==0x10B5) {
|
||||
BUGMSG(D_NORMAL, "SOHARD\n");
|
||||
ioaddr = pci_resource_start(pdev, 4);
|
||||
}
|
||||
else {
|
||||
BUGMSG(D_NORMAL, "Contemporary Controls\n");
|
||||
ioaddr = pci_resource_start(pdev, 2);
|
||||
for (i = 0; i < ci->devcount; i++) {
|
||||
struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i];
|
||||
struct com20020_dev *card;
|
||||
|
||||
dev = alloc_arcdev(device);
|
||||
if (!dev) {
|
||||
ret = -ENOMEM;
|
||||
goto out_port;
|
||||
}
|
||||
|
||||
dev->netdev_ops = &com20020_netdev_ops;
|
||||
|
||||
lp = netdev_priv(dev);
|
||||
|
||||
BUGMSG(D_NORMAL, "%s Controls\n", ci->name);
|
||||
ioaddr = pci_resource_start(pdev, cm->bar) + cm->offset;
|
||||
|
||||
r = devm_request_region(&pdev->dev, ioaddr, cm->size,
|
||||
"com20020-pci");
|
||||
if (!r) {
|
||||
pr_err("IO region %xh-%xh already allocated.\n",
|
||||
ioaddr, ioaddr + cm->size - 1);
|
||||
ret = -EBUSY;
|
||||
goto out_port;
|
||||
}
|
||||
|
||||
/* Dummy access after Reset
|
||||
* ARCNET controller needs
|
||||
* this access to detect bustype
|
||||
*/
|
||||
outb(0x00, ioaddr + 1);
|
||||
inb(ioaddr + 1);
|
||||
|
||||
dev->base_addr = ioaddr;
|
||||
dev->dev_addr[0] = node;
|
||||
dev->irq = pdev->irq;
|
||||
lp->card_name = "PCI COM20020";
|
||||
lp->card_flags = ci->flags;
|
||||
lp->backplane = backplane;
|
||||
lp->clockp = clockp & 7;
|
||||
lp->clockm = clockm & 3;
|
||||
lp->timeout = timeout;
|
||||
lp->hw.owner = THIS_MODULE;
|
||||
|
||||
if (ASTATUS() == 0xFF) {
|
||||
pr_err("IO address %Xh is empty!\n", ioaddr);
|
||||
ret = -EIO;
|
||||
goto out_port;
|
||||
}
|
||||
if (com20020_check(dev)) {
|
||||
ret = -EIO;
|
||||
goto out_port;
|
||||
}
|
||||
|
||||
card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev),
|
||||
GFP_KERNEL);
|
||||
if (!card) {
|
||||
pr_err("%s out of memory!\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->index = i;
|
||||
card->pci_priv = priv;
|
||||
card->dev = dev;
|
||||
|
||||
dev_set_drvdata(&dev->dev, card);
|
||||
|
||||
ret = com20020_found(dev, IRQF_SHARED);
|
||||
if (ret)
|
||||
goto out_port;
|
||||
|
||||
list_add(&card->list, &priv->list_dev);
|
||||
}
|
||||
|
||||
if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com20020-pci")) {
|
||||
BUGMSG(D_INIT, "IO region %xh-%xh already allocated.\n",
|
||||
ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
|
||||
err = -EBUSY;
|
||||
goto out_dev;
|
||||
}
|
||||
|
||||
// Dummy access after Reset
|
||||
// ARCNET controller needs this access to detect bustype
|
||||
outb(0x00,ioaddr+1);
|
||||
inb(ioaddr+1);
|
||||
|
||||
dev->base_addr = ioaddr;
|
||||
dev->irq = pdev->irq;
|
||||
dev->dev_addr[0] = node;
|
||||
lp->card_name = "PCI COM20020";
|
||||
lp->card_flags = id->driver_data;
|
||||
lp->backplane = backplane;
|
||||
lp->clockp = clockp & 7;
|
||||
lp->clockm = clockm & 3;
|
||||
lp->timeout = timeout;
|
||||
lp->hw.owner = THIS_MODULE;
|
||||
|
||||
if (ASTATUS() == 0xFF) {
|
||||
BUGMSG(D_NORMAL, "IO address %Xh was reported by PCI BIOS, "
|
||||
"but seems empty!\n", ioaddr);
|
||||
err = -EIO;
|
||||
goto out_port;
|
||||
}
|
||||
if (com20020_check(dev)) {
|
||||
err = -EIO;
|
||||
goto out_port;
|
||||
}
|
||||
|
||||
if ((err = com20020_found(dev, IRQF_SHARED)) != 0)
|
||||
goto out_port;
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
|
||||
out_port:
|
||||
release_region(ioaddr, ARCNET_TOTAL_SIZE);
|
||||
out_dev:
|
||||
free_netdev(dev);
|
||||
return err;
|
||||
com20020pci_remove(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void com20020pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct net_device *dev = pci_get_drvdata(pdev);
|
||||
unregister_netdev(dev);
|
||||
free_irq(dev->irq, dev);
|
||||
release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
|
||||
free_netdev(dev);
|
||||
struct com20020_dev *card, *tmpcard;
|
||||
struct com20020_priv *priv;
|
||||
|
||||
priv = pci_get_drvdata(pdev);
|
||||
|
||||
list_for_each_entry_safe(card, tmpcard, &priv->list_dev, list) {
|
||||
struct net_device *dev = card->dev;
|
||||
|
||||
unregister_netdev(dev);
|
||||
free_irq(dev->irq, dev);
|
||||
free_netdev(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static struct com20020_pci_card_info card_info_10mbit = {
|
||||
.name = "ARC-PCI",
|
||||
.devcount = 1,
|
||||
.chan_map_tbl = {
|
||||
{ 2, 0x00, 0x08 },
|
||||
},
|
||||
.flags = ARC_CAN_10MBIT,
|
||||
};
|
||||
|
||||
static struct com20020_pci_card_info card_info_5mbit = {
|
||||
.name = "ARC-PCI",
|
||||
.devcount = 1,
|
||||
.chan_map_tbl = {
|
||||
{ 2, 0x00, 0x08 },
|
||||
},
|
||||
.flags = ARC_IS_5MBIT,
|
||||
};
|
||||
|
||||
static struct com20020_pci_card_info card_info_sohard = {
|
||||
.name = "PLX-PCI",
|
||||
.devcount = 1,
|
||||
/* SOHARD needs PCI base addr 4 */
|
||||
.chan_map_tbl = {
|
||||
{4, 0x00, 0x08},
|
||||
},
|
||||
.flags = ARC_CAN_10MBIT,
|
||||
};
|
||||
|
||||
static struct com20020_pci_card_info card_info_eae = {
|
||||
.name = "EAE PLX-PCI",
|
||||
.devcount = 2,
|
||||
.chan_map_tbl = {
|
||||
{ 2, 0x00, 0x08 },
|
||||
{ 2, 0x08, 0x08 }
|
||||
},
|
||||
.flags = ARC_CAN_10MBIT,
|
||||
};
|
||||
|
||||
static const struct pci_device_id com20020pci_id_table[] = {
|
||||
{ 0x1571, 0xa001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0x1571, 0xa002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0x1571, 0xa003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0x1571, 0xa004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0x1571, 0xa005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0x1571, 0xa006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0x1571, 0xa007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0x1571, 0xa008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0x1571, 0xa009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
|
||||
{ 0x1571, 0xa00a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
|
||||
{ 0x1571, 0xa00b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
|
||||
{ 0x1571, 0xa00c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
|
||||
{ 0x1571, 0xa00d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
|
||||
{ 0x1571, 0xa00e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
|
||||
{ 0x1571, 0xa201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
|
||||
{ 0x1571, 0xa202, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
|
||||
{ 0x1571, 0xa203, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
|
||||
{ 0x1571, 0xa204, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
|
||||
{ 0x1571, 0xa205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
|
||||
{ 0x1571, 0xa206, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
|
||||
{ 0x10B5, 0x9030, 0x10B5, 0x2978, 0, 0, ARC_CAN_10MBIT },
|
||||
{ 0x10B5, 0x9050, 0x10B5, 0x2273, 0, 0, ARC_CAN_10MBIT },
|
||||
{ 0x14BA, 0x6000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
|
||||
{ 0x10B5, 0x2200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
|
||||
{0,}
|
||||
{
|
||||
0x1571, 0xa001,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
0x1571, 0xa002,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
0x1571, 0xa003,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
0
|
||||
},
|
||||
{
|
||||
0x1571, 0xa004,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
0,
|
||||
},
|
||||
{
|
||||
0x1571, 0xa005,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
0
|
||||
},
|
||||
{
|
||||
0x1571, 0xa006,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
0
|
||||
},
|
||||
{
|
||||
0x1571, 0xa007,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
0
|
||||
},
|
||||
{
|
||||
0x1571, 0xa008,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
0
|
||||
},
|
||||
{
|
||||
0x1571, 0xa009,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_5mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa00a,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_5mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa00b,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_5mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa00c,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_5mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa00d,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_5mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa00e,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_5mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa201,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_10mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa202,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_10mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa203,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_10mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa204,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_10mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa205,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_10mbit
|
||||
},
|
||||
{
|
||||
0x1571, 0xa206,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_10mbit
|
||||
},
|
||||
{
|
||||
0x10B5, 0x9030,
|
||||
0x10B5, 0x2978,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_sohard
|
||||
},
|
||||
{
|
||||
0x10B5, 0x9050,
|
||||
0x10B5, 0x2273,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_sohard
|
||||
},
|
||||
{
|
||||
0x10B5, 0x9050,
|
||||
0x10B5, 0x3292,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_eae
|
||||
},
|
||||
{
|
||||
0x14BA, 0x6000,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_10mbit
|
||||
},
|
||||
{
|
||||
0x10B5, 0x2200,
|
||||
PCI_ANY_ID, PCI_ANY_ID,
|
||||
0, 0,
|
||||
(kernel_ulong_t)&card_info_10mbit
|
||||
},
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, com20020pci_id_table);
|
||||
|
|
|
@ -149,11 +149,25 @@ int com20020_check(struct net_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int com20020_set_hwaddr(struct net_device *dev, void *addr)
|
||||
{
|
||||
int ioaddr = dev->base_addr;
|
||||
struct arcnet_local *lp = netdev_priv(dev);
|
||||
struct sockaddr *hwaddr = addr;
|
||||
|
||||
memcpy(dev->dev_addr, hwaddr->sa_data, 1);
|
||||
SET_SUBADR(SUB_NODE);
|
||||
outb(dev->dev_addr[0], _XREG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct net_device_ops com20020_netdev_ops = {
|
||||
.ndo_open = arcnet_open,
|
||||
.ndo_stop = arcnet_close,
|
||||
.ndo_start_xmit = arcnet_send_packet,
|
||||
.ndo_tx_timeout = arcnet_timeout,
|
||||
.ndo_set_mac_address = com20020_set_hwaddr,
|
||||
.ndo_set_rx_mode = com20020_set_mc_list,
|
||||
};
|
||||
|
||||
|
|
|
@ -112,10 +112,6 @@ static void com20020_detach(struct pcmcia_device *p_dev);
|
|||
|
||||
/*====================================================================*/
|
||||
|
||||
struct com20020_dev {
|
||||
struct net_device *dev;
|
||||
};
|
||||
|
||||
static int com20020_probe(struct pcmcia_device *p_dev)
|
||||
{
|
||||
struct com20020_dev *info;
|
||||
|
|
|
@ -102,17 +102,20 @@ static const u8 lacpdu_mcast_addr[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
|
|||
/* ================= main 802.3ad protocol functions ================== */
|
||||
static int ad_lacpdu_send(struct port *port);
|
||||
static int ad_marker_send(struct port *port, struct bond_marker *marker);
|
||||
static void ad_mux_machine(struct port *port);
|
||||
static void ad_mux_machine(struct port *port, bool *update_slave_arr);
|
||||
static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port);
|
||||
static void ad_tx_machine(struct port *port);
|
||||
static void ad_periodic_machine(struct port *port);
|
||||
static void ad_port_selection_logic(struct port *port);
|
||||
static void ad_agg_selection_logic(struct aggregator *aggregator);
|
||||
static void ad_port_selection_logic(struct port *port, bool *update_slave_arr);
|
||||
static void ad_agg_selection_logic(struct aggregator *aggregator,
|
||||
bool *update_slave_arr);
|
||||
static void ad_clear_agg(struct aggregator *aggregator);
|
||||
static void ad_initialize_agg(struct aggregator *aggregator);
|
||||
static void ad_initialize_port(struct port *port, int lacp_fast);
|
||||
static void ad_enable_collecting_distributing(struct port *port);
|
||||
static void ad_disable_collecting_distributing(struct port *port);
|
||||
static void ad_enable_collecting_distributing(struct port *port,
|
||||
bool *update_slave_arr);
|
||||
static void ad_disable_collecting_distributing(struct port *port,
|
||||
bool *update_slave_arr);
|
||||
static void ad_marker_info_received(struct bond_marker *marker_info,
|
||||
struct port *port);
|
||||
static void ad_marker_response_received(struct bond_marker *marker,
|
||||
|
@ -233,24 +236,6 @@ static inline int __check_agg_selection_timer(struct port *port)
|
|||
return BOND_AD_INFO(bond).agg_select_timer ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __get_state_machine_lock - lock the port's state machines
|
||||
* @port: the port we're looking at
|
||||
*/
|
||||
static inline void __get_state_machine_lock(struct port *port)
|
||||
{
|
||||
spin_lock_bh(&(SLAVE_AD_INFO(port->slave)->state_machine_lock));
|
||||
}
|
||||
|
||||
/**
|
||||
* __release_state_machine_lock - unlock the port's state machines
|
||||
* @port: the port we're looking at
|
||||
*/
|
||||
static inline void __release_state_machine_lock(struct port *port)
|
||||
{
|
||||
spin_unlock_bh(&(SLAVE_AD_INFO(port->slave)->state_machine_lock));
|
||||
}
|
||||
|
||||
/**
|
||||
* __get_link_speed - get a port's speed
|
||||
* @port: the port we're looking at
|
||||
|
@ -315,15 +300,14 @@ static u16 __get_link_speed(struct port *port)
|
|||
static u8 __get_duplex(struct port *port)
|
||||
{
|
||||
struct slave *slave = port->slave;
|
||||
|
||||
u8 retval;
|
||||
|
||||
/* handling a special case: when the configuration starts with
|
||||
* link down, it sets the duplex to 0.
|
||||
*/
|
||||
if (slave->link != BOND_LINK_UP)
|
||||
if (slave->link != BOND_LINK_UP) {
|
||||
retval = 0x0;
|
||||
else {
|
||||
} else {
|
||||
switch (slave->duplex) {
|
||||
case DUPLEX_FULL:
|
||||
retval = 0x1;
|
||||
|
@ -341,16 +325,6 @@ static u8 __get_duplex(struct port *port)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* __initialize_port_locks - initialize a port's STATE machine spinlock
|
||||
* @port: the slave of the port we're looking at
|
||||
*/
|
||||
static inline void __initialize_port_locks(struct slave *slave)
|
||||
{
|
||||
/* make sure it isn't called twice */
|
||||
spin_lock_init(&(SLAVE_AD_INFO(slave)->state_machine_lock));
|
||||
}
|
||||
|
||||
/* Conversions */
|
||||
|
||||
/**
|
||||
|
@ -825,8 +799,9 @@ static int ad_marker_send(struct port *port, struct bond_marker *marker)
|
|||
/**
|
||||
* ad_mux_machine - handle a port's mux state machine
|
||||
* @port: the port we're looking at
|
||||
* @update_slave_arr: Does slave array need update?
|
||||
*/
|
||||
static void ad_mux_machine(struct port *port)
|
||||
static void ad_mux_machine(struct port *port, bool *update_slave_arr)
|
||||
{
|
||||
mux_states_t last_state;
|
||||
|
||||
|
@ -930,7 +905,8 @@ static void ad_mux_machine(struct port *port)
|
|||
switch (port->sm_mux_state) {
|
||||
case AD_MUX_DETACHED:
|
||||
port->actor_oper_port_state &= ~AD_STATE_SYNCHRONIZATION;
|
||||
ad_disable_collecting_distributing(port);
|
||||
ad_disable_collecting_distributing(port,
|
||||
update_slave_arr);
|
||||
port->actor_oper_port_state &= ~AD_STATE_COLLECTING;
|
||||
port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING;
|
||||
port->ntt = true;
|
||||
|
@ -942,13 +918,15 @@ static void ad_mux_machine(struct port *port)
|
|||
port->actor_oper_port_state |= AD_STATE_SYNCHRONIZATION;
|
||||
port->actor_oper_port_state &= ~AD_STATE_COLLECTING;
|
||||
port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING;
|
||||
ad_disable_collecting_distributing(port);
|
||||
ad_disable_collecting_distributing(port,
|
||||
update_slave_arr);
|
||||
port->ntt = true;
|
||||
break;
|
||||
case AD_MUX_COLLECTING_DISTRIBUTING:
|
||||
port->actor_oper_port_state |= AD_STATE_COLLECTING;
|
||||
port->actor_oper_port_state |= AD_STATE_DISTRIBUTING;
|
||||
ad_enable_collecting_distributing(port);
|
||||
ad_enable_collecting_distributing(port,
|
||||
update_slave_arr);
|
||||
port->ntt = true;
|
||||
break;
|
||||
default:
|
||||
|
@ -1216,12 +1194,13 @@ static void ad_periodic_machine(struct port *port)
|
|||
/**
|
||||
* ad_port_selection_logic - select aggregation groups
|
||||
* @port: the port we're looking at
|
||||
* @update_slave_arr: Does slave array need update?
|
||||
*
|
||||
* Select aggregation groups, and assign each port for it's aggregetor. The
|
||||
* selection logic is called in the inititalization (after all the handshkes),
|
||||
* and after every lacpdu receive (if selected is off).
|
||||
*/
|
||||
static void ad_port_selection_logic(struct port *port)
|
||||
static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
|
||||
{
|
||||
struct aggregator *aggregator, *free_aggregator = NULL, *temp_aggregator;
|
||||
struct port *last_port = NULL, *curr_port;
|
||||
|
@ -1376,7 +1355,7 @@ static void ad_port_selection_logic(struct port *port)
|
|||
__agg_ports_are_ready(port->aggregator));
|
||||
|
||||
aggregator = __get_first_agg(port);
|
||||
ad_agg_selection_logic(aggregator);
|
||||
ad_agg_selection_logic(aggregator, update_slave_arr);
|
||||
}
|
||||
|
||||
/* Decide if "agg" is a better choice for the new active aggregator that
|
||||
|
@ -1464,6 +1443,7 @@ static int agg_device_up(const struct aggregator *agg)
|
|||
/**
|
||||
* ad_agg_selection_logic - select an aggregation group for a team
|
||||
* @aggregator: the aggregator we're looking at
|
||||
* @update_slave_arr: Does slave array need update?
|
||||
*
|
||||
* It is assumed that only one aggregator may be selected for a team.
|
||||
*
|
||||
|
@ -1486,7 +1466,8 @@ static int agg_device_up(const struct aggregator *agg)
|
|||
* __get_active_agg() won't work correctly. This function should be better
|
||||
* called with the bond itself, and retrieve the first agg from it.
|
||||
*/
|
||||
static void ad_agg_selection_logic(struct aggregator *agg)
|
||||
static void ad_agg_selection_logic(struct aggregator *agg,
|
||||
bool *update_slave_arr)
|
||||
{
|
||||
struct aggregator *best, *active, *origin;
|
||||
struct bonding *bond = agg->slave->bond;
|
||||
|
@ -1579,6 +1560,8 @@ static void ad_agg_selection_logic(struct aggregator *agg)
|
|||
__disable_port(port);
|
||||
}
|
||||
}
|
||||
/* Slave array needs update. */
|
||||
*update_slave_arr = true;
|
||||
}
|
||||
|
||||
/* if the selected aggregator is of join individuals
|
||||
|
@ -1707,24 +1690,30 @@ static void ad_initialize_port(struct port *port, int lacp_fast)
|
|||
/**
|
||||
* ad_enable_collecting_distributing - enable a port's transmit/receive
|
||||
* @port: the port we're looking at
|
||||
* @update_slave_arr: Does slave array need update?
|
||||
*
|
||||
* Enable @port if it's in an active aggregator
|
||||
*/
|
||||
static void ad_enable_collecting_distributing(struct port *port)
|
||||
static void ad_enable_collecting_distributing(struct port *port,
|
||||
bool *update_slave_arr)
|
||||
{
|
||||
if (port->aggregator->is_active) {
|
||||
pr_debug("Enabling port %d(LAG %d)\n",
|
||||
port->actor_port_number,
|
||||
port->aggregator->aggregator_identifier);
|
||||
__enable_port(port);
|
||||
/* Slave array needs update */
|
||||
*update_slave_arr = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ad_disable_collecting_distributing - disable a port's transmit/receive
|
||||
* @port: the port we're looking at
|
||||
* @update_slave_arr: Does slave array need update?
|
||||
*/
|
||||
static void ad_disable_collecting_distributing(struct port *port)
|
||||
static void ad_disable_collecting_distributing(struct port *port,
|
||||
bool *update_slave_arr)
|
||||
{
|
||||
if (port->aggregator &&
|
||||
!MAC_ADDRESS_EQUAL(&(port->aggregator->partner_system),
|
||||
|
@ -1733,6 +1722,8 @@ static void ad_disable_collecting_distributing(struct port *port)
|
|||
port->actor_port_number,
|
||||
port->aggregator->aggregator_identifier);
|
||||
__disable_port(port);
|
||||
/* Slave array needs an update */
|
||||
*update_slave_arr = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1843,7 +1834,6 @@ void bond_3ad_bind_slave(struct slave *slave)
|
|||
|
||||
ad_initialize_port(port, bond->params.lacp_fast);
|
||||
|
||||
__initialize_port_locks(slave);
|
||||
port->slave = slave;
|
||||
port->actor_port_number = SLAVE_AD_INFO(slave)->id;
|
||||
/* key is determined according to the link speed, duplex and user key(which
|
||||
|
@ -1898,7 +1888,10 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
|||
struct bonding *bond = slave->bond;
|
||||
struct slave *slave_iter;
|
||||
struct list_head *iter;
|
||||
bool dummy_slave_update; /* Ignore this value as caller updates array */
|
||||
|
||||
/* Sync against bond_3ad_state_machine_handler() */
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
|
||||
|
@ -1906,7 +1899,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
|||
if (!port->slave) {
|
||||
netdev_warn(bond->dev, "Trying to unbind an uninitialized port on %s\n",
|
||||
slave->dev->name);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
netdev_dbg(bond->dev, "Unbinding Link Aggregation Group %d\n",
|
||||
|
@ -1979,7 +1972,8 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
|||
ad_clear_agg(aggregator);
|
||||
|
||||
if (select_new_active_agg)
|
||||
ad_agg_selection_logic(__get_first_agg(port));
|
||||
ad_agg_selection_logic(__get_first_agg(port),
|
||||
&dummy_slave_update);
|
||||
} else {
|
||||
netdev_warn(bond->dev, "unbinding aggregator, and could not find a new aggregator for its ports\n");
|
||||
}
|
||||
|
@ -1994,7 +1988,8 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
|||
/* select new active aggregator */
|
||||
temp_aggregator = __get_first_agg(port);
|
||||
if (temp_aggregator)
|
||||
ad_agg_selection_logic(temp_aggregator);
|
||||
ad_agg_selection_logic(temp_aggregator,
|
||||
&dummy_slave_update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2024,7 +2019,8 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
|||
if (select_new_active_agg) {
|
||||
netdev_info(bond->dev, "Removing an active aggregator\n");
|
||||
/* select new active aggregator */
|
||||
ad_agg_selection_logic(__get_first_agg(port));
|
||||
ad_agg_selection_logic(__get_first_agg(port),
|
||||
&dummy_slave_update);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -2032,6 +2028,9 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
|||
}
|
||||
}
|
||||
port->slave = NULL;
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2056,8 +2055,13 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
|
|||
struct slave *slave;
|
||||
struct port *port;
|
||||
bool should_notify_rtnl = BOND_SLAVE_NOTIFY_LATER;
|
||||
bool update_slave_arr = false;
|
||||
|
||||
read_lock(&bond->lock);
|
||||
/* Lock to protect data accessed by all (e.g., port->sm_vars) and
|
||||
* against running with bond_3ad_unbind_slave. ad_rx_machine may run
|
||||
* concurrently due to incoming LACPDU as well.
|
||||
*/
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
rcu_read_lock();
|
||||
|
||||
/* check if there are any slaves */
|
||||
|
@ -2079,7 +2083,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
|
|||
}
|
||||
|
||||
aggregator = __get_first_agg(port);
|
||||
ad_agg_selection_logic(aggregator);
|
||||
ad_agg_selection_logic(aggregator, &update_slave_arr);
|
||||
}
|
||||
bond_3ad_set_carrier(bond);
|
||||
}
|
||||
|
@ -2093,23 +2097,15 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
|
|||
goto re_arm;
|
||||
}
|
||||
|
||||
/* Lock around state machines to protect data accessed
|
||||
* by all (e.g., port->sm_vars). ad_rx_machine may run
|
||||
* concurrently due to incoming LACPDU.
|
||||
*/
|
||||
__get_state_machine_lock(port);
|
||||
|
||||
ad_rx_machine(NULL, port);
|
||||
ad_periodic_machine(port);
|
||||
ad_port_selection_logic(port);
|
||||
ad_mux_machine(port);
|
||||
ad_port_selection_logic(port, &update_slave_arr);
|
||||
ad_mux_machine(port, &update_slave_arr);
|
||||
ad_tx_machine(port);
|
||||
|
||||
/* turn off the BEGIN bit, since we already handled it */
|
||||
if (port->sm_vars & AD_PORT_BEGIN)
|
||||
port->sm_vars &= ~AD_PORT_BEGIN;
|
||||
|
||||
__release_state_machine_lock(port);
|
||||
}
|
||||
|
||||
re_arm:
|
||||
|
@ -2120,7 +2116,10 @@ re_arm:
|
|||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
read_unlock(&bond->lock);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
|
||||
if (update_slave_arr)
|
||||
bond_slave_arr_work_rearm(bond, 0);
|
||||
|
||||
if (should_notify_rtnl && rtnl_trylock()) {
|
||||
bond_slave_state_notify(bond);
|
||||
|
@ -2161,9 +2160,9 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave,
|
|||
netdev_dbg(slave->bond->dev, "Received LACPDU on port %d\n",
|
||||
port->actor_port_number);
|
||||
/* Protect against concurrent state machines */
|
||||
__get_state_machine_lock(port);
|
||||
spin_lock(&slave->bond->mode_lock);
|
||||
ad_rx_machine(lacpdu, port);
|
||||
__release_state_machine_lock(port);
|
||||
spin_unlock(&slave->bond->mode_lock);
|
||||
break;
|
||||
|
||||
case AD_TYPE_MARKER:
|
||||
|
@ -2213,7 +2212,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
|
|||
return;
|
||||
}
|
||||
|
||||
__get_state_machine_lock(port);
|
||||
spin_lock_bh(&slave->bond->mode_lock);
|
||||
|
||||
port->actor_admin_port_key &= ~AD_SPEED_KEY_BITS;
|
||||
port->actor_oper_port_key = port->actor_admin_port_key |=
|
||||
|
@ -2224,7 +2223,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
|
|||
*/
|
||||
port->sm_vars |= AD_PORT_BEGIN;
|
||||
|
||||
__release_state_machine_lock(port);
|
||||
spin_unlock_bh(&slave->bond->mode_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2246,7 +2245,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
|
|||
return;
|
||||
}
|
||||
|
||||
__get_state_machine_lock(port);
|
||||
spin_lock_bh(&slave->bond->mode_lock);
|
||||
|
||||
port->actor_admin_port_key &= ~AD_DUPLEX_KEY_BITS;
|
||||
port->actor_oper_port_key = port->actor_admin_port_key |=
|
||||
|
@ -2257,7 +2256,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
|
|||
*/
|
||||
port->sm_vars |= AD_PORT_BEGIN;
|
||||
|
||||
__release_state_machine_lock(port);
|
||||
spin_unlock_bh(&slave->bond->mode_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2280,7 +2279,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
|
|||
return;
|
||||
}
|
||||
|
||||
__get_state_machine_lock(port);
|
||||
spin_lock_bh(&slave->bond->mode_lock);
|
||||
/* on link down we are zeroing duplex and speed since
|
||||
* some of the adaptors(ce1000.lan) report full duplex/speed
|
||||
* instead of N/A(duplex) / 0(speed).
|
||||
|
@ -2311,7 +2310,12 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
|
|||
*/
|
||||
port->sm_vars |= AD_PORT_BEGIN;
|
||||
|
||||
__release_state_machine_lock(port);
|
||||
spin_unlock_bh(&slave->bond->mode_lock);
|
||||
|
||||
/* RTNL is held and mode_lock is released so it's safe
|
||||
* to update slave_array here.
|
||||
*/
|
||||
bond_update_slave_arr(slave->bond, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2395,7 +2399,6 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Wrapper used to hold bond->lock so no slave manipulation can occur */
|
||||
int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
|
||||
{
|
||||
int ret;
|
||||
|
@ -2407,90 +2410,19 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(dev);
|
||||
struct slave *slave, *first_ok_slave;
|
||||
struct aggregator *agg;
|
||||
struct ad_info ad_info;
|
||||
struct list_head *iter;
|
||||
int slaves_in_agg;
|
||||
int slave_agg_no;
|
||||
int agg_id;
|
||||
|
||||
if (__bond_3ad_get_active_agg_info(bond, &ad_info)) {
|
||||
netdev_dbg(dev, "__bond_3ad_get_active_agg_info failed\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
slaves_in_agg = ad_info.ports;
|
||||
agg_id = ad_info.aggregator_id;
|
||||
|
||||
if (slaves_in_agg == 0) {
|
||||
netdev_dbg(dev, "active aggregator is empty\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
slave_agg_no = bond_xmit_hash(bond, skb) % slaves_in_agg;
|
||||
first_ok_slave = NULL;
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
agg = SLAVE_AD_INFO(slave)->port.aggregator;
|
||||
if (!agg || agg->aggregator_identifier != agg_id)
|
||||
continue;
|
||||
|
||||
if (slave_agg_no >= 0) {
|
||||
if (!first_ok_slave && bond_slave_can_tx(slave))
|
||||
first_ok_slave = slave;
|
||||
slave_agg_no--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bond_slave_can_tx(slave)) {
|
||||
bond_dev_queue_xmit(bond, skb, slave->dev);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (slave_agg_no >= 0) {
|
||||
netdev_err(dev, "Couldn't find a slave to tx on for aggregator ID %d\n",
|
||||
agg_id);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* we couldn't find any suitable slave after the agg_no, so use the
|
||||
* first suitable found, if found.
|
||||
*/
|
||||
if (first_ok_slave)
|
||||
bond_dev_queue_xmit(bond, skb, first_ok_slave->dev);
|
||||
else
|
||||
goto err_free;
|
||||
|
||||
out:
|
||||
return NETDEV_TX_OK;
|
||||
err_free:
|
||||
/* no suitable interface, frame not sent */
|
||||
dev_kfree_skb_any(skb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *slave)
|
||||
{
|
||||
int ret = RX_HANDLER_ANOTHER;
|
||||
struct lacpdu *lacpdu, _lacpdu;
|
||||
|
||||
if (skb->protocol != PKT_TYPE_LACPDU)
|
||||
return ret;
|
||||
return RX_HANDLER_ANOTHER;
|
||||
|
||||
lacpdu = skb_header_pointer(skb, 0, sizeof(_lacpdu), &_lacpdu);
|
||||
if (!lacpdu)
|
||||
return ret;
|
||||
return RX_HANDLER_ANOTHER;
|
||||
|
||||
read_lock(&bond->lock);
|
||||
ret = bond_3ad_rx_indication(lacpdu, slave, skb->len);
|
||||
read_unlock(&bond->lock);
|
||||
return ret;
|
||||
return bond_3ad_rx_indication(lacpdu, slave, skb->len);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2500,7 +2432,7 @@ int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
|
|||
* When modify lacp_rate parameter via sysfs,
|
||||
* update actor_oper_port_state of each port.
|
||||
*
|
||||
* Hold slave->state_machine_lock,
|
||||
* Hold bond->mode_lock,
|
||||
* so we can modify port->actor_oper_port_state,
|
||||
* no matter bond is up or down.
|
||||
*/
|
||||
|
@ -2512,13 +2444,13 @@ void bond_3ad_update_lacp_rate(struct bonding *bond)
|
|||
int lacp_fast;
|
||||
|
||||
lacp_fast = bond->params.lacp_fast;
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
__get_state_machine_lock(port);
|
||||
if (lacp_fast)
|
||||
port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT;
|
||||
else
|
||||
port->actor_oper_port_state &= ~AD_STATE_LACP_TIMEOUT;
|
||||
__release_state_machine_lock(port);
|
||||
}
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
|
|
@ -259,7 +259,6 @@ struct ad_bond_info {
|
|||
struct ad_slave_info {
|
||||
struct aggregator aggregator; /* 802.3ad aggregator structure */
|
||||
struct port port; /* 802.3ad port structure */
|
||||
spinlock_t state_machine_lock; /* mutex state machines vs. incoming LACPDU */
|
||||
u16 id;
|
||||
};
|
||||
|
||||
|
|
|
@ -100,27 +100,6 @@ static inline u8 _simple_hash(const u8 *hash_start, int hash_size)
|
|||
|
||||
/*********************** tlb specific functions ***************************/
|
||||
|
||||
static inline void _lock_tx_hashtbl_bh(struct bonding *bond)
|
||||
{
|
||||
spin_lock_bh(&(BOND_ALB_INFO(bond).tx_hashtbl_lock));
|
||||
}
|
||||
|
||||
static inline void _unlock_tx_hashtbl_bh(struct bonding *bond)
|
||||
{
|
||||
spin_unlock_bh(&(BOND_ALB_INFO(bond).tx_hashtbl_lock));
|
||||
}
|
||||
|
||||
static inline void _lock_tx_hashtbl(struct bonding *bond)
|
||||
{
|
||||
spin_lock(&(BOND_ALB_INFO(bond).tx_hashtbl_lock));
|
||||
}
|
||||
|
||||
static inline void _unlock_tx_hashtbl(struct bonding *bond)
|
||||
{
|
||||
spin_unlock(&(BOND_ALB_INFO(bond).tx_hashtbl_lock));
|
||||
}
|
||||
|
||||
/* Caller must hold tx_hashtbl lock */
|
||||
static inline void tlb_init_table_entry(struct tlb_client_info *entry, int save_load)
|
||||
{
|
||||
if (save_load) {
|
||||
|
@ -140,7 +119,6 @@ static inline void tlb_init_slave(struct slave *slave)
|
|||
SLAVE_TLB_INFO(slave).head = TLB_NULL_INDEX;
|
||||
}
|
||||
|
||||
/* Caller must hold bond lock for read, BH disabled */
|
||||
static void __tlb_clear_slave(struct bonding *bond, struct slave *slave,
|
||||
int save_load)
|
||||
{
|
||||
|
@ -163,13 +141,12 @@ static void __tlb_clear_slave(struct bonding *bond, struct slave *slave,
|
|||
tlb_init_slave(slave);
|
||||
}
|
||||
|
||||
/* Caller must hold bond lock for read */
|
||||
static void tlb_clear_slave(struct bonding *bond, struct slave *slave,
|
||||
int save_load)
|
||||
{
|
||||
_lock_tx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
__tlb_clear_slave(bond, slave, save_load);
|
||||
_unlock_tx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
/* Must be called before starting the monitor timer */
|
||||
|
@ -184,14 +161,14 @@ static int tlb_initialize(struct bonding *bond)
|
|||
if (!new_hashtbl)
|
||||
return -1;
|
||||
|
||||
_lock_tx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
bond_info->tx_hashtbl = new_hashtbl;
|
||||
|
||||
for (i = 0; i < TLB_HASH_TABLE_SIZE; i++)
|
||||
tlb_init_table_entry(&bond_info->tx_hashtbl[i], 0);
|
||||
|
||||
_unlock_tx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -200,18 +177,13 @@ static int tlb_initialize(struct bonding *bond)
|
|||
static void tlb_deinitialize(struct bonding *bond)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
struct tlb_up_slave *arr;
|
||||
|
||||
_lock_tx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
kfree(bond_info->tx_hashtbl);
|
||||
bond_info->tx_hashtbl = NULL;
|
||||
|
||||
_unlock_tx_hashtbl_bh(bond);
|
||||
|
||||
arr = rtnl_dereference(bond_info->slave_arr);
|
||||
if (arr)
|
||||
kfree_rcu(arr, rcu);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
static long long compute_gap(struct slave *slave)
|
||||
|
@ -220,7 +192,6 @@ static long long compute_gap(struct slave *slave)
|
|||
(s64) (SLAVE_TLB_INFO(slave).load << 3); /* Bytes to bits */
|
||||
}
|
||||
|
||||
/* Caller must hold bond lock for read */
|
||||
static struct slave *tlb_get_least_loaded_slave(struct bonding *bond)
|
||||
{
|
||||
struct slave *slave, *least_loaded;
|
||||
|
@ -281,42 +252,23 @@ static struct slave *__tlb_choose_channel(struct bonding *bond, u32 hash_index,
|
|||
return assigned_slave;
|
||||
}
|
||||
|
||||
/* Caller must hold bond lock for read */
|
||||
static struct slave *tlb_choose_channel(struct bonding *bond, u32 hash_index,
|
||||
u32 skb_len)
|
||||
{
|
||||
struct slave *tx_slave;
|
||||
/*
|
||||
* We don't need to disable softirq here, becase
|
||||
|
||||
/* We don't need to disable softirq here, becase
|
||||
* tlb_choose_channel() is only called by bond_alb_xmit()
|
||||
* which already has softirq disabled.
|
||||
*/
|
||||
_lock_tx_hashtbl(bond);
|
||||
spin_lock(&bond->mode_lock);
|
||||
tx_slave = __tlb_choose_channel(bond, hash_index, skb_len);
|
||||
_unlock_tx_hashtbl(bond);
|
||||
spin_unlock(&bond->mode_lock);
|
||||
|
||||
return tx_slave;
|
||||
}
|
||||
|
||||
/*********************** rlb specific functions ***************************/
|
||||
static inline void _lock_rx_hashtbl_bh(struct bonding *bond)
|
||||
{
|
||||
spin_lock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
|
||||
}
|
||||
|
||||
static inline void _unlock_rx_hashtbl_bh(struct bonding *bond)
|
||||
{
|
||||
spin_unlock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
|
||||
}
|
||||
|
||||
static inline void _lock_rx_hashtbl(struct bonding *bond)
|
||||
{
|
||||
spin_lock(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
|
||||
}
|
||||
|
||||
static inline void _unlock_rx_hashtbl(struct bonding *bond)
|
||||
{
|
||||
spin_unlock(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
|
||||
}
|
||||
|
||||
/* when an ARP REPLY is received from a client update its info
|
||||
* in the rx_hashtbl
|
||||
|
@ -327,7 +279,7 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp)
|
|||
struct rlb_client_info *client_info;
|
||||
u32 hash_index;
|
||||
|
||||
_lock_rx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
hash_index = _simple_hash((u8 *)&(arp->ip_src), sizeof(arp->ip_src));
|
||||
client_info = &(bond_info->rx_hashtbl[hash_index]);
|
||||
|
@ -342,7 +294,7 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp)
|
|||
bond_info->rx_ntt = 1;
|
||||
}
|
||||
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
static int rlb_arp_recv(const struct sk_buff *skb, struct bonding *bond,
|
||||
|
@ -378,40 +330,7 @@ out:
|
|||
return RX_HANDLER_ANOTHER;
|
||||
}
|
||||
|
||||
/* Caller must hold bond lock for read */
|
||||
static struct slave *rlb_next_rx_slave(struct bonding *bond)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
struct slave *before = NULL, *rx_slave = NULL, *slave;
|
||||
struct list_head *iter;
|
||||
bool found = false;
|
||||
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
if (!bond_slave_can_tx(slave))
|
||||
continue;
|
||||
if (!found) {
|
||||
if (!before || before->speed < slave->speed)
|
||||
before = slave;
|
||||
} else {
|
||||
if (!rx_slave || rx_slave->speed < slave->speed)
|
||||
rx_slave = slave;
|
||||
}
|
||||
if (slave == bond_info->rx_slave)
|
||||
found = true;
|
||||
}
|
||||
/* we didn't find anything after the current or we have something
|
||||
* better before and up to the current slave
|
||||
*/
|
||||
if (!rx_slave || (before && rx_slave->speed < before->speed))
|
||||
rx_slave = before;
|
||||
|
||||
if (rx_slave)
|
||||
bond_info->rx_slave = rx_slave;
|
||||
|
||||
return rx_slave;
|
||||
}
|
||||
|
||||
/* Caller must hold rcu_read_lock() for read */
|
||||
/* Caller must hold rcu_read_lock() */
|
||||
static struct slave *__rlb_next_rx_slave(struct bonding *bond)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
|
@ -444,14 +363,28 @@ static struct slave *__rlb_next_rx_slave(struct bonding *bond)
|
|||
return rx_slave;
|
||||
}
|
||||
|
||||
/* Caller must hold RTNL, rcu_read_lock is obtained only to silence checkers */
|
||||
static struct slave *rlb_next_rx_slave(struct bonding *bond)
|
||||
{
|
||||
struct slave *rx_slave;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
rcu_read_lock();
|
||||
rx_slave = __rlb_next_rx_slave(bond);
|
||||
rcu_read_unlock();
|
||||
|
||||
return rx_slave;
|
||||
}
|
||||
|
||||
/* teach the switch the mac of a disabled slave
|
||||
* on the primary for fault tolerance
|
||||
*
|
||||
* Caller must hold bond->curr_slave_lock for write or bond lock for write
|
||||
* Caller must hold RTNL
|
||||
*/
|
||||
static void rlb_teach_disabled_mac_on_primary(struct bonding *bond, u8 addr[])
|
||||
{
|
||||
struct slave *curr_active = bond_deref_active_protected(bond);
|
||||
struct slave *curr_active = rtnl_dereference(bond->curr_active_slave);
|
||||
|
||||
if (!curr_active)
|
||||
return;
|
||||
|
@ -479,7 +412,7 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave)
|
|||
u32 index, next_index;
|
||||
|
||||
/* clear slave from rx_hashtbl */
|
||||
_lock_rx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
rx_hash_table = bond_info->rx_hashtbl;
|
||||
index = bond_info->rx_hashtbl_used_head;
|
||||
|
@ -510,14 +443,10 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave)
|
|||
}
|
||||
}
|
||||
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
|
||||
write_lock_bh(&bond->curr_slave_lock);
|
||||
|
||||
if (slave != bond_deref_active_protected(bond))
|
||||
if (slave != rtnl_dereference(bond->curr_active_slave))
|
||||
rlb_teach_disabled_mac_on_primary(bond, slave->dev->dev_addr);
|
||||
|
||||
write_unlock_bh(&bond->curr_slave_lock);
|
||||
}
|
||||
|
||||
static void rlb_update_client(struct rlb_client_info *client_info)
|
||||
|
@ -565,7 +494,7 @@ static void rlb_update_rx_clients(struct bonding *bond)
|
|||
struct rlb_client_info *client_info;
|
||||
u32 hash_index;
|
||||
|
||||
_lock_rx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
hash_index = bond_info->rx_hashtbl_used_head;
|
||||
for (; hash_index != RLB_NULL_INDEX;
|
||||
|
@ -583,7 +512,7 @@ static void rlb_update_rx_clients(struct bonding *bond)
|
|||
*/
|
||||
bond_info->rlb_update_delay_counter = RLB_UPDATE_DELAY;
|
||||
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
/* The slave was assigned a new mac address - update the clients */
|
||||
|
@ -594,7 +523,7 @@ static void rlb_req_update_slave_clients(struct bonding *bond, struct slave *sla
|
|||
int ntt = 0;
|
||||
u32 hash_index;
|
||||
|
||||
_lock_rx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
hash_index = bond_info->rx_hashtbl_used_head;
|
||||
for (; hash_index != RLB_NULL_INDEX;
|
||||
|
@ -615,7 +544,7 @@ static void rlb_req_update_slave_clients(struct bonding *bond, struct slave *sla
|
|||
bond_info->rlb_update_retry_counter = RLB_UPDATE_RETRY;
|
||||
}
|
||||
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
/* mark all clients using src_ip to be updated */
|
||||
|
@ -625,7 +554,7 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip)
|
|||
struct rlb_client_info *client_info;
|
||||
u32 hash_index;
|
||||
|
||||
_lock_rx_hashtbl(bond);
|
||||
spin_lock(&bond->mode_lock);
|
||||
|
||||
hash_index = bond_info->rx_hashtbl_used_head;
|
||||
for (; hash_index != RLB_NULL_INDEX;
|
||||
|
@ -636,7 +565,7 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip)
|
|||
netdev_err(bond->dev, "found a client with no channel in the client's hash table\n");
|
||||
continue;
|
||||
}
|
||||
/*update all clients using this src_ip, that are not assigned
|
||||
/* update all clients using this src_ip, that are not assigned
|
||||
* to the team's address (curr_active_slave) and have a known
|
||||
* unicast mac address.
|
||||
*/
|
||||
|
@ -649,10 +578,9 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip)
|
|||
}
|
||||
}
|
||||
|
||||
_unlock_rx_hashtbl(bond);
|
||||
spin_unlock(&bond->mode_lock);
|
||||
}
|
||||
|
||||
/* Caller must hold both bond and ptr locks for read */
|
||||
static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bond)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
|
@ -661,7 +589,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
|
|||
struct rlb_client_info *client_info;
|
||||
u32 hash_index = 0;
|
||||
|
||||
_lock_rx_hashtbl(bond);
|
||||
spin_lock(&bond->mode_lock);
|
||||
|
||||
curr_active_slave = rcu_dereference(bond->curr_active_slave);
|
||||
|
||||
|
@ -680,7 +608,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
|
|||
|
||||
assigned_slave = client_info->slave;
|
||||
if (assigned_slave) {
|
||||
_unlock_rx_hashtbl(bond);
|
||||
spin_unlock(&bond->mode_lock);
|
||||
return assigned_slave;
|
||||
}
|
||||
} else {
|
||||
|
@ -742,7 +670,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon
|
|||
}
|
||||
}
|
||||
|
||||
_unlock_rx_hashtbl(bond);
|
||||
spin_unlock(&bond->mode_lock);
|
||||
|
||||
return assigned_slave;
|
||||
}
|
||||
|
@ -763,9 +691,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
|
|||
return NULL;
|
||||
|
||||
if (arp->op_code == htons(ARPOP_REPLY)) {
|
||||
/* the arp must be sent on the selected
|
||||
* rx channel
|
||||
*/
|
||||
/* the arp must be sent on the selected rx channel */
|
||||
tx_slave = rlb_choose_channel(skb, bond);
|
||||
if (tx_slave)
|
||||
ether_addr_copy(arp->mac_src, tx_slave->dev->dev_addr);
|
||||
|
@ -795,7 +721,6 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
|
|||
return tx_slave;
|
||||
}
|
||||
|
||||
/* Caller must hold bond lock for read */
|
||||
static void rlb_rebalance(struct bonding *bond)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
|
@ -804,7 +729,7 @@ static void rlb_rebalance(struct bonding *bond)
|
|||
int ntt;
|
||||
u32 hash_index;
|
||||
|
||||
_lock_rx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
ntt = 0;
|
||||
hash_index = bond_info->rx_hashtbl_used_head;
|
||||
|
@ -822,10 +747,10 @@ static void rlb_rebalance(struct bonding *bond)
|
|||
/* update the team's flag only after the whole iteration */
|
||||
if (ntt)
|
||||
bond_info->rx_ntt = 1;
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
/* Caller must hold rx_hashtbl lock */
|
||||
/* Caller must hold mode_lock */
|
||||
static void rlb_init_table_entry_dst(struct rlb_client_info *entry)
|
||||
{
|
||||
entry->used_next = RLB_NULL_INDEX;
|
||||
|
@ -913,15 +838,16 @@ static void rlb_src_link(struct bonding *bond, u32 ip_src_hash, u32 ip_dst_hash)
|
|||
bond_info->rx_hashtbl[ip_src_hash].src_first = ip_dst_hash;
|
||||
}
|
||||
|
||||
/* deletes all rx_hashtbl entries with arp->ip_src if their mac_src does
|
||||
* not match arp->mac_src */
|
||||
/* deletes all rx_hashtbl entries with arp->ip_src if their mac_src does
|
||||
* not match arp->mac_src
|
||||
*/
|
||||
static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
u32 ip_src_hash = _simple_hash((u8 *)&(arp->ip_src), sizeof(arp->ip_src));
|
||||
u32 index;
|
||||
|
||||
_lock_rx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
index = bond_info->rx_hashtbl[ip_src_hash].src_first;
|
||||
while (index != RLB_NULL_INDEX) {
|
||||
|
@ -932,7 +858,7 @@ static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp)
|
|||
rlb_delete_table_entry(bond, index);
|
||||
index = next_index;
|
||||
}
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
static int rlb_initialize(struct bonding *bond)
|
||||
|
@ -946,7 +872,7 @@ static int rlb_initialize(struct bonding *bond)
|
|||
if (!new_hashtbl)
|
||||
return -1;
|
||||
|
||||
_lock_rx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
bond_info->rx_hashtbl = new_hashtbl;
|
||||
|
||||
|
@ -955,7 +881,7 @@ static int rlb_initialize(struct bonding *bond)
|
|||
for (i = 0; i < RLB_HASH_TABLE_SIZE; i++)
|
||||
rlb_init_table_entry(bond_info->rx_hashtbl + i);
|
||||
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
|
||||
/* register to receive ARPs */
|
||||
bond->recv_probe = rlb_arp_recv;
|
||||
|
@ -967,13 +893,13 @@ static void rlb_deinitialize(struct bonding *bond)
|
|||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
|
||||
_lock_rx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
kfree(bond_info->rx_hashtbl);
|
||||
bond_info->rx_hashtbl = NULL;
|
||||
bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX;
|
||||
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
static void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id)
|
||||
|
@ -981,7 +907,7 @@ static void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id)
|
|||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
u32 curr_index;
|
||||
|
||||
_lock_rx_hashtbl_bh(bond);
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
curr_index = bond_info->rx_hashtbl_used_head;
|
||||
while (curr_index != RLB_NULL_INDEX) {
|
||||
|
@ -994,7 +920,7 @@ static void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id)
|
|||
curr_index = next_index;
|
||||
}
|
||||
|
||||
_unlock_rx_hashtbl_bh(bond);
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
}
|
||||
|
||||
/*********************** tlb/rlb shared functions *********************/
|
||||
|
@ -1091,8 +1017,9 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* for rlb each slave must have a unique hw mac addresses so that */
|
||||
/* each slave will receive packets destined to a different mac */
|
||||
/* for rlb each slave must have a unique hw mac addresses so that
|
||||
* each slave will receive packets destined to a different mac
|
||||
*/
|
||||
memcpy(s_addr.sa_data, addr, dev->addr_len);
|
||||
s_addr.sa_family = dev->type;
|
||||
if (dev_set_mac_address(dev, &s_addr)) {
|
||||
|
@ -1103,13 +1030,10 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Swap MAC addresses between two slaves.
|
||||
/* Swap MAC addresses between two slaves.
|
||||
*
|
||||
* Called with RTNL held, and no other locks.
|
||||
*
|
||||
*/
|
||||
|
||||
static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
|
||||
{
|
||||
u8 tmp_mac_addr[ETH_ALEN];
|
||||
|
@ -1120,8 +1044,7 @@ static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* Send learning packets after MAC address swap.
|
||||
/* Send learning packets after MAC address swap.
|
||||
*
|
||||
* Called with RTNL and no other locks
|
||||
*/
|
||||
|
@ -1194,7 +1117,6 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla
|
|||
found_slave = bond_slave_has_mac(bond, slave->perm_hwaddr);
|
||||
|
||||
if (found_slave) {
|
||||
/* locking: needs RTNL and nothing else */
|
||||
alb_swap_mac_addr(slave, found_slave);
|
||||
alb_fasten_mac_swap(bond, slave, found_slave);
|
||||
}
|
||||
|
@ -1243,7 +1165,8 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav
|
|||
return 0;
|
||||
|
||||
/* Try setting slave mac to bond address and fall-through
|
||||
to code handling that situation below... */
|
||||
* to code handling that situation below...
|
||||
*/
|
||||
alb_set_slave_mac_addr(slave, bond->dev->dev_addr);
|
||||
}
|
||||
|
||||
|
@ -1351,7 +1274,6 @@ int bond_alb_initialize(struct bonding *bond, int rlb_enabled)
|
|||
|
||||
if (rlb_enabled) {
|
||||
bond->alb_info.rlb_enabled = 1;
|
||||
/* initialize rlb */
|
||||
res = rlb_initialize(bond);
|
||||
if (res) {
|
||||
tlb_deinitialize(bond);
|
||||
|
@ -1375,7 +1297,7 @@ void bond_alb_deinitialize(struct bonding *bond)
|
|||
}
|
||||
|
||||
static int bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *tx_slave)
|
||||
struct slave *tx_slave)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
struct ethhdr *eth_data = eth_hdr(skb);
|
||||
|
@ -1388,7 +1310,7 @@ static int bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,
|
|||
}
|
||||
|
||||
if (tx_slave && bond_slave_can_tx(tx_slave)) {
|
||||
if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
|
||||
if (tx_slave != rcu_access_pointer(bond->curr_active_slave)) {
|
||||
ether_addr_copy(eth_data->h_source,
|
||||
tx_slave->dev->dev_addr);
|
||||
}
|
||||
|
@ -1398,9 +1320,9 @@ static int bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,
|
|||
}
|
||||
|
||||
if (tx_slave && bond->params.tlb_dynamic_lb) {
|
||||
_lock_tx_hashtbl(bond);
|
||||
spin_lock(&bond->mode_lock);
|
||||
__tlb_clear_slave(bond, tx_slave, 0);
|
||||
_unlock_tx_hashtbl(bond);
|
||||
spin_unlock(&bond->mode_lock);
|
||||
}
|
||||
|
||||
/* no suitable interface, frame not sent */
|
||||
|
@ -1409,39 +1331,9 @@ out:
|
|||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static int bond_tlb_update_slave_arr(struct bonding *bond,
|
||||
struct slave *skipslave)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
struct slave *tx_slave;
|
||||
struct list_head *iter;
|
||||
struct tlb_up_slave *new_arr, *old_arr;
|
||||
|
||||
new_arr = kzalloc(offsetof(struct tlb_up_slave, arr[bond->slave_cnt]),
|
||||
GFP_ATOMIC);
|
||||
if (!new_arr)
|
||||
return -ENOMEM;
|
||||
|
||||
bond_for_each_slave(bond, tx_slave, iter) {
|
||||
if (!bond_slave_can_tx(tx_slave))
|
||||
continue;
|
||||
if (skipslave == tx_slave)
|
||||
continue;
|
||||
new_arr->arr[new_arr->count++] = tx_slave;
|
||||
}
|
||||
|
||||
old_arr = rtnl_dereference(bond_info->slave_arr);
|
||||
rcu_assign_pointer(bond_info->slave_arr, new_arr);
|
||||
if (old_arr)
|
||||
kfree_rcu(old_arr, rcu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
struct ethhdr *eth_data;
|
||||
struct slave *tx_slave = NULL;
|
||||
u32 hash_index;
|
||||
|
@ -1462,12 +1354,14 @@ int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
hash_index & 0xFF,
|
||||
skb->len);
|
||||
} else {
|
||||
struct tlb_up_slave *slaves;
|
||||
struct bond_up_slave *slaves;
|
||||
unsigned int count;
|
||||
|
||||
slaves = rcu_dereference(bond_info->slave_arr);
|
||||
if (slaves && slaves->count)
|
||||
slaves = rcu_dereference(bond->slave_arr);
|
||||
count = slaves ? ACCESS_ONCE(slaves->count) : 0;
|
||||
if (likely(count))
|
||||
tx_slave = slaves->arr[hash_index %
|
||||
slaves->count];
|
||||
count];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1595,13 +1489,6 @@ void bond_alb_monitor(struct work_struct *work)
|
|||
if (bond_info->lp_counter >= BOND_ALB_LP_TICKS(bond)) {
|
||||
bool strict_match;
|
||||
|
||||
/* change of curr_active_slave involves swapping of mac addresses.
|
||||
* in order to avoid this swapping from happening while
|
||||
* sending the learning packets, the curr_slave_lock must be held for
|
||||
* read.
|
||||
*/
|
||||
read_lock(&bond->curr_slave_lock);
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
/* If updating current_active, use all currently
|
||||
* user mac addreses (!strict_match). Otherwise, only
|
||||
|
@ -1613,17 +1500,11 @@ void bond_alb_monitor(struct work_struct *work)
|
|||
alb_send_learning_packets(slave, slave->dev->dev_addr,
|
||||
strict_match);
|
||||
}
|
||||
|
||||
read_unlock(&bond->curr_slave_lock);
|
||||
|
||||
bond_info->lp_counter = 0;
|
||||
}
|
||||
|
||||
/* rebalance tx traffic */
|
||||
if (bond_info->tx_rebalance_counter >= BOND_TLB_REBALANCE_TICKS) {
|
||||
|
||||
read_lock(&bond->curr_slave_lock);
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
tlb_clear_slave(bond, slave, 1);
|
||||
if (slave == rcu_access_pointer(bond->curr_active_slave)) {
|
||||
|
@ -1633,19 +1514,14 @@ void bond_alb_monitor(struct work_struct *work)
|
|||
bond_info->unbalanced_load = 0;
|
||||
}
|
||||
}
|
||||
|
||||
read_unlock(&bond->curr_slave_lock);
|
||||
|
||||
bond_info->tx_rebalance_counter = 0;
|
||||
}
|
||||
|
||||
/* handle rlb stuff */
|
||||
if (bond_info->rlb_enabled) {
|
||||
if (bond_info->primary_is_promisc &&
|
||||
(++bond_info->rlb_promisc_timeout_counter >= RLB_PROMISC_TIMEOUT)) {
|
||||
|
||||
/*
|
||||
* dev_set_promiscuity requires rtnl and
|
||||
/* dev_set_promiscuity requires rtnl and
|
||||
* nothing else. Avoid race with bond_close.
|
||||
*/
|
||||
rcu_read_unlock();
|
||||
|
@ -1715,8 +1591,7 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove slave from tlb and rlb hash tables, and fix up MAC addresses
|
||||
/* Remove slave from tlb and rlb hash tables, and fix up MAC addresses
|
||||
* if necessary.
|
||||
*
|
||||
* Caller must hold RTNL and no other locks
|
||||
|
@ -1733,13 +1608,8 @@ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave)
|
|||
rlb_clear_slave(bond, slave);
|
||||
}
|
||||
|
||||
if (bond_is_nondyn_tlb(bond))
|
||||
if (bond_tlb_update_slave_arr(bond, slave))
|
||||
pr_err("Failed to build slave-array for TLB mode.\n");
|
||||
|
||||
}
|
||||
|
||||
/* Caller must hold bond lock for read */
|
||||
void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char link)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
|
@ -1762,7 +1632,7 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
|
|||
}
|
||||
|
||||
if (bond_is_nondyn_tlb(bond)) {
|
||||
if (bond_tlb_update_slave_arr(bond, NULL))
|
||||
if (bond_update_slave_arr(bond, NULL))
|
||||
pr_err("Failed to build slave-array for TLB mode.\n");
|
||||
}
|
||||
}
|
||||
|
@ -1775,22 +1645,14 @@ void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char
|
|||
* Set the bond->curr_active_slave to @new_slave and handle
|
||||
* mac address swapping and promiscuity changes as needed.
|
||||
*
|
||||
* If new_slave is NULL, caller must hold curr_slave_lock or
|
||||
* bond->lock for write.
|
||||
*
|
||||
* If new_slave is not NULL, caller must hold RTNL, curr_slave_lock
|
||||
* for write. Processing here may sleep, so no other locks may be held.
|
||||
* Caller must hold RTNL
|
||||
*/
|
||||
void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave)
|
||||
__releases(&bond->curr_slave_lock)
|
||||
__acquires(&bond->curr_slave_lock)
|
||||
{
|
||||
struct slave *swap_slave;
|
||||
struct slave *curr_active;
|
||||
|
||||
curr_active = rcu_dereference_protected(bond->curr_active_slave,
|
||||
!new_slave ||
|
||||
lockdep_is_held(&bond->curr_slave_lock));
|
||||
curr_active = rtnl_dereference(bond->curr_active_slave);
|
||||
if (curr_active == new_slave)
|
||||
return;
|
||||
|
||||
|
@ -1812,8 +1674,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
|
|||
if (!swap_slave)
|
||||
swap_slave = bond_slave_has_mac(bond, bond->dev->dev_addr);
|
||||
|
||||
/*
|
||||
* Arrange for swap_slave and new_slave to temporarily be
|
||||
/* Arrange for swap_slave and new_slave to temporarily be
|
||||
* ignored so we can mess with their MAC addresses without
|
||||
* fear of interference from transmit activity.
|
||||
*/
|
||||
|
@ -1821,10 +1682,6 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
|
|||
tlb_clear_slave(bond, swap_slave, 1);
|
||||
tlb_clear_slave(bond, new_slave, 1);
|
||||
|
||||
write_unlock_bh(&bond->curr_slave_lock);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* in TLB mode, the slave might flip down/up with the old dev_addr,
|
||||
* and thus filter bond->dev_addr's packets, so force bond's mac
|
||||
*/
|
||||
|
@ -1853,16 +1710,10 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
|
|||
alb_send_learning_packets(new_slave, bond->dev->dev_addr,
|
||||
false);
|
||||
}
|
||||
|
||||
write_lock_bh(&bond->curr_slave_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with RTNL
|
||||
*/
|
||||
/* Called with RTNL */
|
||||
int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
|
||||
__acquires(&bond->lock)
|
||||
__releases(&bond->lock)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct sockaddr *sa = addr;
|
||||
|
@ -1895,14 +1746,12 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr)
|
|||
} else {
|
||||
alb_set_slave_mac_addr(curr_active, bond_dev->dev_addr);
|
||||
|
||||
read_lock(&bond->lock);
|
||||
alb_send_learning_packets(curr_active,
|
||||
bond_dev->dev_addr, false);
|
||||
if (bond->alb_info.rlb_enabled) {
|
||||
/* inform clients mac address has changed */
|
||||
rlb_req_update_slave_clients(bond, curr_active);
|
||||
}
|
||||
read_unlock(&bond->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -139,24 +139,14 @@ struct tlb_slave_info {
|
|||
*/
|
||||
};
|
||||
|
||||
struct tlb_up_slave {
|
||||
unsigned int count;
|
||||
struct rcu_head rcu;
|
||||
struct slave *arr[0];
|
||||
};
|
||||
|
||||
struct alb_bond_info {
|
||||
struct tlb_client_info *tx_hashtbl; /* Dynamically allocated */
|
||||
spinlock_t tx_hashtbl_lock;
|
||||
u32 unbalanced_load;
|
||||
int tx_rebalance_counter;
|
||||
int lp_counter;
|
||||
/* -------- non-dynamic tlb mode only ---------*/
|
||||
struct tlb_up_slave __rcu *slave_arr; /* Up slaves */
|
||||
/* -------- rlb parameters -------- */
|
||||
int rlb_enabled;
|
||||
struct rlb_client_info *rx_hashtbl; /* Receive hash table */
|
||||
spinlock_t rx_hashtbl_lock;
|
||||
u32 rx_hashtbl_used_head;
|
||||
u8 rx_ntt; /* flag - need to transmit
|
||||
* to all rx clients
|
||||
|
|
|
@ -13,9 +13,7 @@
|
|||
|
||||
static struct dentry *bonding_debug_root;
|
||||
|
||||
/*
|
||||
* Show RLB hash table
|
||||
*/
|
||||
/* Show RLB hash table */
|
||||
static int bond_debug_rlb_hash_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct bonding *bond = m->private;
|
||||
|
@ -29,7 +27,7 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v)
|
|||
seq_printf(m, "SourceIP DestinationIP "
|
||||
"Destination MAC DEV\n");
|
||||
|
||||
spin_lock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
|
||||
spin_lock_bh(&bond->mode_lock);
|
||||
|
||||
hash_index = bond_info->rx_hashtbl_used_head;
|
||||
for (; hash_index != RLB_NULL_INDEX;
|
||||
|
@ -42,7 +40,7 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v)
|
|||
client_info->slave->dev->name);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock));
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -96,6 +96,10 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = {
|
|||
[IFLA_BOND_AD_INFO] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = {
|
||||
[IFLA_BOND_SLAVE_QUEUE_ID] = { .type = NLA_U16 },
|
||||
};
|
||||
|
||||
static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
|
||||
{
|
||||
if (tb[IFLA_ADDRESS]) {
|
||||
|
@ -107,6 +111,33 @@ static int bond_validate(struct nlattr *tb[], struct nlattr *data[])
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bond_slave_changelink(struct net_device *bond_dev,
|
||||
struct net_device *slave_dev,
|
||||
struct nlattr *tb[], struct nlattr *data[])
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct bond_opt_value newval;
|
||||
int err;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
if (data[IFLA_BOND_SLAVE_QUEUE_ID]) {
|
||||
u16 queue_id = nla_get_u16(data[IFLA_BOND_SLAVE_QUEUE_ID]);
|
||||
char queue_id_str[IFNAMSIZ + 7];
|
||||
|
||||
/* queue_id option setting expects slave_name:queue_id */
|
||||
snprintf(queue_id_str, sizeof(queue_id_str), "%s:%u\n",
|
||||
slave_dev->name, queue_id);
|
||||
bond_opt_initstr(&newval, queue_id_str);
|
||||
err = __bond_opt_set(bond, BOND_OPT_QUEUE_ID, &newval);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bond_changelink(struct net_device *bond_dev,
|
||||
struct nlattr *tb[], struct nlattr *data[])
|
||||
{
|
||||
|
@ -412,6 +443,7 @@ static int bond_fill_info(struct sk_buff *skb,
|
|||
unsigned int packets_per_slave;
|
||||
int ifindex, i, targets_added;
|
||||
struct nlattr *targets;
|
||||
struct slave *primary;
|
||||
|
||||
if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond)))
|
||||
goto nla_put_failure;
|
||||
|
@ -461,9 +493,9 @@ static int bond_fill_info(struct sk_buff *skb,
|
|||
bond->params.arp_all_targets))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (bond->primary_slave &&
|
||||
nla_put_u32(skb, IFLA_BOND_PRIMARY,
|
||||
bond->primary_slave->dev->ifindex))
|
||||
primary = rtnl_dereference(bond->primary_slave);
|
||||
if (primary &&
|
||||
nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put_u8(skb, IFLA_BOND_PRIMARY_RESELECT,
|
||||
|
@ -562,6 +594,9 @@ struct rtnl_link_ops bond_link_ops __read_mostly = {
|
|||
.get_num_tx_queues = bond_get_num_tx_queues,
|
||||
.get_num_rx_queues = bond_get_num_tx_queues, /* Use the same number
|
||||
as for TX queues */
|
||||
.slave_maxtype = IFLA_BOND_SLAVE_MAX,
|
||||
.slave_policy = bond_slave_policy,
|
||||
.slave_changelink = bond_slave_changelink,
|
||||
.get_slave_size = bond_get_slave_size,
|
||||
.fill_slave_info = bond_fill_slave_info,
|
||||
};
|
||||
|
|
|
@ -625,6 +625,8 @@ int __bond_opt_set(struct bonding *bond,
|
|||
out:
|
||||
if (ret)
|
||||
bond_opt_error_interpret(bond, opt, ret, val);
|
||||
else
|
||||
call_netdevice_notifiers(NETDEV_CHANGEINFODATA, bond->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -732,15 +734,13 @@ static int bond_option_active_slave_set(struct bonding *bond,
|
|||
}
|
||||
|
||||
block_netpoll_tx();
|
||||
write_lock_bh(&bond->curr_slave_lock);
|
||||
|
||||
/* check to see if we are clearing active */
|
||||
if (!slave_dev) {
|
||||
netdev_info(bond->dev, "Clearing current active slave\n");
|
||||
RCU_INIT_POINTER(bond->curr_active_slave, NULL);
|
||||
bond_select_active_slave(bond);
|
||||
} else {
|
||||
struct slave *old_active = bond_deref_active_protected(bond);
|
||||
struct slave *old_active = rtnl_dereference(bond->curr_active_slave);
|
||||
struct slave *new_active = bond_slave_get_rtnl(slave_dev);
|
||||
|
||||
BUG_ON(!new_active);
|
||||
|
@ -763,8 +763,6 @@ static int bond_option_active_slave_set(struct bonding *bond,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_unlock_bh(&bond->curr_slave_lock);
|
||||
unblock_netpoll_tx();
|
||||
|
||||
return ret;
|
||||
|
@ -953,14 +951,7 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
|
|||
|
||||
static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* not to race with bond_arp_rcv */
|
||||
write_lock_bh(&bond->lock);
|
||||
ret = _bond_option_arp_ip_target_add(bond, target);
|
||||
write_unlock_bh(&bond->lock);
|
||||
|
||||
return ret;
|
||||
return _bond_option_arp_ip_target_add(bond, target);
|
||||
}
|
||||
|
||||
static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
|
||||
|
@ -989,9 +980,6 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
|
|||
|
||||
netdev_info(bond->dev, "Removing ARP target %pI4\n", &target);
|
||||
|
||||
/* not to race with bond_arp_rcv */
|
||||
write_lock_bh(&bond->lock);
|
||||
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
targets_rx = slave->target_last_arp_rx;
|
||||
for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
|
||||
|
@ -1002,8 +990,6 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
|
|||
targets[i] = targets[i+1];
|
||||
targets[i] = 0;
|
||||
|
||||
write_unlock_bh(&bond->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1011,11 +997,8 @@ void bond_option_arp_ip_targets_clear(struct bonding *bond)
|
|||
{
|
||||
int i;
|
||||
|
||||
/* not to race with bond_arp_rcv */
|
||||
write_lock_bh(&bond->lock);
|
||||
for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
|
||||
_bond_options_arp_ip_target_set(bond, i, 0, 0);
|
||||
write_unlock_bh(&bond->lock);
|
||||
}
|
||||
|
||||
static int bond_option_arp_ip_targets_set(struct bonding *bond,
|
||||
|
@ -1079,8 +1062,6 @@ static int bond_option_primary_set(struct bonding *bond,
|
|||
struct slave *slave;
|
||||
|
||||
block_netpoll_tx();
|
||||
read_lock(&bond->lock);
|
||||
write_lock_bh(&bond->curr_slave_lock);
|
||||
|
||||
p = strchr(primary, '\n');
|
||||
if (p)
|
||||
|
@ -1088,7 +1069,7 @@ static int bond_option_primary_set(struct bonding *bond,
|
|||
/* check to see if we are clearing primary */
|
||||
if (!strlen(primary)) {
|
||||
netdev_info(bond->dev, "Setting primary slave to None\n");
|
||||
bond->primary_slave = NULL;
|
||||
RCU_INIT_POINTER(bond->primary_slave, NULL);
|
||||
memset(bond->params.primary, 0, sizeof(bond->params.primary));
|
||||
bond_select_active_slave(bond);
|
||||
goto out;
|
||||
|
@ -1098,16 +1079,16 @@ static int bond_option_primary_set(struct bonding *bond,
|
|||
if (strncmp(slave->dev->name, primary, IFNAMSIZ) == 0) {
|
||||
netdev_info(bond->dev, "Setting %s as primary slave\n",
|
||||
slave->dev->name);
|
||||
bond->primary_slave = slave;
|
||||
rcu_assign_pointer(bond->primary_slave, slave);
|
||||
strcpy(bond->params.primary, slave->dev->name);
|
||||
bond_select_active_slave(bond);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (bond->primary_slave) {
|
||||
if (rtnl_dereference(bond->primary_slave)) {
|
||||
netdev_info(bond->dev, "Setting primary slave to None\n");
|
||||
bond->primary_slave = NULL;
|
||||
RCU_INIT_POINTER(bond->primary_slave, NULL);
|
||||
bond_select_active_slave(bond);
|
||||
}
|
||||
strncpy(bond->params.primary, primary, IFNAMSIZ);
|
||||
|
@ -1117,8 +1098,6 @@ static int bond_option_primary_set(struct bonding *bond,
|
|||
primary, bond->dev->name);
|
||||
|
||||
out:
|
||||
write_unlock_bh(&bond->curr_slave_lock);
|
||||
read_unlock(&bond->lock);
|
||||
unblock_netpoll_tx();
|
||||
|
||||
return 0;
|
||||
|
@ -1132,9 +1111,7 @@ static int bond_option_primary_reselect_set(struct bonding *bond,
|
|||
bond->params.primary_reselect = newval->value;
|
||||
|
||||
block_netpoll_tx();
|
||||
write_lock_bh(&bond->curr_slave_lock);
|
||||
bond_select_active_slave(bond);
|
||||
write_unlock_bh(&bond->curr_slave_lock);
|
||||
unblock_netpoll_tx();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -7,21 +7,18 @@
|
|||
|
||||
static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
__acquires(&bond->lock)
|
||||
{
|
||||
struct bonding *bond = seq->private;
|
||||
struct list_head *iter;
|
||||
struct slave *slave;
|
||||
loff_t off = 0;
|
||||
|
||||
/* make sure the bond won't be taken away */
|
||||
rcu_read_lock();
|
||||
read_lock(&bond->lock);
|
||||
|
||||
if (*pos == 0)
|
||||
return SEQ_START_TOKEN;
|
||||
|
||||
bond_for_each_slave(bond, slave, iter)
|
||||
bond_for_each_slave_rcu(bond, slave, iter)
|
||||
if (++off == *pos)
|
||||
return slave;
|
||||
|
||||
|
@ -37,12 +34,9 @@ static void *bond_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|||
|
||||
++*pos;
|
||||
if (v == SEQ_START_TOKEN)
|
||||
return bond_first_slave(bond);
|
||||
return bond_first_slave_rcu(bond);
|
||||
|
||||
if (bond_is_last_slave(bond, v))
|
||||
return NULL;
|
||||
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
if (found)
|
||||
return slave;
|
||||
if (slave == v)
|
||||
|
@ -53,12 +47,8 @@ static void *bond_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|||
}
|
||||
|
||||
static void bond_info_seq_stop(struct seq_file *seq, void *v)
|
||||
__releases(&bond->lock)
|
||||
__releases(RCU)
|
||||
{
|
||||
struct bonding *bond = seq->private;
|
||||
|
||||
read_unlock(&bond->lock);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
|
@ -66,7 +56,7 @@ static void bond_info_show_master(struct seq_file *seq)
|
|||
{
|
||||
struct bonding *bond = seq->private;
|
||||
const struct bond_opt_value *optval;
|
||||
struct slave *curr;
|
||||
struct slave *curr, *primary;
|
||||
int i;
|
||||
|
||||
curr = rcu_dereference(bond->curr_active_slave);
|
||||
|
@ -83,8 +73,7 @@ static void bond_info_show_master(struct seq_file *seq)
|
|||
|
||||
seq_printf(seq, "\n");
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_XOR ||
|
||||
BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
if (bond_mode_uses_xmit_hash(bond)) {
|
||||
optval = bond_opt_get_val(BOND_OPT_XMIT_HASH,
|
||||
bond->params.xmit_policy);
|
||||
seq_printf(seq, "Transmit Hash Policy: %s (%d)\n",
|
||||
|
@ -92,10 +81,10 @@ static void bond_info_show_master(struct seq_file *seq)
|
|||
}
|
||||
|
||||
if (bond_uses_primary(bond)) {
|
||||
primary = rcu_dereference(bond->primary_slave);
|
||||
seq_printf(seq, "Primary Slave: %s",
|
||||
(bond->primary_slave) ?
|
||||
bond->primary_slave->dev->name : "None");
|
||||
if (bond->primary_slave) {
|
||||
primary ? primary->dev->name : "None");
|
||||
if (primary) {
|
||||
optval = bond_opt_get_val(BOND_OPT_PRIMARY_RESELECT,
|
||||
bond->params.primary_reselect);
|
||||
seq_printf(seq, " (primary_reselect %s)",
|
||||
|
|
|
@ -91,7 +91,6 @@ static struct net_device *bond_get_by_name(struct bond_net *bn, const char *ifna
|
|||
* creates and deletes entire bonds.
|
||||
*
|
||||
* The class parameter is ignored.
|
||||
*
|
||||
*/
|
||||
static ssize_t bonding_store_bonds(struct class *cls,
|
||||
struct class_attribute *attr,
|
||||
|
@ -425,11 +424,15 @@ static ssize_t bonding_show_primary(struct device *d,
|
|||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int count = 0;
|
||||
struct bonding *bond = to_bond(d);
|
||||
struct slave *primary;
|
||||
int count = 0;
|
||||
|
||||
if (bond->primary_slave)
|
||||
count = sprintf(buf, "%s\n", bond->primary_slave->dev->name);
|
||||
rcu_read_lock();
|
||||
primary = rcu_dereference(bond->primary_slave);
|
||||
if (primary)
|
||||
count = sprintf(buf, "%s\n", primary->dev->name);
|
||||
rcu_read_unlock();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/inetdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/reciprocal_div.h>
|
||||
#include <linux/if_link.h>
|
||||
|
||||
#include "bond_3ad.h"
|
||||
#include "bond_alb.h"
|
||||
|
@ -83,7 +84,7 @@
|
|||
* @pos: current slave
|
||||
* @iter: list_head * iterator
|
||||
*
|
||||
* Caller must hold bond->lock
|
||||
* Caller must hold RTNL
|
||||
*/
|
||||
#define bond_for_each_slave(bond, pos, iter) \
|
||||
netdev_for_each_lower_private((bond)->dev, pos, iter)
|
||||
|
@ -175,6 +176,13 @@ struct slave {
|
|||
struct netpoll *np;
|
||||
#endif
|
||||
struct kobject kobj;
|
||||
struct rtnl_link_stats64 slave_stats;
|
||||
};
|
||||
|
||||
struct bond_up_slave {
|
||||
unsigned int count;
|
||||
struct rcu_head rcu;
|
||||
struct slave *arr[0];
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -184,24 +192,26 @@ struct slave {
|
|||
|
||||
/*
|
||||
* Here are the locking policies for the two bonding locks:
|
||||
*
|
||||
* 1) Get bond->lock when reading/writing slave list.
|
||||
* 2) Get bond->curr_slave_lock when reading/writing bond->curr_active_slave.
|
||||
* (It is unnecessary when the write-lock is put with bond->lock.)
|
||||
* 3) When we lock with bond->curr_slave_lock, we must lock with bond->lock
|
||||
* beforehand.
|
||||
* Get rcu_read_lock when reading or RTNL when writing slave list.
|
||||
*/
|
||||
struct bonding {
|
||||
struct net_device *dev; /* first - useful for panic debug */
|
||||
struct slave __rcu *curr_active_slave;
|
||||
struct slave __rcu *current_arp_slave;
|
||||
struct slave *primary_slave;
|
||||
struct slave __rcu *primary_slave;
|
||||
struct bond_up_slave __rcu *slave_arr; /* Array of usable slaves */
|
||||
bool force_primary;
|
||||
s32 slave_cnt; /* never change this value outside the attach/detach wrappers */
|
||||
int (*recv_probe)(const struct sk_buff *, struct bonding *,
|
||||
struct slave *);
|
||||
rwlock_t lock;
|
||||
rwlock_t curr_slave_lock;
|
||||
/* mode_lock is used for mode-specific locking needs, currently used by:
|
||||
* 3ad mode (4) - protect against running bond_3ad_unbind_slave() and
|
||||
* bond_3ad_state_machine_handler() concurrently and also
|
||||
* the access to the state machine shared variables.
|
||||
* TLB mode (5) - to sync the use and modifications of its hash table
|
||||
* ALB mode (6) - to sync the use and modifications of its hash table
|
||||
*/
|
||||
spinlock_t mode_lock;
|
||||
u8 send_peer_notif;
|
||||
u8 igmp_retrans;
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
@ -219,10 +229,12 @@ struct bonding {
|
|||
struct delayed_work alb_work;
|
||||
struct delayed_work ad_work;
|
||||
struct delayed_work mcast_work;
|
||||
struct delayed_work slave_arr_work;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
/* debugging support via debugfs */
|
||||
struct dentry *debug_dir;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
struct rtnl_link_stats64 bond_stats;
|
||||
};
|
||||
|
||||
#define bond_slave_get_rcu(dev) \
|
||||
|
@ -231,10 +243,6 @@ struct bonding {
|
|||
#define bond_slave_get_rtnl(dev) \
|
||||
((struct slave *) rtnl_dereference(dev->rx_handler_data))
|
||||
|
||||
#define bond_deref_active_protected(bond) \
|
||||
rcu_dereference_protected(bond->curr_active_slave, \
|
||||
lockdep_is_held(&bond->curr_slave_lock))
|
||||
|
||||
struct bond_vlan_tag {
|
||||
__be16 vlan_proto;
|
||||
unsigned short vlan_id;
|
||||
|
@ -274,6 +282,13 @@ static inline bool bond_is_nondyn_tlb(const struct bonding *bond)
|
|||
(bond->params.tlb_dynamic_lb == 0);
|
||||
}
|
||||
|
||||
static inline bool bond_mode_uses_xmit_hash(const struct bonding *bond)
|
||||
{
|
||||
return (BOND_MODE(bond) == BOND_MODE_8023AD ||
|
||||
BOND_MODE(bond) == BOND_MODE_XOR ||
|
||||
bond_is_nondyn_tlb(bond));
|
||||
}
|
||||
|
||||
static inline bool bond_mode_uses_arp(int mode)
|
||||
{
|
||||
return mode != BOND_MODE_8023AD && mode != BOND_MODE_TLB &&
|
||||
|
@ -527,6 +542,8 @@ const char *bond_slave_link_status(s8 link);
|
|||
struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
|
||||
struct net_device *end_dev,
|
||||
int level);
|
||||
int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave);
|
||||
void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
void bond_create_proc_entry(struct bonding *bond);
|
||||
|
|
|
@ -65,7 +65,7 @@ config CAN_LEDS
|
|||
|
||||
config CAN_AT91
|
||||
tristate "Atmel AT91 onchip CAN controller"
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on (ARCH_AT91 || COMPILE_TEST) && HAS_IOMEM
|
||||
---help---
|
||||
This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
|
||||
and AT91SAM9X5 processors.
|
||||
|
@ -143,6 +143,8 @@ source "drivers/net/can/sja1000/Kconfig"
|
|||
|
||||
source "drivers/net/can/c_can/Kconfig"
|
||||
|
||||
source "drivers/net/can/m_can/Kconfig"
|
||||
|
||||
source "drivers/net/can/cc770/Kconfig"
|
||||
|
||||
source "drivers/net/can/spi/Kconfig"
|
||||
|
|
|
@ -17,6 +17,7 @@ obj-y += softing/
|
|||
obj-$(CONFIG_CAN_SJA1000) += sja1000/
|
||||
obj-$(CONFIG_CAN_MSCAN) += mscan/
|
||||
obj-$(CONFIG_CAN_C_CAN) += c_can/
|
||||
obj-$(CONFIG_CAN_M_CAN) += m_can/
|
||||
obj-$(CONFIG_CAN_CC770) += cc770/
|
||||
obj-$(CONFIG_CAN_AT91) += at91_can.o
|
||||
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
|
||||
|
@ -28,4 +29,4 @@ obj-$(CONFIG_CAN_GRCAN) += grcan.o
|
|||
obj-$(CONFIG_CAN_RCAR) += rcar_can.o
|
||||
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
|
||||
|
||||
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
||||
subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
||||
|
|
|
@ -5,5 +5,3 @@
|
|||
obj-$(CONFIG_CAN_C_CAN) += c_can.o
|
||||
obj-$(CONFIG_CAN_C_CAN_PLATFORM) += c_can_platform.o
|
||||
obj-$(CONFIG_CAN_C_CAN_PCI) += c_can_pci.o
|
||||
|
||||
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
||||
|
|
|
@ -5,5 +5,3 @@
|
|||
obj-$(CONFIG_CAN_CC770) += cc770.o
|
||||
obj-$(CONFIG_CAN_CC770_ISA) += cc770_isa.o
|
||||
obj-$(CONFIG_CAN_CC770_PLATFORM) += cc770_platform.o
|
||||
|
||||
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
||||
|
|
|
@ -103,11 +103,11 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
|
|||
const struct can_bittiming_const *btc)
|
||||
{
|
||||
struct can_priv *priv = netdev_priv(dev);
|
||||
long rate, best_rate = 0;
|
||||
long best_error = 1000000000, error = 0;
|
||||
int best_tseg = 0, best_brp = 0, brp = 0;
|
||||
int tsegall, tseg = 0, tseg1 = 0, tseg2 = 0;
|
||||
int spt_error = 1000, spt = 0, sampl_pt;
|
||||
long rate;
|
||||
u64 v64;
|
||||
|
||||
/* Use CIA recommended sample points */
|
||||
|
@ -152,7 +152,6 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
|
|||
}
|
||||
best_tseg = tseg / 2;
|
||||
best_brp = brp;
|
||||
best_rate = rate;
|
||||
if (error == 0)
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -92,6 +92,27 @@
|
|||
#define FLEXCAN_CTRL_ERR_ALL \
|
||||
(FLEXCAN_CTRL_ERR_BUS | FLEXCAN_CTRL_ERR_STATE)
|
||||
|
||||
/* FLEXCAN control register 2 (CTRL2) bits */
|
||||
#define FLEXCAN_CRL2_ECRWRE BIT(29)
|
||||
#define FLEXCAN_CRL2_WRMFRZ BIT(28)
|
||||
#define FLEXCAN_CRL2_RFFN(x) (((x) & 0x0f) << 24)
|
||||
#define FLEXCAN_CRL2_TASD(x) (((x) & 0x1f) << 19)
|
||||
#define FLEXCAN_CRL2_MRP BIT(18)
|
||||
#define FLEXCAN_CRL2_RRS BIT(17)
|
||||
#define FLEXCAN_CRL2_EACEN BIT(16)
|
||||
|
||||
/* FLEXCAN memory error control register (MECR) bits */
|
||||
#define FLEXCAN_MECR_ECRWRDIS BIT(31)
|
||||
#define FLEXCAN_MECR_HANCEI_MSK BIT(19)
|
||||
#define FLEXCAN_MECR_FANCEI_MSK BIT(18)
|
||||
#define FLEXCAN_MECR_CEI_MSK BIT(16)
|
||||
#define FLEXCAN_MECR_HAERRIE BIT(15)
|
||||
#define FLEXCAN_MECR_FAERRIE BIT(14)
|
||||
#define FLEXCAN_MECR_EXTERRIE BIT(13)
|
||||
#define FLEXCAN_MECR_RERRDIS BIT(9)
|
||||
#define FLEXCAN_MECR_ECCDIS BIT(8)
|
||||
#define FLEXCAN_MECR_NCEFAFRZ BIT(7)
|
||||
|
||||
/* FLEXCAN error and status register (ESR) bits */
|
||||
#define FLEXCAN_ESR_TWRN_INT BIT(17)
|
||||
#define FLEXCAN_ESR_RWRN_INT BIT(16)
|
||||
|
@ -163,18 +184,20 @@
|
|||
* FLEXCAN hardware feature flags
|
||||
*
|
||||
* Below is some version info we got:
|
||||
* SOC Version IP-Version Glitch- [TR]WRN_INT
|
||||
* Filter? connected?
|
||||
* MX25 FlexCAN2 03.00.00.00 no no
|
||||
* MX28 FlexCAN2 03.00.04.00 yes yes
|
||||
* MX35 FlexCAN2 03.00.00.00 no no
|
||||
* MX53 FlexCAN2 03.00.00.00 yes no
|
||||
* MX6s FlexCAN3 10.00.12.00 yes yes
|
||||
* SOC Version IP-Version Glitch- [TR]WRN_INT Memory err
|
||||
* Filter? connected? detection
|
||||
* MX25 FlexCAN2 03.00.00.00 no no no
|
||||
* MX28 FlexCAN2 03.00.04.00 yes yes no
|
||||
* MX35 FlexCAN2 03.00.00.00 no no no
|
||||
* MX53 FlexCAN2 03.00.00.00 yes no no
|
||||
* MX6s FlexCAN3 10.00.12.00 yes yes no
|
||||
* VF610 FlexCAN3 ? no yes yes
|
||||
*
|
||||
* Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected.
|
||||
*/
|
||||
#define FLEXCAN_HAS_V10_FEATURES BIT(1) /* For core version >= 10 */
|
||||
#define FLEXCAN_HAS_BROKEN_ERR_STATE BIT(2) /* [TR]WRN_INT not connected */
|
||||
#define FLEXCAN_HAS_MECR_FEATURES BIT(3) /* Memory error detection */
|
||||
|
||||
/* Structure of the message buffer */
|
||||
struct flexcan_mb {
|
||||
|
@ -205,8 +228,17 @@ struct flexcan_regs {
|
|||
u32 crcr; /* 0x44 */
|
||||
u32 rxfgmask; /* 0x48 */
|
||||
u32 rxfir; /* 0x4c */
|
||||
u32 _reserved3[12];
|
||||
struct flexcan_mb cantxfg[64];
|
||||
u32 _reserved3[12]; /* 0x50 */
|
||||
struct flexcan_mb cantxfg[64]; /* 0x80 */
|
||||
u32 _reserved4[408];
|
||||
u32 mecr; /* 0xae0 */
|
||||
u32 erriar; /* 0xae4 */
|
||||
u32 erridpr; /* 0xae8 */
|
||||
u32 errippr; /* 0xaec */
|
||||
u32 rerrar; /* 0xaf0 */
|
||||
u32 rerrdr; /* 0xaf4 */
|
||||
u32 rerrsynr; /* 0xaf8 */
|
||||
u32 errsr; /* 0xafc */
|
||||
};
|
||||
|
||||
struct flexcan_devtype_data {
|
||||
|
@ -236,6 +268,9 @@ static struct flexcan_devtype_data fsl_imx28_devtype_data;
|
|||
static struct flexcan_devtype_data fsl_imx6q_devtype_data = {
|
||||
.features = FLEXCAN_HAS_V10_FEATURES,
|
||||
};
|
||||
static struct flexcan_devtype_data fsl_vf610_devtype_data = {
|
||||
.features = FLEXCAN_HAS_V10_FEATURES | FLEXCAN_HAS_MECR_FEATURES,
|
||||
};
|
||||
|
||||
static const struct can_bittiming_const flexcan_bittiming_const = {
|
||||
.name = DRV_NAME,
|
||||
|
@ -391,8 +426,9 @@ static int flexcan_chip_softreset(struct flexcan_priv *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int flexcan_get_berr_counter(const struct net_device *dev,
|
||||
struct can_berr_counter *bec)
|
||||
|
||||
static int __flexcan_get_berr_counter(const struct net_device *dev,
|
||||
struct can_berr_counter *bec)
|
||||
{
|
||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||
struct flexcan_regs __iomem *regs = priv->base;
|
||||
|
@ -404,6 +440,29 @@ static int flexcan_get_berr_counter(const struct net_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int flexcan_get_berr_counter(const struct net_device *dev,
|
||||
struct can_berr_counter *bec)
|
||||
{
|
||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_ipg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk_per);
|
||||
if (err)
|
||||
goto out_disable_ipg;
|
||||
|
||||
err = __flexcan_get_berr_counter(dev, bec);
|
||||
|
||||
clk_disable_unprepare(priv->clk_per);
|
||||
out_disable_ipg:
|
||||
clk_disable_unprepare(priv->clk_ipg);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
const struct flexcan_priv *priv = netdev_priv(dev);
|
||||
|
@ -524,7 +583,7 @@ static void do_state(struct net_device *dev,
|
|||
struct flexcan_priv *priv = netdev_priv(dev);
|
||||
struct can_berr_counter bec;
|
||||
|
||||
flexcan_get_berr_counter(dev, &bec);
|
||||
__flexcan_get_berr_counter(dev, &bec);
|
||||
|
||||
switch (priv->can.state) {
|
||||
case CAN_STATE_ERROR_ACTIVE:
|
||||
|
@ -823,9 +882,8 @@ static int flexcan_chip_start(struct net_device *dev)
|
|||
{
|
||||
struct flexcan_priv *priv = netdev_priv(dev);
|
||||
struct flexcan_regs __iomem *regs = priv->base;
|
||||
int err;
|
||||
u32 reg_mcr, reg_ctrl;
|
||||
int i;
|
||||
u32 reg_mcr, reg_ctrl, reg_crl2, reg_mecr;
|
||||
int err, i;
|
||||
|
||||
/* enable module */
|
||||
err = flexcan_chip_enable(priv);
|
||||
|
@ -914,6 +972,31 @@ static int flexcan_chip_start(struct net_device *dev)
|
|||
if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES)
|
||||
flexcan_write(0x0, ®s->rxfgmask);
|
||||
|
||||
/*
|
||||
* On Vybrid, disable memory error detection interrupts
|
||||
* and freeze mode.
|
||||
* This also works around errata e5295 which generates
|
||||
* false positive memory errors and put the device in
|
||||
* freeze mode.
|
||||
*/
|
||||
if (priv->devtype_data->features & FLEXCAN_HAS_MECR_FEATURES) {
|
||||
/*
|
||||
* Follow the protocol as described in "Detection
|
||||
* and Correction of Memory Errors" to write to
|
||||
* MECR register
|
||||
*/
|
||||
reg_crl2 = flexcan_read(®s->crl2);
|
||||
reg_crl2 |= FLEXCAN_CRL2_ECRWRE;
|
||||
flexcan_write(reg_crl2, ®s->crl2);
|
||||
|
||||
reg_mecr = flexcan_read(®s->mecr);
|
||||
reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS;
|
||||
flexcan_write(reg_mecr, ®s->mecr);
|
||||
reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK |
|
||||
FLEXCAN_MECR_FANCEI_MSK);
|
||||
flexcan_write(reg_mecr, ®s->mecr);
|
||||
}
|
||||
|
||||
err = flexcan_transceiver_enable(priv);
|
||||
if (err)
|
||||
goto out_chip_disable;
|
||||
|
@ -1124,6 +1207,7 @@ static const struct of_device_id flexcan_of_match[] = {
|
|||
{ .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
|
||||
{ .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
|
||||
{ .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
|
||||
{ .compatible = "fsl,vf610-flexcan", .data = &fsl_vf610_devtype_data, },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, flexcan_of_match);
|
||||
|
|
4
drivers/net/can/m_can/Kconfig
Normal file
4
drivers/net/can/m_can/Kconfig
Normal file
|
@ -0,0 +1,4 @@
|
|||
config CAN_M_CAN
|
||||
tristate "Bosch M_CAN devices"
|
||||
---help---
|
||||
Say Y here if you want to support for Bosch M_CAN controller.
|
5
drivers/net/can/m_can/Makefile
Normal file
5
drivers/net/can/m_can/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for the Bosch M_CAN controller driver.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_CAN_M_CAN) += m_can.o
|
1202
drivers/net/can/m_can/m_can.c
Normal file
1202
drivers/net/can/m_can/m_can.c
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue