hostap: move under intersil vendor directory

Part of reorganising wireless drivers directory and Kconfig.

Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
Kalle Valo
2015-11-18 09:42:58 +02:00
parent 6948300c79
commit eb4f98d5de
25 changed files with 20 additions and 3 deletions

View File

@@ -0,0 +1,98 @@
config HOSTAP
tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)"
select WIRELESS_EXT
select WEXT_SPY
select WEXT_PRIV
select CRYPTO
select CRYPTO_ARC4
select CRYPTO_ECB
select CRYPTO_AES
select CRYPTO_MICHAEL_MIC
select CRYPTO_ECB
select CRC32
select LIB80211
select LIB80211_CRYPT_WEP
select LIB80211_CRYPT_TKIP
select LIB80211_CRYPT_CCMP
---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.
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.
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. This option by itself only enables downloading to the
volatile memory, i.e. the card RAM. This option is required to
support cards that don't have firmware in flash, such as D-Link
DWL-520 rev E and D-Link DWL-650 rev P.
Firmware image downloading needs a user space tool, prism2_srec.
It is available from http://hostap.epitest.fi/.
config HOSTAP_FIRMWARE_NVRAM
bool "Support for non-volatile firmware download"
depends on HOSTAP_FIRMWARE
---help---
Allow Host AP driver to write firmware images to the non-volatile
card memory, i.e. flash memory that survives power cycling.
Enable this option if you want to be able to change card firmware
permanently.
Firmware image downloading needs a 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.
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.
config HOSTAP_CS
tristate "Host AP driver for Prism2/2.5/3 PC Cards"
depends on PCMCIA && 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.

View File

@@ -0,0 +1,7 @@
hostap-y := hostap_80211_rx.o hostap_80211_tx.o hostap_ap.o hostap_info.o \
hostap_ioctl.o hostap_main.o hostap_proc.o
obj-$(CONFIG_HOSTAP) += hostap.o
obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o
obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o
obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o

View File

@@ -0,0 +1,95 @@
#ifndef HOSTAP_H
#define HOSTAP_H
#include <linux/ethtool.h>
#include <linux/kernel.h>
#include "hostap_wlan.h"
#include "hostap_ap.h"
static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
2447, 2452, 2457, 2462, 2467, 2472, 2484 };
#define FREQ_COUNT ARRAY_SIZE(freq_list)
/* 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);
extern const struct header_ops hostap_80211_ops;
int hostap_80211_get_hdrlen(__le16 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 type);
void hostap_set_multicast_list_queue(struct work_struct *work);
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, u16 stype,
u8 *body, size_t bodylen);
int prism2_sta_deauth(local_info_t *local, u16 reason);
int prism2_wds_add(local_info_t *local, u8 *remote_addr,
int rtnl_locked);
int prism2_wds_del(local_info_t *local, u8 *remote_addr,
int rtnl_locked, int do_not_remove);
/* hostap_ap.c */
int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac);
int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac);
void ap_control_flush_macs(struct mac_restrictions *mac_restrictions);
int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac);
void ap_control_kickall(struct ap_data *ap);
void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent,
struct lib80211_crypt_data ***crypt);
int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[],
struct iw_quality qual[], int buf_size,
int aplist);
int prism2_ap_translate_scan(struct net_device *dev,
struct iw_request_info *info, char *buffer);
int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param);
/* 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);
/* hostap_ioctl.c */
extern const struct iw_handler_def hostap_iw_handler_def;
extern const struct ethtool_ops prism2_ethtool_ops;
int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
#endif /* HOSTAP_H */

View File

@@ -0,0 +1,96 @@
#ifndef HOSTAP_80211_H
#define HOSTAP_80211_H
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
struct hostap_ieee80211_mgmt {
__le16 frame_control;
__le16 duration;
u8 da[6];
u8 sa[6];
u8 bssid[6];
__le16 seq_ctrl;
union {
struct {
__le16 auth_alg;
__le16 auth_transaction;
__le16 status_code;
/* possibly followed by Challenge text */
u8 variable[0];
} __packed auth;
struct {
__le16 reason_code;
} __packed deauth;
struct {
__le16 capab_info;
__le16 listen_interval;
/* followed by SSID and Supported rates */
u8 variable[0];
} __packed assoc_req;
struct {
__le16 capab_info;
__le16 status_code;
__le16 aid;
/* followed by Supported rates */
u8 variable[0];
} __packed assoc_resp, reassoc_resp;
struct {
__le16 capab_info;
__le16 listen_interval;
u8 current_ap[6];
/* followed by SSID and Supported rates */
u8 variable[0];
} __packed reassoc_req;
struct {
__le16 reason_code;
} __packed disassoc;
struct {
} __packed probe_req;
struct {
u8 timestamp[8];
__le16 beacon_int;
__le16 capab_info;
/* followed by some of SSID, Supported rates,
* FH Params, DS Params, CF Params, IBSS Params, TIM */
u8 variable[0];
} __packed beacon, probe_resp;
} u;
} __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 */
};
/* 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);
netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb,
struct net_device *dev);
netdev_tx_t hostap_mgmt_start_xmit(struct sk_buff *skb,
struct net_device *dev);
netdev_tx_t 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,553 @@
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/etherdevice.h>
#include "hostap_80211.h"
#include "hostap_common.h"
#include "hostap_wlan.h"
#include "hostap.h"
#include "hostap_ap.h"
/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
static unsigned char rfc1042_header[] =
{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
static unsigned char bridge_tunnel_header[] =
{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
/* No encapsulation header if EtherType < 0x600 (=length) */
void hostap_dump_tx_80211(const char *name, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr;
u16 fc;
hdr = (struct 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, (fc & IEEE80211_FCTL_FTYPE) >> 2,
(fc & IEEE80211_FCTL_STYPE) >> 4,
fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "",
fc & IEEE80211_FCTL_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=%pM", hdr->addr1);
printk(" A2=%pM", hdr->addr2);
printk(" A3=%pM", hdr->addr3);
if (skb->len >= 30)
printk(" A4=%pM", 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. */
netdev_tx_t 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 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 NETDEV_TX_OK;
}
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 NETDEV_TX_OK;
}
} 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 NETDEV_TX_OK;
} else if (local->iw_mode == IW_MODE_INFRA &&
(local->wds_type & HOSTAP_WDS_AP_CLIENT) &&
!ether_addr_equal(skb->data + ETH_ALEN, dev->dev_addr)) {
/* 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 = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA;
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 |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
/* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA,
* Addr4 = SA */
skb_copy_from_linear_data_offset(skb, ETH_ALEN,
&hdr.addr4, ETH_ALEN);
hdr_len += ETH_ALEN;
} else {
/* bogus 4-addr format to workaround Prism2 station
* f/w bug */
fc |= IEEE80211_FCTL_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
*/
skb_copy_from_linear_data_offset(skb, ETH_ALEN,
&hdr.addr4, 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) &&
is_multicast_ether_addr(skb->data))
eth_broadcast_addr(hdr.addr1);
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);
skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN);
} else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) {
fc |= IEEE80211_FCTL_FROMDS;
/* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */
skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN);
memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN);
skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr3,
ETH_ALEN);
} else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) {
fc |= IEEE80211_FCTL_TODS;
/* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */
memcpy(&hdr.addr1, to_assoc_ap ?
local->assoc_ap_addr : local->bssid, ETH_ALEN);
skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2,
ETH_ALEN);
skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN);
} else if (local->iw_mode == IW_MODE_ADHOC) {
/* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */
skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN);
skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2,
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 NETDEV_TX_OK;
}
if (pskb_expand_head(skb, need_headroom, need_tailroom,
GFP_ATOMIC)) {
kfree_skb(skb);
iface->stats.tx_dropped++;
return NETDEV_TX_OK;
}
} 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 NETDEV_TX_OK;
}
} else {
skb = skb_unshare(skb, GFP_ATOMIC);
if (skb == NULL) {
iface->stats.tx_dropped++;
return NETDEV_TX_OK;
}
}
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_reset_mac_header(skb);
meta = (struct hostap_skb_tx_data *) skb->cb;
memset(meta, 0, sizeof(*meta));
meta->magic = HOSTAP_SKB_TX_DATA_MAGIC;
if (use_wds)
meta->flags |= HOSTAP_TX_FLAGS_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 NETDEV_TX_OK;
}
/* hard_start_xmit function for hostapd wlan#ap interfaces */
netdev_tx_t 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 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 NETDEV_TX_OK;
}
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 ieee80211_hdr *) skb->data;
fc = le16_to_cpu(hdr->frame_control);
if (ieee80211_is_data(hdr->frame_control) &&
(fc & IEEE80211_FCTL_STYPE) == IEEE80211_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 NETDEV_TX_OK;
}
/* Called only from software IRQ */
static struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
struct lib80211_crypt_data *crypt)
{
struct hostap_interface *iface;
local_info_t *local;
struct ieee80211_hdr *hdr;
int prefix_len, postfix_len, 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 &&
strcmp(crypt->ops->name, "TKIP") == 0) {
hdr = (struct ieee80211_hdr *) skb->data;
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
"TX packet to %pM\n",
local->dev->name, hdr->addr1);
}
kfree_skb(skb);
return NULL;
}
skb = skb_unshare(skb, GFP_ATOMIC);
if (skb == NULL)
return NULL;
prefix_len = crypt->ops->extra_mpdu_prefix_len +
crypt->ops->extra_msdu_prefix_len;
postfix_len = crypt->ops->extra_mpdu_postfix_len +
crypt->ops->extra_msdu_postfix_len;
if ((skb_headroom(skb) < prefix_len ||
skb_tailroom(skb) < postfix_len) &&
pskb_expand_head(skb, prefix_len, postfix_len, GFP_ATOMIC)) {
kfree_skb(skb);
return NULL;
}
hdr = (struct ieee80211_hdr *) skb->data;
hdr_len = hostap_80211_get_hdrlen(hdr->frame_control);
/* 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. */
netdev_tx_t hostap_master_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct hostap_interface *iface;
local_info_t *local;
netdev_tx_t ret = NETDEV_TX_BUSY;
u16 fc;
struct hostap_tx_data tx;
ap_tx_ret tx_ret;
struct hostap_skb_tx_data *meta;
int no_encrypt = 0;
struct 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 = NETDEV_TX_OK;
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_info.crypt[local->crypt_info.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 = NETDEV_TX_OK;
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 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 &&
ieee80211_is_data(hdr->frame_control) &&
meta->ethertype != ETH_P_PAE &&
!(meta->flags & HOSTAP_TX_FLAGS_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 = NETDEV_TX_OK; /* drop packet */
iface->stats.tx_dropped++;
goto fail;
}
break;
case AP_TX_DROP:
ret = NETDEV_TX_OK; /* 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 = NETDEV_TX_OK;
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 & IEEE80211_FCTL_VERS) == 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 &= ~IEEE80211_FCTL_VERS;
hdr->frame_control = cpu_to_le16(fc);
}
if (!ieee80211_is_data(hdr->frame_control)) {
no_encrypt = 1;
tx.crypt = NULL;
}
if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt &&
!(fc & IEEE80211_FCTL_PROTECTED)) {
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_info.crypt[local->crypt_info.tx_keyidx]) &&
!no_encrypt) {
/* Add ISWEP flag both for firmware and host based encryption
*/
fc |= IEEE80211_FCTL_PROTECTED;
hdr->frame_control = cpu_to_le16(fc);
} else if (local->drop_unencrypted &&
ieee80211_is_data(hdr->frame_control) &&
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 = NETDEV_TX_OK;
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 = NETDEV_TX_OK;
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 = NETDEV_TX_OK;
iface->stats.tx_dropped++;
goto fail;
}
}
if (local->func->tx == NULL || local->func->tx(skb, dev)) {
ret = NETDEV_TX_OK;
iface->stats.tx_dropped++;
} else {
ret = NETDEV_TX_OK;
iface->stats.tx_packets++;
iface->stats.tx_bytes += skb->len;
}
fail:
if (ret == NETDEV_TX_OK && skb)
dev_kfree_skb(skb);
tx_exit:
if (tx.sta_ptr)
hostap_handle_sta_release(tx.sta_ptr);
return ret;
}
EXPORT_SYMBOL(hostap_master_start_xmit);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,263 @@
#ifndef HOSTAP_AP_H
#define HOSTAP_AP_H
#include "hostap_80211.h"
/* AP data structures for STAs */
/* maximum number of frames to buffer per STA */
#define STA_MAX_TX_BUFFER 32
/* 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 lib80211_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. Similarly, 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 lib80211_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 lib80211_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 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 ieee80211_hdr *hdr,
struct lib80211_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 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,419 @@
#ifndef HOSTAP_COMMON_H
#define HOSTAP_COMMON_H
#include <linux/types.h>
#include <linux/if_ether.h>
/* IEEE 802.11 defines */
/* 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
{
__le16 id;
__le16 variant;
__le16 major;
__le16 minor;
} __packed;
#define HFA384X_COMP_ID_PRI 0x15
#define HFA384X_COMP_ID_STA 0x1f
#define HFA384X_COMP_ID_FW_AP 0x14b
struct hfa384x_sup_range
{
__le16 role;
__le16 id;
__le16 variant;
__le16 bottom;
__le16 top;
} __packed;
struct hfa384x_build_id
{
__le16 pri_seq;
__le16 sec_seq;
} __packed;
/* FD01 - Download Buffer */
struct hfa384x_rid_download_buffer
{
__le16 page;
__le16 offset;
__le16 length;
} __packed;
/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */
struct hfa384x_comms_quality {
__le16 comm_qual; /* 0 .. 92 */
__le16 signal_level; /* 27 .. 154 */
__le16 noise_level; /* 27 .. 154 */
} __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, REMOVED 2005-08-14 */
/* PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, REMOVED 2005-08-14 */
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,
PRISM2_PARAM_SCAN_CHANNEL_MASK = 40,
};
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 \
offsetof(struct prism2_hostapd_param, u.rid.data)
#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
offsetof(struct prism2_hostapd_param, 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,48 @@
#ifndef HOSTAP_CONFIG_H
#define HOSTAP_CONFIG_H
/* 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
/* 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
/* Allow kernel configuration to enable non-volatile download support. */
#ifdef CONFIG_HOSTAP_FIRMWARE_NVRAM
#define PRISM2_NON_VOLATILE_DOWNLOAD
#endif
/* 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 ccflags-y,
* e.g.,
* 'make pccard ccflags-y="-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,708 @@
#define PRISM2_PCCARD
#include <linux/module.h>
#include <linux/if.h>
#include <linux/slab.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/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include <asm/io.h>
#include "hostap_wlan.h"
static char *dev_info = "hostap_cs";
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 ignore_cis_vcc;
module_param(ignore_cis_vcc, int, 0444);
MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
/* struct local_info::hw_priv */
struct hostap_cs_priv {
struct pcmcia_device *link;
int sandisk_connectplus;
};
#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(struct pcmcia_device *p_dev);
static void prism2_release(u_long arg);
static int prism2_config(struct pcmcia_device *link);
static int prism2_pccard_card_present(local_info_t *local)
{
struct hostap_cs_priv *hw_priv = local->hw_priv;
if (hw_priv != NULL && hw_priv->link != NULL && pcmcia_dev_present(hw_priv->link))
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;
struct hostap_cs_priv *hw_priv = local->hw_priv;
res = pcmcia_write_config_byte(hw_priv->link, 0x10,
hw_priv->link->resource[0]->start & 0x00ff);
if (res != 0) {
printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -"
" res=%d\n", res);
}
udelay(10);
res = pcmcia_write_config_byte(hw_priv->link, 0x12,
(hw_priv->link->resource[0]->start >> 8) & 0x00ff);
if (res != 0) {
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;
struct hostap_interface *iface = netdev_priv(dev);
local_info_t *local = iface->local;
struct hostap_cs_priv *hw_priv = local->hw_priv;
if (resource_size(hw_priv->link->resource[0]) < 0x42) {
/* Not enough ports to be SanDisk multi-function card */
ret = -ENODEV;
goto done;
}
if (hw_priv->link->manf_id != 0xd601 || hw_priv->link->card_id != 0x0101) {
/* No SanDisk manfid found */
ret = -ENODEV;
goto done;
}
if (hw_priv->link->socket->functions < 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);
hw_priv->sandisk_connectplus = 1;
res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
COR_SOFT_RESET);
if (res != 0) {
printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
dev->name, res);
goto done;
}
mdelay(5);
/*
* Do not enable interrupts here to avoid some bogus events. Interrupts
* will be enabled during the first cor_sreset call.
*/
res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
(COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE |
COR_FUNC_ENA));
if (res != 0) {
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:
return ret;
}
static void prism2_pccard_cor_sreset(local_info_t *local)
{
int res;
u8 val;
struct hostap_cs_priv *hw_priv = local->hw_priv;
if (!prism2_pccard_card_present(local))
return;
res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &val);
if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n",
res);
return;
}
printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n",
val);
val |= COR_SOFT_RESET;
res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val);
if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n",
res);
return;
}
mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
val &= ~COR_SOFT_RESET;
if (hw_priv->sandisk_connectplus)
val |= COR_IREQ_ENA;
res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val);
if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n",
res);
return;
}
mdelay(hw_priv->sandisk_connectplus ? 5 : 2);
if (hw_priv->sandisk_connectplus)
sandisk_set_iobase(local);
}
static void prism2_pccard_genesis_reset(local_info_t *local, int hcr)
{
int res;
u8 old_cor;
struct hostap_cs_priv *hw_priv = local->hw_priv;
if (!prism2_pccard_card_present(local))
return;
if (hw_priv->sandisk_connectplus) {
sandisk_write_hcr(local, hcr);
return;
}
res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &old_cor);
if (res != 0) {
printk(KERN_DEBUG "%s failed 1 (%d)\n", __func__, res);
return;
}
printk(KERN_DEBUG "%s: original COR %02x\n", __func__, old_cor);
res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
old_cor | COR_SOFT_RESET);
if (res != 0) {
printk(KERN_DEBUG "%s failed 2 (%d)\n", __func__, res);
return;
}
mdelay(10);
/* Setup Genesis mode */
res = pcmcia_write_config_byte(hw_priv->link, CISREG_CCSR, hcr);
if (res != 0) {
printk(KERN_DEBUG "%s failed 3 (%d)\n", __func__, res);
return;
}
mdelay(10);
res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR,
old_cor & ~COR_SOFT_RESET);
if (res != 0) {
printk(KERN_DEBUG "%s failed 4 (%d)\n", __func__, res);
return;
}
mdelay(10);
}
static struct prism2_helper_functions prism2_pccard_funcs =
{
.card_present = prism2_pccard_card_present,
.cor_sreset = prism2_pccard_cor_sreset,
.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 int hostap_cs_probe(struct pcmcia_device *p_dev)
{
int ret;
PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
ret = prism2_config(p_dev);
if (ret) {
PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n");
}
return ret;
}
static void prism2_detach(struct pcmcia_device *link)
{
PDEBUG(DEBUG_FLOW, "prism2_detach\n");
prism2_release((u_long)link);
/* release net devices */
if (link->priv) {
struct hostap_cs_priv *hw_priv;
struct net_device *dev;
struct hostap_interface *iface;
dev = link->priv;
iface = netdev_priv(dev);
hw_priv = iface->local->hw_priv;
prism2_free_local_data(dev);
kfree(hw_priv);
}
}
static int prism2_config_check(struct pcmcia_device *p_dev, void *priv_data)
{
if (p_dev->config_index == 0)
return -EINVAL;
return pcmcia_request_io(p_dev);
}
static int prism2_config(struct pcmcia_device *link)
{
struct net_device *dev;
struct hostap_interface *iface;
local_info_t *local;
int ret = 1;
struct hostap_cs_priv *hw_priv;
unsigned long flags;
PDEBUG(DEBUG_FLOW, "prism2_config()\n");
hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
if (hw_priv == NULL) {
ret = -ENOMEM;
goto failed;
}
/* Look for an appropriate configuration table entry in the CIS */
link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO |
CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
if (ignore_cis_vcc)
link->config_flags &= ~CONF_AUTO_CHECK_VCC;
ret = pcmcia_loop_config(link, prism2_config_check, NULL);
if (ret) {
if (!ignore_cis_vcc)
printk(KERN_ERR "GetNextTuple(): No matching "
"CIS configuration. Maybe you need the "
"ignore_cis_vcc=1 parameter.\n");
goto failed;
}
/* Need to allocate net_device before requesting IRQ handler */
dev = prism2_init_local_data(&prism2_pccard_funcs, 0,
&link->dev);
if (dev == NULL)
goto failed;
link->priv = dev;
iface = netdev_priv(dev);
local = iface->local;
local->hw_priv = hw_priv;
hw_priv->link = link;
/*
* We enable IRQ here, but IRQ handler will not proceed
* until dev->base_addr is set below. This protect us from
* receive interrupts when driver is not initialized.
*/
ret = pcmcia_request_irq(link, prism2_interrupt);
if (ret)
goto failed;
ret = pcmcia_enable_device(link);
if (ret)
goto failed;
spin_lock_irqsave(&local->irq_init_lock, flags);
dev->irq = link->irq;
dev->base_addr = link->resource[0]->start;
spin_unlock_irqrestore(&local->irq_init_lock, flags);
local->shutdown = 0;
sandisk_enable_wireless(dev);
ret = prism2_hw_config(dev, 1);
if (!ret)
ret = hostap_hw_ready(dev);
return ret;
failed:
kfree(hw_priv);
prism2_release((u_long)link);
return ret;
}
static void prism2_release(u_long arg)
{
struct pcmcia_device *link = (struct pcmcia_device *)arg;
PDEBUG(DEBUG_FLOW, "prism2_release\n");
if (link->priv) {
struct net_device *dev = link->priv;
struct hostap_interface *iface;
iface = netdev_priv(dev);
prism2_hw_shutdown(dev, 0);
iface->local->shutdown = 1;
}
pcmcia_disable_device(link);
PDEBUG(DEBUG_FLOW, "release - done\n");
}
static int hostap_cs_suspend(struct pcmcia_device *link)
{
struct net_device *dev = (struct net_device *) link->priv;
int dev_open = 0;
struct hostap_interface *iface = NULL;
if (!dev)
return -ENODEV;
iface = netdev_priv(dev);
PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info);
if (iface && iface->local)
dev_open = iface->local->num_dev_open > 0;
if (dev_open) {
netif_stop_queue(dev);
netif_device_detach(dev);
}
prism2_suspend(dev);
return 0;
}
static int hostap_cs_resume(struct pcmcia_device *link)
{
struct net_device *dev = (struct net_device *) link->priv;
int dev_open = 0;
struct hostap_interface *iface = NULL;
if (!dev)
return -ENODEV;
iface = netdev_priv(dev);
PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info);
if (iface && iface->local)
dev_open = iface->local->num_dev_open > 0;
prism2_hw_shutdown(dev, 1);
prism2_hw_config(dev, dev_open ? 0 : 1);
if (dev_open) {
netif_device_attach(dev);
netif_start_queue(dev);
}
return 0;
}
static const struct pcmcia_device_id hostap_cs_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100),
PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300),
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777),
PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000),
PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002),
PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x3301),
PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002),
PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030b),
PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612),
PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613),
PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002),
PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002),
PCMCIA_DEVICE_MANF_CARD(0x02d2, 0x0001),
PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x0001),
PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300),
/* PCMCIA_DEVICE_MANF_CARD(0xc00f, 0x0000), conflict with pcnet_cs */
PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002),
PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002),
PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005),
PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010),
PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002),
PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF",
0x2d858104),
PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL",
0x74c5e40d),
PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil",
0x4b801a17),
PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.02",
0x4b74baa0),
PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus",
0x7a954bd9, 0x74be00c6),
PCMCIA_DEVICE_PROD_ID123(
"Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02",
0xe6ec52ce, 0x08649af2, 0x4b74baa0),
PCMCIA_DEVICE_PROD_ID123(
"Canon", "Wireless LAN CF Card K30225", "Version 01.00",
0x96ef6fe2, 0x263fcbab, 0xa57adb8c),
PCMCIA_DEVICE_PROD_ID123(
"D", "Link DWL-650 11Mbps WLAN Card", "Version 01.02",
0x71b18589, 0xb6f1b0ab, 0x4b74baa0),
PCMCIA_DEVICE_PROD_ID123(
"Instant Wireless ", " Network PC CARD", "Version 01.02",
0x11d901af, 0x6e9bd926, 0x4b74baa0),
PCMCIA_DEVICE_PROD_ID123(
"SMC", "SMC2632W", "Version 01.02",
0xc4f8b18b, 0x474a1f2a, 0x4b74baa0),
PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G",
0x2decece3, 0x82067c18),
PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card",
0x54f7c49c, 0x15a75e5b),
PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE",
0x74c5e40d, 0xdb472a18),
PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card",
0x0733cc81, 0x0c52f395),
PCMCIA_DEVICE_PROD_ID12(
"ZoomAir 11Mbps High", "Rate wireless Networking",
0x273fe3db, 0x32a1eaee),
PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card",
0xa37434e9, 0x9762e8f1),
PCMCIA_DEVICE_PROD_ID123(
"Pretec", "CompactWLAN Card 802.11b", "2.5",
0x1cadd3e5, 0xe697636c, 0x7a5bfcf1),
PCMCIA_DEVICE_PROD_ID123(
"U.S. Robotics", "IEEE 802.11b PC-CARD", "Version 01.02",
0xc7b8df9d, 0x1700d087, 0x4b74baa0),
PCMCIA_DEVICE_PROD_ID123(
"Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio",
"Ver. 1.00",
0x5cd01705, 0x4271660f, 0x9d08ee12),
PCMCIA_DEVICE_PROD_ID123(
"Wireless LAN" , "11Mbps PC Card", "Version 01.02",
0x4b8870ff, 0x70e946d1, 0x4b74baa0),
PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092),
PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2),
PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b),
PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39),
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids);
static struct pcmcia_driver hostap_driver = {
.name = "hostap_cs",
.probe = hostap_cs_probe,
.remove = prism2_detach,
.owner = THIS_MODULE,
.id_table = hostap_cs_ids,
.suspend = hostap_cs_suspend,
.resume = hostap_cs_resume,
};
module_pcmcia_driver(hostap_driver);

View File

@@ -0,0 +1,812 @@
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
{
__le16 *pos = (__le16 *) 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
{
__le16 *pos = (__le16 *) 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)
{
__le16 *pda = (__le16 *) 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;
}
#define prism2_download_aux_dump_npages 65536
struct prism2_download_aux_dump {
local_info_t *local;
u16 page[0x80];
};
static int prism2_download_aux_dump_proc_show(struct seq_file *m, void *v)
{
struct prism2_download_aux_dump *ctx = m->private;
hfa384x_from_aux(ctx->local->dev, (unsigned long)v - 1, 0x80, ctx->page);
seq_write(m, ctx->page, 0x80);
return 0;
}
static void *prism2_download_aux_dump_proc_start(struct seq_file *m, loff_t *_pos)
{
struct prism2_download_aux_dump *ctx = m->private;
prism2_enable_aux_port(ctx->local->dev, 1);
if (*_pos >= prism2_download_aux_dump_npages)
return NULL;
return (void *)((unsigned long)*_pos + 1);
}
static void *prism2_download_aux_dump_proc_next(struct seq_file *m, void *v, loff_t *_pos)
{
++*_pos;
if (*_pos >= prism2_download_aux_dump_npages)
return NULL;
return (void *)((unsigned long)*_pos + 1);
}
static void prism2_download_aux_dump_proc_stop(struct seq_file *m, void *v)
{
struct prism2_download_aux_dump *ctx = m->private;
prism2_enable_aux_port(ctx->local->dev, 0);
}
static const struct seq_operations prism2_download_aux_dump_proc_seqops = {
.start = prism2_download_aux_dump_proc_start,
.next = prism2_download_aux_dump_proc_next,
.stop = prism2_download_aux_dump_proc_stop,
.show = prism2_download_aux_dump_proc_show,
};
static int prism2_download_aux_dump_proc_open(struct inode *inode, struct file *file)
{
int ret = seq_open_private(file, &prism2_download_aux_dump_proc_seqops,
sizeof(struct prism2_download_aux_dump));
if (ret == 0) {
struct seq_file *m = file->private_data;
m->private = PDE_DATA(inode);
}
return ret;
}
static const struct file_operations prism2_download_aux_dump_proc_fops = {
.open = prism2_download_aux_dump_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
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 = 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 {
__le16 page;
__le16 offset;
__le16 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;
}
printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n",
le16_to_cpu(dlbuffer.len),
le16_to_cpu(dlbuffer.page),
le16_to_cpu(dlbuffer.offset));
bufaddr = (le16_to_cpu(dlbuffer.page) << 7) + le16_to_cpu(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 = kzalloc(sizeof(*dl) + param->num_areas *
sizeof(struct prism2_download_data_area), GFP_KERNEL);
if (dl == NULL) {
ret = -ENOMEM;
goto out;
}
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,507 @@
/* Host AP driver Info Frame processing (part of hostap.o module) */
#include <linux/if_arp.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/etherdevice.h>
#include "hostap_wlan.h"
#include "hostap.h"
#include "hostap_ap.h"
/* 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_hostscan_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 %pM\n",
dev->name, 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, ETH_ALEN);
req.channel = selected->chid;
spin_unlock_irqrestore(&local->lock, flags);
PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=%pM"
" channel=%d\n",
dev->name, 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, i;
unsigned long flags;
struct hfa384x_scan_result *res;
struct hfa384x_hostscan_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_hostscan_result),
GFP_ATOMIC);
if (results == NULL)
return;
/* Convert to hostscan result format. */
res = (struct hfa384x_scan_result *) pos;
for (i = 0; i < new_count; i++) {
memcpy(&results[i], &res[i],
sizeof(struct hfa384x_scan_result));
results[i].atim = 0;
}
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;
__le16 *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 = (__le16 *) 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 = kcalloc(new_count, sizeof(struct hfa384x_hostscan_result),
GFP_ATOMIC);
if (results == NULL)
return;
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_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);
}
#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 (le16_to_cpu(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, le16_to_cpu(info->len),
le16_to_cpu(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=%pM\n",
local->dev->name,
(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);
eth_zero_addr(wrqu.ap_addr.sa_data);
}
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);
if (local->host_roaming == 2 && local->iw_mode == IW_MODE_INFRA &&
!is_zero_ether_addr(local->preferred_ap)) {
/*
* Firmware seems to be getting into odd state in host_roaming
* mode 2 when hostscan is used without join command, so try
* to fix this by re-joining the current AP. This does not
* actually trigger a new association if the current AP is
* still in the scan results.
*/
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(struct work_struct *work)
{
local_info_t *local = container_of(work, local_info_t, info_queue);
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);
#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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,459 @@
#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/module.h>
#include <linux/if.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/slab.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 *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");
/* struct local_info::hw_priv */
struct hostap_pci_priv {
void __iomem *mem_start;
};
/* FIX: do we need mb/wmb/rmb with memory operations? */
static const struct pci_device_id prism2_pci_id_table[] = {
/* 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;
struct hostap_pci_priv *hw_priv;
local_info_t *local;
unsigned long flags;
iface = netdev_priv(dev);
local = iface->local;
hw_priv = local->hw_priv;
spin_lock_irqsave(&local->lock, flags);
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v);
writeb(v, hw_priv->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;
struct hostap_pci_priv *hw_priv;
local_info_t *local;
unsigned long flags;
u8 v;
iface = netdev_priv(dev);
local = iface->local;
hw_priv = local->hw_priv;
spin_lock_irqsave(&local->lock, flags);
v = readb(hw_priv->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;
struct hostap_pci_priv *hw_priv;
local_info_t *local;
unsigned long flags;
iface = netdev_priv(dev);
local = iface->local;
hw_priv = local->hw_priv;
spin_lock_irqsave(&local->lock, flags);
prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v);
writew(v, hw_priv->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;
struct hostap_pci_priv *hw_priv;
local_info_t *local;
unsigned long flags;
u16 v;
iface = netdev_priv(dev);
local = iface->local;
hw_priv = local->hw_priv;
spin_lock_irqsave(&local->lock, flags);
v = readw(hw_priv->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), le16_to_cpu((v)))
#define HFA384X_INW_DATA(a) cpu_to_le16(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;
struct hostap_pci_priv *hw_priv;
iface = netdev_priv(dev);
hw_priv = iface->local->hw_priv;
writeb(v, hw_priv->mem_start + a);
}
static inline u8 hfa384x_inb(struct net_device *dev, int a)
{
struct hostap_interface *iface;
struct hostap_pci_priv *hw_priv;
iface = netdev_priv(dev);
hw_priv = iface->local->hw_priv;
return readb(hw_priv->mem_start + a);
}
static inline void hfa384x_outw(struct net_device *dev, int a, u16 v)
{
struct hostap_interface *iface;
struct hostap_pci_priv *hw_priv;
iface = netdev_priv(dev);
hw_priv = iface->local->hw_priv;
writew(v, hw_priv->mem_start + a);
}
static inline u16 hfa384x_inw(struct net_device *dev, int a)
{
struct hostap_interface *iface;
struct hostap_pci_priv *hw_priv;
iface = netdev_priv(dev);
hw_priv = iface->local->hw_priv;
return readw(hw_priv->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), le16_to_cpu((v)))
#define HFA384X_INW_DATA(a) cpu_to_le16(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;
__le16 *pos;
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
pos = (__le16 *) 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;
__le16 *pos;
d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF;
pos = (__le16 *) 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,
.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;
struct hostap_pci_priv *hw_priv;
hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
if (hw_priv == NULL)
return -ENOMEM;
if (pci_enable_device(pdev))
goto err_out_free;
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 = pci_ioremap_bar(pdev, 0);
if (mem == NULL) {
printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ;
goto fail;
}
dev = prism2_init_local_data(&prism2_pci_funcs, cards_found,
&pdev->dev);
if (dev == NULL)
goto fail;
iface = netdev_priv(dev);
local = iface->local;
local->hw_priv = hw_priv;
cards_found++;
dev->irq = pdev->irq;
hw_priv->mem_start = mem;
dev->base_addr = (unsigned long) mem;
prism2_pci_cor_sreset(local);
pci_set_drvdata(pdev, dev);
if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, 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);
err_out_free:
kfree(hw_priv);
return -ENODEV;
}
static void prism2_pci_remove(struct pci_dev *pdev)
{
struct net_device *dev;
struct hostap_interface *iface;
void __iomem *mem_start;
struct hostap_pci_priv *hw_priv;
dev = pci_get_drvdata(pdev);
iface = netdev_priv(dev);
hw_priv = iface->local->hw_priv;
/* 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 = hw_priv->mem_start;
prism2_free_local_data(dev);
kfree(hw_priv);
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, pm_message_t 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, PCI_D3hot);
return 0;
}
static int prism2_pci_resume(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
int err;
err = pci_enable_device(pdev);
if (err) {
printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
dev->name);
return err;
}
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_driver = {
.name = "hostap_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 */
};
module_pci_driver(prism2_pci_driver);

View File

@@ -0,0 +1,618 @@
#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/module.h>
#include <linux/if.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/slab.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 *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");
/* struct local_info::hw_priv */
struct hostap_plx_priv {
void __iomem *attr_mem;
unsigned int cor_offset;
};
#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 const struct pci_device_id prism2_plx_id_table[] = {
PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"),
PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"),
PLXDEV(0x126c, 0x8030, "Nortel emobility"),
PLXDEV(0x1562, 0x0001, "Symbol LA-4123"),
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, 0x1100, "Global Sun Tech GL24110P"),
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;
struct hostap_plx_priv *hw_priv = local->hw_priv;
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 (hw_priv->attr_mem == NULL) {
/* TMD7160 - COR at card's first I/O addr */
corsave = inb(hw_priv->cor_offset);
outb(corsave | COR_SRESET, hw_priv->cor_offset);
mdelay(2);
outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
mdelay(2);
} else {
/* PLX9052 */
corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
writeb(corsave | COR_SRESET,
hw_priv->attr_mem + hw_priv->cor_offset);
mdelay(2);
writeb(corsave & ~COR_SRESET,
hw_priv->attr_mem + hw_priv->cor_offset);
mdelay(2);
}
}
static void prism2_plx_genesis_reset(local_info_t *local, int hcr)
{
unsigned char corsave;
struct hostap_plx_priv *hw_priv = local->hw_priv;
if (hw_priv->attr_mem == NULL) {
/* TMD7160 - COR at card's first I/O addr */
corsave = inb(hw_priv->cor_offset);
outb(corsave | COR_SRESET, hw_priv->cor_offset);
mdelay(10);
outb(hcr, hw_priv->cor_offset + 2);
mdelay(10);
outb(corsave & ~COR_SRESET, hw_priv->cor_offset);
mdelay(10);
} else {
/* PLX9052 */
corsave = readb(hw_priv->attr_mem + hw_priv->cor_offset);
writeb(corsave | COR_SRESET,
hw_priv->attr_mem + hw_priv->cor_offset);
mdelay(10);
writeb(hcr, hw_priv->attr_mem + hw_priv->cor_offset + 2);
mdelay(10);
writeb(corsave & ~COR_SRESET,
hw_priv->attr_mem + hw_priv->cor_offset);
mdelay(10);
}
}
static struct prism2_helper_functions prism2_plx_funcs =
{
.card_present = NULL,
.cor_sreset = prism2_plx_cor_sreset,
.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 + 2 + cis[pos + 1] > CIS_MAX_LEN)
goto cis_error;
switch (cis[pos]) {
case CISTPL_CONFIG:
if (cis[pos + 1] < 2)
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 = 0, cor_index = 0;
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;
struct hostap_plx_priv *hw_priv;
hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
if (hw_priv == NULL)
return -ENOMEM;
if (pci_enable_device(pdev))
goto err_out_free;
/* 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,
&pdev->dev);
if (dev == NULL)
goto fail;
iface = netdev_priv(dev);
local = iface->local;
local->hw_priv = hw_priv;
cards_found++;
dev->irq = pdev->irq;
dev->base_addr = pccard_ioaddr;
hw_priv->attr_mem = attr_mem;
hw_priv->cor_offset = cor_offset;
pci_set_drvdata(pdev, dev);
if (request_irq(dev->irq, prism2_interrupt, IRQF_SHARED, 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:
if (irq_registered && dev)
free_irq(dev->irq, dev);
if (attr_mem)
iounmap(attr_mem);
pci_disable_device(pdev);
prism2_free_local_data(dev);
err_out_free:
kfree(hw_priv);
return -ENODEV;
}
static void prism2_plx_remove(struct pci_dev *pdev)
{
struct net_device *dev;
struct hostap_interface *iface;
struct hostap_plx_priv *hw_priv;
dev = pci_get_drvdata(pdev);
iface = netdev_priv(dev);
hw_priv = iface->local->hw_priv;
/* Reset the hardware, and ensure interrupts are disabled. */
prism2_plx_cor_sreset(iface->local);
hfa384x_disable_interrupts(dev);
if (hw_priv->attr_mem)
iounmap(hw_priv->attr_mem);
if (dev->irq)
free_irq(dev->irq, dev);
prism2_free_local_data(dev);
kfree(hw_priv);
pci_disable_device(pdev);
}
MODULE_DEVICE_TABLE(pci, prism2_plx_id_table);
static struct pci_driver prism2_plx_driver = {
.name = "hostap_plx",
.id_table = prism2_plx_id_table,
.probe = prism2_plx_probe,
.remove = prism2_plx_remove,
};
module_pci_driver(prism2_plx_driver);

View File

@@ -0,0 +1,499 @@
/* /proc routines for Host AP driver */
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/export.h>
#include <net/lib80211.h>
#include "hostap_wlan.h"
#include "hostap.h"
#define PROC_LIMIT (PAGE_SIZE - 80)
#ifndef PRISM2_NO_PROCFS_DEBUG
static int prism2_debug_proc_show(struct seq_file *m, void *v)
{
local_info_t *local = m->private;
int i;
seq_printf(m, "next_txfid=%d next_alloc=%d\n",
local->next_txfid, local->next_alloc);
for (i = 0; i < PRISM2_TXFID_COUNT; i++)
seq_printf(m, "FID: tx=%04X intransmit=%04X\n",
local->txfid[i], local->intransmitfid[i]);
seq_printf(m, "FW TX rate control: %d\n", local->fw_tx_rate_control);
seq_printf(m, "beacon_int=%d\n", local->beacon_int);
seq_printf(m, "dtim_period=%d\n", local->dtim_period);
seq_printf(m, "wds_max_connections=%d\n", local->wds_max_connections);
seq_printf(m, "dev_enabled=%d\n", local->dev_enabled);
seq_printf(m, "sw_tick_stuck=%d\n", local->sw_tick_stuck);
for (i = 0; i < WEP_KEYS; i++) {
if (local->crypt_info.crypt[i] &&
local->crypt_info.crypt[i]->ops) {
seq_printf(m, "crypt[%d]=%s\n", i,
local->crypt_info.crypt[i]->ops->name);
}
}
seq_printf(m, "pri_only=%d\n", local->pri_only);
seq_printf(m, "pci=%d\n", local->func->hw_type == HOSTAP_HW_PCI);
seq_printf(m, "sram_type=%d\n", local->sram_type);
seq_printf(m, "no_pri=%d\n", local->no_pri);
return 0;
}
static int prism2_debug_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, prism2_debug_proc_show, PDE_DATA(inode));
}
static const struct file_operations prism2_debug_proc_fops = {
.open = prism2_debug_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif /* PRISM2_NO_PROCFS_DEBUG */
static int prism2_stats_proc_show(struct seq_file *m, void *v)
{
local_info_t *local = m->private;
struct comm_tallies_sums *sums = &local->comm_tallies;
seq_printf(m, "TxUnicastFrames=%u\n", sums->tx_unicast_frames);
seq_printf(m, "TxMulticastframes=%u\n", sums->tx_multicast_frames);
seq_printf(m, "TxFragments=%u\n", sums->tx_fragments);
seq_printf(m, "TxUnicastOctets=%u\n", sums->tx_unicast_octets);
seq_printf(m, "TxMulticastOctets=%u\n", sums->tx_multicast_octets);
seq_printf(m, "TxDeferredTransmissions=%u\n",
sums->tx_deferred_transmissions);
seq_printf(m, "TxSingleRetryFrames=%u\n", sums->tx_single_retry_frames);
seq_printf(m, "TxMultipleRetryFrames=%u\n",
sums->tx_multiple_retry_frames);
seq_printf(m, "TxRetryLimitExceeded=%u\n",
sums->tx_retry_limit_exceeded);
seq_printf(m, "TxDiscards=%u\n", sums->tx_discards);
seq_printf(m, "RxUnicastFrames=%u\n", sums->rx_unicast_frames);
seq_printf(m, "RxMulticastFrames=%u\n", sums->rx_multicast_frames);
seq_printf(m, "RxFragments=%u\n", sums->rx_fragments);
seq_printf(m, "RxUnicastOctets=%u\n", sums->rx_unicast_octets);
seq_printf(m, "RxMulticastOctets=%u\n", sums->rx_multicast_octets);
seq_printf(m, "RxFCSErrors=%u\n", sums->rx_fcs_errors);
seq_printf(m, "RxDiscardsNoBuffer=%u\n", sums->rx_discards_no_buffer);
seq_printf(m, "TxDiscardsWrongSA=%u\n", sums->tx_discards_wrong_sa);
seq_printf(m, "RxDiscardsWEPUndecryptable=%u\n",
sums->rx_discards_wep_undecryptable);
seq_printf(m, "RxMessageInMsgFragments=%u\n",
sums->rx_message_in_msg_fragments);
seq_printf(m, "RxMessageInBadMsgFragments=%u\n",
sums->rx_message_in_bad_msg_fragments);
/* FIX: this may grow too long for one page(?) */
return 0;
}
static int prism2_stats_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, prism2_stats_proc_show, PDE_DATA(inode));
}
static const struct file_operations prism2_stats_proc_fops = {
.open = prism2_stats_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int prism2_wds_proc_show(struct seq_file *m, void *v)
{
struct list_head *ptr = v;
struct hostap_interface *iface;
iface = list_entry(ptr, struct hostap_interface, list);
if (iface->type == HOSTAP_INTERFACE_WDS)
seq_printf(m, "%s\t%pM\n",
iface->dev->name, iface->u.wds.remote_addr);
return 0;
}
static void *prism2_wds_proc_start(struct seq_file *m, loff_t *_pos)
{
local_info_t *local = m->private;
read_lock_bh(&local->iface_lock);
return seq_list_start(&local->hostap_interfaces, *_pos);
}
static void *prism2_wds_proc_next(struct seq_file *m, void *v, loff_t *_pos)
{
local_info_t *local = m->private;
return seq_list_next(v, &local->hostap_interfaces, _pos);
}
static void prism2_wds_proc_stop(struct seq_file *m, void *v)
{
local_info_t *local = m->private;
read_unlock_bh(&local->iface_lock);
}
static const struct seq_operations prism2_wds_proc_seqops = {
.start = prism2_wds_proc_start,
.next = prism2_wds_proc_next,
.stop = prism2_wds_proc_stop,
.show = prism2_wds_proc_show,
};
static int prism2_wds_proc_open(struct inode *inode, struct file *file)
{
int ret = seq_open(file, &prism2_wds_proc_seqops);
if (ret == 0) {
struct seq_file *m = file->private_data;
m->private = PDE_DATA(inode);
}
return ret;
}
static const struct file_operations prism2_wds_proc_fops = {
.open = prism2_wds_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int prism2_bss_list_proc_show(struct seq_file *m, void *v)
{
local_info_t *local = m->private;
struct list_head *ptr = v;
struct hostap_bss_info *bss;
if (ptr == &local->bss_list) {
seq_printf(m, "#BSSID\tlast_update\tcount\tcapab_info\tSSID(txt)\t"
"SSID(hex)\tWPA IE\n");
return 0;
}
bss = list_entry(ptr, struct hostap_bss_info, list);
seq_printf(m, "%pM\t%lu\t%u\t0x%x\t",
bss->bssid, bss->last_update,
bss->count, bss->capab_info);
seq_printf(m, "%*pE", (int)bss->ssid_len, bss->ssid);
seq_putc(m, '\t');
seq_printf(m, "%*phN", (int)bss->ssid_len, bss->ssid);
seq_putc(m, '\t');
seq_printf(m, "%*phN", (int)bss->wpa_ie_len, bss->wpa_ie);
seq_putc(m, '\n');
return 0;
}
static void *prism2_bss_list_proc_start(struct seq_file *m, loff_t *_pos)
{
local_info_t *local = m->private;
spin_lock_bh(&local->lock);
return seq_list_start_head(&local->bss_list, *_pos);
}
static void *prism2_bss_list_proc_next(struct seq_file *m, void *v, loff_t *_pos)
{
local_info_t *local = m->private;
return seq_list_next(v, &local->bss_list, _pos);
}
static void prism2_bss_list_proc_stop(struct seq_file *m, void *v)
{
local_info_t *local = m->private;
spin_unlock_bh(&local->lock);
}
static const struct seq_operations prism2_bss_list_proc_seqops = {
.start = prism2_bss_list_proc_start,
.next = prism2_bss_list_proc_next,
.stop = prism2_bss_list_proc_stop,
.show = prism2_bss_list_proc_show,
};
static int prism2_bss_list_proc_open(struct inode *inode, struct file *file)
{
int ret = seq_open(file, &prism2_bss_list_proc_seqops);
if (ret == 0) {
struct seq_file *m = file->private_data;
m->private = PDE_DATA(inode);
}
return ret;
}
static const struct file_operations prism2_bss_list_proc_fops = {
.open = prism2_bss_list_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int prism2_crypt_proc_show(struct seq_file *m, void *v)
{
local_info_t *local = m->private;
int i;
seq_printf(m, "tx_keyidx=%d\n", local->crypt_info.tx_keyidx);
for (i = 0; i < WEP_KEYS; i++) {
if (local->crypt_info.crypt[i] &&
local->crypt_info.crypt[i]->ops &&
local->crypt_info.crypt[i]->ops->print_stats) {
local->crypt_info.crypt[i]->ops->print_stats(
m, local->crypt_info.crypt[i]->priv);
}
}
return 0;
}
static int prism2_crypt_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, prism2_crypt_proc_show, PDE_DATA(inode));
}
static const struct file_operations prism2_crypt_proc_fops = {
.open = prism2_crypt_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static ssize_t prism2_pda_proc_read(struct file *file, char __user *buf,
size_t count, loff_t *_pos)
{
local_info_t *local = PDE_DATA(file_inode(file));
size_t off;
if (local->pda == NULL || *_pos >= PRISM2_PDA_SIZE)
return 0;
off = *_pos;
if (count > PRISM2_PDA_SIZE - off)
count = PRISM2_PDA_SIZE - off;
if (copy_to_user(buf, local->pda + off, count) != 0)
return -EFAULT;
*_pos += count;
return count;
}
static const struct file_operations prism2_pda_proc_fops = {
.read = prism2_pda_proc_read,
.llseek = generic_file_llseek,
};
static ssize_t prism2_aux_dump_proc_no_read(struct file *file, char __user *buf,
size_t bufsize, loff_t *_pos)
{
return 0;
}
static const struct file_operations prism2_aux_dump_proc_fops = {
.read = prism2_aux_dump_proc_no_read,
};
#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_show(struct seq_file *m, void *v)
{
local_info_t *local = m->private;
unsigned long entry;
int i, len;
struct hfa384x_hostscan_result *scanres;
u8 *p;
if (v == SEQ_START_TOKEN) {
seq_printf(m,
"CHID ANL SL BcnInt Capab Rate BSSID ATIM SupRates SSID\n");
return 0;
}
entry = (unsigned long)v - 2;
scanres = &local->last_scan_results[entry];
seq_printf(m, "%d %d %d %d 0x%02x %d %pM %d ",
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),
scanres->bssid,
le16_to_cpu(scanres->atim));
p = scanres->sup_rates;
for (i = 0; i < sizeof(scanres->sup_rates); i++) {
if (p[i] == 0)
break;
seq_printf(m, "<%02x>", p[i]);
}
seq_putc(m, ' ');
p = scanres->ssid;
len = le16_to_cpu(scanres->ssid_len);
if (len > 32)
len = 32;
for (i = 0; i < len; i++) {
unsigned char c = p[i];
if (c >= 32 && c < 127)
seq_putc(m, c);
else
seq_printf(m, "<%02x>", c);
}
seq_putc(m, '\n');
return 0;
}
static void *prism2_scan_results_proc_start(struct seq_file *m, loff_t *_pos)
{
local_info_t *local = m->private;
spin_lock_bh(&local->lock);
/* We have a header (pos 0) + N results to show (pos 1...N) */
if (*_pos > local->last_scan_results_count)
return NULL;
return (void *)(unsigned long)(*_pos + 1); /* 0 would be EOF */
}
static void *prism2_scan_results_proc_next(struct seq_file *m, void *v, loff_t *_pos)
{
local_info_t *local = m->private;
++*_pos;
if (*_pos > local->last_scan_results_count)
return NULL;
return (void *)(unsigned long)(*_pos + 1); /* 0 would be EOF */
}
static void prism2_scan_results_proc_stop(struct seq_file *m, void *v)
{
local_info_t *local = m->private;
spin_unlock_bh(&local->lock);
}
static const struct seq_operations prism2_scan_results_proc_seqops = {
.start = prism2_scan_results_proc_start,
.next = prism2_scan_results_proc_next,
.stop = prism2_scan_results_proc_stop,
.show = prism2_scan_results_proc_show,
};
static int prism2_scan_results_proc_open(struct inode *inode, struct file *file)
{
int ret = seq_open(file, &prism2_scan_results_proc_seqops);
if (ret == 0) {
struct seq_file *m = file->private_data;
m->private = PDE_DATA(inode);
}
return ret;
}
static const struct file_operations prism2_scan_results_proc_fops = {
.open = prism2_scan_results_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#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
proc_create_data("debug", 0, local->proc,
&prism2_debug_proc_fops, local);
#endif /* PRISM2_NO_PROCFS_DEBUG */
proc_create_data("stats", 0, local->proc,
&prism2_stats_proc_fops, local);
proc_create_data("wds", 0, local->proc,
&prism2_wds_proc_fops, local);
proc_create_data("pda", 0, local->proc,
&prism2_pda_proc_fops, local);
proc_create_data("aux_dump", 0, local->proc,
local->func->read_aux_fops ?: &prism2_aux_dump_proc_fops,
local);
proc_create_data("bss_list", 0, local->proc,
&prism2_bss_list_proc_fops, local);
proc_create_data("crypt", 0, local->proc,
&prism2_crypt_proc_fops, local);
#ifdef PRISM2_IO_DEBUG
proc_create_data("io_debug", 0, local->proc,
&prism2_io_debug_proc_fops, local);
#endif /* PRISM2_IO_DEBUG */
#ifndef PRISM2_NO_STATION_MODES
proc_create_data("scan_results", 0, local->proc,
&prism2_scan_results_proc_fops, local);
#endif /* PRISM2_NO_STATION_MODES */
}
void hostap_remove_proc(local_info_t *local)
{
proc_remove(local->proc);
}
EXPORT_SYMBOL(hostap_init_proc);
EXPORT_SYMBOL(hostap_remove_proc);

File diff suppressed because it is too large Load Diff