* [PATCH 4/5] myri10ge - Driver core
From: Brice Goglin @ 2006-05-23 7:12 UTC (permalink / raw)
To: netdev; +Cc: gallatin, linux-kernel
In-Reply-To: <20060523070919.GB30499@myri.com>
[PATCH 4/5] myri10ge - Driver core
The core of the myri10ge driver.
Signed-off-by: Brice Goglin <brice@myri.com>
Signed-off-by: Andrew J. Gallatin <gallatin@myri.com>
drivers/net/myri10ge/myri10ge.c | 2851 +++++++++++++++++++++++++++++
1 file changed, 2851 insertions(+)
--- /dev/null 2006-05-16 20:08:50.920483500 +0200
+++ linux-tmp/drivers/net/myri10ge/myri10ge.c 2006-05-22 23:16:35.000000000 +0200
@@ -0,0 +1,2851 @@
+/*************************************************************************
+ * myri10ge.c: Myricom Myri-10G Ethernet driver.
+ *
+ * Copyright (C) 2005, 2006 Myricom, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of Myricom, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * If the eeprom on your board is not recent enough, you will need to get a
+ * newer firmware image at:
+ * http://www.myri.com/scs/download-Myri10GE.html
+ *
+ * Contact Information:
+ * <help@myri.com>
+ * Myricom, Inc., 325N Santa Anita Avenue, Arcadia, CA 91006
+ *************************************************************************/
+
+#include <linux/tcp.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/inet.h>
+#include <linux/in.h>
+#include <linux/ethtool.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/timer.h>
+#include <linux/vmalloc.h>
+#include <linux/crc32.h>
+#include <linux/moduleparam.h>
+#include <linux/io.h>
+#include <net/checksum.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/processor.h>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include "myri10ge_mcp.h"
+#include "myri10ge_mcp_gen_header.h"
+
+#define MYRI10GE_VERSION_STR "0.9.0"
+
+MODULE_DESCRIPTION("Myricom 10G driver (10GbE)");
+MODULE_AUTHOR("Maintainer: help@myri.com");
+MODULE_VERSION(MYRI10GE_VERSION_STR);
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define MYRI10GE_MAX_ETHER_MTU 9014
+
+#define MYRI10GE_ETH_STOPPED 0
+#define MYRI10GE_ETH_STOPPING 1
+#define MYRI10GE_ETH_STARTING 2
+#define MYRI10GE_ETH_RUNNING 3
+#define MYRI10GE_ETH_OPEN_FAILED 4
+
+#define MYRI10GE_EEPROM_STRINGS_SIZE 256
+#define MYRI10GE_MAX_SEND_DESC_TSO ((65536 / 2048) * 2)
+
+#define MYRI10GE_NO_CONFIRM_DATA 0xffffffff
+#define MYRI10GE_NO_RESPONSE_RESULT 0xffffffff
+
+struct myri10ge_rx_buffer_state {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(bus)
+ DECLARE_PCI_UNMAP_LEN(len)
+};
+
+struct myri10ge_tx_buffer_state {
+ struct sk_buff *skb;
+ int last;
+ DECLARE_PCI_UNMAP_ADDR(bus)
+ DECLARE_PCI_UNMAP_LEN(len)
+};
+
+struct myri10ge_cmd {
+ u32 data0;
+ u32 data1;
+ u32 data2;
+};
+
+struct myri10ge_rx_buf {
+ struct mcp_kreq_ether_recv __iomem *lanai; /* lanai ptr for recv ring */
+ u8 __iomem *wc_fifo; /* w/c rx dma addr fifo address */
+ struct mcp_kreq_ether_recv *shadow; /* host shadow of recv ring */
+ struct myri10ge_rx_buffer_state *info;
+ int cnt;
+ int alloc_fail;
+ int mask; /* number of rx slots -1 */
+};
+
+struct myri10ge_tx_buf {
+ struct mcp_kreq_ether_send __iomem *lanai; /* lanai ptr for sendq */
+ u8 __iomem *wc_fifo; /* w/c send fifo address */
+ struct mcp_kreq_ether_send *req_list; /* host shadow of sendq */
+ char *req_bytes;
+ struct myri10ge_tx_buffer_state *info;
+ int mask; /* number of transmit slots -1 */
+ int boundary; /* boundary transmits cannot cross */
+ int req ____cacheline_aligned; /* transmit slots submitted */
+ int pkt_start; /* packets started */
+ int done ____cacheline_aligned; /* transmit slots completed */
+ int pkt_done; /* packets completed */
+};
+
+struct myri10ge_rx_done {
+ struct mcp_slot *entry;
+ dma_addr_t bus;
+ int cnt;
+ int idx;
+};
+
+struct myri10ge_priv {
+ int running; /* running? */
+ int csum_flag; /* rx_csums? */
+ struct myri10ge_tx_buf tx; /* transmit ring */
+ struct myri10ge_rx_buf rx_small;
+ struct myri10ge_rx_buf rx_big;
+ struct myri10ge_rx_done rx_done;
+ int small_bytes;
+ struct net_device *dev;
+ struct net_device_stats stats;
+ u8 __iomem *sram;
+ int sram_size;
+ unsigned long board_span;
+ unsigned long iomem_base;
+ u32 __iomem *irq_claim;
+ u32 __iomem *irq_deassert;
+ char *mac_addr_string;
+ struct mcp_cmd_response *cmd;
+ dma_addr_t cmd_bus;
+ struct mcp_irq_data *fw_stats;
+ dma_addr_t fw_stats_bus;
+ struct pci_dev *pdev;
+ int msi_enabled;
+ unsigned int link_state;
+ unsigned int rdma_tags_available;
+ int intr_coal_delay;
+ u32 __iomem *intr_coal_delay_ptr;
+ int mtrr;
+ int wake_queue;
+ int stop_queue;
+ int down_cnt;
+ wait_queue_head_t down_wq;
+ struct work_struct watchdog_work;
+ struct timer_list watchdog_timer;
+ int watchdog_tx_done;
+ int watchdog_resets;
+ int tx_linearized;
+ int pause;
+ char *fw_name;
+ char eeprom_strings[MYRI10GE_EEPROM_STRINGS_SIZE];
+ char fw_version[128];
+ u8 mac_addr[6]; /* eeprom mac address */
+ unsigned long serial_number;
+ int vendor_specific_offset;
+ u32 devctl;
+ u16 msi_flags;
+ u32 pm_state[16];
+ u32 read_dma;
+ u32 write_dma;
+ u32 read_write_dma;
+};
+
+static char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e.dat";
+static char *myri10ge_fw_aligned = "myri10ge_eth_z8e.dat";
+
+static char *myri10ge_fw_name = NULL;
+module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name\n");
+
+static int myri10ge_ecrc_enable = 1;
+module_param(myri10ge_ecrc_enable, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_ecrc_enable, "Enable Extended CRC on PCI-E\n");
+
+static int myri10ge_max_intr_slots = 1024;
+module_param(myri10ge_max_intr_slots, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_max_intr_slots, "Interrupt queue slots\n");
+
+static int myri10ge_small_bytes = -1; /* -1 == auto */
+module_param(myri10ge_small_bytes, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(myri10ge_small_bytes, "Threshold of small packets\n");
+
+static int myri10ge_msi = 1; /* enable msi by default */
+module_param(myri10ge_msi, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_msi, "Enable Message Signalled Interrupts\n");
+
+static int myri10ge_intr_coal_delay = 25;
+module_param(myri10ge_intr_coal_delay, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_intr_coal_delay, "Interrupt coalescing delay\n");
+
+static int myri10ge_flow_control = 1;
+module_param(myri10ge_flow_control, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_flow_control, "Pause parameter\n");
+
+static int myri10ge_deassert_wait = 1;
+module_param(myri10ge_deassert_wait, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(myri10ge_deassert_wait,
+ "Wait when deasserting legacy interrupts\n");
+
+static int myri10ge_force_firmware = 0;
+module_param(myri10ge_force_firmware, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_force_firmware,
+ "Force firmware to assume aligned completions\n");
+
+static int myri10ge_skb_cross_4k = 0;
+module_param(myri10ge_skb_cross_4k, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(myri10ge_skb_cross_4k,
+ "Can a small skb cross a 4KB boundary?\n");
+
+static int myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
+module_param(myri10ge_initial_mtu, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_initial_mtu, "Initial MTU\n");
+
+static int myri10ge_napi_weight = 64;
+module_param(myri10ge_napi_weight, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_napi_weight, "Set NAPI weight\n");
+
+static int myri10ge_watchdog_timeout = 1;
+module_param(myri10ge_watchdog_timeout, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_watchdog_timeout, "Set watchdog timeout\n");
+
+static int myri10ge_max_irq_loops = 1048576;
+module_param(myri10ge_max_irq_loops, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_max_irq_loops,
+ "Set stuck legacy IRQ detection threshold\n");
+
+#define MYRI10GE_FW_OFFSET 1024*1024
+#define MYRI10GE_HIGHPART_TO_U32(X) \
+(sizeof (X) == 8) ? ((u32)((u64)(X) >> 32)) : (0)
+#define MYRI10GE_LOWPART_TO_U32(X) ((u32)(X))
+
+#define myri10ge_pio_copy(to,from,size) __iowrite64_copy(to,from,size/8)
+
+static int
+myri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd,
+ struct myri10ge_cmd *data, int atomic)
+{
+ struct mcp_cmd *buf;
+ char buf_bytes[sizeof(*buf) + 8];
+ struct mcp_cmd_response *response = mgp->cmd;
+ char __iomem *cmd_addr = mgp->sram + MXGEFW_CMD_OFFSET;
+ u32 dma_low, dma_high, result, value;
+ int sleep_total = 0;
+
+ /* ensure buf is aligned to 8 bytes */
+ buf = (struct mcp_cmd *)ALIGN((unsigned long)buf_bytes, 8);
+
+ buf->data0 = htonl(data->data0);
+ buf->data1 = htonl(data->data1);
+ buf->data2 = htonl(data->data2);
+ buf->cmd = htonl(cmd);
+ dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
+ dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);
+
+ buf->response_addr.low = htonl(dma_low);
+ buf->response_addr.high = htonl(dma_high);
+ response->result = MYRI10GE_NO_RESPONSE_RESULT;
+ mb();
+ myri10ge_pio_copy(cmd_addr, buf, sizeof(*buf));
+
+ /* wait up to 15ms. Longest command is the DMA benchmark,
+ * which is capped at 5ms, but runs from a timeout handler
+ * that runs every 7.8ms. So a 15ms timeout leaves us with
+ * a 2.2ms margin
+ */
+ if (atomic) {
+ /* if atomic is set, do not sleep,
+ * and try to get the completion quickly
+ * (1ms will be enough for those commands) */
+ for (sleep_total = 0;
+ sleep_total < 1000
+ && response->result == MYRI10GE_NO_RESPONSE_RESULT;
+ sleep_total += 10)
+ udelay(10);
+ } else {
+ /* use msleep for most command */
+ for (sleep_total = 0;
+ sleep_total < 15
+ && response->result == MYRI10GE_NO_RESPONSE_RESULT;
+ sleep_total++)
+ msleep(1);
+ }
+
+ result = ntohl(response->result);
+ value = ntohl(response->data);
+ if (result != MYRI10GE_NO_RESPONSE_RESULT) {
+ if (result == 0) {
+ data->data0 = value;
+ return 0;
+ } else {
+ dev_err(&mgp->pdev->dev,
+ "command %d failed, result = %d\n",
+ cmd, result);
+ return -ENXIO;
+ }
+ }
+
+ dev_err(&mgp->pdev->dev, "command %d timed out, result = %d\n",
+ cmd, result);
+ return -EAGAIN;
+}
+
+/*
+ * The eeprom strings on the lanaiX have the format
+ * SN=x\0
+ * MAC=x:x:x:x:x:x\0
+ * PT:ddd mmm xx xx:xx:xx xx\0
+ * PV:ddd mmm xx xx:xx:xx xx\0
+ */
+static int myri10ge_read_mac_addr(struct myri10ge_priv *mgp)
+{
+ char *ptr, *limit;
+ int i;
+
+ ptr = mgp->eeprom_strings;
+ limit = mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE;
+
+ while (*ptr != '\0' && ptr < limit) {
+ if (memcmp(ptr, "MAC=", 4) == 0) {
+ ptr += 4;
+ mgp->mac_addr_string = ptr;
+ for (i = 0; i < 6; i++) {
+ if ((ptr + 2) > limit)
+ goto abort;
+ mgp->mac_addr[i] =
+ simple_strtoul(ptr, &ptr, 16);
+ ptr += 1;
+ }
+ }
+ if (memcmp((const void *)ptr, "SN=", 3) == 0) {
+ ptr += 3;
+ mgp->serial_number = simple_strtoul(ptr, &ptr, 10);
+ }
+ while (ptr < limit && *ptr++) ;
+ }
+
+ return 0;
+
+abort:
+ dev_err(&mgp->pdev->dev, "failed to parse eeprom_strings\n");
+ return -ENXIO;
+}
+
+/*
+ * Enable or disable periodic RDMAs from the host to make certain
+ * chipsets resend dropped PCIe messages
+ */
+
+static void myri10ge_dummy_rdma(struct myri10ge_priv *mgp, int enable)
+{
+ char __iomem *submit;
+ u32 buf[16];
+ u32 dma_low, dma_high;
+ int i;
+
+ /* clear confirmation addr */
+ mgp->cmd->data = 0;
+ mb();
+
+ /* send a rdma command to the PCIe engine, and wait for the
+ * response in the confirmation address. The firmware should
+ * write a -1 there to indicate it is alive and well
+ */
+ dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
+ dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);
+
+ buf[0] = htonl(dma_high); /* confirm addr MSW */
+ buf[1] = htonl(dma_low); /* confirm addr LSW */
+ buf[2] = htonl(MYRI10GE_NO_CONFIRM_DATA); /* confirm data */
+ buf[3] = htonl(dma_high); /* dummy addr MSW */
+ buf[4] = htonl(dma_low); /* dummy addr LSW */
+ buf[5] = htonl(enable); /* enable? */
+
+ submit = mgp->sram + 0xfc01c0;
+
+ myri10ge_pio_copy(submit, &buf, sizeof(buf));
+ for (i = 0; mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 20; i++)
+ msleep(1);
+ if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA)
+ dev_err(&mgp->pdev->dev, "dummy rdma %s failed\n",
+ (enable ? "enable" : "disable"));
+}
+
+static int
+myri10ge_validate_firmware(struct myri10ge_priv *mgp,
+ struct mcp_gen_header *hdr)
+{
+ struct device *dev = &mgp->pdev->dev;
+ int major, minor;
+
+ /* check firmware type */
+ if (ntohl(hdr->mcp_type) != MCP_TYPE_ETH) {
+ dev_err(dev, "Bad firmware type: 0x%x\n", ntohl(hdr->mcp_type));
+ return -EINVAL;
+ }
+
+ /* save firmware version for ethtool */
+ strncpy(mgp->fw_version, hdr->version, sizeof(mgp->fw_version));
+
+ sscanf(mgp->fw_version, "%d.%d", &major, &minor);
+
+ if (!(major == MXGEFW_VERSION_MAJOR && minor == MXGEFW_VERSION_MINOR)) {
+ dev_err(dev, "Found firmware version %s\n", mgp->fw_version);
+ dev_err(dev, "Driver needs %d.%d\n", MXGEFW_VERSION_MAJOR,
+ MXGEFW_VERSION_MINOR);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int myri10ge_load_hotplug_firmware(struct myri10ge_priv *mgp, u32 * size)
+{
+ unsigned crc, reread_crc;
+ const struct firmware *fw;
+ struct device *dev = &mgp->pdev->dev;
+ struct mcp_gen_header *hdr;
+ size_t hdr_offset;
+ int status;
+
+ if ((status = request_firmware(&fw, mgp->fw_name, dev)) < 0) {
+ dev_err(dev, "Unable to load %s firmware image via hotplug\n",
+ mgp->fw_name);
+ status = -EINVAL;
+ goto abort_with_nothing;
+ }
+
+ /* check size */
+
+ if (fw->size >= mgp->sram_size - MYRI10GE_FW_OFFSET ||
+ fw->size < MCP_HEADER_PTR_OFFSET + 4) {
+ dev_err(dev, "Firmware size invalid:%d\n", (int)fw->size);
+ status = -EINVAL;
+ goto abort_with_fw;
+ }
+
+ /* check id */
+ hdr_offset = ntohl(*(u32 *) (fw->data + MCP_HEADER_PTR_OFFSET));
+ if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->size) {
+ dev_err(dev, "Bad firmware file\n");
+ status = -EINVAL;
+ goto abort_with_fw;
+ }
+ hdr = (void *)(fw->data + hdr_offset);
+
+ status = myri10ge_validate_firmware(mgp, hdr);
+ if (status != 0)
+ goto abort_with_fw;
+
+ crc = crc32(~0, fw->data, fw->size);
+ memcpy_toio(mgp->sram + MYRI10GE_FW_OFFSET, fw->data, fw->size);
+ /* corruption checking is good for parity recovery and buggy chipset */
+ memcpy_fromio(fw->data, mgp->sram + MYRI10GE_FW_OFFSET, fw->size);
+ reread_crc = crc32(~0, fw->data, fw->size);
+ if (crc != reread_crc) {
+ dev_err(dev, "CRC failed(fw-len=%u), got 0x%x (expect 0x%x)\n",
+ (unsigned)fw->size, reread_crc, crc);
+ status = -EIO;
+ goto abort_with_fw;
+ }
+ *size = (u32) fw->size;
+
+abort_with_fw:
+ release_firmware(fw);
+
+abort_with_nothing:
+ return status;
+}
+
+static int myri10ge_adopt_running_firmware(struct myri10ge_priv *mgp)
+{
+ struct mcp_gen_header *hdr;
+ struct device *dev = &mgp->pdev->dev;
+ const size_t bytes = sizeof(struct mcp_gen_header);
+ size_t hdr_offset;
+ int status;
+
+ /* find running firmware header */
+ hdr_offset = ntohl(__raw_readl(mgp->sram + MCP_HEADER_PTR_OFFSET));
+
+ if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > mgp->sram_size) {
+ dev_err(dev, "Running firmware has bad header offset (%d)\n",
+ (int)hdr_offset);
+ return -EIO;
+ }
+
+ /* copy header of running firmware from SRAM to host memory to
+ * validate firmware */
+ hdr = kmalloc(bytes, GFP_KERNEL);
+ if (hdr == NULL) {
+ dev_err(dev, "could not malloc firmware hdr\n");
+ return -ENOMEM;
+ }
+ memcpy_fromio(hdr, mgp->sram + hdr_offset, bytes);
+ status = myri10ge_validate_firmware(mgp, hdr);
+ kfree(hdr);
+ return status;
+}
+
+static int myri10ge_load_firmware(struct myri10ge_priv *mgp)
+{
+ char __iomem *submit;
+ u32 buf[16];
+ u32 dma_low, dma_high, size;
+ int status, i;
+
+ status = myri10ge_load_hotplug_firmware(mgp, &size);
+ if (status) {
+ dev_warn(&mgp->pdev->dev, "hotplug firmware loading failed\n");
+
+ /* Do not attempt to adopt firmware if there
+ * was a bad crc */
+ if (status == -EIO)
+ return status;
+
+ status = myri10ge_adopt_running_firmware(mgp);
+ if (status != 0) {
+ dev_err(&mgp->pdev->dev,
+ "failed to adopt running firmware\n");
+ return status;
+ }
+ dev_info(&mgp->pdev->dev,
+ "Successfully adopted running firmware\n");
+ if (mgp->tx.boundary == 4096) {
+ dev_warn(&mgp->pdev->dev,
+ "Using firmware currently running on NIC"
+ ". For optimal\n");
+ dev_warn(&mgp->pdev->dev,
+ "performance consider loading optimized "
+ "firmware\n");
+ dev_warn(&mgp->pdev->dev, "via hotplug\n");
+ }
+
+ mgp->fw_name = "adopted";
+ mgp->tx.boundary = 2048;
+ return status;
+ }
+
+ /* clear confirmation addr */
+ mgp->cmd->data = 0;
+ mb();
+
+ /* send a reload command to the bootstrap MCP, and wait for the
+ * response in the confirmation address. The firmware should
+ * write a -1 there to indicate it is alive and well
+ */
+ dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
+ dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);
+
+ buf[0] = htonl(dma_high); /* confirm addr MSW */
+ buf[1] = htonl(dma_low); /* confirm addr LSW */
+ buf[2] = htonl(MYRI10GE_NO_CONFIRM_DATA); /* confirm data */
+
+ /* FIX: All newest firmware should un-protect the bottom of
+ * the sram before handoff. However, the very first interfaces
+ * do not. Therefore the handoff copy must skip the first 8 bytes
+ */
+ buf[3] = htonl(MYRI10GE_FW_OFFSET + 8); /* where the code starts */
+ buf[4] = htonl(size - 8); /* length of code */
+ buf[5] = htonl(8); /* where to copy to */
+ buf[6] = htonl(0); /* where to jump to */
+
+ submit = mgp->sram + 0xfc0000;
+
+ myri10ge_pio_copy(submit, &buf, sizeof(buf));
+ mb();
+ msleep(1);
+ mb();
+ i = 0;
+ while (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 20) {
+ msleep(1);
+ i++;
+ }
+ if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA) {
+ dev_err(&mgp->pdev->dev, "handoff failed\n");
+ return -ENXIO;
+ }
+ dev_info(&mgp->pdev->dev, "handoff confirmed\n");
+ myri10ge_dummy_rdma(mgp, mgp->tx.boundary != 4096);
+
+ return 0;
+}
+
+static int myri10ge_update_mac_address(struct myri10ge_priv *mgp, u8 * addr)
+{
+ struct myri10ge_cmd cmd;
+ int status;
+
+ cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
+ | (addr[2] << 8) | addr[3]);
+
+ cmd.data1 = ((addr[4] << 8) | (addr[5]));
+
+ status = myri10ge_send_cmd(mgp, MXGEFW_SET_MAC_ADDRESS, &cmd, 0);
+ return status;
+}
+
+static int myri10ge_change_pause(struct myri10ge_priv *mgp, int pause)
+{
+ struct myri10ge_cmd cmd;
+ int status, ctl;
+
+ ctl = pause ? MXGEFW_ENABLE_FLOW_CONTROL : MXGEFW_DISABLE_FLOW_CONTROL;
+ status = myri10ge_send_cmd(mgp, ctl, &cmd, 0);
+
+ if (status) {
+ printk(KERN_ERR
+ "myri10ge: %s: Failed to set flow control mode\n",
+ mgp->dev->name);
+ return status;
+ }
+ mgp->pause = pause;
+ return 0;
+}
+
+static void
+myri10ge_change_promisc(struct myri10ge_priv *mgp, int promisc, int atomic)
+{
+ struct myri10ge_cmd cmd;
+ int status, ctl;
+
+ ctl = promisc ? MXGEFW_ENABLE_PROMISC : MXGEFW_DISABLE_PROMISC;
+ status = myri10ge_send_cmd(mgp, ctl, &cmd, atomic);
+ if (status)
+ printk(KERN_ERR "myri10ge: %s: Failed to set promisc mode\n",
+ mgp->dev->name);
+}
+
+static int myri10ge_reset(struct myri10ge_priv *mgp)
+{
+ struct myri10ge_cmd cmd;
+ int status;
+ size_t bytes;
+ u32 len;
+
+ /* try to send a reset command to the card to see if it
+ * is alive */
+ memset(&cmd, 0, sizeof(cmd));
+ status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd, 0);
+ if (status != 0) {
+ dev_err(&mgp->pdev->dev, "failed reset\n");
+ return -ENXIO;
+ }
+
+ /* Now exchange information about interrupts */
+
+ bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
+ memset(mgp->rx_done.entry, 0, bytes);
+ cmd.data0 = (u32) bytes;
+ status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd, 0);
+ cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus);
+ status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_DMA, &cmd, 0);
+
+ status |=
+ myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd, 0);
+ mgp->irq_claim = (__iomem u32 *) (mgp->sram + cmd.data0);
+ if (!mgp->msi_enabled) {
+ status |= myri10ge_send_cmd
+ (mgp, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd, 0);
+ mgp->irq_deassert = (__iomem u32 *) (mgp->sram + cmd.data0);
+
+ }
+ status |= myri10ge_send_cmd
+ (mgp, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd, 0);
+ mgp->intr_coal_delay_ptr = (__iomem u32 *) (mgp->sram + cmd.data0);
+ if (status != 0) {
+ dev_err(&mgp->pdev->dev, "failed set interrupt parameters\n");
+ return status;
+ }
+ __raw_writel(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr);
+
+ /* Run a small DMA test.
+ * The magic multipliers to the length tell the firmware
+ * to do DMA read, write, or read+write tests. The
+ * results are returned in cmd.data0. The upper 16
+ * bits or the return is the number of transfers completed.
+ * The lower 16 bits is the time in 0.5us ticks that the
+ * transfers took to complete.
+ */
+
+ len = mgp->tx.boundary;
+
+ cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus);
+ cmd.data2 = len * 0x10000;
+ status = myri10ge_send_cmd(mgp, MXGEFW_DMA_TEST, &cmd, 0);
+ if (status == 0)
+ mgp->read_dma = ((cmd.data0 >> 16) * len * 2) /
+ (cmd.data0 & 0xffff);
+ else
+ dev_warn(&mgp->pdev->dev, "DMA read benchmark failed: %d\n",
+ status);
+ cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus);
+ cmd.data2 = len * 0x1;
+ status = myri10ge_send_cmd(mgp, MXGEFW_DMA_TEST, &cmd, 0);
+ if (status == 0)
+ mgp->write_dma = ((cmd.data0 >> 16) * len * 2) /
+ (cmd.data0 & 0xffff);
+ else
+ dev_warn(&mgp->pdev->dev, "DMA write benchmark failed: %d\n",
+ status);
+
+ cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus);
+ cmd.data2 = len * 0x10001;
+ status = myri10ge_send_cmd(mgp, MXGEFW_DMA_TEST, &cmd, 0);
+ if (status == 0)
+ mgp->read_write_dma = ((cmd.data0 >> 16) * len * 2 * 2) /
+ (cmd.data0 & 0xffff);
+ else
+ dev_warn(&mgp->pdev->dev,
+ "DMA read/write benchmark failed: %d\n", status);
+
+ memset(mgp->rx_done.entry, 0, bytes);
+
+ /* reset mcp/driver shared state back to 0 */
+ mgp->tx.req = 0;
+ mgp->tx.done = 0;
+ mgp->tx.pkt_start = 0;
+ mgp->tx.pkt_done = 0;
+ mgp->rx_big.cnt = 0;
+ mgp->rx_small.cnt = 0;
+ mgp->rx_done.idx = 0;
+ mgp->rx_done.cnt = 0;
+ status = myri10ge_update_mac_address(mgp, mgp->dev->dev_addr);
+ myri10ge_change_promisc(mgp, 0, 0);
+ myri10ge_change_pause(mgp, mgp->pause);
+ return status;
+}
+
+static inline void
+myri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst,
+ struct mcp_kreq_ether_recv *src)
+{
+ u32 low;
+
+ low = src->addr_low;
+ src->addr_low = DMA_32BIT_MASK;
+ myri10ge_pio_copy(dst, src, 8 * sizeof(*src));
+ mb();
+ src->addr_low = low;
+ __raw_writel(low, &dst->addr_low);
+ mb();
+}
+
+/*
+ * Set of routunes to get a new receive buffer. Any buffer which
+ * crosses a 4KB boundary must start on a 4KB boundary due to PCIe
+ * wdma restrictions. We also try to align any smaller allocation to
+ * at least a 16 byte boundary for efficiency. We assume the linux
+ * memory allocator works by powers of 2, and will not return memory
+ * smaller than 2KB which crosses a 4KB boundary. If it does, we fall
+ * back to allocating 2x as much space as required.
+ *
+ * We intend to replace large (>4KB) skb allocations by using
+ * pages directly and building a fraglist in the near future.
+ */
+
+static inline struct sk_buff *myri10ge_alloc_big(int bytes)
+{
+ struct sk_buff *skb;
+ unsigned long data, roundup;
+
+ skb = dev_alloc_skb(bytes + 4096 + MXGEFW_PAD);
+ if (skb == NULL)
+ return NULL;
+
+ /* Correct skb->truesize so that socket buffer
+ * accounting is not confused the rounding we must
+ * do to satisfy alignment constraints.
+ */
+ skb->truesize -= 4096;
+
+ data = (unsigned long)(skb->data);
+ roundup = (-data) & (4095);
+ skb_reserve(skb, roundup);
+ return skb;
+}
+
+/* Allocate 2x as much space as required and use whichever portion
+ * does not cross a 4KB boundary */
+static inline struct sk_buff *myri10ge_alloc_small_safe(unsigned int bytes)
+{
+ struct sk_buff *skb;
+ unsigned long data, boundary;
+
+ skb = dev_alloc_skb(2 * (bytes + MXGEFW_PAD) - 1);
+ if (unlikely(skb == NULL))
+ return NULL;
+
+ /* Correct skb->truesize so that socket buffer
+ * accounting is not confused the rounding we must
+ * do to satisfy alignment constraints.
+ */
+ skb->truesize -= bytes + MXGEFW_PAD;
+
+ data = (unsigned long)(skb->data);
+ boundary = (data + 4095UL) & ~4095UL;
+ if ((boundary - data) >= (bytes + MXGEFW_PAD))
+ return skb;
+
+ skb_reserve(skb, boundary - data);
+ return skb;
+}
+
+/* Allocate just enough space, and verify that the allocated
+ * space does not cross a 4KB boundary */
+static inline struct sk_buff *myri10ge_alloc_small(int bytes)
+{
+ struct sk_buff *skb;
+ unsigned long roundup, data, end;
+
+ skb = dev_alloc_skb(bytes + 16 + MXGEFW_PAD);
+ if (unlikely(skb == NULL))
+ return NULL;
+
+ /* Round allocated buffer to 16 byte boundary */
+ data = (unsigned long)(skb->data);
+ roundup = (-data) & 15UL;
+ skb_reserve(skb, roundup);
+ /* Verify that the data buffer does not cross a page boundary */
+ data = (unsigned long)(skb->data);
+ end = data + bytes + MXGEFW_PAD - 1;
+ if (unlikely(((end >> 12) != (data >> 12)) && (data & 4095UL))) {
+ printk(KERN_NOTICE
+ "myri10ge_alloc_small: small skb crossed 4KB boundary\n");
+ myri10ge_skb_cross_4k = 1;
+ dev_kfree_skb_any(skb);
+ skb = myri10ge_alloc_small_safe(bytes);
+ }
+ return skb;
+}
+
+static inline int
+myri10ge_getbuf(struct myri10ge_rx_buf *rx, struct pci_dev *pdev, int bytes,
+ int idx)
+{
+ struct sk_buff *skb;
+ dma_addr_t bus;
+ int len, retval = 0;
+
+ bytes += VLAN_HLEN; /* account for 802.1q vlan tag */
+
+ if ((bytes + MXGEFW_PAD) > (4096 - 16) /* linux overhead */ )
+ skb = myri10ge_alloc_big(bytes);
+ else if (myri10ge_skb_cross_4k)
+ skb = myri10ge_alloc_small_safe(bytes);
+ else
+ skb = myri10ge_alloc_small(bytes);
+
+ if (unlikely(skb == NULL)) {
+ rx->alloc_fail++;
+ retval = -ENOBUFS;
+ goto done;
+ }
+
+ /* set len so that it only covers the area we
+ * need mapped for DMA */
+ len = bytes + MXGEFW_PAD;
+
+ bus = pci_map_single(pdev, skb->data, len, PCI_DMA_FROMDEVICE);
+ rx->info[idx].skb = skb;
+ pci_unmap_addr_set(&rx->info[idx], bus, bus);
+ pci_unmap_len_set(&rx->info[idx], len, len);
+ rx->shadow[idx].addr_low = htonl(MYRI10GE_LOWPART_TO_U32(bus));
+ rx->shadow[idx].addr_high = htonl(MYRI10GE_HIGHPART_TO_U32(bus));
+
+done:
+ /* copy 8 descriptors (64-bytes) to the mcp at a time */
+ if ((idx & 7) == 7) {
+ if (rx->wc_fifo == NULL)
+ myri10ge_submit_8rx(&rx->lanai[idx - 7],
+ &rx->shadow[idx - 7]);
+ else {
+ mb();
+ myri10ge_pio_copy(rx->wc_fifo,
+ &rx->shadow[idx - 7], 64);
+ }
+ }
+ return retval;
+}
+
+static inline void myri10ge_vlan_ip_csum(struct sk_buff *skb, u16 hw_csum)
+{
+ struct vlan_hdr *vh = (struct vlan_hdr *)(skb->data);
+
+ if ((skb->protocol == ntohs(ETH_P_8021Q)) &&
+ (vh->h_vlan_encapsulated_proto == htons(ETH_P_IP) ||
+ vh->h_vlan_encapsulated_proto == htons(ETH_P_IPV6))) {
+ skb->csum = hw_csum;
+ skb->ip_summed = CHECKSUM_HW;
+ }
+}
+
+static inline unsigned long
+myri10ge_rx_done(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx,
+ int bytes, int len, int csum)
+{
+ dma_addr_t bus;
+ struct sk_buff *skb;
+ int idx, unmap_len;
+
+ idx = rx->cnt & rx->mask;
+ rx->cnt++;
+
+ /* save a pointer to the received skb */
+ skb = rx->info[idx].skb;
+ bus = pci_unmap_addr(&rx->info[idx], bus);
+ unmap_len = pci_unmap_len(&rx->info[idx], len);
+
+ /* try to replace the received skb */
+ if (myri10ge_getbuf(rx, mgp->pdev, bytes, idx)) {
+ /* drop the frame -- the old skbuf is re-cycled */
+ mgp->stats.rx_dropped += 1;
+ return 0;
+ }
+
+ /* unmap the recvd skb */
+ pci_unmap_single(mgp->pdev, bus, unmap_len, PCI_DMA_FROMDEVICE);
+
+ /* mcp implicitly skips 1st bytes so that packet is properly
+ * aligned */
+ skb_reserve(skb, MXGEFW_PAD);
+
+ /* set the length of the frame */
+ skb_put(skb, len);
+
+ skb->protocol = eth_type_trans(skb, mgp->dev);
+ skb->dev = mgp->dev;
+ if (mgp->csum_flag) {
+ if ((skb->protocol == ntohs(ETH_P_IP)) ||
+ (skb->protocol == ntohs(ETH_P_IPV6))) {
+ skb->csum = ntohs((u16) csum);
+ skb->ip_summed = CHECKSUM_HW;
+ } else
+ myri10ge_vlan_ip_csum(skb, ntohs((u16) csum));
+ }
+
+ netif_receive_skb(skb);
+ mgp->dev->last_rx = jiffies;
+ return 1;
+}
+
+static inline void myri10ge_tx_done(struct myri10ge_priv *mgp, int mcp_index)
+{
+ struct pci_dev *pdev = mgp->pdev;
+ struct myri10ge_tx_buf *tx = &mgp->tx;
+ struct sk_buff *skb;
+ int idx, len;
+ int limit = 0;
+
+ while (tx->pkt_done != mcp_index) {
+ idx = tx->done & tx->mask;
+ skb = tx->info[idx].skb;
+
+ /* Mark as free */
+ tx->info[idx].skb = NULL;
+ if (tx->info[idx].last) {
+ tx->pkt_done++;
+ tx->info[idx].last = 0;
+ }
+ tx->done++;
+ len = pci_unmap_len(&tx->info[idx], len);
+ pci_unmap_len_set(&tx->info[idx], len, 0);
+ if (skb) {
+ mgp->stats.tx_bytes += skb->len;
+ mgp->stats.tx_packets++;
+ dev_kfree_skb_irq(skb);
+ if (len)
+ pci_unmap_single(pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ } else {
+ if (len)
+ pci_unmap_page(pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ }
+
+ /* limit potential for livelock by only handling
+ * 2 full tx rings per call */
+ if (unlikely(++limit > 2 * tx->mask))
+ break;
+ }
+ /* start the queue if we've stopped it */
+ if (netif_queue_stopped(mgp->dev)
+ && tx->req - tx->done < (tx->mask >> 1)) {
+ mgp->wake_queue++;
+ netif_wake_queue(mgp->dev);
+ }
+}
+
+static inline void myri10ge_clean_rx_done(struct myri10ge_priv *mgp, int *limit)
+{
+ struct myri10ge_rx_done *rx_done = &mgp->rx_done;
+ unsigned long rx_bytes = 0;
+ unsigned long rx_packets = 0;
+ unsigned long rx_ok;
+
+ int idx = rx_done->idx;
+ int cnt = rx_done->cnt;
+ u16 length;
+ u16 checksum;
+
+ while (rx_done->entry[idx].length != 0 && *limit != 0) {
+ length = ntohs(rx_done->entry[idx].length);
+ rx_done->entry[idx].length = 0;
+ checksum = ntohs(rx_done->entry[idx].checksum);
+ if (length <= mgp->small_bytes)
+ rx_ok = myri10ge_rx_done(mgp, &mgp->rx_small,
+ mgp->small_bytes,
+ length, checksum);
+ else
+ rx_ok = myri10ge_rx_done(mgp, &mgp->rx_big,
+ mgp->dev->mtu + ETH_HLEN,
+ length, checksum);
+ rx_packets += rx_ok;
+ rx_bytes += rx_ok * (unsigned long)length;
+ cnt++;
+ idx = cnt & (myri10ge_max_intr_slots - 1);
+
+ /* limit potential for livelock by only handling a
+ * limited number of frames. */
+ (*limit)--;
+ }
+ rx_done->idx = idx;
+ rx_done->cnt = cnt;
+ mgp->stats.rx_packets += rx_packets;
+ mgp->stats.rx_bytes += rx_bytes;
+}
+
+static inline void myri10ge_check_statblock(struct myri10ge_priv *mgp)
+{
+ struct mcp_irq_data *stats = mgp->fw_stats;
+
+ if (unlikely(stats->stats_updated)) {
+ if (mgp->link_state != stats->link_up) {
+ mgp->link_state = stats->link_up;
+ if (mgp->link_state) {
+ printk(KERN_INFO "myri10ge: %s: link up\n",
+ mgp->dev->name);
+ netif_carrier_on(mgp->dev);
+ } else {
+ printk(KERN_INFO "myri10ge: %s: link down\n",
+ mgp->dev->name);
+ netif_carrier_off(mgp->dev);
+ }
+ }
+ if (mgp->rdma_tags_available !=
+ ntohl(mgp->fw_stats->rdma_tags_available)) {
+ mgp->rdma_tags_available =
+ ntohl(mgp->fw_stats->rdma_tags_available);
+ printk(KERN_WARNING "myri10ge: %s: RDMA timed out! "
+ "%d tags left\n", mgp->dev->name,
+ mgp->rdma_tags_available);
+ }
+ mgp->down_cnt += stats->link_down;
+ if (stats->link_down)
+ wake_up(&mgp->down_wq);
+ }
+}
+
+static int myri10ge_poll(struct net_device *netdev, int *budget)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ struct myri10ge_rx_done *rx_done = &mgp->rx_done;
+ int limit, orig_limit, work_done;
+
+ /* process as many rx events as NAPI will allow */
+ limit = min(*budget, netdev->quota);
+ orig_limit = limit;
+ myri10ge_clean_rx_done(mgp, &limit);
+ work_done = orig_limit - limit;
+ *budget -= work_done;
+ netdev->quota -= work_done;
+
+ if (rx_done->entry[rx_done->idx].length == 0 || !netif_running(netdev)) {
+ netif_rx_complete(netdev);
+ __raw_writel(htonl(3), mgp->irq_claim);
+ return 0;
+ }
+ return 1;
+}
+
+static irqreturn_t myri10ge_intr(int irq, void *arg, struct pt_regs *regs)
+{
+ struct myri10ge_priv *mgp = arg;
+ struct mcp_irq_data *stats = mgp->fw_stats;
+ struct myri10ge_tx_buf *tx = &mgp->tx;
+ u32 send_done_count;
+ int i;
+
+ /* make sure it is our IRQ, and that the DMA has finished */
+ if (unlikely(!stats->valid))
+ return (IRQ_NONE);
+
+ /* low bit indicates receives are present, so schedule
+ * napi poll handler */
+ if (stats->valid & 1)
+ netif_rx_schedule(mgp->dev);
+
+ if (!mgp->msi_enabled) {
+ __raw_writel(0, mgp->irq_deassert);
+ if (!myri10ge_deassert_wait)
+ stats->valid = 0;
+ mb();
+ } else
+ stats->valid = 0;
+
+ /* Wait for IRQ line to go low, if using INTx */
+ i = 0;
+ while (1) {
+ i++;
+ /* check for transmit completes and receives */
+ send_done_count = ntohl(stats->send_done_count);
+ if (send_done_count != tx->pkt_done)
+ myri10ge_tx_done(mgp, (int)send_done_count);
+ if (unlikely(i > myri10ge_max_irq_loops)) {
+ printk(KERN_WARNING "myri10ge: %s: irq stuck?\n",
+ mgp->dev->name);
+ stats->valid = 0;
+ schedule_work(&mgp->watchdog_work);
+ }
+ if (likely(stats->valid == 0))
+ break;
+ cpu_relax();
+ barrier();
+ }
+
+ myri10ge_check_statblock(mgp);
+
+ __raw_writel(htonl(3), mgp->irq_claim + 1);
+ return (IRQ_HANDLED);
+}
+
+static int
+myri10ge_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->speed = SPEED_10000;
+ cmd->duplex = DUPLEX_FULL;
+ return 0;
+}
+
+static void
+myri10ge_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+
+ strlcpy(info->driver, "myri10ge", sizeof(info->driver));
+ strlcpy(info->version, MYRI10GE_VERSION_STR, sizeof(info->version));
+ strlcpy(info->fw_version, mgp->fw_version, sizeof(info->fw_version));
+ strlcpy(info->bus_info, pci_name(mgp->pdev), sizeof(info->bus_info));
+}
+
+static int
+myri10ge_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ coal->rx_coalesce_usecs = mgp->intr_coal_delay;
+ return 0;
+}
+
+static int
+myri10ge_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+
+ mgp->intr_coal_delay = coal->rx_coalesce_usecs;
+ __raw_writel(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr);
+ return 0;
+}
+
+static void
+myri10ge_get_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *pause)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+
+ pause->autoneg = 0;
+ pause->rx_pause = mgp->pause;
+ pause->tx_pause = mgp->pause;
+}
+
+static int
+myri10ge_set_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *pause)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+
+ if (pause->tx_pause != mgp->pause)
+ return myri10ge_change_pause(mgp, pause->tx_pause);
+ if (pause->rx_pause != mgp->pause)
+ return myri10ge_change_pause(mgp, pause->tx_pause);
+ if (pause->autoneg != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static void
+myri10ge_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+
+ ring->rx_mini_max_pending = mgp->rx_small.mask + 1;
+ ring->rx_max_pending = mgp->rx_big.mask + 1;
+ ring->rx_jumbo_max_pending = 0;
+ ring->tx_max_pending = mgp->rx_small.mask + 1;
+ ring->rx_mini_pending = ring->rx_mini_max_pending;
+ ring->rx_pending = ring->rx_max_pending;
+ ring->rx_jumbo_pending = ring->rx_jumbo_max_pending;
+ ring->tx_pending = ring->tx_max_pending;
+}
+
+static u32 myri10ge_get_rx_csum(struct net_device *netdev)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ if (mgp->csum_flag)
+ return 1;
+ else
+ return 0;
+}
+
+static int myri10ge_set_rx_csum(struct net_device *netdev, u32 csum_enabled)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ if (csum_enabled)
+ mgp->csum_flag = MXGEFW_FLAGS_CKSUM;
+ else
+ mgp->csum_flag = 0;
+ return 0;
+}
+
+static const char myri10ge_gstrings_stats[][ETH_GSTRING_LEN] = {
+ "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors",
+ "tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions",
+ "rx_length_errors", "rx_over_errors", "rx_crc_errors",
+ "rx_frame_errors", "rx_fifo_errors", "rx_missed_errors",
+ "tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors",
+ "tx_heartbeat_errors", "tx_window_errors",
+ /* device-specific stats */
+ "read_dma_bw_MBs", "write_dma_bw_MBs", "read_write_dma_bw_MBs",
+ "serial_number", "tx_pkt_start", "tx_pkt_done",
+ "tx_req", "tx_done", "rx_small_cnt", "rx_big_cnt",
+ "wake_queue", "stop_queue", "watchdog_resets", "tx_linearized",
+ "link_up", "dropped_link_overflow", "dropped_link_error_or_filtered",
+ "dropped_runt", "dropped_overrun", "dropped_no_small_buffer",
+ "dropped_no_big_buffer"
+};
+
+#define MYRI10GE_NET_STATS_LEN 21
+#define MYRI10GE_STATS_LEN sizeof(myri10ge_gstrings_stats) / ETH_GSTRING_LEN
+
+static void
+myri10ge_get_strings(struct net_device *netdev, u32 stringset, u8 * data)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ memcpy(data, *myri10ge_gstrings_stats,
+ sizeof(myri10ge_gstrings_stats));
+ break;
+ }
+}
+
+static int myri10ge_get_stats_count(struct net_device *netdev)
+{
+ return MYRI10GE_STATS_LEN;
+}
+
+static void
+myri10ge_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 * data)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ int i;
+
+ for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++)
+ data[i] = ((unsigned long *)&mgp->stats)[i];
+
+ data[i++] = (unsigned int)mgp->read_dma;
+ data[i++] = (unsigned int)mgp->write_dma;
+ data[i++] = (unsigned int)mgp->read_write_dma;
+ data[i++] = (unsigned int)mgp->serial_number;
+ data[i++] = (unsigned int)mgp->tx.pkt_start;
+ data[i++] = (unsigned int)mgp->tx.pkt_done;
+ data[i++] = (unsigned int)mgp->tx.req;
+ data[i++] = (unsigned int)mgp->tx.done;
+ data[i++] = (unsigned int)mgp->rx_small.cnt;
+ data[i++] = (unsigned int)mgp->rx_big.cnt;
+ data[i++] = (unsigned int)mgp->wake_queue;
+ data[i++] = (unsigned int)mgp->stop_queue;
+ data[i++] = (unsigned int)mgp->watchdog_resets;
+ data[i++] = (unsigned int)mgp->tx_linearized;
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->link_up);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_link_overflow);
+ data[i++] =
+ (unsigned int)ntohl(mgp->fw_stats->dropped_link_error_or_filtered);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_runt);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_overrun);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_no_small_buffer);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_no_big_buffer);
+}
+
+static struct ethtool_ops myri10ge_ethtool_ops = {
+ .get_settings = myri10ge_get_settings,
+ .get_drvinfo = myri10ge_get_drvinfo,
+ .get_coalesce = myri10ge_get_coalesce,
+ .set_coalesce = myri10ge_set_coalesce,
+ .get_pauseparam = myri10ge_get_pauseparam,
+ .set_pauseparam = myri10ge_set_pauseparam,
+ .get_ringparam = myri10ge_get_ringparam,
+ .get_rx_csum = myri10ge_get_rx_csum,
+ .set_rx_csum = myri10ge_set_rx_csum,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = ethtool_op_set_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+#ifdef NETIF_F_TSO
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
+#endif
+ .get_strings = myri10ge_get_strings,
+ .get_stats_count = myri10ge_get_stats_count,
+ .get_ethtool_stats = myri10ge_get_ethtool_stats
+};
+
+static int myri10ge_allocate_rings(struct net_device *dev)
+{
+ struct myri10ge_priv *mgp;
+ struct myri10ge_cmd cmd;
+ int tx_ring_size, rx_ring_size;
+ int tx_ring_entries, rx_ring_entries;
+ int i, status;
+ size_t bytes;
+
+ mgp = netdev_priv(dev);
+
+ /* get ring sizes */
+
+ status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd, 0);
+ tx_ring_size = cmd.data0;
+ status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd, 0);
+ rx_ring_size = cmd.data0;
+
+ tx_ring_entries = tx_ring_size / sizeof(struct mcp_kreq_ether_send);
+ rx_ring_entries = rx_ring_size / sizeof(struct mcp_dma_addr);
+ mgp->tx.mask = tx_ring_entries - 1;
+ mgp->rx_small.mask = mgp->rx_big.mask = rx_ring_entries - 1;
+
+ /* allocate the host shadow rings */
+
+ bytes = 8 + (MYRI10GE_MAX_SEND_DESC_TSO + 4)
+ * sizeof(*mgp->tx.req_list);
+ mgp->tx.req_bytes = kzalloc(bytes, GFP_KERNEL);
+ if (mgp->tx.req_bytes == NULL)
+ goto abort_with_nothing;
+
+ /* ensure req_list entries are aligned to 8 bytes */
+ mgp->tx.req_list = (struct mcp_kreq_ether_send *)
+ ALIGN((unsigned long)mgp->tx.req_bytes, 8);
+
+ bytes = rx_ring_entries * sizeof(*mgp->rx_small.shadow);
+ mgp->rx_small.shadow = kzalloc(bytes, GFP_KERNEL);
+ if (mgp->rx_small.shadow == NULL)
+ goto abort_with_tx_req_bytes;
+
+ bytes = rx_ring_entries * sizeof(*mgp->rx_big.shadow);
+ mgp->rx_big.shadow = kzalloc(bytes, GFP_KERNEL);
+ if (mgp->rx_big.shadow == NULL)
+ goto abort_with_rx_small_shadow;
+
+ /* allocate the host info rings */
+
+ bytes = tx_ring_entries * sizeof(*mgp->tx.info);
+ mgp->tx.info = kzalloc(bytes, GFP_KERNEL);
+ if (mgp->tx.info == NULL)
+ goto abort_with_rx_big_shadow;
+
+ bytes = rx_ring_entries * sizeof(*mgp->rx_small.info);
+ mgp->rx_small.info = kzalloc(bytes, GFP_KERNEL);
+ if (mgp->rx_small.info == NULL)
+ goto abort_with_tx_info;
+
+ bytes = rx_ring_entries * sizeof(*mgp->rx_big.info);
+ mgp->rx_big.info = kzalloc(bytes, GFP_KERNEL);
+ if (mgp->rx_big.info == NULL)
+ goto abort_with_rx_small_info;
+
+ /* Fill the receive rings */
+
+ for (i = 0; i <= mgp->rx_small.mask; i++) {
+ status = myri10ge_getbuf(&mgp->rx_small, mgp->pdev,
+ mgp->small_bytes, i);
+ if (status) {
+ printk(KERN_ERR
+ "myri10ge: %s: alloced only %d small bufs\n",
+ dev->name, i);
+ goto abort_with_rx_small_ring;
+ }
+ }
+
+ for (i = 0; i <= mgp->rx_big.mask; i++) {
+ status =
+ myri10ge_getbuf(&mgp->rx_big, mgp->pdev,
+ dev->mtu + ETH_HLEN, i);
+ if (status) {
+ printk(KERN_ERR
+ "myri10ge: %s: alloced only %d big bufs\n",
+ dev->name, i);
+ goto abort_with_rx_big_ring;
+ }
+ }
+
+ return 0;
+
+abort_with_rx_big_ring:
+ for (i = 0; i <= mgp->rx_big.mask; i++) {
+ if (mgp->rx_big.info[i].skb != NULL)
+ dev_kfree_skb_any(mgp->rx_big.info[i].skb);
+ if (pci_unmap_len(&mgp->rx_big.info[i], len))
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&mgp->rx_big.info[i],
+ bus),
+ pci_unmap_len(&mgp->rx_big.info[i],
+ len),
+ PCI_DMA_FROMDEVICE);
+ }
+
+abort_with_rx_small_ring:
+ for (i = 0; i <= mgp->rx_small.mask; i++) {
+ if (mgp->rx_small.info[i].skb != NULL)
+ dev_kfree_skb_any(mgp->rx_small.info[i].skb);
+ if (pci_unmap_len(&mgp->rx_small.info[i], len))
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&mgp->rx_small.info[i],
+ bus),
+ pci_unmap_len(&mgp->rx_small.info[i],
+ len),
+ PCI_DMA_FROMDEVICE);
+ }
+ kfree(mgp->rx_big.info);
+
+abort_with_rx_small_info:
+ kfree(mgp->rx_small.info);
+
+abort_with_tx_info:
+ kfree(mgp->tx.info);
+
+abort_with_rx_big_shadow:
+ kfree(mgp->rx_big.shadow);
+
+abort_with_rx_small_shadow:
+ kfree(mgp->rx_small.shadow);
+
+abort_with_tx_req_bytes:
+ kfree(mgp->tx.req_bytes);
+ mgp->tx.req_bytes = NULL;
+ mgp->tx.req_list = NULL;
+
+abort_with_nothing:
+ return status;
+}
+
+static void myri10ge_free_rings(struct net_device *dev)
+{
+ struct myri10ge_priv *mgp;
+ struct sk_buff *skb;
+ struct myri10ge_tx_buf *tx;
+ int i, len, idx;
+
+ mgp = netdev_priv(dev);
+
+ for (i = 0; i <= mgp->rx_big.mask; i++) {
+ if (mgp->rx_big.info[i].skb != NULL)
+ dev_kfree_skb_any(mgp->rx_big.info[i].skb);
+ if (pci_unmap_len(&mgp->rx_big.info[i], len))
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&mgp->rx_big.info[i],
+ bus),
+ pci_unmap_len(&mgp->rx_big.info[i],
+ len),
+ PCI_DMA_FROMDEVICE);
+ }
+
+ for (i = 0; i <= mgp->rx_small.mask; i++) {
+ if (mgp->rx_small.info[i].skb != NULL)
+ dev_kfree_skb_any(mgp->rx_small.info[i].skb);
+ if (pci_unmap_len(&mgp->rx_small.info[i], len))
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&mgp->rx_small.info[i],
+ bus),
+ pci_unmap_len(&mgp->rx_small.info[i],
+ len),
+ PCI_DMA_FROMDEVICE);
+ }
+
+ tx = &mgp->tx;
+ while (tx->done != tx->req) {
+ idx = tx->done & tx->mask;
+ skb = tx->info[idx].skb;
+
+ /* Mark as free */
+ tx->info[idx].skb = NULL;
+ tx->done++;
+ len = pci_unmap_len(&tx->info[idx], len);
+ pci_unmap_len_set(&tx->info[idx], len, 0);
+ if (skb) {
+ mgp->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ if (len)
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ } else {
+ if (len)
+ pci_unmap_page(mgp->pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ }
+ }
+ kfree(mgp->rx_big.info);
+
+ kfree(mgp->rx_small.info);
+
+ kfree(mgp->tx.info);
+
+ kfree(mgp->rx_big.shadow);
+
+ kfree(mgp->rx_small.shadow);
+
+ kfree(mgp->tx.req_bytes);
+ mgp->tx.req_bytes = NULL;
+ mgp->tx.req_list = NULL;
+}
+
+static int myri10ge_open(struct net_device *dev)
+{
+ struct myri10ge_priv *mgp;
+ struct myri10ge_cmd cmd;
+ int status, big_pow2;
+
+ mgp = netdev_priv(dev);
+
+ if (mgp->running != MYRI10GE_ETH_STOPPED)
+ return -EBUSY;
+
+ mgp->running = MYRI10GE_ETH_STARTING;
+ status = myri10ge_reset(mgp);
+ if (status != 0) {
+ printk(KERN_ERR "myri10ge: %s: failed reset\n", dev->name);
+ mgp->running = MYRI10GE_ETH_STOPPED;
+ return -ENXIO;
+ }
+
+ /* decide what small buffer size to use. For good TCP rx
+ * performance, it is important to not receive 1514 byte
+ * frames into jumbo buffers, as it confuses the socket buffer
+ * accounting code, leading to drops and erratic performance.
+ */
+
+ if (dev->mtu <= ETH_DATA_LEN)
+ mgp->small_bytes = 128; /* enough for a TCP header */
+ else
+ mgp->small_bytes = ETH_FRAME_LEN; /* enough for an ETH_DATA_LEN frame */
+
+ /* Override the small buffer size? */
+ if (myri10ge_small_bytes > 0)
+ mgp->small_bytes = myri10ge_small_bytes;
+
+ /* If the user sets an obscenely small MTU, adjust the small
+ * bytes down to nearly nothing */
+ if (mgp->small_bytes >= (dev->mtu + ETH_HLEN))
+ mgp->small_bytes = 64;
+
+ /* get the lanai pointers to the send and receive rings */
+
+ status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_OFFSET, &cmd, 0);
+ mgp->tx.lanai =
+ (struct mcp_kreq_ether_send __iomem *)(mgp->sram + cmd.data0);
+
+ status |=
+ myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SMALL_RX_OFFSET, &cmd, 0);
+ mgp->rx_small.lanai =
+ (struct mcp_kreq_ether_recv __iomem *)(mgp->sram + cmd.data0);
+
+ status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd, 0);
+ mgp->rx_big.lanai =
+ (struct mcp_kreq_ether_recv __iomem *)(mgp->sram + cmd.data0);
+
+ if (status != 0) {
+ printk(KERN_ERR
+ "myri10ge: %s: failed to get ring sizes or locations\n",
+ dev->name);
+ mgp->running = MYRI10GE_ETH_STOPPED;
+ return -ENXIO;
+ }
+
+ if (mgp->mtrr >= 0) {
+ mgp->tx.wc_fifo = (u8 __iomem *) mgp->sram + 0x200000;
+ mgp->rx_small.wc_fifo = (u8 __iomem *) mgp->sram + 0x300000;
+ mgp->rx_big.wc_fifo = (u8 __iomem *) mgp->sram + 0x340000;
+ } else {
+ mgp->tx.wc_fifo = NULL;
+ mgp->rx_small.wc_fifo = NULL;
+ mgp->rx_big.wc_fifo = NULL;
+ }
+
+ status = myri10ge_allocate_rings(dev);
+ if (status != 0)
+ goto abort_with_nothing;
+
+ /* Firmware needs the big buff size as a power of 2. Lie and
+ * tell him the buffer is larger, because we only use 1
+ * buffer/pkt, and the mtu will prevent overruns.
+ */
+ big_pow2 = dev->mtu + ETH_HLEN + MXGEFW_PAD;
+ while ((big_pow2 & (big_pow2 - 1)) != 0)
+ big_pow2++;
+
+ /* now give firmware buffers sizes, and MTU */
+ cmd.data0 = dev->mtu + ETH_HLEN + VLAN_HLEN;
+ status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_MTU, &cmd, 0);
+ cmd.data0 = mgp->small_bytes;
+ status |=
+ myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, &cmd, 0);
+ cmd.data0 = big_pow2;
+ status |=
+ myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_BIG_BUFFER_SIZE, &cmd, 0);
+ if (status) {
+ printk(KERN_ERR "myri10ge: %s: Couldn't set buffer sizes\n",
+ dev->name);
+ goto abort_with_rings;
+ }
+
+ cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->fw_stats_bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->fw_stats_bus);
+ status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_STATS_DMA, &cmd, 0);
+ if (status) {
+ printk(KERN_ERR "myri10ge: %s: Couldn't set stats DMA\n",
+ dev->name);
+ goto abort_with_rings;
+ }
+
+ mgp->link_state = -1;
+ mgp->rdma_tags_available = 15;
+
+ netif_poll_enable(mgp->dev); /* must happen prior to any irq */
+
+ status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_UP, &cmd, 0);
+ if (status) {
+ printk(KERN_ERR "myri10ge: %s: Couldn't bring up link\n",
+ dev->name);
+ goto abort_with_rings;
+ }
+
+ mgp->wake_queue = 0;
+ mgp->stop_queue = 0;
+ mgp->running = MYRI10GE_ETH_RUNNING;
+ mgp->watchdog_timer.expires = jiffies + myri10ge_watchdog_timeout * HZ;
+ add_timer(&mgp->watchdog_timer);
+ netif_wake_queue(dev);
+ return 0;
+
+abort_with_rings:
+ myri10ge_free_rings(dev);
+
+abort_with_nothing:
+ mgp->running = MYRI10GE_ETH_STOPPED;
+ return -ENOMEM;
+}
+
+static int myri10ge_close(struct net_device *dev)
+{
+ struct myri10ge_priv *mgp;
+ struct myri10ge_cmd cmd;
+ int status, old_down_cnt;
+
+ mgp = netdev_priv(dev);
+
+ if (mgp->running != MYRI10GE_ETH_RUNNING)
+ return 0;
+
+ if (mgp->tx.req_bytes == NULL)
+ return 0;
+
+ del_timer_sync(&mgp->watchdog_timer);
+ mgp->running = MYRI10GE_ETH_STOPPING;
+ netif_poll_disable(mgp->dev);
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+ old_down_cnt = mgp->down_cnt;
+ mb();
+ status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ETHERNET_DOWN, &cmd, 0);
+ if (status)
+ printk(KERN_ERR "myri10ge: %s: Couldn't bring down link\n",
+ dev->name);
+
+ wait_event_timeout(mgp->down_wq, old_down_cnt != mgp->down_cnt, HZ);
+ if (old_down_cnt == mgp->down_cnt)
+ printk(KERN_ERR "myri10ge: %s never got down irq\n", dev->name);
+
+ netif_tx_disable(dev);
+
+ myri10ge_free_rings(dev);
+
+ mgp->running = MYRI10GE_ETH_STOPPED;
+ return 0;
+}
+
+/* copy an array of struct mcp_kreq_ether_send's to the mcp. Copy
+ * backwards one at a time and handle ring wraps */
+
+static inline void
+myri10ge_submit_req_backwards(struct myri10ge_tx_buf *tx,
+ struct mcp_kreq_ether_send *src, int cnt)
+{
+ int idx, starting_slot;
+ starting_slot = tx->req;
+ while (cnt > 1) {
+ cnt--;
+ idx = (starting_slot + cnt) & tx->mask;
+ myri10ge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
+ mb();
+ }
+}
+
+/*
+ * copy an array of struct mcp_kreq_ether_send's to the mcp. Copy
+ * at most 32 bytes at a time, so as to avoid involving the software
+ * pio handler in the nic. We re-write the first segment's flags
+ * to mark them valid only after writing the entire chain.
+ */
+
+static inline void
+myri10ge_submit_req(struct myri10ge_tx_buf *tx, struct mcp_kreq_ether_send *src,
+ int cnt)
+{
+ int idx, i;
+ struct mcp_kreq_ether_send __iomem *dstp, *dst;
+ struct mcp_kreq_ether_send *srcp;
+ u8 last_flags;
+
+ idx = tx->req & tx->mask;
+
+ last_flags = src->flags;
+ src->flags = 0;
+ mb();
+ dst = dstp = &tx->lanai[idx];
+ srcp = src;
+
+ if ((idx + cnt) < tx->mask) {
+ for (i = 0; i < (cnt - 1); i += 2) {
+ myri10ge_pio_copy(dstp, srcp, 2 * sizeof(*src));
+ mb(); /* force write every 32 bytes */
+ srcp += 2;
+ dstp += 2;
+ }
+ } else {
+ /* submit all but the first request, and ensure
+ * that it is submitted below */
+ myri10ge_submit_req_backwards(tx, src, cnt);
+ i = 0;
+ }
+ if (i < cnt) {
+ /* submit the first request */
+ myri10ge_pio_copy(dstp, srcp, sizeof(*src));
+ mb(); /* barrier before setting valid flag */
+ }
+
+ /* re-write the last 32-bits with the valid flags */
+ src->flags = last_flags;
+ __raw_writel(*((u32 *) src + 3), (u32 __iomem *) dst + 3);
+ tx->req += cnt;
+ mb();
+}
+
+static inline void
+myri10ge_submit_req_wc(struct myri10ge_tx_buf *tx,
+ struct mcp_kreq_ether_send *src, int cnt)
+{
+ tx->req += cnt;
+ mb();
+ while (cnt >= 4) {
+ myri10ge_pio_copy(tx->wc_fifo, src, 64);
+ mb();
+ src += 4;
+ cnt -= 4;
+ }
+ if (cnt > 0) {
+ /* pad it to 64 bytes. The src is 64 bytes bigger than it
+ * needs to be so that we don't overrun it */
+ myri10ge_pio_copy(tx->wc_fifo + (cnt << 18), src, 64);
+ mb();
+ }
+}
+
+/*
+ * Transmit a packet. We need to split the packet so that a single
+ * segment does not cross myri10ge->tx.boundary, so this makes segment
+ * counting tricky. So rather than try to count segments up front, we
+ * just give up if there are too few segments to hold a reasonably
+ * fragmented packet currently available. If we run
+ * out of segments while preparing a packet for DMA, we just linearize
+ * it and try again.
+ */
+
+static int myri10ge_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct myri10ge_priv *mgp = netdev_priv(dev);
+ struct mcp_kreq_ether_send *req;
+ struct myri10ge_tx_buf *tx = &mgp->tx;
+ struct skb_frag_struct *frag;
+ dma_addr_t bus;
+ u32 low, high_swapped;
+ unsigned int len;
+ int idx, last_idx, avail, frag_cnt, frag_idx, count, mss, max_segments;
+ u16 pseudo_hdr_offset, cksum_offset;
+ int cum_len, seglen, boundary, rdma_count;
+ u8 flags, odd_flag;
+
+again:
+ req = tx->req_list;
+ avail = tx->mask - 1 - (tx->req - tx->done);
+
+ mss = 0;
+ max_segments = MXGEFW_MAX_SEND_DESC;
+
+#ifdef NETIF_F_TSO
+ if (skb->len > (dev->mtu + ETH_HLEN)) {
+ mss = skb_shinfo(skb)->tso_size;
+ if (mss != 0)
+ max_segments = MYRI10GE_MAX_SEND_DESC_TSO;
+ }
+#endif /*NETIF_F_TSO */
+
+ if ((unlikely(avail < max_segments))) {
+ /* we are out of transmit resources */
+ mgp->stop_queue++;
+ netif_stop_queue(dev);
+ return 1;
+ }
+
+ /* Setup checksum offloading, if needed */
+ cksum_offset = 0;
+ pseudo_hdr_offset = 0;
+ odd_flag = 0;
+ flags = (MXGEFW_FLAGS_NO_TSO | MXGEFW_FLAGS_FIRST);
+ if (likely(skb->ip_summed == CHECKSUM_HW)) {
+ cksum_offset = (skb->h.raw - skb->data);
+ pseudo_hdr_offset = (skb->h.raw + skb->csum) - skb->data;
+ /* If the headers are excessively large, then we must
+ * fall back to a software checksum */
+ if (unlikely(cksum_offset > 255 || pseudo_hdr_offset > 127)) {
+ if (skb_checksum_help(skb, 0))
+ goto drop;
+ cksum_offset = 0;
+ pseudo_hdr_offset = 0;
+ } else {
+ pseudo_hdr_offset = htons(pseudo_hdr_offset);
+ odd_flag = MXGEFW_FLAGS_ALIGN_ODD;
+ flags |= MXGEFW_FLAGS_CKSUM;
+ }
+ }
+
+ cum_len = 0;
+
+#ifdef NETIF_F_TSO
+ if (mss) { /* TSO */
+ /* this removes any CKSUM flag from before */
+ flags = (MXGEFW_FLAGS_TSO_HDR | MXGEFW_FLAGS_FIRST);
+
+ /* negative cum_len signifies to the
+ * send loop that we are still in the
+ * header portion of the TSO packet.
+ * TSO header must be at most 134 bytes long */
+ cum_len = -((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+
+ /* for TSO, pseudo_hdr_offset holds mss.
+ * The firmware figures out where to put
+ * the checksum by parsing the header. */
+ pseudo_hdr_offset = htons(mss);
+ } else
+#endif /*NETIF_F_TSO */
+ /* Mark small packets, and pad out tiny packets */
+ if (skb->len <= MXGEFW_SEND_SMALL_SIZE) {
+ flags |= MXGEFW_FLAGS_SMALL;
+
+ /* pad frames to at least ETH_ZLEN bytes */
+ if (unlikely(skb->len < ETH_ZLEN)) {
+ skb = skb_padto(skb, ETH_ZLEN);
+ if (skb == NULL) {
+ /* The packet is gone, so we must
+ * return 0 */
+ mgp->stats.tx_dropped += 1;
+ return 0;
+ }
+ /* adjust the len to account for the zero pad
+ * so that the nic can know how long it is */
+ skb->len = ETH_ZLEN;
+ }
+ }
+
+ /* map the skb for DMA */
+ len = skb->len - skb->data_len;
+ idx = tx->req & tx->mask;
+ tx->info[idx].skb = skb;
+ bus = pci_map_single(mgp->pdev, skb->data, len, PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&tx->info[idx], bus, bus);
+ pci_unmap_len_set(&tx->info[idx], len, len);
+
+ frag_cnt = skb_shinfo(skb)->nr_frags;
+ frag_idx = 0;
+ count = 0;
+ rdma_count = 0;
+
+ /* "rdma_count" is the number of RDMAs belonging to the
+ * current packet BEFORE the current send request. For
+ * non-TSO packets, this is equal to "count".
+ * For TSO packets, rdma_count needs to be reset
+ * to 0 after a segment cut.
+ *
+ * The rdma_count field of the send request is
+ * the number of RDMAs of the packet starting at
+ * that request. For TSO send requests with one ore more cuts
+ * in the middle, this is the number of RDMAs starting
+ * after the last cut in the request. All previous
+ * segments before the last cut implicitly have 1 RDMA.
+ *
+ * Since the number of RDMAs is not known beforehand,
+ * it must be filled-in retroactively - after each
+ * segmentation cut or at the end of the entire packet.
+ */
+
+ while (1) {
+ /* Break the SKB or Fragment up into pieces which
+ * do not cross mgp->tx.boundary */
+ low = MYRI10GE_LOWPART_TO_U32(bus);
+ high_swapped = htonl(MYRI10GE_HIGHPART_TO_U32(bus));
+ while (len) {
+ u8 flags_next;
+ int cum_len_next;
+
+ if (unlikely(count == max_segments))
+ goto abort_linearize;
+
+ boundary = (low + tx->boundary) & ~(tx->boundary - 1);
+ seglen = boundary - low;
+ if (seglen > len)
+ seglen = len;
+ flags_next = flags & ~MXGEFW_FLAGS_FIRST;
+ cum_len_next = cum_len + seglen;
+#ifdef NETIF_F_TSO
+ if (mss) { /* TSO */
+ (req - rdma_count)->rdma_count = rdma_count + 1;
+
+ if (likely(cum_len >= 0)) { /* payload */
+ int next_is_first, chop;
+
+ chop = (cum_len_next > mss);
+ cum_len_next = cum_len_next % mss;
+ next_is_first = (cum_len_next == 0);
+ flags |= chop * MXGEFW_FLAGS_TSO_CHOP;
+ flags_next |= next_is_first *
+ MXGEFW_FLAGS_FIRST;
+ rdma_count |= -(chop | next_is_first);
+ rdma_count += chop & !next_is_first;
+ } else if (likely(cum_len_next >= 0)) { /* header ends */
+ int small;
+
+ rdma_count = -1;
+ cum_len_next = 0;
+ seglen = -cum_len;
+ small = (mss <= MXGEFW_SEND_SMALL_SIZE);
+ flags_next = MXGEFW_FLAGS_TSO_PLD |
+ MXGEFW_FLAGS_FIRST |
+ (small * MXGEFW_FLAGS_SMALL);
+ }
+ }
+#endif /* NETIF_F_TSO */
+ req->addr_high = high_swapped;
+ req->addr_low = htonl(low);
+ req->pseudo_hdr_offset = pseudo_hdr_offset;
+ req->pad = 0; /* complete solid 16-byte block; does this matter? */
+ req->rdma_count = 1;
+ req->length = htons(seglen);
+ req->cksum_offset = cksum_offset;
+ req->flags = flags | ((cum_len & 1) * odd_flag);
+
+ low += seglen;
+ len -= seglen;
+ cum_len = cum_len_next;
+ flags = flags_next;
+ req++;
+ count++;
+ rdma_count++;
+ if (unlikely(cksum_offset > seglen))
+ cksum_offset -= seglen;
+ else
+ cksum_offset = 0;
+ }
+ if (frag_idx == frag_cnt)
+ break;
+
+ /* map next fragment for DMA */
+ idx = (count + tx->req) & tx->mask;
+ frag = &skb_shinfo(skb)->frags[frag_idx];
+ frag_idx++;
+ len = frag->size;
+ bus = pci_map_page(mgp->pdev, frag->page, frag->page_offset,
+ len, PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&tx->info[idx], bus, bus);
+ pci_unmap_len_set(&tx->info[idx], len, len);
+ }
+
+ (req - rdma_count)->rdma_count = rdma_count;
+#ifdef NETIF_F_TSO
+ if (mss)
+ do {
+ req--;
+ req->flags |= MXGEFW_FLAGS_TSO_LAST;
+ } while (!(req->flags & (MXGEFW_FLAGS_TSO_CHOP |
+ MXGEFW_FLAGS_FIRST)));
+#endif
+ idx = ((count - 1) + tx->req) & tx->mask;
+ tx->info[idx].last = 1;
+ if (tx->wc_fifo == NULL)
+ myri10ge_submit_req(tx, tx->req_list, count);
+ else
+ myri10ge_submit_req_wc(tx, tx->req_list, count);
+ tx->pkt_start++;
+ if ((avail - count) < MXGEFW_MAX_SEND_DESC) {
+ mgp->stop_queue++;
+ netif_stop_queue(dev);
+ }
+ dev->trans_start = jiffies;
+ return 0;
+
+abort_linearize:
+ /* Free any DMA resources we've alloced and clear out the skb
+ * slot so as to not trip up assertions, and to avoid a
+ * double-free if linearizing fails */
+
+ last_idx = (idx + 1) & tx->mask;
+ idx = tx->req & tx->mask;
+ tx->info[idx].skb = NULL;
+ do {
+ len = pci_unmap_len(&tx->info[idx], len);
+ if (len) {
+ if (tx->info[idx].skb != NULL)
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ else
+ pci_unmap_page(mgp->pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ pci_unmap_len_set(&tx->info[idx], len, 0);
+ tx->info[idx].skb = NULL;
+ }
+ idx = (idx + 1) & tx->mask;
+ } while (idx != last_idx);
+ if (skb_shinfo(skb)->tso_size) {
+ printk(KERN_ERR
+ "myri10ge: %s: TSO but wanted to linearize?!?!?\n",
+ mgp->dev->name);
+ goto drop;
+ }
+
+ if (skb_linearize(skb, GFP_ATOMIC))
+ goto drop;
+
+ mgp->tx_linearized++;
+ goto again;
+
+drop:
+ dev_kfree_skb_any(skb);
+ mgp->stats.tx_dropped += 1;
+ return 0;
+
+}
+
+static struct net_device_stats *myri10ge_get_stats(struct net_device *dev)
+{
+ struct myri10ge_priv *mgp = netdev_priv(dev);
+ return &mgp->stats;
+}
+
+static void myri10ge_set_multicast_list(struct net_device *dev)
+{
+ /* can be called from atomic contexts,
+ * pass 1 to force atomicity in myri10ge_send_cmd() */
+ myri10ge_change_promisc(netdev_priv(dev), dev->flags & IFF_PROMISC, 1);
+}
+
+static int myri10ge_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+ struct myri10ge_priv *mgp = netdev_priv(dev);
+ int status;
+
+ if (!is_valid_ether_addr(sa->sa_data))
+ return -EADDRNOTAVAIL;
+
+ status = myri10ge_update_mac_address(mgp, sa->sa_data);
+ if (status != 0) {
+ printk(KERN_ERR
+ "myri10ge: %s: changing mac address failed with %d\n",
+ dev->name, status);
+ return status;
+ }
+
+ /* change the dev structure */
+ memcpy(dev->dev_addr, sa->sa_data, 6);
+ return 0;
+}
+
+static int myri10ge_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct myri10ge_priv *mgp = netdev_priv(dev);
+ int error = 0;
+
+ if ((new_mtu < 68) || (ETH_HLEN + new_mtu > MYRI10GE_MAX_ETHER_MTU)) {
+ printk(KERN_ERR "myri10ge: %s: new mtu (%d) is not valid\n",
+ dev->name, new_mtu);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "%s: changing mtu from %d to %d\n",
+ dev->name, dev->mtu, new_mtu);
+ if (mgp->running) {
+ /* if we change the mtu on an active device, we must
+ * reset the device so the firmware sees the change */
+ myri10ge_close(dev);
+ dev->mtu = new_mtu;
+ myri10ge_open(dev);
+ } else
+ dev->mtu = new_mtu;
+
+ return error;
+}
+
+/*
+ * Enable ECRC to align PCI-E Completion packets on an 8-byte boundary.
+ * Only do it if the bridge is a root port since we don't want to disturb
+ * any other device, except if forced with myri10ge_ecrc_enable > 1.
+ */
+
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_PCIE 0x005d
+
+static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *bridge = mgp->pdev->bus->self;
+ struct device *dev = &mgp->pdev->dev;
+ unsigned cap;
+ unsigned err_cap;
+ u16 val;
+ u8 ext_type;
+ int ret;
+
+ if (!myri10ge_ecrc_enable || !bridge)
+ return;
+
+ /* check that the bridge is a root port */
+ cap = pci_find_capability(bridge, PCI_CAP_ID_EXP);
+ pci_read_config_word(bridge, cap + PCI_CAP_FLAGS, &val);
+ ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
+ if (ext_type != PCI_EXP_TYPE_ROOT_PORT) {
+ if (myri10ge_ecrc_enable > 1) {
+ struct pci_dev *old_bridge = bridge;
+
+ /* Walk the hierarchy up to the root port
+ * where ECRC has to be enabled */
+ do {
+ bridge = bridge->bus->self;
+ if (!bridge) {
+ dev_err(dev,
+ "Failed to find root port"
+ " to force ECRC\n");
+ return;
+ }
+ cap =
+ pci_find_capability(bridge, PCI_CAP_ID_EXP);
+ pci_read_config_word(bridge,
+ cap + PCI_CAP_FLAGS, &val);
+ ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
+ } while (ext_type != PCI_EXP_TYPE_ROOT_PORT);
+
+ dev_info(dev,
+ "Forcing ECRC on non-root port %s"
+ " (enabling on root port %s)\n",
+ pci_name(old_bridge), pci_name(bridge));
+ } else {
+ dev_err(dev,
+ "Not enabling ECRC on non-root port %s\n",
+ pci_name(bridge));
+ return;
+ }
+ }
+
+ cap = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ERR);
+ /* nvidia ext cap is not always linked in ext cap chain */
+ if (!cap
+ && bridge->vendor == PCI_VENDOR_ID_NVIDIA
+ && bridge->device == PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_PCIE)
+ cap = 0x160;
+
+ if (!cap)
+ return;
+
+ ret = pci_read_config_dword(bridge, cap + PCI_ERR_CAP, &err_cap);
+ if (ret) {
+ dev_err(dev, "failed reading ext-conf-space of %s\n",
+ pci_name(bridge));
+ dev_err(dev, "\t pci=nommconf in use? "
+ "or buggy/incomplete/absent ACPI MCFG attr?\n");
+ return;
+ }
+ if (!(err_cap & PCI_ERR_CAP_ECRC_GENC))
+ return;
+
+ err_cap |= PCI_ERR_CAP_ECRC_GENE;
+ pci_write_config_dword(bridge, cap + PCI_ERR_CAP, err_cap);
+ dev_info(dev, "Enabled ECRC on upstream bridge %s\n", pci_name(bridge));
+ mgp->tx.boundary = 4096;
+ mgp->fw_name = myri10ge_fw_aligned;
+}
+
+/*
+ * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
+ * when the PCI-E Completion packets are aligned on an 8-byte
+ * boundary. Some PCI-E chip sets always align Completion packets; on
+ * the ones that do not, the alignment can be enforced by enabling
+ * ECRC generation (if supported).
+ *
+ * When PCI-E Completion packets are not aligned, it is actually more
+ * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
+ *
+ * If the driver can neither enable ECRC nor verify that it has
+ * already been enabled, then it must use a firmware image which works
+ * around unaligned completion packets (myri10ge_ethp_z8e.dat), and it
+ * should also ensure that it never gives the device a Read-DMA which is
+ * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is
+ * enabled, then the driver should use the aligned (myri10ge_eth_z8e.dat)
+ * firmware image, and set tx.boundary to 4KB.
+ */
+
+#define PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE 0x0132
+
+static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *bridge = mgp->pdev->bus->self;
+
+ mgp->tx.boundary = 2048;
+ mgp->fw_name = myri10ge_fw_unaligned;
+
+ if (myri10ge_force_firmware == 0) {
+ myri10ge_enable_ecrc(mgp);
+
+ /* Check to see if the upstream bridge is known to
+ * provide aligned completions */
+ if (bridge
+ /* ServerWorks HT2000/HT1000 */
+ && bridge->vendor == PCI_VENDOR_ID_SERVERWORKS
+ && bridge->device ==
+ PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE) {
+ dev_info(&mgp->pdev->dev,
+ "Assuming aligned completions (0x%x:0x%x)\n",
+ bridge->vendor, bridge->device);
+ mgp->tx.boundary = 4096;
+ mgp->fw_name = myri10ge_fw_aligned;
+ }
+ } else {
+ if (myri10ge_force_firmware == 1) {
+ dev_info(&mgp->pdev->dev,
+ "Assuming aligned completions (forced)\n");
+ mgp->tx.boundary = 4096;
+ mgp->fw_name = myri10ge_fw_aligned;
+ } else {
+ dev_info(&mgp->pdev->dev,
+ "Assuming unaligned completions (forced)\n");
+ mgp->tx.boundary = 2048;
+ mgp->fw_name = myri10ge_fw_unaligned;
+ }
+ }
+ if (myri10ge_fw_name != NULL) {
+ dev_info(&mgp->pdev->dev, "overriding firmware to %s\n",
+ myri10ge_fw_name);
+ mgp->fw_name = myri10ge_fw_name;
+ }
+}
+
+static void myri10ge_save_state(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *pdev = mgp->pdev;
+ int cap;
+
+ pci_save_state(pdev);
+ /* now save PCIe and MSI state that Linux will not
+ * save for us */
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ pci_read_config_dword(pdev, cap + PCI_EXP_DEVCTL, &mgp->devctl);
+ cap = pci_find_capability(pdev, PCI_CAP_ID_MSI);
+ pci_read_config_word(pdev, cap + PCI_MSI_FLAGS, &mgp->msi_flags);
+}
+
+static void myri10ge_restore_state(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *pdev = mgp->pdev;
+ int cap;
+
+ /* restore PCIe and MSI state that linux will not */
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ pci_write_config_dword(pdev, cap + PCI_CAP_ID_EXP, mgp->devctl);
+ cap = pci_find_capability(pdev, PCI_CAP_ID_MSI);
+ pci_write_config_word(pdev, cap + PCI_MSI_FLAGS, mgp->msi_flags);
+
+ pci_restore_state(pdev);
+}
+
+#ifdef CONFIG_PM
+
+static int myri10ge_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct myri10ge_priv *mgp;
+ struct net_device *netdev;
+
+ mgp = pci_get_drvdata(pdev);
+ if (mgp == NULL)
+ return -EINVAL;
+ netdev = mgp->dev;
+
+ netif_device_detach(netdev);
+ if (netif_running(netdev)) {
+ printk(KERN_INFO "myri10ge: closing %s\n", netdev->name);
+ rtnl_lock();
+ myri10ge_close(netdev);
+ rtnl_unlock();
+ }
+ myri10ge_dummy_rdma(mgp, 0);
+ free_irq(pdev->irq, mgp);
+ myri10ge_save_state(mgp);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
+}
+
+static int myri10ge_resume(struct pci_dev *pdev)
+{
+ struct myri10ge_priv *mgp;
+ struct net_device *netdev;
+ int status;
+ u16 vendor;
+
+ mgp = pci_get_drvdata(pdev);
+ if (mgp == NULL)
+ return -EINVAL;
+ netdev = mgp->dev;
+ pci_set_power_state(pdev, 0); /* zeros conf space as a side effect */
+ msleep(5); /* give card time to respond */
+ pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);
+ if (vendor == 0xffff) {
+ printk(KERN_ERR "myri10ge: %s: device disappeared!\n",
+ mgp->dev->name);
+ return -EIO;
+ }
+ myri10ge_restore_state(mgp);
+ pci_enable_device(pdev);
+ pci_set_master(pdev);
+
+ status = request_irq(pdev->irq, myri10ge_intr, SA_SHIRQ,
+ netdev->name, mgp);
+ if (status != 0) {
+ dev_err(&pdev->dev, "failed to allocate IRQ\n");
+ goto abort_with_msi;
+ }
+
+ myri10ge_reset(mgp);
+ myri10ge_dummy_rdma(mgp, mgp->tx.boundary != 4096);
+
+ /* Save configuration space to be restored if the
+ * nic resets due to a parity error */
+ myri10ge_save_state(mgp);
+
+ if (netif_running(netdev)) {
+ rtnl_lock();
+ myri10ge_open(netdev);
+ rtnl_unlock();
+ }
+ netif_device_attach(netdev);
+
+ return 0;
+
+abort_with_msi:
+ return -EIO;
+
+}
+
+#endif /* CONFIG_PM */
+
+static u32 myri10ge_read_reboot(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *pdev = mgp->pdev;
+ int vs = mgp->vendor_specific_offset;
+ u32 reboot;
+
+ /*enter read32 mode */
+ pci_write_config_byte(pdev, vs + 0x10, 0x3);
+
+ /*read REBOOT_STATUS (0xfffffff0) */
+ pci_write_config_dword(pdev, vs + 0x18, 0xfffffff0);
+ pci_read_config_dword(pdev, vs + 0x14, &reboot);
+ return reboot;
+}
+
+/*
+ * This watchdog is used to check whether the board has suffered
+ * from a parity error and needs to be recovered.
+ */
+static void myri10ge_watchdog(void *arg)
+{
+ struct myri10ge_priv *mgp = arg;
+ u32 reboot;
+ int status;
+ u16 cmd, vendor;
+
+ mgp->watchdog_resets++;
+ pci_read_config_word(mgp->pdev, PCI_COMMAND, &cmd);
+ if ((cmd & PCI_COMMAND_MASTER) == 0) {
+ /* Bus master DMA disabled? Check to see
+ * if the card rebooted due to a parity error
+ * For now, just report it */
+ reboot = myri10ge_read_reboot(mgp);
+ printk(KERN_ERR
+ "myri10ge: %s: NIC rebooted (0x%x), resetting\n",
+ mgp->dev->name, reboot);
+ /*
+ * A rebooted nic will come back with config space as
+ * it was after power was applied to PCIe bus.
+ * Attempt to restore config space which was saved
+ * when the driver was loaded, or the last time the
+ * nic was resumed from power saving mode.
+ */
+ myri10ge_restore_state(mgp);
+ } else {
+ /* if we get back -1's from our slot, perhaps somebody
+ * powered off our card. Don't try to reset it in
+ * this case */
+ if (cmd == 0xffff) {
+ pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);
+ if (vendor == 0xffff) {
+ printk(KERN_ERR
+ "myri10ge: %s: device disappeared!\n",
+ mgp->dev->name);
+ return;
+ }
+ }
+ /* Perhaps it is a software error. Try to reset */
+
+ printk(KERN_ERR "myri10ge: %s: device timeout, resetting\n",
+ mgp->dev->name);
+ printk(KERN_INFO "myri10ge: %s: %d %d %d %d %d\n",
+ mgp->dev->name, mgp->tx.req, mgp->tx.done,
+ mgp->tx.pkt_start, mgp->tx.pkt_done,
+ (int)ntohl(mgp->fw_stats->send_done_count));
+ msleep(2000);
+ printk(KERN_INFO "myri10ge: %s: %d %d %d %d %d\n",
+ mgp->dev->name, mgp->tx.req, mgp->tx.done,
+ mgp->tx.pkt_start, mgp->tx.pkt_done,
+ (int)ntohl(mgp->fw_stats->send_done_count));
+ }
+ rtnl_lock();
+ myri10ge_close(mgp->dev);
+ status = myri10ge_load_firmware(mgp);
+ if (status != 0)
+ printk(KERN_ERR "myri10ge: %s: failed to load firmware\n",
+ mgp->dev->name);
+ else
+ myri10ge_open(mgp->dev);
+ rtnl_unlock();
+}
+
+/*
+ * We use our own timer routine rather than relying upon
+ * netdev->tx_timeout because we have a very large hardware transmit
+ * queue. Due to the large queue, the netdev->tx_timeout function
+ * cannot detect a NIC with a parity error in a timely fashion if the
+ * NIC is lightly loaded.
+ */
+static void myri10ge_watchdog_timer(unsigned long arg)
+{
+ struct myri10ge_priv *mgp;
+
+ mgp = (struct myri10ge_priv *)arg;
+ if (mgp->tx.req != mgp->tx.done &&
+ mgp->tx.done == mgp->watchdog_tx_done)
+ /* nic seems like it might be stuck.. */
+ schedule_work(&mgp->watchdog_work);
+ else
+ /* rearm timer */
+ mod_timer(&mgp->watchdog_timer,
+ jiffies + myri10ge_watchdog_timeout * HZ);
+
+ mgp->watchdog_tx_done = mgp->tx.done;
+}
+
+static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct net_device *netdev;
+ struct myri10ge_priv *mgp;
+ struct device *dev = &pdev->dev;
+ size_t bytes;
+ int i;
+ int status = -ENXIO;
+ int cap;
+ int dac_enabled;
+ u16 val;
+
+ netdev = alloc_etherdev(sizeof(*mgp));
+ if (netdev == NULL) {
+ dev_err(dev, "Could not allocate ethernet device\n");
+ return -ENOMEM;
+ }
+
+ mgp = netdev_priv(netdev);
+ memset(mgp, 0, sizeof(*mgp));
+ mgp->dev = netdev;
+ mgp->pdev = pdev;
+ mgp->csum_flag = MXGEFW_FLAGS_CKSUM;
+ mgp->pause = myri10ge_flow_control;
+ mgp->intr_coal_delay = myri10ge_intr_coal_delay;
+ init_waitqueue_head(&mgp->down_wq);
+
+ if (pci_enable_device(pdev)) {
+ dev_err(&pdev->dev, "pci_enable_device call failed\n");
+ status = -ENODEV;
+ goto abort_with_netdev;
+ }
+ myri10ge_select_firmware(mgp);
+
+ /* Find the vendor-specific cap so we can check
+ * the reboot register later on */
+ mgp->vendor_specific_offset
+ = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
+
+ /* Set our max read request to 4KB */
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (cap < 64) {
+ dev_err(&pdev->dev, "Bad PCI_CAP_ID_EXP location %d\n", cap);
+ goto abort_with_netdev;
+ }
+ status = pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &val);
+ if (status != 0) {
+ dev_err(&pdev->dev, "Error %d reading PCI_EXP_DEVCTL\n",
+ status);
+ goto abort_with_netdev;
+ }
+ val = (val & ~PCI_EXP_DEVCTL_READRQ) | (5 << 12);
+ status = pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, val);
+ if (status != 0) {
+ dev_err(&pdev->dev, "Error %d writing PCI_EXP_DEVCTL\n",
+ status);
+ goto abort_with_netdev;
+ }
+
+ pci_set_master(pdev);
+ dac_enabled = 1;
+ status = pci_set_dma_mask(pdev, DMA_64BIT_MASK);
+ if (status != 0) {
+ dac_enabled = 0;
+ dev_err(&pdev->dev,
+ "64-bit pci address mask was refused, trying 32-bit");
+ status = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ }
+ if (status != 0) {
+ dev_err(&pdev->dev, "Error %d setting DMA mask\n", status);
+ goto abort_with_netdev;
+ }
+ mgp->cmd = pci_alloc_consistent(pdev, sizeof(*mgp->cmd), &mgp->cmd_bus);
+ if (mgp->cmd == NULL)
+ goto abort_with_netdev;
+
+ mgp->fw_stats = pci_alloc_consistent(pdev, sizeof(*mgp->fw_stats),
+ &mgp->fw_stats_bus);
+ if (mgp->fw_stats == NULL)
+ goto abort_with_cmd;
+
+ mgp->board_span = pci_resource_len(pdev, 0);
+ mgp->iomem_base = pci_resource_start(pdev, 0);
+ mgp->mtrr = -1;
+#ifdef CONFIG_MTRR
+ mgp->mtrr = mtrr_add(mgp->iomem_base, mgp->board_span,
+ MTRR_TYPE_WRCOMB, 1);
+#endif
+ /* Hack. need to get rid of these magic numbers */
+ mgp->sram_size =
+ 2 * 1024 * 1024 - (2 * (48 * 1024) + (32 * 1024)) - 0x100;
+ if (mgp->sram_size > mgp->board_span) {
+ dev_err(&pdev->dev, "board span %ld bytes too small\n",
+ mgp->board_span);
+ goto abort_with_wc;
+ }
+ mgp->sram = ioremap(mgp->iomem_base, mgp->board_span);
+ if (mgp->sram == NULL) {
+ dev_err(&pdev->dev, "ioremap failed for %ld bytes at 0x%lx\n",
+ mgp->board_span, mgp->iomem_base);
+ status = -ENXIO;
+ goto abort_with_wc;
+ }
+ memcpy_fromio(mgp->eeprom_strings,
+ mgp->sram + mgp->sram_size - MYRI10GE_EEPROM_STRINGS_SIZE,
+ MYRI10GE_EEPROM_STRINGS_SIZE);
+ memset(mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE - 2, 0, 2);
+ status = myri10ge_read_mac_addr(mgp);
+ if (status)
+ goto abort_with_ioremap;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ netdev->dev_addr[i] = mgp->mac_addr[i];
+
+ /* allocate rx done ring */
+ bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
+ mgp->rx_done.entry =
+ pci_alloc_consistent(pdev, bytes, &mgp->rx_done.bus);
+ if (mgp->rx_done.entry == NULL)
+ goto abort_with_ioremap;
+ memset(mgp->rx_done.entry, 0, bytes);
+
+ status = myri10ge_load_firmware(mgp);
+ if (status != 0) {
+ dev_err(&pdev->dev, "failed to load firmware\n");
+ goto abort_with_rx_done;
+ }
+
+ status = myri10ge_reset(mgp);
+ if (status != 0) {
+ dev_err(&pdev->dev, "failed reset\n");
+ goto abort_with_firmware;
+ }
+
+ if (myri10ge_msi) {
+ status = pci_enable_msi(pdev);
+ if (status != 0)
+ dev_err(&pdev->dev,
+ "Error %d setting up MSI; falling back to xPIC\n",
+ status);
+ else
+ mgp->msi_enabled = 1;
+ }
+
+ status = request_irq(pdev->irq, myri10ge_intr, SA_SHIRQ,
+ netdev->name, mgp);
+ if (status != 0) {
+ dev_err(&pdev->dev, "failed to allocate IRQ\n");
+ goto abort_with_firmware;
+ }
+
+ pci_set_drvdata(pdev, mgp);
+ if ((myri10ge_initial_mtu + ETH_HLEN) > MYRI10GE_MAX_ETHER_MTU)
+ myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
+ if ((myri10ge_initial_mtu + ETH_HLEN) < 68)
+ myri10ge_initial_mtu = 68;
+ netdev->mtu = myri10ge_initial_mtu;
+ netdev->open = myri10ge_open;
+ netdev->stop = myri10ge_close;
+ netdev->hard_start_xmit = myri10ge_xmit;
+ netdev->get_stats = myri10ge_get_stats;
+ netdev->base_addr = mgp->iomem_base;
+ netdev->irq = pdev->irq;
+ netdev->change_mtu = myri10ge_change_mtu;
+ netdev->set_multicast_list = myri10ge_set_multicast_list;
+ netdev->set_mac_address = myri10ge_set_mac_address;
+ netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO;
+ if (dac_enabled)
+ netdev->features |= NETIF_F_HIGHDMA;
+ netdev->poll = myri10ge_poll;
+ netdev->weight = myri10ge_napi_weight;
+
+ /* Save configuration space to be restored if the
+ * nic resets due to a parity error */
+ myri10ge_save_state(mgp);
+ /* Restore state immediately since pci_save_msi_state disables MSI */
+ myri10ge_restore_state(mgp);
+
+ /* Setup the watchdog timer */
+ setup_timer(&mgp->watchdog_timer, myri10ge_watchdog_timer,
+ (unsigned long)mgp);
+
+ SET_ETHTOOL_OPS(netdev, &myri10ge_ethtool_ops);
+ INIT_WORK(&mgp->watchdog_work, myri10ge_watchdog, mgp);
+ status = register_netdev(netdev);
+ if (status != 0) {
+ dev_err(&pdev->dev, "register_netdev failed: %d\n", status);
+ goto abort_with_irq;
+ }
+
+ printk(KERN_INFO "myri10ge: %s: %s IRQ %d, tx bndry %d, fw %s, WC %s\n",
+ netdev->name, (mgp->msi_enabled ? "MSI" : "xPIC"),
+ pdev->irq, mgp->tx.boundary, mgp->fw_name,
+ (mgp->mtrr >= 0 ? "Enabled" : "Disabled"));
+
+ return 0;
+
+abort_with_irq:
+ free_irq(pdev->irq, mgp);
+ if (mgp->msi_enabled)
+ pci_disable_msi(pdev);
+
+abort_with_firmware:
+ myri10ge_dummy_rdma(mgp, 0);
+
+abort_with_rx_done:
+ bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
+ pci_free_consistent(pdev, bytes, mgp->rx_done.entry, mgp->rx_done.bus);
+
+abort_with_ioremap:
+ iounmap(mgp->sram);
+
+abort_with_wc:
+#ifdef CONFIG_MTRR
+ if (mgp->mtrr >= 0)
+ mtrr_del(mgp->mtrr, mgp->iomem_base, mgp->board_span);
+#endif
+ pci_free_consistent(pdev, sizeof(*mgp->fw_stats),
+ mgp->fw_stats, mgp->fw_stats_bus);
+
+abort_with_cmd:
+ pci_free_consistent(pdev, sizeof(*mgp->cmd), mgp->cmd, mgp->cmd_bus);
+
+abort_with_netdev:
+
+ free_netdev(netdev);
+ return status;
+}
+
+/*
+ * myri10ge_remove
+ *
+ * Does what is necessary to shutdown one Myrinet device. Called
+ * once for each Myrinet card by the kernel when a module is
+ * unloaded.
+ */
+static void myri10ge_remove(struct pci_dev *pdev)
+{
+ struct myri10ge_priv *mgp;
+ struct net_device *netdev;
+ size_t bytes;
+
+ mgp = pci_get_drvdata(pdev);
+ if (mgp == NULL)
+ return;
+
+ flush_scheduled_work();
+ netdev = mgp->dev;
+ unregister_netdev(netdev);
+ free_irq(pdev->irq, mgp);
+ if (mgp->msi_enabled)
+ pci_disable_msi(pdev);
+
+ myri10ge_dummy_rdma(mgp, 0);
+
+ bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
+ pci_free_consistent(pdev, bytes, mgp->rx_done.entry, mgp->rx_done.bus);
+
+ iounmap(mgp->sram);
+
+#ifdef CONFIG_MTRR
+ if (mgp->mtrr >= 0)
+ mtrr_del(mgp->mtrr, mgp->iomem_base, mgp->board_span);
+#endif
+ pci_free_consistent(pdev, sizeof(*mgp->fw_stats),
+ mgp->fw_stats, mgp->fw_stats_bus);
+
+ pci_free_consistent(pdev, sizeof(*mgp->cmd), mgp->cmd, mgp->cmd_bus);
+
+ free_netdev(netdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+#define PCI_DEVICE_ID_MYIRCOM_MYRI10GE_Z8E 0x0008
+
+static struct pci_device_id myri10ge_pci_tbl[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_MYRICOM, PCI_DEVICE_ID_MYIRCOM_MYRI10GE_Z8E)},
+ {0},
+};
+
+static struct pci_driver myri10ge_driver = {
+ .name = "myri10ge",
+ .probe = myri10ge_probe,
+ .remove = myri10ge_remove,
+ .id_table = myri10ge_pci_tbl,
+#ifdef CONFIG_PM
+ .suspend = myri10ge_suspend,
+ .resume = myri10ge_resume,
+#endif
+};
+
+static __init int myri10ge_init_module(void)
+{
+ printk(KERN_INFO "%s: Version %s\n", myri10ge_driver.name,
+ MYRI10GE_VERSION_STR);
+ return pci_register_driver(&myri10ge_driver);
+}
+
+module_init(myri10ge_init_module);
+
+static __exit void myri10ge_cleanup_module(void)
+{
+ pci_unregister_driver(&myri10ge_driver);
+}
+
+module_exit(myri10ge_cleanup_module);
^ permalink raw reply
* [PATCH 3/5] myri10ge - Driver header files
From: Brice Goglin @ 2006-05-23 7:11 UTC (permalink / raw)
To: netdev; +Cc: gallatin, linux-kernel
In-Reply-To: <20060523070919.GB30499@myri.com>
[PATCH 3/5] myri10ge - Driver header files
myri10ge driver header files.
myri10ge_mcp.h is the generic header, while myri10ge_mcp_gen_header.h
is automatically generated from our firmware image.
Signed-off-by: Brice Goglin <brice@myri.com>
Signed-off-by: Andrew J. Gallatin <gallatin@myri.com>
drivers/net/myri10ge/myri10ge_mcp.h | 205 ++++++++++++++
drivers/net/myri10ge/myri10ge_mcp_gen_header.h | 58 +++
2 files changed, 263 insertions(+)
--- /dev/null 2006-05-16 20:08:50.920483500 +0200
+++ linux-tmp/drivers/net/myri10ge/myri10ge_mcp.h 2006-05-22 23:16:35.000000000 +0200
@@ -0,0 +1,205 @@
+#ifndef __MYRI10GE_MCP_H__
+#define __MYRI10GE_MCP_H__
+
+#define MXGEFW_VERSION_MAJOR 1
+#define MXGEFW_VERSION_MINOR 4
+
+/* 8 Bytes */
+struct mcp_dma_addr {
+ u32 high;
+ u32 low;
+};
+
+/* 4 Bytes */
+struct mcp_slot {
+ u16 checksum;
+ u16 length;
+};
+
+/* 64 Bytes */
+struct mcp_cmd {
+ u32 cmd;
+ u32 data0; /* will be low portion if data > 32 bits */
+ /* 8 */
+ u32 data1; /* will be high portion if data > 32 bits */
+ u32 data2; /* currently unused.. */
+ /* 16 */
+ struct mcp_dma_addr response_addr;
+ /* 24 */
+ u8 pad[40];
+};
+
+/* 8 Bytes */
+struct mcp_cmd_response {
+ u32 data;
+ u32 result;
+};
+
+/*
+ * flags used in mcp_kreq_ether_send_t:
+ *
+ * The SMALL flag is only needed in the first segment. It is raised
+ * for packets that are total less or equal 512 bytes.
+ *
+ * The CKSUM flag must be set in all segments.
+ *
+ * The PADDED flags is set if the packet needs to be padded, and it
+ * must be set for all segments.
+ *
+ * The MXGEFW_FLAGS_ALIGN_ODD must be set if the cumulative
+ * length of all previous segments was odd.
+ */
+
+#define MXGEFW_FLAGS_SMALL 0x1
+#define MXGEFW_FLAGS_TSO_HDR 0x1
+#define MXGEFW_FLAGS_FIRST 0x2
+#define MXGEFW_FLAGS_ALIGN_ODD 0x4
+#define MXGEFW_FLAGS_CKSUM 0x8
+#define MXGEFW_FLAGS_TSO_LAST 0x8
+#define MXGEFW_FLAGS_NO_TSO 0x10
+#define MXGEFW_FLAGS_TSO_CHOP 0x10
+#define MXGEFW_FLAGS_TSO_PLD 0x20
+
+#define MXGEFW_SEND_SMALL_SIZE 1520
+#define MXGEFW_MAX_MTU 9400
+
+union mcp_pso_or_cumlen {
+ u16 pseudo_hdr_offset;
+ u16 cum_len;
+};
+
+#define MXGEFW_MAX_SEND_DESC 12
+#define MXGEFW_PAD 2
+
+/* 16 Bytes */
+struct mcp_kreq_ether_send {
+ u32 addr_high;
+ u32 addr_low;
+ u16 pseudo_hdr_offset;
+ u16 length;
+ u8 pad;
+ u8 rdma_count;
+ u8 cksum_offset; /* where to start computing cksum */
+ u8 flags; /* as defined above */
+};
+
+/* 8 Bytes */
+struct mcp_kreq_ether_recv {
+ u32 addr_high;
+ u32 addr_low;
+};
+
+/* Commands */
+
+#define MXGEFW_CMD_OFFSET 0xf80000
+
+enum myri10ge_mcp_cmd_type {
+ MXGEFW_CMD_NONE = 0,
+ /* Reset the mcp, it is left in a safe state, waiting
+ * for the driver to set all its parameters */
+ MXGEFW_CMD_RESET,
+
+ /* get the version number of the current firmware..
+ * (may be available in the eeprom strings..? */
+ MXGEFW_GET_MCP_VERSION,
+
+ /* Parameters which must be set by the driver before it can
+ * issue MXGEFW_CMD_ETHERNET_UP. They persist until the next
+ * MXGEFW_CMD_RESET is issued */
+
+ MXGEFW_CMD_SET_INTRQ_DMA,
+ MXGEFW_CMD_SET_BIG_BUFFER_SIZE, /* in bytes, power of 2 */
+ MXGEFW_CMD_SET_SMALL_BUFFER_SIZE, /* in bytes */
+
+ /* Parameters which refer to lanai SRAM addresses where the
+ * driver must issue PIO writes for various things */
+
+ MXGEFW_CMD_GET_SEND_OFFSET,
+ MXGEFW_CMD_GET_SMALL_RX_OFFSET,
+ MXGEFW_CMD_GET_BIG_RX_OFFSET,
+ MXGEFW_CMD_GET_IRQ_ACK_OFFSET,
+ MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
+
+ /* Parameters which refer to rings stored on the MCP,
+ * and whose size is controlled by the mcp */
+
+ MXGEFW_CMD_GET_SEND_RING_SIZE, /* in bytes */
+ MXGEFW_CMD_GET_RX_RING_SIZE, /* in bytes */
+
+ /* Parameters which refer to rings stored in the host,
+ * and whose size is controlled by the host. Note that
+ * all must be physically contiguous and must contain
+ * a power of 2 number of entries. */
+
+ MXGEFW_CMD_SET_INTRQ_SIZE, /* in bytes */
+
+ /* command to bring ethernet interface up. Above parameters
+ * (plus mtu & mac address) must have been exchanged prior
+ * to issuing this command */
+ MXGEFW_CMD_ETHERNET_UP,
+
+ /* command to bring ethernet interface down. No further sends
+ * or receives may be processed until an MXGEFW_CMD_ETHERNET_UP
+ * is issued, and all interrupt queues must be flushed prior
+ * to ack'ing this command */
+
+ MXGEFW_CMD_ETHERNET_DOWN,
+
+ /* commands the driver may issue live, without resetting
+ * the nic. Note that increasing the mtu "live" should
+ * only be done if the driver has already supplied buffers
+ * sufficiently large to handle the new mtu. Decreasing
+ * the mtu live is safe */
+
+ MXGEFW_CMD_SET_MTU,
+ MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, /* in microseconds */
+ MXGEFW_CMD_SET_STATS_INTERVAL, /* in microseconds */
+ MXGEFW_CMD_SET_STATS_DMA,
+
+ MXGEFW_ENABLE_PROMISC,
+ MXGEFW_DISABLE_PROMISC,
+ MXGEFW_SET_MAC_ADDRESS,
+
+ MXGEFW_ENABLE_FLOW_CONTROL,
+ MXGEFW_DISABLE_FLOW_CONTROL,
+
+ /* do a DMA test
+ * data0,data1 = DMA address
+ * data2 = RDMA length (MSH), WDMA length (LSH)
+ * command return data = repetitions (MSH), 0.5-ms ticks (LSH)
+ */
+ MXGEFW_DMA_TEST
+};
+
+enum myri10ge_mcp_cmd_status {
+ MXGEFW_CMD_OK = 0,
+ MXGEFW_CMD_UNKNOWN,
+ MXGEFW_CMD_ERROR_RANGE,
+ MXGEFW_CMD_ERROR_BUSY,
+ MXGEFW_CMD_ERROR_EMPTY,
+ MXGEFW_CMD_ERROR_CLOSED,
+ MXGEFW_CMD_ERROR_HASH_ERROR,
+ MXGEFW_CMD_ERROR_BAD_PORT,
+ MXGEFW_CMD_ERROR_RESOURCES
+};
+
+/* 40 Bytes */
+struct mcp_irq_data {
+ u32 send_done_count;
+
+ u32 link_up;
+ u32 dropped_link_overflow;
+ u32 dropped_link_error_or_filtered;
+ u32 dropped_runt;
+ u32 dropped_overrun;
+ u32 dropped_no_small_buffer;
+ u32 dropped_no_big_buffer;
+ u32 rdma_tags_available;
+
+ u8 tx_stopped;
+ u8 link_down;
+ u8 stats_updated;
+ u8 valid;
+};
+
+#endif /* __MYRI10GE_MCP_H__ */
--- /dev/null 2006-05-16 20:08:50.920483500 +0200
+++ linux-tmp/drivers/net/myri10ge/myri10ge_mcp_gen_header.h 2006-05-22 23:16:35.000000000 +0200
@@ -0,0 +1,58 @@
+#ifndef __MYRI10GE_MCP_GEN_HEADER_H__
+#define __MYRI10GE_MCP_GEN_HEADER_H__
+
+/* this file define a standard header used as a first entry point to
+ * exchange information between firmware/driver and driver. The
+ * header structure can be anywhere in the mcp. It will usually be in
+ * the .data section, because some fields needs to be initialized at
+ * compile time.
+ * The 32bit word at offset MX_HEADER_PTR_OFFSET in the mcp must
+ * contains the location of the header.
+ *
+ * Typically a MCP will start with the following:
+ * .text
+ * .space 52 ! to help catch MEMORY_INT errors
+ * bt start ! jump to real code
+ * nop
+ * .long _gen_mcp_header
+ *
+ * The source will have a definition like:
+ *
+ * mcp_gen_header_t gen_mcp_header = {
+ * .header_length = sizeof(mcp_gen_header_t),
+ * .mcp_type = MCP_TYPE_XXX,
+ * .version = "something $Id: mcp_gen_header.h,v 1.2 2006/05/13 10:04:35 bgoglin Exp $",
+ * .mcp_globals = (unsigned)&Globals
+ * };
+ */
+
+#define MCP_HEADER_PTR_OFFSET 0x3c
+
+#define MCP_TYPE_MX 0x4d582020 /* "MX " */
+#define MCP_TYPE_PCIE 0x70636965 /* "PCIE" pcie-only MCP */
+#define MCP_TYPE_ETH 0x45544820 /* "ETH " */
+#define MCP_TYPE_MCP0 0x4d435030 /* "MCP0" */
+
+struct mcp_gen_header {
+ /* the first 4 fields are filled at compile time */
+ unsigned header_length;
+ unsigned mcp_type;
+ char version[128];
+ unsigned mcp_globals; /* pointer to mcp-type specific structure */
+
+ /* filled by the MCP at run-time */
+ unsigned sram_size;
+ unsigned string_specs; /* either the original STRING_SPECS or a superset */
+ unsigned string_specs_len;
+
+ /* Fields above this comment are guaranteed to be present.
+ *
+ * Fields below this comment are extensions added in later versions
+ * of this struct, drivers should compare the header_length against
+ * offsetof(field) to check wether a given MCP implements them.
+ *
+ * Never remove any field. Keep everything naturally align.
+ */
+};
+
+#endif /* __MYRI10GE_MCP_GEN_HEADER_H__ */
^ permalink raw reply
* [PATCH 2/5] myri10ge - Revive pci_find_ext_capability
From: Brice Goglin @ 2006-05-23 7:11 UTC (permalink / raw)
To: netdev; +Cc: gallatin, linux-kernel
In-Reply-To: <20060523070919.GB30499@myri.com>
[PATCH 2/5] myri10ge - Revive pci_find_ext_capability
This patch revives pci_find_ext_capability (has been disabled a couple month
ago since it was not used anywhere. See http://lkml.org/lkml/2006/1/20/247).
It will now be used by the myri10ge driver.
Signed-off-by: Brice Goglin <brice@myri.com>
Signed-off-by: Andrew J. Gallatin <gallatin@myri.com>
drivers/pci/pci.c | 3 +--
include/linux/pci.h | 2 ++
2 files changed, 3 insertions(+), 2 deletions(-)
--- linux-mm/drivers/pci/pci.c.old
+++ linux-mm/drivers/pci/pci.c
@@ -164,7 +164,6 @@ int pci_bus_find_capability(struct pci_b
return __pci_bus_find_cap(bus, devfn, hdr_type & 0x7f, cap);
}
-#if 0
/**
* pci_find_ext_capability - Find an extended capability
* @dev: PCI device to query
@@ -212,7 +211,7 @@ int pci_find_ext_capability(struct pci_d
return 0;
}
-#endif /* 0 */
+EXPORT_SYMBOL_GPL(pci_find_ext_capability);
/**
* pci_find_parent_resource - return resource region of parent bus of given region
--- linux-mm/include/linux/pci.h.old
+++ linux-mm/include/linux/pci.h
@@ -442,6 +442,7 @@ struct pci_dev *pci_find_device_reverse
struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
int pci_find_capability (struct pci_dev *dev, int cap);
int pci_find_next_capability (struct pci_dev *dev, u8 pos, int cap);
+int pci_find_ext_capability (struct pci_dev *dev, int cap);
struct pci_bus * pci_find_next_bus(const struct pci_bus *from);
struct pci_dev *pci_get_device (unsigned int vendor, unsigned int device, struct pci_dev *from);
@@ -664,6 +665,7 @@ static inline int pci_register_driver(st
static inline void pci_unregister_driver(struct pci_driver *drv) { }
static inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; }
static inline int pci_find_next_capability (struct pci_dev *dev, u8 post, int cap) { return 0; }
+static inline int pci_find_ext_capability (struct pci_dev *dev, int cap) {return 0; }
static inline const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) { return NULL; }
/* Power management related routines */
^ permalink raw reply
* [PATCH 1/5] Add Myricom PCI vendor ID to pci_ids.h
From: Brice Goglin @ 2006-05-23 7:10 UTC (permalink / raw)
To: netdev; +Cc: gallatin, linux-kernel
In-Reply-To: <20060523070919.GB30499@myri.com>
[PATCH 1/5] Add Myricom PCI vendor ID to pci_ids.h
Add PCI_VENDOR_ID_MYRICOM to pci_ids.h so that the myri10ge driver
and any other driver for Myricom hardware may use it.
Signed-off-by: Brice Goglin <brice@myri.com>
Signed-off-by: Andrew J. Gallatin <gallatin@myri.com>
include/linux/pci_ids.h | 1 +
1 file changed, 1 insertion(+)
--- linux-mm/include/linux/pci_ids.h.old 2006-05-19 04:34:30.000000000 +0200
+++ linux-mm/include/linux/pci_ids.h 2006-05-16 01:42:49.000000000 +0200
@@ -1841,6 +1841,7 @@
#define PCI_VENDOR_ID_SAMSUNG 0x144d
+#define PCI_VENDOR_ID_MYRICOM 0x14c1
#define PCI_VENDOR_ID_TITAN 0x14D2
#define PCI_DEVICE_ID_TITAN_010L 0x8001
^ permalink raw reply
* [PATCH 0/5] myri10ge - Myri-10G Ethernet driver - v3
From: Brice Goglin @ 2006-05-23 7:09 UTC (permalink / raw)
To: netdev; +Cc: gallatin, linux-kernel
[PATCH 0/5] myri10ge - Myri-10G Ethernet driver - v3
We think we have addressed all problems that have been reported after the
second submission. We would like this driver to be merged in the git-netdev
tree or in -mm, with the aim of being in 2.6.18.
The changes since the previous patchset are:
- drop all volatile, fix __iomem annotations and cleanup/remove some casts
- use kzalloc()
- use msleep() instead of udelay() or schedule_timeout()
- add an atomic flag to myri10ge_send_cmd() so that set_multicast_list
(which may be called from an atomic context) does not end up using
msleep when waiting for the command to complete
- split myri10ge_open() to make it more readable
- move Myricom vendor id to pci_ids.h
- place module_param and MODULE_PARM_DESC near the declaration of
the module parameter variable
- fix missing printk levels
- #define explicit names for our 0xffffffff special values
- reduce MYRI10GE_MCP{_ETHER,}_ into the new firmare prefix MXGEFW_
- fix indentation of goto labels
- various trivial things
myri10ge_xmit() is still very long since there is no nice way to
split it in multiple functions.
We still use several printk instead of dev_err/warn/info/printk
since there are some place where it is better to speak about "eth2"
instead of a bus id in the kernel messages.
The changes that were made in the previous submission can be found
in http://lkml.org/lkml/2006/5/17/236
The following patches introduce the myri10ge driver for Myricom Myri-10G
boards in Ethernet mode. The driver is called myri10ge. The patches are
against 2.6.17-rc4-mm3.
[1/5] add Myricom vendor id to pci_ids.h
[2/5] revive pci_find_ext_capability
[3/5] myri10ge driver header files.
[4/5] myri10ge driver core.
[5/5] add Kconfig and Makefile support for the myri10ge driver.
It also uses the following patches that have been sent on May 2
(http://lkml.org/lkml/2006/5/2/286 and 288) and merged into -mm.
add-__iowrite64_copy.patch
Introduce __iowrite64_copy.
add-pci_cap_id_vndr.patch
Add the vendor specific extended capability PCI_CAP_ID_VNDR.
The Myri-10G board operates as a regular PCI-Express Ethernet NIC.
If a firmware is available through hotplug, the driver will load it if its
version matches the driver requirements. If not, the driver will adopt the
running firmware that came in the board's eeprom if it is recent enough.
This driver supports in particular NAPI, power management, IPv4 and IPv6
checksum offload, 802.1q VLAN, and TCP Segmentation Offload.
Brice Goglin
^ permalink raw reply
* Re: [PATCH 2/2] ipg: redundancy with mii.h
From: jesse\(建興\) @ 2006-05-23 6:50 UTC (permalink / raw)
To: Francois Romieu, David Vrabel; +Cc: Pekka Enberg, linux-kernel, netdev, david
In-Reply-To: <20060521101620.GA28210@electric-eye.fr.zoreil.com>
Dear All:
I had tested the following patch of IP1000A
http://www.fr.zoreil.com/people/francois/misc/20060521-2.6.17-rc4-git-ip1000-test.patch
It works fine. We will discuss this driver with our members see if there are
anything need to add or modify.
Thanks for everybody's effort for our IP1000A.
Would anybody need some piece of IP1000A, please give me your address, we
will send some of it to you.
Thanks!
If any problem, please feel free to contact me. jesse@icplus.com.tw
Best Regards,
Jesse
----- Original Message -----
From: "Francois Romieu" <romieu@fr.zoreil.com>
To: "David Vrabel" <dvrabel@cantab.net>
Cc: "Pekka Enberg" <penberg@cs.helsinki.fi>; <linux-kernel@vger.kernel.org>;
<netdev@vger.kernel.org>; <david@pleyades.net>; <jesse@icplus.com.tw>
Sent: Sunday, May 21, 2006 6:16 PM
Subject: Re: [PATCH 2/2] ipg: redundancy with mii.h
David Vrabel <dvrabel@cantab.net> :
[...]
> 0007-ipg-plug-leaks-in-the-error-path-of-ipg_nic_open.txt broke receive
> since it was skipping the initialization of the Rx buffers. Patch
attached.
Oops. Applied to branch netdev-ipg of
git://electric-eye.fr.zoreil.com/home/romieu/linux-2.6.git
(please include the '-p' option in future invocation of diff)
I have put an updated patch against 2.6.17-rc4 for the whole driver at
http://www.fr.zoreil.com/people/francois/misc/20060521-2.6.17-rc4-git-ip1000-test.patch
--
Ueimor
^ permalink raw reply
* How can I disable ipv6 module only for oneway DVB receiving card?
From: SangOg Na @ 2006-05-23 5:59 UTC (permalink / raw)
To: netdev
Hi!
I am porting pentavalue driver to kernel 2.6.
But when unload module, the following messages are displayed
and I can't unload it.
unregister_netdevice: waiting for pentaval0 to become free. Usage count = 5
unregister_netdevice: waiting for pentaval0 to become free. Usage count = 5
I insert printk("refcnt=%d\n,atomic_read(&dev->refcnt)); at dev_hold and
dev_put
in include/linux/netdevice.h, and I found ipv6 module increase refcnt at 3
times,
but at unloading module, decrease one only one time.
I built linux kernel no ipv6, refcnt problem did not occur.
Can I disable ipv6 only for my pentaval driver?
Test kernel version is 2.6.15.
Bye!
^ permalink raw reply
* Re: DEAL
From: Roxanne Aguilar @ 2006-05-23 2:48 UTC (permalink / raw)
To: netdev
-S'ensationall revoolution in m'edicine!
-E'n'l'a'r'g'e your p''enis up to 10 cm or up to 4 inches!
-It's herbal solution what hasn't side effect, but has 100% guaranted results!
-Don't lose your chance and but know wihtout doubts, you will be impressed with results!
Clisk here: http://13catastrophes.info
effie symposia dandelion oman frightful sandblast convolute devilish immunization freeing
embroidery vulnerable because excellent bimodal howell voltmeter indignity phil
riverbank s's poisson begin chirp dreamlike charisma stabile ecumenic gerber preponderant
deport testate tolstoy cook committal department corpse assault
bicker coleridge edmund dessert aversion wrongdoer informal dogmatism
export ingrate prosthetic protrusive mynah query tunis benevolent
^ permalink raw reply
* Re: How to submit a new module to linux kernel?
From: Ian McDonald @ 2006-05-22 21:41 UTC (permalink / raw)
To: Erik Mouw; +Cc: #ZHOU BIN#, netdev
In-Reply-To: <20060522160650.GG28093@harddisk-recovery.com>
On 5/23/06, Erik Mouw <erik@harddisk-recovery.com> wrote:
> On Mon, May 22, 2006 at 03:18:12PM +0800, #ZHOU BIN# wrote:
> > I'm new in this mailing list. I implemented a new TCP congestion
> > control module for linux kernel 2.6.16.13.
> > Does anybody know how to apply for the integration of it into the
> > linux kernel? How long will this process take?
>
> See Documentation/SubmittingPatches in your kernel tree.
>
>
I would also add that for this type of patch a peer reviewed paper
outlining the congestion control work would be useful/needed.
Every person wants to improve TCP and has written a congestion control
mechanism (myself included!) but that doesn't mean it is worth
including in the kernel (mine certainly isn't....).
In particular for TCP congestion control you need to show in what
cases it is better than others, what cases it is worse and how fair is
it compared to other TCP flows.
Ian
--
Ian McDonald
Web: http://wand.net.nz/~iam4
Blog: http://imcdnzl.blogspot.com
WAND Network Research Group
Department of Computer Science
University of Waikato
New Zealand
^ permalink raw reply
* Re: Via Velocity minimum MTU size
From: Francois Romieu @ 2006-05-22 21:25 UTC (permalink / raw)
To: Jay Cliburn; +Cc: netdev
In-Reply-To: <1148266609.2329.16.camel@osprey.hogchain.net>
Jay Cliburn <jacliburn@bellsouth.net> :
[...]
> Can someone please explain why the driver might constrain the NIC to a
> minimum MTU size of 1500?
None that I remember of. The driver suffered some breakage when changing
the mtu while the interface is up but it has been fixed since february
(i.e. way before 2.6.16-1.2111_FC5).
>From a cursory glance at the documentation, I see no specific low limit
for the mtu. So... Does it break when you lower VELOCITY_MIN_MTU ?
--
Ueimor
^ permalink raw reply
* Re: Was change to ip_push_pending_frames intended to break udp (more specifically, WCCP?)
From: Vlad Yasevich @ 2006-05-22 21:10 UTC (permalink / raw)
To: Paul P Komkoff Jr; +Cc: Rick Jones, linux-kernel, netdev
In-Reply-To: <20060522202113.GA8196@stingr.net>
On Tue, 2006-05-23 at 00:21 +0400, Paul P Komkoff Jr wrote:
> Replying to Vlad Yasevich:
> > /* This is only to work around buggy Windows95/2000
> > * VJ compression implementations. If the ID field
> > * does not change, they drop every other packet in
> > * a TCP stream using header compression.
> > */
>
> Unfortunately, cisco IOS also complains that packets are "duplicate".
> And, regarding to your previous message on how to fix this - IIRC, if
> I do connect() on this socket, it will refuse to receive datagrams
> from hosts other than specified in connect(), and I will be unable to
> bind another socket to the same port on my side.
>
> That said, the only solution which is close to what been before, will
> be to keep one socket for receive, and create socket for each router I
> am communicating with, right?
Yewwww... I see you problem.
To me this sounds like a bug in IOS. I hope someone else would comment.
I did previously search a bunch of RFC and nowhere did a find a
requirement that IDs should be non-zero when DF bit is set. The only
time IP IDs are mentioned is in the fragmentation and reassembly
description.
-vlad
^ permalink raw reply
* Re: Was change to ip_push_pending_frames intended to break udp (more specifically, WCCP?)
From: Rick Jones @ 2006-05-22 21:06 UTC (permalink / raw)
To: Alan Cox; +Cc: Vlad Yasevich, Paul P Komkoff Jr, linux-kernel, netdev
In-Reply-To: <1148332293.17376.114.camel@localhost.localdomain>
Alan Cox wrote:
> On Llu, 2006-05-22 at 11:48 -0700, Rick Jones wrote:
>
>>ID of zero again? I thought that went away years ago? Anyway, given
>>the number of "helpful" devices out there willing to clear the DF bit,
>>fragment and forward, perhaps always setting the IP ID to 0, even if DF
>>is set, isn't such a good idea?
>
>
> Any device that clears DF is so terminally broken that you've already
> lost the battle the moment you bought it.
Perhaps, but still, always setting the IP datagram ID to the same value
even with the DF bit set seems contrary to the "conservative in what we
send" that is so often brought-forth as a reason a stack behaves the way
it does.
rick jones
^ permalink raw reply
* Re: Was change to ip_push_pending_frames intended to break udp (more specifically, WCCP?)
From: Alan Cox @ 2006-05-22 21:11 UTC (permalink / raw)
To: Rick Jones; +Cc: Vlad Yasevich, Paul P Komkoff Jr, linux-kernel, netdev
In-Reply-To: <4472078D.8010706@hp.com>
On Llu, 2006-05-22 at 11:48 -0700, Rick Jones wrote:
> ID of zero again? I thought that went away years ago? Anyway, given
> the number of "helpful" devices out there willing to clear the DF bit,
> fragment and forward, perhaps always setting the IP ID to 0, even if DF
> is set, isn't such a good idea?
Any device that clears DF is so terminally broken that you've already
lost the battle the moment you bought it.
Alan
^ permalink raw reply
* Re: wireless-dev updated
From: Jiri Benc @ 2006-05-22 20:32 UTC (permalink / raw)
To: John W. Linville; +Cc: netdev
In-Reply-To: <20060522195545.GF13964@tuxdriver.com>
Mon, 22 May 2006 15:55:50 -0400, John W. Linville wrote:
> On Mon, May 22, 2006 at 02:42:14PM +0200, Jiri Benc wrote:
> > This patch is wrong, it uses incorrect ioctl number (collides with
> > userspace MLME).
>
> Since you had posted that in the same thread, I had presumed that
> you had accounted for that... :-)
I realized it only after I had added Jouni's patch to my tree :-(
> As you wish...you know I love to shake-up the history in my trees... :-(
Sorry for that. I will try to send pull requests more frequently. Hm,
hopefully. Maybe I will be stuck with those #$@ sysfs patches forever.
Thanks,
Jiri
--
Jiri Benc
SUSE Labs
^ permalink raw reply
* Re: Was change to ip_push_pending_frames intended to break udp (more specifically, WCCP?)
From: Paul P Komkoff Jr @ 2006-05-22 20:21 UTC (permalink / raw)
To: Vlad Yasevich; +Cc: Rick Jones, Paul P Komkoff Jr, linux-kernel, netdev
In-Reply-To: <1148324083.15323.325.camel@galen.zko.hp.com>
Replying to Vlad Yasevich:
> /* This is only to work around buggy Windows95/2000
> * VJ compression implementations. If the ID field
> * does not change, they drop every other packet in
> * a TCP stream using header compression.
> */
Unfortunately, cisco IOS also complains that packets are "duplicate".
And, regarding to your previous message on how to fix this - IIRC, if
I do connect() on this socket, it will refuse to receive datagrams
from hosts other than specified in connect(), and I will be unable to
bind another socket to the same port on my side.
That said, the only solution which is close to what been before, will
be to keep one socket for receive, and create socket for each router I
am communicating with, right?
--
Paul P 'Stingray' Komkoff Jr // http://stingr.net/key <- my pgp key
This message represents the official view of the voices in my head
^ permalink raw reply
* Re: wireless-dev updated
From: John W. Linville @ 2006-05-22 19:55 UTC (permalink / raw)
To: Jiri Benc; +Cc: netdev
In-Reply-To: <20060522144214.02f0adb2@griffin.suse.cz>
On Mon, May 22, 2006 at 02:42:14PM +0200, Jiri Benc wrote:
> On Fri, 19 May 2006 15:15:54 -0400, John W. Linville wrote:
> > Jiri Benc:
> > d80211: switching management interface on/off
>
> This patch is wrong, it uses incorrect ioctl number (collides with
> userspace MLME).
Since you had posted that in the same thread, I had presumed that
you had accounted for that... :-)
> Would it be possible to drop these patches and pull from 'up' branch of
> my tree?
As you wish...you know I love to shake-up the history in my trees... :-(
John
---
The following changes since commit b3783fbd3cc23106a21995b1bebde396047b2759:
John W. Linville:
Merge branch 'from-linus'
are found in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-dev.git
Jiri Benc:
d80211: don't config uninitialized interface
d80211: fix recursive locking
d80211: switching management interface on/off
d80211: use alloc_netdev
d80211: fix is_ieee80211_device
John W. Linville:
bcm43xx-d80211: remove out-dated docs and script
Merge branch 'up' of git://git.kernel.org/.../jbenc/dscape
Jouni Malinen:
d80211: Add support for user space client MLME
Michael Buesch:
bcm43xx-d80211: rewrite interface handling
bcm43xx-d80211: measure the channel change time
bcm43xx-d80211: Fix access to non-existent PHY registers
Michael Wu:
d80211: Don't discriminate against 802.11b drivers
Stefano Brivio:
bcm43xx-d80211: check for valid MAC address in SPROM
bcm43xx-d80211: add PCI ID for bcm4319
Documentation/networking/bcm43xx-d80211-HOWTO.txt | 67 -----
Documentation/networking/bcm43xx-d80211.txt | 31 ---
drivers/net/wireless/d80211/bcm43xx/bcm43xx.h | 42 +++
drivers/net/wireless/d80211/bcm43xx/bcm43xx_leds.c | 2
drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c | 245 +++++++++++++-------
drivers/net/wireless/d80211/bcm43xx/bcm43xx_phy.c | 6
net/d80211/hostapd_ioctl.h | 2
net/d80211/ieee80211.c | 167 +++++++-------
net/d80211/ieee80211_i.h | 16 +
net/d80211/ieee80211_iface.c | 81 +++++--
net/d80211/ieee80211_ioctl.c | 63 +++++
net/d80211/ieee80211_sta.c | 9 -
scripts/bcm43xx-d80211-sta_up.sh | 80 -------
13 files changed, 417 insertions(+), 394 deletions(-)
delete mode 100644 Documentation/networking/bcm43xx-d80211-HOWTO.txt
delete mode 100644 Documentation/networking/bcm43xx-d80211.txt
delete mode 100755 scripts/bcm43xx-d80211-sta_up.sh
diff --git a/Documentation/networking/bcm43xx-d80211-HOWTO.txt b/Documentation/networking/bcm43xx-d80211-HOWTO.txt
deleted file mode 100644
index 90e4ddf..0000000
--- a/Documentation/networking/bcm43xx-d80211-HOWTO.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-**** ****
-**** HOWTO get bcm43xx-dscape running ****
-**** ****
-
-This is a port of the bcm43xx driver for the devicescape ieee802.11
-stack. The devicescape ieee802.11 stack is an advanced out-of-mainline
-802.11 protocol module.
-I will call the "devicescape ieee802.11 stack" simply "dscape" in the
-following text.
-
-
-*** Setting up the bcm43xx driver with dscape is currently non-trivial,
-*** as several modifications to the kernel and the userland
-*** wpa_supplicant tool are required. We are working on it...
-
-
-1) You need to patch the kernel with the ieee80211-devicescape stack
- and the bcm43xx-dscape port.
- Both are available in one cummulative at:
- ftp://ftp.bu3sch.de/bcm43xx-snapshots/all-in-one/bcm43xx-dscape/
- Read the instructions on http://bcm43xx.berlios.de/ for how to apply
- this patch.
-
- Patch the kernel, compile and install it.
- When configuring, enable
- "Networking/Generic IEEE 802.11 Networking Stack (dscape)"
- and
- "Device Drivers/Network device support/Wireless LAN (non-hamradio)/Broadcom BCM43xx wireless support (DeviceScape stack)"
- Reboot.
-
-2) Set up a wpa_supplicant config file in /etc/wpa_supplicant.conf
- Here is an example for an AES WPA encrypted network:
-
- # WPA AES encryption
- ctrl_interface=/var/run/wpa_supplicant
- network={
- ssid="ACCESSPOINT_SSID"
- key_mgmt=WPA-PSK
- proto=WPA
- pairwise=CCMP TKIP
- group=CCMP TKIP
- psk="MY PASSPHRASE"
- priority=3
- }
-
-3) Take a bottle of your favourite beer, open it and take a swallow.
-
-4) Now it's time to bring the driver up.
- Do modprobe bcm43xx-d80211 to load the driver.
- There is an ugly bash script to bring the driver up after insmod.
- It is found in the "scripts" subdirectory of the patched kernel
- tree and is called "bcm43xx-d80211-sta_up.sh".
- Call scripts/bcm43xx-d80211-sta_up.sh --help to get some usage information.
- It may suffice to call scripts/bcm43xx-d80211-sta_up.sh without any parameters. See
- the help. Default parameters, which are used when called without parameters,
- are explained there.
-
-5) If you want to access the internet, make sure your default route
- is correctly set up with your gateway's IP:
- route add default gw 192.168.xxx.xxx
-
-6) Take another swallow from your bottle of beer and test if it works:
- ping www.kernel.org
-
-7) If it works, drink the rest of your beer. Otherwise read this HOWTO again,
- and again and again. Complain to bcm43xx-dev@lists.berlios.de, if it still
- does not work.
diff --git a/Documentation/networking/bcm43xx-d80211.txt b/Documentation/networking/bcm43xx-d80211.txt
deleted file mode 100644
index 89b101d..0000000
--- a/Documentation/networking/bcm43xx-d80211.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-
- BCM43xx Linux Driver Project
- ============================
-
-About this software
--------------------
-
-The goal of this project is to develop a linux driver for Broadcom
-BCM43xx chips, based on the specification at
-http://bcm-specs.sipsolutions.net/
-
-The project page is http://bcm43xx.berlios.de/
-
-This is a port of the bcm43xx driver for the devicescape ieee802.11
-stack.
-
-Requirements
-------------
-
-1) Linux Kernel 2.6.16 or later
- http://www.kernel.org/
-
- See Documentation/networking/bcm43xx-d80211-HOWTO.txt for further
- instructions.
-
-2) Firmware Files
-
- Please try bcm43xx-fwcutter. It can extract the firmware from various
- binary driver files and supports driver files from Windows, MacOS and
- Linux. You can get bcm43xx-fwcutter from http://bcm43xx.berlios.de/.
- Also, bcm43xx-fwcutter comes with a README file for further instructions.
diff --git a/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h b/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h
index 97bde56..a4a97c2 100644
--- a/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h
+++ b/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h
@@ -626,10 +626,32 @@ struct bcm43xx_key {
u8 algorithm;
};
+struct bcm43xx_interface {
+ /* Opaque ID of the operating interface (!= monitor
+ * interface) from the ieee80211 subsystem.
+ * Do not modify.
+ */
+ int if_id;
+ /* MAC address. */
+ u8 *mac_addr;
+ /* Current BSSID (if any). */
+ u8 *bssid;
+
+ /* Interface type. (IEEE80211_IF_TYPE_XXX) */
+ int type;
+ /* Counter of active monitor interfaces. */
+ int monitor;
+ /* Is the card operating in AP, STA or IBSS mode? */
+ unsigned int operating:1;
+ /* Promisc mode active?
+ * Note that (monitor != 0) implies promisc.
+ */
+ unsigned int promisc:1;
+};
+
struct bcm43xx_private {
struct ieee80211_hw *ieee;
struct ieee80211_low_level_stats ieee_stats;
- int iw_mode;
struct net_device *net_dev;
struct pci_dev *pci_dev;
@@ -653,6 +675,13 @@ struct bcm43xx_private {
short_slot:1, /* TRUE, if short slot timing is enabled. */
firmware_norelease:1; /* Do not release the firmware. Used on suspend. */
+ /* One physical device can have one operating interface
+ * and a virtually infinite amount of monitoring interfaces.
+ * This keeps track of the interfaces and the corresponding
+ * hardware modes.
+ */
+ struct bcm43xx_interface interface;
+ /* Various statistics about the physical device. */
struct bcm43xx_stats stats;
/* Bus type we are connected to.
@@ -716,8 +745,6 @@ #endif
/* Informational stuff. */
char nick[IW_ESSID_MAX_SIZE + 1];
- u8 bssid[ETH_ALEN];
- int interfaces;
/* encryption/decryption */
u16 security_offset;
@@ -854,6 +881,15 @@ struct bcm43xx_private * dev_to_bcm(stru
return bcm;
}
+/* Is the device operating in a specified mode (IEEE80211_IF_TYPE_XXX). */
+static inline
+int bcm43xx_is_mode(struct bcm43xx_private *bcm, int type)
+{
+ if (type == IEEE80211_IF_TYPE_MNTR)
+ return !!bcm->interface.monitor;
+ return (bcm->interface.operating &&
+ bcm->interface.type == type);
+}
static inline
u16 bcm43xx_read16(struct bcm43xx_private *bcm, u16 offset)
diff --git a/drivers/net/wireless/d80211/bcm43xx/bcm43xx_leds.c b/drivers/net/wireless/d80211/bcm43xx/bcm43xx_leds.c
index 02bb08d..439bae4 100644
--- a/drivers/net/wireless/d80211/bcm43xx/bcm43xx_leds.c
+++ b/drivers/net/wireless/d80211/bcm43xx/bcm43xx_leds.c
@@ -217,7 +217,7 @@ void bcm43xx_leds_update(struct bcm43xx_
bcm43xx_led_blink_stop(led, 0);
continue;
case BCM43xx_LED_APTRANSFER:
- if (bcm->iw_mode == IW_MODE_MASTER) {
+ if (bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_AP)) {
if (transferring) {
interval = BCM43xx_LEDBLINK_FAST;
turn_on = 1;
diff --git a/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
index 93fc6f4..88f7dfe 100644
--- a/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
@@ -128,6 +128,8 @@ static struct pci_device_id bcm43xx_pci_
{ PCI_VENDOR_ID_BROADCOM, 0x4307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
/* Broadcom 4318 802.11b/g */
{ PCI_VENDOR_ID_BROADCOM, 0x4318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ /* Broadcom 4319 802.11b/g */
+ { PCI_VENDOR_ID_BROADCOM, 0x4319, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
/* Broadcom 4306 802.11b/g */
{ PCI_VENDOR_ID_BROADCOM, 0x4320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
/* Broadcom 4306 802.11a */
@@ -354,6 +356,33 @@ void bcm43xx_tsf_write(struct bcm43xx_pr
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
}
+static void bcm43xx_measure_channel_change_time(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio;
+ u64 start, stop;
+ unsigned long flags;
+ u8 oldchan, testchan;
+
+ /* We (ab)use the bcm43xx TSF timer to measure the time needed
+ * to switch channels. This information is handed over to
+ * the ieee80211 subsystem.
+ * Time is measured in microseconds.
+ */
+
+ bcm43xx_lock_mmio(bcm, flags);
+ radio = bcm43xx_current_radio(bcm);
+ oldchan = radio->channel;
+ testchan = (oldchan == 6) ? 7 : 6;
+ bcm43xx_tsf_read(bcm, &start);
+ bcm43xx_radio_selectchannel(bcm, testchan, 0);
+ bcm43xx_tsf_read(bcm, &stop);
+ bcm43xx_radio_selectchannel(bcm, oldchan, 0);
+ bcm43xx_unlock_mmio(bcm, flags);
+
+ assert(stop > start);
+ bcm->ieee->channel_change_time = stop - start;
+}
+
static
void bcm43xx_macfilter_set(struct bcm43xx_private *bcm,
u16 offset,
@@ -378,18 +407,26 @@ void bcm43xx_macfilter_set(struct bcm43x
static void bcm43xx_macfilter_clear(struct bcm43xx_private *bcm,
u16 offset)
{
- const u8 zero_addr[ETH_ALEN] = { 0 };
+ static const u8 zero_addr[ETH_ALEN] = { 0 };
bcm43xx_macfilter_set(bcm, offset, zero_addr);
}
static void bcm43xx_write_mac_bssid_templates(struct bcm43xx_private *bcm)
{
- const u8 *mac = (const u8 *)(bcm->net_dev->dev_addr);
- const u8 *bssid = bcm->bssid;
+ static const u8 zero_addr[ETH_ALEN] = { 0 };
+ const u8 *mac = NULL;
+ const u8 *bssid = NULL;
u8 mac_bssid[ETH_ALEN * 2];
int i;
+ bssid = bcm->interface.bssid;
+ if (!bssid)
+ bssid = zero_addr;
+ mac = bcm->interface.mac_addr;
+ if (!mac)
+ mac = zero_addr;
+
memcpy(mac_bssid, mac, ETH_ALEN);
memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN);
@@ -1438,15 +1475,15 @@ generate_new:
static void handle_irq_ps(struct bcm43xx_private *bcm)
{
- if (bcm->iw_mode == IW_MODE_MASTER) {
+ if (bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_AP)) {
///TODO: PS TBTT
} else {
if (1/*FIXME: the last PSpoll frame was sent successfully */)
bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
}
- if (bcm->iw_mode == IW_MODE_ADHOC)
+ bcm->reg124_set_0x4 = 0;
+ if (bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_IBSS))
bcm->reg124_set_0x4 = 1;
- //FIXME else set to false?
}
static void handle_irq_reg124(struct bcm43xx_private *bcm)
@@ -1456,7 +1493,6 @@ static void handle_irq_reg124(struct bcm
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD,
bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD)
| 0x4);
- //FIXME: reset reg124_set_0x4 to false?
}
static void handle_irq_pmq(struct bcm43xx_private *bcm)
@@ -1509,7 +1545,9 @@ static void handle_irq_beacon(struct bcm
* Request the 80211 subsystem to generate a new beacon
* frame and use it as template.
*/
- bcm->cached_beacon = ieee80211_beacon_get(bcm->net_dev, 0, &control);
+ bcm->cached_beacon = ieee80211_beacon_get(bcm->net_dev,
+ bcm->interface.if_id,
+ &control);
if (unlikely(!bcm->cached_beacon)) {
dprintkl(KERN_WARNING PFX "Could not generate beacon template.\n");
goto ack;
@@ -1603,7 +1641,7 @@ #endif /* CONFIG_BCM43XX_D80211_DEBUG*/
}
if (reason & BCM43xx_IRQ_BEACON) {
- if (bcm->iw_mode == IW_MODE_MASTER)
+ if (bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_AP))
handle_irq_beacon(bcm);
bcmirq_handled(BCM43xx_IRQ_BEACON);
}
@@ -2147,55 +2185,49 @@ void bcm43xx_mac_suspend(struct bcm43xx_
printkl(KERN_ERR PFX "MAC suspend failed\n");
}
-void bcm43xx_set_iwmode(struct bcm43xx_private *bcm,
- int iw_mode)
+static void bcm43xx_select_opmode(struct bcm43xx_private *bcm)
{
- struct net_device *net_dev = bcm->net_dev;
u32 status;
u16 value;
- bcm->iw_mode = iw_mode;
- if (iw_mode == IW_MODE_MONITOR)
- net_dev->type = ARPHRD_IEEE80211;
- else
- net_dev->type = ARPHRD_ETHER;
-
status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
- /* Reset status to infrastructured mode */
- status &= ~(BCM43xx_SBF_MODE_AP | BCM43xx_SBF_MODE_MONITOR);
+ /* Reset status to default STA mode */
+ status &= ~BCM43xx_SBF_MODE_AP;
+ status &= ~BCM43xx_SBF_MODE_MONITOR;
status &= ~BCM43xx_SBF_MODE_PROMISC;
status |= BCM43xx_SBF_MODE_NOTADHOC;
-/* FIXME: Always enable promisc mode, until we get the MAC filters working correctly. */
-status |= BCM43xx_SBF_MODE_PROMISC;
-
- switch (iw_mode) {
- case IW_MODE_MONITOR:
+ if (bcm->interface.operating) {
+ switch (bcm->interface.type) {
+ case IEEE80211_IF_TYPE_AP:
+ status |= BCM43xx_SBF_MODE_AP;
+ break;
+ case IEEE80211_IF_TYPE_IBSS:
+ status &= ~BCM43xx_SBF_MODE_NOTADHOC;
+ break;
+ case IEEE80211_IF_TYPE_STA:
+ case IEEE80211_IF_TYPE_MNTR:
+ case IEEE80211_IF_TYPE_WDS:
+ break;
+ default:
+ assert(0);
+ }
+ }
+ if (bcm->interface.monitor) {
status |= BCM43xx_SBF_MODE_MONITOR;
status |= BCM43xx_SBF_MODE_PROMISC;
- break;
- case IW_MODE_ADHOC:
- status &= ~BCM43xx_SBF_MODE_NOTADHOC;
- break;
- case IW_MODE_MASTER:
- status |= BCM43xx_SBF_MODE_AP;
- break;
- case IW_MODE_SECOND:
- case IW_MODE_REPEAT:
- TODO(); /* TODO */
- break;
- case IW_MODE_INFRA:
- /* nothing to be done here... */
- break;
- default:
- dprintk(KERN_ERR PFX "Unknown mode in set_iwmode: %d\n", iw_mode);
}
- if (net_dev->flags & IFF_PROMISC)
+ if (bcm->interface.promisc)
status |= BCM43xx_SBF_MODE_PROMISC;
+
+/* FIXME: Always enable promisc mode, until we get the MAC filters working correctly. */
+status |= BCM43xx_SBF_MODE_PROMISC;
+
bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
value = 0x0002;
- if (iw_mode != IW_MODE_ADHOC && iw_mode != IW_MODE_MASTER) {
+ if ((status & BCM43xx_SBF_MODE_NOTADHOC) &&
+ !(status & BCM43xx_SBF_MODE_AP)) {
if (bcm->chip_id == 0x4306 && bcm->chip_rev == 3)
value = 0x0064;
else
@@ -2291,7 +2323,7 @@ static int bcm43xx_chip_init(struct bcm4
bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0074, 0x0000);
/* Initially set the wireless operation mode. */
- bcm43xx_set_iwmode(bcm, bcm->iw_mode);
+ bcm43xx_select_opmode(bcm);
if (bcm->current_core->rev < 3) {
bcm43xx_write16(bcm, 0x060E, 0x0000);
@@ -2599,27 +2631,6 @@ out:
return err;
}
-static void bcm43xx_gen_bssid(struct bcm43xx_private *bcm)
-{
- const u8 *mac = (const u8*)(bcm->net_dev->dev_addr);
- u8 *bssid = bcm->bssid;
-
- switch (bcm->iw_mode) {
- case IW_MODE_ADHOC:
- random_ether_addr(bssid);
- break;
- case IW_MODE_MASTER:
- case IW_MODE_INFRA:
- case IW_MODE_REPEAT:
- case IW_MODE_SECOND:
- case IW_MODE_MONITOR:
- memcpy(bssid, mac, ETH_ALEN);
- break;
- default:
- assert(0);
- }
-}
-
static void bcm43xx_rate_memory_write(struct bcm43xx_private *bcm,
u16 rate,
int is_ofdm)
@@ -2746,7 +2757,6 @@ static int bcm43xx_wireless_core_init(st
/* Maximum Contention Window */
bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff);
- bcm43xx_gen_bssid(bcm);
bcm43xx_write_mac_bssid_templates(bcm);
if (bcm->current_core->rev >= 5)
@@ -3725,6 +3735,7 @@ static int bcm43xx_init_board(struct bcm
dprintk(KERN_INFO PFX "80211 cores initialized\n");
bcm43xx_setup_modes(bcm);
bcm43xx_security_init(bcm);
+ bcm43xx_measure_channel_change_time(bcm);
ieee80211_update_hw(bcm->net_dev, bcm->ieee);
ieee80211_netif_oper(bcm->net_dev, NETIF_ATTACH);
ieee80211_netif_oper(bcm->net_dev, NETIF_START);
@@ -3934,7 +3945,7 @@ static int bcm43xx_attach_board(struct b
bcm43xx_pctl_set_crystal(bcm, 0);
/* Set the MAC address in the networking subsystem */
- if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)
+ if (is_valid_ether_addr(bcm->sprom.et1macaddr))
memcpy(bcm->net_dev->dev_addr, bcm->sprom.et1macaddr, 6);
else
memcpy(bcm->net_dev->dev_addr, bcm->sprom.il0macaddr, 6);
@@ -4169,13 +4180,8 @@ #endif /* CONFIG_NET_POLL_CONTROLLER */
static int bcm43xx_net_open(struct net_device *net_dev)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
- int res;
- res = bcm43xx_init_board(bcm);
- if (!res)
- return res;
- bcm43xx_set_iwmode(bcm, bcm->iw_mode);
- return 0;
+ return bcm43xx_init_board(bcm);
}
static int bcm43xx_net_stop(struct net_device *net_dev)
@@ -4194,33 +4200,91 @@ static int bcm43xx_add_interface(struct
struct ieee80211_if_init_conf *conf)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int err = -EOPNOTSUPP;
+
+ bcm43xx_lock_mmio(bcm, flags);
- if (bcm->interfaces > 0)
- return -ENOBUFS;
if (conf->type == IEEE80211_IF_TYPE_MNTR) {
- bcm->iw_mode = IW_MODE_MONITOR;
+ bcm->interface.monitor++;
} else {
- if (memcmp(bcm->net_dev->dev_addr, conf->mac_addr, ETH_ALEN) != 0)
- return -EADDRNOTAVAIL;
- if (conf->type == IEEE80211_IF_TYPE_STA)
- bcm->iw_mode = IW_MODE_INFRA;
- else if (conf->type == IEEE80211_IF_TYPE_IBSS)
- bcm->iw_mode = IW_MODE_ADHOC;
- else if (conf->type == IEEE80211_IF_TYPE_AP)
- bcm->iw_mode = IW_MODE_MASTER;
- else
- return -EOPNOTSUPP;
+ if (bcm->interface.operating)
+ goto out_unlock;
+ bcm->interface.operating = 1;
+ bcm->interface.if_id = conf->if_id;
+ bcm->interface.mac_addr = conf->mac_addr;
+ bcm->interface.type = conf->type;
}
- bcm->interfaces++;
- return 0;
+ if (bcm->initialized)
+ bcm43xx_select_opmode(bcm);
+ err = 0;
+
+ dprintk(KERN_INFO PFX "Virtual interface added "
+ "(type: 0x%08X, ID: %d, MAC: "
+ BCM43xx_MACFMT ")\n",
+ conf->type, conf->if_id,
+ BCM43xx_MACARG(conf->mac_addr));
+
+out_unlock:
+ bcm43xx_unlock_mmio(bcm, flags);
+
+ return err;
}
static void bcm43xx_remove_interface(struct net_device *net_dev,
struct ieee80211_if_init_conf *conf)
{
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
- bcm->interfaces--;
+ bcm43xx_lock_mmio(bcm, flags);
+ if (conf->type == IEEE80211_IF_TYPE_MNTR) {
+ bcm->interface.monitor--;
+ assert(bcm->interface.monitor >= 0);
+ } else
+ bcm->interface.operating = 0;
+ if (bcm->initialized)
+ bcm43xx_select_opmode(bcm);
+ bcm43xx_unlock_mmio(bcm, flags);
+
+ dprintk(KERN_INFO PFX "Virtual interface removed "
+ "(type: 0x%08X, ID: %d, MAC: "
+ BCM43xx_MACFMT ")\n",
+ conf->type, conf->if_id,
+ BCM43xx_MACARG(conf->mac_addr));
+}
+
+static int bcm43xx_config_interface(struct net_device *net_dev,
+ int if_id,
+ struct ieee80211_if_conf *conf)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+
+ bcm43xx_lock(bcm, flags);
+ if (conf->type != IEEE80211_IF_TYPE_MNTR) {
+ assert(bcm->interface.if_id == if_id);
+ bcm->interface.bssid = conf->bssid;
+ }
+ bcm43xx_unlock(bcm, flags);
+
+ return 0;
+}
+
+static void bcm43xx_set_multicast_list(struct net_device *net_dev,
+ unsigned short netflags,
+ int mc_count)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if (bcm->interface.promisc != !!(netflags & IFF_PROMISC)) {
+ bcm->interface.promisc = !!(netflags & IFF_PROMISC);
+ if (bcm->initialized)
+ bcm43xx_select_opmode(bcm);
+ }
+ bcm43xx_unlock_mmio(bcm, flags);
}
/* Initialization of struct net_device, just after allocation. */
@@ -4294,6 +4358,7 @@ #endif
ieee->name = KBUILD_MODNAME;
ieee->host_gen_beacon = 1;
ieee->rx_includes_fcs = 1;
+ ieee->monitor_during_oper = 1;
ieee->tx = bcm43xx_net_hard_start_xmit;
ieee->open = bcm43xx_net_open;
ieee->stop = bcm43xx_net_stop;
@@ -4301,6 +4366,8 @@ #endif
ieee->remove_interface = bcm43xx_remove_interface;
ieee->reset = bcm43xx_net_reset;
ieee->config = bcm43xx_net_config;
+ ieee->config_interface = bcm43xx_config_interface;
+ ieee->set_multicast_list = bcm43xx_set_multicast_list;
//TODO ieee->set_key = bcm43xx_net_set_key;
ieee->get_stats = bcm43xx_net_get_stats;
ieee->queues = 1;
diff --git a/drivers/net/wireless/d80211/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/d80211/bcm43xx/bcm43xx_phy.c
index 45da79d..413b2ed 100644
--- a/drivers/net/wireless/d80211/bcm43xx/bcm43xx_phy.c
+++ b/drivers/net/wireless/d80211/bcm43xx/bcm43xx_phy.c
@@ -94,7 +94,7 @@ void bcm43xx_raw_phy_lock(struct bcm43xx
bcm43xx_mac_suspend(bcm);
spin_lock(&phy->lock);
} else {
- if (bcm->iw_mode != IW_MODE_MASTER)
+ if (!bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_AP))
bcm43xx_power_saving_ctl_bits(bcm, -1, 1);
}
phy->is_locked = 1;
@@ -111,7 +111,7 @@ void bcm43xx_raw_phy_unlock(struct bcm43
bcm43xx_mac_enable(bcm);
}
} else {
- if (bcm->iw_mode != IW_MODE_MASTER)
+ if (!bcm43xx_is_mode(bcm, IEEE80211_IF_TYPE_AP))
bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
}
phy->is_locked = 0;
@@ -1287,7 +1287,7 @@ static void bcm43xx_phy_initg(struct bcm
if (radio->revision == 8)
bcm43xx_phy_write(bcm, 0x0805, 0x3230);
bcm43xx_phy_init_pctl(bcm);
- if (bcm->chip_id == 0x4306 && bcm->chip_package != 2) {
+ if (bcm->chip_id == 0x4306 && bcm->chip_package == 2) {
bcm43xx_phy_write(bcm, 0x0429,
bcm43xx_phy_read(bcm, 0x0429) & 0xBFFF);
bcm43xx_phy_write(bcm, 0x04C3,
diff --git a/net/d80211/hostapd_ioctl.h b/net/d80211/hostapd_ioctl.h
index a462688..7c7305c 100644
--- a/net/d80211/hostapd_ioctl.h
+++ b/net/d80211/hostapd_ioctl.h
@@ -91,6 +91,8 @@ enum {
PRISM2_PARAM_KEY_MGMT = 1040,
PRISM2_PARAM_RADAR_DETECT = 1043,
PRISM2_PARAM_SPECTRUM_MGMT = 1044,
+ PRISM2_PARAM_USER_SPACE_MLME = 1045,
+ PRISM2_PARAM_MGMT_IF = 1046,
/* NOTE: Please try to coordinate with other active development
* branches before allocating new param numbers so that each new param
* will be unique within all branches and the allocated number will not
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index ffb7985..b850fb7 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -59,6 +59,8 @@ static int rate_control_initialize(struc
static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len);
+static int ieee80211_mgmt_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
struct ieee80211_key_conf *
ieee80211_key_data2conf(struct ieee80211_local *local,
@@ -1097,7 +1099,8 @@ __ieee80211_tx_prepare(struct ieee80211_
static int inline is_ieee80211_device(struct net_device *dev)
{
return (dev->wireless_handlers ==
- (struct iw_handler_def *) &ieee80211_iw_handler_def);
+ (struct iw_handler_def *) &ieee80211_iw_handler_def) ||
+ (dev->hard_start_xmit == ieee80211_mgmt_start_xmit);
}
/* Device in tx->dev has a reference added; use dev_put(tx->dev) when
@@ -1754,7 +1757,7 @@ int ieee80211_if_config(struct net_devic
struct ieee80211_local *local = dev->priv;
struct ieee80211_if_conf conf;
- if (!local->hw->config_interface)
+ if (!local->hw->config_interface || !netif_running(dev))
return 0;
memset(&conf, 0, sizeof(conf));
@@ -1954,8 +1957,6 @@ static inline int identical_mac_addr_all
{
return (type1 == IEEE80211_IF_TYPE_MNTR ||
type2 == IEEE80211_IF_TYPE_MNTR ||
- type1 == IEEE80211_IF_TYPE_MGMT ||
- type2 == IEEE80211_IF_TYPE_MGMT ||
(type1 == IEEE80211_IF_TYPE_AP &&
type2 == IEEE80211_IF_TYPE_WDS) ||
(type1 == IEEE80211_IF_TYPE_WDS &&
@@ -1990,6 +1991,20 @@ static int ieee80211_master_stop(struct
return 0;
}
+static int ieee80211_mgmt_open(struct net_device *dev)
+{
+ struct ieee80211_local *local = dev->priv;
+
+ if (!netif_running(local->mdev))
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static int ieee80211_mgmt_stop(struct net_device *dev)
+{
+ return 0;
+}
+
/* Check if running monitor interfaces should go to a "soft monitor" mode
* and switch them if necessary. */
static inline void ieee80211_start_soft_monitor(struct ieee80211_local *local)
@@ -2032,7 +2047,6 @@ static int ieee80211_open(struct net_dev
struct net_device *ndev = nsdata->dev;
if (ndev != dev && ndev != local->mdev &&
- ndev != local->apdev &&
netif_running(ndev) &&
memcmp(dev->dev_addr, ndev->dev_addr, ETH_ALEN) == 0 &&
!identical_mac_addr_allowed(sdata->type, nsdata->type)) {
@@ -2075,8 +2089,11 @@ static int ieee80211_open(struct net_dev
res = local->hw->open(sdata->master);
if (res == 0) {
res = dev_open(sdata->master);
- if (res && local->hw->stop)
- local->hw->stop(sdata->master);
+ if (res) {
+ if (local->hw->stop)
+ local->hw->stop(sdata->master);
+ } else if (local->apdev)
+ dev_open(local->apdev);
}
if (res) {
if (local->hw->remove_interface)
@@ -2089,6 +2106,8 @@ static int ieee80211_open(struct net_dev
if (sdata->type == IEEE80211_IF_TYPE_MNTR)
local->monitors++;
+ else
+ ieee80211_if_config(dev);
netif_start_queue(dev);
return 0;
@@ -2119,6 +2138,8 @@ static int ieee80211_stop(struct net_dev
if (local->open_count == 0) {
ieee80211_stop_scan(sdata->master);
dev_close(sdata->master);
+ if (local->apdev)
+ dev_close(local->apdev);
if (local->hw->stop)
local->hw->stop(sdata->master);
}
@@ -2367,6 +2388,10 @@ ieee80211_rx_mgmt(struct net_device *dev
if (msg_type != ieee80211_msg_monitor)
dev = local->apdev;
+ if (!dev) {
+ dev_kfree_skb(skb);
+ return;
+ }
skb->dev = dev;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -3116,8 +3141,9 @@ ieee80211_rx_h_mgmt(struct ieee80211_txr
{
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
- if (sdata->type == IEEE80211_IF_TYPE_STA ||
- sdata->type == IEEE80211_IF_TYPE_IBSS) {
+ if ((sdata->type == IEEE80211_IF_TYPE_STA ||
+ sdata->type == IEEE80211_IF_TYPE_IBSS) &&
+ !rx->local->user_space_mlme) {
ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
} else {
/* Management frames are sent to hostapd for processing */
@@ -3998,15 +4024,31 @@ void ieee80211_if_setup(struct net_devic
dev->destructor = ieee80211_if_free;
}
+void ieee80211_if_mgmt_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+ dev->hard_start_xmit = ieee80211_mgmt_start_xmit;
+ dev->change_mtu = ieee80211_change_mtu_apdev;
+ dev->get_stats = ieee80211_get_stats;
+ dev->open = ieee80211_mgmt_open;
+ dev->stop = ieee80211_mgmt_stop;
+ dev->type = ARPHRD_IEEE80211_PRISM;
+ dev->hard_header_parse = header_parse_80211;
+ dev->tx_queue_len = 0;
+ dev->destructor = ieee80211_if_free;
+}
-static void ieee80211_precalc_rates(struct ieee80211_hw *hw)
+static void ieee80211_precalc_modes(struct ieee80211_hw *hw,
+ struct ieee80211_local *local)
{
struct ieee80211_hw_modes *mode;
struct ieee80211_rate *rate;
int m, r;
+ local->hw_modes = 0;
for (m = 0; m < hw->num_modes; m++) {
mode = &hw->modes[m];
+ local->hw_modes |= 1 << mode->mode;
for (r = 0; r < mode->num_rates; r++) {
rate = &mode->rates[r];
rate->rate_inv = CHAN_UTIL_RATE_LCM / rate->rate;
@@ -4018,57 +4060,43 @@ static void ieee80211_precalc_rates(stru
struct net_device *ieee80211_alloc_hw(size_t priv_data_len,
void (*setup)(struct net_device *))
{
- struct net_device *apdev, *mdev;
+ struct net_device *mdev;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
- int alloc_size;
+ int priv_size;
- /* Ensure 32-bit alignment of our private data and hw private data.
- * Each net_device is followed by a sub_if_data which which is used
- * for wds/vlan information; it is aligned as well.
+ /* Ensure 32-byte alignment of our private data and hw private data.
+ * Each net_device is followed by a sub_if_data which is used for
+ * interface specific information.
*
* Sample memory map looks something like:
*
* 0000 *****************
* * net_dev *
- * 015c *****************
+ * 0160 *****************
* * sub_if *
- * 017c *****************
+ * 0180 *****************
* * local *
- * 0b84 *****************
+ * 0b80 *****************
* * hw_priv *
* 1664 *****************
- * * ap net_dev *
- * 17c0 *****************
- * * sub_if *
- * *****************
*/
- alloc_size = sizeof(struct net_device) +
- sizeof(struct ieee80211_sub_if_data) + 3 +
- sizeof(struct ieee80211_local) + 3 +
- priv_data_len + 3 +
- sizeof(struct net_device) + 3 +
- sizeof(struct ieee80211_sub_if_data) + 3 +
- 4096;
- mdev = (struct net_device *) kzalloc(alloc_size, GFP_KERNEL);
+ priv_size = ((sizeof(struct ieee80211_sub_if_data) +
+ NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST) +
+ ((sizeof(struct ieee80211_local) +
+ NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST) +
+ priv_data_len;
+ mdev = alloc_netdev(priv_size, "wmaster%d", ether_setup);
if (mdev == NULL)
return NULL;
- mdev->priv = (struct net_device *)
- ((char *) mdev +
- ((sizeof(struct net_device) + 3) & ~3) +
- ((sizeof(struct ieee80211_sub_if_data) + 3) & ~3));
+ mdev->priv = (char *)netdev_priv(mdev) +
+ ((sizeof(struct ieee80211_sub_if_data) +
+ NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
local = mdev->priv;
- local->hw_priv = (void *)
- ((char *) local + ((sizeof(struct ieee80211_local) + 3) & ~3));
- apdev = (struct net_device *)
- ((char *) local->hw_priv + ((priv_data_len + 3) & ~3));
-
- ether_setup(mdev);
- memcpy(mdev->name, "wmaster%d", 10);
-
- if (strlen(mdev->name) + 2 >= sizeof(mdev->name))
- goto fail;
+ local->hw_priv = (char *)local +
+ ((sizeof(struct ieee80211_local) +
+ NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
local->dev_index = -1;
local->mdev = mdev;
@@ -4087,7 +4115,7 @@ struct net_device *ieee80211_alloc_hw(si
local->rate_ctrl_num_down = RATE_CONTROL_NUM_DOWN;
local->scan.in_scan = 0;
- local->hw_modes = (unsigned int) -1;
+ local->enabled_modes = (unsigned int) -1;
init_timer(&local->scan.timer); /* clear it out */
@@ -4104,28 +4132,6 @@ struct net_device *ieee80211_alloc_hw(si
ieee80211_if_init(mdev);
- apdev = (struct net_device *)
- ((char *) local->hw_priv + ((priv_data_len + 3) & ~3));
- local->apdev = apdev;
- ether_setup(apdev);
- apdev->priv = local;
- apdev->hard_start_xmit = ieee80211_mgmt_start_xmit;
- apdev->change_mtu = ieee80211_change_mtu_apdev;
- apdev->get_stats = ieee80211_get_stats;
- apdev->open = ieee80211_open;
- apdev->stop = ieee80211_stop;
- apdev->type = ARPHRD_IEEE80211_PRISM;
- apdev->hard_header_parse = header_parse_80211;
- apdev->tx_queue_len = 0;
- sprintf(apdev->name, "%sap", mdev->name);
-
- sdata = IEEE80211_DEV_TO_SUB_IF(apdev);
- sdata->type = IEEE80211_IF_TYPE_MGMT;
- sdata->dev = apdev;
- sdata->master = mdev;
- sdata->local = local;
- list_add_tail(&sdata->list, &local->sub_if_list);
-
mdev->hard_start_xmit = ieee80211_master_start_xmit;
mdev->wireless_handlers =
(struct iw_handler_def *) &ieee80211_iw_handler_def;
@@ -4155,10 +4161,6 @@ struct net_device *ieee80211_alloc_hw(si
setup(mdev);
return mdev;
-
- fail:
- ieee80211_free_hw(mdev);
- return NULL;
}
@@ -4193,15 +4195,11 @@ int ieee80211_register_hw(struct net_dev
sta_info_start(local);
- result = register_netdev(local->apdev);
- if (result < 0)
- goto fail_1st_dev;
-
if (hw->fraglist)
dev->features |= NETIF_F_FRAGLIST;
result = register_netdev(dev);
if (result < 0)
- goto fail_2nd_dev;
+ goto fail_dev;
if (rate_control_initialize(local) < 0) {
printk(KERN_DEBUG "%s: Failed to initialize rate control "
@@ -4226,9 +4224,7 @@ int ieee80211_register_hw(struct net_dev
fail_rate:
unregister_netdev(dev);
-fail_2nd_dev:
- unregister_netdev(local->apdev);
-fail_1st_dev:
+fail_dev:
sta_info_stop(local);
ieee80211_unregister_sysfs(local);
fail_sysfs:
@@ -4247,17 +4243,11 @@ int ieee80211_update_hw(struct net_devic
if (hw->queues == 0)
hw->queues = 1;
- memcpy(local->apdev->dev_addr, dev->dev_addr, ETH_ALEN);
- local->apdev->base_addr = dev->base_addr;
- local->apdev->irq = dev->irq;
- local->apdev->mem_start = dev->mem_start;
- local->apdev->mem_end = dev->mem_end;
-
if (!hw->modes || !hw->modes->channels || !hw->modes->rates ||
!hw->modes->num_channels || !hw->modes->num_rates)
return -1;
- ieee80211_precalc_rates(hw);
+ ieee80211_precalc_modes(hw, local);
local->conf.phymode = hw->modes[0].mode;
local->curr_rates = hw->modes[0].rates;
local->num_curr_rates = hw->modes[0].num_rates;
@@ -4291,6 +4281,9 @@ void ieee80211_unregister_hw(struct net_
del_timer_sync(&local->scan_timer);
ieee80211_rx_bss_list_deinit(dev);
+ if (local->apdev)
+ ieee80211_if_del(local->apdev);
+
list_for_each_safe(ptr, n, &local->sub_if_list) {
struct ieee80211_sub_if_data *sdata =
list_entry(ptr, struct ieee80211_sub_if_data, list);
@@ -4323,7 +4316,7 @@ void ieee80211_unregister_hw(struct net_
void ieee80211_free_hw(struct net_device *dev)
{
- kfree(dev);
+ free_netdev(dev);
}
diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h
index ee0b399..94e151d 100644
--- a/net/d80211/ieee80211_i.h
+++ b/net/d80211/ieee80211_i.h
@@ -307,11 +307,7 @@ #define NUM_DEFAULT_KEYS 4
int channel_use_raw;
};
-#define IEEE80211_DEV_TO_SUB_IF(dev) ((struct ieee80211_sub_if_data *) \
- ((char *)(dev) + ((sizeof(struct net_device) + 3) & ~3)))
-#define IEEE80211_SUB_IF_TO_DEV(sub_if) ((struct net_device *) \
- ((char *)(sub_if) - ((sizeof(struct net_device) + 3) & ~3)))
-
+#define IEEE80211_DEV_TO_SUB_IF(dev) netdev_priv(dev)
struct ieee80211_local {
struct ieee80211_hw *hw;
@@ -409,7 +405,6 @@ #define IEEE80211_IRQSAFE_QUEUE_LIMIT 12
int scan_oper_antenna_max;
u8 scan_ssid[IEEE80211_MAX_SSID_LEN];
size_t scan_ssid_len;
- int scan_skip_11b;
struct list_head sta_bss_list;
struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE];
spinlock_t sta_bss_lock;
@@ -500,8 +495,12 @@ #endif /* CONFIG_D80211_DEBUG_COUNTERS *
int wifi_wme_noack_test;
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
- unsigned int hw_modes; /* bitfield of allowed hardware modes;
+ unsigned int enabled_modes; /* bitfield of allowed modes;
+ * (1 << MODE_*) */
+ unsigned int hw_modes; /* bitfield of supported hardware modes;
* (1 << MODE_*) */
+
+ int user_space_mlme;
};
@@ -518,6 +517,7 @@ void ieee80211_prepare_rates(struct net_
void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx);
int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
void ieee80211_if_setup(struct net_device *dev);
+void ieee80211_if_mgmt_setup(struct net_device *dev);
/* ieee80211_ioctl.c */
int ieee80211_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -595,6 +595,8 @@ int ieee80211_if_remove(struct net_devic
void ieee80211_if_free(struct net_device *dev);
void ieee80211_if_flush(struct net_device *dev);
void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata);
+int ieee80211_if_add_mgmt(struct net_device *dev);
+void ieee80211_if_del_mgmt(struct net_device *dev);
/* ieee80211_sysfs.c */
int ieee80211_register_sysfs(struct ieee80211_local *local);
diff --git a/net/d80211/ieee80211_iface.c b/net/d80211/ieee80211_iface.c
index f3ce45f..6631738 100644
--- a/net/d80211/ieee80211_iface.c
+++ b/net/d80211/ieee80211_iface.c
@@ -31,16 +31,12 @@ int ieee80211_if_add(struct net_device *
struct net_device *ndev, *tmp_dev;
struct ieee80211_local *local = dev->priv;
struct ieee80211_sub_if_data *sdata = NULL, *sdata_parent;
- int alloc_size;
int ret;
int i;
ASSERT_RTNL();
- /* ensure 32-bit alignment of our private data and hw private data */
- alloc_size = sizeof(struct net_device) + 3 +
- sizeof(struct ieee80211_sub_if_data) + 3;
-
- ndev = *new_dev = (struct net_device *) kzalloc(alloc_size, GFP_KERNEL);
+ ndev = *new_dev = alloc_netdev(sizeof(struct ieee80211_sub_if_data),
+ "", ieee80211_if_setup);
if (ndev == NULL)
return -ENOMEM;
@@ -68,7 +64,6 @@ int ieee80211_if_add(struct net_device *
ndev->mem_start = dev->mem_start;
ndev->mem_end = dev->mem_end;
ndev->flags = dev->flags & IFF_MULTICAST;
- ieee80211_if_setup(ndev);
sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
sdata->type = IEEE80211_IF_TYPE_AP;
@@ -88,11 +83,66 @@ int ieee80211_if_add(struct net_device *
return 0;
fail:
- kfree(ndev);
+ free_netdev(ndev);
*new_dev = NULL;
return ret;
}
+int ieee80211_if_add_mgmt(struct net_device *dev)
+{
+ struct net_device *ndev;
+ struct ieee80211_local *local = dev->priv;
+ struct ieee80211_sub_if_data *sdata, *nsdata;
+ int ret;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ ASSERT_RTNL();
+
+ ndev = alloc_netdev(sizeof(struct ieee80211_sub_if_data), "",
+ ieee80211_if_mgmt_setup);
+ if (ndev == NULL)
+ return -ENOMEM;
+ ret = dev_alloc_name(ndev, "wmgmt%d");
+ if (ret)
+ goto fail;
+
+ ndev->priv = local;
+ memcpy(ndev->dev_addr, dev->dev_addr, ETH_ALEN);
+ ndev->base_addr = dev->base_addr;
+ ndev->irq = dev->irq;
+ ndev->mem_start = dev->mem_start;
+ ndev->mem_end = dev->mem_end;
+
+ nsdata = IEEE80211_DEV_TO_SUB_IF(ndev);
+ nsdata->type = IEEE80211_IF_TYPE_MGMT;
+ nsdata->master = local->mdev;
+ nsdata->dev = ndev;
+ nsdata->local = local;
+ ieee80211_if_sdata_init(nsdata);
+
+ ret = register_netdevice(ndev);
+ if (ret)
+ goto fail;
+ if (local->open_count > 0)
+ dev_open(ndev);
+ local->apdev = ndev;
+ return 0;
+fail:
+ free_netdev(ndev);
+ return ret;
+}
+
+void ieee80211_if_del_mgmt(struct net_device *dev)
+{
+ struct ieee80211_local *local = dev->priv;
+ struct net_device *apdev;
+
+ ASSERT_RTNL();
+ apdev = local->apdev;
+ local->apdev = NULL;
+ unregister_netdevice(apdev);
+}
+
void ieee80211_if_set_type(struct net_device *dev, int type)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -244,9 +294,8 @@ void __ieee80211_if_del(struct ieee80211
list_del(&sdata->list);
ieee80211_proc_deinit_virtual(dev);
unregister_netdevice(dev);
- /* Default data device and management device are allocated with the
- * master device. All other devices are separately allocated and will
- * be freed by net_device->destructor (i. e. ieee80211_if_free). */
+ /* Except master interface, the net_device will be freed by
+ * net_device->destructor (i. e. ieee80211_if_free). */
}
/* Must be called with rtnl lock held. */
@@ -263,8 +312,7 @@ int ieee80211_if_remove(struct net_devic
list_for_each_entry_safe(sdata, n, &local->sub_if_list, list) {
if ((sdata->type == id || id == -1) &&
strcmp(name, sdata->dev->name) == 0 &&
- sdata->dev != local->mdev &&
- sdata->dev != local->apdev) {
+ sdata->dev != local->mdev) {
__ieee80211_if_del(local, sdata);
return 0;
}
@@ -277,7 +325,7 @@ void ieee80211_if_free(struct net_device
struct ieee80211_local *local = dev->priv;
BUG_ON(dev == local->mdev || dev == local->apdev);
- kfree(dev);
+ free_netdev(dev);
}
/* Must be called with rtnl lock held. */
@@ -298,6 +346,9 @@ void ieee80211_if_del(struct net_device
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
rtnl_lock();
- __ieee80211_if_del(local, sdata);
+ if (sdata->type == IEEE80211_IF_TYPE_MGMT)
+ ieee80211_if_del_mgmt(local->mdev);
+ else
+ __ieee80211_if_del(local, sdata);
rtnl_unlock();
}
diff --git a/net/d80211/ieee80211_ioctl.c b/net/d80211/ieee80211_ioctl.c
index 5d31a8f..30dfc76 100644
--- a/net/d80211/ieee80211_ioctl.c
+++ b/net/d80211/ieee80211_ioctl.c
@@ -937,7 +937,8 @@ static int ieee80211_ioctl_add_if(struct
ieee80211_if_set_type(new_dev, IEEE80211_IF_TYPE_WDS);
res = ieee80211_if_update_wds(new_dev, wds->remote_addr);
if (res)
- ieee80211_if_del(new_dev);
+ __ieee80211_if_del(dev->priv,
+ IEEE80211_DEV_TO_SUB_IF(new_dev));
return res;
} else if (param->u.if_info.type == HOSTAP_IF_VLAN) {
if (left < sizeof(struct hostapd_if_vlan))
@@ -950,7 +951,8 @@ static int ieee80211_ioctl_add_if(struct
#if 0
res = ieee80211_if_update_vlan(new_dev, vlan->id);
if (res)
- ieee80211_if_del(new_dev);
+ __ieee80211_if_del(dev->priv,
+ IEEE80211_DEV_TO_SUB_IF(new_dev));
#endif
return res;
} else if (param->u.if_info.type == HOSTAP_IF_BSS) {
@@ -1048,10 +1050,14 @@ static int ieee80211_ioctl_scan_req(stru
struct prism2_hostapd_param *param,
int param_len)
{
+ struct ieee80211_local *local = dev->priv;
u8 *pos = param->u.scan_req.ssid;
int left = param_len - ((u8 *) pos - (u8 *) param);
int len = param->u.scan_req.ssid_len;
+ if (local->user_space_mlme)
+ return -EOPNOTSUPP;
+
if (left < len || len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
@@ -1076,8 +1082,12 @@ static int ieee80211_ioctl_sta_get_state
static int ieee80211_ioctl_mlme(struct net_device *dev,
struct prism2_hostapd_param *param)
{
+ struct ieee80211_local *local = dev->priv;
struct ieee80211_sub_if_data *sdata;
+ if (local->user_space_mlme)
+ return -EOPNOTSUPP;
+
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type != IEEE80211_IF_TYPE_STA &&
sdata->type != IEEE80211_IF_TYPE_IBSS)
@@ -1136,6 +1146,10 @@ #endif
static int ieee80211_set_gen_ie(struct net_device *dev, u8 *ie, size_t len)
{
struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_local *local = dev->priv;
+
+ if (local->user_space_mlme)
+ return -EOPNOTSUPP;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
@@ -1699,7 +1713,7 @@ int ieee80211_ioctl_siwfreq(struct net_d
if (chan->flag & IEEE80211_CHAN_W_SCAN &&
((freq->e == 0 && chan->chan == freq->m) ||
(freq->e > 0 && nfreq == chan->freq)) &&
- (local->hw_modes & (1 << mode->mode))) {
+ (local->enabled_modes & (1 << mode->mode))) {
/* Use next_mode as the mode preference to
* resolve non-unique channel numbers. */
if (set && mode->mode != local->next_mode)
@@ -1745,6 +1759,7 @@ static int ieee80211_ioctl_siwessid(stru
struct iw_request_info *info,
struct iw_point *data, char *ssid)
{
+ struct ieee80211_local *local = dev->priv;
struct ieee80211_sub_if_data *sdata;
size_t len = data->length;
@@ -1754,8 +1769,16 @@ static int ieee80211_ioctl_siwessid(stru
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
- sdata->type == IEEE80211_IF_TYPE_IBSS)
+ sdata->type == IEEE80211_IF_TYPE_IBSS) {
+ if (local->user_space_mlme) {
+ if (len > IEEE80211_MAX_SSID_LEN)
+ return -EINVAL;
+ memcpy(sdata->u.sta.ssid, ssid, len);
+ sdata->u.sta.ssid_len = len;
+ return 0;
+ }
return ieee80211_sta_set_ssid(dev, ssid, len);
+ }
if (sdata->type == IEEE80211_IF_TYPE_AP) {
memcpy(sdata->u.ap.ssid, ssid, len);
@@ -1804,11 +1827,17 @@ static int ieee80211_ioctl_siwap(struct
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra)
{
+ struct ieee80211_local *local = dev->priv;
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->type == IEEE80211_IF_TYPE_STA ||
sdata->type == IEEE80211_IF_TYPE_IBSS) {
+ if (local->user_space_mlme) {
+ memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data,
+ ETH_ALEN);
+ return 0;
+ }
return ieee80211_sta_set_bssid(dev, (u8 *) &ap_addr->sa_data);
} else if (sdata->type == IEEE80211_IF_TYPE_WDS) {
if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
@@ -2447,7 +2476,7 @@ static int ieee80211_ioctl_prism2_param(
break;
case PRISM2_PARAM_HW_MODES:
- local->hw_modes = value;
+ local->enabled_modes = value;
break;
case PRISM2_PARAM_CREATE_IBSS:
@@ -2469,6 +2498,19 @@ static int ieee80211_ioctl_prism2_param(
case PRISM2_PARAM_SPECTRUM_MGMT:
local->conf.spect_mgmt = value;
break;
+ case PRISM2_PARAM_MGMT_IF:
+ if (value == 1) {
+ if (local->apdev == NULL)
+ ret = ieee80211_if_add_mgmt(local->mdev);
+ } else if (value == 0) {
+ if (local->apdev)
+ ieee80211_if_del_mgmt(local->mdev);
+ } else
+ ret = -EINVAL;
+ break;
+ case PRISM2_PARAM_USER_SPACE_MLME:
+ local->user_space_mlme = value;
+ break;
default:
ret = -EOPNOTSUPP;
break;
@@ -2620,7 +2662,7 @@ static int ieee80211_ioctl_get_prism2_pa
break;
case PRISM2_PARAM_HW_MODES:
- *param = local->hw_modes;
+ *param = local->enabled_modes;
break;
case PRISM2_PARAM_CREATE_IBSS:
@@ -2651,6 +2693,15 @@ static int ieee80211_ioctl_get_prism2_pa
else
*param = !!sdata->u.sta.wmm_enabled;
break;
+ case PRISM2_PARAM_MGMT_IF:
+ if (local->apdev)
+ *param = local->apdev->ifindex;
+ else
+ ret = -ENOENT;
+ break;
+ case PRISM2_PARAM_USER_SPACE_MLME:
+ *param = local->user_space_mlme;
+ break;
default:
ret = -EOPNOTSUPP;
diff --git a/net/d80211/ieee80211_sta.c b/net/d80211/ieee80211_sta.c
index 2720f1d..af58013 100644
--- a/net/d80211/ieee80211_sta.c
+++ b/net/d80211/ieee80211_sta.c
@@ -2462,13 +2462,13 @@ static void ieee80211_sta_scan_timer(uns
}
return;
}
- skip = !(local->hw_modes & (1 << mode->mode));
+ skip = !(local->enabled_modes & (1 << mode->mode));
chan = &mode->channels[local->scan_channel_idx];
if (!(chan->flag & IEEE80211_CHAN_W_SCAN) ||
(sdata->type == IEEE80211_IF_TYPE_IBSS &&
!(chan->flag & IEEE80211_CHAN_W_IBSS)) ||
- (local->hw_modes & (1 << MODE_IEEE80211G) &&
- mode->mode == MODE_IEEE80211B && local->scan_skip_11b))
+ (local->hw_modes & local->enabled_modes &
+ (1 << MODE_IEEE80211G) && mode->mode == MODE_IEEE80211B))
skip = 1;
if (!skip) {
@@ -2566,7 +2566,6 @@ int ieee80211_sta_req_scan(struct net_de
memcpy(local->scan_ssid, ssid, ssid_len);
} else
local->scan_ssid_len = 0;
- local->scan_skip_11b = 1; /* FIX: clear this is 11g is not supported */
local->scan_state = SCAN_SET_CHANNEL;
local->scan_hw_mode_idx = 0;
local->scan_channel_idx = 0;
@@ -2592,7 +2591,7 @@ ieee80211_sta_scan_result(struct net_dev
bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE))
return current_ev;
- if (!(local->hw_modes & (1 << bss->hw_mode)))
+ if (!(local->enabled_modes & (1 << bss->hw_mode)))
return current_ev;
if (local->scan_flags & IEEE80211_SCAN_WPA_ONLY &&
diff --git a/scripts/bcm43xx-d80211-sta_up.sh b/scripts/bcm43xx-d80211-sta_up.sh
deleted file mode 100755
index a21ddff..0000000
--- a/scripts/bcm43xx-d80211-sta_up.sh
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/bin/bash
-
-if [ "$1" == "--help" ] || [ "$1" == "-h" ]; then
- echo "$0, an ugly script to configure and bring up a STA (802.11 station)"
- echo "device for the linux devicescape 802.11 stack."
- echo
- echo "Usage:"
- echo "$0 [ wlan_device local_ip wpasupplicant_config ]"
- echo
- echo "Examples:"
- echo "Run with default parameters: $0"
- echo "Manually define parameters: $0 wlan0 192.168.1.1 ./wpasupp.conf"
- echo
- echo "Default parameters are: $0 wlan0 192.168.1.101 /etc/wpa_supplicant.conf"
- exit 1
-fi
-
-wlan_dev="$1"
-ip_addr="$2"
-wpasupp_conf="$3"
-
-if [ -z "$wlan_dev" ]; then
- wlan_dev="wlan0"
-fi
-if [ -z "$sta_dev" ]; then
- sta_dev="sta0"
-fi
-if [ -z "$ip_addr" ]; then
- ip_addr="192.168.1.101"
-fi
-if [ -z "$wpasupp_conf" ]; then
- wpasupp_conf="/etc/wpa_supplicant.conf"
-fi
-
-idx=$(echo $wlan_dev | awk '{ gsub("[^0-9]", "", $0); printf($0); }')
-if [ -z "$idx" ]; then
- echo "Invalid wlan_device parameter \"$wlan_dev\". Example: wlan0"
- exit 1
-fi
-sta_dev="sta$idx"
-phy_dev="phy$idx"
-
-function run()
-{
- echo "$@"
- $@
- res=$?
- if [ $res -ne 0 ]; then
- echo "FAILED ($res)"
- exit 1
- fi
-}
-
-if [ -z "$(grep -e bcm43xx /proc/modules)" ]; then
- echo "ERROR: bcm43xx module not loaded."
- exit 1
-fi
-
-killall wpa_supplicant 2>/dev/null
-echo -n "$sta_dev" > /sys/class/ieee80211/${phy_dev}/add_iface
-if [ $? -ne 0 ]; then
- echo "ERROR: Could not add STA device."
- exit 1
-fi
-run iwconfig $wlan_dev.11 mode managed
-run ifconfig $wlan_dev.11 up
-
-hwaddr="$(ifconfig | grep $wlan_dev.11 | awk '{print $NF}')"
-run ifconfig $sta_dev hw ether $hwaddr
-run ifconfig $sta_dev $ip_addr
-run ifconfig $sta_dev up
-run iwconfig $sta_dev mode managed
-
-run wpa_supplicant -B -Dwext -i$sta_dev -c$wpasupp_conf
-
-echo
-echo "You may want to set the default route, now:"
-echo " route add default gw GATEWAY_IP_ADDRESS"
-
-exit 0
--
John W. Linville
linville@tuxdriver.com
^ permalink raw reply related
* Please pull 'upstream' branch of wireless-2.6
From: John W. Linville @ 2006-05-22 19:19 UTC (permalink / raw)
To: jeff; +Cc: netdev
In-Reply-To: <20060522191759.GD13964@tuxdriver.com>
The following changes since commit 01d654d25d23fb73deb7904ce1c0b3a0f5fc2908:
John W. Linville:
Merge branch 'upstream-fixes' into upstream
are found in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git upstream
Marcin Juszkiewicz:
hostap: new pcmcia IDs
drivers/net/wireless/hostap/hostap_cs.c | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
index 55bed92..db03dc2 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -881,6 +881,12 @@ static struct pcmcia_device_id hostap_cs
PCMCIA_DEVICE_PROD_ID12(
"ZoomAir 11Mbps High", "Rate wireless Networking",
0x273fe3db, 0x32a1eaee),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Pretec", "CompactWLAN Card 802.11b", "2.5",
+ 0x1cadd3e5, 0xe697636c, 0x7a5bfcf1),
+ PCMCIA_DEVICE_PROD_ID123(
+ "U.S. Robotics", "IEEE 802.11b PC-CARD", "Version 01.02",
+ 0xc7b8df9d, 0x1700d087, 0x4b74baa0),
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids);
--
John W. Linville
linville@tuxdriver.com
^ permalink raw reply related
* Please pull 'upstream-fixes' branch of wireless-2.6
From: John W. Linville @ 2006-05-22 19:18 UTC (permalink / raw)
To: jeff; +Cc: netdev, akpm
The following changes since commit 353b28bafd1b962359a866ff263a7fad833d29a1:
David S. Miller:
[SPARC]: Add robust futex syscall entries.
are found in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git upstream-fixes
Florin Malita:
orinoco: possible null pointer dereference in orinoco_rx_monitor()
drivers/net/wireless/orinoco.c | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 06523e2..c2d0b09 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -812,7 +812,6 @@ static void orinoco_rx_monitor(struct ne
if (datalen > IEEE80211_DATA_LEN + 12) {
printk(KERN_DEBUG "%s: oversized monitor frame, "
"data length = %d\n", dev->name, datalen);
- err = -EIO;
stats->rx_length_errors++;
goto update_stats;
}
@@ -821,8 +820,7 @@ static void orinoco_rx_monitor(struct ne
if (!skb) {
printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n",
dev->name);
- err = -ENOMEM;
- goto drop;
+ goto update_stats;
}
/* Copy the 802.11 header to the skb */
--
John W. Linville
linville@tuxdriver.com
^ permalink raw reply related
* [PATCH] sky2: fix jumbo packet support
From: Stephen Hemminger @ 2006-05-22 19:03 UTC (permalink / raw)
To: Jeff Garzik; +Cc: netdev
The truncate threshold calculation to prevent receiver from getting stuck
was incorrect, and it didn't take into account the upper limit on bits
in the register so the jumbo packet support was broken.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
--- sky2.orig/drivers/net/sky2.c
+++ sky2/drivers/net/sky2.c
@@ -979,6 +979,7 @@ static int sky2_rx_start(struct sky2_por
struct sky2_hw *hw = sky2->hw;
unsigned rxq = rxqaddr[sky2->port];
int i;
+ unsigned thresh;
sky2->rx_put = sky2->rx_next = 0;
sky2_qset(hw, rxq);
@@ -1003,9 +1004,21 @@ static int sky2_rx_start(struct sky2_por
sky2_rx_add(sky2, re->mapaddr);
}
- /* Truncate oversize frames */
- sky2_write16(hw, SK_REG(sky2->port, RX_GMF_TR_THR), sky2->rx_bufsize - 8);
- sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_ON);
+
+ /*
+ * The receiver hangs if it receives frames larger than the
+ * packet buffer. As a workaround, truncate oversize frames, but
+ * the register is limited to 9 bits, so if you do frames > 2052
+ * you better get the MTU right!
+ */
+ thresh = (sky2->rx_bufsize - 8) / sizeof(u32);
+ if (thresh > 0x1ff)
+ sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_OFF);
+ else {
+ sky2_write16(hw, SK_REG(sky2->port, RX_GMF_TR_THR), thresh);
+ sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_ON);
+ }
+
/* Tell chip about available buffers */
sky2_write16(hw, Y2_QADDR(rxq, PREF_UNIT_PUT_IDX), sky2->rx_put);
^ permalink raw reply
* Re: Was change to ip_push_pending_frames intended to break udp (more specifically, WCCP?)
From: Vlad Yasevich @ 2006-05-22 18:54 UTC (permalink / raw)
To: Rick Jones; +Cc: Paul P Komkoff Jr, linux-kernel, netdev
In-Reply-To: <4472078D.8010706@hp.com>
On Mon, 2006-05-22 at 11:48 -0700, Rick Jones wrote:
> > IP id is set to 0 on unconnected sockets when the DF bit is set (path
> > mtu discovery is enabled). Try issuing a connect() in your application
> > and see if the ids are increasing again.
>
> ID of zero again? I thought that went away years ago? Anyway, given
> the number of "helpful" devices out there willing to clear the DF bit,
> fragment and forward, perhaps always setting the IP ID to 0, even if DF
> is set, isn't such a good idea?
Hey... I just report what I find... ;)
I had to look at this code a bit for some SCTP cases as well and this
seems to be how it works. Here a comment from the code for the case
of the DF bit being set
/* This is only to work around buggy Windows95/2000
* VJ compression implementations. If the ID field
* does not change, they drop every other packet in
* a TCP stream using header compression.
*/
-vlad
>
> rick jones
>
^ permalink raw reply
* Re: netlink vs. debugfs (was Re: [Patch 0/6] statistics infrastructure)
From: Evgeniy Polyakov @ 2006-05-22 18:53 UTC (permalink / raw)
To: Balbir Singh
Cc: Tim Bird, Andrew Morton, Martin Peschke, linux-kernel, netdev
In-Reply-To: <20060522183359.GA26551@in.ibm.com>
On Tue, May 23, 2006 at 12:04:00AM +0530, Balbir Singh (balbir@in.ibm.com) wrote:
> Anybody else want to take a shot in comparing the two?
Netlink is always presented in the kernel, so no need to make
additional dependencies for special FS.
But number of netlink sockets is not that big, so use new one if you
create really generic mechanism, or consider using connector/gennetlink.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: [PATCH 2.6.17] tg3: Add some missing rx error counters
From: Jeff Garzik @ 2006-05-22 18:50 UTC (permalink / raw)
To: Michael Chan; +Cc: davem, netdev
In-Reply-To: <1148316876.4780.110.camel@rh4>
Michael Chan wrote:
> Add some missing rx error counters for 5705 and newer chips.
>
> Update version to 3.58.
>
> Signed-off-by: Michael Chan <mchan@broadcom.com>
>
>
> diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
> index e1b33a2..49ad60b 100644
> --- a/drivers/net/tg3.c
> +++ b/drivers/net/tg3.c
> @@ -69,8 +69,8 @@
>
> #define DRV_MODULE_NAME "tg3"
> #define PFX DRV_MODULE_NAME ": "
> -#define DRV_MODULE_VERSION "3.57"
> -#define DRV_MODULE_RELDATE "Apr 28, 2006"
> +#define DRV_MODULE_VERSION "3.58"
> +#define DRV_MODULE_RELDATE "May 22, 2006"
>
> #define TG3_DEF_MAC_MODE 0
> #define TG3_DEF_RX_MODE 0
> @@ -6488,6 +6488,10 @@ static void tg3_periodic_fetch_stats(str
> TG3_STAT_ADD32(&sp->rx_frame_too_long_errors, MAC_RX_STATS_FRAME_TOO_LONG);
> TG3_STAT_ADD32(&sp->rx_jabbers, MAC_RX_STATS_JABBERS);
> TG3_STAT_ADD32(&sp->rx_undersize_packets, MAC_RX_STATS_UNDERSIZE);
> +
> + TG3_STAT_ADD32(&sp->rxbds_empty, RCVLPC_NO_RCV_BD_CNT);
> + TG3_STAT_ADD32(&sp->rx_discards, RCVLPC_IN_DISCARDS_CNT);
> + TG3_STAT_ADD32(&sp->rx_errors, RCVLPC_IN_ERRORS_CNT);
ACK
^ permalink raw reply
* Re: Was change to ip_push_pending_frames intended to break udp (more specifically, WCCP?)
From: Rick Jones @ 2006-05-22 18:48 UTC (permalink / raw)
To: Vlad Yasevich; +Cc: Paul P Komkoff Jr, linux-kernel, netdev
In-Reply-To: <1148322152.15322.299.camel@galen.zko.hp.com>
> IP id is set to 0 on unconnected sockets when the DF bit is set (path
> mtu discovery is enabled). Try issuing a connect() in your application
> and see if the ids are increasing again.
ID of zero again? I thought that went away years ago? Anyway, given
the number of "helpful" devices out there willing to clear the DF bit,
fragment and forward, perhaps always setting the IP ID to 0, even if DF
is set, isn't such a good idea?
rick jones
^ permalink raw reply
* Re: [PATCH 2.6.17 1/2] bnx2: Fix bug in bnx2_nvram_write()
From: Jeff Garzik @ 2006-05-22 18:48 UTC (permalink / raw)
To: Michael Chan; +Cc: davem, netdev
In-Reply-To: <1148317309.4780.116.camel@rh4>
Michael Chan wrote:
> Fix a bug in bnx2_nvram_write() caused by a counter variable not
> correctly incremented by 4.
>
> Signed-off-by: Michael Chan <mchan@broadcom.com>
ACK patches 1-2
^ permalink raw reply
* [PATCH 2.6.17 2/2] bnx2: Use kmalloc instead of array
From: Michael Chan @ 2006-05-22 17:02 UTC (permalink / raw)
To: davem; +Cc: netdev
Use kmalloc() instead of a local array in bnx2_nvram_write().
Update version to 1.4.40.
Signed-off-by: Michael Chan <mchan@broadcom.com>
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 509f104..54161ae 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -55,8 +55,8 @@
#define DRV_MODULE_NAME "bnx2"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "1.4.39"
-#define DRV_MODULE_RELDATE "March 22, 2006"
+#define DRV_MODULE_VERSION "1.4.40"
+#define DRV_MODULE_RELDATE "May 22, 2006"
#define RUN_AT(x) (jiffies + (x))
@@ -2945,7 +2945,7 @@ bnx2_nvram_write(struct bnx2 *bp, u32 of
int buf_size)
{
u32 written, offset32, len32;
- u8 *buf, start[4], end[4];
+ u8 *buf, start[4], end[4], *flash_buffer = NULL;
int rc = 0;
int align_start, align_end;
@@ -2985,12 +2985,19 @@ bnx2_nvram_write(struct bnx2 *bp, u32 of
memcpy(buf + align_start, data_buf, buf_size);
}
+ if (bp->flash_info->buffered == 0) {
+ flash_buffer = kmalloc(264, GFP_KERNEL);
+ if (flash_buffer == NULL) {
+ rc = -ENOMEM;
+ goto nvram_write_end;
+ }
+ }
+
written = 0;
while ((written < len32) && (rc == 0)) {
u32 page_start, page_end, data_start, data_end;
u32 addr, cmd_flags;
int i;
- u8 flash_buffer[264];
/* Find the page_start addr */
page_start = offset32 + written;
@@ -3109,6 +3116,9 @@ bnx2_nvram_write(struct bnx2 *bp, u32 of
}
nvram_write_end:
+ if (bp->flash_info->buffered == 0)
+ kfree(flash_buffer);
+
if (align_start || align_end)
kfree(buf);
return rc;
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox