Add HostAP wireless driver.

Includes minor cleanups from Adrian Bunk <bunk@stusta.de>.
This commit is contained in:
Jouni Malinen 2005-05-12 22:54:16 -04:00 committed by Jeff Garzik
parent 88d7bd8cb9
commit ff1d2767d5
34 changed files with 21602 additions and 68 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 */

View 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".

View 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

File diff suppressed because it is too large Load Diff

View 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 */

View 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 */

File diff suppressed because it is too large Load Diff

View 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);

File diff suppressed because it is too large Load Diff

View 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 */

View 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 */

View 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 */

View 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);

View 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 */

View 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);

View 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);

View 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);

View 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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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);

View 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;
}

File diff suppressed because it is too large Load Diff

View 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);

File diff suppressed because it is too large Load Diff

View 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);

View 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);

View 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);

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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;

View File

@ -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 ****************************/

View File

@ -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 */

View File

@ -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;