#include <linux/delay.h>
#endif
+/* Support Cisco MIC feature */
+/* As this feature requires the AES encryption algorithm, it is not included
+ in the kernel tree. If you want to enable it, you need to download the
+ aes.h, aestab.h and mic.h files from the CVS at
+ http://sf.net/projects/airo-linux/ Put the files in the same directory
+ as airo.c and compile normally */
+#undef MICSUPPORT
+
/* Hack to do some power saving */
#define POWER_ON_DOWN
#define NO_PACKET -2
/* Commands */
-#define NOP2 0x00
-#define MAC_ENABLE 0x01
-#define MAC_ENABLETX 0x101
-#define CMD_ENABLEAUX 0x111
-#define MAC_ENABLERX 0x201
-#define MAC_DISABLE 0x02
-#define CMD_LOSE_SYNC 0x03
-#define CMD_LOSE_SYNC_BSS 0x103
-#define CMD_SOFTRESET 0x04
-#define HOSTSLEEP 0x05
-#define CMD_MAGICPACKET 0x06
-#define CMD_SETWAKEMASK 0x07
-#define CMD_SAVECONFIG 0x108
-#define CMD_READCONFIG 0x08
-#define CMD_SETMODE 0x09
-#define CMD_ALLOCATETX 0x0A
-#define CMD_TRANSMIT 0x0B
-#define CMD_DEALLOC 0x0C
-#define CMD_LISTBSS 0x103
-#define NOP 0x10
-#define CMD_WORKAROUND 0x11
-#define CMD_ACCESS 0x21
-#define CMD_WRITERID 0x121
-#define CMD_PCIBAP 0x22
-#define CMD_PCIAUX 0x23
-#define CMD_ALLOCTLV 0x28
-#define CMD_GETTLV 0x29
-#define CMD_PUTTLV 0x2A
-#define CMD_DELTLV 0x2B
-#define CMD_FINDNEXTTLV 0x2C
-#define CMD_SETPHYREG 0x3E
-#define CMD_TXTEST 0x3F
-#define CMD_NOPSPNODES 0x30
-#define CMD_USEPSPNODES 0x130
-#define CMD_SETCW 0x31
-#define CMD_SETPCF 0x32
+#define NOP2 0x0000
+#define MAC_ENABLE 0x0001
+#define MAC_DISABLE 0x0002
+#define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */
+#define CMD_SOFTRESET 0x0004
+#define HOSTSLEEP 0x0005
+#define CMD_MAGIC_PKT 0x0006
+#define CMD_SETWAKEMASK 0x0007
+#define CMD_READCFG 0x0008
+#define CMD_SETMODE 0x0009
+#define CMD_ALLOCATETX 0x000a
+#define CMD_TRANSMIT 0x000b
+#define CMD_DEALLOCATETX 0x000c
+#define NOP 0x0010
+#define CMD_WORKAROUND 0x0011
+#define CMD_ACCESS 0x0021
+#define CMD_PCIBAP 0x0022
+#define CMD_PCIAUX 0x0023
+#define CMD_ALLOCBUF 0x0028
+#define CMD_GETTLV 0x0029
+#define CMD_PUTTLV 0x002a
+#define CMD_DELTLV 0x002b
+#define CMD_FINDNEXTTLV 0x002c
+#define CMD_PSPNODES 0x0030
+#define CMD_SETCW 0x0031
+#define CMD_SETPCF 0x0032
+#define CMD_SETPHYREG 0x003e
+#define CMD_TXTEST 0x003f
+#define MAC_ENABLETX 0x0101
+#define CMD_LISTBSS 0x0103
+#define CMD_SAVECFG 0x0108
+#define CMD_ENABLEAUX 0x0111
+#define CMD_WRITERID 0x0121
+#define CMD_USEPSPNODES 0x0130
+#define MAC_ENABLERX 0x0201
/* Command errors */
#define ERROR_QUALIF 0x00
#define EV_AWAKE 0x100
#define EV_UNKNOWN 0x800
#define EV_MIC 0x1000 /* Message Integrity Check Interrupt */
-#define STATUS_INTS ( EV_AWAKE | EV_LINK | EV_TXEXC | EV_TX | EV_RX)
+#define STATUS_INTS ( EV_AWAKE | EV_LINK | EV_TXEXC | EV_TX | EV_RX | EV_MIC )
#ifdef CHECK_UNKNOWN_INTS
-#define IGNORE_INTS ( EV_MIC | EV_CMD | EV_UNKNOWN)
+#define IGNORE_INTS ( EV_CMD | EV_UNKNOWN)
#else
#define IGNORE_INTS (~STATUS_INTS)
#endif
#define RID_UNKNOWN54 0xFF54
#define RID_UNKNOWN55 0xFF55
#define RID_UNKNOWN56 0xFF56
+#define RID_MIC 0xFF57
#define RID_STATS16 0xFF60
#define RID_STATS16DELTA 0xFF61
#define RID_STATS16DELTACLEAR 0xFF62
#define MODE_ETHER_LLC (1<<12) /* enable ethernet LLC */
#define MODE_LEAF_NODE (1<<13) /* enable leaf node bridge */
#define MODE_CF_POLLABLE (1<<14) /* enable CF pollable */
+#define MODE_MIC (1<<15) /* enable MIC */
u16 rmode; /* receive mode */
#define RXMODE_BC_MC_ADDR 0
#define RXMODE_BC_ADDR 1 /* ignore multicasts */
u16 softCap;
u16 bootBlockVer;
u16 requiredHard;
+ u16 extSoftCap;
} CapabilityRid;
typedef struct {
tdsRssiEntry x[256];
} tdsRssiRid;
+typedef struct {
+ u16 len;
+ u16 state;
+ u16 multicastValid;
+ u8 multicast[16];
+ u16 unicastValid;
+ u8 unicast[16];
+} MICRid;
+
+typedef struct {
+ u16 typelen;
+
+ union {
+ u8 snap[8];
+ struct {
+ u8 dsap;
+ u8 ssap;
+ u8 control;
+ u8 orgcode[3];
+ u8 fieldtype[2];
+ } llc;
+ } u;
+ u32 mic;
+ u32 seq;
+} MICBuffer;
+
+typedef struct {
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+} etherHead;
+
#pragma pack()
#define TXCTL_TXOK (1<<1) /* report if tx is ok */
#define AIROGSTAT 8
#define AIROGSTATSC32 9
#define AIROGSTATSD32 10
+#define AIROGMICRID 11
+#define AIROGMICSTATS 12
+#define AIROGFLAGS 13
/* Leave gap of 40 commands after AIROGSTATSD32 for future */
-#define AIROPCAP AIROGSTATSD32 + 40
+#define AIROPCAP AIROGSTATSD32 + 40
#define AIROPVLIST AIROPCAP + 1
#define AIROPSLIST AIROPVLIST + 1
#define AIROPCFG AIROPSLIST + 1
} aironet_ioctl;
#endif /* CISCO_EXT */
+#define NUM_MODULES 2
+#define MIC_MSGLEN_MAX 2400
+#define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX
+
+typedef struct {
+ u32 size; // size
+ u8 enabled; // MIC enabled or not
+ u32 rxSuccess; // successful packets received
+ u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison
+ u32 rxNotMICed; // pkts dropped due to not being MIC'd
+ u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed
+ u32 rxWrongSequence; // pkts dropped due to sequence number violation
+ u32 reserve[32];
+} mic_statistics;
+
+typedef struct {
+ u32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2];
+ u64 accum; // accumulated mic, reduced to u32 in final()
+ int position; // current position (byte offset) in message
+ union {
+ u8 d8[4];
+ u32 d32;
+ } part; // saves partial message word across update() calls
+} emmh32_context;
+
+typedef struct {
+ emmh32_context seed; // Context - the seed
+ u32 rx; // Received sequence number
+ u32 tx; // Tx sequence number
+ u32 window; // Start of window
+ u8 valid; // Flag to say if context is valid or not
+ u8 key[16];
+} miccntx;
+
+typedef struct {
+ miccntx mCtx; // Multicast context
+ miccntx uCtx; // Unicast context
+} mic_module;
+
#ifdef WIRELESS_EXT
// Frequency list (map channels to frequencies)
const long frequency_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
#endif /* WIRELESS_EXT > 12 */
#endif /* WIRELESS_EXT */
-static const char version[] = "airo.c 0.5 (Ben Reed & Javier Achirica)";
+static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)";
struct airo_info;
static int writerids(struct net_device *dev, aironet_ioctl *comp);
int flashcard(struct net_device *dev, aironet_ioctl *comp);
#endif /* CISCO_EXT */
+#ifdef MICSUPPORT
+static void micinit(struct airo_info *ai, MICRid *micr);
+static void micsetup(struct airo_info *ai);
+static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
+static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
+#endif
struct airo_info {
struct net_device_stats stats;
#define FLAG_RADIO_DOWN 0x08 /* ifup/ifdown disabling of MAC */
#define FLAG_LOCKED 2 /* 0x04 - use as a bit offset */
#define FLAG_FLASHING 0x10
+#define FLAG_ADHOC 0x01 /* Needed by MIC */
+#define FLAG_MIC_CAPABLE 0x20
+#define FLAG_UPDATE_MULTI 0x40
+#define FLAG_UPDATE_UNI 0x80
#define FLAG_802_11 0x200
int (*bap_read)(struct airo_info*, u16 *pu16Dst, int bytelen,
int whichbap);
struct iw_quality spy_stat[IW_MAX_SPY];
#endif /* WIRELESS_SPY */
#endif /* WIRELESS_EXT */
+ /* MIC stuff */
+ mic_module mod[2];
+ mic_statistics micstats;
+ struct tq_struct mic_task;
};
static inline int bap_read(struct airo_info *ai, u16 *pu16Dst, int bytelen,
static int takedown_proc_entry( struct net_device *dev,
struct airo_info *apriv );
+#ifdef MICSUPPORT
+#include "mic.h"
+#endif
+
static int readBSSListRid(struct airo_info *ai, int first,
BSSListRid *list) {
int rc;
ai->need_commit = 0;
checkThrottle(ai);
+ if ((cfgr.opmode & 0xFF) == MODE_STA_IBSS)
+ ai->flags |= FLAG_ADHOC;
+ else
+ ai->flags &= ~FLAG_ADHOC;
+
cfgr = ai->config;
for(s = &cfgr.len; s <= &cfgr.rtsThres; s++) *s = cpu_to_le16(*s);
}
#endif
+static void airo_read_mic(struct airo_info *ai) {
+ MICRid mic_rid;
+
+ if (down_trylock(&ai->sem) == 0) {
+ __set_bit(FLAG_LOCKED, &ai->flags);
+ PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid));
+ clear_bit(FLAG_LOCKED, &ai->flags);
+ up(&ai->sem);
+#ifdef MICSUPPORT
+ micinit (ai, &mic_rid);
+#endif
+ } else {
+ ai->mic_task.routine = (void (*)(void *))airo_read_mic;
+ ai->mic_task.data = (void *)ai;
+ schedule_task(&ai->mic_task);
+ }
+}
+
static void airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) {
struct net_device *dev = (struct net_device *)dev_id;
u16 status;
OUT4500( apriv, EVINTEN, 0 );
}
+ if ( status & EV_MIC ) {
+ OUT4500( apriv, EVACK, EV_MIC );
+ airo_read_mic( apriv );
+ }
if ( status & EV_LINK ) {
#if WIRELESS_EXT > 13
union iwreq_data wrqu;
struct task_struct *task = apriv->task;
if (task)
wake_up_process (task);
+ apriv->flags|=FLAG_UPDATE_UNI|FLAG_UPDATE_MULTI;
}
#if WIRELESS_EXT > 13
/* Question : is ASSOCIATED the only status
bap_read (apriv, buffer + hdrlen/2, len, BAP0);
} else {
- bap_read (apriv, buffer,len + hdrlen,BAP0);
+ MICBuffer micbuf;
+ bap_read (apriv, buffer, ETH_ALEN*2, BAP0);
+ if (apriv->micstats.enabled) {
+ bap_read (apriv,(u16*)&micbuf,sizeof(micbuf),BAP0);
+ if (ntohs(micbuf.typelen) > 0x05DC)
+ bap_setup (apriv, fid, 0x44, BAP0);
+ else {
+ len -= sizeof(micbuf);
+ if (len < 48)
+ len = 48;
+ skb_trim (skb, len + hdrlen);
+ }
+ }
+ bap_read(apriv,buffer+ETH_ALEN,len,BAP0);
+#ifdef MICSUPPORT
+ if (decapsulate(apriv,&micbuf,(etherHead*)buffer,len)) {
+ dev_kfree_skb_irq (skb);
+ len = 0;
+ }
+#endif
}
}
if (len) {
up(&ai->sem);
if (ai->config.len == 0) {
tdsRssiRid rssi_rid;
+ CapabilityRid cap_rid;
// general configuration (read/modify/write)
status = readConfigRid(ai);
if ( status != SUCCESS ) return ERROR;
+ status = readCapabilityRid(ai, &cap_rid);
+ if ( status != SUCCESS ) return ERROR;
+
status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid));
if ( status == SUCCESS ) {
if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512);
}
else {
- CapabilityRid cap_rid;
if (ai->rssi) {
kfree(ai->rssi);
ai->rssi = NULL;
}
- status = readCapabilityRid(ai, &cap_rid);
- if ((status == SUCCESS) && (cap_rid.softCap & 8))
+ if (cap_rid.softCap & 8)
ai->config.rmode |= RXMODE_NORMALIZED_RSSI;
else
printk(KERN_WARNING "airo: unknown received signal level scale\n");
}
ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS;
+#ifdef MICSUPPORT
+ if ((cap_rid.len==sizeof(cap_rid)) && (cap_rid.extSoftCap&1)) {
+ ai->config.opmode |= MODE_MIC;
+ ai->flags |= FLAG_MIC_CAPABLE;
+ micsetup(ai);
+ }
+#endif
+
/* Save off the MAC */
for( i = 0; i < ETH_ALEN; i++ ) {
mac[i] = ai->config.macAddr[i];
}
}
}
- if (auto_wep)
- ai->config.authType = AUTH_SHAREDKEY;
ai->need_commit = 1;
}
u16 payloadLen;
Cmd cmd;
Resp rsp;
+ int miclen = 0;
u16 txFid = len;
+ MICBuffer pMic;
+
len >>= 16;
if (len < ETH_ALEN * 2) {
printk( KERN_WARNING "Short packet %d\n", len );
return ERROR;
}
+ len -= ETH_ALEN * 2;
+
+#ifdef MICSUPPORT
+ if ((ai->flags & FLAG_MIC_CAPABLE) && ai->micstats.enabled &&
+ (ntohs(((u16 *)pPacket)[6]) != 0x888E)) {
+ if (encapsulate(ai,(etherHead *)pPacket,&pMic,len) != SUCCESS)
+ return ERROR;
+ miclen = sizeof(pMic);
+ }
+#endif
// packet is destination[6], source[6], payload[len-12]
// write the payload length and dst/src/payload
if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR;
/* The hardware addresses aren't counted as part of the payload, so
* we have to subtract the 12 bytes for the addresses off */
- payloadLen = cpu_to_le16(len-12);
+ payloadLen = cpu_to_le16(len + miclen);
bap_write(ai, &payloadLen, sizeof(payloadLen),BAP1);
- bap_write(ai, (const u16*)pPacket, len, BAP1);
+ bap_write(ai, (const u16*)pPacket, sizeof(etherHead), BAP1);
+ if (miclen)
+ bap_write(ai, (const u16*)&pMic, miclen, BAP1);
+ bap_write(ai, (const u16*)(pPacket + sizeof(etherHead)), len, BAP1);
// issue the transmit command
memset( &cmd, 0, sizeof( cmd ) );
cmd.cmd = CMD_TRANSMIT;
need_reset = 1;
ai->config.rmode &= 0xfe00;
ai->flags &= ~FLAG_802_11;
+ ai->config.opmode &= 0xFF00;
if ( line[0] == 'a' ) {
- ai->config.opmode = 0;
+ ai->config.opmode |= 0;
} else {
- ai->config.opmode = 1;
+ ai->config.opmode |= 1;
if ( line[0] == 'r' ) {
ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
ai->flags |= FLAG_802_11;
"DataRates: %d %d %d %d %d %d %d %d\n"
"Channel: %d\n"
"XmitPower: %d\n",
- ai->config.opmode == 0 ? "adhoc" :
- ai->config.opmode == 1 ? get_rmode(ai->config.rmode):
- ai->config.opmode == 2 ? "AP" :
- ai->config.opmode == 3 ? "AP RPTR" : "Error",
+ (ai->config.opmode & 0xFF) == 0 ? "adhoc" :
+ (ai->config.opmode & 0xFF) == 1 ? get_rmode(ai->config.rmode):
+ (ai->config.opmode & 0xFF) == 2 ? "AP" :
+ (ai->config.opmode & 0xFF) == 3 ? "AP RPTR" : "Error",
ai->flags&FLAG_RADIO_OFF ? "off" : "on",
ai->config.nodeName,
ai->config.powerSaveMode == 0 ? "CAM" :
switch(*uwrq) {
case IW_MODE_ADHOC:
- local->config.opmode = MODE_STA_IBSS;
+ local->config.opmode &= 0xFF00;
+ local->config.opmode |= MODE_STA_IBSS;
break;
case IW_MODE_INFRA:
- local->config.opmode = MODE_STA_ESS;
+ local->config.opmode &= 0xFF00;
+ local->config.opmode |= MODE_STA_ESS;
break;
case IW_MODE_MASTER:
- local->config.opmode = MODE_AP;
+ local->config.opmode &= 0xFF00;
+ local->config.opmode |= MODE_AP;
break;
case IW_MODE_REPEAT:
- local->config.opmode = MODE_AP_RPTR;
+ local->config.opmode &= 0xFF00;
+ local->config.opmode |= MODE_AP_RPTR;
break;
default:
return -EINVAL;
/* Seperate R/W functions bracket legality here
*/
- if ( com.command <= AIROGSTATSD32 )
+ if ( com.command <= AIROGMICSTATS )
rc = readrids(dev,&com);
else if ( com.command >= AIROPCAP && com.command <= AIROPLEAPUSR )
rc = writerids(dev,&com);
switch(comp->command)
{
case AIROGCAP: ridcode = RID_CAPABILITIES; break;
- case AIROGCFG: ridcode = RID_CONFIG; break;
- case AIROPCFG: writeConfigRid ((struct airo_info *)dev->priv);
+ case AIROGCFG: writeConfigRid (ai);
ridcode = RID_CONFIG; break;
case AIROGSLIST: ridcode = RID_SSID; break;
case AIROGVLIST: ridcode = RID_APLIST; break;
case AIROGSTAT: ridcode = RID_STATUS; break;
case AIROGSTATSD32: ridcode = RID_STATSDELTA; break;
case AIROGSTATSC32: ridcode = RID_STATS; break;
+ case AIROGMICSTATS:
+ if (copy_to_user(comp->data, &ai->micstats,
+ min((int)comp->len,(int)sizeof(ai->micstats))))
+ return -EFAULT;
+ return 0;
default:
return -EINVAL;
break;
}
- PC4500_readrid((struct airo_info *)dev->priv,ridcode,iobuf,sizeof(iobuf));
+ PC4500_readrid(ai,ridcode,iobuf,sizeof(iobuf));
/* get the count of bytes in the rid docs say 1st 2 bytes is it.
* then return it to the user
* 9/22/2000 Honor user given length
static int writerids(struct net_device *dev, aironet_ioctl *comp) {
struct airo_info *ai = dev->priv;
- int ridcode;
+ int ridcode, enabled;
Resp rsp;
static int (* writer)(struct airo_info *, u16 rid, const void *, int);
unsigned char iobuf[2048];
case AIROPSIDS: ridcode = RID_SSID; break;
case AIROPCAP: ridcode = RID_CAPABILITIES; break;
case AIROPAPLIST: ridcode = RID_APLIST; break;
- case AIROPCFG: ((struct airo_info *)dev->priv)->config.len = 0;
+ case AIROPCFG: ai->config.len = 0;
ridcode = RID_CONFIG; break;
case AIROPWEPKEYNV: ridcode = RID_WEP_PERM; break;
case AIROPLEAPUSR: ridcode = RID_LEAPUSERNAME; break;
* same with MAC off
*/
case AIROPMACON:
- if (enable_MAC(dev->priv, &rsp) != 0)
+ if (enable_MAC(ai, &rsp) != 0)
return -EIO;
return 0;
* as disable_MAC. it's probably so short the compiler does not gen one.
*/
case AIROPMACOFF:
- disable_MAC(dev->priv);
+ disable_MAC(ai);
return 0;
/* This command merely clears the counts does not actually store any data
* writerid routines.
*/
case AIROPSTCLR:
- ridcode = RID_STATSDELTACLEAR;
+ PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,sizeof(iobuf));
- PC4500_readrid(dev->priv,ridcode,iobuf,sizeof(iobuf));
+ enabled = ai->micstats.enabled;
+ memset(&ai->micstats,0,sizeof(ai->micstats));
+ ai->micstats.enabled = enabled;
if (copy_to_user(comp->data, iobuf,
min((int)comp->len, (int)sizeof(iobuf))))
if (copy_from_user(iobuf,comp->data,comp->len))
return -EFAULT;
- if((*writer)((struct airo_info *)dev->priv, ridcode, iobuf,comp->len))
+
+ if (comp->command == AIROPCFG) {
+ ConfigRid *cfg = (ConfigRid *)iobuf;
+
+ if (ai->flags & FLAG_MIC_CAPABLE)
+ cfg->opmode |= MODE_MIC;
+
+ if ((cfg->opmode & 0xFF) == MODE_STA_IBSS)
+ ai->flags |= FLAG_ADHOC;
+ else
+ ai->flags &= ~FLAG_ADHOC;
+ }
+
+ if((*writer)(ai, ridcode, iobuf,comp->len))
return -EIO;
return 0;
}