summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/intersil
diff options
context:
space:
mode:
authorKalle Valo <kvalo@codeaurora.org>2015-11-18 09:42:58 +0200
committerKalle Valo <kvalo@codeaurora.org>2015-11-18 14:28:30 +0200
commiteb4f98d5deb9b3a36e8b3ab7a9dec1b5ebc76e75 (patch)
treeb76fb98a7e16814626782e643b84e6fbf174d408 /drivers/net/wireless/intersil
parent6948300c79dba2b6c7b54af43d1924f51e47e017 (diff)
hostap: move under intersil vendor directory
Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Diffstat (limited to 'drivers/net/wireless/intersil')
-rw-r--r--drivers/net/wireless/intersil/Kconfig16
-rw-r--r--drivers/net/wireless/intersil/Makefile1
-rw-r--r--drivers/net/wireless/intersil/hostap/Kconfig98
-rw-r--r--drivers/net/wireless/intersil/hostap/Makefile7
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap.h95
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211.h96
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211_rx.c1117
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_80211_tx.c553
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ap.c3339
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ap.h263
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_common.h419
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_config.h48
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_cs.c708
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_download.c812
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_hw.c3424
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_info.c507
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_ioctl.c4054
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_main.c1140
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_pci.c459
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_plx.c618
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_proc.c499
-rw-r--r--drivers/net/wireless/intersil/hostap/hostap_wlan.h1047
22 files changed, 19320 insertions, 0 deletions
diff --git a/drivers/net/wireless/intersil/Kconfig b/drivers/net/wireless/intersil/Kconfig
new file mode 100644
index 000000000000..ec80b9117fd2
--- /dev/null
+++ b/drivers/net/wireless/intersil/Kconfig
@@ -0,0 +1,16 @@
+config WLAN_VENDOR_INTERSIL
+ bool "Intersil devices"
+ default y
+ ---help---
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_INTERSIL
+
+source "drivers/net/wireless/intersil/hostap/Kconfig"
+
+endif # WLAN_VENDOR_INTERSIL
diff --git a/drivers/net/wireless/intersil/Makefile b/drivers/net/wireless/intersil/Makefile
new file mode 100644
index 000000000000..4890ef79f280
--- /dev/null
+++ b/drivers/net/wireless/intersil/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HOSTAP) += hostap/
diff --git a/drivers/net/wireless/intersil/hostap/Kconfig b/drivers/net/wireless/intersil/hostap/Kconfig
new file mode 100644
index 000000000000..287d82728bc3
--- /dev/null
+++ b/drivers/net/wireless/intersil/hostap/Kconfig
@@ -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.
diff --git a/drivers/net/wireless/intersil/hostap/Makefile b/drivers/net/wireless/intersil/hostap/Makefile
new file mode 100644
index 000000000000..b8e41a702c00
--- /dev/null
+++ b/drivers/net/wireless/intersil/hostap/Makefile
@@ -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
diff --git a/drivers/net/wireless/intersil/hostap/hostap.h b/drivers/net/wireless/intersil/hostap/hostap.h
new file mode 100644
index 000000000000..ce8721fbc10e
--- /dev/null
+++ b/drivers/net/wireless/intersil/hostap/hostap.h
@@ -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 */
diff --git a/drivers/net/wireless/intersil/hostap/hostap_80211.h b/drivers/net/wireless/intersil/hostap/hostap_80211.h
new file mode 100644
index 000000000000..ed98ce7c8f65
--- /dev/null
+++ b/drivers/net/wireless/intersil/hostap/hostap_80211.h
@@ -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 */
diff --git a/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c
new file mode 100644
index 000000000000..599f30f22841
--- /dev/null
+++ b/drivers/net/wireless/intersil/hostap/hostap_80211_rx.c
@@ -0,0 +1,1117 @@
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <net/lib80211.h>
+#include <linux/if_arp.h>
+
+#include "hostap_80211.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_rx_80211(const char *name, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+ printk(KERN_DEBUG "%s: RX signal=%d noise=%d rate=%d len=%d "
+ "jiffies=%ld\n",
+ name, rx_stats->signal, rx_stats->noise, rx_stats->rate,
+ 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");
+}
+
+
+/* Send RX frame to netif with 802.11 (and possible prism) header.
+ * Called from hardware or software IRQ context. */
+int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats, int type)
+{
+ struct hostap_interface *iface;
+ local_info_t *local;
+ int hdrlen, phdrlen, head_need, tail_need;
+ u16 fc;
+ int prism_header, ret;
+ struct ieee80211_hdr *fhdr;
+
+ iface = netdev_priv(dev);
+ local = iface->local;
+
+ if (dev->type == ARPHRD_IEEE80211_PRISM) {
+ if (local->monitor_type == PRISM2_MONITOR_PRISM) {
+ prism_header = 1;
+ phdrlen = sizeof(struct linux_wlan_ng_prism_hdr);
+ } else { /* local->monitor_type == PRISM2_MONITOR_CAPHDR */
+ prism_header = 2;
+ phdrlen = sizeof(struct linux_wlan_ng_cap_hdr);
+ }
+ } else if (dev->type == ARPHRD_IEEE80211_RADIOTAP) {
+ prism_header = 3;
+ phdrlen = sizeof(struct hostap_radiotap_rx);
+ } else {
+ prism_header = 0;
+ phdrlen = 0;
+ }
+
+ fhdr = (struct ieee80211_hdr *) skb->data;
+ fc = le16_to_cpu(fhdr->frame_control);
+
+ if (type == PRISM2_RX_MGMT && (fc & IEEE80211_FCTL_VERS)) {
+ printk(KERN_DEBUG "%s: dropped management frame with header "
+ "version %d\n", dev->name, fc & IEEE80211_FCTL_VERS);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ hdrlen = hostap_80211_get_hdrlen(fhdr->frame_control);
+
+ /* check if there is enough room for extra data; if not, expand skb
+ * buffer to be large enough for the changes */
+ head_need = phdrlen;
+ tail_need = 0;
+#ifdef PRISM2_ADD_BOGUS_CRC
+ tail_need += 4;
+#endif /* PRISM2_ADD_BOGUS_CRC */
+
+ head_need -= skb_headroom(skb);
+ tail_need -= skb_tailroom(skb);
+
+ if (head_need > 0 || tail_need > 0) {
+ if (pskb_expand_head(skb, head_need > 0 ? head_need : 0,
+ tail_need > 0 ? tail_need : 0,
+ GFP_ATOMIC)) {
+ printk(KERN_DEBUG "%s: prism2_rx_80211 failed to "
+ "reallocate skb buffer\n", dev->name);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+ }
+
+ /* We now have an skb with enough head and tail room, so just insert
+ * the extra data */
+
+#ifdef PRISM2_ADD_BOGUS_CRC
+ memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */
+#endif /* PRISM2_ADD_BOGUS_CRC */
+
+ if (prism_header == 1) {
+ struct linux_wlan_ng_prism_hdr *hdr;
+ hdr = (struct linux_wlan_ng_prism_hdr *)
+ skb_push(skb, phdrlen);
+ memset(hdr, 0, phdrlen);
+ hdr->msgcode = LWNG_CAP_DID_BASE;
+ hdr->msglen = sizeof(*hdr);
+ memcpy(hdr->devname, dev->name, sizeof(hdr->devname));
+#define LWNG_SETVAL(f,i,s,l,d) \
+hdr->f.did = LWNG_CAP_DID_BASE | (i << 12); \
+hdr->f.status = s; hdr->f.len = l; hdr->f.data = d
+ LWNG_SETVAL(hosttime, 1, 0, 4, jiffies);
+ LWNG_SETVAL(mactime, 2, 0, 4, rx_stats->mac_time);
+ LWNG_SETVAL(channel, 3, 1 /* no value */, 4, 0);
+ LWNG_SETVAL(rssi, 4, 1 /* no value */, 4, 0);
+ LWNG_SETVAL(sq, 5, 1 /* no value */, 4, 0);
+ LWNG_SETVAL(signal, 6, 0, 4, rx_stats->signal);
+ LWNG_SETVAL(noise, 7, 0, 4, rx_stats->noise);
+ LWNG_SETVAL(rate, 8, 0, 4, rx_stats->rate / 5);
+ LWNG_SETVAL(istx, 9, 0, 4, 0);
+ LWNG_SETVAL(frmlen, 10, 0, 4, skb->len - phdrlen);
+#undef LWNG_SETVAL
+ } else if (prism_header == 2) {
+ struct linux_wlan_ng_cap_hdr *hdr;
+ hdr = (struct linux_wlan_ng_cap_hdr *)
+ skb_push(skb, phdrlen);
+ memset(hdr, 0, phdrlen);
+ hdr->version = htonl(LWNG_CAPHDR_VERSION);
+ hdr->length = htonl(phdrlen);
+ hdr->mactime = __cpu_to_be64(rx_stats->mac_time);
+ hdr->hosttime = __cpu_to_be64(jiffies);
+ hdr->phytype = htonl(4); /* dss_dot11_b */
+ hdr->channel = htonl(local->channel);
+ hdr->datarate = htonl(rx_stats->rate);
+ hdr->antenna = htonl(0); /* unknown */
+ hdr->priority = htonl(0); /* unknown */
+ hdr->ssi_type = htonl(3); /* raw */
+ hdr->ssi_signal = htonl(rx_stats->signal);
+ hdr->ssi_noise = htonl(rx_stats->noise);
+ hdr->preamble = htonl(0); /* unknown */
+ hdr->encoding = htonl(1); /* cck */
+ } else if (prism_header == 3) {
+ struct hostap_radiotap_rx *hdr;
+ hdr = (struct hostap_radiotap_rx *)skb_push(skb, phdrlen);
+ memset(hdr, 0, phdrlen);
+ hdr->hdr.it_len = cpu_to_le16(phdrlen);
+ hdr->hdr.it_present =
+ cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) |
+ (1 << IEEE80211_RADIOTAP_CHANNEL) |
+ (1 << IEEE80211_RADIOTAP_RATE) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE));
+ hdr->tsft = cpu_to_le64(rx_stats->mac_time);
+ hdr->chan_freq = cpu_to_le16(freq_list[local->channel - 1]);
+ hdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_CCK |
+ IEEE80211_CHAN_2GHZ);
+ hdr->rate = rx_stats->rate / 5;
+ hdr->dbm_antsignal = rx_stats->signal;
+ hdr->dbm_antnoise = rx_stats->noise;
+ }
+
+ ret = skb->len - phdrlen;
+ skb->dev = dev;
+ skb_reset_mac_header(skb);
+ skb_pull(skb, hdrlen);
+ if (prism_header)
+ skb_pull(skb, phdrlen);
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = cpu_to_be16(ETH_P_802_2);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_rx(skb);
+
+ return ret;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static void monitor_rx(struct net_device *dev, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats)
+{
+ int len;
+
+ len = prism2_rx_80211(dev, skb, rx_stats, PRISM2_RX_MONITOR);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct prism2_frag_entry *
+prism2_frag_cache_find(local_info_t *local, unsigned int seq,
+ unsigned int frag, u8 *src, u8 *dst)
+{
+ struct prism2_frag_entry *entry;
+ int i;
+
+ for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) {
+ entry = &local->frag_cache[i];
+ if (entry->skb != NULL &&
+ time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
+ printk(KERN_DEBUG "%s: expiring fragment cache entry "
+ "seq=%u last_frag=%u\n",
+ local->dev->name, entry->seq, entry->last_frag);
+ dev_kfree_skb(entry->skb);
+ entry->skb = NULL;
+ }
+
+ if (entry->skb != NULL && entry->seq == seq &&
+ (entry->last_frag + 1 == frag || frag == -1) &&
+ memcmp(entry->src_addr, src, ETH_ALEN) == 0 &&
+ memcmp(entry->dst_addr, dst, ETH_ALEN) == 0)
+ return entry;
+ }
+
+ return NULL;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct sk_buff *
+prism2_frag_cache_get(local_info_t *local, struct ieee80211_hdr *hdr)
+{
+ struct sk_buff *skb = NULL;
+ u16 sc;
+ unsigned int frag, seq;
+ struct prism2_frag_entry *entry;
+
+ sc = le16_to_cpu(hdr->seq_ctrl);
+ frag = sc & IEEE80211_SCTL_FRAG;
+ seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
+
+ if (frag == 0) {
+ /* Reserve enough space to fit maximum frame length */
+ skb = dev_alloc_skb(local->dev->mtu +
+ sizeof(struct ieee80211_hdr) +
+ 8 /* LLC */ +
+ 2 /* alignment */ +
+ 8 /* WEP */ + ETH_ALEN /* WDS */);
+ if (skb == NULL)
+ return NULL;
+
+ entry = &local->frag_cache[local->frag_next_idx];
+ local->frag_next_idx++;
+ if (local->frag_next_idx >= PRISM2_FRAG_CACHE_LEN)
+ local->frag_next_idx = 0;
+
+ if (entry->skb != NULL)
+ dev_kfree_skb(entry->skb);
+
+ entry->first_frag_time = jiffies;
+ entry->seq = seq;
+ entry->last_frag = frag;
+ entry->skb = skb;
+ memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
+ } else {
+ /* received a fragment of a frame for which the head fragment
+ * should have already been received */
+ entry = prism2_frag_cache_find(local, seq, frag, hdr->addr2,
+ hdr->addr1);
+ if (entry != NULL) {
+ entry->last_frag = frag;
+ skb = entry->skb;
+ }
+ }
+
+ return skb;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static int prism2_frag_cache_invalidate(local_info_t *local,
+ struct ieee80211_hdr *hdr)
+{
+ u16 sc;
+ unsigned int seq;
+ struct prism2_frag_entry *entry;
+
+ sc = le16_to_cpu(hdr->seq_ctrl);
+ seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
+
+ entry = prism2_frag_cache_find(local, seq, -1, hdr->addr2, hdr->addr1);
+
+ if (entry == NULL) {
+ printk(KERN_DEBUG "%s: could not invalidate fragment cache "
+ "entry (seq=%u)\n",
+ local->dev->name, seq);
+ return -1;
+ }
+
+ entry->skb = NULL;
+ return 0;
+}
+
+
+static struct hostap_bss_info *__hostap_get_bss(local_info_t *local, u8 *bssid,
+ u8 *ssid, size_t ssid_len)
+{
+ struct list_head *ptr;
+ struct hostap_bss_info *bss;
+
+ list_for_each(ptr, &local->bss_list) {
+ bss = list_entry(ptr, struct hostap_bss_info, list);
+ if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+ (ssid == NULL ||
+ (ssid_len == bss->ssid_len &&
+ memcmp(ssid, bss->ssid, ssid_len) == 0))) {
+ list_move(&bss->list, &local->bss_list);
+ return bss;
+ }
+ }
+
+ return NULL;
+}
+
+
+static struct hostap_bss_info *__hostap_add_bss(local_info_t *local, u8 *bssid,
+ u8 *ssid, size_t ssid_len)
+{
+ struct hostap_bss_info *bss;
+
+ if (local->num_bss_info >= HOSTAP_MAX_BSS_COUNT) {
+ bss = list_entry(local->bss_list.prev,
+ struct hostap_bss_info, list);
+ list_del(&bss->list);
+ local->num_bss_info--;
+ } else {
+ bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
+ if (bss == NULL)
+ return NULL;
+ }
+
+ memset(bss, 0, sizeof(*bss));
+ memcpy(bss->bssid, bssid, ETH_ALEN);
+ memcpy(bss->ssid, ssid, ssid_len);
+ bss->ssid_len = ssid_len;
+ local->num_bss_info++;
+ list_add(&bss->list, &local->bss_list);
+ return bss;
+}
+
+
+static void __hostap_expire_bss(local_info_t *local)
+{
+ struct hostap_bss_info *bss;
+
+ while (local->num_bss_info > 0) {
+ bss = list_entry(local->bss_list.prev,
+ struct hostap_bss_info, list);
+ if (!time_after(jiffies, bss->last_update + 60 * HZ))
+ break;
+
+ list_del(&bss->list);
+ local->num_bss_info--;
+ kfree(bss);
+ }
+}
+
+
+/* Both IEEE 802.11 Beacon and Probe Response frames have similar structure, so
+ * the same routine can be used to parse both of them. */
+static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb,
+ int stype)
+{
+ struct hostap_ieee80211_mgmt *mgmt;
+ int left, chan = 0;
+ u8 *pos;
+ u8 *ssid = NULL, *wpa = NULL, *rsn = NULL;
+ size_t ssid_len = 0, wpa_len = 0, rsn_len = 0;
+ struct hostap_bss_info *bss;
+
+ if (skb->len < IEEE80211_MGMT_HDR_LEN + sizeof(mgmt->u.beacon))
+ return;
+
+ mgmt = (struct hostap_ieee80211_mgmt *) skb->data;
+ pos = mgmt->u.beacon.variable;
+ left = skb->len - (pos - skb->data);
+
+ while (left >= 2) {
+ if (2 + pos[1] > left)
+ return; /* parse failed */
+ switch (*pos) {
+ case WLAN_EID_SSID:
+ ssid = pos + 2;
+ ssid_len = pos[1];
+ break;
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (pos[1] >= 4 &&
+ pos[2] == 0x00 && pos[3] == 0x50 &&
+ pos[4] == 0xf2 && pos[5] == 1) {
+ wpa = pos;
+ wpa_len = pos[1] + 2;
+ }
+ break;
+ case WLAN_EID_RSN:
+ rsn = pos;
+ rsn_len = pos[1] + 2;
+ break;
+ case WLAN_EID_DS_PARAMS:
+ if (pos[1] >= 1)
+ chan = pos[2];
+ break;
+ }
+ left -= 2 + pos[1];
+ pos += 2 + pos[1];
+ }
+
+ if (wpa_len > MAX_WPA_IE_LEN)
+ wpa_len = MAX_WPA_IE_LEN;
+ if (rsn_len > MAX_WPA_IE_LEN)
+ rsn_len = MAX_WPA_IE_LEN;
+ if (ssid_len > sizeof(bss->ssid))
+ ssid_len = sizeof(bss->ssid);
+
+ spin_lock(&local->lock);
+ bss = __hostap_get_bss(local, mgmt->bssid, ssid, ssid_len);
+ if (bss == NULL)
+ bss = __hostap_add_bss(local, mgmt->bssid, ssid, ssid_len);
+ if (bss) {
+ bss->last_update = jiffies;
+ bss->count++;
+ bss->capab_info = le16_to_cpu(mgmt->u.beacon.capab_info);
+ if (wpa) {
+ memcpy(bss->wpa_ie, wpa, wpa_len);
+ bss->wpa_ie_len = wpa_len;
+ } else
+ bss->wpa_ie_len = 0;
+ if (rsn) {
+ memcpy(bss->rsn_ie, rsn, rsn_len);
+ bss->rsn_ie_len = rsn_len;
+ } else
+ bss->rsn_ie_len = 0;
+ bss->chan = chan;
+ }
+ __hostap_expire_bss(local);
+ spin_unlock(&local->lock);
+}
+
+
+static int
+hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb,
+ struct hostap_80211_rx_status *rx_stats, u16 type,
+ u16 stype)
+{
+ if (local->iw_mode == IW_MODE_MASTER)
+ hostap_update_sta_ps(local, (struct ieee80211_hdr *) skb->data);
+
+ if (local->hostapd && type == IEEE80211_FTYPE_MGMT) {
+ if (stype == IEEE80211_STYPE_BEACON &&
+ local->iw_mode == IW_MODE_MASTER) {
+ struct sk_buff *skb2;
+ /* Process beacon frames also in kernel driver to
+ * update STA(AP) table statistics */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2)
+ hostap_rx(skb2->dev, skb2, rx_stats);
+ }
+
+ /* send management frames to the user space daemon for
+ * processing */
+ local->apdevstats.rx_packets++;
+ local->apdevstats.rx_bytes += skb->len;
+ if (local->apdev == NULL)
+ return -1;
+ prism2_rx_80211(local->apdev, skb, rx_stats, PRISM2_RX_MGMT);
+ return 0;
+ }
+
+ if (local->iw_mode == IW_MODE_MASTER) {
+ if (type != IEEE80211_FTYPE_MGMT &&
+ type != IEEE80211_FTYPE_CTL) {
+ printk(KERN_DEBUG "%s: unknown management frame "
+ "(type=0x%02x, stype=0x%02x) dropped\n",
+ skb->dev->name, type >> 2, stype >> 4);
+ return -1;
+ }
+
+ hostap_rx(skb->dev, skb, rx_stats);
+ return 0;
+ } else if (type == IEEE80211_FTYPE_MGMT &&
+ (stype == IEEE80211_STYPE_BEACON ||
+ stype == IEEE80211_STYPE_PROBE_RESP)) {
+ hostap_rx_sta_beacon(local, skb, stype);
+ return -1;
+ } else if (type == IEEE80211_FTYPE_MGMT &&
+ (stype == IEEE80211_STYPE_ASSOC_RESP ||
+ stype == IEEE80211_STYPE_REASSOC_RESP)) {
+ /* Ignore (Re)AssocResp silently since these are not currently
+ * needed but are still received when WPA/RSN mode is enabled.
+ */
+ return -1;
+ } else {
+ printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: dropped unhandled"
+ " management frame in non-Host AP mode (type=%d:%d)\n",
+ skb->dev->name, type >> 2, stype >> 4);
+ return -1;
+ }
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct net_device *prism2_rx_get_wds(local_info_t *local,
+ u8 *addr)
+{
+ struct hostap_interface *iface = NULL;
+ struct list_head *ptr;
+
+ read_lock_bh(&local->iface_lock);
+ list_for_each(ptr, &local->hostap_interfaces) {
+ iface = list_entry(ptr, struct hostap_interface, list);
+ if (iface->type == HOSTAP_INTERFACE_WDS &&
+ memcmp(iface->u.wds.remote_add