(DSA_MAX_SWITCHES).
Each of these switch child nodes should have the following required properties:
-- reg : Describes the switch address on the MII bus
+- reg : Contains two fields. The first one describes the
+ address on the MII bus. The second is the switch
+ number that must be unique in cascaded configurations
- #address-cells : Must be 1
- #size-cells : Must be 0
--- /dev/null
+* NXP Semiconductors NXP NCI NFC Controllers
+
+Required properties:
+- compatible: Should be "nxp,nxp-nci-i2c".
+- clock-frequency: I²C work frequency.
+- reg: address on the bus
+- interrupt-parent: phandle for the interrupt gpio controller
+- interrupts: GPIO interrupt to which the chip is connected
+- enable-gpios: Output GPIO pin used for enabling/disabling the chip
+- firmware-gpios: Output GPIO pin used to enter firmware download mode
+
+Optional SoC Specific Properties:
+- pinctrl-names: Contains only one value - "default".
+- pintctrl-0: Specifies the pin control groups used for this controller.
+
+Example (for ARM-based BeagleBone with NPC100 NFC controller on I2C2):
+
+&i2c2 {
+
+ status = "okay";
+
+ npc100: npc100@29 {
+
+ compatible = "nxp,nxp-nci-i2c";
+
+ reg = <0x29>;
+ clock-frequency = <100000>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <29 GPIO_ACTIVE_HIGH>;
+
+ enable-gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
+ firmware-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
+ };
+};
- reset-names: Should contain the reset signal name "stmmaceth", if a
reset phandle is given
- max-frame-size: See ethernet.txt file in the same directory
-- clocks: If present, the first clock should be the GMAC main clock,
- further clocks may be specified in derived bindings.
+- clocks: If present, the first clock should be the GMAC main clock and
+ the second clock should be peripheral's register interface clock. Further
+ clocks may be specified in derived bindings.
- clock-names: One name for each entry in the clocks property, the
- first one should be "stmmaceth".
+ first one should be "stmmaceth" and the second one should be "pclk".
- clk_ptp_ref: this is the PTP reference clock; in case of the PTP is
available this clock is used for programming the Timestamp Addend Register.
If not passed then the system clock will be used and this is fine on some
byte 4: 0 y6 y5 y4 y3 y2 y1 y0
byte 5: 0 z6 z5 z4 z3 z2 z1 z0
+Protocol Version 2 DualPoint devices send standard PS/2 mouse packets for
+the DualPoint Stick.
+
Dualpoint device -- interleaved packet format
---------------------------------------------
byte 7: 0 y6 y5 y4 y3 y2 y1 y0
byte 8: 0 z6 z5 z4 z3 z2 z1 z0
+Devices which use the interleaving format normally send standard PS/2 mouse
+packets for the DualPoint Stick + ALPS Absolute Mode packets for the
+touchpad, switching to the interleaved packet format when both the stick and
+the touchpad are used at the same time.
+
ALPS Absolute Mode - Protocol Version 3
---------------------------------------
The kernel does not provide button emulation for such devices but treats
them as any other INPUT_PROP_BUTTONPAD device.
+INPUT_PROP_ACCELEROMETER
+-------------------------
+Directional axes on this device (absolute and/or relative x, y, z) represent
+accelerometer data. All other axes retain their meaning. A device must not mix
+regular directional axes and accelerometer axes on the same event node.
+
Guidelines:
==========
The guidelines below ensure proper single-touch and multi-finger functionality.
The type of approaching tool. A lot of kernel drivers cannot distinguish
between different tool types, such as a finger or a pen. In such cases, the
-event should be omitted. The protocol currently supports MT_TOOL_FINGER and
-MT_TOOL_PEN [2]. For type B devices, this event is handled by input core;
-drivers should instead use input_mt_report_slot_state().
+event should be omitted. The protocol currently supports MT_TOOL_FINGER,
+MT_TOOL_PEN, and MT_TOOL_PALM [2]. For type B devices, this event is handled
+by input core; drivers should instead use input_mt_report_slot_state().
+A contact's ABS_MT_TOOL_TYPE may change over time while still touching the
+device, because the firmware may not be able to determine which tool is being
+used when it first appears.
ABS_MT_BLOB_ID
4.1.3 RAW socket option CAN_RAW_LOOPBACK
4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS
4.1.5 RAW socket option CAN_RAW_FD_FRAMES
- 4.1.6 RAW socket returned message flags
+ 4.1.6 RAW socket option CAN_RAW_JOIN_FILTERS
+ 4.1.7 RAW socket returned message flags
4.2 Broadcast Manager protocol sockets (SOCK_DGRAM)
4.2.1 Broadcast Manager operations
4.2.2 Broadcast Manager message flags
CAN FD frames by checking if the device maximum transfer unit is CANFD_MTU.
The CAN device MTU can be retrieved e.g. with a SIOCGIFMTU ioctl() syscall.
- 4.1.6 RAW socket returned message flags
+ 4.1.6 RAW socket option CAN_RAW_JOIN_FILTERS
+
+ The CAN_RAW socket can set multiple CAN identifier specific filters that
+ lead to multiple filters in the af_can.c filter processing. These filters
+ are indenpendent from each other which leads to logical OR'ed filters when
+ applied (see 4.1.1).
+
+ This socket option joines the given CAN filters in the way that only CAN
+ frames are passed to user space that matched *all* given CAN filters. The
+ semantic for the applied filters is therefore changed to a logical AND.
+
+ This is useful especially when the filterset is a combination of filters
+ where the CAN_INV_FILTER flag is set in order to notch single CAN IDs or
+ CAN ID ranges from the incoming traffic.
+
+ 4.1.7 RAW socket returned message flags
When using recvmsg() call, the msg->msg_flags may contain following flags:
Jumbo Frames
------------
Jumbo Frames support is enabled by changing the MTU to a value larger than
- the default of 1500. Use the ifconfig command to increase the MTU size.
+ the default of 1500. Use the ip command to increase the MTU size.
For example:
- ifconfig eth<x> mtu 9000 up
+ ip link set dev eth<x> mtu 9000
This setting is not saved across reboots.
The driver information previously displayed in the /proc filesystem is not
supported in this release. Alternatively, you can use ethtool (version 1.6
-or later), lspci, and ifconfig to obtain the same information.
+or later), lspci, and iproute2 to obtain the same information.
Instructions on updating ethtool can be found in the section "Additional
Configurations" later in this document.
3. Assign an IP address to the interface by entering the following, where
x is the interface number:
- ifconfig ethx <IP_address>
+ ip addr add ethx <IP_address>
4. Verify that the interface works. Enter the following, where <IP_address>
is the IP address for another machine on the same subnet as the interface
tuning your network performance.
The changes are made in three major ways, listed in order of greatest effect:
-- Use ifconfig to modify the mtu (maximum transmission unit) and the txqueuelen
+- Use ip link to modify the mtu (maximum transmission unit) and the txqueuelen
parameter.
- Use sysctl to modify /proc parameters (essentially kernel tuning)
- Use setpci to modify the MMRBC field in PCI-X configuration space to increase
# to change as well.
# set the txqueuelen
# your ixgb adapter should be loaded as eth1 for this to work, change if needed
-ifconfig eth1 mtu 9000 txqueuelen 1000 up
+ip li set dev eth1 mtu 9000 txqueuelen 1000 up
# call the sysctl utility to modify /proc/sys entries
sysctl -p ./sysctl_ixgb.conf
- END ixgb_perf.sh
------------
The driver supports Jumbo Frames for all adapters. Jumbo Frames support is
enabled by changing the MTU to a value larger than the default of 1500.
- The maximum value for the MTU is 16114. Use the ifconfig command to
+ The maximum value for the MTU is 16114. Use the ip command to
increase the MTU size. For example:
- ifconfig ethx mtu 9000 up
+ ip li set dev ethx mtu 9000
The maximum MTU setting for Jumbo Frames is 16114. This value coincides
with the maximum Jumbo Frames size of 16128.
82599-based adapters support all passive and active limiting direct attach
cables that comply with SFF-8431 v4.1 and SFF-8472 v10.4 specifications.
-Laser turns off for SFP+ when ifconfig down
+Laser turns off for SFP+ when device is down
-------------------------------------------
-"ifconfig down" turns off the laser for 82599-based SFP+ fiber adapters.
-"ifconfig up" turns on the laser.
+"ip link set down" turns off the laser for 82599-based SFP+ fiber adapters.
+"ip link set up" turns on the laser.
82598-BASED ADAPTERS
------------
The driver supports Jumbo Frames for all adapters. Jumbo Frames support is
enabled by changing the MTU to a value larger than the default of 1500.
- The maximum value for the MTU is 16110. Use the ifconfig command to
+ The maximum value for the MTU is 16110. Use the ip command to
increase the MTU size. For example:
- ifconfig ethx mtu 9000 up
+ ip link set dev ethx mtu 9000
- The maximum MTU setting for Jumbo Frames is 16110. This value coincides
- with the maximum Jumbo Frames size of 16128.
+ The maximum MTU setting for Jumbo Frames is 9710. This value coincides
+ with the maximum Jumbo Frames size of 9728.
Generic Receive Offload, aka GRO
--------------------------------
F: include/uapi/linux/kfd_ioctl.h
AMD MICROCODE UPDATE SUPPORT
-M: Andreas Herrmann <herrmann.der.user@googlemail.com>
-L: amd64-microcode@amd64.org
+M: Borislav Petkov <bp@alien8.de>
S: Maintained
F: arch/x86/kernel/cpu/microcode/amd*
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-mvebu/
-F: drivers/rtc/armada38x-rtc
+F: drivers/rtc/rtc-armada38x.c
ARM/Marvell Berlin SoC support
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
F: drivers/*/*rockchip*
F: drivers/*/*/*rockchip*
F: sound/soc/rockchip/
+N: rockchip
ARM/SAMSUNG EXYNOS ARM ARCHITECTURES
M: Kukjin Kim <kgene@kernel.org>
F: include/linux/platform_data/at24.h
ATA OVER ETHERNET (AOE) DRIVER
-M: "Ed L. Cashin" <ecashin@coraid.com>
-W: http://support.coraid.com/support/linux
+M: "Ed L. Cashin" <ed.cashin@acm.org>
+W: http://www.openaoe.org/
S: Supported
F: Documentation/aoe/
F: drivers/block/aoe/
F: Documentation/hwmon/dme1737
F: drivers/hwmon/dme1737.c
+DMI/SMBIOS SUPPORT
+M: Jean Delvare <jdelvare@suse.de>
+S: Maintained
+F: drivers/firmware/dmi-id.c
+F: drivers/firmware/dmi_scan.c
+F: include/linux/dmi.h
+
DOCKING STATION DRIVER
M: Shaohua Li <shaohua.li@intel.com>
L: linux-acpi@vger.kernel.org
F: drivers/platform/x86/intel_menlow.c
INTEL IA32 MICROCODE UPDATE SUPPORT
-M: Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
+M: Borislav Petkov <bp@alien8.de>
S: Maintained
F: arch/x86/kernel/cpu/microcode/core*
F: arch/x86/kernel/cpu/microcode/intel*
S: Maintained
F: drivers/char/hw_random/ixp4xx-rng.c
-INTEL ETHERNET DRIVERS (e100/e1000/e1000e/fm10k/igb/igbvf/ixgb/ixgbe/ixgbevf/i40e/i40evf)
+INTEL ETHERNET DRIVERS
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: 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>
-L: e1000-devel@lists.sourceforge.net
+R: Jesse Brandeburg <jesse.brandeburg@intel.com>
+R: Shannon Nelson <shannon.nelson@intel.com>
+R: Carolyn Wyborny <carolyn.wyborny@intel.com>
+R: Don Skidmore <donald.c.skidmore@intel.com>
+R: Matthew Vick <matthew.vick@intel.com>
+R: John Ronciak <john.ronciak@intel.com>
+R: Mitch Williams <mitch.a.williams@intel.com>
+L: intel-wired-lan@lists.osuosl.org
W: http://www.intel.com/support/feedback.htm
W: http://e1000.sourceforge.net/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net.git
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next.git
+Q: http://patchwork.ozlabs.org/project/intel-wired-lan/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-queue.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue.git
S: Supported
F: Documentation/networking/e100.txt
F: Documentation/networking/e1000.txt
MELLANOX ETHERNET DRIVER (mlx4_en)
M: Amir Vadai <amirv@mellanox.com>
+M: Ido Shamay <idos@mellanox.com>
L: netdev@vger.kernel.org
S: Supported
W: http://www.mellanox.com
F: drivers/block/nvme*
F: include/linux/nvme.h
+NXP-NCI NFC DRIVER
+M: Clément Perrochaud <clement.perrochaud@effinnov.com>
+R: Charles Gorand <charles.gorand@effinnov.com>
+L: linux-nfc@lists.01.org (moderated for non-subscribers)
+S: Supported
+F: drivers/nfc/nxp-nci
+
NXP TDA998X DRM DRIVER
M: Russell King <rmk+kernel@arm.linux.org.uk>
S: Supported
VERSION = 4
PATCHLEVEL = 0
SUBLEVEL = 0
-EXTRAVERSION = -rc5
+EXTRAVERSION = -rc7
NAME = Hurr durr I'ma sheep
# *DOCUMENTATION*
sigset_t *set)
{
int err;
- err = __copy_to_user(&(sf->uc.uc_mcontext.regs), regs,
+ err = __copy_to_user(&(sf->uc.uc_mcontext.regs.scratch), regs,
sizeof(sf->uc.uc_mcontext.regs.scratch));
err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(sigset_t));
if (!err)
set_current_blocked(&set);
- err |= __copy_from_user(regs, &(sf->uc.uc_mcontext.regs),
+ err |= __copy_from_user(regs, &(sf->uc.uc_mcontext.regs.scratch),
sizeof(sf->uc.uc_mcontext.regs.scratch));
return err;
/* Don't restart from sigreturn */
syscall_wont_restart(regs);
+ /*
+ * Ensure that sigreturn always returns to user mode (in case the
+ * regs saved on user stack got fudged between save and sigreturn)
+ * Otherwise it is easy to panic the kernel with a custom
+ * signal handler and/or restorer which clobberes the status32/ret
+ * to return to a bogus location in kernel mode.
+ */
+ regs->status32 |= STATUS_U_MASK;
+
return regs->r0;
badframe:
/*
* handler returns using sigreturn stub provided already by userpsace
+ * If not, nuke the process right away
*/
- BUG_ON(!(ksig->ka.sa.sa_flags & SA_RESTORER));
+ if(!(ksig->ka.sa.sa_flags & SA_RESTORER))
+ return 1;
+
regs->blink = (unsigned long)ksig->ka.sa.sa_restorer;
/* User Stack for signal handler will be above the frame just carved */
handle_signal(struct ksignal *ksig, struct pt_regs *regs)
{
sigset_t *oldset = sigmask_to_save();
- int ret;
+ int failed;
/* Set up the stack frame */
- ret = setup_rt_frame(ksig, oldset, regs);
+ failed = setup_rt_frame(ksig, oldset, regs);
- signal_setup_done(ret, ksig, 0);
+ signal_setup_done(failed, ksig, 0);
}
void do_signal(struct pt_regs *regs)
select GENERIC_CLOCKEVENTS
select GPIO_PXA
select HAVE_IDE
+ select IRQ_DOMAIN
select MULTI_IRQ_HANDLER
select PLAT_PXA
select SPARSE_IRQ
>;
};
+ mmc_pins: pinmux_mmc_pins {
+ pinctrl-single,pins = <
+ DM816X_IOPAD(0x0a70, MUX_MODE0) /* SD_POW */
+ DM816X_IOPAD(0x0a74, MUX_MODE0) /* SD_CLK */
+ DM816X_IOPAD(0x0a78, MUX_MODE0) /* SD_CMD */
+ DM816X_IOPAD(0x0a7C, MUX_MODE0) /* SD_DAT0 */
+ DM816X_IOPAD(0x0a80, MUX_MODE0) /* SD_DAT1 */
+ DM816X_IOPAD(0x0a84, MUX_MODE0) /* SD_DAT2 */
+ DM816X_IOPAD(0x0a88, MUX_MODE0) /* SD_DAT2 */
+ DM816X_IOPAD(0x0a8c, MUX_MODE2) /* GP1[7] */
+ DM816X_IOPAD(0x0a90, MUX_MODE2) /* GP1[8] */
+ >;
+ };
+
usb0_pins: pinmux_usb0_pins {
pinctrl-single,pins = <
DM816X_IOPAD(0x0d00, MUX_MODE0) /* USB0_DRVVBUS */
};
&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc_pins>;
vmmc-supply = <&vmmcsd_fixed>;
+ bus-width = <4>;
+ cd-gpios = <&gpio2 7 GPIO_ACTIVE_LOW>;
+ wp-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>;
};
/* At least dm8168-evm rev c won't support multipoint, later may */
};
gpio1: gpio@48032000 {
- compatible = "ti,omap3-gpio";
+ compatible = "ti,omap4-gpio";
ti,hwmods = "gpio1";
+ ti,gpio-always-on;
reg = <0x48032000 0x1000>;
- interrupts = <97>;
+ interrupts = <96>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
};
gpio2: gpio@4804c000 {
- compatible = "ti,omap3-gpio";
+ compatible = "ti,omap4-gpio";
ti,hwmods = "gpio2";
+ ti,gpio-always-on;
reg = <0x4804c000 0x1000>;
- interrupts = <99>;
+ interrupts = <98>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
};
gpmc: gpmc@50000000 {
"wkupclk", "refclk",
"div-clk", "phy-div";
#phy-cells = <0>;
- ti,hwmods = "pcie1-phy";
};
pcie2_phy: pciephy@4a095000 {
"wkupclk", "refclk",
"div-clk", "phy-div";
#phy-cells = <0>;
- ti,hwmods = "pcie2-phy";
status = "disabled";
};
};
ti,hwmods = "aes";
reg = <0x480c5000 0x50>;
interrupts = <0>;
+ dmas = <&sdma 65 &sdma 66>;
+ dma-names = "tx", "rx";
};
prm: prm@48306000 {
ti,hwmods = "sham";
reg = <0x480c3000 0x64>;
interrupts = <49>;
+ dmas = <&sdma 69>;
+ dma-names = "rx";
};
smartreflex_core: smartreflex@480cb000 {
"mac_clk_rx", "mac_clk_tx",
"clk_mac_ref", "clk_mac_refout",
"aclk_mac", "pclk_mac";
+ status = "disabled";
};
usb_host0_ehci: usb@ff500000 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0xfff01000 0x1000>;
- interrupts = <0 156 4>;
+ interrupts = <0 155 4>;
num-cs = <4>;
clocks = <&spi_m_clk>;
status = "disabled";
model = "Olimex A10-OLinuXino-LIME";
compatible = "olimex,a10-olinuxino-lime", "allwinner,sun4i-a10";
+ cpus {
+ cpu0: cpu@0 {
+ /*
+ * The A10-Lime is known to be unstable
+ * when running at 1008 MHz
+ */
+ operating-points = <
+ /* kHz uV */
+ 912000 1350000
+ 864000 1300000
+ 624000 1250000
+ >;
+ cooling-max-level = <2>;
+ };
+ };
+
soc@01c00000 {
emac: ethernet@01c0b000 {
pinctrl-names = "default";
clock-latency = <244144>; /* 8 32k periods */
operating-points = <
/* kHz uV */
- 1056000 1500000
1008000 1400000
912000 1350000
864000 1300000
>;
#cooling-cells = <2>;
cooling-min-level = <0>;
- cooling-max-level = <4>;
+ cooling-max-level = <3>;
};
};
clock-latency = <244144>; /* 8 32k periods */
operating-points = <
/* kHz uV */
- 1104000 1500000
1008000 1400000
912000 1350000
864000 1300000
>;
#cooling-cells = <2>;
cooling-min-level = <0>;
- cooling-max-level = <6>;
+ cooling-max-level = <5>;
};
};
clock-latency = <244144>; /* 8 32k periods */
operating-points = <
/* kHz uV */
- 1008000 1450000
960000 1400000
912000 1400000
864000 1300000
>;
#cooling-cells = <2>;
cooling-min-level = <0>;
- cooling-max-level = <7>;
+ cooling-max-level = <6>;
};
cpu@1 {
return kasprintf(GFP_KERNEL, "OMAP4");
else if (soc_is_omap54xx())
return kasprintf(GFP_KERNEL, "OMAP5");
+ else if (soc_is_am33xx() || soc_is_am335x())
+ return kasprintf(GFP_KERNEL, "AM33xx");
else if (soc_is_am43xx())
return kasprintf(GFP_KERNEL, "AM43xx");
else if (soc_is_dra7xx())
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#define ICHP_VAL_IRQ (1 << 31)
#define ICHP_IRQ(i) (((i) >> 16) & 0x7fff)
#define IPR_VALID (1 << 31)
-#define IRQ_BIT(n) (((n) - PXA_IRQ(0)) & 0x1f)
#define MAX_INTERNAL_IRQS 128
static void __iomem *pxa_irq_base;
static int pxa_internal_irq_nr;
static bool cpu_has_ipr;
+static struct irq_domain *pxa_irq_domain;
static inline void __iomem *irq_base(int i)
{
void pxa_mask_irq(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
uint32_t icmr = __raw_readl(base + ICMR);
- icmr &= ~(1 << IRQ_BIT(d->irq));
+ icmr &= ~BIT(irq & 0x1f);
__raw_writel(icmr, base + ICMR);
}
void pxa_unmask_irq(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
uint32_t icmr = __raw_readl(base + ICMR);
- icmr |= 1 << IRQ_BIT(d->irq);
+ icmr |= BIT(irq & 0x1f);
__raw_writel(icmr, base + ICMR);
}
} while (1);
}
-void __init pxa_init_irq(int irq_nr, int (*fn)(struct irq_data *, unsigned int))
+static int pxa_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
{
- int irq, i, n;
+ void __iomem *base = irq_base(hw / 32);
- BUG_ON(irq_nr > MAX_INTERNAL_IRQS);
+ /* initialize interrupt priority */
+ if (cpu_has_ipr)
+ __raw_writel(hw | IPR_VALID, pxa_irq_base + IPR(hw));
+
+ irq_set_chip_and_handler(virq, &pxa_internal_irq_chip,
+ handle_level_irq);
+ irq_set_chip_data(virq, base);
+ set_irq_flags(virq, IRQF_VALID);
+
+ return 0;
+}
+
+static struct irq_domain_ops pxa_irq_ops = {
+ .map = pxa_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static __init void
+pxa_init_irq_common(struct device_node *node, int irq_nr,
+ int (*fn)(struct irq_data *, unsigned int))
+{
+ int n;
pxa_internal_irq_nr = irq_nr;
- cpu_has_ipr = !cpu_is_pxa25x();
- pxa_irq_base = io_p2v(0x40d00000);
+ pxa_irq_domain = irq_domain_add_legacy(node, irq_nr,
+ PXA_IRQ(0), 0,
+ &pxa_irq_ops, NULL);
+ if (!pxa_irq_domain)
+ panic("Unable to add PXA IRQ domain\n");
+ irq_set_default_host(pxa_irq_domain);
for (n = 0; n < irq_nr; n += 32) {
void __iomem *base = irq_base(n >> 5);
__raw_writel(0, base + ICMR); /* disable all IRQs */
__raw_writel(0, base + ICLR); /* all IRQs are IRQ, not FIQ */
- for (i = n; (i < (n + 32)) && (i < irq_nr); i++) {
- /* initialize interrupt priority */
- if (cpu_has_ipr)
- __raw_writel(i | IPR_VALID, pxa_irq_base + IPR(i));
-
- irq = PXA_IRQ(i);
- irq_set_chip_and_handler(irq, &pxa_internal_irq_chip,
- handle_level_irq);
- irq_set_chip_data(irq, base);
- set_irq_flags(irq, IRQF_VALID);
- }
}
-
/* only unmasked interrupts kick us out of idle */
__raw_writel(1, irq_base(0) + ICCR);
pxa_internal_irq_chip.irq_set_wake = fn;
}
+void __init pxa_init_irq(int irq_nr, int (*fn)(struct irq_data *, unsigned int))
+{
+ BUG_ON(irq_nr > MAX_INTERNAL_IRQS);
+
+ pxa_irq_base = io_p2v(0x40d00000);
+ cpu_has_ipr = !cpu_is_pxa25x();
+ pxa_init_irq_common(NULL, irq_nr, fn);
+}
+
#ifdef CONFIG_PM
static unsigned long saved_icmr[MAX_INTERNAL_IRQS/32];
static unsigned long saved_ipr[MAX_INTERNAL_IRQS];
};
#ifdef CONFIG_OF
-static struct irq_domain *pxa_irq_domain;
-
-static int pxa_irq_map(struct irq_domain *h, unsigned int virq,
- irq_hw_number_t hw)
-{
- void __iomem *base = irq_base(hw / 32);
-
- /* initialize interrupt priority */
- if (cpu_has_ipr)
- __raw_writel(hw | IPR_VALID, pxa_irq_base + IPR(hw));
-
- irq_set_chip_and_handler(hw, &pxa_internal_irq_chip,
- handle_level_irq);
- irq_set_chip_data(hw, base);
- set_irq_flags(hw, IRQF_VALID);
-
- return 0;
-}
-
-static struct irq_domain_ops pxa_irq_ops = {
- .map = pxa_irq_map,
- .xlate = irq_domain_xlate_onecell,
-};
-
static const struct of_device_id intc_ids[] __initconst = {
{ .compatible = "marvell,pxa-intc", },
{}
{
struct device_node *node;
struct resource res;
- int n, ret;
+ int ret;
node = of_find_matching_node(NULL, intc_ids);
if (!node) {
return;
}
- pxa_irq_domain = irq_domain_add_legacy(node, pxa_internal_irq_nr, 0, 0,
- &pxa_irq_ops, NULL);
- if (!pxa_irq_domain)
- panic("Unable to add PXA IRQ domain\n");
-
- irq_set_default_host(pxa_irq_domain);
-
- for (n = 0; n < pxa_internal_irq_nr; n += 32) {
- void __iomem *base = irq_base(n >> 5);
-
- __raw_writel(0, base + ICMR); /* disable all IRQs */
- __raw_writel(0, base + ICLR); /* all IRQs are IRQ, not FIQ */
- }
-
- /* only unmasked interrupts kick us out of idle */
- __raw_writel(1, irq_base(0) + ICCR);
-
- pxa_internal_irq_chip.irq_set_wake = fn;
+ pxa_init_irq_common(node, pxa_internal_irq_nr, fn);
}
#endif /* CONFIG_OF */
};
static struct platform_device can_regulator_device = {
- .name = "reg-fixed-volage",
+ .name = "reg-fixed-voltage",
.id = 0,
.dev = {
.platform_data = &can_regulator_pdata,
menuconfig ARCH_SUNXI
bool "Allwinner SoCs" if ARCH_MULTI_V7
select ARCH_REQUIRE_GPIOLIB
+ select ARCH_HAS_RESET_CONTROLLER
select CLKSRC_MMIO
select GENERIC_IRQ_CHIP
select PINCTRL
select SUN4I_TIMER
+ select RESET_CONTROLLER
if ARCH_SUNXI
config MACH_SUN6I
bool "Allwinner A31 (sun6i) SoCs support"
default ARCH_SUNXI
- select ARCH_HAS_RESET_CONTROLLER
select ARM_GIC
select MFD_SUN6I_PRCM
- select RESET_CONTROLLER
select SUN5I_HSTIMER
config MACH_SUN7I
config MACH_SUN8I
bool "Allwinner A23 (sun8i) SoCs support"
default ARCH_SUNXI
- select ARCH_HAS_RESET_CONTROLLER
select ARM_GIC
select MFD_SUN6I_PRCM
- select RESET_CONTROLLER
config MACH_SUN9I
bool "Allwinner (sun9i) SoCs support"
default ARCH_SUNXI
- select ARCH_HAS_RESET_CONTROLLER
select ARM_GIC
- select RESET_CONTROLLER
endif
struct device *dev = &pdev->dev;
const struct of_device_id *match;
const struct dmtimer_platform_data *pdata;
+ int ret;
match = of_match_device(of_match_ptr(omap_timer_match), dev);
pdata = match ? match->data : dev->platform_data;
}
if (!timer->reserved) {
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "%s: pm_runtime_get_sync failed!\n",
+ __func__);
+ goto err_get_sync;
+ }
__omap_dm_timer_init_regs(timer);
pm_runtime_put(dev);
}
dev_dbg(dev, "Device Probed.\n");
return 0;
+
+err_get_sync:
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ return ret;
}
/**
}
spin_unlock_irqrestore(&dm_timer_lock, flags);
+ pm_runtime_disable(&pdev->dev);
+
return ret;
}
*/
/* SoC fixed clocks */
- soc_uartclk: refclk72738khz {
+ soc_uartclk: refclk7273800hz {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <7273800>;
__ret; \
})
-#define this_cpu_cmpxchg_1(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
-#define this_cpu_cmpxchg_2(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
-#define this_cpu_cmpxchg_4(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
-#define this_cpu_cmpxchg_8(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
-
-#define this_cpu_cmpxchg_double_8(ptr1, ptr2, o1, o2, n1, n2) \
- cmpxchg_double_local(raw_cpu_ptr(&(ptr1)), raw_cpu_ptr(&(ptr2)), \
- o1, o2, n1, n2)
+#define _protect_cmpxchg_local(pcp, o, n) \
+({ \
+ typeof(*raw_cpu_ptr(&(pcp))) __ret; \
+ preempt_disable(); \
+ __ret = cmpxchg_local(raw_cpu_ptr(&(pcp)), o, n); \
+ preempt_enable(); \
+ __ret; \
+})
+
+#define this_cpu_cmpxchg_1(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+#define this_cpu_cmpxchg_2(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+#define this_cpu_cmpxchg_4(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+#define this_cpu_cmpxchg_8(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+
+#define this_cpu_cmpxchg_double_8(ptr1, ptr2, o1, o2, n1, n2) \
+({ \
+ int __ret; \
+ preempt_disable(); \
+ __ret = cmpxchg_double_local( raw_cpu_ptr(&(ptr1)), \
+ raw_cpu_ptr(&(ptr2)), \
+ o1, o2, n1, n2); \
+ preempt_enable(); \
+ __ret; \
+})
#define cmpxchg64(ptr,o,n) cmpxchg((ptr),(o),(n))
#define cmpxchg64_local(ptr,o,n) cmpxchg_local((ptr),(o),(n))
{
unsigned int cpu = smp_processor_id();
+ /*
+ * init_mm.pgd does not contain any user mappings and it is always
+ * active for kernel addresses in TTBR1. Just set the reserved TTBR0.
+ */
+ if (next == &init_mm) {
+ cpu_set_reserved_ttbr0();
+ return;
+ }
+
if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next)
check_and_switch_context(next, tsk);
}
return ret;
}
+#define _percpu_read(pcp) \
+({ \
+ typeof(pcp) __retval; \
+ preempt_disable(); \
+ __retval = (typeof(pcp))__percpu_read(raw_cpu_ptr(&(pcp)), \
+ sizeof(pcp)); \
+ preempt_enable(); \
+ __retval; \
+})
+
+#define _percpu_write(pcp, val) \
+do { \
+ preempt_disable(); \
+ __percpu_write(raw_cpu_ptr(&(pcp)), (unsigned long)(val), \
+ sizeof(pcp)); \
+ preempt_enable(); \
+} while(0) \
+
+#define _pcp_protect(operation, pcp, val) \
+({ \
+ typeof(pcp) __retval; \
+ preempt_disable(); \
+ __retval = (typeof(pcp))operation(raw_cpu_ptr(&(pcp)), \
+ (val), sizeof(pcp)); \
+ preempt_enable(); \
+ __retval; \
+})
+
#define _percpu_add(pcp, val) \
- __percpu_add(raw_cpu_ptr(&(pcp)), val, sizeof(pcp))
+ _pcp_protect(__percpu_add, pcp, val)
-#define _percpu_add_return(pcp, val) (typeof(pcp)) (_percpu_add(pcp, val))
+#define _percpu_add_return(pcp, val) _percpu_add(pcp, val)
#define _percpu_and(pcp, val) \
- __percpu_and(raw_cpu_ptr(&(pcp)), val, sizeof(pcp))
+ _pcp_protect(__percpu_and, pcp, val)
#define _percpu_or(pcp, val) \
- __percpu_or(raw_cpu_ptr(&(pcp)), val, sizeof(pcp))
-
-#define _percpu_read(pcp) (typeof(pcp)) \
- (__percpu_read(raw_cpu_ptr(&(pcp)), sizeof(pcp)))
-
-#define _percpu_write(pcp, val) \
- __percpu_write(raw_cpu_ptr(&(pcp)), (unsigned long)(val), sizeof(pcp))
+ _pcp_protect(__percpu_or, pcp, val)
#define _percpu_xchg(pcp, val) (typeof(pcp)) \
- (__percpu_xchg(raw_cpu_ptr(&(pcp)), (unsigned long)(val), sizeof(pcp)))
+ _pcp_protect(__percpu_xchg, pcp, (unsigned long)(val))
#define this_cpu_add_1(pcp, val) _percpu_add(pcp, val)
#define this_cpu_add_2(pcp, val) _percpu_add(pcp, val)
#define _ASM_METAG_IO_H
#include <linux/types.h>
+#include <asm/pgtable-bits.h>
#define IO_SPACE_LIMIT 0
--- /dev/null
+/*
+ * Meta page table definitions.
+ */
+
+#ifndef _METAG_PGTABLE_BITS_H
+#define _METAG_PGTABLE_BITS_H
+
+#include <asm/metag_mem.h>
+
+/*
+ * Definitions for MMU descriptors
+ *
+ * These are the hardware bits in the MMCU pte entries.
+ * Derived from the Meta toolkit headers.
+ */
+#define _PAGE_PRESENT MMCU_ENTRY_VAL_BIT
+#define _PAGE_WRITE MMCU_ENTRY_WR_BIT
+#define _PAGE_PRIV MMCU_ENTRY_PRIV_BIT
+/* Write combine bit - this can cause writes to occur out of order */
+#define _PAGE_WR_COMBINE MMCU_ENTRY_WRC_BIT
+/* Sys coherent bit - this bit is never used by Linux */
+#define _PAGE_SYS_COHERENT MMCU_ENTRY_SYS_BIT
+#define _PAGE_ALWAYS_ZERO_1 0x020
+#define _PAGE_CACHE_CTRL0 0x040
+#define _PAGE_CACHE_CTRL1 0x080
+#define _PAGE_ALWAYS_ZERO_2 0x100
+#define _PAGE_ALWAYS_ZERO_3 0x200
+#define _PAGE_ALWAYS_ZERO_4 0x400
+#define _PAGE_ALWAYS_ZERO_5 0x800
+
+/* These are software bits that we stuff into the gaps in the hardware
+ * pte entries that are not used. Note, these DO get stored in the actual
+ * hardware, but the hardware just does not use them.
+ */
+#define _PAGE_ACCESSED _PAGE_ALWAYS_ZERO_1
+#define _PAGE_DIRTY _PAGE_ALWAYS_ZERO_2
+
+/* Pages owned, and protected by, the kernel. */
+#define _PAGE_KERNEL _PAGE_PRIV
+
+/* No cacheing of this page */
+#define _PAGE_CACHE_WIN0 (MMCU_CWIN_UNCACHED << MMCU_ENTRY_CWIN_S)
+/* burst cacheing - good for data streaming */
+#define _PAGE_CACHE_WIN1 (MMCU_CWIN_BURST << MMCU_ENTRY_CWIN_S)
+/* One cache way per thread */
+#define _PAGE_CACHE_WIN2 (MMCU_CWIN_C1SET << MMCU_ENTRY_CWIN_S)
+/* Full on cacheing */
+#define _PAGE_CACHE_WIN3 (MMCU_CWIN_CACHED << MMCU_ENTRY_CWIN_S)
+
+#define _PAGE_CACHEABLE (_PAGE_CACHE_WIN3 | _PAGE_WR_COMBINE)
+
+/* which bits are used for cache control ... */
+#define _PAGE_CACHE_MASK (_PAGE_CACHE_CTRL0 | _PAGE_CACHE_CTRL1 | \
+ _PAGE_WR_COMBINE)
+
+/* This is a mask of the bits that pte_modify is allowed to change. */
+#define _PAGE_CHG_MASK (PAGE_MASK)
+
+#define _PAGE_SZ_SHIFT 1
+#define _PAGE_SZ_4K (0x0)
+#define _PAGE_SZ_8K (0x1 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_16K (0x2 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_32K (0x3 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_64K (0x4 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_128K (0x5 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_256K (0x6 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_512K (0x7 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_1M (0x8 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_2M (0x9 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_4M (0xa << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_MASK (0xf << _PAGE_SZ_SHIFT)
+
+#if defined(CONFIG_PAGE_SIZE_4K)
+#define _PAGE_SZ (_PAGE_SZ_4K)
+#elif defined(CONFIG_PAGE_SIZE_8K)
+#define _PAGE_SZ (_PAGE_SZ_8K)
+#elif defined(CONFIG_PAGE_SIZE_16K)
+#define _PAGE_SZ (_PAGE_SZ_16K)
+#endif
+#define _PAGE_TABLE (_PAGE_SZ | _PAGE_PRESENT)
+
+#if defined(CONFIG_HUGETLB_PAGE_SIZE_8K)
+# define _PAGE_SZHUGE (_PAGE_SZ_8K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_16K)
+# define _PAGE_SZHUGE (_PAGE_SZ_16K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_32K)
+# define _PAGE_SZHUGE (_PAGE_SZ_32K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
+# define _PAGE_SZHUGE (_PAGE_SZ_64K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_128K)
+# define _PAGE_SZHUGE (_PAGE_SZ_128K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_256K)
+# define _PAGE_SZHUGE (_PAGE_SZ_256K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512K)
+# define _PAGE_SZHUGE (_PAGE_SZ_512K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_1M)
+# define _PAGE_SZHUGE (_PAGE_SZ_1M)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_2M)
+# define _PAGE_SZHUGE (_PAGE_SZ_2M)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_4M)
+# define _PAGE_SZHUGE (_PAGE_SZ_4M)
+#endif
+
+#endif /* _METAG_PGTABLE_BITS_H */
#ifndef _METAG_PGTABLE_H
#define _METAG_PGTABLE_H
+#include <asm/pgtable-bits.h>
#include <asm-generic/pgtable-nopmd.h>
/* Invalid regions on Meta: 0x00000000-0x001FFFFF and 0xFFFF0000-0xFFFFFFFF */
#define VMALLOC_END 0x7FFFFFFF
#endif
-/*
- * Definitions for MMU descriptors
- *
- * These are the hardware bits in the MMCU pte entries.
- * Derived from the Meta toolkit headers.
- */
-#define _PAGE_PRESENT MMCU_ENTRY_VAL_BIT
-#define _PAGE_WRITE MMCU_ENTRY_WR_BIT
-#define _PAGE_PRIV MMCU_ENTRY_PRIV_BIT
-/* Write combine bit - this can cause writes to occur out of order */
-#define _PAGE_WR_COMBINE MMCU_ENTRY_WRC_BIT
-/* Sys coherent bit - this bit is never used by Linux */
-#define _PAGE_SYS_COHERENT MMCU_ENTRY_SYS_BIT
-#define _PAGE_ALWAYS_ZERO_1 0x020
-#define _PAGE_CACHE_CTRL0 0x040
-#define _PAGE_CACHE_CTRL1 0x080
-#define _PAGE_ALWAYS_ZERO_2 0x100
-#define _PAGE_ALWAYS_ZERO_3 0x200
-#define _PAGE_ALWAYS_ZERO_4 0x400
-#define _PAGE_ALWAYS_ZERO_5 0x800
-
-/* These are software bits that we stuff into the gaps in the hardware
- * pte entries that are not used. Note, these DO get stored in the actual
- * hardware, but the hardware just does not use them.
- */
-#define _PAGE_ACCESSED _PAGE_ALWAYS_ZERO_1
-#define _PAGE_DIRTY _PAGE_ALWAYS_ZERO_2
-
-/* Pages owned, and protected by, the kernel. */
-#define _PAGE_KERNEL _PAGE_PRIV
-
-/* No cacheing of this page */
-#define _PAGE_CACHE_WIN0 (MMCU_CWIN_UNCACHED << MMCU_ENTRY_CWIN_S)
-/* burst cacheing - good for data streaming */
-#define _PAGE_CACHE_WIN1 (MMCU_CWIN_BURST << MMCU_ENTRY_CWIN_S)
-/* One cache way per thread */
-#define _PAGE_CACHE_WIN2 (MMCU_CWIN_C1SET << MMCU_ENTRY_CWIN_S)
-/* Full on cacheing */
-#define _PAGE_CACHE_WIN3 (MMCU_CWIN_CACHED << MMCU_ENTRY_CWIN_S)
-
-#define _PAGE_CACHEABLE (_PAGE_CACHE_WIN3 | _PAGE_WR_COMBINE)
-
-/* which bits are used for cache control ... */
-#define _PAGE_CACHE_MASK (_PAGE_CACHE_CTRL0 | _PAGE_CACHE_CTRL1 | \
- _PAGE_WR_COMBINE)
-
-/* This is a mask of the bits that pte_modify is allowed to change. */
-#define _PAGE_CHG_MASK (PAGE_MASK)
-
-#define _PAGE_SZ_SHIFT 1
-#define _PAGE_SZ_4K (0x0)
-#define _PAGE_SZ_8K (0x1 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_16K (0x2 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_32K (0x3 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_64K (0x4 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_128K (0x5 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_256K (0x6 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_512K (0x7 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_1M (0x8 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_2M (0x9 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_4M (0xa << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_MASK (0xf << _PAGE_SZ_SHIFT)
-
-#if defined(CONFIG_PAGE_SIZE_4K)
-#define _PAGE_SZ (_PAGE_SZ_4K)
-#elif defined(CONFIG_PAGE_SIZE_8K)
-#define _PAGE_SZ (_PAGE_SZ_8K)
-#elif defined(CONFIG_PAGE_SIZE_16K)
-#define _PAGE_SZ (_PAGE_SZ_16K)
-#endif
-#define _PAGE_TABLE (_PAGE_SZ | _PAGE_PRESENT)
-
-#if defined(CONFIG_HUGETLB_PAGE_SIZE_8K)
-# define _PAGE_SZHUGE (_PAGE_SZ_8K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_16K)
-# define _PAGE_SZHUGE (_PAGE_SZ_16K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_32K)
-# define _PAGE_SZHUGE (_PAGE_SZ_32K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
-# define _PAGE_SZHUGE (_PAGE_SZ_64K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_128K)
-# define _PAGE_SZHUGE (_PAGE_SZ_128K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_256K)
-# define _PAGE_SZHUGE (_PAGE_SZ_256K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512K)
-# define _PAGE_SZHUGE (_PAGE_SZ_512K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_1M)
-# define _PAGE_SZHUGE (_PAGE_SZ_1M)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_2M)
-# define _PAGE_SZHUGE (_PAGE_SZ_2M)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_4M)
-# define _PAGE_SZHUGE (_PAGE_SZ_4M)
-#endif
-
/*
* The Linux memory management assumes a three-level page table setup. On
* Meta, we use that, but "fold" the mid level into the top-level page
if (likely(pgd != NULL)) {
memset(pgd, 0, PAGE_SIZE<<PGD_ALLOC_ORDER);
-#ifdef CONFIG_64BIT
+#if PT_NLEVELS == 3
actual_pgd += PTRS_PER_PGD;
/* Populate first pmd with allocated memory. We mark it
* with PxD_FLAG_ATTACHED as a signal to the system that this
static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
{
-#ifdef CONFIG_64BIT
+#if PT_NLEVELS == 3
pgd -= PTRS_PER_PGD;
#endif
free_pages((unsigned long)pgd, PGD_ALLOC_ORDER);
static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
{
-#ifdef CONFIG_64BIT
if(pmd_flag(*pmd) & PxD_FLAG_ATTACHED)
- /* This is the permanent pmd attached to the pgd;
- * cannot free it */
+ /*
+ * This is the permanent pmd attached to the pgd;
+ * cannot free it.
+ * Increment the counter to compensate for the decrement
+ * done by generic mm code.
+ */
+ mm_inc_nr_pmds(mm);
return;
-#endif
free_pages((unsigned long)pmd, PMD_ORDER);
}
static inline void
pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte)
{
-#ifdef CONFIG_64BIT
+#if PT_NLEVELS == 3
/* preserve the gateway marker if this is the beginning of
* the permanent pmd */
if(pmd_flag(*pmd) & PxD_FLAG_ATTACHED)
#define ENTRY_COMP(_name_) .word sys_##_name_
#endif
- ENTRY_SAME(restart_syscall) /* 0 */
- ENTRY_SAME(exit)
+90: ENTRY_SAME(restart_syscall) /* 0 */
+91: ENTRY_SAME(exit)
ENTRY_SAME(fork_wrapper)
ENTRY_SAME(read)
ENTRY_SAME(write)
ENTRY_SAME(bpf)
ENTRY_COMP(execveat)
- /* Nothing yet */
+
+.ifne (. - 90b) - (__NR_Linux_syscalls * (91b - 90b))
+.error "size of syscall table does not fit value of __NR_Linux_syscalls"
+.endif
#undef ENTRY_SAME
#undef ENTRY_DIFF
static inline int cpu_nr_cores(void)
{
- return NR_CPUS >> threads_shift;
+ return nr_cpu_ids >> threads_shift;
}
static inline cpumask_t cpu_online_cores_map(void)
#define PPC_INST_MFSPR_PVR_MASK 0xfc1fffff
#define PPC_INST_MFTMR 0x7c0002dc
#define PPC_INST_MSGSND 0x7c00019c
+#define PPC_INST_MSGCLR 0x7c0001dc
#define PPC_INST_MSGSNDP 0x7c00011c
#define PPC_INST_MTTMR 0x7c0003dc
#define PPC_INST_NOP 0x60000000
___PPC_RB(b) | __PPC_EH(eh))
#define PPC_MSGSND(b) stringify_in_c(.long PPC_INST_MSGSND | \
___PPC_RB(b))
+#define PPC_MSGCLR(b) stringify_in_c(.long PPC_INST_MSGCLR | \
+ ___PPC_RB(b))
#define PPC_MSGSNDP(b) stringify_in_c(.long PPC_INST_MSGSNDP | \
___PPC_RB(b))
#define PPC_POPCNTB(a, s) stringify_in_c(.long PPC_INST_POPCNTB | \
#define SRR1_ISI_N_OR_G 0x10000000 /* ISI: Access is no-exec or G */
#define SRR1_ISI_PROT 0x08000000 /* ISI: Other protection fault */
#define SRR1_WAKEMASK 0x00380000 /* reason for wakeup */
+#define SRR1_WAKEMASK_P8 0x003c0000 /* reason for wakeup on POWER8 */
#define SRR1_WAKESYSERR 0x00300000 /* System error */
#define SRR1_WAKEEE 0x00200000 /* External interrupt */
#define SRR1_WAKEMT 0x00280000 /* mtctrl */
#define SRR1_WAKEHMI 0x00280000 /* Hypervisor maintenance */
#define SRR1_WAKEDEC 0x00180000 /* Decrementer interrupt */
+#define SRR1_WAKEDBELL 0x00140000 /* Privileged doorbell on P8 */
#define SRR1_WAKETHERM 0x00100000 /* Thermal management interrupt */
#define SRR1_WAKERESET 0x00100000 /* System reset */
+#define SRR1_WAKEHDBELL 0x000c0000 /* Hypervisor doorbell on P8 */
#define SRR1_WAKESTATE 0x00030000 /* Powersave exit mask [46:47] */
#define SRR1_WS_DEEPEST 0x00030000 /* Some resources not maintained,
* may not be recoverable */
.machine_check_early = __machine_check_early_realmode_p8,
.platform = "power8",
},
+ { /* Power8NVL */
+ .pvr_mask = 0xffff0000,
+ .pvr_value = 0x004c0000,
+ .cpu_name = "POWER8NVL (raw)",
+ .cpu_features = CPU_FTRS_POWER8,
+ .cpu_user_features = COMMON_USER_POWER8,
+ .cpu_user_features2 = COMMON_USER2_POWER8,
+ .mmu_features = MMU_FTRS_POWER8,
+ .icache_bsize = 128,
+ .dcache_bsize = 128,
+ .num_pmcs = 6,
+ .pmc_type = PPC_PMC_IBM,
+ .oprofile_cpu_type = "ppc64/power8",
+ .oprofile_type = PPC_OPROFILE_INVALID,
+ .cpu_setup = __setup_cpu_power8,
+ .cpu_restore = __restore_cpu_power8,
+ .flush_tlb = __flush_tlb_power8,
+ .machine_check_early = __machine_check_early_realmode_p8,
+ .platform = "power8",
+ },
{ /* Power8 DD1: Does not support doorbell IPIs */
.pvr_mask = 0xffffff00,
.pvr_value = 0x004d0100,
#include <asm/dbell.h>
#include <asm/irq_regs.h>
+#include <asm/kvm_ppc.h>
#ifdef CONFIG_SMP
void doorbell_setup_this_cpu(void)
may_hard_irq_enable();
+ kvmppc_set_host_ipi(smp_processor_id(), 0);
__this_cpu_inc(irq_stat.doorbell_irqs);
smp_ipi_demux();
bne 9f /* continue in V mode if we are. */
5:
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_64_HANDLER
/*
* We are coming from kernel context. Check if we are coming from
* guest. if yes, then we can continue. We will fall through
spin_lock(&vcpu->arch.vpa_update_lock);
lppaca = (struct lppaca *)vcpu->arch.vpa.pinned_addr;
if (lppaca)
- yield_count = lppaca->yield_count;
+ yield_count = be32_to_cpu(lppaca->yield_count);
spin_unlock(&vcpu->arch.vpa_update_lock);
return yield_count;
}
static void kvmppc_set_lpcr(struct kvm_vcpu *vcpu, u64 new_lpcr,
bool preserve_top32)
{
+ struct kvm *kvm = vcpu->kvm;
struct kvmppc_vcore *vc = vcpu->arch.vcore;
u64 mask;
+ mutex_lock(&kvm->lock);
spin_lock(&vc->lock);
/*
* If ILE (interrupt little-endian) has changed, update the
* MSR_LE bit in the intr_msr for each vcpu in this vcore.
*/
if ((new_lpcr & LPCR_ILE) != (vc->lpcr & LPCR_ILE)) {
- struct kvm *kvm = vcpu->kvm;
struct kvm_vcpu *vcpu;
int i;
- mutex_lock(&kvm->lock);
kvm_for_each_vcpu(i, vcpu, kvm) {
if (vcpu->arch.vcore != vc)
continue;
else
vcpu->arch.intr_msr &= ~MSR_LE;
}
- mutex_unlock(&kvm->lock);
}
/*
mask &= 0xFFFFFFFF;
vc->lpcr = (vc->lpcr & ~mask) | (new_lpcr & mask);
spin_unlock(&vc->lock);
+ mutex_unlock(&kvm->lock);
}
static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
/* Save HEIR (HV emulation assist reg) in emul_inst
if this is an HEI (HV emulation interrupt, e40) */
li r3,KVM_INST_FETCH_FAILED
+ stw r3,VCPU_LAST_INST(r9)
cmpwi r12,BOOK3S_INTERRUPT_H_EMUL_ASSIST
bne 11f
mfspr r3,SPRN_HEIR
#include <asm/runlatch.h>
#include <asm/code-patching.h>
#include <asm/dbell.h>
+#include <asm/kvm_ppc.h>
+#include <asm/ppc-opcode.h>
#include "powernv.h"
static void pnv_smp_cpu_kill_self(void)
{
unsigned int cpu;
- unsigned long srr1;
+ unsigned long srr1, wmask;
u32 idle_states;
/* Standard hot unplug procedure */
generic_set_cpu_dead(cpu);
smp_wmb();
+ wmask = SRR1_WAKEMASK;
+ if (cpu_has_feature(CPU_FTR_ARCH_207S))
+ wmask = SRR1_WAKEMASK_P8;
+
idle_states = pnv_get_supported_cpuidle_states();
/* We don't want to take decrementer interrupts while we are offline,
* so clear LPCR:PECE1. We keep PECE2 enabled.
* having finished executing in a KVM guest, then srr1
* contains 0.
*/
- if ((srr1 & SRR1_WAKEMASK) == SRR1_WAKEEE) {
+ if ((srr1 & wmask) == SRR1_WAKEEE) {
icp_native_flush_interrupt();
local_paca->irq_happened &= PACA_IRQ_HARD_DIS;
smp_mb();
+ } else if ((srr1 & wmask) == SRR1_WAKEHDBELL) {
+ unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER);
+ asm volatile(PPC_MSGCLR(%0) : : "r" (msg));
+ kvmppc_set_host_ipi(cpu, 0);
}
if (cpu_core_split_required())
static struct kobject *mobility_kobj;
struct update_props_workarea {
- u32 phandle;
- u32 state;
- u64 reserved;
- u32 nprops;
+ __be32 phandle;
+ __be32 state;
+ __be64 reserved;
+ __be32 nprops;
} __packed;
#define NODE_ACTION_MASK 0xff000000
return rc;
}
-static int delete_dt_node(u32 phandle)
+static int delete_dt_node(__be32 phandle)
{
struct device_node *dn;
- dn = of_find_node_by_phandle(phandle);
+ dn = of_find_node_by_phandle(be32_to_cpu(phandle));
if (!dn)
return -ENOENT;
return 0;
}
-static int update_dt_node(u32 phandle, s32 scope)
+static int update_dt_node(__be32 phandle, s32 scope)
{
struct update_props_workarea *upwa;
struct device_node *dn;
char *prop_data;
char *rtas_buf;
int update_properties_token;
+ u32 nprops;
u32 vd;
update_properties_token = rtas_token("ibm,update-properties");
if (!rtas_buf)
return -ENOMEM;
- dn = of_find_node_by_phandle(phandle);
+ dn = of_find_node_by_phandle(be32_to_cpu(phandle));
if (!dn) {
kfree(rtas_buf);
return -ENOENT;
break;
prop_data = rtas_buf + sizeof(*upwa);
+ nprops = be32_to_cpu(upwa->nprops);
/* On the first call to ibm,update-properties for a node the
* the first property value descriptor contains an empty
*/
if (*prop_data == 0) {
prop_data++;
- vd = *(u32 *)prop_data;
+ vd = be32_to_cpu(*(__be32 *)prop_data);
prop_data += vd + sizeof(vd);
- upwa->nprops--;
+ nprops--;
}
- for (i = 0; i < upwa->nprops; i++) {
+ for (i = 0; i < nprops; i++) {
char *prop_name;
prop_name = prop_data;
prop_data += strlen(prop_name) + 1;
- vd = *(u32 *)prop_data;
+ vd = be32_to_cpu(*(__be32 *)prop_data);
prop_data += sizeof(vd);
switch (vd) {
return 0;
}
-static int add_dt_node(u32 parent_phandle, u32 drc_index)
+static int add_dt_node(__be32 parent_phandle, __be32 drc_index)
{
struct device_node *dn;
struct device_node *parent_dn;
int rc;
- parent_dn = of_find_node_by_phandle(parent_phandle);
+ parent_dn = of_find_node_by_phandle(be32_to_cpu(parent_phandle));
if (!parent_dn)
return -ENOENT;
int pseries_devicetree_update(s32 scope)
{
char *rtas_buf;
- u32 *data;
+ __be32 *data;
int update_nodes_token;
int rc;
if (rc && rc != 1)
break;
- data = (u32 *)rtas_buf + 4;
- while (*data & NODE_ACTION_MASK) {
+ data = (__be32 *)rtas_buf + 4;
+ while (be32_to_cpu(*data) & NODE_ACTION_MASK) {
int i;
- u32 action = *data & NODE_ACTION_MASK;
- int node_count = *data & NODE_COUNT_MASK;
+ u32 action = be32_to_cpu(*data) & NODE_ACTION_MASK;
+ u32 node_count = be32_to_cpu(*data) & NODE_COUNT_MASK;
data++;
for (i = 0; i < node_count; i++) {
- u32 phandle = *data++;
- u32 drc_index;
+ __be32 phandle = *data++;
+ __be32 drc_index;
switch (action) {
case DELETE_DT_NODE:
extern unsigned long mmap_rnd_mask;
-#define STACK_RND_MASK (mmap_rnd_mask)
+#define STACK_RND_MASK (test_thread_flag(TIF_31BIT) ? 0x7ff : mmap_rnd_mask)
#define ARCH_DLINFO \
do { \
unsigned long ftrace_plt;
+static inline void ftrace_generate_orig_insn(struct ftrace_insn *insn)
+{
+#ifdef CC_USING_HOTPATCH
+ /* brcl 0,0 */
+ insn->opc = 0xc004;
+ insn->disp = 0;
+#else
+ /* stg r14,8(r15) */
+ insn->opc = 0xe3e0;
+ insn->disp = 0xf0080024;
+#endif
+}
+
+static inline int is_kprobe_on_ftrace(struct ftrace_insn *insn)
+{
+#ifdef CONFIG_KPROBES
+ if (insn->opc == BREAKPOINT_INSTRUCTION)
+ return 1;
+#endif
+ return 0;
+}
+
+static inline void ftrace_generate_kprobe_nop_insn(struct ftrace_insn *insn)
+{
+#ifdef CONFIG_KPROBES
+ insn->opc = BREAKPOINT_INSTRUCTION;
+ insn->disp = KPROBE_ON_FTRACE_NOP;
+#endif
+}
+
+static inline void ftrace_generate_kprobe_call_insn(struct ftrace_insn *insn)
+{
+#ifdef CONFIG_KPROBES
+ insn->opc = BREAKPOINT_INSTRUCTION;
+ insn->disp = KPROBE_ON_FTRACE_CALL;
+#endif
+}
+
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
unsigned long addr)
{
return -EFAULT;
if (addr == MCOUNT_ADDR) {
/* Initial code replacement */
-#ifdef CC_USING_HOTPATCH
- /* We expect to see brcl 0,0 */
- ftrace_generate_nop_insn(&orig);
-#else
- /* We expect to see stg r14,8(r15) */
- orig.opc = 0xe3e0;
- orig.disp = 0xf0080024;
-#endif
+ ftrace_generate_orig_insn(&orig);
ftrace_generate_nop_insn(&new);
- } else if (old.opc == BREAKPOINT_INSTRUCTION) {
+ } else if (is_kprobe_on_ftrace(&old)) {
/*
* If we find a breakpoint instruction, a kprobe has been
* placed at the beginning of the function. We write the
* bytes of the original instruction so that the kprobes
* handler can execute a nop, if it reaches this breakpoint.
*/
- new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
- orig.disp = KPROBE_ON_FTRACE_CALL;
- new.disp = KPROBE_ON_FTRACE_NOP;
+ ftrace_generate_kprobe_call_insn(&orig);
+ ftrace_generate_kprobe_nop_insn(&new);
} else {
/* Replace ftrace call with a nop. */
ftrace_generate_call_insn(&orig, rec->ip);
if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
return -EFAULT;
- if (old.opc == BREAKPOINT_INSTRUCTION) {
+ if (is_kprobe_on_ftrace(&old)) {
/*
* If we find a breakpoint instruction, a kprobe has been
* placed at the beginning of the function. We write the
* bytes of the original instruction so that the kprobes
* handler can execute a brasl if it reaches this breakpoint.
*/
- new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
- orig.disp = KPROBE_ON_FTRACE_NOP;
- new.disp = KPROBE_ON_FTRACE_CALL;
+ ftrace_generate_kprobe_nop_insn(&orig);
+ ftrace_generate_kprobe_call_insn(&new);
} else {
/* Replace nop with an ftrace call. */
ftrace_generate_nop_insn(&orig);
static struct attribute *cpumsf_pmu_events_attr[] = {
CPUMF_EVENT_PTR(SF, SF_CYCLES_BASIC),
- CPUMF_EVENT_PTR(SF, SF_CYCLES_BASIC_DIAG),
+ NULL,
NULL,
};
return -EINVAL;
}
- if (si.ad)
+ if (si.ad) {
sfb_set_limits(CPUM_SF_MIN_SDB, CPUM_SF_MAX_SDB);
+ cpumsf_pmu_events_attr[1] =
+ CPUMF_EVENT_PTR(SF, SF_CYCLES_BASIC_DIAG);
+ }
sfdbg = debug_register(KMSG_COMPONENT, 2, 1, 80);
if (!sfdbg)
lhi %r1,1
sigp %r1,%r0,SIGP_SET_ARCHITECTURE
sam64
+#ifdef CONFIG_SMP
+ larl %r1,smp_cpu_mt_shift
+ icm %r1,15,0(%r1)
+ jz smt_done
+ llgfr %r1,%r1
+smt_loop:
+ sigp %r1,%r0,SIGP_SET_MULTI_THREADING
+ brc 8,smt_done /* accepted */
+ brc 2,smt_loop /* busy, try again */
+smt_done:
+#endif
larl %r1,.Lnew_pgm_check_psw
lpswe 0(%r1)
pgm_check_entry:
INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PREC_DIST */
INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.LOAD_LATENCY */
/* CYCLE_ACTIVITY.CYCLES_L1D_PENDING */
- INTEL_EVENT_CONSTRAINT(0x08a3, 0x4),
+ INTEL_UEVENT_CONSTRAINT(0x08a3, 0x4),
/* CYCLE_ACTIVITY.STALLS_L1D_PENDING */
- INTEL_EVENT_CONSTRAINT(0x0ca3, 0x4),
+ INTEL_UEVENT_CONSTRAINT(0x0ca3, 0x4),
/* CYCLE_ACTIVITY.CYCLES_NO_EXECUTE */
- INTEL_EVENT_CONSTRAINT(0x04a3, 0xf),
+ INTEL_UEVENT_CONSTRAINT(0x04a3, 0xf),
EVENT_CONSTRAINT_END
};
if (c)
return c;
- c = intel_pebs_constraints(event);
+ c = intel_shared_regs_constraints(cpuc, event);
if (c)
return c;
- c = intel_shared_regs_constraints(cpuc, event);
+ c = intel_pebs_constraints(event);
if (c)
return c;
* Has incomplete stack frame and undefined top of stack.
*/
ret_from_sys_call:
- testl $_TIF_ALLWORK_MASK,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
- jnz int_ret_from_sys_call_fixup /* Go the the slow path */
-
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
+
+ /*
+ * We must check ti flags with interrupts (or at least preemption)
+ * off because we must *never* return to userspace without
+ * processing exit work that is enqueued if we're preempted here.
+ * In particular, returning to userspace with any of the one-shot
+ * flags (TIF_NOTIFY_RESUME, TIF_USER_RETURN_NOTIFY, etc) set is
+ * very bad.
+ */
+ testl $_TIF_ALLWORK_MASK,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
+ jnz int_ret_from_sys_call_fixup /* Go the the slow path */
+
CFI_REMEMBER_STATE
/*
* sysretq will re-enable interrupts:
int_ret_from_sys_call_fixup:
FIXUP_TOP_OF_STACK %r11, -ARGOFFSET
- jmp int_ret_from_sys_call
+ jmp int_ret_from_sys_call_irqs_off
/* Do syscall tracing */
tracesys:
GLOBAL(int_ret_from_sys_call)
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
+int_ret_from_sys_call_irqs_off:
movl $_TIF_ALLWORK_MASK,%edi
/* edi: mask to check */
GLOBAL(int_with_check)
cmpq %r11,(EFLAGS-ARGOFFSET)(%rsp) /* R11 == RFLAGS */
jne opportunistic_sysret_failed
- testq $X86_EFLAGS_RF,%r11 /* sysret can't restore RF */
+ /*
+ * SYSRET can't restore RF. SYSRET can restore TF, but unlike IRET,
+ * restoring TF results in a trap from userspace immediately after
+ * SYSRET. This would cause an infinite loop whenever #DB happens
+ * with register state that satisfies the opportunistic SYSRET
+ * conditions. For example, single-stepping this user code:
+ *
+ * movq $stuck_here,%rcx
+ * pushfq
+ * popq %r11
+ * stuck_here:
+ *
+ * would never get past 'stuck_here'.
+ */
+ testq $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11
jnz opportunistic_sysret_failed
/* nothing to check for RSP */
{ "bx", 8, offsetof(struct pt_regs, bx) },
{ "cx", 8, offsetof(struct pt_regs, cx) },
{ "dx", 8, offsetof(struct pt_regs, dx) },
- { "si", 8, offsetof(struct pt_regs, dx) },
+ { "si", 8, offsetof(struct pt_regs, si) },
{ "di", 8, offsetof(struct pt_regs, di) },
{ "bp", 8, offsetof(struct pt_regs, bp) },
{ "sp", 8, offsetof(struct pt_regs, sp) },
},
},
+ /* ASRock */
+ { /* Handle problems with rebooting on ASRock Q1900DC-ITX */
+ .callback = set_pci_reboot,
+ .ident = "ASRock Q1900DC-ITX",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASRock"),
+ DMI_MATCH(DMI_BOARD_NAME, "Q1900DC-ITX"),
+ },
+ },
+
/* ASUS */
{ /* Handle problems with rebooting on ASUS P4S800 */
.callback = set_bios_reboot,
struct kvm_ioapic *ioapic, int vector, int trigger_mode)
{
int i;
+ struct kvm_lapic *apic = vcpu->arch.apic;
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
union kvm_ioapic_redirect_entry *ent = &ioapic->redirtbl[i];
kvm_notify_acked_irq(ioapic->kvm, KVM_IRQCHIP_IOAPIC, i);
spin_lock(&ioapic->lock);
- if (trigger_mode != IOAPIC_LEVEL_TRIG)
+ if (trigger_mode != IOAPIC_LEVEL_TRIG ||
+ kvm_apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI)
continue;
ASSERT(ent->fields.trig_mode == IOAPIC_LEVEL_TRIG);
static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
{
- if (!(kvm_apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI) &&
- kvm_ioapic_handles_vector(apic->vcpu->kvm, vector)) {
+ if (kvm_ioapic_handles_vector(apic->vcpu->kvm, vector)) {
int trigger_mode;
if (apic_test_vector(vector, apic->regs + APIC_TMR))
trigger_mode = IOAPIC_LEVEL_TRIG;
if (enable_ept) {
/* nested EPT: emulate EPT also to L1 */
vmx->nested.nested_vmx_secondary_ctls_high |=
- SECONDARY_EXEC_ENABLE_EPT |
- SECONDARY_EXEC_UNRESTRICTED_GUEST;
+ SECONDARY_EXEC_ENABLE_EPT;
vmx->nested.nested_vmx_ept_caps = VMX_EPT_PAGE_WALK_4_BIT |
VMX_EPTP_WB_BIT | VMX_EPT_2MB_PAGE_BIT |
VMX_EPT_INVEPT_BIT;
} else
vmx->nested.nested_vmx_ept_caps = 0;
+ if (enable_unrestricted_guest)
+ vmx->nested.nested_vmx_secondary_ctls_high |=
+ SECONDARY_EXEC_UNRESTRICTED_GUEST;
+
/* miscellaneous data */
rdmsr(MSR_IA32_VMX_MISC,
vmx->nested.nested_vmx_misc_low,
unsigned long xen_max_p2m_pfn __read_mostly;
EXPORT_SYMBOL_GPL(xen_max_p2m_pfn);
+#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG_LIMIT
+#define P2M_LIMIT CONFIG_XEN_BALLOON_MEMORY_HOTPLUG_LIMIT
+#else
+#define P2M_LIMIT 0
+#endif
+
static DEFINE_SPINLOCK(p2m_update_lock);
static unsigned long *p2m_mid_missing_mfn;
void __init xen_vmalloc_p2m_tree(void)
{
static struct vm_struct vm;
+ unsigned long p2m_limit;
+ p2m_limit = (phys_addr_t)P2M_LIMIT * 1024 * 1024 * 1024 / PAGE_SIZE;
vm.flags = VM_ALLOC;
- vm.size = ALIGN(sizeof(unsigned long) * xen_max_p2m_pfn,
+ vm.size = ALIGN(sizeof(unsigned long) * max(xen_max_p2m_pfn, p2m_limit),
PMD_SIZE * PMDS_PER_MID_PAGE);
vm_area_register_early(&vm, PMD_SIZE * PMDS_PER_MID_PAGE);
pr_notice("p2m virtual area at %p, size is %lx\n", vm.addr, vm.size);
if (q->queue_flags & (1 << QUEUE_FLAG_SG_GAPS)) {
struct bio_vec *bprev;
- bprev = &rq->biotail->bi_io_vec[bio->bi_vcnt - 1];
+ bprev = &rq->biotail->bi_io_vec[rq->biotail->bi_vcnt - 1];
if (bvec_gap_to_prev(bprev, bio->bi_io_vec[0].bv_offset))
return false;
}
/*
* We're out of tags on this hardware queue, kick any
* pending IO submits before going to sleep waiting for
- * some to complete.
+ * some to complete. Note that hctx can be NULL here for
+ * reserved tag allocation.
*/
- blk_mq_run_hw_queue(hctx, false);
+ if (hctx)
+ blk_mq_run_hw_queue(hctx, false);
/*
* Retry tag allocation after running the hardware queue,
*/
if (percpu_ref_init(&q->mq_usage_counter, blk_mq_usage_counter_release,
PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
- goto err_map;
+ goto err_mq_usage;
setup_timer(&q->timeout, blk_mq_rq_timer, (unsigned long) q);
blk_queue_rq_timeout(q, 30000);
blk_mq_init_cpu_queues(q, set->nr_hw_queues);
if (blk_mq_init_hw_queues(q, set))
- goto err_hw;
+ goto err_mq_usage;
mutex_lock(&all_q_mutex);
list_add_tail(&q->all_q_node, &all_q_list);
return q;
-err_hw:
+err_mq_usage:
blk_cleanup_queue(q);
err_hctxs:
kfree(map);
b->physical_block_size);
t->io_min = max(t->io_min, b->io_min);
- t->io_opt = lcm(t->io_opt, b->io_opt);
+ t->io_opt = lcm_not_zero(t->io_opt, b->io_opt);
t->cluster &= b->cluster;
t->discard_zeroes_data &= b->discard_zeroes_data;
b->raid_partial_stripes_expensive);
/* Find lowest common alignment_offset */
- t->alignment_offset = lcm(t->alignment_offset, alignment)
+ t->alignment_offset = lcm_not_zero(t->alignment_offset, alignment)
% max(t->physical_block_size, t->io_min);
/* Verify that new alignment_offset is on a logical block boundary */
b->max_discard_sectors);
t->discard_granularity = max(t->discard_granularity,
b->discard_granularity);
- t->discard_alignment = lcm(t->discard_alignment, alignment) %
+ t->discard_alignment = lcm_not_zero(t->discard_alignment, alignment) %
t->discard_granularity;
}
sg_unmark_end(sgl_prev->sg + sgl_prev->npages - 1);
sg_chain(sgl_prev->sg, sgl_prev->npages + 1, sgl_new->sg);
}
-EXPORT_SYMBOL(af_alg_link_sg);
+EXPORT_SYMBOL_GPL(af_alg_link_sg);
void af_alg_free_sg(struct af_alg_sgl *sgl)
{
struct skcipher_async_req *sreq;
struct ablkcipher_request *req;
struct skcipher_async_rsgl *last_rsgl = NULL;
- unsigned int len = 0, tx_nents = skcipher_all_sg_nents(ctx);
+ unsigned int txbufs = 0, len = 0, tx_nents = skcipher_all_sg_nents(ctx);
unsigned int reqlen = sizeof(struct skcipher_async_req) +
GET_REQ_SIZE(ctx) + GET_IV_SIZE(ctx);
- int i = 0;
int err = -ENOMEM;
+ bool mark = false;
lock_sock(sk);
req = kmalloc(reqlen, GFP_KERNEL);
iov_iter_count(&msg->msg_iter));
used = min_t(unsigned long, used, sg->length);
- if (i == tx_nents) {
+ if (txbufs == tx_nents) {
struct scatterlist *tmp;
int x;
/* Ran out of tx slots in async request
kfree(sreq->tsg);
sreq->tsg = tmp;
tx_nents *= 2;
+ mark = true;
}
/* Need to take over the tx sgl from ctx
* to the asynch req - these sgls will be freed later */
- sg_set_page(sreq->tsg + i++, sg_page(sg), sg->length,
+ sg_set_page(sreq->tsg + txbufs++, sg_page(sg), sg->length,
sg->offset);
if (list_empty(&sreq->list)) {
rsgl = &sreq->first_sgl;
list_add_tail(&rsgl->list, &sreq->list);
} else {
- rsgl = kzalloc(sizeof(*rsgl), GFP_KERNEL);
+ rsgl = kmalloc(sizeof(*rsgl), GFP_KERNEL);
if (!rsgl) {
err = -ENOMEM;
goto free;
iov_iter_advance(&msg->msg_iter, used);
}
+ if (mark)
+ sg_mark_end(sreq->tsg + txbufs - 1);
+
ablkcipher_request_set_crypt(req, sreq->tsg, sreq->first_sgl.sgl.sg,
len, sreq->iv);
err = ctx->enc ? crypto_ablkcipher_encrypt(req) :
{ "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER },
/* devices that don't properly handle queued TRIM commands */
- { "Micron_M[56]*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Micron_M5[15]0*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Crucial_CT*M550*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Samsung SSD 850 PRO*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
- { "Crucial_CT*SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, },
/*
* As defined, the DRAT (Deterministic Read After Trim) and RZAT
*/
{ "INTEL*SSDSC2MH*", NULL, 0, },
+ { "Micron*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Crucial*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "INTEL*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "SSD*INTEL*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Samsung*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
return NULL;
/* libsas case */
- if (!ap->scsi_host) {
+ if (ap->flags & ATA_FLAG_SAS_HOST) {
tag = ata_sas_allocate_tag(ap);
if (tag < 0)
return NULL;
tag = qc->tag;
if (likely(ata_tag_valid(tag))) {
qc->tag = ATA_TAG_POISON;
- if (!ap->scsi_host)
+ if (ap->flags & ATA_FLAG_SAS_HOST)
ata_sas_free_tag(tag, ap);
}
}
extern struct regcache_ops regcache_lzo_ops;
extern struct regcache_ops regcache_flat_ops;
+static inline const char *regmap_name(const struct regmap *map)
+{
+ if (map->dev)
+ return dev_name(map->dev);
+
+ return map->name;
+}
+
#endif
ret = map->cache_ops->read(map, reg, value);
if (ret == 0)
- trace_regmap_reg_read_cache(map->dev, reg, *value);
+ trace_regmap_reg_read_cache(map, reg, *value);
return ret;
}
dev_dbg(map->dev, "Syncing %s cache\n",
map->cache_ops->name);
name = map->cache_ops->name;
- trace_regcache_sync(map->dev, name, "start");
+ trace_regcache_sync(map, name, "start");
if (!map->cache_dirty)
goto out;
regmap_async_complete(map);
- trace_regcache_sync(map->dev, name, "stop");
+ trace_regcache_sync(map, name, "stop");
return ret;
}
name = map->cache_ops->name;
dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max);
- trace_regcache_sync(map->dev, name, "start region");
+ trace_regcache_sync(map, name, "start region");
if (!map->cache_dirty)
goto out;
regmap_async_complete(map);
- trace_regcache_sync(map->dev, name, "stop region");
+ trace_regcache_sync(map, name, "stop region");
return ret;
}
map->lock(map->lock_arg);
- trace_regcache_drop_region(map->dev, min, max);
+ trace_regcache_drop_region(map, min, max);
ret = map->cache_ops->drop(map, min, max);
map->lock(map->lock_arg);
WARN_ON(map->cache_bypass && enable);
map->cache_only = enable;
- trace_regmap_cache_only(map->dev, enable);
+ trace_regmap_cache_only(map, enable);
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_only);
map->lock(map->lock_arg);
WARN_ON(map->cache_only && enable);
map->cache_bypass = enable;
- trace_regmap_cache_bypass(map->dev, enable);
+ trace_regmap_cache_bypass(map, enable);
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
if (map->async && map->bus->async_write) {
struct regmap_async *async;
- trace_regmap_async_write_start(map->dev, reg, val_len);
+ trace_regmap_async_write_start(map, reg, val_len);
spin_lock_irqsave(&map->async_lock, flags);
async = list_first_entry_or_null(&map->async_free,
return ret;
}
- trace_regmap_hw_write_start(map->dev, reg,
- val_len / map->format.val_bytes);
+ trace_regmap_hw_write_start(map, reg, val_len / map->format.val_bytes);
/* If we're doing a single register write we can probably just
* send the work_buf directly, otherwise try to do a gather
kfree(buf);
}
- trace_regmap_hw_write_done(map->dev, reg,
- val_len / map->format.val_bytes);
+ trace_regmap_hw_write_done(map, reg, val_len / map->format.val_bytes);
return ret;
}
map->format.format_write(map, reg, val);
- trace_regmap_hw_write_start(map->dev, reg, 1);
+ trace_regmap_hw_write_start(map, reg, 1);
ret = map->bus->write(map->bus_context, map->work_buf,
map->format.buf_size);
- trace_regmap_hw_write_done(map->dev, reg, 1);
+ trace_regmap_hw_write_done(map, reg, 1);
return ret;
}
dev_info(map->dev, "%x <= %x\n", reg, val);
#endif
- trace_regmap_reg_write(map->dev, reg, val);
+ trace_regmap_reg_write(map, reg, val);
return map->reg_write(context, reg, val);
}
for (i = 0; i < num_regs; i++) {
int reg = regs[i].reg;
int val = regs[i].def;
- trace_regmap_hw_write_start(map->dev, reg, 1);
+ trace_regmap_hw_write_start(map, reg, 1);
map->format.format_reg(u8, reg, map->reg_shift);
u8 += reg_bytes + pad_bytes;
map->format.format_val(u8, val, 0);
for (i = 0; i < num_regs; i++) {
int reg = regs[i].reg;
- trace_regmap_hw_write_done(map->dev, reg, 1);
+ trace_regmap_hw_write_done(map, reg, 1);
}
return ret;
}
*/
u8[0] |= map->read_flag_mask;
- trace_regmap_hw_read_start(map->dev, reg,
- val_len / map->format.val_bytes);
+ trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes);
ret = map->bus->read(map->bus_context, map->work_buf,
map->format.reg_bytes + map->format.pad_bytes,
val, val_len);
- trace_regmap_hw_read_done(map->dev, reg,
- val_len / map->format.val_bytes);
+ trace_regmap_hw_read_done(map, reg, val_len / map->format.val_bytes);
return ret;
}
dev_info(map->dev, "%x => %x\n", reg, *val);
#endif
- trace_regmap_reg_read(map->dev, reg, *val);
+ trace_regmap_reg_read(map, reg, *val);
if (!map->cache_bypass)
regcache_write(map, reg, *val);
struct regmap *map = async->map;
bool wake;
- trace_regmap_async_io_complete(map->dev);
+ trace_regmap_async_io_complete(map);
spin_lock(&map->async_lock);
list_move(&async->list, &map->async_free);
if (!map->bus || !map->bus->async_write)
return 0;
- trace_regmap_async_complete_start(map->dev);
+ trace_regmap_async_complete_start(map);
wait_event(map->async_waitq, regmap_async_is_done(map));
map->async_ret = 0;
spin_unlock_irqrestore(&map->async_lock, flags);
- trace_regmap_async_complete_done(map->dev);
+ trace_regmap_async_complete_done(map);
return ret;
}
config BCMA_POSSIBLE
bool
- depends on HAS_IOMEM && HAS_DMA && PCI
+ depends on HAS_IOMEM && HAS_DMA
default y
menu "Broadcom specific AMBA"
If unsure, say N
-# TODO: make it depend on PCI when ready
config BCMA_DRIVER_PCI
- bool
+ bool "BCMA Broadcom PCI core driver"
+ depends on BCMA && PCI
default y
help
BCMA bus may have many versions of PCIe core. This driver
#endif /* CONFIG_BCMA_HOST_SOC && CONFIG_OF */
/* driver_pci.c */
+#ifdef CONFIG_BCMA_DRIVER_PCI
u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address);
void bcma_core_pci_early_init(struct bcma_drv_pci *pc);
void bcma_core_pci_init(struct bcma_drv_pci *pc);
void bcma_core_pci_up(struct bcma_drv_pci *pc);
void bcma_core_pci_down(struct bcma_drv_pci *pc);
+#else
+static inline void bcma_core_pci_early_init(struct bcma_drv_pci *pc)
+{
+ WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI);
+}
+static inline void bcma_core_pci_init(struct bcma_drv_pci *pc)
+{
+ /* Initialization is required for PCI hosted bus */
+ WARN_ON(pc->core->bus->hosttype == BCMA_HOSTTYPE_PCI);
+}
+#endif
/* driver_pcie2.c */
+#ifdef CONFIG_BCMA_DRIVER_PCI
void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2);
void bcma_core_pcie2_up(struct bcma_drv_pcie2 *pcie2);
+#else
+static inline void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2)
+{
+ /* Initialization is required for PCI hosted bus */
+ WARN_ON(pcie2->core->bus->hosttype == BCMA_HOSTTYPE_PCI);
+}
+#endif
extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc);
#include "bcma_private.h"
+#define BCMA_GPIO_MAX_PINS 32
+
static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip)
{
return container_of(chip, struct bcma_drv_cc, gpio);
int bcma_gpio_init(struct bcma_drv_cc *cc)
{
+ struct bcma_bus *bus = cc->core->bus;
struct gpio_chip *chip = &cc->gpio;
int err;
if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
chip->of_node = cc->core->dev.of_node;
#endif
- switch (cc->core->bus->chipinfo.id) {
+ switch (bus->chipinfo.id) {
case BCMA_CHIP_ID_BCM5357:
case BCMA_CHIP_ID_BCM53572:
chip->ngpio = 32;
chip->ngpio = 16;
}
- /* There is just one SoC in one device and its GPIO addresses should be
- * deterministic to address them more easily. The other buses could get
- * a random base number. */
- if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
- chip->base = 0;
- else
- chip->base = -1;
+ /*
+ * On MIPS we register GPIO devices (LEDs, buttons) using absolute GPIO
+ * pin numbers. We don't have Device Tree there and we can't really use
+ * relative (per chip) numbers.
+ * So let's use predictable base for BCM47XX and "random" for all other.
+ */
+#if IS_BUILTIN(CONFIG_BCM47XX)
+ chip->base = bus->num * BCMA_GPIO_MAX_PINS;
+#else
+ chip->base = -1;
+#endif
err = bcma_gpio_irq_domain_init(cc);
if (err)
}
EXPORT_SYMBOL_GPL(bcma_core_pci_power_save);
-int bcma_core_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core,
- bool enable)
-{
- struct pci_dev *pdev;
- u32 coremask, tmp;
- int err = 0;
-
- if (bus->hosttype != BCMA_HOSTTYPE_PCI) {
- /* This bcma device is not on a PCI host-bus. So the IRQs are
- * not routed through the PCI core.
- * So we must not enable routing through the PCI core. */
- goto out;
- }
-
- pdev = bus->host_pci;
-
- err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
- if (err)
- goto out;
-
- coremask = BIT(core->core_index) << 8;
- if (enable)
- tmp |= coremask;
- else
- tmp &= ~coremask;
-
- err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
-
-out:
- return err;
-}
-EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
-
static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
{
u32 w;
bcma_core_pci_down(&bus->drv_pci[0]);
}
EXPORT_SYMBOL_GPL(bcma_host_pci_down);
+
+/* See also si_pci_setup */
+int bcma_host_pci_irq_ctl(struct bcma_bus *bus, struct bcma_device *core,
+ bool enable)
+{
+ struct pci_dev *pdev;
+ u32 coremask, tmp;
+ int err = 0;
+
+ if (bus->hosttype != BCMA_HOSTTYPE_PCI) {
+ /* This bcma device is not on a PCI host-bus. So the IRQs are
+ * not routed through the PCI core.
+ * So we must not enable routing through the PCI core. */
+ goto out;
+ }
+
+ pdev = bus->host_pci;
+
+ err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
+ if (err)
+ goto out;
+
+ coremask = BIT(core->core_index) << 8;
+ if (enable)
+ tmp |= coremask;
+ else
+ tmp &= ~coremask;
+
+ err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
+
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(bcma_host_pci_irq_ctl);
return -EINVAL;
}
- nbd_dev = kcalloc(nbds_max, sizeof(*nbd_dev), GFP_KERNEL);
- if (!nbd_dev)
- return -ENOMEM;
-
part_shift = 0;
if (max_part > 0) {
part_shift = fls(max_part);
if (nbds_max > 1UL << (MINORBITS - part_shift))
return -EINVAL;
+ nbd_dev = kcalloc(nbds_max, sizeof(*nbd_dev), GFP_KERNEL);
+ if (!nbd_dev)
+ return -ENOMEM;
+
for (i = 0; i < nbds_max; i++) {
struct gendisk *disk = alloc_disk(1 << part_shift);
if (!disk)
}
get_device(dev->device);
+ INIT_LIST_HEAD(&dev->node);
INIT_WORK(&dev->probe_work, nvme_async_probe);
schedule_work(&dev->probe_work);
return 0;
menu "Bluetooth device drivers"
depends on BT
+config BT_INTEL
+ tristate
+
+config BT_BCM
+ tristate
+ select FW_LOADER
+
config BT_HCIBTUSB
tristate "HCI USB driver"
depends on USB
+ select BT_INTEL
help
Bluetooth HCI USB driver.
This driver is required if you want to use Bluetooth devices with
Say Y here to compile support for Bluetooth USB devices into the
kernel or say M to compile it as module (btusb).
+config BT_HCIBTUSB_BCM
+ bool "Broadcom protocol support"
+ depends on BT_HCIBTUSB
+ select BT_BCM
+ default y
+ help
+ The Broadcom protocol support enables firmware and patchram
+ download support for Broadcom Bluetooth controllers.
+
+ Say Y here to compile support for Broadcom protocol.
+
config BT_HCIBTSDIO
tristate "HCI SDIO driver"
depends on MMC
config BT_HCIUART_ATH3K
bool "Atheros AR300x serial support"
depends on BT_HCIUART
+ select BT_HCIUART_H4
help
HCIATH3K (HCI Atheros AR300x) is a serial protocol for
communication between host and Atheros AR300x Bluetooth devices.
Say Y here to compile support for Three-wire UART protocol.
+config BT_HCIUART_INTEL
+ bool "Intel protocol support"
+ depends on BT_HCIUART
+ select BT_INTEL
+ help
+ The Intel protocol support enables Bluetooth HCI over serial
+ port interface for Intel Bluetooth controllers.
+
+ Say Y here to compile support for Intel protocol.
+
+config BT_HCIUART_BCM
+ bool "Broadcom protocol support"
+ depends on BT_HCIUART
+ select BT_HCIUART_H4
+ select BT_BCM
+ help
+ The Broadcom protocol support enables Bluetooth HCI over serial
+ port interface for Broadcom Bluetooth controllers.
+
+ Say Y here to compile support for Broadcom protocol.
+
config BT_HCIBCM203X
tristate "HCI BCM203x USB driver"
depends on USB
obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
+obj-$(CONFIG_BT_INTEL) += btintel.o
obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
obj-$(CONFIG_BT_WILINK) += btwilink.o
+obj-$(CONFIG_BT_BCM) += btbcm.o
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o
hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o
hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o
+hci_uart-$(CONFIG_BT_HCIUART_INTEL) += hci_intel.o
+hci_uart-$(CONFIG_BT_HCIUART_BCM) += hci_bcm.o
hci_uart-objs := $(hci_uart-y)
ccflags-y += -D__CHECK_ENDIAN__
--- /dev/null
+/*
+ *
+ * Bluetooth support for Broadcom devices
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btbcm.h"
+
+#define VERSION "0.1"
+
+#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
+
+int btbcm_check_bdaddr(struct hci_dev *hdev)
+{
+ struct hci_rp_read_bd_addr *bda;
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ int err = PTR_ERR(skb);
+ BT_ERR("%s: BCM: Reading device address failed (%d)",
+ hdev->name, err);
+ return err;
+ }
+
+ if (skb->len != sizeof(*bda)) {
+ BT_ERR("%s: BCM: Device address length mismatch", hdev->name);
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ bda = (struct hci_rp_read_bd_addr *)skb->data;
+ if (bda->status) {
+ BT_ERR("%s: BCM: Device address result failed (%02x)",
+ hdev->name, bda->status);
+ kfree_skb(skb);
+ return -bt_to_errno(bda->status);
+ }
+
+ /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
+ * with no configured address.
+ */
+ if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) {
+ BT_INFO("%s: BCM: Using default device address (%pMR)",
+ hdev->name, &bda->bdaddr);
+ set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+ }
+
+ kfree_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_check_bdaddr);
+
+int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ struct sk_buff *skb;
+ int err;
+
+ skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ BT_ERR("%s: BCM: Change address command failed (%d)",
+ hdev->name, err);
+ return err;
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_set_bdaddr);
+
+static int btbcm_reset(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ int err = PTR_ERR(skb);
+ BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err);
+ return err;
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: BCM: Reading local version info failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return skb;
+ }
+
+ if (skb->len != sizeof(struct hci_rp_read_local_version)) {
+ BT_ERR("%s: BCM: Local version length mismatch", hdev->name);
+ kfree_skb(skb);
+ return ERR_PTR(-EIO);
+ }
+
+ return skb;
+}
+
+static struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: BCM: Read verbose config info failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return skb;
+ }
+
+ if (skb->len != 7) {
+ BT_ERR("%s: BCM: Verbose config length mismatch", hdev->name);
+ kfree_skb(skb);
+ return ERR_PTR(-EIO);
+ }
+
+ return skb;
+}
+
+static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: BCM: Read USB product info failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return skb;
+ }
+
+ if (skb->len != 5) {
+ BT_ERR("%s: BCM: USB product length mismatch", hdev->name);
+ kfree_skb(skb);
+ return ERR_PTR(-EIO);
+ }
+
+ return skb;
+}
+
+static const struct {
+ u16 subver;
+ const char *name;
+} bcm_uart_subver_table[] = {
+ { 0x410e, "BCM43341B0" }, /* 002.001.014 */
+ { }
+};
+
+static const struct {
+ u16 subver;
+ const char *name;
+} bcm_usb_subver_table[] = {
+ { 0x210b, "BCM43142A0" }, /* 001.001.011 */
+ { 0x2112, "BCM4314A0" }, /* 001.001.018 */
+ { 0x2118, "BCM20702A0" }, /* 001.001.024 */
+ { 0x2126, "BCM4335A0" }, /* 001.001.038 */
+ { 0x220e, "BCM20702A1" }, /* 001.002.014 */
+ { 0x230f, "BCM4354A2" }, /* 001.003.015 */
+ { 0x4106, "BCM4335B0" }, /* 002.001.006 */
+ { 0x410e, "BCM20702B0" }, /* 002.001.014 */
+ { 0x6109, "BCM4335C0" }, /* 003.001.009 */
+ { 0x610c, "BCM4354" }, /* 003.001.012 */
+ { }
+};
+
+int btbcm_setup_patchram(struct hci_dev *hdev)
+{
+ const struct hci_command_hdr *cmd;
+ const struct firmware *fw;
+ const u8 *fw_ptr;
+ size_t fw_size;
+ char fw_name[64];
+ u16 opcode, subver, rev, pid, vid;
+ const char *hw_name = NULL;
+ struct sk_buff *skb;
+ struct hci_rp_read_local_version *ver;
+ int i, err;
+
+ /* Reset */
+ err = btbcm_reset(hdev);
+ if (err)
+ return err;
+
+ /* Read Local Version Info */
+ skb = btbcm_read_local_version(hdev);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ ver = (struct hci_rp_read_local_version *)skb->data;
+ rev = le16_to_cpu(ver->hci_rev);
+ subver = le16_to_cpu(ver->lmp_subver);
+ kfree_skb(skb);
+
+ /* Read Verbose Config Version Info */
+ skb = btbcm_read_verbose_config(hdev);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
+ kfree_skb(skb);
+
+ switch ((rev & 0xf000) >> 12) {
+ case 0:
+ for (i = 0; bcm_uart_subver_table[i].name; i++) {
+ if (subver == bcm_uart_subver_table[i].subver) {
+ hw_name = bcm_uart_subver_table[i].name;
+ break;
+ }
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd",
+ hw_name ? : "BCM");
+ break;
+ case 1:
+ case 2:
+ /* Read USB Product Info */
+ skb = btbcm_read_usb_product(hdev);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ vid = get_unaligned_le16(skb->data + 1);
+ pid = get_unaligned_le16(skb->data + 3);
+ kfree_skb(skb);
+
+ for (i = 0; bcm_usb_subver_table[i].name; i++) {
+ if (subver == bcm_usb_subver_table[i].subver) {
+ hw_name = bcm_usb_subver_table[i].name;
+ break;
+ }
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
+ hw_name ? : "BCM", vid, pid);
+ break;
+ default:
+ return 0;
+ }
+
+ BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
+ hw_name ? : "BCM", (subver & 0x7000) >> 13,
+ (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
+
+ err = request_firmware(&fw, fw_name, &hdev->dev);
+ if (err < 0) {
+ BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
+ return 0;
+ }
+
+ /* Start Download */
+ skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ BT_ERR("%s: BCM: Download Minidrv command failed (%d)",
+ hdev->name, err);
+ goto reset;
+ }
+ kfree_skb(skb);
+
+ /* 50 msec delay after Download Minidrv completes */
+ msleep(50);
+
+ fw_ptr = fw->data;
+ fw_size = fw->size;
+
+ while (fw_size >= sizeof(*cmd)) {
+ const u8 *cmd_param;
+
+ 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);
+ err = -EINVAL;
+ goto reset;
+ }
+
+ cmd_param = fw_ptr;
+ fw_ptr += cmd->plen;
+ fw_size -= cmd->plen;
+
+ opcode = le16_to_cpu(cmd->opcode);
+
+ skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ BT_ERR("%s: BCM: patch command %04x failed (%d)",
+ hdev->name, opcode, err);
+ goto reset;
+ }
+ kfree_skb(skb);
+ }
+
+ /* 250 msec delay after Launch Ram completes */
+ msleep(250);
+
+reset:
+ /* Reset */
+ err = btbcm_reset(hdev);
+ if (err)
+ goto done;
+
+ /* Read Local Version Info */
+ skb = btbcm_read_local_version(hdev);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ goto done;
+ }
+
+ ver = (struct hci_rp_read_local_version *)skb->data;
+ rev = le16_to_cpu(ver->hci_rev);
+ subver = le16_to_cpu(ver->lmp_subver);
+ kfree_skb(skb);
+
+ BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
+ hw_name ? : "BCM", (subver & 0x7000) >> 13,
+ (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
+
+ btbcm_check_bdaddr(hdev);
+
+ set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+
+done:
+ release_firmware(fw);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
+
+int btbcm_setup_apple(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+
+ /* Read Verbose Config Version Info */
+ skb = btbcm_read_verbose_config(hdev);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1],
+ get_unaligned_le16(skb->data + 5));
+ kfree_skb(skb);
+
+ set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btbcm_setup_apple);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ *
+ * Bluetooth support for Broadcom devices
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if IS_ENABLED(CONFIG_BT_BCM)
+
+int btbcm_check_bdaddr(struct hci_dev *hdev);
+int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+
+int btbcm_setup_patchram(struct hci_dev *hdev);
+int btbcm_setup_apple(struct hci_dev *hdev);
+
+#else
+
+static inline int btbcm_check_bdaddr(struct hci_dev *hdev)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btbcm_setup_patchram(struct hci_dev *hdev)
+{
+ return 0;
+}
+
+static inline int btbcm_setup_apple(struct hci_dev *hdev)
+{
+ return 0;
+}
+
+#endif
--- /dev/null
+/*
+ *
+ * Bluetooth support for Intel devices
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btintel.h"
+
+#define VERSION "0.1"
+
+#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
+
+int btintel_check_bdaddr(struct hci_dev *hdev)
+{
+ struct hci_rp_read_bd_addr *bda;
+ struct sk_buff *skb;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ int err = PTR_ERR(skb);
+ BT_ERR("%s: Reading Intel device address failed (%d)",
+ hdev->name, err);
+ return err;
+ }
+
+ if (skb->len != sizeof(*bda)) {
+ BT_ERR("%s: Intel device address length mismatch", hdev->name);
+ kfree_skb(skb);
+ return -EIO;
+ }
+
+ bda = (struct hci_rp_read_bd_addr *)skb->data;
+ if (bda->status) {
+ BT_ERR("%s: Intel device address result failed (%02x)",
+ hdev->name, bda->status);
+ kfree_skb(skb);
+ return -bt_to_errno(bda->status);
+ }
+
+ /* For some Intel based controllers, the default Bluetooth device
+ * address 00:03:19:9E:8B:00 can be found. These controllers are
+ * fully operational, but have the danger of duplicate addresses
+ * and that in turn can cause problems with Bluetooth operation.
+ */
+ if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) {
+ BT_ERR("%s: Found Intel default device address (%pMR)",
+ hdev->name, &bda->bdaddr);
+ set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+ }
+
+ kfree_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_check_bdaddr);
+
+int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ struct sk_buff *skb;
+ int err;
+
+ skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ BT_ERR("%s: Changing Intel device address failed (%d)",
+ hdev->name, err);
+ return err;
+ }
+ kfree_skb(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btintel_set_bdaddr);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ *
+ * Bluetooth support for Intel devices
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+struct intel_version {
+ u8 status;
+ u8 hw_platform;
+ u8 hw_variant;
+ u8 hw_revision;
+ u8 fw_variant;
+ u8 fw_revision;
+ u8 fw_build_num;
+ u8 fw_build_ww;
+ u8 fw_build_yy;
+ u8 fw_patch_num;
+} __packed;
+
+struct intel_boot_params {
+ __u8 status;
+ __u8 otp_format;
+ __u8 otp_content;
+ __u8 otp_patch;
+ __le16 dev_revid;
+ __u8 secure_boot;
+ __u8 key_from_hdr;
+ __u8 key_type;
+ __u8 otp_lock;
+ __u8 api_lock;
+ __u8 debug_lock;
+ bdaddr_t otp_bdaddr;
+ __u8 min_fw_build_nn;
+ __u8 min_fw_build_cw;
+ __u8 min_fw_build_yy;
+ __u8 limited_cce;
+ __u8 unlocked_state;
+} __packed;
+
+struct intel_bootup {
+ __u8 zero;
+ __u8 num_cmds;
+ __u8 source;
+ __u8 reset_type;
+ __u8 reset_reason;
+ __u8 ddc_status;
+} __packed;
+
+struct intel_secure_send_result {
+ __u8 result;
+ __le16 opcode;
+ __u8 status;
+} __packed;
+
+#if IS_ENABLED(CONFIG_BT_INTEL)
+
+int btintel_check_bdaddr(struct hci_dev *hdev);
+int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+
+#else
+
+static inline int btintel_check_bdaddr(struct hci_dev *hdev)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/firmware.h>
-#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#define VERSION "0.7"
+#include "btintel.h"
+#include "btbcm.h"
+
+#define VERSION "0.8"
static bool disable_scofix;
static bool force_scofix;
{ USB_DEVICE(0x0c10, 0x0000) },
/* Broadcom BCM20702A0 */
- { USB_DEVICE(0x0489, 0xe042) },
- { USB_DEVICE(0x04ca, 0x2003) },
- { USB_DEVICE(0x0b05, 0x17b5) },
- { USB_DEVICE(0x0b05, 0x17cb) },
{ USB_DEVICE(0x413c, 0x8197) },
- { USB_DEVICE(0x13d3, 0x3404),
- .driver_info = BTUSB_BCM_PATCHRAM },
/* Broadcom BCM20702B0 (Dynex/Insignia) */
{ USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM },
.driver_info = BTUSB_BCM_PATCHRAM },
/* Belkin F8065bf - Broadcom based */
- { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },
/* IMC Networks - Broadcom based */
- { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },
/* Intel Bluetooth USB Bootloader (RAM module) */
{ USB_DEVICE(0x8087, 0x0a5a),
return ret;
}
-struct intel_version {
- u8 status;
- u8 hw_platform;
- u8 hw_variant;
- u8 hw_revision;
- u8 fw_variant;
- u8 fw_revision;
- u8 fw_build_num;
- u8 fw_build_ww;
- u8 fw_build_yy;
- u8 fw_patch_num;
-} __packed;
-
-struct intel_boot_params {
- __u8 status;
- __u8 otp_format;
- __u8 otp_content;
- __u8 otp_patch;
- __le16 dev_revid;
- __u8 secure_boot;
- __u8 key_from_hdr;
- __u8 key_type;
- __u8 otp_lock;
- __u8 api_lock;
- __u8 debug_lock;
- bdaddr_t otp_bdaddr;
- __u8 min_fw_build_nn;
- __u8 min_fw_build_cw;
- __u8 min_fw_build_yy;
- __u8 limited_cce;
- __u8 unlocked_state;
-} __packed;
-
static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
struct intel_version *ver)
{
return 0;
}
-#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
-
-static int btusb_check_bdaddr_intel(struct hci_dev *hdev)
-{
- struct sk_buff *skb;
- struct hci_rp_read_bd_addr *rp;
-
- skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- BT_ERR("%s reading Intel device address failed (%ld)",
- hdev->name, PTR_ERR(skb));
- return PTR_ERR(skb);
- }
-
- if (skb->len != sizeof(*rp)) {
- BT_ERR("%s Intel device address length mismatch", hdev->name);
- kfree_skb(skb);
- return -EIO;
- }
-
- 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);
- kfree_skb(skb);
- return -bt_to_errno(rp->status);
- }
-
- /* For some Intel based controllers, the default Bluetooth device
- * address 00:03:19:9E:8B:00 can be found. These controllers are
- * fully operational, but have the danger of duplicate addresses
- * and that in turn can cause problems with Bluetooth operation.
- */
- if (!bacmp(&rp->bdaddr, BDADDR_INTEL)) {
- BT_ERR("%s found Intel default device address (%pMR)",
- hdev->name, &rp->bdaddr);
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
- }
-
- kfree_skb(skb);
-
- return 0;
-}
-
static int btusb_setup_intel(struct hci_dev *hdev)
{
struct sk_buff *skb;
BT_INFO("%s: Intel device is already patched. patch num: %02x",
hdev->name, ver->fw_patch_num);
kfree_skb(skb);
- btusb_check_bdaddr_intel(hdev);
+ btintel_check_bdaddr(hdev);
return 0;
}
fw = btusb_setup_intel_get_fw(hdev, ver);
if (!fw) {
kfree_skb(skb);
- btusb_check_bdaddr_intel(hdev);
+ btintel_check_bdaddr(hdev);
return 0;
}
fw_ptr = fw->data;
BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
hdev->name);
- btusb_check_bdaddr_intel(hdev);
+ btintel_check_bdaddr(hdev);
return 0;
exit_mfg_disable:
BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
- btusb_check_bdaddr_intel(hdev);
+ btintel_check_bdaddr(hdev);
return 0;
exit_mfg_deactivate:
BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
hdev->name);
- btusb_check_bdaddr_intel(hdev);
+ btintel_check_bdaddr(hdev);
return 0;
}
return btusb_recv_bulk(data, buffer, count);
}
+static void btusb_intel_bootup(struct btusb_data *data, const void *ptr,
+ unsigned int len)
+{
+ const struct intel_bootup *evt = ptr;
+
+ if (len != sizeof(*evt))
+ return;
+
+ if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) {
+ smp_mb__after_atomic();
+ wake_up_bit(&data->flags, BTUSB_BOOTING);
+ }
+}
+
+static void btusb_intel_secure_send_result(struct btusb_data *data,
+ const void *ptr, unsigned int len)
+{
+ const struct intel_secure_send_result *evt = ptr;
+
+ if (len != sizeof(*evt))
+ return;
+
+ if (evt->result)
+ set_bit(BTUSB_FIRMWARE_FAILED, &data->flags);
+
+ if (test_and_clear_bit(BTUSB_DOWNLOADING, &data->flags) &&
+ test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) {
+ smp_mb__after_atomic();
+ wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
+ }
+}
+
static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
struct hci_event_hdr *hdr = (void *)skb->data;
- /* When the firmware loading completes the device sends
- * out a vendor specific event indicating the result of
- * the firmware loading.
- */
- if (skb->len == 7 && hdr->evt == 0xff && hdr->plen == 0x05 &&
- skb->data[2] == 0x06) {
- if (skb->data[3] != 0x00)
- test_bit(BTUSB_FIRMWARE_FAILED, &data->flags);
-
- if (test_and_clear_bit(BTUSB_DOWNLOADING,
- &data->flags) &&
- test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) {
- smp_mb__after_atomic();
- wake_up_bit(&data->flags, BTUSB_DOWNLOADING);
- }
- }
-
- /* When switching to the operational firmware the device
- * sends a vendor specific event indicating that the bootup
- * completed.
- */
- if (skb->len == 9 && hdr->evt == 0xff && hdr->plen == 0x07 &&
- skb->data[2] == 0x02) {
- if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) {
- smp_mb__after_atomic();
- wake_up_bit(&data->flags, BTUSB_BOOTING);
+ if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
+ hdr->plen > 0) {
+ const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
+ unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;
+
+ switch (skb->data[2]) {
+ case 0x02:
+ /* When switching to the operational firmware
+ * the device sends a vendor specific event
+ * indicating that the bootup completed.
+ */
+ btusb_intel_bootup(data, ptr, len);
+ break;
+ case 0x06:
+ /* When the firmware loading completes the
+ * device sends out a vendor specific event
+ * indicating the result of the firmware
+ * loading.
+ */
+ btusb_intel_secure_send_result(data, ptr, len);
+ break;
}
}
}
if (ver->fw_variant == 0x23) {
kfree_skb(skb);
clear_bit(BTUSB_BOOTLOADER, &data->flags);
- btusb_check_bdaddr_intel(hdev);
+ btintel_check_bdaddr(hdev);
return 0;
}
kfree_skb(skb);
}
-static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
-{
- struct sk_buff *skb;
- long ret;
-
- skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: changing Intel device address failed (%ld)",
- hdev->name, ret);
- return ret;
- }
- kfree_skb(skb);
-
- return 0;
-}
-
static int btusb_shutdown_intel(struct hci_dev *hdev)
{
struct sk_buff *skb;
return 0;
}
-static const struct {
- u16 subver;
- const char *name;
-} bcm_subver_table[] = {
- { 0x210b, "BCM43142A0" }, /* 001.001.011 */
- { 0x2112, "BCM4314A0" }, /* 001.001.018 */
- { 0x2118, "BCM20702A0" }, /* 001.001.024 */
- { 0x2126, "BCM4335A0" }, /* 001.001.038 */
- { 0x220e, "BCM20702A1" }, /* 001.002.014 */
- { 0x230f, "BCM4354A2" }, /* 001.003.015 */
- { 0x4106, "BCM4335B0" }, /* 002.001.006 */
- { 0x410e, "BCM20702B0" }, /* 002.001.014 */
- { 0x6109, "BCM4335C0" }, /* 003.001.009 */
- { 0x610c, "BCM4354" }, /* 003.001.012 */
- { }
-};
-
-#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
-
-static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
-{
- struct btusb_data *data = hci_get_drvdata(hdev);
- struct usb_device *udev = data->udev;
- char fw_name[64];
- const struct firmware *fw;
- const u8 *fw_ptr;
- size_t fw_size;
- const struct hci_command_hdr *cmd;
- const u8 *cmd_param;
- u16 opcode, subver, rev;
- const char *hw_name = NULL;
- struct sk_buff *skb;
- struct hci_rp_read_local_version *ver;
- struct hci_rp_read_bd_addr *bda;
- long ret;
- int i;
-
- /* Reset */
- skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
- return ret;
- }
- kfree_skb(skb);
-
- /* Read Local Version Info */
- skb = btusb_read_local_version(hdev);
- if (IS_ERR(skb))
- return PTR_ERR(skb);
-
- ver = (struct hci_rp_read_local_version *)skb->data;
- rev = le16_to_cpu(ver->hci_rev);
- subver = le16_to_cpu(ver->lmp_subver);
- kfree_skb(skb);
-
- /* Read Verbose Config Version Info */
- skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: BCM: Read Verbose Version failed (%ld)",
- hdev->name, ret);
- return ret;
- }
-
- if (skb->len != 7) {
- BT_ERR("%s: BCM: Read Verbose Version event length mismatch",
- hdev->name);
- kfree_skb(skb);
- return -EIO;
- }
-
- BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
- kfree_skb(skb);
-
- for (i = 0; bcm_subver_table[i].name; i++) {
- if (subver == bcm_subver_table[i].subver) {
- hw_name = bcm_subver_table[i].name;
- break;
- }
- }
-
- BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
- hw_name ? : "BCM", (subver & 0x7000) >> 13,
- (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
-
- snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
- hw_name ? : "BCM",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct));
-
- ret = request_firmware(&fw, fw_name, &hdev->dev);
- if (ret < 0) {
- BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
- return 0;
- }
-
- /* Start Download */
- skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: BCM: Download Minidrv command failed (%ld)",
- hdev->name, ret);
- goto reset_fw;
- }
- kfree_skb(skb);
-
- /* 50 msec delay after Download Minidrv completes */
- msleep(50);
-
- fw_ptr = fw->data;
- fw_size = fw->size;
-
- while (fw_size >= sizeof(*cmd)) {
- 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);
- ret = -EINVAL;
- goto reset_fw;
- }
-
- cmd_param = fw_ptr;
- fw_ptr += cmd->plen;
- fw_size -= cmd->plen;
-
- opcode = le16_to_cpu(cmd->opcode);
-
- skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: BCM: patch command %04x failed (%ld)",
- hdev->name, opcode, ret);
- goto reset_fw;
- }
- kfree_skb(skb);
- }
-
- /* 250 msec delay after Launch Ram completes */
- msleep(250);
-
-reset_fw:
- /* Reset */
- skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret);
- goto done;
- }
- kfree_skb(skb);
-
- /* Read Local Version Info */
- skb = btusb_read_local_version(hdev);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- goto done;
- }
-
- ver = (struct hci_rp_read_local_version *)skb->data;
- rev = le16_to_cpu(ver->hci_rev);
- subver = le16_to_cpu(ver->lmp_subver);
- kfree_skb(skb);
-
- BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
- hw_name ? : "BCM", (subver & 0x7000) >> 13,
- (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
-
- /* Read BD Address */
- skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: HCI_OP_READ_BD_ADDR failed (%ld)",
- hdev->name, ret);
- goto done;
- }
-
- if (skb->len != sizeof(*bda)) {
- BT_ERR("%s: HCI_OP_READ_BD_ADDR event length mismatch",
- hdev->name);
- kfree_skb(skb);
- ret = -EIO;
- goto done;
- }
-
- 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);
- kfree_skb(skb);
- ret = -bt_to_errno(bda->status);
- goto done;
- }
-
- /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
- * with no configured address.
- */
- if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) {
- BT_INFO("%s: BCM: using default device address (%pMR)",
- hdev->name, &bda->bdaddr);
- set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
- }
-
- kfree_skb(skb);
-
-done:
- release_firmware(fw);
-
- return ret;
-}
-
-static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
-{
- struct sk_buff *skb;
- long ret;
-
- skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- ret = PTR_ERR(skb);
- BT_ERR("%s: BCM: Change address command failed (%ld)",
- hdev->name, ret);
- return ret;
- }
- kfree_skb(skb);
-
- return 0;
-}
-
-static int btusb_setup_bcm_apple(struct hci_dev *hdev)
-{
- struct sk_buff *skb;
- int err;
-
- /* Read Verbose Config Version Info */
- skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: BCM: Read Verbose Version failed (%d)",
- hdev->name, err);
- return err;
- }
-
- if (skb->len != 7) {
- BT_ERR("%s: BCM: Read Verbose Version event length mismatch",
- hdev->name);
- kfree_skb(skb);
- return -EIO;
- }
-
- BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1],
- get_unaligned_le16(skb->data + 5));
- kfree_skb(skb);
-
- return 0;
-}
-
static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
const bdaddr_t *bdaddr)
{
if (id->driver_info & BTUSB_BCM92035)
hdev->setup = btusb_setup_bcm92035;
+#ifdef CONFIG_BT_HCIBTUSB_BCM
if (id->driver_info & BTUSB_BCM_PATCHRAM) {
- hdev->setup = btusb_setup_bcm_patchram;
- hdev->set_bdaddr = btusb_set_bdaddr_bcm;
- set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+ hdev->setup = btbcm_setup_patchram;
+ hdev->set_bdaddr = btbcm_set_bdaddr;
}
- if (id->driver_info & BTUSB_BCM_APPLE) {
- hdev->setup = btusb_setup_bcm_apple;
- set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
- }
+ if (id->driver_info & BTUSB_BCM_APPLE)
+ hdev->setup = btbcm_setup_apple;
+#endif
if (id->driver_info & BTUSB_INTEL) {
hdev->setup = btusb_setup_intel;
hdev->shutdown = btusb_shutdown_intel;
- hdev->set_bdaddr = btusb_set_bdaddr_intel;
+ hdev->set_bdaddr = btintel_set_bdaddr;
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
}
hdev->send = btusb_send_frame_intel;
hdev->setup = btusb_setup_intel_new;
hdev->hw_error = btusb_hw_error_intel;
- hdev->set_bdaddr = btusb_set_bdaddr_intel;
+ hdev->set_bdaddr = btintel_set_bdaddr;
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
}
struct hci_uart *hu;
unsigned int cur_sleep;
+ struct sk_buff *rx_skb;
struct sk_buff_head txq;
struct work_struct ctxtsw;
};
skb_queue_purge(&ath->txq);
+ kfree_skb(ath->rx_skb);
+
cancel_work_sync(&ath->ctxtsw);
hu->priv = NULL;
return skb_dequeue(&ath->txq);
}
+static const struct h4_recv_pkt ath_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = hci_recv_frame },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
/* Recv data */
-static int ath_recv(struct hci_uart *hu, void *data, int count)
+static int ath_recv(struct hci_uart *hu, const void *data, int count)
{
- int ret;
+ struct ath_struct *ath = hu->priv;
- ret = hci_recv_stream_fragment(hu->hdev, data, count);
- if (ret < 0) {
- BT_ERR("Frame Reassembly Failed");
- return ret;
+ ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count,
+ ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
+ if (IS_ERR(ath->rx_skb)) {
+ int err = PTR_ERR(ath->rx_skb);
+ BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+ return err;
}
return count;
}
-static struct hci_uart_proto athp = {
- .id = HCI_UART_ATH3K,
- .open = ath_open,
- .close = ath_close,
- .recv = ath_recv,
- .enqueue = ath_enqueue,
- .dequeue = ath_dequeue,
- .flush = ath_flush,
+static const struct hci_uart_proto athp = {
+ .id = HCI_UART_ATH3K,
+ .name = "ATH3K",
+ .open = ath_open,
+ .close = ath_close,
+ .recv = ath_recv,
+ .enqueue = ath_enqueue,
+ .dequeue = ath_dequeue,
+ .flush = ath_flush,
};
int __init ath_init(void)
{
- int err = hci_uart_register_proto(&athp);
-
- if (!err)
- BT_INFO("HCIATH3K protocol initialized");
- else
- BT_ERR("HCIATH3K protocol registration failed");
-
- return err;
+ return hci_uart_register_proto(&athp);
}
int __exit ath_deinit(void)
--- /dev/null
+/*
+ *
+ * Bluetooth HCI UART driver for Broadcom devices
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btbcm.h"
+#include "hci_uart.h"
+
+struct bcm_data {
+ struct sk_buff *rx_skb;
+ struct sk_buff_head txq;
+};
+
+static int bcm_open(struct hci_uart *hu)
+{
+ struct bcm_data *bcm;
+
+ BT_DBG("hu %p", hu);
+
+ bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
+ if (!bcm)
+ return -ENOMEM;
+
+ skb_queue_head_init(&bcm->txq);
+
+ hu->priv = bcm;
+ return 0;
+}
+
+static int bcm_close(struct hci_uart *hu)
+{
+ struct bcm_data *bcm = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&bcm->txq);
+ kfree_skb(bcm->rx_skb);
+ kfree(bcm);
+
+ hu->priv = NULL;
+ return 0;
+}
+
+static int bcm_flush(struct hci_uart *hu)
+{
+ struct bcm_data *bcm = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&bcm->txq);
+
+ return 0;
+}
+
+static int bcm_setup(struct hci_uart *hu)
+{
+ BT_DBG("hu %p", hu);
+
+ hu->hdev->set_bdaddr = btbcm_set_bdaddr;
+
+ return btbcm_setup_patchram(hu->hdev);
+}
+
+static const struct h4_recv_pkt bcm_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = hci_recv_frame },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
+static int bcm_recv(struct hci_uart *hu, const void *data, int count)
+{
+ struct bcm_data *bcm = hu->priv;
+
+ if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+ return -EUNATCH;
+
+ bcm->rx_skb = h4_recv_buf(hu->hdev, bcm->rx_skb, data, count,
+ bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
+ if (IS_ERR(bcm->rx_skb)) {
+ int err = PTR_ERR(bcm->rx_skb);
+ BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+ return err;
+ }
+
+ return count;
+}
+
+static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ struct bcm_data *bcm = hu->priv;
+
+ BT_DBG("hu %p skb %p", hu, skb);
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&bcm->txq, skb);
+
+ return 0;
+}
+
+static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
+{
+ struct bcm_data *bcm = hu->priv;
+
+ return skb_dequeue(&bcm->txq);
+}
+
+static const struct hci_uart_proto bcm_proto = {
+ .id = HCI_UART_BCM,
+ .name = "BCM",
+ .open = bcm_open,
+ .close = bcm_close,
+ .flush = bcm_flush,
+ .setup = bcm_setup,
+ .recv = bcm_recv,
+ .enqueue = bcm_enqueue,
+ .dequeue = bcm_dequeue,
+};
+
+int __init bcm_init(void)
+{
+ return hci_uart_register_proto(&bcm_proto);
+}
+
+int __exit bcm_deinit(void)
+{
+ return hci_uart_unregister_proto(&bcm_proto);
+}
#include "hci_uart.h"
-#define VERSION "0.3"
-
static bool txcrc = 1;
static bool hciextn = 1;
}
/* Recv data */
-static int bcsp_recv(struct hci_uart *hu, void *data, int count)
+static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
{
struct bcsp_struct *bcsp = hu->priv;
- unsigned char *ptr;
+ const unsigned char *ptr;
BT_DBG("hu %p count %d rx_state %d rx_count %ld",
hu, count, bcsp->rx_state, bcsp->rx_count);
return 0;
}
-static struct hci_uart_proto bcsp = {
+static const struct hci_uart_proto bcsp = {
.id = HCI_UART_BCSP,
+ .name = "BCSP",
.open = bcsp_open,
.close = bcsp_close,
.enqueue = bcsp_enqueue,
int __init bcsp_init(void)
{
- int err = hci_uart_register_proto(&bcsp);
-
- if (!err)
- BT_INFO("HCI BCSP protocol initialized");
- else
- BT_ERR("HCI BCSP protocol registration failed");
-
- return err;
+ return hci_uart_register_proto(&bcsp);
}
int __exit bcsp_deinit(void)
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
+#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
-#define VERSION "1.2"
-
struct h4_struct {
- unsigned long rx_state;
- unsigned long rx_count;
struct sk_buff *rx_skb;
struct sk_buff_head txq;
};
return 0;
}
+static const struct h4_recv_pkt h4_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = hci_recv_frame },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
/* Recv data */
-static int h4_recv(struct hci_uart *hu, void *data, int count)
+static int h4_recv(struct hci_uart *hu, const void *data, int count)
{
- int ret;
+ struct h4_struct *h4 = hu->priv;
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- ret = hci_recv_stream_fragment(hu->hdev, data, count);
- if (ret < 0) {
- BT_ERR("Frame Reassembly Failed");
- return ret;
+ h4->rx_skb = h4_recv_buf(hu->hdev, h4->rx_skb, data, count,
+ h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts));
+ if (IS_ERR(h4->rx_skb)) {
+ int err = PTR_ERR(h4->rx_skb);
+ BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+ return err;
}
return count;
return skb_dequeue(&h4->txq);
}
-static struct hci_uart_proto h4p = {
+static const struct hci_uart_proto h4p = {
.id = HCI_UART_H4,
+ .name = "H4",
.open = h4_open,
.close = h4_close,
.recv = h4_recv,
int __init h4_init(void)
{
- int err = hci_uart_register_proto(&h4p);
-
- if (!err)
- BT_INFO("HCI H4 protocol initialized");
- else
- BT_ERR("HCI H4 protocol registration failed");
-
- return err;
+ return hci_uart_register_proto(&h4p);
}
int __exit h4_deinit(void)
{
return hci_uart_unregister_proto(&h4p);
}
+
+struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+ const unsigned char *buffer, int count,
+ const struct h4_recv_pkt *pkts, int pkts_count)
+{
+ while (count) {
+ int i, len;
+
+ if (!skb) {
+ for (i = 0; i < pkts_count; i++) {
+ if (buffer[0] != (&pkts[i])->type)
+ continue;
+
+ skb = bt_skb_alloc((&pkts[i])->maxlen,
+ GFP_ATOMIC);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ bt_cb(skb)->pkt_type = (&pkts[i])->type;
+ bt_cb(skb)->expect = (&pkts[i])->hlen;
+ break;
+ }
+
+ /* Check for invalid packet type */
+ if (!skb)
+ return ERR_PTR(-EILSEQ);
+
+ count -= 1;
+ buffer += 1;
+ }
+
+ len = min_t(uint, bt_cb(skb)->expect - skb->len, count);
+ memcpy(skb_put(skb, len), buffer, len);
+
+ count -= len;
+ buffer += len;
+
+ /* Check for partial packet */
+ if (skb->len < bt_cb(skb)->expect)
+ continue;
+
+ for (i = 0; i < pkts_count; i++) {
+ if (bt_cb(skb)->pkt_type == (&pkts[i])->type)
+ break;
+ }
+
+ if (i >= pkts_count) {
+ kfree_skb(skb);
+ return ERR_PTR(-EILSEQ);
+ }
+
+ if (skb->len == (&pkts[i])->hlen) {
+ u16 dlen;
+
+ switch ((&pkts[i])->lsize) {
+ case 0:
+ /* No variable data length */
+ (&pkts[i])->recv(hdev, skb);
+ skb = NULL;
+ break;
+ case 1:
+ /* Single octet variable length */
+ dlen = skb->data[(&pkts[i])->loff];
+ bt_cb(skb)->expect += dlen;
+
+ if (skb_tailroom(skb) < dlen) {
+ kfree_skb(skb);
+ return ERR_PTR(-EMSGSIZE);
+ }
+ break;
+ case 2:
+ /* Double octet variable length */
+ dlen = get_unaligned_le16(skb->data +
+ (&pkts[i])->loff);
+ bt_cb(skb)->expect += dlen;
+
+ if (skb_tailroom(skb) < dlen) {
+ kfree_skb(skb);
+ return ERR_PTR(-EMSGSIZE);
+ }
+ break;
+ default:
+ /* Unsupported variable length */
+ kfree_skb(skb);
+ return ERR_PTR(-EILSEQ);
+ }
+ } else {
+ /* Complete frame */
+ (&pkts[i])->recv(hdev, skb);
+ skb = NULL;
+ }
+ }
+
+ return skb;
+}
clear_bit(H5_RX_ESC, &h5->flags);
}
-static int h5_recv(struct hci_uart *hu, void *data, int count)
+static int h5_recv(struct hci_uart *hu, const void *data, int count)
{
struct h5 *h5 = hu->priv;
- unsigned char *ptr = data;
+ const unsigned char *ptr = data;
BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending,
count);
return 0;
}
-static struct hci_uart_proto h5p = {
+static const struct hci_uart_proto h5p = {
.id = HCI_UART_3WIRE,
+ .name = "Three-wire (H5)",
.open = h5_open,
.close = h5_close,
.recv = h5_recv,
int __init h5_init(void)
{
- int err = hci_uart_register_proto(&h5p);
-
- if (!err)
- BT_INFO("HCI Three-wire UART (H5) protocol initialized");
- else
- BT_ERR("HCI Three-wire UART (H5) protocol init failed");
-
- return err;
+ return hci_uart_register_proto(&h5p);
}
int __exit h5_deinit(void)
--- /dev/null
+/*
+ *
+ * Bluetooth HCI UART driver for Intel devices
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include "btintel.h"
+#include "btbcm.h"
#include "hci_uart.h"
-#define VERSION "2.2"
+#define VERSION "2.3"
-static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
+static const struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
-int hci_uart_register_proto(struct hci_uart_proto *p)
+int hci_uart_register_proto(const struct hci_uart_proto *p)
{
if (p->id >= HCI_UART_MAX_PROTO)
return -EINVAL;
hup[p->id] = p;
+ BT_INFO("HCI UART protocol %s registered", p->name);
+
return 0;
}
-int hci_uart_unregister_proto(struct hci_uart_proto *p)
+int hci_uart_unregister_proto(const struct hci_uart_proto *p)
{
if (p->id >= HCI_UART_MAX_PROTO)
return -EINVAL;
return 0;
}
-static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
+static const struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
{
if (id >= HCI_UART_MAX_PROTO)
return NULL;
static int hci_uart_setup(struct hci_dev *hdev)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct hci_rp_read_local_version *ver;
+ struct sk_buff *skb;
if (hu->proto->setup)
return hu->proto->setup(hu);
+ if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags))
+ return 0;
+
+ skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+ HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ BT_ERR("%s: Reading local version information failed (%ld)",
+ hdev->name, PTR_ERR(skb));
+ return 0;
+ }
+
+ if (skb->len != sizeof(*ver)) {
+ BT_ERR("%s: Event length mismatch for version information",
+ hdev->name);
+ goto done;
+ }
+
+ ver = (struct hci_rp_read_local_version *)skb->data;
+
+ switch (le16_to_cpu(ver->manufacturer)) {
+#ifdef CONFIG_BT_HCIUART_INTEL
+ case 2:
+ hdev->set_bdaddr = btintel_set_bdaddr;
+ btintel_check_bdaddr(hdev);
+ break;
+#endif
+#ifdef CONFIG_BT_HCIUART_BCM
+ case 15:
+ hdev->set_bdaddr = btbcm_set_bdaddr;
+ btbcm_check_bdaddr(hdev);
+ break;
+#endif
+ }
+
+done:
+ kfree_skb(skb);
return 0;
}
*/
static void hci_uart_tty_close(struct tty_struct *tty)
{
- struct hci_uart *hu = (void *)tty->disc_data;
+ struct hci_uart *hu = tty->disc_data;
struct hci_dev *hdev;
BT_DBG("tty %p", tty);
*/
static void hci_uart_tty_wakeup(struct tty_struct *tty)
{
- struct hci_uart *hu = (void *)tty->disc_data;
+ struct hci_uart *hu = tty->disc_data;
BT_DBG("");
*
* Return Value: None
*/
-static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
+static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
+ char *flags, int count)
{
- struct hci_uart *hu = (void *)tty->disc_data;
+ struct hci_uart *hu = tty->disc_data;
if (!hu || tty != hu->tty)
return;
return;
spin_lock(&hu->rx_lock);
- hu->proto->recv(hu, (void *) data, count);
+ hu->proto->recv(hu, data, count);
if (hu->hdev)
hu->hdev->stat.byte_rx += count;
static int hci_uart_set_proto(struct hci_uart *hu, int id)
{
- struct hci_uart_proto *p;
+ const struct hci_uart_proto *p;
int err;
p = hci_uart_get_proto(id);
BIT(HCI_UART_RESET_ON_INIT) |
BIT(HCI_UART_CREATE_AMP) |
BIT(HCI_UART_INIT_PENDING) |
- BIT(HCI_UART_EXT_CONFIG);
+ BIT(HCI_UART_EXT_CONFIG) |
+ BIT(HCI_UART_VND_DETECT);
- if ((flags & ~valid_flags))
+ if (flags & ~valid_flags)
return -EINVAL;
hu->hdev_flags = flags;
*
* Return Value: Command dependent
*/
-static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
+static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
- struct hci_uart *hu = (void *)tty->disc_data;
+ struct hci_uart *hu = tty->disc_data;
int err = 0;
BT_DBG("");
* We don't provide read/write/poll interface for user space.
*/
static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
- unsigned char __user *buf, size_t nr)
+ unsigned char __user *buf, size_t nr)
{
return 0;
}
static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file,
- const unsigned char *data, size_t count)
+ const unsigned char *data, size_t count)
{
return 0;
}
static unsigned int hci_uart_tty_poll(struct tty_struct *tty,
- struct file *filp, poll_table *wait)
+ struct file *filp, poll_table *wait)
{
return 0;
}
#ifdef CONFIG_BT_HCIUART_3WIRE
h5_init();
#endif
+#ifdef CONFIG_BT_HCIUART_BCM
+ bcm_init();
+#endif
return 0;
}
#ifdef CONFIG_BT_HCIUART_3WIRE
h5_deinit();
#endif
+#ifdef CONFIG_BT_HCIUART_BCM
+ bcm_deinit();
+#endif
/* Release tty registration of line discipline */
err = tty_unregister_ldisc(N_HCI);
}
/* Recv data */
-static int ll_recv(struct hci_uart *hu, void *data, int count)
+static int ll_recv(struct hci_uart *hu, const void *data, int count)
{
struct ll_struct *ll = hu->priv;
- char *ptr;
+ const char *ptr;
struct hci_event_hdr *eh;
struct hci_acl_hdr *ah;
struct hci_sco_hdr *sh;
return skb_dequeue(&ll->txq);
}
-static struct hci_uart_proto llp = {
+static const struct hci_uart_proto llp = {
.id = HCI_UART_LL,
+ .name = "LL",
.open = ll_open,
.close = ll_close,
.recv = ll_recv,
int __init ll_init(void)
{
- int err = hci_uart_register_proto(&llp);
-
- if (!err)
- BT_INFO("HCILL protocol initialized");
- else
- BT_ERR("HCILL protocol registration failed");
-
- return err;
+ return hci_uart_register_proto(&llp);
}
int __exit ll_deinit(void)
#define HCIUARTGETFLAGS _IOR('U', 204, int)
/* UART protocols */
-#define HCI_UART_MAX_PROTO 6
+#define HCI_UART_MAX_PROTO 8
#define HCI_UART_H4 0
#define HCI_UART_BCSP 1
#define HCI_UART_H4DS 3
#define HCI_UART_LL 4
#define HCI_UART_ATH3K 5
+#define HCI_UART_INTEL 6
+#define HCI_UART_BCM 7
#define HCI_UART_RAW_DEVICE 0
#define HCI_UART_RESET_ON_INIT 1
#define HCI_UART_CREATE_AMP 2
#define HCI_UART_INIT_PENDING 3
#define HCI_UART_EXT_CONFIG 4
+#define HCI_UART_VND_DETECT 5
struct hci_uart;
struct hci_uart_proto {
unsigned int id;
+ const char *name;
int (*open)(struct hci_uart *hu);
int (*close)(struct hci_uart *hu);
int (*flush)(struct hci_uart *hu);
- int (*recv)(struct hci_uart *hu, void *data, int len);
- int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
int (*setup)(struct hci_uart *hu);
+ int (*recv)(struct hci_uart *hu, const void *data, int len);
+ int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
struct sk_buff *(*dequeue)(struct hci_uart *hu);
};
struct work_struct init_ready;
struct work_struct write_work;
- struct hci_uart_proto *proto;
+ const struct hci_uart_proto *proto;
void *priv;
struct sk_buff *tx_skb;
#define HCI_UART_SENDING 1
#define HCI_UART_TX_WAKEUP 2
-int hci_uart_register_proto(struct hci_uart_proto *p);
-int hci_uart_unregister_proto(struct hci_uart_proto *p);
+int hci_uart_register_proto(const struct hci_uart_proto *p);
+int hci_uart_unregister_proto(const struct hci_uart_proto *p);
int hci_uart_tx_wakeup(struct hci_uart *hu);
int hci_uart_init_ready(struct hci_uart *hu);
#ifdef CONFIG_BT_HCIUART_H4
int h4_init(void);
int h4_deinit(void);
+
+struct h4_recv_pkt {
+ u8 type; /* Packet type */
+ u8 hlen; /* Header length */
+ u8 loff; /* Data length offset in header */
+ u8 lsize; /* Data length field size */
+ u16 maxlen; /* Max overall packet length */
+ int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
+};
+
+#define H4_RECV_ACL \
+ .type = HCI_ACLDATA_PKT, \
+ .hlen = HCI_ACL_HDR_SIZE, \
+ .loff = 2, \
+ .lsize = 2, \
+ .maxlen = HCI_MAX_FRAME_SIZE \
+
+#define H4_RECV_SCO \
+ .type = HCI_SCODATA_PKT, \
+ .hlen = HCI_SCO_HDR_SIZE, \
+ .loff = 2, \
+ .lsize = 1, \
+ .maxlen = HCI_MAX_SCO_SIZE
+
+#define H4_RECV_EVENT \
+ .type = HCI_EVENT_PKT, \
+ .hlen = HCI_EVENT_HDR_SIZE, \
+ .loff = 1, \
+ .lsize = 1, \
+ .maxlen = HCI_MAX_EVENT_SIZE
+
+struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+ const unsigned char *buffer, int count,
+ const struct h4_recv_pkt *pkts, int pkts_count);
#endif
#ifdef CONFIG_BT_HCIUART_BCSP
int h5_init(void);
int h5_deinit(void);
#endif
+
+#ifdef CONFIG_BT_HCIUART_BCM
+int bcm_init(void);
+int bcm_deinit(void);
+#endif
config SH_TIMER_CMT
bool "Renesas CMT timer driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS
+ depends on HAS_IOMEM
default SYS_SUPPORTS_SH_CMT
help
This enables build of a clocksource and clockevent driver for
config SH_TIMER_MTU2
bool "Renesas MTU2 timer driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS
+ depends on HAS_IOMEM
default SYS_SUPPORTS_SH_MTU2
help
This enables build of a clockevent driver for the Multi-Function
config SH_TIMER_TMU
bool "Renesas TMU timer driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS
+ depends on HAS_IOMEM
default SYS_SUPPORTS_SH_TMU
help
This enables build of a clocksource and clockevent driver for
#include <linux/irq.h>
#include <linux/irqreturn.h>
#include <linux/reset.h>
-#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
.dev_id = &sun5i_clockevent,
};
-static u64 sun5i_timer_sched_read(void)
-{
- return ~readl(timer_base + TIMER_CNTVAL_LO_REG(1));
-}
-
static void __init sun5i_timer_init(struct device_node *node)
{
struct reset_control *rstc;
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
timer_base + TIMER_CTL_REG(1));
- sched_clock_register(sun5i_timer_sched_read, 32, rate);
clocksource_mmio_init(timer_base + TIMER_CNTVAL_LO_REG(1), node->name,
rate, 340, 32, clocksource_mmio_readl_down);
* c->desc is NULL and exit.)
*/
if (c->desc) {
+ bcm2835_dma_desc_free(&c->desc->vd);
c->desc = NULL;
bcm2835_dma_abort(c->chan_base);
kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc));
}
+#define JZ4740_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+
static int jz4740_dma_probe(struct platform_device *pdev)
{
struct jz4740_dmaengine_chan *chan;
dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic;
dd->device_config = jz4740_dma_slave_config;
dd->device_terminate_all = jz4740_dma_terminate_all;
+ dd->src_addr_widths = JZ4740_DMA_BUSWIDTHS;
+ dd->dst_addr_widths = JZ4740_DMA_BUSWIDTHS;
+ dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
dd->dev = &pdev->dev;
INIT_LIST_HEAD(&dd->channels);
*/
if (echan->edesc) {
int cyclic = echan->edesc->cyclic;
+
+ /*
+ * free the running request descriptor
+ * since it is not in any of the vdesc lists
+ */
+ edma_desc_free(&echan->edesc->vdesc);
+
echan->edesc = NULL;
edma_stop(echan->ch_num);
/* Move the cyclic channel back to default queue */
spin_lock_irqsave(&ch->vc.lock, flags);
- if (ch->desc)
+ if (ch->desc) {
+ moxart_dma_desc_free(&ch->desc->vd);
ch->desc = NULL;
+ }
ctrl = readl(ch->base + REG_OFF_CTRL);
ctrl &= ~(APB_DMA_ENABLE | APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN);
* c->desc is NULL and exit.)
*/
if (c->desc) {
+ omap_dma_desc_free(&c->desc->vd);
c->desc = NULL;
/* Avoid stopping the dma twice */
if (!c->paused)
int i = 0;
/*
- * Stop when we see all the items the table claimed to have
- * OR we run off the end of the table (also happens)
+ * Stop when we have seen all the items the table claimed to have
+ * (SMBIOS < 3.0 only) OR we reach an end-of-table marker OR we run
+ * off the end of the table (should never happen but sometimes does
+ * on bogus implementations.)
*/
- while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) {
+ while ((!num || i < num) &&
+ (data - buf + sizeof(struct dmi_header)) <= len) {
const struct dmi_header *dm = (const struct dmi_header *)data;
/*
if (memcmp(buf, "_SM3_", 5) == 0 &&
buf[6] < 32 && dmi_checksum(buf, buf[6])) {
dmi_ver = get_unaligned_be16(buf + 7);
+ dmi_num = 0; /* No longer specified */
dmi_len = get_unaligned_le32(buf + 12);
dmi_base = get_unaligned_le64(buf + 16);
- /*
- * The 64-bit SMBIOS 3.0 entry point no longer has a field
- * containing the number of structures present in the table.
- * Instead, it defines the table size as a maximum size, and
- * relies on the end-of-table structure type (#127) to be used
- * to signal the end of the table.
- * So let's define dmi_num as an upper bound as well: each
- * structure has a 4 byte header, so dmi_len / 4 is an upper
- * bound for the number of structures in the table.
- */
- dmi_num = dmi_len / 4;
-
if (dmi_walk_early(dmi_decode) == 0) {
pr_info("SMBIOS %d.%d present.\n",
dmi_ver >> 8, dmi_ver & 0xFF);
.xlate = irq_domain_xlate_twocell,
};
-static struct of_device_id mpc8xxx_gpio_ids[] __initdata = {
+static struct of_device_id mpc8xxx_gpio_ids[] = {
{ .compatible = "fsl,mpc8349-gpio", },
{ .compatible = "fsl,mpc8572-gpio", },
{ .compatible = "fsl,mpc8610-gpio", },
ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
&priv->dir_reg_offset);
if (ret)
- dev_err(dev, "can't read the dir register offset!\n");
+ dev_dbg(dev, "can't read the dir register offset!\n");
priv->dir_reg_offset <<= 3;
}
if (!handler)
return AE_BAD_PARAMETER;
+ pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ if (pin < 0)
+ return AE_BAD_PARAMETER;
+
desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event");
if (IS_ERR(desc)) {
dev_err(chip->dev, "Failed to request GPIO\n");
struct gpio_desc *desc;
bool found;
+ pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ if (pin < 0) {
+ status = AE_BAD_PARAMETER;
+ goto out;
+ }
+
mutex_lock(&achip->conn_lock);
found = false;
}
EXPORT_SYMBOL(drm_framebuffer_reference);
-static void drm_framebuffer_free_bug(struct kref *kref)
-{
- BUG();
-}
-
-static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
-{
- DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
- kref_put(&fb->refcount, drm_framebuffer_free_bug);
-}
-
/**
* drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
* @fb: fb to unregister
return;
}
/* disconnect the plane from the fb and crtc: */
- __drm_framebuffer_unreference(plane->old_fb);
+ drm_framebuffer_unreference(plane->old_fb);
plane->old_fb = NULL;
plane->fb = NULL;
plane->crtc = NULL;
drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
+ drm_edid_to_eld(connector, edid);
kfree(edid);
return ret;
struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;
count = drm_add_edid_modes(connector, edid);
+ drm_edid_to_eld(connector, edid);
} else
count = (*connector_funcs->get_modes)(connector);
}
unsigned int ovl_height;
unsigned int fb_width;
unsigned int fb_height;
+ unsigned int fb_pitch;
unsigned int bpp;
unsigned int pixel_format;
dma_addr_t dma_addr;
win_data->offset_y = plane->crtc_y;
win_data->ovl_width = plane->crtc_width;
win_data->ovl_height = plane->crtc_height;
+ win_data->fb_pitch = plane->pitch;
win_data->fb_width = plane->fb_width;
win_data->fb_height = plane->fb_height;
win_data->dma_addr = plane->dma_addr[0] + offset;
win_data->bpp = plane->bpp;
win_data->pixel_format = plane->pixel_format;
- win_data->buf_offsize = (plane->fb_width - plane->crtc_width) *
- (plane->bpp >> 3);
+ win_data->buf_offsize =
+ plane->pitch - (plane->crtc_width * (plane->bpp >> 3));
win_data->line_size = plane->crtc_width * (plane->bpp >> 3);
DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
/* buffer end address */
- size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
+ size = win_data->fb_pitch * win_data->ovl_height * (win_data->bpp >> 3);
val = (unsigned long)(win_data->dma_addr + size);
writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
unsigned int fb_x;
unsigned int fb_y;
unsigned int fb_width;
+ unsigned int fb_pitch;
unsigned int fb_height;
unsigned int src_width;
unsigned int src_height;
} else {
luma_addr[0] = win_data->dma_addr;
chroma_addr[0] = win_data->dma_addr
- + (win_data->fb_width * win_data->fb_height);
+ + (win_data->fb_pitch * win_data->fb_height);
}
if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
luma_addr[1] = luma_addr[0] + 0x40;
chroma_addr[1] = chroma_addr[0] + 0x40;
} else {
- luma_addr[1] = luma_addr[0] + win_data->fb_width;
- chroma_addr[1] = chroma_addr[0] + win_data->fb_width;
+ luma_addr[1] = luma_addr[0] + win_data->fb_pitch;
+ chroma_addr[1] = chroma_addr[0] + win_data->fb_pitch;
}
} else {
ctx->interlace = false;
vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
/* setting size of input image */
- vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) |
+ vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_pitch) |
VP_IMG_VSIZE(win_data->fb_height));
/* chroma height has to reduced by 2 to avoid chroma distorions */
- vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) |
+ vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_pitch) |
VP_IMG_VSIZE(win_data->fb_height / 2));
vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
/* converting dma address base and source offset */
dma_addr = win_data->dma_addr
+ (win_data->fb_x * win_data->bpp >> 3)
- + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3);
+ + (win_data->fb_y * win_data->fb_pitch);
src_x_offset = 0;
src_y_offset = 0;
MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
/* setup geometry */
- mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
+ mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
+ win_data->fb_pitch / (win_data->bpp >> 3));
/* setup display size */
if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
win_data->fb_y = plane->fb_y;
win_data->fb_width = plane->fb_width;
win_data->fb_height = plane->fb_height;
+ win_data->fb_pitch = plane->pitch;
win_data->src_width = plane->src_width;
win_data->src_height = plane->src_height;
WARN_ON(i915_verify_lists(ring->dev));
- /* Move any buffers on the active list that are no longer referenced
- * by the ringbuffer to the flushing/inactive lists as appropriate,
- * before we free the context associated with the requests.
+ /* Retire requests first as we use it above for the early return.
+ * If we retire requests last, we may use a later seqno and so clear
+ * the requests lists without clearing the active list, leading to
+ * confusion.
*/
- while (!list_empty(&ring->active_list)) {
- struct drm_i915_gem_object *obj;
-
- obj = list_first_entry(&ring->active_list,
- struct drm_i915_gem_object,
- ring_list);
-
- if (!i915_gem_request_completed(obj->last_read_req, true))
- break;
-
- i915_gem_object_move_to_inactive(obj);
- }
-
-
while (!list_empty(&ring->request_list)) {
struct drm_i915_gem_request *request;
struct intel_ringbuffer *ringbuf;
i915_gem_free_request(request);
}
+ /* Move any buffers on the active list that are no longer referenced
+ * by the ringbuffer to the flushing/inactive lists as appropriate,
+ * before we free the context associated with the requests.
+ */
+ while (!list_empty(&ring->active_list)) {
+ struct drm_i915_gem_object *obj;
+
+ obj = list_first_entry(&ring->active_list,
+ struct drm_i915_gem_object,
+ ring_list);
+
+ if (!i915_gem_request_completed(obj->last_read_req, true))
+ break;
+
+ i915_gem_object_move_to_inactive(obj);
+ }
+
if (unlikely(ring->trace_irq_req &&
i915_gem_request_completed(ring->trace_irq_req, true))) {
ring->irq_put(ring);
goto err;
}
- if (i915_needs_cmd_parser(ring)) {
+ if (i915_needs_cmd_parser(ring) && args->batch_len) {
batch_obj = i915_gem_execbuffer_parse(ring,
&shadow_exec_entry,
eb,
if (!intel_crtc->base.primary->fb)
return;
- if (intel_alloc_plane_obj(intel_crtc, plane_config))
+ if (intel_alloc_plane_obj(intel_crtc, plane_config)) {
+ struct drm_plane *primary = intel_crtc->base.primary;
+
+ primary->state->crtc = &intel_crtc->base;
+ primary->crtc = &intel_crtc->base;
+ update_state_fb(primary);
+
return;
+ }
kfree(intel_crtc->base.primary->fb);
intel_crtc->base.primary->fb = NULL;
continue;
if (i915_gem_obj_ggtt_offset(obj) == plane_config->base) {
+ struct drm_plane *primary = intel_crtc->base.primary;
+
if (obj->tiling_mode != I915_TILING_NONE)
dev_priv->preserve_bios_swizzle = true;
drm_framebuffer_reference(c->primary->fb);
- intel_crtc->base.primary->fb = c->primary->fb;
+ primary->fb = c->primary->fb;
+ primary->state->crtc = &intel_crtc->base;
+ primary->crtc = &intel_crtc->base;
obj->frontbuffer_bits |= INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe);
break;
}
plane_config->size);
crtc->base.primary->fb = fb;
- update_state_fb(crtc->base.primary);
}
static void chv_crtc_clock_get(struct intel_crtc *crtc,
plane_config->size);
crtc->base.primary->fb = fb;
- update_state_fb(crtc->base.primary);
return;
error:
plane_config->size);
crtc->base.primary->fb = fb;
- update_state_fb(crtc->base.primary);
}
static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
drm_modeset_lock_all(dev);
plane = drm_plane_find(dev, set->plane_id);
- if (!plane) {
+ if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY) {
ret = -ENOENT;
goto out_unlock;
}
drm_modeset_lock_all(dev);
plane = drm_plane_find(dev, get->plane_id);
- if (!plane) {
+ if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY) {
ret = -ENOENT;
goto out_unlock;
}
#define VCE_UENC_REG_CLOCK_GATING 0x207c0
#define VCE_SYS_INT_EN 0x21300
# define VCE_SYS_INT_TRAP_INTERRUPT_EN (1 << 3)
+#define VCE_LMI_VCPU_CACHE_40BIT_BAR 0x2145c
#define VCE_LMI_CTRL2 0x21474
#define VCE_LMI_CTRL 0x21498
#define VCE_LMI_VM_CTRL 0x214a0
int new_active_crtc_count;
u32 current_active_crtcs;
int current_active_crtc_count;
+ bool single_display;
struct radeon_dpm_dynamic_state dyn_state;
struct radeon_dpm_fan fan;
u32 tdp_limit;
static bool radeon_read_bios(struct radeon_device *rdev)
{
- uint8_t __iomem *bios;
+ uint8_t __iomem *bios, val1, val2;
size_t size;
rdev->bios = NULL;
return false;
}
- if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) {
+ val1 = readb(&bios[0]);
+ val2 = readb(&bios[1]);
+
+ if (size == 0 || val1 != 0x55 || val2 != 0xaa) {
pci_unmap_rom(rdev->pdev, bios);
return false;
}
- rdev->bios = kmemdup(bios, size, GFP_KERNEL);
+ rdev->bios = kzalloc(size, GFP_KERNEL);
if (rdev->bios == NULL) {
pci_unmap_rom(rdev->pdev, bios);
return false;
}
+ memcpy_fromio(rdev->bios, bios, size);
pci_unmap_rom(rdev->pdev, bios);
return true;
}
it = interval_tree_iter_first(&rmn->objects, start, end);
while (it) {
struct radeon_bo *bo;
- struct fence *fence;
int r;
bo = container_of(it, struct radeon_bo, mn_it);
continue;
}
- fence = reservation_object_get_excl(bo->tbo.resv);
- if (fence) {
- r = radeon_fence_wait((struct radeon_fence *)fence, false);
- if (r)
- DRM_ERROR("(%d) failed to wait for user bo\n", r);
- }
+ r = reservation_object_wait_timeout_rcu(bo->tbo.resv, true,
+ false, MAX_SCHEDULE_TIMEOUT);
+ if (r)
+ DRM_ERROR("(%d) failed to wait for user bo\n", r);
radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU);
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
radeon_pm_compute_clocks(rdev);
}
-static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
- enum radeon_pm_state_type dpm_state)
+static bool radeon_dpm_single_display(struct radeon_device *rdev)
{
- int i;
- struct radeon_ps *ps;
- u32 ui_class;
bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ?
true : false;
if (single_display && (r600_dpm_get_vrefresh(rdev) >= 120))
single_display = false;
+ return single_display;
+}
+
+static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
+ enum radeon_pm_state_type dpm_state)
+{
+ int i;
+ struct radeon_ps *ps;
+ u32 ui_class;
+ bool single_display = radeon_dpm_single_display(rdev);
+
/* certain older asics have a separare 3D performance state,
* so try that first if the user selected performance
*/
struct radeon_ps *ps;
enum radeon_pm_state_type dpm_state;
int ret;
+ bool single_display = radeon_dpm_single_display(rdev);
/* if dpm init failed */
if (!rdev->pm.dpm_enabled)
/* vce just modifies an existing state so force a change */
if (ps->vce_active != rdev->pm.dpm.vce_active)
goto force;
+ /* user has made a display change (such as timing) */
+ if (rdev->pm.dpm.single_display != single_display)
+ goto force;
if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) {
/* for pre-BTC and APUs if the num crtcs changed but state is the same,
* all we need to do is update the display configuration.
rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
+ rdev->pm.dpm.single_display = single_display;
/* wait for the rings to drain */
for (i = 0; i < RADEON_NUM_RINGS; i++) {
seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw);
seq_printf(m, "%u dwords in ring\n", count);
- if (!ring->ready)
+ if (!ring->ring)
return 0;
/* print 8 dw before current rptr as often it's the last executed
enum dma_data_direction direction = write ?
DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
+ /* double check that we don't free the table twice */
+ if (!ttm->sg->sgl)
+ return;
+
/* free the sg table and pages again */
dma_unmap_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction);
WREG32(VCE_LMI_SWAP_CNTL1, 0);
WREG32(VCE_LMI_VM_CTRL, 0);
+ WREG32(VCE_LMI_VCPU_CACHE_40BIT_BAR, addr >> 8);
+
+ addr &= 0xff;
size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size);
WREG32(VCE_VCPU_CACHE_OFFSET0, addr & 0x7fffffff);
WREG32(VCE_VCPU_CACHE_SIZE0, size);
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = bma180_get_data_reg(data, bit);
if (ret < 0) {
int val;
int val2;
u8 bw_bits;
-} bmc150_accel_samp_freq_table[] = { {7, 810000, 0x08},
- {15, 630000, 0x09},
- {31, 250000, 0x0A},
- {62, 500000, 0x0B},
- {125, 0, 0x0C},
- {250, 0, 0x0D},
- {500, 0, 0x0E},
- {1000, 0, 0x0F} };
+} bmc150_accel_samp_freq_table[] = { {15, 620000, 0x08},
+ {31, 260000, 0x09},
+ {62, 500000, 0x0A},
+ {125, 0, 0x0B},
+ {250, 0, 0x0C},
+ {500, 0, 0x0D},
+ {1000, 0, 0x0E},
+ {2000, 0, 0x0F} };
static const struct {
int bw_bits;
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
- "7.810000 15.630000 31.250000 62.500000 125 250 500 1000");
+ "15.620000 31.260000 62.50000 125 250 500 1000 2000");
static struct attribute *bmc150_accel_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
int bit, ret, i = 0;
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = i2c_smbus_read_word_data(data->client,
BMC150_ACCEL_AXIS_TO_REG(bit));
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = kxcjk1013_get_acc_reg(data, bit);
if (ret < 0) {
config CC10001_ADC
tristate "Cosmic Circuits 10001 ADC driver"
- depends on HAS_IOMEM || HAVE_CLK || REGULATOR
+ depends on HAVE_CLK || REGULATOR
+ depends on HAS_IOMEM
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
{
struct iio_dev *idev = iio_trigger_get_drvdata(trig);
struct at91_adc_state *st = iio_priv(idev);
- struct iio_buffer *buffer = idev->buffer;
struct at91_adc_reg_desc *reg = st->registers;
u32 status = at91_adc_readl(st, reg->trigger_register);
int value;
at91_adc_writel(st, reg->trigger_register,
status | value);
- for_each_set_bit(bit, buffer->scan_mask,
+ for_each_set_bit(bit, idev->active_scan_mask,
st->num_channels) {
struct iio_chan_spec const *chan = idev->channels + bit;
at91_adc_writel(st, AT91_ADC_CHER,
at91_adc_writel(st, reg->trigger_register,
status & ~value);
- for_each_set_bit(bit, buffer->scan_mask,
+ for_each_set_bit(bit, idev->active_scan_mask,
st->num_channels) {
struct iio_chan_spec const *chan = idev->channels + bit;
at91_adc_writel(st, AT91_ADC_CHDR,
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
- struct iio_buffer *buffer = indio_dev->buffer;
unsigned int enb = 0;
u8 bit;
tiadc_step_config(indio_dev);
- for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels)
+ for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
adc_dev->buffer_en_ch_steps = enb;
struct regulator *vref;
struct vf610_adc_feature adc_feature;
+ u32 sample_freq_avail[5];
+
struct completion completion;
};
+static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
+
#define VF610_ADC_CHAN(_idx, _chan_type) { \
.type = (_chan_type), \
.indexed = 1, \
/* sentinel */
};
-/*
- * ADC sample frequency, unit is ADCK cycles.
- * ADC clk source is ipg clock, which is the same as bus clock.
- *
- * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
- * SFCAdder: fixed to 6 ADCK cycles
- * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
- * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
- * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
- *
- * By default, enable 12 bit resolution mode, clock source
- * set to ipg clock, So get below frequency group:
- */
-static const u32 vf610_sample_freq_avail[5] =
-{1941176, 559332, 286957, 145374, 73171};
+static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
+{
+ unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
+ int i;
+
+ /*
+ * Calculate ADC sample frequencies
+ * Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
+ * which is the same as bus clock.
+ *
+ * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
+ * SFCAdder: fixed to 6 ADCK cycles
+ * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
+ * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
+ * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
+ */
+ adck_rate = ipg_rate / info->adc_feature.clk_div;
+ for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
+ info->sample_freq_avail[i] =
+ adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3));
+}
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+
/* set default Configuration for ADC controller */
- info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET;
- info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET;
+ adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
+ adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
+
+ adc_feature->calibration = true;
+ adc_feature->ovwren = true;
+
+ adc_feature->res_mode = 12;
+ adc_feature->sample_rate = 1;
+ adc_feature->lpm = true;
- info->adc_feature.calibration = true;
- info->adc_feature.ovwren = true;
+ /* Use a save ADCK which is below 20MHz on all devices */
+ adc_feature->clk_div = 8;
- info->adc_feature.clk_div = 1;
- info->adc_feature.res_mode = 12;
- info->adc_feature.sample_rate = 1;
- info->adc_feature.lpm = true;
+ vf610_adc_calculate_rates(info);
}
static void vf610_adc_cfg_post_set(struct vf610_adc *info)
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
- /* low power configuration */
cfg_data &= ~VF610_ADC_ADLPC_EN;
if (adc_feature->lpm)
cfg_data |= VF610_ADC_ADLPC_EN;
- /* disable high speed */
cfg_data &= ~VF610_ADC_ADHSC_EN;
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
return IRQ_HANDLED;
}
-static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171");
+static ssize_t vf610_show_samp_freq_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev));
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%u ", info->sample_freq_avail[i]);
+
+ /* replace trailing space by newline */
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail);
static struct attribute *vf610_attributes[] = {
- &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
- *val = vf610_sample_freq_avail[info->adc_feature.sample_rate];
+ *val = info->sample_freq_avail[info->adc_feature.sample_rate];
*val2 = 0;
return IIO_VAL_INT;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
for (i = 0;
- i < ARRAY_SIZE(vf610_sample_freq_avail);
+ i < ARRAY_SIZE(info->sample_freq_avail);
i++)
- if (val == vf610_sample_freq_avail[i]) {
+ if (val == info->sample_freq_avail[i]) {
info->adc_feature.sample_rate = i;
vf610_adc_sample_set(info);
return 0;
int bit, ret, i = 0;
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = i2c_smbus_read_word_data(data->client,
BMG160_AXIS_TO_REG(bit));
iio_trigger_set_drvdata(adis->trig, adis);
ret = iio_trigger_register(adis->trig);
- indio_dev->trig = adis->trig;
+ indio_dev->trig = iio_trigger_get(adis->trig);
if (ret)
goto error_free_irq;
}
}
-static int inv_mpu6050_write_fsr(struct inv_mpu6050_state *st, int fsr)
+static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val)
{
- int result;
+ int result, i;
u8 d;
- if (fsr < 0 || fsr > INV_MPU6050_MAX_GYRO_FS_PARAM)
- return -EINVAL;
- if (fsr == st->chip_config.fsr)
- return 0;
+ for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) {
+ if (gyro_scale_6050[i] == val) {
+ d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st,
+ st->reg->gyro_config, d);
+ if (result)
+ return result;
- d = (fsr << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
- result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d);
- if (result)
- return result;
- st->chip_config.fsr = fsr;
+ st->chip_config.fsr = i;
+ return 0;
+ }
+ }
- return 0;
+ return -EINVAL;
}
-static int inv_mpu6050_write_accel_fs(struct inv_mpu6050_state *st, int fs)
+static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val)
{
- int result;
+ int result, i;
u8 d;
- if (fs < 0 || fs > INV_MPU6050_MAX_ACCL_FS_PARAM)
- return -EINVAL;
- if (fs == st->chip_config.accl_fs)
- return 0;
+ for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) {
+ if (accel_scale[i] == val) {
+ d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st,
+ st->reg->accl_config, d);
+ if (result)
+ return result;
- d = (fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
- result = inv_mpu6050_write_reg(st, st->reg->accl_config, d);
- if (result)
- return result;
- st->chip_config.accl_fs = fs;
+ st->chip_config.accl_fs = i;
+ return 0;
+ }
+ }
- return 0;
+ return -EINVAL;
}
static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ANGL_VEL:
- result = inv_mpu6050_write_fsr(st, val);
+ result = inv_mpu6050_write_gyro_scale(st, val2);
break;
case IIO_ACCEL:
- result = inv_mpu6050_write_accel_fs(st, val);
+ result = inv_mpu6050_write_accel_scale(st, val2);
break;
default:
result = -EINVAL;
#include <linux/poll.h>
#include "inv_mpu_iio.h"
+static void inv_clear_kfifo(struct inv_mpu6050_state *st)
+{
+ unsigned long flags;
+
+ /* take the spin lock sem to avoid interrupt kick in */
+ spin_lock_irqsave(&st->time_stamp_lock, flags);
+ kfifo_reset(&st->timestamps);
+ spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
int inv_reset_fifo(struct iio_dev *indio_dev)
{
int result;
INV_MPU6050_BIT_FIFO_RST);
if (result)
goto reset_fifo_fail;
+
+ /* clear timestamps fifo */
+ inv_clear_kfifo(st);
+
/* enable interrupt */
if (st->chip_config.accl_fifo_enable ||
st->chip_config.gyro_fifo_enable) {
return result;
}
-static void inv_clear_kfifo(struct inv_mpu6050_state *st)
-{
- unsigned long flags;
-
- /* take the spin lock sem to avoid interrupt kick in */
- spin_lock_irqsave(&st->time_stamp_lock, flags);
- kfifo_reset(&st->timestamps);
- spin_unlock_irqrestore(&st->time_stamp_lock, flags);
-}
-
/**
* inv_mpu6050_irq_handler() - Cache a timestamp at each data ready interrupt.
*/
flush_fifo:
/* Flush HW and SW FIFOs. */
inv_reset_fifo(indio_dev);
- inv_clear_kfifo(st);
mutex_unlock(&indio_dev->mlock);
iio_trigger_notify_done(indio_dev->trig);
base = KMX61_MAG_XOUT_L;
mutex_lock(&data->lock);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = kmx61_read_measurement(data, base, bit);
if (ret < 0) {
* @attr_list: List of IIO device attributes
*
* This function frees the memory allocated for each of the IIO device
- * attributes in the list. Note: if you want to reuse the list after calling
- * this function you have to reinitialize it using INIT_LIST_HEAD().
+ * attributes in the list.
*/
void iio_free_chan_devattr_list(struct list_head *attr_list)
{
list_for_each_entry_safe(p, n, attr_list, l) {
kfree(p->dev_attr.attr.name);
+ list_del(&p->l);
kfree(p);
}
}
iio_free_chan_devattr_list(&indio_dev->channel_attr_list);
kfree(indio_dev->chan_attr_group.attrs);
+ indio_dev->chan_attr_group.attrs = NULL;
}
static void iio_dev_release(struct device *device)
error_free_setup_event_lines:
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
kfree(indio_dev->event_interface);
+ indio_dev->event_interface = NULL;
return ret;
}
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
&val);
if (dmasync)
dma_set_attr(DMA_ATTR_WRITE_BARRIER, &attrs);
+ /*
+ * If the combination of the addr and size requested for this memory
+ * region causes an integer overflow, return error.
+ */
+ if ((PAGE_ALIGN(addr + size) <= size) ||
+ (PAGE_ALIGN(addr + size) <= addr))
+ return ERR_PTR(-EINVAL);
+
if (!can_do_mlock())
return ERR_PTR(-EPERM);
((__be32 *) mailbox->buf)[1] = cpu_to_be32(cap_mask);
}
- err = mlx4_cmd(dev->dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT,
- MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED);
+ err = mlx4_cmd(dev->dev, mailbox->dma, port, MLX4_SET_PORT_IB_OPCODE,
+ MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
+ MLX4_CMD_WRAPPED);
mlx4_free_cmd_mailbox(dev->dev, mailbox);
return err;
memcpy(gids, gw->gids, sizeof gw->gids);
err = mlx4_cmd(dev, mailbox->dma, MLX4_SET_PORT_GID_TABLE << 8 | gw->port,
- 1, MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
- MLX4_CMD_WRAPPED);
+ MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT,
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED);
if (err)
pr_warn("set port command failed\n");
else
IB_LINK_LAYER_ETHERNET) {
err = mlx4_cmd(dev, mailbox->dma,
MLX4_SET_PORT_GID_TABLE << 8 | gw->port,
- 1, MLX4_CMD_SET_PORT,
+ MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT,
MLX4_CMD_TIME_CLASS_B,
MLX4_CMD_WRAPPED);
if (err)
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
int mlx5_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
{
+ struct mlx5_core_dev *mdev = to_mdev(ibcq->device)->mdev;
+ void __iomem *uar_page = mdev->priv.uuari.uars[0].map;
+
mlx5_cq_arm(&to_mcq(ibcq)->mcq,
(flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED ?
MLX5_CQ_DB_REQ_NOT_SOL : MLX5_CQ_DB_REQ_NOT,
- to_mdev(ibcq->device)->mdev->priv.uuari.uars[0].map,
- MLX5_GET_DOORBELL_LOCK(&to_mdev(ibcq->device)->mdev->priv.cq_uar_lock));
+ uar_page,
+ MLX5_GET_DOORBELL_LOCK(&mdev->priv.cq_uar_lock),
+ to_mcq(ibcq)->mcq.cons_index);
return 0;
}
cq->mcq.set_ci_db = cq->db.db;
cq->mcq.arm_db = cq->db.db + 1;
- *cq->mcq.set_ci_db = 0;
- *cq->mcq.arm_db = 0;
cq->mcq.cqe_sz = cqe_size;
err = alloc_cq_buf(dev, &cq->buf, entries, cqe_size);
cq->cqe_size = cqe_size;
cqb->ctx.cqe_sz_flags = cqe_sz_to_mlx_sz(cqe_size) << 5;
cqb->ctx.log_sz_usr_page = cpu_to_be32((ilog2(entries) << 24) | index);
- err = mlx5_vector2eqn(dev, vector, &eqn, &irqn);
+ err = mlx5_vector2eqn(dev->mdev, vector, &eqn, &irqn);
if (err)
goto err_cqb;
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
DRIVER_NAME ": Mellanox Connect-IB Infiniband driver v"
DRIVER_VERSION " (" DRIVER_RELDATE ")\n";
-int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn)
-{
- struct mlx5_eq_table *table = &dev->mdev->priv.eq_table;
- struct mlx5_eq *eq, *n;
- int err = -ENOENT;
-
- spin_lock(&table->lock);
- list_for_each_entry_safe(eq, n, &dev->eqs_list, list) {
- if (eq->index == vector) {
- *eqn = eq->eqn;
- *irqn = eq->irqn;
- err = 0;
- break;
- }
- }
- spin_unlock(&table->lock);
-
- return err;
-}
-
-static int alloc_comp_eqs(struct mlx5_ib_dev *dev)
-{
- struct mlx5_eq_table *table = &dev->mdev->priv.eq_table;
- char name[MLX5_MAX_EQ_NAME];
- struct mlx5_eq *eq, *n;
- int ncomp_vec;
- int nent;
- int err;
- int i;
-
- INIT_LIST_HEAD(&dev->eqs_list);
- ncomp_vec = table->num_comp_vectors;
- nent = MLX5_COMP_EQ_SIZE;
- for (i = 0; i < ncomp_vec; i++) {
- eq = kzalloc(sizeof(*eq), GFP_KERNEL);
- if (!eq) {
- err = -ENOMEM;
- goto clean;
- }
-
- snprintf(name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i);
- err = mlx5_create_map_eq(dev->mdev, eq,
- i + MLX5_EQ_VEC_COMP_BASE, nent, 0,
- name, &dev->mdev->priv.uuari.uars[0]);
- if (err) {
- kfree(eq);
- goto clean;
- }
- mlx5_ib_dbg(dev, "allocated completion EQN %d\n", eq->eqn);
- eq->index = i;
- spin_lock(&table->lock);
- list_add_tail(&eq->list, &dev->eqs_list);
- spin_unlock(&table->lock);
- }
-
- dev->num_comp_vectors = ncomp_vec;
- return 0;
-
-clean:
- spin_lock(&table->lock);
- list_for_each_entry_safe(eq, n, &dev->eqs_list, list) {
- list_del(&eq->list);
- spin_unlock(&table->lock);
- if (mlx5_destroy_unmap_eq(dev->mdev, eq))
- mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn);
- kfree(eq);
- spin_lock(&table->lock);
- }
- spin_unlock(&table->lock);
- return err;
-}
-
-static void free_comp_eqs(struct mlx5_ib_dev *dev)
-{
- struct mlx5_eq_table *table = &dev->mdev->priv.eq_table;
- struct mlx5_eq *eq, *n;
-
- spin_lock(&table->lock);
- list_for_each_entry_safe(eq, n, &dev->eqs_list, list) {
- list_del(&eq->list);
- spin_unlock(&table->lock);
- if (mlx5_destroy_unmap_eq(dev->mdev, eq))
- mlx5_ib_warn(dev, "failed to destroy EQ 0x%x\n", eq->eqn);
- kfree(eq);
- spin_lock(&table->lock);
- }
- spin_unlock(&table->lock);
-}
-
static int mlx5_ib_query_device(struct ib_device *ibdev,
struct ib_device_attr *props)
{
get_ext_port_caps(dev);
- err = alloc_comp_eqs(dev);
- if (err)
- goto err_dealloc;
-
MLX5_INIT_DOORBELL_LOCK(&dev->uar_lock);
strlcpy(dev->ib_dev.name, "mlx5_%d", IB_DEVICE_NAME_MAX);
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.num_comp_vectors =
+ dev->mdev->priv.eq_table.num_comp_vectors;
dev->ib_dev.dma_device = &mdev->pdev->dev;
dev->ib_dev.uverbs_abi_ver = MLX5_IB_UVERBS_ABI_VERSION;
err = init_node_data(dev);
if (err)
- goto err_eqs;
+ goto err_dealloc;
mutex_init(&dev->cap_mask_mutex);
err = create_dev_resources(&dev->devr);
if (err)
- goto err_eqs;
+ goto err_dealloc;
err = mlx5_ib_odp_init_one(dev);
if (err)
err_rsrc:
destroy_dev_resources(&dev->devr);
-err_eqs:
- free_comp_eqs(dev);
-
err_dealloc:
ib_dealloc_device((struct ib_device *)dev);
destroy_umrc_res(dev);
mlx5_ib_odp_remove_one(dev);
destroy_dev_resources(&dev->devr);
- free_comp_eqs(dev);
ib_dealloc_device(&dev->ib_dev);
}
.add = mlx5_ib_add,
.remove = mlx5_ib_remove,
.event = mlx5_ib_event,
+ .protocol = MLX5_INTERFACE_PROTOCOL_IB,
};
static int __init mlx5_ib_init(void)
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
struct ib_device ib_dev;
struct mlx5_core_dev *mdev;
MLX5_DECLARE_DOORBELL_LOCK(uar_lock);
- struct list_head eqs_list;
int num_ports;
- int num_comp_vectors;
/* serialize update of capability mask
*/
struct mutex cap_mask_mutex;
struct ib_ucontext *context,
struct ib_udata *udata);
int mlx5_ib_dealloc_xrcd(struct ib_xrcd *xrcd);
-int mlx5_vector2eqn(struct mlx5_ib_dev *dev, int vector, int *eqn, int *irqn);
int mlx5_ib_get_buf_offset(u64 addr, int page_shift, u32 *offset);
int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, u8 port);
int mlx5_ib_query_port(struct ib_device *ibdev, u8 port,
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2014 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
goto err_free;
}
- qp->db.db[0] = 0;
- qp->db.db[1] = 0;
-
qp->sq.wrid = kmalloc(qp->sq.wqe_cnt * sizeof(*qp->sq.wrid), GFP_KERNEL);
qp->sq.wr_data = kmalloc(qp->sq.wqe_cnt * sizeof(*qp->sq.wr_data), GFP_KERNEL);
qp->rq.wrid = kmalloc(qp->rq.wqe_cnt * sizeof(*qp->rq.wrid), GFP_KERNEL);
in = kzalloc(sizeof(*in), GFP_KERNEL);
if (!in)
return;
+
if (qp->state != IB_QPS_RESET) {
mlx5_ib_qp_disable_pagefaults(qp);
if (mlx5_core_qp_modify(dev->mdev, to_mlx5_state(qp->state),
- MLX5_QP_STATE_RST, in, sizeof(*in), &qp->mqp))
+ MLX5_QP_STATE_RST, in, 0, &qp->mqp))
mlx5_ib_warn(dev, "mlx5_ib: modify QP %06x to RESET failed\n",
qp->mqp.qpn);
}
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
return err;
}
- *srq->db.db = 0;
-
if (mlx5_buf_alloc(dev->mdev, buf_size, PAGE_SIZE * 2, &srq->buf)) {
mlx5_ib_dbg(dev, "buf alloc failed\n");
err = -ENOMEM;
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
queue_work(ipoib_workqueue, &priv->restart_task);
}
+static int ipoib_get_iflink(const struct net_device *dev)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+ return priv->parent->ifindex;
+}
+
static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr)
{
/*
.ndo_start_xmit = ipoib_start_xmit,
.ndo_tx_timeout = ipoib_timeout,
.ndo_set_rx_mode = ipoib_set_mcast_list,
+ .ndo_get_iflink = ipoib_get_iflink,
};
void ipoib_setup(struct net_device *dev)
}
priv->child_type = type;
- priv->dev->iflink = ppriv->dev->ifindex;
list_add_tail(&priv->list, &ppriv->child_intfs);
return 0;
mutex_unlock(&alps_mutex);
}
-static void alps_report_bare_ps2_packet(struct input_dev *dev,
+static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
unsigned char packet[],
bool report_buttons)
{
+ struct alps_data *priv = psmouse->private;
+ struct input_dev *dev;
+
+ /* Figure out which device to use to report the bare packet */
+ if (priv->proto_version == ALPS_PROTO_V2 &&
+ (priv->flags & ALPS_DUALPOINT)) {
+ /* On V2 devices the DualPoint Stick reports bare packets */
+ dev = priv->dev2;
+ } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) {
+ /* Register dev3 mouse if we received PS/2 packet first time */
+ if (!IS_ERR(priv->dev3))
+ psmouse_queue_work(psmouse, &priv->dev3_register_work,
+ 0);
+ return;
+ } else {
+ dev = priv->dev3;
+ }
+
if (report_buttons)
alps_report_buttons(dev, NULL,
packet[0] & 1, packet[0] & 2, packet[0] & 4);
* de-synchronization.
*/
- alps_report_bare_ps2_packet(priv->dev2,
- &psmouse->packet[3], false);
+ alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3],
+ false);
/*
* Continue with the standard ALPS protocol handling,
* properly we only do this if the device is fully synchronized.
*/
if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
-
- /* Register dev3 mouse if we received PS/2 packet first time */
- if (unlikely(!priv->dev3))
- psmouse_queue_work(psmouse,
- &priv->dev3_register_work, 0);
-
if (psmouse->pktcnt == 3) {
- /* Once dev3 mouse device is registered report data */
- if (likely(!IS_ERR_OR_NULL(priv->dev3)))
- alps_report_bare_ps2_packet(priv->dev3,
- psmouse->packet,
- true);
+ alps_report_bare_ps2_packet(psmouse, psmouse->packet,
+ true);
return PSMOUSE_FULL_PACKET;
}
return PSMOUSE_GOOD_DATA;
priv->set_abs_params = alps_set_abs_params_mt;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
- priv->x_max = 1360;
- priv->y_max = 660;
priv->x_bits = 23;
priv->y_bits = 12;
+
+ if (alps_dolphin_get_device_area(psmouse, priv))
+ return -EIO;
+
break;
case ALPS_PROTO_V6:
priv->set_abs_params = alps_set_abs_params_mt;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
-
- if (alps_dolphin_get_device_area(psmouse, priv))
- return -EIO;
+ priv->x_max = 0xfff;
+ priv->y_max = 0x7ff;
if (priv->fw_ver[1] != 0xba)
priv->flags |= ALPS_BUTTONPAD;
{ANY_BOARD_ID, ANY_BOARD_ID},
1024, 5022, 2508, 4832
},
+ {
+ (const char * const []){"LEN2006", NULL},
+ {2691, 2691},
+ 1024, 5045, 2457, 4832
+ },
{
(const char * const []){"LEN2006", NULL},
{ANY_BOARD_ID, ANY_BOARD_ID},
"LEN2003",
"LEN2004", /* L440 */
"LEN2005",
- "LEN2006",
+ "LEN2006", /* Edge E440/E540 */
"LEN2007",
"LEN2008",
"LEN2009",
return 0;
spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
- if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS)
+ if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS &&
+ smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
ret = arm_smmu_iova_to_phys_hard(domain, iova);
- else
+ } else {
ret = ops->iova_to_phys(ops, iova);
+ }
+
spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
return ret;
return -ENODEV;
}
- if (smmu->version == 1 || (!(id & ID0_ATOSNS) && (id & ID0_S1TS))) {
+ if ((id & ID0_S1TS) && ((smmu->version == 1) || (id & ID0_ATOSNS))) {
smmu->features |= ARM_SMMU_FEAT_TRANS_OPS;
dev_notice(smmu->dev, "\taddress translation ops\n");
}
static void domain_exit(struct dmar_domain *domain)
{
- struct dmar_drhd_unit *drhd;
- struct intel_iommu *iommu;
struct page *freelist = NULL;
+ int i;
/* Domain 0 is reserved, so dont process it */
if (!domain)
/* clear attached or cached domains */
rcu_read_lock();
- for_each_active_iommu(iommu, drhd)
- iommu_detach_domain(domain, iommu);
+ for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus)
+ iommu_detach_domain(domain, g_iommus[i]);
rcu_read_unlock();
dma_free_pagelist(freelist);
static const struct of_device_id ipmmu_of_ids[] = {
{ .compatible = "renesas,ipmmu-vmsa", },
+ { }
};
static struct platform_driver ipmmu_driver = {
static void its_encode_devid(struct its_cmd_block *cmd, u32 devid)
{
- cmd->raw_cmd[0] &= ~(0xffffUL << 32);
+ cmd->raw_cmd[0] &= BIT_ULL(32) - 1;
cmd->raw_cmd[0] |= ((u64)devid) << 32;
}
int i;
int psz = SZ_64K;
u64 shr = GITS_BASER_InnerShareable;
+ u64 cache = GITS_BASER_WaWb;
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
val = (virt_to_phys(base) |
(type << GITS_BASER_TYPE_SHIFT) |
((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) |
- GITS_BASER_WaWb |
+ cache |
shr |
GITS_BASER_VALID);
* Shareability didn't stick. Just use
* whatever the read reported, which is likely
* to be the only thing this redistributor
- * supports.
+ * supports. If that's zero, make it
+ * non-cacheable as well.
*/
shr = tmp & GITS_BASER_SHAREABILITY_MASK;
+ if (!shr)
+ cache = GITS_BASER_nC;
goto retry_baser;
}
tmp = readq_relaxed(rbase + GICR_PROPBASER);
if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) {
+ if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) {
+ /*
+ * The HW reports non-shareable, we must
+ * remove the cacheability attributes as
+ * well.
+ */
+ val &= ~(GICR_PROPBASER_SHAREABILITY_MASK |
+ GICR_PROPBASER_CACHEABILITY_MASK);
+ val |= GICR_PROPBASER_nC;
+ writeq_relaxed(val, rbase + GICR_PROPBASER);
+ }
pr_info_once("GIC: using cache flushing for LPI property table\n");
gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING;
}
/* set PENDBASE */
val = (page_to_phys(pend_page) |
- GICR_PROPBASER_InnerShareable |
- GICR_PROPBASER_WaWb);
+ GICR_PENDBASER_InnerShareable |
+ GICR_PENDBASER_WaWb);
writeq_relaxed(val, rbase + GICR_PENDBASER);
+ tmp = readq_relaxed(rbase + GICR_PENDBASER);
+
+ if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) {
+ /*
+ * The HW reports non-shareable, we must remove the
+ * cacheability attributes as well.
+ */
+ val &= ~(GICR_PENDBASER_SHAREABILITY_MASK |
+ GICR_PENDBASER_CACHEABILITY_MASK);
+ val |= GICR_PENDBASER_nC;
+ writeq_relaxed(val, rbase + GICR_PENDBASER);
+ }
/* Enable LPIs */
val = readl_relaxed(rbase + GICR_CTLR);
* This ITS wants a linear CPU number.
*/
target = readq_relaxed(gic_data_rdist_rd_base() + GICR_TYPER);
- target = GICR_TYPER_CPU_NUMBER(target);
+ target = GICR_TYPER_CPU_NUMBER(target) << 16;
}
/* Perform collection mapping */
writeq_relaxed(baser, its->base + GITS_CBASER);
tmp = readq_relaxed(its->base + GITS_CBASER);
- writeq_relaxed(0, its->base + GITS_CWRITER);
- writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
- if ((tmp ^ baser) & GITS_BASER_SHAREABILITY_MASK) {
+ if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) {
+ if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) {
+ /*
+ * The HW reports non-shareable, we must
+ * remove the cacheability attributes as
+ * well.
+ */
+ baser &= ~(GITS_CBASER_SHAREABILITY_MASK |
+ GITS_CBASER_CACHEABILITY_MASK);
+ baser |= GITS_CBASER_nC;
+ writeq_relaxed(baser, its->base + GITS_CBASER);
+ }
pr_info("ITS: using cache flushing for cmd queue\n");
its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING;
}
+ writeq_relaxed(0, its->base + GITS_CWRITER);
+ writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
+
if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) {
its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its);
if (!its->domain) {
config LGUEST
tristate "Linux hypervisor example code"
- depends on X86_32 && EVENTFD && TTY
+ depends on X86_32 && EVENTFD && TTY && PCI_DIRECT
select HVC_DRIVER
---help---
This is a very simple module which allows you to run
dm_get(md);
atomic_inc(&md->open_count);
-
out:
spin_unlock(&_minor_lock);
static void dm_blk_close(struct gendisk *disk, fmode_t mode)
{
- struct mapped_device *md = disk->private_data;
+ struct mapped_device *md;
spin_lock(&_minor_lock);
+ md = disk->private_data;
+ if (WARN_ON(!md))
+ goto out;
+
if (atomic_dec_and_test(&md->open_count) &&
(test_bit(DMF_DEFERRED_REMOVE, &md->flags)))
queue_work(deferred_remove_workqueue, &deferred_remove_work);
dm_put(md);
-
+out:
spin_unlock(&_minor_lock);
}
int minor = MINOR(disk_devt(md->disk));
unlock_fs(md);
- bdput(md->bdev);
destroy_workqueue(md->wq);
if (md->kworker_task)
mempool_destroy(md->rq_pool);
if (md->bs)
bioset_free(md->bs);
- blk_integrity_unregister(md->disk);
- del_gendisk(md->disk);
+
cleanup_srcu_struct(&md->io_barrier);
free_table_devices(&md->table_devices);
- free_minor(minor);
+ dm_stats_cleanup(&md->stats);
spin_lock(&_minor_lock);
md->disk->private_data = NULL;
spin_unlock(&_minor_lock);
-
+ if (blk_get_integrity(md->disk))
+ blk_integrity_unregister(md->disk);
+ del_gendisk(md->disk);
put_disk(md->disk);
blk_cleanup_queue(md->queue);
- dm_stats_cleanup(&md->stats);
+ bdput(md->bdev);
+ free_minor(minor);
+
module_put(THIS_MODULE);
kfree(md);
}
might_sleep();
- spin_lock(&_minor_lock);
map = dm_get_live_table(md, &srcu_idx);
+
+ spin_lock(&_minor_lock);
idr_replace(&_minor_idr, MINOR_ALLOCED, MINOR(disk_devt(dm_disk(md))));
set_bit(DMF_FREEING, &md->flags);
spin_unlock(&_minor_lock);
for (id = kempld_dmi_table;
id->matches[0].slot != DMI_NONE; id++)
if (strstr(id->ident, force_device_id))
- if (id->callback && id->callback(id))
+ if (id->callback && !id->callback(id))
break;
if (id->matches[0].slot == DMI_NONE)
return -ENODEV;
int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data)
{
u16 value;
+ u8 *buf;
+ int ret;
if (!data)
return -EINVAL;
- *data = 0;
+
+ buf = kzalloc(sizeof(u8), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT;
value = swab16(addr);
- return usb_control_msg(ucr->pusb_dev,
+ ret = usb_control_msg(ucr->pusb_dev,
usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, 0, data, 1, 100);
+ value, 0, buf, 1, 100);
+ *data = *buf;
+
+ kfree(buf);
+ return ret;
}
EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register);
int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status)
{
int ret;
+ u16 *buf;
if (!status)
return -EINVAL;
- if (polling_pipe == 0)
+ if (polling_pipe == 0) {
+ buf = kzalloc(sizeof(u16), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(ucr->pusb_dev,
usb_rcvctrlpipe(ucr->pusb_dev, 0),
RTSX_USB_REQ_POLL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, 0, status, 2, 100);
- else
+ 0, 0, buf, 2, 100);
+ *status = *buf;
+
+ kfree(buf);
+ } else {
ret = rtsx_usb_get_status_with_bulk(ucr, status);
+ }
/* usb_control_msg may return positive when success */
if (ret < 0)
else
port->aggregator->is_individual = true;
- port->aggregator->actor_admin_aggregator_key = port->actor_admin_port_key;
- port->aggregator->actor_oper_aggregator_key = port->actor_oper_port_key;
+ port->aggregator->actor_admin_aggregator_key =
+ port->actor_admin_port_key;
+ port->aggregator->actor_oper_aggregator_key =
+ port->actor_oper_port_key;
port->aggregator->partner_system =
port->partner_oper.system;
port->aggregator->partner_system_priority =
};
if (port) {
- port->actor_port_number = 1;
port->actor_port_priority = 0xff;
- port->actor_system = null_mac_addr;
- port->actor_system_priority = 0xffff;
port->actor_port_aggregator_identifier = 0;
port->ntt = false;
- port->actor_admin_port_key = 1;
- port->actor_oper_port_key = 1;
port->actor_admin_port_state = AD_STATE_AGGREGATION |
AD_STATE_LACP_ACTIVITY;
port->actor_oper_port_state = AD_STATE_AGGREGATION |
port->sm_mux_state = 0;
port->sm_mux_timer_counter = 0;
port->sm_tx_state = 0;
- port->sm_tx_timer_counter = 0;
- port->slave = NULL;
port->aggregator = NULL;
port->next_port_in_aggregator = NULL;
port->transaction_id = 0;
* lacpdu's are sent in one second)
*/
port->sm_tx_timer_counter = ad_ticks_per_sec/AD_MAX_TX_IN_SECOND;
- port->aggregator = NULL;
- port->next_port_in_aggregator = NULL;
__disable_port(port);
spin_lock_bh(&slave->bond->mode_lock);
port->actor_admin_port_key &= ~AD_SPEED_KEY_MASKS;
- port->actor_oper_port_key = port->actor_admin_port_key |=
- (__get_link_speed(port) << 1);
+ port->actor_admin_port_key |= __get_link_speed(port) << 1;
+ port->actor_oper_port_key = port->actor_admin_port_key;
netdev_dbg(slave->bond->dev, "Port %d changed speed\n", port->actor_port_number);
/* there is no need to reselect a new aggregator, just signal the
* state machines to reinitialize
spin_lock_bh(&slave->bond->mode_lock);
port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS;
- port->actor_oper_port_key = port->actor_admin_port_key |=
- __get_duplex(port);
+ port->actor_admin_port_key |= __get_duplex(port);
+ port->actor_oper_port_key = port->actor_admin_port_key;
netdev_dbg(slave->bond->dev, "Port %d slave %s changed duplex\n",
port->actor_port_number, slave->dev->name);
if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
* on link up we are forcing recheck on the duplex and speed since
* some of he adaptors(ce1000.lan) report.
*/
+ port->actor_admin_port_key &= ~(AD_DUPLEX_KEY_MASKS|AD_SPEED_KEY_MASKS);
if (link == BOND_LINK_UP) {
port->is_enabled = true;
- port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS;
- port->actor_oper_port_key = port->actor_admin_port_key |=
- __get_duplex(port);
- port->actor_admin_port_key &= ~AD_SPEED_KEY_MASKS;
- port->actor_oper_port_key = port->actor_admin_port_key |=
- (__get_link_speed(port) << 1);
- if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS)
+ port->actor_admin_port_key |=
+ (__get_link_speed(port) << 1) | __get_duplex(port);
+ if (port->actor_admin_port_key & AD_DUPLEX_KEY_MASKS)
port->sm_vars |= AD_PORT_LACP_ENABLED;
} else {
/* link has failed */
port->is_enabled = false;
- port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS;
- port->actor_oper_port_key = (port->actor_admin_port_key &=
- ~AD_SPEED_KEY_MASKS);
port->sm_vars &= ~AD_PORT_LACP_ENABLED;
}
+ port->actor_oper_port_key = port->actor_admin_port_key;
netdev_dbg(slave->bond->dev, "Port %d changed link status to %s\n",
port->actor_port_number,
link == BOND_LINK_UP ? "UP" : "DOWN");
/* Find out if any slaves have the same mapping as this skb. */
bond_for_each_slave_rcu(bond, slave, iter) {
if (slave->queue_id == skb->queue_mapping) {
- if (bond_slave_can_tx(slave)) {
+ if (bond_slave_is_up(slave) &&
+ slave->link == BOND_LINK_UP) {
bond_dev_queue_xmit(bond, skb, slave->dev);
return 0;
}
struct tty_struct *tty;
bool tx_started;
unsigned long state;
- char *tty_name;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_tty_dir;
struct debugfs_blob_wrapper tx_blob;
rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ?
CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
new_state = max(tx_state, rx_state);
- } else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE)) {
+ } else {
__flexcan_get_berr_counter(dev, &bec);
- new_state = CAN_STATE_ERROR_PASSIVE;
+ new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ?
+ CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF;
rx_state = bec.rxerr >= bec.txerr ? new_state : 0;
tx_state = bec.rxerr <= bec.txerr ? new_state : 0;
- } else {
- new_state = CAN_STATE_BUS_OFF;
}
/* state hasn't changed */
const struct flexcan_devtype_data *devtype_data;
struct net_device *dev;
struct flexcan_priv *priv;
+ struct regulator *reg_xceiver;
struct resource *mem;
struct clk *clk_ipg = NULL, *clk_per = NULL;
void __iomem *base;
int err, irq;
u32 clock_freq = 0;
+ reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
+ if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ else if (IS_ERR(reg_xceiver))
+ reg_xceiver = NULL;
+
if (pdev->dev.of_node)
of_property_read_u32(pdev->dev.of_node,
"clock-frequency", &clock_freq);
priv->pdata = dev_get_platdata(&pdev->dev);
priv->devtype_data = devtype_data;
- priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
- if (IS_ERR(priv->reg_xceiver))
- priv->reg_xceiver = NULL;
+ priv->reg_xceiver = reg_xceiver;
netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);
* CPC_MSG_TYPE_EXT_CAN_FRAME or CPC_MSG_TYPE_EXT_RTR_FRAME.
*/
struct cpc_can_msg {
- u32 id;
+ __le32 id;
u8 length;
u8 msg[8];
};
u8 type; /* type of message */
u8 length; /* length of data within union 'msg' */
u8 msgid; /* confirmation handle */
- u32 ts_sec; /* timestamp in seconds */
- u32 ts_nsec; /* timestamp in nano seconds */
+ __le32 ts_sec; /* timestamp in seconds */
+ __le32 ts_nsec; /* timestamp in nano seconds */
union {
u8 generic[64];
msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE];
- msg->msg.can_msg.id = cf->can_id & CAN_ERR_MASK;
+ msg->msg.can_msg.id = cpu_to_le32(cf->can_id & CAN_ERR_MASK);
msg->msg.can_msg.length = cf->can_dlc;
if (cf->can_id & CAN_RTR_FLAG) {
msg->length = CPC_CAN_MSG_MIN_SIZE + cf->can_dlc;
}
- /* Respect byte order */
- msg->msg.can_msg.id = cpu_to_le32(msg->msg.can_msg.id);
-
for (i = 0; i < MAX_TX_URBS; i++) {
if (dev->tx_contexts[i].echo_index == MAX_TX_URBS) {
context = &dev->tx_contexts[i];
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
init_usb_anchor(&dev->rx_submitted);
atomic_set(&dev->active_channels, 0);
#include <linux/can/dev.h>
#include <linux/can/error.h>
-#define MAX_TX_URBS 16
#define MAX_RX_URBS 4
#define START_TIMEOUT 1000 /* msecs */
#define STOP_TIMEOUT 1000 /* msecs */
};
};
+/* Context for an outstanding, not yet ACKed, transmission */
struct kvaser_usb_tx_urb_context {
struct kvaser_usb_net_priv *priv;
u32 echo_index;
struct usb_endpoint_descriptor *bulk_in, *bulk_out;
struct usb_anchor rx_submitted;
+ /* @max_tx_urbs: Firmware-reported maximum number of oustanding,
+ * not yet ACKed, transmissions on this device. This value is
+ * also used as a sentinel for marking free tx contexts.
+ */
u32 fw_version;
unsigned int nchannels;
+ unsigned int max_tx_urbs;
enum kvaser_usb_family family;
bool rxinitdone;
struct kvaser_usb_net_priv {
struct can_priv can;
-
- spinlock_t tx_contexts_lock;
- int active_tx_contexts;
- struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
-
- struct usb_anchor tx_submitted;
- struct completion start_comp, stop_comp;
+ struct can_berr_counter bec;
struct kvaser_usb *dev;
struct net_device *netdev;
int channel;
- struct can_berr_counter bec;
+ struct completion start_comp, stop_comp;
+ struct usb_anchor tx_submitted;
+
+ spinlock_t tx_contexts_lock;
+ int active_tx_contexts;
+ struct kvaser_usb_tx_urb_context tx_contexts[];
};
static const struct usb_device_id kvaser_usb_table[] = {
* for further details.
*/
if (tmp->len == 0) {
- pos = round_up(pos,
- dev->bulk_in->wMaxPacketSize);
+ pos = round_up(pos, le16_to_cpu(dev->bulk_in->
+ wMaxPacketSize));
continue;
}
switch (dev->family) {
case KVASER_LEAF:
dev->fw_version = le32_to_cpu(msg.u.leaf.softinfo.fw_version);
+ dev->max_tx_urbs =
+ le16_to_cpu(msg.u.leaf.softinfo.max_outstanding_tx);
break;
case KVASER_USBCAN:
dev->fw_version = le32_to_cpu(msg.u.usbcan.softinfo.fw_version);
+ dev->max_tx_urbs =
+ le16_to_cpu(msg.u.usbcan.softinfo.max_outstanding_tx);
break;
}
stats = &priv->netdev->stats;
- context = &priv->tx_contexts[tid % MAX_TX_URBS];
+ context = &priv->tx_contexts[tid % dev->max_tx_urbs];
/* Sometimes the state change doesn't come after a bus-off event */
if (priv->can.restart_ms &&
spin_lock_irqsave(&priv->tx_contexts_lock, flags);
can_get_echo_skb(priv->netdev, context->echo_index);
- context->echo_index = MAX_TX_URBS;
+ context->echo_index = dev->max_tx_urbs;
--priv->active_tx_contexts;
netif_wake_queue(priv->netdev);
* number of events in case of a heavy rx load on the bus.
*/
if (msg->len == 0) {
- pos = round_up(pos, dev->bulk_in->wMaxPacketSize);
+ pos = round_up(pos, le16_to_cpu(dev->bulk_in->
+ wMaxPacketSize));
continue;
}
static void kvaser_usb_reset_tx_urb_contexts(struct kvaser_usb_net_priv *priv)
{
- int i;
+ int i, max_tx_urbs;
+
+ max_tx_urbs = priv->dev->max_tx_urbs;
priv->active_tx_contexts = 0;
- for (i = 0; i < MAX_TX_URBS; i++)
- priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+ for (i = 0; i < max_tx_urbs; i++)
+ priv->tx_contexts[i].echo_index = max_tx_urbs;
}
/* This method might sleep. Do not call it in the atomic context
*msg_tx_can_flags |= MSG_FLAG_REMOTE_FRAME;
spin_lock_irqsave(&priv->tx_contexts_lock, flags);
- for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
- if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ for (i = 0; i < dev->max_tx_urbs; i++) {
+ if (priv->tx_contexts[i].echo_index == dev->max_tx_urbs) {
context = &priv->tx_contexts[i];
context->echo_index = i;
can_put_echo_skb(skb, netdev, context->echo_index);
++priv->active_tx_contexts;
- if (priv->active_tx_contexts >= MAX_TX_URBS)
+ if (priv->active_tx_contexts >= dev->max_tx_urbs)
netif_stop_queue(netdev);
break;
spin_lock_irqsave(&priv->tx_contexts_lock, flags);
can_free_echo_skb(netdev, context->echo_index);
- context->echo_index = MAX_TX_URBS;
+ context->echo_index = dev->max_tx_urbs;
--priv->active_tx_contexts;
netif_wake_queue(netdev);
if (err)
return err;
- netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ netdev = alloc_candev(sizeof(*priv) +
+ dev->max_tx_urbs * sizeof(*priv->tx_contexts),
+ dev->max_tx_urbs);
if (!netdev) {
dev_err(&intf->dev, "Cannot alloc candev\n");
return -ENOMEM;
return err;
}
+ dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
+ ((dev->fw_version >> 24) & 0xff),
+ ((dev->fw_version >> 16) & 0xff),
+ (dev->fw_version & 0xffff));
+
+ dev_dbg(&intf->dev, "Max oustanding tx = %d URBs\n", dev->max_tx_urbs);
+
err = kvaser_usb_get_card_info(dev);
if (err) {
dev_err(&intf->dev,
return err;
}
- dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
- ((dev->fw_version >> 24) & 0xff),
- ((dev->fw_version >> 16) & 0xff),
- (dev->fw_version & 0xffff));
-
for (i = 0; i < dev->nchannels; i++) {
err = kvaser_usb_init_one(intf, id, i);
if (err) {
#define PUCAN_CMD_FILTER_STD 0x008
#define PUCAN_CMD_TX_ABORT 0x009
#define PUCAN_CMD_WR_ERR_CNT 0x00a
-#define PUCAN_CMD_RX_FRAME_ENABLE 0x00b
-#define PUCAN_CMD_RX_FRAME_DISABLE 0x00c
+#define PUCAN_CMD_SET_EN_OPTION 0x00b
+#define PUCAN_CMD_CLR_DIS_OPTION 0x00c
#define PUCAN_CMD_END_OF_COLLECTION 0x3ff
/* uCAN received messages list */
u16 unused;
};
-/* uCAN RX_FRAME_ENABLE command fields */
-#define PUCAN_FLTEXT_ERROR 0x0001
-#define PUCAN_FLTEXT_BUSLOAD 0x0002
+/* uCAN SET_EN/CLR_DIS _OPTION command fields */
+#define PUCAN_OPTION_ERROR 0x0001
+#define PUCAN_OPTION_BUSLOAD 0x0002
+#define PUCAN_OPTION_CANDFDISO 0x0004
-struct __packed pucan_filter_ext {
+struct __packed pucan_options {
__le16 opcode_channel;
- __le16 ext_mask;
+ __le16 options;
u32 unused;
};
u8 unused[5];
};
-/* Extended usage of uCAN commands CMD_RX_FRAME_xxxABLE for PCAN-USB Pro FD */
+/* Extended usage of uCAN commands CMD_xxx_xx_OPTION for PCAN-USB Pro FD */
#define PCAN_UFD_FLTEXT_CALIBRATION 0x8000
-struct __packed pcan_ufd_filter_ext {
+struct __packed pcan_ufd_options {
__le16 opcode_channel;
- __le16 ext_mask;
+ __le16 ucan_mask;
u16 unused;
__le16 usb_mask;
};
static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail)
{
void *cmd_head = pcan_usb_fd_cmd_buffer(dev);
- int err;
+ int err = 0;
u8 *packet_ptr;
int i, n = 1, packet_len;
ptrdiff_t cmd_len;
/* moves the pointer forward */
pc += sizeof(struct pucan_wr_err_cnt);
+ /* add command to switch from ISO to non-ISO mode, if fw allows it */
+ if (dev->can.ctrlmode_supported & CAN_CTRLMODE_FD_NON_ISO) {
+ struct pucan_options *puo = (struct pucan_options *)pc;
+
+ puo->opcode_channel =
+ (dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ?
+ pucan_cmd_opcode_channel(dev,
+ PUCAN_CMD_CLR_DIS_OPTION) :
+ pucan_cmd_opcode_channel(dev, PUCAN_CMD_SET_EN_OPTION);
+
+ puo->options = cpu_to_le16(PUCAN_OPTION_CANDFDISO);
+
+ /* to be sure that no other extended bits will be taken into
+ * account
+ */
+ puo->unused = 0;
+
+ /* moves the pointer forward */
+ pc += sizeof(struct pucan_options);
+ }
+
/* next, go back to operational mode */
cmd = (struct pucan_command *)pc;
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
return pcan_usb_fd_send_cmd(dev, cmd);
}
-/* set/unset notifications filter:
+/* set/unset options
*
- * onoff sets(1)/unset(0) notifications
- * mask each bit defines a kind of notification to set/unset
+ * onoff set(1)/unset(0) options
+ * mask each bit defines a kind of options to set/unset
*/
-static int pcan_usb_fd_set_filter_ext(struct peak_usb_device *dev,
- bool onoff, u16 ext_mask, u16 usb_mask)
+static int pcan_usb_fd_set_options(struct peak_usb_device *dev,
+ bool onoff, u16 ucan_mask, u16 usb_mask)
{
- struct pcan_ufd_filter_ext *cmd = pcan_usb_fd_cmd_buffer(dev);
+ struct pcan_ufd_options *cmd = pcan_usb_fd_cmd_buffer(dev);
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
- (onoff) ? PUCAN_CMD_RX_FRAME_ENABLE :
- PUCAN_CMD_RX_FRAME_DISABLE);
+ (onoff) ? PUCAN_CMD_SET_EN_OPTION :
+ PUCAN_CMD_CLR_DIS_OPTION);
- cmd->ext_mask = cpu_to_le16(ext_mask);
+ cmd->ucan_mask = cpu_to_le16(ucan_mask);
cmd->usb_mask = cpu_to_le16(usb_mask);
/* send the command */
&pcan_usb_pro_fd);
/* enable USB calibration messages */
- err = pcan_usb_fd_set_filter_ext(dev, 1,
- PUCAN_FLTEXT_ERROR,
- PCAN_UFD_FLTEXT_CALIBRATION);
+ err = pcan_usb_fd_set_options(dev, 1,
+ PUCAN_OPTION_ERROR,
+ PCAN_UFD_FLTEXT_CALIBRATION);
}
pdev->usb_if->dev_opened_count++;
/* turn off special msgs for that interface if no other dev opened */
if (pdev->usb_if->dev_opened_count == 1)
- pcan_usb_fd_set_filter_ext(dev, 0,
- PUCAN_FLTEXT_ERROR,
- PCAN_UFD_FLTEXT_CALIBRATION);
+ pcan_usb_fd_set_options(dev, 0,
+ PUCAN_OPTION_ERROR,
+ PCAN_UFD_FLTEXT_CALIBRATION);
pdev->usb_if->dev_opened_count--;
return 0;
pdev->usb_if->fw_info.fw_version[2],
dev->adapter->ctrl_count);
- /* the currently supported hw is non-ISO */
- dev->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO;
+ /* check for ability to switch between ISO/non-ISO modes */
+ if (pdev->usb_if->fw_info.fw_version[0] >= 2) {
+ /* firmware >= 2.x supports ISO/non-ISO switching */
+ dev->can.ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO;
+ } else {
+ /* firmware < 2.x only supports fixed(!) non-ISO */
+ dev->can.ctrlmode |= CAN_CTRLMODE_FD_NON_ISO;
+ }
/* tell the hardware the can driver is running */
err = pcan_usb_fd_drv_loaded(dev, 1);
if (dev->ctrl_idx == 0) {
/* turn off calibration message if any device were opened */
if (pdev->usb_if->dev_opened_count > 0)
- pcan_usb_fd_set_filter_ext(dev, 0,
- PUCAN_FLTEXT_ERROR,
- PCAN_UFD_FLTEXT_CALIBRATION);
+ pcan_usb_fd_set_options(dev, 0,
+ PUCAN_OPTION_ERROR,
+ PCAN_UFD_FLTEXT_CALIBRATION);
/* tell USB adapter that the driver is being unloaded */
pcan_usb_fd_drv_loaded(dev, 0);
if (bus == NULL)
return NULL;
- ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
+ ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
if (ret >= 0) {
- if (ret == 0x1212)
+ if (ret == PORT_SWITCH_ID_6123_A1)
return "Marvell 88E6123 (A1)";
- if (ret == 0x1213)
+ if (ret == PORT_SWITCH_ID_6123_A2)
return "Marvell 88E6123 (A2)";
- if ((ret & 0xfff0) == 0x1210)
+ if ((ret & 0xfff0) == PORT_SWITCH_ID_6123)
return "Marvell 88E6123";
- if (ret == 0x1612)
+ if (ret == PORT_SWITCH_ID_6161_A1)
return "Marvell 88E6161 (A1)";
- if (ret == 0x1613)
+ if (ret == PORT_SWITCH_ID_6161_A2)
return "Marvell 88E6161 (A2)";
- if ((ret & 0xfff0) == 0x1610)
+ if ((ret & 0xfff0) == PORT_SWITCH_ID_6161)
return "Marvell 88E6161";
- if (ret == 0x1652)
+ if (ret == PORT_SWITCH_ID_6165_A1)
return "Marvell 88E6165 (A1)";
- if (ret == 0x1653)
+ if (ret == PORT_SWITCH_ID_6165_A2)
return "Marvell 88e6165 (A2)";
- if ((ret & 0xfff0) == 0x1650)
+ if ((ret & 0xfff0) == PORT_SWITCH_ID_6165)
return "Marvell 88E6165";
}
return NULL;
}
-static int mv88e6123_61_65_switch_reset(struct dsa_switch *ds)
-{
- int i;
- int ret;
- unsigned long timeout;
-
- /* Set all ports to the disabled state. */
- for (i = 0; i < 8; i++) {
- ret = REG_READ(REG_PORT(i), 0x04);
- REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
- }
-
- /* Wait for transmit queues to drain. */
- usleep_range(2000, 4000);
-
- /* Reset the switch. */
- REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
-
- /* Wait up to one second for reset to complete. */
- timeout = jiffies + 1 * HZ;
- while (time_before(jiffies, timeout)) {
- ret = REG_READ(REG_GLOBAL, 0x00);
- if ((ret & 0xc800) == 0xc800)
- break;
-
- usleep_range(1000, 2000);
- }
- if (time_after(jiffies, timeout))
- return -ETIMEDOUT;
-
- return 0;
-}
-
static int mv88e6123_61_65_setup_global(struct dsa_switch *ds)
{
int ret;
static int mv88e6123_61_65_setup(struct dsa_switch *ds)
{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int i;
int ret;
if (ret < 0)
return ret;
- ret = mv88e6123_61_65_switch_reset(ds);
+ switch (ps->id) {
+ case PORT_SWITCH_ID_6123:
+ ps->num_ports = 3;
+ break;
+ case PORT_SWITCH_ID_6161:
+ case PORT_SWITCH_ID_6165:
+ ps->num_ports = 6;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ret = mv88e6xxx_switch_reset(ds, false);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
- for (i = 0; i < 6; i++) {
+ for (i = 0; i < ps->num_ports; i++) {
ret = mv88e6123_61_65_setup_port(ds, i);
if (ret < 0)
return ret;
return 0;
}
-static int mv88e6123_61_65_port_to_phy_addr(int port)
-{
- if (port >= 0 && port <= 4)
- return port;
- return -1;
-}
-
-static int
-mv88e6123_61_65_phy_read(struct dsa_switch *ds, int port, int regnum)
-{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int addr = mv88e6123_61_65_port_to_phy_addr(port);
- int ret;
-
- mutex_lock(&ps->phy_mutex);
- ret = mv88e6xxx_phy_read(ds, addr, regnum);
- mutex_unlock(&ps->phy_mutex);
- return ret;
-}
-
-static int
-mv88e6123_61_65_phy_write(struct dsa_switch *ds,
- int port, int regnum, u16 val)
-{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int addr = mv88e6123_61_65_port_to_phy_addr(port);
- int ret;
-
- mutex_lock(&ps->phy_mutex);
- ret = mv88e6xxx_phy_write(ds, addr, regnum, val);
- mutex_unlock(&ps->phy_mutex);
- return ret;
-}
-
-static struct mv88e6xxx_hw_stat mv88e6123_61_65_hw_stats[] = {
- { "in_good_octets", 8, 0x00, },
- { "in_bad_octets", 4, 0x02, },
- { "in_unicast", 4, 0x04, },
- { "in_broadcasts", 4, 0x06, },
- { "in_multicasts", 4, 0x07, },
- { "in_pause", 4, 0x16, },
- { "in_undersize", 4, 0x18, },
- { "in_fragments", 4, 0x19, },
- { "in_oversize", 4, 0x1a, },
- { "in_jabber", 4, 0x1b, },
- { "in_rx_error", 4, 0x1c, },
- { "in_fcs_error", 4, 0x1d, },
- { "out_octets", 8, 0x0e, },
- { "out_unicast", 4, 0x10, },
- { "out_broadcasts", 4, 0x13, },
- { "out_multicasts", 4, 0x12, },
- { "out_pause", 4, 0x15, },
- { "excessive", 4, 0x11, },
- { "collisions", 4, 0x1e, },
- { "deferred", 4, 0x05, },
- { "single", 4, 0x14, },
- { "multiple", 4, 0x17, },
- { "out_fcs_error", 4, 0x03, },
- { "late", 4, 0x1f, },
- { "hist_64bytes", 4, 0x08, },
- { "hist_65_127bytes", 4, 0x09, },
- { "hist_128_255bytes", 4, 0x0a, },
- { "hist_256_511bytes", 4, 0x0b, },
- { "hist_512_1023bytes", 4, 0x0c, },
- { "hist_1024_max_bytes", 4, 0x0d, },
- { "sw_in_discards", 4, 0x110, },
- { "sw_in_filtered", 2, 0x112, },
- { "sw_out_filtered", 2, 0x113, },
-};
-
-static void
-mv88e6123_61_65_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
-{
- mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats),
- mv88e6123_61_65_hw_stats, port, data);
-}
-
-static void
-mv88e6123_61_65_get_ethtool_stats(struct dsa_switch *ds,
- int port, uint64_t *data)
-{
- mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats),
- mv88e6123_61_65_hw_stats, port, data);
-}
-
-static int mv88e6123_61_65_get_sset_count(struct dsa_switch *ds)
-{
- return ARRAY_SIZE(mv88e6123_61_65_hw_stats);
-}
-
struct dsa_switch_driver mv88e6123_61_65_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_EDSA,
.priv_size = sizeof(struct mv88e6xxx_priv_state),
.probe = mv88e6123_61_65_probe,
.setup = mv88e6123_61_65_setup,
.set_addr = mv88e6xxx_set_addr_indirect,
- .phy_read = mv88e6123_61_65_phy_read,
- .phy_write = mv88e6123_61_65_phy_write,
+ .phy_read = mv88e6xxx_phy_read,
+ .phy_write = mv88e6xxx_phy_write,
.poll_link = mv88e6xxx_poll_link,
- .get_strings = mv88e6123_61_65_get_strings,
- .get_ethtool_stats = mv88e6123_61_65_get_ethtool_stats,
- .get_sset_count = mv88e6123_61_65_get_sset_count,
+ .get_strings = mv88e6xxx_get_strings,
+ .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
+ .get_sset_count = mv88e6xxx_get_sset_count,
#ifdef CONFIG_NET_DSA_HWMON
.get_temp = mv88e6xxx_get_temp,
#endif
#include <net/dsa.h>
#include "mv88e6xxx.h"
-/* Switch product IDs */
-#define ID_6085 0x04a0
-#define ID_6095 0x0950
-#define ID_6131 0x1060
-#define ID_6131_B2 0x1066
-
static char *mv88e6131_probe(struct device *host_dev, int sw_addr)
{
struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
if (bus == NULL)
return NULL;
- ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
+ ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
if (ret >= 0) {
int ret_masked = ret & 0xfff0;
- if (ret_masked == ID_6085)
+ if (ret_masked == PORT_SWITCH_ID_6085)
return "Marvell 88E6085";
- if (ret_masked == ID_6095)
+ if (ret_masked == PORT_SWITCH_ID_6095)
return "Marvell 88E6095/88E6095F";
- if (ret == ID_6131_B2)
+ if (ret == PORT_SWITCH_ID_6131_B2)
return "Marvell 88E6131 (B2)";
- if (ret_masked == ID_6131)
+ if (ret_masked == PORT_SWITCH_ID_6131)
return "Marvell 88E6131";
}
return NULL;
}
-static int mv88e6131_switch_reset(struct dsa_switch *ds)
-{
- int i;
- int ret;
- unsigned long timeout;
-
- /* Set all ports to the disabled state. */
- for (i = 0; i < 11; i++) {
- ret = REG_READ(REG_PORT(i), 0x04);
- REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
- }
-
- /* Wait for transmit queues to drain. */
- usleep_range(2000, 4000);
-
- /* Reset the switch. */
- REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
-
- /* Wait up to one second for reset to complete. */
- timeout = jiffies + 1 * HZ;
- while (time_before(jiffies, timeout)) {
- ret = REG_READ(REG_GLOBAL, 0x00);
- if ((ret & 0xc800) == 0xc800)
- break;
-
- usleep_range(1000, 2000);
- }
- if (time_after(jiffies, timeout))
- return -ETIMEDOUT;
-
- return 0;
-}
-
static int mv88e6131_setup_global(struct dsa_switch *ds)
{
int ret;
* (100 Mb/s on 6085) full duplex.
*/
if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p))
- if (ps->id == ID_6085)
+ if (ps->id == PORT_SWITCH_ID_6085)
REG_WRITE(addr, 0x01, 0x003d); /* 100 Mb/s */
else
REG_WRITE(addr, 0x01, 0x003e); /* 1000 Mb/s */
/* On 6085, unknown multicast forward is controlled
* here rather than in Port Control 2 register.
*/
- if (ps->id == ID_6085)
+ if (ps->id == PORT_SWITCH_ID_6085)
val |= 0x0008;
}
if (ds->dsa_port_mask & (1 << p))
val |= 0x0100;
REG_WRITE(addr, 0x04, val);
- /* Port Control 1: disable trunking. Also, if this is the
- * CPU port, enable learn messages to be sent to this port.
- */
- REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
-
- /* Port based VLAN map: give each port its own address
- * database, allow the CPU port to talk to each of the 'real'
- * ports, and allow each of the 'real' ports to only talk to
- * the upstream port.
- */
- val = (p & 0xf) << 12;
- if (dsa_is_cpu_port(ds, p))
- val |= ds->phys_port_mask;
- else
- val |= 1 << dsa_upstream_port(ds);
- REG_WRITE(addr, 0x06, val);
-
- /* Default VLAN ID and priority: don't set a default VLAN
- * ID, and set the default packet priority to zero.
- */
- REG_WRITE(addr, 0x07, 0x0000);
-
/* Port Control 2: don't force a good FCS, don't use
* VLAN-based, source address-based or destination
* address-based priority overrides, don't let the switch
* If this is the upstream port for this switch, enable
* forwarding of unknown multicast addresses.
*/
- if (ps->id == ID_6085)
+ if (ps->id == PORT_SWITCH_ID_6085)
/* on 6085, bits 3:0 are reserved, bit 6 control ARP
* mirroring, and multicast forward is handled in
* Port Control register.
*/
REG_WRITE(addr, 0x19, 0x7654);
- return 0;
+ return mv88e6xxx_setup_port_common(ds, p);
}
static int mv88e6131_setup(struct dsa_switch *ds)
int i;
int ret;
- mutex_init(&ps->smi_mutex);
+ ret = mv88e6xxx_setup_common(ds);
+ if (ret < 0)
+ return ret;
+
mv88e6xxx_ppu_state_init(ds);
- mutex_init(&ps->stats_mutex);
- ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0;
+ switch (ps->id) {
+ case PORT_SWITCH_ID_6085:
+ ps->num_ports = 10;
+ break;
+ case PORT_SWITCH_ID_6095:
+ ps->num_ports = 11;
+ break;
+ case PORT_SWITCH_ID_6131:
+ case PORT_SWITCH_ID_6131_B2:
+ ps->num_ports = 8;
+ break;
+ default:
+ return -ENODEV;
+ }
- ret = mv88e6131_switch_reset(ds);
+ ret = mv88e6xxx_switch_reset(ds, false);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
- for (i = 0; i < 11; i++) {
+ for (i = 0; i < ps->num_ports; i++) {
ret = mv88e6131_setup_port(ds, i);
if (ret < 0)
return ret;
return 0;
}
-static int mv88e6131_port_to_phy_addr(int port)
+static int mv88e6131_port_to_phy_addr(struct dsa_switch *ds, int port)
{
- if (port >= 0 && port <= 11)
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+ if (port >= 0 && port < ps->num_ports)
return port;
- return -1;
+
+ return -EINVAL;
}
static int
mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum)
{
- int addr = mv88e6131_port_to_phy_addr(port);
+ int addr = mv88e6131_port_to_phy_addr(ds, port);
+
+ if (addr < 0)
+ return addr;
+
return mv88e6xxx_phy_read_ppu(ds, addr, regnum);
}
mv88e6131_phy_write(struct dsa_switch *ds,
int port, int regnum, u16 val)
{
- int addr = mv88e6131_port_to_phy_addr(port);
- return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
-}
-
-static struct mv88e6xxx_hw_stat mv88e6131_hw_stats[] = {
- { "in_good_octets", 8, 0x00, },
- { "in_bad_octets", 4, 0x02, },
- { "in_unicast", 4, 0x04, },
- { "in_broadcasts", 4, 0x06, },
- { "in_multicasts", 4, 0x07, },
- { "in_pause", 4, 0x16, },
- { "in_undersize", 4, 0x18, },
- { "in_fragments", 4, 0x19, },
- { "in_oversize", 4, 0x1a, },
- { "in_jabber", 4, 0x1b, },
- { "in_rx_error", 4, 0x1c, },
- { "in_fcs_error", 4, 0x1d, },
- { "out_octets", 8, 0x0e, },
- { "out_unicast", 4, 0x10, },
- { "out_broadcasts", 4, 0x13, },
- { "out_multicasts", 4, 0x12, },
- { "out_pause", 4, 0x15, },
- { "excessive", 4, 0x11, },
- { "collisions", 4, 0x1e, },
- { "deferred", 4, 0x05, },
- { "single", 4, 0x14, },
- { "multiple", 4, 0x17, },
- { "out_fcs_error", 4, 0x03, },
- { "late", 4, 0x1f, },
- { "hist_64bytes", 4, 0x08, },
- { "hist_65_127bytes", 4, 0x09, },
- { "hist_128_255bytes", 4, 0x0a, },
- { "hist_256_511bytes", 4, 0x0b, },
- { "hist_512_1023bytes", 4, 0x0c, },
- { "hist_1024_max_bytes", 4, 0x0d, },
-};
-
-static void
-mv88e6131_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
-{
- mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6131_hw_stats),
- mv88e6131_hw_stats, port, data);
-}
+ int addr = mv88e6131_port_to_phy_addr(ds, port);
-static void
-mv88e6131_get_ethtool_stats(struct dsa_switch *ds,
- int port, uint64_t *data)
-{
- mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6131_hw_stats),
- mv88e6131_hw_stats, port, data);
-}
+ if (addr < 0)
+ return addr;
-static int mv88e6131_get_sset_count(struct dsa_switch *ds)
-{
- return ARRAY_SIZE(mv88e6131_hw_stats);
+ return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
}
struct dsa_switch_driver mv88e6131_switch_driver = {
.phy_read = mv88e6131_phy_read,
.phy_write = mv88e6131_phy_write,
.poll_link = mv88e6xxx_poll_link,
- .get_strings = mv88e6131_get_strings,
- .get_ethtool_stats = mv88e6131_get_ethtool_stats,
- .get_sset_count = mv88e6131_get_sset_count,
+ .get_strings = mv88e6xxx_get_strings,
+ .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
+ .get_sset_count = mv88e6xxx_get_sset_count,
};
MODULE_ALIAS("platform:mv88e6085");
#include <net/dsa.h>
#include "mv88e6xxx.h"
-/* Switch product IDs */
-#define ID_6171 0x1710
-#define ID_6172 0x1720
-
static char *mv88e6171_probe(struct device *host_dev, int sw_addr)
{
struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
if (bus == NULL)
return NULL;
- ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
+ ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
if (ret >= 0) {
- if ((ret & 0xfff0) == ID_6171)
+ if ((ret & 0xfff0) == PORT_SWITCH_ID_6171)
return "Marvell 88E6171";
- if ((ret & 0xfff0) == ID_6172)
+ if ((ret & 0xfff0) == PORT_SWITCH_ID_6172)
return "Marvell 88E6172";
}
return NULL;
}
-static int mv88e6171_switch_reset(struct dsa_switch *ds)
-{
- int i;
- int ret;
- unsigned long timeout;
-
- /* Set all ports to the disabled state. */
- for (i = 0; i < 8; i++) {
- ret = REG_READ(REG_PORT(i), 0x04);
- REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
- }
-
- /* Wait for transmit queues to drain. */
- usleep_range(2000, 4000);
-
- /* Reset the switch. Keep PPU active. The PPU needs to be
- * active to support indirect phy register accesses through
- * global registers 0x18 and 0x19.
- */
- REG_WRITE(REG_GLOBAL, 0x04, 0xc000);
-
- /* Wait up to one second for reset to complete. */
- timeout = jiffies + 1 * HZ;
- while (time_before(jiffies, timeout)) {
- ret = REG_READ(REG_GLOBAL, 0x00);
- if ((ret & 0xc800) == 0xc800)
- break;
-
- usleep_range(1000, 2000);
- }
- if (time_after(jiffies, timeout))
- return -ETIMEDOUT;
-
- /* Enable ports not under DSA, e.g. WAN port */
- for (i = 0; i < 8; i++) {
- if (dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i))
- continue;
-
- ret = REG_READ(REG_PORT(i), 0x04);
- REG_WRITE(REG_PORT(i), 0x04, ret | 0x03);
- }
-
- return 0;
-}
-
static int mv88e6171_setup_global(struct dsa_switch *ds)
{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
int i;
}
/* Clear all trunk masks. */
- for (i = 0; i < 8; i++)
+ for (i = 0; i < ps->num_ports; i++)
REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff);
/* Clear all trunk mappings. */
static int mv88e6171_setup(struct dsa_switch *ds)
{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int i;
int ret;
if (ret < 0)
return ret;
- ret = mv88e6171_switch_reset(ds);
+ ps->num_ports = 7;
+
+ ret = mv88e6xxx_switch_reset(ds, true);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < ps->num_ports; i++) {
if (!(dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i)))
continue;
return 0;
}
-static int mv88e6171_port_to_phy_addr(int port)
-{
- if (port >= 0 && port <= 4)
- return port;
- return -1;
-}
-
-static int
-mv88e6171_phy_read(struct dsa_switch *ds, int port, int regnum)
-{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int addr = mv88e6171_port_to_phy_addr(port);
- int ret;
-
- mutex_lock(&ps->phy_mutex);
- ret = mv88e6xxx_phy_read_indirect(ds, addr, regnum);
- mutex_unlock(&ps->phy_mutex);
- return ret;
-}
-
-static int
-mv88e6171_phy_write(struct dsa_switch *ds,
- int port, int regnum, u16 val)
-{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int addr = mv88e6171_port_to_phy_addr(port);
- int ret;
-
- mutex_lock(&ps->phy_mutex);
- ret = mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
- mutex_unlock(&ps->phy_mutex);
- return ret;
-}
-
-static struct mv88e6xxx_hw_stat mv88e6171_hw_stats[] = {
- { "in_good_octets", 8, 0x00, },
- { "in_bad_octets", 4, 0x02, },
- { "in_unicast", 4, 0x04, },
- { "in_broadcasts", 4, 0x06, },
- { "in_multicasts", 4, 0x07, },
- { "in_pause", 4, 0x16, },
- { "in_undersize", 4, 0x18, },
- { "in_fragments", 4, 0x19, },
- { "in_oversize", 4, 0x1a, },
- { "in_jabber", 4, 0x1b, },
- { "in_rx_error", 4, 0x1c, },
- { "in_fcs_error", 4, 0x1d, },
- { "out_octets", 8, 0x0e, },
- { "out_unicast", 4, 0x10, },
- { "out_broadcasts", 4, 0x13, },
- { "out_multicasts", 4, 0x12, },
- { "out_pause", 4, 0x15, },
- { "excessive", 4, 0x11, },
- { "collisions", 4, 0x1e, },
- { "deferred", 4, 0x05, },
- { "single", 4, 0x14, },
- { "multiple", 4, 0x17, },
- { "out_fcs_error", 4, 0x03, },
- { "late", 4, 0x1f, },
- { "hist_64bytes", 4, 0x08, },
- { "hist_65_127bytes", 4, 0x09, },
- { "hist_128_255bytes", 4, 0x0a, },
- { "hist_256_511bytes", 4, 0x0b, },
- { "hist_512_1023bytes", 4, 0x0c, },
- { "hist_1024_max_bytes", 4, 0x0d, },
-};
-
-static void
-mv88e6171_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
-{
- mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6171_hw_stats),
- mv88e6171_hw_stats, port, data);
-}
-
-static void
-mv88e6171_get_ethtool_stats(struct dsa_switch *ds,
- int port, uint64_t *data)
-{
- mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6171_hw_stats),
- mv88e6171_hw_stats, port, data);
-}
-
-static int mv88e6171_get_sset_count(struct dsa_switch *ds)
-{
- return ARRAY_SIZE(mv88e6171_hw_stats);
-}
-
static int mv88e6171_get_eee(struct dsa_switch *ds, int port,
struct ethtool_eee *e)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- if (ps->id == ID_6172)
+ if (ps->id == PORT_SWITCH_ID_6172)
return mv88e6xxx_get_eee(ds, port, e);
return -EOPNOTSUPP;
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- if (ps->id == ID_6172)
+ if (ps->id == PORT_SWITCH_ID_6172)
return mv88e6xxx_set_eee(ds, port, phydev, e);
return -EOPNOTSUPP;
.probe = mv88e6171_probe,
.setup = mv88e6171_setup,
.set_addr = mv88e6xxx_set_addr_indirect,
- .phy_read = mv88e6171_phy_read,
- .phy_write = mv88e6171_phy_write,
+ .phy_read = mv88e6xxx_phy_read_indirect,
+ .phy_write = mv88e6xxx_phy_write_indirect,
.poll_link = mv88e6xxx_poll_link,
- .get_strings = mv88e6171_get_strings,
- .get_ethtool_stats = mv88e6171_get_ethtool_stats,
- .get_sset_count = mv88e6171_get_sset_count,
+ .get_strings = mv88e6xxx_get_strings,
+ .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
+ .get_sset_count = mv88e6xxx_get_sset_count,
.set_eee = mv88e6171_set_eee,
.get_eee = mv88e6171_get_eee,
#ifdef CONFIG_NET_DSA_HWMON
if (bus == NULL)
return NULL;
- ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
+ ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
if (ret >= 0) {
- if ((ret & 0xfff0) == 0x1760)
+ if ((ret & 0xfff0) == PORT_SWITCH_ID_6176)
return "Marvell 88E6176";
- if (ret == 0x3521)
+ if (ret == PORT_SWITCH_ID_6352_A0)
return "Marvell 88E6352 (A0)";
- if (ret == 0x3522)
+ if (ret == PORT_SWITCH_ID_6352_A1)
return "Marvell 88E6352 (A1)";
- if ((ret & 0xfff0) == 0x3520)
+ if ((ret & 0xfff0) == PORT_SWITCH_ID_6352)
return "Marvell 88E6352";
}
return NULL;
}
-static int mv88e6352_switch_reset(struct dsa_switch *ds)
-{
- unsigned long timeout;
- int ret;
- int i;
-
- /* Set all ports to the disabled state. */
- for (i = 0; i < 7; i++) {
- ret = REG_READ(REG_PORT(i), 0x04);
- REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
- }
-
- /* Wait for transmit queues to drain. */
- usleep_range(2000, 4000);
-
- /* Reset the switch. Keep PPU active (bit 14, undocumented).
- * The PPU needs to be active to support indirect phy register
- * accesses through global registers 0x18 and 0x19.
- */
- REG_WRITE(REG_GLOBAL, 0x04, 0xc000);
-
- /* Wait up to one second for reset to complete. */
- timeout = jiffies + 1 * HZ;
- while (time_before(jiffies, timeout)) {
- ret = REG_READ(REG_GLOBAL, 0x00);
- if ((ret & 0x8800) == 0x8800)
- break;
- usleep_range(1000, 2000);
- }
- if (time_after(jiffies, timeout))
- return -ETIMEDOUT;
-
- return 0;
-}
-
static int mv88e6352_setup_global(struct dsa_switch *ds)
{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
int i;
/* Disable ingress rate limiting by resetting all ingress
* rate limit registers to their initial state.
*/
- for (i = 0; i < 7; i++)
+ for (i = 0; i < ps->num_ports; i++)
REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8));
/* Initialise cross-chip port VLAN table to reset defaults. */
#ifdef CONFIG_NET_DSA_HWMON
-static int mv88e6352_phy_page_read(struct dsa_switch *ds,
- int port, int page, int reg)
-{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int ret;
-
- mutex_lock(&ps->phy_mutex);
- ret = mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
- if (ret < 0)
- goto error;
- ret = mv88e6xxx_phy_read_indirect(ds, port, reg);
-error:
- mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
- mutex_unlock(&ps->phy_mutex);
- return ret;
-}
-
-static int mv88e6352_phy_page_write(struct dsa_switch *ds,
- int port, int page, int reg, int val)
-{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int ret;
-
- mutex_lock(&ps->phy_mutex);
- ret = mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
- if (ret < 0)
- goto error;
-
- ret = mv88e6xxx_phy_write_indirect(ds, port, reg, val);
-error:
- mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
- mutex_unlock(&ps->phy_mutex);
- return ret;
-}
-
static int mv88e6352_get_temp(struct dsa_switch *ds, int *temp)
{
int ret;
*temp = 0;
- ret = mv88e6352_phy_page_read(ds, 0, 6, 27);
+ ret = mv88e6xxx_phy_page_read(ds, 0, 6, 27);
if (ret < 0)
return ret;
*temp = 0;
- ret = mv88e6352_phy_page_read(ds, 0, 6, 26);
+ ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
if (ret < 0)
return ret;
{
int ret;
- ret = mv88e6352_phy_page_read(ds, 0, 6, 26);
+ ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
if (ret < 0)
return ret;
temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
- return mv88e6352_phy_page_write(ds, 0, 6, 26,
+ return mv88e6xxx_phy_page_write(ds, 0, 6, 26,
(ret & 0xe0ff) | (temp << 8));
}
*alarm = false;
- ret = mv88e6352_phy_page_read(ds, 0, 6, 26);
+ ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
+ ps->num_ports = 7;
+
mutex_init(&ps->eeprom_mutex);
- ret = mv88e6352_switch_reset(ds);
+ ret = mv88e6xxx_switch_reset(ds, true);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
- for (i = 0; i < 7; i++) {
+ for (i = 0; i < ps->num_ports; i++) {
ret = mv88e6352_setup_port(ds, i);
if (ret < 0)
return ret;
return 0;
}
-static int mv88e6352_port_to_phy_addr(int port)
-{
- if (port >= 0 && port <= 4)
- return port;
- return -EINVAL;
-}
-
-static int
-mv88e6352_phy_read(struct dsa_switch *ds, int port, int regnum)
-{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int addr = mv88e6352_port_to_phy_addr(port);
- int ret;
-
- if (addr < 0)
- return addr;
-
- mutex_lock(&ps->phy_mutex);
- ret = mv88e6xxx_phy_read_indirect(ds, addr, regnum);
- mutex_unlock(&ps->phy_mutex);
-
- return ret;
-}
-
-static int
-mv88e6352_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
-{
- struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
- int addr = mv88e6352_port_to_phy_addr(port);
- int ret;
-
- if (addr < 0)
- return addr;
-
- mutex_lock(&ps->phy_mutex);
- ret = mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
- mutex_unlock(&ps->phy_mutex);
-
- return ret;
-}
-
-static struct mv88e6xxx_hw_stat mv88e6352_hw_stats[] = {
- { "in_good_octets", 8, 0x00, },
- { "in_bad_octets", 4, 0x02, },
- { "in_unicast", 4, 0x04, },
- { "in_broadcasts", 4, 0x06, },
- { "in_multicasts", 4, 0x07, },
- { "in_pause", 4, 0x16, },
- { "in_undersize", 4, 0x18, },
- { "in_fragments", 4, 0x19, },
- { "in_oversize", 4, 0x1a, },
- { "in_jabber", 4, 0x1b, },
- { "in_rx_error", 4, 0x1c, },
- { "in_fcs_error", 4, 0x1d, },
- { "out_octets", 8, 0x0e, },
- { "out_unicast", 4, 0x10, },
- { "out_broadcasts", 4, 0x13, },
- { "out_multicasts", 4, 0x12, },
- { "out_pause", 4, 0x15, },
- { "excessive", 4, 0x11, },
- { "collisions", 4, 0x1e, },
- { "deferred", 4, 0x05, },
- { "single", 4, 0x14, },
- { "multiple", 4, 0x17, },
- { "out_fcs_error", 4, 0x03, },
- { "late", 4, 0x1f, },
- { "hist_64bytes", 4, 0x08, },
- { "hist_65_127bytes", 4, 0x09, },
- { "hist_128_255bytes", 4, 0x0a, },
- { "hist_256_511bytes", 4, 0x0b, },
- { "hist_512_1023bytes", 4, 0x0c, },
- { "hist_1024_max_bytes", 4, 0x0d, },
- { "sw_in_discards", 4, 0x110, },
- { "sw_in_filtered", 2, 0x112, },
- { "sw_out_filtered", 2, 0x113, },
-};
-
static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
return 0;
}
-static void
-mv88e6352_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
-{
- mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6352_hw_stats),
- mv88e6352_hw_stats, port, data);
-}
-
-static void
-mv88e6352_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
-{
- mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6352_hw_stats),
- mv88e6352_hw_stats, port, data);
-}
-
-static int mv88e6352_get_sset_count(struct dsa_switch *ds)
-{
- return ARRAY_SIZE(mv88e6352_hw_stats);
-}
-
struct dsa_switch_driver mv88e6352_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_EDSA,
.priv_size = sizeof(struct mv88e6xxx_priv_state),
.probe = mv88e6352_probe,
.setup = mv88e6352_setup,
.set_addr = mv88e6xxx_set_addr_indirect,
- .phy_read = mv88e6352_phy_read,
- .phy_write = mv88e6352_phy_write,
+ .phy_read = mv88e6xxx_phy_read_indirect,
+ .phy_write = mv88e6xxx_phy_write_indirect,
.poll_link = mv88e6xxx_poll_link,
- .get_strings = mv88e6352_get_strings,
- .get_ethtool_stats = mv88e6352_get_ethtool_stats,
- .get_sset_count = mv88e6352_get_sset_count,
+ .get_strings = mv88e6xxx_get_strings,
+ .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
+ .get_sset_count = mv88e6xxx_get_sset_count,
.set_eee = mv88e6xxx_set_eee,
.get_eee = mv88e6xxx_get_eee,
#ifdef CONFIG_NET_DSA_HWMON
int i;
for (i = 0; i < 16; i++) {
- ret = mdiobus_read(bus, sw_addr, 0);
+ ret = mdiobus_read(bus, sw_addr, SMI_CMD);
if (ret < 0)
return ret;
- if ((ret & 0x8000) == 0)
+ if ((ret & SMI_CMD_BUSY) == 0)
return 0;
}
return ret;
/* Transmit the read command. */
- ret = mdiobus_write(bus, sw_addr, 0, 0x9800 | (addr << 5) | reg);
+ ret = mdiobus_write(bus, sw_addr, SMI_CMD,
+ SMI_CMD_OP_22_READ | (addr << 5) | reg);
if (ret < 0)
return ret;
return ret;
/* Read the data. */
- ret = mdiobus_read(bus, sw_addr, 1);
+ ret = mdiobus_read(bus, sw_addr, SMI_DATA);
if (ret < 0)
return ret;
return ret;
/* Transmit the data to write. */
- ret = mdiobus_write(bus, sw_addr, 1, val);
+ ret = mdiobus_write(bus, sw_addr, SMI_DATA, val);
if (ret < 0)
return ret;
/* Transmit the write command. */
- ret = mdiobus_write(bus, sw_addr, 0, 0x9400 | (addr << 5) | reg);
+ ret = mdiobus_write(bus, sw_addr, SMI_CMD,
+ SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
if (ret < 0)
return ret;
int mv88e6xxx_config_prio(struct dsa_switch *ds)
{
/* Configure the IP ToS mapping registers. */
- REG_WRITE(REG_GLOBAL, 0x10, 0x0000);
- REG_WRITE(REG_GLOBAL, 0x11, 0x0000);
- REG_WRITE(REG_GLOBAL, 0x12, 0x5555);
- REG_WRITE(REG_GLOBAL, 0x13, 0x5555);
- REG_WRITE(REG_GLOBAL, 0x14, 0xaaaa);
- REG_WRITE(REG_GLOBAL, 0x15, 0xaaaa);
- REG_WRITE(REG_GLOBAL, 0x16, 0xffff);
- REG_WRITE(REG_GLOBAL, 0x17, 0xffff);
+ REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
+ REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
+ REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
+ REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
+ REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
+ REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
+ REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
+ REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
/* Configure the IEEE 802.1p priority mapping register. */
- REG_WRITE(REG_GLOBAL, 0x18, 0xfa41);
+ REG_WRITE(REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
return 0;
}
int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
{
- REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]);
- REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]);
- REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]);
+ REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
+ REG_WRITE(REG_GLOBAL, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
+ REG_WRITE(REG_GLOBAL, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
return 0;
}
int j;
/* Write the MAC address byte. */
- REG_WRITE(REG_GLOBAL2, 0x0d, 0x8000 | (i << 8) | addr[i]);
+ REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
+ GLOBAL2_SWITCH_MAC_BUSY | (i << 8) | addr[i]);
/* Wait for the write to complete. */
for (j = 0; j < 16; j++) {
- ret = REG_READ(REG_GLOBAL2, 0x0d);
- if ((ret & 0x8000) == 0)
+ ret = REG_READ(REG_GLOBAL2, GLOBAL2_SWITCH_MAC);
+ if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
break;
}
if (j == 16)
return 0;
}
-int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
+/* Must be called with phy mutex held */
+static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
{
if (addr >= 0)
return mv88e6xxx_reg_read(ds, addr, regnum);
return 0xffff;
}
-int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val)
+/* Must be called with phy mutex held */
+static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum,
+ u16 val)
{
if (addr >= 0)
return mv88e6xxx_reg_write(ds, addr, regnum, val);
int ret;
unsigned long timeout;
- ret = REG_READ(REG_GLOBAL, 0x04);
- REG_WRITE(REG_GLOBAL, 0x04, ret & ~0x4000);
+ ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL);
+ REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL,
+ ret & ~GLOBAL_CONTROL_PPU_ENABLE);
timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) {
- ret = REG_READ(REG_GLOBAL, 0x00);
+ ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS);
usleep_range(1000, 2000);
- if ((ret & 0xc000) != 0xc000)
+ if ((ret & GLOBAL_STATUS_PPU_MASK) !=
+ GLOBAL_STATUS_PPU_POLLING)
return 0;
}
int ret;
unsigned long timeout;
- ret = REG_READ(REG_GLOBAL, 0x04);
- REG_WRITE(REG_GLOBAL, 0x04, ret | 0x4000);
+ ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL);
+ REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, ret | GLOBAL_CONTROL_PPU_ENABLE);
timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) {
- ret = REG_READ(REG_GLOBAL, 0x00);
+ ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS);
usleep_range(1000, 2000);
- if ((ret & 0xc000) == 0xc000)
+ if ((ret & GLOBAL_STATUS_PPU_MASK) ==
+ GLOBAL_STATUS_PPU_POLLING)
return 0;
}
link = 0;
if (dev->flags & IFF_UP) {
- port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), 0x00);
+ port_status = mv88e6xxx_reg_read(ds, REG_PORT(i),
+ PORT_STATUS);
if (port_status < 0)
continue;
- link = !!(port_status & 0x0800);
+ link = !!(port_status & PORT_STATUS_LINK);
}
if (!link) {
continue;
}
- switch (port_status & 0x0300) {
- case 0x0000:
+ switch (port_status & PORT_STATUS_SPEED_MASK) {
+ case PORT_STATUS_SPEED_10:
speed = 10;
break;
- case 0x0100:
+ case PORT_STATUS_SPEED_100:
speed = 100;
break;
- case 0x0200:
+ case PORT_STATUS_SPEED_1000:
speed = 1000;
break;
default:
speed = -1;
break;
}
- duplex = (port_status & 0x0400) ? 1 : 0;
- fc = (port_status & 0x8000) ? 1 : 0;
+ duplex = (port_status & PORT_STATUS_DUPLEX) ? 1 : 0;
+ fc = (port_status & PORT_STATUS_PAUSE_EN) ? 1 : 0;
if (!netif_carrier_ok(dev)) {
netdev_info(dev,
}
}
+static bool mv88e6xxx_6352_family(struct dsa_switch *ds)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+ switch (ps->id) {
+ case PORT_SWITCH_ID_6352:
+ case PORT_SWITCH_ID_6172:
+ case PORT_SWITCH_ID_6176:
+ return true;
+ }
+ return false;
+}
+
static int mv88e6xxx_stats_wait(struct dsa_switch *ds)
{
int ret;
int i;
for (i = 0; i < 10; i++) {
- ret = REG_READ(REG_GLOBAL, 0x1d);
- if ((ret & 0x8000) == 0)
+ ret = REG_READ(REG_GLOBAL, GLOBAL_STATS_OP);
+ if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
return 0;
}
{
int ret;
+ if (mv88e6xxx_6352_family(ds))
+ port = (port + 1) << 5;
+
/* Snapshot the hardware statistics counters for this port. */
- REG_WRITE(REG_GLOBAL, 0x1d, 0xdc00 | port);
+ REG_WRITE(REG_GLOBAL, GLOBAL_STATS_OP,
+ GLOBAL_STATS_OP_CAPTURE_PORT |
+ GLOBAL_STATS_OP_HIST_RX_TX | port);
/* Wait for the snapshotting to complete. */
ret = mv88e6xxx_stats_wait(ds);
*val = 0;
- ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1d, 0xcc00 | stat);
+ ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
+ GLOBAL_STATS_OP_READ_CAPTURED |
+ GLOBAL_STATS_OP_HIST_RX_TX | stat);
if (ret < 0)
return;
if (ret < 0)
return;
- ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1e);
+ ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
if (ret < 0)
return;
_val = ret << 16;
- ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1f);
+ ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
if (ret < 0)
return;
*val = _val | ret;
}
-void mv88e6xxx_get_strings(struct dsa_switch *ds,
- int nr_stats, struct mv88e6xxx_hw_stat *stats,
- int port, uint8_t *data)
+static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
+ { "in_good_octets", 8, 0x00, },
+ { "in_bad_octets", 4, 0x02, },
+ { "in_unicast", 4, 0x04, },
+ { "in_broadcasts", 4, 0x06, },
+ { "in_multicasts", 4, 0x07, },
+ { "in_pause", 4, 0x16, },
+ { "in_undersize", 4, 0x18, },
+ { "in_fragments", 4, 0x19, },
+ { "in_oversize", 4, 0x1a, },
+ { "in_jabber", 4, 0x1b, },
+ { "in_rx_error", 4, 0x1c, },
+ { "in_fcs_error", 4, 0x1d, },
+ { "out_octets", 8, 0x0e, },
+ { "out_unicast", 4, 0x10, },
+ { "out_broadcasts", 4, 0x13, },
+ { "out_multicasts", 4, 0x12, },
+ { "out_pause", 4, 0x15, },
+ { "excessive", 4, 0x11, },
+ { "collisions", 4, 0x1e, },
+ { "deferred", 4, 0x05, },
+ { "single", 4, 0x14, },
+ { "multiple", 4, 0x17, },
+ { "out_fcs_error", 4, 0x03, },
+ { "late", 4, 0x1f, },
+ { "hist_64bytes", 4, 0x08, },
+ { "hist_65_127bytes", 4, 0x09, },
+ { "hist_128_255bytes", 4, 0x0a, },
+ { "hist_256_511bytes", 4, 0x0b, },
+ { "hist_512_1023bytes", 4, 0x0c, },
+ { "hist_1024_max_bytes", 4, 0x0d, },
+ /* Not all devices have the following counters */
+ { "sw_in_discards", 4, 0x110, },
+ { "sw_in_filtered", 2, 0x112, },
+ { "sw_out_filtered", 2, 0x113, },
+
+};
+
+static bool have_sw_in_discards(struct dsa_switch *ds)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+ switch (ps->id) {
+ case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161:
+ case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171:
+ case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176:
+ case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185:
+ case PORT_SWITCH_ID_6352:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void _mv88e6xxx_get_strings(struct dsa_switch *ds,
+ int nr_stats,
+ struct mv88e6xxx_hw_stat *stats,
+ int port, uint8_t *data)
{
int i;
}
}
-void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
- int nr_stats, struct mv88e6xxx_hw_stat *stats,
- int port, uint64_t *data)
+static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
+ int nr_stats,
+ struct mv88e6xxx_hw_stat *stats,
+ int port, uint64_t *data)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
mutex_unlock(&ps->stats_mutex);
}
+/* All the statistics in the table */
+void
+mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+{
+ if (have_sw_in_discards(ds))
+ _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
+ mv88e6xxx_hw_stats, port, data);
+ else
+ _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
+ mv88e6xxx_hw_stats, port, data);
+}
+
+int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
+{
+ if (have_sw_in_discards(ds))
+ return ARRAY_SIZE(mv88e6xxx_hw_stats);
+ return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
+}
+
+void
+mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
+ int port, uint64_t *data)
+{
+ if (have_sw_in_discards(ds))
+ _mv88e6xxx_get_ethtool_stats(
+ ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
+ mv88e6xxx_hw_stats, port, data);
+ else
+ _mv88e6xxx_get_ethtool_stats(
+ ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
+ mv88e6xxx_hw_stats, port, data);
+}
+
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
{
return 32 * sizeof(u16);
mutex_lock(&ps->phy_mutex);
- ret = mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6);
+ ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6);
if (ret < 0)
goto error;
/* Enable temperature sensor */
- ret = mv88e6xxx_phy_read(ds, 0x0, 0x1a);
+ ret = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
if (ret < 0)
goto error;
- ret = mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5));
+ ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5));
if (ret < 0)
goto error;
/* Wait for temperature to stabilize */
usleep_range(10000, 12000);
- val = mv88e6xxx_phy_read(ds, 0x0, 0x1a);
+ val = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
if (val < 0) {
ret = val;
goto error;
}
/* Disable temperature sensor */
- ret = mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5));
+ ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5));
if (ret < 0)
goto error;
*temp = ((val & 0x1f) - 5) * 5;
error:
- mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0);
+ _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0);
mutex_unlock(&ps->phy_mutex);
return ret;
}
int mv88e6xxx_phy_wait(struct dsa_switch *ds)
{
- return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x18, 0x8000);
+ return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
+ GLOBAL2_SMI_OP_BUSY);
}
int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
{
- return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x0800);
+ return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
+ GLOBAL2_EEPROM_OP_LOAD);
}
int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
{
- return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x8000);
+ return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
+ GLOBAL2_EEPROM_OP_BUSY);
}
/* Must be called with SMI lock held */
/* Must be called with SMI lock held */
static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
{
- return _mv88e6xxx_wait(ds, REG_GLOBAL, 0x0b, ATU_BUSY);
+ return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP,
+ GLOBAL_ATU_OP_BUSY);
}
-int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum)
+/* Must be called with phy mutex held */
+static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
+ int regnum)
{
int ret;
- REG_WRITE(REG_GLOBAL2, 0x18, 0x9800 | (addr << 5) | regnum);
+ REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP,
+ GLOBAL2_SMI_OP_22_READ | (addr << 5) | regnum);
ret = mv88e6xxx_phy_wait(ds);
if (ret < 0)
return ret;
- return REG_READ(REG_GLOBAL2, 0x19);
+ return REG_READ(REG_GLOBAL2, GLOBAL2_SMI_DATA);
}
-int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum,
- u16 val)
+/* Must be called with phy mutex held */
+static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr,
+ int regnum, u16 val)
{
- REG_WRITE(REG_GLOBAL2, 0x19, val);
- REG_WRITE(REG_GLOBAL2, 0x18, 0x9400 | (addr << 5) | regnum);
+ REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
+ REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP,
+ GLOBAL2_SMI_OP_22_WRITE | (addr << 5) | regnum);
return mv88e6xxx_phy_wait(ds);
}
int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int reg;
- reg = mv88e6xxx_phy_read_indirect(ds, port, 16);
+ mutex_lock(&ps->phy_mutex);
+
+ reg = _mv88e6xxx_phy_read_indirect(ds, port, 16);
if (reg < 0)
- return -EOPNOTSUPP;
+ goto out;
e->eee_enabled = !!(reg & 0x0200);
e->tx_lpi_enabled = !!(reg & 0x0100);
- reg = REG_READ(REG_PORT(port), 0);
- e->eee_active = !!(reg & 0x0040);
-
- return 0;
-}
-
-static int mv88e6xxx_eee_enable_set(struct dsa_switch *ds, int port,
- bool eee_enabled, bool tx_lpi_enabled)
-{
- int reg, nreg;
-
- reg = mv88e6xxx_phy_read_indirect(ds, port, 16);
+ reg = mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
if (reg < 0)
- return reg;
-
- nreg = reg & ~0x0300;
- if (eee_enabled)
- nreg |= 0x0200;
- if (tx_lpi_enabled)
- nreg |= 0x0100;
+ goto out;
- if (nreg != reg)
- return mv88e6xxx_phy_write_indirect(ds, port, 16, nreg);
+ e->eee_active = !!(reg & PORT_STATUS_EEE);
+ reg = 0;
- return 0;
+out:
+ mutex_unlock(&ps->phy_mutex);
+ return reg;
}
int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
struct phy_device *phydev, struct ethtool_eee *e)
{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ int reg;
int ret;
- ret = mv88e6xxx_eee_enable_set(ds, port, e->eee_enabled,
- e->tx_lpi_enabled);
- if (ret)
- return -EOPNOTSUPP;
+ mutex_lock(&ps->phy_mutex);
- return 0;
+ ret = _mv88e6xxx_phy_read_indirect(ds, port, 16);
+ if (ret < 0)
+ goto out;
+
+ reg = ret & ~0x0300;
+ if (e->eee_enabled)
+ reg |= 0x0200;
+ if (e->tx_lpi_enabled)
+ reg |= 0x0100;
+
+ ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg);
+out:
+ mutex_unlock(&ps->phy_mutex);
+
+ return ret;
}
static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
if (ret < 0)
return ret;
- ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0b, cmd);
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
if (ret < 0)
return ret;
if (ret < 0)
return ret;
- return _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_FLUSH_NONSTATIC_FID);
+ return _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB);
}
static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
mutex_lock(&ps->smi_mutex);
- reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), 0x04);
+ reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL);
if (reg < 0)
goto abort;
- oldstate = reg & PSTATE_MASK;
+ oldstate = reg & PORT_CONTROL_STATE_MASK;
if (oldstate != state) {
/* Flush forwarding database if we're moving a port
* from Learning or Forwarding state to Disabled or
* Blocking or Listening state.
*/
- if (oldstate >= PSTATE_LEARNING && state <= PSTATE_BLOCKING) {
+ if (oldstate >= PORT_CONTROL_STATE_LEARNING &&
+ state <= PORT_CONTROL_STATE_BLOCKING) {
ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]);
if (ret)
goto abort;
}
- reg = (reg & ~PSTATE_MASK) | state;
- ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x04, reg);
+ reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
+ ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL,
+ reg);
}
abort:
reg |= (ps->bridge_mask[fid] |
(1 << dsa_upstream_port(ds))) & ~(1 << port);
- return _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x06, reg);
+ return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg);
}
/* Must be called with smi lock held */
switch (state) {
case BR_STATE_DISABLED:
- stp_state = PSTATE_DISABLED;
+ stp_state = PORT_CONTROL_STATE_DISABLED;
break;
case BR_STATE_BLOCKING:
case BR_STATE_LISTENING:
- stp_state = PSTATE_BLOCKING;
+ stp_state = PORT_CONTROL_STATE_BLOCKING;
break;
case BR_STATE_LEARNING:
- stp_state = PSTATE_LEARNING;
+ stp_state = PORT_CONTROL_STATE_LEARNING;
break;
case BR_STATE_FORWARDING:
default:
- stp_state = PSTATE_FORWARDING;
+ stp_state = PORT_CONTROL_STATE_FORWARDING;
break;
}
int i, ret;
for (i = 0; i < 3; i++) {
- ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0d + i,
- (addr[i * 2] << 8) | addr[i * 2 + 1]);
+ ret = _mv88e6xxx_reg_write(
+ ds, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
+ (addr[i * 2] << 8) | addr[i * 2 + 1]);
if (ret < 0)
return ret;
}
int i, ret;
for (i = 0; i < 3; i++) {
- ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0d + i);
+ ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
+ GLOBAL_ATU_MAC_01 + i);
if (ret < 0)
return ret;
addr[i * 2] = ret >> 8;
if (ret < 0)
return ret;
- ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0c,
+ ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA,
(0x10 << port) | state);
if (ret)
return ret;
- ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_LOAD_FID);
+ ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_LOAD_DB);
return ret;
}
const unsigned char *addr, u16 vid)
{
int state = is_multicast_ether_addr(addr) ?
- FDB_STATE_MC_STATIC : FDB_STATE_STATIC;
+ GLOBAL_ATU_DATA_STATE_MC_STATIC :
+ GLOBAL_ATU_DATA_STATE_UC_STATIC;
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
int ret;
mutex_lock(&ps->smi_mutex);
- ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, FDB_STATE_UNUSED);
+ ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr,
+ GLOBAL_ATU_DATA_STATE_UNUSED);
mutex_unlock(&ps->smi_mutex);
return ret;
return ret;
do {
- ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_GETNEXT_FID);
+ ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
if (ret < 0)
return ret;
- ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0c);
+ ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
if (ret < 0)
return ret;
- state = ret & FDB_STATE_MASK;
- if (state == FDB_STATE_UNUSED)
+ state = ret & GLOBAL_ATU_DATA_STATE_MASK;
+ if (state == GLOBAL_ATU_DATA_STATE_UNUSED)
return -ENOENT;
} while (!(((ret >> 4) & 0xff) & (1 << port)));
return ret;
*is_static = state == (is_multicast_ether_addr(addr) ?
- FDB_STATE_MC_STATIC : FDB_STATE_STATIC);
+ GLOBAL_ATU_DATA_STATE_MC_STATIC :
+ GLOBAL_ATU_DATA_STATE_UC_STATIC);
return 0;
}
/* Port Control 1: disable trunking, disable sending
* learning messages to this port.
*/
- ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x05, 0x0000);
+ ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
+ 0x0000);
if (ret)
goto abort;
mutex_init(&ps->stats_mutex);
mutex_init(&ps->phy_mutex);
- ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0;
+ ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0;
ps->fid_mask = (1 << DSA_MAX_PORTS) - 1;
return 0;
}
+int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
+ unsigned long timeout;
+ int ret;
+ int i;
+
+ /* Set all ports to the disabled state. */
+ for (i = 0; i < ps->num_ports; i++) {
+ ret = REG_READ(REG_PORT(i), PORT_CONTROL);
+ REG_WRITE(REG_PORT(i), PORT_CONTROL, ret & 0xfffc);
+ }
+
+ /* Wait for transmit queues to drain. */
+ usleep_range(2000, 4000);
+
+ /* Reset the switch. Keep the PPU active if requested. The PPU
+ * needs to be active to support indirect phy register access
+ * through global registers 0x18 and 0x19.
+ */
+ if (ppu_active)
+ REG_WRITE(REG_GLOBAL, 0x04, 0xc000);
+ else
+ REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
+
+ /* Wait up to one second for reset to complete. */
+ timeout = jiffies + 1 * HZ;
+ while (time_before(jiffies, timeout)) {
+ ret = REG_READ(REG_GLOBAL, 0x00);
+ if ((ret & is_reset) == is_reset)
+ break;
+ usleep_range(1000, 2000);
+ }
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ int ret;
+
+ mutex_lock(&ps->phy_mutex);
+ ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
+ if (ret < 0)
+ goto error;
+ ret = _mv88e6xxx_phy_read_indirect(ds, port, reg);
+error:
+ _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
+ mutex_unlock(&ps->phy_mutex);
+ return ret;
+}
+
+int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
+ int reg, int val)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ int ret;
+
+ mutex_lock(&ps->phy_mutex);
+ ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
+ if (ret < 0)
+ goto error;
+
+ ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val);
+error:
+ _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
+ mutex_unlock(&ps->phy_mutex);
+ return ret;
+}
+
+static int mv88e6xxx_port_to_phy_addr(struct dsa_switch *ds, int port)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+
+ if (port >= 0 && port < ps->num_ports)
+ return port;
+ return -EINVAL;
+}
+
+int
+mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ int addr = mv88e6xxx_port_to_phy_addr(ds, port);
+ int ret;
+
+ if (addr < 0)
+ return addr;
+
+ mutex_lock(&ps->phy_mutex);
+ ret = _mv88e6xxx_phy_read(ds, addr, regnum);
+ mutex_unlock(&ps->phy_mutex);
+ return ret;
+}
+
+int
+mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ int addr = mv88e6xxx_port_to_phy_addr(ds, port);
+ int ret;
+
+ if (addr < 0)
+ return addr;
+
+ mutex_lock(&ps->phy_mutex);
+ ret = _mv88e6xxx_phy_write(ds, addr, regnum, val);
+ mutex_unlock(&ps->phy_mutex);
+ return ret;
+}
+
+int
+mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ int addr = mv88e6xxx_port_to_phy_addr(ds, port);
+ int ret;
+
+ if (addr < 0)
+ return addr;
+
+ mutex_lock(&ps->phy_mutex);
+ ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum);
+ mutex_unlock(&ps->phy_mutex);
+ return ret;
+}
+
+int
+mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
+ u16 val)
+{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
+ int addr = mv88e6xxx_port_to_phy_addr(ds, port);
+ int ret;
+
+ if (addr < 0)
+ return addr;
+
+ mutex_lock(&ps->phy_mutex);
+ ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
+ mutex_unlock(&ps->phy_mutex);
+ return ret;
+}
+
static int __init mv88e6xxx_init(void)
{
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
#ifndef __MV88E6XXX_H
#define __MV88E6XXX_H
-#define REG_PORT(p) (0x10 + (p))
-#define REG_GLOBAL 0x1b
-#define REG_GLOBAL2 0x1c
-
-/* ATU commands */
-
-#define ATU_BUSY 0x8000
-
-#define ATU_CMD_LOAD_FID (ATU_BUSY | 0x3000)
-#define ATU_CMD_GETNEXT_FID (ATU_BUSY | 0x4000)
-#define ATU_CMD_FLUSH_NONSTATIC_FID (ATU_BUSY | 0x6000)
-
-/* port states */
+#define SMI_CMD 0x00
+#define SMI_CMD_BUSY BIT(15)
+#define SMI_CMD_CLAUSE_22 BIT(12)
+#define SMI_CMD_OP_22_WRITE ((1 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
+#define SMI_CMD_OP_22_READ ((2 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
+#define SMI_CMD_OP_45_WRITE_ADDR ((0 << 10) | SMI_CMD_BUSY)
+#define SMI_CMD_OP_45_WRITE_DATA ((1 << 10) | SMI_CMD_BUSY)
+#define SMI_CMD_OP_45_READ_DATA ((2 << 10) | SMI_CMD_BUSY)
+#define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY)
+#define SMI_DATA 0x01
-#define PSTATE_MASK 0x03
-#define PSTATE_DISABLED 0x00
-#define PSTATE_BLOCKING 0x01
-#define PSTATE_LEARNING 0x02
-#define PSTATE_FORWARDING 0x03
-
-/* FDB states */
+#define REG_PORT(p) (0x10 + (p))
+#define PORT_STATUS 0x00
+#define PORT_STATUS_PAUSE_EN BIT(15)
+#define PORT_STATUS_MY_PAUSE BIT(14)
+#define PORT_STATUS_HD_FLOW BIT(13)
+#define PORT_STATUS_PHY_DETECT BIT(12)
+#define PORT_STATUS_LINK BIT(11)
+#define PORT_STATUS_DUPLEX BIT(10)
+#define PORT_STATUS_SPEED_MASK 0x0300
+#define PORT_STATUS_SPEED_10 0x0000
+#define PORT_STATUS_SPEED_100 0x0100
+#define PORT_STATUS_SPEED_1000 0x0200
+#define PORT_STATUS_EEE BIT(6) /* 6352 */
+#define PORT_STATUS_AM_DIS BIT(6) /* 6165 */
+#define PORT_STATUS_MGMII BIT(6) /* 6185 */
+#define PORT_STATUS_TX_PAUSED BIT(5)
+#define PORT_STATUS_FLOW_CTRL BIT(4)
+#define PORT_PCS_CTRL 0x01
+#define PORT_SWITCH_ID 0x03
+#define PORT_SWITCH_ID_6085 0x04a0
+#define PORT_SWITCH_ID_6095 0x0950
+#define PORT_SWITCH_ID_6123 0x1210
+#define PORT_SWITCH_ID_6123_A1 0x1212
+#define PORT_SWITCH_ID_6123_A2 0x1213
+#define PORT_SWITCH_ID_6131 0x1060
+#define PORT_SWITCH_ID_6131_B2 0x1066
+#define PORT_SWITCH_ID_6152 0x1a40
+#define PORT_SWITCH_ID_6155 0x1a50
+#define PORT_SWITCH_ID_6161 0x1610
+#define PORT_SWITCH_ID_6161_A1 0x1612
+#define PORT_SWITCH_ID_6161_A2 0x1613
+#define PORT_SWITCH_ID_6165 0x1650
+#define PORT_SWITCH_ID_6165_A1 0x1652
+#define PORT_SWITCH_ID_6165_A2 0x1653
+#define PORT_SWITCH_ID_6171 0x1710
+#define PORT_SWITCH_ID_6172 0x1720
+#define PORT_SWITCH_ID_6176 0x1760
+#define PORT_SWITCH_ID_6182 0x1a60
+#define PORT_SWITCH_ID_6185 0x1a70
+#define PORT_SWITCH_ID_6352 0x3520
+#define PORT_SWITCH_ID_6352_A0 0x3521
+#define PORT_SWITCH_ID_6352_A1 0x3522
+#define PORT_CONTROL 0x04
+#define PORT_CONTROL_STATE_MASK 0x03
+#define PORT_CONTROL_STATE_DISABLED 0x00
+#define PORT_CONTROL_STATE_BLOCKING 0x01
+#define PORT_CONTROL_STATE_LEARNING 0x02
+#define PORT_CONTROL_STATE_FORWARDING 0x03
+#define PORT_CONTROL_1 0x05
+#define PORT_BASE_VLAN 0x06
+#define PORT_DEFAULT_VLAN 0x07
+#define PORT_CONTROL_2 0x08
+#define PORT_RATE_CONTROL 0x09
+#define PORT_RATE_CONTROL_2 0x0a
+#define PORT_ASSOC_VECTOR 0x0b
+#define PORT_IN_DISCARD_LO 0x10
+#define PORT_IN_DISCARD_HI 0x11
+#define PORT_IN_FILTERED 0x12
+#define PORT_OUT_FILTERED 0x13
+#define PORT_TAG_REGMAP_0123 0x19
+#define PORT_TAG_REGMAP_4567 0x1a
-#define FDB_STATE_MASK 0x0f
+#define REG_GLOBAL 0x1b
+#define GLOBAL_STATUS 0x00
+#define GLOBAL_STATUS_PPU_STATE BIT(15) /* 6351 and 6171 */
+/* Two bits for 6165, 6185 etc */
+#define GLOBAL_STATUS_PPU_MASK (0x3 << 14)
+#define GLOBAL_STATUS_PPU_DISABLED_RST (0x0 << 14)
+#define GLOBAL_STATUS_PPU_INITIALIZING (0x1 << 14)
+#define GLOBAL_STATUS_PPU_DISABLED (0x2 << 14)
+#define GLOBAL_STATUS_PPU_POLLING (0x3 << 14)
+#define GLOBAL_MAC_01 0x01
+#define GLOBAL_MAC_23 0x02
+#define GLOBAL_MAC_45 0x03
+#define GLOBAL_CONTROL 0x04
+#define GLOBAL_CONTROL_SW_RESET BIT(15)
+#define GLOBAL_CONTROL_PPU_ENABLE BIT(14)
+#define GLOBAL_CONTROL_DISCARD_EXCESS BIT(13) /* 6352 */
+#define GLOBAL_CONTROL_SCHED_PRIO BIT(11) /* 6152 */
+#define GLOBAL_CONTROL_MAX_FRAME_1632 BIT(10) /* 6152 */
+#define GLOBAL_CONTROL_RELOAD_EEPROM BIT(9) /* 6152 */
+#define GLOBAL_CONTROL_DEVICE_EN BIT(7)
+#define GLOBAL_CONTROL_STATS_DONE_EN BIT(6)
+#define GLOBAL_CONTROL_VTU_PROBLEM_EN BIT(5)
+#define GLOBAL_CONTROL_VTU_DONE_EN BIT(4)
+#define GLOBAL_CONTROL_ATU_PROBLEM_EN BIT(3)
+#define GLOBAL_CONTROL_ATU_DONE_EN BIT(2)
+#define GLOBAL_CONTROL_TCAM_EN BIT(1)
+#define GLOBAL_CONTROL_EEPROM_DONE_EN BIT(0)
+#define GLOBAL_VTU_OP 0x05
+#define GLOBAL_VTU_VID 0x06
+#define GLOBAL_VTU_DATA_0_3 0x07
+#define GLOBAL_VTU_DATA_4_7 0x08
+#define GLOBAL_VTU_DATA_8_11 0x09
+#define GLOBAL_ATU_CONTROL 0x0a
+#define GLOBAL_ATU_OP 0x0b
+#define GLOBAL_ATU_OP_BUSY BIT(15)
+#define GLOBAL_ATU_OP_NOP (0 << 12)
+#define GLOBAL_ATU_OP_FLUSH_ALL ((1 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_FLUSH_NON_STATIC ((2 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_LOAD_DB ((3 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_GET_NEXT_DB ((4 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_FLUSH_DB ((5 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_OP_GET_CLR_VIOLATION ((7 << 12) | GLOBAL_ATU_OP_BUSY)
+#define GLOBAL_ATU_DATA 0x0c
+#define GLOBAL_ATU_DATA_STATE_MASK 0x0f
+#define GLOBAL_ATU_DATA_STATE_UNUSED 0x00
+#define GLOBAL_ATU_DATA_STATE_UC_MGMT 0x0d
+#define GLOBAL_ATU_DATA_STATE_UC_STATIC 0x0e
+#define GLOBAL_ATU_DATA_STATE_UC_PRIO_OVER 0x0f
+#define GLOBAL_ATU_DATA_STATE_MC_NONE_RATE 0x05
+#define GLOBAL_ATU_DATA_STATE_MC_STATIC 0x07
+#define GLOBAL_ATU_DATA_STATE_MC_MGMT 0x0e
+#define GLOBAL_ATU_DATA_STATE_MC_PRIO_OVER 0x0f
+#define GLOBAL_ATU_MAC_01 0x0d
+#define GLOBAL_ATU_MAC_23 0x0e
+#define GLOBAL_ATU_MAC_45 0x0f
+#define GLOBAL_IP_PRI_0 0x10
+#define GLOBAL_IP_PRI_1 0x11
+#define GLOBAL_IP_PRI_2 0x12
+#define GLOBAL_IP_PRI_3 0x13
+#define GLOBAL_IP_PRI_4 0x14
+#define GLOBAL_IP_PRI_5 0x15
+#define GLOBAL_IP_PRI_6 0x16
+#define GLOBAL_IP_PRI_7 0x17
+#define GLOBAL_IEEE_PRI 0x18
+#define GLOBAL_CORE_TAG_TYPE 0x19
+#define GLOBAL_MONITOR_CONTROL 0x1a
+#define GLOBAL_CONTROL_2 0x1c
+#define GLOBAL_STATS_OP 0x1d
+#define GLOBAL_STATS_OP_BUSY BIT(15)
+#define GLOBAL_STATS_OP_NOP (0 << 12)
+#define GLOBAL_STATS_OP_FLUSH_ALL ((1 << 12) | GLOBAL_STATS_OP_BUSY)
+#define GLOBAL_STATS_OP_FLUSH_PORT ((2 << 12) | GLOBAL_STATS_OP_BUSY)
+#define GLOBAL_STATS_OP_READ_CAPTURED ((4 << 12) | GLOBAL_STATS_OP_BUSY)
+#define GLOBAL_STATS_OP_CAPTURE_PORT ((5 << 12) | GLOBAL_STATS_OP_BUSY)
+#define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY)
+#define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY)
+#define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY)
+#define GLOBAL_STATS_COUNTER_32 0x1e
+#define GLOBAL_STATS_COUNTER_01 0x1f
-#define FDB_STATE_UNUSED 0x00
-#define FDB_STATE_MC_STATIC 0x07 /* static multicast */
-#define FDB_STATE_STATIC 0x0e /* static unicast */
+#define REG_GLOBAL2 0x1c
+#define GLOBAL2_INT_SOURCE 0x00
+#define GLOBAL2_INT_MASK 0x01
+#define GLOBAL2_MGMT_EN_2X 0x02
+#define GLOBAL2_MGMT_EN_0X 0x03
+#define GLOBAL2_FLOW_CONTROL 0x04
+#define GLOBAL2_SWITCH_MGMT 0x05
+#define GLOBAL2_DEVICE_MAPPING 0x06
+#define GLOBAL2_TRUNK_MASK 0x07
+#define GLOBAL2_TRUNK_MAPPING 0x08
+#define GLOBAL2_INGRESS_OP 0x09
+#define GLOBAL2_INGRESS_DATA 0x0a
+#define GLOBAL2_PVT_ADDR 0x0b
+#define GLOBAL2_PVT_DATA 0x0c
+#define GLOBAL2_SWITCH_MAC 0x0d
+#define GLOBAL2_SWITCH_MAC_BUSY BIT(15)
+#define GLOBAL2_ATU_STATS 0x0e
+#define GLOBAL2_PRIO_OVERRIDE 0x0f
+#define GLOBAL2_EEPROM_OP 0x14
+#define GLOBAL2_EEPROM_OP_BUSY BIT(15)
+#define GLOBAL2_EEPROM_OP_LOAD BIT(11)
+#define GLOBAL2_EEPROM_DATA 0x15
+#define GLOBAL2_PTP_AVB_OP 0x16
+#define GLOBAL2_PTP_AVB_DATA 0x17
+#define GLOBAL2_SMI_OP 0x18
+#define GLOBAL2_SMI_OP_BUSY BIT(15)
+#define GLOBAL2_SMI_OP_CLAUSE_22 BIT(12)
+#define GLOBAL2_SMI_OP_22_WRITE ((1 << 10) | GLOBAL2_SMI_OP_BUSY | \
+ GLOBAL2_SMI_OP_CLAUSE_22)
+#define GLOBAL2_SMI_OP_22_READ ((2 << 10) | GLOBAL2_SMI_OP_BUSY | \
+ GLOBAL2_SMI_OP_CLAUSE_22)
+#define GLOBAL2_SMI_OP_45_WRITE_ADDR ((0 << 10) | GLOBAL2_SMI_OP_BUSY)
+#define GLOBAL2_SMI_OP_45_WRITE_DATA ((1 << 10) | GLOBAL2_SMI_OP_BUSY)
+#define GLOBAL2_SMI_OP_45_READ_DATA ((2 << 10) | GLOBAL2_SMI_OP_BUSY)
+#define GLOBAL2_SMI_DATA 0x19
+#define GLOBAL2_SCRATCH_MISC 0x1a
+#define GLOBAL2_WDOG_CONTROL 0x1b
+#define GLOBAL2_QOS_WEIGHT 0x1c
+#define GLOBAL2_MISC 0x1d
struct mv88e6xxx_priv_state {
/* When using multi-chip addressing, this mutex protects
struct mutex eeprom_mutex;
int id; /* switch product id */
+ int num_ports; /* number of switch ports */
/* hw bridging */
int reg;
};
+int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active);
int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port);
int mv88e6xxx_setup_common(struct dsa_switch *ds);
int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg);
int mv88e6xxx_config_prio(struct dsa_switch *ds);
int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr);
int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr);
-int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum);
-int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val);
+int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum);
+int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val);
+int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum);
+int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
+ u16 val);
void mv88e6xxx_ppu_state_init(struct dsa_switch *ds);
int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum);
int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
int regnum, u16 val);
void mv88e6xxx_poll_link(struct dsa_switch *ds);
-void mv88e6xxx_get_strings(struct dsa_switch *ds,
- int nr_stats, struct mv88e6xxx_hw_stat *stats,
- int port, uint8_t *data);
-void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
- int nr_stats, struct mv88e6xxx_hw_stat *stats,
- int port, uint64_t *data);
+void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data);
+void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
+ uint64_t *data);
+int mv88e6xxx_get_sset_count(struct dsa_switch *ds);
+int mv88e6xxx_get_sset_count_basic(struct dsa_switch *ds);
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port);
void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
struct ethtool_regs *regs, void *_p);
const unsigned char *addr, u16 vid);
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
unsigned char *addr, bool *is_static);
-
+int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg);
+int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
+ int reg, int val);
extern struct dsa_switch_driver mv88e6131_switch_driver;
extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;
extern struct dsa_switch_driver mv88e6352_switch_driver;
return 0;
}
+static int xgbe_config_rx_mode(struct xgbe_prv_data *pdata)
+{
+ struct net_device *netdev = pdata->netdev;
+ unsigned int pr_mode, am_mode;
+
+ pr_mode = ((netdev->flags & IFF_PROMISC) != 0);
+ am_mode = ((netdev->flags & IFF_ALLMULTI) != 0);
+
+ xgbe_set_promiscuous_mode(pdata, pr_mode);
+ xgbe_set_all_multicast_mode(pdata, am_mode);
+
+ xgbe_add_mac_addresses(pdata);
+
+ return 0;
+}
+
static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
int mmd_reg)
{
DBGPR("<--tx_desc_init\n");
}
-static void xgbe_rx_desc_reset(struct xgbe_ring_data *rdata)
+static void xgbe_rx_desc_reset(struct xgbe_prv_data *pdata,
+ struct xgbe_ring_data *rdata, unsigned int index)
{
struct xgbe_ring_desc *rdesc = rdata->rdesc;
+ unsigned int rx_usecs = pdata->rx_usecs;
+ unsigned int rx_frames = pdata->rx_frames;
+ unsigned int inte;
+
+ if (!rx_usecs && !rx_frames) {
+ /* No coalescing, interrupt for every descriptor */
+ inte = 1;
+ } else {
+ /* Set interrupt based on Rx frame coalescing setting */
+ if (rx_frames && !((index + 1) % rx_frames))
+ inte = 1;
+ else
+ inte = 0;
+ }
/* Reset the Rx descriptor
* Set buffer 1 (lo) address to header dma address (lo)
rdesc->desc2 = cpu_to_le32(lower_32_bits(rdata->rx.buf.dma));
rdesc->desc3 = cpu_to_le32(upper_32_bits(rdata->rx.buf.dma));
- XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE,
- rdata->interrupt ? 1 : 0);
+ XGMAC_SET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, INTE, inte);
/* Since the Rx DMA engine is likely running, make sure everything
* is written to the descriptor(s) before setting the OWN bit
struct xgbe_ring *ring = channel->rx_ring;
struct xgbe_ring_data *rdata;
unsigned int start_index = ring->cur;
- unsigned int rx_coalesce, rx_frames;
unsigned int i;
DBGPR("-->rx_desc_init\n");
- rx_coalesce = (pdata->rx_riwt || pdata->rx_frames) ? 1 : 0;
- rx_frames = pdata->rx_frames;
-
/* Initialize all descriptors */
for (i = 0; i < ring->rdesc_count; i++) {
rdata = XGBE_GET_DESC_DATA(ring, i);
- /* Set interrupt on completion bit as appropriate */
- if (rx_coalesce && (!rx_frames || ((i + 1) % rx_frames)))
- rdata->interrupt = 0;
- else
- rdata->interrupt = 1;
-
/* Initialize Rx descriptor */
- xgbe_rx_desc_reset(rdata);
+ xgbe_rx_desc_reset(pdata, rdata, i);
}
/* Update the total number of Rx descriptors */
* Initialize MAC related features
*/
xgbe_config_mac_address(pdata);
+ xgbe_config_rx_mode(pdata);
xgbe_config_jumbo_enable(pdata);
xgbe_config_flow_control(pdata);
xgbe_config_mac_speed(pdata);
hw_if->tx_complete = xgbe_tx_complete;
- hw_if->set_promiscuous_mode = xgbe_set_promiscuous_mode;
- hw_if->set_all_multicast_mode = xgbe_set_all_multicast_mode;
- hw_if->add_mac_addresses = xgbe_add_mac_addresses;
hw_if->set_mac_address = xgbe_set_mac_address;
+ hw_if->config_rx_mode = xgbe_config_rx_mode;
hw_if->enable_rx_csum = xgbe_enable_rx_csum;
hw_if->disable_rx_csum = xgbe_disable_rx_csum;
static int xgbe_one_poll(struct napi_struct *, int);
static int xgbe_all_poll(struct napi_struct *, int);
-static void xgbe_set_rx_mode(struct net_device *);
static int xgbe_alloc_channels(struct xgbe_prv_data *pdata)
{
DBGPR("-->xgbe_start\n");
- xgbe_set_rx_mode(netdev);
-
hw_if->init(pdata);
phy_start(pdata->phydev);
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
struct xgbe_hw_if *hw_if = &pdata->hw_if;
- unsigned int pr_mode, am_mode;
DBGPR("-->xgbe_set_rx_mode\n");
- pr_mode = ((netdev->flags & IFF_PROMISC) != 0);
- am_mode = ((netdev->flags & IFF_ALLMULTI) != 0);
-
- hw_if->set_promiscuous_mode(pdata, pr_mode);
- hw_if->set_all_multicast_mode(pdata, am_mode);
-
- hw_if->add_mac_addresses(pdata);
+ hw_if->config_rx_mode(pdata);
DBGPR("<--xgbe_set_rx_mode\n");
}
return 0;
}
+static void xgbe_tx_timeout(struct net_device *netdev)
+{
+ struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+ netdev_warn(netdev, "tx timeout, device restarting\n");
+ schedule_work(&pdata->restart_work);
+}
+
static struct rtnl_link_stats64 *xgbe_get_stats64(struct net_device *netdev,
struct rtnl_link_stats64 *s)
{
.ndo_validate_addr = eth_validate_addr,
.ndo_do_ioctl = xgbe_ioctl,
.ndo_change_mtu = xgbe_change_mtu,
+ .ndo_tx_timeout = xgbe_tx_timeout,
.ndo_get_stats64 = xgbe_get_stats64,
.ndo_vlan_rx_add_vid = xgbe_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = xgbe_vlan_rx_kill_vid,
if (desc_if->map_rx_buffer(pdata, ring, rdata))
break;
- hw_if->rx_desc_reset(rdata);
+ hw_if->rx_desc_reset(pdata, rdata, ring->dirty);
ring->dirty++;
}
(ec->rate_sample_interval))
return -EOPNOTSUPP;
- /* Can only change rx-frames when interface is down (see
- * rx_descriptor_init in xgbe-dev.c)
- */
- rx_frames = pdata->rx_frames;
- if (rx_frames != ec->rx_max_coalesced_frames && netif_running(netdev)) {
- netdev_alert(netdev,
- "interface must be down to change rx-frames\n");
- return -EINVAL;
- }
-
rx_riwt = hw_if->usec_to_riwt(pdata, ec->rx_coalesce_usecs);
rx_usecs = ec->rx_coalesce_usecs;
rx_frames = ec->rx_max_coalesced_frames;
netdev->priv_flags |= IFF_UNICAST_FLT;
+ /* Use default watchdog timeout */
+ netdev->watchdog_timeo = 0;
+
xgbe_init_rx_coalesce(pdata);
xgbe_init_tx_coalesce(pdata);
struct xgbe_tx_ring_data tx; /* Tx-related data */
struct xgbe_rx_ring_data rx; /* Rx-related data */
- unsigned int interrupt; /* Interrupt indicator */
-
unsigned int mapped_as_page;
/* Incomplete receive save location. If the budget is exhausted
struct xgbe_hw_if {
int (*tx_complete)(struct xgbe_ring_desc *);
- int (*set_promiscuous_mode)(struct xgbe_prv_data *, unsigned int);
- int (*set_all_multicast_mode)(struct xgbe_prv_data *, unsigned int);
- int (*add_mac_addresses)(struct xgbe_prv_data *);
int (*set_mac_address)(struct xgbe_prv_data *, u8 *addr);
+ int (*config_rx_mode)(struct xgbe_prv_data *);
int (*enable_rx_csum)(struct xgbe_prv_data *);
int (*disable_rx_csum)(struct xgbe_prv_data *);
int (*dev_read)(struct xgbe_channel *);
void (*tx_desc_init)(struct xgbe_channel *);
void (*rx_desc_init)(struct xgbe_channel *);
- void (*rx_desc_reset)(struct xgbe_ring_data *);
void (*tx_desc_reset)(struct xgbe_ring_data *);
+ void (*rx_desc_reset)(struct xgbe_prv_data *, struct xgbe_ring_data *,
+ unsigned int);
int (*is_last_desc)(struct xgbe_ring_desc *);
int (*is_context_desc)(struct xgbe_ring_desc *);
void (*tx_start_xmit)(struct xgbe_channel *, struct xgbe_ring *);
int stats_state;
/* used for synchronization of concurrent threads statistics handling */
- spinlock_t stats_lock;
+ struct mutex stats_lock;
/* used by dmae command loader */
struct dmae_command stats_dmae;
int fp_array_size;
u32 dump_preset_idx;
- bool stats_started;
- struct semaphore stats_sema;
u8 phys_port_id[ETH_ALEN];
u32 xmac_val;
u32 emac_addr;
u32 emac_val;
- u32 umac_addr;
- u32 umac_val;
+ u32 umac_addr[2];
+ u32 umac_val[2];
u32 bmac_addr;
u32 bmac_val[2];
};
return 0;
}
+/* previous driver DMAE transaction may have occurred when pre-boot stage ended
+ * and boot began, or when kdump kernel was loaded. Either case would invalidate
+ * the addresses of the transaction, resulting in was-error bit set in the pci
+ * causing all hw-to-host pcie transactions to timeout. If this happened we want
+ * to clear the interrupt which detected this from the pglueb and the was done
+ * bit
+ */
+static void bnx2x_clean_pglue_errors(struct bnx2x *bp)
+{
+ if (!CHIP_IS_E1x(bp))
+ REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR,
+ 1 << BP_ABS_FUNC(bp));
+}
+
static int bnx2x_init_hw_func(struct bnx2x *bp)
{
int port = BP_PORT(bp);
bnx2x_init_block(bp, BLOCK_PGLUE_B, init_phase);
- if (!CHIP_IS_E1x(bp))
- REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, func);
+ bnx2x_clean_pglue_errors(bp);
bnx2x_init_block(bp, BLOCK_ATC, init_phase);
bnx2x_init_block(bp, BLOCK_DMAE, init_phase);
return base + (BP_ABS_FUNC(bp)) * stride;
}
+static bool bnx2x_prev_unload_close_umac(struct bnx2x *bp,
+ u8 port, u32 reset_reg,
+ struct bnx2x_mac_vals *vals)
+{
+ u32 mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port;
+ u32 base_addr;
+
+ if (!(mask & reset_reg))
+ return false;
+
+ BNX2X_DEV_INFO("Disable umac Rx %02x\n", port);
+ base_addr = port ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
+ vals->umac_addr[port] = base_addr + UMAC_REG_COMMAND_CONFIG;
+ vals->umac_val[port] = REG_RD(bp, vals->umac_addr[port]);
+ REG_WR(bp, vals->umac_addr[port], 0);
+
+ return true;
+}
+
static void bnx2x_prev_unload_close_mac(struct bnx2x *bp,
struct bnx2x_mac_vals *vals)
{
u8 port = BP_PORT(bp);
/* reset addresses as they also mark which values were changed */
- vals->bmac_addr = 0;
- vals->umac_addr = 0;
- vals->xmac_addr = 0;
- vals->emac_addr = 0;
+ memset(vals, 0, sizeof(*vals));
reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2);
REG_WR(bp, vals->xmac_addr, 0);
mac_stopped = true;
}
- mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port;
- if (mask & reset_reg) {
- BNX2X_DEV_INFO("Disable umac Rx\n");
- base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
- vals->umac_addr = base_addr + UMAC_REG_COMMAND_CONFIG;
- vals->umac_val = REG_RD(bp, vals->umac_addr);
- REG_WR(bp, vals->umac_addr, 0);
- mac_stopped = true;
- }
+
+ mac_stopped |= bnx2x_prev_unload_close_umac(bp, 0,
+ reset_reg, vals);
+ mac_stopped |= bnx2x_prev_unload_close_umac(bp, 1,
+ reset_reg, vals);
}
if (mac_stopped)
/* Close the MAC Rx to prevent BRB from filling up */
bnx2x_prev_unload_close_mac(bp, &mac_vals);
- /* close LLH filters towards the BRB */
+ /* close LLH filters for both ports towards the BRB */
bnx2x_set_rx_filter(&bp->link_params, 0);
+ bp->link_params.port ^= 1;
+ bnx2x_set_rx_filter(&bp->link_params, 0);
+ bp->link_params.port ^= 1;
/* Check if the UNDI driver was previously loaded */
if (bnx2x_prev_is_after_undi(bp)) {
if (mac_vals.xmac_addr)
REG_WR(bp, mac_vals.xmac_addr, mac_vals.xmac_val);
- if (mac_vals.umac_addr)
- REG_WR(bp, mac_vals.umac_addr, mac_vals.umac_val);
+ if (mac_vals.umac_addr[0])
+ REG_WR(bp, mac_vals.umac_addr[0], mac_vals.umac_val[0]);
+ if (mac_vals.umac_addr[1])
+ REG_WR(bp, mac_vals.umac_addr[1], mac_vals.umac_val[1]);
if (mac_vals.emac_addr)
REG_WR(bp, mac_vals.emac_addr, mac_vals.emac_val);
if (mac_vals.bmac_addr) {
return bnx2x_prev_mcp_done(bp);
}
-/* previous driver DMAE transaction may have occurred when pre-boot stage ended
- * and boot began, or when kdump kernel was loaded. Either case would invalidate
- * the addresses of the transaction, resulting in was-error bit set in the pci
- * causing all hw-to-host pcie transactions to timeout. If this happened we want
- * to clear the interrupt which detected this from the pglueb and the was done
- * bit
- */
-static void bnx2x_prev_interrupted_dmae(struct bnx2x *bp)
-{
- if (!CHIP_IS_E1x(bp)) {
- u32 val = REG_RD(bp, PGLUE_B_REG_PGLUE_B_INT_STS);
- if (val & PGLUE_B_PGLUE_B_INT_STS_REG_WAS_ERROR_ATTN) {
- DP(BNX2X_MSG_SP,
- "'was error' bit was found to be set in pglueb upon startup. Clearing\n");
- REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR,
- 1 << BP_FUNC(bp));
- }
- }
-}
-
static int bnx2x_prev_unload(struct bnx2x *bp)
{
int time_counter = 10;
/* clear hw from errors which may have resulted from an interrupted
* dmae transaction.
*/
- bnx2x_prev_interrupted_dmae(bp);
+ bnx2x_clean_pglue_errors(bp);
/* Release previously held locks */
hw_lock_reg = (BP_FUNC(bp) <= 5) ?
mutex_init(&bp->port.phy_mutex);
mutex_init(&bp->fw_mb_mutex);
mutex_init(&bp->drv_info_mutex);
+ mutex_init(&bp->stats_lock);
bp->drv_info_mng_owner = false;
- spin_lock_init(&bp->stats_lock);
- sema_init(&bp->stats_sema, 1);
INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task);
INIT_DELAYED_WORK(&bp->sp_rtnl_task, bnx2x_sp_rtnl_task);
cancel_delayed_work_sync(&bp->sp_task);
cancel_delayed_work_sync(&bp->period_task);
- spin_lock_bh(&bp->stats_lock);
+ mutex_lock(&bp->stats_lock);
bp->stats_state = STATS_STATE_DISABLED;
- spin_unlock_bh(&bp->stats_lock);
+ mutex_unlock(&bp->stats_lock);
bnx2x_save_statistics(bp);
cookie.vf = vf;
cookie.state = VF_ACQUIRED;
- bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie);
+ rc = bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie);
+ if (rc)
+ goto op_err;
}
DP(BNX2X_MSG_IOV, "set state to acquired\n");
*/
static void bnx2x_storm_stats_post(struct bnx2x *bp)
{
- if (!bp->stats_pending) {
- int rc;
+ int rc;
- spin_lock_bh(&bp->stats_lock);
-
- if (bp->stats_pending) {
- spin_unlock_bh(&bp->stats_lock);
- return;
- }
-
- bp->fw_stats_req->hdr.drv_stats_counter =
- cpu_to_le16(bp->stats_counter++);
+ if (bp->stats_pending)
+ return;
- DP(BNX2X_MSG_STATS, "Sending statistics ramrod %d\n",
- le16_to_cpu(bp->fw_stats_req->hdr.drv_stats_counter));
+ bp->fw_stats_req->hdr.drv_stats_counter =
+ cpu_to_le16(bp->stats_counter++);
- /* adjust the ramrod to include VF queues statistics */
- bnx2x_iov_adjust_stats_req(bp);
- bnx2x_dp_stats(bp);
+ DP(BNX2X_MSG_STATS, "Sending statistics ramrod %d\n",
+ le16_to_cpu(bp->fw_stats_req->hdr.drv_stats_counter));
- /* send FW stats ramrod */
- rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STAT_QUERY, 0,
- U64_HI(bp->fw_stats_req_mapping),
- U64_LO(bp->fw_stats_req_mapping),
- NONE_CONNECTION_TYPE);
- if (rc == 0)
- bp->stats_pending = 1;
+ /* adjust the ramrod to include VF queues statistics */
+ bnx2x_iov_adjust_stats_req(bp);
+ bnx2x_dp_stats(bp);
- spin_unlock_bh(&bp->stats_lock);
- }
+ /* send FW stats ramrod */
+ rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STAT_QUERY, 0,
+ U64_HI(bp->fw_stats_req_mapping),
+ U64_LO(bp->fw_stats_req_mapping),
+ NONE_CONNECTION_TYPE);
+ if (rc == 0)
+ bp->stats_pending = 1;
}
static void bnx2x_hw_stats_post(struct bnx2x *bp)
*/
/* should be called under stats_sema */
-static void __bnx2x_stats_pmf_update(struct bnx2x *bp)
+static void bnx2x_stats_pmf_update(struct bnx2x *bp)
{
struct dmae_command *dmae;
u32 opcode;
}
/* should be called under stats_sema */
-static void __bnx2x_stats_start(struct bnx2x *bp)
+static void bnx2x_stats_start(struct bnx2x *bp)
{
if (IS_PF(bp)) {
if (bp->port.pmf)
bnx2x_hw_stats_post(bp);
bnx2x_storm_stats_post(bp);
}
-
- bp->stats_started = true;
-}
-
-static void bnx2x_stats_start(struct bnx2x *bp)
-{
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
- __bnx2x_stats_start(bp);
- up(&bp->stats_sema);
}
static void bnx2x_stats_pmf_start(struct bnx2x *bp)
{
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
bnx2x_stats_comp(bp);
- __bnx2x_stats_pmf_update(bp);
- __bnx2x_stats_start(bp);
- up(&bp->stats_sema);
-}
-
-static void bnx2x_stats_pmf_update(struct bnx2x *bp)
-{
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
- __bnx2x_stats_pmf_update(bp);
- up(&bp->stats_sema);
+ bnx2x_stats_pmf_update(bp);
+ bnx2x_stats_start(bp);
}
static void bnx2x_stats_restart(struct bnx2x *bp)
*/
if (IS_VF(bp))
return;
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
+
bnx2x_stats_comp(bp);
- __bnx2x_stats_start(bp);
- up(&bp->stats_sema);
+ bnx2x_stats_start(bp);
}
static void bnx2x_bmac_stats_update(struct bnx2x *bp)
{
u32 *stats_comp = bnx2x_sp(bp, stats_comp);
- /* we run update from timer context, so give up
- * if somebody is in the middle of transition
- */
- if (down_trylock(&bp->stats_sema))
+ if (bnx2x_edebug_stats_stopped(bp))
return;
- if (bnx2x_edebug_stats_stopped(bp) || !bp->stats_started)
- goto out;
-
if (IS_PF(bp)) {
if (*stats_comp != DMAE_COMP_VAL)
- goto out;
+ return;
if (bp->port.pmf)
bnx2x_hw_stats_update(bp);
BNX2X_ERR("storm stats were not updated for 3 times\n");
bnx2x_panic();
}
- goto out;
+ return;
}
} else {
/* vf doesn't collect HW statistics, and doesn't get completions
/* vf is done */
if (IS_VF(bp))
- goto out;
+ return;
if (netif_msg_timer(bp)) {
struct bnx2x_eth_stats *estats = &bp->eth_stats;
bnx2x_hw_stats_post(bp);
bnx2x_storm_stats_post(bp);
-
-out:
- up(&bp->stats_sema);
}
static void bnx2x_port_stats_stop(struct bnx2x *bp)
static void bnx2x_stats_stop(struct bnx2x *bp)
{
- int update = 0;
-
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
-
- bp->stats_started = false;
+ bool update = false;
bnx2x_stats_comp(bp);
bnx2x_hw_stats_post(bp);
bnx2x_stats_comp(bp);
}
-
- up(&bp->stats_sema);
}
static void bnx2x_stats_do_nothing(struct bnx2x *bp)
void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event)
{
- enum bnx2x_stats_state state;
- void (*action)(struct bnx2x *bp);
+ enum bnx2x_stats_state state = bp->stats_state;
+
if (unlikely(bp->panic))
return;
- spin_lock_bh(&bp->stats_lock);
- state = bp->stats_state;
+ /* Statistics update run from timer context, and we don't want to stop
+ * that context in case someone is in the middle of a transition.
+ * For other events, wait a bit until lock is taken.
+ */
+ if (!mutex_trylock(&bp->stats_lock)) {
+ if (event == STATS_EVENT_UPDATE)
+ return;
+
+ DP(BNX2X_MSG_STATS,
+ "Unlikely stats' lock contention [event %d]\n", event);
+ mutex_lock(&bp->stats_lock);
+ }
+
+ bnx2x_stats_stm[state][event].action(bp);
bp->stats_state = bnx2x_stats_stm[state][event].next_state;
- action = bnx2x_stats_stm[state][event].action;
- spin_unlock_bh(&bp->stats_lock);
- action(bp);
+ mutex_unlock(&bp->stats_lock);
if ((event != STATS_EVENT_UPDATE) || netif_msg_timer(bp))
DP(BNX2X_MSG_STATS, "state %d -> event %d -> state %d\n",
}
}
-void bnx2x_stats_safe_exec(struct bnx2x *bp,
- void (func_to_exec)(void *cookie),
- void *cookie){
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
+int bnx2x_stats_safe_exec(struct bnx2x *bp,
+ void (func_to_exec)(void *cookie),
+ void *cookie)
+{
+ int cnt = 10, rc = 0;
+
+ /* Wait for statistics to end [while blocking further requests],
+ * then run supplied function 'safely'.
+ */
+ mutex_lock(&bp->stats_lock);
+
bnx2x_stats_comp(bp);
+ while (bp->stats_pending && cnt--)
+ if (bnx2x_storm_stats_update(bp))
+ usleep_range(1000, 2000);
+ if (bp->stats_pending) {
+ BNX2X_ERR("Failed to wait for stats pending to clear [possibly FW is stuck]\n");
+ rc = -EBUSY;
+ goto out;
+ }
+
func_to_exec(cookie);
- __bnx2x_stats_start(bp);
- up(&bp->stats_sema);
+
+out:
+ /* No need to restart statistics - if they're enabled, the timer
+ * will restart the statistics.
+ */
+ mutex_unlock(&bp->stats_lock);
+
+ return rc;
}
void bnx2x_memset_stats(struct bnx2x *bp);
void bnx2x_stats_init(struct bnx2x *bp);
void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event);
-void bnx2x_stats_safe_exec(struct bnx2x *bp,
- void (func_to_exec)(void *cookie),
- void *cookie);
+int bnx2x_stats_safe_exec(struct bnx2x *bp,
+ void (func_to_exec)(void *cookie),
+ void *cookie);
/**
* bnx2x_save_statistics - save statistics when unloading.
} else if (priv->ext_phy) {
int0_enable |= UMAC_IRQ_LINK_EVENT;
} else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) {
+ if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET)
+ int0_enable |= UMAC_IRQ_LINK_EVENT;
+
reg = bcmgenet_bp_mc_get(priv);
reg |= BIT(priv->hw_params->bp_in_en_shift);
.rdma_offset = 0x10000,
.tdma_offset = 0x11000,
.words_per_bd = 2,
- .flags = GENET_HAS_EXT | GENET_HAS_MDIO_INTR,
+ .flags = GENET_HAS_EXT | GENET_HAS_MDIO_INTR |
+ GENET_HAS_MOCA_LINK_DET,
},
[GENET_V4] = {
.tx_queues = 4,
.rdma_offset = 0x2000,
.tdma_offset = 0x4000,
.words_per_bd = 3,
- .flags = GENET_HAS_40BITS | GENET_HAS_EXT | GENET_HAS_MDIO_INTR,
+ .flags = GENET_HAS_40BITS | GENET_HAS_EXT |
+ GENET_HAS_MDIO_INTR | GENET_HAS_MOCA_LINK_DET,
},
};
#define GENET_HAS_40BITS (1 << 0)
#define GENET_HAS_EXT (1 << 1)
#define GENET_HAS_MDIO_INTR (1 << 2)
+#define GENET_HAS_MOCA_LINK_DET (1 << 3)
/* BCMGENET hardware parameters, keep this structure nicely aligned
* since it is going to be used in hot paths
return 0;
}
+static int bcmgenet_fixed_phy_link_update(struct net_device *dev,
+ struct fixed_phy_status *status)
+{
+ if (dev && dev->phydev && status)
+ status->link = dev->phydev->link;
+
+ return 0;
+}
+
static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
dev_err(kdev, "failed to register fixed PHY device\n");
return -ENODEV;
}
+
+ if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET) {
+ ret = fixed_phy_set_link_update(
+ phydev, bcmgenet_fixed_phy_link_update);
+ if (!ret)
+ phydev->link = 0;
+ }
}
priv->phydev = phydev;
hwstat->rx_oversize_pkts +
hwstat->rx_jabbers +
hwstat->rx_undersize_pkts +
- hwstat->sqe_test_errors +
hwstat->rx_length_mismatch);
nstat->tx_errors = (hwstat->tx_late_cols +
hwstat->tx_excessive_cols +
hwstat->tx_underruns +
- hwstat->tx_carrier_errors);
+ hwstat->tx_carrier_errors +
+ hwstat->sqe_test_errors);
nstat->collisions = (hwstat->tx_single_cols +
hwstat->tx_multiple_cols +
hwstat->tx_excessive_cols);
adapter->msg_enable = val;
}
-static char stats_strings[][ETH_GSTRING_LEN] = {
+static const char stats_strings[][ETH_GSTRING_LEN] = {
"TxOctetsOK",
"TxOctetsBad",
"TxUnicastFramesOK",
adapter->msg_enable = val;
}
-static char stats_strings[][ETH_GSTRING_LEN] = {
+static const char stats_strings[][ETH_GSTRING_LEN] = {
"TxOctetsOK ",
"TxFramesOK ",
"TxMulticastFramesOK",
d->addr_lo = cpu_to_be32(mapping);
d->addr_hi = cpu_to_be32((u64) mapping >> 32);
- wmb();
+ dma_wmb();
d->len_gen = cpu_to_be32(V_FLD_GEN1(gen));
d->gen2 = cpu_to_be32(V_FLD_GEN2(gen));
return 0;
{
d->addr_lo = cpu_to_be32(mapping);
d->addr_hi = cpu_to_be32((u64) mapping >> 32);
- wmb();
+ dma_wmb();
d->len_gen = cpu_to_be32(V_FLD_GEN1(gen));
d->gen2 = cpu_to_be32(V_FLD_GEN2(gen));
return 0;
q->sdesc[q->pidx] = q->sdesc[idx];
to->addr_lo = from->addr_lo; /* already big endian */
to->addr_hi = from->addr_hi; /* likewise */
- wmb();
+ dma_wmb();
to->len_gen = cpu_to_be32(V_FLD_GEN1(q->gen));
to->gen2 = cpu_to_be32(V_FLD_GEN2(q->gen));
sd->eop = 1;
wrp->wr_hi = htonl(F_WR_SOP | F_WR_EOP | V_WR_DATATYPE(1) |
V_WR_SGLSFLT(flits)) | wr_hi;
- wmb();
+ dma_wmb();
wrp->wr_lo = htonl(V_WR_LEN(flits + sgl_flits) |
V_WR_GEN(gen)) | wr_lo;
wr_gen2(d, gen);
}
sd->eop = 1;
wrp->wr_hi |= htonl(F_WR_EOP);
- wmb();
+ dma_wmb();
wp->wr_lo = htonl(V_WR_LEN(WR_FLITS) | V_WR_GEN(ogen)) | wr_lo;
wr_gen2((struct tx_desc *)wp, ogen);
WARN_ON(ndesc != 0);
cpl->wr.wr_hi = htonl(V_WR_BCNTLFLT(skb->len & 7) |
V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT)
| F_WR_SOP | F_WR_EOP | compl);
- wmb();
+ dma_wmb();
cpl->wr.wr_lo = htonl(V_WR_LEN(flits) | V_WR_GEN(gen) |
V_WR_TID(q->token));
wr_gen2(d, gen);
to->wr_hi = from->wr_hi | htonl(F_WR_SOP | F_WR_EOP |
V_WR_BCNTLFLT(len & 7));
- wmb();
+ dma_wmb();
to->wr_lo = from->wr_lo | htonl(V_WR_GEN(gen) |
V_WR_LEN((len + 7) / 8));
wr_gen2(d, gen);
u32 len, flags;
__be32 rss_hi, rss_lo;
- rmb();
+ dma_rmb();
eth = r->rss_hdr.opcode == CPL_RX_PKT;
rss_hi = *(const __be32 *)r;
rss_lo = r->rss_hdr.rss_hash_val;
}
if (!is_new_response(r, q))
break;
- rmb();
+ dma_rmb();
} while (is_pure_response(r));
if (sleeping)
if (!is_new_response(r, q))
return -1;
- rmb();
+ dma_rmb();
if (is_pure_response(r) && process_pure_responses(adap, qs, r) == 0) {
t3_write_reg(adap, A_SG_GTS, V_RSPQ(q->cntxt_id) |
V_NEWTIMER(q->holdoff_tmr) | V_NEWINDEX(q->cidx));
obj-$(CONFIG_CHELSIO_T4) += cxgb4.o
-cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o
+cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o
cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o
cxgb4-$(CONFIG_CHELSIO_T4_FCOE) += cxgb4_fcoe.o
cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o
PN_LEN = 16, /* Part Number length */
};
+enum {
+ T4_REGMAP_SIZE = (160 * 1024),
+ T5_REGMAP_SIZE = (332 * 1024),
+};
+
enum {
MEM_EDC0,
MEM_EDC1,
MAX_ISCSI_QUEUES = NCHAN, /* # of streaming iSCSI Rx queues */
};
+enum {
+ MAX_TXQ_ENTRIES = 16384,
+ MAX_CTRL_TXQ_ENTRIES = 1024,
+ MAX_RSPQ_ENTRIES = 16384,
+ MAX_RX_BUFFERS = 16384,
+ MIN_TXQ_ENTRIES = 32,
+ MIN_CTRL_TXQ_ENTRIES = 32,
+ MIN_RSPQ_ENTRIES = 128,
+ MIN_FL_ENTRIES = 16
+};
+
enum {
INGQ_EXTRAS = 2, /* firmware event queue and */
/* forwarded interrupts */
- MAX_EGRQ = MAX_ETH_QSETS*2 + MAX_OFLD_QSETS*2
- + MAX_CTRL_QUEUES + MAX_RDMA_QUEUES + MAX_ISCSI_QUEUES,
MAX_INGQ = MAX_ETH_QSETS + MAX_OFLD_QSETS + MAX_RDMA_QUEUES
+ MAX_RDMA_CIQS + MAX_ISCSI_QUEUES + INGQ_EXTRAS,
};
unsigned int idma_qid[2]; /* SGE IDMA Hung Ingress Queue ID */
unsigned int egr_start;
+ unsigned int egr_sz;
unsigned int ingr_start;
- void *egr_map[MAX_EGRQ]; /* qid->queue egress queue map */
- struct sge_rspq *ingr_map[MAX_INGQ]; /* qid->queue ingress queue map */
- DECLARE_BITMAP(starving_fl, MAX_EGRQ);
- DECLARE_BITMAP(txq_maperr, MAX_EGRQ);
+ unsigned int ingr_sz;
+ void **egr_map; /* qid->queue egress queue map */
+ struct sge_rspq **ingr_map; /* qid->queue ingress queue map */
+ unsigned long *starving_fl;
+ unsigned long *txq_maperr;
struct timer_list rx_timer; /* refills starving FLs */
struct timer_list tx_timer; /* checks Tx queues */
};
}
#endif /* CONFIG_NET_RX_BUSY_POLL */
+/* Return a version number to identify the type of adapter. The scheme is:
+ * - bits 0..9: chip version
+ * - bits 10..15: chip revision
+ * - bits 16..23: register dump version
+ */
+static inline unsigned int mk_adap_vers(struct adapter *ap)
+{
+ return CHELSIO_CHIP_VERSION(ap->params.chip) |
+ (CHELSIO_CHIP_RELEASE(ap->params.chip) << 10) | (1 << 16);
+}
+
+/* Return a queue's interrupt hold-off time in us. 0 means no timer. */
+static inline unsigned int qtimer_val(const struct adapter *adap,
+ const struct sge_rspq *q)
+{
+ unsigned int idx = q->intr_params >> 1;
+
+ return idx < SGE_NTIMERS ? adap->sge.timer_val[idx] : 0;
+}
+
+/* driver version & name used for ethtool_drvinfo */
+extern char cxgb4_driver_name[];
+extern const char cxgb4_driver_version[];
+
void t4_os_portmod_changed(const struct adapter *adap, int port_id);
void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat);
void t4_sge_start(struct adapter *adap);
void t4_sge_stop(struct adapter *adap);
int cxgb_busy_poll(struct napi_struct *napi);
+int cxgb4_set_rspq_intr_params(struct sge_rspq *q, unsigned int us,
+ unsigned int cnt);
+void cxgb4_set_ethtool_ops(struct net_device *netdev);
+int cxgb4_write_rss(const struct port_info *pi, const u16 *queues);
extern int dbfifo_int_thresh;
#define for_each_port(adapter, iter) \
return t4_memory_rw(adap, 0, mtype, addr, len, buf, 0);
}
+unsigned int t4_get_regs_len(struct adapter *adapter);
+void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size);
+
int t4_seeprom_wp(struct adapter *adapter, bool enable);
int get_vpd_params(struct adapter *adapter, struct vpd_params *p);
int t4_read_flash(struct adapter *adapter, unsigned int addr,
unsigned int qtimer_val(const struct adapter *adap,
const struct sge_rspq *q);
+
+int t4_init_devlog_params(struct adapter *adapter);
int t4_init_sge_params(struct adapter *adapter);
int t4_init_tp_params(struct adapter *adap);
int t4_filter_field_shift(const struct adapter *adap, int filter_sel);
"0.9375" };
int i;
- u16 incr[NMTUS][NCCTRL_WIN];
+ u16 (*incr)[NCCTRL_WIN];
struct adapter *adap = seq->private;
+ incr = kmalloc(sizeof(*incr) * NMTUS, GFP_KERNEL);
+ if (!incr)
+ return -ENOMEM;
+
t4_read_cong_tbl(adap, incr);
for (i = 0; i < NCCTRL_WIN; ++i) {
adap->params.a_wnd[i],
dec_fac[adap->params.b_wnd[i]]);
}
+
+ kfree(incr);
return 0;
}
--- /dev/null
+/*
+ * Copyright (C) 2013-2015 Chelsio Communications. All rights reserved.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ */
+
+#include <linux/firmware.h>
+#include <linux/mdio.h>
+
+#include "cxgb4.h"
+#include "t4_regs.h"
+#include "t4fw_api.h"
+
+#define EEPROM_MAGIC 0x38E2F10C
+
+static u32 get_msglevel(struct net_device *dev)
+{
+ return netdev2adap(dev)->msg_enable;
+}
+
+static void set_msglevel(struct net_device *dev, u32 val)
+{
+ netdev2adap(dev)->msg_enable = val;
+}
+
+static const char stats_strings[][ETH_GSTRING_LEN] = {
+ "TxOctetsOK ",
+ "TxFramesOK ",
+ "TxBroadcastFrames ",
+ "TxMulticastFrames ",
+ "TxUnicastFrames ",
+ "TxErrorFrames ",
+
+ "TxFrames64 ",
+ "TxFrames65To127 ",
+ "TxFrames128To255 ",
+ "TxFrames256To511 ",
+ "TxFrames512To1023 ",
+ "TxFrames1024To1518 ",
+ "TxFrames1519ToMax ",
+
+ "TxFramesDropped ",
+ "TxPauseFrames ",
+ "TxPPP0Frames ",
+ "TxPPP1Frames ",
+ "TxPPP2Frames ",
+ "TxPPP3Frames ",
+ "TxPPP4Frames ",
+ "TxPPP5Frames ",
+ "TxPPP6Frames ",
+ "TxPPP7Frames ",
+
+ "RxOctetsOK ",
+ "RxFramesOK ",
+ "RxBroadcastFrames ",
+ "RxMulticastFrames ",
+ "RxUnicastFrames ",
+
+ "RxFramesTooLong ",
+ "RxJabberErrors ",
+ "RxFCSErrors ",
+ "RxLengthErrors ",
+ "RxSymbolErrors ",
+ "RxRuntFrames ",
+
+ "RxFrames64 ",
+ "RxFrames65To127 ",
+ "RxFrames128To255 ",
+ "RxFrames256To511 ",
+ "RxFrames512To1023 ",
+ "RxFrames1024To1518 ",
+ "RxFrames1519ToMax ",
+
+ "RxPauseFrames ",
+ "RxPPP0Frames ",
+ "RxPPP1Frames ",
+ "RxPPP2Frames ",
+ "RxPPP3Frames ",
+ "RxPPP4Frames ",
+ "RxPPP5Frames ",
+ "RxPPP6Frames ",
+ "RxPPP7Frames ",
+
+ "RxBG0FramesDropped ",
+ "RxBG1FramesDropped ",
+ "RxBG2FramesDropped ",
+ "RxBG3FramesDropped ",
+ "RxBG0FramesTrunc ",
+ "RxBG1FramesTrunc ",
+ "RxBG2FramesTrunc ",
+ "RxBG3FramesTrunc ",
+
+ "TSO ",
+ "TxCsumOffload ",
+ "RxCsumGood ",
+ "VLANextractions ",
+ "VLANinsertions ",
+ "GROpackets ",
+ "GROmerged ",
+ "WriteCoalSuccess ",
+ "WriteCoalFail ",
+};
+
+static int get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(stats_strings);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int get_regs_len(struct net_device *dev)
+{
+ struct adapter *adap = netdev2adap(dev);
+
+ return t4_get_regs_len(adap);
+}
+
+static int get_eeprom_len(struct net_device *dev)
+{
+ return EEPROMSIZE;
+}
+
+static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct adapter *adapter = netdev2adap(dev);
+ u32 exprom_vers;
+
+ strlcpy(info->driver, cxgb4_driver_name, sizeof(info->driver));
+ strlcpy(info->version, cxgb4_driver_version,
+ sizeof(info->version));
+ strlcpy(info->bus_info, pci_name(adapter->pdev),
+ sizeof(info->bus_info));
+
+ if (adapter->params.fw_vers)
+ snprintf(info->fw_version, sizeof(info->fw_version),
+ "%u.%u.%u.%u, TP %u.%u.%u.%u",
+ FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers),
+ FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers),
+ FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers),
+ FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers),
+ FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers),
+ FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers),
+ FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers),
+ FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers));
+
+ if (!t4_get_exprom_version(adapter, &exprom_vers))
+ snprintf(info->erom_version, sizeof(info->erom_version),
+ "%u.%u.%u.%u",
+ FW_HDR_FW_VER_MAJOR_G(exprom_vers),
+ FW_HDR_FW_VER_MINOR_G(exprom_vers),
+ FW_HDR_FW_VER_MICRO_G(exprom_vers),
+ FW_HDR_FW_VER_BUILD_G(exprom_vers));
+}
+
+static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ if (stringset == ETH_SS_STATS)
+ memcpy(data, stats_strings, sizeof(stats_strings));
+}
+
+/* port stats maintained per queue of the port. They should be in the same
+ * order as in stats_strings above.
+ */
+struct queue_port_stats {
+ u64 tso;
+ u64 tx_csum;
+ u64 rx_csum;
+ u64 vlan_ex;
+ u64 vlan_ins;
+ u64 gro_pkts;
+ u64 gro_merged;
+};
+
+static void collect_sge_port_stats(const struct adapter *adap,
+ const struct port_info *p,
+ struct queue_port_stats *s)
+{
+ int i;
+ const struct sge_eth_txq *tx = &adap->sge.ethtxq[p->first_qset];
+ const struct sge_eth_rxq *rx = &adap->sge.ethrxq[p->first_qset];
+
+ memset(s, 0, sizeof(*s));
+ for (i = 0; i < p->nqsets; i++, rx++, tx++) {
+ s->tso += tx->tso;
+ s->tx_csum += tx->tx_cso;
+ s->rx_csum += rx->stats.rx_cso;
+ s->vlan_ex += rx->stats.vlan_ex;
+ s->vlan_ins += tx->vlan_ins;
+ s->gro_pkts += rx->stats.lro_pkts;
+ s->gro_merged += rx->stats.lro_merged;
+ }
+}
+
+static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
+ u64 *data)
+{
+ struct port_info *pi = netdev_priv(dev);
+ struct adapter *adapter = pi->adapter;
+ u32 val1, val2;
+
+ t4_get_port_stats(adapter, pi->tx_chan, (struct port_stats *)data);
+
+ data += sizeof(struct port_stats) / sizeof(u64);
+ collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data);
+ data += sizeof(struct queue_port_stats) / sizeof(u64);
+ if (!is_t4(adapter->params.chip)) {
+ t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7));
+ val1 = t4_read_reg(adapter, SGE_STAT_TOTAL_A);
+ val2 = t4_read_reg(adapter, SGE_STAT_MATCH_A);
+ *data = val1 - val2;
+ data++;
+ *data = val2;
+ data++;
+ } else {
+ memset(data, 0, 2 * sizeof(u64));
+ *data += 2;
+ }
+}
+
+static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct adapter *adap = netdev2adap(dev);
+ size_t buf_size;
+
+ buf_size = t4_get_regs_len(adap);
+ regs->version = mk_adap_vers(adap);
+ t4_get_regs(adap, buf, buf_size);
+}
+
+static int restart_autoneg(struct net_device *dev)
+{
+ struct port_info *p = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return -EAGAIN;
+ if (p->link_cfg.autoneg != AUTONEG_ENABLE)
+ return -EINVAL;
+ t4_restart_aneg(p->adapter, p->adapter->fn, p->tx_chan);
+ return 0;
+}
+
+static int identify_port(struct net_device *dev,
+ enum ethtool_phys_id_state state)
+{
+ unsigned int val;
+ struct adapter *adap = netdev2adap(dev);
+
+ if (state == ETHTOOL_ID_ACTIVE)
+ val = 0xffff;
+ else if (state == ETHTOOL_ID_INACTIVE)
+ val = 0;
+ else
+ return -EINVAL;
+
+ return t4_identify_port(adap, adap->fn, netdev2pinfo(dev)->viid, val);
+}
+
+static unsigned int from_fw_linkcaps(enum fw_port_type type, unsigned int caps)
+{
+ unsigned int v = 0;
+
+ if (type == FW_PORT_TYPE_BT_SGMII || type == FW_PORT_TYPE_BT_XFI ||
+ type == FW_PORT_TYPE_BT_XAUI) {
+ v |= SUPPORTED_TP;
+ if (caps & FW_PORT_CAP_SPEED_100M)
+ v |= SUPPORTED_100baseT_Full;
+ if (caps & FW_PORT_CAP_SPEED_1G)
+ v |= SUPPORTED_1000baseT_Full;
+ if (caps & FW_PORT_CAP_SPEED_10G)
+ v |= SUPPORTED_10000baseT_Full;
+ } else if (type == FW_PORT_TYPE_KX4 || type == FW_PORT_TYPE_KX) {
+ v |= SUPPORTED_Backplane;
+ if (caps & FW_PORT_CAP_SPEED_1G)
+ v |= SUPPORTED_1000baseKX_Full;
+ if (caps & FW_PORT_CAP_SPEED_10G)
+ v |= SUPPORTED_10000baseKX4_Full;
+ } else if (type == FW_PORT_TYPE_KR) {
+ v |= SUPPORTED_Backplane | SUPPORTED_10000baseKR_Full;
+ } else if (type == FW_PORT_TYPE_BP_AP) {
+ v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
+ SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full;
+ } else if (type == FW_PORT_TYPE_BP4_AP) {
+ v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
+ SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full |
+ SUPPORTED_10000baseKX4_Full;
+ } else if (type == FW_PORT_TYPE_FIBER_XFI ||
+ type == FW_PORT_TYPE_FIBER_XAUI ||
+ type == FW_PORT_TYPE_SFP ||
+ type == FW_PORT_TYPE_QSFP_10G ||
+ type == FW_PORT_TYPE_QSA) {
+ v |= SUPPORTED_FIBRE;
+ if (caps & FW_PORT_CAP_SPEED_1G)
+ v |= SUPPORTED_1000baseT_Full;
+ if (caps & FW_PORT_CAP_SPEED_10G)
+ v |= SUPPORTED_10000baseT_Full;
+ } else if (type == FW_PORT_TYPE_BP40_BA ||
+ type == FW_PORT_TYPE_QSFP) {
+ v |= SUPPORTED_40000baseSR4_Full;
+ v |= SUPPORTED_FIBRE;
+ }
+
+ if (caps & FW_PORT_CAP_ANEG)
+ v |= SUPPORTED_Autoneg;
+ return v;
+}
+
+static unsigned int to_fw_linkcaps(unsigned int caps)
+{
+ unsigned int v = 0;
+
+ if (caps & ADVERTISED_100baseT_Full)
+ v |= FW_PORT_CAP_SPEED_100M;
+ if (caps & ADVERTISED_1000baseT_Full)
+ v |= FW_PORT_CAP_SPEED_1G;
+ if (caps & ADVERTISED_10000baseT_Full)
+ v |= FW_PORT_CAP_SPEED_10G;
+ if (caps & ADVERTISED_40000baseSR4_Full)
+ v |= FW_PORT_CAP_SPEED_40G;
+ return v;
+}
+
+static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ const struct port_info *p = netdev_priv(dev);
+
+ if (p->port_type == FW_PORT_TYPE_BT_SGMII ||
+ p->port_type == FW_PORT_TYPE_BT_XFI ||
+ p->port_type == FW_PORT_TYPE_BT_XAUI) {
+ cmd->port = PORT_TP;
+ } else if (p->port_type == FW_PORT_TYPE_FIBER_XFI ||
+ p->port_type == FW_PORT_TYPE_FIBER_XAUI) {
+ cmd->port = PORT_FIBRE;
+ } else if (p->port_type == FW_PORT_TYPE_SFP ||
+ p->port_type == FW_PORT_TYPE_QSFP_10G ||
+ p->port_type == FW_PORT_TYPE_QSA ||
+ p->port_type == FW_PORT_TYPE_QSFP) {
+ if (p->mod_type == FW_PORT_MOD_TYPE_LR ||
+ p->mod_type == FW_PORT_MOD_TYPE_SR ||
+ p->mod_type == FW_PORT_MOD_TYPE_ER ||
+ p->mod_type == FW_PORT_MOD_TYPE_LRM)
+ cmd->port = PORT_FIBRE;
+ else if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE ||
+ p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE)
+ cmd->port = PORT_DA;
+ else
+ cmd->port = PORT_OTHER;
+ } else {
+ cmd->port = PORT_OTHER;
+ }
+
+ if (p->mdio_addr >= 0) {
+ cmd->phy_address = p->mdio_addr;
+ cmd->transceiver = XCVR_EXTERNAL;
+ cmd->mdio_support = p->port_type == FW_PORT_TYPE_BT_SGMII ?
+ MDIO_SUPPORTS_C22 : MDIO_SUPPORTS_C45;
+ } else {
+ cmd->phy_address = 0; /* not really, but no better option */
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->mdio_support = 0;
+ }
+
+ cmd->supported = from_fw_linkcaps(p->port_type, p->link_cfg.supported);
+ cmd->advertising = from_fw_linkcaps(p->port_type,
+ p->link_cfg.advertising);
+ ethtool_cmd_speed_set(cmd,
+ netif_carrier_ok(dev) ? p->link_cfg.speed : 0);
+ cmd->duplex = DUPLEX_FULL;
+ cmd->autoneg = p->link_cfg.autoneg;
+ cmd->maxtxpkt = 0;
+ cmd->maxrxpkt = 0;
+ return 0;
+}
+
+static unsigned int speed_to_caps(int speed)
+{
+ if (speed == 100)
+ return FW_PORT_CAP_SPEED_100M;
+ if (speed == 1000)
+ return FW_PORT_CAP_SPEED_1G;
+ if (speed == 10000)
+ return FW_PORT_CAP_SPEED_10G;
+ if (speed == 40000)
+ return FW_PORT_CAP_SPEED_40G;
+ return 0;
+}
+
+static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ unsigned int cap;
+ struct port_info *p = netdev_priv(dev);
+ struct link_config *lc = &p->link_cfg;
+ u32 speed = ethtool_cmd_speed(cmd);
+
+ if (cmd->duplex != DUPLEX_FULL) /* only full-duplex supported */
+ return -EINVAL;
+
+ if (!(lc->supported & FW_PORT_CAP_ANEG)) {
+ /* PHY offers a single speed. See if that's what's
+ * being requested.
+ */
+ if (cmd->autoneg == AUTONEG_DISABLE &&
+ (lc->supported & speed_to_caps(speed)))
+ return 0;
+ return -EINVAL;
+ }
+
+ if (cmd->autoneg == AUTONEG_DISABLE) {
+ cap = speed_to_caps(speed);
+
+ if (!(lc->supported & cap) ||
+ (speed == 1000) ||
+ (speed == 10000) ||
+ (speed == 40000))
+ return -EINVAL;
+ lc->requested_speed = cap;
+ lc->advertising = 0;
+ } else {
+ cap = to_fw_linkcaps(cmd->advertising);
+ if (!(lc->supported & cap))
+ return -EINVAL;
+ lc->requested_speed = 0;
+ lc->advertising = cap | FW_PORT_CAP_ANEG;
+ }
+ lc->autoneg = cmd->autoneg;
+
+ if (netif_running(dev))
+ return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
+ lc);
+ return 0;
+}
+
+static void get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct port_info *p = netdev_priv(dev);
+
+ epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0;
+ epause->rx_pause = (p->link_cfg.fc & PAUSE_RX) != 0;
+ epause->tx_pause = (p->link_cfg.fc & PAUSE_TX) != 0;
+}
+
+static int set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *epause)
+{
+ struct port_info *p = netdev_priv(dev);
+ struct link_config *lc = &p->link_cfg;
+
+ if (epause->autoneg == AUTONEG_DISABLE)
+ lc->requested_fc = 0;
+ else if (lc->supported & FW_PORT_CAP_ANEG)
+ lc->requested_fc = PAUSE_AUTONEG;
+ else
+ return -EINVAL;
+
+ if (epause->rx_pause)
+ lc->requested_fc |= PAUSE_RX;
+ if (epause->tx_pause)
+ lc->requested_fc |= PAUSE_TX;
+ if (netif_running(dev))
+ return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
+ lc);
+ return 0;
+}
+
+static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
+{
+ const struct port_info *pi = netdev_priv(dev);
+ const struct sge *s = &pi->adapter->sge;
+
+ e->rx_max_pending = MAX_RX_BUFFERS;
+ e->rx_mini_max_pending = MAX_RSPQ_ENTRIES;
+ e->rx_jumbo_max_pending = 0;
+ e->tx_max_pending = MAX_TXQ_ENTRIES;
+
+ e->rx_pending = s->ethrxq[pi->first_qset].fl.size - 8;
+ e->rx_mini_pending = s->ethrxq[pi->first_qset].rspq.size;
+ e->rx_jumbo_pending = 0;
+ e->tx_pending = s->ethtxq[pi->first_qset].q.size;
+}
+
+static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
+{
+ int i;
+ const struct port_info *pi = netdev_priv(dev);
+ struct adapter *adapter = pi->adapter;
+ struct sge *s = &adapter->sge;
+
+ if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending ||
+ e->tx_pending > MAX_TXQ_ENTRIES ||
+ e->rx_mini_pending > MAX_RSPQ_ENTRIES ||
+ e->rx_mini_pending < MIN_RSPQ_ENTRIES ||
+ e->rx_pending < MIN_FL_ENTRIES || e->tx_pending < MIN_TXQ_ENTRIES)
+ return -EINVAL;
+
+ if (adapter->flags & FULL_INIT_DONE)
+ return -EBUSY;
+
+ for (i = 0; i < pi->nqsets; ++i) {
+ s->ethtxq[pi->first_qset + i].q.size = e->tx_pending;
+ s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8;
+ s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending;
+ }
+ return 0;
+}
+
+/**
+ * set_rx_intr_params - set a net devices's RX interrupt holdoff paramete!
+ * @dev: the network device
+ * @us: the hold-off time in us, or 0 to disable timer
+ * @cnt: the hold-off packet count, or 0 to disable counter
+ *
+ * Set the RX interrupt hold-off parameters for a network device.
+ */
+static int set_rx_intr_params(struct net_device *dev,
+ unsigned int us, unsigned int cnt)
+{
+ int i, err;
+ struct port_info *pi = netdev_priv(dev);
+ struct adapter *adap = pi->adapter;
+ struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
+
+ for (i = 0; i < pi->nqsets; i++, q++) {
+ err = cxgb4_set_rspq_intr_params(&q->rspq, us, cnt);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int set_adaptive_rx_setting(struct net_device *dev, int adaptive_rx)
+{
+ int i;
+ struct port_info *pi = netdev_priv(dev);
+ struct adapter *adap = pi->adapter;
+ struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
+
+ for (i = 0; i < pi->nqsets; i++, q++)
+ q->rspq.adaptive_rx = adaptive_rx;
+
+ return 0;
+}
+
+static int get_adaptive_rx_setting(struct net_device *dev)
+{
+ struct port_info *pi = netdev_priv(dev);
+ struct adapter *adap = pi->adapter;
+ struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
+
+ return q->rspq.adaptive_rx;
+}
+
+static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+{
+ set_adaptive_rx_setting(dev, c->use_adaptive_rx_coalesce);
+ return set_rx_intr_params(dev, c->rx_coalesce_usecs,
+ c->rx_max_coalesced_frames);
+}
+
+static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
+{
+ const struct port_info *pi = netdev_priv(dev);
+ const struct adapter *adap = pi->adapter;
+ const struct sge_rspq *rq = &adap->sge.ethrxq[pi->first_qset].rspq;
+
+ c->rx_coalesce_usecs = qtimer_val(adap, rq);
+ c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN) ?
+ adap->sge.counter_val[rq->pktcnt_idx] : 0;
+ c->use_adaptive_rx_coalesce = get_adaptive_rx_setting(dev);
+ return 0;
+}
+
+/**
+ * eeprom_ptov - translate a physical EEPROM address to virtual
+ * @phys_addr: the physical EEPROM address
+ * @fn: the PCI function number
+ * @sz: size of function-specific area
+ *
+ * Translate a physical EEPROM address to virtual. The first 1K is
+ * accessed through virtual addresses starting at 31K, the rest is
+ * accessed through virtual addresses starting at 0.
+ *
+ * The mapping is as follows:
+ * [0..1K) -> [31K..32K)
+ * [1K..1K+A) -> [31K-A..31K)
+ * [1K+A..ES) -> [0..ES-A-1K)
+ *
+ * where A = @fn * @sz, and ES = EEPROM size.
+ */
+static int eeprom_ptov(unsigned int phys_addr, unsigned int fn, unsigned int sz)
+{
+ fn *= sz;
+ if (phys_addr < 1024)
+ return phys_addr + (31 << 10);
+ if (phys_addr < 1024 + fn)
+ return 31744 - fn + phys_addr - 1024;
+ if (phys_addr < EEPROMSIZE)
+ return phys_addr - 1024 - fn;
+ return -EINVAL;
+}
+
+/* The next two routines implement eeprom read/write from physical addresses.
+ */
+static int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v)
+{
+ int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE);
+
+ if (vaddr >= 0)
+ vaddr = pci_read_vpd(adap->pdev, vaddr, sizeof(u32), v);
+ return vaddr < 0 ? vaddr : 0;
+}
+
+static int eeprom_wr_phys(struct adapter *adap, unsigned int phys_addr, u32 v)
+{
+ int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE);
+
+ if (vaddr >= 0)
+ vaddr = pci_write_vpd(adap->pdev, vaddr, sizeof(u32), &v);
+ return vaddr < 0 ? vaddr : 0;
+}
+
+#define EEPROM_MAGIC 0x38E2F10C
+
+static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
+ u8 *data)
+{
+ int i, err = 0;
+ struct adapter *adapter = netdev2adap(dev);
+ u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ e->magic = EEPROM_MAGIC;
+ for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4)
+ err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]);
+
+ if (!err)
+ memcpy(data, buf + e->offset, e->len);
+ kfree(buf);
+ return err;
+}
+
+static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+ u8 *data)
+{
+ u8 *buf;
+ int err = 0;
+ u32 aligned_offset, aligned_len, *p;
+ struct adapter *adapter = netdev2adap(dev);
+
+ if (eeprom->magic != EEPROM_MAGIC)
+ return -EINVAL;
+
+ aligned_offset = eeprom->offset & ~3;
+ aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3;
+
+ if (adapter->fn > 0) {
+ u32 start = 1024 + adapter->fn * EEPROMPFSIZE;
+
+ if (aligned_offset < start ||
+ aligned_offset + aligned_len > start + EEPROMPFSIZE)
+ return -EPERM;
+ }
+
+ if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) {
+ /* RMW possibly needed for first or last words.
+ */
+ buf = kmalloc(aligned_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf);
+ if (!err && aligned_len > 4)
+ err = eeprom_rd_phys(adapter,
+ aligned_offset + aligned_len - 4,
+ (u32 *)&buf[aligned_len - 4]);
+ if (err)
+ goto out;
+ memcpy(buf + (eeprom->offset & 3), data, eeprom->len);
+ } else {
+ buf = data;
+ }
+
+ err = t4_seeprom_wp(adapter, false);
+ if (err)
+ goto out;
+
+ for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) {
+ err = eeprom_wr_phys(adapter, aligned_offset, *p);
+ aligned_offset += 4;
+ }
+
+ if (!err)
+ err = t4_seeprom_wp(adapter, true);
+out:
+ if (buf != data)
+ kfree(buf);
+ return err;
+}
+
+static int set_flash(struct net_device *netdev, struct ethtool_flash *ef)
+{
+ int ret;
+ const struct firmware *fw;
+ struct adapter *adap = netdev2adap(netdev);
+ unsigned int mbox = PCIE_FW_MASTER_M + 1;
+
+ ef->data[sizeof(ef->data) - 1] = '\0';
+ ret = request_firmware(&fw, ef->data, adap->pdev_dev);
+ if (ret < 0)
+ return ret;
+
+ /* If the adapter has been fully initialized then we'll go ahead and
+ * try to get the firmware's cooperation in upgrading to the new
+ * firmware image otherwise we'll try to do the entire job from the
+ * host ... and we always "force" the operation in this path.
+ */
+ if (adap->flags & FULL_INIT_DONE)
+ mbox = adap->mbox;
+
+ ret = t4_fw_upgrade(adap, mbox, fw->data, fw->size, 1);
+ release_firmware(fw);
+ if (!ret)
+ dev_info(adap->pdev_dev,
+ "loaded firmware %s, reload cxgb4 driver\n", ef->data);
+ return ret;
+}
+
+#define WOL_SUPPORTED (WAKE_BCAST | WAKE_MAGIC)
+#define BCAST_CRC 0xa0ccc1a6
+
+static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ wol->supported = WAKE_BCAST | WAKE_MAGIC;
+ wol->wolopts = netdev2adap(dev)->wol;
+ memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+
+static int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ int err = 0;
+ struct port_info *pi = netdev_priv(dev);
+
+ if (wol->wolopts & ~WOL_SUPPORTED)
+ return -EINVAL;
+ t4_wol_magic_enable(pi->adapter, pi->tx_chan,
+ (wol->wolopts & WAKE_MAGIC) ? dev->dev_addr : NULL);
+ if (wol->wolopts & WAKE_BCAST) {
+ err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0xfe, ~0ULL,
+ ~0ULL, 0, false);
+ if (!err)
+ err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 1,
+ ~6ULL, ~0ULL, BCAST_CRC, true);
+ } else {
+ t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0, 0, 0, 0, false);
+ }
+ return err;
+}
+
+static u32 get_rss_table_size(struct net_device *dev)
+{
+ const struct port_info *pi = netdev_priv(dev);
+
+ return pi->rss_size;
+}
+
+static int get_rss_table(struct net_device *dev, u32 *p, u8 *key, u8 *hfunc)
+{
+ const struct port_info *pi = netdev_priv(dev);
+ unsigned int n = pi->rss_size;
+
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+ if (!p)
+ return 0;
+ while (n--)
+ p[n] = pi->rss[n];
+ return 0;
+}
+
+static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key,
+ const u8 hfunc)
+{
+ unsigned int i;
+ struct port_info *pi = netdev_priv(dev);
+
+ /* We require at least one supported parameter to be changed and no
+ * change in any of the unsupported parameters
+ */
+ if (key ||
+ (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+ return -EOPNOTSUPP;
+ if (!p)
+ return 0;
+
+ for (i = 0; i < pi->rss_size; i++)
+ pi->rss[i] = p[i];
+ if (pi->adapter->flags & FULL_INIT_DONE)
+ return cxgb4_write_rss(pi, pi->rss);
+ return 0;
+}
+
+static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
+ u32 *rules)
+{
+ const struct port_info *pi = netdev_priv(dev);
+
+ switch (info->cmd) {
+ case ETHTOOL_GRXFH: {
+ unsigned int v = pi->rss_mode;
+
+ info->data = 0;
+ switch (info->flow_type) {
+ case TCP_V4_FLOW:
+ if (v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F)
+ info->data = RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
+ info->data = RXH_IP_SRC | RXH_IP_DST;
+ break;
+ case UDP_V4_FLOW:
+ if ((v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) &&
+ (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F))
+ info->data = RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
+ info->data = RXH_IP_SRC | RXH_IP_DST;
+ break;
+ case SCTP_V4_FLOW:
+ case AH_ESP_V4_FLOW:
+ case IPV4_FLOW:
+ if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
+ info->data = RXH_IP_SRC | RXH_IP_DST;
+ break;
+ case TCP_V6_FLOW:
+ if (v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F)
+ info->data = RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
+ info->data = RXH_IP_SRC | RXH_IP_DST;
+ break;
+ case UDP_V6_FLOW:
+ if ((v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) &&
+ (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F))
+ info->data = RXH_IP_SRC | RXH_IP_DST |
+ RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
+ info->data = RXH_IP_SRC | RXH_IP_DST;
+ break;
+ case SCTP_V6_FLOW:
+ case AH_ESP_V6_FLOW:
+ case IPV6_FLOW:
+ if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
+ info->data = RXH_IP_SRC | RXH_IP_DST;
+ break;
+ }
+ return 0;
+ }
+ case ETHTOOL_GRXRINGS:
+ info->data = pi->nqsets;
+ return 0;
+ }
+ return -EOPNOTSUPP;
+}
+
+static const struct ethtool_ops cxgb_ethtool_ops = {
+ .get_settings = get_settings,
+ .set_settings = set_settings,
+ .get_drvinfo = get_drvinfo,
+ .get_msglevel = get_msglevel,
+ .set_msglevel = set_msglevel,
+ .get_ringparam = get_sge_param,
+ .set_ringparam = set_sge_param,
+ .get_coalesce = get_coalesce,
+ .set_coalesce = set_coalesce,
+ .get_eeprom_len = get_eeprom_len,
+ .get_eeprom = get_eeprom,
+ .set_eeprom = set_eeprom,
+ .get_pauseparam = get_pauseparam,
+ .set_pauseparam = set_pauseparam,
+ .get_link = ethtool_op_get_link,
+ .get_strings = get_strings,
+ .set_phys_id = identify_port,
+ .nway_reset = restart_autoneg,
+ .get_sset_count = get_sset_count,
+ .get_ethtool_stats = get_stats,
+ .get_regs_len = get_regs_len,
+ .get_regs = get_regs,
+ .get_wol = get_wol,
+ .set_wol = set_wol,
+ .get_rxnfc = get_rxnfc,
+ .get_rxfh_indir_size = get_rss_table_size,
+ .get_rxfh = get_rss_table,
+ .set_rxfh = set_rss_table,
+ .flash_device = set_flash,
+};
+
+void cxgb4_set_ethtool_ops(struct net_device *netdev)
+{
+ netdev->ethtool_ops = &cxgb_ethtool_ops;
+}
#include "clip_tbl.h"
#include "l2t.h"
+char cxgb4_driver_name[] = KBUILD_MODNAME;
+
#ifdef DRV_VERSION
#undef DRV_VERSION
#endif
#define DRV_VERSION "2.0.0-ko"
+const char cxgb4_driver_version[] = DRV_VERSION;
#define DRV_DESC "Chelsio T4/T5 Network Driver"
-enum {
- MAX_TXQ_ENTRIES = 16384,
- MAX_CTRL_TXQ_ENTRIES = 1024,
- MAX_RSPQ_ENTRIES = 16384,
- MAX_RX_BUFFERS = 16384,
- MIN_TXQ_ENTRIES = 32,
- MIN_CTRL_TXQ_ENTRIES = 32,
- MIN_RSPQ_ENTRIES = 128,
- MIN_FL_ENTRIES = 16
-};
-
/* Host shadow copy of ingress filter entry. This is in host native format
* and doesn't match the ordering or bit order, etc. of the hardware of the
* firmware command. The use of bit-field structure elements is purely to
}
/**
- * write_rss - write the RSS table for a given port
+ * cxgb4_write_rss - write the RSS table for a given port
* @pi: the port
* @queues: array of queue indices for RSS
*
* Sets up the portion of the HW RSS table for the port's VI to distribute
* packets to the Rx queues in @queues.
*/
-static int write_rss(const struct port_info *pi, const u16 *queues)
+int cxgb4_write_rss(const struct port_info *pi, const u16 *queues)
{
u16 *rss;
int i, err;
for_each_port(adap, i) {
const struct port_info *pi = adap2pinfo(adap, i);
- err = write_rss(pi, pi->rss);
+ err = cxgb4_write_rss(pi, pi->rss);
if (err)
return err;
}
{
int i;
- for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) {
+ for (i = 0; i < adap->sge.ingr_sz; i++) {
struct sge_rspq *q = adap->sge.ingr_map[i];
if (q && q->handler) {
}
}
+/* Disable interrupt and napi handler */
+static void disable_interrupts(struct adapter *adap)
+{
+ if (adap->flags & FULL_INIT_DONE) {
+ t4_intr_disable(adap);
+ if (adap->flags & USING_MSIX) {
+ free_msix_queue_irqs(adap);
+ free_irq(adap->msix_info[0].vec, adap);
+ } else {
+ free_irq(adap->pdev->irq, adap);
+ }
+ quiesce_rx(adap);
+ }
+}
+
/*
* Enable NAPI scheduling and interrupt generation for all Rx queues.
*/
{
int i;
- for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) {
+ for (i = 0; i < adap->sge.ingr_sz; i++) {
struct sge_rspq *q = adap->sge.ingr_map[i];
if (!q)
int err, msi_idx, i, j;
struct sge *s = &adap->sge;
- bitmap_zero(s->starving_fl, MAX_EGRQ);
- bitmap_zero(s->txq_maperr, MAX_EGRQ);
+ bitmap_zero(s->starving_fl, s->egr_sz);
+ bitmap_zero(s->txq_maperr, s->egr_sz);
if (adap->flags & USING_MSIX)
msi_idx = 1; /* vector 0 is for non-queue interrupts */
msi_idx = -((int)s->intrq.abs_id + 1);
}
+ /* NOTE: If you add/delete any Ingress/Egress Queue allocations in here,
+ * don't forget to update the following which need to be
+ * synchronized to and changes here.
+ *
+ * 1. The calculations of MAX_INGQ in cxgb4.h.
+ *
+ * 2. Update enable_msix/name_msix_vecs/request_msix_queue_irqs
+ * to accommodate any new/deleted Ingress Queues
+ * which need MSI-X Vectors.
+ *
+ * 3. Update sge_qinfo_show() to include information on the
+ * new/deleted queues.
+ */
err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0],
msi_idx, NULL, fwevtq_handler);
if (err) {
return adap->params.offload;
}
-/*
- * Implementation of ethtool operations.
- */
-
-static u32 get_msglevel(struct net_device *dev)
-{
- return netdev2adap(dev)->msg_enable;
-}
-
-static void set_msglevel(struct net_device *dev, u32 val)
-{
- netdev2adap(dev)->msg_enable = val;
-}
-
-static char stats_strings[][ETH_GSTRING_LEN] = {
- "TxOctetsOK ",
- "TxFramesOK ",
- "TxBroadcastFrames ",
- "TxMulticastFrames ",
- "TxUnicastFrames ",
- "TxErrorFrames ",
-
- "TxFrames64 ",
- "TxFrames65To127 ",
- "TxFrames128To255 ",
- "TxFrames256To511 ",
- "TxFrames512To1023 ",
- "TxFrames1024To1518 ",
- "TxFrames1519ToMax ",
-
- "TxFramesDropped ",
- "TxPauseFrames ",
- "TxPPP0Frames ",
- "TxPPP1Frames ",
- "TxPPP2Frames ",
- "TxPPP3Frames ",
- "TxPPP4Frames ",
- "TxPPP5Frames ",
- "TxPPP6Frames ",
- "TxPPP7Frames ",
-
- "RxOctetsOK ",
- "RxFramesOK ",
- "RxBroadcastFrames ",
- "RxMulticastFrames ",
- "RxUnicastFrames ",
-
- "RxFramesTooLong ",
- "RxJabberErrors ",
- "RxFCSErrors ",
- "RxLengthErrors ",
- "RxSymbolErrors ",
- "RxRuntFrames ",
-
- "RxFrames64 ",
- "RxFrames65To127 ",
- "RxFrames128To255 ",
- "RxFrames256To511 ",
- "RxFrames512To1023 ",
- "RxFrames1024To1518 ",
- "RxFrames1519ToMax ",
-
- "RxPauseFrames ",
- "RxPPP0Frames ",
- "RxPPP1Frames ",
- "RxPPP2Frames ",
- "RxPPP3Frames ",
- "RxPPP4Frames ",
- "RxPPP5Frames ",
- "RxPPP6Frames ",
- "RxPPP7Frames ",
-
- "RxBG0FramesDropped ",
- "RxBG1FramesDropped ",
- "RxBG2FramesDropped ",
- "RxBG3FramesDropped ",
- "RxBG0FramesTrunc ",
- "RxBG1FramesTrunc ",
- "RxBG2FramesTrunc ",
- "RxBG3FramesTrunc ",
-
- "TSO ",
- "TxCsumOffload ",
- "RxCsumGood ",
- "VLANextractions ",
- "VLANinsertions ",
- "GROpackets ",
- "GROmerged ",
- "WriteCoalSuccess ",
- "WriteCoalFail ",
-};
-
-static int get_sset_count(struct net_device *dev, int sset)
-{
- switch (sset) {
- case ETH_SS_STATS:
- return ARRAY_SIZE(stats_strings);
- default:
- return -EOPNOTSUPP;
- }
-}
-
-#define T4_REGMAP_SIZE (160 * 1024)
-#define T5_REGMAP_SIZE (332 * 1024)
-
-static int get_regs_len(struct net_device *dev)
-{
- struct adapter *adap = netdev2adap(dev);
- if (is_t4(adap->params.chip))
- return T4_REGMAP_SIZE;
- else
- return T5_REGMAP_SIZE;
-}
-
-static int get_eeprom_len(struct net_device *dev)
-{
- return EEPROMSIZE;
-}
-
-static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
- struct adapter *adapter = netdev2adap(dev);
- u32 exprom_vers;
-
- strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
- strlcpy(info->version, DRV_VERSION, sizeof(info->version));
- strlcpy(info->bus_info, pci_name(adapter->pdev),
- sizeof(info->bus_info));
-
- if (adapter->params.fw_vers)
- snprintf(info->fw_version, sizeof(info->fw_version),
- "%u.%u.%u.%u, TP %u.%u.%u.%u",
- FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers),
- FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers),
- FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers),
- FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers),
- FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers),
- FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers),
- FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers),
- FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers));
-
- if (!t4_get_exprom_version(adapter, &exprom_vers))
- snprintf(info->erom_version, sizeof(info->erom_version),
- "%u.%u.%u.%u",
- FW_HDR_FW_VER_MAJOR_G(exprom_vers),
- FW_HDR_FW_VER_MINOR_G(exprom_vers),
- FW_HDR_FW_VER_MICRO_G(exprom_vers),
- FW_HDR_FW_VER_BUILD_G(exprom_vers));
-}
-
-static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
-{
- if (stringset == ETH_SS_STATS)
- memcpy(data, stats_strings, sizeof(stats_strings));
-}
-
-/*
- * port stats maintained per queue of the port. They should be in the same
- * order as in stats_strings above.
- */
-struct queue_port_stats {
- u64 tso;
- u64 tx_csum;
- u64 rx_csum;
- u64 vlan_ex;
- u64 vlan_ins;
- u64 gro_pkts;
- u64 gro_merged;
-};
-
-static void collect_sge_port_stats(const struct adapter *adap,
- const struct port_info *p, struct queue_port_stats *s)
-{
- int i;
- const struct sge_eth_txq *tx = &adap->sge.ethtxq[p->first_qset];
- const struct sge_eth_rxq *rx = &adap->sge.ethrxq[p->first_qset];
-
- memset(s, 0, sizeof(*s));
- for (i = 0; i < p->nqsets; i++, rx++, tx++) {
- s->tso += tx->tso;
- s->tx_csum += tx->tx_cso;
- s->rx_csum += rx->stats.rx_cso;
- s->vlan_ex += rx->stats.vlan_ex;
- s->vlan_ins += tx->vlan_ins;
- s->gro_pkts += rx->stats.lro_pkts;
- s->gro_merged += rx->stats.lro_merged;
- }
-}
-
-static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
- u64 *data)
-{
- struct port_info *pi = netdev_priv(dev);
- struct adapter *adapter = pi->adapter;
- u32 val1, val2;
-
- t4_get_port_stats(adapter, pi->tx_chan, (struct port_stats *)data);
-
- data += sizeof(struct port_stats) / sizeof(u64);
- collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data);
- data += sizeof(struct queue_port_stats) / sizeof(u64);
- if (!is_t4(adapter->params.chip)) {
- t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7));
- val1 = t4_read_reg(adapter, SGE_STAT_TOTAL_A);
- val2 = t4_read_reg(adapter, SGE_STAT_MATCH_A);
- *data = val1 - val2;
- data++;
- *data = val2;
- data++;
- } else {
- memset(data, 0, 2 * sizeof(u64));
- *data += 2;
- }
-}
-
-/*
- * Return a version number to identify the type of adapter. The scheme is:
- * - bits 0..9: chip version
- * - bits 10..15: chip revision
- * - bits 16..23: register dump version
- */
-static inline unsigned int mk_adap_vers(const struct adapter *ap)
-{
- return CHELSIO_CHIP_VERSION(ap->params.chip) |
- (CHELSIO_CHIP_RELEASE(ap->params.chip) << 10) | (1 << 16);
-}
-
-static void reg_block_dump(struct adapter *ap, void *buf, unsigned int start,
- unsigned int end)
-{
- u32 *p = buf + start;
-
- for ( ; start <= end; start += sizeof(u32))
- *p++ = t4_read_reg(ap, start);
-}
-
-static void get_regs(struct net_device *dev, struct ethtool_regs *regs,
- void *buf)
-{
- static const unsigned int t4_reg_ranges[] = {
- 0x1008, 0x1108,
- 0x1180, 0x11b4,
- 0x11fc, 0x123c,
- 0x1300, 0x173c,
- 0x1800, 0x18fc,
- 0x3000, 0x30d8,
- 0x30e0, 0x5924,
- 0x5960, 0x59d4,
- 0x5a00, 0x5af8,
- 0x6000, 0x6098,
- 0x6100, 0x6150,
- 0x6200, 0x6208,
- 0x6240, 0x6248,
- 0x6280, 0x6338,
- 0x6370, 0x638c,
- 0x6400, 0x643c,
- 0x6500, 0x6524,
- 0x6a00, 0x6a38,
- 0x6a60, 0x6a78,
- 0x6b00, 0x6b84,
- 0x6bf0, 0x6c84,
- 0x6cf0, 0x6d84,
- 0x6df0, 0x6e84,
- 0x6ef0, 0x6f84,
- 0x6ff0, 0x7084,
- 0x70f0, 0x7184,
- 0x71f0, 0x7284,
- 0x72f0, 0x7384,
- 0x73f0, 0x7450,
- 0x7500, 0x7530,
- 0x7600, 0x761c,
- 0x7680, 0x76cc,
- 0x7700, 0x7798,
- 0x77c0, 0x77fc,
- 0x7900, 0x79fc,
- 0x7b00, 0x7c38,
- 0x7d00, 0x7efc,
- 0x8dc0, 0x8e1c,
- 0x8e30, 0x8e78,
- 0x8ea0, 0x8f6c,
- 0x8fc0, 0x9074,
- 0x90fc, 0x90fc,
- 0x9400, 0x9458,
- 0x9600, 0x96bc,
- 0x9800, 0x9808,
- 0x9820, 0x983c,
- 0x9850, 0x9864,
- 0x9c00, 0x9c6c,
- 0x9c80, 0x9cec,
- 0x9d00, 0x9d6c,
- 0x9d80, 0x9dec,
- 0x9e00, 0x9e6c,
- 0x9e80, 0x9eec,
- 0x9f00, 0x9f6c,
- 0x9f80, 0x9fec,
- 0xd004, 0xd03c,
- 0xdfc0, 0xdfe0,
- 0xe000, 0xea7c,
- 0xf000, 0x11110,
- 0x11118, 0x11190,
- 0x19040, 0x1906c,
- 0x19078, 0x19080,
- 0x1908c, 0x19124,
- 0x19150, 0x191b0,
- 0x191d0, 0x191e8,
- 0x19238, 0x1924c,
- 0x193f8, 0x19474,
- 0x19490, 0x194f8,
- 0x19800, 0x19f30,
- 0x1a000, 0x1a06c,
- 0x1a0b0, 0x1a120,
- 0x1a128, 0x1a138,
- 0x1a190, 0x1a1c4,
- 0x1a1fc, 0x1a1fc,
- 0x1e040, 0x1e04c,
- 0x1e284, 0x1e28c,
- 0x1e2c0, 0x1e2c0,
- 0x1e2e0, 0x1e2e0,
- 0x1e300, 0x1e384,
- 0x1e3c0, 0x1e3c8,
- 0x1e440, 0x1e44c,
- 0x1e684, 0x1e68c,
- 0x1e6c0, 0x1e6c0,
- 0x1e6e0, 0x1e6e0,
- 0x1e700, 0x1e784,
- 0x1e7c0, 0x1e7c8,
- 0x1e840, 0x1e84c,
- 0x1ea84, 0x1ea8c,
- 0x1eac0, 0x1eac0,
- 0x1eae0, 0x1eae0,
- 0x1eb00, 0x1eb84,
- 0x1ebc0, 0x1ebc8,
- 0x1ec40, 0x1ec4c,
- 0x1ee84, 0x1ee8c,
- 0x1eec0, 0x1eec0,
- 0x1eee0, 0x1eee0,
- 0x1ef00, 0x1ef84,
- 0x1efc0, 0x1efc8,
- 0x1f040, 0x1f04c,
- 0x1f284, 0x1f28c,
- 0x1f2c0, 0x1f2c0,
- 0x1f2e0, 0x1f2e0,
- 0x1f300, 0x1f384,
- 0x1f3c0, 0x1f3c8,
- 0x1f440, 0x1f44c,
- 0x1f684, 0x1f68c,
- 0x1f6c0, 0x1f6c0,
- 0x1f6e0, 0x1f6e0,
- 0x1f700, 0x1f784,
- 0x1f7c0, 0x1f7c8,
- 0x1f840, 0x1f84c,
- 0x1fa84, 0x1fa8c,
- 0x1fac0, 0x1fac0,
- 0x1fae0, 0x1fae0,
- 0x1fb00, 0x1fb84,
- 0x1fbc0, 0x1fbc8,
- 0x1fc40, 0x1fc4c,
- 0x1fe84, 0x1fe8c,
- 0x1fec0, 0x1fec0,
- 0x1fee0, 0x1fee0,
- 0x1ff00, 0x1ff84,
- 0x1ffc0, 0x1ffc8,
- 0x20000, 0x2002c,
- 0x20100, 0x2013c,
- 0x20190, 0x201c8,
- 0x20200, 0x20318,
- 0x20400, 0x20528,
- 0x20540, 0x20614,
- 0x21000, 0x21040,
- 0x2104c, 0x21060,
- 0x210c0, 0x210ec,
- 0x21200, 0x21268,
- 0x21270, 0x21284,
- 0x212fc, 0x21388,
- 0x21400, 0x21404,
- 0x21500, 0x21518,
- 0x2152c, 0x2153c,
- 0x21550, 0x21554,
- 0x21600, 0x21600,
- 0x21608, 0x21628,
- 0x21630, 0x2163c,
- 0x21700, 0x2171c,
- 0x21780, 0x2178c,
- 0x21800, 0x21c38,
- 0x21c80, 0x21d7c,
- 0x21e00, 0x21e04,
- 0x22000, 0x2202c,
- 0x22100, 0x2213c,
- 0x22190, 0x221c8,
- 0x22200, 0x22318,
- 0x22400, 0x22528,
- 0x22540, 0x22614,
- 0x23000, 0x23040,
- 0x2304c, 0x23060,
- 0x230c0, 0x230ec,
- 0x23200, 0x23268,
- 0x23270, 0x23284,
- 0x232fc, 0x23388,
- 0x23400, 0x23404,
- 0x23500, 0x23518,
- 0x2352c, 0x2353c,
- 0x23550, 0x23554,
- 0x23600, 0x23600,
- 0x23608, 0x23628,
- 0x23630, 0x2363c,
- 0x23700, 0x2371c,
- 0x23780, 0x2378c,
- 0x23800, 0x23c38,
- 0x23c80, 0x23d7c,
- 0x23e00, 0x23e04,
- 0x24000, 0x2402c,
- 0x24100, 0x2413c,
- 0x24190, 0x241c8,
- 0x24200, 0x24318,
- 0x24400, 0x24528,
- 0x24540, 0x24614,
- 0x25000, 0x25040,
- 0x2504c, 0x25060,
- 0x250c0, 0x250ec,
- 0x25200, 0x25268,
- 0x25270, 0x25284,
- 0x252fc, 0x25388,
- 0x25400, 0x25404,
- 0x25500, 0x25518,
- 0x2552c, 0x2553c,
- 0x25550, 0x25554,
- 0x25600, 0x25600,
- 0x25608, 0x25628,
- 0x25630, 0x2563c,
- 0x25700, 0x2571c,
- 0x25780, 0x2578c,
- 0x25800, 0x25c38,
- 0x25c80, 0x25d7c,
- 0x25e00, 0x25e04,
- 0x26000, 0x2602c,
- 0x26100, 0x2613c,
- 0x26190, 0x261c8,
- 0x26200, 0x26318,
- 0x26400, 0x26528,
- 0x26540, 0x26614,
- 0x27000, 0x27040,
- 0x2704c, 0x27060,
- 0x270c0, 0x270ec,
- 0x27200, 0x27268,
- 0x27270, 0x27284,
- 0x272fc, 0x27388,
- 0x27400, 0x27404,
- 0x27500, 0x27518,
- 0x2752c, 0x2753c,
- 0x27550, 0x27554,
- 0x27600, 0x27600,
- 0x27608, 0x27628,
- 0x27630, 0x2763c,
- 0x27700, 0x2771c,
- 0x27780, 0x2778c,
- 0x27800, 0x27c38,
- 0x27c80, 0x27d7c,
- 0x27e00, 0x27e04
- };
-
- static const unsigned int t5_reg_ranges[] = {
- 0x1008, 0x1148,
- 0x1180, 0x11b4,
- 0x11fc, 0x123c,
- 0x1280, 0x173c,
- 0x1800, 0x18fc,
- 0x3000, 0x3028,
- 0x3060, 0x30d8,
- 0x30e0, 0x30fc,
- 0x3140, 0x357c,
- 0x35a8, 0x35cc,
- 0x35ec, 0x35ec,
- 0x3600, 0x5624,
- 0x56cc, 0x575c,
- 0x580c, 0x5814,
- 0x5890, 0x58bc,
- 0x5940, 0x59dc,
- 0x59fc, 0x5a18,
- 0x5a60, 0x5a9c,
- 0x5b9c, 0x5bfc,
- 0x6000, 0x6040,
- 0x6058, 0x614c,
- 0x7700, 0x7798,
- 0x77c0, 0x78fc,
- 0x7b00, 0x7c54,
- 0x7d00, 0x7efc,
- 0x8dc0, 0x8de0,
- 0x8df8, 0x8e84,
- 0x8ea0, 0x8f84,
- 0x8fc0, 0x90f8,
- 0x9400, 0x9470,
- 0x9600, 0x96f4,
- 0x9800, 0x9808,
- 0x9820, 0x983c,
- 0x9850, 0x9864,
- 0x9c00, 0x9c6c,
- 0x9c80, 0x9cec,
- 0x9d00, 0x9d6c,
- 0x9d80, 0x9dec,
- 0x9e00, 0x9e6c,
- 0x9e80, 0x9eec,
- 0x9f00, 0x9f6c,
- 0x9f80, 0xa020,
- 0xd004, 0xd03c,
- 0xdfc0, 0xdfe0,
- 0xe000, 0x11088,
- 0x1109c, 0x11110,
- 0x11118, 0x1117c,
- 0x11190, 0x11204,
- 0x19040, 0x1906c,
- 0x19078, 0x19080,
- 0x1908c, 0x19124,
- 0x19150, 0x191b0,
- 0x191d0, 0x191e8,
- 0x19238, 0x19290,
- 0x193f8, 0x19474,
- 0x19490, 0x194cc,
- 0x194f0, 0x194f8,
- 0x19c00, 0x19c60,
- 0x19c94, 0x19e10,
- 0x19e50, 0x19f34,
- 0x19f40, 0x19f50,
- 0x19f90, 0x19fe4,
- 0x1a000, 0x1a06c,
- 0x1a0b0, 0x1a120,
- 0x1a128, 0x1a138,
- 0x1a190, 0x1a1c4,
- 0x1a1fc, 0x1a1fc,
- 0x1e008, 0x1e00c,
- 0x1e040, 0x1e04c,
- 0x1e284, 0x1e290,
- 0x1e2c0, 0x1e2c0,
- 0x1e2e0, 0x1e2e0,
- 0x1e300, 0x1e384,
- 0x1e3c0, 0x1e3c8,
- 0x1e408, 0x1e40c,
- 0x1e440, 0x1e44c,
- 0x1e684, 0x1e690,
- 0x1e6c0, 0x1e6c0,
- 0x1e6e0, 0x1e6e0,
- 0x1e700, 0x1e784,
- 0x1e7c0, 0x1e7c8,
- 0x1e808, 0x1e80c,
- 0x1e840, 0x1e84c,
- 0x1ea84, 0x1ea90,
- 0x1eac0, 0x1eac0,
- 0x1eae0, 0x1eae0,
- 0x1eb00, 0x1eb84,
- 0x1ebc0, 0x1ebc8,
- 0x1ec08, 0x1ec0c,
- 0x1ec40, 0x1ec4c,
- 0x1ee84, 0x1ee90,
- 0x1eec0, 0x1eec0,
- 0x1eee0, 0x1eee0,
- 0x1ef00, 0x1ef84,
- 0x1efc0, 0x1efc8,
- 0x1f008, 0x1f00c,
- 0x1f040, 0x1f04c,
- 0x1f284, 0x1f290,
- 0x1f2c0, 0x1f2c0,
- 0x1f2e0, 0x1f2e0,
- 0x1f300, 0x1f384,
- 0x1f3c0, 0x1f3c8,
- 0x1f408, 0x1f40c,
- 0x1f440, 0x1f44c,
- 0x1f684, 0x1f690,
- 0x1f6c0, 0x1f6c0,
- 0x1f6e0, 0x1f6e0,
- 0x1f700, 0x1f784,
- 0x1f7c0, 0x1f7c8,
- 0x1f808, 0x1f80c,
- 0x1f840, 0x1f84c,
- 0x1fa84, 0x1fa90,
- 0x1fac0, 0x1fac0,
- 0x1fae0, 0x1fae0,
- 0x1fb00, 0x1fb84,
- 0x1fbc0, 0x1fbc8,
- 0x1fc08, 0x1fc0c,
- 0x1fc40, 0x1fc4c,
- 0x1fe84, 0x1fe90,
- 0x1fec0, 0x1fec0,
- 0x1fee0, 0x1fee0,
- 0x1ff00, 0x1ff84,
- 0x1ffc0, 0x1ffc8,
- 0x30000, 0x30030,
- 0x30100, 0x30144,
- 0x30190, 0x301d0,
- 0x30200, 0x30318,
- 0x30400, 0x3052c,
- 0x30540, 0x3061c,
- 0x30800, 0x30834,
- 0x308c0, 0x30908,
- 0x30910, 0x309ac,
- 0x30a00, 0x30a04,
- 0x30a0c, 0x30a2c,
- 0x30a44, 0x30a50,
- 0x30a74, 0x30c24,
- 0x30d08, 0x30d14,
- 0x30d1c, 0x30d20,
- 0x30d3c, 0x30d50,
- 0x31200, 0x3120c,
- 0x31220, 0x31220,
- 0x31240, 0x31240,
- 0x31600, 0x31600,
- 0x31608, 0x3160c,
- 0x31a00, 0x31a1c,
- 0x31e04, 0x31e20,
- 0x31e38, 0x31e3c,
- 0x31e80, 0x31e80,
- 0x31e88, 0x31ea8,
- 0x31eb0, 0x31eb4,
- 0x31ec8, 0x31ed4,
- 0x31fb8, 0x32004,
- 0x32208, 0x3223c,
- 0x32600, 0x32630,
- 0x32a00, 0x32abc,
- 0x32b00, 0x32b70,
- 0x33000, 0x33048,
- 0x33060, 0x3309c,
- 0x330f0, 0x33148,
- 0x33160, 0x3319c,
- 0x331f0, 0x332e4,
- 0x332f8, 0x333e4,
- 0x333f8, 0x33448,
- 0x33460, 0x3349c,
- 0x334f0, 0x33548,
- 0x33560, 0x3359c,
- 0x335f0, 0x336e4,
- 0x336f8, 0x337e4,
- 0x337f8, 0x337fc,
- 0x33814, 0x33814,
- 0x3382c, 0x3382c,
- 0x33880, 0x3388c,
- 0x338e8, 0x338ec,
- 0x33900, 0x33948,
- 0x33960, 0x3399c,
- 0x339f0, 0x33ae4,
- 0x33af8, 0x33b10,
- 0x33b28, 0x33b28,
- 0x33b3c, 0x33b50,
- 0x33bf0, 0x33c10,
- 0x33c28, 0x33c28,
- 0x33c3c, 0x33c50,
- 0x33cf0, 0x33cfc,
- 0x34000, 0x34030,
- 0x34100, 0x34144,
- 0x34190, 0x341d0,
- 0x34200, 0x34318,
- 0x34400, 0x3452c,
- 0x34540, 0x3461c,
- 0x34800, 0x34834,
- 0x348c0, 0x34908,
- 0x34910, 0x349ac,
- 0x34a00, 0x34a04,
- 0x34a0c, 0x34a2c,
- 0x34a44, 0x34a50,
- 0x34a74, 0x34c24,
- 0x34d08, 0x34d14,
- 0x34d1c, 0x34d20,
- 0x34d3c, 0x34d50,
- 0x35200, 0x3520c,
- 0x35220, 0x35220,
- 0x35240, 0x35240,
- 0x35600, 0x35600,
- 0x35608, 0x3560c,
- 0x35a00, 0x35a1c,
- 0x35e04, 0x35e20,
- 0x35e38, 0x35e3c,
- 0x35e80, 0x35e80,
- 0x35e88, 0x35ea8,
- 0x35eb0, 0x35eb4,
- 0x35ec8, 0x35ed4,
- 0x35fb8, 0x36004,
- 0x36208, 0x3623c,
- 0x36600, 0x36630,
- 0x36a00, 0x36abc,
- 0x36b00, 0x36b70,
- 0x37000, 0x37048,
- 0x37060, 0x3709c,
- 0x370f0, 0x37148,
- 0x37160, 0x3719c,
- 0x371f0, 0x372e4,
- 0x372f8, 0x373e4,
- 0x373f8, 0x37448,
- 0x37460, 0x3749c,
- 0x374f0, 0x37548,
- 0x37560, 0x3759c,
- 0x375f0, 0x376e4,
- 0x376f8, 0x377e4,
- 0x377f8, 0x377fc,
- 0x37814, 0x37814,
- 0x3782c, 0x3782c,
- 0x37880, 0x3788c,
- 0x378e8, 0x378ec,
- 0x37900, 0x37948,
- 0x37960, 0x3799c,
- 0x379f0, 0x37ae4,
- 0x37af8, 0x37b10,
- 0x37b28, 0x37b28,
- 0x37b3c, 0x37b50,
- 0x37bf0, 0x37c10,
- 0x37c28, 0x37c28,
- 0x37c3c, 0x37c50,
- 0x37cf0, 0x37cfc,
- 0x38000, 0x38030,
- 0x38100, 0x38144,
- 0x38190, 0x381d0,
- 0x38200, 0x38318,
- 0x38400, 0x3852c,
- 0x38540, 0x3861c,
- 0x38800, 0x38834,
- 0x388c0, 0x38908,
- 0x38910, 0x389ac,
- 0x38a00, 0x38a04,
- 0x38a0c, 0x38a2c,
- 0x38a44, 0x38a50,
- 0x38a74, 0x38c24,
- 0x38d08, 0x38d14,
- 0x38d1c, 0x38d20,
- 0x38d3c, 0x38d50,
- 0x39200, 0x3920c,
- 0x39220, 0x39220,
- 0x39240, 0x39240,
- 0x39600, 0x39600,
- 0x39608, 0x3960c,
- 0x39a00, 0x39a1c,
- 0x39e04, 0x39e20,
- 0x39e38, 0x39e3c,
- 0x39e80, 0x39e80,
- 0x39e88, 0x39ea8,
- 0x39eb0, 0x39eb4,
- 0x39ec8, 0x39ed4,
- 0x39fb8, 0x3a004,
- 0x3a208, 0x3a23c,
- 0x3a600, 0x3a630,
- 0x3aa00, 0x3aabc,
- 0x3ab00, 0x3ab70,
- 0x3b000, 0x3b048,
- 0x3b060, 0x3b09c,
- 0x3b0f0, 0x3b148,
- 0x3b160, 0x3b19c,
- 0x3b1f0, 0x3b2e4,
- 0x3b2f8, 0x3b3e4,
- 0x3b3f8, 0x3b448,
- 0x3b460, 0x3b49c,
- 0x3b4f0, 0x3b548,
- 0x3b560, 0x3b59c,
- 0x3b5f0, 0x3b6e4,
- 0x3b6f8, 0x3b7e4,
- 0x3b7f8, 0x3b7fc,
- 0x3b814, 0x3b814,
- 0x3b82c, 0x3b82c,
- 0x3b880, 0x3b88c,
- 0x3b8e8, 0x3b8ec,
- 0x3b900, 0x3b948,
- 0x3b960, 0x3b99c,
- 0x3b9f0, 0x3bae4,
- 0x3baf8, 0x3bb10,
- 0x3bb28, 0x3bb28,
- 0x3bb3c, 0x3bb50,
- 0x3bbf0, 0x3bc10,
- 0x3bc28, 0x3bc28,
- 0x3bc3c, 0x3bc50,
- 0x3bcf0, 0x3bcfc,
- 0x3c000, 0x3c030,
- 0x3c100, 0x3c144,
- 0x3c190, 0x3c1d0,
- 0x3c200, 0x3c318,
- 0x3c400, 0x3c52c,
- 0x3c540, 0x3c61c,
- 0x3c800, 0x3c834,
- 0x3c8c0, 0x3c908,
- 0x3c910, 0x3c9ac,
- 0x3ca00, 0x3ca04,
- 0x3ca0c, 0x3ca2c,
- 0x3ca44, 0x3ca50,
- 0x3ca74, 0x3cc24,
- 0x3cd08, 0x3cd14,
- 0x3cd1c, 0x3cd20,
- 0x3cd3c, 0x3cd50,
- 0x3d200, 0x3d20c,
- 0x3d220, 0x3d220,
- 0x3d240, 0x3d240,
- 0x3d600, 0x3d600,
- 0x3d608, 0x3d60c,
- 0x3da00, 0x3da1c,
- 0x3de04, 0x3de20,
- 0x3de38, 0x3de3c,
- 0x3de80, 0x3de80,
- 0x3de88, 0x3dea8,
- 0x3deb0, 0x3deb4,
- 0x3dec8, 0x3ded4,
- 0x3dfb8, 0x3e004,
- 0x3e208, 0x3e23c,
- 0x3e600, 0x3e630,
- 0x3ea00, 0x3eabc,
- 0x3eb00, 0x3eb70,
- 0x3f000, 0x3f048,
- 0x3f060, 0x3f09c,
- 0x3f0f0, 0x3f148,
- 0x3f160, 0x3f19c,
- 0x3f1f0, 0x3f2e4,
- 0x3f2f8, 0x3f3e4,
- 0x3f3f8, 0x3f448,
- 0x3f460, 0x3f49c,
- 0x3f4f0, 0x3f548,
- 0x3f560, 0x3f59c,
- 0x3f5f0, 0x3f6e4,
- 0x3f6f8, 0x3f7e4,
- 0x3f7f8, 0x3f7fc,
- 0x3f814, 0x3f814,
- 0x3f82c, 0x3f82c,
- 0x3f880, 0x3f88c,
- 0x3f8e8, 0x3f8ec,
- 0x3f900, 0x3f948,
- 0x3f960, 0x3f99c,
- 0x3f9f0, 0x3fae4,
- 0x3faf8, 0x3fb10,
- 0x3fb28, 0x3fb28,
- 0x3fb3c, 0x3fb50,
- 0x3fbf0, 0x3fc10,
- 0x3fc28, 0x3fc28,
- 0x3fc3c, 0x3fc50,
- 0x3fcf0, 0x3fcfc,
- 0x40000, 0x4000c,
- 0x40040, 0x40068,
- 0x40080, 0x40144,
- 0x40180, 0x4018c,
- 0x40200, 0x40298,
- 0x402ac, 0x4033c,
- 0x403f8, 0x403fc,
- 0x41304, 0x413c4,
- 0x41400, 0x4141c,
- 0x41480, 0x414d0,
- 0x44000, 0x44078,
- 0x440c0, 0x44278,
- 0x442c0, 0x44478,
- 0x444c0, 0x44678,
- 0x446c0, 0x44878,
- 0x448c0, 0x449fc,
- 0x45000, 0x45068,
- 0x45080, 0x45084,
- 0x450a0, 0x450b0,
- 0x45200, 0x45268,
- 0x45280, 0x45284,
- 0x452a0, 0x452b0,
- 0x460c0, 0x460e4,
- 0x47000, 0x4708c,
- 0x47200, 0x47250,
- 0x47400, 0x47420,
- 0x47600, 0x47618,
- 0x47800, 0x47814,
- 0x48000, 0x4800c,
- 0x48040, 0x48068,
- 0x48080, 0x48144,
- 0x48180, 0x4818c,
- 0x48200, 0x48298,
- 0x482ac, 0x4833c,
- 0x483f8, 0x483fc,
- 0x49304, 0x493c4,
- 0x49400, 0x4941c,
- 0x49480, 0x494d0,
- 0x4c000, 0x4c078,
- 0x4c0c0, 0x4c278,
- 0x4c2c0, 0x4c478,
- 0x4c4c0, 0x4c678,
- 0x4c6c0, 0x4c878,
- 0x4c8c0, 0x4c9fc,
- 0x4d000, 0x4d068,
- 0x4d080, 0x4d084,
- 0x4d0a0, 0x4d0b0,
- 0x4d200, 0x4d268,
- 0x4d280, 0x4d284,
- 0x4d2a0, 0x4d2b0,
- 0x4e0c0, 0x4e0e4,
- 0x4f000, 0x4f08c,
- 0x4f200, 0x4f250,
- 0x4f400, 0x4f420,
- 0x4f600, 0x4f618,
- 0x4f800, 0x4f814,
- 0x50000, 0x500cc,
- 0x50400, 0x50400,
- 0x50800, 0x508cc,
- 0x50c00, 0x50c00,
- 0x51000, 0x5101c,
- 0x51300, 0x51308,
- };
-
- int i;
- struct adapter *ap = netdev2adap(dev);
- static const unsigned int *reg_ranges;
- int arr_size = 0, buf_size = 0;
-
- if (is_t4(ap->params.chip)) {
- reg_ranges = &t4_reg_ranges[0];
- arr_size = ARRAY_SIZE(t4_reg_ranges);
- buf_size = T4_REGMAP_SIZE;
- } else {
- reg_ranges = &t5_reg_ranges[0];
- arr_size = ARRAY_SIZE(t5_reg_ranges);
- buf_size = T5_REGMAP_SIZE;
- }
-
- regs->version = mk_adap_vers(ap);
-
- memset(buf, 0, buf_size);
- for (i = 0; i < arr_size; i += 2)
- reg_block_dump(ap, buf, reg_ranges[i], reg_ranges[i + 1]);
-}
-
-static int restart_autoneg(struct net_device *dev)
-{
- struct port_info *p = netdev_priv(dev);
-
- if (!netif_running(dev))
- return -EAGAIN;
- if (p->link_cfg.autoneg != AUTONEG_ENABLE)
- return -EINVAL;
- t4_restart_aneg(p->adapter, p->adapter->fn, p->tx_chan);
- return 0;
-}
-
-static int identify_port(struct net_device *dev,
- enum ethtool_phys_id_state state)
-{
- unsigned int val;
- struct adapter *adap = netdev2adap(dev);
-
- if (state == ETHTOOL_ID_ACTIVE)
- val = 0xffff;
- else if (state == ETHTOOL_ID_INACTIVE)
- val = 0;
- else
- return -EINVAL;
-
- return t4_identify_port(adap, adap->fn, netdev2pinfo(dev)->viid, val);
-}
-
-static unsigned int from_fw_linkcaps(enum fw_port_type type, unsigned int caps)
-{
- unsigned int v = 0;
-
- if (type == FW_PORT_TYPE_BT_SGMII || type == FW_PORT_TYPE_BT_XFI ||
- type == FW_PORT_TYPE_BT_XAUI) {
- v |= SUPPORTED_TP;
- if (caps & FW_PORT_CAP_SPEED_100M)
- v |= SUPPORTED_100baseT_Full;
- if (caps & FW_PORT_CAP_SPEED_1G)
- v |= SUPPORTED_1000baseT_Full;
- if (caps & FW_PORT_CAP_SPEED_10G)
- v |= SUPPORTED_10000baseT_Full;
- } else if (type == FW_PORT_TYPE_KX4 || type == FW_PORT_TYPE_KX) {
- v |= SUPPORTED_Backplane;
- if (caps & FW_PORT_CAP_SPEED_1G)
- v |= SUPPORTED_1000baseKX_Full;
- if (caps & FW_PORT_CAP_SPEED_10G)
- v |= SUPPORTED_10000baseKX4_Full;
- } else if (type == FW_PORT_TYPE_KR)
- v |= SUPPORTED_Backplane | SUPPORTED_10000baseKR_Full;
- else if (type == FW_PORT_TYPE_BP_AP)
- v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
- SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full;
- else if (type == FW_PORT_TYPE_BP4_AP)
- v |= SUPPORTED_Backplane | SUPPORTED_10000baseR_FEC |
- SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full |
- SUPPORTED_10000baseKX4_Full;
- else if (type == FW_PORT_TYPE_FIBER_XFI ||
- type == FW_PORT_TYPE_FIBER_XAUI ||
- type == FW_PORT_TYPE_SFP ||
- type == FW_PORT_TYPE_QSFP_10G ||
- type == FW_PORT_TYPE_QSA) {
- v |= SUPPORTED_FIBRE;
- if (caps & FW_PORT_CAP_SPEED_1G)
- v |= SUPPORTED_1000baseT_Full;
- if (caps & FW_PORT_CAP_SPEED_10G)
- v |= SUPPORTED_10000baseT_Full;
- } else if (type == FW_PORT_TYPE_BP40_BA ||
- type == FW_PORT_TYPE_QSFP) {
- v |= SUPPORTED_40000baseSR4_Full;
- v |= SUPPORTED_FIBRE;
- }
-
- if (caps & FW_PORT_CAP_ANEG)
- v |= SUPPORTED_Autoneg;
- return v;
-}
-
-static unsigned int to_fw_linkcaps(unsigned int caps)
-{
- unsigned int v = 0;
-
- if (caps & ADVERTISED_100baseT_Full)
- v |= FW_PORT_CAP_SPEED_100M;
- if (caps & ADVERTISED_1000baseT_Full)
- v |= FW_PORT_CAP_SPEED_1G;
- if (caps & ADVERTISED_10000baseT_Full)
- v |= FW_PORT_CAP_SPEED_10G;
- if (caps & ADVERTISED_40000baseSR4_Full)
- v |= FW_PORT_CAP_SPEED_40G;
- return v;
-}
-
-static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
- const struct port_info *p = netdev_priv(dev);
-
- if (p->port_type == FW_PORT_TYPE_BT_SGMII ||
- p->port_type == FW_PORT_TYPE_BT_XFI ||
- p->port_type == FW_PORT_TYPE_BT_XAUI)
- cmd->port = PORT_TP;
- else if (p->port_type == FW_PORT_TYPE_FIBER_XFI ||
- p->port_type == FW_PORT_TYPE_FIBER_XAUI)
- cmd->port = PORT_FIBRE;
- else if (p->port_type == FW_PORT_TYPE_SFP ||
- p->port_type == FW_PORT_TYPE_QSFP_10G ||
- p->port_type == FW_PORT_TYPE_QSA ||
- p->port_type == FW_PORT_TYPE_QSFP) {
- if (p->mod_type == FW_PORT_MOD_TYPE_LR ||
- p->mod_type == FW_PORT_MOD_TYPE_SR ||
- p->mod_type == FW_PORT_MOD_TYPE_ER ||
- p->mod_type == FW_PORT_MOD_TYPE_LRM)
- cmd->port = PORT_FIBRE;
- else if (p->mod_type == FW_PORT_MOD_TYPE_TWINAX_PASSIVE ||
- p->mod_type == FW_PORT_MOD_TYPE_TWINAX_ACTIVE)
- cmd->port = PORT_DA;
- else
- cmd->port = PORT_OTHER;
- } else
- cmd->port = PORT_OTHER;
-
- if (p->mdio_addr >= 0) {
- cmd->phy_address = p->mdio_addr;
- cmd->transceiver = XCVR_EXTERNAL;
- cmd->mdio_support = p->port_type == FW_PORT_TYPE_BT_SGMII ?
- MDIO_SUPPORTS_C22 : MDIO_SUPPORTS_C45;
- } else {
- cmd->phy_address = 0; /* not really, but no better option */
- cmd->transceiver = XCVR_INTERNAL;
- cmd->mdio_support = 0;
- }
-
- cmd->supported = from_fw_linkcaps(p->port_type, p->link_cfg.supported);
- cmd->advertising = from_fw_linkcaps(p->port_type,
- p->link_cfg.advertising);
- ethtool_cmd_speed_set(cmd,
- netif_carrier_ok(dev) ? p->link_cfg.speed : 0);
- cmd->duplex = DUPLEX_FULL;
- cmd->autoneg = p->link_cfg.autoneg;
- cmd->maxtxpkt = 0;
- cmd->maxrxpkt = 0;
- return 0;
-}
-
-static unsigned int speed_to_caps(int speed)
-{
- if (speed == 100)
- return FW_PORT_CAP_SPEED_100M;
- if (speed == 1000)
- return FW_PORT_CAP_SPEED_1G;
- if (speed == 10000)
- return FW_PORT_CAP_SPEED_10G;
- if (speed == 40000)
- return FW_PORT_CAP_SPEED_40G;
- return 0;
-}
-
-static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
- unsigned int cap;
- struct port_info *p = netdev_priv(dev);
- struct link_config *lc = &p->link_cfg;
- u32 speed = ethtool_cmd_speed(cmd);
-
- if (cmd->duplex != DUPLEX_FULL) /* only full-duplex supported */
- return -EINVAL;
-
- if (!(lc->supported & FW_PORT_CAP_ANEG)) {
- /*
- * PHY offers a single speed. See if that's what's
- * being requested.
- */
- if (cmd->autoneg == AUTONEG_DISABLE &&
- (lc->supported & speed_to_caps(speed)))
- return 0;
- return -EINVAL;
- }
-
- if (cmd->autoneg == AUTONEG_DISABLE) {
- cap = speed_to_caps(speed);
-
- if (!(lc->supported & cap) ||
- (speed == 1000) ||
- (speed == 10000) ||
- (speed == 40000))
- return -EINVAL;
- lc->requested_speed = cap;
- lc->advertising = 0;
- } else {
- cap = to_fw_linkcaps(cmd->advertising);
- if (!(lc->supported & cap))
- return -EINVAL;
- lc->requested_speed = 0;
- lc->advertising = cap | FW_PORT_CAP_ANEG;
- }
- lc->autoneg = cmd->autoneg;
-
- if (netif_running(dev))
- return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
- lc);
- return 0;
-}
-
-static void get_pauseparam(struct net_device *dev,
- struct ethtool_pauseparam *epause)
-{
- struct port_info *p = netdev_priv(dev);
-
- epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0;
- epause->rx_pause = (p->link_cfg.fc & PAUSE_RX) != 0;
- epause->tx_pause = (p->link_cfg.fc & PAUSE_TX) != 0;
-}
-
-static int set_pauseparam(struct net_device *dev,
- struct ethtool_pauseparam *epause)
-{
- struct port_info *p = netdev_priv(dev);
- struct link_config *lc = &p->link_cfg;
-
- if (epause->autoneg == AUTONEG_DISABLE)
- lc->requested_fc = 0;
- else if (lc->supported & FW_PORT_CAP_ANEG)
- lc->requested_fc = PAUSE_AUTONEG;
- else
- return -EINVAL;
-
- if (epause->rx_pause)
- lc->requested_fc |= PAUSE_RX;
- if (epause->tx_pause)
- lc->requested_fc |= PAUSE_TX;
- if (netif_running(dev))
- return t4_link_start(p->adapter, p->adapter->fn, p->tx_chan,
- lc);
- return 0;
-}
-
-static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
-{
- const struct port_info *pi = netdev_priv(dev);
- const struct sge *s = &pi->adapter->sge;
-
- e->rx_max_pending = MAX_RX_BUFFERS;
- e->rx_mini_max_pending = MAX_RSPQ_ENTRIES;
- e->rx_jumbo_max_pending = 0;
- e->tx_max_pending = MAX_TXQ_ENTRIES;
-
- e->rx_pending = s->ethrxq[pi->first_qset].fl.size - 8;
- e->rx_mini_pending = s->ethrxq[pi->first_qset].rspq.size;
- e->rx_jumbo_pending = 0;
- e->tx_pending = s->ethtxq[pi->first_qset].q.size;
-}
-
-static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
-{
- int i;
- const struct port_info *pi = netdev_priv(dev);
- struct adapter *adapter = pi->adapter;
- struct sge *s = &adapter->sge;
-
- if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending ||
- e->tx_pending > MAX_TXQ_ENTRIES ||
- e->rx_mini_pending > MAX_RSPQ_ENTRIES ||
- e->rx_mini_pending < MIN_RSPQ_ENTRIES ||
- e->rx_pending < MIN_FL_ENTRIES || e->tx_pending < MIN_TXQ_ENTRIES)
- return -EINVAL;
-
- if (adapter->flags & FULL_INIT_DONE)
- return -EBUSY;
-
- for (i = 0; i < pi->nqsets; ++i) {
- s->ethtxq[pi->first_qset + i].q.size = e->tx_pending;
- s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8;
- s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending;
- }
- return 0;
-}
-
static int closest_timer(const struct sge *s, int time)
{
int i, delta, match = 0, min_delta = INT_MAX;
return match;
}
-/*
- * Return a queue's interrupt hold-off time in us. 0 means no timer.
- */
-unsigned int qtimer_val(const struct adapter *adap,
- const struct sge_rspq *q)
-{
- unsigned int idx = q->intr_params >> 1;
-
- return idx < SGE_NTIMERS ? adap->sge.timer_val[idx] : 0;
-}
-
/**
- * set_rspq_intr_params - set a queue's interrupt holdoff parameters
+ * cxgb4_set_rspq_intr_params - set a queue's interrupt holdoff parameters
* @q: the Rx queue
* @us: the hold-off time in us, or 0 to disable timer
* @cnt: the hold-off packet count, or 0 to disable counter
* Sets an Rx queue's interrupt hold-off time and packet count. At least
* one of the two needs to be enabled for the queue to generate interrupts.
*/
-static int set_rspq_intr_params(struct sge_rspq *q,
- unsigned int us, unsigned int cnt)
+int cxgb4_set_rspq_intr_params(struct sge_rspq *q,
+ unsigned int us, unsigned int cnt)
{
struct adapter *adap = q->adap;
return 0;
}
-/**
- * set_rx_intr_params - set a net devices's RX interrupt holdoff paramete!
- * @dev: the network device
- * @us: the hold-off time in us, or 0 to disable timer
- * @cnt: the hold-off packet count, or 0 to disable counter
- *
- * Set the RX interrupt hold-off parameters for a network device.
- */
-static int set_rx_intr_params(struct net_device *dev,
- unsigned int us, unsigned int cnt)
-{
- int i, err;
- struct port_info *pi = netdev_priv(dev);
- struct adapter *adap = pi->adapter;
- struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
-
- for (i = 0; i < pi->nqsets; i++, q++) {
- err = set_rspq_intr_params(&q->rspq, us, cnt);
- if (err)
- return err;
- }
- return 0;
-}
-
-static int set_adaptive_rx_setting(struct net_device *dev, int adaptive_rx)
-{
- int i;
- struct port_info *pi = netdev_priv(dev);
- struct adapter *adap = pi->adapter;
- struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
-
- for (i = 0; i < pi->nqsets; i++, q++)
- q->rspq.adaptive_rx = adaptive_rx;
-
- return 0;
-}
-
-static int get_adaptive_rx_setting(struct net_device *dev)
-{
- struct port_info *pi = netdev_priv(dev);
- struct adapter *adap = pi->adapter;
- struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset];
-
- return q->rspq.adaptive_rx;
-}
-
-static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
-{
- set_adaptive_rx_setting(dev, c->use_adaptive_rx_coalesce);
- return set_rx_intr_params(dev, c->rx_coalesce_usecs,
- c->rx_max_coalesced_frames);
-}
-
-static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
-{
- const struct port_info *pi = netdev_priv(dev);
- const struct adapter *adap = pi->adapter;
- const struct sge_rspq *rq = &adap->sge.ethrxq[pi->first_qset].rspq;
-
- c->rx_coalesce_usecs = qtimer_val(adap, rq);
- c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN) ?
- adap->sge.counter_val[rq->pktcnt_idx] : 0;
- c->use_adaptive_rx_coalesce = get_adaptive_rx_setting(dev);
- return 0;
-}
-
-/**
- * eeprom_ptov - translate a physical EEPROM address to virtual
- * @phys_addr: the physical EEPROM address
- * @fn: the PCI function number
- * @sz: size of function-specific area
- *
- * Translate a physical EEPROM address to virtual. The first 1K is
- * accessed through virtual addresses starting at 31K, the rest is
- * accessed through virtual addresses starting at 0.
- *
- * The mapping is as follows:
- * [0..1K) -> [31K..32K)
- * [1K..1K+A) -> [31K-A..31K)
- * [1K+A..ES) -> [0..ES-A-1K)
- *
- * where A = @fn * @sz, and ES = EEPROM size.
- */
-static int eeprom_ptov(unsigned int phys_addr, unsigned int fn, unsigned int sz)
-{
- fn *= sz;
- if (phys_addr < 1024)
- return phys_addr + (31 << 10);
- if (phys_addr < 1024 + fn)
- return 31744 - fn + phys_addr - 1024;
- if (phys_addr < EEPROMSIZE)
- return phys_addr - 1024 - fn;
- return -EINVAL;
-}
-
-/*
- * The next two routines implement eeprom read/write from physical addresses.
- */
-static int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v)
-{
- int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE);
-
- if (vaddr >= 0)
- vaddr = pci_read_vpd(adap->pdev, vaddr, sizeof(u32), v);
- return vaddr < 0 ? vaddr : 0;
-}
-
-static int eeprom_wr_phys(struct adapter *adap, unsigned int phys_addr, u32 v)
-{
- int vaddr = eeprom_ptov(phys_addr, adap->fn, EEPROMPFSIZE);
-
- if (vaddr >= 0)
- vaddr = pci_write_vpd(adap->pdev, vaddr, sizeof(u32), &v);
- return vaddr < 0 ? vaddr : 0;
-}
-
-#define EEPROM_MAGIC 0x38E2F10C
-
-static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
- u8 *data)
-{
- int i, err = 0;
- struct adapter *adapter = netdev2adap(dev);
-
- u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- e->magic = EEPROM_MAGIC;
- for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4)
- err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]);
-
- if (!err)
- memcpy(data, buf + e->offset, e->len);
- kfree(buf);
- return err;
-}
-
-static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
- u8 *data)
-{
- u8 *buf;
- int err = 0;
- u32 aligned_offset, aligned_len, *p;
- struct adapter *adapter = netdev2adap(dev);
-
- if (eeprom->magic != EEPROM_MAGIC)
- return -EINVAL;
-
- aligned_offset = eeprom->offset & ~3;
- aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3;
-
- if (adapter->fn > 0) {
- u32 start = 1024 + adapter->fn * EEPROMPFSIZE;
-
- if (aligned_offset < start ||
- aligned_offset + aligned_len > start + EEPROMPFSIZE)
- return -EPERM;
- }
-
- if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) {
- /*
- * RMW possibly needed for first or last words.
- */
- buf = kmalloc(aligned_len, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf);
- if (!err && aligned_len > 4)
- err = eeprom_rd_phys(adapter,
- aligned_offset + aligned_len - 4,
- (u32 *)&buf[aligned_len - 4]);
- if (err)
- goto out;
- memcpy(buf + (eeprom->offset & 3), data, eeprom->len);
- } else
- buf = data;
-
- err = t4_seeprom_wp(adapter, false);
- if (err)
- goto out;
-
- for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) {
- err = eeprom_wr_phys(adapter, aligned_offset, *p);
- aligned_offset += 4;
- }
-
- if (!err)
- err = t4_seeprom_wp(adapter, true);
-out:
- if (buf != data)
- kfree(buf);
- return err;
-}
-
-static int set_flash(struct net_device *netdev, struct ethtool_flash *ef)
-{
- int ret;
- const struct firmware *fw;
- struct adapter *adap = netdev2adap(netdev);
- unsigned int mbox = PCIE_FW_MASTER_M + 1;
-
- ef->data[sizeof(ef->data) - 1] = '\0';
- ret = request_firmware(&fw, ef->data, adap->pdev_dev);
- if (ret < 0)
- return ret;
-
- /* If the adapter has been fully initialized then we'll go ahead and
- * try to get the firmware's cooperation in upgrading to the new
- * firmware image otherwise we'll try to do the entire job from the
- * host ... and we always "force" the operation in this path.
- */
- if (adap->flags & FULL_INIT_DONE)
- mbox = adap->mbox;
-
- ret = t4_fw_upgrade(adap, mbox, fw->data, fw->size, 1);
- release_firmware(fw);
- if (!ret)
- dev_info(adap->pdev_dev, "loaded firmware %s,"
- " reload cxgb4 driver\n", ef->data);
- return ret;
-}
-
-#define WOL_SUPPORTED (WAKE_BCAST | WAKE_MAGIC)
-#define BCAST_CRC 0xa0ccc1a6
-
-static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- wol->supported = WAKE_BCAST | WAKE_MAGIC;
- wol->wolopts = netdev2adap(dev)->wol;
- memset(&wol->sopass, 0, sizeof(wol->sopass));
-}
-
-static int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- int err = 0;
- struct port_info *pi = netdev_priv(dev);
-
- if (wol->wolopts & ~WOL_SUPPORTED)
- return -EINVAL;
- t4_wol_magic_enable(pi->adapter, pi->tx_chan,
- (wol->wolopts & WAKE_MAGIC) ? dev->dev_addr : NULL);
- if (wol->wolopts & WAKE_BCAST) {
- err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0xfe, ~0ULL,
- ~0ULL, 0, false);
- if (!err)
- err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 1,
- ~6ULL, ~0ULL, BCAST_CRC, true);
- } else
- t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0, 0, 0, 0, false);
- return err;
-}
-
static int cxgb_set_features(struct net_device *dev, netdev_features_t features)
{
const struct port_info *pi = netdev_priv(dev);
return err;
}
-static u32 get_rss_table_size(struct net_device *dev)
-{
- const struct port_info *pi = netdev_priv(dev);
-
- return pi->rss_size;
-}
-
-static int get_rss_table(struct net_device *dev, u32 *p, u8 *key, u8 *hfunc)
-{
- const struct port_info *pi = netdev_priv(dev);
- unsigned int n = pi->rss_size;
-
- if (hfunc)
- *hfunc = ETH_RSS_HASH_TOP;
- if (!p)
- return 0;
- while (n--)
- p[n] = pi->rss[n];
- return 0;
-}
-
-static int set_rss_table(struct net_device *dev, const u32 *p, const u8 *key,
- const u8 hfunc)
-{
- unsigned int i;
- struct port_info *pi = netdev_priv(dev);
-
- /* We require at least one supported parameter to be changed and no
- * change in any of the unsupported parameters
- */
- if (key ||
- (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
- return -EOPNOTSUPP;
- if (!p)
- return 0;
-
- for (i = 0; i < pi->rss_size; i++)
- pi->rss[i] = p[i];
- if (pi->adapter->flags & FULL_INIT_DONE)
- return write_rss(pi, pi->rss);
- return 0;
-}
-
-static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
- u32 *rules)
-{
- const struct port_info *pi = netdev_priv(dev);
-
- switch (info->cmd) {
- case ETHTOOL_GRXFH: {
- unsigned int v = pi->rss_mode;
-
- info->data = 0;
- switch (info->flow_type) {
- case TCP_V4_FLOW:
- if (v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F)
- info->data = RXH_IP_SRC | RXH_IP_DST |
- RXH_L4_B_0_1 | RXH_L4_B_2_3;
- else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
- info->data = RXH_IP_SRC | RXH_IP_DST;
- break;
- case UDP_V4_FLOW:
- if ((v & FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN_F) &&
- (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F))
- info->data = RXH_IP_SRC | RXH_IP_DST |
- RXH_L4_B_0_1 | RXH_L4_B_2_3;
- else if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
- info->data = RXH_IP_SRC | RXH_IP_DST;
- break;
- case SCTP_V4_FLOW:
- case AH_ESP_V4_FLOW:
- case IPV4_FLOW:
- if (v & FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN_F)
- info->data = RXH_IP_SRC | RXH_IP_DST;
- break;
- case TCP_V6_FLOW:
- if (v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F)
- info->data = RXH_IP_SRC | RXH_IP_DST |
- RXH_L4_B_0_1 | RXH_L4_B_2_3;
- else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
- info->data = RXH_IP_SRC | RXH_IP_DST;
- break;
- case UDP_V6_FLOW:
- if ((v & FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN_F) &&
- (v & FW_RSS_VI_CONFIG_CMD_UDPEN_F))
- info->data = RXH_IP_SRC | RXH_IP_DST |
- RXH_L4_B_0_1 | RXH_L4_B_2_3;
- else if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
- info->data = RXH_IP_SRC | RXH_IP_DST;
- break;
- case SCTP_V6_FLOW:
- case AH_ESP_V6_FLOW:
- case IPV6_FLOW:
- if (v & FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN_F)
- info->data = RXH_IP_SRC | RXH_IP_DST;
- break;
- }
- return 0;
- }
- case ETHTOOL_GRXRINGS:
- info->data = pi->nqsets;
- return 0;
- }
- return -EOPNOTSUPP;
-}
-
-static const struct ethtool_ops cxgb_ethtool_ops = {
- .get_settings = get_settings,
- .set_settings = set_settings,
- .get_drvinfo = get_drvinfo,
- .get_msglevel = get_msglevel,
- .set_msglevel = set_msglevel,
- .get_ringparam = get_sge_param,
- .set_ringparam = set_sge_param,
- .get_coalesce = get_coalesce,
- .set_coalesce = set_coalesce,
- .get_eeprom_len = get_eeprom_len,
- .get_eeprom = get_eeprom,
- .set_eeprom = set_eeprom,
- .get_pauseparam = get_pauseparam,
- .set_pauseparam = set_pauseparam,
- .get_link = ethtool_op_get_link,
- .get_strings = get_strings,
- .set_phys_id = identify_port,
- .nway_reset = restart_autoneg,
- .get_sset_count = get_sset_count,
- .get_ethtool_stats = get_stats,
- .get_regs_len = get_regs_len,
- .get_regs = get_regs,
- .get_wol = get_wol,
- .set_wol = set_wol,
- .get_rxnfc = get_rxnfc,
- .get_rxfh_indir_size = get_rss_table_size,
- .get_rxfh = get_rss_table,
- .set_rxfh = set_rss_table,
- .flash_device = set_flash,
-};
-
static int setup_debugfs(struct adapter *adap)
{
if (IS_ERR_OR_NULL(adap->debugfs_root))
static void cxgb_down(struct adapter *adapter)
{
- t4_intr_disable(adapter);
cancel_work_sync(&adapter->tid_release_task);
cancel_work_sync(&adapter->db_full_task);
cancel_work_sync(&adapter->db_drop_task);
adapter->tid_release_task_busy = false;
adapter->tid_release_head = NULL;
- if (adapter->flags & USING_MSIX) {
- free_msix_queue_irqs(adapter);
- free_irq(adapter->msix_info[0].vec, adapter);
- } else
- free_irq(adapter->pdev->irq, adapter);
- quiesce_rx(adapter);
t4_sge_stop(adapter);
t4_free_sge_resources(adapter);
adapter->flags &= ~FULL_INIT_DONE;
if (ret < 0)
return ret;
- ret = t4_cfg_pfvf(adap, adap->fn, adap->fn, 0, MAX_EGRQ, 64, MAX_INGQ,
- 0, 0, 4, 0xf, 0xf, 16, FW_CMD_CAP_PF, FW_CMD_CAP_PF);
+ ret = t4_cfg_pfvf(adap, adap->fn, adap->fn, 0, adap->sge.egr_sz, 64,
+ MAX_INGQ, 0, 0, 4, 0xf, 0xf, 16, FW_CMD_CAP_PF,
+ FW_CMD_CAP_PF);
if (ret < 0)
return ret;
enum dev_state state;
u32 params[7], val[7];
struct fw_caps_config_cmd caps_cmd;
- struct fw_devlog_cmd devlog_cmd;
- u32 devlog_meminfo;
int reset = 1;
+ /* Grab Firmware Device Log parameters as early as possible so we have
+ * access to it for debugging, etc.
+ */
+ ret = t4_init_devlog_params(adap);
+ if (ret < 0)
+ return ret;
+
/* Contact FW, advertising Master capability */
ret = t4_fw_hello(adap, adap->mbox, adap->mbox, MASTER_MAY, &state);
if (ret < 0) {
if (ret < 0)
goto bye;
- /* Read firmware device log parameters. We really need to find a way
- * to get these parameters initialized with some default values (which
- * are likely to be correct) for the case where we either don't
- * attache to the firmware or it's crashed when we probe the adapter.
- * That way we'll still be able to perform early firmware startup
- * debugging ... If the request to get the Firmware's Device Log
- * parameters fails, we'll live so we don't make that a fatal error.
- */
- memset(&devlog_cmd, 0, sizeof(devlog_cmd));
- devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) |
- FW_CMD_REQUEST_F | FW_CMD_READ_F);
- devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd));
- ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd),
- &devlog_cmd);
- if (ret == 0) {
- devlog_meminfo =
- ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog);
- adap->params.devlog.memtype =
- FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo);
- adap->params.devlog.start =
- FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4;
- adap->params.devlog.size = ntohl(devlog_cmd.memsize_devlog);
- }
-
/*
* Find out what ports are available to us. Note that we need to do
* this before calling adap_init0_no_config() since it needs nports
adap->tids.nftids = val[4] - val[3] + 1;
adap->sge.ingr_start = val[5];
+ /* qids (ingress/egress) returned from firmware can be anywhere
+ * in the range from EQ(IQFLINT)_START to EQ(IQFLINT)_END.
+ * Hence driver needs to allocate memory for this range to
+ * store the queue info. Get the highest IQFLINT/EQ index returned
+ * in FW_EQ_*_CMD.alloc command.
+ */
+ params[0] = FW_PARAM_PFVF(EQ_END);
+ params[1] = FW_PARAM_PFVF(IQFLINT_END);
+ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val);
+ if (ret < 0)
+ goto bye;
+ adap->sge.egr_sz = val[0] - adap->sge.egr_start + 1;
+ adap->sge.ingr_sz = val[1] - adap->sge.ingr_start + 1;
+
+ adap->sge.egr_map = kcalloc(adap->sge.egr_sz,
+ sizeof(*adap->sge.egr_map), GFP_KERNEL);
+ if (!adap->sge.egr_map) {
+ ret = -ENOMEM;
+ goto bye;
+ }
+
+ adap->sge.ingr_map = kcalloc(adap->sge.ingr_sz,
+ sizeof(*adap->sge.ingr_map), GFP_KERNEL);
+ if (!adap->sge.ingr_map) {
+ ret = -ENOMEM;
+ goto bye;
+ }
+
+ /* Allocate the memory for the vaious egress queue bitmaps
+ * ie starving_fl and txq_maperr.
+ */
+ adap->sge.starving_fl = kcalloc(BITS_TO_LONGS(adap->sge.egr_sz),
+ sizeof(long), GFP_KERNEL);
+ if (!adap->sge.starving_fl) {
+ ret = -ENOMEM;
+ goto bye;
+ }
+
+ adap->sge.txq_maperr = kcalloc(BITS_TO_LONGS(adap->sge.egr_sz),
+ sizeof(long), GFP_KERNEL);
+ if (!adap->sge.txq_maperr) {
+ ret = -ENOMEM;
+ goto bye;
+ }
+
params[0] = FW_PARAM_PFVF(CLIP_START);
params[1] = FW_PARAM_PFVF(CLIP_END);
ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val);
* happened to HW/FW, stop issuing commands.
*/
bye:
+ kfree(adap->sge.egr_map);
+ kfree(adap->sge.ingr_map);
+ kfree(adap->sge.starving_fl);
+ kfree(adap->sge.txq_maperr);
if (ret != -ETIMEDOUT && ret != -EIO)
t4_fw_bye(adap, adap->mbox);
return ret;
netif_carrier_off(dev);
}
spin_unlock(&adap->stats_lock);
+ disable_interrupts(adap);
if (adap->flags & FULL_INIT_DONE)
cxgb_down(adap);
rtnl_unlock();
unsigned int size, unsigned int iqe_size)
{
q->adap = adap;
- set_rspq_intr_params(q, us, cnt);
+ cxgb4_set_rspq_intr_params(q, us, cnt);
q->iqe_len = iqe_size;
q->size = size;
}
t4_free_mem(adapter->l2t);
t4_free_mem(adapter->tids.tid_tab);
+ kfree(adapter->sge.egr_map);
+ kfree(adapter->sge.ingr_map);
+ kfree(adapter->sge.starving_fl);
+ kfree(adapter->sge.txq_maperr);
disable_msi(adapter);
for_each_port(adapter, i)
netdev->dcbnl_ops = &cxgb4_dcb_ops;
cxgb4_dcb_state_init(netdev);
#endif
- netdev->ethtool_ops = &cxgb_ethtool_ops;
+ cxgb4_set_ethtool_ops(netdev);
}
pci_set_drvdata(pdev, adapter);
if (is_offload(adapter))
detach_ulds(adapter);
+ disable_interrupts(adapter);
+
for_each_port(adapter, i)
if (adapter->port[i]->reg_state == NETREG_REGISTERED)
unregister_netdev(adapter->port[i]);
if (!is_new_response(rc, q))
break;
- rmb();
+ dma_rmb();
rsp_type = RSPD_TYPE(rc->type_gen);
if (likely(rsp_type == RSP_TYPE_FLBUF)) {
struct page_frag *fp;
if (!is_new_response(rc, q))
break;
- rmb();
+ dma_rmb();
if (RSPD_TYPE(rc->type_gen) == RSP_TYPE_INTR) {
unsigned int qid = ntohl(rc->pldbuflen_qid);
struct adapter *adap = (struct adapter *)data;
struct sge *s = &adap->sge;
- for (i = 0; i < ARRAY_SIZE(s->starving_fl); i++)
+ for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++)
for (m = s->starving_fl[i]; m; m &= m - 1) {
struct sge_eth_rxq *rxq;
unsigned int id = __ffs(m) + i * BITS_PER_LONG;
struct adapter *adap = (struct adapter *)data;
struct sge *s = &adap->sge;
- for (i = 0; i < ARRAY_SIZE(s->txq_maperr); i++)
+ for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++)
for (m = s->txq_maperr[i]; m; m &= m - 1) {
unsigned long id = __ffs(m) + i * BITS_PER_LONG;
struct sge_ofld_txq *txq = s->egr_map[id];
free_rspq_fl(adap, &adap->sge.intrq, NULL);
/* clear the reverse egress queue map */
- memset(adap->sge.egr_map, 0, sizeof(adap->sge.egr_map));
+ memset(adap->sge.egr_map, 0,
+ adap->sge.egr_sz * sizeof(*adap->sge.egr_map));
}
void t4_sge_start(struct adapter *adap)
return 0;
}
+/**
+ * t4_get_regs_len - return the size of the chips register set
+ * @adapter: the adapter
+ *
+ * Returns the size of the chip's BAR0 register space.
+ */
+unsigned int t4_get_regs_len(struct adapter *adapter)
+{
+ unsigned int chip_version = CHELSIO_CHIP_VERSION(adapter->params.chip);
+
+ switch (chip_version) {
+ case CHELSIO_T4:
+ return T4_REGMAP_SIZE;
+
+ case CHELSIO_T5:
+ return T5_REGMAP_SIZE;
+ }
+
+ dev_err(adapter->pdev_dev,
+ "Unsupported chip version %d\n", chip_version);
+ return 0;
+}
+
+/**
+ * t4_get_regs - read chip registers into provided buffer
+ * @adap: the adapter
+ * @buf: register buffer
+ * @buf_size: size (in bytes) of register buffer
+ *
+ * If the provided register buffer isn't large enough for the chip's
+ * full register range, the register dump will be truncated to the
+ * register buffer's size.
+ */
+void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
+{
+ static const unsigned int t4_reg_ranges[] = {
+ 0x1008, 0x1108,
+ 0x1180, 0x11b4,
+ 0x11fc, 0x123c,
+ 0x1300, 0x173c,
+ 0x1800, 0x18fc,
+ 0x3000, 0x30d8,
+ 0x30e0, 0x5924,
+ 0x5960, 0x59d4,
+ 0x5a00, 0x5af8,
+ 0x6000, 0x6098,
+ 0x6100, 0x6150,
+ 0x6200, 0x6208,
+ 0x6240, 0x6248,
+ 0x6280, 0x6338,
+ 0x6370, 0x638c,
+ 0x6400, 0x643c,
+ 0x6500, 0x6524,
+ 0x6a00, 0x6a38,
+ 0x6a60, 0x6a78,
+ 0x6b00, 0x6b84,
+ 0x6bf0, 0x6c84,
+ 0x6cf0, 0x6d84,
+ 0x6df0, 0x6e84,
+ 0x6ef0, 0x6f84,
+ 0x6ff0, 0x7084,
+ 0x70f0, 0x7184,
+ 0x71f0, 0x7284,
+ 0x72f0, 0x7384,
+ 0x73f0, 0x7450,
+ 0x7500, 0x7530,
+ 0x7600, 0x761c,
+ 0x7680, 0x76cc,
+ 0x7700, 0x7798,
+ 0x77c0, 0x77fc,
+ 0x7900, 0x79fc,
+ 0x7b00, 0x7c38,
+ 0x7d00, 0x7efc,
+ 0x8dc0, 0x8e1c,
+ 0x8e30, 0x8e78,
+ 0x8ea0, 0x8f6c,
+ 0x8fc0, 0x9074,
+ 0x90fc, 0x90fc,
+ 0x9400, 0x9458,
+ 0x9600, 0x96bc,
+ 0x9800, 0x9808,
+ 0x9820, 0x983c,
+ 0x9850, 0x9864,
+ 0x9c00, 0x9c6c,
+ 0x9c80, 0x9cec,
+ 0x9d00, 0x9d6c,
+ 0x9d80, 0x9dec,
+ 0x9e00, 0x9e6c,
+ 0x9e80, 0x9eec,
+ 0x9f00, 0x9f6c,
+ 0x9f80, 0x9fec,
+ 0xd004, 0xd03c,
+ 0xdfc0, 0xdfe0,
+ 0xe000, 0xea7c,
+ 0xf000, 0x11110,
+ 0x11118, 0x11190,
+ 0x19040, 0x1906c,
+ 0x19078, 0x19080,
+ 0x1908c, 0x19124,
+ 0x19150, 0x191b0,
+ 0x191d0, 0x191e8,
+ 0x19238, 0x1924c,
+ 0x193f8, 0x19474,
+ 0x19490, 0x194f8,
+ 0x19800, 0x19f30,
+ 0x1a000, 0x1a06c,
+ 0x1a0b0, 0x1a120,
+ 0x1a128, 0x1a138,
+ 0x1a190, 0x1a1c4,
+ 0x1a1fc, 0x1a1fc,
+ 0x1e040, 0x1e04c,
+ 0x1e284, 0x1e28c,
+ 0x1e2c0, 0x1e2c0,
+ 0x1e2e0, 0x1e2e0,
+ 0x1e300, 0x1e384,
+ 0x1e3c0, 0x1e3c8,
+ 0x1e440, 0x1e44c,
+ 0x1e684, 0x1e68c,
+ 0x1e6c0, 0x1e6c0,
+ 0x1e6e0, 0x1e6e0,
+ 0x1e700, 0x1e784,
+ 0x1e7c0, 0x1e7c8,
+ 0x1e840, 0x1e84c,
+ 0x1ea84, 0x1ea8c,
+ 0x1eac0, 0x1eac0,
+ 0x1eae0, 0x1eae0,
+ 0x1eb00, 0x1eb84,
+ 0x1ebc0, 0x1ebc8,
+ 0x1ec40, 0x1ec4c,
+ 0x1ee84, 0x1ee8c,
+ 0x1eec0, 0x1eec0,
+ 0x1eee0, 0x1eee0,
+ 0x1ef00, 0x1ef84,
+ 0x1efc0, 0x1efc8,
+ 0x1f040, 0x1f04c,
+ 0x1f284, 0x1f28c,
+ 0x1f2c0, 0x1f2c0,
+ 0x1f2e0, 0x1f2e0,
+ 0x1f300, 0x1f384,
+ 0x1f3c0, 0x1f3c8,
+ 0x1f440, 0x1f44c,
+ 0x1f684, 0x1f68c,
+ 0x1f6c0, 0x1f6c0,
+ 0x1f6e0, 0x1f6e0,
+ 0x1f700, 0x1f784,
+ 0x1f7c0, 0x1f7c8,
+ 0x1f840, 0x1f84c,
+ 0x1fa84, 0x1fa8c,
+ 0x1fac0, 0x1fac0,
+ 0x1fae0, 0x1fae0,
+ 0x1fb00, 0x1fb84,
+ 0x1fbc0, 0x1fbc8,
+ 0x1fc40, 0x1fc4c,
+ 0x1fe84, 0x1fe8c,
+ 0x1fec0, 0x1fec0,
+ 0x1fee0, 0x1fee0,
+ 0x1ff00, 0x1ff84,
+ 0x1ffc0, 0x1ffc8,
+ 0x20000, 0x2002c,
+ 0x20100, 0x2013c,
+ 0x20190, 0x201c8,
+ 0x20200, 0x20318,
+ 0x20400, 0x20528,
+ 0x20540, 0x20614,
+ 0x21000, 0x21040,
+ 0x2104c, 0x21060,
+ 0x210c0, 0x210ec,
+ 0x21200, 0x21268,
+ 0x21270, 0x21284,
+ 0x212fc, 0x21388,
+ 0x21400, 0x21404,
+ 0x21500, 0x21518,
+ 0x2152c, 0x2153c,
+ 0x21550, 0x21554,
+ 0x21600, 0x21600,
+ 0x21608, 0x21628,
+ 0x21630, 0x2163c,
+ 0x21700, 0x2171c,
+ 0x21780, 0x2178c,
+ 0x21800, 0x21c38,
+ 0x21c80, 0x21d7c,
+ 0x21e00, 0x21e04,
+ 0x22000, 0x2202c,
+ 0x22100, 0x2213c,
+ 0x22190, 0x221c8,
+ 0x22200, 0x22318,
+ 0x22400, 0x22528,
+ 0x22540, 0x22614,
+ 0x23000, 0x23040,
+ 0x2304c, 0x23060,
+ 0x230c0, 0x230ec,
+ 0x23200, 0x23268,
+ 0x23270, 0x23284,
+ 0x232fc, 0x23388,
+ 0x23400, 0x23404,
+ 0x23500, 0x23518,
+ 0x2352c, 0x2353c,
+ 0x23550, 0x23554,
+ 0x23600, 0x23600,
+ 0x23608, 0x23628,
+ 0x23630, 0x2363c,
+ 0x23700, 0x2371c,
+ 0x23780, 0x2378c,
+ 0x23800, 0x23c38,
+ 0x23c80, 0x23d7c,
+ 0x23e00, 0x23e04,
+ 0x24000, 0x2402c,
+ 0x24100, 0x2413c,
+ 0x24190, 0x241c8,
+ 0x24200, 0x24318,
+ 0x24400, 0x24528,
+ 0x24540, 0x24614,
+ 0x25000, 0x25040,
+ 0x2504c, 0x25060,
+ 0x250c0, 0x250ec,
+ 0x25200, 0x25268,
+ 0x25270, 0x25284,
+ 0x252fc, 0x25388,
+ 0x25400, 0x25404,
+ 0x25500, 0x25518,
+ 0x2552c, 0x2553c,
+ 0x25550, 0x25554,
+ 0x25600, 0x25600,
+ 0x25608, 0x25628,
+ 0x25630, 0x2563c,
+ 0x25700, 0x2571c,
+ 0x25780, 0x2578c,
+ 0x25800, 0x25c38,
+ 0x25c80, 0x25d7c,
+ 0x25e00, 0x25e04,
+ 0x26000, 0x2602c,
+ 0x26100, 0x2613c,
+ 0x26190, 0x261c8,
+ 0x26200, 0x26318,
+ 0x26400, 0x26528,
+ 0x26540, 0x26614,
+ 0x27000, 0x27040,
+ 0x2704c, 0x27060,
+ 0x270c0, 0x270ec,
+ 0x27200, 0x27268,
+ 0x27270, 0x27284,
+ 0x272fc, 0x27388,
+ 0x27400, 0x27404,
+ 0x27500, 0x27518,
+ 0x2752c, 0x2753c,
+ 0x27550, 0x27554,
+ 0x27600, 0x27600,
+ 0x27608, 0x27628,
+ 0x27630, 0x2763c,
+ 0x27700, 0x2771c,
+ 0x27780, 0x2778c,
+ 0x27800, 0x27c38,
+ 0x27c80, 0x27d7c,
+ 0x27e00, 0x27e04
+ };
+
+ static const unsigned int t5_reg_ranges[] = {
+ 0x1008, 0x1148,
+ 0x1180, 0x11b4,
+ 0x11fc, 0x123c,
+ 0x1280, 0x173c,
+ 0x1800, 0x18fc,
+ 0x3000, 0x3028,
+ 0x3060, 0x30d8,
+ 0x30e0, 0x30fc,
+ 0x3140, 0x357c,
+ 0x35a8, 0x35cc,
+ 0x35ec, 0x35ec,
+ 0x3600, 0x5624,
+ 0x56cc, 0x575c,
+ 0x580c, 0x5814,
+ 0x5890, 0x58bc,
+ 0x5940, 0x59dc,
+ 0x59fc, 0x5a18,
+ 0x5a60, 0x5a9c,
+ 0x5b9c, 0x5bfc,
+ 0x6000, 0x6040,
+ 0x6058, 0x614c,
+ 0x7700, 0x7798,
+ 0x77c0, 0x78fc,
+ 0x7b00, 0x7c54,
+ 0x7d00, 0x7efc,
+ 0x8dc0, 0x8de0,
+ 0x8df8, 0x8e84,
+ 0x8ea0, 0x8f84,
+ 0x8fc0, 0x90f8,
+ 0x9400, 0x9470,
+ 0x9600, 0x96f4,
+ 0x9800, 0x9808,
+ 0x9820, 0x983c,
+ 0x9850, 0x9864,
+ 0x9c00, 0x9c6c,
+ 0x9c80, 0x9cec,
+ 0x9d00, 0x9d6c,
+ 0x9d80, 0x9dec,
+ 0x9e00, 0x9e6c,
+ 0x9e80, 0x9eec,
+ 0x9f00, 0x9f6c,
+ 0x9f80, 0xa020,
+ 0xd004, 0xd03c,
+ 0xdfc0, 0xdfe0,
+ 0xe000, 0x11088,
+ 0x1109c, 0x11110,
+ 0x11118, 0x1117c,
+ 0x11190, 0x11204,
+ 0x19040, 0x1906c,
+ 0x19078, 0x19080,
+ 0x1908c, 0x19124,
+ 0x19150, 0x191b0,
+ 0x191d0, 0x191e8,
+ 0x19238, 0x19290,
+ 0x193f8, 0x19474,
+ 0x19490, 0x194cc,
+ 0x194f0, 0x194f8,
+ 0x19c00, 0x19c60,
+ 0x19c94, 0x19e10,
+ 0x19e50, 0x19f34,
+ 0x19f40, 0x19f50,
+ 0x19f90, 0x19fe4,
+ 0x1a000, 0x1a06c,
+ 0x1a0b0, 0x1a120,
+ 0x1a128, 0x1a138,
+ 0x1a190, 0x1a1c4,
+ 0x1a1fc, 0x1a1fc,
+ 0x1e008, 0x1e00c,
+ 0x1e040, 0x1e04c,
+ 0x1e284, 0x1e290,
+ 0x1e2c0, 0x1e2c0,
+ 0x1e2e0, 0x1e2e0,
+ 0x1e300, 0x1e384,
+ 0x1e3c0, 0x1e3c8,
+ 0x1e408, 0x1e40c,
+ 0x1e440, 0x1e44c,
+ 0x1e684, 0x1e690,
+ 0x1e6c0, 0x1e6c0,
+ 0x1e6e0, 0x1e6e0,
+ 0x1e700, 0x1e784,
+ 0x1e7c0, 0x1e7c8,
+ 0x1e808, 0x1e80c,
+ 0x1e840, 0x1e84c,
+ 0x1ea84, 0x1ea90,
+ 0x1eac0, 0x1eac0,
+ 0x1eae0, 0x1eae0,
+ 0x1eb00, 0x1eb84,
+ 0x1ebc0, 0x1ebc8,
+ 0x1ec08, 0x1ec0c,
+ 0x1ec40, 0x1ec4c,
+ 0x1ee84, 0x1ee90,
+ 0x1eec0, 0x1eec0,
+ 0x1eee0, 0x1eee0,
+ 0x1ef00, 0x1ef84,
+ 0x1efc0, 0x1efc8,
+ 0x1f008, 0x1f00c,
+ 0x1f040, 0x1f04c,
+ 0x1f284, 0x1f290,
+ 0x1f2c0, 0x1f2c0,
+ 0x1f2e0, 0x1f2e0,
+ 0x1f300, 0x1f384,
+ 0x1f3c0, 0x1f3c8,
+ 0x1f408, 0x1f40c,
+ 0x1f440, 0x1f44c,
+ 0x1f684, 0x1f690,
+ 0x1f6c0, 0x1f6c0,
+ 0x1f6e0, 0x1f6e0,
+ 0x1f700, 0x1f784,
+ 0x1f7c0, 0x1f7c8,
+ 0x1f808, 0x1f80c,
+ 0x1f840, 0x1f84c,
+ 0x1fa84, 0x1fa90,
+ 0x1fac0, 0x1fac0,
+ 0x1fae0, 0x1fae0,
+ 0x1fb00, 0x1fb84,
+ 0x1fbc0, 0x1fbc8,
+ 0x1fc08, 0x1fc0c,
+ 0x1fc40, 0x1fc4c,
+ 0x1fe84, 0x1fe90,
+ 0x1fec0, 0x1fec0,
+ 0x1fee0, 0x1fee0,
+ 0x1ff00, 0x1ff84,
+ 0x1ffc0, 0x1ffc8,
+ 0x30000, 0x30030,
+ 0x30100, 0x30144,
+ 0x30190, 0x301d0,
+ 0x30200, 0x30318,
+ 0x30400, 0x3052c,
+ 0x30540, 0x3061c,
+ 0x30800, 0x30834,
+ 0x308c0, 0x30908,
+ 0x30910, 0x309ac,
+ 0x30a00, 0x30a04,
+ 0x30a0c, 0x30a2c,
+ 0x30a44, 0x30a50,
+ 0x30a74, 0x30c24,
+ 0x30d08, 0x30d14,
+ 0x30d1c, 0x30d20,
+ 0x30d3c, 0x30d50,
+ 0x31200, 0x3120c,
+ 0x31220, 0x31220,
+ 0x31240, 0x31240,
+ 0x31600, 0x31600,
+ 0x31608, 0x3160c,
+ 0x31a00, 0x31a1c,
+ 0x31e04, 0x31e20,
+ 0x31e38, 0x31e3c,
+ 0x31e80, 0x31e80,
+ 0x31e88, 0x31ea8,
+ 0x31eb0, 0x31eb4,
+ 0x31ec8, 0x31ed4,
+ 0x31fb8, 0x32004,
+ 0x32208, 0x3223c,
+ 0x32600, 0x32630,
+ 0x32a00, 0x32abc,
+ 0x32b00, 0x32b70,
+ 0x33000, 0x33048,
+ 0x33060, 0x3309c,
+ 0x330f0, 0x33148,
+ 0x33160, 0x3319c,
+ 0x331f0, 0x332e4,
+ 0x332f8, 0x333e4,
+ 0x333f8, 0x33448,
+ 0x33460, 0x3349c,
+ 0x334f0, 0x33548,
+ 0x33560, 0x3359c,
+ 0x335f0, 0x336e4,
+ 0x336f8, 0x337e4,
+ 0x337f8, 0x337fc,
+ 0x33814, 0x33814,
+ 0x3382c, 0x3382c,
+ 0x33880, 0x3388c,
+ 0x338e8, 0x338ec,
+ 0x33900, 0x33948,
+ 0x33960, 0x3399c,
+ 0x339f0, 0x33ae4,
+ 0x33af8, 0x33b10,
+ 0x33b28, 0x33b28,
+ 0x33b3c, 0x33b50,
+ 0x33bf0, 0x33c10,
+ 0x33c28, 0x33c28,
+ 0x33c3c, 0x33c50,
+ 0x33cf0, 0x33cfc,
+ 0x34000, 0x34030,
+ 0x34100, 0x34144,
+ 0x34190, 0x341d0,
+ 0x34200, 0x34318,
+ 0x34400, 0x3452c,
+ 0x34540, 0x3461c,
+ 0x34800, 0x34834,
+ 0x348c0, 0x34908,
+ 0x34910, 0x349ac,
+ 0x34a00, 0x34a04,
+ 0x34a0c, 0x34a2c,
+ 0x34a44, 0x34a50,
+ 0x34a74, 0x34c24,
+ 0x34d08, 0x34d14,
+ 0x34d1c, 0x34d20,
+ 0x34d3c, 0x34d50,
+ 0x35200, 0x3520c,
+ 0x35220, 0x35220,
+ 0x35240, 0x35240,
+ 0x35600, 0x35600,
+ 0x35608, 0x3560c,
+ 0x35a00, 0x35a1c,
+ 0x35e04, 0x35e20,
+ 0x35e38, 0x35e3c,
+ 0x35e80, 0x35e80,
+ 0x35e88, 0x35ea8,
+ 0x35eb0, 0x35eb4,
+ 0x35ec8, 0x35ed4,
+ 0x35fb8, 0x36004,
+ 0x36208, 0x3623c,
+ 0x36600, 0x36630,
+ 0x36a00, 0x36abc,
+ 0x36b00, 0x36b70,
+ 0x37000, 0x37048,
+ 0x37060, 0x3709c,
+ 0x370f0, 0x37148,
+ 0x37160, 0x3719c,
+ 0x371f0, 0x372e4,
+ 0x372f8, 0x373e4,
+ 0x373f8, 0x37448,
+ 0x37460, 0x3749c,
+ 0x374f0, 0x37548,
+ 0x37560, 0x3759c,
+ 0x375f0, 0x376e4,
+ 0x376f8, 0x377e4,
+ 0x377f8, 0x377fc,
+ 0x37814, 0x37814,
+ 0x3782c, 0x3782c,
+ 0x37880, 0x3788c,
+ 0x378e8, 0x378ec,
+ 0x37900, 0x37948,
+ 0x37960, 0x3799c,
+ 0x379f0, 0x37ae4,
+ 0x37af8, 0x37b10,
+ 0x37b28, 0x37b28,
+ 0x37b3c, 0x37b50,
+ 0x37bf0, 0x37c10,
+ 0x37c28, 0x37c28,
+ 0x37c3c, 0x37c50,
+ 0x37cf0, 0x37cfc,
+ 0x38000, 0x38030,
+ 0x38100, 0x38144,
+ 0x38190, 0x381d0,
+ 0x38200, 0x38318,
+ 0x38400, 0x3852c,
+ 0x38540, 0x3861c,
+ 0x38800, 0x38834,
+ 0x388c0, 0x38908,
+ 0x38910, 0x389ac,
+ 0x38a00, 0x38a04,
+ 0x38a0c, 0x38a2c,
+ 0x38a44, 0x38a50,
+ 0x38a74, 0x38c24,
+ 0x38d08, 0x38d14,
+ 0x38d1c, 0x38d20,
+ 0x38d3c, 0x38d50,
+ 0x39200, 0x3920c,
+ 0x39220, 0x39220,
+ 0x39240, 0x39240,
+ 0x39600, 0x39600,
+ 0x39608, 0x3960c,
+ 0x39a00, 0x39a1c,
+ 0x39e04, 0x39e20,
+ 0x39e38, 0x39e3c,
+ 0x39e80, 0x39e80,
+ 0x39e88, 0x39ea8,
+ 0x39eb0, 0x39eb4,
+ 0x39ec8, 0x39ed4,
+ 0x39fb8, 0x3a004,
+ 0x3a208, 0x3a23c,
+ 0x3a600, 0x3a630,
+ 0x3aa00, 0x3aabc,
+ 0x3ab00, 0x3ab70,
+ 0x3b000, 0x3b048,
+ 0x3b060, 0x3b09c,
+ 0x3b0f0, 0x3b148,
+ 0x3b160, 0x3b19c,
+ 0x3b1f0, 0x3b2e4,
+ 0x3b2f8, 0x3b3e4,
+ 0x3b3f8, 0x3b448,
+ 0x3b460, 0x3b49c,
+ 0x3b4f0, 0x3b548,
+ 0x3b560, 0x3b59c,
+ 0x3b5f0, 0x3b6e4,
+ 0x3b6f8, 0x3b7e4,
+ 0x3b7f8, 0x3b7fc,
+ 0x3b814, 0x3b814,
+ 0x3b82c, 0x3b82c,
+ 0x3b880, 0x3b88c,
+ 0x3b8e8, 0x3b8ec,
+ 0x3b900, 0x3b948,
+ 0x3b960, 0x3b99c,
+ 0x3b9f0, 0x3bae4,
+ 0x3baf8, 0x3bb10,
+ 0x3bb28, 0x3bb28,
+ 0x3bb3c, 0x3bb50,
+ 0x3bbf0, 0x3bc10,
+ 0x3bc28, 0x3bc28,
+ 0x3bc3c, 0x3bc50,
+ 0x3bcf0, 0x3bcfc,
+ 0x3c000, 0x3c030,
+ 0x3c100, 0x3c144,
+ 0x3c190, 0x3c1d0,
+ 0x3c200, 0x3c318,
+ 0x3c400, 0x3c52c,
+ 0x3c540, 0x3c61c,
+ 0x3c800, 0x3c834,
+ 0x3c8c0, 0x3c908,
+ 0x3c910, 0x3c9ac,
+ 0x3ca00, 0x3ca04,
+ 0x3ca0c, 0x3ca2c,
+ 0x3ca44, 0x3ca50,
+ 0x3ca74, 0x3cc24,
+ 0x3cd08, 0x3cd14,
+ 0x3cd1c, 0x3cd20,
+ 0x3cd3c, 0x3cd50,
+ 0x3d200, 0x3d20c,
+ 0x3d220, 0x3d220,
+ 0x3d240, 0x3d240,
+ 0x3d600, 0x3d600,
+ 0x3d608, 0x3d60c,
+ 0x3da00, 0x3da1c,
+ 0x3de04, 0x3de20,
+ 0x3de38, 0x3de3c,
+ 0x3de80, 0x3de80,
+ 0x3de88, 0x3dea8,
+ 0x3deb0, 0x3deb4,
+ 0x3dec8, 0x3ded4,
+ 0x3dfb8, 0x3e004,
+ 0x3e208, 0x3e23c,
+ 0x3e600, 0x3e630,
+ 0x3ea00, 0x3eabc,
+ 0x3eb00, 0x3eb70,
+ 0x3f000, 0x3f048,
+ 0x3f060, 0x3f09c,
+ 0x3f0f0, 0x3f148,
+ 0x3f160, 0x3f19c,
+ 0x3f1f0, 0x3f2e4,
+ 0x3f2f8, 0x3f3e4,
+ 0x3f3f8, 0x3f448,
+ 0x3f460, 0x3f49c,
+ 0x3f4f0, 0x3f548,
+ 0x3f560, 0x3f59c,
+ 0x3f5f0, 0x3f6e4,
+ 0x3f6f8, 0x3f7e4,
+ 0x3f7f8, 0x3f7fc,
+ 0x3f814, 0x3f814,
+ 0x3f82c, 0x3f82c,
+ 0x3f880, 0x3f88c,
+ 0x3f8e8, 0x3f8ec,
+ 0x3f900, 0x3f948,
+ 0x3f960, 0x3f99c,
+ 0x3f9f0, 0x3fae4,
+ 0x3faf8, 0x3fb10,
+ 0x3fb28, 0x3fb28,
+ 0x3fb3c, 0x3fb50,
+ 0x3fbf0, 0x3fc10,
+ 0x3fc28, 0x3fc28,
+ 0x3fc3c, 0x3fc50,
+ 0x3fcf0, 0x3fcfc,
+ 0x40000, 0x4000c,
+ 0x40040, 0x40068,
+ 0x40080, 0x40144,
+ 0x40180, 0x4018c,
+ 0x40200, 0x40298,
+ 0x402ac, 0x4033c,
+ 0x403f8, 0x403fc,
+ 0x41304, 0x413c4,
+ 0x41400, 0x4141c,
+ 0x41480, 0x414d0,
+ 0x44000, 0x44078,
+ 0x440c0, 0x44278,
+ 0x442c0, 0x44478,
+ 0x444c0, 0x44678,
+ 0x446c0, 0x44878,
+ 0x448c0, 0x449fc,
+ 0x45000, 0x45068,
+ 0x45080, 0x45084,
+ 0x450a0, 0x450b0,
+ 0x45200, 0x45268,
+ 0x45280, 0x45284,
+ 0x452a0, 0x452b0,
+ 0x460c0, 0x460e4,
+ 0x47000, 0x4708c,
+ 0x47200, 0x47250,
+ 0x47400, 0x47420,
+ 0x47600, 0x47618,
+ 0x47800, 0x47814,
+ 0x48000, 0x4800c,
+ 0x48040, 0x48068,
+ 0x48080, 0x48144,
+ 0x48180, 0x4818c,
+ 0x48200, 0x48298,
+ 0x482ac, 0x4833c,
+ 0x483f8, 0x483fc,
+ 0x49304, 0x493c4,
+ 0x49400, 0x4941c,
+ 0x49480, 0x494d0,
+ 0x4c000, 0x4c078,
+ 0x4c0c0, 0x4c278,
+ 0x4c2c0, 0x4c478,
+ 0x4c4c0, 0x4c678,
+ 0x4c6c0, 0x4c878,
+ 0x4c8c0, 0x4c9fc,
+ 0x4d000, 0x4d068,
+ 0x4d080, 0x4d084,
+ 0x4d0a0, 0x4d0b0,
+ 0x4d200, 0x4d268,
+ 0x4d280, 0x4d284,
+ 0x4d2a0, 0x4d2b0,
+ 0x4e0c0, 0x4e0e4,
+ 0x4f000, 0x4f08c,
+ 0x4f200, 0x4f250,
+ 0x4f400, 0x4f420,
+ 0x4f600, 0x4f618,
+ 0x4f800, 0x4f814,
+ 0x50000, 0x500cc,
+ 0x50400, 0x50400,
+ 0x50800, 0x508cc,
+ 0x50c00, 0x50c00,
+ 0x51000, 0x5101c,
+ 0x51300, 0x51308,
+ };
+
+ u32 *buf_end = (u32 *)((char *)buf + buf_size);
+ const unsigned int *reg_ranges;
+ int reg_ranges_size, range;
+ unsigned int chip_version = CHELSIO_CHIP_VERSION(adap->params.chip);
+
+ /* Select the right set of register ranges to dump depending on the
+ * adapter chip type.
+ */
+ switch (chip_version) {
+ case CHELSIO_T4:
+ reg_ranges = t4_reg_ranges;
+ reg_ranges_size = ARRAY_SIZE(t4_reg_ranges);
+ break;
+
+ case CHELSIO_T5:
+ reg_ranges = t5_reg_ranges;
+ reg_ranges_size = ARRAY_SIZE(t5_reg_ranges);
+ break;
+
+ default:
+ dev_err(adap->pdev_dev,
+ "Unsupported chip version %d\n", chip_version);
+ return;
+ }
+
+ /* Clear the register buffer and insert the appropriate register
+ * values selected by the above register ranges.
+ */
+ memset(buf, 0, buf_size);
+ for (range = 0; range < reg_ranges_size; range += 2) {
+ unsigned int reg = reg_ranges[range];
+ unsigned int last_reg = reg_ranges[range + 1];
+ u32 *bufp = (u32 *)((char *)buf + reg);
+
+ /* Iterate across the register range filling in the register
+ * buffer but don't write past the end of the register buffer.
+ */
+ while (reg <= last_reg && bufp < buf_end) {
+ *bufp++ = t4_read_reg(adap, reg);
+ reg += sizeof(u32);
+ }
+ }
+}
+
#define EEPROM_STAT_ADDR 0x7bfc
#define VPD_BASE 0x400
#define VPD_BASE_OLD 0
return 0;
}
+/**
+ * t4_init_devlog_params - initialize adapter->params.devlog
+ * @adap: the adapter
+ *
+ * Initialize various fields of the adapter's Firmware Device Log
+ * Parameters structure.
+ */
+int t4_init_devlog_params(struct adapter *adap)
+{
+ struct devlog_params *dparams = &adap->params.devlog;
+ u32 pf_dparams;
+ unsigned int devlog_meminfo;
+ struct fw_devlog_cmd devlog_cmd;
+ int ret;
+
+ /* If we're dealing with newer firmware, the Device Log Paramerters
+ * are stored in a designated register which allows us to access the
+ * Device Log even if we can't talk to the firmware.
+ */
+ pf_dparams =
+ t4_read_reg(adap, PCIE_FW_REG(PCIE_FW_PF_A, PCIE_FW_PF_DEVLOG));
+ if (pf_dparams) {
+ unsigned int nentries, nentries128;
+
+ dparams->memtype = PCIE_FW_PF_DEVLOG_MEMTYPE_G(pf_dparams);
+ dparams->start = PCIE_FW_PF_DEVLOG_ADDR16_G(pf_dparams) << 4;
+
+ nentries128 = PCIE_FW_PF_DEVLOG_NENTRIES128_G(pf_dparams);
+ nentries = (nentries128 + 1) * 128;
+ dparams->size = nentries * sizeof(struct fw_devlog_e);
+
+ return 0;
+ }
+
+ /* Otherwise, ask the firmware for it's Device Log Parameters.
+ */
+ memset(&devlog_cmd, 0, sizeof(devlog_cmd));
+ devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_READ_F);
+ devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd));
+ ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd),
+ &devlog_cmd);
+ if (ret)
+ return ret;
+
+ devlog_meminfo = ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog);
+ dparams->memtype = FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo);
+ dparams->start = FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4;
+ dparams->size = ntohl(devlog_cmd.memsize_devlog);
+
+ return 0;
+}
+
/**
* t4_init_sge_params - initialize adap->params.sge
* @adapter: the adapter
#define MC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4)
#define EDC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4)
+#define PCIE_FW_REG(reg_addr, idx) ((reg_addr) + (idx) * 4)
+
#define SGE_PF_KDOORBELL_A 0x0
#define QID_S 15
#define PFNUM_V(x) ((x) << PFNUM_S)
#define PCIE_FW_A 0x30b8
+#define PCIE_FW_PF_A 0x30bc
#define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A 0x5908
FW_RI_BIND_MW_WR = 0x18,
FW_RI_FR_NSMR_WR = 0x19,
FW_RI_INV_LSTAG_WR = 0x1a,
- FW_LASTC2E_WR = 0x40
+ FW_LASTC2E_WR = 0x70
};
struct fw_wr_hdr {
FW_MEMTYPE_CF_EXTMEM = 0x2,
FW_MEMTYPE_CF_FLASH = 0x4,
FW_MEMTYPE_CF_INTERNAL = 0x5,
+ FW_MEMTYPE_CF_EXTMEM1 = 0x6,
};
struct fw_caps_config_cmd {
FW_PARAMS_MNEM_PFVF = 2, /* function params */
FW_PARAMS_MNEM_REG = 3, /* limited register access */
FW_PARAMS_MNEM_DMAQ = 4, /* dma queue params */
+ FW_PARAMS_MNEM_CHNET = 5, /* chnet params */
FW_PARAMS_MNEM_LAST
};
FW_DEVLOG_FACILITY_FCOE = 0x2E,
FW_DEVLOG_FACILITY_FOISCSI = 0x30,
FW_DEVLOG_FACILITY_FOFCOE = 0x32,
- FW_DEVLOG_FACILITY_MAX = 0x32,
+ FW_DEVLOG_FACILITY_CHNET = 0x34,
+ FW_DEVLOG_FACILITY_MAX = 0x34,
};
/* log message format */
(((x) >> FW_DEVLOG_CMD_MEMADDR16_DEVLOG_S) & \
FW_DEVLOG_CMD_MEMADDR16_DEVLOG_M)
+/* P C I E F W P F 7 R E G I S T E R */
+
+/* PF7 stores the Firmware Device Log parameters which allows Host Drivers to
+ * access the "devlog" which needing to contact firmware. The encoding is
+ * mostly the same as that returned by the DEVLOG command except for the size
+ * which is encoded as the number of entries in multiples-1 of 128 here rather
+ * than the memory size as is done in the DEVLOG command. Thus, 0 means 128
+ * and 15 means 2048. This of course in turn constrains the allowed values
+ * for the devlog size ...
+ */
+#define PCIE_FW_PF_DEVLOG 7
+
+#define PCIE_FW_PF_DEVLOG_NENTRIES128_S 28
+#define PCIE_FW_PF_DEVLOG_NENTRIES128_M 0xf
+#define PCIE_FW_PF_DEVLOG_NENTRIES128_V(x) \
+ ((x) << PCIE_FW_PF_DEVLOG_NENTRIES128_S)
+#define PCIE_FW_PF_DEVLOG_NENTRIES128_G(x) \
+ (((x) >> PCIE_FW_PF_DEVLOG_NENTRIES128_S) & \
+ PCIE_FW_PF_DEVLOG_NENTRIES128_M)
+
+#define PCIE_FW_PF_DEVLOG_ADDR16_S 4
+#define PCIE_FW_PF_DEVLOG_ADDR16_M 0xffffff
+#define PCIE_FW_PF_DEVLOG_ADDR16_V(x) ((x) << PCIE_FW_PF_DEVLOG_ADDR16_S)
+#define PCIE_FW_PF_DEVLOG_ADDR16_G(x) \
+ (((x) >> PCIE_FW_PF_DEVLOG_ADDR16_S) & PCIE_FW_PF_DEVLOG_ADDR16_M)
+
+#define PCIE_FW_PF_DEVLOG_MEMTYPE_S 0
+#define PCIE_FW_PF_DEVLOG_MEMTYPE_M 0xf
+#define PCIE_FW_PF_DEVLOG_MEMTYPE_V(x) ((x) << PCIE_FW_PF_DEVLOG_MEMTYPE_S)
+#define PCIE_FW_PF_DEVLOG_MEMTYPE_G(x) \
+ (((x) >> PCIE_FW_PF_DEVLOG_MEMTYPE_S) & PCIE_FW_PF_DEVLOG_MEMTYPE_M)
+
#endif /* _T4FW_INTERFACE_H_ */
#define __T4FW_VERSION_H__
#define T4FW_VERSION_MAJOR 0x01
-#define T4FW_VERSION_MINOR 0x0C
-#define T4FW_VERSION_MICRO 0x19
+#define T4FW_VERSION_MINOR 0x0D
+#define T4FW_VERSION_MICRO 0x20
#define T4FW_VERSION_BUILD 0x00
#define T5FW_VERSION_MAJOR 0x01
-#define T5FW_VERSION_MINOR 0x0C
-#define T5FW_VERSION_MICRO 0x19
+#define T5FW_VERSION_MINOR 0x0D
+#define T5FW_VERSION_MICRO 0x20
#define T5FW_VERSION_BUILD 0x00
#endif
? (tq->pidx - 1)
: (tq->size - 1));
__be64 *src = (__be64 *)&tq->desc[index];
- __be64 __iomem *dst = (__be64 *)(tq->bar2_addr +
+ __be64 __iomem *dst = (__be64 __iomem *)(tq->bar2_addr +
SGE_UDB_WCDOORBELL);
unsigned int count = EQ_UNIT / sizeof(__be64);
* DMA.
*/
while (count) {
- writeq(*src, dst);
+ /* the (__force u64) is because the compiler
+ * doesn't understand the endian swizzling
+ * going on
+ */
+ writeq((__force u64)*src, dst);
src++;
dst++;
count--;
BUG_ON(DIV_ROUND_UP(ETHTXQ_MAX_HDR, TXD_PER_EQ_UNIT) > 1);
wr = (void *)&txq->q.desc[txq->q.pidx];
wr->equiq_to_len16 = cpu_to_be32(wr_mid);
- wr->r3[0] = cpu_to_be64(0);
- wr->r3[1] = cpu_to_be64(0);
+ wr->r3[0] = cpu_to_be32(0);
+ wr->r3[1] = cpu_to_be32(0);
skb_copy_from_linear_data(skb, (void *)wr->ethmacdst, fw_hdr_copy_len);
end = (u64 *)wr + flits;
* Figure out what kind of response we've received from the
* SGE.
*/
- rmb();
+ dma_rmb();
rsp_type = RSPD_TYPE(rc->type_gen);
if (likely(rsp_type == RSP_TYPE_FLBUF)) {
struct page_frag *fp;
* error and go on to the next response message. This should
* never happen ...
*/
- rmb();
+ dma_rmb();
if (unlikely(RSPD_TYPE(rc->type_gen) != RSP_TYPE_INTR)) {
dev_err(adapter->pdev_dev,
"Unexpected INTRQ response type %d\n",
if (rpl) {
/* request bit in high-order BE word */
- WARN_ON((be32_to_cpu(*(const u32 *)cmd)
+ WARN_ON((be32_to_cpu(*(const __be32 *)cmd)
& FW_CMD_REQUEST_F) == 0);
get_mbox_rpl(adapter, rpl, size, mbox_data);
- WARN_ON((be32_to_cpu(*(u32 *)rpl)
+ WARN_ON((be32_to_cpu(*(__be32 *)rpl)
& FW_CMD_REQUEST_F) != 0);
}
t4_write_reg(adapter, mbox_ctl,
* o The BAR2 Queue ID.
* o The BAR2 Queue ID Offset into the BAR2 page.
*/
- bar2_page_offset = ((qid >> qpp_shift) << page_shift);
+ bar2_page_offset = ((u64)(qid >> qpp_shift) << page_shift);
bar2_qid = qid & qpp_mask;
bar2_qid_offset = bar2_qid * SGE_UDB_SIZE;
struct fec_enet_private *fep = netdev_priv(ndev);
struct device_node *node;
int err = -ENXIO, i;
+ u32 mii_speed, holdtime;
/*
* The i.MX28 dual fec interfaces are not equal.
* Reference Manual has an error on this, and gets fixed on i.MX6Q
* document.
*/
- fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
+ mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
if (fep->quirks & FEC_QUIRK_ENET_MAC)
- fep->phy_speed--;
- fep->phy_speed <<= 1;
+ mii_speed--;
+ if (mii_speed > 63) {
+ dev_err(&pdev->dev,
+ "fec clock (%lu) to fast to get right mii speed\n",
+ clk_get_rate(fep->clk_ipg));
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ /*
+ * The i.MX28 and i.MX6 types have another filed in the MSCR (aka
+ * MII_SPEED) register that defines the MDIO output hold time. Earlier
+ * versions are RAZ there, so just ignore the difference and write the
+ * register always.
+ * The minimal hold time according to IEE802.3 (clause 22) is 10 ns.
+ * HOLDTIME + 1 is the number of clk cycles the fec is holding the
+ * output.
+ * The HOLDTIME bitfield takes values between 0 and 7 (inclusive).
+ * Given that ceil(clkrate / 5000000) <= 64, the calculation for
+ * holdtime cannot result in a value greater than 3.
+ */
+ holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1;
+
+ fep->phy_speed = mii_speed << 1 | holdtime << 8;
+
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
fep->mii_bus = mdiobus_alloc();
ugeth->phy_interface = phy_interface;
ugeth->max_speed = max_speed;
+ /* Carrier starts down, phylib will bring it up */
+ netif_carrier_off(dev);
+
err = register_netdev(dev);
if (err) {
if (netif_msg_probe(ugeth))
/* Order is important otherwise we'll be in a race with h/w:
* set S-bit in current first, then clear S-bit in previous. */
cb->command |= cpu_to_le16(cb_s);
- wmb();
+ dma_wmb();
cb->prev->command &= cpu_to_le16(~cb_s);
while (nic->cb_to_send != nic->cb_to_use) {
for (cb = nic->cb_to_clean;
cb->status & cpu_to_le16(cb_complete);
cb = nic->cb_to_clean = cb->next) {
- rmb(); /* read skb after status */
+ dma_rmb(); /* read skb after status */
netif_printk(nic, tx_done, KERN_DEBUG, nic->netdev,
"cb[%d]->status = 0x%04X\n",
(int)(((void*)cb - (void*)nic->cbs)/sizeof(struct cb)),
netif_printk(nic, rx_status, KERN_DEBUG, nic->netdev,
"status=0x%04X\n", rfd_status);
- rmb(); /* read size after status bit */
+ dma_rmb(); /* read size after status bit */
/* If data isn't ready, nothing to indicate */
if (unlikely(!(rfd_status & cb_complete))) {
while ((eop_desc->upper.data & cpu_to_le32(E1000_TXD_STAT_DD)) &&
(count < tx_ring->count)) {
bool cleaned = false;
- rmb(); /* read buffer_info after eop_desc */
+ dma_rmb(); /* read buffer_info after eop_desc */
for ( ; !cleaned; count++) {
tx_desc = E1000_TX_DESC(*tx_ring, i);
buffer_info = &tx_ring->buffer_info[i];
if (*work_done >= work_to_do)
break;
(*work_done)++;
- rmb(); /* read descriptor and rx_buffer_info after status DD */
+ dma_rmb(); /* read descriptor and rx_buffer_info after status DD */
status = rx_desc->status;
if (*work_done >= work_to_do)
break;
(*work_done)++;
- rmb(); /* read descriptor and rx_buffer_info after status DD */
+ dma_rmb(); /* read descriptor and rx_buffer_info after status DD */
status = rx_desc->status;
length = le16_to_cpu(rx_desc->length);
if (*work_done >= work_to_do)
break;
(*work_done)++;
- rmb(); /* read descriptor and rx_buffer_info after status DD */
+ dma_rmb(); /* read descriptor and rx_buffer_info after status DD */
skb = buffer_info->skb;
buffer_info->skb = NULL;
(count < tx_ring->count)) {
bool cleaned = false;
- rmb(); /* read buffer_info after eop_desc */
+ dma_rmb(); /* read buffer_info after eop_desc */
for (; !cleaned; count++) {
tx_desc = E1000_TX_DESC(*tx_ring, i);
buffer_info = &tx_ring->buffer_info[i];
break;
(*work_done)++;
skb = buffer_info->skb;
- rmb(); /* read descriptor and rx_buffer_info after status DD */
+ dma_rmb(); /* read descriptor and rx_buffer_info after status DD */
/* in the packet split case this is header only */
prefetch(skb->data - NET_IP_ALIGN);
if (*work_done >= work_to_do)
break;
(*work_done)++;
- rmb(); /* read descriptor and rx_buffer_info after status DD */
+ dma_rmb(); /* read descriptor and rx_buffer_info after status DD */
skb = buffer_info->skb;
buffer_info->skb = NULL;
extern const char i40e_driver_version_str[];
void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags);
void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags);
+struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id);
void i40e_update_stats(struct i40e_vsi *vsi);
void i40e_update_eth_stats(struct i40e_vsi *vsi);
struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi);
if (!(pf->flags & I40E_FLAG_DCB_ENABLED))
return;
+ /* MFP mode but not an iSCSI PF so return */
+ if ((pf->flags & I40E_FLAG_MFP_ENABLED) && !(pf->hw.func_caps.iscsi))
+ return;
+
dcbxcfg = &hw->local_dcbx_config;
/* Set up all the App TLVs if DCBx is negotiated */
struct i40e_dcb_app_priority_table app;
int i;
+ /* MFP mode but not an iSCSI PF so return */
+ if ((pf->flags & I40E_FLAG_MFP_ENABLED) && !(pf->hw.func_caps.iscsi))
+ return;
+
for (i = 0; i < old_cfg->numapps; i++) {
app = old_cfg->app[i];
/* The APP is not available anymore delete it */
/* MACLEN is ether header length in words not bytes */
td_offset |= (maclen >> 1) << I40E_TX_DESC_LENGTH_MACLEN_SHIFT;
- return i40e_tx_map(tx_ring, skb, first, tx_flags, hdr_len,
- td_cmd, td_offset);
+ i40e_tx_map(tx_ring, skb, first, tx_flags, hdr_len, td_cmd, td_offset);
}
/**
#define DRV_KERN "-k"
#define DRV_VERSION_MAJOR 1
-#define DRV_VERSION_MINOR 2
-#define DRV_VERSION_BUILD 43
+#define DRV_VERSION_MINOR 3
+#define DRV_VERSION_BUILD 1
#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
__stringify(DRV_VERSION_MINOR) "." \
__stringify(DRV_VERSION_BUILD) DRV_KERN
return count;
}
+/**
+ * i40e_find_vsi_from_id - searches for the vsi with the given id
+ * @pf - the pf structure to search for the vsi
+ * @id - id of the vsi it is searching for
+ **/
+struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id)
+{
+ int i;
+
+ for (i = 0; i < pf->num_alloc_vsi; i++)
+ if (pf->vsi[i] && (pf->vsi[i]->id == id))
+ return pf->vsi[i];
+
+ return NULL;
+}
+
/**
* i40e_service_event_schedule - Schedule the service task to wake up
* @pf: board private structure
I40E_AQ_VSI_PVLAN_EMOD_STR_BOTH;
ctxt.seid = vsi->seid;
- memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+ ctxt.info = vsi->info;
ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL);
if (ret) {
dev_info(&vsi->back->pdev->dev,
I40E_AQ_VSI_PVLAN_EMOD_NOTHING;
ctxt.seid = vsi->seid;
- memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+ ctxt.info = vsi->info;
ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL);
if (ret) {
dev_info(&vsi->back->pdev->dev,
I40E_AQ_VSI_PVLAN_EMOD_STR;
ctxt.seid = vsi->seid;
- memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+ ctxt.info = vsi->info;
aq_ret = i40e_aq_update_vsi_params(&vsi->back->hw, &ctxt, NULL);
if (aq_ret) {
dev_info(&vsi->back->pdev->dev,
if (icr0 & I40E_PFINT_ICR0_HMC_ERR_MASK) {
icr0 &= ~I40E_PFINT_ICR0_HMC_ERR_MASK;
dev_info(&pf->pdev->dev, "HMC error interrupt\n");
+ dev_info(&pf->pdev->dev, "HMC error info 0x%x, HMC error data 0x%x\n",
+ rd32(hw, I40E_PFHMC_ERRORINFO),
+ rd32(hw, I40E_PFHMC_ERRORDATA));
}
if (icr0 & I40E_PFINT_ICR0_TIMESYNC_MASK) {
ctxt.pf_num = vsi->back->hw.pf_id;
ctxt.vf_num = 0;
ctxt.uplink_seid = vsi->uplink_seid;
- memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info));
+ ctxt.info = vsi->info;
i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, false);
/* Update the VSI after updating the VSI queue-mapping information */
goto exit;
}
- memset(&tmp_dcbx_cfg, 0, sizeof(tmp_dcbx_cfg));
/* Store the old configuration */
- memcpy(&tmp_dcbx_cfg, &hw->local_dcbx_config, sizeof(tmp_dcbx_cfg));
+ tmp_dcbx_cfg = hw->local_dcbx_config;
/* Reset the old DCBx configuration data */
memset(&hw->local_dcbx_config, 0, sizeof(hw->local_dcbx_config));
struct i40e_hw *hw = &pf->hw;
struct i40e_aqc_get_link_status *status =
(struct i40e_aqc_get_link_status *)&e->desc.params.raw;
- struct i40e_link_status *hw_link_info = &hw->phy.link_info;
/* save off old link status information */
- memcpy(&pf->hw.phy.link_info_old, hw_link_info,
- sizeof(pf->hw.phy.link_info_old));
+ hw->phy.link_info_old = hw->phy.link_info;
/* Do a new status request to re-enable LSE reporting
* and load new status information into the hw struct
{
struct i40e_hw *hw = &pf->hw;
i40e_status ret;
- u8 filter_index;
__be16 port;
int i;
if (pf->pending_vxlan_bitmap & (1 << i)) {
pf->pending_vxlan_bitmap &= ~(1 << i);
port = pf->vxlan_ports[i];
- ret = port ?
- i40e_aq_add_udp_tunnel(hw, ntohs(port),
+ if (port)
+ ret = i40e_aq_add_udp_tunnel(hw, ntohs(port),
I40E_AQC_TUNNEL_TYPE_VXLAN,
- &filter_index, NULL)
- : i40e_aq_del_udp_tunnel(hw, i, NULL);
+ NULL, NULL);
+ else
+ ret = i40e_aq_del_udp_tunnel(hw, i, NULL);
if (ret) {
- dev_info(&pf->pdev->dev, "Failed to execute AQ command for %s port %d with index %d\n",
- port ? "adding" : "deleting",
- ntohs(port), port ? i : i);
-
+ dev_info(&pf->pdev->dev,
+ "%s vxlan port %d, index %d failed, err %d, aq_err %d\n",
+ port ? "add" : "delete",
+ ntohs(port), i, ret,
+ pf->hw.aq.asq_last_status);
pf->vxlan_ports[i] = 0;
- } else {
- dev_info(&pf->pdev->dev, "%s port %d with AQ command with index %d\n",
- port ? "Added" : "Deleted",
- ntohs(port), port ? i : filter_index);
}
}
}
/* Check if port already exists */
if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
- netdev_info(netdev, "Port %d already offloaded\n", ntohs(port));
+ netdev_info(netdev, "vxlan port %d already offloaded\n",
+ ntohs(port));
return;
}
next_idx = i40e_get_vxlan_port_idx(pf, 0);
if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
- netdev_info(netdev, "Maximum number of UDP ports reached, not adding port %d\n",
+ netdev_info(netdev, "maximum number of vxlan UDP ports reached, not adding port %d\n",
ntohs(port));
return;
}
/* New port: add it and mark its index in the bitmap */
pf->vxlan_ports[next_idx] = port;
pf->pending_vxlan_bitmap |= (1 << next_idx);
-
pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+
+ dev_info(&pf->pdev->dev, "adding vxlan port %d\n", ntohs(port));
}
/**
* and make it pending
*/
pf->vxlan_ports[idx] = 0;
-
pf->pending_vxlan_bitmap |= (1 << idx);
-
pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+
+ dev_info(&pf->pdev->dev, "deleting vxlan port %d\n",
+ ntohs(port));
} else {
- netdev_warn(netdev, "Port %d was not found, not deleting\n",
+ netdev_warn(netdev, "vxlan port %d was not found, not deleting\n",
ntohs(port));
}
}
ret, pf->hw.aq.asq_last_status);
return -ENOENT;
}
- memcpy(&vsi->info, &ctxt.info, sizeof(ctxt.info));
+ vsi->info = ctxt.info;
vsi->info.valid_sections = 0;
vsi->seid = ctxt.seid;
ret = -ENOENT;
goto err;
}
- memcpy(&vsi->info, &ctxt.info, sizeof(ctxt.info));
+ vsi->info = ctxt.info;
vsi->info.valid_sections = 0;
vsi->seid = ctxt.seid;
vsi->id = ctxt.vsi_number;
set_bit(__I40E_DOWN, &pf->state);
del_timer_sync(&pf->service_timer);
cancel_work_sync(&pf->service_task);
+ i40e_fdir_teardown(pf);
+
rtnl_lock();
i40e_prep_for_reset(pf);
rtnl_unlock();
* any other fields out of the rx_desc until we know the
* DD bit is set.
*/
- rmb();
+ dma_rmb();
if (i40e_rx_is_programming_status(qword)) {
i40e_clean_programming_status(rx_ring, rx_desc);
I40E_RX_INCREMENT(rx_ring, i);
if (likely(!skb)) {
skb = netdev_alloc_skb_ip_align(rx_ring->netdev,
rx_ring->rx_hdr_len);
- if (!skb)
+ if (!skb) {
rx_ring->rx_stats.alloc_buff_failed++;
+ break;
+ }
+
/* initialize queue mapping */
skb_record_rx_queue(skb, rx_ring->queue_index);
/* we are reusing so sync this buffer for CPU use */
* any other fields out of the rx_desc until we know the
* DD bit is set.
*/
- rmb();
+ dma_rmb();
if (i40e_rx_is_programming_status(qword)) {
i40e_clean_programming_status(rx_ring, rx_desc);
__be16 protocol = skb->protocol;
u32 tx_flags = 0;
+ if (protocol == htons(ETH_P_8021Q) &&
+ !(tx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) {
+ /* When HW VLAN acceleration is turned off by the user the
+ * stack sets the protocol to 8021q so that the driver
+ * can take any steps required to support the SW only
+ * VLAN handling. In our case the driver doesn't need
+ * to take any further steps so just set the protocol
+ * to the encapsulated ethertype.
+ */
+ skb->protocol = vlan_get_protocol(skb);
+ goto out;
+ }
+
/* if we have a HW VLAN tag being added, default to the HW one */
if (skb_vlan_tag_present(skb)) {
tx_flags |= skb_vlan_tag_get(skb) << I40E_TX_FLAGS_VLAN_SHIFT;
*
* check for the valid VSI id
**/
-static inline bool i40e_vc_isvalid_vsi_id(struct i40e_vf *vf, u8 vsi_id)
+static inline bool i40e_vc_isvalid_vsi_id(struct i40e_vf *vf, u16 vsi_id)
{
struct i40e_pf *pf = vf->pf;
+ struct i40e_vsi *vsi = i40e_find_vsi_from_id(pf, vsi_id);
- return pf->vsi[vsi_id]->vf_id == vf->vf_id;
+ return (vsi && (vsi->vf_id == vf->vf_id));
}
/**
*
* check for the valid queue id
**/
-static inline bool i40e_vc_isvalid_queue_id(struct i40e_vf *vf, u8 vsi_id,
+static inline bool i40e_vc_isvalid_queue_id(struct i40e_vf *vf, u16 vsi_id,
u8 qid)
{
struct i40e_pf *pf = vf->pf;
+ struct i40e_vsi *vsi = i40e_find_vsi_from_id(pf, vsi_id);
- return qid < pf->vsi[vsi_id]->alloc_queue_pairs;
+ return (vsi && (qid < vsi->alloc_queue_pairs));
}
/**
/**
* i40e_vc_get_pf_queue_id
* @vf: pointer to the VF info
- * @vsi_idx: index of VSI in PF struct
+ * @vsi_id: id of VSI as provided by the FW
* @vsi_queue_id: vsi relative queue id
*
* return PF relative queue id
**/
-static u16 i40e_vc_get_pf_queue_id(struct i40e_vf *vf, u8 vsi_idx,
+static u16 i40e_vc_get_pf_queue_id(struct i40e_vf *vf, u16 vsi_id,
u8 vsi_queue_id)
{
struct i40e_pf *pf = vf->pf;
- struct i40e_vsi *vsi = pf->vsi[vsi_idx];
+ struct i40e_vsi *vsi = i40e_find_vsi_from_id(pf, vsi_id);
u16 pf_queue_id = I40E_QUEUE_END_OF_LIST;
+ if (!vsi)
+ return pf_queue_id;
+
if (le16_to_cpu(vsi->info.mapping_flags) &
I40E_AQ_VSI_QUE_MAP_NONCONTIG)
pf_queue_id =
/**
* i40e_config_irq_link_list
* @vf: pointer to the VF info
- * @vsi_idx: index of VSI in PF struct
+ * @vsi_id: id of VSI as given by the FW
* @vecmap: irq map info
*
* configure irq link list from the map
**/
-static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_idx,
+static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_id,
struct i40e_virtchnl_vector_map *vecmap)
{
unsigned long linklistmap = 0, tempmap;
I40E_VIRTCHNL_SUPPORTED_QTYPES));
vsi_queue_id = next_q/I40E_VIRTCHNL_SUPPORTED_QTYPES;
qtype = next_q%I40E_VIRTCHNL_SUPPORTED_QTYPES;
- pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_idx, vsi_queue_id);
+ pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id);
reg = ((qtype << I40E_VPINT_LNKLSTN_FIRSTQ_TYPE_SHIFT) | pf_queue_id);
wr32(hw, reg_idx, reg);
(I40E_MAX_VSI_QP * I40E_VIRTCHNL_SUPPORTED_QTYPES)) {
vsi_queue_id = next_q / I40E_VIRTCHNL_SUPPORTED_QTYPES;
qtype = next_q % I40E_VIRTCHNL_SUPPORTED_QTYPES;
- pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_idx,
+ pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id,
vsi_queue_id);
} else {
pf_queue_id = I40E_QUEUE_END_OF_LIST;
/**
* i40e_config_vsi_tx_queue
* @vf: pointer to the VF info
- * @vsi_idx: index of VSI in PF struct
+ * @vsi_id: id of VSI as provided by the FW
* @vsi_queue_id: vsi relative queue index
* @info: config. info
*
* configure tx queue
**/
-static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_idx,
+static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_id,
u16 vsi_queue_id,
struct i40e_virtchnl_txq_info *info)
{
struct i40e_pf *pf = vf->pf;
struct i40e_hw *hw = &pf->hw;
struct i40e_hmc_obj_txq tx_ctx;
+ struct i40e_vsi *vsi;
u16 pf_queue_id;
u32 qtx_ctl;
int ret = 0;
- pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_idx, vsi_queue_id);
+ pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id);
+ vsi = i40e_find_vsi_from_id(pf, vsi_id);
/* clear the context structure first */
memset(&tx_ctx, 0, sizeof(struct i40e_hmc_obj_txq));
/* only set the required fields */
tx_ctx.base = info->dma_ring_addr / 128;
tx_ctx.qlen = info->ring_len;
- tx_ctx.rdylist = le16_to_cpu(pf->vsi[vsi_idx]->info.qs_handle[0]);
+ tx_ctx.rdylist = le16_to_cpu(vsi->info.qs_handle[0]);
tx_ctx.rdylist_act = 0;
tx_ctx.head_wb_ena = info->headwb_enabled;
tx_ctx.head_wb_addr = info->dma_headwb_addr;
/**
* i40e_config_vsi_rx_queue
* @vf: pointer to the VF info
- * @vsi_idx: index of VSI in PF struct
+ * @vsi_id: id of VSI as provided by the FW
* @vsi_queue_id: vsi relative queue index
* @info: config. info
*
* configure rx queue
**/
-static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_idx,
+static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_id,
u16 vsi_queue_id,
struct i40e_virtchnl_rxq_info *info)
{
u16 pf_queue_id;
int ret = 0;
- pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_idx, vsi_queue_id);
+ pf_queue_id = i40e_vc_get_pf_queue_id(vf, vsi_id, vsi_queue_id);
/* clear the context structure first */
memset(&rx_ctx, 0, sizeof(struct i40e_hmc_obj_rxq));
}
if (type == I40E_VSI_SRIOV) {
u8 brdcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- vf->lan_vsi_index = vsi->idx;
+ vf->lan_vsi_idx = vsi->idx;
vf->lan_vsi_id = vsi->id;
/* If the port VLAN has been configured and then the
* VF driver was removed then the VSI port VLAN
wr32(hw, I40E_VPLAN_MAPENA(vf->vf_id), reg);
/* map PF queues to VF queues */
- for (j = 0; j < pf->vsi[vf->lan_vsi_index]->alloc_queue_pairs; j++) {
- u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_index, j);
+ for (j = 0; j < pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; j++) {
+ u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id, j);
reg = (qid & I40E_VPLAN_QTABLE_QINDEX_MASK);
wr32(hw, I40E_VPLAN_QTABLE(total_queue_pairs, vf->vf_id), reg);
total_queue_pairs++;
/* map PF queues to VSI */
for (j = 0; j < 7; j++) {
- if (j * 2 >= pf->vsi[vf->lan_vsi_index]->alloc_queue_pairs) {
+ if (j * 2 >= pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs) {
reg = 0x07FF07FF; /* unused */
} else {
- u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_index,
+ u16 qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id,
j * 2);
reg = qid;
- qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_index,
+ qid = i40e_vc_get_pf_queue_id(vf, vf->lan_vsi_id,
(j * 2) + 1);
reg |= qid << 16;
}
int i, msix_vf;
/* free vsi & disconnect it from the parent uplink */
- if (vf->lan_vsi_index) {
- i40e_vsi_release(pf->vsi[vf->lan_vsi_index]);
- vf->lan_vsi_index = 0;
+ if (vf->lan_vsi_idx) {
+ i40e_vsi_release(pf->vsi[vf->lan_vsi_idx]);
+ vf->lan_vsi_idx = 0;
vf->lan_vsi_id = 0;
}
msix_vf = pf->hw.func_caps.num_msix_vectors_vf;
ret = i40e_alloc_vsi_res(vf, I40E_VSI_SRIOV);
if (ret)
goto error_alloc;
- total_queue_pairs += pf->vsi[vf->lan_vsi_index]->alloc_queue_pairs;
+ total_queue_pairs += pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs;
set_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
/* store the total qps number for the runtime
wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg);
/* On initial reset, we won't have any queues */
- if (vf->lan_vsi_index == 0)
+ if (vf->lan_vsi_idx == 0)
goto complete_reset;
- i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_index], false);
+ i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_idx], false);
complete_reset:
/* reallocate VF resources to reset the VSI state */
i40e_free_vf_res(vf);
*/
if (!pci_vfs_assigned(pf->pdev))
pci_disable_sriov(pf->pdev);
+ else
+ dev_warn(&pf->pdev->dev, "VFs are assigned - not disabling SR-IOV\n");
msleep(20); /* let any messages in transit get finished up */
bit_idx = (hw->func_caps.vf_base_id + vf_id) % 32;
wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), (1 << bit_idx));
}
- } else {
- dev_warn(&pf->pdev->dev,
- "unable to disable SR-IOV because VFs are assigned.\n");
}
clear_bit(__I40E_VF_DISABLE, &pf->state);
}
}
vfres->vf_offload_flags = I40E_VIRTCHNL_VF_OFFLOAD_L2;
- vsi = pf->vsi[vf->lan_vsi_index];
+ vsi = pf->vsi[vf->lan_vsi_idx];
if (!vsi->info.pvid)
vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_VLAN;
vfres->num_vsis = num_vsis;
vfres->num_queue_pairs = vf->num_queue_pairs;
vfres->max_vectors = pf->hw.func_caps.num_msix_vectors_vf;
- if (vf->lan_vsi_index) {
- vfres->vsi_res[i].vsi_id = vf->lan_vsi_index;
+ if (vf->lan_vsi_idx) {
+ vfres->vsi_res[i].vsi_id = vf->lan_vsi_id;
vfres->vsi_res[i].vsi_type = I40E_VSI_SRIOV;
vfres->vsi_res[i].num_queue_pairs =
- pf->vsi[vf->lan_vsi_index]->alloc_queue_pairs;
+ pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs;
memcpy(vfres->vsi_res[i].default_mac_addr,
vf->default_lan_addr.addr, ETH_ALEN);
i++;
bool allmulti = false;
i40e_status aq_ret;
+ vsi = i40e_find_vsi_from_id(pf, info->vsi_id);
if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) ||
!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) ||
!i40e_vc_isvalid_vsi_id(vf, info->vsi_id) ||
- (pf->vsi[info->vsi_id]->type != I40E_VSI_FCOE)) {
+ (vsi->type != I40E_VSI_FCOE)) {
aq_ret = I40E_ERR_PARAM;
goto error_param;
}
- vsi = pf->vsi[info->vsi_id];
if (info->flags & I40E_FLAG_VF_MULTICAST_PROMISC)
allmulti = true;
aq_ret = i40e_aq_set_vsi_multicast_promiscuous(hw, vsi->seid,
}
}
/* set vsi num_queue_pairs in use to num configured by VF */
- pf->vsi[vf->lan_vsi_index]->num_queue_pairs = qci->num_queue_pairs;
+ pf->vsi[vf->lan_vsi_idx]->num_queue_pairs = qci->num_queue_pairs;
error_param:
/* send the response to the VF */
aq_ret = I40E_ERR_PARAM;
goto error_param;
}
- if (i40e_vsi_control_rings(pf->vsi[vsi_id], true))
+
+ if (i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_idx], true))
aq_ret = I40E_ERR_TIMEOUT;
error_param:
/* send the response to the VF */
struct i40e_virtchnl_queue_select *vqs =
(struct i40e_virtchnl_queue_select *)msg;
struct i40e_pf *pf = vf->pf;
- u16 vsi_id = vqs->vsi_id;
i40e_status aq_ret = 0;
if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) {
aq_ret = I40E_ERR_PARAM;
goto error_param;
}
- if (i40e_vsi_control_rings(pf->vsi[vsi_id], false))
+
+ if (i40e_vsi_control_rings(pf->vsi[vf->lan_vsi_idx], false))
aq_ret = I40E_ERR_TIMEOUT;
error_param:
goto error_param;
}
- vsi = pf->vsi[vqs->vsi_id];
+ vsi = pf->vsi[vf->lan_vsi_idx];
if (!vsi) {
aq_ret = I40E_ERR_PARAM;
goto error_param;
if (ret)
goto error_param;
}
- vsi = pf->vsi[vsi_id];
+ vsi = pf->vsi[vf->lan_vsi_idx];
/* add new addresses to the list */
for (i = 0; i < al->num_elements; i++) {
goto error_param;
}
}
- vsi = pf->vsi[vsi_id];
+ vsi = pf->vsi[vf->lan_vsi_idx];
/* delete addresses from the list */
for (i = 0; i < al->num_elements; i++)
goto error_param;
}
}
- vsi = pf->vsi[vsi_id];
+ vsi = pf->vsi[vf->lan_vsi_idx];
if (vsi->info.pvid) {
aq_ret = I40E_ERR_PARAM;
goto error_param;
}
}
- vsi = pf->vsi[vsi_id];
+ vsi = pf->vsi[vf->lan_vsi_idx];
if (vsi->info.pvid) {
aq_ret = I40E_ERR_PARAM;
goto error_param;
}
vf = &(pf->vf[vf_id]);
- vsi = pf->vsi[vf->lan_vsi_index];
+ vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
dev_err(&pf->pdev->dev,
"Uninitialized VF %d\n", vf_id);
}
vf = &(pf->vf[vf_id]);
- vsi = pf->vsi[vf->lan_vsi_index];
+ vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id);
ret = -EINVAL;
}
vf = &(pf->vf[vf_id]);
- vsi = pf->vsi[vf->lan_vsi_index];
+ vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
dev_err(&pf->pdev->dev, "Uninitialized VF %d.\n", vf_id);
ret = -EINVAL;
vf = &(pf->vf[vf_id]);
/* first vsi is always the LAN vsi */
- vsi = pf->vsi[vf->lan_vsi_index];
+ vsi = pf->vsi[vf->lan_vsi_idx];
if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) {
dev_err(&pf->pdev->dev, "Uninitialized VF %d\n", vf_id);
ret = -EINVAL;
vf->spoofchk = enable;
memset(&ctxt, 0, sizeof(ctxt));
- ctxt.seid = pf->vsi[vf->lan_vsi_index]->seid;
+ ctxt.seid = pf->vsi[vf->lan_vsi_idx]->seid;
ctxt.pf_num = pf->hw.pf_id;
ctxt.info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID);
if (enable)
* When assigned, these will be non-zero, because VSI 0 is always
* the main LAN VSI for the PF.
*/
- u8 lan_vsi_index; /* index into PF struct */
+ u8 lan_vsi_idx; /* index into PF struct */
u8 lan_vsi_id; /* ID as used by firmware */
u8 num_queue_pairs; /* num of qps assigned to VF vsis */
* so the total length of IPv4 header is IHL*4 bytes
* The UDP_0 bit *may* bet set if the *inner* header is UDP
*/
- if (ipv4_tunnel &&
- (decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) &&
- !(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) {
+ if (ipv4_tunnel) {
skb->transport_header = skb->mac_header +
sizeof(struct ethhdr) +
(ip_hdr(skb)->ihl * 4);
skb->protocol == htons(ETH_P_8021AD))
? VLAN_HLEN : 0;
- rx_udp_csum = udp_csum(skb);
- iph = ip_hdr(skb);
- csum = csum_tcpudp_magic(
- iph->saddr, iph->daddr,
- (skb->len - skb_transport_offset(skb)),
- IPPROTO_UDP, rx_udp_csum);
+ if ((ip_hdr(skb)->protocol == IPPROTO_UDP) &&
+ (udp_hdr(skb)->check != 0)) {
+ rx_udp_csum = udp_csum(skb);
+ iph = ip_hdr(skb);
+ csum = csum_tcpudp_magic(iph->saddr, iph->daddr,
+ (skb->len -
+ skb_transport_offset(skb)),
+ IPPROTO_UDP, rx_udp_csum);
- if (udp_hdr(skb)->check != csum)
- goto checksum_fail;
+ if (udp_hdr(skb)->check != csum)
+ goto checksum_fail;
+
+ } /* else its GRE and so no outer UDP header */
}
skb->ip_summed = CHECKSUM_UNNECESSARY;
* any other fields out of the rx_desc until we know the
* DD bit is set.
*/
- rmb();
+ dma_rmb();
rx_bi = &rx_ring->rx_bi[i];
skb = rx_bi->skb;
if (likely(!skb)) {
skb = netdev_alloc_skb_ip_align(rx_ring->netdev,
rx_ring->rx_hdr_len);
- if (!skb)
+ if (!skb) {
rx_ring->rx_stats.alloc_buff_failed++;
+ break;
+ }
+
/* initialize queue mapping */
skb_record_rx_queue(skb, rx_ring->queue_index);
/* we are reusing so sync this buffer for CPU use */
* any other fields out of the rx_desc until we know the
* DD bit is set.
*/
- rmb();
+ dma_rmb();
rx_bi = &rx_ring->rx_bi[i];
skb = rx_bi->skb;
__be16 protocol = skb->protocol;
u32 tx_flags = 0;
+ if (protocol == htons(ETH_P_8021Q) &&
+ !(tx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) {
+ /* When HW VLAN acceleration is turned off by the user the
+ * stack sets the protocol to 8021q so that the driver
+ * can take any steps required to support the SW only
+ * VLAN handling. In our case the driver doesn't need
+ * to take any further steps so just set the protocol
+ * to the encapsulated ethertype.
+ */
+ skb->protocol = vlan_get_protocol(skb);
+ goto out;
+ }
+
/* if we have a HW VLAN tag being added, default to the HW one */
if (skb_vlan_tag_present(skb)) {
tx_flags |= skb_vlan_tag_get(skb) << I40E_TX_FLAGS_VLAN_SHIFT;
tx_flags |= I40E_TX_FLAGS_SW_VLAN;
}
+out:
*flags = tx_flags;
return 0;
}
static struct
i40evf_vlan_filter *i40evf_add_vlan(struct i40evf_adapter *adapter, u16 vlan)
{
- struct i40evf_vlan_filter *f;
+ struct i40evf_vlan_filter *f = NULL;
+ int count = 50;
+
+ while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
+ &adapter->crit_section)) {
+ udelay(1);
+ if (--count == 0)
+ goto out;
+ }
f = i40evf_find_vlan(adapter, vlan);
if (!f) {
f = kzalloc(sizeof(*f), GFP_ATOMIC);
if (!f)
- return NULL;
+ goto clearout;
f->vlan = vlan;
adapter->aq_required |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER;
}
+clearout:
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
+out:
return f;
}
static void i40evf_del_vlan(struct i40evf_adapter *adapter, u16 vlan)
{
struct i40evf_vlan_filter *f;
+ int count = 50;
+
+ while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK,
+ &adapter->crit_section)) {
+ udelay(1);
+ if (--count == 0)
+ return;
+ }
f = i40evf_find_vlan(adapter, vlan);
if (f) {
f->remove = true;
adapter->aq_required |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER;
}
+ clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section);
}
/**
}
/**
- * next_queue - increment to next available tx queue
- * @adapter: board private structure
- * @j: queue counter
- *
- * Helper function for RSS programming to increment through available
- * queus. Returns the next queue value.
- **/
-static int next_queue(struct i40evf_adapter *adapter, int j)
-{
- j += 1;
-
- return j >= adapter->num_active_queues ? 0 : j;
-}
-
-/**
- * i40evf_configure_rss - Prepare for RSS if used
+ * i40evf_configure_rss - Prepare for RSS
* @adapter: board private structure
**/
static void i40evf_configure_rss(struct i40evf_adapter *adapter)
{
u32 rss_key[I40E_VFQF_HKEY_MAX_INDEX + 1];
struct i40e_hw *hw = &adapter->hw;
+ u32 cqueue = 0;
u32 lut = 0;
int i, j;
u64 hena;
- /* No RSS for single queue. */
- if (adapter->num_active_queues == 1) {
- wr32(hw, I40E_VFQF_HENA(0), 0);
- wr32(hw, I40E_VFQF_HENA(1), 0);
- return;
- }
-
/* Hash type is configured by the PF - we just supply the key */
netdev_rss_key_fill(rss_key, sizeof(rss_key));
+
+ /* Fill out hash function seed */
for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
wr32(hw, I40E_VFQF_HKEY(i), rss_key[i]);
wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32));
/* Populate the LUT with max no. of queues in round robin fashion */
- j = adapter->num_active_queues;
for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
- j = next_queue(adapter, j);
- lut = j;
- j = next_queue(adapter, j);
- lut |= j << 8;
- j = next_queue(adapter, j);
- lut |= j << 16;
- j = next_queue(adapter, j);
- lut |= j << 24;
+ lut = 0;
+ for (j = 0; j < 4; j++) {
+ if (cqueue == adapter->vsi_res->num_queue_pairs)
+ cqueue = 0;
+ lut |= ((cqueue) << (8 * j));
+ cqueue++;
+ }
wr32(hw, I40E_VFQF_HLUT(i), lut);
}
i40e_flush(hw);
#define IXGBE_FLAG2_RSS_FIELD_IPV4_UDP (u32)(1 << 8)
#define IXGBE_FLAG2_RSS_FIELD_IPV6_UDP (u32)(1 << 9)
#define IXGBE_FLAG2_PTP_PPS_ENABLED (u32)(1 << 10)
-#define IXGBE_FLAG2_BRIDGE_MODE_VEB (u32)(1 << 11)
/* Tx fast path data */
int num_tx_queues;
u8 __iomem *io_addr; /* Mainly for iounmap use */
u32 wol;
+ u16 bridge_mode;
+
u16 eeprom_verh;
u16 eeprom_verl;
u16 eeprom_cap;
struct ixgbe_fcoe *fcoe;
struct ixgbe_adapter *adapter;
struct ixgbe_fcoe_ddp *ddp;
+ struct ixgbe_hw *hw;
u32 fcbuff;
if (!netdev)
if (!ddp->udl)
return 0;
+ hw = &adapter->hw;
len = ddp->len;
- /* if there an error, force to invalidate ddp context */
- if (ddp->err) {
+ /* if no error then skip ddp context invalidation */
+ if (!ddp->err)
+ goto skip_ddpinv;
+
+ if (hw->mac.type == ixgbe_mac_X550) {
+ /* X550 does not require DDP FCoE lock */
+
+ IXGBE_WRITE_REG(hw, IXGBE_FCDFC(0, xid), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_FCDFC(3, xid),
+ (xid | IXGBE_FCFLTRW_WE));
+
+ /* program FCBUFF */
+ IXGBE_WRITE_REG(hw, IXGBE_FCDDC(2, xid), 0);
+
+ /* program FCDMARW */
+ IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid),
+ (xid | IXGBE_FCDMARW_WE));
+
+ /* read FCBUFF to check context invalidated */
+ IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid),
+ (xid | IXGBE_FCDMARW_RE));
+ fcbuff = IXGBE_READ_REG(hw, IXGBE_FCDDC(2, xid));
+ } else {
+ /* other hardware requires DDP FCoE lock */
spin_lock_bh(&fcoe->lock);
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCFLT, 0);
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCFLTRW,
+ IXGBE_WRITE_REG(hw, IXGBE_FCFLT, 0);
+ IXGBE_WRITE_REG(hw, IXGBE_FCFLTRW,
(xid | IXGBE_FCFLTRW_WE));
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCBUFF, 0);
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCDMARW,
+ IXGBE_WRITE_REG(hw, IXGBE_FCBUFF, 0);
+ IXGBE_WRITE_REG(hw, IXGBE_FCDMARW,
(xid | IXGBE_FCDMARW_WE));
/* guaranteed to be invalidated after 100us */
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCDMARW,
+ IXGBE_WRITE_REG(hw, IXGBE_FCDMARW,
(xid | IXGBE_FCDMARW_RE));
- fcbuff = IXGBE_READ_REG(&adapter->hw, IXGBE_FCBUFF);
+ fcbuff = IXGBE_READ_REG(hw, IXGBE_FCBUFF);
spin_unlock_bh(&fcoe->lock);
- if (fcbuff & IXGBE_FCBUFF_VALID)
- udelay(100);
- }
+ }
+
+ if (fcbuff & IXGBE_FCBUFF_VALID)
+ usleep_range(100, 150);
+
+skip_ddpinv:
if (ddp->sgl)
dma_unmap_sg(&adapter->pdev->dev, ddp->sgl, ddp->sgc,
DMA_FROM_DEVICE);
/* program DMA context */
hw = &adapter->hw;
- spin_lock_bh(&fcoe->lock);
/* turn on last frame indication for target mode as FCP_RSPtarget is
* supposed to send FCP_RSP when it is done. */
IXGBE_WRITE_REG(hw, IXGBE_FCRXCTRL, fcrxctl);
}
- IXGBE_WRITE_REG(hw, IXGBE_FCPTRL, ddp->udp & DMA_BIT_MASK(32));
- IXGBE_WRITE_REG(hw, IXGBE_FCPTRH, (u64)ddp->udp >> 32);
- IXGBE_WRITE_REG(hw, IXGBE_FCBUFF, fcbuff);
- IXGBE_WRITE_REG(hw, IXGBE_FCDMARW, fcdmarw);
- /* program filter context */
- IXGBE_WRITE_REG(hw, IXGBE_FCPARAM, 0);
- IXGBE_WRITE_REG(hw, IXGBE_FCFLT, IXGBE_FCFLT_VALID);
- IXGBE_WRITE_REG(hw, IXGBE_FCFLTRW, fcfltrw);
+ if (hw->mac.type == ixgbe_mac_X550) {
+ /* X550 does not require DDP lock */
+
+ IXGBE_WRITE_REG(hw, IXGBE_FCDDC(0, xid),
+ ddp->udp & DMA_BIT_MASK(32));
+ IXGBE_WRITE_REG(hw, IXGBE_FCDDC(1, xid), (u64)ddp->udp >> 32);
+ IXGBE_WRITE_REG(hw, IXGBE_FCDDC(2, xid), fcbuff);
+ IXGBE_WRITE_REG(hw, IXGBE_FCDDC(3, xid), fcdmarw);
+ /* program filter context */
+ IXGBE_WRITE_REG(hw, IXGBE_FCDFC(0, xid), IXGBE_FCFLT_VALID);
+ IXGBE_WRITE_REG(hw, IXGBE_FCDFC(1, xid), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_FCDFC(3, xid), fcfltrw);
+ } else {
+ /* DDP lock for indirect DDP context access */
+ spin_lock_bh(&fcoe->lock);
+
+ IXGBE_WRITE_REG(hw, IXGBE_FCPTRL, ddp->udp & DMA_BIT_MASK(32));
+ IXGBE_WRITE_REG(hw, IXGBE_FCPTRH, (u64)ddp->udp >> 32);
+ IXGBE_WRITE_REG(hw, IXGBE_FCBUFF, fcbuff);
+ IXGBE_WRITE_REG(hw, IXGBE_FCDMARW, fcdmarw);
+ /* program filter context */
+ IXGBE_WRITE_REG(hw, IXGBE_FCPARAM, 0);
+ IXGBE_WRITE_REG(hw, IXGBE_FCFLT, IXGBE_FCFLT_VALID);
+ IXGBE_WRITE_REG(hw, IXGBE_FCFLTRW, fcfltrw);
- spin_unlock_bh(&fcoe->lock);
+ spin_unlock_bh(&fcoe->lock);
+ }
return 1;
struct fcoe_crc_eof *crc;
__le32 fcerr = ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_FCERR);
__le32 ddp_err;
+ int ddp_max;
u32 fctl;
u16 xid;
else
xid = be16_to_cpu(fh->fh_rx_id);
- if (xid >= IXGBE_FCOE_DDP_MAX)
+ ddp_max = IXGBE_FCOE_DDP_MAX;
+ /* X550 has different DDP Max limit */
+ if (adapter->hw.mac.type == ixgbe_mac_X550)
+ ddp_max = IXGBE_FCOE_DDP_MAX_X550;
+ if (xid >= ddp_max)
return -EINVAL;
fcoe = &adapter->fcoe;
{
struct ixgbe_ring_feature *fcoe = &adapter->ring_feature[RING_F_FCOE];
struct ixgbe_hw *hw = &adapter->hw;
- int i, fcoe_q, fcoe_i;
+ int i, fcoe_q, fcoe_i, fcoe_q_h = 0;
+ int fcreta_size;
u32 etqf;
/* Minimal functionality for FCoE requires at least CRC offloads */
return;
/* Use one or more Rx queues for FCoE by redirection table */
- for (i = 0; i < IXGBE_FCRETA_SIZE; i++) {
+ fcreta_size = IXGBE_FCRETA_SIZE;
+ if (adapter->hw.mac.type == ixgbe_mac_X550)
+ fcreta_size = IXGBE_FCRETA_SIZE_X550;
+
+ for (i = 0; i < fcreta_size; i++) {
+ if (adapter->hw.mac.type == ixgbe_mac_X550) {
+ int fcoe_i_h = fcoe->offset + ((i + fcreta_size) %
+ fcoe->indices);
+ fcoe_q_h = adapter->rx_ring[fcoe_i_h]->reg_idx;
+ fcoe_q_h = (fcoe_q_h << IXGBE_FCRETA_ENTRY_HIGH_SHIFT) &
+ IXGBE_FCRETA_ENTRY_HIGH_MASK;
+ }
+
fcoe_i = fcoe->offset + (i % fcoe->indices);
fcoe_i &= IXGBE_FCRETA_ENTRY_MASK;
fcoe_q = adapter->rx_ring[fcoe_i]->reg_idx;
+ fcoe_q |= fcoe_q_h;
IXGBE_WRITE_REG(hw, IXGBE_FCRETA(i), fcoe_q);
}
IXGBE_WRITE_REG(hw, IXGBE_FCRECTL, IXGBE_FCRECTL_ENA);
void ixgbe_free_fcoe_ddp_resources(struct ixgbe_adapter *adapter)
{
struct ixgbe_fcoe *fcoe = &adapter->fcoe;
- int cpu, i;
+ int cpu, i, ddp_max;
/* do nothing if no DDP pools were allocated */
if (!fcoe->ddp_pool)
return;
- for (i = 0; i < IXGBE_FCOE_DDP_MAX; i++)
+ ddp_max = IXGBE_FCOE_DDP_MAX;
+ /* X550 has different DDP Max limit */
+ if (adapter->hw.mac.type == ixgbe_mac_X550)
+ ddp_max = IXGBE_FCOE_DDP_MAX_X550;
+
+ for (i = 0; i < ddp_max; i++)
ixgbe_fcoe_ddp_put(adapter->netdev, i);
for_each_possible_cpu(cpu)
}
adapter->netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX - 1;
+ /* X550 has different DDP Max limit */
+ if (adapter->hw.mac.type == ixgbe_mac_X550)
+ adapter->netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX_X550 - 1;
return 0;
}
#define IXGBE_FCBUFF_MAX 65536 /* 64KB max */
#define IXGBE_FCBUFF_MIN 4096 /* 4KB min */
#define IXGBE_FCOE_DDP_MAX 512 /* 9 bits xid */
+#define IXGBE_FCOE_DDP_MAX_X550 2048 /* 11 bits xid */
/* Default traffic class to use for FCoE */
#define IXGBE_FCOE_DEFTC 3
struct ixgbe_fcoe_ddp_pool __percpu *ddp_pool;
atomic_t refcnt;
spinlock_t lock;
- struct ixgbe_fcoe_ddp ddp[IXGBE_FCOE_DDP_MAX];
+ struct ixgbe_fcoe_ddp ddp[IXGBE_FCOE_DDP_MAX_X550];
void *extra_ddp_buffer;
dma_addr_t extra_ddp_buffer_dma;
unsigned long mode;
IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset ^ 1), reg_offset - 1);
IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset), (~0) << vf_shift);
IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset ^ 1), reg_offset - 1);
- if (adapter->flags2 & IXGBE_FLAG2_BRIDGE_MODE_VEB)
+ if (adapter->bridge_mode == BRIDGE_MODE_VEB)
IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN);
/* Map PF MAC address in RAR Entry 0 to first pool following VFs */
return ndo_dflt_fdb_add(ndm, tb, dev, addr, vid, flags);
}
+/**
+ * ixgbe_configure_bridge_mode - set various bridge modes
+ * @adapter - the private structure
+ * @mode - requested bridge mode
+ *
+ * Configure some settings require for various bridge modes.
+ **/
+static int ixgbe_configure_bridge_mode(struct ixgbe_adapter *adapter,
+ __u16 mode)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ unsigned int p, num_pools;
+ u32 vmdctl;
+
+ switch (mode) {
+ case BRIDGE_MODE_VEPA:
+ /* disable Tx loopback, rely on switch hairpin mode */
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_PFDTXGSWC, 0);
+
+ /* must enable Rx switching replication to allow multicast
+ * packet reception on all VFs, and to enable source address
+ * pruning.
+ */
+ vmdctl = IXGBE_READ_REG(hw, IXGBE_VMD_CTL);
+ vmdctl |= IXGBE_VT_CTL_REPLEN;
+ IXGBE_WRITE_REG(hw, IXGBE_VMD_CTL, vmdctl);
+
+ /* enable Rx source address pruning. Note, this requires
+ * replication to be enabled or else it does nothing.
+ */
+ num_pools = adapter->num_vfs + adapter->num_rx_pools;
+ for (p = 0; p < num_pools; p++) {
+ if (hw->mac.ops.set_source_address_pruning)
+ hw->mac.ops.set_source_address_pruning(hw,
+ true,
+ p);
+ }
+ break;
+ case BRIDGE_MODE_VEB:
+ /* enable Tx loopback for internal VF/PF communication */
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_PFDTXGSWC,
+ IXGBE_PFDTXGSWC_VT_LBEN);
+
+ /* disable Rx switching replication unless we have SR-IOV
+ * virtual functions
+ */
+ vmdctl = IXGBE_READ_REG(hw, IXGBE_VMD_CTL);
+ if (!adapter->num_vfs)
+ vmdctl &= ~IXGBE_VT_CTL_REPLEN;
+ IXGBE_WRITE_REG(hw, IXGBE_VMD_CTL, vmdctl);
+
+ /* disable Rx source address pruning, since we don't expect to
+ * be receiving external loopback of our transmitted frames.
+ */
+ num_pools = adapter->num_vfs + adapter->num_rx_pools;
+ for (p = 0; p < num_pools; p++) {
+ if (hw->mac.ops.set_source_address_pruning)
+ hw->mac.ops.set_source_address_pruning(hw,
+ false,
+ p);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adapter->bridge_mode = mode;
+
+ e_info(drv, "enabling bridge mode: %s\n",
+ mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB");
+
+ return 0;
+}
+
static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
struct nlmsghdr *nlh, u16 flags)
{
return -EINVAL;
nla_for_each_nested(attr, br_spec, rem) {
+ u32 status;
__u16 mode;
- u32 reg = 0;
if (nla_type(attr) != IFLA_BRIDGE_MODE)
continue;
return -EINVAL;
mode = nla_get_u16(attr);
- if (mode == BRIDGE_MODE_VEPA) {
- reg = 0;
- adapter->flags2 &= ~IXGBE_FLAG2_BRIDGE_MODE_VEB;
- } else if (mode == BRIDGE_MODE_VEB) {
- reg = IXGBE_PFDTXGSWC_VT_LBEN;
- adapter->flags2 |= IXGBE_FLAG2_BRIDGE_MODE_VEB;
- } else
- return -EINVAL;
-
- IXGBE_WRITE_REG(&adapter->hw, IXGBE_PFDTXGSWC, reg);
+ status = ixgbe_configure_bridge_mode(adapter, mode);
+ if (status)
+ return status;
- e_info(drv, "enabling bridge mode: %s\n",
- mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB");
+ break;
}
return 0;
u32 filter_mask)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
- u16 mode;
if (!(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED))
return 0;
- if (adapter->flags2 & IXGBE_FLAG2_BRIDGE_MODE_VEB)
- mode = BRIDGE_MODE_VEB;
- else
- mode = BRIDGE_MODE_VEPA;
-
- return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode, 0, 0);
+ return ndo_dflt_bridge_getlink(skb, pid, seq, dev,
+ adapter->bridge_mode, 0, 0);
}
static void *ixgbe_fwd_add(struct net_device *pdev, struct net_device *vdev)
NETIF_F_IPV6_CSUM |
NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_CTAG_RX |
- NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_TSO |
NETIF_F_TSO6 |
NETIF_F_RXHASH |
}
netdev->hw_features |= NETIF_F_RXALL;
+ netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
netdev->vlan_features |= NETIF_F_TSO;
netdev->vlan_features |= NETIF_F_TSO6;
pci_unregister_driver(&ixgbe_driver);
ixgbe_dbg_exit();
-
- rcu_barrier(); /* Wait for completion of call_rcu()'s */
}
#ifdef CONFIG_IXGBE_DCA
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/ipv6.h>
+#include <linux/if_bridge.h>
#ifdef NETIF_F_HW_VLAN_CTAG_TX
#include <linux/if_vlan.h>
#endif
/* Initialize default switching mode VEB */
IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN);
- adapter->flags2 |= IXGBE_FLAG2_BRIDGE_MODE_VEB;
+ adapter->bridge_mode = BRIDGE_MODE_VEB;
/* If call to enable VFs succeeded then allocate memory
* for per VF control structures.
#define IXGBE_VLVF(_i) (0x0F100 + ((_i) * 4)) /* 64 of these (0-63) */
#define IXGBE_VLVFB(_i) (0x0F200 + ((_i) * 4)) /* 128 of these (0-127) */
#define IXGBE_VMVIR(_i) (0x08000 + ((_i) * 4)) /* 64 of these (0-63) */
+#define IXGBE_PFFLPL 0x050B0
+#define IXGBE_PFFLPH 0x050B4
#define IXGBE_VT_CTL 0x051B0
#define IXGBE_PFMAILBOX(_i) (0x04B00 + (4 * (_i))) /* 64 total */
#define IXGBE_PFMBMEM(_i) (0x13000 + (64 * (_i))) /* 64 Mailboxes, 16 DW each */
#define IXGBE_RTTBCNRM 0x04980
#define IXGBE_RTTQCNRM 0x04980
+/* FCoE Direct DMA Context */
+#define IXGBE_FCDDC(_i, _j) (0x20000 + ((_i) * 0x4) + ((_j) * 0x10))
/* FCoE DMA Context Registers */
#define IXGBE_FCPTRL 0x02410 /* FC User Desc. PTR Low */
#define IXGBE_FCPTRH 0x02414 /* FC USer Desc. PTR High */
#define IXGBE_TSOFF 0x04A98 /* Tx FC SOF */
#define IXGBE_REOFF 0x05158 /* Rx FC EOF */
#define IXGBE_RSOFF 0x051F8 /* Rx FC SOF */
+/* FCoE Direct Filter Context */
+#define IXGBE_FCDFC(_i, _j) (0x28000 + ((_i) * 0x4) + ((_j) * 0x10))
+#define IXGBE_FCDFCD(_i) (0x30000 + ((_i) * 0x4))
/* FCoE Filter Context Registers */
#define IXGBE_FCFLT 0x05108 /* FC FLT Context */
#define IXGBE_FCFLTRW 0x05110 /* FC Filter RW Control */
#define IXGBE_FCRECTL_ENA 0x1 /* FCoE Redir Table Enable */
#define IXGBE_FCRETA_SIZE 8 /* Max entries in FCRETA */
#define IXGBE_FCRETA_ENTRY_MASK 0x0000007f /* 7 bits for the queue index */
+#define IXGBE_FCRETA_SIZE_X550 32 /* Max entries in FCRETA */
+/* Higher 7 bits for the queue index */
+#define IXGBE_FCRETA_ENTRY_HIGH_MASK 0x007F0000
+#define IXGBE_FCRETA_ENTRY_HIGH_SHIFT 16
/* Stats registers */
#define IXGBE_CRCERRS 0x04000
s32 (*init_thermal_sensor_thresh)(struct ixgbe_hw *hw);
void (*disable_rx)(struct ixgbe_hw *hw);
void (*enable_rx)(struct ixgbe_hw *hw);
+ void (*set_source_address_pruning)(struct ixgbe_hw *, bool,
+ unsigned int);
void (*set_ethertype_anti_spoofing)(struct ixgbe_hw *, bool, int);
/* DMA Coalescing */
IXGBE_WRITE_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg), pfvfspoof);
}
+/** ixgbe_set_source_address_pruning_X550 - Enable/Disbale src address pruning
+ * @hw: pointer to hardware structure
+ * @enable: enable or disable source address pruning
+ * @pool: Rx pool to set source address pruning for
+ **/
+static void ixgbe_set_source_address_pruning_X550(struct ixgbe_hw *hw,
+ bool enable,
+ unsigned int pool)
+{
+ u64 pfflp;
+
+ /* max rx pool is 63 */
+ if (pool > 63)
+ return;
+
+ pfflp = (u64)IXGBE_READ_REG(hw, IXGBE_PFFLPL);
+ pfflp |= (u64)IXGBE_READ_REG(hw, IXGBE_PFFLPH) << 32;
+
+ if (enable)
+ pfflp |= (1ULL << pool);
+ else
+ pfflp &= ~(1ULL << pool);
+
+ IXGBE_WRITE_REG(hw, IXGBE_PFFLPL, (u32)pfflp);
+ IXGBE_WRITE_REG(hw, IXGBE_PFFLPH, (u32)(pfflp >> 32));
+}
+
#define X550_COMMON_MAC \
.init_hw = &ixgbe_init_hw_generic, \
.start_hw = &ixgbe_start_hw_X540, \
.init_uta_tables = &ixgbe_init_uta_tables_generic, \
.set_mac_anti_spoofing = &ixgbe_set_mac_anti_spoofing, \
.set_vlan_anti_spoofing = &ixgbe_set_vlan_anti_spoofing, \
+ .set_source_address_pruning = \
+ &ixgbe_set_source_address_pruning_X550, \
.set_ethertype_anti_spoofing = \
&ixgbe_set_ethertype_anti_spoofing_X550, \
.acquire_swfw_sync = &ixgbe_acquire_swfw_sync_X540, \
#define MVNETA_TXQ_CMD 0x2448
#define MVNETA_TXQ_DISABLE_SHIFT 8
#define MVNETA_TXQ_ENABLE_MASK 0x000000ff
+#define MVNETA_GMAC_CLOCK_DIVIDER 0x24f4
+#define MVNETA_GMAC_1MS_CLOCK_ENABLE BIT(31)
#define MVNETA_ACC_MODE 0x2500
#define MVNETA_CPU_MAP(cpu) (0x2540 + ((cpu) << 2))
#define MVNETA_CPU_RXQ_ACCESS_ALL_MASK 0x000000ff
#define MVNETA_TX_INTR_MASK_ALL (0xff << 0)
#define MVNETA_RX_INTR_MASK(nr_rxqs) (((1 << nr_rxqs) - 1) << 8)
#define MVNETA_RX_INTR_MASK_ALL (0xff << 8)
+#define MVNETA_MISCINTR_INTR_MASK BIT(31)
#define MVNETA_INTR_OLD_CAUSE 0x25a8
#define MVNETA_INTR_OLD_MASK 0x25ac
#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc
#define MVNETA_GMAC0_PORT_ENABLE BIT(0)
#define MVNETA_GMAC_CTRL_2 0x2c08
+#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0)
#define MVNETA_GMAC2_PCS_ENABLE BIT(3)
#define MVNETA_GMAC2_PORT_RGMII BIT(4)
#define MVNETA_GMAC2_PORT_RESET BIT(6)
#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c
#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0)
#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1)
+#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2)
#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5)
#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6)
#define MVNETA_GMAC_AN_SPEED_EN BIT(7)
+#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11)
#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12)
#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13)
#define MVNETA_MIB_COUNTERS_BASE 0x3080
unsigned int link;
unsigned int duplex;
unsigned int speed;
+ int use_inband_status:1;
};
/* The mvneta_tx_desc and mvneta_rx_desc structures describe the
val &= ~MVNETA_PHY_POLLING_ENABLE;
mvreg_write(pp, MVNETA_UNIT_CONTROL, val);
+ if (pp->use_inband_status) {
+ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
+ MVNETA_GMAC_FORCE_LINK_DOWN |
+ MVNETA_GMAC_AN_FLOW_CTRL_EN);
+ val |= MVNETA_GMAC_INBAND_AN_ENABLE |
+ MVNETA_GMAC_AN_SPEED_EN |
+ MVNETA_GMAC_AN_DUPLEX_EN;
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+ val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
+ }
+
mvneta_set_ucast_table(pp, -1);
mvneta_set_special_mcast_table(pp, -1);
mvneta_set_other_mcast_table(pp, -1);
return IRQ_HANDLED;
}
+static int mvneta_fixed_link_update(struct mvneta_port *pp,
+ struct phy_device *phy)
+{
+ struct fixed_phy_status status;
+ struct fixed_phy_status changed = {};
+ u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
+
+ status.link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
+ if (gmac_stat & MVNETA_GMAC_SPEED_1000)
+ status.speed = SPEED_1000;
+ else if (gmac_stat & MVNETA_GMAC_SPEED_100)
+ status.speed = SPEED_100;
+ else
+ status.speed = SPEED_10;
+ status.duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
+ changed.link = 1;
+ changed.speed = 1;
+ changed.duplex = 1;
+ fixed_phy_update_state(phy, &status, &changed);
+ return 0;
+}
+
/* NAPI handler
* Bits 0 - 7 of the causeRxTx register indicate that are transmitted
* packets on the corresponding TXQ (Bit 0 is for TX queue 1).
}
/* Read cause register */
- cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE) &
- (MVNETA_RX_INTR_MASK(rxq_number) | MVNETA_TX_INTR_MASK(txq_number));
+ cause_rx_tx = mvreg_read(pp, MVNETA_INTR_NEW_CAUSE);
+ if (cause_rx_tx & MVNETA_MISCINTR_INTR_MASK) {
+ u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE);
+
+ mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
+ if (pp->use_inband_status && (cause_misc &
+ (MVNETA_CAUSE_PHY_STATUS_CHANGE |
+ MVNETA_CAUSE_LINK_CHANGE |
+ MVNETA_CAUSE_PSC_SYNC_CHANGE))) {
+ mvneta_fixed_link_update(pp, pp->phy_dev);
+ }
+ }
/* Release Tx descriptors */
if (cause_rx_tx & MVNETA_TX_INTR_MASK_ALL) {
napi_complete(napi);
local_irq_save(flags);
mvreg_write(pp, MVNETA_INTR_NEW_MASK,
- MVNETA_RX_INTR_MASK(rxq_number) | MVNETA_TX_INTR_MASK(txq_number));
+ MVNETA_RX_INTR_MASK(rxq_number) |
+ MVNETA_TX_INTR_MASK(txq_number) |
+ MVNETA_MISCINTR_INTR_MASK);
local_irq_restore(flags);
}
/* Unmask interrupts */
mvreg_write(pp, MVNETA_INTR_NEW_MASK,
- MVNETA_RX_INTR_MASK(rxq_number) | MVNETA_TX_INTR_MASK(txq_number));
+ MVNETA_RX_INTR_MASK(rxq_number) |
+ MVNETA_TX_INTR_MASK(txq_number) |
+ MVNETA_MISCINTR_INTR_MASK);
+ mvreg_write(pp, MVNETA_INTR_MISC_MASK,
+ MVNETA_CAUSE_PHY_STATUS_CHANGE |
+ MVNETA_CAUSE_LINK_CHANGE |
+ MVNETA_CAUSE_PSC_SYNC_CHANGE);
phy_start(pp->phy_dev);
netif_tx_start_all_queues(pp->dev);
val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
MVNETA_GMAC_CONFIG_GMII_SPEED |
- MVNETA_GMAC_CONFIG_FULL_DUPLEX |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_DUPLEX_EN);
+ MVNETA_GMAC_CONFIG_FULL_DUPLEX);
if (phydev->duplex)
val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
if (status_change) {
if (phydev->link) {
- u32 val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val |= (MVNETA_GMAC_FORCE_LINK_PASS |
- MVNETA_GMAC_FORCE_LINK_DOWN);
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ if (!pp->use_inband_status) {
+ u32 val = mvreg_read(pp,
+ MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
+ val |= MVNETA_GMAC_FORCE_LINK_PASS;
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
+ val);
+ }
mvneta_port_up(pp);
} else {
+ if (!pp->use_inband_status) {
+ u32 val = mvreg_read(pp,
+ MVNETA_GMAC_AUTONEG_CONFIG);
+ val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
+ val |= MVNETA_GMAC_FORCE_LINK_DOWN;
+ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
+ val);
+ }
mvneta_port_down(pp);
}
phy_print_status(phydev);
static int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct mvneta_port *pp = netdev_priv(dev);
- int ret;
if (!pp->phy_dev)
return -ENOTSUPP;
- ret = phy_mii_ioctl(pp->phy_dev, ifr, cmd);
- if (!ret)
- mvneta_adjust_link(dev);
-
- return ret;
+ return phy_mii_ioctl(pp->phy_dev, ifr, cmd);
}
/* Ethtool methods */
return -EINVAL;
}
+ if (pp->use_inband_status)
+ ctrl |= MVNETA_GMAC2_INBAND_AN_ENABLE;
+
/* Cancel Port Reset */
ctrl &= ~MVNETA_GMAC2_PORT_RESET;
mvreg_write(pp, MVNETA_GMAC_CTRL_2, ctrl);
char hw_mac_addr[ETH_ALEN];
const char *mac_from;
int phy_mode;
+ int fixed_phy = 0;
int err;
/* Our multiqueue support is not complete, so for now, only
dev_err(&pdev->dev, "cannot register fixed PHY\n");
goto err_free_irq;
}
+ fixed_phy = 1;
/* In the case of a fixed PHY, the DT node associated
* to the PHY is the Ethernet MAC DT node.
pp = netdev_priv(dev);
pp->phy_node = phy_node;
pp->phy_interface = phy_mode;
+ pp->use_inband_status = (phy_mode == PHY_INTERFACE_MODE_SGMII) &&
+ fixed_phy;
pp->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pp->clk)) {
platform_set_drvdata(pdev, pp->dev);
+ if (pp->use_inband_status) {
+ struct phy_device *phy = of_phy_find_device(dn);
+
+ mvneta_fixed_link_update(pp, phy);
+ }
+
return 0;
err_free_stats:
obj-$(CONFIG_MLX4_CORE) += mlx4_core.o
-mlx4_core-y := alloc.o catas.o cmd.o cq.o eq.o fw.o icm.o intf.o main.o mcg.o \
- mr.o pd.o port.o profile.o qp.o reset.o sense.o srq.o resource_tracker.o
+mlx4_core-y := alloc.o catas.o cmd.o cq.o eq.o fw.o fw_qos.o icm.o intf.o \
+ main.o mcg.o mr.o pd.o port.o profile.o qp.o reset.o sense.o \
+ srq.o resource_tracker.o
obj-$(CONFIG_MLX4_EN) += mlx4_en.o
#include "mlx4.h"
#include "fw.h"
+#include "fw_qos.h"
#define CMD_POLL_TOKEN 0xffff
#define INBOX_MASK 0xffffffffffffff00ULL
* on the host, we deprecate the error message for this
* specific command/input_mod/opcode_mod/fw-status to be debug.
*/
- if (op == MLX4_CMD_SET_PORT && in_modifier == 1 &&
- op_modifier == 0 && context->fw_status == CMD_STAT_BAD_SIZE)
+ if (op == MLX4_CMD_SET_PORT &&
+ (in_modifier == 1 || in_modifier == 2) &&
+ op_modifier == MLX4_SET_PORT_IB_OPCODE &&
+ context->fw_status == CMD_STAT_BAD_SIZE)
mlx4_dbg(dev, "command 0x%x failed: fw status = 0x%x\n",
op, context->fw_status);
else
.verify = NULL,
.wrapper = mlx4_CMD_EPERM_wrapper,
},
+ {
+ .opcode = MLX4_CMD_ALLOCATE_VPP,
+ .has_inbox = false,
+ .has_outbox = true,
+ .out_is_imm = false,
+ .encode_slave_id = false,
+ .verify = NULL,
+ .wrapper = mlx4_CMD_EPERM_wrapper,
+ },
+ {
+ .opcode = MLX4_CMD_SET_VPORT_QOS,
+ .has_inbox = false,
+ .has_outbox = true,
+ .out_is_imm = false,
+ .encode_slave_id = false,
+ .verify = NULL,
+ .wrapper = mlx4_CMD_EPERM_wrapper,
+ },
{
.opcode = MLX4_CMD_CONF_SPECIAL_QP,
.has_inbox = false,
if (vp_oper->state.default_vlan == vp_admin->default_vlan &&
vp_oper->state.default_qos == vp_admin->default_qos &&
- vp_oper->state.link_state == vp_admin->link_state)
+ vp_oper->state.link_state == vp_admin->link_state &&
+ vp_oper->state.qos_vport == vp_admin->qos_vport)
return 0;
if (!(priv->mfunc.master.slave_state[slave].active &&
vp_oper->state.default_vlan = vp_admin->default_vlan;
vp_oper->state.default_qos = vp_admin->default_qos;
vp_oper->state.link_state = vp_admin->link_state;
+ vp_oper->state.qos_vport = vp_admin->qos_vport;
if (vp_admin->link_state == IFLA_VF_LINK_STATE_DISABLE)
work->flags |= MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE;
work->port = port;
work->slave = slave;
work->qos = vp_oper->state.default_qos;
+ work->qos_vport = vp_oper->state.qos_vport;
work->vlan_id = vp_oper->state.default_vlan;
work->vlan_ix = vp_oper->vlan_idx;
work->priv = priv;
return 0;
}
+static void mlx4_set_default_port_qos(struct mlx4_dev *dev, int port)
+{
+ struct mlx4_qos_manager *port_qos_ctl;
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ port_qos_ctl = &priv->mfunc.master.qos_ctl[port];
+ bitmap_zero(port_qos_ctl->priority_bm, MLX4_NUM_UP);
+
+ /* Enable only default prio at PF init routine */
+ set_bit(MLX4_DEFAULT_QOS_PRIO, port_qos_ctl->priority_bm);
+}
+
+static void mlx4_allocate_port_vpps(struct mlx4_dev *dev, int port)
+{
+ int i;
+ int err;
+ int num_vfs;
+ u16 availible_vpp;
+ u8 vpp_param[MLX4_NUM_UP];
+ struct mlx4_qos_manager *port_qos;
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param);
+ if (err) {
+ mlx4_info(dev, "Failed query availible VPPs\n");
+ return;
+ }
+
+ port_qos = &priv->mfunc.master.qos_ctl[port];
+ num_vfs = (availible_vpp /
+ bitmap_weight(port_qos->priority_bm, MLX4_NUM_UP));
+
+ for (i = 0; i < MLX4_NUM_UP; i++) {
+ if (test_bit(i, port_qos->priority_bm))
+ vpp_param[i] = num_vfs;
+ }
+
+ err = mlx4_ALLOCATE_VPP_set(dev, port, vpp_param);
+ if (err) {
+ mlx4_info(dev, "Failed allocating VPPs\n");
+ return;
+ }
+
+ /* Query actual allocated VPP, just to make sure */
+ err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param);
+ if (err) {
+ mlx4_info(dev, "Failed query availible VPPs\n");
+ return;
+ }
+
+ port_qos->num_of_qos_vfs = num_vfs;
+ mlx4_dbg(dev, "Port %d Availible VPPs %d\n", port, availible_vpp);
+
+ for (i = 0; i < MLX4_NUM_UP; i++)
+ mlx4_dbg(dev, "Port %d UP %d Allocated %d VPPs\n", port, i,
+ vpp_param[i]);
+}
static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave)
{
goto reset_slave;
slave_state[slave].vhcr_dma = ((u64) param) << 48;
priv->mfunc.master.slave_state[slave].cookie = 0;
- mutex_init(&priv->mfunc.master.gen_eqe_mutex[slave]);
break;
case MLX4_COMM_CMD_VHCR1:
if (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR0)
}
if (mlx4_is_master(dev)) {
+ struct mlx4_vf_oper_state *vf_oper;
+ struct mlx4_vf_admin_state *vf_admin;
+
priv->mfunc.master.slave_state =
kzalloc(dev->num_slaves *
sizeof(struct mlx4_slave_state), GFP_KERNEL);
goto err_comm_oper;
for (i = 0; i < dev->num_slaves; ++i) {
+ vf_admin = &priv->mfunc.master.vf_admin[i];
+ vf_oper = &priv->mfunc.master.vf_oper[i];
s_state = &priv->mfunc.master.slave_state[i];
s_state->last_cmd = MLX4_COMM_CMD_RESET;
+ mutex_init(&priv->mfunc.master.gen_eqe_mutex[i]);
for (j = 0; j < MLX4_EVENT_TYPES_NUM; ++j)
s_state->event_eq[j].eqn = -1;
__raw_writel((__force u32) 0,
&priv->mfunc.comm[i].slave_read);
mmiowb();
for (port = 1; port <= MLX4_MAX_PORTS; port++) {
+ struct mlx4_vport_state *admin_vport;
+ struct mlx4_vport_state *oper_vport;
+
s_state->vlan_filter[port] =
kzalloc(sizeof(struct mlx4_vlan_fltr),
GFP_KERNEL);
kfree(s_state->vlan_filter[port]);
goto err_slaves;
}
+
+ admin_vport = &vf_admin->vport[port];
+ oper_vport = &vf_oper->vport[port].state;
INIT_LIST_HEAD(&s_state->mcast_filters[port]);
- priv->mfunc.master.vf_admin[i].vport[port].default_vlan = MLX4_VGT;
- priv->mfunc.master.vf_oper[i].vport[port].state.default_vlan = MLX4_VGT;
- priv->mfunc.master.vf_oper[i].vport[port].vlan_idx = NO_INDX;
- priv->mfunc.master.vf_oper[i].vport[port].mac_idx = NO_INDX;
+ admin_vport->default_vlan = MLX4_VGT;
+ oper_vport->default_vlan = MLX4_VGT;
+ admin_vport->qos_vport =
+ MLX4_VPP_DEFAULT_VPORT;
+ oper_vport->qos_vport = MLX4_VPP_DEFAULT_VPORT;
+ vf_oper->vport[port].vlan_idx = NO_INDX;
+ vf_oper->vport[port].mac_idx = NO_INDX;
}
spin_lock_init(&s_state->lock);
}
+ if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP) {
+ for (port = 1; port <= dev->caps.num_ports; port++) {
+ if (mlx4_is_eth(dev, port)) {
+ mlx4_set_default_port_qos(dev, port);
+ mlx4_allocate_port_vpps(dev, port);
+ }
+ }
+ }
+
memset(&priv->mfunc.master.cmd_eqe, 0, dev->caps.eqe_size);
priv->mfunc.master.cmd_eqe.type = MLX4_EVENT_TYPE_CMD;
INIT_WORK(&priv->mfunc.master.comm_work,
return port;
}
+static int mlx4_set_vport_qos(struct mlx4_priv *priv, int slave, int port,
+ int max_tx_rate)
+{
+ int i;
+ int err;
+ struct mlx4_qos_manager *port_qos;
+ struct mlx4_dev *dev = &priv->dev;
+ struct mlx4_vport_qos_param vpp_qos[MLX4_NUM_UP];
+
+ port_qos = &priv->mfunc.master.qos_ctl[port];
+ memset(vpp_qos, 0, sizeof(struct mlx4_vport_qos_param) * MLX4_NUM_UP);
+
+ if (slave > port_qos->num_of_qos_vfs) {
+ mlx4_info(dev, "No availible VPP resources for this VF\n");
+ return -EINVAL;
+ }
+
+ /* Query for default QoS values from Vport 0 is needed */
+ err = mlx4_SET_VPORT_QOS_get(dev, port, 0, vpp_qos);
+ if (err) {
+ mlx4_info(dev, "Failed to query Vport 0 QoS values\n");
+ return err;
+ }
+
+ for (i = 0; i < MLX4_NUM_UP; i++) {
+ if (test_bit(i, port_qos->priority_bm) && max_tx_rate) {
+ vpp_qos[i].max_avg_bw = max_tx_rate;
+ vpp_qos[i].enable = 1;
+ } else {
+ /* if user supplied tx_rate == 0, meaning no rate limit
+ * configuration is required. so we are leaving the
+ * value of max_avg_bw as queried from Vport 0.
+ */
+ vpp_qos[i].enable = 0;
+ }
+ }
+
+ err = mlx4_SET_VPORT_QOS_set(dev, port, slave, vpp_qos);
+ if (err) {
+ mlx4_info(dev, "Failed to set Vport %d QoS values\n", slave);
+ return err;
+ }
+
+ return 0;
+}
+
+static bool mlx4_is_vf_vst_and_prio_qos(struct mlx4_dev *dev, int port,
+ struct mlx4_vport_state *vf_admin)
+{
+ struct mlx4_qos_manager *info;
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ if (!mlx4_is_master(dev) ||
+ !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP))
+ return false;
+
+ info = &priv->mfunc.master.qos_ctl[port];
+
+ if (vf_admin->default_vlan != MLX4_VGT &&
+ test_bit(vf_admin->default_qos, info->priority_bm))
+ return true;
+
+ return false;
+}
+
+static bool mlx4_valid_vf_state_change(struct mlx4_dev *dev, int port,
+ struct mlx4_vport_state *vf_admin,
+ int vlan, int qos)
+{
+ struct mlx4_vport_state dummy_admin = {0};
+
+ if (!mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin) ||
+ !vf_admin->tx_rate)
+ return true;
+
+ dummy_admin.default_qos = qos;
+ dummy_admin.default_vlan = vlan;
+
+ /* VF wants to move to other VST state which is valid with current
+ * rate limit. Either differnt default vlan in VST or other
+ * supported QoS priority. Otherwise we don't allow this change when
+ * the TX rate is still configured.
+ */
+ if (mlx4_is_vf_vst_and_prio_qos(dev, port, &dummy_admin))
+ return true;
+
+ mlx4_info(dev, "Cannot change VF state to %s while rate is set\n",
+ (vlan == MLX4_VGT) ? "VGT" : "VST");
+
+ if (vlan != MLX4_VGT)
+ mlx4_info(dev, "VST priority %d not supported for QoS\n", qos);
+
+ mlx4_info(dev, "Please set rate to 0 prior to this VF state change\n");
+
+ return false;
+}
+
int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac)
{
struct mlx4_priv *priv = mlx4_priv(dev);
port = mlx4_slaves_closest_port(dev, slave, port);
vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port];
+ if (!mlx4_valid_vf_state_change(dev, port, vf_admin, vlan, qos))
+ return -EPERM;
+
if ((0 == vlan) && (0 == qos))
vf_admin->default_vlan = MLX4_VGT;
else
vf_admin->default_vlan = vlan;
vf_admin->default_qos = qos;
+ /* If rate was configured prior to VST, we saved the configured rate
+ * in vf_admin->rate and now, if priority supported we enforce the QoS
+ */
+ if (mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin) &&
+ vf_admin->tx_rate)
+ vf_admin->qos_vport = slave;
+
if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port))
mlx4_info(dev,
"updating vf %d port %d config will take effect on next VF restart\n",
}
EXPORT_SYMBOL_GPL(mlx4_set_vf_vlan);
+int mlx4_set_vf_rate(struct mlx4_dev *dev, int port, int vf, int min_tx_rate,
+ int max_tx_rate)
+{
+ int err;
+ int slave;
+ struct mlx4_vport_state *vf_admin;
+ struct mlx4_priv *priv = mlx4_priv(dev);
+
+ if (!mlx4_is_master(dev) ||
+ !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_QOS_VPP))
+ return -EPROTONOSUPPORT;
+
+ if (min_tx_rate) {
+ mlx4_info(dev, "Minimum BW share not supported\n");
+ return -EPROTONOSUPPORT;
+ }
+
+ slave = mlx4_get_slave_indx(dev, vf);
+ if (slave < 0)
+ return -EINVAL;
+
+ port = mlx4_slaves_closest_port(dev, slave, port);
+ vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port];
+
+ err = mlx4_set_vport_qos(priv, slave, port, max_tx_rate);
+ if (err) {
+ mlx4_info(dev, "vf %d failed to set rate %d\n", vf,
+ max_tx_rate);
+ return err;
+ }
+
+ vf_admin->tx_rate = max_tx_rate;
+ /* if VF is not in supported mode (VST with supported prio),
+ * we do not change vport configuration for its QPs, but save
+ * the rate, so it will be enforced when it moves to supported
+ * mode next time.
+ */
+ if (!mlx4_is_vf_vst_and_prio_qos(dev, port, vf_admin)) {
+ mlx4_info(dev,
+ "rate set for VF %d when not in valid state\n", vf);
+
+ if (vf_admin->default_vlan != MLX4_VGT)
+ mlx4_info(dev, "VST priority not supported by QoS\n");
+ else
+ mlx4_info(dev, "VF in VGT mode (needed VST)\n");
+
+ mlx4_info(dev,
+ "rate %d take affect when VF moves to valid state\n",
+ max_tx_rate);
+ return 0;
+ }
+
+ /* If user sets rate 0 assigning default vport for its QPs */
+ vf_admin->qos_vport = max_tx_rate ? slave : MLX4_VPP_DEFAULT_VPORT;
+
+ if (priv->mfunc.master.slave_state[slave].active &&
+ dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP)
+ mlx4_master_immediate_activate_vlan_qos(priv, slave, port);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mlx4_set_vf_rate);
+
/* mlx4_get_slave_default_vlan -
* return true if VST ( default vlan)
* if VST, will return vlan & qos (if not NULL)
ivf->vlan = s_info->default_vlan;
ivf->qos = s_info->default_qos;
- ivf->max_tx_rate = s_info->tx_rate;
+
+ if (mlx4_is_vf_vst_and_prio_qos(dev, port, s_info))
+ ivf->max_tx_rate = s_info->tx_rate;
+ else
+ ivf->max_tx_rate = 0;
+
ivf->min_tx_rate = 0;
ivf->spoofchk = s_info->spoofchk;
ivf->linkstate = s_info->link_state;
#include <linux/math64.h>
#include "mlx4_en.h"
+#include "fw_qos.h"
/* Definitions for QCN
*/
return 0;
}
+static int mlx4_en_set_phys_id(struct net_device *dev,
+ enum ethtool_phys_id_state state)
+{
+ int err;
+ u16 beacon_duration;
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+ struct mlx4_en_dev *mdev = priv->mdev;
+
+ if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_BEACON))
+ return -EOPNOTSUPP;
+
+ switch (state) {
+ case ETHTOOL_ID_ACTIVE:
+ beacon_duration = PORT_BEACON_MAX_LIMIT;
+ break;
+ case ETHTOOL_ID_INACTIVE:
+ beacon_duration = 0;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ err = mlx4_SET_PORT_BEACON(mdev->dev, priv->port, beacon_duration);
+ return err;
+}
+
const struct ethtool_ops mlx4_en_ethtool_ops = {
.get_drvinfo = mlx4_en_get_drvinfo,
.get_settings = mlx4_en_get_settings,
.get_sset_count = mlx4_en_get_sset_count,
.get_ethtool_stats = mlx4_en_get_ethtool_stats,
.self_test = mlx4_en_self_test,
+ .set_phys_id = mlx4_en_set_phys_id,
.get_wol = mlx4_en_get_wol,
.set_wol = mlx4_en_set_wol,
.get_msglevel = mlx4_en_get_msglevel,
{
struct mlx4_en_priv *priv = netdev_priv(dev);
+ if (features & NETIF_F_LOOPBACK)
+ priv->ctrl_flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK);
+ else
+ priv->ctrl_flags &= cpu_to_be32(~MLX4_WQE_CTRL_FORCE_LOOPBACK);
+
priv->flags &= ~(MLX4_EN_FLAG_RX_FILTER_NEEDED|
MLX4_EN_FLAG_ENABLE_HW_LOOPBACK);
netdev_features_t features)
{
struct mlx4_en_priv *priv = netdev_priv(netdev);
+ bool reset = false;
int ret = 0;
+ if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_RXFCS)) {
+ en_info(priv, "Turn %s RX-FCS\n",
+ (features & NETIF_F_RXFCS) ? "ON" : "OFF");
+ reset = true;
+ }
+
+ if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_RXALL)) {
+ u8 ignore_fcs_value = (features & NETIF_F_RXALL) ? 1 : 0;
+
+ en_info(priv, "Turn %s RX-ALL\n",
+ ignore_fcs_value ? "ON" : "OFF");
+ ret = mlx4_SET_PORT_fcs_check(priv->mdev->dev,
+ priv->port, ignore_fcs_value);
+ if (ret)
+ return ret;
+ }
+
if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_HW_VLAN_CTAG_RX)) {
en_info(priv, "Turn %s RX vlan strip offload\n",
(features & NETIF_F_HW_VLAN_CTAG_RX) ? "ON" : "OFF");
- ret = mlx4_en_reset_config(netdev, priv->hwtstamp_config,
- features);
- if (ret)
- return ret;
+ reset = true;
}
if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_HW_VLAN_CTAG_TX))
en_info(priv, "Turn %s TX vlan strip offload\n",
(features & NETIF_F_HW_VLAN_CTAG_TX) ? "ON" : "OFF");
- if (features & NETIF_F_LOOPBACK)
- priv->ctrl_flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK);
- else
- priv->ctrl_flags &=
- cpu_to_be32(~MLX4_WQE_CTRL_FORCE_LOOPBACK);
+ if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_LOOPBACK)) {
+ en_info(priv, "Turn %s loopback\n",
+ (features & NETIF_F_LOOPBACK) ? "ON" : "OFF");
+ mlx4_en_update_loopback_state(netdev, features);
+ }
- mlx4_en_update_loopback_state(netdev, features);
+ if (reset) {
+ ret = mlx4_en_reset_config(netdev, priv->hwtstamp_config,
+ features);
+ if (ret)
+ return ret;
+ }
return 0;
-
}
static int mlx4_en_set_vf_mac(struct net_device *dev, int queue, u8 *mac)
return mlx4_set_vf_vlan(mdev->dev, en_priv->port, vf, vlan, qos);
}
+static int mlx4_en_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate,
+ int max_tx_rate)
+{
+ struct mlx4_en_priv *en_priv = netdev_priv(dev);
+ struct mlx4_en_dev *mdev = en_priv->mdev;
+
+ return mlx4_set_vf_rate(mdev->dev, en_priv->port, vf, min_tx_rate,
+ max_tx_rate);
+}
+
static int mlx4_en_set_vf_spoofchk(struct net_device *dev, int vf, bool setting)
{
struct mlx4_en_priv *en_priv = netdev_priv(dev);
.ndo_vlan_rx_kill_vid = mlx4_en_vlan_rx_kill_vid,
.ndo_set_vf_mac = mlx4_en_set_vf_mac,
.ndo_set_vf_vlan = mlx4_en_set_vf_vlan,
+ .ndo_set_vf_rate = mlx4_en_set_vf_rate,
.ndo_set_vf_spoofchk = mlx4_en_set_vf_spoofchk,
.ndo_set_vf_link_state = mlx4_en_set_vf_link_state,
.ndo_get_vf_config = mlx4_en_get_vf_config,
priv->msg_enable = MLX4_EN_MSG_LEVEL;
#ifdef CONFIG_MLX4_EN_DCB
if (!mlx4_is_slave(priv->mdev->dev)) {
- if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_SET_ETH_SCHED) {
+ if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG) {
dev->dcbnl_ops = &mlx4_en_dcbnl_ops;
} else {
en_info(priv, "enabling only PFC DCB ops\n");
dev->hw_features |= NETIF_F_LOOPBACK |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+ if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP)
+ dev->hw_features |= NETIF_F_RXFCS;
+
+ if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_IGNORE_FCS)
+ dev->hw_features |= NETIF_F_RXALL;
+
if (mdev->dev->caps.steering_mode ==
MLX4_STEERING_MODE_DEVICE_MANAGED &&
mdev->dev->caps.dmfs_high_steer_mode != MLX4_STEERING_DMFS_A0_STATIC)
netif_carrier_off(dev);
mlx4_en_set_default_moderation(priv);
- err = register_netdev(dev);
- if (err) {
- en_err(priv, "Netdev registration failed for port %d\n", port);
- goto out;
- }
- priv->registered = 1;
-
en_warn(priv, "Using %d TX rings\n", prof->tx_ring_num);
en_warn(priv, "Using %d RX rings\n", prof->rx_ring_num);
mdev->profile.prof[priv->port].tx_ppp,
mdev->profile.prof[priv->port].tx_pause);
+ err = register_netdev(dev);
+ if (err) {
+ en_err(priv, "Netdev registration failed for port %d\n", port);
+ goto out;
+ }
+
+ priv->registered = 1;
+
return 0;
out:
if (priv->hwtstamp_config.tx_type == ts_config.tx_type &&
priv->hwtstamp_config.rx_filter == ts_config.rx_filter &&
- !DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX))
+ !DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX) &&
+ !DEV_FEATURE_CHANGED(dev, features, NETIF_F_RXFCS))
return 0; /* Nothing to change */
if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX) &&
dev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
}
+ if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_RXFCS)) {
+ if (features & NETIF_F_RXFCS)
+ dev->features |= NETIF_F_RXFCS;
+ else
+ dev->features &= ~NETIF_F_RXFCS;
+ }
+
/* RX vlan offload and RX time-stamping can't co-exist !
* Regardless of the caller's choice,
* Turn Off RX vlan offload in case of time-stamping is ON
/*
* make sure we read the CQE after we read the ownership bit
*/
- rmb();
+ dma_rmb();
/* Drop packet on bad receive or bad checksum */
if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) ==
/* Cancel FCS removal if FW allows */
if (mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP) {
context->param3 |= cpu_to_be32(1 << 29);
- ring->fcs_del = ETH_FCS_LEN;
+ if (priv->dev->features & NETIF_F_RXFCS)
+ ring->fcs_del = 0;
+ else
+ ring->fcs_del = ETH_FCS_LEN;
} else
ring->fcs_del = 0;
* make sure we read the CQE after we read the
* ownership bit
*/
- rmb();
+ dma_rmb();
if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) ==
MLX4_CQE_OPCODE_ERROR)) {
skb_frag_size(&shinfo->frags[0]));
}
- wmb();
+ dma_wmb();
inl->byte_count = cpu_to_be32(1 << 31 | (skb->len - spc));
}
}
data->addr = cpu_to_be64(dma);
data->lkey = ring->mr_key;
- wmb();
+ dma_wmb();
data->byte_count = cpu_to_be32(byte_count);
--data;
}
data->addr = cpu_to_be64(dma);
data->lkey = ring->mr_key;
- wmb();
+ dma_wmb();
data->byte_count = cpu_to_be32(byte_count);
}
/* tx completion can avoid cache line miss for common cases */
/* Ensure new descriptor hits memory
* before setting ownership of this descriptor to HW
*/
- wmb();
+ dma_wmb();
tx_desc->ctrl.owner_opcode = op_own;
wmb();
/* Ensure new descriptor hits memory
* before setting ownership of this descriptor to HW
*/
- wmb();
+ dma_wmb();
tx_desc->ctrl.owner_opcode = op_own;
if (send_doorbell) {
wmb();
/* All active slaves need to receive the event */
if (slave == ALL_SLAVES) {
- for (i = 0; i < dev->num_slaves; i++) {
- if (i != dev->caps.function &&
- master->slave_state[i].active)
- if (mlx4_GEN_EQE(dev, i, eqe))
- mlx4_warn(dev, "Failed to generate event for slave %d\n",
- i);
+ for (i = 0; i <= dev->persist->num_vfs; i++) {
+ if (mlx4_GEN_EQE(dev, i, eqe))
+ mlx4_warn(dev, "Failed to generate event for slave %d\n",
+ i);
}
} else {
if (mlx4_GEN_EQE(dev, slave, eqe))
memcpy(s_eqe, eqe, dev->caps.eqe_size - 1);
s_eqe->slave_id = slave;
/* ensure all information is written before setting the ownersip bit */
- wmb();
+ dma_wmb();
s_eqe->owner = !!(slave_eq->prod & SLAVE_EVENT_EQ_SIZE) ? 0x0 : 0x80;
++slave_eq->prod;
struct mlx4_eqe *eqe)
{
struct mlx4_priv *priv = mlx4_priv(dev);
- struct mlx4_slave_state *s_slave =
- &priv->mfunc.master.slave_state[slave];
- if (!s_slave->active) {
- /*mlx4_warn(dev, "Trying to pass event to inactive slave\n");*/
+ if (slave < 0 || slave > dev->persist->num_vfs ||
+ slave == dev->caps.function ||
+ !priv->mfunc.master.slave_state[slave].active)
return;
- }
slave_event(dev, slave, eqe);
}
* Make sure we read EQ entry contents after we've
* checked the ownership bit.
*/
- rmb();
+ dma_rmb();
switch (eqe->type) {
case MLX4_EVENT_TYPE_COMP:
extern void __buggy_use_of_MLX4_GET(void);
extern void __buggy_use_of_MLX4_PUT(void);
-static bool enable_qos;
+static bool enable_qos = true;
module_param(enable_qos, bool, 0444);
-MODULE_PARM_DESC(enable_qos, "Enable Quality of Service support in the HCA (default: off)");
+MODULE_PARM_DESC(enable_qos, "Enable Enhanced QoS support (default: on)");
#define MLX4_GET(dest, source, offset) \
do { \
[41] = "Unicast VEP steering support",
[42] = "Multicast VEP steering support",
[48] = "Counters support",
+ [52] = "RSS IP fragments support",
[53] = "Port ETS Scheduler support",
[55] = "Port link type sensing support",
[59] = "Port management change event support",
[21] = "Port Remap support",
[22] = "QCN support",
[23] = "QP rate limiting support",
- [24] = "Ethernet Flow control statistics support"
+ [24] = "Ethernet Flow control statistics support",
+ [25] = "Granular QoS per VF support",
+ [26] = "Port ETS Scheduler support",
+ [27] = "Port beacon support",
+ [28] = "RX-ALL support",
};
int i;
#define QUERY_DEV_CAP_RSS_OFFSET 0x2e
#define QUERY_DEV_CAP_MAX_RDMA_OFFSET 0x2f
#define QUERY_DEV_CAP_RSZ_SRQ_OFFSET 0x33
+#define QUERY_DEV_CAP_PORT_BEACON_OFFSET 0x34
#define QUERY_DEV_CAP_ACK_DELAY_OFFSET 0x35
#define QUERY_DEV_CAP_MTU_WIDTH_OFFSET 0x36
#define QUERY_DEV_CAP_VL_PORT_OFFSET 0x37
if (field & 0x80)
dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_FS_EN;
dev_cap->fs_log_max_ucast_qp_range_size = field & 0x1f;
+ MLX4_GET(field, outbox, QUERY_DEV_CAP_PORT_BEACON_OFFSET);
+ if (field & 0x80)
+ dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_PORT_BEACON;
MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_IPOIB_OFFSET);
if (field & 0x80)
dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_DMFS_IPOIB;
MLX4_GET(size, outbox, QUERY_DEV_CAP_MAX_DESC_SZ_RQ_OFFSET);
dev_cap->max_rq_desc_sz = size;
MLX4_GET(field, outbox, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE);
+ if (field & (1 << 4))
+ dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_QOS_VPP;
if (field & (1 << 5))
dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_ETH_PROT_CTRL;
if (field & (1 << 6))
MLX4_GET(field, outbox, QUERY_DEV_CAP_CONFIG_DEV_OFFSET);
if (field & 0x20)
dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_CONFIG_DEV;
+ if (field & (1 << 2))
+ dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_IGNORE_FCS;
MLX4_GET(dev_cap->reserved_lkey, outbox,
QUERY_DEV_CAP_RSVD_LKEY_OFFSET);
MLX4_GET(field32, outbox, QUERY_DEV_CAP_ETH_BACKPL_OFFSET);
MLX4_GET(field, outbox, QUERY_DEV_CAP_VXLAN);
if (field & 1<<3)
dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS;
+ if (field & (1 << 5))
+ dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_ETS_CFG;
MLX4_GET(dev_cap->max_icm_sz, outbox,
QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET);
if (dev_cap->flags & MLX4_DEV_CAP_FLAG_COUNTERS)
}
for (; slave_port < dev->caps.num_ports; ++slave_port)
flags &= ~(MLX4_DEV_CAP_FLAG_WOL_PORT1 << slave_port);
+
+ /* Not exposing RSS IP fragments to guests */
+ flags &= ~MLX4_DEV_CAP_FLAG_RSS_IP_FRAG;
MLX4_PUT(outbox->buf, flags, QUERY_DEV_CAP_EXT_FLAGS_OFFSET);
MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_VL_PORT_OFFSET);
field &= 0x7f;
MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CQ_TS_SUPPORT_OFFSET);
- /* For guests, disable vxlan tunneling */
+ /* For guests, disable vxlan tunneling and QoS support */
MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_VXLAN);
- field &= 0xf7;
+ field &= 0xd7;
MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_VXLAN);
+ /* For guests, disable port BEACON */
+ MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_PORT_BEACON_OFFSET);
+ field &= 0x7f;
+ MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_PORT_BEACON_OFFSET);
+
/* For guests, report Blueflame disabled */
MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_BF_OFFSET);
field &= 0x7f;
field16 = 0;
MLX4_PUT(outbox->buf, field16, QUERY_DEV_CAP_QP_RATE_LIMIT_NUM_OFFSET);
+ /* turn off QoS per VF support for guests */
+ MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE);
+ field &= 0xef;
+ MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CQ_EQ_CACHE_LINE_STRIDE);
+
+ /* turn off ignore FCS feature for guests */
+ MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_CONFIG_DEV_OFFSET);
+ field &= 0xfb;
+ MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CONFIG_DEV_OFFSET);
+
return 0;
}
*(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 3);
/* Enable QoS support if module parameter set */
- if (enable_qos)
+ if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG && enable_qos)
*(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 2);
/* enable counters */
if (dev->caps.flags & MLX4_DEV_CAP_FLAG_COUNTERS)
*(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 4);
+ /* Enable RSS spread to fragmented IP packets when supported */
+ if (dev->caps.flags & MLX4_DEV_CAP_FLAG_RSS_IP_FRAG)
+ *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 13);
+
/* CX3 is capable of extending CQEs/EQEs from 32 to 64 bytes */
if (dev->caps.flags & MLX4_DEV_CAP_FLAG_64B_EQE) {
*(inbox + INIT_HCA_EQE_CQE_OFFSETS / 4) |= cpu_to_be32(1 << 29);
else
param->steering_mode = MLX4_STEERING_MODE_A0;
}
+
+ if (dword_field & (1 << 13))
+ param->rss_ip_frags = 1;
+
/* steering attributes */
if (param->steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) {
MLX4_GET(param->mc_base, outbox, INIT_HCA_FS_BASE_OFFSET);
u64 dev_cap_enabled;
u16 cqe_size; /* For use only when CQE stride feature enabled */
u16 eqe_size; /* For use only when EQE stride feature enabled */
+ u8 rss_ip_frags;
};
struct mlx4_init_ib_param {
--- /dev/null
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies.
+ * All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/export.h>
+#include "fw_qos.h"
+#include "fw.h"
+
+enum {
+ /* allocate vpp opcode modifiers */
+ MLX4_ALLOCATE_VPP_ALLOCATE = 0x0,
+ MLX4_ALLOCATE_VPP_QUERY = 0x1
+};
+
+enum {
+ /* set vport qos opcode modifiers */
+ MLX4_SET_VPORT_QOS_SET = 0x0,
+ MLX4_SET_VPORT_QOS_QUERY = 0x1
+};
+
+struct mlx4_set_port_prio2tc_context {
+ u8 prio2tc[4];
+};
+
+struct mlx4_port_scheduler_tc_cfg_be {
+ __be16 pg;
+ __be16 bw_precentage;
+ __be16 max_bw_units; /* 3-100Mbps, 4-1Gbps, other values - reserved */
+ __be16 max_bw_value;
+};
+
+struct mlx4_set_port_scheduler_context {
+ struct mlx4_port_scheduler_tc_cfg_be tc[MLX4_NUM_TC];
+};
+
+/* Granular Qos (per VF) section */
+struct mlx4_alloc_vpp_param {
+ __be32 availible_vpp;
+ __be32 vpp_p_up[MLX4_NUM_UP];
+};
+
+struct mlx4_prio_qos_param {
+ __be32 bw_share;
+ __be32 max_avg_bw;
+ __be32 reserved;
+ __be32 enable;
+ __be32 reserved1[4];
+};
+
+struct mlx4_set_vport_context {
+ __be32 reserved[8];
+ struct mlx4_prio_qos_param qos_p_up[MLX4_NUM_UP];
+};
+
+int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc)
+{
+ struct mlx4_cmd_mailbox *mailbox;
+ struct mlx4_set_port_prio2tc_context *context;
+ int err;
+ u32 in_mod;
+ int i;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ context = mailbox->buf;
+
+ for (i = 0; i < MLX4_NUM_UP; i += 2)
+ context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1];
+
+ in_mod = MLX4_SET_PORT_PRIO2TC << 8 | port;
+ err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ return err;
+}
+EXPORT_SYMBOL(mlx4_SET_PORT_PRIO2TC);
+
+int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw,
+ u8 *pg, u16 *ratelimit)
+{
+ struct mlx4_cmd_mailbox *mailbox;
+ struct mlx4_set_port_scheduler_context *context;
+ int err;
+ u32 in_mod;
+ int i;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ context = mailbox->buf;
+
+ for (i = 0; i < MLX4_NUM_TC; i++) {
+ struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i];
+ u16 r;
+
+ if (ratelimit && ratelimit[i]) {
+ if (ratelimit[i] <= MLX4_MAX_100M_UNITS_VAL) {
+ r = ratelimit[i];
+ tc->max_bw_units =
+ htons(MLX4_RATELIMIT_100M_UNITS);
+ } else {
+ r = ratelimit[i] / 10;
+ tc->max_bw_units =
+ htons(MLX4_RATELIMIT_1G_UNITS);
+ }
+ tc->max_bw_value = htons(r);
+ } else {
+ tc->max_bw_value = htons(MLX4_RATELIMIT_DEFAULT);
+ tc->max_bw_units = htons(MLX4_RATELIMIT_1G_UNITS);
+ }
+
+ tc->pg = htons(pg[i]);
+ tc->bw_precentage = htons(tc_tx_bw[i]);
+ }
+
+ in_mod = MLX4_SET_PORT_SCHEDULER << 8 | port;
+ err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ return err;
+}
+EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER);
+
+int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port,
+ u16 *availible_vpp, u8 *vpp_p_up)
+{
+ int i;
+ int err;
+ struct mlx4_cmd_mailbox *mailbox;
+ struct mlx4_alloc_vpp_param *out_param;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ out_param = mailbox->buf;
+
+ err = mlx4_cmd_box(dev, 0, mailbox->dma, port,
+ MLX4_ALLOCATE_VPP_QUERY,
+ MLX4_CMD_ALLOCATE_VPP,
+ MLX4_CMD_TIME_CLASS_A,
+ MLX4_CMD_NATIVE);
+ if (err)
+ goto out;
+
+ /* Total number of supported VPPs */
+ *availible_vpp = (u16)be32_to_cpu(out_param->availible_vpp);
+
+ for (i = 0; i < MLX4_NUM_UP; i++)
+ vpp_p_up[i] = (u8)be32_to_cpu(out_param->vpp_p_up[i]);
+
+out:
+ mlx4_free_cmd_mailbox(dev, mailbox);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx4_ALLOCATE_VPP_get);
+
+int mlx4_ALLOCATE_VPP_set(struct mlx4_dev *dev, u8 port, u8 *vpp_p_up)
+{
+ int i;
+ int err;
+ struct mlx4_cmd_mailbox *mailbox;
+ struct mlx4_alloc_vpp_param *in_param;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ in_param = mailbox->buf;
+
+ for (i = 0; i < MLX4_NUM_UP; i++)
+ in_param->vpp_p_up[i] = cpu_to_be32(vpp_p_up[i]);
+
+ err = mlx4_cmd(dev, mailbox->dma, port,
+ MLX4_ALLOCATE_VPP_ALLOCATE,
+ MLX4_CMD_ALLOCATE_VPP,
+ MLX4_CMD_TIME_CLASS_A,
+ MLX4_CMD_NATIVE);
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ return err;
+}
+EXPORT_SYMBOL(mlx4_ALLOCATE_VPP_set);
+
+int mlx4_SET_VPORT_QOS_get(struct mlx4_dev *dev, u8 port, u8 vport,
+ struct mlx4_vport_qos_param *out_param)
+{
+ int i;
+ int err;
+ struct mlx4_cmd_mailbox *mailbox;
+ struct mlx4_set_vport_context *ctx;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ ctx = mailbox->buf;
+
+ err = mlx4_cmd_box(dev, 0, mailbox->dma, (vport << 8) | port,
+ MLX4_SET_VPORT_QOS_QUERY,
+ MLX4_CMD_SET_VPORT_QOS,
+ MLX4_CMD_TIME_CLASS_A,
+ MLX4_CMD_NATIVE);
+ if (err)
+ goto out;
+
+ for (i = 0; i < MLX4_NUM_UP; i++) {
+ out_param[i].bw_share = be32_to_cpu(ctx->qos_p_up[i].bw_share);
+ out_param[i].max_avg_bw =
+ be32_to_cpu(ctx->qos_p_up[i].max_avg_bw);
+ out_param[i].enable =
+ !!(be32_to_cpu(ctx->qos_p_up[i].enable) & 31);
+ }
+
+out:
+ mlx4_free_cmd_mailbox(dev, mailbox);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx4_SET_VPORT_QOS_get);
+
+int mlx4_SET_VPORT_QOS_set(struct mlx4_dev *dev, u8 port, u8 vport,
+ struct mlx4_vport_qos_param *in_param)
+{
+ int i;
+ int err;
+ struct mlx4_cmd_mailbox *mailbox;
+ struct mlx4_set_vport_context *ctx;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ ctx = mailbox->buf;
+
+ for (i = 0; i < MLX4_NUM_UP; i++) {
+ ctx->qos_p_up[i].bw_share = cpu_to_be32(in_param[i].bw_share);
+ ctx->qos_p_up[i].max_avg_bw =
+ cpu_to_be32(in_param[i].max_avg_bw);
+ ctx->qos_p_up[i].enable =
+ cpu_to_be32(in_param[i].enable << 31);
+ }
+
+ err = mlx4_cmd(dev, mailbox->dma, (vport << 8) | port,
+ MLX4_SET_VPORT_QOS_SET,
+ MLX4_CMD_SET_VPORT_QOS,
+ MLX4_CMD_TIME_CLASS_A,
+ MLX4_CMD_NATIVE);
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ return err;
+}
+EXPORT_SYMBOL(mlx4_SET_VPORT_QOS_set);
--- /dev/null
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved.
+ * Copyright (c) 2005, 2006, 2007, 2008 Mellanox Technologies.
+ * All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef MLX4_FW_QOS_H
+#define MLX4_FW_QOS_H
+
+#include <linux/mlx4/cmd.h>
+#include <linux/mlx4/device.h>
+
+#define MLX4_NUM_UP 8
+#define MLX4_NUM_TC 8
+
+/* Default supported priorities for VPP allocation */
+#define MLX4_DEFAULT_QOS_PRIO (0)
+
+/* Derived from FW feature definition, 0 is the default vport fo all QPs */
+#define MLX4_VPP_DEFAULT_VPORT (0)
+
+struct mlx4_vport_qos_param {
+ u32 bw_share;
+ u32 max_avg_bw;
+ u8 enable;
+};
+
+/**
+ * mlx4_SET_PORT_PRIO2TC - This routine maps user priorities to traffic
+ * classes of a given port and device.
+ *
+ * @dev: mlx4_dev.
+ * @port: Physical port number.
+ * @prio2tc: Array of TC associated with each priorities.
+ *
+ * Returns 0 on success or a negative mlx4_core errno code.
+ **/
+int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc);
+
+/**
+ * mlx4_SET_PORT_SCHEDULER - This routine configures the arbitration between
+ * traffic classes (ETS) and configured rate limit for traffic classes.
+ * tc_tx_bw, pg and ratelimit are arrays where each index represents a TC.
+ * The description for those parameters below refers to a single TC.
+ *
+ * @dev: mlx4_dev.
+ * @port: Physical port number.
+ * @tc_tx_bw: The percentage of the bandwidth allocated for traffic class
+ * within a TC group. The sum of the bw_percentage of all the traffic
+ * classes within a TC group must equal 100% for correct operation.
+ * @pg: The TC group the traffic class is associated with.
+ * @ratelimit: The maximal bandwidth allowed for the use by this traffic class.
+ *
+ * Returns 0 on success or a negative mlx4_core errno code.
+ **/
+int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw,
+ u8 *pg, u16 *ratelimit);
+/**
+ * mlx4_ALLOCATE_VPP_get - Query port VPP availible resources and allocation.
+ * Before distribution of VPPs to priorities, only availible_vpp is returned.
+ * After initialization it returns the distribution of VPPs among priorities.
+ *
+ * @dev: mlx4_dev.
+ * @port: Physical port number.
+ * @availible_vpp: Pointer to variable where number of availible VPPs is stored
+ * @vpp_p_up: Distribution of VPPs to priorities is stored in this array
+ *
+ * Returns 0 on success or a negative mlx4_core errno code.
+ **/
+int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port,
+ u16 *availible_vpp, u8 *vpp_p_up);
+/**
+ * mlx4_ALLOCATE_VPP_set - Distribution of VPPs among differnt priorities.
+ * The total number of VPPs assigned to all for a port must not exceed
+ * the value reported by availible_vpp in mlx4_ALLOCATE_VPP_get.
+ * VPP allocation is allowed only after the port type has been set,
+ * and while no QPs are open for this port.
+ *
+ * @dev: mlx4_dev.
+ * @port: Physical port number.
+ * @vpp_p_up: Allocation of VPPs to different priorities.
+ *
+ * Returns 0 on success or a negative mlx4_core errno code.
+ **/
+int mlx4_ALLOCATE_VPP_set(struct mlx4_dev *dev, u8 port, u8 *vpp_p_up);
+
+/**
+ * mlx4_SET_VPORT_QOS_get - Query QoS proporties of a Vport.
+ * Each priority allowed for the Vport is assigned with a share of the BW,
+ * and a BW limitation. This commands query the current QoS values.
+ *
+ * @dev: mlx4_dev.
+ * @port: Physical port number.
+ * @vport: Vport id.
+ * @out_param: Array of mlx4_vport_qos_param that will contain the values.
+ *
+ * Returns 0 on success or a negative mlx4_core errno code.
+ **/
+int mlx4_SET_VPORT_QOS_get(struct mlx4_dev *dev, u8 port, u8 vport,
+ struct mlx4_vport_qos_param *out_param);
+
+/**
+ * mlx4_SET_VPORT_QOS_set - Set QoS proporties of a Vport.
+ * QoS parameters can be modified at any time, but must be initialized
+ * before any QP is associated with the VPort.
+ *
+ * @dev: mlx4_dev.
+ * @port: Physical port number.
+ * @vport: Vport id.
+ * @out_param: Array of mlx4_vport_qos_param which holds the requested values.
+ *
+ * Returns 0 on success or a negative mlx4_core errno code.
+ **/
+int mlx4_SET_VPORT_QOS_set(struct mlx4_dev *dev, u8 port, u8 vport,
+ struct mlx4_vport_qos_param *in_param);
+
+#endif /* MLX4_FW_QOS_H */
return err;
}
+static inline void mlx4_enable_ignore_fcs(struct mlx4_dev *dev)
+{
+ if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_IGNORE_FCS))
+ return;
+
+ if (mlx4_is_mfunc(dev)) {
+ mlx4_dbg(dev, "SRIOV mode - Disabling Ignore FCS");
+ dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_IGNORE_FCS;
+ return;
+ }
+
+ if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_FCS_KEEP)) {
+ mlx4_dbg(dev,
+ "Keep FCS is not supported - Disabling Ignore FCS");
+ dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_IGNORE_FCS;
+ return;
+ }
+}
+
#define MLX4_A0_STEERING_TABLE_SIZE 256
static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
{
dev->caps.alloc_res_qp_mask =
(dev->caps.bf_reg_size ? MLX4_RESERVE_ETH_BF_QP : 0) |
MLX4_RESERVE_A0_QP;
+
+ if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETS_CFG) &&
+ dev->caps.flags & MLX4_DEV_CAP_FLAG_SET_ETH_SCHED) {
+ mlx4_warn(dev, "Old device ETS support detected\n");
+ mlx4_warn(dev, "Consider upgrading device FW.\n");
+ dev->caps.flags2 |= MLX4_DEV_CAP_FLAG2_ETS_CFG;
+ }
+
} else {
dev->caps.alloc_res_qp_mask = 0;
}
+ mlx4_enable_ignore_fcs(dev);
+
return 0;
}
mlx4_warn(dev, "Timestamping is not supported in slave mode\n");
slave_adjust_steering_mode(dev, &dev_cap, &hca_param);
+ mlx4_dbg(dev, "RSS support for IP fragments is %s\n",
+ hca_param.rss_ip_frags ? "on" : "off");
if (func_cap.extra_flags & MLX4_QUERY_FUNC_FLAGS_BF_RES_QP &&
dev->caps.bf_reg_size)
#include <linux/mlx4/driver.h>
#include <linux/mlx4/doorbell.h>
#include <linux/mlx4/cmd.h>
+#include "fw_qos.h"
#define DRV_NAME "mlx4_core"
#define PFX DRV_NAME ": "
#define INIT_HCA_TPT_MW_ENABLE (1 << 7)
-struct mlx4_set_port_prio2tc_context {
- u8 prio2tc[4];
-};
-
-struct mlx4_port_scheduler_tc_cfg_be {
- __be16 pg;
- __be16 bw_precentage;
- __be16 max_bw_units; /* 3-100Mbps, 4-1Gbps, other values - reserved */
- __be16 max_bw_value;
-};
-
-struct mlx4_set_port_scheduler_context {
- struct mlx4_port_scheduler_tc_cfg_be tc[MLX4_NUM_TC];
-};
-
enum {
MLX4_HCR_BASE = 0x80680,
MLX4_HCR_SIZE = 0x0001c,
u32 tx_rate;
bool spoofchk;
u32 link_state;
+ u8 qos_vport;
};
struct mlx4_vf_admin_state {
struct mlx4_eqe event_eqe[SLAVE_EVENT_EQ_SIZE];
};
+struct mlx4_qos_manager {
+ int num_of_qos_vfs;
+ DECLARE_BITMAP(priority_bm, MLX4_NUM_UP);
+};
+
struct mlx4_master_qp0_state {
int proxy_qp0_active;
int qp0_active;
struct mlx4_eqe cmd_eqe;
struct mlx4_slave_event_eq slave_eq;
struct mutex gen_eqe_mutex[MLX4_MFUNC_MAX];
+ struct mlx4_qos_manager qos_ctl[MLX4_MAX_PORTS + 1];
};
struct mlx4_mfunc {
int orig_vlan_ix;
u8 port;
u8 qos;
+ u8 qos_vport;
u16 vlan_id;
u16 orig_vlan_id;
};
struct mlx4_set_port_general_context {
- u8 reserved[3];
+ u16 reserved1;
+ u8 v_ignore_fcs;
u8 flags;
- u16 reserved2;
+ u8 ignore_fcs;
+ u8 reserved2;
__be16 mtu;
u8 pptx;
u8 pfctx;
MLX4_EN_FLAG_RX_CSUM_NON_TCP_UDP = (1 << 5),
};
+#define PORT_BEACON_MAX_LIMIT (65535)
#define MLX4_EN_MAC_HASH_SIZE (1 << BITS_PER_BYTE)
#define MLX4_EN_MAC_HASH_IDX 5
#define MLX4_VLAN_VALID (1u << 31)
#define MLX4_VLAN_MASK 0xfff
+#define MLX4_STATS_TRAFFIC_COUNTERS_MASK 0xfULL
+#define MLX4_STATS_TRAFFIC_DROPS_MASK 0xc0ULL
+#define MLX4_STATS_ERROR_COUNTERS_MASK 0x1ffc30ULL
+#define MLX4_STATS_PORT_COUNTERS_MASK 0x1fe00000ULL
+
+#define MLX4_FLAG_V_IGNORE_FCS_MASK 0x2
+#define MLX4_IGNORE_FCS_MASK 0x1
+
void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table)
{
int i;
in_mod = MLX4_SET_PORT_MAC_TABLE << 8 | port;
- err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
- MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
+ err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE,
+ MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
+ MLX4_CMD_NATIVE);
mlx4_free_cmd_mailbox(dev, mailbox);
return err;
memcpy(mailbox->buf, entries, MLX4_VLAN_TABLE_SIZE);
in_mod = MLX4_SET_PORT_VLAN_TABLE << 8 | port;
- err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
- MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
+ err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE,
+ MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
+ MLX4_CMD_NATIVE);
mlx4_free_cmd_mailbox(dev, mailbox);
MLX4_ROCE_GID_ENTRY_SIZE);
err = mlx4_cmd(dev, mailbox->dma,
- ((u32)port) | (MLX4_SET_PORT_GID_TABLE << 8), 1,
- MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
- MLX4_CMD_NATIVE);
+ ((u32)port) | (MLX4_SET_PORT_GID_TABLE << 8),
+ MLX4_SET_PORT_ETH_OPCODE, MLX4_CMD_SET_PORT,
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
mutex_unlock(&(priv->port[port].gid_table.mutex));
return err;
}
MLX4_CMD_NATIVE);
}
+ /* Slaves are not allowed to SET_PORT beacon (LED) blink */
+ if (op_mod == MLX4_SET_PORT_BEACON_OPCODE) {
+ mlx4_warn(dev, "denying SET_PORT Beacon slave:%d\n", slave);
+ return -EPERM;
+ }
+
/* For IB, we only consider:
* - The capability mask, which is set to the aggregate of all
* slave function capabilities
(pkey_tbl_flag << MLX4_CHANGE_PORT_PKEY_TBL_SZ) |
(dev->caps.port_ib_mtu[port] << MLX4_SET_PORT_MTU_CAP) |
(vl_cap << MLX4_SET_PORT_VL_CAP));
- err = mlx4_cmd(dev, mailbox->dma, port, 0, MLX4_CMD_SET_PORT,
- MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED);
+ err = mlx4_cmd(dev, mailbox->dma, port,
+ MLX4_SET_PORT_IB_OPCODE, MLX4_CMD_SET_PORT,
+ MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED);
if (err != -ENOMEM)
break;
}
context->pfcrx = pfcrx;
in_mod = MLX4_SET_PORT_GENERAL << 8 | port;
- err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
- MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED);
+ err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE,
+ MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
+ MLX4_CMD_WRAPPED);
mlx4_free_cmd_mailbox(dev, mailbox);
return err;
context->vlan_miss = MLX4_VLAN_MISS_IDX;
in_mod = MLX4_SET_PORT_RQP_CALC << 8 | port;
- err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
- MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED);
+ err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE,
+ MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
+ MLX4_CMD_WRAPPED);
mlx4_free_cmd_mailbox(dev, mailbox);
return err;
}
EXPORT_SYMBOL(mlx4_SET_PORT_qpn_calc);
-int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc)
+int mlx4_SET_PORT_fcs_check(struct mlx4_dev *dev, u8 port, u8 ignore_fcs_value)
{
struct mlx4_cmd_mailbox *mailbox;
- struct mlx4_set_port_prio2tc_context *context;
- int err;
+ struct mlx4_set_port_general_context *context;
u32 in_mod;
- int i;
-
- mailbox = mlx4_alloc_cmd_mailbox(dev);
- if (IS_ERR(mailbox))
- return PTR_ERR(mailbox);
- context = mailbox->buf;
- for (i = 0; i < MLX4_NUM_UP; i += 2)
- context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1];
-
- in_mod = MLX4_SET_PORT_PRIO2TC << 8 | port;
- err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
- MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
-
- mlx4_free_cmd_mailbox(dev, mailbox);
- return err;
-}
-EXPORT_SYMBOL(mlx4_SET_PORT_PRIO2TC);
-
-int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw,
- u8 *pg, u16 *ratelimit)
-{
- struct mlx4_cmd_mailbox *mailbox;
- struct mlx4_set_port_scheduler_context *context;
int err;
- u32 in_mod;
- int i;
mailbox = mlx4_alloc_cmd_mailbox(dev);
if (IS_ERR(mailbox))
return PTR_ERR(mailbox);
context = mailbox->buf;
+ context->v_ignore_fcs |= MLX4_FLAG_V_IGNORE_FCS_MASK;
+ if (ignore_fcs_value)
+ context->ignore_fcs |= MLX4_IGNORE_FCS_MASK;
+ else
+ context->ignore_fcs &= ~MLX4_IGNORE_FCS_MASK;
- for (i = 0; i < MLX4_NUM_TC; i++) {
- struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i];
- u16 r;
-
- if (ratelimit && ratelimit[i]) {
- if (ratelimit[i] <= MLX4_MAX_100M_UNITS_VAL) {
- r = ratelimit[i];
- tc->max_bw_units =
- htons(MLX4_RATELIMIT_100M_UNITS);
- } else {
- r = ratelimit[i]/10;
- tc->max_bw_units =
- htons(MLX4_RATELIMIT_1G_UNITS);
- }
- tc->max_bw_value = htons(r);
- } else {
- tc->max_bw_value = htons(MLX4_RATELIMIT_DEFAULT);
- tc->max_bw_units = htons(MLX4_RATELIMIT_1G_UNITS);
- }
-
- tc->pg = htons(pg[i]);
- tc->bw_precentage = htons(tc_tx_bw[i]);
- }
-
- in_mod = MLX4_SET_PORT_SCHEDULER << 8 | port;
+ in_mod = MLX4_SET_PORT_GENERAL << 8 | port;
err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
mlx4_free_cmd_mailbox(dev, mailbox);
return err;
}
-EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER);
+EXPORT_SYMBOL(mlx4_SET_PORT_fcs_check);
enum {
VXLAN_ENABLE_MODIFY = 1 << 7,
context->steering = steering;
in_mod = MLX4_SET_PORT_VXLAN << 8 | port;
- err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT,
- MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
+ err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE,
+ MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
+ MLX4_CMD_NATIVE);
mlx4_free_cmd_mailbox(dev, mailbox);
return err;
}
EXPORT_SYMBOL(mlx4_SET_PORT_VXLAN);
+int mlx4_SET_PORT_BEACON(struct mlx4_dev *dev, u8 port, u16 time)
+{
+ int err;
+ struct mlx4_cmd_mailbox *mailbox;
+
+ mailbox = mlx4_alloc_cmd_mailbox(dev);
+ if (IS_ERR(mailbox))
+ return PTR_ERR(mailbox);
+
+ *((__be32 *)mailbox->buf) = cpu_to_be32(time);
+
+ err = mlx4_cmd(dev, mailbox->dma, port, MLX4_SET_PORT_BEACON_OPCODE,
+ MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
+ MLX4_CMD_NATIVE);
+
+ mlx4_free_cmd_mailbox(dev, mailbox);
+ return err;
+}
+EXPORT_SYMBOL(mlx4_SET_PORT_BEACON);
+
int mlx4_SET_MCAST_FLTR_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
cmd->qp_context.rate_limit_params = cpu_to_be16((params->rate_unit << 14) | params->rate_val);
}
+ if (attr & MLX4_UPDATE_QP_QOS_VPORT) {
+ qp_mask |= 1ULL << MLX4_UPD_QP_MASK_QOS_VPP;
+ cmd->qp_context.qos_vport = params->qos_vport;
+ }
+
cmd->primary_addr_path_mask = cpu_to_be64(pri_addr_path_mask);
cmd->qp_mask = cpu_to_be64(qp_mask);
int qpn;
};
-static int mlx4_is_eth(struct mlx4_dev *dev, int port)
-{
- return dev->caps.port_mask[port] == MLX4_PORT_TYPE_IB ? 0 : 1;
-}
-
static void *res_tracker_lookup(struct rb_root *root, u64 res_id)
{
struct rb_node *node = root->rb_node;
qpc->pri_path.feup |= MLX4_FEUP_FORCE_ETH_UP | MLX4_FVL_FORCE_ETH_VLAN;
qpc->pri_path.sched_queue &= 0xC7;
qpc->pri_path.sched_queue |= (vp_oper->state.default_qos) << 3;
+ qpc->qos_vport = vp_oper->state.qos_vport;
}
if (vp_oper->state.spoofchk) {
qpc->pri_path.feup |= MLX4_FSM_FORCE_ETH_SRC_MAC;
if (!priv->mfunc.master.slave_state)
return -EINVAL;
+ /* check for slave valid, slave not PF, and slave active */
+ if (slave < 0 || slave > dev->persist->num_vfs ||
+ slave == dev->caps.function ||
+ !priv->mfunc.master.slave_state[slave].active)
+ return 0;
+
event_eq = &priv->mfunc.master.slave_state[slave].event_eq[eqe->type];
/* Create the event only if the slave is registered */
qp->sched_queue & 0xC7;
upd_context->qp_context.pri_path.sched_queue |=
((work->qos & 0x7) << 3);
+ upd_context->qp_mask |=
+ cpu_to_be64(1ULL <<
+ MLX4_UPD_QP_MASK_QOS_VPP);
+ upd_context->qp_context.qos_vport =
+ work->qos_vport;
}
err = mlx4_cmd(dev, mailbox->dma,
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
db->db = pgdir->db_page + offset / sizeof(*pgdir->db_page);
db->dma = pgdir->db_dma + offset;
+ db->db[0] = 0;
+ db->db[1] = 0;
+
return 0;
}
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
u8 token;
spin_lock(&cmd->token_lock);
- token = cmd->token++ % 255 + 1;
+ cmd->token++;
+ if (cmd->token == 0)
+ cmd->token++;
+ token = cmd->token;
spin_unlock(&cmd->token_lock);
return token;
ent->ts1 = ktime_get_ns();
/* ring doorbell after the descriptor is valid */
+ mlx5_core_dbg(dev, "writing 0x%x to command doorbell\n", 1 << ent->idx);
wmb();
iowrite32be(1 << ent->idx, &dev->iseg->cmd_dbell);
- mlx5_core_dbg(dev, "write 0x%x to command doorbell\n", 1 << ent->idx);
mmiowb();
+ /* if not in polling don't use ent after this point */
if (cmd->mode == CMD_MODE_POLLING) {
poll_timeout(ent);
/* make sure we read the descriptor after ownership is SW */
goto out_out;
}
- err = mlx5_copy_from_msg(out, outb, out_size);
+ if (!callback)
+ err = mlx5_copy_from_msg(out, outb, out_size);
out_out:
if (!callback)
return err;
}
+static int alloc_cmd_page(struct mlx5_core_dev *dev, struct mlx5_cmd *cmd)
+{
+ struct device *ddev = &dev->pdev->dev;
+
+ cmd->cmd_alloc_buf = dma_zalloc_coherent(ddev, MLX5_ADAPTER_PAGE_SIZE,
+ &cmd->alloc_dma, GFP_KERNEL);
+ if (!cmd->cmd_alloc_buf)
+ return -ENOMEM;
+
+ /* make sure it is aligned to 4K */
+ if (!((uintptr_t)cmd->cmd_alloc_buf & (MLX5_ADAPTER_PAGE_SIZE - 1))) {
+ cmd->cmd_buf = cmd->cmd_alloc_buf;
+ cmd->dma = cmd->alloc_dma;
+ cmd->alloc_size = MLX5_ADAPTER_PAGE_SIZE;
+ return 0;
+ }
+
+ dma_free_coherent(ddev, MLX5_ADAPTER_PAGE_SIZE, cmd->cmd_alloc_buf,
+ cmd->alloc_dma);
+ cmd->cmd_alloc_buf = dma_zalloc_coherent(ddev,
+ 2 * MLX5_ADAPTER_PAGE_SIZE - 1,
+ &cmd->alloc_dma, GFP_KERNEL);
+ if (!cmd->cmd_alloc_buf)
+ return -ENOMEM;
+
+ cmd->cmd_buf = PTR_ALIGN(cmd->cmd_alloc_buf, MLX5_ADAPTER_PAGE_SIZE);
+ cmd->dma = ALIGN(cmd->alloc_dma, MLX5_ADAPTER_PAGE_SIZE);
+ cmd->alloc_size = 2 * MLX5_ADAPTER_PAGE_SIZE - 1;
+ return 0;
+}
+
+static void free_cmd_page(struct mlx5_core_dev *dev, struct mlx5_cmd *cmd)
+{
+ struct device *ddev = &dev->pdev->dev;
+
+ dma_free_coherent(ddev, cmd->alloc_size, cmd->cmd_alloc_buf,
+ cmd->alloc_dma);
+}
+
int mlx5_cmd_init(struct mlx5_core_dev *dev)
{
int size = sizeof(struct mlx5_cmd_prot_block);
if (!cmd->pool)
return -ENOMEM;
- cmd->cmd_buf = (void *)__get_free_pages(GFP_ATOMIC, 0);
- if (!cmd->cmd_buf) {
- err = -ENOMEM;
+ err = alloc_cmd_page(dev, cmd);
+ if (err)
goto err_free_pool;
- }
- cmd->dma = dma_map_single(&dev->pdev->dev, cmd->cmd_buf, PAGE_SIZE,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(&dev->pdev->dev, cmd->dma)) {
- err = -ENOMEM;
- goto err_free;
- }
cmd_l = ioread32be(&dev->iseg->cmdq_addr_l_sz) & 0xff;
cmd->log_sz = cmd_l >> 4 & 0xf;
dev_err(&dev->pdev->dev, "firmware reports too many outstanding commands %d\n",
1 << cmd->log_sz);
err = -EINVAL;
- goto err_map;
+ goto err_free_page;
}
if (cmd->log_sz + cmd->log_stride > MLX5_ADAPTER_PAGE_SHIFT) {
dev_err(&dev->pdev->dev, "command queue size overflow\n");
err = -EINVAL;
- goto err_map;
+ goto err_free_page;
}
cmd->checksum_disabled = 1;
dev_err(&dev->pdev->dev, "driver does not support command interface version. driver %d, firmware %d\n",
CMD_IF_REV, cmd->cmdif_rev);
err = -ENOTSUPP;
- goto err_map;
+ goto err_free_page;
}
spin_lock_init(&cmd->alloc_lock);
if (cmd_l & 0xfff) {
dev_err(&dev->pdev->dev, "invalid command queue address\n");
err = -ENOMEM;
- goto err_map;
+ goto err_free_page;
}
iowrite32be(cmd_h, &dev->iseg->cmdq_addr_h);
err = create_msg_cache(dev);
if (err) {
dev_err(&dev->pdev->dev, "failed to create command cache\n");
- goto err_map;
+ goto err_free_page;
}
set_wqname(dev);
err_cache:
destroy_msg_cache(dev);
-err_map:
- dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE,
- DMA_BIDIRECTIONAL);
-err_free:
- free_pages((unsigned long)cmd->cmd_buf, 0);
+err_free_page:
+ free_cmd_page(dev, cmd);
err_free_pool:
pci_pool_destroy(cmd->pool);
clean_debug_files(dev);
destroy_workqueue(cmd->wq);
destroy_msg_cache(dev);
- dma_unmap_single(&dev->pdev->dev, cmd->dma, PAGE_SIZE,
- DMA_BIDIRECTIONAL);
- free_pages((unsigned long)cmd->cmd_buf, 0);
+ free_cmd_page(dev, cmd);
pci_pool_destroy(cmd->pool);
}
EXPORT_SYMBOL(mlx5_cmd_cleanup);
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* Make sure we read EQ entry contents after we've
* checked the ownership bit.
*/
- rmb();
+ dma_rmb();
mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n",
eq->eqn, eqe_type_str(eqe->type));
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
#include "mlx5_core.h"
#define DRIVER_NAME "mlx5_core"
-#define DRIVER_VERSION "2.2-1"
-#define DRIVER_RELDATE "Feb 2014"
+#define DRIVER_VERSION "3.0"
+#define DRIVER_RELDATE "January 2015"
MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
-MODULE_DESCRIPTION("Mellanox ConnectX-IB HCA core library");
+MODULE_DESCRIPTION("Mellanox Connect-IB, ConnectX-4 core driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(DRIVER_VERSION);
MLX5_SET(cmd_hca_cap, to, log_max_ra_req_qp, from->gen.log_max_ra_req_qp);
MLX5_SET(cmd_hca_cap, to, log_max_ra_res_qp, from->gen.log_max_ra_res_qp);
MLX5_SET(cmd_hca_cap, to, pkey_table_size, from->gen.pkey_table_size);
- MLX5_SET(cmd_hca_cap, to, log_max_ra_req_dc, from->gen.log_max_ra_req_dc);
- MLX5_SET(cmd_hca_cap, to, log_max_ra_res_dc, from->gen.log_max_ra_res_dc);
MLX5_SET(cmd_hca_cap, to, pkey_table_size, to_fw_pkey_sz(from->gen.pkey_table_size));
MLX5_SET(cmd_hca_cap, to, log_uar_page_sz, PAGE_SHIFT - 12);
v64 = from->gen.flags & MLX5_CAP_BITS_RW_MASK;
return 0;
}
+int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn, int *irqn)
+{
+ struct mlx5_eq_table *table = &dev->priv.eq_table;
+ struct mlx5_eq *eq, *n;
+ int err = -ENOENT;
+
+ spin_lock(&table->lock);
+ list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) {
+ if (eq->index == vector) {
+ *eqn = eq->eqn;
+ *irqn = eq->irqn;
+ err = 0;
+ break;
+ }
+ }
+ spin_unlock(&table->lock);
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_vector2eqn);
+
+static void free_comp_eqs(struct mlx5_core_dev *dev)
+{
+ struct mlx5_eq_table *table = &dev->priv.eq_table;
+ struct mlx5_eq *eq, *n;
+
+ spin_lock(&table->lock);
+ list_for_each_entry_safe(eq, n, &table->comp_eqs_list, list) {
+ list_del(&eq->list);
+ spin_unlock(&table->lock);
+ if (mlx5_destroy_unmap_eq(dev, eq))
+ mlx5_core_warn(dev, "failed to destroy EQ 0x%x\n",
+ eq->eqn);
+ kfree(eq);
+ spin_lock(&table->lock);
+ }
+ spin_unlock(&table->lock);
+}
+
+static int alloc_comp_eqs(struct mlx5_core_dev *dev)
+{
+ struct mlx5_eq_table *table = &dev->priv.eq_table;
+ char name[MLX5_MAX_EQ_NAME];
+ struct mlx5_eq *eq;
+ int ncomp_vec;
+ int nent;
+ int err;
+ int i;
+
+ INIT_LIST_HEAD(&table->comp_eqs_list);
+ ncomp_vec = table->num_comp_vectors;
+ nent = MLX5_COMP_EQ_SIZE;
+ for (i = 0; i < ncomp_vec; i++) {
+ eq = kzalloc(sizeof(*eq), GFP_KERNEL);
+ if (!eq) {
+ err = -ENOMEM;
+ goto clean;
+ }
+
+ snprintf(name, MLX5_MAX_EQ_NAME, "mlx5_comp%d", i);
+ err = mlx5_create_map_eq(dev, eq,
+ i + MLX5_EQ_VEC_COMP_BASE, nent, 0,
+ name, &dev->priv.uuari.uars[0]);
+ if (err) {
+ kfree(eq);
+ goto clean;
+ }
+ mlx5_core_dbg(dev, "allocated completion EQN %d\n", eq->eqn);
+ eq->index = i;
+ spin_lock(&table->lock);
+ list_add_tail(&eq->list, &table->comp_eqs_list);
+ spin_unlock(&table->lock);
+ }
+
+ return 0;
+
+clean:
+ free_comp_eqs(dev);
+ return err;
+}
+
static int mlx5_dev_init(struct mlx5_core_dev *dev, struct pci_dev *pdev)
{
struct mlx5_priv *priv = &dev->priv;
goto err_free_uar;
}
+ err = alloc_comp_eqs(dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to alloc completion EQs\n");
+ goto err_stop_eqs;
+ }
+
MLX5_INIT_DOORBELL_LOCK(&priv->cq_uar_lock);
mlx5_init_cq_table(dev);
return 0;
+err_stop_eqs:
+ mlx5_stop_eqs(dev);
+
err_free_uar:
mlx5_free_uuars(dev, &priv->uuari);
mlx5_cleanup_srq_table(dev);
mlx5_cleanup_qp_table(dev);
mlx5_cleanup_cq_table(dev);
+ free_comp_eqs(dev);
mlx5_stop_eqs(dev);
mlx5_free_uuars(dev, &priv->uuari);
mlx5_eq_cleanup(dev);
}
EXPORT_SYMBOL(mlx5_unregister_interface);
+void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol)
+{
+ struct mlx5_priv *priv = &mdev->priv;
+ struct mlx5_device_context *dev_ctx;
+ unsigned long flags;
+ void *result = NULL;
+
+ spin_lock_irqsave(&priv->ctx_lock, flags);
+
+ list_for_each_entry(dev_ctx, &mdev->priv.ctx_list, list)
+ if ((dev_ctx->intf->protocol == protocol) &&
+ dev_ctx->intf->get_dev) {
+ result = dev_ctx->intf->get_dev(dev_ctx->context);
+ break;
+ }
+
+ spin_unlock_irqrestore(&priv->ctx_lock, flags);
+
+ return result;
+}
+EXPORT_SYMBOL(mlx5_get_protocol_dev);
+
static void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event,
unsigned long param)
{
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
int mlx5_core_query_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
struct mlx5_query_mkey_mbox_out *out, int outlen)
{
- struct mlx5_destroy_mkey_mbox_in in;
+ struct mlx5_query_mkey_mbox_in in;
int err;
memset(&in, 0, sizeof(in));
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
struct page *page;
u64 addr;
int err;
+ int nid = dev_to_node(&dev->pdev->dev);
- page = alloc_page(GFP_HIGHUSER);
+ page = alloc_pages_node(nid, GFP_HIGHUSER, 0);
if (!page) {
mlx5_core_warn(dev, "failed to allocate page\n");
return -ENOMEM;
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
DBG_PRINT(INFO_DBG, "%s: Could not allocate skb\n",
ring->dev->name);
if (first_rxdp) {
- wmb();
+ dma_wmb();
first_rxdp->Control_1 |= RXD_OWN_XENA;
}
swstats->mem_alloc_fail_cnt++;
rxdp->Control_2 |= SET_RXD_MARKER;
if (!(alloc_tab & ((1 << rxsync_frequency) - 1))) {
if (first_rxdp) {
- wmb();
+ dma_wmb();
first_rxdp->Control_1 |= RXD_OWN_XENA;
}
first_rxdp = rxdp;
* and other fields are seen by adapter correctly.
*/
if (first_rxdp) {
- wmb();
+ dma_wmb();
first_rxdp->Control_1 |= RXD_OWN_XENA;
}
}
set_rxd_buffer_size(sp, rxdp, size);
- wmb();
+ dma_wmb();
/* flip the Ownership bit to Hardware */
rxdp->Control_1 |= RXD_OWN_XENA;
}
#include "vxge-ethtool.h"
+static const char ethtool_driver_stats_keys[][ETH_GSTRING_LEN] = {
+ {"\n DRIVER STATISTICS"},
+ {"vpaths_opened"},
+ {"vpath_open_fail_cnt"},
+ {"link_up_cnt"},
+ {"link_down_cnt"},
+ {"tx_frms"},
+ {"tx_errors"},
+ {"tx_bytes"},
+ {"txd_not_free"},
+ {"txd_out_of_desc"},
+ {"rx_frms"},
+ {"rx_errors"},
+ {"rx_bytes"},
+ {"rx_mcast"},
+ {"pci_map_fail_cnt"},
+ {"skb_alloc_fail_cnt"}
+};
+
/**
* vxge_ethtool_sset - Sets different link parameters.
* @dev: device pointer.
/* Ethtool related variables and Macros. */
static int vxge_ethtool_get_sset_count(struct net_device *dev, int sset);
-static char ethtool_driver_stats_keys[][ETH_GSTRING_LEN] = {
- {"\n DRIVER STATISTICS"},
- {"vpaths_opened"},
- {"vpath_open_fail_cnt"},
- {"link_up_cnt"},
- {"link_down_cnt"},
- {"tx_frms"},
- {"tx_errors"},
- {"tx_bytes"},
- {"txd_not_free"},
- {"txd_out_of_desc"},
- {"rx_frms"},
- {"rx_errors"},
- {"rx_bytes"},
- {"rx_mcast"},
- {"pci_map_fail_cnt"},
- {"skb_alloc_fail_cnt"}
-};
-
#define VXGE_TITLE_LEN 5
#define VXGE_HW_VPATH_STATS_LEN 27
#define VXGE_HW_AGGR_STATS_LEN 13
struct net_device *master = netdev_master_upper_dev_get(dev);
int err = 0;
+ /* There are currently three cases handled here:
+ * 1. Joining a bridge
+ * 2. Leaving a previously joined bridge
+ * 3. Other, e.g. being added to or removed from a bond or openvswitch,
+ * in which case nothing is done
+ */
if (master && master->rtnl_link_ops &&
!strcmp(master->rtnl_link_ops->kind, "bridge"))
err = rocker_port_bridge_join(rocker_port, master);
- else
+ else if (rocker_port_is_bridged(rocker_port))
err = rocker_port_bridge_leave(rocker_port);
return err;
int wolopts;
int wol_irq;
struct clk *stmmac_clk;
+ struct clk *pclk;
struct reset_control *stmmac_rst;
int clk_csr;
struct timer_list eee_ctrl_timer;
}
clk_prepare_enable(priv->stmmac_clk);
+ priv->pclk = devm_clk_get(priv->device, "pclk");
+ if (IS_ERR(priv->pclk)) {
+ if (PTR_ERR(priv->pclk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto error_pclk_get;
+ }
+ priv->pclk = NULL;
+ }
+ clk_prepare_enable(priv->pclk);
+
priv->stmmac_rst = devm_reset_control_get(priv->device,
STMMAC_RESOURCE_NAME);
if (IS_ERR(priv->stmmac_rst)) {
error_netdev_register:
netif_napi_del(&priv->napi);
error_hw_init:
+ clk_disable_unprepare(priv->pclk);
+error_pclk_get:
clk_disable_unprepare(priv->stmmac_clk);
error_clk_get:
free_netdev(ndev);
unregister_netdev(ndev);
if (priv->stmmac_rst)
reset_control_assert(priv->stmmac_rst);
+ clk_disable_unprepare(priv->pclk);
clk_disable_unprepare(priv->stmmac_clk);
free_netdev(ndev);
stmmac_set_mac(priv->ioaddr, false);
pinctrl_pm_select_sleep_state(priv->device);
/* Disable clock in case of PWM is off */
+ clk_disable(priv->pclk);
clk_disable(priv->stmmac_clk);
}
spin_unlock_irqrestore(&priv->lock, flags);
pinctrl_pm_select_default_state(priv->device);
/* enable the clk prevously disabled */
clk_enable(priv->stmmac_clk);
+ clk_enable(priv->pclk);
/* reset the phy so that it's ready */
if (priv->mii)
stmmac_mdio_reset(priv->mii);
cluster_start = curr = (gp->rx_new & ~(4 - 1));
count = 0;
kick = -1;
- wmb();
+ dma_wmb();
while (curr != limit) {
curr = NEXT_RX(curr);
if (++count == 4) {
if (gem_intme(entry))
ctrl |= TXDCTRL_INTME;
txd->buffer = cpu_to_le64(mapping);
- wmb();
+ dma_wmb();
txd->control_word = cpu_to_le64(ctrl);
entry = NEXT_TX(entry);
} else {
txd = &gp->init_block->txd[entry];
txd->buffer = cpu_to_le64(mapping);
- wmb();
+ dma_wmb();
txd->control_word = cpu_to_le64(this_ctrl | len);
if (gem_intme(entry))
}
txd = &gp->init_block->txd[first_entry];
txd->buffer = cpu_to_le64(first_mapping);
- wmb();
+ dma_wmb();
txd->control_word =
cpu_to_le64(ctrl | TXDCTRL_SOF | intme | first_len);
}
gp->rx_skbs[i] = NULL;
}
rxd->status_word = 0;
- wmb();
+ dma_wmb();
rxd->buffer = 0;
}
RX_BUF_ALLOC_SIZE(gp),
PCI_DMA_FROMDEVICE);
rxd->buffer = cpu_to_le64(dma_addr);
- wmb();
+ dma_wmb();
rxd->status_word = cpu_to_le64(RXDCTRL_FRESH(gp));
skb_reserve(skb, RX_OFFSET);
}
struct gem_txd *txd = &gb->txd[i];
txd->control_word = 0;
- wmb();
+ dma_wmb();
txd->buffer = 0;
}
wmb();
static void sbus_hme_write_rxd(struct happy_meal_rxd *rxd, u32 flags, u32 addr)
{
rxd->rx_addr = (__force hme32)addr;
- wmb();
+ dma_wmb();
rxd->rx_flags = (__force hme32)flags;
}
static void sbus_hme_write_txd(struct happy_meal_txd *txd, u32 flags, u32 addr)
{
txd->tx_addr = (__force hme32)addr;
- wmb();
+ dma_wmb();
txd->tx_flags = (__force hme32)flags;
}
static void pci_hme_write_rxd(struct happy_meal_rxd *rxd, u32 flags, u32 addr)
{
rxd->rx_addr = (__force hme32)cpu_to_le32(addr);
- wmb();
+ dma_wmb();
rxd->rx_flags = (__force hme32)cpu_to_le32(flags);
}
static void pci_hme_write_txd(struct happy_meal_txd *txd, u32 flags, u32 addr)
{
txd->tx_addr = (__force hme32)cpu_to_le32(addr);
- wmb();
+ dma_wmb();
txd->tx_flags = (__force hme32)cpu_to_le32(flags);
}
sbus_readl(__reg)
#define hme_write_rxd(__hp, __rxd, __flags, __addr) \
do { (__rxd)->rx_addr = (__force hme32)(u32)(__addr); \
- wmb(); \
+ dma_wmb(); \
(__rxd)->rx_flags = (__force hme32)(u32)(__flags); \
} while(0)
#define hme_write_txd(__hp, __txd, __flags, __addr) \
do { (__txd)->tx_addr = (__force hme32)(u32)(__addr); \
- wmb(); \
+ dma_wmb(); \
(__txd)->tx_flags = (__force hme32)(u32)(__flags); \
} while(0)
#define hme_read_desc32(__hp, __p) ((__force u32)(hme32)*(__p))
readl(__reg)
#define hme_write_rxd(__hp, __rxd, __flags, __addr) \
do { (__rxd)->rx_addr = (__force hme32)cpu_to_le32(__addr); \
- wmb(); \
+ dma_wmb(); \
(__rxd)->rx_flags = (__force hme32)cpu_to_le32(__flags); \
} while(0)
#define hme_write_txd(__hp, __txd, __flags, __addr) \
do { (__txd)->tx_addr = (__force hme32)cpu_to_le32(__addr); \
- wmb(); \
+ dma_wmb(); \
(__txd)->tx_flags = (__force hme32)cpu_to_le32(__flags); \
} while(0)
static inline u32 hme_read_desc32(struct happy_meal *hp, hme32 *p)
if (desc->hdr.state != VIO_DESC_READY)
return 1;
- rmb();
+ dma_rmb();
viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%llx:%llx]\n",
desc->hdr.state, desc->hdr.ack,
/* This has to be a non-SMP write barrier because we are writing
* to memory which is shared with the peer LDOM.
*/
- wmb();
+ dma_wmb();
d->hdr.state = VIO_DESC_READY;
* is marked READY, but start_cons was false.
* If so, vnet_ack() should send out the missed "start" trigger.
*
- * Note that the wmb() above makes sure the cookies et al. are
+ * Note that the dma_wmb() above makes sure the cookies et al. are
* not globally visible before the VIO_DESC_READY, and that the
* stores are ordered correctly by the compiler. The consumer will
* not proceed until the VIO_DESC_READY is visible assuring that
u32 status;
bool part_of_skb;
- struct hv_device *device;
bool is_data_pkt;
bool xmit_more; /* from skb */
u16 vlan_tci;
struct hv_netvsc_packet *packet);
void netvsc_linkstatus_callback(struct hv_device *device_obj,
struct rndis_message *resp);
+void netvsc_xmit_completion(void *context);
int netvsc_recv_callback(struct hv_device *device_obj,
struct hv_netvsc_packet *packet,
struct ndis_tcp_ip_checksum_info *csum_info);
#define NDIS_HASH_PPI_SIZE (sizeof(struct rndis_per_packet_info) + \
sizeof(u32))
+/* Total size of all PPI data */
+#define NDIS_ALL_PPI_SIZE (NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE + \
+ NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE)
+
/* Format of Information buffer passed in a SetRequest for the OID */
/* OID_GEN_RNDIS_CONFIG_PARAMETER. */
struct rndis_config_parameter_info {
#define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \
sizeof(union rndis_message_container))
+#define RNDIS_AND_PPI_SIZE (sizeof(struct rndis_message) + NDIS_ALL_PPI_SIZE)
+
#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
packet->send_buf_index = section_index;
packet->total_data_buflen += msd_len;
- kfree(msdp->pkt);
+ if (msdp->pkt)
+ netvsc_xmit_completion(msdp->pkt);
+
if (packet->xmit_more) {
msdp->pkt = packet;
msdp->count++;
if (m_ret != 0) {
netvsc_free_send_slot(net_device,
msd_send->send_buf_index);
- kfree(msd_send);
+ netvsc_xmit_completion(msd_send);
}
}
}
count = vmxferpage_packet->range_cnt;
- netvsc_packet->device = device;
netvsc_packet->channel = channel;
/* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */
return q_idx;
}
-static void netvsc_xmit_completion(void *context)
+void netvsc_xmit_completion(void *context)
{
struct hv_netvsc_packet *packet = (struct hv_netvsc_packet *)context;
struct sk_buff *skb = (struct sk_buff *)
static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
{
struct net_device_context *net_device_ctx = netdev_priv(net);
- struct hv_netvsc_packet *packet;
+ struct hv_netvsc_packet *packet = NULL;
int ret;
unsigned int num_data_pgs;
struct rndis_message *rndis_msg;
struct rndis_packet *rndis_pkt;
u32 rndis_msg_size;
bool isvlan;
+ bool linear = false;
struct rndis_per_packet_info *ppi;
struct ndis_tcp_ip_checksum_info *csum_info;
struct ndis_tcp_lso_info *lso_info;
int hdr_offset;
u32 net_trans_info;
u32 hash;
- u32 skb_length = skb->len;
- u32 head_room = skb_headroom(skb);
+ u32 skb_length;
+ u32 head_room;
u32 pkt_sz;
struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
/* We will atmost need two pages to describe the rndis
* header. We can only transmit MAX_PAGE_BUFFER_COUNT number
- * of pages in a single packet.
+ * of pages in a single packet. If skb is scattered around
+ * more pages we try linearizing it.
*/
+
+check_size:
+ skb_length = skb->len;
+ head_room = skb_headroom(skb);
num_data_pgs = netvsc_get_slots(skb) + 2;
- if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
- netdev_err(net, "Packet too big: %u\n", skb->len);
- dev_kfree_skb(skb);
- net->stats.tx_dropped++;
- return NETDEV_TX_OK;
+ if (num_data_pgs > MAX_PAGE_BUFFER_COUNT && linear) {
+ net_alert_ratelimited("packet too big: %u pages (%u bytes)\n",
+ num_data_pgs, skb->len);
+ ret = -EFAULT;
+ goto drop;
+ } else if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
+ if (skb_linearize(skb)) {
+ net_alert_ratelimited("failed to linearize skb\n");
+ ret = -ENOMEM;
+ goto drop;
+ }
+ linear = true;
+ goto check_size;
}
- pkt_sz = sizeof(struct hv_netvsc_packet) +
- sizeof(struct rndis_message) +
- NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE +
- NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE;
+ pkt_sz = sizeof(struct hv_netvsc_packet) + RNDIS_AND_PPI_SIZE;
if (head_room < pkt_sz) {
packet = kmalloc(pkt_sz, GFP_ATOMIC);
if (!packet) {
/* out of memory, drop packet */
netdev_err(net, "unable to alloc hv_netvsc_packet\n");
- dev_kfree_skb(skb);
- net->stats.tx_dropped++;
- return NETDEV_TX_OK;
+ ret = -ENOMEM;
+ goto drop;
}
packet->part_of_skb = false;
} else {
packet->rndis_msg = (struct rndis_message *)((unsigned long)packet +
sizeof(struct hv_netvsc_packet));
- memset(packet->rndis_msg, 0, sizeof(struct rndis_message) +
- NDIS_VLAN_PPI_SIZE +
- NDIS_CSUM_PPI_SIZE +
- NDIS_LSO_PPI_SIZE +
- NDIS_HASH_PPI_SIZE);
+ memset(packet->rndis_msg, 0, RNDIS_AND_PPI_SIZE);
/* Set the completion routine */
packet->send_completion = netvsc_xmit_completion;
net->stats.tx_bytes += skb_length;
net->stats.tx_packets++;
} else {
- if (!packet->part_of_skb)
+ if (packet && !packet->part_of_skb)
kfree(packet);
if (ret != -EAGAIN) {
dev_kfree_skb_any(skb);
return -ENOMEM;
max_needed_headroom = sizeof(struct hv_netvsc_packet) +
- sizeof(struct rndis_message) +
- NDIS_VLAN_PPI_SIZE + NDIS_CSUM_PPI_SIZE +
- NDIS_LSO_PPI_SIZE + NDIS_HASH_PPI_SIZE;
+ RNDIS_AND_PPI_SIZE;
netif_carrier_off(net);
/* Simplify allocation by having a netvsc packet inline */
struct hv_netvsc_packet pkt;
- /* Set 2 pages for rndis requests crossing page boundary */
- struct hv_page_buffer buf[2];
struct rndis_message request_msg;
/*
}
static int
-at86rf230_set_txpower(struct ieee802154_hw *hw, int db)
+at86rf230_set_txpower(struct ieee802154_hw *hw, s8 db)
{
struct at86rf230_local *lp = hw->priv;
rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb);
int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev);
void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr);
-bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6);
+struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
+ const void *iaddr, bool is_v6);
+bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6);
struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
const void *iaddr, bool is_v6);
void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync);
hash = (addr->atype == IPVL_IPV6) ?
ipvlan_get_v6_hash(&addr->ip6addr) :
ipvlan_get_v4_hash(&addr->ip4addr);
- hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]);
+ if (hlist_unhashed(&addr->hlnode))
+ hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]);
}
void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync)
{
- hlist_del_rcu(&addr->hlnode);
+ hlist_del_init_rcu(&addr->hlnode);
if (sync)
synchronize_rcu();
}
-bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
+struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
+ const void *iaddr, bool is_v6)
{
- struct ipvl_port *port = ipvlan->port;
struct ipvl_addr *addr;
list_for_each_entry(addr, &ipvlan->addrs, anode) {
ipv6_addr_equal(&addr->ip6addr, iaddr)) ||
(!is_v6 && addr->atype == IPVL_IPV4 &&
addr->ip4addr.s_addr == ((struct in_addr *)iaddr)->s_addr))
- return true;
+ return addr;
}
+ return NULL;
+}
+
+bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
+{
+ struct ipvl_dev *ipvlan;
- if (ipvlan_ht_addr_lookup(port, iaddr, is_v6))
- return true;
+ ASSERT_RTNL();
+ list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+ if (ipvlan_find_addr(ipvlan, iaddr, is_v6))
+ return true;
+ }
return false;
}
if (skb->protocol == htons(ETH_P_PAUSE))
return;
- list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) {
if (local && (ipvlan == in_dev))
continue;
mcast_acct:
ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true);
}
+ rcu_read_unlock();
/* Locally generated? ...Forward a copy to the main-device as
* well. On the RX side we'll ignore it (wont give it to any
struct rtable *rt;
int err, ret = NET_XMIT_DROP;
struct flowi4 fl4 = {
- .flowi4_oif = dev->iflink,
+ .flowi4_oif = dev_get_iflink(dev),
.flowi4_tos = RT_TOS(ip4h->tos),
.flowi4_flags = FLOWI_FLAG_ANYSRC,
.daddr = ip4h->daddr,
dev->features = phy_dev->features & IPVLAN_FEATURES;
dev->features |= NETIF_F_LLTX;
dev->gso_max_size = phy_dev->gso_max_size;
- dev->iflink = phy_dev->ifindex;
dev->hard_header_len = phy_dev->hard_header_len;
ipvlan_set_lockdep_class(dev);
return 0;
}
+static int ipvlan_get_iflink(const struct net_device *dev)
+{
+ struct ipvl_dev *ipvlan = netdev_priv(dev);
+
+ return ipvlan->phy_dev->ifindex;
+}
+
static const struct net_device_ops ipvlan_netdev_ops = {
.ndo_init = ipvlan_init,
.ndo_uninit = ipvlan_uninit,
.ndo_get_stats64 = ipvlan_get_stats64,
.ndo_vlan_rx_add_vid = ipvlan_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = ipvlan_vlan_rx_kill_vid,
+ .ndo_get_iflink = ipvlan_get_iflink,
};
static int ipvlan_hard_header(struct sk_buff *skb, struct net_device *dev,
if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
ipvlan_ht_addr_del(addr, !dev->dismantle);
- list_del_rcu(&addr->anode);
+ list_del(&addr->anode);
}
}
list_del_rcu(&ipvlan->pnode);
{
struct ipvl_addr *addr;
- if (ipvlan_addr_busy(ipvlan, ip6_addr, true)) {
+ if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true)) {
netif_err(ipvlan, ifup, ipvlan->dev,
"Failed to add IPv6=%pI6c addr for %s intf\n",
ip6_addr, ipvlan->dev->name);
addr->master = ipvlan;
memcpy(&addr->ip6addr, ip6_addr, sizeof(struct in6_addr));
addr->atype = IPVL_IPV6;
- list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+ list_add_tail(&addr->anode, &ipvlan->addrs);
ipvlan->ipv6cnt++;
- ipvlan_ht_addr_add(ipvlan, addr);
+ /* If the interface is not up, the address will be added to the hash
+ * list by ipvlan_open.
+ */
+ if (netif_running(ipvlan->dev))
+ ipvlan_ht_addr_add(ipvlan, addr);
return 0;
}
{
struct ipvl_addr *addr;
- addr = ipvlan_ht_addr_lookup(ipvlan->port, ip6_addr, true);
+ addr = ipvlan_find_addr(ipvlan, ip6_addr, true);
if (!addr)
return;
ipvlan_ht_addr_del(addr, true);
- list_del_rcu(&addr->anode);
+ list_del(&addr->anode);
ipvlan->ipv6cnt--;
WARN_ON(ipvlan->ipv6cnt < 0);
kfree_rcu(addr, rcu);
{
struct ipvl_addr *addr;
- if (ipvlan_addr_busy(ipvlan, ip4_addr, false)) {
+ if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) {
netif_err(ipvlan, ifup, ipvlan->dev,
"Failed to add IPv4=%pI4 on %s intf.\n",
ip4_addr, ipvlan->dev->name);
addr->master = ipvlan;
memcpy(&addr->ip4addr, ip4_addr, sizeof(struct in_addr));
addr->atype = IPVL_IPV4;
- list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+ list_add_tail(&addr->anode, &ipvlan->addrs);
ipvlan->ipv4cnt++;
- ipvlan_ht_addr_add(ipvlan, addr);
+ /* If the interface is not up, the address will be added to the hash
+ * list by ipvlan_open.
+ */
+ if (netif_running(ipvlan->dev))
+ ipvlan_ht_addr_add(ipvlan, addr);
ipvlan_set_broadcast_mac_filter(ipvlan, true);
return 0;
{
struct ipvl_addr *addr;
- addr = ipvlan_ht_addr_lookup(ipvlan->port, ip4_addr, false);
+ addr = ipvlan_find_addr(ipvlan, ip4_addr, false);
if (!addr)
return;
ipvlan_ht_addr_del(addr, true);
- list_del_rcu(&addr->anode);
+ list_del(&addr->anode);
ipvlan->ipv4cnt--;
WARN_ON(ipvlan->ipv4cnt < 0);
if (!ipvlan->ipv4cnt)
dev->hw_features |= NETIF_F_LRO;
dev->vlan_features = lowerdev->vlan_features & MACVLAN_FEATURES;
dev->gso_max_size = lowerdev->gso_max_size;
- dev->iflink = lowerdev->ifindex;
dev->hard_header_len = lowerdev->hard_header_len;
macvlan_set_lockdep_class(dev);
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
+static int macvlan_dev_get_iflink(const struct net_device *dev)
+{
+ struct macvlan_dev *vlan = netdev_priv(dev);
+
+ return vlan->lowerdev->ifindex;
+}
+
static const struct ethtool_ops macvlan_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_settings = macvlan_ethtool_get_settings,
.ndo_netpoll_setup = macvlan_dev_netpoll_setup,
.ndo_netpoll_cleanup = macvlan_dev_netpoll_cleanup,
#endif
+ .ndo_get_iflink = macvlan_dev_get_iflink,
};
void macvlan_common_setup(struct net_device *dev)
{
struct device *dev = &phydev->dev;
struct at803x_priv *priv;
+ struct gpio_desc *gpiod_reset;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- priv->gpiod_reset = devm_gpiod_get(dev, "reset");
- if (IS_ERR(priv->gpiod_reset))
- priv->gpiod_reset = NULL;
- else
- gpiod_direction_output(priv->gpiod_reset, 1);
+ gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_reset))
+ return PTR_ERR(gpiod_reset);
+
+ priv->gpiod_reset = gpiod_reset;
phydev->priv = priv;
}
EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
+int fixed_phy_update_state(struct phy_device *phydev,
+ const struct fixed_phy_status *status,
+ const struct fixed_phy_status *changed)
+{
+ struct fixed_mdio_bus *fmb = &platform_fmb;
+ struct fixed_phy *fp;
+
+ if (!phydev || !phydev->bus)
+ return -EINVAL;
+
+ list_for_each_entry(fp, &fmb->phys, node) {
+ if (fp->addr == phydev->addr) {
+#define _UPD(x) if (changed->x) \
+ fp->status.x = status->x
+ _UPD(link);
+ _UPD(speed);
+ _UPD(duplex);
+ _UPD(pause);
+ _UPD(asym_pause);
+#undef _UPD
+ fixed_phy_update_regs(fp);
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(fixed_phy_update_state);
+
int fixed_phy_add(unsigned int irq, int phy_addr,
struct fixed_phy_status *status)
{
skb_put(skb, sizeof(padbytes));
}
- usbnet_set_skb_tx_stats(skb, 1);
+ usbnet_set_skb_tx_stats(skb, 1, 0);
return skb;
}
#define DELL_VENDOR_ID 0x413C
#define REALTEK_VENDOR_ID 0x0bda
#define SAMSUNG_VENDOR_ID 0x04e8
+#define LENOVO_VENDOR_ID 0x17ef
static const struct usb_device_id products[] = {
/* BLACKLIST !!
.driver_info = 0,
},
+/* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+
/* WHITELIST!!!
*
* CDC Ether uses two interfaces, not necessarily consecutive.
ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload;
ctx->tx_ntbs++;
- /* usbnet has already counted all the framing overhead.
+ /* usbnet will count all the framing overhead by default.
* Adjust the stats so that the tx_bytes counter show real
* payload data instead.
*/
- dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload;
-
- usbnet_set_skb_tx_stats(skb_out, n);
+ usbnet_set_skb_tx_stats(skb_out, n,
+ ctx->tx_curr_frame_payload - skb_out->len);
return skb_out;
/* Define these values to match your device */
#define VENDOR_ID_REALTEK 0x0bda
#define VENDOR_ID_SAMSUNG 0x04e8
+#define VENDOR_ID_LENOVO 0x17ef
#define MCU_TYPE_PLA 0x0100
#define MCU_TYPE_USB 0x0000
{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)},
{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)},
{REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)},
+ {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)},
{}
};
skb_put(skb, sizeof(padbytes));
}
- usbnet_set_skb_tx_stats(skb, 1);
+ usbnet_set_skb_tx_stats(skb, 1, 0);
return skb;
}
} else
urb->transfer_flags |= URB_ZERO_PACKET;
}
- entry->length = urb->transfer_buffer_length = length;
- if (!(info->flags & FLAG_MULTI_PACKET))
- usbnet_set_skb_tx_stats(skb, 1);
+ urb->transfer_buffer_length = length;
+
+ if (info->flags & FLAG_MULTI_PACKET) {
+ /* Driver has set number of packets and a length delta.
+ * Calculate the complete length and ensure that it's
+ * positive.
+ */
+ entry->length += length;
+ if (WARN_ON_ONCE(entry->length <= 0))
+ entry->length = length;
+ } else {
+ usbnet_set_skb_tx_stats(skb, 1, length);
+ }
spin_lock_irqsave(&dev->txq.lock, flags);
retval = usb_autopm_get_interface_async(dev->intf);
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
+static int veth_get_iflink(const struct net_device *dev)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+ struct net_device *peer;
+ int iflink;
+
+ rcu_read_lock();
+ peer = rcu_dereference(priv->peer);
+ iflink = peer ? peer->ifindex : 0;
+ rcu_read_unlock();
+
+ return iflink;
+}
+
static const struct net_device_ops veth_netdev_ops = {
.ndo_init = veth_dev_init,
.ndo_open = veth_open,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = veth_poll_controller,
#endif
+ .ndo_get_iflink = veth_get_iflink,
};
#define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
skb_orphan(skb);
nf_reset(skb);
- /* It is better to stop queue if running out of space
- * instead of forcing queuing layer to requeue the skb
- * by returning TX_BUSY (and cause a BUG message).
- * Since most packets only take 1 or 2 ring slots
- * this means 16 slots are typically wasted.
+ /* If running out of space, stop queue to avoid getting packets that we
+ * are then unable to transmit.
+ * An alternative would be to force queuing layer to requeue the skb by
+ * returning NETDEV_TX_BUSY. However, NETDEV_TX_BUSY should not be
+ * returned in a normal path of operation: it means that driver is not
+ * maintaining the TX queue stop/start state properly, and causes
+ * the stack to do a non-trivial amount of useless work.
+ * Since most packets only take 1 or 2 ring slots, stopping the queue
+ * early means 16 slots are typically wasted.
*/
if (sq->vq->num_free < 2+MAX_SKB_FRAGS) {
netif_stop_subqueue(dev, qnum);
rx_buf_alloc_failure) },
};
-/* gloabl stats maintained by the driver */
+/* global stats maintained by the driver */
static const struct vmxnet3_stat_desc
vmxnet3_global_stats[] = {
/* description, offset */
adapter->shared->devRead.misc.uptFeatures &=
~UPT1_F_RXCSUM;
- /* update harware LRO capability accordingly */
+ /* update hardware LRO capability accordingly */
if (features & NETIF_F_LRO)
adapter->shared->devRead.misc.uptFeatures |=
UPT1_F_LRO;
/* Watch incoming packets to learn mapping between Ethernet address
* and Tunnel endpoint.
- * Return true if packet is bogus and should be droppped.
+ * Return true if packet is bogus and should be dropped.
*/
static bool vxlan_snoop(struct net_device *dev,
union vxlan_addr *src_ip, const u8 *src_mac)
EXPORT_SYMBOL_GPL(vxlan_sock_release);
/* Update multicast group membership when first VNI on
- * multicast asddress is brought up
+ * multicast address is brought up
*/
static int vxlan_igmp_join(struct vxlan_dev *vxlan)
{
* this as a malformed packet. This behavior diverges from
* VXLAN RFC (RFC7348) which stipulates that bits in reserved
* in reserved fields are to be ignored. The approach here
- * maintains compatbility with previous stack code, and also
+ * maintains compatibility with previous stack code, and also
* is more robust and provides a little more security in
* adding extensions to VXLAN.
*/
}
#if IS_ENABLED(CONFIG_IPV6)
-static int vxlan6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb,
+static int vxlan6_xmit_skb(struct dst_entry *dst, struct sock *sk,
+ struct sk_buff *skb,
struct net_device *dev, struct in6_addr *saddr,
struct in6_addr *daddr, __u8 prio, __u8 ttl,
__be16 src_port, __be16 dst_port,
skb_set_inner_protocol(skb, htons(ETH_P_TEB));
- udp_tunnel6_xmit_skb(dst, skb, dev, saddr, daddr, prio,
+ udp_tunnel6_xmit_skb(dst, sk, skb, dev, saddr, daddr, prio,
ttl, src_port, dst_port,
!!(vxflags & VXLAN_F_UDP_ZERO_CSUM6_TX));
return 0;
}
#endif
-int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb,
+int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
__be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
__be16 src_port, __be16 dst_port,
struct vxlan_metadata *md, bool xnet, u32 vxflags)
skb_set_inner_protocol(skb, htons(ETH_P_TEB));
- return udp_tunnel_xmit_skb(rt, skb, src, dst, tos,
+ return udp_tunnel_xmit_skb(rt, sk, skb, src, dst, tos,
ttl, df, src_port, dst_port, xnet,
!(vxflags & VXLAN_F_UDP_CSUM));
}
struct vxlan_rdst *rdst, bool did_rsc)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct sock *sk = vxlan->vn_sock->sock->sk;
struct rtable *rt = NULL;
const struct iphdr *old_iph;
struct flowi4 fl4;
md.vni = htonl(vni << 8);
md.gbp = skb->mark;
- err = vxlan_xmit_skb(rt, skb, fl4.saddr,
+ err = vxlan_xmit_skb(rt, sk, skb, fl4.saddr,
dst->sin.sin_addr.s_addr, tos, ttl, df,
src_port, dst_port, &md,
!net_eq(vxlan->net, dev_net(vxlan->dev)),
iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
#if IS_ENABLED(CONFIG_IPV6)
} else {
- struct sock *sk = vxlan->vn_sock->sock->sk;
struct dst_entry *ndst;
struct flowi6 fl6;
u32 flags;
md.vni = htonl(vni << 8);
md.gbp = skb->mark;
- err = vxlan6_xmit_skb(ndst, skb, dev, &fl6.saddr, &fl6.daddr,
+ err = vxlan6_xmit_skb(ndst, sk, skb, dev, &fl6.saddr, &fl6.daddr,
0, ttl, src_port, dst_port, &md,
!net_eq(vxlan->net, dev_net(vxlan->dev)),
vxlan->flags);
int ret = 0;
if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip) &&
- !vxlan_group_used(vn, vxlan)) {
+ !vxlan_group_used(vn, vxlan))
ret = vxlan_igmp_leave(vxlan);
- if (ret)
- return ret;
- }
del_timer_sync(&vxlan->age_timer);
/* Register the network interface */
if (!(chan->netdev = alloc_hdlcdev(chan))) {
pr_warn("%s: alloc_hdlcdev failed\n", chan->name);
+ err = -ENOMEM;
goto err_hdlcdev;
}
dev_to_hdlc(chan->netdev)->attach = cosa_net_attach;
dev = alloc_hdlcdev(sc);
if (!dev) {
printk(KERN_ERR "lmc:alloc_netdev for device failed\n");
+ err = -ENOMEM;
goto err_hdlcdev;
}
ieee80211_stop_queues(hw);
}
- data->skb = skb;
-
spin_lock_irqsave(&ar->tx_data_list_lock, flags);
list_add_tail(&data->list, &ar->tx_queue_pending);
spin_unlock_irqrestore(&ar->tx_data_list_lock, flags);
if (!data)
break;
- skb = data->skb;
+ txi = container_of((void *)data, struct ieee80211_tx_info,
+ driver_data);
txqid = 0;
- txi = IEEE80211_SKB_CB(skb);
+
+ skb = container_of((void *)txi, struct sk_buff, cb);
paylen = skb->len;
+
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
ar5523_err(ar, "Failed to allocate TX urb\n");
struct ar5523_tx_data {
struct list_head list;
struct ar5523 *ar;
- struct sk_buff *skb;
struct urb *urb;
};
void (*enable_write_buffer)(void *);
void (*write_flush) (void *);
u32 (*rmw)(void *, u32 reg_offset, u32 set, u32 clr);
+ void (*enable_rmw_buffer)(void *);
+ void (*rmw_flush) (void *);
+
};
struct ath_common;
#define ATH_STAT_PROMISC 1
#define ATH_STAT_LEDSOFT 2 /* enable LED gpio status */
#define ATH_STAT_STARTED 3 /* opened & irqs enabled */
+#define ATH_STAT_RESET 4 /* hw reset */
unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
unsigned int fif_filter_flags; /* Current FIF_* filter flags */
enum ath5k_int imask;
unsigned long flags;
+ if (test_bit(ATH_STAT_RESET, ah->status))
+ return;
+
spin_lock_irqsave(&ah->irqlock, flags);
imask = ah->imask;
if (ah->rx_pending)
{
struct ath_common *common = ath5k_hw_common(ah);
int ret, ani_mode;
- bool fast;
+ bool fast = chan && modparam_fastchanswitch ? 1 : 0;
ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "resetting\n");
+ __set_bit(ATH_STAT_RESET, ah->status);
+
ath5k_hw_set_imr(ah, 0);
synchronize_irq(ah->irq);
ath5k_stop_tasklets(ah);
* so we should also free any remaining
* tx buffers */
ath5k_drain_tx_buffs(ah);
+
+ /* Stop PCU */
+ ath5k_hw_stop_rx_pcu(ah);
+
+ /* Stop DMA
+ *
+ * Note: If DMA didn't stop continue
+ * since only a reset will fix it.
+ */
+ ret = ath5k_hw_dma_stop(ah);
+
+ /* RF Bus grant won't work if we have pending
+ * frames
+ */
+ if (ret && fast) {
+ ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
+ "DMA didn't stop, falling back to normal reset\n");
+ fast = false;
+ }
+
if (chan)
ah->curchan = chan;
- fast = ((chan != NULL) && modparam_fastchanswitch) ? 1 : 0;
-
ret = ath5k_hw_reset(ah, ah->opmode, ah->curchan, fast, skip_pcu);
if (ret) {
ATH5K_ERR(ah, "can't reset hardware (%d)\n", ret);
*/
/* ath5k_chan_change(ah, c); */
+ __clear_bit(ATH_STAT_RESET, ah->status);
+
ath5k_beacon_config(ah);
/* intrs are enabled by ath5k_beacon_config */
if (ah->ah_version == AR5K_AR5212)
ath5k_hw_set_sleep_clock(ah, false);
- /*
- * Stop PCU
- */
- ath5k_hw_stop_rx_pcu(ah);
-
- /*
- * Stop DMA
- *
- * Note: If DMA didn't stop continue
- * since only a reset will fix it.
- */
- ret = ath5k_hw_dma_stop(ah);
-
- /* RF Bus grant won't work if we have pending
- * frames */
- if (ret && fast) {
- ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
- "DMA didn't stop, falling back to normal reset\n");
- fast = false;
- /* Non fatal, just continue with
- * normal reset */
- ret = 0;
- }
-
mode = channel->hw_value;
switch (mode) {
case AR5K_MODE_11A:
ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o
ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \
- ar9003_mci.o
+ ar9003_mci.o \
+ ar9003_aic.o
ath9k_hw-$(CONFIG_ATH9K_PCOEM) += ar9003_rtt.o
static void ath9k_hw_update_mibstats(struct ath_hw *ah,
struct ath9k_mib_stats *stats)
{
- stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
- stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
- stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
- stats->rts_good += REG_READ(ah, AR_RTS_OK);
- stats->beacons += REG_READ(ah, AR_BEACON_CNT);
+ u32 addr[5] = {AR_RTS_OK, AR_RTS_FAIL, AR_ACK_FAIL,
+ AR_FCS_FAIL, AR_BEACON_CNT};
+ u32 data[5];
+
+ REG_READ_MULTI(ah, &addr[0], &data[0], 5);
+ /* AR_RTS_OK */
+ stats->rts_good += data[0];
+ /* AR_RTS_FAIL */
+ stats->rts_bad += data[1];
+ /* AR_ACK_FAIL */
+ stats->ackrcv_bad += data[2];
+ /* AR_FCS_FAIL */
+ stats->fcs_bad += data[3];
+ /* AR_BEACON_CNT */
+ stats->beacons += data[4];
}
static void ath9k_ani_restart(struct ath_hw *ah)
phymode |= AR_PHY_FC_DYN2040_PRI_CH;
}
+ ENABLE_REGWRITE_BUFFER(ah);
REG_WRITE(ah, AR_PHY_TURBO, phymode);
+ /* This function do only REG_WRITE, so
+ * we can include it to REGWRITE_BUFFER. */
ath9k_hw_set11nmac2040(ah, chan);
- ENABLE_REGWRITE_BUFFER(ah);
-
REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S);
u32 regVal;
unsigned int i;
u32 regList[][2] = {
- { 0x786c, 0 },
- { 0x7854, 0 },
- { 0x7820, 0 },
- { 0x7824, 0 },
- { 0x7868, 0 },
- { 0x783c, 0 },
- { 0x7838, 0 } ,
- { 0x7828, 0 } ,
+ { AR9285_AN_TOP3, 0 },
+ { AR9285_AN_RXTXBB1, 0 },
+ { AR9285_AN_RF2G1, 0 },
+ { AR9285_AN_RF2G2, 0 },
+ { AR9285_AN_TOP2, 0 },
+ { AR9285_AN_RF2G8, 0 },
+ { AR9285_AN_RF2G7, 0 },
+ { AR9285_AN_RF2G3, 0 },
};
- for (i = 0; i < ARRAY_SIZE(regList); i++)
- regList[i][1] = REG_READ(ah, regList[i][0]);
-
- regVal = REG_READ(ah, 0x7834);
- regVal &= (~(0x1));
- REG_WRITE(ah, 0x7834, regVal);
- regVal = REG_READ(ah, 0x9808);
- regVal |= (0x1 << 27);
- REG_WRITE(ah, 0x9808, regVal);
+ REG_READ_ARRAY(ah, regList, ARRAY_SIZE(regList));
+ ENABLE_REG_RMW_BUFFER(ah);
+ /* 7834, b1=0 */
+ REG_CLR_BIT(ah, AR9285_AN_RF2G6, 1 << 0);
+ /* 9808, b27=1 */
+ REG_SET_BIT(ah, 0x9808, 1 << 27);
/* 786c,b23,1, pwddac=1 */
- REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1);
+ REG_SET_BIT(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC);
/* 7854, b5,1, pdrxtxbb=1 */
- REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1);
+ REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1);
/* 7854, b7,1, pdv2i=1 */
- REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1);
+ REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I);
/* 7854, b8,1, pddacinterface=1 */
- REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1);
+ REG_SET_BIT(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF);
/* 7824,b12,0, offcal=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL);
/* 7838, b1,0, pwddb=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB);
/* 7820,b11,0, enpacal=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL);
/* 7820,b25,1, pdpadrv1=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1);
/* 7820,b24,0, pdpadrv2=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2);
/* 7820,b23,0, pdpaout=0 */
- REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0);
+ REG_CLR_BIT(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT);
/* 783c,b14-16,7, padrvgn2tab_0=7 */
REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7);
/*
* does not matter since we turn it off
*/
REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0);
-
+ /* 7828, b0-11, ccom=fff */
REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff);
+ REG_RMW_BUFFER_FLUSH(ah);
/* Set:
* localmode=1,bmode=1,bmoderxtx=1,synthon=1,
/* find off_6_1; */
for (i = 6; i > 0; i--) {
- regVal = REG_READ(ah, 0x7834);
+ regVal = REG_READ(ah, AR9285_AN_RF2G6);
regVal |= (1 << (20 + i));
- REG_WRITE(ah, 0x7834, regVal);
+ REG_WRITE(ah, AR9285_AN_RF2G6, regVal);
udelay(1);
/* regVal = REG_READ(ah, 0x7834); */
regVal &= (~(0x1 << (20 + i)));
- regVal |= (MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9)
+ regVal |= (MS(REG_READ(ah, AR9285_AN_RF2G9),
+ AR9285_AN_RXTXBB1_SPARE9)
<< (20 + i));
- REG_WRITE(ah, 0x7834, regVal);
+ REG_WRITE(ah, AR9285_AN_RF2G6, regVal);
}
regVal = (regVal >> 20) & 0x7f;
ah->pacal_info.prev_offset = regVal;
}
- ENABLE_REGWRITE_BUFFER(ah);
- regVal = REG_READ(ah, 0x7834);
- regVal |= 0x1;
- REG_WRITE(ah, 0x7834, regVal);
- regVal = REG_READ(ah, 0x9808);
- regVal &= (~(0x1 << 27));
- REG_WRITE(ah, 0x9808, regVal);
+ ENABLE_REG_RMW_BUFFER(ah);
+ /* 7834, b1=1 */
+ REG_SET_BIT(ah, AR9285_AN_RF2G6, 1 << 0);
+ /* 9808, b27=0 */
+ REG_CLR_BIT(ah, 0x9808, 1 << 27);
+ REG_RMW_BUFFER_FLUSH(ah);
+ ENABLE_REGWRITE_BUFFER(ah);
for (i = 0; i < ARRAY_SIZE(regList); i++)
REG_WRITE(ah, regList[i][0], regList[i][1]);
--- /dev/null
+/*
+ * Copyright (c) 2015 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "hw.h"
+#include "hw-ops.h"
+#include "ar9003_mci.h"
+#include "ar9003_aic.h"
+#include "ar9003_phy.h"
+#include "reg_aic.h"
+
+static const u8 com_att_db_table[ATH_AIC_MAX_COM_ATT_DB_TABLE] = {
+ 0, 3, 9, 15, 21, 27
+};
+
+static const u16 aic_lin_table[ATH_AIC_MAX_AIC_LIN_TABLE] = {
+ 8191, 7300, 6506, 5799, 5168, 4606, 4105, 3659,
+ 3261, 2906, 2590, 2309, 2057, 1834, 1634, 1457,
+ 1298, 1157, 1031, 919, 819, 730, 651, 580,
+ 517, 461, 411, 366, 326, 291, 259, 231,
+ 206, 183, 163, 146, 130, 116, 103, 92,
+ 82, 73, 65, 58, 52, 46, 41, 37,
+ 33, 29, 26, 23, 21, 18, 16, 15,
+ 13, 12, 10, 9, 8, 7, 7, 6,
+ 5, 5, 4, 4, 3
+};
+
+static bool ar9003_hw_is_aic_enabled(struct ath_hw *ah)
+{
+ struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;
+
+ /*
+ * Disable AIC for now, until we have all the
+ * HW code and the driver-layer support ready.
+ */
+ return false;
+
+ if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_AIC)
+ return false;
+
+ return true;
+}
+
+static int16_t ar9003_aic_find_valid(struct ath_aic_sram_info *cal_sram,
+ bool dir, u8 index)
+{
+ int16_t i;
+
+ if (dir) {
+ for (i = index + 1; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+ if (cal_sram[i].valid)
+ break;
+ }
+ } else {
+ for (i = index - 1; i >= 0; i--) {
+ if (cal_sram[i].valid)
+ break;
+ }
+ }
+
+ if ((i >= ATH_AIC_MAX_BT_CHANNEL) || (i < 0))
+ i = -1;
+
+ return i;
+}
+
+/*
+ * type 0: aic_lin_table, 1: com_att_db_table
+ */
+static int16_t ar9003_aic_find_index(u8 type, int16_t value)
+{
+ int16_t i = -1;
+
+ if (type == 0) {
+ for (i = ATH_AIC_MAX_AIC_LIN_TABLE - 1; i >= 0; i--) {
+ if (aic_lin_table[i] >= value)
+ break;
+ }
+ } else if (type == 1) {
+ for (i = 0; i < ATH_AIC_MAX_COM_ATT_DB_TABLE; i++) {
+ if (com_att_db_table[i] > value) {
+ i--;
+ break;
+ }
+ }
+
+ if (i >= ATH_AIC_MAX_COM_ATT_DB_TABLE)
+ i = -1;
+ }
+
+ return i;
+}
+
+static void ar9003_aic_gain_table(struct ath_hw *ah)
+{
+ u32 aic_atten_word[19], i;
+
+ /* Config LNA gain difference */
+ REG_WRITE(ah, AR_PHY_BT_COEX_4, 0x2c200a00);
+ REG_WRITE(ah, AR_PHY_BT_COEX_5, 0x5c4e4438);
+
+ /* Program gain table */
+ aic_atten_word[0] = (0x1 & 0xf) << 14 | (0x1f & 0x1f) << 9 | (0x0 & 0xf) << 5 |
+ (0x1f & 0x1f); /* -01 dB: 4'd1, 5'd31, 00 dB: 4'd0, 5'd31 */
+ aic_atten_word[1] = (0x3 & 0xf) << 14 | (0x1f & 0x1f) << 9 | (0x2 & 0xf) << 5 |
+ (0x1f & 0x1f); /* -03 dB: 4'd3, 5'd31, -02 dB: 4'd2, 5'd31 */
+ aic_atten_word[2] = (0x5 & 0xf) << 14 | (0x1f & 0x1f) << 9 | (0x4 & 0xf) << 5 |
+ (0x1f & 0x1f); /* -05 dB: 4'd5, 5'd31, -04 dB: 4'd4, 5'd31 */
+ aic_atten_word[3] = (0x1 & 0xf) << 14 | (0x1e & 0x1f) << 9 | (0x0 & 0xf) << 5 |
+ (0x1e & 0x1f); /* -07 dB: 4'd1, 5'd30, -06 dB: 4'd0, 5'd30 */
+ aic_atten_word[4] = (0x3 & 0xf) << 14 | (0x1e & 0x1f) << 9 | (0x2 & 0xf) << 5 |
+ (0x1e & 0x1f); /* -09 dB: 4'd3, 5'd30, -08 dB: 4'd2, 5'd30 */
+ aic_atten_word[5] = (0x5 & 0xf) << 14 | (0x1e & 0x1f) << 9 | (0x4 & 0xf) << 5 |
+ (0x1e & 0x1f); /* -11 dB: 4'd5, 5'd30, -10 dB: 4'd4, 5'd30 */
+ aic_atten_word[6] = (0x1 & 0xf) << 14 | (0xf & 0x1f) << 9 | (0x0 & 0xf) << 5 |
+ (0xf & 0x1f); /* -13 dB: 4'd1, 5'd15, -12 dB: 4'd0, 5'd15 */
+ aic_atten_word[7] = (0x3 & 0xf) << 14 | (0xf & 0x1f) << 9 | (0x2 & 0xf) << 5 |
+ (0xf & 0x1f); /* -15 dB: 4'd3, 5'd15, -14 dB: 4'd2, 5'd15 */
+ aic_atten_word[8] = (0x5 & 0xf) << 14 | (0xf & 0x1f) << 9 | (0x4 & 0xf) << 5 |
+ (0xf & 0x1f); /* -17 dB: 4'd5, 5'd15, -16 dB: 4'd4, 5'd15 */
+ aic_atten_word[9] = (0x1 & 0xf) << 14 | (0x7 & 0x1f) << 9 | (0x0 & 0xf) << 5 |
+ (0x7 & 0x1f); /* -19 dB: 4'd1, 5'd07, -18 dB: 4'd0, 5'd07 */
+ aic_atten_word[10] = (0x3 & 0xf) << 14 | (0x7 & 0x1f) << 9 | (0x2 & 0xf) << 5 |
+ (0x7 & 0x1f); /* -21 dB: 4'd3, 5'd07, -20 dB: 4'd2, 5'd07 */
+ aic_atten_word[11] = (0x5 & 0xf) << 14 | (0x7 & 0x1f) << 9 | (0x4 & 0xf) << 5 |
+ (0x7 & 0x1f); /* -23 dB: 4'd5, 5'd07, -22 dB: 4'd4, 5'd07 */
+ aic_atten_word[12] = (0x7 & 0xf) << 14 | (0x7 & 0x1f) << 9 | (0x6 & 0xf) << 5 |
+ (0x7 & 0x1f); /* -25 dB: 4'd7, 5'd07, -24 dB: 4'd6, 5'd07 */
+ aic_atten_word[13] = (0x3 & 0xf) << 14 | (0x3 & 0x1f) << 9 | (0x2 & 0xf) << 5 |
+ (0x3 & 0x1f); /* -27 dB: 4'd3, 5'd03, -26 dB: 4'd2, 5'd03 */
+ aic_atten_word[14] = (0x5 & 0xf) << 14 | (0x3 & 0x1f) << 9 | (0x4 & 0xf) << 5 |
+ (0x3 & 0x1f); /* -29 dB: 4'd5, 5'd03, -28 dB: 4'd4, 5'd03 */
+ aic_atten_word[15] = (0x1 & 0xf) << 14 | (0x1 & 0x1f) << 9 | (0x0 & 0xf) << 5 |
+ (0x1 & 0x1f); /* -31 dB: 4'd1, 5'd01, -30 dB: 4'd0, 5'd01 */
+ aic_atten_word[16] = (0x3 & 0xf) << 14 | (0x1 & 0x1f) << 9 | (0x2 & 0xf) << 5 |
+ (0x1 & 0x1f); /* -33 dB: 4'd3, 5'd01, -32 dB: 4'd2, 5'd01 */
+ aic_atten_word[17] = (0x5 & 0xf) << 14 | (0x1 & 0x1f) << 9 | (0x4 & 0xf) << 5 |
+ (0x1 & 0x1f); /* -35 dB: 4'd5, 5'd01, -34 dB: 4'd4, 5'd01 */
+ aic_atten_word[18] = (0x7 & 0xf) << 14 | (0x1 & 0x1f) << 9 | (0x6 & 0xf) << 5 |
+ (0x1 & 0x1f); /* -37 dB: 4'd7, 5'd01, -36 dB: 4'd6, 5'd01 */
+
+ /* Write to Gain table with auto increment enabled. */
+ REG_WRITE(ah, (AR_PHY_AIC_SRAM_ADDR_B0 + 0x3000),
+ (ATH_AIC_SRAM_AUTO_INCREMENT |
+ ATH_AIC_SRAM_GAIN_TABLE_OFFSET));
+
+ for (i = 0; i < 19; i++) {
+ REG_WRITE(ah, (AR_PHY_AIC_SRAM_DATA_B0 + 0x3000),
+ aic_atten_word[i]);
+ }
+}
+
+static u8 ar9003_aic_cal_start(struct ath_hw *ah, u8 min_valid_count)
+{
+ struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+ int i;
+
+ /* Write to Gain table with auto increment enabled. */
+ REG_WRITE(ah, (AR_PHY_AIC_SRAM_ADDR_B0 + 0x3000),
+ (ATH_AIC_SRAM_AUTO_INCREMENT |
+ ATH_AIC_SRAM_CAL_OFFSET));
+
+ for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+ REG_WRITE(ah, (AR_PHY_AIC_SRAM_DATA_B0 + 0x3000), 0);
+ aic->aic_sram[i] = 0;
+ }
+
+ REG_WRITE(ah, AR_PHY_AIC_CTRL_0_B0,
+ (SM(0, AR_PHY_AIC_MON_ENABLE) |
+ SM(127, AR_PHY_AIC_CAL_MAX_HOP_COUNT) |
+ SM(min_valid_count, AR_PHY_AIC_CAL_MIN_VALID_COUNT) |
+ SM(37, AR_PHY_AIC_F_WLAN) |
+ SM(1, AR_PHY_AIC_CAL_CH_VALID_RESET) |
+ SM(0, AR_PHY_AIC_CAL_ENABLE) |
+ SM(0x40, AR_PHY_AIC_BTTX_PWR_THR) |
+ SM(0, AR_PHY_AIC_ENABLE)));
+
+ REG_WRITE(ah, AR_PHY_AIC_CTRL_0_B1,
+ (SM(0, AR_PHY_AIC_MON_ENABLE) |
+ SM(1, AR_PHY_AIC_CAL_CH_VALID_RESET) |
+ SM(0, AR_PHY_AIC_CAL_ENABLE) |
+ SM(0x40, AR_PHY_AIC_BTTX_PWR_THR) |
+ SM(0, AR_PHY_AIC_ENABLE)));
+
+ REG_WRITE(ah, AR_PHY_AIC_CTRL_1_B0,
+ (SM(8, AR_PHY_AIC_CAL_BT_REF_DELAY) |
+ SM(0, AR_PHY_AIC_BT_IDLE_CFG) |
+ SM(1, AR_PHY_AIC_STDBY_COND) |
+ SM(37, AR_PHY_AIC_STDBY_ROT_ATT_DB) |
+ SM(5, AR_PHY_AIC_STDBY_COM_ATT_DB) |
+ SM(15, AR_PHY_AIC_RSSI_MAX) |
+ SM(0, AR_PHY_AIC_RSSI_MIN)));
+
+ REG_WRITE(ah, AR_PHY_AIC_CTRL_1_B1,
+ (SM(15, AR_PHY_AIC_RSSI_MAX) |
+ SM(0, AR_PHY_AIC_RSSI_MIN)));
+
+ REG_WRITE(ah, AR_PHY_AIC_CTRL_2_B0,
+ (SM(44, AR_PHY_AIC_RADIO_DELAY) |
+ SM(8, AR_PHY_AIC_CAL_STEP_SIZE_CORR) |
+ SM(12, AR_PHY_AIC_CAL_ROT_IDX_CORR) |
+ SM(2, AR_PHY_AIC_CAL_CONV_CHECK_FACTOR) |
+ SM(5, AR_PHY_AIC_ROT_IDX_COUNT_MAX) |
+ SM(0, AR_PHY_AIC_CAL_SYNTH_TOGGLE) |
+ SM(0, AR_PHY_AIC_CAL_SYNTH_AFTER_BTRX) |
+ SM(200, AR_PHY_AIC_CAL_SYNTH_SETTLING)));
+
+ REG_WRITE(ah, AR_PHY_AIC_CTRL_3_B0,
+ (SM(2, AR_PHY_AIC_MON_MAX_HOP_COUNT) |
+ SM(1, AR_PHY_AIC_MON_MIN_STALE_COUNT) |
+ SM(1, AR_PHY_AIC_MON_PWR_EST_LONG) |
+ SM(2, AR_PHY_AIC_MON_PD_TALLY_SCALING) |
+ SM(10, AR_PHY_AIC_MON_PERF_THR) |
+ SM(2, AR_PHY_AIC_CAL_TARGET_MAG_SETTING) |
+ SM(1, AR_PHY_AIC_CAL_PERF_CHECK_FACTOR) |
+ SM(1, AR_PHY_AIC_CAL_PWR_EST_LONG)));
+
+ REG_WRITE(ah, AR_PHY_AIC_CTRL_4_B0,
+ (SM(2, AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO) |
+ SM(3, AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO) |
+ SM(0, AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING) |
+ SM(2, AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF) |
+ SM(1, AR_PHY_AIC_CAL_COM_ATT_DB_FIXED)));
+
+ REG_WRITE(ah, AR_PHY_AIC_CTRL_4_B1,
+ (SM(2, AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO) |
+ SM(3, AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO) |
+ SM(0, AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING) |
+ SM(2, AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF) |
+ SM(1, AR_PHY_AIC_CAL_COM_ATT_DB_FIXED)));
+
+ ar9003_aic_gain_table(ah);
+
+ /* Need to enable AIC reference signal in BT modem. */
+ REG_WRITE(ah, ATH_AIC_BT_JUPITER_CTRL,
+ (REG_READ(ah, ATH_AIC_BT_JUPITER_CTRL) |
+ ATH_AIC_BT_AIC_ENABLE));
+
+ aic->aic_cal_start_time = REG_READ(ah, AR_TSF_L32);
+
+ /* Start calibration */
+ REG_CLR_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE);
+ REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_CH_VALID_RESET);
+ REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE);
+
+ aic->aic_caled_chan = 0;
+ aic->aic_cal_state = AIC_CAL_STATE_STARTED;
+
+ return aic->aic_cal_state;
+}
+
+static bool ar9003_aic_cal_post_process(struct ath_hw *ah)
+{
+ struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+ struct ath_aic_sram_info cal_sram[ATH_AIC_MAX_BT_CHANNEL];
+ struct ath_aic_out_info aic_sram[ATH_AIC_MAX_BT_CHANNEL];
+ u32 dir_path_gain_idx, quad_path_gain_idx, value;
+ u32 fixed_com_att_db;
+ int8_t dir_path_sign, quad_path_sign;
+ int16_t i;
+ bool ret = true;
+
+ memset(&cal_sram, 0, sizeof(cal_sram));
+ memset(&aic_sram, 0, sizeof(aic_sram));
+
+ for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+ value = aic->aic_sram[i];
+
+ cal_sram[i].valid =
+ MS(value, AR_PHY_AIC_SRAM_VALID);
+ cal_sram[i].rot_quad_att_db =
+ MS(value, AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB);
+ cal_sram[i].vga_quad_sign =
+ MS(value, AR_PHY_AIC_SRAM_VGA_QUAD_SIGN);
+ cal_sram[i].rot_dir_att_db =
+ MS(value, AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB);
+ cal_sram[i].vga_dir_sign =
+ MS(value, AR_PHY_AIC_SRAM_VGA_DIR_SIGN);
+ cal_sram[i].com_att_6db =
+ MS(value, AR_PHY_AIC_SRAM_COM_ATT_6DB);
+
+ if (cal_sram[i].valid) {
+ dir_path_gain_idx = cal_sram[i].rot_dir_att_db +
+ com_att_db_table[cal_sram[i].com_att_6db];
+ quad_path_gain_idx = cal_sram[i].rot_quad_att_db +
+ com_att_db_table[cal_sram[i].com_att_6db];
+
+ dir_path_sign = (cal_sram[i].vga_dir_sign) ? 1 : -1;
+ quad_path_sign = (cal_sram[i].vga_quad_sign) ? 1 : -1;
+
+ aic_sram[i].dir_path_gain_lin = dir_path_sign *
+ aic_lin_table[dir_path_gain_idx];
+ aic_sram[i].quad_path_gain_lin = quad_path_sign *
+ aic_lin_table[quad_path_gain_idx];
+ }
+ }
+
+ for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+ int16_t start_idx, end_idx;
+
+ if (cal_sram[i].valid)
+ continue;
+
+ start_idx = ar9003_aic_find_valid(cal_sram, 0, i);
+ end_idx = ar9003_aic_find_valid(cal_sram, 1, i);
+
+ if (start_idx < 0) {
+ /* extrapolation */
+ start_idx = end_idx;
+ end_idx = ar9003_aic_find_valid(cal_sram, 1, start_idx);
+
+ if (end_idx < 0) {
+ ret = false;
+ break;
+ }
+
+ aic_sram[i].dir_path_gain_lin =
+ ((aic_sram[start_idx].dir_path_gain_lin -
+ aic_sram[end_idx].dir_path_gain_lin) *
+ (start_idx - i) + ((end_idx - i) >> 1)) /
+ (end_idx - i) +
+ aic_sram[start_idx].dir_path_gain_lin;
+ aic_sram[i].quad_path_gain_lin =
+ ((aic_sram[start_idx].quad_path_gain_lin -
+ aic_sram[end_idx].quad_path_gain_lin) *
+ (start_idx - i) + ((end_idx - i) >> 1)) /
+ (end_idx - i) +
+ aic_sram[start_idx].quad_path_gain_lin;
+ }
+
+ if (end_idx < 0) {
+ /* extrapolation */
+ end_idx = ar9003_aic_find_valid(cal_sram, 0, start_idx);
+
+ if (end_idx < 0) {
+ ret = false;
+ break;
+ }
+
+ aic_sram[i].dir_path_gain_lin =
+ ((aic_sram[start_idx].dir_path_gain_lin -
+ aic_sram[end_idx].dir_path_gain_lin) *
+ (i - start_idx) + ((start_idx - end_idx) >> 1)) /
+ (start_idx - end_idx) +
+ aic_sram[start_idx].dir_path_gain_lin;
+ aic_sram[i].quad_path_gain_lin =
+ ((aic_sram[start_idx].quad_path_gain_lin -
+ aic_sram[end_idx].quad_path_gain_lin) *
+ (i - start_idx) + ((start_idx - end_idx) >> 1)) /
+ (start_idx - end_idx) +
+ aic_sram[start_idx].quad_path_gain_lin;
+
+ } else if (start_idx >= 0){
+ /* interpolation */
+ aic_sram[i].dir_path_gain_lin =
+ (((end_idx - i) * aic_sram[start_idx].dir_path_gain_lin) +
+ ((i - start_idx) * aic_sram[end_idx].dir_path_gain_lin) +
+ ((end_idx - start_idx) >> 1)) /
+ (end_idx - start_idx);
+ aic_sram[i].quad_path_gain_lin =
+ (((end_idx - i) * aic_sram[start_idx].quad_path_gain_lin) +
+ ((i - start_idx) * aic_sram[end_idx].quad_path_gain_lin) +
+ ((end_idx - start_idx) >> 1))/
+ (end_idx - start_idx);
+ }
+ }
+
+ /* From dir/quad_path_gain_lin to sram. */
+ i = ar9003_aic_find_valid(cal_sram, 1, 0);
+ if (i < 0) {
+ i = 0;
+ ret = false;
+ }
+ fixed_com_att_db = com_att_db_table[cal_sram[i].com_att_6db];
+
+ for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+ int16_t rot_dir_path_att_db, rot_quad_path_att_db;
+
+ aic_sram[i].sram.vga_dir_sign =
+ (aic_sram[i].dir_path_gain_lin >= 0) ? 1 : 0;
+ aic_sram[i].sram.vga_quad_sign=
+ (aic_sram[i].quad_path_gain_lin >= 0) ? 1 : 0;
+
+ rot_dir_path_att_db =
+ ar9003_aic_find_index(0, abs(aic_sram[i].dir_path_gain_lin)) -
+ fixed_com_att_db;
+ rot_quad_path_att_db =
+ ar9003_aic_find_index(0, abs(aic_sram[i].quad_path_gain_lin)) -
+ fixed_com_att_db;
+
+ aic_sram[i].sram.com_att_6db =
+ ar9003_aic_find_index(1, fixed_com_att_db);
+
+ aic_sram[i].sram.valid = 1;
+
+ aic_sram[i].sram.rot_dir_att_db =
+ min(max(rot_dir_path_att_db,
+ (int16_t)ATH_AIC_MIN_ROT_DIR_ATT_DB),
+ ATH_AIC_MAX_ROT_DIR_ATT_DB);
+ aic_sram[i].sram.rot_quad_att_db =
+ min(max(rot_quad_path_att_db,
+ (int16_t)ATH_AIC_MIN_ROT_QUAD_ATT_DB),
+ ATH_AIC_MAX_ROT_QUAD_ATT_DB);
+ }
+
+ for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+ aic->aic_sram[i] = (SM(aic_sram[i].sram.vga_dir_sign,
+ AR_PHY_AIC_SRAM_VGA_DIR_SIGN) |
+ SM(aic_sram[i].sram.vga_quad_sign,
+ AR_PHY_AIC_SRAM_VGA_QUAD_SIGN) |
+ SM(aic_sram[i].sram.com_att_6db,
+ AR_PHY_AIC_SRAM_COM_ATT_6DB) |
+ SM(aic_sram[i].sram.valid,
+ AR_PHY_AIC_SRAM_VALID) |
+ SM(aic_sram[i].sram.rot_dir_att_db,
+ AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB) |
+ SM(aic_sram[i].sram.rot_quad_att_db,
+ AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB));
+ }
+
+ return ret;
+}
+
+static void ar9003_aic_cal_done(struct ath_hw *ah)
+{
+ struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+
+ /* Disable AIC reference signal in BT modem. */
+ REG_WRITE(ah, ATH_AIC_BT_JUPITER_CTRL,
+ (REG_READ(ah, ATH_AIC_BT_JUPITER_CTRL) &
+ ~ATH_AIC_BT_AIC_ENABLE));
+
+ if (ar9003_aic_cal_post_process(ah))
+ aic->aic_cal_state = AIC_CAL_STATE_DONE;
+ else
+ aic->aic_cal_state = AIC_CAL_STATE_ERROR;
+}
+
+static u8 ar9003_aic_cal_continue(struct ath_hw *ah, bool cal_once)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;
+ struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+ int i, num_chan;
+
+ num_chan = MS(mci_hw->config, ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN);
+
+ if (!num_chan) {
+ aic->aic_cal_state = AIC_CAL_STATE_ERROR;
+ return aic->aic_cal_state;
+ }
+
+ if (cal_once) {
+ for (i = 0; i < 10000; i++) {
+ if ((REG_READ(ah, AR_PHY_AIC_CTRL_0_B1) &
+ AR_PHY_AIC_CAL_ENABLE) == 0)
+ break;
+
+ udelay(100);
+ }
+ }
+
+ /*
+ * Use AR_PHY_AIC_CAL_ENABLE bit instead of AR_PHY_AIC_CAL_DONE.
+ * Sometimes CAL_DONE bit is not asserted.
+ */
+ if ((REG_READ(ah, AR_PHY_AIC_CTRL_0_B1) &
+ AR_PHY_AIC_CAL_ENABLE) != 0) {
+ ath_dbg(common, MCI, "AIC cal is not done after 40ms");
+ goto exit;
+ }
+
+ REG_WRITE(ah, AR_PHY_AIC_SRAM_ADDR_B1,
+ (ATH_AIC_SRAM_CAL_OFFSET | ATH_AIC_SRAM_AUTO_INCREMENT));
+
+ for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+ u32 value;
+
+ value = REG_READ(ah, AR_PHY_AIC_SRAM_DATA_B1);
+
+ if (value & 0x01) {
+ if (aic->aic_sram[i] == 0)
+ aic->aic_caled_chan++;
+
+ aic->aic_sram[i] = value;
+
+ if (!cal_once)
+ break;
+ }
+ }
+
+ if ((aic->aic_caled_chan >= num_chan) || cal_once) {
+ ar9003_aic_cal_done(ah);
+ } else {
+ /* Start calibration */
+ REG_CLR_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE);
+ REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1,
+ AR_PHY_AIC_CAL_CH_VALID_RESET);
+ REG_SET_BIT(ah, AR_PHY_AIC_CTRL_0_B1, AR_PHY_AIC_CAL_ENABLE);
+ }
+exit:
+ return aic->aic_cal_state;
+
+}
+
+u8 ar9003_aic_calibration(struct ath_hw *ah)
+{
+ struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+ u8 cal_ret = AIC_CAL_STATE_ERROR;
+
+ switch (aic->aic_cal_state) {
+ case AIC_CAL_STATE_IDLE:
+ cal_ret = ar9003_aic_cal_start(ah, 1);
+ break;
+ case AIC_CAL_STATE_STARTED:
+ cal_ret = ar9003_aic_cal_continue(ah, false);
+ break;
+ case AIC_CAL_STATE_DONE:
+ cal_ret = AIC_CAL_STATE_DONE;
+ break;
+ default:
+ break;
+ }
+
+ return cal_ret;
+}
+
+u8 ar9003_aic_start_normal(struct ath_hw *ah)
+{
+ struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+ int16_t i;
+
+ if (aic->aic_cal_state != AIC_CAL_STATE_DONE)
+ return 1;
+
+ ar9003_aic_gain_table(ah);
+
+ REG_WRITE(ah, AR_PHY_AIC_SRAM_ADDR_B1, ATH_AIC_SRAM_AUTO_INCREMENT);
+
+ for (i = 0; i < ATH_AIC_MAX_BT_CHANNEL; i++) {
+ REG_WRITE(ah, AR_PHY_AIC_SRAM_DATA_B1, aic->aic_sram[i]);
+ }
+
+ /* FIXME: Replace these with proper register names */
+ REG_WRITE(ah, 0xa6b0, 0x80);
+ REG_WRITE(ah, 0xa6b4, 0x5b2df0);
+ REG_WRITE(ah, 0xa6b8, 0x10762cc8);
+ REG_WRITE(ah, 0xa6bc, 0x1219a4b);
+ REG_WRITE(ah, 0xa6c0, 0x1e01);
+ REG_WRITE(ah, 0xb6b4, 0xf0);
+ REG_WRITE(ah, 0xb6c0, 0x1e01);
+ REG_WRITE(ah, 0xb6b0, 0x81);
+ REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX4, 0x40000000);
+
+ aic->aic_enabled = true;
+
+ return 0;
+}
+
+u8 ar9003_aic_cal_reset(struct ath_hw *ah)
+{
+ struct ath9k_hw_aic *aic = &ah->btcoex_hw.aic;
+
+ aic->aic_cal_state = AIC_CAL_STATE_IDLE;
+ return aic->aic_cal_state;
+}
+
+u8 ar9003_aic_calibration_single(struct ath_hw *ah)
+{
+ struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci;
+ u8 cal_ret;
+ int num_chan;
+
+ num_chan = MS(mci_hw->config, ATH_MCI_CONFIG_AIC_CAL_NUM_CHAN);
+
+ (void) ar9003_aic_cal_start(ah, num_chan);
+ cal_ret = ar9003_aic_cal_continue(ah, true);
+
+ return cal_ret;
+}
+
+void ar9003_hw_attach_aic_ops(struct ath_hw *ah)
+{
+ struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
+
+ priv_ops->is_aic_enabled = ar9003_hw_is_aic_enabled;
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef AR9003_AIC_H
+#define AR9003_AIC_H
+
+#define ATH_AIC_MAX_COM_ATT_DB_TABLE 6
+#define ATH_AIC_MAX_AIC_LIN_TABLE 69
+#define ATH_AIC_MIN_ROT_DIR_ATT_DB 0
+#define ATH_AIC_MIN_ROT_QUAD_ATT_DB 0
+#define ATH_AIC_MAX_ROT_DIR_ATT_DB 37
+#define ATH_AIC_MAX_ROT_QUAD_ATT_DB 37
+#define ATH_AIC_SRAM_AUTO_INCREMENT 0x80000000
+#define ATH_AIC_SRAM_GAIN_TABLE_OFFSET 0x280
+#define ATH_AIC_SRAM_CAL_OFFSET 0x140
+#define ATH_AIC_SRAM_OFFSET 0x00
+#define ATH_AIC_MEAS_MAG_THRESH 20
+#define ATH_AIC_BT_JUPITER_CTRL 0x66820
+#define ATH_AIC_BT_AIC_ENABLE 0x02
+
+enum aic_cal_state {
+ AIC_CAL_STATE_IDLE = 0,
+ AIC_CAL_STATE_STARTED,
+ AIC_CAL_STATE_DONE,
+ AIC_CAL_STATE_ERROR
+};
+
+struct ath_aic_sram_info {
+ bool valid:1;
+ bool vga_quad_sign:1;
+ bool vga_dir_sign:1;
+ u8 rot_quad_att_db;
+ u8 rot_dir_att_db;
+ u8 com_att_6db;
+};
+
+struct ath_aic_out_info {
+ int16_t dir_path_gain_lin;
+ int16_t quad_path_gain_lin;
+ struct ath_aic_sram_info sram;
+};
+
+u8 ar9003_aic_calibration(struct ath_hw *ah);
+u8 ar9003_aic_start_normal(struct ath_hw *ah);
+u8 ar9003_aic_cal_reset(struct ath_hw *ah);
+u8 ar9003_aic_calibration_single(struct ath_hw *ah);
+
+#endif /* AR9003_AIC_H */
INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
ar9485_1_1_baseband_core_txfir_coeff_japan_2484);
- if (ah->config.no_pll_pwrsave) {
+ if (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) {
INIT_INI_ARRAY(&ah->iniPcieSerdes,
- ar9485_1_1_pcie_phy_clkreq_disable_L1);
+ ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
- ar9485_1_1_pcie_phy_clkreq_disable_L1);
+ ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
} else {
INIT_INI_ARRAY(&ah->iniPcieSerdes,
- ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
+ ar9485_1_1_pcie_phy_clkreq_disable_L1);
INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
- ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1);
+ ar9485_1_1_pcie_phy_clkreq_disable_L1);
}
} else if (AR_SREV_9462_21(ah)) {
INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
ar9462_2p1_modes_fast_clock);
INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
ar9462_2p1_baseband_core_txfir_coeff_japan_2484);
- INIT_INI_ARRAY(&ah->iniPcieSerdes,
- ar9462_2p1_pciephy_clkreq_disable_L1);
- INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
- ar9462_2p1_pciephy_clkreq_disable_L1);
+
+ /* Awake -> Sleep Setting */
+ if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+ (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) {
+ INIT_INI_ARRAY(&ah->iniPcieSerdes,
+ ar9462_2p1_pciephy_clkreq_disable_L1);
+ }
+
+ /* Sleep -> Awake Setting */
+ if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+ (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) {
+ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+ ar9462_2p1_pciephy_clkreq_disable_L1);
+ }
} else if (AR_SREV_9462_20(ah)) {
INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_2p0_mac_core);
ar9462_2p0_common_rx_gain);
/* Awake -> Sleep Setting */
- INIT_INI_ARRAY(&ah->iniPcieSerdes,
- ar9462_2p0_pciephy_clkreq_disable_L1);
+ if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+ (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) {
+ INIT_INI_ARRAY(&ah->iniPcieSerdes,
+ ar9462_2p0_pciephy_clkreq_disable_L1);
+ }
+
/* Sleep -> Awake Setting */
- INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
- ar9462_2p0_pciephy_clkreq_disable_L1);
+ if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+ (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) {
+ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+ ar9462_2p0_pciephy_clkreq_disable_L1);
+ }
/* Fast clock modal settings */
INIT_INI_ARRAY(&ah->iniModesFastClock,
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9565_1p1_Modes_lowest_ob_db_tx_gain_table);
- INIT_INI_ARRAY(&ah->iniPcieSerdes,
- ar9565_1p1_pciephy_clkreq_disable_L1);
- INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
- ar9565_1p1_pciephy_clkreq_disable_L1);
+ /* Awake -> Sleep Setting */
+ if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+ (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) {
+ INIT_INI_ARRAY(&ah->iniPcieSerdes,
+ ar9565_1p1_pciephy_clkreq_disable_L1);
+ }
+
+ /* Sleep -> Awake Setting */
+ if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+ (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) {
+ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+ ar9565_1p1_pciephy_clkreq_disable_L1);
+ }
INIT_INI_ARRAY(&ah->iniModesFastClock,
ar9565_1p1_modes_fast_clock);
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9565_1p0_Modes_lowest_ob_db_tx_gain_table);
- INIT_INI_ARRAY(&ah->iniPcieSerdes,
- ar9565_1p0_pciephy_clkreq_disable_L1);
- INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
- ar9565_1p0_pciephy_clkreq_disable_L1);
+ /* Awake -> Sleep Setting */
+ if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+ (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D3)) {
+ INIT_INI_ARRAY(&ah->iniPcieSerdes,
+ ar9565_1p0_pciephy_clkreq_disable_L1);
+ }
+
+ /* Sleep -> Awake Setting */
+ if ((ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_CONTROL) &&
+ (ah->config.pll_pwrsave & AR_PCIE_PLL_PWRSAVE_ON_D0)) {
+ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
+ ar9565_1p0_pciephy_clkreq_disable_L1);
+ }
INIT_INI_ARRAY(&ah->iniModesFastClock,
ar9565_1p0_modes_fast_clock);
struct ath_hw_ops *ops = ath9k_hw_ops(ah);
ar9003_hw_init_mode_regs(ah);
+
+ if (AR_SREV_9003_PCOEM(ah)) {
+ WARN_ON(!ah->iniPcieSerdes.ia_array);
+ WARN_ON(!ah->iniPcieSerdesLowPower.ia_array);
+ }
+
priv_ops->init_mode_gain_regs = ar9003_hw_init_mode_gain_regs;
priv_ops->init_hang_checks = ar9003_hw_init_hang_checks;
priv_ops->detect_mac_hang = ar9003_hw_detect_mac_hang;
ar9003_hw_attach_phy_ops(ah);
ar9003_hw_attach_calib_ops(ah);
ar9003_hw_attach_mac_ops(ah);
+ ar9003_hw_attach_aic_ops(ah);
}
#include "hw-ops.h"
#include "ar9003_phy.h"
#include "ar9003_mci.h"
+#include "ar9003_aic.h"
static void ar9003_mci_reset_req_wakeup(struct ath_hw *ah)
{
if (en_int)
ar9003_mci_enable_interrupt(ah);
+ if (ath9k_hw_is_aic_enabled(ah))
+ ar9003_aic_start_normal(ah);
+
return 0;
}
value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0;
mci->need_flush_btinfo = false;
break;
+ case MCI_STATE_AIC_CAL:
+ if (ath9k_hw_is_aic_enabled(ah))
+ value = ar9003_aic_calibration(ah);
+ break;
+ case MCI_STATE_AIC_START:
+ if (ath9k_hw_is_aic_enabled(ah))
+ ar9003_aic_start_normal(ah);
+ break;
+ case MCI_STATE_AIC_CAL_RESET:
+ if (ath9k_hw_is_aic_enabled(ah))
+ value = ar9003_aic_cal_reset(ah);
+ break;
+ case MCI_STATE_AIC_CAL_SINGLE:
+ if (ath9k_hw_is_aic_enabled(ah))
+ value = ar9003_aic_calibration_single(ah);
+ break;
default:
break;
}
#define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE 0x0000ff00
#define AR_PHY_BB_THERM_ADC_4_LATEST_VOLT_VALUE_S 8
-/* AIC Registers */
-#define AR_PHY_AIC_CTRL_0_B0 (AR_SM_BASE + 0x4b0)
-#define AR_PHY_AIC_CTRL_1_B0 (AR_SM_BASE + 0x4b4)
-#define AR_PHY_AIC_CTRL_2_B0 (AR_SM_BASE + 0x4b8)
-#define AR_PHY_AIC_CTRL_3_B0 (AR_SM_BASE + 0x4bc)
-#define AR_PHY_AIC_STAT_0_B0 (AR_SM_BASE + 0x4c4))
-#define AR_PHY_AIC_STAT_1_B0 (AR_SM_BASE + 0x4c8))
-#define AR_PHY_AIC_CTRL_4_B0 (AR_SM_BASE + 0x4c0)
-#define AR_PHY_AIC_STAT_2_B0 (AR_SM_BASE + 0x4cc)
-
#define AR_PHY_65NM_CH0_TXRF3 0x16048
#define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G 0x0000001e
#define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G_S 1
#define AR_PHY_TX_IQCAL_STATUS_B1 (AR_SM1_BASE + 0x48c)
#define AR_PHY_TX_IQCAL_CORR_COEFF_B1(_i) (AR_SM1_BASE + 0x450 + ((_i) << 2))
-/* SM 1 AIC Registers */
-
-#define AR_PHY_AIC_CTRL_0_B1 (AR_SM1_BASE + 0x4b0)
-#define AR_PHY_AIC_CTRL_1_B1 (AR_SM1_BASE + 0x4b4)
-#define AR_PHY_AIC_CTRL_2_B1 (AR_SM1_BASE + 0x4b8)
-#define AR_PHY_AIC_STAT_0_B1 (AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \
- 0x4c0 : 0x4c4))
-#define AR_PHY_AIC_STAT_1_B1 (AR_SM1_BASE + (AR_SREV_9462_10(ah) ? \
- 0x4c4 : 0x4c8))
-#define AR_PHY_AIC_CTRL_4_B1 (AR_SM1_BASE + 0x4c0)
-#define AR_PHY_AIC_STAT_2_B1 (AR_SM1_BASE + 0x4cc)
-
-#define AR_PHY_AIC_SRAM_ADDR_B1 (AR_SM1_BASE + 0x5f0)
-#define AR_PHY_AIC_SRAM_DATA_B1 (AR_SM1_BASE + 0x5f4)
-
#define AR_PHY_RTT_TABLE_SW_INTF_B(i) (0x384 + ((i) ? \
AR_SM1_BASE : AR_SM_BASE))
#define AR_PHY_RTT_TABLE_SW_INTF_1_B(i) (0x388 + ((i) ? \
int chain, i;
for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
- if (!(ah->rxchainmask & (1 << chain)))
+ if (!(ah->caps.rx_chainmask & (1 << chain)))
continue;
for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) {
ar9003_hw_rtt_load_hist_entry(ah, chain, i,
int chain, i;
for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
- if (!(ah->rxchainmask & (1 << chain)))
+ if (!(ah->caps.rx_chainmask & (1 << chain)))
continue;
for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) {
ah->caldata->rtt_table[chain][i] =
int chain, i;
for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
- if (!(ah->rxchainmask & (1 << chain)))
+ if (!(ah->caps.rx_chainmask & (1 << chain)))
continue;
for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++)
ar9003_hw_rtt_load_hist_entry(ah, chain, i, 0);
struct ath_buf *bf;
u16 framelen;
s8 txq;
- enum ath9k_key_type keytype;
u8 keyix;
u8 rtscts_rate;
u8 retries : 7;
u8 baw_tracked : 1;
u8 tx_power;
+ enum ath9k_key_type keytype:2;
};
struct ath_rxbuf {
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (void *)vif->drv_priv;
struct ath_buf *bf = avp->av_bcbuf;
+ struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
avp->av_bslot);
tasklet_disable(&sc->bcon_tasklet);
+ cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
+
if (bf && bf->bf_mpdu) {
struct sk_buff *skb = bf->bf_mpdu;
dma_unmap_single(sc->dev, bf->bf_buf_addr,
}
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
- if ((vif->type != NL80211_IFTYPE_AP) ||
- (sc->nbcnvifs > 1)) {
+ if (vif->type != NL80211_IFTYPE_AP) {
ath_dbg(common, CONFIG,
"An AP interface is already present !\n");
return false;
* enabling/disabling SWBA.
*/
if (changed & BSS_CHANGED_BEACON_ENABLED) {
- if (!bss_conf->enable_beacon &&
- (sc->nbcnvifs <= 1)) {
- cur_conf->enable_beacon = false;
- } else if (bss_conf->enable_beacon) {
- cur_conf->enable_beacon = true;
- ath9k_cache_beacon_config(sc, ctx, bss_conf);
+ bool enabled = cur_conf->enable_beacon;
+
+ if (!bss_conf->enable_beacon) {
+ cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
+ } else {
+ cur_conf->enable_beacon |= BIT(avp->av_bslot);
+ if (!enabled)
+ ath9k_cache_beacon_config(sc, ctx, bss_conf);
}
}
#define AR9300_NUM_BT_WEIGHTS 4
#define AR9300_NUM_WLAN_WEIGHTS 4
+
+#define ATH_AIC_MAX_BT_CHANNEL 79
+
/* Defines the BT AR_BT_COEX_WGHT used */
enum ath_stomp_type {
ATH_BTCOEX_STOMP_ALL,
u32 last_recovery;
};
+struct ath9k_hw_aic {
+ bool aic_enabled;
+ u8 aic_cal_state;
+ u8 aic_caled_chan;
+ u32 aic_sram[ATH_AIC_MAX_BT_CHANNEL];
+ u32 aic_cal_start_time;
+};
+
struct ath_btcoex_hw {
enum ath_btcoex_scheme scheme;
struct ath9k_hw_mci mci;
+ struct ath9k_hw_aic aic;
bool enabled;
u8 wlanactive_gpio;
u8 btactive_gpio;
{
struct ath9k_nfcal_hist *h = NULL;
unsigned i, j;
- int32_t val;
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
struct ath_common *common = ath9k_hw_common(ah);
s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
if (ah->caldata)
h = ah->caldata->nfCalHist;
+ ENABLE_REG_RMW_BUFFER(ah);
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
s16 nfval;
else
nfval = default_nf;
- val = REG_READ(ah, ah->nf_regs[i]);
- val &= 0xFFFFFE00;
- val |= (((u32) nfval << 1) & 0x1ff);
- REG_WRITE(ah, ah->nf_regs[i], val);
+ REG_RMW(ah, ah->nf_regs[i],
+ (((u32) nfval << 1) & 0x1ff), 0x1ff);
}
}
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+ REG_RMW_BUFFER_FLUSH(ah);
/*
* Wait for load to complete, should be fast, a few 10s of us.
* by the median we just loaded. This will be initial (and max) value
* of next noise floor calibration the baseband does.
*/
- ENABLE_REGWRITE_BUFFER(ah);
+ ENABLE_REG_RMW_BUFFER(ah);
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
continue;
- val = REG_READ(ah, ah->nf_regs[i]);
- val &= 0xFFFFFE00;
- val |= (((u32) (-50) << 1) & 0x1ff);
- REG_WRITE(ah, ah->nf_regs[i], val);
+ REG_RMW(ah, ah->nf_regs[i],
+ (((u32) (-50) << 1) & 0x1ff), 0x1ff);
}
}
- REGWRITE_BUFFER_FLUSH(ah);
+ REG_RMW_BUFFER_FLUSH(ah);
return 0;
}
u16 dtim_period;
u16 bmiss_timeout;
u8 dtim_count;
- bool enable_beacon;
+ u8 enable_beacon;
bool ibss_creator;
u32 nexttbtt;
u32 intval;
DFS_STAT_INC(sc, pulses_detected);
return true;
}
-#undef PRI_CH_RADAR_FOUND
-#undef EXT_CH_RADAR_FOUND
+
+static void
+ath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe)
+{
+ struct dfs_pattern_detector *pd = sc->dfs_detector;
+ DFS_STAT_INC(sc, pulses_processed);
+ if (pd == NULL)
+ return;
+ if (!pd->add_pulse(pd, pe))
+ return;
+ DFS_STAT_INC(sc, radar_detected);
+ ieee80211_radar_detected(sc->hw);
+}
/*
* DFS: check PHY-error for radar pulse and feed the detector
ard.pulse_length_pri = vdata_end[-3];
pe.freq = ah->curchan->channel;
pe.ts = mactime;
- if (ath9k_postprocess_radar_event(sc, &ard, &pe)) {
- struct dfs_pattern_detector *pd = sc->dfs_detector;
- ath_dbg(common, DFS,
- "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, "
- "width=%d, rssi=%d, delta_ts=%llu\n",
- pe.freq, pe.ts, pe.width, pe.rssi,
- pe.ts - sc->dfs_prev_pulse_ts);
- sc->dfs_prev_pulse_ts = pe.ts;
- DFS_STAT_INC(sc, pulses_processed);
- if (pd != NULL && pd->add_pulse(pd, &pe)) {
- DFS_STAT_INC(sc, radar_detected);
- ieee80211_radar_detected(sc->hw);
- }
+ if (!ath9k_postprocess_radar_event(sc, &ard, &pe))
+ return;
+
+ ath_dbg(common, DFS,
+ "ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, "
+ "width=%d, rssi=%d, delta_ts=%llu\n",
+ ard.pulse_bw_info, pe.freq, pe.ts, pe.width, pe.rssi,
+ pe.ts - sc->dfs_prev_pulse_ts);
+ sc->dfs_prev_pulse_ts = pe.ts;
+ if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND)
+ ath9k_dfs_process_radar_pulse(sc, &pe);
+ if (ard.pulse_bw_info & EXT_CH_RADAR_FOUND) {
+ pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20;
+ ath9k_dfs_process_radar_pulse(sc, &pe);
}
}
+#undef PRI_CH_RADAR_FOUND
+#undef EXT_CH_RADAR_FOUND
void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask,
u32 shift, u32 val)
{
- u32 regVal;
-
- regVal = REG_READ(ah, reg) & ~mask;
- regVal |= (val << shift) & mask;
-
- REG_WRITE(ah, reg, regVal);
+ REG_RMW(ah, reg, ((val << shift) & mask), mask);
if (ah->config.analog_shiftreg)
udelay(100);
}
}
+ ENABLE_REG_RMW_BUFFER(ah);
REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,
(numXpdGain - 1) & 0x3);
REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1,
REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2,
xpdGainValues[1]);
REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, 0);
+ REG_RMW_BUFFER_FLUSH(ah);
for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) {
regChainOffset = i * 0x1000;
struct ar5416_eeprom_4k *eep,
u8 txRxAttenLocal)
{
- REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0,
- pModal->antCtrlChain[0]);
+ ENABLE_REG_RMW_BUFFER(ah);
+ REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0,
+ pModal->antCtrlChain[0], 0);
- REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0),
- (REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) &
- ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
- AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
- SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
- SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
+ REG_RMW(ah, AR_PHY_TIMING_CTRL4(0),
+ SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
+ SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF),
+ AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF);
if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
AR5416_EEP_MINOR_VER_3) {
AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000,
AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
+ REG_RMW_BUFFER_FLUSH(ah);
}
/*
}
}
+ ENABLE_REG_RMW_BUFFER(ah);
if (AR_SREV_9271(ah)) {
ath9k_hw_analog_shift_rmw(ah,
AR9285_AN_RF2G3,
AR9285_AN_RF2G4_DB2_4_S,
db2[4]);
}
+ REG_RMW_BUFFER_FLUSH(ah);
-
+ ENABLE_REG_RMW_BUFFER(ah);
REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH,
pModal->switchSettling);
REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC,
pModal->adcDesiredSize);
- REG_WRITE(ah, AR_PHY_RF_CTL4,
- SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) |
- SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) |
- SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) |
- SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON));
+ REG_RMW(ah, AR_PHY_RF_CTL4,
+ SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) |
+ SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) |
+ SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) |
+ SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON), 0);
REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
pModal->txEndToRxOn);
pModal->swSettleHt40);
}
+ REG_RMW_BUFFER_FLUSH(ah);
+
bb_desired_scale = (pModal->bb_scale_smrt_antenna &
EEP_4K_BB_DESIRED_SCALE_MASK);
if ((pBase->txGainType == 0) && (bb_desired_scale != 0)) {
mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25);
pwrctrl = mask * bb_desired_scale;
clr = mask * 0x1f;
+ ENABLE_REG_RMW_BUFFER(ah);
REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr);
REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr);
REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr);
clr = mask * 0x1f;
REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr);
REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr);
+ REG_RMW_BUFFER_FLUSH(ah);
}
}
struct ar5416_eeprom_def *eep,
u8 txRxAttenLocal, int regChainOffset, int i)
{
+ ENABLE_REG_RMW_BUFFER(ah);
if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) {
txRxAttenLocal = pModal->txRxAttenCh[i];
AR_PHY_GAIN_2GHZ_XATTEN2_DB,
pModal->xatten2Db[i]);
} else {
- REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
- (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
- | SM(pModal-> bswMargin[i],
- AR_PHY_GAIN_2GHZ_BSW_MARGIN));
- REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
- (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
- | SM(pModal->bswAtten[i],
- AR_PHY_GAIN_2GHZ_BSW_ATTEN));
+ REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ SM(pModal-> bswMargin[i], AR_PHY_GAIN_2GHZ_BSW_MARGIN),
+ AR_PHY_GAIN_2GHZ_BSW_MARGIN);
+ REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ SM(pModal->bswAtten[i], AR_PHY_GAIN_2GHZ_BSW_ATTEN),
+ AR_PHY_GAIN_2GHZ_BSW_ATTEN);
}
}
AR_PHY_RXGAIN + regChainOffset,
AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[i]);
} else {
- REG_WRITE(ah,
- AR_PHY_RXGAIN + regChainOffset,
- (REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) &
- ~AR_PHY_RXGAIN_TXRX_ATTEN)
- | SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN));
- REG_WRITE(ah,
- AR_PHY_GAIN_2GHZ + regChainOffset,
- (REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) &
- ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
- SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
+ REG_RMW(ah, AR_PHY_RXGAIN + regChainOffset,
+ SM(txRxAttenLocal, AR_PHY_RXGAIN_TXRX_ATTEN),
+ AR_PHY_RXGAIN_TXRX_ATTEN);
+ REG_RMW(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
+ SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN),
+ AR_PHY_GAIN_2GHZ_RXTX_MARGIN);
}
+ REG_RMW_BUFFER_FLUSH(ah);
}
static void ath9k_hw_def_set_board_values(struct ath_hw *ah,
{
struct ath_hw *ah = sc->sc_ah;
- if (AR_SREV_9100(ah) || (ah->led_pin >= 0))
+ if (AR_SREV_9100(ah))
return;
+ if (ah->led_pin >= 0) {
+ if (!((1 << ah->led_pin) & AR_GPIO_OE_OUT_MASK))
+ ath9k_hw_request_gpio(ah, ah->led_pin, "ath9k-led");
+ return;
+ }
+
if (AR_SREV_9287(ah))
ah->led_pin = ATH_LED_PIN_9287;
else if (AR_SREV_9485(sc->sc_ah))
#define OP_BT_SCAN BIT(4)
#define OP_TSF_RESET BIT(6)
+enum htc_op_flags {
+ HTC_FWFLAG_NO_RMW,
+};
+
struct ath9k_htc_priv {
struct device *dev;
struct ieee80211_hw *hw;
bool reconfig_beacon;
unsigned int rxfilter;
unsigned long op_flags;
+ unsigned long fw_flags;
struct ath9k_hw_cal_data caldata;
struct ath_spec_scan_priv spec_priv;
mutex_unlock(&priv->wmi->multi_write_mutex);
}
-static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
+static void ath9k_reg_rmw_buffer(void *hw_priv,
+ u32 reg_offset, u32 set, u32 clr)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+ u32 rsp_status;
+ int r;
+
+ mutex_lock(&priv->wmi->multi_rmw_mutex);
+
+ /* Store the register/value */
+ priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].reg =
+ cpu_to_be32(reg_offset);
+ priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].set =
+ cpu_to_be32(set);
+ priv->wmi->multi_rmw[priv->wmi->multi_rmw_idx].clr =
+ cpu_to_be32(clr);
+
+ priv->wmi->multi_rmw_idx++;
+
+ /* If the buffer is full, send it out. */
+ if (priv->wmi->multi_rmw_idx == MAX_RMW_CMD_NUMBER) {
+ r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
+ (u8 *) &priv->wmi->multi_rmw,
+ sizeof(struct register_write) * priv->wmi->multi_rmw_idx,
+ (u8 *) &rsp_status, sizeof(rsp_status),
+ 100);
+ if (unlikely(r)) {
+ ath_dbg(common, WMI,
+ "REGISTER RMW FAILED, multi len: %d\n",
+ priv->wmi->multi_rmw_idx);
+ }
+ priv->wmi->multi_rmw_idx = 0;
+ }
+
+ mutex_unlock(&priv->wmi->multi_rmw_mutex);
+}
+
+static void ath9k_reg_rmw_flush(void *hw_priv)
{
- u32 val;
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+ u32 rsp_status;
+ int r;
+
+ if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
+ return;
+
+ atomic_dec(&priv->wmi->m_rmw_cnt);
- val = ath9k_regread(hw_priv, reg_offset);
- val &= ~clr;
- val |= set;
- ath9k_regwrite(hw_priv, val, reg_offset);
+ mutex_lock(&priv->wmi->multi_rmw_mutex);
+
+ if (priv->wmi->multi_rmw_idx) {
+ r = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
+ (u8 *) &priv->wmi->multi_rmw,
+ sizeof(struct register_rmw) * priv->wmi->multi_rmw_idx,
+ (u8 *) &rsp_status, sizeof(rsp_status),
+ 100);
+ if (unlikely(r)) {
+ ath_dbg(common, WMI,
+ "REGISTER RMW FAILED, multi len: %d\n",
+ priv->wmi->multi_rmw_idx);
+ }
+ priv->wmi->multi_rmw_idx = 0;
+ }
+
+ mutex_unlock(&priv->wmi->multi_rmw_mutex);
+}
+
+static void ath9k_enable_rmw_buffer(void *hw_priv)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+
+ if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags))
+ return;
+
+ atomic_inc(&priv->wmi->m_rmw_cnt);
+}
+
+static u32 ath9k_reg_rmw_single(void *hw_priv,
+ u32 reg_offset, u32 set, u32 clr)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+ struct register_rmw buf, buf_ret;
+ int ret;
+ u32 val = 0;
+
+ buf.reg = cpu_to_be32(reg_offset);
+ buf.set = cpu_to_be32(set);
+ buf.clr = cpu_to_be32(clr);
+
+ ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_RMW_CMDID,
+ (u8 *) &buf, sizeof(buf),
+ (u8 *) &buf_ret, sizeof(buf_ret),
+ 100);
+ if (unlikely(ret)) {
+ ath_dbg(common, WMI, "REGISTER RMW FAILED:(0x%04x, %d)\n",
+ reg_offset, ret);
+ }
return val;
}
+static u32 ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 clr)
+{
+ struct ath_hw *ah = (struct ath_hw *) hw_priv;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
+
+ if (test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags)) {
+ u32 val;
+
+ val = REG_READ(ah, reg_offset);
+ val &= ~clr;
+ val |= set;
+ REG_WRITE(ah, reg_offset, val);
+
+ return 0;
+ }
+
+ if (atomic_read(&priv->wmi->m_rmw_cnt))
+ ath9k_reg_rmw_buffer(hw_priv, reg_offset, set, clr);
+ else
+ ath9k_reg_rmw_single(hw_priv, reg_offset, set, clr);
+
+ return 0;
+}
+
static void ath_usb_read_cachesize(struct ath_common *common, int *csz)
{
*csz = L1_CACHE_BYTES >> 2;
ah->reg_ops.write = ath9k_regwrite;
ah->reg_ops.enable_write_buffer = ath9k_enable_regwrite_buffer;
ah->reg_ops.write_flush = ath9k_regwrite_flush;
+ ah->reg_ops.enable_rmw_buffer = ath9k_enable_rmw_buffer;
+ ah->reg_ops.rmw_flush = ath9k_reg_rmw_flush;
ah->reg_ops.rmw = ath9k_reg_rmw;
priv->ah = ah;
return -EINVAL;
}
+ if (priv->fw_version_major == 1 && priv->fw_version_minor < 4)
+ set_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags);
+
+ dev_info(priv->dev, "FW RMW support: %s\n",
+ test_bit(HTC_FWFLAG_NO_RMW, &priv->fw_flags) ? "Off" : "On");
+
return 0;
}
ath9k_hw_ops(ah)->set_bt_ant_diversity(ah, enable);
}
+static inline bool ath9k_hw_is_aic_enabled(struct ath_hw *ah)
+{
+ if (ath9k_hw_private_ops(ah)->is_aic_enabled)
+ return ath9k_hw_private_ops(ah)->is_aic_enabled(ah);
+
+ return false;
+}
+
#endif
/* Private hardware call ops */
#include <linux/time.h>
#include <linux/bitops.h>
#include <linux/etherdevice.h>
+#include <linux/gpio.h>
#include <asm/unaligned.h>
#include "hw.h"
REGWRITE_BUFFER_FLUSH(ah);
}
+void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size)
+{
+ u32 *tmp_reg_list, *tmp_data;
+ int i;
+
+ tmp_reg_list = kmalloc(size * sizeof(u32), GFP_KERNEL);
+ if (!tmp_reg_list) {
+ dev_err(ah->dev, "%s: tmp_reg_list: alloc filed\n", __func__);
+ return;
+ }
+
+ tmp_data = kmalloc(size * sizeof(u32), GFP_KERNEL);
+ if (!tmp_data) {
+ dev_err(ah->dev, "%s tmp_data: alloc filed\n", __func__);
+ goto error_tmp_data;
+ }
+
+ for (i = 0; i < size; i++)
+ tmp_reg_list[i] = array[i][0];
+
+ REG_READ_MULTI(ah, tmp_reg_list, tmp_data, size);
+
+ for (i = 0; i < size; i++)
+ array[i][1] = tmp_data[i];
+
+ kfree(tmp_data);
+error_tmp_data:
+ kfree(tmp_reg_list);
+}
+
u32 ath9k_hw_reverse_bits(u32 val, u32 n)
{
u32 retval;
ah->config.rimt_first = 700;
}
+ if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
+ ah->config.pll_pwrsave = 7;
+
/*
* We need this for PCI devices only (Cardbus, PCI, miniPCI)
* _and_ if on non-uniprocessor systems (Multiprocessor/HT).
ah->power_mode = ATH9K_PM_UNDEFINED;
ah->htc_reset_init = true;
- ah->tpc_enabled = true;
+ ah->tpc_enabled = false;
ah->ani_function = ATH9K_ANI_ALL;
if (!AR_SREV_9300_20_OR_LATER(ah))
u32 mask = AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC;
u32 set = AR_STA_ID1_KSRCH_MODE;
+ ENABLE_REG_RMW_BUFFER(ah);
switch (opmode) {
case NL80211_IFTYPE_ADHOC:
if (!AR_SREV_9340_13(ah)) {
break;
}
REG_RMW(ah, AR_STA_ID1, set, mask);
+ REG_RMW_BUFFER_FLUSH(ah);
}
void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
if (!ath9k_hw_mci_is_enabled(ah))
REG_WRITE(ah, AR_OBS, 8);
+ ENABLE_REG_RMW_BUFFER(ah);
if (ah->config.rx_intr_mitigation) {
REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, ah->config.rimt_last);
REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, ah->config.rimt_first);
REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, 300);
REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, 750);
}
+ REG_RMW_BUFFER_FLUSH(ah);
ath9k_hw_init_bb(ah, chan);
if (AR_SREV_9271(ah))
val = ~val;
- REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
- AR_GPIO_BIT(gpio));
+ if ((1 << gpio) & AR_GPIO_OE_OUT_MASK)
+ REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
+ AR_GPIO_BIT(gpio));
+ else
+ gpio_set_value(gpio, val & 1);
}
EXPORT_SYMBOL(ath9k_hw_set_gpio);
+void ath9k_hw_request_gpio(struct ath_hw *ah, u32 gpio, const char *label)
+{
+ if (gpio >= ah->caps.num_gpio_pins)
+ return;
+
+ gpio_request_one(gpio, GPIOF_DIR_OUT | GPIOF_INIT_LOW, label);
+}
+EXPORT_SYMBOL(ath9k_hw_request_gpio);
+
void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna)
{
REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
(_ah)->reg_ops.write_flush((_ah)); \
} while (0)
+#define ENABLE_REG_RMW_BUFFER(_ah) \
+ do { \
+ if ((_ah)->reg_ops.enable_rmw_buffer) \
+ (_ah)->reg_ops.enable_rmw_buffer((_ah)); \
+ } while (0)
+
+#define REG_RMW_BUFFER_FLUSH(_ah) \
+ do { \
+ if ((_ah)->reg_ops.rmw_flush) \
+ (_ah)->reg_ops.rmw_flush((_ah)); \
+ } while (0)
+
#define PR_EEP(_s, _val) \
do { \
len += scnprintf(buf + len, size - len, "%20s : %10d\n",\
#define REG_WRITE_ARRAY(iniarray, column, regWr) \
ath9k_hw_write_array(ah, iniarray, column, &(regWr))
+#define REG_READ_ARRAY(ah, array, size) \
+ ath9k_hw_read_array(ah, array, size)
#define AR_GPIO_OUTPUT_MUX_AS_OUTPUT 0
#define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1
HW_MAC_HANG = BIT(5),
};
+#define AR_PCIE_PLL_PWRSAVE_CONTROL BIT(0)
+#define AR_PCIE_PLL_PWRSAVE_ON_D3 BIT(1)
+#define AR_PCIE_PLL_PWRSAVE_ON_D0 BIT(2)
+#define AR_PCIE_CDR_PWRSAVE_ON_D3 BIT(3)
+#define AR_PCIE_CDR_PWRSAVE_ON_D0 BIT(4)
+
struct ath9k_ops_config {
int dma_beacon_response_time;
int sw_beacon_response_time;
u32 ant_ctrl_comm2g_switch_enable;
bool xatten_margin_cfg;
bool alt_mingainidx;
- bool no_pll_pwrsave;
+ u8 pll_pwrsave;
bool tx_gain_buffalo;
bool led_active_high;
};
/* ANI */
void (*ani_cache_ini_regs)(struct ath_hw *ah);
+
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+ bool (*is_aic_enabled)(struct ath_hw *ah);
+#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
};
/**
void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
u32 ah_signal_type);
void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val);
+void ath9k_hw_request_gpio(struct ath_hw *ah, u32 gpio, const char *label);
void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna);
/* General Operation */
bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout);
void ath9k_hw_write_array(struct ath_hw *ah, const struct ar5416IniArray *array,
int column, unsigned int *writecnt);
+void ath9k_hw_read_array(struct ath_hw *ah, u32 array[][2], int size);
u32 ath9k_hw_reverse_bits(u32 val, u32 n);
u16 ath9k_hw_computetxtime(struct ath_hw *ah,
u8 phy, int kbps,
void ath9k_hw_setslottime(struct ath_hw *ah, u32 us);
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+void ar9003_hw_attach_aic_ops(struct ath_hw *ah);
static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah)
{
return ah->btcoex_hw.enabled;
return ah->btcoex_hw.scheme;
}
#else
+static inline void ar9003_hw_attach_aic_ops(struct ath_hw *ah)
+{
+}
static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah)
{
return false;
return val;
}
+static void ath9k_multi_ioread32(void *hw_priv, u32 *addr,
+ u32 *val, u16 count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ val[i] = ath9k_ioread32(hw_priv, addr[i]);
+}
+
+
static unsigned int __ath9k_reg_rmw(struct ath_softc *sc, u32 reg_offset,
u32 set, u32 clr)
{
ath_info(common, "Enable WAR for ASPM D3/L1\n");
}
+ /*
+ * The default value of pll_pwrsave is 1.
+ * For certain AR9485 cards, it is set to 0.
+ * For AR9462, AR9565 it's set to 7.
+ */
+ ah->config.pll_pwrsave = 1;
+
if (sc->driver_data & ATH9K_PCI_NO_PLL_PWRSAVE) {
- ah->config.no_pll_pwrsave = true;
+ ah->config.pll_pwrsave = 0;
ath_info(common, "Disable PLL PowerSave\n");
}
ah->hw = sc->hw;
ah->hw_version.devid = devid;
ah->reg_ops.read = ath9k_ioread32;
+ ah->reg_ops.multi_read = ath9k_multi_ioread32;
ah->reg_ops.write = ath9k_iowrite32;
ah->reg_ops.rmw = ath9k_reg_rmw;
pCap = &ah->caps;
.num_different_channels = 1,
.beacon_int_infra_match = true,
.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
- BIT(NL80211_CHAN_WIDTH_20),
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40),
}
#endif
};
#define AR_SREV_9550(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9550))
+#define AR_SREV_9550_OR_LATER(_ah) \
+ (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9550))
#define AR_SREV_9580(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \
#define AR_GPIO_OE_OUT (AR_SREV_9340(ah) ? 0x4030 : \
(AR_SREV_9300_20_OR_LATER(ah) ? 0x4050 : 0x404c))
+#define AR_GPIO_OE_OUT_MASK (AR_SREV_9550_OR_LATER(ah) ? \
+ 0x0000000F : 0xFFFFFFFF)
#define AR_GPIO_OE_OUT_DRV 0x3
#define AR_GPIO_OE_OUT_DRV_NO 0x0
#define AR_GPIO_OE_OUT_DRV_LOW 0x1
--- /dev/null
+/*
+ * Copyright (c) 2015 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef REG_AIC_H
+#define REG_AIC_H
+
+#define AR_SM_BASE 0xa200
+#define AR_SM1_BASE 0xb200
+#define AR_AGC_BASE 0x9e00
+
+#define AR_PHY_AIC_CTRL_0_B0 (AR_SM_BASE + 0x4b0)
+#define AR_PHY_AIC_CTRL_1_B0 (AR_SM_BASE + 0x4b4)
+#define AR_PHY_AIC_CTRL_2_B0 (AR_SM_BASE + 0x4b8)
+#define AR_PHY_AIC_CTRL_3_B0 (AR_SM_BASE + 0x4bc)
+#define AR_PHY_AIC_CTRL_4_B0 (AR_SM_BASE + 0x4c0)
+
+#define AR_PHY_AIC_STAT_0_B0 (AR_SM_BASE + 0x4c4)
+#define AR_PHY_AIC_STAT_1_B0 (AR_SM_BASE + 0x4c8)
+#define AR_PHY_AIC_STAT_2_B0 (AR_SM_BASE + 0x4cc)
+
+#define AR_PHY_AIC_CTRL_0_B1 (AR_SM1_BASE + 0x4b0)
+#define AR_PHY_AIC_CTRL_1_B1 (AR_SM1_BASE + 0x4b4)
+#define AR_PHY_AIC_CTRL_4_B1 (AR_SM1_BASE + 0x4c0)
+
+#define AR_PHY_AIC_STAT_0_B1 (AR_SM1_BASE + 0x4c4)
+#define AR_PHY_AIC_STAT_1_B1 (AR_SM1_BASE + 0x4c8)
+#define AR_PHY_AIC_STAT_2_B1 (AR_SM1_BASE + 0x4cc)
+
+#define AR_PHY_AIC_SRAM_ADDR_B0 (AR_SM_BASE + 0x5f0)
+#define AR_PHY_AIC_SRAM_DATA_B0 (AR_SM_BASE + 0x5f4)
+
+#define AR_PHY_AIC_SRAM_ADDR_B1 (AR_SM1_BASE + 0x5f0)
+#define AR_PHY_AIC_SRAM_DATA_B1 (AR_SM1_BASE + 0x5f4)
+
+#define AR_PHY_BT_COEX_4 (AR_AGC_BASE + 0x60)
+#define AR_PHY_BT_COEX_5 (AR_AGC_BASE + 0x64)
+
+/* AIC fields */
+#define AR_PHY_AIC_MON_ENABLE 0x80000000
+#define AR_PHY_AIC_MON_ENABLE_S 31
+#define AR_PHY_AIC_CAL_MAX_HOP_COUNT 0x7F000000
+#define AR_PHY_AIC_CAL_MAX_HOP_COUNT_S 24
+#define AR_PHY_AIC_CAL_MIN_VALID_COUNT 0x00FE0000
+#define AR_PHY_AIC_CAL_MIN_VALID_COUNT_S 17
+#define AR_PHY_AIC_F_WLAN 0x0001FC00
+#define AR_PHY_AIC_F_WLAN_S 10
+#define AR_PHY_AIC_CAL_CH_VALID_RESET 0x00000200
+#define AR_PHY_AIC_CAL_CH_VALID_RESET_S 9
+#define AR_PHY_AIC_CAL_ENABLE 0x00000100
+#define AR_PHY_AIC_CAL_ENABLE_S 8
+#define AR_PHY_AIC_BTTX_PWR_THR 0x000000FE
+#define AR_PHY_AIC_BTTX_PWR_THR_S 1
+#define AR_PHY_AIC_ENABLE 0x00000001
+#define AR_PHY_AIC_ENABLE_S 0
+#define AR_PHY_AIC_CAL_BT_REF_DELAY 0x00F00000
+#define AR_PHY_AIC_CAL_BT_REF_DELAY_S 20
+#define AR_PHY_AIC_BT_IDLE_CFG 0x00080000
+#define AR_PHY_AIC_BT_IDLE_CFG_S 19
+#define AR_PHY_AIC_STDBY_COND 0x00060000
+#define AR_PHY_AIC_STDBY_COND_S 17
+#define AR_PHY_AIC_STDBY_ROT_ATT_DB 0x0001F800
+#define AR_PHY_AIC_STDBY_ROT_ATT_DB_S 11
+#define AR_PHY_AIC_STDBY_COM_ATT_DB 0x00000700
+#define AR_PHY_AIC_STDBY_COM_ATT_DB_S 8
+#define AR_PHY_AIC_RSSI_MAX 0x000000F0
+#define AR_PHY_AIC_RSSI_MAX_S 4
+#define AR_PHY_AIC_RSSI_MIN 0x0000000F
+#define AR_PHY_AIC_RSSI_MIN_S 0
+#define AR_PHY_AIC_RADIO_DELAY 0x7F000000
+#define AR_PHY_AIC_RADIO_DELAY_S 24
+#define AR_PHY_AIC_CAL_STEP_SIZE_CORR 0x00F00000
+#define AR_PHY_AIC_CAL_STEP_SIZE_CORR_S 20
+#define AR_PHY_AIC_CAL_ROT_IDX_CORR 0x000F8000
+#define AR_PHY_AIC_CAL_ROT_IDX_CORR_S 15
+#define AR_PHY_AIC_CAL_CONV_CHECK_FACTOR 0x00006000
+#define AR_PHY_AIC_CAL_CONV_CHECK_FACTOR_S 13
+#define AR_PHY_AIC_ROT_IDX_COUNT_MAX 0x00001C00
+#define AR_PHY_AIC_ROT_IDX_COUNT_MAX_S 10
+#define AR_PHY_AIC_CAL_SYNTH_TOGGLE 0x00000200
+#define AR_PHY_AIC_CAL_SYNTH_TOGGLE_S 9
+#define AR_PHY_AIC_CAL_SYNTH_AFTER_BTRX 0x00000100
+#define AR_PHY_AIC_CAL_SYNTH_AFTER_BTRX_S 8
+#define AR_PHY_AIC_CAL_SYNTH_SETTLING 0x000000FF
+#define AR_PHY_AIC_CAL_SYNTH_SETTLING_S 0
+#define AR_PHY_AIC_MON_MAX_HOP_COUNT 0x07F00000
+#define AR_PHY_AIC_MON_MAX_HOP_COUNT_S 20
+#define AR_PHY_AIC_MON_MIN_STALE_COUNT 0x000FE000
+#define AR_PHY_AIC_MON_MIN_STALE_COUNT_S 13
+#define AR_PHY_AIC_MON_PWR_EST_LONG 0x00001000
+#define AR_PHY_AIC_MON_PWR_EST_LONG_S 12
+#define AR_PHY_AIC_MON_PD_TALLY_SCALING 0x00000C00
+#define AR_PHY_AIC_MON_PD_TALLY_SCALING_S 10
+#define AR_PHY_AIC_MON_PERF_THR 0x000003E0
+#define AR_PHY_AIC_MON_PERF_THR_S 5
+#define AR_PHY_AIC_CAL_TARGET_MAG_SETTING 0x00000018
+#define AR_PHY_AIC_CAL_TARGET_MAG_SETTING_S 3
+#define AR_PHY_AIC_CAL_PERF_CHECK_FACTOR 0x00000006
+#define AR_PHY_AIC_CAL_PERF_CHECK_FACTOR_S 1
+#define AR_PHY_AIC_CAL_PWR_EST_LONG 0x00000001
+#define AR_PHY_AIC_CAL_PWR_EST_LONG_S 0
+#define AR_PHY_AIC_MON_DONE 0x80000000
+#define AR_PHY_AIC_MON_DONE_S 31
+#define AR_PHY_AIC_MON_ACTIVE 0x40000000
+#define AR_PHY_AIC_MON_ACTIVE_S 30
+#define AR_PHY_AIC_MEAS_COUNT 0x3F000000
+#define AR_PHY_AIC_MEAS_COUNT_S 24
+#define AR_PHY_AIC_CAL_ANT_ISO_EST 0x00FC0000
+#define AR_PHY_AIC_CAL_ANT_ISO_EST_S 18
+#define AR_PHY_AIC_CAL_HOP_COUNT 0x0003F800
+#define AR_PHY_AIC_CAL_HOP_COUNT_S 11
+#define AR_PHY_AIC_CAL_VALID_COUNT 0x000007F0
+#define AR_PHY_AIC_CAL_VALID_COUNT_S 4
+#define AR_PHY_AIC_CAL_BT_TOO_WEAK_ERR 0x00000008
+#define AR_PHY_AIC_CAL_BT_TOO_WEAK_ERR_S 3
+#define AR_PHY_AIC_CAL_BT_TOO_STRONG_ERR 0x00000004
+#define AR_PHY_AIC_CAL_BT_TOO_STRONG_ERR_S 2
+#define AR_PHY_AIC_CAL_DONE 0x00000002
+#define AR_PHY_AIC_CAL_DONE_S 1
+#define AR_PHY_AIC_CAL_ACTIVE 0x00000001
+#define AR_PHY_AIC_CAL_ACTIVE_S 0
+
+#define AR_PHY_AIC_MEAS_MAG_MIN 0xFFC00000
+#define AR_PHY_AIC_MEAS_MAG_MIN_S 22
+#define AR_PHY_AIC_MON_STALE_COUNT 0x003F8000
+#define AR_PHY_AIC_MON_STALE_COUNT_S 15
+#define AR_PHY_AIC_MON_HOP_COUNT 0x00007F00
+#define AR_PHY_AIC_MON_HOP_COUNT_S 8
+#define AR_PHY_AIC_CAL_AIC_SM 0x000000F8
+#define AR_PHY_AIC_CAL_AIC_SM_S 3
+#define AR_PHY_AIC_SM 0x00000007
+#define AR_PHY_AIC_SM_S 0
+#define AR_PHY_AIC_SRAM_VALID 0x00000001
+#define AR_PHY_AIC_SRAM_VALID_S 0
+#define AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB 0x0000007E
+#define AR_PHY_AIC_SRAM_ROT_QUAD_ATT_DB_S 1
+#define AR_PHY_AIC_SRAM_VGA_QUAD_SIGN 0x00000080
+#define AR_PHY_AIC_SRAM_VGA_QUAD_SIGN_S 7
+#define AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB 0x00003F00
+#define AR_PHY_AIC_SRAM_ROT_DIR_ATT_DB_S 8
+#define AR_PHY_AIC_SRAM_VGA_DIR_SIGN 0x00004000
+#define AR_PHY_AIC_SRAM_VGA_DIR_SIGN_S 14
+#define AR_PHY_AIC_SRAM_COM_ATT_6DB 0x00038000
+#define AR_PHY_AIC_SRAM_COM_ATT_6DB_S 15
+#define AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO 0x0000E000
+#define AR_PHY_AIC_CAL_ROT_ATT_DB_EST_ISO_S 13
+#define AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO 0x00001E00
+#define AR_PHY_AIC_CAL_COM_ATT_DB_EST_ISO_S 9
+#define AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING 0x000001F8
+#define AR_PHY_AIC_CAL_ISO_EST_INIT_SETTING_S 3
+#define AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF 0x00000006
+#define AR_PHY_AIC_CAL_COM_ATT_DB_BACKOFF_S 1
+#define AR_PHY_AIC_CAL_COM_ATT_DB_FIXED 0x00000001
+#define AR_PHY_AIC_CAL_COM_ATT_DB_FIXED_S 0
+
+#endif /* REG_AIC_H */
return "WMI_REG_READ_CMDID";
case WMI_REG_WRITE_CMDID:
return "WMI_REG_WRITE_CMDID";
+ case WMI_REG_RMW_CMDID:
+ return "WMI_REG_RMW_CMDID";
case WMI_RC_STATE_CHANGE_CMDID:
return "WMI_RC_STATE_CHANGE_CMDID";
case WMI_RC_RATE_UPDATE_CMDID:
spin_lock_init(&wmi->event_lock);
mutex_init(&wmi->op_mutex);
mutex_init(&wmi->multi_write_mutex);
+ mutex_init(&wmi->multi_rmw_mutex);
init_completion(&wmi->cmd_wait);
INIT_LIST_HEAD(&wmi->pending_tx_events);
tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,
/* Check if there has been a timeout. */
spin_lock(&wmi->wmi_lock);
- if (cmd_id != wmi->last_cmd_id) {
+ if (be16_to_cpu(hdr->seq_no) != wmi->last_seq_id) {
spin_unlock(&wmi->wmi_lock);
goto free_skb;
}
enum wmi_cmd_id cmd, u16 len)
{
struct wmi_cmd_hdr *hdr;
+ unsigned long flags;
hdr = (struct wmi_cmd_hdr *) skb_push(skb, sizeof(struct wmi_cmd_hdr));
hdr->command_id = cpu_to_be16(cmd);
hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id);
+ spin_lock_irqsave(&wmi->wmi_lock, flags);
+ wmi->last_seq_id = wmi->tx_seq_id;
+ spin_unlock_irqrestore(&wmi->wmi_lock, flags);
+
return htc_send_epid(wmi->htc, skb, wmi->ctrl_epid);
}
struct sk_buff *skb;
u8 *data;
int time_left, ret = 0;
- unsigned long flags;
if (ah->ah_flags & AH_UNPLUGGED)
return 0;
wmi->cmd_rsp_buf = rsp_buf;
wmi->cmd_rsp_len = rsp_len;
- spin_lock_irqsave(&wmi->wmi_lock, flags);
- wmi->last_cmd_id = cmd_id;
- spin_unlock_irqrestore(&wmi->wmi_lock, flags);
-
ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len);
if (ret)
goto out;
WMI_TX_STATS_CMDID,
WMI_RX_STATS_CMDID,
WMI_BITRATE_MASK_CMDID,
+ WMI_REG_RMW_CMDID,
};
enum wmi_event_id {
};
#define MAX_CMD_NUMBER 62
+#define MAX_RMW_CMD_NUMBER 15
struct register_write {
__be32 reg;
__be32 val;
};
+struct register_rmw {
+ __be32 reg;
+ __be32 set;
+ __be32 clr;
+} __packed;
+
struct ath9k_htc_tx_event {
int count;
struct __wmi_event_txstatus txs;
enum htc_endpoint_id ctrl_epid;
struct mutex op_mutex;
struct completion cmd_wait;
- enum wmi_cmd_id last_cmd_id;
+ u16 last_seq_id;
struct sk_buff_head wmi_event_queue;
struct tasklet_struct wmi_event_tasklet;
u16 tx_seq_id;
spinlock_t wmi_lock;
+ /* multi write section */
atomic_t mwrite_cnt;
struct register_write multi_write[MAX_CMD_NUMBER];
u32 multi_write_idx;
struct mutex multi_write_mutex;
+
+ /* multi rmw section */
+ atomic_t m_rmw_cnt;
+ struct register_rmw multi_rmw[MAX_RMW_CMD_NUMBER];
+ u32 multi_rmw_idx;
+ struct mutex multi_rmw_mutex;
+
};
struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv);
"count=%d, count_false=%d\n",
event->freq, pd->rs->type_id,
ps->pri, ps->count, ps->count_falses);
- channel_detector_reset(dpd, cd);
+ pd->reset(pd, dpd->last_pulse_ts);
return true;
}
}
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/etherdevice.h>
#include "wil6210.h"
#include "wmi.h"
if (cid < 0)
return -ENOENT;
- memcpy(mac, wil->sta[cid].addr, ETH_ALEN);
+ ether_addr_copy(mac, wil->sta[cid].addr);
wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
rc = wil_cid_fill_sinfo(wil, cid, sinfo);
}
conn.channel = ch - 1;
- memcpy(conn.bssid, bss->bssid, ETH_ALEN);
- memcpy(conn.dst_mac, bss->bssid, ETH_ALEN);
+ ether_addr_copy(conn.bssid, bss->bssid);
+ ether_addr_copy(conn.dst_mac, bss->bssid);
set_bit(wil_status_fwconnecting, wil->status);
rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype,
channel->hw_value);
if (rc)
- netif_carrier_off(ndev);
+ goto err_pcp_start;
+ rc = wil_bcast_init(wil);
+ if (rc)
+ goto err_bcast;
+
+ goto out; /* success */
+err_bcast:
+ wmi_pcp_stop(wil);
+err_pcp_start:
+ netif_carrier_off(ndev);
out:
mutex_unlock(&wil->mutex);
return rc;
return 0;
}
+static int wil_cfg80211_change_bss(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct bss_parameters *params)
+{
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+ if (params->ap_isolate >= 0) {
+ wil_dbg_misc(wil, "%s(ap_isolate %d => %d)\n", __func__,
+ wil->ap_isolate, params->ap_isolate);
+ wil->ap_isolate = params->ap_isolate;
+ }
+
+ return 0;
+}
+
static struct cfg80211_ops wil_cfg80211_ops = {
.scan = wil_cfg80211_scan,
.connect = wil_cfg80211_connect,
.stop_ap = wil_cfg80211_stop_ap,
.del_station = wil_cfg80211_del_station,
.probe_client = wil_cfg80211_probe_client,
+ .change_bss = wil_cfg80211_change_bss,
};
static void wil_wiphy_init(struct wiphy *wiphy)
snprintf(name, sizeof(name), "tx_%2d", i);
- seq_printf(s,
- "\n%pM CID %d TID %d BACK([%d] %d TU A%s) [%3d|%3d] idle %s\n",
- wil->sta[cid].addr, cid, tid,
- txdata->agg_wsize, txdata->agg_timeout,
- txdata->agg_amsdu ? "+" : "-",
- used, avail, sidle);
+ if (cid < WIL6210_MAX_CID)
+ seq_printf(s,
+ "\n%pM CID %d TID %d BACK([%u] %u TU A%s) [%3d|%3d] idle %s\n",
+ wil->sta[cid].addr, cid, tid,
+ txdata->agg_wsize,
+ txdata->agg_timeout,
+ txdata->agg_amsdu ? "+" : "-",
+ used, avail, sidle);
+ else
+ seq_printf(s,
+ "\nBroadcast [%3d|%3d] idle %s\n",
+ used, avail, sidle);
wil_print_vring(s, wil, name, vring, '_', 'H');
}
WIL_FIELD(fw_version, S_IRUGO, doff_u32),
WIL_FIELD(hw_version, S_IRUGO, doff_x32),
WIL_FIELD(recovery_count, S_IRUGO, doff_u32),
+ WIL_FIELD(ap_isolate, S_IRUGO, doff_u32),
{},
};
static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
+static uint bcast_ring_order = WIL_BCAST_RING_SIZE_ORDER_DEFAULT;
static int ring_order_set(const char *val, const struct kernel_param *kp)
{
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
+ wil_bcast_fini(wil);
netif_tx_stop_all_queues(ndev);
netif_carrier_off(ndev);
return -EINVAL;
}
+int wil_bcast_init(struct wil6210_priv *wil)
+{
+ int ri = wil->bcast_vring, rc;
+
+ if ((ri >= 0) && wil->vring_tx[ri].va)
+ return 0;
+
+ ri = wil_find_free_vring(wil);
+ if (ri < 0)
+ return ri;
+
+ rc = wil_vring_init_bcast(wil, ri, 1 << bcast_ring_order);
+ if (rc == 0)
+ wil->bcast_vring = ri;
+
+ return rc;
+}
+
+void wil_bcast_fini(struct wil6210_priv *wil)
+{
+ int ri = wil->bcast_vring;
+
+ if (ri < 0)
+ return;
+
+ wil->bcast_vring = -1;
+ wil_vring_fini_tx(wil, ri);
+}
+
static void wil_connect_worker(struct work_struct *work)
{
int rc;
init_completion(&wil->wmi_call);
wil->pending_connect_cid = -1;
+ wil->bcast_vring = -1;
setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
cancel_work_sync(&wil->disconnect_worker);
wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
+ wil_bcast_fini(wil);
/* prevent NAPI from being scheduled */
bitmap_zero(wil->status, wil_status_last);
/* init after reset */
wil->pending_connect_cid = -1;
+ wil->ap_isolate = 0;
reinit_completion(&wil->wmi_ready);
reinit_completion(&wil->wmi_call);
/* we just started MAC, wait for FW ready */
rc = wil_wait_for_fw_ready(wil);
+ if (rc == 0) /* check FW is responsive */
+ rc = wmi_echo(wil);
}
return rc;
wil_rx_handle(wil, "a);
done = budget - quota;
- if (done <= 1) { /* burst ends - only one packet processed */
+ if (done < budget) {
napi_complete(napi);
wil6210_unmask_irq_rx(wil);
wil_dbg_txrx(wil, "NAPI RX complete\n");
tx_done += wil_tx_complete(wil, i);
}
- if (tx_done <= 1) { /* burst ends - only one packet processed */
+ if (tx_done < budget) {
napi_complete(napi);
wil6210_unmask_irq_tx(wil);
wil_dbg_txrx(wil, "NAPI TX complete\n");
wil6210_debugfs_init(wil);
- /* check FW is alive */
- wmi_echo(wil);
return 0;
MODULE_PARM_DESC(rtap_include_phy_info,
" Include PHY info in the radiotap header, default - no");
+bool rx_align_2;
+module_param(rx_align_2, bool, S_IRUGO);
+MODULE_PARM_DESC(rx_align_2, " align Rx buffers on 4*n+2, default - no");
+
+static inline uint wil_rx_snaplen(void)
+{
+ return rx_align_2 ? 6 : 0;
+}
+
static inline int wil_vring_is_empty(struct vring *vring)
{
return vring->swhead == vring->swtail;
u32 i, int headroom)
{
struct device *dev = wil_to_dev(wil);
- unsigned int sz = mtu_max + ETH_HLEN;
+ unsigned int sz = mtu_max + ETH_HLEN + wil_rx_snaplen();
struct vring_rx_desc dd, *d = ⅆ
volatile struct vring_rx_desc *_d = &vring->va[i].rx;
dma_addr_t pa;
struct vring_rx_desc *d;
struct sk_buff *skb;
dma_addr_t pa;
- unsigned int sz = mtu_max + ETH_HLEN;
+ unsigned int snaplen = wil_rx_snaplen();
+ unsigned int sz = mtu_max + ETH_HLEN + snaplen;
u16 dmalen;
u8 ftype;
int cid;
+ int i = (int)vring->swhead;
struct wil_net_stats *stats;
BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb));
if (unlikely(wil_vring_is_empty(vring)))
return NULL;
- _d = &vring->va[vring->swhead].rx;
+ _d = &vring->va[i].rx;
if (unlikely(!(_d->dma.status & RX_DMA_STATUS_DU))) {
/* it is not error, we just reached end of Rx done area */
return NULL;
}
- skb = vring->ctx[vring->swhead].skb;
+ skb = vring->ctx[i].skb;
+ vring->ctx[i].skb = NULL;
+ wil_vring_advance_head(vring, 1);
+ if (!skb) {
+ wil_err(wil, "No Rx skb at [%d]\n", i);
+ return NULL;
+ }
d = wil_skb_rxdesc(skb);
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
- vring->ctx[vring->swhead].skb = NULL;
- wil_vring_advance_head(vring, 1);
dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
dmalen = le16_to_cpu(d->dma.length);
- trace_wil6210_rx(vring->swhead, d);
- wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen);
+ trace_wil6210_rx(i, d);
+ wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", i, dmalen);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
return NULL;
}
- if (unlikely(skb->len < ETH_HLEN)) {
+ if (unlikely(skb->len < ETH_HLEN + snaplen)) {
wil_err(wil, "Short frame, len = %d\n", skb->len);
/* TODO: process it (i.e. BAR) */
kfree_skb(skb);
*/
}
+ if (snaplen) {
+ /* Packet layout
+ * +-------+-------+---------+------------+------+
+ * | SA(6) | DA(6) | SNAP(6) | ETHTYPE(2) | DATA |
+ * +-------+-------+---------+------------+------+
+ * Need to remove SNAP, shifting SA and DA forward
+ */
+ memmove(skb->data + snaplen, skb->data, 2 * ETH_ALEN);
+ skb_pull(skb, snaplen);
+ }
+
return skb;
}
*/
void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
{
- gro_result_t rc;
+ gro_result_t rc = GRO_NORMAL;
struct wil6210_priv *wil = ndev_to_wil(ndev);
+ struct wireless_dev *wdev = wil_to_wdev(wil);
unsigned int len = skb->len;
struct vring_rx_desc *d = wil_skb_rxdesc(skb);
- int cid = wil_rxdesc_cid(d);
+ int cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */
+ struct ethhdr *eth = (void *)skb->data;
+ /* here looking for DA, not A1, thus Rxdesc's 'mcast' indication
+ * is not suitable, need to look at data
+ */
+ int mcast = is_multicast_ether_addr(eth->h_dest);
struct wil_net_stats *stats = &wil->sta[cid].stats;
+ struct sk_buff *xmit_skb = NULL;
+ static const char * const gro_res_str[] = {
+ [GRO_MERGED] = "GRO_MERGED",
+ [GRO_MERGED_FREE] = "GRO_MERGED_FREE",
+ [GRO_HELD] = "GRO_HELD",
+ [GRO_NORMAL] = "GRO_NORMAL",
+ [GRO_DROP] = "GRO_DROP",
+ };
skb_orphan(skb);
- rc = napi_gro_receive(&wil->napi_rx, skb);
+ if (wdev->iftype == NL80211_IFTYPE_AP && !wil->ap_isolate) {
+ if (mcast) {
+ /* send multicast frames both to higher layers in
+ * local net stack and back to the wireless medium
+ */
+ xmit_skb = skb_copy(skb, GFP_ATOMIC);
+ } else {
+ int xmit_cid = wil_find_cid(wil, eth->h_dest);
+
+ if (xmit_cid >= 0) {
+ /* The destination station is associated to
+ * this AP (in this VLAN), so send the frame
+ * directly to it and do not pass it to local
+ * net stack.
+ */
+ xmit_skb = skb;
+ skb = NULL;
+ }
+ }
+ }
+ if (xmit_skb) {
+ /* Send to wireless media and increase priority by 256 to
+ * keep the received priority instead of reclassifying
+ * the frame (see cfg80211_classify8021d).
+ */
+ xmit_skb->dev = ndev;
+ xmit_skb->priority += 256;
+ xmit_skb->protocol = htons(ETH_P_802_3);
+ skb_reset_network_header(xmit_skb);
+ skb_reset_mac_header(xmit_skb);
+ wil_dbg_txrx(wil, "Rx -> Tx %d bytes\n", len);
+ dev_queue_xmit(xmit_skb);
+ }
+ if (skb) { /* deliver to local stack */
+
+ skb->protocol = eth_type_trans(skb, ndev);
+ rc = napi_gro_receive(&wil->napi_rx, skb);
+ wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n",
+ len, gro_res_str[rc]);
+ }
+ /* statistics. rc set to GRO_NORMAL for AP bridging */
if (unlikely(rc == GRO_DROP)) {
ndev->stats.rx_dropped++;
stats->rx_dropped++;
stats->rx_packets++;
ndev->stats.rx_bytes += len;
stats->rx_bytes += len;
- }
- {
- static const char * const gro_res_str[] = {
- [GRO_MERGED] = "GRO_MERGED",
- [GRO_MERGED_FREE] = "GRO_MERGED_FREE",
- [GRO_HELD] = "GRO_HELD",
- [GRO_NORMAL] = "GRO_NORMAL",
- [GRO_DROP] = "GRO_DROP",
- };
- wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n",
- len, gro_res_str[rc]);
+ if (mcast)
+ ndev->stats.multicast++;
}
}
skb->protocol = htons(ETH_P_802_2);
wil_netif_rx_any(skb, ndev);
} else {
- skb->protocol = eth_type_trans(skb, ndev);
wil_rx_reorder(wil, skb);
}
}
return rc;
}
+int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
+{
+ int rc;
+ struct wmi_bcast_vring_cfg_cmd cmd = {
+ .action = cpu_to_le32(WMI_VRING_CMD_ADD),
+ .vring_cfg = {
+ .tx_sw_ring = {
+ .max_mpdu_size =
+ cpu_to_le16(wil_mtu2macbuf(mtu_max)),
+ .ring_size = cpu_to_le16(size),
+ },
+ .ringid = id,
+ .encap_trans_type = WMI_VRING_ENC_TYPE_802_3,
+ },
+ };
+ struct {
+ struct wil6210_mbox_hdr_wmi wmi;
+ struct wmi_vring_cfg_done_event cmd;
+ } __packed reply;
+ struct vring *vring = &wil->vring_tx[id];
+ struct vring_tx_data *txdata = &wil->vring_tx_data[id];
+
+ wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__,
+ cmd.vring_cfg.tx_sw_ring.max_mpdu_size);
+
+ if (vring->va) {
+ wil_err(wil, "Tx ring [%d] already allocated\n", id);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ memset(txdata, 0, sizeof(*txdata));
+ spin_lock_init(&txdata->lock);
+ vring->size = size;
+ rc = wil_vring_alloc(wil, vring);
+ if (rc)
+ goto out;
+
+ wil->vring2cid_tid[id][0] = WIL6210_MAX_CID; /* CID */
+ wil->vring2cid_tid[id][1] = 0; /* TID */
+
+ cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
+
+ rc = wmi_call(wil, WMI_BCAST_VRING_CFG_CMDID, &cmd, sizeof(cmd),
+ WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100);
+ if (rc)
+ goto out_free;
+
+ if (reply.cmd.status != WMI_FW_STATUS_SUCCESS) {
+ wil_err(wil, "Tx config failed, status 0x%02x\n",
+ reply.cmd.status);
+ rc = -EINVAL;
+ goto out_free;
+ }
+ vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+
+ txdata->enabled = 1;
+
+ return 0;
+ out_free:
+ wil_vring_free(wil, vring, 1);
+ out:
+
+ return rc;
+}
+
void wil_vring_fini_tx(struct wil6210_priv *wil, int id)
{
struct vring *vring = &wil->vring_tx[id];
memset(txdata, 0, sizeof(*txdata));
}
-static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
+static struct vring *wil_find_tx_ucast(struct wil6210_priv *wil,
struct sk_buff *skb)
{
int i;
return NULL;
}
-static void wil_set_da_for_vring(struct wil6210_priv *wil,
- struct sk_buff *skb, int vring_index)
-{
- struct ethhdr *eth = (void *)skb->data;
- int cid = wil->vring2cid_tid[vring_index][0];
-
- memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN);
-}
-
static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb);
continue;
cid = wil->vring2cid_tid[i][0];
+ if (cid >= WIL6210_MAX_CID) /* skip BCAST */
+ continue;
+
if (!wil->sta[cid].data_port_open &&
(skb->protocol != cpu_to_be16(ETH_P_PAE)))
break;
return NULL;
}
-/*
- * Find 1-st vring and return it; set dest address for this vring in skb
- * duplicate skb and send it to other active vrings
+/* Use one of 2 strategies:
+ *
+ * 1. New (real broadcast):
+ * use dedicated broadcast vring
+ * 2. Old (pseudo-DMS):
+ * Find 1-st vring and return it;
+ * duplicate skb and send it to other active vrings;
+ * in all cases override dest address to unicast peer's address
+ * Use old strategy when new is not supported yet:
+ * - for PBSS
+ * - for secure link
*/
-static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
- struct sk_buff *skb)
+static struct vring *wil_find_tx_bcast_1(struct wil6210_priv *wil,
+ struct sk_buff *skb)
+{
+ struct vring *v;
+ int i = wil->bcast_vring;
+
+ if (i < 0)
+ return NULL;
+ v = &wil->vring_tx[i];
+ if (!v->va)
+ return NULL;
+
+ return v;
+}
+
+static void wil_set_da_for_vring(struct wil6210_priv *wil,
+ struct sk_buff *skb, int vring_index)
+{
+ struct ethhdr *eth = (void *)skb->data;
+ int cid = wil->vring2cid_tid[vring_index][0];
+
+ ether_addr_copy(eth->h_dest, wil->sta[cid].addr);
+}
+
+static struct vring *wil_find_tx_bcast_2(struct wil6210_priv *wil,
+ struct sk_buff *skb)
{
struct vring *v, *v2;
struct sk_buff *skb2;
int i;
u8 cid;
+ struct ethhdr *eth = (void *)skb->data;
+ char *src = eth->h_source;
/* find 1-st vring eligible for data */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
continue;
cid = wil->vring2cid_tid[i][0];
+ if (cid >= WIL6210_MAX_CID) /* skip BCAST */
+ continue;
if (!wil->sta[cid].data_port_open)
continue;
+ /* don't Tx back to source when re-routing Rx->Tx at the AP */
+ if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN))
+ continue;
+
goto found;
}
if (!v2->va)
continue;
cid = wil->vring2cid_tid[i][0];
+ if (cid >= WIL6210_MAX_CID) /* skip BCAST */
+ continue;
if (!wil->sta[cid].data_port_open)
continue;
+ if (0 == memcmp(wil->sta[cid].addr, src, ETH_ALEN))
+ continue;
+
skb2 = skb_copy(skb, GFP_ATOMIC);
if (skb2) {
wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
return v;
}
+static struct vring *wil_find_tx_bcast(struct wil6210_priv *wil,
+ struct sk_buff *skb)
+{
+ struct wireless_dev *wdev = wil->wdev;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP)
+ return wil_find_tx_bcast_2(wil, skb);
+
+ if (wil->privacy)
+ return wil_find_tx_bcast_2(wil, skb);
+
+ return wil_find_tx_bcast_1(wil, skb);
+}
+
static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
int vring_index)
{
uint i = swhead;
dma_addr_t pa;
int used;
+ bool mcast = (vring_index == wil->bcast_vring);
+ uint len = skb_headlen(skb);
wil_dbg_txrx(wil, "%s()\n", __func__);
return -EINVAL;
vring->ctx[i].mapped_as = wil_mapped_as_single;
/* 1-st segment */
- wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
+ wil_tx_desc_map(d, pa, len, vring_index);
+ if (unlikely(mcast)) {
+ d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_0_MCS_EN_POS); /* MCS 0 */
+ if (unlikely(len > WIL_BCAST_MCS0_LIMIT)) {
+ /* set MCS 1 */
+ d->mac.d[0] |= (1 << MAC_CFG_DESC_TX_0_MCS_INDEX_POS);
+ /* packet mode 2 */
+ d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS) |
+ (2 << MAC_CFG_DESC_TX_1_PKT_MODE_POS);
+ }
+ }
/* Process TCP/UDP checksum offloading */
if (unlikely(wil_tx_desc_offload_cksum_set(wil, d, skb))) {
wil_err(wil, "Tx[%2d] Failed to set cksum, drop packet\n",
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
struct ethhdr *eth = (void *)skb->data;
+ bool bcast = is_multicast_ether_addr(eth->h_dest);
struct vring *vring;
static bool pr_once_fw;
int rc;
/* in STA mode (ESS), all to same VRING */
vring = wil_find_tx_vring_sta(wil, skb);
} else { /* direct communication, find matching VRING */
- if (is_unicast_ether_addr(eth->h_dest))
- vring = wil_find_tx_vring(wil, skb);
- else
- vring = wil_tx_bcast(wil, skb);
+ vring = bcast ? wil_find_tx_bcast(wil, skb) :
+ wil_find_tx_ucast(wil, skb);
}
if (unlikely(!vring)) {
wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest);
struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
int done = 0;
int cid = wil->vring2cid_tid[ringid][0];
- struct wil_net_stats *stats = &wil->sta[cid].stats;
+ struct wil_net_stats *stats = NULL;
volatile struct vring_tx_desc *_d;
int used_before_complete;
int used_new;
used_before_complete = wil_vring_used_tx(vring);
+ if (cid < WIL6210_MAX_CID)
+ stats = &wil->sta[cid].stats;
+
while (!wil_vring_is_empty(vring)) {
int new_swtail;
struct wil_ctx *ctx = &vring->ctx[vring->swtail];
if (skb) {
if (likely(d->dma.error == 0)) {
ndev->stats.tx_packets++;
- stats->tx_packets++;
ndev->stats.tx_bytes += skb->len;
- stats->tx_bytes += skb->len;
+ if (stats) {
+ stats->tx_packets++;
+ stats->tx_bytes += skb->len;
+ }
} else {
ndev->stats.tx_errors++;
- stats->tx_errors++;
+ if (stats)
+ stats->tx_errors++;
}
wil_consume_skb(skb, d->dma.error == 0);
}
extern unsigned short rx_ring_overflow_thrsh;
extern int agg_wsize;
extern u32 vring_idle_trsh;
+extern bool rx_align_2;
#define WIL_NAME "wil6210"
#define WIL_FW_NAME "wil6210.fw" /* code */
#define WIL_TX_Q_LEN_DEFAULT (4000)
#define WIL_RX_RING_SIZE_ORDER_DEFAULT (10)
#define WIL_TX_RING_SIZE_ORDER_DEFAULT (10)
+#define WIL_BCAST_RING_SIZE_ORDER_DEFAULT (7)
+#define WIL_BCAST_MCS0_LIMIT (1024) /* limit for MCS0 frame size */
/* limit ring size in range [32..32k] */
#define WIL_RING_SIZE_ORDER_MIN (5)
#define WIL_RING_SIZE_ORDER_MAX (15)
u32 monitor_flags;
u32 privacy; /* secure connection? */
int sinfo_gen;
+ u32 ap_isolate; /* no intra-BSS communication */
/* interrupt moderation */
u32 tx_max_burst_duration;
u32 tx_interframe_timeout;
struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS];
u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
struct wil_sta_info sta[WIL6210_MAX_CID];
+ int bcast_vring;
/* scan */
struct cfg80211_scan_request *scan_request;
int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
int cid, int tid);
void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
+int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size);
+int wil_bcast_init(struct wil6210_priv *wil);
+void wil_bcast_fini(struct wil6210_priv *wil);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
int wil_tx_complete(struct wil6210_priv *wil, int ringid);
/* FIXME FW can transmit only ucast frames to peer */
/* FIXME real ring_id instead of hard coded 0 */
- memcpy(wil->sta[evt->cid].addr, evt->bssid, ETH_ALEN);
+ ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid);
wil->sta[evt->cid].status = wil_sta_conn_pending;
wil->pending_connect_cid = evt->cid;
}
eth = (struct ethhdr *)skb_put(skb, ETH_HLEN);
- memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN);
- memcpy(eth->h_source, evt->src_mac, ETH_ALEN);
+ ether_addr_copy(eth->h_dest, ndev->dev_addr);
+ ether_addr_copy(eth->h_source, evt->src_mac);
eth->h_proto = cpu_to_be16(ETH_P_PAE);
memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len);
skb->protocol = eth_type_trans(skb, ndev);
{
struct wmi_set_mac_address_cmd cmd;
- memcpy(cmd.mac, addr, ETH_ALEN);
+ ether_addr_copy(cmd.mac, addr);
wil_dbg_wmi(wil, "Set MAC %pM\n", addr);
*/
cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS);
}
+
+ if (rx_align_2)
+ cmd.l2_802_3_offload_ctrl |=
+ L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK;
+
/* typical time for secure PCP is 840ms */
rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
struct wmi_disconnect_sta_cmd cmd = {
.disconnect_reason = cpu_to_le16(reason),
};
- memcpy(cmd.dst_mac, mac, ETH_ALEN);
+
+ ether_addr_copy(cmd.dst_mac, mac);
wil_dbg_wmi(wil, "%s(%pM, reason %d)\n", __func__, mac, reason);
WMI_SET_UCODE_IDLE_CMDID = 0x0813,
WMI_SET_WORK_MODE_CMDID = 0x0815,
WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816,
- WMI_MARLON_R_ACTIVATE_CMDID = 0x0817,
WMI_MARLON_R_READ_CMDID = 0x0818,
WMI_MARLON_R_WRITE_CMDID = 0x0819,
WMI_MARLON_R_TXRX_SEL_CMDID = 0x081a,
WMI_RF_RX_TEST_CMDID = 0x081e,
WMI_CFG_RX_CHAIN_CMDID = 0x0820,
WMI_VRING_CFG_CMDID = 0x0821,
+ WMI_BCAST_VRING_CFG_CMDID = 0x0822,
WMI_VRING_BA_EN_CMDID = 0x0823,
WMI_VRING_BA_DIS_CMDID = 0x0824,
WMI_RCP_ADDBA_RESP_CMDID = 0x0825,
WMI_BF_TXSS_MGMT_CMDID = 0x0837,
WMI_BF_SM_MGMT_CMDID = 0x0838,
WMI_BF_RXSS_MGMT_CMDID = 0x0839,
+ WMI_BF_TRIG_CMDID = 0x083A,
WMI_SET_SECTORS_CMDID = 0x0849,
WMI_MAINTAIN_PAUSE_CMDID = 0x0850,
WMI_MAINTAIN_RESUME_CMDID = 0x0851,
struct wmi_vring_cfg vring_cfg;
} __packed;
+/*
+ * WMI_BCAST_VRING_CFG_CMDID
+ */
+struct wmi_bcast_vring_cfg {
+ struct wmi_sw_ring_cfg tx_sw_ring;
+ u8 ringid; /* 0-23 vrings */
+ u8 encap_trans_type;
+ u8 ds_cfg; /* 802.3 DS cfg */
+ u8 nwifi_ds_trans_type;
+} __packed;
+
+struct wmi_bcast_vring_cfg_cmd {
+ __le32 action;
+ struct wmi_bcast_vring_cfg vring_cfg;
+} __packed;
+
/*
* WMI_VRING_BA_EN_CMDID
*/
#define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0)
#define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1)
#define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1)
+ #define L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_POS (1)
+ #define L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_LEN (1)
+ #define L2_802_3_OFFLOAD_CTRL_SNAP_KEEP_MSK (0x2)
u8 l2_802_3_offload_ctrl;
#define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0)
WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812,
WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815,
WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816,
- WMI_MARLON_R_ACTIVATE_DONE_EVENTID = 0x1817,
WMI_MARLON_R_READ_DONE_EVENTID = 0x1818,
WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819,
WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181a,
size_t buffersize, bool dma_to_device)
{
if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr)))
- return 1;
+ return true;
switch (ring->type) {
case B43_DMA_30BIT:
}
/* The address is OK. */
- return 0;
+ return false;
address_error:
/* We can't support this address. Unmap it again. */
unmap_descbuffer(ring, addr, buffersize, dma_to_device);
- return 1;
+ return true;
}
static bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb)
enum b43_dmatype type)
{
if (type != B43_DMA_64BIT)
- return 1;
+ return true;
#ifdef CONFIG_B43_SSB
if (dev->dev->bus_type == B43_BUS_SSB &&
dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
!(pci_is_pcie(dev->dev->sdev->bus->host_pci) &&
ssb_read32(dev->dev->sdev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64))
- return 1;
+ return true;
#endif
- return 0;
+ return false;
}
int b43_dma_init(struct b43_wldev *dev)
switch (dev->dev->bus_type) {
#ifdef CONFIG_B43_BCMA
case B43_BUS_BCMA:
- bcma_core_pci_irq_ctl(dev->dev->bdev->bus,
+ bcma_host_pci_irq_ctl(dev->dev->bdev->bus,
dev->dev->bdev, true);
bcma_host_pci_up(dev->dev->bdev->bus);
break;
bool dma_to_device)
{
if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr)))
- return 1;
+ return true;
switch (ring->type) {
case B43legacy_DMA_30BIT:
}
/* The address is OK. */
- return 0;
+ return false;
address_error:
/* We can't support this address. Unmap it again. */
unmap_descbuffer(ring, addr, buffersize, dma_to_device);
- return 1;
+ return true;
}
static int setup_rx_descbuffer(struct b43legacy_dmaring *ring,
if (dev->dev->id.revision >= 3) {
if (!(b43legacy_read32(dev, B43legacy_MMIO_RADIO_HWENABLED_HI)
& B43legacy_MMIO_RADIO_HWENABLED_HI_MASK))
- return 1;
+ return true;
} else {
/* To prevent CPU fault on PPC, do not read a register
* unless the interface is started; however, on resume
* that happens, unconditionally return TRUE.
*/
if (b43legacy_status(dev) < B43legacy_STAT_STARTED)
- return 1;
+ return true;
if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO)
& B43legacy_MMIO_RADIO_HWENABLED_LO_MASK)
- return 1;
+ return true;
}
- return 0;
+ return false;
}
/* The poll callback for the hardware button. */
#include <linux/mmc/host.h>
#include <linux/platform_device.h>
#include <linux/platform_data/brcmfmac-sdio.h>
+#include <linux/pm_runtime.h>
#include <linux/suspend.h>
#include <linux/errno.h>
#include <linux/module.h>
sg_free_table(&sdiodev->sgtable);
sdiodev->sbwad = 0;
+ pm_runtime_allow(sdiodev->func[1]->card->host->parent);
return 0;
}
ret = -ENODEV;
goto out;
}
-
+ pm_runtime_forbid(host->parent);
out:
if (ret)
brcmf_sdiod_remove(sdiodev);
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
{ /* end: all zeroes */ }
};
brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
brcmf_dbg(SDIO, "Function: %d\n", func->num);
- if (func->num != 1 && func->num != 2)
+ if (func->num != 1)
return;
bus_if = dev_get_drvdata(&func->dev);
#define BCM4329_CORE_SOCRAM_BASE 0x18003000
/* ARM Cortex M3 core, ID 0x82a */
#define BCM4329_CORE_ARM_BASE 0x18002000
-#define BCM4329_RAMSIZE 0x48000
-/* bcm43143 */
-#define BCM43143_RAMSIZE 0x70000
#define CORE_SB(base, field) \
(base + SBCONFIGOFF + offsetof(struct sbconfig, field))
u32 sbidhigh; /* identification */
};
+/* bankidx and bankinfo reg defines corerev >= 8 */
+#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000
+#define SOCRAM_BANKINFO_SZMASK 0x0000007f
+#define SOCRAM_BANKIDX_ROM_MASK 0x00000100
+
+#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8
+/* socram bankinfo memtype */
+#define SOCRAM_MEMTYPE_RAM 0
+#define SOCRAM_MEMTYPE_R0M 1
+#define SOCRAM_MEMTYPE_DEVRAM 2
+
+#define SOCRAM_BANKINFO_SZBASE 8192
+#define SRCI_LSS_MASK 0x00f00000
+#define SRCI_LSS_SHIFT 20
+#define SRCI_SRNB_MASK 0xf0
+#define SRCI_SRNB_SHIFT 4
+#define SRCI_SRBSZ_MASK 0xf
+#define SRCI_SRBSZ_SHIFT 0
+#define SR_BSZ_BASE 14
+
+struct sbsocramregs {
+ u32 coreinfo;
+ u32 bwalloc;
+ u32 extracoreinfo;
+ u32 biststat;
+ u32 bankidx;
+ u32 standbyctrl;
+
+ u32 errlogstatus; /* rev 6 */
+ u32 errlogaddr; /* rev 6 */
+ /* used for patching rev 3 & 5 */
+ u32 cambankidx;
+ u32 cambankstandbyctrl;
+ u32 cambankpatchctrl;
+ u32 cambankpatchtblbaseaddr;
+ u32 cambankcmdreg;
+ u32 cambankdatareg;
+ u32 cambankmaskreg;
+ u32 PAD[1];
+ u32 bankinfo; /* corev 8 */
+ u32 bankpda;
+ u32 PAD[14];
+ u32 extmemconfig;
+ u32 extmemparitycsr;
+ u32 extmemparityerrdata;
+ u32 extmemparityerrcnt;
+ u32 extmemwrctrlandsize;
+ u32 PAD[84];
+ u32 workaround;
+ u32 pwrctl; /* corerev >= 2 */
+ u32 PAD[133];
+ u32 sr_control; /* corerev >= 15 */
+ u32 sr_status; /* corerev >= 15 */
+ u32 sr_address; /* corerev >= 15 */
+ u32 sr_data; /* corerev >= 15 */
+};
+
+#define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f)
+
+#define ARMCR4_CAP (0x04)
+#define ARMCR4_BANKIDX (0x40)
+#define ARMCR4_BANKINFO (0x44)
+#define ARMCR4_BANKPDA (0x4C)
+
+#define ARMCR4_TCBBNB_MASK 0xf0
+#define ARMCR4_TCBBNB_SHIFT 4
+#define ARMCR4_TCBANB_MASK 0xf
+#define ARMCR4_TCBANB_SHIFT 0
+
+#define ARMCR4_BSZ_MASK 0x3f
+#define ARMCR4_BSZ_MULT 8192
+
struct brcmf_core_priv {
struct brcmf_core pub;
u32 wrapbase;
return &core->pub;
}
-#ifdef DEBUG
/* safety check for chipinfo */
static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
{
struct brcmf_core_priv *core;
bool need_socram = false;
bool has_socram = false;
+ bool cpu_found = false;
int idx = 1;
list_for_each_entry(core, &ci->cores, list) {
switch (core->pub.id) {
case BCMA_CORE_ARM_CM3:
+ cpu_found = true;
need_socram = true;
break;
case BCMA_CORE_INTERNAL_MEM:
has_socram = true;
break;
case BCMA_CORE_ARM_CR4:
- if (ci->pub.rambase == 0) {
- brcmf_err("RAM base not provided with ARM CR4 core\n");
- return -ENOMEM;
- }
+ cpu_found = true;
break;
default:
break;
}
}
+ if (!cpu_found) {
+ brcmf_err("CPU core not detected\n");
+ return -ENXIO;
+ }
/* check RAM core presence for ARM CM3 core */
if (need_socram && !has_socram) {
brcmf_err("RAM core not provided with ARM CM3 core\n");
}
return 0;
}
-#else /* DEBUG */
-static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
+
+static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg)
{
- return 0;
+ return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg);
}
-#endif
-static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
+static void brcmf_chip_core_write32(struct brcmf_core_priv *core,
+ u16 reg, u32 val)
{
- switch (ci->pub.chip) {
- case BRCM_CC_4329_CHIP_ID:
- ci->pub.ramsize = BCM4329_RAMSIZE;
- break;
- case BRCM_CC_43143_CHIP_ID:
- ci->pub.ramsize = BCM43143_RAMSIZE;
- break;
- case BRCM_CC_43241_CHIP_ID:
- ci->pub.ramsize = 0x90000;
- break;
- case BRCM_CC_4330_CHIP_ID:
- ci->pub.ramsize = 0x48000;
- break;
+ core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val);
+}
+
+static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx,
+ u32 *banksize)
+{
+ u32 bankinfo;
+ u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT);
+
+ bankidx |= idx;
+ brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx);
+ bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo));
+ *banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1;
+ *banksize *= SOCRAM_BANKINFO_SZBASE;
+ return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK);
+}
+
+static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize,
+ u32 *srsize)
+{
+ u32 coreinfo;
+ uint nb, banksize, lss;
+ bool retent;
+ int i;
+
+ *ramsize = 0;
+ *srsize = 0;
+
+ if (WARN_ON(sr->pub.rev < 4))
+ return;
+
+ if (!brcmf_chip_iscoreup(&sr->pub))
+ brcmf_chip_resetcore(&sr->pub, 0, 0, 0);
+
+ /* Get info for determining size */
+ coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo));
+ nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+
+ if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) {
+ banksize = (coreinfo & SRCI_SRBSZ_MASK);
+ lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT;
+ if (lss != 0)
+ nb--;
+ *ramsize = nb * (1 << (banksize + SR_BSZ_BASE));
+ if (lss != 0)
+ *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE));
+ } else {
+ nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+ for (i = 0; i < nb; i++) {
+ retent = brcmf_chip_socram_banksize(sr, i, &banksize);
+ *ramsize += banksize;
+ if (retent)
+ *srsize += banksize;
+ }
+ }
+
+ /* hardcoded save&restore memory sizes */
+ switch (sr->chip->pub.chip) {
case BRCM_CC_4334_CHIP_ID:
- case BRCM_CC_43340_CHIP_ID:
- ci->pub.ramsize = 0x80000;
+ if (sr->chip->pub.chiprev < 2)
+ *srsize = (32 * 1024);
break;
- case BRCM_CC_4335_CHIP_ID:
- ci->pub.ramsize = 0xc0000;
- ci->pub.rambase = 0x180000;
+ case BRCM_CC_43430_CHIP_ID:
+ /* assume sr for now as we can not check
+ * firmware sr capability at this point.
+ */
+ *srsize = (64 * 1024);
break;
- case BRCM_CC_43362_CHIP_ID:
- ci->pub.ramsize = 0x3c000;
+ default:
break;
+ }
+}
+
+/** Return the TCM-RAM size of the ARMCR4 core. */
+static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4)
+{
+ u32 corecap;
+ u32 memsize = 0;
+ u32 nab;
+ u32 nbb;
+ u32 totb;
+ u32 bxinfo;
+ u32 idx;
+
+ corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP);
+
+ nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT;
+ nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT;
+ totb = nab + nbb;
+
+ for (idx = 0; idx < totb; idx++) {
+ brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx);
+ bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO);
+ memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT;
+ }
+
+ return memsize;
+}
+
+static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci)
+{
+ switch (ci->pub.chip) {
+ case BRCM_CC_4345_CHIP_ID:
+ return 0x198000;
+ case BRCM_CC_4335_CHIP_ID:
case BRCM_CC_4339_CHIP_ID:
case BRCM_CC_4354_CHIP_ID:
case BRCM_CC_4356_CHIP_ID:
case BRCM_CC_43567_CHIP_ID:
case BRCM_CC_43569_CHIP_ID:
case BRCM_CC_43570_CHIP_ID:
- ci->pub.ramsize = 0xc0000;
- ci->pub.rambase = 0x180000;
- break;
case BRCM_CC_43602_CHIP_ID:
- ci->pub.ramsize = 0xf0000;
- ci->pub.rambase = 0x180000;
- break;
+ return 0x180000;
default:
brcmf_err("unknown chip: %s\n", ci->pub.name);
break;
}
+ return 0;
+}
+
+static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
+{
+ struct brcmf_core_priv *mem_core;
+ struct brcmf_core *mem;
+
+ mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4);
+ if (mem) {
+ mem_core = container_of(mem, struct brcmf_core_priv, pub);
+ ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core);
+ ci->pub.rambase = brcmf_chip_tcm_rambase(ci);
+ if (!ci->pub.rambase) {
+ brcmf_err("RAM base not provided with ARM CR4 core\n");
+ return -EINVAL;
+ }
+ } else {
+ mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_INTERNAL_MEM);
+ mem_core = container_of(mem, struct brcmf_core_priv, pub);
+ brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize,
+ &ci->pub.srsize);
+ }
+ brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n",
+ ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize,
+ ci->pub.srsize, ci->pub.srsize);
+
+ if (!ci->pub.ramsize) {
+ brcmf_err("RAM size is undetermined\n");
+ return -ENOMEM;
+ }
+ return 0;
}
static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr,
struct brcmf_core *core;
u32 regdata;
u32 socitype;
+ int ret;
/* Get CC core rev
* Chipid is assume to be at offset 0 from SI_ENUM_BASE
return -ENODEV;
}
- brcmf_chip_get_raminfo(ci);
+ ret = brcmf_chip_cores_check(ci);
+ if (ret)
+ return ret;
- return brcmf_chip_cores_check(ci);
+ /* assure chip is passive for core access */
+ brcmf_chip_set_passive(&ci->pub);
+ return brcmf_chip_get_raminfo(ci);
}
static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id)
if (chip->ops->setup)
ret = chip->ops->setup(chip->ctx, pub);
- /*
- * Make sure any on-chip ARM is off (in case strapping is wrong),
- * or downloaded code was already running.
- */
- brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
- brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4);
return ret;
}
err = -EINVAL;
if (WARN_ON(!ops->prepare))
err = -EINVAL;
- if (WARN_ON(!ops->exit_dl))
+ if (WARN_ON(!ops->activate))
err = -EINVAL;
if (err < 0)
return ERR_PTR(-EINVAL);
}
static void
-brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip)
+brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip)
{
struct brcmf_core *core;
+ struct brcmf_core_priv *sr;
brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3);
core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211);
D11_BCMA_IOCTL_PHYCLOCKEN);
core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM);
brcmf_chip_resetcore(core, 0, 0, 0);
+
+ /* disable bank #3 remap for this device */
+ if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) {
+ sr = container_of(core, struct brcmf_core_priv, pub);
+ brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3);
+ brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0);
+ }
}
-static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip)
+static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip)
{
struct brcmf_core *core;
return false;
}
- chip->ops->exit_dl(chip->ctx, &chip->pub, 0);
+ chip->ops->activate(chip->ctx, &chip->pub, 0);
core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3);
brcmf_chip_resetcore(core, 0, 0, 0);
}
static inline void
-brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip)
+brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip)
{
struct brcmf_core *core;
D11_BCMA_IOCTL_PHYCLOCKEN);
}
-static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec)
+static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec)
{
struct brcmf_core *core;
- chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec);
+ chip->ops->activate(chip->ctx, &chip->pub, rstvec);
/* restore ARM */
core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4);
return true;
}
-void brcmf_chip_enter_download(struct brcmf_chip *pub)
+void brcmf_chip_set_passive(struct brcmf_chip *pub)
{
struct brcmf_chip_priv *chip;
struct brcmf_core *arm;
chip = container_of(pub, struct brcmf_chip_priv, pub);
arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
if (arm) {
- brcmf_chip_cr4_enterdl(chip);
+ brcmf_chip_cr4_set_passive(chip);
return;
}
- brcmf_chip_cm3_enterdl(chip);
+ brcmf_chip_cm3_set_passive(chip);
}
-bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec)
+bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec)
{
struct brcmf_chip_priv *chip;
struct brcmf_core *arm;
chip = container_of(pub, struct brcmf_chip_priv, pub);
arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4);
if (arm)
- return brcmf_chip_cr4_exitdl(chip, rstvec);
+ return brcmf_chip_cr4_set_active(chip, rstvec);
- return brcmf_chip_cm3_exitdl(chip);
+ return brcmf_chip_cm3_set_active(chip);
}
bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
addr = CORE_CC_REG(base, chipcontrol_data);
reg = chip->ops->read32(chip->ctx, addr);
return (reg & pmu_cc3_mask) != 0;
+ case BRCM_CC_43430_CHIP_ID:
+ addr = CORE_CC_REG(base, sr_control1);
+ reg = chip->ops->read32(chip->ctx, addr);
+ return reg != 0;
default:
addr = CORE_CC_REG(base, pmucapabilities_ext);
reg = chip->ops->read32(chip->ctx, addr);
* @pmucaps: PMU capabilities.
* @pmurev: PMU revision.
* @rambase: RAM base address (only applicable for ARM CR4 chips).
- * @ramsize: amount of RAM on chip.
+ * @ramsize: amount of RAM on chip including retention.
+ * @srsize: amount of retention RAM on chip.
* @name: string representation of the chip identifier.
*/
struct brcmf_chip {
u32 pmurev;
u32 rambase;
u32 ramsize;
+ u32 srsize;
char name[8];
};
* @write32: write 32-bit value over bus.
* @prepare: prepare bus for core configuration.
* @setup: bus-specific core setup.
- * @exit_dl: exit download state.
+ * @active: chip becomes active.
* The callback should use the provided @rstvec when non-zero.
*/
struct brcmf_buscore_ops {
void (*write32)(void *ctx, u32 addr, u32 value);
int (*prepare)(void *ctx);
int (*setup)(void *ctx, struct brcmf_chip *chip);
- void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
+ void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec);
};
struct brcmf_chip *brcmf_chip_attach(void *ctx,
void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset);
void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
u32 postreset);
-void brcmf_chip_enter_download(struct brcmf_chip *ci);
-bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec);
+void brcmf_chip_set_passive(struct brcmf_chip *ci);
+bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec);
bool brcmf_chip_sr_capable(struct brcmf_chip *pub);
#endif /* BRCMF_AXIDMP_H */
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
if (drvr->bus_if->wowl_supported)
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
- brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
+ if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID)
+ brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
/* set chip related quirks */
switch (drvr->bus_if->chip) {
static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
{
- if (waitqueue_active(&msgbuf->ioctl_resp_wait)) {
- msgbuf->ctl_completed = true;
+ msgbuf->ctl_completed = true;
+ if (waitqueue_active(&msgbuf->ioctl_resp_wait))
wake_up(&msgbuf->ioctl_resp_wait);
- }
}
#ifdef CONFIG_BRCMFMAC_PROTO_MSGBUF
-#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 20
-#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 256
-#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 20
+#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 64
+#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 512
+#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 64
#define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024
-#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 256
+#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 512
#define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512
#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40
#define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin"
#define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt"
-#define BRCMF_PCIE_4354_FW_NAME "brcm/brcmfmac4354-pcie.bin"
-#define BRCMF_PCIE_4354_NVRAM_NAME "brcm/brcmfmac4354-pcie.txt"
#define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin"
#define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt"
#define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin"
MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME);
MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4354_FW_NAME);
-MODULE_FIRMWARE(BRCMF_PCIE_4354_NVRAM_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME);
+MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME);
MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME);
MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME);
static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo)
{
- brcmf_chip_enter_download(devinfo->ci);
-
if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) {
brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4);
brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX,
brcmf_chip_resetcore(core, 0, 0, 0);
}
- return !brcmf_chip_exit_download(devinfo->ci, resetintr);
+ return !brcmf_chip_set_active(devinfo->ci, resetintr);
}
console->log_str[console->log_idx] = ch;
console->log_idx++;
}
-
if (ch == '\n') {
console->log_str[console->log_idx] = 0;
- brcmf_dbg(PCIE, "CONSOLE: %s\n", console->log_str);
+ brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str);
console->log_idx = 0;
}
}
fw_name = BRCMF_PCIE_43602_FW_NAME;
nvram_name = BRCMF_PCIE_43602_NVRAM_NAME;
break;
- case BRCM_CC_4354_CHIP_ID:
- fw_name = BRCMF_PCIE_4354_FW_NAME;
- nvram_name = BRCMF_PCIE_4354_NVRAM_NAME;
- break;
case BRCM_CC_4356_CHIP_ID:
fw_name = BRCMF_PCIE_4356_FW_NAME;
nvram_name = BRCMF_PCIE_4356_NVRAM_NAME;
}
-static void brcmf_pcie_buscore_exitdl(void *ctx, struct brcmf_chip *chip,
- u32 rstvec)
+static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip,
+ u32 rstvec)
{
struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = {
.prepare = brcmf_pcie_buscoreprep,
- .exit_dl = brcmf_pcie_buscore_exitdl,
+ .activate = brcmf_pcie_buscore_activate,
.read32 = brcmf_pcie_buscore_read32,
.write32 = brcmf_pcie_buscore_write32,
};
PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
static struct pci_device_id brcmf_pcie_devid_table[] = {
- BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
struct brcmf_sdio_dev *sdiodev; /* sdio device handler */
struct brcmf_chip *ci; /* Chip info struct */
- u32 ramsize; /* Size of RAM in SOCRAM (bytes) */
-
u32 hostintmask; /* Copy of Host Interrupt Mask */
atomic_t intstatus; /* Intstatus bits (events) pending */
atomic_t fcstate; /* State of dongle flow-control */
#endif /* DEBUG */
uint clkstate; /* State of sd and backplane clock(s) */
- bool activity; /* Activity flag for clock down */
s32 idletime; /* Control for activity timeout */
- s32 idlecount; /* Activity timeout counter */
- s32 idleclock; /* How to set bus driver when idle */
+ s32 idlecount; /* Activity timeout counter */
+ s32 idleclock; /* How to set bus driver when idle */
bool rxflow_mode; /* Rx flow control mode */
bool rxflow; /* Is rx flow control on */
bool alp_only; /* Don't use HT clock (ALP only) */
struct workqueue_struct *brcmf_wq;
struct work_struct datawork;
- atomic_t dpc_tskcnt;
+ bool dpc_triggered;
+ bool dpc_running;
bool txoff; /* Transmit flow-controlled */
struct brcmf_sdio_count sdcnt;
#define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt"
#define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin"
#define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt"
+#define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin"
+#define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt"
+#define BCM43455_FIRMWARE_NAME "brcm/brcmfmac43455-sdio.bin"
+#define BCM43455_NVRAM_NAME "brcm/brcmfmac43455-sdio.txt"
#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin"
#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt"
MODULE_FIRMWARE(BCM43362_NVRAM_NAME);
MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME);
MODULE_FIRMWARE(BCM4339_NVRAM_NAME);
+MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM43430_NVRAM_NAME);
+MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM43455_NVRAM_NAME);
MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME);
MODULE_FIRMWARE(BCM4354_NVRAM_NAME);
{ BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
{ BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
{ BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
+ { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) },
+ { BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) },
{ BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
};
brcmf_dbg(SDIO, "Enter\n");
/* Early exit if we're already there */
- if (bus->clkstate == target) {
- if (target == CLK_AVAIL) {
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
- bus->activity = true;
- }
+ if (bus->clkstate == target)
return 0;
- }
switch (target) {
case CLK_AVAIL:
brcmf_sdio_sdclk(bus, true);
/* Now request HT Avail on the backplane */
brcmf_sdio_htclk(bus, true, pendok);
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
- bus->activity = true;
break;
case CLK_SDONLY:
else
brcmf_err("request for %d -> %d\n",
bus->clkstate, target);
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
break;
case CLK_NONE:
brcmf_sdio_htclk(bus, false, false);
/* Now remove the SD clock */
brcmf_sdio_sdclk(bus, false);
- brcmf_sdio_wd_timer(bus, 0);
break;
}
#ifdef DEBUG
/* Going to sleep */
if (sleep) {
- /* Don't sleep if something is pending */
- if (atomic_read(&bus->intstatus) ||
- atomic_read(&bus->ipend) > 0 ||
- bus->ctrl_frame_stat ||
- (!atomic_read(&bus->fcstate) &&
- brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
- data_ok(bus))) {
- err = -EBUSY;
- goto done;
- }
-
clkcsr = brcmf_sdiod_regrb(bus->sdiodev,
SBSDIO_FUNC1_CHIPCLKCSR,
&err);
SBSDIO_ALP_AVAIL_REQ, &err);
}
err = brcmf_sdio_kso_control(bus, false);
- /* disable watchdog */
- if (!err)
- brcmf_sdio_wd_timer(bus, 0);
} else {
- bus->idlecount = 0;
err = brcmf_sdio_kso_control(bus, true);
}
if (err) {
brcmf_sdio_clkctl(bus, CLK_NONE, pendok);
} else {
brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
+ brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
}
bus->sleeping = sleep;
brcmf_dbg(SDIO, "new state %s\n",
static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
struct sdpcm_shared *sh)
{
- u32 addr;
+ u32 addr = 0;
int rv;
u32 shaddr = 0;
struct sdpcm_shared_le sh_le;
__le32 addr_le;
- shaddr = bus->ci->rambase + bus->ramsize - 4;
+ sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_bus_sleep(bus, false, false);
/*
* Read last word in socram to determine
* address of sdpcm_shared structure
*/
- sdio_claim_host(bus->sdiodev->func[1]);
- brcmf_sdio_bus_sleep(bus, false, false);
- rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
- sdio_release_host(bus->sdiodev->func[1]);
+ shaddr = bus->ci->rambase + bus->ci->ramsize - 4;
+ if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci))
+ shaddr -= bus->ci->srsize;
+ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr,
+ (u8 *)&addr_le, 4);
if (rv < 0)
- return rv;
-
- addr = le32_to_cpu(addr_le);
-
- brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr);
+ goto fail;
/*
* Check if addr is valid.
* NVRAM length at the end of memory should have been overwritten.
*/
+ addr = le32_to_cpu(addr_le);
if (!brcmf_sdio_valid_shared_address(addr)) {
- brcmf_err("invalid sdpcm_shared address 0x%08X\n",
- addr);
- return -EINVAL;
+ brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr);
+ rv = -EINVAL;
+ goto fail;
}
+ brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr);
+
/* Read hndrte_shared structure */
rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
sizeof(struct sdpcm_shared_le));
if (rv < 0)
- return rv;
+ goto fail;
+
+ sdio_release_host(bus->sdiodev->func[1]);
/* Endianness */
sh->flags = le32_to_cpu(sh_le.flags);
sh->flags & SDPCM_SHARED_VERSION_MASK);
return -EPROTO;
}
-
return 0;
+
+fail:
+ brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n",
+ rv, addr);
+ sdio_release_host(bus->sdiodev->func[1]);
+ return rv;
}
static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
data_ok(bus)) {
sdio_claim_host(bus->sdiodev->func[1]);
- err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
- bus->ctrl_frame_len);
+ if (bus->ctrl_frame_stat) {
+ err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
+ bus->ctrl_frame_len);
+ bus->ctrl_frame_err = err;
+ wmb();
+ bus->ctrl_frame_stat = false;
+ }
sdio_release_host(bus->sdiodev->func[1]);
- bus->ctrl_frame_err = err;
- bus->ctrl_frame_stat = false;
brcmf_sdio_wait_event_wakeup(bus);
}
/* Send queued frames (limit 1 if rx may still be pending) */
if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) {
brcmf_err("failed backplane access over SDIO, halting operation\n");
atomic_set(&bus->intstatus, 0);
+ if (bus->ctrl_frame_stat) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ bus->ctrl_frame_err = -ENODEV;
+ wmb();
+ bus->ctrl_frame_stat = false;
+ brcmf_sdio_wait_event_wakeup(bus);
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
} else if (atomic_read(&bus->intstatus) ||
atomic_read(&bus->ipend) > 0 ||
(!atomic_read(&bus->fcstate) &&
brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
data_ok(bus))) {
- atomic_inc(&bus->dpc_tskcnt);
+ bus->dpc_triggered = true;
}
}
/* Send from dpc */
bus->ctrl_frame_buf = msg;
bus->ctrl_frame_len = msglen;
+ wmb();
bus->ctrl_frame_stat = true;
brcmf_sdio_trigger_dpc(bus);
wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat,
msecs_to_jiffies(CTL_DONE_TIMEOUT));
-
- if (!bus->ctrl_frame_stat) {
+ ret = 0;
+ if (bus->ctrl_frame_stat) {
+ sdio_claim_host(bus->sdiodev->func[1]);
+ if (bus->ctrl_frame_stat) {
+ brcmf_dbg(SDIO, "ctrl_frame timeout\n");
+ bus->ctrl_frame_stat = false;
+ ret = -ETIMEDOUT;
+ }
+ sdio_release_host(bus->sdiodev->func[1]);
+ }
+ if (!ret) {
brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n",
bus->ctrl_frame_err);
+ rmb();
ret = bus->ctrl_frame_err;
- } else {
- brcmf_dbg(SDIO, "ctrl_frame timeout\n");
- bus->ctrl_frame_stat = false;
- ret = -ETIMEDOUT;
}
if (ret)
sdio_claim_host(bus->sdiodev->func[1]);
brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
- /* Keep arm in reset */
- brcmf_chip_enter_download(bus->ci);
-
rstvec = get_unaligned_le32(fw->data);
brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec);
}
/* Take arm out of reset */
- if (!brcmf_chip_exit_download(bus->ci, rstvec)) {
+ if (!brcmf_chip_set_active(bus->ci, rstvec)) {
brcmf_err("error getting out of ARM core reset\n");
goto err;
}
void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus)
{
- if (atomic_read(&bus->dpc_tskcnt) == 0) {
- atomic_inc(&bus->dpc_tskcnt);
+ if (!bus->dpc_triggered) {
+ bus->dpc_triggered = true;
queue_work(bus->brcmf_wq, &bus->datawork);
}
}
if (!bus->intr)
brcmf_err("isr w/o interrupt configured!\n");
- atomic_inc(&bus->dpc_tskcnt);
+ bus->dpc_triggered = true;
queue_work(bus->brcmf_wq, &bus->datawork);
}
-static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
+static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
{
brcmf_dbg(TIMER, "Enter\n");
if (!bus->intr ||
(bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
- if (atomic_read(&bus->dpc_tskcnt) == 0) {
+ if (!bus->dpc_triggered) {
u8 devpend;
sdio_claim_host(bus->sdiodev->func[1]);
bus->sdcnt.pollcnt++;
atomic_set(&bus->ipend, 1);
- atomic_inc(&bus->dpc_tskcnt);
+ bus->dpc_triggered = true;
queue_work(bus->brcmf_wq, &bus->datawork);
}
}
#endif /* DEBUG */
/* On idle timeout clear activity flag and/or turn off clock */
- if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
- if (++bus->idlecount >= bus->idletime) {
- bus->idlecount = 0;
- if (bus->activity) {
- bus->activity = false;
- brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
- } else {
+ if (!bus->dpc_triggered) {
+ rmb();
+ if ((!bus->dpc_running) && (bus->idletime > 0) &&
+ (bus->clkstate == CLK_AVAIL)) {
+ bus->idlecount++;
+ if (bus->idlecount > bus->idletime) {
brcmf_dbg(SDIO, "idle\n");
sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_wd_timer(bus, 0);
+ bus->idlecount = 0;
brcmf_sdio_bus_sleep(bus, true, false);
sdio_release_host(bus->sdiodev->func[1]);
}
+ } else {
+ bus->idlecount = 0;
}
+ } else {
+ bus->idlecount = 0;
}
-
- return (atomic_read(&bus->ipend) > 0);
}
static void brcmf_sdio_dataworker(struct work_struct *work)
struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio,
datawork);
- while (atomic_read(&bus->dpc_tskcnt)) {
- atomic_set(&bus->dpc_tskcnt, 0);
+ bus->dpc_running = true;
+ wmb();
+ while (ACCESS_ONCE(bus->dpc_triggered)) {
+ bus->dpc_triggered = false;
brcmf_sdio_dpc(bus);
+ bus->idlecount = 0;
}
+ bus->dpc_running = false;
if (brcmf_sdiod_freezing(bus->sdiodev)) {
brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN);
brcmf_sdiod_try_freeze(bus->sdiodev);
return 0;
}
-static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip,
- u32 rstvec)
+static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip,
+ u32 rstvec)
{
struct brcmf_sdio_dev *sdiodev = ctx;
struct brcmf_core *core;
static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = {
.prepare = brcmf_sdio_buscoreprep,
- .exit_dl = brcmf_sdio_buscore_exitdl,
+ .activate = brcmf_sdio_buscore_activate,
.read32 = brcmf_sdio_buscore_read32,
.write32 = brcmf_sdio_buscore_write32,
};
drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH;
brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);
- /* Get info on the SOCRAM cores... */
- bus->ramsize = bus->ci->ramsize;
- if (!(bus->ramsize)) {
- brcmf_err("failed to find SOCRAM memory!\n");
- goto fail;
- }
-
/* Set card control so an SDIO card reset does a WLAN backplane reset */
reg_val = brcmf_sdiod_regrb(bus->sdiodev,
SDIO_CCCR_BRCM_CARDCTRL, &err);
bus->watchdog_tsk = NULL;
}
/* Initialize DPC thread */
- atomic_set(&bus->dpc_tskcnt, 0);
+ bus->dpc_triggered = false;
+ bus->dpc_running = false;
/* Assign bus interface call back */
bus->sdiodev->bus_if->dev = bus->sdiodev->dev;
if (bus->ci) {
if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) {
sdio_claim_host(bus->sdiodev->func[1]);
+ brcmf_sdio_wd_timer(bus, 0);
brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
/* Leave the device in state where it is
- * 'quiet'. This is done by putting it in
- * download_state which essentially resets
- * all necessary cores.
+ * 'passive'. This is done by resetting all
+ * necessary cores.
*/
msleep(20);
- brcmf_chip_enter_download(bus->ci);
+ brcmf_chip_set_passive(bus->ci);
brcmf_sdio_clkctl(bus, CLK_NONE, false);
sdio_release_host(bus->sdiodev->func[1]);
}
* Configure pci/pcmcia here instead of in brcms_c_attach()
* to allow mfg hotswap: down, hotswap (chip power cycle), up.
*/
- bcma_core_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core,
+ bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core,
true);
/*
if (ISNPHY(pi))
return wlc_phy_n_txpower_ipa_ison(pi);
else
- return 0;
+ return false;
}
bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi)
{
if (wlc_lcnphy_tempsense_based_pwr_ctrl_enabled(pi))
- return 0;
+ return false;
else
return (LCNPHY_TX_PWR_CTRL_HW ==
wlc_lcnphy_get_tx_pwr_ctrl((pi)));
#define BRCM_CC_43362_CHIP_ID 43362
#define BRCM_CC_4335_CHIP_ID 0x4335
#define BRCM_CC_4339_CHIP_ID 0x4339
+#define BRCM_CC_43430_CHIP_ID 43430
+#define BRCM_CC_4345_CHIP_ID 0x4345
#define BRCM_CC_4354_CHIP_ID 0x4354
#define BRCM_CC_4356_CHIP_ID 0x4356
#define BRCM_CC_43566_CHIP_ID 43566
u8 uart1lsr;
u8 uart1msr;
u8 uart1scratch;
- u32 PAD[126];
+ u32 PAD[62];
+
+ /* save/restore, corerev >= 48 */
+ u32 sr_capability; /* 0x500 */
+ u32 sr_control0; /* 0x504 */
+ u32 sr_control1; /* 0x508 */
+ u32 gpio_control; /* 0x50C */
+ u32 PAD[60];
/* PMU registers (corerev >= 20) */
u32 pmucontrol; /* 0x600 */
}
#ifdef CONFIG_PM
-static int cw1200_spi_suspend(struct device *dev, pm_message_t state)
+static int cw1200_spi_suspend(struct device *dev)
{
struct hwbus_priv *self = spi_get_drvdata(to_spi_device(dev));
return 0;
}
-static int cw1200_spi_resume(struct device *dev)
-{
- return 0;
-}
+static SIMPLE_DEV_PM_OPS(cw1200_pm_ops, cw1200_spi_suspend, NULL);
+
#endif
static struct spi_driver spi_driver = {
.bus = &spi_bus_type,
.owner = THIS_MODULE,
#ifdef CONFIG_PM
- .suspend = cw1200_spi_suspend,
- .resume = cw1200_spi_resume,
+ .pm = &cw1200_pm_ops,
#endif
},
};
unsigned long reload_jiffies;
int reload_count;
bool ucode_loaded;
- bool init_ucode_run; /* Don't run init uCode again */
u8 plcp_delta_threshold;
scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) |
BIT(IWL_DEFAULT_CMD_QUEUE_NUM));
- if (vif)
- scd_queues &= ~BIT(vif->hw_queue[IEEE80211_AC_VO]);
-
- IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", scd_queues);
- if (iwlagn_txfifo_flush(priv, scd_queues)) {
- IWL_ERR(priv, "flush request fail\n");
- goto done;
+ if (drop) {
+ IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n",
+ scd_queues);
+ if (iwlagn_txfifo_flush(priv, scd_queues)) {
+ IWL_ERR(priv, "flush request fail\n");
+ goto done;
+ }
}
+
IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n");
- iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
+ iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues);
done:
mutex_unlock(&priv->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
desc += sprintf(buff+desc, "lq type %s\n",
(is_legacy(tbl->lq_type)) ? "legacy" : "HT");
if (is_Ht(tbl->lq_type)) {
- desc += sprintf(buff+desc, " %s",
+ desc += sprintf(buff + desc, " %s",
(is_siso(tbl->lq_type)) ? "SISO" :
((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
- desc += sprintf(buff+desc, " %s",
+ desc += sprintf(buff + desc, " %s",
(tbl->is_ht40) ? "40MHz" : "20MHz");
- desc += sprintf(buff+desc, " %s %s %s\n", (tbl->is_SGI) ? "SGI" : "",
+ desc += sprintf(buff + desc, " %s %s %s\n",
+ (tbl->is_SGI) ? "SGI" : "",
(lq_sta->is_green) ? "GF enabled" : "",
(lq_sta->is_agg) ? "AGG on" : "");
}
rate_flags |= RATE_MCS_CCK_MSK;
/* Set up antennas */
- if (priv->lib->bt_params &&
- priv->lib->bt_params->advanced_bt_coexist &&
- priv->bt_full_concurrent) {
+ if (priv->lib->bt_params &&
+ priv->lib->bt_params->advanced_bt_coexist &&
+ priv->bt_full_concurrent) {
/* operated as 1x1 in full concurrency mode */
priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
first_antenna(priv->nvm_data->valid_tx_ant));
if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len)
return 0;
- if (priv->init_ucode_run)
- return 0;
-
iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
calib_complete, ARRAY_SIZE(calib_complete),
iwlagn_wait_calib, priv);
*/
ret = iwl_wait_notification(&priv->notif_wait, &calib_wait,
UCODE_CALIB_TIMEOUT);
- if (!ret)
- priv->init_ucode_run = true;
goto out;
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
-#define IWL7260_UCODE_API_MAX 12
-#define IWL3160_UCODE_API_MAX 12
+#define IWL7260_UCODE_API_MAX 13
+#define IWL3160_UCODE_API_MAX 13
/* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK 10
-#define IWL3160_UCODE_API_OK 10
+#define IWL7260_UCODE_API_OK 12
+#define IWL3160_UCODE_API_OK 12
/* Lowest firmware API version supported */
#define IWL7260_UCODE_API_MIN 10
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
-#define IWL8000_UCODE_API_MAX 12
+#define IWL8000_UCODE_API_MAX 13
/* Oldest version we won't warn about */
-#define IWL8000_UCODE_API_OK 10
+#define IWL8000_UCODE_API_OK 12
/* Lowest firmware API version supported */
#define IWL8000_UCODE_API_MIN 10
IWL8000_FW_PRE "-" __stringify(api) ".ucode"
#define NVM_HW_SECTION_NUM_FAMILY_8000 10
-#define DEFAULT_NVM_FILE_FAMILY_8000A "iwl_nvm_8000.bin"
-#define DEFAULT_NVM_FILE_FAMILY_8000 "iwl_nvm_8000B.bin"
+#define DEFAULT_NVM_FILE_FAMILY_8000B "nvmData-8000B"
+#define DEFAULT_NVM_FILE_FAMILY_8000C "nvmData-8000C"
/* Max SDIO RX aggregation size of the ADDBA request/response */
#define MAX_RX_AGG_SIZE_8260_SDIO 28
.ht_params = &iwl8000_ht_params,
.nvm_ver = IWL8000_NVM_VERSION,
.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
- .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
- .default_nvm_file_8000A = DEFAULT_NVM_FILE_FAMILY_8000A,
+ .default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B,
+ .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C,
.max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
.disable_dummy_notification = true,
.max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
.ht_params = &iwl8000_ht_params,
.nvm_ver = IWL8000_NVM_VERSION,
.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
- .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
- .default_nvm_file_8000A = DEFAULT_NVM_FILE_FAMILY_8000A,
+ .default_nvm_file_B_step = DEFAULT_NVM_FILE_FAMILY_8000B,
+ .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C,
.max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
.bt_shared_single_ant = true,
.disable_dummy_notification = true,
{
/* return 1 only for family 8000 B0 */
if ((family == IWL_DEVICE_FAMILY_8000) && (hw_rev & 0xC))
- return 1;
+ return true;
- return 0;
+ return false;
}
/*
/**
* struct iwl_cfg
- * @name: Offical name of the device
+ * @name: Official name of the device
* @fw_name_pre: Firmware filename prefix. The api version and extension
* (.ucode) will be added to filename before loading from disk. The
* filename is constructed as fw_name_pre<api>.ucode.
bool lp_xtal_workaround;
const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
bool no_power_up_nic_in_init;
- const char *default_nvm_file;
- const char *default_nvm_file_8000A;
+ const char *default_nvm_file_B_step;
+ const char *default_nvm_file_C_step;
unsigned int max_rx_agg_size;
bool disable_dummy_notification;
unsigned int max_tx_agg_size;
/* 0x0000F000 - 0x00001000 */
#define IWL_DL_ASSOC 0x00001000
#define IWL_DL_DROP 0x00002000
+#define IWL_DL_LAR 0x00004000
#define IWL_DL_COEX 0x00008000
/* 0x000F0000 - 0x00010000 */
#define IWL_DL_FW 0x00010000
#define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a)
#define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a)
#define IWL_DEBUG_RPM(p, f, a...) IWL_DEBUG(p, IWL_DL_RPM, f, ## a)
+#define IWL_DEBUG_LAR(p, f, a...) IWL_DEBUG(p, IWL_DL_LAR, f, ## a)
#endif
#define IWL_DEFAULT_SCAN_CHANNELS 40
/*
- * struct fw_sec: Just for the image parsing proccess.
+ * struct fw_sec: Just for the image parsing process.
* For the fw storage we are using struct fw_desc.
*/
struct fw_sec {
* previous name and uses the new format.
*/
if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
- char rev_step[2] = {
- 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev), 0
- };
-
- /* A-step doesn't have an indication */
- if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP)
- rev_step[0] = 0;
+ char rev_step = 'A' + CSR_HW_REV_STEP(drv->trans->hw_rev);
snprintf(drv->firmware_name, sizeof(drv->firmware_name),
- "%s%s-%s.ucode", name_pre, rev_step, tag);
+ "%s%c-%s.ucode", name_pre, rev_step, tag);
}
IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n",
/* Verify that uCode images will fit in card's SRAM. */
if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) >
- cfg->max_inst_size) {
+ cfg->max_inst_size) {
IWL_ERR(drv, "uCode instr len %Zd too large to fit in\n",
get_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_INST));
+ IWL_UCODE_SECTION_INST));
return -1;
}
if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) >
- cfg->max_data_size) {
+ cfg->max_data_size) {
IWL_ERR(drv, "uCode data len %Zd too large to fit in\n",
get_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_DATA));
+ IWL_UCODE_SECTION_DATA));
return -1;
}
- if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) >
- cfg->max_inst_size) {
+ if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) >
+ cfg->max_inst_size) {
IWL_ERR(drv, "uCode init instr len %Zd too large to fit in\n",
get_sec_size(pieces, IWL_UCODE_INIT,
- IWL_UCODE_SECTION_INST));
+ IWL_UCODE_SECTION_INST));
return -1;
}
if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) >
- cfg->max_data_size) {
+ cfg->max_data_size) {
IWL_ERR(drv, "uCode init data len %Zd too large to fit in\n",
get_sec_size(pieces, IWL_UCODE_REGULAR,
- IWL_UCODE_SECTION_DATA));
+ IWL_UCODE_SECTION_DATA));
return -1;
}
return 0;
const unsigned int api_max = drv->cfg->ucode_api_max;
unsigned int api_ok = drv->cfg->ucode_api_ok;
const unsigned int api_min = drv->cfg->ucode_api_min;
+ size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX];
u32 api_ver;
int i;
bool load_module = false;
}
}
+ memset(&trigger_tlv_sz, 0xff, sizeof(trigger_tlv_sz));
+
+ trigger_tlv_sz[FW_DBG_TRIGGER_MISSED_BEACONS] =
+ sizeof(struct iwl_fw_dbg_trigger_missed_bcon);
+ trigger_tlv_sz[FW_DBG_TRIGGER_CHANNEL_SWITCH] = 0;
+ trigger_tlv_sz[FW_DBG_TRIGGER_FW_NOTIF] =
+ sizeof(struct iwl_fw_dbg_trigger_cmd);
+ trigger_tlv_sz[FW_DBG_TRIGGER_MLME] =
+ sizeof(struct iwl_fw_dbg_trigger_mlme);
+ trigger_tlv_sz[FW_DBG_TRIGGER_STATS] =
+ sizeof(struct iwl_fw_dbg_trigger_stats);
+ trigger_tlv_sz[FW_DBG_TRIGGER_RSSI] =
+ sizeof(struct iwl_fw_dbg_trigger_low_rssi);
+ trigger_tlv_sz[FW_DBG_TRIGGER_TXQ_TIMERS] =
+ sizeof(struct iwl_fw_dbg_trigger_txq_timer);
+ trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] =
+ sizeof(struct iwl_fw_dbg_trigger_time_event);
+
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
if (pieces->dbg_trigger_tlv[i]) {
+ /*
+ * If the trigger isn't long enough, WARN and exit.
+ * Someone is trying to debug something and he won't
+ * be able to catch the bug he is trying to chase.
+ * We'd better be noisy to be sure he knows what's
+ * going on.
+ */
+ if (WARN_ON(pieces->dbg_trigger_tlv_len[i] <
+ (trigger_tlv_sz[i] +
+ sizeof(struct iwl_fw_dbg_trigger_tlv))))
+ goto out_free_fw;
drv->fw.dbg_trigger_tlv_len[i] =
pieces->dbg_trigger_tlv_len[i];
drv->fw.dbg_trigger_tlv[i] =
op->name, err);
#endif
}
+ kfree(pieces);
return;
try_again:
bool, S_IRUGO);
MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)");
+module_param_named(lar_disable, iwlwifi_mod_params.lar_disable,
+ bool, S_IRUGO);
+MODULE_PARM_DESC(lar_disable, "disable LAR functionality (default: N)");
+
module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable,
bool, S_IRUGO | S_IWUSR);
#ifdef CONFIG_IWLWIFI_UAPSD
/* for all modules */
#define DRV_NAME "iwlwifi"
-#define DRV_COPYRIGHT "Copyright(c) 2003- 2014 Intel Corporation"
+#define DRV_COPYRIGHT "Copyright(c) 2003- 2015 Intel Corporation"
#define DRV_AUTHOR "<ilw@linux.intel.com>"
/* radio config bits (actual values from NVM definition) */
* starts the driver: fetches the firmware. This should be called by bus
* specific system flows implementations. For example, the bus specific probe
* function should do bus related operations only, and then call to this
- * function. It returns the driver object or %NULL if an error occured.
+ * function. It returns the driver object or %NULL if an error occurred.
*/
struct iwl_drv *iwl_drv_start(struct iwl_trans *trans,
const struct iwl_cfg *cfg);
u32 nvm_version;
s8 max_tx_pwr_half_dbm;
+ bool lar_enabled;
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
struct ieee80211_channel channels[];
};
otpgp = iwl_read32(trans, CSR_OTP_GP_REG);
if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
/* stop in this case */
- /* set the uncorrectable OTP ECC bit for acknowledgement */
+ /* set the uncorrectable OTP ECC bit for acknowledgment */
iwl_set_bit(trans, CSR_OTP_GP_REG,
CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n");
}
if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
/* continue in this case */
- /* set the correctable OTP ECC bit for acknowledgement */
+ /* set the correctable OTP ECC bit for acknowledgment */
iwl_set_bit(trans, CSR_OTP_GP_REG,
CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
IWL_ERR(trans, "Correctable OTP ECC error, continue read\n");
#define RX_LOW_WATERMARK 8
/**
- * struct iwl_rb_status - reseve buffer status
+ * struct iwl_rb_status - reserve buffer status
* host memory mapped FH registers
* @closed_rb_num [0:11] - Indicates the index of the RB which was closed
* @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed
* struct iwl_fw_error_dump_fw_mon - FW monitor data
* @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
* @fw_mon_base_ptr: base pointer of the data
- * @fw_mon_cycle_cnt: number of wrap arounds
+ * @fw_mon_cycle_cnt: number of wraparounds
* @reserved: for future use
* @data: captured data
*/
* @FW_DBG_TRIGGER_CHANNEL_SWITCH: trigger log collection upon channel switch.
* @FW_DBG_TRIGGER_FW_NOTIF: trigger log collection when the firmware sends a
* command response or a notification.
- * @FW_DB_TRIGGER_RESERVED: reserved
+ * @FW_DBG_TRIGGER_MLME: trigger log collection upon MLME event.
* @FW_DBG_TRIGGER_STATS: trigger log collection upon statistics threshold.
* @FW_DBG_TRIGGER_RSSI: trigger log collection when the rssi of the beacon
* goes below a threshold.
+ * @FW_DBG_TRIGGER_TXQ_TIMERS: configures the timers for the Tx queue hang
+ * detection.
+ * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related
+ * events.
*/
enum iwl_fw_dbg_trigger {
FW_DBG_TRIGGER_INVALID = 0,
FW_DBG_TRIGGER_MISSED_BEACONS,
FW_DBG_TRIGGER_CHANNEL_SWITCH,
FW_DBG_TRIGGER_FW_NOTIF,
- FW_DB_TRIGGER_RESERVED,
+ FW_DBG_TRIGGER_MLME,
FW_DBG_TRIGGER_STATS,
FW_DBG_TRIGGER_RSSI,
+ FW_DBG_TRIGGER_TXQ_TIMERS,
+ FW_DBG_TRIGGER_TIME_EVENT,
/* must be last */
FW_DBG_TRIGGER_MAX,
* enum iwl_ucode_tlv_flag - ucode API flags
* @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
* was a separate TLV but moved here to save space.
- * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
+ * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behavior on hidden SSID,
* treats good CRC threshold as a boolean
* @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
* @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
/**
* enum iwl_ucode_tlv_api - ucode api
* @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
- * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
- * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif.
* @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
* longer than the passive one, which is essential for fragmented scan.
+ * @IWL_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source.
* IWL_UCODE_TLV_API_HDC_PHASE_0: ucode supports finer configuration of LTR
* @IWL_UCODE_TLV_API_BASIC_DWELL: use only basic dwell time in scan command,
* regardless of the band or the number of the probes. FW will calculate
*/
enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3),
- IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5),
- IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF = BIT(7),
IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8),
+ IWL_UCODE_TLV_API_WIFI_MCC_UPDATE = BIT(9),
IWL_UCODE_TLV_API_HDC_PHASE_0 = BIT(10),
IWL_UCODE_TLV_API_BASIC_DWELL = BIT(13),
IWL_UCODE_TLV_API_SCD_CFG = BIT(15),
* @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
* @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics
* @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running
+ * @IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different
+ * sources for the MCC. This TLV bit is a future replacement to
+ * IWL_UCODE_TLV_API_WIFI_MCC_UPDATE. When either is set, multi-source LAR
+ * is supported.
+ * @IWL_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC
*/
enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0),
IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18),
IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = BIT(22),
IWL_UCODE_TLV_CAPA_BT_COEX_PLCR = BIT(28),
+ IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC = BIT(29),
+ IWL_UCODE_TLV_CAPA_BT_COEX_RRC = BIT(30),
};
/* The default calibrate table size if not specified by firmware file */
__le32 rssi;
} __packed;
+/**
+ * struct iwl_fw_dbg_trigger_mlme - configures trigger for mlme events
+ * @stop_auth_denied: number of denied authentication to collect
+ * @stop_auth_timeout: number of authentication timeout to collect
+ * @stop_rx_deauth: number of Rx deauth before to collect
+ * @stop_tx_deauth: number of Tx deauth before to collect
+ * @stop_assoc_denied: number of denied association to collect
+ * @stop_assoc_timeout: number of association timeout to collect
+ * @stop_connection_loss: number of connection loss to collect
+ * @start_auth_denied: number of denied authentication to start recording
+ * @start_auth_timeout: number of authentication timeout to start recording
+ * @start_rx_deauth: number of Rx deauth to start recording
+ * @start_tx_deauth: number of Tx deauth to start recording
+ * @start_assoc_denied: number of denied association to start recording
+ * @start_assoc_timeout: number of association timeout to start recording
+ * @start_connection_loss: number of connection loss to start recording
+ */
+struct iwl_fw_dbg_trigger_mlme {
+ u8 stop_auth_denied;
+ u8 stop_auth_timeout;
+ u8 stop_rx_deauth;
+ u8 stop_tx_deauth;
+
+ u8 stop_assoc_denied;
+ u8 stop_assoc_timeout;
+ u8 stop_connection_loss;
+ u8 reserved;
+
+ u8 start_auth_denied;
+ u8 start_auth_timeout;
+ u8 start_rx_deauth;
+ u8 start_tx_deauth;
+
+ u8 start_assoc_denied;
+ u8 start_assoc_timeout;
+ u8 start_connection_loss;
+ u8 reserved2;
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_txq_timer - configures the Tx queue's timer
+ * @command_queue: timeout for the command queue in ms
+ * @bss: timeout for the queues of a BSS (except for TDLS queues) in ms
+ * @softap: timeout for the queues of a softAP in ms
+ * @p2p_go: timeout for the queues of a P2P GO in ms
+ * @p2p_client: timeout for the queues of a P2P client in ms
+ * @p2p_device: timeout for the queues of a P2P device in ms
+ * @ibss: timeout for the queues of an IBSS in ms
+ * @tdls: timeout for the queues of a TDLS station in ms
+ */
+struct iwl_fw_dbg_trigger_txq_timer {
+ __le32 command_queue;
+ __le32 bss;
+ __le32 softap;
+ __le32 p2p_go;
+ __le32 p2p_client;
+ __le32 p2p_device;
+ __le32 ibss;
+ __le32 tdls;
+ __le32 reserved[4];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger_time_event - configures a time event trigger
+ * time_Events: a list of tuples <id, action_bitmap>. The driver will issue a
+ * trigger each time a time event notification that relates to time event
+ * id with one of the actions in the bitmap is received and
+ * BIT(notif->status) is set in status_bitmap.
+ *
+ */
+struct iwl_fw_dbg_trigger_time_event {
+ struct {
+ __le32 id;
+ __le32 action_bitmap;
+ __le32 status_bitmap;
+ } __packed time_events[16];
+} __packed;
+
/**
* struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
* @id: conf id
void iwl_force_nmi(struct iwl_trans *trans)
{
- /*
- * In HW previous to the 8000 HW family, and in the 8000 HW family
- * itself when the revision step==0, the DEVICE_SET_NMI_REG is used
- * to force an NMI. Otherwise, a different register -
- * DEVICE_SET_NMI_8000B_REG - is used.
- */
- if ((trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) ||
- (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)) {
+ if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
iwl_write_prph(trans, DEVICE_SET_NMI_REG,
DEVICE_SET_NMI_VAL_DRV);
iwl_write_prph(trans, DEVICE_SET_NMI_REG,
DEVICE_SET_NMI_VAL_HW);
} else {
- iwl_write_prph(trans, DEVICE_SET_NMI_8000B_REG,
- DEVICE_SET_NMI_8000B_VAL);
+ iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG,
+ DEVICE_SET_NMI_8000_VAL);
+ iwl_write_prph(trans, DEVICE_SET_NMI_REG,
+ DEVICE_SET_NMI_VAL_DRV);
}
}
IWL_EXPORT_SYMBOL(iwl_force_nmi);
* @debug_level: levels are IWL_DL_*
* @ant_coupling: antenna coupling in dB, default = 0
* @d0i3_disable: disable d0i3, default = 1,
+ * @lar_disable: disable LAR (regulatory), default = 0
* @fw_monitor: allow to use firmware monitor
*/
struct iwl_mod_params {
char *nvm_file;
bool uapsd_disable;
bool d0i3_disable;
+ bool lar_disable;
bool fw_monitor;
};
/* NVM SW-Section offset (in words) definitions */
NVM_SW_SECTION_FAMILY_8000 = 0x1C0,
NVM_VERSION_FAMILY_8000 = 0,
- RADIO_CFG_FAMILY_8000 = 2,
- SKU_FAMILY_8000 = 4,
- N_HW_ADDRS_FAMILY_8000 = 5,
+ RADIO_CFG_FAMILY_8000 = 0,
+ SKU_FAMILY_8000 = 2,
+ N_HW_ADDRS_FAMILY_8000 = 3,
/* NVM REGULATORY -Section offset (in words) definitions */
NVM_CHANNELS_FAMILY_8000 = 0,
+ NVM_LAR_OFFSET_FAMILY_8000_OLD = 0x4C7,
+ NVM_LAR_OFFSET_FAMILY_8000 = 0x507,
+ NVM_LAR_ENABLED_FAMILY_8000 = 0x7,
/* NVM calibration section offset (in words) definitions */
NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8,
#define NUM_2GHZ_CHANNELS_FAMILY_8000 14
#define FIRST_2GHZ_HT_MINUS 5
#define LAST_2GHZ_HT_PLUS 9
-#define LAST_5GHZ_HT 161
+#define LAST_5GHZ_HT 165
+#define LAST_5GHZ_HT_FAMILY_8000 181
+#define N_HW_ADDR_MASK 0xF
/* rate data (static) */
static struct ieee80211_rate iwl_cfg80211_rates[] = {
#define CHECK_AND_PRINT_I(x) \
((ch_flags & NVM_CHANNEL_##x) ? # x " " : "")
+static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, bool is_5ghz,
+ u16 nvm_flags, const struct iwl_cfg *cfg)
+{
+ u32 flags = IEEE80211_CHAN_NO_HT40;
+ u32 last_5ghz_ht = LAST_5GHZ_HT;
+
+ if (cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;
+
+ if (!is_5ghz && (nvm_flags & NVM_CHANNEL_40MHZ)) {
+ if (ch_num <= LAST_2GHZ_HT_PLUS)
+ flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+ if (ch_num >= FIRST_2GHZ_HT_MINUS)
+ flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+ } else if (ch_num <= last_5ghz_ht && (nvm_flags & NVM_CHANNEL_40MHZ)) {
+ if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+ flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+ else
+ flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+ }
+ if (!(nvm_flags & NVM_CHANNEL_80MHZ))
+ flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (!(nvm_flags & NVM_CHANNEL_160MHZ))
+ flags |= IEEE80211_CHAN_NO_160MHZ;
+
+ if (!(nvm_flags & NVM_CHANNEL_IBSS))
+ flags |= IEEE80211_CHAN_NO_IR;
+
+ if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
+ flags |= IEEE80211_CHAN_NO_IR;
+
+ if (nvm_flags & NVM_CHANNEL_RADAR)
+ flags |= IEEE80211_CHAN_RADAR;
+
+ if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
+ flags |= IEEE80211_CHAN_INDOOR_ONLY;
+
+ /* Set the GO concurrent flag only in case that NO_IR is set.
+ * Otherwise it is meaningless
+ */
+ if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
+ (flags & IEEE80211_CHAN_NO_IR))
+ flags |= IEEE80211_CHAN_GO_CONCURRENT;
+
+ return flags;
+}
+
static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
- const __le16 * const nvm_ch_flags)
+ const __le16 * const nvm_ch_flags,
+ bool lar_supported)
{
int ch_idx;
int n_channels = 0;
if (ch_idx >= num_2ghz_channels &&
!data->sku_cap_band_52GHz_enable)
- ch_flags &= ~NVM_CHANNEL_VALID;
+ continue;
- if (!(ch_flags & NVM_CHANNEL_VALID)) {
+ if (!lar_supported && !(ch_flags & NVM_CHANNEL_VALID)) {
+ /*
+ * Channels might become valid later if lar is
+ * supported, hence we still want to add them to
+ * the list of supported channels to cfg80211.
+ */
IWL_DEBUG_EEPROM(dev,
"Ch. %d Flags %x [%sGHz] - No traffic\n",
nvm_chan[ch_idx],
ieee80211_channel_to_frequency(
channel->hw_value, channel->band);
- /* TODO: Need to be dependent to the NVM */
- channel->flags = IEEE80211_CHAN_NO_HT40;
- if (ch_idx < num_2ghz_channels &&
- (ch_flags & NVM_CHANNEL_40MHZ)) {
- if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
- channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
- if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)
- channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
- } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT &&
- (ch_flags & NVM_CHANNEL_40MHZ)) {
- if ((ch_idx - num_2ghz_channels) % 2 == 0)
- channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
- else
- channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
- }
- if (!(ch_flags & NVM_CHANNEL_80MHZ))
- channel->flags |= IEEE80211_CHAN_NO_80MHZ;
- if (!(ch_flags & NVM_CHANNEL_160MHZ))
- channel->flags |= IEEE80211_CHAN_NO_160MHZ;
-
- if (!(ch_flags & NVM_CHANNEL_IBSS))
- channel->flags |= IEEE80211_CHAN_NO_IR;
-
- if (!(ch_flags & NVM_CHANNEL_ACTIVE))
- channel->flags |= IEEE80211_CHAN_NO_IR;
-
- if (ch_flags & NVM_CHANNEL_RADAR)
- channel->flags |= IEEE80211_CHAN_RADAR;
-
- if (ch_flags & NVM_CHANNEL_INDOOR_ONLY)
- channel->flags |= IEEE80211_CHAN_INDOOR_ONLY;
-
- /* Set the GO concurrent flag only in case that NO_IR is set.
- * Otherwise it is meaningless
- */
- if ((ch_flags & NVM_CHANNEL_GO_CONCURRENT) &&
- (channel->flags & IEEE80211_CHAN_NO_IR))
- channel->flags |= IEEE80211_CHAN_GO_CONCURRENT;
-
/* Initialize regulatory-based run-time data */
/*
*/
channel->max_power = IWL_DEFAULT_MAX_TX_POWER;
is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
+
+ /* don't put limitations in case we're using LAR */
+ if (!lar_supported)
+ channel->flags = iwl_get_channel_flags(nvm_chan[ch_idx],
+ ch_idx, is_5ghz,
+ ch_flags, cfg);
+ else
+ channel->flags = 0;
+
IWL_DEBUG_EEPROM(dev,
"Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
channel->hw_value,
static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
- const __le16 *ch_section, bool enable_vht,
- u8 tx_chains, u8 rx_chains)
+ const __le16 *ch_section,
+ u8 tx_chains, u8 rx_chains, bool lar_supported)
{
int n_channels;
int n_used = 0;
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
n_channels = iwl_init_channel_map(
dev, cfg, data,
- &ch_section[NVM_CHANNELS]);
+ &ch_section[NVM_CHANNELS], lar_supported);
else
n_channels = iwl_init_channel_map(
dev, cfg, data,
- &ch_section[NVM_CHANNELS_FAMILY_8000]);
+ &ch_section[NVM_CHANNELS_FAMILY_8000],
+ lar_supported);
sband = &data->bands[IEEE80211_BAND_2GHZ];
sband->band = IEEE80211_BAND_2GHZ;
IEEE80211_BAND_5GHZ);
iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ,
tx_chains, rx_chains);
- if (enable_vht)
+ if (data->sku_cap_11ac_enable)
iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap,
tx_chains, rx_chains);
n_used, n_channels);
}
-static int iwl_get_sku(const struct iwl_cfg *cfg,
- const __le16 *nvm_sw)
+static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+ const __le16 *phy_sku)
{
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
return le16_to_cpup(nvm_sw + SKU);
- else
- return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000));
+
+ return le32_to_cpup((__le32 *)(phy_sku + SKU_FAMILY_8000));
}
-static int iwl_get_nvm_version(const struct iwl_cfg *cfg,
- const __le16 *nvm_sw)
+static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
{
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
return le16_to_cpup(nvm_sw + NVM_VERSION);
NVM_VERSION_FAMILY_8000));
}
-static int iwl_get_radio_cfg(const struct iwl_cfg *cfg,
- const __le16 *nvm_sw)
+static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw,
+ const __le16 *phy_sku)
{
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
return le16_to_cpup(nvm_sw + RADIO_CFG);
- else
- return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000));
+
+ return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000));
+
}
-#define N_HW_ADDRS_MASK_FAMILY_8000 0xF
-static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg,
- const __le16 *nvm_sw)
+static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw)
{
+ int n_hw_addr;
+
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
return le16_to_cpup(nvm_sw + N_HW_ADDRS);
- else
- return le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000))
- & N_HW_ADDRS_MASK_FAMILY_8000;
+
+ n_hw_addr = le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000));
+
+ return n_hw_addr & N_HW_ADDR_MASK;
}
static void iwl_set_radio_cfg(const struct iwl_cfg *cfg,
const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
const __le16 *mac_override,
- const __le16 *nvm_hw)
+ const __le16 *nvm_hw,
+ u32 mac_addr0, u32 mac_addr1)
{
const u8 *hw_addr;
}
if (nvm_hw) {
- /* read the MAC address from OTP */
- if (!dev_is_pci(dev) || (data->nvm_version < 0xE08)) {
- /* read the mac address from the WFPM location */
- hw_addr = (const u8 *)(nvm_hw +
- HW_ADDR0_WFPM_FAMILY_8000);
- data->hw_addr[0] = hw_addr[3];
- data->hw_addr[1] = hw_addr[2];
- data->hw_addr[2] = hw_addr[1];
- data->hw_addr[3] = hw_addr[0];
-
- hw_addr = (const u8 *)(nvm_hw +
- HW_ADDR1_WFPM_FAMILY_8000);
- data->hw_addr[4] = hw_addr[1];
- data->hw_addr[5] = hw_addr[0];
- } else if ((data->nvm_version >= 0xE08) &&
- (data->nvm_version < 0xE0B)) {
- /* read "reverse order" from the PCIe location */
- hw_addr = (const u8 *)(nvm_hw +
- HW_ADDR0_PCIE_FAMILY_8000);
- data->hw_addr[5] = hw_addr[2];
- data->hw_addr[4] = hw_addr[1];
- data->hw_addr[3] = hw_addr[0];
-
- hw_addr = (const u8 *)(nvm_hw +
- HW_ADDR1_PCIE_FAMILY_8000);
- data->hw_addr[2] = hw_addr[3];
- data->hw_addr[1] = hw_addr[2];
- data->hw_addr[0] = hw_addr[1];
- } else {
- /* read from the PCIe location */
- hw_addr = (const u8 *)(nvm_hw +
- HW_ADDR0_PCIE_FAMILY_8000);
- data->hw_addr[5] = hw_addr[0];
- data->hw_addr[4] = hw_addr[1];
- data->hw_addr[3] = hw_addr[2];
-
- hw_addr = (const u8 *)(nvm_hw +
- HW_ADDR1_PCIE_FAMILY_8000);
- data->hw_addr[2] = hw_addr[1];
- data->hw_addr[1] = hw_addr[2];
- data->hw_addr[0] = hw_addr[3];
- }
+ /* read the MAC address from HW resisters */
+ hw_addr = (const u8 *)&mac_addr0;
+ data->hw_addr[0] = hw_addr[3];
+ data->hw_addr[1] = hw_addr[2];
+ data->hw_addr[2] = hw_addr[1];
+ data->hw_addr[3] = hw_addr[0];
+
+ hw_addr = (const u8 *)&mac_addr1;
+ data->hw_addr[4] = hw_addr[1];
+ data->hw_addr[5] = hw_addr[0];
+
if (!is_valid_ether_addr(data->hw_addr))
IWL_ERR_DEV(dev,
"mac address from hw section is not valid\n");
iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw,
const __le16 *nvm_calib, const __le16 *regulatory,
- const __le16 *mac_override, u8 tx_chains, u8 rx_chains)
+ const __le16 *mac_override, const __le16 *phy_sku,
+ u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
+ u32 mac_addr0, u32 mac_addr1)
{
struct iwl_nvm_data *data;
u32 sku;
u32 radio_cfg;
+ u16 lar_config;
if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
data = kzalloc(sizeof(*data) +
data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw);
- radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw);
+ radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw, phy_sku);
iwl_set_radio_cfg(cfg, data, radio_cfg);
if (data->valid_tx_ant)
tx_chains &= data->valid_tx_ant;
if (data->valid_rx_ant)
rx_chains &= data->valid_rx_ant;
- sku = iwl_get_sku(cfg, nvm_sw);
+ sku = iwl_get_sku(cfg, nvm_sw, phy_sku);
data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
- data->sku_cap_11ac_enable = sku & NVM_SKU_CAP_11AC_ENABLE;
if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
data->sku_cap_11n_enable = false;
+ data->sku_cap_11ac_enable = data->sku_cap_11n_enable &&
+ (sku & NVM_SKU_CAP_11AC_ENABLE);
data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw);
iwl_set_hw_address(cfg, data, nvm_hw);
iwl_init_sbands(dev, cfg, data, nvm_sw,
- sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
- rx_chains);
+ tx_chains, rx_chains, lar_fw_supported);
} else {
+ u16 lar_offset = data->nvm_version < 0xE39 ?
+ NVM_LAR_OFFSET_FAMILY_8000_OLD :
+ NVM_LAR_OFFSET_FAMILY_8000;
+
+ lar_config = le16_to_cpup(regulatory + lar_offset);
+ data->lar_enabled = !!(lar_config &
+ NVM_LAR_ENABLED_FAMILY_8000);
+
/* MAC address in family 8000 */
iwl_set_hw_address_family_8000(dev, cfg, data, mac_override,
- nvm_hw);
+ nvm_hw, mac_addr0, mac_addr1);
iwl_init_sbands(dev, cfg, data, regulatory,
- sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
- rx_chains);
+ tx_chains, rx_chains,
+ lar_fw_supported && data->lar_enabled);
}
data->calib_version = 255;
return data;
}
IWL_EXPORT_SYMBOL(iwl_parse_nvm_data);
+
+static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan,
+ int ch_idx, u16 nvm_flags,
+ const struct iwl_cfg *cfg)
+{
+ u32 flags = NL80211_RRF_NO_HT40;
+ u32 last_5ghz_ht = LAST_5GHZ_HT;
+
+ if (cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ last_5ghz_ht = LAST_5GHZ_HT_FAMILY_8000;
+
+ if (ch_idx < NUM_2GHZ_CHANNELS &&
+ (nvm_flags & NVM_CHANNEL_40MHZ)) {
+ if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS)
+ flags &= ~NL80211_RRF_NO_HT40PLUS;
+ if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS)
+ flags &= ~NL80211_RRF_NO_HT40MINUS;
+ } else if (nvm_chan[ch_idx] <= last_5ghz_ht &&
+ (nvm_flags & NVM_CHANNEL_40MHZ)) {
+ if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+ flags &= ~NL80211_RRF_NO_HT40PLUS;
+ else
+ flags &= ~NL80211_RRF_NO_HT40MINUS;
+ }
+
+ if (!(nvm_flags & NVM_CHANNEL_80MHZ))
+ flags |= NL80211_RRF_NO_80MHZ;
+ if (!(nvm_flags & NVM_CHANNEL_160MHZ))
+ flags |= NL80211_RRF_NO_160MHZ;
+
+ if (!(nvm_flags & NVM_CHANNEL_ACTIVE))
+ flags |= NL80211_RRF_NO_IR;
+
+ if (nvm_flags & NVM_CHANNEL_RADAR)
+ flags |= NL80211_RRF_DFS;
+
+ if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY)
+ flags |= NL80211_RRF_NO_OUTDOOR;
+
+ /* Set the GO concurrent flag only in case that NO_IR is set.
+ * Otherwise it is meaningless
+ */
+ if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) &&
+ (flags & NL80211_RRF_NO_IR))
+ flags |= NL80211_RRF_GO_CONCURRENT;
+
+ return flags;
+}
+
+struct ieee80211_regdomain *
+iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
+ int num_of_ch, __le32 *channels, u16 fw_mcc)
+{
+ int ch_idx;
+ u16 ch_flags, prev_ch_flags = 0;
+ const u8 *nvm_chan = cfg->device_family == IWL_DEVICE_FAMILY_8000 ?
+ iwl_nvm_channels_family_8000 : iwl_nvm_channels;
+ struct ieee80211_regdomain *regd;
+ int size_of_regd;
+ struct ieee80211_reg_rule *rule;
+ enum ieee80211_band band;
+ int center_freq, prev_center_freq = 0;
+ int valid_rules = 0;
+ bool new_rule;
+ int max_num_ch = cfg->device_family == IWL_DEVICE_FAMILY_8000 ?
+ IWL_NUM_CHANNELS_FAMILY_8000 : IWL_NUM_CHANNELS;
+
+ if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES))
+ return ERR_PTR(-EINVAL);
+
+ if (WARN_ON(num_of_ch > max_num_ch))
+ num_of_ch = max_num_ch;
+
+ IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n",
+ num_of_ch);
+
+ /* build a regdomain rule for every valid channel */
+ size_of_regd =
+ sizeof(struct ieee80211_regdomain) +
+ num_of_ch * sizeof(struct ieee80211_reg_rule);
+
+ regd = kzalloc(size_of_regd, GFP_KERNEL);
+ if (!regd)
+ return ERR_PTR(-ENOMEM);
+
+ for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
+ ch_flags = (u16)__le32_to_cpup(channels + ch_idx);
+ band = (ch_idx < NUM_2GHZ_CHANNELS) ?
+ IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+ center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx],
+ band);
+ new_rule = false;
+
+ if (!(ch_flags & NVM_CHANNEL_VALID)) {
+ IWL_DEBUG_DEV(dev, IWL_DL_LAR,
+ "Ch. %d Flags %x [%sGHz] - No traffic\n",
+ nvm_chan[ch_idx],
+ ch_flags,
+ (ch_idx >= NUM_2GHZ_CHANNELS) ?
+ "5.2" : "2.4");
+ continue;
+ }
+
+ /* we can't continue the same rule */
+ if (ch_idx == 0 || prev_ch_flags != ch_flags ||
+ center_freq - prev_center_freq > 20) {
+ valid_rules++;
+ new_rule = true;
+ }
+
+ rule = ®d->reg_rules[valid_rules - 1];
+
+ if (new_rule)
+ rule->freq_range.start_freq_khz =
+ MHZ_TO_KHZ(center_freq - 10);
+
+ rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10);
+
+ /* this doesn't matter - not used by FW */
+ rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
+ rule->power_rule.max_eirp =
+ DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER);
+
+ rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx,
+ ch_flags, cfg);
+
+ /* rely on auto-calculation to merge BW of contiguous chans */
+ rule->flags |= NL80211_RRF_AUTO_BW;
+ rule->freq_range.max_bandwidth_khz = 0;
+
+ prev_ch_flags = ch_flags;
+ prev_center_freq = center_freq;
+
+ IWL_DEBUG_DEV(dev, IWL_DL_LAR,
+ "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n",
+ center_freq,
+ band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4",
+ CHECK_AND_PRINT_I(VALID),
+ CHECK_AND_PRINT_I(ACTIVE),
+ CHECK_AND_PRINT_I(RADAR),
+ CHECK_AND_PRINT_I(WIDE),
+ CHECK_AND_PRINT_I(40MHZ),
+ CHECK_AND_PRINT_I(80MHZ),
+ CHECK_AND_PRINT_I(160MHZ),
+ CHECK_AND_PRINT_I(INDOOR_ONLY),
+ CHECK_AND_PRINT_I(GO_CONCURRENT),
+ ch_flags,
+ ((ch_flags & NVM_CHANNEL_ACTIVE) &&
+ !(ch_flags & NVM_CHANNEL_RADAR))
+ ? "" : "not ");
+ }
+
+ regd->n_reg_rules = valid_rules;
+
+ /* set alpha2 from FW. */
+ regd->alpha2[0] = fw_mcc >> 8;
+ regd->alpha2[1] = fw_mcc & 0xff;
+
+ return regd;
+}
+IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info);
#ifndef __iwl_nvm_parse_h__
#define __iwl_nvm_parse_h__
+#include <net/cfg80211.h>
#include "iwl-eeprom-parse.h"
/**
iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
const __le16 *nvm_hw, const __le16 *nvm_sw,
const __le16 *nvm_calib, const __le16 *regulatory,
- const __le16 *mac_override, u8 tx_chains, u8 rx_chains);
+ const __le16 *mac_override, const __le16 *phy_sku,
+ u8 tx_chains, u8 rx_chains, bool lar_fw_supported,
+ u32 mac_addr0, u32 mac_addr1);
+
+/**
+ * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW
+ *
+ * This function parses the regulatory channel data received as a
+ * MCC_UPDATE_CMD command. It returns a newly allocation regulatory domain,
+ * to be fed into the regulatory core. An ERR_PTR is returned on error.
+ * If not given to the regulatory core, the user is responsible for freeing
+ * the regdomain returned here with kfree.
+ */
+struct ieee80211_regdomain *
+iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg,
+ int num_of_ch, __le32 *channels, u16 fw_mcc);
#endif /* __iwl_nvm_parse_h__ */
* The operational mode has a very simple life cycle.
*
* 1) The driver layer (iwl-drv.c) chooses the op_mode based on the
- * capabilities advertized by the fw file (in TLV format).
+ * capabilities advertised by the fw file (in TLV format).
* 2) The driver layer starts the op_mode (ops->start)
* 3) The op_mode registers mac80211
* 4) The op_mode is governed by mac80211
* May sleep
* @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the
* HCMD this Rx responds to. Can't sleep.
- * @napi_add: NAPI initialisation. The transport is fully responsible for NAPI,
+ * @napi_add: NAPI initialization. The transport is fully responsible for NAPI,
* but the higher layers need to know about it (in particular mac80211 to
* to able to call the right NAPI RX functions); this function is needed
* to eventually call netif_napi_add() with higher layer involvement.
} __packed;
/*
- * phy db - Receieve phy db chunk after calibrations
+ * phy db - Receive phy db chunk after calibrations
*/
struct iwl_calib_res_notif_phy_db {
__le16 type;
#define DEVICE_SET_NMI_REG 0x00a01c30
#define DEVICE_SET_NMI_VAL_HW BIT(0)
#define DEVICE_SET_NMI_VAL_DRV BIT(7)
-#define DEVICE_SET_NMI_8000B_REG 0x00a01c24
-#define DEVICE_SET_NMI_8000B_VAL 0x1000000
+#define DEVICE_SET_NMI_8000_REG 0x00a01c24
+#define DEVICE_SET_NMI_8000_VAL 0x1000000
/* Shared registers (0x0..0x3ff, via target indirect or periphery */
#define SHR_BASE 0x00a10000
#define OSC_CLK (0xa04068)
#define OSC_CLK_FORCE_CONTROL (0x8)
-/* SECURE boot registers */
-#define LMPM_SECURE_BOOT_CONFIG_ADDR (0x100)
-enum secure_boot_config_reg {
- LMPM_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001,
- LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ = 0x00000002,
-};
-
-#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0 (0xA01E30)
-#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR (0x1E30)
-#define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR (0x1E34)
-enum secure_boot_status_reg {
- LMPM_SECURE_BOOT_CPU_STATUS_VERF_STATUS = 0x00000001,
- LMPM_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED = 0x00000002,
- LMPM_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS = 0x00000004,
- LMPM_SECURE_BOOT_CPU_STATUS_VERF_FAIL = 0x00000008,
- LMPM_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL = 0x00000010,
- LMPM_SECURE_BOOT_STATUS_SUCCESS = 0x00000003,
-};
-
#define FH_UCODE_LOAD_STATUS (0x1AF0)
#define CSR_UCODE_LOAD_STATUS_ADDR (0x1E70)
enum secure_load_status_reg {
#define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000)
#define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400)
-#define LMPM_SECURE_TIME_OUT (100) /* 10 micro */
-
/* Rx FIFO */
#define RXF_SIZE_ADDR (0xa00c88)
#define RXF_RD_D_SPACE (0xa00c40)
#define DBGC_IN_SAMPLE (0xa03c00)
+/* enable the ID buf for read */
+#define WFPM_PS_CTL_CLR 0xA0300C
+#define WFMP_MAC_ADDR_0 0xA03080
+#define WFMP_MAC_ADDR_1 0xA03084
+#define LMPM_PMG_EN 0xA01CEC
+#define RADIO_REG_SYS_MANUAL_DFT_0 0xAD4078
+#define RFIC_REG_RD 0xAD0470
+#define WFPM_CTRL_REG 0xA03030
+enum {
+ ENABLE_WFPM = BIT(31),
+ WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK = 0x80000000,
+};
+
+#define AUX_MISC_REG 0xA200B0
+enum {
+ HW_STEP_LOCATION_BITS = 24,
+};
+
+#define AUX_MISC_MASTER1_EN 0xA20818
+enum aux_misc_master1_en {
+ AUX_MISC_MASTER1_EN_SBE_MSK = 0x1,
+};
+
+#define AUX_MISC_MASTER1_SMPHR_STATUS 0xA20800
+#define RSA_ENABLE 0xA24B08
+#define PREG_AUX_BUS_WPROT_0 0xA04CC0
+
/* FW chicken bits */
#define LMPM_CHICK 0xA01FF8
enum {
/**
* DOC: Transport layer - what is it ?
*
- * The tranport layer is the layer that deals with the HW directly. It provides
+ * The transport layer is the layer that deals with the HW directly. It provides
* an abstraction of the underlying HW to the upper layer. The transport layer
* doesn't provide any policy, algorithm or anything of this kind, but only
- * mechanisms to make the HW do something.It is not completely stateless but
+ * mechanisms to make the HW do something. It is not completely stateless but
* close to it.
* We will have an implementation for each different supported bus.
*/
/**
* DOC: Host command section
*
- * A host command is a commaned issued by the upper layer to the fw. There are
+ * A host command is a command issued by the upper layer to the fw. There are
* several versions of fw that have several APIs. The transport layer is
* completely agnostic to these differences.
- * The transport does provide helper functionnality (i.e. SYNC / ASYNC mode),
+ * The transport does provide helper functionality (i.e. SYNC / ASYNC mode),
*/
#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f)
#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8)
* @CMD_WANT_SKB: Not valid with CMD_ASYNC. The caller needs the buffer of
* the response. The caller needs to call iwl_free_resp when done.
* @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the
- * command queue, but after other high priority commands. valid only
+ * command queue, but after other high priority commands. Valid only
* with CMD_ASYNC.
* @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle.
* @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle.
* @txq_disable: de-configure a Tx queue to send AMPDUs
* Must be atomic
* @wait_tx_queue_empty: wait until tx queues are empty. May sleep.
+ * @freeze_txq_timer: prevents the timer of the queue from firing until the
+ * queue is set to awake. Must be atomic.
* @dbgfs_register: add the dbgfs files under this directory. Files will be
* automatically deleted.
* @write8: write a u8 to a register at offset ofs from the BAR
int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir);
int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm);
+ void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs,
+ bool freeze);
void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
* @cfg - pointer to the configuration
* @status: a bit-mask of transport status flags
* @dev - pointer to struct device * that represents the device
- * @hw_id: a u32 with the ID of the device / subdevice.
+ * @hw_id: a u32 with the ID of the device / sub-device.
* Set during transport allocation.
* @hw_id_str: a string with info about HW ID. Set during transport allocation.
* @pm_support: set to true in start_hw if link pm is supported
iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg, queue_wdg_timeout);
}
+static inline void iwl_trans_freeze_txq_timer(struct iwl_trans *trans,
+ unsigned long txqs,
+ bool freeze)
+{
+ if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
+ IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state);
+
+ if (trans->ops->freeze_txq_timer)
+ trans->ops->freeze_txq_timer(trans, txqs, freeze);
+}
+
static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans,
u32 txqs)
{
#include "mvm.h"
#include "iwl-debug.h"
-const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = {
- [BT_KILL_MSK_DEFAULT] = 0xfffffc00,
- [BT_KILL_MSK_NEVER] = 0xffffffff,
- [BT_KILL_MSK_ALWAYS] = 0,
-};
-
-const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
- {
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- },
- {
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_NEVER,
- },
- {
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_NEVER,
- },
- {
- BT_KILL_MSK_DEFAULT,
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_DEFAULT,
- },
-};
-
-const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
- {
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- },
- {
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- },
- {
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_ALWAYS,
- },
- {
- BT_KILL_MSK_DEFAULT,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_DEFAULT,
- },
-};
-
-static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
- cpu_to_le32(0xf0f0f0f0), /* 50% */
- cpu_to_le32(0xc0c0c0c0), /* 25% */
- cpu_to_le32(0xfcfcfcfc), /* 75% */
- cpu_to_le32(0xfefefefe), /* 87.5% */
-};
-
-static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
- {
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- },
- {
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- },
- {
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x40000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x44000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- },
-};
-
-static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
- {
- /* Tight */
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaeaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xcc00ff28),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xcc00aaaa),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0x00004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xf0005000),
- },
- {
- /* Loose */
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xcc00ff28),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xcc00aaaa),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0x00000000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xf0005000),
- },
- {
- /* Tx Tx disabled */
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xeeaaaaaa),
- cpu_to_le32(0xaaaaaaaa),
- cpu_to_le32(0xcc00ff28),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xcc00aaaa),
- cpu_to_le32(0x0000aaaa),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xc0004000),
- cpu_to_le32(0xf0005000),
- cpu_to_le32(0xf0005000),
- },
-};
-
/* 20MHz / 40MHz below / 40Mhz above*/
static const __le64 iwl_ci_mask[][3] = {
/* dummy entry for channel 0 */
goto send_cmd;
}
- bt_cmd->max_kill = cpu_to_le32(5);
- bt_cmd->bt4_antenna_isolation_thr =
- cpu_to_le32(IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS);
- bt_cmd->bt4_tx_tx_delta_freq_thr = cpu_to_le32(15);
- bt_cmd->bt4_tx_rx_max_freq0 = cpu_to_le32(15);
- bt_cmd->override_primary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
- bt_cmd->override_secondary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
-
mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE;
bt_cmd->mode = cpu_to_le32(mode);
bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
- if (mvm->cfg->bt_shared_single_ant)
- memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
- sizeof(iwl_single_shared_ant));
- else
- memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
- sizeof(iwl_combined_lookup));
-
- memcpy(&bt_cmd->mplut_prio_boost, iwl_bt_prio_boost,
- sizeof(iwl_bt_prio_boost));
- bt_cmd->multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0);
- bt_cmd->multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1);
-
send_cmd:
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
return ret;
}
-static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm)
-{
- struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
- u32 primary_lut = le32_to_cpu(notif->primary_ch_lut);
- u32 secondary_lut = le32_to_cpu(notif->secondary_ch_lut);
- u32 ag = le32_to_cpu(notif->bt_activity_grading);
- struct iwl_bt_coex_sw_boost_update_cmd cmd = {};
- u8 ack_kill_msk[NUM_PHY_CTX] = {};
- u8 cts_kill_msk[NUM_PHY_CTX] = {};
- int i;
-
- lockdep_assert_held(&mvm->mutex);
-
- ack_kill_msk[0] = iwl_bt_ack_kill_msk[ag][primary_lut];
- cts_kill_msk[0] = iwl_bt_cts_kill_msk[ag][primary_lut];
-
- ack_kill_msk[1] = iwl_bt_ack_kill_msk[ag][secondary_lut];
- cts_kill_msk[1] = iwl_bt_cts_kill_msk[ag][secondary_lut];
-
- /* Don't send HCMD if there is no update */
- if (!memcmp(ack_kill_msk, mvm->bt_ack_kill_msk, sizeof(ack_kill_msk)) ||
- !memcmp(cts_kill_msk, mvm->bt_cts_kill_msk, sizeof(cts_kill_msk)))
- return 0;
-
- memcpy(mvm->bt_ack_kill_msk, ack_kill_msk,
- sizeof(mvm->bt_ack_kill_msk));
- memcpy(mvm->bt_cts_kill_msk, cts_kill_msk,
- sizeof(mvm->bt_cts_kill_msk));
-
- BUILD_BUG_ON(ARRAY_SIZE(ack_kill_msk) < ARRAY_SIZE(cmd.boost_values));
-
- for (i = 0; i < ARRAY_SIZE(cmd.boost_values); i++) {
- cmd.boost_values[i].kill_ack_msk =
- cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk[i]]);
- cmd.boost_values[i].kill_cts_msk =
- cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk[i]]);
- }
-
- return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0,
- sizeof(cmd), &cmd);
-}
-
static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
bool enable)
{
IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
}
-
- if (iwl_mvm_bt_udpate_sw_boost(mvm))
- IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
}
int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_bt_rssi_iterator, &data);
-
- if (iwl_mvm_bt_udpate_sw_boost(mvm))
- IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
}
#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)
},
};
+enum iwl_bt_kill_msk {
+ BT_KILL_MSK_DEFAULT,
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_MAX,
+};
+
+static const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = {
+ [BT_KILL_MSK_DEFAULT] = 0xfffffc00,
+ [BT_KILL_MSK_NEVER] = 0xffffffff,
+ [BT_KILL_MSK_ALWAYS] = 0,
+};
+
+static const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
+ {
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ },
+ {
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_NEVER,
+ },
+ {
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_NEVER,
+ },
+ {
+ BT_KILL_MSK_DEFAULT,
+ BT_KILL_MSK_NEVER,
+ BT_KILL_MSK_DEFAULT,
+ },
+};
+
+static const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
+ {
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ },
+ {
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ },
+ {
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_ALWAYS,
+ },
+ {
+ BT_KILL_MSK_DEFAULT,
+ BT_KILL_MSK_ALWAYS,
+ BT_KILL_MSK_DEFAULT,
+ },
+};
+
struct corunning_block_luts {
u8 range;
__le32 lut20[BT_COEX_CORUN_LUT_SIZE];
if (IWL_MVM_BT_COEX_TTC)
bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC);
- if (IWL_MVM_BT_COEX_RRC)
+ if (iwl_mvm_bt_is_rrc_supported(mvm))
bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC);
if (mvm->cfg->bt_shared_single_ant)
if (ret)
IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
+ if (iwl_mvm_is_lar_supported(mvm) && iwl_mvm_init_fw_regd(mvm))
+ IWL_ERR(mvm, "Failed to initialize D3 LAR information\n");
+
return 0;
}
iwl_trans_d3_suspend(mvm->trans, test);
out:
if (ret < 0) {
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
ieee80211_restart_hw(mvm->hw);
iwl_mvm_free_nd(mvm);
}
/* RF-kill already asserted again... */
if (!cmd.resp_pkt) {
- ret = -ERFKILL;
+ fw_status = ERR_PTR(-ERFKILL);
goto out_free_resp;
}
len = iwl_rx_packet_payload_len(cmd.resp_pkt);
if (len < status_size) {
IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
- ret = -EIO;
+ fw_status = ERR_PTR(-EIO);
goto out_free_resp;
}
if (len != (status_size +
ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) {
IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
- ret = -EIO;
+ fw_status = ERR_PTR(-EIO);
goto out_free_resp;
}
out_free_resp:
iwl_free_resp(&cmd);
- return ret ? ERR_PTR(ret) : fw_status;
+ return fw_status;
}
/* releases the MVM mutex */
results->matched_profiles = le32_to_cpu(query->matched_profiles);
memcpy(results->matches, query->matches, sizeof(results->matches));
+#ifdef CPTCFG_IWLWIFI_DEBUGFS
+ mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done);
+#endif
+
out_free_resp:
iwl_free_resp(&cmd);
return ret;
/* query SRAM first in case we want event logging */
iwl_mvm_read_d3_sram(mvm);
+ /*
+ * Query the current location and source from the D3 firmware so we
+ * can play it back when we re-intiailize the D0 firmware
+ */
+ iwl_mvm_update_changed_regdom(mvm);
+
if (mvm->net_detect) {
iwl_mvm_query_netdetect_reasons(mvm, vif);
/* has unlocked the mutex, so skip that */
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (keep)
mvm->keep_vif = vif;
+#endif
/* has unlocked the mutex, so skip that */
goto out_iterate;
-#endif
}
out_unlock:
__iwl_mvm_resume(mvm, true);
rtnl_unlock();
iwl_abort_notification_waits(&mvm->notif_wait);
+ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
ieee80211_restart_hw(mvm->hw);
/* wait for restart and disconnect all interfaces */
"\tSecondary Channel Bitmap 0x%016llx\n",
le64_to_cpu(cmd->bt_secondary_ci));
- pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
- pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
- iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]);
- pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
- iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]);
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "BT Configuration CMD - 0=default, 1=never, 2=always\n");
+ pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill msk idx %d\n",
+ mvm->bt_ack_kill_msk[0]);
+ pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill msk idx %d\n",
+ mvm->bt_cts_kill_msk[0]);
} else {
struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
pos += scnprintf(buf+pos, bufsz-pos,
"\tSecondary Channel Bitmap 0x%016llx\n",
le64_to_cpu(cmd->bt_secondary_ci));
-
- pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tPrimary: ACK Kill Mask 0x%08x\n",
- iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]);
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tPrimary: CTS Kill Mask 0x%08x\n",
- iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]);
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tSecondary: ACK Kill Mask 0x%08x\n",
- iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[1]]);
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tSecondary: CTS Kill Mask 0x%08x\n",
- iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[1]]);
-
}
mutex_unlock(&mvm->mutex);
return count;
}
-static ssize_t iwl_dbgfs_enable_scan_iteration_notif_write(struct iwl_mvm *mvm,
- char *buf,
- size_t count,
- loff_t *ppos)
-{
- int val;
-
- mutex_lock(&mvm->mutex);
-
- if (kstrtoint(buf, 10, &val)) {
- mutex_unlock(&mvm->mutex);
- return -EINVAL;
- }
-
- mvm->scan_iter_notif_enabled = val;
- mutex_unlock(&mvm->mutex);
-
- return count;
-}
-
MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64);
/* Device wide debugfs entries */
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8);
-MVM_DEBUGFS_WRITE_FILE_OPS(enable_scan_iteration_notif, 8);
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR);
- MVM_DEBUGFS_ADD_FILE(enable_scan_iteration_notif, mvm->debugfs_dir,
- S_IWUSR);
+ if (!debugfs_create_bool("enable_scan_iteration_notif",
+ S_IRUSR | S_IWUSR,
+ mvm->debugfs_dir,
+ &mvm->scan_iter_notif_enabled))
+ goto err;
#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR,
mvm->debugfs_dir, &mvm->d3_wake_sysassert))
goto err;
+ if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR,
+ mvm->debugfs_dir, &mvm->last_netdetect_scans))
+ goto err;
MVM_DEBUGFS_ADD_FILE(netdetect, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
#endif
* struct iwl_bt_coex_cmd - bt coex configuration command
* @mode: enum %iwl_bt_coex_mode
* @enabled_modules: enum %iwl_bt_coex_enabled_modules
- * @max_kill: max count of Tx retries due to kill from PTA
- * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
- * should be set by default
- * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
- * should be set by default
- * @bt4_antenna_isolation_thr: antenna threshold value
- * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
- * @bt4_tx_rx_max_freq0: TxRx max frequency
- * @multiprio_lut: multi priority LUT configuration
- * @mplut_prio_boost: BT priority boost registers
- * @decision_lut: PTA decision LUT, per Prio-Ch
*
* The structure is used for the BT_COEX command.
*/
struct iwl_bt_coex_cmd {
__le32 mode;
__le32 enabled_modules;
-
- __le32 max_kill;
- __le32 override_primary_lut;
- __le32 override_secondary_lut;
- __le32 bt4_antenna_isolation_thr;
-
- __le32 bt4_tx_tx_delta_freq_thr;
- __le32 bt4_tx_rx_max_freq0;
-
- __le32 multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
- __le32 mplut_prio_boost[BT_COEX_BOOST_SIZE];
-
- __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
} __packed; /* BT_COEX_CMD_API_S_VER_6 */
/**
__le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE];
} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */
-/**
- * struct iwl_bt_coex_sw_boost - SW boost values
- * @wifi_tx_prio_boost: SW boost of wifi tx priority
- * @wifi_rx_prio_boost: SW boost of wifi rx priority
- * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK.
- * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS.
- */
-struct iwl_bt_coex_sw_boost {
- __le32 wifi_tx_prio_boost;
- __le32 wifi_rx_prio_boost;
- __le32 kill_ack_msk;
- __le32 kill_cts_msk;
-};
-
-/**
- * struct iwl_bt_coex_sw_boost_update_cmd - command to update the SW boost
- * @boost_values: check struct %iwl_bt_coex_sw_boost - one for each channel
- * primary / secondary / low priority
- */
-struct iwl_bt_coex_sw_boost_update_cmd {
- struct iwl_bt_coex_sw_boost boost_values[3];
-} __packed; /* BT_COEX_UPDATE_SW_BOOST_S_VER_1 */
-
/**
* struct iwl_bt_coex_reduced_txp_update_cmd
* @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the
* @solicited_node_ipv6_addr: broken -- solicited node address exists
* for each target address
* @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor soliciation response MAC address
+ * @ndp_mac_addr: neighbor solicitation response MAC address
*/
struct iwl_proto_offload_cmd_v1 {
struct iwl_proto_offload_cmd_common common;
* @solicited_node_ipv6_addr: broken -- solicited node address exists
* for each target address
* @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor soliciation response MAC address
+ * @ndp_mac_addr: neighbor solicitation response MAC address
*/
struct iwl_proto_offload_cmd_v2 {
struct iwl_proto_offload_cmd_common common;
/**
* enum iwl_mac_filter_flags - MAC context filter flags
* @MAC_FILTER_IN_PROMISC: accept all data frames
- * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all mangement and
+ * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and
* control frames to the host
* @MAC_FILTER_ACCEPT_GRP: accept multicast frames
* @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames
* @SCAN_COMP_STATUS_ERR_COEX: medium was lost ot WiMax
* @SCAN_COMP_STATUS_P2P_ACTION_OK: P2P public action frame TX was successful
* (not an error!)
- * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repeatition the driver
+ * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repetition the driver
* asked for
* @SCAN_COMP_STATUS_ERR_ALLOC_TE: scan could not allocate time events
*/
* struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6
* @scan_flags: see enum iwl_scan_flags
* @channel_count: channels in channel list
- * @quiet_time: dwell time, in milisiconds, on quiet channel
+ * @quiet_time: dwell time, in milliseconds, on quiet channel
* @quiet_plcp_th: quiet channel num of packets threshold
* @good_CRC_th: passive to active promotion threshold
* @rx_chain: RXON rx chain.
- * @max_out_time: max TUs to be out of assoceated channel
+ * @max_out_time: max TUs to be out of associated channel
* @suspend_time: pause scan this TUs when returning to service channel
* @flags: RXON flags
* @filter_flags: RXONfilter
* see enum iwl_scan_offload_channel_flags.
* __le16 channel_number: channel number 1-13 etc.
* __le16 iter_count: repetition count for the channel.
- * __le32 iter_interval: interval between two innteration on one channel.
+ * __le32 iter_interval: interval between two iterations on one channel.
* u8 active_dwell.
* u8 passive_dwell.
*/
/**
* iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S
* @ssid_index: index to ssid list in fixed part
- * @unicast_cipher: encryption olgorithm to match - bitmap
- * @aut_alg: authentication olgorithm to match - bitmap
+ * @unicast_cipher: encryption algorithm to match - bitmap
+ * @aut_alg: authentication algorithm to match - bitmap
* @network_type: enum iwl_scan_offload_network_type
* @band_selection: enum iwl_scan_offload_band_selection
* @client_bitmap: clients waiting for match - enum scan_framework_client
* @flags: bitmap - 0-19: directed scan to i'th ssid.
* @channel_num: channel number 1-13 etc.
* @iter_count: repetition count for the channel.
- * @iter_interval: interval between two scan interations on one channel.
+ * @iter_interval: interval between two scan iterations on one channel.
*/
struct iwl_scan_channel_cfg_umac {
__le32 flags;
REPLY_RX_MPDU_CMD = 0xc1,
BA_NOTIF = 0xc5,
+ /* Location Aware Regulatory */
+ MCC_UPDATE_CMD = 0xc8,
+ MCC_CHUB_UPDATE_CMD = 0xc9,
+
MARKER_CMD = 0xcb,
/* BT Coex */
NVM_SECTION_TYPE_CALIBRATION = 4,
NVM_SECTION_TYPE_PRODUCTION = 5,
NVM_SECTION_TYPE_MAC_OVERRIDE = 11,
- NVM_MAX_NUM_SECTIONS = 12,
+ NVM_SECTION_TYPE_PHY_SKU = 12,
+ NVM_MAX_NUM_SECTIONS = 13,
};
/**
#define SF_W_MARK_LEGACY 4096
#define SF_W_MARK_SCAN 4096
-/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */
+/* SF Scenarios timers for default configuration (aligned to 32 uSec) */
+#define SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */
+#define SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */
+#define SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */
+#define SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */
+#define SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */
+#define SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */
+#define SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */
+#define SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */
+#define SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */
+#define SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */
+
+/* SF Scenarios timers for BSS MAC configuration (aligned to 32 uSec) */
#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */
#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */
#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */
__le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
} __packed; /* SF_CFG_API_S_VER_2 */
+/***********************************
+ * Location Aware Regulatory (LAR) API - MCC updates
+ ***********************************/
+
+/**
+ * struct iwl_mcc_update_cmd - Request the device to update geographic
+ * regulatory profile according to the given MCC (Mobile Country Code).
+ * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
+ * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
+ * MCC in the cmd response will be the relevant MCC in the NVM.
+ * @mcc: given mobile country code
+ * @source_id: the source from where we got the MCC, see iwl_mcc_source
+ * @reserved: reserved for alignment
+ */
+struct iwl_mcc_update_cmd {
+ __le16 mcc;
+ u8 source_id;
+ u8 reserved;
+} __packed; /* LAR_UPDATE_MCC_CMD_API_S */
+
+/**
+ * iwl_mcc_update_resp - response to MCC_UPDATE_CMD.
+ * Contains the new channel control profile map, if changed, and the new MCC
+ * (mobile country code).
+ * The new MCC may be different than what was requested in MCC_UPDATE_CMD.
+ * @status: see &enum iwl_mcc_update_status
+ * @mcc: the new applied MCC
+ * @cap: capabilities for all channels which matches the MCC
+ * @source_id: the MCC source, see iwl_mcc_source
+ * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51
+ * channels, depending on platform)
+ * @channels: channel control data map, DWORD for each channel. Only the first
+ * 16bits are used.
+ */
+struct iwl_mcc_update_resp {
+ __le32 status;
+ __le16 mcc;
+ u8 cap;
+ u8 source_id;
+ __le32 n_channels;
+ __le32 channels[0];
+} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */
+
+/**
+ * struct iwl_mcc_chub_notif - chub notifies of mcc change
+ * (MCC_CHUB_UPDATE_CMD = 0xc9)
+ * The Chub (Communication Hub, CommsHUB) is a HW component that connects to
+ * the cellular and connectivity cores that gets updates of the mcc, and
+ * notifies the ucode directly of any mcc change.
+ * The ucode requests the driver to request the device to update geographic
+ * regulatory profile according to the given MCC (Mobile Country Code).
+ * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
+ * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
+ * MCC in the cmd response will be the relevant MCC in the NVM.
+ * @mcc: given mobile country code
+ * @source_id: identity of the change originator, see iwl_mcc_source
+ * @reserved1: reserved for alignment
+ */
+struct iwl_mcc_chub_notif {
+ u16 mcc;
+ u8 source_id;
+ u8 reserved1;
+} __packed; /* LAR_MCC_NOTIFY_S */
+
+enum iwl_mcc_update_status {
+ MCC_RESP_NEW_CHAN_PROFILE,
+ MCC_RESP_SAME_CHAN_PROFILE,
+ MCC_RESP_INVALID,
+ MCC_RESP_NVM_DISABLED,
+ MCC_RESP_ILLEGAL,
+ MCC_RESP_LOW_PRIORITY,
+};
+
+enum iwl_mcc_source {
+ MCC_SOURCE_OLD_FW = 0,
+ MCC_SOURCE_ME = 1,
+ MCC_SOURCE_BIOS = 2,
+ MCC_SOURCE_3G_LTE_HOST = 3,
+ MCC_SOURCE_3G_LTE_DEVICE = 4,
+ MCC_SOURCE_WIFI = 5,
+ MCC_SOURCE_RESERVED = 6,
+ MCC_SOURCE_DEFAULT = 7,
+ MCC_SOURCE_UNINITIALIZED = 8,
+ MCC_SOURCE_GET_CURRENT = 0x10
+};
+
/* DTS measurements */
enum iwl_dts_measurement_flags {
int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
struct iwl_fw_dbg_trigger_tlv *trigger,
- const char *str, size_t len)
+ const char *fmt, ...)
{
unsigned int delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
u16 occurrences = le16_to_cpu(trigger->occurrences);
- int ret;
+ int ret, len = 0;
+ char buf[64];
if (!occurrences)
return 0;
- ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), str,
+ if (fmt) {
+ va_list ap;
+
+ buf[sizeof(buf) - 1] = '\0';
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ /* check for truncation */
+ if (WARN_ON_ONCE(buf[sizeof(buf) - 1]))
+ buf[sizeof(buf) - 1] = '\0';
+
+ len = strlen(buf) + 1;
+ }
+
+ ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf,
len, delay);
if (ret)
return ret;
if (ret)
goto error;
+ /*
+ * RTNL is not taken during Ct-kill, but we don't need to scan/Tx
+ * anyway, so don't init MCC.
+ */
+ if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) {
+ ret = iwl_mvm_init_mcc(mvm);
+ if (ret)
+ goto error;
+ }
+
if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
ret = iwl_mvm_config_scan(mvm);
if (ret)
int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
- unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ?
- mvm->cfg->base_params->wd_timeout :
- IWL_WATCHDOG_DISABLED;
+ unsigned int wdg_timeout =
+ iwl_mvm_get_wd_timeout(mvm, vif, false, false);
u32 ac;
int ret;
if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx ||
rx_missed_bcon >= stop_trig_missed_bcon)
- iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL, 0);
+ iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
}
int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
#include "iwl-fw-error-dump.h"
#include "iwl-prph.h"
#include "iwl-csr.h"
+#include "iwl-nvm-parse.h"
static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
{
}
}
+struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
+ const char *alpha2,
+ enum iwl_mcc_source src_id,
+ bool *changed)
+{
+ struct ieee80211_regdomain *regd = NULL;
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mcc_update_resp *resp;
+
+ IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ resp = iwl_mvm_update_mcc(mvm, alpha2, src_id);
+ if (IS_ERR_OR_NULL(resp)) {
+ IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n",
+ PTR_RET(resp));
+ goto out;
+ }
+
+ if (changed)
+ *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE);
+
+ regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
+ __le32_to_cpu(resp->n_channels),
+ resp->channels,
+ __le16_to_cpu(resp->mcc));
+ /* Store the return source id */
+ src_id = resp->source_id;
+ kfree(resp);
+ if (IS_ERR_OR_NULL(regd)) {
+ IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n",
+ PTR_RET(regd));
+ goto out;
+ }
+
+ IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n",
+ regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id);
+ mvm->lar_regdom_set = true;
+ mvm->mcc_src = src_id;
+
+out:
+ return regd;
+}
+
+void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm)
+{
+ bool changed;
+ struct ieee80211_regdomain *regd;
+
+ if (!iwl_mvm_is_lar_supported(mvm))
+ return;
+
+ regd = iwl_mvm_get_current_regdomain(mvm, &changed);
+ if (!IS_ERR_OR_NULL(regd)) {
+ /* only update the regulatory core if changed */
+ if (changed)
+ regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
+
+ kfree(regd);
+ }
+}
+
+struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
+ bool *changed)
+{
+ return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ",
+ iwl_mvm_is_wifi_mcc_supported(mvm) ?
+ MCC_SOURCE_GET_CURRENT :
+ MCC_SOURCE_OLD_FW, changed);
+}
+
+int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
+{
+ enum iwl_mcc_source used_src;
+ struct ieee80211_regdomain *regd;
+ int ret;
+ bool changed;
+ const struct ieee80211_regdomain *r =
+ rtnl_dereference(mvm->hw->wiphy->regd);
+
+ if (!r)
+ return -ENOENT;
+
+ /* save the last source in case we overwrite it below */
+ used_src = mvm->mcc_src;
+ if (iwl_mvm_is_wifi_mcc_supported(mvm)) {
+ /* Notify the firmware we support wifi location updates */
+ regd = iwl_mvm_get_current_regdomain(mvm, NULL);
+ if (!IS_ERR_OR_NULL(regd))
+ kfree(regd);
+ }
+
+ /* Now set our last stored MCC and source */
+ regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src,
+ &changed);
+ if (IS_ERR_OR_NULL(regd))
+ return -EIO;
+
+ /* update cfg80211 if the regdomain was changed */
+ if (changed)
+ ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+ else
+ ret = 0;
+
+ kfree(regd);
+ return ret;
+}
+
int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
{
struct ieee80211_hw *hw = mvm->hw;
BIT(NL80211_IFTYPE_ADHOC);
hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
- hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
- REGULATORY_DISABLE_BEACON_HINTS;
+ hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR;
+ if (iwl_mvm_is_lar_supported(mvm))
+ hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+ else
+ hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
+ REGULATORY_DISABLE_BEACON_HINTS;
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
mvm->fw_dump_desc = NULL;
}
+#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */
+#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */
+
void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
{
struct iwl_fw_error_dump_file *dump_file;
lockdep_assert_held(&mvm->mutex);
- /* W/A for 8000 HW family A-step */
- if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
- CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) {
- if (smem_len)
- smem_len = 0x38000;
-
- if (sram2_len)
- sram2_len = 0x10000;
- }
-
fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
if (!fw_error_dump)
return;
fifo_data_len +
sizeof(*dump_info);
+ /*
+ * In 8000 HW family B-step include the ICCM (which resides separately)
+ */
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+ CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP)
+ file_len += sizeof(*dump_data) + sizeof(*dump_mem) +
+ IWL8260_ICCM_LEN;
+
if (mvm->fw_dump_desc)
file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
mvm->fw_dump_desc->len;
dump_mem->data, sram2_len);
}
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+ CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) {
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+ dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN +
+ sizeof(*dump_mem));
+ dump_mem = (void *)dump_data->data;
+ dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+ dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET);
+ iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET,
+ dump_mem->data, IWL8260_ICCM_LEN);
+ }
+
fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans);
fw_error_dump->op_mode_len = file_len;
if (fw_error_dump->trans_ptr)
clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
iwl_mvm_d0i3_enable_tx(mvm, NULL);
- ret = iwl_mvm_update_quotas(mvm, NULL);
+ ret = iwl_mvm_update_quotas(mvm, false, NULL);
if (ret)
IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
ret);
*/
clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+ /* We shouldn't have any UIDs still set. Loop over all the UIDs to
+ * make sure there's nothing left there and warn if any is found.
+ */
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+ int i;
+
+ for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+ if (WARN_ONCE(mvm->scan_uid[i],
+ "UMAC scan UID %d was not cleaned\n",
+ mvm->scan_uid[i]))
+ mvm->scan_uid[i] = 0;
+ }
+ }
+
mvm->ucode_loaded = false;
}
u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif);
if (tfd_msk) {
+ /*
+ * mac80211 first removes all the stations of the vif and
+ * then removes the vif. When it removes a station it also
+ * flushes the AMPDU session. So by now, all the AMPDU sessions
+ * of all the stations of this vif are closed, and the queues
+ * of these AMPDU sessions are properly closed.
+ * We still need to take care of the shared queues of the vif.
+ * Flush them here.
+ */
mutex_lock(&mvm->mutex);
iwl_mvm_flush_tx_path(mvm, tfd_msk, true);
mutex_unlock(&mvm->mutex);
+
+ /*
+ * There are transports that buffer a few frames in the host.
+ * For these, the flush above isn't enough since while we were
+ * flushing, the transport might have sent more frames to the
+ * device. To solve this, wait here until the transport is
+ * empty. Technically, this could have replaced the flush
+ * above, but flush is much faster than draining. So flush
+ * first, and drain to make sure we have no frames in the
+ * transport anymore.
+ * If a station still had frames on the shared queues, it is
+ * already marked as draining, so to complete the draining, we
+ * just need to wait until the transport is empty.
+ */
+ iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk);
}
if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
sizeof(mvmvif->beacon_stats));
/* add quota for this interface */
- ret = iwl_mvm_update_quotas(mvm, NULL);
+ ret = iwl_mvm_update_quotas(mvm, true, NULL);
if (ret) {
IWL_ERR(mvm, "failed to update quotas\n");
return;
mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
/* remove quota for this interface */
- ret = iwl_mvm_update_quotas(mvm, NULL);
+ ret = iwl_mvm_update_quotas(mvm, false, NULL);
if (ret)
IWL_ERR(mvm, "failed to update quotas\n");
/* power updated needs to be done before quotas */
iwl_mvm_power_update_mac(mvm);
- ret = iwl_mvm_update_quotas(mvm, NULL);
+ ret = iwl_mvm_update_quotas(mvm, false, NULL);
if (ret)
goto out_quota_failed;
if (iwl_mvm_phy_ctx_count(mvm) > 1)
iwl_mvm_teardown_tdls_peers(mvm);
- mutex_unlock(&mvm->mutex);
- return 0;
+ goto out_unlock;
out_quota_failed:
iwl_mvm_power_update_mac(mvm);
if (vif->p2p && mvm->p2p_device_vif)
iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
- iwl_mvm_update_quotas(mvm, NULL);
+ iwl_mvm_update_quotas(mvm, false, NULL);
iwl_mvm_send_rm_bcast_sta(mvm, vif);
iwl_mvm_binding_remove_vif(mvm, vif);
mutex_lock(&mvm->mutex);
+ if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
+ IWL_ERR(mvm, "scan while LAR regdomain is not set\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
ret = -EBUSY;
goto out;
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ unsigned long txqs = 0, tids = 0;
int tid;
+ spin_lock_bh(&mvmsta->lock);
+ for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+
+ if (tid_data->state != IWL_AGG_ON &&
+ tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
+ continue;
+
+ __set_bit(tid_data->txq_id, &txqs);
+
+ if (iwl_mvm_tid_queued(tid_data) == 0)
+ continue;
+
+ __set_bit(tid, &tids);
+ }
+
switch (cmd) {
case STA_NOTIFY_SLEEP:
if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
ieee80211_sta_block_awake(hw, sta, true);
- spin_lock_bh(&mvmsta->lock);
- for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
- struct iwl_mvm_tid_data *tid_data;
- tid_data = &mvmsta->tid_data[tid];
- if (tid_data->state != IWL_AGG_ON &&
- tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
- continue;
- if (iwl_mvm_tid_queued(tid_data) == 0)
- continue;
+ for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT)
ieee80211_sta_set_buffered(sta, tid, true);
- }
- spin_unlock_bh(&mvmsta->lock);
+
+ if (txqs)
+ iwl_trans_freeze_txq_timer(mvm->trans, txqs, true);
/*
* The fw updates the STA to be asleep. Tx packets on the Tx
* queues to this station will not be transmitted. The fw will
case STA_NOTIFY_AWAKE:
if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
break;
+
+ if (txqs)
+ iwl_trans_freeze_txq_timer(mvm->trans, txqs, false);
iwl_mvm_sta_modify_ps_wake(mvm, sta);
break;
default:
break;
}
+ spin_unlock_bh(&mvmsta->lock);
}
static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
+ if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
+ IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
if (!vif->bss_conf.idle) {
ret = -EBUSY;
goto out;
IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
duration, type);
+ flush_work(&mvm->roc_done_wk);
+
mutex_lock(&mvm->mutex);
switch (vif->type) {
*/
if (vif->type == NL80211_IFTYPE_MONITOR) {
mvmvif->monitor_active = true;
- ret = iwl_mvm_update_quotas(mvm, NULL);
+ ret = iwl_mvm_update_quotas(mvm, false, NULL);
if (ret)
goto out_remove_binding;
}
/* Handle binding during CSA */
if (vif->type == NL80211_IFTYPE_AP) {
- iwl_mvm_update_quotas(mvm, NULL);
+ iwl_mvm_update_quotas(mvm, false, NULL);
iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
}
iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
- iwl_mvm_update_quotas(mvm, NULL);
+ iwl_mvm_update_quotas(mvm, false, NULL);
}
goto out;
break;
}
- iwl_mvm_update_quotas(mvm, disabled_vif);
+ iwl_mvm_update_quotas(mvm, false, disabled_vif);
iwl_mvm_binding_remove_vif(mvm, vif);
out:
mvm->noa_duration = noa_duration;
mvm->noa_vif = vif;
- return iwl_mvm_update_quotas(mvm, NULL);
+ return iwl_mvm_update_quotas(mvm, false, NULL);
case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
/* must be associated client vif - ignore authorized */
if (!vif || vif->type != NL80211_IFTYPE_STATION ||
mutex_lock(&mvm->mutex);
+ mvmvif->csa_failed = false;
+
IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
chsw->chandef.center_freq1);
- iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH,
- NULL, 0);
+ iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH);
switch (vif->type) {
case NL80211_IFTYPE_AP:
mutex_lock(&mvm->mutex);
+ if (mvmvif->csa_failed) {
+ mvmvif->csa_failed = false;
+ ret = -EIO;
+ goto out_unlock;
+ }
+
if (vif->type == NL80211_IFTYPE_STATION) {
struct iwl_mvm_sta *mvmsta;
mvm->radio_stats.on_time_scan;
do_div(survey->time_scan, USEC_PER_MSEC);
+ ret = 0;
out:
mutex_unlock(&mvm->mutex);
return ret;
mutex_unlock(&mvm->mutex);
}
+static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const struct ieee80211_event *event)
+{
+#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...) \
+ do { \
+ if ((_cnt) && --(_cnt)) \
+ break; \
+ iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\
+ } while (0)
+
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_mlme *trig_mlme;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
+ return;
+
+ if (event->u.mlme.status == MLME_SUCCESS)
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
+ trig_mlme = (void *)trig->data;
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+ return;
+
+ if (event->u.mlme.data == ASSOC_EVENT) {
+ if (event->u.mlme.status == MLME_DENIED)
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_assoc_denied,
+ "DENIED ASSOC: reason %d",
+ event->u.mlme.reason);
+ else if (event->u.mlme.status == MLME_TIMEOUT)
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_assoc_timeout,
+ "ASSOC TIMEOUT");
+ } else if (event->u.mlme.data == AUTH_EVENT) {
+ if (event->u.mlme.status == MLME_DENIED)
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_auth_denied,
+ "DENIED AUTH: reason %d",
+ event->u.mlme.reason);
+ else if (event->u.mlme.status == MLME_TIMEOUT)
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_auth_timeout,
+ "AUTH TIMEOUT");
+ } else if (event->u.mlme.data == DEAUTH_RX_EVENT) {
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_rx_deauth,
+ "DEAUTH RX %d", event->u.mlme.reason);
+ } else if (event->u.mlme.data == DEAUTH_TX_EVENT) {
+ CHECK_MLME_TRIGGER(mvm, trig, buf,
+ trig_mlme->stop_tx_deauth,
+ "DEAUTH TX %d", event->u.mlme.reason);
+ }
+#undef CHECK_MLME_TRIGGER
+}
+
const struct ieee80211_ops iwl_mvm_hw_ops = {
.tx = iwl_mvm_mac_tx,
.ampdu_action = iwl_mvm_mac_ampdu_action,
.tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
.tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
+ .event_callback = iwl_mvm_mac_event_callback,
+
CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
#ifdef CONFIG_PM_SLEEP
* @bcast_sta: station used for broadcast packets. Used by the following
* vifs: P2P_DEVICE, GO and AP.
* @beacon_skb: the skb used to hold the AP/GO beacon template
- * @smps_requests: the SMPS requests of differents parts of the driver,
+ * @smps_requests: the SMPS requests of different parts of the driver,
* combined on update to yield the overall request to mac80211.
* @beacon_stats: beacon statistics, containing the # of received beacons,
* # of received beacons accumulated over FW restart, and the current
* average signal of beacons retrieved from the firmware
+ * @csa_failed: CSA failed to schedule time event, report an error later
*/
struct iwl_mvm_vif {
struct iwl_mvm *mvm;
/* Indicates that CSA countdown may be started */
bool csa_countdown;
+ bool csa_failed;
};
static inline struct iwl_mvm_vif *
bool disable_power_off;
bool disable_power_off_d3;
- bool scan_iter_notif_enabled;
+ u32 scan_iter_notif_enabled; /* must be u32 for debugfs_create_bool */
struct debugfs_blob_wrapper nvm_hw_blob;
struct debugfs_blob_wrapper nvm_sw_blob;
void *d3_resume_sram;
u32 d3_test_pme_ptr;
struct ieee80211_vif *keep_vif;
+ u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */
#endif
#endif
/* system time of last beacon (for AP/GO interface) */
u32 ap_last_beacon_gp2;
+ bool lar_regdom_set;
+ enum iwl_mcc_source mcc_src;
+
u8 low_latency_agg_frame_limit;
/* TDLS channel switch data */
(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
}
+static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
+{
+ bool nvm_lar = mvm->nvm_data->lar_enabled;
+ bool tlv_lar = mvm->fw->ucode_capa.capa[0] &
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT;
+
+ if (iwlwifi_mod_params.lar_disable)
+ return false;
+
+ /*
+ * Enable LAR only if it is supported by the FW (TLV) &&
+ * enabled in the NVM
+ */
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ return nvm_lar && tlv_lar;
+ else
+ return tlv_lar;
+}
+
+static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm)
+{
+ return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WIFI_MCC_UPDATE ||
+ mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC;
+}
+
static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm)
{
return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SCD_CFG;
IWL_MVM_BT_COEX_CORUNNING;
}
+static inline bool iwl_mvm_bt_is_rrc_supported(struct iwl_mvm *mvm)
+{
+ return (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BT_COEX_RRC) &&
+ IWL_MVM_BT_COEX_RRC;
+}
+
extern const u8 iwl_mvm_ac_to_tx_fifo[];
struct iwl_rate_info {
int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
/* Quota management */
-int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload,
struct ieee80211_vif *disabled_vif);
/* Scanning */
int iwl_mvm_scan_size(struct iwl_mvm *mvm);
int iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm, bool is_sched_scan);
+void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm);
/* Scheduled scan */
int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
-enum iwl_bt_kill_msk {
- BT_KILL_MSK_DEFAULT,
- BT_KILL_MSK_NEVER,
- BT_KILL_MSK_ALWAYS,
- BT_KILL_MSK_MAX,
-};
-
-extern const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT];
-extern const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT];
-extern const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX];
-
/* beacon filtering */
#ifdef CONFIG_IWLWIFI_DEBUGFS
void
void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
int iwl_mvm_get_temp(struct iwl_mvm *mvm);
+/* Location Aware Regulatory */
+struct iwl_mcc_update_resp *
+iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
+ enum iwl_mcc_source src_id);
+int iwl_mvm_init_mcc(struct iwl_mvm *mvm);
+int iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
+ const char *alpha2,
+ enum iwl_mcc_source src_id,
+ bool *changed);
+struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
+ bool *changed);
+int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm);
+void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm);
+
/* smart fifo */
int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool added_vif);
void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm);
int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
struct iwl_fw_dbg_trigger_tlv *trigger,
- const char *str, size_t len);
-
+ const char *fmt, ...) __printf(3, 4);
+unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool tdls, bool cmd_q);
+void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ const char *errmsg);
static inline bool
iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
struct ieee80211_vif *vif)
static inline void
iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- enum iwl_fw_dbg_trigger trig,
- const char *str, size_t len)
+ enum iwl_fw_dbg_trigger trig)
{
struct iwl_fw_dbg_trigger_tlv *trigger;
if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
return;
- iwl_mvm_fw_dbg_collect_trig(mvm, trigger, str, len);
+ iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL);
}
#endif /* __IWL_MVM_H__ */
*
*****************************************************************************/
#include <linux/firmware.h>
+#include <linux/rtnetlink.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
#include "iwl-trans.h"
#include "iwl-csr.h"
#include "mvm.h"
#include "iwl-eeprom-parse.h"
#include "iwl-eeprom-read.h"
#include "iwl-nvm-parse.h"
+#include "iwl-prph.h"
/* Default NVM size to read */
#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
#define IWL_MAX_NVM_SECTION_SIZE 0x1b58
-#define IWL_MAX_NVM_8000A_SECTION_SIZE 0xffc
-#define IWL_MAX_NVM_8000B_SECTION_SIZE 0x1ffc
+#define IWL_MAX_NVM_8000_SECTION_SIZE 0x1ffc
#define NVM_WRITE_OPCODE 1
#define NVM_READ_OPCODE 0
iwl_parse_nvm_sections(struct iwl_mvm *mvm)
{
struct iwl_nvm_section *sections = mvm->nvm_sections;
- const __le16 *hw, *sw, *calib, *regulatory, *mac_override;
+ const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku;
+ bool lar_enabled;
+ u32 mac_addr0, mac_addr1;
/* Checking for required sections */
if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
"Can't parse mac_address, empty sections\n");
return NULL;
}
+
+ /* PHY_SKU section is mandatory in B0 */
+ if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) {
+ IWL_ERR(mvm,
+ "Can't parse phy_sku in B0, empty sections\n");
+ return NULL;
+ }
}
if (WARN_ON(!mvm->cfg))
return NULL;
+ /* read the mac address from WFMP registers */
+ mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0);
+ mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1);
+
hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data;
sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data;
mac_override =
(const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data;
+ phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data;
+
+ lar_enabled = !iwlwifi_mod_params.lar_disable &&
+ (mvm->fw->ucode_capa.capa[0] &
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib,
- regulatory, mac_override,
- mvm->fw->valid_tx_ant,
- mvm->fw->valid_rx_ant);
+ regulatory, mac_override, phy_sku,
+ mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant,
+ lar_enabled, mac_addr0, mac_addr1);
}
#define MAX_NVM_FILE_LEN 16384
/* Maximal size depends on HW family and step */
if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
max_section_size = IWL_MAX_NVM_SECTION_SIZE;
- else if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP)
- max_section_size = IWL_MAX_NVM_8000A_SECTION_SIZE;
- else /* Family 8000 B-step or C-step */
- max_section_size = IWL_MAX_NVM_8000B_SECTION_SIZE;
+ else
+ max_section_size = IWL_MAX_NVM_8000_SECTION_SIZE;
/*
* Obtain NVM image via request_firmware. Since we already used
IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
IWL_INFO(mvm, "NVM Manufacturing date %08X\n",
le32_to_cpu(dword_buff[3]));
+
+ /* nvm file validation, dword_buff[2] holds the file version */
+ if ((CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP &&
+ le32_to_cpu(dword_buff[2]) < 0xE4A) ||
+ (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP &&
+ le32_to_cpu(dword_buff[2]) >= 0xE4A)) {
+ ret = -EFAULT;
+ goto out;
+ }
} else {
file_sec = (void *)fw_entry->data;
}
int ret, section;
u32 size_read = 0;
u8 *nvm_buffer, *temp;
+ const char *nvm_file_B = mvm->cfg->default_nvm_file_B_step;
+ const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step;
if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
return -EINVAL;
/* load external NVM if configured */
if (mvm->nvm_file_name) {
- /* move to External NVM flow */
+ /* read External NVM file - take the default */
ret = iwl_mvm_read_external_nvm(mvm);
- if (ret)
- return ret;
+ if (ret) {
+ /* choose the nvm_file name according to the
+ * HW step
+ */
+ if (CSR_HW_REV_STEP(mvm->trans->hw_rev) ==
+ SILICON_B_STEP)
+ mvm->nvm_file_name = nvm_file_B;
+ else
+ mvm->nvm_file_name = nvm_file_C;
+
+ if (ret == -EFAULT && mvm->nvm_file_name) {
+ /* in case nvm file was failed try again */
+ ret = iwl_mvm_read_external_nvm(mvm);
+ if (ret)
+ return ret;
+ } else {
+ return ret;
+ }
+ }
}
/* parse the relevant nvm sections */
return 0;
}
+
+struct iwl_mcc_update_resp *
+iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
+ enum iwl_mcc_source src_id)
+{
+ struct iwl_mcc_update_cmd mcc_update_cmd = {
+ .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),
+ .source_id = (u8)src_id,
+ };
+ struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL;
+ struct iwl_rx_packet *pkt;
+ struct iwl_host_cmd cmd = {
+ .id = MCC_UPDATE_CMD,
+ .flags = CMD_WANT_SKB,
+ .data = { &mcc_update_cmd },
+ };
+
+ int ret;
+ u32 status;
+ int resp_len, n_channels;
+ u16 mcc;
+
+ if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ cmd.len[0] = sizeof(struct iwl_mcc_update_cmd);
+
+ IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n",
+ alpha2[0], alpha2[1], src_id);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret)
+ return ERR_PTR(ret);
+
+ pkt = cmd.resp_pkt;
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+ IWL_ERR(mvm, "Bad return from MCC_UPDATE_COMMAND (0x%08X)\n",
+ pkt->hdr.flags);
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* Extract MCC response */
+ mcc_resp = (void *)pkt->data;
+ status = le32_to_cpu(mcc_resp->status);
+
+ mcc = le16_to_cpu(mcc_resp->mcc);
+
+ /* W/A for a FW/NVM issue - returns 0x00 for the world domain */
+ if (mcc == 0) {
+ mcc = 0x3030; /* "00" - world */
+ mcc_resp->mcc = cpu_to_le16(mcc);
+ }
+
+ n_channels = __le32_to_cpu(mcc_resp->n_channels);
+ IWL_DEBUG_LAR(mvm,
+ "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n",
+ status, mcc, mcc >> 8, mcc & 0xff,
+ !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels);
+
+ resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32);
+ resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL);
+ if (!resp_cp) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = 0;
+exit:
+ iwl_free_resp(&cmd);
+ if (ret)
+ return ERR_PTR(ret);
+ return resp_cp;
+}
+
+#ifdef CONFIG_ACPI
+#define WRD_METHOD "WRDD"
+#define WRDD_WIFI (0x07)
+#define WRDD_WIGIG (0x10)
+
+static u32 iwl_mvm_wrdd_get_mcc(struct iwl_mvm *mvm, union acpi_object *wrdd)
+{
+ union acpi_object *mcc_pkg, *domain_type, *mcc_value;
+ u32 i;
+
+ if (wrdd->type != ACPI_TYPE_PACKAGE ||
+ wrdd->package.count < 2 ||
+ wrdd->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ wrdd->package.elements[0].integer.value != 0) {
+ IWL_DEBUG_LAR(mvm, "Unsupported wrdd structure\n");
+ return 0;
+ }
+
+ for (i = 1 ; i < wrdd->package.count ; ++i) {
+ mcc_pkg = &wrdd->package.elements[i];
+
+ if (mcc_pkg->type != ACPI_TYPE_PACKAGE ||
+ mcc_pkg->package.count < 2 ||
+ mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
+ mcc_pkg = NULL;
+ continue;
+ }
+
+ domain_type = &mcc_pkg->package.elements[0];
+ if (domain_type->integer.value == WRDD_WIFI)
+ break;
+
+ mcc_pkg = NULL;
+ }
+
+ if (mcc_pkg) {
+ mcc_value = &mcc_pkg->package.elements[1];
+ return mcc_value->integer.value;
+ }
+
+ return 0;
+}
+
+static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc)
+{
+ acpi_handle root_handle;
+ acpi_handle handle;
+ struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL};
+ acpi_status status;
+ u32 mcc_val;
+ struct pci_dev *pdev = to_pci_dev(mvm->dev);
+
+ root_handle = ACPI_HANDLE(&pdev->dev);
+ if (!root_handle) {
+ IWL_DEBUG_LAR(mvm,
+ "Could not retrieve root port ACPI handle\n");
+ return -ENOENT;
+ }
+
+ /* Get the method's handle */
+ status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle);
+ if (ACPI_FAILURE(status)) {
+ IWL_DEBUG_LAR(mvm, "WRD method not found\n");
+ return -ENOENT;
+ }
+
+ /* Call WRDD with no arguments */
+ status = acpi_evaluate_object(handle, NULL, NULL, &wrdd);
+ if (ACPI_FAILURE(status)) {
+ IWL_DEBUG_LAR(mvm, "WRDC invocation failed (0x%x)\n", status);
+ return -ENOENT;
+ }
+
+ mcc_val = iwl_mvm_wrdd_get_mcc(mvm, wrdd.pointer);
+ kfree(wrdd.pointer);
+ if (!mcc_val)
+ return -ENOENT;
+
+ mcc[0] = (mcc_val >> 8) & 0xff;
+ mcc[1] = mcc_val & 0xff;
+ mcc[2] = '\0';
+ return 0;
+}
+#else /* CONFIG_ACPI */
+static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc)
+{
+ return -ENOENT;
+}
+#endif
+
+int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
+{
+ bool tlv_lar;
+ bool nvm_lar;
+ int retval;
+ struct ieee80211_regdomain *regd;
+ char mcc[3];
+
+ if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+ tlv_lar = mvm->fw->ucode_capa.capa[0] &
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT;
+ nvm_lar = mvm->nvm_data->lar_enabled;
+ if (tlv_lar != nvm_lar)
+ IWL_INFO(mvm,
+ "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n",
+ tlv_lar ? "enabled" : "disabled",
+ nvm_lar ? "enabled" : "disabled");
+ }
+
+ if (!iwl_mvm_is_lar_supported(mvm))
+ return 0;
+
+ /*
+ * try to replay the last set MCC to FW. If it doesn't exist,
+ * queue an update to cfg80211 to retrieve the default alpha2 from FW.
+ */
+ retval = iwl_mvm_init_fw_regd(mvm);
+ if (retval != -ENOENT)
+ return retval;
+
+ /*
+ * Driver regulatory hint for initial update, this also informs the
+ * firmware we support wifi location updates.
+ * Disallow scans that might crash the FW while the LAR regdomain
+ * is not set.
+ */
+ mvm->lar_regdom_set = false;
+
+ regd = iwl_mvm_get_current_regdomain(mvm, NULL);
+ if (IS_ERR_OR_NULL(regd))
+ return -EIO;
+
+ if (iwl_mvm_is_wifi_mcc_supported(mvm) &&
+ !iwl_mvm_get_bios_mcc(mvm, mcc)) {
+ kfree(regd);
+ regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc,
+ MCC_SOURCE_BIOS, NULL);
+ if (IS_ERR_OR_NULL(regd))
+ return -EIO;
+ }
+
+ retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+ kfree(regd);
+ return retval;
+}
+
+int iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mcc_chub_notif *notif = (void *)pkt->data;
+ enum iwl_mcc_source src;
+ char mcc[3];
+ struct ieee80211_regdomain *regd;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
+ return 0;
+
+ mcc[0] = notif->mcc >> 8;
+ mcc[1] = notif->mcc & 0xff;
+ mcc[2] = '\0';
+ src = notif->source_id;
+
+ IWL_DEBUG_LAR(mvm,
+ "RX: received chub update mcc cmd (mcc '%s' src %d)\n",
+ mcc, src);
+ regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL);
+ if (IS_ERR_OR_NULL(regd))
+ return 0;
+
+ regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
+ kfree(regd);
+
+ return 0;
+}
#include "rs.h"
#include "fw-api-scan.h"
#include "time-event.h"
-#include "iwl-fw-error-dump.h"
#define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux"
MODULE_DESCRIPTION(DRV_DESCRIPTION);
iwl_mvm_rx_ant_coupling_notif, true),
RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
+ RX_HANDLER(MCC_CHUB_UPDATE_CMD, iwl_mvm_rx_chub_update_mcc, true),
RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false),
CMD(TDLS_CHANNEL_SWITCH_CMD),
CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION),
CMD(TDLS_CONFIG_CMD),
+ CMD(MCC_UPDATE_CMD),
};
#undef CMD
/* Set a short watchdog for the command queue */
trans_cfg.cmd_q_wdg_timeout =
- iwlmvm_mod_params.tfd_q_hang_detect ? IWL_DEF_WD_TIMEOUT :
- IWL_WATCHDOG_DISABLED;
+ iwl_mvm_get_wd_timeout(mvm, NULL, false, true);
snprintf(mvm->hw->wiphy->fw_version,
sizeof(mvm->hw->wiphy->fw_version),
/* set the nvm_file_name according to priority */
if (iwlwifi_mod_params.nvm_file) {
mvm->nvm_file_name = iwlwifi_mod_params.nvm_file;
- } else {
- if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) &&
- (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP))
- mvm->nvm_file_name = mvm->cfg->default_nvm_file_8000A;
+ } else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+ if (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP)
+ mvm->nvm_file_name = mvm->cfg->default_nvm_file_B_step;
else
- mvm->nvm_file_name = mvm->cfg->default_nvm_file;
+ mvm->nvm_file_name = mvm->cfg->default_nvm_file_C_step;
}
if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name,
{
struct iwl_fw_dbg_trigger_tlv *trig;
struct iwl_fw_dbg_trigger_cmd *cmds_trig;
- char buf[32];
int i;
if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF))
if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd)
continue;
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "CMD 0x%02x received", pkt->hdr.cmd);
- iwl_mvm_fw_dbg_collect_trig(mvm, trig, buf, sizeof(buf));
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+ "CMD 0x%02x received",
+ pkt->hdr.cmd);
break;
}
}
/* start recording again if the firmware is not crashed */
WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) &&
- mvm->fw->dbg_dest_tlv &&
- iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf));
+ mvm->fw->dbg_dest_tlv &&
+ iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf));
mutex_unlock(&mvm->mutex);
* the next start() call from mac80211. If restart isn't called
* (no fw restart) scan status will stay busy.
*/
- switch (mvm->scan_status) {
- case IWL_MVM_SCAN_NONE:
- break;
- case IWL_MVM_SCAN_OS:
- ieee80211_scan_completed(mvm->hw, true);
- break;
- case IWL_MVM_SCAN_SCHED:
- /* Sched scan will be restarted by mac80211 in restart_hw. */
- if (!mvm->restart_fw)
- ieee80211_sched_scan_stopped(mvm->hw);
- break;
- }
+ iwl_mvm_report_scan_aborted(mvm);
/*
* If we're restarting already, don't cycle restarts.
if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc &&
mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id)
- ieee80211_connection_loss(vif);
+ iwl_mvm_connection_loss(mvm, vif, "D0i3");
}
void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq)
iwl_free_resp(&get_status_cmd);
out:
iwl_mvm_d0i3_enable_tx(mvm, qos_seq);
+
+ /* the FW might have updated the regdomain */
+ iwl_mvm_update_changed_regdom(mvm);
+
iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK);
mutex_unlock(&mvm->mutex);
}
#include "fw-api.h"
#include "mvm.h"
-/* Maps the driver specific channel width definition to the the fw values */
+/* Maps the driver specific channel width definition to the fw values */
u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
{
switch (chandef->width) {
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
- !mvmvif->pm_enabled || iwl_mvm_tdls_sta_count(mvm, vif))
+ !mvmvif->pm_enabled)
return;
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
if (vifs->ap_vif)
ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
+ /* don't allow PM if any TDLS stations exist */
+ if (iwl_mvm_tdls_sta_count(mvm, NULL))
+ return;
+
/* enable PM on bss if bss stand alone */
if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
bss_mvmvif->pm_enabled = true;
}
int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+ bool force_update,
struct ieee80211_vif *disabled_vif)
{
struct iwl_time_quota_cmd cmd = {};
"zero quota on binding %d\n", i);
}
- if (!send) {
+ if (!send && !force_update) {
/* don't send a practically unchanged command, the firmware has
* to re-initialize a lot of state and that can have an adverse
* impact on it
&& ant_match;
}
+static inline enum rs_column rs_get_column_from_rate(struct rs_rate *rate)
+{
+ if (is_legacy(rate)) {
+ if (rate->ant == ANT_A)
+ return RS_COLUMN_LEGACY_ANT_A;
+
+ if (rate->ant == ANT_B)
+ return RS_COLUMN_LEGACY_ANT_B;
+
+ goto err;
+ }
+
+ if (is_siso(rate)) {
+ if (rate->ant == ANT_A || rate->stbc || rate->bfer)
+ return rate->sgi ? RS_COLUMN_SISO_ANT_A_SGI :
+ RS_COLUMN_SISO_ANT_A;
+
+ if (rate->ant == ANT_B)
+ return rate->sgi ? RS_COLUMN_SISO_ANT_B_SGI :
+ RS_COLUMN_SISO_ANT_B;
+
+ goto err;
+ }
+
+ if (is_mimo(rate))
+ return rate->sgi ? RS_COLUMN_MIMO2_SGI : RS_COLUMN_MIMO2;
+
+err:
+ return RS_COLUMN_INVALID;
+}
+
static u8 rs_get_tid(struct ieee80211_hdr *hdr)
{
u8 tid = IWL_MAX_TID_COUNT;
return;
}
+ /* This packet was aggregated but doesn't carry status info */
+ if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
+ !(info->flags & IEEE80211_TX_STAT_AMPDU))
+ return;
+
+ rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate);
+
#ifdef CONFIG_MAC80211_DEBUGFS
- /* Disable last tx check if we are debugging with fixed rate */
+ /* Disable last tx check if we are debugging with fixed rate but
+ * update tx stats */
if (lq_sta->pers.dbg_fixed_rate) {
- IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n");
+ int index = tx_resp_rate.index;
+ enum rs_column column;
+ int attempts, success;
+
+ column = rs_get_column_from_rate(&tx_resp_rate);
+ if (WARN_ONCE(column == RS_COLUMN_INVALID,
+ "Can't map rate 0x%x to column",
+ tx_resp_hwrate))
+ return;
+
+ if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+ attempts = info->status.ampdu_len;
+ success = info->status.ampdu_ack_len;
+ } else {
+ attempts = info->status.rates[0].count;
+ success = !!(info->flags & IEEE80211_TX_STAT_ACK);
+ }
+
+ lq_sta->pers.tx_stats[column][index].total += attempts;
+ lq_sta->pers.tx_stats[column][index].success += success;
+
+ IWL_DEBUG_RATE(mvm, "Fixed rate 0x%x success %d attempts %d\n",
+ tx_resp_hwrate, success, attempts);
return;
}
#endif
- /* This packet was aggregated but doesn't carry status info */
- if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
- !(info->flags & IEEE80211_TX_STAT_AMPDU))
- return;
if (time_after(jiffies,
(unsigned long)(lq_sta->last_tx +
table = &lq_sta->lq;
lq_hwrate = le32_to_cpu(table->rs_table[0]);
rs_rate_from_ucode_rate(lq_hwrate, info->band, &lq_rate);
- rs_rate_from_ucode_rate(tx_resp_hwrate, info->band, &tx_resp_rate);
/* Here we actually compare this rate to the latest LQ command */
if (!rs_rate_equal(&tx_resp_rate, &lq_rate, allow_ant_mismatch)) {
info->status.ampdu_ack_len);
}
} else {
- /*
- * For legacy, update frame history with for each Tx retry.
- */
+ /* For legacy, update frame history with for each Tx retry. */
retries = info->status.rates[0].count - 1;
/* HW doesn't send more than 15 retries */
retries = min(retries, 15);
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ if (!iwl_mvm_sta_from_mac80211(sta)->vif)
+ return;
+
if (!ieee80211_is_data(hdr->frame_control) ||
info->flags & IEEE80211_TX_CTL_NO_ACK)
return;
static void rs_update_rate_tbl(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
struct iwl_lq_sta *lq_sta,
- struct rs_rate *rate)
+ struct iwl_scale_tbl_info *tbl)
{
- rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
+ rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
}
rate->type = LQ_NONE;
lq_sta->search_better_tbl = 0;
tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
+ rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
}
return;
}
/* Replace uCode's rate table for the destination station. */
if (update_lq) {
tbl->rate.index = index;
- rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
+ rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
}
rs_stay_in_table(lq_sta, false);
rs_dump_rate(mvm, &tbl->rate,
"Switch to SEARCH TABLE:");
- rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
- iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
+ rs_update_rate_tbl(mvm, sta, lq_sta, tbl);
} else {
done_search = 1;
}
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_lq_sta *lq_sta = mvm_sta;
+ if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) {
+ /* if vif isn't initialized mvm doesn't know about
+ * this station, so don't do anything with the it
+ */
+ sta = NULL;
+ mvm_sta = NULL;
+ }
+
/* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
/* Treat uninitialized rate scaling data same as non-existing. */
(struct iwl_op_mode *)mvm_r;
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ if (!iwl_mvm_sta_from_mac80211(sta)->vif)
+ return;
+
/* Stop any ongoing aggregations as rs starts off assuming no agg */
for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
ieee80211_stop_tx_ba_session(sta, tid);
lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
/*
- * In case of low latency, tell the firwmare to leave a frame in the
+ * In case of low latency, tell the firmware to leave a frame in the
* Tx Fifo so that it can start a transaction in the same TxOP. This
* basically allows the firmware to send bursts.
*/
(is_legacy(rate)) ? "legacy" :
is_vht(rate) ? "VHT" : "HT");
if (!is_legacy(rate)) {
- desc += sprintf(buff+desc, " %s",
+ desc += sprintf(buff + desc, " %s",
(is_siso(rate)) ? "SISO" : "MIMO2");
- desc += sprintf(buff+desc, " %s",
- (is_ht20(rate)) ? "20MHz" :
- (is_ht40(rate)) ? "40MHz" :
- (is_ht80(rate)) ? "80Mhz" : "BAD BW");
- desc += sprintf(buff+desc, " %s %s %s\n",
- (rate->sgi) ? "SGI" : "NGI",
- (rate->ldpc) ? "LDPC" : "BCC",
- (lq_sta->is_agg) ? "AGG on" : "");
+ desc += sprintf(buff + desc, " %s",
+ (is_ht20(rate)) ? "20MHz" :
+ (is_ht40(rate)) ? "40MHz" :
+ (is_ht80(rate)) ? "80Mhz" : "BAD BW");
+ desc += sprintf(buff + desc, " %s %s %s\n",
+ (rate->sgi) ? "SGI" : "NGI",
+ (rate->ldpc) ? "LDPC" : "BCC",
+ (lq_sta->is_agg) ? "AGG on" : "");
}
desc += sprintf(buff+desc, "last tx rate=0x%X\n",
lq_sta->last_rate_n_flags);
ss_params = le32_to_cpu(lq_sta->lq.ss_params);
desc += sprintf(buff+desc, "single stream params: %s%s%s%s\n",
(ss_params & LQ_SS_PARAMS_VALID) ?
- "VALID," : "INVALID",
+ "VALID" : "INVALID",
(ss_params & LQ_SS_BFER_ALLOWED) ?
- "BFER," : "",
+ ", BFER" : "",
(ss_params & LQ_SS_STBC_1SS_ALLOWED) ?
- "STBC," : "",
+ ", STBC" : "",
(ss_params & LQ_SS_FORCE) ?
- "FORCE" : "");
+ ", FORCE" : "");
desc += sprintf(buff+desc,
"Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
lq_sta->lq.initial_rate_index[0],
MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32);
-static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
+static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir)
{
- struct iwl_lq_sta *lq_sta = mvm_sta;
+ struct iwl_lq_sta *lq_sta = priv_sta;
+ struct iwl_mvm_sta *mvmsta;
+
+ mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta);
+
+ if (!mvmsta->vif)
+ return;
debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
lq_sta, &rs_sta_dbgfs_scale_table_ops);
/**
* iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
- * Tx protection, according to this rquest and previous requests,
+ * Tx protection, according to this request and previous requests,
* and send the LQ command.
* @mvmsta: The station
* @enable: Enable Tx protection?
iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif,
trig);
if (trig_check && rx_status->signal < rssi)
- iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL, 0);
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
}
}
if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold)
return;
- iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL, 0);
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL);
}
void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
cmd->n_channels = (u8)req->n_channels;
+ cmd->delay = cpu_to_le32(req->delay);
+
if (iwl_mvm_scan_pass_all(mvm, req))
flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
else
return false;
}
+static int iwl_mvm_find_first_scan(struct iwl_mvm *mvm,
+ enum iwl_umac_scan_uid_type type)
+{
+ int i;
+
+ for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+ if (mvm->scan_uid[i] & type)
+ return i;
+
+ return i;
+}
+
static u32 iwl_generate_scan_uid(struct iwl_mvm *mvm,
enum iwl_umac_scan_uid_type type)
{
cpu_to_le16(req->interval / MSEC_PER_SEC);
sec_part->schedule[0].iter_count = 0xff;
- sec_part->delay = 0;
+ if (req->delay > U16_MAX) {
+ IWL_DEBUG_SCAN(mvm,
+ "delay value is > 16-bits, set to max possible\n");
+ sec_part->delay = cpu_to_le16(U16_MAX);
+ } else {
+ sec_part->delay = cpu_to_le16(req->delay);
+ }
iwl_mvm_build_unified_scan_probe(mvm, vif, ies, &sec_part->preq,
req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
mvm->fw->ucode_capa.n_scan_channels +
sizeof(struct iwl_scan_probe_req);
}
+
+/*
+ * This function is used in nic restart flow, to inform mac80211 about scans
+ * that was aborted by restart flow or by an assert.
+ */
+void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm)
+{
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+ u32 uid, i;
+
+ uid = iwl_mvm_find_first_scan(mvm, IWL_UMAC_SCAN_UID_REG_SCAN);
+ if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS) {
+ ieee80211_scan_completed(mvm->hw, true);
+ mvm->scan_uid[uid] = 0;
+ }
+ uid = iwl_mvm_find_first_scan(mvm,
+ IWL_UMAC_SCAN_UID_SCHED_SCAN);
+ if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS && !mvm->restart_fw) {
+ ieee80211_sched_scan_stopped(mvm->hw);
+ mvm->scan_uid[uid] = 0;
+ }
+
+ /* We shouldn't have any UIDs still set. Loop over all the
+ * UIDs to make sure there's nothing left there and warn if
+ * any is found.
+ */
+ for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+ if (WARN_ONCE(mvm->scan_uid[i],
+ "UMAC scan UID %d was not cleaned\n",
+ mvm->scan_uid[i]))
+ mvm->scan_uid[i] = 0;
+ }
+ } else {
+ switch (mvm->scan_status) {
+ case IWL_MVM_SCAN_NONE:
+ break;
+ case IWL_MVM_SCAN_OS:
+ ieee80211_scan_completed(mvm->hw, true);
+ break;
+ case IWL_MVM_SCAN_SCHED:
+ /*
+ * Sched scan will be restarted by mac80211 in
+ * restart_hw, so do not report if FW is about to be
+ * restarted.
+ */
+ if (!mvm->restart_fw)
+ ieee80211_sched_scan_stopped(mvm->hw);
+ break;
+ }
+ }
+}
/*
* Aging and idle timeouts for the different possible scenarios
- * in SF_FULL_ON state.
+ * in default configuration
+ */
+static const
+__le32 sf_full_timeout_def[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
+ {
+ cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER_DEF),
+ cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
+ },
+ {
+ cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER_DEF),
+ cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER_DEF)
+ },
+ {
+ cpu_to_le32(SF_MCAST_AGING_TIMER_DEF),
+ cpu_to_le32(SF_MCAST_IDLE_TIMER_DEF)
+ },
+ {
+ cpu_to_le32(SF_BA_AGING_TIMER_DEF),
+ cpu_to_le32(SF_BA_IDLE_TIMER_DEF)
+ },
+ {
+ cpu_to_le32(SF_TX_RE_AGING_TIMER_DEF),
+ cpu_to_le32(SF_TX_RE_IDLE_TIMER_DEF)
+ },
+};
+
+/*
+ * Aging and idle timeouts for the different possible scenarios
+ * in single BSS MAC configuration.
*/
static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
{
},
};
-static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd,
+static void iwl_mvm_fill_sf_command(struct iwl_mvm *mvm,
+ struct iwl_sf_cfg_cmd *sf_cmd,
struct ieee80211_sta *sta)
{
int i, j, watermark;
cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
}
}
- BUILD_BUG_ON(sizeof(sf_full_timeout) !=
- sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES);
- memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
- sizeof(sf_full_timeout));
+ if (sta || IWL_UCODE_API(mvm->fw->ucode_ver) < 13) {
+ BUILD_BUG_ON(sizeof(sf_full_timeout) !=
+ sizeof(__le32) * SF_NUM_SCENARIO *
+ SF_NUM_TIMEOUT_TYPES);
+
+ memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
+ sizeof(sf_full_timeout));
+ } else {
+ BUILD_BUG_ON(sizeof(sf_full_timeout_def) !=
+ sizeof(__le32) * SF_NUM_SCENARIO *
+ SF_NUM_TIMEOUT_TYPES);
+
+ memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,
+ sizeof(sf_full_timeout_def));
+ }
+
}
static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
enum iwl_sf_state new_state)
{
struct iwl_sf_cfg_cmd sf_cmd = {
- .state = cpu_to_le32(new_state),
+ .state = cpu_to_le32(SF_FULL_ON),
};
struct ieee80211_sta *sta;
int ret = 0;
- if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF &&
- mvm->cfg->disable_dummy_notification)
+ if (IWL_UCODE_API(mvm->fw->ucode_ver) < 13)
+ sf_cmd.state = cpu_to_le32(new_state);
+
+ if (mvm->cfg->disable_dummy_notification)
sf_cmd.state |= cpu_to_le32(SF_CFG_DUMMY_NOTIF_OFF);
/*
switch (new_state) {
case SF_UNINIT:
+ if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 13)
+ iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
break;
case SF_FULL_ON:
if (sta_id == IWL_MVM_STATION_COUNT) {
rcu_read_unlock();
return -EINVAL;
}
- iwl_mvm_fill_sf_command(&sf_cmd, sta);
+ iwl_mvm_fill_sf_command(mvm, &sf_cmd, sta);
rcu_read_unlock();
break;
case SF_INIT_OFF:
- iwl_mvm_fill_sf_command(&sf_cmd, NULL);
+ iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL);
break;
default:
WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
{
unsigned long used_hw_queues;
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ?
- mvm->cfg->base_params->wd_timeout :
- IWL_WATCHDOG_DISABLED;
+ unsigned int wdg_timeout =
+ iwl_mvm_get_wd_timeout(mvm, NULL, true, false);
u32 ac;
lockdep_assert_held(&mvm->mutex);
else
sta_id = mvm_sta->sta_id;
- if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
+ if (sta_id == IWL_MVM_STATION_COUNT)
return -ENOSPC;
spin_lock_init(&mvm_sta->lock);
if (vif->type == NL80211_IFTYPE_STATION &&
mvmvif->ap_sta_id == mvm_sta->sta_id) {
+ ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
+ if (ret)
+ return ret;
/* flush its queues here since we are freeing mvm_sta */
ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
+ if (ret)
+ return ret;
+ ret = iwl_trans_wait_tx_queue_empty(mvm->trans,
+ mvm_sta->tfd_queue_msk);
+ if (ret)
+ return ret;
+ ret = iwl_mvm_drain_sta(mvm, mvm_sta, false);
/* if we are associated - we can't remove the AP STA now */
if (vif->bss_conf.assoc)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
- unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ?
- mvm->cfg->base_params->wd_timeout :
- IWL_WATCHDOG_DISABLED;
+ unsigned int wdg_timeout =
+ iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false);
int queue, fifo, ret;
u16 ssn;
spin_unlock_bh(&mvmsta->lock);
if (old_state >= IWL_AGG_ON) {
+ iwl_mvm_drain_sta(mvm, mvmsta, true);
if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+ iwl_trans_wait_tx_queue_empty(mvm->trans,
+ mvmsta->tfd_queue_msk);
+ iwl_mvm_drain_sta(mvm, mvmsta, false);
iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false);
};
int ret;
- if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_DISABLE_STA_TX))
- return;
-
ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
if (ret)
IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
mvm_sta->disable_tx = disable;
/*
- * Tell mac80211 to start/stop queueing tx for this station,
- * but don't stop queueing if there are still pending frames
+ * Tell mac80211 to start/stop queuing tx for this station,
+ * but don't stop queuing if there are still pending frames
* for this station.
*/
if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
* DOC: station table - AP Station in STA mode
*
* %iwl_mvm_vif includes the index of the AP station in the fw's STA table:
- * %ap_sta_id. To get the point to the coresponsding %ieee80211_sta,
+ * %ap_sta_id. To get the point to the corresponding %ieee80211_sta,
* &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove
* the AP station from the fw before setting the MAC context as unassociated.
* Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is
* When a trigger frame is received, mac80211 tells the driver to send frames
* from the AMPDU queues or sends frames to non-aggregation queues itself,
* depending on which ACs are delivery-enabled and what TID has frames to
- * transmit. Note that mac80211 has all the knowledege since all the non-agg
+ * transmit. Note that mac80211 has all the knowledge since all the non-agg
* frames are buffered / filtered, and the driver tells mac80211 about agg
* frames). The driver needs to tell the fw to let frames out even if the
* station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count.
*
* When we receive a frame from that station with PM bit unset, the driver
* needs to let the fw know that this station isn't asleep any more. This is
- * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signalling the
+ * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signaling the
* station's wakeup.
*
* For a GO, the Service Period might be cut short due to an absence period
/*
* Flush the offchannel queue -- this is called when the time
- * event finishes or is cancelled, so that frames queued for it
+ * event finishes or is canceled, so that frames queued for it
* won't get stuck on the queue and be transmitted in the next
* time event.
* We have to send the command asynchronously since this cannot
return false;
if (errmsg)
IWL_ERR(mvm, "%s\n", errmsg);
- ieee80211_connection_loss(vif);
+
+ iwl_mvm_connection_loss(mvm, vif, errmsg);
return true;
}
struct iwl_mvm_time_event_data *te_data,
struct iwl_time_event_notif *notif)
{
- if (!le32_to_cpu(notif->status)) {
+ struct ieee80211_vif *vif = te_data->vif;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (!notif->status)
IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
- iwl_mvm_te_clear_data(mvm, te_data);
- return;
- }
switch (te_data->vif->type) {
case NL80211_IFTYPE_AP:
+ if (!notif->status)
+ mvmvif->csa_failed = true;
iwl_mvm_csa_noa_start(mvm);
break;
case NL80211_IFTYPE_STATION:
+ if (!notif->status) {
+ iwl_mvm_connection_loss(mvm, vif,
+ "CSA TE failed to start");
+ break;
+ }
iwl_mvm_csa_client_absent(mvm, te_data->vif);
ieee80211_chswitch_done(te_data->vif, true);
break;
iwl_mvm_te_clear_data(mvm, te_data);
}
+static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm,
+ struct iwl_time_event_notif *notif,
+ struct iwl_mvm_time_event_data *te_data)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_time_event *te_trig;
+ int i;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT))
+ return;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT);
+ te_trig = (void *)trig->data;
+
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) {
+ u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id);
+ u32 trig_action_bitmap =
+ le32_to_cpu(te_trig->time_events[i].action_bitmap);
+ u32 trig_status_bitmap =
+ le32_to_cpu(te_trig->time_events[i].status_bitmap);
+
+ if (trig_te_id != te_data->id ||
+ !(trig_action_bitmap & le32_to_cpu(notif->action)) ||
+ !(trig_status_bitmap & BIT(le32_to_cpu(notif->status))))
+ continue;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+ "Time event %d Action 0x%x received status: %d",
+ te_data->id,
+ le32_to_cpu(notif->action),
+ le32_to_cpu(notif->status));
+ break;
+ }
+}
+
/*
* Handles a FW notification for an event that is known to the driver.
*
le32_to_cpu(notif->unique_id),
le32_to_cpu(notif->action));
+ iwl_mvm_te_check_trigger(mvm, notif, te_data);
+
/*
* The FW sends the start/end time event notifications even for events
* that it fails to schedule. This is indicated in the status field of
* events in the system).
*/
if (!le32_to_cpu(notif->status)) {
- bool start = le32_to_cpu(notif->action) &
- TE_V2_NOTIF_HOST_EVENT_START;
- IWL_WARN(mvm, "Time Event %s notification failure\n",
- start ? "start" : "end");
- if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) {
+ const char *msg;
+
+ if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START))
+ msg = "Time Event start notification failure";
+ else
+ msg = "Time Event end notification failure";
+
+ IWL_DEBUG_TE(mvm, "%s\n", msg);
+
+ if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) {
iwl_mvm_te_clear_data(mvm, te_data);
return;
}
"TE ended - current time %lu, estimated end %lu\n",
jiffies, te_data->end_jiffies);
- if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ switch (te_data->vif->type) {
+ case NL80211_IFTYPE_P2P_DEVICE:
ieee80211_remain_on_channel_expired(mvm->hw);
iwl_mvm_roc_finished(mvm);
+ break;
+ case NL80211_IFTYPE_STATION:
+ /*
+ * By now, we should have finished association
+ * and know the dtim period.
+ */
+ iwl_mvm_te_check_disconnect(mvm, te_data->vif,
+ "No association and the time event is over already...");
+ break;
+ default:
+ break;
}
- /*
- * By now, we should have finished association
- * and know the dtim period.
- */
- iwl_mvm_te_check_disconnect(mvm, te_data->vif,
- "No association and the time event is over already...");
iwl_mvm_te_clear_data(mvm, te_data);
} else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
te_data->running = true;
if (!aux_roc_te) /* Not a Aux ROC time event */
return -EINVAL;
+ iwl_mvm_te_check_trigger(mvm, notif, te_data);
+
if (!le32_to_cpu(notif->status)) {
IWL_DEBUG_TE(mvm,
"ERROR: Aux ROC Time Event %s notification failure\n",
* Iterate over the list of aux roc time events and find the time
* event that is associated with a BSS interface.
* This assumes that a BSS interface can have only a single time
- * event at any given time and this time event coresponds to a ROC
+ * event at any given time and this time event corresponds to a ROC
* request
*/
list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) {
* @vif: the virtual interface for which the session is issued
*
* This functions cancels the session protection which is an act of good
- * citizenship. If it is not needed any more it should be cancelled because
+ * citizenship. If it is not needed any more it should be canceled because
* the other bindings wait for the medium during that time.
* This funtions doesn't sleep.
*/
struct iwl_device_cmd *cmd);
/**
- * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionlity
+ * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality
* @mvm: the mvm component
* @vif: the virtual interface for which the roc is requested. It is assumed
* that the vif type is NL80211_IFTYPE_P2P_DEVICE
mvmsta = iwl_mvm_sta_from_mac80211(sta);
tid_data = &mvmsta->tid_data[tid];
- if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d",
- tid_data->txq_id, tid, scd_flow)) {
+ if (tid_data->txq_id != scd_flow) {
+ IWL_ERR(mvm,
+ "invalid BA notification: Q %d, tid %d, flow %d\n",
+ tid_data->txq_id, tid, scd_flow);
rcu_read_unlock();
return 0;
}
return 0;
}
+/*
+ * Note that there are transports that buffer frames before they reach
+ * the firmware. This means that after flush_tx_path is called, the
+ * queue might not be empty. The race-free way to handle this is to:
+ * 1) set the station as draining
+ * 2) flush the Tx path
+ * 3) wait for the transport queues to be empty
+ */
int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
{
int ret;
}
/*
- * We assume that the caller set the status to the sucess value
+ * We assume that the caller set the status to the success value
*/
int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
u32 *status)
}
/**
- * iwl_mvm_update_smps - Get a requst to change the SMPS mode
+ * iwl_mvm_update_smps - Get a request to change the SMPS mode
* @req_type: The part of the driver who call for a change.
* @smps_requests: The request to change the SMPS mode.
*
mvmvif->low_latency = value;
- res = iwl_mvm_update_quotas(mvm, NULL);
+ res = iwl_mvm_update_quotas(mvm, false, NULL);
if (res)
return res;
return bss_iter_data.vif;
}
+
+unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool tdls, bool cmd_q)
+{
+ struct iwl_fw_dbg_trigger_tlv *trigger;
+ struct iwl_fw_dbg_trigger_txq_timer *txq_timer;
+ unsigned int default_timeout =
+ cmd_q ? IWL_DEF_WD_TIMEOUT : mvm->cfg->base_params->wd_timeout;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS))
+ return iwlmvm_mod_params.tfd_q_hang_detect ?
+ default_timeout : IWL_WATCHDOG_DISABLED;
+
+ trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS);
+ txq_timer = (void *)trigger->data;
+
+ if (tdls)
+ return le32_to_cpu(txq_timer->tdls);
+
+ if (cmd_q)
+ return le32_to_cpu(txq_timer->command_queue);
+
+ if (WARN_ON(!vif))
+ return default_timeout;
+
+ switch (ieee80211_vif_type_p2p(vif)) {
+ case NL80211_IFTYPE_ADHOC:
+ return le32_to_cpu(txq_timer->ibss);
+ case NL80211_IFTYPE_STATION:
+ return le32_to_cpu(txq_timer->bss);
+ case NL80211_IFTYPE_AP:
+ return le32_to_cpu(txq_timer->softap);
+ case NL80211_IFTYPE_P2P_CLIENT:
+ return le32_to_cpu(txq_timer->p2p_client);
+ case NL80211_IFTYPE_P2P_GO:
+ return le32_to_cpu(txq_timer->p2p_go);
+ case NL80211_IFTYPE_P2P_DEVICE:
+ return le32_to_cpu(txq_timer->p2p_device);
+ default:
+ WARN_ON(1);
+ return mvm->cfg->base_params->wd_timeout;
+ }
+}
+
+void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ const char *errmsg)
+{
+ struct iwl_fw_dbg_trigger_tlv *trig;
+ struct iwl_fw_dbg_trigger_mlme *trig_mlme;
+
+ if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
+ goto out;
+
+ trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
+ trig_mlme = (void *)trig->data;
+ if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+ goto out;
+
+ if (trig_mlme->stop_connection_loss &&
+ --trig_mlme->stop_connection_loss)
+ goto out;
+
+ iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg);
+
+out:
+ ieee80211_connection_loss(vif);
+}
/* 3165 Series */
{IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)},
{IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4210, iwl3165_2ac_cfg)},
{IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)},
{IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)},
/* 7265 Series */
{IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
/* 8000 Series */
{IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x1130, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0xC030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0xD030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)},
{IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)},
{IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)},
#endif /* CONFIG_IWLMVM */
{0}
* @active: stores if queue is active
* @ampdu: true if this queue is an ampdu queue for an specific RA/TID
* @wd_timeout: queue watchdog timeout (jiffies) - per queue
+ * @frozen: tx stuck queue timer is frozen
+ * @frozen_expiry_remainder: remember how long until the timer fires
*
* A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
* descriptors) and required locking structures.
dma_addr_t scratchbufs_dma;
struct iwl_pcie_txq_entry *entries;
spinlock_t lock;
+ unsigned long frozen_expiry_remainder;
struct timer_list stuck_timer;
struct iwl_trans_pcie *trans_pcie;
bool need_update;
+ bool frozen;
u8 active;
bool ampdu;
unsigned long wd_timeout;
if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID))
break;
- IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n",
- rxcb._offset, get_cmd_string(trans_pcie, pkt->hdr.cmd),
- pkt->hdr.cmd);
+ IWL_DEBUG_RX(trans,
+ "cmd at offset %d: %s (0x%.2x, seq 0x%x)\n",
+ rxcb._offset,
+ get_cmd_string(trans_pcie, pkt->hdr.cmd),
+ pkt->hdr.cmd, le16_to_cpu(pkt->hdr.sequence));
len = iwl_rx_packet_len(pkt);
len += sizeof(u32); /* account for status word */
return ret;
}
-static int iwl_pcie_load_cpu_sections_8000b(struct iwl_trans *trans,
- const struct fw_img *image,
- int cpu,
- int *first_ucode_section)
+/*
+ * Driver Takes the ownership on secure machine before FW load
+ * and prevent race with the BT load.
+ * W/A for ROM bug. (should be remove in the next Si step)
+ */
+static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans)
+{
+ u32 val, loop = 1000;
+
+ /*
+ * Check the RSA semaphore is accessible.
+ * If the HW isn't locked and the rsa semaphore isn't accessible,
+ * we are in trouble.
+ */
+ val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0);
+ if (val & (BIT(1) | BIT(17))) {
+ IWL_INFO(trans,
+ "can't access the RSA semaphore it is write protected\n");
+ return 0;
+ }
+
+ /* take ownership on the AUX IF */
+ iwl_write_prph(trans, WFPM_CTRL_REG, WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK);
+ iwl_write_prph(trans, AUX_MISC_MASTER1_EN, AUX_MISC_MASTER1_EN_SBE_MSK);
+
+ do {
+ iwl_write_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS, 0x1);
+ val = iwl_read_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS);
+ if (val == 0x1) {
+ iwl_write_prph(trans, RSA_ENABLE, 0);
+ return 0;
+ }
+
+ udelay(10);
+ loop--;
+ } while (loop > 0);
+
+ IWL_ERR(trans, "Failed to take ownership on secure machine\n");
+ return -EIO;
+}
+
+static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans,
+ const struct fw_img *image,
+ int cpu,
+ int *first_ucode_section)
{
int shift_param;
int i, ret = 0, sec_num = 0x1;
}
/* release CPU reset */
- if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
- iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
- else
- iwl_write32(trans, CSR_RESET, 0);
+ iwl_write32(trans, CSR_RESET, 0);
return 0;
}
-static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
- const struct fw_img *image)
+static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
+ const struct fw_img *image)
{
int ret = 0;
int first_ucode_section;
- u32 reg;
IWL_DEBUG_FW(trans, "working with %s CPU\n",
image->is_dual_cpus ? "Dual" : "Single");
if (trans->dbg_dest_tlv)
iwl_pcie_apply_destination(trans);
+ /* TODO: remove in the next Si step */
+ ret = iwl_pcie_rsa_race_bug_wa(trans);
+ if (ret)
+ return ret;
+
/* configure the ucode to be ready to get the secured image */
/* release CPU reset */
iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
/* load to FW the binary Secured sections of CPU1 */
- ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 1,
- &first_ucode_section);
+ ret = iwl_pcie_load_cpu_sections_8000(trans, image, 1,
+ &first_ucode_section);
if (ret)
return ret;
/* load to FW the binary sections of CPU2 */
- ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 2,
- &first_ucode_section);
+ ret = iwl_pcie_load_cpu_sections_8000(trans, image, 2,
+ &first_ucode_section);
if (ret)
return ret;
- /* wait for image verification to complete */
- ret = iwl_poll_prph_bit(trans, LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0,
- LMPM_SECURE_BOOT_STATUS_SUCCESS,
- LMPM_SECURE_BOOT_STATUS_SUCCESS,
- LMPM_SECURE_TIME_OUT);
- if (ret < 0) {
- reg = iwl_read_prph(trans,
- LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0);
-
- IWL_ERR(trans, "Timeout on secure boot process, reg = %x\n",
- reg);
- return ret;
- }
-
return 0;
}
static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
const struct fw_img *fw, bool run_in_rfkill)
{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret;
bool hw_rfkill;
return ret;
}
- /* init ref_count to 1 (should be cleared when ucode is loaded) */
- trans_pcie->ref_count = 1;
-
/* make sure rfkill handshake bits are cleared */
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR,
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
/* Load the given image to the HW */
- if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) &&
- (CSR_HW_REV_STEP(trans->hw_rev) != SILICON_A_STEP))
- return iwl_pcie_load_given_ucode_8000b(trans, fw);
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ return iwl_pcie_load_given_ucode_8000(trans, fw);
else
return iwl_pcie_load_given_ucode(trans, fw);
}
trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
trans_pcie->scd_set_active = trans_cfg->scd_set_active;
+ /* init ref_count to 1 (should be cleared when ucode is loaded) */
+ trans_pcie->ref_count = 1;
+
/* Initialize NAPI here - it should be before registering to mac80211
* in the opmode but after the HW struct is allocated.
* As this function may be called again in some corner cases don't
return ret;
}
+static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans,
+ unsigned long txqs,
+ bool freeze)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int queue;
+
+ for_each_set_bit(queue, &txqs, BITS_PER_LONG) {
+ struct iwl_txq *txq = &trans_pcie->txq[queue];
+ unsigned long now;
+
+ spin_lock_bh(&txq->lock);
+
+ now = jiffies;
+
+ if (txq->frozen == freeze)
+ goto next_queue;
+
+ IWL_DEBUG_TX_QUEUES(trans, "%s TXQ %d\n",
+ freeze ? "Freezing" : "Waking", queue);
+
+ txq->frozen = freeze;
+
+ if (txq->q.read_ptr == txq->q.write_ptr)
+ goto next_queue;
+
+ if (freeze) {
+ if (unlikely(time_after(now,
+ txq->stuck_timer.expires))) {
+ /*
+ * The timer should have fired, maybe it is
+ * spinning right now on the lock.
+ */
+ goto next_queue;
+ }
+ /* remember how long until the timer fires */
+ txq->frozen_expiry_remainder =
+ txq->stuck_timer.expires - now;
+ del_timer(&txq->stuck_timer);
+ goto next_queue;
+ }
+
+ /*
+ * Wake a non-empty queue -> arm timer with the
+ * remainder before it froze
+ */
+ mod_timer(&txq->stuck_timer,
+ now + txq->frozen_expiry_remainder);
+
+next_queue:
+ spin_unlock_bh(&txq->lock);
+ }
+}
+
#define IWL_FLUSH_WAIT_MS 2000
static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm)
int ret;
size_t bufsz;
- bufsz = sizeof(char) * 64 * trans->cfg->base_params->num_of_queues;
+ bufsz = sizeof(char) * 75 * trans->cfg->base_params->num_of_queues;
if (!trans_pcie->txq)
return -EAGAIN;
txq = &trans_pcie->txq[cnt];
q = &txq->q;
pos += scnprintf(buf + pos, bufsz - pos,
- "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d%s\n",
+ "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d frozen=%d%s\n",
cnt, q->read_ptr, q->write_ptr,
!!test_bit(cnt, trans_pcie->queue_used),
!!test_bit(cnt, trans_pcie->queue_stopped),
- txq->need_update,
+ txq->need_update, txq->frozen,
(cnt == trans_pcie->cmd_queue ? " HCMD" : ""));
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
{ .start = 0x00a01c7c, .end = 0x00a01c7c },
{ .start = 0x00a01c28, .end = 0x00a01c54 },
{ .start = 0x00a01c5c, .end = 0x00a01c5c },
- { .start = 0x00a01c84, .end = 0x00a01c84 },
+ { .start = 0x00a01c60, .end = 0x00a01cdc },
{ .start = 0x00a01ce0, .end = 0x00a01d0c },
{ .start = 0x00a01d18, .end = 0x00a01d20 },
{ .start = 0x00a01d2c, .end = 0x00a01d30 },
{ .start = 0x00a01d40, .end = 0x00a01d5c },
{ .start = 0x00a01d80, .end = 0x00a01d80 },
- { .start = 0x00a01d98, .end = 0x00a01d98 },
+ { .start = 0x00a01d98, .end = 0x00a01d9c },
+ { .start = 0x00a01da8, .end = 0x00a01da8 },
+ { .start = 0x00a01db8, .end = 0x00a01df4 },
{ .start = 0x00a01dc0, .end = 0x00a01dfc },
{ .start = 0x00a01e00, .end = 0x00a01e2c },
{ .start = 0x00a01e40, .end = 0x00a01e60 },
+ { .start = 0x00a01e68, .end = 0x00a01e6c },
+ { .start = 0x00a01e74, .end = 0x00a01e74 },
{ .start = 0x00a01e84, .end = 0x00a01e90 },
{ .start = 0x00a01e9c, .end = 0x00a01ec4 },
- { .start = 0x00a01ed0, .end = 0x00a01ed0 },
- { .start = 0x00a01f00, .end = 0x00a01f14 },
- { .start = 0x00a01f44, .end = 0x00a01f58 },
- { .start = 0x00a01f80, .end = 0x00a01fa8 },
- { .start = 0x00a01fb0, .end = 0x00a01fbc },
- { .start = 0x00a01ff8, .end = 0x00a01ffc },
+ { .start = 0x00a01ed0, .end = 0x00a01ee0 },
+ { .start = 0x00a01f00, .end = 0x00a01f1c },
+ { .start = 0x00a01f44, .end = 0x00a01ffc },
{ .start = 0x00a02000, .end = 0x00a02048 },
{ .start = 0x00a02068, .end = 0x00a020f0 },
{ .start = 0x00a02100, .end = 0x00a02118 },
.dbgfs_register = iwl_trans_pcie_dbgfs_register,
.wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty,
+ .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
.write8 = iwl_trans_pcie_write8,
.write32 = iwl_trans_pcie_write32,
* "dash" value). To keep hw_rev backwards compatible - we'll store it
* in the old format.
*/
- if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
+ unsigned long flags;
+ int ret;
+
trans->hw_rev = (trans->hw_rev & 0xfff0) |
(CSR_HW_REV_STEP(trans->hw_rev << 2) << 2);
+ /*
+ * in-order to recognize C step driver should read chip version
+ * id located at the AUX bus MISC address space.
+ */
+ iwl_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+ udelay(2);
+
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
+ 25000);
+ if (ret < 0) {
+ IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n");
+ goto out_pci_disable_msi;
+ }
+
+ if (iwl_trans_grab_nic_access(trans, false, &flags)) {
+ u32 hw_step;
+
+ hw_step = __iwl_read_prph(trans, WFPM_CTRL_REG);
+ hw_step |= ENABLE_WFPM;
+ __iwl_write_prph(trans, WFPM_CTRL_REG, hw_step);
+ hw_step = __iwl_read_prph(trans, AUX_MISC_REG);
+ hw_step = (hw_step >> HW_STEP_LOCATION_BITS) & 0xF;
+ if (hw_step == 0x3)
+ trans->hw_rev = (trans->hw_rev & 0xFFFFFFF3) |
+ (SILICON_C_STEP << 2);
+ iwl_trans_release_nic_access(trans, &flags);
+ }
+ }
+
trans->hw_id = (pdev->device << 16) + pdev->subsystem_device;
snprintf(trans->hw_id_str, sizeof(trans->hw_id_str),
"PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device);
iwl_pcie_tx_start(trans, 0);
}
+static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ unsigned long flags;
+ int ch, ret;
+ u32 mask = 0;
+
+ spin_lock(&trans_pcie->irq_lock);
+
+ if (!iwl_trans_grab_nic_access(trans, false, &flags))
+ goto out;
+
+ /* Stop each Tx DMA channel */
+ for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) {
+ iwl_write32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
+ mask |= FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch);
+ }
+
+ /* Wait for DMA channels to be idle */
+ ret = iwl_poll_bit(trans, FH_TSSR_TX_STATUS_REG, mask, mask, 5000);
+ if (ret < 0)
+ IWL_ERR(trans,
+ "Failing on timeout while stopping DMA channel %d [0x%08x]\n",
+ ch, iwl_read32(trans, FH_TSSR_TX_STATUS_REG));
+
+ iwl_trans_release_nic_access(trans, &flags);
+
+out:
+ spin_unlock(&trans_pcie->irq_lock);
+}
+
/*
* iwl_pcie_tx_stop - Stop all Tx DMA channels
*/
int iwl_pcie_tx_stop(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int ch, txq_id, ret;
+ int txq_id;
/* Turn off all Tx DMA fifos */
- spin_lock(&trans_pcie->irq_lock);
-
iwl_scd_deactivate_fifos(trans);
- /* Stop each Tx DMA channel, and wait for it to be idle */
- for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) {
- iwl_write_direct32(trans,
- FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
- ret = iwl_poll_direct_bit(trans, FH_TSSR_TX_STATUS_REG,
- FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000);
- if (ret < 0)
- IWL_ERR(trans,
- "Failing on timeout while stopping DMA channel %d [0x%08x]\n",
- ch,
- iwl_read_direct32(trans,
- FH_TSSR_TX_STATUS_REG));
- }
- spin_unlock(&trans_pcie->irq_lock);
+ /* Turn off all Tx DMA channels */
+ iwl_pcie_tx_stop_fh(trans);
/*
* This function can be called before the op_mode disabled the
static inline void iwl_pcie_txq_progress(struct iwl_txq *txq)
{
+ lockdep_assert_held(&txq->lock);
+
if (!txq->wd_timeout)
return;
+ /*
+ * station is asleep and we send data - that must
+ * be uAPSD or PS-Poll. Don't rearm the timer.
+ */
+ if (txq->frozen)
+ return;
+
/*
* if empty delete timer, otherwise move timer forward
* since we're making progress on this queue
SCD_TX_STTS_QUEUE_OFFSET(txq_id);
static const u32 zero_val[4] = {};
+ trans_pcie->txq[txq_id].frozen_expiry_remainder = 0;
+ trans_pcie->txq[txq_id].frozen = false;
+
/*
* Upon HW Rfkill - we stop the device, and then stop the queues
* in the op_mode. Just for the sake of the simplicity of the op_mode,
return ret;
}
-EXPORT_SYMBOL_GPL(if_usb_reset_device);
/**
* usb_tx_block - transfer data to the device
lbtf_deb_leave_args(LBTF_DEB_USB, "ret %d", ret);
return ret;
}
-EXPORT_SYMBOL_GPL(if_usb_prog_firmware);
#define if_usb_suspend NULL
int tid;
struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
+ struct mwifiex_ra_list_tbl *ra_list;
u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
>> BLOCKACKPARAM_TID_POS;
+ ra_list = mwifiex_wmm_get_ralist_node(priv, tid, add_ba_rsp->
+ peer_mac_addr);
if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
+ if (ra_list) {
+ ra_list->ba_status = BA_SETUP_NONE;
+ ra_list->amsdu_in_ampdu = false;
+ }
mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,
TYPE_DELBA_SENT, true);
if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)
tx_ba_tbl->amsdu = true;
else
tx_ba_tbl->amsdu = false;
+ if (ra_list) {
+ ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu;
+ ra_list->ba_status = BA_SETUP_COMPLETE;
+ }
} else {
dev_err(priv->adapter->dev, "BA stream not created\n");
}
enum mwifiex_ba_status ba_status)
{
struct mwifiex_tx_ba_stream_tbl *new_node;
+ struct mwifiex_ra_list_tbl *ra_list;
unsigned long flags;
if (!mwifiex_get_ba_tbl(priv, tid, ra)) {
GFP_ATOMIC);
if (!new_node)
return;
-
+ ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra);
+ if (ra_list) {
+ ra_list->ba_status = ba_status;
+ ra_list->amsdu_in_ampdu = false;
+ }
INIT_LIST_HEAD(&new_node->list);
new_node->tid = tid;
return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false;
}
-/* This function checks whether AMSDU is allowed for BA stream. */
-static inline u8
-mwifiex_is_amsdu_in_ampdu_allowed(struct mwifiex_private *priv,
- struct mwifiex_ra_list_tbl *ptr, int tid)
-{
- struct mwifiex_tx_ba_stream_tbl *tx_tbl;
-
- if (is_broadcast_ether_addr(ptr->ra))
- return false;
- tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra);
- if (tx_tbl)
- return tx_tbl->amsdu;
-
- return false;
-}
-
/* This function checks whether AMPDU is allowed or not for a particular TID. */
static inline u8
mwifiex_is_ampdu_allowed(struct mwifiex_private *priv,
return ret;
}
-/*
- * This function checks whether BA stream is set up or not.
- */
-static inline int
-mwifiex_is_ba_stream_setup(struct mwifiex_private *priv,
- struct mwifiex_ra_list_tbl *ptr, int tid)
-{
- struct mwifiex_tx_ba_stream_tbl *tx_tbl;
-
- tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra);
- if (tx_tbl && IS_BASTREAM_SETUP(tx_tbl))
- return true;
-
- return false;
-}
-
/*
* This function checks whether associated station is 11n enabled
*/
struct mwifiex_adapter *adapter = priv->adapter;
struct sk_buff *skb_aggr, *skb_src;
struct mwifiex_txinfo *tx_info_aggr, *tx_info_src;
- int pad = 0, ret;
+ int pad = 0, aggr_num = 0, ret;
struct mwifiex_tx_param tx_param;
struct txpd *ptx_pd = NULL;
struct timeval tv;
}
tx_info_src = MWIFIEX_SKB_TXCB(skb_src);
- skb_aggr = dev_alloc_skb(adapter->tx_buf_size);
+ skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size,
+ GFP_ATOMIC | GFP_DMA);
if (!skb_aggr) {
dev_err(adapter->dev, "%s: alloc skb_aggr\n", __func__);
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
+ tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT;
skb_aggr->priority = skb_src->priority;
do_gettimeofday(&tv);
break;
skb_src = skb_dequeue(&pra_list->skb_head);
-
pra_list->total_pkt_count--;
-
atomic_dec(&priv->wmm.tx_pkts_queued);
-
+ aggr_num++;
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
ra_list_flags);
mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad);
ptx_pd = (struct txpd *)skb_aggr->data;
skb_push(skb_aggr, headroom);
+ tx_info_aggr->aggr_num = aggr_num * 2;
+ if (adapter->data_sent || adapter->tx_lock_flag) {
+ atomic_add(aggr_num * 2, &adapter->tx_queued);
+ skb_queue_tail(&adapter->tx_data_q, skb_aggr);
+ return 0;
+ }
if (adapter->iface_type == MWIFIEX_USB) {
adapter->data_sent = true;
{
struct mwifiex_rx_reorder_tbl *tbl;
struct mwifiex_tx_ba_stream_tbl *ptx_tbl;
+ struct mwifiex_ra_list_tbl *ra_list;
u8 cleanup_rx_reorder_tbl;
unsigned long flags;
"event: TID, RA not found in table\n");
return;
}
-
+ ra_list = mwifiex_wmm_get_ralist_node(priv, tid, peer_mac);
+ if (ra_list) {
+ ra_list->amsdu_in_ampdu = false;
+ ra_list->ba_status = BA_SETUP_NONE;
+ }
spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl);
spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
static int mwifiex_deinit_priv_params(struct mwifiex_private *priv)
{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ unsigned long flags;
+
priv->mgmt_frame_mask = 0;
if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG,
HostCmd_ACT_GEN_SET, 0,
}
mwifiex_deauthenticate(priv, NULL);
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ adapter->main_locked = true;
+ if (adapter->mwifiex_processing) {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ flush_workqueue(adapter->workqueue);
+ } else {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ }
+
+ spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+ adapter->rx_locked = true;
+ if (adapter->rx_processing) {
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ flush_workqueue(adapter->rx_workqueue);
+ } else {
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ }
+
mwifiex_free_priv(priv);
priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
struct net_device *dev,
enum nl80211_iftype type)
{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ unsigned long flags;
+
mwifiex_init_priv(priv);
priv->bss_mode = type;
return -EOPNOTSUPP;
}
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ adapter->main_locked = false;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+
+ spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+ adapter->rx_locked = false;
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+
return 0;
}
}
#ifdef CONFIG_PM
-static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
- struct cfg80211_wowlan *wowlan)
+static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv,
+ struct mwifiex_mef_entry *mef_entry)
+{
+ int i, filt_num = 0, num_ipv4 = 0;
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+ __be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR];
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ mef_entry->mode = MEF_MODE_HOST_SLEEP;
+ mef_entry->action = MEF_ACTION_AUTO_ARP;
+
+ /* Enable ARP offload feature */
+ memset(ips, 0, sizeof(ips));
+ for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
+ if (adapter->priv[i]->netdev) {
+ in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev);
+ if (!in_dev)
+ continue;
+ ifa = in_dev->ifa_list;
+ if (!ifa || !ifa->ifa_local)
+ continue;
+ ips[i] = ifa->ifa_local;
+ num_ipv4++;
+ }
+ }
+
+ for (i = 0; i < num_ipv4; i++) {
+ if (!ips[i])
+ continue;
+ mef_entry->filter[filt_num].repeat = 1;
+ memcpy(mef_entry->filter[filt_num].byte_seq,
+ (u8 *)&ips[i], sizeof(ips[i]));
+ mef_entry->filter[filt_num].
+ byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+ sizeof(ips[i]);
+ mef_entry->filter[filt_num].offset = 46;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ if (filt_num) {
+ mef_entry->filter[filt_num].filt_action =
+ TYPE_OR;
+ }
+ filt_num++;
+ }
+
+ mef_entry->filter[filt_num].repeat = 1;
+ mef_entry->filter[filt_num].byte_seq[0] = 0x08;
+ mef_entry->filter[filt_num].byte_seq[1] = 0x06;
+ mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2;
+ mef_entry->filter[filt_num].offset = 20;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ mef_entry->filter[filt_num].filt_action = TYPE_AND;
+}
+
+static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv,
+ struct mwifiex_ds_mef_cfg *mef_cfg,
+ struct mwifiex_mef_entry *mef_entry,
+ struct cfg80211_wowlan *wowlan)
{
int i, filt_num = 0, ret = 0;
bool first_pat = true;
u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
const u8 ipv4_mc_mac[] = {0x33, 0x33};
const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
- struct mwifiex_ds_mef_cfg mef_cfg;
- struct mwifiex_mef_entry *mef_entry;
- mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL);
- if (!mef_entry)
- return -ENOMEM;
-
- memset(&mef_cfg, 0, sizeof(mef_cfg));
- mef_cfg.num_entries = 1;
- mef_cfg.mef_entry = mef_entry;
mef_entry->mode = MEF_MODE_HOST_SLEEP;
mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST;
if (!wowlan->patterns[i].pkt_offset) {
if (!(byte_seq[0] & 0x01) &&
(byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) {
- mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
+ mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
continue;
} else if (is_broadcast_ether_addr(byte_seq)) {
- mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST;
+ mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST;
continue;
} else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
(byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) ||
(!memcmp(byte_seq, ipv6_mc_mac, 3) &&
(byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) {
- mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST;
+ mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST;
continue;
}
}
-
mef_entry->filter[filt_num].repeat = 1;
mef_entry->filter[filt_num].offset =
wowlan->patterns[i].pkt_offset;
}
if (wowlan->magic_pkt) {
- mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
+ mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
mef_entry->filter[filt_num].repeat = 16;
memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
ETH_ALEN);
mef_entry->filter[filt_num].filt_type = TYPE_EQ;
mef_entry->filter[filt_num].filt_action = TYPE_OR;
}
+ return ret;
+}
+
+static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
+ struct cfg80211_wowlan *wowlan)
+{
+ int ret = 0, num_entries = 1;
+ struct mwifiex_ds_mef_cfg mef_cfg;
+ struct mwifiex_mef_entry *mef_entry;
+
+ if (wowlan->n_patterns || wowlan->magic_pkt)
+ num_entries++;
+
+ mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL);
+ if (!mef_entry)
+ return -ENOMEM;
+
+ memset(&mef_cfg, 0, sizeof(mef_cfg));
+ mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST |
+ MWIFIEX_CRITERIA_UNICAST;
+ mef_cfg.num_entries = num_entries;
+ mef_cfg.mef_entry = mef_entry;
+
+ mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]);
+
+ if (wowlan->n_patterns || wowlan->magic_pkt)
+ ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg,
+ &mef_entry[1], wowlan);
if (!mef_cfg.criteria)
mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST |
MWIFIEX_CRITERIA_MULTICAST;
ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG,
- HostCmd_ACT_GEN_SET, 0, &mef_cfg, true);
-
+ HostCmd_ACT_GEN_SET, 0,
+ &mef_cfg, true);
kfree(mef_entry);
return ret;
}
{
struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
struct mwifiex_ds_hs_cfg hs_cfg;
- int ret = 0;
- struct mwifiex_private *priv =
- mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+ int i, ret = 0;
+ struct mwifiex_private *priv;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ mwifiex_abort_cac(priv);
+ }
+
+ mwifiex_cancel_all_pending_cmd(adapter);
if (!wowlan) {
dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n");
return 0;
}
+ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+
if (!priv->media_connected) {
dev_warn(adapter->dev,
"Can not configure WOWLAN in disconnected state\n");
return 0;
}
- if (wowlan->n_patterns || wowlan->magic_pkt) {
- ret = mwifiex_set_mef_filter(priv, wowlan);
- if (ret) {
- dev_err(adapter->dev, "Failed to set MEF filter\n");
- return ret;
- }
+ ret = mwifiex_set_mef_filter(priv, wowlan);
+ if (ret) {
+ dev_err(adapter->dev, "Failed to set MEF filter\n");
+ return ret;
}
if (wowlan->disconnect) {
#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2)
#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3)
#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4)
+#define MWIFIEX_BUF_FLAG_AGGR_PKT BIT(5)
#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024
#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128
#define MWIFIEX_A_BAND_START_FREQ 5000
+/* SDIO Aggr data packet special info */
+#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255)
+#define BLOCK_NUMBER_OFFSET 15
+#define SDIO_HEADER_OFFSET 28
+
enum mwifiex_bss_type {
MWIFIEX_BSS_TYPE_STA = 0,
MWIFIEX_BSS_TYPE_UAP = 1,
};
struct mwifiex_rxinfo {
+ struct sk_buff *parent;
u8 bss_num;
u8 bss_type;
- struct sk_buff *parent;
u8 use_count;
+ u8 buf_type;
};
struct mwifiex_txinfo {
u8 flags;
u8 bss_num;
u8 bss_type;
+ u8 aggr_num;
u32 pkt_len;
u8 ack_frame_id;
u64 cookie;
#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14))
+#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16))
#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \
#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d
#define HostCmd_CMD_11AC_CFG 0x0112
#define HostCmd_CMD_TDLS_OPER 0x0122
+#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223
#define PROTOCOL_NO_SECURITY 0x01
#define PROTOCOL_STATIC_WEP 0x02
#define TYPE_OR (MAX_OPERAND+5)
#define MEF_MODE_HOST_SLEEP 1
#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3
+#define MEF_ACTION_AUTO_ARP 0x10
#define MWIFIEX_CRITERIA_BROADCAST BIT(0)
#define MWIFIEX_CRITERIA_UNICAST BIT(1)
#define MWIFIEX_CRITERIA_MULTICAST BIT(3)
+#define MWIFIEX_MAX_SUPPORTED_IPADDR 4
#define ACT_TDLS_DELETE 0x00
#define ACT_TDLS_CREATE 0x01
u8 tlvbuf[0];
} __packed;
+struct host_cmd_sdio_sp_rx_aggr_cfg {
+ u8 action;
+ u8 enable;
+ __le16 block_size;
+} __packed;
+
struct mwifiex_fixed_bcn_param {
__le64 timestamp;
__le16 beacon_period;
struct host_cmd_ds_coalesce_cfg coalesce_cfg;
struct host_cmd_ds_tdls_oper tdls_oper;
struct host_cmd_ds_chan_rpt_req chan_rpt_req;
+ struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg;
} params;
} __packed;
mwifiex_wmm_init(adapter);
- if (adapter->sleep_cfm) {
- sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *)
- adapter->sleep_cfm->data;
- memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
- sleep_cfm_buf->command =
- cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
- sleep_cfm_buf->size =
- cpu_to_le16(adapter->sleep_cfm->len);
- sleep_cfm_buf->result = 0;
- sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
- sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
- }
+ sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *)
+ adapter->sleep_cfm->data;
+ memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
+ sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
+ sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len);
+ sleep_cfm_buf->result = 0;
+ sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
+ sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
+
memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params));
memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period));
adapter->tx_lock_flag = false;
spin_lock_init(&adapter->rx_proc_lock);
skb_queue_head_init(&adapter->rx_data_q);
+ skb_queue_head_init(&adapter->tx_data_q);
for (i = 0; i < adapter->priv_num; ++i) {
INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
}
}
+ atomic_set(&adapter->tx_queued, 0);
+ while ((skb = skb_dequeue(&adapter->tx_data_q)))
+ mwifiex_write_data_complete(adapter, skb, 0, 0);
+
spin_lock_irqsave(&adapter->rx_proc_lock, flags);
while ((skb = skb_dequeue(&adapter->rx_data_q))) {
return 0;
}
+void mwifiex_queue_main_work(struct mwifiex_adapter *adapter)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ if (adapter->mwifiex_processing) {
+ adapter->more_task_flag = true;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ queue_work(adapter->workqueue, &adapter->main_work);
+ }
+}
+EXPORT_SYMBOL_GPL(mwifiex_queue_main_work);
+
+static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->rx_proc_lock, flags);
+ if (adapter->rx_processing) {
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
+ queue_work(adapter->rx_workqueue, &adapter->rx_work);
+ }
+}
+
static int mwifiex_process_rx(struct mwifiex_adapter *adapter)
{
unsigned long flags;
struct sk_buff *skb;
+ struct mwifiex_rxinfo *rx_info;
spin_lock_irqsave(&adapter->rx_proc_lock, flags);
if (adapter->rx_processing || adapter->rx_locked) {
if (adapter->if_ops.submit_rem_rx_urbs)
adapter->if_ops.submit_rem_rx_urbs(adapter);
adapter->delay_main_work = false;
- queue_work(adapter->workqueue, &adapter->main_work);
+ mwifiex_queue_main_work(adapter);
+ }
+ rx_info = MWIFIEX_SKB_RXCB(skb);
+ if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) {
+ if (adapter->if_ops.deaggr_pkt)
+ adapter->if_ops.deaggr_pkt(adapter, skb);
+ dev_kfree_skb_any(skb);
+ } else {
+ mwifiex_handle_rx_packet(adapter, skb);
}
- mwifiex_handle_rx_packet(adapter, skb);
}
spin_lock_irqsave(&adapter->rx_proc_lock, flags);
adapter->rx_processing = false;
spin_lock_irqsave(&adapter->main_proc_lock, flags);
/* Check if already processing */
- if (adapter->mwifiex_processing) {
+ if (adapter->mwifiex_processing || adapter->main_locked) {
adapter->more_task_flag = true;
spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
goto exit_main_proc;
if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
adapter->iface_type != MWIFIEX_USB) {
adapter->delay_main_work = true;
- if (!adapter->rx_processing)
- queue_work(adapter->rx_workqueue,
- &adapter->rx_work);
+ mwifiex_queue_rx_work(adapter);
break;
}
}
if (adapter->rx_work_enabled && adapter->data_received)
- queue_work(adapter->rx_workqueue, &adapter->rx_work);
+ mwifiex_queue_rx_work(adapter);
/* Need to wake up the card ? */
if ((adapter->ps_state == PS_STATE_SLEEP) &&
(adapter->pm_wakeup_card_req &&
!adapter->pm_wakeup_fw_try) &&
(is_command_pending(adapter) ||
+ !skb_queue_empty(&adapter->tx_data_q) ||
!mwifiex_wmm_lists_empty(adapter))) {
adapter->pm_wakeup_fw_try = true;
mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3));
if (IS_CARD_RX_RCVD(adapter)) {
adapter->data_received = false;
adapter->pm_wakeup_fw_try = false;
- del_timer_sync(&adapter->wakeup_timer);
+ del_timer(&adapter->wakeup_timer);
if (adapter->ps_state == PS_STATE_SLEEP)
adapter->ps_state = PS_STATE_AWAKE;
} else {
if ((!adapter->scan_chan_gap_enabled &&
adapter->scan_processing) || adapter->data_sent ||
- mwifiex_wmm_lists_empty(adapter)) {
+ (mwifiex_wmm_lists_empty(adapter) &&
+ skb_queue_empty(&adapter->tx_data_q))) {
if (adapter->cmd_sent || adapter->curr_cmd ||
(!is_command_pending(adapter)))
break;
}
}
+ if ((adapter->scan_chan_gap_enabled ||
+ !adapter->scan_processing) &&
+ !adapter->data_sent &&
+ !skb_queue_empty(&adapter->tx_data_q)) {
+ mwifiex_process_tx_queue(adapter);
+ if (adapter->hs_activated) {
+ adapter->is_hs_configured = false;
+ mwifiex_hs_activated_event
+ (mwifiex_get_priv
+ (adapter, MWIFIEX_BSS_ROLE_ANY),
+ false);
+ }
+ }
+
if ((adapter->scan_chan_gap_enabled ||
!adapter->scan_processing) &&
!adapter->data_sent && !mwifiex_wmm_lists_empty(adapter)) {
if (adapter->delay_null_pkt && !adapter->cmd_sent &&
!adapter->curr_cmd && !is_command_pending(adapter) &&
- mwifiex_wmm_lists_empty(adapter)) {
+ (mwifiex_wmm_lists_empty(adapter) &&
+ skb_queue_empty(&adapter->tx_data_q))) {
if (!mwifiex_send_null_packet
(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET |
atomic_inc(&priv->adapter->tx_pending);
mwifiex_wmm_add_buf_txqueue(priv, skb);
- queue_work(priv->adapter->workqueue, &priv->adapter->main_work);
+ mwifiex_queue_main_work(priv->adapter);
return 0;
}
INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue);
}
- if (adapter->if_ops.iface_work)
- INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work);
-
/* Register the device. Fill up the private data structure with relevant
information from the card. */
if (adapter->if_ops.register_dev(adapter)) {
#include <linux/ctype.h>
#include <linux/of.h>
#include <linux/idr.h>
+#include <linux/inetdevice.h>
#include "decl.h"
#include "ioctl.h"
#define MWIFIEX_MAX_AP 64
+#define MWIFIEX_MAX_PKTS_TXQ 16
+
#define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ)
#define MWIFIEX_TIMER_10S 10000
#define MWIFIEX_TYPE_CMD 1
#define MWIFIEX_TYPE_DATA 0
+#define MWIFIEX_TYPE_AGGR_DATA 10
#define MWIFIEX_TYPE_EVENT 3
#define MAX_BITMAP_RATES_SIZE 18
u8 amsdu;
};
+enum mwifiex_ba_status {
+ BA_SETUP_NONE = 0,
+ BA_SETUP_INPROGRESS,
+ BA_SETUP_COMPLETE
+};
+
struct mwifiex_ra_list_tbl {
struct list_head list;
struct sk_buff_head skb_head;
u16 max_amsdu;
u16 ba_pkt_count;
u8 ba_packet_thr;
+ enum mwifiex_ba_status ba_status;
+ u8 amsdu_in_ampdu;
u16 total_pkt_count;
bool tdls_link;
};
struct mwifiex_11h_intf_state state_11h;
};
-enum mwifiex_ba_status {
- BA_SETUP_NONE = 0,
- BA_SETUP_INPROGRESS,
- BA_SETUP_COMPLETE
-};
struct mwifiex_tx_ba_stream_tbl {
struct list_head list;
int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
void (*iface_work)(struct work_struct *work);
void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter);
+ void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *);
};
struct mwifiex_adapter {
bool rx_processing;
bool delay_main_work;
bool rx_locked;
+ bool main_locked;
struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM];
/* spin lock for init/shutdown */
spinlock_t mwifiex_lock;
u8 more_task_flag;
u16 tx_buf_size;
u16 curr_tx_buf_size;
+ bool sdio_rx_aggr_enable;
+ u16 sdio_rx_block_size;
u32 ioport;
enum MWIFIEX_HARDWARE_STATUS hw_status;
u16 number_of_antenna;
spinlock_t scan_pending_q_lock;
/* spin lock for RX processing routine */
spinlock_t rx_proc_lock;
+ struct sk_buff_head tx_data_q;
+ atomic_t tx_queued;
u32 scan_processing;
u16 region_code;
struct mwifiex_802_11d_domain_reg domain_reg;
bool ext_scan;
u8 fw_api_ver;
u8 key_api_major_ver, key_api_minor_ver;
- struct work_struct iface_work;
- unsigned long iface_work_flags;
struct memory_type_mapping *mem_type_mapping_tbl;
u8 num_mem_types;
u8 curr_mem_idx;
bool auto_tdls;
};
+void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter);
+
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
void mwifiex_set_trans_start(struct net_device *dev);
u8 rx_rate, u8 ht_info);
void mwifiex_dump_drv_info(struct mwifiex_adapter *adapter);
-void *mwifiex_alloc_rx_buf(int rx_len, gfp_t flags);
+void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags);
+void mwifiex_queue_main_work(struct mwifiex_adapter *adapter);
#ifdef CONFIG_DEBUG_FS
void mwifiex_debugfs_init(void);
if (!adapter || !adapter->priv_num)
return;
- cancel_work_sync(&adapter->iface_work);
-
if (user_rmmod) {
#ifdef CONFIG_PM_SLEEP
if (adapter->is_suspended)
for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) {
/* Allocate skb here so that firmware can DMA data from it */
- skb = mwifiex_alloc_rx_buf(MWIFIEX_RX_DATA_BUF_SIZE,
- GFP_KERNEL | GFP_DMA);
+ skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE,
+ GFP_KERNEL | GFP_DMA);
if (!skb) {
dev_err(adapter->dev,
"Unable to allocate skb for RX ring.\n");
}
}
- skb_tmp = mwifiex_alloc_rx_buf(MWIFIEX_RX_DATA_BUF_SIZE,
- GFP_KERNEL | GFP_DMA);
+ skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE,
+ GFP_KERNEL | GFP_DMA);
if (!skb_tmp) {
dev_err(adapter->dev,
"Unable to allocate skb.\n");
goto exit;
mwifiex_interrupt_status(adapter);
- queue_work(adapter->workqueue, &adapter->main_work);
+ mwifiex_queue_main_work(adapter);
exit:
return IRQ_HANDLED;
adapter->curr_mem_idx = 0;
}
+static unsigned long iface_work_flags;
+static struct mwifiex_adapter *save_adapter;
static void mwifiex_pcie_work(struct work_struct *work)
{
- struct mwifiex_adapter *adapter =
- container_of(work, struct mwifiex_adapter, iface_work);
-
if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP,
- &adapter->iface_work_flags))
- mwifiex_pcie_fw_dump_work(adapter);
+ &iface_work_flags))
+ mwifiex_pcie_fw_dump_work(save_adapter);
}
+static DECLARE_WORK(pcie_work, mwifiex_pcie_work);
/* This function dumps FW information */
static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter)
{
- if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags))
+ save_adapter = adapter;
+ if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags))
return;
- set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags);
+ set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags);
- schedule_work(&adapter->iface_work);
+ schedule_work(&pcie_work);
}
/*
.init_fw_port = mwifiex_pcie_init_fw_port,
.clean_pcie_ring = mwifiex_clean_pcie_ring_buf,
.fw_dump = mwifiex_pcie_fw_dump,
- .iface_work = mwifiex_pcie_work,
};
/*
/* Set the flag as user is removing this module. */
user_rmmod = 1;
+ cancel_work_sync(&pcie_work);
pci_unregister_driver(&mwifiex_pcie);
}
static u8 user_rmmod;
static struct mwifiex_if_ops sdio_ops;
+static unsigned long iface_work_flags;
static struct semaphore add_remove_card_sem;
if (!adapter || !adapter->priv_num)
return;
- cancel_work_sync(&adapter->iface_work);
-
if (user_rmmod) {
if (adapter->is_suspended)
mwifiex_sdio_resume(adapter->dev);
return ret;
}
+/*
+ * This function decode sdio aggreation pkt.
+ *
+ * Based on the the data block size and pkt_len,
+ * skb data will be decoded to few packets.
+ */
+static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb)
+{
+ u32 total_pkt_len, pkt_len;
+ struct sk_buff *skb_deaggr;
+ u32 pkt_type;
+ u16 blk_size;
+ u8 blk_num;
+ u8 *data;
+
+ data = skb->data;
+ total_pkt_len = skb->len;
+
+ while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) {
+ if (total_pkt_len < adapter->sdio_rx_block_size)
+ break;
+ blk_num = *(data + BLOCK_NUMBER_OFFSET);
+ blk_size = adapter->sdio_rx_block_size * blk_num;
+ if (blk_size > total_pkt_len) {
+ dev_err(adapter->dev, "%s: error in pkt,\t"
+ "blk_num=%d, blk_size=%d, total_pkt_len=%d\n",
+ __func__, blk_num, blk_size, total_pkt_len);
+ break;
+ }
+ pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET));
+ pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET +
+ 2));
+ if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) {
+ dev_err(adapter->dev, "%s: error in pkt,\t"
+ "pkt_len=%d, blk_size=%d\n",
+ __func__, pkt_len, blk_size);
+ break;
+ }
+ skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len,
+ GFP_KERNEL | GFP_DMA);
+ if (!skb_deaggr)
+ break;
+ skb_put(skb_deaggr, pkt_len);
+ memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len);
+ skb_pull(skb_deaggr, INTF_HEADER_LEN);
+
+ mwifiex_handle_rx_packet(adapter, skb_deaggr);
+ data += blk_size;
+ total_pkt_len -= blk_size;
+ }
+}
+
/*
* This function decodes a received packet.
*
u8 *cmd_buf;
__le16 *curr_ptr = (__le16 *)skb->data;
u16 pkt_len = le16_to_cpu(*curr_ptr);
+ struct mwifiex_rxinfo *rx_info;
- skb_trim(skb, pkt_len);
- skb_pull(skb, INTF_HEADER_LEN);
+ if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) {
+ skb_trim(skb, pkt_len);
+ skb_pull(skb, INTF_HEADER_LEN);
+ }
switch (upld_typ) {
+ case MWIFIEX_TYPE_AGGR_DATA:
+ dev_dbg(adapter->dev, "info: --- Rx: Aggr Data packet ---\n");
+ rx_info = MWIFIEX_SKB_RXCB(skb);
+ rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA;
+ if (adapter->rx_work_enabled) {
+ skb_queue_tail(&adapter->rx_data_q, skb);
+ atomic_inc(&adapter->rx_pending);
+ adapter->data_received = true;
+ } else {
+ mwifiex_deaggr_sdio_pkt(adapter, skb);
+ dev_kfree_skb_any(skb);
+ }
+ break;
+
case MWIFIEX_TYPE_DATA:
dev_dbg(adapter->dev, "info: --- Rx: Data packet ---\n");
if (adapter->rx_work_enabled) {
* provided there is space left, processed and finally uploaded.
*/
static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
- struct sk_buff *skb, u8 port)
+ u16 rx_len, u8 port)
{
struct sdio_mmc_card *card = adapter->card;
s32 f_do_rx_aggr = 0;
s32 f_do_rx_cur = 0;
s32 f_aggr_cur = 0;
+ s32 f_post_aggr_cur = 0;
struct sk_buff *skb_deaggr;
- u32 pind;
- u32 pkt_len, pkt_type, mport;
+ struct sk_buff *skb = NULL;
+ u32 pkt_len, pkt_type, mport, pind;
u8 *curr_ptr;
- u32 rx_len = skb->len;
if ((card->has_control_mask) && (port == CTRL_PORT)) {
/* Read the command Resp without aggr */
dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__);
if (MP_RX_AGGR_IN_PROGRESS(card)) {
- if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len)) {
+ if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) {
f_aggr_cur = 1;
} else {
/* No room in Aggr buf, do rx aggr now */
f_do_rx_aggr = 1;
- f_do_rx_cur = 1;
+ f_post_aggr_cur = 1;
}
} else {
/* Rx aggr not in progress */
if (MP_RX_AGGR_IN_PROGRESS(card)) {
f_do_rx_aggr = 1;
- if (MP_RX_AGGR_BUF_HAS_ROOM(card, skb->len))
+ if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len))
f_aggr_cur = 1;
else
/* No room in Aggr buf, do rx aggr now */
if (f_aggr_cur) {
dev_dbg(adapter->dev, "info: current packet aggregation\n");
/* Curr pkt can be aggregated */
- mp_rx_aggr_setup(card, skb, port);
+ mp_rx_aggr_setup(card, rx_len, port);
if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) ||
mp_rx_aggr_port_limit_reached(card)) {
curr_ptr = card->mpa_rx.buf;
for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) {
+ u32 *len_arr = card->mpa_rx.len_arr;
/* get curr PKT len & type */
pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]);
pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]);
/* copy pkt to deaggr buf */
- skb_deaggr = card->mpa_rx.skb_arr[pind];
+ skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind],
+ GFP_KERNEL |
+ GFP_DMA);
+ if (!skb_deaggr) {
+ dev_err(adapter->dev, "skb allocation failure drop pkt len=%d type=%d\n",
+ pkt_len, pkt_type);
+ curr_ptr += len_arr[pind];
+ continue;
+ }
- if ((pkt_type == MWIFIEX_TYPE_DATA) && (pkt_len <=
- card->mpa_rx.len_arr[pind])) {
+ skb_put(skb_deaggr, len_arr[pind]);
+
+ if ((pkt_type == MWIFIEX_TYPE_DATA ||
+ (pkt_type == MWIFIEX_TYPE_AGGR_DATA &&
+ adapter->sdio_rx_aggr_enable)) &&
+ (pkt_len <= len_arr[pind])) {
memcpy(skb_deaggr->data, curr_ptr, pkt_len);
mwifiex_decode_rx_packet(adapter, skb_deaggr,
pkt_type);
} else {
- dev_err(adapter->dev, "wrong aggr pkt:"
- " type=%d len=%d max_len=%d\n",
+ dev_err(adapter->dev, " drop wrong aggr pkt:\t"
+ "sdio_single_port_rx_aggr=%d\t"
+ "type=%d len=%d max_len=%d\n",
+ adapter->sdio_rx_aggr_enable,
pkt_type, pkt_len,
- card->mpa_rx.len_arr[pind]);
+ len_arr[pind]);
dev_kfree_skb_any(skb_deaggr);
}
- curr_ptr += card->mpa_rx.len_arr[pind];
+ curr_ptr += len_arr[pind];
}
MP_RX_AGGR_BUF_RESET(card);
}
dev_dbg(adapter->dev, "info: RX: port: %d, rx_len: %d\n",
port, rx_len);
+ skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA);
+ if (!skb) {
+ dev_err(adapter->dev, "single skb allocated fail,\t"
+ "drop pkt port=%d len=%d\n", port, rx_len);
+ if (mwifiex_sdio_card_to_host(adapter, &pkt_type,
+ card->mpa_rx.buf, rx_len,
+ adapter->ioport + port))
+ goto error;
+ return 0;
+ }
+
+ skb_put(skb, rx_len);
+
if (mwifiex_sdio_card_to_host(adapter, &pkt_type,
skb->data, skb->len,
adapter->ioport + port))
goto error;
+ if (!adapter->sdio_rx_aggr_enable &&
+ pkt_type == MWIFIEX_TYPE_AGGR_DATA) {
+ dev_err(adapter->dev, "drop wrong pkt type %d\t"
+ "current SDIO RX Aggr not enabled\n",
+ pkt_type);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
mwifiex_decode_rx_packet(adapter, skb, pkt_type);
}
+ if (f_post_aggr_cur) {
+ dev_dbg(adapter->dev, "info: current packet aggregation\n");
+ /* Curr pkt can be aggregated */
+ mp_rx_aggr_setup(card, rx_len, port);
+ }
return 0;
-
error:
- if (MP_RX_AGGR_IN_PROGRESS(card)) {
- /* Multiport-aggregation transfer failed - cleanup */
- for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) {
- /* copy pkt to deaggr buf */
- skb_deaggr = card->mpa_rx.skb_arr[pind];
- dev_kfree_skb_any(skb_deaggr);
- }
+ if (MP_RX_AGGR_IN_PROGRESS(card))
MP_RX_AGGR_BUF_RESET(card);
- }
- if (f_do_rx_cur)
+ if (f_do_rx_cur && skb)
/* Single transfer pending. Free curr buff also */
dev_kfree_skb_any(skb);
MWIFIEX_RX_DATA_BUF_SIZE)
return -1;
rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+ dev_dbg(adapter->dev, "info: rx_len = %d\n", rx_len);
- skb = mwifiex_alloc_rx_buf(rx_len, GFP_KERNEL | GFP_DMA);
+ skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA);
if (!skb)
return -1;
1) / MWIFIEX_SDIO_BLOCK_SIZE;
if (rx_len <= INTF_HEADER_LEN ||
(rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
- MWIFIEX_RX_DATA_BUF_SIZE) {
+ card->mpa_rx.buf_size) {
dev_err(adapter->dev, "invalid rx_len=%d\n",
rx_len);
return -1;
}
- rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
-
- skb = mwifiex_alloc_rx_buf(rx_len,
- GFP_KERNEL | GFP_DMA);
-
- if (!skb) {
- dev_err(adapter->dev, "%s: failed to alloc skb",
- __func__);
- return -1;
- }
- skb_put(skb, rx_len);
-
- dev_dbg(adapter->dev, "info: rx_len = %d skb->len = %d\n",
- rx_len, skb->len);
+ rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+ dev_dbg(adapter->dev, "info: rx_len = %d\n", rx_len);
- if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb,
+ if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len,
port)) {
dev_err(adapter->dev, "card_to_host_mpa failed:"
" int status=%#x\n", sdio_ireg);
u32 mpa_tx_buf_size, u32 mpa_rx_buf_size)
{
struct sdio_mmc_card *card = adapter->card;
+ u32 rx_buf_size;
int ret = 0;
card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL);
card->mpa_tx.buf_size = mpa_tx_buf_size;
- card->mpa_rx.buf = kzalloc(mpa_rx_buf_size, GFP_KERNEL);
+ rx_buf_size = max_t(u32, mpa_rx_buf_size,
+ (u32)SDIO_MAX_AGGR_BUF_SIZE);
+ card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL);
if (!card->mpa_rx.buf) {
ret = -1;
goto error;
}
- card->mpa_rx.buf_size = mpa_rx_buf_size;
+ card->mpa_rx.buf_size = rx_buf_size;
error:
if (ret) {
port, card->mp_data_port_mask);
}
+static struct mwifiex_adapter *save_adapter;
static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
}
/* This function dump firmware memory to file */
-static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
+static void mwifiex_sdio_fw_dump_work(struct mwifiex_adapter *adapter)
{
- struct mwifiex_adapter *adapter =
- container_of(work, struct mwifiex_adapter, iface_work);
struct sdio_mmc_card *card = adapter->card;
int ret = 0;
unsigned int reg, reg_start, reg_end;
static void mwifiex_sdio_work(struct work_struct *work)
{
- struct mwifiex_adapter *adapter =
- container_of(work, struct mwifiex_adapter, iface_work);
-
- if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET,
- &adapter->iface_work_flags))
- mwifiex_sdio_card_reset_work(adapter);
if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP,
- &adapter->iface_work_flags))
- mwifiex_sdio_fw_dump_work(work);
+ &iface_work_flags))
+ mwifiex_sdio_fw_dump_work(save_adapter);
+ if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET,
+ &iface_work_flags))
+ mwifiex_sdio_card_reset_work(save_adapter);
}
+static DECLARE_WORK(sdio_work, mwifiex_sdio_work);
/* This function resets the card */
static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter)
{
- if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags))
+ save_adapter = adapter;
+ if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags))
return;
- set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags);
+ set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags);
- schedule_work(&adapter->iface_work);
+ schedule_work(&sdio_work);
}
/* This function dumps FW information */
static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter)
{
- if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags))
+ save_adapter = adapter;
+ if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags))
return;
- set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags);
- schedule_work(&adapter->iface_work);
+ set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &iface_work_flags);
+ schedule_work(&sdio_work);
}
/* Function to dump SDIO function registers and SDIO scratch registers in case
.cmdrsp_complete = mwifiex_sdio_cmdrsp_complete,
.event_complete = mwifiex_sdio_event_complete,
.card_reset = mwifiex_sdio_card_reset,
- .iface_work = mwifiex_sdio_work,
.fw_dump = mwifiex_sdio_fw_dump,
.reg_dump = mwifiex_sdio_reg_dump,
+ .deaggr_pkt = mwifiex_deaggr_sdio_pkt,
};
/*
/* Set the flag as user is removing this module. */
user_rmmod = 1;
+ cancel_work_sync(&sdio_work);
sdio_unregister_driver(&mwifiex_sdio);
}
#define MWIFIEX_MP_AGGR_BUF_SIZE_16K (16384)
#define MWIFIEX_MP_AGGR_BUF_SIZE_32K (32768)
+/* we leave one block of 256 bytes for DMA alignment*/
+#define MWIFIEX_MP_AGGR_BUF_SIZE_MAX (65280)
/* Misc. Config Register : Auto Re-enable interrupts */
#define AUTO_RE_ENABLE_INT BIT(4)
.max_ports = 32,
.mp_agg_pkt_limit = 16,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
- .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
- .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
+ .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
+ .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
.supports_sdio_new_mode = true,
.has_control_mask = false,
.can_dump_fw = true,
/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card,
- struct sk_buff *skb, u8 port)
+ u16 rx_len, u8 port)
{
- card->mpa_rx.buf_len += skb->len;
+ card->mpa_rx.buf_len += rx_len;
if (!card->mpa_rx.pkt_cnt)
card->mpa_rx.start_port = port;
else
card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1);
}
- card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb;
- card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len;
+ card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = NULL;
+ card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len;
card->mpa_rx.pkt_cnt++;
}
#endif /* _MWIFIEX_SDIO_H */
struct mwifiex_ds_mef_cfg *mef)
{
struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg;
+ struct mwifiex_fw_mef_entry *mef_entry = NULL;
u8 *pos = (u8 *)mef_cfg;
+ u16 i;
cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG);
mef_cfg->criteria = cpu_to_le32(mef->criteria);
mef_cfg->num_entries = cpu_to_le16(mef->num_entries);
pos += sizeof(*mef_cfg);
- mef_cfg->mef_entry->mode = mef->mef_entry->mode;
- mef_cfg->mef_entry->action = mef->mef_entry->action;
- pos += sizeof(*(mef_cfg->mef_entry));
- if (mwifiex_cmd_append_rpn_expression(priv, mef->mef_entry, &pos))
- return -1;
+ for (i = 0; i < mef->num_entries; i++) {
+ mef_entry = (struct mwifiex_fw_mef_entry *)pos;
+ mef_entry->mode = mef->mef_entry[i].mode;
+ mef_entry->action = mef->mef_entry[i].action;
+ pos += sizeof(*mef_cfg->mef_entry);
+
+ if (mwifiex_cmd_append_rpn_expression(priv,
+ &mef->mef_entry[i], &pos))
+ return -1;
- mef_cfg->mef_entry->exprsize =
- cpu_to_le16(pos - mef_cfg->mef_entry->expr);
+ mef_entry->exprsize =
+ cpu_to_le16(pos - mef_entry->expr);
+ }
cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN);
return 0;
return 0;
}
+
+/* This function prepares command of sdio rx aggr info. */
+static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd,
+ u16 cmd_action, void *data_buf)
+{
+ struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =
+ &cmd->params.sdio_rx_aggr_cfg;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG);
+ cmd->size =
+ cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) +
+ S_DS_GEN);
+ cfg->action = cmd_action;
+ if (cmd_action == HostCmd_ACT_GEN_SET)
+ cfg->enable = *(u8 *)data_buf;
+
+ return 0;
+}
+
/*
* This function prepares the commands before sending them to the firmware.
*
ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr,
data_buf);
break;
+ case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
+ ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action,
+ data_buf);
+ break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd- %#x\n", cmd_no);
struct mwifiex_ds_auto_ds auto_ds;
enum state_11d_t state_11d;
struct mwifiex_ds_11n_tx_cfg tx_cfg;
+ u8 sdio_sp_rx_aggr_enable;
if (first_sta) {
if (priv->adapter->iface_type == MWIFIEX_PCIE) {
if (ret)
return -1;
+ /** Set SDIO Single Port RX Aggr Info */
+ if (priv->adapter->iface_type == MWIFIEX_SDIO &&
+ ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info)) {
+ sdio_sp_rx_aggr_enable = true;
+ ret = mwifiex_send_cmd(priv,
+ HostCmd_CMD_SDIO_SP_RX_AGGR_CFG,
+ HostCmd_ACT_GEN_SET, 0,
+ &sdio_sp_rx_aggr_enable,
+ true);
+ if (ret) {
+ dev_err(priv->adapter->dev,
+ "error while enabling SP aggregation..disable it");
+ adapter->sdio_rx_aggr_enable = false;
+ }
+ }
+
/* Reconfigure tx buf size */
ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
HostCmd_ACT_GEN_SET, 0,
case HostCmd_CMD_MAC_CONTROL:
break;
+ case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
+ dev_err(priv->adapter->dev, "SDIO RX single-port aggregation Not support\n");
+ break;
+
default:
break;
}
return 0;
}
+/** This Function handles the command response of sdio rx aggr */
+static int mwifiex_ret_sdio_rx_aggr_cfg(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =
+ &resp->params.sdio_rx_aggr_cfg;
+
+ adapter->sdio_rx_aggr_enable = cfg->enable;
+ adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size);
+
+ return 0;
+}
+
/*
* This function handles the command responses.
*
break;
case HostCmd_CMD_CHAN_REPORT_REQUEST:
break;
+ case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
+ ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp);
+ break;
default:
dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
resp->command);
adapter->ps_state = PS_STATE_AWAKE;
adapter->pm_wakeup_card_req = false;
adapter->pm_wakeup_fw_try = false;
- del_timer_sync(&adapter->wakeup_timer);
+ del_timer(&adapter->wakeup_timer);
break;
}
if (!mwifiex_send_null_packet
adapter->ps_state = PS_STATE_AWAKE;
adapter->pm_wakeup_card_req = false;
adapter->pm_wakeup_fw_try = false;
- del_timer_sync(&adapter->wakeup_timer);
+ del_timer(&adapter->wakeup_timer);
break;
else
head_ptr = mwifiex_process_sta_txpd(priv, skb);
+ if ((adapter->data_sent || adapter->tx_lock_flag) && head_ptr) {
+ skb_queue_tail(&adapter->tx_data_q, skb);
+ atomic_inc(&adapter->tx_queued);
+ return 0;
+ }
+
if (head_ptr) {
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
local_tx_pd = (struct txpd *)(head_ptr + hroom);
return ret;
}
+static int mwifiex_host_to_card(struct mwifiex_adapter *adapter,
+ struct sk_buff *skb,
+ struct mwifiex_tx_param *tx_param)
+{
+ struct txpd *local_tx_pd = NULL;
+ u8 *head_ptr = skb->data;
+ int ret = 0;
+ struct mwifiex_private *priv;
+ struct mwifiex_txinfo *tx_info;
+
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num,
+ tx_info->bss_type);
+ if (!priv) {
+ dev_err(adapter->dev, "data: priv not found. Drop TX packet\n");
+ adapter->dbg.num_tx_host_to_card_failure++;
+ mwifiex_write_data_complete(adapter, skb, 0, 0);
+ return ret;
+ }
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
+ if (adapter->iface_type == MWIFIEX_USB)
+ local_tx_pd = (struct txpd *)head_ptr;
+ else
+ local_tx_pd = (struct txpd *) (head_ptr +
+ INTF_HEADER_LEN);
+ }
+
+ if (adapter->iface_type == MWIFIEX_USB) {
+ adapter->data_sent = true;
+ ret = adapter->if_ops.host_to_card(adapter,
+ MWIFIEX_USB_EP_DATA,
+ skb, NULL);
+ } else {
+ ret = adapter->if_ops.host_to_card(adapter,
+ MWIFIEX_TYPE_DATA,
+ skb, tx_param);
+ }
+ switch (ret) {
+ case -ENOSR:
+ dev_err(adapter->dev, "data: -ENOSR is returned\n");
+ break;
+ case -EBUSY:
+ if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+ (adapter->pps_uapsd_mode) &&
+ (adapter->tx_lock_flag)) {
+ priv->adapter->tx_lock_flag = false;
+ if (local_tx_pd)
+ local_tx_pd->flags = 0;
+ }
+ skb_queue_head(&adapter->tx_data_q, skb);
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT)
+ atomic_add(tx_info->aggr_num, &adapter->tx_queued);
+ else
+ atomic_inc(&adapter->tx_queued);
+ dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
+ break;
+ case -1:
+ if (adapter->iface_type != MWIFIEX_PCIE)
+ adapter->data_sent = false;
+ dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n",
+ ret);
+ adapter->dbg.num_tx_host_to_card_failure++;
+ mwifiex_write_data_complete(adapter, skb, 0, ret);
+ break;
+ case -EINPROGRESS:
+ if (adapter->iface_type != MWIFIEX_PCIE)
+ adapter->data_sent = false;
+ break;
+ case 0:
+ mwifiex_write_data_complete(adapter, skb, 0, ret);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int
+mwifiex_dequeue_tx_queue(struct mwifiex_adapter *adapter)
+{
+ struct sk_buff *skb, *skb_next;
+ struct mwifiex_txinfo *tx_info;
+ struct mwifiex_tx_param tx_param;
+
+ skb = skb_dequeue(&adapter->tx_data_q);
+ if (!skb)
+ return -1;
+
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT)
+ atomic_sub(tx_info->aggr_num, &adapter->tx_queued);
+ else
+ atomic_dec(&adapter->tx_queued);
+
+ if (!skb_queue_empty(&adapter->tx_data_q))
+ skb_next = skb_peek(&adapter->tx_data_q);
+ else
+ skb_next = NULL;
+ tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0);
+ if (!tx_param.next_pkt_len) {
+ if (!mwifiex_wmm_lists_empty(adapter))
+ tx_param.next_pkt_len = 1;
+ }
+ return mwifiex_host_to_card(adapter, skb, &tx_param);
+}
+
+void
+mwifiex_process_tx_queue(struct mwifiex_adapter *adapter)
+{
+ do {
+ if (adapter->data_sent || adapter->tx_lock_flag)
+ break;
+ if (mwifiex_dequeue_tx_queue(adapter))
+ break;
+ } while (!skb_queue_empty(&adapter->tx_data_q));
+}
+
/*
* Packet send completion callback handler.
*
priv->stats.tx_errors++;
}
- if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT)
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) {
atomic_dec_return(&adapter->pending_bridged_pkts);
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT)
+ goto done;
+ }
if (aggr)
/* For skb_aggr, do not wake up tx queue */
dev_dbg(adapter->dev, "info: recv_length=%d, status=%d\n",
recv_length, status);
if (status == -EINPROGRESS) {
- queue_work(adapter->workqueue, &adapter->main_work);
+ mwifiex_queue_main_work(adapter);
/* urb for data_ep is re-submitted now;
* urb for cmd_ep will be re-submitted in callback
urb->status ? -1 : 0);
}
- queue_work(adapter->workqueue, &adapter->main_work);
+ mwifiex_queue_main_work(adapter);
return;
}
{
/* Simulation of HS_AWAKE event */
adapter->pm_wakeup_fw_try = false;
- del_timer_sync(&adapter->wakeup_timer);
+ del_timer(&adapter->wakeup_timer);
adapter->pm_wakeup_card_req = false;
adapter->ps_state = PS_STATE_AWAKE;
atomic_set(&phist_data->sig_str[ix], 0);
}
-void *mwifiex_alloc_rx_buf(int rx_len, gfp_t flags)
+void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags)
{
struct sk_buff *skb;
int buf_len, pad;
return skb;
}
-EXPORT_SYMBOL_GPL(mwifiex_alloc_rx_buf);
+EXPORT_SYMBOL_GPL(mwifiex_alloc_dma_align_buf);
ra_list->is_11n_enabled = 0;
ra_list->tdls_link = false;
+ ra_list->ba_status = BA_SETUP_NONE;
+ ra_list->amsdu_in_ampdu = false;
if (!mwifiex_queuing_ra_based(priv)) {
if (mwifiex_get_tdls_link_status(priv, ra) ==
TDLS_SETUP_COMPLETE) {
* This function retrieves a particular RA list node, matching with the
* given TID and RA address.
*/
-static struct mwifiex_ra_list_tbl *
+struct mwifiex_ra_list_tbl *
mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
const u8 *ra_addr)
{
struct mwifiex_ra_list_tbl *ptr;
struct mwifiex_tid_tbl *tid_ptr;
atomic_t *hqp;
- unsigned long flags_bss, flags_ra;
+ unsigned long flags_ra;
int i, j;
/* check the BSS with highest priority first */
for (j = adapter->priv_num - 1; j >= 0; --j) {
- spin_lock_irqsave(&adapter->bss_prio_tbl[j].bss_prio_lock,
- flags_bss);
-
/* iterate over BSS with the equal priority */
list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur,
&adapter->bss_prio_tbl[j].bss_prio_head,
}
}
- spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
- flags_bss);
}
return NULL;
found:
- /* holds bss_prio_lock / ra_list_spinlock */
+ /* holds ra_list_spinlock */
if (atomic_read(hqp) > i)
atomic_set(hqp, i);
spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra);
- spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
- flags_bss);
*priv = priv_tmp;
*tid = tos_to_tid[i];
skb = skb_dequeue(&ptr->skb_head);
+ if (adapter->data_sent || adapter->tx_lock_flag) {
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+ ra_list_flags);
+ skb_queue_tail(&adapter->tx_data_q, skb);
+ atomic_inc(&adapter->tx_queued);
+ return;
+ }
+
if (!skb_queue_empty(&ptr->skb_head))
skb_next = skb_peek(&ptr->skb_head);
else
}
if (!ptr->is_11n_enabled ||
- mwifiex_is_ba_stream_setup(priv, ptr, tid) ||
- priv->wps.session_enable) {
+ ptr->ba_status ||
+ priv->wps.session_enable) {
if (ptr->is_11n_enabled &&
- mwifiex_is_ba_stream_setup(priv, ptr, tid) &&
- mwifiex_is_amsdu_in_ampdu_allowed(priv, ptr, tid) &&
- mwifiex_is_amsdu_allowed(priv, tid) &&
- mwifiex_is_11n_aggragation_possible(priv, ptr,
+ ptr->ba_status &&
+ ptr->amsdu_in_ampdu &&
+ mwifiex_is_amsdu_allowed(priv, tid) &&
+ mwifiex_is_11n_aggragation_possible(priv, ptr,
adapter->tx_buf_size))
mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags);
/* ra_list_spinlock has been freed in
mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter)
{
do {
- /* Check if busy */
- if (adapter->data_sent || adapter->tx_lock_flag)
- break;
-
if (mwifiex_dequeue_tx_packet(adapter))
break;
+ if (adapter->iface_type != MWIFIEX_SDIO) {
+ if (adapter->data_sent ||
+ adapter->tx_lock_flag)
+ break;
+ } else {
+ if (atomic_read(&adapter->tx_queued) >=
+ MWIFIEX_MAX_PKTS_TXQ)
+ break;
+ }
} while (!mwifiex_wmm_lists_empty(adapter));
}
const u8 *ra_addr);
u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid);
+struct mwifiex_ra_list_tbl *mwifiex_wmm_get_ralist_node(struct mwifiex_private
+ *priv, u8 tid, const u8 *ra_addr);
#endif /* !_MWIFIEX_WMM_H_ */
{
__le32 *reg;
u32 fw_mode;
+ int ret;
reg = kmalloc(sizeof(*reg), GFP_KERNEL);
if (reg == NULL)
* magic value USB_MODE_AUTORUN (0x11) to the device, thus the
* returned value would be invalid.
*/
- rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE,
- USB_VENDOR_REQUEST_IN, 0, USB_MODE_AUTORUN,
- reg, sizeof(*reg), REGISTER_TIMEOUT_FIRMWARE);
+ ret = rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE,
+ USB_VENDOR_REQUEST_IN, 0,
+ USB_MODE_AUTORUN, reg, sizeof(*reg),
+ REGISTER_TIMEOUT_FIRMWARE);
fw_mode = le32_to_cpu(*reg);
kfree(reg);
+ if (ret < 0)
+ return ret;
if ((fw_mode & 0x00000003) == 2)
return 1;
if (retval) {
rt2x00_info(rt2x00dev,
"Firmware loading not required - NIC in AutoRun mode\n");
+ __clear_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags);
} else {
rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE,
data + offset, length);
static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev)
{
rt2800_disable_radio(rt2x00dev);
- rt2x00usb_disable_radio(rt2x00dev);
}
static int rt2800usb_set_state(struct rt2x00_dev *rt2x00dev,
{ USB_DEVICE(0x07d1, 0x3c17) },
{ USB_DEVICE(0x2001, 0x3317) },
{ USB_DEVICE(0x2001, 0x3c1b) },
+ { USB_DEVICE(0x2001, 0x3c25) },
/* Draytek */
{ USB_DEVICE(0x07fa, 0x7712) },
/* DVICO */
const unsigned int offset,
u32 *value)
{
- __le32 reg;
+ __le32 reg = 0;
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
USB_VENDOR_REQUEST_IN, offset,
®, sizeof(reg));
const unsigned int offset,
u32 *value)
{
- __le32 reg;
+ __le32 reg = 0;
rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ,
USB_VENDOR_REQUEST_IN, offset,
®, sizeof(reg), REGISTER_TIMEOUT);
}
/*should call before software enc*/
-u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx)
+u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
+ bool is_enc)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
break;
}
- offset = mac_hdr_len + SNAP_SIZE + encrypt_header_len;
+ offset = mac_hdr_len + SNAP_SIZE;
+ if (is_enc)
+ offset += encrypt_header_len;
ether_type = be16_to_cpup((__be16 *)(skb->data + offset));
if (ETH_P_IP == ether_type) {
int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht,
bool isvht, u8 desc_rate);
bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb);
-u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx);
+u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx,
+ bool is_enc);
void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb);
-void rtl_watch_dog_timer_callback(unsigned long data);
int rtl_tx_agg_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, u16 tid, u16 *ssn);
int rtl_tx_agg_stop(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
unicast = true;
rtlpriv->stats.rxbytesunicast += skb->len;
}
- rtl_is_special_data(hw, skb, false);
+ rtl_is_special_data(hw, skb, false, true);
if (ieee80211_is_data(fc)) {
rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX);
/*This is for new trx flow*/
struct rtl_tx_buffer_desc *pbuffer_desc = NULL;
u8 temp_one = 1;
+ u8 *entry;
memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc));
ring = &rtlpci->tx_ring[BEACON_QUEUE];
pskb = __skb_dequeue(&ring->queue);
- if (pskb)
+ if (rtlpriv->use_new_trx_flow)
+ entry = (u8 *)(&ring->buffer_desc[ring->idx]);
+ else
+ entry = (u8 *)(&ring->desc[ring->idx]);
+ if (pskb) {
+ pci_unmap_single(rtlpci->pdev,
+ rtlpriv->cfg->ops->get_desc(
+ (u8 *)entry, true, HW_DESC_TXBUFF_ADDR),
+ pskb->len, PCI_DMA_TODEVICE);
kfree_skb(pskb);
+ }
/*NB: the beacon data buffer must be 32-bit aligned. */
pskb = ieee80211_beacon_get(hw, mac->vif);
wireless_mode = sta_entry->wireless_mode;
}
- if (rtl_is_special_data(rtlpriv->mac80211.hw, skb, true) || not_data) {
+ if (rtl_is_special_data(rtlpriv->mac80211.hw, skb, true, false) ||
+ not_data) {
return 0;
} else {
if (rtlhal->current_bandtype == BAND_ON_2_4G) {
if (!priv_sta || !ieee80211_is_data(fc))
return;
- if (rtl_is_special_data(mac->hw, skb, true))
+ if (rtl_is_special_data(mac->hw, skb, true, true))
return;
if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) ||
#include "../cam.h"
#include "../ps.h"
#include "../pci.h"
+#include "../pwrseqcmd.h"
#include "reg.h"
#include "def.h"
#include "phy.h"
rtl_write_word(rtlpriv, REG_CR, 0x2ff);
rtl_write_byte(rtlpriv, REG_CR+1, 0x06);
- rtl_write_byte(rtlpriv, REG_CR+2, 0x00);
+ rtl_write_byte(rtlpriv, MSR, 0x00);
if (!rtlhal->mac_func_enable) {
if (_rtl88ee_llt_table_init(hw) == false) {
mode);
}
- rtl_write_byte(rtlpriv, (MSR), bt_msr | mode);
+ rtl_write_byte(rtlpriv, MSR, bt_msr | mode);
rtlpriv->cfg->ops->led_control(hw, ledaction);
if (mode == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
"Network type %d not supported!\n", type);
goto error_out;
}
- rtl_write_byte(rtlpriv, (MSR), bt_msr);
+ rtl_write_byte(rtlpriv, MSR, bt_msr);
rtlpriv->cfg->ops->led_control(hw, ledaction);
if ((bt_msr & MSR_MASK) == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
rtl_write_word(rtlpriv, REG_BCNTCFG, 0x66FF);
}
-static void _beacon_function_enable(struct ieee80211_hw *hw, bool Enable,
- bool Linked)
+static void _beacon_function_enable(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x50);
rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x50);
}
- _beacon_function_enable(hw, true, true);
+ _beacon_function_enable(hw);
}
void rtl92cu_set_beacon_interval(struct ieee80211_hw *hw)
"Network type %d not supported!\n", type);
return -EOPNOTSUPP;
}
- rtl_write_byte(rtlpriv, (REG_CR + 2), value);
+ rtl_write_byte(rtlpriv, MSR, value);
return 0;
}
{RTL_USB_DEVICE(0x07b8, 0x8188, rtl92cu_hal_cfg)}, /*Abocom - Abocom*/
{RTL_USB_DEVICE(0x07b8, 0x8189, rtl92cu_hal_cfg)}, /*Funai - Abocom*/
{RTL_USB_DEVICE(0x0846, 0x9041, rtl92cu_hal_cfg)}, /*NetGear WNA1000M*/
+ {RTL_USB_DEVICE(0x0b05, 0x17ba, rtl92cu_hal_cfg)}, /*ASUS-Edimax*/
{RTL_USB_DEVICE(0x0bda, 0x5088, rtl92cu_hal_cfg)}, /*Thinkware-CC&C*/
{RTL_USB_DEVICE(0x0df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/
{RTL_USB_DEVICE(0x0df6, 0x005c, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/
{RTL_USB_DEVICE(0x2001, 0x3307, rtl92cu_hal_cfg)}, /*D-Link-Cameo*/
{RTL_USB_DEVICE(0x2001, 0x3309, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
{RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/
+ {RTL_USB_DEVICE(0x2001, 0x330d, rtl92cu_hal_cfg)}, /*D-Link DWA-131 */
{RTL_USB_DEVICE(0x2019, 0xab2b, rtl92cu_hal_cfg)}, /*Planex -Abocom*/
{RTL_USB_DEVICE(0x20f4, 0x624d, rtl92cu_hal_cfg)}, /*TRENDNet*/
{RTL_USB_DEVICE(0x2357, 0x0100, rtl92cu_hal_cfg)}, /*TP-Link WN8200ND*/
break;
}
- rtl_write_byte(rtlpriv, REG_CR + 2, bt_msr);
+ rtl_write_byte(rtlpriv, MSR, bt_msr);
rtlpriv->cfg->ops->led_control(hw, ledaction);
if ((bt_msr & MSR_MASK) == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
mode);
}
- rtl_write_byte(rtlpriv, (MSR), bt_msr | mode);
+ rtl_write_byte(rtlpriv, MSR, bt_msr | mode);
rtlpriv->cfg->ops->led_control(hw, ledaction);
if (mode == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
if (type != NL80211_IFTYPE_AP &&
rtlpriv->mac80211.link_state < MAC80211_LINKED)
bt_msr = rtl_read_byte(rtlpriv, MSR) & ~MSR_LINK_MASK;
- rtl_write_byte(rtlpriv, (MSR), bt_msr);
+ rtl_write_byte(rtlpriv, MSR, bt_msr);
temp = rtl_read_dword(rtlpriv, TCR);
rtl_write_dword(rtlpriv, TCR, temp & (~BIT(8)));
mode);
}
- rtl_write_byte(rtlpriv, (MSR), bt_msr | mode);
+ rtl_write_byte(rtlpriv, MSR, bt_msr | mode);
rtlpriv->cfg->ops->led_control(hw, ledaction);
if (mode == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
mode);
}
- rtl_write_byte(rtlpriv, (MSR), bt_msr | mode);
+ rtl_write_byte(rtlpriv, MSR, bt_msr | mode);
rtlpriv->cfg->ops->led_control(hw, ledaction);
if (mode == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
*((u16 *)(val+4)) = rtl_read_word(rtlpriv, REG_BSSID+4);
break;
case HW_VAR_MEDIA_STATUS:
- val[0] = rtl_read_byte(rtlpriv, REG_CR+2) & 0x3;
+ val[0] = rtl_read_byte(rtlpriv, MSR) & 0x3;
break;
case HW_VAR_SLOT_TIME:
*((u8 *)(val)) = mac->slot_time;
return 1;
}
- rtl_write_byte(rtlpriv, (MSR), bt_msr);
+ rtl_write_byte(rtlpriv, MSR, bt_msr);
rtlpriv->cfg->ops->led_control(hw, ledaction);
if ((bt_msr & 0xfc) == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
return ret_val;
}
+static u8 _rtl8821ae_evm_dbm_jaguar(char value)
+{
+ char ret_val = value;
+
+ /* -33dB~0dB to 33dB ~ 0dB*/
+ if (ret_val == -128)
+ ret_val = 127;
+ else if (ret_val < 0)
+ ret_val = 0 - ret_val;
+
+ ret_val = ret_val >> 1;
+ return ret_val;
+}
+
static void query_rxphystatus(struct ieee80211_hw *hw,
struct rtl_stats *pstatus, u8 *pdesc,
struct rx_fwinfo_8821ae *p_drvinfo,
for (i = 0; i < max_spatial_stream; i++) {
evm = rtl_evm_db_to_percentage(p_phystrpt->rxevm[i]);
- evmdbm = rtl_evm_dbm_jaguar(p_phystrpt->rxevm[i]);
+ evmdbm = _rtl8821ae_evm_dbm_jaguar(p_phystrpt->rxevm[i]);
if (bpacket_match_bssid) {
/* Fill value in RFD, Get the first
u8 rtl_evm_db_to_percentage(char value)
{
- char ret_val;
- ret_val = value;
+ char ret_val = clamp(-value, 0, 33) * 3;
- if (ret_val >= 0)
- ret_val = 0;
- if (ret_val <= -33)
- ret_val = -33;
- ret_val = 0 - ret_val;
- ret_val *= 3;
if (ret_val == 99)
ret_val = 100;
}
EXPORT_SYMBOL(rtl_evm_db_to_percentage);
-u8 rtl_evm_dbm_jaguar(char value)
-{
- char ret_val = value;
-
- /* -33dB~0dB to 33dB ~ 0dB*/
- if (ret_val == -128)
- ret_val = 127;
- else if (ret_val < 0)
- ret_val = 0 - ret_val;
-
- ret_val = ret_val >> 1;
- return ret_val;
-}
-EXPORT_SYMBOL(rtl_evm_dbm_jaguar);
-
static long rtl_translate_todbm(struct ieee80211_hw *hw,
u8 signal_strength_index)
{
u8 rtl_query_rxpwrpercentage(char antpower);
u8 rtl_evm_db_to_percentage(char value);
-u8 rtl_evm_dbm_jaguar(char value);
long rtl_signal_scale_mapping(struct ieee80211_hw *hw, long currsig);
void rtl_process_phyinfo(struct ieee80211_hw *hw, u8 *buffer,
struct rtl_stats *pstatus);
}
EXPORT_SYMBOL_GPL(wl1251_free_hw);
-MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core");
+MODULE_DESCRIPTION("TI wl1251 Wireless LAN Driver Core");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
MODULE_FIRMWARE(WL1251_FW_NAME);
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u");
-WL18XX_DEBUGFS_FWSTATS_FILE(rx_rate, rx_frames_per_rates, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(rx_rate, rx_frames_per_rates, 50);
WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_vs_rate,
AGGR_STATS_TX_AGG*AGGR_STATS_TX_RATE);
#include "wlcore.h"
-int wl1271_format_buffer(char __user *userbuf, size_t count,
- loff_t *ppos, char *fmt, ...);
+__printf(4, 5) int wl1271_format_buffer(char __user *userbuf, size_t count,
+ loff_t *ppos, char *fmt, ...);
int wl1271_debugfs_init(struct wl1271 *wl);
void wl1271_debugfs_exit(struct wl1271 *wl);
static int xennet_change_mtu(struct net_device *dev, int mtu)
{
- int max = xennet_can_sg(dev) ?
- XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER : ETH_DATA_LEN;
+ int max = xennet_can_sg(dev) ? XEN_NETIF_MAX_TX_SIZE : ETH_DATA_LEN;
if (mtu > max)
return -EINVAL;
netdev->ethtool_ops = &xennet_ethtool_ops;
SET_NETDEV_DEV(netdev, &dev->dev);
- netif_set_gso_max_size(netdev, XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER);
-
np->netdev = netdev;
netif_carrier_off(netdev);
source "drivers/nfc/nfcmrvl/Kconfig"
source "drivers/nfc/st21nfca/Kconfig"
source "drivers/nfc/st21nfcb/Kconfig"
+source "drivers/nfc/nxp-nci/Kconfig"
endmenu
obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o
obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/
obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb/
+obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
if (r < 0)
goto err_irq;
- nfc_info(&client->dev, "Probed");
+ nfc_info(&client->dev, "Probed\n");
return 0;
priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0);
if (!priv->ndev) {
- nfc_err(dev, "nci_allocate_device failed");
+ nfc_err(dev, "nci_allocate_device failed\n");
rc = -ENOMEM;
goto error;
}
rc = nci_register_device(priv->ndev);
if (rc) {
- nfc_err(dev, "nci_register_device failed %d", rc);
+ nfc_err(dev, "nci_register_device failed %d\n", rc);
nci_free_device(priv->ndev);
goto error;
}
if (!urb->status) {
if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer,
urb->actual_length) < 0)
- nfc_err(&drv_data->udev->dev, "corrupted Rx packet");
+ nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n");
}
if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
*/
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
- "urb %p failed to resubmit (%d)", urb, -err);
+ "urb %p failed to resubmit (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
}
if (err) {
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
- "urb %p submission failed (%d)", urb, -err);
+ "urb %p submission failed (%d)\n", urb, -err);
usb_unanchor_urb(urb);
}
struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
- nfc_info(priv->dev, "urb %p status %d count %d",
+ nfc_info(priv->dev, "urb %p status %d count %d\n",
urb, urb->status, urb->actual_length);
spin_lock(&drv_data->txlock);
if (err) {
if (err != -EPERM && err != -ENODEV)
nfc_err(&drv_data->udev->dev,
- "urb %p submission failed (%d)", urb, -err);
+ "urb %p submission failed (%d)\n", urb, -err);
kfree(urb->setup_packet);
usb_unanchor_urb(urb);
} else {
int i;
struct usb_device *udev = interface_to_usbdev(intf);
- nfc_info(&udev->dev, "intf %p id %p", intf, id);
+ nfc_info(&udev->dev, "intf %p id %p\n", intf, id);
drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
if (!drv_data)
if (!drv_data)
return;
- nfc_info(&drv_data->udev->dev, "intf %p", intf);
+ nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
nfcmrvl_nci_unregister_dev(drv_data->priv);
{
struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
- nfc_info(&drv_data->udev->dev, "intf %p", intf);
+ nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
if (drv_data->suspend_count++)
return 0;
struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
int err = 0;
- nfc_info(&drv_data->udev->dev, "intf %p", intf);
+ nfc_info(&drv_data->udev->dev, "intf %p\n", intf);
if (--drv_data->suspend_count)
return 0;
--- /dev/null
+config NFC_NXP_NCI
+ tristate "NXP-NCI NFC driver"
+ depends on NFC_NCI
+ default n
+ ---help---
+ Generic core driver for NXP NCI chips such as the NPC100
+ or PN7150 families.
+ This is a driver based on the NCI NFC kernel layers and
+ will thus not work with NXP libnfc library.
+
+ To compile this driver as a module, choose m here. The module will
+ be called nxp_nci.
+ Say N if unsure.
+
+config NFC_NXP_NCI_I2C
+ tristate "NXP-NCI I2C support"
+ depends on NFC_NXP_NCI && I2C
+ ---help---
+ This module adds support for an I2C interface to the NXP NCI
+ chips.
+ Select this if your platform is using the I2C bus.
+
+ To compile this driver as a module, choose m here. The module will
+ be called nxp_nci_i2c.
+ Say Y if unsure.
--- /dev/null
+#
+# Makefile for NXP-NCI NFC driver
+#
+
+nxp-nci-objs = core.o firmware.o
+nxp-nci_i2c-objs = i2c.o
+
+obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o
+obj-$(CONFIG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o
+
+ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
--- /dev/null
+/*
+ * Generic driver for NXP NCI NFC chips
+ *
+ * Copyright (C) 2014 NXP Semiconductors All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * 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 that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/platform_data/nxp-nci.h>
+
+#include <net/nfc/nci_core.h>
+
+#include "nxp-nci.h"
+
+#define NXP_NCI_HDR_LEN 4
+
+#define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
+ NFC_PROTO_MIFARE_MASK | \
+ NFC_PROTO_FELICA_MASK | \
+ NFC_PROTO_ISO14443_MASK | \
+ NFC_PROTO_ISO14443_B_MASK | \
+ NFC_PROTO_NFC_DEP_MASK)
+
+static int nxp_nci_open(struct nci_dev *ndev)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ int r = 0;
+
+ mutex_lock(&info->info_lock);
+
+ if (info->mode != NXP_NCI_MODE_COLD) {
+ r = -EBUSY;
+ goto open_exit;
+ }
+
+ if (info->phy_ops->set_mode)
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI);
+
+ info->mode = NXP_NCI_MODE_NCI;
+
+open_exit:
+ mutex_unlock(&info->info_lock);
+ return r;
+}
+
+static int nxp_nci_close(struct nci_dev *ndev)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ int r = 0;
+
+ mutex_lock(&info->info_lock);
+
+ if (info->phy_ops->set_mode)
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+
+ info->mode = NXP_NCI_MODE_COLD;
+
+ mutex_unlock(&info->info_lock);
+ return r;
+}
+
+static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ int r;
+
+ if (!info->phy_ops->write) {
+ r = -ENOTSUPP;
+ goto send_exit;
+ }
+
+ if (info->mode != NXP_NCI_MODE_NCI) {
+ r = -EINVAL;
+ goto send_exit;
+ }
+
+ r = info->phy_ops->write(info->phy_id, skb);
+ if (r < 0)
+ kfree_skb(skb);
+
+send_exit:
+ return r;
+}
+
+static struct nci_ops nxp_nci_ops = {
+ .open = nxp_nci_open,
+ .close = nxp_nci_close,
+ .send = nxp_nci_send,
+ .fw_download = nxp_nci_fw_download,
+};
+
+int nxp_nci_probe(void *phy_id, struct device *pdev,
+ struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload,
+ struct nci_dev **ndev)
+{
+ struct nxp_nci_info *info;
+ int r;
+
+ info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL);
+ if (!info) {
+ r = -ENOMEM;
+ goto probe_exit;
+ }
+
+ info->phy_id = phy_id;
+ info->pdev = pdev;
+ info->phy_ops = phy_ops;
+ info->max_payload = max_payload;
+ INIT_WORK(&info->fw_info.work, nxp_nci_fw_work);
+ init_completion(&info->fw_info.cmd_completion);
+ mutex_init(&info->info_lock);
+
+ if (info->phy_ops->set_mode) {
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+ if (r < 0)
+ goto probe_exit;
+ }
+
+ info->mode = NXP_NCI_MODE_COLD;
+
+ info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS,
+ NXP_NCI_HDR_LEN, 0);
+ if (!info->ndev) {
+ r = -ENOMEM;
+ goto probe_exit;
+ }
+
+ nci_set_parent_dev(info->ndev, pdev);
+ nci_set_drvdata(info->ndev, info);
+ r = nci_register_device(info->ndev);
+ if (r < 0)
+ goto probe_exit_free_nci;
+
+ *ndev = info->ndev;
+
+ goto probe_exit;
+
+probe_exit_free_nci:
+ nci_free_device(info->ndev);
+probe_exit:
+ return r;
+}
+EXPORT_SYMBOL(nxp_nci_probe);
+
+void nxp_nci_remove(struct nci_dev *ndev)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+
+ if (info->mode == NXP_NCI_MODE_FW)
+ nxp_nci_fw_work_complete(info, -ESHUTDOWN);
+ cancel_work_sync(&info->fw_info.work);
+
+ mutex_lock(&info->info_lock);
+
+ if (info->phy_ops->set_mode)
+ info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+
+ nci_unregister_device(ndev);
+ nci_free_device(ndev);
+
+ mutex_unlock(&info->info_lock);
+}
+EXPORT_SYMBOL(nxp_nci_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NXP NCI NFC driver");
+MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
--- /dev/null
+/*
+ * Generic driver for NXP NCI NFC chips
+ *
+ * Copyright (C) 2014 NXP Semiconductors All rights reserved.
+ *
+ * Author: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * 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 that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <linux/unaligned/access_ok.h>
+
+#include "nxp-nci.h"
+
+/* Crypto operations can take up to 30 seconds */
+#define NXP_NCI_FW_ANSWER_TIMEOUT msecs_to_jiffies(30000)
+
+#define NXP_NCI_FW_CMD_RESET 0xF0
+#define NXP_NCI_FW_CMD_GETVERSION 0xF1
+#define NXP_NCI_FW_CMD_CHECKINTEGRITY 0xE0
+#define NXP_NCI_FW_CMD_WRITE 0xC0
+#define NXP_NCI_FW_CMD_READ 0xA2
+#define NXP_NCI_FW_CMD_GETSESSIONSTATE 0xF2
+#define NXP_NCI_FW_CMD_LOG 0xA7
+#define NXP_NCI_FW_CMD_FORCE 0xD0
+#define NXP_NCI_FW_CMD_GET_DIE_ID 0xF4
+
+#define NXP_NCI_FW_CHUNK_FLAG 0x0400
+
+#define NXP_NCI_FW_RESULT_OK 0x00
+#define NXP_NCI_FW_RESULT_INVALID_ADDR 0x01
+#define NXP_NCI_FW_RESULT_GENERIC_ERROR 0x02
+#define NXP_NCI_FW_RESULT_UNKNOWN_CMD 0x0B
+#define NXP_NCI_FW_RESULT_ABORTED_CMD 0x0C
+#define NXP_NCI_FW_RESULT_PLL_ERROR 0x0D
+#define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR 0x1E
+#define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR 0x1F
+#define NXP_NCI_FW_RESULT_MEM_BSY 0x20
+#define NXP_NCI_FW_RESULT_SIGNATURE_ERROR 0x21
+#define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR 0x24
+#define NXP_NCI_FW_RESULT_PROTOCOL_ERROR 0x28
+#define NXP_NCI_FW_RESULT_SFWU_DEGRADED 0x2A
+#define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK 0x2D
+#define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK 0x2E
+#define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5 0xC5
+
+void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result)
+{
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+ int r;
+
+ if (info->phy_ops->set_mode) {
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD);
+ if (r < 0 && result == 0)
+ result = -r;
+ }
+
+ info->mode = NXP_NCI_MODE_COLD;
+
+ if (fw_info->fw) {
+ release_firmware(fw_info->fw);
+ fw_info->fw = NULL;
+ }
+
+ nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result);
+}
+
+/* crc_ccitt cannot be used since it is computed MSB first and not LSB first */
+static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len)
+{
+ u16 crc = 0xffff;
+
+ while (len--) {
+ crc = ((crc >> 8) | (crc << 8)) ^ *buffer++;
+ crc ^= (crc & 0xff) >> 4;
+ crc ^= (crc & 0xff) << 12;
+ crc ^= (crc & 0xff) << 5;
+ }
+
+ return crc;
+}
+
+static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info)
+{
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+ u16 header, crc;
+ struct sk_buff *skb;
+ size_t chunk_len;
+ size_t remaining_len;
+ int r;
+
+ skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL);
+ if (!skb) {
+ r = -ENOMEM;
+ goto chunk_exit;
+ }
+
+ chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN;
+ remaining_len = fw_info->frame_size - fw_info->written;
+
+ if (remaining_len > chunk_len) {
+ header = NXP_NCI_FW_CHUNK_FLAG;
+ } else {
+ chunk_len = remaining_len;
+ header = 0x0000;
+ }
+
+ header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK;
+ put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN));
+
+ memcpy(skb_put(skb, chunk_len), fw_info->data + fw_info->written,
+ chunk_len);
+
+ crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN);
+ put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN));
+
+ r = info->phy_ops->write(info->phy_id, skb);
+ if (r >= 0)
+ r = chunk_len;
+
+ kfree_skb(skb);
+
+chunk_exit:
+ return r;
+}
+
+static int nxp_nci_fw_send(struct nxp_nci_info *info)
+{
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+ long completion_rc;
+ int r;
+
+ reinit_completion(&fw_info->cmd_completion);
+
+ if (fw_info->written == 0) {
+ fw_info->frame_size = get_unaligned_be16(fw_info->data) &
+ NXP_NCI_FW_FRAME_LEN_MASK;
+ fw_info->data += NXP_NCI_FW_HDR_LEN;
+ fw_info->size -= NXP_NCI_FW_HDR_LEN;
+ }
+
+ if (fw_info->frame_size > fw_info->size)
+ return -EMSGSIZE;
+
+ r = nxp_nci_fw_send_chunk(info);
+ if (r < 0)
+ return r;
+
+ fw_info->written += r;
+
+ if (*fw_info->data == NXP_NCI_FW_CMD_RESET) {
+ fw_info->cmd_result = 0;
+ if (fw_info->fw)
+ schedule_work(&fw_info->work);
+ } else {
+ completion_rc = wait_for_completion_interruptible_timeout(
+ &fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT);
+ if (completion_rc == 0)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+void nxp_nci_fw_work(struct work_struct *work)
+{
+ struct nxp_nci_info *info;
+ struct nxp_nci_fw_info *fw_info;
+ int r;
+
+ fw_info = container_of(work, struct nxp_nci_fw_info, work);
+ info = container_of(fw_info, struct nxp_nci_info, fw_info);
+
+ mutex_lock(&info->info_lock);
+
+ r = fw_info->cmd_result;
+ if (r < 0)
+ goto exit_work;
+
+ if (fw_info->written == fw_info->frame_size) {
+ fw_info->data += fw_info->frame_size;
+ fw_info->size -= fw_info->frame_size;
+ fw_info->written = 0;
+ }
+
+ if (fw_info->size > 0)
+ r = nxp_nci_fw_send(info);
+
+exit_work:
+ if (r < 0 || fw_info->size == 0)
+ nxp_nci_fw_work_complete(info, r);
+ mutex_unlock(&info->info_lock);
+}
+
+int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+ int r;
+
+ mutex_lock(&info->info_lock);
+
+ if (!info->phy_ops->set_mode || !info->phy_ops->write) {
+ r = -ENOTSUPP;
+ goto fw_download_exit;
+ }
+
+ if (!firmware_name || firmware_name[0] == '\0') {
+ r = -EINVAL;
+ goto fw_download_exit;
+ }
+
+ strcpy(fw_info->name, firmware_name);
+
+ r = request_firmware(&fw_info->fw, firmware_name,
+ ndev->nfc_dev->dev.parent);
+ if (r < 0)
+ goto fw_download_exit;
+
+ r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW);
+ if (r < 0) {
+ release_firmware(fw_info->fw);
+ goto fw_download_exit;
+ }
+
+ info->mode = NXP_NCI_MODE_FW;
+
+ fw_info->data = fw_info->fw->data;
+ fw_info->size = fw_info->fw->size;
+ fw_info->written = 0;
+ fw_info->frame_size = 0;
+ fw_info->cmd_result = 0;
+
+ schedule_work(&fw_info->work);
+
+fw_download_exit:
+ mutex_unlock(&info->info_lock);
+ return r;
+}
+
+static int nxp_nci_fw_read_status(u8 stat)
+{
+ switch (stat) {
+ case NXP_NCI_FW_RESULT_OK:
+ return 0;
+ case NXP_NCI_FW_RESULT_INVALID_ADDR:
+ return -EINVAL;
+ case NXP_NCI_FW_RESULT_UNKNOWN_CMD:
+ return -EINVAL;
+ case NXP_NCI_FW_RESULT_ABORTED_CMD:
+ return -EMSGSIZE;
+ case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR:
+ return -EADDRNOTAVAIL;
+ case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR:
+ return -ENOBUFS;
+ case NXP_NCI_FW_RESULT_MEM_BSY:
+ return -ENOKEY;
+ case NXP_NCI_FW_RESULT_SIGNATURE_ERROR:
+ return -EKEYREJECTED;
+ case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR:
+ return -EALREADY;
+ case NXP_NCI_FW_RESULT_PROTOCOL_ERROR:
+ return -EPROTO;
+ case NXP_NCI_FW_RESULT_SFWU_DEGRADED:
+ return -EHWPOISON;
+ case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK:
+ return 0;
+ case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK:
+ return 0;
+ case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5:
+ return -EINVAL;
+ default:
+ return -EIO;
+ }
+}
+
+static u16 nxp_nci_fw_check_crc(struct sk_buff *skb)
+{
+ u16 crc, frame_crc;
+ size_t len = skb->len - NXP_NCI_FW_CRC_LEN;
+
+ crc = nxp_nci_fw_crc(skb->data, len);
+ frame_crc = get_unaligned_be16(skb->data + len);
+
+ return (crc ^ frame_crc);
+}
+
+void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
+{
+ struct nxp_nci_info *info = nci_get_drvdata(ndev);
+ struct nxp_nci_fw_info *fw_info = &info->fw_info;
+
+ complete(&fw_info->cmd_completion);
+
+ if (skb) {
+ if (nxp_nci_fw_check_crc(skb) != 0x00)
+ fw_info->cmd_result = -EBADMSG;
+ else
+ fw_info->cmd_result = nxp_nci_fw_read_status(
+ *skb_pull(skb, NXP_NCI_FW_HDR_LEN));
+ kfree_skb(skb);
+ } else {
+ fw_info->cmd_result = -EIO;
+ }
+
+ if (fw_info->fw)
+ schedule_work(&fw_info->work);
+}
+EXPORT_SYMBOL(nxp_nci_fw_recv_frame);
--- /dev/null
+/*
+ * I2C link layer for the NXP NCI driver
+ *
+ * Copyright (C) 2014 NXP Semiconductors All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * 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 that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/platform_data/nxp-nci.h>
+#include <linux/unaligned/access_ok.h>
+
+#include <net/nfc/nfc.h>
+
+#include "nxp-nci.h"
+
+#define NXP_NCI_I2C_DRIVER_NAME "nxp-nci_i2c"
+
+#define NXP_NCI_I2C_MAX_PAYLOAD 32
+
+struct nxp_nci_i2c_phy {
+ struct i2c_client *i2c_dev;
+ struct nci_dev *ndev;
+
+ unsigned int gpio_en;
+ unsigned int gpio_fw;
+
+ int hard_fault; /*
+ * < 0 if hardware error occurred (e.g. i2c err)
+ * and prevents normal operation.
+ */
+};
+
+static int nxp_nci_i2c_set_mode(void *phy_id,
+ enum nxp_nci_mode mode)
+{
+ struct nxp_nci_i2c_phy *phy = (struct nxp_nci_i2c_phy *) phy_id;
+
+ gpio_set_value(phy->gpio_fw, (mode == NXP_NCI_MODE_FW) ? 1 : 0);
+ gpio_set_value(phy->gpio_en, (mode != NXP_NCI_MODE_COLD) ? 1 : 0);
+ usleep_range(10000, 15000);
+
+ if (mode == NXP_NCI_MODE_COLD)
+ phy->hard_fault = 0;
+
+ return 0;
+}
+
+static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb)
+{
+ int r;
+ struct nxp_nci_i2c_phy *phy = phy_id;
+ struct i2c_client *client = phy->i2c_dev;
+
+ if (phy->hard_fault != 0)
+ return phy->hard_fault;
+
+ r = i2c_master_send(client, skb->data, skb->len);
+ if (r == -EREMOTEIO) {
+ /* Retry, chip was in standby */
+ usleep_range(110000, 120000);
+ r = i2c_master_send(client, skb->data, skb->len);
+ }
+
+ if (r < 0) {
+ nfc_err(&client->dev, "Error %d on I2C send\n", r);
+ } else if (r != skb->len) {
+ nfc_err(&client->dev,
+ "Invalid length sent: %u (expected %u)\n",
+ r, skb->len);
+ r = -EREMOTEIO;
+ } else {
+ /* Success but return 0 and not number of bytes */
+ r = 0;
+ }
+
+ return r;
+}
+
+static struct nxp_nci_phy_ops i2c_phy_ops = {
+ .set_mode = nxp_nci_i2c_set_mode,
+ .write = nxp_nci_i2c_write,
+};
+
+static int nxp_nci_i2c_fw_read(struct nxp_nci_i2c_phy *phy,
+ struct sk_buff **skb)
+{
+ struct i2c_client *client = phy->i2c_dev;
+ u16 header;
+ size_t frame_len;
+ int r;
+
+ r = i2c_master_recv(client, (u8 *) &header, NXP_NCI_FW_HDR_LEN);
+ if (r < 0) {
+ goto fw_read_exit;
+ } else if (r != NXP_NCI_FW_HDR_LEN) {
+ nfc_err(&client->dev, "Incorrect header length: %u\n", r);
+ r = -EBADMSG;
+ goto fw_read_exit;
+ }
+
+ frame_len = (get_unaligned_be16(&header) & NXP_NCI_FW_FRAME_LEN_MASK) +
+ NXP_NCI_FW_CRC_LEN;
+
+ *skb = alloc_skb(NXP_NCI_FW_HDR_LEN + frame_len, GFP_KERNEL);
+ if (*skb == NULL) {
+ r = -ENOMEM;
+ goto fw_read_exit;
+ }
+
+ memcpy(skb_put(*skb, NXP_NCI_FW_HDR_LEN), &header, NXP_NCI_FW_HDR_LEN);
+
+ r = i2c_master_recv(client, skb_put(*skb, frame_len), frame_len);
+ if (r != frame_len) {
+ nfc_err(&client->dev,
+ "Invalid frame length: %u (expected %zu)\n",
+ r, frame_len);
+ r = -EBADMSG;
+ goto fw_read_exit_free_skb;
+ }
+
+ return 0;
+
+fw_read_exit_free_skb:
+ kfree_skb(*skb);
+fw_read_exit:
+ return r;
+}
+
+static int nxp_nci_i2c_nci_read(struct nxp_nci_i2c_phy *phy,
+ struct sk_buff **skb)
+{
+ struct nci_ctrl_hdr header; /* May actually be a data header */
+ struct i2c_client *client = phy->i2c_dev;
+ int r;
+
+ r = i2c_master_recv(client, (u8 *) &header, NCI_CTRL_HDR_SIZE);
+ if (r < 0) {
+ goto nci_read_exit;
+ } else if (r != NCI_CTRL_HDR_SIZE) {
+ nfc_err(&client->dev, "Incorrect header length: %u\n", r);
+ r = -EBADMSG;
+ goto nci_read_exit;
+ }
+
+ *skb = alloc_skb(NCI_CTRL_HDR_SIZE + header.plen, GFP_KERNEL);
+ if (*skb == NULL) {
+ r = -ENOMEM;
+ goto nci_read_exit;
+ }
+
+ memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), (void *) &header,
+ NCI_CTRL_HDR_SIZE);
+
+ r = i2c_master_recv(client, skb_put(*skb, header.plen), header.plen);
+ if (r != header.plen) {
+ nfc_err(&client->dev,
+ "Invalid frame payload length: %u (expected %u)\n",
+ r, header.plen);
+ r = -EBADMSG;
+ goto nci_read_exit_free_skb;
+ }
+
+ return 0;
+
+nci_read_exit_free_skb:
+ kfree_skb(*skb);
+nci_read_exit:
+ return r;
+}
+
+static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id)
+{
+ struct nxp_nci_i2c_phy *phy = phy_id;
+ struct i2c_client *client;
+ struct nxp_nci_info *info;
+
+ struct sk_buff *skb = NULL;
+ int r = 0;
+
+ if (!phy || !phy->ndev)
+ goto exit_irq_none;
+
+ client = phy->i2c_dev;
+
+ if (!client || irq != client->irq)
+ goto exit_irq_none;
+
+ info = nci_get_drvdata(phy->ndev);
+
+ if (!info)
+ goto exit_irq_none;
+
+ mutex_lock(&info->info_lock);
+
+ if (phy->hard_fault != 0)
+ goto exit_irq_handled;
+
+ switch (info->mode) {
+ case NXP_NCI_MODE_NCI:
+ r = nxp_nci_i2c_nci_read(phy, &skb);
+ break;
+ case NXP_NCI_MODE_FW:
+ r = nxp_nci_i2c_fw_read(phy, &skb);
+ break;
+ case NXP_NCI_MODE_COLD:
+ r = -EREMOTEIO;
+ break;
+ }
+
+ if (r == -EREMOTEIO) {
+ phy->hard_fault = r;
+ skb = NULL;
+ } else if (r < 0) {
+ nfc_err(&client->dev, "Read failed with error %d\n", r);
+ goto exit_irq_handled;
+ }
+
+ switch (info->mode) {
+ case NXP_NCI_MODE_NCI:
+ nci_recv_frame(phy->ndev, skb);
+ break;
+ case NXP_NCI_MODE_FW:
+ nxp_nci_fw_recv_frame(phy->ndev, skb);
+ break;
+ case NXP_NCI_MODE_COLD:
+ break;
+ }
+
+exit_irq_handled:
+ mutex_unlock(&info->info_lock);
+ return IRQ_HANDLED;
+exit_irq_none:
+ WARN_ON_ONCE(1);
+ return IRQ_NONE;
+}
+
+#ifdef CONFIG_OF
+
+static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
+{
+ struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
+ struct device_node *pp;
+ int r;
+
+ pp = client->dev.of_node;
+ if (!pp)
+ return -ENODEV;
+
+ r = of_get_named_gpio(pp, "enable-gpios", 0);
+ if (r == -EPROBE_DEFER)
+ r = of_get_named_gpio(pp, "enable-gpios", 0);
+ if (r < 0) {
+ nfc_err(&client->dev, "Failed to get EN gpio, error: %d\n", r);
+ return r;
+ }
+ phy->gpio_en = r;
+
+ r = of_get_named_gpio(pp, "firmware-gpios", 0);
+ if (r == -EPROBE_DEFER)
+ r = of_get_named_gpio(pp, "firmware-gpios", 0);
+ if (r < 0) {
+ nfc_err(&client->dev, "Failed to get FW gpio, error: %d\n", r);
+ return r;
+ }
+ phy->gpio_fw = r;
+
+ r = irq_of_parse_and_map(pp, 0);
+ if (r < 0) {
+ nfc_err(&client->dev, "Unable to get irq, error: %d\n", r);
+ return r;
+ }
+ client->irq = r;
+
+ return 0;
+}
+
+#else
+
+static int nxp_nci_i2c_parse_devtree(struct i2c_client *client)
+{
+ return -ENODEV;
+}
+
+#endif
+
+static int nxp_nci_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct nxp_nci_i2c_phy *phy;
+ struct nxp_nci_nfc_platform_data *pdata;
+ int r;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ nfc_err(&client->dev, "Need I2C_FUNC_I2C\n");
+ r = -ENODEV;
+ goto probe_exit;
+ }
+
+ phy = devm_kzalloc(&client->dev, sizeof(struct nxp_nci_i2c_phy),
+ GFP_KERNEL);
+ if (!phy) {
+ r = -ENOMEM;
+ goto probe_exit;
+ }
+
+ phy->i2c_dev = client;
+ i2c_set_clientdata(client, phy);
+
+ pdata = client->dev.platform_data;
+
+ if (!pdata && client->dev.of_node) {
+ r = nxp_nci_i2c_parse_devtree(client);
+ if (r < 0) {
+ nfc_err(&client->dev, "Failed to get DT data\n");
+ goto probe_exit;
+ }
+ } else if (pdata) {
+ phy->gpio_en = pdata->gpio_en;
+ phy->gpio_fw = pdata->gpio_fw;
+ client->irq = pdata->irq;
+ } else {
+ nfc_err(&client->dev, "No platform data\n");
+ r = -EINVAL;
+ goto probe_exit;
+ }
+
+ r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en,
+ GPIOF_OUT_INIT_LOW, "nxp_nci_en");
+ if (r < 0)
+ goto probe_exit;
+
+ r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw,
+ GPIOF_OUT_INIT_LOW, "nxp_nci_fw");
+ if (r < 0)
+ goto probe_exit;
+
+ r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops,
+ NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev);
+ if (r < 0)
+ goto probe_exit;
+
+ r = request_threaded_irq(client->irq, NULL,
+ nxp_nci_i2c_irq_thread_fn,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ NXP_NCI_I2C_DRIVER_NAME, phy);
+ if (r < 0)
+ nfc_err(&client->dev, "Unable to register IRQ handler\n");
+
+probe_exit:
+ return r;
+}
+
+static int nxp_nci_i2c_remove(struct i2c_client *client)
+{
+ struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client);
+
+ nxp_nci_remove(phy->ndev);
+ free_irq(client->irq, phy);
+
+ return 0;
+}
+
+static struct i2c_device_id nxp_nci_i2c_id_table[] = {
+ {"nxp-nci_i2c", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, nxp_nci_i2c_id_table);
+
+static const struct of_device_id of_nxp_nci_i2c_match[] = {
+ { .compatible = "nxp,nxp-nci-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match);
+
+static struct i2c_driver nxp_nci_i2c_driver = {
+ .driver = {
+ .name = NXP_NCI_I2C_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_nxp_nci_i2c_match),
+ },
+ .probe = nxp_nci_i2c_probe,
+ .id_table = nxp_nci_i2c_id_table,
+ .remove = nxp_nci_i2c_remove,
+};
+
+module_i2c_driver(nxp_nci_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers");
+MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>");
--- /dev/null
+/*
+ * Copyright (C) 2014 NXP Semiconductors All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * Derived from PN544 device driver:
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * 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 that 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __LOCAL_NXP_NCI_H_
+#define __LOCAL_NXP_NCI_H_
+
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/nfc.h>
+#include <linux/platform_data/nxp-nci.h>
+
+#include <net/nfc/nci_core.h>
+
+#define NXP_NCI_FW_HDR_LEN 2
+#define NXP_NCI_FW_CRC_LEN 2
+
+#define NXP_NCI_FW_FRAME_LEN_MASK 0x03FF
+
+enum nxp_nci_mode {
+ NXP_NCI_MODE_COLD,
+ NXP_NCI_MODE_NCI,
+ NXP_NCI_MODE_FW
+};
+
+struct nxp_nci_phy_ops {
+ int (*set_mode)(void *id, enum nxp_nci_mode mode);
+ int (*write)(void *id, struct sk_buff *skb);
+};
+
+struct nxp_nci_fw_info {
+ char name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+ const struct firmware *fw;
+
+ size_t size;
+ size_t written;
+
+ const u8 *data;
+ size_t frame_size;
+
+ struct work_struct work;
+ struct completion cmd_completion;
+
+ int cmd_result;
+};
+
+struct nxp_nci_info {
+ struct nci_dev *ndev;
+ void *phy_id;
+ struct device *pdev;
+
+ enum nxp_nci_mode mode;
+
+ struct nxp_nci_phy_ops *phy_ops;
+ unsigned int max_payload;
+
+ struct mutex info_lock;
+
+ struct nxp_nci_fw_info fw_info;
+};
+
+int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name);
+void nxp_nci_fw_work(struct work_struct *work);
+void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
+void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result);
+
+int nxp_nci_probe(void *phy_id, struct device *pdev,
+ struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload,
+ struct nci_dev **ndev);
+void nxp_nci_remove(struct nci_dev *ndev);
+
+#endif /* __LOCAL_NXP_NCI_H_ */
if (IS_ERR(resp)) {
rc = PTR_ERR(resp);
- nfc_err(&dev->interface->dev, "RF setting error %d", rc);
+ nfc_err(&dev->interface->dev, "RF setting error %d\n", rc);
return rc;
}
}
skb = pn533_build_response(dev);
- if (!skb)
+ if (!skb) {
+ rc = -ENOMEM;
goto error;
+ }
arg->cb(arg->cb_context, skb, 0);
kfree(arg);
}
nfc_info(dev, "GPIO resource, no:%d irq:%d\n",
- desc_to_gpio(gpiod_irq), ret);
+ desc_to_gpio(gpiod_irq), ret);
client->irq = ret;
return 0;
phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy),
GFP_KERNEL);
- if (!phy) {
- nfc_err(&client->dev,
- "Cannot allocate memory for pn544 i2c phy.\n");
+ if (!phy)
return -ENOMEM;
- }
INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work);
phy->fw_work_state = FW_WORK_STATE_IDLE;
case -ECONNRESET:
case -ENOENT:
nfc_err(&dev->interface->dev,
- "The urb has been canceled (status %d)", urb->status);
+ "The urb has been canceled (status %d)\n", urb->status);
goto sched_wq;
case -ESHUTDOWN:
default:
- nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+ nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
urb->status);
goto sched_wq;
}
in_frame = dev->in_urb->transfer_buffer;
if (!port100_rx_frame_is_valid(in_frame)) {
- nfc_err(&dev->interface->dev, "Received an invalid frame");
+ nfc_err(&dev->interface->dev, "Received an invalid frame\n");
cmd->status = -EIO;
goto sched_wq;
}
if (!port100_rx_frame_is_cmd_response(dev, in_frame)) {
nfc_err(&dev->interface->dev,
- "It's not the response to the last command");
+ "It's not the response to the last command\n");
cmd->status = -EIO;
goto sched_wq;
}
case -ECONNRESET:
case -ENOENT:
nfc_err(&dev->interface->dev,
- "The urb has been stopped (status %d)", urb->status);
+ "The urb has been stopped (status %d)\n", urb->status);
goto sched_wq;
case -ESHUTDOWN:
default:
- nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+ nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
urb->status);
goto sched_wq;
}
in_frame = dev->in_urb->transfer_buffer;
if (!port100_rx_frame_is_ack(in_frame)) {
- nfc_err(&dev->interface->dev, "Received an invalid ack");
+ nfc_err(&dev->interface->dev, "Received an invalid ack\n");
cmd->status = -EIO;
goto sched_wq;
}
rc = port100_submit_urb_for_response(dev, GFP_ATOMIC);
if (rc) {
nfc_err(&dev->interface->dev,
- "usb_submit_urb failed with result %d", rc);
+ "usb_submit_urb failed with result %d\n", rc);
cmd->status = rc;
goto sched_wq;
}
case -ECONNRESET:
case -ENOENT:
nfc_err(&dev->interface->dev,
- "The urb has been stopped (status %d)", urb->status);
+ "The urb has been stopped (status %d)\n", urb->status);
break;
case -ESHUTDOWN:
default:
- nfc_err(&dev->interface->dev, "Urb failure (status %d)",
+ nfc_err(&dev->interface->dev, "Urb failure (status %d)\n",
urb->status);
}
}
if (resp->len < 4) {
nfc_err(&dev->interface->dev,
- "Invalid packet length received.\n");
+ "Invalid packet length received\n");
rc = -EIO;
goto error;
}
PORT100_MDAA_TGT_WAS_ACTIVATED_MASK;
break;
default:
- nfc_err(&dev->interface->dev, "Unknonwn command type.\n");
+ nfc_err(&dev->interface->dev, "Unknown command type\n");
return false;
}
cmd_type_mask = port100_get_command_type_mask(dev);
if (!cmd_type_mask) {
nfc_err(&interface->dev,
- "Could not get supported command types.\n");
+ "Could not get supported command types\n");
rc = -ENODEV;
goto error;
}
rc = port100_set_command_type(dev, dev->cmd_type);
if (rc) {
nfc_err(&interface->dev,
- "The device does not support command type %u.\n",
+ "The device does not support command type %u\n",
dev->cmd_type);
goto error;
}
fw_version = port100_get_firmware_version(dev);
if (!fw_version)
nfc_err(&interface->dev,
- "Could not get device firmware version.\n");
+ "Could not get device firmware version\n");
nfc_info(&interface->dev,
"Sony NFC Port-100 Series attached (firmware v%x.%02x)\n",
dev->skb_tailroom);
if (!dev->nfc_digital_dev) {
nfc_err(&interface->dev,
- "Could not allocate nfc_digital_dev.\n");
+ "Could not allocate nfc_digital_dev\n");
rc = -ENOMEM;
goto error;
}
rc = nfc_digital_register_device(dev->nfc_digital_dev);
if (rc) {
nfc_err(&interface->dev,
- "Could not register digital device.\n");
+ "Could not register digital device\n");
goto free_nfc_dev;
}
kfree(dev->cmd);
- nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected");
+ nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected\n");
}
static struct usb_driver port100_driver = {
return r;
}
-static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate,
+static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *uid,
int *len)
{
int r;
goto exit;
}
- gate = uid_skb->data;
+ memcpy(uid, uid_skb->data, uid_skb->len);
*len = uid_skb->len;
exit:
kfree_skb(uid_skb);
case ST21NFCA_EVT_CONNECTIVITY:
break;
case ST21NFCA_EVT_TRANSACTION:
+ /*
+ * According to specification etsi 102 622
+ * 11.2.2.4 EVT_TRANSACTION Table 52
+ * Description Tag Length
+ * AID 81 5 to 16
+ * PARAMETERS 82 0 to 255
+ */
if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
return -EPROTO;
skb->len - 2, GFP_KERNEL);
transaction->aid_len = skb->data[1];
- memcpy(transaction->aid, &skb->data[2], skb->data[1]);
+ memcpy(transaction->aid, &skb->data[2],
+ transaction->aid_len);
+ /* Check next byte is PARAMETERS tag (82) */
if (skb->data[transaction->aid_len + 2] !=
NFC_EVT_TRANSACTION_PARAMS_TAG)
return -EPROTO;
return phy->ndlc->hard_fault;
r = i2c_master_send(client, skb->data, skb->len);
- if (r == -EREMOTEIO) { /* Retry, chip was in standby */
+ if (r < 0) { /* Retry, chip was in standby */
usleep_range(1000, 4000);
r = i2c_master_send(client, skb->data, skb->len);
}
struct i2c_client *client = phy->i2c_dev;
r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE);
- if (r == -EREMOTEIO) { /* Retry, chip was in standby */
+ if (r < 0) { /* Retry, chip was in standby */
usleep_range(1000, 4000);
r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE);
}
phy = devm_kzalloc(&client->dev, sizeof(struct st21nfcb_i2c_phy),
GFP_KERNEL);
- if (!phy) {
- nfc_err(&client->dev,
- "Cannot allocate memory for st21nfcb i2c phy.\n");
+ if (!phy)
return -ENOMEM;
- }
phy->i2c_dev = client;
struct llt_ndlc *ndlc;
ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL);
- if (!ndlc) {
- nfc_err(dev, "Cannot allocate memory for ndlc.\n");
+ if (!ndlc)
return -ENOMEM;
- }
+
ndlc->ops = phy_ops;
ndlc->phy_id = phy_id;
ndlc->dev = dev;
break;
case ST21NFCB_EVT_TRANSACTION:
+ /* According to specification etsi 102 622
+ * 11.2.2.4 EVT_TRANSACTION Table 52
+ * Description Tag Length
+ * AID 81 5 to 16
+ * PARAMETERS 82 0 to 255
+ */
if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
return -EPROTO;
skb->len - 2, GFP_KERNEL);
transaction->aid_len = skb->data[1];
- memcpy(transaction->aid, &skb->data[2], skb->data[1]);
+ memcpy(transaction->aid, &skb->data[2], transaction->aid_len);
+ /* Check next byte is PARAMETERS tag (82) */
if (skb->data[transaction->aid_len + 2] !=
NFC_EVT_TRANSACTION_PARAMS_TAG)
return -EPROTO;
transaction->aid_len + 4, transaction->params_len);
r = nfc_se_transaction(ndev->nfc_dev, host, transaction);
+ break;
default:
return 1;
}
r = nci_hci_dev_session_init(ndev);
if (r != NCI_HCI_ANY_OK)
- goto exit;
+ goto free_dest_params;
r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id,
NCI_NFCEE_ENABLE);
if (r != NCI_STATUS_OK)
- goto exit;
-
- return 0;
+ goto free_dest_params;
free_dest_params:
kfree(dest_params);
return NULL;
}
-static int of_empty_ranges_quirk(void)
+static int of_empty_ranges_quirk(struct device_node *np)
{
if (IS_ENABLED(CONFIG_PPC)) {
- /* To save cycles, we cache the result */
+ /* To save cycles, we cache the result for global "Mac" setting */
static int quirk_state = -1;
+ /* PA-SEMI sdc DT bug */
+ if (of_device_is_compatible(np, "1682m-sdc"))
+ return true;
+
+ /* Make quirk cached */
if (quirk_state < 0)
quirk_state =
of_machine_is_compatible("Power Macintosh") ||
* This code is only enabled on powerpc. --gcl
*/
ranges = of_get_property(parent, rprop, &rlen);
- if (ranges == NULL && !of_empty_ranges_quirk()) {
+ if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
pr_debug("OF: no ranges; cannot translate\n");
return 1;
}
if (!pmic)
return -ENOMEM;
+ if (of_device_is_compatible(node, "ti,tps659038-pmic"))
+ palmas_generic_regs_info[PALMAS_REG_REGEN2].ctrl_addr =
+ TPS659038_REGEN2_CTRL;
+
pmic->dev = &pdev->dev;
pmic->palmas = palmas;
palmas->pmic = pmic;
mrst->dev = NULL;
}
-#ifdef CONFIG_PM
-static int mrst_suspend(struct device *dev, pm_message_t mesg)
+#ifdef CONFIG_PM_SLEEP
+static int mrst_suspend(struct device *dev)
{
struct mrst_rtc *mrst = dev_get_drvdata(dev);
unsigned char tmp;
*/
static inline int mrst_poweroff(struct device *dev)
{
- return mrst_suspend(dev, PMSG_HIBERNATE);
+ return mrst_suspend(dev);
}
static int mrst_resume(struct device *dev)
return 0;
}
+static SIMPLE_DEV_PM_OPS(mrst_pm_ops, mrst_suspend, mrst_resume);
+#define MRST_PM_OPS (&mrst_pm_ops)
+
#else
-#define mrst_suspend NULL
-#define mrst_resume NULL
+#define MRST_PM_OPS NULL
static inline int mrst_poweroff(struct device *dev)
{
.remove = vrtc_mrst_platform_remove,
.shutdown = vrtc_mrst_platform_shutdown,
.driver = {
- .name = (char *) driver_name,
- .suspend = mrst_suspend,
- .resume = mrst_resume,
+ .name = driver_name,
+ .pm = MRST_PM_OPS,
}
};
};
static struct ata_port_info sata_port_info = {
- .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA,
+ .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA |
+ ATA_FLAG_SAS_HOST,
.pio_mask = ATA_PIO4_ONLY,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
};
static struct ata_port_info sata_port_info = {
- .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ,
+ .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ |
+ ATA_FLAG_SAS_HOST,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
{
struct dw_spi *dws = arg;
- if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY))
+ clear_bit(TX_BUSY, &dws->dma_chan_busy);
+ if (test_bit(RX_BUSY, &dws->dma_chan_busy))
return;
dw_spi_xfer_done(dws);
}
{
struct dw_spi *dws = arg;
- if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY))
+ clear_bit(RX_BUSY, &dws->dma_chan_busy);
+ if (test_bit(TX_BUSY, &dws->dma_chan_busy))
return;
dw_spi_xfer_done(dws);
}
struct resource *res;
struct device *dev;
void __iomem *base;
- u32 max_freq, iomode;
+ u32 max_freq, iomode, num_cs;
int ret, irq, size;
dev = &pdev->dev;
}
/* use num-cs unless not present or out of range */
- if (of_property_read_u16(dev->of_node, "num-cs",
- &master->num_chipselect) ||
- (master->num_chipselect > SPI_NUM_CHIPSELECTS))
+ if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) ||
+ num_cs > SPI_NUM_CHIPSELECTS)
master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ else
+ master->num_chipselect = num_cs;
master->bus_num = pdev->id;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
"failed to unprepare message: %d\n", ret);
}
}
+
+ trace_spi_message_done(mesg);
+
master->cur_msg_prepared = false;
mesg->state = NULL;
if (mesg->complete)
mesg->complete(mesg->context);
-
- trace_spi_message_done(mesg);
}
EXPORT_SYMBOL_GPL(spi_finalize_current_message);
pcicore_write32(pc, SSB_PCICORE_SBTOPCI2,
SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA);
+ /*
+ * Accessing PCI config without a proper delay after devices reset (not
+ * GPIO reset) was causing reboots on WRT300N v1.0.
+ * Tested delay 850 us lowered reboot chance to 50-80%, 1000 us fixed it
+ * completely. Flushing all writes was also tested but with no luck.
+ */
+ if (pc->dev->bus->chip_id == 0x4704)
+ usleep_range(1000, 2000);
+
/* Enable PCI bridge BAR0 prefetch and burst */
val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2);
config IIO_SIMPLE_DUMMY_BUFFER
bool "Buffered capture support"
select IIO_BUFFER
+ select IIO_TRIGGER
select IIO_KFIFO_BUF
help
Add buffered data capture to the simple dummy driver.
mutex_init(&data->lock);
indio_dev->dev.parent = dev;
+ indio_dev->name = dev->driver->name;
indio_dev->info = &hmc5843_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = data->variant->channels;
writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
sport->port.membase + UARTPFIFO);
+ /* explicitly clear RDRF */
+ readb(sport->port.membase + UARTSR1);
+
/* flush Tx and Rx FIFO */
writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
sport->port.membase + UARTCFIFO);
sport->txfifo_size = 0x1 << (((temp >> UARTPFIFO_TXSIZE_OFF) &
UARTPFIFO_FIFOSIZE_MASK) + 1);
+ sport->port.fifosize = sport->txfifo_size;
+
sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
UARTPFIFO_FIFOSIZE_MASK) + 1);
free_irq(ourport->tx_irq, ourport);
tx_enabled(port) = 0;
ourport->tx_claimed = 0;
+ ourport->tx_mode = 0;
}
if (ourport->rx_claimed) {
status = PORT_PLC;
port_change_bit = "link state";
break;
+ case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
+ status = PORT_CEC;
+ port_change_bit = "config error";
+ break;
default:
/* Should never happen */
return;
status |= USB_PORT_STAT_C_LINK_STATE << 16;
if ((raw_port_status & PORT_WRC))
status |= USB_PORT_STAT_C_BH_RESET << 16;
+ if ((raw_port_status & PORT_CEC))
+ status |= USB_PORT_STAT_C_CONFIG_ERROR << 16;
}
if (hcd->speed != HCD_USB3) {
case USB_PORT_FEAT_C_OVER_CURRENT:
case USB_PORT_FEAT_C_ENABLE:
case USB_PORT_FEAT_C_PORT_LINK_STATE:
+ case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
xhci_clear_port_change_bit(xhci, wValue, wIndex,
port_array[wIndex], temp);
break;
*/
status = bus_state->resuming_ports;
- mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC;
+ mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC | PORT_CEC;
spin_lock_irqsave(&xhci->lock, flags);
/* For each port, did anything change? If so, set that bit in buf. */
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
xhci->quirks |= XHCI_LPM_SUPPORT;
xhci->quirks |= XHCI_INTEL_HOST;
+ xhci->quirks |= XHCI_AVOID_BEI;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) {
* PPT chipsets.
*/
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
- xhci->quirks |= XHCI_AVOID_BEI;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
if (udc->driver) {
dev_err(udc->isp->dev, "UDC already has a gadget driver\n");
- spin_unlock(&udc->lock);
+ spin_unlock_irqrestore(&udc->lock, flags);
return -EBUSY;
}
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE(FTDI_VID, FTDI_SYNAPSE_SS200_PID) },
/*
* ELV devices:
*/
{
struct usb_device *udev = serial->dev;
- if ((udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) ||
- (udev->product && !strcmp(udev->product, "BeagleBone/XDS100V2")))
+ if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems"))
+ return ftdi_jtag_probe(serial);
+
+ if (udev->product &&
+ (!strcmp(udev->product, "BeagleBone/XDS100V2") ||
+ !strcmp(udev->product, "SNAP Connect E10")))
return ftdi_jtag_probe(serial);
return 0;
*/
#define FTDI_NT_ORIONLXM_PID 0x7c90 /* OrionLXm Substation Automation Platform */
+/*
+ * Synapse Wireless product ids (FTDI_VID)
+ * http://www.synapse-wireless.com
+ */
+#define FTDI_SYNAPSE_SS200_PID 0x9090 /* SS200 - SNAP Stick 200 */
+
/********************************/
/** third-party VID/PID combos **/
/* For Xircom PGSDB9 and older Entrega version of the same device */
#define XIRCOM_VENDOR_ID 0x085a
#define XIRCOM_FAKE_ID 0x8027
+#define XIRCOM_FAKE_ID_2 0x8025 /* "PGMFHUB" serial */
#define ENTREGA_VENDOR_ID 0x1645
#define ENTREGA_FAKE_ID 0x8093
#endif
#ifdef XIRCOM
{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
+ { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
{ USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) },
#endif
{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) },
#ifdef XIRCOM
static const struct usb_device_id id_table_fake_xircom[] = {
{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
+ { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
{ USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) },
{ }
};
#define PDC_WDT_MIN_TIMEOUT 1
#define PDC_WDT_DEF_TIMEOUT 64
-static int heartbeat;
+static int heartbeat = PDC_WDT_DEF_TIMEOUT;
module_param(heartbeat, int, 0);
-MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
- "(default = " __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds "
+ "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
pdc_wdt->wdt_dev.ops = &pdc_wdt_ops;
pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK;
pdc_wdt->wdt_dev.parent = &pdev->dev;
+ watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev);
if (ret < 0) {
watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout);
platform_set_drvdata(pdev, pdc_wdt);
- watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
ret = watchdog_register_device(&pdc_wdt->wdt_dev);
if (ret)
u32 reg;
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
void __iomem *wdt_base = mtk_wdt->wdt_base;
- u32 ret;
+ int ret;
ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
if (ret < 0)
In that case step 3 should be omitted.
+config XEN_BALLOON_MEMORY_HOTPLUG_LIMIT
+ int "Hotplugged memory limit (in GiB) for a PV guest"
+ default 512 if X86_64
+ default 4 if X86_32
+ range 0 64 if X86_32
+ depends on XEN_HAVE_PVMMU
+ depends on XEN_BALLOON_MEMORY_HOTPLUG
+ help
+ Maxmium amount of memory (in GiB) that a PV guest can be
+ expanded to when using memory hotplug.
+
+ A PV guest can have more memory than this limit if is
+ started with a larger maximum.
+
+ This value is used to allocate enough space in internal
+ tables needed for physical memory administration.
+
config XEN_SCRUB_PAGES
bool "Scrub pages before returning them to system"
depends on XEN_BALLOON
balloon_hotplug = round_up(balloon_hotplug, PAGES_PER_SECTION);
nid = memory_add_physaddr_to_nid(hotplug_start_paddr);
+#ifdef CONFIG_XEN_HAVE_PVMMU
+ /*
+ * add_memory() will build page tables for the new memory so
+ * the p2m must contain invalid entries so the correct
+ * non-present PTEs will be written.
+ *
+ * If a failure occurs, the original (identity) p2m entries
+ * are not restored since this region is now known not to
+ * conflict with any devices.
+ */
+ if (!xen_feature(XENFEAT_auto_translated_physmap)) {
+ unsigned long pfn, i;
+
+ pfn = PFN_DOWN(hotplug_start_paddr);
+ for (i = 0; i < balloon_hotplug; i++) {
+ if (!set_phys_to_machine(pfn + i, INVALID_P2M_ENTRY)) {
+ pr_warn("set_phys_to_machine() failed, no memory added\n");
+ return BP_ECANCELED;
+ }
+ }
+ }
+#endif
+
rc = add_memory(nid, hotplug_start_paddr, balloon_hotplug << PAGE_SHIFT);
if (rc) {
boff = tmp % bsize;
if (boff) {
bh = affs_bread_ino(inode, bidx, 0);
- if (IS_ERR(bh))
- return PTR_ERR(bh);
+ if (IS_ERR(bh)) {
+ written = PTR_ERR(bh);
+ goto err_first_bh;
+ }
tmp = min(bsize - boff, to - from);
BUG_ON(boff + tmp > bsize || tmp > bsize);
memcpy(AFFS_DATA(bh) + boff, data + from, tmp);
bidx++;
} else if (bidx) {
bh = affs_bread_ino(inode, bidx - 1, 0);
- if (IS_ERR(bh))
- return PTR_ERR(bh);
+ if (IS_ERR(bh)) {
+ written = PTR_ERR(bh);
+ goto err_first_bh;
+ }
}
while (from + bsize <= to) {
prev_bh = bh;
bh = affs_getemptyblk_ino(inode, bidx);
if (IS_ERR(bh))
- goto out;
+ goto err_bh;
memcpy(AFFS_DATA(bh), data + from, bsize);
if (buffer_new(bh)) {
AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
prev_bh = bh;
bh = affs_bread_ino(inode, bidx, 1);
if (IS_ERR(bh))
- goto out;
+ goto err_bh;
tmp = min(bsize, to - from);
BUG_ON(tmp > bsize);
memcpy(AFFS_DATA(bh), data + from, tmp);
if (tmp > inode->i_size)
inode->i_size = AFFS_I(inode)->mmu_private = tmp;
+err_first_bh:
unlock_page(page);
page_cache_release(page);
return written;
-out:
+err_bh:
bh = prev_bh;
if (!written)
written = PTR_ERR(bh);
/*
* fs/cifs/cifsencrypt.c
*
+ * Encryption and hashing operations relating to NTLM, NTLMv2. See MS-NLMP
+ * for more detailed information
+ *
* Copyright (C) International Business Machines Corp., 2005,2013
* Author(s): Steve French (sfrench@us.ibm.com)
*
__func__);
return rc;
}
- } else if (ses->serverName) {
+ } else {
+ /* We use ses->serverName if no domain name available */
len = strlen(ses->serverName);
server = kmalloc(2 + (len * 2), GFP_KERNEL);
pr_warn("CIFS: username too long\n");
goto cifs_parse_mount_err;
}
+
+ kfree(vol->username);
vol->username = kstrdup(string, GFP_KERNEL);
if (!vol->username)
goto cifs_parse_mount_err;
goto cifs_parse_mount_err;
}
+ kfree(vol->domainname);
vol->domainname = kstrdup(string, GFP_KERNEL);
if (!vol->domainname) {
pr_warn("CIFS: no memory for domainname\n");
}
if (strncasecmp(string, "default", 7) != 0) {
+ kfree(vol->iocharset);
vol->iocharset = kstrdup(string,
GFP_KERNEL);
if (!vol->iocharset) {
* calling name ends in null (byte 16) from old smb
* convention.
*/
- if (server->workstation_RFC1001_name &&
- server->workstation_RFC1001_name[0] != 0)
+ if (server->workstation_RFC1001_name[0] != 0)
rfc1002mangle(ses_init_buf->trailer.
session_req.calling_name,
server->workstation_RFC1001_name,
#endif /* CIFS_WEAK_PW_HASH */
rc = SMBNTencrypt(tcon->password, ses->server->cryptkey,
bcc_ptr, nls_codepage);
+ if (rc) {
+ cifs_dbg(FYI, "%s Can't generate NTLM rsp. Error: %d\n",
+ __func__, rc);
+ cifs_buf_release(smb_buffer);
+ return rc;
+ }
bcc_ptr += CIFS_AUTH_RESP_SIZE;
if (ses->capabilities & CAP_UNICODE) {
cifsFileInfo_put(inv_file);
spin_lock(&cifs_file_list_lock);
++refind;
+ inv_file = NULL;
goto refind_writable;
}
}
cifs_buf_release(srchinf->ntwrk_buf_start);
}
kfree(srchinf);
+ if (rc)
+ goto cgii_exit;
} else
goto cgii_exit;
/* return pointer to beginning of data area, ie offset from SMB start */
if ((*off != 0) && (*len != 0))
- return hdr->ProtocolId + *off;
+ return (char *)(&hdr->ProtocolId[0]) + *off;
else
return NULL;
}
/* No need to change MaxChunks since already set to 1 */
chunk_sizes_updated = true;
- }
+ } else
+ goto cchunk_out;
}
cchunk_out:
struct smb2_ioctl_req *req;
struct smb2_ioctl_rsp *rsp;
struct TCP_Server_Info *server;
- struct cifs_ses *ses = tcon->ses;
+ struct cifs_ses *ses;
struct kvec iov[2];
int resp_buftype;
int num_iovecs;
if (plen)
*plen = 0;
+ if (tcon)
+ ses = tcon->ses;
+ else
+ return -EIO;
+
if (ses && (ses->server))
server = ses->server;
else
rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
if ((rc != 0) && (rc != -EINVAL)) {
- if (tcon)
- cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+ cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
goto ioctl_exit;
} else if (rc == -EINVAL) {
if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) &&
(opcode != FSCTL_SRV_COPYCHUNK)) {
- if (tcon)
- cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+ cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
goto ioctl_exit;
}
}
rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
- if ((rc != 0) && tcon)
+ if (rc != 0)
cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE);
free_rsp_buf(resp_buftype, iov[0].iov_base);
struct kvec iov[2];
int rc = 0;
int len;
- int resp_buftype;
+ int resp_buftype = CIFS_NO_BUFFER;
unsigned char *bufptr;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
#define BNEPCONNDEL _IOW('B', 201, int)
#define BNEPGETCONNLIST _IOR('B', 210, int)
#define BNEPGETCONNINFO _IOR('B', 211, int)
+#define BNEPGETSUPPFEAT _IOR('B', 212, int)
#define CMTPCONNADD _IOW('C', 200, int)
#define CMTPCONNDEL _IOW('C', 201, int)
COMPATIBLE_IOCTL(BNEPCONNDEL)
COMPATIBLE_IOCTL(BNEPGETCONNLIST)
COMPATIBLE_IOCTL(BNEPGETCONNINFO)
+COMPATIBLE_IOCTL(BNEPGETSUPPFEAT)
COMPATIBLE_IOCTL(CMTPCONNADD)
COMPATIBLE_IOCTL(CMTPCONNDEL)
COMPATIBLE_IOCTL(CMTPGETCONNLIST)
struct completion *done; /* set if the caller waits */
};
+/*
+ * If an inode is constantly having its pages dirtied, but then the
+ * updates stop dirtytime_expire_interval seconds in the past, it's
+ * possible for the worst case time between when an inode has its
+ * timestamps updated and when they finally get written out to be two
+ * dirtytime_expire_intervals. We set the default to 12 hours (in
+ * seconds), which means most of the time inodes will have their
+ * timestamps written to disk after 12 hours, but in the worst case a
+ * few inodes might not their timestamps updated for 24 hours.
+ */
+unsigned int dirtytime_expire_interval = 12 * 60 * 60;
+
/**
* writeback_in_progress - determine whether there is writeback in progress
* @bdi: the device's backing_dev_info structure.
if ((flags & EXPIRE_DIRTY_ATIME) == 0)
older_than_this = work->older_than_this;
- else if ((work->reason == WB_REASON_SYNC) == 0) {
- expire_time = jiffies - (HZ * 86400);
+ else if (!work->for_sync) {
+ expire_time = jiffies - (dirtytime_expire_interval * HZ);
older_than_this = &expire_time;
}
while (!list_empty(delaying_queue)) {
*/
redirty_tail(inode, wb);
} else if (inode->i_state & I_DIRTY_TIME) {
+ inode->dirtied_when = jiffies;
list_move(&inode->i_wb_list, &wb->b_dirty_time);
} else {
/* The inode is clean. Remove from writeback lists. */
spin_lock(&inode->i_lock);
dirty = inode->i_state & I_DIRTY;
- if (((dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) &&
- (inode->i_state & I_DIRTY_TIME)) ||
- (inode->i_state & I_DIRTY_TIME_EXPIRED)) {
- dirty |= I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED;
- trace_writeback_lazytime(inode);
- }
+ if (inode->i_state & I_DIRTY_TIME) {
+ if ((dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) ||
+ unlikely(inode->i_state & I_DIRTY_TIME_EXPIRED) ||
+ unlikely(time_after(jiffies,
+ (inode->dirtied_time_when +
+ dirtytime_expire_interval * HZ)))) {
+ dirty |= I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED;
+ trace_writeback_lazytime(inode);
+ }
+ } else
+ inode->i_state &= ~I_DIRTY_TIME_EXPIRED;
inode->i_state &= ~dirty;
/*
rcu_read_unlock();
}
+/*
+ * Wake up bdi's periodically to make sure dirtytime inodes gets
+ * written back periodically. We deliberately do *not* check the
+ * b_dirtytime list in wb_has_dirty_io(), since this would cause the
+ * kernel to be constantly waking up once there are any dirtytime
+ * inodes on the system. So instead we define a separate delayed work
+ * function which gets called much more rarely. (By default, only
+ * once every 12 hours.)
+ *
+ * If there is any other write activity going on in the file system,
+ * this function won't be necessary. But if the only thing that has
+ * happened on the file system is a dirtytime inode caused by an atime
+ * update, we need this infrastructure below to make sure that inode
+ * eventually gets pushed out to disk.
+ */
+static void wakeup_dirtytime_writeback(struct work_struct *w);
+static DECLARE_DELAYED_WORK(dirtytime_work, wakeup_dirtytime_writeback);
+
+static void wakeup_dirtytime_writeback(struct work_struct *w)
+{
+ struct backing_dev_info *bdi;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) {
+ if (list_empty(&bdi->wb.b_dirty_time))
+ continue;
+ bdi_wakeup_thread(bdi);
+ }
+ rcu_read_unlock();
+ schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+}
+
+static int __init start_dirtytime_writeback(void)
+{
+ schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+ return 0;
+}
+__initcall(start_dirtytime_writeback);
+
+int dirtytime_interval_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int ret;
+
+ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+ if (ret == 0 && write)
+ mod_delayed_work(system_wq, &dirtytime_work, 0);
+ return ret;
+}
+
static noinline void block_dump___mark_inode_dirty(struct inode *inode)
{
if (inode->i_ino || strcmp(inode->i_sb->s_id, "bdev")) {
}
inode->dirtied_when = jiffies;
- list_move(&inode->i_wb_list, dirtytime ?
- &bdi->wb.b_dirty_time : &bdi->wb.b_dirty);
+ if (dirtytime)
+ inode->dirtied_time_when = jiffies;
+ if (inode->i_state & (I_DIRTY_INODE | I_DIRTY_PAGES))
+ list_move(&inode->i_wb_list, &bdi->wb.b_dirty);
+ else
+ list_move(&inode->i_wb_list,
+ &bdi->wb.b_dirty_time);
spin_unlock(&bdi->wb.list_lock);
trace_writeback_dirty_inode_enqueue(inode);
hfs_bnode_write(node, entry, data_off + key_len, entry_len);
hfs_bnode_dump(node);
- if (new_node) {
- /* update parent key if we inserted a key
- * at the start of the first node
- */
- if (!rec && new_node != node)
- hfs_brec_update_parent(fd);
+ /*
+ * update parent key if we inserted a key
+ * at the start of the node and it is not the new node
+ */
+ if (!rec && new_node != node) {
+ hfs_bnode_read_key(node, fd->search_key, data_off + size);
+ hfs_brec_update_parent(fd);
+ }
+ if (new_node) {
hfs_bnode_put(fd->bnode);
if (!new_node->parent) {
hfs_btree_inc_height(tree);
goto again;
}
- if (!rec)
- hfs_brec_update_parent(fd);
-
return 0;
}
if (IS_ERR(parent))
return PTR_ERR(parent);
__hfs_brec_find(parent, fd, hfs_find_rec_by_key);
+ if (fd->record < 0)
+ return -ENOENT;
hfs_bnode_dump(parent);
rec = fd->record;
int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
{
int error = 0;
- struct file_lock *new_fl;
struct file_lock_context *ctx = inode->i_flctx;
- struct file_lock *fl;
+ struct file_lock *new_fl, *fl, *tmp;
unsigned long break_time;
int want_write = (mode & O_ACCMODE) != O_RDONLY;
LIST_HEAD(dispose);
break_time++; /* so that 0 means no break time */
}
- list_for_each_entry(fl, &ctx->flc_lease, fl_list) {
+ list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, fl_list) {
if (!leases_conflict(fl, new_fl))
continue;
if (want_write) {
seg->offset = iomap.offset;
seg->length = iomap.length;
- dprintk("GET: %lld:%lld %d\n", bex->foff, bex->len, bex->es);
+ dprintk("GET: 0x%llx:0x%llx %d\n", bex->foff, bex->len, bex->es);
return 0;
out_error:
p = xdr_decode_hyper(p, &bex.foff);
if (bex.foff & (block_size - 1)) {
- dprintk("%s: unaligned offset %lld\n",
+ dprintk("%s: unaligned offset 0x%llx\n",
__func__, bex.foff);
goto fail;
}
p = xdr_decode_hyper(p, &bex.len);
if (bex.len & (block_size - 1)) {
- dprintk("%s: unaligned length %lld\n",
+ dprintk("%s: unaligned length 0x%llx\n",
__func__, bex.foff);
goto fail;
}
p = xdr_decode_hyper(p, &bex.soff);
if (bex.soff & (block_size - 1)) {
- dprintk("%s: unaligned disk offset %lld\n",
+ dprintk("%s: unaligned disk offset 0x%llx\n",
__func__, bex.soff);
goto fail;
}
{
struct super_block *sb = exp->ex_path.mnt->mnt_sb;
- if (exp->ex_flags & NFSEXP_NOPNFS)
+ if (!(exp->ex_flags & NFSEXP_PNFS))
return;
if (sb->s_export_op->get_uuid &&
list_move_tail(&lp->lo_perstate, reaplist);
return;
}
- end = seg->offset;
+ lo->offset = layout_end(seg);
} else {
/* retain the whole layout segment on a split. */
if (layout_end(seg) < end) {
dprintk("%s: split not supported\n", __func__);
return;
}
-
- lo->offset = layout_end(seg);
+ end = seg->offset;
}
layout_update_len(lo, end);
spin_lock(&clp->cl_lock);
list_for_each_entry_safe(ls, n, &clp->cl_lo_states, ls_perclnt) {
+ if (ls->ls_layout_type != lrp->lr_layout_type)
+ continue;
+
if (lrp->lr_return_type == RETURN_FSID &&
!fh_fsid_match(&ls->ls_stid.sc_file->fi_fhandle,
&cstate->current_fh.fh_handle))
rpc_ntop((struct sockaddr *)&clp->cl_addr, addr_str, sizeof(addr_str));
+ trace_layout_recall_fail(&ls->ls_stid.sc_stateid);
+
printk(KERN_WARNING
"nfsd: client %s failed to respond to layout recall. "
" Fencing..\n", addr_str);
nfserr = ops->proc_getdeviceinfo(exp->ex_path.mnt->mnt_sb, gdp);
gdp->gd_notify_types &= ops->notify_types;
- exp_put(exp);
out:
+ exp_put(exp);
return nfserr;
}
} else
nfs4_free_openowner(&oo->oo_owner);
spin_unlock(&clp->cl_lock);
- return oo;
+ return ret;
}
static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) {
} else
nfs4_free_lockowner(&lo->lo_owner);
spin_unlock(&clp->cl_lock);
- return lo;
+ return ret;
}
static void
p = xdr_decode_hyper(p, &lgp->lg_seg.offset);
p = xdr_decode_hyper(p, &lgp->lg_seg.length);
p = xdr_decode_hyper(p, &lgp->lg_minlength);
- nfsd4_decode_stateid(argp, &lgp->lg_sid);
+
+ status = nfsd4_decode_stateid(argp, &lgp->lg_sid);
+ if (status)
+ return status;
+
READ_BUF(4);
lgp->lg_maxcount = be32_to_cpup(p++);
p = xdr_decode_hyper(p, &lcp->lc_seg.offset);
p = xdr_decode_hyper(p, &lcp->lc_seg.length);
lcp->lc_reclaim = be32_to_cpup(p++);
- nfsd4_decode_stateid(argp, &lcp->lc_sid);
+
+ status = nfsd4_decode_stateid(argp, &lcp->lc_sid);
+ if (status)
+ return status;
+
READ_BUF(4);
lcp->lc_newoffset = be32_to_cpup(p++);
if (lcp->lc_newoffset) {
READ_BUF(16);
p = xdr_decode_hyper(p, &lrp->lr_seg.offset);
p = xdr_decode_hyper(p, &lrp->lr_seg.length);
- nfsd4_decode_stateid(argp, &lrp->lr_sid);
+
+ status = nfsd4_decode_stateid(argp, &lrp->lr_sid);
+ if (status)
+ return status;
+
READ_BUF(4);
lrp->lrf_body_len = be32_to_cpup(p++);
if (lrp->lrf_body_len > 0) {
return nfserr_resource;
*p++ = cpu_to_be32(lrp->lrs_present);
if (lrp->lrs_present)
- nfsd4_encode_stateid(xdr, &lrp->lr_sid);
+ return nfsd4_encode_stateid(xdr, &lrp->lr_sid);
return nfs_ok;
}
#endif /* CONFIG_NFSD_PNFS */
{
unsigned int hashsize;
unsigned int i;
+ int status = 0;
max_drc_entries = nfsd_cache_size_limit();
atomic_set(&num_drc_entries, 0);
hashsize = nfsd_hashsize(max_drc_entries);
maskbits = ilog2(hashsize);
- register_shrinker(&nfsd_reply_cache_shrinker);
+ status = register_shrinker(&nfsd_reply_cache_shrinker);
+ if (status)
+ return status;
+
drc_slab = kmem_cache_create("nfsd_drc", sizeof(struct svc_cacherep),
0, 0, NULL);
if (!drc_slab)
#ifdef CONFIG_BCMA_HOST_PCI
extern void bcma_host_pci_up(struct bcma_bus *bus);
extern void bcma_host_pci_down(struct bcma_bus *bus);
+extern int bcma_host_pci_irq_ctl(struct bcma_bus *bus,
+ struct bcma_device *core, bool enable);
#else
static inline void bcma_host_pci_up(struct bcma_bus *bus)
{
static inline void bcma_host_pci_down(struct bcma_bus *bus)
{
}
+static inline int bcma_host_pci_irq_ctl(struct bcma_bus *bus,
+ struct bcma_device *core, bool enable)
+{
+ if (bus->hosttype == BCMA_HOSTTYPE_PCI)
+ return -ENOTSUPP;
+ return 0;
+}
#endif
extern bool bcma_core_is_enabled(struct bcma_device *core);
#define pcicore_write16(pc, offset, val) bcma_write16((pc)->core, offset, val)
#define pcicore_write32(pc, offset, val) bcma_write32((pc)->core, offset, val)
-extern int bcma_core_pci_irq_ctl(struct bcma_bus *bus,
- struct bcma_device *core, bool enable);
+#ifdef CONFIG_BCMA_DRIVER_PCI
extern void bcma_core_pci_power_save(struct bcma_bus *bus, bool up);
+#else
+static inline void bcma_core_pci_power_save(struct bcma_bus *bus, bool up)
+{
+}
+#endif
extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev);
extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev);
struct mutex i_mutex;
unsigned long dirtied_when; /* jiffies of first dirtying */
+ unsigned long dirtied_time_when;
struct hlist_node i_hash;
struct list_head i_wb_list; /* backing dev IO list */
#define GICR_PROPBASER_WaWb (5U << 7)
#define GICR_PROPBASER_RaWaWt (6U << 7)
#define GICR_PROPBASER_RaWaWb (7U << 7)
+#define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
#define GICR_PROPBASER_IDBITS_MASK (0x1f)
+#define GICR_PENDBASER_NonShareable (0U << 10)
+#define GICR_PENDBASER_InnerShareable (1U << 10)
+#define GICR_PENDBASER_OuterShareable (2U << 10)
+#define GICR_PENDBASER_SHAREABILITY_MASK (3UL << 10)
+#define GICR_PENDBASER_nCnB (0U << 7)
+#define GICR_PENDBASER_nC (1U << 7)
+#define GICR_PENDBASER_RaWt (2U << 7)
+#define GICR_PENDBASER_RaWb (3U << 7)
+#define GICR_PENDBASER_WaWt (4U << 7)
+#define GICR_PENDBASER_WaWb (5U << 7)
+#define GICR_PENDBASER_RaWaWt (6U << 7)
+#define GICR_PENDBASER_RaWaWb (7U << 7)
+#define GICR_PENDBASER_CACHEABILITY_MASK (7U << 7)
+
/*
* Re-Distributor registers, offsets from SGI_base
*/
#define GITS_CBASER_WaWb (5UL << 59)
#define GITS_CBASER_RaWaWt (6UL << 59)
#define GITS_CBASER_RaWaWb (7UL << 59)
+#define GITS_CBASER_CACHEABILITY_MASK (7UL << 59)
#define GITS_CBASER_NonShareable (0UL << 10)
#define GITS_CBASER_InnerShareable (1UL << 10)
#define GITS_CBASER_OuterShareable (2UL << 10)
#define GITS_BASER_WaWb (5UL << 59)
#define GITS_BASER_RaWaWt (6UL << 59)
#define GITS_BASER_RaWaWb (7UL << 59)
+#define GITS_BASER_CACHEABILITY_MASK (7UL << 59)
#define GITS_BASER_TYPE_SHIFT (56)
#define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7)
#define GITS_BASER_ENTRY_SIZE_SHIFT (48)
}
-/* jhash_3words - hash exactly 3, 2 or 1 word(s) */
-static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
+/* __jhash_nwords - hash exactly 3, 2 or 1 word(s) */
+static inline u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval)
{
- a += JHASH_INITVAL;
- b += JHASH_INITVAL;
+ a += initval;
+ b += initval;
c += initval;
__jhash_final(a, b, c);
return c;
}
+static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
+{
+ return __jhash_nwords(a, b, c, initval + JHASH_INITVAL + (3 << 2));
+}
+
static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
{
- return jhash_3words(a, b, 0, initval);
+ return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2));
}
static inline u32 jhash_1word(u32 a, u32 initval)
{
- return jhash_3words(a, 0, 0, initval);
+ return __jhash_nwords(a, 0, 0, initval + JHASH_INITVAL + (1 << 2));
}
#endif /* _LINUX_JHASH_H */
#include <linux/compiler.h>
unsigned long lcm(unsigned long a, unsigned long b) __attribute_const__;
+unsigned long lcm_not_zero(unsigned long a, unsigned long b) __attribute_const__;
#endif /* _LCM_H */
* led */
ATA_FLAG_NO_DIPM = (1 << 23), /* host not happy with DIPM */
ATA_FLAG_LOWTAG = (1 << 24), /* host wants lowest available tag */
+ ATA_FLAG_SAS_HOST = (1 << 25), /* SAS host */
/* bits 24:31 of ap->flags are reserved for LLD specific flags */
#define PALMAS_GPADC_TRIM15 0x0E
#define PALMAS_GPADC_TRIM16 0x0F
+/* TPS659038 regen2_ctrl offset iss different from palmas */
+#define TPS659038_REGEN2_CTRL 0x12
+
/* TPS65917 Interrupt registers */
/* Registers for function INTERRUPT */
MLX4_CMD_UNMAP_ICM_AUX = 0xffb,
MLX4_CMD_SET_ICM_SIZE = 0xffd,
MLX4_CMD_ACCESS_REG = 0x3b,
+ MLX4_CMD_ALLOCATE_VPP = 0x80,
+ MLX4_CMD_SET_VPORT_QOS = 0x81,
/*master notify fw on finish for slave's flr*/
MLX4_CMD_INFORM_FLR_DONE = 0x5b,
};
enum {
- /* set port opcode modifiers */
+ /* Set port opcode modifiers */
+ MLX4_SET_PORT_IB_OPCODE = 0x0,
+ MLX4_SET_PORT_ETH_OPCODE = 0x1,
+ MLX4_SET_PORT_BEACON_OPCODE = 0x4,
+};
+
+enum {
+ /* Set port Ethernet input modifiers */
MLX4_SET_PORT_GENERAL = 0x0,
MLX4_SET_PORT_RQP_CALC = 0x1,
MLX4_SET_PORT_MAC_TABLE = 0x2,
u32 mlx4_comm_get_version(void);
int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac);
int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos);
+int mlx4_set_vf_rate(struct mlx4_dev *dev, int port, int vf, int min_tx_rate,
+ int max_tx_rate);
int mlx4_set_vf_spoofchk(struct mlx4_dev *dev, int port, int vf, bool setting);
int mlx4_get_vf_config(struct mlx4_dev *dev, int port, int vf, struct ifla_vf_info *ivf);
int mlx4_set_vf_link_state(struct mlx4_dev *dev, int port, int vf, int link_state);
#define MSIX_LEGACY_SZ 4
#define MIN_MSIX_P_PORT 5
-#define MLX4_NUM_UP 8
-#define MLX4_NUM_TC 8
#define MLX4_MAX_100M_UNITS_VAL 255 /*
* work around: can't set values
* greater then this value when
MLX4_DEV_CAP_FLAG_VEP_UC_STEER = 1LL << 41,
MLX4_DEV_CAP_FLAG_VEP_MC_STEER = 1LL << 42,
MLX4_DEV_CAP_FLAG_COUNTERS = 1LL << 48,
+ MLX4_DEV_CAP_FLAG_RSS_IP_FRAG = 1LL << 52,
MLX4_DEV_CAP_FLAG_SET_ETH_SCHED = 1LL << 53,
MLX4_DEV_CAP_FLAG_SENSE_SUPPORT = 1LL << 55,
MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV = 1LL << 59,
MLX4_DEV_CAP_FLAG2_PORT_REMAP = 1LL << 21,
MLX4_DEV_CAP_FLAG2_QCN = 1LL << 22,
MLX4_DEV_CAP_FLAG2_QP_RATE_LIMIT = 1LL << 23,
- MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN = 1LL << 24
+ MLX4_DEV_CAP_FLAG2_FLOWSTATS_EN = 1LL << 24,
+ MLX4_DEV_CAP_FLAG2_QOS_VPP = 1LL << 25,
+ MLX4_DEV_CAP_FLAG2_ETS_CFG = 1LL << 26,
+ MLX4_DEV_CAP_FLAG2_PORT_BEACON = 1LL << 27,
+ MLX4_DEV_CAP_FLAG2_IGNORE_FCS = 1LL << 28,
};
enum {
return dev->flags & MLX4_FLAG_SLAVE;
}
+static inline int mlx4_is_eth(struct mlx4_dev *dev, int port)
+{
+ return dev->caps.port_type[port] == MLX4_PORT_TYPE_IB ? 0 : 1;
+}
+
int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct,
struct mlx4_buf *buf, gfp_t gfp);
void mlx4_buf_free(struct mlx4_dev *dev, int size, struct mlx4_buf *buf);
u8 pptx, u8 pfctx, u8 pprx, u8 pfcrx);
int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
u8 promisc);
-int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc);
-int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw,
- u8 *pg, u16 *ratelimit);
+int mlx4_SET_PORT_BEACON(struct mlx4_dev *dev, u8 port, u16 time);
+int mlx4_SET_PORT_fcs_check(struct mlx4_dev *dev, u8 port,
+ u8 ignore_fcs_value);
int mlx4_SET_PORT_VXLAN(struct mlx4_dev *dev, u8 port, u8 steering, int enable);
int mlx4_find_cached_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *idx);
int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx);
__be16 sq_wqe_counter;
u32 reserved3;
__be16 rate_limit_params;
- __be16 reserved4;
+ u8 reserved4;
+ u8 qos_vport;
__be32 param3;
__be32 nummmcpeers_basemkey;
u8 log_page_size;
enum {
MLX4_UPD_QP_MASK_PM_STATE = 32,
MLX4_UPD_QP_MASK_VSD = 33,
+ MLX4_UPD_QP_MASK_QOS_VPP = 34,
MLX4_UPD_QP_MASK_RATE_LIMIT = 35,
};
MLX4_UPDATE_QP_SMAC = 1 << 0,
MLX4_UPDATE_QP_VSD = 1 << 1,
MLX4_UPDATE_QP_RATE_LIMIT = 1 << 2,
- MLX4_UPDATE_QP_SUPPORTED_ATTRS = (1 << 3) - 1
+ MLX4_UPDATE_QP_QOS_VPORT = 1 << 3,
+ MLX4_UPDATE_QP_SUPPORTED_ATTRS = (1 << 4) - 1
};
enum mlx4_update_qp_params_flags {
struct mlx4_update_qp_params {
u8 smac_index;
+ u8 qos_vport;
u32 flags;
u16 rate_unit;
u16 rate_val;
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
static inline void mlx5_cq_arm(struct mlx5_core_cq *cq, u32 cmd,
void __iomem *uar_page,
- spinlock_t *doorbell_lock)
+ spinlock_t *doorbell_lock,
+ u32 cons_index)
{
__be32 doorbell[2];
u32 sn;
u32 ci;
sn = cq->arm_sn & 3;
- ci = cq->cons_index & 0xffffff;
+ ci = cons_index & 0xffffff;
*cq->arm_db = cpu_to_be32(sn << 28 | cmd | ci);
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
};
struct mlx5_cmd {
+ void *cmd_alloc_buf;
+ dma_addr_t alloc_dma;
+ int alloc_size;
void *cmd_buf;
dma_addr_t dma;
u16 cmdif_rev;
struct mlx5_eq_table {
void __iomem *update_ci;
void __iomem *update_arm_ci;
- struct list_head *comp_eq_head;
+ struct list_head comp_eqs_list;
struct mlx5_eq pages_eq;
struct mlx5_eq async_eq;
struct mlx5_eq cmd_eq;
int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq);
int mlx5_start_eqs(struct mlx5_core_dev *dev);
int mlx5_stop_eqs(struct mlx5_core_dev *dev);
+int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn, int *irqn);
int mlx5_core_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn);
int mlx5_core_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn);
MAX_MR_CACHE_ENTRIES = 16,
};
+enum {
+ MLX5_INTERFACE_PROTOCOL_IB = 0,
+ MLX5_INTERFACE_PROTOCOL_ETH = 1,
+};
+
struct mlx5_interface {
void * (*add)(struct mlx5_core_dev *dev);
void (*remove)(struct mlx5_core_dev *dev, void *context);
void (*event)(struct mlx5_core_dev *dev, void *context,
enum mlx5_dev_event event, unsigned long param);
+ void * (*get_dev)(void *context);
+ int protocol;
struct list_head list;
};
+void *mlx5_get_protocol_dev(struct mlx5_core_dev *mdev, int protocol);
int mlx5_register_interface(struct mlx5_interface *intf);
void mlx5_unregister_interface(struct mlx5_interface *intf);
/*
- * Copyright (c) 2014, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
/*
- * Copyright (c) 2013, Mellanox Technologies inc. All rights reserved.
+ * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
#define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d
#define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335
#define SDIO_DEVICE_ID_BROADCOM_43362 0xa962
+#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6
+#define SDIO_DEVICE_ID_BROADCOM_4345 0x4345
#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
#define SDIO_VENDOR_ID_INTEL 0x0089
* netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
* struct net_device *dev);
* Called when a packet needs to be transmitted.
- * Must return NETDEV_TX_OK , NETDEV_TX_BUSY.
+ * Returns NETDEV_TX_OK. Can return NETDEV_TX_BUSY, but you should stop
+ * the queue before that can happen; it's for obsolete devices and weird
+ * corner cases, but the stack really does a non-trivial amount
+ * of useless work if you return NETDEV_TX_BUSY.
* (can also return NETDEV_TX_LOCKED iff NETIF_F_LLTX)
* Required can not be NULL.
*
* int queue_index, u32 maxrate);
* Called when a user wants to set a max-rate limitation of specific
* TX queue.
+ * int (*ndo_get_iflink)(const struct net_device *dev);
+ * Called to get the iflink value of this device.
*/
struct net_device_ops {
int (*ndo_init)(struct net_device *dev);
int (*ndo_set_tx_maxrate)(struct net_device *dev,
int queue_index,
u32 maxrate);
+ int (*ndo_get_iflink)(const struct net_device *dev);
};
/**
* @mpls_features: Mask of features inheritable by MPLS
*
* @ifindex: interface index
- * @iflink: unique device identifier
+ * @group: The group, that the device belongs to
*
* @stats: Statistics struct, which was left as a legacy, use
* rtnl_link_stats64 instead
*
* @qdisc_tx_busylock: XXX: need comments on this one
*
- * @group: The group, that the device belongs to
* @pm_qos_req: Power Management QoS object
*
* FIXME: cleanup struct net_device such that network protocol info
netdev_features_t mpls_features;
int ifindex;
- int iflink;
+ int group;
struct net_device_stats stats;
#endif
struct phy_device *phydev;
struct lock_class_key *qdisc_tx_busylock;
- int group;
struct pm_qos_request pm_qos_req;
};
#define to_net_dev(d) container_of(d, struct net_device, dev)
void dev_add_offload(struct packet_offload *po);
void dev_remove_offload(struct packet_offload *po);
+int dev_get_iflink(const struct net_device *dev);
struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags,
unsigned short mask);
struct net_device *dev_get_by_name(struct net *net, const char *name);
int dev_close(struct net_device *dev);
int dev_close_many(struct list_head *head, bool unlink);
void dev_disable_lro(struct net_device *dev);
-int dev_loopback_xmit(struct sk_buff *newskb);
-int dev_queue_xmit(struct sk_buff *skb);
+int dev_loopback_xmit(struct sock *sk, struct sk_buff *newskb);
+int dev_queue_xmit_sk(struct sock *sk, struct sk_buff *skb);
+static inline int dev_queue_xmit(struct sk_buff *skb)
+{
+ return dev_queue_xmit_sk(skb->sk, skb);
+}
int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv);
int register_netdevice(struct net_device *dev);
void unregister_netdevice_queue(struct net_device *dev, struct list_head *head);
void synchronize_net(void);
int init_dummy_netdev(struct net_device *dev);
+DECLARE_PER_CPU(int, xmit_recursion);
+static inline int dev_recursion_level(void)
+{
+ return this_cpu_read(xmit_recursion);
+}
+
struct net_device *dev_get_by_index(struct net *net, int ifindex);
struct net_device *__dev_get_by_index(struct net *net, int ifindex);
struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
int netif_rx(struct sk_buff *skb);
int netif_rx_ni(struct sk_buff *skb);
-int netif_receive_skb(struct sk_buff *skb);
+int netif_receive_skb_sk(struct sock *sk, struct sk_buff *skb);
+static inline int netif_receive_skb(struct sk_buff *skb)
+{
+ return netif_receive_skb_sk(skb->sk, skb);
+}
gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb);
void napi_gro_flush(struct napi_struct *napi, bool flush_old);
struct sk_buff *napi_get_frags(struct napi_struct *napi);
struct sk_buff;
struct nf_hook_ops;
+
+struct sock;
+
+struct nf_hook_state {
+ unsigned int hook;
+ int thresh;
+ u_int8_t pf;
+ struct net_device *in;
+ struct net_device *out;
+ struct sock *sk;
+ int (*okfn)(struct sock *, struct sk_buff *);
+};
+
+static inline void nf_hook_state_init(struct nf_hook_state *p,
+ unsigned int hook,
+ int thresh, u_int8_t pf,
+ struct net_device *indev,
+ struct net_device *outdev,
+ struct sock *sk,
+ int (*okfn)(struct sock *, struct sk_buff *))
+{
+ p->hook = hook;
+ p->thresh = thresh;
+ p->pf = pf;
+ p->in = indev;
+ p->out = outdev;
+ p->sk = sk;
+ p->okfn = okfn;
+}
+
typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *));
+ const struct nf_hook_state *state);
struct nf_hook_ops {
struct list_head list;
}
#endif
-int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
- struct net_device *indev, struct net_device *outdev,
- int (*okfn)(struct sk_buff *), int thresh);
+int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state);
/**
* nf_hook_thresh - call a netfilter hook
* value indicates the packet has been consumed by the hook.
*/
static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
+ struct sock *sk,
struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
- int (*okfn)(struct sk_buff *), int thresh)
+ int (*okfn)(struct sock *, struct sk_buff *),
+ int thresh)
{
- if (nf_hooks_active(pf, hook))
- return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);
+ if (nf_hooks_active(pf, hook)) {
+ struct nf_hook_state state;
+
+ nf_hook_state_init(&state, hook, thresh, pf,
+ indev, outdev, sk, okfn);
+ return nf_hook_slow(skb, &state);
+ }
return 1;
}
-static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
- struct net_device *indev, struct net_device *outdev,
- int (*okfn)(struct sk_buff *))
+static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sock *sk,
+ struct sk_buff *skb, struct net_device *indev,
+ struct net_device *outdev,
+ int (*okfn)(struct sock *, struct sk_buff *))
{
- return nf_hook_thresh(pf, hook, skb, indev, outdev, okfn, INT_MIN);
+ return nf_hook_thresh(pf, hook, sk, skb, indev, outdev, okfn, INT_MIN);
}
/* Activate hook; either okfn or kfree_skb called, unless a hook
*/
static inline int
-NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sk_buff *skb,
- struct net_device *in, struct net_device *out,
- int (*okfn)(struct sk_buff *), int thresh)
+NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sock *sk,
+ struct sk_buff *skb, struct net_device *in,
+ struct net_device *out,
+ int (*okfn)(struct sock *, struct sk_buff *), int thresh)
{
- int ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, thresh);
+ int ret = nf_hook_thresh(pf, hook, sk, skb, in, out, okfn, thresh);
if (ret == 1)
- ret = okfn(skb);
+ ret = okfn(sk, skb);
return ret;
}
static inline int
-NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sk_buff *skb,
- struct net_device *in, struct net_device *out,
- int (*okfn)(struct sk_buff *), bool cond)
+NF_HOOK_COND(uint8_t pf, unsigned int hook, struct sock *sk,
+ struct sk_buff *skb, struct net_device *in, struct net_device *out,
+ int (*okfn)(struct sock *, struct sk_buff *), bool cond)
{
int ret;
if (!cond ||
- ((ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, INT_MIN)) == 1))
- ret = okfn(skb);
+ ((ret = nf_hook_thresh(pf, hook, sk, skb, in, out, okfn, INT_MIN)) == 1))
+ ret = okfn(sk, skb);
return ret;
}
static inline int
-NF_HOOK(uint8_t pf, unsigned int hook, struct sk_buff *skb,
+NF_HOOK(uint8_t pf, unsigned int hook, struct sock *sk, struct sk_buff *skb,
struct net_device *in, struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ int (*okfn)(struct sock *, struct sk_buff *))
{
- return NF_HOOK_THRESH(pf, hook, skb, in, out, okfn, INT_MIN);
+ return NF_HOOK_THRESH(pf, hook, sk, skb, in, out, okfn, INT_MIN);
}
/* Call setsockopt() */
}
#else /* !CONFIG_NETFILTER */
-#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
-#define NF_HOOK_COND(pf, hook, skb, indev, outdev, okfn, cond) (okfn)(skb)
+#define NF_HOOK(pf, hook, sk, skb, indev, outdev, okfn) (okfn)(sk, skb)
+#define NF_HOOK_COND(pf, hook, sk, skb, indev, outdev, okfn, cond) (okfn)(sk, skb)
static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
+ struct sock *sk,
struct sk_buff *skb,
struct net_device *indev,
struct net_device *outdev,
- int (*okfn)(struct sk_buff *), int thresh)
+ int (*okfn)(struct sock *sk, struct sk_buff *), int thresh)
{
- return okfn(skb);
+ return okfn(sk, skb);
}
-static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
- struct net_device *indev, struct net_device *outdev,
- int (*okfn)(struct sk_buff *))
+static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sock *sk,
+ struct sk_buff *skb, struct net_device *indev,
+ struct net_device *outdev,
+ int (*okfn)(struct sock *, struct sk_buff *))
{
return 1;
}
extern void arpt_unregister_table(struct xt_table *table);
extern unsigned int arpt_do_table(struct sk_buff *skb,
unsigned int hook,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct xt_table *table);
#ifdef CONFIG_COMPAT
#define __LINUX_BRIDGE_NETFILTER_H
#include <uapi/linux/netfilter_bridge.h>
-
+#include <linux/skbuff.h>
enum nf_br_hook_priorities {
NF_BR_PRI_FIRST = INT_MIN,
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
-#define BRNF_PKT_TYPE 0x01
#define BRNF_BRIDGED_DNAT 0x02
#define BRNF_NF_BRIDGE_PREROUTING 0x08
-#define BRNF_8021Q 0x10
-#define BRNF_PPPoE 0x20
static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb)
{
- if (unlikely(skb->nf_bridge->mask & BRNF_PPPoE))
+ if (skb->nf_bridge->orig_proto == BRNF_PROTO_PPPOE)
return PPPOE_SES_HLEN;
return 0;
}
-int br_handle_frame_finish(struct sk_buff *skb);
+int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb);
static inline void br_drop_fake_rtable(struct sk_buff *skb)
{
skb_dst_drop(skb);
}
+static inline int nf_bridge_get_physinif(const struct sk_buff *skb)
+{
+ return skb->nf_bridge ? skb->nf_bridge->physindev->ifindex : 0;
+}
+
+static inline int nf_bridge_get_physoutif(const struct sk_buff *skb)
+{
+ return skb->nf_bridge ? skb->nf_bridge->physoutdev->ifindex : 0;
+}
+
+static inline struct net_device *
+nf_bridge_get_physindev(const struct sk_buff *skb)
+{
+ return skb->nf_bridge ? skb->nf_bridge->physindev : NULL;
+}
+
+static inline struct net_device *
+nf_bridge_get_physoutdev(const struct sk_buff *skb)
+{
+ return skb->nf_bridge ? skb->nf_bridge->physoutdev : NULL;
+}
#else
#define br_drop_fake_rtable(skb) do { } while (0)
#endif /* CONFIG_BRIDGE_NETFILTER */
extern void *ipt_alloc_initial_table(const struct xt_table *);
extern unsigned int ipt_do_table(struct sk_buff *skb,
unsigned int hook,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct xt_table *table);
#ifdef CONFIG_COMPAT
extern void ip6t_unregister_table(struct net *net, struct xt_table *table);
extern unsigned int ip6t_do_table(struct sk_buff *skb,
unsigned int hook,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct xt_table *table);
/* Check for an extension */
extern int fixed_phy_set_link_update(struct phy_device *phydev,
int (*link_update)(struct net_device *,
struct fixed_phy_status *));
+extern int fixed_phy_update_state(struct phy_device *phydev,
+ const struct fixed_phy_status *status,
+ const struct fixed_phy_status *changed);
#else
static inline int fixed_phy_add(unsigned int irq, int phy_id,
struct fixed_phy_status *status)
{
return -ENODEV;
}
+static inline int fixed_phy_update_state(struct phy_device *phydev,
+ const struct fixed_phy_status *status,
+ const struct fixed_phy_status *changed)
+{
+ return -ENODEV;
+}
#endif /* CONFIG_FIXED_PHY */
#endif /* __PHY_FIXED_H */
--- /dev/null
+/*
+ * Generic platform data for the NXP NCI NFC chips.
+ *
+ * Copyright (C) 2014 NXP Semiconductors All rights reserved.
+ *
+ * Authors: Clément Perrochaud <clement.perrochaud@nxp.com>
+ *
+ * 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 that 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.
+ */
+
+#ifndef _NXP_NCI_H_
+#define _NXP_NCI_H_
+
+struct nxp_nci_nfc_platform_data {
+ unsigned int gpio_en;
+ unsigned int gpio_fw;
+ unsigned int irq;
+};
+
+#endif /* _NXP_NCI_H_ */
* @driver_data: private regulator data
* @of_node: OpenFirmware node to parse for device tree bindings (may be
* NULL).
- * @regmap: regmap to use for core regmap helpers if dev_get_regulator() is
+ * @regmap: regmap to use for core regmap helpers if dev_get_regmap() is
* insufficient.
* @ena_gpio_initialized: GPIO controlling regulator enable was properly
* initialized, meaning that >= 0 is a valid gpio
/*
* numa_faults_locality tracks if faults recorded during the last
- * scan window were remote/local. The task scan period is adapted
- * based on the locality of the faults with different weights
- * depending on whether they were shared or private faults
+ * scan window were remote/local or failed to migrate. The task scan
+ * period is adapted based on the locality of the faults with different
+ * weights depending on whether they were shared or private faults
*/
- unsigned long numa_faults_locality[2];
+ unsigned long numa_faults_locality[3];
unsigned long numa_pages_migrated;
#endif /* CONFIG_NUMA_BALANCING */
#define TNF_NO_GROUP 0x02
#define TNF_SHARED 0x04
#define TNF_FAULT_LOCAL 0x08
+#define TNF_MIGRATE_FAIL 0x10
#ifdef CONFIG_NUMA_BALANCING
extern void task_numa_fault(int last_node, int node, int pages, int flags);
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
struct nf_bridge_info {
atomic_t use;
+ enum {
+ BRNF_PROTO_UNCHANGED,
+ BRNF_PROTO_8021Q,
+ BRNF_PROTO_PPPOE
+ } orig_proto;
+ bool pkt_otherhost;
unsigned int mask;
struct net_device *physindev;
struct net_device *physoutdev;
- unsigned long data[32 / sizeof(unsigned long)];
+ char neigh_header[8];
};
#endif
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
void rpc_register_sysctl(void);
void rpc_unregister_sysctl(void);
-int sunrpc_debugfs_init(void);
+void sunrpc_debugfs_init(void);
void sunrpc_debugfs_exit(void);
-int rpc_clnt_debugfs_register(struct rpc_clnt *);
+void rpc_clnt_debugfs_register(struct rpc_clnt *);
void rpc_clnt_debugfs_unregister(struct rpc_clnt *);
-int rpc_xprt_debugfs_register(struct rpc_xprt *);
+void rpc_xprt_debugfs_register(struct rpc_xprt *);
void rpc_xprt_debugfs_unregister(struct rpc_xprt *);
#else
-static inline int
+static inline void
sunrpc_debugfs_init(void)
{
- return 0;
+ return;
}
static inline void
return;
}
-static inline int
+static inline void
rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
{
- return 0;
+ return;
}
static inline void
return;
}
-static inline int
+static inline void
rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
{
- return 0;
+ return;
}
static inline void
struct tcp_fastopen_cookie {
s8 len;
u8 val[TCP_FASTOPEN_COOKIE_MAX];
+ bool exp; /* In RFC6994 experimental option format */
};
/* This defines a selective acknowledgement block. */
u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */
syn_data:1, /* SYN includes data */
syn_fastopen:1, /* SYN includes Fast Open option */
+ syn_fastopen_exp:1,/* SYN includes Fast Open exp. option */
syn_data_acked:1,/* data in SYN is acked by SYN-ACK */
is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */
u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */
struct urb *urb;
struct usbnet *dev;
enum skb_state state;
- size_t length;
+ long length;
unsigned long packets;
};
* tx_fixup method before returning an skb.
*/
static inline void
-usbnet_set_skb_tx_stats(struct sk_buff *skb, unsigned long packets)
+usbnet_set_skb_tx_stats(struct sk_buff *skb,
+ unsigned long packets, long bytes_delta)
{
struct skb_data *entry = (struct skb_data *) skb->cb;
entry->packets = packets;
+ entry->length = bytes_delta;
}
extern int usbnet_open(struct net_device *net);
extern unsigned long vm_dirty_bytes;
extern unsigned int dirty_writeback_interval;
extern unsigned int dirty_expire_interval;
+extern unsigned int dirtytime_expire_interval;
extern int vm_highmem_is_dirtyable;
extern int block_dump;
extern int laptop_mode;
extern int dirty_bytes_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos);
+int dirtytime_interval_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos);
struct ctl_table;
int dirty_writeback_centisecs_handler(struct ctl_table *, int,
__u16 reqseq;
__u16 txseq;
__u8 retries;
+ __le16 psm;
+ bdaddr_t bdaddr;
+ struct l2cap_chan *chan;
};
struct hci_dev;
typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
+typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status,
+ u16 opcode, struct sk_buff *skb);
+
+struct req_ctrl {
+ bool start;
+ u8 event;
+ hci_req_complete_t complete;
+ hci_req_complete_skb_t complete_skb;
+};
struct bt_skb_cb {
__u8 pkt_type;
__u16 opcode;
__u16 expect;
__u8 incoming:1;
- __u8 req_start:1;
- u8 req_event;
- hci_req_complete_t req_complete;
- struct l2cap_chan *chan;
- struct l2cap_ctrl control;
- bdaddr_t bdaddr;
- __le16 psm;
+ union {
+ struct l2cap_ctrl l2cap;
+ struct req_ctrl req;
+ };
};
#define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
/* LE features */
#define HCI_LE_ENCRYPTION 0x01
#define HCI_LE_CONN_PARAM_REQ_PROC 0x02
+#define HCI_LE_SLAVE_FEATURES 0x08
#define HCI_LE_PING 0x10
#define HCI_LE_DATA_LEN_EXT 0x20
#define HCI_LE_EXT_SCAN_POLICY 0x80
#define EIR_NAME_COMPLETE 0x09 /* complete local name */
#define EIR_TX_POWER 0x0A /* transmit power level */
#define EIR_CLASS_OF_DEV 0x0D /* Class of Device */
-#define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */
-#define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */
+#define EIR_SSP_HASH_C192 0x0E /* Simple Pairing Hash C-192 */
+#define EIR_SSP_RAND_R192 0x0F /* Simple Pairing Randomizer R-192 */
#define EIR_DEVICE_ID 0x10 /* device ID */
#define EIR_APPEARANCE 0x19 /* Device appearance */
#define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */
#define EIR_LE_ROLE 0x1C /* LE role */
+#define EIR_SSP_HASH_C256 0x1D /* Simple Pairing Hash C-256 */
+#define EIR_SSP_RAND_R256 0x1E /* Simple Pairing Rand R-256 */
#define EIR_LE_SC_CONFIRM 0x22 /* LE SC Confirmation Value */
#define EIR_LE_SC_RANDOM 0x23 /* LE SC Random Value */
__le16 max_ce_len;
} __packed;
+#define HCI_OP_LE_READ_REMOTE_FEATURES 0x2016
+struct hci_cp_le_read_remote_features {
+ __le16 handle;
+} __packed;
+
#define HCI_OP_LE_START_ENC 0x2019
struct hci_cp_le_start_enc {
__le16 handle;
__le16 supervision_timeout;
} __packed;
+#define HCI_EV_LE_REMOTE_FEAT_COMPLETE 0x04
+struct hci_ev_le_remote_feat_complete {
+ __u8 status;
+ __le16 handle;
+ __u8 features[8];
+} __packed;
+
#define HCI_EV_LE_LTK_REQ 0x05
struct hci_ev_le_ltk_req {
__le16 handle;
#define HCI_MAX_PAGES 3
-#define NUM_REASSEMBLY 4
struct hci_dev {
struct list_head list;
struct mutex lock;
struct sk_buff_head raw_q;
struct sk_buff_head cmd_q;
- struct sk_buff *recv_evt;
struct sk_buff *sent_cmd;
- struct sk_buff *reassembly[NUM_REASSEMBLY];
struct mutex req_lock;
wait_queue_head_t req_wait_q;
__u32 req_status;
__u32 req_result;
+ struct sk_buff *req_skb;
void *smp_data;
void *smp_bredr_data;
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
-int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count);
void hci_init_sysfs(struct hci_dev *hdev);
void hci_conn_init_sysfs(struct hci_conn *conn);
int hci_register_cb(struct hci_cb *hcb);
int hci_unregister_cb(struct hci_cb *hcb);
-bool hci_req_pending(struct hci_dev *hdev);
-
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param, u32 timeout);
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
u8 status);
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
-void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
- u8 *rand192, u8 *hash256, u8 *rand256,
- u8 status);
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
void dn_neigh_init(void);
void dn_neigh_cleanup(void);
-int dn_neigh_router_hello(struct sk_buff *skb);
-int dn_neigh_endnode_hello(struct sk_buff *skb);
+int dn_neigh_router_hello(struct sock *sk, struct sk_buff *skb);
+int dn_neigh_endnode_hello(struct sock *sk, struct sk_buff *skb);
void dn_neigh_pointopoint_hello(struct sk_buff *skb);
int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n);
-int dn_to_neigh_output(struct sk_buff *skb);
+int dn_to_neigh_output(struct sock *sk, struct sk_buff *skb);
extern struct neigh_table dn_neigh_table;
int ip_mr_input(struct sk_buff *skb);
int ip_output(struct sock *sk, struct sk_buff *skb);
int ip_mc_output(struct sock *sk, struct sk_buff *skb);
-int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
+int ip_fragment(struct sock *sk, struct sk_buff *skb,
+ int (*output)(struct sock *, struct sk_buff *));
int ip_do_nat(struct sk_buff *skb);
void ip_send_check(struct iphdr *ip);
int __ip_local_out(struct sk_buff *skb);
#endif
-static inline int sk_mc_loop(struct sock *sk)
-{
- if (!sk)
- return 1;
- switch (sk->sk_family) {
- case AF_INET:
- return inet_sk(sk)->mc_loop;
-#if IS_ENABLED(CONFIG_IPV6)
- case AF_INET6:
- return inet6_sk(sk)->mc_loop;
-#endif
- }
- WARN_ON(1);
- return 1;
-}
-
bool ip_call_ra_chain(struct sk_buff *skb);
/*
return rt->rt6i_flags & RTF_ANYCAST;
}
-int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
+int ip6_fragment(struct sock *sk, struct sk_buff *skb,
+ int (*output)(struct sock *, struct sk_buff *));
static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
{
- struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
+ struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
+ inet6_sk(skb->sk) : NULL;
return (np && np->pmtudisc >= IPV6_PMTUDISC_PROBE) ?
skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
__u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,
const struct in6_addr *raddr);
struct net *ip6_tnl_get_link_net(const struct net_device *dev);
+int ip6_tnl_get_iflink(const struct net_device *dev);
-static inline void ip6tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,
+ struct net_device *dev)
{
struct net_device_stats *stats = &dev->stats;
int pkt_len, err;
pkt_len = skb->len;
- err = ip6_local_out(skb);
+ err = ip6_local_out_sk(sk, skb);
if (net_xmit_eval(err) == 0) {
struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
void ip_tunnel_uninit(struct net_device *dev);
void ip_tunnel_dellink(struct net_device *dev, struct list_head *head);
struct net *ip_tunnel_get_link_net(const struct net_device *dev);
+int ip_tunnel_get_iflink(const struct net_device *dev);
int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id,
struct rtnl_link_ops *ops, char *devname);
#define NEXTHDR_MAX 255
-
-
#define IPV6_DEFAULT_HOPLIMIT 64
#define IPV6_DEFAULT_MCASTHOPS 1
int ipv6_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev);
-int ip6_rcv_finish(struct sk_buff *skb);
+int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb);
/*
* upper-layer output functions
int ip6_mc_input(struct sk_buff *skb);
int __ip6_local_out(struct sk_buff *skb);
+int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb);
int ip6_local_out(struct sk_buff *skb);
/*
int (*set_hw_addr_filt)(struct ieee802154_hw *hw,
struct ieee802154_hw_addr_filt *filt,
unsigned long changed);
- int (*set_txpower)(struct ieee802154_hw *hw, int db);
+ int (*set_txpower)(struct ieee802154_hw *hw, s8 dbm);
int (*set_lbt)(struct ieee802154_hw *hw, bool on);
int (*set_cca_mode)(struct ieee802154_hw *hw,
const struct wpan_phy_cca *cca);
__put_unaligned_memmove64(swab64p(le64_src), be64_dst);
}
-/* Basic interface to register ieee802154 hwice */
+/* Basic interface to register ieee802154 device */
struct ieee802154_hw *
ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops);
void ieee802154_free_hw(struct ieee802154_hw *hw);
unsigned int hooknum);
unsigned int nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct));
unsigned int nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct));
unsigned int nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct));
unsigned int nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct));
int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conn *ct,
unsigned int hooknum, unsigned int hdrlen);
unsigned int nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct));
unsigned int nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct));
unsigned int nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct));
unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct));
#endif /* _NF_NAT_L3PROTO_H */
unsigned int id;
struct nf_hook_ops *elem;
- u_int8_t pf;
+ struct nf_hook_state state;
u16 size; /* sizeof(entry) + saved route keys */
- unsigned int hook;
- struct net_device *indev;
- struct net_device *outdev;
- int (*okfn)(struct sk_buff *);
/* extra space to store route keys */
};
static inline void nft_set_pktinfo(struct nft_pktinfo *pkt,
const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out)
+ const struct nf_hook_state *state)
{
pkt->skb = skb;
- pkt->in = pkt->xt.in = in;
- pkt->out = pkt->xt.out = out;
+ pkt->in = pkt->xt.in = state->in;
+ pkt->out = pkt->xt.out = state->out;
pkt->ops = ops;
pkt->xt.hooknum = ops->hooknum;
pkt->xt.family = ops->pf;
};
struct nft_set_ext;
+struct nft_expr;
/**
* struct nft_set_ops - nf_tables set operations
bool (*lookup)(const struct nft_set *set,
const struct nft_data *key,
const struct nft_set_ext **ext);
+ bool (*update)(struct nft_set *set,
+ const struct nft_data *key,
+ void *(*new)(struct nft_set *,
+ const struct nft_expr *,
+ struct nft_data []),
+ const struct nft_expr *expr,
+ struct nft_data data[],
+ const struct nft_set_ext **ext);
+
int (*insert)(const struct nft_set *set,
const struct nft_set_elem *elem);
void (*activate)(const struct nft_set *set,
* @dtype: data type (verdict or numeric type defined by userspace)
* @size: maximum set size
* @nelems: number of elements
+ * @ndeact: number of deactivated elements queued for removal
+ * @timeout: default timeout value in msecs
+ * @gc_int: garbage collection interval in msecs
* @policy: set parameterization (see enum nft_set_policies)
* @ops: set ops
* @pnet: network namespace
u32 ktype;
u32 dtype;
u32 size;
- u32 nelems;
+ atomic_t nelems;
+ u32 ndeact;
+ u64 timeout;
+ u32 gc_int;
u16 policy;
/* runtime data below here */
const struct nft_set_ops *ops ____cacheline_aligned;
return (void *)set->data;
}
+static inline struct nft_set *nft_set_container_of(const void *priv)
+{
+ return (void *)priv - offsetof(struct nft_set, data);
+}
+
struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
const struct nlattr *nla);
struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
const struct nlattr *nla);
+static inline unsigned long nft_set_gc_interval(const struct nft_set *set)
+{
+ return set->gc_int ? msecs_to_jiffies(set->gc_int) : HZ;
+}
+
/**
* struct nft_set_binding - nf_tables set binding
*
* @list: set bindings list node
* @chain: chain containing the rule bound to the set
+ * @flags: set action flags
*
* A set binding contains all information necessary for validation
* of new elements added to a bound set.
struct nft_set_binding {
struct list_head list;
const struct nft_chain *chain;
+ u32 flags;
};
int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
* @NFT_SET_EXT_KEY: element key
* @NFT_SET_EXT_DATA: mapping data
* @NFT_SET_EXT_FLAGS: element flags
+ * @NFT_SET_EXT_TIMEOUT: element timeout
+ * @NFT_SET_EXT_EXPIRATION: element expiration time
+ * @NFT_SET_EXT_USERDATA: user data associated with the element
* @NFT_SET_EXT_NUM: number of extension types
*/
enum nft_set_extensions {
NFT_SET_EXT_KEY,
NFT_SET_EXT_DATA,
NFT_SET_EXT_FLAGS,
+ NFT_SET_EXT_TIMEOUT,
+ NFT_SET_EXT_EXPIRATION,
+ NFT_SET_EXT_USERDATA,
NFT_SET_EXT_NUM
};
return nft_set_ext(ext, NFT_SET_EXT_FLAGS);
}
+static inline u64 *nft_set_ext_timeout(const struct nft_set_ext *ext)
+{
+ return nft_set_ext(ext, NFT_SET_EXT_TIMEOUT);
+}
+
+static inline unsigned long *nft_set_ext_expiration(const struct nft_set_ext *ext)
+{
+ return nft_set_ext(ext, NFT_SET_EXT_EXPIRATION);
+}
+
+static inline struct nft_userdata *nft_set_ext_userdata(const struct nft_set_ext *ext)
+{
+ return nft_set_ext(ext, NFT_SET_EXT_USERDATA);
+}
+
+static inline bool nft_set_elem_expired(const struct nft_set_ext *ext)
+{
+ return nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION) &&
+ time_is_before_eq_jiffies(*nft_set_ext_expiration(ext));
+}
+
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
+void *nft_set_elem_init(const struct nft_set *set,
+ const struct nft_set_ext_tmpl *tmpl,
+ const struct nft_data *key,
+ const struct nft_data *data,
+ u64 timeout, gfp_t gfp);
void nft_set_elem_destroy(const struct nft_set *set, void *elem);
+/**
+ * struct nft_set_gc_batch_head - nf_tables set garbage collection batch
+ *
+ * @rcu: rcu head
+ * @set: set the elements belong to
+ * @cnt: count of elements
+ */
+struct nft_set_gc_batch_head {
+ struct rcu_head rcu;
+ const struct nft_set *set;
+ unsigned int cnt;
+};
+
+#define NFT_SET_GC_BATCH_SIZE ((PAGE_SIZE - \
+ sizeof(struct nft_set_gc_batch_head)) / \
+ sizeof(void *))
+
+/**
+ * struct nft_set_gc_batch - nf_tables set garbage collection batch
+ *
+ * @head: GC batch head
+ * @elems: garbage collection elements
+ */
+struct nft_set_gc_batch {
+ struct nft_set_gc_batch_head head;
+ void *elems[NFT_SET_GC_BATCH_SIZE];
+};
+
+struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set,
+ gfp_t gfp);
+void nft_set_gc_batch_release(struct rcu_head *rcu);
+
+static inline void nft_set_gc_batch_complete(struct nft_set_gc_batch *gcb)
+{
+ if (gcb != NULL)
+ call_rcu(&gcb->head.rcu, nft_set_gc_batch_release);
+}
+
+static inline struct nft_set_gc_batch *
+nft_set_gc_batch_check(const struct nft_set *set, struct nft_set_gc_batch *gcb,
+ gfp_t gfp)
+{
+ if (gcb != NULL) {
+ if (gcb->head.cnt + 1 < ARRAY_SIZE(gcb->elems))
+ return gcb;
+ nft_set_gc_batch_complete(gcb);
+ }
+ return nft_set_gc_batch_alloc(set, gfp);
+}
+
+static inline void nft_set_gc_batch_add(struct nft_set_gc_batch *gcb,
+ void *elem)
+{
+ gcb->elems[gcb->head.cnt++] = elem;
+}
+
/**
* struct nft_expr_type - nf_tables expression type
*
return 1 << ACCESS_ONCE(net->nft.gencursor);
}
+#define NFT_GENMASK_ANY ((1 << 0) | (1 << 1))
+
/*
* Set element transaction helpers
*/
ext->genmask ^= nft_genmask_next(read_pnet(&set->pnet));
}
+/*
+ * We use a free bit in the genmask field to indicate the element
+ * is busy, meaning it is currently being processed either by
+ * the netlink API or GC.
+ *
+ * Even though the genmask is only a single byte wide, this works
+ * because the extension structure if fully constant once initialized,
+ * so there are no non-atomic write accesses unless it is already
+ * marked busy.
+ */
+#define NFT_SET_ELEM_BUSY_MASK (1 << 2)
+
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+#define NFT_SET_ELEM_BUSY_BIT 2
+#elif defined(__BIG_ENDIAN_BITFIELD)
+#define NFT_SET_ELEM_BUSY_BIT (BITS_PER_LONG - BITS_PER_BYTE + 2)
+#else
+#error
+#endif
+
+static inline int nft_set_elem_mark_busy(struct nft_set_ext *ext)
+{
+ unsigned long *word = (unsigned long *)ext;
+
+ BUILD_BUG_ON(offsetof(struct nft_set_ext, genmask) != 0);
+ return test_and_set_bit(NFT_SET_ELEM_BUSY_BIT, word);
+}
+
+static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext)
+{
+ unsigned long *word = (unsigned long *)ext;
+
+ clear_bit(NFT_SET_ELEM_BUSY_BIT, word);
+}
+
/**
* struct nft_trans - nf_tables object update in transaction
*
int nft_lookup_module_init(void);
void nft_lookup_module_exit(void);
+int nft_dynset_module_init(void);
+void nft_dynset_module_exit(void);
+
int nft_bitwise_module_init(void);
void nft_bitwise_module_exit(void);
nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out)
+ const struct nf_hook_state *state)
{
struct iphdr *ip;
- nft_set_pktinfo(pkt, ops, skb, in, out);
+ nft_set_pktinfo(pkt, ops, skb, state);
ip = ip_hdr(pkt->skb);
pkt->tprot = ip->protocol;
nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out)
+ const struct nf_hook_state *state)
{
int protohdr, thoff = 0;
unsigned short frag_off;
- nft_set_pktinfo(pkt, ops, skb, in, out);
+ nft_set_pktinfo(pkt, ops, skb, state);
protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
/* If malformed, drop it */
};
#define NFC_HCI_MAX_CUSTOM_GATES 50
+/*
+ * According to specification 102 622 chapter 4.4 Pipes,
+ * the pipe identifier is 7 bits long.
+ */
#define NFC_HCI_MAX_PIPES 127
struct nfc_hci_init_data {
u8 gate_count;
int (*close)(struct nci_dev *ndev);
int (*send)(struct nci_dev *ndev, struct sk_buff *skb);
int (*setup)(struct nci_dev *ndev);
+ int (*fw_download)(struct nci_dev *ndev, const char *firmware_name);
__u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol);
int (*discover_se)(struct nci_dev *ndev);
int (*disable_se)(struct nci_dev *ndev, u32 se_idx);
#define NCI_HCI_INVALID_HOST 0x80
#define NCI_HCI_MAX_CUSTOM_GATES 50
+/*
+ * According to specification 102 622 chapter 4.4 Pipes,
+ * the pipe identifier is 7 bits long.
+ */
#define NCI_HCI_MAX_PIPES 127
struct nci_hci_gate {
u32 aid_len;
u8 aid[NFC_MAX_AID_LENGTH];
u8 params_len;
- u8 params[NFC_MAX_PARAMS_LENGTH];
+ u8 params[0];
} __packed;
struct nfc_genl_data {
void rtnl_af_unregister(struct rtnl_af_ops *ops);
struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]);
-struct net_device *rtnl_create_link(struct net *net, char *ifname,
+struct net_device *rtnl_create_link(struct net *net, const char *ifname,
unsigned char name_assign_type,
const struct rtnl_link_ops *ops,
struct nlattr *tb[]);
struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie);
+bool sk_mc_loop(struct sock *sk);
+
static inline bool sk_can_gso(const struct sock *sk)
{
return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type);
#define TCPOPT_SACK 5 /* SACK Block */
#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */
#define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */
+#define TCPOPT_FASTOPEN 34 /* Fast open (RFC7413) */
#define TCPOPT_EXP 254 /* Experimental */
/* Magic number to be after the option value for sharing TCP
* experimental options. See draft-ietf-tcpm-experimental-options-00.txt
#define TCPOLEN_SACK_PERM 2
#define TCPOLEN_TIMESTAMP 10
#define TCPOLEN_MD5SIG 18
+#define TCPOLEN_FASTOPEN_BASE 2
#define TCPOLEN_EXP_FASTOPEN_BASE 4
/* But this is what stacks really send out. */
struct tcp_fastopen_cookie *cookie, int *syn_loss,
unsigned long *last_syn_loss);
void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
- struct tcp_fastopen_cookie *cookie, bool syn_lost);
+ struct tcp_fastopen_cookie *cookie, bool syn_lost,
+ u16 try_exp);
struct tcp_fastopen_request {
/* Fast Open cookie. Size 0 means a cookie request */
struct tcp_fastopen_cookie cookie;
struct udp_tunnel_sock_cfg *sock_cfg);
/* Transmit the skb using UDP encapsulation. */
-int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb,
+int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
__be32 src, __be32 dst, __u8 tos, __u8 ttl,
__be16 df, __be16 src_port, __be16 dst_port,
bool xnet, bool nocheck);
#if IS_ENABLED(CONFIG_IPV6)
-int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb,
+int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
+ struct sk_buff *skb,
struct net_device *dev, struct in6_addr *saddr,
struct in6_addr *daddr,
__u8 prio, __u8 ttl, __be16 src_port,
void vxlan_sock_release(struct vxlan_sock *vs);
-int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb,
+int vxlan_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
__be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
__be16 src_port, __be16 dst_port, struct vxlan_metadata *md,
bool xnet, u32 vxflags);
int (*tmpl_sort)(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n);
int (*state_sort)(struct xfrm_state **dst, struct xfrm_state **src, int n);
int (*output)(struct sock *sk, struct sk_buff *skb);
- int (*output_finish)(struct sk_buff *skb);
+ int (*output_finish)(struct sock *sk, struct sk_buff *skb);
int (*extract_input)(struct xfrm_state *x,
struct sk_buff *skb);
int (*extract_output)(struct xfrm_state *x,
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
int xfrm_output_resume(struct sk_buff *skb, int err);
-int xfrm_output(struct sk_buff *skb);
+int xfrm_output(struct sock *sk, struct sk_buff *skb);
int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
void xfrm_local_error(struct sk_buff *skb, int mtu);
int xfrm4_extract_header(struct sk_buff *skb);
int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_output(struct sock *sk, struct sk_buff *skb);
-int xfrm4_output_finish(struct sk_buff *skb);
+int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb);
int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err);
int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol);
int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char protocol);
int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm6_output(struct sock *sk, struct sk_buff *skb);
-int xfrm6_output_finish(struct sk_buff *skb);
+int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb);
int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
u8 **prevhdr);
#include <linux/ktime.h>
#include <linux/tracepoint.h>
-struct device;
-struct regmap;
+#include "../../../drivers/base/regmap/internal.h"
/*
* Log register events
*/
DECLARE_EVENT_CLASS(regmap_reg,
- TP_PROTO(struct device *dev, unsigned int reg,
+ TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
- TP_ARGS(dev, reg, val),
+ TP_ARGS(map, reg, val),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __field( unsigned int, reg )
- __field( unsigned int, val )
+ __string( name, regmap_name(map) )
+ __field( unsigned int, reg )
+ __field( unsigned int, val )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__entry->reg = reg;
__entry->val = val;
),
DEFINE_EVENT(regmap_reg, regmap_reg_write,
- TP_PROTO(struct device *dev, unsigned int reg,
+ TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
- TP_ARGS(dev, reg, val)
+ TP_ARGS(map, reg, val)
);
DEFINE_EVENT(regmap_reg, regmap_reg_read,
- TP_PROTO(struct device *dev, unsigned int reg,
+ TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
- TP_ARGS(dev, reg, val)
+ TP_ARGS(map, reg, val)
);
DEFINE_EVENT(regmap_reg, regmap_reg_read_cache,
- TP_PROTO(struct device *dev, unsigned int reg,
+ TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
- TP_ARGS(dev, reg, val)
+ TP_ARGS(map, reg, val)
);
DECLARE_EVENT_CLASS(regmap_block,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count),
+ TP_ARGS(map, reg, count),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __field( unsigned int, reg )
- __field( int, count )
+ __string( name, regmap_name(map) )
+ __field( unsigned int, reg )
+ __field( int, count )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__entry->reg = reg;
__entry->count = count;
),
DEFINE_EVENT(regmap_block, regmap_hw_read_start,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_block, regmap_hw_read_done,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_block, regmap_hw_write_start,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_block, regmap_hw_write_done,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
TRACE_EVENT(regcache_sync,
- TP_PROTO(struct device *dev, const char *type,
+ TP_PROTO(struct regmap *map, const char *type,
const char *status),
- TP_ARGS(dev, type, status),
+ TP_ARGS(map, type, status),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __string( status, status )
- __string( type, type )
- __field( int, type )
+ __string( name, regmap_name(map) )
+ __string( status, status )
+ __string( type, type )
+ __field( int, type )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__assign_str(status, status);
__assign_str(type, type);
),
DECLARE_EVENT_CLASS(regmap_bool,
- TP_PROTO(struct device *dev, bool flag),
+ TP_PROTO(struct regmap *map, bool flag),
- TP_ARGS(dev, flag),
+ TP_ARGS(map, flag),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __field( int, flag )
+ __string( name, regmap_name(map) )
+ __field( int, flag )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__entry->flag = flag;
),
DEFINE_EVENT(regmap_bool, regmap_cache_only,
- TP_PROTO(struct device *dev, bool flag),
+ TP_PROTO(struct regmap *map, bool flag),
- TP_ARGS(dev, flag)
+ TP_ARGS(map, flag)
);
DEFINE_EVENT(regmap_bool, regmap_cache_bypass,
- TP_PROTO(struct device *dev, bool flag),
+ TP_PROTO(struct regmap *map, bool flag),
- TP_ARGS(dev, flag)
+ TP_ARGS(map, flag)
);
DECLARE_EVENT_CLASS(regmap_async,
- TP_PROTO(struct device *dev),
+ TP_PROTO(struct regmap *map),
- TP_ARGS(dev),
+ TP_ARGS(map),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
+ __string( name, regmap_name(map) )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
),
TP_printk("%s", __get_str(name))
DEFINE_EVENT(regmap_block, regmap_async_write_start,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_async, regmap_async_io_complete,
- TP_PROTO(struct device *dev),
+ TP_PROTO(struct regmap *map),
- TP_ARGS(dev)
+ TP_ARGS(map)
);
DEFINE_EVENT(regmap_async, regmap_async_complete_start,
- TP_PROTO(struct device *dev),
+ TP_PROTO(struct regmap *map),
- TP_ARGS(dev)
+ TP_ARGS(map)
);
DEFINE_EVENT(regmap_async, regmap_async_complete_done,
- TP_PROTO(struct device *dev),
+ TP_PROTO(struct regmap *map),
- TP_ARGS(dev)
+ TP_ARGS(map)
);
TRACE_EVENT(regcache_drop_region,
- TP_PROTO(struct device *dev, unsigned int from,
+ TP_PROTO(struct regmap *map, unsigned int from,
unsigned int to),
- TP_ARGS(dev, from, to),
+ TP_ARGS(map, from, to),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __field( unsigned int, from )
- __field( unsigned int, to )
+ __string( name, regmap_name(map) )
+ __field( unsigned int, from )
+ __field( unsigned int, to )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__entry->from = from;
__entry->to = to;
),
BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */
BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */
BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */
- BPF_FUNC_skb_store_bytes, /* int skb_store_bytes(skb, offset, from, len) */
+
+ /**
+ * skb_store_bytes(skb, offset, from, len, flags) - store bytes into packet
+ * @skb: pointer to skb
+ * @offset: offset within packet from skb->data
+ * @from: pointer where to copy bytes from
+ * @len: number of bytes to store into packet
+ * @flags: bit 0 - if true, recompute skb->csum
+ * other bits - reserved
+ * Return: 0 on success
+ */
+ BPF_FUNC_skb_store_bytes,
+
+ /**
+ * l3_csum_replace(skb, offset, from, to, flags) - recompute IP checksum
+ * @skb: pointer to skb
+ * @offset: offset within packet where IP checksum is located
+ * @from: old value of header field
+ * @to: new value of header field
+ * @flags: bits 0-3 - size of header field
+ * other bits - reserved
+ * Return: 0 on success
+ */
+ BPF_FUNC_l3_csum_replace,
+
+ /**
+ * l4_csum_replace(skb, offset, from, to, flags) - recompute TCP/UDP checksum
+ * @skb: pointer to skb
+ * @offset: offset within packet where TCP/UDP checksum is located
+ * @from: old value of header field
+ * @to: new value of header field
+ * @flags: bits 0-3 - size of header field
+ * bit 4 - is pseudo header
+ * other bits - reserved
+ * Return: 0 on success
+ */
+ BPF_FUNC_l4_csum_replace,
__BPF_FUNC_MAX_ID,
};
__u32 vlan_present;
__u32 vlan_tci;
__u32 vlan_proto;
+ __u32 priority;
};
#endif /* _UAPI__LINUX_BPF_H__ */
CAN_RAW_LOOPBACK, /* local loopback (default:on) */
CAN_RAW_RECV_OWN_MSGS, /* receive my own msgs (default:off) */
CAN_RAW_FD_FRAMES, /* allow CAN FD frames (default:off) */
+ CAN_RAW_JOIN_FILTERS, /* all filters must match to trigger */
};
#endif /* !_UAPI_CAN_RAW_H */
*/
#define MT_TOOL_FINGER 0
#define MT_TOOL_PEN 1
-#define MT_TOOL_MAX 1
+#define MT_TOOL_PALM 2
+#define MT_TOOL_MAX 2
/*
* Values describing the status of a force-feedback effect
* @NFT_SET_CONSTANT: set contents may not change while bound
* @NFT_SET_INTERVAL: set contains intervals
* @NFT_SET_MAP: set is used as a dictionary
+ * @NFT_SET_TIMEOUT: set uses timeouts
*/
enum nft_set_flags {
NFT_SET_ANONYMOUS = 0x1,
NFT_SET_CONSTANT = 0x2,
NFT_SET_INTERVAL = 0x4,
NFT_SET_MAP = 0x8,
+ NFT_SET_TIMEOUT = 0x10,
};
/**
* @NFTA_SET_POLICY: selection policy (NLA_U32)
* @NFTA_SET_DESC: set description (NLA_NESTED)
* @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
+ * @NFTA_SET_TIMEOUT: default timeout value (NLA_U64)
+ * @NFTA_SET_GC_INTERVAL: garbage collection interval (NLA_U32)
*/
enum nft_set_attributes {
NFTA_SET_UNSPEC,
NFTA_SET_POLICY,
NFTA_SET_DESC,
NFTA_SET_ID,
+ NFTA_SET_TIMEOUT,
+ NFTA_SET_GC_INTERVAL,
__NFTA_SET_MAX
};
#define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
* @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data)
* @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes)
* @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32)
+ * @NFTA_SET_ELEM_TIMEOUT: timeout value (NLA_U64)
+ * @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64)
+ * @NFTA_SET_ELEM_USERDATA: user data (NLA_BINARY)
*/
enum nft_set_elem_attributes {
NFTA_SET_ELEM_UNSPEC,
NFTA_SET_ELEM_KEY,
NFTA_SET_ELEM_DATA,
NFTA_SET_ELEM_FLAGS,
+ NFTA_SET_ELEM_TIMEOUT,
+ NFTA_SET_ELEM_EXPIRATION,
+ NFTA_SET_ELEM_USERDATA,
__NFTA_SET_ELEM_MAX
};
#define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1)
};
#define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1)
+enum nft_dynset_ops {
+ NFT_DYNSET_OP_ADD,
+ NFT_DYNSET_OP_UPDATE,
+};
+
+/**
+ * enum nft_dynset_attributes - dynset expression attributes
+ *
+ * @NFTA_DYNSET_SET_NAME: name of set the to add data to (NLA_STRING)
+ * @NFTA_DYNSET_SET_ID: uniquely identifier of the set in the transaction (NLA_U32)
+ * @NFTA_DYNSET_OP: operation (NLA_U32)
+ * @NFTA_DYNSET_SREG_KEY: source register of the key (NLA_U32)
+ * @NFTA_DYNSET_SREG_DATA: source register of the data (NLA_U32)
+ * @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64)
+ */
+enum nft_dynset_attributes {
+ NFTA_DYNSET_UNSPEC,
+ NFTA_DYNSET_SET_NAME,
+ NFTA_DYNSET_SET_ID,
+ NFTA_DYNSET_OP,
+ NFTA_DYNSET_SREG_KEY,
+ NFTA_DYNSET_SREG_DATA,
+ NFTA_DYNSET_TIMEOUT,
+ __NFTA_DYNSET_MAX,
+};
+#define NFTA_DYNSET_MAX (__NFTA_DYNSET_MAX - 1)
+
/**
* enum nft_payload_bases - nf_tables payload expression offset bases
*
* exported filesystem.
*/
#define NFSEXP_V4ROOT 0x10000
-#define NFSEXP_NOPNFS 0x20000
+#define NFSEXP_PNFS 0x20000
/* All flags that we claim to support. (Note we don't support NOACL.) */
#define NFSEXP_ALLFLAGS 0x3FE7F
RTM_NEWNSID = 88,
#define RTM_NEWNSID RTM_NEWNSID
+ RTM_DELNSID = 89,
+#define RTM_DELNSID RTM_DELNSID
RTM_GETNSID = 90,
#define RTM_GETNSID RTM_GETNSID
#define RTNLGRP_MDB RTNLGRP_MDB
RTNLGRP_MPLS_ROUTE,
#define RTNLGRP_MPLS_ROUTE RTNLGRP_MPLS_ROUTE
+ RTNLGRP_NSID,
+#define RTNLGRP_NSID RTNLGRP_NSID
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
{
struct perf_event *event = container_of(entry,
struct perf_event, pending);
+ int rctx;
+
+ rctx = perf_swevent_get_recursion_context();
+ /*
+ * If we 'fail' here, that's OK, it means recursion is already disabled
+ * and we won't recurse 'further'.
+ */
if (event->pending_disable) {
event->pending_disable = 0;
event->pending_wakeup = 0;
perf_event_wakeup(event);
}
+
+ if (rctx >= 0)
+ perf_swevent_put_recursion_context(rctx);
}
/*
if (!new_class->name)
return 0;
- list_for_each_entry(class, &all_lock_classes, lock_entry) {
+ list_for_each_entry_rcu(class, &all_lock_classes, lock_entry) {
if (new_class->key - new_class->subclass == class->key)
return class->name_version;
if (class->name && !strcmp(class->name, new_class->name))
hash_head = classhashentry(key);
/*
- * We can walk the hash lockfree, because the hash only
- * grows, and we are careful when adding entries to the end:
+ * We do an RCU walk of the hash, see lockdep_free_key_range().
*/
- list_for_each_entry(class, hash_head, hash_entry) {
+ if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
+ return NULL;
+
+ list_for_each_entry_rcu(class, hash_head, hash_entry) {
if (class->key == key) {
/*
* Huh! same key, different name? Did someone trample
struct lockdep_subclass_key *key;
struct list_head *hash_head;
struct lock_class *class;
- unsigned long flags;
+
+ DEBUG_LOCKS_WARN_ON(!irqs_disabled());
class = look_up_lock_class(lock, subclass);
if (likely(class))
key = lock->key->subkeys + subclass;
hash_head = classhashentry(key);
- raw_local_irq_save(flags);
if (!graph_lock()) {
- raw_local_irq_restore(flags);
return NULL;
}
/*
* We have to do the hash-walk again, to avoid races
* with another CPU:
*/
- list_for_each_entry(class, hash_head, hash_entry)
+ list_for_each_entry_rcu(class, hash_head, hash_entry) {
if (class->key == key)
goto out_unlock_set;
+ }
+
/*
* Allocate a new key from the static array, and add it to
* the hash:
*/
if (nr_lock_classes >= MAX_LOCKDEP_KEYS) {
if (!debug_locks_off_graph_unlock()) {
- raw_local_irq_restore(flags);
return NULL;
}
- raw_local_irq_restore(flags);
print_lockdep_off("BUG: MAX_LOCKDEP_KEYS too low!");
dump_stack();
if (verbose(class)) {
graph_unlock();
- raw_local_irq_restore(flags);
printk("\nnew class %p: %s", class->key, class->name);
if (class->name_version > 1)
printk("\n");
dump_stack();
- raw_local_irq_save(flags);
if (!graph_lock()) {
- raw_local_irq_restore(flags);
return NULL;
}
}
out_unlock_set:
graph_unlock();
- raw_local_irq_restore(flags);
out_set_class_cache:
if (!subclass || force)
entry->distance = distance;
entry->trace = *trace;
/*
- * Since we never remove from the dependency list, the list can
- * be walked lockless by other CPUs, it's only allocation
- * that must be protected by the spinlock. But this also means
- * we must make new entries visible only once writes to the
- * entry become visible - hence the RCU op:
+ * Both allocation and removal are done under the graph lock; but
+ * iteration is under RCU-sched; see look_up_lock_class() and
+ * lockdep_free_key_range().
*/
list_add_tail_rcu(&entry->entry, head);
else
head = &lock->class->locks_before;
- list_for_each_entry(entry, head, entry) {
+ DEBUG_LOCKS_WARN_ON(!irqs_disabled());
+
+ list_for_each_entry_rcu(entry, head, entry) {
if (!lock_accessed(entry)) {
unsigned int cq_depth;
mark_lock_accessed(entry, lock);
* We can walk it lock-free, because entries only get added
* to the hash:
*/
- list_for_each_entry(chain, hash_head, entry) {
+ list_for_each_entry_rcu(chain, hash_head, entry) {
if (chain->chain_key == chain_key) {
cache_hit:
debug_atomic_inc(chain_lookup_hits);
if (unlikely(!debug_locks))
return;
- if (subclass)
+ if (subclass) {
+ unsigned long flags;
+
+ if (DEBUG_LOCKS_WARN_ON(current->lockdep_recursion))
+ return;
+
+ raw_local_irq_save(flags);
+ current->lockdep_recursion = 1;
register_lock_class(lock, subclass, 1);
+ current->lockdep_recursion = 0;
+ raw_local_irq_restore(flags);
+ }
}
EXPORT_SYMBOL_GPL(lockdep_init_map);
return addr >= start && addr < start + size;
}
+/*
+ * Used in module.c to remove lock classes from memory that is going to be
+ * freed; and possibly re-used by other modules.
+ *
+ * We will have had one sync_sched() before getting here, so we're guaranteed
+ * nobody will look up these exact classes -- they're properly dead but still
+ * allocated.
+ */
void lockdep_free_key_range(void *start, unsigned long size)
{
- struct lock_class *class, *next;
+ struct lock_class *class;
struct list_head *head;
unsigned long flags;
int i;
head = classhash_table + i;
if (list_empty(head))
continue;
- list_for_each_entry_safe(class, next, head, hash_entry) {
+ list_for_each_entry_rcu(class, head, hash_entry) {
if (within(class->key, start, size))
zap_class(class);
else if (within(class->name, start, size))
if (locked)
graph_unlock();
raw_local_irq_restore(flags);
+
+ /*
+ * Wait for any possible iterators from look_up_lock_class() to pass
+ * before continuing to free the memory they refer to.
+ *
+ * sync_sched() is sufficient because the read-side is IRQ disable.
+ */
+ synchronize_sched();
+
+ /*
+ * XXX at this point we could return the resources to the pool;
+ * instead we leak them. We would need to change to bitmap allocators
+ * instead of the linear allocators we have now.
+ */
}
void lockdep_reset_lock(struct lockdep_map *lock)
{
- struct lock_class *class, *next;
+ struct lock_class *class;
struct list_head *head;
unsigned long flags;
int i, j;
head = classhash_table + i;
if (list_empty(head))
continue;
- list_for_each_entry_safe(class, next, head, hash_entry) {
+ list_for_each_entry_rcu(class, head, hash_entry) {
int match = 0;
for (j = 0; j < NR_LOCKDEP_CACHING_CLASSES; j++)
kfree(mod->args);
percpu_modfree(mod);
- /* Free lock-classes: */
+ /* Free lock-classes; relies on the preceding sync_rcu(). */
lockdep_free_key_range(mod->module_core, mod->core_size);
/* Finally, free the core (containing the module structure) */
module_bug_cleanup(mod);
mutex_unlock(&module_mutex);
- /* Free lock-classes: */
- lockdep_free_key_range(mod->module_core, mod->core_size);
-
/* we can't deallocate the module until we clear memory protection */
unset_module_init_ro_nx(mod);
unset_module_core_ro_nx(mod);
synchronize_rcu();
mutex_unlock(&module_mutex);
free_module:
+ /* Free lock-classes; relies on the preceding sync_rcu() */
+ lockdep_free_key_range(mod->module_core, mod->core_size);
+
module_deallocate(mod, info);
free_copy:
free_copy(info);
} else {
if (dl_prio(oldprio))
p->dl.dl_boosted = 0;
+ if (rt_prio(oldprio))
+ p->rt.timeout = 0;
p->sched_class = &fair_sched_class;
}
/*
* If there were no record hinting faults then either the task is
* completely idle or all activity is areas that are not of interest
- * to automatic numa balancing. Scan slower
+ * to automatic numa balancing. Related to that, if there were failed
+ * migration then it implies we are migrating too quickly or the local
+ * node is overloaded. In either case, scan slower
*/
- if (local + shared == 0) {
+ if (local + shared == 0 || p->numa_faults_locality[2]) {
p->numa_scan_period = min(p->numa_scan_period_max,
p->numa_scan_period << 1);
if (migrated)
p->numa_pages_migrated += pages;
+ if (flags & TNF_MIGRATE_FAIL)
+ p->numa_faults_locality[2] += pages;
p->numa_faults[task_faults_idx(NUMA_MEMBUF, mem_node, priv)] += pages;
p->numa_faults[task_faults_idx(NUMA_CPUBUF, cpu_node, priv)] += pages;
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero,
},
+ {
+ .procname = "dirtytime_expire_seconds",
+ .data = &dirtytime_expire_interval,
+ .maxlen = sizeof(dirty_expire_interval),
+ .mode = 0644,
+ .proc_handler = dirtytime_interval_handler,
+ .extra1 = &zero,
+ },
{
.procname = "nr_pdflush_threads",
.mode = 0444 /* read-only */,
*/
static int bc_set_next(ktime_t expires, struct clock_event_device *bc)
{
+ int bc_moved;
/*
* We try to cancel the timer first. If the callback is on
* flight on some other cpu then we let it handle it. If we
* restart the timer because we are in the callback, but we
* can set the expiry time and let the callback return
* HRTIMER_RESTART.
+ *
+ * Since we are in the idle loop at this point and because
+ * hrtimer_{start/cancel} functions call into tracing,
+ * calls to these functions must be bound within RCU_NONIDLE.
*/
- if (hrtimer_try_to_cancel(&bctimer) >= 0) {
- hrtimer_start(&bctimer, expires, HRTIMER_MODE_ABS_PINNED);
+ RCU_NONIDLE(bc_moved = (hrtimer_try_to_cancel(&bctimer) >= 0) ?
+ !hrtimer_start(&bctimer, expires, HRTIMER_MODE_ABS_PINNED) :
+ 0);
+ if (bc_moved) {
/* Bind the "device" to the cpu */
bc->bound_on = smp_processor_id();
} else if (bc->bound_on == smp_processor_id()) {
return 0;
}
EXPORT_SYMBOL_GPL(lcm);
+
+unsigned long lcm_not_zero(unsigned long a, unsigned long b)
+{
+ unsigned long l = lcm(a, b);
+
+ if (l)
+ return l;
+
+ return (b ? : a);
+}
+EXPORT_SYMBOL_GPL(lcm_not_zero);
int minlen = min_t(int, count, nla_len(src));
memcpy(dest, nla_data(src), minlen);
+ if (count > minlen)
+ memset(dest + minlen, 0, count - minlen);
return minlen;
}
.key_offset = offsetof(struct test_obj, value),
.key_len = sizeof(int),
.hashfn = jhash,
- .max_size = 2, /* we expand/shrink manually here */
.nulls_base = (3U << RHT_BASE_SHIFT),
};
int target_nid, last_cpupid = -1;
bool page_locked;
bool migrated = false;
+ bool was_writable;
int flags = 0;
/* A PROT_NONE fault should not end up here */
flags |= TNF_FAULT_LOCAL;
}
- /*
- * Avoid grouping on DSO/COW pages in specific and RO pages
- * in general, RO pages shouldn't hurt as much anyway since
- * they can be in shared cache state.
- *
- * FIXME! This checks "pmd_dirty()" as an approximation of
- * "is this a read-only page", since checking "pmd_write()"
- * is even more broken. We haven't actually turned this into
- * a writable page, so pmd_write() will always be false.
- */
- if (!pmd_dirty(pmd))
+ /* See similar comment in do_numa_page for explanation */
+ if (!(vma->vm_flags & VM_WRITE))
flags |= TNF_NO_GROUP;
/*
if (migrated) {
flags |= TNF_MIGRATED;
page_nid = target_nid;
- }
+ } else
+ flags |= TNF_MIGRATE_FAIL;
goto out;
clear_pmdnuma:
BUG_ON(!PageLocked(page));
+ was_writable = pmd_write(pmd);
pmd = pmd_modify(pmd, vma->vm_page_prot);
+ pmd = pmd_mkyoung(pmd);
+ if (was_writable)
+ pmd = pmd_mkwrite(pmd);
set_pmd_at(mm, haddr, pmdp, pmd);
update_mmu_cache_pmd(vma, addr, pmdp);
unlock_page(page);
if (__pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
pmd_t entry;
+ bool preserve_write = prot_numa && pmd_write(*pmd);
ret = 1;
/*
if (!prot_numa || !pmd_protnone(*pmd)) {
entry = pmdp_get_and_clear_notify(mm, addr, pmd);
entry = pmd_modify(entry, newprot);
+ if (preserve_write)
+ entry = pmd_mkwrite(entry);
ret = HPAGE_PMD_NR;
set_pmd_at(mm, addr, pmd, entry);
- BUG_ON(pmd_write(entry));
+ BUG_ON(!preserve_write && pmd_write(entry));
}
spin_unlock(ptl);
}
int last_cpupid;
int target_nid;
bool migrated = false;
+ bool was_writable = pte_write(pte);
int flags = 0;
/* A PROT_NONE fault should not end up here */
/* Make it present again */
pte = pte_modify(pte, vma->vm_page_prot);
pte = pte_mkyoung(pte);
+ if (was_writable)
+ pte = pte_mkwrite(pte);
set_pte_at(mm, addr, ptep, pte);
update_mmu_cache(vma, addr, ptep);
}
/*
- * Avoid grouping on DSO/COW pages in specific and RO pages
- * in general, RO pages shouldn't hurt as much anyway since
- * they can be in shared cache state.
- *
- * FIXME! This checks "pmd_dirty()" as an approximation of
- * "is this a read-only page", since checking "pmd_write()"
- * is even more broken. We haven't actually turned this into
- * a writable page, so pmd_write() will always be false.
+ * Avoid grouping on RO pages in general. RO pages shouldn't hurt as
+ * much anyway since they can be in shared cache state. This misses
+ * the case where a mapping is writable but the process never writes
+ * to it but pte_write gets cleared during protection updates and
+ * pte_dirty has unpredictable behaviour between PTE scan updates,
+ * background writeback, dirty balancing and application behaviour.
*/
- if (!pte_dirty(pte))
+ if (!(vma->vm_flags & VM_WRITE))
flags |= TNF_NO_GROUP;
/*
if (migrated) {
page_nid = target_nid;
flags |= TNF_MIGRATED;
- }
+ } else
+ flags |= TNF_MIGRATE_FAIL;
out:
if (page_nid != -1)
return NULL;
arch_refresh_nodedata(nid, pgdat);
+ } else {
+ /* Reset the nr_zones and classzone_idx to 0 before reuse */
+ pgdat->nr_zones = 0;
+ pgdat->classzone_idx = 0;
}
/* we can use NODE_DATA(nid) from here */
if (is_vmalloc_addr(zone->wait_table))
vfree(zone->wait_table);
}
-
- /*
- * Since there is no way to guarentee the address of pgdat/zone is not
- * on stack of any kernel threads or used by other kernel objects
- * without reference counting or other symchronizing method, do not
- * reset node_data and free pgdat here. Just reset it to 0 and reuse
- * the memory when the node is online again.
- */
- memset(pgdat, 0, sizeof(*pgdat));
}
EXPORT_SYMBOL(try_offline_node);
importer->anon_vma = exporter->anon_vma;
error = anon_vma_clone(importer, exporter);
- if (error) {
- importer->anon_vma = NULL;
+ if (error)
return error;
- }
}
}
oldpte = *pte;
if (pte_present(oldpte)) {
pte_t ptent;
+ bool preserve_write = prot_numa && pte_write(oldpte);
/*
* Avoid trapping faults against the zero or KSM
ptent = ptep_modify_prot_start(mm, addr, pte);
ptent = pte_modify(ptent, newprot);
+ if (preserve_write)
+ ptent = pte_mkwrite(ptent);
/* Avoid taking write faults for known dirty pages */
if (dirty_accountable && pte_dirty(ptent) &&
* bw * elapsed + write_bandwidth * (period - elapsed)
* write_bandwidth = ---------------------------------------------------
* period
+ *
+ * @written may have decreased due to account_page_redirty().
+ * Avoid underflowing @bw calculation.
*/
- bw = written - bdi->written_stamp;
+ bw = written - min(written, bdi->written_stamp);
bw *= HZ;
if (unlikely(elapsed > period)) {
do_div(bw, elapsed);
unsigned long now)
{
static DEFINE_SPINLOCK(dirty_lock);
- static unsigned long update_time;
+ static unsigned long update_time = INITIAL_JIFFIES;
/*
* check locklessly first to optimize away locking for the most time
if (!is_migrate_isolate_page(buddy)) {
__isolate_free_page(page, order);
+ kernel_map_pages(page, (1 << order), 1);
set_page_refcounted(page);
isolated_page = page;
}
vma = vma->vm_next;
err = walk_page_test(start, next, walk);
- if (err > 0)
+ if (err > 0) {
+ /*
+ * positive return values are purely for
+ * controlling the pagewalk, so should never
+ * be passed to the callers.
+ */
+ err = 0;
continue;
+ }
if (err < 0)
break;
}
return 0;
enomem_failure:
+ /*
+ * dst->anon_vma is dropped here otherwise its degree can be incorrectly
+ * decremented in unlink_anon_vmas().
+ * We can safely do this because callers of anon_vma_clone() don't care
+ * about dst->anon_vma if anon_vma_clone() failed.
+ */
+ dst->anon_vma = NULL;
unlink_anon_vmas(dst);
return -ENOMEM;
}
do {
tid = this_cpu_read(s->cpu_slab->tid);
c = raw_cpu_ptr(s->cpu_slab);
- } while (IS_ENABLED(CONFIG_PREEMPT) && unlikely(tid != c->tid));
+ } while (IS_ENABLED(CONFIG_PREEMPT) &&
+ unlikely(tid != READ_ONCE(c->tid)));
/*
* Irqless object alloc/free algorithm used here depends on sequence
do {
tid = this_cpu_read(s->cpu_slab->tid);
c = raw_cpu_ptr(s->cpu_slab);
- } while (IS_ENABLED(CONFIG_PREEMPT) && unlikely(tid != c->tid));
+ } while (IS_ENABLED(CONFIG_PREEMPT) &&
+ unlikely(tid != READ_ONCE(c->tid)));
/* Same with comment on barrier() in slab_alloc_node() */
barrier();
/* IFF_BROADCAST|IFF_MULTICAST; ??? */
dev->flags = real_dev->flags & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
IFF_MASTER | IFF_SLAVE);
- dev->iflink = real_dev->ifindex;
dev->state = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) |
(1<<__LINK_STATE_DORMANT))) |
(1<<__LINK_STATE_PRESENT);
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
+static int vlan_dev_get_iflink(const struct net_device *dev)
+{
+ struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
+
+ return real_dev->ifindex;
+}
+
static const struct ethtool_ops vlan_ethtool_ops = {
.get_settings = vlan_ethtool_get_settings,
.get_drvinfo = vlan_ethtool_get_drvinfo,
#endif
.ndo_fix_features = vlan_dev_fix_features,
.ndo_get_lock_subclass = vlan_dev_get_lock_subclass,
+ .ndo_get_iflink = vlan_dev_get_iflink,
};
static void vlan_dev_free(struct net_device *dev)
return true;
/* no more parents..stop recursion */
- if (net_dev->iflink == 0 || net_dev->iflink == net_dev->ifindex)
+ if (dev_get_iflink(net_dev) == 0 ||
+ dev_get_iflink(net_dev) == net_dev->ifindex)
return false;
/* recurse over the parent device */
- parent_dev = __dev_get_by_index(&init_net, net_dev->iflink);
+ parent_dev = __dev_get_by_index(&init_net, dev_get_iflink(net_dev));
/* if we got a NULL parent_dev there is something broken.. */
if (WARN(!parent_dev, "Cannot find parent device"))
return false;
#define BNEPCONNDEL _IOW('B', 201, int)
#define BNEPGETCONNLIST _IOR('B', 210, int)
#define BNEPGETCONNINFO _IOR('B', 211, int)
+#define BNEPGETSUPPFEAT _IOR('B', 212, int)
+
+#define BNEP_SETUP_RESPONSE 0
+#define BNEP_SETUP_RSP_SENT 10
struct bnep_connadd_req {
int sock; /* Connected socket */
break;
case BNEP_SETUP_CONN_REQ:
- err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
+ /* Successful response should be sent only once */
+ if (test_bit(BNEP_SETUP_RESPONSE, &s->flags) &&
+ !test_and_set_bit(BNEP_SETUP_RSP_SENT, &s->flags))
+ err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
+ BNEP_SUCCESS);
+ else
+ err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
+ BNEP_CONN_NOT_ALLOWED);
break;
default: {
pkt[0] = BNEP_CONTROL;
pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
pkt[2] = cmd;
- bnep_send(s, pkt, sizeof(pkt));
+ err = bnep_send(s, pkt, sizeof(pkt));
}
break;
}
{
struct net_device *dev = s->dev;
struct sk_buff *nskb;
- u8 type;
+ u8 type, ctrl_type;
dev->stats.rx_bytes += skb->len;
type = *(u8 *) skb->data;
skb_pull(skb, 1);
+ ctrl_type = *(u8 *)skb->data;
if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
goto badframe;
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
- bnep_rx_control(s, skb->data, skb->len);
- kfree_skb(skb);
- return 0;
- }
+ if (bnep_rx_control(s, skb->data, skb->len) < 0) {
+ dev->stats.tx_errors++;
+ kfree_skb(skb);
+ return 0;
+ }
- skb_reset_mac_header(skb);
+ if (!(type & BNEP_EXT_HEADER)) {
+ kfree_skb(skb);
+ return 0;
+ }
- /* Verify and pull out header */
- if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
- goto badframe;
+ /* Verify and pull ctrl message since it's already processed */
+ switch (ctrl_type) {
+ case BNEP_SETUP_CONN_REQ:
+ /* Pull: ctrl type (1 b), len (1 b), data (len bytes) */
+ if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2))
+ goto badframe;
+ break;
+ case BNEP_FILTER_MULTI_ADDR_SET:
+ case BNEP_FILTER_NET_TYPE_SET:
+ /* Pull: ctrl type (1 b), len (2 b), data (len bytes) */
+ if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2))
+ goto badframe;
+ break;
+ default:
+ kfree_skb(skb);
+ return 0;
+ }
+ } else {
+ skb_reset_mac_header(skb);
- s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
+ /* Verify and pull out header */
+ if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
+ goto badframe;
+
+ s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
+ }
if (type & BNEP_EXT_HEADER) {
if (bnep_rx_extension(s, skb) < 0)
int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
{
+ u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
struct net_device *dev;
struct bnep_session *s, *ss;
u8 dst[ETH_ALEN], src[ETH_ALEN];
if (!l2cap_is_socket(sock))
return -EBADFD;
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
s->sock = sock;
s->role = req->role;
s->state = BT_CONNECTED;
+ s->flags = req->flags;
s->msg.msg_flags = MSG_NOSIGNAL;
int bnep_del_connection(struct bnep_conndel_req *req)
{
+ u32 valid_flags = 0;
struct bnep_session *s;
int err = 0;
BT_DBG("");
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
down_read(&bnep_session_sem);
s = __bnep_get_session(req->dst);
static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
{
+ u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
+
memset(ci, 0, sizeof(*ci));
memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
strcpy(ci->device, s->dev->name);
- ci->flags = s->flags;
+ ci->flags = s->flags & valid_flags;
ci->state = s->state;
ci->role = s->role;
}
struct bnep_conninfo ci;
struct socket *nsock;
void __user *argp = (void __user *)arg;
+ __u32 supp_feat = BIT(BNEP_SETUP_RESPONSE);
int err;
BT_DBG("cmd %x arg %lx", cmd, arg);
return err;
+ case BNEPGETSUPPFEAT:
+ if (copy_to_user(argp, &supp_feat, sizeof(supp_feat)))
+ return -EFAULT;
+
+ return 0;
+
default:
return -EINVAL;
}
return;
}
- if (session->flags & (1 << CMTP_LOOPBACK)) {
+ if (session->flags & BIT(CMTP_LOOPBACK)) {
kfree_skb(skb);
return;
}
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
{
+ u32 valid_flags = BIT(CMTP_LOOPBACK);
memset(ci, 0, sizeof(*ci));
bacpy(&ci->bdaddr, &session->bdaddr);
- ci->flags = session->flags;
+ ci->flags = session->flags & valid_flags;
ci->state = session->state;
ci->num = session->num;
down_write(&cmtp_session_sem);
- if (!(session->flags & (1 << CMTP_LOOPBACK)))
+ if (!(session->flags & BIT(CMTP_LOOPBACK)))
cmtp_detach_device(session);
fput(session->sock->file);
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
{
+ u32 valid_flags = BIT(CMTP_LOOPBACK);
struct cmtp_session *session, *s;
int i, err;
if (!l2cap_is_socket(sock))
return -EBADFD;
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL);
if (!session)
return -ENOMEM;
goto unlink;
}
- if (!(session->flags & (1 << CMTP_LOOPBACK))) {
+ if (!(session->flags & BIT(CMTP_LOOPBACK))) {
err = cmtp_attach_device(session);
if (err < 0) {
atomic_inc(&session->terminate);
int cmtp_del_connection(struct cmtp_conndel_req *req)
{
+ u32 valid_flags = 0;
struct cmtp_session *session;
int err = 0;
BT_DBG("");
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
down_read(&cmtp_session_sem);
session = __cmtp_get_session(&req->bdaddr);
/* ---- HCI requests ---- */
-static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode)
+static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
+ struct sk_buff *skb)
{
BT_DBG("%s result 0x%2.2x", hdev->name, result);
if (hdev->req_status == HCI_REQ_PEND) {
hdev->req_result = result;
hdev->req_status = HCI_REQ_DONE;
+ if (skb)
+ hdev->req_skb = skb_get(skb);
wake_up_interruptible(&hdev->req_wait_q);
}
}
}
}
-static struct sk_buff *hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
- u8 event)
-{
- struct hci_ev_cmd_complete *ev;
- struct hci_event_hdr *hdr;
- struct sk_buff *skb;
-
- hci_dev_lock(hdev);
-
- skb = hdev->recv_evt;
- hdev->recv_evt = NULL;
-
- hci_dev_unlock(hdev);
-
- if (!skb)
- return ERR_PTR(-ENODATA);
-
- if (skb->len < sizeof(*hdr)) {
- BT_ERR("Too short HCI event");
- goto failed;
- }
-
- hdr = (void *) skb->data;
- skb_pull(skb, HCI_EVENT_HDR_SIZE);
-
- if (event) {
- if (hdr->evt != event)
- goto failed;
- return skb;
- }
-
- if (hdr->evt != HCI_EV_CMD_COMPLETE) {
- BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt);
- goto failed;
- }
-
- if (skb->len < sizeof(*ev)) {
- BT_ERR("Too short cmd_complete event");
- goto failed;
- }
-
- ev = (void *) skb->data;
- skb_pull(skb, sizeof(*ev));
-
- if (opcode == __le16_to_cpu(ev->opcode))
- return skb;
-
- BT_DBG("opcode doesn't match (0x%2.2x != 0x%2.2x)", opcode,
- __le16_to_cpu(ev->opcode));
-
-failed:
- kfree_skb(skb);
- return ERR_PTR(-ENODATA);
-}
-
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param, u8 event, u32 timeout)
{
DECLARE_WAITQUEUE(wait, current);
struct hci_request req;
+ struct sk_buff *skb;
int err = 0;
BT_DBG("%s", hdev->name);
add_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- err = hci_req_run(&req, hci_req_sync_complete);
+ err = hci_req_run_skb(&req, hci_req_sync_complete);
if (err < 0) {
remove_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_RUNNING);
}
hdev->req_status = hdev->req_result = 0;
+ skb = hdev->req_skb;
+ hdev->req_skb = NULL;
BT_DBG("%s end: err %d", hdev->name, err);
- if (err < 0)
+ if (err < 0) {
+ kfree_skb(skb);
return ERR_PTR(err);
+ }
- return hci_get_cmd_complete(hdev, opcode, event);
+ if (!skb)
+ return ERR_PTR(-ENODATA);
+
+ return skb;
}
EXPORT_SYMBOL(__hci_cmd_sync_ev);
add_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- err = hci_req_run(&req, hci_req_sync_complete);
+ err = hci_req_run_skb(&req, hci_req_sync_complete);
if (err < 0) {
hdev->req_status = 0;
hdev->sent_cmd = NULL;
}
- kfree_skb(hdev->recv_evt);
- hdev->recv_evt = NULL;
-
/* After this point our queues are empty
* and no tasks are scheduled. */
hdev->close(hdev);
/* Unregister HCI device */
void hci_unregister_dev(struct hci_dev *hdev)
{
- int i, id;
+ int id;
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
hci_dev_do_close(hdev);
- for (i = 0; i < NUM_REASSEMBLY; i++)
- kfree_skb(hdev->reassembly[i]);
-
cancel_work_sync(&hdev->power_on);
if (!test_bit(HCI_INIT, &hdev->flags) &&
}
EXPORT_SYMBOL(hci_recv_frame);
-static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
- int count, __u8 index)
-{
- int len = 0;
- int hlen = 0;
- int remain = count;
- struct sk_buff *skb;
- struct bt_skb_cb *scb;
-
- if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) ||
- index >= NUM_REASSEMBLY)
- return -EILSEQ;
-
- skb = hdev->reassembly[index];
-
- if (!skb) {
- switch (type) {
- case HCI_ACLDATA_PKT:
- len = HCI_MAX_FRAME_SIZE;
- hlen = HCI_ACL_HDR_SIZE;
- break;
- case HCI_EVENT_PKT:
- len = HCI_MAX_EVENT_SIZE;
- hlen = HCI_EVENT_HDR_SIZE;
- break;
- case HCI_SCODATA_PKT:
- len = HCI_MAX_SCO_SIZE;
- hlen = HCI_SCO_HDR_SIZE;
- break;
- }
-
- skb = bt_skb_alloc(len, GFP_ATOMIC);
- if (!skb)
- return -ENOMEM;
-
- scb = (void *) skb->cb;
- scb->expect = hlen;
- scb->pkt_type = type;
-
- hdev->reassembly[index] = skb;
- }
-
- while (count) {
- scb = (void *) skb->cb;
- len = min_t(uint, scb->expect, count);
-
- memcpy(skb_put(skb, len), data, len);
-
- count -= len;
- data += len;
- scb->expect -= len;
- remain = count;
-
- switch (type) {
- case HCI_EVENT_PKT:
- if (skb->len == HCI_EVENT_HDR_SIZE) {
- struct hci_event_hdr *h = hci_event_hdr(skb);
- scb->expect = h->plen;
-
- if (skb_tailroom(skb) < scb->expect) {
- kfree_skb(skb);
- hdev->reassembly[index] = NULL;
- return -ENOMEM;
- }
- }
- break;
-
- case HCI_ACLDATA_PKT:
- if (skb->len == HCI_ACL_HDR_SIZE) {
- struct hci_acl_hdr *h = hci_acl_hdr(skb);
- scb->expect = __le16_to_cpu(h->dlen);
-
- if (skb_tailroom(skb) < scb->expect) {
- kfree_skb(skb);
- hdev->reassembly[index] = NULL;
- return -ENOMEM;
- }
- }
- break;
-
- case HCI_SCODATA_PKT:
- if (skb->len == HCI_SCO_HDR_SIZE) {
- struct hci_sco_hdr *h = hci_sco_hdr(skb);
- scb->expect = h->dlen;
-
- if (skb_tailroom(skb) < scb->expect) {
- kfree_skb(skb);
- hdev->reassembly[index] = NULL;
- return -ENOMEM;
- }
- }
- break;
- }
-
- if (scb->expect == 0) {
- /* Complete frame */
-
- bt_cb(skb)->pkt_type = type;
- hci_recv_frame(hdev, skb);
-
- hdev->reassembly[index] = NULL;
- return remain;
- }
- }
-
- return remain;
-}
-
-#define STREAM_REASSEMBLY 0
-
-int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count)
-{
- int type;
- int rem = 0;
-
- while (count) {
- struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY];
-
- if (!skb) {
- struct { char type; } *pkt;
-
- /* Start of the frame */
- pkt = data;
- type = pkt->type;
-
- data++;
- count--;
- } else
- type = bt_cb(skb)->pkt_type;
-
- rem = hci_reassembly(hdev, type, data, count,
- STREAM_REASSEMBLY);
- if (rem < 0)
- return rem;
-
- data += (count - rem);
- count = rem;
- }
-
- return rem;
-}
-EXPORT_SYMBOL(hci_recv_stream_fragment);
-
/* ---- Interface to upper protocols ---- */
int hci_register_cb(struct hci_cb *cb)
}
}
-bool hci_req_pending(struct hci_dev *hdev)
-{
- return (hdev->req_status == HCI_REQ_PEND);
-}
-
/* Send HCI command */
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
const void *param)
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->req_start = 1;
+ bt_cb(skb)->req.start = true;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
if (!skb)
return true;
- return bt_cb(skb)->req_start;
+ return bt_cb(skb)->req.start;
}
static void hci_resend_last(struct hci_dev *hdev)
queue_work(hdev->workqueue, &hdev->cmd_work);
}
-void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status)
+void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
+ hci_req_complete_t *req_complete,
+ hci_req_complete_skb_t *req_complete_skb)
{
- hci_req_complete_t req_complete = NULL;
struct sk_buff *skb;
unsigned long flags;
* callback would be found in hdev->sent_cmd instead of the
* command queue (hdev->cmd_q).
*/
- if (hdev->sent_cmd) {
- req_complete = bt_cb(hdev->sent_cmd)->req_complete;
-
- if (req_complete) {
- /* We must set the complete callback to NULL to
- * avoid calling the callback more than once if
- * this function gets called again.
- */
- bt_cb(hdev->sent_cmd)->req_complete = NULL;
+ if (bt_cb(hdev->sent_cmd)->req.complete) {
+ *req_complete = bt_cb(hdev->sent_cmd)->req.complete;
+ return;
+ }
- goto call_complete;
- }
+ if (bt_cb(hdev->sent_cmd)->req.complete_skb) {
+ *req_complete_skb = bt_cb(hdev->sent_cmd)->req.complete_skb;
+ return;
}
/* Remove all pending commands belonging to this request */
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
while ((skb = __skb_dequeue(&hdev->cmd_q))) {
- if (bt_cb(skb)->req_start) {
+ if (bt_cb(skb)->req.start) {
__skb_queue_head(&hdev->cmd_q, skb);
break;
}
- req_complete = bt_cb(skb)->req_complete;
+ *req_complete = bt_cb(skb)->req.complete;
+ *req_complete_skb = bt_cb(skb)->req.complete_skb;
kfree_skb(skb);
}
spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
-
-call_complete:
- if (req_complete)
- req_complete(hdev, status, status ? opcode : HCI_OP_NOP);
}
static void hci_rx_work(struct work_struct *work)
.release = single_release,
};
+static int device_id_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+
+ hci_dev_lock(hdev);
+ seq_printf(f, "%4.4x:%4.4x:%4.4x:%4.4x\n", hdev->devid_source,
+ hdev->devid_vendor, hdev->devid_product, hdev->devid_version);
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int device_id_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, device_id_show, inode->i_private);
+}
+
+static const struct file_operations device_id_fops = {
+ .open = device_id_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int device_list_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
debugfs_create_u8("hardware_error", 0444, hdev->debugfs,
&hdev->hw_error_code);
+ debugfs_create_file("device_id", 0444, hdev->debugfs, hdev,
+ &device_id_fops);
debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
&device_list_fops);
struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
-
- hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
- rp->status);
- hci_dev_unlock(hdev);
}
static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
-
- hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
- rp->hash256, rp->rand256,
- rp->status);
- hci_dev_unlock(hdev);
}
-
static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
hci_dev_unlock(hdev);
}
+static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_le_read_remote_features *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_REMOTE_FEATURES);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn) {
+ if (conn->state == BT_CONFIG) {
+ hci_connect_cfm(conn, status);
+ hci_conn_drop(conn);
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
{
struct hci_cp_le_start_enc *cp;
hci_dev_unlock(hdev);
}
-static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
+ u16 *opcode, u8 *status,
+ hci_req_complete_t *req_complete,
+ hci_req_complete_skb_t *req_complete_skb)
{
struct hci_ev_cmd_complete *ev = (void *) skb->data;
- u8 status = skb->data[sizeof(*ev)];
- __u16 opcode;
- skb_pull(skb, sizeof(*ev));
+ *opcode = __le16_to_cpu(ev->opcode);
+ *status = skb->data[sizeof(*ev)];
- opcode = __le16_to_cpu(ev->opcode);
+ skb_pull(skb, sizeof(*ev));
- switch (opcode) {
+ switch (*opcode) {
case HCI_OP_INQUIRY_CANCEL:
hci_cc_inquiry_cancel(hdev, skb);
break;
break;
default:
- BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
+ BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
break;
}
- if (opcode != HCI_OP_NOP)
+ if (*opcode != HCI_OP_NOP)
cancel_delayed_work(&hdev->cmd_timer);
- hci_req_cmd_complete(hdev, opcode, status);
-
- if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
+ if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
atomic_set(&hdev->cmd_cnt, 1);
- if (!skb_queue_empty(&hdev->cmd_q))
- queue_work(hdev->workqueue, &hdev->cmd_work);
- }
+
+ hci_req_cmd_complete(hdev, *opcode, *status, req_complete,
+ req_complete_skb);
+
+ if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
+ queue_work(hdev->workqueue, &hdev->cmd_work);
}
-static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
+ u16 *opcode, u8 *status,
+ hci_req_complete_t *req_complete,
+ hci_req_complete_skb_t *req_complete_skb)
{
struct hci_ev_cmd_status *ev = (void *) skb->data;
- __u16 opcode;
skb_pull(skb, sizeof(*ev));
- opcode = __le16_to_cpu(ev->opcode);
+ *opcode = __le16_to_cpu(ev->opcode);
+ *status = ev->status;
- switch (opcode) {
+ switch (*opcode) {
case HCI_OP_INQUIRY:
hci_cs_inquiry(hdev, ev->status);
break;
hci_cs_le_create_conn(hdev, ev->status);
break;
+ case HCI_OP_LE_READ_REMOTE_FEATURES:
+ hci_cs_le_read_remote_features(hdev, ev->status);
+ break;
+
case HCI_OP_LE_START_ENC:
hci_cs_le_start_enc(hdev, ev->status);
break;
default:
- BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
+ BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
break;
}
- if (opcode != HCI_OP_NOP)
+ if (*opcode != HCI_OP_NOP)
cancel_delayed_work(&hdev->cmd_timer);
+ if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
+ atomic_set(&hdev->cmd_cnt, 1);
+
+ /* Indicate request completion if the command failed. Also, if
+ * we're not waiting for a special event and we get a success
+ * command status we should try to flag the request as completed
+ * (since for this kind of commands there will not be a command
+ * complete event).
+ */
if (ev->status ||
- (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req_event))
- hci_req_cmd_complete(hdev, opcode, ev->status);
+ (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event))
+ hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete,
+ req_complete_skb);
- if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
- atomic_set(&hdev->cmd_cnt, 1);
- if (!skb_queue_empty(&hdev->cmd_q))
- queue_work(hdev->workqueue, &hdev->cmd_work);
- }
+ if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
+ queue_work(hdev->workqueue, &hdev->cmd_work);
}
static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
- conn->state = BT_CONNECTED;
+ conn->state = BT_CONFIG;
conn->le_conn_interval = le16_to_cpu(ev->interval);
conn->le_conn_latency = le16_to_cpu(ev->latency);
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
- hci_connect_cfm(conn, ev->status);
+ if (!ev->status) {
+ /* The remote features procedure is defined for master
+ * role only. So only in case of an initiated connection
+ * request the remote features.
+ *
+ * If the local controller supports slave-initiated features
+ * exchange, then requesting the remote features in slave
+ * role is possible. Otherwise just transition into the
+ * connected state without requesting the remote features.
+ */
+ if (conn->out ||
+ (hdev->le_features[0] & HCI_LE_SLAVE_FEATURES)) {
+ struct hci_cp_le_read_remote_features cp;
+
+ cp.handle = __cpu_to_le16(conn->handle);
+
+ hci_send_cmd(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
+ sizeof(cp), &cp);
+
+ hci_conn_hold(conn);
+ } else {
+ conn->state = BT_CONNECTED;
+ hci_connect_cfm(conn, ev->status);
+ }
+ } else {
+ hci_connect_cfm(conn, ev->status);
+ }
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
conn->dst_type);
hci_dev_unlock(hdev);
}
+static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_le_remote_feat_complete *ev = (void *)skb->data;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+ if (conn) {
+ if (!ev->status)
+ memcpy(conn->features[0], ev->features, 8);
+
+ if (conn->state == BT_CONFIG) {
+ __u8 status;
+
+ /* If the local controller supports slave-initiated
+ * features exchange, but the remote controller does
+ * not, then it is possible that the error code 0x1a
+ * for unsupported remote feature gets returned.
+ *
+ * In this specific case, allow the connection to
+ * transition into connected state and mark it as
+ * successful.
+ */
+ if ((hdev->le_features[0] & HCI_LE_SLAVE_FEATURES) &&
+ !conn->out && ev->status == 0x1a)
+ status = 0x00;
+ else
+ status = ev->status;
+
+ conn->state = BT_CONNECTED;
+ hci_connect_cfm(conn, status);
+ hci_conn_drop(conn);
+ }
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_le_ltk_req *ev = (void *) skb->data;
hci_le_adv_report_evt(hdev, skb);
break;
+ case HCI_EV_LE_REMOTE_FEAT_COMPLETE:
+ hci_le_remote_feat_complete_evt(hdev, skb);
+ break;
+
case HCI_EV_LE_LTK_REQ:
hci_le_ltk_request_evt(hdev, skb);
break;
amp_read_loc_assoc_final_data(hdev, hcon);
}
-void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
+static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
+ u8 event, struct sk_buff *skb)
{
- struct hci_event_hdr *hdr = (void *) skb->data;
- __u8 event = hdr->evt;
+ struct hci_ev_cmd_complete *ev;
+ struct hci_event_hdr *hdr;
- hci_dev_lock(hdev);
+ if (!skb)
+ return false;
- /* Received events are (currently) only needed when a request is
- * ongoing so avoid unnecessary memory allocation.
- */
- if (hci_req_pending(hdev)) {
- kfree_skb(hdev->recv_evt);
- hdev->recv_evt = skb_clone(skb, GFP_KERNEL);
+ if (skb->len < sizeof(*hdr)) {
+ BT_ERR("Too short HCI event");
+ return false;
}
- hci_dev_unlock(hdev);
-
+ hdr = (void *) skb->data;
skb_pull(skb, HCI_EVENT_HDR_SIZE);
- if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req_event == event) {
- struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
- u16 opcode = __le16_to_cpu(cmd_hdr->opcode);
+ if (event) {
+ if (hdr->evt != event)
+ return false;
+ return true;
+ }
+
+ if (hdr->evt != HCI_EV_CMD_COMPLETE) {
+ BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt);
+ return false;
+ }
+
+ if (skb->len < sizeof(*ev)) {
+ BT_ERR("Too short cmd_complete event");
+ return false;
+ }
- hci_req_cmd_complete(hdev, opcode, 0);
+ ev = (void *) skb->data;
+ skb_pull(skb, sizeof(*ev));
+
+ if (opcode != __le16_to_cpu(ev->opcode)) {
+ BT_DBG("opcode doesn't match (0x%2.2x != 0x%2.2x)", opcode,
+ __le16_to_cpu(ev->opcode));
+ return false;
}
+ return true;
+}
+
+void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_event_hdr *hdr = (void *) skb->data;
+ hci_req_complete_t req_complete = NULL;
+ hci_req_complete_skb_t req_complete_skb = NULL;
+ struct sk_buff *orig_skb = NULL;
+ u8 status = 0, event = hdr->evt, req_evt = 0;
+ u16 opcode = HCI_OP_NOP;
+
+ if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) {
+ struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
+ opcode = __le16_to_cpu(cmd_hdr->opcode);
+ hci_req_cmd_complete(hdev, opcode, status, &req_complete,
+ &req_complete_skb);
+ req_evt = event;
+ }
+
+ /* If it looks like we might end up having to call
+ * req_complete_skb, store a pristine copy of the skb since the
+ * various handlers may modify the original one through
+ * skb_pull() calls, etc.
+ */
+ if (req_complete_skb || event == HCI_EV_CMD_STATUS ||
+ event == HCI_EV_CMD_COMPLETE)
+ orig_skb = skb_clone(skb, GFP_KERNEL);
+
+ skb_pull(skb, HCI_EVENT_HDR_SIZE);
+
switch (event) {
case HCI_EV_INQUIRY_COMPLETE:
hci_inquiry_complete_evt(hdev, skb);
break;
case HCI_EV_CMD_COMPLETE:
- hci_cmd_complete_evt(hdev, skb);
+ hci_cmd_complete_evt(hdev, skb, &opcode, &status,
+ &req_complete, &req_complete_skb);
break;
case HCI_EV_CMD_STATUS:
- hci_cmd_status_evt(hdev, skb);
+ hci_cmd_status_evt(hdev, skb, &opcode, &status, &req_complete,
+ &req_complete_skb);
break;
case HCI_EV_HARDWARE_ERROR:
break;
}
+ if (req_complete) {
+ req_complete(hdev, status, opcode);
+ } else if (req_complete_skb) {
+ if (!hci_get_cmd_complete(hdev, opcode, req_evt, orig_skb)) {
+ kfree_skb(orig_skb);
+ orig_skb = NULL;
+ }
+ req_complete_skb(hdev, status, opcode, orig_skb);
+ }
+
+ kfree_skb(orig_skb);
kfree_skb(skb);
hdev->stat.evt_rx++;
}
req->err = 0;
}
-int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
+static int req_run(struct hci_request *req, hci_req_complete_t complete,
+ hci_req_complete_skb_t complete_skb)
{
struct hci_dev *hdev = req->hdev;
struct sk_buff *skb;
return -ENODATA;
skb = skb_peek_tail(&req->cmd_q);
- bt_cb(skb)->req_complete = complete;
+ bt_cb(skb)->req.complete = complete;
+ bt_cb(skb)->req.complete_skb = complete_skb;
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
return 0;
}
+int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
+{
+ return req_run(req, complete, NULL);
+}
+
+int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete)
+{
+ return req_run(req, NULL, complete);
+}
+
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param)
{
}
if (skb_queue_empty(&req->cmd_q))
- bt_cb(skb)->req_start = 1;
+ bt_cb(skb)->req.start = true;
- bt_cb(skb)->req_event = event;
+ bt_cb(skb)->req.event = event;
skb_queue_tail(&req->cmd_q, skb);
}
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
+int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete);
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
const void *param);
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
const void *param, u8 event);
-void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
+void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
+ hci_req_complete_t *req_complete,
+ hci_req_complete_skb_t *req_complete_skb);
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param);
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->req_start = 1;
+ bt_cb(skb)->req.start = true;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
static void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
{
+ u32 valid_flags = 0;
memset(ci, 0, sizeof(*ci));
bacpy(&ci->bdaddr, &session->bdaddr);
- ci->flags = session->flags;
+ ci->flags = session->flags & valid_flags;
ci->state = BT_CONNECTED;
if (session->input) {
kref_init(&session->ref);
atomic_set(&session->state, HIDP_SESSION_IDLING);
init_waitqueue_head(&session->state_queue);
- session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
+ session->flags = req->flags & BIT(HIDP_BLUETOOTH_VENDOR_ID);
/* connection management */
bacpy(&session->bdaddr, bdaddr);
struct socket *ctrl_sock,
struct socket *intr_sock)
{
+ u32 valid_flags = 0;
struct hidp_session *session;
struct l2cap_conn *conn;
struct l2cap_chan *chan;
if (ret)
return ret;
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
chan = l2cap_pi(ctrl_sock->sk)->chan;
conn = NULL;
l2cap_chan_lock(chan);
int hidp_connection_del(struct hidp_conndel_req *req)
{
+ u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG);
struct hidp_session *session;
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
session = hidp_session_find(&req->bdaddr);
if (!session)
return -ENOENT;
- if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG))
+ if (req->flags & BIT(HIDP_VIRTUAL_CABLE_UNPLUG))
hidp_send_ctrl_message(session,
HIDP_TRANS_HID_CONTROL |
HIDP_CTRL_VIRTUAL_CABLE_UNPLUG,
struct sk_buff *skb;
skb_queue_walk(head, skb) {
- if (bt_cb(skb)->control.txseq == seq)
+ if (bt_cb(skb)->l2cap.txseq == seq)
return skb;
}
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
__unpack_extended_control(get_unaligned_le32(skb->data),
- &bt_cb(skb)->control);
+ &bt_cb(skb)->l2cap);
skb_pull(skb, L2CAP_EXT_CTRL_SIZE);
} else {
__unpack_enhanced_control(get_unaligned_le16(skb->data),
- &bt_cb(skb)->control);
+ &bt_cb(skb)->l2cap);
skb_pull(skb, L2CAP_ENH_CTRL_SIZE);
}
}
chan->retry_count = 0;
skb_queue_walk(&chan->tx_q, skb) {
- if (bt_cb(skb)->control.retries)
- bt_cb(skb)->control.retries = 1;
+ if (bt_cb(skb)->l2cap.retries)
+ bt_cb(skb)->l2cap.retries = 1;
else
break;
}
skb = skb_dequeue(&chan->tx_q);
- bt_cb(skb)->control.retries = 1;
- control = &bt_cb(skb)->control;
+ bt_cb(skb)->l2cap.retries = 1;
+ control = &bt_cb(skb)->l2cap;
control->reqseq = 0;
control->txseq = chan->next_tx_seq;
skb = chan->tx_send_head;
- bt_cb(skb)->control.retries = 1;
- control = &bt_cb(skb)->control;
+ bt_cb(skb)->l2cap.retries = 1;
+ control = &bt_cb(skb)->l2cap;
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control->final = 1;
continue;
}
- bt_cb(skb)->control.retries++;
- control = bt_cb(skb)->control;
+ bt_cb(skb)->l2cap.retries++;
+ control = bt_cb(skb)->l2cap;
if (chan->max_tx != 0 &&
- bt_cb(skb)->control.retries > chan->max_tx) {
+ bt_cb(skb)->l2cap.retries > chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan, ECONNRESET);
l2cap_seq_list_clear(&chan->retrans_list);
if (chan->unacked_frames) {
skb_queue_walk(&chan->tx_q, skb) {
- if (bt_cb(skb)->control.txseq == control->reqseq ||
+ if (bt_cb(skb)->l2cap.txseq == control->reqseq ||
skb == chan->tx_send_head)
break;
}
break;
l2cap_seq_list_append(&chan->retrans_list,
- bt_cb(skb)->control.txseq);
+ bt_cb(skb)->l2cap.txseq);
}
l2cap_ertm_resend(chan);
return ERR_PTR(err);
}
- bt_cb(skb)->control.fcs = chan->fcs;
- bt_cb(skb)->control.retries = 0;
+ bt_cb(skb)->l2cap.fcs = chan->fcs;
+ bt_cb(skb)->l2cap.retries = 0;
return skb;
}
return PTR_ERR(skb);
}
- bt_cb(skb)->control.sar = sar;
+ bt_cb(skb)->l2cap.sar = sar;
__skb_queue_tail(seg_queue, skb);
len -= pdu_len;
continue;
/* Don't send frame to the channel it came from */
- if (bt_cb(skb)->chan == chan)
+ if (bt_cb(skb)->l2cap.chan == chan)
continue;
nskb = skb_clone(skb, GFP_KERNEL);
skb_unlink(skb, &chan->srej_q);
chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
- err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->control);
+ err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->l2cap);
if (err)
break;
}
return;
}
- if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) {
+ if (chan->max_tx != 0 && bt_cb(skb)->l2cap.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
if (chan->max_tx && skb &&
- bt_cb(skb)->control.retries >= chan->max_tx) {
+ bt_cb(skb)->l2cap.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
{
- struct l2cap_ctrl *control = &bt_cb(skb)->control;
+ struct l2cap_ctrl *control = &bt_cb(skb)->l2cap;
u16 len;
u8 event;
goto drop;
/* Store remote BD_ADDR and PSM for msg_name */
- bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
- bt_cb(skb)->psm = psm;
+ bacpy(&bt_cb(skb)->l2cap.bdaddr, &hcon->dst);
+ bt_cb(skb)->l2cap.psm = psm;
if (!chan->ops->recv(chan, skb)) {
l2cap_chan_put(chan);
skb->priority = sk->sk_priority;
- bt_cb(skb)->chan = chan;
+ bt_cb(skb)->l2cap.chan = chan;
return skb;
}
memset(la, 0, sizeof(struct sockaddr_l2));
la->l2_family = AF_BLUETOOTH;
- la->l2_psm = bt_cb(skb)->psm;
- bacpy(&la->l2_bdaddr, &bt_cb(skb)->bdaddr);
+ la->l2_psm = bt_cb(skb)->l2cap.psm;
+ bacpy(&la->l2_bdaddr, &bt_cb(skb)->l2cap.bdaddr);
*msg_namelen = sizeof(struct sockaddr_l2);
}
/* Instance 0 always manages the "Tx Power" and "Flags" fields */
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
- /* For instance 0, assemble the flags from global settings */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE) ||
- get_connectable(hdev))
+ /* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting corresponds
+ * to the "connectable" instance flag.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
flags |= MGMT_ADV_FLAG_CONNECTABLE;
return flags;
}
+static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
+{
+ /* Ignore instance 0 and other unsupported instances */
+ if (instance != 0x01)
+ return 0;
+
+ /* TODO: Take into account the "appearance" and "local-name" flags here.
+ * These are currently being ignored as they are not supported.
+ */
+ return hdev->adv_instance.scan_rsp_len;
+}
+
static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
{
u8 ad_len = 0, flags = 0;
}
}
+ if (instance) {
+ memcpy(ptr, hdev->adv_instance.adv_data,
+ hdev->adv_instance.adv_data_len);
+
+ ad_len += hdev->adv_instance.adv_data_len;
+ ptr += hdev->adv_instance.adv_data_len;
+ }
+
/* Provide Tx Power only if we can provide a valid value for it */
if (hdev->adv_tx_power != HCI_TX_POWER_INVALID &&
(instance_flags & MGMT_ADV_FLAG_TX_POWER)) {
ptr += 3;
}
- if (instance) {
- memcpy(ptr, hdev->adv_instance.adv_data,
- hdev->adv_instance.adv_data_len);
- ad_len += hdev->adv_instance.adv_data_len;
- }
-
return ad_len;
}
instance = get_current_adv_instance(hdev);
flags = get_adv_instance_flags(hdev, instance);
- connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE);
+
+ /* If the "connectable" instance flag was not set, then choose between
+ * ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
+ */
+ connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
+ get_connectable(hdev);
/* Set require_privacy to true only when non-connectable
* advertising is used. In that case it is fine to use a
memset(&cp, 0, sizeof(cp));
cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval);
cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval);
- cp.type = connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND;
+
+ if (connectable)
+ cp.type = LE_ADV_IND;
+ else if (get_adv_instance_scan_rsp_len(hdev, instance))
+ cp.type = LE_ADV_SCAN_IND;
+ else
+ cp.type = LE_ADV_NONCONN_IND;
+
cp.own_address_type = own_addr_type;
cp.channel_map = hdev->le_adv_channel_map;
no_scan_update:
/* Update the advertising parameters if necessary */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+ hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
enable_advertising(&req);
err = hci_req_run(&req, set_connectable_complete);
return err;
}
+static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
+ u16 opcode, struct sk_buff *skb)
+{
+ struct mgmt_rp_read_local_oob_data mgmt_rp;
+ size_t rp_size = sizeof(mgmt_rp);
+ struct mgmt_pending_cmd *cmd;
+
+ BT_DBG("%s status %u", hdev->name, status);
+
+ cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
+ if (!cmd)
+ return;
+
+ if (status || !skb) {
+ mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ status ? mgmt_status(status) : MGMT_STATUS_FAILED);
+ goto remove;
+ }
+
+ memset(&mgmt_rp, 0, sizeof(mgmt_rp));
+
+ if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) {
+ struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
+
+ if (skb->len < sizeof(*rp)) {
+ mgmt_cmd_status(cmd->sk, hdev->id,
+ MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_FAILED);
+ goto remove;
+ }
+
+ memcpy(mgmt_rp.hash192, rp->hash, sizeof(rp->hash));
+ memcpy(mgmt_rp.rand192, rp->rand, sizeof(rp->rand));
+
+ rp_size -= sizeof(mgmt_rp.hash256) + sizeof(mgmt_rp.rand256);
+ } else {
+ struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
+
+ if (skb->len < sizeof(*rp)) {
+ mgmt_cmd_status(cmd->sk, hdev->id,
+ MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_FAILED);
+ goto remove;
+ }
+
+ memcpy(mgmt_rp.hash192, rp->hash192, sizeof(rp->hash192));
+ memcpy(mgmt_rp.rand192, rp->rand192, sizeof(rp->rand192));
+
+ memcpy(mgmt_rp.hash256, rp->hash256, sizeof(rp->hash256));
+ memcpy(mgmt_rp.rand256, rp->rand256, sizeof(rp->rand256));
+ }
+
+ mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_SUCCESS, &mgmt_rp, rp_size);
+
+remove:
+ mgmt_pending_remove(cmd);
+}
+
static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
struct mgmt_pending_cmd *cmd;
+ struct hci_request req;
int err;
BT_DBG("%s", hdev->name);
goto unlock;
}
+ hci_req_init(&req, hdev);
+
if (bredr_sc_enabled(hdev))
- err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA,
- 0, NULL);
+ hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL);
else
- err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+ hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+ err = hci_req_run_skb(&req, read_local_oob_data_complete);
if (err < 0)
mgmt_pending_remove(cmd);
return eir_len;
}
+static void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status,
+ u16 opcode, struct sk_buff *skb)
+{
+ const struct mgmt_cp_read_local_oob_ext_data *mgmt_cp;
+ struct mgmt_rp_read_local_oob_ext_data *mgmt_rp;
+ u8 *h192, *r192, *h256, *r256;
+ struct mgmt_pending_cmd *cmd;
+ u16 eir_len;
+ int err;
+
+ BT_DBG("%s status %u", hdev->name, status);
+
+ cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev);
+ if (!cmd)
+ return;
+
+ mgmt_cp = cmd->param;
+
+ if (status) {
+ status = mgmt_status(status);
+ eir_len = 0;
+
+ h192 = NULL;
+ r192 = NULL;
+ h256 = NULL;
+ r256 = NULL;
+ } else if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) {
+ struct hci_rp_read_local_oob_data *rp;
+
+ if (skb->len != sizeof(*rp)) {
+ status = MGMT_STATUS_FAILED;
+ eir_len = 0;
+ } else {
+ status = MGMT_STATUS_SUCCESS;
+ rp = (void *)skb->data;
+
+ eir_len = 5 + 18 + 18;
+ h192 = rp->hash;
+ r192 = rp->rand;
+ h256 = NULL;
+ r256 = NULL;
+ }
+ } else {
+ struct hci_rp_read_local_oob_ext_data *rp;
+
+ if (skb->len != sizeof(*rp)) {
+ status = MGMT_STATUS_FAILED;
+ eir_len = 0;
+ } else {
+ status = MGMT_STATUS_SUCCESS;
+ rp = (void *)skb->data;
+
+ if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) {
+ eir_len = 5 + 18 + 18;
+ h192 = NULL;
+ r192 = NULL;
+ } else {
+ eir_len = 5 + 18 + 18 + 18 + 18;
+ h192 = rp->hash192;
+ r192 = rp->rand192;
+ }
+
+ h256 = rp->hash256;
+ r256 = rp->rand256;
+ }
+ }
+
+ mgmt_rp = kmalloc(sizeof(*mgmt_rp) + eir_len, GFP_KERNEL);
+ if (!mgmt_rp)
+ goto done;
+
+ if (status)
+ goto send_rsp;
+
+ eir_len = eir_append_data(mgmt_rp->eir, 0, EIR_CLASS_OF_DEV,
+ hdev->dev_class, 3);
+
+ if (h192 && r192) {
+ eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+ EIR_SSP_HASH_C192, h192, 16);
+ eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+ EIR_SSP_RAND_R192, r192, 16);
+ }
+
+ if (h256 && r256) {
+ eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+ EIR_SSP_HASH_C256, h256, 16);
+ eir_len = eir_append_data(mgmt_rp->eir, eir_len,
+ EIR_SSP_RAND_R256, r256, 16);
+ }
+
+send_rsp:
+ mgmt_rp->type = mgmt_cp->type;
+ mgmt_rp->eir_len = cpu_to_le16(eir_len);
+
+ err = mgmt_cmd_complete(cmd->sk, hdev->id,
+ MGMT_OP_READ_LOCAL_OOB_EXT_DATA, status,
+ mgmt_rp, sizeof(*mgmt_rp) + eir_len);
+ if (err < 0 || status)
+ goto done;
+
+ hci_sock_set_flag(cmd->sk, HCI_MGMT_OOB_DATA_EVENTS);
+
+ err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev,
+ mgmt_rp, sizeof(*mgmt_rp) + eir_len,
+ HCI_MGMT_OOB_DATA_EVENTS, cmd->sk);
+done:
+ kfree(mgmt_rp);
+ mgmt_pending_remove(cmd);
+}
+
+static int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk,
+ struct mgmt_cp_read_local_oob_ext_data *cp)
+{
+ struct mgmt_pending_cmd *cmd;
+ struct hci_request req;
+ int err;
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev,
+ cp, sizeof(*cp));
+ if (!cmd)
+ return -ENOMEM;
+
+ hci_req_init(&req, hdev);
+
+ if (bredr_sc_enabled(hdev))
+ hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL);
+ else
+ hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+
+ err = hci_req_run_skb(&req, read_local_oob_ext_data_complete);
+ if (err < 0) {
+ mgmt_pending_remove(cmd);
+ return err;
+ }
+
+ return 0;
+}
+
static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
BT_DBG("%s", hdev->name);
- if (!hdev_is_powered(hdev))
- return mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- MGMT_STATUS_NOT_POWERED,
- &cp->type, sizeof(cp->type));
-
- switch (cp->type) {
- case BIT(BDADDR_BREDR):
- status = mgmt_bredr_support(hdev);
- if (status)
- return mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- status, &cp->type,
- sizeof(cp->type));
- eir_len = 5;
- break;
- case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
- status = mgmt_le_support(hdev);
- if (status)
- return mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- status, &cp->type,
- sizeof(cp->type));
- eir_len = 9 + 3 + 18 + 18 + 3;
- break;
- default:
- return mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- MGMT_STATUS_INVALID_PARAMS,
- &cp->type, sizeof(cp->type));
+ if (hdev_is_powered(hdev)) {
+ switch (cp->type) {
+ case BIT(BDADDR_BREDR):
+ status = mgmt_bredr_support(hdev);
+ if (status)
+ eir_len = 0;
+ else
+ eir_len = 5;
+ break;
+ case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
+ status = mgmt_le_support(hdev);
+ if (status)
+ eir_len = 0;
+ else
+ eir_len = 9 + 3 + 18 + 18 + 3;
+ break;
+ default:
+ status = MGMT_STATUS_INVALID_PARAMS;
+ eir_len = 0;
+ break;
+ }
+ } else {
+ status = MGMT_STATUS_NOT_POWERED;
+ eir_len = 0;
}
- hci_dev_lock(hdev);
-
rp_len = sizeof(*rp) + eir_len;
rp = kmalloc(rp_len, GFP_ATOMIC);
- if (!rp) {
- hci_dev_unlock(hdev);
+ if (!rp)
return -ENOMEM;
- }
+
+ if (status)
+ goto complete;
+
+ hci_dev_lock(hdev);
eir_len = 0;
switch (cp->type) {
case BIT(BDADDR_BREDR):
- eir_len = eir_append_data(rp->eir, eir_len, EIR_CLASS_OF_DEV,
- hdev->dev_class, 3);
+ if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) {
+ err = read_local_ssp_oob_req(hdev, sk, cp);
+ hci_dev_unlock(hdev);
+ if (!err)
+ goto done;
+
+ status = MGMT_STATUS_FAILED;
+ goto complete;
+ } else {
+ eir_len = eir_append_data(rp->eir, eir_len,
+ EIR_CLASS_OF_DEV,
+ hdev->dev_class, 3);
+ }
break;
case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
smp_generate_oob(hdev, hash, rand) < 0) {
hci_dev_unlock(hdev);
- err = mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- MGMT_STATUS_FAILED,
- &cp->type, sizeof(cp->type));
- goto done;
+ status = MGMT_STATUS_FAILED;
+ goto complete;
}
+ /* This should return the active RPA, but since the RPA
+ * is only programmed on demand, it is really hard to fill
+ * this in at the moment. For now disallow retrieving
+ * local out-of-band data when privacy is in use.
+ *
+ * Returning the identity address will not help here since
+ * pairing happens before the identity resolving key is
+ * known and thus the connection establishment happens
+ * based on the RPA and not the identity address.
+ */
if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
- memcpy(addr, &hdev->rpa, 6);
- addr[6] = 0x01;
- } else if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
- !bacmp(&hdev->bdaddr, BDADDR_ANY) ||
- (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
- bacmp(&hdev->static_addr, BDADDR_ANY))) {
+ hci_dev_unlock(hdev);
+ status = MGMT_STATUS_REJECTED;
+ goto complete;
+ }
+
+ if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
+ !bacmp(&hdev->bdaddr, BDADDR_ANY) ||
+ (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
+ bacmp(&hdev->static_addr, BDADDR_ANY))) {
memcpy(addr, &hdev->static_addr, 6);
addr[6] = 0x01;
} else {
break;
}
- rp->type = cp->type;
- rp->eir_len = cpu_to_le16(eir_len);
-
hci_dev_unlock(hdev);
hci_sock_set_flag(sk, HCI_MGMT_OOB_DATA_EVENTS);
+ status = MGMT_STATUS_SUCCESS;
+
+complete:
+ rp->type = cp->type;
+ rp->eir_len = cpu_to_le16(eir_len);
+
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- MGMT_STATUS_SUCCESS, rp, sizeof(*rp) + eir_len);
- if (err < 0)
+ status, rp, sizeof(*rp) + eir_len);
+ if (err < 0 || status)
goto done;
err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev,
cmd ? cmd->sk : NULL);
}
-void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
- u8 *rand192, u8 *hash256, u8 *rand256,
- u8 status)
-{
- struct mgmt_pending_cmd *cmd;
-
- BT_DBG("%s status %u", hdev->name, status);
-
- cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
- if (!cmd)
- return;
-
- if (status) {
- mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
- mgmt_status(status));
- } else {
- struct mgmt_rp_read_local_oob_data rp;
- size_t rp_size = sizeof(rp);
-
- memcpy(rp.hash192, hash192, sizeof(rp.hash192));
- memcpy(rp.rand192, rand192, sizeof(rp.rand192));
-
- if (bredr_sc_enabled(hdev) && hash256 && rand256) {
- memcpy(rp.hash256, hash256, sizeof(rp.hash256));
- memcpy(rp.rand256, rand256, sizeof(rp.rand256));
- } else {
- rp_size -= sizeof(rp.hash256) + sizeof(rp.rand256);
- }
-
- mgmt_cmd_complete(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA, 0,
- &rp, rp_size);
- }
-
- mgmt_pending_remove(cmd);
-}
-
static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])
{
int i;
SOFTWARE IS DISCLAIMED.
*/
+#include <linux/debugfs.h>
+
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
return 0;
}
+static char test_ecdh_buffer[32];
+
+static ssize_t test_ecdh_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return simple_read_from_buffer(user_buf, count, ppos, test_ecdh_buffer,
+ strlen(test_ecdh_buffer));
+}
+
+static const struct file_operations test_ecdh_fops = {
+ .open = simple_open,
+ .read = test_ecdh_read,
+ .llseek = default_llseek,
+};
+
static int __init test_ecdh(void)
{
ktime_t calltime, delta, rettime;
err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1);
if (err) {
BT_ERR("ECDH sample 1 failed");
- return err;
+ goto done;
}
err = test_ecdh_sample(priv_a_2, priv_b_2, pub_a_2, pub_b_2, dhkey_2);
if (err) {
BT_ERR("ECDH sample 2 failed");
- return err;
+ goto done;
}
err = test_ecdh_sample(priv_a_3, priv_a_3, pub_a_3, pub_a_3, dhkey_3);
if (err) {
BT_ERR("ECDH sample 3 failed");
- return err;
+ goto done;
}
rettime = ktime_get();
BT_INFO("ECDH test passed in %llu usecs", duration);
- return 0;
+done:
+ if (!err)
+ snprintf(test_ecdh_buffer, sizeof(test_ecdh_buffer),
+ "PASS (%llu usecs)\n", duration);
+ else
+ snprintf(test_ecdh_buffer, sizeof(test_ecdh_buffer), "FAIL\n");
+
+ debugfs_create_file("selftest_ecdh", 0444, bt_debugfs, NULL,
+ &test_ecdh_fops);
+
+ return err;
}
#else
return ERR_PTR(-ENOMEM);
skb->priority = HCI_PRIO_MAX;
- bt_cb(skb)->chan = chan;
+ bt_cb(skb)->l2cap.chan = chan;
return skb;
}
return 0;
}
+static char test_smp_buffer[32];
+
+static ssize_t test_smp_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return simple_read_from_buffer(user_buf, count, ppos, test_smp_buffer,
+ strlen(test_smp_buffer));
+}
+
+static const struct file_operations test_smp_fops = {
+ .open = simple_open,
+ .read = test_smp_read,
+ .llseek = default_llseek,
+};
+
static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
struct crypto_hash *tfm_cmac)
{
err = test_ah(tfm_aes);
if (err) {
BT_ERR("smp_ah test failed");
- return err;
+ goto done;
}
err = test_c1(tfm_aes);
if (err) {
BT_ERR("smp_c1 test failed");
- return err;
+ goto done;
}
err = test_s1(tfm_aes);
if (err) {
BT_ERR("smp_s1 test failed");
- return err;
+ goto done;
}
err = test_f4(tfm_cmac);
if (err) {
BT_ERR("smp_f4 test failed");
- return err;
+ goto done;
}
err = test_f5(tfm_cmac);
if (err) {
BT_ERR("smp_f5 test failed");
- return err;
+ goto done;
}
err = test_f6(tfm_cmac);
if (err) {
BT_ERR("smp_f6 test failed");
- return err;
+ goto done;
}
err = test_g2(tfm_cmac);
if (err) {
BT_ERR("smp_g2 test failed");
- return err;
+ goto done;
}
err = test_h6(tfm_cmac);
if (err) {
BT_ERR("smp_h6 test failed");
- return err;
+ goto done;
}
rettime = ktime_get();
BT_INFO("SMP test passed in %llu usecs", duration);
- return 0;
+done:
+ if (!err)
+ snprintf(test_smp_buffer, sizeof(test_smp_buffer),
+ "PASS (%llu usecs)\n", duration);
+ else
+ snprintf(test_smp_buffer, sizeof(test_smp_buffer), "FAIL\n");
+
+ debugfs_create_file("selftest_smp", 0444, bt_debugfs, NULL,
+ &test_smp_fops);
+
+ return err;
}
int __init bt_selftest_smp(void)
p->state == BR_STATE_FORWARDING;
}
-int br_dev_queue_push_xmit(struct sk_buff *skb)
+int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb)
{
if (!is_skb_forwardable(skb->dev, skb)) {
kfree_skb(skb);
}
EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit);
-int br_forward_finish(struct sk_buff *skb)
+int br_forward_finish(struct sock *sk, struct sk_buff *skb)
{
- return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+ return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING, sk, skb,
+ NULL, skb->dev,
br_dev_queue_push_xmit);
}
return;
}
- NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
+ NULL, skb->dev,
br_forward_finish);
}
skb->dev = to->dev;
skb_forward_csum(skb);
- NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
+ NF_HOOK(NFPROTO_BRIDGE, NF_BR_FORWARD, NULL, skb,
+ indev, skb->dev,
br_forward_finish);
}
if (!skb)
return NET_RX_DROP;
- return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
- netif_receive_skb);
+ return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, NULL, skb,
+ indev, NULL,
+ netif_receive_skb_sk);
}
static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
}
/* note: already called with rcu_read_lock */
-int br_handle_frame_finish(struct sk_buff *skb)
+int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb)
{
const unsigned char *dest = eth_hdr(skb)->h_dest;
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
EXPORT_SYMBOL_GPL(br_handle_frame_finish);
/* note: already called with rcu_read_lock */
-static int br_handle_local_finish(struct sk_buff *skb)
+static int br_handle_local_finish(struct sock *sk, struct sk_buff *skb)
{
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
u16 vid = 0;
}
/* Deliver packet to local host only */
- if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
- NULL, br_handle_local_finish)) {
+ if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, NULL, skb,
+ skb->dev, NULL, br_handle_local_finish)) {
return RX_HANDLER_CONSUMED; /* consumed by filter */
} else {
*pskb = skb;
if (ether_addr_equal(p->br->dev->dev_addr, dest))
skb->pkt_type = PACKET_HOST;
- NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ NF_HOOK(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, NULL, skb,
+ skb->dev, NULL,
br_handle_frame_finish);
break;
default:
if (port) {
skb->dev = port->dev;
- NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
+ NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
+ NULL, skb->dev,
br_dev_queue_push_xmit);
} else {
br_multicast_select_own_querier(br, ip, skb);
pppoe_proto(skb) == htons(PPP_IPV6) && \
brnf_filter_pppoe_tagged)
+/* largest possible L2 header, see br_nf_dev_queue_xmit() */
+#define NF_BRIDGE_MAX_MAC_HEADER_LENGTH (PPPOE_SES_HLEN + ETH_HLEN)
+
+#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
+struct brnf_frag_data {
+ char mac[NF_BRIDGE_MAX_MAC_HEADER_LENGTH];
+ u8 encap_size;
+ u8 size;
+};
+
+static DEFINE_PER_CPU(struct brnf_frag_data, brnf_frag_data_storage);
+#endif
+
+static struct nf_bridge_info *nf_bridge_info_get(const struct sk_buff *skb)
+{
+ return skb->nf_bridge;
+}
+
static inline struct rtable *bridge_parent_rtable(const struct net_device *dev)
{
struct net_bridge_port *port;
skb->network_header += len;
}
-static inline void nf_bridge_save_header(struct sk_buff *skb)
-{
- int header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
-
- skb_copy_from_linear_data_offset(skb, -header_size,
- skb->nf_bridge->data, header_size);
-}
-
/* When handing a packet over to the IP layer
* check whether we have a skb that is in the
* expected format
static void nf_bridge_update_protocol(struct sk_buff *skb)
{
- if (skb->nf_bridge->mask & BRNF_8021Q)
+ switch (skb->nf_bridge->orig_proto) {
+ case BRNF_PROTO_8021Q:
skb->protocol = htons(ETH_P_8021Q);
- else if (skb->nf_bridge->mask & BRNF_PPPoE)
+ break;
+ case BRNF_PROTO_PPPOE:
skb->protocol = htons(ETH_P_PPP_SES);
+ break;
+ case BRNF_PROTO_UNCHANGED:
+ break;
+ }
}
/* PF_BRIDGE/PRE_ROUTING *********************************************/
/* Undo the changes made for ip6tables PREROUTING and continue the
* bridge PRE_ROUTING hook. */
-static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
+static int br_nf_pre_routing_finish_ipv6(struct sock *sk, struct sk_buff *skb)
{
- struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+ struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
struct rtable *rt;
- if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ if (nf_bridge->pkt_otherhost) {
skb->pkt_type = PACKET_OTHERHOST;
- nf_bridge->mask ^= BRNF_PKT_TYPE;
+ nf_bridge->pkt_otherhost = false;
}
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
skb->dev = nf_bridge->physindev;
nf_bridge_update_protocol(skb);
nf_bridge_push_encap_header(skb);
- NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
+ skb->dev, NULL,
br_handle_frame_finish, 1);
return 0;
* don't, we use the neighbour framework to find out. In both cases, we make
* sure that br_handle_frame_finish() is called afterwards.
*/
-static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
+static int br_nf_pre_routing_finish_bridge(struct sock *sk, struct sk_buff *skb)
{
- struct nf_bridge_info *nf_bridge = skb->nf_bridge;
struct neighbour *neigh;
struct dst_entry *dst;
dst = skb_dst(skb);
neigh = dst_neigh_lookup_skb(dst, skb);
if (neigh) {
+ struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
int ret;
if (neigh->hh.hh_len) {
neigh_hh_bridge(&neigh->hh, skb);
skb->dev = nf_bridge->physindev;
- ret = br_handle_frame_finish(skb);
+ ret = br_handle_frame_finish(sk, skb);
} else {
/* the neighbour function below overwrites the complete
* MAC header, so we save the Ethernet source address and
*/
skb_copy_from_linear_data_offset(skb,
-(ETH_HLEN-ETH_ALEN),
- skb->nf_bridge->data,
+ nf_bridge->neigh_header,
ETH_HLEN-ETH_ALEN);
/* tell br_dev_xmit to continue with forwarding */
nf_bridge->mask |= BRNF_BRIDGED_DNAT;
* device, we proceed as if ip_route_input() succeeded. If it differs from the
* logical bridge port or if ip_route_output_key() fails we drop the packet.
*/
-static int br_nf_pre_routing_finish(struct sk_buff *skb)
+static int br_nf_pre_routing_finish(struct sock *sk, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct iphdr *iph = ip_hdr(skb);
- struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+ struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
struct rtable *rt;
int err;
int frag_max_size;
frag_max_size = IPCB(skb)->frag_max_size;
BR_INPUT_SKB_CB(skb)->frag_max_size = frag_max_size;
- if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ if (nf_bridge->pkt_otherhost) {
skb->pkt_type = PACKET_OTHERHOST;
- nf_bridge->mask ^= BRNF_PKT_TYPE;
+ nf_bridge->pkt_otherhost = false;
}
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
if (dnat_took_place(skb)) {
nf_bridge_push_encap_header(skb);
NF_HOOK_THRESH(NFPROTO_BRIDGE,
NF_BR_PRE_ROUTING,
- skb, skb->dev, NULL,
+ sk, skb, skb->dev, NULL,
br_nf_pre_routing_finish_bridge,
1);
return 0;
skb->dev = nf_bridge->physindev;
nf_bridge_update_protocol(skb);
nf_bridge_push_encap_header(skb);
- NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+ NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, sk, skb,
+ skb->dev, NULL,
br_handle_frame_finish, 1);
return 0;
/* Some common code for IPv4/IPv6 */
static struct net_device *setup_pre_routing(struct sk_buff *skb)
{
- struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+ struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
if (skb->pkt_type == PACKET_OTHERHOST) {
skb->pkt_type = PACKET_HOST;
- nf_bridge->mask |= BRNF_PKT_TYPE;
+ nf_bridge->pkt_otherhost = true;
}
nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
nf_bridge->physindev = skb->dev;
skb->dev = brnf_get_logical_dev(skb, skb->dev);
+
if (skb->protocol == htons(ETH_P_8021Q))
- nf_bridge->mask |= BRNF_8021Q;
+ nf_bridge->orig_proto = BRNF_PROTO_8021Q;
else if (skb->protocol == htons(ETH_P_PPP_SES))
- nf_bridge->mask |= BRNF_PPPoE;
+ nf_bridge->orig_proto = BRNF_PROTO_PPPOE;
/* Must drop socket now because of tproxy. */
skb_orphan(skb);
* to ip6tables, which doesn't support NAT, so things are fairly simple. */
static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
const struct ipv6hdr *hdr;
u32 pkt_len;
return NF_DROP;
skb->protocol = htons(ETH_P_IPV6);
- NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+ NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->sk, skb,
+ skb->dev, NULL,
br_nf_pre_routing_finish_ipv6);
return NF_STOLEN;
* address to be able to detect DNAT afterwards. */
static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct net_bridge_port *p;
struct net_bridge *br;
if (unlikely(!pskb_may_pull(skb, len)))
return NF_DROP;
- p = br_port_get_rcu(in);
+ p = br_port_get_rcu(state->in);
if (p == NULL)
return NF_DROP;
br = p->br;
return NF_ACCEPT;
nf_bridge_pull_encap_header_rcsum(skb);
- return br_nf_pre_routing_ipv6(ops, skb, in, out, okfn);
+ return br_nf_pre_routing_ipv6(ops, skb, state);
}
if (!brnf_call_iptables && !br->nf_call_iptables)
skb->protocol = htons(ETH_P_IP);
- NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+ NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, state->sk, skb,
+ skb->dev, NULL,
br_nf_pre_routing_finish);
return NF_STOLEN;
* prevent this from happening. */
static unsigned int br_nf_local_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
br_drop_fake_rtable(skb);
return NF_ACCEPT;
}
/* PF_BRIDGE/FORWARD *************************************************/
-static int br_nf_forward_finish(struct sk_buff *skb)
+static int br_nf_forward_finish(struct sock *sk, struct sk_buff *skb)
{
- struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+ struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
struct net_device *in;
if (!IS_ARP(skb) && !IS_VLAN_ARP(skb)) {
+ int frag_max_size;
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ frag_max_size = IPCB(skb)->frag_max_size;
+ BR_INPUT_SKB_CB(skb)->frag_max_size = frag_max_size;
+ }
+
in = nf_bridge->physindev;
- if (nf_bridge->mask & BRNF_PKT_TYPE) {
+ if (nf_bridge->pkt_otherhost) {
skb->pkt_type = PACKET_OTHERHOST;
- nf_bridge->mask ^= BRNF_PKT_TYPE;
+ nf_bridge->pkt_otherhost = false;
}
nf_bridge_update_protocol(skb);
} else {
}
nf_bridge_push_encap_header(skb);
- NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, skb, in,
- skb->dev, br_forward_finish, 1);
+ NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_FORWARD, sk, skb,
+ in, skb->dev, br_forward_finish, 1);
return 0;
}
* bridge ports. */
static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct nf_bridge_info *nf_bridge;
struct net_device *parent;
if (!nf_bridge_unshare(skb))
return NF_DROP;
- parent = bridge_parent(out);
+ nf_bridge = nf_bridge_info_get(skb);
+ if (!nf_bridge)
+ return NF_DROP;
+
+ parent = bridge_parent(state->out);
if (!parent)
return NF_DROP;
nf_bridge_pull_encap_header(skb);
- nf_bridge = skb->nf_bridge;
if (skb->pkt_type == PACKET_OTHERHOST) {
skb->pkt_type = PACKET_HOST;
- nf_bridge->mask |= BRNF_PKT_TYPE;
+ nf_bridge->pkt_otherhost = true;
}
- if (pf == NFPROTO_IPV4 && br_parse_ip_options(skb))
- return NF_DROP;
+ if (pf == NFPROTO_IPV4) {
+ int frag_max = BR_INPUT_SKB_CB(skb)->frag_max_size;
+
+ if (br_parse_ip_options(skb))
+ return NF_DROP;
+
+ IPCB(skb)->frag_max_size = frag_max;
+ }
nf_bridge->physoutdev = skb->dev;
if (pf == NFPROTO_IPV4)
else
skb->protocol = htons(ETH_P_IPV6);
- NF_HOOK(pf, NF_INET_FORWARD, skb, brnf_get_logical_dev(skb, in), parent,
- br_nf_forward_finish);
+ NF_HOOK(pf, NF_INET_FORWARD, NULL, skb,
+ brnf_get_logical_dev(skb, state->in),
+ parent, br_nf_forward_finish);
return NF_STOLEN;
}
static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct net_bridge_port *p;
struct net_bridge *br;
struct net_device **d = (struct net_device **)(skb->cb);
- p = br_port_get_rcu(out);
+ p = br_port_get_rcu(state->out);
if (p == NULL)
return NF_ACCEPT;
br = p->br;
nf_bridge_push_encap_header(skb);
return NF_ACCEPT;
}
- *d = (struct net_device *)in;
- NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, skb, (struct net_device *)in,
- (struct net_device *)out, br_nf_forward_finish);
+ *d = state->in;
+ NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, state->sk, skb,
+ state->in, state->out, br_nf_forward_finish);
return NF_STOLEN;
}
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4)
-static bool nf_bridge_copy_header(struct sk_buff *skb)
+static int br_nf_push_frag_xmit(struct sock *sk, struct sk_buff *skb)
{
+ struct brnf_frag_data *data;
int err;
- unsigned int header_size;
-
- nf_bridge_update_protocol(skb);
- header_size = ETH_HLEN + nf_bridge_encap_header_len(skb);
- err = skb_cow_head(skb, header_size);
- if (err)
- return false;
- skb_copy_to_linear_data_offset(skb, -header_size,
- skb->nf_bridge->data, header_size);
- __skb_push(skb, nf_bridge_encap_header_len(skb));
- return true;
-}
+ data = this_cpu_ptr(&brnf_frag_data_storage);
+ err = skb_cow_head(skb, data->size);
-static int br_nf_push_frag_xmit(struct sk_buff *skb)
-{
- if (!nf_bridge_copy_header(skb)) {
+ if (err) {
kfree_skb(skb);
return 0;
}
- return br_dev_queue_push_xmit(skb);
+ skb_copy_to_linear_data_offset(skb, -data->size, data->mac, data->size);
+ __skb_push(skb, data->encap_size);
+
+ return br_dev_queue_push_xmit(sk, skb);
}
-static int br_nf_dev_queue_xmit(struct sk_buff *skb)
+static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
{
int ret;
int frag_max_size;
unsigned int mtu_reserved;
if (skb_is_gso(skb) || skb->protocol != htons(ETH_P_IP))
- return br_dev_queue_push_xmit(skb);
+ return br_dev_queue_push_xmit(sk, skb);
mtu_reserved = nf_bridge_mtu_reduction(skb);
/* This is wrong! We should preserve the original fragment
* boundaries by preserving frag_list rather than refragmenting.
*/
if (skb->len + mtu_reserved > skb->dev->mtu) {
+ struct brnf_frag_data *data;
+
frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size;
if (br_parse_ip_options(skb))
/* Drop invalid packet */
return NF_DROP;
IPCB(skb)->frag_max_size = frag_max_size;
- ret = ip_fragment(skb, br_nf_push_frag_xmit);
- } else
- ret = br_dev_queue_push_xmit(skb);
+
+ nf_bridge_update_protocol(skb);
+
+ data = this_cpu_ptr(&brnf_frag_data_storage);
+ data->encap_size = nf_bridge_encap_header_len(skb);
+ data->size = ETH_HLEN + data->encap_size;
+
+ skb_copy_from_linear_data_offset(skb, -data->size, data->mac,
+ data->size);
+
+ ret = ip_fragment(sk, skb, br_nf_push_frag_xmit);
+ } else {
+ ret = br_dev_queue_push_xmit(sk, skb);
+ }
return ret;
}
#else
-static int br_nf_dev_queue_xmit(struct sk_buff *skb)
+static int br_nf_dev_queue_xmit(struct sock *sk, struct sk_buff *skb)
{
- return br_dev_queue_push_xmit(skb);
+ return br_dev_queue_push_xmit(sk, skb);
}
#endif
/* PF_BRIDGE/POST_ROUTING ********************************************/
static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+ struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
struct net_device *realoutdev = bridge_parent(skb->dev);
u_int8_t pf;
* about the value of skb->pkt_type. */
if (skb->pkt_type == PACKET_OTHERHOST) {
skb->pkt_type = PACKET_HOST;
- nf_bridge->mask |= BRNF_PKT_TYPE;
+ nf_bridge->pkt_otherhost = true;
}
nf_bridge_pull_encap_header(skb);
- nf_bridge_save_header(skb);
if (pf == NFPROTO_IPV4)
skb->protocol = htons(ETH_P_IP);
else
skb->protocol = htons(ETH_P_IPV6);
- NF_HOOK(pf, NF_INET_POST_ROUTING, skb, NULL, realoutdev,
+ NF_HOOK(pf, NF_INET_POST_ROUTING, state->sk, skb,
+ NULL, realoutdev,
br_nf_dev_queue_xmit);
return NF_STOLEN;
* for the second time. */
static unsigned int ip_sabotage_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
if (skb->nf_bridge &&
!(skb->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)) {
*/
static void br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb)
{
- struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+ struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
skb_pull(skb, ETH_HLEN);
nf_bridge->mask &= ~BRNF_BRIDGED_DNAT;
- skb_copy_to_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN),
- skb->nf_bridge->data, ETH_HLEN-ETH_ALEN);
+ BUILD_BUG_ON(sizeof(nf_bridge->neigh_header) != (ETH_HLEN - ETH_ALEN));
+
+ skb_copy_to_linear_data_offset(skb, -(ETH_HLEN - ETH_ALEN),
+ nf_bridge->neigh_header,
+ ETH_HLEN - ETH_ALEN);
skb->dev = nf_bridge->physindev;
- br_handle_frame_finish(skb);
+ br_handle_frame_finish(NULL, skb);
}
static int br_nf_dev_xmit(struct sk_buff *skb)
nla_put_u8(skb, IFLA_OPERSTATE, operstate) ||
(dev->addr_len &&
nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
- (dev->ifindex != dev->iflink &&
- nla_put_u32(skb, IFLA_LINK, dev->iflink)))
+ (dev->ifindex != dev_get_iflink(dev) &&
+ nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))))
goto nla_put_failure;
if (event == RTM_NEWLINK && port) {
/* br_forward.c */
void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb);
-int br_dev_queue_push_xmit(struct sk_buff *skb);
+int br_dev_queue_push_xmit(struct sock *sk, struct sk_buff *skb);
void br_forward(const struct net_bridge_port *to,
struct sk_buff *skb, struct sk_buff *skb0);
-int br_forward_finish(struct sk_buff *skb);
+int br_forward_finish(struct sock *sk, struct sk_buff *skb);
void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast);
void br_flood_forward(struct net_bridge *br, struct sk_buff *skb,
struct sk_buff *skb2, bool unicast);
void br_manage_promisc(struct net_bridge *br);
/* br_input.c */
-int br_handle_frame_finish(struct sk_buff *skb);
+int br_handle_frame_finish(struct sock *sk, struct sk_buff *skb);
rx_handler_result_t br_handle_frame(struct sk_buff **pskb);
static inline bool br_rx_handler_check_rcu(const struct net_device *dev)
skb_reset_mac_header(skb);
- NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
- dev_queue_xmit);
+ NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, NULL, skb,
+ NULL, skb->dev,
+ dev_queue_xmit_sk);
}
static inline void br_set_ticks(unsigned char *dest, int j)
static unsigned int
ebt_in_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return ebt_do_table(ops->hooknum, skb, in, out,
- dev_net(in)->xt.frame_filter);
+ return ebt_do_table(ops->hooknum, skb, state->in, state->out,
+ dev_net(state->in)->xt.frame_filter);
}
static unsigned int
ebt_out_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return ebt_do_table(ops->hooknum, skb, in, out,
- dev_net(out)->xt.frame_filter);
+ return ebt_do_table(ops->hooknum, skb, state->in, state->out,
+ dev_net(state->out)->xt.frame_filter);
}
static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
static unsigned int
ebt_nat_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return ebt_do_table(ops->hooknum, skb, in, out,
- dev_net(in)->xt.frame_nat);
+ return ebt_do_table(ops->hooknum, skb, state->in, state->out,
+ dev_net(state->in)->xt.frame_nat);
}
static unsigned int
ebt_nat_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return ebt_do_table(ops->hooknum, skb, in, out,
- dev_net(out)->xt.frame_nat);
+ return ebt_do_table(ops->hooknum, skb, state->in, state->out,
+ dev_net(state->out)->xt.frame_nat);
}
static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
static inline void nft_bridge_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out)
+ const struct nf_hook_state *state)
{
if (nft_bridge_iphdr_validate(skb))
- nft_set_pktinfo_ipv4(pkt, ops, skb, in, out);
+ nft_set_pktinfo_ipv4(pkt, ops, skb, state);
else
- nft_set_pktinfo(pkt, ops, skb, in, out);
+ nft_set_pktinfo(pkt, ops, skb, state);
}
static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
- const struct nf_hook_ops *ops,
- struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out)
+ const struct nf_hook_ops *ops,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
{
#if IS_ENABLED(CONFIG_IPV6)
if (nft_bridge_ip6hdr_validate(skb) &&
- nft_set_pktinfo_ipv6(pkt, ops, skb, in, out) == 0)
+ nft_set_pktinfo_ipv6(pkt, ops, skb, state) == 0)
return;
#endif
- nft_set_pktinfo(pkt, ops, skb, in, out);
+ nft_set_pktinfo(pkt, ops, skb, state);
}
static unsigned int
nft_do_chain_bridge(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct nft_pktinfo pkt;
switch (eth_hdr(skb)->h_proto) {
case htons(ETH_P_IP):
- nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+ nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, state);
break;
case htons(ETH_P_IPV6):
- nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
+ nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, state);
break;
default:
- nft_set_pktinfo(&pkt, ops, skb, in, out);
+ nft_set_pktinfo(&pkt, ops, skb, state);
break;
}
if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
goto nla_put_failure;
break;
+ default:
+ break;
}
return 0;
* storing the single filter in dfilter, to avoid using dynamic memory.
*/
+struct uniqframe {
+ ktime_t tstamp;
+ const struct sk_buff *skb;
+ unsigned int join_rx_count;
+};
+
struct raw_sock {
struct sock sk;
int bound;
int loopback;
int recv_own_msgs;
int fd_frames;
+ int join_filters;
int count; /* number of active filters */
struct can_filter dfilter; /* default/single filter */
struct can_filter *filter; /* pointer to filter(s) */
can_err_mask_t err_mask;
+ struct uniqframe __percpu *uniq;
};
/*
if (!ro->fd_frames && oskb->len != CAN_MTU)
return;
+ /* eliminate multiple filter matches for the same skb */
+ if (this_cpu_ptr(ro->uniq)->skb == oskb &&
+ ktime_equal(this_cpu_ptr(ro->uniq)->tstamp, oskb->tstamp)) {
+ if (ro->join_filters) {
+ this_cpu_inc(ro->uniq->join_rx_count);
+ /* drop frame until all enabled filters matched */
+ if (this_cpu_ptr(ro->uniq)->join_rx_count < ro->count)
+ return;
+ } else {
+ return;
+ }
+ } else {
+ this_cpu_ptr(ro->uniq)->skb = oskb;
+ this_cpu_ptr(ro->uniq)->tstamp = oskb->tstamp;
+ this_cpu_ptr(ro->uniq)->join_rx_count = 1;
+ /* drop first frame to check all enabled filters? */
+ if (ro->join_filters && ro->count > 1)
+ return;
+ }
+
/* clone the given skb to be able to enqueue it into the rcv queue */
skb = skb_clone(oskb, GFP_ATOMIC);
if (!skb)
ro->loopback = 1;
ro->recv_own_msgs = 0;
ro->fd_frames = 0;
+ ro->join_filters = 0;
+
+ /* alloc_percpu provides zero'ed memory */
+ ro->uniq = alloc_percpu(struct uniqframe);
+ if (unlikely(!ro->uniq))
+ return -ENOMEM;
/* set notifier */
ro->notifier.notifier_call = raw_notifier;
ro->ifindex = 0;
ro->bound = 0;
ro->count = 0;
+ free_percpu(ro->uniq);
sock_orphan(sk);
sock->sk = NULL;
break;
+ case CAN_RAW_JOIN_FILTERS:
+ if (optlen != sizeof(ro->join_filters))
+ return -EINVAL;
+
+ if (copy_from_user(&ro->join_filters, optval, optlen))
+ return -EFAULT;
+
+ break;
+
default:
return -ENOPROTOOPT;
}
val = &ro->fd_frames;
break;
+ case CAN_RAW_JOIN_FILTERS:
+ if (len > sizeof(int))
+ len = sizeof(int);
+ val = &ro->join_filters;
+ break;
+
default:
return -ENOPROTOOPT;
}
*******************************************************************************/
+/**
+ * dev_get_iflink - get 'iflink' value of a interface
+ * @dev: targeted interface
+ *
+ * Indicates the ifindex the interface is linked to.
+ * Physical interfaces have the same 'ifindex' and 'iflink' values.
+ */
+
+int dev_get_iflink(const struct net_device *dev)
+{
+ if (dev->netdev_ops && dev->netdev_ops->ndo_get_iflink)
+ return dev->netdev_ops->ndo_get_iflink(dev);
+
+ /* If dev->rtnl_link_ops is set, it's a virtual interface. */
+ if (dev->rtnl_link_ops)
+ return 0;
+
+ return dev->ifindex;
+}
+EXPORT_SYMBOL(dev_get_iflink);
+
/**
* __dev_get_by_name - find a device by its name
* @net: the applicable net namespace
#define skb_update_prio(skb)
#endif
-static DEFINE_PER_CPU(int, xmit_recursion);
+DEFINE_PER_CPU(int, xmit_recursion);
+EXPORT_SYMBOL(xmit_recursion);
+
#define RECURSION_LIMIT 10
/**
* dev_loopback_xmit - loop back @skb
* @skb: buffer to transmit
*/
-int dev_loopback_xmit(struct sk_buff *skb)
+int dev_loopback_xmit(struct sock *sk, struct sk_buff *skb)
{
skb_reset_mac_header(skb);
__skb_pull(skb, skb_network_offset(skb));
return rc;
}
-int dev_queue_xmit(struct sk_buff *skb)
+int dev_queue_xmit_sk(struct sock *sk, struct sk_buff *skb)
{
return __dev_queue_xmit(skb, NULL);
}
-EXPORT_SYMBOL(dev_queue_xmit);
+EXPORT_SYMBOL(dev_queue_xmit_sk);
int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv)
{
* NET_RX_SUCCESS: no congestion
* NET_RX_DROP: packet was dropped
*/
-int netif_receive_skb(struct sk_buff *skb)
+int netif_receive_skb_sk(struct sock *sk, struct sk_buff *skb)
{
trace_netif_receive_skb_entry(skb);
return netif_receive_skb_internal(skb);
}
-EXPORT_SYMBOL(netif_receive_skb);
+EXPORT_SYMBOL(netif_receive_skb_sk);
/* Network device is going away, flush any packets still pending
* Called with irqs disabled.
spin_lock_init(&dev->addr_list_lock);
netdev_set_addr_lockdep_class(dev);
- dev->iflink = -1;
-
ret = dev_get_valid_name(net, dev, dev->name);
if (ret < 0)
goto out;
else if (__dev_get_by_index(net, dev->ifindex))
goto err_uninit;
- if (dev->iflink == -1)
- dev->iflink = dev->ifindex;
-
/* Transfer changeable features to wanted_features and enable
* software offloads (GSO and GRO).
*/
dev_net_set(dev, net);
/* If there is an ifindex conflict assign a new one */
- if (__dev_get_by_index(net, dev->ifindex)) {
- int iflink = (dev->iflink == dev->ifindex);
+ if (__dev_get_by_index(net, dev->ifindex))
dev->ifindex = dev_new_index(net);
- if (iflink)
- dev->iflink = dev->ifindex;
- }
/* Send a netdev-add uevent to the new namespace */
kobject_uevent(&dev->dev.kobj, KOBJ_ADD);
spin_lock(&net->rules_mod_lock);
list_del_rcu(&ops->list);
- fib_rules_cleanup_ops(ops);
spin_unlock(&net->rules_mod_lock);
+ fib_rules_cleanup_ops(ops);
kfree_rcu(ops, rcu);
}
EXPORT_SYMBOL_GPL(fib_rules_unregister);
return 0;
}
-static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
+#define BPF_RECOMPUTE_CSUM(flags) ((flags) & 1)
+
+static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)
{
struct sk_buff *skb = (struct sk_buff *) (long) r1;
unsigned int offset = (unsigned int) r2;
*
* so check for invalid 'offset' and too large 'len'
*/
- if (offset > 0xffff || len > sizeof(buf))
+ if (unlikely(offset > 0xffff || len > sizeof(buf)))
return -EFAULT;
if (skb_cloned(skb) && !skb_clone_writable(skb, offset + len))
if (unlikely(!ptr))
return -EFAULT;
- skb_postpull_rcsum(skb, ptr, len);
+ if (BPF_RECOMPUTE_CSUM(flags))
+ skb_postpull_rcsum(skb, ptr, len);
memcpy(ptr, from, len);
/* skb_store_bits cannot return -EFAULT here */
skb_store_bits(skb, offset, ptr, len);
- if (skb->ip_summed == CHECKSUM_COMPLETE)
+ if (BPF_RECOMPUTE_CSUM(flags) && skb->ip_summed == CHECKSUM_COMPLETE)
skb->csum = csum_add(skb->csum, csum_partial(ptr, len, 0));
return 0;
}
.arg2_type = ARG_ANYTHING,
.arg3_type = ARG_PTR_TO_STACK,
.arg4_type = ARG_CONST_STACK_SIZE,
+ .arg5_type = ARG_ANYTHING,
+};
+
+#define BPF_HEADER_FIELD_SIZE(flags) ((flags) & 0x0f)
+#define BPF_IS_PSEUDO_HEADER(flags) ((flags) & 0x10)
+
+static u64 bpf_l3_csum_replace(u64 r1, u64 offset, u64 from, u64 to, u64 flags)
+{
+ struct sk_buff *skb = (struct sk_buff *) (long) r1;
+ __sum16 sum, *ptr;
+
+ if (unlikely(offset > 0xffff))
+ return -EFAULT;
+
+ if (skb_cloned(skb) && !skb_clone_writable(skb, offset + sizeof(sum)))
+ return -EFAULT;
+
+ ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
+ if (unlikely(!ptr))
+ return -EFAULT;
+
+ switch (BPF_HEADER_FIELD_SIZE(flags)) {
+ case 2:
+ csum_replace2(ptr, from, to);
+ break;
+ case 4:
+ csum_replace4(ptr, from, to);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ptr == &sum)
+ /* skb_store_bits guaranteed to not return -EFAULT here */
+ skb_store_bits(skb, offset, ptr, sizeof(sum));
+
+ return 0;
+}
+
+const struct bpf_func_proto bpf_l3_csum_replace_proto = {
+ .func = bpf_l3_csum_replace,
+ .gpl_only = false,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_CTX,
+ .arg2_type = ARG_ANYTHING,
+ .arg3_type = ARG_ANYTHING,
+ .arg4_type = ARG_ANYTHING,
+ .arg5_type = ARG_ANYTHING,
+};
+
+static u64 bpf_l4_csum_replace(u64 r1, u64 offset, u64 from, u64 to, u64 flags)
+{
+ struct sk_buff *skb = (struct sk_buff *) (long) r1;
+ u32 is_pseudo = BPF_IS_PSEUDO_HEADER(flags);
+ __sum16 sum, *ptr;
+
+ if (unlikely(offset > 0xffff))
+ return -EFAULT;
+
+ if (skb_cloned(skb) && !skb_clone_writable(skb, offset + sizeof(sum)))
+ return -EFAULT;
+
+ ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
+ if (unlikely(!ptr))
+ return -EFAULT;
+
+ switch (BPF_HEADER_FIELD_SIZE(flags)) {
+ case 2:
+ inet_proto_csum_replace2(ptr, skb, from, to, is_pseudo);
+ break;
+ case 4:
+ inet_proto_csum_replace4(ptr, skb, from, to, is_pseudo);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ptr == &sum)
+ /* skb_store_bits guaranteed to not return -EFAULT here */
+ skb_store_bits(skb, offset, ptr, sizeof(sum));
+
+ return 0;
+}
+
+const struct bpf_func_proto bpf_l4_csum_replace_proto = {
+ .func = bpf_l4_csum_replace,
+ .gpl_only = false,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_PTR_TO_CTX,
+ .arg2_type = ARG_ANYTHING,
+ .arg3_type = ARG_ANYTHING,
+ .arg4_type = ARG_ANYTHING,
+ .arg5_type = ARG_ANYTHING,
};
static const struct bpf_func_proto *
switch (func_id) {
case BPF_FUNC_skb_store_bytes:
return &bpf_skb_store_bytes_proto;
+ case BPF_FUNC_l3_csum_replace:
+ return &bpf_l3_csum_replace_proto;
+ case BPF_FUNC_l4_csum_replace:
+ return &bpf_l4_csum_replace_proto;
default:
return sk_filter_func_proto(func_id);
}
offsetof(struct sk_buff, vlan_proto));
break;
+ case offsetof(struct __sk_buff, priority):
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, priority) != 4);
+
+ *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg,
+ offsetof(struct sk_buff, priority));
+ break;
+
case offsetof(struct __sk_buff, mark):
return convert_skb_access(SKF_AD_MARK, dst_reg, src_reg, insn);
static unsigned char default_operstate(const struct net_device *dev)
{
if (!netif_carrier_ok(dev))
- return (dev->ifindex != dev->iflink ?
+ return (dev->ifindex != dev_get_iflink(dev) ?
IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
if (netif_dormant(dev))
if (!netif_running(dev))
return false;
- if (dev->ifindex != dev->iflink)
+ if (dev->ifindex != dev_get_iflink(dev))
return true;
if (dev->priv_flags & IFF_TEAM_PORT)
NETDEVICE_SHOW_RO(dev_port, fmt_dec);
NETDEVICE_SHOW_RO(addr_assign_type, fmt_dec);
NETDEVICE_SHOW_RO(addr_len, fmt_dec);
-NETDEVICE_SHOW_RO(iflink, fmt_dec);
NETDEVICE_SHOW_RO(ifindex, fmt_dec);
NETDEVICE_SHOW_RO(type, fmt_dec);
NETDEVICE_SHOW_RO(link_mode, fmt_dec);
+static ssize_t iflink_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct net_device *ndev = to_net_dev(dev);
+
+ return sprintf(buf, fmt_dec, dev_get_iflink(ndev));
+}
+static DEVICE_ATTR_RO(iflink);
+
static ssize_t format_name_assign_type(const struct net_device *dev, char *buf)
{
return sprintf(buf, fmt_dec, dev->name_assign_type);
}
}
+static void rtnl_net_notifyid(struct net *net, struct net *peer, int cmd,
+ int id);
static int alloc_netid(struct net *net, struct net *peer, int reqid)
{
- int min = 0, max = 0;
+ int min = 0, max = 0, id;
ASSERT_RTNL();
max = reqid + 1;
}
- return idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL);
+ id = idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL);
+ if (id >= 0)
+ rtnl_net_notifyid(net, peer, RTM_NEWNSID, id);
+
+ return id;
}
/* This function is used by idr_for_each(). If net is equal to peer, the
*/
int peernet2id(struct net *net, struct net *peer)
{
- int id = __peernet2id(net, peer, true);
+ bool alloc = atomic_read(&peer->count) == 0 ? false : true;
+ int id;
+ id = __peernet2id(net, peer, alloc);
return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED;
}
EXPORT_SYMBOL(peernet2id);
for_each_net(tmp) {
int id = __peernet2id(tmp, net, false);
- if (id >= 0)
+ if (id >= 0) {
+ rtnl_net_notifyid(tmp, net, RTM_DELNSID, id);
idr_remove(&tmp->netns_ids, id);
+ }
}
idr_destroy(&net->netns_ids);
}
static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags,
- int cmd, struct net *net, struct net *peer)
+ int cmd, struct net *net, struct net *peer,
+ int nsid)
{
struct nlmsghdr *nlh;
struct rtgenmsg *rth;
rth = nlmsg_data(nlh);
rth->rtgen_family = AF_UNSPEC;
- id = __peernet2id(net, peer, false);
- if (id < 0)
- id = NETNSA_NSID_NOT_ASSIGNED;
+ if (nsid >= 0) {
+ id = nsid;
+ } else {
+ id = __peernet2id(net, peer, false);
+ if (id < 0)
+ id = NETNSA_NSID_NOT_ASSIGNED;
+ }
if (nla_put_s32(skb, NETNSA_NSID, id))
goto nla_put_failure;
struct net *net = sock_net(skb->sk);
struct nlattr *tb[NETNSA_MAX + 1];
struct sk_buff *msg;
- int err = -ENOBUFS;
struct net *peer;
+ int err;
err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX,
rtnl_net_policy);
}
err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
- RTM_GETNSID, net, peer);
+ RTM_GETNSID, net, peer, -1);
if (err < 0)
goto err_out;
return err;
}
+struct rtnl_net_dump_cb {
+ struct net *net;
+ struct sk_buff *skb;
+ struct netlink_callback *cb;
+ int idx;
+ int s_idx;
+};
+
+static int rtnl_net_dumpid_one(int id, void *peer, void *data)
+{
+ struct rtnl_net_dump_cb *net_cb = (struct rtnl_net_dump_cb *)data;
+ int ret;
+
+ if (net_cb->idx < net_cb->s_idx)
+ goto cont;
+
+ ret = rtnl_net_fill(net_cb->skb, NETLINK_CB(net_cb->cb->skb).portid,
+ net_cb->cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ RTM_NEWNSID, net_cb->net, peer, id);
+ if (ret < 0)
+ return ret;
+
+cont:
+ net_cb->idx++;
+ return 0;
+}
+
+static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct rtnl_net_dump_cb net_cb = {
+ .net = net,
+ .skb = skb,
+ .cb = cb,
+ .idx = 0,
+ .s_idx = cb->args[0],
+ };
+
+ ASSERT_RTNL();
+
+ idr_for_each(&net->netns_ids, rtnl_net_dumpid_one, &net_cb);
+
+ cb->args[0] = net_cb.idx;
+ return skb->len;
+}
+
+static void rtnl_net_notifyid(struct net *net, struct net *peer, int cmd,
+ int id)
+{
+ struct sk_buff *msg;
+ int err = -ENOMEM;
+
+ msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL);
+ if (!msg)
+ goto out;
+
+ err = rtnl_net_fill(msg, 0, 0, 0, cmd, net, peer, id);
+ if (err < 0)
+ goto err_out;
+
+ rtnl_notify(msg, net, 0, RTNLGRP_NSID, NULL, 0);
+ return;
+
+err_out:
+ nlmsg_free(msg);
+out:
+ rtnl_set_sk_err(net, RTNLGRP_NSID, err);
+}
+
static int __init net_ns_init(void)
{
struct net_generic *ng;
register_pernet_subsys(&net_ns_ops);
rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL, NULL);
- rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, NULL, NULL);
+ rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, rtnl_net_dumpid,
+ NULL);
return 0;
}
#ifdef CONFIG_RPS
nla_put_u32(skb, IFLA_NUM_RX_QUEUES, dev->num_rx_queues) ||
#endif
- (dev->ifindex != dev->iflink &&
- nla_put_u32(skb, IFLA_LINK, dev->iflink)) ||
+ (dev->ifindex != dev_get_iflink(dev) &&
+ nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))) ||
(upper_dev &&
nla_put_u32(skb, IFLA_MASTER, upper_dev->ifindex)) ||
nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
EXPORT_SYMBOL(rtnl_configure_link);
struct net_device *rtnl_create_link(struct net *net,
- char *ifname, unsigned char name_assign_type,
+ const char *ifname, unsigned char name_assign_type,
const struct rtnl_link_ops *ops, struct nlattr *tb[])
{
int err;
struct ifinfomsg *ifm,
struct nlattr **tb)
{
- struct net_device *dev;
+ struct net_device *dev, *aux;
int err;
- for_each_netdev(net, dev) {
+ for_each_netdev_safe(net, dev, aux) {
if (dev->group == group) {
err = do_setlink(skb, dev, ifm, tb, NULL, 0);
if (err < 0)
static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
struct net_device *dev,
- u8 *addr, u32 pid, u32 seq,
+ u8 *addr, u16 vid, u32 pid, u32 seq,
int type, unsigned int flags,
int nlflags)
{
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr))
goto nla_put_failure;
+ if (vid)
+ if (nla_put(skb, NDA_VLAN, sizeof(u16), &vid))
+ goto nla_put_failure;
nlmsg_end(skb, nlh);
return 0;
return NLMSG_ALIGN(sizeof(struct ndmsg)) + nla_total_size(ETH_ALEN);
}
-static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, int type)
+static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, u16 vid, int type)
{
struct net *net = dev_net(dev);
struct sk_buff *skb;
if (!skb)
goto errout;
- err = nlmsg_populate_fdb_fill(skb, dev, addr, 0, 0, type, NTF_SELF, 0);
+ err = nlmsg_populate_fdb_fill(skb, dev, addr, vid,
+ 0, 0, type, NTF_SELF, 0);
if (err < 0) {
kfree_skb(skb);
goto errout;
nlh->nlmsg_flags);
if (!err) {
- rtnl_fdb_notify(dev, addr, RTM_NEWNEIGH);
+ rtnl_fdb_notify(dev, addr, vid, RTM_NEWNEIGH);
ndm->ndm_flags &= ~NTF_SELF;
}
}
err = ndo_dflt_fdb_del(ndm, tb, dev, addr, vid);
if (!err) {
- rtnl_fdb_notify(dev, addr, RTM_DELNEIGH);
+ rtnl_fdb_notify(dev, addr, vid, RTM_DELNEIGH);
ndm->ndm_flags &= ~NTF_SELF;
}
}
if (*idx < cb->args[0])
goto skip;
- err = nlmsg_populate_fdb_fill(skb, dev, ha->addr,
+ err = nlmsg_populate_fdb_fill(skb, dev, ha->addr, 0,
portid, seq,
RTM_NEWNEIGH, NTF_SELF,
NLM_F_MULTI);
{
struct net_device *dev;
struct nlattr *tb[IFLA_MAX+1];
- struct net_device *bdev = NULL;
struct net_device *br_dev = NULL;
const struct net_device_ops *ops = NULL;
const struct net_device_ops *cops = NULL;
return -ENODEV;
ops = br_dev->netdev_ops;
- bdev = br_dev;
}
for_each_netdev(net, dev) {
cops = br_dev->netdev_ops;
}
- bdev = dev;
} else {
if (dev != br_dev &&
!(dev->priv_flags & IFF_BRIDGE_PORT))
!(dev->priv_flags & IFF_EBRIDGE))
continue;
- bdev = br_dev;
cops = ops;
}
nla_put_u32(skb, IFLA_MASTER, br_dev->ifindex)) ||
(dev->addr_len &&
nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
- (dev->ifindex != dev->iflink &&
- nla_put_u32(skb, IFLA_LINK, dev->iflink)))
+ (dev->ifindex != dev_get_iflink(dev) &&
+ nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))))
goto nla_put_failure;
br_afspec = nla_nest_start(skb, IFLA_AF_SPEC);
}
EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
-
/**
* skb_partial_csum_set - set up and verify partial csum values for packet
* @skb: the skb to set
sock_reset_flag(sk, bit);
}
+bool sk_mc_loop(struct sock *sk)
+{
+ if (dev_recursion_level())
+ return false;
+ if (!sk)
+ return true;
+ switch (sk->sk_family) {
+ case AF_INET:
+ return inet_sk(sk)->mc_loop;
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ return inet6_sk(sk)->mc_loop;
+#endif
+ }
+ WARN_ON(1);
+ return true;
+}
+EXPORT_SYMBOL(sk_mc_loop);
+
/*
* This is meant for all protocols to use and covers goings on
* at the socket level. Everything here is generic.
return err;
}
-static int dn_neigh_output_packet(struct sk_buff *skb)
+static int dn_neigh_output_packet(struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct dn_route *rt = (struct dn_route *)dst;
/*
* For talking to broadcast devices: Ethernet & PPP
*/
-static int dn_long_output(struct neighbour *neigh, struct sk_buff *skb)
+static int dn_long_output(struct neighbour *neigh, struct sock *sk,
+ struct sk_buff *skb)
{
struct net_device *dev = neigh->dev;
int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3;
skb_reset_network_header(skb);
- return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, skb, NULL,
- neigh->dev, dn_neigh_output_packet);
+ return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb,
+ NULL, neigh->dev, dn_neigh_output_packet);
}
/*
* For talking to pointopoint and multidrop devices: DDCMP and X.25
*/
-static int dn_short_output(struct neighbour *neigh, struct sk_buff *skb)
+static int dn_short_output(struct neighbour *neigh, struct sock *sk,
+ struct sk_buff *skb)
{
struct net_device *dev = neigh->dev;
int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
skb_reset_network_header(skb);
- return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, skb, NULL,
- neigh->dev, dn_neigh_output_packet);
+ return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb,
+ NULL, neigh->dev, dn_neigh_output_packet);
}
/*
* Phase 3 output is the same as short output, execpt that
* it clears the area bits before transmission.
*/
-static int dn_phase3_output(struct neighbour *neigh, struct sk_buff *skb)
+static int dn_phase3_output(struct neighbour *neigh, struct sock *sk,
+ struct sk_buff *skb)
{
struct net_device *dev = neigh->dev;
int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
skb_reset_network_header(skb);
- return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, skb, NULL,
- neigh->dev, dn_neigh_output_packet);
+ return NF_HOOK(NFPROTO_DECNET, NF_DN_POST_ROUTING, sk, skb,
+ NULL, neigh->dev, dn_neigh_output_packet);
}
-int dn_to_neigh_output(struct sk_buff *skb)
+int dn_to_neigh_output(struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct dn_route *rt = (struct dn_route *) dst;
rcu_read_unlock();
if (dn->flags & DN_NDFLAG_P3)
- return dn_phase3_output(neigh, skb);
+ return dn_phase3_output(neigh, sk, skb);
if (use_long)
- return dn_long_output(neigh, skb);
+ return dn_long_output(neigh, sk, skb);
else
- return dn_short_output(neigh, skb);
+ return dn_short_output(neigh, sk, skb);
}
/*
/*
* Ethernet router hello message received
*/
-int dn_neigh_router_hello(struct sk_buff *skb)
+int dn_neigh_router_hello(struct sock *sk, struct sk_buff *skb)
{
struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data;
/*
* Endnode hello message received
*/
-int dn_neigh_endnode_hello(struct sk_buff *skb)
+int dn_neigh_endnode_hello(struct sock *sk, struct sk_buff *skb)
{
struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data;
struct neighbour *neigh;
return ret;
}
-static int dn_nsp_rx_packet(struct sk_buff *skb)
+static int dn_nsp_rx_packet(struct sock *sk2, struct sk_buff *skb)
{
struct dn_skb_cb *cb = DN_SKB_CB(skb);
struct sock *sk = NULL;
int dn_nsp_rx(struct sk_buff *skb)
{
- return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN, skb, skb->dev, NULL,
+ return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_IN, NULL, skb,
+ skb->dev, NULL,
dn_nsp_rx_packet);
}
*
* Returns: result of input function if route is found, error code otherwise
*/
-static int dn_route_rx_packet(struct sk_buff *skb)
+static int dn_route_rx_packet(struct sock *sk, struct sk_buff *skb)
{
struct dn_skb_cb *cb;
int err;
ptr++;
cb->hops = *ptr++; /* Visit Count */
- return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, skb, skb->dev, NULL,
+ return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, NULL, skb,
+ skb->dev, NULL,
dn_route_rx_packet);
drop_it:
ptr += 2;
cb->hops = *ptr & 0x3f;
- return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, skb, skb->dev, NULL,
+ return NF_HOOK(NFPROTO_DECNET, NF_DN_PRE_ROUTING, NULL, skb,
+ skb->dev, NULL,
dn_route_rx_packet);
drop_it:
return NET_RX_DROP;
}
-static int dn_route_discard(struct sk_buff *skb)
+static int dn_route_discard(struct sock *sk, struct sk_buff *skb)
{
/*
* I know we drop the packet here, but thats considered success in
return NET_RX_SUCCESS;
}
-static int dn_route_ptp_hello(struct sk_buff *skb)
+static int dn_route_ptp_hello(struct sock *sk, struct sk_buff *skb)
{
dn_dev_hello(skb);
dn_neigh_pointopoint_hello(skb);
switch (flags & DN_RT_CNTL_MSK) {
case DN_RT_PKT_HELO:
return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
- skb, skb->dev, NULL,
+ NULL, skb, skb->dev, NULL,
dn_route_ptp_hello);
case DN_RT_PKT_L1RT:
case DN_RT_PKT_L2RT:
return NF_HOOK(NFPROTO_DECNET, NF_DN_ROUTE,
- skb, skb->dev, NULL,
+ NULL, skb, skb->dev, NULL,
dn_route_discard);
case DN_RT_PKT_ERTH:
return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
- skb, skb->dev, NULL,
+ NULL, skb, skb->dev, NULL,
dn_neigh_router_hello);
case DN_RT_PKT_EEDH:
return NF_HOOK(NFPROTO_DECNET, NF_DN_HELLO,
- skb, skb->dev, NULL,
+ NULL, skb, skb->dev, NULL,
dn_neigh_endnode_hello);
}
} else {
cb->rt_flags |= DN_RT_F_IE;
cb->hops = 0;
- return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT, skb, NULL, dev,
+ return NF_HOOK(NFPROTO_DECNET, NF_DN_LOCAL_OUT, sk, skb,
+ NULL, dev,
dn_to_neigh_output);
error:
if (rt->rt_flags & RTCF_DOREDIRECT)
cb->rt_flags |= DN_RT_F_IE;
- return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD, skb, dev, skb->dev,
+ return NF_HOOK(NFPROTO_DECNET, NF_DN_FORWARD, NULL, skb,
+ dev, skb->dev,
dn_to_neigh_output);
drop:
void __exit dn_fib_rules_cleanup(void)
{
+ rtnl_lock();
fib_rules_unregister(dn_fib_rules_ops);
+ rtnl_unlock();
rcu_barrier();
}
static unsigned int dnrmg_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
dnrmg_send_peer(skb);
return NF_ACCEPT;
#ifdef CONFIG_OF
static int dsa_of_setup_routing_table(struct dsa_platform_data *pd,
struct dsa_chip_data *cd,
- int chip_index,
+ int chip_index, int port_index,
struct device_node *link)
{
- int ret;
const __be32 *reg;
- int link_port_addr;
int link_sw_addr;
struct device_node *parent_sw;
int len;
if (!reg || (len != sizeof(*reg) * 2))
return -EINVAL;
+ /*
+ * Get the destination switch number from the second field of its 'reg'
+ * property, i.e. for "reg = <0x19 1>" sw_addr is '1'.
+ */
link_sw_addr = be32_to_cpup(reg + 1);
if (link_sw_addr >= pd->nr_chips)
memset(cd->rtable, -1, pd->nr_chips * sizeof(s8));
}
- reg = of_get_property(link, "reg", NULL);
- if (!reg) {
- ret = -EINVAL;
- goto out;
- }
-
- link_port_addr = be32_to_cpup(reg);
-
- cd->rtable[link_sw_addr] = link_port_addr;
+ cd->rtable[link_sw_addr] = port_index;
return 0;
-out:
- kfree(cd->rtable);
- return ret;
}
static void dsa_of_free_platform_data(struct dsa_platform_data *pd)
if (!strcmp(port_name, "dsa") && link &&
pd->nr_chips > 1) {
ret = dsa_of_setup_routing_table(pd, cd,
- chip_index, link);
+ chip_index, port_index, link);
if (ret)
goto out_free_chip;
}
/* slave device handling ****************************************************/
-static int dsa_slave_init(struct net_device *dev)
+static int dsa_slave_get_iflink(const struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
- dev->iflink = p->parent->dst->master_netdev->ifindex;
-
- return 0;
+ return p->parent->dst->master_netdev->ifindex;
}
static inline bool dsa_port_is_bridged(struct dsa_slave_priv *p)
};
static const struct net_device_ops dsa_slave_netdev_ops = {
- .ndo_init = dsa_slave_init,
.ndo_open = dsa_slave_open,
.ndo_stop = dsa_slave_close,
.ndo_start_xmit = dsa_slave_xmit,
.ndo_fdb_del = dsa_slave_fdb_del,
.ndo_fdb_dump = dsa_slave_fdb_dump,
.ndo_do_ioctl = dsa_slave_ioctl,
+ .ndo_get_iflink = dsa_slave_get_iflink,
};
static const struct swdev_ops dsa_slave_swdev_ops = {
* shutdown() (rather than close()).
*/
if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) != 0 &&
- inet_csk(sk)->icsk_accept_queue.fastopenq == NULL) {
+ !inet_csk(sk)->icsk_accept_queue.fastopenq) {
if ((sysctl_tcp_fastopen & TFO_SERVER_WO_SOCKOPT1) != 0)
err = fastopen_init_queue(sk, backlog);
else if ((sysctl_tcp_fastopen &
answer_flags = answer->flags;
rcu_read_unlock();
- WARN_ON(answer_prot->slab == NULL);
+ WARN_ON(!answer_prot->slab);
err = -ENOBUFS;
sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
- if (sk == NULL)
+ if (!sk)
goto out;
err = 0;
if (udpfrag) {
iph->id = htons(id);
iph->frag_off = htons(offset >> 3);
- if (skb->next != NULL)
+ if (skb->next)
iph->frag_off |= htons(IP_MF);
offset += skb->len - nhoff - ihl;
} else {
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev);
- if (in_dev == NULL) {
+ if (!in_dev) {
rcu_read_unlock();
return -EINVAL;
}
*/
/*
- * Create an arp packet. If (dest_hw == NULL), we create a broadcast
+ * Create an arp packet. If dest_hw is not set, we create a broadcast
* message.
*/
struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,
*/
skb = alloc_skb(arp_hdr_len(dev) + hlen + tlen, GFP_ATOMIC);
- if (skb == NULL)
+ if (!skb)
return NULL;
skb_reserve(skb, hlen);
arp = (struct arphdr *) skb_put(skb, arp_hdr_len(dev));
skb->dev = dev;
skb->protocol = htons(ETH_P_ARP);
- if (src_hw == NULL)
+ if (!src_hw)
src_hw = dev->dev_addr;
- if (dest_hw == NULL)
+ if (!dest_hw)
dest_hw = dev->broadcast;
/*
break;
#endif
default:
- if (target_hw != NULL)
+ if (target_hw)
memcpy(arp_ptr, target_hw, dev->addr_len);
else
memset(arp_ptr, 0, dev->addr_len);
void arp_xmit(struct sk_buff *skb)
{
/* Send it off, maybe filter it using firewalling first. */
- NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit);
+ NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, NULL, skb,
+ NULL, skb->dev, dev_queue_xmit_sk);
}
EXPORT_SYMBOL(arp_xmit);
skb = arp_create(type, ptype, dest_ip, dev, src_ip,
dest_hw, src_hw, target_hw);
- if (skb == NULL)
+ if (!skb)
return;
arp_xmit(skb);
* Process an arp request.
*/
-static int arp_process(struct sk_buff *skb)
+static int arp_process(struct sock *sk, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct in_device *in_dev = __in_dev_get_rcu(dev);
* is ARP'able.
*/
- if (in_dev == NULL)
+ if (!in_dev)
goto out;
arp = arp_hdr(skb);
is_garp = arp->ar_op == htons(ARPOP_REQUEST) && tip == sip &&
inet_addr_type(net, sip) == RTN_UNICAST;
- if (n == NULL &&
+ if (!n &&
((arp->ar_op == htons(ARPOP_REPLY) &&
inet_addr_type(net, sip) == RTN_UNICAST) || is_garp))
n = __neigh_lookup(&arp_tbl, &sip, dev, 1);
static void parp_redo(struct sk_buff *skb)
{
- arp_process(skb);
+ arp_process(NULL, skb);
}
memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
- return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);
+ return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, NULL, skb,
+ dev, NULL, arp_process);
consumeskb:
consume_skb(skb);
static int arp_req_set_proxy(struct net *net, struct net_device *dev, int on)
{
- if (dev == NULL) {
+ if (!dev) {
IPV4_DEVCONF_ALL(net, PROXY_ARP) = on;
return 0;
}
return -ENODEV;
}
if (mask) {
- if (pneigh_lookup(&arp_tbl, net, &ip, dev, 1) == NULL)
+ if (!pneigh_lookup(&arp_tbl, net, &ip, dev, 1))
return -ENOBUFS;
return 0;
}
ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
if (r->arp_flags & ATF_PERM)
r->arp_flags |= ATF_COM;
- if (dev == NULL) {
+ if (!dev) {
struct rtable *rt = ip_route_output(net, ip, 0, RTO_ONLINK, 0);
if (IS_ERR(rt))
return arp_req_delete_public(net, r, dev);
ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
- if (dev == NULL) {
+ if (!dev) {
struct rtable *rt = ip_route_output(net, ip, 0, RTO_ONLINK, 0);
if (IS_ERR(rt))
return PTR_ERR(rt);
if (r.arp_dev[0]) {
err = -ENODEV;
dev = __dev_get_by_name(net, r.arp_dev);
- if (dev == NULL)
+ if (!dev)
goto out;
/* Mmmm... It is wrong... ARPHRD_NETROM==0 */
cipso_v4_cache = kcalloc(CIPSO_V4_CACHE_BUCKETS,
sizeof(struct cipso_v4_map_cache_bkt),
GFP_KERNEL);
- if (cipso_v4_cache == NULL)
+ if (!cipso_v4_cache)
return -ENOMEM;
for (iter = 0; iter < CIPSO_V4_CACHE_BUCKETS; iter++) {
secattr->cache = entry->lsm_data;
secattr->flags |= NETLBL_SECATTR_CACHE;
secattr->type = NETLBL_NLTYPE_CIPSOV4;
- if (prev_entry == NULL) {
+ if (!prev_entry) {
spin_unlock_bh(&cipso_v4_cache[bkt].lock);
return 0;
}
cipso_ptr_len = cipso_ptr[1];
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
- if (entry == NULL)
+ if (!entry)
return -ENOMEM;
entry->key = kmemdup(cipso_ptr, cipso_ptr_len, GFP_ATOMIC);
- if (entry->key == NULL) {
+ if (!entry->key) {
ret_val = -ENOMEM;
goto cache_add_failure;
}
atomic_set(&doi_def->refcount, 1);
spin_lock(&cipso_v4_doi_list_lock);
- if (cipso_v4_doi_search(doi_def->doi) != NULL) {
+ if (cipso_v4_doi_search(doi_def->doi)) {
spin_unlock(&cipso_v4_doi_list_lock);
ret_val = -EEXIST;
goto doi_add_return;
doi_add_return:
audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_ADD, audit_info);
- if (audit_buf != NULL) {
+ if (audit_buf) {
const char *type_str;
switch (doi_type) {
case CIPSO_V4_MAP_TRANS:
*/
void cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
{
- if (doi_def == NULL)
+ if (!doi_def)
return;
switch (doi_def->type) {
spin_lock(&cipso_v4_doi_list_lock);
doi_def = cipso_v4_doi_search(doi);
- if (doi_def == NULL) {
+ if (!doi_def) {
spin_unlock(&cipso_v4_doi_list_lock);
ret_val = -ENOENT;
goto doi_remove_return;
doi_remove_return:
audit_buf = netlbl_audit_start(AUDIT_MAC_CIPSOV4_DEL, audit_info);
- if (audit_buf != NULL) {
+ if (audit_buf) {
audit_log_format(audit_buf,
" cipso_doi=%u res=%u",
doi, ret_val == 0 ? 1 : 0);
rcu_read_lock();
doi_def = cipso_v4_doi_search(doi);
- if (doi_def == NULL)
+ if (!doi_def)
goto doi_getdef_return;
if (!atomic_inc_not_zero(&doi_def->refcount))
doi_def = NULL;
*/
void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def)
{
- if (doi_def == NULL)
+ if (!doi_def)
return;
if (!atomic_dec_and_test(&doi_def->refcount))
rcu_read_lock();
doi_def = cipso_v4_doi_search(get_unaligned_be32(&opt[2]));
- if (doi_def == NULL) {
+ if (!doi_def) {
err_offset = 2;
goto validate_return_locked;
}
* not the loopback device drop the packet. Further,
* there is no legitimate reason for setting this from
* userspace so reject it if skb is NULL. */
- if (skb == NULL || !(skb->dev->flags & IFF_LOOPBACK)) {
+ if (!skb || !(skb->dev->flags & IFF_LOOPBACK)) {
err_offset = opt_iter;
goto validate_return_locked;
}
* defined yet but it is not a problem as the only users of these
* "lite" PF_INET sockets are functions which do an accept() call
* afterwards so we will label the socket as part of the accept(). */
- if (sk == NULL)
+ if (!sk)
return 0;
/* We allocate the maximum CIPSO option size here so we are probably
* on and after all we are only talking about 40 bytes. */
buf_len = CIPSO_V4_OPT_LEN_MAX;
buf = kmalloc(buf_len, GFP_ATOMIC);
- if (buf == NULL) {
+ if (!buf) {
ret_val = -ENOMEM;
goto socket_setattr_failure;
}
* set the IPOPT_CIPSO option. */
opt_len = (buf_len + 3) & ~3;
opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
- if (opt == NULL) {
+ if (!opt) {
ret_val = -ENOMEM;
goto socket_setattr_failure;
}
* on and after all we are only talking about 40 bytes. */
buf_len = CIPSO_V4_OPT_LEN_MAX;
buf = kmalloc(buf_len, GFP_ATOMIC);
- if (buf == NULL) {
+ if (!buf) {
ret_val = -ENOMEM;
goto req_setattr_failure;
}
* set the IPOPT_CIPSO option. */
opt_len = (buf_len + 3) & ~3;
opt = kzalloc(sizeof(*opt) + opt_len, GFP_ATOMIC);
- if (opt == NULL) {
+ if (!opt) {
ret_val = -ENOMEM;
goto req_setattr_failure;
}
sk_inet = inet_sk(sk);
opt = rcu_dereference_protected(sk_inet->inet_opt, 1);
- if (opt == NULL || opt->opt.cipso == 0)
+ if (!opt || opt->opt.cipso == 0)
return;
hdr_delta = cipso_v4_delopt(&sk_inet->inet_opt);
req_inet = inet_rsk(req);
opt = req_inet->opt;
- if (opt == NULL || opt->opt.cipso == 0)
+ if (!opt || opt->opt.cipso == 0)
return;
cipso_v4_delopt(&req_inet->opt);
doi = get_unaligned_be32(&cipso[2]);
rcu_read_lock();
doi_def = cipso_v4_doi_search(doi);
- if (doi_def == NULL)
+ if (!doi_def)
goto getattr_return;
/* XXX - This code assumes only one tag per CIPSO option which isn't
* really a good assumption to make but since we only support the MAC
ifm = nlmsg_data(nlh);
in_dev = inetdev_by_index(net, ifm->ifa_index);
- if (in_dev == NULL) {
+ if (!in_dev) {
err = -ENODEV;
goto errout;
}
ifm = nlmsg_data(nlh);
err = -EINVAL;
- if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
+ if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL])
goto errout;
dev = __dev_get_by_index(net, ifm->ifa_index);
err = -ENODEV;
- if (dev == NULL)
+ if (!dev)
goto errout;
in_dev = __in_dev_get_rtnl(dev);
err = -ENOBUFS;
- if (in_dev == NULL)
+ if (!in_dev)
goto errout;
ifa = inet_alloc_ifa();
- if (ifa == NULL)
+ if (!ifa)
/*
* A potential indev allocation can be left alive, it stays
* assigned to its device and is destroy with it.
neigh_parms_data_state_setall(in_dev->arp_parms);
in_dev_hold(in_dev);
- if (tb[IFA_ADDRESS] == NULL)
+ if (!tb[IFA_ADDRESS])
tb[IFA_ADDRESS] = tb[IFA_LOCAL];
INIT_HLIST_NODE(&ifa->hash);
__be32 addr = 0;
struct net_device *dev;
- if (in_dev != NULL)
+ if (in_dev)
return confirm_addr_indev(in_dev, dst, local, scope);
rcu_read_lock();
if (named++ == 0)
goto skip;
dot = strchr(old, ':');
- if (dot == NULL) {
+ if (!dot) {
sprintf(old, ":%d", named);
dot = old;
}
u32 preferred, valid;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
- if (nlh == NULL)
+ if (!nlh)
return -EMSGSIZE;
ifm = nlmsg_data(nlh);
net = dev_net(ifa->ifa_dev->dev);
skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
- if (skb == NULL)
+ if (!skb)
goto errout;
err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0);
return -ENODATA;
nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
- if (nla == NULL)
+ if (!nla)
return -EMSGSIZE;
for (i = 0; i < IPV4_DEVCONF_MAX; i++)
nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
flags);
- if (nlh == NULL)
+ if (!nlh)
return -EMSGSIZE;
ncm = nlmsg_data(nlh);
int err = -ENOBUFS;
skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC);
- if (skb == NULL)
+ if (!skb)
goto errout;
err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
break;
default:
dev = __dev_get_by_index(net, ifindex);
- if (dev == NULL)
+ if (!dev)
goto errout;
in_dev = __in_dev_get_rtnl(dev);
- if (in_dev == NULL)
+ if (!in_dev)
goto errout;
devconf = &in_dev->cnf;
break;
err = -ENOBUFS;
skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC);
- if (skb == NULL)
+ if (!skb)
goto errout;
err = inet_netconf_fill_devconf(skb, ifindex, devconf,
{
struct devinet_sysctl_table *t = cnf->sysctl;
- if (t == NULL)
+ if (!t)
return;
cnf->sysctl = NULL;
if (!net_eq(net, &init_net)) {
all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
- if (all == NULL)
+ if (!all)
goto err_alloc_all;
dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
- if (dflt == NULL)
+ if (!dflt)
goto err_alloc_dflt;
#ifdef CONFIG_SYSCTL
tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
- if (tbl == NULL)
+ if (!tbl)
goto err_alloc_ctl;
tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
err = -ENOMEM;
forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
- if (forw_hdr == NULL)
+ if (!forw_hdr)
goto err_reg_ctl;
net->ipv4.forw_hdr = forw_hdr;
#endif
int err;
err = -EINVAL;
- if (x->ealg == NULL)
+ if (!x->ealg)
goto error;
err = -ENAMETOOLONG;
struct fib_table *local_table, *main_table;
main_table = fib_trie_table(RT_TABLE_MAIN, NULL);
- if (main_table == NULL)
+ if (!main_table)
return -ENOMEM;
local_table = fib_trie_table(RT_TABLE_LOCAL, main_table);
- if (local_table == NULL)
+ if (!local_table)
goto fail;
hlist_add_head_rcu(&local_table->tb_hlist,
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
if (strcmp(ifa->ifa_label, devname) == 0)
break;
- if (ifa == NULL)
+ if (!ifa)
return -ENODEV;
cfg->fc_prefsrc = ifa->ifa_local;
}
int len = 0;
mx = kzalloc(3 * nla_total_size(4), GFP_KERNEL);
- if (mx == NULL)
+ if (!mx)
return -ENOMEM;
if (rt->rt_flags & RTF_MTU)
goto errout;
tb = fib_get_table(net, cfg.fc_table);
- if (tb == NULL) {
+ if (!tb) {
err = -ESRCH;
goto errout;
}
goto errout;
tb = fib_new_table(net, cfg.fc_table);
- if (tb == NULL) {
+ if (!tb) {
err = -ENOBUFS;
goto errout;
}
else
tb = fib_new_table(net, RT_TABLE_LOCAL);
- if (tb == NULL)
+ if (!tb)
return;
cfg.fc_table = tb->tb_id;
if (ifa->ifa_flags & IFA_F_SECONDARY) {
prim = inet_ifa_byprefix(in_dev, prefix, mask);
- if (prim == NULL) {
+ if (!prim) {
pr_warn("%s: bug: prim == NULL\n", __func__);
return;
}
if (ifa->ifa_flags & IFA_F_SECONDARY) {
prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
- if (prim == NULL) {
+ if (!prim) {
pr_warn("%s: bug: prim == NULL\n", __func__);
return;
}
return;
skb = netlink_skb_clone(skb, GFP_KERNEL);
- if (skb == NULL)
+ if (!skb)
return;
nlh = nlmsg_hdr(skb);
};
sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, &cfg);
- if (sk == NULL)
+ if (!sk)
return -EAFNOSUPPORT;
net->ipv4.fibnl = sk;
return 0;
case NETDEV_DOWN:
fib_del_ifaddr(ifa, NULL);
atomic_inc(&net->ipv4.dev_addr_genid);
- if (ifa->ifa_dev->ifa_list == NULL) {
+ if (!ifa->ifa_dev->ifa_list) {
/* Last address was deleted from this interface.
* Disable IP.
*/
size = max_t(size_t, size, L1_CACHE_BYTES);
net->ipv4.fib_table_hash = kzalloc(size, GFP_KERNEL);
- if (net->ipv4.fib_table_hash == NULL)
+ if (!net->ipv4.fib_table_hash)
return -ENOMEM;
err = fib4_rules_init(net);
unsigned int i;
rtnl_lock();
-
#ifdef CONFIG_IP_MULTIPLE_TABLES
RCU_INIT_POINTER(net->ipv4.fib_local, NULL);
RCU_INIT_POINTER(net->ipv4.fib_main, NULL);
RCU_INIT_POINTER(net->ipv4.fib_default, NULL);
#endif
-
for (i = 0; i < FIB_TABLE_HASHSZ; i++) {
struct hlist_head *head = &net->ipv4.fib_table_hash[i];
struct hlist_node *tmp;
u32 id;
for (id = 1; id <= RT_TABLE_MAX; id++)
- if (fib_get_table(net, id) == NULL)
+ if (!fib_get_table(net, id))
return fib_new_table(net, id);
return NULL;
}
struct fib_table *table;
table = fib_empty_table(net);
- if (table == NULL) {
+ if (!table) {
err = -ENOBUFS;
goto errout;
}
int err = -ENOBUFS;
skb = nlmsg_new(fib_nlmsg_size(fa->fa_info), GFP_KERNEL);
- if (skb == NULL)
+ if (!skb)
goto errout;
err = fib_dump_info(skb, info->portid, seq, event, tb_id,
}
#ifdef CONFIG_IP_ROUTE_MULTIPATH
- if (cfg->fc_mp == NULL)
+ if (!cfg->fc_mp)
return 0;
rtnh = cfg->fc_mp;
rcu_read_lock();
err = -ENODEV;
in_dev = inetdev_by_index(net, nh->nh_oif);
- if (in_dev == NULL)
+ if (!in_dev)
goto out;
err = -ENETDOWN;
if (!(in_dev->dev->flags & IFF_UP))
}
fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
- if (fi == NULL)
+ if (!fi)
goto failure;
fib_info_cnt++;
if (cfg->fc_mx) {
nh->nh_scope = RT_SCOPE_NOWHERE;
nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif);
err = -ENODEV;
- if (nh->nh_dev == NULL)
+ if (!nh->nh_dev)
goto failure;
} else {
change_nexthops(fi) {
struct rtmsg *rtm;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), flags);
- if (nlh == NULL)
+ if (!nlh)
return -EMSGSIZE;
rtm = nlmsg_data(nlh);
struct nlattr *mp;
mp = nla_nest_start(skb, RTA_MULTIPATH);
- if (mp == NULL)
+ if (!mp)
goto nla_put_failure;
for_nexthops(fi) {
rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh));
- if (rtnh == NULL)
+ if (!rtnh)
goto nla_put_failure;
rtnh->rtnh_flags = nh->nh_flags & 0xFF;
struct hlist_head *head = &fib_info_laddrhash[hash];
struct fib_info *fi;
- if (fib_info_laddrhash == NULL || local == 0)
+ if (!fib_info_laddrhash || local == 0)
return 0;
hlist_for_each_entry(fi, head, fib_lhash) {
fib_alias_accessed(fa);
- if (fi == NULL) {
+ if (!fi) {
if (next_fi != res->fi)
break;
} else if (!fib_detect_death(fi, order, &last_resort,
order++;
}
- if (order <= 0 || fi == NULL) {
+ if (order <= 0 || !fi) {
tb->tb_default = -1;
goto out;
}
alive++;
continue;
}
- if (nexthop_nh->nh_dev == NULL ||
+ if (!nexthop_nh->nh_dev ||
!(nexthop_nh->nh_dev->flags & IFF_UP))
continue;
if (nexthop_nh->nh_dev != dev ||
BUG_ON(i >= child_length(tn));
/* update emptyChildren, overflow into fullChildren */
- if (n == NULL && chi != NULL)
+ if (!n && chi)
empty_child_inc(tn);
- if (n != NULL && chi == NULL)
+ if (n && !chi)
empty_child_dec(tn);
/* update fullChildren */
unsigned long j, k;
/* An empty child */
- if (inode == NULL)
+ if (!inode)
continue;
/* A leaf or an internal node with skipped bits */
}
err = -ENOBUFS;
new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
- if (new_fa == NULL)
+ if (!new_fa)
goto out;
fi_drop = fa->fa_info;
err = -ENOBUFS;
new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
- if (new_fa == NULL)
+ if (!new_fa)
goto out;
new_fa->fa_info = fi;
sz += sizeof(struct trie);
tb = kzalloc(sz, GFP_KERNEL);
- if (tb == NULL)
+ if (!tb)
return NULL;
tb->tb_id = id;
skb_set_inner_protocol(skb, htons(ETH_P_TEB));
- return udp_tunnel_xmit_skb(rt, skb, src, dst,
+ return udp_tunnel_xmit_skb(rt, gs->sock->sk, skb, src, dst,
tos, ttl, df, src_port, dst_port, xnet,
!csum);
}
rcu_read_lock();
ptype = gro_find_receive_by_type(type);
- if (ptype == NULL) {
+ if (!ptype) {
flush = 1;
goto out_unlock;
}
rcu_read_lock();
ptype = gro_find_complete_by_type(type);
- if (ptype != NULL)
+ if (ptype)
err = ptype->callbacks.gro_complete(skb, nhoff + gh_len);
rcu_read_unlock();
rcu_read_lock();
ptype = gro_find_receive_by_type(type);
- if (ptype == NULL)
+ if (!ptype)
goto out_unlock;
grehlen = GRE_HEADER_SECTION;
rcu_read_lock();
ptype = gro_find_complete_by_type(type);
- if (ptype != NULL)
+ if (ptype)
err = ptype->callbacks.gro_complete(skb, nhoff + grehlen);
rcu_read_unlock();
return;
sk = icmp_xmit_lock(net);
- if (sk == NULL)
+ if (!sk)
return;
inet = inet_sk(sk);
skb_in->data,
sizeof(_inner_type),
&_inner_type);
- if (itp == NULL)
+ if (!itp)
goto out;
/*
return;
sk = icmp_xmit_lock(net);
- if (sk == NULL)
+ if (!sk)
goto out_free;
/*
hlen = LL_RESERVED_SPACE(dev);
tlen = dev->needed_tailroom;
skb = alloc_skb(IGMP_SIZE + hlen + tlen, GFP_ATOMIC);
- if (skb == NULL) {
+ if (!skb) {
ip_rt_put(rt);
return -1;
}
int len = skb->len;
bool dropped = true;
- if (in_dev == NULL)
+ if (!in_dev)
goto drop;
if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
if (count >= sysctl_igmp_max_memberships)
goto done;
iml = sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL);
- if (iml == NULL)
+ if (!iml)
goto done;
memcpy(&iml->multi, imr, sizeof(*imr));
struct ip_sf_socklist *psf = rtnl_dereference(iml->sflist);
int err;
- if (psf == NULL) {
+ if (!psf) {
/* any-source empty exclude case */
return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
iml->sfmode, 0, NULL, 0);
struct ip_mc_socklist *iml;
struct net *net = sock_net(sk);
- if (inet->mc_list == NULL)
+ if (!inet->mc_list)
return;
rtnl_lock();
inet->mc_list = iml->next_rcu;
in_dev = inetdev_by_index(net, iml->multi.imr_ifindex);
(void) ip_mc_leave_src(sk, iml, in_dev);
- if (in_dev != NULL)
+ if (in_dev)
ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
/* decrease mem now to avoid the memleak warning */
atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
for_each_netdev_rcu(net, state->dev) {
struct in_device *idev;
idev = __in_dev_get_rcu(state->dev);
- if (unlikely(idev == NULL))
+ if (unlikely(!idev))
continue;
im = rcu_dereference(idev->mc_list);
- if (likely(im != NULL)) {
+ if (likely(im)) {
spin_lock_bh(&im->lock);
psf = im->sources;
- if (likely(psf != NULL)) {
+ if (likely(psf)) {
state->im = im;
state->idev = idev;
break;
__releases(rcu)
{
struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
- if (likely(state->im != NULL)) {
+ if (likely(state->im)) {
spin_unlock_bh(&state->im->lock);
state->im = NULL;
}
{
struct sock *newsk = sk_clone_lock(sk, priority);
- if (newsk != NULL) {
+ if (newsk) {
struct inet_connection_sock *newicsk = inet_csk(newsk);
newsk->sk_state = TCP_SYN_RECV;
sk_acceptq_removed(sk);
reqsk_put(req);
}
- if (queue->fastopenq != NULL) {
+ if (queue->fastopenq) {
/* Free all the reqs queued in rskq_rst_head. */
spin_lock_bh(&queue->fastopenq->lock);
acc_req = queue->fastopenq->rskq_rst_head;
{
const struct inet_connection_sock *icsk = inet_csk(sk);
- if (icsk->icsk_af_ops->compat_getsockopt != NULL)
+ if (icsk->icsk_af_ops->compat_getsockopt)
return icsk->icsk_af_ops->compat_getsockopt(sk, level, optname,
optval, optlen);
return icsk->icsk_af_ops->getsockopt(sk, level, optname,
{
const struct inet_connection_sock *icsk = inet_csk(sk);
- if (icsk->icsk_af_ops->compat_setsockopt != NULL)
+ if (icsk->icsk_af_ops->compat_setsockopt)
return icsk->icsk_af_ops->compat_setsockopt(sk, level, optname,
optval, optlen);
return icsk->icsk_af_ops->setsockopt(sk, level, optname,
}
q = kmem_cache_zalloc(f->frags_cachep, GFP_ATOMIC);
- if (q == NULL)
+ if (!q)
return NULL;
q->net = nf;
struct inet_frag_queue *q;
q = inet_frag_alloc(nf, f, arg);
- if (q == NULL)
+ if (!q)
return NULL;
return inet_frag_intern(nf, q, f, arg);
{
struct inet_bind_bucket *tb = kmem_cache_alloc(cachep, GFP_ATOMIC);
- if (tb != NULL) {
+ if (tb) {
write_pnet(&tb->ib_net, net);
tb->port = snum;
tb->fastreuse = 0;
struct inet_timewait_sock *tw =
kmem_cache_alloc(sk->sk_prot_creator->twsk_prot->twsk_slab,
GFP_ATOMIC);
- if (tw != NULL) {
+ if (tw) {
const struct inet_sock *inet = inet_sk(sk);
kmemcheck_annotate_bitfield(tw, flags);
}
-static int ip_forward_finish(struct sk_buff *skb)
+static int ip_forward_finish(struct sock *sk, struct sk_buff *skb)
{
struct ip_options *opt = &(IPCB(skb)->opt);
ip_forward_options(skb);
skb_sender_cpu_clear(skb);
- return dst_output(skb);
+ return dst_output_sk(sk, skb);
}
int ip_forward(struct sk_buff *skb)
skb->priority = rt_tos2priority(iph->tos);
- return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev,
- rt->dst.dev, ip_forward_finish);
+ return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, NULL, skb,
+ skb->dev, rt->dst.dev, ip_forward_finish);
sr_failed:
/*
goto err;
err = -ENOMEM;
- if (pskb_pull(skb, ihl) == NULL)
+ if (!pskb_pull(skb, ihl))
goto err;
err = pskb_trim_rcsum(skb, end - offset);
qp->q.fragments = head;
}
- WARN_ON(head == NULL);
+ WARN_ON(!head);
WARN_ON(FRAG_CB(head)->offset != 0);
/* Allocate a new buffer for the datagram. */
struct sk_buff *clone;
int i, plen = 0;
- if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
+ clone = alloc_skb(0, GFP_ATOMIC);
+ if (!clone)
goto out_nomem;
clone->next = head->next;
head->next = clone;
IP_INC_STATS_BH(net, IPSTATS_MIB_REASMREQDS);
/* Lookup (or create) queue header */
- if ((qp = ip_find(net, ip_hdr(skb), user)) != NULL) {
+ qp = ip_find(net, ip_hdr(skb), user);
+ if (qp) {
int ret;
spin_lock(&qp->q.lock);
table = ip4_frags_ns_ctl_table;
if (!net_eq(net, &init_net)) {
table = kmemdup(table, sizeof(ip4_frags_ns_ctl_table), GFP_KERNEL);
- if (table == NULL)
+ if (!table)
goto err_alloc;
table[0].data = &net->ipv4.frags.high_thresh;
}
hdr = register_net_sysctl(net, "net/ipv4", table);
- if (hdr == NULL)
+ if (!hdr)
goto err_reg;
net->ipv4.frags_hdr = hdr;
t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
iph->daddr, iph->saddr, tpi->key);
- if (t == NULL)
+ if (!t)
return PACKET_REJECT;
if (t->parms.iph.daddr == 0 ||
return -EADDRNOTAVAIL;
dev = rt->dst.dev;
ip_rt_put(rt);
- if (__in_dev_get_rtnl(dev) == NULL)
+ if (!__in_dev_get_rtnl(dev))
return -EADDRNOTAVAIL;
t->mlink = dev->ifindex;
ip_mc_inc_group(__in_dev_get_rtnl(dev), t->parms.iph.daddr);
.ndo_do_ioctl = ipgre_tunnel_ioctl,
.ndo_change_mtu = ip_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64,
+ .ndo_get_iflink = ip_tunnel_get_iflink,
};
#define GRE_FEATURES (NETIF_F_SG | \
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = ip_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64,
+ .ndo_get_iflink = ip_tunnel_get_iflink,
};
static void ipgre_tap_setup(struct net_device *dev)
return false;
}
-static int ip_local_deliver_finish(struct sk_buff *skb)
+static int ip_local_deliver_finish(struct sock *sk, struct sk_buff *skb)
{
struct net *net = dev_net(skb->dev);
raw = raw_local_deliver(skb, protocol);
ipprot = rcu_dereference(inet_protos[protocol]);
- if (ipprot != NULL) {
+ if (ipprot) {
int ret;
if (!ipprot->no_policy) {
return 0;
}
- return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
+ return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, NULL, skb,
+ skb->dev, NULL,
ip_local_deliver_finish);
}
int sysctl_ip_early_demux __read_mostly = 1;
EXPORT_SYMBOL(sysctl_ip_early_demux);
-static int ip_rcv_finish(struct sk_buff *skb)
+static int ip_rcv_finish(struct sock *sk, struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt;
- if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
+ if (sysctl_ip_early_demux && !skb_dst(skb) && !skb->sk) {
const struct net_protocol *ipprot;
int protocol = iph->protocol;
IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);
- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
goto out;
}
/* Must drop socket now because of tproxy. */
skb_orphan(skb);
- return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,
+ return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL, skb,
+ dev, NULL,
ip_rcv_finish);
csum_error:
unsigned char *iph;
int optlen, l;
- if (skb != NULL) {
+ if (skb) {
rt = skb_rtable(skb);
optptr = (unsigned char *)&(ip_hdr(skb)[1]);
} else
}
EXPORT_SYMBOL(ip_send_check);
-int __ip_local_out(struct sk_buff *skb)
+int __ip_local_out_sk(struct sock *sk, struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb);
iph->tot_len = htons(skb->len);
ip_send_check(iph);
- return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
- skb_dst(skb)->dev, dst_output);
+ return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb, NULL,
+ skb_dst(skb)->dev, dst_output_sk);
+}
+
+int __ip_local_out(struct sk_buff *skb)
+{
+ return __ip_local_out_sk(skb->sk, skb);
}
int ip_local_out_sk(struct sock *sk, struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);
-static inline int ip_finish_output2(struct sk_buff *skb)
+static inline int ip_finish_output2(struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct rtable *rt = (struct rtable *)dst;
struct sk_buff *skb2;
skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
- if (skb2 == NULL) {
+ if (!skb2) {
kfree_skb(skb);
return -ENOMEM;
}
return -EINVAL;
}
-static int ip_finish_output_gso(struct sk_buff *skb)
+static int ip_finish_output_gso(struct sock *sk, struct sk_buff *skb)
{
netdev_features_t features;
struct sk_buff *segs;
/* common case: locally created skb or seglen is <= mtu */
if (((IPCB(skb)->flags & IPSKB_FORWARDED) == 0) ||
skb_gso_network_seglen(skb) <= ip_skb_dst_mtu(skb))
- return ip_finish_output2(skb);
+ return ip_finish_output2(sk, skb);
/* Slowpath - GSO segment length is exceeding the dst MTU.
*
int err;
segs->next = NULL;
- err = ip_fragment(segs, ip_finish_output2);
+ err = ip_fragment(sk, segs, ip_finish_output2);
if (err && ret == 0)
ret = err;
return ret;
}
-static int ip_finish_output(struct sk_buff *skb)
+static int ip_finish_output(struct sock *sk, struct sk_buff *skb)
{
#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
/* Policy lookup after SNAT yielded a new policy */
- if (skb_dst(skb)->xfrm != NULL) {
+ if (skb_dst(skb)->xfrm) {
IPCB(skb)->flags |= IPSKB_REROUTED;
- return dst_output(skb);
+ return dst_output_sk(sk, skb);
}
#endif
if (skb_is_gso(skb))
- return ip_finish_output_gso(skb);
+ return ip_finish_output_gso(sk, skb);
if (skb->len > ip_skb_dst_mtu(skb))
- return ip_fragment(skb, ip_finish_output2);
+ return ip_fragment(sk, skb, ip_finish_output2);
- return ip_finish_output2(skb);
+ return ip_finish_output2(sk, skb);
}
int ip_mc_output(struct sock *sk, struct sk_buff *skb)
struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
if (newskb)
NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,
- newskb, NULL, newskb->dev,
+ sk, newskb, NULL, newskb->dev,
dev_loopback_xmit);
}
if (rt->rt_flags&RTCF_BROADCAST) {
struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
if (newskb)
- NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, newskb,
+ NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, newskb,
NULL, newskb->dev, dev_loopback_xmit);
}
- return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL,
+ return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb, NULL,
skb->dev, ip_finish_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}
skb->dev = dev;
skb->protocol = htons(ETH_P_IP);
- return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,
+ return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb,
+ NULL, dev,
ip_finish_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}
inet_opt = rcu_dereference(inet->inet_opt);
fl4 = &fl->u.ip4;
rt = skb_rtable(skb);
- if (rt != NULL)
+ if (rt)
goto packet_routed;
/* Make sure we can route this packet. */
rt = (struct rtable *)__sk_dst_check(sk, 0);
- if (rt == NULL) {
+ if (!rt) {
__be32 daddr;
/* Use correct destination address if we have options. */
}
EXPORT_SYMBOL(ip_queue_xmit);
-
static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
{
to->pkt_type = from->pkt_type;
* single device frame, and queue such a frame for sending.
*/
-int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
+int ip_fragment(struct sock *sk, struct sk_buff *skb,
+ int (*output)(struct sock *, struct sk_buff *))
{
struct iphdr *iph;
int ptr;
ip_options_fragment(frag);
offset += skb->len - hlen;
iph->frag_off = htons(offset>>3);
- if (frag->next != NULL)
+ if (frag->next)
iph->frag_off |= htons(IP_MF);
/* Ready, complete checksum */
ip_send_check(iph);
}
- err = output(skb);
+ err = output(sk, skb);
if (!err)
IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
ip_send_check(iph);
- err = output(skb2);
+ err = output(sk, skb2);
if (err)
goto fail;
* device, so create one single skb packet containing complete
* udp datagram
*/
- if ((skb = skb_peek_tail(queue)) == NULL) {
+ skb = skb_peek_tail(queue);
+ if (!skb) {
skb = sock_alloc_send_skb(sk,
hh_len + fragheaderlen + transhdrlen + 20,
(flags & MSG_DONTWAIT), &err);
- if (skb == NULL)
+ if (!skb)
return err;
/* reserve space for Hardware header */
skb->csum = 0;
-
__skb_queue_tail(queue, skb);
} else if (skb_is_gso(skb)) {
goto append;
skb = sock_wmalloc(sk,
alloclen + hh_len + 15, 1,
sk->sk_allocation);
- if (unlikely(skb == NULL))
+ if (unlikely(!skb))
err = -ENOBUFS;
}
- if (skb == NULL)
+ if (!skb)
goto error;
/*
*/
opt = ipc->opt;
if (opt) {
- if (cork->opt == NULL) {
+ if (!cork->opt) {
cork->opt = kmalloc(sizeof(struct ip_options) + 40,
sk->sk_allocation);
- if (unlikely(cork->opt == NULL))
+ if (unlikely(!cork->opt))
return -ENOBUFS;
}
memcpy(cork->opt, &opt->opt, sizeof(struct ip_options) + opt->opt.optlen);
return -EMSGSIZE;
}
- if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
+ skb = skb_peek_tail(&sk->sk_write_queue);
+ if (!skb)
return -EINVAL;
cork->length += size;
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
}
-
while (size > 0) {
int i;
__be16 df = 0;
__u8 ttl;
- if ((skb = __skb_dequeue(queue)) == NULL)
+ skb = __skb_dequeue(queue);
+ if (!skb)
goto out;
tail_skb = &(skb_shinfo(skb)->frag_list);
return 0;
}
}
- if (new_ra == NULL) {
+ if (!new_ra) {
spin_unlock_bh(&ip_ra_lock);
return -ENOBUFS;
}
skb_network_header(skb);
serr->port = port;
- if (skb_pull(skb, payload - skb->data) != NULL) {
+ if (skb_pull(skb, payload - skb->data)) {
skb_reset_transport_header(skb);
if (sock_queue_err_skb(sk, skb) == 0)
return;
err = -EAGAIN;
skb = sock_dequeue_err_skb(sk);
- if (skb == NULL)
+ if (!skb)
goto out;
copied = skb->len;
hlen = tdev->hard_header_len + tdev->needed_headroom;
mtu = tdev->mtu;
}
- dev->iflink = tunnel->parms.link;
dev->needed_headroom = t_hlen + hlen;
mtu -= (dev->hard_header_len + t_hlen);
if (dst == 0) {
/* NBMA tunnel */
- if (skb_dst(skb) == NULL) {
+ if (!skb_dst(skb)) {
dev->stats.tx_fifo_errors++;
goto tx_error;
}
neigh = dst_neigh_lookup(skb_dst(skb),
&ipv6_hdr(skb)->daddr);
- if (neigh == NULL)
+ if (!neigh)
goto tx_error;
addr6 = (const struct in6_addr *)&neigh->primary_key;
return;
}
- err = iptunnel_xmit(skb->sk, rt, skb, fl4.saddr, fl4.daddr, protocol,
+ err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, protocol,
tos, ttl, df, !net_eq(tunnel->net, dev_net(dev)));
iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
case SIOCGETTUNNEL:
if (dev == itn->fb_tunnel_dev) {
t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type);
- if (t == NULL)
+ if (!t)
t = netdev_priv(dev);
}
memcpy(p, &t->parms, sizeof(*p));
break;
}
if (dev != itn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
- if (t != NULL) {
+ if (t) {
if (t->dev != dev) {
err = -EEXIST;
break;
if (dev == itn->fb_tunnel_dev) {
err = -ENOENT;
t = ip_tunnel_find(itn, p, itn->fb_tunnel_dev->type);
- if (t == NULL)
+ if (!t)
goto done;
err = -EPERM;
if (t == netdev_priv(itn->fb_tunnel_dev))
}
EXPORT_SYMBOL(ip_tunnel_get_link_net);
+int ip_tunnel_get_iflink(const struct net_device *dev)
+{
+ struct ip_tunnel *tunnel = netdev_priv(dev);
+
+ return tunnel->parms.link;
+}
+EXPORT_SYMBOL(ip_tunnel_get_iflink);
+
int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id,
struct rtnl_link_ops *ops, char *devname)
{
iph->daddr = dst;
iph->saddr = src;
iph->ttl = ttl;
- __ip_select_ident(sock_net(sk), iph, skb_shinfo(skb)->gso_segs ?: 1);
+ __ip_select_ident(dev_net(rt->dst.dev), iph,
+ skb_shinfo(skb)->gso_segs ?: 1);
err = ip_local_out_sk(sk, skb);
if (unlikely(net_xmit_eval(err)))
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
iph->saddr, iph->daddr, 0);
- if (tunnel != NULL) {
+ if (tunnel) {
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
.ndo_do_ioctl = vti_tunnel_ioctl,
.ndo_change_mtu = ip_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64,
+ .ndo_get_iflink = ip_tunnel_get_iflink,
};
static void vti_tunnel_setup(struct net_device *dev)
dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr);
dev->mtu = ETH_DATA_LEN;
dev->flags = IFF_NOARP;
- dev->iflink = 0;
dev->addr_len = 4;
dev->features |= NETIF_F_LLTX;
netif_keep_dst(dev);
struct xfrm_state *t;
t = xfrm_state_alloc(net);
- if (t == NULL)
+ if (!t)
goto out;
t->id.proto = IPPROTO_IPIP;
if (!net_eq(dev_net(dev), &init_net))
goto drop;
- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
return NET_RX_DROP;
if (!pskb_may_pull(skb, sizeof(struct arphdr)))
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop;
- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
return NET_RX_DROP;
if (!pskb_may_pull(skb,
err = -ENOENT;
t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
iph->daddr, iph->saddr, 0);
- if (t == NULL)
+ if (!t)
goto out;
if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
.ndo_do_ioctl = ipip_tunnel_ioctl,
.ndo_change_mtu = ip_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64,
+ .ndo_get_iflink = ip_tunnel_get_iflink,
};
#define IPIP_FEATURES (NETIF_F_SG | \
dev->type = ARPHRD_TUNNEL;
dev->flags = IFF_NOARP;
- dev->iflink = 0;
dev->addr_len = 4;
dev->features |= NETIF_F_LLTX;
netif_keep_dst(dev);
}
mrt = ipmr_get_table(rule->fr_net, rule->table);
- if (mrt == NULL)
+ if (!mrt)
return -EAGAIN;
res->mrt = mrt;
return 0;
INIT_LIST_HEAD(&net->ipv4.mr_tables);
mrt = ipmr_new_table(net, RT_TABLE_DEFAULT);
- if (mrt == NULL) {
+ if (!mrt) {
err = -ENOMEM;
goto err1;
}
return 0;
err2:
- kfree(mrt);
+ ipmr_free_table(mrt);
err1:
fib_rules_unregister(ops);
return err;
{
struct mr_table *mrt, *next;
+ rtnl_lock();
list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) {
list_del(&mrt->list);
ipmr_free_table(mrt);
}
fib_rules_unregister(net->ipv4.mr_rules_ops);
+ rtnl_unlock();
}
#else
#define ipmr_for_each_table(mrt, net) \
static void __net_exit ipmr_rules_exit(struct net *net)
{
+ rtnl_lock();
ipmr_free_table(net->ipv4.mrt);
+ net->ipv4.mrt = NULL;
+ rtnl_unlock();
}
#endif
unsigned int i;
mrt = ipmr_get_table(net, id);
- if (mrt != NULL)
+ if (mrt)
return mrt;
mrt = kzalloc(sizeof(*mrt), GFP_KERNEL);
- if (mrt == NULL)
+ if (!mrt)
return NULL;
write_pnet(&mrt->net, net);
mrt->id = id;
dev->flags |= IFF_MULTICAST;
in_dev = __in_dev_get_rtnl(dev);
- if (in_dev == NULL)
+ if (!in_dev)
goto failure;
ipv4_devconf_setall(in_dev);
return NETDEV_TX_OK;
}
+static int reg_vif_get_iflink(const struct net_device *dev)
+{
+ return 0;
+}
+
static const struct net_device_ops reg_vif_netdev_ops = {
.ndo_start_xmit = reg_vif_xmit,
+ .ndo_get_iflink = reg_vif_get_iflink,
};
static void reg_vif_setup(struct net_device *dev)
dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup);
- if (dev == NULL)
+ if (!dev)
return NULL;
dev_net_set(dev, net);
free_netdev(dev);
return NULL;
}
- dev->iflink = 0;
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev);
case 0:
if (vifc->vifc_flags == VIFF_USE_IFINDEX) {
dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex);
- if (dev && __in_dev_get_rtnl(dev) == NULL) {
+ if (dev && !__in_dev_get_rtnl(dev)) {
dev_put(dev);
return -EADDRNOTAVAIL;
}
v->pkt_out = 0;
v->link = dev->ifindex;
if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER))
- v->link = dev->iflink;
+ v->link = dev_get_iflink(dev);
/* And finish update writing critical data */
write_lock_bh(&mrt_lock);
rcu_read_lock();
mroute_sk = rcu_dereference(mrt->mroute_sk);
- if (mroute_sk == NULL) {
+ if (!mroute_sk) {
rcu_read_unlock();
kfree_skb(skb);
return -EINVAL;
return -EINVAL;
c = ipmr_cache_alloc();
- if (c == NULL)
+ if (!c)
return -ENOMEM;
c->mfc_origin = mfc->mfcc_origin.s_addr;
return -EOPNOTSUPP;
mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
- if (mrt == NULL)
+ if (!mrt)
return -ENOENT;
if (optname != MRT_INIT) {
return -EOPNOTSUPP;
mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
- if (mrt == NULL)
+ if (!mrt)
return -ENOENT;
if (optname != MRT_VERSION &&
struct mr_table *mrt;
mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
- if (mrt == NULL)
+ if (!mrt)
return -ENOENT;
switch (cmd) {
struct mr_table *mrt;
mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
- if (mrt == NULL)
+ if (!mrt)
return -ENOENT;
switch (cmd) {
nf_reset(skb);
}
-static inline int ipmr_forward_finish(struct sk_buff *skb)
+static inline int ipmr_forward_finish(struct sock *sk, struct sk_buff *skb)
{
struct ip_options *opt = &(IPCB(skb)->opt);
if (unlikely(opt->optlen))
ip_forward_options(skb);
- return dst_output(skb);
+ return dst_output_sk(sk, skb);
}
/*
struct flowi4 fl4;
int encap = 0;
- if (vif->dev == NULL)
+ if (!vif->dev)
goto out_free;
#ifdef CONFIG_IP_PIMSM
* not mrouter) cannot join to more than one interface - it will
* result in receiving multiple packets.
*/
- NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev,
+ NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, NULL, skb,
+ skb->dev, dev,
ipmr_forward_finish);
return;
/* already under rcu_read_lock() */
cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
- if (cache == NULL) {
+ if (!cache) {
int vif = ipmr_find_vif(mrt, skb->dev);
if (vif >= 0)
/*
* No usable cache entry
*/
- if (cache == NULL) {
+ if (!cache) {
int vif;
if (local) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
ip_local_deliver(skb);
- if (skb2 == NULL)
+ if (!skb2)
return -ENOBUFS;
skb = skb2;
}
reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev;
read_unlock(&mrt_lock);
- if (reg_dev == NULL)
+ if (!reg_dev)
return 1;
skb->mac_header = skb->network_header;
int err;
mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
- if (mrt == NULL)
+ if (!mrt)
return -ENOENT;
rcu_read_lock();
cache = ipmr_cache_find(mrt, saddr, daddr);
- if (cache == NULL && skb->dev) {
+ if (!cache && skb->dev) {
int vif = ipmr_find_vif(mrt, skb->dev);
if (vif >= 0)
cache = ipmr_cache_find_any(mrt, daddr, vif);
}
- if (cache == NULL) {
+ if (!cache) {
struct sk_buff *skb2;
struct iphdr *iph;
struct net_device *dev;
int err;
nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags);
- if (nlh == NULL)
+ if (!nlh)
return -EMSGSIZE;
rtm = nlmsg_data(nlh);
skb = nlmsg_new(mroute_msgsize(mfc->mfc_parent >= MAXVIFS, mrt->maxvif),
GFP_ATOMIC);
- if (skb == NULL)
+ if (!skb)
goto errout;
err = ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0);
struct mr_table *mrt;
mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
- if (mrt == NULL)
+ if (!mrt)
return ERR_PTR(-ENOENT);
iter->mrt = mrt;
struct mr_table *mrt;
mrt = ipmr_get_table(net, RT_TABLE_DEFAULT);
- if (mrt == NULL)
+ if (!mrt)
return ERR_PTR(-ENOENT);
it->mrt = mrt;
{
struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
- if (entry->hook == NF_INET_LOCAL_OUT) {
+ if (entry->state.hook == NF_INET_LOCAL_OUT) {
const struct iphdr *iph = ip_hdr(skb);
rt_info->tos = iph->tos;
{
const struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
- if (entry->hook == NF_INET_LOCAL_OUT) {
+ if (entry->state.hook == NF_INET_LOCAL_OUT) {
const struct iphdr *iph = ip_hdr(skb);
if (!(iph->tos == rt_info->tos &&
unsigned int arpt_do_table(struct sk_buff *skb,
unsigned int hook,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct xt_table *table)
{
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
if (!pskb_may_pull(skb, arp_hdr_len(skb->dev)))
return NF_DROP;
- indev = in ? in->name : nulldevname;
- outdev = out ? out->name : nulldevname;
+ indev = state->in ? state->in->name : nulldevname;
+ outdev = state->out ? state->out->name : nulldevname;
local_bh_disable();
addend = xt_write_recseq_begin();
e = get_entry(table_base, private->hook_entry[hook]);
back = get_entry(table_base, private->underflow[hook]);
- acpar.in = in;
- acpar.out = out;
+ acpar.in = state->in;
+ acpar.out = state->out;
acpar.hooknum = hook;
acpar.family = NFPROTO_ARP;
acpar.hotdrop = false;
/* The work comes in here from netfilter.c */
static unsigned int
arptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- const struct net *net = dev_net((in != NULL) ? in : out);
+ const struct net *net = dev_net(state->in ? state->in : state->out);
- return arpt_do_table(skb, ops->hooknum, in, out,
+ return arpt_do_table(skb, ops->hooknum, state,
net->ipv4.arptable_filter);
}
unsigned int
ipt_do_table(struct sk_buff *skb,
unsigned int hook,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct xt_table *table)
{
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
/* Initialization */
ip = ip_hdr(skb);
- indev = in ? in->name : nulldevname;
- outdev = out ? out->name : nulldevname;
+ indev = state->in ? state->in->name : nulldevname;
+ outdev = state->out ? state->out->name : nulldevname;
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
* normally, except that they will NEVER match rules that ask
acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
acpar.thoff = ip_hdrlen(skb);
acpar.hotdrop = false;
- acpar.in = in;
- acpar.out = out;
+ acpar.in = state->in;
+ acpar.out = state->out;
acpar.family = NFPROTO_IPV4;
acpar.hooknum = hook;
#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
/* The packet is traced: log it */
if (unlikely(skb->nf_trace))
- trace_packet(skb, hook, in, out,
+ trace_packet(skb, hook, state->in, state->out,
table->name, private, e);
#endif
/* Standard target? */
static unsigned int
arp_mangle(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct arphdr *arp = arp_hdr(skb);
struct arp_payload *payload;
struct clusterip_config *c;
- struct net *net = dev_net(in ? in : out);
+ struct net *net = dev_net(state->in ? state->in : state->out);
/* we don't care about non-ethernet and non-ipv4 ARP */
if (arp->ar_hrd != htons(ARPHRD_ETHER) ||
* addresses on different interfacs. However, in the CLUSTERIP case
* this wouldn't work, since we didn't subscribe the mcast group on
* other interfaces */
- if (c->dev != out) {
+ if (c->dev != state->out) {
pr_debug("not mangling arp reply on different "
"interface: cip'%s'-skb'%s'\n",
- c->dev->name, out->name);
+ c->dev->name, state->out->name);
clusterip_config_put(c);
return NF_ACCEPT;
}
static unsigned int ipv4_synproxy_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *nhs)
{
- struct synproxy_net *snet = synproxy_pernet(dev_net(in ? : out));
+ struct synproxy_net *snet = synproxy_pernet(dev_net(nhs->in ? : nhs->out));
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
struct nf_conn_synproxy *synproxy;
static unsigned int
iptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
const struct net *net;
/* root is playing with raw sockets. */
return NF_ACCEPT;
- net = dev_net((in != NULL) ? in : out);
- return ipt_do_table(skb, ops->hooknum, in, out,
- net->ipv4.iptable_filter);
+ net = dev_net(state->in ? state->in : state->out);
+ return ipt_do_table(skb, ops->hooknum, state, net->ipv4.iptable_filter);
}
static struct nf_hook_ops *filter_ops __read_mostly;
};
static unsigned int
-ipt_mangle_out(struct sk_buff *skb, const struct net_device *out)
+ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
{
+ struct net_device *out = state->out;
unsigned int ret;
const struct iphdr *iph;
u_int8_t tos;
daddr = iph->daddr;
tos = iph->tos;
- ret = ipt_do_table(skb, NF_INET_LOCAL_OUT, NULL, out,
+ ret = ipt_do_table(skb, NF_INET_LOCAL_OUT, state,
dev_net(out)->ipv4.iptable_mangle);
/* Reroute for ANY change. */
if (ret != NF_DROP && ret != NF_STOLEN) {
static unsigned int
iptable_mangle_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
if (ops->hooknum == NF_INET_LOCAL_OUT)
- return ipt_mangle_out(skb, out);
+ return ipt_mangle_out(skb, state);
if (ops->hooknum == NF_INET_POST_ROUTING)
- return ipt_do_table(skb, ops->hooknum, in, out,
- dev_net(out)->ipv4.iptable_mangle);
+ return ipt_do_table(skb, ops->hooknum, state,
+ dev_net(state->out)->ipv4.iptable_mangle);
/* PREROUTING/INPUT/FORWARD: */
- return ipt_do_table(skb, ops->hooknum, in, out,
- dev_net(in)->ipv4.iptable_mangle);
+ return ipt_do_table(skb, ops->hooknum, state,
+ dev_net(state->in)->ipv4.iptable_mangle);
}
static struct nf_hook_ops *mangle_ops __read_mostly;
static unsigned int iptable_nat_do_chain(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
- return ipt_do_table(skb, ops->hooknum, in, out, net->ipv4.nat_table);
+ return ipt_do_table(skb, ops->hooknum, state, net->ipv4.nat_table);
}
static unsigned int iptable_nat_ipv4_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv4_fn(ops, skb, in, out, iptable_nat_do_chain);
+ return nf_nat_ipv4_fn(ops, skb, state, iptable_nat_do_chain);
}
static unsigned int iptable_nat_ipv4_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv4_in(ops, skb, in, out, iptable_nat_do_chain);
+ return nf_nat_ipv4_in(ops, skb, state, iptable_nat_do_chain);
}
static unsigned int iptable_nat_ipv4_out(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv4_out(ops, skb, in, out, iptable_nat_do_chain);
+ return nf_nat_ipv4_out(ops, skb, state, iptable_nat_do_chain);
}
static unsigned int iptable_nat_ipv4_local_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv4_local_fn(ops, skb, in, out, iptable_nat_do_chain);
+ return nf_nat_ipv4_local_fn(ops, skb, state, iptable_nat_do_chain);
}
static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = {
/* The work comes in here from netfilter.c. */
static unsigned int
iptable_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
const struct net *net;
/* root is playing with raw sockets. */
return NF_ACCEPT;
- net = dev_net((in != NULL) ? in : out);
- return ipt_do_table(skb, ops->hooknum, in, out, net->ipv4.iptable_raw);
+ net = dev_net(state->in ? state->in : state->out);
+ return ipt_do_table(skb, ops->hooknum, state, net->ipv4.iptable_raw);
}
static struct nf_hook_ops *rawtable_ops __read_mostly;
static unsigned int
iptable_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
const struct net *net;
/* Somebody is playing with raw sockets. */
return NF_ACCEPT;
- net = dev_net((in != NULL) ? in : out);
- return ipt_do_table(skb, ops->hooknum, in, out,
+ net = dev_net(state->in ? state->in : state->out);
+ return ipt_do_table(skb, ops->hooknum, state,
net->ipv4.iptable_security);
}
static unsigned int ipv4_helper(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
static unsigned int ipv4_confirm(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
static unsigned int ipv4_conntrack_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_conntrack_in(dev_net(in), PF_INET, ops->hooknum, skb);
+ return nf_conntrack_in(dev_net(state->in), PF_INET, ops->hooknum, skb);
}
static unsigned int ipv4_conntrack_local(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
/* root is playing with raw sockets. */
if (skb->len < sizeof(struct iphdr) ||
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
- return nf_conntrack_in(dev_net(out), PF_INET, ops->hooknum, skb);
+ return nf_conntrack_in(dev_net(state->out), PF_INET, ops->hooknum, skb);
}
/* Connection tracking may drop packets, but never alters them, so
static unsigned int ipv4_conntrack_defrag(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct sock *sk = skb->sk;
struct inet_sock *inet = inet_sk(skb->sk);
unsigned int
nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct))
{
struct nf_conn *ct;
if (!nf_nat_initialized(ct, maniptype)) {
unsigned int ret;
- ret = do_chain(ops, skb, in, out, ct);
+ ret = do_chain(ops, skb, state, ct);
if (ret != NF_ACCEPT)
return ret;
pr_debug("Already setup manip %s for ct %p\n",
maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
ct);
- if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
+ if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat,
+ state->out))
goto oif_changed;
}
break;
/* ESTABLISHED */
NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
ctinfo == IP_CT_ESTABLISHED_REPLY);
- if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
+ if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, state->out))
goto oif_changed;
}
unsigned int
nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct))
{
unsigned int ret;
__be32 daddr = ip_hdr(skb)->daddr;
- ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain);
+ ret = nf_nat_ipv4_fn(ops, skb, state, do_chain);
if (ret != NF_DROP && ret != NF_STOLEN &&
daddr != ip_hdr(skb)->daddr)
skb_dst_drop(skb);
unsigned int
nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct))
{
#ifdef CONFIG_XFRM
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
- ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain);
+ ret = nf_nat_ipv4_fn(ops, skb, state, do_chain);
#ifdef CONFIG_XFRM
if (ret != NF_DROP && ret != NF_STOLEN &&
!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
unsigned int
nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct))
{
const struct nf_conn *ct;
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
- ret = nf_nat_ipv4_fn(ops, skb, in, out, do_chain);
+ ret = nf_nat_ipv4_fn(ops, skb, state, do_chain);
if (ret != NF_DROP && ret != NF_STOLEN &&
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
#include <net/dst.h>
#include <net/netfilter/ipv4/nf_reject.h>
#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_bridge.h>
#include <net/netfilter/ipv4/nf_reject.h>
const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb,
*/
if (oldskb->nf_bridge) {
struct ethhdr *oeth = eth_hdr(oldskb);
- nskb->dev = oldskb->nf_bridge->physindev;
+
+ nskb->dev = nf_bridge_get_physindev(oldskb);
niph->tot_len = htons(nskb->len);
ip_send_check(niph);
if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
static unsigned int
nft_do_chain_arp(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct nft_pktinfo pkt;
- nft_set_pktinfo(&pkt, ops, skb, in, out);
+ nft_set_pktinfo(&pkt, ops, skb, state);
return nft_do_chain(&pkt, ops);
}
static unsigned int nft_do_chain_ipv4(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct nft_pktinfo pkt;
- nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+ nft_set_pktinfo_ipv4(&pkt, ops, skb, state);
return nft_do_chain(&pkt, ops);
}
static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
if (unlikely(skb->len < sizeof(struct iphdr) ||
ip_hdr(skb)->ihl < sizeof(struct iphdr) / 4)) {
return NF_ACCEPT;
}
- return nft_do_chain_ipv4(ops, skb, in, out, okfn);
+ return nft_do_chain_ipv4(ops, skb, state);
}
struct nft_af_info nft_af_ipv4 __read_mostly = {
static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct)
{
struct nft_pktinfo pkt;
- nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+ nft_set_pktinfo_ipv4(&pkt, ops, skb, state);
return nft_do_chain(&pkt, ops);
}
static unsigned int nft_nat_ipv4_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv4_fn(ops, skb, in, out, nft_nat_do_chain);
+ return nf_nat_ipv4_fn(ops, skb, state, nft_nat_do_chain);
}
static unsigned int nft_nat_ipv4_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv4_in(ops, skb, in, out, nft_nat_do_chain);
+ return nf_nat_ipv4_in(ops, skb, state, nft_nat_do_chain);
}
static unsigned int nft_nat_ipv4_out(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv4_out(ops, skb, in, out, nft_nat_do_chain);
+ return nf_nat_ipv4_out(ops, skb, state, nft_nat_do_chain);
}
static unsigned int nft_nat_ipv4_local_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv4_local_fn(ops, skb, in, out, nft_nat_do_chain);
+ return nf_nat_ipv4_local_fn(ops, skb, state, nft_nat_do_chain);
}
static const struct nf_chain_type nft_chain_nat_ipv4 = {
static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
unsigned int ret;
struct nft_pktinfo pkt;
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
- nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+ nft_set_pktinfo_ipv4(&pkt, ops, skb, state);
mark = skb->mark;
iph = ip_hdr(skb);
case NFT_REJECT_TCP_RST:
nf_send_reset(pkt->skb, pkt->ops->hooknum);
break;
+ default:
+ break;
}
data[NFT_REG_VERDICT].verdict = NF_DROP;
ntohs(icmph->un.echo.sequence));
sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id));
- if (sk == NULL) {
+ if (!sk) {
pr_debug("no socket, dropping\n");
return; /* No socket for error */
}
skb_push(skb, skb->data - (u8 *)icmph);
sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id));
- if (sk != NULL) {
+ if (sk) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
pr_debug("rcv on socket %p\n", sk);
read_lock(&raw_v4_hashinfo.lock);
raw_sk = sk_head(&raw_v4_hashinfo.ht[hash]);
- if (raw_sk != NULL) {
+ if (raw_sk) {
iph = (const struct iphdr *)skb->data;
net = dev_net(skb->dev);
skb = sock_alloc_send_skb(sk,
length + hlen + tlen + 15,
flags & MSG_DONTWAIT, &err);
- if (skb == NULL)
+ if (!skb)
goto error;
skb_reserve(skb, hlen);
icmp_out_count(net, ((struct icmphdr *)
skb_transport_header(skb))->type);
- err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
- rt->dst.dev, dst_output);
+ err = NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, sk, skb,
+ NULL, rt->dst.dev, dst_output_sk);
if (err > 0)
err = net_xmit_errno(err);
if (err)
spin_lock_bh(&sk->sk_receive_queue.lock);
skb = skb_peek(&sk->sk_receive_queue);
- if (skb != NULL)
+ if (skb)
amount = skb->len;
spin_unlock_bh(&sk->sk_receive_queue.lock);
return put_user(amount, (int __user *)arg);
__build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
rt = (struct rtable *)odst;
- if (odst->obsolete && odst->ops->check(odst, 0) == NULL) {
+ if (odst->obsolete && !odst->ops->check(odst, 0)) {
rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
if (IS_ERR(rt))
goto out;
/* Primary sanity checks. */
- if (in_dev == NULL)
+ if (!in_dev)
return -EINVAL;
if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr) ||
/* get a working reference to the output device */
out_dev = __in_dev_get_rcu(FIB_RES_DEV(*res));
- if (out_dev == NULL) {
+ if (!out_dev) {
net_crit_ratelimited("Bug in ip_route_input_slow(). Please report.\n");
return -EINVAL;
}
fnhe = find_exception(&FIB_RES_NH(*res), daddr);
if (do_cache) {
- if (fnhe != NULL)
+ if (fnhe)
rth = rcu_dereference(fnhe->fnhe_rth_input);
else
rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);
ipv4_is_lbcast(fl4->daddr))) {
/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
dev_out = __ip_dev_find(net, fl4->saddr, false);
- if (dev_out == NULL)
+ if (!dev_out)
goto out;
/* Special hack: user can direct multicasts
if (fl4->flowi4_oif) {
dev_out = dev_get_by_index_rcu(net, fl4->flowi4_oif);
rth = ERR_PTR(-ENODEV);
- if (dev_out == NULL)
+ if (!dev_out)
goto out;
/* RACE: Check return value of inet_select_addr instead. */
u32 metrics[RTAX_MAX];
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*r), flags);
- if (nlh == NULL)
+ if (!nlh)
return -EMSGSIZE;
r = nlmsg_data(nlh);
rtm = nlmsg_data(nlh);
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
- if (skb == NULL) {
+ if (!skb) {
err = -ENOBUFS;
goto errout;
}
struct net_device *dev;
dev = __dev_get_by_index(net, iif);
- if (dev == NULL) {
+ if (!dev) {
err = -ENODEV;
goto errout_free;
}
tbl = ipv4_route_flush_table;
if (!net_eq(net, &init_net)) {
tbl = kmemdup(tbl, sizeof(ipv4_route_flush_table), GFP_KERNEL);
- if (tbl == NULL)
+ if (!tbl)
goto err_dup;
/* Don't export sysctls to unprivileged users */
tbl[0].extra1 = net;
net->ipv4.route_hdr = register_net_sysctl(net, "net/ipv4/route", tbl);
- if (net->ipv4.route_hdr == NULL)
+ if (!net->ipv4.route_hdr)
goto err_reg;
return 0;
int i;
table = kmemdup(table, sizeof(ipv4_net_table), GFP_KERNEL);
- if (table == NULL)
+ if (!table)
goto err_alloc;
/* Update the variables to point into the current struct net */
}
net->ipv4.ipv4_hdr = register_net_sysctl(net, "net/ipv4", table);
- if (net->ipv4.ipv4_hdr == NULL)
+ if (!net->ipv4.ipv4_hdr)
goto err_reg;
net->ipv4.sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);
struct ctl_table_header *hdr;
hdr = register_net_sysctl(&init_net, "net/ipv4", ipv4_table);
- if (hdr == NULL)
+ if (!hdr)
return -ENOMEM;
if (register_pernet_subsys(&ipv4_sysctl_ops)) {
/* Connected or passive Fast Open socket? */
if (sk->sk_state != TCP_SYN_SENT &&
- (sk->sk_state != TCP_SYN_RECV || tp->fastopen_rsk != NULL)) {
+ (sk->sk_state != TCP_SYN_RECV || tp->fastopen_rsk)) {
int target = sock_rcvlowat(sk, 0, INT_MAX);
if (tp->urg_seq == tp->copied_seq &&
void tcp_free_fastopen_req(struct tcp_sock *tp)
{
- if (tp->fastopen_req != NULL) {
+ if (tp->fastopen_req) {
kfree(tp->fastopen_req);
tp->fastopen_req = NULL;
}
if (!(sysctl_tcp_fastopen & TFO_CLIENT_ENABLE))
return -EOPNOTSUPP;
- if (tp->fastopen_req != NULL)
+ if (tp->fastopen_req)
return -EALREADY; /* Another Fast Open is in progress */
tp->fastopen_req = kzalloc(sizeof(struct tcp_fastopen_request),
sk->sk_allocation);
- if (unlikely(tp->fastopen_req == NULL))
+ if (unlikely(!tp->fastopen_req))
return -ENOBUFS;
tp->fastopen_req->data = msg;
tp->fastopen_req->size = size;
* aborted (e.g., closed with unread data) before 3WHS
* finishes.
*/
- if (req != NULL)
+ if (req)
reqsk_fastopen_remove(sk, req, false);
inet_csk_destroy_sock(sk);
}
break;
case TCP_FASTOPEN:
- if (icsk->icsk_accept_queue.fastopenq != NULL)
+ if (icsk->icsk_accept_queue.fastopenq)
val = icsk->icsk_accept_queue.fastopenq->max_qlen;
else
val = 0;
tcp_set_state(sk, TCP_CLOSE);
tcp_clear_xmit_timers(sk);
- if (req != NULL)
+ if (req)
reqsk_fastopen_remove(sk, req, false);
sk->sk_shutdown = SHUTDOWN_MASK;
r->idiag_rqueue = max_t(int, tp->rcv_nxt - tp->copied_seq, 0);
r->idiag_wqueue = tp->write_seq - tp->snd_una;
}
- if (info != NULL)
+ if (info)
tcp_get_info(sk, info);
}
req->sk = NULL;
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
- if (child == NULL)
+ if (!child)
return false;
spin_lock(&queue->fastopenq->lock);
sk->sk_data_ready(sk);
bh_unlock_sock(child);
sock_put(child);
- WARN_ON(req->sk == NULL);
+ WARN_ON(!req->sk);
return true;
}
* temporarily vs a server not supporting Fast Open at all.
*/
fastopenq = inet_csk(sk)->icsk_accept_queue.fastopenq;
- if (fastopenq == NULL || fastopenq->max_qlen == 0)
+ if (!fastopenq || fastopenq->max_qlen == 0)
return false;
if (fastopenq->qlen >= fastopenq->max_qlen) {
} else if (foc->len > 0) /* Client presents an invalid cookie */
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENPASSIVEFAIL);
+ valid_foc.exp = foc->exp;
*foc = valid_foc;
return false;
}
/* This must be called before lost_out is incremented */
static void tcp_verify_retransmit_hint(struct tcp_sock *tp, struct sk_buff *skb)
{
- if ((tp->retransmit_skb_hint == NULL) ||
+ if (!tp->retransmit_skb_hint ||
before(TCP_SKB_CB(skb)->seq,
TCP_SKB_CB(tp->retransmit_skb_hint)->seq))
tp->retransmit_skb_hint = skb;
fack_count += pcount;
/* Lost marker hint past SACKed? Tweak RFC3517 cnt */
- if (!tcp_is_fack(tp) && (tp->lost_skb_hint != NULL) &&
+ if (!tcp_is_fack(tp) && tp->lost_skb_hint &&
before(start_seq, TCP_SKB_CB(tp->lost_skb_hint)->seq))
tp->lost_cnt_hint += pcount;
if (!before(TCP_SKB_CB(skb)->seq, end_seq))
break;
- if ((next_dup != NULL) &&
+ if (next_dup &&
before(TCP_SKB_CB(skb)->seq, next_dup->end_seq)) {
in_sack = tcp_match_skb_to_sack(sk, skb,
next_dup->start_seq,
if (in_sack <= 0) {
tmp = tcp_shift_skb_data(sk, skb, state,
start_seq, end_seq, dup_sack);
- if (tmp != NULL) {
+ if (tmp) {
if (tmp != skb) {
skb = tmp;
continue;
struct tcp_sacktag_state *state,
u32 skip_to_seq)
{
- if (next_dup == NULL)
+ if (!next_dup)
return skb;
if (before(next_dup->start_seq, skip_to_seq)) {
if (tcp_highest_sack_seq(tp) == cache->end_seq) {
/* ...but better entrypoint exists! */
skb = tcp_highest_sack(sk);
- if (skb == NULL)
+ if (!skb)
break;
state.fack_count = tp->fackets_out;
cache++;
if (!before(start_seq, tcp_highest_sack_seq(tp))) {
skb = tcp_highest_sack(sk);
- if (skb == NULL)
+ if (!skb)
break;
state.fack_count = tp->fackets_out;
}
if (!first_ackt.v64)
first_ackt = last_ackt;
- if (!(sacked & TCPCB_SACKED_ACKED))
+ if (!(sacked & TCPCB_SACKED_ACKED)) {
reord = min(pkts_acked, reord);
- if (!after(scb->end_seq, tp->high_seq))
- flag |= FLAG_ORIG_SACK_ACKED;
+ if (!after(scb->end_seq, tp->high_seq))
+ flag |= FLAG_ORIG_SACK_ACKED;
+ }
}
if (sacked & TCPCB_SACKED_ACKED)
return 0;
}
+static void tcp_parse_fastopen_option(int len, const unsigned char *cookie,
+ bool syn, struct tcp_fastopen_cookie *foc,
+ bool exp_opt)
+{
+ /* Valid only in SYN or SYN-ACK with an even length. */
+ if (!foc || !syn || len < 0 || (len & 1))
+ return;
+
+ if (len >= TCP_FASTOPEN_COOKIE_MIN &&
+ len <= TCP_FASTOPEN_COOKIE_MAX)
+ memcpy(foc->val, cookie, len);
+ else if (len != 0)
+ len = -1;
+ foc->len = len;
+ foc->exp = exp_opt;
+}
+
/* Look for tcp options. Normally only called on SYN and SYNACK packets.
* But, this can also be called on packets in the established flow when
* the fast version below fails.
*/
break;
#endif
+ case TCPOPT_FASTOPEN:
+ tcp_parse_fastopen_option(
+ opsize - TCPOLEN_FASTOPEN_BASE,
+ ptr, th->syn, foc, false);
+ break;
+
case TCPOPT_EXP:
/* Fast Open option shares code 254 using a
- * 16 bits magic number. It's valid only in
- * SYN or SYN-ACK with an even size.
+ * 16 bits magic number.
*/
- if (opsize < TCPOLEN_EXP_FASTOPEN_BASE ||
- get_unaligned_be16(ptr) != TCPOPT_FASTOPEN_MAGIC ||
- foc == NULL || !th->syn || (opsize & 1))
- break;
- foc->len = opsize - TCPOLEN_EXP_FASTOPEN_BASE;
- if (foc->len >= TCP_FASTOPEN_COOKIE_MIN &&
- foc->len <= TCP_FASTOPEN_COOKIE_MAX)
- memcpy(foc->val, ptr + 2, foc->len);
- else if (foc->len != 0)
- foc->len = -1;
+ if (opsize >= TCPOLEN_EXP_FASTOPEN_BASE &&
+ get_unaligned_be16(ptr) ==
+ TCPOPT_FASTOPEN_MAGIC)
+ tcp_parse_fastopen_option(opsize -
+ TCPOLEN_EXP_FASTOPEN_BASE,
+ ptr + 2, th->syn, foc, true);
break;
}
struct sk_buff *head;
u32 start, end;
- if (skb == NULL)
+ if (!skb)
return;
start = TCP_SKB_CB(skb)->seq;
{
struct tcp_sock *tp = tcp_sk(sk);
- if (unlikely(sk->sk_rx_dst == NULL))
+ if (unlikely(!sk->sk_rx_dst))
inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb);
/*
* Header prediction.
tcp_set_state(sk, TCP_ESTABLISHED);
- if (skb != NULL) {
+ if (skb) {
icsk->icsk_af_ops->sk_rx_dst_set(sk, skb);
security_inet_conn_established(sk, skb);
}
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *data = tp->syn_data ? tcp_write_queue_head(sk) : NULL;
- u16 mss = tp->rx_opt.mss_clamp;
- bool syn_drop;
+ u16 mss = tp->rx_opt.mss_clamp, try_exp = 0;
+ bool syn_drop = false;
if (mss == tp->rx_opt.user_mss) {
struct tcp_options_received opt;
mss = opt.mss_clamp;
}
- if (!tp->syn_fastopen) /* Ignore an unsolicited cookie */
+ if (!tp->syn_fastopen) {
+ /* Ignore an unsolicited cookie */
cookie->len = -1;
+ } else if (tp->total_retrans) {
+ /* SYN timed out and the SYN-ACK neither has a cookie nor
+ * acknowledges data. Presumably the remote received only
+ * the retransmitted (regular) SYNs: either the original
+ * SYN-data or the corresponding SYN-ACK was dropped.
+ */
+ syn_drop = (cookie->len < 0 && data);
+ } else if (cookie->len < 0 && !tp->syn_data) {
+ /* We requested a cookie but didn't get it. If we did not use
+ * the (old) exp opt format then try so next time (try_exp=1).
+ * Otherwise we go back to use the RFC7413 opt (try_exp=2).
+ */
+ try_exp = tp->syn_fastopen_exp ? 2 : 1;
+ }
- /* The SYN-ACK neither has cookie nor acknowledges the data. Presumably
- * the remote receives only the retransmitted (regular) SYNs: either
- * the original SYN-data or the corresponding SYN-ACK is lost.
- */
- syn_drop = (cookie->len <= 0 && data && tp->total_retrans);
-
- tcp_fastopen_cache_set(sk, mss, cookie, syn_drop);
+ tcp_fastopen_cache_set(sk, mss, cookie, syn_drop, try_exp);
if (data) { /* Retransmit unacked data in SYN */
tcp_for_write_queue_from(data, sk) {
}
req = tp->fastopen_rsk;
- if (req != NULL) {
+ if (req) {
WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV &&
sk->sk_state != TCP_FIN_WAIT1);
- if (tcp_check_req(sk, skb, req, true) == NULL)
+ if (!tcp_check_req(sk, skb, req, true))
goto discard;
}
* ACK we have received, this would have acknowledged
* our SYNACK so stop the SYNACK timer.
*/
- if (req != NULL) {
+ if (req) {
/* Return RST if ack_seq is invalid.
* Note that RFC793 only says to generate a
* DUPACK for it but for TCP Fast Open it seems
and use initial timestamp retrieved from peer table.
*/
if (tcptw->tw_ts_recent_stamp &&
- (twp == NULL || (sysctl_tcp_tw_reuse &&
+ (!twp || (sysctl_tcp_tw_reuse &&
get_seconds() - tcptw->tw_ts_recent_stamp > 1))) {
tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
if (tp->write_seq == 0)
/* Only in fast or simultaneous open. If a fast open socket is
* is already accepted it is treated as a connected one below.
*/
- if (fastopen && fastopen->sk == NULL)
+ if (fastopen && !fastopen->sk)
break;
if (!sock_owned_by_user(sk)) {
struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk,
const struct sock *addr_sk)
{
- union tcp_md5_addr *addr;
+ const union tcp_md5_addr *addr;
- addr = (union tcp_md5_addr *)&sk->sk_daddr;
+ addr = (const union tcp_md5_addr *)&addr_sk->sk_daddr;
return tcp_md5_do_lookup(sk, addr, AF_INET);
}
EXPORT_SYMBOL(tcp_v4_md5_lookup);
/* Copy over the MD5 key from the original socket */
key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&newinet->inet_daddr,
AF_INET);
- if (key != NULL) {
+ if (key) {
/*
* We're using one, so create a matching key
* on the newsk structure. If we fail to get
sk_mark_napi_id(sk, skb);
if (dst) {
if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
- dst->ops->check(dst, 0) == NULL) {
+ !dst->ops->check(dst, 0)) {
dst_release(dst);
sk->sk_rx_dst = NULL;
}
skb->sk = sk;
skb->destructor = sock_edemux;
if (sk_fullsock(sk)) {
- struct dst_entry *dst = sk->sk_rx_dst;
+ struct dst_entry *dst = READ_ONCE(sk->sk_rx_dst);
if (dst)
dst = dst_check(dst, 0);
if (inet_csk(sk)->icsk_bind_hash)
inet_put_port(sk);
- BUG_ON(tp->fastopen_rsk != NULL);
+ BUG_ON(tp->fastopen_rsk);
/* If socket is aborted during connect operation */
tcp_free_fastopen_req(tp);
struct tcp_fastopen_metrics {
u16 mss;
- u16 syn_loss:10; /* Recurring Fast Open SYN losses */
+ u16 syn_loss:10, /* Recurring Fast Open SYN losses */
+ try_exp:2; /* Request w/ exp. option (once) */
unsigned long last_syn_loss; /* Last Fast Open SYN loss */
struct tcp_fastopen_cookie cookie;
};
if (fastopen_clear) {
tm->tcpm_fastopen.mss = 0;
tm->tcpm_fastopen.syn_loss = 0;
+ tm->tcpm_fastopen.try_exp = 0;
+ tm->tcpm_fastopen.cookie.exp = false;
tm->tcpm_fastopen.cookie.len = 0;
}
}
struct tcp_metrics_block *tm;
u32 val, crtt = 0; /* cached RTT scaled by 8 */
- if (dst == NULL)
+ if (!dst)
goto reset;
dst_confirm(dst);
if (tfom->mss)
*mss = tfom->mss;
*cookie = tfom->cookie;
+ if (cookie->len <= 0 && tfom->try_exp == 1)
+ cookie->exp = true;
*syn_loss = tfom->syn_loss;
*last_syn_loss = *syn_loss ? tfom->last_syn_loss : 0;
} while (read_seqretry(&fastopen_seqlock, seq));
}
void tcp_fastopen_cache_set(struct sock *sk, u16 mss,
- struct tcp_fastopen_cookie *cookie, bool syn_lost)
+ struct tcp_fastopen_cookie *cookie, bool syn_lost,
+ u16 try_exp)
{
struct dst_entry *dst = __sk_dst_get(sk);
struct tcp_metrics_block *tm;
tfom->mss = mss;
if (cookie && cookie->len > 0)
tfom->cookie = *cookie;
+ else if (try_exp > tfom->try_exp &&
+ tfom->cookie.len <= 0 && !tfom->cookie.exp)
+ tfom->try_exp = try_exp;
if (syn_lost) {
++tfom->syn_loss;
tfom->last_syn_loss = jiffies;
if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
tw = inet_twsk_alloc(sk, state);
- if (tw != NULL) {
+ if (tw) {
struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);
struct inet_sock *inet = inet_sk(sk);
struct tcp_md5sig_key *key;
tcptw->tw_md5_key = NULL;
key = tp->af_specific->md5_lookup(sk, sk);
- if (key != NULL) {
+ if (key) {
tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
if (tcptw->tw_md5_key && !tcp_alloc_md5sig_pool())
BUG();
{
struct sock *newsk = inet_csk_clone_lock(sk, req, GFP_ATOMIC);
- if (newsk != NULL) {
+ if (newsk) {
const struct inet_request_sock *ireq = inet_rsk(req);
struct tcp_request_sock *treq = tcp_rsk(req);
struct inet_connection_sock *newicsk = inet_csk(newsk);
LINUX_MIB_TCPACKSKIPPEDSYNRECV,
&tcp_rsk(req)->last_oow_ack_time) &&
- !inet_rtx_syn_ack(sk, req))
- mod_timer_pending(&req->rsk_timer, jiffies +
- min(TCP_TIMEOUT_INIT << req->num_timeout,
- TCP_RTO_MAX));
+ !inet_rtx_syn_ack(sk, req)) {
+ unsigned long expires = jiffies;
+
+ expires += min(TCP_TIMEOUT_INIT << req->num_timeout,
+ TCP_RTO_MAX);
+ if (!fastopen)
+ mod_timer_pending(&req->rsk_timer, expires);
+ else
+ req->rsk_timer.expires = expires;
+ }
return NULL;
}
* socket is created, wait for troubles.
*/
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
- if (child == NULL)
+ if (!child)
goto listen_overflow;
inet_csk_reqsk_queue_unlink(sk, req);
if (unlikely(OPTION_FAST_OPEN_COOKIE & options)) {
struct tcp_fastopen_cookie *foc = opts->fastopen_cookie;
+ u8 *p = (u8 *)ptr;
+ u32 len; /* Fast Open option length */
+
+ if (foc->exp) {
+ len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
+ *ptr = htonl((TCPOPT_EXP << 24) | (len << 16) |
+ TCPOPT_FASTOPEN_MAGIC);
+ p += TCPOLEN_EXP_FASTOPEN_BASE;
+ } else {
+ len = TCPOLEN_FASTOPEN_BASE + foc->len;
+ *p++ = TCPOPT_FASTOPEN;
+ *p++ = len;
+ }
- *ptr++ = htonl((TCPOPT_EXP << 24) |
- ((TCPOLEN_EXP_FASTOPEN_BASE + foc->len) << 16) |
- TCPOPT_FASTOPEN_MAGIC);
-
- memcpy(ptr, foc->val, foc->len);
- if ((foc->len & 3) == 2) {
- u8 *align = ((u8 *)ptr) + foc->len;
- align[0] = align[1] = TCPOPT_NOP;
+ memcpy(p, foc->val, foc->len);
+ if ((len & 3) == 2) {
+ p[foc->len] = TCPOPT_NOP;
+ p[foc->len + 1] = TCPOPT_NOP;
}
- ptr += (foc->len + 3) >> 2;
+ ptr += (len + 3) >> 2;
}
}
opts->mss = tcp_advertise_mss(sk);
remaining -= TCPOLEN_MSS_ALIGNED;
- if (likely(sysctl_tcp_timestamps && *md5 == NULL)) {
+ if (likely(sysctl_tcp_timestamps && !*md5)) {
opts->options |= OPTION_TS;
opts->tsval = tcp_skb_timestamp(skb) + tp->tsoffset;
opts->tsecr = tp->rx_opt.ts_recent;
}
if (fastopen && fastopen->cookie.len >= 0) {
- u32 need = TCPOLEN_EXP_FASTOPEN_BASE + fastopen->cookie.len;
+ u32 need = fastopen->cookie.len;
+
+ need += fastopen->cookie.exp ? TCPOLEN_EXP_FASTOPEN_BASE :
+ TCPOLEN_FASTOPEN_BASE;
need = (need + 3) & ~3U; /* Align to 32 bits */
if (remaining >= need) {
opts->options |= OPTION_FAST_OPEN_COOKIE;
opts->fastopen_cookie = &fastopen->cookie;
remaining -= need;
tp->syn_fastopen = 1;
+ tp->syn_fastopen_exp = fastopen->cookie.exp ? 1 : 0;
}
}
remaining -= TCPOLEN_SACKPERM_ALIGNED;
}
if (foc != NULL && foc->len >= 0) {
- u32 need = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
+ u32 need = foc->len;
+
+ need += foc->exp ? TCPOLEN_EXP_FASTOPEN_BASE :
+ TCPOLEN_FASTOPEN_BASE;
need = (need + 3) & ~3U; /* Align to 32 bits */
if (remaining >= need) {
opts->options |= OPTION_FAST_OPEN_COOKIE;
/* Get a new skb... force flag on. */
buff = sk_stream_alloc_skb(sk, nsize, gfp);
- if (buff == NULL)
+ if (!buff)
return -ENOMEM; /* We'll just try again later. */
sk->sk_wmem_queued += buff->truesize;
return tcp_fragment(sk, skb, len, mss_now, gfp);
buff = sk_stream_alloc_skb(sk, 0, gfp);
- if (unlikely(buff == NULL))
+ if (unlikely(!buff))
return -ENOMEM;
sk->sk_wmem_queued += buff->truesize;
}
/* We're allowed to probe. Build it now. */
- if ((nskb = sk_stream_alloc_skb(sk, probe_size, GFP_ATOMIC)) == NULL)
+ nskb = sk_stream_alloc_skb(sk, probe_size, GFP_ATOMIC);
+ if (!nskb)
return -1;
sk->sk_wmem_queued += nskb->truesize;
sk_mem_charge(sk, nskb->truesize);
int mss = tcp_current_mss(sk);
int err = -1;
- if (tcp_send_head(sk) != NULL) {
+ if (tcp_send_head(sk)) {
err = tcp_write_xmit(sk, mss, TCP_NAGLE_OFF, 2, GFP_ATOMIC);
goto rearm_timer;
}
if (skb == tcp_send_head(sk))
break;
/* we could do better than to assign each time */
- if (hole == NULL)
+ if (!hole)
tp->retransmit_skb_hint = skb;
/* Assume this retransmit will generate
if (!tcp_can_forward_retransmit(sk))
break;
/* Backtrack if necessary to non-L'ed skb */
- if (hole != NULL) {
+ if (hole) {
skb = hole;
hole = NULL;
}
goto begin_fwd;
} else if (!(sacked & TCPCB_LOST)) {
- if (hole == NULL && !(sacked & (TCPCB_SACKED_RETRANS|TCPCB_SACKED_ACKED)))
+ if (!hole && !(sacked & (TCPCB_SACKED_RETRANS|TCPCB_SACKED_ACKED)))
hole = skb;
continue;
*/
mss_now = tcp_current_mss(sk);
- if (tcp_send_head(sk) != NULL) {
+ if (tcp_send_head(sk)) {
TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN;
TCP_SKB_CB(skb)->end_seq++;
tp->write_seq++;
struct sk_buff *skb;
skb = tcp_write_queue_head(sk);
- if (skb == NULL || !(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN)) {
+ if (!skb || !(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN)) {
pr_debug("%s: wrong queue state\n", __func__);
return -EFAULT;
}
if (!(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK)) {
if (skb_cloned(skb)) {
struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
- if (nskb == NULL)
+ if (!nskb)
return -ENOMEM;
tcp_unlink_write_queue(skb, sk);
__skb_header_release(nskb);
(sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
#ifdef CONFIG_TCP_MD5SIG
- if (tp->af_specific->md5_lookup(sk, sk) != NULL)
+ if (tp->af_specific->md5_lookup(sk, sk))
tp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED;
#endif
* sock.
*/
buff = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
- if (buff == NULL) {
+ if (!buff) {
inet_csk_schedule_ack(sk);
inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN;
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
/* We don't queue it, tcp_transmit_skb() sets ownership. */
skb = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
- if (skb == NULL)
+ if (!skb)
return -1;
/* Reserve space for headers and set control bits. */
if (sk->sk_state == TCP_CLOSE)
return -1;
- if ((skb = tcp_send_head(sk)) != NULL &&
- before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp))) {
+ skb = tcp_send_head(sk);
+ if (skb && before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp))) {
int err;
unsigned int mss = tcp_current_mss(sk);
unsigned int seg_size = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq;
if (icsk->icsk_retransmits) {
dst_negative_advice(sk);
if (tp->syn_fastopen || tp->syn_data)
- tcp_fastopen_cache_set(sk, 0, NULL, true);
+ tcp_fastopen_cache_set(sk, 0, NULL, true, 0);
if (tp->syn_data)
NET_INC_STATS_BH(sock_net(sk),
LINUX_MIB_TCPFASTOPENACTIVEFAIL);
udp_ehash_secret + net_hash_mix(net));
}
-
/* called with read_rcu_lock() */
static struct sock *udp4_lib_lookup2(struct net *net,
__be32 saddr, __be16 sport,
sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
iph->saddr, uh->source, skb->dev->ifindex, udptable);
- if (sk == NULL) {
+ if (!sk) {
ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
return; /* No socket for error */
}
if (connected)
rt = (struct rtable *)sk_dst_check(sk, 0);
- if (rt == NULL) {
+ if (!rt) {
struct net *net = sock_net(sk);
fl4 = &fl4_stack;
return ret;
}
-
/**
* first_packet_length - return length of first packet in receive queue
* @sk: socket
goto try_again;
}
-
int udp_disconnect(struct sock *sk, int flags)
{
struct inet_sock *inet = inet_sk(sk);
/* if we're overly short, let UDP handle it */
encap_rcv = ACCESS_ONCE(up->encap_rcv);
- if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {
+ if (skb->len > sizeof(struct udphdr) && encap_rcv) {
int ret;
/* Verify checksum before giving to encap */
udp_lib_checksum_complete(skb))
goto csum_error;
-
if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_RCVBUFERRORS,
is_udplite);
return -1;
}
-
static void flush_stack(struct sock **stack, unsigned int count,
struct sk_buff *skb, unsigned int final)
{
for (i = 0; i < count; i++) {
sk = stack[i];
- if (likely(skb1 == NULL))
+ if (likely(!skb1))
skb1 = (i == final) ? skb : skb_clone(skb, GFP_ATOMIC);
if (!skb1) {
saddr, daddr, udptable, proto);
sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
- if (sk != NULL) {
+ if (sk) {
int ret;
if (inet_get_convert_csum(sk) && uh->check && !IS_UDPLITE(sk))
goto out_nosk;
err = -ENOENT;
- if (sk == NULL)
+ if (!sk)
goto out_nosk;
err = sock_diag_check_cookie(sk, req->id.idiag_cookie);
pr_warn("udp_del_offload: didn't find offload for port %d\n", ntohs(uo->port));
unlock:
spin_unlock(&udp_offload_lock);
- if (uo_priv != NULL)
+ if (uo_priv)
call_rcu(&uo_priv->rcu, udp_offload_free_routine);
}
EXPORT_SYMBOL(udp_del_offload);
break;
}
- if (uo_priv != NULL) {
+ if (uo_priv) {
NAPI_GRO_CB(skb)->proto = uo_priv->offload->ipproto;
err = uo_priv->offload->callbacks.gro_complete(skb,
nhoff + sizeof(struct udphdr),
}
EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
-int udp_tunnel_xmit_skb(struct rtable *rt, struct sk_buff *skb,
+int udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
__be32 src, __be32 dst, __u8 tos, __u8 ttl,
__be16 df, __be16 src_port, __be16 dst_port,
bool xnet, bool nocheck)
udp_set_csum(nocheck, skb, src, dst, skb->len);
- return iptunnel_xmit(skb->sk, rt, skb, src, dst, IPPROTO_UDP,
+ return iptunnel_xmit(sk, rt, skb, src, dst, IPPROTO_UDP,
tos, ttl, df, xnet);
}
EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb);
return xfrm4_extract_header(skb);
}
-static inline int xfrm4_rcv_encap_finish(struct sk_buff *skb)
+static inline int xfrm4_rcv_encap_finish(struct sock *sk, struct sk_buff *skb)
{
- if (skb_dst(skb) == NULL) {
+ if (!skb_dst(skb)) {
const struct iphdr *iph = ip_hdr(skb);
if (ip_route_input_noref(skb, iph->daddr, iph->saddr,
iph->tot_len = htons(skb->len);
ip_send_check(iph);
- NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+ NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL, skb,
+ skb->dev, NULL,
xfrm4_rcv_encap_finish);
return 0;
}
}
EXPORT_SYMBOL(xfrm4_prepare_output);
-int xfrm4_output_finish(struct sk_buff *skb)
+int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb)
{
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
#endif
- return xfrm_output(skb);
+ return xfrm_output(sk, skb);
}
-static int __xfrm4_output(struct sk_buff *skb)
+static int __xfrm4_output(struct sock *sk, struct sk_buff *skb)
{
struct xfrm_state *x = skb_dst(skb)->xfrm;
#ifdef CONFIG_NETFILTER
if (!x) {
IPCB(skb)->flags |= IPSKB_REROUTED;
- return dst_output(skb);
+ return dst_output_sk(sk, skb);
}
#endif
- return x->outer_mode->afinfo->output_finish(skb);
+ return x->outer_mode->afinfo->output_finish(sk, skb);
}
int xfrm4_output(struct sock *sk, struct sk_buff *skb)
{
- return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb,
+ return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, sk, skb,
NULL, skb_dst(skb)->dev, __xfrm4_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}
{
struct ctl_table *table;
- if (net->ipv4.xfrm4_hdr == NULL)
+ if (!net->ipv4.xfrm4_hdr)
return;
table = net->ipv4.xfrm4_hdr->ctl_table_arg;
(dev->addr_len &&
nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
nla_put_u32(skb, IFLA_MTU, dev->mtu) ||
- (dev->ifindex != dev->iflink &&
- nla_put_u32(skb, IFLA_LINK, dev->iflink)))
+ (dev->ifindex != dev_get_iflink(dev) &&
+ nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))))
goto nla_put_failure;
protoinfo = nla_nest_start(skb, IFLA_PROTINFO);
if (!protoinfo)
static void __net_exit fib6_rules_net_exit(struct net *net)
{
+ rtnl_lock();
fib_rules_unregister(net->ipv6.fib6_rules_ops);
+ rtnl_unlock();
}
static struct pernet_operations fib6_rules_net_ops = {
skb_set_inner_protocol(skb, protocol);
- ip6tunnel_xmit(skb, dev);
+ ip6tunnel_xmit(NULL, skb, dev);
if (ndst)
ip6_tnl_dst_store(tunnel, ndst);
return 0;
.ndo_do_ioctl = ip6gre_tunnel_ioctl,
.ndo_change_mtu = ip6gre_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64,
+ .ndo_get_iflink = ip6_tnl_get_iflink,
};
static void ip6gre_dev_free(struct net_device *dev)
if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
dev->mtu -= 8;
dev->flags |= IFF_NOARP;
- dev->iflink = 0;
dev->addr_len = sizeof(struct in6_addr);
netif_keep_dst(dev);
}
u64_stats_init(&ip6gre_tunnel_stats->syncp);
}
- dev->iflink = tunnel->parms.link;
-
return 0;
}
if (!dev->tstats)
return -ENOMEM;
- dev->iflink = tunnel->parms.link;
-
return 0;
}
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = ip6gre_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64,
+ .ndo_get_iflink = ip6_tnl_get_iflink,
};
static void ip6gre_tap_setup(struct net_device *dev)
dev->netdev_ops = &ip6gre_tap_netdev_ops;
dev->destructor = ip6gre_dev_free;
- dev->iflink = 0;
dev->features |= NETIF_F_NETNS_LOCAL;
}
#include <net/xfrm.h>
#include <net/inet_ecn.h>
-
-int ip6_rcv_finish(struct sk_buff *skb)
+int ip6_rcv_finish(struct sock *sk, struct sk_buff *skb)
{
if (sysctl_ip_early_demux && !skb_dst(skb) && skb->sk == NULL) {
const struct inet6_protocol *ipprot;
/* Must drop socket now because of tproxy. */
skb_orphan(skb);
- return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, dev, NULL,
+ return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NULL, skb,
+ dev, NULL,
ip6_rcv_finish);
err:
IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS);
*/
-static int ip6_input_finish(struct sk_buff *skb)
+static int ip6_input_finish(struct sock *sk, struct sk_buff *skb)
{
struct net *net = dev_net(skb_dst(skb)->dev);
const struct inet6_protocol *ipprot;
int ip6_input(struct sk_buff *skb)
{
- return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
+ return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_IN, NULL, skb,
+ skb->dev, NULL,
ip6_input_finish);
}
#include <net/checksum.h>
#include <linux/mroute6.h>
-static int ip6_finish_output2(struct sk_buff *skb)
+static int ip6_finish_output2(struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct net_device *dev = dst->dev;
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb));
- if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(skb->sk) &&
+ if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) &&
((mroute6_socket(dev_net(dev), skb) &&
!(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
*/
if (newskb)
NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING,
- newskb, NULL, newskb->dev,
+ sk, newskb, NULL, newskb->dev,
dev_loopback_xmit);
if (ipv6_hdr(skb)->hop_limit == 0) {
return -EINVAL;
}
-static int ip6_finish_output(struct sk_buff *skb)
+static int ip6_finish_output(struct sock *sk, struct sk_buff *skb)
{
if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
dst_allfrag(skb_dst(skb)) ||
(IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
- return ip6_fragment(skb, ip6_finish_output2);
+ return ip6_fragment(sk, skb, ip6_finish_output2);
else
- return ip6_finish_output2(skb);
+ return ip6_finish_output2(sk, skb);
}
int ip6_output(struct sock *sk, struct sk_buff *skb)
return 0;
}
- return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb, NULL, dev,
+ return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, sk, skb,
+ NULL, dev,
ip6_finish_output,
!(IP6CB(skb)->flags & IP6SKB_REROUTED));
}
if ((skb->len <= mtu) || skb->ignore_df || skb_is_gso(skb)) {
IP6_UPD_PO_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_OUT, skb->len);
- return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
- dst->dev, dst_output);
+ return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+ NULL, dst->dev, dst_output_sk);
}
skb->dev = dst->dev;
return 0;
}
-static inline int ip6_forward_finish(struct sk_buff *skb)
+static inline int ip6_forward_finish(struct sock *sk, struct sk_buff *skb)
{
skb_sender_cpu_clear(skb);
- return dst_output(skb);
+ return dst_output_sk(sk, skb);
}
static unsigned int ip6_dst_mtu_forward(const struct dst_entry *dst)
IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
IP6_ADD_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
- return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, skb, skb->dev, dst->dev,
+ return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb,
+ skb->dev, dst->dev,
ip6_forward_finish);
error:
skb_copy_secmark(to, from);
}
-int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
+int ip6_fragment(struct sock *sk, struct sk_buff *skb,
+ int (*output)(struct sock *, struct sk_buff *))
{
struct sk_buff *frag;
struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
- struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
+ struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
+ inet6_sk(skb->sk) : NULL;
struct ipv6hdr *tmp_hdr;
struct frag_hdr *fh;
unsigned int mtu, hlen, left, len;
ip6_copy_metadata(frag, skb);
}
- err = output(skb);
+ err = output(sk, skb);
if (!err)
IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
IPSTATS_MIB_FRAGCREATES);
/*
* Put this fragment into the sending queue.
*/
- err = output(frag);
+ err = output(sk, frag);
if (err)
goto fail;
ipv6h->nexthdr = proto;
ipv6h->saddr = fl6->saddr;
ipv6h->daddr = fl6->daddr;
- ip6tunnel_xmit(skb, dev);
+ ip6tunnel_xmit(NULL, skb, dev);
if (ndst)
ip6_tnl_dst_store(t, ndst);
return 0;
else
dev->flags &= ~IFF_POINTOPOINT;
- dev->iflink = p->link;
-
if (p->flags & IP6_TNL_F_CAP_XMIT) {
int strict = (ipv6_addr_type(&p->raddr) &
(IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
return 0;
}
+int ip6_tnl_get_iflink(const struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+
+ return t->parms.link;
+}
+EXPORT_SYMBOL(ip6_tnl_get_iflink);
static const struct net_device_ops ip6_tnl_netdev_ops = {
.ndo_init = ip6_tnl_dev_init,
.ndo_do_ioctl = ip6_tnl_ioctl,
.ndo_change_mtu = ip6_tnl_change_mtu,
.ndo_get_stats = ip6_get_stats,
+ .ndo_get_iflink = ip6_tnl_get_iflink,
};
}
EXPORT_SYMBOL_GPL(udp_sock_create6);
-int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb,
+int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
+ struct sk_buff *skb,
struct net_device *dev, struct in6_addr *saddr,
struct in6_addr *daddr,
__u8 prio, __u8 ttl, __be16 src_port,
ip6h->daddr = *daddr;
ip6h->saddr = *saddr;
- ip6tunnel_xmit(skb, dev);
+ ip6tunnel_xmit(sk, skb, dev);
return 0;
}
EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb);
dev->flags |= IFF_POINTOPOINT;
else
dev->flags &= ~IFF_POINTOPOINT;
-
- dev->iflink = p->link;
}
/**
.ndo_do_ioctl = vti6_ioctl,
.ndo_change_mtu = vti6_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64,
+ .ndo_get_iflink = ip6_tnl_get_iflink,
};
/**
return 0;
err2:
- kfree(mrt);
+ ip6mr_free_table(mrt);
err1:
fib_rules_unregister(ops);
return err;
list_del(&mrt->list);
ip6mr_free_table(mrt);
}
- rtnl_unlock();
fib_rules_unregister(net->ipv6.mr6_rules_ops);
+ rtnl_unlock();
}
#else
#define ip6mr_for_each_table(mrt, net) \
static void ip6mr_free_table(struct mr6_table *mrt)
{
- del_timer(&mrt->ipmr_expire_timer);
+ del_timer_sync(&mrt->ipmr_expire_timer);
mroute_clean_tables(mrt);
kfree(mrt);
}
return NETDEV_TX_OK;
}
+static int reg_vif_get_iflink(const struct net_device *dev)
+{
+ return 0;
+}
+
static const struct net_device_ops reg_vif_netdev_ops = {
.ndo_start_xmit = reg_vif_xmit,
+ .ndo_get_iflink = reg_vif_get_iflink,
};
static void reg_vif_setup(struct net_device *dev)
free_netdev(dev);
return NULL;
}
- dev->iflink = 0;
if (dev_open(dev))
goto failure;
v->pkt_out = 0;
v->link = dev->ifindex;
if (v->flags & MIFF_REGISTER)
- v->link = dev->iflink;
+ v->link = dev_get_iflink(dev);
/* And finish update writing critical data */
write_lock_bh(&mrt_lock);
}
#endif
-static inline int ip6mr_forward2_finish(struct sk_buff *skb)
+static inline int ip6mr_forward2_finish(struct sock *sk, struct sk_buff *skb)
{
IP6_INC_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_OUTFORWDATAGRAMS);
IP6_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_OUTOCTETS, skb->len);
- return dst_output(skb);
+ return dst_output_sk(sk, skb);
}
/*
IP6CB(skb)->flags |= IP6SKB_FORWARDED;
- return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, skb, skb->dev, dev,
+ return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, NULL, skb,
+ skb->dev, dev,
ip6mr_forward2_finish);
out_free:
payload_len = skb->len;
- err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
- dst_output);
+ err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
+ net->ipv6.igmp_sk, skb, NULL, skb->dev,
+ dst_output_sk);
out:
if (!err) {
ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT);
}
skb_dst_set(skb, dst);
- err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
- dst_output);
+ err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+ NULL, skb->dev, dst_output_sk);
out:
if (!err) {
ICMP6MSGOUT_INC_STATS(net, idev, type);
idev = __in6_dev_get(dst->dev);
IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
- err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
- dst_output);
+ err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+ NULL, dst->dev,
+ dst_output_sk);
if (!err) {
ICMP6MSGOUT_INC_STATS(net, idev, type);
ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
if (rt)
rt6_set_expires(rt, jiffies + (HZ * lifetime));
if (ra_msg->icmph.icmp6_hop_limit) {
- in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
+ /* Only set hop_limit on the interface if it is higher than
+ * the current hop_limit.
+ */
+ if (in6_dev->cnf.hop_limit < ra_msg->icmph.icmp6_hop_limit) {
+ in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
+ } else {
+ ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than current\n");
+ }
if (rt)
dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
ra_msg->icmph.icmp6_hop_limit);
{
struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry);
- if (entry->hook == NF_INET_LOCAL_OUT) {
+ if (entry->state.hook == NF_INET_LOCAL_OUT) {
const struct ipv6hdr *iph = ipv6_hdr(skb);
rt_info->daddr = iph->daddr;
{
struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry);
- if (entry->hook == NF_INET_LOCAL_OUT) {
+ if (entry->state.hook == NF_INET_LOCAL_OUT) {
const struct ipv6hdr *iph = ipv6_hdr(skb);
if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
!ipv6_addr_equal(&iph->saddr, &rt_info->saddr) ||
unsigned int
ip6t_do_table(struct sk_buff *skb,
unsigned int hook,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct xt_table *table)
{
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
unsigned int addend;
/* Initialization */
- indev = in ? in->name : nulldevname;
- outdev = out ? out->name : nulldevname;
+ indev = state->in ? state->in->name : nulldevname;
+ outdev = state->out ? state->out->name : nulldevname;
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
* normally, except that they will NEVER match rules that ask
* rule is also a fragment-specific rule, non-fragments won't
* match it. */
acpar.hotdrop = false;
- acpar.in = in;
- acpar.out = out;
+ acpar.in = state->in;
+ acpar.out = state->out;
acpar.family = NFPROTO_IPV6;
acpar.hooknum = hook;
#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
/* The packet is traced: log it */
if (unlikely(skb->nf_trace))
- trace_packet(skb, hook, in, out,
+ trace_packet(skb, hook, state->in, state->out,
table->name, private, e);
#endif
/* Standard target? */
static unsigned int ipv6_synproxy_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *nhs)
{
- struct synproxy_net *snet = synproxy_pernet(dev_net(in ? : out));
+ struct synproxy_net *snet = synproxy_pernet(dev_net(nhs->in ? : nhs->out));
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
struct nf_conn_synproxy *synproxy;
/* The work comes in here from netfilter.c. */
static unsigned int
ip6table_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- const struct net *net = dev_net((in != NULL) ? in : out);
+ const struct net *net = dev_net(state->in ? state->in : state->out);
- return ip6t_do_table(skb, ops->hooknum, in, out,
- net->ipv6.ip6table_filter);
+ return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_filter);
}
static struct nf_hook_ops *filter_ops __read_mostly;
};
static unsigned int
-ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out)
+ip6t_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
{
unsigned int ret;
struct in6_addr saddr, daddr;
/* flowlabel and prio (includes version, which shouldn't change either */
flowlabel = *((u_int32_t *)ipv6_hdr(skb));
- ret = ip6t_do_table(skb, NF_INET_LOCAL_OUT, NULL, out,
- dev_net(out)->ipv6.ip6table_mangle);
+ ret = ip6t_do_table(skb, NF_INET_LOCAL_OUT, state,
+ dev_net(state->out)->ipv6.ip6table_mangle);
if (ret != NF_DROP && ret != NF_STOLEN &&
(!ipv6_addr_equal(&ipv6_hdr(skb)->saddr, &saddr) ||
/* The work comes in here from netfilter.c. */
static unsigned int
ip6table_mangle_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
if (ops->hooknum == NF_INET_LOCAL_OUT)
- return ip6t_mangle_out(skb, out);
+ return ip6t_mangle_out(skb, state);
if (ops->hooknum == NF_INET_POST_ROUTING)
- return ip6t_do_table(skb, ops->hooknum, in, out,
- dev_net(out)->ipv6.ip6table_mangle);
+ return ip6t_do_table(skb, ops->hooknum, state,
+ dev_net(state->out)->ipv6.ip6table_mangle);
/* INPUT/FORWARD */
- return ip6t_do_table(skb, ops->hooknum, in, out,
- dev_net(in)->ipv6.ip6table_mangle);
+ return ip6t_do_table(skb, ops->hooknum, state,
+ dev_net(state->in)->ipv6.ip6table_mangle);
}
static struct nf_hook_ops *mangle_ops __read_mostly;
static unsigned int ip6table_nat_do_chain(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
- return ip6t_do_table(skb, ops->hooknum, in, out, net->ipv6.ip6table_nat);
+ return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_nat);
}
static unsigned int ip6table_nat_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv6_fn(ops, skb, in, out, ip6table_nat_do_chain);
+ return nf_nat_ipv6_fn(ops, skb, state, ip6table_nat_do_chain);
}
static unsigned int ip6table_nat_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv6_in(ops, skb, in, out, ip6table_nat_do_chain);
+ return nf_nat_ipv6_in(ops, skb, state, ip6table_nat_do_chain);
}
static unsigned int ip6table_nat_out(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv6_out(ops, skb, in, out, ip6table_nat_do_chain);
+ return nf_nat_ipv6_out(ops, skb, state, ip6table_nat_do_chain);
}
static unsigned int ip6table_nat_local_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv6_local_fn(ops, skb, in, out, ip6table_nat_do_chain);
+ return nf_nat_ipv6_local_fn(ops, skb, state, ip6table_nat_do_chain);
}
static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
/* The work comes in here from netfilter.c. */
static unsigned int
ip6table_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- const struct net *net = dev_net((in != NULL) ? in : out);
+ const struct net *net = dev_net(state->in ? state->in : state->out);
- return ip6t_do_table(skb, ops->hooknum, in, out,
- net->ipv6.ip6table_raw);
+ return ip6t_do_table(skb, ops->hooknum, state, net->ipv6.ip6table_raw);
}
static struct nf_hook_ops *rawtable_ops __read_mostly;
static unsigned int
ip6table_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- const struct net *net = dev_net((in != NULL) ? in : out);
+ const struct net *net = dev_net(state->in ? state->in : state->out);
- return ip6t_do_table(skb, ops->hooknum, in, out,
+ return ip6t_do_table(skb, ops->hooknum, state,
net->ipv6.ip6table_security);
}
static unsigned int ipv6_helper(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct nf_conn *ct;
const struct nf_conn_help *help;
static unsigned int ipv6_confirm(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
static unsigned int ipv6_conntrack_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_conntrack_in(dev_net(in), PF_INET6, ops->hooknum, skb);
+ return nf_conntrack_in(dev_net(state->in), PF_INET6, ops->hooknum, skb);
}
static unsigned int ipv6_conntrack_local(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
/* root is playing with raw sockets. */
if (skb->len < sizeof(struct ipv6hdr)) {
net_notice_ratelimited("ipv6_conntrack_local: packet too short\n");
return NF_ACCEPT;
}
- return nf_conntrack_in(dev_net(out), PF_INET6, ops->hooknum, skb);
+ return nf_conntrack_in(dev_net(state->out), PF_INET6, ops->hooknum, skb);
}
static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
static unsigned int ipv6_defrag(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct sk_buff *reasm;
nf_ct_frag6_consume_orig(reasm);
- NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, reasm,
- (struct net_device *) in, (struct net_device *) out,
- okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
+ NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, state->sk, reasm,
+ state->in, state->out,
+ state->okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
return NF_STOLEN;
}
unsigned int
nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct))
{
struct nf_conn *ct;
if (!nf_nat_initialized(ct, maniptype)) {
unsigned int ret;
- ret = do_chain(ops, skb, in, out, ct);
+ ret = do_chain(ops, skb, state, ct);
if (ret != NF_ACCEPT)
return ret;
pr_debug("Already setup manip %s for ct %p\n",
maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
ct);
- if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
+ if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, state->out))
goto oif_changed;
}
break;
/* ESTABLISHED */
NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
ctinfo == IP_CT_ESTABLISHED_REPLY);
- if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
+ if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, state->out))
goto oif_changed;
}
unsigned int
nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct))
{
unsigned int ret;
struct in6_addr daddr = ipv6_hdr(skb)->daddr;
- ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain);
+ ret = nf_nat_ipv6_fn(ops, skb, state, do_chain);
if (ret != NF_DROP && ret != NF_STOLEN &&
ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
skb_dst_drop(skb);
unsigned int
nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct))
{
#ifdef CONFIG_XFRM
if (skb->len < sizeof(struct ipv6hdr))
return NF_ACCEPT;
- ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain);
+ ret = nf_nat_ipv6_fn(ops, skb, state, do_chain);
#ifdef CONFIG_XFRM
if (ret != NF_DROP && ret != NF_STOLEN &&
!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
unsigned int
nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
+ const struct nf_hook_state *state,
unsigned int (*do_chain)(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct))
{
const struct nf_conn *ct;
if (skb->len < sizeof(struct ipv6hdr))
return NF_ACCEPT;
- ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain);
+ ret = nf_nat_ipv6_fn(ops, skb, state, do_chain);
if (ret != NF_DROP && ret != NF_STOLEN &&
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
#include <net/ip6_checksum.h>
#include <net/netfilter/ipv6/nf_reject.h>
#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter_bridge.h>
#include <net/netfilter/ipv6/nf_reject.h>
const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb,
*/
if (oldskb->nf_bridge) {
struct ethhdr *oeth = eth_hdr(oldskb);
- nskb->dev = oldskb->nf_bridge->physindev;
+
+ nskb->dev = nf_bridge_get_physindev(oldskb);
nskb->protocol = htons(ETH_P_IPV6);
ip6h->payload_len = htons(sizeof(struct tcphdr));
if (dev_hard_header(nskb, nskb->dev, ntohs(nskb->protocol),
static unsigned int nft_do_chain_ipv6(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct nft_pktinfo pkt;
/* malformed packet, drop it */
- if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0)
+ if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state) < 0)
return NF_DROP;
return nft_do_chain(&pkt, ops);
static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
if (unlikely(skb->len < sizeof(struct ipv6hdr))) {
if (net_ratelimit())
return NF_ACCEPT;
}
- return nft_do_chain_ipv6(ops, skb, in, out, okfn);
+ return nft_do_chain_ipv6(ops, skb, state);
}
struct nft_af_info nft_af_ipv6 __read_mostly = {
static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
+ const struct nf_hook_state *state,
struct nf_conn *ct)
{
struct nft_pktinfo pkt;
- nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
+ nft_set_pktinfo_ipv6(&pkt, ops, skb, state);
return nft_do_chain(&pkt, ops);
}
static unsigned int nft_nat_ipv6_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv6_fn(ops, skb, in, out, nft_nat_do_chain);
+ return nf_nat_ipv6_fn(ops, skb, state, nft_nat_do_chain);
}
static unsigned int nft_nat_ipv6_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv6_in(ops, skb, in, out, nft_nat_do_chain);
+ return nf_nat_ipv6_in(ops, skb, state, nft_nat_do_chain);
}
static unsigned int nft_nat_ipv6_out(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv6_out(ops, skb, in, out, nft_nat_do_chain);
+ return nf_nat_ipv6_out(ops, skb, state, nft_nat_do_chain);
}
static unsigned int nft_nat_ipv6_local_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return nf_nat_ipv6_local_fn(ops, skb, in, out, nft_nat_do_chain);
+ return nf_nat_ipv6_local_fn(ops, skb, state, nft_nat_do_chain);
}
static const struct nf_chain_type nft_chain_nat_ipv6 = {
static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
unsigned int ret;
struct nft_pktinfo pkt;
u32 mark, flowlabel;
/* malformed packet, drop it */
- if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0)
+ if (nft_set_pktinfo_ipv6(&pkt, ops, skb, state) < 0)
return NF_DROP;
/* save source/dest address, mark, hoplimit, flowlabel, priority */
case NFT_REJECT_TCP_RST:
nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
break;
+ default:
+ break;
}
data[NFT_REG_VERDICT].verdict = NF_DROP;
EXPORT_SYMBOL(ip6_dst_hoplimit);
#endif
-int __ip6_local_out(struct sk_buff *skb)
+static int __ip6_local_out_sk(struct sock *sk, struct sk_buff *skb)
{
int len;
ipv6_hdr(skb)->payload_len = htons(len);
IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
- return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
- skb_dst(skb)->dev, dst_output);
+ return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+ NULL, skb_dst(skb)->dev, dst_output_sk);
+}
+
+int __ip6_local_out(struct sk_buff *skb)
+{
+ return __ip6_local_out_sk(skb->sk, skb);
}
EXPORT_SYMBOL_GPL(__ip6_local_out);
-int ip6_local_out(struct sk_buff *skb)
+int ip6_local_out_sk(struct sock *sk, struct sk_buff *skb)
{
int err;
- err = __ip6_local_out(skb);
+ err = __ip6_local_out_sk(sk, skb);
if (likely(err == 1))
- err = dst_output(skb);
+ err = dst_output_sk(sk, skb);
return err;
}
+EXPORT_SYMBOL_GPL(ip6_local_out_sk);
+
+int ip6_local_out(struct sk_buff *skb)
+{
+ return ip6_local_out_sk(skb->sk, skb);
+}
EXPORT_SYMBOL_GPL(ip6_local_out);
goto error_fault;
IP6_UPD_PO_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len);
- err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL,
- rt->dst.dev, dst_output);
+ err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, sk, skb,
+ NULL, rt->dst.dev, dst_output_sk);
if (err > 0)
err = net_xmit_errno(err);
if (err)
skb_set_inner_ipproto(skb, IPPROTO_IPV6);
- err = iptunnel_xmit(skb->sk, rt, skb, fl4.saddr, fl4.daddr,
+ err = iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr,
protocol, tos, ttl, df,
!net_eq(tunnel->net, dev_net(dev)));
iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
if (dev->mtu < IPV6_MIN_MTU)
dev->mtu = IPV6_MIN_MTU;
}
- dev->iflink = tunnel->parms.link;
}
static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
.ndo_do_ioctl = ipip6_tunnel_ioctl,
.ndo_change_mtu = ipip6_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64,
+ .ndo_get_iflink = ip_tunnel_get_iflink,
};
static void ipip6_dev_free(struct net_device *dev)
dev->mtu = ETH_DATA_LEN - t_hlen;
dev->flags = IFF_NOARP;
netif_keep_dst(dev);
- dev->iflink = 0;
dev->addr_len = 4;
dev->features |= NETIF_F_LLTX;
dev->features |= SIT_FEATURES;
TCP_SKB_CB(skb)->sacked = 0;
}
+static void tcp_v6_restore_cb(struct sk_buff *skb)
+{
+ /* We need to move header back to the beginning if xfrm6_policy_check()
+ * and tcp_v6_fill_cb() are going to be called again.
+ */
+ memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
+ sizeof(struct inet6_skb_parm));
+}
+
static int tcp_v6_rcv(struct sk_buff *skb)
{
const struct tcphdr *th;
inet_twsk_deschedule(tw, &tcp_death_row);
inet_twsk_put(tw);
sk = sk2;
+ tcp_v6_restore_cb(skb);
goto process;
}
/* Fall through to ACK */
tcp_v6_timewait_ack(sk, skb);
break;
case TCP_TW_RST:
+ tcp_v6_restore_cb(skb);
goto no_tcp_socket;
case TCP_TW_SUCCESS:
;
skb->sk = sk;
skb->destructor = sock_edemux;
if (sk_fullsock(sk)) {
- struct dst_entry *dst = sk->sk_rx_dst;
+ struct dst_entry *dst = READ_ONCE(sk->sk_rx_dst);
if (dst)
dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
return hash ^ port;
}
-
int udp_v6_get_port(struct sock *sk, unsigned short snum)
{
unsigned int hash2_nulladdr =
}
EXPORT_SYMBOL_GPL(udp6_lib_lookup);
-
/*
* This should be easy, if there is something there we
* return it, otherwise we block.
.flags = INET_PROTOSW_PERMANENT,
};
-
int __init udpv6_init(void)
{
int ret;
ipv6_hdr(skb)->payload_len = htons(skb->len);
__skb_push(skb, skb->data - skb_network_header(skb));
- NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, skb->dev, NULL,
+ NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NULL, skb,
+ skb->dev, NULL,
ip6_rcv_finish);
return -1;
}
}
EXPORT_SYMBOL(xfrm6_prepare_output);
-int xfrm6_output_finish(struct sk_buff *skb)
+int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb)
{
memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
#endif
- return xfrm_output(skb);
+ return xfrm_output(sk, skb);
}
-static int __xfrm6_output(struct sk_buff *skb)
+static int __xfrm6_output(struct sock *sk, struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct xfrm_state *x = dst->xfrm;
#ifdef CONFIG_NETFILTER
if (!x) {
IP6CB(skb)->flags |= IP6SKB_REROUTED;
- return dst_output(skb);
+ return dst_output_sk(sk, skb);
}
#endif
if (x->props.mode == XFRM_MODE_TUNNEL &&
((skb->len > mtu && !skb_is_gso(skb)) ||
dst_allfrag(skb_dst(skb)))) {
- return ip6_fragment(skb, x->outer_mode->afinfo->output_finish);
+ return ip6_fragment(sk, skb,
+ x->outer_mode->afinfo->output_finish);
}
- return x->outer_mode->afinfo->output_finish(skb);
+ return x->outer_mode->afinfo->output_finish(sk, skb);
}
int xfrm6_output(struct sock *sk, struct sk_buff *skb)
{
- return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, skb,
+ return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, sk, skb,
NULL, skb_dst(skb)->dev, __xfrm6_output,
!(IP6CB(skb)->flags & IP6SKB_REROUTED));
}
noblock, &err);
else
skb = sock_alloc_send_skb(sk, len, noblock, &err);
- if (!skb) {
- err = -ENOMEM;
+ if (!skb)
goto out;
- }
if (iucv->transport == AF_IUCV_TRANS_HIPER)
skb_reserve(skb, sizeof(struct af_iucv_trans_hdr) + ETH_HLEN);
if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
l2tp_wq = alloc_workqueue("l2tp", WQ_UNBOUND, 0);
if (!l2tp_wq) {
pr_err("alloc_workqueue failed\n");
+ unregister_pernet_device(&l2tp_net_ops);
rc = -ENOMEM;
goto out;
}
container_of(h, struct tid_ampdu_rx, rcu_head);
int i;
- del_timer_sync(&tid_rx->reorder_timer);
-
for (i = 0; i < tid_rx->buf_size; i++)
__skb_queue_purge(&tid_rx->reorder_buf[i]);
kfree(tid_rx->reorder_buf);
del_timer_sync(&tid_rx->session_timer);
+ /* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
+ spin_lock_bh(&tid_rx->reorder_lock);
+ tid_rx->removed = true;
+ spin_unlock_bh(&tid_rx->reorder_lock);
+ del_timer_sync(&tid_rx->reorder_timer);
+
call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
}
set_release_timer:
- mod_timer(&tid_agg_rx->reorder_timer,
- tid_agg_rx->reorder_time[j] + 1 +
- HT_RX_REORDER_BUF_TIMEOUT);
+ if (!tid_agg_rx->removed)
+ mod_timer(&tid_agg_rx->reorder_timer,
+ tid_agg_rx->reorder_time[j] + 1 +
+ HT_RX_REORDER_BUF_TIMEOUT);
} else {
del_timer(&tid_agg_rx->reorder_timer);
}
* @reorder_lock: serializes access to reorder buffer, see below.
* @auto_seq: used for offloaded BA sessions to automatically pick head_seq_and
* and ssn.
+ * @removed: this session is removed (but might have been found due to RCU)
*
* This structure's lifetime is managed by RCU, assignments to
* the array holding it must hold the aggregation mutex.
u16 timeout;
u8 dialog_token;
bool auto_seq;
+ bool removed;
};
/**
# nf_tables
nf_tables-objs += nf_tables_core.o nf_tables_api.o
-nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o
+nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nft_dynset.o
nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o
obj-$(CONFIG_NF_TABLES) += nf_tables.o
unsigned int nf_iterate(struct list_head *head,
struct sk_buff *skb,
- unsigned int hook,
- const struct net_device *indev,
- const struct net_device *outdev,
- struct nf_hook_ops **elemp,
- int (*okfn)(struct sk_buff *),
- int hook_thresh)
+ struct nf_hook_state *state,
+ struct nf_hook_ops **elemp)
{
unsigned int verdict;
* function because of risk of continuing from deleted element.
*/
list_for_each_entry_continue_rcu((*elemp), head, list) {
- if (hook_thresh > (*elemp)->priority)
+ if (state->thresh > (*elemp)->priority)
continue;
/* Optimization: we don't need to hold module
reference here, since function can't sleep. --RR */
repeat:
- verdict = (*elemp)->hook(*elemp, skb, indev, outdev, okfn);
+ verdict = (*elemp)->hook(*elemp, skb, state);
if (verdict != NF_ACCEPT) {
#ifdef CONFIG_NETFILTER_DEBUG
if (unlikely((verdict & NF_VERDICT_MASK)
> NF_MAX_VERDICT)) {
NFDEBUG("Evil return from %p(%u).\n",
- (*elemp)->hook, hook);
+ (*elemp)->hook, state->hook);
continue;
}
#endif
/* Returns 1 if okfn() needs to be executed by the caller,
* -EPERM for NF_DROP, 0 otherwise. */
-int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
- struct net_device *indev,
- struct net_device *outdev,
- int (*okfn)(struct sk_buff *),
- int hook_thresh)
+int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state)
{
struct nf_hook_ops *elem;
unsigned int verdict;
/* We may already have this, but read-locks nest anyway */
rcu_read_lock();
- elem = list_entry_rcu(&nf_hooks[pf][hook], struct nf_hook_ops, list);
+ elem = list_entry_rcu(&nf_hooks[state->pf][state->hook],
+ struct nf_hook_ops, list);
next_hook:
- verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,
- outdev, &elem, okfn, hook_thresh);
+ verdict = nf_iterate(&nf_hooks[state->pf][state->hook], skb, state,
+ &elem);
if (verdict == NF_ACCEPT || verdict == NF_STOP) {
ret = 1;
} else if ((verdict & NF_VERDICT_MASK) == NF_DROP) {
if (ret == 0)
ret = -EPERM;
} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
- int err = nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
- verdict >> NF_VERDICT_QBITS);
+ int err = nf_queue(skb, elem, state,
+ verdict >> NF_VERDICT_QBITS);
if (err < 0) {
if (err == -ECANCELED)
goto next_hook;
#include <net/netlink.h>
#include <linux/netfilter.h>
+#include <linux/netfilter_bridge.h>
#include <linux/netfilter/ipset/pfxlen.h>
#include <linux/netfilter/ipset/ip_set.h>
#include <linux/netfilter/ipset/ip_set_hash.h>
#define HKEY_DATALEN sizeof(struct hash_netiface4_elem_hashed)
#include "ip_set_hash_gen.h"
+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
+static const char *get_physindev_name(const struct sk_buff *skb)
+{
+ struct net_device *dev = nf_bridge_get_physindev(skb);
+
+ return dev ? dev->name : NULL;
+}
+
+static const char *get_phyoutdev_name(const struct sk_buff *skb)
+{
+ struct net_device *dev = nf_bridge_get_physoutdev(skb);
+
+ return dev ? dev->name : NULL;
+}
+#endif
+
static int
hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
e.ip &= ip_set_netmask(e.cidr);
#define IFACE(dir) (par->dir ? par->dir->name : NULL)
-#define PHYSDEV(dir) (nf_bridge->dir ? nf_bridge->dir->name : NULL)
#define SRCDIR (opt->flags & IPSET_DIM_TWO_SRC)
if (opt->cmdflags & IPSET_FLAG_PHYSDEV) {
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
- const struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+ e.iface = SRCDIR ? get_physindev_name(skb) :
+ get_phyoutdev_name(skb);
- if (!nf_bridge)
+ if (!e.iface)
return -EINVAL;
- e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
e.physdev = 1;
#else
e.iface = NULL;
if (opt->cmdflags & IPSET_FLAG_PHYSDEV) {
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
- const struct nf_bridge_info *nf_bridge = skb->nf_bridge;
-
- if (!nf_bridge)
+ e.iface = SRCDIR ? get_physindev_name(skb) :
+ get_phyoutdev_name(skb);
+ if (!e.iface)
return -EINVAL;
- e.iface = SRCDIR ? PHYSDEV(physindev) : PHYSDEV(physoutdev);
+
e.physdev = 1;
#else
e.iface = NULL;
*/
static unsigned int
ip_vs_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
return ip_vs_out(ops->hooknum, skb, AF_INET);
}
*/
static unsigned int
ip_vs_local_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
return ip_vs_out(ops->hooknum, skb, AF_INET);
}
*/
static unsigned int
ip_vs_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
return ip_vs_out(ops->hooknum, skb, AF_INET6);
}
*/
static unsigned int
ip_vs_local_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
return ip_vs_out(ops->hooknum, skb, AF_INET6);
}
*/
static unsigned int
ip_vs_remote_request4(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
return ip_vs_in(ops->hooknum, skb, AF_INET);
}
*/
static unsigned int
ip_vs_local_request4(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
return ip_vs_in(ops->hooknum, skb, AF_INET);
}
*/
static unsigned int
ip_vs_remote_request6(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
return ip_vs_in(ops->hooknum, skb, AF_INET6);
}
*/
static unsigned int
ip_vs_local_request6(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
return ip_vs_in(ops->hooknum, skb, AF_INET6);
}
*/
static unsigned int
ip_vs_forward_icmp(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
int r;
struct net *net;
#ifdef CONFIG_IP_VS_IPV6
static unsigned int
ip_vs_forward_icmp_v6(const struct nf_hook_ops *ops, struct sk_buff *skb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
int r;
struct net *net;
ip_vs_update_conntrack(skb, cp, 1);
if (!local) {
skb_forward_csum(skb);
- NF_HOOK(pf, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev,
- dst_output);
+ NF_HOOK(pf, NF_INET_LOCAL_OUT, NULL, skb,
+ NULL, skb_dst(skb)->dev, dst_output_sk);
} else
ret = NF_ACCEPT;
return ret;
ip_vs_notrack(skb);
if (!local) {
skb_forward_csum(skb);
- NF_HOOK(pf, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev,
- dst_output);
+ NF_HOOK(pf, NF_INET_LOCAL_OUT, NULL, skb,
+ NULL, skb_dst(skb)->dev, dst_output_sk);
} else
ret = NF_ACCEPT;
return ret;
/* core.c */
unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb,
- unsigned int hook, const struct net_device *indev,
- const struct net_device *outdev,
- struct nf_hook_ops **elemp,
- int (*okfn)(struct sk_buff *), int hook_thresh);
+ struct nf_hook_state *state, struct nf_hook_ops **elemp);
/* nf_queue.c */
-int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem, u_int8_t pf,
- unsigned int hook, struct net_device *indev,
- struct net_device *outdev, int (*okfn)(struct sk_buff *),
- unsigned int queuenum);
+int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem,
+ struct nf_hook_state *state, unsigned int queuenum);
int __init netfilter_queue_init(void);
/* nf_log.c */
#include <net/route.h>
#include <linux/netfilter.h>
+#include <linux/netfilter_bridge.h>
#include <linux/netfilter/xt_LOG.h>
#include <net/netfilter/nf_log.h>
const struct net_device *physindev;
const struct net_device *physoutdev;
- physindev = skb->nf_bridge->physindev;
+ physindev = nf_bridge_get_physindev(skb);
if (physindev && in != physindev)
nf_log_buf_add(m, "PHYSIN=%s ", physindev->name);
- physoutdev = skb->nf_bridge->physoutdev;
+ physoutdev = nf_bridge_get_physoutdev(skb);
if (physoutdev && out != physoutdev)
nf_log_buf_add(m, "PHYSOUT=%s ", physoutdev->name);
}
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
+#include <linux/netfilter_bridge.h>
#include <linux/seq_file.h>
#include <linux/rcupdate.h>
#include <net/protocol.h>
void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
{
+ struct nf_hook_state *state = &entry->state;
+
/* Release those devices we held, or Alexey will kill me. */
- if (entry->indev)
- dev_put(entry->indev);
- if (entry->outdev)
- dev_put(entry->outdev);
+ if (state->in)
+ dev_put(state->in);
+ if (state->out)
+ dev_put(state->out);
+ if (state->sk)
+ sock_put(state->sk);
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
if (entry->skb->nf_bridge) {
- struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge;
+ struct net_device *physdev;
- if (nf_bridge->physindev)
- dev_put(nf_bridge->physindev);
- if (nf_bridge->physoutdev)
- dev_put(nf_bridge->physoutdev);
+ physdev = nf_bridge_get_physindev(entry->skb);
+ if (physdev)
+ dev_put(physdev);
+ physdev = nf_bridge_get_physoutdev(entry->skb);
+ if (physdev)
+ dev_put(physdev);
}
#endif
/* Drop reference to owner of hook which queued us. */
/* Bump dev refs so they don't vanish while packet is out */
bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
{
+ struct nf_hook_state *state = &entry->state;
+
if (!try_module_get(entry->elem->owner))
return false;
- if (entry->indev)
- dev_hold(entry->indev);
- if (entry->outdev)
- dev_hold(entry->outdev);
+ if (state->in)
+ dev_hold(state->in);
+ if (state->out)
+ dev_hold(state->out);
+ if (state->sk)
+ sock_hold(state->sk);
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
if (entry->skb->nf_bridge) {
- struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge;
struct net_device *physdev;
- physdev = nf_bridge->physindev;
+ physdev = nf_bridge_get_physindev(entry->skb);
if (physdev)
dev_hold(physdev);
- physdev = nf_bridge->physoutdev;
+ physdev = nf_bridge_get_physoutdev(entry->skb);
if (physdev)
dev_hold(physdev);
}
* through nf_reinject().
*/
int nf_queue(struct sk_buff *skb,
- struct nf_hook_ops *elem,
- u_int8_t pf, unsigned int hook,
- struct net_device *indev,
- struct net_device *outdev,
- int (*okfn)(struct sk_buff *),
- unsigned int queuenum)
+ struct nf_hook_ops *elem,
+ struct nf_hook_state *state,
+ unsigned int queuenum)
{
int status = -ENOENT;
struct nf_queue_entry *entry = NULL;
goto err_unlock;
}
- afinfo = nf_get_afinfo(pf);
+ afinfo = nf_get_afinfo(state->pf);
if (!afinfo)
goto err_unlock;
*entry = (struct nf_queue_entry) {
.skb = skb,
.elem = elem,
- .pf = pf,
- .hook = hook,
- .indev = indev,
- .outdev = outdev,
- .okfn = okfn,
+ .state = *state,
.size = sizeof(*entry) + afinfo->route_key_size,
};
}
if (verdict == NF_ACCEPT) {
- afinfo = nf_get_afinfo(entry->pf);
+ afinfo = nf_get_afinfo(entry->state.pf);
if (!afinfo || afinfo->reroute(skb, entry) < 0)
verdict = NF_DROP;
}
+ entry->state.thresh = INT_MIN;
+
if (verdict == NF_ACCEPT) {
next_hook:
- verdict = nf_iterate(&nf_hooks[entry->pf][entry->hook],
- skb, entry->hook,
- entry->indev, entry->outdev, &elem,
- entry->okfn, INT_MIN);
+ verdict = nf_iterate(&nf_hooks[entry->state.pf][entry->state.hook],
+ skb, &entry->state, &elem);
}
switch (verdict & NF_VERDICT_MASK) {
case NF_ACCEPT:
case NF_STOP:
local_bh_disable();
- entry->okfn(skb);
+ entry->state.okfn(entry->state.sk, skb);
local_bh_enable();
break;
case NF_QUEUE:
- err = nf_queue(skb, elem, entry->pf, entry->hook,
- entry->indev, entry->outdev, entry->okfn,
- verdict >> NF_VERDICT_QBITS);
+ err = nf_queue(skb, elem, &entry->state,
+ verdict >> NF_VERDICT_QBITS);
if (err < 0) {
if (err == -ECANCELED)
goto next_hook;
features = 0;
if (nla[NFTA_SET_FLAGS] != NULL) {
features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
- features &= NFT_SET_INTERVAL | NFT_SET_MAP;
+ features &= NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_TIMEOUT;
}
bops = NULL;
[NFTA_SET_POLICY] = { .type = NLA_U32 },
[NFTA_SET_DESC] = { .type = NLA_NESTED },
[NFTA_SET_ID] = { .type = NLA_U32 },
+ [NFTA_SET_TIMEOUT] = { .type = NLA_U64 },
+ [NFTA_SET_GC_INTERVAL] = { .type = NLA_U32 },
};
static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
goto nla_put_failure;
}
+ if (set->timeout &&
+ nla_put_be64(skb, NFTA_SET_TIMEOUT, cpu_to_be64(set->timeout)))
+ goto nla_put_failure;
+ if (set->gc_int &&
+ nla_put_be32(skb, NFTA_SET_GC_INTERVAL, htonl(set->gc_int)))
+ goto nla_put_failure;
+
if (set->policy != NFT_SET_POL_PERFORMANCE) {
if (nla_put_be32(skb, NFTA_SET_POLICY, htonl(set->policy)))
goto nla_put_failure;
char name[IFNAMSIZ];
unsigned int size;
bool create;
- u32 ktype, dtype, flags, policy;
+ u64 timeout;
+ u32 ktype, dtype, flags, policy, gc_int;
struct nft_set_desc desc;
int err;
if (nla[NFTA_SET_FLAGS] != NULL) {
flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
- NFT_SET_INTERVAL | NFT_SET_MAP))
+ NFT_SET_INTERVAL | NFT_SET_MAP |
+ NFT_SET_TIMEOUT))
return -EINVAL;
}
} else if (flags & NFT_SET_MAP)
return -EINVAL;
+ timeout = 0;
+ if (nla[NFTA_SET_TIMEOUT] != NULL) {
+ if (!(flags & NFT_SET_TIMEOUT))
+ return -EINVAL;
+ timeout = be64_to_cpu(nla_get_be64(nla[NFTA_SET_TIMEOUT]));
+ }
+ gc_int = 0;
+ if (nla[NFTA_SET_GC_INTERVAL] != NULL) {
+ if (!(flags & NFT_SET_TIMEOUT))
+ return -EINVAL;
+ gc_int = ntohl(nla_get_be32(nla[NFTA_SET_GC_INTERVAL]));
+ }
+
policy = NFT_SET_POL_PERFORMANCE;
if (nla[NFTA_SET_POLICY] != NULL)
policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
set->flags = flags;
set->size = desc.size;
set->policy = policy;
+ set->timeout = timeout;
+ set->gc_int = gc_int;
err = ops->init(set, &desc, nla);
if (err < 0)
if (!list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS)
return -EBUSY;
- if (set->flags & NFT_SET_MAP) {
+ if (binding->flags & NFT_SET_MAP) {
/* If the set is already bound to the same chain all
* jumps are already validated for that chain.
*/
list_for_each_entry(i, &set->bindings, list) {
- if (i->chain == binding->chain)
+ if (binding->flags & NFT_SET_MAP &&
+ i->chain == binding->chain)
goto bind;
}
.len = sizeof(u8),
.align = __alignof__(u8),
},
+ [NFT_SET_EXT_TIMEOUT] = {
+ .len = sizeof(u64),
+ .align = __alignof__(u64),
+ },
+ [NFT_SET_EXT_EXPIRATION] = {
+ .len = sizeof(unsigned long),
+ .align = __alignof__(unsigned long),
+ },
+ [NFT_SET_EXT_USERDATA] = {
+ .len = sizeof(struct nft_userdata),
+ .align = __alignof__(struct nft_userdata),
+ },
};
EXPORT_SYMBOL_GPL(nft_set_ext_types);
[NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED },
[NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED },
[NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 },
+ [NFTA_SET_ELEM_TIMEOUT] = { .type = NLA_U64 },
+ [NFTA_SET_ELEM_USERDATA] = { .type = NLA_BINARY,
+ .len = NFT_USERDATA_MAXLEN },
};
static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
htonl(*nft_set_ext_flags(ext))))
goto nla_put_failure;
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
+ nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
+ cpu_to_be64(*nft_set_ext_timeout(ext))))
+ goto nla_put_failure;
+
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
+ unsigned long expires, now = jiffies;
+
+ expires = *nft_set_ext_expiration(ext);
+ if (time_before(now, expires))
+ expires -= now;
+ else
+ expires = 0;
+
+ if (nla_put_be64(skb, NFTA_SET_ELEM_EXPIRATION,
+ cpu_to_be64(jiffies_to_msecs(expires))))
+ goto nla_put_failure;
+ }
+
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_USERDATA)) {
+ struct nft_userdata *udata;
+
+ udata = nft_set_ext_userdata(ext);
+ if (nla_put(skb, NFTA_SET_ELEM_USERDATA,
+ udata->len + 1, udata->data))
+ goto nla_put_failure;
+ }
+
nla_nest_end(skb, nest);
return 0;
return trans;
}
-static void *nft_set_elem_init(const struct nft_set *set,
- const struct nft_set_ext_tmpl *tmpl,
- const struct nft_data *key,
- const struct nft_data *data,
- gfp_t gfp)
+void *nft_set_elem_init(const struct nft_set *set,
+ const struct nft_set_ext_tmpl *tmpl,
+ const struct nft_data *key,
+ const struct nft_data *data,
+ u64 timeout, gfp_t gfp)
{
struct nft_set_ext *ext;
void *elem;
memcpy(nft_set_ext_key(ext), key, set->klen);
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
memcpy(nft_set_ext_data(ext), data, set->dlen);
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION))
+ *nft_set_ext_expiration(ext) =
+ jiffies + msecs_to_jiffies(timeout);
+ if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT))
+ *nft_set_ext_timeout(ext) = timeout;
return elem;
}
struct nft_set_ext *ext;
struct nft_set_elem elem;
struct nft_set_binding *binding;
+ struct nft_userdata *udata;
struct nft_data data;
enum nft_registers dreg;
struct nft_trans *trans;
+ u64 timeout;
u32 flags;
+ u8 ulen;
int err;
- if (set->size && set->nelems == set->size)
- return -ENFILE;
-
err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
nft_set_elem_policy);
if (err < 0)
return -EINVAL;
}
+ timeout = 0;
+ if (nla[NFTA_SET_ELEM_TIMEOUT] != NULL) {
+ if (!(set->flags & NFT_SET_TIMEOUT))
+ return -EINVAL;
+ timeout = be64_to_cpu(nla_get_be64(nla[NFTA_SET_ELEM_TIMEOUT]));
+ } else if (set->flags & NFT_SET_TIMEOUT) {
+ timeout = set->timeout;
+ }
+
err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]);
if (err < 0)
goto err1;
goto err2;
nft_set_ext_add(&tmpl, NFT_SET_EXT_KEY);
+ if (timeout > 0) {
+ nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION);
+ if (timeout != set->timeout)
+ nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
+ }
if (nla[NFTA_SET_ELEM_DATA] != NULL) {
err = nft_data_init(ctx, &data, &d2, nla[NFTA_SET_ELEM_DATA]);
.chain = (struct nft_chain *)binding->chain,
};
+ if (!(binding->flags & NFT_SET_MAP))
+ continue;
+
err = nft_validate_data_load(&bind_ctx, dreg,
&data, d2.type);
if (err < 0)
nft_set_ext_add(&tmpl, NFT_SET_EXT_DATA);
}
+ /* The full maximum length of userdata can exceed the maximum
+ * offset value (U8_MAX) for following extensions, therefor it
+ * must be the last extension added.
+ */
+ ulen = 0;
+ if (nla[NFTA_SET_ELEM_USERDATA] != NULL) {
+ ulen = nla_len(nla[NFTA_SET_ELEM_USERDATA]);
+ if (ulen > 0)
+ nft_set_ext_add_length(&tmpl, NFT_SET_EXT_USERDATA,
+ ulen);
+ }
+
err = -ENOMEM;
- elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data, GFP_KERNEL);
+ elem.priv = nft_set_elem_init(set, &tmpl, &elem.key, &data,
+ timeout, GFP_KERNEL);
if (elem.priv == NULL)
goto err3;
ext = nft_set_elem_ext(set, elem.priv);
if (flags)
*nft_set_ext_flags(ext) = flags;
+ if (ulen > 0) {
+ udata = nft_set_ext_userdata(ext);
+ udata->len = ulen - 1;
+ nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
+ }
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
if (trans == NULL)
goto err4;
- ext->genmask = nft_genmask_cur(ctx->net);
+ ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
err = set->ops->insert(set, &elem);
if (err < 0)
goto err5;
return -EBUSY;
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
+ if (set->size &&
+ !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact))
+ return -ENFILE;
+
err = nft_add_set_elem(&ctx, set, attr);
- if (err < 0)
+ if (err < 0) {
+ atomic_dec(&set->nelems);
break;
-
- set->nelems++;
+ }
}
return err;
}
if (err < 0)
break;
- set->nelems--;
+ set->ndeact++;
}
return err;
}
+void nft_set_gc_batch_release(struct rcu_head *rcu)
+{
+ struct nft_set_gc_batch *gcb;
+ unsigned int i;
+
+ gcb = container_of(rcu, struct nft_set_gc_batch, head.rcu);
+ for (i = 0; i < gcb->head.cnt; i++)
+ nft_set_elem_destroy(gcb->head.set, gcb->elems[i]);
+ kfree(gcb);
+}
+EXPORT_SYMBOL_GPL(nft_set_gc_batch_release);
+
+struct nft_set_gc_batch *nft_set_gc_batch_alloc(const struct nft_set *set,
+ gfp_t gfp)
+{
+ struct nft_set_gc_batch *gcb;
+
+ gcb = kzalloc(sizeof(*gcb), gfp);
+ if (gcb == NULL)
+ return gcb;
+ gcb->head.set = set;
+ return gcb;
+}
+EXPORT_SYMBOL_GPL(nft_set_gc_batch_alloc);
+
static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net,
u32 portid, u32 seq)
{
&te->elem,
NFT_MSG_DELSETELEM, 0);
te->set->ops->remove(te->set, &te->elem);
+ atomic_dec(&te->set->nelems);
+ te->set->ndeact--;
break;
}
}
nft_trans_destroy(trans);
break;
case NFT_MSG_NEWSETELEM:
- nft_trans_elem_set(trans)->nelems--;
te = (struct nft_trans_elem *)trans->data;
te->set->ops->remove(te->set, &te->elem);
+ atomic_dec(&te->set->nelems);
break;
case NFT_MSG_DELSETELEM:
te = (struct nft_trans_elem *)trans->data;
- nft_trans_elem_set(trans)->nelems++;
te->set->ops->activate(te->set, &te->elem);
+ te->set->ndeact--;
nft_trans_destroy(trans);
break;
continue;
list_for_each_entry(binding, &set->bindings, list) {
- if (binding->chain != chain)
+ if (!(binding->flags & NFT_SET_MAP) ||
+ binding->chain != chain)
continue;
iter.skip = 0;
if (err < 0)
goto err6;
+ err = nft_dynset_module_init();
+ if (err < 0)
+ goto err7;
+
return 0;
+err7:
+ nft_payload_module_exit();
err6:
nft_byteorder_module_exit();
err5:
void nf_tables_core_module_exit(void)
{
+ nft_dynset_module_exit();
nft_payload_module_exit();
nft_byteorder_module_exit();
nft_bitwise_module_exit();
#include <linux/ipv6.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
+#include <linux/netfilter_bridge.h>
#include <net/netlink.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_log.h>
htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
goto nla_put_failure;
} else {
+ struct net_device *physindev;
+
/* Case 2: indev is bridge group, we need to look for
* physical device (when called from ipv4) */
if (nla_put_be32(inst->skb, NFULA_IFINDEX_INDEV,
htonl(indev->ifindex)))
goto nla_put_failure;
- if (skb->nf_bridge && skb->nf_bridge->physindev &&
+
+ physindev = nf_bridge_get_physindev(skb);
+ if (physindev &&
nla_put_be32(inst->skb, NFULA_IFINDEX_PHYSINDEV,
- htonl(skb->nf_bridge->physindev->ifindex)))
+ htonl(physindev->ifindex)))
goto nla_put_failure;
}
#endif
htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
goto nla_put_failure;
} else {
+ struct net_device *physoutdev;
+
/* Case 2: indev is a bridge group, we need to look
* for physical device (when called from ipv4) */
if (nla_put_be32(inst->skb, NFULA_IFINDEX_OUTDEV,
htonl(outdev->ifindex)))
goto nla_put_failure;
- if (skb->nf_bridge && skb->nf_bridge->physoutdev &&
+
+ physoutdev = nf_bridge_get_physoutdev(skb);
+ if (physoutdev &&
nla_put_be32(inst->skb, NFULA_IFINDEX_PHYSOUTDEV,
- htonl(skb->nf_bridge->physoutdev->ifindex)))
+ htonl(physoutdev->ifindex)))
goto nla_put_failure;
}
#endif
#include <linux/proc_fs.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter_bridge.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_queue.h>
#include <linux/list.h>
if (entskb->tstamp.tv64)
size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp));
- if (entry->hook <= NF_INET_FORWARD ||
- (entry->hook == NF_INET_POST_ROUTING && entskb->sk == NULL))
+ if (entry->state.hook <= NF_INET_FORWARD ||
+ (entry->state.hook == NF_INET_POST_ROUTING && entskb->sk == NULL))
csum_verify = !skb_csum_unnecessary(entskb);
else
csum_verify = false;
- outdev = entry->outdev;
+ outdev = entry->state.out;
switch ((enum nfqnl_config_mode)ACCESS_ONCE(queue->copy_mode)) {
case NFQNL_COPY_META:
return NULL;
}
nfmsg = nlmsg_data(nlh);
- nfmsg->nfgen_family = entry->pf;
+ nfmsg->nfgen_family = entry->state.pf;
nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = htons(queue->queue_num);
nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg));
pmsg = nla_data(nla);
pmsg->hw_protocol = entskb->protocol;
- pmsg->hook = entry->hook;
+ pmsg->hook = entry->state.hook;
*packet_id_ptr = &pmsg->packet_id;
- indev = entry->indev;
+ indev = entry->state.in;
if (indev) {
#if !IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
if (nla_put_be32(skb, NFQA_IFINDEX_INDEV, htonl(indev->ifindex)))
goto nla_put_failure;
#else
- if (entry->pf == PF_BRIDGE) {
+ if (entry->state.pf == PF_BRIDGE) {
/* Case 1: indev is physical input device, we need to
* look for bridge group (when called from
* netfilter_bridge) */
htonl(br_port_get_rcu(indev)->br->dev->ifindex)))
goto nla_put_failure;
} else {
+ int physinif;
+
/* Case 2: indev is bridge group, we need to look for
* physical device (when called from ipv4) */
if (nla_put_be32(skb, NFQA_IFINDEX_INDEV,
htonl(indev->ifindex)))
goto nla_put_failure;
- if (entskb->nf_bridge && entskb->nf_bridge->physindev &&
+
+ physinif = nf_bridge_get_physinif(entskb);
+ if (physinif &&
nla_put_be32(skb, NFQA_IFINDEX_PHYSINDEV,
- htonl(entskb->nf_bridge->physindev->ifindex)))
+ htonl(physinif)))
goto nla_put_failure;
}
#endif
if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV, htonl(outdev->ifindex)))
goto nla_put_failure;
#else
- if (entry->pf == PF_BRIDGE) {
+ if (entry->state.pf == PF_BRIDGE) {
/* Case 1: outdev is physical output device, we need to
* look for bridge group (when called from
* netfilter_bridge) */
htonl(br_port_get_rcu(outdev)->br->dev->ifindex)))
goto nla_put_failure;
} else {
+ int physoutif;
+
/* Case 2: outdev is bridge group, we need to look for
* physical output device (when called from ipv4) */
if (nla_put_be32(skb, NFQA_IFINDEX_OUTDEV,
htonl(outdev->ifindex)))
goto nla_put_failure;
- if (entskb->nf_bridge && entskb->nf_bridge->physoutdev &&
+
+ physoutif = nf_bridge_get_physoutif(entskb);
+ if (physoutif &&
nla_put_be32(skb, NFQA_IFINDEX_PHYSOUTDEV,
- htonl(entskb->nf_bridge->physoutdev->ifindex)))
+ htonl(physoutif)))
goto nla_put_failure;
}
#endif
struct nfqnl_instance *queue;
struct sk_buff *skb, *segs;
int err = -ENOBUFS;
- struct net *net = dev_net(entry->indev ?
- entry->indev : entry->outdev);
+ struct net *net = dev_net(entry->state.in ?
+ entry->state.in : entry->state.out);
struct nfnl_queue_net *q = nfnl_queue_pernet(net);
/* rcu_read_lock()ed by nf_hook_slow() */
skb = entry->skb;
- switch (entry->pf) {
+ switch (entry->state.pf) {
case NFPROTO_IPV4:
skb->protocol = htons(ETH_P_IP);
break;
static int
dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
{
- if (entry->indev)
- if (entry->indev->ifindex == ifindex)
+ if (entry->state.in)
+ if (entry->state.in->ifindex == ifindex)
return 1;
- if (entry->outdev)
- if (entry->outdev->ifindex == ifindex)
+ if (entry->state.out)
+ if (entry->state.out->ifindex == ifindex)
return 1;
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
if (entry->skb->nf_bridge) {
- if (entry->skb->nf_bridge->physindev &&
- entry->skb->nf_bridge->physindev->ifindex == ifindex)
- return 1;
- if (entry->skb->nf_bridge->physoutdev &&
- entry->skb->nf_bridge->physoutdev->ifindex == ifindex)
+ int physinif, physoutif;
+
+ physinif = nf_bridge_get_physinif(entry->skb);
+ physoutif = nf_bridge_get_physoutif(entry->skb);
+
+ if (physinif == ifindex || physoutif == ifindex)
return 1;
}
#endif
return;
}
- switch(ret) {
- case true:
+ switch (ret ? 1 : 0) {
+ case 1:
data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
break;
- case false:
+ case 0:
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
break;
}
state = NF_CT_STATE_BIT(ctinfo);
dest->data[0] = state;
return;
+ default:
+ break;
}
if (ct == NULL)
return;
}
#endif
+ default:
+ break;
}
tuple = &ct->tuplehash[priv->dir].tuple;
case NFT_CT_PROTO_DST:
dest->data[0] = (__force __u16)tuple->dst.u.all;
return;
+ default:
+ break;
}
return;
err:
}
break;
#endif
+ default:
+ break;
}
}
--- /dev/null
+/*
+ * Copyright (c) 2015 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_core.h>
+
+struct nft_dynset {
+ struct nft_set *set;
+ struct nft_set_ext_tmpl tmpl;
+ enum nft_dynset_ops op:8;
+ enum nft_registers sreg_key:8;
+ enum nft_registers sreg_data:8;
+ u64 timeout;
+ struct nft_set_binding binding;
+};
+
+static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
+ struct nft_data data[NFT_REG_MAX + 1])
+{
+ const struct nft_dynset *priv = nft_expr_priv(expr);
+ u64 timeout;
+ void *elem;
+
+ if (set->size && !atomic_add_unless(&set->nelems, 1, set->size))
+ return NULL;
+
+ timeout = priv->timeout ? : set->timeout;
+ elem = nft_set_elem_init(set, &priv->tmpl,
+ &data[priv->sreg_key], &data[priv->sreg_data],
+ timeout, GFP_ATOMIC);
+ if (elem == NULL) {
+ if (set->size)
+ atomic_dec(&set->nelems);
+ }
+ return elem;
+}
+
+static void nft_dynset_eval(const struct nft_expr *expr,
+ struct nft_data data[NFT_REG_MAX + 1],
+ const struct nft_pktinfo *pkt)
+{
+ const struct nft_dynset *priv = nft_expr_priv(expr);
+ struct nft_set *set = priv->set;
+ const struct nft_set_ext *ext;
+ u64 timeout;
+
+ if (set->ops->update(set, &data[priv->sreg_key], nft_dynset_new,
+ expr, data, &ext)) {
+ if (priv->op == NFT_DYNSET_OP_UPDATE &&
+ nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
+ timeout = priv->timeout ? : set->timeout;
+ *nft_set_ext_expiration(ext) = jiffies + timeout;
+ return;
+ }
+ }
+
+ data[NFT_REG_VERDICT].verdict = NFT_BREAK;
+}
+
+static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
+ [NFTA_DYNSET_SET_NAME] = { .type = NLA_STRING },
+ [NFTA_DYNSET_SET_ID] = { .type = NLA_U32 },
+ [NFTA_DYNSET_OP] = { .type = NLA_U32 },
+ [NFTA_DYNSET_SREG_KEY] = { .type = NLA_U32 },
+ [NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 },
+ [NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 },
+};
+
+static int nft_dynset_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_dynset *priv = nft_expr_priv(expr);
+ struct nft_set *set;
+ u64 timeout;
+ int err;
+
+ if (tb[NFTA_DYNSET_SET_NAME] == NULL ||
+ tb[NFTA_DYNSET_OP] == NULL ||
+ tb[NFTA_DYNSET_SREG_KEY] == NULL)
+ return -EINVAL;
+
+ set = nf_tables_set_lookup(ctx->table, tb[NFTA_DYNSET_SET_NAME]);
+ if (IS_ERR(set)) {
+ if (tb[NFTA_DYNSET_SET_ID])
+ set = nf_tables_set_lookup_byid(ctx->net,
+ tb[NFTA_DYNSET_SET_ID]);
+ if (IS_ERR(set))
+ return PTR_ERR(set);
+ }
+
+ if (set->flags & NFT_SET_CONSTANT)
+ return -EBUSY;
+
+ priv->op = ntohl(nla_get_be32(tb[NFTA_DYNSET_OP]));
+ switch (priv->op) {
+ case NFT_DYNSET_OP_ADD:
+ break;
+ case NFT_DYNSET_OP_UPDATE:
+ if (!(set->flags & NFT_SET_TIMEOUT))
+ return -EOPNOTSUPP;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ timeout = 0;
+ if (tb[NFTA_DYNSET_TIMEOUT] != NULL) {
+ if (!(set->flags & NFT_SET_TIMEOUT))
+ return -EINVAL;
+ timeout = be64_to_cpu(nla_get_be64(tb[NFTA_DYNSET_TIMEOUT]));
+ }
+
+ priv->sreg_key = ntohl(nla_get_be32(tb[NFTA_DYNSET_SREG_KEY]));
+ err = nft_validate_input_register(priv->sreg_key);
+ if (err < 0)
+ return err;
+
+ if (tb[NFTA_DYNSET_SREG_DATA] != NULL) {
+ if (!(set->flags & NFT_SET_MAP))
+ return -EINVAL;
+ if (set->dtype == NFT_DATA_VERDICT)
+ return -EOPNOTSUPP;
+
+ priv->sreg_data = ntohl(nla_get_be32(tb[NFTA_DYNSET_SREG_DATA]));
+ err = nft_validate_input_register(priv->sreg_data);
+ if (err < 0)
+ return err;
+ } else if (set->flags & NFT_SET_MAP)
+ return -EINVAL;
+
+ nft_set_ext_prepare(&priv->tmpl);
+ nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);
+ if (set->flags & NFT_SET_MAP)
+ nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_DATA, set->dlen);
+ if (set->flags & NFT_SET_TIMEOUT) {
+ if (timeout || set->timeout)
+ nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
+ }
+
+ priv->timeout = timeout;
+
+ err = nf_tables_bind_set(ctx, set, &priv->binding);
+ if (err < 0)
+ return err;
+
+ priv->set = set;
+ return 0;
+}
+
+static void nft_dynset_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+{
+ struct nft_dynset *priv = nft_expr_priv(expr);
+
+ nf_tables_unbind_set(ctx, priv->set, &priv->binding);
+}
+
+static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ const struct nft_dynset *priv = nft_expr_priv(expr);
+
+ if (nla_put_be32(skb, NFTA_DYNSET_SREG_KEY, htonl(priv->sreg_key)))
+ goto nla_put_failure;
+ if (priv->set->flags & NFT_SET_MAP &&
+ nla_put_be32(skb, NFTA_DYNSET_SREG_DATA, htonl(priv->sreg_data)))
+ goto nla_put_failure;
+ if (nla_put_be32(skb, NFTA_DYNSET_OP, htonl(priv->op)))
+ goto nla_put_failure;
+ if (nla_put_string(skb, NFTA_DYNSET_SET_NAME, priv->set->name))
+ goto nla_put_failure;
+ if (nla_put_be64(skb, NFTA_DYNSET_TIMEOUT, cpu_to_be64(priv->timeout)))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+static struct nft_expr_type nft_dynset_type;
+static const struct nft_expr_ops nft_dynset_ops = {
+ .type = &nft_dynset_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_dynset)),
+ .eval = nft_dynset_eval,
+ .init = nft_dynset_init,
+ .destroy = nft_dynset_destroy,
+ .dump = nft_dynset_dump,
+};
+
+static struct nft_expr_type nft_dynset_type __read_mostly = {
+ .name = "dynset",
+ .ops = &nft_dynset_ops,
+ .policy = nft_dynset_policy,
+ .maxattr = NFTA_DYNSET_MAX,
+ .owner = THIS_MODULE,
+};
+
+int __init nft_dynset_module_init(void)
+{
+ return nft_register_expr(&nft_dynset_type);
+}
+
+void nft_dynset_module_exit(void)
+{
+ nft_unregister_expr(&nft_dynset_type);
+}
#include <linux/log2.h>
#include <linux/jhash.h>
#include <linux/netlink.h>
+#include <linux/workqueue.h>
#include <linux/rhashtable.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
struct nft_hash {
struct rhashtable ht;
+ struct delayed_work gc_work;
};
struct nft_hash_elem {
if (nft_data_cmp(nft_set_ext_key(&he->ext), x->key, x->set->klen))
return 1;
+ if (nft_set_elem_expired(&he->ext))
+ return 1;
if (!nft_set_elem_active(&he->ext, x->genmask))
return 1;
return 0;
return !!he;
}
+static bool nft_hash_update(struct nft_set *set, const struct nft_data *key,
+ void *(*new)(struct nft_set *,
+ const struct nft_expr *,
+ struct nft_data []),
+ const struct nft_expr *expr,
+ struct nft_data data[],
+ const struct nft_set_ext **ext)
+{
+ struct nft_hash *priv = nft_set_priv(set);
+ struct nft_hash_elem *he;
+ struct nft_hash_cmp_arg arg = {
+ .genmask = NFT_GENMASK_ANY,
+ .set = set,
+ .key = key,
+ };
+
+ he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
+ if (he != NULL)
+ goto out;
+
+ he = new(set, expr, data);
+ if (he == NULL)
+ goto err1;
+ if (rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node,
+ nft_hash_params))
+ goto err2;
+out:
+ *ext = &he->ext;
+ return true;
+
+err2:
+ nft_set_elem_destroy(set, he);
+err1:
+ return false;
+}
+
static int nft_hash_insert(const struct nft_set *set,
const struct nft_set_elem *elem)
{
struct nft_hash_elem *he = elem->priv;
nft_set_elem_change_active(set, &he->ext);
+ nft_set_elem_clear_busy(&he->ext);
}
static void *nft_hash_deactivate(const struct nft_set *set,
.key = &elem->key,
};
+ rcu_read_lock();
he = rhashtable_lookup_fast(&priv->ht, &arg, nft_hash_params);
- if (he != NULL)
- nft_set_elem_change_active(set, &he->ext);
+ if (he != NULL) {
+ if (!nft_set_elem_mark_busy(&he->ext))
+ nft_set_elem_change_active(set, &he->ext);
+ else
+ he = NULL;
+ }
+ rcu_read_unlock();
return he;
}
if (iter->count < iter->skip)
goto cont;
+ if (nft_set_elem_expired(&he->ext))
+ goto cont;
if (!nft_set_elem_active(&he->ext, genmask))
goto cont;
rhashtable_walk_exit(&hti);
}
+static void nft_hash_gc(struct work_struct *work)
+{
+ struct nft_set *set;
+ struct nft_hash_elem *he;
+ struct nft_hash *priv;
+ struct nft_set_gc_batch *gcb = NULL;
+ struct rhashtable_iter hti;
+ int err;
+
+ priv = container_of(work, struct nft_hash, gc_work.work);
+ set = nft_set_container_of(priv);
+
+ err = rhashtable_walk_init(&priv->ht, &hti);
+ if (err)
+ goto schedule;
+
+ err = rhashtable_walk_start(&hti);
+ if (err && err != -EAGAIN)
+ goto out;
+
+ while ((he = rhashtable_walk_next(&hti))) {
+ if (IS_ERR(he)) {
+ if (PTR_ERR(he) != -EAGAIN)
+ goto out;
+ continue;
+ }
+
+ if (!nft_set_elem_expired(&he->ext))
+ continue;
+ if (nft_set_elem_mark_busy(&he->ext))
+ continue;
+
+ gcb = nft_set_gc_batch_check(set, gcb, GFP_ATOMIC);
+ if (gcb == NULL)
+ goto out;
+ rhashtable_remove_fast(&priv->ht, &he->node, nft_hash_params);
+ atomic_dec(&set->nelems);
+ nft_set_gc_batch_add(gcb, he);
+ }
+out:
+ rhashtable_walk_stop(&hti);
+ rhashtable_walk_exit(&hti);
+
+ nft_set_gc_batch_complete(gcb);
+schedule:
+ queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
+ nft_set_gc_interval(set));
+}
+
static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
{
return sizeof(struct nft_hash);
{
struct nft_hash *priv = nft_set_priv(set);
struct rhashtable_params params = nft_hash_params;
+ int err;
params.nelem_hint = desc->size ?: NFT_HASH_ELEMENT_HINT;
params.key_len = set->klen;
- return rhashtable_init(&priv->ht, ¶ms);
+ err = rhashtable_init(&priv->ht, ¶ms);
+ if (err < 0)
+ return err;
+
+ INIT_DEFERRABLE_WORK(&priv->gc_work, nft_hash_gc);
+ if (set->flags & NFT_SET_TIMEOUT)
+ queue_delayed_work(system_power_efficient_wq, &priv->gc_work,
+ nft_set_gc_interval(set));
+ return 0;
}
static void nft_hash_elem_destroy(void *ptr, void *arg)
{
struct nft_hash *priv = nft_set_priv(set);
+ cancel_delayed_work_sync(&priv->gc_work);
rhashtable_free_and_destroy(&priv->ht, nft_hash_elem_destroy,
(void *)set);
}
.deactivate = nft_hash_deactivate,
.remove = nft_hash_remove,
.lookup = nft_hash_lookup,
+ .update = nft_hash_update,
.walk = nft_hash_walk,
- .features = NFT_SET_MAP,
+ .features = NFT_SET_MAP | NFT_SET_TIMEOUT,
.owner = THIS_MODULE,
};
} else if (set->flags & NFT_SET_MAP)
return -EINVAL;
+ priv->binding.flags = set->flags & NFT_SET_MAP;
+
err = nf_tables_bind_set(ctx, set, &priv->binding);
if (err < 0)
return err;
dest->data[0] = out->group;
break;
case NFT_META_CGROUP:
- if (skb->sk == NULL)
- break;
-
+ if (skb->sk == NULL || !sk_fullsock(skb->sk))
+ goto err;
dest->data[0] = skb->sk->sk_classid;
break;
default:
{
const struct xt_cgroup_info *info = par->matchinfo;
- if (skb->sk == NULL)
+ if (skb->sk == NULL || !sk_fullsock(skb->sk))
return false;
return (info->id == skb->sk->sk_classid) ^ info->invert;
static bool
physdev_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
- static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
const struct xt_physdev_info *info = par->matchinfo;
+ const struct net_device *physdev;
unsigned long ret;
const char *indev, *outdev;
- const struct nf_bridge_info *nf_bridge;
/* Not a bridged IP packet or no info available yet:
* LOCAL_OUT/mangle and LOCAL_OUT/nat don't know if
* the destination device will be a bridge. */
- if (!(nf_bridge = skb->nf_bridge)) {
+ if (!skb->nf_bridge) {
/* Return MATCH if the invert flags of the used options are on */
if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
!(info->invert & XT_PHYSDEV_OP_BRIDGED))
return true;
}
+ physdev = nf_bridge_get_physoutdev(skb);
+ outdev = physdev ? physdev->name : NULL;
+
/* This only makes sense in the FORWARD and POSTROUTING chains */
if ((info->bitmask & XT_PHYSDEV_OP_BRIDGED) &&
- (!!nf_bridge->physoutdev ^ !(info->invert & XT_PHYSDEV_OP_BRIDGED)))
+ (!!outdev ^ !(info->invert & XT_PHYSDEV_OP_BRIDGED)))
return false;
+ physdev = nf_bridge_get_physindev(skb);
+ indev = physdev ? physdev->name : NULL;
+
if ((info->bitmask & XT_PHYSDEV_OP_ISIN &&
- (!nf_bridge->physindev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) ||
+ (!indev ^ !!(info->invert & XT_PHYSDEV_OP_ISIN))) ||
(info->bitmask & XT_PHYSDEV_OP_ISOUT &&
- (!nf_bridge->physoutdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))))
+ (!outdev ^ !!(info->invert & XT_PHYSDEV_OP_ISOUT))))
return false;
if (!(info->bitmask & XT_PHYSDEV_OP_IN))
goto match_outdev;
- indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
- ret = ifname_compare_aligned(indev, info->physindev, info->in_mask);
- if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN))
- return false;
+ if (indev) {
+ ret = ifname_compare_aligned(indev, info->physindev,
+ info->in_mask);
+
+ if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN))
+ return false;
+ }
match_outdev:
if (!(info->bitmask & XT_PHYSDEV_OP_OUT))
return true;
- outdev = nf_bridge->physoutdev ?
- nf_bridge->physoutdev->name : nulldevname;
+
+ if (!outdev)
+ return false;
+
ret = ifname_compare_aligned(outdev, info->physoutdev, info->out_mask);
return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT));
}
}
-static bool
-socket_match(const struct sk_buff *skb, struct xt_action_param *par,
- const struct xt_socket_mtinfo1 *info)
+static struct sock *xt_socket_lookup_slow_v4(const struct sk_buff *skb,
+ const struct net_device *indev)
{
const struct iphdr *iph = ip_hdr(skb);
- struct udphdr _hdr, *hp = NULL;
- struct sock *sk = skb->sk;
__be32 uninitialized_var(daddr), uninitialized_var(saddr);
__be16 uninitialized_var(dport), uninitialized_var(sport);
u8 uninitialized_var(protocol);
#endif
if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
+ struct udphdr _hdr, *hp;
+
hp = skb_header_pointer(skb, ip_hdrlen(skb),
sizeof(_hdr), &_hdr);
if (hp == NULL)
- return false;
+ return NULL;
protocol = iph->protocol;
saddr = iph->saddr;
} else if (iph->protocol == IPPROTO_ICMP) {
if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr,
- &sport, &dport))
- return false;
+ &sport, &dport))
+ return NULL;
} else {
- return false;
+ return NULL;
}
#ifdef XT_SOCKET_HAVE_CONNTRACK
- /* Do the lookup with the original socket address in case this is a
- * reply packet of an established SNAT-ted connection. */
-
+ /* Do the lookup with the original socket address in
+ * case this is a reply packet of an established
+ * SNAT-ted connection.
+ */
ct = nf_ct_get(skb, &ctinfo);
if (ct && !nf_ct_is_untracked(ct) &&
((iph->protocol != IPPROTO_ICMP &&
}
#endif
+ return xt_socket_get_sock_v4(dev_net(skb->dev), protocol, saddr, daddr,
+ sport, dport, indev);
+}
+
+static bool
+socket_match(const struct sk_buff *skb, struct xt_action_param *par,
+ const struct xt_socket_mtinfo1 *info)
+{
+ struct sock *sk = skb->sk;
+
if (!sk)
- sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol,
- saddr, daddr, sport, dport,
- par->in);
+ sk = xt_socket_lookup_slow_v4(skb, par->in);
if (sk) {
bool wildcard;
bool transparent = true;
sk = NULL;
}
- pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n",
- protocol, &saddr, ntohs(sport),
- &daddr, ntohs(dport),
- &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
-
- return (sk != NULL);
+ return sk != NULL;
}
static bool
return NULL;
}
-static bool
-socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
+static struct sock *xt_socket_lookup_slow_v6(const struct sk_buff *skb,
+ const struct net_device *indev)
{
- struct ipv6hdr ipv6_var, *iph = ipv6_hdr(skb);
- struct udphdr _hdr, *hp = NULL;
- struct sock *sk = skb->sk;
- const struct in6_addr *daddr = NULL, *saddr = NULL;
__be16 uninitialized_var(dport), uninitialized_var(sport);
- int thoff = 0, uninitialized_var(tproto);
- const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
+ const struct in6_addr *daddr = NULL, *saddr = NULL;
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+ int thoff = 0, tproto;
tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
if (tproto < 0) {
pr_debug("unable to find transport header in IPv6 packet, dropping\n");
- return NF_DROP;
+ return NULL;
}
if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) {
- hp = skb_header_pointer(skb, thoff,
- sizeof(_hdr), &_hdr);
+ struct udphdr _hdr, *hp;
+
+ hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
if (hp == NULL)
- return false;
+ return NULL;
saddr = &iph->saddr;
sport = hp->source;
dport = hp->dest;
} else if (tproto == IPPROTO_ICMPV6) {
+ struct ipv6hdr ipv6_var;
+
if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr,
&sport, &dport, &ipv6_var))
- return false;
+ return NULL;
} else {
- return false;
+ return NULL;
}
+ return xt_socket_get_sock_v6(dev_net(skb->dev), tproto, saddr, daddr,
+ sport, dport, indev);
+}
+
+static bool
+socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
+ struct sock *sk = skb->sk;
+
if (!sk)
- sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto,
- saddr, daddr, sport, dport,
- par->in);
+ sk = xt_socket_lookup_slow_v6(skb, par->in);
if (sk) {
bool wildcard;
bool transparent = true;
sk = NULL;
}
- pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu "
- "(orig %pI6:%hu) sock %p\n",
- tproto, saddr, ntohs(sport),
- daddr, ntohs(dport),
- &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
-
- return (sk != NULL);
+ return sk != NULL;
}
#endif
return 0;
}
+static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+ if (!ndev->ops->fw_download)
+ return -ENOTSUPP;
+
+ return ndev->ops->fw_download(ndev, firmware_name);
+}
+
static struct nfc_ops nci_nfc_ops = {
.dev_up = nci_dev_up,
.dev_down = nci_dev_down,
.disable_se = nci_disable_se,
.discover_se = nci_discover_se,
.se_io = nci_se_io,
+ .fw_download = nci_fw_download,
};
/* ---- Interface to NCI drivers ---- */
{
struct net *net = ovs_dp_get_net(vport->dp);
struct vxlan_port *vxlan_port = vxlan_vport(vport);
- __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport;
+ struct sock *sk = vxlan_port->vs->sock->sk;
+ __be16 dst_port = inet_sk(sk)->inet_sport;
const struct ovs_key_ipv4_tunnel *tun_key;
struct vxlan_metadata md = {0};
struct rtable *rt;
vxflags = vxlan_port->exts |
(tun_key->tun_flags & TUNNEL_CSUM ? VXLAN_F_UDP_CSUM : 0);
- err = vxlan_xmit_skb(rt, skb, fl.saddr, tun_key->ipv4_dst,
+ err = vxlan_xmit_skb(rt, sk, skb, fl.saddr, tun_key->ipv4_dst,
tun_key->ipv4_tos, tun_key->ipv4_ttl, df,
src_port, dst_port,
&md, false, vxflags);
ASSERT_OVSL();
hlist_del_rcu(&vport->hash_node);
-
- vport->ops->destroy(vport);
-
module_put(vport->ops->owner);
+ vport->ops->destroy(vport);
}
/**
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Meant to be mostly used for localy generated traffic :
+ * Meant to be mostly used for locally generated traffic :
* Fast classification depends on skb->sk being set before reaching us.
* If not, (router workload), we use rxhash as fallback, with 32 bits wide hash.
* All packets belonging to a socket are considered as a 'flow'.
struct sk_buff *tail; /* last skb in the list */
unsigned long age; /* jiffies when flow was emptied, for gc */
};
- struct rb_node fq_node; /* anchor in fq_root[] trees */
+ struct rb_node fq_node; /* anchor in fq_root[] trees */
struct sock *sk;
int qlen; /* number of packets in flow queue */
int credit;
struct super_block *pipefs_sb;
int err;
- err = rpc_clnt_debugfs_register(clnt);
- if (err)
- return err;
+ rpc_clnt_debugfs_register(clnt);
pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) {
.release = tasks_release,
};
-int
+void
rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
{
- int len, err;
+ int len;
char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
+ struct rpc_xprt *xprt;
/* Already registered? */
- if (clnt->cl_debugfs)
- return 0;
+ if (clnt->cl_debugfs || !rpc_clnt_dir)
+ return;
len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
if (len >= sizeof(name))
- return -EINVAL;
+ return;
/* make the per-client dir */
clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
if (!clnt->cl_debugfs)
- return -ENOMEM;
+ return;
/* make tasks file */
- err = -ENOMEM;
if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
clnt, &tasks_fops))
goto out_err;
- err = -EINVAL;
rcu_read_lock();
+ xprt = rcu_dereference(clnt->cl_xprt);
+ /* no "debugfs" dentry? Don't bother with the symlink. */
+ if (!xprt->debugfs) {
+ rcu_read_unlock();
+ return;
+ }
len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
- rcu_dereference(clnt->cl_xprt)->debugfs->d_name.name);
+ xprt->debugfs->d_name.name);
rcu_read_unlock();
+
if (len >= sizeof(name))
goto out_err;
- err = -ENOMEM;
if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name))
goto out_err;
- return 0;
+ return;
out_err:
debugfs_remove_recursive(clnt->cl_debugfs);
clnt->cl_debugfs = NULL;
- return err;
}
void
.release = xprt_info_release,
};
-int
+void
rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
{
int len, id;
static atomic_t cur_id;
char name[9]; /* 8 hex digits + NULL term */
+ if (!rpc_xprt_dir)
+ return;
+
id = (unsigned int)atomic_inc_return(&cur_id);
len = snprintf(name, sizeof(name), "%x", id);
if (len >= sizeof(name))
- return -EINVAL;
+ return;
/* make the per-client dir */
xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
if (!xprt->debugfs)
- return -ENOMEM;
+ return;
/* make tasks file */
if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs,
xprt, &xprt_info_fops)) {
debugfs_remove_recursive(xprt->debugfs);
xprt->debugfs = NULL;
- return -ENOMEM;
}
-
- return 0;
}
void
sunrpc_debugfs_exit(void)
{
debugfs_remove_recursive(topdir);
+ topdir = NULL;
+ rpc_clnt_dir = NULL;
+ rpc_xprt_dir = NULL;
}
-int __init
+void __init
sunrpc_debugfs_init(void)
{
topdir = debugfs_create_dir("sunrpc", NULL);
if (!topdir)
- goto out;
+ return;
rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
if (!rpc_clnt_dir)
if (!rpc_xprt_dir)
goto out_remove;
- return 0;
+ return;
out_remove:
debugfs_remove_recursive(topdir);
topdir = NULL;
-out:
- return -ENOMEM;
+ rpc_clnt_dir = NULL;
}
if (err)
goto out4;
- err = sunrpc_debugfs_init();
- if (err)
- goto out5;
-
+ sunrpc_debugfs_init();
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
rpc_register_sysctl();
#endif
init_socket_xprt(); /* clnt sock transport */
return 0;
-out5:
- unregister_rpc_pipefs();
out4:
unregister_pernet_subsys(&sunrpc_net_ops);
out3:
*/
struct rpc_xprt *xprt_create_transport(struct xprt_create *args)
{
- int err;
struct rpc_xprt *xprt;
struct xprt_class *t;
return ERR_PTR(-ENOMEM);
}
- err = rpc_xprt_debugfs_register(xprt);
- if (err) {
- xprt_destroy(xprt);
- return ERR_PTR(err);
- }
+ rpc_xprt_debugfs_register(xprt);
dprintk("RPC: created transport %p with %u slots\n", xprt,
xprt->max_reqs);
*/
if (((seqno - tn->own_addr) % TIPC_MIN_LINK_WIN) == 0) {
tipc_link_proto_xmit(node->active_links[node->addr & 1],
- STATE_MSG, 0, 0, 0, 0, 0);
+ STATE_MSG, 0, 0, 0, 0);
tn->bcl->stats.sent_acks++;
}
}
skb_queue_head_init(&bclink->inputq);
bcl->owner = &bclink->node;
bcl->owner->net = net;
- bcl->max_pkt = MAX_PKT_DEFAULT_MCAST;
+ bcl->mtu = MAX_PKT_DEFAULT_MCAST;
tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT);
bcl->bearer_id = MAX_BEARERS;
rcu_assign_pointer(tn->bearer_list[MAX_BEARERS], &bcbearer->bearer);
static void __exit tipc_exit(void)
{
tipc_bearer_cleanup();
+ unregister_pernet_subsys(&tipc_net_ops);
tipc_netlink_stop();
tipc_netlink_compat_stop();
tipc_socket_stop();
tipc_unregister_sysctl();
- unregister_pernet_subsys(&tipc_net_ops);
pr_info("Deactivated\n");
}
#define TIMEOUT_EVT 560817u /* link timer expired */
/*
- * The following two 'message types' is really just implementation
- * data conveniently stored in the message header.
- * They must not be considered part of the protocol
+ * State value stored in 'failover_pkts'
*/
-#define OPEN_MSG 0
-#define CLOSED_MSG 1
-
-/*
- * State value stored in 'exp_msg_count'
- */
-#define START_CHANGEOVER 100000u
+#define FIRST_FAILOVER 0xffffu
static void link_handle_out_of_seq_msg(struct tipc_link *link,
struct sk_buff *skb);
static void tipc_link_proto_rcv(struct tipc_link *link,
struct sk_buff *skb);
-static int tipc_link_tunnel_rcv(struct tipc_node *node,
- struct sk_buff **skb);
static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tol);
static void link_state_event(struct tipc_link *l_ptr, u32 event);
static void link_reset_statistics(struct tipc_link *l_ptr);
static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf);
static void tipc_link_input(struct tipc_link *l, struct sk_buff *skb);
static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb);
-
+static bool tipc_link_failover_rcv(struct tipc_link *l, struct sk_buff **skb);
/*
* Simple link routines
*/
return l->owner->active_links[1];
}
-static void link_init_max_pkt(struct tipc_link *l_ptr)
-{
- struct tipc_node *node = l_ptr->owner;
- struct tipc_net *tn = net_generic(node->net, tipc_net_id);
- struct tipc_bearer *b_ptr;
- u32 max_pkt;
-
- rcu_read_lock();
- b_ptr = rcu_dereference_rtnl(tn->bearer_list[l_ptr->bearer_id]);
- if (!b_ptr) {
- rcu_read_unlock();
- return;
- }
- max_pkt = (b_ptr->mtu & ~3);
- rcu_read_unlock();
-
- if (max_pkt > MAX_MSG_SIZE)
- max_pkt = MAX_MSG_SIZE;
-
- l_ptr->max_pkt_target = max_pkt;
- if (l_ptr->max_pkt_target < MAX_PKT_DEFAULT)
- l_ptr->max_pkt = l_ptr->max_pkt_target;
- else
- l_ptr->max_pkt = MAX_PKT_DEFAULT;
-
- l_ptr->max_pkt_probes = 0;
-}
-
/*
* Simple non-static link routines (i.e. referenced outside this file)
*/
msg_set_bearer_id(msg, b_ptr->identity);
strcpy((char *)msg_data(msg), if_name);
l_ptr->net_plane = b_ptr->net_plane;
- link_init_max_pkt(l_ptr);
+ l_ptr->advertised_mtu = b_ptr->mtu;
+ l_ptr->mtu = l_ptr->advertised_mtu;
l_ptr->priority = b_ptr->priority;
tipc_link_set_queue_limits(l_ptr, b_ptr->window);
l_ptr->next_out_no = 1;
}
/**
- * link_delete - Conditional deletion of link.
- * If timer still running, real delete is done when it expires
- * @link: link to be deleted
+ * tipc_link_delete - Delete a link
+ * @l: link to be deleted
*/
-void tipc_link_delete(struct tipc_link *link)
+void tipc_link_delete(struct tipc_link *l)
{
- tipc_link_reset_fragments(link);
- tipc_node_detach_link(link->owner, link);
- tipc_link_put(link);
+ tipc_link_reset(l);
+ if (del_timer(&l->timer))
+ tipc_link_put(l);
+ l->flags |= LINK_STOPPED;
+ /* Delete link now, or when timer is finished: */
+ tipc_link_reset_fragments(l);
+ tipc_node_detach_link(l->owner, l);
+ tipc_link_put(l);
}
void tipc_link_delete_list(struct net *net, unsigned int bearer_id,
struct tipc_net *tn = net_generic(net, tipc_net_id);
struct tipc_link *link;
struct tipc_node *node;
- bool del_link;
rcu_read_lock();
list_for_each_entry_rcu(node, &tn->node_list, list) {
tipc_node_lock(node);
link = node->links[bearer_id];
- if (!link) {
- tipc_node_unlock(node);
- continue;
- }
- del_link = !tipc_link_is_up(link) && !link->exp_msg_count;
- tipc_link_reset(link);
- if (del_timer(&link->timer))
- tipc_link_put(link);
- link->flags |= LINK_STOPPED;
- /* Delete link now, or when failover is finished: */
- if (shutting_down || !tipc_node_is_up(node) || del_link)
+ if (link)
tipc_link_delete(link);
tipc_node_unlock(node);
}
void tipc_link_reset(struct tipc_link *l_ptr)
{
u32 prev_state = l_ptr->state;
- u32 checkpoint = l_ptr->next_in_no;
int was_active_link = tipc_link_is_active(l_ptr);
struct tipc_node *owner = l_ptr->owner;
+ struct tipc_link *pl = tipc_parallel_link(l_ptr);
msg_set_session(l_ptr->pmsg, ((msg_session(l_ptr->pmsg) + 1) & 0xffff));
/* Link is down, accept any session */
l_ptr->peer_session = INVALID_SESSION;
- /* Prepare for max packet size negotiation */
- link_init_max_pkt(l_ptr);
+ /* Prepare for renewed mtu size negotiation */
+ l_ptr->mtu = l_ptr->advertised_mtu;
l_ptr->state = RESET_UNKNOWN;
tipc_node_link_down(l_ptr->owner, l_ptr);
tipc_bearer_remove_dest(owner->net, l_ptr->bearer_id, l_ptr->addr);
- if (was_active_link && tipc_node_active_links(l_ptr->owner)) {
- l_ptr->reset_checkpoint = checkpoint;
- l_ptr->exp_msg_count = START_CHANGEOVER;
+ if (was_active_link && tipc_node_is_up(l_ptr->owner) && (pl != l_ptr)) {
+ l_ptr->flags |= LINK_FAILINGOVER;
+ l_ptr->failover_checkpt = l_ptr->next_in_no;
+ pl->failover_pkts = FIRST_FAILOVER;
+ pl->failover_checkpt = l_ptr->next_in_no;
+ pl->failover_skb = l_ptr->reasm_buf;
+ } else {
+ kfree_skb(l_ptr->reasm_buf);
}
-
/* Clean up all queues, except inputq: */
__skb_queue_purge(&l_ptr->transmq);
__skb_queue_purge(&l_ptr->deferdq);
if (!skb_queue_empty(owner->inputq))
owner->action_flags |= TIPC_MSG_EVT;
tipc_link_purge_backlog(l_ptr);
+ l_ptr->reasm_buf = NULL;
l_ptr->rcv_unacked = 0;
l_ptr->checkpoint = 1;
l_ptr->next_out_no = 1;
if (!(l_ptr->flags & LINK_STARTED) && (event != STARTING_EVT))
return; /* Not yet. */
- /* Check whether changeover is going on */
- if (l_ptr->exp_msg_count) {
+ if (l_ptr->flags & LINK_FAILINGOVER) {
if (event == TIMEOUT_EVT)
link_set_timer(l_ptr, cont_intv);
return;
l_ptr->checkpoint = l_ptr->next_in_no;
if (tipc_bclink_acks_missing(l_ptr->owner)) {
tipc_link_proto_xmit(l_ptr, STATE_MSG,
- 0, 0, 0, 0, 0);
- l_ptr->fsm_msg_cnt++;
- } else if (l_ptr->max_pkt < l_ptr->max_pkt_target) {
- tipc_link_proto_xmit(l_ptr, STATE_MSG,
- 1, 0, 0, 0, 0);
+ 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
}
link_set_timer(l_ptr, cont_intv);
}
l_ptr->state = WORKING_UNKNOWN;
l_ptr->fsm_msg_cnt = 0;
- tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv / 4);
break;
l_ptr->state = RESET_RESET;
l_ptr->fsm_msg_cnt = 0;
tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
l_ptr->state = RESET_RESET;
l_ptr->fsm_msg_cnt = 0;
tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
l_ptr->checkpoint = l_ptr->next_in_no;
if (tipc_bclink_acks_missing(l_ptr->owner)) {
tipc_link_proto_xmit(l_ptr, STATE_MSG,
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
}
link_set_timer(l_ptr, cont_intv);
} else if (l_ptr->fsm_msg_cnt < l_ptr->abort_limit) {
tipc_link_proto_xmit(l_ptr, STATE_MSG,
- 1, 0, 0, 0, 0);
+ 1, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv / 4);
} else { /* Link has failed */
l_ptr->state = RESET_UNKNOWN;
l_ptr->fsm_msg_cnt = 0;
tipc_link_proto_xmit(l_ptr, RESET_MSG,
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
}
l_ptr->state = WORKING_WORKING;
l_ptr->fsm_msg_cnt = 0;
link_activate(l_ptr);
- tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
if (l_ptr->owner->working_links == 1)
tipc_link_sync_xmit(l_ptr);
l_ptr->state = RESET_RESET;
l_ptr->fsm_msg_cnt = 0;
tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
- 1, 0, 0, 0, 0);
+ 1, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
link_set_timer(l_ptr, cont_intv);
break;
case TIMEOUT_EVT:
- tipc_link_proto_xmit(l_ptr, RESET_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, RESET_MSG, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
l_ptr->state = WORKING_WORKING;
l_ptr->fsm_msg_cnt = 0;
link_activate(l_ptr);
- tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 1, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
if (l_ptr->owner->working_links == 1)
tipc_link_sync_xmit(l_ptr);
break;
case TIMEOUT_EVT:
tipc_link_proto_xmit(l_ptr, ACTIVATE_MSG,
- 0, 0, 0, 0, 0);
+ 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
link_set_timer(l_ptr, cont_intv);
break;
struct tipc_msg *msg = buf_msg(skb_peek(list));
unsigned int maxwin = link->window;
unsigned int imp = msg_importance(msg);
- uint mtu = link->max_pkt;
+ uint mtu = link->mtu;
uint ack = mod(link->next_in_no - 1);
uint seqno = link->next_out_no;
uint bc_last_in = link->owner->bclink.last_in;
link_retrieve_defq(l_ptr, &head);
if (unlikely(++l_ptr->rcv_unacked >= TIPC_MIN_LINK_WIN)) {
l_ptr->stats.sent_acks++;
- tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0);
}
tipc_link_input(l_ptr, skb);
skb = NULL;
node->action_flags |= TIPC_NAMED_MSG_EVT;
return true;
case MSG_BUNDLER:
- case CHANGEOVER_PROTOCOL:
+ case TUNNEL_PROTOCOL:
case MSG_FRAGMENTER:
case BCAST_PROTOCOL:
return false;
return;
switch (msg_user(msg)) {
- case CHANGEOVER_PROTOCOL:
+ case TUNNEL_PROTOCOL:
if (msg_dup(msg)) {
link->flags |= LINK_SYNCHING;
link->synch_point = msg_seqno(msg_get_wrapped(msg));
+ kfree_skb(skb);
+ break;
}
- if (!tipc_link_tunnel_rcv(node, &skb))
+ if (!tipc_link_failover_rcv(link, &skb))
break;
if (msg_user(buf_msg(skb)) != MSG_BUNDLER) {
tipc_data_input(link, skb);
if (tipc_link_defer_pkt(&l_ptr->deferdq, buf)) {
l_ptr->stats.deferred_recv++;
if ((skb_queue_len(&l_ptr->deferdq) % TIPC_MIN_LINK_WIN) == 1)
- tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0);
} else {
l_ptr->stats.duplicates++;
}
* Send protocol message to the other endpoint.
*/
void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
- u32 gap, u32 tolerance, u32 priority, u32 ack_mtu)
+ u32 gap, u32 tolerance, u32 priority)
{
struct sk_buff *buf = NULL;
struct tipc_msg *msg = l_ptr->pmsg;
u32 msg_size = sizeof(l_ptr->proto_msg);
int r_flag;
- /* Don't send protocol message during link changeover */
- if (l_ptr->exp_msg_count)
+ /* Don't send protocol message during link failover */
+ if (l_ptr->flags & LINK_FAILINGOVER)
return;
/* Abort non-RESET send if communication with node is prohibited */
l_ptr->stats.sent_nacks++;
msg_set_link_tolerance(msg, tolerance);
msg_set_linkprio(msg, priority);
- msg_set_max_pkt(msg, ack_mtu);
+ msg_set_max_pkt(msg, l_ptr->mtu);
msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
msg_set_probe(msg, probe_msg != 0);
- if (probe_msg) {
- u32 mtu = l_ptr->max_pkt;
-
- if ((mtu < l_ptr->max_pkt_target) &&
- link_working_working(l_ptr) &&
- l_ptr->fsm_msg_cnt) {
- msg_size = (mtu + (l_ptr->max_pkt_target - mtu)/2 + 2) & ~3;
- if (l_ptr->max_pkt_probes == 10) {
- l_ptr->max_pkt_target = (msg_size - 4);
- l_ptr->max_pkt_probes = 0;
- msg_size = (mtu + (l_ptr->max_pkt_target - mtu)/2 + 2) & ~3;
- }
- l_ptr->max_pkt_probes++;
- }
-
+ if (probe_msg)
l_ptr->stats.sent_probes++;
- }
l_ptr->stats.sent_states++;
} else { /* RESET_MSG or ACTIVATE_MSG */
- msg_set_ack(msg, mod(l_ptr->reset_checkpoint - 1));
+ msg_set_ack(msg, mod(l_ptr->failover_checkpt - 1));
msg_set_seq_gap(msg, 0);
msg_set_next_sent(msg, 1);
msg_set_probe(msg, 0);
msg_set_link_tolerance(msg, l_ptr->tolerance);
msg_set_linkprio(msg, l_ptr->priority);
- msg_set_max_pkt(msg, l_ptr->max_pkt_target);
+ msg_set_max_pkt(msg, l_ptr->advertised_mtu);
}
r_flag = (l_ptr->owner->working_links > tipc_link_is_up(l_ptr));
struct sk_buff *buf)
{
u32 rec_gap = 0;
- u32 max_pkt_info;
- u32 max_pkt_ack;
u32 msg_tol;
struct tipc_msg *msg = buf_msg(buf);
- /* Discard protocol message during link changeover */
- if (l_ptr->exp_msg_count)
+ if (l_ptr->flags & LINK_FAILINGOVER)
goto exit;
if (l_ptr->net_plane != msg_net_plane(msg))
if (msg_linkprio(msg) > l_ptr->priority)
l_ptr->priority = msg_linkprio(msg);
- max_pkt_info = msg_max_pkt(msg);
- if (max_pkt_info) {
- if (max_pkt_info < l_ptr->max_pkt_target)
- l_ptr->max_pkt_target = max_pkt_info;
- if (l_ptr->max_pkt > l_ptr->max_pkt_target)
- l_ptr->max_pkt = l_ptr->max_pkt_target;
- } else {
- l_ptr->max_pkt = l_ptr->max_pkt_target;
- }
+ if (l_ptr->mtu > msg_max_pkt(msg))
+ l_ptr->mtu = msg_max_pkt(msg);
/* Synchronize broadcast link info, if not done previously */
if (!tipc_node_is_up(l_ptr->owner)) {
mod(l_ptr->next_in_no));
}
- max_pkt_ack = msg_max_pkt(msg);
- if (max_pkt_ack > l_ptr->max_pkt) {
- l_ptr->max_pkt = max_pkt_ack;
- l_ptr->max_pkt_probes = 0;
- }
-
- max_pkt_ack = 0;
- if (msg_probe(msg)) {
+ if (msg_probe(msg))
l_ptr->stats.recv_probes++;
- if (msg_size(msg) > sizeof(l_ptr->proto_msg))
- max_pkt_ack = msg_size(msg);
- }
/* Protocol message before retransmits, reduce loss risk */
if (l_ptr->owner->bclink.recv_permitted)
msg_last_bcast(msg));
if (rec_gap || (msg_probe(msg))) {
- tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, rec_gap, 0,
- 0, max_pkt_ack);
+ tipc_link_proto_xmit(l_ptr, STATE_MSG, 0,
+ rec_gap, 0, 0);
}
if (msg_seq_gap(msg)) {
l_ptr->stats.recv_nacks++;
if (!tunnel)
return;
- tipc_msg_init(link_own_addr(l_ptr), &tunnel_hdr, CHANGEOVER_PROTOCOL,
- ORIGINAL_MSG, INT_H_SIZE, l_ptr->addr);
+ tipc_msg_init(link_own_addr(l_ptr), &tunnel_hdr, TUNNEL_PROTOCOL,
+ FAILOVER_MSG, INT_H_SIZE, l_ptr->addr);
skb_queue_splice_tail_init(&l_ptr->backlogq, &l_ptr->transmq);
tipc_link_purge_backlog(l_ptr);
msgcount = skb_queue_len(&l_ptr->transmq);
struct sk_buff_head *queue = &link->transmq;
int mcnt;
- tipc_msg_init(link_own_addr(link), &tnl_hdr, CHANGEOVER_PROTOCOL,
- DUPLICATE_MSG, INT_H_SIZE, link->addr);
+ tipc_msg_init(link_own_addr(link), &tnl_hdr, TUNNEL_PROTOCOL,
+ SYNCH_MSG, INT_H_SIZE, link->addr);
mcnt = skb_queue_len(&link->transmq) + skb_queue_len(&link->backlogq);
msg_set_msgcnt(&tnl_hdr, mcnt);
msg_set_bearer_id(&tnl_hdr, link->peer_bearer_id);
goto tunnel_queue;
}
-/* tipc_link_dup_rcv(): Receive a tunnelled DUPLICATE_MSG packet.
- * Owner node is locked.
- */
-static void tipc_link_dup_rcv(struct tipc_link *link,
- struct sk_buff *skb)
-{
- struct sk_buff *iskb;
- int pos = 0;
-
- if (!tipc_link_is_up(link))
- return;
-
- if (!tipc_msg_extract(skb, &iskb, &pos)) {
- pr_warn("%sfailed to extract inner dup pkt\n", link_co_err);
- return;
- }
- /* Append buffer to deferred queue, if applicable: */
- link_handle_out_of_seq_msg(link, iskb);
-}
-
-/* tipc_link_failover_rcv(): Receive a tunnelled ORIGINAL_MSG packet
+/* tipc_link_failover_rcv(): Receive a tunnelled FAILOVER_MSG packet
* Owner node is locked.
*/
-static struct sk_buff *tipc_link_failover_rcv(struct tipc_link *l_ptr,
- struct sk_buff *t_buf)
+static bool tipc_link_failover_rcv(struct tipc_link *link,
+ struct sk_buff **skb)
{
- struct tipc_msg *t_msg = buf_msg(t_buf);
- struct sk_buff *buf = NULL;
- struct tipc_msg *msg;
+ struct tipc_msg *msg = buf_msg(*skb);
+ struct sk_buff *iskb = NULL;
+ struct tipc_link *pl = NULL;
+ int bearer_id = msg_bearer_id(msg);
int pos = 0;
- if (tipc_link_is_up(l_ptr))
- tipc_link_reset(l_ptr);
-
- /* First failover packet? */
- if (l_ptr->exp_msg_count == START_CHANGEOVER)
- l_ptr->exp_msg_count = msg_msgcnt(t_msg);
-
- /* Should there be an inner packet? */
- if (l_ptr->exp_msg_count) {
- l_ptr->exp_msg_count--;
- if (!tipc_msg_extract(t_buf, &buf, &pos)) {
- pr_warn("%sno inner failover pkt\n", link_co_err);
- goto exit;
- }
- msg = buf_msg(buf);
-
- if (less(msg_seqno(msg), l_ptr->reset_checkpoint)) {
- kfree_skb(buf);
- buf = NULL;
- goto exit;
- }
- if (msg_user(msg) == MSG_FRAGMENTER) {
- l_ptr->stats.recv_fragments++;
- tipc_buf_append(&l_ptr->reasm_buf, &buf);
- }
+ if (msg_type(msg) != FAILOVER_MSG) {
+ pr_warn("%sunknown tunnel pkt received\n", link_co_err);
+ goto exit;
}
-exit:
- if ((!l_ptr->exp_msg_count) && (l_ptr->flags & LINK_STOPPED))
- tipc_link_delete(l_ptr);
- return buf;
-}
+ if (bearer_id >= MAX_BEARERS)
+ goto exit;
-/* tipc_link_tunnel_rcv(): Receive a tunnelled packet, sent
- * via other link as result of a failover (ORIGINAL_MSG) or
- * a new active link (DUPLICATE_MSG). Failover packets are
- * returned to the active link for delivery upwards.
- * Owner node is locked.
- */
-static int tipc_link_tunnel_rcv(struct tipc_node *n_ptr,
- struct sk_buff **buf)
-{
- struct sk_buff *t_buf = *buf;
- struct tipc_link *l_ptr;
- struct tipc_msg *t_msg = buf_msg(t_buf);
- u32 bearer_id = msg_bearer_id(t_msg);
+ if (bearer_id == link->bearer_id)
+ goto exit;
- *buf = NULL;
+ pl = link->owner->links[bearer_id];
+ if (pl && tipc_link_is_up(pl))
+ tipc_link_reset(pl);
- if (bearer_id >= MAX_BEARERS)
+ if (link->failover_pkts == FIRST_FAILOVER)
+ link->failover_pkts = msg_msgcnt(msg);
+
+ /* Should we expect an inner packet? */
+ if (!link->failover_pkts)
goto exit;
- l_ptr = n_ptr->links[bearer_id];
- if (!l_ptr)
+ if (!tipc_msg_extract(*skb, &iskb, &pos)) {
+ pr_warn("%sno inner failover pkt\n", link_co_err);
+ *skb = NULL;
goto exit;
+ }
+ link->failover_pkts--;
+ *skb = NULL;
- if (msg_type(t_msg) == DUPLICATE_MSG)
- tipc_link_dup_rcv(l_ptr, t_buf);
- else if (msg_type(t_msg) == ORIGINAL_MSG)
- *buf = tipc_link_failover_rcv(l_ptr, t_buf);
- else
- pr_warn("%sunknown tunnel pkt received\n", link_co_err);
+ /* Was this packet already delivered? */
+ if (less(buf_seqno(iskb), link->failover_checkpt)) {
+ kfree_skb(iskb);
+ iskb = NULL;
+ goto exit;
+ }
+ if (msg_user(buf_msg(iskb)) == MSG_FRAGMENTER) {
+ link->stats.recv_fragments++;
+ tipc_buf_append(&link->failover_skb, &iskb);
+ }
exit:
- kfree_skb(t_buf);
- return *buf != NULL;
+ if (!link->failover_pkts && pl)
+ pl->flags &= ~LINK_FAILINGOVER;
+ kfree_skb(*skb);
+ *skb = iskb;
+ return *skb;
}
static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tol)
void tipc_link_set_queue_limits(struct tipc_link *l, u32 win)
{
- int max_bulk = TIPC_MAX_PUBLICATIONS / (l->max_pkt / ITEM_SIZE);
+ int max_bulk = TIPC_MAX_PUBLICATIONS / (l->mtu / ITEM_SIZE);
l->window = win;
l->backlog[TIPC_LOW_IMPORTANCE].limit = win / 2;
tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
link_set_supervision_props(link, tol);
- tipc_link_proto_xmit(link, STATE_MSG, 0, 0, tol, 0, 0);
+ tipc_link_proto_xmit(link, STATE_MSG, 0, 0, tol, 0);
}
if (props[TIPC_NLA_PROP_PRIO]) {
u32 prio;
prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
link->priority = prio;
- tipc_link_proto_xmit(link, STATE_MSG, 0, 0, 0, prio, 0);
+ tipc_link_proto_xmit(link, STATE_MSG, 0, 0, 0, prio);
}
if (props[TIPC_NLA_PROP_WIN]) {
u32 win;
if (nla_put_u32(msg->skb, TIPC_NLA_LINK_DEST,
tipc_cluster_mask(tn->own_addr)))
goto attr_msg_full;
- if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->max_pkt))
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->mtu))
goto attr_msg_full;
if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, link->next_in_no))
goto attr_msg_full;
/* Link endpoint execution states
*/
-#define LINK_STARTED 0x0001
-#define LINK_STOPPED 0x0002
-#define LINK_SYNCHING 0x0004
+#define LINK_STARTED 0x0001
+#define LINK_STOPPED 0x0002
+#define LINK_SYNCHING 0x0004
+#define LINK_FAILINGOVER 0x0008
/* Starting value for maximum packet size negotiation on unicast links
* (unless bearer MTU is less)
* @backlog_limit: backlog queue congestion thresholds (indexed by importance)
* @exp_msg_count: # of tunnelled messages expected during link changeover
* @reset_checkpoint: seq # of last acknowledged message at time of link reset
- * @max_pkt: current maximum packet size for this link
- * @max_pkt_target: desired maximum packet size for this link
- * @max_pkt_probes: # of probes based on current (max_pkt, max_pkt_target)
+ * @mtu: current maximum packet size for this link
+ * @advertised_mtu: advertised own mtu when link is being established
* @transmitq: queue for sent, non-acked messages
* @backlogq: queue for messages waiting to be sent
* @next_out_no: next sequence number to use for outbound messages
struct tipc_msg *pmsg;
u32 priority;
char net_plane;
+ u16 synch_point;
- /* Changeover */
- u32 exp_msg_count;
- u32 reset_checkpoint;
- u32 synch_point;
+ /* Failover */
+ u16 failover_pkts;
+ u16 failover_checkpt;
+ struct sk_buff *failover_skb;
/* Max packet negotiation */
- u32 max_pkt;
- u32 max_pkt_target;
- u32 max_pkt_probes;
+ u16 mtu;
+ u16 advertised_mtu;
/* Sending */
struct sk_buff_head transmq;
struct sk_buff_head wakeupq;
/* Fragmentation/reassembly */
- u32 long_msg_seq_no;
struct sk_buff *reasm_buf;
/* Statistics */
int __tipc_link_xmit(struct net *net, struct tipc_link *link,
struct sk_buff_head *list);
void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob,
- u32 gap, u32 tolerance, u32 priority, u32 acked_mtu);
+ u32 gap, u32 tolerance, u32 priority);
void tipc_link_push_packets(struct tipc_link *l_ptr);
u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *buf);
void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window);
start = align(bsz);
pad = start - bsz;
- if (unlikely(msg_user(msg) == CHANGEOVER_PROTOCOL))
+ if (unlikely(msg_user(msg) == TUNNEL_PROTOCOL))
return false;
if (unlikely(msg_user(msg) == BCAST_PROTOCOL))
return false;
if (msg_user(msg) == MSG_FRAGMENTER)
return false;
- if (msg_user(msg) == CHANGEOVER_PROTOCOL)
+ if (msg_user(msg) == TUNNEL_PROTOCOL)
return false;
if (msg_user(msg) == BCAST_PROTOCOL)
return false;
#define MSG_BUNDLER 6
#define LINK_PROTOCOL 7
#define CONN_MANAGER 8
-#define CHANGEOVER_PROTOCOL 10
+#define TUNNEL_PROTOCOL 10
#define NAME_DISTRIBUTOR 11
#define MSG_FRAGMENTER 12
#define LINK_CONFIG 13
/*
* Changeover tunnel message types
*/
-#define DUPLICATE_MSG 0
-#define ORIGINAL_MSG 1
+#define SYNCH_MSG 0
+#define FAILOVER_MSG 1
/*
* Config protocol message types
static inline bool msg_dup(struct tipc_msg *m)
{
- if (likely(msg_user(m) != CHANGEOVER_PROTOCOL))
+ if (likely(msg_user(m) != TUNNEL_PROTOCOL))
return false;
- if (msg_type(m) != DUPLICATE_MSG)
+ if (msg_type(m) != SYNCH_MSG)
return false;
return true;
}
active[0] = active[1] = l_ptr;
exit:
/* Leave room for changeover header when returning 'mtu' to users: */
- n_ptr->act_mtus[0] = active[0]->max_pkt - INT_H_SIZE;
- n_ptr->act_mtus[1] = active[1]->max_pkt - INT_H_SIZE;
+ n_ptr->act_mtus[0] = active[0]->mtu - INT_H_SIZE;
+ n_ptr->act_mtus[1] = active[1]->mtu - INT_H_SIZE;
}
/**
/* Leave room for changeover header when returning 'mtu' to users: */
if (active[0]) {
- n_ptr->act_mtus[0] = active[0]->max_pkt - INT_H_SIZE;
- n_ptr->act_mtus[1] = active[1]->max_pkt - INT_H_SIZE;
+ n_ptr->act_mtus[0] = active[0]->mtu - INT_H_SIZE;
+ n_ptr->act_mtus[1] = active[1]->mtu - INT_H_SIZE;
return;
}
-
/* Loopback link went down? No fragmentation needed from now on. */
if (n_ptr->addr == tn->own_addr) {
n_ptr->act_mtus[0] = MAX_MSG_SIZE;
n_ptr->bclink.recv_permitted = false;
}
- /* Abort link changeover */
+ /* Abort any ongoing link failover */
for (i = 0; i < MAX_BEARERS; i++) {
struct tipc_link *l_ptr = n_ptr->links[i];
if (!l_ptr)
continue;
- l_ptr->reset_checkpoint = l_ptr->next_in_no;
- l_ptr->exp_msg_count = 0;
+ l_ptr->flags &= ~LINK_FAILINGOVER;
+ l_ptr->failover_checkpt = 0;
+ l_ptr->failover_pkts = 0;
+ kfree_skb(l_ptr->failover_skb);
+ l_ptr->failover_skb = NULL;
tipc_link_reset_fragments(l_ptr);
-
- /* Link marked for deletion after failover? => do it now */
- if (l_ptr->flags & LINK_STOPPED)
- tipc_link_delete(l_ptr);
}
n_ptr->action_flags &= ~TIPC_WAIT_OWN_LINKS_DOWN;
goto tx_error;
}
ttl = ip4_dst_hoplimit(&rt->dst);
- err = udp_tunnel_xmit_skb(rt, clone, src->ipv4.s_addr,
+ err = udp_tunnel_xmit_skb(rt, ub->ubsock->sk, clone,
+ src->ipv4.s_addr,
dst->ipv4.s_addr, 0, ttl, 0,
src->udp_port, dst->udp_port,
false, true);
if (err)
goto tx_error;
ttl = ip6_dst_hoplimit(ndst);
- err = udp_tunnel6_xmit_skb(ndst, clone, ndst->dev, &src->ipv6,
+ err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, clone,
+ ndst->dev, &src->ipv6,
&dst->ipv6, 0, ttl, src->udp_port,
dst->udp_port, false);
#endif
#include <net/dst.h>
#include <net/xfrm.h>
-static int xfrm_output2(struct sk_buff *skb);
+static int xfrm_output2(struct sock *sk, struct sk_buff *skb);
static int xfrm_skb_check_space(struct sk_buff *skb)
{
return dst_output(skb);
err = nf_hook(skb_dst(skb)->ops->family,
- NF_INET_POST_ROUTING, skb,
+ NF_INET_POST_ROUTING, skb->sk, skb,
NULL, skb_dst(skb)->dev, xfrm_output2);
if (unlikely(err != 1))
goto out;
}
EXPORT_SYMBOL_GPL(xfrm_output_resume);
-static int xfrm_output2(struct sk_buff *skb)
+static int xfrm_output2(struct sock *sk, struct sk_buff *skb)
{
return xfrm_output_resume(skb, 1);
}
-static int xfrm_output_gso(struct sk_buff *skb)
+static int xfrm_output_gso(struct sock *sk, struct sk_buff *skb)
{
struct sk_buff *segs;
int err;
segs->next = NULL;
- err = xfrm_output2(segs);
+ err = xfrm_output2(sk, segs);
if (unlikely(err)) {
kfree_skb_list(nskb);
return 0;
}
-int xfrm_output(struct sk_buff *skb)
+int xfrm_output(struct sock *sk, struct sk_buff *skb)
{
struct net *net = dev_net(skb_dst(skb)->dev);
int err;
if (skb_is_gso(skb))
- return xfrm_output_gso(skb);
+ return xfrm_output_gso(sk, skb);
if (skb->ip_summed == CHECKSUM_PARTIAL) {
err = skb_checksum_help(skb);
}
}
- return xfrm_output2(skb);
+ return xfrm_output2(sk, skb);
}
EXPORT_SYMBOL_GPL(xfrm_output);
const struct xfrm_link *link;
int type, err;
+#ifdef CONFIG_COMPAT
+ if (is_compat_task())
+ return -ENOTSUPP;
+#endif
+
type = nlh->nlmsg_type;
if (type > XFRM_MSG_MAX)
return -EINVAL;
always := $(hostprogs-y)
always += sockex1_kern.o
always += sockex2_kern.o
+always += tcbpf1_kern.o
HOSTCFLAGS += -I$(objtree)/usr/include
unsigned int max_entries;
};
+static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) =
+ (void *) BPF_FUNC_skb_store_bytes;
+static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) =
+ (void *) BPF_FUNC_l3_csum_replace;
+static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) =
+ (void *) BPF_FUNC_l4_csum_replace;
+
#endif
--- /dev/null
+#include <uapi/linux/bpf.h>
+#include <uapi/linux/if_ether.h>
+#include <uapi/linux/if_packet.h>
+#include <uapi/linux/ip.h>
+#include <uapi/linux/in.h>
+#include <uapi/linux/tcp.h>
+#include "bpf_helpers.h"
+
+/* compiler workaround */
+#define _htonl __builtin_bswap32
+
+static inline void set_dst_mac(struct __sk_buff *skb, char *mac)
+{
+ bpf_skb_store_bytes(skb, 0, mac, ETH_ALEN, 1);
+}
+
+/* use 1 below for ingress qdisc and 0 for egress */
+#if 0
+#undef ETH_HLEN
+#define ETH_HLEN 0
+#endif
+
+#define IP_CSUM_OFF (ETH_HLEN + offsetof(struct iphdr, check))
+#define TOS_OFF (ETH_HLEN + offsetof(struct iphdr, tos))
+
+static inline void set_ip_tos(struct __sk_buff *skb, __u8 new_tos)
+{
+ __u8 old_tos = load_byte(skb, TOS_OFF);
+
+ bpf_l3_csum_replace(skb, IP_CSUM_OFF, htons(old_tos), htons(new_tos), 2);
+ bpf_skb_store_bytes(skb, TOS_OFF, &new_tos, sizeof(new_tos), 0);
+}
+
+#define TCP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, check))
+#define IP_SRC_OFF (ETH_HLEN + offsetof(struct iphdr, saddr))
+
+#define IS_PSEUDO 0x10
+
+static inline void set_tcp_ip_src(struct __sk_buff *skb, __u32 new_ip)
+{
+ __u32 old_ip = _htonl(load_word(skb, IP_SRC_OFF));
+
+ bpf_l4_csum_replace(skb, TCP_CSUM_OFF, old_ip, new_ip, IS_PSEUDO | sizeof(new_ip));
+ bpf_l3_csum_replace(skb, IP_CSUM_OFF, old_ip, new_ip, sizeof(new_ip));
+ bpf_skb_store_bytes(skb, IP_SRC_OFF, &new_ip, sizeof(new_ip), 0);
+}
+
+#define TCP_DPORT_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct tcphdr, dest))
+static inline void set_tcp_dest_port(struct __sk_buff *skb, __u16 new_port)
+{
+ __u16 old_port = htons(load_half(skb, TCP_DPORT_OFF));
+
+ bpf_l4_csum_replace(skb, TCP_CSUM_OFF, old_port, new_port, sizeof(new_port));
+ bpf_skb_store_bytes(skb, TCP_DPORT_OFF, &new_port, sizeof(new_port), 0);
+}
+
+SEC("classifier")
+int bpf_prog1(struct __sk_buff *skb)
+{
+ __u8 proto = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol));
+ long *value;
+
+ if (proto == IPPROTO_TCP) {
+ set_ip_tos(skb, 8);
+ set_tcp_ip_src(skb, 0xA010101);
+ set_tcp_dest_port(skb, 5001);
+ }
+
+ return 0;
+}
+char _license[] SEC("license") = "GPL";
static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return selinux_ip_forward(skb, in, PF_INET);
+ return selinux_ip_forward(skb, state->in, PF_INET);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return selinux_ip_forward(skb, in, PF_INET6);
+ return selinux_ip_forward(skb, state->in, PF_INET6);
}
#endif /* IPV6 */
static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
return selinux_ip_output(skb, PF_INET);
}
static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return selinux_ip_postroute(skb, out, PF_INET);
+ return selinux_ip_postroute(skb, state->out, PF_INET);
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
- return selinux_ip_postroute(skb, out, PF_INET6);
+ return selinux_ip_postroute(skb, state->out, PF_INET6);
}
#endif /* IPV6 */
{ RTM_NEWMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_DELMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
{ RTM_GETMDB, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWNSID, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ },
};
static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
{ XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
{ XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_NEWSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_GETSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_NEWSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
};
static struct nlmsg_perm nlmsg_audit_perms[] =
goto out;
/* No partial writes. */
- length = EINVAL;
+ length = -EINVAL;
if (*ppos != 0)
goto out;
static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct socket_smack *ssp;
struct smack_known *skp;
static unsigned int smack_ipv4_output(const struct nf_hook_ops *ops,
struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct nf_hook_state *state)
{
struct socket_smack *ssp;
struct smack_known *skp;
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Sunrise Point */
{ PCI_DEVICE(0x8086, 0xa170),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
/* Sunrise Point-LP */
{ PCI_DEVICE(0x8086, 0x9d70),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
{
/* We currently only handle front, HP */
static hda_nid_t pins[] = {
- 0x0f, 0x10, 0x14, 0x15, 0
+ 0x0f, 0x10, 0x14, 0x15, 0x17, 0
};
hda_nid_t *p;
for (p = pins; *p; p++)
SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
TARGETS_HOTPLUG = cpu-hotplug
TARGETS_HOTPLUG += memory-hotplug
+# Clear LDFLAGS and MAKEFLAGS if called from main
+# Makefile to avoid test build failures when test
+# Makefile doesn't have explicit build rules.
+ifeq (1,$(MAKELEVEL))
+undefine LDFLAGS
+override MAKEFLAGS =
+endif
+
all:
for TARGET in $(TARGETS); do \
make -C $$TARGET; \
BUILD_BUG_ON(KVM_MEM_SLOTS_NUM > SHRT_MAX);
r = -ENOMEM;
- kvm->memslots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
+ kvm->memslots = kvm_kvzalloc(sizeof(struct kvm_memslots));
if (!kvm->memslots)
goto out_err_no_srcu;
out_err_no_disable:
for (i = 0; i < KVM_NR_BUSES; i++)
kfree(kvm->buses[i]);
- kfree(kvm->memslots);
+ kvfree(kvm->memslots);
kvm_arch_free_vm(kvm);
return ERR_PTR(r);
}
kvm_for_each_memslot(memslot, slots)
kvm_free_physmem_slot(kvm, memslot, NULL);
- kfree(kvm->memslots);
+ kvfree(kvm->memslots);
}
static void kvm_destroy_devices(struct kvm *kvm)
goto out_free;
}
- slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots),
- GFP_KERNEL);
+ slots = kvm_kvzalloc(sizeof(struct kvm_memslots));
if (!slots)
goto out_free;
+ memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
if ((change == KVM_MR_DELETE) || (change == KVM_MR_MOVE)) {
slot = id_to_memslot(slots, mem->slot);
kvm_arch_commit_memory_region(kvm, mem, &old, change);
kvm_free_physmem_slot(kvm, &old, &new);
- kfree(old_memslots);
+ kvfree(old_memslots);
/*
* IOMMU mapping: New slots need to be mapped. Old slots need to be
return 0;
out_slots:
- kfree(slots);
+ kvfree(slots);
out_free:
kvm_free_physmem_slot(kvm, &new, &old);
out: