From 4dbedf43d26276f6d7c8c3146d0a5b2f0309d968 Mon Sep 17 00:00:00 2001 From: Nathan Williams Date: Wed, 25 Mar 2009 20:27:37 +1100 Subject: solos: support new FPGA RAM layout Buffer sizes have been changed to 2048 bytes. Flash upgrades use a dedicated RAM block. Add support for daughterboard. Signed-off-by: Nathan Williams Signed-off-by: David Woodhouse --- drivers/atm/solos-pci.c | 71 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index bc3079d1787..bfef8d25581 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -59,21 +59,29 @@ #define RX_DMA_ADDR(port) (0x30 + (4 * (port))) #define DATA_RAM_SIZE 32768 -#define BUF_SIZE 4096 +#define BUF_SIZE 2048 +#define OLD_BUF_SIZE 4096 /* For FPGA versions <= 2*/ #define FPGA_PAGE 528 /* FPGA flash page size*/ #define SOLOS_PAGE 512 /* Solos flash page size*/ #define FPGA_BLOCK (FPGA_PAGE * 8) /* FPGA flash block size*/ #define SOLOS_BLOCK (SOLOS_PAGE * 8) /* Solos flash block size*/ -#define RX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2) -#define TX_BUF(card, nr) ((card->buffers) + (nr)*BUF_SIZE*2 + BUF_SIZE) +#define RX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2) +#define TX_BUF(card, nr) ((card->buffers) + (nr)*(card->buffer_size)*2 + (card->buffer_size)) +#define FLASH_BUF ((card->buffers) + 4*(card->buffer_size)*2) #define RX_DMA_SIZE 2048 +#define FPGA_VERSION(a,b) (((a) << 8) + (b)) +#define LEGACY_BUFFERS 2 +#define DMA_SUPPORTED 4 + static int reset = 0; static int atmdebug = 0; static int firmware_upgrade = 0; static int fpga_upgrade = 0; +static int db_firmware_upgrade = 0; +static int db_fpga_upgrade = 0; struct pkt_hdr { __le16 size; @@ -116,6 +124,8 @@ struct solos_card { wait_queue_head_t param_wq; wait_queue_head_t fw_wq; int using_dma; + int fpga_version; + int buffer_size; }; @@ -136,10 +146,14 @@ MODULE_PARM_DESC(reset, "Reset Solos chips on startup"); MODULE_PARM_DESC(atmdebug, "Print ATM data"); MODULE_PARM_DESC(firmware_upgrade, "Initiate Solos firmware upgrade"); MODULE_PARM_DESC(fpga_upgrade, "Initiate FPGA upgrade"); +MODULE_PARM_DESC(db_firmware_upgrade, "Initiate daughter board Solos firmware upgrade"); +MODULE_PARM_DESC(db_fpga_upgrade, "Initiate daughter board FPGA upgrade"); module_param(reset, int, 0444); module_param(atmdebug, int, 0644); module_param(firmware_upgrade, int, 0444); module_param(fpga_upgrade, int, 0444); +module_param(db_firmware_upgrade, int, 0444); +module_param(db_fpga_upgrade, int, 0444); static void fpga_queue(struct solos_card *card, int port, struct sk_buff *skb, struct atm_vcc *vcc); @@ -517,10 +531,32 @@ static int flash_upgrade(struct solos_card *card, int chip) if (chip == 0) { fw_name = "solos-FPGA.bin"; blocksize = FPGA_BLOCK; - } else { + } + + if (chip == 1) { fw_name = "solos-Firmware.bin"; blocksize = SOLOS_BLOCK; } + + if (chip == 2){ + if (card->fpga_version > LEGACY_BUFFERS){ + fw_name = "solos-db-FPGA.bin"; + blocksize = FPGA_BLOCK; + } else { + dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n"); + return -EPERM; + } + } + + if (chip == 3){ + if (card->fpga_version > LEGACY_BUFFERS){ + fw_name = "solos-Firmware.bin"; + blocksize = SOLOS_BLOCK; + } else { + dev_info(&card->dev->dev, "FPGA version doesn't support daughter board upgrades\n"); + return -EPERM; + } + } if (request_firmware(&fw, fw_name, &card->dev->dev)) return -ENOENT; @@ -536,8 +572,10 @@ static int flash_upgrade(struct solos_card *card, int chip) data32 = ioread32(card->config_regs + FPGA_MODE); /* Set mode to Chip Erase */ - dev_info(&card->dev->dev, "Set FPGA Flash mode to %s Chip Erase\n", - chip?"Solos":"FPGA"); + if(chip == 0 || chip == 2) + dev_info(&card->dev->dev, "Set FPGA Flash mode to FPGA Chip Erase\n"); + if(chip == 1 || chip == 3) + dev_info(&card->dev->dev, "Set FPGA Flash mode to Solos Chip Erase\n"); iowrite32((chip * 2), card->config_regs + FLASH_MODE); @@ -557,7 +595,10 @@ static int flash_upgrade(struct solos_card *card, int chip) /* Copy block to buffer, swapping each 16 bits */ for(i = 0; i < blocksize; i += 4) { uint32_t word = swahb32p((uint32_t *)(fw->data + offset + i)); - iowrite32(word, RX_BUF(card, 3) + i); + if(card->fpga_version > LEGACY_BUFFERS) + iowrite32(word, FLASH_BUF + i); + else + iowrite32(word, RX_BUF(card, 3) + i); } /* Specify block number and then trigger flash write */ @@ -1094,12 +1135,18 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) fpga_ver = (data32 & 0x0000FFFF); major_ver = ((data32 & 0xFF000000) >> 24); minor_ver = ((data32 & 0x00FF0000) >> 16); + card->fpga_version = FPGA_VERSION(major_ver,minor_ver); + if (card->fpga_version > LEGACY_BUFFERS) + card->buffer_size = BUF_SIZE; + else + card->buffer_size = OLD_BUF_SIZE; dev_info(&dev->dev, "Solos FPGA Version %d.%02d svn-%d\n", major_ver, minor_ver, fpga_ver); - if (0 && fpga_ver > 27) + if (card->fpga_version >= DMA_SUPPORTED){ card->using_dma = 1; - else { + } else { + card->using_dma = 0; /* Set RX empty flag for all ports */ iowrite32(0xF0, card->config_regs + FLAGS_ADDR); } @@ -1131,6 +1178,12 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) if (firmware_upgrade) flash_upgrade(card, 1); + if (db_fpga_upgrade) + flash_upgrade(card, 2); + + if (db_firmware_upgrade) + flash_upgrade(card, 3); + err = atm_init(card); if (err) goto out_free_irq; -- cgit v1.2.3 From 78f857f265241dfa6f343d75b45e8b30935f71df Mon Sep 17 00:00:00 2001 From: Nathan Williams Date: Wed, 25 Mar 2009 20:33:42 +1100 Subject: solos: Check for rogue received packets Sometimes there can be received packets with the size field set to 0xFFFF. This seems to only occur after an FPGA or firmware upgrade. This patch discards packets with an invalid size. Signed-off-by: Nathan Williams Signed-off-by: David Woodhouse --- drivers/atm/solos-pci.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index bfef8d25581..6c828347c9c 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -671,6 +671,10 @@ void solos_bh(unsigned long card_arg) memcpy_fromio(header, RX_BUF(card, port), sizeof(*header)); size = le16_to_cpu(header->size); + if (size > (card->buffer_size - sizeof(*header))){ + dev_warn(&card->dev->dev, "Invalid buffer size\n"); + continue; + } skb = alloc_skb(size + 1, GFP_ATOMIC); if (!skb) { -- cgit v1.2.3 From a3434660e5b47ab90387b017e311437bb3a88083 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 30 Apr 2009 15:38:01 +0100 Subject: solos: Add some margin-related parameters Signed-off-by: David Woodhouse --- drivers/atm/solos-attrlist.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/atm/solos-attrlist.c b/drivers/atm/solos-attrlist.c index efa2808dd94..94672639939 100644 --- a/drivers/atm/solos-attrlist.c +++ b/drivers/atm/solos-attrlist.c @@ -62,6 +62,13 @@ SOLOS_ATTR_RW(Defaults) SOLOS_ATTR_RW(LineMode) SOLOS_ATTR_RW(Profile) SOLOS_ATTR_RW(DetectNoise) +SOLOS_ATTR_RW(BisAForceSNRMarginDn) +SOLOS_ATTR_RW(BisMForceSNRMarginDn) +SOLOS_ATTR_RW(BisAMaxMargin) +SOLOS_ATTR_RW(BisMMaxMargin) +SOLOS_ATTR_RW(AnnexAForceSNRMarginDn) +SOLOS_ATTR_RW(AnnexAMaxMargin) +SOLOS_ATTR_RW(AnnexMMaxMargin) SOLOS_ATTR_RO(SupportedAnnexes) SOLOS_ATTR_RO(Status) SOLOS_ATTR_RO(TotalStart) -- cgit v1.2.3 From 69a9ceab83928e743fafbba5b98bd09aed7c0e0d Mon Sep 17 00:00:00 2001 From: Simon Farnsworth Date: Thu, 7 May 2009 19:43:42 +0100 Subject: solos: Show Interleaving details for ADSL2 and 2+ InterleaveRDn and InterleaveRUp only apply to G.dmt. The equivalents for ADSL2 and 2+ are BisRDn and BisRUp. In addition, the INPdown and INPup statuses are useful when trying to track down instability on a line. Signed-off-by: Simon Farnsworth Signed-off-by: David Woodhouse --- drivers/atm/solos-attrlist.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/atm/solos-attrlist.c b/drivers/atm/solos-attrlist.c index 94672639939..1a9332e4efe 100644 --- a/drivers/atm/solos-attrlist.c +++ b/drivers/atm/solos-attrlist.c @@ -25,6 +25,10 @@ SOLOS_ATTR_RO(RSCorrectedErrorsUp) SOLOS_ATTR_RO(RSUnCorrectedErrorsUp) SOLOS_ATTR_RO(InterleaveRDn) SOLOS_ATTR_RO(InterleaveRUp) +SOLOS_ATTR_RO(BisRDn) +SOLOS_ATTR_RO(BisRUp) +SOLOS_ATTR_RO(INPdown) +SOLOS_ATTR_RO(INPup) SOLOS_ATTR_RO(ShowtimeStart) SOLOS_ATTR_RO(ATURVendor) SOLOS_ATTR_RO(ATUCCountry) -- cgit v1.2.3 From 762c6aa14c541f0fef44d030f9d422b47583365d Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 15 Sep 2009 21:44:22 +0000 Subject: cpmac: fix compilation errors against undeclared BUS_ID_SIZE With the removal of BUS_ID_SIZE, cpmac was not fully converted to use MII_BUS_ID_SIZE as it ought to. This patch fixes the following cpmac build failure: CC drivers/net/cpmac.o drivers/net/cpmac.c: In function 'cpmac_start_xmit': drivers/net/cpmac.c:563: warning: comparison of distinct pointer types lacks a cast drivers/net/cpmac.c: In function 'cpmac_probe': drivers/net/cpmac.c:1112: error: 'BUS_ID_SIZE' undeclared (first use in this function) drivers/net/cpmac.c:1112: error: (Each undeclared identifier is reported only once drivers/net/cpmac.c:1112: error: for each function it appears in.) Reported-by: Ralf Baechle Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/cpmac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 3e3fab8afb1..61f9da2b494 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -1109,7 +1109,7 @@ static int external_switch; static int __devinit cpmac_probe(struct platform_device *pdev) { int rc, phy_id; - char mdio_bus_id[BUS_ID_SIZE]; + char mdio_bus_id[MII_BUS_ID_SIZE]; struct resource *mem; struct cpmac_priv *priv; struct net_device *dev; @@ -1118,7 +1118,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; if (external_switch || dumb_switch) { - strncpy(mdio_bus_id, "0", BUS_ID_SIZE); /* fixed phys bus */ + strncpy(mdio_bus_id, "0", MII_BUS_ID_SIZE); /* fixed phys bus */ phy_id = pdev->id; } else { for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) { @@ -1126,7 +1126,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) continue; if (!cpmac_mii->phy_map[phy_id]) continue; - strncpy(mdio_bus_id, cpmac_mii->id, BUS_ID_SIZE); + strncpy(mdio_bus_id, cpmac_mii->id, MII_BUS_ID_SIZE); break; } } @@ -1167,7 +1167,7 @@ static int __devinit cpmac_probe(struct platform_device *pdev) priv->msg_enable = netif_msg_init(debug_level, 0xff); memcpy(dev->dev_addr, pdata->dev_addr, sizeof(dev->dev_addr)); - snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); + snprintf(priv->phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link, 0, PHY_INTERFACE_MODE_MII); -- cgit v1.2.3 From c1e815c03b7313d6474164bfe91916da1329ad2b Mon Sep 17 00:00:00 2001 From: Sebastian Haas Date: Wed, 16 Sep 2009 02:04:15 +0000 Subject: cpc-usb: Removed driver from staging tree This patch prepares replacing the staging driver cpc-usb with the new developed ems_usb CAN driver. Signed-off-by: Sebastian Haas Acked-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/cpc-usb/Kconfig | 4 - drivers/staging/cpc-usb/Makefile | 3 - drivers/staging/cpc-usb/TODO | 9 - drivers/staging/cpc-usb/cpc-usb_drv.c | 1185 --------------------------------- drivers/staging/cpc-usb/cpc.h | 440 ------------ drivers/staging/cpc-usb/cpc_int.h | 83 --- drivers/staging/cpc-usb/cpcusb.h | 86 --- drivers/staging/cpc-usb/sja2m16c.h | 41 -- drivers/staging/cpc-usb/sja2m16c_2.c | 452 ------------- 11 files changed, 2306 deletions(-) delete mode 100644 drivers/staging/cpc-usb/Kconfig delete mode 100644 drivers/staging/cpc-usb/Makefile delete mode 100644 drivers/staging/cpc-usb/TODO delete mode 100644 drivers/staging/cpc-usb/cpc-usb_drv.c delete mode 100644 drivers/staging/cpc-usb/cpc.h delete mode 100644 drivers/staging/cpc-usb/cpc_int.h delete mode 100644 drivers/staging/cpc-usb/cpcusb.h delete mode 100644 drivers/staging/cpc-usb/sja2m16c.h delete mode 100644 drivers/staging/cpc-usb/sja2m16c_2.c (limited to 'drivers') diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 975ecddbce3..5e4b8655b57 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -121,8 +121,6 @@ source "drivers/staging/serqt_usb2/Kconfig" source "drivers/staging/vt6655/Kconfig" -source "drivers/staging/cpc-usb/Kconfig" - source "drivers/staging/pata_rdc/Kconfig" source "drivers/staging/udlfb/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 2241ae1b21e..ede1599e7e6 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -42,6 +42,5 @@ obj-$(CONFIG_LINE6_USB) += line6/ obj-$(CONFIG_USB_SERIAL_QUATECH2) += serqt_usb2/ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_VT6655) += vt6655/ -obj-$(CONFIG_USB_CPC) += cpc-usb/ obj-$(CONFIG_RDC_17F3101X) += pata_rdc/ obj-$(CONFIG_FB_UDL) += udlfb/ diff --git a/drivers/staging/cpc-usb/Kconfig b/drivers/staging/cpc-usb/Kconfig deleted file mode 100644 index 2be0bc9c39d..00000000000 --- a/drivers/staging/cpc-usb/Kconfig +++ /dev/null @@ -1,4 +0,0 @@ -config USB_CPC - tristate "CPC CAN USB driver" - depends on USB && PROC_FS - default n diff --git a/drivers/staging/cpc-usb/Makefile b/drivers/staging/cpc-usb/Makefile deleted file mode 100644 index 3f83170a8fa..00000000000 --- a/drivers/staging/cpc-usb/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_USB_CPC) += cpc-usb.o - -cpc-usb-y := cpc-usb_drv.o sja2m16c_2.o diff --git a/drivers/staging/cpc-usb/TODO b/drivers/staging/cpc-usb/TODO deleted file mode 100644 index 000e8bbc618..00000000000 --- a/drivers/staging/cpc-usb/TODO +++ /dev/null @@ -1,9 +0,0 @@ -Things to do for this driver to get merged into the main portion of the -kernel: - - checkpatch cleanups - - sparse clean - - remove proc code - - tie into CAN socket interfaces if possible - - figure out sane userspace api - -Send patches to Greg Kroah-Hartman diff --git a/drivers/staging/cpc-usb/cpc-usb_drv.c b/drivers/staging/cpc-usb/cpc-usb_drv.c deleted file mode 100644 index 9bf3f98c682..00000000000 --- a/drivers/staging/cpc-usb/cpc-usb_drv.c +++ /dev/null @@ -1,1185 +0,0 @@ -/* - * CPC-USB CAN Interface Kernel Driver - * - * Copyright (C) 2004-2009 EMS Dr. Thomas Wuensche - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published - * by the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "cpc.h" - -#include "cpc_int.h" -#include "cpcusb.h" - -#include "sja2m16c.h" - -/* Version Information */ -#define DRIVER_AUTHOR "Sebastian Haas " -#define DRIVER_DESC "CPC-USB Driver for Linux Kernel 2.6" -#define DRIVER_VERSION CPC_DRIVER_VERSION - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL v2"); - -/* Define these values to match your devices */ -#define USB_CPCUSB_VENDOR_ID 0x12D6 - -#define USB_CPCUSB_M16C_PRODUCT_ID 0x0888 -#define USB_CPCUSB_LPC2119_PRODUCT_ID 0x0444 - -#define CPC_USB_PROC_DIR CPC_PROC_DIR "cpc-usb" - -static struct proc_dir_entry *procDir; -static struct proc_dir_entry *procEntry; - -/* Module parameters */ -static int debug; -module_param(debug, int, S_IRUGO); - -/* table of devices that work with this driver */ -static struct usb_device_id cpcusb_table[] = { - {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_M16C_PRODUCT_ID)}, - {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_LPC2119_PRODUCT_ID)}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, cpcusb_table); - -/* use to prevent kernel panic if driver is unloaded - * while a programm has still open the device - */ -DECLARE_WAIT_QUEUE_HEAD(rmmodWq); -atomic_t useCount; - -static CPC_USB_T *CPCUSB_Table[CPC_USB_CARD_CNT] = { 0 }; -static unsigned int CPCUsbCnt; - -/* prevent races between open() and disconnect() */ -static DECLARE_MUTEX(disconnect_sem); - -/* local function prototypes */ -static ssize_t cpcusb_read(struct file *file, char *buffer, size_t count, - loff_t *ppos); -static ssize_t cpcusb_write(struct file *file, const char *buffer, - size_t count, loff_t *ppos); -static unsigned int cpcusb_poll(struct file *file, poll_table * wait); -static int cpcusb_open(struct inode *inode, struct file *file); -static int cpcusb_release(struct inode *inode, struct file *file); - -static int cpcusb_probe(struct usb_interface *interface, - const struct usb_device_id *id); -static void cpcusb_disconnect(struct usb_interface *interface); - -static void cpcusb_read_bulk_callback(struct urb *urb); -static void cpcusb_write_bulk_callback(struct urb *urb); -static void cpcusb_read_interrupt_callback(struct urb *urb); - -static int cpcusb_setup_intrep(CPC_USB_T *card); - -static struct file_operations cpcusb_fops = { - /* - * The owner field is part of the module-locking - * mechanism. The idea is that the kernel knows - * which module to increment the use-counter of - * BEFORE it calls the device's open() function. - * This also means that the kernel can decrement - * the use-counter again before calling release() - * or should the open() function fail. - */ - .owner = THIS_MODULE, - - .read = cpcusb_read, - .write = cpcusb_write, - .poll = cpcusb_poll, - .open = cpcusb_open, - .release = cpcusb_release, -}; - -/* - * usb class driver info in order to get a minor number from the usb core, - * and to have the device registered with devfs and the driver core - */ -static struct usb_class_driver cpcusb_class = { - .name = "usb/cpc_usb%d", - .fops = &cpcusb_fops, - .minor_base = CPC_USB_BASE_MNR, -}; - -/* usb specific object needed to register this driver with the usb subsystem */ -static struct usb_driver cpcusb_driver = { - .name = "cpc-usb", - .probe = cpcusb_probe, - .disconnect = cpcusb_disconnect, - .id_table = cpcusb_table, -}; - -static int cpcusb_create_info_output(char *buf) -{ - int i = 0, j; - - for (j = 0; j < CPC_USB_CARD_CNT; j++) { - if (CPCUSB_Table[j]) { - CPC_USB_T *card = CPCUSB_Table[j]; - CPC_CHAN_T *chan = card->chan; - - /* MINOR CHANNELNO BUSNO SLOTNO */ - i += sprintf(&buf[i], "%d %s\n", chan->minor, - card->serialNumber); - } - } - - return i; -} - -static int cpcusb_proc_read_info(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpcusb_create_info_output(page); - - if (len <= off + count) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - - return len; -} - -/* - * Remove CPC-USB and cleanup - */ -static inline void cpcusb_delete(CPC_USB_T *card) -{ - if (card) { - if (card->chan) { - if (card->chan->buf) - vfree(card->chan->buf); - - if (card->chan->CPCWait_q) - kfree(card->chan->CPCWait_q); - - kfree(card->chan); - } - - CPCUSB_Table[card->idx] = NULL; - kfree(card); - } -} - -/* - * setup the interrupt IN endpoint of a specific CPC-USB device - */ -static int cpcusb_setup_intrep(CPC_USB_T *card) -{ - int retval = 0; - struct usb_endpoint_descriptor *ep; - - ep = &card->interface->altsetting[0].endpoint[card->num_intr_in].desc; - - card->intr_in_buffer[0] = 0; - card->free_slots = 15; /* initial size */ - - /* setup the urb */ - usb_fill_int_urb(card->intr_in_urb, card->udev, - usb_rcvintpipe(card->udev, card->num_intr_in), - card->intr_in_buffer, - sizeof(card->intr_in_buffer), - cpcusb_read_interrupt_callback, - card, - ep->bInterval); - - card->intr_in_urb->status = 0; /* needed! */ - - /* submit the urb */ - retval = usb_submit_urb(card->intr_in_urb, GFP_KERNEL); - - if (retval) - err("%s - failed submitting intr urb, error %d", __func__, - retval); - - return retval; -} - -static int cpcusb_open(struct inode *inode, struct file *file) -{ - CPC_USB_T *card = NULL; - struct usb_interface *interface; - int subminor; - int j, retval = 0; - - subminor = iminor(inode); - - /* prevent disconnects */ - down(&disconnect_sem); - - interface = usb_find_interface(&cpcusb_driver, subminor); - if (!interface) { - err("%s - error, can't find device for minor %d", - __func__, subminor); - retval = CPC_ERR_NO_INTERFACE_PRESENT; - goto exit_no_device; - } - - card = usb_get_intfdata(interface); - if (!card) { - retval = CPC_ERR_NO_INTERFACE_PRESENT; - goto exit_no_device; - } - - /* lock this device */ - down(&card->sem); - - /* increment our usage count for the driver */ - if (card->open) { - dbg("device already opened"); - retval = CPC_ERR_CHANNEL_ALREADY_OPEN; - goto exit_on_error; - } - - /* save our object in the file's private structure */ - file->private_data = card; - for (j = 0; j < CPC_USB_URB_CNT; j++) { - usb_fill_bulk_urb(card->urbs[j].urb, card->udev, - usb_rcvbulkpipe(card->udev, card->num_bulk_in), - card->urbs[j].buffer, card->urbs[j].size, - cpcusb_read_bulk_callback, card); - - retval = usb_submit_urb(card->urbs[j].urb, GFP_KERNEL); - - if (retval) { - err("%s - failed submitting read urb, error %d", - __func__, retval); - retval = CPC_ERR_TRANSMISSION_FAILED; - goto exit_on_error; - } - } - - info("%s - %d URB's submitted", __func__, j); - - ResetBuffer(card->chan); - - cpcusb_setup_intrep(card); - card->open = 1; - - atomic_inc(&useCount); - -exit_on_error: - /* unlock this device */ - up(&card->sem); - -exit_no_device: - up(&disconnect_sem); - - return retval; -} - -static unsigned int cpcusb_poll(struct file *file, poll_table * wait) -{ - CPC_USB_T *card = (CPC_USB_T *) file->private_data; - unsigned int retval = 0; - - if (!card) { - err("%s - device object lost", __func__); - return -EIO; - } - - poll_wait(file, card->chan->CPCWait_q, wait); - - if (IsBufferNotEmpty(card->chan) || !(card->present)) - retval |= (POLLIN | POLLRDNORM); - - if (card->free_slots) - retval |= (POLLOUT | POLLWRNORM); - - return retval; -} - -static int cpcusb_release(struct inode *inode, struct file *file) -{ - CPC_USB_T *card = (CPC_USB_T *) file->private_data; - int j, retval = 0; - - if (card == NULL) { - dbg("%s - object is NULL", __func__); - return CPC_ERR_NO_INTERFACE_PRESENT; - } - - /* lock our device */ - down(&card->sem); - - if (!card->open) { - dbg("%s - device not opened", __func__); - retval = CPC_ERR_NO_INTERFACE_PRESENT; - goto exit_not_opened; - } - - /* if device wasn't unplugged kill all urbs */ - if (card->present) { - /* kill read urbs */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - usb_kill_urb(card->urbs[j].urb); - } - - /* kill irq urb */ - usb_kill_urb(card->intr_in_urb); - - /* kill write urbs */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - if (atomic_read(&card->wrUrbs[j].busy)) { - usb_kill_urb(card->wrUrbs[j].urb); - wait_for_completion(&card->wrUrbs[j].finished); - } - } - } - - atomic_dec(&useCount); - - /* last process detached */ - if (atomic_read(&useCount) == 0) { - wake_up(&rmmodWq); - } - - if (!card->present && card->open) { - /* the device was unplugged before the file was released */ - up(&card->sem); - cpcusb_delete(card); - return 0; - } - - card->open = 0; - -exit_not_opened: - up(&card->sem); - - return 0; -} - -static ssize_t cpcusb_read(struct file *file, char *buffer, size_t count, - loff_t *ppos) -{ - CPC_USB_T *card = (CPC_USB_T *) file->private_data; - CPC_CHAN_T *chan; - int retval = 0; - - if (count < sizeof(CPC_MSG_T)) - return CPC_ERR_UNKNOWN; - - /* check if can read from the given address */ - if (!access_ok(VERIFY_WRITE, buffer, count)) - return CPC_ERR_UNKNOWN; - - /* lock this object */ - down(&card->sem); - - /* verify that the device wasn't unplugged */ - if (!card->present) { - up(&card->sem); - return CPC_ERR_NO_INTERFACE_PRESENT; - } - - if (IsBufferEmpty(card->chan)) { - retval = 0; - } else { - chan = card->chan; - -#if 0 - /* convert LPC2119 params back to SJA1000 params */ - if (card->deviceRevision >= 0x0200 - && chan->buf[chan->oidx].type == CPC_MSG_T_CAN_PRMS) { - LPC2119_TO_SJA1000_Params(&chan->buf[chan->oidx]); - } -#endif - - if (copy_to_user(buffer, &chan->buf[chan->oidx], count) != 0) { - retval = CPC_ERR_IO_TRANSFER; - } else { - chan->oidx = (chan->oidx + 1) % CPC_MSG_BUF_CNT; - chan->WnR = 1; - retval = sizeof(CPC_MSG_T); - } - } -/* spin_unlock_irqrestore(&card->slock, flags); */ - - /* unlock the device */ - up(&card->sem); - - return retval; -} - -#define SHIFT 1 -static inline void cpcusb_align_buffer_alignment(unsigned char *buf) -{ - /* CPC-USB uploads packed bytes. */ - CPC_MSG_T *cpc = (CPC_MSG_T *) buf; - unsigned int i; - - for (i = 0; i < cpc->length + (2 * sizeof(unsigned long)); i++) { - ((unsigned char *) &cpc->msgid)[1 + i] = - ((unsigned char *) &cpc->msgid)[1 + SHIFT + i]; - } -} - -static int cpc_get_buffer_count(CPC_CHAN_T *chan) -{ - /* check the buffer parameters */ - if (chan->iidx == chan->oidx) - return !chan->WnR ? CPC_MSG_BUF_CNT : 0; - else if (chan->iidx >= chan->oidx) - return (chan->iidx - chan->oidx) % CPC_MSG_BUF_CNT; - - return (chan->iidx + CPC_MSG_BUF_CNT - chan->oidx) % CPC_MSG_BUF_CNT; -} - -static ssize_t cpcusb_write(struct file *file, const char *buffer, - size_t count, loff_t *ppos) -{ - CPC_USB_T *card = (CPC_USB_T *) file->private_data; - CPC_USB_WRITE_URB_T *wrUrb = NULL; - - ssize_t bytes_written = 0; - int retval = 0; - int j; - - unsigned char *obuf = NULL; - unsigned char type = 0; - CPC_MSG_T *info = NULL; - - dbg("%s - entered minor %d, count = %zu, present = %d", - __func__, card->minor, count, card->present); - - if (count > sizeof(CPC_MSG_T)) - return CPC_ERR_UNKNOWN; - - /* check if can read from the given address */ - if (!access_ok(VERIFY_READ, buffer, count)) - return CPC_ERR_UNKNOWN; - - /* lock this object */ - down(&card->sem); - - /* verify that the device wasn't unplugged */ - if (!card->present) { - retval = CPC_ERR_NO_INTERFACE_PRESENT; - goto exit; - } - - /* verify that we actually have some data to write */ - if (count == 0) { - dbg("%s - write request of 0 bytes", __func__); - goto exit; - } - - if (card->free_slots <= 5) { - info = (CPC_MSG_T *) buffer; - - if (info->type != CPC_CMD_T_CLEAR_CMD_QUEUE - || card->free_slots <= 0) { - dbg("%s - send buffer full please try again %d", - __func__, card->free_slots); - retval = CPC_ERR_CAN_NO_TRANSMIT_BUF; - goto exit; - } - } - - /* Find a free write urb */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - if (!atomic_read(&card->wrUrbs[j].busy)) { - wrUrb = &card->wrUrbs[j]; /* remember found URB */ - atomic_set(&wrUrb->busy, 1); /* lock this URB */ - init_completion(&wrUrb->finished); /* init completion */ - dbg("WR URB no. %d started", j); - break; - } - } - - /* don't found write urb say error */ - if (!wrUrb) { - dbg("%s - no free send urb available", __func__); - retval = CPC_ERR_CAN_NO_TRANSMIT_BUF; - goto exit; - } - dbg("URB write req"); - - obuf = (unsigned char *) wrUrb->urb->transfer_buffer; - - /* copy the data from userspace into our transfer buffer; - * this is the only copy required. - */ - if (copy_from_user(&obuf[4], buffer, count) != 0) { - atomic_set(&wrUrb->busy, 0); /* release urb */ - retval = CPC_ERR_IO_TRANSFER; - goto exit; - } - - /* check if it is a DRIVER information message, so we can - * response to that message and not the USB - */ - info = (CPC_MSG_T *) &obuf[4]; - - bytes_written = 11 + info->length; - if (bytes_written >= wrUrb->size) { - retval = CPC_ERR_IO_TRANSFER; - goto exit; - } - - switch (info->type) { - case CPC_CMD_T_CLEAR_MSG_QUEUE: - ResetBuffer(card->chan); - break; - - case CPC_CMD_T_INQ_MSG_QUEUE_CNT: - retval = cpc_get_buffer_count(card->chan); - atomic_set(&wrUrb->busy, 0); - - goto exit; - - case CPC_CMD_T_INQ_INFO: - if (info->msg.info.source == CPC_INFOMSG_T_DRIVER) { - /* release urb cause we'll use it for driver - * information - */ - atomic_set(&wrUrb->busy, 0); - if (IsBufferFull(card->chan)) { - retval = CPC_ERR_IO_TRANSFER; - goto exit; - } - - /* it is a driver information request message and we have - * free rx slots to store the response - */ - type = info->msg.info.type; - info = &card->chan->buf[card->chan->iidx]; - - info->type = CPC_MSG_T_INFO; - info->msg.info.source = CPC_INFOMSG_T_DRIVER; - info->msg.info.type = type; - - switch (type) { - case CPC_INFOMSG_T_VERSION: - info->length = strlen(CPC_DRIVER_VERSION) + 2; - sprintf(info->msg.info.msg, "%s\n", - CPC_DRIVER_VERSION); - break; - - case CPC_INFOMSG_T_SERIAL: - info->length = strlen(CPC_DRIVER_SERIAL) + 2; - sprintf(info->msg.info.msg, "%s\n", - CPC_DRIVER_SERIAL); - break; - - default: - info->length = 2; - info->msg.info.type = - CPC_INFOMSG_T_UNKNOWN_TYPE; - } - - card->chan->WnR = 0; - card->chan->iidx = - (card->chan->iidx + 1) % CPC_MSG_BUF_CNT; - - retval = info->length; - goto exit; - } - break; - case CPC_CMD_T_CAN_PRMS: - /* Check the controller type. If it's the new CPC-USB, make sure if these are SJA1000 params */ - if (info->msg.canparams.cc_type != SJA1000 - && info->msg.canparams.cc_type != M16C_BASIC - && (card->productId == USB_CPCUSB_LPC2119_PRODUCT_ID - && info->msg.canparams.cc_type != SJA1000)) { - /* don't forget to release the urb */ - atomic_set(&wrUrb->busy, 0); - retval = CPC_ERR_WRONG_CONTROLLER_TYPE; - goto exit; - } - break; - } - - /* just convert the params if it is an old CPC-USB with M16C controller */ - if (card->productId == USB_CPCUSB_M16C_PRODUCT_ID) { - /* if it is a parameter message convert it from SJA1000 controller - * settings to M16C Basic controller settings - */ - SJA1000_TO_M16C_BASIC_Params((CPC_MSG_T *) &obuf[4]); - } - - /* don't forget the byte alignment */ - cpcusb_align_buffer_alignment(&obuf[4]); - - /* setup a the 4 byte header */ - obuf[0] = obuf[1] = obuf[2] = obuf[3] = 0; - - /* this urb was already set up, except for this write size */ - wrUrb->urb->transfer_buffer_length = bytes_written + 4; - - /* send the data out the bulk port */ - /* a character device write uses GFP_KERNEL, - unless a spinlock is held */ - retval = usb_submit_urb(wrUrb->urb, GFP_KERNEL); - if (retval) { - atomic_set(&wrUrb->busy, 0); /* release urb */ - err("%s - failed submitting write urb, error %d", - __func__, retval); - } else { - retval = bytes_written; - } - -exit: - /* unlock the device */ - up(&card->sem); - - dbg("%s - leaved", __func__); - - return retval; -} - -/* - * callback for interrupt IN urb - */ -static void cpcusb_read_interrupt_callback(struct urb *urb) -{ - CPC_USB_T *card = (CPC_USB_T *) urb->context; - int retval; - unsigned long flags; - - spin_lock_irqsave(&card->slock, flags); - - if (!card->present) { - spin_unlock_irqrestore(&card->slock, flags); - info("%s - no such device", __func__); - return; - } - - switch (urb->status) { - case 0: /* success */ - card->free_slots = card->intr_in_buffer[1]; - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* urb was killed */ - spin_unlock_irqrestore(&card->slock, flags); - dbg("%s - intr urb killed", __func__); - return; - default: - info("%s - nonzero urb status %d", __func__, urb->status); - break; - } - - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) { - err("%s - failed resubmitting intr urb, error %d", - __func__, retval); - } - - spin_unlock_irqrestore(&card->slock, flags); - wake_up_interruptible(card->chan->CPCWait_q); - - return; -} - -#define UN_SHIFT 1 -#define CPCMSG_HEADER_LEN_FIRMWARE 11 -static inline int cpcusb_unalign_and_copy_buffy(unsigned char *out, - unsigned char *in) -{ - unsigned int i, j; - - for (i = 0; i < 3; i++) - out[i] = in[i]; - - for (j = 0; j < (in[1] + (CPCMSG_HEADER_LEN_FIRMWARE - 3)); j++) - out[j + i + UN_SHIFT] = in[j + i]; - - return i + j; -} - -/* - * callback for bulk IN urb - */ -static void cpcusb_read_bulk_callback(struct urb *urb) -{ - CPC_USB_T *card = (CPC_USB_T *) urb->context; - CPC_CHAN_T *chan; - unsigned char *ibuf = urb->transfer_buffer; - int retval, msgCnt, start, again = 0; - unsigned long flags; - - if (!card) { - err("%s - device object lost", __func__); - return; - } - - spin_lock_irqsave(&card->slock, flags); - - if (!card->present) { - spin_unlock_irqrestore(&card->slock, flags); - info("%s - no such device", __func__); - return; - } - - switch (urb->status) { - case 0: /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* urb was killed */ - spin_unlock_irqrestore(&card->slock, flags); - dbg("%s - read urb killed", __func__); - return; - default: - info("%s - nonzero urb status %d", __func__, urb->status); - break; - } - - if (urb->actual_length) { - msgCnt = ibuf[0] & ~0x80; - again = ibuf[0] & 0x80; - - /* we have a 4 byte header */ - start = 4; - chan = card->chan; - while (msgCnt) { - if (!(IsBufferFull(card->chan))) { - start += - cpcusb_unalign_and_copy_buffy((unsigned char *) - &chan->buf[chan->iidx], &ibuf[start]); - - if (start > urb->transfer_buffer_length) { - err("%d > %d", start, urb->transfer_buffer_length); - break; - } - - chan->WnR = 0; - chan->iidx = (chan->iidx + 1) % CPC_MSG_BUF_CNT; - msgCnt--; - } else { - break; - } - } - } - - usb_fill_bulk_urb(urb, card->udev, - usb_rcvbulkpipe(card->udev, card->num_bulk_in), - urb->transfer_buffer, - urb->transfer_buffer_length, - cpcusb_read_bulk_callback, card); - - retval = usb_submit_urb(urb, GFP_ATOMIC); - - if (retval) { - err("%s - failed resubmitting read urb, error %d", __func__, retval); - } - - spin_unlock_irqrestore(&card->slock, flags); - - wake_up_interruptible(card->chan->CPCWait_q); -} - -/* - * callback for bulk IN urb - */ -static void cpcusb_write_bulk_callback(struct urb *urb) -{ - CPC_USB_T *card = (CPC_USB_T *) urb->context; - unsigned long flags; - int j; - - spin_lock_irqsave(&card->slock, flags); - - /* find this urb */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - if (card->wrUrbs[j].urb == urb) { - dbg("URB found no. %d", j); - /* notify anyone waiting that the write has finished */ - complete(&card->wrUrbs[j].finished); - atomic_set(&card->wrUrbs[j].busy, 0); - break; - } - } - - switch (urb->status) { - case 0: /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* urb was killed */ - spin_unlock_irqrestore(&card->slock, flags); - dbg("%s - write urb no. %d killed", __func__, j); - return; - default: - info("%s - nonzero urb status %d", __func__, urb->status); - break; - } - - spin_unlock_irqrestore(&card->slock, flags); - - wake_up_interruptible(card->chan->CPCWait_q); -} - -static inline int cpcusb_get_free_slot(void) -{ - int i; - - for (i = 0; i < CPC_USB_CARD_CNT; i++) { - if (!CPCUSB_Table[i]) - return i; - } - - return -1; -} - -/* - * probe function for new CPC-USB devices - */ -static int cpcusb_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - CPC_USB_T *card = NULL; - CPC_CHAN_T *chan = NULL; - - struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - - int i, j, retval = -ENOMEM, slot; - - slot = cpcusb_get_free_slot(); - if (slot < 0) { - info("No more devices supported"); - return -ENOMEM; - } - - /* allocate memory for our device state and initialize it */ - card = kzalloc(sizeof(CPC_USB_T), GFP_KERNEL); - if (!card) { - err("Out of memory"); - return -ENOMEM; - } - CPCUSB_Table[slot] = card; - - /* allocate and initialize the channel struct */ - card->chan = kmalloc(sizeof(CPC_CHAN_T), GFP_KERNEL); - if (!card->chan) { - kfree(card); - err("Out of memory"); - return -ENOMEM; - } - - chan = card->chan; - memset(chan, 0, sizeof(CPC_CHAN_T)); - ResetBuffer(chan); - - init_MUTEX(&card->sem); - spin_lock_init(&card->slock); - - card->udev = udev; - card->interface = interface; - if (udev->descriptor.iSerialNumber) { - usb_string(udev, udev->descriptor.iSerialNumber, card->serialNumber, - 128); - info("Serial %s", card->serialNumber); - } - - card->productId = udev->descriptor.idProduct; - info("Product %s", - card->productId == USB_CPCUSB_LPC2119_PRODUCT_ID ? - "CPC-USB/ARM7" : "CPC-USB/M16C"); - - /* set up the endpoint information */ - /* check out the endpoints */ - /* use only the first bulk-in and bulk-out endpoints */ - iface_desc = &interface->altsetting[0]; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!card->num_intr_in && - (endpoint->bEndpointAddress & USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_INT)) { - card->intr_in_urb = usb_alloc_urb(0, GFP_KERNEL); - card->num_intr_in = 1; - - if (!card->intr_in_urb) { - err("No free urbs available"); - goto error; - } - - dbg("intr_in urb %d", card->num_intr_in); - } - - if (!card->num_bulk_in && - (endpoint->bEndpointAddress & USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK)) { - card->num_bulk_in = 2; - for (j = 0; j < CPC_USB_URB_CNT; j++) { - card->urbs[j].size = endpoint->wMaxPacketSize; - card->urbs[j].urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->urbs[j].urb) { - err("No free urbs available"); - goto error; - } - card->urbs[j].buffer = - usb_buffer_alloc(udev, - card->urbs[j].size, - GFP_KERNEL, - &card->urbs[j].urb->transfer_dma); - if (!card->urbs[j].buffer) { - err("Couldn't allocate bulk_in_buffer"); - goto error; - } - } - info("%s - %d reading URB's allocated", - __func__, CPC_USB_URB_CNT); - } - - if (!card->num_bulk_out && - !(endpoint->bEndpointAddress & USB_DIR_IN) && - ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - == USB_ENDPOINT_XFER_BULK)) { - - card->num_bulk_out = 2; - - for (j = 0; j < CPC_USB_URB_CNT; j++) { - card->wrUrbs[j].size = - endpoint->wMaxPacketSize; - card->wrUrbs[j].urb = - usb_alloc_urb(0, GFP_KERNEL); - if (!card->wrUrbs[j].urb) { - err("No free urbs available"); - goto error; - } - card->wrUrbs[j].buffer = usb_buffer_alloc(udev, - card->wrUrbs[j].size, GFP_KERNEL, - &card->wrUrbs[j].urb->transfer_dma); - - if (!card->wrUrbs[j].buffer) { - err("Couldn't allocate bulk_out_buffer"); - goto error; - } - - usb_fill_bulk_urb(card->wrUrbs[j].urb, udev, - usb_sndbulkpipe(udev, endpoint->bEndpointAddress), - card->wrUrbs[j].buffer, - card->wrUrbs[j].size, - cpcusb_write_bulk_callback, - card); - } - - info("%s - %d writing URB's allocated", __func__, CPC_USB_URB_CNT); - } - } - - if (!(card->num_bulk_in && card->num_bulk_out)) { - err("Couldn't find both bulk-in and bulk-out endpoints"); - goto error; - } - - /* allow device read, write and ioctl */ - card->present = 1; - - /* we can register the device now, as it is ready */ - usb_set_intfdata(interface, card); - retval = usb_register_dev(interface, &cpcusb_class); - - if (retval) { - /* something prevented us from registering this driver */ - err("Not able to get a minor for this device."); - usb_set_intfdata(interface, NULL); - goto error; - } - - card->chan->minor = card->minor = interface->minor; - - chan->buf = vmalloc(sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT); - if (chan->buf == NULL) { - err("Out of memory"); - retval = -ENOMEM; - goto error; - } - info("Allocated memory for %d messages (%lu kbytes)", - CPC_MSG_BUF_CNT, (long unsigned int)(sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT) / 1000); - memset(chan->buf, 0, sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT); - - ResetBuffer(chan); - - card->chan->CPCWait_q = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); - if (!card->chan->CPCWait_q) { - err("Out of memory"); - retval = -ENOMEM; - goto error; - } - init_waitqueue_head(card->chan->CPCWait_q); - - CPCUSB_Table[slot] = card; - card->idx = slot; - CPCUsbCnt++; - - /* let the user know what node this device is now attached to */ - info("Device now attached to USB-%d", card->minor); - return 0; - -error: - for (j = 0; j < CPC_USB_URB_CNT; j++) { - if (card->urbs[j].buffer) { - usb_buffer_free(card->udev, card->urbs[j].size, - card->urbs[j].buffer, - card->urbs[j].urb->transfer_dma); - card->urbs[j].buffer = NULL; - } - if (card->urbs[j].urb) { - usb_free_urb(card->urbs[j].urb); - card->urbs[j].urb = NULL; - } - } - - cpcusb_delete(card); - return retval; -} - -/* - * called by the usb core when the device is removed from the system - */ -static void cpcusb_disconnect(struct usb_interface *interface) -{ - CPC_USB_T *card = NULL; - int minor, j; - - /* prevent races with open() */ - down(&disconnect_sem); - - card = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - down(&card->sem); - - /* prevent device read, write and ioctl */ - card->present = 0; - - minor = card->minor; - - /* free all urbs and their buffers */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - /* terminate an ongoing write */ - if (atomic_read(&card->wrUrbs[j].busy)) { - usb_kill_urb(card->wrUrbs[j].urb); - wait_for_completion(&card->wrUrbs[j].finished); - } - usb_buffer_free(card->udev, card->wrUrbs[j].size, - card->wrUrbs[j].buffer, - card->wrUrbs[j].urb->transfer_dma); - usb_free_urb(card->wrUrbs[j].urb); - } - info("%d write URBs freed", CPC_USB_URB_CNT); - - /* free all urbs and their buffers */ - for (j = 0; j < CPC_USB_URB_CNT; j++) { - usb_buffer_free(card->udev, card->urbs[j].size, - card->urbs[j].buffer, - card->urbs[j].urb->transfer_dma); - usb_free_urb(card->urbs[j].urb); - } - info("%d read URBs freed", CPC_USB_URB_CNT); - usb_free_urb(card->intr_in_urb); - - /* give back our minor */ - usb_deregister_dev(interface, &cpcusb_class); - - up(&card->sem); - - /* if the device is opened, cpcusb_release will clean this up */ - if (!card->open) - cpcusb_delete(card); - else - wake_up_interruptible(card->chan->CPCWait_q); - - up(&disconnect_sem); - - CPCUsbCnt--; - info("USB-%d now disconnected", minor); -} - -static int __init CPCUsb_Init(void) -{ - int result, i; - - info(DRIVER_DESC " v" DRIVER_VERSION); - info("Build on " __DATE__ " at " __TIME__); - - for (i = 0; i < CPC_USB_CARD_CNT; i++) - CPCUSB_Table[i] = 0; - - /* register this driver with the USB subsystem */ - result = usb_register(&cpcusb_driver); - if (result) { - err("usb_register failed. Error number %d", result); - return result; - } - - procDir = proc_mkdir(CPC_USB_PROC_DIR, NULL); - if (!procDir) { - err("Could not create proc entry"); - } else { - procEntry = create_proc_read_entry("info", 0444, procDir, - cpcusb_proc_read_info, - NULL); - if (!procEntry) { - err("Could not create proc entry %s", CPC_USB_PROC_DIR "/info"); - remove_proc_entry(CPC_USB_PROC_DIR, NULL); - procDir = NULL; - } - } - - return 0; -} - -static void __exit CPCUsb_Exit(void) -{ - wait_event(rmmodWq, !atomic_read(&useCount)); - - /* deregister this driver with the USB subsystem */ - usb_deregister(&cpcusb_driver); - - if (procDir) { - if (procEntry) - remove_proc_entry("info", procDir); - remove_proc_entry(CPC_USB_PROC_DIR, NULL); - } -} - -module_init(CPCUsb_Init); -module_exit(CPCUsb_Exit); diff --git a/drivers/staging/cpc-usb/cpc.h b/drivers/staging/cpc-usb/cpc.h deleted file mode 100644 index ed8cb34d476..00000000000 --- a/drivers/staging/cpc-usb/cpc.h +++ /dev/null @@ -1,440 +0,0 @@ -/* - * CPC CAN Interface Definitions - * - * Copyright (C) 2000-2008 EMS Dr. Thomas Wuensche - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - */ -#ifndef CPC_HEADER -#define CPC_HEADER - -// the maximum length of the union members within a CPC_MSG -// this value can be defined by the customer, but has to be -// >= 64 bytes -// however, if not defined before, we set a length of 64 byte -#if !defined(CPC_MSG_LEN) || (CPC_MSG_LEN < 64) -#undef CPC_MSG_LEN -#define CPC_MSG_LEN 64 -#endif - -// check the operating system used -#ifdef _WIN32 // running a Windows OS - -// define basic types on Windows platforms -#ifdef _MSC_VER // Visual Studio - typedef unsigned __int8 u8; - typedef unsigned __int16 u16; - typedef unsigned __int32 u32; -#else // Borland Compiler - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; -#endif - // on Windows OS we use a byte alignment of 1 - #pragma pack(push, 1) - - // set the calling conventions for the library function calls - #define CALL_CONV __stdcall -#else - // Kernel headers already define this types - #ifndef __KERNEL__ - // define basic types - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; - #endif - - // Linux does not use this calling convention - #define CALL_CONV -#endif - -// Transmission of events from CPC interfaces to PC can be individually -// controlled per event type. Default state is: don't transmit -// Control values are constructed by bit-or of Subject and Action -// and passed to CPC_Control() - -// Control-Values for CPC_Control() Command Subject Selection -#define CONTR_CAN_Message 0x04 -#define CONTR_Busload 0x08 -#define CONTR_CAN_State 0x0C -#define CONTR_SendAck 0x10 -#define CONTR_Filter 0x14 -#define CONTR_CmdQueue 0x18 // reserved, do not use -#define CONTR_BusError 0x1C - -// Control Command Actions -#define CONTR_CONT_OFF 0 -#define CONTR_CONT_ON 1 -#define CONTR_SING_ON 2 -// CONTR_SING_ON doesn't change CONTR_CONT_ON state, so it should be -// read as: transmit at least once - -// defines for confirmed request -#define DO_NOT_CONFIRM 0 -#define DO_CONFIRM 1 - -// event flags -#define EVENT_READ 0x01 -#define EVENT_WRITE 0x02 - -// Messages from CPC to PC contain a message object type field. -// The following message types are sent by CPC and can be used in -// handlers, others should be ignored. -#define CPC_MSG_T_RESYNC 0 // Normally to be ignored -#define CPC_MSG_T_CAN 1 // CAN data frame -#define CPC_MSG_T_BUSLOAD 2 // Busload message -#define CPC_MSG_T_STRING 3 // Normally to be ignored -#define CPC_MSG_T_CONTI 4 // Normally to be ignored -#define CPC_MSG_T_MEM 7 // Normally not to be handled -#define CPC_MSG_T_RTR 8 // CAN remote frame -#define CPC_MSG_T_TXACK 9 // Send acknowledge -#define CPC_MSG_T_POWERUP 10 // Power-up message -#define CPC_MSG_T_CMD_NO 11 // Normally to be ignored -#define CPC_MSG_T_CAN_PRMS 12 // Actual CAN parameters -#define CPC_MSG_T_ABORTED 13 // Command aborted message -#define CPC_MSG_T_CANSTATE 14 // CAN state message -#define CPC_MSG_T_RESET 15 // used to reset CAN-Controller -#define CPC_MSG_T_XCAN 16 // XCAN data frame -#define CPC_MSG_T_XRTR 17 // XCAN remote frame -#define CPC_MSG_T_INFO 18 // information strings -#define CPC_MSG_T_CONTROL 19 // used for control of interface/driver behaviour -#define CPC_MSG_T_CONFIRM 20 // response type for confirmed requests -#define CPC_MSG_T_OVERRUN 21 // response type for overrun conditions -#define CPC_MSG_T_KEEPALIVE 22 // response type for keep alive conditions -#define CPC_MSG_T_CANERROR 23 // response type for bus error conditions -#define CPC_MSG_T_DISCONNECTED 24 // response type for a disconnected interface -#define CPC_MSG_T_ERR_COUNTER 25 // RX/TX error counter of CAN controller - -#define CPC_MSG_T_FIRMWARE 100 // response type for USB firmware download - -// Messages from the PC to the CPC interface contain a command field -// Most of the command types are wrapped by the library functions and have therefore -// normally not to be used. -// However, programmers who wish to circumvent the library and talk directly -// to the drivers (mainly Linux programmers) can use the following -// command types: - -#define CPC_CMD_T_CAN 1 // CAN data frame -#define CPC_CMD_T_CONTROL 3 // used for control of interface/driver behaviour -#define CPC_CMD_T_CAN_PRMS 6 // set CAN parameters -#define CPC_CMD_T_CLEARBUF 8 // clears input queue; this is depricated, use CPC_CMD_T_CLEAR_MSG_QUEUE instead -#define CPC_CMD_T_INQ_CAN_PARMS 11 // inquire actual CAN parameters -#define CPC_CMD_T_FILTER_PRMS 12 // set filter parameter -#define CPC_CMD_T_RTR 13 // CAN remote frame -#define CPC_CMD_T_CANSTATE 14 // CAN state message -#define CPC_CMD_T_XCAN 15 // XCAN data frame -#define CPC_CMD_T_XRTR 16 // XCAN remote frame -#define CPC_CMD_T_RESET 17 // used to reset CAN-Controller -#define CPC_CMD_T_INQ_INFO 18 // miscellanous information strings -#define CPC_CMD_T_OPEN_CHAN 19 // open a channel -#define CPC_CMD_T_CLOSE_CHAN 20 // close a channel -#define CPC_CMD_T_CNTBUF 21 // this is depricated, use CPC_CMD_T_INQ_MSG_QUEUE_CNT instead -#define CPC_CMD_T_CAN_EXIT 200 // exit the CAN (disable interrupts; reset bootrate; reset output_cntr; mode = 1) - -#define CPC_CMD_T_INQ_MSG_QUEUE_CNT CPC_CMD_T_CNTBUF // inquires the count of elements in the message queue -#define CPC_CMD_T_INQ_ERR_COUNTER 25 // request the CAN controllers error counter -#define CPC_CMD_T_CLEAR_MSG_QUEUE CPC_CMD_T_CLEARBUF // clear CPC_MSG queue -#define CPC_CMD_T_CLEAR_CMD_QUEUE 28 // clear CPC_CMD queue -#define CPC_CMD_T_FIRMWARE 100 // reserved, must not be used -#define CPC_CMD_T_USB_RESET 101 // reserved, must not be used -#define CPC_CMD_T_WAIT_NOTIFY 102 // reserved, must not be used -#define CPC_CMD_T_WAIT_SETUP 103 // reserved, must not be used -#define CPC_CMD_T_ABORT 255 // Normally not to be used - -// definitions for CPC_MSG_T_INFO -// information sources -#define CPC_INFOMSG_T_UNKNOWN_SOURCE 0 -#define CPC_INFOMSG_T_INTERFACE 1 -#define CPC_INFOMSG_T_DRIVER 2 -#define CPC_INFOMSG_T_LIBRARY 3 - -// information types -#define CPC_INFOMSG_T_UNKNOWN_TYPE 0 -#define CPC_INFOMSG_T_VERSION 1 -#define CPC_INFOMSG_T_SERIAL 2 - -// definitions for controller types -#define PCA82C200 1 // Philips basic CAN controller, replaced by SJA1000 -#define SJA1000 2 // Philips basic CAN controller -#define AN82527 3 // Intel full CAN controller -#define M16C_BASIC 4 // M16C controller running in basic CAN (not full CAN) mode - -// channel open error codes -#define CPC_ERR_NO_FREE_CHANNEL -1 // no more free space within the channel array -#define CPC_ERR_CHANNEL_ALREADY_OPEN -2 // the channel is already open -#define CPC_ERR_CHANNEL_NOT_ACTIVE -3 // access to a channel not active failed -#define CPC_ERR_NO_DRIVER_PRESENT -4 // no driver at the location searched by the library -#define CPC_ERR_NO_INIFILE_PRESENT -5 // the library could not find the inifile -#define CPC_ERR_WRONG_PARAMETERS -6 // wrong parameters in the inifile -#define CPC_ERR_NO_INTERFACE_PRESENT -7 // 1. The specified interface is not connected - // 2. The interface (mostly CPC-USB) was disconnected upon operation -#define CPC_ERR_NO_MATCHING_CHANNEL -8 // the driver couldn't find a matching channel -#define CPC_ERR_NO_BUFFER_AVAILABLE -9 // the driver couldn't allocate buffer for messages -#define CPC_ERR_NO_INTERRUPT -10 // the requested interrupt couldn't be claimed -#define CPC_ERR_NO_MATCHING_INTERFACE -11 // no interface type related to this channel was found -#define CPC_ERR_NO_RESOURCES -12 // the requested resources could not be claimed -#define CPC_ERR_SOCKET -13 // error concerning TCP sockets - -// init error codes -#define CPC_ERR_WRONG_CONTROLLER_TYPE -14 // wrong CAN controller type within initialization -#define CPC_ERR_NO_RESET_MODE -15 // the controller could not be set into reset mode -#define CPC_ERR_NO_CAN_ACCESS -16 // the CAN controller could not be accessed - -// transmit error codes -#define CPC_ERR_CAN_WRONG_ID -20 // the provided CAN id is too big -#define CPC_ERR_CAN_WRONG_LENGTH -21 // the provided CAN length is too long -#define CPC_ERR_CAN_NO_TRANSMIT_BUF -22 // the transmit buffer was occupied -#define CPC_ERR_CAN_TRANSMIT_TIMEOUT -23 // The message could not be sent within a - // specified time - -// other error codes -#define CPC_ERR_SERVICE_NOT_SUPPORTED -30 // the requested service is not supported by the interface -#define CPC_ERR_IO_TRANSFER -31 // a transmission error down to the driver occurred -#define CPC_ERR_TRANSMISSION_FAILED -32 // a transmission error down to the interface occurred -#define CPC_ERR_TRANSMISSION_TIMEOUT -33 // a timeout occurred within transmission to the interface -#define CPC_ERR_OP_SYS_NOT_SUPPORTED -35 // the operating system is not supported -#define CPC_ERR_UNKNOWN -40 // an unknown error ocurred (mostly IOCTL errors) - -#define CPC_ERR_LOADING_DLL -50 // the library 'cpcwin.dll' could not be loaded -#define CPC_ERR_ASSIGNING_FUNCTION -51 // the specified function could not be assigned -#define CPC_ERR_DLL_INITIALIZATION -52 // the DLL was not initialized correctly -#define CPC_ERR_MISSING_LICFILE -55 // the file containing the licenses does not exist -#define CPC_ERR_MISSING_LICENSE -56 // a required license was not found - -// CAN state bit values. Ignore any bits not listed -#define CPC_CAN_STATE_BUSOFF 0x80 -#define CPC_CAN_STATE_ERROR 0x40 - -// Mask to help ignore undefined bits -#define CPC_CAN_STATE_MASK 0xc0 - -// CAN-Message representation in a CPC_MSG -// Message object type is CPC_MSG_T_CAN or CPC_MSG_T_RTR -// or CPC_MSG_T_XCAN or CPC_MSG_T_XRTR -typedef struct CPC_CAN_MSG { - u32 id; - u8 length; - u8 msg[8]; -} CPC_CAN_MSG_T; - - -// representation of the CAN parameters for the PCA82C200 controller -typedef struct CPC_PCA82C200_PARAMS { - u8 acc_code; // Acceptance-code for receive, Standard: 0 - u8 acc_mask; // Acceptance-mask for receive, Standard: 0xff (everything) - u8 btr0; // Bus-timing register 0 - u8 btr1; // Bus-timing register 1 - u8 outp_contr; // Output-control register -} CPC_PCA82C200_PARAMS_T; - -// representation of the CAN parameters for the SJA1000 controller -typedef struct CPC_SJA1000_PARAMS { - u8 mode; // enables single or dual acceptance filtering - u8 acc_code0; // Acceptance-code for receive, Standard: 0 - u8 acc_code1; - u8 acc_code2; - u8 acc_code3; - u8 acc_mask0; // Acceptance-mask for receive, Standard: 0xff (everything) - u8 acc_mask1; - u8 acc_mask2; - u8 acc_mask3; - u8 btr0; // Bus-timing register 0 - u8 btr1; // Bus-timing register 1 - u8 outp_contr; // Output-control register -} CPC_SJA1000_PARAMS_T; - -// representation of the CAN parameters for the M16C controller -// in basic CAN mode (means no full CAN) -typedef struct CPC_M16C_BASIC_PARAMS { - u8 con0; - u8 con1; - u8 ctlr0; - u8 ctlr1; - u8 clk; - u8 acc_std_code0; - u8 acc_std_code1; - u8 acc_ext_code0; - u8 acc_ext_code1; - u8 acc_ext_code2; - u8 acc_ext_code3; - u8 acc_std_mask0; - u8 acc_std_mask1; - u8 acc_ext_mask0; - u8 acc_ext_mask1; - u8 acc_ext_mask2; - u8 acc_ext_mask3; -} CPC_M16C_BASIC_PARAMS_T; - -// CAN params message representation -typedef struct CPC_CAN_PARAMS { - u8 cc_type; // represents the controller type - union { - CPC_M16C_BASIC_PARAMS_T m16c_basic; - CPC_SJA1000_PARAMS_T sja1000; - CPC_PCA82C200_PARAMS_T pca82c200; - } cc_params; -} CPC_CAN_PARAMS_T; - -// the following structures are slightly different for Windows and Linux -// To be able to use the 'Select' mechanism with Linux the application -// needs to know the devices file desciptor. -// This mechanism is not implemented within Windows and the file descriptor -// is therefore not needed -#ifdef _WIN32 - -// CAN init params message representation -typedef struct CPC_INIT_PARAMS { - CPC_CAN_PARAMS_T canparams; -} CPC_INIT_PARAMS_T; - -#else// Linux - -// CHAN init params representation -typedef struct CPC_CHAN_PARAMS { - int fd; -} CPC_CHAN_PARAMS_T; - -// CAN init params message representation -typedef struct CPC_INIT_PARAMS { - CPC_CHAN_PARAMS_T chanparams; - CPC_CAN_PARAMS_T canparams; -} CPC_INIT_PARAMS_T; - -#endif - -// structure for confirmed message handling -typedef struct CPC_CONFIRM { - u8 result; // error code -} CPC_CONFIRM_T; - -// structure for information requests -typedef struct CPC_INFO { - u8 source; // interface, driver or library - u8 type; // version or serial number - char msg[CPC_MSG_LEN - 2]; // string holding the requested information -} CPC_INFO_T; - -// OVERRUN /////////////////////////////////////// -// In general two types of overrun may occur. -// A hardware overrun, where the CAN controller -// lost a message, because the interrupt was -// not handled before the next messgae comes in. -// Or a software overrun, where i.e. a received -// message could not be stored in the CPC_MSG -// buffer. - -// After a software overrun has occurred -// we wait until we have CPC_OVR_GAP slots -// free in the CPC_MSG buffer. -#define CPC_OVR_GAP 10 - -// Two types of software overrun may occur. -// A received CAN message or a CAN state event -// can cause an overrun. -// Note: A CPC_CMD which would normally store -// its result immediately in the CPC_MSG -// queue may fail, because the message queue is full. -// This will not generate an overrun message, but -// will halt command execution, until this command -// is able to store its message in the message queue. -#define CPC_OVR_EVENT_CAN 0x01 -#define CPC_OVR_EVENT_CANSTATE 0x02 -#define CPC_OVR_EVENT_BUSERROR 0x04 - -// If the CAN controller lost a message -// we indicate it with the highest bit -// set in the count field. -#define CPC_OVR_HW 0x80 - -// structure for overrun conditions -typedef struct { - u8 event; - u8 count; -} CPC_OVERRUN_T; - -// CAN errors //////////////////////////////////// -// Each CAN controller type has different -// registers to record errors. -// Therefor a structure containing the specific -// errors is set up for each controller here - -// SJA1000 error structure -// see the SJA1000 datasheet for detailed -// explanation of the registers -typedef struct CPC_SJA1000_CAN_ERROR { - u8 ecc; // error capture code register - u8 rxerr; // RX error counter register - u8 txerr; // TX error counter register -} CPC_SJA1000_CAN_ERROR_T; - -// M16C error structure -// see the M16C datasheet for detailed -// explanation of the registers -typedef struct CPC_M16C_CAN_ERROR { - u8 tbd; // to be defined -} CPC_M16C_CAN_ERROR_T; - -// structure for CAN error conditions -#define CPC_CAN_ECODE_ERRFRAME 0x01 -typedef struct CPC_CAN_ERROR { - u8 ecode; - struct { - u8 cc_type; // CAN controller type - union { - CPC_SJA1000_CAN_ERROR_T sja1000; - CPC_M16C_CAN_ERROR_T m16c; - } regs; - } cc; -} CPC_CAN_ERROR_T; - -// Structure containing RX/TX error counter. -// This structure is used to request the -// values of the CAN controllers TX and RX -// error counter. -typedef struct CPC_CAN_ERR_COUNTER { - u8 rx; - u8 tx; -} CPC_CAN_ERR_COUNTER_T; - -// If this flag is set, transmissions from PC to CPC are protected against loss -#define CPC_SECURE_TO_CPC 0x01 - -// If this flag is set, transmissions from CPC to PC are protected against loss -#define CPC_SECURE_TO_PC 0x02 - -// If this flag is set, the CAN-transmit buffer is checked to be free before sending a message -#define CPC_SECURE_SEND 0x04 - -// If this flag is set, the transmission complete flag is checked -// after sending a message -// THIS IS CURRENTLY ONLY IMPLEMENTED IN THE PASSIVE INTERFACE DRIVERS -#define CPC_SECURE_TRANSMIT 0x08 - -// main message type used between library and application -typedef struct CPC_MSG { - u8 type; // type of message - u8 length; // length of data within union 'msg' - u8 msgid; // confirmation handle - u32 ts_sec; // timestamp in seconds - u32 ts_nsec; // timestamp in nano seconds - union { - u8 generic[CPC_MSG_LEN]; - CPC_CAN_MSG_T canmsg; - CPC_CAN_PARAMS_T canparams; - CPC_CONFIRM_T confirmation; - CPC_INFO_T info; - CPC_OVERRUN_T overrun; - CPC_CAN_ERROR_T error; - CPC_CAN_ERR_COUNTER_T err_counter; - u8 busload; - u8 canstate; - } msg; -} CPC_MSG_T; - -#ifdef _WIN32 -#pragma pack(pop) // reset the byte alignment -#endif - -#endif // CPC_HEADER diff --git a/drivers/staging/cpc-usb/cpc_int.h b/drivers/staging/cpc-usb/cpc_int.h deleted file mode 100644 index a0d60c08081..00000000000 --- a/drivers/staging/cpc-usb/cpc_int.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * CPCLIB - * - * Copyright (C) 2000-2008 EMS Dr. Thomas Wuensche - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - */ -#ifndef CPC_INT_H -#define CPC_INT_H - -#include - -#define CPC_MSG_BUF_CNT 1500 - -#define CPC_PROC_DIR "driver/" - -#undef dbg -#undef err -#undef info - -/* Use our own dbg macro */ -#define dbg(format, arg...) do { if (debug) printk( KERN_INFO format "\n" , ## arg); } while (0) -#define err(format, arg...) do { printk( KERN_INFO "ERROR " format "\n" , ## arg); } while (0) -#define info(format, arg...) do { printk( KERN_INFO format "\n" , ## arg); } while (0) - -/* Macros help using of our buffers */ -#define IsBufferFull(x) (!(x)->WnR) && ((x)->iidx == (x)->oidx) -#define IsBufferEmpty(x) ((x)->WnR) && ((x)->iidx == (x)->oidx) -#define IsBufferNotEmpty(x) (!(x)->WnR) || ((x)->iidx != (x)->oidx) -#define ResetBuffer(x) do { (x)->oidx = (x)->iidx=0; (x)->WnR = 1; } while(0); - -#define CPC_BufWriteAllowed ((chan->oidx != chan->iidx) || chan->WnR) - -typedef void (*chan_write_byte_t) (void *chan, unsigned int reg, - unsigned char val); -typedef unsigned char (*chan_read_byte_t) (void *chan, unsigned int reg); - -typedef struct CPC_CHAN { - void __iomem * canBase; // base address of SJA1000 - chan_read_byte_t read_byte; // CAN controller read access routine - chan_write_byte_t write_byte; // CAN controller write access routine - CPC_MSG_T *buf; // buffer for CPC msg - unsigned int iidx; - unsigned int oidx; - unsigned int WnR; - unsigned int minor; - unsigned int locked; - unsigned int irqDisabled; - - unsigned char cpcCtrlCANMessage; - unsigned char cpcCtrlCANState; - unsigned char cpcCtrlBUSState; - - unsigned char controllerType; - - unsigned long ovrTimeSec; - unsigned long ovrTimeNSec; - unsigned long ovrLockedBuffer; - CPC_OVERRUN_T ovr; - - /* for debugging only */ - unsigned int handledIrqs; - unsigned int lostMessages; - - unsigned int sentStdCan; - unsigned int sentExtCan; - unsigned int sentStdRtr; - unsigned int sentExtRtr; - - unsigned int recvStdCan; - unsigned int recvExtCan; - unsigned int recvStdRtr; - unsigned int recvExtRtr; - - wait_queue_head_t *CPCWait_q; - - void *private; -} CPC_CHAN_T; - -#endif diff --git a/drivers/staging/cpc-usb/cpcusb.h b/drivers/staging/cpc-usb/cpcusb.h deleted file mode 100644 index e5273ddd9e0..00000000000 --- a/drivers/staging/cpc-usb/cpcusb.h +++ /dev/null @@ -1,86 +0,0 @@ -/* Header for CPC-USB Driver ******************** - * Copyright 1999, 2000, 2001 - * - * Company: EMS Dr. Thomas Wuensche - * Sonnenhang 3 - * 85304 Ilmmuenster - * Phone: +49-8441-490260 - * Fax: +49-8441-81860 - * email: support@ems-wuensche.com - * WWW: www.ems-wuensche.com - */ - -#ifndef CPCUSB_H -#define CPCUSB_H - -#undef err -#undef dbg -#undef info - -/* Use our own dbg macro */ -#define dbg(format, arg...) do { if (debug) printk(KERN_INFO "CPC-USB: " format "\n" , ## arg); } while (0) -#define info(format, arg...) do { printk(KERN_INFO "CPC-USB: " format "\n" , ## arg); } while (0) -#define err(format, arg...) do { printk(KERN_INFO "CPC-USB(ERROR): " format "\n" , ## arg); } while (0) - -#define CPC_USB_CARD_CNT 4 - -typedef struct CPC_USB_READ_URB { - unsigned char *buffer; /* the buffer to send data */ - size_t size; /* the size of the send buffer */ - struct urb *urb; /* the urb used to send data */ -} CPC_USB_READ_URB_T; - -typedef struct CPC_USB_WRITE_URB { - unsigned char *buffer; /* the buffer to send data */ - size_t size; /* the size of the send buffer */ - struct urb *urb; /* the urb used to send data */ - atomic_t busy; /* true if write urb is busy */ - struct completion finished; /* wait for the write to finish */ -} CPC_USB_WRITE_URB_T; - -#define CPC_USB_URB_CNT 10 - -typedef struct CPC_USB { - struct usb_device *udev; /* save off the usb device pointer */ - struct usb_interface *interface; /* the interface for this device */ - unsigned char minor; /* the starting minor number for this device */ - unsigned char num_ports; /* the number of ports this device has */ - int num_intr_in; /* number of interrupt in endpoints we have */ - int num_bulk_in; /* number of bulk in endpoints we have */ - int num_bulk_out; /* number of bulk out endpoints we have */ - - CPC_USB_READ_URB_T urbs[CPC_USB_URB_CNT]; - - unsigned char intr_in_buffer[4]; /* interrupt transfer buffer */ - struct urb *intr_in_urb; /* interrupt transfer urb */ - - CPC_USB_WRITE_URB_T wrUrbs[CPC_USB_URB_CNT]; - - int open; /* if the port is open or not */ - int present; /* if the device is not disconnected */ - struct semaphore sem; /* locks this structure */ - - int free_slots; /* free send slots of CPC-USB */ - int idx; - - spinlock_t slock; - - char serialNumber[128]; /* serial number */ - int productId; /* product id to differ between M16C and LPC2119 */ - CPC_CHAN_T *chan; -} CPC_USB_T; - -#define CPCTable CPCUSB_Table - -#define CPC_DRIVER_VERSION "0.724" -#define CPC_DRIVER_SERIAL "not applicable" - -#define OBUF_SIZE 255 // 4096 - -/* read timeouts -- RD_NAK_TIMEOUT * RD_EXPIRE = Number of seconds */ -#define RD_NAK_TIMEOUT (10*HZ) /* Default number of X seconds to wait */ -#define RD_EXPIRE 12 /* Number of attempts to wait X seconds */ - -#define CPC_USB_BASE_MNR 0 /* CPC-USB start at minor 0 */ - -#endif diff --git a/drivers/staging/cpc-usb/sja2m16c.h b/drivers/staging/cpc-usb/sja2m16c.h deleted file mode 100644 index 654bd3fc91d..00000000000 --- a/drivers/staging/cpc-usb/sja2m16c.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _SJA2M16C_H -#define _SJA2M16C_H - -#include "cpc.h" - -#define BAUDRATE_TOLERANCE_PERCENT 1 -#define SAMPLEPOINT_TOLERANCE_PERCENT 5 -#define SAMPLEPOINT_UPPER_LIMIT 88 - -/* M16C parameters */ -struct FIELD_C0CONR { - unsigned int brp:4; - unsigned int sam:1; - unsigned int pr:3; - unsigned int dummy:8; -}; -struct FIELD_C1CONR { - unsigned int ph1:3; - unsigned int ph2:3; - unsigned int sjw:2; - unsigned int dummy:8; -}; -typedef union C0CONR { - unsigned char c0con; - struct FIELD_C0CONR bc0con; -} C0CONR_T; -typedef union C1CONR { - unsigned char c1con; - struct FIELD_C1CONR bc1con; -} C1CONR_T; - -#define SJA_TSEG1 ((pParams->btr1 & 0x0f)+1) -#define SJA_TSEG2 (((pParams->btr1 & 0x70)>>4)+1) -#define SJA_BRP ((pParams->btr0 & 0x3f)+1) -#define SJA_SJW ((pParams->btr0 & 0xc0)>>6) -#define SJA_SAM ((pParams->btr1 & 0x80)>>7) -int baudrate_m16c(int clk, int brp, int pr, int ph1, int ph2); -int samplepoint_m16c(int brp, int pr, int ph1, int ph2); -int SJA1000_TO_M16C_BASIC_Params(CPC_MSG_T *pMsg); - -#endif diff --git a/drivers/staging/cpc-usb/sja2m16c_2.c b/drivers/staging/cpc-usb/sja2m16c_2.c deleted file mode 100644 index bf0230fb778..00000000000 --- a/drivers/staging/cpc-usb/sja2m16c_2.c +++ /dev/null @@ -1,452 +0,0 @@ -/**************************************************************************** -* -* Copyright (c) 2003,2004 by EMS Dr. Thomas Wuensche -* -* - All rights reserved - -* -* This code is provided "as is" without warranty of any kind, either -* expressed or implied, including but not limited to the liability -* concerning the freedom from material defects, the fitness for parti- -* cular purposes or the freedom of proprietary rights of third parties. -* -***************************************************************************** -* Module name.: cpcusb -***************************************************************************** -* Include file: cpc.h -***************************************************************************** -* Project.....: Windows Driver Development Kit -* Filename....: sja2m16c.cpp -* Authors.....: (GU) Gerhard Uttenthaler -* (CS) Christian Schoett -***************************************************************************** -* Short descr.: converts baudrate between SJA1000 and M16C -***************************************************************************** -* Description.: handles the baudrate conversion from SJA1000 parameters to -* M16C parameters -***************************************************************************** -* Address : EMS Dr. Thomas Wuensche -* Sonnenhang 3 -* D-85304 Ilmmuenster -* Tel. : +49-8441-490260 -* Fax. : +49-8441-81860 -* email: support@ems-wuensche.com -***************************************************************************** -* History -***************************************************************************** -* Version Date Auth Remark -* -* 01.00 ?? GU - initial release -* 01.10 ?????????? CS - adapted to fit into the USB Windows driver -* 02.00 18.08.2004 GU - improved the baudrate calculating algorithm -* - implemented acceptance filtering -* 02.10 10.09.2004 CS - adapted to fit into the USB Windows driver -***************************************************************************** -* ToDo's -***************************************************************************** -*/ - -/****************************************************************************/ -/* I N C L U D E S -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpc.h" -#include "cpc_int.h" -#include "cpcusb.h" - -#include "sja2m16c.h" - -/*********************************************************************/ -int baudrate_m16c(int clk, int brp, int pr, int ph1, int ph2) -{ - return (16000000 / (1 << clk)) / 2 / (brp + 1) / (1 + pr + 1 + - ph1 + 1 + ph2 + - 1); -} - - -/*********************************************************************/ -int samplepoint_m16c(int brp, int pr, int ph1, int ph2) -{ - return (100 * (1 + pr + 1 + ph1 + 1)) / (1 + pr + 1 + ph1 + 1 + - ph2 + 1); -} - - -/**************************************************************************** -* Function.....: SJA1000_TO_M16C_BASIC_Params -* -* Task.........: This routine converts SJA1000 CAN btr parameters into M16C -* parameters based on the sample point and the error. In -* addition it converts the acceptance filter parameters to -* suit the M16C parameters -* -* Parameters...: None -* -* Return values: None -* -* Comments.....: -***************************************************************************** -* History -***************************************************************************** -* 19.01.2005 CS - modifed the conversion of SJA1000 filter params into -* M16C params. Due to compatibility reasons with the -* older 82C200 CAN controller the SJA1000 -****************************************************************************/ -int SJA1000_TO_M16C_BASIC_Params(CPC_MSG_T * in) -{ - int sjaBaudrate; - int sjaSamplepoint; - int *baudrate_error; // BRP[0..15], PR[0..7], PH1[0..7], PH2[0..7] - int *samplepoint_error; // BRP[0..15], PR[0..7], PH1[0..7], PH2[0..7] - int baudrate_error_merk; - int clk, brp, pr, ph1, ph2; - int clk_merk, brp_merk, pr_merk, ph1_merk, ph2_merk; - int index; - unsigned char acc_code0, acc_code1, acc_code2, acc_code3; - unsigned char acc_mask0, acc_mask1, acc_mask2, acc_mask3; - CPC_MSG_T * out; - C0CONR_T c0con; - C1CONR_T c1con; - int tmpAccCode; - int tmpAccMask; - - // we have to convert the parameters into M16C parameters - CPC_SJA1000_PARAMS_T * pParams; - - // check if the type is CAN parameters and if we have to convert the given params - if (in->type != CPC_CMD_T_CAN_PRMS - || in->msg.canparams.cc_type != SJA1000) - return 0; - pParams = - (CPC_SJA1000_PARAMS_T *) & in->msg.canparams.cc_params.sja1000; - acc_code0 = pParams->acc_code0; - acc_code1 = pParams->acc_code1; - acc_code2 = pParams->acc_code2; - acc_code3 = pParams->acc_code3; - acc_mask0 = pParams->acc_mask0; - acc_mask1 = pParams->acc_mask1; - acc_mask2 = pParams->acc_mask2; - acc_mask3 = pParams->acc_mask3; - -#ifdef _DEBUG_OUTPUT_CAN_PARAMS - info("acc_code0: %2.2Xh\n", acc_code0); - info("acc_code1: %2.2Xh\n", acc_code1); - info("acc_code2: %2.2Xh\n", acc_code2); - info("acc_code3: %2.2Xh\n", acc_code3); - info("acc_mask0: %2.2Xh\n", acc_mask0); - info("acc_mask1: %2.2Xh\n", acc_mask1); - info("acc_mask2: %2.2Xh\n", acc_mask2); - info("acc_mask3: %2.2Xh\n", acc_mask3); - -#endif /* */ - if (! - (baudrate_error = - (int *) vmalloc(sizeof(int) * 16 * 8 * 8 * 8 * 5))) { - err("Could not allocate memory\n"); - return -3; - } - if (! - (samplepoint_error = - (int *) vmalloc(sizeof(int) * 16 * 8 * 8 * 8 * 5))) { - err("Could not allocate memory\n"); - vfree(baudrate_error); - return -3; - } - memset(baudrate_error, 0xff, sizeof(baudrate_error)); - memset(samplepoint_error, 0xff, sizeof(baudrate_error)); - sjaBaudrate = - 16000000 / 2 / SJA_BRP / (1 + SJA_TSEG1 + SJA_TSEG2); - sjaSamplepoint = - 100 * (1 + SJA_TSEG1) / (1 + SJA_TSEG1 + SJA_TSEG2); - if (sjaBaudrate == 0) { - vfree(baudrate_error); - vfree(samplepoint_error); - return -2; - } - -#ifdef _DEBUG_OUTPUT_CAN_PARAMS - info("\nStarting SJA CAN params\n"); - info("-------------------------\n"); - info("TS1 : %2.2Xh TS2 : %2.2Xh\n", SJA_TSEG1, SJA_TSEG2); - info("BTR0 : %2.2Xh BTR1: %2.2Xh\n", pParams->btr0, - pParams->btr1); - info("Baudrate: %d.%dkBaud\n", sjaBaudrate / 1000, - sjaBaudrate % 1000); - info("Sample P: 0.%d\n", sjaSamplepoint); - info("\n"); - -#endif /* */ - c0con.bc0con.sam = SJA_SAM; - c1con.bc1con.sjw = SJA_SJW; - - // calculate errors for all baudrates - index = 0; - for (clk = 0; clk < 5; clk++) { - for (brp = 0; brp < 16; brp++) { - for (pr = 0; pr < 8; pr++) { - for (ph1 = 0; ph1 < 8; ph1++) { - for (ph2 = 0; ph2 < 8; ph2++) { - baudrate_error[index] = - 100 * - abs(baudrate_m16c - (clk, brp, pr, ph1, - ph2) - - sjaBaudrate) / - sjaBaudrate; - samplepoint_error[index] = - abs(samplepoint_m16c - (brp, pr, ph1, - ph2) - - sjaSamplepoint); - -#if 0 - info - ("Baudrate : %d kBaud\n", - baudrate_m16c(clk, - brp, pr, - ph1, - ph2)); - info - ("Baudrate Error: %d\n", - baudrate_error - [index]); - info - ("Sample P Error: %d\n", - samplepoint_error - [index]); - info - ("clk : %d\n", - clk); - -#endif /* */ - index++; - } - } - } - } - } - - // mark all baudrate_error entries which are outer limits - index = 0; - for (clk = 0; clk < 5; clk++) { - for (brp = 0; brp < 16; brp++) { - for (pr = 0; pr < 8; pr++) { - for (ph1 = 0; ph1 < 8; ph1++) { - for (ph2 = 0; ph2 < 8; ph2++) { - if ((baudrate_error[index] - > - BAUDRATE_TOLERANCE_PERCENT) - || - (samplepoint_error - [index] > - SAMPLEPOINT_TOLERANCE_PERCENT) - || - (samplepoint_m16c - (brp, pr, ph1, - ph2) > - SAMPLEPOINT_UPPER_LIMIT)) - { - baudrate_error - [index] = -1; - } else - if (((1 + pr + 1 + - ph1 + 1 + ph2 + - 1) < 8) - || - ((1 + pr + 1 + - ph1 + 1 + ph2 + - 1) > 25)) { - baudrate_error - [index] = -1; - } - -#if 0 - else { - info - ("Baudrate : %d kBaud\n", - baudrate_m16c - (clk, brp, pr, - ph1, ph2)); - info - ("Baudrate Error: %d\n", - baudrate_error - [index]); - info - ("Sample P Error: %d\n", - samplepoint_error - [index]); - } - -#endif /* */ - index++; - } - } - } - } - } - - // find list of minimum of baudrate_error within unmarked entries - clk_merk = brp_merk = pr_merk = ph1_merk = ph2_merk = 0; - baudrate_error_merk = 100; - index = 0; - for (clk = 0; clk < 5; clk++) { - for (brp = 0; brp < 16; brp++) { - for (pr = 0; pr < 8; pr++) { - for (ph1 = 0; ph1 < 8; ph1++) { - for (ph2 = 0; ph2 < 8; ph2++) { - if (baudrate_error[index] - != -1) { - if (baudrate_error - [index] < - baudrate_error_merk) - { - baudrate_error_merk - = - baudrate_error - [index]; - brp_merk = - brp; - pr_merk = - pr; - ph1_merk = - ph1; - ph2_merk = - ph2; - clk_merk = - clk; - -#if 0 - info - ("brp: %2.2Xh pr: %2.2Xh ph1: %2.2Xh ph2: %2.2Xh\n", - brp, - pr, - ph1, - ph2); - info - ("Baudrate : %d kBaud\n", - baudrate_m16c - (clk, - brp, - pr, - ph1, - ph2)); - info - ("Baudrate Error: %d\n", - baudrate_error - [index]); - info - ("Sample P Error: %d\n", - samplepoint_error - [index]); - -#endif /* */ - } - } - index++; - } - } - } - } - } - if (baudrate_error_merk == 100) { - info("ERROR: Could not convert CAN init parameter\n"); - vfree(baudrate_error); - vfree(samplepoint_error); - return -1; - } - - // setting m16c CAN parameter - c0con.bc0con.brp = brp_merk; - c0con.bc0con.pr = pr_merk; - c1con.bc1con.ph1 = ph1_merk; - c1con.bc1con.ph2 = ph2_merk; - -#ifdef _DEBUG_OUTPUT_CAN_PARAMS - info("\nResulting M16C CAN params\n"); - info("-------------------------\n"); - info("clk : %2.2Xh\n", clk_merk); - info("ph1 : %2.2Xh ph2: %2.2Xh\n", c1con.bc1con.ph1 + 1, - c1con.bc1con.ph2 + 1); - info("pr : %2.2Xh brp: %2.2Xh\n", c0con.bc0con.pr + 1, - c0con.bc0con.brp + 1); - info("sjw : %2.2Xh sam: %2.2Xh\n", c1con.bc1con.sjw, - c0con.bc0con.sam); - info("co1 : %2.2Xh co0: %2.2Xh\n", c1con.c1con, c0con.c0con); - info("Baudrate: %d.%dBaud\n", - baudrate_m16c(clk_merk, c0con.bc0con.brp, c0con.bc0con.pr, - c1con.bc1con.ph1, c1con.bc1con.ph2) / 1000, - baudrate_m16c(clk_merk, c0con.bc0con.brp, c0con.bc0con.pr, - c1con.bc1con.ph1, c1con.bc1con.ph2) % 1000); - info("Sample P: 0.%d\n", - samplepoint_m16c(c0con.bc0con.brp, c0con.bc0con.pr, - c1con.bc1con.ph1, c1con.bc1con.ph2)); - info("\n"); - -#endif /* */ - out = in; - out->type = 6; - out->length = sizeof(CPC_M16C_BASIC_PARAMS_T) + 1; - out->msg.canparams.cc_type = M16C_BASIC; - out->msg.canparams.cc_params.m16c_basic.con0 = c0con.c0con; - out->msg.canparams.cc_params.m16c_basic.con1 = c1con.c1con; - out->msg.canparams.cc_params.m16c_basic.ctlr0 = 0x4C; - out->msg.canparams.cc_params.m16c_basic.ctlr1 = 0x00; - out->msg.canparams.cc_params.m16c_basic.clk = clk_merk; - out->msg.canparams.cc_params.m16c_basic.acc_std_code0 = - acc_code0; - out->msg.canparams.cc_params.m16c_basic.acc_std_code1 = acc_code1; - -// info("code0: 0x%2.2X, code1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_code0, out->msg.canparams.cc_params.m16c_basic.acc_std_code1); - tmpAccCode = (acc_code1 >> 5) + (acc_code0 << 3); - out->msg.canparams.cc_params.m16c_basic.acc_std_code0 = - (unsigned char) tmpAccCode; - out->msg.canparams.cc_params.m16c_basic.acc_std_code1 = - (unsigned char) (tmpAccCode >> 8); - -// info("code0: 0x%2.2X, code1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_code0, out->msg.canparams.cc_params.m16c_basic.acc_std_code1); - out->msg.canparams.cc_params.m16c_basic.acc_std_mask0 = - ~acc_mask0; - out->msg.canparams.cc_params.m16c_basic.acc_std_mask1 = - ~acc_mask1; - -// info("mask0: 0x%2.2X, mask1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_mask0, out->msg.canparams.cc_params.m16c_basic.acc_std_mask1); - tmpAccMask = ((acc_mask1) >> 5) + ((acc_mask0) << 3); - -// info("tmpAccMask: 0x%4.4X\n", tmpAccMask); - out->msg.canparams.cc_params.m16c_basic.acc_std_mask0 = - (unsigned char) ~tmpAccMask; - out->msg.canparams.cc_params.m16c_basic.acc_std_mask1 = - (unsigned char) ~(tmpAccMask >> 8); - -// info("mask0: 0x%2.2X, mask1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_mask0, out->msg.canparams.cc_params.m16c_basic.acc_std_mask1); - out->msg.canparams.cc_params.m16c_basic.acc_ext_code0 = - (unsigned char) tmpAccCode; - out->msg.canparams.cc_params.m16c_basic.acc_ext_code1 = - (unsigned char) (tmpAccCode >> 8); - out->msg.canparams.cc_params.m16c_basic.acc_ext_code2 = acc_code2; - out->msg.canparams.cc_params.m16c_basic.acc_ext_code3 = acc_code3; - out->msg.canparams.cc_params.m16c_basic.acc_ext_mask0 = - (unsigned char) ~tmpAccMask; - out->msg.canparams.cc_params.m16c_basic.acc_ext_mask1 = - (unsigned char) ~(tmpAccMask >> 8); - out->msg.canparams.cc_params.m16c_basic.acc_ext_mask2 = - ~acc_mask2; - out->msg.canparams.cc_params.m16c_basic.acc_ext_mask3 = - ~acc_mask3; - vfree(baudrate_error); - vfree(samplepoint_error); - return 0; -} - - -- cgit v1.2.3 From 702171adeed3607ee9603ec30ce081411e36ae42 Mon Sep 17 00:00:00 2001 From: Sebastian Haas Date: Wed, 16 Sep 2009 02:04:20 +0000 Subject: ems_usb: Added support for EMS CPC-USB/ARM7 CAN/USB interface This patch adds support for one channel CAN/USB interace CPC-USB/ARM7 from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.com). Signed-off-by: Sebastian Haas Signed-off-by: Wolfgang Grandegger Signed-off-by: David S. Miller --- drivers/net/can/Kconfig | 7 + drivers/net/can/Makefile | 2 + drivers/net/can/usb/Makefile | 5 + drivers/net/can/usb/ems_usb.c | 1155 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1169 insertions(+) create mode 100644 drivers/net/can/usb/Makefile create mode 100644 drivers/net/can/usb/ems_usb.c (limited to 'drivers') diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 09007437246..6ac5aa588a3 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -75,6 +75,13 @@ config CAN_EMS_PCI CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). +config CAN_EMS_USB + tristate "EMS CPC-USB/ARM7 CAN/USB interface" + depends on USB && CAN_DEV + ---help--- + This driver is for the one channel CPC-USB/ARM7 CAN/USB interface + from from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). + config CAN_KVASER_PCI tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards" depends on PCI && CAN_SJA1000 diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 523a941b358..9b035ecb2f3 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_CAN_VCAN) += vcan.o obj-$(CONFIG_CAN_DEV) += can-dev.o can-dev-y := dev.o +obj-y += usb/ + obj-$(CONFIG_CAN_SJA1000) += sja1000/ ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile new file mode 100644 index 00000000000..c3f75ba701b --- /dev/null +++ b/drivers/net/can/usb/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Linux Controller Area Network USB drivers. +# + +obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c new file mode 100644 index 00000000000..9012e0abc62 --- /dev/null +++ b/drivers/net/can/usb/ems_usb.c @@ -0,0 +1,1155 @@ +/* + * CAN driver for EMS Dr. Thomas Wuensche CPC-USB/ARM7 + * + * Copyright (C) 2004-2009 EMS Dr. Thomas Wuensche + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("Sebastian Haas "); +MODULE_DESCRIPTION("CAN driver for EMS Dr. Thomas Wuensche CAN/USB interfaces"); +MODULE_LICENSE("GPL v2"); + +/* Control-Values for CPC_Control() Command Subject Selection */ +#define CONTR_CAN_MESSAGE 0x04 +#define CONTR_CAN_STATE 0x0C +#define CONTR_BUS_ERROR 0x1C + +/* Control Command Actions */ +#define CONTR_CONT_OFF 0 +#define CONTR_CONT_ON 1 +#define CONTR_ONCE 2 + +/* Messages from CPC to PC */ +#define CPC_MSG_TYPE_CAN_FRAME 1 /* CAN data frame */ +#define CPC_MSG_TYPE_RTR_FRAME 8 /* CAN remote frame */ +#define CPC_MSG_TYPE_CAN_PARAMS 12 /* Actual CAN parameters */ +#define CPC_MSG_TYPE_CAN_STATE 14 /* CAN state message */ +#define CPC_MSG_TYPE_EXT_CAN_FRAME 16 /* Extended CAN data frame */ +#define CPC_MSG_TYPE_EXT_RTR_FRAME 17 /* Extended remote frame */ +#define CPC_MSG_TYPE_CONTROL 19 /* change interface behavior */ +#define CPC_MSG_TYPE_CONFIRM 20 /* command processed confirmation */ +#define CPC_MSG_TYPE_OVERRUN 21 /* overrun events */ +#define CPC_MSG_TYPE_CAN_FRAME_ERROR 23 /* detected bus errors */ +#define CPC_MSG_TYPE_ERR_COUNTER 25 /* RX/TX error counter */ + +/* Messages from the PC to the CPC interface */ +#define CPC_CMD_TYPE_CAN_FRAME 1 /* CAN data frame */ +#define CPC_CMD_TYPE_CONTROL 3 /* control of interface behavior */ +#define CPC_CMD_TYPE_CAN_PARAMS 6 /* set CAN parameters */ +#define CPC_CMD_TYPE_RTR_FRAME 13 /* CAN remote frame */ +#define CPC_CMD_TYPE_CAN_STATE 14 /* CAN state message */ +#define CPC_CMD_TYPE_EXT_CAN_FRAME 15 /* Extended CAN data frame */ +#define CPC_CMD_TYPE_EXT_RTR_FRAME 16 /* Extended CAN remote frame */ +#define CPC_CMD_TYPE_CAN_EXIT 200 /* exit the CAN */ + +#define CPC_CMD_TYPE_INQ_ERR_COUNTER 25 /* request the CAN error counters */ +#define CPC_CMD_TYPE_CLEAR_MSG_QUEUE 8 /* clear CPC_MSG queue */ +#define CPC_CMD_TYPE_CLEAR_CMD_QUEUE 28 /* clear CPC_CMD queue */ + +#define CPC_CC_TYPE_SJA1000 2 /* Philips basic CAN controller */ + +#define CPC_CAN_ECODE_ERRFRAME 0x01 /* Ecode type */ + +/* Overrun types */ +#define CPC_OVR_EVENT_CAN 0x01 +#define CPC_OVR_EVENT_CANSTATE 0x02 +#define CPC_OVR_EVENT_BUSERROR 0x04 + +/* + * If the CAN controller lost a message we indicate it with the highest bit + * set in the count field. + */ +#define CPC_OVR_HW 0x80 + +/* Size of the "struct ems_cpc_msg" without the union */ +#define CPC_MSG_HEADER_LEN 11 +#define CPC_CAN_MSG_MIN_SIZE 5 + +/* Define these values to match your devices */ +#define USB_CPCUSB_VENDOR_ID 0x12D6 + +#define USB_CPCUSB_ARM7_PRODUCT_ID 0x0444 + +/* Mode register NXP LPC2119/SJA1000 CAN Controller */ +#define SJA1000_MOD_NORMAL 0x00 +#define SJA1000_MOD_RM 0x01 + +/* ECC register NXP LPC2119/SJA1000 CAN Controller */ +#define SJA1000_ECC_SEG 0x1F +#define SJA1000_ECC_DIR 0x20 +#define SJA1000_ECC_ERR 0x06 +#define SJA1000_ECC_BIT 0x00 +#define SJA1000_ECC_FORM 0x40 +#define SJA1000_ECC_STUFF 0x80 +#define SJA1000_ECC_MASK 0xc0 + +/* Status register content */ +#define SJA1000_SR_BS 0x80 +#define SJA1000_SR_ES 0x40 + +#define SJA1000_DEFAULT_OUTPUT_CONTROL 0xDA + +/* + * The device actually uses a 16MHz clock to generate the CAN clock + * but it expects SJA1000 bit settings based on 8MHz (is internally + * converted). + */ +#define EMS_USB_ARM7_CLOCK 8000000 + +/* + * CAN-Message representation in a CPC_MSG. Message object type is + * CPC_MSG_TYPE_CAN_FRAME or CPC_MSG_TYPE_RTR_FRAME or + * CPC_MSG_TYPE_EXT_CAN_FRAME or CPC_MSG_TYPE_EXT_RTR_FRAME. + */ +struct cpc_can_msg { + u32 id; + u8 length; + u8 msg[8]; +}; + +/* Representation of the CAN parameters for the SJA1000 controller */ +struct cpc_sja1000_params { + u8 mode; + u8 acc_code0; + u8 acc_code1; + u8 acc_code2; + u8 acc_code3; + u8 acc_mask0; + u8 acc_mask1; + u8 acc_mask2; + u8 acc_mask3; + u8 btr0; + u8 btr1; + u8 outp_contr; +}; + +/* CAN params message representation */ +struct cpc_can_params { + u8 cc_type; + + /* Will support M16C CAN controller in the future */ + union { + struct cpc_sja1000_params sja1000; + } cc_params; +}; + +/* Structure for confirmed message handling */ +struct cpc_confirm { + u8 error; /* error code */ +}; + +/* Structure for overrun conditions */ +struct cpc_overrun { + u8 event; + u8 count; +}; + +/* SJA1000 CAN errors (compatible to NXP LPC2119) */ +struct cpc_sja1000_can_error { + u8 ecc; + u8 rxerr; + u8 txerr; +}; + +/* structure for CAN error conditions */ +struct cpc_can_error { + u8 ecode; + + struct { + u8 cc_type; + + /* Other controllers may also provide error code capture regs */ + union { + struct cpc_sja1000_can_error sja1000; + } regs; + } cc; +}; + +/* + * Structure containing RX/TX error counter. This structure is used to request + * the values of the CAN controllers TX and RX error counter. + */ +struct cpc_can_err_counter { + u8 rx; + u8 tx; +}; + +/* Main message type used between library and application */ +struct __attribute__ ((packed)) ems_cpc_msg { + u8 type; /* type of message */ + u8 length; /* length of data within union 'msg' */ + u8 msgid; /* confirmation handle */ + u32 ts_sec; /* timestamp in seconds */ + u32 ts_nsec; /* timestamp in nano seconds */ + + union { + u8 generic[64]; + struct cpc_can_msg can_msg; + struct cpc_can_params can_params; + struct cpc_confirm confirmation; + struct cpc_overrun overrun; + struct cpc_can_error error; + struct cpc_can_err_counter err_counter; + u8 can_state; + } msg; +}; + +/* + * Table of devices that work with this driver + * NOTE: This driver supports only CPC-USB/ARM7 (LPC2119) yet. + */ +static struct usb_device_id ems_usb_table[] = { + {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_ARM7_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ems_usb_table); + +#define RX_BUFFER_SIZE 64 +#define CPC_HEADER_SIZE 4 +#define INTR_IN_BUFFER_SIZE 4 + +#define MAX_RX_URBS 10 +#define MAX_TX_URBS CAN_ECHO_SKB_MAX + +struct ems_usb; + +struct ems_tx_urb_context { + struct ems_usb *dev; + + u32 echo_index; + u8 dlc; +}; + +struct ems_usb { + struct can_priv can; /* must be the first member */ + int open_time; + + struct sk_buff *echo_skb[MAX_TX_URBS]; + + struct usb_device *udev; + struct net_device *netdev; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct ems_tx_urb_context tx_contexts[MAX_TX_URBS]; + + struct usb_anchor rx_submitted; + + struct urb *intr_urb; + + u8 *tx_msg_buffer; + + u8 *intr_in_buffer; + unsigned int free_slots; /* remember number of available slots */ + + struct ems_cpc_msg active_params; /* active controller parameters */ +}; + +static void ems_usb_read_interrupt_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev = dev->netdev; + int err; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: + dev->free_slots = dev->intr_in_buffer[1]; + break; + + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + dev_info(netdev->dev.parent, "Rx interrupt aborted %d\n", + urb->status); + break; + } + + err = usb_submit_urb(urb, GFP_ATOMIC); + + if (err == -ENODEV) + netif_device_detach(netdev); + else if (err) + dev_err(netdev->dev.parent, + "failed resubmitting intr urb: %d\n", err); + + return; +} + +static void ems_usb_rx_can_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + int i; + struct net_device_stats *stats = &dev->netdev->stats; + + skb = netdev_alloc_skb(dev->netdev, sizeof(struct can_frame)); + if (skb == NULL) + return; + + skb->protocol = htons(ETH_P_CAN); + + cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + + cf->can_id = msg->msg.can_msg.id; + cf->can_dlc = min_t(u8, msg->msg.can_msg.length, 8); + + if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME + || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) + cf->can_id |= CAN_EFF_FLAG; + + if (msg->type == CPC_MSG_TYPE_RTR_FRAME + || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = msg->msg.can_msg.msg[i]; + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &dev->netdev->stats; + + skb = netdev_alloc_skb(dev->netdev, sizeof(struct can_frame)); + if (skb == NULL) + return; + + skb->protocol = htons(ETH_P_CAN); + + cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + memset(cf, 0, sizeof(struct can_frame)); + + cf->can_id = CAN_ERR_FLAG; + cf->can_dlc = CAN_ERR_DLC; + + if (msg->type == CPC_MSG_TYPE_CAN_STATE) { + u8 state = msg->msg.can_state; + + if (state & SJA1000_SR_BS) { + dev->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + + can_bus_off(dev->netdev); + } else if (state & SJA1000_SR_ES) { + dev->can.state = CAN_STATE_ERROR_WARNING; + dev->can.can_stats.error_warning++; + } else { + dev->can.state = CAN_STATE_ERROR_ACTIVE; + dev->can.can_stats.error_passive++; + } + } else if (msg->type == CPC_MSG_TYPE_CAN_FRAME_ERROR) { + u8 ecc = msg->msg.error.cc.regs.sja1000.ecc; + u8 txerr = msg->msg.error.cc.regs.sja1000.txerr; + u8 rxerr = msg->msg.error.cc.regs.sja1000.rxerr; + + /* bus error interrupt */ + dev->can.can_stats.bus_error++; + stats->rx_errors++; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch (ecc & SJA1000_ECC_MASK) { + case SJA1000_ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case SJA1000_ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case SJA1000_ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + cf->data[3] = ecc & SJA1000_ECC_SEG; + break; + } + + /* Error occured during transmission? */ + if ((ecc & SJA1000_ECC_DIR) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + + if (dev->can.state == CAN_STATE_ERROR_WARNING || + dev->can.state == CAN_STATE_ERROR_PASSIVE) { + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE; + } + } else if (msg->type == CPC_MSG_TYPE_OVERRUN) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + stats->rx_over_errors++; + stats->rx_errors++; + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_read_bulk_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev; + int retval; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + return; + + default: + dev_info(netdev->dev.parent, "Rx URB aborted (%d)\n", + urb->status); + goto resubmit_urb; + } + + if (urb->actual_length > CPC_HEADER_SIZE) { + struct ems_cpc_msg *msg; + u8 *ibuf = urb->transfer_buffer; + u8 msg_count, again, start; + + msg_count = ibuf[0] & ~0x80; + again = ibuf[0] & 0x80; + + start = CPC_HEADER_SIZE; + + while (msg_count) { + msg = (struct ems_cpc_msg *)&ibuf[start]; + + switch (msg->type) { + case CPC_MSG_TYPE_CAN_STATE: + /* Process CAN state changes */ + ems_usb_rx_err(dev, msg); + break; + + case CPC_MSG_TYPE_CAN_FRAME: + case CPC_MSG_TYPE_EXT_CAN_FRAME: + case CPC_MSG_TYPE_RTR_FRAME: + case CPC_MSG_TYPE_EXT_RTR_FRAME: + ems_usb_rx_can_msg(dev, msg); + break; + + case CPC_MSG_TYPE_CAN_FRAME_ERROR: + /* Process errorframe */ + ems_usb_rx_err(dev, msg); + break; + + case CPC_MSG_TYPE_OVERRUN: + /* Message lost while receiving */ + ems_usb_rx_err(dev, msg); + break; + } + + start += CPC_MSG_HEADER_LEN + msg->length; + msg_count--; + + if (start > urb->transfer_buffer_length) { + dev_err(netdev->dev.parent, "format error\n"); + break; + } + } + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + urb->transfer_buffer, RX_BUFFER_SIZE, + ems_usb_read_bulk_callback, dev); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if (retval) + dev_err(netdev->dev.parent, + "failed resubmitting read bulk urb: %d\n", retval); + + return; +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_write_bulk_callback(struct urb *urb) +{ + struct ems_tx_urb_context *context = urb->context; + struct ems_usb *dev; + struct net_device *netdev; + + BUG_ON(!context); + + dev = context->dev; + netdev = dev->netdev; + + /* free up our allocated buffer */ + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + atomic_dec(&dev->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + dev_info(netdev->dev.parent, "Tx URB aborted (%d)\n", + urb->status); + + netdev->trans_start = jiffies; + + /* transmission complete interrupt */ + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += context->dlc; + + can_get_echo_skb(netdev, context->echo_index); + + /* Release context */ + context->echo_index = MAX_TX_URBS; + + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); +} + +/* + * Send the given CPC command synchronously + */ +static int ems_usb_command_msg(struct ems_usb *dev, struct ems_cpc_msg *msg) +{ + int actual_length; + + /* Copy payload */ + memcpy(&dev->tx_msg_buffer[CPC_HEADER_SIZE], msg, + msg->length + CPC_MSG_HEADER_LEN); + + /* Clear header */ + memset(&dev->tx_msg_buffer[0], 0, CPC_HEADER_SIZE); + + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), + &dev->tx_msg_buffer[0], + msg->length + CPC_MSG_HEADER_LEN + CPC_HEADER_SIZE, + &actual_length, 1000); +} + +/* + * Change CAN controllers' mode register + */ +static int ems_usb_write_mode(struct ems_usb *dev, u8 mode) +{ + dev->active_params.msg.can_params.cc_params.sja1000.mode = mode; + + return ems_usb_command_msg(dev, &dev->active_params); +} + +/* + * Send a CPC_Control command to change behaviour when interface receives a CAN + * message, bus error or CAN state changed notifications. + */ +static int ems_usb_control_cmd(struct ems_usb *dev, u8 val) +{ + struct ems_cpc_msg cmd; + + cmd.type = CPC_CMD_TYPE_CONTROL; + cmd.length = CPC_MSG_HEADER_LEN + 1; + + cmd.msgid = 0; + + cmd.msg.generic[0] = val; + + return ems_usb_command_msg(dev, &cmd); +} + +/* + * Start interface + */ +static int ems_usb_start(struct ems_usb *dev) +{ + struct net_device *netdev = dev->netdev; + int err, i; + + dev->intr_in_buffer[0] = 0; + dev->free_slots = 15; /* initial size */ + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_err(netdev->dev.parent, + "No memory left for URBs\n"); + return -ENOMEM; + } + + buf = usb_buffer_alloc(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + dev_err(netdev->dev.parent, + "No memory left for USB buffer\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + buf, RX_BUFFER_SIZE, + ems_usb_read_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + usb_unanchor_urb(urb); + usb_buffer_free(dev->udev, RX_BUFFER_SIZE, buf, + urb->transfer_dma); + break; + } + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* Did we submit any URBs */ + if (i == 0) { + dev_warn(netdev->dev.parent, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MAX_RX_URBS) + dev_warn(netdev->dev.parent, "rx performance may be slow\n"); + + /* Setup and start interrupt URB */ + usb_fill_int_urb(dev->intr_urb, dev->udev, + usb_rcvintpipe(dev->udev, 1), + dev->intr_in_buffer, + INTR_IN_BUFFER_SIZE, + ems_usb_read_interrupt_callback, dev, 1); + + err = usb_submit_urb(dev->intr_urb, GFP_KERNEL); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + dev_warn(netdev->dev.parent, "intr URB submit failed: %d\n", + err); + + return err; + } + + /* CPC-USB will transfer received message to host */ + err = ems_usb_control_cmd(dev, CONTR_CAN_MESSAGE | CONTR_CONT_ON); + if (err) + goto failed; + + /* CPC-USB will transfer CAN state changes to host */ + err = ems_usb_control_cmd(dev, CONTR_CAN_STATE | CONTR_CONT_ON); + if (err) + goto failed; + + /* CPC-USB will transfer bus errors to host */ + err = ems_usb_control_cmd(dev, CONTR_BUS_ERROR | CONTR_CONT_ON); + if (err) + goto failed; + + err = ems_usb_write_mode(dev, SJA1000_MOD_NORMAL); + if (err) + goto failed; + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +failed: + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + dev_warn(netdev->dev.parent, "couldn't submit control: %d\n", err); + + return err; +} + +static void unlink_all_urbs(struct ems_usb *dev) +{ + int i; + + usb_unlink_urb(dev->intr_urb); + + usb_kill_anchored_urbs(&dev->rx_submitted); + + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = MAX_TX_URBS; +} + +static int ems_usb_open(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + int err; + + err = ems_usb_write_mode(dev, SJA1000_MOD_RM); + if (err) + return err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + /* finally start device */ + err = ems_usb_start(dev); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + dev_warn(netdev->dev.parent, "couldn't start device: %d\n", + err); + + close_candev(netdev); + + return err; + } + + dev->open_time = jiffies; + + netif_start_queue(netdev); + + return 0; +} + +static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + struct ems_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct ems_cpc_msg *msg; + struct urb *urb; + u8 *buf; + int i, err; + size_t size = CPC_HEADER_SIZE + CPC_MSG_HEADER_LEN + + sizeof(struct cpc_can_msg); + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + dev_err(netdev->dev.parent, "No memory left for URBs\n"); + goto nomem; + } + + buf = usb_buffer_alloc(dev->udev, size, GFP_ATOMIC, &urb->transfer_dma); + if (!buf) { + dev_err(netdev->dev.parent, "No memory left for USB buffer\n"); + usb_free_urb(urb); + goto nomem; + } + + msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE]; + + msg->msg.can_msg.id = cf->can_id & CAN_ERR_MASK; + msg->msg.can_msg.length = cf->can_dlc; + + if (cf->can_id & CAN_RTR_FLAG) { + msg->type = cf->can_id & CAN_EFF_FLAG ? + CPC_CMD_TYPE_EXT_RTR_FRAME : CPC_CMD_TYPE_RTR_FRAME; + + msg->length = CPC_CAN_MSG_MIN_SIZE; + } else { + msg->type = cf->can_id & CAN_EFF_FLAG ? + CPC_CMD_TYPE_EXT_CAN_FRAME : CPC_CMD_TYPE_CAN_FRAME; + + for (i = 0; i < cf->can_dlc; i++) + msg->msg.can_msg.msg[i] = cf->data[i]; + + msg->length = CPC_CAN_MSG_MIN_SIZE + cf->can_dlc; + } + + for (i = 0; i < MAX_TX_URBS; i++) { + if (dev->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &dev->tx_contexts[i]; + break; + } + } + + /* + * May never happen! When this happens we'd more URBs in flight as + * allowed (MAX_TX_URBS). + */ + if (!context) { + usb_unanchor_urb(urb); + usb_buffer_free(dev->udev, size, buf, urb->transfer_dma); + + dev_warn(netdev->dev.parent, "couldn't find free context\n"); + + return NETDEV_TX_BUSY; + } + + context->dev = dev; + context->echo_index = i; + context->dlc = cf->can_dlc; + + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf, + size, ems_usb_write_bulk_callback, context); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&dev->active_tx_urbs); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + can_free_echo_skb(netdev, context->echo_index); + + usb_unanchor_urb(urb); + usb_buffer_free(dev->udev, size, buf, urb->transfer_dma); + dev_kfree_skb(skb); + + atomic_dec(&dev->active_tx_urbs); + + if (err == -ENODEV) { + netif_device_detach(netdev); + } else { + dev_warn(netdev->dev.parent, "failed tx_urb %d\n", err); + + stats->tx_dropped++; + } + } else { + netdev->trans_start = jiffies; + + /* Slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= MAX_TX_URBS || + dev->free_slots < 5) { + netif_stop_queue(netdev); + } + } + + /* + * Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return NETDEV_TX_OK; + +nomem: + if (skb) + dev_kfree_skb(skb); + + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +static int ems_usb_close(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + + /* Stop polling */ + unlink_all_urbs(dev); + + netif_stop_queue(netdev); + + /* Set CAN controller to reset mode */ + if (ems_usb_write_mode(dev, SJA1000_MOD_RM)) + dev_warn(netdev->dev.parent, "couldn't stop device"); + + close_candev(netdev); + + dev->open_time = 0; + + return 0; +} + +static const struct net_device_ops ems_usb_netdev_ops = { + .ndo_open = ems_usb_open, + .ndo_stop = ems_usb_close, + .ndo_start_xmit = ems_usb_start_xmit, +}; + +static struct can_bittiming_const ems_usb_bittiming_const = { + .name = "ems_usb", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +static int ems_usb_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct ems_usb *dev = netdev_priv(netdev); + + if (!dev->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + if (ems_usb_write_mode(dev, SJA1000_MOD_NORMAL)) + dev_warn(netdev->dev.parent, "couldn't start device"); + + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ems_usb_set_bittiming(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + dev_info(netdev->dev.parent, "setting BTR0=0x%02x BTR1=0x%02x\n", + btr0, btr1); + + dev->active_params.msg.can_params.cc_params.sja1000.btr0 = btr0; + dev->active_params.msg.can_params.cc_params.sja1000.btr1 = btr1; + + return ems_usb_command_msg(dev, &dev->active_params); +} + +static void init_params_sja1000(struct ems_cpc_msg *msg) +{ + struct cpc_sja1000_params *sja1000 = + &msg->msg.can_params.cc_params.sja1000; + + msg->type = CPC_CMD_TYPE_CAN_PARAMS; + msg->length = sizeof(struct cpc_can_params); + msg->msgid = 0; + + msg->msg.can_params.cc_type = CPC_CC_TYPE_SJA1000; + + /* Acceptance filter open */ + sja1000->acc_code0 = 0x00; + sja1000->acc_code1 = 0x00; + sja1000->acc_code2 = 0x00; + sja1000->acc_code3 = 0x00; + + /* Acceptance filter open */ + sja1000->acc_mask0 = 0xFF; + sja1000->acc_mask1 = 0xFF; + sja1000->acc_mask2 = 0xFF; + sja1000->acc_mask3 = 0xFF; + + sja1000->btr0 = 0; + sja1000->btr1 = 0; + + sja1000->outp_contr = SJA1000_DEFAULT_OUTPUT_CONTROL; + sja1000->mode = SJA1000_MOD_RM; +} + +/* + * probe function for new CPC-USB devices + */ +static int ems_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct ems_usb *dev; + int i, err = -ENOMEM; + + netdev = alloc_candev(sizeof(struct ems_usb)); + if (!netdev) { + dev_err(netdev->dev.parent, "Couldn't alloc candev\n"); + return -ENOMEM; + } + + dev = netdev_priv(netdev); + + dev->udev = interface_to_usbdev(intf); + dev->netdev = netdev; + + dev->can.state = CAN_STATE_STOPPED; + dev->can.clock.freq = EMS_USB_ARM7_CLOCK; + dev->can.bittiming_const = &ems_usb_bittiming_const; + dev->can.do_set_bittiming = ems_usb_set_bittiming; + dev->can.do_set_mode = ems_usb_set_mode; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + netdev->netdev_ops = &ems_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&dev->rx_submitted); + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = MAX_TX_URBS; + + dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->intr_urb) { + dev_err(netdev->dev.parent, "Couldn't alloc intr URB\n"); + goto cleanup_candev; + } + + dev->intr_in_buffer = kzalloc(INTR_IN_BUFFER_SIZE, GFP_KERNEL); + if (!dev->intr_in_buffer) { + dev_err(netdev->dev.parent, "Couldn't alloc Intr buffer\n"); + goto cleanup_intr_urb; + } + + dev->tx_msg_buffer = kzalloc(CPC_HEADER_SIZE + + sizeof(struct ems_cpc_msg), GFP_KERNEL); + if (!dev->tx_msg_buffer) { + dev_err(netdev->dev.parent, "Couldn't alloc Tx buffer\n"); + goto cleanup_intr_in_buffer; + } + + usb_set_intfdata(intf, dev); + + SET_NETDEV_DEV(netdev, &intf->dev); + + init_params_sja1000(&dev->active_params); + + err = ems_usb_command_msg(dev, &dev->active_params); + if (err) { + dev_err(netdev->dev.parent, + "couldn't initialize controller: %d\n", err); + goto cleanup_tx_msg_buffer; + } + + err = register_candev(netdev); + if (err) { + dev_err(netdev->dev.parent, + "couldn't register CAN device: %d\n", err); + goto cleanup_tx_msg_buffer; + } + + return 0; + +cleanup_tx_msg_buffer: + kfree(dev->tx_msg_buffer); + +cleanup_intr_in_buffer: + kfree(dev->intr_in_buffer); + +cleanup_intr_urb: + usb_free_urb(dev->intr_urb); + +cleanup_candev: + free_candev(netdev); + + return err; +} + +/* + * called by the usb core when the device is removed from the system + */ +static void ems_usb_disconnect(struct usb_interface *intf) +{ + struct ems_usb *dev = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (dev) { + unregister_netdev(dev->netdev); + free_candev(dev->netdev); + + unlink_all_urbs(dev); + + usb_free_urb(dev->intr_urb); + + kfree(dev->intr_in_buffer); + } +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver ems_usb_driver = { + .name = "ems_usb", + .probe = ems_usb_probe, + .disconnect = ems_usb_disconnect, + .id_table = ems_usb_table, +}; + +static int __init ems_usb_init(void) +{ + int err; + + printk(KERN_INFO "CPC-USB kernel driver loaded\n"); + + /* register this driver with the USB subsystem */ + err = usb_register(&ems_usb_driver); + + if (err) { + err("usb_register failed. Error number %d\n", err); + return err; + } + + return 0; +} + +static void __exit ems_usb_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&ems_usb_driver); +} + +module_init(ems_usb_init); +module_exit(ems_usb_exit); -- cgit v1.2.3 From 0a1f87cbdbf26e1257b36cc5e9c00a08c2236ff2 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Fri, 18 Sep 2009 09:45:43 +0000 Subject: ixgbe: fix sfp_timer clean up in ixgbe_down We weren't stoping the sfp_timer after the device was brought down. This patch properly cleans up. Signed-off-by: Shannon Nelson Signed-off-by: Don Skidmore Acked-by: Peter P Waskiewicz Jr Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ixgbe/ixgbe_main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 59ad9590e70..056434c9831 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -2926,6 +2926,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter) ixgbe_napi_disable_all(adapter); + clear_bit(__IXGBE_SFP_MODULE_NOT_FOUND, &adapter->state); + del_timer_sync(&adapter->sfp_timer); del_timer_sync(&adapter->watchdog_timer); cancel_work_sync(&adapter->watchdog_task); -- cgit v1.2.3 From f7554a2bf2696daad25465734bb962d0184469f2 Mon Sep 17 00:00:00 2001 From: "Nelson, Shannon" Date: Fri, 18 Sep 2009 09:46:06 +0000 Subject: ixgbe: Allow tx itr specific settings Allow the user to set Tx specific itr values. This only makes sense when there are separate vectors for Tx and Rx. When the queues are doubled up RxTx on the vectors, we still only use the rx itr value. Signed-off-by: Shannon Nelson Acked-by: Peter P Waskiewicz Jr Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ixgbe/ixgbe.h | 6 ++-- drivers/net/ixgbe/ixgbe_ethtool.c | 75 +++++++++++++++++++++++++++++++-------- drivers/net/ixgbe/ixgbe_main.c | 31 +++++++++------- 3 files changed, 83 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h index dd688d45e9c..385be601666 100644 --- a/drivers/net/ixgbe/ixgbe.h +++ b/drivers/net/ixgbe/ixgbe.h @@ -267,7 +267,8 @@ struct ixgbe_adapter { enum ixgbe_fc_mode last_lfc_mode; /* Interrupt Throttle Rate */ - u32 itr_setting; + u32 rx_itr_setting; + u32 tx_itr_setting; u16 eitr_low; u16 eitr_high; @@ -351,7 +352,8 @@ struct ixgbe_adapter { struct ixgbe_hw_stats stats; /* Interrupt Throttle Rate */ - u32 eitr_param; + u32 rx_eitr_param; + u32 tx_eitr_param; unsigned long state; u64 tx_busy; diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 026e94a9984..53b0a668025 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -1929,7 +1929,7 @@ static int ixgbe_get_coalesce(struct net_device *netdev, ec->tx_max_coalesced_frames_irq = adapter->tx_ring[0].work_limit; /* only valid if in constant ITR mode */ - switch (adapter->itr_setting) { + switch (adapter->rx_itr_setting) { case 0: /* throttling disabled */ ec->rx_coalesce_usecs = 0; @@ -1940,9 +1940,25 @@ static int ixgbe_get_coalesce(struct net_device *netdev, break; default: /* fixed interrupt rate mode */ - ec->rx_coalesce_usecs = 1000000/adapter->eitr_param; + ec->rx_coalesce_usecs = 1000000/adapter->rx_eitr_param; break; } + + /* only valid if in constant ITR mode */ + switch (adapter->tx_itr_setting) { + case 0: + /* throttling disabled */ + ec->tx_coalesce_usecs = 0; + break; + case 1: + /* dynamic ITR mode */ + ec->tx_coalesce_usecs = 1; + break; + default: + ec->tx_coalesce_usecs = 1000000/adapter->tx_eitr_param; + break; + } + return 0; } @@ -1953,6 +1969,14 @@ static int ixgbe_set_coalesce(struct net_device *netdev, struct ixgbe_q_vector *q_vector; int i; + /* + * don't accept tx specific changes if we've got mixed RxTx vectors + * test and jump out here if needed before changing the rx numbers + */ + if ((1000000/ec->tx_coalesce_usecs) != adapter->tx_eitr_param && + adapter->q_vector[0]->txr_count && adapter->q_vector[0]->rxr_count) + return -EINVAL; + if (ec->tx_max_coalesced_frames_irq) adapter->tx_ring[0].work_limit = ec->tx_max_coalesced_frames_irq; @@ -1963,26 +1987,49 @@ static int ixgbe_set_coalesce(struct net_device *netdev, return -EINVAL; /* store the value in ints/second */ - adapter->eitr_param = 1000000/ec->rx_coalesce_usecs; + adapter->rx_eitr_param = 1000000/ec->rx_coalesce_usecs; /* static value of interrupt rate */ - adapter->itr_setting = adapter->eitr_param; + adapter->rx_itr_setting = adapter->rx_eitr_param; /* clear the lower bit as its used for dynamic state */ - adapter->itr_setting &= ~1; + adapter->rx_itr_setting &= ~1; } else if (ec->rx_coalesce_usecs == 1) { /* 1 means dynamic mode */ - adapter->eitr_param = 20000; - adapter->itr_setting = 1; + adapter->rx_eitr_param = 20000; + adapter->rx_itr_setting = 1; } else { /* * any other value means disable eitr, which is best * served by setting the interrupt rate very high */ if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) - adapter->eitr_param = IXGBE_MAX_RSC_INT_RATE; + adapter->rx_eitr_param = IXGBE_MAX_RSC_INT_RATE; else - adapter->eitr_param = IXGBE_MAX_INT_RATE; - adapter->itr_setting = 0; + adapter->rx_eitr_param = IXGBE_MAX_INT_RATE; + adapter->rx_itr_setting = 0; + } + + if (ec->tx_coalesce_usecs > 1) { + /* check the limits */ + if ((1000000/ec->tx_coalesce_usecs > IXGBE_MAX_INT_RATE) || + (1000000/ec->tx_coalesce_usecs < IXGBE_MIN_INT_RATE)) + return -EINVAL; + + /* store the value in ints/second */ + adapter->tx_eitr_param = 1000000/ec->tx_coalesce_usecs; + + /* static value of interrupt rate */ + adapter->tx_itr_setting = adapter->tx_eitr_param; + + /* clear the lower bit as its used for dynamic state */ + adapter->tx_itr_setting &= ~1; + } else if (ec->tx_coalesce_usecs == 1) { + /* 1 means dynamic mode */ + adapter->tx_eitr_param = 10000; + adapter->tx_itr_setting = 1; + } else { + adapter->tx_eitr_param = IXGBE_MAX_INT_RATE; + adapter->tx_itr_setting = 0; } /* MSI/MSIx Interrupt Mode */ @@ -1992,17 +2039,17 @@ static int ixgbe_set_coalesce(struct net_device *netdev, for (i = 0; i < num_vectors; i++) { q_vector = adapter->q_vector[i]; if (q_vector->txr_count && !q_vector->rxr_count) - /* tx vector gets half the rate */ - q_vector->eitr = (adapter->eitr_param >> 1); + /* tx only */ + q_vector->eitr = adapter->tx_eitr_param; else /* rx only or mixed */ - q_vector->eitr = adapter->eitr_param; + q_vector->eitr = adapter->rx_eitr_param; ixgbe_write_eitr(q_vector); } /* Legacy Interrupt Mode */ } else { q_vector = adapter->q_vector[0]; - q_vector->eitr = adapter->eitr_param; + q_vector->eitr = adapter->rx_eitr_param; ixgbe_write_eitr(q_vector); } diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 056434c9831..1aa9f6a5adf 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -926,12 +926,12 @@ static void ixgbe_configure_msix(struct ixgbe_adapter *adapter) r_idx + 1); } - /* if this is a tx only vector halve the interrupt rate */ if (q_vector->txr_count && !q_vector->rxr_count) - q_vector->eitr = (adapter->eitr_param >> 1); + /* tx only */ + q_vector->eitr = adapter->tx_eitr_param; else if (q_vector->rxr_count) - /* rx only */ - q_vector->eitr = adapter->eitr_param; + /* rx or mixed */ + q_vector->eitr = adapter->rx_eitr_param; ixgbe_write_eitr(q_vector); } @@ -1359,7 +1359,7 @@ static int ixgbe_clean_rxonly(struct napi_struct *napi, int budget) /* If all Rx work done, exit the polling mode */ if (work_done < budget) { napi_complete(napi); - if (adapter->itr_setting & 1) + if (adapter->rx_itr_setting & 1) ixgbe_set_itr_msix(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) ixgbe_irq_enable_queues(adapter, @@ -1420,7 +1420,7 @@ static int ixgbe_clean_rxtx_many(struct napi_struct *napi, int budget) /* If all Rx work done, exit the polling mode */ if (work_done < budget) { napi_complete(napi); - if (adapter->itr_setting & 1) + if (adapter->rx_itr_setting & 1) ixgbe_set_itr_msix(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) ixgbe_irq_enable_queues(adapter, @@ -1458,10 +1458,10 @@ static int ixgbe_clean_txonly(struct napi_struct *napi, int budget) if (!ixgbe_clean_tx_irq(q_vector, tx_ring)) work_done = budget; - /* If all Rx work done, exit the polling mode */ + /* If all Tx work done, exit the polling mode */ if (work_done < budget) { napi_complete(napi); - if (adapter->itr_setting & 1) + if (adapter->tx_itr_setting & 1) ixgbe_set_itr_msix(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) ixgbe_irq_enable_queues(adapter, ((u64)1 << q_vector->v_idx)); @@ -1848,7 +1848,7 @@ static void ixgbe_configure_msi_and_legacy(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; IXGBE_WRITE_REG(hw, IXGBE_EITR(0), - EITR_INTS_PER_SEC_TO_REG(adapter->eitr_param)); + EITR_INTS_PER_SEC_TO_REG(adapter->rx_eitr_param)); ixgbe_set_ivar(adapter, 0, 0, 0); ixgbe_set_ivar(adapter, 1, 0, 0); @@ -2991,7 +2991,7 @@ static int ixgbe_poll(struct napi_struct *napi, int budget) /* If budget not fully consumed, exit the polling mode */ if (work_done < budget) { napi_complete(napi); - if (adapter->itr_setting & 1) + if (adapter->rx_itr_setting & 1) ixgbe_set_itr(adapter); if (!test_bit(__IXGBE_DOWN, &adapter->state)) ixgbe_irq_enable_queues(adapter, IXGBE_EIMS_RTX_QUEUE); @@ -3601,7 +3601,10 @@ static int ixgbe_alloc_q_vectors(struct ixgbe_adapter *adapter) if (!q_vector) goto err_out; q_vector->adapter = adapter; - q_vector->eitr = adapter->eitr_param; + if (q_vector->txr_count && !q_vector->rxr_count) + q_vector->eitr = adapter->tx_eitr_param; + else + q_vector->eitr = adapter->rx_eitr_param; q_vector->v_idx = q_idx; netif_napi_add(adapter->netdev, &q_vector->napi, (*poll), 64); adapter->q_vector[q_idx] = q_vector; @@ -3870,8 +3873,10 @@ static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter) hw->fc.disable_fc_autoneg = false; /* enable itr by default in dynamic mode */ - adapter->itr_setting = 1; - adapter->eitr_param = 20000; + adapter->rx_itr_setting = 1; + adapter->rx_eitr_param = 20000; + adapter->tx_itr_setting = 1; + adapter->tx_eitr_param = 10000; /* set defaults for eitr in MegaBytes */ adapter->eitr_low = 10; -- cgit v1.2.3 From bb5a9ad2b81bba0cccc1c78f101e38ea785c56b3 Mon Sep 17 00:00:00 2001 From: "Nelson, Shannon" Date: Fri, 18 Sep 2009 09:46:27 +0000 Subject: ixgbe: move rx queue RSC configuration to a separate function Shorten ixgbe_configure_rx() and lessen indent depth. Signed-off-by: Shannon Nelson Acked-by: Peter P Waskiewicz Jr Signed-off-by: Don Skidmore Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ixgbe/ixgbe_main.c | 78 +++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 1aa9f6a5adf..c407bd9de0d 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -1969,6 +1969,50 @@ static u32 ixgbe_setup_mrqc(struct ixgbe_adapter *adapter) return mrqc; } +/** + * ixgbe_configure_rscctl - enable RSC for the indicated ring + * @adapter: address of board private structure + * @index: index of ring to set + * @rx_buf_len: rx buffer length + **/ +static void ixgbe_configure_rscctl(struct ixgbe_adapter *adapter, int index, + int rx_buf_len) +{ + struct ixgbe_ring *rx_ring; + struct ixgbe_hw *hw = &adapter->hw; + int j; + u32 rscctrl; + + rx_ring = &adapter->rx_ring[index]; + j = rx_ring->reg_idx; + rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(j)); + rscctrl |= IXGBE_RSCCTL_RSCEN; + /* + * we must limit the number of descriptors so that the + * total size of max desc * buf_len is not greater + * than 65535 + */ + if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) { +#if (MAX_SKB_FRAGS > 16) + rscctrl |= IXGBE_RSCCTL_MAXDESC_16; +#elif (MAX_SKB_FRAGS > 8) + rscctrl |= IXGBE_RSCCTL_MAXDESC_8; +#elif (MAX_SKB_FRAGS > 4) + rscctrl |= IXGBE_RSCCTL_MAXDESC_4; +#else + rscctrl |= IXGBE_RSCCTL_MAXDESC_1; +#endif + } else { + if (rx_buf_len < IXGBE_RXBUFFER_4096) + rscctrl |= IXGBE_RSCCTL_MAXDESC_16; + else if (rx_buf_len < IXGBE_RXBUFFER_8192) + rscctrl |= IXGBE_RSCCTL_MAXDESC_8; + else + rscctrl |= IXGBE_RSCCTL_MAXDESC_4; + } + IXGBE_WRITE_REG(hw, IXGBE_RSCCTL(j), rscctrl); +} + /** * ixgbe_configure_rx - Configure 8259x Receive Unit after Reset * @adapter: board private structure @@ -1990,7 +2034,6 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) u32 fctrl, hlreg0; u32 reta = 0, mrqc = 0; u32 rdrxctl; - u32 rscctrl; int rx_buf_len; /* Decide whether to use packet split mode or not */ @@ -2148,36 +2191,9 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) { /* Enable 82599 HW-RSC */ - for (i = 0; i < adapter->num_rx_queues; i++) { - rx_ring = &adapter->rx_ring[i]; - j = rx_ring->reg_idx; - rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(j)); - rscctrl |= IXGBE_RSCCTL_RSCEN; - /* - * we must limit the number of descriptors so that the - * total size of max desc * buf_len is not greater - * than 65535 - */ - if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) { -#if (MAX_SKB_FRAGS > 16) - rscctrl |= IXGBE_RSCCTL_MAXDESC_16; -#elif (MAX_SKB_FRAGS > 8) - rscctrl |= IXGBE_RSCCTL_MAXDESC_8; -#elif (MAX_SKB_FRAGS > 4) - rscctrl |= IXGBE_RSCCTL_MAXDESC_4; -#else - rscctrl |= IXGBE_RSCCTL_MAXDESC_1; -#endif - } else { - if (rx_buf_len < IXGBE_RXBUFFER_4096) - rscctrl |= IXGBE_RSCCTL_MAXDESC_16; - else if (rx_buf_len < IXGBE_RXBUFFER_8192) - rscctrl |= IXGBE_RSCCTL_MAXDESC_8; - else - rscctrl |= IXGBE_RSCCTL_MAXDESC_4; - } - IXGBE_WRITE_REG(hw, IXGBE_RSCCTL(j), rscctrl); - } + for (i = 0; i < adapter->num_rx_queues; i++) + ixgbe_configure_rscctl(adapter, i, rx_buf_len); + /* Disable RSC for ACK packets */ IXGBE_WRITE_REG(hw, IXGBE_RSCDBU, (IXGBE_RSCDBU_RSCACKDIS | IXGBE_READ_REG(hw, IXGBE_RSCDBU))); -- cgit v1.2.3 From 44c852ead55b7450c8cff0dc90a3d48310300210 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 17 Sep 2009 14:52:29 +0000 Subject: igb: resolve namespacecheck warning for igb_hash_mc_addr This patch resolves a warning seen when doing namespace checking via "make namespacecheck" Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/igb/e1000_mac.c | 72 ++++++++++++++++++++++----------------------- drivers/net/igb/e1000_mac.h | 1 - 2 files changed, 36 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/net/igb/e1000_mac.c b/drivers/net/igb/e1000_mac.c index a0231cd079f..7d76bb085e1 100644 --- a/drivers/net/igb/e1000_mac.c +++ b/drivers/net/igb/e1000_mac.c @@ -285,41 +285,6 @@ void igb_mta_set(struct e1000_hw *hw, u32 hash_value) wrfl(); } -/** - * igb_update_mc_addr_list - Update Multicast addresses - * @hw: pointer to the HW structure - * @mc_addr_list: array of multicast addresses to program - * @mc_addr_count: number of multicast addresses to program - * - * Updates entire Multicast Table Array. - * The caller must have a packed mc_addr_list of multicast addresses. - **/ -void igb_update_mc_addr_list(struct e1000_hw *hw, - u8 *mc_addr_list, u32 mc_addr_count) -{ - u32 hash_value, hash_bit, hash_reg; - int i; - - /* clear mta_shadow */ - memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); - - /* update mta_shadow from mc_addr_list */ - for (i = 0; (u32) i < mc_addr_count; i++) { - hash_value = igb_hash_mc_addr(hw, mc_addr_list); - - hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); - hash_bit = hash_value & 0x1F; - - hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); - mc_addr_list += (ETH_ALEN); - } - - /* replace the entire MTA table */ - for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) - array_wr32(E1000_MTA, i, hw->mac.mta_shadow[i]); - wrfl(); -} - /** * igb_hash_mc_addr - Generate a multicast hash value * @hw: pointer to the HW structure @@ -329,7 +294,7 @@ void igb_update_mc_addr_list(struct e1000_hw *hw, * the multicast filter table array address and new table value. See * igb_mta_set() **/ -u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) +static u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) { u32 hash_value, hash_mask; u8 bit_shift = 0; @@ -391,6 +356,41 @@ u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) return hash_value; } +/** + * igb_update_mc_addr_list - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * + * Updates entire Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + **/ +void igb_update_mc_addr_list(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count) +{ + u32 hash_value, hash_bit, hash_reg; + int i; + + /* clear mta_shadow */ + memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); + + /* update mta_shadow from mc_addr_list */ + for (i = 0; (u32) i < mc_addr_count; i++) { + hash_value = igb_hash_mc_addr(hw, mc_addr_list); + + hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); + hash_bit = hash_value & 0x1F; + + hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); + mc_addr_list += (ETH_ALEN); + } + + /* replace the entire MTA table */ + for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) + array_wr32(E1000_MTA, i, hw->mac.mta_shadow[i]); + wrfl(); +} + /** * igb_clear_hw_cntrs_base - Clear base hardware counters * @hw: pointer to the HW structure diff --git a/drivers/net/igb/e1000_mac.h b/drivers/net/igb/e1000_mac.h index 7518af8cbbf..bca17d88241 100644 --- a/drivers/net/igb/e1000_mac.h +++ b/drivers/net/igb/e1000_mac.h @@ -88,6 +88,5 @@ enum e1000_mng_mode { #define E1000_MNG_DHCP_COOKIE_STATUS_VLAN 0x2 extern void e1000_init_function_pointers_82575(struct e1000_hw *hw); -extern u32 igb_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr); #endif -- cgit v1.2.3 From 03b88a66c040acb4b770221ca24a609527b47fc5 Mon Sep 17 00:00:00 2001 From: Jaswinder Singh Rajput Date: Sat, 12 Sep 2009 09:08:49 +0000 Subject: pcmcia: pcnet_cs.c removing useless condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'if (i < NR_INFO)' will only true if we breaks from 'for (i = 0; i < NR_INFO; i++)' So removing useless 'if (i < NR_INFO)' This also fixed following compilation warning : CC [M] drivers/net/pcmcia/pcnet_cs.o drivers/net/pcmcia/pcnet_cs.c: In function ‘get_hwinfo’: drivers/net/pcmcia/pcnet_cs.c:321: warning: ‘base’ may be used uninitialized in this function Signed-off-by: Jaswinder Singh Rajput Signed-off-by: David S. Miller --- drivers/net/pcmcia/pcnet_cs.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index 97db1c73234..474876c879c 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -340,12 +340,11 @@ static hw_info_t *get_hwinfo(struct pcmcia_device *link) base = &virt[hw_info[i].offset & (req.Size-1)]; if ((readb(base+0) == hw_info[i].a0) && (readb(base+2) == hw_info[i].a1) && - (readb(base+4) == hw_info[i].a2)) - break; - } - if (i < NR_INFO) { - for (j = 0; j < 6; j++) - dev->dev_addr[j] = readb(base + (j<<1)); + (readb(base+4) == hw_info[i].a2)) { + for (j = 0; j < 6; j++) + dev->dev_addr[j] = readb(base + (j<<1)); + break; + } } iounmap(virt); -- cgit v1.2.3 From a598ae177a11ebae065e20059d9bc63a5da4ccc3 Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Sun, 20 Sep 2009 19:20:38 +0000 Subject: netxen: fix minor tx timeout bug Fix minor bug in netdev tx timeout handling which could always lead to firmware reset instead of pci function reset. netxen_nic_reset_context() requires __NX_RESETTING bit cleared. Signed-off-by: Dhananjay Phadke Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index f7bdde111df..b8c723539dd 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -1903,12 +1903,13 @@ static void netxen_tx_timeout_task(struct work_struct *work) netif_wake_queue(adapter->netdev); - goto done; + clear_bit(__NX_RESETTING, &adapter->state); } else { + clear_bit(__NX_RESETTING, &adapter->state); if (!netxen_nic_reset_context(adapter)) { adapter->netdev->trans_start = jiffies; - goto done; + return; } /* context reset failed, fall through for fw reset */ @@ -1916,8 +1917,6 @@ static void netxen_tx_timeout_task(struct work_struct *work) request_reset: adapter->need_fw_reset = 1; -done: - clear_bit(__NX_RESETTING, &adapter->state); } struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev) -- cgit v1.2.3 From 07653aa3afe7b74cbddfe3bca82a898c413855b1 Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Sun, 20 Sep 2009 19:20:39 +0000 Subject: netxen: fix firmware init after resume After successful firmware init, return instead of falling to error path (leading to detach) after resuming to D0 state. This was broken in recent firmware reset rehaul. Signed-off-by: Dhananjay Phadke Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic_main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index b8c723539dd..b5aa974827e 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -1469,6 +1469,7 @@ netxen_nic_resume(struct pci_dev *pdev) } netxen_schedule_work(adapter, netxen_fw_poll_work, FW_POLL_DELAY); + return 0; err_out_detach: netxen_nic_detach(adapter); -- cgit v1.2.3 From 95acf7d7ed95fdb9bfd5b7a71752701a0e382bd9 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Tue, 22 Sep 2009 05:24:14 +0000 Subject: net: xilinx_emaclite: Fix problem with first incoming packet You can't ping the board or connect to it unless you send any packet out from board. Tested-by: John Williams Signed-off-by: Michal Simek Acked-by: John Linn Acked-by: Grant Likely Signed-off-by: David S. Miller --- drivers/net/xilinx_emaclite.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/xilinx_emaclite.c b/drivers/net/xilinx_emaclite.c index dc22782633a..83a044dbd1d 100644 --- a/drivers/net/xilinx_emaclite.c +++ b/drivers/net/xilinx_emaclite.c @@ -134,18 +134,15 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata) } /* Enable the Rx interrupts for the first buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET); out_be32(drvdata->base_addr + XEL_RSR_OFFSET, - reg_data | XEL_RSR_RECV_IE_MASK); + XEL_RSR_RECV_IE_MASK); /* Enable the Rx interrupts for the second Buffer if * configured in HW */ if (drvdata->rx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_RSR_OFFSET); out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET, - reg_data | XEL_RSR_RECV_IE_MASK); + XEL_RSR_RECV_IE_MASK); } /* Enable the Global Interrupt Enable */ -- cgit v1.2.3 From 6f41d12bab6e5ce317b0494f5b3cd0b60e14ebc3 Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Tue, 22 Sep 2009 05:13:02 +0000 Subject: smsc95xx: add additional USB product IDs Signed-off-by: Steve Glendinning Signed-off-by: David S. Miller --- drivers/net/usb/smsc95xx.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'drivers') diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 938fb3530a7..3aafebdbe7b 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1236,11 +1236,76 @@ static const struct usb_device_id products[] = { USB_DEVICE(0x0424, 0x9500), .driver_info = (unsigned long) &smsc95xx_info, }, + { + /* SMSC9505 USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9505), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500A USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9E00), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9505A USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9E01), + .driver_info = (unsigned long) &smsc95xx_info, + }, { /* SMSC9512/9514 USB Hub & Ethernet Device */ USB_DEVICE(0x0424, 0xec00), .driver_info = (unsigned long) &smsc95xx_info, }, + { + /* SMSC9500 USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9900), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9505 USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9901), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500A USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9902), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9505A USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9903), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9512/9514 USB Hub & Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9904), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500A USB Ethernet Device (HAL) */ + USB_DEVICE(0x0424, 0x9905), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9505A USB Ethernet Device (HAL) */ + USB_DEVICE(0x0424, 0x9906), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500 USB Ethernet Device (Alternate ID) */ + USB_DEVICE(0x0424, 0x9907), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9500A USB Ethernet Device (Alternate ID) */ + USB_DEVICE(0x0424, 0x9908), + .driver_info = (unsigned long) &smsc95xx_info, + }, + { + /* SMSC9512/9514 USB Hub & Ethernet Device (Alternate ID) */ + USB_DEVICE(0x0424, 0x9909), + .driver_info = (unsigned long) &smsc95xx_info, + }, { }, /* END */ }; MODULE_DEVICE_TABLE(usb, products); -- cgit v1.2.3 From 051b982bcc620695de629d29c333c95b66e9b95e Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Sat, 19 Sep 2009 11:18:21 +0000 Subject: kaweth: Fix memory leak in kaweth_control() kaweth_control() never frees the buffer that it allocates for the USB control message. Test case: while :; do ifconfig eth2 down ; ifconfig eth2 up ; done This is a tiny buffer so it is a slow leak. If you want to speed up the process, you can change the allocation size to e.g. 16384 bytes, and it will consume several megabytes within a few minutes. Signed-off-by: Kevin Cernekee Acked-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/kaweth.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index e2a39b9be96..e391ef969c2 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -263,6 +263,7 @@ static int kaweth_control(struct kaweth_device *kaweth, int timeout) { struct usb_ctrlrequest *dr; + int retval; dbg("kaweth_control()"); @@ -278,18 +279,21 @@ static int kaweth_control(struct kaweth_device *kaweth, return -ENOMEM; } - dr->bRequestType= requesttype; + dr->bRequestType = requesttype; dr->bRequest = request; dr->wValue = cpu_to_le16(value); dr->wIndex = cpu_to_le16(index); dr->wLength = cpu_to_le16(size); - return kaweth_internal_control_msg(kaweth->dev, - pipe, - dr, - data, - size, - timeout); + retval = kaweth_internal_control_msg(kaweth->dev, + pipe, + dr, + data, + size, + timeout); + + kfree(dr); + return retval; } /**************************************************************** -- cgit v1.2.3 From 5ee2124524aa65cc48fcf0b498d4ef036d2c9ee5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 19 Sep 2009 09:48:41 +0000 Subject: drivers/net: remove duplicate structure field initialization The definitions of vnet_ops and ehea_netdev_ops have initializations of a local function and eth_change_mtu for their respective ndo_change_mtu fields. This change uses only the local function. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r@ identifier I, s, fld; position p0,p; expression E; @@ struct I s =@p0 { ... .fld@p = E, ...}; @s@ identifier I, s, r.fld; position r.p0,p; expression E; @@ struct I s =@p0 { ... .fld@p = E, ...}; @script:python@ p0 << r.p0; fld << r.fld; ps << s.p; pr << r.p; @@ if int(ps[0].line)!=int(pr[0].line) or int(ps[0].column)!=int(pr[0].column): cocci.print_main(fld,p0) // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- drivers/net/ehea/ehea_main.c | 1 - drivers/net/sunvnet.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index 977c3d35827..41bd7aeafd8 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -3083,7 +3083,6 @@ static const struct net_device_ops ehea_netdev_ops = { .ndo_poll_controller = ehea_netpoll, #endif .ndo_get_stats = ehea_get_stats, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = ehea_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_set_multicast_list = ehea_set_multicast_list, diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index f1e5e4542c2..bc74db0d12f 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c @@ -1016,7 +1016,6 @@ static const struct net_device_ops vnet_ops = { .ndo_open = vnet_open, .ndo_stop = vnet_close, .ndo_set_multicast_list = vnet_set_rx_mode, - .ndo_change_mtu = eth_change_mtu, .ndo_set_mac_address = vnet_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_tx_timeout = vnet_tx_timeout, -- cgit v1.2.3 From 55865c66d24b8f1a885a1a03836d219286d41818 Mon Sep 17 00:00:00 2001 From: "jie.yang@atheros.com" Date: Sun, 20 Sep 2009 19:01:58 +0000 Subject: atl1c:remove compiling warning Set wol_ctrl_data to value 0, to remove compiling warning. Signed-off-by: Jie Yang Signed-off-by: David S. Miller --- drivers/net/atl1c/atl1c_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index be2c6cfe6e8..1372e9a99f5 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -2296,7 +2296,7 @@ static int atl1c_suspend(struct pci_dev *pdev, pm_message_t state) u32 ctrl; u32 mac_ctrl_data; u32 master_ctrl_data; - u32 wol_ctrl_data; + u32 wol_ctrl_data = 0; u16 mii_bmsr_data; u16 save_autoneg_advertised; u16 mii_intr_status_data; -- cgit v1.2.3 From 74a61ebf653c6abe459f228eb40e9f24f7ef1fb7 Mon Sep 17 00:00:00 2001 From: Mike McCormack Date: Mon, 21 Sep 2009 04:08:52 +0000 Subject: sky2: Set SKY2_HW_RAM_BUFFER in sky2_init The SKY2_HW_RAM_BUFFER bit in hw->flags was checked in sky2_mac_init(), before being set later in sky2_up(). Setting SKY2_HW_RAM_BUFFER in sky2_init() where other hw->flags are set should avoid this problem recurring. Signed-off-by: Mike McCormack Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/sky2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 4bb52e9cd37..68d256b9638 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1497,7 +1497,6 @@ static int sky2_up(struct net_device *dev) if (ramsize > 0) { u32 rxspace; - hw->flags |= SKY2_HW_RAM_BUFFER; pr_debug(PFX "%s: ram buffer %dK\n", dev->name, ramsize); if (ramsize < 16) rxspace = ramsize / 2; @@ -2926,6 +2925,9 @@ static int __devinit sky2_init(struct sky2_hw *hw) ++hw->ports; } + if (sky2_read8(hw, B2_E_0)) + hw->flags |= SKY2_HW_RAM_BUFFER; + return 0; } -- cgit v1.2.3 From 5d5baa92664e66f63b75abc51fbcd0b96d81f5d8 Mon Sep 17 00:00:00 2001 From: roel kluin Date: Sun, 20 Sep 2009 07:11:28 +0000 Subject: atm: dereference of he_dev->rbps_virt in he_init_group() he_dev->rbps_virt or he_dev->rbpl_virt allocation may fail, s them. Make sure that he_init_group() cleans up after errors. Signed-off-by: Roel Kluin Signed-off-by: David S. Miller --- drivers/atm/he.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/atm/he.c b/drivers/atm/he.c index 2de64065aa1..29e66d603d3 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -790,11 +790,15 @@ he_init_group(struct he_dev *he_dev, int group) he_dev->rbps_base = pci_alloc_consistent(he_dev->pci_dev, CONFIG_RBPS_SIZE * sizeof(struct he_rbp), &he_dev->rbps_phys); if (he_dev->rbps_base == NULL) { - hprintk("failed to alloc rbps\n"); - return -ENOMEM; + hprintk("failed to alloc rbps_base\n"); + goto out_destroy_rbps_pool; } memset(he_dev->rbps_base, 0, CONFIG_RBPS_SIZE * sizeof(struct he_rbp)); he_dev->rbps_virt = kmalloc(CONFIG_RBPS_SIZE * sizeof(struct he_virt), GFP_KERNEL); + if (he_dev->rbps_virt == NULL) { + hprintk("failed to alloc rbps_virt\n"); + goto out_free_rbps_base; + } for (i = 0; i < CONFIG_RBPS_SIZE; ++i) { dma_addr_t dma_handle; @@ -802,7 +806,7 @@ he_init_group(struct he_dev *he_dev, int group) cpuaddr = pci_pool_alloc(he_dev->rbps_pool, GFP_KERNEL|GFP_DMA, &dma_handle); if (cpuaddr == NULL) - return -ENOMEM; + goto out_free_rbps_virt; he_dev->rbps_virt[i].virt = cpuaddr; he_dev->rbps_base[i].status = RBP_LOANED | RBP_SMALLBUF | (i << RBP_INDEX_OFF); @@ -827,17 +831,21 @@ he_init_group(struct he_dev *he_dev, int group) CONFIG_RBPL_BUFSIZE, 8, 0); if (he_dev->rbpl_pool == NULL) { hprintk("unable to create rbpl pool\n"); - return -ENOMEM; + goto out_free_rbps_virt; } he_dev->rbpl_base = pci_alloc_consistent(he_dev->pci_dev, CONFIG_RBPL_SIZE * sizeof(struct he_rbp), &he_dev->rbpl_phys); if (he_dev->rbpl_base == NULL) { - hprintk("failed to alloc rbpl\n"); - return -ENOMEM; + hprintk("failed to alloc rbpl_base\n"); + goto out_destroy_rbpl_pool; } memset(he_dev->rbpl_base, 0, CONFIG_RBPL_SIZE * sizeof(struct he_rbp)); he_dev->rbpl_virt = kmalloc(CONFIG_RBPL_SIZE * sizeof(struct he_virt), GFP_KERNEL); + if (he_dev->rbpl_virt == NULL) { + hprintk("failed to alloc rbpl_virt\n"); + goto out_free_rbpl_base; + } for (i = 0; i < CONFIG_RBPL_SIZE; ++i) { dma_addr_t dma_handle; @@ -845,7 +853,7 @@ he_init_group(struct he_dev *he_dev, int group) cpuaddr = pci_pool_alloc(he_dev->rbpl_pool, GFP_KERNEL|GFP_DMA, &dma_handle); if (cpuaddr == NULL) - return -ENOMEM; + goto out_free_rbpl_virt; he_dev->rbpl_virt[i].virt = cpuaddr; he_dev->rbpl_base[i].status = RBP_LOANED | (i << RBP_INDEX_OFF); @@ -870,7 +878,7 @@ he_init_group(struct he_dev *he_dev, int group) CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), &he_dev->rbrq_phys); if (he_dev->rbrq_base == NULL) { hprintk("failed to allocate rbrq\n"); - return -ENOMEM; + goto out_free_rbpl_virt; } memset(he_dev->rbrq_base, 0, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq)); @@ -894,7 +902,7 @@ he_init_group(struct he_dev *he_dev, int group) CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), &he_dev->tbrq_phys); if (he_dev->tbrq_base == NULL) { hprintk("failed to allocate tbrq\n"); - return -ENOMEM; + goto out_free_rbpq_base; } memset(he_dev->tbrq_base, 0, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq)); @@ -906,6 +914,39 @@ he_init_group(struct he_dev *he_dev, int group) he_writel(he_dev, CONFIG_TBRQ_THRESH, G0_TBRQ_THRESH + (group * 16)); return 0; + +out_free_rbpq_base: + pci_free_consistent(he_dev->pci_dev, CONFIG_RBRQ_SIZE * + sizeof(struct he_rbrq), he_dev->rbrq_base, + he_dev->rbrq_phys); + i = CONFIG_RBPL_SIZE; +out_free_rbpl_virt: + while (--i) + pci_pool_free(he_dev->rbps_pool, he_dev->rbpl_virt[i].virt, + he_dev->rbps_base[i].phys); + kfree(he_dev->rbpl_virt); + +out_free_rbpl_base: + pci_free_consistent(he_dev->pci_dev, CONFIG_RBPL_SIZE * + sizeof(struct he_rbp), he_dev->rbpl_base, + he_dev->rbpl_phys); +out_destroy_rbpl_pool: + pci_pool_destroy(he_dev->rbpl_pool); + + i = CONFIG_RBPL_SIZE; +out_free_rbps_virt: + while (--i) + pci_pool_free(he_dev->rbpl_pool, he_dev->rbps_virt[i].virt, + he_dev->rbpl_base[i].phys); + kfree(he_dev->rbps_virt); + +out_free_rbps_base: + pci_free_consistent(he_dev->pci_dev, CONFIG_RBPS_SIZE * + sizeof(struct he_rbp), he_dev->rbps_base, + he_dev->rbps_phys); +out_destroy_rbps_pool: + pci_pool_destroy(he_dev->rbps_pool); + return -ENOMEM; } static int __devinit -- cgit v1.2.3 From 9cc4085585a011179caf86a6408225626f6c2263 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Tue, 22 Sep 2009 04:05:39 +0000 Subject: 8139cp: fix duplicate loglevel in module load message This was introduced by b93d58 "8139*: convert printk() to pr_()": [ 2256252443 ] <6>8139cp: 10/100 PCI Ethernet driver v1.3 (Mar 22, 2004) The "version" string is printed using pr_info(), so it doesn't need to include a loglevel. Signed-off-by: Alan Jenkins CC: Alexander Beregalov Signed-off-by: David S. Miller --- drivers/net/8139cp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 462d9f59c53..83a1922e68e 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -87,7 +87,7 @@ /* These identify the driver base version and may not be removed. */ static char version[] = -KERN_INFO DRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; +DRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; MODULE_AUTHOR("Jeff Garzik "); MODULE_DESCRIPTION("RealTek RTL-8139C+ series 10/100 PCI Ethernet driver"); -- cgit v1.2.3 From 36989b90879c785f95b877bdcf65a2527dadd893 Mon Sep 17 00:00:00 2001 From: Kusanagi Kouichi Date: Wed, 16 Sep 2009 21:36:13 +0000 Subject: tun: Return -EINVAL if neither IFF_TUN nor IFF_TAP is set. After commit 2b980dbd77d229eb60588802162c9659726b11f4 ("lsm: Add hooks to the TUN driver") tun_set_iff doesn't return -EINVAL though neither IFF_TUN nor IFF_TAP is set. Signed-off-by: Kusanagi Kouichi Reviewed-by: Paul Moore Signed-off-by: David S. Miller --- drivers/net/tun.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3f5d28851aa..e091756166a 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -946,8 +946,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) char *name; unsigned long flags = 0; - err = -EINVAL; - if (!capable(CAP_NET_ADMIN)) return -EPERM; err = security_tun_dev_create(); @@ -964,7 +962,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) flags |= TUN_TAP_DEV; name = "tap%d"; } else - goto failed; + return -EINVAL; if (*ifr->ifr_name) name = ifr->ifr_name; -- cgit v1.2.3 From ec4756238239f1a331d9fb95bad8b281dad56855 Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Tue, 22 Sep 2009 04:00:27 +0000 Subject: smsc95xx: fix transmission where ZLP is expected Usbnet framework assumes USB hardware doesn't handle zero length packets, but SMSC LAN95xx requires these to be sent for correct operation. This patch fixes an easily reproducible tx lockup when sending a frame that results in exactly 512 bytes in a USB transmission (e.g. a UDP frame with 458 data bytes, due to IP headers and our USB headers). It adds an extra flag to usbnet for the hardware driver to indicate that it can handle and requires the zero length packets. This patch should not affect other usbnet users, please also consider for -stable. Signed-off-by: Steve Glendinning Signed-off-by: David S. Miller --- drivers/net/usb/smsc95xx.c | 2 +- drivers/net/usb/usbnet.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 3aafebdbe7b..c6c922247d0 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1227,7 +1227,7 @@ static const struct driver_info smsc95xx_info = { .rx_fixup = smsc95xx_rx_fixup, .tx_fixup = smsc95xx_tx_fixup, .status = smsc95xx_status, - .flags = FLAG_ETHER, + .flags = FLAG_ETHER | FLAG_SEND_ZLP, }; static const struct usb_device_id products[] = { diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 24b36f79515..ca5ca5ae061 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1049,7 +1049,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, * NOTE: strictly conforming cdc-ether devices should expect * the ZLP here, but ignore the one-byte packet. */ - if ((length % dev->maxpacket) == 0) { + if (!(info->flags & FLAG_SEND_ZLP) && (length % dev->maxpacket) == 0) { urb->transfer_buffer_length++; if (skb_tailroom(skb)) { skb->data[skb->len] = 0; -- cgit v1.2.3 From 68f40152747da28ae0de21633c9062671448b66c Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Tue, 22 Sep 2009 14:46:57 -0700 Subject: at91_can: add driver for Atmel's CAN controller on AT91SAM9263 This patch add the driver for the SoC CAN controller in Atmel's AT91SAM9263. Signed-off-by: Marc Kleine-Budde Signed-off-by: Wolfgang Grandegger Signed-off-by: David S. Miller --- drivers/net/can/Kconfig | 6 ++++++ drivers/net/can/Makefile | 1 + 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 6ac5aa588a3..df32c109b7a 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -89,6 +89,12 @@ config CAN_KVASER_PCI This driver is for the the PCIcanx and PCIcan cards (1, 2 or 4 channel) from Kvaser (http://www.kvaser.com). +config CAN_AT91 + tristate "Atmel AT91 onchip CAN controller" + depends on CAN && CAN_DEV && ARCH_AT91SAM9263 + ---help--- + This is a driver for the SoC CAN controller in Atmel's AT91SAM9263. + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 9b035ecb2f3..0dea62721f2 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -10,5 +10,6 @@ can-dev-y := dev.o obj-y += usb/ obj-$(CONFIG_CAN_SJA1000) += sja1000/ +obj-$(CONFIG_CAN_AT91) += at91_can.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG -- cgit v1.2.3 From 6ef57a0ea77e7bf4123a06da895a0e26312b5737 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 21 Sep 2009 15:39:37 +0000 Subject: cnic: Shutdown iSCSI ring during uio_close. The iSCSI ring should be shutdown during uio_close instead of uio_open for proper operations. This fixes the problem of the ring getting stuck intermittently. Signed-off-by: Michael Chan Signed-off-by: Benjamin Li Signed-off-by: David S. Miller --- drivers/net/cnic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index d45eacb7670..211c8e9182f 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -85,8 +85,6 @@ static int cnic_uio_open(struct uio_info *uinfo, struct inode *inode) cp->uio_dev = iminor(inode); - cnic_shutdown_bnx2_rx_ring(dev); - cnic_init_bnx2_tx_ring(dev); cnic_init_bnx2_rx_ring(dev); @@ -98,6 +96,8 @@ static int cnic_uio_close(struct uio_info *uinfo, struct inode *inode) struct cnic_dev *dev = uinfo->priv; struct cnic_local *cp = dev->cnic_priv; + cnic_shutdown_bnx2_rx_ring(dev); + cp->uio_dev = -1; return 0; } -- cgit v1.2.3 From fcd195cdfdd52b18a977581a254455ff48080fc3 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 21 Sep 2009 14:41:21 +0000 Subject: fec: Add FEC support for MX25 processor Add FEC support for MX25 processor. Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 21333c18f34..f9fbd52b799 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1875,7 +1875,7 @@ config 68360_ENET config FEC bool "FEC ethernet controller (of ColdFire and some i.MX CPUs)" - depends on M523x || M527x || M5272 || M528x || M520x || M532x || MACH_MX27 || ARCH_MX35 + depends on M523x || M527x || M5272 || M528x || M520x || M532x || MACH_MX27 || ARCH_MX35 || ARCH_MX25 help Say Y here if you want to use the built-in 10/100 Fast ethernet controller on some Motorola ColdFire and Freescale i.MX processors. -- cgit v1.2.3 From 05f57195ab5a232afd773328f00784e79c60e64e Mon Sep 17 00:00:00 2001 From: Martin Decky Date: Thu, 10 Sep 2009 03:44:47 +0200 Subject: hostap: Revert a toxic part of the conversion to net_device_ops As the hostap driver was converted to use net_device_ops, a mistake was made in hostap_main.c (commit 5ae4efbcd2611562a8b93596be034e63495706a5). Originally, the tx_queue_len was set to 0 for every other interface than HOSTAP_INTERFACE_MASTER, but the new fragment of code sets tx_queue_len to 0 only for HOSTAP_INTERFACE_MASTER. The opposite of the previous behavior makes the driver to drop all packets in AP mode. Change the way 0 is assigned to tx_queue_len according to the original logic. Signed-off-by: Martin Decky Signed-off-by: John W. Linville --- drivers/net/wireless/hostap/hostap_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index 6fe122f18c0..eb57d1ea361 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -875,15 +875,16 @@ void hostap_setup_dev(struct net_device *dev, local_info_t *local, switch(type) { case HOSTAP_INTERFACE_AP: + dev->tx_queue_len = 0; /* use main radio device queue */ dev->netdev_ops = &hostap_mgmt_netdev_ops; dev->type = ARPHRD_IEEE80211; dev->header_ops = &hostap_80211_ops; break; case HOSTAP_INTERFACE_MASTER: - dev->tx_queue_len = 0; /* use main radio device queue */ dev->netdev_ops = &hostap_master_ops; break; default: + dev->tx_queue_len = 0; /* use main radio device queue */ dev->netdev_ops = &hostap_netdev_ops; } -- cgit v1.2.3 From 5ab9549a64c77b6975b079b27e95fc2c115f613b Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Thu, 10 Sep 2009 20:31:46 +0200 Subject: b43: Force-wake queues on init Force wake the mac80211 queues on init. Under rare circumstances they may be stopped, if a DMA error or something else causes a device reset while a queue was stopped. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 7a9a3fa5542..78e9834d4c1 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4287,6 +4287,8 @@ static int b43_wireless_core_init(struct b43_wldev *dev) if (!dev->suspend_in_progress) b43_rng_init(wl); + ieee80211_wake_queues(dev->wl->hw); + b43_set_status(dev, B43_STAT_INITIALIZED); if (!dev->suspend_in_progress) -- cgit v1.2.3 From 10a01bfd4e077ff03a4ace76a6d9b81272f91678 Mon Sep 17 00:00:00 2001 From: Sujith Date: Fri, 11 Sep 2009 08:30:03 +0530 Subject: ath9k: Fix bug in ANI channel handling When processing MIB interrupts, OFDM and CCK error handling routines for low RSSI values have to be invoked only when the channel mode is 11G/11B. Since HT channels will also fall under the bands 2Ghz/5Ghz, check appropriately. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index a7cbb07988c..2b493742ef1 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -327,7 +327,8 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) aniState->firstepLevel + 1); return; } else { - if (conf->channel->band == IEEE80211_BAND_2GHZ) { + if ((conf->channel->band == IEEE80211_BAND_2GHZ) && + !conf_is_ht(conf)) { if (!aniState->ofdmWeakSigDetectOff) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, @@ -369,7 +370,8 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); } else { - if (conf->channel->band == IEEE80211_BAND_2GHZ) { + if ((conf->channel->band == IEEE80211_BAND_2GHZ) && + !conf_is_ht(conf)) { if (aniState->firstepLevel > 0) ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0); -- cgit v1.2.3 From 7ab44f17bc5b16ab1b4fc4dad4a0667c21e727b5 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 11 Sep 2009 04:43:29 -0400 Subject: wireless: default CONFIG_WLAN to y When this was added no defaults were set and it seems this implies n. Default this to y. Reported-by: Jouni Malinen Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index ad89d23968d..49ea9c92b7e 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -5,6 +5,7 @@ menuconfig WLAN bool "Wireless LAN" depends on !S390 + default y ---help--- This section contains all the pre 802.11 and 802.11 wireless device drivers. For a complete list of drivers and documentation -- cgit v1.2.3 From 44fdd375cd8bb3544f9832b4a42df497d180feb2 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 11 Sep 2009 16:08:06 +0200 Subject: ssb: Disable verbose SDIO coreswitch Disable SDIO coreswitch debugging. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/ssb/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ssb/sdio.c b/drivers/ssb/sdio.c index 114051056b5..4ff73473e69 100644 --- a/drivers/ssb/sdio.c +++ b/drivers/ssb/sdio.c @@ -21,7 +21,7 @@ #include "ssb_private.h" /* Define the following to 1 to enable a printk on each coreswitch. */ -#define SSB_VERBOSE_SDIOCORESWITCH_DEBUG 1 +#define SSB_VERBOSE_SDIOCORESWITCH_DEBUG 0 /* Hardware invariants CIS tuples */ -- cgit v1.2.3 From 91d372c0afc55faf44c1b763693f089b5dad31ec Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 11 Sep 2009 18:31:32 +0200 Subject: b43: Fix resume failure This fixes a resume failure where a signal is pending on resume so the firmware upload fails. This removes the interruptible sleep, because we don't really need it. In the worst case (with broken firmware) the sleep loop will take 1 second. In the common case (working firmware), it will only take a few milliseconds. So we don't really need to be interruptible. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 78e9834d4c1..e789792a36b 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2289,11 +2289,7 @@ static int b43_upload_microcode(struct b43_wldev *dev) err = -ENODEV; goto error; } - msleep_interruptible(50); - if (signal_pending(current)) { - err = -EINTR; - goto error; - } + msleep(50); } b43_read32(dev, B43_MMIO_GEN_IRQ_REASON); /* dummy read */ -- cgit v1.2.3 From c929c5a1281b666fabc9aa94c607932ec6d28c6d Mon Sep 17 00:00:00 2001 From: Daniel C Halperin Date: Fri, 11 Sep 2009 10:38:08 -0700 Subject: iwlwifi: fix HT operation in 2.4 GHz band When we cleaned up the driver to properly tell mac80211 about HT rates ("iwlwifi: use iwl_hwrate_get_mac80211_idx where appropriate"), we broke internal rate indexing in 2.4 GHz band. Signed-off-by: Daniel C Halperin Tested-by: Wey-Yi Guy Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 40b207aa8fe..fd731534df1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -883,6 +883,12 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */ if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) mac_index++; + /* + * mac80211 HT index is always zero-indexed; we need to move + * HT OFDM rates after CCK rates in 2.4 GHz band + */ + if (priv->band == IEEE80211_BAND_2GHZ) + mac_index += IWL_FIRST_OFDM_RATE; } if ((mac_index < 0) || -- cgit v1.2.3 From de0bd50845eb5935ce3d503c5d2f565d6cb9ece1 Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Fri, 11 Sep 2009 10:38:12 -0700 Subject: iwlwifi: fix potential rx buffer loss RX handling maintains a few lists that keep track of the RX buffers. Buffers move from one list to the other as they are used, replenished, and again made available for usage. In one such instance, when a buffer is used it enters the "rx_used" list. When buffers are replenished an skb is attached to the buffer and it is moved to the "rx_free" list. The problem here is that the buffer is first removed from the "rx_used" list _before_ the skb is allocated. Thus, if the skb allocation fails this buffer remains removed from the "rx_used" list and is thus lost for future usage. Fix this by first allocating the skb before trying to attach it to a list. We add an additional check to not do this unnecessarily. Reported-by: Rick Farrington Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-rx.c | 24 +++++++++++++++++------- drivers/net/wireless/iwlwifi/iwl3945-base.c | 24 ++++++++++++++++-------- 2 files changed, 33 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 8150c5c3a16..b90adcb73b0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -239,26 +239,22 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority) struct iwl_rx_queue *rxq = &priv->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; + struct sk_buff *skb; unsigned long flags; while (1) { spin_lock_irqsave(&rxq->lock, flags); - if (list_empty(&rxq->rx_used)) { spin_unlock_irqrestore(&rxq->lock, flags); return; } - element = rxq->rx_used.next; - rxb = list_entry(element, struct iwl_rx_mem_buffer, list); - list_del(element); - spin_unlock_irqrestore(&rxq->lock, flags); /* Alloc a new receive buffer */ - rxb->skb = alloc_skb(priv->hw_params.rx_buf_size + 256, + skb = alloc_skb(priv->hw_params.rx_buf_size + 256, priority); - if (!rxb->skb) { + if (!skb) { IWL_CRIT(priv, "Can not allocate SKB buffers\n"); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs @@ -266,6 +262,20 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority) break; } + spin_lock_irqsave(&rxq->lock, flags); + + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + dev_kfree_skb_any(skb); + return; + } + element = rxq->rx_used.next; + rxb = list_entry(element, struct iwl_rx_mem_buffer, list); + list_del(element); + + spin_unlock_irqrestore(&rxq->lock, flags); + + rxb->skb = skb; /* Get physical address of RB/SKB */ rxb->real_dma_addr = pci_map_single( priv->pci_dev, diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 2238c9f2018..090966837f3 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -1134,6 +1134,7 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority) struct iwl_rx_queue *rxq = &priv->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; + struct sk_buff *skb; unsigned long flags; while (1) { @@ -1143,17 +1144,11 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority) spin_unlock_irqrestore(&rxq->lock, flags); return; } - - element = rxq->rx_used.next; - rxb = list_entry(element, struct iwl_rx_mem_buffer, list); - list_del(element); spin_unlock_irqrestore(&rxq->lock, flags); /* Alloc a new receive buffer */ - rxb->skb = - alloc_skb(priv->hw_params.rx_buf_size, - priority); - if (!rxb->skb) { + skb = alloc_skb(priv->hw_params.rx_buf_size, priority); + if (!skb) { if (net_ratelimit()) IWL_CRIT(priv, ": Can not allocate SKB buffers\n"); /* We don't reschedule replenish work here -- we will @@ -1162,6 +1157,19 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority) break; } + spin_lock_irqsave(&rxq->lock, flags); + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + dev_kfree_skb_any(skb); + return; + } + element = rxq->rx_used.next; + rxb = list_entry(element, struct iwl_rx_mem_buffer, list); + list_del(element); + spin_unlock_irqrestore(&rxq->lock, flags); + + rxb->skb = skb; + /* If radiotap head is required, reserve some headroom here. * The physical head count is a variable rx_stats->phy_count. * We reserve 4 bytes here. Plus these extra bytes, the -- cgit v1.2.3 From 4a86eaa46dbaf26498bcf0bb3535d4fd36a32858 Mon Sep 17 00:00:00 2001 From: Wey-Yi Guy Date: Fri, 11 Sep 2009 10:38:18 -0700 Subject: iwlwifi: find the correct first antenna We can not assume antenna "A" is the first valid anttena for all the NIC. Need to make sure choice the correct antenna based on h/w configuration for transmit to avoid sending frame on invalid antenna Signed-off-by: Wey-Yi Guy Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index fd731534df1..346dc06fa7b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -760,6 +760,7 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, u16 high_low; u8 switch_to_legacy = 0; u8 is_green = lq_sta->is_green; + struct iwl_priv *priv = lq_sta->drv; /* check if we need to switch from HT to legacy rates. * assumption is that mandatory rates (1Mbps or 6Mbps) @@ -773,7 +774,8 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, tbl->lq_type = LQ_G; if (num_of_ant(tbl->ant_type) > 1) - tbl->ant_type = ANT_A;/*FIXME:RS*/ + tbl->ant_type = + first_antenna(priv->hw_params.valid_tx_ant); tbl->is_ht40 = 0; tbl->is_SGI = 0; -- cgit v1.2.3 From 96d8c6af24bcacb16f18fc88fb7f2de1a59c09d6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 11 Sep 2009 10:50:37 -0700 Subject: iwlwifi: disable powersave for 4965 There's a bug in 4965 powersave that appears to be related to the way it keeps track of its data during sleep, but we haven't found it yet. Due to that, using powersave may spontaneously cause the device to SYSASSERT when transitioning from sleep to wake. Therefore, disable powersave for 4965, until (if ever, unfortunately) we can identify and fix the problem. Cf. http://bugzilla.intellinuxwireless.org/show_bug.cgi?id=1982 which was closed, but now has re-appeared with IDLE mode, which probably means we never really fixed it. Signed-off-by: Johannes Berg Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 1 + drivers/net/wireless/iwlwifi/iwl-core.c | 9 ++++++--- drivers/net/wireless/iwlwifi/iwl-core.h | 1 + drivers/net/wireless/iwlwifi/iwl-power.c | 5 +++-- 4 files changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 6a13bfbc9d9..ca61d3796ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2346,6 +2346,7 @@ struct iwl_cfg iwl4965_agn_cfg = { .mod_params = &iwl4965_mod_params, .use_isr_legacy = true, .ht_greenfield_support = false, + .broken_powersave = true, }; /* Module firmware */ diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index acfd7b40afb..fd26c0dc9c5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1585,9 +1585,12 @@ int iwl_setup_mac(struct iwl_priv *priv) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_SPECTRUM_MGMT | - IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + IEEE80211_HW_SPECTRUM_MGMT; + + if (!priv->cfg->broken_powersave) + hw->flags |= IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index c04d2a27081..7ff9ffb2b70 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -252,6 +252,7 @@ struct iwl_cfg { const u16 max_ll_items; const bool shadow_ram_support; const bool ht_greenfield_support; + const bool broken_powersave; }; /*************************** diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index 4ec6a8307cc..60be976afff 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -292,8 +292,9 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force) else dtimper = 1; - /* TT power setting overwrites everything */ - if (tt->state >= IWL_TI_1) + if (priv->cfg->broken_powersave) + iwl_power_sleep_cam_cmd(priv, &cmd); + else if (tt->state >= IWL_TI_1) iwl_static_sleep_cmd(priv, &cmd, tt->tt_power_mode, dtimper); else if (!enabled) iwl_power_sleep_cam_cmd(priv, &cmd); -- cgit v1.2.3 From d45b90ddee58db8caa3b06c3709b73dd0444559c Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 13 Sep 2009 15:55:13 -0500 Subject: ssb: Fix error when V1 SPROM extraction is forced When an SPROM revision is not recognized, the code falls back to a V1 SPROM; however, that revision is not forced in the appropriate structure. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index f853d5600ca..9e50896233a 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -600,6 +600,7 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, ssb_printk(KERN_WARNING PFX "Unsupported SPROM" " revision %d detected. Will extract" " v1\n", out->revision); + out->revision = 1; sprom_extract_r123(out, in); } } -- cgit v1.2.3 From c3de943ad6e535ceb5238b3b7d3903c6e294061a Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 14 Sep 2009 23:08:43 +0200 Subject: p54usb: add Zcomax XG-705A usbid This patch adds a new usbid for Zcomax XG-705A to the device table. Cc: stable@kernel.org Reported-by: Jari Jaakola Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54usb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index e44460ff149..17e199546ee 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -67,6 +67,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/ {USB_DEVICE(0x0cde, 0x0006)}, /* Medion MD40900 */ {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */ + {USB_DEVICE(0x0cde, 0x0015)}, /* Zcomax XG-705A */ {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */ {USB_DEVICE(0x124a, 0x4025)}, /* IOGear GWU513 (GW3887IK chip) */ {USB_DEVICE(0x1260, 0xee22)}, /* SMC 2862W-G version 2 */ -- cgit v1.2.3 From 71702bc13efd3238e02197780d6e62c51c196a9d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 15 Sep 2009 14:52:40 -0700 Subject: ssb/sdio: fix printk format warnings Fix printk format warnings: drivers/ssb/sdio.c:336: warning: format '%u' expects type 'unsigned int', but argument 7 has type 'size_t' drivers/ssb/sdio.c:443: warning: format '%u' expects type 'unsigned int', but argument 7 has type 'size_t' Signed-off-by: Randy Dunlap Signed-off-by: John W. Linville --- drivers/ssb/sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ssb/sdio.c b/drivers/ssb/sdio.c index 4ff73473e69..65a6080cb02 100644 --- a/drivers/ssb/sdio.c +++ b/drivers/ssb/sdio.c @@ -333,7 +333,7 @@ static void ssb_sdio_block_read(struct ssb_device *dev, void *buffer, goto out; err_out: - dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%u), error %d\n", + dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n", bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error); out: sdio_release_host(bus->host_sdio); @@ -440,7 +440,7 @@ static void ssb_sdio_block_write(struct ssb_device *dev, const void *buffer, goto out; err_out: - dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%u), error %d\n", + dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n", bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error); out: sdio_release_host(bus->host_sdio); -- cgit v1.2.3 From a78b3bb2f3ab9afcf78dbcff18fd7bf900c7c27e Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 11 Sep 2009 21:44:05 +0200 Subject: b43: Rewrite suspend/resume code This removes most of the b43 suspend/resume code (it's handled by mac80211) and moves the registration of devices to the attachment phase. This is required, because we must not register/unregister devices on suspend/resume. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/b43.h | 19 +-- drivers/net/wireless/b43/leds.c | 259 ++++++++++++++++++++++++++++------------ drivers/net/wireless/b43/leds.h | 28 ++++- drivers/net/wireless/b43/main.c | 100 ++++------------ 4 files changed, 233 insertions(+), 173 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 09cfe68537b..89ccf0d50f3 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -629,13 +629,6 @@ struct b43_wl { * from the mac80211 subsystem. */ u16 mac80211_initially_registered_queues; - /* R/W lock for data transmission. - * Transmissions on 2+ queues can run concurrently, but somebody else - * might sync with TX by write_lock_irqsave()'ing. */ - rwlock_t tx_lock; - /* Lock for LEDs access. */ - spinlock_t leds_lock; - /* We can only have one operating interface (802.11 core) * at a time. General information about this interface follows. */ @@ -686,6 +679,9 @@ struct b43_wl { struct work_struct tx_work; /* Queue of packets to be transmitted. */ struct sk_buff_head tx_queue; + + /* The device LEDs. */ + struct b43_leds leds; }; /* The type of the firmware file. */ @@ -768,13 +764,10 @@ struct b43_wldev { /* The device initialization status. * Use b43_status() to query. */ atomic_t __init_status; - /* Saved init status for handling suspend. */ - int suspend_init_status; bool bad_frames_preempt; /* Use "Bad Frames Preemption" (default off) */ bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM) */ bool radio_hw_enable; /* saved state of radio hardware enabled state */ - bool suspend_in_progress; /* TRUE, if we are in a suspend/resume cycle */ bool qos_enabled; /* TRUE, if QoS is used. */ bool hwcrypto_enabled; /* TRUE, if HW crypto acceleration is enabled. */ @@ -794,12 +787,6 @@ struct b43_wldev { /* Various statistics about the physical device. */ struct b43_stats stats; - /* The device LEDs. */ - struct b43_led led_tx; - struct b43_led led_rx; - struct b43_led led_assoc; - struct b43_led led_radio; - /* Reason code of the last interrupt. */ u32 irq_reason; u32 dma_reason[6]; diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index c8b317094c3..ac5a322c84b 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -34,57 +34,91 @@ static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index, bool activelow) { - struct b43_wl *wl = dev->wl; - unsigned long flags; u16 ctl; - spin_lock_irqsave(&wl->leds_lock, flags); ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); if (activelow) ctl &= ~(1 << led_index); else ctl |= (1 << led_index); b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); - spin_unlock_irqrestore(&wl->leds_lock, flags); } static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index, bool activelow) { - struct b43_wl *wl = dev->wl; - unsigned long flags; u16 ctl; - spin_lock_irqsave(&wl->leds_lock, flags); ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); if (activelow) ctl |= (1 << led_index); else ctl &= ~(1 << led_index); b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); - spin_unlock_irqrestore(&wl->leds_lock, flags); } -/* Callback from the LED subsystem. */ -static void b43_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) +static void b43_led_update(struct b43_wldev *dev, + struct b43_led *led) { - struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); - struct b43_wldev *dev = led->dev; bool radio_enabled; + bool turn_on; - if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) + if (!led->wl) return; - /* Checking the radio-enabled status here is slightly racy, - * but we want to avoid the locking overhead and we don't care - * whether the LED has the wrong state for a second. */ radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); - if (brightness == LED_OFF || !radio_enabled) - b43_led_turn_off(dev, led->index, led->activelow); + /* The led->state read is racy, but we don't care. In case we raced + * with the brightness_set handler, we will be called again soon + * to fixup our state. */ + if (radio_enabled) + turn_on = atomic_read(&led->state) != LED_OFF; else + turn_on = 0; + if (turn_on == led->hw_state) + return; + led->hw_state = turn_on; + + if (turn_on) b43_led_turn_on(dev, led->index, led->activelow); + else + b43_led_turn_off(dev, led->index, led->activelow); +} + +static void b43_leds_work(struct work_struct *work) +{ + struct b43_leds *leds = container_of(work, struct b43_leds, work); + struct b43_wl *wl = container_of(leds, struct b43_wl, leds); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) + goto out_unlock; + + b43_led_update(dev, &wl->leds.led_tx); + b43_led_update(dev, &wl->leds.led_rx); + b43_led_update(dev, &wl->leds.led_radio); + b43_led_update(dev, &wl->leds.led_assoc); + +out_unlock: + mutex_unlock(&wl->mutex); +} + +/* Callback from the LED subsystem. */ +static void b43_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); + struct b43_wl *wl = led->wl; + + /* The check for current_dev is only needed while unregistering, + * so it is sequencial and does not race. But we must not dereference + * current_dev here. */ + if (likely(wl->current_dev)) { + atomic_set(&led->state, brightness); + ieee80211_queue_work(wl->hw, &wl->leds.work); + } } static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, @@ -93,15 +127,15 @@ static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, { int err; - b43_led_turn_off(dev, led_index, activelow); - if (led->dev) + if (led->wl) return -EEXIST; if (!default_trigger) return -EINVAL; - led->dev = dev; + led->wl = dev->wl; led->index = led_index; led->activelow = activelow; strncpy(led->name, name, sizeof(led->name)); + atomic_set(&led->state, 0); led->led_dev.name = led->name; led->led_dev.default_trigger = default_trigger; @@ -110,19 +144,19 @@ static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, err = led_classdev_register(dev->dev->dev, &led->led_dev); if (err) { b43warn(dev->wl, "LEDs: Failed to register %s\n", name); - led->dev = NULL; + led->wl = NULL; return err; } + return 0; } static void b43_unregister_led(struct b43_led *led) { - if (!led->dev) + if (!led->wl) return; led_classdev_unregister(&led->led_dev); - b43_led_turn_off(led->dev, led->index, led->activelow); - led->dev = NULL; + led->wl = NULL; } static void b43_map_led(struct b43_wldev *dev, @@ -137,24 +171,20 @@ static void b43_map_led(struct b43_wldev *dev, * generic LED triggers. */ switch (behaviour) { case B43_LED_INACTIVE: - break; case B43_LED_OFF: - b43_led_turn_off(dev, led_index, activelow); - break; case B43_LED_ON: - b43_led_turn_on(dev, led_index, activelow); break; case B43_LED_ACTIVITY: case B43_LED_TRANSFER: case B43_LED_APTRANSFER: snprintf(name, sizeof(name), "b43-%s::tx", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_tx, name, + b43_register_led(dev, &dev->wl->leds.led_tx, name, ieee80211_get_tx_led_name(hw), led_index, activelow); snprintf(name, sizeof(name), "b43-%s::rx", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_rx, name, + b43_register_led(dev, &dev->wl->leds.led_rx, name, ieee80211_get_rx_led_name(hw), led_index, activelow); break; @@ -164,18 +194,15 @@ static void b43_map_led(struct b43_wldev *dev, case B43_LED_MODE_BG: snprintf(name, sizeof(name), "b43-%s::radio", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_radio, name, + b43_register_led(dev, &dev->wl->leds.led_radio, name, ieee80211_get_radio_led_name(hw), led_index, activelow); - /* Sync the RF-kill LED state with radio and switch states. */ - if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) - b43_led_turn_on(dev, led_index, activelow); break; case B43_LED_WEIRD: case B43_LED_ASSOC: snprintf(name, sizeof(name), "b43-%s::assoc", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_assoc, name, + b43_register_led(dev, &dev->wl->leds.led_assoc, name, ieee80211_get_assoc_led_name(hw), led_index, activelow); break; @@ -186,58 +213,140 @@ static void b43_map_led(struct b43_wldev *dev, } } -void b43_leds_init(struct b43_wldev *dev) +static void b43_led_get_sprominfo(struct b43_wldev *dev, + unsigned int led_index, + enum b43_led_behaviour *behaviour, + bool *activelow) { struct ssb_bus *bus = dev->dev->bus; u8 sprom[4]; - int i; - enum b43_led_behaviour behaviour; - bool activelow; sprom[0] = bus->sprom.gpio0; sprom[1] = bus->sprom.gpio1; sprom[2] = bus->sprom.gpio2; sprom[3] = bus->sprom.gpio3; - for (i = 0; i < 4; i++) { - if (sprom[i] == 0xFF) { - /* There is no LED information in the SPROM - * for this LED. Hardcode it here. */ - activelow = 0; - switch (i) { - case 0: - behaviour = B43_LED_ACTIVITY; - activelow = 1; - if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) - behaviour = B43_LED_RADIO_ALL; - break; - case 1: - behaviour = B43_LED_RADIO_B; - if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) - behaviour = B43_LED_ASSOC; - break; - case 2: - behaviour = B43_LED_RADIO_A; - break; - case 3: - behaviour = B43_LED_OFF; - break; - default: - B43_WARN_ON(1); - return; - } + if (sprom[led_index] == 0xFF) { + /* There is no LED information in the SPROM + * for this LED. Hardcode it here. */ + *activelow = 0; + switch (led_index) { + case 0: + *behaviour = B43_LED_ACTIVITY; + *activelow = 1; + if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) + *behaviour = B43_LED_RADIO_ALL; + break; + case 1: + *behaviour = B43_LED_RADIO_B; + if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) + *behaviour = B43_LED_ASSOC; + break; + case 2: + *behaviour = B43_LED_RADIO_A; + break; + case 3: + *behaviour = B43_LED_OFF; + break; + default: + B43_WARN_ON(1); + return; + } + } else { + *behaviour = sprom[led_index] & B43_LED_BEHAVIOUR; + *activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW); + } +} + +void b43_leds_init(struct b43_wldev *dev) +{ + struct b43_led *led; + unsigned int i; + enum b43_led_behaviour behaviour; + bool activelow; + + /* Sync the RF-kill LED state (if we have one) with radio and switch states. */ + led = &dev->wl->leds.led_radio; + if (led->wl) { + if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) { + b43_led_turn_on(dev, led->index, led->activelow); + led->hw_state = 1; + atomic_set(&led->state, 1); } else { - behaviour = sprom[i] & B43_LED_BEHAVIOUR; - activelow = !!(sprom[i] & B43_LED_ACTIVELOW); + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + } + + /* Initialize TX/RX/ASSOC leds */ + led = &dev->wl->leds.led_tx; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + led = &dev->wl->leds.led_rx; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + led = &dev->wl->leds.led_assoc; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + + /* Initialize other LED states. */ + for (i = 0; i < B43_MAX_NR_LEDS; i++) { + b43_led_get_sprominfo(dev, i, &behaviour, &activelow); + switch (behaviour) { + case B43_LED_OFF: + b43_led_turn_off(dev, i, activelow); + break; + case B43_LED_ON: + b43_led_turn_on(dev, i, activelow); + break; + default: + /* Leave others as-is. */ + break; } - b43_map_led(dev, i, behaviour, activelow); } } void b43_leds_exit(struct b43_wldev *dev) { - b43_unregister_led(&dev->led_tx); - b43_unregister_led(&dev->led_rx); - b43_unregister_led(&dev->led_assoc); - b43_unregister_led(&dev->led_radio); + struct b43_leds *leds = &dev->wl->leds; + + b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow); + b43_led_turn_off(dev, leds->led_rx.index, leds->led_rx.activelow); + b43_led_turn_off(dev, leds->led_assoc.index, leds->led_assoc.activelow); + b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow); +} + +void b43_leds_register(struct b43_wldev *dev) +{ + unsigned int i; + enum b43_led_behaviour behaviour; + bool activelow; + + INIT_WORK(&dev->wl->leds.work, b43_leds_work); + + /* Register the LEDs to the LED subsystem. */ + for (i = 0; i < B43_MAX_NR_LEDS; i++) { + b43_led_get_sprominfo(dev, i, &behaviour, &activelow); + b43_map_led(dev, i, behaviour, activelow); + } +} + +void b43_leds_unregister(struct b43_wldev *dev) +{ + struct b43_leds *leds = &dev->wl->leds; + + b43_unregister_led(&leds->led_tx); + b43_unregister_led(&leds->led_rx); + b43_unregister_led(&leds->led_assoc); + b43_unregister_led(&leds->led_radio); } diff --git a/drivers/net/wireless/b43/leds.h b/drivers/net/wireless/b43/leds.h index b8b1dd52124..c4a58a0fc1d 100644 --- a/drivers/net/wireless/b43/leds.h +++ b/drivers/net/wireless/b43/leds.h @@ -7,12 +7,13 @@ struct b43_wldev; #include #include +#include #define B43_LED_MAX_NAME_LEN 31 struct b43_led { - struct b43_wldev *dev; + struct b43_wl *wl; /* The LED class device */ struct led_classdev led_dev; /* The index number of the LED. */ @@ -22,8 +23,23 @@ struct b43_led { bool activelow; /* The unique name string for this LED device. */ char name[B43_LED_MAX_NAME_LEN + 1]; + /* The current status of the LED. This is updated locklessly. */ + atomic_t state; + /* The active state in hardware. */ + bool hw_state; }; +struct b43_leds { + struct b43_led led_tx; + struct b43_led led_rx; + struct b43_led led_radio; + struct b43_led led_assoc; + + struct work_struct work; +}; + +#define B43_MAX_NR_LEDS 4 + #define B43_LED_BEHAVIOUR 0x7F #define B43_LED_ACTIVELOW 0x80 /* LED behaviour values */ @@ -42,6 +58,8 @@ enum b43_led_behaviour { B43_LED_INACTIVE, }; +void b43_leds_register(struct b43_wldev *dev); +void b43_leds_unregister(struct b43_wldev *dev); void b43_leds_init(struct b43_wldev *dev); void b43_leds_exit(struct b43_wldev *dev); @@ -49,10 +67,16 @@ void b43_leds_exit(struct b43_wldev *dev); #else /* CONFIG_B43_LEDS */ /* LED support disabled */ -struct b43_led { +struct b43_leds { /* empty */ }; +static inline void b43_leds_register(struct b43_wldev *dev) +{ +} +static inline void b43_leds_unregister(struct b43_wldev *dev) +{ +} static inline void b43_leds_init(struct b43_wldev *dev) { } diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index e789792a36b..2984a915f8b 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3002,14 +3002,18 @@ static void b43_security_init(struct b43_wldev *dev) static int b43_rng_read(struct hwrng *rng, u32 *data) { struct b43_wl *wl = (struct b43_wl *)rng->priv; + struct b43_wldev *dev; + int count = -ENODEV; - /* FIXME: We need to take wl->mutex here to make sure the device - * is not going away from under our ass. However it could deadlock - * with hwrng internal locking. */ - - *data = b43_read16(wl->current_dev, B43_MMIO_RNG); + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (likely(dev && b43_status(dev) >= B43_STAT_INITIALIZED)) { + *data = b43_read16(dev, B43_MMIO_RNG); + count = sizeof(u16); + } + mutex_unlock(&wl->mutex); - return (sizeof(u16)); + return count; } #endif /* CONFIG_B43_HWRNG */ @@ -3851,6 +3855,7 @@ redo: b43_mac_suspend(dev); free_irq(dev->dev->irq, dev); + b43_leds_exit(dev); b43dbg(wl, "Wireless interface stopped\n"); return dev; @@ -3882,8 +3887,10 @@ static int b43_wireless_core_start(struct b43_wldev *dev) /* Start maintainance work */ b43_periodic_tasks_setup(dev); + b43_leds_init(dev); + b43dbg(dev->wl, "Wireless interface started\n"); - out: +out: return err; } @@ -4160,10 +4167,6 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) macctl |= B43_MACCTL_PSM_JMP0; b43_write32(dev, B43_MMIO_MACCTL, macctl); - if (!dev->suspend_in_progress) { - b43_leds_exit(dev); - b43_rng_exit(dev->wl); - } b43_dma_free(dev); b43_pio_free(dev); b43_chip_exit(dev); @@ -4180,7 +4183,6 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) /* Initialize a wireless core */ static int b43_wireless_core_init(struct b43_wldev *dev) { - struct b43_wl *wl = dev->wl; struct ssb_bus *bus = dev->dev->bus; struct ssb_sprom *sprom = &bus->sprom; struct b43_phy *phy = &dev->phy; @@ -4280,15 +4282,11 @@ static int b43_wireless_core_init(struct b43_wldev *dev) ssb_bus_powerup(bus, !(sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)); b43_upload_card_macaddress(dev); b43_security_init(dev); - if (!dev->suspend_in_progress) - b43_rng_init(wl); ieee80211_wake_queues(dev->wl->hw); b43_set_status(dev, B43_STAT_INITIALIZED); - if (!dev->suspend_in_progress) - b43_leds_init(dev); out: return err; @@ -4837,7 +4835,6 @@ static int b43_wireless_init(struct ssb_device *dev) /* Initialize struct b43_wl */ wl->hw = hw; - spin_lock_init(&wl->leds_lock); mutex_init(&wl->mutex); spin_lock_init(&wl->hardirq_lock); INIT_LIST_HEAD(&wl->devlist); @@ -4878,6 +4875,8 @@ static int b43_probe(struct ssb_device *dev, const struct ssb_device_id *id) err = ieee80211_register_hw(wl->hw); if (err) goto err_one_core_detach; + b43_leds_register(wl->current_dev); + b43_rng_init(wl); } out: @@ -4906,12 +4905,16 @@ static void b43_remove(struct ssb_device *dev) * might have modified it. Restoring is important, so the networking * stack can properly free resources. */ wl->hw->queues = wl->mac80211_initially_registered_queues; + wl->current_dev = NULL; + cancel_work_sync(&wl->leds.work); ieee80211_unregister_hw(wl->hw); } b43_one_core_detach(dev); if (list_empty(&wl->devlist)) { + b43_rng_exit(wl); + b43_leds_unregister(wldev); /* Last core on the chip unregistered. * We can destroy common struct b43_wl. */ @@ -4929,74 +4932,11 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason) ieee80211_queue_work(dev->wl->hw, &dev->restart_work); } -#ifdef CONFIG_PM - -static int b43_suspend(struct ssb_device *dev, pm_message_t state) -{ - struct b43_wldev *wldev = ssb_get_drvdata(dev); - struct b43_wl *wl = wldev->wl; - - b43dbg(wl, "Suspending...\n"); - - mutex_lock(&wl->mutex); - wldev->suspend_in_progress = true; - wldev->suspend_init_status = b43_status(wldev); - if (wldev->suspend_init_status >= B43_STAT_STARTED) - wldev = b43_wireless_core_stop(wldev); - if (wldev && wldev->suspend_init_status >= B43_STAT_INITIALIZED) - b43_wireless_core_exit(wldev); - mutex_unlock(&wl->mutex); - - b43dbg(wl, "Device suspended.\n"); - - return 0; -} - -static int b43_resume(struct ssb_device *dev) -{ - struct b43_wldev *wldev = ssb_get_drvdata(dev); - struct b43_wl *wl = wldev->wl; - int err = 0; - - b43dbg(wl, "Resuming...\n"); - - mutex_lock(&wl->mutex); - if (wldev->suspend_init_status >= B43_STAT_INITIALIZED) { - err = b43_wireless_core_init(wldev); - if (err) { - b43err(wl, "Resume failed at core init\n"); - goto out; - } - } - if (wldev->suspend_init_status >= B43_STAT_STARTED) { - err = b43_wireless_core_start(wldev); - if (err) { - b43_leds_exit(wldev); - b43_rng_exit(wldev->wl); - b43_wireless_core_exit(wldev); - b43err(wl, "Resume failed at core start\n"); - goto out; - } - } - b43dbg(wl, "Device resumed.\n"); - out: - wldev->suspend_in_progress = false; - mutex_unlock(&wl->mutex); - return err; -} - -#else /* CONFIG_PM */ -# define b43_suspend NULL -# define b43_resume NULL -#endif /* CONFIG_PM */ - static struct ssb_driver b43_ssb_driver = { .name = KBUILD_MODNAME, .id_table = b43_ssb_tbl, .probe = b43_probe, .remove = b43_remove, - .suspend = b43_suspend, - .resume = b43_resume, }; static void b43_print_driverinfo(void) -- cgit v1.2.3 From 3dbba8e281552da640080f08a0f127d48456669f Mon Sep 17 00:00:00 2001 From: Albert Herranz Date: Thu, 10 Sep 2009 19:34:49 +0200 Subject: b43: Add Soft-MAC SDIO device support This adds support for Soft-MAC SDIO devices to b43. The driver still lacks some fixes for SDIO devices, so it's currently marked as BROKEN. Signed-off-by: Albert Herranz Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/Kconfig | 21 +++- drivers/net/wireless/b43/Makefile | 1 + drivers/net/wireless/b43/main.c | 76 ++++++++++++--- drivers/net/wireless/b43/sdio.c | 197 ++++++++++++++++++++++++++++++++++++++ drivers/net/wireless/b43/sdio.h | 45 +++++++++ 5 files changed, 324 insertions(+), 16 deletions(-) create mode 100644 drivers/net/wireless/b43/sdio.c create mode 100644 drivers/net/wireless/b43/sdio.h (limited to 'drivers') diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 83e38134acc..4fc2ce429c7 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -61,11 +61,28 @@ config B43_PCMCIA If unsure, say N. +config B43_SDIO + bool "Broadcom 43xx SDIO device support (EXPERIMENTAL)" + depends on B43 && SSB_SDIOHOST_POSSIBLE && EXPERIMENTAL && BROKEN + select SSB_SDIOHOST + ---help--- + Broadcom 43xx device support for Soft-MAC SDIO devices. + + With this config option you can drive Soft-MAC b43 cards with a + Secure Digital I/O interface. + This includes the WLAN daughter card found on the Nintendo Wii + video game console. + Note that this does not support Broadcom 43xx Full-MAC devices. + + It's safe to select Y here, even if you don't have a B43 SDIO device. + + If unsure, say N. + # Data transfers to the device via PIO -# This is only needed on PCMCIA devices. All others can do DMA properly. +# This is only needed on PCMCIA and SDIO devices. All others can do DMA properly. config B43_PIO bool - depends on B43 && (B43_PCMCIA || B43_FORCE_PIO) + depends on B43 && (B43_SDIO || B43_PCMCIA || B43_FORCE_PIO) select SSB_BLOCKIO default y diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile index da379f4b0c3..84772a2542d 100644 --- a/drivers/net/wireless/b43/Makefile +++ b/drivers/net/wireless/b43/Makefile @@ -16,6 +16,7 @@ b43-$(CONFIG_B43_PIO) += pio.o b43-y += rfkill.o b43-$(CONFIG_B43_LEDS) += leds.o b43-$(CONFIG_B43_PCMCIA) += pcmcia.o +b43-$(CONFIG_B43_SDIO) += sdio.o b43-$(CONFIG_B43_DEBUG) += debugfs.o obj-$(CONFIG_B43) += b43.o diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 2984a915f8b..950a838757c 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -8,6 +8,9 @@ Copyright (c) 2005 Danny van Dyk Copyright (c) 2005 Andreas Jaggi + SDIO support + Copyright (c) 2009 Albert Herranz + Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. @@ -53,6 +56,8 @@ #include "xmit.h" #include "lo.h" #include "pcmcia.h" +#include "sdio.h" +#include MODULE_DESCRIPTION("Broadcom B43 wireless driver"); MODULE_AUTHOR("Martin Langer"); @@ -1587,7 +1592,7 @@ static void b43_beacon_update_trigger_work(struct work_struct *work) mutex_lock(&wl->mutex); dev = wl->current_dev; if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) { - if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) { + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { /* wl->mutex is enough. */ b43_do_beacon_update_trigger_work(dev); mmiowb(); @@ -1905,6 +1910,27 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id) return ret; } +/* SDIO interrupt handler. This runs in process context. */ +static void b43_sdio_interrupt_handler(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + struct sdio_func *func = dev->dev->bus->host_sdio; + irqreturn_t ret; + + if (unlikely(b43_status(dev) < B43_STAT_STARTED)) + return; + + mutex_lock(&wl->mutex); + sdio_release_host(func); + + ret = b43_do_interrupt(dev); + if (ret == IRQ_WAKE_THREAD) + b43_do_interrupt_thread(dev); + + sdio_claim_host(func); + mutex_unlock(&wl->mutex); +} + void b43_do_release_fw(struct b43_firmware_file *fw) { release_firmware(fw->data); @@ -3824,7 +3850,7 @@ redo: /* Disable interrupts on the device. */ b43_set_status(dev, B43_STAT_INITIALIZED); - if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) { + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { /* wl->mutex is locked. That is enough. */ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ @@ -3854,7 +3880,10 @@ redo: dev_kfree_skb(skb_dequeue(&wl->tx_queue)); b43_mac_suspend(dev); - free_irq(dev->dev->irq, dev); + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) + b43_sdio_free_irq(dev); + else + free_irq(dev->dev->irq, dev); b43_leds_exit(dev); b43dbg(wl, "Wireless interface stopped\n"); @@ -3869,12 +3898,20 @@ static int b43_wireless_core_start(struct b43_wldev *dev) B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED); drain_txstatus_queue(dev); - err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, - b43_interrupt_thread_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); - if (err) { - b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); - goto out; + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { + err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler); + if (err) { + b43err(dev->wl, "Cannot request SDIO IRQ\n"); + goto out; + } + } else { + err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, + b43_interrupt_thread_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (err) { + b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); + goto out; + } } /* We are ready to run. */ @@ -4266,7 +4303,9 @@ static int b43_wireless_core_init(struct b43_wldev *dev) /* Maximum Contention Window */ b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); - if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || B43_FORCE_PIO) { + if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || + (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) || + B43_FORCE_PIO) { dev->__using_pio_transfers = 1; err = b43_pio_init(dev); } else { @@ -4942,7 +4981,7 @@ static struct ssb_driver b43_ssb_driver = { static void b43_print_driverinfo(void) { const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "", - *feat_leds = ""; + *feat_leds = "", *feat_sdio = ""; #ifdef CONFIG_B43_PCI_AUTOSELECT feat_pci = "P"; @@ -4955,12 +4994,15 @@ static void b43_print_driverinfo(void) #endif #ifdef CONFIG_B43_LEDS feat_leds = "L"; +#endif +#ifdef CONFIG_B43_SDIO + feat_sdio = "S"; #endif printk(KERN_INFO "Broadcom 43xx driver loaded " - "[ Features: %s%s%s%s, Firmware-ID: " + "[ Features: %s%s%s%s%s, Firmware-ID: " B43_SUPPORTED_FIRMWARE_ID " ]\n", feat_pci, feat_pcmcia, feat_nphy, - feat_leds); + feat_leds, feat_sdio); } static int __init b43_init(void) @@ -4971,13 +5013,18 @@ static int __init b43_init(void) err = b43_pcmcia_init(); if (err) goto err_dfs_exit; - err = ssb_driver_register(&b43_ssb_driver); + err = b43_sdio_init(); if (err) goto err_pcmcia_exit; + err = ssb_driver_register(&b43_ssb_driver); + if (err) + goto err_sdio_exit; b43_print_driverinfo(); return err; +err_sdio_exit: + b43_sdio_exit(); err_pcmcia_exit: b43_pcmcia_exit(); err_dfs_exit: @@ -4988,6 +5035,7 @@ err_dfs_exit: static void __exit b43_exit(void) { ssb_driver_unregister(&b43_ssb_driver); + b43_sdio_exit(); b43_pcmcia_exit(); b43_debugfs_exit(); } diff --git a/drivers/net/wireless/b43/sdio.c b/drivers/net/wireless/b43/sdio.c new file mode 100644 index 00000000000..2d337f1a60b --- /dev/null +++ b/drivers/net/wireless/b43/sdio.c @@ -0,0 +1,197 @@ +/* + * Broadcom B43 wireless driver + * + * SDIO over Sonics Silicon Backplane bus glue for b43. + * + * Copyright (C) 2009 Albert Herranz + * Copyright (C) 2009 Michael Buesch + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include "sdio.h" +#include "b43.h" + + +#define HNBU_CHIPID 0x01 /* vendor & device id */ + +#define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */ + + +static const struct b43_sdio_quirk { + u16 vendor; + u16 device; + unsigned int quirks; +} b43_sdio_quirks[] = { + { 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, }, + { }, +}; + + +static unsigned int b43_sdio_get_quirks(u16 vendor, u16 device) +{ + const struct b43_sdio_quirk *q; + + for (q = b43_sdio_quirks; q->quirks; q++) { + if (vendor == q->vendor && device == q->device) + return q->quirks; + } + + return 0; +} + +static void b43_sdio_interrupt_dispatcher(struct sdio_func *func) +{ + struct b43_sdio *sdio = sdio_get_drvdata(func); + struct b43_wldev *dev = sdio->irq_handler_opaque; + + sdio->irq_handler(dev); +} + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)) +{ + struct ssb_bus *bus = dev->dev->bus; + struct sdio_func *func = bus->host_sdio; + struct b43_sdio *sdio = sdio_get_drvdata(func); + int err; + + sdio->irq_handler_opaque = dev; + sdio->irq_handler = handler; + sdio_claim_host(func); + err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher); + sdio_release_host(func); + + return err; +} + +void b43_sdio_free_irq(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct sdio_func *func = bus->host_sdio; + struct b43_sdio *sdio = sdio_get_drvdata(func); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); + sdio->irq_handler_opaque = NULL; + sdio->irq_handler = NULL; +} + +static int b43_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct b43_sdio *sdio; + struct sdio_func_tuple *tuple; + u16 vendor = 0, device = 0; + int error; + + /* Look for the card chip identifier. */ + tuple = func->tuples; + while (tuple) { + switch (tuple->code) { + case 0x80: + switch (tuple->data[0]) { + case HNBU_CHIPID: + if (tuple->size != 5) + break; + vendor = tuple->data[1] | (tuple->data[2]<<8); + device = tuple->data[3] | (tuple->data[4]<<8); + dev_info(&func->dev, "Chip ID %04x:%04x\n", + vendor, device); + break; + default: + break; + } + break; + default: + break; + } + tuple = tuple->next; + } + if (!vendor || !device) { + error = -ENODEV; + goto out; + } + + sdio_claim_host(func); + error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE); + if (error) { + dev_err(&func->dev, "failed to set block size to %u bytes," + " error %d\n", B43_SDIO_BLOCK_SIZE, error); + goto err_release_host; + } + error = sdio_enable_func(func); + if (error) { + dev_err(&func->dev, "failed to enable func, error %d\n", error); + goto err_release_host; + } + sdio_release_host(func); + + sdio = kzalloc(sizeof(*sdio), GFP_KERNEL); + if (!sdio) { + error = -ENOMEM; + dev_err(&func->dev, "failed to allocate ssb bus\n"); + goto err_disable_func; + } + error = ssb_bus_sdiobus_register(&sdio->ssb, func, + b43_sdio_get_quirks(vendor, device)); + if (error) { + dev_err(&func->dev, "failed to register ssb sdio bus," + " error %d\n", error); + goto err_free_ssb; + } + sdio_set_drvdata(func, sdio); + + return 0; + +err_free_ssb: + kfree(sdio); +err_disable_func: + sdio_disable_func(func); +err_release_host: + sdio_release_host(func); +out: + return error; +} + +static void b43_sdio_remove(struct sdio_func *func) +{ + struct b43_sdio *sdio = sdio_get_drvdata(func); + + ssb_bus_unregister(&sdio->ssb); + sdio_disable_func(func); + kfree(sdio); + sdio_set_drvdata(func, NULL); +} + +static const struct sdio_device_id b43_sdio_ids[] = { + { SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */ + { }, +}; + +static struct sdio_driver b43_sdio_driver = { + .name = "b43-sdio", + .id_table = b43_sdio_ids, + .probe = b43_sdio_probe, + .remove = b43_sdio_remove, +}; + +int b43_sdio_init(void) +{ + return sdio_register_driver(&b43_sdio_driver); +} + +void b43_sdio_exit(void) +{ + sdio_unregister_driver(&b43_sdio_driver); +} diff --git a/drivers/net/wireless/b43/sdio.h b/drivers/net/wireless/b43/sdio.h new file mode 100644 index 00000000000..fb633094403 --- /dev/null +++ b/drivers/net/wireless/b43/sdio.h @@ -0,0 +1,45 @@ +#ifndef B43_SDIO_H_ +#define B43_SDIO_H_ + +#include + +struct b43_wldev; + + +#ifdef CONFIG_B43_SDIO + +struct b43_sdio { + struct ssb_bus ssb; + void *irq_handler_opaque; + void (*irq_handler)(struct b43_wldev *dev); +}; + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)); +void b43_sdio_free_irq(struct b43_wldev *dev); + +int b43_sdio_init(void); +void b43_sdio_exit(void); + + +#else /* CONFIG_B43_SDIO */ + + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)) +{ + return -ENODEV; +} +void b43_sdio_free_irq(struct b43_wldev *dev) +{ +} +static inline int b43_sdio_init(void) +{ + return 0; +} +static inline void b43_sdio_exit(void) +{ +} + +#endif /* CONFIG_B43_SDIO */ +#endif /* B43_SDIO_H_ */ -- cgit v1.2.3 From ce6c4a13927bc0418169fe543c6614abfab051eb Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Thu, 10 Sep 2009 20:22:02 +0200 Subject: b43: Do not use _irqsafe callbacks We don't need to call the irqsafe callbacks. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/dma.c | 4 ++-- drivers/net/wireless/b43/pio.c | 2 +- drivers/net/wireless/b43/xmit.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index a467ee260a1..8701034569f 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1428,9 +1428,9 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ring->nr_failed_tx_packets++; ring->nr_total_packet_tries += status->frame_count; #endif /* DEBUG */ - ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb); + ieee80211_tx_status(dev->wl->hw, meta->skb); - /* skb is freed by ieee80211_tx_status_irqsafe() */ + /* skb is freed by ieee80211_tx_status() */ meta->skb = NULL; } else { /* No need to call free_descriptor_buffer here, as diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c index 3498b68385e..e96091b3149 100644 --- a/drivers/net/wireless/b43/pio.c +++ b/drivers/net/wireless/b43/pio.c @@ -574,7 +574,7 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev, q->buffer_used -= total_len; q->free_packet_slots += 1; - ieee80211_tx_status_irqsafe(dev->wl->hw, pack->skb); + ieee80211_tx_status(dev->wl->hw, pack->skb); pack->skb = NULL; list_add(&pack->list, &q->packets_list); diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 14f541248b5..c403c616d3b 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -690,7 +690,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) } memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx_irqsafe(dev->wl->hw, skb); + ieee80211_rx(dev->wl->hw, skb); return; drop: -- cgit v1.2.3 From a8696c800b8154e27b359866f7b837079cdb513a Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 11 Sep 2009 16:00:19 +0200 Subject: b43: Fix SDIO interrupt handler deadlock We need to release the SDIO host before locking the driver mutex. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 6 ------ drivers/net/wireless/b43/sdio.c | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 950a838757c..5b0aeff01db 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -1914,20 +1914,14 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id) static void b43_sdio_interrupt_handler(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; - struct sdio_func *func = dev->dev->bus->host_sdio; irqreturn_t ret; - if (unlikely(b43_status(dev) < B43_STAT_STARTED)) - return; - mutex_lock(&wl->mutex); - sdio_release_host(func); ret = b43_do_interrupt(dev); if (ret == IRQ_WAKE_THREAD) b43_do_interrupt_thread(dev); - sdio_claim_host(func); mutex_unlock(&wl->mutex); } diff --git a/drivers/net/wireless/b43/sdio.c b/drivers/net/wireless/b43/sdio.c index 2d337f1a60b..0d3ac64147a 100644 --- a/drivers/net/wireless/b43/sdio.c +++ b/drivers/net/wireless/b43/sdio.c @@ -54,7 +54,12 @@ static void b43_sdio_interrupt_dispatcher(struct sdio_func *func) struct b43_sdio *sdio = sdio_get_drvdata(func); struct b43_wldev *dev = sdio->irq_handler_opaque; + if (unlikely(b43_status(dev) < B43_STAT_STARTED)) + return; + + sdio_release_host(func); sdio->irq_handler(dev); + sdio_claim_host(func); } int b43_sdio_request_irq(struct b43_wldev *dev, -- cgit v1.2.3 From 176e9f6a4cf4b1bf66d18243ede0938a35c81541 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 11 Sep 2009 23:04:04 +0200 Subject: b43: Fix IRQ sync for SDIO synchronize_irq is meaningless for SDIO. sdio_release_irq will sync the IRQ thread for us. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 5b0aeff01db..7d22dadfd89 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3854,10 +3854,15 @@ redo: b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ spin_unlock_irq(&wl->hardirq_lock); } - /* Synchronize the interrupt handlers. Unlock to avoid deadlocks. */ + /* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */ orig_dev = dev; mutex_unlock(&wl->mutex); - synchronize_irq(dev->dev->irq); + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { + b43_sdio_free_irq(dev); + } else { + synchronize_irq(dev->dev->irq); + free_irq(dev->dev->irq, dev); + } mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev) @@ -3874,10 +3879,6 @@ redo: dev_kfree_skb(skb_dequeue(&wl->tx_queue)); b43_mac_suspend(dev); - if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) - b43_sdio_free_irq(dev); - else - free_irq(dev->dev->irq, dev); b43_leds_exit(dev); b43dbg(wl, "Wireless interface stopped\n"); -- cgit v1.2.3 From 990b86f4f9c54f83085d4136498ac44719b17654 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sat, 12 Sep 2009 00:48:03 +0200 Subject: b43: Add optional verbose runtime statistics This adds support for verbose runtime statistics. It defaults to off and must be enabled in debugfs, if desired. The first measurement may be incorrect, because statistics are not cleared after they got enabled through debugfs. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/b43.h | 4 ++++ drivers/net/wireless/b43/debugfs.c | 1 + drivers/net/wireless/b43/debugfs.h | 1 + drivers/net/wireless/b43/main.c | 34 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/b43/xmit.c | 3 +++ 5 files changed, 43 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 89ccf0d50f3..fa1549a03c7 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -817,6 +817,10 @@ struct b43_wldev { /* Debugging stuff follows. */ #ifdef CONFIG_B43_DEBUG struct b43_dfsentry *dfsentry; + unsigned int irq_count; + unsigned int irq_bit_count[32]; + unsigned int tx_count; + unsigned int rx_count; #endif }; diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c index 8f64943e3f6..80b19a44a40 100644 --- a/drivers/net/wireless/b43/debugfs.c +++ b/drivers/net/wireless/b43/debugfs.c @@ -689,6 +689,7 @@ static void b43_add_dynamic_debug(struct b43_wldev *dev) add_dyn_dbg("debug_lo", B43_DBG_LO, 0); add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, 0); add_dyn_dbg("debug_keys", B43_DBG_KEYS, 0); + add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, 0); #undef add_dyn_dbg } diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h index e47b4b488b0..822aad8842f 100644 --- a/drivers/net/wireless/b43/debugfs.h +++ b/drivers/net/wireless/b43/debugfs.h @@ -13,6 +13,7 @@ enum b43_dyndbg { /* Dynamic debugging features */ B43_DBG_LO, B43_DBG_FIRMWARE, B43_DBG_KEYS, + B43_DBG_VERBOSESTATS, __B43_NR_DYNDBG, }; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 7d22dadfd89..6468bbeba68 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -1830,6 +1830,16 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev) /* Re-enable interrupts on the device by restoring the current interrupt mask. */ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); + +#if B43_DEBUG + if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { + dev->irq_count++; + for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { + if (reason & (1 << i)) + dev->irq_bit_count[i]++; + } + } +#endif } /* Interrupt thread handler. Handles device interrupts in thread context. */ @@ -2893,6 +2903,27 @@ static void b43_periodic_every15sec(struct b43_wldev *dev) atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); wmb(); + +#if B43_DEBUG + if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { + unsigned int i; + + b43dbg(dev->wl, "Stats: %7u IRQs/sec, %7u TX/sec, %7u RX/sec\n", + dev->irq_count / 15, + dev->tx_count / 15, + dev->rx_count / 15); + dev->irq_count = 0; + dev->tx_count = 0; + dev->rx_count = 0; + for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { + if (dev->irq_bit_count[i]) { + b43dbg(dev->wl, "Stats: %7u IRQ-%02u/sec (0x%08X)\n", + dev->irq_bit_count[i] / 15, i, (1 << i)); + dev->irq_bit_count[i] = 0; + } + } + } +#endif } static void do_periodic_work(struct b43_wldev *dev) @@ -3092,6 +3123,9 @@ static void b43_tx_work(struct work_struct *work) dev_kfree_skb(skb); /* Drop it */ } +#if B43_DEBUG + dev->tx_count++; +#endif mutex_unlock(&wl->mutex); } diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index c403c616d3b..ac9f600995e 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -692,6 +692,9 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); ieee80211_rx(dev->wl->hw, skb); +#if B43_DEBUG + dev->rx_count++; +#endif return; drop: b43dbg(dev->wl, "RX: Packet dropped\n"); -- cgit v1.2.3 From 09ebe2f94373e95952ed6340b0a5d21739eb99da Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sat, 12 Sep 2009 00:52:48 +0200 Subject: b43: Disable PMQ mechanism This reduces IRQ pressure by about one third on a saturated link by disabling the PMQ mechanism. We currently don't use that mechanism. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 6468bbeba68..7fa947ca7c3 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2675,6 +2675,20 @@ static void b43_adjust_opmode(struct b43_wldev *dev) cfp_pretbtt = 50; } b43_write16(dev, 0x612, cfp_pretbtt); + + /* FIXME: We don't currently implement the PMQ mechanism, + * so always disable it. If we want to implement PMQ, + * we need to enable it here (clear DISCPMQ) in AP mode. + */ + if (0 /* ctl & B43_MACCTL_AP */) { + b43_write32(dev, B43_MMIO_MACCTL, + b43_read32(dev, B43_MMIO_MACCTL) + & ~B43_MACCTL_DISCPMQ); + } else { + b43_write32(dev, B43_MMIO_MACCTL, + b43_read32(dev, B43_MMIO_MACCTL) + | B43_MACCTL_DISCPMQ); + } } static void b43_rate_memory_write(struct b43_wldev *dev, u16 rate, int is_ofdm) -- cgit v1.2.3 From 9308779aad721cab8434fd5f98d3c85d3716fdec Mon Sep 17 00:00:00 2001 From: Thomas Ilnseher Date: Mon, 14 Sep 2009 23:01:33 +0200 Subject: b43: Add LP PHY Analog Switch Support The current verison of b43 uses "b43_phyop_switch_analog_generic" for A, G and LP phys. According to the spec, this is the wrong behaviour for the LP PHY (see: http://bcm-v4.sipsolutions.net/802.11/PHY/Anacore ) While no problems on the x86 plattform where seen, this leads to a crash on the BCM5354 SoC (MIPS 32 LE plattform). This patch implements the analog switch for LP PHYs according to the specs. It fixes the crash signed-off-by: Thomas Ilnseher Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_lp.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 3e02d969f68..1e318d815a5 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -2228,6 +2228,16 @@ static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev, return B43_TXPWR_RES_DONE; } +void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on) +{ + if (on) { + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xfff8); + } else { + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0x0007); + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x0007); + } +} + const struct b43_phy_operations b43_phyops_lp = { .allocate = b43_lpphy_op_allocate, .free = b43_lpphy_op_free, @@ -2239,7 +2249,7 @@ const struct b43_phy_operations b43_phyops_lp = { .radio_read = b43_lpphy_op_radio_read, .radio_write = b43_lpphy_op_radio_write, .software_rfkill = b43_lpphy_op_software_rfkill, - .switch_analog = b43_phyop_switch_analog_generic, + .switch_analog = b43_lpphy_op_switch_analog, .switch_channel = b43_lpphy_op_switch_channel, .get_default_chan = b43_lpphy_op_get_default_chan, .set_rx_antenna = b43_lpphy_op_set_rx_antenna, -- cgit v1.2.3 From e899a3f232125c393cdf7f7bf6533501ef8808fb Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Mon, 14 Sep 2009 23:22:08 +0200 Subject: b43: Don't abuse wl->current_dev in the led work Don't abuse wl->current_dev in the LED work for checking whether we're going down. Add an explicit variable. This fixes a crash on rmmod dereferencing the wl->current_dev NULL pointer in various other places of the driver. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/leds.c | 7 +++---- drivers/net/wireless/b43/leds.h | 1 + drivers/net/wireless/b43/main.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index ac5a322c84b..fbe31e02688 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -112,10 +112,7 @@ static void b43_led_brightness_set(struct led_classdev *led_dev, struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); struct b43_wl *wl = led->wl; - /* The check for current_dev is only needed while unregistering, - * so it is sequencial and does not race. But we must not dereference - * current_dev here. */ - if (likely(wl->current_dev)) { + if (likely(!wl->leds.stop)) { atomic_set(&led->state, brightness); ieee80211_queue_work(wl->hw, &wl->leds.work); } @@ -314,6 +311,8 @@ void b43_leds_init(struct b43_wldev *dev) break; } } + + dev->wl->leds.stop = 0; } void b43_leds_exit(struct b43_wldev *dev) diff --git a/drivers/net/wireless/b43/leds.h b/drivers/net/wireless/b43/leds.h index c4a58a0fc1d..49d3da540c2 100644 --- a/drivers/net/wireless/b43/leds.h +++ b/drivers/net/wireless/b43/leds.h @@ -35,6 +35,7 @@ struct b43_leds { struct b43_led led_radio; struct b43_led led_assoc; + bool stop; struct work_struct work; }; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 7fa947ca7c3..a8840ef6377 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4987,7 +4987,7 @@ static void b43_remove(struct ssb_device *dev) * might have modified it. Restoring is important, so the networking * stack can properly free resources. */ wl->hw->queues = wl->mac80211_initially_registered_queues; - wl->current_dev = NULL; + wl->leds.stop = 1; cancel_work_sync(&wl->leds.work); ieee80211_unregister_hw(wl->hw); } -- cgit v1.2.3 From 82905ace22614fe220875fc5748b1afbac868a76 Mon Sep 17 00:00:00 2001 From: Albert Herranz Date: Wed, 16 Sep 2009 00:26:19 +0200 Subject: b43: fix build error if !CONFIG_B43_LEDS Fix the following build error when CONFIG_B43_LEDS is not selected: drivers/net/wireless/b43/main.c: In function 'b43_remove': drivers/net/wireless/b43/main.c:4990: error: 'struct b43_leds' has no member named 'stop' drivers/net/wireless/b43/main.c:4991: error: 'struct b43_leds' has no member named 'work' make[4]: *** [drivers/net/wireless/b43/main.o] Error 1 Signed-off-by: Albert Herranz Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/leds.c | 8 ++++++++ drivers/net/wireless/b43/leds.h | 4 ++++ drivers/net/wireless/b43/main.c | 3 +-- 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index fbe31e02688..fbe3d4f62ce 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -325,6 +325,14 @@ void b43_leds_exit(struct b43_wldev *dev) b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow); } +void b43_leds_stop(struct b43_wldev *dev) +{ + struct b43_leds *leds = &dev->wl->leds; + + leds->stop = 1; + cancel_work_sync(&leds->work); +} + void b43_leds_register(struct b43_wldev *dev) { unsigned int i; diff --git a/drivers/net/wireless/b43/leds.h b/drivers/net/wireless/b43/leds.h index 49d3da540c2..9592e4c5a5f 100644 --- a/drivers/net/wireless/b43/leds.h +++ b/drivers/net/wireless/b43/leds.h @@ -63,6 +63,7 @@ void b43_leds_register(struct b43_wldev *dev); void b43_leds_unregister(struct b43_wldev *dev); void b43_leds_init(struct b43_wldev *dev); void b43_leds_exit(struct b43_wldev *dev); +void b43_leds_stop(struct b43_wldev *dev); #else /* CONFIG_B43_LEDS */ @@ -84,6 +85,9 @@ static inline void b43_leds_init(struct b43_wldev *dev) static inline void b43_leds_exit(struct b43_wldev *dev) { } +static inline void b43_leds_stop(struct b43_wldev *dev) +{ +} #endif /* CONFIG_B43_LEDS */ #endif /* B43_LEDS_H_ */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index a8840ef6377..6c0ea214a82 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4987,8 +4987,7 @@ static void b43_remove(struct ssb_device *dev) * might have modified it. Restoring is important, so the networking * stack can properly free resources. */ wl->hw->queues = wl->mac80211_initially_registered_queues; - wl->leds.stop = 1; - cancel_work_sync(&wl->leds.work); + b43_leds_stop(wldev); ieee80211_unregister_hw(wl->hw); } -- cgit v1.2.3 From 4963cceea830f6afa65f0b921cfaff6765ac7748 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Wed, 16 Sep 2009 01:13:57 +0200 Subject: b43: Remove BROKEN attribute from SDIO SDIO works (more or less), so remove the BROKEN dependency and let people test it. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 4fc2ce429c7..54ea61c15d8 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -63,7 +63,7 @@ config B43_PCMCIA config B43_SDIO bool "Broadcom 43xx SDIO device support (EXPERIMENTAL)" - depends on B43 && SSB_SDIOHOST_POSSIBLE && EXPERIMENTAL && BROKEN + depends on B43 && SSB_SDIOHOST_POSSIBLE && EXPERIMENTAL select SSB_SDIOHOST ---help--- Broadcom 43xx device support for Soft-MAC SDIO devices. -- cgit v1.2.3 From 692e565e4b07ed872e6de5e1e3a6ae5b950e0369 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 21 Sep 2009 17:59:27 -0500 Subject: b43: Implement RFKILL status for LP PHY The LP (and probably N) PHY has the same radio disabled bit as the higher-revision A and G PHYs. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/b43/rfkill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c index 31e55999893..7a3218c5ba7 100644 --- a/drivers/net/wireless/b43/rfkill.c +++ b/drivers/net/wireless/b43/rfkill.c @@ -28,7 +28,7 @@ /* Returns TRUE, if the radio is enabled in hardware. */ bool b43_is_hw_radio_enabled(struct b43_wldev *dev) { - if (dev->phy.rev >= 3) { + if (dev->phy.rev >= 3 || dev->phy.type == B43_PHYTYPE_LP) { if (!(b43_read32(dev, B43_MMIO_RADIO_HWENABLED_HI) & B43_MMIO_RADIO_HWENABLED_HI_MASK)) return 1; -- cgit v1.2.3 From 6170cd5c72399f8536412ac75cba29cddc9919fb Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Thu, 17 Sep 2009 09:24:24 +0530 Subject: ath9k: Set default noise floor value for AR9287 The default noise floor was never initialized for AR9287.This patch helps in reporting the correct RSSI for this version of chipset. Signed-off-by: Vivek Natarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 2 ++ drivers/net/wireless/ath/ath9k/calib.h | 1 + 2 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 3234995e888..439f2c743d8 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -697,6 +697,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah) noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE; else if (AR_SREV_9285(ah)) noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE; + else if (AR_SREV_9287(ah)) + noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE; else noise_floor = AR_PHY_CCA_MAX_AR5416_GOOD_VALUE; diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 019bcbba40e..9028ab193e4 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -28,6 +28,7 @@ extern const struct ath9k_percal_data adc_init_dc_cal; #define AR_PHY_CCA_MAX_AR5416_GOOD_VALUE -85 #define AR_PHY_CCA_MAX_AR9280_GOOD_VALUE -112 #define AR_PHY_CCA_MAX_AR9285_GOOD_VALUE -118 +#define AR_PHY_CCA_MAX_AR9287_GOOD_VALUE -118 #define AR_PHY_CCA_MAX_HIGH_VALUE -62 #define AR_PHY_CCA_MIN_BAD_VALUE -140 #define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 -- cgit v1.2.3 From 93b1b37f6a4de8bce17f55d9cfa10ef5c8c04e8a Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Thu, 17 Sep 2009 09:24:58 +0530 Subject: ath9k: Revamp PCIE workarounds * Disable L1 state ONLY when device is in D3 mode. * Clear bit 22 of register 0x4004. * Handle power on/off properly Not setting the workarounds properly resulted in the disappearance of the card in certain cases. Signed-off-by: Vivek Natarajan Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 162 ++++++++++++++++++++-------------- drivers/net/wireless/ath/ath9k/hw.h | 2 +- drivers/net/wireless/ath/ath9k/main.c | 8 +- drivers/net/wireless/ath/ath9k/reg.h | 3 +- 4 files changed, 103 insertions(+), 72 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index b6c6cca0781..82a24408528 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -965,7 +965,7 @@ int ath9k_hw_init(struct ath_hw *ah) ath9k_hw_init_mode_regs(ah); if (ah->is_pciexpress) - ath9k_hw_configpcipowersave(ah, 0); + ath9k_hw_configpcipowersave(ah, 0, 0); else ath9k_hw_disablepcie(ah); @@ -3005,9 +3005,10 @@ void ath9k_ps_restore(struct ath_softc *sc) * Programming the SerDes must go through the same 288 bit serial shift * register as the other analog registers. Hence the 9 writes. */ -void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore) +void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off) { u8 i; + u32 val; if (ah->is_pciexpress != true) return; @@ -3017,84 +3018,113 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore) return; /* Nothing to do on restore for 11N */ - if (restore) - return; - - if (AR_SREV_9280_20_OR_LATER(ah)) { - /* - * AR9280 2.0 or later chips use SerDes values from the - * initvals.h initialized depending on chipset during - * ath9k_hw_init() - */ - for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) { - REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0), - INI_RA(&ah->iniPcieSerdes, i, 1)); - } - } else if (AR_SREV_9280(ah) && - (ah->hw_version.macRev == AR_SREV_REVISION_9280_10)) { - REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00); - REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + if (!restore) { + if (AR_SREV_9280_20_OR_LATER(ah)) { + /* + * AR9280 2.0 or later chips use SerDes values from the + * initvals.h initialized depending on chipset during + * ath9k_hw_init() + */ + for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) { + REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0), + INI_RA(&ah->iniPcieSerdes, i, 1)); + } + } else if (AR_SREV_9280(ah) && + (ah->hw_version.macRev == AR_SREV_REVISION_9280_10)) { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + + /* RX shut off when elecidle is asserted */ + REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019); + REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560); + + /* Shut off CLKREQ active in L1 */ + if (ah->config.pcie_clock_req) + REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc); + else + REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd); - /* RX shut off when elecidle is asserted */ - REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019); - REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820); - REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560); + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007); - /* Shut off CLKREQ active in L1 */ - if (ah->config.pcie_clock_req) - REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc); - else - REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd); + /* Load the new settings */ + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); - REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); - REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); - REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007); + } else { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); - /* Load the new settings */ - REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + /* RX shut off when elecidle is asserted */ + REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); + REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); - } else { - REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); - REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + /* + * Ignore ah->ah_config.pcie_clock_req setting for + * pre-AR9280 11n + */ + REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); - /* RX shut off when elecidle is asserted */ - REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); - REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); - REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); - /* - * Ignore ah->ah_config.pcie_clock_req setting for - * pre-AR9280 11n - */ - REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); + /* Load the new settings */ + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + } - REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); - REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); - REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); + udelay(1000); - /* Load the new settings */ - REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); - } + /* set bit 19 to allow forcing of pcie core into L1 state */ + REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); - udelay(1000); + /* Several PCIe massages to ensure proper behaviour */ + if (ah->config.pcie_waen) { + val = ah->config.pcie_waen; + if (!power_off) + val &= (~AR_WA_D3_L1_DISABLE); + } else { + if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || + AR_SREV_9287(ah)) { + val = AR9285_WA_DEFAULT; + if (!power_off) + val &= (~AR_WA_D3_L1_DISABLE); + } else if (AR_SREV_9280(ah)) { + /* + * On AR9280 chips bit 22 of 0x4004 needs to be + * set otherwise card may disappear. + */ + val = AR9280_WA_DEFAULT; + if (!power_off) + val &= (~AR_WA_D3_L1_DISABLE); + } else + val = AR_WA_DEFAULT; + } - /* set bit 19 to allow forcing of pcie core into L1 state */ - REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + REG_WRITE(ah, AR_WA, val); + } - /* Several PCIe massages to ensure proper behaviour */ - if (ah->config.pcie_waen) { - REG_WRITE(ah, AR_WA, ah->config.pcie_waen); - } else { - if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) - REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); + if (power_off) { /* - * On AR9280 chips bit 22 of 0x4004 needs to be set to - * otherwise card may disappear. + * Set PCIe workaround bits + * bit 14 in WA register (disable L1) should only + * be set when device enters D3 and be cleared + * when device comes back to D0. */ - else if (AR_SREV_9280(ah)) - REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT); - else - REG_WRITE(ah, AR_WA, AR_WA_DEFAULT); + if (ah->config.pcie_waen) { + if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE) + REG_SET_BIT(ah, AR_WA, AR_WA_D3_L1_DISABLE); + } else { + if (((AR_SREV_9285(ah) || AR_SREV_9271(ah) || + AR_SREV_9287(ah)) && + (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)) || + (AR_SREV_9280(ah) && + (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE))) { + REG_SET_BIT(ah, AR_WA, AR_WA_D3_L1_DISABLE); + } + } } } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 9106a0b537d..75c3041aadc 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -650,7 +650,7 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs); bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode); -void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore); +void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off); /* Interrupt Handling */ bool ath9k_hw_intrpend(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3dc7b5a13e6..5055f1842b8 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1131,7 +1131,7 @@ void ath_radio_enable(struct ath_softc *sc) int r; ath9k_ps_wakeup(sc); - ath9k_hw_configpcipowersave(ah, 0); + ath9k_hw_configpcipowersave(ah, 0, 0); if (!ah->curchan) ah->curchan = ath_get_curchannel(sc, sc->hw); @@ -1202,7 +1202,7 @@ void ath_radio_disable(struct ath_softc *sc) spin_unlock_bh(&sc->sc_resetlock); ath9k_hw_phy_disable(ah); - ath9k_hw_configpcipowersave(ah, 1); + ath9k_hw_configpcipowersave(ah, 1, 1); ath9k_ps_restore(sc); ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); } @@ -1942,7 +1942,7 @@ static int ath9k_start(struct ieee80211_hw *hw) init_channel = ath_get_curchannel(sc, hw); /* Reset SERDES registers */ - ath9k_hw_configpcipowersave(sc->sc_ah, 0); + ath9k_hw_configpcipowersave(sc->sc_ah, 0, 0); /* * The basic interface to setting the hardware in a good @@ -2170,7 +2170,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) /* disable HAL and put h/w to sleep */ ath9k_hw_disable(sc->sc_ah); - ath9k_hw_configpcipowersave(sc->sc_ah, 1); + ath9k_hw_configpcipowersave(sc->sc_ah, 1, 1); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); sc->sc_flags |= SC_OP_INVALID; diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index e5c29eb86e8..d83b77f821e 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -676,8 +676,9 @@ #define AR_RC_HOSTIF 0x00000100 #define AR_WA 0x4004 +#define AR_WA_D3_L1_DISABLE (1 << 14) #define AR9285_WA_DEFAULT 0x004a05cb -#define AR9280_WA_DEFAULT 0x0040073f +#define AR9280_WA_DEFAULT 0x0040073b #define AR_WA_DEFAULT 0x0000073f -- cgit v1.2.3 From 46fe782c97f5e54a02485ad97b2e6256386a8c8f Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 17 Sep 2009 09:25:25 +0530 Subject: ath9k: Restore TSF after RESET For chips requiring RTC reset, TSF has to be restored after power on reset. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 82a24408528..af5bb50a2e8 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2338,6 +2338,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_channel *curchan = ah->curchan; u32 saveDefAntenna; u32 macStaId1; + u64 tsf = 0; int i, rx_chainmask, r; ah->extprotspacing = sc->ht_extprotspacing; @@ -2372,6 +2373,10 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; + /* For chips on which RTC reset is done, save TSF before it gets cleared */ + if (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) + tsf = ath9k_hw_gettsf64(ah); + saveLedState = REG_READ(ah, AR_CFG_LED) & (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW); @@ -2398,6 +2403,10 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, udelay(50); } + /* Restore TSF */ + if (tsf && AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) + ath9k_hw_settsf64(ah, tsf); + if (AR_SREV_9280_10_OR_LATER(ah)) REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); -- cgit v1.2.3 From d8caa83967d62c05f406d34abaaf2e1baa172c6c Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 17 Sep 2009 09:25:45 +0530 Subject: ath9k: Fix chip wakeup issue Waking up the chip after powering it down fails sometimes. In this case the CPU is locked for 200ms. Reduce this interval to 10ms to avoid excessive busy looping. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 75c3041aadc..b8923457182 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -106,7 +106,7 @@ #define AH_TSF_WRITE_TIMEOUT 100 /* (us) */ #define AH_TIME_QUANTUM 10 #define AR_KEYTABLE_SIZE 128 -#define POWER_UP_TIME 200000 +#define POWER_UP_TIME 10000 #define SPUR_RSSI_THRESH 40 #define CAB_TIMEOUT_VAL 10 -- cgit v1.2.3 From 204d794086f26a7b191d9a9c3483af8b81362b16 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 17 Sep 2009 09:26:14 +0530 Subject: ath9k: Fix rx data corruption Setting bit 20 and 25 of 0x8344 can cause occasional rx data corruption, clear them to fix this issue. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index af5bb50a2e8..4731ad2d999 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1273,6 +1273,15 @@ static void ath9k_hw_override_ini(struct ath_hw *ah, */ REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + if (AR_SREV_9280_10_OR_LATER(ah)) { + val = REG_READ(ah, AR_PCU_MISC_MODE2) & + (~AR_PCU_MISC_MODE2_HWWAR1); + + if (AR_SREV_9287_10_OR_LATER(ah)) + val = val & (~AR_PCU_MISC_MODE2_HWWAR2); + + REG_WRITE(ah, AR_PCU_MISC_MODE2, val); + } if (!AR_SREV_5416_20_OR_LATER(ah) || AR_SREV_9280_10_OR_LATER(ah)) -- cgit v1.2.3 From 9ebef7997d0c131a8d53c045bff68becddd58124 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 17 Sep 2009 09:26:44 +0530 Subject: ath9k: Don't read NF when chip has gone through full sleep mode NF value may be incorrect when we read it just after the chip has gone through a full sleep mode. Reading incorrect NF values affects RX throughput. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 4731ad2d999..2c904c6e209 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2357,7 +2357,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; - if (curchan) + if (curchan && !ah->chip_fullsleep) ath9k_hw_getnf(ah, curchan); if (bChannelChange && -- cgit v1.2.3 From 0a475cc6a0ac8e38ac3587cff87026551800b7fe Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Thu, 17 Sep 2009 09:27:10 +0530 Subject: ath9k: Do a full reset for AR9280 AR9280 requires a full reset during channel change and HW reset. Currently, a fast channel change is done. This patch fixes this bug. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 2c904c6e209..323df96dad0 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2366,8 +2366,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, (chan->channel != ah->curchan->channel) && ((chan->channelFlags & CHANNEL_ALL) == (ah->curchan->channelFlags & CHANNEL_ALL)) && - (!AR_SREV_9280(ah) || (!IS_CHAN_A_5MHZ_SPACED(chan) && - !IS_CHAN_A_5MHZ_SPACED(ah->curchan)))) { + !(AR_SREV_9280(ah) || IS_CHAN_A_5MHZ_SPACED(chan) || + IS_CHAN_A_5MHZ_SPACED(ah->curchan))) { if (ath9k_hw_channel_change(ah, chan, sc->tx_chan_width)) { ath9k_hw_loadnf(ah, ah->curchan); -- cgit v1.2.3 From ce143bb04491a586ec95798711dee7fd43fbe042 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Thu, 17 Sep 2009 09:27:33 +0530 Subject: ath9k: Adjust the chainmasks properly This is needed to account for the number of chains in use, not just the number of chains present. Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 439f2c743d8..22a3a690386 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -609,14 +609,24 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) AR_PHY_CH1_EXT_CCA, AR_PHY_CH2_EXT_CCA }; - u8 chainmask; + u8 chainmask, rx_chain_status; + rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK); if (AR_SREV_9285(ah)) chainmask = 0x9; - else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) - chainmask = 0x1B; - else - chainmask = 0x3F; + else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) { + if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4)) + chainmask = 0x1B; + else + chainmask = 0x09; + } else { + if (rx_chain_status & 0x4) + chainmask = 0x3F; + else if (rx_chain_status & 0x2) + chainmask = 0x1B; + else + chainmask = 0x09; + } h = ah->nfCalHist; -- cgit v1.2.3 From 42abfbee23bf86e2c74d569b7ddd040d29782c5a Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Thu, 17 Sep 2009 09:27:59 +0530 Subject: ath9k: Fix AHB reset for AR9280 The commit "ath9k: Do an AHB reset before doing RTC reset" fixed RTC reset issue for AR9280 2.0 chipsets and above. The fix is valid for all AR9280 chipsets. Signed-off-by: Vivek Natarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 323df96dad0..814682618b8 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1793,7 +1793,7 @@ static void ath9k_hw_set_regs(struct ath_hw *ah, struct ath9k_channel *chan, static bool ath9k_hw_chip_reset(struct ath_hw *ah, struct ath9k_channel *chan) { - if (OLC_FOR_AR9280_20_LATER) { + if (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) { if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) return false; } else if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM)) -- cgit v1.2.3 From d865ca6c147552a1c38161e2e262c4ab59e762d6 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Thu, 17 Sep 2009 09:28:21 +0530 Subject: ath9k: Fix bug in chain handling * This patch fixes a bug in calculating the scaled power for three chain chipsets. * Also, a delay is needed after setting DAC low-power mode in TOP1 RF register (Top Level Register Bits). Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/eeprom_def.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index ae7fb5dcb26..4071fc91da0 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -509,6 +509,8 @@ static void ath9k_hw_def_set_board_values(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, eep->baseEepHeader.dacLpMode); + udelay(100); + REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, pModal->miscBits >> 2); @@ -902,7 +904,7 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, u16 powerLimit) { #define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ -#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */ +#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 9 /* 10*log10(3)*2 */ struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; -- cgit v1.2.3 From edbf51f65eefb952a03c00eadefa9aa48580fcc6 Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 17 Sep 2009 09:28:41 +0530 Subject: ath9k: Fix regression in PA calibration The commit "ath9k: Fix bugs in programming registers during PA CAL" removed a REG_READ of 0x7834. This resulted in incorrect computation of the subsequent value to be written in RF2G6. This patch fixes the regression by re-adding the REG_READ. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 22a3a690386..0ad6d0b76e9 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -936,6 +936,7 @@ static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah, bool is_reset) regVal |= (1 << (19 + i)); REG_WRITE(ah, 0x7834, regVal); udelay(1); + regVal = REG_READ(ah, 0x7834); regVal &= (~(0x1 << (19 + i))); reg_field = MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9); regVal |= (reg_field << (19 + i)); -- cgit v1.2.3 From a3ca95fbe488b9cc6edd4903c609b19ba0365efc Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Thu, 17 Sep 2009 09:29:07 +0530 Subject: ath9k: Disable autosleep feature by default. Autosleep needs to be disabled for AR9287 chipsets also. Since autosleep is not used for any of the currently supported chipsets, disable it by default and can be enabled if needed for any of the future chipsets. Signed-off-by: Vivek Natarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 814682618b8..84abf3066cf 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -3700,15 +3700,7 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) } #endif - if ((ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || - (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE) || - (ah->hw_version.macVersion == AR_SREV_VERSION_9160) || - (ah->hw_version.macVersion == AR_SREV_VERSION_9100) || - (ah->hw_version.macVersion == AR_SREV_VERSION_9280) || - (ah->hw_version.macVersion == AR_SREV_VERSION_9285)) - pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP; - else - pCap->hw_caps |= ATH9K_HW_CAP_AUTOSLEEP; + pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP; if (AR_SREV_9280(ah) || AR_SREV_9285(ah)) pCap->hw_caps &= ~ATH9K_HW_CAP_4KB_SPLITTRANS; -- cgit v1.2.3 From fe9f6342c86292aee1941447f22dc5470735e5a1 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Thu, 17 Sep 2009 13:46:53 +0200 Subject: ar9170usb: add usbid for TP-Link TL-WN821N v2 This patch adds the usbid for TP-Link TL-WN821N v2. Cc: stable@kernel.org Reported-by: Fabian Lenz Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar9170/usb.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c index e0138ac8bf5..e974e5829e1 100644 --- a/drivers/net/wireless/ath/ar9170/usb.c +++ b/drivers/net/wireless/ath/ar9170/usb.c @@ -64,6 +64,8 @@ static struct usb_device_id ar9170_usb_ids[] = { { USB_DEVICE(0x0cf3, 0x9170) }, /* Atheros TG121N */ { USB_DEVICE(0x0cf3, 0x1001) }, + /* TP-Link TL-WN821N v2 */ + { USB_DEVICE(0x0cf3, 0x1002) }, /* Cace Airpcap NX */ { USB_DEVICE(0xcace, 0x0300) }, /* D-Link DWA 160A */ -- cgit v1.2.3 From bba98871c6c5f1f086086ccf13836a02e0f27e77 Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Thu, 17 Sep 2009 10:43:55 -0700 Subject: iwlwifi: do not send sync command while holding spinlock commit 10c994ca70e8e94bbc85a5bf13de5911ee8de4d2 "iwlwifi: fix remove key error" fixed an error reported by mac80211 during interface down. The fix involved changing an async command to synchronous. Unfortunately this was inside a spinlock section in which we cannot sleep. Modify the sending of the command back to async. This causes the mac80211 error "mac80211-phy0: failed to remove key (0, ff:ff:ff:ff:ff:ff) from hardware (-16)." to return. This error is not serious since this occurs during interface down and the keys will be cleared anyway when ucode is unloaded. Having this error message is thus less serious than a potential deadlock introduced when sleeping while holding a spinlock. We will have to find another fix for that error. This is a revert of the abovementioned commit. Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-sta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index a2b9ec82b96..c6633fec821 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -520,7 +520,7 @@ int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty) struct iwl_host_cmd cmd = { .id = REPLY_WEPKEY, .data = wep_cmd, - .flags = CMD_SYNC, + .flags = CMD_ASYNC, }; memset(wep_cmd, 0, cmd_size + -- cgit v1.2.3 From f82a924cc88a5541df1d4b9d38a0968cd077a051 Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Thu, 17 Sep 2009 10:43:56 -0700 Subject: iwlwifi: reduce noise when skb allocation fails Replenishment of receive buffers is done in the tasklet handling received frames as well as in a workqueue. When we are in the tasklet we cannot sleep and thus attempt atomic skb allocations. It is generally not a big problem if this fails since iwl_rx_allocate is always followed by a call to iwl_rx_queue_restock which will queue the work to replenish the buffers at a time when sleeping is allowed. We thus add the __GFP_NOWARN to the skb allocation in iwl_rx_allocate to reduce the noise if such an allocation fails while we still have enough buffers. We do maintain the warning and the error message when we are low on buffers to communicate to the user that there is a potential problem with memory availability on system This addresses issue reported upstream in thread "iwlagn: order 2 page allocation failures" in http://thread.gmane.org/gmane.linux.kernel.wireless.general/39187 Signed-off-by: Reinette Chatre Acked-by: Mel Gorman Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-rx.c | 10 +++++++++- drivers/net/wireless/iwlwifi/iwl3945-base.c | 9 ++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index b90adcb73b0..8e1bb53c0aa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -250,12 +250,20 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority) } spin_unlock_irqrestore(&rxq->lock, flags); + if (rxq->free_count > RX_LOW_WATERMARK) + priority |= __GFP_NOWARN; /* Alloc a new receive buffer */ skb = alloc_skb(priv->hw_params.rx_buf_size + 256, priority); if (!skb) { - IWL_CRIT(priv, "Can not allocate SKB buffers\n"); + if (net_ratelimit()) + IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n"); + if ((rxq->free_count <= RX_LOW_WATERMARK) && + net_ratelimit()) + IWL_CRIT(priv, "Failed to allocate SKB buffer with %s. Only %u free buffers remaining.\n", + priority == GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", + rxq->free_count); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 090966837f3..4f2d4393728 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -1146,11 +1146,18 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority) } spin_unlock_irqrestore(&rxq->lock, flags); + if (rxq->free_count > RX_LOW_WATERMARK) + priority |= __GFP_NOWARN; /* Alloc a new receive buffer */ skb = alloc_skb(priv->hw_params.rx_buf_size, priority); if (!skb) { if (net_ratelimit()) - IWL_CRIT(priv, ": Can not allocate SKB buffers\n"); + IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n"); + if ((rxq->free_count <= RX_LOW_WATERMARK) && + net_ratelimit()) + IWL_CRIT(priv, "Failed to allocate SKB buffer with %s. Only %u free buffers remaining.\n", + priority == GFP_ATOMIC ? "GFP_ATOMIC" : "GFP_KERNEL", + rxq->free_count); /* We don't reschedule replenish work here -- we will * call the restock method and if it still needs * more buffers it will schedule replenish */ -- cgit v1.2.3 From fe2475633676b0a976400dfc53f8d7006f56543e Mon Sep 17 00:00:00 2001 From: Andrew Price Date: Thu, 17 Sep 2009 21:15:48 +0100 Subject: rt2x00: fix the definition of rt2x00crypto_rx_insert_iv Remove the redundant l2pad parameter from the definition of rt2x00crypto_rx_insert_iv which is used when only CONFIG_RT2500PCI but none of the other rt2x00 family drivers is configured. Signed-off-by: Andrew Price Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00lib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index 5462cb5ad99..567f029a8cd 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h @@ -380,7 +380,7 @@ static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, { } -static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, bool l2pad, +static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, unsigned int header_length, struct rxdone_entry_desc *rxdesc) { -- cgit v1.2.3 From 018ae37292dd391494172495142b87e816a841c0 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 17 Sep 2009 14:17:56 -0700 Subject: wl12xx: fix kconfig/link errors "boolean" converts a module dependency (MAC80211=m) to YES, then the WL12XX driver can be built-in instead of only modular, which leads to linker errors: wl1271_main.c:(.text+0x11177d): undefined reference to `ieee80211_frequency_to_channel' wl1271_main.c:(.text+0x111adc): undefined reference to `ieee80211_stop_queues' wl1271_main.c:(.text+0x112005): undefined reference to `ieee80211_scan_completed' (.text+0x1139c8): undefined reference to `ieee80211_scan_completed' (.text+0x113bb0): undefined reference to `ieee80211_tx_status' (.text+0x113e53): undefined reference to `ieee80211_stop_queues' (.text+0x113e8d): undefined reference to `ieee80211_wake_queues' (.text+0x113f3b): undefined reference to `ieee80211_tx_status' (.text+0x113f60): undefined reference to `ieee80211_tx_status' (.text+0x1140f0): undefined reference to `ieee80211_channel_to_frequency' (.text+0x114153): undefined reference to `ieee80211_rx' wl1271_main.c:(.devinit.text+0xca08): undefined reference to `ieee80211_alloc_hw' wl1271_main.c:(.devinit.text+0xccf5): undefined reference to `ieee80211_register_hw' wl1271_main.c:(.devinit.text+0xcd6b): undefined reference to `ieee80211_free_hw' wl1271_main.c:(.devexit.text+0x1353): undefined reference to `ieee80211_unregister_hw' wl1271_main.c:(.devexit.text+0x13c9): undefined reference to `ieee80211_free_hw' Signed-off-by: Randy Dunlap Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 7b14d5bc63d..88060e11754 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -1,5 +1,5 @@ menuconfig WL12XX - boolean "TI wl12xx driver support" + tristate "TI wl12xx driver support" depends on MAC80211 && WLAN_80211 && EXPERIMENTAL ---help--- This will enable TI wl12xx driver support. The drivers make -- cgit v1.2.3 From 33e2fb2f2b5d3b2f491fccab0ead256e34447cc6 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 22 Sep 2009 13:45:16 +0200 Subject: drivers/net/wireless: Use usb_endpoint_dir_out Use the usb_endpoint_dir_out API function. Note that the use of USB_TYPE_MASK in the original code is incorrect; it results in a test that is always false. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ struct usb_endpoint_descriptor *endpoint; expression E; @@ - (endpoint->bEndpointAddress & E) == USB_DIR_OUT + usb_endpoint_dir_out(endpoint) // Signed-off-by: Julia Lawall Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 38688847d56..23a6a6d4863 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -1070,7 +1070,7 @@ static int eject_installer(struct usb_interface *intf) /* Find bulk out endpoint */ endpoint = &iface_desc->endpoint[1].desc; - if ((endpoint->bEndpointAddress & USB_TYPE_MASK) == USB_DIR_OUT && + if (usb_endpoint_dir_out(endpoint) && usb_endpoint_xfer_bulk(endpoint)) { bulk_out_ep = endpoint->bEndpointAddress; } else { -- cgit v1.2.3 From e31f7b96f0252e8da06df1bc7fd0f6dbc8cbec42 Mon Sep 17 00:00:00 2001 From: Sujith Date: Wed, 23 Sep 2009 13:49:12 +0530 Subject: ath9k: Fix RFKILL bugs This patch fixes 2 issues in RFKILL: * Calling wiphy_rfkill_stop_polling() in ath9k_stop would mean that the driver cannot report HW status when the radio is re-enabled. Move this to ath_detach(). * Calling ath_radio_{enable/disable} without checking the current state results in ath_radio_enable() being called repeatedly for every invocation of rfkill_poll(). This is not needed in any case since wiphy_rfkill_set_hw_state() would call ->stop() if the radio has been disabled. Signed-off-by: Sujith Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5055f1842b8..52bed89063d 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1226,11 +1226,6 @@ static void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) bool blocked = !!ath_is_rfkill_set(sc); wiphy_rfkill_set_hw_state(hw->wiphy, blocked); - - if (blocked) - ath_radio_disable(sc); - else - ath_radio_enable(sc); } static void ath_start_rfkill_poll(struct ath_softc *sc) @@ -1260,6 +1255,7 @@ void ath_detach(struct ath_softc *sc) DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n"); ath_deinit_leds(sc); + wiphy_rfkill_stop_polling(sc->hw->wiphy); for (i = 0; i < sc->num_sec_wiphy; i++) { struct ath_wiphy *aphy = sc->sec_wiphy[i]; @@ -2166,8 +2162,6 @@ static void ath9k_stop(struct ieee80211_hw *hw) } else sc->rx.rxlink = NULL; - wiphy_rfkill_stop_polling(sc->hw->wiphy); - /* disable HAL and put h/w to sleep */ ath9k_hw_disable(sc->sc_ah); ath9k_hw_configpcipowersave(sc->sc_ah, 1, 1); -- cgit v1.2.3 From 6c6a22e26868285dc3dac280e0e57de029bfae1b Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 23 Sep 2009 10:51:34 +0200 Subject: iwlagn: fix panic in iwl{5000,4965}_rx_reply_tx In some cases firmware can give us bad value of index in transmit buffers array. This patch add sanity check for such values and return from processing function instantly when it happens. https://bugzilla.redhat.com/show_bug.cgi?id=521931 Patch was tested by reporter on iwl5000. I think check can be also helpful for 4965. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 6 ++++++ drivers/net/wireless/iwlwifi/iwl-5000.c | 6 ++++++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index ca61d3796ce..3259b884154 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2021,6 +2021,12 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv, agg->frame_count, txq_id, idx); hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx); + if (!hdr) { + IWL_ERR(priv, + "BUG_ON idx doesn't point to valid skb" + " idx=%d, txq_id=%d\n", idx, txq_id); + return -1; + } sc = le16_to_cpu(hdr->seq_ctrl); if (idx != (SEQ_TO_SN(sc) & 0xff)) { diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 1d539e3b8db..a6391c7fea5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1163,6 +1163,12 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv, agg->frame_count, txq_id, idx); hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx); + if (!hdr) { + IWL_ERR(priv, + "BUG_ON idx doesn't point to valid skb" + " idx=%d, txq_id=%d\n", idx, txq_id); + return -1; + } sc = le16_to_cpu(hdr->seq_ctrl); if (idx != (SEQ_TO_SN(sc) & 0xff)) { -- cgit v1.2.3 From b37fa870c7ccb500c7bf6aabc72cefa757da9791 Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Wed, 23 Sep 2009 16:27:27 +0530 Subject: ath9k: Initialize txgain and rxgain for newer AR9287 chipsets. Signed-off-by: Vivek Natarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 84abf3066cf..ca7694caf36 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -842,7 +842,7 @@ static void ath9k_hw_init_mode_regs(struct ath_hw *ah) static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah) { - if (AR_SREV_9287_11(ah)) + if (AR_SREV_9287_11_OR_LATER(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9287Modes_rx_gain_9287_1_1, ARRAY_SIZE(ar9287Modes_rx_gain_9287_1_1), 6); @@ -853,7 +853,7 @@ static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah) else if (AR_SREV_9280_20(ah)) ath9k_hw_init_rxgain_ini(ah); - if (AR_SREV_9287_11(ah)) { + if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModesTxGain, ar9287Modes_tx_gain_9287_1_1, ARRAY_SIZE(ar9287Modes_tx_gain_9287_1_1), 6); -- cgit v1.2.3 From edf42a27e8e242e543716663e54aa2b592ea070a Mon Sep 17 00:00:00 2001 From: Sebastian Haas Date: Thu, 24 Sep 2009 03:55:05 +0000 Subject: ems_pci: fix size of CAN controllers BAR mapping for CPC-PCI v2 The driver mapped only 128 bytes of the CAN controller address space when a CPC-PCI v2 was detected (incl. CPC-104P). This patch will fix it by always mapping the whole address space (4096 bytes on all boards) of the corresponding PCI BAR. Signed-off-by: Sebastian Haas Signed-off-by: Wolfgang Grandegger Signed-off-by: David S. Miller --- drivers/net/can/sja1000/ems_pci.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c index 7d84b8ac9c1..fd04789d337 100644 --- a/drivers/net/can/sja1000/ems_pci.c +++ b/drivers/net/can/sja1000/ems_pci.c @@ -94,12 +94,14 @@ struct ems_pci_card { #define EMS_PCI_CDR (CDR_CBP | CDR_CLKOUT_MASK) #define EMS_PCI_V1_BASE_BAR 1 -#define EMS_PCI_V1_MEM_SIZE 4096 +#define EMS_PCI_V1_CONF_SIZE 4096 /* size of PITA control area */ #define EMS_PCI_V2_BASE_BAR 2 -#define EMS_PCI_V2_MEM_SIZE 128 +#define EMS_PCI_V2_CONF_SIZE 128 /* size of PLX control area */ #define EMS_PCI_CAN_BASE_OFFSET 0x400 /* offset where the controllers starts */ #define EMS_PCI_CAN_CTRL_SIZE 0x200 /* memory size for each controller */ +#define EMS_PCI_BASE_SIZE 4096 /* size of controller area */ + static struct pci_device_id ems_pci_tbl[] = { /* CPC-PCI v1 */ {PCI_VENDOR_ID_SIEMENS, 0x2104, PCI_ANY_ID, PCI_ANY_ID,}, @@ -224,7 +226,7 @@ static int __devinit ems_pci_add_card(struct pci_dev *pdev, struct sja1000_priv *priv; struct net_device *dev; struct ems_pci_card *card; - int max_chan, mem_size, base_bar; + int max_chan, conf_size, base_bar; int err, i; /* Enabling PCI device */ @@ -251,22 +253,22 @@ static int __devinit ems_pci_add_card(struct pci_dev *pdev, card->version = 2; /* CPC-PCI v2 */ max_chan = EMS_PCI_V2_MAX_CHAN; base_bar = EMS_PCI_V2_BASE_BAR; - mem_size = EMS_PCI_V2_MEM_SIZE; + conf_size = EMS_PCI_V2_CONF_SIZE; } else { card->version = 1; /* CPC-PCI v1 */ max_chan = EMS_PCI_V1_MAX_CHAN; base_bar = EMS_PCI_V1_BASE_BAR; - mem_size = EMS_PCI_V1_MEM_SIZE; + conf_size = EMS_PCI_V1_CONF_SIZE; } /* Remap configuration space and controller memory area */ - card->conf_addr = pci_iomap(pdev, 0, mem_size); + card->conf_addr = pci_iomap(pdev, 0, conf_size); if (card->conf_addr == NULL) { err = -ENOMEM; goto failure_cleanup; } - card->base_addr = pci_iomap(pdev, base_bar, mem_size); + card->base_addr = pci_iomap(pdev, base_bar, EMS_PCI_BASE_SIZE); if (card->base_addr == NULL) { err = -ENOMEM; goto failure_cleanup; -- cgit v1.2.3 From 704cc92e9ffe29458ea8831ae097c631b1160c01 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 24 Sep 2009 08:31:52 +0000 Subject: 3c59x: Get rid of "Trying to free already-free IRQ" Following trace pops up if we try to suspend with 3c59x ethernet NIC brought down: root@b1:~# ifconfig eth16 down root@b1:~# echo mem > /sys/power/state ... 3c59x 0000:00:10.0: suspend 3c59x 0000:00:10.0: PME# disabled Trying to free already-free IRQ 48 ------------[ cut here ]------------ Badness at c00554e4 [verbose debug info unavailable] NIP: c00554e4 LR: c00554e4 CTR: c019a098 REGS: c7975c60 TRAP: 0700 Not tainted (2.6.31-rc4) MSR: 00021032 CR: 28242422 XER: 20000000 TASK = c79cb0c0[1746] 'bash' THREAD: c7974000 ... NIP [c00554e4] __free_irq+0x108/0x1b0 LR [c00554e4] __free_irq+0x108/0x1b0 Call Trace: [c7975d10] [c00554e4] __free_irq+0x108/0x1b0 (unreliable) [c7975d30] [c005559c] free_irq+0x10/0x24 [c7975d40] [c01e21ec] vortex_suspend+0x70/0xc4 [c7975d60] [c017e584] pci_legacy_suspend+0x58/0x100 This is because the driver manages interrupts without checking for netif_running(). Though, there are few other issues with suspend/resume in this driver. The intention of calling free_irq() in suspend() was to avoid any possible spurious interrupts (see commit 5b039e681b8c5f30aac9cc04385 "3c59x PM fixes"). But, - On resume, the driver was requesting IRQ just after pci_set_master(), but before vortex_up() (which actually resets 3c59x chips). - Issuing free_irq() on a shared IRQ doesn't guarantee that a buggy HW won't trigger spurious interrupts in another driver that requested the same interrupt. So, if we want to protect from unexpected interrupts, then on suspend we should issue disable_irq(), not free_irq(). Signed-off-by: Anton Vorontsov Signed-off-by: David S. Miller --- drivers/net/3c59x.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 7adff4d0960..b9eeadf01b7 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -813,10 +813,10 @@ static int vortex_suspend(struct pci_dev *pdev, pm_message_t state) if (netif_running(dev)) { netif_device_detach(dev); vortex_down(dev, 1); + disable_irq(dev->irq); } pci_save_state(pdev); pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); - free_irq(dev->irq, dev); pci_disable_device(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); } @@ -839,18 +839,12 @@ static int vortex_resume(struct pci_dev *pdev) return err; } pci_set_master(pdev); - if (request_irq(dev->irq, vp->full_bus_master_rx ? - &boomerang_interrupt : &vortex_interrupt, IRQF_SHARED, dev->name, dev)) { - pr_warning("%s: Could not reserve IRQ %d\n", dev->name, dev->irq); - pci_disable_device(pdev); - return -EBUSY; - } if (netif_running(dev)) { err = vortex_up(dev); if (err) return err; - else - netif_device_attach(dev); + enable_irq(dev->irq); + netif_device_attach(dev); } } return 0; -- cgit v1.2.3