Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
8eae4cbf
Commit
8eae4cbf
authored
Mar 12, 2004
by
Jeff Garzik
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[wireless] Add new Prism54 wireless driver.
parent
0a912921
Changes
17
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
6439 additions
and
0 deletions
+6439
-0
drivers/net/wireless/Kconfig
drivers/net/wireless/Kconfig
+47
-0
drivers/net/wireless/Makefile
drivers/net/wireless/Makefile
+2
-0
drivers/net/wireless/prism54/Makefile
drivers/net/wireless/prism54/Makefile
+10
-0
drivers/net/wireless/prism54/isl_38xx.c
drivers/net/wireless/prism54/isl_38xx.c
+397
-0
drivers/net/wireless/prism54/isl_38xx.h
drivers/net/wireless/prism54/isl_38xx.h
+179
-0
drivers/net/wireless/prism54/isl_ioctl.c
drivers/net/wireless/prism54/isl_ioctl.c
+2174
-0
drivers/net/wireless/prism54/isl_ioctl.h
drivers/net/wireless/prism54/isl_ioctl.h
+60
-0
drivers/net/wireless/prism54/isl_oid.h
drivers/net/wireless/prism54/isl_oid.h
+365
-0
drivers/net/wireless/prism54/islpci_dev.c
drivers/net/wireless/prism54/islpci_dev.c
+828
-0
drivers/net/wireless/prism54/islpci_dev.h
drivers/net/wireless/prism54/islpci_dev.h
+228
-0
drivers/net/wireless/prism54/islpci_eth.c
drivers/net/wireless/prism54/islpci_eth.c
+429
-0
drivers/net/wireless/prism54/islpci_eth.h
drivers/net/wireless/prism54/islpci_eth.h
+31
-0
drivers/net/wireless/prism54/islpci_hotplug.c
drivers/net/wireless/prism54/islpci_hotplug.c
+428
-0
drivers/net/wireless/prism54/islpci_mgt.c
drivers/net/wireless/prism54/islpci_mgt.c
+512
-0
drivers/net/wireless/prism54/islpci_mgt.h
drivers/net/wireless/prism54/islpci_mgt.h
+166
-0
drivers/net/wireless/prism54/oid_mgt.c
drivers/net/wireless/prism54/oid_mgt.c
+532
-0
drivers/net/wireless/prism54/oid_mgt.h
drivers/net/wireless/prism54/oid_mgt.h
+51
-0
No files found.
drivers/net/wireless/Kconfig
View file @
8eae4cbf
...
...
@@ -307,6 +307,53 @@ config PCMCIA_WL3501
It has basic support for Linux wireless extensions and initial
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
config NET_WIRELESS
bool
...
...
drivers/net/wireless/Makefile
View file @
8eae4cbf
...
...
@@ -26,6 +26,8 @@ obj-$(CONFIG_ATMEL) += atmel.o
obj-$(CONFIG_PCI_ATMEL)
+=
atmel_pci.o
obj-$(CONFIG_PCMCIA_ATMEL)
+=
atmel_cs.o
obj-$(CONFIG_PRISM54)
+=
prism54/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS)
+=
ray_cs.o
obj-$(CONFIG_PCMCIA_WL3501)
+=
wl3501_cs.o
drivers/net/wireless/prism54/Makefile
0 → 100644
View file @
8eae4cbf
# $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
drivers/net/wireless/prism54/isl_38xx.c
0 → 100644
View file @
8eae4cbf
/* $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
;
}
drivers/net/wireless/prism54/isl_38xx.h
0 → 100644
View file @
8eae4cbf
/* $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 */
drivers/net/wireless/prism54/isl_ioctl.c
0 → 100644
View file @
8eae4cbf
/* $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:
\n
0: Auto
\n
1: Ad-Hoc
\n
2: Managed Client (Default)
\n
3: Master / Access Point
\n
4: Repeater (Not supported yet)
\n
5: Secondary (Not supported yet)
\n
6: 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:
\n
0 0x0000: None
\n
1 0x0001: DOT11_AUTH_OS (Default)
\n
2 0x0002: DOT11_AUTH_SK
\n
3 0x0003: DOT11_AUTH_BOTH"
);
MODULE_PARM
(
init_dot1x
,
"i"
);
MODULE_PARM_DESC
(
init_dot1x
,
"
\n
0: None/not set (Default)
\n
1: DOT11_DOT1X_AUTHENABLED
\n
2: DOT11_DOT1X_KEYTXENABLED"
);
MODULE_PARM
(
init_mlme
,
"i"
);
MODULE_PARM_DESC
(
init_mlme
,
"Sets the MAC layer management entity (MLME) mode of operation,
\n
0: DOT11_MLME_AUTO (Default)
\n
1: DOT11_MLME_INTERMEDIATE
\n
2: 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
\t
len: %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 */
drivers/net/wireless/prism54/isl_ioctl.h
0 → 100644
View file @
8eae4cbf
/* $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 */
drivers/net/wireless/prism54/isl_oid.h
0 → 100644
View file @
8eae4cbf
/*
* $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 */
drivers/net/wireless/prism54/islpci_dev.c
0 → 100644
View file @
8eae4cbf
/* $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
;
}
drivers/net/wireless/prism54/islpci_dev.h
0 → 100644
View file @
8eae4cbf
/* $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 */
drivers/net/wireless/prism54/islpci_eth.c
0 → 100644
View file @
8eae4cbf
/* $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
,
"
\n
tx %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
,
"
\n
rx %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
,
"
\n
rx %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
;
}
drivers/net/wireless/prism54/islpci_eth.h
0 → 100644
View file @
8eae4cbf
/* $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 */
drivers/net/wireless/prism54/islpci_hotplug.c
0 → 100644
View file @
8eae4cbf
/* $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 */
drivers/net/wireless/prism54/islpci_mgt.c
0 → 100644
View file @
8eae4cbf
/* $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
;
}
drivers/net/wireless/prism54/islpci_mgt.h
0 → 100644
View file @
8eae4cbf
/* $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 */
drivers/net/wireless/prism54/oid_mgt.c
0 → 100644
View file @
8eae4cbf
/*
* 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
;
}
drivers/net/wireless/prism54/oid_mgt.h
0 → 100644
View file @
8eae4cbf
/*
* 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 */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment