📄 orinoco.c
字号:
return; drop: dev_kfree_skb_irq(skb); update_stats: stats->rx_errors++; stats->rx_dropped++;}/********************************************************************//* Rx path (info frames) *//********************************************************************/static void print_linkstatus(struct net_device *dev, u16 status){ char * s; if (suppress_linkstatus) return; 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; default: s = "UNKNOWN"; } printk(KERN_INFO "%s: New link status: %s (%04x)\n", dev->name, s, status);}/* Search scan results for requested BSSID, join it if found */static void orinoco_join_ap(struct work_struct *work){ struct orinoco_private *priv = container_of(work, struct orinoco_private, join_work); struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; unsigned long flags; struct join_req { u8 bssid[ETH_ALEN]; __le16 channel; } __attribute__ ((packed)) req; const int atom_len = offsetof(struct prism2_scan_apinfo, atim); struct prism2_scan_apinfo *atom = NULL; int offset = 4; int found = 0; u8 *buf; u16 len; /* Allocate buffer for scan results */ buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL); if (! buf) return; if (orinoco_lock(priv, &flags) != 0) goto fail_lock; /* Sanity checks in case user changed something in the meantime */ if (! priv->bssid_fixed) goto out; if (strlen(priv->desired_essid) == 0) goto out; /* Read scan results from the firmware */ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SCANRESULTSTABLE, MAX_SCAN_LEN, &len, buf); if (err) { printk(KERN_ERR "%s: Cannot read scan results\n", dev->name); goto out; } len = HERMES_RECLEN_TO_BYTES(len); /* Go through the scan results looking for the channel of the AP * we were requested to join */ for (; offset + atom_len <= len; offset += atom_len) { atom = (struct prism2_scan_apinfo *) (buf + offset); if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0) { found = 1; break; } } if (! found) { DEBUG(1, "%s: Requested AP not found in scan results\n", dev->name); goto out; } memcpy(req.bssid, priv->desired_bssid, ETH_ALEN); req.channel = atom->channel; /* both are little-endian */ err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST, &req); if (err) printk(KERN_ERR "%s: Error issuing join request\n", dev->name); out: orinoco_unlock(priv, &flags); fail_lock: kfree(buf);}/* Send new BSSID to userspace */static void orinoco_send_wevents(struct work_struct *work){ struct orinoco_private *priv = container_of(work, struct orinoco_private, wevent_work); struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; union iwreq_data wrqu; int err; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return; err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENTBSSID, ETH_ALEN, NULL, wrqu.ap_addr.sa_data); if (err != 0) goto out; wrqu.ap_addr.sa_family = ARPHRD_ETHER; /* Send event to user space */ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); out: orinoco_unlock(priv, &flags);}static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw){ struct orinoco_private *priv = netdev_priv(dev); u16 infofid; struct { __le16 len; __le16 type; } __attribute__ ((packed)) info; int len, type; int err; /* This is an answer to an INQUIRE command that we did earlier, * or an information "event" generated by the card * 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; } len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len)); type = le16_to_cpu(info.type); switch (type) { case HERMES_INQ_TALLIES: { struct hermes_tallies_frame tallies; 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); } err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len, infofid, sizeof(info)); if (err) break; /* 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; int connected; if (priv->iw_mode == IW_MODE_MONITOR) break; if (len != sizeof(linkstatus)) { printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", dev->name, len); break; } err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len, infofid, sizeof(info)); if (err) break; newstatus = le16_to_cpu(linkstatus.linkstatus); /* Symbol firmware uses "out of range" to signal that * the hostscan frame can be requested. */ if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE && priv->firmware_type == FIRMWARE_TYPE_SYMBOL && priv->has_hostscan && priv->scan_inprogress) { hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL); break; } connected = (newstatus == HERMES_LINKSTATUS_CONNECTED) || (newstatus == HERMES_LINKSTATUS_AP_CHANGE) || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE); if (connected) netif_carrier_on(dev); else if (!ignore_disconnect) netif_carrier_off(dev); if (newstatus != priv->last_linkstatus) { priv->last_linkstatus = newstatus; print_linkstatus(dev, newstatus); /* The info frame contains only one word which is the * status (see hermes.h). The status is pretty boring * in itself, that's why we export the new BSSID... * Jean II */ schedule_work(&priv->wevent_work); } } break; case HERMES_INQ_SCAN: if (!priv->scan_inprogress && priv->bssid_fixed && priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { schedule_work(&priv->join_work); break; } /* fall through */ case HERMES_INQ_HOSTSCAN: case HERMES_INQ_HOSTSCAN_SYMBOL: { /* Result of a scanning. Contains information about * cells in the vicinity - Jean II */ union iwreq_data wrqu; unsigned char *buf; /* Sanity check */ if (len > 4096) { printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", dev->name, len); break; } /* We are a strict producer. If the previous scan results * have not been consumed, we just have to drop this * frame. We can't remove the previous results ourselves, * that would be *very* racy... Jean II */ if (priv->scan_result != NULL) { printk(KERN_WARNING "%s: Previous scan results not consumed, dropping info frame.\n", dev->name); break; } /* Allocate buffer for results */ buf = kmalloc(len, GFP_ATOMIC); if (buf == NULL) /* No memory, so can't printk()... */ break; /* Read scan data */ err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len, infofid, sizeof(info)); if (err) { kfree(buf); break; }#ifdef ORINOCO_DEBUG { int i; printk(KERN_DEBUG "Scan result [%02X", buf[0]); for(i = 1; i < (len * 2); i++) printk(":%02X", buf[i]); printk("]\n"); }#endif /* ORINOCO_DEBUG */ /* Allow the clients to access the results */ priv->scan_len = len; priv->scan_result = buf; /* Send an empty event to user space. * We don't send the received data on the event because * it would require us to do complex transcoding, and * we want to minimise the work done in the irq handler * Use a request to extract the data - Jean II */ wrqu.data.length = 0; wrqu.data.flags = 0; wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); } break; case HERMES_INQ_SEC_STAT_AGERE: /* Security status (Agere specific) */ /* Ignore this frame for now */ if (priv->firmware_type == FIRMWARE_TYPE_AGERE) break; /* fall through */ default: printk(KERN_DEBUG "%s: Unknown information frame received: " "type 0x%04x, length %d\n", dev->name, type, len); /* We don't actually do anything about it */ break; }}static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw){ if (net_ratelimit()) printk(KERN_DEBUG "%s: Information frame lost.\n", dev->name);}/********************************************************************//* Internal hardware control routines *//********************************************************************/int __orinoco_up(struct net_device *dev){ struct orinoco_private *priv = netdev_priv(dev); struct hermes *hw = &priv->hw; int err; netif_carrier_off(dev); /* just to make sure */ 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); struct hermes *hw = &priv->hw; int err; netif_stop_queue(dev); if (! priv->hw_unavailable) { if (! priv->broken_disableport) { err = hermes_disable_port(hw, 0); if (err) { /* Some firmwares (e.g. Intersil 1.3.x) seem * to have problems disabling the port, oh * 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); hermes_write_regn(hw, EVACK, 0xffff); } /* firmware will have to reassociate */ netif_carrier_off(dev); priv->last_linkstatus = 0xffff; return 0;}static int orinoco_allocate_fid(struct net_device *dev){ struct orinoco_private *priv = netdev_priv(dev); struct hermes *hw = &priv->hw; int err; err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) { /* 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;}int orinoco_reinit_firmware(struct net_device *dev){ struct orinoco_private *priv = netdev_priv(dev); struct hermes *hw = &priv->hw; int err; err = hermes_init(hw); if (!err) err = orinoco_allocate_fid(dev); return err;}static int __orinoco_hw_set_bitrate(struct orinoco_private *priv){ hermes_t *hw = &priv->hw; int err = 0; if (priv->bitratemode >= BITRATE_TABLE_SIZE) { printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", priv->ndev->name, priv->bitratemode); return -EINVAL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -