Commit d97bdc8d authored by David Gibson's avatar David Gibson Committed by Jeff Garzik

[PATCH] orinoco merge preliminaries - rearrange code

Rearrange functions in the orinoco driver in a more logical order.
This patch is large and looks complicated, but in fact only moves code
around (within or between files) without changing it.  The only
exceptions are some extra comments describing the file's layout, and
updated prototypes for the new function order.

This makes the order of functions match the 0.15rc1 version, so later
patches have a fighting chance of being meaningful.
Signed-off-by: default avatarDavid Gibson <hermes@gibson.dropbear.id.au>

Index: working-2.6/drivers/net/wireless/orinoco.c
===================================================================
parent 8d50e0e0
...@@ -157,16 +157,6 @@ ...@@ -157,16 +157,6 @@
#define HERMES_802_3_OFFSET (14+32) #define HERMES_802_3_OFFSET (14+32)
#define HERMES_802_2_OFFSET (14+32+14) #define HERMES_802_2_OFFSET (14+32+14)
struct hermes_rx_descriptor {
u16 status;
u32 time;
u8 silence;
u8 signal;
u8 rate;
u8 rxflow;
u32 reserved;
} __attribute__ ((packed));
#define HERMES_RXSTAT_ERR (0x0003) #define HERMES_RXSTAT_ERR (0x0003)
#define HERMES_RXSTAT_BADCRC (0x0001) #define HERMES_RXSTAT_BADCRC (0x0001)
#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) #define HERMES_RXSTAT_UNDECRYPTABLE (0x0002)
...@@ -262,6 +252,20 @@ struct hermes_linkstatus { ...@@ -262,6 +252,20 @@ struct hermes_linkstatus {
u16 linkstatus; /* Link status */ u16 linkstatus; /* Link status */
} __attribute__ ((packed)); } __attribute__ ((packed));
typedef struct hermes_response {
u16 status, resp0, resp1, resp2;
} hermes_response_t;
/* "ID" structure - used for ESSID and station nickname */
struct hermes_idstring {
u16 len;
u16 val[16];
} __attribute__ ((packed));
typedef struct hermes_multicast {
u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
} __attribute__ ((packed)) hermes_multicast_t;
// #define HERMES_DEBUG_BUFFER 1 // #define HERMES_DEBUG_BUFFER 1
#define HERMES_DEBUG_BUFSIZE 4096 #define HERMES_DEBUG_BUFSIZE 4096
struct hermes_debug_entry { struct hermes_debug_entry {
...@@ -294,10 +298,6 @@ typedef struct hermes { ...@@ -294,10 +298,6 @@ typedef struct hermes {
#endif #endif
} hermes_t; } hermes_t;
typedef struct hermes_response {
u16 status, resp0, resp1, resp2;
} hermes_response_t;
/* Register access convenience macros */ /* Register access convenience macros */
#define hermes_read_reg(hw, off) ((hw)->io_space ? \ #define hermes_read_reg(hw, off) ((hw)->io_space ? \
inw((hw)->iobase + ( (off) << (hw)->reg_spacing )) : \ inw((hw)->iobase + ( (off) << (hw)->reg_spacing )) : \
......
...@@ -140,14 +140,4 @@ ...@@ -140,14 +140,4 @@
#define HERMES_RID_BUILDSEQ 0xFFFE #define HERMES_RID_BUILDSEQ 0xFFFE
#define HERMES_RID_FWID 0xFFFF #define HERMES_RID_FWID 0xFFFF
/* "ID" structure - used for ESSID and station nickname */
struct hermes_idstring {
u16 len;
u16 val[16];
} __attribute__ ((packed));
typedef struct hermes_multicast {
u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
} __attribute__ ((packed)) hermes_multicast_t;
#endif #endif
...@@ -496,6 +496,10 @@ MODULE_PARM(suppress_linkstatus, "i"); ...@@ -496,6 +496,10 @@ MODULE_PARM(suppress_linkstatus, "i");
HERMES_MAX_MULTICAST : 0)*/ HERMES_MAX_MULTICAST : 0)*/
#define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST) #define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST)
#define ORINOCO_INTEN ( HERMES_EV_RX | HERMES_EV_ALLOC | HERMES_EV_TX | \
HERMES_EV_TXEXC | HERMES_EV_WTERR | HERMES_EV_INFO | \
HERMES_EV_INFDROP )
/********************************************************************/ /********************************************************************/
/* Data tables */ /* Data tables */
/********************************************************************/ /********************************************************************/
...@@ -548,133 +552,55 @@ u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; ...@@ -548,133 +552,55 @@ u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) #define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
struct hermes_rx_descriptor {
u16 status;
u32 time;
u8 silence;
u8 signal;
u8 rate;
u8 rxflow;
u32 reserved;
} __attribute__ ((packed));
/********************************************************************/ /********************************************************************/
/* Function prototypes */ /* Function prototypes */
/********************************************************************/ /********************************************************************/
static void orinoco_stat_gather(struct net_device *dev, static int orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
struct sk_buff *skb,
struct hermes_rx_descriptor *desc);
static struct net_device_stats *orinoco_get_stats(struct net_device *dev);
static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev);
/* Hardware control routines */
static int __orinoco_program_rids(struct net_device *dev); static int __orinoco_program_rids(struct net_device *dev);
static int __orinoco_hw_set_bitrate(struct orinoco_private *priv);
static int __orinoco_hw_setup_wep(struct orinoco_private *priv);
static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]);
static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
char buf[IW_ESSID_MAX_SIZE+1]);
static long orinoco_hw_get_freq(struct orinoco_private *priv);
static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates,
s32 *rates, int max);
static void __orinoco_set_multicast_list(struct net_device *dev); static void __orinoco_set_multicast_list(struct net_device *dev);
/* Interrupt handling routines */
static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw);
/* ioctl() routines */
static int orinoco_debug_dump_recs(struct net_device *dev); static int orinoco_debug_dump_recs(struct net_device *dev);
/********************************************************************/ /********************************************************************/
/* Function prototypes */ /* Internal helper functions */
/********************************************************************/ /********************************************************************/
int __orinoco_up(struct net_device *dev) static inline void
{ set_port_type(struct orinoco_private *priv)
struct orinoco_private *priv = netdev_priv(dev);
struct hermes *hw = &priv->hw;
int err;
err = __orinoco_program_rids(dev);
if (err) {
printk(KERN_ERR "%s: Error %d configuring card\n",
dev->name, err);
return err;
}
/* Fire things up again */
hermes_set_irqmask(hw, ORINOCO_INTEN);
err = hermes_enable_port(hw, 0);
if (err) {
printk(KERN_ERR "%s: Error %d enabling MAC port\n",
dev->name, err);
return err;
}
netif_start_queue(dev);
return 0;
}
int __orinoco_down(struct net_device *dev)
{ {
struct orinoco_private *priv = netdev_priv(dev); switch (priv->iw_mode) {
struct hermes *hw = &priv->hw; case IW_MODE_INFRA:
int err; priv->port_type = 1;
priv->createibss = 0;
netif_stop_queue(dev); break;
case IW_MODE_ADHOC:
if (! priv->hw_unavailable) { if (priv->prefer_port3) {
if (! priv->broken_disableport) { priv->port_type = 3;
err = hermes_disable_port(hw, 0); priv->createibss = 0;
if (err) { } else {
/* Some firmwares (e.g. Intersil 1.3.x) seem priv->port_type = priv->ibss_port;
* to have problems disabling the port, oh priv->createibss = 1;
* well, too bad. */
printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
dev->name, err);
priv->broken_disableport = 1;
}
} }
hermes_set_irqmask(hw, 0); break;
hermes_write_regn(hw, EVACK, 0xffff); default:
printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
priv->ndev->name);
} }
/* firmware will have to reassociate */
priv->last_linkstatus = 0xffff;
priv->connected = 0;
return 0;
} }
int orinoco_reinit_firmware(struct net_device *dev) /********************************************************************/
{ /* Device methods */
struct orinoco_private *priv = netdev_priv(dev); /********************************************************************/
struct hermes *hw = &priv->hw;
int err;
err = hermes_init(hw);
if (err)
return err;
err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
if (err == -EIO) {
/* Try workaround for old Symbol firmware bug */
printk(KERN_WARNING "%s: firmware ALLOC bug detected "
"(old Symbol firmware?). Trying to work around... ",
dev->name);
priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
if (err)
printk("failed!\n");
else
printk("ok.\n");
}
return err;
}
static int orinoco_open(struct net_device *dev) static int orinoco_open(struct net_device *dev)
{ {
...@@ -715,337 +641,323 @@ int orinoco_stop(struct net_device *dev) ...@@ -715,337 +641,323 @@ int orinoco_stop(struct net_device *dev)
return err; return err;
} }
static int __orinoco_program_rids(struct net_device *dev) struct net_device_stats *
orinoco_get_stats(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
return &priv->stats;
}
struct iw_statistics *
orinoco_get_wireless_stats(struct net_device *dev)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
int err; struct iw_statistics *wstats = &priv->wstats;
struct hermes_idstring idbuf; int err = 0;
unsigned long flags;
/* Set the MAC address */ if (! netif_device_present(dev)) {
err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n",
HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); dev->name);
if (err) { return NULL; /* FIXME: Can we do better than this? */
printk(KERN_ERR "%s: Error %d setting MAC address\n", dev->name, err);
return err;
} }
/* Set up the link mode */ err = orinoco_lock(priv, &flags);
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type); if (err)
if (err) { return NULL; /* FIXME: Erg, we've been signalled, how
printk(KERN_ERR "%s: Error %d setting port type\n", dev->name, err); * do we propagate this back up? */
return err;
}
/* Set the channel/frequency */
if (priv->channel == 0) {
printk(KERN_DEBUG "%s: Channel is 0 in __orinoco_program_rids()\n", dev->name);
if (priv->createibss)
priv->channel = 10;
}
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel);
if (err) {
printk(KERN_ERR "%s: Error %d setting channel\n", dev->name, err);
return err;
}
if (priv->has_ibss) { if (priv->iw_mode == IW_MODE_ADHOC) {
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS, memset(&wstats->qual, 0, sizeof(wstats->qual));
priv->createibss); /* If a spy address is defined, we report stats of the
if (err) { * first spy address - Jean II */
printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err); if (SPY_NUMBER(priv)) {
return err; wstats->qual.qual = priv->spy_stat[0].qual;
wstats->qual.level = priv->spy_stat[0].level;
wstats->qual.noise = priv->spy_stat[0].noise;
wstats->qual.updated = priv->spy_stat[0].updated;
} }
} else {
struct {
u16 qual, signal, noise;
} __attribute__ ((packed)) cq;
if ((strlen(priv->desired_essid) == 0) && (priv->createibss) err = HERMES_READ_RECORD(hw, USER_BAP,
&& (!priv->has_ibss_any)) { HERMES_RID_COMMSQUALITY, &cq);
printk(KERN_WARNING "%s: This firmware requires an \
ESSID in IBSS-Ad-Hoc mode.\n", dev->name); wstats->qual.qual = (int)le16_to_cpu(cq.qual);
/* With wvlan_cs, in this case, we would crash. wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95;
* hopefully, this driver will behave better... wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95;
* Jean II */ wstats->qual.updated = 7;
}
} }
/* Set the desired ESSID */ /* We can't really wait for the tallies inquiry command to
idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); * complete, so we just use the previous results and trigger
memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); * a new tallies inquiry command for next time - Jean II */
/* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ /* FIXME: We're in user context (I think?), so we should just
err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, wait for the tallies to come through */
HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), err = hermes_inquire(hw, HERMES_INQ_TALLIES);
&idbuf);
if (err) { orinoco_unlock(priv, &flags);
printk(KERN_ERR "%s: Error %d setting OWNSSID\n", dev->name, err);
return err;
}
err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
&idbuf);
if (err) {
printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", dev->name, err);
return err;
}
/* Set the station name */ if (err)
idbuf.len = cpu_to_le16(strlen(priv->nick)); return NULL;
memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, return wstats;
HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), }
&idbuf);
if (err) {
printk(KERN_ERR "%s: Error %d setting nickname\n", dev->name, err);
return err;
}
/* Set AP density */ static void
if (priv->has_sensitivity) { orinoco_set_multicast_list(struct net_device *dev)
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, {
priv->ap_density); struct orinoco_private *priv = netdev_priv(dev);
if (err) { unsigned long flags;
printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
"Disabling sensitivity control\n", dev->name, err);
priv->has_sensitivity = 0; if (orinoco_lock(priv, &flags) != 0) {
} printk(KERN_DEBUG "%s: orinoco_set_multicast_list() "
"called when hw_unavailable\n", dev->name);
return;
} }
/* Set RTS threshold */ __orinoco_set_multicast_list(dev);
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh); orinoco_unlock(priv, &flags);
if (err) { }
printk(KERN_ERR "%s: Error %d setting RTS threshold\n", dev->name, err);
return err;
}
/* Set fragmentation threshold or MWO robustness */
if (priv->has_mwo)
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFMWOROBUST_AGERE,
priv->mwo_robust);
else
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
priv->frag_thresh);
if (err) {
printk(KERN_ERR "%s: Error %d setting framentation\n", dev->name, err);
return err;
}
/* Set bitrate */
err = __orinoco_hw_set_bitrate(priv);
if (err) {
printk(KERN_ERR "%s: Error %d setting bitrate\n", dev->name, err);
return err;
}
/* Set power management */
if (priv->has_pm) {
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED,
priv->pm_on);
if (err) {
printk(KERN_ERR "%s: Error %d setting up PM\n",
dev->name, err);
return err;
}
err = hermes_write_wordrec(hw, USER_BAP, static int
HERMES_RID_CNFMULTICASTRECEIVE, orinoco_change_mtu(struct net_device *dev, int new_mtu)
priv->pm_mcast); {
if (err) { struct orinoco_private *priv = netdev_priv(dev);
printk(KERN_ERR "%s: Error %d setting up PM\n",
dev->name, err);
return err;
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFMAXSLEEPDURATION,
priv->pm_period);
if (err) {
printk(KERN_ERR "%s: Error %d setting up PM\n",
dev->name, err);
return err;
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFPMHOLDOVERDURATION,
priv->pm_timeout);
if (err) {
printk(KERN_ERR "%s: Error %d setting up PM\n",
dev->name, err);
return err;
}
}
/* Set preamble - only for Symbol so far... */ if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
if (priv->has_preamble) { return -EINVAL;
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFPREAMBLE_SYMBOL,
priv->preamble);
if (err) {
printk(KERN_ERR "%s: Error %d setting preamble\n",
dev->name, err);
return err;
}
}
/* Set up encryption */ if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) >
if (priv->has_wep) { (priv->nicbuf_size - ETH_HLEN) )
err = __orinoco_hw_setup_wep(priv); return -EINVAL;
if (err) {
printk(KERN_ERR "%s: Error %d activating WEP\n",
dev->name, err);
return err;
}
}
/* Set promiscuity / multicast*/ dev->mtu = new_mtu;
priv->promiscuous = 0;
priv->mc_count = 0;
__orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */
return 0; return 0;
} }
/* xyzzy */ /********************************************************************/
static int orinoco_reconfigure(struct net_device *dev) /* Tx path */
/********************************************************************/
static int
orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
struct hermes *hw = &priv->hw; struct net_device_stats *stats = &priv->stats;
unsigned long flags; hermes_t *hw = &priv->hw;
int err = 0; int err = 0;
u16 txfid = priv->txfid;
char *p;
struct ethhdr *eh;
int len, data_len, data_off;
struct hermes_tx_descriptor desc;
unsigned long flags;
if (priv->broken_disableport) { TRACE_ENTER(dev->name);
schedule_work(&priv->reset_work);
if (! netif_running(dev)) {
printk(KERN_ERR "%s: Tx on stopped device!\n",
dev->name);
TRACE_EXIT(dev->name);
return 1;
}
if (netif_queue_stopped(dev)) {
printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
dev->name);
TRACE_EXIT(dev->name);
return 1;
}
if (orinoco_lock(priv, &flags) != 0) {
printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
dev->name);
TRACE_EXIT(dev->name);
/* BUG(); */
return 1;
}
if (! priv->connected) {
/* Oops, the firmware hasn't established a connection,
silently drop the packet (this seems to be the
safest approach). */
stats->tx_errors++;
orinoco_unlock(priv, &flags);
dev_kfree_skb(skb);
TRACE_EXIT(dev->name);
return 0; return 0;
} }
err = orinoco_lock(priv, &flags); /* Length of the packet body */
if (err) /* FIXME: what if the skb is smaller than this? */
return err; len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN);
eh = (struct ethhdr *)skb->data;
err = hermes_disable_port(hw, 0);
memset(&desc, 0, sizeof(desc));
desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX);
err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0);
if (err) { if (err) {
printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n", printk(KERN_ERR "%s: Error %d writing Tx descriptor to BAP\n",
dev->name); dev->name, err);
priv->broken_disableport = 1; stats->tx_errors++;
goto out; goto fail;
} }
err = __orinoco_program_rids(dev); /* Clear the 802.11 header and data length fields - some
if (err) { * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
printk(KERN_WARNING "%s: Unable to reconfigure card\n", * if this isn't done. */
dev->name); hermes_clear_words(hw, HERMES_DATA0,
goto out; HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
/* Encapsulate Ethernet-II frames */
if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */
struct header_struct hdr;
data_len = len;
data_off = HERMES_802_3_OFFSET + sizeof(hdr);
p = skb->data + ETH_HLEN;
/* 802.3 header */
memcpy(hdr.dest, eh->h_dest, ETH_ALEN);
memcpy(hdr.src, eh->h_source, ETH_ALEN);
hdr.len = htons(data_len + ENCAPS_OVERHEAD);
/* 802.2 header */
memcpy(&hdr.dsap, &encaps_hdr, sizeof(encaps_hdr));
hdr.ethertype = eh->h_proto;
err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
txfid, HERMES_802_3_OFFSET);
if (err) {
printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
dev->name, err);
stats->tx_errors++;
goto fail;
}
} else { /* IEEE 802.3 frame */
data_len = len + ETH_HLEN;
data_off = HERMES_802_3_OFFSET;
p = skb->data;
} }
err = hermes_enable_port(hw, 0); /* Round up for odd length packets */
err = hermes_bap_pwrite(hw, USER_BAP, p, RUP_EVEN(data_len), txfid, data_off);
if (err) { if (err) {
printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
dev->name); dev->name, err);
goto out; stats->tx_errors++;
goto fail;
} }
out: /* Finally, we actually initiate the send */
netif_stop_queue(dev);
err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, txfid, NULL);
if (err) { if (err) {
printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); netif_start_queue(dev);
schedule_work(&priv->reset_work); printk(KERN_ERR "%s: Error %d transmitting packet\n", dev->name, err);
err = 0; stats->tx_errors++;
goto fail;
} }
dev->trans_start = jiffies;
stats->tx_bytes += data_off + data_len;
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return err;
dev_kfree_skb(skb);
TRACE_EXIT(dev->name);
return 0;
fail:
TRACE_EXIT(dev->name);
orinoco_unlock(priv, &flags);
return err;
} }
/* This must be called from user context, without locks held - use static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
* schedule_work() */
static void orinoco_reset(struct net_device *dev)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
struct hermes *hw = &priv->hw;
int err;
unsigned long flags;
err = orinoco_lock(priv, &flags);
if (err)
/* When the hardware becomes available again, whatever
* detects that is responsible for re-initializing
* it. So no need for anything further*/
return;
netif_stop_queue(dev); u16 fid = hermes_read_regn(hw, ALLOCFID);
/* Shut off interrupts. Depending on what state the hardware if (fid != priv->txfid) {
* is in, this might not work, but we'll try anyway */ if (fid != DUMMY_FID)
hermes_set_irqmask(hw, 0); printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
hermes_write_regn(hw, EVACK, 0xffff); dev->name, fid);
return;
} else {
netif_wake_queue(dev);
}
priv->hw_unavailable++; hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ }
priv->connected = 0;
orinoco_unlock(priv, &flags); static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
{
struct orinoco_private *priv = netdev_priv(dev);
struct net_device_stats *stats = &priv->stats;
if (priv->hard_reset) stats->tx_packets++;
err = (*priv->hard_reset)(priv);
if (err) {
printk(KERN_ERR "%s: orinoco_reset: Error %d performing hard reset\n",
dev->name, err);
/* FIXME: shutdown of some sort */
return;
}
err = orinoco_reinit_firmware(dev); hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
}
static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
{
struct orinoco_private *priv = netdev_priv(dev);
struct net_device_stats *stats = &priv->stats;
u16 fid = hermes_read_regn(hw, TXCOMPLFID);
struct hermes_tx_descriptor desc;
int err = 0;
if (fid == DUMMY_FID)
return; /* Nothing's really happened */
err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), fid, 0);
if (err) { if (err) {
printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
dev->name, err); "(FID=%04X error %d)\n",
return; dev->name, fid, err);
} else {
DEBUG(1, "%s: Tx error, status %d\n",
dev->name, le16_to_cpu(desc.status));
} }
stats->tx_errors++;
spin_lock_irq(&priv->lock); /* This has to be called from user context */ hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
}
priv->hw_unavailable--; static void
orinoco_tx_timeout(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
struct net_device_stats *stats = &priv->stats;
struct hermes *hw = &priv->hw;
/* priv->open or priv->hw_unavailable might have changed while printk(KERN_WARNING "%s: Tx timeout! "
* we dropped the lock */ "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n",
if (priv->open && (! priv->hw_unavailable)) { dev->name, hermes_read_regn(hw, ALLOCFID),
err = __orinoco_up(dev); hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
if (err) {
printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
dev->name, err);
} else
dev->trans_start = jiffies;
}
spin_unlock_irq(&priv->lock); stats->tx_errors++;
return; schedule_work(&priv->reset_work);
} }
/********************************************************************/ /********************************************************************/
/* Internal helper functions */ /* Rx path (data frames) */
/********************************************************************/ /********************************************************************/
static inline void
set_port_type(struct orinoco_private *priv)
{
switch (priv->iw_mode) {
case IW_MODE_INFRA:
priv->port_type = 1;
priv->createibss = 0;
break;
case IW_MODE_ADHOC:
if (priv->prefer_port3) {
priv->port_type = 3;
priv->createibss = 0;
} else {
priv->port_type = priv->ibss_port;
priv->createibss = 1;
}
break;
default:
printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
priv->ndev->name);
}
}
/* Does the frame have a SNAP header indicating it should be /* Does the frame have a SNAP header indicating it should be
* de-encapsulated to Ethernet-II? */ * de-encapsulated to Ethernet-II? */
static inline int static inline int
...@@ -1060,804 +972,1033 @@ is_ethersnap(struct header_struct *hdr) ...@@ -1060,804 +972,1033 @@ is_ethersnap(struct header_struct *hdr)
&& ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) ); && ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) );
} }
static void static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
orinoco_set_multicast_list(struct net_device *dev) int level, int noise)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
unsigned long flags; int i;
if (orinoco_lock(priv, &flags) != 0) {
printk(KERN_DEBUG "%s: orinoco_set_multicast_list() "
"called when hw_unavailable\n", dev->name);
return;
}
__orinoco_set_multicast_list(dev); /* Gather wireless spy statistics: for each packet, compare the
orinoco_unlock(priv, &flags); * source address with out list, and if match, get the stats... */
for (i = 0; i < priv->spy_number; i++)
if (!memcmp(mac, priv->spy_address[i], ETH_ALEN)) {
priv->spy_stat[i].level = level - 0x95;
priv->spy_stat[i].noise = noise - 0x95;
priv->spy_stat[i].qual = (level > noise) ? (level - noise) : 0;
priv->spy_stat[i].updated = 7;
}
} }
/********************************************************************/ void
/* Hardware control functions */ orinoco_stat_gather(struct net_device *dev,
/********************************************************************/ struct sk_buff *skb,
struct hermes_rx_descriptor *desc)
{
struct orinoco_private *priv = netdev_priv(dev);
/* Using spy support with lots of Rx packets, like in an
* infrastructure (AP), will really slow down everything, because
* the MAC address must be compared to each entry of the spy list.
* If the user really asks for it (set some address in the
* spy list), we do it, but he will pay the price.
* Note that to get here, you need both WIRELESS_SPY
* compiled in AND some addresses in the list !!!
*/
/* Note : gcc will optimise the whole section away if
* WIRELESS_SPY is not defined... - Jean II */
if (SPY_NUMBER(priv)) {
orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN,
desc->signal, desc->silence);
}
}
static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
{ {
hermes_t *hw = &priv->hw; struct orinoco_private *priv = netdev_priv(dev);
int err = 0; struct net_device_stats *stats = &priv->stats;
struct iw_statistics *wstats = &priv->wstats;
struct sk_buff *skb = NULL;
u16 rxfid, status;
int length, data_len, data_off;
char *p;
struct hermes_rx_descriptor desc;
struct header_struct hdr;
struct ethhdr *eh;
int err;
if (priv->bitratemode >= BITRATE_TABLE_SIZE) { rxfid = hermes_read_regn(hw, RXFID);
printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
priv->ndev->name, priv->bitratemode); err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc),
return -EINVAL; rxfid, 0);
if (err) {
printk(KERN_ERR "%s: error %d reading Rx descriptor. "
"Frame dropped.\n", dev->name, err);
stats->rx_errors++;
goto drop;
} }
switch (priv->firmware_type) { status = le16_to_cpu(desc.status);
case FIRMWARE_TYPE_AGERE:
err = hermes_write_wordrec(hw, USER_BAP, if (status & HERMES_RXSTAT_ERR) {
HERMES_RID_CNFTXRATECONTROL, if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
bitrate_table[priv->bitratemode].agere_txratectrl); wstats->discard.code++;
break; DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
case FIRMWARE_TYPE_INTERSIL: dev->name);
case FIRMWARE_TYPE_SYMBOL: } else {
err = hermes_write_wordrec(hw, USER_BAP, stats->rx_crc_errors++;
HERMES_RID_CNFTXRATECONTROL, DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name);
bitrate_table[priv->bitratemode].intersil_txratectrl); }
break; stats->rx_errors++;
default: goto drop;
BUG();
} }
return err; /* For now we ignore the 802.11 header completely, assuming
} that the card's firmware has handled anything vital */
err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr),
rxfid, HERMES_802_3_OFFSET);
if (err) {
printk(KERN_ERR "%s: error %d reading frame header. "
"Frame dropped.\n", dev->name, err);
stats->rx_errors++;
goto drop;
}
static int __orinoco_hw_setup_wep(struct orinoco_private *priv) length = ntohs(hdr.len);
{
hermes_t *hw = &priv->hw; /* Sanity checks */
int err = 0; if (length < 3) { /* No for even an 802.2 LLC header */
int master_wep_flag; /* At least on Symbol firmware with PCF we get quite a
int auth_flag; lot of these legitimately - Poll frames with no
data. */
stats->rx_dropped++;
goto drop;
}
if (length > IEEE802_11_DATA_LEN) {
printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
dev->name, length);
stats->rx_length_errors++;
stats->rx_errors++;
goto drop;
}
switch (priv->firmware_type) { /* We need space for the packet data itself, plus an ethernet
case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ header, plus 2 bytes so we can align the IP header on a
if (priv->wep_on) { 32bit boundary, plus 1 byte so we can read in odd length
err = hermes_write_wordrec(hw, USER_BAP, packets from the card, which has an IO granularity of 16
HERMES_RID_CNFTXKEY_AGERE, bits */
priv->tx_key); skb = dev_alloc_skb(length+ETH_HLEN+2+1);
if (err) if (!skb) {
return err; printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
dev->name);
err = HERMES_WRITE_RECORD(hw, USER_BAP, goto drop;
HERMES_RID_CNFWEPKEYS_AGERE, }
&priv->keys);
if (err)
return err;
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFWEPENABLED_AGERE,
priv->wep_on);
if (err)
return err;
break;
case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
master_wep_flag = 0; /* Off */
if (priv->wep_on) {
int keylen;
int i;
/* Fudge around firmware weirdness */
keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
/* Write all 4 keys */
for(i = 0; i < ORINOCO_MAX_KEYS; i++) {
/* int keylen = le16_to_cpu(priv->keys[i].len); */
if (keylen > LARGE_KEY_SIZE) {
printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
priv->ndev->name, i, keylen);
return -E2BIG;
}
err = hermes_write_ltv(hw, USER_BAP,
HERMES_RID_CNFDEFAULTKEY0 + i,
HERMES_BYTES_TO_RECLEN(keylen),
priv->keys[i].data);
if (err)
return err;
}
/* Write the index of the key used in transmission */ skb_reserve(skb, 2); /* This way the IP header is aligned */
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPDEFAULTKEYID,
priv->tx_key);
if (err)
return err;
if (priv->wep_restrict) {
auth_flag = 2;
master_wep_flag = 3;
} else {
/* Authentication is where Intersil and Symbol
* firmware differ... */
auth_flag = 1;
if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
master_wep_flag = 3; /* Symbol */
else
master_wep_flag = 1; /* Intersil */
}
/* Handle decapsulation
* In most cases, the firmware tell us about SNAP frames.
* For some reason, the SNAP frames sent by LinkSys APs
* are not properly recognised by most firmwares.
* So, check ourselves */
if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
is_ethersnap(&hdr)) {
/* These indicate a SNAP within 802.2 LLC within
802.11 frame which we'll need to de-encapsulate to
the original EthernetII frame. */
err = hermes_write_wordrec(hw, USER_BAP, if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */
HERMES_RID_CNFAUTHENTICATION, auth_flag); stats->rx_length_errors++;
if (err) goto drop;
return err;
} }
/* Master WEP setting : on/off */
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFWEPFLAGS_INTERSIL,
master_wep_flag);
if (err)
return err;
break; /* Remove SNAP header, reconstruct EthernetII frame */
data_len = length - ENCAPS_OVERHEAD;
data_off = HERMES_802_3_OFFSET + sizeof(hdr);
default: eh = (struct ethhdr *)skb_put(skb, ETH_HLEN);
if (priv->wep_on) {
printk(KERN_ERR "%s: WEP enabled, although not supported!\n", memcpy(eh, &hdr, 2 * ETH_ALEN);
priv->ndev->name); eh->h_proto = hdr.ethertype;
return -EINVAL; } else {
} /* All other cases indicate a genuine 802.3 frame. No
decapsulation needed. We just throw the whole
thing in, and hope the protocol layer can deal with
it as 802.3 */
data_len = length;
data_off = HERMES_802_3_OFFSET;
/* FIXME: we re-read from the card data we already read here */
} }
return 0; p = skb_put(skb, data_len);
} err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len),
rxfid, data_off);
if (err) {
printk(KERN_ERR "%s: error %d reading frame. "
"Frame dropped.\n", dev->name, err);
stats->rx_errors++;
goto drop;
}
static int orinoco_hw_get_bssid(struct orinoco_private *priv, dev->last_rx = jiffies;
char buf[ETH_ALEN]) skb->dev = dev;
{ skb->protocol = eth_type_trans(skb, dev);
hermes_t *hw = &priv->hw; skb->ip_summed = CHECKSUM_NONE;
int err = 0;
unsigned long flags; /* Process the wireless stats if needed */
orinoco_stat_gather(dev, skb, &desc);
err = orinoco_lock(priv, &flags); /* Pass the packet to the networking stack */
if (err) netif_rx(skb);
return err; stats->rx_packets++;
stats->rx_bytes += length;
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, return;
ETH_ALEN, NULL, buf);
orinoco_unlock(priv, &flags); drop:
stats->rx_dropped++;
return err; if (skb)
dev_kfree_skb_irq(skb);
return;
} }
static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, /********************************************************************/
char buf[IW_ESSID_MAX_SIZE+1]) /* Rx path (info frames) */
{ /********************************************************************/
hermes_t *hw = &priv->hw;
int err = 0;
struct hermes_idstring essidbuf;
char *p = (char *)(&essidbuf.val);
int len;
unsigned long flags;
err = orinoco_lock(priv, &flags);
if (err)
return err;
if (strlen(priv->desired_essid) > 0) {
/* We read the desired SSID from the hardware rather
than from priv->desired_essid, just in case the
firmware is allowed to change it on us. I'm not
sure about this */
/* My guess is that the OWNSSID should always be whatever
* we set to the card, whereas CURRENT_SSID is the one that
* may change... - Jean II */
u16 rid;
*active = 1; static void print_linkstatus(struct net_device *dev, u16 status)
{
char * s;
rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : if (suppress_linkstatus)
HERMES_RID_CNFDESIREDSSID; return;
err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
NULL, &essidbuf);
if (err)
goto fail_unlock;
} else {
*active = 0;
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, switch (status) {
sizeof(essidbuf), NULL, &essidbuf); case HERMES_LINKSTATUS_NOT_CONNECTED:
if (err) s = "Not Connected";
goto fail_unlock; break;
case HERMES_LINKSTATUS_CONNECTED:
s = "Connected";
break;
case HERMES_LINKSTATUS_DISCONNECTED:
s = "Disconnected";
break;
case HERMES_LINKSTATUS_AP_CHANGE:
s = "AP Changed";
break;
case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
s = "AP Out of Range";
break;
case HERMES_LINKSTATUS_AP_IN_RANGE:
s = "AP In Range";
break;
case HERMES_LINKSTATUS_ASSOC_FAILED:
s = "Association Failed";
break;
default:
s = "UNKNOWN";
} }
len = le16_to_cpu(essidbuf.len); printk(KERN_INFO "%s: New link status: %s (%04x)\n",
dev->name, s, status);
memset(buf, 0, IW_ESSID_MAX_SIZE+1);
memcpy(buf, p, len);
buf[len] = '\0';
fail_unlock:
orinoco_unlock(priv, &flags);
return err;
} }
static long orinoco_hw_get_freq(struct orinoco_private *priv) static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
{ {
struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; u16 infofid;
int err = 0; struct {
u16 channel; u16 len;
long freq = 0; u16 type;
unsigned long flags; } __attribute__ ((packed)) info;
int len, type;
int err;
err = orinoco_lock(priv, &flags); /* This is an answer to an INQUIRE command that we did earlier,
if (err) * or an information "event" generated by the card
return err; * The controller return to us a pseudo frame containing
* the information in question - Jean II */
infofid = hermes_read_regn(hw, INFOFID);
/* Read the info frame header - don't try too hard */
err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
infofid, 0);
if (err) {
printk(KERN_ERR "%s: error %d reading info frame. "
"Frame dropped.\n", dev->name, err);
return;
}
err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel); len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len));
if (err) type = le16_to_cpu(info.type);
goto out;
/* Intersil firmware 1.3.5 returns 0 when the interface is down */ switch (type) {
if (channel == 0) { case HERMES_INQ_TALLIES: {
err = -EBUSY; struct hermes_tallies_frame tallies;
goto out; struct iw_statistics *wstats = &priv->wstats;
if (len > sizeof(tallies)) {
printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n",
dev->name, len);
len = sizeof(tallies);
}
/* Read directly the data (no seek) */
hermes_read_words(hw, HERMES_DATA1, (void *) &tallies,
len / 2); /* FIXME: blech! */
/* Increment our various counters */
/* wstats->discard.nwid - no wrong BSSID stuff */
wstats->discard.code +=
le16_to_cpu(tallies.RxWEPUndecryptable);
if (len == sizeof(tallies))
wstats->discard.code +=
le16_to_cpu(tallies.RxDiscards_WEPICVError) +
le16_to_cpu(tallies.RxDiscards_WEPExcluded);
wstats->discard.misc +=
le16_to_cpu(tallies.TxDiscardsWrongSA);
wstats->discard.fragment +=
le16_to_cpu(tallies.RxMsgInBadMsgFragments);
wstats->discard.retries +=
le16_to_cpu(tallies.TxRetryLimitExceeded);
/* wstats->miss.beacon - no match */
} }
break;
case HERMES_INQ_LINKSTATUS: {
struct hermes_linkstatus linkstatus;
u16 newstatus;
if (len != sizeof(linkstatus)) {
printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
dev->name, len);
break;
}
if ( (channel < 1) || (channel > NUM_CHANNELS) ) { hermes_read_words(hw, HERMES_DATA1, (void *) &linkstatus,
printk(KERN_WARNING "%s: Channel out of range (%d)!\n", len / 2);
priv->ndev->name, channel); newstatus = le16_to_cpu(linkstatus.linkstatus);
err = -EBUSY;
goto out;
} if ( (newstatus == HERMES_LINKSTATUS_CONNECTED)
freq = channel_frequency[channel-1] * 100000; || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
|| (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE) )
priv->connected = 1;
else if ( (newstatus == HERMES_LINKSTATUS_NOT_CONNECTED)
|| (newstatus == HERMES_LINKSTATUS_DISCONNECTED)
|| (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE)
|| (newstatus == HERMES_LINKSTATUS_ASSOC_FAILED) )
priv->connected = 0;
out: if (newstatus != priv->last_linkstatus)
orinoco_unlock(priv, &flags); print_linkstatus(dev, newstatus);
if (err > 0) priv->last_linkstatus = newstatus;
err = -EBUSY; }
return err ? err : freq; break;
default:
printk(KERN_DEBUG "%s: Unknown information frame received (type %04x).\n",
dev->name, type);
/* We don't actually do anything about it */
break;
}
} }
static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
int *numrates, s32 *rates, int max)
{ {
hermes_t *hw = &priv->hw; if (net_ratelimit())
struct hermes_idstring list; printk(KERN_WARNING "%s: Information frame lost.\n", dev->name);
unsigned char *p = (unsigned char *)&list.val; }
int err = 0;
int num;
int i;
unsigned long flags;
err = orinoco_lock(priv, &flags); /********************************************************************/
if (err) /* Internal hardware control routines */
return err; /********************************************************************/
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, int __orinoco_up(struct net_device *dev)
sizeof(list), NULL, &list); {
orinoco_unlock(priv, &flags); struct orinoco_private *priv = netdev_priv(dev);
struct hermes *hw = &priv->hw;
int err;
if (err) err = __orinoco_program_rids(dev);
if (err) {
printk(KERN_ERR "%s: Error %d configuring card\n",
dev->name, err);
return err; return err;
}
num = le16_to_cpu(list.len);
*numrates = num;
num = min(num, max);
for (i = 0; i < num; i++) { /* Fire things up again */
rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ hermes_set_irqmask(hw, ORINOCO_INTEN);
err = hermes_enable_port(hw, 0);
if (err) {
printk(KERN_ERR "%s: Error %d enabling MAC port\n",
dev->name, err);
return err;
} }
netif_start_queue(dev);
return 0; return 0;
} }
#if 0 int __orinoco_down(struct net_device *dev)
static void show_rx_frame(struct orinoco_rxframe_hdr *frame)
{ {
printk(KERN_DEBUG "RX descriptor:\n"); struct orinoco_private *priv = netdev_priv(dev);
printk(KERN_DEBUG " status = 0x%04x\n", frame->desc.status); struct hermes *hw = &priv->hw;
printk(KERN_DEBUG " time = 0x%08x\n", frame->desc.time); int err;
printk(KERN_DEBUG " silence = 0x%02x\n", frame->desc.silence);
printk(KERN_DEBUG " signal = 0x%02x\n", frame->desc.signal);
printk(KERN_DEBUG " rate = 0x%02x\n", frame->desc.rate);
printk(KERN_DEBUG " rxflow = 0x%02x\n", frame->desc.rxflow);
printk(KERN_DEBUG " reserved = 0x%08x\n", frame->desc.reserved);
printk(KERN_DEBUG "IEEE 802.11 header:\n"); netif_stop_queue(dev);
printk(KERN_DEBUG " frame_ctl = 0x%04x\n",
frame->p80211.frame_ctl);
printk(KERN_DEBUG " duration_id = 0x%04x\n",
frame->p80211.duration_id);
printk(KERN_DEBUG " addr1 = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p80211.addr1[0], frame->p80211.addr1[1],
frame->p80211.addr1[2], frame->p80211.addr1[3],
frame->p80211.addr1[4], frame->p80211.addr1[5]);
printk(KERN_DEBUG " addr2 = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p80211.addr2[0], frame->p80211.addr2[1],
frame->p80211.addr2[2], frame->p80211.addr2[3],
frame->p80211.addr2[4], frame->p80211.addr2[5]);
printk(KERN_DEBUG " addr3 = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p80211.addr3[0], frame->p80211.addr3[1],
frame->p80211.addr3[2], frame->p80211.addr3[3],
frame->p80211.addr3[4], frame->p80211.addr3[5]);
printk(KERN_DEBUG " seq_ctl = 0x%04x\n",
frame->p80211.seq_ctl);
printk(KERN_DEBUG " addr4 = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p80211.addr4[0], frame->p80211.addr4[1],
frame->p80211.addr4[2], frame->p80211.addr4[3],
frame->p80211.addr4[4], frame->p80211.addr4[5]);
printk(KERN_DEBUG " data_len = 0x%04x\n",
frame->p80211.data_len);
printk(KERN_DEBUG "IEEE 802.3 header:\n"); if (! priv->hw_unavailable) {
printk(KERN_DEBUG " dest = %02x:%02x:%02x:%02x:%02x:%02x\n", if (! priv->broken_disableport) {
frame->p8023.h_dest[0], frame->p8023.h_dest[1], err = hermes_disable_port(hw, 0);
frame->p8023.h_dest[2], frame->p8023.h_dest[3], if (err) {
frame->p8023.h_dest[4], frame->p8023.h_dest[5]); /* Some firmwares (e.g. Intersil 1.3.x) seem
printk(KERN_DEBUG " src = %02x:%02x:%02x:%02x:%02x:%02x\n", * to have problems disabling the port, oh
frame->p8023.h_source[0], frame->p8023.h_source[1], * well, too bad. */
frame->p8023.h_source[2], frame->p8023.h_source[3], printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
frame->p8023.h_source[4], frame->p8023.h_source[5]); dev->name, err);
printk(KERN_DEBUG " len = 0x%04x\n", frame->p8023.h_proto); priv->broken_disableport = 1;
}
}
hermes_set_irqmask(hw, 0);
hermes_write_regn(hw, EVACK, 0xffff);
}
/* firmware will have to reassociate */
priv->last_linkstatus = 0xffff;
priv->connected = 0;
printk(KERN_DEBUG "IEEE 802.2 LLC/SNAP header:\n"); return 0;
printk(KERN_DEBUG " DSAP = 0x%02x\n", frame->p8022.dsap);
printk(KERN_DEBUG " SSAP = 0x%02x\n", frame->p8022.ssap);
printk(KERN_DEBUG " ctrl = 0x%02x\n", frame->p8022.ctrl);
printk(KERN_DEBUG " OUI = %02x:%02x:%02x\n",
frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]);
printk(KERN_DEBUG " ethertype = 0x%04x\n", frame->ethertype);
} }
#endif /* 0 */
/* int orinoco_reinit_firmware(struct net_device *dev)
* Interrupt handler
*/
irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{ {
struct net_device *dev = (struct net_device *)dev_id;
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; struct hermes *hw = &priv->hw;
int count = MAX_IRQLOOPS_PER_IRQ; int err;
u16 evstat, events;
/* These are used to detect a runaway interrupt situation */
/* If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
* we panic and shut down the hardware */
static int last_irq_jiffy = 0; /* jiffies value the last time we were called */
static int loops_this_jiffy = 0;
unsigned long flags;
if (orinoco_lock(priv, &flags) != 0) { err = hermes_init(hw);
/* If hw is unavailable - we don't know if the irq was if (err)
* for us or not */ return err;
return IRQ_HANDLED;
}
evstat = hermes_read_regn(hw, EVSTAT); err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
events = evstat & hw->inten; if (err == -EIO) {
if (! events) { /* Try workaround for old Symbol firmware bug */
orinoco_unlock(priv, &flags); printk(KERN_WARNING "%s: firmware ALLOC bug detected "
return IRQ_NONE; "(old Symbol firmware?). Trying to work around... ",
} dev->name);
if (jiffies != last_irq_jiffy) priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
loops_this_jiffy = 0; err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
last_irq_jiffy = jiffies; if (err)
printk("failed!\n");
else
printk("ok.\n");
}
while (events && count--) { return err;
if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { }
printk(KERN_WARNING "%s: IRQ handler is looping too "
"much! Resetting.\n", dev->name);
/* Disable interrupts for now */
hermes_set_irqmask(hw, 0);
schedule_work(&priv->reset_work);
break;
}
/* Check the card hasn't been removed */ static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
if (! hermes_present(hw)) { {
DEBUG(0, "orinoco_interrupt(): card removed\n"); hermes_t *hw = &priv->hw;
break; int err = 0;
}
if (events & HERMES_EV_TICK) if (priv->bitratemode >= BITRATE_TABLE_SIZE) {
__orinoco_ev_tick(dev, hw); printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
if (events & HERMES_EV_WTERR) priv->ndev->name, priv->bitratemode);
__orinoco_ev_wterr(dev, hw); return -EINVAL;
if (events & HERMES_EV_INFDROP) }
__orinoco_ev_infdrop(dev, hw);
if (events & HERMES_EV_INFO)
__orinoco_ev_info(dev, hw);
if (events & HERMES_EV_RX)
__orinoco_ev_rx(dev, hw);
if (events & HERMES_EV_TXEXC)
__orinoco_ev_txexc(dev, hw);
if (events & HERMES_EV_TX)
__orinoco_ev_tx(dev, hw);
if (events & HERMES_EV_ALLOC)
__orinoco_ev_alloc(dev, hw);
hermes_write_regn(hw, EVACK, events);
evstat = hermes_read_regn(hw, EVSTAT); switch (priv->firmware_type) {
events = evstat & hw->inten; case FIRMWARE_TYPE_AGERE:
}; err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFTXRATECONTROL,
bitrate_table[priv->bitratemode].agere_txratectrl);
break;
case FIRMWARE_TYPE_INTERSIL:
case FIRMWARE_TYPE_SYMBOL:
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFTXRATECONTROL,
bitrate_table[priv->bitratemode].intersil_txratectrl);
break;
default:
BUG();
}
orinoco_unlock(priv, &flags); return err;
return IRQ_HANDLED;
} }
static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw) static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
{ {
printk(KERN_DEBUG "%s: TICK\n", dev->name); hermes_t *hw = &priv->hw;
} int err = 0;
int master_wep_flag;
int auth_flag;
static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw) switch (priv->firmware_type) {
{ case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
/* This seems to happen a fair bit under load, but ignoring it if (priv->wep_on) {
seems to work fine...*/ err = hermes_write_wordrec(hw, USER_BAP,
printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", HERMES_RID_CNFTXKEY_AGERE,
dev->name); priv->tx_key);
} if (err)
return err;
err = HERMES_WRITE_RECORD(hw, USER_BAP,
HERMES_RID_CNFWEPKEYS_AGERE,
&priv->keys);
if (err)
return err;
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFWEPENABLED_AGERE,
priv->wep_on);
if (err)
return err;
break;
static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
{ case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
if (net_ratelimit()) master_wep_flag = 0; /* Off */
printk(KERN_WARNING "%s: Information frame lost.\n", dev->name); if (priv->wep_on) {
} int keylen;
int i;
static void print_linkstatus(struct net_device *dev, u16 status) /* Fudge around firmware weirdness */
{ keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
char * s;
/* Write all 4 keys */
for(i = 0; i < ORINOCO_MAX_KEYS; i++) {
/* int keylen = le16_to_cpu(priv->keys[i].len); */
if (keylen > LARGE_KEY_SIZE) {
printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
priv->ndev->name, i, keylen);
return -E2BIG;
}
if (suppress_linkstatus) err = hermes_write_ltv(hw, USER_BAP,
return; HERMES_RID_CNFDEFAULTKEY0 + i,
HERMES_BYTES_TO_RECLEN(keylen),
priv->keys[i].data);
if (err)
return err;
}
/* Write the index of the key used in transmission */
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPDEFAULTKEYID,
priv->tx_key);
if (err)
return err;
if (priv->wep_restrict) {
auth_flag = 2;
master_wep_flag = 3;
} else {
/* Authentication is where Intersil and Symbol
* firmware differ... */
auth_flag = 1;
if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
master_wep_flag = 3; /* Symbol */
else
master_wep_flag = 1; /* Intersil */
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFAUTHENTICATION, auth_flag);
if (err)
return err;
}
/* Master WEP setting : on/off */
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFWEPFLAGS_INTERSIL,
master_wep_flag);
if (err)
return err;
switch (status) {
case HERMES_LINKSTATUS_NOT_CONNECTED:
s = "Not Connected";
break;
case HERMES_LINKSTATUS_CONNECTED:
s = "Connected";
break;
case HERMES_LINKSTATUS_DISCONNECTED:
s = "Disconnected";
break;
case HERMES_LINKSTATUS_AP_CHANGE:
s = "AP Changed";
break;
case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
s = "AP Out of Range";
break;
case HERMES_LINKSTATUS_AP_IN_RANGE:
s = "AP In Range";
break;
case HERMES_LINKSTATUS_ASSOC_FAILED:
s = "Association Failed";
break; break;
default: default:
s = "UNKNOWN"; if (priv->wep_on) {
printk(KERN_ERR "%s: WEP enabled, although not supported!\n",
priv->ndev->name);
return -EINVAL;
}
} }
printk(KERN_INFO "%s: New link status: %s (%04x)\n", return 0;
dev->name, s, status);
} }
static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) static int __orinoco_program_rids(struct net_device *dev)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
u16 infofid; hermes_t *hw = &priv->hw;
struct {
u16 len;
u16 type;
} __attribute__ ((packed)) info;
int len, type;
int err; int err;
struct hermes_idstring idbuf;
/* This is an answer to an INQUIRE command that we did earlier, /* Set the MAC address */
* or an information "event" generated by the card err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
* The controller return to us a pseudo frame containing HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
* the information in question - Jean II */ if (err) {
infofid = hermes_read_regn(hw, INFOFID); printk(KERN_ERR "%s: Error %d setting MAC address\n", dev->name, err);
return err;
}
/* Read the info frame header - don't try too hard */ /* Set up the link mode */
err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info), err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type);
infofid, 0);
if (err) { if (err) {
printk(KERN_ERR "%s: error %d reading info frame. " printk(KERN_ERR "%s: Error %d setting port type\n", dev->name, err);
"Frame dropped.\n", dev->name, err); return err;
return; }
/* Set the channel/frequency */
if (priv->channel == 0) {
printk(KERN_DEBUG "%s: Channel is 0 in __orinoco_program_rids()\n", dev->name);
if (priv->createibss)
priv->channel = 10;
}
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel);
if (err) {
printk(KERN_ERR "%s: Error %d setting channel\n", dev->name, err);
return err;
}
if (priv->has_ibss) {
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS,
priv->createibss);
if (err) {
printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err);
return err;
}
if ((strlen(priv->desired_essid) == 0) && (priv->createibss)
&& (!priv->has_ibss_any)) {
printk(KERN_WARNING "%s: This firmware requires an \
ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
/* With wvlan_cs, in this case, we would crash.
* hopefully, this driver will behave better...
* Jean II */
}
}
/* Set the desired ESSID */
idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
/* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
&idbuf);
if (err) {
printk(KERN_ERR "%s: Error %d setting OWNSSID\n", dev->name, err);
return err;
}
err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
&idbuf);
if (err) {
printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", dev->name, err);
return err;
}
/* Set the station name */
idbuf.len = cpu_to_le16(strlen(priv->nick));
memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
&idbuf);
if (err) {
printk(KERN_ERR "%s: Error %d setting nickname\n", dev->name, err);
return err;
}
/* Set AP density */
if (priv->has_sensitivity) {
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE,
priv->ap_density);
if (err) {
printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. "
"Disabling sensitivity control\n", dev->name, err);
priv->has_sensitivity = 0;
}
}
/* Set RTS threshold */
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh);
if (err) {
printk(KERN_ERR "%s: Error %d setting RTS threshold\n", dev->name, err);
return err;
}
/* Set fragmentation threshold or MWO robustness */
if (priv->has_mwo)
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFMWOROBUST_AGERE,
priv->mwo_robust);
else
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
priv->frag_thresh);
if (err) {
printk(KERN_ERR "%s: Error %d setting framentation\n", dev->name, err);
return err;
}
/* Set bitrate */
err = __orinoco_hw_set_bitrate(priv);
if (err) {
printk(KERN_ERR "%s: Error %d setting bitrate\n", dev->name, err);
return err;
}
/* Set power management */
if (priv->has_pm) {
err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED,
priv->pm_on);
if (err) {
printk(KERN_ERR "%s: Error %d setting up PM\n",
dev->name, err);
return err;
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFMULTICASTRECEIVE,
priv->pm_mcast);
if (err) {
printk(KERN_ERR "%s: Error %d setting up PM\n",
dev->name, err);
return err;
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFMAXSLEEPDURATION,
priv->pm_period);
if (err) {
printk(KERN_ERR "%s: Error %d setting up PM\n",
dev->name, err);
return err;
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFPMHOLDOVERDURATION,
priv->pm_timeout);
if (err) {
printk(KERN_ERR "%s: Error %d setting up PM\n",
dev->name, err);
return err;
}
}
/* Set preamble - only for Symbol so far... */
if (priv->has_preamble) {
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFPREAMBLE_SYMBOL,
priv->preamble);
if (err) {
printk(KERN_ERR "%s: Error %d setting preamble\n",
dev->name, err);
return err;
}
} }
len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len));
type = le16_to_cpu(info.type);
switch (type) { /* Set up encryption */
case HERMES_INQ_TALLIES: { if (priv->has_wep) {
struct hermes_tallies_frame tallies; err = __orinoco_hw_setup_wep(priv);
struct iw_statistics *wstats = &priv->wstats; if (err) {
printk(KERN_ERR "%s: Error %d activating WEP\n",
if (len > sizeof(tallies)) { dev->name, err);
printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n", return err;
dev->name, len);
len = sizeof(tallies);
} }
/* Read directly the data (no seek) */
hermes_read_words(hw, HERMES_DATA1, (void *) &tallies,
len / 2); /* FIXME: blech! */
/* Increment our various counters */
/* wstats->discard.nwid - no wrong BSSID stuff */
wstats->discard.code +=
le16_to_cpu(tallies.RxWEPUndecryptable);
if (len == sizeof(tallies))
wstats->discard.code +=
le16_to_cpu(tallies.RxDiscards_WEPICVError) +
le16_to_cpu(tallies.RxDiscards_WEPExcluded);
wstats->discard.misc +=
le16_to_cpu(tallies.TxDiscardsWrongSA);
wstats->discard.fragment +=
le16_to_cpu(tallies.RxMsgInBadMsgFragments);
wstats->discard.retries +=
le16_to_cpu(tallies.TxRetryLimitExceeded);
/* wstats->miss.beacon - no match */
} }
break;
case HERMES_INQ_LINKSTATUS: {
struct hermes_linkstatus linkstatus;
u16 newstatus;
if (len != sizeof(linkstatus)) {
printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
dev->name, len);
break;
}
hermes_read_words(hw, HERMES_DATA1, (void *) &linkstatus, /* Set promiscuity / multicast*/
len / 2); priv->promiscuous = 0;
newstatus = le16_to_cpu(linkstatus.linkstatus); priv->mc_count = 0;
__orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */
if ( (newstatus == HERMES_LINKSTATUS_CONNECTED) return 0;
|| (newstatus == HERMES_LINKSTATUS_AP_CHANGE) }
|| (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE) )
priv->connected = 1;
else if ( (newstatus == HERMES_LINKSTATUS_NOT_CONNECTED)
|| (newstatus == HERMES_LINKSTATUS_DISCONNECTED)
|| (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE)
|| (newstatus == HERMES_LINKSTATUS_ASSOC_FAILED) )
priv->connected = 0;
if (newstatus != priv->last_linkstatus) /* FIXME: return int? */
print_linkstatus(dev, newstatus); static void
__orinoco_set_multicast_list(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw;
int err = 0;
int promisc, mc_count;
priv->last_linkstatus = newstatus; /* The Hermes doesn't seem to have an allmulti mode, so we go
* into promiscuous mode and let the upper levels deal. */
if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
(dev->mc_count > MAX_MULTICAST(priv)) ) {
promisc = 1;
mc_count = 0;
} else {
promisc = 0;
mc_count = dev->mc_count;
} }
break;
default: if (promisc != priv->promiscuous) {
printk(KERN_DEBUG "%s: Unknown information frame received (type %04x).\n", err = hermes_write_wordrec(hw, USER_BAP,
dev->name, type); HERMES_RID_CNFPROMISCUOUSMODE,
/* We don't actually do anything about it */ promisc);
break; if (err) {
printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
dev->name, err);
} else
priv->promiscuous = promisc;
}
if (! promisc && (mc_count || priv->mc_count) ) {
struct dev_mc_list *p = dev->mc_list;
hermes_multicast_t mclist;
int i;
for (i = 0; i < mc_count; i++) {
/* Paranoia: */
if (! p)
BUG(); /* Multicast list shorter than mc_count */
if (p->dmi_addrlen != ETH_ALEN)
BUG(); /* Bad address size in multicast list */
memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
p = p->next;
}
if (p)
printk(KERN_WARNING "Multicast list is longer than mc_count\n");
err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES,
HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN),
&mclist);
if (err)
printk(KERN_ERR "%s: Error %d setting multicast list.\n",
dev->name, err);
else
priv->mc_count = mc_count;
} }
/* Since we can set the promiscuous flag when it wasn't asked
for, make sure the net_device knows about it. */
if (priv->promiscuous)
dev->flags |= IFF_PROMISC;
else
dev->flags &= ~IFF_PROMISC;
} }
static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) static int orinoco_reconfigure(struct net_device *dev)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
struct net_device_stats *stats = &priv->stats; struct hermes *hw = &priv->hw;
struct iw_statistics *wstats = &priv->wstats; unsigned long flags;
struct sk_buff *skb = NULL; int err = 0;
u16 rxfid, status;
int length, data_len, data_off;
char *p;
struct hermes_rx_descriptor desc;
struct header_struct hdr;
struct ethhdr *eh;
int err;
rxfid = hermes_read_regn(hw, RXFID); if (priv->broken_disableport) {
schedule_work(&priv->reset_work);
return 0;
}
err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), err = orinoco_lock(priv, &flags);
rxfid, 0); if (err)
return err;
err = hermes_disable_port(hw, 0);
if (err) { if (err) {
printk(KERN_ERR "%s: error %d reading Rx descriptor. " printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n",
"Frame dropped.\n", dev->name, err); dev->name);
stats->rx_errors++; priv->broken_disableport = 1;
goto drop; goto out;
} }
status = le16_to_cpu(desc.status); err = __orinoco_program_rids(dev);
if (err) {
if (status & HERMES_RXSTAT_ERR) { printk(KERN_WARNING "%s: Unable to reconfigure card\n",
if (status & HERMES_RXSTAT_UNDECRYPTABLE) { dev->name);
wstats->discard.code++; goto out;
DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
dev->name);
} else {
stats->rx_crc_errors++;
DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name);
}
stats->rx_errors++;
goto drop;
} }
/* For now we ignore the 802.11 header completely, assuming err = hermes_enable_port(hw, 0);
that the card's firmware has handled anything vital */
err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr),
rxfid, HERMES_802_3_OFFSET);
if (err) { if (err) {
printk(KERN_ERR "%s: error %d reading frame header. " printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
"Frame dropped.\n", dev->name, err); dev->name);
stats->rx_errors++; goto out;
goto drop;
} }
length = ntohs(hdr.len); out:
if (err) {
/* Sanity checks */ printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
if (length < 3) { /* No for even an 802.2 LLC header */ schedule_work(&priv->reset_work);
/* At least on Symbol firmware with PCF we get quite a err = 0;
lot of these legitimately - Poll frames with no
data. */
stats->rx_dropped++;
goto drop;
}
if (length > IEEE802_11_DATA_LEN) {
printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
dev->name, length);
stats->rx_length_errors++;
stats->rx_errors++;
goto drop;
} }
/* We need space for the packet data itself, plus an ethernet orinoco_unlock(priv, &flags);
header, plus 2 bytes so we can align the IP header on a return err;
32bit boundary, plus 1 byte so we can read in odd length
packets from the card, which has an IO granularity of 16
bits */
skb = dev_alloc_skb(length+ETH_HLEN+2+1);
if (!skb) {
printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
dev->name);
goto drop;
}
skb_reserve(skb, 2); /* This way the IP header is aligned */ }
/* This must be called from user context, without locks held - use
* schedule_work() */
static void orinoco_reset(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
struct hermes *hw = &priv->hw;
int err;
unsigned long flags;
err = orinoco_lock(priv, &flags);
if (err)
/* When the hardware becomes available again, whatever
* detects that is responsible for re-initializing
* it. So no need for anything further*/
return;
/* Handle decapsulation netif_stop_queue(dev);
* In most cases, the firmware tell us about SNAP frames.
* For some reason, the SNAP frames sent by LinkSys APs
* are not properly recognised by most firmwares.
* So, check ourselves */
if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
is_ethersnap(&hdr)) {
/* These indicate a SNAP within 802.2 LLC within
802.11 frame which we'll need to de-encapsulate to
the original EthernetII frame. */
if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */ /* Shut off interrupts. Depending on what state the hardware
stats->rx_length_errors++; * is in, this might not work, but we'll try anyway */
goto drop; hermes_set_irqmask(hw, 0);
} hermes_write_regn(hw, EVACK, 0xffff);
/* Remove SNAP header, reconstruct EthernetII frame */ priv->hw_unavailable++;
data_len = length - ENCAPS_OVERHEAD; priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
data_off = HERMES_802_3_OFFSET + sizeof(hdr); priv->connected = 0;
eh = (struct ethhdr *)skb_put(skb, ETH_HLEN); orinoco_unlock(priv, &flags);
memcpy(eh, &hdr, 2 * ETH_ALEN); if (priv->hard_reset)
eh->h_proto = hdr.ethertype; err = (*priv->hard_reset)(priv);
} else { if (err) {
/* All other cases indicate a genuine 802.3 frame. No printk(KERN_ERR "%s: orinoco_reset: Error %d performing hard reset\n",
decapsulation needed. We just throw the whole dev->name, err);
thing in, and hope the protocol layer can deal with /* FIXME: shutdown of some sort */
it as 802.3 */ return;
data_len = length;
data_off = HERMES_802_3_OFFSET;
/* FIXME: we re-read from the card data we already read here */
} }
p = skb_put(skb, data_len); err = orinoco_reinit_firmware(dev);
err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len),
rxfid, data_off);
if (err) { if (err) {
printk(KERN_ERR "%s: error %d reading frame. " printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
"Frame dropped.\n", dev->name, err); dev->name, err);
stats->rx_errors++; return;
goto drop;
} }
dev->last_rx = jiffies; spin_lock_irq(&priv->lock); /* This has to be called from user context */
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_NONE;
/* Process the wireless stats if needed */
orinoco_stat_gather(dev, skb, &desc);
/* Pass the packet to the networking stack */ priv->hw_unavailable--;
netif_rx(skb);
stats->rx_packets++;
stats->rx_bytes += length;
return; /* priv->open or priv->hw_unavailable might have changed while
* we dropped the lock */
if (priv->open && (! priv->hw_unavailable)) {
err = __orinoco_up(dev);
if (err) {
printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
dev->name, err);
} else
dev->trans_start = jiffies;
}
drop: spin_unlock_irq(&priv->lock);
stats->rx_dropped++;
if (skb)
dev_kfree_skb_irq(skb);
return; return;
} }
static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) /********************************************************************/
{ /* Interrupt handler */
struct orinoco_private *priv = netdev_priv(dev); /********************************************************************/
struct net_device_stats *stats = &priv->stats;
u16 fid = hermes_read_regn(hw, TXCOMPLFID);
struct hermes_tx_descriptor desc;
int err = 0;
if (fid == DUMMY_FID)
return; /* Nothing's really happened */
err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), fid, 0);
if (err) {
printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
"(FID=%04X error %d)\n",
dev->name, fid, err);
} else {
DEBUG(1, "%s: Tx error, status %d\n",
dev->name, le16_to_cpu(desc.status));
}
stats->tx_errors++;
hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw)
{
printk(KERN_DEBUG "%s: TICK\n", dev->name);
} }
static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw) static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
{ {
struct orinoco_private *priv = netdev_priv(dev); /* This seems to happen a fair bit under load, but ignoring it
struct net_device_stats *stats = &priv->stats; seems to work fine...*/
printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
stats->tx_packets++; dev->name);
hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
} }
static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{ {
struct net_device *dev = (struct net_device *)dev_id;
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw;
int count = MAX_IRQLOOPS_PER_IRQ;
u16 evstat, events;
/* These are used to detect a runaway interrupt situation */
/* If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
* we panic and shut down the hardware */
static int last_irq_jiffy = 0; /* jiffies value the last time we were called */
static int loops_this_jiffy = 0;
unsigned long flags;
u16 fid = hermes_read_regn(hw, ALLOCFID); if (orinoco_lock(priv, &flags) != 0) {
/* If hw is unavailable - we don't know if the irq was
* for us or not */
return IRQ_HANDLED;
}
if (fid != priv->txfid) { evstat = hermes_read_regn(hw, EVSTAT);
if (fid != DUMMY_FID) events = evstat & hw->inten;
printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", if (! events) {
dev->name, fid); orinoco_unlock(priv, &flags);
return; return IRQ_NONE;
} else {
netif_wake_queue(dev);
} }
if (jiffies != last_irq_jiffy)
loops_this_jiffy = 0;
last_irq_jiffy = jiffies;
hermes_write_regn(hw, ALLOCFID, DUMMY_FID); while (events && count--) {
if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
printk(KERN_WARNING "%s: IRQ handler is looping too "
"much! Resetting.\n", dev->name);
/* Disable interrupts for now */
hermes_set_irqmask(hw, 0);
schedule_work(&priv->reset_work);
break;
}
/* Check the card hasn't been removed */
if (! hermes_present(hw)) {
DEBUG(0, "orinoco_interrupt(): card removed\n");
break;
}
if (events & HERMES_EV_TICK)
__orinoco_ev_tick(dev, hw);
if (events & HERMES_EV_WTERR)
__orinoco_ev_wterr(dev, hw);
if (events & HERMES_EV_INFDROP)
__orinoco_ev_infdrop(dev, hw);
if (events & HERMES_EV_INFO)
__orinoco_ev_info(dev, hw);
if (events & HERMES_EV_RX)
__orinoco_ev_rx(dev, hw);
if (events & HERMES_EV_TXEXC)
__orinoco_ev_txexc(dev, hw);
if (events & HERMES_EV_TX)
__orinoco_ev_tx(dev, hw);
if (events & HERMES_EV_ALLOC)
__orinoco_ev_alloc(dev, hw);
hermes_write_regn(hw, EVACK, events);
evstat = hermes_read_regn(hw, EVSTAT);
events = evstat & hw->inten;
};
orinoco_unlock(priv, &flags);
return IRQ_HANDLED;
} }
/********************************************************************/
/* Initialization */
/********************************************************************/
struct sta_id { struct sta_id {
u16 id, variant, major, minor; u16 id, variant, major, minor;
} __attribute__ ((packed)); } __attribute__ ((packed));
...@@ -2009,10 +2150,6 @@ static void determine_firmware(struct net_device *dev) ...@@ -2009,10 +2150,6 @@ static void determine_firmware(struct net_device *dev)
} }
} }
/*
* struct net_device methods
*/
static int static int
orinoco_init(struct net_device *dev) orinoco_init(struct net_device *dev)
{ {
...@@ -2180,370 +2317,215 @@ orinoco_init(struct net_device *dev) ...@@ -2180,370 +2317,215 @@ orinoco_init(struct net_device *dev)
goto out; goto out;
} }
/* Make the hardware available, as long as it hasn't been /* Make the hardware available, as long as it hasn't been
* removed elsewhere (e.g. by PCMCIA hot unplug) */ * removed elsewhere (e.g. by PCMCIA hot unplug) */
spin_lock_irq(&priv->lock); spin_lock_irq(&priv->lock);
priv->hw_unavailable--; priv->hw_unavailable--;
spin_unlock_irq(&priv->lock); spin_unlock_irq(&priv->lock);
printk(KERN_DEBUG "%s: ready\n", dev->name);
out:
TRACE_EXIT(dev->name);
return err;
}
struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct orinoco_private *))
{
struct net_device *dev;
struct orinoco_private *priv;
dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
if (!dev)
return NULL;
priv = netdev_priv(dev);
priv->ndev = dev;
if (sizeof_card)
priv->card = (void *)((unsigned long)dev->priv + sizeof(struct orinoco_private));
else
priv->card = NULL;
/* Setup / override net_device fields */
dev->init = orinoco_init;
dev->hard_start_xmit = orinoco_xmit;
dev->tx_timeout = orinoco_tx_timeout;
dev->watchdog_timeo = HZ; /* 1 second timeout */
dev->get_stats = orinoco_get_stats;
dev->get_wireless_stats = orinoco_get_wireless_stats;
dev->do_ioctl = orinoco_ioctl;
dev->change_mtu = orinoco_change_mtu;
dev->set_multicast_list = orinoco_set_multicast_list;
/* we use the default eth_mac_addr for setting the MAC addr */
/* Set up default callbacks */
dev->open = orinoco_open;
dev->stop = orinoco_stop;
priv->hard_reset = hard_reset;
spin_lock_init(&priv->lock);
priv->open = 0;
priv->hw_unavailable = 1; /* orinoco_init() must clear this
* before anything else touches the
* hardware */
INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev);
priv->last_linkstatus = 0xffff;
priv->connected = 0;
printk(KERN_DEBUG "%s: ready\n", dev->name); return dev;
out:
TRACE_EXIT(dev->name);
return err;
} }
struct net_device_stats * /********************************************************************/
orinoco_get_stats(struct net_device *dev) /* Wireless extensions */
{ /********************************************************************/
struct orinoco_private *priv = netdev_priv(dev);
return &priv->stats;
}
struct iw_statistics * static int orinoco_hw_get_bssid(struct orinoco_private *priv,
orinoco_get_wireless_stats(struct net_device *dev) char buf[ETH_ALEN])
{ {
struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
struct iw_statistics *wstats = &priv->wstats;
int err = 0; int err = 0;
unsigned long flags; unsigned long flags;
if (! netif_device_present(dev)) {
printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n",
dev->name);
return NULL; /* FIXME: Can we do better than this? */
}
err = orinoco_lock(priv, &flags); err = orinoco_lock(priv, &flags);
if (err) if (err)
return NULL; /* FIXME: Erg, we've been signalled, how return err;
* do we propagate this back up? */
if (priv->iw_mode == IW_MODE_ADHOC) {
memset(&wstats->qual, 0, sizeof(wstats->qual));
/* If a spy address is defined, we report stats of the
* first spy address - Jean II */
if (SPY_NUMBER(priv)) {
wstats->qual.qual = priv->spy_stat[0].qual;
wstats->qual.level = priv->spy_stat[0].level;
wstats->qual.noise = priv->spy_stat[0].noise;
wstats->qual.updated = priv->spy_stat[0].updated;
}
} else {
struct {
u16 qual, signal, noise;
} __attribute__ ((packed)) cq;
err = HERMES_READ_RECORD(hw, USER_BAP, err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
HERMES_RID_COMMSQUALITY, &cq); ETH_ALEN, NULL, buf);
wstats->qual.qual = (int)le16_to_cpu(cq.qual);
wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95;
wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95;
wstats->qual.updated = 7;
}
/* We can't really wait for the tallies inquiry command to
* complete, so we just use the previous results and trigger
* a new tallies inquiry command for next time - Jean II */
/* FIXME: We're in user context (I think?), so we should just
wait for the tallies to come through */
err = hermes_inquire(hw, HERMES_INQ_TALLIES);
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
if (err) return err;
return NULL;
return wstats;
}
static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
int level, int noise)
{
struct orinoco_private *priv = netdev_priv(dev);
int i;
/* Gather wireless spy statistics: for each packet, compare the
* source address with out list, and if match, get the stats... */
for (i = 0; i < priv->spy_number; i++)
if (!memcmp(mac, priv->spy_address[i], ETH_ALEN)) {
priv->spy_stat[i].level = level - 0x95;
priv->spy_stat[i].noise = noise - 0x95;
priv->spy_stat[i].qual = (level > noise) ? (level - noise) : 0;
priv->spy_stat[i].updated = 7;
}
}
void
orinoco_stat_gather(struct net_device *dev,
struct sk_buff *skb,
struct hermes_rx_descriptor *desc)
{
struct orinoco_private *priv = netdev_priv(dev);
/* Using spy support with lots of Rx packets, like in an
* infrastructure (AP), will really slow down everything, because
* the MAC address must be compared to each entry of the spy list.
* If the user really asks for it (set some address in the
* spy list), we do it, but he will pay the price.
* Note that to get here, you need both WIRELESS_SPY
* compiled in AND some addresses in the list !!!
*/
/* Note : gcc will optimise the whole section away if
* WIRELESS_SPY is not defined... - Jean II */
if (SPY_NUMBER(priv)) {
orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN,
desc->signal, desc->silence);
}
} }
static int static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
orinoco_xmit(struct sk_buff *skb, struct net_device *dev) char buf[IW_ESSID_MAX_SIZE+1])
{ {
struct orinoco_private *priv = netdev_priv(dev);
struct net_device_stats *stats = &priv->stats;
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
int err = 0; int err = 0;
u16 txfid = priv->txfid; struct hermes_idstring essidbuf;
char *p; char *p = (char *)(&essidbuf.val);
struct ethhdr *eh; int len;
int len, data_len, data_off;
struct hermes_tx_descriptor desc;
unsigned long flags; unsigned long flags;
TRACE_ENTER(dev->name); err = orinoco_lock(priv, &flags);
if (err)
if (! netif_running(dev)) { return err;
printk(KERN_ERR "%s: Tx on stopped device!\n",
dev->name);
TRACE_EXIT(dev->name);
return 1;
}
if (netif_queue_stopped(dev)) {
printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
dev->name);
TRACE_EXIT(dev->name);
return 1;
}
if (orinoco_lock(priv, &flags) != 0) {
printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n",
dev->name);
TRACE_EXIT(dev->name);
/* BUG(); */
return 1;
}
if (! priv->connected) {
/* Oops, the firmware hasn't established a connection,
silently drop the packet (this seems to be the
safest approach). */
stats->tx_errors++;
orinoco_unlock(priv, &flags);
dev_kfree_skb(skb);
TRACE_EXIT(dev->name);
return 0;
}
/* Length of the packet body */
/* FIXME: what if the skb is smaller than this? */
len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN);
eh = (struct ethhdr *)skb->data;
memset(&desc, 0, sizeof(desc));
desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX);
err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0);
if (err) {
printk(KERN_ERR "%s: Error %d writing Tx descriptor to BAP\n",
dev->name, err);
stats->tx_errors++;
goto fail;
}
/* Clear the 802.11 header and data length fields - some if (strlen(priv->desired_essid) > 0) {
* firmwares (e.g. Lucent/Agere 8.xx) appear to get confused /* We read the desired SSID from the hardware rather
* if this isn't done. */ than from priv->desired_essid, just in case the
hermes_clear_words(hw, HERMES_DATA0, firmware is allowed to change it on us. I'm not
HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); sure about this */
/* My guess is that the OWNSSID should always be whatever
* we set to the card, whereas CURRENT_SSID is the one that
* may change... - Jean II */
u16 rid;
/* Encapsulate Ethernet-II frames */ *active = 1;
if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */
struct header_struct hdr;
data_len = len;
data_off = HERMES_802_3_OFFSET + sizeof(hdr);
p = skb->data + ETH_HLEN;
/* 802.3 header */ rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
memcpy(hdr.dest, eh->h_dest, ETH_ALEN); HERMES_RID_CNFDESIREDSSID;
memcpy(hdr.src, eh->h_source, ETH_ALEN);
hdr.len = htons(data_len + ENCAPS_OVERHEAD);
/* 802.2 header */ err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
memcpy(&hdr.dsap, &encaps_hdr, sizeof(encaps_hdr)); NULL, &essidbuf);
if (err)
hdr.ethertype = eh->h_proto; goto fail_unlock;
err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr), } else {
txfid, HERMES_802_3_OFFSET); *active = 0;
if (err) {
printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
dev->name, err);
stats->tx_errors++;
goto fail;
}
} else { /* IEEE 802.3 frame */
data_len = len + ETH_HLEN;
data_off = HERMES_802_3_OFFSET;
p = skb->data;
}
/* Round up for odd length packets */
err = hermes_bap_pwrite(hw, USER_BAP, p, RUP_EVEN(data_len), txfid, data_off);
if (err) {
printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
dev->name, err);
stats->tx_errors++;
goto fail;
}
/* Finally, we actually initiate the send */
netif_stop_queue(dev);
err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, txfid, NULL);
if (err) {
netif_start_queue(dev);
printk(KERN_ERR "%s: Error %d transmitting packet\n", dev->name, err);
stats->tx_errors++;
goto fail;
}
dev->trans_start = jiffies;
stats->tx_bytes += data_off + data_len;
orinoco_unlock(priv, &flags);
dev_kfree_skb(skb); err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
sizeof(essidbuf), NULL, &essidbuf);
if (err)
goto fail_unlock;
}
TRACE_EXIT(dev->name); len = le16_to_cpu(essidbuf.len);
return 0; memset(buf, 0, IW_ESSID_MAX_SIZE+1);
fail: memcpy(buf, p, len);
TRACE_EXIT(dev->name); buf[len] = '\0';
fail_unlock:
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return err;
return err;
} }
static void static long orinoco_hw_get_freq(struct orinoco_private *priv)
orinoco_tx_timeout(struct net_device *dev)
{ {
struct orinoco_private *priv = netdev_priv(dev);
struct net_device_stats *stats = &priv->stats; hermes_t *hw = &priv->hw;
struct hermes *hw = &priv->hw; int err = 0;
u16 channel;
printk(KERN_WARNING "%s: Tx timeout! " long freq = 0;
"ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", unsigned long flags;
dev->name, hermes_read_regn(hw, ALLOCFID),
hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT));
stats->tx_errors++;
schedule_work(&priv->reset_work); err = orinoco_lock(priv, &flags);
} if (err)
return err;
err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel);
if (err)
goto out;
static int /* Intersil firmware 1.3.5 returns 0 when the interface is down */
orinoco_change_mtu(struct net_device *dev, int new_mtu) if (channel == 0) {
{ err = -EBUSY;
struct orinoco_private *priv = netdev_priv(dev); goto out;
}
if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) ) if ( (channel < 1) || (channel > NUM_CHANNELS) ) {
return -EINVAL; printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
priv->ndev->name, channel);
err = -EBUSY;
goto out;
if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) > }
(priv->nicbuf_size - ETH_HLEN) ) freq = channel_frequency[channel-1] * 100000;
return -EINVAL;
dev->mtu = new_mtu; out:
orinoco_unlock(priv, &flags);
return 0; if (err > 0)
err = -EBUSY;
return err ? err : freq;
} }
/* FIXME: return int? */ static int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
static void int *numrates, s32 *rates, int max)
__orinoco_set_multicast_list(struct net_device *dev)
{ {
struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
struct hermes_idstring list;
unsigned char *p = (unsigned char *)&list.val;
int err = 0; int err = 0;
int promisc, mc_count; int num;
int i;
/* The Hermes doesn't seem to have an allmulti mode, so we go unsigned long flags;
* into promiscuous mode and let the upper levels deal. */
if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
(dev->mc_count > MAX_MULTICAST(priv)) ) {
promisc = 1;
mc_count = 0;
} else {
promisc = 0;
mc_count = dev->mc_count;
}
if (promisc != priv->promiscuous) { err = orinoco_lock(priv, &flags);
err = hermes_write_wordrec(hw, USER_BAP, if (err)
HERMES_RID_CNFPROMISCUOUSMODE, return err;
promisc);
if (err) {
printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
dev->name, err);
} else
priv->promiscuous = promisc;
}
if (! promisc && (mc_count || priv->mc_count) ) { err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
struct dev_mc_list *p = dev->mc_list; sizeof(list), NULL, &list);
hermes_multicast_t mclist; orinoco_unlock(priv, &flags);
int i;
for (i = 0; i < mc_count; i++) { if (err)
/* Paranoia: */ return err;
if (! p)
BUG(); /* Multicast list shorter than mc_count */ num = le16_to_cpu(list.len);
if (p->dmi_addrlen != ETH_ALEN) *numrates = num;
BUG(); /* Bad address size in multicast list */ num = min(num, max);
memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
p = p->next;
}
if (p)
printk(KERN_WARNING "Multicast list is longer than mc_count\n");
err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES, for (i = 0; i < num; i++) {
HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN), rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */
&mclist);
if (err)
printk(KERN_ERR "%s: Error %d setting multicast list.\n",
dev->name, err);
else
priv->mc_count = mc_count;
} }
/* Since we can set the promiscuous flag when it wasn't asked return 0;
for, make sure the net_device knows about it. */
if (priv->promiscuous)
dev->flags |= IFF_PROMISC;
else
dev->flags &= ~IFF_PROMISC;
} }
/********************************************************************/
/* Wireless extensions support */
/********************************************************************/
static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq) static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
...@@ -4102,51 +4084,68 @@ static int orinoco_debug_dump_recs(struct net_device *dev) ...@@ -4102,51 +4084,68 @@ static int orinoco_debug_dump_recs(struct net_device *dev)
return 0; return 0;
} }
struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct orinoco_private *)) /********************************************************************/
{ /* Debugging */
struct net_device *dev; /********************************************************************/
struct orinoco_private *priv;
dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card);
if (!dev)
return NULL;
priv = netdev_priv(dev);
priv->ndev = dev;
if (sizeof_card)
priv->card = (void *)((unsigned long)dev->priv + sizeof(struct orinoco_private));
else
priv->card = NULL;
/* Setup / override net_device fields */
dev->init = orinoco_init;
dev->hard_start_xmit = orinoco_xmit;
dev->tx_timeout = orinoco_tx_timeout;
dev->watchdog_timeo = HZ; /* 1 second timeout */
dev->get_stats = orinoco_get_stats;
dev->get_wireless_stats = orinoco_get_wireless_stats;
dev->do_ioctl = orinoco_ioctl;
dev->change_mtu = orinoco_change_mtu;
dev->set_multicast_list = orinoco_set_multicast_list;
/* we use the default eth_mac_addr for setting the MAC addr */
/* Set up default callbacks */
dev->open = orinoco_open;
dev->stop = orinoco_stop;
priv->hard_reset = hard_reset;
spin_lock_init(&priv->lock); #if 0
priv->open = 0; static void show_rx_frame(struct orinoco_rxframe_hdr *frame)
priv->hw_unavailable = 1; /* orinoco_init() must clear this {
* before anything else touches the printk(KERN_DEBUG "RX descriptor:\n");
* hardware */ printk(KERN_DEBUG " status = 0x%04x\n", frame->desc.status);
INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); printk(KERN_DEBUG " time = 0x%08x\n", frame->desc.time);
printk(KERN_DEBUG " silence = 0x%02x\n", frame->desc.silence);
printk(KERN_DEBUG " signal = 0x%02x\n", frame->desc.signal);
printk(KERN_DEBUG " rate = 0x%02x\n", frame->desc.rate);
printk(KERN_DEBUG " rxflow = 0x%02x\n", frame->desc.rxflow);
printk(KERN_DEBUG " reserved = 0x%08x\n", frame->desc.reserved);
priv->last_linkstatus = 0xffff; printk(KERN_DEBUG "IEEE 802.11 header:\n");
priv->connected = 0; printk(KERN_DEBUG " frame_ctl = 0x%04x\n",
frame->p80211.frame_ctl);
printk(KERN_DEBUG " duration_id = 0x%04x\n",
frame->p80211.duration_id);
printk(KERN_DEBUG " addr1 = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p80211.addr1[0], frame->p80211.addr1[1],
frame->p80211.addr1[2], frame->p80211.addr1[3],
frame->p80211.addr1[4], frame->p80211.addr1[5]);
printk(KERN_DEBUG " addr2 = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p80211.addr2[0], frame->p80211.addr2[1],
frame->p80211.addr2[2], frame->p80211.addr2[3],
frame->p80211.addr2[4], frame->p80211.addr2[5]);
printk(KERN_DEBUG " addr3 = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p80211.addr3[0], frame->p80211.addr3[1],
frame->p80211.addr3[2], frame->p80211.addr3[3],
frame->p80211.addr3[4], frame->p80211.addr3[5]);
printk(KERN_DEBUG " seq_ctl = 0x%04x\n",
frame->p80211.seq_ctl);
printk(KERN_DEBUG " addr4 = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p80211.addr4[0], frame->p80211.addr4[1],
frame->p80211.addr4[2], frame->p80211.addr4[3],
frame->p80211.addr4[4], frame->p80211.addr4[5]);
printk(KERN_DEBUG " data_len = 0x%04x\n",
frame->p80211.data_len);
return dev; printk(KERN_DEBUG "IEEE 802.3 header:\n");
printk(KERN_DEBUG " dest = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p8023.h_dest[0], frame->p8023.h_dest[1],
frame->p8023.h_dest[2], frame->p8023.h_dest[3],
frame->p8023.h_dest[4], frame->p8023.h_dest[5]);
printk(KERN_DEBUG " src = %02x:%02x:%02x:%02x:%02x:%02x\n",
frame->p8023.h_source[0], frame->p8023.h_source[1],
frame->p8023.h_source[2], frame->p8023.h_source[3],
frame->p8023.h_source[4], frame->p8023.h_source[5]);
printk(KERN_DEBUG " len = 0x%04x\n", frame->p8023.h_proto);
printk(KERN_DEBUG "IEEE 802.2 LLC/SNAP header:\n");
printk(KERN_DEBUG " DSAP = 0x%02x\n", frame->p8022.dsap);
printk(KERN_DEBUG " SSAP = 0x%02x\n", frame->p8022.ssap);
printk(KERN_DEBUG " ctrl = 0x%02x\n", frame->p8022.ctrl);
printk(KERN_DEBUG " OUI = %02x:%02x:%02x\n",
frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]);
printk(KERN_DEBUG " ethertype = 0x%04x\n", frame->ethertype);
} }
#endif /* 0 */
/********************************************************************/ /********************************************************************/
/* Module initialization */ /* Module initialization */
......
...@@ -27,11 +27,6 @@ struct orinoco_key { ...@@ -27,11 +27,6 @@ struct orinoco_key {
char data[ORINOCO_MAX_KEY_SIZE]; char data[ORINOCO_MAX_KEY_SIZE];
} __attribute__ ((packed)); } __attribute__ ((packed));
#define ORINOCO_INTEN ( HERMES_EV_RX | HERMES_EV_ALLOC | HERMES_EV_TX | \
HERMES_EV_TXEXC | HERMES_EV_WTERR | HERMES_EV_INFO | \
HERMES_EV_INFDROP )
struct orinoco_private { struct orinoco_private {
void *card; /* Pointer to card dependent structure */ void *card; /* Pointer to card dependent structure */
int (*hard_reset)(struct orinoco_private *); int (*hard_reset)(struct orinoco_private *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment