mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-29 05:55:02 +08:00
Add HostAP wireless driver.
Includes minor cleanups from Adrian Bunk <bunk@stusta.de>.
This commit is contained in:
parent
88d7bd8cb9
commit
ff1d2767d5
@ -965,6 +965,13 @@ M: mike.miller@hp.com
|
||||
L: iss_storagedev@hp.com
|
||||
S: Supported
|
||||
|
||||
HOST AP DRIVER
|
||||
P: Jouni Malinen
|
||||
M: jkmaline@cc.hut.fi
|
||||
L: hostap@shmoo.com
|
||||
W: http://hostap.epitest.fi/
|
||||
S: Maintained
|
||||
|
||||
HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
|
||||
P: Jaroslav Kysela
|
||||
M: perex@suse.cz
|
||||
|
@ -355,6 +355,8 @@ config PRISM54
|
||||
say M here and read <file:Documentation/modules.txt>. The module
|
||||
will be called prism54.ko.
|
||||
|
||||
source "drivers/net/wireless/hostap/Kconfig"
|
||||
|
||||
# yes, this works even when no drivers are selected
|
||||
config NET_WIRELESS
|
||||
bool
|
||||
|
@ -28,6 +28,8 @@ obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
|
||||
|
||||
obj-$(CONFIG_PRISM54) += prism54/
|
||||
|
||||
obj-$(CONFIG_HOSTAP) += hostap/
|
||||
|
||||
# 16-bit wireless PCMCIA client drivers
|
||||
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
|
||||
obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
|
||||
|
@ -1040,7 +1040,7 @@ typedef struct {
|
||||
u16 status;
|
||||
} WifiCtlHdr;
|
||||
|
||||
WifiCtlHdr wifictlhdr8023 = {
|
||||
static WifiCtlHdr wifictlhdr8023 = {
|
||||
.ctlhdr = {
|
||||
.ctl = HOST_DONT_RLSE,
|
||||
}
|
||||
@ -1111,13 +1111,13 @@ static int airo_thread(void *data);
|
||||
static void timer_func( struct net_device *dev );
|
||||
static int airo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
|
||||
#ifdef WIRELESS_EXT
|
||||
struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
|
||||
static struct iw_statistics *airo_get_wireless_stats (struct net_device *dev);
|
||||
static void airo_read_wireless_stats (struct airo_info *local);
|
||||
#endif /* WIRELESS_EXT */
|
||||
#ifdef CISCO_EXT
|
||||
static int readrids(struct net_device *dev, aironet_ioctl *comp);
|
||||
static int writerids(struct net_device *dev, aironet_ioctl *comp);
|
||||
int flashcard(struct net_device *dev, aironet_ioctl *comp);
|
||||
static int flashcard(struct net_device *dev, aironet_ioctl *comp);
|
||||
#endif /* CISCO_EXT */
|
||||
#ifdef MICSUPPORT
|
||||
static void micinit(struct airo_info *ai);
|
||||
@ -1223,6 +1223,12 @@ static int setup_proc_entry( struct net_device *dev,
|
||||
static int takedown_proc_entry( struct net_device *dev,
|
||||
struct airo_info *apriv );
|
||||
|
||||
static int cmdreset(struct airo_info *ai);
|
||||
static int setflashmode (struct airo_info *ai);
|
||||
static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime);
|
||||
static int flashputbuf(struct airo_info *ai);
|
||||
static int flashrestart(struct airo_info *ai,struct net_device *dev);
|
||||
|
||||
#ifdef MICSUPPORT
|
||||
/***********************************************************************
|
||||
* MIC ROUTINES *
|
||||
@ -1231,10 +1237,11 @@ static int takedown_proc_entry( struct net_device *dev,
|
||||
|
||||
static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq);
|
||||
static void MoveWindow(miccntx *context, u32 micSeq);
|
||||
void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *);
|
||||
void emmh32_init(emmh32_context *context);
|
||||
void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
|
||||
void emmh32_final(emmh32_context *context, u8 digest[4]);
|
||||
static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *);
|
||||
static void emmh32_init(emmh32_context *context);
|
||||
static void emmh32_update(emmh32_context *context, u8 *pOctets, int len);
|
||||
static void emmh32_final(emmh32_context *context, u8 digest[4]);
|
||||
static int flashpchar(struct airo_info *ai,int byte,int dwelltime);
|
||||
|
||||
/* micinit - Initialize mic seed */
|
||||
|
||||
@ -1312,7 +1319,7 @@ static int micsetup(struct airo_info *ai) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
char micsnap[]= {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
|
||||
static char micsnap[] = {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02};
|
||||
|
||||
/*===========================================================================
|
||||
* Description: Mic a packet
|
||||
@ -1567,7 +1574,7 @@ static void MoveWindow(miccntx *context, u32 micSeq)
|
||||
static unsigned char aes_counter[16];
|
||||
|
||||
/* expand the key to fill the MMH coefficient array */
|
||||
void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *tfm)
|
||||
static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto_tfm *tfm)
|
||||
{
|
||||
/* take the keying material, expand if necessary, truncate at 16-bytes */
|
||||
/* run through AES counter mode to generate context->coeff[] */
|
||||
@ -1599,7 +1606,7 @@ void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, struct crypto
|
||||
}
|
||||
|
||||
/* prepare for calculation of a new mic */
|
||||
void emmh32_init(emmh32_context *context)
|
||||
static void emmh32_init(emmh32_context *context)
|
||||
{
|
||||
/* prepare for new mic calculation */
|
||||
context->accum = 0;
|
||||
@ -1607,7 +1614,7 @@ void emmh32_init(emmh32_context *context)
|
||||
}
|
||||
|
||||
/* add some bytes to the mic calculation */
|
||||
void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
|
||||
static void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
|
||||
{
|
||||
int coeff_position, byte_position;
|
||||
|
||||
@ -1649,7 +1656,7 @@ void emmh32_update(emmh32_context *context, u8 *pOctets, int len)
|
||||
static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L };
|
||||
|
||||
/* calculate the mic */
|
||||
void emmh32_final(emmh32_context *context, u8 digest[4])
|
||||
static void emmh32_final(emmh32_context *context, u8 digest[4])
|
||||
{
|
||||
int coeff_position, byte_position;
|
||||
u32 val;
|
||||
@ -2251,7 +2258,7 @@ static void airo_read_stats(struct airo_info *ai) {
|
||||
ai->stats.rx_fifo_errors = vals[0];
|
||||
}
|
||||
|
||||
struct net_device_stats *airo_get_stats(struct net_device *dev)
|
||||
static struct net_device_stats *airo_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct airo_info *local = dev->priv;
|
||||
|
||||
@ -2410,7 +2417,7 @@ EXPORT_SYMBOL(stop_airo_card);
|
||||
|
||||
static int add_airo_dev( struct net_device *dev );
|
||||
|
||||
int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
|
||||
static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr)
|
||||
{
|
||||
memcpy(haddr, skb->mac.raw + 10, ETH_ALEN);
|
||||
return ETH_ALEN;
|
||||
@ -2677,7 +2684,7 @@ static struct net_device *init_wifidev(struct airo_info *ai,
|
||||
return dev;
|
||||
}
|
||||
|
||||
int reset_card( struct net_device *dev , int lock) {
|
||||
static int reset_card( struct net_device *dev , int lock) {
|
||||
struct airo_info *ai = dev->priv;
|
||||
|
||||
if (lock && down_interruptible(&ai->sem))
|
||||
@ -2692,9 +2699,9 @@ int reset_card( struct net_device *dev , int lock) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct net_device *_init_airo_card( unsigned short irq, int port,
|
||||
int is_pcmcia, struct pci_dev *pci,
|
||||
struct device *dmdev )
|
||||
static struct net_device *_init_airo_card( unsigned short irq, int port,
|
||||
int is_pcmcia, struct pci_dev *pci,
|
||||
struct device *dmdev )
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct airo_info *ai;
|
||||
@ -7177,7 +7184,7 @@ static void airo_read_wireless_stats(struct airo_info *local)
|
||||
local->wstats.miss.beacon = vals[34];
|
||||
}
|
||||
|
||||
struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
|
||||
static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
|
||||
{
|
||||
struct airo_info *local = dev->priv;
|
||||
|
||||
@ -7392,14 +7399,8 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) {
|
||||
* Flash command switch table
|
||||
*/
|
||||
|
||||
int flashcard(struct net_device *dev, aironet_ioctl *comp) {
|
||||
static int flashcard(struct net_device *dev, aironet_ioctl *comp) {
|
||||
int z;
|
||||
int cmdreset(struct airo_info *);
|
||||
int setflashmode(struct airo_info *);
|
||||
int flashgchar(struct airo_info *,int,int);
|
||||
int flashpchar(struct airo_info *,int,int);
|
||||
int flashputbuf(struct airo_info *);
|
||||
int flashrestart(struct airo_info *,struct net_device *);
|
||||
|
||||
/* Only super-user can modify flash */
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
@ -7457,7 +7458,7 @@ int flashcard(struct net_device *dev, aironet_ioctl *comp) {
|
||||
* card.
|
||||
*/
|
||||
|
||||
int cmdreset(struct airo_info *ai) {
|
||||
static int cmdreset(struct airo_info *ai) {
|
||||
disable_MAC(ai, 1);
|
||||
|
||||
if(!waitbusy (ai)){
|
||||
@ -7481,7 +7482,7 @@ int cmdreset(struct airo_info *ai) {
|
||||
* mode
|
||||
*/
|
||||
|
||||
int setflashmode (struct airo_info *ai) {
|
||||
static int setflashmode (struct airo_info *ai) {
|
||||
set_bit (FLAG_FLASHING, &ai->flags);
|
||||
|
||||
OUT4500(ai, SWS0, FLASH_COMMAND);
|
||||
@ -7508,7 +7509,7 @@ int setflashmode (struct airo_info *ai) {
|
||||
* x 50us for echo .
|
||||
*/
|
||||
|
||||
int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
|
||||
static int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
|
||||
int echo;
|
||||
int waittime;
|
||||
|
||||
@ -7548,7 +7549,7 @@ int flashpchar(struct airo_info *ai,int byte,int dwelltime) {
|
||||
* Get a character from the card matching matchbyte
|
||||
* Step 3)
|
||||
*/
|
||||
int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
|
||||
static int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
|
||||
int rchar;
|
||||
unsigned char rbyte=0;
|
||||
|
||||
@ -7579,7 +7580,7 @@ int flashgchar(struct airo_info *ai,int matchbyte,int dwelltime){
|
||||
* send to the card
|
||||
*/
|
||||
|
||||
int flashputbuf(struct airo_info *ai){
|
||||
static int flashputbuf(struct airo_info *ai){
|
||||
int nwords;
|
||||
|
||||
/* Write stuff */
|
||||
@ -7601,7 +7602,7 @@ int flashputbuf(struct airo_info *ai){
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int flashrestart(struct airo_info *ai,struct net_device *dev){
|
||||
static int flashrestart(struct airo_info *ai,struct net_device *dev){
|
||||
int i,status;
|
||||
|
||||
ssleep(1); /* Added 12/7/00 */
|
||||
|
104
drivers/net/wireless/hostap/Kconfig
Normal file
104
drivers/net/wireless/hostap/Kconfig
Normal file
@ -0,0 +1,104 @@
|
||||
config HOSTAP
|
||||
tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"
|
||||
depends on NET_RADIO
|
||||
---help---
|
||||
Shared driver code for IEEE 802.11b wireless cards based on
|
||||
Intersil Prism2/2.5/3 chipset. This driver supports so called
|
||||
Host AP mode that allows the card to act as an IEEE 802.11
|
||||
access point.
|
||||
|
||||
In addition, this includes generic IEEE 802.11 code, e.g., for
|
||||
WEP/TKIP/CCMP encryption that can be shared with other drivers.
|
||||
|
||||
See <http://hostap.epitest.fi/> for more information about the
|
||||
Host AP driver configuration and tools. This site includes
|
||||
information and tools (hostapd and wpa_supplicant) for WPA/WPA2
|
||||
support.
|
||||
|
||||
This option includes the base Host AP driver code that is shared by
|
||||
different hardware models. You will also need to enable support for
|
||||
PLX/PCI/CS version of the driver to actually use the driver.
|
||||
|
||||
The driver can be compiled as a module and it will be called
|
||||
"hostap.ko".
|
||||
|
||||
config HOSTAP_WEP
|
||||
tristate "IEEE 802.11 WEP encryption"
|
||||
depends on HOSTAP
|
||||
select CRYPTO
|
||||
---help---
|
||||
Software implementation of IEEE 802.11 WEP encryption.
|
||||
|
||||
This can be compiled as a modules and it will be called
|
||||
"hostap_crypt_wep.ko".
|
||||
|
||||
config HOSTAP_TKIP
|
||||
tristate "IEEE 802.11 TKIP encryption"
|
||||
depends on HOSTAP
|
||||
select CRYPTO
|
||||
---help---
|
||||
Software implementation of IEEE 802.11 TKIP encryption.
|
||||
|
||||
This can be compiled as a modules and it will be called
|
||||
"hostap_crypt_tkip.ko".
|
||||
|
||||
config HOSTAP_CCMP
|
||||
tristate "IEEE 802.11 CCMP encryption"
|
||||
depends on HOSTAP
|
||||
select CRYPTO
|
||||
---help---
|
||||
Software implementation of IEEE 802.11 CCMP encryption.
|
||||
|
||||
This can be compiled as a modules and it will be called
|
||||
"hostap_crypt_ccmp.ko".
|
||||
|
||||
config HOSTAP_FIRMWARE
|
||||
bool "Support downloading firmware images with Host AP driver"
|
||||
depends on HOSTAP
|
||||
---help---
|
||||
Configure Host AP driver to include support for firmware image
|
||||
download. Current version supports only downloading to volatile, i.e.,
|
||||
RAM memory. Flash upgrade is not yet supported.
|
||||
|
||||
Firmware image downloading needs user space tool, prism2_srec. It is
|
||||
available from http://hostap.epitest.fi/.
|
||||
|
||||
config HOSTAP_PLX
|
||||
tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors"
|
||||
depends on PCI && HOSTAP
|
||||
---help---
|
||||
Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based
|
||||
PCI adaptors.
|
||||
|
||||
"Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
|
||||
driver and its help text includes more information about the Host AP
|
||||
driver.
|
||||
|
||||
The driver can be compiled as a module and will be named
|
||||
"hostap_plx.ko".
|
||||
|
||||
config HOSTAP_PCI
|
||||
tristate "Host AP driver for Prism2.5 PCI adaptors"
|
||||
depends on PCI && HOSTAP
|
||||
---help---
|
||||
Host AP driver's version for Prism2.5 PCI adaptors.
|
||||
|
||||
"Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
|
||||
driver and its help text includes more information about the Host AP
|
||||
driver.
|
||||
|
||||
The driver can be compiled as a module and will be named
|
||||
"hostap_pci.ko".
|
||||
|
||||
config HOSTAP_CS
|
||||
tristate "Host AP driver for Prism2/2.5/3 PC Cards"
|
||||
depends on PCMCIA!=n && HOSTAP
|
||||
---help---
|
||||
Host AP driver's version for Prism2/2.5/3 PC Cards.
|
||||
|
||||
"Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this
|
||||
driver and its help text includes more information about the Host AP
|
||||
driver.
|
||||
|
||||
The driver can be compiled as a module and will be named
|
||||
"hostap_cs.ko".
|
8
drivers/net/wireless/hostap/Makefile
Normal file
8
drivers/net/wireless/hostap/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
obj-$(CONFIG_HOSTAP) += hostap.o
|
||||
obj-$(CONFIG_HOSTAP_WEP) += hostap_crypt_wep.o
|
||||
obj-$(CONFIG_HOSTAP_TKIP) += hostap_crypt_tkip.o
|
||||
obj-$(CONFIG_HOSTAP_CCMP) += hostap_crypt_ccmp.o
|
||||
|
||||
obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o
|
||||
obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o
|
||||
obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o
|
1207
drivers/net/wireless/hostap/hostap.c
Normal file
1207
drivers/net/wireless/hostap/hostap.c
Normal file
File diff suppressed because it is too large
Load Diff
57
drivers/net/wireless/hostap/hostap.h
Normal file
57
drivers/net/wireless/hostap/hostap.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef HOSTAP_H
|
||||
#define HOSTAP_H
|
||||
|
||||
/* hostap.c */
|
||||
|
||||
extern struct proc_dir_entry *hostap_proc;
|
||||
|
||||
u16 hostap_tx_callback_register(local_info_t *local,
|
||||
void (*func)(struct sk_buff *, int ok, void *),
|
||||
void *data);
|
||||
int hostap_tx_callback_unregister(local_info_t *local, u16 idx);
|
||||
int hostap_set_word(struct net_device *dev, int rid, u16 val);
|
||||
int hostap_set_string(struct net_device *dev, int rid, const char *val);
|
||||
u16 hostap_get_porttype(local_info_t *local);
|
||||
int hostap_set_encryption(local_info_t *local);
|
||||
int hostap_set_antsel(local_info_t *local);
|
||||
int hostap_set_roaming(local_info_t *local);
|
||||
int hostap_set_auth_algs(local_info_t *local);
|
||||
void hostap_dump_rx_header(const char *name,
|
||||
const struct hfa384x_rx_frame *rx);
|
||||
void hostap_dump_tx_header(const char *name,
|
||||
const struct hfa384x_tx_frame *tx);
|
||||
int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr);
|
||||
int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr);
|
||||
int hostap_80211_get_hdrlen(u16 fc);
|
||||
struct net_device_stats *hostap_get_stats(struct net_device *dev);
|
||||
void hostap_setup_dev(struct net_device *dev, local_info_t *local,
|
||||
int main_dev);
|
||||
void hostap_set_multicast_list_queue(void *data);
|
||||
int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked);
|
||||
int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked);
|
||||
void hostap_cleanup(local_info_t *local);
|
||||
void hostap_cleanup_handler(void *data);
|
||||
struct net_device * hostap_add_interface(struct local_info *local,
|
||||
int type, int rtnl_locked,
|
||||
const char *prefix, const char *name);
|
||||
void hostap_remove_interface(struct net_device *dev, int rtnl_locked,
|
||||
int remove_from_list);
|
||||
int prism2_update_comms_qual(struct net_device *dev);
|
||||
int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u8 stype,
|
||||
u8 *body, size_t bodylen);
|
||||
int prism2_sta_deauth(local_info_t *local, u16 reason);
|
||||
|
||||
|
||||
/* hostap_proc.c */
|
||||
|
||||
void hostap_init_proc(local_info_t *local);
|
||||
void hostap_remove_proc(local_info_t *local);
|
||||
|
||||
|
||||
/* hostap_info.c */
|
||||
|
||||
void hostap_info_init(local_info_t *local);
|
||||
void hostap_info_process(local_info_t *local, struct sk_buff *skb);
|
||||
|
||||
|
||||
#endif /* HOSTAP_H */
|
107
drivers/net/wireless/hostap/hostap_80211.h
Normal file
107
drivers/net/wireless/hostap/hostap_80211.h
Normal file
@ -0,0 +1,107 @@
|
||||
#ifndef HOSTAP_80211_H
|
||||
#define HOSTAP_80211_H
|
||||
|
||||
struct hostap_ieee80211_hdr {
|
||||
u16 frame_control;
|
||||
u16 duration_id;
|
||||
u8 addr1[6];
|
||||
u8 addr2[6];
|
||||
u8 addr3[6];
|
||||
u16 seq_ctrl;
|
||||
u8 addr4[6];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
struct hostap_ieee80211_mgmt {
|
||||
u16 frame_control;
|
||||
u16 duration;
|
||||
u8 da[6];
|
||||
u8 sa[6];
|
||||
u8 bssid[6];
|
||||
u16 seq_ctrl;
|
||||
union {
|
||||
struct {
|
||||
u16 auth_alg;
|
||||
u16 auth_transaction;
|
||||
u16 status_code;
|
||||
/* possibly followed by Challenge text */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) auth;
|
||||
struct {
|
||||
u16 reason_code;
|
||||
} __attribute__ ((packed)) deauth;
|
||||
struct {
|
||||
u16 capab_info;
|
||||
u16 listen_interval;
|
||||
/* followed by SSID and Supported rates */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) assoc_req;
|
||||
struct {
|
||||
u16 capab_info;
|
||||
u16 status_code;
|
||||
u16 aid;
|
||||
/* followed by Supported rates */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) assoc_resp, reassoc_resp;
|
||||
struct {
|
||||
u16 capab_info;
|
||||
u16 listen_interval;
|
||||
u8 current_ap[6];
|
||||
/* followed by SSID and Supported rates */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) reassoc_req;
|
||||
struct {
|
||||
u16 reason_code;
|
||||
} __attribute__ ((packed)) disassoc;
|
||||
struct {
|
||||
} __attribute__ ((packed)) probe_req;
|
||||
struct {
|
||||
u8 timestamp[8];
|
||||
u16 beacon_int;
|
||||
u16 capab_info;
|
||||
/* followed by some of SSID, Supported rates,
|
||||
* FH Params, DS Params, CF Params, IBSS Params, TIM */
|
||||
u8 variable[0];
|
||||
} __attribute__ ((packed)) beacon, probe_resp;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
#define IEEE80211_MGMT_HDR_LEN 24
|
||||
#define IEEE80211_DATA_HDR3_LEN 24
|
||||
#define IEEE80211_DATA_HDR4_LEN 30
|
||||
|
||||
|
||||
struct hostap_80211_rx_status {
|
||||
u32 mac_time;
|
||||
u8 signal;
|
||||
u8 noise;
|
||||
u16 rate; /* in 100 kbps */
|
||||
};
|
||||
|
||||
|
||||
void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
|
||||
|
||||
/* prism2_rx_80211 'type' argument */
|
||||
enum {
|
||||
PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC,
|
||||
PRISM2_RX_NULLFUNC_ACK
|
||||
};
|
||||
|
||||
int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats, int type);
|
||||
void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
|
||||
void hostap_dump_tx_80211(const char *name, struct sk_buff *skb);
|
||||
int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
|
||||
struct prism2_crypt_data *crypt);
|
||||
int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
|
||||
#endif /* HOSTAP_80211_H */
|
1084
drivers/net/wireless/hostap/hostap_80211_rx.c
Normal file
1084
drivers/net/wireless/hostap/hostap_80211_rx.c
Normal file
File diff suppressed because it is too large
Load Diff
522
drivers/net/wireless/hostap/hostap_80211_tx.c
Normal file
522
drivers/net/wireless/hostap/hostap_80211_tx.c
Normal file
@ -0,0 +1,522 @@
|
||||
void hostap_dump_tx_80211(const char *name, struct sk_buff *skb)
|
||||
{
|
||||
struct hostap_ieee80211_hdr *hdr;
|
||||
u16 fc;
|
||||
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
|
||||
printk(KERN_DEBUG "%s: TX len=%d jiffies=%ld\n",
|
||||
name, skb->len, jiffies);
|
||||
|
||||
if (skb->len < 2)
|
||||
return;
|
||||
|
||||
fc = le16_to_cpu(hdr->frame_control);
|
||||
printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s",
|
||||
fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc),
|
||||
fc & WLAN_FC_TODS ? " [ToDS]" : "",
|
||||
fc & WLAN_FC_FROMDS ? " [FromDS]" : "");
|
||||
|
||||
if (skb->len < IEEE80211_DATA_HDR3_LEN) {
|
||||
printk("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id),
|
||||
le16_to_cpu(hdr->seq_ctrl));
|
||||
|
||||
printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR,
|
||||
MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3));
|
||||
if (skb->len >= 30)
|
||||
printk(" A4=" MACSTR, MAC2STR(hdr->addr4));
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
|
||||
/* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta)
|
||||
* Convert Ethernet header into a suitable IEEE 802.11 header depending on
|
||||
* device configuration. */
|
||||
int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
int need_headroom, need_tailroom = 0;
|
||||
struct hostap_ieee80211_hdr hdr;
|
||||
u16 fc, ethertype = 0;
|
||||
enum {
|
||||
WDS_NO = 0, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME
|
||||
} use_wds = WDS_NO;
|
||||
u8 *encaps_data;
|
||||
int hdr_len, encaps_len, skip_header_bytes;
|
||||
int to_assoc_ap = 0;
|
||||
struct hostap_skb_tx_data *meta;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
if (skb->len < ETH_HLEN) {
|
||||
printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb "
|
||||
"(len=%d)\n", dev->name, skb->len);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (local->ddev != dev) {
|
||||
use_wds = (local->iw_mode == IW_MODE_MASTER &&
|
||||
!(local->wds_type & HOSTAP_WDS_STANDARD_FRAME)) ?
|
||||
WDS_OWN_FRAME : WDS_COMPLIANT_FRAME;
|
||||
if (dev == local->stadev) {
|
||||
to_assoc_ap = 1;
|
||||
use_wds = WDS_NO;
|
||||
} else if (dev == local->apdev) {
|
||||
printk(KERN_DEBUG "%s: prism2_tx: trying to use "
|
||||
"AP device with Ethernet net dev\n", dev->name);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (local->iw_mode == IW_MODE_REPEAT) {
|
||||
printk(KERN_DEBUG "%s: prism2_tx: trying to use "
|
||||
"non-WDS link in Repeater mode\n", dev->name);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
} else if (local->iw_mode == IW_MODE_INFRA &&
|
||||
(local->wds_type & HOSTAP_WDS_AP_CLIENT) &&
|
||||
memcmp(skb->data + ETH_ALEN, dev->dev_addr,
|
||||
ETH_ALEN) != 0) {
|
||||
/* AP client mode: send frames with foreign src addr
|
||||
* using 4-addr WDS frames */
|
||||
use_wds = WDS_COMPLIANT_FRAME;
|
||||
}
|
||||
}
|
||||
|
||||
/* Incoming skb->data: dst_addr[6], src_addr[6], proto[2], payload
|
||||
* ==>
|
||||
* Prism2 TX frame with 802.11 header:
|
||||
* txdesc (address order depending on used mode; includes dst_addr and
|
||||
* src_addr), possible encapsulation (RFC1042/Bridge-Tunnel;
|
||||
* proto[2], payload {, possible addr4[6]} */
|
||||
|
||||
ethertype = (skb->data[12] << 8) | skb->data[13];
|
||||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
|
||||
/* Length of data after IEEE 802.11 header */
|
||||
encaps_data = NULL;
|
||||
encaps_len = 0;
|
||||
skip_header_bytes = ETH_HLEN;
|
||||
if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
|
||||
encaps_data = bridge_tunnel_header;
|
||||
encaps_len = sizeof(bridge_tunnel_header);
|
||||
skip_header_bytes -= 2;
|
||||
} else if (ethertype >= 0x600) {
|
||||
encaps_data = rfc1042_header;
|
||||
encaps_len = sizeof(rfc1042_header);
|
||||
skip_header_bytes -= 2;
|
||||
}
|
||||
|
||||
fc = (WLAN_FC_TYPE_DATA << 2) | (WLAN_FC_STYPE_DATA << 4);
|
||||
hdr_len = IEEE80211_DATA_HDR3_LEN;
|
||||
|
||||
if (use_wds != WDS_NO) {
|
||||
/* Note! Prism2 station firmware has problems with sending real
|
||||
* 802.11 frames with four addresses; until these problems can
|
||||
* be fixed or worked around, 4-addr frames needed for WDS are
|
||||
* using incompatible format: FromDS flag is not set and the
|
||||
* fourth address is added after the frame payload; it is
|
||||
* assumed, that the receiving station knows how to handle this
|
||||
* frame format */
|
||||
|
||||
if (use_wds == WDS_COMPLIANT_FRAME) {
|
||||
fc |= WLAN_FC_FROMDS | WLAN_FC_TODS;
|
||||
/* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA,
|
||||
* Addr4 = SA */
|
||||
memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
hdr_len += ETH_ALEN;
|
||||
} else {
|
||||
/* bogus 4-addr format to workaround Prism2 station
|
||||
* f/w bug */
|
||||
fc |= WLAN_FC_TODS;
|
||||
/* From DS: Addr1 = DA (used as RA),
|
||||
* Addr2 = BSSID (used as TA), Addr3 = SA (used as DA),
|
||||
*/
|
||||
|
||||
/* SA from skb->data + ETH_ALEN will be added after
|
||||
* frame payload; use hdr.addr4 as a temporary buffer
|
||||
*/
|
||||
memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
need_tailroom += ETH_ALEN;
|
||||
}
|
||||
|
||||
/* send broadcast and multicast frames to broadcast RA, if
|
||||
* configured; otherwise, use unicast RA of the WDS link */
|
||||
if ((local->wds_type & HOSTAP_WDS_BROADCAST_RA) &&
|
||||
skb->data[0] & 0x01)
|
||||
memset(&hdr.addr1, 0xff, ETH_ALEN);
|
||||
else if (iface->type == HOSTAP_INTERFACE_WDS)
|
||||
memcpy(&hdr.addr1, iface->u.wds.remote_addr,
|
||||
ETH_ALEN);
|
||||
else
|
||||
memcpy(&hdr.addr1, local->bssid, ETH_ALEN);
|
||||
memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
|
||||
memcpy(&hdr.addr3, skb->data, ETH_ALEN);
|
||||
} else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) {
|
||||
fc |= WLAN_FC_FROMDS;
|
||||
/* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */
|
||||
memcpy(&hdr.addr1, skb->data, ETH_ALEN);
|
||||
memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
|
||||
memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
} else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) {
|
||||
fc |= WLAN_FC_TODS;
|
||||
/* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
|
||||
memcpy(&hdr.addr1, to_assoc_ap ?
|
||||
local->assoc_ap_addr : local->bssid, ETH_ALEN);
|
||||
memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
memcpy(&hdr.addr3, skb->data, ETH_ALEN);
|
||||
} else if (local->iw_mode == IW_MODE_ADHOC) {
|
||||
/* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
|
||||
memcpy(&hdr.addr1, skb->data, ETH_ALEN);
|
||||
memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
|
||||
memcpy(&hdr.addr3, local->bssid, ETH_ALEN);
|
||||
}
|
||||
|
||||
hdr.frame_control = cpu_to_le16(fc);
|
||||
|
||||
skb_pull(skb, skip_header_bytes);
|
||||
need_headroom = local->func->need_tx_headroom + hdr_len + encaps_len;
|
||||
if (skb_tailroom(skb) < need_tailroom) {
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
if (skb == NULL) {
|
||||
iface->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
if (pskb_expand_head(skb, need_headroom, need_tailroom,
|
||||
GFP_ATOMIC)) {
|
||||
kfree_skb(skb);
|
||||
iface->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
} else if (skb_headroom(skb) < need_headroom) {
|
||||
struct sk_buff *tmp = skb;
|
||||
skb = skb_realloc_headroom(skb, need_headroom);
|
||||
kfree_skb(tmp);
|
||||
if (skb == NULL) {
|
||||
iface->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
if (skb == NULL) {
|
||||
iface->stats.tx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (encaps_data)
|
||||
memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
|
||||
memcpy(skb_push(skb, hdr_len), &hdr, hdr_len);
|
||||
if (use_wds == WDS_OWN_FRAME) {
|
||||
memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN);
|
||||
}
|
||||
|
||||
iface->stats.tx_packets++;
|
||||
iface->stats.tx_bytes += skb->len;
|
||||
|
||||
skb->mac.raw = skb->data;
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
memset(meta, 0, sizeof(*meta));
|
||||
meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
|
||||
meta->wds = use_wds;
|
||||
meta->ethertype = ethertype;
|
||||
meta->iface = iface;
|
||||
|
||||
/* Send IEEE 802.11 encapsulated frame using the master radio device */
|
||||
skb->dev = local->dev;
|
||||
dev_queue_xmit(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* hard_start_xmit function for hostapd wlan#ap interfaces */
|
||||
int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
struct hostap_skb_tx_data *meta;
|
||||
struct hostap_ieee80211_hdr *hdr;
|
||||
u16 fc;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
if (skb->len < 10) {
|
||||
printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb "
|
||||
"(len=%d)\n", dev->name, skb->len);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iface->stats.tx_packets++;
|
||||
iface->stats.tx_bytes += skb->len;
|
||||
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
memset(meta, 0, sizeof(*meta));
|
||||
meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
|
||||
meta->iface = iface;
|
||||
|
||||
if (skb->len >= IEEE80211_DATA_HDR3_LEN + sizeof(rfc1042_header) + 2) {
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
fc = le16_to_cpu(hdr->frame_control);
|
||||
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
|
||||
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_DATA) {
|
||||
u8 *pos = &skb->data[IEEE80211_DATA_HDR3_LEN +
|
||||
sizeof(rfc1042_header)];
|
||||
meta->ethertype = (pos[0] << 8) | pos[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* Send IEEE 802.11 encapsulated frame using the master radio device */
|
||||
skb->dev = local->dev;
|
||||
dev_queue_xmit(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Called only from software IRQ */
|
||||
struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
|
||||
struct prism2_crypt_data *crypt)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
struct hostap_ieee80211_hdr *hdr;
|
||||
u16 fc;
|
||||
int hdr_len, res;
|
||||
|
||||
iface = netdev_priv(skb->dev);
|
||||
local = iface->local;
|
||||
|
||||
if (skb->len < IEEE80211_DATA_HDR3_LEN) {
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (local->tkip_countermeasures &&
|
||||
crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
|
||||
"TX packet to " MACSTR "\n",
|
||||
local->dev->name, MAC2STR(hdr->addr1));
|
||||
}
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skb = skb_unshare(skb, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return NULL;
|
||||
|
||||
if ((skb_headroom(skb) < crypt->ops->extra_prefix_len ||
|
||||
skb_tailroom(skb) < crypt->ops->extra_postfix_len) &&
|
||||
pskb_expand_head(skb, crypt->ops->extra_prefix_len,
|
||||
crypt->ops->extra_postfix_len, GFP_ATOMIC)) {
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
fc = le16_to_cpu(hdr->frame_control);
|
||||
hdr_len = hostap_80211_get_hdrlen(fc);
|
||||
|
||||
/* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so
|
||||
* call both MSDU and MPDU encryption functions from here. */
|
||||
atomic_inc(&crypt->refcnt);
|
||||
res = 0;
|
||||
if (crypt->ops->encrypt_msdu)
|
||||
res = crypt->ops->encrypt_msdu(skb, hdr_len, crypt->priv);
|
||||
if (res == 0 && crypt->ops->encrypt_mpdu)
|
||||
res = crypt->ops->encrypt_mpdu(skb, hdr_len, crypt->priv);
|
||||
atomic_dec(&crypt->refcnt);
|
||||
if (res < 0) {
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
|
||||
/* hard_start_xmit function for master radio interface wifi#.
|
||||
* AP processing (TX rate control, power save buffering, etc.).
|
||||
* Use hardware TX function to send the frame. */
|
||||
int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
int ret = 1;
|
||||
u16 fc;
|
||||
struct hostap_tx_data tx;
|
||||
ap_tx_ret tx_ret;
|
||||
struct hostap_skb_tx_data *meta;
|
||||
int no_encrypt = 0;
|
||||
struct hostap_ieee80211_hdr *hdr;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
tx.skb = skb;
|
||||
tx.sta_ptr = NULL;
|
||||
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) {
|
||||
printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, "
|
||||
"expected 0x%08x)\n",
|
||||
dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC);
|
||||
ret = 0;
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (local->host_encrypt) {
|
||||
/* Set crypt to default algorithm and key; will be replaced in
|
||||
* AP code if STA has own alg/key */
|
||||
tx.crypt = local->crypt[local->tx_keyidx];
|
||||
tx.host_encrypt = 1;
|
||||
} else {
|
||||
tx.crypt = NULL;
|
||||
tx.host_encrypt = 0;
|
||||
}
|
||||
|
||||
if (skb->len < 24) {
|
||||
printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb "
|
||||
"(len=%d)\n", dev->name, skb->len);
|
||||
ret = 0;
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* FIX (?):
|
||||
* Wi-Fi 802.11b test plan suggests that AP should ignore power save
|
||||
* bit in authentication and (re)association frames and assume tha
|
||||
* STA remains awake for the response. */
|
||||
tx_ret = hostap_handle_sta_tx(local, &tx);
|
||||
skb = tx.skb;
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
fc = le16_to_cpu(hdr->frame_control);
|
||||
switch (tx_ret) {
|
||||
case AP_TX_CONTINUE:
|
||||
break;
|
||||
case AP_TX_CONTINUE_NOT_AUTHORIZED:
|
||||
if (local->ieee_802_1x &&
|
||||
WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
|
||||
meta->ethertype != ETH_P_PAE && !meta->wds) {
|
||||
printk(KERN_DEBUG "%s: dropped frame to unauthorized "
|
||||
"port (IEEE 802.1X): ethertype=0x%04x\n",
|
||||
dev->name, meta->ethertype);
|
||||
hostap_dump_tx_80211(dev->name, skb);
|
||||
|
||||
ret = 0; /* drop packet */
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case AP_TX_DROP:
|
||||
ret = 0; /* drop packet */
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
case AP_TX_RETRY:
|
||||
goto fail;
|
||||
case AP_TX_BUFFERED:
|
||||
/* do not free skb here, it will be freed when the
|
||||
* buffered frame is sent/timed out */
|
||||
ret = 0;
|
||||
goto tx_exit;
|
||||
}
|
||||
|
||||
/* Request TX callback if protocol version is 2 in 802.11 header;
|
||||
* this version 2 is a special case used between hostapd and kernel
|
||||
* driver */
|
||||
if (((fc & WLAN_FC_PVER) == BIT(1)) &&
|
||||
local->ap && local->ap->tx_callback_idx && meta->tx_cb_idx == 0) {
|
||||
meta->tx_cb_idx = local->ap->tx_callback_idx;
|
||||
|
||||
/* remove special version from the frame header */
|
||||
fc &= ~WLAN_FC_PVER;
|
||||
hdr->frame_control = cpu_to_le16(fc);
|
||||
}
|
||||
|
||||
if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_DATA) {
|
||||
no_encrypt = 1;
|
||||
tx.crypt = NULL;
|
||||
}
|
||||
|
||||
if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt &&
|
||||
!(fc & WLAN_FC_ISWEP)) {
|
||||
no_encrypt = 1;
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing "
|
||||
"unencrypted EAPOL frame\n", dev->name);
|
||||
tx.crypt = NULL; /* no encryption for IEEE 802.1X frames */
|
||||
}
|
||||
|
||||
if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu))
|
||||
tx.crypt = NULL;
|
||||
else if ((tx.crypt || local->crypt[local->tx_keyidx]) && !no_encrypt) {
|
||||
/* Add ISWEP flag both for firmware and host based encryption
|
||||
*/
|
||||
fc |= WLAN_FC_ISWEP;
|
||||
hdr->frame_control = cpu_to_le16(fc);
|
||||
} else if (local->drop_unencrypted &&
|
||||
WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
|
||||
meta->ethertype != ETH_P_PAE) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "%s: dropped unencrypted TX data "
|
||||
"frame (drop_unencrypted=1)\n", dev->name);
|
||||
}
|
||||
iface->stats.tx_dropped++;
|
||||
ret = 0;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (tx.crypt) {
|
||||
skb = hostap_tx_encrypt(skb, tx.crypt);
|
||||
if (skb == NULL) {
|
||||
printk(KERN_DEBUG "%s: TX - encryption failed\n",
|
||||
dev->name);
|
||||
ret = 0;
|
||||
goto fail;
|
||||
}
|
||||
meta = (struct hostap_skb_tx_data *) skb->cb;
|
||||
if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) {
|
||||
printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, "
|
||||
"expected 0x%08x) after hostap_tx_encrypt\n",
|
||||
dev->name, meta->magic,
|
||||
HOSTAP_SKB_TX_DATA_MAGIC);
|
||||
ret = 0;
|
||||
iface->stats.tx_dropped++;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (local->func->tx == NULL || local->func->tx(skb, dev)) {
|
||||
ret = 0;
|
||||
iface->stats.tx_dropped++;
|
||||
} else {
|
||||
ret = 0;
|
||||
iface->stats.tx_packets++;
|
||||
iface->stats.tx_bytes += skb->len;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (!ret && skb)
|
||||
dev_kfree_skb(skb);
|
||||
tx_exit:
|
||||
if (tx.sta_ptr)
|
||||
hostap_handle_sta_release(tx.sta_ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(hostap_dump_tx_80211);
|
||||
EXPORT_SYMBOL(hostap_tx_encrypt);
|
||||
EXPORT_SYMBOL(hostap_master_start_xmit);
|
3286
drivers/net/wireless/hostap/hostap_ap.c
Normal file
3286
drivers/net/wireless/hostap/hostap_ap.c
Normal file
File diff suppressed because it is too large
Load Diff
272
drivers/net/wireless/hostap/hostap_ap.h
Normal file
272
drivers/net/wireless/hostap/hostap_ap.h
Normal file
@ -0,0 +1,272 @@
|
||||
#ifndef HOSTAP_AP_H
|
||||
#define HOSTAP_AP_H
|
||||
|
||||
/* AP data structures for STAs */
|
||||
|
||||
/* maximum number of frames to buffer per STA */
|
||||
#define STA_MAX_TX_BUFFER 32
|
||||
|
||||
/* Flags used in skb->cb[6] to control how the packet is handled in TX path.
|
||||
* skb->cb[0..5] must contain magic value 'hostap' to indicate that cb[6] is
|
||||
* used. */
|
||||
#define AP_SKB_CB_MAGIC "hostap"
|
||||
#define AP_SKB_CB_MAGIC_LEN 6
|
||||
#define AP_SKB_CB_BUFFERED_FRAME BIT(0)
|
||||
#define AP_SKB_CB_ADD_MOREDATA BIT(1)
|
||||
|
||||
|
||||
/* STA flags */
|
||||
#define WLAN_STA_AUTH BIT(0)
|
||||
#define WLAN_STA_ASSOC BIT(1)
|
||||
#define WLAN_STA_PS BIT(2)
|
||||
#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */
|
||||
#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */
|
||||
#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is
|
||||
* controlling whether STA is authorized to
|
||||
* send and receive non-IEEE 802.1X frames
|
||||
*/
|
||||
#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
|
||||
|
||||
#define WLAN_RATE_1M BIT(0)
|
||||
#define WLAN_RATE_2M BIT(1)
|
||||
#define WLAN_RATE_5M5 BIT(2)
|
||||
#define WLAN_RATE_11M BIT(3)
|
||||
#define WLAN_RATE_COUNT 4
|
||||
|
||||
/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8,
|
||||
* but some pre-standard IEEE 802.11g products use longer elements. */
|
||||
#define WLAN_SUPP_RATES_MAX 32
|
||||
|
||||
/* Try to increase TX rate after # successfully sent consecutive packets */
|
||||
#define WLAN_RATE_UPDATE_COUNT 50
|
||||
|
||||
/* Decrease TX rate after # consecutive dropped packets */
|
||||
#define WLAN_RATE_DECREASE_THRESHOLD 2
|
||||
|
||||
struct sta_info {
|
||||
struct list_head list;
|
||||
struct sta_info *hnext; /* next entry in hash table list */
|
||||
atomic_t users; /* number of users (do not remove if > 0) */
|
||||
struct proc_dir_entry *proc;
|
||||
|
||||
u8 addr[6];
|
||||
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
|
||||
u32 flags;
|
||||
u16 capability;
|
||||
u16 listen_interval; /* or beacon_int for APs */
|
||||
u8 supported_rates[WLAN_SUPP_RATES_MAX];
|
||||
|
||||
unsigned long last_auth;
|
||||
unsigned long last_assoc;
|
||||
unsigned long last_rx;
|
||||
unsigned long last_tx;
|
||||
unsigned long rx_packets, tx_packets;
|
||||
unsigned long rx_bytes, tx_bytes;
|
||||
struct sk_buff_head tx_buf;
|
||||
/* FIX: timeout buffers with an expiry time somehow derived from
|
||||
* listen_interval */
|
||||
|
||||
s8 last_rx_silence; /* Noise in dBm */
|
||||
s8 last_rx_signal; /* Signal strength in dBm */
|
||||
u8 last_rx_rate; /* TX rate in 0.1 Mbps */
|
||||
u8 last_rx_updated; /* IWSPY's struct iw_quality::updated */
|
||||
|
||||
u8 tx_supp_rates; /* bit field of supported TX rates */
|
||||
u8 tx_rate; /* current TX rate (in 0.1 Mbps) */
|
||||
u8 tx_rate_idx; /* current TX rate (WLAN_RATE_*) */
|
||||
u8 tx_max_rate; /* max TX rate (WLAN_RATE_*) */
|
||||
u32 tx_count[WLAN_RATE_COUNT]; /* number of frames sent (per rate) */
|
||||
u32 rx_count[WLAN_RATE_COUNT]; /* number of frames received (per rate)
|
||||
*/
|
||||
u32 tx_since_last_failure;
|
||||
u32 tx_consecutive_exc;
|
||||
|
||||
struct prism2_crypt_data *crypt;
|
||||
|
||||
int ap; /* whether this station is an AP */
|
||||
|
||||
local_info_t *local;
|
||||
|
||||
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
|
||||
union {
|
||||
struct {
|
||||
char *challenge; /* shared key authentication
|
||||
* challenge */
|
||||
} sta;
|
||||
struct {
|
||||
int ssid_len;
|
||||
unsigned char ssid[MAX_SSID_LEN + 1]; /* AP's ssid */
|
||||
int channel;
|
||||
unsigned long last_beacon; /* last RX beacon time */
|
||||
} ap;
|
||||
} u;
|
||||
|
||||
struct timer_list timer;
|
||||
enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH } timeout_next;
|
||||
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
|
||||
};
|
||||
|
||||
|
||||
#define MAX_STA_COUNT 1024
|
||||
|
||||
/* Maximum number of AIDs to use for STAs; must be 2007 or lower
|
||||
* (8802.11 limitation) */
|
||||
#define MAX_AID_TABLE_SIZE 128
|
||||
|
||||
#define STA_HASH_SIZE 256
|
||||
#define STA_HASH(sta) (sta[5])
|
||||
|
||||
|
||||
/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY_SEC
|
||||
* has passed since last received frame from the station, a nullfunc data
|
||||
* frame is sent to the station. If this frame is not acknowledged and no other
|
||||
* frames have been received, the station will be disassociated after
|
||||
* AP_DISASSOC_DELAY. Similarily, a the station will be deauthenticated after
|
||||
* AP_DEAUTH_DELAY. AP_TIMEOUT_RESOLUTION is the resolution that is used with
|
||||
* max inactivity timer. */
|
||||
#define AP_MAX_INACTIVITY_SEC (5 * 60)
|
||||
#define AP_DISASSOC_DELAY (HZ)
|
||||
#define AP_DEAUTH_DELAY (HZ)
|
||||
|
||||
/* ap_policy: whether to accept frames to/from other APs/IBSS */
|
||||
typedef enum {
|
||||
AP_OTHER_AP_SKIP_ALL = 0,
|
||||
AP_OTHER_AP_SAME_SSID = 1,
|
||||
AP_OTHER_AP_ALL = 2,
|
||||
AP_OTHER_AP_EVEN_IBSS = 3
|
||||
} ap_policy_enum;
|
||||
|
||||
#define PRISM2_AUTH_OPEN BIT(0)
|
||||
#define PRISM2_AUTH_SHARED_KEY BIT(1)
|
||||
|
||||
|
||||
/* MAC address-based restrictions */
|
||||
struct mac_entry {
|
||||
struct list_head list;
|
||||
u8 addr[6];
|
||||
};
|
||||
|
||||
struct mac_restrictions {
|
||||
enum { MAC_POLICY_OPEN = 0, MAC_POLICY_ALLOW, MAC_POLICY_DENY } policy;
|
||||
unsigned int entries;
|
||||
struct list_head mac_list;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
|
||||
struct add_sta_proc_data {
|
||||
u8 addr[ETH_ALEN];
|
||||
struct add_sta_proc_data *next;
|
||||
};
|
||||
|
||||
|
||||
typedef enum { WDS_ADD, WDS_DEL } wds_oper_type;
|
||||
struct wds_oper_data {
|
||||
wds_oper_type type;
|
||||
u8 addr[ETH_ALEN];
|
||||
struct wds_oper_data *next;
|
||||
};
|
||||
|
||||
|
||||
struct ap_data {
|
||||
int initialized; /* whether ap_data has been initialized */
|
||||
local_info_t *local;
|
||||
int bridge_packets; /* send packet to associated STAs directly to the
|
||||
* wireless media instead of higher layers in the
|
||||
* kernel */
|
||||
unsigned int bridged_unicast; /* number of unicast frames bridged on
|
||||
* wireless media */
|
||||
unsigned int bridged_multicast; /* number of non-unicast frames
|
||||
* bridged on wireless media */
|
||||
unsigned int tx_drop_nonassoc; /* number of unicast TX packets dropped
|
||||
* because they were to an address that
|
||||
* was not associated */
|
||||
int nullfunc_ack; /* use workaround for nullfunc frame ACKs */
|
||||
|
||||
spinlock_t sta_table_lock;
|
||||
int num_sta; /* number of entries in sta_list */
|
||||
struct list_head sta_list; /* STA info list head */
|
||||
struct sta_info *sta_hash[STA_HASH_SIZE];
|
||||
|
||||
struct proc_dir_entry *proc;
|
||||
|
||||
ap_policy_enum ap_policy;
|
||||
unsigned int max_inactivity;
|
||||
int autom_ap_wds;
|
||||
|
||||
struct mac_restrictions mac_restrictions; /* MAC-based auth */
|
||||
int last_tx_rate;
|
||||
|
||||
struct work_struct add_sta_proc_queue;
|
||||
struct add_sta_proc_data *add_sta_proc_entries;
|
||||
|
||||
struct work_struct wds_oper_queue;
|
||||
struct wds_oper_data *wds_oper_entries;
|
||||
|
||||
u16 tx_callback_idx;
|
||||
|
||||
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
|
||||
/* pointers to STA info; based on allocated AID or NULL if AID free
|
||||
* AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1
|
||||
* and so on
|
||||
*/
|
||||
struct sta_info *sta_aid[MAX_AID_TABLE_SIZE];
|
||||
|
||||
u16 tx_callback_auth, tx_callback_assoc, tx_callback_poll;
|
||||
|
||||
/* WEP operations for generating challenges to be used with shared key
|
||||
* authentication */
|
||||
struct hostap_crypto_ops *crypt;
|
||||
void *crypt_priv;
|
||||
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
|
||||
};
|
||||
|
||||
|
||||
void hostap_rx(struct net_device *dev, struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
void hostap_init_data(local_info_t *local);
|
||||
void hostap_init_ap_proc(local_info_t *local);
|
||||
void hostap_free_data(struct ap_data *ap);
|
||||
void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver);
|
||||
|
||||
typedef enum {
|
||||
AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED,
|
||||
AP_TX_CONTINUE_NOT_AUTHORIZED
|
||||
} ap_tx_ret;
|
||||
struct hostap_tx_data {
|
||||
struct sk_buff *skb;
|
||||
int host_encrypt;
|
||||
struct prism2_crypt_data *crypt;
|
||||
void *sta_ptr;
|
||||
};
|
||||
ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx);
|
||||
void hostap_handle_sta_release(void *ptr);
|
||||
void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb);
|
||||
int hostap_update_sta_ps(local_info_t *local,
|
||||
struct hostap_ieee80211_hdr *hdr);
|
||||
typedef enum {
|
||||
AP_RX_CONTINUE, AP_RX_DROP, AP_RX_EXIT, AP_RX_CONTINUE_NOT_AUTHORIZED
|
||||
} ap_rx_ret;
|
||||
ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev,
|
||||
struct sk_buff *skb,
|
||||
struct hostap_80211_rx_status *rx_stats,
|
||||
int wds);
|
||||
int hostap_handle_sta_crypto(local_info_t *local,
|
||||
struct hostap_ieee80211_hdr *hdr,
|
||||
struct prism2_crypt_data **crypt, void **sta_ptr);
|
||||
int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr);
|
||||
int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr);
|
||||
int hostap_add_sta(struct ap_data *ap, u8 *sta_addr);
|
||||
int hostap_update_rx_stats(struct ap_data *ap,
|
||||
struct hostap_ieee80211_hdr *hdr,
|
||||
struct hostap_80211_rx_status *rx_stats);
|
||||
void hostap_update_rates(local_info_t *local);
|
||||
void hostap_add_wds_links(local_info_t *local);
|
||||
void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type);
|
||||
|
||||
#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT
|
||||
void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap,
|
||||
int resend);
|
||||
#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */
|
||||
|
||||
#endif /* HOSTAP_AP_H */
|
557
drivers/net/wireless/hostap/hostap_common.h
Normal file
557
drivers/net/wireless/hostap/hostap_common.h
Normal file
@ -0,0 +1,557 @@
|
||||
#ifndef HOSTAP_COMMON_H
|
||||
#define HOSTAP_COMMON_H
|
||||
|
||||
#define BIT(x) (1 << (x))
|
||||
|
||||
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
|
||||
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
|
||||
|
||||
#ifndef ETH_P_PAE
|
||||
#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
|
||||
#endif /* ETH_P_PAE */
|
||||
|
||||
#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
|
||||
|
||||
|
||||
|
||||
/* IEEE 802.11 defines */
|
||||
|
||||
#define WLAN_FC_PVER (BIT(1) | BIT(0))
|
||||
#define WLAN_FC_TODS BIT(8)
|
||||
#define WLAN_FC_FROMDS BIT(9)
|
||||
#define WLAN_FC_MOREFRAG BIT(10)
|
||||
#define WLAN_FC_RETRY BIT(11)
|
||||
#define WLAN_FC_PWRMGT BIT(12)
|
||||
#define WLAN_FC_MOREDATA BIT(13)
|
||||
#define WLAN_FC_ISWEP BIT(14)
|
||||
#define WLAN_FC_ORDER BIT(15)
|
||||
|
||||
#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2)
|
||||
#define WLAN_FC_GET_STYPE(fc) \
|
||||
(((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4)
|
||||
|
||||
#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
|
||||
#define WLAN_GET_SEQ_SEQ(seq) \
|
||||
(((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
|
||||
|
||||
#define WLAN_FC_TYPE_MGMT 0
|
||||
#define WLAN_FC_TYPE_CTRL 1
|
||||
#define WLAN_FC_TYPE_DATA 2
|
||||
|
||||
/* management */
|
||||
#define WLAN_FC_STYPE_ASSOC_REQ 0
|
||||
#define WLAN_FC_STYPE_ASSOC_RESP 1
|
||||
#define WLAN_FC_STYPE_REASSOC_REQ 2
|
||||
#define WLAN_FC_STYPE_REASSOC_RESP 3
|
||||
#define WLAN_FC_STYPE_PROBE_REQ 4
|
||||
#define WLAN_FC_STYPE_PROBE_RESP 5
|
||||
#define WLAN_FC_STYPE_BEACON 8
|
||||
#define WLAN_FC_STYPE_ATIM 9
|
||||
#define WLAN_FC_STYPE_DISASSOC 10
|
||||
#define WLAN_FC_STYPE_AUTH 11
|
||||
#define WLAN_FC_STYPE_DEAUTH 12
|
||||
|
||||
/* control */
|
||||
#define WLAN_FC_STYPE_PSPOLL 10
|
||||
#define WLAN_FC_STYPE_RTS 11
|
||||
#define WLAN_FC_STYPE_CTS 12
|
||||
#define WLAN_FC_STYPE_ACK 13
|
||||
#define WLAN_FC_STYPE_CFEND 14
|
||||
#define WLAN_FC_STYPE_CFENDACK 15
|
||||
|
||||
/* data */
|
||||
#define WLAN_FC_STYPE_DATA 0
|
||||
#define WLAN_FC_STYPE_DATA_CFACK 1
|
||||
#define WLAN_FC_STYPE_DATA_CFPOLL 2
|
||||
#define WLAN_FC_STYPE_DATA_CFACKPOLL 3
|
||||
#define WLAN_FC_STYPE_NULLFUNC 4
|
||||
#define WLAN_FC_STYPE_CFACK 5
|
||||
#define WLAN_FC_STYPE_CFPOLL 6
|
||||
#define WLAN_FC_STYPE_CFACKPOLL 7
|
||||
|
||||
/* Authentication algorithms */
|
||||
#define WLAN_AUTH_OPEN 0
|
||||
#define WLAN_AUTH_SHARED_KEY 1
|
||||
|
||||
#define WLAN_AUTH_CHALLENGE_LEN 128
|
||||
|
||||
#define WLAN_CAPABILITY_ESS BIT(0)
|
||||
#define WLAN_CAPABILITY_IBSS BIT(1)
|
||||
#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
|
||||
#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
|
||||
#define WLAN_CAPABILITY_PRIVACY BIT(4)
|
||||
|
||||
/* Status codes */
|
||||
#define WLAN_STATUS_SUCCESS 0
|
||||
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
|
||||
#define WLAN_STATUS_CAPS_UNSUPPORTED 10
|
||||
#define WLAN_STATUS_REASSOC_NO_ASSOC 11
|
||||
#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
|
||||
#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
|
||||
#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
|
||||
#define WLAN_STATUS_CHALLENGE_FAIL 15
|
||||
#define WLAN_STATUS_AUTH_TIMEOUT 16
|
||||
#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
|
||||
#define WLAN_STATUS_ASSOC_DENIED_RATES 18
|
||||
/* 802.11b */
|
||||
#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
|
||||
#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
|
||||
#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
|
||||
/* IEEE 802.11i */
|
||||
#define WLAN_STATUS_INVALID_IE 40
|
||||
#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
|
||||
#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
|
||||
#define WLAN_STATUS_AKMP_NOT_VALID 43
|
||||
#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44
|
||||
#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45
|
||||
#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46
|
||||
|
||||
/* Reason codes */
|
||||
#define WLAN_REASON_UNSPECIFIED 1
|
||||
#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
|
||||
#define WLAN_REASON_DEAUTH_LEAVING 3
|
||||
#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
|
||||
#define WLAN_REASON_DISASSOC_AP_BUSY 5
|
||||
#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
|
||||
#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
|
||||
#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
|
||||
#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
|
||||
/* IEEE 802.11i */
|
||||
#define WLAN_REASON_INVALID_IE 13
|
||||
#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
|
||||
#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
|
||||
#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16
|
||||
#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17
|
||||
#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18
|
||||
#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19
|
||||
#define WLAN_REASON_AKMP_NOT_VALID 20
|
||||
#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21
|
||||
#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
|
||||
#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
|
||||
#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
|
||||
|
||||
|
||||
/* Information Element IDs */
|
||||
#define WLAN_EID_SSID 0
|
||||
#define WLAN_EID_SUPP_RATES 1
|
||||
#define WLAN_EID_FH_PARAMS 2
|
||||
#define WLAN_EID_DS_PARAMS 3
|
||||
#define WLAN_EID_CF_PARAMS 4
|
||||
#define WLAN_EID_TIM 5
|
||||
#define WLAN_EID_IBSS_PARAMS 6
|
||||
#define WLAN_EID_CHALLENGE 16
|
||||
#define WLAN_EID_RSN 48
|
||||
#define WLAN_EID_GENERIC 221
|
||||
|
||||
|
||||
/* HFA384X Configuration RIDs */
|
||||
#define HFA384X_RID_CNFPORTTYPE 0xFC00
|
||||
#define HFA384X_RID_CNFOWNMACADDR 0xFC01
|
||||
#define HFA384X_RID_CNFDESIREDSSID 0xFC02
|
||||
#define HFA384X_RID_CNFOWNCHANNEL 0xFC03
|
||||
#define HFA384X_RID_CNFOWNSSID 0xFC04
|
||||
#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05
|
||||
#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06
|
||||
#define HFA384X_RID_CNFMAXDATALEN 0xFC07
|
||||
#define HFA384X_RID_CNFWDSADDRESS 0xFC08
|
||||
#define HFA384X_RID_CNFPMENABLED 0xFC09
|
||||
#define HFA384X_RID_CNFPMEPS 0xFC0A
|
||||
#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B
|
||||
#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C
|
||||
#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D
|
||||
#define HFA384X_RID_CNFOWNNAME 0xFC0E
|
||||
#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10
|
||||
#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */
|
||||
#define HFA384X_RID_UNKNOWN1 0xFC20
|
||||
#define HFA384X_RID_UNKNOWN2 0xFC21
|
||||
#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23
|
||||
#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24
|
||||
#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25
|
||||
#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26
|
||||
#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27
|
||||
#define HFA384X_RID_CNFWEPFLAGS 0xFC28
|
||||
#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29
|
||||
#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A
|
||||
#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */
|
||||
#define HFA384X_RID_CNFTXCONTROL 0xFC2C
|
||||
#define HFA384X_RID_CNFROAMINGMODE 0xFC2D
|
||||
#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */
|
||||
#define HFA384X_RID_CNFRCVCRCERROR 0xFC30
|
||||
#define HFA384X_RID_CNFMMLIFE 0xFC31
|
||||
#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32
|
||||
#define HFA384X_RID_CNFBEACONINT 0xFC33
|
||||
#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFSTAPCFINFO 0xFC35
|
||||
#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37
|
||||
#define HFA384X_RID_CNFTIMCTRL 0xFC40
|
||||
#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */
|
||||
#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */
|
||||
#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */
|
||||
#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0;
|
||||
* write only */
|
||||
#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */
|
||||
#define HFA384X_RID_GROUPADDRESSES 0xFC80
|
||||
#define HFA384X_RID_CREATEIBSS 0xFC81
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82
|
||||
#define HFA384X_RID_RTSTHRESHOLD 0xFC83
|
||||
#define HFA384X_RID_TXRATECONTROL 0xFC84
|
||||
#define HFA384X_RID_PROMISCUOUSMODE 0xFC85
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */
|
||||
#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */
|
||||
#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */
|
||||
#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */
|
||||
#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0
|
||||
#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1
|
||||
#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2
|
||||
#define HFA384X_RID_CNFBASICRATES 0xFCB3
|
||||
#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4
|
||||
#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */
|
||||
#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */
|
||||
#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */
|
||||
#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */
|
||||
#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */
|
||||
#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */
|
||||
#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */
|
||||
#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */
|
||||
#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */
|
||||
#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */
|
||||
#define HFA384X_RID_TICKTIME 0xFCE0
|
||||
#define HFA384X_RID_SCANREQUEST 0xFCE1
|
||||
#define HFA384X_RID_JOINREQUEST 0xFCE2
|
||||
#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */
|
||||
#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */
|
||||
#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */
|
||||
|
||||
/* HFA384X Information RIDs */
|
||||
#define HFA384X_RID_MAXLOADTIME 0xFD00
|
||||
#define HFA384X_RID_DOWNLOADBUFFER 0xFD01
|
||||
#define HFA384X_RID_PRIID 0xFD02
|
||||
#define HFA384X_RID_PRISUPRANGE 0xFD03
|
||||
#define HFA384X_RID_CFIACTRANGES 0xFD04
|
||||
#define HFA384X_RID_NICSERNUM 0xFD0A
|
||||
#define HFA384X_RID_NICID 0xFD0B
|
||||
#define HFA384X_RID_MFISUPRANGE 0xFD0C
|
||||
#define HFA384X_RID_CFISUPRANGE 0xFD0D
|
||||
#define HFA384X_RID_CHANNELLIST 0xFD10
|
||||
#define HFA384X_RID_REGULATORYDOMAINS 0xFD11
|
||||
#define HFA384X_RID_TEMPTYPE 0xFD12
|
||||
#define HFA384X_RID_CIS 0xFD13
|
||||
#define HFA384X_RID_STAID 0xFD20
|
||||
#define HFA384X_RID_STASUPRANGE 0xFD21
|
||||
#define HFA384X_RID_MFIACTRANGES 0xFD22
|
||||
#define HFA384X_RID_CFIACTRANGES2 0xFD23
|
||||
#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1;
|
||||
* only Prism2.5(?) */
|
||||
#define HFA384X_RID_PORTSTATUS 0xFD40
|
||||
#define HFA384X_RID_CURRENTSSID 0xFD41
|
||||
#define HFA384X_RID_CURRENTBSSID 0xFD42
|
||||
#define HFA384X_RID_COMMSQUALITY 0xFD43
|
||||
#define HFA384X_RID_CURRENTTXRATE 0xFD44
|
||||
#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45
|
||||
#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46
|
||||
#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47
|
||||
#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48
|
||||
#define HFA384X_RID_LONGRETRYLIMIT 0xFD49
|
||||
#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A
|
||||
#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B
|
||||
#define HFA384X_RID_CFPOLLABLE 0xFD4C
|
||||
#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D
|
||||
#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F
|
||||
#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */
|
||||
#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */
|
||||
#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */
|
||||
#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */
|
||||
#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */
|
||||
#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */
|
||||
#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */
|
||||
#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */
|
||||
#define HFA384X_RID_PHYTYPE 0xFDC0
|
||||
#define HFA384X_RID_CURRENTCHANNEL 0xFDC1
|
||||
#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2
|
||||
#define HFA384X_RID_CCAMODE 0xFDC3
|
||||
#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6
|
||||
#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */
|
||||
#define HFA384X_RID_BUILDSEQ 0xFFFE
|
||||
#define HFA384X_RID_FWID 0xFFFF
|
||||
|
||||
|
||||
struct hfa384x_comp_ident
|
||||
{
|
||||
u16 id;
|
||||
u16 variant;
|
||||
u16 major;
|
||||
u16 minor;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define HFA384X_COMP_ID_PRI 0x15
|
||||
#define HFA384X_COMP_ID_STA 0x1f
|
||||
#define HFA384X_COMP_ID_FW_AP 0x14b
|
||||
|
||||
struct hfa384x_sup_range
|
||||
{
|
||||
u16 role;
|
||||
u16 id;
|
||||
u16 variant;
|
||||
u16 bottom;
|
||||
u16 top;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
struct hfa384x_build_id
|
||||
{
|
||||
u16 pri_seq;
|
||||
u16 sec_seq;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* FD01 - Download Buffer */
|
||||
struct hfa384x_rid_download_buffer
|
||||
{
|
||||
u16 page;
|
||||
u16 offset;
|
||||
u16 length;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */
|
||||
struct hfa384x_comms_quality {
|
||||
u16 comm_qual; /* 0 .. 92 */
|
||||
u16 signal_level; /* 27 .. 154 */
|
||||
u16 noise_level; /* 27 .. 154 */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
|
||||
|
||||
/* New wireless extensions API - SET/GET convention (even ioctl numbers are
|
||||
* root only)
|
||||
*/
|
||||
#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
|
||||
#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
|
||||
#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
|
||||
#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
|
||||
#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
|
||||
#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
|
||||
#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
|
||||
#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
|
||||
#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
|
||||
#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
|
||||
#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
|
||||
#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
|
||||
#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
|
||||
#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
|
||||
|
||||
/* following are not in SIOCGIWPRIV list; check permission in the driver code
|
||||
*/
|
||||
#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
|
||||
#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
|
||||
|
||||
|
||||
/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
|
||||
enum {
|
||||
/* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
|
||||
PRISM2_PARAM_TXRATECTRL = 2,
|
||||
PRISM2_PARAM_BEACON_INT = 3,
|
||||
PRISM2_PARAM_PSEUDO_IBSS = 4,
|
||||
PRISM2_PARAM_ALC = 5,
|
||||
/* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
|
||||
PRISM2_PARAM_DUMP = 7,
|
||||
PRISM2_PARAM_OTHER_AP_POLICY = 8,
|
||||
PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
|
||||
PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
|
||||
PRISM2_PARAM_DTIM_PERIOD = 11,
|
||||
PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
|
||||
PRISM2_PARAM_MAX_WDS = 13,
|
||||
PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
|
||||
PRISM2_PARAM_AP_AUTH_ALGS = 15,
|
||||
PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
|
||||
PRISM2_PARAM_HOST_ENCRYPT = 17,
|
||||
PRISM2_PARAM_HOST_DECRYPT = 18,
|
||||
PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19,
|
||||
PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20,
|
||||
PRISM2_PARAM_HOST_ROAMING = 21,
|
||||
PRISM2_PARAM_BCRX_STA_KEY = 22,
|
||||
PRISM2_PARAM_IEEE_802_1X = 23,
|
||||
PRISM2_PARAM_ANTSEL_TX = 24,
|
||||
PRISM2_PARAM_ANTSEL_RX = 25,
|
||||
PRISM2_PARAM_MONITOR_TYPE = 26,
|
||||
PRISM2_PARAM_WDS_TYPE = 27,
|
||||
PRISM2_PARAM_HOSTSCAN = 28,
|
||||
PRISM2_PARAM_AP_SCAN = 29,
|
||||
PRISM2_PARAM_ENH_SEC = 30,
|
||||
PRISM2_PARAM_IO_DEBUG = 31,
|
||||
PRISM2_PARAM_BASIC_RATES = 32,
|
||||
PRISM2_PARAM_OPER_RATES = 33,
|
||||
PRISM2_PARAM_HOSTAPD = 34,
|
||||
PRISM2_PARAM_HOSTAPD_STA = 35,
|
||||
PRISM2_PARAM_WPA = 36,
|
||||
PRISM2_PARAM_PRIVACY_INVOKED = 37,
|
||||
PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
|
||||
PRISM2_PARAM_DROP_UNENCRYPTED = 39,
|
||||
};
|
||||
|
||||
enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
|
||||
HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
|
||||
|
||||
|
||||
/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
|
||||
enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
|
||||
AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
|
||||
AP_MAC_CMD_KICKALL = 4 };
|
||||
|
||||
|
||||
/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
|
||||
enum {
|
||||
PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
|
||||
/* Note! Old versions of prism2_srec have a fatal error in CRC-16
|
||||
* calculation, which will corrupt all non-volatile downloads.
|
||||
* PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
|
||||
* prevent use of old versions of prism2_srec for non-volatile
|
||||
* download. */
|
||||
PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
|
||||
PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
|
||||
/* Persistent versions of volatile download commands (keep firmware
|
||||
* data in memory and automatically re-download after hw_reset */
|
||||
PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
|
||||
PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
|
||||
};
|
||||
|
||||
struct prism2_download_param {
|
||||
u32 dl_cmd;
|
||||
u32 start_addr;
|
||||
u32 num_areas;
|
||||
struct prism2_download_area {
|
||||
u32 addr; /* wlan card address */
|
||||
u32 len;
|
||||
void __user *ptr; /* pointer to data in user space */
|
||||
} data[0];
|
||||
};
|
||||
|
||||
#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
|
||||
#define PRISM2_MAX_DOWNLOAD_LEN 262144
|
||||
|
||||
|
||||
/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
|
||||
enum {
|
||||
PRISM2_HOSTAPD_FLUSH = 1,
|
||||
PRISM2_HOSTAPD_ADD_STA = 2,
|
||||
PRISM2_HOSTAPD_REMOVE_STA = 3,
|
||||
PRISM2_HOSTAPD_GET_INFO_STA = 4,
|
||||
/* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
|
||||
PRISM2_SET_ENCRYPTION = 6,
|
||||
PRISM2_GET_ENCRYPTION = 7,
|
||||
PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
|
||||
PRISM2_HOSTAPD_GET_RID = 9,
|
||||
PRISM2_HOSTAPD_SET_RID = 10,
|
||||
PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
|
||||
PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
|
||||
PRISM2_HOSTAPD_MLME = 13,
|
||||
PRISM2_HOSTAPD_SCAN_REQ = 14,
|
||||
PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
|
||||
};
|
||||
|
||||
#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
|
||||
#define PRISM2_HOSTAPD_RID_HDR_LEN \
|
||||
((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
|
||||
#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
|
||||
((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
|
||||
|
||||
/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
|
||||
*/
|
||||
#define HOSTAP_CRYPT_ALG_NAME_LEN 16
|
||||
|
||||
|
||||
struct prism2_hostapd_param {
|
||||
u32 cmd;
|
||||
u8 sta_addr[ETH_ALEN];
|
||||
union {
|
||||
struct {
|
||||
u16 aid;
|
||||
u16 capability;
|
||||
u8 tx_supp_rates;
|
||||
} add_sta;
|
||||
struct {
|
||||
u32 inactive_sec;
|
||||
} get_info_sta;
|
||||
struct {
|
||||
u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
|
||||
u32 flags;
|
||||
u32 err;
|
||||
u8 idx;
|
||||
u8 seq[8]; /* sequence counter (set: RX, get: TX) */
|
||||
u16 key_len;
|
||||
u8 key[0];
|
||||
} crypt;
|
||||
struct {
|
||||
u32 flags_and;
|
||||
u32 flags_or;
|
||||
} set_flags_sta;
|
||||
struct {
|
||||
u16 rid;
|
||||
u16 len;
|
||||
u8 data[0];
|
||||
} rid;
|
||||
struct {
|
||||
u8 len;
|
||||
u8 data[0];
|
||||
} generic_elem;
|
||||
struct {
|
||||
#define MLME_STA_DEAUTH 0
|
||||
#define MLME_STA_DISASSOC 1
|
||||
u16 cmd;
|
||||
u16 reason_code;
|
||||
} mlme;
|
||||
struct {
|
||||
u8 ssid_len;
|
||||
u8 ssid[32];
|
||||
} scan_req;
|
||||
} u;
|
||||
};
|
||||
|
||||
#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
|
||||
#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
|
||||
|
||||
#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
|
||||
#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
|
||||
#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
|
||||
#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
|
||||
#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
|
||||
#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
|
||||
|
||||
|
||||
#endif /* HOSTAP_COMMON_H */
|
86
drivers/net/wireless/hostap/hostap_config.h
Normal file
86
drivers/net/wireless/hostap/hostap_config.h
Normal file
@ -0,0 +1,86 @@
|
||||
#ifndef HOSTAP_CONFIG_H
|
||||
#define HOSTAP_CONFIG_H
|
||||
|
||||
#define PRISM2_VERSION "CVS"
|
||||
|
||||
/* In the previous versions of Host AP driver, support for user space version
|
||||
* of IEEE 802.11 management (hostapd) used to be disabled in the default
|
||||
* configuration. From now on, support for hostapd is always included and it is
|
||||
* possible to disable kernel driver version of IEEE 802.11 management with a
|
||||
* separate define, PRISM2_NO_KERNEL_IEEE80211_MGMT. */
|
||||
/* #define PRISM2_NO_KERNEL_IEEE80211_MGMT */
|
||||
|
||||
/* Maximum number of events handler per one interrupt */
|
||||
#define PRISM2_MAX_INTERRUPT_EVENTS 20
|
||||
|
||||
/* Use PCI bus master to copy data to/from BAP (only available for
|
||||
* hostap_pci.o).
|
||||
*
|
||||
* Note! This is extremely experimental. PCI bus master is not supported by
|
||||
* Intersil and it seems to have some problems at least on TX path (see below).
|
||||
* The driver code for implementing bus master support is based on guessing
|
||||
* and experimenting suitable control bits and these might not be correct.
|
||||
* This code is included because using bus master makes a huge difference in
|
||||
* host CPU load (something like 40% host CPU usage to 5-10% when sending or
|
||||
* receiving at maximum throughput).
|
||||
*
|
||||
* Note2! Station firmware version 1.3.5 and primary firmware version 1.0.7
|
||||
* have some fixes for PCI corruption and these (or newer) versions are
|
||||
* recommended especially when using bus mastering.
|
||||
*
|
||||
* NOTE: PCI bus mastering code has not been updated for long time and it is
|
||||
* not likely to compile and it will _not_ work as is. Only enable this if you
|
||||
* are prepared to first fix the implementation..
|
||||
*/
|
||||
/* #define PRISM2_BUS_MASTER */
|
||||
|
||||
#ifdef PRISM2_BUS_MASTER
|
||||
|
||||
/* PCI bus master implementation seems to be broken in current
|
||||
* hardware/firmware versions. Enable this to use enable command to fix
|
||||
* something before starting bus master operation on TX path. This will add
|
||||
* some latency and an extra interrupt to each TX packet. */
|
||||
#define PRISM2_ENABLE_BEFORE_TX_BUS_MASTER
|
||||
|
||||
#endif /* PRISM2_BUS_MASTER */
|
||||
|
||||
/* Include code for downloading firmware images into volatile RAM. */
|
||||
#define PRISM2_DOWNLOAD_SUPPORT
|
||||
|
||||
/* Allow kernel configuration to enable download support. */
|
||||
#if !defined(PRISM2_DOWNLOAD_SUPPORT) && defined(CONFIG_HOSTAP_FIRMWARE)
|
||||
#define PRISM2_DOWNLOAD_SUPPORT
|
||||
#endif
|
||||
|
||||
#ifdef PRISM2_DOWNLOAD_SUPPORT
|
||||
/* Allow writing firmware images into flash, i.e., to non-volatile storage.
|
||||
* Before you enable this option, you should make absolutely sure that you are
|
||||
* using prism2_srec utility that comes with THIS version of the driver!
|
||||
* In addition, please note that it is possible to kill your card with
|
||||
* non-volatile download if you are using incorrect image. This feature has not
|
||||
* been fully tested, so please be careful with it. */
|
||||
/* #define PRISM2_NON_VOLATILE_DOWNLOAD */
|
||||
#endif /* PRISM2_DOWNLOAD_SUPPORT */
|
||||
|
||||
/* Save low-level I/O for debugging. This should not be enabled in normal use.
|
||||
*/
|
||||
/* #define PRISM2_IO_DEBUG */
|
||||
|
||||
/* Following defines can be used to remove unneeded parts of the driver, e.g.,
|
||||
* to limit the size of the kernel module. Definitions can be added here in
|
||||
* hostap_config.h or they can be added to make command with EXTRA_CFLAGS,
|
||||
* e.g.,
|
||||
* 'make pccard EXTRA_CFLAGS="-DPRISM2_NO_DEBUG -DPRISM2_NO_PROCFS_DEBUG"'
|
||||
*/
|
||||
|
||||
/* Do not include debug messages into the driver */
|
||||
/* #define PRISM2_NO_DEBUG */
|
||||
|
||||
/* Do not include /proc/net/prism2/wlan#/{registers,debug} */
|
||||
/* #define PRISM2_NO_PROCFS_DEBUG */
|
||||
|
||||
/* Do not include station functionality (i.e., allow only Master (Host AP) mode
|
||||
*/
|
||||
/* #define PRISM2_NO_STATION_MODES */
|
||||
|
||||
#endif /* HOSTAP_CONFIG_H */
|
167
drivers/net/wireless/hostap/hostap_crypt.c
Normal file
167
drivers/net/wireless/hostap/hostap_crypt.c
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Host AP crypto routines
|
||||
*
|
||||
* Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
struct hostap_crypto_alg {
|
||||
struct list_head list;
|
||||
struct hostap_crypto_ops *ops;
|
||||
};
|
||||
|
||||
|
||||
struct hostap_crypto {
|
||||
struct list_head algs;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct hostap_crypto *hcrypt;
|
||||
|
||||
|
||||
int hostap_register_crypto_ops(struct hostap_crypto_ops *ops)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct hostap_crypto_alg *alg;
|
||||
|
||||
if (hcrypt == NULL)
|
||||
return -1;
|
||||
|
||||
alg = (struct hostap_crypto_alg *) kmalloc(sizeof(*alg), GFP_KERNEL);
|
||||
if (alg == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(alg, 0, sizeof(*alg));
|
||||
alg->ops = ops;
|
||||
|
||||
spin_lock_irqsave(&hcrypt->lock, flags);
|
||||
list_add(&alg->list, &hcrypt->algs);
|
||||
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
||||
|
||||
printk(KERN_DEBUG "hostap_crypt: registered algorithm '%s'\n",
|
||||
ops->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int hostap_unregister_crypto_ops(struct hostap_crypto_ops *ops)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *ptr;
|
||||
struct hostap_crypto_alg *del_alg = NULL;
|
||||
|
||||
if (hcrypt == NULL)
|
||||
return -1;
|
||||
|
||||
spin_lock_irqsave(&hcrypt->lock, flags);
|
||||
for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
|
||||
struct hostap_crypto_alg *alg =
|
||||
(struct hostap_crypto_alg *) ptr;
|
||||
if (alg->ops == ops) {
|
||||
list_del(&alg->list);
|
||||
del_alg = alg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
||||
|
||||
if (del_alg) {
|
||||
printk(KERN_DEBUG "hostap_crypt: unregistered algorithm "
|
||||
"'%s'\n", ops->name);
|
||||
kfree(del_alg);
|
||||
}
|
||||
|
||||
return del_alg ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
struct hostap_crypto_ops * hostap_get_crypto_ops(const char *name)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct list_head *ptr;
|
||||
struct hostap_crypto_alg *found_alg = NULL;
|
||||
|
||||
if (hcrypt == NULL)
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&hcrypt->lock, flags);
|
||||
for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
|
||||
struct hostap_crypto_alg *alg =
|
||||
(struct hostap_crypto_alg *) ptr;
|
||||
if (strcmp(alg->ops->name, name) == 0) {
|
||||
found_alg = alg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
||||
|
||||
if (found_alg)
|
||||
return found_alg->ops;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void * hostap_crypt_null_init(int keyidx) { return (void *) 1; }
|
||||
static void hostap_crypt_null_deinit(void *priv) {}
|
||||
|
||||
static struct hostap_crypto_ops hostap_crypt_null = {
|
||||
.name = "NULL",
|
||||
.init = hostap_crypt_null_init,
|
||||
.deinit = hostap_crypt_null_deinit,
|
||||
.encrypt_mpdu = NULL,
|
||||
.decrypt_mpdu = NULL,
|
||||
.encrypt_msdu = NULL,
|
||||
.decrypt_msdu = NULL,
|
||||
.set_key = NULL,
|
||||
.get_key = NULL,
|
||||
.extra_prefix_len = 0,
|
||||
.extra_postfix_len = 0
|
||||
};
|
||||
|
||||
|
||||
static int __init hostap_crypto_init(void)
|
||||
{
|
||||
hcrypt = (struct hostap_crypto *) kmalloc(sizeof(*hcrypt), GFP_KERNEL);
|
||||
if (hcrypt == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(hcrypt, 0, sizeof(*hcrypt));
|
||||
INIT_LIST_HEAD(&hcrypt->algs);
|
||||
spin_lock_init(&hcrypt->lock);
|
||||
|
||||
(void) hostap_register_crypto_ops(&hostap_crypt_null);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void __exit hostap_crypto_deinit(void)
|
||||
{
|
||||
struct list_head *ptr, *n;
|
||||
|
||||
if (hcrypt == NULL)
|
||||
return;
|
||||
|
||||
for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs;
|
||||
ptr = n, n = ptr->next) {
|
||||
struct hostap_crypto_alg *alg =
|
||||
(struct hostap_crypto_alg *) ptr;
|
||||
list_del(ptr);
|
||||
printk(KERN_DEBUG "hostap_crypt: unregistered algorithm "
|
||||
"'%s' (deinit)\n", alg->ops->name);
|
||||
kfree(alg);
|
||||
}
|
||||
|
||||
kfree(hcrypt);
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(hostap_register_crypto_ops);
|
||||
EXPORT_SYMBOL(hostap_unregister_crypto_ops);
|
||||
EXPORT_SYMBOL(hostap_get_crypto_ops);
|
50
drivers/net/wireless/hostap/hostap_crypt.h
Normal file
50
drivers/net/wireless/hostap/hostap_crypt.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef PRISM2_CRYPT_H
|
||||
#define PRISM2_CRYPT_H
|
||||
|
||||
struct hostap_crypto_ops {
|
||||
char *name;
|
||||
|
||||
/* init new crypto context (e.g., allocate private data space,
|
||||
* select IV, etc.); returns NULL on failure or pointer to allocated
|
||||
* private data on success */
|
||||
void * (*init)(int keyidx);
|
||||
|
||||
/* deinitialize crypto context and free allocated private data */
|
||||
void (*deinit)(void *priv);
|
||||
|
||||
/* encrypt/decrypt return < 0 on error or >= 0 on success. The return
|
||||
* value from decrypt_mpdu is passed as the keyidx value for
|
||||
* decrypt_msdu. skb must have enough head and tail room for the
|
||||
* encryption; if not, error will be returned; these functions are
|
||||
* called for all MPDUs (i.e., fragments).
|
||||
*/
|
||||
int (*encrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv);
|
||||
int (*decrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv);
|
||||
|
||||
/* These functions are called for full MSDUs, i.e. full frames.
|
||||
* These can be NULL if full MSDU operations are not needed. */
|
||||
int (*encrypt_msdu)(struct sk_buff *skb, int hdr_len, void *priv);
|
||||
int (*decrypt_msdu)(struct sk_buff *skb, int keyidx, int hdr_len,
|
||||
void *priv);
|
||||
|
||||
int (*set_key)(void *key, int len, u8 *seq, void *priv);
|
||||
int (*get_key)(void *key, int len, u8 *seq, void *priv);
|
||||
|
||||
/* procfs handler for printing out key information and possible
|
||||
* statistics */
|
||||
char * (*print_stats)(char *p, void *priv);
|
||||
|
||||
/* maximum number of bytes added by encryption; encrypt buf is
|
||||
* allocated with extra_prefix_len bytes, copy of in_buf, and
|
||||
* extra_postfix_len; encrypt need not use all this space, but
|
||||
* the result must start at the beginning of the buffer and correct
|
||||
* length must be returned */
|
||||
int extra_prefix_len, extra_postfix_len;
|
||||
};
|
||||
|
||||
|
||||
int hostap_register_crypto_ops(struct hostap_crypto_ops *ops);
|
||||
int hostap_unregister_crypto_ops(struct hostap_crypto_ops *ops);
|
||||
struct hostap_crypto_ops * hostap_get_crypto_ops(const char *name);
|
||||
|
||||
#endif /* PRISM2_CRYPT_H */
|
486
drivers/net/wireless/hostap/hostap_crypt_ccmp.c
Normal file
486
drivers/net/wireless/hostap/hostap_crypt_ccmp.c
Normal file
@ -0,0 +1,486 @@
|
||||
/*
|
||||
* Host AP crypt: host-based CCMP encryption implementation for Host AP driver
|
||||
*
|
||||
* Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/iw_handler.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#include "hostap_crypt.h"
|
||||
#include "hostap_wlan.h"
|
||||
#include "hostap_80211.h"
|
||||
|
||||
#ifndef CONFIG_CRYPTO
|
||||
#error CONFIG_CRYPTO is required to build this module.
|
||||
#endif
|
||||
#include <linux/crypto.h>
|
||||
#include <asm/scatterlist.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Host AP crypt: CCMP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
#define AES_BLOCK_LEN 16
|
||||
#define CCMP_HDR_LEN 8
|
||||
#define CCMP_MIC_LEN 8
|
||||
#define CCMP_TK_LEN 16
|
||||
#define CCMP_PN_LEN 6
|
||||
|
||||
|
||||
struct hostap_ccmp_data {
|
||||
u8 key[CCMP_TK_LEN];
|
||||
int key_set;
|
||||
|
||||
u8 tx_pn[CCMP_PN_LEN];
|
||||
u8 rx_pn[CCMP_PN_LEN];
|
||||
|
||||
u32 dot11RSNAStatsCCMPFormatErrors;
|
||||
u32 dot11RSNAStatsCCMPReplays;
|
||||
u32 dot11RSNAStatsCCMPDecryptErrors;
|
||||
|
||||
int key_idx;
|
||||
|
||||
struct crypto_tfm *tfm;
|
||||
|
||||
/* scratch buffers for virt_to_page() (crypto API) */
|
||||
u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
|
||||
tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
|
||||
u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
|
||||
};
|
||||
|
||||
|
||||
void hostap_ccmp_aes_encrypt(struct crypto_tfm *tfm,
|
||||
const u8 pt[16], u8 ct[16])
|
||||
{
|
||||
struct scatterlist src, dst;
|
||||
|
||||
src.page = virt_to_page(pt);
|
||||
src.offset = offset_in_page(pt);
|
||||
src.length = AES_BLOCK_LEN;
|
||||
|
||||
dst.page = virt_to_page(ct);
|
||||
dst.offset = offset_in_page(ct);
|
||||
dst.length = AES_BLOCK_LEN;
|
||||
|
||||
crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN);
|
||||
}
|
||||
|
||||
|
||||
static void * hostap_ccmp_init(int key_idx)
|
||||
{
|
||||
struct hostap_ccmp_data *priv;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return NULL;
|
||||
|
||||
priv = (struct hostap_ccmp_data *) kmalloc(sizeof(*priv), GFP_ATOMIC);
|
||||
if (priv == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
priv->key_idx = key_idx;
|
||||
|
||||
priv->tfm = crypto_alloc_tfm("aes", 0);
|
||||
if (priv->tfm == NULL) {
|
||||
printk(KERN_DEBUG "hostap_crypt_ccmp: could not allocate "
|
||||
"crypto API aes\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return priv;
|
||||
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tfm)
|
||||
crypto_free_tfm(priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
module_put(THIS_MODULE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void hostap_ccmp_deinit(void *priv)
|
||||
{
|
||||
struct hostap_ccmp_data *_priv = priv;
|
||||
if (_priv && _priv->tfm)
|
||||
crypto_free_tfm(_priv->tfm);
|
||||
kfree(priv);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
|
||||
static inline void xor_block(u8 *b, u8 *a, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++)
|
||||
b[i] ^= a[i];
|
||||
}
|
||||
|
||||
|
||||
static void ccmp_init_blocks(struct crypto_tfm *tfm,
|
||||
struct hostap_ieee80211_hdr *hdr,
|
||||
u8 *pn, size_t dlen, u8 *b0, u8 *auth,
|
||||
u8 *s0)
|
||||
{
|
||||
u8 *pos, qc = 0;
|
||||
size_t aad_len;
|
||||
u16 fc;
|
||||
int a4_included, qc_included;
|
||||
u8 aad[2 * AES_BLOCK_LEN];
|
||||
|
||||
fc = le16_to_cpu(hdr->frame_control);
|
||||
a4_included = ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
|
||||
(WLAN_FC_TODS | WLAN_FC_FROMDS));
|
||||
qc_included = ((WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) &&
|
||||
(WLAN_FC_GET_STYPE(fc) & 0x08));
|
||||
aad_len = 22;
|
||||
if (a4_included)
|
||||
aad_len += 6;
|
||||
if (qc_included) {
|
||||
pos = (u8 *) &hdr->addr4;
|
||||
if (a4_included)
|
||||
pos += 6;
|
||||
qc = *pos & 0x0f;
|
||||
aad_len += 2;
|
||||
}
|
||||
|
||||
/* CCM Initial Block:
|
||||
* Flag (Include authentication header, M=3 (8-octet MIC),
|
||||
* L=1 (2-octet Dlen))
|
||||
* Nonce: 0x00 | A2 | PN
|
||||
* Dlen */
|
||||
b0[0] = 0x59;
|
||||
b0[1] = qc;
|
||||
memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
|
||||
memcpy(b0 + 8, pn, CCMP_PN_LEN);
|
||||
b0[14] = (dlen >> 8) & 0xff;
|
||||
b0[15] = dlen & 0xff;
|
||||
|
||||
/* AAD:
|
||||
* FC with bits 4..6 and 11..13 masked to zero; 14 is always one
|
||||
* A1 | A2 | A3
|
||||
* SC with bits 4..15 (seq#) masked to zero
|
||||
* A4 (if present)
|
||||
* QC (if present)
|
||||
*/
|
||||
pos = (u8 *) hdr;
|
||||
aad[0] = 0; /* aad_len >> 8 */
|
||||
aad[1] = aad_len & 0xff;
|
||||
aad[2] = pos[0] & 0x8f;
|
||||
aad[3] = pos[1] & 0xc7;
|
||||
memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
|
||||
pos = (u8 *) &hdr->seq_ctrl;
|
||||
aad[22] = pos[0] & 0x0f;
|
||||
aad[23] = 0; /* all bits masked */
|
||||
memset(aad + 24, 0, 8);
|
||||
if (a4_included)
|
||||
memcpy(aad + 24, hdr->addr4, ETH_ALEN);
|
||||
if (qc_included) {
|
||||
aad[a4_included ? 30 : 24] = qc;
|
||||
/* rest of QC masked */
|
||||
}
|
||||
|
||||
/* Start with the first block and AAD */
|
||||
hostap_ccmp_aes_encrypt(tfm, b0, auth);
|
||||
xor_block(auth, aad, AES_BLOCK_LEN);
|
||||
hostap_ccmp_aes_encrypt(tfm, auth, auth);
|
||||
xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
|
||||
hostap_ccmp_aes_encrypt(tfm, auth, auth);
|
||||
b0[0] &= 0x07;
|
||||
b0[14] = b0[15] = 0;
|
||||
hostap_ccmp_aes_encrypt(tfm, b0, s0);
|
||||
}
|
||||
|
||||
|
||||
static int hostap_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct hostap_ccmp_data *key = priv;
|
||||
int data_len, i, blocks, last, len;
|
||||
u8 *pos, *mic;
|
||||
struct hostap_ieee80211_hdr *hdr;
|
||||
u8 *b0 = key->tx_b0;
|
||||
u8 *b = key->tx_b;
|
||||
u8 *e = key->tx_e;
|
||||
u8 *s0 = key->tx_s0;
|
||||
|
||||
if (skb_headroom(skb) < CCMP_HDR_LEN ||
|
||||
skb_tailroom(skb) < CCMP_MIC_LEN ||
|
||||
skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
data_len = skb->len - hdr_len;
|
||||
pos = skb_push(skb, CCMP_HDR_LEN);
|
||||
memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
|
||||
pos += hdr_len;
|
||||
mic = skb_put(skb, CCMP_MIC_LEN);
|
||||
|
||||
i = CCMP_PN_LEN - 1;
|
||||
while (i >= 0) {
|
||||
key->tx_pn[i]++;
|
||||
if (key->tx_pn[i] != 0)
|
||||
break;
|
||||
i--;
|
||||
}
|
||||
|
||||
*pos++ = key->tx_pn[5];
|
||||
*pos++ = key->tx_pn[4];
|
||||
*pos++ = 0;
|
||||
*pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */;
|
||||
*pos++ = key->tx_pn[3];
|
||||
*pos++ = key->tx_pn[2];
|
||||
*pos++ = key->tx_pn[1];
|
||||
*pos++ = key->tx_pn[0];
|
||||
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
|
||||
|
||||
blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
|
||||
last = data_len % AES_BLOCK_LEN;
|
||||
|
||||
for (i = 1; i <= blocks; i++) {
|
||||
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
|
||||
/* Authentication */
|
||||
xor_block(b, pos, len);
|
||||
hostap_ccmp_aes_encrypt(key->tfm, b, b);
|
||||
/* Encryption, with counter */
|
||||
b0[14] = (i >> 8) & 0xff;
|
||||
b0[15] = i & 0xff;
|
||||
hostap_ccmp_aes_encrypt(key->tfm, b0, e);
|
||||
xor_block(pos, e, len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
for (i = 0; i < CCMP_MIC_LEN; i++)
|
||||
mic[i] = b[i] ^ s0[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostap_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct hostap_ccmp_data *key = priv;
|
||||
u8 keyidx, *pos;
|
||||
struct hostap_ieee80211_hdr *hdr;
|
||||
u8 *b0 = key->rx_b0;
|
||||
u8 *b = key->rx_b;
|
||||
u8 *a = key->rx_a;
|
||||
u8 pn[6];
|
||||
int i, blocks, last, len;
|
||||
size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
|
||||
u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
|
||||
|
||||
if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
|
||||
key->dot11RSNAStatsCCMPFormatErrors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
pos = skb->data + hdr_len;
|
||||
keyidx = pos[3];
|
||||
if (!(keyidx & (1 << 5))) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "CCMP: received packet without ExtIV"
|
||||
" flag from " MACSTR "\n", MAC2STR(hdr->addr2));
|
||||
}
|
||||
key->dot11RSNAStatsCCMPFormatErrors++;
|
||||
return -2;
|
||||
}
|
||||
keyidx >>= 6;
|
||||
if (key->key_idx != keyidx) {
|
||||
printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
|
||||
"keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
|
||||
return -6;
|
||||
}
|
||||
if (!key->key_set) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "CCMP: received packet from " MACSTR
|
||||
" with keyid=%d that does not have a configured"
|
||||
" key\n", MAC2STR(hdr->addr2), keyidx);
|
||||
}
|
||||
return -3;
|
||||
}
|
||||
|
||||
pn[0] = pos[7];
|
||||
pn[1] = pos[6];
|
||||
pn[2] = pos[5];
|
||||
pn[3] = pos[4];
|
||||
pn[4] = pos[1];
|
||||
pn[5] = pos[0];
|
||||
pos += 8;
|
||||
|
||||
if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "CCMP: replay detected: STA=" MACSTR
|
||||
" previous PN %02x%02x%02x%02x%02x%02x "
|
||||
"received PN %02x%02x%02x%02x%02x%02x\n",
|
||||
MAC2STR(hdr->addr2), MAC2STR(key->rx_pn),
|
||||
MAC2STR(pn));
|
||||
}
|
||||
key->dot11RSNAStatsCCMPReplays++;
|
||||
return -4;
|
||||
}
|
||||
|
||||
ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
|
||||
xor_block(mic, b, CCMP_MIC_LEN);
|
||||
|
||||
blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
|
||||
last = data_len % AES_BLOCK_LEN;
|
||||
|
||||
for (i = 1; i <= blocks; i++) {
|
||||
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
|
||||
/* Decrypt, with counter */
|
||||
b0[14] = (i >> 8) & 0xff;
|
||||
b0[15] = i & 0xff;
|
||||
hostap_ccmp_aes_encrypt(key->tfm, b0, b);
|
||||
xor_block(pos, b, len);
|
||||
/* Authentication */
|
||||
xor_block(a, pos, len);
|
||||
hostap_ccmp_aes_encrypt(key->tfm, a, a);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "CCMP: decrypt failed: STA="
|
||||
MACSTR "\n", MAC2STR(hdr->addr2));
|
||||
}
|
||||
key->dot11RSNAStatsCCMPDecryptErrors++;
|
||||
return -5;
|
||||
}
|
||||
|
||||
memcpy(key->rx_pn, pn, CCMP_PN_LEN);
|
||||
|
||||
/* Remove hdr and MIC */
|
||||
memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
|
||||
skb_pull(skb, CCMP_HDR_LEN);
|
||||
skb_trim(skb, skb->len - CCMP_MIC_LEN);
|
||||
|
||||
return keyidx;
|
||||
}
|
||||
|
||||
|
||||
static int hostap_ccmp_set_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct hostap_ccmp_data *data = priv;
|
||||
int keyidx;
|
||||
struct crypto_tfm *tfm = data->tfm;
|
||||
|
||||
keyidx = data->key_idx;
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->key_idx = keyidx;
|
||||
data->tfm = tfm;
|
||||
if (len == CCMP_TK_LEN) {
|
||||
memcpy(data->key, key, CCMP_TK_LEN);
|
||||
data->key_set = 1;
|
||||
if (seq) {
|
||||
data->rx_pn[0] = seq[5];
|
||||
data->rx_pn[1] = seq[4];
|
||||
data->rx_pn[2] = seq[3];
|
||||
data->rx_pn[3] = seq[2];
|
||||
data->rx_pn[4] = seq[1];
|
||||
data->rx_pn[5] = seq[0];
|
||||
}
|
||||
crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
|
||||
} else if (len == 0) {
|
||||
data->key_set = 0;
|
||||
} else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostap_ccmp_get_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct hostap_ccmp_data *data = priv;
|
||||
|
||||
if (len < CCMP_TK_LEN)
|
||||
return -1;
|
||||
|
||||
if (!data->key_set)
|
||||
return 0;
|
||||
memcpy(key, data->key, CCMP_TK_LEN);
|
||||
|
||||
if (seq) {
|
||||
seq[0] = data->tx_pn[5];
|
||||
seq[1] = data->tx_pn[4];
|
||||
seq[2] = data->tx_pn[3];
|
||||
seq[3] = data->tx_pn[2];
|
||||
seq[4] = data->tx_pn[1];
|
||||
seq[5] = data->tx_pn[0];
|
||||
}
|
||||
|
||||
return CCMP_TK_LEN;
|
||||
}
|
||||
|
||||
|
||||
static char * hostap_ccmp_print_stats(char *p, void *priv)
|
||||
{
|
||||
struct hostap_ccmp_data *ccmp = priv;
|
||||
p += sprintf(p, "key[%d] alg=CCMP key_set=%d "
|
||||
"tx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"rx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"format_errors=%d replays=%d decrypt_errors=%d\n",
|
||||
ccmp->key_idx, ccmp->key_set,
|
||||
MAC2STR(ccmp->tx_pn), MAC2STR(ccmp->rx_pn),
|
||||
ccmp->dot11RSNAStatsCCMPFormatErrors,
|
||||
ccmp->dot11RSNAStatsCCMPReplays,
|
||||
ccmp->dot11RSNAStatsCCMPDecryptErrors);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static struct hostap_crypto_ops hostap_crypt_ccmp = {
|
||||
.name = "CCMP",
|
||||
.init = hostap_ccmp_init,
|
||||
.deinit = hostap_ccmp_deinit,
|
||||
.encrypt_mpdu = hostap_ccmp_encrypt,
|
||||
.decrypt_mpdu = hostap_ccmp_decrypt,
|
||||
.encrypt_msdu = NULL,
|
||||
.decrypt_msdu = NULL,
|
||||
.set_key = hostap_ccmp_set_key,
|
||||
.get_key = hostap_ccmp_get_key,
|
||||
.print_stats = hostap_ccmp_print_stats,
|
||||
.extra_prefix_len = CCMP_HDR_LEN,
|
||||
.extra_postfix_len = CCMP_MIC_LEN
|
||||
};
|
||||
|
||||
|
||||
static int __init hostap_crypto_ccmp_init(void)
|
||||
{
|
||||
if (hostap_register_crypto_ops(&hostap_crypt_ccmp) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void __exit hostap_crypto_ccmp_exit(void)
|
||||
{
|
||||
hostap_unregister_crypto_ops(&hostap_crypt_ccmp);
|
||||
}
|
||||
|
||||
|
||||
module_init(hostap_crypto_ccmp_init);
|
||||
module_exit(hostap_crypto_ccmp_exit);
|
696
drivers/net/wireless/hostap/hostap_crypt_tkip.c
Normal file
696
drivers/net/wireless/hostap/hostap_crypt_tkip.c
Normal file
@ -0,0 +1,696 @@
|
||||
/*
|
||||
* Host AP crypt: host-based TKIP encryption implementation for Host AP driver
|
||||
*
|
||||
* Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/iw_handler.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#include "hostap_crypt.h"
|
||||
#include "hostap_wlan.h"
|
||||
#include "hostap_80211.h"
|
||||
#include "hostap_config.h"
|
||||
|
||||
#ifndef CONFIG_CRYPTO
|
||||
#error CONFIG_CRYPTO is required to build this module.
|
||||
#endif
|
||||
#include <linux/crypto.h>
|
||||
#include <asm/scatterlist.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Host AP crypt: TKIP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
struct hostap_tkip_data {
|
||||
#define TKIP_KEY_LEN 32
|
||||
u8 key[TKIP_KEY_LEN];
|
||||
int key_set;
|
||||
|
||||
u32 tx_iv32;
|
||||
u16 tx_iv16;
|
||||
u16 tx_ttak[5];
|
||||
int tx_phase1_done;
|
||||
|
||||
u32 rx_iv32;
|
||||
u16 rx_iv16;
|
||||
u16 rx_ttak[5];
|
||||
int rx_phase1_done;
|
||||
u32 rx_iv32_new;
|
||||
u16 rx_iv16_new;
|
||||
|
||||
u32 dot11RSNAStatsTKIPReplays;
|
||||
u32 dot11RSNAStatsTKIPICVErrors;
|
||||
u32 dot11RSNAStatsTKIPLocalMICFailures;
|
||||
|
||||
int key_idx;
|
||||
|
||||
struct crypto_tfm *tfm_arc4;
|
||||
struct crypto_tfm *tfm_michael;
|
||||
|
||||
/* scratch buffers for virt_to_page() (crypto API) */
|
||||
u8 rx_hdr[16], tx_hdr[16];
|
||||
};
|
||||
|
||||
|
||||
static void * hostap_tkip_init(int key_idx)
|
||||
{
|
||||
struct hostap_tkip_data *priv;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return NULL;
|
||||
|
||||
priv = (struct hostap_tkip_data *) kmalloc(sizeof(*priv), GFP_ATOMIC);
|
||||
if (priv == NULL)
|
||||
goto fail;
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
priv->key_idx = key_idx;
|
||||
|
||||
priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0);
|
||||
if (priv->tfm_arc4 == NULL) {
|
||||
printk(KERN_DEBUG "hostap_crypt_tkip: could not allocate "
|
||||
"crypto API arc4\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->tfm_michael = crypto_alloc_tfm("michael_mic", 0);
|
||||
if (priv->tfm_michael == NULL) {
|
||||
printk(KERN_DEBUG "hostap_crypt_tkip: could not allocate "
|
||||
"crypto API michael_mic\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return priv;
|
||||
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tfm_michael)
|
||||
crypto_free_tfm(priv->tfm_michael);
|
||||
if (priv->tfm_arc4)
|
||||
crypto_free_tfm(priv->tfm_arc4);
|
||||
kfree(priv);
|
||||
}
|
||||
module_put(THIS_MODULE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void hostap_tkip_deinit(void *priv)
|
||||
{
|
||||
struct hostap_tkip_data *_priv = priv;
|
||||
if (_priv && _priv->tfm_michael)
|
||||
crypto_free_tfm(_priv->tfm_michael);
|
||||
if (_priv && _priv->tfm_arc4)
|
||||
crypto_free_tfm(_priv->tfm_arc4);
|
||||
kfree(priv);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
|
||||
static inline u16 RotR1(u16 val)
|
||||
{
|
||||
return (val >> 1) | (val << 15);
|
||||
}
|
||||
|
||||
|
||||
static inline u8 Lo8(u16 val)
|
||||
{
|
||||
return val & 0xff;
|
||||
}
|
||||
|
||||
|
||||
static inline u8 Hi8(u16 val)
|
||||
{
|
||||
return val >> 8;
|
||||
}
|
||||
|
||||
|
||||
static inline u16 Lo16(u32 val)
|
||||
{
|
||||
return val & 0xffff;
|
||||
}
|
||||
|
||||
|
||||
static inline u16 Hi16(u32 val)
|
||||
{
|
||||
return val >> 16;
|
||||
}
|
||||
|
||||
|
||||
static inline u16 Mk16(u8 hi, u8 lo)
|
||||
{
|
||||
return lo | (((u16) hi) << 8);
|
||||
}
|
||||
|
||||
|
||||
static inline u16 Mk16_le(u16 *v)
|
||||
{
|
||||
return le16_to_cpu(*v);
|
||||
}
|
||||
|
||||
|
||||
static const u16 Sbox[256] =
|
||||
{
|
||||
0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
|
||||
0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
|
||||
0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
|
||||
0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
|
||||
0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
|
||||
0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
|
||||
0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
|
||||
0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
|
||||
0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
|
||||
0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
|
||||
0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
|
||||
0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
|
||||
0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
|
||||
0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
|
||||
0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
|
||||
0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
|
||||
0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
|
||||
0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
|
||||
0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
|
||||
0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
|
||||
0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
|
||||
0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
|
||||
0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
|
||||
0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
|
||||
0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
|
||||
0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
|
||||
0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
|
||||
0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
|
||||
0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
|
||||
0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
|
||||
0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
|
||||
0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
|
||||
};
|
||||
|
||||
|
||||
static inline u16 _S_(u16 v)
|
||||
{
|
||||
u16 t = Sbox[Hi8(v)];
|
||||
return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
|
||||
}
|
||||
|
||||
|
||||
#define PHASE1_LOOP_COUNT 8
|
||||
|
||||
static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
|
||||
TTAK[0] = Lo16(IV32);
|
||||
TTAK[1] = Hi16(IV32);
|
||||
TTAK[2] = Mk16(TA[1], TA[0]);
|
||||
TTAK[3] = Mk16(TA[3], TA[2]);
|
||||
TTAK[4] = Mk16(TA[5], TA[4]);
|
||||
|
||||
for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
|
||||
j = 2 * (i & 1);
|
||||
TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
|
||||
TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
|
||||
TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
|
||||
TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
|
||||
TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
|
||||
u16 IV16)
|
||||
{
|
||||
/* Make temporary area overlap WEP seed so that the final copy can be
|
||||
* avoided on little endian hosts. */
|
||||
u16 *PPK = (u16 *) &WEPSeed[4];
|
||||
|
||||
/* Step 1 - make copy of TTAK and bring in TSC */
|
||||
PPK[0] = TTAK[0];
|
||||
PPK[1] = TTAK[1];
|
||||
PPK[2] = TTAK[2];
|
||||
PPK[3] = TTAK[3];
|
||||
PPK[4] = TTAK[4];
|
||||
PPK[5] = TTAK[4] + IV16;
|
||||
|
||||
/* Step 2 - 96-bit bijective mixing using S-box */
|
||||
PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0]));
|
||||
PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2]));
|
||||
PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4]));
|
||||
PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6]));
|
||||
PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8]));
|
||||
PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10]));
|
||||
|
||||
PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12]));
|
||||
PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14]));
|
||||
PPK[2] += RotR1(PPK[1]);
|
||||
PPK[3] += RotR1(PPK[2]);
|
||||
PPK[4] += RotR1(PPK[3]);
|
||||
PPK[5] += RotR1(PPK[4]);
|
||||
|
||||
/* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
|
||||
* WEPSeed[0..2] is transmitted as WEP IV */
|
||||
WEPSeed[0] = Hi8(IV16);
|
||||
WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
|
||||
WEPSeed[2] = Lo8(IV16);
|
||||
WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1);
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 6; i++)
|
||||
PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static int hostap_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct hostap_tkip_data *tkey = priv;
|
||||
int len;
|
||||
u8 rc4key[16], *pos, *icv;
|
||||
struct hostap_ieee80211_hdr *hdr;
|
||||
u32 crc;
|
||||
struct scatterlist sg;
|
||||
|
||||
if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 ||
|
||||
skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
if (!tkey->tx_phase1_done) {
|
||||
tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
|
||||
tkey->tx_iv32);
|
||||
tkey->tx_phase1_done = 1;
|
||||
}
|
||||
tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
|
||||
|
||||
len = skb->len - hdr_len;
|
||||
pos = skb_push(skb, 8);
|
||||
memmove(pos, pos + 8, hdr_len);
|
||||
pos += hdr_len;
|
||||
icv = skb_put(skb, 4);
|
||||
|
||||
*pos++ = rc4key[0];
|
||||
*pos++ = rc4key[1];
|
||||
*pos++ = rc4key[2];
|
||||
*pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */;
|
||||
*pos++ = tkey->tx_iv32 & 0xff;
|
||||
*pos++ = (tkey->tx_iv32 >> 8) & 0xff;
|
||||
*pos++ = (tkey->tx_iv32 >> 16) & 0xff;
|
||||
*pos++ = (tkey->tx_iv32 >> 24) & 0xff;
|
||||
|
||||
crc = ~crc32_le(~0, pos, len);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
|
||||
crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16);
|
||||
sg.page = virt_to_page(pos);
|
||||
sg.offset = offset_in_page(pos);
|
||||
sg.length = len + 4;
|
||||
crypto_cipher_encrypt(tkey->tfm_arc4, &sg, &sg, len + 4);
|
||||
|
||||
tkey->tx_iv16++;
|
||||
if (tkey->tx_iv16 == 0) {
|
||||
tkey->tx_phase1_done = 0;
|
||||
tkey->tx_iv32++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostap_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct hostap_tkip_data *tkey = priv;
|
||||
u8 rc4key[16];
|
||||
u8 keyidx, *pos, icv[4];
|
||||
u32 iv32;
|
||||
u16 iv16;
|
||||
struct hostap_ieee80211_hdr *hdr;
|
||||
u32 crc;
|
||||
struct scatterlist sg;
|
||||
int plen;
|
||||
|
||||
if (skb->len < hdr_len + 8 + 4)
|
||||
return -1;
|
||||
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
pos = skb->data + hdr_len;
|
||||
keyidx = pos[3];
|
||||
if (!(keyidx & (1 << 5))) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "TKIP: received packet without ExtIV"
|
||||
" flag from " MACSTR "\n", MAC2STR(hdr->addr2));
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
keyidx >>= 6;
|
||||
if (tkey->key_idx != keyidx) {
|
||||
printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
|
||||
"keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
|
||||
return -6;
|
||||
}
|
||||
if (!tkey->key_set) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "TKIP: received packet from " MACSTR
|
||||
" with keyid=%d that does not have a configured"
|
||||
" key\n", MAC2STR(hdr->addr2), keyidx);
|
||||
}
|
||||
return -3;
|
||||
}
|
||||
iv16 = (pos[0] << 8) | pos[2];
|
||||
iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
|
||||
pos += 8;
|
||||
|
||||
if (iv32 < tkey->rx_iv32 ||
|
||||
(iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) {
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "TKIP: replay detected: STA=" MACSTR
|
||||
" previous TSC %08x%04x received TSC "
|
||||
"%08x%04x\n", MAC2STR(hdr->addr2),
|
||||
tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
|
||||
}
|
||||
tkey->dot11RSNAStatsTKIPReplays++;
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
|
||||
tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
|
||||
tkey->rx_phase1_done = 1;
|
||||
}
|
||||
tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
|
||||
|
||||
plen = skb->len - hdr_len - 12;
|
||||
|
||||
crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16);
|
||||
sg.page = virt_to_page(pos);
|
||||
sg.offset = offset_in_page(pos);
|
||||
sg.length = plen + 4;
|
||||
crypto_cipher_decrypt(tkey->tfm_arc4, &sg, &sg, plen + 4);
|
||||
|
||||
crc = ~crc32_le(~0, pos, plen);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
if (memcmp(icv, pos + plen, 4) != 0) {
|
||||
if (iv32 != tkey->rx_iv32) {
|
||||
/* Previously cached Phase1 result was already lost, so
|
||||
* it needs to be recalculated for the next packet. */
|
||||
tkey->rx_phase1_done = 0;
|
||||
}
|
||||
if (net_ratelimit()) {
|
||||
printk(KERN_DEBUG "TKIP: ICV error detected: STA="
|
||||
MACSTR "\n", MAC2STR(hdr->addr2));
|
||||
}
|
||||
tkey->dot11RSNAStatsTKIPICVErrors++;
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* Update real counters only after Michael MIC verification has
|
||||
* completed */
|
||||
tkey->rx_iv32_new = iv32;
|
||||
tkey->rx_iv16_new = iv16;
|
||||
|
||||
/* Remove IV and ICV */
|
||||
memmove(skb->data + 8, skb->data, hdr_len);
|
||||
skb_pull(skb, 8);
|
||||
skb_trim(skb, skb->len - 4);
|
||||
|
||||
return keyidx;
|
||||
}
|
||||
|
||||
|
||||
static int michael_mic(struct hostap_tkip_data *tkey, u8 *key, u8 *hdr,
|
||||
u8 *data, size_t data_len, u8 *mic)
|
||||
{
|
||||
struct scatterlist sg[2];
|
||||
|
||||
if (tkey->tfm_michael == NULL) {
|
||||
printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
|
||||
return -1;
|
||||
}
|
||||
sg[0].page = virt_to_page(hdr);
|
||||
sg[0].offset = offset_in_page(hdr);
|
||||
sg[0].length = 16;
|
||||
|
||||
sg[1].page = virt_to_page(data);
|
||||
sg[1].offset = offset_in_page(data);
|
||||
sg[1].length = data_len;
|
||||
|
||||
crypto_digest_init(tkey->tfm_michael);
|
||||
crypto_digest_setkey(tkey->tfm_michael, key, 8);
|
||||
crypto_digest_update(tkey->tfm_michael, sg, 2);
|
||||
crypto_digest_final(tkey->tfm_michael, mic);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void michael_mic_hdr(struct sk_buff *skb, u8 *hdr)
|
||||
{
|
||||
struct hostap_ieee80211_hdr *hdr11;
|
||||
|
||||
hdr11 = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
switch (le16_to_cpu(hdr11->frame_control) &
|
||||
(WLAN_FC_FROMDS | WLAN_FC_TODS)) {
|
||||
case WLAN_FC_TODS:
|
||||
memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
|
||||
break;
|
||||
case WLAN_FC_FROMDS:
|
||||
memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
|
||||
break;
|
||||
case WLAN_FC_FROMDS | WLAN_FC_TODS:
|
||||
memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
|
||||
break;
|
||||
case 0:
|
||||
memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
|
||||
break;
|
||||
}
|
||||
|
||||
hdr[12] = 0; /* priority */
|
||||
hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
|
||||
}
|
||||
|
||||
|
||||
static int hostap_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct hostap_tkip_data *tkey = priv;
|
||||
u8 *pos;
|
||||
|
||||
if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
|
||||
printk(KERN_DEBUG "Invalid packet for Michael MIC add "
|
||||
"(tailroom=%d hdr_len=%d skb->len=%d)\n",
|
||||
skb_tailroom(skb), hdr_len, skb->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
michael_mic_hdr(skb, tkey->tx_hdr);
|
||||
pos = skb_put(skb, 8);
|
||||
if (michael_mic(tkey, &tkey->key[16], tkey->tx_hdr,
|
||||
skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void hostap_michael_mic_failure(struct net_device *dev,
|
||||
struct hostap_ieee80211_hdr *hdr,
|
||||
int keyidx)
|
||||
{
|
||||
union iwreq_data wrqu;
|
||||
char buf[128];
|
||||
|
||||
/* TODO: needed parameters: count, keyid, key type, src address, TSC */
|
||||
sprintf(buf, "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr="
|
||||
MACSTR ")", keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
|
||||
MAC2STR(hdr->addr2));
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.data.length = strlen(buf);
|
||||
wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
|
||||
}
|
||||
|
||||
|
||||
static int hostap_michael_mic_verify(struct sk_buff *skb, int keyidx,
|
||||
int hdr_len, void *priv)
|
||||
{
|
||||
struct hostap_tkip_data *tkey = priv;
|
||||
u8 mic[8];
|
||||
|
||||
if (!tkey->key_set)
|
||||
return -1;
|
||||
|
||||
michael_mic_hdr(skb, tkey->rx_hdr);
|
||||
if (michael_mic(tkey, &tkey->key[24], tkey->rx_hdr,
|
||||
skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
|
||||
return -1;
|
||||
if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
|
||||
struct hostap_ieee80211_hdr *hdr;
|
||||
hdr = (struct hostap_ieee80211_hdr *) skb->data;
|
||||
printk(KERN_DEBUG "%s: Michael MIC verification failed for "
|
||||
"MSDU from " MACSTR " keyidx=%d\n",
|
||||
skb->dev ? skb->dev->name : "N/A", MAC2STR(hdr->addr2),
|
||||
keyidx);
|
||||
if (skb->dev)
|
||||
hostap_michael_mic_failure(skb->dev, hdr, keyidx);
|
||||
tkey->dot11RSNAStatsTKIPLocalMICFailures++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Update TSC counters for RX now that the packet verification has
|
||||
* completed. */
|
||||
tkey->rx_iv32 = tkey->rx_iv32_new;
|
||||
tkey->rx_iv16 = tkey->rx_iv16_new;
|
||||
|
||||
skb_trim(skb, skb->len - 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostap_tkip_set_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct hostap_tkip_data *tkey = priv;
|
||||
int keyidx;
|
||||
struct crypto_tfm *tfm = tkey->tfm_michael;
|
||||
struct crypto_tfm *tfm2 = tkey->tfm_arc4;
|
||||
|
||||
keyidx = tkey->key_idx;
|
||||
memset(tkey, 0, sizeof(*tkey));
|
||||
tkey->key_idx = keyidx;
|
||||
tkey->tfm_michael = tfm;
|
||||
tkey->tfm_arc4 = tfm2;
|
||||
if (len == TKIP_KEY_LEN) {
|
||||
memcpy(tkey->key, key, TKIP_KEY_LEN);
|
||||
tkey->key_set = 1;
|
||||
tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
|
||||
if (seq) {
|
||||
tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
|
||||
(seq[3] << 8) | seq[2];
|
||||
tkey->rx_iv16 = (seq[1] << 8) | seq[0];
|
||||
}
|
||||
} else if (len == 0) {
|
||||
tkey->key_set = 0;
|
||||
} else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostap_tkip_get_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct hostap_tkip_data *tkey = priv;
|
||||
|
||||
if (len < TKIP_KEY_LEN)
|
||||
return -1;
|
||||
|
||||
if (!tkey->key_set)
|
||||
return 0;
|
||||
memcpy(key, tkey->key, TKIP_KEY_LEN);
|
||||
|
||||
if (seq) {
|
||||
/* Return the sequence number of the last transmitted frame. */
|
||||
u16 iv16 = tkey->tx_iv16;
|
||||
u32 iv32 = tkey->tx_iv32;
|
||||
if (iv16 == 0)
|
||||
iv32--;
|
||||
iv16--;
|
||||
seq[0] = tkey->tx_iv16;
|
||||
seq[1] = tkey->tx_iv16 >> 8;
|
||||
seq[2] = tkey->tx_iv32;
|
||||
seq[3] = tkey->tx_iv32 >> 8;
|
||||
seq[4] = tkey->tx_iv32 >> 16;
|
||||
seq[5] = tkey->tx_iv32 >> 24;
|
||||
}
|
||||
|
||||
return TKIP_KEY_LEN;
|
||||
}
|
||||
|
||||
|
||||
static char * hostap_tkip_print_stats(char *p, void *priv)
|
||||
{
|
||||
struct hostap_tkip_data *tkip = priv;
|
||||
p += sprintf(p, "key[%d] alg=TKIP key_set=%d "
|
||||
"tx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"rx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"replays=%d icv_errors=%d local_mic_failures=%d\n",
|
||||
tkip->key_idx, tkip->key_set,
|
||||
(tkip->tx_iv32 >> 24) & 0xff,
|
||||
(tkip->tx_iv32 >> 16) & 0xff,
|
||||
(tkip->tx_iv32 >> 8) & 0xff,
|
||||
tkip->tx_iv32 & 0xff,
|
||||
(tkip->tx_iv16 >> 8) & 0xff,
|
||||
tkip->tx_iv16 & 0xff,
|
||||
(tkip->rx_iv32 >> 24) & 0xff,
|
||||
(tkip->rx_iv32 >> 16) & 0xff,
|
||||
(tkip->rx_iv32 >> 8) & 0xff,
|
||||
tkip->rx_iv32 & 0xff,
|
||||
(tkip->rx_iv16 >> 8) & 0xff,
|
||||
tkip->rx_iv16 & 0xff,
|
||||
tkip->dot11RSNAStatsTKIPReplays,
|
||||
tkip->dot11RSNAStatsTKIPICVErrors,
|
||||
tkip->dot11RSNAStatsTKIPLocalMICFailures);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static struct hostap_crypto_ops hostap_crypt_tkip = {
|
||||
.name = "TKIP",
|
||||
.init = hostap_tkip_init,
|
||||
.deinit = hostap_tkip_deinit,
|
||||
.encrypt_mpdu = hostap_tkip_encrypt,
|
||||
.decrypt_mpdu = hostap_tkip_decrypt,
|
||||
.encrypt_msdu = hostap_michael_mic_add,
|
||||
.decrypt_msdu = hostap_michael_mic_verify,
|
||||
.set_key = hostap_tkip_set_key,
|
||||
.get_key = hostap_tkip_get_key,
|
||||
.print_stats = hostap_tkip_print_stats,
|
||||
.extra_prefix_len = 4 + 4 /* IV + ExtIV */,
|
||||
.extra_postfix_len = 8 + 4 /* MIC + ICV */
|
||||
};
|
||||
|
||||
|
||||
static int __init hostap_crypto_tkip_init(void)
|
||||
{
|
||||
if (hostap_register_crypto_ops(&hostap_crypt_tkip) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void __exit hostap_crypto_tkip_exit(void)
|
||||
{
|
||||
hostap_unregister_crypto_ops(&hostap_crypt_tkip);
|
||||
}
|
||||
|
||||
|
||||
module_init(hostap_crypto_tkip_init);
|
||||
module_exit(hostap_crypto_tkip_exit);
|
281
drivers/net/wireless/hostap/hostap_crypt_wep.c
Normal file
281
drivers/net/wireless/hostap/hostap_crypt_wep.c
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Host AP crypt: host-based WEP encryption implementation for Host AP driver
|
||||
*
|
||||
* Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#include "hostap_crypt.h"
|
||||
|
||||
#ifndef CONFIG_CRYPTO
|
||||
#error CONFIG_CRYPTO is required to build this module.
|
||||
#endif
|
||||
#include <linux/crypto.h>
|
||||
#include <asm/scatterlist.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Host AP crypt: WEP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
struct prism2_wep_data {
|
||||
u32 iv;
|
||||
#define WEP_KEY_LEN 13
|
||||
u8 key[WEP_KEY_LEN + 1];
|
||||
u8 key_len;
|
||||
u8 key_idx;
|
||||
struct crypto_tfm *tfm;
|
||||
};
|
||||
|
||||
|
||||
static void * prism2_wep_init(int keyidx)
|
||||
{
|
||||
struct prism2_wep_data *priv;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return NULL;
|
||||
|
||||
priv = (struct prism2_wep_data *) kmalloc(sizeof(*priv), GFP_ATOMIC);
|
||||
if (priv == NULL)
|
||||
goto fail;
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
priv->key_idx = keyidx;
|
||||
|
||||
priv->tfm = crypto_alloc_tfm("arc4", 0);
|
||||
if (priv->tfm == NULL) {
|
||||
printk(KERN_DEBUG "hostap_crypt_wep: could not allocate "
|
||||
"crypto API arc4\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* start WEP IV from a random value */
|
||||
get_random_bytes(&priv->iv, 4);
|
||||
|
||||
return priv;
|
||||
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tfm)
|
||||
crypto_free_tfm(priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
module_put(THIS_MODULE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void prism2_wep_deinit(void *priv)
|
||||
{
|
||||
struct prism2_wep_data *_priv = priv;
|
||||
if (_priv && _priv->tfm)
|
||||
crypto_free_tfm(_priv->tfm);
|
||||
kfree(priv);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
|
||||
/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
|
||||
* for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
|
||||
* so the payload length increases with 8 bytes.
|
||||
*
|
||||
* WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
|
||||
*/
|
||||
static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
u32 crc, klen, len;
|
||||
u8 key[WEP_KEY_LEN + 3];
|
||||
u8 *pos, *icv;
|
||||
struct scatterlist sg;
|
||||
|
||||
if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4 ||
|
||||
skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
len = skb->len - hdr_len;
|
||||
pos = skb_push(skb, 4);
|
||||
memmove(pos, pos + 4, hdr_len);
|
||||
pos += hdr_len;
|
||||
|
||||
klen = 3 + wep->key_len;
|
||||
|
||||
wep->iv++;
|
||||
|
||||
/* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
|
||||
* scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
|
||||
* can be used to speedup attacks, so avoid using them. */
|
||||
if ((wep->iv & 0xff00) == 0xff00) {
|
||||
u8 B = (wep->iv >> 16) & 0xff;
|
||||
if (B >= 3 && B < klen)
|
||||
wep->iv += 0x0100;
|
||||
}
|
||||
|
||||
/* Prepend 24-bit IV to RC4 key and TX frame */
|
||||
*pos++ = key[0] = (wep->iv >> 16) & 0xff;
|
||||
*pos++ = key[1] = (wep->iv >> 8) & 0xff;
|
||||
*pos++ = key[2] = wep->iv & 0xff;
|
||||
*pos++ = wep->key_idx << 6;
|
||||
|
||||
/* Copy rest of the WEP key (the secret part) */
|
||||
memcpy(key + 3, wep->key, wep->key_len);
|
||||
|
||||
/* Append little-endian CRC32 and encrypt it to produce ICV */
|
||||
crc = ~crc32_le(~0, pos, len);
|
||||
icv = skb_put(skb, 4);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
|
||||
crypto_cipher_setkey(wep->tfm, key, klen);
|
||||
sg.page = virt_to_page(pos);
|
||||
sg.offset = offset_in_page(pos);
|
||||
sg.length = len + 4;
|
||||
crypto_cipher_encrypt(wep->tfm, &sg, &sg, len + 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
|
||||
* the frame: IV (4 bytes), encrypted payload (including SNAP header),
|
||||
* ICV (4 bytes). len includes both IV and ICV.
|
||||
*
|
||||
* Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
|
||||
* failure. If frame is OK, IV and ICV will be removed.
|
||||
*/
|
||||
static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
u32 crc, klen, plen;
|
||||
u8 key[WEP_KEY_LEN + 3];
|
||||
u8 keyidx, *pos, icv[4];
|
||||
struct scatterlist sg;
|
||||
|
||||
if (skb->len < hdr_len + 8)
|
||||
return -1;
|
||||
|
||||
pos = skb->data + hdr_len;
|
||||
key[0] = *pos++;
|
||||
key[1] = *pos++;
|
||||
key[2] = *pos++;
|
||||
keyidx = *pos++ >> 6;
|
||||
if (keyidx != wep->key_idx)
|
||||
return -1;
|
||||
|
||||
klen = 3 + wep->key_len;
|
||||
|
||||
/* Copy rest of the WEP key (the secret part) */
|
||||
memcpy(key + 3, wep->key, wep->key_len);
|
||||
|
||||
/* Apply RC4 to data and compute CRC32 over decrypted data */
|
||||
plen = skb->len - hdr_len - 8;
|
||||
|
||||
crypto_cipher_setkey(wep->tfm, key, klen);
|
||||
sg.page = virt_to_page(pos);
|
||||
sg.offset = offset_in_page(pos);
|
||||
sg.length = plen + 4;
|
||||
crypto_cipher_decrypt(wep->tfm, &sg, &sg, plen + 4);
|
||||
|
||||
crc = ~crc32_le(~0, pos, plen);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
if (memcmp(icv, pos + plen, 4) != 0) {
|
||||
/* ICV mismatch - drop frame */
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Remove IV and ICV */
|
||||
memmove(skb->data + 4, skb->data, hdr_len);
|
||||
skb_pull(skb, 4);
|
||||
skb_trim(skb, skb->len - 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_wep_set_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
|
||||
if (len < 0 || len > WEP_KEY_LEN)
|
||||
return -1;
|
||||
|
||||
memcpy(wep->key, key, len);
|
||||
wep->key_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_wep_get_key(void *key, int len, u8 *seq, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
|
||||
if (len < wep->key_len)
|
||||
return -1;
|
||||
|
||||
memcpy(key, wep->key, wep->key_len);
|
||||
|
||||
return wep->key_len;
|
||||
}
|
||||
|
||||
|
||||
static char * prism2_wep_print_stats(char *p, void *priv)
|
||||
{
|
||||
struct prism2_wep_data *wep = priv;
|
||||
p += sprintf(p, "key[%d] alg=WEP len=%d\n",
|
||||
wep->key_idx, wep->key_len);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static struct hostap_crypto_ops hostap_crypt_wep = {
|
||||
.name = "WEP",
|
||||
.init = prism2_wep_init,
|
||||
.deinit = prism2_wep_deinit,
|
||||
.encrypt_mpdu = prism2_wep_encrypt,
|
||||
.decrypt_mpdu = prism2_wep_decrypt,
|
||||
.encrypt_msdu = NULL,
|
||||
.decrypt_msdu = NULL,
|
||||
.set_key = prism2_wep_set_key,
|
||||
.get_key = prism2_wep_get_key,
|
||||
.print_stats = prism2_wep_print_stats,
|
||||
.extra_prefix_len = 4 /* IV */,
|
||||
.extra_postfix_len = 4 /* ICV */
|
||||
};
|
||||
|
||||
|
||||
static int __init hostap_crypto_wep_init(void)
|
||||
{
|
||||
if (hostap_register_crypto_ops(&hostap_crypt_wep) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void __exit hostap_crypto_wep_exit(void)
|
||||
{
|
||||
hostap_unregister_crypto_ops(&hostap_crypt_wep);
|
||||
}
|
||||
|
||||
|
||||
module_init(hostap_crypto_wep_init);
|
||||
module_exit(hostap_crypto_wep_exit);
|
950
drivers/net/wireless/hostap/hostap_cs.c
Normal file
950
drivers/net/wireless/hostap/hostap_cs.c
Normal file
@ -0,0 +1,950 @@
|
||||
#define PRISM2_PCCARD
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/iw_handler.h>
|
||||
|
||||
#include <pcmcia/version.h>
|
||||
#include <pcmcia/cs_types.h>
|
||||
#include <pcmcia/cs.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
#include <pcmcia/ds.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hostap_wlan.h"
|
||||
|
||||
|
||||
static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
|
||||
static dev_info_t dev_info = "hostap_cs";
|
||||
static dev_link_t *dev_list = NULL;
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
|
||||
"cards (PC Card).");
|
||||
MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
static int irq_mask = 0xdeb8;
|
||||
module_param(irq_mask, int, 0444);
|
||||
|
||||
static int irq_list[4] = { -1 };
|
||||
module_param_array(irq_list, int, NULL, 0444);
|
||||
|
||||
static int ignore_cis_vcc;
|
||||
module_param(ignore_cis_vcc, int, 0444);
|
||||
MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
|
||||
|
||||
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
|
||||
static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
|
||||
outb(v, dev->base_addr + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u8 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = inb(dev->base_addr + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
|
||||
outw(v, dev->base_addr + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u16 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = inw(dev->base_addr + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
|
||||
u8 *buf, int wc)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
|
||||
outsw(dev->base_addr + a, buf, wc);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline void hfa384x_insw_debug(struct net_device *dev, int a,
|
||||
u8 *buf, int wc)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
|
||||
insw(dev->base_addr + a, buf, wc);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
|
||||
#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
|
||||
#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
|
||||
#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
|
||||
#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
|
||||
#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
|
||||
|
||||
#else /* PRISM2_IO_DEBUG */
|
||||
|
||||
#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
|
||||
#define HFA384X_INB(a) inb(dev->base_addr + (a))
|
||||
#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
|
||||
#define HFA384X_INW(a) inw(dev->base_addr + (a))
|
||||
#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
|
||||
#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
|
||||
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
|
||||
|
||||
static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
|
||||
int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
if (len / 2)
|
||||
HFA384X_INSW(d_off, buf, len / 2);
|
||||
pos += len / 2;
|
||||
|
||||
if (len & 1)
|
||||
*((char *) pos) = HFA384X_INB(d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
if (len / 2)
|
||||
HFA384X_OUTSW(d_off, buf, len / 2);
|
||||
pos += len / 2;
|
||||
|
||||
if (len & 1)
|
||||
HFA384X_OUTB(*((char *) pos), d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* FIX: This might change at some point.. */
|
||||
#include "hostap_hw.c"
|
||||
|
||||
|
||||
|
||||
static void prism2_detach(dev_link_t *link);
|
||||
static void prism2_release(u_long arg);
|
||||
static int prism2_event(event_t event, int priority,
|
||||
event_callback_args_t *args);
|
||||
|
||||
|
||||
static int prism2_pccard_card_present(local_info_t *local)
|
||||
{
|
||||
if (local->link != NULL &&
|
||||
((local->link->state & (DEV_PRESENT | DEV_CONFIG)) ==
|
||||
(DEV_PRESENT | DEV_CONFIG)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0
|
||||
* Document No. 20-10-00058, January 2004
|
||||
* http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf
|
||||
*/
|
||||
#define SANDISK_WLAN_ACTIVATION_OFF 0x40
|
||||
#define SANDISK_HCR_OFF 0x42
|
||||
|
||||
|
||||
static void sandisk_set_iobase(local_info_t *local)
|
||||
{
|
||||
int res;
|
||||
conf_reg_t reg;
|
||||
|
||||
reg.Function = 0;
|
||||
reg.Action = CS_WRITE;
|
||||
reg.Offset = 0x10; /* 0x3f0 IO base 1 */
|
||||
reg.Value = local->link->io.BasePort1 & 0x00ff;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -"
|
||||
" res=%d\n", res);
|
||||
}
|
||||
udelay(10);
|
||||
|
||||
reg.Function = 0;
|
||||
reg.Action = CS_WRITE;
|
||||
reg.Offset = 0x12; /* 0x3f2 IO base 2 */
|
||||
reg.Value = (local->link->io.BasePort1 & 0xff00) >> 8;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -"
|
||||
" res=%d\n", res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void sandisk_write_hcr(local_info_t *local, int hcr)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
int i;
|
||||
|
||||
HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF);
|
||||
udelay(50);
|
||||
for (i = 0; i < 10; i++) {
|
||||
HFA384X_OUTB(hcr, SANDISK_HCR_OFF);
|
||||
}
|
||||
udelay(55);
|
||||
HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF);
|
||||
}
|
||||
|
||||
|
||||
static int sandisk_enable_wireless(struct net_device *dev)
|
||||
{
|
||||
int res, ret = 0;
|
||||
conf_reg_t reg;
|
||||
struct hostap_interface *iface = dev->priv;
|
||||
local_info_t *local = iface->local;
|
||||
tuple_t tuple;
|
||||
cisparse_t *parse = NULL;
|
||||
u_char buf[64];
|
||||
|
||||
if (local->link->io.NumPorts1 < 0x42) {
|
||||
/* Not enough ports to be SanDisk multi-function card */
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
|
||||
if (parse == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
tuple.DesiredTuple = CISTPL_MANFID;
|
||||
tuple.Attributes = TUPLE_RETURN_COMMON;
|
||||
tuple.TupleData = buf;
|
||||
tuple.TupleDataMax = sizeof(buf);
|
||||
tuple.TupleOffset = 0;
|
||||
if (pcmcia_get_first_tuple(local->link->handle, &tuple) ||
|
||||
pcmcia_get_tuple_data(local->link->handle, &tuple) ||
|
||||
pcmcia_parse_tuple(local->link->handle, &tuple, parse) ||
|
||||
parse->manfid.manf != 0xd601 || parse->manfid.card != 0x0101) {
|
||||
/* No SanDisk manfid found */
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
|
||||
if (pcmcia_get_first_tuple(local->link->handle, &tuple) ||
|
||||
pcmcia_get_tuple_data(local->link->handle, &tuple) ||
|
||||
pcmcia_parse_tuple(local->link->handle, &tuple, parse) ||
|
||||
parse->longlink_mfc.nfn < 2) {
|
||||
/* No multi-function links found */
|
||||
ret = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected"
|
||||
" - using vendor-specific initialization\n", dev->name);
|
||||
local->sandisk_connectplus = 1;
|
||||
|
||||
reg.Function = 0;
|
||||
reg.Action = CS_WRITE;
|
||||
reg.Offset = CISREG_COR;
|
||||
reg.Value = COR_SOFT_RESET;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
|
||||
dev->name, res);
|
||||
goto done;
|
||||
}
|
||||
mdelay(5);
|
||||
|
||||
reg.Function = 0;
|
||||
reg.Action = CS_WRITE;
|
||||
reg.Offset = CISREG_COR;
|
||||
/*
|
||||
* Do not enable interrupts here to avoid some bogus events. Interrupts
|
||||
* will be enabled during the first cor_sreset call.
|
||||
*/
|
||||
reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
|
||||
dev->name, res);
|
||||
goto done;
|
||||
}
|
||||
mdelay(5);
|
||||
|
||||
sandisk_set_iobase(local);
|
||||
|
||||
HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF);
|
||||
udelay(10);
|
||||
HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF);
|
||||
udelay(10);
|
||||
|
||||
done:
|
||||
kfree(parse);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void prism2_pccard_cor_sreset(local_info_t *local)
|
||||
{
|
||||
int res;
|
||||
conf_reg_t reg;
|
||||
|
||||
if (!prism2_pccard_card_present(local))
|
||||
return;
|
||||
|
||||
reg.Function = 0;
|
||||
reg.Action = CS_READ;
|
||||
reg.Offset = CISREG_COR;
|
||||
reg.Value = 0;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n",
|
||||
res);
|
||||
return;
|
||||
}
|
||||
printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n",
|
||||
reg.Value);
|
||||
|
||||
reg.Action = CS_WRITE;
|
||||
reg.Value |= COR_SOFT_RESET;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n",
|
||||
res);
|
||||
return;
|
||||
}
|
||||
|
||||
mdelay(local->sandisk_connectplus ? 5 : 2);
|
||||
|
||||
reg.Value &= ~COR_SOFT_RESET;
|
||||
if (local->sandisk_connectplus)
|
||||
reg.Value |= COR_IREQ_ENA;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n",
|
||||
res);
|
||||
return;
|
||||
}
|
||||
|
||||
mdelay(local->sandisk_connectplus ? 5 : 2);
|
||||
|
||||
if (local->sandisk_connectplus)
|
||||
sandisk_set_iobase(local);
|
||||
}
|
||||
|
||||
|
||||
static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
|
||||
{
|
||||
int res;
|
||||
conf_reg_t reg;
|
||||
int old_cor;
|
||||
|
||||
if (!prism2_pccard_card_present(local))
|
||||
return;
|
||||
|
||||
if (local->sandisk_connectplus) {
|
||||
sandisk_write_hcr(local, hcr);
|
||||
return;
|
||||
}
|
||||
|
||||
reg.Function = 0;
|
||||
reg.Action = CS_READ;
|
||||
reg.Offset = CISREG_COR;
|
||||
reg.Value = 0;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 "
|
||||
"(%d)\n", res);
|
||||
return;
|
||||
}
|
||||
printk(KERN_DEBUG "prism2_pccard_genesis_sreset: original COR %02x\n",
|
||||
reg.Value);
|
||||
old_cor = reg.Value;
|
||||
|
||||
reg.Action = CS_WRITE;
|
||||
reg.Value |= COR_SOFT_RESET;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 "
|
||||
"(%d)\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
mdelay(10);
|
||||
|
||||
/* Setup Genesis mode */
|
||||
reg.Action = CS_WRITE;
|
||||
reg.Value = hcr;
|
||||
reg.Offset = CISREG_CCSR;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 "
|
||||
"(%d)\n", res);
|
||||
return;
|
||||
}
|
||||
mdelay(10);
|
||||
|
||||
reg.Action = CS_WRITE;
|
||||
reg.Offset = CISREG_COR;
|
||||
reg.Value = old_cor & ~COR_SOFT_RESET;
|
||||
res = pcmcia_access_configuration_register(local->link->handle, ®);
|
||||
if (res != CS_SUCCESS) {
|
||||
printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 "
|
||||
"(%d)\n", res);
|
||||
return;
|
||||
}
|
||||
|
||||
mdelay(10);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_pccard_dev_open(local_info_t *local)
|
||||
{
|
||||
local->link->open++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_pccard_dev_close(local_info_t *local)
|
||||
{
|
||||
if (local == NULL || local->link == NULL)
|
||||
return 1;
|
||||
|
||||
if (!local->link->open) {
|
||||
printk(KERN_WARNING "%s: prism2_pccard_dev_close(): "
|
||||
"link not open?!\n", local->dev->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
local->link->open--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct prism2_helper_functions prism2_pccard_funcs =
|
||||
{
|
||||
.card_present = prism2_pccard_card_present,
|
||||
.cor_sreset = prism2_pccard_cor_sreset,
|
||||
.dev_open = prism2_pccard_dev_open,
|
||||
.dev_close = prism2_pccard_dev_close,
|
||||
.genesis_reset = prism2_pccard_genesis_reset,
|
||||
.hw_type = HOSTAP_HW_PCCARD,
|
||||
};
|
||||
|
||||
|
||||
/* allocate local data and register with CardServices
|
||||
* initialize dev_link structure, but do not configure the card yet */
|
||||
static dev_link_t *prism2_attach(void)
|
||||
{
|
||||
dev_link_t *link;
|
||||
client_reg_t client_reg;
|
||||
int ret;
|
||||
|
||||
link = kmalloc(sizeof(dev_link_t), GFP_KERNEL);
|
||||
if (link == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(link, 0, sizeof(dev_link_t));
|
||||
|
||||
PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
|
||||
link->conf.Vcc = 33;
|
||||
link->conf.IntType = INT_MEMORY_AND_IO;
|
||||
|
||||
/* register with CardServices */
|
||||
link->next = dev_list;
|
||||
dev_list = link;
|
||||
client_reg.dev_info = &dev_info;
|
||||
client_reg.Attributes = INFO_IO_CLIENT;
|
||||
client_reg.EventMask = CS_EVENT_CARD_INSERTION |
|
||||
CS_EVENT_CARD_REMOVAL |
|
||||
CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
|
||||
CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
|
||||
client_reg.event_handler = &prism2_event;
|
||||
client_reg.Version = 0x0210;
|
||||
client_reg.event_callback_args.client_data = link;
|
||||
ret = pcmcia_register_client(&link->handle, &client_reg);
|
||||
if (ret != CS_SUCCESS) {
|
||||
cs_error(link->handle, RegisterClient, ret);
|
||||
prism2_detach(link);
|
||||
return NULL;
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
|
||||
static void prism2_detach(dev_link_t *link)
|
||||
{
|
||||
dev_link_t **linkp;
|
||||
|
||||
PDEBUG(DEBUG_FLOW, "prism2_detach\n");
|
||||
|
||||
for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
|
||||
if (*linkp == link)
|
||||
break;
|
||||
if (*linkp == NULL) {
|
||||
printk(KERN_WARNING "%s: Attempt to detach non-existing "
|
||||
"PCMCIA client\n", dev_info);
|
||||
return;
|
||||
}
|
||||
|
||||
if (link->state & DEV_CONFIG) {
|
||||
prism2_release((u_long)link);
|
||||
}
|
||||
|
||||
if (link->handle) {
|
||||
int res = pcmcia_deregister_client(link->handle);
|
||||
if (res) {
|
||||
printk("CardService(DeregisterClient) => %d\n", res);
|
||||
cs_error(link->handle, DeregisterClient, res);
|
||||
}
|
||||
}
|
||||
|
||||
*linkp = link->next;
|
||||
/* release net devices */
|
||||
if (link->priv) {
|
||||
prism2_free_local_data((struct net_device *) link->priv);
|
||||
|
||||
}
|
||||
kfree(link);
|
||||
}
|
||||
|
||||
|
||||
#define CS_CHECK(fn, ret) \
|
||||
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
|
||||
|
||||
#define CFG_CHECK2(fn, retf) \
|
||||
do { int ret = (retf); \
|
||||
if (ret != 0) { \
|
||||
PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \
|
||||
cs_error(link->handle, fn, ret); \
|
||||
goto next_entry; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* run after a CARD_INSERTION event is received to configure the PCMCIA
|
||||
* socket and make the device available to the system */
|
||||
static int prism2_config(dev_link_t *link)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
int ret = 1;
|
||||
tuple_t tuple;
|
||||
cisparse_t *parse;
|
||||
int last_fn, last_ret;
|
||||
u_char buf[64];
|
||||
config_info_t conf;
|
||||
cistpl_cftable_entry_t dflt = { 0 };
|
||||
|
||||
PDEBUG(DEBUG_FLOW, "prism2_config()\n");
|
||||
|
||||
parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
|
||||
if (parse == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
tuple.DesiredTuple = CISTPL_CONFIG;
|
||||
tuple.Attributes = 0;
|
||||
tuple.TupleData = buf;
|
||||
tuple.TupleDataMax = sizeof(buf);
|
||||
tuple.TupleOffset = 0;
|
||||
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
|
||||
CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link->handle, &tuple));
|
||||
CS_CHECK(ParseTuple, pcmcia_parse_tuple(link->handle, &tuple, parse));
|
||||
link->conf.ConfigBase = parse->config.base;
|
||||
link->conf.Present = parse->config.rmask[0];
|
||||
|
||||
CS_CHECK(GetConfigurationInfo,
|
||||
pcmcia_get_configuration_info(link->handle, &conf));
|
||||
PDEBUG(DEBUG_HW, "%s: %s Vcc=%d (from config)\n", dev_info,
|
||||
ignore_cis_vcc ? "ignoring" : "setting", conf.Vcc);
|
||||
link->conf.Vcc = conf.Vcc;
|
||||
|
||||
/* Look for an appropriate configuration table entry in the CIS */
|
||||
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
|
||||
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple));
|
||||
for (;;) {
|
||||
cistpl_cftable_entry_t *cfg = &(parse->cftable_entry);
|
||||
CFG_CHECK2(GetTupleData,
|
||||
pcmcia_get_tuple_data(link->handle, &tuple));
|
||||
CFG_CHECK2(ParseTuple,
|
||||
pcmcia_parse_tuple(link->handle, &tuple, parse));
|
||||
|
||||
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
|
||||
dflt = *cfg;
|
||||
if (cfg->index == 0)
|
||||
goto next_entry;
|
||||
link->conf.ConfigIndex = cfg->index;
|
||||
PDEBUG(DEBUG_EXTRA, "Checking CFTABLE_ENTRY 0x%02X "
|
||||
"(default 0x%02X)\n", cfg->index, dflt.index);
|
||||
|
||||
/* Does this card need audio output? */
|
||||
if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
|
||||
link->conf.Attributes |= CONF_ENABLE_SPKR;
|
||||
link->conf.Status = CCSR_AUDIO_ENA;
|
||||
}
|
||||
|
||||
/* Use power settings for Vcc and Vpp if present */
|
||||
/* Note that the CIS values need to be rescaled */
|
||||
if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
|
||||
if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] /
|
||||
10000 && !ignore_cis_vcc) {
|
||||
PDEBUG(DEBUG_EXTRA, " Vcc mismatch - skipping"
|
||||
" this entry\n");
|
||||
goto next_entry;
|
||||
}
|
||||
} else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
|
||||
if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] /
|
||||
10000 && !ignore_cis_vcc) {
|
||||
PDEBUG(DEBUG_EXTRA, " Vcc (default) mismatch "
|
||||
"- skipping this entry\n");
|
||||
goto next_entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
|
||||
link->conf.Vpp1 = link->conf.Vpp2 =
|
||||
cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
|
||||
else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
|
||||
link->conf.Vpp1 = link->conf.Vpp2 =
|
||||
dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
|
||||
|
||||
/* Do we need to allocate an interrupt? */
|
||||
if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
|
||||
link->conf.Attributes |= CONF_ENABLE_IRQ;
|
||||
else if (!(link->conf.Attributes & CONF_ENABLE_IRQ)) {
|
||||
/* At least Compaq WL200 does not have IRQInfo1 set,
|
||||
* but it does not work without interrupts.. */
|
||||
printk("Config has no IRQ info, but trying to enable "
|
||||
"IRQ anyway..\n");
|
||||
link->conf.Attributes |= CONF_ENABLE_IRQ;
|
||||
}
|
||||
|
||||
/* IO window settings */
|
||||
PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d "
|
||||
"dflt.io.nwin=%d\n",
|
||||
cfg->io.nwin, dflt.io.nwin);
|
||||
link->io.NumPorts1 = link->io.NumPorts2 = 0;
|
||||
if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
|
||||
cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
|
||||
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
|
||||
PDEBUG(DEBUG_EXTRA, "io->flags = 0x%04X, "
|
||||
"io.base=0x%04x, len=%d\n", io->flags,
|
||||
io->win[0].base, io->win[0].len);
|
||||
if (!(io->flags & CISTPL_IO_8BIT))
|
||||
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
|
||||
if (!(io->flags & CISTPL_IO_16BIT))
|
||||
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
|
||||
link->io.IOAddrLines = io->flags &
|
||||
CISTPL_IO_LINES_MASK;
|
||||
link->io.BasePort1 = io->win[0].base;
|
||||
link->io.NumPorts1 = io->win[0].len;
|
||||
if (io->nwin > 1) {
|
||||
link->io.Attributes2 = link->io.Attributes1;
|
||||
link->io.BasePort2 = io->win[1].base;
|
||||
link->io.NumPorts2 = io->win[1].len;
|
||||
}
|
||||
}
|
||||
|
||||
/* This reserves IO space but doesn't actually enable it */
|
||||
CFG_CHECK2(RequestIO,
|
||||
pcmcia_request_io(link->handle, &link->io));
|
||||
|
||||
/* This configuration table entry is OK */
|
||||
break;
|
||||
|
||||
next_entry:
|
||||
CS_CHECK(GetNextTuple,
|
||||
pcmcia_get_next_tuple(link->handle, &tuple));
|
||||
}
|
||||
|
||||
/* Need to allocate net_device before requesting IRQ handler */
|
||||
dev = prism2_init_local_data(&prism2_pccard_funcs, 0);
|
||||
if (dev == NULL)
|
||||
goto failed;
|
||||
link->priv = dev;
|
||||
|
||||
/*
|
||||
* Allocate an interrupt line. Note that this does not assign a
|
||||
* handler to the interrupt, unless the 'Handler' member of the
|
||||
* irq structure is initialized.
|
||||
*/
|
||||
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
|
||||
int i;
|
||||
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
|
||||
link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
|
||||
if (irq_list[0] == -1)
|
||||
link->irq.IRQInfo2 = irq_mask;
|
||||
else
|
||||
for (i = 0; i < 4; i++)
|
||||
link->irq.IRQInfo2 |= 1 << irq_list[i];
|
||||
link->irq.Handler = prism2_interrupt;
|
||||
link->irq.Instance = dev;
|
||||
CS_CHECK(RequestIRQ,
|
||||
pcmcia_request_irq(link->handle, &link->irq));
|
||||
}
|
||||
|
||||
/*
|
||||
* This actually configures the PCMCIA socket -- setting up
|
||||
* the I/O windows and the interrupt mapping, and putting the
|
||||
* card and host interface into "Memory and IO" mode.
|
||||
*/
|
||||
CS_CHECK(RequestConfiguration,
|
||||
pcmcia_request_configuration(link->handle, &link->conf));
|
||||
|
||||
dev->irq = link->irq.AssignedIRQ;
|
||||
dev->base_addr = link->io.BasePort1;
|
||||
|
||||
/* Finally, report what we've done */
|
||||
printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
|
||||
dev_info, link->conf.ConfigIndex,
|
||||
link->conf.Vcc / 10, link->conf.Vcc % 10);
|
||||
if (link->conf.Vpp1)
|
||||
printk(", Vpp %d.%d", link->conf.Vpp1 / 10,
|
||||
link->conf.Vpp1 % 10);
|
||||
if (link->conf.Attributes & CONF_ENABLE_IRQ)
|
||||
printk(", irq %d", link->irq.AssignedIRQ);
|
||||
if (link->io.NumPorts1)
|
||||
printk(", io 0x%04x-0x%04x", link->io.BasePort1,
|
||||
link->io.BasePort1+link->io.NumPorts1-1);
|
||||
if (link->io.NumPorts2)
|
||||
printk(" & 0x%04x-0x%04x", link->io.BasePort2,
|
||||
link->io.BasePort2+link->io.NumPorts2-1);
|
||||
printk("\n");
|
||||
|
||||
link->state |= DEV_CONFIG;
|
||||
link->state &= ~DEV_CONFIG_PENDING;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
local->link = link;
|
||||
strcpy(local->node.dev_name, dev->name);
|
||||
link->dev = &local->node;
|
||||
|
||||
local->shutdown = 0;
|
||||
|
||||
sandisk_enable_wireless(dev);
|
||||
|
||||
ret = prism2_hw_config(dev, 1);
|
||||
if (!ret) {
|
||||
ret = hostap_hw_ready(dev);
|
||||
if (ret == 0 && local->ddev)
|
||||
strcpy(local->node.dev_name, local->ddev->name);
|
||||
}
|
||||
kfree(parse);
|
||||
return ret;
|
||||
|
||||
cs_failed:
|
||||
cs_error(link->handle, last_fn, last_ret);
|
||||
|
||||
failed:
|
||||
kfree(parse);
|
||||
prism2_release((u_long)link);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void prism2_release(u_long arg)
|
||||
{
|
||||
dev_link_t *link = (dev_link_t *)arg;
|
||||
|
||||
PDEBUG(DEBUG_FLOW, "prism2_release\n");
|
||||
|
||||
if (link->priv) {
|
||||
struct net_device *dev = link->priv;
|
||||
struct hostap_interface *iface;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
if (link->state & DEV_CONFIG)
|
||||
prism2_hw_shutdown(dev, 0);
|
||||
iface->local->shutdown = 1;
|
||||
}
|
||||
|
||||
if (link->win)
|
||||
pcmcia_release_window(link->win);
|
||||
pcmcia_release_configuration(link->handle);
|
||||
if (link->io.NumPorts1)
|
||||
pcmcia_release_io(link->handle, &link->io);
|
||||
if (link->irq.AssignedIRQ)
|
||||
pcmcia_release_irq(link->handle, &link->irq);
|
||||
|
||||
link->state &= ~DEV_CONFIG;
|
||||
|
||||
PDEBUG(DEBUG_FLOW, "release - done\n");
|
||||
}
|
||||
|
||||
|
||||
static int prism2_event(event_t event, int priority,
|
||||
event_callback_args_t *args)
|
||||
{
|
||||
dev_link_t *link = args->client_data;
|
||||
struct net_device *dev = (struct net_device *) link->priv;
|
||||
|
||||
switch (event) {
|
||||
case CS_EVENT_CARD_INSERTION:
|
||||
PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_INSERTION\n", dev_info);
|
||||
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
|
||||
if (prism2_config(link)) {
|
||||
PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case CS_EVENT_CARD_REMOVAL:
|
||||
PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_REMOVAL\n", dev_info);
|
||||
link->state &= ~DEV_PRESENT;
|
||||
if (link->state & DEV_CONFIG) {
|
||||
netif_stop_queue(dev);
|
||||
netif_device_detach(dev);
|
||||
prism2_release((u_long) link);
|
||||
}
|
||||
break;
|
||||
|
||||
case CS_EVENT_PM_SUSPEND:
|
||||
PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info);
|
||||
link->state |= DEV_SUSPEND;
|
||||
/* fall through */
|
||||
|
||||
case CS_EVENT_RESET_PHYSICAL:
|
||||
PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_RESET_PHYSICAL\n", dev_info);
|
||||
if (link->state & DEV_CONFIG) {
|
||||
if (link->open) {
|
||||
netif_stop_queue(dev);
|
||||
netif_device_detach(dev);
|
||||
}
|
||||
prism2_suspend(dev);
|
||||
pcmcia_release_configuration(link->handle);
|
||||
}
|
||||
break;
|
||||
|
||||
case CS_EVENT_PM_RESUME:
|
||||
PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info);
|
||||
link->state &= ~DEV_SUSPEND;
|
||||
/* fall through */
|
||||
|
||||
case CS_EVENT_CARD_RESET:
|
||||
PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_RESET\n", dev_info);
|
||||
if (link->state & DEV_CONFIG) {
|
||||
pcmcia_request_configuration(link->handle,
|
||||
&link->conf);
|
||||
prism2_hw_shutdown(dev, 1);
|
||||
prism2_hw_config(dev, link->open ? 0 : 1);
|
||||
if (link->open) {
|
||||
netif_device_attach(dev);
|
||||
netif_start_queue(dev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
PDEBUG(DEBUG_EXTRA, "%s: prism2_event() - unknown event %d\n",
|
||||
dev_info, event);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct pcmcia_driver hostap_driver = {
|
||||
.drv = {
|
||||
.name = "hostap_cs",
|
||||
},
|
||||
.attach = prism2_attach,
|
||||
.detach = prism2_detach,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init init_prism2_pccard(void)
|
||||
{
|
||||
printk(KERN_INFO "%s: %s\n", dev_info, version);
|
||||
return pcmcia_register_driver(&hostap_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_prism2_pccard(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&hostap_driver);
|
||||
printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
|
||||
}
|
||||
|
||||
|
||||
module_init(init_prism2_pccard);
|
||||
module_exit(exit_prism2_pccard);
|
766
drivers/net/wireless/hostap/hostap_download.c
Normal file
766
drivers/net/wireless/hostap/hostap_download.c
Normal file
@ -0,0 +1,766 @@
|
||||
static int prism2_enable_aux_port(struct net_device *dev, int enable)
|
||||
{
|
||||
u16 val, reg;
|
||||
int i, tries;
|
||||
unsigned long flags;
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
if (local->no_pri) {
|
||||
if (enable) {
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux "
|
||||
"port is already enabled\n", dev->name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&local->cmdlock, flags);
|
||||
|
||||
/* wait until busy bit is clear */
|
||||
tries = HFA384X_CMD_BUSY_TIMEOUT;
|
||||
while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
|
||||
tries--;
|
||||
udelay(1);
|
||||
}
|
||||
if (tries == 0) {
|
||||
reg = HFA384X_INW(HFA384X_CMD_OFF);
|
||||
spin_unlock_irqrestore(&local->cmdlock, flags);
|
||||
printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n",
|
||||
dev->name, reg);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
val = HFA384X_INW(HFA384X_CONTROL_OFF);
|
||||
|
||||
if (enable) {
|
||||
HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF);
|
||||
HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF);
|
||||
HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF);
|
||||
|
||||
if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED)
|
||||
printk("prism2_enable_aux_port: was not disabled!?\n");
|
||||
val &= ~HFA384X_AUX_PORT_MASK;
|
||||
val |= HFA384X_AUX_PORT_ENABLE;
|
||||
} else {
|
||||
HFA384X_OUTW(0, HFA384X_PARAM0_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
|
||||
if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED)
|
||||
printk("prism2_enable_aux_port: was not enabled!?\n");
|
||||
val &= ~HFA384X_AUX_PORT_MASK;
|
||||
val |= HFA384X_AUX_PORT_DISABLE;
|
||||
}
|
||||
HFA384X_OUTW(val, HFA384X_CONTROL_OFF);
|
||||
|
||||
udelay(5);
|
||||
|
||||
i = 10000;
|
||||
while (i > 0) {
|
||||
val = HFA384X_INW(HFA384X_CONTROL_OFF);
|
||||
val &= HFA384X_AUX_PORT_MASK;
|
||||
|
||||
if ((enable && val == HFA384X_AUX_PORT_ENABLED) ||
|
||||
(!enable && val == HFA384X_AUX_PORT_DISABLED))
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
i--;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&local->cmdlock, flags);
|
||||
|
||||
if (i == 0) {
|
||||
printk("prism2_enable_aux_port(%d) timed out\n",
|
||||
enable);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len,
|
||||
void *buf)
|
||||
{
|
||||
u16 page, offset;
|
||||
if (addr & 1 || len & 1)
|
||||
return -1;
|
||||
|
||||
page = addr >> 7;
|
||||
offset = addr & 0x7f;
|
||||
|
||||
HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
|
||||
HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
|
||||
|
||||
udelay(5);
|
||||
|
||||
#ifdef PRISM2_PCI
|
||||
{
|
||||
u16 *pos = (u16 *) buf;
|
||||
while (len > 0) {
|
||||
*pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF);
|
||||
len -= 2;
|
||||
}
|
||||
}
|
||||
#else /* PRISM2_PCI */
|
||||
HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2);
|
||||
#endif /* PRISM2_PCI */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len,
|
||||
void *buf)
|
||||
{
|
||||
u16 page, offset;
|
||||
if (addr & 1 || len & 1)
|
||||
return -1;
|
||||
|
||||
page = addr >> 7;
|
||||
offset = addr & 0x7f;
|
||||
|
||||
HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
|
||||
HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
|
||||
|
||||
udelay(5);
|
||||
|
||||
#ifdef PRISM2_PCI
|
||||
{
|
||||
u16 *pos = (u16 *) buf;
|
||||
while (len > 0) {
|
||||
HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF);
|
||||
len -= 2;
|
||||
}
|
||||
}
|
||||
#else /* PRISM2_PCI */
|
||||
HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2);
|
||||
#endif /* PRISM2_PCI */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_pda_ok(u8 *buf)
|
||||
{
|
||||
u16 *pda = (u16 *) buf;
|
||||
int pos;
|
||||
u16 len, pdr;
|
||||
|
||||
if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff &&
|
||||
buf[3] == 0x00)
|
||||
return 0;
|
||||
|
||||
pos = 0;
|
||||
while (pos + 1 < PRISM2_PDA_SIZE / 2) {
|
||||
len = le16_to_cpu(pda[pos]);
|
||||
pdr = le16_to_cpu(pda[pos + 1]);
|
||||
if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2)
|
||||
return 0;
|
||||
|
||||
if (pdr == 0x0000 && len == 2) {
|
||||
/* PDA end found */
|
||||
return 1;
|
||||
}
|
||||
|
||||
pos += len + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download_aux_dump(struct net_device *dev,
|
||||
unsigned int addr, int len, u8 *buf)
|
||||
{
|
||||
int res;
|
||||
|
||||
prism2_enable_aux_port(dev, 1);
|
||||
res = hfa384x_from_aux(dev, addr, len, buf);
|
||||
prism2_enable_aux_port(dev, 0);
|
||||
if (res)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static u8 * prism2_read_pda(struct net_device *dev)
|
||||
{
|
||||
u8 *buf;
|
||||
int res, i, found = 0;
|
||||
#define NUM_PDA_ADDRS 4
|
||||
unsigned int pda_addr[NUM_PDA_ADDRS] = {
|
||||
0x7f0000 /* others than HFA3841 */,
|
||||
0x3f0000 /* HFA3841 */,
|
||||
0x390000 /* apparently used in older cards */,
|
||||
0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */,
|
||||
};
|
||||
|
||||
buf = (u8 *) kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Note: wlan card should be in initial state (just after init cmd)
|
||||
* and no other operations should be performed concurrently. */
|
||||
|
||||
prism2_enable_aux_port(dev, 1);
|
||||
|
||||
for (i = 0; i < NUM_PDA_ADDRS; i++) {
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x",
|
||||
dev->name, pda_addr[i]);
|
||||
res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf);
|
||||
if (res)
|
||||
continue;
|
||||
if (res == 0 && prism2_pda_ok(buf)) {
|
||||
PDEBUG2(DEBUG_EXTRA2, ": OK\n");
|
||||
found = 1;
|
||||
break;
|
||||
} else {
|
||||
PDEBUG2(DEBUG_EXTRA2, ": failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
prism2_enable_aux_port(dev, 0);
|
||||
|
||||
if (!found) {
|
||||
printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name);
|
||||
kfree(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download_volatile(local_info_t *local,
|
||||
struct prism2_download_data *param)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
int ret = 0, i;
|
||||
u16 param0, param1;
|
||||
|
||||
if (local->hw_downloading) {
|
||||
printk(KERN_WARNING "%s: Already downloading - aborting new "
|
||||
"request\n", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
local->hw_downloading = 1;
|
||||
if (local->pri_only) {
|
||||
hfa384x_disable_interrupts(dev);
|
||||
} else {
|
||||
prism2_hw_shutdown(dev, 0);
|
||||
|
||||
if (prism2_hw_init(dev, 0)) {
|
||||
printk(KERN_WARNING "%s: Could not initialize card for"
|
||||
" download\n", dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (prism2_enable_aux_port(dev, 1)) {
|
||||
printk(KERN_WARNING "%s: Could not enable AUX port\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
param0 = param->start_addr & 0xffff;
|
||||
param1 = param->start_addr >> 16;
|
||||
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
|
||||
if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_ENABLE_VOLATILE << 8),
|
||||
param0)) {
|
||||
printk(KERN_WARNING "%s: Download command execution failed\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < param->num_areas; i++) {
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
|
||||
dev->name, param->data[i].len, param->data[i].addr);
|
||||
if (hfa384x_to_aux(dev, param->data[i].addr,
|
||||
param->data[i].len, param->data[i].data)) {
|
||||
printk(KERN_WARNING "%s: RAM download at 0x%08x "
|
||||
"(len=%d) failed\n", dev->name,
|
||||
param->data[i].addr, param->data[i].len);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_DISABLE << 8), param0)) {
|
||||
printk(KERN_WARNING "%s: Download command execution failed\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
/* ProgMode disable causes the hardware to restart itself from the
|
||||
* given starting address. Give hw some time and ACK command just in
|
||||
* case restart did not happen. */
|
||||
mdelay(5);
|
||||
HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
|
||||
|
||||
if (prism2_enable_aux_port(dev, 0)) {
|
||||
printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
|
||||
dev->name);
|
||||
/* continue anyway.. restart should have taken care of this */
|
||||
}
|
||||
|
||||
mdelay(5);
|
||||
local->hw_downloading = 0;
|
||||
if (prism2_hw_config(dev, 2)) {
|
||||
printk(KERN_WARNING "%s: Card configuration after RAM "
|
||||
"download failed\n", dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
local->hw_downloading = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_enable_genesis(local_info_t *local, int hcr)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff };
|
||||
u8 readbuf[4];
|
||||
|
||||
printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n",
|
||||
dev->name, hcr);
|
||||
local->func->cor_sreset(local);
|
||||
hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
|
||||
local->func->genesis_reset(local, hcr);
|
||||
|
||||
/* Readback test */
|
||||
hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
|
||||
hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
|
||||
hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
|
||||
|
||||
if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) {
|
||||
printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n",
|
||||
hcr);
|
||||
return 0;
|
||||
} else {
|
||||
printk(KERN_DEBUG "Readback test failed, HCR 0x%02x "
|
||||
"write %02x %02x %02x %02x read %02x %02x %02x %02x\n",
|
||||
hcr, initseq[0], initseq[1], initseq[2], initseq[3],
|
||||
readbuf[0], readbuf[1], readbuf[2], readbuf[3]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int prism2_get_ram_size(local_info_t *local)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */
|
||||
if (prism2_enable_genesis(local, 0x1f) == 0)
|
||||
ret = 8;
|
||||
else if (prism2_enable_genesis(local, 0x0f) == 0)
|
||||
ret = 16;
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
/* Disable genesis mode */
|
||||
local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download_genesis(local_info_t *local,
|
||||
struct prism2_download_data *param)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
int ram16 = 0, i;
|
||||
int ret = 0;
|
||||
|
||||
if (local->hw_downloading) {
|
||||
printk(KERN_WARNING "%s: Already downloading - aborting new "
|
||||
"request\n", dev->name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!local->func->genesis_reset || !local->func->cor_sreset) {
|
||||
printk(KERN_INFO "%s: Genesis mode downloading not supported "
|
||||
"with this hwmodel\n", dev->name);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
local->hw_downloading = 1;
|
||||
|
||||
if (prism2_enable_aux_port(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: failed to enable AUX port\n",
|
||||
dev->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->sram_type == -1) {
|
||||
/* 0x1F for x8 SRAM or 0x0F for x16 SRAM */
|
||||
if (prism2_enable_genesis(local, 0x1f) == 0) {
|
||||
ram16 = 0;
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 "
|
||||
"SRAM\n", dev->name);
|
||||
} else if (prism2_enable_genesis(local, 0x0f) == 0) {
|
||||
ram16 = 1;
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 "
|
||||
"SRAM\n", dev->name);
|
||||
} else {
|
||||
printk(KERN_DEBUG "%s: Could not initiate genesis "
|
||||
"mode\n", dev->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (prism2_enable_genesis(local, local->sram_type == 8 ?
|
||||
0x1f : 0x0f)) {
|
||||
printk(KERN_DEBUG "%s: Failed to set Genesis "
|
||||
"mode (sram_type=%d)\n", dev->name,
|
||||
local->sram_type);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
ram16 = local->sram_type != 8;
|
||||
}
|
||||
|
||||
for (i = 0; i < param->num_areas; i++) {
|
||||
PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
|
||||
dev->name, param->data[i].len, param->data[i].addr);
|
||||
if (hfa384x_to_aux(dev, param->data[i].addr,
|
||||
param->data[i].len, param->data[i].data)) {
|
||||
printk(KERN_WARNING "%s: RAM download at 0x%08x "
|
||||
"(len=%d) failed\n", dev->name,
|
||||
param->data[i].addr, param->data[i].len);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n");
|
||||
local->func->genesis_reset(local, ram16 ? 0x07 : 0x17);
|
||||
if (prism2_enable_aux_port(dev, 0)) {
|
||||
printk(KERN_DEBUG "%s: Failed to disable AUX port\n",
|
||||
dev->name);
|
||||
}
|
||||
|
||||
mdelay(5);
|
||||
local->hw_downloading = 0;
|
||||
|
||||
PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n");
|
||||
/*
|
||||
* Make sure the INIT command does not generate a command completion
|
||||
* event by disabling interrupts.
|
||||
*/
|
||||
hfa384x_disable_interrupts(dev);
|
||||
if (prism2_hw_init(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: Initialization after genesis mode "
|
||||
"download failed\n", dev->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n");
|
||||
if (prism2_hw_init2(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: Initialization(2) after genesis mode "
|
||||
"download failed\n", dev->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
local->hw_downloading = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef PRISM2_NON_VOLATILE_DOWNLOAD
|
||||
/* Note! Non-volatile downloading functionality has not yet been tested
|
||||
* thoroughly and it may corrupt flash image and effectively kill the card that
|
||||
* is being updated. You have been warned. */
|
||||
|
||||
static inline int prism2_download_block(struct net_device *dev,
|
||||
u32 addr, u8 *data,
|
||||
u32 bufaddr, int rest_len)
|
||||
{
|
||||
u16 param0, param1;
|
||||
int block_len;
|
||||
|
||||
block_len = rest_len < 4096 ? rest_len : 4096;
|
||||
|
||||
param0 = addr & 0xffff;
|
||||
param1 = addr >> 16;
|
||||
|
||||
HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF);
|
||||
HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
|
||||
|
||||
if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8),
|
||||
param0)) {
|
||||
printk(KERN_WARNING "%s: Flash download command execution "
|
||||
"failed\n", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hfa384x_to_aux(dev, bufaddr, block_len, data)) {
|
||||
printk(KERN_WARNING "%s: flash download at 0x%08x "
|
||||
"(len=%d) failed\n", dev->name, addr, block_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
|
||||
if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8),
|
||||
0)) {
|
||||
printk(KERN_WARNING "%s: Flash write command execution "
|
||||
"failed\n", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return block_len;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download_nonvolatile(local_info_t *local,
|
||||
struct prism2_download_data *dl)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
int ret = 0, i;
|
||||
struct {
|
||||
u16 page;
|
||||
u16 offset;
|
||||
u16 len;
|
||||
} dlbuffer;
|
||||
u32 bufaddr;
|
||||
|
||||
if (local->hw_downloading) {
|
||||
printk(KERN_WARNING "%s: Already downloading - aborting new "
|
||||
"request\n", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER,
|
||||
&dlbuffer, 6, 0);
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "%s: Could not read download buffer "
|
||||
"parameters\n", dev->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dlbuffer.page = le16_to_cpu(dlbuffer.page);
|
||||
dlbuffer.offset = le16_to_cpu(dlbuffer.offset);
|
||||
dlbuffer.len = le16_to_cpu(dlbuffer.len);
|
||||
|
||||
printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n",
|
||||
dlbuffer.len, dlbuffer.page, dlbuffer.offset);
|
||||
|
||||
bufaddr = (dlbuffer.page << 7) + dlbuffer.offset;
|
||||
|
||||
local->hw_downloading = 1;
|
||||
|
||||
if (!local->pri_only) {
|
||||
prism2_hw_shutdown(dev, 0);
|
||||
|
||||
if (prism2_hw_init(dev, 0)) {
|
||||
printk(KERN_WARNING "%s: Could not initialize card for"
|
||||
" download\n", dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
hfa384x_disable_interrupts(dev);
|
||||
|
||||
if (prism2_enable_aux_port(dev, 1)) {
|
||||
printk(KERN_WARNING "%s: Could not enable AUX port\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: starting flash download\n", dev->name);
|
||||
for (i = 0; i < dl->num_areas; i++) {
|
||||
int rest_len = dl->data[i].len;
|
||||
int data_off = 0;
|
||||
|
||||
while (rest_len > 0) {
|
||||
int block_len;
|
||||
|
||||
block_len = prism2_download_block(
|
||||
dev, dl->data[i].addr + data_off,
|
||||
dl->data[i].data + data_off, bufaddr,
|
||||
rest_len);
|
||||
|
||||
if (block_len < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rest_len -= block_len;
|
||||
data_off += block_len;
|
||||
}
|
||||
}
|
||||
|
||||
HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
|
||||
HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
|
||||
if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
|
||||
(HFA384X_PROGMODE_DISABLE << 8), 0)) {
|
||||
printk(KERN_WARNING "%s: Download command execution failed\n",
|
||||
dev->name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (prism2_enable_aux_port(dev, 0)) {
|
||||
printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
|
||||
dev->name);
|
||||
/* continue anyway.. restart should have taken care of this */
|
||||
}
|
||||
|
||||
mdelay(5);
|
||||
|
||||
local->func->hw_reset(dev);
|
||||
local->hw_downloading = 0;
|
||||
if (prism2_hw_config(dev, 2)) {
|
||||
printk(KERN_WARNING "%s: Card configuration after flash "
|
||||
"download failed\n", dev->name);
|
||||
ret = -1;
|
||||
} else {
|
||||
printk(KERN_INFO "%s: Card initialized successfully after "
|
||||
"flash download\n", dev->name);
|
||||
}
|
||||
|
||||
out:
|
||||
local->hw_downloading = 0;
|
||||
return ret;
|
||||
}
|
||||
#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
|
||||
|
||||
|
||||
static void prism2_download_free_data(struct prism2_download_data *dl)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dl == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < dl->num_areas; i++)
|
||||
kfree(dl->data[i].data);
|
||||
kfree(dl);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_download(local_info_t *local,
|
||||
struct prism2_download_param *param)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
u32 total_len = 0;
|
||||
struct prism2_download_data *dl = NULL;
|
||||
|
||||
printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x "
|
||||
"num_areas=%d\n",
|
||||
param->dl_cmd, param->start_addr, param->num_areas);
|
||||
|
||||
if (param->num_areas > 100) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dl = kmalloc(sizeof(*dl) + param->num_areas *
|
||||
sizeof(struct prism2_download_data_area), GFP_KERNEL);
|
||||
if (dl == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memset(dl, 0, sizeof(*dl) + param->num_areas *
|
||||
sizeof(struct prism2_download_data_area));
|
||||
dl->dl_cmd = param->dl_cmd;
|
||||
dl->start_addr = param->start_addr;
|
||||
dl->num_areas = param->num_areas;
|
||||
for (i = 0; i < param->num_areas; i++) {
|
||||
PDEBUG(DEBUG_EXTRA2,
|
||||
" area %d: addr=0x%08x len=%d ptr=0x%p\n",
|
||||
i, param->data[i].addr, param->data[i].len,
|
||||
param->data[i].ptr);
|
||||
|
||||
dl->data[i].addr = param->data[i].addr;
|
||||
dl->data[i].len = param->data[i].len;
|
||||
|
||||
total_len += param->data[i].len;
|
||||
if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN ||
|
||||
total_len > PRISM2_MAX_DOWNLOAD_LEN) {
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL);
|
||||
if (dl->data[i].data == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_from_user(dl->data[i].data, param->data[i].ptr,
|
||||
param->data[i].len)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
switch (param->dl_cmd) {
|
||||
case PRISM2_DOWNLOAD_VOLATILE:
|
||||
case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT:
|
||||
ret = prism2_download_volatile(local, dl);
|
||||
break;
|
||||
case PRISM2_DOWNLOAD_VOLATILE_GENESIS:
|
||||
case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT:
|
||||
ret = prism2_download_genesis(local, dl);
|
||||
break;
|
||||
case PRISM2_DOWNLOAD_NON_VOLATILE:
|
||||
#ifdef PRISM2_NON_VOLATILE_DOWNLOAD
|
||||
ret = prism2_download_nonvolatile(local, dl);
|
||||
#else /* PRISM2_NON_VOLATILE_DOWNLOAD */
|
||||
printk(KERN_INFO "%s: non-volatile downloading not enabled\n",
|
||||
local->dev->name);
|
||||
ret = -EOPNOTSUPP;
|
||||
#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
|
||||
break;
|
||||
default:
|
||||
printk(KERN_DEBUG "%s: unsupported download command %d\n",
|
||||
local->dev->name, param->dl_cmd);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
};
|
||||
|
||||
out:
|
||||
if (ret == 0 && dl &&
|
||||
param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) {
|
||||
prism2_download_free_data(local->dl_pri);
|
||||
local->dl_pri = dl;
|
||||
} else if (ret == 0 && dl &&
|
||||
param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) {
|
||||
prism2_download_free_data(local->dl_sec);
|
||||
local->dl_sec = dl;
|
||||
} else
|
||||
prism2_download_free_data(dl);
|
||||
|
||||
return ret;
|
||||
}
|
3631
drivers/net/wireless/hostap/hostap_hw.c
Normal file
3631
drivers/net/wireless/hostap/hostap_hw.c
Normal file
File diff suppressed because it is too large
Load Diff
478
drivers/net/wireless/hostap/hostap_info.c
Normal file
478
drivers/net/wireless/hostap/hostap_info.c
Normal file
@ -0,0 +1,478 @@
|
||||
/* Host AP driver Info Frame processing (part of hostap.o module) */
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_commtallies16(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
struct hfa384x_comm_tallies *tallies;
|
||||
|
||||
if (left < sizeof(struct hfa384x_comm_tallies)) {
|
||||
printk(KERN_DEBUG "%s: too short (len=%d) commtallies "
|
||||
"info frame\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
tallies = (struct hfa384x_comm_tallies *) buf;
|
||||
#define ADD_COMM_TALLIES(name) \
|
||||
local->comm_tallies.name += le16_to_cpu(tallies->name)
|
||||
ADD_COMM_TALLIES(tx_unicast_frames);
|
||||
ADD_COMM_TALLIES(tx_multicast_frames);
|
||||
ADD_COMM_TALLIES(tx_fragments);
|
||||
ADD_COMM_TALLIES(tx_unicast_octets);
|
||||
ADD_COMM_TALLIES(tx_multicast_octets);
|
||||
ADD_COMM_TALLIES(tx_deferred_transmissions);
|
||||
ADD_COMM_TALLIES(tx_single_retry_frames);
|
||||
ADD_COMM_TALLIES(tx_multiple_retry_frames);
|
||||
ADD_COMM_TALLIES(tx_retry_limit_exceeded);
|
||||
ADD_COMM_TALLIES(tx_discards);
|
||||
ADD_COMM_TALLIES(rx_unicast_frames);
|
||||
ADD_COMM_TALLIES(rx_multicast_frames);
|
||||
ADD_COMM_TALLIES(rx_fragments);
|
||||
ADD_COMM_TALLIES(rx_unicast_octets);
|
||||
ADD_COMM_TALLIES(rx_multicast_octets);
|
||||
ADD_COMM_TALLIES(rx_fcs_errors);
|
||||
ADD_COMM_TALLIES(rx_discards_no_buffer);
|
||||
ADD_COMM_TALLIES(tx_discards_wrong_sa);
|
||||
ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
|
||||
ADD_COMM_TALLIES(rx_message_in_msg_fragments);
|
||||
ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
|
||||
#undef ADD_COMM_TALLIES
|
||||
}
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_commtallies32(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
struct hfa384x_comm_tallies32 *tallies;
|
||||
|
||||
if (left < sizeof(struct hfa384x_comm_tallies32)) {
|
||||
printk(KERN_DEBUG "%s: too short (len=%d) commtallies32 "
|
||||
"info frame\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
tallies = (struct hfa384x_comm_tallies32 *) buf;
|
||||
#define ADD_COMM_TALLIES(name) \
|
||||
local->comm_tallies.name += le32_to_cpu(tallies->name)
|
||||
ADD_COMM_TALLIES(tx_unicast_frames);
|
||||
ADD_COMM_TALLIES(tx_multicast_frames);
|
||||
ADD_COMM_TALLIES(tx_fragments);
|
||||
ADD_COMM_TALLIES(tx_unicast_octets);
|
||||
ADD_COMM_TALLIES(tx_multicast_octets);
|
||||
ADD_COMM_TALLIES(tx_deferred_transmissions);
|
||||
ADD_COMM_TALLIES(tx_single_retry_frames);
|
||||
ADD_COMM_TALLIES(tx_multiple_retry_frames);
|
||||
ADD_COMM_TALLIES(tx_retry_limit_exceeded);
|
||||
ADD_COMM_TALLIES(tx_discards);
|
||||
ADD_COMM_TALLIES(rx_unicast_frames);
|
||||
ADD_COMM_TALLIES(rx_multicast_frames);
|
||||
ADD_COMM_TALLIES(rx_fragments);
|
||||
ADD_COMM_TALLIES(rx_unicast_octets);
|
||||
ADD_COMM_TALLIES(rx_multicast_octets);
|
||||
ADD_COMM_TALLIES(rx_fcs_errors);
|
||||
ADD_COMM_TALLIES(rx_discards_no_buffer);
|
||||
ADD_COMM_TALLIES(tx_discards_wrong_sa);
|
||||
ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
|
||||
ADD_COMM_TALLIES(rx_message_in_msg_fragments);
|
||||
ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
|
||||
#undef ADD_COMM_TALLIES
|
||||
}
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_commtallies(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
if (local->tallies32)
|
||||
prism2_info_commtallies32(local, buf, left);
|
||||
else
|
||||
prism2_info_commtallies16(local, buf, left);
|
||||
}
|
||||
|
||||
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
#ifndef PRISM2_NO_DEBUG
|
||||
static const char* hfa384x_linkstatus_str(u16 linkstatus)
|
||||
{
|
||||
switch (linkstatus) {
|
||||
case HFA384X_LINKSTATUS_CONNECTED:
|
||||
return "Connected";
|
||||
case HFA384X_LINKSTATUS_DISCONNECTED:
|
||||
return "Disconnected";
|
||||
case HFA384X_LINKSTATUS_AP_CHANGE:
|
||||
return "Access point change";
|
||||
case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE:
|
||||
return "Access point out of range";
|
||||
case HFA384X_LINKSTATUS_AP_IN_RANGE:
|
||||
return "Access point in range";
|
||||
case HFA384X_LINKSTATUS_ASSOC_FAILED:
|
||||
return "Association failed";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
#endif /* PRISM2_NO_DEBUG */
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
u16 val;
|
||||
int non_sta_mode;
|
||||
|
||||
/* Alloc new JoinRequests to occur since LinkStatus for the previous
|
||||
* has been received */
|
||||
local->last_join_time = 0;
|
||||
|
||||
if (left != 2) {
|
||||
printk(KERN_DEBUG "%s: invalid linkstatus info frame "
|
||||
"length %d\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
non_sta_mode = local->iw_mode == IW_MODE_MASTER ||
|
||||
local->iw_mode == IW_MODE_REPEAT ||
|
||||
local->iw_mode == IW_MODE_MONITOR;
|
||||
|
||||
val = buf[0] | (buf[1] << 8);
|
||||
if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) {
|
||||
PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n",
|
||||
local->dev->name, val, hfa384x_linkstatus_str(val));
|
||||
}
|
||||
|
||||
if (non_sta_mode) {
|
||||
netif_carrier_on(local->dev);
|
||||
netif_carrier_on(local->ddev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get current BSSID later in scheduled task */
|
||||
set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info);
|
||||
local->prev_link_status = val;
|
||||
schedule_work(&local->info_queue);
|
||||
}
|
||||
|
||||
|
||||
static void prism2_host_roaming(local_info_t *local)
|
||||
{
|
||||
struct hfa384x_join_request req;
|
||||
struct net_device *dev = local->dev;
|
||||
struct hfa384x_scan_result *selected, *entry;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
if (local->last_join_time &&
|
||||
time_before(jiffies, local->last_join_time + 10 * HZ)) {
|
||||
PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been "
|
||||
"completed - waiting for it before issuing new one\n",
|
||||
dev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ScanResults are sorted: first ESS results in decreasing signal
|
||||
* quality then IBSS results in similar order.
|
||||
* Trivial roaming policy: just select the first entry.
|
||||
* This could probably be improved by adding hysteresis to limit
|
||||
* number of handoffs, etc.
|
||||
*
|
||||
* Could do periodic RID_SCANREQUEST or Inquire F101 to get new
|
||||
* ScanResults */
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
if (local->last_scan_results == NULL ||
|
||||
local->last_scan_results_count == 0) {
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n",
|
||||
dev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
selected = &local->last_scan_results[0];
|
||||
|
||||
if (local->preferred_ap[0] || local->preferred_ap[1] ||
|
||||
local->preferred_ap[2] || local->preferred_ap[3] ||
|
||||
local->preferred_ap[4] || local->preferred_ap[5]) {
|
||||
/* Try to find preferred AP */
|
||||
PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID " MACSTR "\n",
|
||||
dev->name, MAC2STR(local->preferred_ap));
|
||||
for (i = 0; i < local->last_scan_results_count; i++) {
|
||||
entry = &local->last_scan_results[i];
|
||||
if (memcmp(local->preferred_ap, entry->bssid, 6) == 0)
|
||||
{
|
||||
PDEBUG(DEBUG_EXTRA, "%s: using preferred AP "
|
||||
"selection\n", dev->name);
|
||||
selected = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(req.bssid, selected->bssid, 6);
|
||||
req.channel = selected->chid;
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
|
||||
PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=" MACSTR " channel=%d\n",
|
||||
dev->name, MAC2STR(req.bssid), le16_to_cpu(req.channel));
|
||||
if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req,
|
||||
sizeof(req))) {
|
||||
printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name);
|
||||
}
|
||||
local->last_join_time = jiffies;
|
||||
}
|
||||
|
||||
|
||||
static void hostap_report_scan_complete(local_info_t *local)
|
||||
{
|
||||
union iwreq_data wrqu;
|
||||
|
||||
/* Inform user space about new scan results (just empty event,
|
||||
* SIOCGIWSCAN can be used to fetch data */
|
||||
wrqu.data.length = 0;
|
||||
wrqu.data.flags = 0;
|
||||
wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL);
|
||||
|
||||
/* Allow SIOCGIWSCAN handling to occur since we have received
|
||||
* scanning result */
|
||||
local->scan_timestamp = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_scanresults(local_info_t *local, unsigned char *buf,
|
||||
int left)
|
||||
{
|
||||
u16 *pos;
|
||||
int new_count;
|
||||
unsigned long flags;
|
||||
struct hfa384x_scan_result *results, *prev;
|
||||
|
||||
if (left < 4) {
|
||||
printk(KERN_DEBUG "%s: invalid scanresult info frame "
|
||||
"length %d\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
pos = (u16 *) buf;
|
||||
pos++;
|
||||
pos++;
|
||||
left -= 4;
|
||||
|
||||
new_count = left / sizeof(struct hfa384x_scan_result);
|
||||
results = kmalloc(new_count * sizeof(struct hfa384x_scan_result),
|
||||
GFP_ATOMIC);
|
||||
if (results == NULL)
|
||||
return;
|
||||
memcpy(results, pos, new_count * sizeof(struct hfa384x_scan_result));
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
local->last_scan_type = PRISM2_SCAN;
|
||||
prev = local->last_scan_results;
|
||||
local->last_scan_results = results;
|
||||
local->last_scan_results_count = new_count;
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
kfree(prev);
|
||||
|
||||
hostap_report_scan_complete(local);
|
||||
|
||||
/* Perform rest of ScanResults handling later in scheduled task */
|
||||
set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info);
|
||||
schedule_work(&local->info_queue);
|
||||
}
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
static void prism2_info_hostscanresults(local_info_t *local,
|
||||
unsigned char *buf, int left)
|
||||
{
|
||||
int i, result_size, copy_len, new_count;
|
||||
struct hfa384x_hostscan_result *results, *prev;
|
||||
unsigned long flags;
|
||||
u16 *pos;
|
||||
u8 *ptr;
|
||||
|
||||
wake_up_interruptible(&local->hostscan_wq);
|
||||
|
||||
if (left < 4) {
|
||||
printk(KERN_DEBUG "%s: invalid hostscanresult info frame "
|
||||
"length %d\n", local->dev->name, left);
|
||||
return;
|
||||
}
|
||||
|
||||
pos = (u16 *) buf;
|
||||
copy_len = result_size = le16_to_cpu(*pos);
|
||||
if (result_size == 0) {
|
||||
printk(KERN_DEBUG "%s: invalid result_size (0) in "
|
||||
"hostscanresults\n", local->dev->name);
|
||||
return;
|
||||
}
|
||||
if (copy_len > sizeof(struct hfa384x_hostscan_result))
|
||||
copy_len = sizeof(struct hfa384x_hostscan_result);
|
||||
|
||||
pos++;
|
||||
pos++;
|
||||
left -= 4;
|
||||
ptr = (u8 *) pos;
|
||||
|
||||
new_count = left / result_size;
|
||||
results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result),
|
||||
GFP_ATOMIC);
|
||||
if (results == NULL)
|
||||
return;
|
||||
memset(results, 0, new_count * sizeof(struct hfa384x_hostscan_result));
|
||||
|
||||
for (i = 0; i < new_count; i++) {
|
||||
memcpy(&results[i], ptr, copy_len);
|
||||
ptr += result_size;
|
||||
left -= result_size;
|
||||
}
|
||||
|
||||
if (left) {
|
||||
printk(KERN_DEBUG "%s: short HostScan result entry (%d/%d)\n",
|
||||
local->dev->name, left, result_size);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
local->last_scan_type = PRISM2_HOSTSCAN;
|
||||
prev = local->last_hostscan_results;
|
||||
local->last_hostscan_results = results;
|
||||
local->last_hostscan_results_count = new_count;
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
kfree(prev);
|
||||
|
||||
hostap_report_scan_complete(local);
|
||||
}
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
|
||||
|
||||
/* Called only as a tasklet (software IRQ) */
|
||||
void hostap_info_process(local_info_t *local, struct sk_buff *skb)
|
||||
{
|
||||
struct hfa384x_info_frame *info;
|
||||
unsigned char *buf;
|
||||
int left;
|
||||
#ifndef PRISM2_NO_DEBUG
|
||||
int i;
|
||||
#endif /* PRISM2_NO_DEBUG */
|
||||
|
||||
info = (struct hfa384x_info_frame *) skb->data;
|
||||
buf = skb->data + sizeof(*info);
|
||||
left = skb->len - sizeof(*info);
|
||||
|
||||
switch (info->type) {
|
||||
case HFA384X_INFO_COMMTALLIES:
|
||||
prism2_info_commtallies(local, buf, left);
|
||||
break;
|
||||
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
case HFA384X_INFO_LINKSTATUS:
|
||||
prism2_info_linkstatus(local, buf, left);
|
||||
break;
|
||||
|
||||
case HFA384X_INFO_SCANRESULTS:
|
||||
prism2_info_scanresults(local, buf, left);
|
||||
break;
|
||||
|
||||
case HFA384X_INFO_HOSTSCANRESULTS:
|
||||
prism2_info_hostscanresults(local, buf, left);
|
||||
break;
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
|
||||
#ifndef PRISM2_NO_DEBUG
|
||||
default:
|
||||
PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n",
|
||||
local->dev->name, info->len, info->type);
|
||||
PDEBUG(DEBUG_EXTRA, "Unknown info frame:");
|
||||
for (i = 0; i < (left < 100 ? left : 100); i++)
|
||||
PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]);
|
||||
PDEBUG2(DEBUG_EXTRA, "\n");
|
||||
break;
|
||||
#endif /* PRISM2_NO_DEBUG */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
static void handle_info_queue_linkstatus(local_info_t *local)
|
||||
{
|
||||
int val = local->prev_link_status;
|
||||
int connected;
|
||||
union iwreq_data wrqu;
|
||||
|
||||
connected =
|
||||
val == HFA384X_LINKSTATUS_CONNECTED ||
|
||||
val == HFA384X_LINKSTATUS_AP_CHANGE ||
|
||||
val == HFA384X_LINKSTATUS_AP_IN_RANGE;
|
||||
|
||||
if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID,
|
||||
local->bssid, ETH_ALEN, 1) < 0) {
|
||||
printk(KERN_DEBUG "%s: could not read CURRENTBSSID after "
|
||||
"LinkStatus event\n", local->dev->name);
|
||||
} else {
|
||||
PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=" MACSTR "\n",
|
||||
local->dev->name,
|
||||
MAC2STR((unsigned char *) local->bssid));
|
||||
if (local->wds_type & HOSTAP_WDS_AP_CLIENT)
|
||||
hostap_add_sta(local->ap, local->bssid);
|
||||
}
|
||||
|
||||
/* Get BSSID if we have a valid AP address */
|
||||
if (connected) {
|
||||
netif_carrier_on(local->dev);
|
||||
netif_carrier_on(local->ddev);
|
||||
memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN);
|
||||
} else {
|
||||
netif_carrier_off(local->dev);
|
||||
netif_carrier_off(local->ddev);
|
||||
memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
|
||||
}
|
||||
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
|
||||
|
||||
/*
|
||||
* Filter out sequential disconnect events in order not to cause a
|
||||
* flood of SIOCGIWAP events that have a race condition with EAPOL
|
||||
* frames and can confuse wpa_supplicant about the current association
|
||||
* status.
|
||||
*/
|
||||
if (connected || local->prev_linkstatus_connected)
|
||||
wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
|
||||
local->prev_linkstatus_connected = connected;
|
||||
}
|
||||
|
||||
|
||||
static void handle_info_queue_scanresults(local_info_t *local)
|
||||
{
|
||||
if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA)
|
||||
prism2_host_roaming(local);
|
||||
}
|
||||
|
||||
|
||||
/* Called only as scheduled task after receiving info frames (used to avoid
|
||||
* pending too much time in HW IRQ handler). */
|
||||
static void handle_info_queue(void *data)
|
||||
{
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
|
||||
if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS,
|
||||
&local->pending_info))
|
||||
handle_info_queue_linkstatus(local);
|
||||
|
||||
if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS,
|
||||
&local->pending_info))
|
||||
handle_info_queue_scanresults(local);
|
||||
}
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
|
||||
|
||||
void hostap_info_init(local_info_t *local)
|
||||
{
|
||||
skb_queue_head_init(&local->info_list);
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
INIT_WORK(&local->info_queue, handle_info_queue, local);
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(hostap_info_init);
|
||||
EXPORT_SYMBOL(hostap_info_process);
|
4123
drivers/net/wireless/hostap/hostap_ioctl.c
Normal file
4123
drivers/net/wireless/hostap/hostap_ioctl.c
Normal file
File diff suppressed because it is too large
Load Diff
453
drivers/net/wireless/hostap/hostap_pci.c
Normal file
453
drivers/net/wireless/hostap/hostap_pci.c
Normal file
@ -0,0 +1,453 @@
|
||||
#define PRISM2_PCI
|
||||
|
||||
/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on
|
||||
* driver patches from Reyk Floeter <reyk@vantronix.net> and
|
||||
* Andy Warner <andyw@pobox.com> */
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/iw_handler.h>
|
||||
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hostap_wlan.h"
|
||||
|
||||
|
||||
static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
|
||||
static char *dev_info = "hostap_pci";
|
||||
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN "
|
||||
"PCI cards.");
|
||||
MODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
/* FIX: do we need mb/wmb/rmb with memory operations? */
|
||||
|
||||
|
||||
static struct pci_device_id prism2_pci_id_table[] __devinitdata = {
|
||||
/* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */
|
||||
{ 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID },
|
||||
/* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */
|
||||
{ 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID },
|
||||
/* Samsung MagicLAN SWL-2210P */
|
||||
{ 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
|
||||
static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
|
||||
writeb(v, local->mem_start + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u8 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = readb(local->mem_start + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
|
||||
writew(v, local->mem_start + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u16 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = readw(local->mem_start + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
|
||||
#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
|
||||
#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
|
||||
#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
|
||||
#define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), cpu_to_le16((v)))
|
||||
#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw_debug(dev, (a)))
|
||||
|
||||
#else /* PRISM2_IO_DEBUG */
|
||||
|
||||
static inline void hfa384x_outb(struct net_device *dev, int a, u8 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
writeb(v, local->mem_start + a);
|
||||
}
|
||||
|
||||
static inline u8 hfa384x_inb(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
return readb(local->mem_start + a);
|
||||
}
|
||||
|
||||
static inline void hfa384x_outw(struct net_device *dev, int a, u16 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
writew(v, local->mem_start + a);
|
||||
}
|
||||
|
||||
static inline u16 hfa384x_inw(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
return readw(local->mem_start + a);
|
||||
}
|
||||
|
||||
#define HFA384X_OUTB(v,a) hfa384x_outb(dev, (a), (v))
|
||||
#define HFA384X_INB(a) hfa384x_inb(dev, (a))
|
||||
#define HFA384X_OUTW(v,a) hfa384x_outw(dev, (a), (v))
|
||||
#define HFA384X_INW(a) hfa384x_inw(dev, (a))
|
||||
#define HFA384X_OUTW_DATA(v,a) hfa384x_outw(dev, (a), cpu_to_le16((v)))
|
||||
#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw(dev, (a)))
|
||||
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
|
||||
|
||||
static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
|
||||
int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
for ( ; len > 1; len -= 2)
|
||||
*pos++ = HFA384X_INW_DATA(d_off);
|
||||
|
||||
if (len & 1)
|
||||
*((char *) pos) = HFA384X_INB(d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
for ( ; len > 1; len -= 2)
|
||||
HFA384X_OUTW_DATA(*pos++, d_off);
|
||||
|
||||
if (len & 1)
|
||||
HFA384X_OUTB(*((char *) pos), d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* FIX: This might change at some point.. */
|
||||
#include "hostap_hw.c"
|
||||
|
||||
static void prism2_pci_cor_sreset(local_info_t *local)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
u16 reg;
|
||||
|
||||
reg = HFA384X_INB(HFA384X_PCICOR_OFF);
|
||||
printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg);
|
||||
|
||||
/* linux-wlan-ng uses extremely long hold and settle times for
|
||||
* COR sreset. A comment in the driver code mentions that the long
|
||||
* delays appear to be necessary. However, at least IBM 22P6901 seems
|
||||
* to work fine with shorter delays.
|
||||
*
|
||||
* Longer delays can be configured by uncommenting following line: */
|
||||
/* #define PRISM2_PCI_USE_LONG_DELAYS */
|
||||
|
||||
#ifdef PRISM2_PCI_USE_LONG_DELAYS
|
||||
int i;
|
||||
|
||||
HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF);
|
||||
mdelay(250);
|
||||
|
||||
HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF);
|
||||
mdelay(500);
|
||||
|
||||
/* Wait for f/w to complete initialization (CMD:BUSY == 0) */
|
||||
i = 2000000 / 10;
|
||||
while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i)
|
||||
udelay(10);
|
||||
|
||||
#else /* PRISM2_PCI_USE_LONG_DELAYS */
|
||||
|
||||
HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF);
|
||||
mdelay(2);
|
||||
HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF);
|
||||
mdelay(2);
|
||||
|
||||
#endif /* PRISM2_PCI_USE_LONG_DELAYS */
|
||||
|
||||
if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) {
|
||||
printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void prism2_pci_genesis_reset(local_info_t *local, int hcr)
|
||||
{
|
||||
struct net_device *dev = local->dev;
|
||||
|
||||
HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF);
|
||||
mdelay(10);
|
||||
HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF);
|
||||
mdelay(10);
|
||||
HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF);
|
||||
mdelay(10);
|
||||
}
|
||||
|
||||
|
||||
static struct prism2_helper_functions prism2_pci_funcs =
|
||||
{
|
||||
.card_present = NULL,
|
||||
.cor_sreset = prism2_pci_cor_sreset,
|
||||
.dev_open = NULL,
|
||||
.dev_close = NULL,
|
||||
.genesis_reset = prism2_pci_genesis_reset,
|
||||
.hw_type = HOSTAP_HW_PCI,
|
||||
};
|
||||
|
||||
|
||||
static int prism2_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
unsigned long phymem;
|
||||
void __iomem *mem = NULL;
|
||||
local_info_t *local = NULL;
|
||||
struct net_device *dev = NULL;
|
||||
static int cards_found /* = 0 */;
|
||||
int irq_registered = 0;
|
||||
struct hostap_interface *iface;
|
||||
|
||||
if (pci_enable_device(pdev))
|
||||
return -EIO;
|
||||
|
||||
phymem = pci_resource_start(pdev, 0);
|
||||
|
||||
if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) {
|
||||
printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n");
|
||||
goto err_out_disable;
|
||||
}
|
||||
|
||||
mem = ioremap(phymem, pci_resource_len(pdev, 0));
|
||||
if (mem == NULL) {
|
||||
printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef PRISM2_BUS_MASTER
|
||||
pci_set_master(pdev);
|
||||
#endif /* PRISM2_BUS_MASTER */
|
||||
|
||||
dev = prism2_init_local_data(&prism2_pci_funcs, cards_found);
|
||||
if (dev == NULL)
|
||||
goto fail;
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
cards_found++;
|
||||
|
||||
dev->irq = pdev->irq;
|
||||
local->mem_start = mem;
|
||||
|
||||
prism2_pci_cor_sreset(local);
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name,
|
||||
dev)) {
|
||||
printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
|
||||
goto fail;
|
||||
} else
|
||||
irq_registered = 1;
|
||||
|
||||
if (!local->pri_only && prism2_hw_config(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: hardware initialization failed\n",
|
||||
dev_info);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: Intersil Prism2.5 PCI: "
|
||||
"mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq);
|
||||
|
||||
return hostap_hw_ready(dev);
|
||||
|
||||
fail:
|
||||
if (irq_registered && dev)
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
if (mem)
|
||||
iounmap(mem);
|
||||
|
||||
release_mem_region(phymem, pci_resource_len(pdev, 0));
|
||||
|
||||
err_out_disable:
|
||||
pci_disable_device(pdev);
|
||||
prism2_free_local_data(dev);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static void prism2_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct hostap_interface *iface;
|
||||
void __iomem *mem_start;
|
||||
|
||||
dev = pci_get_drvdata(pdev);
|
||||
iface = netdev_priv(dev);
|
||||
|
||||
/* Reset the hardware, and ensure interrupts are disabled. */
|
||||
prism2_pci_cor_sreset(iface->local);
|
||||
hfa384x_disable_interrupts(dev);
|
||||
|
||||
if (dev->irq)
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
mem_start = iface->local->mem_start;
|
||||
prism2_free_local_data(dev);
|
||||
|
||||
iounmap(mem_start);
|
||||
|
||||
release_mem_region(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int prism2_pci_suspend(struct pci_dev *pdev, u32 state)
|
||||
{
|
||||
struct net_device *dev = pci_get_drvdata(pdev);
|
||||
|
||||
if (netif_running(dev)) {
|
||||
netif_stop_queue(dev);
|
||||
netif_device_detach(dev);
|
||||
}
|
||||
prism2_suspend(dev);
|
||||
pci_save_state(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prism2_pci_resume(struct pci_dev *pdev)
|
||||
{
|
||||
struct net_device *dev = pci_get_drvdata(pdev);
|
||||
|
||||
pci_enable_device(pdev);
|
||||
pci_restore_state(pdev);
|
||||
prism2_hw_config(dev, 0);
|
||||
if (netif_running(dev)) {
|
||||
netif_device_attach(dev);
|
||||
netif_start_queue(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, prism2_pci_id_table);
|
||||
|
||||
static struct pci_driver prism2_pci_drv_id = {
|
||||
.name = "prism2_pci",
|
||||
.id_table = prism2_pci_id_table,
|
||||
.probe = prism2_pci_probe,
|
||||
.remove = prism2_pci_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = prism2_pci_suspend,
|
||||
.resume = prism2_pci_resume,
|
||||
#endif /* CONFIG_PM */
|
||||
/* Linux 2.4.6 added save_state and enable_wake that are not used here
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
static int __init init_prism2_pci(void)
|
||||
{
|
||||
printk(KERN_INFO "%s: %s\n", dev_info, version);
|
||||
|
||||
return pci_register_driver(&prism2_pci_drv_id);
|
||||
}
|
||||
|
||||
|
||||
static void __exit exit_prism2_pci(void)
|
||||
{
|
||||
pci_unregister_driver(&prism2_pci_drv_id);
|
||||
printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
|
||||
}
|
||||
|
||||
|
||||
module_init(init_prism2_pci);
|
||||
module_exit(exit_prism2_pci);
|
620
drivers/net/wireless/hostap/hostap_plx.c
Normal file
620
drivers/net/wireless/hostap/hostap_plx.c
Normal file
@ -0,0 +1,620 @@
|
||||
#define PRISM2_PLX
|
||||
|
||||
/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is
|
||||
* based on:
|
||||
* - Host AP driver patch from james@madingley.org
|
||||
* - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/iw_handler.h>
|
||||
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hostap_wlan.h"
|
||||
|
||||
|
||||
static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)";
|
||||
static char *dev_info = "hostap_plx";
|
||||
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN "
|
||||
"cards (PLX).");
|
||||
MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
static int ignore_cis;
|
||||
module_param(ignore_cis, int, 0444);
|
||||
MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS");
|
||||
|
||||
|
||||
#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */
|
||||
#define COR_SRESET 0x80
|
||||
#define COR_LEVLREQ 0x40
|
||||
#define COR_ENABLE_FUNC 0x01
|
||||
/* PCI Configuration Registers */
|
||||
#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */
|
||||
/* Local Configuration Registers */
|
||||
#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */
|
||||
#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */
|
||||
#define PLX_CNTRL 0x50
|
||||
#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28)
|
||||
|
||||
|
||||
#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID }
|
||||
|
||||
static struct pci_device_id prism2_plx_id_table[] __devinitdata = {
|
||||
PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
|
||||
PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
|
||||
PLXDEV(0x126c, 0x8030, "Nortel emobility"),
|
||||
PLXDEV(0x1385, 0x4100, "Netgear MA301"),
|
||||
PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"),
|
||||
PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"),
|
||||
PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"),
|
||||
PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"),
|
||||
PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"),
|
||||
PLXDEV(0x16ab, 0x1103, "Longshine 8031"),
|
||||
PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"),
|
||||
PLXDEV(0xec80, 0xec00, "Belkin F5D6000"),
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid
|
||||
* is not listed here, you will need to add it here to get the driver
|
||||
* initialized. */
|
||||
static struct prism2_plx_manfid {
|
||||
u16 manfid1, manfid2;
|
||||
} prism2_plx_known_manfids[] = {
|
||||
{ 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */,
|
||||
{ 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */,
|
||||
{ 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */,
|
||||
{ 0x0126, 0x8000 } /* Proxim RangeLAN */,
|
||||
{ 0x0138, 0x0002 } /* Compaq WL100 */,
|
||||
{ 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */,
|
||||
{ 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */,
|
||||
{ 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */,
|
||||
{ 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */,
|
||||
{ 0x028a, 0x0002 } /* D-Link DRC-650 */,
|
||||
{ 0x0250, 0x0002 } /* Samsung SWL2000-N */,
|
||||
{ 0xc250, 0x0002 } /* EMTAC A2424i */,
|
||||
{ 0xd601, 0x0002 } /* Z-Com XI300 */,
|
||||
{ 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */,
|
||||
{ 0, 0}
|
||||
};
|
||||
|
||||
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
|
||||
static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
|
||||
outb(v, dev->base_addr + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u8 hfa384x_inb_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u8 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = inb(dev->base_addr + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
|
||||
outw(v, dev->base_addr + a);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline u16 hfa384x_inw_debug(struct net_device *dev, int a)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
u16 v;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
v = inw(dev->base_addr + a);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void hfa384x_outsw_debug(struct net_device *dev, int a,
|
||||
u8 *buf, int wc)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc);
|
||||
outsw(dev->base_addr + a, buf, wc);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
static inline void hfa384x_insw_debug(struct net_device *dev, int a,
|
||||
u8 *buf, int wc)
|
||||
{
|
||||
struct hostap_interface *iface;
|
||||
local_info_t *local;
|
||||
unsigned long flags;
|
||||
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
|
||||
spin_lock_irqsave(&local->lock, flags);
|
||||
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc);
|
||||
insw(dev->base_addr + a, buf, wc);
|
||||
spin_unlock_irqrestore(&local->lock, flags);
|
||||
}
|
||||
|
||||
#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v))
|
||||
#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a))
|
||||
#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v))
|
||||
#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a))
|
||||
#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc))
|
||||
#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc))
|
||||
|
||||
#else /* PRISM2_IO_DEBUG */
|
||||
|
||||
#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a))
|
||||
#define HFA384X_INB(a) inb(dev->base_addr + (a))
|
||||
#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a))
|
||||
#define HFA384X_INW(a) inw(dev->base_addr + (a))
|
||||
#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc)
|
||||
#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc)
|
||||
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
|
||||
|
||||
static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf,
|
||||
int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
if (len / 2)
|
||||
HFA384X_INSW(d_off, buf, len / 2);
|
||||
pos += len / 2;
|
||||
|
||||
if (len & 1)
|
||||
*((char *) pos) = HFA384X_INB(d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len)
|
||||
{
|
||||
u16 d_off;
|
||||
u16 *pos;
|
||||
|
||||
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
|
||||
pos = (u16 *) buf;
|
||||
|
||||
if (len / 2)
|
||||
HFA384X_OUTSW(d_off, buf, len / 2);
|
||||
pos += len / 2;
|
||||
|
||||
if (len & 1)
|
||||
HFA384X_OUTB(*((char *) pos), d_off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* FIX: This might change at some point.. */
|
||||
#include "hostap_hw.c"
|
||||
|
||||
|
||||
static void prism2_plx_cor_sreset(local_info_t *local)
|
||||
{
|
||||
unsigned char corsave;
|
||||
|
||||
printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n",
|
||||
dev_info);
|
||||
|
||||
/* Set sreset bit of COR and clear it after hold time */
|
||||
|
||||
if (local->attr_mem == NULL) {
|
||||
/* TMD7160 - COR at card's first I/O addr */
|
||||
corsave = inb(local->cor_offset);
|
||||
outb(corsave | COR_SRESET, local->cor_offset);
|
||||
mdelay(2);
|
||||
outb(corsave & ~COR_SRESET, local->cor_offset);
|
||||
mdelay(2);
|
||||
} else {
|
||||
/* PLX9052 */
|
||||
corsave = readb(local->attr_mem + local->cor_offset);
|
||||
writeb(corsave | COR_SRESET,
|
||||
local->attr_mem + local->cor_offset);
|
||||
mdelay(2);
|
||||
writeb(corsave & ~COR_SRESET,
|
||||
local->attr_mem + local->cor_offset);
|
||||
mdelay(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
|
||||
{
|
||||
unsigned char corsave;
|
||||
|
||||
if (local->attr_mem == NULL) {
|
||||
/* TMD7160 - COR at card's first I/O addr */
|
||||
corsave = inb(local->cor_offset);
|
||||
outb(corsave | COR_SRESET, local->cor_offset);
|
||||
mdelay(10);
|
||||
outb(hcr, local->cor_offset + 2);
|
||||
mdelay(10);
|
||||
outb(corsave & ~COR_SRESET, local->cor_offset);
|
||||
mdelay(10);
|
||||
} else {
|
||||
/* PLX9052 */
|
||||
corsave = readb(local->attr_mem + local->cor_offset);
|
||||
writeb(corsave | COR_SRESET,
|
||||
local->attr_mem + local->cor_offset);
|
||||
mdelay(10);
|
||||
writeb(hcr, local->attr_mem + local->cor_offset + 2);
|
||||
mdelay(10);
|
||||
writeb(corsave & ~COR_SRESET,
|
||||
local->attr_mem + local->cor_offset);
|
||||
mdelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct prism2_helper_functions prism2_plx_funcs =
|
||||
{
|
||||
.card_present = NULL,
|
||||
.cor_sreset = prism2_plx_cor_sreset,
|
||||
.dev_open = NULL,
|
||||
.dev_close = NULL,
|
||||
.genesis_reset = prism2_plx_genesis_reset,
|
||||
.hw_type = HOSTAP_HW_PLX,
|
||||
};
|
||||
|
||||
|
||||
static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len,
|
||||
unsigned int *cor_offset,
|
||||
unsigned int *cor_index)
|
||||
{
|
||||
#define CISTPL_CONFIG 0x1A
|
||||
#define CISTPL_MANFID 0x20
|
||||
#define CISTPL_END 0xFF
|
||||
#define CIS_MAX_LEN 256
|
||||
u8 *cis;
|
||||
int i, pos;
|
||||
unsigned int rmsz, rasz, manfid1, manfid2;
|
||||
struct prism2_plx_manfid *manfid;
|
||||
|
||||
cis = kmalloc(CIS_MAX_LEN, GFP_KERNEL);
|
||||
if (cis == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* read CIS; it is in even offsets in the beginning of attr_mem */
|
||||
for (i = 0; i < CIS_MAX_LEN; i++)
|
||||
cis[i] = readb(attr_mem + 2 * i);
|
||||
printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n",
|
||||
dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]);
|
||||
|
||||
/* set reasonable defaults for Prism2 cards just in case CIS parsing
|
||||
* fails */
|
||||
*cor_offset = 0x3e0;
|
||||
*cor_index = 0x01;
|
||||
manfid1 = manfid2 = 0;
|
||||
|
||||
pos = 0;
|
||||
while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) {
|
||||
if (pos + cis[pos + 1] >= CIS_MAX_LEN)
|
||||
goto cis_error;
|
||||
|
||||
switch (cis[pos]) {
|
||||
case CISTPL_CONFIG:
|
||||
if (cis[pos + 1] < 1)
|
||||
goto cis_error;
|
||||
rmsz = (cis[pos + 2] & 0x3c) >> 2;
|
||||
rasz = cis[pos + 2] & 0x03;
|
||||
if (4 + rasz + rmsz > cis[pos + 1])
|
||||
goto cis_error;
|
||||
*cor_index = cis[pos + 3] & 0x3F;
|
||||
*cor_offset = 0;
|
||||
for (i = 0; i <= rasz; i++)
|
||||
*cor_offset += cis[pos + 4 + i] << (8 * i);
|
||||
printk(KERN_DEBUG "%s: cor_index=0x%x "
|
||||
"cor_offset=0x%x\n", dev_info,
|
||||
*cor_index, *cor_offset);
|
||||
if (*cor_offset > attr_len) {
|
||||
printk(KERN_ERR "%s: COR offset not within "
|
||||
"attr_mem\n", dev_info);
|
||||
kfree(cis);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case CISTPL_MANFID:
|
||||
if (cis[pos + 1] < 4)
|
||||
goto cis_error;
|
||||
manfid1 = cis[pos + 2] + (cis[pos + 3] << 8);
|
||||
manfid2 = cis[pos + 4] + (cis[pos + 5] << 8);
|
||||
printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n",
|
||||
dev_info, manfid1, manfid2);
|
||||
break;
|
||||
}
|
||||
|
||||
pos += cis[pos + 1] + 2;
|
||||
}
|
||||
|
||||
if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END)
|
||||
goto cis_error;
|
||||
|
||||
for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++)
|
||||
if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) {
|
||||
kfree(cis);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is"
|
||||
" not supported card\n", dev_info, manfid1, manfid2);
|
||||
goto fail;
|
||||
|
||||
cis_error:
|
||||
printk(KERN_WARNING "%s: invalid CIS data\n", dev_info);
|
||||
|
||||
fail:
|
||||
kfree(cis);
|
||||
if (ignore_cis) {
|
||||
printk(KERN_INFO "%s: ignore_cis parameter set - ignoring "
|
||||
"errors during CIS verification\n", dev_info);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_plx_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
unsigned int pccard_ioaddr, plx_ioaddr;
|
||||
unsigned long pccard_attr_mem;
|
||||
unsigned int pccard_attr_len;
|
||||
void __iomem *attr_mem = NULL;
|
||||
unsigned int cor_offset, cor_index;
|
||||
u32 reg;
|
||||
local_info_t *local = NULL;
|
||||
struct net_device *dev = NULL;
|
||||
struct hostap_interface *iface;
|
||||
static int cards_found /* = 0 */;
|
||||
int irq_registered = 0;
|
||||
int tmd7160;
|
||||
|
||||
if (pci_enable_device(pdev))
|
||||
return -EIO;
|
||||
|
||||
/* National Datacomm NCP130 based on TMD7160, not PLX9052. */
|
||||
tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131);
|
||||
|
||||
plx_ioaddr = pci_resource_start(pdev, 1);
|
||||
pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3);
|
||||
|
||||
if (tmd7160) {
|
||||
/* TMD7160 */
|
||||
attr_mem = NULL; /* no access to PC Card attribute memory */
|
||||
|
||||
printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, "
|
||||
"irq=%d, pccard_io=0x%x\n",
|
||||
plx_ioaddr, pdev->irq, pccard_ioaddr);
|
||||
|
||||
cor_offset = plx_ioaddr;
|
||||
cor_index = 0x04;
|
||||
|
||||
outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr);
|
||||
mdelay(1);
|
||||
reg = inb(plx_ioaddr);
|
||||
if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) {
|
||||
printk(KERN_ERR "%s: Error setting COR (expected="
|
||||
"0x%02x, was=0x%02x)\n", dev_info,
|
||||
cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/* PLX9052 */
|
||||
pccard_attr_mem = pci_resource_start(pdev, 2);
|
||||
pccard_attr_len = pci_resource_len(pdev, 2);
|
||||
if (pccard_attr_len < PLX_MIN_ATTR_LEN)
|
||||
goto fail;
|
||||
|
||||
|
||||
attr_mem = ioremap(pccard_attr_mem, pccard_attr_len);
|
||||
if (attr_mem == NULL) {
|
||||
printk(KERN_ERR "%s: cannot remap attr_mem\n",
|
||||
dev_info);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: "
|
||||
"mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n",
|
||||
pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr);
|
||||
|
||||
if (prism2_plx_check_cis(attr_mem, pccard_attr_len,
|
||||
&cor_offset, &cor_index)) {
|
||||
printk(KERN_INFO "Unknown PC Card CIS - not a "
|
||||
"Prism2/2.5 card?\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 "
|
||||
"adapter\n");
|
||||
|
||||
/* Write COR to enable PC Card */
|
||||
writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC,
|
||||
attr_mem + cor_offset);
|
||||
|
||||
/* Enable PCI interrupts if they are not already enabled */
|
||||
reg = inl(plx_ioaddr + PLX_INTCSR);
|
||||
printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg);
|
||||
if (!(reg & PLX_INTCSR_PCI_INTEN)) {
|
||||
outl(reg | PLX_INTCSR_PCI_INTEN,
|
||||
plx_ioaddr + PLX_INTCSR);
|
||||
if (!(inl(plx_ioaddr + PLX_INTCSR) &
|
||||
PLX_INTCSR_PCI_INTEN)) {
|
||||
printk(KERN_WARNING "%s: Could not enable "
|
||||
"Local Interrupts\n", dev_info);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
reg = inl(plx_ioaddr + PLX_CNTRL);
|
||||
printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM "
|
||||
"present=%d)\n",
|
||||
reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0);
|
||||
/* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is
|
||||
* not present; but are there really such cards in use(?) */
|
||||
}
|
||||
|
||||
dev = prism2_init_local_data(&prism2_plx_funcs, cards_found);
|
||||
if (dev == NULL)
|
||||
goto fail;
|
||||
iface = netdev_priv(dev);
|
||||
local = iface->local;
|
||||
cards_found++;
|
||||
|
||||
dev->irq = pdev->irq;
|
||||
dev->base_addr = pccard_ioaddr;
|
||||
local->attr_mem = attr_mem;
|
||||
local->cor_offset = cor_offset;
|
||||
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name,
|
||||
dev)) {
|
||||
printk(KERN_WARNING "%s: request_irq failed\n", dev->name);
|
||||
goto fail;
|
||||
} else
|
||||
irq_registered = 1;
|
||||
|
||||
if (prism2_hw_config(dev, 1)) {
|
||||
printk(KERN_DEBUG "%s: hardware initialization failed\n",
|
||||
dev_info);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return hostap_hw_ready(dev);
|
||||
|
||||
fail:
|
||||
prism2_free_local_data(dev);
|
||||
|
||||
if (irq_registered && dev)
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
if (attr_mem)
|
||||
iounmap(attr_mem);
|
||||
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static void prism2_plx_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct hostap_interface *iface;
|
||||
|
||||
dev = pci_get_drvdata(pdev);
|
||||
iface = netdev_priv(dev);
|
||||
|
||||
/* Reset the hardware, and ensure interrupts are disabled. */
|
||||
prism2_plx_cor_sreset(iface->local);
|
||||
hfa384x_disable_interrupts(dev);
|
||||
|
||||
if (iface->local->attr_mem)
|
||||
iounmap(iface->local->attr_mem);
|
||||
if (dev->irq)
|
||||
free_irq(dev->irq, dev);
|
||||
|
||||
prism2_free_local_data(dev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
|
||||
|
||||
static struct pci_driver prism2_plx_drv_id = {
|
||||
.name = "prism2_plx",
|
||||
.id_table = prism2_plx_id_table,
|
||||
.probe = prism2_plx_probe,
|
||||
.remove = prism2_plx_remove,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.enable_wake = NULL
|
||||
};
|
||||
|
||||
|
||||
static int __init init_prism2_plx(void)
|
||||
{
|
||||
printk(KERN_INFO "%s: %s\n", dev_info, version);
|
||||
|
||||
return pci_register_driver(&prism2_plx_drv_id);
|
||||
}
|
||||
|
||||
|
||||
static void __exit exit_prism2_plx(void)
|
||||
{
|
||||
pci_unregister_driver(&prism2_plx_drv_id);
|
||||
printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
|
||||
}
|
||||
|
||||
|
||||
module_init(init_prism2_plx);
|
||||
module_exit(exit_prism2_plx);
|
466
drivers/net/wireless/hostap/hostap_proc.c
Normal file
466
drivers/net/wireless/hostap/hostap_proc.c
Normal file
@ -0,0 +1,466 @@
|
||||
/* /proc routines for Host AP driver */
|
||||
|
||||
#define PROC_LIMIT (PAGE_SIZE - 80)
|
||||
|
||||
|
||||
#ifndef PRISM2_NO_PROCFS_DEBUG
|
||||
static int prism2_debug_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
int i;
|
||||
|
||||
if (off != 0) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += sprintf(p, "next_txfid=%d next_alloc=%d\n",
|
||||
local->next_txfid, local->next_alloc);
|
||||
for (i = 0; i < PRISM2_TXFID_COUNT; i++)
|
||||
p += sprintf(p, "FID: tx=%04X intransmit=%04X\n",
|
||||
local->txfid[i], local->intransmitfid[i]);
|
||||
p += sprintf(p, "FW TX rate control: %d\n", local->fw_tx_rate_control);
|
||||
p += sprintf(p, "beacon_int=%d\n", local->beacon_int);
|
||||
p += sprintf(p, "dtim_period=%d\n", local->dtim_period);
|
||||
p += sprintf(p, "wds_max_connections=%d\n",
|
||||
local->wds_max_connections);
|
||||
p += sprintf(p, "dev_enabled=%d\n", local->dev_enabled);
|
||||
p += sprintf(p, "sw_tick_stuck=%d\n", local->sw_tick_stuck);
|
||||
for (i = 0; i < WEP_KEYS; i++) {
|
||||
if (local->crypt[i] && local->crypt[i]->ops) {
|
||||
p += sprintf(p, "crypt[%d]=%s\n",
|
||||
i, local->crypt[i]->ops->name);
|
||||
}
|
||||
}
|
||||
p += sprintf(p, "pri_only=%d\n", local->pri_only);
|
||||
p += sprintf(p, "pci=%d\n", local->func->hw_type == HOSTAP_HW_PCI);
|
||||
p += sprintf(p, "sram_type=%d\n", local->sram_type);
|
||||
p += sprintf(p, "no_pri=%d\n", local->no_pri);
|
||||
|
||||
return (p - page);
|
||||
}
|
||||
#endif /* PRISM2_NO_PROCFS_DEBUG */
|
||||
|
||||
|
||||
static int prism2_stats_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
struct comm_tallies_sums *sums = (struct comm_tallies_sums *)
|
||||
&local->comm_tallies;
|
||||
|
||||
if (off != 0) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += sprintf(p, "TxUnicastFrames=%u\n", sums->tx_unicast_frames);
|
||||
p += sprintf(p, "TxMulticastframes=%u\n", sums->tx_multicast_frames);
|
||||
p += sprintf(p, "TxFragments=%u\n", sums->tx_fragments);
|
||||
p += sprintf(p, "TxUnicastOctets=%u\n", sums->tx_unicast_octets);
|
||||
p += sprintf(p, "TxMulticastOctets=%u\n", sums->tx_multicast_octets);
|
||||
p += sprintf(p, "TxDeferredTransmissions=%u\n",
|
||||
sums->tx_deferred_transmissions);
|
||||
p += sprintf(p, "TxSingleRetryFrames=%u\n",
|
||||
sums->tx_single_retry_frames);
|
||||
p += sprintf(p, "TxMultipleRetryFrames=%u\n",
|
||||
sums->tx_multiple_retry_frames);
|
||||
p += sprintf(p, "TxRetryLimitExceeded=%u\n",
|
||||
sums->tx_retry_limit_exceeded);
|
||||
p += sprintf(p, "TxDiscards=%u\n", sums->tx_discards);
|
||||
p += sprintf(p, "RxUnicastFrames=%u\n", sums->rx_unicast_frames);
|
||||
p += sprintf(p, "RxMulticastFrames=%u\n", sums->rx_multicast_frames);
|
||||
p += sprintf(p, "RxFragments=%u\n", sums->rx_fragments);
|
||||
p += sprintf(p, "RxUnicastOctets=%u\n", sums->rx_unicast_octets);
|
||||
p += sprintf(p, "RxMulticastOctets=%u\n", sums->rx_multicast_octets);
|
||||
p += sprintf(p, "RxFCSErrors=%u\n", sums->rx_fcs_errors);
|
||||
p += sprintf(p, "RxDiscardsNoBuffer=%u\n",
|
||||
sums->rx_discards_no_buffer);
|
||||
p += sprintf(p, "TxDiscardsWrongSA=%u\n", sums->tx_discards_wrong_sa);
|
||||
p += sprintf(p, "RxDiscardsWEPUndecryptable=%u\n",
|
||||
sums->rx_discards_wep_undecryptable);
|
||||
p += sprintf(p, "RxMessageInMsgFragments=%u\n",
|
||||
sums->rx_message_in_msg_fragments);
|
||||
p += sprintf(p, "RxMessageInBadMsgFragments=%u\n",
|
||||
sums->rx_message_in_bad_msg_fragments);
|
||||
/* FIX: this may grow too long for one page(?) */
|
||||
|
||||
return (p - page);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_wds_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
struct list_head *ptr;
|
||||
struct hostap_interface *iface;
|
||||
|
||||
if (off > PROC_LIMIT) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
read_lock_bh(&local->iface_lock);
|
||||
list_for_each(ptr, &local->hostap_interfaces) {
|
||||
iface = list_entry(ptr, struct hostap_interface, list);
|
||||
if (iface->type != HOSTAP_INTERFACE_WDS)
|
||||
continue;
|
||||
p += sprintf(p, "%s\t" MACSTR "\n",
|
||||
iface->dev->name,
|
||||
MAC2STR(iface->u.wds.remote_addr));
|
||||
if ((p - page) > PROC_LIMIT) {
|
||||
printk(KERN_DEBUG "%s: wds proc did not fit\n",
|
||||
local->dev->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock_bh(&local->iface_lock);
|
||||
|
||||
if ((p - page) <= off) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = page + off;
|
||||
|
||||
return (p - page - off);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_bss_list_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
struct list_head *ptr;
|
||||
struct hostap_bss_info *bss;
|
||||
int i;
|
||||
|
||||
if (off > PROC_LIMIT) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += sprintf(p, "#BSSID\tlast_update\tcount\tcapab_info\tSSID(txt)\t"
|
||||
"SSID(hex)\tWPA IE\n");
|
||||
spin_lock_bh(&local->lock);
|
||||
list_for_each(ptr, &local->bss_list) {
|
||||
bss = list_entry(ptr, struct hostap_bss_info, list);
|
||||
p += sprintf(p, MACSTR "\t%lu\t%u\t0x%x\t",
|
||||
MAC2STR(bss->bssid), bss->last_update,
|
||||
bss->count, bss->capab_info);
|
||||
for (i = 0; i < bss->ssid_len; i++) {
|
||||
p += sprintf(p, "%c",
|
||||
bss->ssid[i] >= 32 && bss->ssid[i] < 127 ?
|
||||
bss->ssid[i] : '_');
|
||||
}
|
||||
p += sprintf(p, "\t");
|
||||
for (i = 0; i < bss->ssid_len; i++) {
|
||||
p += sprintf(p, "%02x", bss->ssid[i]);
|
||||
}
|
||||
p += sprintf(p, "\t");
|
||||
for (i = 0; i < bss->wpa_ie_len; i++) {
|
||||
p += sprintf(p, "%02x", bss->wpa_ie[i]);
|
||||
}
|
||||
p += sprintf(p, "\n");
|
||||
if ((p - page) > PROC_LIMIT) {
|
||||
printk(KERN_DEBUG "%s: BSS proc did not fit\n",
|
||||
local->dev->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&local->lock);
|
||||
|
||||
if ((p - page) <= off) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = page + off;
|
||||
|
||||
return (p - page - off);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_crypt_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
int i;
|
||||
|
||||
if (off > PROC_LIMIT) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += sprintf(p, "tx_keyidx=%d\n", local->tx_keyidx);
|
||||
for (i = 0; i < WEP_KEYS; i++) {
|
||||
if (local->crypt[i] && local->crypt[i]->ops &&
|
||||
local->crypt[i]->ops->print_stats) {
|
||||
p = local->crypt[i]->ops->print_stats(
|
||||
p, local->crypt[i]->priv);
|
||||
}
|
||||
}
|
||||
|
||||
if ((p - page) <= off) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = page + off;
|
||||
|
||||
return (p - page - off);
|
||||
}
|
||||
|
||||
|
||||
static int prism2_pda_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
|
||||
if (local->pda == NULL || off >= PRISM2_PDA_SIZE) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (off + count > PRISM2_PDA_SIZE)
|
||||
count = PRISM2_PDA_SIZE - off;
|
||||
|
||||
memcpy(page, local->pda + off, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static int prism2_aux_dump_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
|
||||
if (local->func->read_aux == NULL) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (local->func->read_aux(local->dev, off, count, page)) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
*start = page;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
static int prism2_io_debug_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
int head = local->io_debug_head;
|
||||
int start_bytes, left, copy, copied;
|
||||
|
||||
if (off + count > PRISM2_IO_DEBUG_SIZE * 4) {
|
||||
*eof = 1;
|
||||
if (off >= PRISM2_IO_DEBUG_SIZE * 4)
|
||||
return 0;
|
||||
count = PRISM2_IO_DEBUG_SIZE * 4 - off;
|
||||
}
|
||||
|
||||
copied = 0;
|
||||
start_bytes = (PRISM2_IO_DEBUG_SIZE - head) * 4;
|
||||
left = count;
|
||||
|
||||
if (off < start_bytes) {
|
||||
copy = start_bytes - off;
|
||||
if (copy > count)
|
||||
copy = count;
|
||||
memcpy(page, ((u8 *) &local->io_debug[head]) + off, copy);
|
||||
left -= copy;
|
||||
if (left > 0)
|
||||
memcpy(&page[copy], local->io_debug, left);
|
||||
} else {
|
||||
memcpy(page, ((u8 *) local->io_debug) + (off - start_bytes),
|
||||
left);
|
||||
}
|
||||
|
||||
*start = page;
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
|
||||
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
static int prism2_scan_results_proc_read(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
char *p = page;
|
||||
local_info_t *local = (local_info_t *) data;
|
||||
int entries, entry, i, len, total = 0, hostscan;
|
||||
struct hfa384x_scan_result *scanres;
|
||||
struct hfa384x_hostscan_result *hscanres;
|
||||
u8 *pos;
|
||||
|
||||
p += sprintf(p, "CHID ANL SL BcnInt Capab Rate BSSID ATIM SupRates "
|
||||
"SSID\n");
|
||||
|
||||
spin_lock_bh(&local->lock);
|
||||
hostscan = local->last_scan_type == PRISM2_HOSTSCAN;
|
||||
entries = hostscan ? local->last_hostscan_results_count :
|
||||
local->last_scan_results_count;
|
||||
for (entry = 0; entry < entries; entry++) {
|
||||
hscanres = &local->last_hostscan_results[entry];
|
||||
scanres = &local->last_scan_results[entry];
|
||||
|
||||
if (total + (p - page) <= off) {
|
||||
total += p - page;
|
||||
p = page;
|
||||
}
|
||||
if (total + (p - page) > off + count)
|
||||
break;
|
||||
if ((p - page) > (PAGE_SIZE - 200))
|
||||
break;
|
||||
|
||||
if (hostscan) {
|
||||
p += sprintf(p, "%d %d %d %d 0x%02x %d " MACSTR " %d ",
|
||||
le16_to_cpu(hscanres->chid),
|
||||
(s16) le16_to_cpu(hscanres->anl),
|
||||
(s16) le16_to_cpu(hscanres->sl),
|
||||
le16_to_cpu(hscanres->beacon_interval),
|
||||
le16_to_cpu(hscanres->capability),
|
||||
le16_to_cpu(hscanres->rate),
|
||||
MAC2STR(hscanres->bssid),
|
||||
le16_to_cpu(hscanres->atim));
|
||||
} else {
|
||||
p += sprintf(p, "%d %d %d %d 0x%02x %d " MACSTR
|
||||
" N/A ",
|
||||
le16_to_cpu(scanres->chid),
|
||||
(s16) le16_to_cpu(scanres->anl),
|
||||
(s16) le16_to_cpu(scanres->sl),
|
||||
le16_to_cpu(scanres->beacon_interval),
|
||||
le16_to_cpu(scanres->capability),
|
||||
le16_to_cpu(scanres->rate),
|
||||
MAC2STR(scanres->bssid));
|
||||
}
|
||||
|
||||
pos = hostscan ? hscanres->sup_rates : scanres->sup_rates;
|
||||
for (i = 0; i < sizeof(hscanres->sup_rates); i++) {
|
||||
if (pos[i] == 0)
|
||||
break;
|
||||
p += sprintf(p, "<%02x>", pos[i]);
|
||||
}
|
||||
p += sprintf(p, " ");
|
||||
|
||||
pos = hostscan ? hscanres->ssid : scanres->ssid;
|
||||
len = le16_to_cpu(hostscan ? hscanres->ssid_len :
|
||||
scanres->ssid_len);
|
||||
if (len > 32)
|
||||
len = 32;
|
||||
for (i = 0; i < len; i++) {
|
||||
unsigned char c = pos[i];
|
||||
if (c >= 32 && c < 127)
|
||||
p += sprintf(p, "%c", c);
|
||||
else
|
||||
p += sprintf(p, "<%02x>", c);
|
||||
}
|
||||
p += sprintf(p, "\n");
|
||||
}
|
||||
spin_unlock_bh(&local->lock);
|
||||
|
||||
total += (p - page);
|
||||
if (total >= off + count)
|
||||
*eof = 1;
|
||||
|
||||
if (total < off) {
|
||||
*eof = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = total - off;
|
||||
if (len > (p - page))
|
||||
len = p - page;
|
||||
*start = p - len;
|
||||
if (len > count)
|
||||
len = count;
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
|
||||
|
||||
void hostap_init_proc(local_info_t *local)
|
||||
{
|
||||
local->proc = NULL;
|
||||
|
||||
if (hostap_proc == NULL) {
|
||||
printk(KERN_WARNING "%s: hostap proc directory not created\n",
|
||||
local->dev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
local->proc = proc_mkdir(local->ddev->name, hostap_proc);
|
||||
if (local->proc == NULL) {
|
||||
printk(KERN_INFO "/proc/net/hostap/%s creation failed\n",
|
||||
local->ddev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef PRISM2_NO_PROCFS_DEBUG
|
||||
create_proc_read_entry("debug", 0, local->proc,
|
||||
prism2_debug_proc_read, local);
|
||||
#endif /* PRISM2_NO_PROCFS_DEBUG */
|
||||
create_proc_read_entry("stats", 0, local->proc,
|
||||
prism2_stats_proc_read, local);
|
||||
create_proc_read_entry("wds", 0, local->proc,
|
||||
prism2_wds_proc_read, local);
|
||||
create_proc_read_entry("pda", 0, local->proc,
|
||||
prism2_pda_proc_read, local);
|
||||
create_proc_read_entry("aux_dump", 0, local->proc,
|
||||
prism2_aux_dump_proc_read, local);
|
||||
create_proc_read_entry("bss_list", 0, local->proc,
|
||||
prism2_bss_list_proc_read, local);
|
||||
create_proc_read_entry("crypt", 0, local->proc,
|
||||
prism2_crypt_proc_read, local);
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
create_proc_read_entry("io_debug", 0, local->proc,
|
||||
prism2_io_debug_proc_read, local);
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
create_proc_read_entry("scan_results", 0, local->proc,
|
||||
prism2_scan_results_proc_read, local);
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
}
|
||||
|
||||
|
||||
void hostap_remove_proc(local_info_t *local)
|
||||
{
|
||||
if (local->proc != NULL) {
|
||||
#ifndef PRISM2_NO_STATION_MODES
|
||||
remove_proc_entry("scan_results", local->proc);
|
||||
#endif /* PRISM2_NO_STATION_MODES */
|
||||
#ifdef PRISM2_IO_DEBUG
|
||||
remove_proc_entry("io_debug", local->proc);
|
||||
#endif /* PRISM2_IO_DEBUG */
|
||||
remove_proc_entry("pda", local->proc);
|
||||
remove_proc_entry("aux_dump", local->proc);
|
||||
remove_proc_entry("wds", local->proc);
|
||||
remove_proc_entry("stats", local->proc);
|
||||
remove_proc_entry("bss_list", local->proc);
|
||||
remove_proc_entry("crypt", local->proc);
|
||||
#ifndef PRISM2_NO_PROCFS_DEBUG
|
||||
remove_proc_entry("debug", local->proc);
|
||||
#endif /* PRISM2_NO_PROCFS_DEBUG */
|
||||
if (hostap_proc != NULL)
|
||||
remove_proc_entry(local->proc->name, hostap_proc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(hostap_init_proc);
|
||||
EXPORT_SYMBOL(hostap_remove_proc);
|
1075
drivers/net/wireless/hostap/hostap_wlan.h
Normal file
1075
drivers/net/wireless/hostap/hostap_wlan.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -209,7 +209,7 @@ enum {
|
||||
NoStructure = 0, /* Really old firmware */
|
||||
StructuredMessages = 1, /* Parsable AT response msgs */
|
||||
ChecksummedMessages = 2 /* Parsable AT response msgs with checksums */
|
||||
} FirmwareLevel;
|
||||
};
|
||||
|
||||
struct strip {
|
||||
int magic;
|
||||
|
@ -59,6 +59,12 @@
|
||||
/* Do *NOT* add other headers here, you are guaranteed to be wrong - Jean II */
|
||||
#include "wavelan_cs.p.h" /* Private header */
|
||||
|
||||
#ifdef WAVELAN_ROAMING
|
||||
static void wl_cell_expiry(unsigned long data);
|
||||
static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp);
|
||||
static void wv_nwid_filter(unsigned char mode, net_local *lp);
|
||||
#endif /* WAVELAN_ROAMING */
|
||||
|
||||
/************************* MISC SUBROUTINES **************************/
|
||||
/*
|
||||
* Subroutines which won't fit in one of the following category
|
||||
@ -500,9 +506,9 @@ fee_write(u_long base, /* i/o port of the card */
|
||||
|
||||
#ifdef WAVELAN_ROAMING /* Conditional compile, see wavelan_cs.h */
|
||||
|
||||
unsigned char WAVELAN_BEACON_ADDRESS[]= {0x09,0x00,0x0e,0x20,0x03,0x00};
|
||||
static unsigned char WAVELAN_BEACON_ADDRESS[] = {0x09,0x00,0x0e,0x20,0x03,0x00};
|
||||
|
||||
void wv_roam_init(struct net_device *dev)
|
||||
static void wv_roam_init(struct net_device *dev)
|
||||
{
|
||||
net_local *lp= netdev_priv(dev);
|
||||
|
||||
@ -531,7 +537,7 @@ void wv_roam_init(struct net_device *dev)
|
||||
printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name);
|
||||
}
|
||||
|
||||
void wv_roam_cleanup(struct net_device *dev)
|
||||
static void wv_roam_cleanup(struct net_device *dev)
|
||||
{
|
||||
wavepoint_history *ptr,*old_ptr;
|
||||
net_local *lp= netdev_priv(dev);
|
||||
@ -550,7 +556,7 @@ void wv_roam_cleanup(struct net_device *dev)
|
||||
}
|
||||
|
||||
/* Enable/Disable NWID promiscuous mode on a given device */
|
||||
void wv_nwid_filter(unsigned char mode, net_local *lp)
|
||||
static void wv_nwid_filter(unsigned char mode, net_local *lp)
|
||||
{
|
||||
mm_t m;
|
||||
unsigned long flags;
|
||||
@ -575,7 +581,7 @@ void wv_nwid_filter(unsigned char mode, net_local *lp)
|
||||
}
|
||||
|
||||
/* Find a record in the WavePoint table matching a given NWID */
|
||||
wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
|
||||
static wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
|
||||
{
|
||||
wavepoint_history *ptr=lp->wavepoint_table.head;
|
||||
|
||||
@ -588,7 +594,7 @@ wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp)
|
||||
}
|
||||
|
||||
/* Create a new wavepoint table entry */
|
||||
wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
|
||||
static wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp)
|
||||
{
|
||||
wavepoint_history *new_wavepoint;
|
||||
|
||||
@ -624,7 +630,7 @@ wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_
|
||||
}
|
||||
|
||||
/* Remove a wavepoint entry from WavePoint table */
|
||||
void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
|
||||
static void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
|
||||
{
|
||||
if(wavepoint==NULL)
|
||||
return;
|
||||
@ -646,7 +652,7 @@ void wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp)
|
||||
}
|
||||
|
||||
/* Timer callback function - checks WavePoint table for stale entries */
|
||||
void wl_cell_expiry(unsigned long data)
|
||||
static void wl_cell_expiry(unsigned long data)
|
||||
{
|
||||
net_local *lp=(net_local *)data;
|
||||
wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point;
|
||||
@ -686,7 +692,7 @@ void wl_cell_expiry(unsigned long data)
|
||||
}
|
||||
|
||||
/* Update SNR history of a wavepoint */
|
||||
void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)
|
||||
static void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq)
|
||||
{
|
||||
int i=0,num_missed=0,ptr=0;
|
||||
int average_fast=0,average_slow=0;
|
||||
@ -723,7 +729,7 @@ void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsi
|
||||
}
|
||||
|
||||
/* Perform a handover to a new WavePoint */
|
||||
void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
|
||||
static void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp)
|
||||
{
|
||||
kio_addr_t base = lp->dev->base_addr;
|
||||
mm_t m;
|
||||
|
@ -62,7 +62,7 @@
|
||||
* like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this
|
||||
* part to accommodate your hardware...
|
||||
*/
|
||||
const unsigned char MAC_ADDRESSES[][3] =
|
||||
static const unsigned char MAC_ADDRESSES[][3] =
|
||||
{
|
||||
{ 0x08, 0x00, 0x0E }, /* AT&T Wavelan (standard) & DEC RoamAbout */
|
||||
{ 0x08, 0x00, 0x6A }, /* AT&T Wavelan (alternate) */
|
||||
@ -79,14 +79,14 @@ const unsigned char MAC_ADDRESSES[][3] =
|
||||
* (as read in the offset register of the dac area).
|
||||
* Used to map channel numbers used by `wfreqsel' to frequencies
|
||||
*/
|
||||
const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
|
||||
static const short channel_bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8,
|
||||
0xD0, 0xF0, 0xF8, 0x150 };
|
||||
|
||||
/* Frequencies of the 1.0 modem (fixed frequencies).
|
||||
* Use to map the PSA `subband' to a frequency
|
||||
* Note : all frequencies apart from the first one need to be multiplied by 10
|
||||
*/
|
||||
const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
|
||||
static const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
|
||||
|
||||
|
||||
/*************************** PC INTERFACE ****************************/
|
||||
|
@ -648,23 +648,6 @@ struct net_local
|
||||
void __iomem *mem;
|
||||
};
|
||||
|
||||
/**************************** PROTOTYPES ****************************/
|
||||
|
||||
#ifdef WAVELAN_ROAMING
|
||||
/* ---------------------- ROAMING SUBROUTINES -----------------------*/
|
||||
|
||||
wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp);
|
||||
wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp);
|
||||
void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp);
|
||||
void wl_cell_expiry(unsigned long data);
|
||||
wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp);
|
||||
void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq);
|
||||
void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp);
|
||||
void wv_nwid_filter(unsigned char mode, net_local *lp);
|
||||
void wv_roam_init(struct net_device *dev);
|
||||
void wv_roam_cleanup(struct net_device *dev);
|
||||
#endif /* WAVELAN_ROAMING */
|
||||
|
||||
/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
|
||||
static inline u_char /* data */
|
||||
hasr_read(u_long); /* Read the host interface : base address */
|
||||
|
@ -297,7 +297,8 @@ static int wl3501_get_flash_mac_addr(struct wl3501_card *this)
|
||||
*
|
||||
* Move 'size' bytes from PC to card. (Shouldn't be interrupted)
|
||||
*/
|
||||
void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src, int size)
|
||||
static void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src,
|
||||
int size)
|
||||
{
|
||||
/* switch to SRAM Page 0 */
|
||||
wl3501_switch_page(this, (dest & 0x8000) ? WL3501_BSS_SPAGE1 :
|
||||
@ -318,8 +319,8 @@ void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src, int size)
|
||||
*
|
||||
* Move 'size' bytes from card to PC. (Shouldn't be interrupted)
|
||||
*/
|
||||
void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest,
|
||||
int size)
|
||||
static void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest,
|
||||
int size)
|
||||
{
|
||||
/* switch to SRAM Page 0 */
|
||||
wl3501_switch_page(this, (src & 0x8000) ? WL3501_BSS_SPAGE1 :
|
||||
@ -1439,14 +1440,14 @@ fail:
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct net_device_stats *wl3501_get_stats(struct net_device *dev)
|
||||
static struct net_device_stats *wl3501_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct wl3501_card *this = dev->priv;
|
||||
|
||||
return &this->stats;
|
||||
}
|
||||
|
||||
struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev)
|
||||
static struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev)
|
||||
{
|
||||
struct wl3501_card *this = dev->priv;
|
||||
struct iw_statistics *wstats = &this->wstats;
|
||||
|
Loading…
Reference in New Issue
Block a user