Discussion:
[Orinoco-users] [RFC] orinoco: Preliminary Agere WPA patch
Dave
2008-01-15 23:49:12 UTC
Permalink
Hi all,

I've got a bunch of updates in my local tree intended to get WPA working. I think I'm close, but it's not working yet. I'd appreciate it if someone more familiar with WPA could cast an eye over the updates (or even better, test) and provide some feedback.

Some notes:
WPA is mostly handled in the firmware, in the same manner as done by the Agere wl_lkm driver.
MIC checking/appending is done by the driver (currently using duplicated mac802.11 implementation)
WEP still works.

Known issues:
1. SIOCSIWAP isn't available for agere firmwares. I think this means we have to use wpa_supplicants ap_scan=2
2. We're not getting an association event after wpa_supplicant calls SIOCSIWESSID - I haven't figured how wl_lkm does this.
3. I have no idea if I've passed the mic calc the right parameters.

If I can get this is working, I'll break this patch into smaller more digestable patches for submission.

Thanks,

Dave.

Relies on the following patches:
orinoco: Agere/Lucent firmware download
<http://marc.info/?l=orinoco-devel&m=118963109015040&w=2>
<http://marc.info/?l=orinoco-devel&m=118963108922013&w=2>
<http://marc.info/?l=orinoco-devel&m=118963109202328&w=2>
<http://marc.info/?l=orinoco-devel&m=118963109902486&w=2>
<http://marc.info/?l=orinoco-devel&m=118963110601146&w=2>

orinoco: Support for agere/lucent fw 9.xx series
<http://marc.info/?l=orinoco-devel&m=119227105017882&w=2>

orinoco: more reliable scan handling
<http://marc.info/?l=linux-wireless&m=119207497430041&w=4>

orinoco: always use latest BSS info when caching scan
<http://marc.info/?l=linux-wireless&m=119687086930337&w=4>

orinoco: Handle extended Agere scans
<http://marc.info/?l=orinoco-devel&m=119620852207540&w=4>

and appropriate firmware as mentioned in:
<http://marc.info/?l=orinoco-devel&m=118963108215134&w=2>

I have a complete patch against Linus' v2.6.24-rc7, but at the moment don't have a place I can post it (~118k).
---
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 86bc9c1..bb91c3b 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -16,7 +16,8 @@ obj-$(CONFIG_WAVELAN) += wavelan.o
obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o
obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o

-obj-$(CONFIG_HERMES) += orinoco.o hermes.o hermes_dld.o
+obj-$(CONFIG_HERMES) += orinoco_wpa.o hermes.o hermes_dld.o
+orinoco_wpa-objs := orinoco.o michael.o
obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o
obj-$(CONFIG_APPLE_AIRPORT) += airport.o
obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o
diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h
index 575703d..212df7c 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/hermes.h
@@ -184,13 +184,17 @@
#define HERMES_RXSTAT_ERR (0x0003)
#define HERMES_RXSTAT_BADCRC (0x0001)
#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002)
+#define HERMES_RXSTAT_MIC (0x0010) /* Frame contains MIC */
#define HERMES_RXSTAT_MACPORT (0x0700)
#define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */
+#define HERMES_RXSTAT_MIC_KEY_ID (0x1800) /* MIC key used */
#define HERMES_RXSTAT_MSGTYPE (0xE000)
#define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */
#define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */
#define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II Management Protocol frame */

+#define HERMES_MIC_KEY_ID_SHIFT 11 /* Shift amount for RXSTAT and TXCTRL */
+
struct hermes_tx_descriptor {
__le16 status;
__le16 reserved1;
@@ -209,6 +213,8 @@ struct hermes_tx_descriptor {
#define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */
#define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */
#define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */
+#define HERMES_TXCTRL_MIC (0x0010) /* 802.3 format with TKIP */
+#define HERMES_TXCTRL_MIC_KEY_ID (0x1800) /* MIC Key ID mask */
#define HERMES_TXCTRL_ALT_RTRY (0x0020)

/* Inquiry constants and data types */
@@ -219,6 +225,7 @@ struct hermes_tx_descriptor {
#define HERMES_INQ_HOSTSCAN (0xF103)
#define HERMES_INQ_HOSTSCAN_SYMBOL (0xF104)
#define HERMES_INQ_LINKSTATUS (0xF200)
+#define HERMES_INQ_ASSOC_STATUS (0xF201)
#define HERMES_INQ_SEC_STAT_AGERE (0xF202)

struct hermes_tallies_frame {
@@ -350,6 +357,16 @@ struct hermes_linkstatus {
__le16 linkstatus; /* Link status */
} __attribute__ ((packed));

+#define HERMES_ASSOC_STATUS_ASSOC (0x0001)
+#define HERMES_ASSOC_STATUS_REASSOC (0x0002)
+#define HERMES_ASSOC_STATUS_DISASSOC (0x0003)
+
+struct hermes_assoc_status {
+ __le16 status; /* Association status */
+ u8 addr[ETH_ALEN];
+ u8 prev_addr[ETH_ALEN];
+} __attribute__ ((packed));
+
struct hermes_response {
u16 status, resp0, resp1, resp2;
};
diff --git a/drivers/net/wireless/hermes_rid.h b/drivers/net/wireless/hermes_rid.h
index bcd9c82..42eb67d 100644
--- a/drivers/net/wireless/hermes_rid.h
+++ b/drivers/net/wireless/hermes_rid.h
@@ -30,6 +30,7 @@
#define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20
#define HERMES_RID_CNFAUTHENTICATION_AGERE 0xFC21
#define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21
+#define HERMES_RID_CNFDROPUNENCRYPTED 0xFC22
#define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23
#define HERMES_RID_CNFDEFAULTKEY0 0xFC24
#define HERMES_RID_CNFDEFAULTKEY1 0xFC25
@@ -85,7 +86,16 @@
#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2
#define HERMES_RID_CNFBASICRATES 0xFCB3
#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4
+#define HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE 0xFCB4
+#define HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE 0xFCB5
+#define HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE 0xFCB6
+#define HERMES_RID_CNFADDMAPPEDTKIPKEY_AGERE 0xFCB7
+#define HERMES_RID_CNFREMMAPPEDTKIPKEY_AGERE 0xFCB8
+#define HERMES_RID_CNFSETWPACAPABILITIES_AGERE 0xFCB9
+#define HERMES_RID_CNFCACHEDPMKADDRESS 0xFCBA
+#define HERMES_RID_CNFREMOVEPMKADDRESS 0xFCBB
#define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2
+#define HERMES_RID_CNFDISASSOCIATE 0xFCC8
#define HERMES_RID_CNFTICKTIME 0xFCE0
#define HERMES_RID_CNFSCANREQUEST 0xFCE1
#define HERMES_RID_CNFJOINREQUEST 0xFCE2
@@ -138,6 +148,12 @@
#define HERMES_RID_CURRENTTXRATE6 0xFD85
#define HERMES_RID_OWNMACADDR 0xFD86
#define HERMES_RID_SCANRESULTSTABLE 0xFD88
+#define HERMES_RID_CURRENT_COUNTRY_INFO 0xFD89
+#define HERMES_RID_CURRENT_WPA_IE 0xFD8A
+#define HERMES_RID_CURRENT_TKIP_IV 0xFD8B
+#define HERMES_RID_CURRENT_ASSOC_REQ_INFO 0xFD8C
+#define HERMES_RID_CURRENT_ASSOC_RESP_INFO 0xFD8D
+#define HERMES_RID_TXQUEUEEMPTY 0xFD91
#define HERMES_RID_PHYTYPE 0xFDC0
#define HERMES_RID_CURRENTCHANNEL 0xFDC1
#define HERMES_RID_CURRENTPOWERSTATE 0xFDC2
diff --git a/drivers/net/wireless/michael.c b/drivers/net/wireless/michael.c
new file mode 100644
index 0000000..0f844f7
--- /dev/null
+++ b/drivers/net/wireless/michael.c
@@ -0,0 +1,104 @@
+/*
+ * Michael MIC implementation - optimized for TKIP MIC operations
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+
+#include "michael.h"
+
+static inline u32 rotr(u32 val, int bits)
+{
+ return (val >> bits) | (val << (32 - bits));
+}
+
+
+static inline u32 rotl(u32 val, int bits)
+{
+ return (val << bits) | (val >> (32 - bits));
+}
+
+
+static inline u32 xswap(u32 val)
+{
+ return ((val & 0xff00ff00) >> 8) | ((val & 0x00ff00ff) << 8);
+}
+
+
+#define michael_block(l, r) \
+do { \
+ r ^= rotl(l, 17); \
+ l += r; \
+ r ^= xswap(l); \
+ l += r; \
+ r ^= rotl(l, 3); \
+ l += r; \
+ r ^= rotr(l, 2); \
+ l += r; \
+} while (0)
+
+
+static inline u32 michael_get32(u8 *data)
+{
+ return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+}
+
+
+static inline void michael_put32(u32 val, u8 *data)
+{
+ data[0] = val & 0xff;
+ data[1] = (val >> 8) & 0xff;
+ data[2] = (val >> 16) & 0xff;
+ data[3] = (val >> 24) & 0xff;
+}
+
+
+void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority,
+ u8 *data, size_t data_len, u8 *mic)
+{
+ u32 l, r, val;
+ size_t block, blocks, left;
+
+ l = michael_get32(key);
+ r = michael_get32(key + 4);
+
+ /* A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC
+ * calculation, but it is _not_ transmitted */
+ l ^= michael_get32(da);
+ michael_block(l, r);
+ l ^= da[4] | (da[5] << 8) | (sa[0] << 16) | (sa[1] << 24);
+ michael_block(l, r);
+ l ^= michael_get32(&sa[2]);
+ michael_block(l, r);
+ l ^= priority;
+ michael_block(l, r);
+
+ /* Real data */
+ blocks = data_len / 4;
+ left = data_len % 4;
+
+ for (block = 0; block < blocks; block++) {
+ l ^= michael_get32(&data[block * 4]);
+ michael_block(l, r);
+ }
+
+ /* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make
+ * total length a multiple of 4. */
+ val = 0x5a;
+ while (left > 0) {
+ val <<= 8;
+ left--;
+ val |= data[blocks * 4 + left];
+ }
+ l ^= val;
+ michael_block(l, r);
+ /* last block is zero, so l ^ 0 = l */
+ michael_block(l, r);
+
+ michael_put32(l, mic);
+ michael_put32(r, mic + 4);
+}
diff --git a/drivers/net/wireless/michael.h b/drivers/net/wireless/michael.h
new file mode 100644
index 0000000..2e6aeba
--- /dev/null
+++ b/drivers/net/wireless/michael.h
@@ -0,0 +1,20 @@
+/*
+ * Michael MIC implementation - optimized for TKIP MIC operations
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef MICHAEL_H
+#define MICHAEL_H
+
+#include <linux/types.h>
+
+#define MICHAEL_MIC_LEN 8
+
+void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority,
+ u8 *data, size_t data_len, u8 *mic);
+
+#endif /* MICHAEL_H */
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index a95fedb..26fa56d 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -89,6 +89,7 @@

#include "hermes_rid.h"
#include "orinoco.h"
+#include "michael.h"

/********************************************************************/
/* Module information */
@@ -317,6 +318,32 @@ static void orinoco_bss_data_init(struct orinoco_private *priv)

}

+static inline u8 *orinoco_get_ie(u8 *data, size_t len,
+ enum ieee80211_mfie eid)
+{
+ u8 *p = data;
+ while ((p + 2) < (data + len)) {
+ if (p[0] == eid)
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
+}
+
+#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
+#define WPA_SELECTOR_LEN 4
+static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
+{
+ u8 *p = data;
+ while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
+ if ((p[0] == MFIE_TYPE_GENERIC) &&
+ (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
+}
+
/********************************************************************/
/* Device methods */
/********************************************************************/
@@ -470,6 +497,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
u16 txfid = priv->txfid;
struct ethhdr *eh;
int data_off;
+ int tx_control;
unsigned long flags;

if (! netif_running(dev)) {
@@ -503,6 +531,12 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)

eh = (struct ethhdr *)skb->data;

+ tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;
+
+ if (priv->add_mic)
+ tx_control |= HERMES_TXCTRL_MIC |
+ (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT);
+
if (priv->has_alt_txcntl) {
/* WPA enabled firmwares have tx_cntl at the end of
* the 802.11 header. So write zeroed descriptor and
@@ -513,9 +547,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)

memset(&desc, 0, sizeof(desc));

- *txcntl = cpu_to_le16(HERMES_TXCTRL_TX_OK |
- HERMES_TXCTRL_TX_EX);
-
+ *txcntl = cpu_to_le16(tx_control);
err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
txfid, 0);
if (err) {
@@ -529,8 +561,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)

memset(&desc, 0, sizeof(desc));

- desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_OK |
- HERMES_TXCTRL_TX_EX);
+ desc.tx_control = cpu_to_le16(tx_control);
err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
txfid, 0);
if (err) {
@@ -583,6 +614,23 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
goto busy;
}

+ if (priv->add_mic) {
+ u8 mic[MICHAEL_MIC_LEN];
+
+ michael_mic(priv->tkip_key[priv->tx_key].tx_mic,
+ eh->h_dest, eh->h_source, 0 /* priority */,
+ skb->data + 24, skb->len - 24, mic);
+
+ /* Append MIC */
+ err = hermes_bap_pwrite(hw, USER_BAP, mic, sizeof(mic),
+ txfid, data_off + skb->len);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
+ dev->name, err);
+ goto busy;
+ }
+ }
+
/* Finally, we actually initiate the send */
netif_stop_queue(dev);

@@ -988,6 +1036,60 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
else
memcpy(hdr->h_source, desc.addr2, ETH_ALEN);

+ /* Calculate and check MIC */
+ if (status & HERMES_RXSTAT_MIC) {
+ int key_id = (status & HERMES_RXSTAT_MIC) >> HERMES_MIC_KEY_ID_SHIFT;
+ int hlen = 24;
+ u8 mic[MICHAEL_MIC_LEN];
+ u8 *data;
+
+ if (fc & 0x80)
+ hlen += 2;
+
+ if (length < (hlen + MICHAEL_MIC_LEN))
+ goto drop;
+
+ length -= MICHAEL_MIC_LEN;
+ data = skb->data + hlen;
+
+ michael_mic(priv->tkip_key[key_id].rx_mic,
+ hdr->h_dest,
+ hdr->h_source,
+ 0, /* priority or QoS? */
+ data,
+ length - hlen,
+ mic);
+
+ if (memcmp(mic, data, MICHAEL_MIC_LEN)) {
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure wxmic;
+
+ printk(KERN_DEBUG "%s: invalid Michael MIC in data frame from "
+ "%2x:%2x:%2x:%2x:%2x:%2x\n", dev->name,
+ hdr->h_source[0], hdr->h_source[1],
+ hdr->h_source[2], hdr->h_source[3],
+ hdr->h_source[4], hdr->h_source[5]);
+
+ /* TODO: update stats */
+
+#if 0
+ /* Notify userspace */
+ wxmic.flags = IW_MICFAILURE_GROUP |
+ (key_id & IW_MICFAILURE_KEY_ID);
+ memcpy(&wxmic.src_addr, hdr->h_source, ETH_ALEN);
+ /* TODO: copy tsc from somewhere */
+
+ wrqu.data.pointer = &wxmic; /* Should be in userspace */
+ wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu);
+#endif
+
+ goto drop;
+ }
+
+ /* remove Michael MIC from payload */
+ skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
+ }
+
dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_NONE;
@@ -1130,30 +1232,93 @@ static void orinoco_join_ap(struct work_struct *work)
}

/* Send new BSSID to userspace */
-static void orinoco_send_wevents(struct work_struct *work)
+static void orinoco_send_bssid_wevent(struct orinoco_private *priv)
{
- struct orinoco_private *priv =
- container_of(work, struct orinoco_private, wevent_work);
struct net_device *dev = priv->ndev;
struct hermes *hw = &priv->hw;
union iwreq_data wrqu;
int err;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return;

err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENTBSSID,
ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
if (err != 0)
- goto out;
+ return;

wrqu.ap_addr.sa_family = ARPHRD_ETHER;

/* Send event to user space */
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv)
+{
+ struct net_device *dev = priv->ndev;
+ struct hermes *hw = &priv->hw;
+ union iwreq_data wrqu;
+ int err;
+ u8 buf[88];
+
+ if (!priv->has_wpa)
+ return;
+
+ err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
+ sizeof(buf), NULL, &buf);
+ if (err != 0)
+ return;
+
+ wrqu.data.pointer = orinoco_get_wpa_ie(buf, sizeof(buf));
+ if (wrqu.data.pointer) {
+ wrqu.data.length = buf[1] + 2;
+ if (wrqu.data.length > sizeof(buf))
+ wrqu.data.length = sizeof(buf);
+
+ if (wrqu.data.length)
+ /* Send event to user space */
+ wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, NULL);
+ }
+}
+
+static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv)
+{
+ struct net_device *dev = priv->ndev;
+ struct hermes *hw = &priv->hw;
+ union iwreq_data wrqu;
+ int err;
+ u8 buf[88]; /* TODO: verify max size */
+
+ if (!priv->has_wpa)
+ return;
+
+ err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
+ sizeof(buf), NULL, &buf);
+ if (err != 0)
+ return;
+
+ wrqu.data.pointer = orinoco_get_wpa_ie(buf, sizeof(buf));
+ if (wrqu.data.pointer) {
+ wrqu.data.length = buf[1] + 2;
+ if (wrqu.data.length > sizeof(buf))
+ wrqu.data.length = sizeof(buf);
+
+ if (wrqu.data.length)
+ /* Send event to user space */
+ wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, NULL);
+ }
+}
+
+static void orinoco_send_wevents(struct work_struct *work)
+{
+ struct orinoco_private *priv =
+ container_of(work, struct orinoco_private, wevent_work);
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return;
+
+ orinoco_send_assocreqie_wevent(priv);
+ orinoco_send_assocrespie_wevent(priv);
+ orinoco_send_bssid_wevent(priv);

- out:
orinoco_unlock(priv, &flags);
}

@@ -1419,6 +1584,9 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
break;
}

+ printk(KERN_DEBUG "%s: wireless link status is %d\n",
+ dev->name, newstatus);
+
connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
|| (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
|| (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
@@ -1556,11 +1724,82 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
kfree(bss);
break;
}
+ case HERMES_INQ_ASSOC_STATUS:
+ /* Link status (Agere specific?) */
+ if (priv->firmware_type == FIRMWARE_TYPE_AGERE) {
+ struct hermes_assoc_status as;
+
+ if (len > sizeof(as))
+ len = sizeof(as);
+
+ /* Read scan data */
+ err = hermes_bap_pread(hw, IRQ_BAP, (void *) &as, len,
+ infofid, sizeof(info));
+ if (err) {
+ printk(KERN_DEBUG
+ "%s: Can't retrieve assoc status data\n",
+ dev->name);
+ break;
+ }
+ printk(KERN_DEBUG "%s: Association status %d ADDR %02d:%02d:%02d:%02d:%02d:%02d PREV: %02d:%02d:%02d:%02d:%02d:%02d\n",
+ dev->name, as.status,
+ as.addr[0], as.addr[1], as.addr[2],
+ as.addr[3], as.addr[4], as.addr[5],
+ as.prev_addr[0], as.prev_addr[1], as.prev_addr[2],
+ as.prev_addr[2], as.prev_addr[4], as.prev_addr[5]);
+ }
+ break;
+
case HERMES_INQ_SEC_STAT_AGERE:
/* Security status (Agere specific) */
/* Ignore this frame for now */
if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
+ {
+ //#ifdef ORINOCO_DEBUG
+ struct {
+ __le16 status;
+ u8 addr[ETH_ALEN];
+ __le16 reason;
+ } __attribute__((packed)) ss;
+ const char *status_string[] = {
+ "Unknown",
+ "[AP] Dissassociate",
+ "[AP] Deauthenticate",
+ "[STA/AP] Authenticate fail",
+ "Mic fail",
+ "Associate fail",
+ };
+ u16 status;
+ u16 reason;
+
+ if (len > sizeof(ss))
+ len = sizeof(ss);
+
+ /* Read scan data */
+ err = hermes_bap_pread(hw, IRQ_BAP, (void *) &ss, len,
+ infofid, sizeof(info));
+ if (err) {
+ printk(KERN_DEBUG
+ "%s: Can't retrieve security status data\n",
+ dev->name);
+ break;
+ }
+
+ status = le16_to_cpu(ss.status);
+ reason = le16_to_cpu(ss.reason);
+
+ if (status > (sizeof(status_string)/sizeof(status_string[0])))
+ status = 0;
+
+ printk(KERN_DEBUG
+ "%s: %02x:%02x:%02x:%02x:%02x:%02x "
+ "Security status %s: reason 0x%04x\n",
+ dev->name, ss.addr[0], ss.addr[1], ss.addr[2],
+ ss.addr[3], ss.addr[4], ss.addr[5],
+ status_string[status], reason);
+ //#endif /* ORINOCO_DEBUG */
break;
+ }
/* fall through */
default:
printk(KERN_DEBUG "%s: Unknown information frame received: "
@@ -1738,7 +1977,7 @@ static int __orinoco_hw_set_wap(struct orinoco_private *priv)
}

/* Change the WEP keys and/or the current keys. Can be called
- * either from __orinoco_hw_setup_wep() or directly from
+ * either from __orinoco_hw_setup_enc() or directly from
* orinoco_ioctl_setiwencode(). In the later case the association
* with the AP is not broken (if the firmware can handle it),
* which is needed for 802.1x implementations. */
@@ -1805,7 +2044,7 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
int master_wep_flag;
int auth_flag;

- if (priv->wep_on)
+ if (priv->encode_alg == IW_ENCODE_ALG_WEP)
__orinoco_hw_setup_wepkeys(priv);

if (priv->wep_restrict)
@@ -1815,7 +2054,7 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)

switch (priv->firmware_type) {
case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
- if (priv->wep_on) {
+ if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
/* Enable the shared-key authentication. */
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFAUTHENTICATION_AGERE,
@@ -1823,14 +2062,14 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFWEPENABLED_AGERE,
- priv->wep_on);
+ (priv->encode_alg == IW_ENCODE_ALG_WEP) ? 1 : 0);
if (err)
return err;
break;

case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
- if (priv->wep_on) {
+ if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
if (priv->wep_restrict ||
(priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
@@ -1862,6 +2101,122 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
return 0;
}

+/* key must be 32 bytes, including the tx and rx MIC keys.
+ * rsc must be 8 bytes
+ * tsc must be 8 bytes or NULL
+ */
+static int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx,
+ u8 *key, u8 *rsc, u8 *tsc)
+{
+ int err;
+ struct {
+ __le16 idx;
+ u8 rsc[IW_ENCODE_SEQ_MAX_SIZE];
+ u8 key[TKIP_KEYLEN];
+ u8 tx_mic[MIC_KEYLEN];
+ u8 rx_mic[MIC_KEYLEN];
+ u8 tsc[IW_ENCODE_SEQ_MAX_SIZE];
+ } buf;
+
+ key_idx &= 0x3;
+
+ if (set_tx)
+ key_idx |= 0x8000;
+
+ buf.idx = cpu_to_le16(key_idx);
+ memcpy(buf.rsc, rsc, sizeof(buf.rsc));
+ memcpy(buf.key, key,
+ sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic));
+ if (tsc == NULL) {
+ memset(buf.tsc, 0, sizeof(buf.tsc));
+ buf.tsc[4] = 0x10;
+ } else
+ memcpy(buf.tsc, tsc, sizeof(buf.tsc));
+
+ err = HERMES_WRITE_RECORD(hw, USER_BAP,
+ HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
+ &buf);
+ if (err)
+ printk(KERN_DEBUG "Error %d setting TKIP key\n",
+ err);
+
+ return err;
+}
+
+static int __orinoco_hw_setup_wpa(struct orinoco_private *priv)
+{
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ int enabled;
+
+ if (!priv->has_wpa)
+ return -EOPNOTSUPP;
+
+ if (priv->encode_alg == IW_ENCODE_ALG_TKIP) {
+ enabled = priv->wpa_enabled ? 2 : 0;
+
+#if 0
+ /* Key programmed directly in ioctl */
+ err = __orinoco_hw_set_tkip_key(hw, priv->tx_key, 1,
+ (u8*) &priv->tkip_key[priv->tx_key],
+ priv->tkip_iv, NULL);
+#endif
+ } else {
+ enabled = 0;
+
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
+ priv->tx_key);
+ if (err)
+ return err;
+ }
+
+#if 0
+ /* Drop unencrypted is only available on AP enabled firmwares */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFDROPUNENCRYPTED,
+ priv->drop_unencrypted);
+ if (err) {
+ printk(KERN_DEBUG "%s: Error %d drop unenc\n",
+ dev->name, err);
+ return err;
+ }
+#endif
+
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFWEPENABLED_AGERE,
+ enabled);
+
+ return err;
+}
+
+static int __orinoco_hw_setup_enc(struct orinoco_private *priv)
+{
+ int err;
+ if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
+ err = __orinoco_hw_setup_wep(priv);
+ priv->add_mic = 0;
+
+ } else if (priv->encode_alg == IW_ENCODE_ALG_TKIP) {
+ err = __orinoco_hw_setup_wpa(priv);
+ /* Start appending MIC */
+ if (!err)
+ priv->add_mic = 1;
+
+ } else {
+ priv->add_mic = 0;
+ /* Clear WEP */
+ err = __orinoco_hw_setup_wep(priv);
+ if (err)
+ return err;
+
+ /* Clear WPA */
+ if (priv->has_wpa)
+ err = __orinoco_hw_setup_wpa(priv);
+ }
+ return err;
+}
+
static int __orinoco_program_rids(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
@@ -2058,10 +2413,10 @@ static int __orinoco_program_rids(struct net_device *dev)
}

/* Set up encryption */
- if (priv->has_wep) {
- err = __orinoco_hw_setup_wep(priv);
+ if (priv->has_wep || priv->has_wpa) {
+ err = __orinoco_hw_setup_enc(priv);
if (err) {
- printk(KERN_ERR "%s: Error %d activating WEP\n",
+ printk(KERN_ERR "%s: Error %d activating encryption\n",
dev->name, err);
return err;
}
@@ -2415,6 +2770,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_big_wep = 0;
priv->has_alt_txcntl = 0;
priv->has_ext_scan = 0;
+ priv->has_wpa = 0;

/* Determine capabilities from the firmware version */
switch (priv->firmware_type) {
@@ -2437,6 +2793,7 @@ static int determine_firmware(struct net_device *dev)
priv->broken_monitor = (firmver >= 0x80000);
priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
+ priv->has_wpa = (firmver >= 0x9002a);
/* Tested with Agere firmware :
* 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
* Tested CableTron firmware : 4.32 => Anton */
@@ -2561,6 +2918,8 @@ static int orinoco_init(struct net_device *dev)
else
printk("40-bit key\n");
}
+ if (priv->has_wpa)
+ printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);

/* Now we have the firmware capabilities, allocate appropiate
* sized scan buffers */
@@ -2682,8 +3041,15 @@ static int orinoco_init(struct net_device *dev)
priv->channel = 0; /* use firmware default */

priv->promiscuous = 0;
- priv->wep_on = 0;
+ priv->encode_alg = IW_ENCODE_ALG_NONE;
priv->tx_key = 0;
+ priv->add_mic = 0;
+ priv->drop_unencrypted = 0;
+ priv->wpa_enabled = 0;
+ priv->tkip_cm_active = 0;
+ priv->key_mgmt = 0;
+ priv->wpa_ie_len = 0;
+ priv->wpa_ie = NULL;

/* Make the hardware available, as long as it hasn't been
* removed elsewhere (e.g. by PCMCIA hot unplug) */
@@ -2754,6 +3120,8 @@ void free_orinocodev(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);

+ priv->wpa_ie_len = 0;
+ kfree(priv->wpa_ie);
orinoco_bss_data_free(priv);
free_netdev(dev);
}
@@ -3065,7 +3433,7 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev,
memset(range, 0, sizeof(struct iw_range));

range->we_version_compiled = WIRELESS_EXT;
- range->we_version_source = 14;
+ range->we_version_source = 18;

/* Set available channels/frequencies */
range->num_channels = NUM_CHANNELS;
@@ -3095,6 +3463,9 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev,
}
}

+ if (priv->has_wpa)
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP;
+
if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))){
/* Quality stats meaningless in ad-hoc mode */
} else {
@@ -3161,7 +3532,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev,
struct orinoco_private *priv = netdev_priv(dev);
int index = (erq->flags & IW_ENCODE_INDEX) - 1;
int setindex = priv->tx_key;
- int enable = priv->wep_on;
+ int encode_alg = priv->encode_alg;
int restricted = priv->wep_restrict;
u16 xlen = 0;
int err = -EINPROGRESS; /* Call commit handler */
@@ -3195,9 +3566,9 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev,
xlen = 0;

/* Switch on WEP if off */
- if ((!enable) && (xlen > 0)) {
+ if ((!encode_alg) && (xlen > 0)) {
setindex = index;
- enable = 1;
+ encode_alg = IW_ENCODE_ALG_WEP;
}
} else {
/* Important note : if the user do "iwconfig eth0 enc off",
@@ -3219,7 +3590,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev,
}

if (erq->flags & IW_ENCODE_DISABLED)
- enable = 0;
+ encode_alg = IW_ENCODE_ALG_NONE;
if (erq->flags & IW_ENCODE_OPEN)
restricted = 0;
if (erq->flags & IW_ENCODE_RESTRICTED)
@@ -3234,14 +3605,15 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev,
priv->tx_key = setindex;

/* Try fast key change if connected and only keys are changed */
- if (priv->wep_on && enable && (priv->wep_restrict == restricted) &&
+ if ((priv->encode_alg == encode_alg) &&
+ (priv->wep_restrict == restricted) &&
netif_carrier_ok(dev)) {
err = __orinoco_hw_setup_wepkeys(priv);
/* No need to commit if successful */
goto out;
}

- priv->wep_on = enable;
+ priv->encode_alg = encode_alg;
priv->wep_restrict = restricted;

out:
@@ -3270,7 +3642,7 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev,
index = priv->tx_key;

erq->flags = 0;
- if (! priv->wep_on)
+ if (!priv->encode_alg)
erq->flags |= IW_ENCODE_DISABLED;
erq->flags |= index + 1;

@@ -3845,6 +4217,373 @@ static int orinoco_ioctl_getpower(struct net_device *dev,
return err;
}

+static int orinoco_ioctl_set_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int idx, key_len, alg = ext->alg, set_key = 1;
+ unsigned long flags;
+ int err = -EINVAL;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ /* Determine and validate the key index */
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (idx < 1 || idx > WEP_KEYS)
+ goto out;
+ idx--;
+ } else
+ idx = priv->tx_key;
+
+ if (encoding->flags & IW_ENCODE_DISABLED)
+ alg = IW_ENCODE_ALG_NONE;
+
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ priv->tx_key = idx;
+ set_key = ext->key_len > 0 ? 1 : 0;
+ }
+
+ if (alg == IW_ENCODE_ALG_TKIP) {
+ hermes_t *hw = &priv->hw;
+
+ /* Need the key to validate RX MIC */
+ if (ext->key_len != (TKIP_KEYLEN + 2*MIC_KEYLEN))
+ goto out;
+ priv->encode_alg = alg;
+ memcpy(&priv->tkip_key[idx], ext->key, ext->key_len);
+
+ if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
+ memcpy(priv->tkip_iv, ext->rx_seq,
+ IW_ENCODE_SEQ_MAX_SIZE);
+
+ /* Program the key immediately */
+ err = __orinoco_hw_set_tkip_key(hw, idx, set_key,
+ (u8*) &priv->tkip_key[idx],
+ priv->tkip_iv, NULL);
+ }
+
+ if (set_key) {
+ /* Set the requested key first */
+ switch (alg) {
+ case IW_ENCODE_ALG_NONE:
+ priv->encode_alg = alg;
+ priv->keys[idx].len = 0;
+ break;
+
+ case IW_ENCODE_ALG_WEP:
+ if (ext->key_len > SMALL_KEY_SIZE) {
+ priv->keys[idx].len = LARGE_KEY_SIZE;
+ } else if (ext->key_len > 0) {
+ priv->keys[idx].len = SMALL_KEY_SIZE;
+ } else {
+ goto out;
+ }
+ priv->encode_alg = alg;
+ memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE);
+ key_len = min(ext->key_len, priv->keys[idx].len);
+ memcpy(priv->keys[idx].data, ext->key, key_len);
+ break;
+
+ case IW_ENCODE_ALG_TKIP:
+ priv->encode_alg = alg;
+
+ /* Yuk. avoid changing err */
+ goto out;
+
+ default:
+ goto out;
+ }
+ }
+ err = -EINPROGRESS;
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_get_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int idx, max_key_len;
+ unsigned long flags;
+ int err;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ err = -EINVAL;
+ max_key_len = encoding->length - sizeof(*ext);
+ if (max_key_len < 0)
+ goto out;
+
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (idx < 1 || idx > WEP_KEYS)
+ goto out;
+ idx--;
+ } else
+ idx = priv->tx_key;
+
+ encoding->flags = idx + 1;
+ memset(ext, 0, sizeof(*ext));
+
+ ext->alg = priv->encode_alg;
+ switch (priv->encode_alg) {
+ case IW_ENCODE_ALG_NONE:
+ ext->key_len = 0;
+ encoding->flags |= IW_ENCODE_DISABLED;
+ break;
+ case IW_ENCODE_ALG_WEP:
+ ext->key_len = priv->keys[idx].len;
+ memcpy(ext->key, priv->keys[idx].data, ext->key_len);
+ encoding->flags |= IW_ENCODE_ENABLED;
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ ext->key_len = sizeof(struct orinoco_tkip_key);
+ memcpy(ext->key, &priv->tkip_key[idx], ext->key_len);
+ encoding->flags |= IW_ENCODE_ENABLED;
+ break;
+ }
+
+ err = 0;
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ struct iw_param *param = &wrqu->param;
+ int ret = -EINPROGRESS;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ case IW_AUTH_PRIVACY_INVOKED:
+ /*
+ * orinoco does not use these parameters
+ */
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ priv->drop_unencrypted = param->value ? 1 : 0;
+ break;
+
+ case IW_AUTH_KEY_MGMT:
+ /* wl_lkm implies value 2 == PSK for Hermes I
+ * which ties in with WEXT
+ * no other hints tho :(
+ */
+ priv->key_mgmt = param->value;
+ ret = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
+ priv->key_mgmt);
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ /* When countermeasures are enabled, shut down the
+ * card; when disabled, re-enable the card. This must
+ * take effect immediately.
+ *
+ * TODO: Make sure that the EAPOL message is getting
+ * out before card disabled
+ */
+ if (param->value) {
+ priv->tkip_cm_active = 1;
+ ret = hermes_enable_port(hw, 0);
+ } else {
+ priv->tkip_cm_active = 0;
+ ret = hermes_disable_port(hw, 0);
+ }
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG: {
+ if (param->value & IW_AUTH_ALG_SHARED_KEY) {
+ priv->wep_restrict = 1;
+ } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+ priv->wep_restrict = 0;
+ } else
+ ret = -EINVAL;
+ break;
+ }
+
+ case IW_AUTH_WPA_ENABLED:
+ /* WPA enable must take effect immediately */
+ if (priv->has_wpa) {
+ priv->wpa_enabled = param->value ? 1 : 0;
+ ret = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFWEPENABLED_AGERE,
+ param->value ? 2 : 0);
+ } else if (param->value)
+ ret = -EOPNOTSUPP;
+ /* else silently accept disable of WPA */
+ priv->wpa_enabled = 0;
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+static int orinoco_ioctl_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct iw_param *param = &wrqu->param;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_DROP_UNENCRYPTED:
+ param->value = priv->drop_unencrypted;
+ break;
+
+ case IW_AUTH_KEY_MGMT:
+ param->value = priv->key_mgmt;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ param->value = priv->tkip_cm_active;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ if (priv->wep_restrict)
+ param->value = IW_AUTH_ALG_SHARED_KEY;
+ else
+ param->value = IW_AUTH_ALG_OPEN_SYSTEM;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ param->value = priv->wpa_enabled;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int orinoco_ioctl_set_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ u8 *buf;
+ int err = 0;
+
+ if (wrqu->data.length > MAX_WPA_IE_LEN ||
+ (wrqu->data.length && (extra == NULL)))
+ return -EINVAL;
+
+ if (wrqu->data.length) {
+ buf = kmalloc(wrqu->data.length, GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(buf, extra, wrqu->data.length);
+ kfree(priv->wpa_ie);
+ priv->wpa_ie = buf;
+ priv->wpa_ie_len = wrqu->data.length;
+ } else {
+ kfree(priv->wpa_ie);
+ priv->wpa_ie = NULL;
+ priv->wpa_ie_len = 0;
+ }
+
+ if (priv->wpa_ie) {
+ /* Looks like wl_lkm wants to check the auth alg, and somehow pass it to the firmware.
+ * Instead it just calls the key mgmt rid */
+#if 0
+ /* Moved to set_auth */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
+ 2);
+#endif
+ }
+
+out:
+ return err;
+}
+
+static int orinoco_ioctl_get_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ int err = 0;
+
+ if (priv->wpa_ie_len == 0 || priv->wpa_ie == NULL) {
+ wrqu->data.length = 0;
+ goto out;
+ }
+
+ if (wrqu->data.length < priv->wpa_ie_len) {
+ err = -E2BIG;
+ goto out;
+ }
+
+ wrqu->data.length = priv->wpa_ie_len;
+ memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
+
+out:
+ return err;
+}
+
+static int orinoco_ioctl_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ u16 reason;
+
+ reason = cpu_to_le16(mlme->reason_code);
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ /* silently ignore */
+ break;
+
+ case IW_MLME_DISASSOC:
+ {
+ struct {
+ u8 addr[ETH_ALEN];
+ __le16 reason_code;
+ } buf;
+ memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN);
+ buf.reason_code = reason;
+ HERMES_WRITE_RECORD(hw, USER_BAP,
+ HERMES_RID_CNFDISASSOCIATE,
+ &buf);
+ break;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
static int orinoco_ioctl_getretry(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rrq,
@@ -4372,33 +5111,6 @@ static inline char *orinoco_translate_scan(struct net_device *dev,
return current_ev;
}

-static inline u8 *orinoco_get_ie(u8 *data, size_t len,
- enum ieee80211_mfie eid)
-{
- u8 *p = data;
- while ((p + 2) < (data + len)) {
- if (p[0] == eid)
- return p;
- p += p[1] + 2;
- }
- return NULL;
-}
-
-#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
-#define WPA_SELECTOR_LEN 4
-
-static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
-{
- u8 *p = data;
- while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
- if ((p[0] == MFIE_TYPE_GENERIC) &&
- (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
- return p;
- p += p[1] + 2;
- }
- return NULL;
-}
-
static inline char *orinoco_translate_ext_scan(struct net_device *dev,
char *current_ev,
char *end_buf,
@@ -4750,6 +5462,13 @@ static const iw_handler orinoco_handler[] = {
[SIOCGIWENCODE-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getiwencode,
[SIOCSIWPOWER -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setpower,
[SIOCGIWPOWER -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getpower,
+ [SIOCSIWGENIE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_set_genie,
+ [SIOCGIWGENIE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_get_genie,
+ [SIOCSIWMLME -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_set_mlme,
+ [SIOCSIWAUTH -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_set_auth,
+ [SIOCGIWAUTH -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_get_auth,
+ [SIOCSIWENCODEEXT-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_set_encodeext,
+ [SIOCGIWENCODEEXT-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_get_encodeext,
};


diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index 75838d0..1719ae2 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -30,6 +30,15 @@ struct orinoco_key {
char data[ORINOCO_MAX_KEY_SIZE];
} __attribute__ ((packed));

+#define TKIP_KEYLEN 16
+#define MIC_KEYLEN 8
+
+struct orinoco_tkip_key {
+ u8 tkip[TKIP_KEYLEN];
+ u8 tx_mic[MIC_KEYLEN];
+ u8 rx_mic[MIC_KEYLEN];
+};
+
typedef enum {
FIRMWARE_TYPE_AGERE,
FIRMWARE_TYPE_INTERSIL,
@@ -91,13 +100,14 @@ struct orinoco_private {
unsigned int has_hostscan:1;
unsigned int has_alt_txcntl:1;
unsigned int has_ext_scan:1;
+ unsigned int has_wpa:1;
unsigned int broken_disableport:1;
unsigned int broken_monitor:1;

/* Configuration paramaters */
u32 iw_mode;
int prefer_port3;
- u16 wep_on, wep_restrict, tx_key;
+ u16 encode_alg, wep_restrict, tx_key;
struct orinoco_key keys[ORINOCO_MAX_KEYS];
int bitratemode;
char nick[IW_ESSID_MAX_SIZE+1];
@@ -125,6 +135,21 @@ struct orinoco_private {

int scan_inprogress; /* Scan pending... */
u32 scan_mode; /* Type of scan done */
+
+ /* WPA support */
+ u8 *wpa_ie;
+ int wpa_ie_len;
+
+ struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS];
+
+ unsigned int add_mic:1;
+ unsigned int drop_unencrypted:1;
+ unsigned int wpa_enabled:1;
+ unsigned int tkip_cm_active:1;
+ unsigned int key_mgmt:3;
+
+ /* For passing information from ioctl to commit */
+ u8 tkip_iv[IW_ENCODE_SEQ_MAX_SIZE];
};

#ifdef ORINOCO_DEBUG
Dave
2008-01-20 14:55:17 UTC
Permalink
I am not a developer, but a user of the orinoco-usb driver. Would these
patches also bring WPA to the USB devices?
I'm not a orinoco-usb user, but a developer :)

Seriously, I only have a single orinoco_cs PCMCIA card (Dell Truemobile 1150), and the changes are targeted at implementing WPA support for that card. I would not expect the changes to work for USB devices as is. There are three issues:

1. Mainline orinoco vs orinoco-usb drivers. I'm not familiar with the differences between the orinoco mainline driver and the orinoco_usb driver, so I can't tell if my modifications would port well to the usb version of the driver.

2. Difference in WPA support between PCMCIA cards and USB cards. Ageres wl_lkm sources (that I'm using as reference) support WPA on USB as well, and the differences don't appear to be major.

3. Non-Agere firmwares. I have no idea if/how Intersil/Prism/Symbol firmwares support WPA, whether USB or not. If you are talking about a USB device with one of these firmwares these patches probably aren't going to help much.

HTH,

Dave.
Sourav K. Mandal
2008-01-20 15:15:07 UTC
Permalink
Post by Dave
3. Non-Agere firmwares. I have no idea if/how Intersil/Prism/Symbol firmwares support WPA, whether USB or not. If you are talking about a USB device with one of these firmwares these patches probably aren't going to help much.
Indeed, I have a Compaq WLAN Multiport W200, which AFAIK has a Prism 2.x
chipset.

Thank you for the information, and for your work on the driver.


Best,

Sourav

Loading...