Commit 8eae4cbf authored by Jeff Garzik's avatar Jeff Garzik

[wireless] Add new Prism54 wireless driver.

parent 0a912921
...@@ -307,6 +307,53 @@ config PCMCIA_WL3501 ...@@ -307,6 +307,53 @@ config PCMCIA_WL3501
It has basic support for Linux wireless extensions and initial It has basic support for Linux wireless extensions and initial
micro support for ethtool. micro support for ethtool.
comment "Prism GT/Duette 802.11(a/b/g) PCI/PCMCIA support"
depends on NET_RADIO && PCI
config PRISM54
tristate 'Intersil Prism GT/Duette/Indigo PCI/PCMCIA'
depends on PCI && NET_RADIO && EXPERIMENTAL && HOTPLUG
select FW_LOADER
---help---
Enable PCI and Cardbus support for the following chipset based cards:
ISL3880 - Prism GT 802.11 b/g
ISL3877 - Prism Indigo 802.11 a
ISL3890 - Prism Duette 802.11 a/b/g
For a complete list of supported cards visit <http://prism54.org>.
Here is the latest confirmed list of supported cards:
3com OfficeConnect 11g Cardbus Card aka 3CRWE154G72
Allnet ALL0271 PCI Card
Compex WL54G Cardbus Card
Corega CG-WLCB54GT Cardbus Card
D-Link Air Plus Xtreme G A1 Cardbus Card aka DWL-g650
I-O Data WN-G54/CB Cardbus Card
Kobishi XG-300 aka Z-Com Cardbus Card
Netgear WG511 Cardbus Card
Ovislink WL-5400PCI PCI Card
Peabird WLG-PCI PCI Card
Sitecom WL-100i Cardbus Card
Sitecom WL-110i PCI Card
SMC2802W - EZ Connect g 2.4GHz 54 Mbps Wireless PCI Card
SMC2835W - EZ Connect g 2.4GHz 54 Mbps Wireless Cardbus Card
Z-Com XG-900 PCI Card
Zyxel G-100 Cardbus Card
If you enable this you will need a firmware file as well.
You will need to copy this to /usr/lib/hotplug/firmware/isl3890.
You can get this non-GPL'd firmware file from the Prism54 project page:
<http://prism54.org>
You will also need the /etc/hotplug/firmware.agent script from
a current hotplug package.
Note: You need a motherboard with DMA support to use any of these cards
If you want to compile the driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/modules.txt>. The module
will be called prism54.ko.
# yes, this works even when no drivers are selected # yes, this works even when no drivers are selected
config NET_WIRELESS config NET_WIRELESS
bool bool
......
...@@ -26,6 +26,8 @@ obj-$(CONFIG_ATMEL) += atmel.o ...@@ -26,6 +26,8 @@ obj-$(CONFIG_ATMEL) += atmel.o
obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o
obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
obj-$(CONFIG_PRISM54) += prism54/
# 16-bit wireless PCMCIA client drivers # 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
# $Id: Makefile.k26,v 1.7 2004/01/30 16:24:00 ajfa Exp $
prism54-objs := islpci_eth.o islpci_mgt.o \
isl_38xx.o isl_ioctl.o islpci_dev.o \
islpci_hotplug.o oid_mgt.o
obj-$(CONFIG_PRISM54) += prism54.o
EXTRA_CFLAGS = -I$(PWD) #-DCONFIG_PRISM54_WDS
/* $Header: /var/lib/cvs/prism54-ng/ksrc/isl_38xx.c,v 1.22 2004/02/28 03:06:07 mcgrof Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
* Copyright (C) 2003-2004 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>_
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define __KERNEL_SYSCALLS__
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/delay.h>
#include "isl_38xx.h"
#include <linux/firmware.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/config.h>
#if !defined(CONFIG_FW_LOADER) && !defined(CONFIG_FW_LOADER_MODULE)
#error No Firmware Loading configured in the kernel !
#endif
#include "islpci_dev.h"
#include "islpci_mgt.h"
/******************************************************************************
Device Interface & Control functions
******************************************************************************/
/**
* isl38xx_disable_interrupts - disable all interrupts
* @device: pci memory base address
*
* Instructs the device to disable all interrupt reporting by asserting
* the IRQ line. New events may still show up in the interrupt identification
* register located at offset %ISL38XX_INT_IDENT_REG.
*/
void
isl38xx_disable_interrupts(void *device)
{
isl38xx_w32_flush(device, 0x00000000, ISL38XX_INT_EN_REG);
udelay(ISL38XX_WRITEIO_DELAY);
}
void
isl38xx_handle_sleep_request(isl38xx_control_block *control_block,
int *powerstate, void *device_base)
{
/* device requests to go into sleep mode
* check whether the transmit queues for data and management are empty */
if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ))
/* data tx queue not empty */
return;
if (isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ))
/* management tx queue not empty */
return;
/* check also whether received frames are pending */
if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_DATA_LQ))
/* data rx queue not empty */
return;
if (isl38xx_in_queue(control_block, ISL38XX_CB_RX_MGMTQ))
/* management rx queue not empty */
return;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "Device going to sleep mode\n");
#endif
/* all queues are empty, allow the device to go into sleep mode */
*powerstate = ISL38XX_PSM_POWERSAVE_STATE;
/* assert the Sleep interrupt in the Device Interrupt Register */
isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_SLEEP,
ISL38XX_DEV_INT_REG);
udelay(ISL38XX_WRITEIO_DELAY);
}
void
isl38xx_handle_wakeup(isl38xx_control_block *control_block,
int *powerstate, void *device_base)
{
/* device is in active state, update the powerstate flag */
*powerstate = ISL38XX_PSM_ACTIVE_STATE;
/* now check whether there are frames pending for the card */
if (!isl38xx_in_queue(control_block, ISL38XX_CB_TX_DATA_LQ)
&& !isl38xx_in_queue(control_block, ISL38XX_CB_TX_MGMTQ))
return;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_ANYTHING, "Wake up handler trigger the device\n");
#endif
/* either data or management transmit queue has a frame pending
* trigger the device by setting the Update bit in the Device Int reg */
isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE,
ISL38XX_DEV_INT_REG);
udelay(ISL38XX_WRITEIO_DELAY);
}
void
isl38xx_trigger_device(int asleep, void *device_base)
{
struct timeval current_time;
u32 reg, counter = 0;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "isl38xx trigger device\n");
#endif
/* check whether the device is in power save mode */
if (asleep) {
/* device is in powersave, trigger the device for wakeup */
#if VERBOSE > SHOW_ERROR_MESSAGES
do_gettimeofday(&current_time);
DEBUG(SHOW_TRACING, "%08li.%08li Device wakeup triggered\n",
current_time.tv_sec, current_time.tv_usec);
#endif
DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n",
current_time.tv_sec, current_time.tv_usec,
readl(device_base + ISL38XX_CTRL_STAT_REG));
udelay(ISL38XX_WRITEIO_DELAY);
if (reg = readl(device_base + ISL38XX_INT_IDENT_REG),
reg == 0xabadface) {
#if VERBOSE > SHOW_ERROR_MESSAGES
do_gettimeofday(&current_time);
DEBUG(SHOW_TRACING,
"%08li.%08li Device register abadface\n",
current_time.tv_sec, current_time.tv_usec);
#endif
/* read the Device Status Register until Sleepmode bit is set */
while (reg = readl(device_base + ISL38XX_CTRL_STAT_REG),
(reg & ISL38XX_CTRL_STAT_SLEEPMODE) == 0) {
udelay(ISL38XX_WRITEIO_DELAY);
counter++;
}
DEBUG(SHOW_TRACING,
"%08li.%08li Device register read %08x\n",
current_time.tv_sec, current_time.tv_usec,
readl(device_base + ISL38XX_CTRL_STAT_REG));
udelay(ISL38XX_WRITEIO_DELAY);
#if VERBOSE > SHOW_ERROR_MESSAGES
do_gettimeofday(&current_time);
DEBUG(SHOW_TRACING,
"%08li.%08li Device asleep counter %i\n",
current_time.tv_sec, current_time.tv_usec,
counter);
#endif
}
/* assert the Wakeup interrupt in the Device Interrupt Register */
isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_WAKEUP,
ISL38XX_DEV_INT_REG);
udelay(ISL38XX_WRITEIO_DELAY);
/* perform another read on the Device Status Register */
reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
udelay(ISL38XX_WRITEIO_DELAY);
#if VERBOSE > SHOW_ERROR_MESSAGES
do_gettimeofday(&current_time);
DEBUG(SHOW_TRACING, "%08li.%08li Device register read %08x\n",
current_time.tv_sec, current_time.tv_usec, reg);
#endif
} else {
/* device is (still) awake */
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "Device is in active state\n");
#endif
/* trigger the device by setting the Update bit in the Device Int reg */
isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_UPDATE,
ISL38XX_DEV_INT_REG);
udelay(ISL38XX_WRITEIO_DELAY);
}
}
void
isl38xx_interface_reset(void *device_base, dma_addr_t host_address)
{
u32 reg;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "isl38xx_interface_reset \n");
#endif
/* load the address of the control block in the device */
isl38xx_w32_flush(device_base, host_address, ISL38XX_CTRL_BLK_BASE_REG);
udelay(ISL38XX_WRITEIO_DELAY);
/* set the reset bit in the Device Interrupt Register */
isl38xx_w32_flush(device_base, ISL38XX_DEV_INT_RESET,
ISL38XX_DEV_INT_REG);
udelay(ISL38XX_WRITEIO_DELAY);
/* enable the interrupt for detecting initialization */
/* Note: Do not enable other interrupts here. We want the
* device to have come up first 100% before allowing any other
* interrupts. */
reg = ISL38XX_INT_IDENT_INIT;
isl38xx_w32_flush(device_base, reg, ISL38XX_INT_EN_REG);
udelay(ISL38XX_WRITEIO_DELAY); /* allow complete full reset */
}
void
isl38xx_enable_common_interrupts(void *device_base) {
u32 reg;
reg = ( ISL38XX_INT_IDENT_UPDATE |
ISL38XX_INT_IDENT_SLEEP | ISL38XX_INT_IDENT_WAKEUP);
isl38xx_w32_flush(device_base, reg, ISL38XX_INT_EN_REG);
udelay(ISL38XX_WRITEIO_DELAY);
}
int
isl38xx_upload_firmware(char *fw_id, _REQ_FW_DEV_T dev, void *device_base,
dma_addr_t host_address)
{
u32 reg, rc;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_ERROR_MESSAGES, "isl38xx_upload_firmware(0x%lx, 0x%lx)\n",
(long) device_base, (long) host_address);
#endif
/* clear the RAMBoot and the Reset bit */
reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
reg &= ~ISL38XX_CTRL_STAT_RESET;
reg &= ~ISL38XX_CTRL_STAT_RAMBOOT;
writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
wmb();
udelay(ISL38XX_WRITEIO_DELAY);
/* set the Reset bit without reading the register ! */
reg |= ISL38XX_CTRL_STAT_RESET;
writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
wmb();
udelay(ISL38XX_WRITEIO_DELAY);
/* clear the Reset bit */
reg &= ~ISL38XX_CTRL_STAT_RESET;
writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
wmb();
/* wait a while for the device to reboot */
mdelay(50);
{
const struct firmware *fw_entry = 0;
long fw_len;
const u32 *fw_ptr;
rc = request_firmware(&fw_entry, fw_id, dev);
if (rc) {
printk(KERN_ERR
"%s: request_firmware() failed for '%s'\n",
"prism54", fw_id);
return rc;
}
/* prepare the Direct Memory Base register */
reg = ISL38XX_DEV_FIRMWARE_ADDRES;
fw_ptr = (u32 *) fw_entry->data;
fw_len = fw_entry->size;
if (fw_len % 4) {
printk(KERN_ERR
"%s: firmware '%s' size is not multiple of 32bit, aborting!\n",
"prism54", fw_id);
release_firmware(fw_entry);
return EILSEQ; /* Illegal byte sequence */;
}
while (fw_len > 0) {
long _fw_len =
(fw_len >
ISL38XX_MEMORY_WINDOW_SIZE) ?
ISL38XX_MEMORY_WINDOW_SIZE : fw_len;
u32 *dev_fw_ptr = device_base + ISL38XX_DIRECT_MEM_WIN;
/* set the cards base address for writting the data */
isl38xx_w32_flush(device_base, reg,
ISL38XX_DIR_MEM_BASE_REG);
wmb(); /* be paranoid */
/* increment the write address for next iteration */
reg += _fw_len;
fw_len -= _fw_len;
/* write the data to the Direct Memory Window 32bit-wise */
/* memcpy_toio() doesn't guarantee 32bit writes :-| */
while (_fw_len > 0) {
/* use non-swapping writel() */
__raw_writel(*fw_ptr, dev_fw_ptr);
fw_ptr++, dev_fw_ptr++;
_fw_len -= 4;
}
/* flush PCI posting */
(void) readl(device_base + ISL38XX_PCI_POSTING_FLUSH);
wmb(); /* be paranoid again */
BUG_ON(_fw_len != 0);
}
BUG_ON(fw_len != 0);
release_firmware(fw_entry);
}
/* now reset the device
* clear the Reset & ClkRun bit, set the RAMBoot bit */
reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
reg &= ~ISL38XX_CTRL_STAT_CLKRUN;
reg &= ~ISL38XX_CTRL_STAT_RESET;
reg |= ISL38XX_CTRL_STAT_RAMBOOT;
isl38xx_w32_flush(device_base, reg, ISL38XX_CTRL_STAT_REG);
wmb();
udelay(ISL38XX_WRITEIO_DELAY);
/* set the reset bit latches the host override and RAMBoot bits
* into the device for operation when the reset bit is reset */
reg |= ISL38XX_CTRL_STAT_RESET;
writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
/* don't do flush PCI posting here! */
wmb();
udelay(ISL38XX_WRITEIO_DELAY);
/* clear the reset bit should start the whole circus */
reg &= ~ISL38XX_CTRL_STAT_RESET;
writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
/* don't do flush PCI posting here! */
wmb();
udelay(ISL38XX_WRITEIO_DELAY);
return 0;
}
int
isl38xx_in_queue(isl38xx_control_block *cb, int queue)
{
const s32 delta = (le32_to_cpu(cb->driver_curr_frag[queue]) -
le32_to_cpu(cb->device_curr_frag[queue]));
/* determine the amount of fragments in the queue depending on the type
* of the queue, either transmit or receive */
BUG_ON(delta < 0); /* driver ptr must be ahead of device ptr */
switch (queue) {
/* send queues */
case ISL38XX_CB_TX_MGMTQ:
BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE);
case ISL38XX_CB_TX_DATA_LQ:
case ISL38XX_CB_TX_DATA_HQ:
BUG_ON(delta > ISL38XX_CB_TX_QSIZE);
return delta;
break;
/* receive queues */
case ISL38XX_CB_RX_MGMTQ:
BUG_ON(delta > ISL38XX_CB_MGMT_QSIZE);
return ISL38XX_CB_MGMT_QSIZE - delta;
break;
case ISL38XX_CB_RX_DATA_LQ:
case ISL38XX_CB_RX_DATA_HQ:
BUG_ON(delta > ISL38XX_CB_RX_QSIZE);
return ISL38XX_CB_RX_QSIZE - delta;
break;
}
BUG();
return 0;
}
/* $Header: /var/lib/cvs/prism54-ng/ksrc/isl_38xx.h,v 1.22 2004/02/28 03:06:07 mcgrof Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _ISL_38XX_H
#define _ISL_38XX_H
#include <linux/version.h>
#include <asm/io.h>
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,75))
#include <linux/device.h>
# define _REQ_FW_DEV_T struct device *
#else
# define _REQ_FW_DEV_T char *
#endif
#include <asm/byteorder.h>
#define ISL38XX_CB_RX_QSIZE 8
#define ISL38XX_CB_TX_QSIZE 32
/* ISL38XX Access Point Specific definitions */
#define ISL38XX_MAX_WDS_LINKS 8
/* ISL38xx Client Specific definitions */
#define ISL38XX_PSM_ACTIVE_STATE 0
#define ISL38XX_PSM_POWERSAVE_STATE 1
/* ISL38XX Host Interface Definitions */
#define ISL38XX_PCI_MEM_SIZE 0x02000
#define ISL38XX_MEMORY_WINDOW_SIZE 0x01000
#define ISL38XX_DEV_FIRMWARE_ADDRES 0x20000
#define ISL38XX_WRITEIO_DELAY 10 /* in us */
#define ISL38XX_RESET_DELAY 50 /* in ms */
#define ISL38XX_WAIT_CYCLE 10 /* in 10ms */
#define ISL38XX_MAX_WAIT_CYCLES 10
/* PCI Memory Area */
#define ISL38XX_HARDWARE_REG 0x0000
#define ISL38XX_CARDBUS_CIS 0x0800
#define ISL38XX_DIRECT_MEM_WIN 0x1000
/* Hardware registers */
#define ISL38XX_DEV_INT_REG 0x0000
#define ISL38XX_INT_IDENT_REG 0x0010
#define ISL38XX_INT_ACK_REG 0x0014
#define ISL38XX_INT_EN_REG 0x0018
#define ISL38XX_GEN_PURP_COM_REG_1 0x0020
#define ISL38XX_GEN_PURP_COM_REG_2 0x0024
#define ISL38XX_CTRL_BLK_BASE_REG ISL38XX_GEN_PURP_COM_REG_1
#define ISL38XX_DIR_MEM_BASE_REG 0x0030
#define ISL38XX_CTRL_STAT_REG 0x0078
/* High end mobos queue up pci writes, the following
* is used to "read" from after a write to force flush */
#define ISL38XX_PCI_POSTING_FLUSH ISL38XX_INT_EN_REG
/**
* isl38xx_w32_flush - PCI iomem write helper
* @base: (host) memory base address of the device
* @val: 32bit value (host order) to write
* @offset: byte offset into @base to write value to
*
* This helper takes care of writing a 32bit datum to the
* specified offset into the device's pci memory space, and making sure
* the pci memory buffers get flushed by performing one harmless read
* from the %ISL38XX_PCI_POSTING_FLUSH offset.
*/
static inline void
isl38xx_w32_flush(void *base, u32 val, unsigned long offset)
{
writel(val, base + offset);
(void) readl(base + ISL38XX_PCI_POSTING_FLUSH);
}
/* Device Interrupt register bits */
#define ISL38XX_DEV_INT_RESET 0x0001
#define ISL38XX_DEV_INT_UPDATE 0x0002
#define ISL38XX_DEV_INT_WAKEUP 0x0008
#define ISL38XX_DEV_INT_SLEEP 0x0010
/* Interrupt Identification/Acknowledge/Enable register bits */
#define ISL38XX_INT_IDENT_UPDATE 0x0002
#define ISL38XX_INT_IDENT_INIT 0x0004
#define ISL38XX_INT_IDENT_WAKEUP 0x0008
#define ISL38XX_INT_IDENT_SLEEP 0x0010
#define ISL38XX_INT_SOURCES 0x001E
/* Control/Status register bits */
#define ISL38XX_CTRL_STAT_SLEEPMODE 0x00000200
#define ISL38XX_CTRL_STAT_CLKRUN 0x00800000
#define ISL38XX_CTRL_STAT_RESET 0x10000000
#define ISL38XX_CTRL_STAT_RAMBOOT 0x20000000
#define ISL38XX_CTRL_STAT_STARTHALTED 0x40000000
#define ISL38XX_CTRL_STAT_HOST_OVERRIDE 0x80000000
/* Control Block definitions */
#define ISL38XX_CB_RX_DATA_LQ 0
#define ISL38XX_CB_TX_DATA_LQ 1
#define ISL38XX_CB_RX_DATA_HQ 2
#define ISL38XX_CB_TX_DATA_HQ 3
#define ISL38XX_CB_RX_MGMTQ 4
#define ISL38XX_CB_TX_MGMTQ 5
#define ISL38XX_CB_QCOUNT 6
#define ISL38XX_CB_MGMT_QSIZE 4
#define ISL38XX_MIN_QTHRESHOLD 4 /* fragments */
/* Memory Manager definitions */
#define MGMT_FRAME_SIZE 1500 /* >= size struct obj_bsslist */
#define MGMT_TX_FRAME_COUNT 24 /* max 4 + spare 4 + 8 init */
#define MGMT_RX_FRAME_COUNT 24 /* 4*4 + spare 8 */
#define MGMT_FRAME_COUNT (MGMT_TX_FRAME_COUNT + MGMT_RX_FRAME_COUNT)
#define CONTROL_BLOCK_SIZE 1024 /* should be enough */
#define PSM_FRAME_SIZE 1536
#define PSM_MINIMAL_STATION_COUNT 64
#define PSM_FRAME_COUNT PSM_MINIMAL_STATION_COUNT
#define PSM_BUFFER_SIZE PSM_FRAME_SIZE * PSM_FRAME_COUNT
#define MAX_TRAP_RX_QUEUE 4
#define HOST_MEM_BLOCK CONTROL_BLOCK_SIZE + PSM_BUFFER_SIZE
/* Fragment package definitions */
#define FRAGMENT_FLAG_MF 0x0001
#define MAX_FRAGMENT_SIZE 1536
/* In monitor mode frames have a header. I don't know exactly how big those
* frame can be but I've never seen any frame bigger than 1584... :
*/
#define MAX_FRAGMENT_SIZE_RX 1600
typedef struct {
u32 address; /* physical address on host */
u16 size; /* packet size */
u16 flags; /* set of bit-wise flags */
} isl38xx_fragment;
struct isl38xx_cb {
u32 driver_curr_frag[ISL38XX_CB_QCOUNT];
u32 device_curr_frag[ISL38XX_CB_QCOUNT];
isl38xx_fragment rx_data_low[ISL38XX_CB_RX_QSIZE];
isl38xx_fragment tx_data_low[ISL38XX_CB_TX_QSIZE];
isl38xx_fragment rx_data_high[ISL38XX_CB_RX_QSIZE];
isl38xx_fragment tx_data_high[ISL38XX_CB_TX_QSIZE];
isl38xx_fragment rx_data_mgmt[ISL38XX_CB_MGMT_QSIZE];
isl38xx_fragment tx_data_mgmt[ISL38XX_CB_MGMT_QSIZE];
};
typedef struct isl38xx_cb isl38xx_control_block;
/* determine number of entries currently in queue */
int isl38xx_in_queue(isl38xx_control_block *cb, int queue);
void isl38xx_disable_interrupts(void *);
void isl38xx_enable_common_interrupts(void *);
void isl38xx_handle_sleep_request(isl38xx_control_block *, int *,
void *);
void isl38xx_handle_wakeup(isl38xx_control_block *, int *, void *);
void isl38xx_trigger_device(int, void *);
void isl38xx_interface_reset(void *, dma_addr_t);
int isl38xx_upload_firmware(char *, _REQ_FW_DEV_T, void *, dma_addr_t);
#endif /* _ISL_38XX_H */
/* $Header: /var/lib/cvs/prism54-ng/ksrc/isl_ioctl.c,v 1.140 2004/02/28 03:06:07 mcgrof Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
* (C) 2003 Aurelien Alleaume <slts@free.fr>
* (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
* (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/if_arp.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include "isl_ioctl.h"
#include "islpci_mgt.h"
#include "isl_oid.h" /* additional types and defs for isl38xx fw */
#include "oid_mgt.h"
#if WIRELESS_EXT > 12
#include <net/iw_handler.h> /* New driver API */
#endif /* WIRELESS_EXT > 12 */
static int init_mode = CARD_DEFAULT_IW_MODE;
static int init_channel = CARD_DEFAULT_CHANNEL;
static int init_wep = CARD_DEFAULT_WEP;
static int init_filter = CARD_DEFAULT_FILTER;
static int init_authen = CARD_DEFAULT_AUTHEN;
static int init_dot1x = CARD_DEFAULT_DOT1X;
static int init_conformance = CARD_DEFAULT_CONFORMANCE;
static int init_mlme = CARD_DEFAULT_MLME_MODE;
MODULE_PARM(init_mode, "i");
MODULE_PARM_DESC(init_mode,
"Set card mode:\n0: Auto\n1: Ad-Hoc\n2: Managed Client (Default)\n3: Master / Access Point\n4: Repeater (Not supported yet)\n5: Secondary (Not supported yet)\n6: Monitor");
MODULE_PARM(init_channel, "i");
MODULE_PARM_DESC(init_channel,
"Check `iwpriv ethx channel` for available channels");
MODULE_PARM(init_wep, "i");
MODULE_PARM(init_filter, "i");
MODULE_PARM(init_authen, "i");
MODULE_PARM_DESC(init_authen,
"Authentication method. Can be of seven types:\n0 0x0000: None\n1 0x0001: DOT11_AUTH_OS (Default)\n2 0x0002: DOT11_AUTH_SK\n3 0x0003: DOT11_AUTH_BOTH");
MODULE_PARM(init_dot1x, "i");
MODULE_PARM_DESC(init_dot1x,
"\n0: None/not set (Default)\n1: DOT11_DOT1X_AUTHENABLED\n2: DOT11_DOT1X_KEYTXENABLED");
MODULE_PARM(init_mlme, "i");
MODULE_PARM_DESC(init_mlme,
"Sets the MAC layer management entity (MLME) mode of operation,\n0: DOT11_MLME_AUTO (Default)\n1: DOT11_MLME_INTERMEDIATE\n2: DOT11_MLME_EXTENDED");
/**
* prism54_mib_mode_helper - MIB change mode helper function
* @mib: the &struct islpci_mib object to modify
* @iw_mode: new mode (%IW_MODE_*)
*
* This is a helper function, hence it does not lock. Make sure
* caller deals with locking *if* necessary. This function sets the
* mode-dependent mib values and does the mapping of the Linux
* Wireless API modes to Device firmware modes. It also checks for
* correct valid Linux wireless modes.
*/
int
prism54_mib_mode_helper(islpci_private *priv, u32 iw_mode)
{
u32 config = INL_CONFIG_MANUALRUN;
u32 mode, bsstype;
/* For now, just catch early the Repeater and Secondary modes here */
if (iw_mode == IW_MODE_REPEAT || iw_mode == IW_MODE_SECOND) {
printk(KERN_DEBUG "%s(): Sorry, Repeater mode and Secondary mode "
"are not yet supported by this driver.\n",
__FUNCTION__);
return -EINVAL;
}
priv->iw_mode = iw_mode;
switch (iw_mode) {
case IW_MODE_AUTO:
mode = INL_MODE_CLIENT;
bsstype = DOT11_BSSTYPE_ANY;
break;
case IW_MODE_ADHOC:
mode = INL_MODE_CLIENT;
bsstype = DOT11_BSSTYPE_IBSS;
break;
case IW_MODE_INFRA:
mode = INL_MODE_CLIENT;
bsstype = DOT11_BSSTYPE_INFRA;
break;
case IW_MODE_MASTER:
mode = INL_MODE_AP;
bsstype = DOT11_BSSTYPE_INFRA;
break;
case IW_MODE_MONITOR:
mode = INL_MODE_PROMISCUOUS;
bsstype = DOT11_BSSTYPE_ANY;
config |= INL_CONFIG_RXANNEX;
break;
default:
return -EINVAL;
}
if (init_wds)
config |= INL_CONFIG_WDS;
mgt_set(priv, DOT11_OID_BSSTYPE, &bsstype);
mgt_set(priv, OID_INL_CONFIG, &config);
mgt_set(priv, OID_INL_MODE, &mode);
return 0;
}
/**
* prism54_mib_init - fill MIB cache with defaults
*
* this function initializes the struct given as @mib with defaults,
* of which many are retrieved from the global module parameter
* variables.
*/
void
prism54_mib_init(islpci_private *priv)
{
u32 t;
struct obj_buffer psm_buffer = {
.size = cpu_to_le32(PSM_BUFFER_SIZE),
.addr = cpu_to_le32(priv->device_psm_buffer)
};
mgt_set(priv, DOT11_OID_CHANNEL, &init_channel);
mgt_set(priv, DOT11_OID_AUTHENABLE, &init_authen);
mgt_set(priv, DOT11_OID_PRIVACYINVOKED, &init_wep);
mgt_set(priv, DOT11_OID_PSMBUFFER, &psm_buffer);
mgt_set(priv, DOT11_OID_EXUNENCRYPTED, &init_filter);
mgt_set(priv, DOT11_OID_DOT1XENABLE, &init_dot1x);
mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &init_mlme);
mgt_set(priv, OID_INL_DOT11D_CONFORMANCE, &init_conformance);
t = 127;
mgt_set(priv, OID_INL_OUTPUTPOWER, &t);
/* Important: we are setting a default wireless mode and we are
* forcing a valid one, so prism54_mib_mode_helper should just set
* mib values depending on what the wireless mode given is. No need
* for it save old values */
if (init_mode > IW_MODE_MONITOR || init_mode < IW_MODE_AUTO) {
printk(KERN_DEBUG "%s(): You passed a non-valid init_mode. "
"Using default mode\n", __FUNCTION__);
init_mode = CARD_DEFAULT_IW_MODE;
}
/* This sets all of the mode-dependent values */
prism54_mib_mode_helper(priv, init_mode);
}
void
prism54_mib_init_work(islpci_private *priv)
{
down_write(&priv->mib_sem);
mgt_commit(priv);
up_write(&priv->mib_sem);
}
/* this will be executed outside of atomic context thanks to
* schedule_work(), thus we can as well use sleeping semaphore
* locking */
void
prism54_update_stats(islpci_private *priv)
{
char *data;
int j;
struct obj_bss bss, *bss2;
union oid_res_t r;
if (down_interruptible(&priv->stats_sem))
return;
/* missing stats are :
* iwstatistics.qual.updated
* iwstatistics.discard.nwid
* iwstatistics.discard.fragment
* iwstatistics.discard.misc
* iwstatistics.miss.beacon */
/* Noise floor.
* I'm not sure if the unit is dBm.
* Note : If we are not connected, this value seems to be irrevelant. */
mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
priv->local_iwstatistics.qual.noise = r.u;
/* Get the rssi of the link. To do this we need to retrieve a bss. */
/* First get the MAC address of the AP we are associated with. */
mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
data = r.ptr;
/* copy this MAC to the bss */
for (j = 0; j < 6; j++)
bss.address[j] = data[j];
kfree(data);
/* now ask for the corresponding bss */
j = mgt_get_request(priv, DOT11_OID_BSSFIND, 0, (void *) &bss, &r);
bss2 = r.ptr;
/* report the rssi and use it to calculate
* link quality through a signal-noise
* ratio */
priv->local_iwstatistics.qual.level = bss2->rssi;
priv->local_iwstatistics.qual.qual =
bss2->rssi - priv->iwstatistics.qual.noise;
kfree(bss2);
/* report that the stats are new */
priv->local_iwstatistics.qual.updated = 0x7;
/* Rx : unable to decrypt the MPDU */
mgt_get_request(priv, DOT11_OID_PRIVRXFAILED, 0, NULL, &r);
priv->local_iwstatistics.discard.code = r.u;
/* Tx : Max MAC retries num reached */
mgt_get_request(priv, DOT11_OID_MPDUTXFAILED, 0, NULL, &r);
priv->local_iwstatistics.discard.retries = r.u;
up(&priv->stats_sem);
return;
}
struct iw_statistics *
prism54_get_wireless_stats(struct net_device *ndev)
{
islpci_private *priv = ndev->priv;
/* If the stats are being updated return old data */
if (down_trylock(&priv->stats_sem) == 0) {
memcpy(&priv->iwstatistics, &priv->local_iwstatistics,
sizeof (struct iw_statistics));
/* They won't be marked updated for the next time */
priv->local_iwstatistics.qual.updated = 0;
up(&priv->stats_sem);
} else
priv->iwstatistics.qual.updated = 0;
/* Update our wireless stats, but do not schedule to often
* (max 1 HZ) */
if ((priv->stats_timestamp == 0) ||
time_after(jiffies, priv->stats_timestamp + 1 * HZ)) {
schedule_work(&priv->stats_work);
priv->stats_timestamp = jiffies;
}
return &priv->iwstatistics;
}
static int
prism54_commit(struct net_device *ndev, struct iw_request_info *info,
char *cwrq, char *extra)
{
islpci_private *priv = ndev->priv;
/* simply re-set the last set SSID, this should commit most stuff */
/* Commit in Monitor mode is not necessary, also setting essid
* in Monitor mode does not make sense and isn't allowed for this
* device's firmware */
if(priv->iw_mode != IW_MODE_MONITOR)
return mgt_set_request(priv, DOT11_OID_SSID, 0, NULL);
return 0;
}
static int
prism54_get_name(struct net_device *ndev, struct iw_request_info *info,
char *cwrq, char *extra)
{
islpci_private *priv = ndev->priv;
char *capabilities;
union oid_res_t r;
int rvalue;
if (islpci_get_state(priv) < PRV_STATE_INIT) {
strncpy(cwrq, "NOT READY!", IFNAMSIZ);
return 0;
}
rvalue = mgt_get_request(priv, OID_INL_PHYCAPABILITIES, 0, NULL, &r);
switch (r.u) {
case INL_PHYCAP_5000MHZ:
capabilities = "IEEE 802.11a/b/g";
break;
case INL_PHYCAP_FAA:
capabilities = "IEEE 802.11b/g - FAA Support";
break;
case INL_PHYCAP_2400MHZ:
default:
capabilities = "IEEE 802.11b/g"; /* Default */
break;
}
strncpy(cwrq, capabilities, IFNAMSIZ);
return rvalue;
}
static int
prism54_set_freq(struct net_device *ndev, struct iw_request_info *info,
struct iw_freq *fwrq, char *extra)
{
islpci_private *priv = ndev->priv;
int rvalue;
u32 c = 0;
/* prepare the structure for the set object */
if (fwrq->m < 1000)
/* structure value contains a channel indication */
c = fwrq->m;
else {
/* structure contains a frequency indication and fwrq->e = 1 */
int f = fwrq->m / 100000;
if (fwrq->e != 1)
return -EINVAL;
if ((f >= 2412) && (f <= 2484)) {
while ((c < 14) && (f != frequency_list_bg[c]))
c++;
if (c >= 14)
return -EINVAL;
} else if ((f >= (int) 5170) && (f <= (int) 5320)) {
while ((c < 12) && (f != frequency_list_a[c]))
c++;
if (c >= 12)
return -EINVAL;
} else
return -EINVAL;
c++;
}
rvalue = mgt_set_request(priv, DOT11_OID_CHANNEL, 0, &c);
/* Call commit handler */
return (rvalue ? rvalue : -EINPROGRESS);
}
static int
prism54_get_freq(struct net_device *ndev, struct iw_request_info *info,
struct iw_freq *fwrq, char *extra)
{
islpci_private *priv = ndev->priv;
union oid_res_t r;
int rvalue;
rvalue = mgt_get_request(priv, DOT11_OID_CHANNEL, 0, NULL, &r);
fwrq->m = r.u;
fwrq->e = 0;
return rvalue;
}
static int
prism54_set_mode(struct net_device *ndev, struct iw_request_info *info,
__u32 * uwrq, char *extra)
{
islpci_private *priv = ndev->priv;
u32 mlmeautolevel = CARD_DEFAULT_MLME_MODE;
/* Let's see if the user passed a valid Linux Wireless mode */
if (*uwrq > IW_MODE_MONITOR || *uwrq < IW_MODE_AUTO) {
printk(KERN_DEBUG
"%s: %s() You passed a non-valid init_mode.\n",
priv->ndev->name, __FUNCTION__);
return -EINVAL;
}
down_write(&priv->mib_sem);
if (prism54_mib_mode_helper(priv, *uwrq)) {
up_write(&priv->mib_sem);
return -EOPNOTSUPP;
}
/* the ACL code needs an intermediate mlmeautolevel. The wpa stuff an
* extended one.
*/
if ((*uwrq == IW_MODE_MASTER) && (priv->acl.policy != MAC_POLICY_OPEN))
mlmeautolevel = DOT11_MLME_INTERMEDIATE;
if (priv->wpa)
mlmeautolevel = DOT11_MLME_EXTENDED;
mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
mgt_commit(priv);
priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR)
? ARPHRD_IEEE80211 : ARPHRD_ETHER;
up_write(&priv->mib_sem);
return 0;
}
/* Use mib cache */
static int
prism54_get_mode(struct net_device *ndev, struct iw_request_info *info,
__u32 * uwrq, char *extra)
{
islpci_private *priv = ndev->priv;
BUG_ON((priv->iw_mode < IW_MODE_AUTO) || (priv->iw_mode >
IW_MODE_MONITOR));
*uwrq = priv->iw_mode;
return 0;
}
/* we use DOT11_OID_EDTHRESHOLD. From what I guess the card will not try to
* emit data if (sensitivity > rssi - noise) (in dBm).
* prism54_set_sens does not seem to work.
*/
static int
prism54_set_sens(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
u32 sens;
/* by default the card sets this to 20. */
sens = vwrq->disabled ? 20 : vwrq->value;
/* set the ed threshold. */
return mgt_set_request(priv, DOT11_OID_EDTHRESHOLD, 0, &sens);
}
static int
prism54_get_sens(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
union oid_res_t r;
int rvalue;
rvalue = mgt_get_request(priv, DOT11_OID_EDTHRESHOLD, 0, NULL, &r);
vwrq->value = r.u;
vwrq->disabled = (vwrq->value == 0);
vwrq->fixed = 1;
return rvalue;
}
static int
prism54_get_range(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
struct iw_range *range = (struct iw_range *) extra;
islpci_private *priv = ndev->priv;
char *data;
int i, m, rvalue;
struct obj_frequencies *freq;
union oid_res_t r;
memset(range, 0, sizeof (struct iw_range));
dwrq->length = sizeof (struct iw_range);
/* set the wireless extension version number */
range->we_version_source = SUPPORTED_WIRELESS_EXT;
range->we_version_compiled = WIRELESS_EXT;
/* Now the encoding capabilities */
range->num_encoding_sizes = 3;
/* 64(40) bits WEP */
range->encoding_size[0] = 5;
/* 128(104) bits WEP */
range->encoding_size[1] = 13;
/* 256 bits for WPA-PSK */
range->encoding_size[2] = 32;
/* 4 keys are allowed */
range->max_encoding_tokens = 4;
/* we don't know the quality range... */
range->max_qual.level = 0;
range->max_qual.noise = 0;
range->max_qual.qual = 0;
/* these value describe an average quality. Needs more tweaking... */
range->avg_qual.level = -80; /* -80 dBm */
range->avg_qual.noise = 0; /* don't know what to put here */
range->avg_qual.qual = 0;
range->sensitivity = 200;
/* retry limit capabilities */
range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
range->retry_flags = IW_RETRY_LIMIT;
range->r_time_flags = IW_RETRY_LIFETIME;
/* I don't know the range. Put stupid things here */
range->min_retry = 1;
range->max_retry = 65535;
range->min_r_time = 1024;
range->max_r_time = 65535 * 1024;
/* txpower is supported in dBm's */
range->txpower_capa = IW_TXPOW_DBM;
if (islpci_get_state(priv) < PRV_STATE_INIT)
return 0;
/* Request the device for the supported frequencies
* not really revelant since some devices will report the 5 GHz band
* frequencies even if they don't support them.
*/
rvalue =
mgt_get_request(priv, DOT11_OID_SUPPORTEDFREQUENCIES, 0, NULL, &r);
freq = r.ptr;
range->num_channels = le16_to_cpu(freq->nr);
range->num_frequency = le16_to_cpu(freq->nr);
/* Frequencies are not listed in the right order. The reordering is probably
* firmware dependant and thus should work for everyone.
*/
m = min(IW_MAX_FREQUENCIES, (int) le16_to_cpu(freq->nr));
for (i = 0; i < m - 12; i++) {
range->freq[i].m = le16_to_cpu(freq->mhz[12 + i]);
range->freq[i].e = 6;
range->freq[i].i = i + 1;
}
for (i = m - 12; i < m; i++) {
range->freq[i].m = le16_to_cpu(freq->mhz[i - m + 12]);
range->freq[i].e = 6;
range->freq[i].i = i + 23;
}
kfree(freq);
rvalue |= mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r);
data = r.ptr;
/* We got an array of char. It is NULL terminated. */
i = 0;
while ((i < IW_MAX_BITRATES) && (*data != 0)) {
/* the result must be in bps. The card gives us 500Kbps */
range->bitrate[i] = (__s32) (*data >> 1);
range->bitrate[i] *= 1000000;
i++;
data++;
}
range->num_bitrates = i;
kfree(r.ptr);
return rvalue;
}
/* Set AP address*/
static int
prism54_set_wap(struct net_device *ndev, struct iw_request_info *info,
struct sockaddr *awrq, char *extra)
{
islpci_private *priv = ndev->priv;
char bssid[6];
int rvalue;
if (awrq->sa_family != ARPHRD_ETHER)
return -EINVAL;
/* prepare the structure for the set object */
memcpy(&bssid[0], awrq->sa_data, 6);
/* set the bssid -- does this make sense when in AP mode? */
rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid);
return (rvalue ? rvalue : -EINPROGRESS); /* Call commit handler */
}
/* get AP address*/
static int
prism54_get_wap(struct net_device *ndev, struct iw_request_info *info,
struct sockaddr *awrq, char *extra)
{
islpci_private *priv = ndev->priv;
union oid_res_t r;
int rvalue;
rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
memcpy(awrq->sa_data, r.ptr, 6);
awrq->sa_family = ARPHRD_ETHER;
kfree(r.ptr);
return rvalue;
}
static int
prism54_set_scan(struct net_device *dev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
/* hehe the device does this automagicaly */
return 0;
}
/* a little helper that will translate our data into a card independent
* format that the Wireless Tools will understand. This was inspired by
* the "Aironet driver for 4500 and 4800 series cards" (GPL)
*/
inline char *
prism54_translate_bss(struct net_device *ndev, char *current_ev,
char *end_buf, struct obj_bss *bss, char noise)
{
struct iw_event iwe; /* Temporary buffer */
short cap;
islpci_private *priv = ndev->priv;
/* The first entry must be the MAC address */
memcpy(iwe.u.ap_addr.sa_data, bss->address, 6);
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
iwe.cmd = SIOCGIWAP;
current_ev =
iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
/* The following entries will be displayed in the same order we give them */
/* The ESSID. */
iwe.u.data.length = bss->ssid.length;
iwe.u.data.flags = 1;
iwe.cmd = SIOCGIWESSID;
current_ev = iwe_stream_add_point(current_ev, end_buf,
&iwe, bss->ssid.octets);
/* Capabilities */
#define CAP_ESS 0x01
#define CAP_IBSS 0x02
#define CAP_CRYPT 0x10
/* Mode */
cap = le16_to_cpu(bss->capinfo);
iwe.u.mode = 0;
if (cap & CAP_ESS)
iwe.u.mode = IW_MODE_MASTER;
else if (cap & CAP_IBSS)
iwe.u.mode = IW_MODE_ADHOC;
iwe.cmd = SIOCGIWMODE;
if (iwe.u.mode)
current_ev =
iwe_stream_add_event(current_ev, end_buf, &iwe,
IW_EV_UINT_LEN);
/* Encryption capability */
if (cap & CAP_CRYPT)
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
else
iwe.u.data.flags = IW_ENCODE_DISABLED;
iwe.u.data.length = 0;
iwe.cmd = SIOCGIWENCODE;
current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL);
/* Add frequency. (short) bss->channel is the frequency in MHz */
iwe.u.freq.m = bss->channel;
iwe.u.freq.e = 6;
iwe.cmd = SIOCGIWFREQ;
current_ev =
iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
/* Add quality statistics */
iwe.u.qual.level = bss->rssi;
iwe.u.qual.noise = noise;
/* do a simple SNR for quality */
iwe.u.qual.qual = bss->rssi - noise;
iwe.cmd = IWEVQUAL;
current_ev =
iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
#if WIRELESS_EXT > 14
if (priv->wpa) {
u8 wpa_ie[MAX_WPA_IE_LEN];
char *buf, *p;
size_t wpa_ie_len;
int i;
wpa_ie_len = prism54_wpa_ie_get(priv, bss->address, wpa_ie);
if (wpa_ie_len > 0 &&
(buf = kmalloc(wpa_ie_len * 2 + 10, GFP_ATOMIC))) {
p = buf;
p += sprintf(p, "wpa_ie=");
for (i = 0; i < wpa_ie_len; i++) {
p += sprintf(p, "%02x", wpa_ie[i]);
}
memset(&iwe, 0, sizeof (iwe));
iwe.cmd = IWEVCUSTOM;
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(current_ev, end_buf,
&iwe, buf);
kfree(buf);
}
}
#endif /* WIRELESS_EXT > 14 */
return current_ev;
}
int
prism54_get_scan(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
islpci_private *priv = ndev->priv;
int i, rvalue;
struct obj_bsslist *bsslist;
u32 noise = 0;
char *current_ev = extra;
union oid_res_t r;
if (islpci_get_state(priv) < PRV_STATE_INIT) {
/* device is not ready, fail gently */
dwrq->length = 0;
return 0;
}
/* first get the noise value. We will use it to report the link quality */
rvalue = mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
noise = r.u;
/* Ask the device for a list of known bss. We can report at most
* IW_MAX_AP=64 to the range struct. But the device won't repport anything
* if you change the value of MAXBSS=24. Anyway 24 AP It is probably enough.
*/
rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r);
bsslist = r.ptr;
/* ok now, scan the list and translate its info */
for (i = 0; i < min(IW_MAX_AP, (int) le32_to_cpu(bsslist->nr)); i++)
current_ev = prism54_translate_bss(ndev, current_ev,
extra + IW_SCAN_MAX_DATA,
&(bsslist->bsslist[i]),
noise);
kfree(bsslist);
dwrq->length = (current_ev - extra);
dwrq->flags = 0; /* todo */
return rvalue;
}
static int
prism54_set_essid(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
islpci_private *priv = ndev->priv;
struct obj_ssid essid;
memset(essid.octets, 0, 33);
/* Check if we were asked for `any' */
if (dwrq->flags && dwrq->length) {
if (dwrq->length > min(33, IW_ESSID_MAX_SIZE + 1))
return -E2BIG;
essid.length = dwrq->length - 1;
memcpy(essid.octets, extra, dwrq->length);
} else
essid.length = 0;
if (priv->iw_mode != IW_MODE_MONITOR)
return mgt_set_request(priv, DOT11_OID_SSID, 0, &essid);
/* If in monitor mode, just save to mib */
mgt_set(priv, DOT11_OID_SSID, &essid);
return 0;
}
static int
prism54_get_essid(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
islpci_private *priv = ndev->priv;
struct obj_ssid *essid;
union oid_res_t r;
int rvalue;
rvalue = mgt_get_request(priv, DOT11_OID_SSID, 0, NULL, &r);
essid = r.ptr;
if (essid->length) {
dwrq->flags = 1; /* set ESSID to ON for Wireless Extensions */
/* if it is to big, trunk it */
dwrq->length = min(IW_ESSID_MAX_SIZE, essid->length + 1);
} else {
dwrq->flags = 0;
dwrq->length = 0;
}
essid->octets[essid->length] = '\0';
memcpy(extra, essid->octets, dwrq->length);
kfree(essid);
return rvalue;
}
/* Provides no functionality, just completes the ioctl. In essence this is a
* just a cosmetic ioctl.
*/
static int
prism54_set_nick(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
islpci_private *priv = ndev->priv;
if (dwrq->length > IW_ESSID_MAX_SIZE)
return -E2BIG;
down_write(&priv->mib_sem);
memset(priv->nickname, 0, sizeof (priv->nickname));
memcpy(priv->nickname, extra, dwrq->length);
up_write(&priv->mib_sem);
return 0;
}
static int
prism54_get_nick(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
islpci_private *priv = ndev->priv;
dwrq->length = 0;
down_read(&priv->mib_sem);
dwrq->length = strlen(priv->nickname) + 1;
memcpy(extra, priv->nickname, dwrq->length);
up_read(&priv->mib_sem);
return 0;
}
/* Set the allowed Bitrates */
static int
prism54_set_rate(struct net_device *ndev,
struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
u32 rate, profile;
char *data;
int ret, i;
union oid_res_t r;
if (vwrq->value == -1) {
/* auto mode. No limit. */
profile = 1;
return mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
}
if((ret = mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r)))
return ret;
rate = (u32) (vwrq->value / 500000);
data = r.ptr;
i = 0;
while(data[i]) {
if(rate && (data[i] == rate)) {
break;
}
if(vwrq->value == i) {
break;
}
data[i] |= 0x80;
i++;
}
if(!data[i]) {
return -EINVAL;
}
data[i] |= 0x80;
data[i + 1] = 0;
/* Now, check if we want a fixed or auto value */
if (vwrq->fixed) {
data[0] = data[i];
data[1] = 0;
}
/*
i = 0;
printk("prism54 rate: ");
while(data[i]) {
printk("%u ", data[i]);
i++;
}
printk("0\n");
*/
profile = -1;
ret = mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
ret |= mgt_set_request(priv, DOT11_OID_EXTENDEDRATES, 0, data);
ret |= mgt_set_request(priv, DOT11_OID_RATES, 0, data);
kfree(r.ptr);
return ret;
}
/* Get the current bit rate */
static int
prism54_get_rate(struct net_device *ndev,
struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
int rvalue;
char *data;
union oid_res_t r;
/* Get the current bit rate */
if((rvalue = mgt_get_request(priv, GEN_OID_LINKSTATE, 0, NULL, &r)))
return rvalue;
vwrq->value = r.u * 500000;
/* request the device for the enabled rates */
if((rvalue = mgt_get_request(priv, DOT11_OID_RATES, 0, NULL, &r)))
return rvalue;
data = r.ptr;
vwrq->fixed = (data[0] != 0) && (data[1] == 0);
kfree(r.ptr);
return 0;
}
static int
prism54_set_rts(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
return mgt_set_request(priv, DOT11_OID_RTSTHRESH, 0, &vwrq->value);
}
static int
prism54_get_rts(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
union oid_res_t r;
int rvalue;
/* get the rts threshold */
rvalue = mgt_get_request(priv, DOT11_OID_RTSTHRESH, 0, NULL, &r);
vwrq->value = r.u;
return rvalue;
}
static int
prism54_set_frag(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
return mgt_set_request(priv, DOT11_OID_FRAGTHRESH, 0, &vwrq->value);
}
static int
prism54_get_frag(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
union oid_res_t r;
int rvalue;
rvalue = mgt_get_request(priv, DOT11_OID_FRAGTHRESH, 0, NULL, &r);
vwrq->value = r.u;
return rvalue;
}
/* Here we have (min,max) = max retries for (small frames, big frames). Where
* big frame <=> bigger than the rts threshold
* small frame <=> smaller than the rts threshold
* This is not really the behavior expected by the wireless tool but it seems
* to be a common behavior in other drivers.
*
* It seems that playing with this tends to hang the card -> DISABLED
*/
static int
prism54_set_retry(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
u32 slimit = 0, llimit = 0; /* short and long limit */
u32 lifetime = 0;
int rvalue = 0;
if (vwrq->disabled)
/* we cannot disable this feature */
return -EINVAL;
if (vwrq->flags & IW_RETRY_LIMIT) {
if (vwrq->flags & IW_RETRY_MIN)
slimit = vwrq->value;
else if (vwrq->flags & IW_RETRY_MAX)
llimit = vwrq->value;
else {
/* we are asked to set both */
slimit = vwrq->value;
llimit = vwrq->value;
}
}
if (vwrq->flags & IW_RETRY_LIFETIME)
/* Wireless tools use us unit while the device uses 1024 us unit */
lifetime = vwrq->value / 1024;
/* now set what is requested */
if (slimit != 0)
rvalue =
mgt_set_request(priv, DOT11_OID_SHORTRETRIES, 0, &slimit);
if (llimit != 0)
rvalue |=
mgt_set_request(priv, DOT11_OID_LONGRETRIES, 0, &llimit);
if (lifetime != 0)
rvalue |=
mgt_set_request(priv, DOT11_OID_MAXTXLIFETIME, 0,
&lifetime);
return rvalue;
}
static int
prism54_get_retry(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
union oid_res_t r;
int rvalue = 0;
vwrq->disabled = 0; /* It cannot be disabled */
if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
/* we are asked for the life time */
rvalue =
mgt_get_request(priv, DOT11_OID_MAXTXLIFETIME, 0, NULL, &r);
vwrq->value = r.u * 1024;
vwrq->flags = IW_RETRY_LIFETIME;
} else if ((vwrq->flags & IW_RETRY_MAX)) {
/* we are asked for the long retry limit */
rvalue |=
mgt_get_request(priv, DOT11_OID_LONGRETRIES, 0, NULL, &r);
vwrq->value = r.u;
vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
} else {
/* default. get the short retry limit */
rvalue |=
mgt_get_request(priv, DOT11_OID_SHORTRETRIES, 0, NULL, &r);
vwrq->value = r.u;
vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
}
return rvalue;
}
static int
prism54_set_encode(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
islpci_private *priv = ndev->priv;
int rvalue = 0, force = 0;
int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0;
union oid_res_t r;
/* with the new API, it's impossible to get a NULL pointer.
* New version of iwconfig set the IW_ENCODE_NOKEY flag
* when no key is given, but older versions don't. */
if (dwrq->length > 0) {
/* we have a key to set */
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
int current_index;
struct obj_key key = { DOT11_PRIV_WEP, 0, "" };
/* get the current key index */
rvalue = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
current_index = r.u;
/* Verify that the key is not marked as invalid */
if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
key.length = dwrq->length > sizeof (key.key) ?
sizeof (key.key) : dwrq->length;
memcpy(key.key, extra, key.length);
if (key.length == 32)
/* we want WPA-PSK */
key.type = DOT11_PRIV_TKIP;
if ((index < 0) || (index > 3))
/* no index provided use the current one */
index = current_index;
/* now send the key to the card */
rvalue |=
mgt_set_request(priv, DOT11_OID_DEFKEYX, index,
&key);
}
/*
* If a valid key is set, encryption should be enabled
* (user may turn it off later).
* This is also how "iwconfig ethX key on" works
*/
if ((index == current_index) && (key.length > 0))
force = 1;
} else {
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
if ((index >= 0) && (index <= 3)) {
/* we want to set the key index */
rvalue |=
mgt_set_request(priv, DOT11_OID_DEFKEYID, 0,
&index);
} else {
if (!dwrq->flags & IW_ENCODE_MODE) {
/* we cannot do anything. Complain. */
return -EINVAL;
}
}
}
/* now read the flags */
if (dwrq->flags & IW_ENCODE_DISABLED) {
/* Encoding disabled,
* authen = DOT11_AUTH_OS;
* invoke = 0;
* exunencrypt = 0; */
}
if (dwrq->flags & IW_ENCODE_OPEN)
/* Encode but accept non-encoded packets. No auth */
invoke = 1;
if ((dwrq->flags & IW_ENCODE_RESTRICTED) || force) {
/* Refuse non-encoded packets. Auth */
authen = DOT11_AUTH_BOTH;
invoke = 1;
exunencrypt = 1;
}
/* do the change if requested */
if ((dwrq->flags & IW_ENCODE_MODE) || force) {
rvalue |=
mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
rvalue |=
mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &invoke);
rvalue |=
mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0,
&exunencrypt);
}
return rvalue;
}
static int
prism54_get_encode(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
islpci_private *priv = ndev->priv;
struct obj_key *key;
u32 devindex, index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
u32 authen = 0, invoke = 0, exunencrypt = 0;
int rvalue;
union oid_res_t r;
/* first get the flags */
rvalue = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
authen = r.u;
rvalue |= mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
invoke = r.u;
rvalue |= mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
exunencrypt = r.u;
if (invoke && (authen == DOT11_AUTH_BOTH) && exunencrypt)
dwrq->flags = IW_ENCODE_RESTRICTED;
else if ((authen == DOT11_AUTH_OS) && !exunencrypt) {
if (invoke)
dwrq->flags = IW_ENCODE_OPEN;
else
dwrq->flags = IW_ENCODE_DISABLED;
} else
/* The card should not work in this state */
dwrq->flags = 0;
/* get the current device key index */
rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
devindex = r.u;
/* Now get the key, return it */
if ((index < 0) || (index > 3))
/* no index provided, use the current one */
index = devindex;
rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYX, index, NULL, &r);
key = r.ptr;
dwrq->length = key->length;
memcpy(extra, key->key, dwrq->length);
kfree(key);
/* return the used key index */
dwrq->flags |= devindex + 1;
return rvalue;
}
static int
prism54_get_txpower(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
union oid_res_t r;
int rvalue;
rvalue = mgt_get_request(priv, OID_INL_OUTPUTPOWER, 0, NULL, &r);
/* intersil firmware operates in 0.25 dBm (1/4 dBm) */
vwrq->value = (s32)r.u / 4;
vwrq->fixed = 1;
/* radio is not turned of
* btw: how is possible to turn off only the radio
*/
vwrq->disabled = 0;
return rvalue;
}
static int
prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info,
struct iw_param *vwrq, char *extra)
{
islpci_private *priv = ndev->priv;
s32 u = vwrq->value;
/* intersil firmware operates in 0.25 dBm (1/4) */
u *= 4;
if (vwrq->disabled) {
/* don't know how to disable radio */
printk(KERN_DEBUG
"%s: %s() disabling radio is not yet supported.\n",
priv->ndev->name, __FUNCTION__);
return -ENOTSUPP;
} else if (vwrq->fixed)
/* currently only fixed value is supported */
return mgt_set_request(priv, OID_INL_OUTPUTPOWER, 0, &u);
else {
printk(KERN_DEBUG
"%s: %s() auto power will be implemented later.\n",
priv->ndev->name, __FUNCTION__);
return -ENOTSUPP;
}
}
static int
prism54_reset(struct net_device *ndev, struct iw_request_info *info,
__u32 * uwrq, char *extra)
{
islpci_reset(ndev->priv, 0);
return 0;
}
static int
prism54_set_beacon(struct net_device *ndev, struct iw_request_info *info,
__u32 * uwrq, char *extra)
{
int rvalue = mgt_set_request((islpci_private *) ndev->priv,
DOT11_OID_BEACONPERIOD, 0, uwrq);
return (rvalue ? rvalue : -EINPROGRESS);
}
static int
prism54_get_beacon(struct net_device *ndev, struct iw_request_info *info,
__u32 * uwrq, char *extra)
{
union oid_res_t r;
int rvalue;
rvalue =
mgt_get_request((islpci_private *) ndev->priv,
DOT11_OID_BEACONPERIOD, 0, NULL, &r);
*uwrq = r.u;
return rvalue;
}
void
prism54_acl_init(struct islpci_acl *acl)
{
sema_init(&acl->sem, 1);
INIT_LIST_HEAD(&acl->mac_list);
acl->size = 0;
acl->policy = MAC_POLICY_OPEN;
}
static void
prism54_clear_mac(struct islpci_acl *acl)
{
struct list_head *ptr, *next;
struct mac_entry *entry;
if (down_interruptible(&acl->sem))
return;
if (acl->size == 0) {
up(&acl->sem);
return;
}
for (ptr = acl->mac_list.next, next = ptr->next;
ptr != &acl->mac_list; ptr = next, next = ptr->next) {
entry = list_entry(ptr, struct mac_entry, _list);
list_del(ptr);
kfree(entry);
}
acl->size = 0;
up(&acl->sem);
}
void
prism54_acl_clean(struct islpci_acl *acl)
{
prism54_clear_mac(acl);
}
static int
prism54_add_mac(struct net_device *ndev, struct iw_request_info *info,
struct sockaddr *awrq, char *extra)
{
islpci_private *priv = ndev->priv;
struct islpci_acl *acl = &priv->acl;
struct mac_entry *entry;
struct sockaddr *addr = (struct sockaddr *) extra;
if (addr->sa_family != ARPHRD_ETHER)
return -EOPNOTSUPP;
entry = kmalloc(sizeof (struct mac_entry), GFP_KERNEL);
if (entry == NULL)
return -ENOMEM;
memcpy(entry->addr, addr->sa_data, ETH_ALEN);
if (down_interruptible(&acl->sem)) {
kfree(entry);
return -ERESTARTSYS;
}
list_add_tail(&entry->_list, &acl->mac_list);
acl->size++;
up(&acl->sem);
return 0;
}
static int
prism54_del_mac(struct net_device *ndev, struct iw_request_info *info,
struct sockaddr *awrq, char *extra)
{
islpci_private *priv = ndev->priv;
struct islpci_acl *acl = &priv->acl;
struct mac_entry *entry;
struct list_head *ptr;
struct sockaddr *addr = (struct sockaddr *) extra;
if (addr->sa_family != ARPHRD_ETHER)
return -EOPNOTSUPP;
if (down_interruptible(&acl->sem))
return -ERESTARTSYS;
for (ptr = acl->mac_list.next; ptr != &acl->mac_list; ptr = ptr->next) {
entry = list_entry(ptr, struct mac_entry, _list);
if (memcmp(entry->addr, addr->sa_data, ETH_ALEN) == 0) {
list_del(ptr);
acl->size--;
kfree(entry);
up(&acl->sem);
return 0;
}
}
up(&acl->sem);
return -EINVAL;
}
static int
prism54_get_mac(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
islpci_private *priv = ndev->priv;
struct islpci_acl *acl = &priv->acl;
struct mac_entry *entry;
struct list_head *ptr;
struct sockaddr *dst = (struct sockaddr *) extra;
dwrq->length = 0;
if (down_interruptible(&acl->sem))
return -ERESTARTSYS;
for (ptr = acl->mac_list.next; ptr != &acl->mac_list; ptr = ptr->next) {
entry = list_entry(ptr, struct mac_entry, _list);
memcpy(dst->sa_data, entry->addr, ETH_ALEN);
dst->sa_family = ARPHRD_ETHER;
dwrq->length++;
dst++;
}
up(&acl->sem);
return 0;
}
/* Setting policy also clears the MAC acl, even if we don't change the defaut
* policy
*/
static int
prism54_set_policy(struct net_device *ndev, struct iw_request_info *info,
__u32 * uwrq, char *extra)
{
islpci_private *priv = ndev->priv;
struct islpci_acl *acl = &priv->acl;
u32 mlmeautolevel;
prism54_clear_mac(acl);
if ((*uwrq < MAC_POLICY_OPEN) || (*uwrq > MAC_POLICY_REJECT))
return -EINVAL;
down_write(&priv->mib_sem);
acl->policy = *uwrq;
/* the ACL code needs an intermediate mlmeautolevel */
if ((priv->iw_mode == IW_MODE_MASTER) &&
(acl->policy != MAC_POLICY_OPEN))
mlmeautolevel = DOT11_MLME_INTERMEDIATE;
else
mlmeautolevel = CARD_DEFAULT_MLME_MODE;
if (priv->wpa)
mlmeautolevel = DOT11_MLME_EXTENDED;
mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
/* restart the card with our new policy */
mgt_commit(priv);
up_write(&priv->mib_sem);
return 0;
}
static int
prism54_get_policy(struct net_device *ndev, struct iw_request_info *info,
__u32 * uwrq, char *extra)
{
islpci_private *priv = ndev->priv;
struct islpci_acl *acl = &priv->acl;
*uwrq = acl->policy;
return 0;
}
/* Return 1 only if client should be accepted. */
static int
prism54_mac_accept(struct islpci_acl *acl, char *mac)
{
struct list_head *ptr;
struct mac_entry *entry;
int res = 0;
if (down_interruptible(&acl->sem))
return -ERESTARTSYS;
if (acl->policy == MAC_POLICY_OPEN) {
up(&acl->sem);
return 1;
}
for (ptr = acl->mac_list.next; ptr != &acl->mac_list; ptr = ptr->next) {
entry = list_entry(ptr, struct mac_entry, _list);
if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
res = 1;
break;
}
}
res = (acl->policy == MAC_POLICY_ACCEPT) ? !res : res;
up(&acl->sem);
return res;
}
static int
prism54_kick_all(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *dwrq, char *extra)
{
struct obj_mlme *mlme;
int rvalue;
mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
if (mlme == NULL)
return -ENOMEM;
/* Tell the card to kick every client */
mlme->id = cpu_to_le16(0);
rvalue = mgt_set_request(ndev->priv, DOT11_OID_DISASSOCIATE, 0, mlme);
kfree(mlme);
return rvalue;
}
static int
prism54_kick_mac(struct net_device *ndev, struct iw_request_info *info,
struct sockaddr *awrq, char *extra)
{
struct obj_mlme *mlme;
struct sockaddr *addr = (struct sockaddr *) extra;
int rvalue;
if (addr->sa_family != ARPHRD_ETHER)
return -EOPNOTSUPP;
mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
if (mlme == NULL)
return -ENOMEM;
/* Tell the card to only kick the corresponding bastard */
memcpy(mlme->address, addr->sa_data, ETH_ALEN);
mlme->id = cpu_to_le16(-1);
rvalue = mgt_set_request(ndev->priv, DOT11_OID_DISASSOCIATE, 0, mlme);
kfree(mlme);
return rvalue;
}
/* Translate a TRAP oid into a wireless event. Called in islpci_mgt_receive. */
static inline void
format_event(islpci_private *priv, char *dest, const char *str,
const struct obj_mlme *mlme, u16 *length, int error)
{
const u8 *a = mlme->address;
int n = snprintf(dest, IW_CUSTOM_MAX,
"%s %s %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X %s",
str,
((priv->iw_mode == IW_MODE_MASTER) ? "to" : "from"),
a[0], a[1], a[2], a[3], a[4], a[5],
(error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ")
: ""));
BUG_ON(n > IW_CUSTOM_MAX);
*length = n;
}
static void
send_formatted_event(islpci_private *priv, const char *str,
const struct obj_mlme *mlme, int error)
{
union iwreq_data wrqu;
wrqu.data.pointer = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
if (!wrqu.data.pointer)
return;
wrqu.data.length = 0;
format_event(priv, wrqu.data.pointer, str, mlme, &wrqu.data.length,
error);
wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, wrqu.data.pointer);
kfree(wrqu.data.pointer);
}
static void
send_simple_event(islpci_private *priv, const char *str)
{
union iwreq_data wrqu;
int n = strlen(str);
wrqu.data.pointer = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
if (!wrqu.data.pointer)
return;
BUG_ON(n > IW_CUSTOM_MAX);
wrqu.data.length = n;
strcpy(wrqu.data.pointer, str);
wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, wrqu.data.pointer);
kfree(wrqu.data.pointer);
}
static void
link_changed(struct net_device *ndev, u32 bitrate)
{
islpci_private *priv = ndev->priv;
if (le32_to_cpu(bitrate)) {
if (priv->iw_mode == IW_MODE_INFRA) {
union iwreq_data uwrq;
prism54_get_wap(ndev, NULL, (struct sockaddr *) &uwrq,
NULL);
wireless_send_event(ndev, SIOCGIWAP, &uwrq, NULL);
} else
send_simple_event(ndev->priv, "Link established");
} else
send_simple_event(ndev->priv, "Link lost");
}
/* Beacon/ProbeResp payload header */
struct ieee80211_beacon_phdr {
u8 timestamp[8];
u16 beacon_int;
u16 capab_info;
} __attribute__ ((packed));
#define WLAN_EID_GENERIC 0xdd
static u8 wpa_oid[4] = { 0x00, 0x50, 0xf2, 1 };
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
void
prism54_wpa_ie_add(islpci_private *priv, u8 *bssid,
u8 *wpa_ie, size_t wpa_ie_len)
{
struct list_head *ptr;
struct islpci_bss_wpa_ie *bss = NULL;
if (wpa_ie_len > MAX_WPA_IE_LEN)
wpa_ie_len = MAX_WPA_IE_LEN;
if (down_interruptible(&priv->wpa_sem))
return;
/* try to use existing entry */
list_for_each(ptr, &priv->bss_wpa_list) {
bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) {
list_move(&bss->list, &priv->bss_wpa_list);
break;
}
bss = NULL;
}
if (bss == NULL) {
/* add a new BSS entry; if max number of entries is already
* reached, replace the least recently updated */
if (priv->num_bss_wpa >= MAX_BSS_WPA_IE_COUNT) {
bss = list_entry(priv->bss_wpa_list.prev,
struct islpci_bss_wpa_ie, list);
list_del(&bss->list);
} else {
bss = kmalloc(sizeof (*bss), GFP_ATOMIC);
if (bss != NULL) {
priv->num_bss_wpa++;
memset(bss, 0, sizeof (*bss));
}
}
if (bss != NULL) {
memcpy(bss->bssid, bssid, ETH_ALEN);
list_add(&bss->list, &priv->bss_wpa_list);
}
}
if (bss != NULL) {
memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len);
bss->wpa_ie_len = wpa_ie_len;
bss->last_update = jiffies;
} else {
printk(KERN_DEBUG "Failed to add BSS WPA entry for " MACSTR
"\n", MAC2STR(bssid));
}
/* expire old entries from WPA list */
while (priv->num_bss_wpa > 0) {
bss = list_entry(priv->bss_wpa_list.prev,
struct islpci_bss_wpa_ie, list);
if (!time_after(jiffies, bss->last_update + 60 * HZ))
break;
list_del(&bss->list);
priv->num_bss_wpa--;
kfree(bss);
}
up(&priv->wpa_sem);
}
size_t
prism54_wpa_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie)
{
struct list_head *ptr;
struct islpci_bss_wpa_ie *bss = NULL;
size_t len = 0;
if (down_interruptible(&priv->wpa_sem))
return 0;
list_for_each(ptr, &priv->bss_wpa_list) {
bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
break;
bss = NULL;
}
if (bss) {
len = bss->wpa_ie_len;
memcpy(wpa_ie, bss->wpa_ie, len);
}
up(&priv->wpa_sem);
return len;
}
void
prism54_wpa_ie_init(islpci_private *priv)
{
INIT_LIST_HEAD(&priv->bss_wpa_list);
sema_init(&priv->wpa_sem, 1);
}
void
prism54_wpa_ie_clean(islpci_private *priv)
{
struct list_head *ptr, *n;
list_for_each_safe(ptr, n, &priv->bss_wpa_list) {
struct islpci_bss_wpa_ie *bss;
bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
kfree(bss);
}
}
static void
prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr,
u8 *payload, size_t len)
{
struct ieee80211_beacon_phdr *hdr;
u8 *pos, *end;
if (!priv->wpa)
return;
hdr = (struct ieee80211_beacon_phdr *) payload;
pos = (u8 *) (hdr + 1);
end = payload + len;
while (pos < end) {
if (pos + 2 + pos[1] > end) {
printk(KERN_DEBUG "Parsing Beacon/ProbeResp failed "
"for " MACSTR "\n", MAC2STR(addr));
return;
}
if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 &&
memcmp(pos + 2, wpa_oid, 4) == 0) {
prism54_wpa_ie_add(priv, addr, pos, pos[1] + 2);
return;
}
pos += 2 + pos[1];
}
}
static void
handle_request(islpci_private *priv, struct obj_mlme *mlme, enum oid_num_t oid)
{
if (((le16_to_cpu(mlme->state) == DOT11_STATE_AUTHING) ||
(le16_to_cpu(mlme->state) == DOT11_STATE_ASSOCING))
&& mgt_mlme_answer(priv)) {
/* Someone is requesting auth and we must respond. Just send back
* the trap with error code set accordingly.
*/
mlme->code = cpu_to_le16(prism54_mac_accept(&priv->acl,
mlme->
address) ? 0 : 1);
mgt_set_request(priv, oid, 0, mlme);
}
}
int
prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
char *data)
{
struct obj_mlme *mlme = (struct obj_mlme *) data;
size_t len;
u8 *payload, *pos = (u8 *) (mlme + 1);
len = pos[0] | (pos[1] << 8); /* little endian data length */
payload = pos + 2;
/* I think all trapable objects are listed here.
* Some oids have a EX version. The difference is that they are emitted
* in DOT11_MLME_EXTENDED mode (set with DOT11_OID_MLMEAUTOLEVEL)
* with more info.
* The few events already defined by the wireless tools are not really
* suited. We use the more flexible custom event facility.
*/
switch (oid) {
case GEN_OID_LINKSTATE:
link_changed(priv->ndev, (u32) *data);
break;
case DOT11_OID_MICFAILURE:
send_simple_event(priv, "Mic failure");
break;
case DOT11_OID_DEAUTHENTICATE:
send_formatted_event(priv, "DeAuthenticate request", mlme, 0);
break;
case DOT11_OID_AUTHENTICATE:
handle_request(priv, mlme, oid);
send_formatted_event(priv, "Authenticate request", mlme, 1);
break;
case DOT11_OID_DISASSOCIATE:
send_formatted_event(priv, "Disassociate request", mlme, 0);
break;
case DOT11_OID_ASSOCIATE:
handle_request(priv, mlme, oid);
send_formatted_event(priv, "Associate request", mlme, 1);
break;
case DOT11_OID_REASSOCIATE:
handle_request(priv, mlme, oid);
send_formatted_event(priv, "ReAssociate request", mlme, 1);
break;
case DOT11_OID_BEACON:
prism54_process_bss_data(priv, oid, mlme->address,
payload, len);
send_formatted_event(priv,
"Received a beacon from an unkown AP",
mlme, 0);
break;
case DOT11_OID_PROBE:
/* we received a probe from a client. */
prism54_process_bss_data(priv, oid, mlme->address,
payload, len);
send_formatted_event(priv, "Received a probe from client", mlme,
0);
break;
/* Note : the following should never happen since we don't run the card in
* extended mode.
* Note : "mlme" is actually a "struct obj_mlmeex *" here, but this
* is backward compatible layout-wise with "struct obj_mlme".
*/
case DOT11_OID_DEAUTHENTICATEEX:
send_formatted_event(priv, "DeAuthenticate request", mlme, 0);
break;
case DOT11_OID_AUTHENTICATEEX:
handle_request(priv, mlme, oid);
send_formatted_event(priv, "Authenticate request", mlme, 1);
break;
case DOT11_OID_DISASSOCIATEEX:
send_formatted_event(priv, "Disassociate request", mlme, 0);
break;
case DOT11_OID_ASSOCIATEEX:
handle_request(priv, mlme, oid);
send_formatted_event(priv, "Associate request", mlme, 1);
break;
case DOT11_OID_REASSOCIATEEX:
handle_request(priv, mlme, oid);
send_formatted_event(priv, "Reassociate request", mlme, 1);
break;
default:
return -EINVAL;
}
return 0;
}
/*
* Process a device trap. This is called via schedule_work(), outside of
* interrupt context, no locks held.
*/
void
prism54_process_trap(void *data)
{
struct islpci_mgmtframe *frame = data;
enum oid_num_t n = mgt_oidtonum(frame->header->oid);
prism54_process_trap_helper(frame->ndev->priv, n, frame->data);
islpci_mgt_release(frame);
}
int
prism54_set_mac_address(struct net_device *ndev, void *addr)
{
islpci_private *priv = ndev->priv;
int ret;
if (ndev->addr_len != 6)
return -EINVAL;
ret = mgt_set_request(priv, GEN_OID_MACADDRESS, 0,
&((struct sockaddr *) addr)->sa_data);
if (!ret)
memcpy(priv->ndev->dev_addr,
&((struct sockaddr *) addr)->sa_data, 6);
return ret;
}
int
prism54_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
{
/* should we really support this old stuff ? */
return -EOPNOTSUPP;
}
int
prism54_set_wpa(struct net_device *ndev, struct iw_request_info *info,
__u32 * uwrq, char *extra)
{
islpci_private *priv = ndev->priv;
down_write(&priv->mib_sem);
priv->wpa = *uwrq;
if (priv->wpa) {
u32 l = DOT11_MLME_EXTENDED;
mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &l);
}
/* restart the card with new level. Needed ? */
mgt_commit(priv);
up_write(&priv->mib_sem);
return 0;
}
int
prism54_get_wpa(struct net_device *ndev, struct iw_request_info *info,
__u32 * uwrq, char *extra)
{
islpci_private *priv = ndev->priv;
*uwrq = priv->wpa;
return 0;
}
int
prism54_oid(struct net_device *ndev, struct iw_request_info *info,
__u32 *uwrq, char *extra)
{
islpci_private *priv = ndev->priv;
priv->priv_oid = *uwrq;
printk("%s: oid 0x%08X\n", ndev->name, *uwrq);
return 0;
}
int
prism54_get_oid(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *data, char *extra)
{
islpci_private *priv = ndev->priv;
struct islpci_mgmtframe *response = NULL;
int ret = -EIO, response_op = PIMFOR_OP_ERROR;
printk("%s: get_oid 0x%08X\n", ndev->name, priv->priv_oid);
data->length = 0;
if (islpci_get_state(priv) >= PRV_STATE_INIT) {
ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET, priv->priv_oid, extra, 256, &response);
response_op = response->header->operation;
printk("%s: ret: %i\n", ndev->name, ret);
printk("%s: response_op: %i\n", ndev->name, response_op);
if (ret || !response || response->header->operation == PIMFOR_OP_ERROR) {
if (response) {
islpci_mgt_release(response);
}
printk("%s: EIO\n", ndev->name);
ret = -EIO;
}
if (!ret) {
data->length = response->header->length;
memcpy(extra, response->data, data->length);
islpci_mgt_release(response);
printk("%s: len: %i\n", ndev->name, data->length);
}
}
return ret;
}
int
prism54_set_oid(struct net_device *ndev, struct iw_request_info *info,
struct iw_point *data, char *extra)
{
islpci_private *priv = ndev->priv;
struct islpci_mgmtframe *response = NULL;
int ret = 0, response_op = PIMFOR_OP_ERROR;
printk("%s: set_oid 0x%08X\tlen: %d\n", ndev->name, priv->priv_oid, data->length);
if (islpci_get_state(priv) >= PRV_STATE_INIT) {
ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, priv->priv_oid, extra, data->length, &response);
printk("%s: ret: %i\n", ndev->name, ret);
if (!ret) {
response_op = response->header->operation;
printk("%s: response_op: %i\n", ndev->name, response_op);
islpci_mgt_release(response);
}
if (ret || response_op == PIMFOR_OP_ERROR) {
printk("%s: EIO\n", ndev->name);
ret = -EIO;
}
}
return ret;
}
#if WIRELESS_EXT > 12
static const iw_handler prism54_handler[] = {
(iw_handler) prism54_commit, /* SIOCSIWCOMMIT */
(iw_handler) prism54_get_name, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
(iw_handler) prism54_set_freq, /* SIOCSIWFREQ */
(iw_handler) prism54_get_freq, /* SIOCGIWFREQ */
(iw_handler) prism54_set_mode, /* SIOCSIWMODE */
(iw_handler) prism54_get_mode, /* SIOCGIWMODE */
(iw_handler) prism54_set_sens, /* SIOCSIWSENS */
(iw_handler) prism54_get_sens, /* SIOCGIWSENS */
(iw_handler) NULL, /* SIOCSIWRANGE */
(iw_handler) prism54_get_range, /* SIOCGIWRANGE */
(iw_handler) NULL, /* SIOCSIWPRIV */
(iw_handler) NULL, /* SIOCGIWPRIV */
(iw_handler) NULL, /* SIOCSIWSTATS */
(iw_handler) NULL, /* SIOCGIWSTATS */
#if WIRELESS_EXT > 15
iw_handler_set_spy, /* SIOCSIWSPY */
iw_handler_get_spy, /* SIOCGIWSPY */
iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
#else /* WIRELESS_EXT > 15 */
(iw_handler) NULL, /* SIOCSIWSPY */
(iw_handler) NULL, /* SIOCGIWSPY */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
#endif /* WIRELESS_EXT > 15 */
(iw_handler) prism54_set_wap, /* SIOCSIWAP */
(iw_handler) prism54_get_wap, /* SIOCGIWAP */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* SIOCGIWAPLIST depreciated */
#if WIRELESS_EXT > 13
(iw_handler) prism54_set_scan, /* SIOCSIWSCAN */
(iw_handler) prism54_get_scan, /* SIOCGIWSCAN */
#else /* WIRELESS_EXT > 13 */
(iw_handler) NULL, /* SIOCSIWSCAN */
(iw_handler) NULL, /* SIOCGIWSCAN */
#endif /* WIRELESS_EXT > 13 */
(iw_handler) prism54_set_essid, /* SIOCSIWESSID */
(iw_handler) prism54_get_essid, /* SIOCGIWESSID */
(iw_handler) prism54_set_nick, /* SIOCSIWNICKN */
(iw_handler) prism54_get_nick, /* SIOCGIWNICKN */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) prism54_set_rate, /* SIOCSIWRATE */
(iw_handler) prism54_get_rate, /* SIOCGIWRATE */
(iw_handler) prism54_set_rts, /* SIOCSIWRTS */
(iw_handler) prism54_get_rts, /* SIOCGIWRTS */
(iw_handler) prism54_set_frag, /* SIOCSIWFRAG */
(iw_handler) prism54_get_frag, /* SIOCGIWFRAG */
(iw_handler) prism54_set_txpower, /* SIOCSIWTXPOW */
(iw_handler) prism54_get_txpower, /* SIOCGIWTXPOW */
(iw_handler) prism54_set_retry, /* SIOCSIWRETRY */
(iw_handler) prism54_get_retry, /* SIOCGIWRETRY */
(iw_handler) prism54_set_encode, /* SIOCSIWENCODE */
(iw_handler) prism54_get_encode, /* SIOCGIWENCODE */
(iw_handler) NULL, /* SIOCSIWPOWER */
(iw_handler) NULL, /* SIOCGIWPOWER */
};
/* The low order bit identify a SET (0) or a GET (1) ioctl. */
#define PRISM54_RESET SIOCIWFIRSTPRIV
#define PRISM54_GET_BEACON SIOCIWFIRSTPRIV+1
#define PRISM54_SET_BEACON SIOCIWFIRSTPRIV+2
#define PRISM54_GET_POLICY SIOCIWFIRSTPRIV+3
#define PRISM54_SET_POLICY SIOCIWFIRSTPRIV+4
#define PRISM54_GET_MAC SIOCIWFIRSTPRIV+5
#define PRISM54_ADD_MAC SIOCIWFIRSTPRIV+6
#define PRISM54_DEL_MAC SIOCIWFIRSTPRIV+8
#define PRISM54_KICK_MAC SIOCIWFIRSTPRIV+10
#define PRISM54_KICK_ALL SIOCIWFIRSTPRIV+12
#define PRISM54_GET_WPA SIOCIWFIRSTPRIV+13
#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+14
#define PRISM54_OID SIOCIWFIRSTPRIV+16
#define PRISM54_GET_OID SIOCIWFIRSTPRIV+17
#define PRISM54_SET_OID SIOCIWFIRSTPRIV+18
static const struct iw_priv_args prism54_private_args[] = {
/*{ cmd, set_args, get_args, name } */
{PRISM54_RESET, 0, 0, "reset"},
{PRISM54_GET_BEACON, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"getBeaconPeriod"},
{PRISM54_SET_BEACON, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
"setBeaconPeriod"},
{PRISM54_GET_POLICY, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"getPolicy"},
{PRISM54_SET_POLICY, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
"setPolicy"},
{PRISM54_GET_MAC, 0, IW_PRIV_TYPE_ADDR | 64, "getMac"},
{PRISM54_ADD_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
"addMac"},
{PRISM54_DEL_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
"delMac"},
{PRISM54_KICK_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
"kickMac"},
{PRISM54_KICK_ALL, 0, 0, "kickAll"},
{PRISM54_GET_WPA, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_wpa"},
{PRISM54_SET_WPA, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
"set_wpa"},
{PRISM54_OID, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "oid"},
{PRISM54_GET_OID, 0, IW_PRIV_TYPE_BYTE | 256, "get_oid"},
{PRISM54_SET_OID, IW_PRIV_TYPE_BYTE | 256, 0, "set_oid"},
};
static const iw_handler prism54_private_handler[] = {
(iw_handler) prism54_reset,
(iw_handler) prism54_get_beacon,
(iw_handler) prism54_set_beacon,
(iw_handler) prism54_get_policy,
(iw_handler) prism54_set_policy,
(iw_handler) prism54_get_mac,
(iw_handler) prism54_add_mac,
(iw_handler) NULL,
(iw_handler) prism54_del_mac,
(iw_handler) NULL,
(iw_handler) prism54_kick_mac,
(iw_handler) NULL,
(iw_handler) prism54_kick_all,
(iw_handler) prism54_get_wpa,
(iw_handler) prism54_set_wpa,
(iw_handler) NULL,
(iw_handler) prism54_oid,
(iw_handler) prism54_get_oid,
(iw_handler) prism54_set_oid,
};
const struct iw_handler_def prism54_handler_def = {
.num_standard = sizeof (prism54_handler) / sizeof (iw_handler),
.num_private = sizeof (prism54_private_handler) / sizeof (iw_handler),
.num_private_args =
sizeof (prism54_private_args) / sizeof (struct iw_priv_args),
.standard = (iw_handler *) prism54_handler,
.private = (iw_handler *) prism54_private_handler,
.private_args = (struct iw_priv_args *) prism54_private_args,
};
#endif /* WIRELESS_EXT > 12 */
/* $Header: /var/lib/cvs/prism54-ng/ksrc/isl_ioctl.h,v 1.30 2004/01/30 16:24:00 ajfa Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
* (C) 2003 Aurelien Alleaume <slts@free.fr>
* (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _ISL_IOCTL_H
#define _ISL_IOCTL_H
#include "islpci_mgt.h"
#include "islpci_dev.h"
#if WIRELESS_EXT > 12
#include <net/iw_handler.h> /* New driver API */
#endif /* WIRELESS_EXT > 12 */
#define SUPPORTED_WIRELESS_EXT 16
void prism54_mib_init(islpci_private *);
void prism54_mib_init_work(islpci_private *);
struct iw_statistics *prism54_get_wireless_stats(struct net_device *);
void prism54_update_stats(islpci_private *);
void prism54_acl_init(struct islpci_acl *);
void prism54_acl_clean(struct islpci_acl *);
void prism54_process_trap(void *);
void prism54_wpa_ie_init(islpci_private *priv);
void prism54_wpa_ie_clean(islpci_private *priv);
void prism54_wpa_ie_add(islpci_private *priv, u8 *bssid,
u8 *wpa_ie, size_t wpa_ie_len);
size_t prism54_wpa_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie);
int prism54_set_mac_address(struct net_device *, void *);
int prism54_ioctl(struct net_device *, struct ifreq *, int);
#if WIRELESS_EXT > 12
extern const struct iw_handler_def prism54_handler_def;
#endif /* WIRELESS_EXT > 12 */
#endif /* _ISL_IOCTL_H */
/*
* $Id: isl_oid.h,v 1.2 2004/01/30 16:24:00 ajfa Exp $
*
* Copyright (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#if !defined(_ISL_OID_H)
#define _ISL_OID_H
/*
* MIB related constant and structure definitions for communicating
* with the device firmware
*/
struct obj_ssid {
u8 length;
char octets[33];
} __attribute__ ((packed));
struct obj_key {
u8 type; /* dot11_priv_t */
u8 length;
char key[32];
} __attribute__ ((packed));
struct obj_mlme {
u8 address[6];
u16 id;
u16 state;
u16 code;
} __attribute__ ((packed));
struct obj_mlmeex {
u8 address[6];
u16 id;
u16 state;
u16 code;
u16 size;
u8 data[0];
} __attribute__ ((packed));
struct obj_buffer {
u32 size;
u32 addr; /* 32bit bus address */
} __attribute__ ((packed));
struct obj_bss {
u8 address[6];
int:16; /* padding */
char state;
char reserved;
short age;
char quality;
char rssi;
struct obj_ssid ssid;
short channel;
char beacon_period;
char dtim_period;
short capinfo;
short rates;
short basic_rates;
int:16; /* padding */
} __attribute__ ((packed));
struct obj_bsslist {
u32 nr;
struct obj_bss bsslist[0];
} __attribute__ ((packed));
struct obj_frequencies {
u16 nr;
u16 mhz[0];
} __attribute__ ((packed));
/*
* in case everything's ok, the inlined function below will be
* optimized away by the compiler...
*/
static inline void
__bug_on_wrong_struct_sizes(void)
{
BUG_ON(sizeof (struct obj_ssid) != 34);
BUG_ON(sizeof (struct obj_key) != 34);
BUG_ON(sizeof (struct obj_mlme) != 12);
BUG_ON(sizeof (struct obj_mlmeex) != 14);
BUG_ON(sizeof (struct obj_buffer) != 8);
BUG_ON(sizeof (struct obj_bss) != 60);
BUG_ON(sizeof (struct obj_bsslist) != 4);
BUG_ON(sizeof (struct obj_frequencies) != 2);
}
enum dot11_state_t {
DOT11_STATE_NONE = 0,
DOT11_STATE_AUTHING = 1,
DOT11_STATE_AUTH = 2,
DOT11_STATE_ASSOCING = 3,
DOT11_STATE_ASSOC = 5,
DOT11_STATE_IBSS = 6,
DOT11_STATE_WDS = 7
};
enum dot11_bsstype_t {
DOT11_BSSTYPE_NONE = 0,
DOT11_BSSTYPE_INFRA = 1,
DOT11_BSSTYPE_IBSS = 2,
DOT11_BSSTYPE_ANY = 3
};
enum dot11_auth_t {
DOT11_AUTH_NONE = 0,
DOT11_AUTH_OS = 1,
DOT11_AUTH_SK = 2,
DOT11_AUTH_BOTH = 3
};
enum dot11_mlme_t {
DOT11_MLME_AUTO = 0,
DOT11_MLME_INTERMEDIATE = 1,
DOT11_MLME_EXTENDED = 2
};
enum dot11_priv_t {
DOT11_PRIV_WEP = 0,
DOT11_PRIV_TKIP = 1
};
/* The dot11d conformance level configures the 802.11d conformance levels.
* The following conformance levels exist:*/
enum oid_inl_conformance_t {
OID_INL_CONFORMANCE_NONE = 0, /* Perform active scanning */
OID_INL_CONFORMANCE_STRICT = 1, /* Strictly adhere to 802.11d */
OID_INL_CONFORMANCE_FLEXIBLE = 2, /* Use passed 802.11d info to
* determine channel AND/OR just make
* assumption that active
* channels are valid channels */
};
enum oid_inl_mode_t {
INL_MODE_NONE = -1,
INL_MODE_PROMISCUOUS = 0,
INL_MODE_CLIENT = 1,
INL_MODE_AP = 2,
INL_MODE_SNIFFER = 3
};
enum oid_inl_config_t {
INL_CONFIG_NOTHING = 0x00,
INL_CONFIG_MANUALRUN = 0x01,
INL_CONFIG_FRAMETRAP = 0x02,
INL_CONFIG_RXANNEX = 0x04,
INL_CONFIG_TXANNEX = 0x08,
INL_CONFIG_WDS = 0x10
};
enum oid_inl_phycap_t {
INL_PHYCAP_2400MHZ = 1,
INL_PHYCAP_5000MHZ = 2,
INL_PHYCAP_FAA = 0x80000000, /* Means card supports the FAA switch */
};
enum oid_num_t {
GEN_OID_MACADDRESS = 0,
GEN_OID_LINKSTATE,
GEN_OID_WATCHDOG,
GEN_OID_MIBOP,
GEN_OID_OPTIONS,
GEN_OID_LEDCONFIG,
/* 802.11 */
DOT11_OID_BSSTYPE,
DOT11_OID_BSSID,
DOT11_OID_SSID,
DOT11_OID_STATE,
DOT11_OID_AID,
DOT11_OID_COUNTRYSTRING,
DOT11_OID_SSIDOVERRIDE,
DOT11_OID_MEDIUMLIMIT,
DOT11_OID_BEACONPERIOD,
DOT11_OID_DTIMPERIOD,
DOT11_OID_ATIMWINDOW,
DOT11_OID_LISTENINTERVAL,
DOT11_OID_CFPPERIOD,
DOT11_OID_CFPDURATION,
DOT11_OID_AUTHENABLE,
DOT11_OID_PRIVACYINVOKED,
DOT11_OID_EXUNENCRYPTED,
DOT11_OID_DEFKEYID,
DOT11_OID_DEFKEYX, /* DOT11_OID_DEFKEY1,...DOT11_OID_DEFKEY4 */
DOT11_OID_STAKEY,
DOT11_OID_REKEYTHRESHOLD,
DOT11_OID_STASC,
DOT11_OID_PRIVTXREJECTED,
DOT11_OID_PRIVRXPLAIN,
DOT11_OID_PRIVRXFAILED,
DOT11_OID_PRIVRXNOKEY,
DOT11_OID_RTSTHRESH,
DOT11_OID_FRAGTHRESH,
DOT11_OID_SHORTRETRIES,
DOT11_OID_LONGRETRIES,
DOT11_OID_MAXTXLIFETIME,
DOT11_OID_MAXRXLIFETIME,
DOT11_OID_AUTHRESPTIMEOUT,
DOT11_OID_ASSOCRESPTIMEOUT,
DOT11_OID_ALOFT_TABLE,
DOT11_OID_ALOFT_CTRL_TABLE,
DOT11_OID_ALOFT_RETREAT,
DOT11_OID_ALOFT_PROGRESS,
DOT11_OID_ALOFT_FIXEDRATE,
DOT11_OID_ALOFT_RSSIGRAPH,
DOT11_OID_ALOFT_CONFIG,
DOT11_OID_VDCFX,
DOT11_OID_MAXFRAMEBURST,
DOT11_OID_PSM,
DOT11_OID_CAMTIMEOUT,
DOT11_OID_RECEIVEDTIMS,
DOT11_OID_ROAMPREFERENCE,
DOT11_OID_BRIDGELOCAL,
DOT11_OID_CLIENTS,
DOT11_OID_CLIENTSASSOCIATED,
DOT11_OID_CLIENTX, /* DOT11_OID_CLIENTX,...DOT11_OID_CLIENT2007 */
DOT11_OID_CLIENTFIND,
DOT11_OID_WDSLINKADD,
DOT11_OID_WDSLINKREMOVE,
DOT11_OID_EAPAUTHSTA,
DOT11_OID_EAPUNAUTHSTA,
DOT11_OID_DOT1XENABLE,
DOT11_OID_MICFAILURE,
DOT11_OID_REKEYINDICATE,
DOT11_OID_MPDUTXSUCCESSFUL,
DOT11_OID_MPDUTXONERETRY,
DOT11_OID_MPDUTXMULTIPLERETRIES,
DOT11_OID_MPDUTXFAILED,
DOT11_OID_MPDURXSUCCESSFUL,
DOT11_OID_MPDURXDUPS,
DOT11_OID_RTSSUCCESSFUL,
DOT11_OID_RTSFAILED,
DOT11_OID_ACKFAILED,
DOT11_OID_FRAMERECEIVES,
DOT11_OID_FRAMEERRORS,
DOT11_OID_FRAMEABORTS,
DOT11_OID_FRAMEABORTSPHY,
DOT11_OID_SLOTTIME,
DOT11_OID_CWMIN,
DOT11_OID_CWMAX,
DOT11_OID_ACKWINDOW,
DOT11_OID_ANTENNARX,
DOT11_OID_ANTENNATX,
DOT11_OID_ANTENNADIVERSITY,
DOT11_OID_CHANNEL,
DOT11_OID_EDTHRESHOLD,
DOT11_OID_PREAMBLESETTINGS,
DOT11_OID_RATES,
DOT11_OID_CCAMODESUPPORTED,
DOT11_OID_CCAMODE,
DOT11_OID_RSSIVECTOR,
DOT11_OID_OUTPUTPOWERTABLE,
DOT11_OID_OUTPUTPOWER,
DOT11_OID_SUPPORTEDRATES,
DOT11_OID_FREQUENCY,
DOT11_OID_SUPPORTEDFREQUENCIES,
DOT11_OID_NOISEFLOOR,
DOT11_OID_FREQUENCYACTIVITY,
DOT11_OID_IQCALIBRATIONTABLE,
DOT11_OID_NONERPPROTECTION,
DOT11_OID_SLOTSETTINGS,
DOT11_OID_NONERPTIMEOUT,
DOT11_OID_PROFILES,
DOT11_OID_EXTENDEDRATES,
DOT11_OID_DEAUTHENTICATE,
DOT11_OID_AUTHENTICATE,
DOT11_OID_DISASSOCIATE,
DOT11_OID_ASSOCIATE,
DOT11_OID_SCAN,
DOT11_OID_BEACON,
DOT11_OID_PROBE,
DOT11_OID_DEAUTHENTICATEEX,
DOT11_OID_AUTHENTICATEEX,
DOT11_OID_DISASSOCIATEEX,
DOT11_OID_ASSOCIATEEX,
DOT11_OID_REASSOCIATE,
DOT11_OID_REASSOCIATEEX,
DOT11_OID_NONERPSTATUS,
DOT11_OID_STATIMEOUT,
DOT11_OID_MLMEAUTOLEVEL,
DOT11_OID_BSSTIMEOUT,
DOT11_OID_ATTACHMENT,
DOT11_OID_PSMBUFFER,
DOT11_OID_BSSS,
DOT11_OID_BSSX, /*DOT11_OID_BSS1,...,DOT11_OID_BSS64 */
DOT11_OID_BSSFIND,
DOT11_OID_BSSLIST,
OID_INL_TUNNEL,
OID_INL_MEMADDR,
OID_INL_MEMORY,
OID_INL_MODE,
OID_INL_COMPONENT_NR,
OID_INL_VERSION,
OID_INL_INTERFACE_ID,
OID_INL_COMPONENT_ID,
OID_INL_CONFIG,
OID_INL_DOT11D_CONFORMANCE,
OID_INL_PHYCAPABILITIES,
OID_INL_OUTPUTPOWER,
OID_NUM_LAST
};
/* We could add more flags. eg: in which mode are they allowed, ro, rw, ...*/
#define OID_FLAG_CACHED 0x01
#define OID_FLAG_U32 0x02
#define OID_FLAG_MLMEEX 0x04 /* this type is special because of a variable
size field when sending. Not yet implemented (not used in driver). */
struct oid_t {
enum oid_num_t oid;
short range; /* to define a range of oid */
short size; /* size of the associated data */
char flags;
};
union oid_res_t {
void *ptr;
u32 u;
};
#define IWMAX_BITRATES 20
#define IWMAX_BSS 24
#define IWMAX_FREQ 30
#endif /* !defined(_ISL_OID_H) */
/* EOF */
/* $Header: /var/lib/cvs/prism54-ng/ksrc/islpci_dev.c,v 1.68 2004/02/28 03:06:07 mcgrof Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
* Copyright (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
* Copyright (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/etherdevice.h>
#include <linux/delay.h>
#include <linux/if_arp.h>
#include <asm/io.h>
#include "isl_38xx.h"
#include "isl_ioctl.h"
#include "islpci_dev.h"
#include "islpci_mgt.h"
#include "islpci_eth.h"
#include "oid_mgt.h"
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
#define prism54_synchronize_irq(irq) synchronize_irq()
#else
#define prism54_synchronize_irq(irq) synchronize_irq(irq)
#endif
#define ISL3877_IMAGE_FILE "isl3877"
#define ISL3890_IMAGE_FILE "isl3890"
/* Temporary dummy MAC address to use until firmware is loaded.
* The idea there is that some tools (such as nameif) may query
* the MAC address before the netdev is 'open'. By using a valid
* OUI prefix, they can process the netdev properly.
* Of course, this is not the final/real MAC address. It doesn't
* matter, as you are suppose to be able to change it anytime via
* ndev->set_mac_address. Jean II */
const unsigned char dummy_mac[6] = { 0x00, 0x30, 0xB4, 0x00, 0x00, 0x00 };
/******************************************************************************
Device Interrupt Handler
******************************************************************************/
irqreturn_t
islpci_interrupt(int irq, void *config, struct pt_regs *regs)
{
u32 reg;
islpci_private *priv = config;
struct net_device *ndev = priv->ndev;
void *device = priv->device_base;
int powerstate = ISL38XX_PSM_POWERSAVE_STATE;
/* received an interrupt request on a shared IRQ line
* first check whether the device is in sleep mode */
reg = readl(device + ISL38XX_CTRL_STAT_REG);
if (reg & ISL38XX_CTRL_STAT_SLEEPMODE)
/* device is in sleep mode, IRQ was generated by someone else */
{
printk(KERN_DEBUG "Assuming someone else called the IRQ\n");
return IRQ_NONE;
}
if (islpci_get_state(priv) != PRV_STATE_SLEEP)
powerstate = ISL38XX_PSM_ACTIVE_STATE;
/* lock the interrupt handler */
spin_lock(&priv->slock);
/* check whether there is any source of interrupt on the device */
reg = readl(device + ISL38XX_INT_IDENT_REG);
/* also check the contents of the Interrupt Enable Register, because this
* will filter out interrupt sources from other devices on the same irq ! */
reg &= readl(device + ISL38XX_INT_EN_REG);
reg &= ISL38XX_INT_SOURCES;
if (reg != 0) {
/* reset the request bits in the Identification register */
isl38xx_w32_flush(device, reg, ISL38XX_INT_ACK_REG);
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS,
"IRQ: Identification register 0x%p 0x%x \n", device, reg);
#endif
/* check for each bit in the register separately */
if (reg & ISL38XX_INT_IDENT_UPDATE) {
#if VERBOSE > SHOW_ERROR_MESSAGES
/* Queue has been updated */
DEBUG(SHOW_TRACING, "IRQ: Update flag \n");
DEBUG(SHOW_QUEUE_INDEXES,
"CB drv Qs: [%i][%i][%i][%i][%i][%i]\n",
le32_to_cpu(priv->control_block->
driver_curr_frag[0]),
le32_to_cpu(priv->control_block->
driver_curr_frag[1]),
le32_to_cpu(priv->control_block->
driver_curr_frag[2]),
le32_to_cpu(priv->control_block->
driver_curr_frag[3]),
le32_to_cpu(priv->control_block->
driver_curr_frag[4]),
le32_to_cpu(priv->control_block->
driver_curr_frag[5])
);
DEBUG(SHOW_QUEUE_INDEXES,
"CB dev Qs: [%i][%i][%i][%i][%i][%i]\n",
le32_to_cpu(priv->control_block->
device_curr_frag[0]),
le32_to_cpu(priv->control_block->
device_curr_frag[1]),
le32_to_cpu(priv->control_block->
device_curr_frag[2]),
le32_to_cpu(priv->control_block->
device_curr_frag[3]),
le32_to_cpu(priv->control_block->
device_curr_frag[4]),
le32_to_cpu(priv->control_block->
device_curr_frag[5])
);
#endif
/* cleanup the data low transmit queue */
islpci_eth_cleanup_transmit(priv, priv->control_block);
/* device is in active state, update the
* powerstate flag if necessary */
powerstate = ISL38XX_PSM_ACTIVE_STATE;
/* check all three queues in priority order
* call the PIMFOR receive function until the
* queue is empty */
if (isl38xx_in_queue(priv->control_block,
ISL38XX_CB_RX_MGMTQ) != 0) {
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING,
"Received frame in Management Queue\n");
#endif
islpci_mgt_receive(ndev);
islpci_mgt_cleanup_transmit(ndev);
/* Refill slots in receive queue */
islpci_mgmt_rx_fill(ndev);
/* no need to trigger the device, next
islpci_mgt_transaction does it */
}
while (isl38xx_in_queue(priv->control_block,
ISL38XX_CB_RX_DATA_LQ) != 0) {
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING,
"Received frame in Data Low Queue \n");
#endif
islpci_eth_receive(priv);
}
/* check whether the data transmit queues were full */
if (priv->data_low_tx_full) {
/* check whether the transmit is not full anymore */
if (ISL38XX_CB_TX_QSIZE -
isl38xx_in_queue(priv->control_block,
ISL38XX_CB_TX_DATA_LQ) >=
ISL38XX_MIN_QTHRESHOLD) {
/* nope, the driver is ready for more network frames */
netif_wake_queue(priv->ndev);
/* reset the full flag */
priv->data_low_tx_full = 0;
}
}
}
if (reg & ISL38XX_INT_IDENT_INIT) {
/* Device has been initialized */
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING,
"IRQ: Init flag, device initialized \n");
#endif
wake_up(&priv->reset_done);
}
if (reg & ISL38XX_INT_IDENT_SLEEP) {
/* Device intends to move to powersave state */
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "IRQ: Sleep flag \n");
#endif
isl38xx_handle_sleep_request(priv->control_block,
&powerstate,
priv->device_base);
}
if (reg & ISL38XX_INT_IDENT_WAKEUP) {
/* Device has been woken up to active state */
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "IRQ: Wakeup flag \n");
#endif
isl38xx_handle_wakeup(priv->control_block,
&powerstate, priv->device_base);
}
}
/* sleep -> ready */
if (islpci_get_state(priv) == PRV_STATE_SLEEP
&& powerstate == ISL38XX_PSM_ACTIVE_STATE)
islpci_set_state(priv, PRV_STATE_READY);
/* !sleep -> sleep */
if (islpci_get_state(priv) != PRV_STATE_SLEEP
&& powerstate == ISL38XX_PSM_POWERSAVE_STATE)
islpci_set_state(priv, PRV_STATE_SLEEP);
/* unlock the interrupt handler */
spin_unlock(&priv->slock);
return IRQ_HANDLED;
}
/******************************************************************************
Network Interface Control & Statistical functions
******************************************************************************/
static int
islpci_open(struct net_device *ndev)
{
u32 rc;
islpci_private *priv = ndev->priv;
printk(KERN_DEBUG "%s: islpci_open()\n", ndev->name);
/* reset data structures, upload firmware and reset device */
rc = islpci_reset(priv,1);
if (rc) {
prism54_bring_down(priv);
return rc; /* Returns informative message */
}
netif_start_queue(ndev);
/* netif_mark_up( ndev ); */
return 0;
}
static int
islpci_close(struct net_device *ndev)
{
islpci_private *priv = ndev->priv;
printk(KERN_DEBUG "%s: islpci_close ()\n", ndev->name);
netif_stop_queue(ndev);
return prism54_bring_down(priv);
}
int
prism54_bring_down(islpci_private *priv)
{
void *device_base = priv->device_base;
u32 reg;
/* we are going to shutdown the device */
islpci_set_state(priv, PRV_STATE_PREBOOT);
/* disable all device interrupts in case they weren't */
isl38xx_disable_interrupts(priv->device_base);
/* For safety reasons, we may want to ensure that no DMA transfer is
* currently in progress by emptying the TX and RX queues. */
/* wait until interrupts have finished executing on other CPUs */
prism54_synchronize_irq(priv->pdev->irq);
reg = readl(device_base + ISL38XX_CTRL_STAT_REG);
reg &= ~(ISL38XX_CTRL_STAT_RESET | ISL38XX_CTRL_STAT_RAMBOOT);
writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
wmb();
udelay(ISL38XX_WRITEIO_DELAY);
reg |= ISL38XX_CTRL_STAT_RESET;
writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
wmb();
udelay(ISL38XX_WRITEIO_DELAY);
/* clear the Reset bit */
reg &= ~ISL38XX_CTRL_STAT_RESET;
writel(reg, device_base + ISL38XX_CTRL_STAT_REG);
wmb();
/* wait a while for the device to reset */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(50*HZ/1000);
return 0;
}
static int
islpci_upload_fw(islpci_private *priv)
{
islpci_state_t old_state;
u32 rc;
old_state = islpci_set_state(priv, PRV_STATE_BOOT);
printk(KERN_DEBUG "%s: uploading firmware...\n", priv->ndev->name);
rc = isl38xx_upload_firmware(priv->firmware,
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,75))
&priv->pdev->dev,
#else
pci_name(priv->pdev),
#endif
priv->device_base,
priv->device_host_address);
if (rc) {
/* error uploading the firmware */
printk(KERN_ERR "%s: could not upload firmware ('%s')\n",
priv->ndev->name, priv->firmware);
islpci_set_state(priv, old_state);
return rc;
}
printk(KERN_DEBUG
"%s: firmware uploaded done, now triggering reset...\n",
priv->ndev->name);
islpci_set_state(priv, PRV_STATE_POSTBOOT);
return 0;
}
static int
islpci_reset_if(islpci_private *priv)
{
long remaining;
int result = -ETIME;
int count;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
/* This is 2.6 specific, nicer, shorter, but not in 2.4 yet */
DEFINE_WAIT(wait);
prepare_to_wait(&priv->reset_done, &wait, TASK_UNINTERRUPTIBLE);
#else
DECLARE_WAITQUEUE(wait, current);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&priv->reset_done, &wait);
#endif
/* now the last step is to reset the interface */
isl38xx_interface_reset(priv->device_base, priv->device_host_address);
islpci_set_state(priv, PRV_STATE_PREINIT);
for(count = 0; count < 2 && result; count++) {
/* The software reset acknowledge needs about 220 msec here.
* Be conservative and wait for up to one second. */
remaining = schedule_timeout(HZ);
if(remaining > 0) {
result = 0;
break;
}
/* If we're here it's because our IRQ hasn't yet gone through.
* Retry a bit more...
*/
printk(KERN_ERR "%s: device soft reset timed out\n",
priv->ndev->name);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
/* 2.6 specific too */
finish_wait(&priv->reset_done, &wait);
#else
remove_wait_queue(&priv->reset_done, &wait);
set_current_state(TASK_RUNNING);
#endif
if(result)
return result;
islpci_set_state(priv, PRV_STATE_INIT);
/* Now that the device is 100% up, let's allow
* for the other interrupts --
* NOTE: this is not *yet* true since we've only allowed the
* INIT interrupt on the IRQ line. We can perhaps poll
* the IRQ line until we know for sure the reset went through */
isl38xx_enable_common_interrupts(priv->device_base);
prism54_mib_init_work(priv);
islpci_set_state(priv, PRV_STATE_READY);
return 0;
}
int
islpci_reset(islpci_private *priv, int reload_firmware)
{
isl38xx_control_block *cb = /* volatile not needed */
(isl38xx_control_block *) priv->control_block;
unsigned counter;
int rc;
if (reload_firmware)
islpci_set_state(priv, PRV_STATE_PREBOOT);
else
islpci_set_state(priv, PRV_STATE_POSTBOOT);
printk(KERN_DEBUG "%s: resetting device...\n", priv->ndev->name);
/* disable all device interrupts in case they weren't */
isl38xx_disable_interrupts(priv->device_base);
/* flush all management queues */
priv->index_mgmt_tx = 0;
priv->index_mgmt_rx = 0;
/* clear the indexes in the frame pointer */
for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) {
cb->driver_curr_frag[counter] = cpu_to_le32(0);
cb->device_curr_frag[counter] = cpu_to_le32(0);
}
/* reset the mgmt receive queue */
for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) {
isl38xx_fragment *frag = &cb->rx_data_mgmt[counter];
frag->size = MGMT_FRAME_SIZE;
frag->flags = 0;
frag->address = priv->mgmt_rx[counter].pci_addr;
}
for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
cb->rx_data_low[counter].address =
cpu_to_le32((u32) priv->pci_map_rx_address[counter]);
}
/* since the receive queues are filled with empty fragments, now we can
* set the corresponding indexes in the Control Block */
priv->control_block->driver_curr_frag[ISL38XX_CB_RX_DATA_LQ] =
cpu_to_le32(ISL38XX_CB_RX_QSIZE);
priv->control_block->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] =
cpu_to_le32(ISL38XX_CB_MGMT_QSIZE);
/* reset the remaining real index registers and full flags */
priv->free_data_rx = 0;
priv->free_data_tx = 0;
priv->data_low_tx_full = 0;
if (reload_firmware) { /* Should we load the firmware ? */
/* now that the data structures are cleaned up, upload
* firmware and reset interface */
rc = islpci_upload_fw(priv);
if (rc)
return rc;
}
/* finally reset interface */
rc = islpci_reset_if(priv);
if (!rc) /* If successful */
return rc;
printk(KERN_DEBUG "prism54: Your card/socket may be faulty, or IRQ line too busy :(\n");
return rc;
}
struct net_device_stats *
islpci_statistics(struct net_device *ndev)
{
islpci_private *priv = ndev->priv;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "islpci_statistics \n");
#endif
return &priv->statistics;
}
/******************************************************************************
Network device configuration functions
******************************************************************************/
int
islpci_alloc_memory(islpci_private *priv)
{
int counter;
#if VERBOSE > SHOW_ERROR_MESSAGES
printk(KERN_DEBUG "islpci_alloc_memory\n");
#endif
/* remap the PCI device base address to accessable */
if (!(priv->device_base =
ioremap(pci_resource_start(priv->pdev, 0),
ISL38XX_PCI_MEM_SIZE))) {
/* error in remapping the PCI device memory address range */
printk(KERN_ERR "PCI memory remapping failed \n");
return -1;
}
/* memory layout for consistent DMA region:
*
* Area 1: Control Block for the device interface
* Area 2: Power Save Mode Buffer for temporary frame storage. Be aware that
* the number of supported stations in the AP determines the minimal
* size of the buffer !
*/
/* perform the allocation */
priv->driver_mem_address = pci_alloc_consistent(priv->pdev,
HOST_MEM_BLOCK,
&priv->
device_host_address);
if (!priv->driver_mem_address) {
/* error allocating the block of PCI memory */
printk(KERN_ERR "%s: could not allocate DMA memory, aborting!",
"prism54");
return -1;
}
/* assign the Control Block to the first address of the allocated area */
priv->control_block =
(isl38xx_control_block *) priv->driver_mem_address;
/* set the Power Save Buffer pointer directly behind the CB */
priv->device_psm_buffer =
priv->device_host_address + CONTROL_BLOCK_SIZE;
/* make sure all buffer pointers are initialized */
for (counter = 0; counter < ISL38XX_CB_QCOUNT; counter++) {
priv->control_block->driver_curr_frag[counter] = cpu_to_le32(0);
priv->control_block->device_curr_frag[counter] = cpu_to_le32(0);
}
priv->index_mgmt_rx = 0;
memset(priv->mgmt_rx, 0, sizeof(priv->mgmt_rx));
memset(priv->mgmt_tx, 0, sizeof(priv->mgmt_tx));
/* allocate rx queue for management frames */
if (islpci_mgmt_rx_fill(priv->ndev) < 0)
goto out_free;
/* now get the data rx skb's */
memset(priv->data_low_rx, 0, sizeof (priv->data_low_rx));
memset(priv->pci_map_rx_address, 0, sizeof (priv->pci_map_rx_address));
for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
struct sk_buff *skb;
/* allocate an sk_buff for received data frames storage
* each frame on receive size consists of 1 fragment
* include any required allignment operations */
if (!(skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2))) {
/* error allocating an sk_buff structure elements */
printk(KERN_ERR "Error allocating skb.\n");
goto out_free;
}
/* add the new allocated sk_buff to the buffer array */
priv->data_low_rx[counter] = skb;
/* map the allocated skb data area to pci */
priv->pci_map_rx_address[counter] =
pci_map_single(priv->pdev, (void *) skb->data,
MAX_FRAGMENT_SIZE_RX + 2,
PCI_DMA_FROMDEVICE);
if (!priv->pci_map_rx_address[counter]) {
/* error mapping the buffer to device
accessable memory address */
printk(KERN_ERR "failed to map skb DMA'able\n");
goto out_free;
}
}
prism54_acl_init(&priv->acl);
prism54_wpa_ie_init(priv);
if (mgt_init(priv))
goto out_free;
return 0;
out_free:
islpci_free_memory(priv);
return -1;
}
int
islpci_free_memory(islpci_private *priv)
{
int counter;
if (priv->device_base)
iounmap(priv->device_base);
priv->device_base = 0;
/* free consistent DMA area... */
if (priv->driver_mem_address)
pci_free_consistent(priv->pdev, HOST_MEM_BLOCK,
priv->driver_mem_address,
priv->device_host_address);
/* clear some dangling pointers */
priv->driver_mem_address = 0;
priv->device_host_address = 0;
priv->device_psm_buffer = 0;
priv->control_block = 0;
/* clean up mgmt rx buffers */
for (counter = 0; counter < ISL38XX_CB_MGMT_QSIZE; counter++) {
struct islpci_membuf *buf = &priv->mgmt_rx[counter];
if (buf->pci_addr)
pci_unmap_single(priv->pdev, buf->pci_addr,
buf->size, PCI_DMA_FROMDEVICE);
buf->pci_addr = 0;
if (buf->mem)
kfree(buf->mem);
buf->size = 0;
buf->mem = NULL;
}
/* clean up data rx buffers */
for (counter = 0; counter < ISL38XX_CB_RX_QSIZE; counter++) {
if (priv->pci_map_rx_address[counter])
pci_unmap_single(priv->pdev,
priv->pci_map_rx_address[counter],
MAX_FRAGMENT_SIZE_RX + 2,
PCI_DMA_FROMDEVICE);
priv->pci_map_rx_address[counter] = 0;
if (priv->data_low_rx[counter])
dev_kfree_skb(priv->data_low_rx[counter]);
priv->data_low_rx[counter] = 0;
}
/* Free the acces control list and the WPA list */
prism54_acl_clean(&priv->acl);
prism54_wpa_ie_clean(priv);
mgt_clean(priv);
return 0;
}
#if 0
static void
islpci_set_multicast_list(struct net_device *dev)
{
/* put device into promisc mode and let network layer handle it */
}
#endif
struct net_device *
islpci_setup(struct pci_dev *pdev)
{
islpci_private *priv;
struct net_device *ndev = alloc_etherdev(sizeof (islpci_private));
if (!ndev)
return ndev;
SET_MODULE_OWNER(ndev);
pci_set_drvdata(pdev, ndev);
#if defined(SET_NETDEV_DEV)
SET_NETDEV_DEV(ndev, &pdev->dev);
#endif
/* setup the structure members */
ndev->base_addr = pci_resource_start(pdev, 0);
ndev->irq = pdev->irq;
/* initialize the function pointers */
ndev->open = &islpci_open;
ndev->stop = &islpci_close;
ndev->get_stats = &islpci_statistics;
ndev->get_wireless_stats = &prism54_get_wireless_stats;
ndev->do_ioctl = &prism54_ioctl;
#if WIRELESS_EXT > 12
ndev->wireless_handlers =
(struct iw_handler_def *) &prism54_handler_def;
#endif /* WIRELESS_EXT > 12 */
ndev->hard_start_xmit = &islpci_eth_transmit;
/* ndev->set_multicast_list = &islpci_set_multicast_list; */
ndev->addr_len = ETH_ALEN;
ndev->set_mac_address = &prism54_set_mac_address;
/* Get a non-zero dummy MAC address for nameif. Jean II */
memcpy(ndev->dev_addr, dummy_mac, 6);
#ifdef HAVE_TX_TIMEOUT
ndev->watchdog_timeo = ISLPCI_TX_TIMEOUT;
ndev->tx_timeout = &islpci_eth_tx_timeout;
#endif
/* allocate a private device structure to the network device */
priv = ndev->priv;
priv->ndev = ndev;
priv->pdev = pdev;
priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR) ?
ARPHRD_IEEE80211: ARPHRD_ETHER;
/* save the start and end address of the PCI memory area */
ndev->mem_start = (unsigned long) priv->device_base;
ndev->mem_end = ndev->mem_start + ISL38XX_PCI_MEM_SIZE;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "PCI Memory remapped to 0x%p\n", priv->device_base);
#endif
init_waitqueue_head(&priv->reset_done);
/* init the queue read locks, process wait counter */
sema_init(&priv->mgmt_sem, 1);
priv->mgmt_received = NULL;
init_waitqueue_head(&priv->mgmt_wqueue);
sema_init(&priv->stats_sem, 1);
spin_lock_init(&priv->slock);
/* init state machine with off#1 state */
priv->state = PRV_STATE_OFF;
priv->state_off = 1;
/* initialize workqueue's */
INIT_WORK(&priv->stats_work,
(void (*)(void *)) prism54_update_stats, priv);
priv->stats_timestamp = 0;
/* allocate various memory areas */
if (islpci_alloc_memory(priv))
goto do_free_netdev;
/* select the firmware file depending on the device id */
switch (pdev->device) {
case PCIDEVICE_ISL3890:
case PCIDEVICE_3COM6001:
strcpy(priv->firmware, ISL3890_IMAGE_FILE);
break;
case PCIDEVICE_ISL3877:
strcpy(priv->firmware, ISL3877_IMAGE_FILE);
break;
default:
strcpy(priv->firmware, ISL3890_IMAGE_FILE);
break;
}
if (register_netdev(ndev)) {
DEBUG(SHOW_ERROR_MESSAGES,
"ERROR: register_netdev() failed \n");
goto do_islpci_free_memory;
}
return ndev;
do_islpci_free_memory:
islpci_free_memory(priv);
do_free_netdev:
pci_set_drvdata(pdev, 0);
free_netdev(ndev);
priv = 0;
return NULL;
}
islpci_state_t
islpci_set_state(islpci_private *priv, islpci_state_t new_state)
{
islpci_state_t old_state;
/* lock */
old_state = priv->state;
/* this means either a race condition or some serious error in
* the driver code */
switch (new_state) {
case PRV_STATE_OFF:
priv->state_off++;
default:
priv->state = new_state;
break;
case PRV_STATE_PREBOOT:
/* there are actually many off-states, enumerated by
* state_off */
if (old_state == PRV_STATE_OFF)
priv->state_off--;
/* only if hw_unavailable is zero now it means we either
* were in off#1 state, or came here from
* somewhere else */
if (!priv->state_off)
priv->state = new_state;
break;
};
#if 0
printk(KERN_DEBUG "%s: state transition %d -> %d (off#%d)\n",
priv->ndev->name, old_state, new_state, priv->state_off);
#endif
/* invariants */
BUG_ON(priv->state_off < 0);
BUG_ON(priv->state_off && (priv->state != PRV_STATE_OFF));
BUG_ON(!priv->state_off && (priv->state == PRV_STATE_OFF));
/* unlock */
return old_state;
}
/* $Header: /var/lib/cvs/prism54-ng/ksrc/islpci_dev.h,v 1.53 2004/02/28 03:06:07 mcgrof Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
* Copyright (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
* Copyright (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _ISLPCI_DEV_H
#define _ISLPCI_DEV_H
#include <linux/version.h>
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <linux/list.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
# include <linux/workqueue.h>
#else
# include <linux/tqueue.h>
# define work_struct tq_struct
# define INIT_WORK INIT_TQUEUE
# define schedule_work schedule_task
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23)
#define free_netdev(x) kfree(x)
#define pci_name(x) x->slot_name
#endif
#include "isl_38xx.h"
#include "isl_oid.h"
#include "islpci_mgt.h"
/* some states might not be superflous and may be removed when
design is finalized (hvr) */
typedef enum {
PRV_STATE_OFF = 0, /* this means hw_unavailable is != 0 */
PRV_STATE_PREBOOT, /* we are in a pre-boot state (empty RAM) */
PRV_STATE_BOOT, /* boot state (fw upload, run fw) */
PRV_STATE_POSTBOOT, /* after boot state, need reset now */
PRV_STATE_PREINIT, /* pre-init state */
PRV_STATE_INIT, /* init state (restore MIB backup to device) */
PRV_STATE_READY, /* driver&device are in operational state */
PRV_STATE_SLEEP /* device in sleep mode */
} islpci_state_t;
/* ACL using MAC address */
struct mac_entry {
struct list_head _list;
char addr[ETH_ALEN];
};
struct islpci_acl {
enum { MAC_POLICY_OPEN=0, MAC_POLICY_ACCEPT=1, MAC_POLICY_REJECT=2 } policy;
struct list_head mac_list; /* a list of mac_entry */
int size; /* size of queue */
struct semaphore sem; /* accessed in ioctls and trap_work */
};
struct islpci_membuf {
int size; /* size of memory */
void *mem; /* address of memory as seen by CPU */
dma_addr_t pci_addr; /* address of memory as seen by device */
};
#define MAX_BSS_WPA_IE_COUNT 64
#define MAX_WPA_IE_LEN 64
struct islpci_bss_wpa_ie {
struct list_head list;
unsigned long last_update;
u8 bssid[ETH_ALEN];
u8 wpa_ie[MAX_WPA_IE_LEN];
size_t wpa_ie_len;
};
typedef struct {
spinlock_t slock; /* generic spinlock; */
u32 priv_oid;
/* our mib cache */
u32 iw_mode;
struct rw_semaphore mib_sem;
void **mib;
char nickname[IW_ESSID_MAX_SIZE+1];
/* Take care of the wireless stats */
struct work_struct stats_work;
struct semaphore stats_sem;
/* remember when we last updated the stats */
unsigned long stats_timestamp;
/* The first is accessed under semaphore locking.
* The second is the clean one we return to iwconfig.
*/
struct iw_statistics local_iwstatistics;
struct iw_statistics iwstatistics;
struct islpci_acl acl;
/* PCI bus allocation & configuration members */
struct pci_dev *pdev; /* PCI structure information */
u32 pci_state[16]; /* used for suspend/resume */
char firmware[33];
void *device_base; /* ioremapped device base address */
/* consistent DMA region */
void *driver_mem_address; /* base DMA address */
dma_addr_t device_host_address; /* base DMA address (bus address) */
dma_addr_t device_psm_buffer; /* host memory for PSM buffering (bus address) */
/* our network_device structure */
struct net_device *ndev;
/* device queue interface members */
struct isl38xx_cb *control_block; /* device control block
(== driver_mem_address!) */
/* Each queue has three indexes:
* free/index_mgmt/data_rx/tx (called index, see below),
* driver_curr_frag, and device_curr_frag (in the control block)
* All indexes are ever-increasing, but interpreted modulo the
* device queue size when used.
* index <= device_curr_frag <= driver_curr_frag at all times
* For rx queues, [index, device_curr_frag) contains fragments
* that the interrupt processing needs to handle (owned by driver).
* [device_curr_frag, driver_curr_frag) is the free space in the
* rx queue, waiting for data (owned by device). The driver
* increments driver_curr_frag to indicate to the device that more
* buffers are available.
* If device_curr_frag == driver_curr_frag, no more rx buffers are
* available, and the rx DMA engine of the device is halted.
* For tx queues, [index, device_curr_frag) contains fragments
* where tx is done; they need to be freed (owned by driver).
* [device_curr_frag, driver_curr_frag) contains the frames
* that are being transferred (owned by device). The driver
* increments driver_curr_frag to indicate that more tx work
* needs to be done.
*/
u32 index_mgmt_rx; /* real index mgmt rx queue */
u32 index_mgmt_tx; /* read index mgmt tx queue */
u32 free_data_rx; /* free pointer data rx queue */
u32 free_data_tx; /* free pointer data tx queue */
u32 data_low_tx_full; /* full detected flag */
/* frame memory buffers for the device queues */
struct islpci_membuf mgmt_tx[ISL38XX_CB_MGMT_QSIZE];
struct islpci_membuf mgmt_rx[ISL38XX_CB_MGMT_QSIZE];
struct sk_buff *data_low_tx[ISL38XX_CB_TX_QSIZE];
struct sk_buff *data_low_rx[ISL38XX_CB_RX_QSIZE];
dma_addr_t pci_map_tx_address[ISL38XX_CB_TX_QSIZE];
dma_addr_t pci_map_rx_address[ISL38XX_CB_RX_QSIZE];
/* driver network interface members */
struct net_device_stats statistics;
/* wait for a reset interrupt */
wait_queue_head_t reset_done;
/* used by islpci_mgt_transaction */
struct semaphore mgmt_sem; /* serialize access to mailbox and wqueue */
struct islpci_mgmtframe *mgmt_received; /* mbox for incoming frame */
wait_queue_head_t mgmt_wqueue; /* waitqueue for mbox */
/* state machine */
islpci_state_t state;
int state_off; /* enumeration of off-state, if 0 then
* we're not in any off-state */
/* WPA stuff */
int wpa; /* WPA mode enabled */
struct list_head bss_wpa_list;
int num_bss_wpa;
struct semaphore wpa_sem;
} islpci_private;
static inline islpci_state_t
islpci_get_state(islpci_private *priv)
{
/* lock */
return priv->state;
/* unlock */
}
islpci_state_t islpci_set_state(islpci_private *priv, islpci_state_t new_state);
#define ISLPCI_TX_TIMEOUT (2*HZ)
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,75))
# define irqreturn_t void
# define IRQ_HANDLED
# define IRQ_NONE
#endif
irqreturn_t islpci_interrupt(int, void *, struct pt_regs *);
int prism54_post_setup(islpci_private *, int);
int islpci_reset(islpci_private *, int);
static inline void
islpci_trigger(islpci_private *priv)
{
isl38xx_trigger_device(islpci_get_state(priv) == PRV_STATE_SLEEP,
priv->device_base);
}
struct net_device_stats *islpci_statistics(struct net_device *);
int prism54_bring_down(islpci_private *);
int islpci_alloc_memory(islpci_private *);
int islpci_free_memory(islpci_private *);
struct net_device *islpci_setup(struct pci_dev *);
#endif /* _ISLPCI_DEV_H */
/* $Header: /var/lib/cvs/prism54-ng/ksrc/islpci_eth.c,v 1.27 2004/01/30 16:24:00 ajfa Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include "isl_38xx.h"
#include "islpci_eth.h"
#include "islpci_mgt.h"
/******************************************************************************
Network Interface functions
******************************************************************************/
void
islpci_eth_cleanup_transmit(islpci_private *priv,
isl38xx_control_block *control_block)
{
struct sk_buff *skb;
u32 index;
/* compare the control block read pointer with the free pointer */
while (priv->free_data_tx !=
le32_to_cpu(control_block->
device_curr_frag[ISL38XX_CB_TX_DATA_LQ])) {
/* read the index of the first fragment to be freed */
index = priv->free_data_tx % ISL38XX_CB_TX_QSIZE;
/* check for holes in the arrays caused by multi fragment frames
* searching for the last fragment of a frame */
if (priv->pci_map_tx_address[index] != (dma_addr_t) NULL) {
/* entry is the last fragment of a frame
* free the skb structure and unmap pci memory */
skb = priv->data_low_tx[index];
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING,
"cleanup skb %p skb->data %p skb->len %u truesize %u\n ",
skb, skb->data, skb->len, skb->truesize);
#endif
pci_unmap_single(priv->pdev,
priv->pci_map_tx_address[index],
skb->len, PCI_DMA_TODEVICE);
dev_kfree_skb_irq(skb);
}
/* increment the free data low queue pointer */
priv->free_data_tx++;
}
}
int
islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev)
{
islpci_private *priv = ndev->priv;
isl38xx_control_block *cb = priv->control_block;
u32 index;
dma_addr_t pci_map_address;
int frame_size;
isl38xx_fragment *fragment;
int offset;
struct sk_buff *newskb;
int newskb_offset;
unsigned long flags;
unsigned char wds_mac[6];
u32 curr_frag;
int err = 0;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_transmit \n");
#endif
/* lock the driver code */
spin_lock_irqsave(&priv->slock, flags);
/* determine the amount of fragments needed to store the frame */
frame_size = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
if (init_wds)
frame_size += 6;
/* check whether the destination queue has enough fragments for the frame */
curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ]);
if (curr_frag - priv->free_data_tx >= ISL38XX_CB_TX_QSIZE) {
printk(KERN_ERR "%s: transmit device queue full when awake\n",
ndev->name);
netif_stop_queue(ndev);
/* trigger the device */
isl38xx_w32_flush(priv->device_base, ISL38XX_DEV_INT_UPDATE,
ISL38XX_DEV_INT_REG);
udelay(ISL38XX_WRITEIO_DELAY);
err = -EBUSY;
goto drop_free;
}
/* Check alignment and WDS frame formatting. The start of the packet should
* be aligned on a 4-byte boundary. If WDS is enabled add another 6 bytes
* and add WDS address information */
if (((long) skb->data & 0x03) | init_wds) {
/* get the number of bytes to add and re-allign */
offset = (4 - (long) skb->data) & 0x03;
offset += init_wds ? 6 : 0;
/* check whether the current skb can be used */
if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) {
unsigned char *src = skb->data;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "skb offset %i wds %i\n", offset,
init_wds);
#endif
/* align the buffer on 4-byte boundary */
skb_reserve(skb, (4 - (long) skb->data) & 0x03);
if (init_wds) {
/* wds requires an additional address field of 6 bytes */
skb_put(skb, 6);
#ifdef ISLPCI_ETH_DEBUG
printk("islpci_eth_transmit:wds_mac\n");
#endif
memmove(skb->data + 6, src, skb->len);
memcpy(skb->data, wds_mac, 6);
} else {
memmove(skb->data, src, skb->len);
}
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "memmove %p %p %i \n", skb->data,
src, skb->len);
#endif
} else {
newskb =
dev_alloc_skb(init_wds ? skb->len + 6 : skb->len);
newskb_offset = (4 - (long) newskb->data) & 0x03;
/* Check if newskb->data is aligned */
if (newskb_offset)
skb_reserve(newskb, newskb_offset);
skb_put(newskb, init_wds ? skb->len + 6 : skb->len);
if (init_wds) {
memcpy(newskb->data + 6, skb->data, skb->len);
memcpy(newskb->data, wds_mac, 6);
#ifdef ISLPCI_ETH_DEBUG
printk("islpci_eth_transmit:wds_mac\n");
#endif
} else
memcpy(newskb->data, skb->data, skb->len);
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n",
newskb->data, skb->data, skb->len, init_wds);
#endif
newskb->dev = skb->dev;
dev_kfree_skb(skb);
skb = newskb;
}
}
/* display the buffer contents for debugging */
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_BUFFER_CONTENTS, "\ntx %p ", skb->data);
display_buffer((char *) skb->data, skb->len);
#endif
/* map the skb buffer to pci memory for DMA operation */
pci_map_address = pci_map_single(priv->pdev,
(void *) skb->data, skb->len,
PCI_DMA_TODEVICE);
if (pci_map_address == 0) {
printk(KERN_WARNING "%s: cannot map buffer to PCI\n",
ndev->name);
err = -EIO;
goto drop_free;
}
/* Place the fragment in the control block structure. */
index = curr_frag % ISL38XX_CB_TX_QSIZE;
fragment = &cb->tx_data_low[index];
priv->pci_map_tx_address[index] = pci_map_address;
/* store the skb address for future freeing */
priv->data_low_tx[index] = skb;
/* set the proper fragment start address and size information */
fragment->size = cpu_to_le16(frame_size);
fragment->flags = cpu_to_le16(0); /* set to 1 if more fragments */
fragment->address = cpu_to_le32(pci_map_address);
curr_frag++;
/* The fragment address in the control block must have been
* written before announcing the frame buffer to device. */
wmb();
cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ] = cpu_to_le32(curr_frag);
if (curr_frag - priv->free_data_tx + ISL38XX_MIN_QTHRESHOLD
> ISL38XX_CB_TX_QSIZE) {
/* stop sends from upper layers */
netif_stop_queue(ndev);
/* set the full flag for the transmission queue */
priv->data_low_tx_full = 1;
}
/* trigger the device */
islpci_trigger(priv);
/* unlock the driver code */
spin_unlock_irqrestore(&priv->slock, flags);
/* set the transmission time */
ndev->trans_start = jiffies;
priv->statistics.tx_packets++;
priv->statistics.tx_bytes += skb->len;
return 0;
drop_free:
/* free the skbuf structure before aborting */
dev_kfree_skb(skb);
priv->statistics.tx_dropped++;
spin_unlock_irqrestore(&priv->slock, flags);
return err;
}
int
islpci_eth_receive(islpci_private *priv)
{
struct net_device *ndev = priv->ndev;
isl38xx_control_block *control_block = priv->control_block;
struct sk_buff *skb;
u16 size;
u32 index, offset;
unsigned char *src;
int discard = 0;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_receive \n");
#endif
/* the device has written an Ethernet frame in the data area
* of the sk_buff without updating the structure, do it now */
index = priv->free_data_rx % ISL38XX_CB_RX_QSIZE;
size = le16_to_cpu(control_block->rx_data_low[index].size);
skb = priv->data_low_rx[index];
offset = ((unsigned long) le32_to_cpu(control_block->rx_data_low[index].address) -
(unsigned long) skb->data) & 3;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING,
"frq->addr %x skb->data %p skb->len %u offset %u truesize %u\n ",
control_block->rx_data_low[priv->free_data_rx].address, skb->data,
skb->len, offset, skb->truesize);
#endif
/* delete the streaming DMA mapping before processing the skb */
pci_unmap_single(priv->pdev,
priv->pci_map_rx_address[index],
MAX_FRAGMENT_SIZE_RX + 2, PCI_DMA_FROMDEVICE);
/* update the skb structure and allign the buffer */
skb_put(skb, size);
if (offset) {
/* shift the buffer allocation offset bytes to get the right frame */
skb_pull(skb, 2);
skb_put(skb, 2);
}
#if VERBOSE > SHOW_ERROR_MESSAGES
/* display the buffer contents for debugging */
DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
display_buffer((char *) skb->data, skb->len);
#endif
/* check whether WDS is enabled and whether the data frame is a WDS frame */
if (init_wds) {
/* WDS enabled, check for the wds address on the first 6 bytes of the buffer */
src = skb->data + 6;
memmove(skb->data, src, skb->len - 6);
skb_trim(skb, skb->len - 6);
}
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "Fragment size %i in skb at %p\n", size, skb);
DEBUG(SHOW_TRACING, "Skb data at %p, length %i\n", skb->data, skb->len);
/* display the buffer contents for debugging */
DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data);
display_buffer((char *) skb->data, skb->len);
#endif
/* do some additional sk_buff and network layer parameters */
skb->dev = ndev;
/* take care of monitor mode */
if (priv->iw_mode == IW_MODE_MONITOR) {
/* The card reports full 802.11 packets but with a 20 bytes
* header and without the FCS. But there a is a bit that
* indicates if the packet is corrupted :-) */
/* int i; */
if (skb->data[8] & 0x01){
/* This one is bad. Drop it !*/
discard = 1;
/* printk("BAD\n");*/
}
/*
for(i=0;i<50;i++)
printk("%2.2X:",skb->data[i]);
printk("\n");
*/
skb_pull(skb, 20);
skb->protocol = htons(ETH_P_802_2);
skb->mac.raw = skb->data;
skb->pkt_type = PACKET_OTHERHOST;
} else
skb->protocol = eth_type_trans(skb, ndev);
skb->ip_summed = CHECKSUM_NONE;
priv->statistics.rx_packets++;
priv->statistics.rx_bytes += size;
/* deliver the skb to the network layer */
#ifdef ISLPCI_ETH_DEBUG
printk
("islpci_eth_receive:netif_rx %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
skb->data[0], skb->data[1], skb->data[2], skb->data[3],
skb->data[4], skb->data[5]);
#endif
if (discard)
dev_kfree_skb(skb);
else
netif_rx(skb);
/* increment the read index for the rx data low queue */
priv->free_data_rx++;
/* add one or more sk_buff structures */
while (index =
le32_to_cpu(control_block->
driver_curr_frag[ISL38XX_CB_RX_DATA_LQ]),
index - priv->free_data_rx < ISL38XX_CB_RX_QSIZE) {
/* allocate an sk_buff for received data frames storage
* include any required allignment operations */
if (skb = dev_alloc_skb(MAX_FRAGMENT_SIZE_RX + 2), skb == NULL) {
/* error allocating an sk_buff structure elements */
DEBUG(SHOW_ERROR_MESSAGES, "Error allocating skb \n");
break;
}
/* store the new skb structure pointer */
index = index % ISL38XX_CB_RX_QSIZE;
priv->data_low_rx[index] = skb;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING,
"new alloc skb %p skb->data %p skb->len %u index %u truesize %u\n ",
skb, skb->data, skb->len, index, skb->truesize);
#endif
/* set the streaming DMA mapping for proper PCI bus operation */
priv->pci_map_rx_address[index] =
pci_map_single(priv->pdev, (void *) skb->data,
MAX_FRAGMENT_SIZE_RX + 2,
PCI_DMA_FROMDEVICE);
if (priv->pci_map_rx_address[index] == (dma_addr_t) NULL) {
/* error mapping the buffer to device accessable memory address */
DEBUG(SHOW_ERROR_MESSAGES,
"Error mapping DMA address\n");
/* free the skbuf structure before aborting */
dev_kfree_skb((struct sk_buff *) skb);
break;
}
/* update the fragment address */
control_block->rx_data_low[index].address = cpu_to_le32((u32)
priv->
pci_map_rx_address
[index]);
wmb();
/* increment the driver read pointer */
add_le32p((u32 *) & control_block->
driver_curr_frag[ISL38XX_CB_RX_DATA_LQ], 1);
}
/* trigger the device */
islpci_trigger(priv);
return 0;
}
void
islpci_eth_tx_timeout(struct net_device *ndev)
{
islpci_private *priv = ndev->priv;
struct net_device_stats *statistics = &priv->statistics;
/* increment the transmit error counter */
statistics->tx_errors++;
#if 0
/* don't do this here! we are not allowed to sleep since we are in interrupt context */
if (islpci_reset(priv))
printk(KERN_ERR "%s: error on TX timeout card reset!\n",
ndev->name);
#endif
/* netif_wake_queue(ndev); */
return;
}
/* $Header: /var/lib/cvs/prism54-ng/ksrc/islpci_eth.h,v 1.5 2004/01/12 22:16:32 jmaurer Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _ISLPCI_ETH_H
#define _ISLPCI_ETH_H
#include "isl_38xx.h"
#include "islpci_dev.h"
void islpci_eth_cleanup_transmit(islpci_private *, isl38xx_control_block *);
int islpci_eth_transmit(struct sk_buff *, struct net_device *);
int islpci_eth_receive(islpci_private *);
void islpci_eth_tx_timeout(struct net_device *);
#endif /* _ISL_GEN_H */
/* $Header: /var/lib/cvs/prism54-ng/ksrc/islpci_hotplug.c,v 1.56 2004/02/26 23:33:02 mcgrof Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
* Copyright (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/init.h> /* For __init, __exit */
#include "islpci_dev.h"
#include "islpci_mgt.h" /* for pc_debug */
#include "isl_oid.h"
#define DRV_NAME "prism54"
#define DRV_VERSION "1.0.2.2"
MODULE_AUTHOR("W.Termorshuizen, R.Bastings, H.V.Riedel, prism54.org team");
MODULE_DESCRIPTION("Intersil 802.11 Wireless LAN adapter");
MODULE_LICENSE("GPL");
/* In this order: vendor, device, subvendor, subdevice, class, class_mask,
* driver_data
* Note: for driver_data we put the device's name
* If you have an update for this please contact prism54-devel@prism54.org
* The latest list can be found at http://prism54.org/supported_cards.php */
static const struct pci_device_id prism54_id_tbl[] = {
{
PCIVENDOR_3COM, PCIDEVICE_3COM6001,
PCIVENDOR_3COM, PCIDEVICE_3COM6001,
0, 0,
(unsigned long) "3COM 3CRWE154G72 Wireless LAN adapter"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_DLINK, 0x3202UL,
0, 0,
(unsigned long) "D-Link Air Plus Xtreme G A1 - DWL-g650 A1"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_IODATA, 0xd019UL,
0, 0,
(unsigned long) "I-O Data WN-G54/CB - WN-G54/CB"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_NETGEAR, 0x4800UL,
0, 0,
(unsigned long) "Netgear WG511"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_I4, 0x0020UL,
0, 0,
(unsigned long) "PLANEX GW-DS54G"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_SMC, 0x2802UL,
0, 0,
(unsigned long) "EZ Connect g 2.4GHz 54 Mbps Wireless PCI Card - SMC2802W"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_SMC, 0x2835UL,
0, 0,
(unsigned long) "EZ Connect g 2.4GHz 54 Mbps Wireless Cardbus Adapter - SMC2835W"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_INTERSIL, 0x0000UL, /* This was probably a bogus reading... */
0, 0,
(unsigned long) "SparkLAN WL-850F"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_I4, 0x0014UL,
0, 0,
(unsigned long) "I4 Z-Com XG-600"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_I4, 0x0020UL,
0, 0,
(unsigned long) "I4 Z-Com XG-900/PLANEX GW-DS54G"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCIVENDOR_ACCTON, 0xee03UL,
0, 0,
(unsigned long) "SMC 2802Wv2"},
{
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3877,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
(unsigned long) "Intersil PRISM Indigo Wireless LAN adapter"},
{ /* Default */
PCIVENDOR_INTERSIL, PCIDEVICE_ISL3890,
PCI_ANY_ID, PCI_ANY_ID,
0, 0,
(unsigned long) "Intersil PRISM Duette/Prism GT Wireless LAN adapter"},
{0,}
};
/* register the device with the Hotplug facilities of the kernel */
MODULE_DEVICE_TABLE(pci, prism54_id_tbl);
static int prism54_probe(struct pci_dev *, const struct pci_device_id *);
static void prism54_remove(struct pci_dev *);
static int prism54_suspend(struct pci_dev *, u32 state);
static int prism54_resume(struct pci_dev *);
static struct pci_driver prism54_driver = {
.name = DRV_NAME,
.id_table = prism54_id_tbl,
.probe = prism54_probe,
.remove = prism54_remove,
.suspend = prism54_suspend,
.resume = prism54_resume,
/* .enable_wake ; we don't support this yet */
};
static void
prism54_get_card_model(struct net_device *ndev)
{
islpci_private *priv;
char *modelp;
priv = ndev->priv;
switch (priv->pdev->subsystem_device) {
case PCIDEVICE_ISL3877:
modelp = "PRISM Indigo";
break;
case PCIDEVICE_3COM6001:
modelp = "3COM 3CRWE154G72";
break;
case 0x3202UL:
modelp = "D-Link DWL-g650 A1";
break;
case 0xd019UL:
modelp = "WN-G54/CB";
break;
case 0x4800UL:
modelp = "Netgear WG511";
break;
case 0x2802UL:
modelp = "SMC2802W";
break;
case 0xee03UL:
modelp = "SMC2802W V2";
break;
case 0x2835UL:
modelp = "SMC2835W";
break;
/* Let's leave this one out for now since it seems bogus/wrong
* Even if the manufacturer did use 0x0000UL it may not be correct
* by their part, therefore deserving no name ;) */
/* case 0x0000UL:
* modelp = "SparkLAN WL-850F";
* break;*/
/* We have two reported for the one below :( */
case 0x0014UL:
modelp = "XG-600";
break;
case 0x0020UL:
modelp = "XG-900/GW-DS54G";
break;
/* Default it */
/*
case PCIDEVICE_ISL3890:
modelp = "PRISM Duette/GT";
break;
*/
default:
modelp = "PRISM Duette/GT";
}
printk(KERN_DEBUG "%s: %s driver detected card model: %s\n",
ndev->name, DRV_NAME, modelp);
return;
}
/******************************************************************************
Module initialization functions
******************************************************************************/
int
prism54_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct net_device *ndev;
u8 latency_tmr;
u32 mem_addr;
islpci_private *priv;
int rvalue;
/* TRACE(DRV_NAME); */
/* Enable the pci device */
if (pci_enable_device(pdev)) {
printk(KERN_ERR "%s: pci_enable_device() failed.\n", DRV_NAME);
return -ENODEV;
}
/* check whether the latency timer is set correctly */
pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_tmr);
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING, "latency timer: %x\n", latency_tmr);
#endif
if (latency_tmr < PCIDEVICE_LATENCY_TIMER_MIN) {
/* set the latency timer */
pci_write_config_byte(pdev, PCI_LATENCY_TIMER,
PCIDEVICE_LATENCY_TIMER_VAL);
}
/* enable PCI DMA */
if (pci_set_dma_mask(pdev, 0xffffffff)) {
printk(KERN_ERR "%s: 32-bit PCI DMA not supported", DRV_NAME);
goto do_pci_disable_device;
}
/* 0x40 is the programmable timer to configure the response timeout (TRDY_TIMEOUT)
* 0x41 is the programmable timer to configure the retry timeout (RETRY_TIMEOUT)
* The RETRY_TIMEOUT is used to set the number of retries that the core, as a
* Master, will perform before abandoning a cycle. The default value for
* RETRY_TIMEOUT is 0x80, which far exceeds the PCI 2.1 requirement for new
* devices. A write of zero to the RETRY_TIMEOUT register disables this
* function to allow use with any non-compliant legacy devices that may
* execute more retries.
*
* Writing zero to both these two registers will disable both timeouts and
* *can* solve problems caused by devices that are slow to respond.
*/
pci_write_config_byte(pdev, 0x40, 0);
pci_write_config_byte(pdev, 0x41, 0);
/* request the pci device I/O regions */
rvalue = pci_request_regions(pdev, DRV_NAME);
if (rvalue) {
printk(KERN_ERR "%s: pci_request_regions failure (rc=%d)\n",
DRV_NAME, rvalue);
goto do_pci_disable_device;
}
/* check if the memory window is indeed set */
rvalue = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &mem_addr);
if (rvalue || !mem_addr) {
printk(KERN_ERR "%s: PCI device memory region not configured; fix your BIOS or CardBus bridge/drivers\n",
DRV_NAME);
goto do_pci_disable_device;
}
/* enable PCI bus-mastering */
DEBUG(SHOW_TRACING, "%s: pci_set_master(pdev)\n", DRV_NAME);
pci_set_master(pdev);
/* setup the network device interface and its structure */
if (!(ndev = islpci_setup(pdev))) {
/* error configuring the driver as a network device */
printk(KERN_ERR "%s: could not configure network device\n",
DRV_NAME);
goto do_pci_release_regions;
}
priv = ndev->priv;
islpci_set_state(priv, PRV_STATE_PREBOOT); /* we are attempting to boot */
/* card is in unknown state yet, might have some interrupts pending */
isl38xx_disable_interrupts(priv->device_base);
/* request for the interrupt before uploading the firmware */
rvalue = request_irq(pdev->irq, &islpci_interrupt,
SA_SHIRQ, ndev->name, priv);
if (rvalue) {
/* error, could not hook the handler to the irq */
printk(KERN_ERR "%s: could not install IRQ handler\n",
ndev->name);
goto do_unregister_netdev;
}
/* firmware upload is triggered in islpci_open */
/* Pretty card model discovery output */
prism54_get_card_model(ndev);
return 0;
do_unregister_netdev:
unregister_netdev(ndev);
islpci_free_memory(priv);
pci_set_drvdata(pdev, 0);
free_netdev(ndev);
priv = 0;
do_pci_release_regions:
pci_release_regions(pdev);
do_pci_disable_device:
pci_disable_device(pdev);
return -EIO;
}
/* set by cleanup_module */
static volatile int __in_cleanup_module = 0;
/* this one removes one(!!) instance only */
void
prism54_remove(struct pci_dev *pdev)
{
struct net_device *ndev = pci_get_drvdata(pdev);
islpci_private *priv = ndev ? ndev->priv : 0;
BUG_ON(!priv);
if (!__in_cleanup_module) {
printk(KERN_DEBUG "%s: hot unplug detected\n", ndev->name);
islpci_set_state(priv, PRV_STATE_OFF);
}
printk(KERN_DEBUG "%s: removing device\n", ndev->name);
unregister_netdev(ndev);
/* free the interrupt request */
if (islpci_get_state(priv) != PRV_STATE_OFF) {
isl38xx_disable_interrupts(priv->device_base);
islpci_set_state(priv, PRV_STATE_OFF);
/* This bellow causes a lockup at rmmod time. It might be
* because some interrupts still linger after rmmod time,
* see bug #17 */
/* pci_set_power_state(pdev, 3);*/ /* try to power-off */
}
free_irq(pdev->irq, priv);
/* free the PCI memory and unmap the remapped page */
islpci_free_memory(priv);
pci_set_drvdata(pdev, 0);
free_netdev(ndev);
priv = 0;
pci_release_regions(pdev);
pci_disable_device(pdev);
}
int
prism54_suspend(struct pci_dev *pdev, u32 state)
{
struct net_device *ndev = pci_get_drvdata(pdev);
islpci_private *priv = ndev ? ndev->priv : 0;
BUG_ON(!priv);
printk(KERN_NOTICE "%s: got suspend request (state %d)\n",
ndev->name, state);
pci_save_state(pdev, priv->pci_state);
/* tell the device not to trigger interrupts for now... */
isl38xx_disable_interrupts(priv->device_base);
/* from now on assume the hardware was already powered down
and don't touch it anymore */
islpci_set_state(priv, PRV_STATE_OFF);
netif_stop_queue(ndev);
netif_device_detach(ndev);
return 0;
}
int
prism54_resume(struct pci_dev *pdev)
{
struct net_device *ndev = pci_get_drvdata(pdev);
islpci_private *priv = ndev ? ndev->priv : 0;
BUG_ON(!priv);
printk(KERN_NOTICE "%s: got resume request\n", ndev->name);
pci_restore_state(pdev, priv->pci_state);
/* alright let's go into the PREBOOT state */
islpci_reset(priv, 1);
netif_device_attach(ndev);
netif_start_queue(ndev);
return 0;
}
static int __init
prism54_module_init(void)
{
printk(KERN_INFO "Loaded %s driver, version %s\n",
DRV_NAME, DRV_VERSION);
__bug_on_wrong_struct_sizes ();
return pci_module_init(&prism54_driver);
}
/* by the time prism54_module_exit() terminates, as a postcondition
* all instances will have been destroyed by calls to
* prism54_remove() */
static void __exit
prism54_module_exit(void)
{
__in_cleanup_module = 1;
pci_unregister_driver(&prism54_driver);
printk(KERN_INFO "Unloaded %s driver\n", DRV_NAME);
__in_cleanup_module = 0;
}
/* register entry points */
module_init(prism54_module_init);
module_exit(prism54_module_exit);
/* EOF */
/* $Header: /var/lib/cvs/prism54-ng/ksrc/islpci_mgt.c,v 1.40 2004/02/01 10:57:23 mcgrof Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
* Copyright 2004 Jens Maurer <Jens.Maurer@gmx.net>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/config.h>
#include <linux/netdevice.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/if_arp.h>
#include "isl_38xx.h"
#include "islpci_mgt.h"
#include "isl_oid.h" /* additional types and defs for isl38xx fw */
#include "isl_ioctl.h"
#if WIRELESS_EXT > 12
#include <net/iw_handler.h>
#endif
/******************************************************************************
Global variable definition section
******************************************************************************/
int pc_debug = VERBOSE;
MODULE_PARM(pc_debug, "i");
/******************************************************************************
Driver general functions
******************************************************************************/
void
display_buffer(char *buffer, int length)
{
if ((pc_debug & SHOW_BUFFER_CONTENTS) == 0)
return;
while (length > 0) {
printk("[%02x]", *buffer & 255);
length--;
buffer++;
}
printk("\n");
}
/*****************************************************************************
Queue handling for management frames
******************************************************************************/
/*
* Helper function to create a PIMFOR management frame header.
*/
static void
pimfor_encode_header(int operation, u32 oid, u32 length, pimfor_header_t *h)
{
h->version = PIMFOR_VERSION;
h->operation = operation;
h->device_id = PIMFOR_DEV_ID_MHLI_MIB;
h->flags = 0;
h->oid = cpu_to_be32(oid);
h->length = cpu_to_be32(length);
}
/*
* Helper function to analyze a PIMFOR management frame header.
*/
static pimfor_header_t *
pimfor_decode_header(void *data, int len)
{
pimfor_header_t *h = data;
while ((void *) h < data + len) {
if(h->flags & PIMFOR_FLAG_LITTLE_ENDIAN) {
le32_to_cpus(&h->oid);
le32_to_cpus(&h->length);
} else {
be32_to_cpus(&h->oid);
be32_to_cpus(&h->length);
}
if (h->oid != OID_INL_TUNNEL)
return h;
h++;
}
return NULL;
}
/*
* Fill the receive queue for management frames with fresh buffers.
*/
int
islpci_mgmt_rx_fill(struct net_device *ndev)
{
islpci_private *priv = ndev->priv;
isl38xx_control_block *cb = /* volatile not needed */
(isl38xx_control_block *) priv->control_block;
u32 curr = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ]);
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgmt_rx_fill \n");
#endif
while (curr - priv->index_mgmt_rx < ISL38XX_CB_MGMT_QSIZE) {
u32 index = curr % ISL38XX_CB_MGMT_QSIZE;
struct islpci_membuf *buf = &priv->mgmt_rx[index];
isl38xx_fragment *frag = &cb->rx_data_mgmt[index];
if (buf->mem == NULL) {
buf->mem = kmalloc(MGMT_FRAME_SIZE, GFP_ATOMIC);
if (!buf->mem) {
printk(KERN_WARNING "Error allocating management frame.\n");
return -ENOMEM;
}
buf->size = MGMT_FRAME_SIZE;
}
if (buf->pci_addr == 0) {
buf->pci_addr = pci_map_single(priv->pdev, buf->mem,
MGMT_FRAME_SIZE,
PCI_DMA_FROMDEVICE);
if(!buf->pci_addr) {
printk(KERN_WARNING "Failed to make memory DMA'able\n.");
return -ENOMEM;
}
}
/* be safe: always reset control block information */
frag->size = cpu_to_le16(MGMT_FRAME_SIZE);
frag->flags = 0;
frag->address = cpu_to_le32(buf->pci_addr);
curr++;
/* The fragment address in the control block must have
* been written before announcing the frame buffer to
* device */
wmb();
cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] =
cpu_to_le32(curr);
}
return 0;
}
/*
* Create and transmit a management frame using "operation" and "oid",
* with arguments data/length.
* We either return an error and free the frame, or we return 0 and
* islpci_mgt_cleanup_transmit() frees the frame in the tx-done
* interrupt.
*/
static int
islpci_mgt_transmit(struct net_device *ndev, int operation, unsigned long oid,
void *data, int length)
{
islpci_private *priv = ndev->priv;
isl38xx_control_block *cb =
(isl38xx_control_block *) priv->control_block;
void *p;
int err = -EINVAL;
unsigned long flags;
isl38xx_fragment *frag;
struct islpci_membuf buf;
u32 curr_frag;
int index;
int frag_len = length + PIMFOR_HEADER_SIZE;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_transmit\n");
#endif
if (frag_len > MGMT_FRAME_SIZE) {
printk(KERN_DEBUG "%s: mgmt frame too large %d\n",
ndev->name, frag_len);
goto error;
}
err = -ENOMEM;
p = buf.mem = kmalloc(frag_len, GFP_KERNEL);
if (!buf.mem) {
printk(KERN_DEBUG "%s: cannot allocate mgmt frame\n",
ndev->name);
goto error;
}
buf.size = frag_len;
/* create the header directly in the fragment data area */
pimfor_encode_header(operation, oid, length, (pimfor_header_t *) p);
p += PIMFOR_HEADER_SIZE;
if (data)
memcpy(p, data, length);
else
memset(p, 0, length);
#if VERBOSE > SHOW_ERROR_MESSAGES
{
pimfor_header_t *h = buf.mem;
DEBUG(SHOW_PIMFOR_FRAMES,
"PIMFOR: op %i, oid 0x%08lx, device %i, flags 0x%x length 0x%x \n",
h->operation, oid, h->device_id, h->flags, length);
/* display the buffer contents for debugging */
display_buffer((char *) h, sizeof (pimfor_header_t));
display_buffer(p, length);
}
#endif
err = -ENOMEM;
buf.pci_addr = pci_map_single(priv->pdev, buf.mem, frag_len,
PCI_DMA_TODEVICE);
if (!buf.pci_addr) {
printk(KERN_WARNING "%s: cannot map PCI memory for mgmt\n",
ndev->name);
goto error_free;
}
/* Protect the control block modifications against interrupts. */
spin_lock_irqsave(&priv->slock, flags);
curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ]);
if (curr_frag - priv->index_mgmt_tx >= ISL38XX_CB_MGMT_QSIZE) {
printk(KERN_WARNING "%s: mgmt tx queue is still full\n",
ndev->name);
goto error_unlock;
}
/* commit the frame to the tx device queue */
index = curr_frag % ISL38XX_CB_MGMT_QSIZE;
priv->mgmt_tx[index] = buf;
frag = &cb->tx_data_mgmt[index];
frag->size = cpu_to_le16(frag_len);
frag->flags = 0; /* for any other than the last fragment, set to 1 */
frag->address = cpu_to_le32(buf.pci_addr);
/* The fragment address in the control block must have
* been written before announcing the frame buffer to
* device */
wmb();
cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ] = cpu_to_le32(curr_frag+1);
spin_unlock_irqrestore(&priv->slock, flags);
/* trigger the device */
islpci_trigger(priv);
return 0;
error_unlock:
spin_unlock_irqrestore(&priv->slock, flags);
error_free:
kfree(buf.mem);
error:
return err;
}
/*
* Receive a management frame from the device.
* This can be an arbitrary number of traps, and at most one response
* frame for a previous request sent via islpci_mgt_transmit().
*/
int
islpci_mgt_receive(struct net_device *ndev)
{
islpci_private *priv = ndev->priv;
isl38xx_control_block *cb =
(isl38xx_control_block *) priv->control_block;
u32 curr_frag;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_receive \n");
#endif
/* Only once per interrupt, determine fragment range to
* process. This avoids an endless loop (i.e. lockup) if
* frames come in faster than we can process them. */
curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_RX_MGMTQ]);
barrier();
for ( ; priv->index_mgmt_rx < curr_frag; priv->index_mgmt_rx++) {
pimfor_header_t *header;
u32 index = priv->index_mgmt_rx % ISL38XX_CB_MGMT_QSIZE;
struct islpci_membuf *buf = &priv->mgmt_rx[index];
u16 frag_len;
int size;
struct islpci_mgmtframe *frame;
/* I have no idea (and no documentation) if flags != 0
* is possible. Drop the frame, reuse the buffer. */
if(le16_to_cpu(cb->rx_data_mgmt[index].flags) != 0) {
printk(KERN_WARNING "%s: unknown flags 0x%04x\n",
ndev->name,
le16_to_cpu(cb->rx_data_mgmt[index].flags));
continue;
}
/* The device only returns the size of the header(s) here. */
frag_len = le16_to_cpu(cb->rx_data_mgmt[index].size);
/*
* We appear to have no way to tell the device the
* size of a receive buffer. Thus, if this check
* triggers, we likely have kernel heap corruption. */
if (frag_len > MGMT_FRAME_SIZE) {
printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\
n",
ndev->name, frag_len, frag_len);
frag_len = MGMT_FRAME_SIZE;
}
/* Ensure the results of device DMA are visible to the CPU. */
pci_dma_sync_single(priv->pdev, buf->pci_addr,
buf->size, PCI_DMA_FROMDEVICE);
/* Perform endianess conversion for PIMFOR header in-place. */
header = pimfor_decode_header(buf->mem, frag_len);
if (!header) {
printk(KERN_WARNING "%s: no PIMFOR header found\n",
ndev->name);
continue;
}
/* The device ID from the PIMFOR packet received from
* the MVC is always 0. We forward a sensible device_id.
* Not that anyone upstream would care... */
header->device_id = priv->ndev->ifindex;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_PIMFOR_FRAMES,
"PIMFOR: op %i, oid 0x%08x, device %i, flags 0x%x length 0x%x \n",
header->operation, header->oid, header->device_id,
header->flags, header->length);
/* display the buffer contents for debugging */
display_buffer((char *) header, PIMFOR_HEADER_SIZE);
display_buffer((char *) header + PIMFOR_HEADER_SIZE, header->length);
#endif
/* nobody sends these */
if (header->flags & PIMFOR_FLAG_APPLIC_ORIGIN) {
printk(KERN_DEBUG "%s: errant PIMFOR application frame\n",
ndev->name);
continue;
}
/* Determine frame size, skipping OID_INL_TUNNEL headers. */
size = PIMFOR_HEADER_SIZE + header->length;
frame = kmalloc(sizeof(struct islpci_mgmtframe) + size,
GFP_ATOMIC);
if (!frame) {
printk(KERN_WARNING "%s: Out of memory, cannot handle oid 0x%08x\n",
ndev->name, header->oid);
continue;
}
frame->ndev = ndev;
memcpy(&frame->buf, header, size);
frame->header = (pimfor_header_t *) frame->buf;
frame->data = frame->buf + PIMFOR_HEADER_SIZE;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_PIMFOR_FRAMES,
"frame: header: %p, data: %p, size: %d\n",
frame->header, frame->data, size);
#endif
if (header->operation == PIMFOR_OP_TRAP) {
#if VERBOSE > SHOW_ERROR_MESSAGES
printk(KERN_DEBUG
"TRAP: oid 0x%x, device %i, flags 0x%x length %i\n",
header->oid, header->device_id, header->flags,
header->length);
#endif
/* Create work to handle trap out of interrupt
* context. */
INIT_WORK(&frame->ws, prism54_process_trap, frame);
schedule_work(&frame->ws);
} else {
/* Signal the one waiting process that a response
* has been received. */
if ((frame = xchg(&priv->mgmt_received, frame)) != NULL) {
printk(KERN_WARNING "%s: mgmt response not collected\n",
ndev->name);
kfree(frame);
}
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_TRACING,
"Wake up Mgmt Queue\n");
#endif
wake_up(&priv->mgmt_wqueue);
}
}
return 0;
}
/*
* Cleanup the transmit queue by freeing all frames handled by the device.
*/
void
islpci_mgt_cleanup_transmit(struct net_device *ndev)
{
islpci_private *priv = ndev->priv;
isl38xx_control_block *cb = /* volatile not needed */
(isl38xx_control_block *) priv->control_block;
u32 curr_frag;
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_cleanup_transmit\n");
#endif
/* Only once per cleanup, determine fragment range to
* process. This avoids an endless loop (i.e. lockup) if
* the device became confused, incrementing device_curr_frag
* rapidly. */
curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_TX_MGMTQ]);
barrier();
for ( ; priv->index_mgmt_tx < curr_frag; priv->index_mgmt_tx++) {
int index = priv->index_mgmt_tx % ISL38XX_CB_MGMT_QSIZE;
struct islpci_membuf *buf = &priv->mgmt_tx[index];
pci_unmap_single(priv->pdev, buf->pci_addr, buf->size,
PCI_DMA_TODEVICE);
buf->pci_addr = 0;
kfree(buf->mem);
buf->mem = NULL;
buf->size = 0;
}
}
/*
* Perform one request-response transaction to the device.
*/
int
islpci_mgt_transaction(struct net_device *ndev,
int operation, unsigned long oid,
void *senddata, int sendlen,
struct islpci_mgmtframe **recvframe)
{
islpci_private *priv = ndev->priv;
const long wait_cycle_jiffies = (ISL38XX_WAIT_CYCLE * 10 * HZ) / 1000;
long timeout_left = ISL38XX_MAX_WAIT_CYCLES * wait_cycle_jiffies;
int err;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
DEFINE_WAIT(wait);
#else
DECLARE_WAITQUEUE(wait, current);
#endif
if (down_interruptible(&priv->mgmt_sem))
return -ERESTARTSYS;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
prepare_to_wait(&priv->mgmt_wqueue, &wait, TASK_UNINTERRUPTIBLE);
#else
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&priv->mgmt_wqueue, &wait);
#endif
err = islpci_mgt_transmit(ndev, operation, oid, senddata, sendlen);
if(err)
goto out;
err = -ETIMEDOUT;
while (timeout_left > 0) {
int timeleft;
struct islpci_mgmtframe *frame;
timeleft = schedule_timeout(wait_cycle_jiffies);
frame = xchg(&priv->mgmt_received, NULL);
if (frame) {
*recvframe = frame;
err = 0;
goto out;
}
if(timeleft == 0) {
printk(KERN_DEBUG "%s: timeout waiting for mgmt response %lu, trigging device\n",
ndev->name, timeout_left);
islpci_trigger(priv);
}
timeout_left += timeleft - wait_cycle_jiffies;
}
printk(KERN_WARNING "%s: timeout waiting for mgmt response\n",
ndev->name);
/* TODO: we should reset the device here */
out:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
finish_wait(&priv->mgmt_wqueue, &wait);
#else
remove_wait_queue(&priv->mgmt_wqueue, &wait);
set_current_state(TASK_RUNNING);
#endif
up(&priv->mgmt_sem);
return err;
}
/* $Header: /var/lib/cvs/prism54-ng/ksrc/islpci_mgt.h,v 1.22 2004/01/30 16:24:00 ajfa Exp $
*
* Copyright (C) 2002 Intersil Americas Inc.
* Copyright (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _ISLPCI_MGT_H
#define _ISLPCI_MGT_H
#include <linux/wireless.h>
#include <linux/skbuff.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
# include <linux/workqueue.h>
#else
# include <linux/tqueue.h>
# define work_struct tq_struct
# define INIT_WORK INIT_TQUEUE
# define schedule_work schedule_task
#endif
/*
* Function definitions
*/
#define K_DEBUG(f, m, args...) do { if(f & m) printk(KERN_DEBUG args); } while(0)
#define DEBUG(f, args...) K_DEBUG(f, pc_debug, args)
#define TRACE(devname) K_DEBUG(SHOW_TRACING, VERBOSE, "%s: -> " __FUNCTION__ "()\n", devname)
extern int pc_debug;
static const int init_wds = 0; /* help compiler optimize away dead code */
/* General driver definitions */
#define PCIVENDOR_INTERSIL 0x1260UL
#define PCIVENDOR_3COM 0x10b7UL
#define PCIVENDOR_DLINK 0x1186UL
#define PCIVENDOR_I4 0x17cfUL
#define PCIVENDOR_IODATA 0x10fcUL
#define PCIVENDOR_NETGEAR 0x1385UL
#define PCIVENDOR_SMC 0x10b8UL
#define PCIVENDOR_ACCTON 0x1113UL
#define PCIDEVICE_ISL3877 0x3877UL
#define PCIDEVICE_ISL3890 0x3890UL
#define PCIDEVICE_3COM6001 0x6001UL
#define PCIDEVICE_LATENCY_TIMER_MIN 0x40
#define PCIDEVICE_LATENCY_TIMER_VAL 0x50
/* Debugging verbose definitions */
#define SHOW_NOTHING 0x00 /* overrules everything */
#define SHOW_ANYTHING 0xFF
#define SHOW_ERROR_MESSAGES 0x01
#define SHOW_TRAPS 0x02
#define SHOW_FUNCTION_CALLS 0x04
#define SHOW_TRACING 0x08
#define SHOW_QUEUE_INDEXES 0x10
#define SHOW_PIMFOR_FRAMES 0x20
#define SHOW_BUFFER_CONTENTS 0x40
#define VERBOSE 0x01
/* Default card definitions */
#define CARD_DEFAULT_CHANNEL 6
#define CARD_DEFAULT_MODE INL_MODE_CLIENT
#define CARD_DEFAULT_IW_MODE IW_MODE_INFRA
#define CARD_DEFAULT_BSSTYPE DOT11_BSSTYPE_INFRA
#define CARD_DEFAULT_CLIENT_SSID ""
#define CARD_DEFAULT_AP_SSID "default"
#define CARD_DEFAULT_KEY1 "default_key_1"
#define CARD_DEFAULT_KEY2 "default_key_2"
#define CARD_DEFAULT_KEY3 "default_key_3"
#define CARD_DEFAULT_KEY4 "default_key_4"
#define CARD_DEFAULT_WEP 0
#define CARD_DEFAULT_FILTER 0
# define CARD_DEFAULT_WDS 0
#define CARD_DEFAULT_AUTHEN DOT11_AUTH_OS
#define CARD_DEFAULT_DOT1X 0
#define CARD_DEFAULT_MLME_MODE DOT11_MLME_AUTO
#define CARD_DEFAULT_CONFORMANCE OID_INL_CONFORMANCE_NONE
/* PIMFOR package definitions */
#define PIMFOR_ETHERTYPE 0x8828
#define PIMFOR_HEADER_SIZE 12
#define PIMFOR_VERSION 1
#define PIMFOR_OP_GET 0
#define PIMFOR_OP_SET 1
#define PIMFOR_OP_RESPONSE 2
#define PIMFOR_OP_ERROR 3
#define PIMFOR_OP_TRAP 4
#define PIMFOR_OP_RESERVED 5 /* till 255 */
#define PIMFOR_DEV_ID_MHLI_MIB 0
#define PIMFOR_FLAG_APPLIC_ORIGIN 0x01
#define PIMFOR_FLAG_LITTLE_ENDIAN 0x02
static inline void
add_le32p(u32 * le_number, u32 add)
{
*le_number = cpu_to_le32(le32_to_cpup(le_number) + add);
}
void display_buffer(char *, int);
/*
* Type definition section
*
* the structure defines only the header allowing copyless
* frame handling
*/
typedef struct {
u8 version;
u8 operation;
u32 oid;
u8 device_id;
u8 flags;
u32 length;
} __attribute__ ((packed))
pimfor_header_t;
/* A received and interrupt-processed management frame, either for
* schedule_work(prism54_process_trap) or for priv->mgmt_received,
* processed by islpci_mgt_transaction(). */
struct islpci_mgmtframe {
struct net_device *ndev; /* pointer to network device */
pimfor_header_t *header; /* payload header, points into buf */
void *data; /* payload ex header, points into buf */
struct work_struct ws; /* argument for schedule_work() */
char buf[0]; /* fragment buffer */
};
int
islpci_mgt_receive(struct net_device *ndev);
int
islpci_mgmt_rx_fill(struct net_device *ndev);
void
islpci_mgt_cleanup_transmit(struct net_device *ndev);
int
islpci_mgt_transaction(struct net_device *ndev,
int operation, unsigned long oid,
void *senddata, int sendlen,
struct islpci_mgmtframe **recvframe);
static inline void
islpci_mgt_release(struct islpci_mgmtframe *frame)
{
kfree(frame);
}
#endif /* _ISLPCI_MGT_H */
/*
* Copyright (C) 2003 Aurelien Alleaume <slts@free.fr>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "islpci_dev.h"
#include "islpci_mgt.h"
#include "isl_oid.h"
#include "oid_mgt.h"
#include "isl_ioctl.h"
/* to convert between channel and freq */
const int frequency_list_bg[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
2447, 2452, 2457, 2462, 2467, 2472, 2484
};
const int frequency_list_a[] = { 5170, 5180, 5190, 5200, 5210, 5220, 5230,
5240, 5260, 5280, 5300, 5320
};
#define OID_U32(x) {x, 0, sizeof(u32), OID_FLAG_U32}
#define OID_U32_C(x) {x, 0, sizeof(u32), OID_FLAG_U32 | OID_FLAG_CACHED}
#define OID_STRUCT(x,s) {x, 0, sizeof(s), 0}
#define OID_STRUCT_C(x,s) {x, 0, sizeof(s), OID_FLAG_CACHED}
#define OID_STRUCT_MLME(x){x, 0, sizeof(struct obj_mlme), 0}
#define OID_STRUCT_MLMEEX(x){x, 0, sizeof(struct obj_mlmeex), OID_FLAG_MLMEEX}
#define OID_UNKNOWN(x) {x, 0, 0, 0}
struct oid_t isl_oid[] = {
[GEN_OID_MACADDRESS] = OID_STRUCT(0x00000000, u8[6]),
[GEN_OID_LINKSTATE] = OID_U32(0x00000001),
[GEN_OID_WATCHDOG] = OID_UNKNOWN(0x00000002),
[GEN_OID_MIBOP] = OID_UNKNOWN(0x00000003),
[GEN_OID_OPTIONS] = OID_UNKNOWN(0x00000004),
[GEN_OID_LEDCONFIG] = OID_UNKNOWN(0x00000005),
/* 802.11 */
[DOT11_OID_BSSTYPE] = OID_U32_C(0x10000000),
[DOT11_OID_BSSID] = OID_STRUCT_C(0x10000001, u8[6]),
[DOT11_OID_SSID] = OID_STRUCT_C(0x10000002, struct obj_ssid),
[DOT11_OID_STATE] = OID_U32(0x10000003),
[DOT11_OID_AID] = OID_U32(0x10000004),
[DOT11_OID_COUNTRYSTRING] = OID_STRUCT(0x10000005, u8[4]),
[DOT11_OID_SSIDOVERRIDE] = OID_STRUCT_C(0x10000006, struct obj_ssid),
[DOT11_OID_MEDIUMLIMIT] = OID_U32(0x11000000),
[DOT11_OID_BEACONPERIOD] = OID_U32_C(0x11000001),
[DOT11_OID_DTIMPERIOD] = OID_U32(0x11000002),
[DOT11_OID_ATIMWINDOW] = OID_U32(0x11000003),
[DOT11_OID_LISTENINTERVAL] = OID_U32(0x11000004),
[DOT11_OID_CFPPERIOD] = OID_U32(0x11000005),
[DOT11_OID_CFPDURATION] = OID_U32(0x11000006),
[DOT11_OID_AUTHENABLE] = OID_U32_C(0x12000000),
[DOT11_OID_PRIVACYINVOKED] = OID_U32_C(0x12000001),
[DOT11_OID_EXUNENCRYPTED] = OID_U32_C(0x12000002),
[DOT11_OID_DEFKEYID] = OID_U32_C(0x12000003),
[DOT11_OID_DEFKEYX] = {0x12000004, 3, sizeof (struct obj_key), OID_FLAG_CACHED}, /* DOT11_OID_DEFKEY1,...DOT11_OID_DEFKEY4 */
[DOT11_OID_STAKEY] = OID_UNKNOWN(0x12000008),
[DOT11_OID_REKEYTHRESHOLD] = OID_U32(0x12000009),
[DOT11_OID_STASC] = OID_UNKNOWN(0x1200000a),
[DOT11_OID_PRIVTXREJECTED] = OID_U32(0x1a000000),
[DOT11_OID_PRIVRXPLAIN] = OID_U32(0x1a000001),
[DOT11_OID_PRIVRXFAILED] = OID_U32(0x1a000002),
[DOT11_OID_PRIVRXNOKEY] = OID_U32(0x1a000003),
[DOT11_OID_RTSTHRESH] = OID_U32_C(0x13000000),
[DOT11_OID_FRAGTHRESH] = OID_U32_C(0x13000001),
[DOT11_OID_SHORTRETRIES] = OID_U32_C(0x13000002),
[DOT11_OID_LONGRETRIES] = OID_U32_C(0x13000003),
[DOT11_OID_MAXTXLIFETIME] = OID_U32_C(0x13000004),
[DOT11_OID_MAXRXLIFETIME] = OID_U32(0x13000005),
[DOT11_OID_AUTHRESPTIMEOUT] = OID_U32(0x13000006),
[DOT11_OID_ASSOCRESPTIMEOUT] = OID_U32(0x13000007),
[DOT11_OID_ALOFT_TABLE] = OID_UNKNOWN(0x1d000000),
[DOT11_OID_ALOFT_CTRL_TABLE] = OID_UNKNOWN(0x1d000001),
[DOT11_OID_ALOFT_RETREAT] = OID_UNKNOWN(0x1d000002),
[DOT11_OID_ALOFT_PROGRESS] = OID_UNKNOWN(0x1d000003),
[DOT11_OID_ALOFT_FIXEDRATE] = OID_U32(0x1d000004),
[DOT11_OID_ALOFT_RSSIGRAPH] = OID_UNKNOWN(0x1d000005),
[DOT11_OID_ALOFT_CONFIG] = OID_UNKNOWN(0x1d000006),
[DOT11_OID_VDCFX] = {0x1b000000, 7, 0, 0},
[DOT11_OID_MAXFRAMEBURST] = OID_U32(0x1b000008),
[DOT11_OID_PSM] = OID_U32(0x14000000),
[DOT11_OID_CAMTIMEOUT] = OID_U32(0x14000001),
[DOT11_OID_RECEIVEDTIMS] = OID_U32(0x14000002),
[DOT11_OID_ROAMPREFERENCE] = OID_U32(0x14000003),
[DOT11_OID_BRIDGELOCAL] = OID_U32(0x15000000),
[DOT11_OID_CLIENTS] = OID_U32(0x15000001),
[DOT11_OID_CLIENTSASSOCIATED] = OID_U32(0x15000002),
[DOT11_OID_CLIENTX] = {0x15000003, 2006, 0, 0}, /* DOT11_OID_CLIENTX,...DOT11_OID_CLIENT2007 */
[DOT11_OID_CLIENTFIND] = OID_STRUCT(0x150007DB, u8[6]),
[DOT11_OID_WDSLINKADD] = OID_STRUCT(0x150007DC, u8[6]),
[DOT11_OID_WDSLINKREMOVE] = OID_STRUCT(0x150007DD, u8[6]),
[DOT11_OID_EAPAUTHSTA] = OID_STRUCT(0x150007DE, u8[6]),
[DOT11_OID_EAPUNAUTHSTA] = OID_STRUCT(0x150007DF, u8[6]),
[DOT11_OID_DOT1XENABLE] = OID_U32_C(0x150007E0),
[DOT11_OID_MICFAILURE] = OID_UNKNOWN(0x150007E1),
[DOT11_OID_REKEYINDICATE] = OID_UNKNOWN(0x150007E2),
[DOT11_OID_MPDUTXSUCCESSFUL] = OID_U32(0x16000000),
[DOT11_OID_MPDUTXONERETRY] = OID_U32(0x16000001),
[DOT11_OID_MPDUTXMULTIPLERETRIES] = OID_U32(0x16000002),
[DOT11_OID_MPDUTXFAILED] = OID_U32(0x16000003),
[DOT11_OID_MPDURXSUCCESSFUL] = OID_U32(0x16000004),
[DOT11_OID_MPDURXDUPS] = OID_U32(0x16000005),
[DOT11_OID_RTSSUCCESSFUL] = OID_U32(0x16000006),
[DOT11_OID_RTSFAILED] = OID_U32(0x16000007),
[DOT11_OID_ACKFAILED] = OID_U32(0x16000008),
[DOT11_OID_FRAMERECEIVES] = OID_U32(0x16000009),
[DOT11_OID_FRAMEERRORS] = OID_U32(0x1600000A),
[DOT11_OID_FRAMEABORTS] = OID_U32(0x1600000B),
[DOT11_OID_FRAMEABORTSPHY] = OID_U32(0x1600000C),
[DOT11_OID_SLOTTIME] = OID_U32(0x17000000),
[DOT11_OID_CWMIN] = OID_U32(0x17000001),
[DOT11_OID_CWMAX] = OID_U32(0x17000002),
[DOT11_OID_ACKWINDOW] = OID_U32(0x17000003),
[DOT11_OID_ANTENNARX] = OID_U32(0x17000004),
[DOT11_OID_ANTENNATX] = OID_U32(0x17000005),
[DOT11_OID_ANTENNADIVERSITY] = OID_U32(0x17000006),
[DOT11_OID_CHANNEL] = OID_U32_C(0x17000007),
[DOT11_OID_EDTHRESHOLD] = OID_U32_C(0x17000008),
[DOT11_OID_PREAMBLESETTINGS] = OID_U32(0x17000009),
[DOT11_OID_RATES] = OID_STRUCT(0x1700000A, u8[IWMAX_BITRATES + 1]),
[DOT11_OID_CCAMODESUPPORTED] = OID_U32(0x1700000B),
[DOT11_OID_CCAMODE] = OID_U32(0x1700000C),
[DOT11_OID_RSSIVECTOR] = OID_U32(0x1700000D),
[DOT11_OID_OUTPUTPOWERTABLE] = OID_U32(0x1700000E),
[DOT11_OID_OUTPUTPOWER] = OID_U32_C(0x1700000F),
[DOT11_OID_SUPPORTEDRATES] =
OID_STRUCT(0x17000010, u8[IWMAX_BITRATES + 1]),
[DOT11_OID_FREQUENCY] = OID_U32_C(0x17000011),
[DOT11_OID_SUPPORTEDFREQUENCIES] = {0x17000012, 0, sizeof (struct
obj_frequencies)
+ sizeof (u16) * IWMAX_FREQ, 0},
[DOT11_OID_NOISEFLOOR] = OID_U32(0x17000013),
[DOT11_OID_FREQUENCYACTIVITY] =
OID_STRUCT(0x17000014, u8[IWMAX_FREQ + 1]),
[DOT11_OID_IQCALIBRATIONTABLE] = OID_UNKNOWN(0x17000015),
[DOT11_OID_NONERPPROTECTION] = OID_U32(0x17000016),
[DOT11_OID_SLOTSETTINGS] = OID_U32(0x17000017),
[DOT11_OID_NONERPTIMEOUT] = OID_U32(0x17000018),
[DOT11_OID_PROFILES] = OID_U32(0x17000019),
[DOT11_OID_EXTENDEDRATES] =
OID_STRUCT(0x17000020, u8[IWMAX_BITRATES + 1]),
[DOT11_OID_DEAUTHENTICATE] = OID_STRUCT_MLME(0x18000000),
[DOT11_OID_AUTHENTICATE] = OID_STRUCT_MLME(0x18000001),
[DOT11_OID_DISASSOCIATE] = OID_STRUCT_MLME(0x18000002),
[DOT11_OID_ASSOCIATE] = OID_STRUCT_MLME(0x18000003),
[DOT11_OID_SCAN] = OID_UNKNOWN(0x18000004),
[DOT11_OID_BEACON] = OID_STRUCT_MLMEEX(0x18000005),
[DOT11_OID_PROBE] = OID_STRUCT_MLMEEX(0x18000006),
[DOT11_OID_DEAUTHENTICATEEX] = OID_STRUCT_MLMEEX(0x18000007),
[DOT11_OID_AUTHENTICATEEX] = OID_STRUCT_MLMEEX(0x18000008),
[DOT11_OID_DISASSOCIATEEX] = OID_STRUCT_MLMEEX(0x18000009),
[DOT11_OID_ASSOCIATEEX] = OID_STRUCT_MLMEEX(0x1800000A),
[DOT11_OID_REASSOCIATE] = OID_STRUCT_MLMEEX(0x1800000B),
[DOT11_OID_REASSOCIATEEX] = OID_STRUCT_MLMEEX(0x1800000C),
[DOT11_OID_NONERPSTATUS] = OID_U32(0x1E000000),
[DOT11_OID_STATIMEOUT] = OID_U32(0x19000000),
[DOT11_OID_MLMEAUTOLEVEL] = OID_U32_C(0x19000001),
[DOT11_OID_BSSTIMEOUT] = OID_U32(0x19000002),
[DOT11_OID_ATTACHMENT] = OID_UNKNOWN(0x19000003),
[DOT11_OID_PSMBUFFER] = OID_STRUCT_C(0x19000004, struct obj_buffer),
[DOT11_OID_BSSS] = OID_U32(0x1C000000),
[DOT11_OID_BSSX] = {0x1C000001, 63, sizeof (struct obj_bss), 0}, /*DOT11_OID_BSS1,...,DOT11_OID_BSS64 */
[DOT11_OID_BSSFIND] = OID_STRUCT(0x1C000042, struct obj_bss),
[DOT11_OID_BSSLIST] = {0x1C000043, 0, sizeof (struct
obj_bsslist) +
sizeof (struct obj_bss[IWMAX_BSS]), 0},
[OID_INL_TUNNEL] = OID_UNKNOWN(0xFF020000),
[OID_INL_MEMADDR] = OID_UNKNOWN(0xFF020001),
[OID_INL_MEMORY] = OID_UNKNOWN(0xFF020002),
[OID_INL_MODE] = OID_U32_C(0xFF020003),
[OID_INL_COMPONENT_NR] = OID_UNKNOWN(0xFF020004),
[OID_INL_VERSION] = OID_UNKNOWN(0xFF020005),
[OID_INL_INTERFACE_ID] = OID_UNKNOWN(0xFF020006),
[OID_INL_COMPONENT_ID] = OID_UNKNOWN(0xFF020007),
[OID_INL_CONFIG] = OID_U32_C(0xFF020008),
[OID_INL_DOT11D_CONFORMANCE] = OID_U32_C(0xFF02000C),
[OID_INL_PHYCAPABILITIES] = OID_U32(0xFF02000D),
[OID_INL_OUTPUTPOWER] = OID_U32_C(0xFF02000F),
};
int
mgt_init(islpci_private *priv)
{
int i;
priv->mib = kmalloc(OID_NUM_LAST * sizeof (void *), GFP_KERNEL);
if (!priv->mib)
return -ENOMEM;
memset(priv->mib, 0, OID_NUM_LAST * sizeof (void *));
/* Alloc the cache */
for (i = 0; i < OID_NUM_LAST; i++) {
if (isl_oid[i].flags & OID_FLAG_CACHED) {
priv->mib[i] = kmalloc(isl_oid[i].size *
(isl_oid[i].range + 1),
GFP_KERNEL);
if (!priv->mib[i])
return -ENOMEM;
memset(priv->mib[i], 0,
isl_oid[i].size * (isl_oid[i].range + 1));
} else
priv->mib[i] = NULL;
}
init_rwsem(&priv->mib_sem);
prism54_mib_init(priv);
return 0;
}
void
mgt_clean(islpci_private *priv)
{
int i;
if (!priv->mib)
return;
for (i = 0; i < OID_NUM_LAST; i++)
if (priv->mib[i]) {
kfree(priv->mib[i]);
priv->mib[i] = NULL;
}
kfree(priv->mib);
priv->mib = NULL;
}
int
mgt_set_request(islpci_private *priv, enum oid_num_t n, int extra, void *data)
{
int ret = 0;
struct islpci_mgmtframe *response;
int response_op = PIMFOR_OP_ERROR;
int dlen;
void *cache, *_data = data;
u32 oid, u;
BUG_ON(OID_NUM_LAST <= n);
BUG_ON(extra > isl_oid[n].range);
if (!priv->mib)
/* memory has been freed */
return -1;
dlen = isl_oid[n].size;
cache = priv->mib[n];
cache += (cache ? extra * dlen : 0);
oid = isl_oid[n].oid + extra;
if (data == NULL)
/* we are requested to re-set a cached value */
_data = cache;
if ((isl_oid[n].flags & OID_FLAG_U32) && data) {
u = cpu_to_le32(*(u32 *) data);
_data = &u;
}
/* If we are going to write to the cache, we don't want anyone to read
* it -> acquire write lock.
* Else we could acquire a read lock to be sure we don't bother the
* commit process (which takes a write lock). But I'm not sure if it's
* needed.
*/
if (cache)
down_write(&priv->mib_sem);
if (islpci_get_state(priv) >= PRV_STATE_INIT) {
ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, oid,
_data, dlen, &response);
if (!ret) {
response_op = response->header->operation;
islpci_mgt_release(response);
}
if (ret || response_op == PIMFOR_OP_ERROR)
ret = -EIO;
} else if (!cache)
ret = -EIO;
if (cache) {
if (!ret && data)
memcpy(cache, _data, dlen);
up_write(&priv->mib_sem);
}
return ret;
}
int
mgt_get_request(islpci_private *priv, enum oid_num_t n, int extra, void *data,
union oid_res_t *res)
{
int ret = -EIO;
int reslen = 0;
struct islpci_mgmtframe *response = NULL;
int dlen;
void *cache, *_res=NULL;
u32 oid;
BUG_ON(OID_NUM_LAST <= n);
BUG_ON(extra > isl_oid[n].range);
if (!priv->mib)
/* memory has been freed */
return -1;
dlen = isl_oid[n].size;
cache = priv->mib[n];
cache += cache ? extra * dlen : 0;
oid = isl_oid[n].oid + extra;
reslen = dlen;
if (cache)
down_read(&priv->mib_sem);
if (islpci_get_state(priv) >= PRV_STATE_INIT) {
ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET,
oid, data, dlen, &response);
if (ret || !response ||
response->header->operation == PIMFOR_OP_ERROR) {
if (response)
islpci_mgt_release(response);
ret = -EIO;
}
if (!ret) {
_res = response->data;
reslen = response->header->length;
}
} else if (cache) {
_res = cache;
ret = 0;
}
if (isl_oid[n].flags & OID_FLAG_U32) {
if (ret)
res->u = 0;
else
res->u = le32_to_cpu(*(u32 *) _res);
} else {
res->ptr = kmalloc(reslen, GFP_KERNEL);
BUG_ON(res->ptr == NULL);
if (ret)
memset(res->ptr, 0, reslen);
else
memcpy(res->ptr, _res, reslen);
}
if (cache)
up_read(&priv->mib_sem);
if (response && !ret)
islpci_mgt_release(response);
if (reslen > isl_oid[n].size)
printk(KERN_DEBUG
"mgt_get_request(0x%x): received data length was bigger "
"than expected (%d > %d). Memory is probably corrupted... ",
oid, reslen, isl_oid[n].size);
return ret;
}
/* lock outside */
int
mgt_commit_list(islpci_private *priv, enum oid_num_t *l, int n)
{
int i, ret = 0;
struct islpci_mgmtframe *response;
for (i = 0; i < n; i++) {
struct oid_t *t = &(isl_oid[l[i]]);
void *data = priv->mib[l[i]];
int j = 0;
u32 oid = t->oid;
BUG_ON(data == NULL);
while (j <= t->range){
response = NULL;
ret |= islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET,
oid, data, t->size,
&response);
if (response) {
ret |= (response->header->operation ==
PIMFOR_OP_ERROR);
islpci_mgt_release(response);
}
j++;
oid++;
data += t->size;
}
}
return ret;
}
/* Lock outside */
void
mgt_set(islpci_private *priv, enum oid_num_t n, void *data)
{
BUG_ON(OID_NUM_LAST <= n);
BUG_ON(priv->mib[n] == NULL);
memcpy(priv->mib[n], data, isl_oid[n].size);
if (isl_oid[n].flags & OID_FLAG_U32)
*(u32 *) priv->mib[n] = cpu_to_le32(*(u32 *) priv->mib[n]);
}
/* Commits the cache. If something goes wrong, it restarts the device. Lock
* outside
*/
static enum oid_num_t commit_part1[] = {
OID_INL_CONFIG,
OID_INL_MODE,
DOT11_OID_BSSTYPE,
DOT11_OID_CHANNEL,
DOT11_OID_MLMEAUTOLEVEL
};
static enum oid_num_t commit_part2[] = {
DOT11_OID_SSID,
DOT11_OID_PSMBUFFER,
DOT11_OID_AUTHENABLE,
DOT11_OID_PRIVACYINVOKED,
DOT11_OID_EXUNENCRYPTED,
DOT11_OID_DEFKEYX, /* MULTIPLE */
DOT11_OID_DEFKEYID,
DOT11_OID_DOT1XENABLE,
OID_INL_DOT11D_CONFORMANCE,
OID_INL_OUTPUTPOWER,
};
void
mgt_commit(islpci_private *priv)
{
int rvalue;
u32 u;
union oid_res_t r;
if (islpci_get_state(priv) < PRV_STATE_INIT)
return;
rvalue = mgt_commit_list(priv, commit_part1,
sizeof (commit_part1) /
sizeof (commit_part1[0]));
if (priv->iw_mode != IW_MODE_MONITOR)
rvalue |= mgt_commit_list(priv, commit_part2,
sizeof (commit_part2) /
sizeof (commit_part2[0]));
u = OID_INL_MODE;
rvalue |= mgt_commit_list(priv, &u, 1);
if (rvalue) {
/* some request have failed. The device might be in an
incoherent state. We should reset it ! */
printk(KERN_DEBUG "%s: mgt_commit has failed. Restart the "
"device \n", priv->ndev->name);
}
/* update the MAC addr. As it's not cached, no lock will be acquired by
* the mgt_get_request
*/
mgt_get_request(priv, GEN_OID_MACADDRESS, 0, NULL, &r);
memcpy(priv->ndev->dev_addr, r.ptr, 6);
kfree(r.ptr);
}
/* This will tell you if you are allowed to answer a mlme(ex) request .*/
inline int
mgt_mlme_answer(islpci_private *priv)
{
u32 mlmeautolevel;
/* Acquire a read lock because if we are in a mode change, it's
* possible to answer true, while the card is leaving master to managed
* mode. Answering to a mlme in this situation could hang the card.
*/
down_read(&priv->mib_sem);
mlmeautolevel =
le32_to_cpu(*(u32 *) priv->mib[DOT11_OID_MLMEAUTOLEVEL]);
up_read(&priv->mib_sem);
return ((priv->iw_mode == IW_MODE_MASTER) &&
(mlmeautolevel >= DOT11_MLME_INTERMEDIATE));
}
inline enum oid_num_t
mgt_oidtonum(u32 oid)
{
int i;
for (i = 0; i < OID_NUM_LAST - 1; i++)
if (isl_oid[i].oid == oid)
return i;
printk(KERN_DEBUG "looking for an unknown oid 0x%x", oid);
return 0;
}
/*
* Copyright (C) 2003 Aurelien Alleaume <slts@free.fr>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#if !defined(_OID_MGT_H)
#define _OID_MGT_H
#include "isl_oid.h"
#include "islpci_dev.h"
extern struct oid_t isl_oid[];
int mgt_init(islpci_private *);
void mgt_clean(islpci_private *);
extern const int frequency_list_bg[];
extern const int frequency_list_a[];
int mgt_set_request(islpci_private *, enum oid_num_t, int, void *);
int mgt_get_request(islpci_private *, enum oid_num_t, int, void *,
union oid_res_t *);
int mgt_commit_list(islpci_private *, enum oid_num_t *, int);
void mgt_set(islpci_private *, enum oid_num_t, void *);
void mgt_commit(islpci_private *);
int mgt_mlme_answer(islpci_private *);
enum oid_num_t mgt_oidtonum(u32 oid);
#endif /* !defined(_OID_MGT_H) */
/* EOF */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment