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
Kirill Smelkov
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
...
@@ -307,6 +307,53 @@ config PCMCIA_WL3501
It has basic support for Linux wireless extensions and initial
It has basic support for Linux wireless extensions and initial
micro support for ethtool.
micro support for ethtool.
comment "Prism GT/Duette 802.11(a/b/g) PCI/PCMCIA support"
depends on NET_RADIO && PCI
config PRISM54
tristate 'Intersil Prism GT/Duette/Indigo PCI/PCMCIA'
depends on PCI && NET_RADIO && EXPERIMENTAL && HOTPLUG
select FW_LOADER
---help---
Enable PCI and Cardbus support for the following chipset based cards:
ISL3880 - Prism GT 802.11 b/g
ISL3877 - Prism Indigo 802.11 a
ISL3890 - Prism Duette 802.11 a/b/g
For a complete list of supported cards visit <http://prism54.org>.
Here is the latest confirmed list of supported cards:
3com OfficeConnect 11g Cardbus Card aka 3CRWE154G72
Allnet ALL0271 PCI Card
Compex WL54G Cardbus Card
Corega CG-WLCB54GT Cardbus Card
D-Link Air Plus Xtreme G A1 Cardbus Card aka DWL-g650
I-O Data WN-G54/CB Cardbus Card
Kobishi XG-300 aka Z-Com Cardbus Card
Netgear WG511 Cardbus Card
Ovislink WL-5400PCI PCI Card
Peabird WLG-PCI PCI Card
Sitecom WL-100i Cardbus Card
Sitecom WL-110i PCI Card
SMC2802W - EZ Connect g 2.4GHz 54 Mbps Wireless PCI Card
SMC2835W - EZ Connect g 2.4GHz 54 Mbps Wireless Cardbus Card
Z-Com XG-900 PCI Card
Zyxel G-100 Cardbus Card
If you enable this you will need a firmware file as well.
You will need to copy this to /usr/lib/hotplug/firmware/isl3890.
You can get this non-GPL'd firmware file from the Prism54 project page:
<http://prism54.org>
You will also need the /etc/hotplug/firmware.agent script from
a current hotplug package.
Note: You need a motherboard with DMA support to use any of these cards
If you want to compile the driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read <file:Documentation/modules.txt>. The module
will be called prism54.ko.
# yes, this works even when no drivers are selected
# yes, this works even when no drivers are selected
config NET_WIRELESS
config NET_WIRELESS
bool
bool
...
...
drivers/net/wireless/Makefile
View file @
8eae4cbf
...
@@ -26,6 +26,8 @@ obj-$(CONFIG_ATMEL) += atmel.o
...
@@ -26,6 +26,8 @@ obj-$(CONFIG_ATMEL) += atmel.o
obj-$(CONFIG_PCI_ATMEL)
+=
atmel_pci.o
obj-$(CONFIG_PCI_ATMEL)
+=
atmel_pci.o
obj-$(CONFIG_PCMCIA_ATMEL)
+=
atmel_cs.o
obj-$(CONFIG_PCMCIA_ATMEL)
+=
atmel_cs.o
obj-$(CONFIG_PRISM54)
+=
prism54/
# 16-bit wireless PCMCIA client drivers
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS)
+=
ray_cs.o
obj-$(CONFIG_PCMCIA_RAYCS)
+=
ray_cs.o
obj-$(CONFIG_PCMCIA_WL3501)
+=
wl3501_cs.o
obj-$(CONFIG_PCMCIA_WL3501)
+=
wl3501_cs.o
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