📄 hostap_hw.c
字号:
* register has changed values between consecutive reads for an unknown reason. * This should really not happen, so more debugging is needed. This test * version is a big slower, but it will detect most of such register changes * and will try to get the correct fid eventually. */#define EXTRA_FID_READ_TESTSstatic inline u16 prism2_read_fid_reg(struct net_device *dev, u16 reg){#ifdef EXTRA_FID_READ_TESTS u16 val, val2, val3; int i; for (i = 0; i < 10; i++) { val = HFA384X_INW(reg); val2 = HFA384X_INW(reg); val3 = HFA384X_INW(reg); if (val == val2 && val == val3) return val; printk(KERN_DEBUG "%s: detected fid change (try=%d, reg=%04x):" " %04x %04x %04x\n", dev->name, i, reg, val, val2, val3); if ((val == val2 || val == val3) && val != 0) return val; if (val2 == val3 && val2 != 0) return val2; } printk(KERN_WARNING "%s: Uhhuh.. could not read good fid from reg " "%04x (%04x %04x %04x)\n", dev->name, reg, val, val2, val3); return val;#else /* EXTRA_FID_READ_TESTS */ return HFA384X_INW(reg);#endif /* EXTRA_FID_READ_TESTS */}/* Called only as a tasklet (software IRQ) */static void prism2_rx(local_info_t *local){ struct net_device *dev = local->dev; int res, rx_pending = 0; u16 len, hdr_len, rxfid, status, macport; struct net_device_stats *stats; struct hfa384x_rx_frame rxdesc; struct sk_buff *skb = NULL; prism2_callback(local, PRISM2_CALLBACK_RX_START); stats = hostap_get_stats(dev); rxfid = prism2_read_fid_reg(dev, HFA384X_RXFID_OFF);#ifndef final_version if (rxfid == 0) { rxfid = HFA384X_INW(HFA384X_RXFID_OFF); printk(KERN_DEBUG "prism2_rx: rxfid=0 (next 0x%04x)\n", rxfid); if (rxfid == 0) { schedule_work(&local->reset_queue); goto rx_dropped; } /* try to continue with the new rxfid value */ }#endif spin_lock(&local->baplock); res = hfa384x_setup_bap(dev, BAP0, rxfid, 0); if (!res) res = hfa384x_from_bap(dev, BAP0, &rxdesc, sizeof(rxdesc)); if (res) { spin_unlock(&local->baplock); printk(KERN_DEBUG "%s: copy from BAP0 failed %d\n", dev->name, res); if (res == -ETIMEDOUT) { schedule_work(&local->reset_queue); } goto rx_dropped; } len = le16_to_cpu(rxdesc.data_len); hdr_len = sizeof(rxdesc); status = le16_to_cpu(rxdesc.status); macport = (status >> 8) & 0x07; /* Drop frames with too large reported payload length. Monitor mode * seems to sometimes pass frames (e.g., ctrl::ack) with signed and * negative value, so allow also values 65522 .. 65534 (-14 .. -2) for * macport 7 */ if (len > PRISM2_DATA_MAXLEN + 8 /* WEP */) { if (macport == 7 && local->iw_mode == IW_MODE_MONITOR) { if (len >= (u16) -14) { hdr_len -= 65535 - len; hdr_len--; } len = 0; } else { spin_unlock(&local->baplock); printk(KERN_DEBUG "%s: Received frame with invalid " "length 0x%04x\n", dev->name, len); hostap_dump_rx_header(dev->name, &rxdesc); goto rx_dropped; } } skb = dev_alloc_skb(len + hdr_len); if (!skb) { spin_unlock(&local->baplock); printk(KERN_DEBUG "%s: RX failed to allocate skb\n", dev->name); goto rx_dropped; } skb->dev = dev; memcpy(skb_put(skb, hdr_len), &rxdesc, hdr_len); if (len > 0) res = hfa384x_from_bap(dev, BAP0, skb_put(skb, len), len); spin_unlock(&local->baplock); if (res) { printk(KERN_DEBUG "%s: RX failed to read " "frame data\n", dev->name); goto rx_dropped; } skb_queue_tail(&local->rx_list, skb); tasklet_schedule(&local->rx_tasklet); rx_exit: prism2_callback(local, PRISM2_CALLBACK_RX_END); if (!rx_pending) { HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); } return; rx_dropped: stats->rx_dropped++; if (skb) dev_kfree_skb(skb); goto rx_exit;}/* Called only as a tasklet (software IRQ) */static void hostap_rx_skb(local_info_t *local, struct sk_buff *skb){ struct hfa384x_rx_frame *rxdesc; struct net_device *dev = skb->dev; struct hostap_80211_rx_status stats; int hdrlen, rx_hdrlen; rx_hdrlen = sizeof(*rxdesc); if (skb->len < sizeof(*rxdesc)) { /* Allow monitor mode to receive shorter frames */ if (local->iw_mode == IW_MODE_MONITOR && skb->len >= sizeof(*rxdesc) - 30) { rx_hdrlen = skb->len; } else { dev_kfree_skb(skb); return; } } rxdesc = (struct hfa384x_rx_frame *) skb->data; if (local->frame_dump & PRISM2_DUMP_RX_HDR && skb->len >= sizeof(*rxdesc)) hostap_dump_rx_header(dev->name, rxdesc); if (le16_to_cpu(rxdesc->status) & HFA384X_RX_STATUS_FCSERR && (!local->monitor_allow_fcserr || local->iw_mode != IW_MODE_MONITOR)) goto drop; if (skb->len > PRISM2_DATA_MAXLEN) { printk(KERN_DEBUG "%s: RX: len(%d) > MAX(%d)\n", dev->name, skb->len, PRISM2_DATA_MAXLEN); goto drop; } stats.mac_time = le32_to_cpu(rxdesc->time); stats.signal = rxdesc->signal - local->rssi_to_dBm; stats.noise = rxdesc->silence - local->rssi_to_dBm; stats.rate = rxdesc->rate; /* Convert Prism2 RX structure into IEEE 802.11 header */ hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(rxdesc->frame_control)); if (hdrlen > rx_hdrlen) hdrlen = rx_hdrlen; memmove(skb_pull(skb, rx_hdrlen - hdrlen), &rxdesc->frame_control, hdrlen); hostap_80211_rx(dev, skb, &stats); return; drop: dev_kfree_skb(skb);}/* Called only as a tasklet (software IRQ) */static void hostap_rx_tasklet(unsigned long data){ local_info_t *local = (local_info_t *) data; struct sk_buff *skb; while ((skb = skb_dequeue(&local->rx_list)) != NULL) hostap_rx_skb(local, skb);}/* Called only from hardware IRQ */static void prism2_alloc_ev(struct net_device *dev){ struct hostap_interface *iface; local_info_t *local; int idx; u16 fid; iface = netdev_priv(dev); local = iface->local; fid = prism2_read_fid_reg(dev, HFA384X_ALLOCFID_OFF); PDEBUG(DEBUG_FID, "FID: interrupt: ALLOC - fid=0x%04x\n", fid); spin_lock(&local->txfidlock); idx = local->next_alloc; do { if (local->txfid[idx] == fid) { PDEBUG(DEBUG_FID, "FID: found matching txfid[%d]\n", idx);#ifndef final_version if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) printk("Already released txfid found at idx " "%d\n", idx); if (local->intransmitfid[idx] == PRISM2_TXFID_RESERVED) printk("Already reserved txfid found at idx " "%d\n", idx);#endif local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; idx++; local->next_alloc = idx >= PRISM2_TXFID_COUNT ? 0 : idx; if (!test_bit(HOSTAP_BITS_TRANSMIT, &local->bits) && netif_queue_stopped(dev)) netif_wake_queue(dev); spin_unlock(&local->txfidlock); return; } idx++; if (idx >= PRISM2_TXFID_COUNT) idx = 0; } while (idx != local->next_alloc); printk(KERN_WARNING "%s: could not find matching txfid (0x%04x, new " "read 0x%04x) for alloc event\n", dev->name, fid, HFA384X_INW(HFA384X_ALLOCFID_OFF)); printk(KERN_DEBUG "TXFIDs:"); for (idx = 0; idx < PRISM2_TXFID_COUNT; idx++) printk(" %04x[%04x]", local->txfid[idx], local->intransmitfid[idx]); printk("\n"); spin_unlock(&local->txfidlock); /* FIX: should probably schedule reset; reference to one txfid was lost * completely.. Bad things will happen if we run out of txfids * Actually, this will cause netdev watchdog to notice TX timeout and * then card reset after all txfids have been leaked. */}/* Called only as a tasklet (software IRQ) */static void hostap_tx_callback(local_info_t *local, struct hfa384x_tx_frame *txdesc, int ok, char *payload){ u16 sw_support, hdrlen, len; struct sk_buff *skb; struct hostap_tx_callback_info *cb; /* Make sure that frame was from us. */ if (memcmp(txdesc->addr2, local->dev->dev_addr, ETH_ALEN)) { printk(KERN_DEBUG "%s: TX callback - foreign frame\n", local->dev->name); return; } sw_support = le16_to_cpu(txdesc->sw_support); spin_lock(&local->lock); cb = local->tx_callback; while (cb != NULL && cb->idx != sw_support) cb = cb->next; spin_unlock(&local->lock); if (cb == NULL) { printk(KERN_DEBUG "%s: could not find TX callback (idx %d)\n", local->dev->name, sw_support); return; } hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(txdesc->frame_control)); len = le16_to_cpu(txdesc->data_len); skb = dev_alloc_skb(hdrlen + len); if (skb == NULL) { printk(KERN_DEBUG "%s: hostap_tx_callback failed to allocate " "skb\n", local->dev->name); return; } memcpy(skb_put(skb, hdrlen), (void *) &txdesc->frame_control, hdrlen); if (payload) memcpy(skb_put(skb, len), payload, len); skb->dev = local->dev; skb->mac.raw = skb->data; cb->func(skb, ok, cb->data);}/* Called only as a tasklet (software IRQ) */static int hostap_tx_compl_read(local_info_t *local, int error, struct hfa384x_tx_frame *txdesc, char **payload){ u16 fid, len; int res, ret = 0; struct net_device *dev = local->dev; fid = prism2_read_fid_reg(dev, HFA384X_TXCOMPLFID_OFF); PDEBUG(DEBUG_FID, "interrupt: TX (err=%d) - fid=0x%04x\n", fid, error); spin_lock(&local->baplock); res = hfa384x_setup_bap(dev, BAP0, fid, 0); if (!res) res = hfa384x_from_bap(dev, BAP0, txdesc, sizeof(*txdesc)); if (res) { PDEBUG(DEBUG_EXTRA, "%s: TX (err=%d) - fid=0x%04x - could not " "read txdesc\n", dev->name, error, fid); if (res == -ETIMEDOUT) { schedule_work(&local->reset_queue); } ret = -1; goto fail; } if (txdesc->sw_support) { len = le16_to_cpu(txdesc->data_len); if (len < PRISM2_DATA_MAXLEN) { *payload = (char *) kmalloc(len, GFP_ATOMIC); if (*payload == NULL || hfa384x_from_bap(dev, BAP0, *payload, len)) { PDEBUG(DEBUG_EXTRA, "%s: could not read TX " "frame payload\n", dev->name); kfree(*payload); *payload = NULL; ret = -1; goto fail; } } } fail: spin_unlock(&local->baplock); return ret;}/* Called only as a tasklet (software IRQ) */static void prism2_tx_ev(local_info_t *local){ struct net_device *dev = local->dev; char *payload = NULL; struct hfa384x_tx_frame txdesc; if (hostap_tx_compl_read(local, 0, &txdesc, &payload)) goto fail; if (local->frame_dump & PRISM2_DUMP_TX_HDR) { PDEBUG(DEBUG_EXTRA, "%s: TX - status=0x%04x " "retry_count=%d tx_rate=%d seq_ctrl=%d " "duration_id=%d\n", dev->name, le16_to_cpu(txdesc.status), txdesc.retry_count, txdesc.tx_rate, le16_to_cpu(txdesc.seq_ctrl), le16_to_cpu(txdesc.duration_id)); } if (txdesc.sw_support) hostap_tx_callback(local, &txdesc, 1, payload); kfree(payload); fail: HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF);}/* Called only as a tasklet (software IRQ) */static void hostap_sta_tx_exc_tasklet(unsigned long data){ local_info_t *local = (local_info_t *) data; struct sk_buff *skb; while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) { struct hfa384x_tx_frame *txdesc = (struct hfa384x_tx_frame *) skb->data; if (skb->len >= sizeof(*txdesc)) { /* Convert Prism2 RX structure into IEEE 802.11 header */ u16 fc = le16_to_cpu(txdesc->frame_control); int hdrlen = hostap_80211_get_hdrlen(fc); memmove(skb_pull(skb, sizeof(*txdesc) - hdrlen), &txdesc->frame_control, hdrlen); hostap_handle_sta_tx_exc(local, skb); } dev_kfree_skb(skb); }}/* Called only as a tasklet (software IRQ) */static void prism2_txexc(local_info_t *local){ struct net_device *dev = local->dev; u16 status, fc; int show_dump, res; char *payload = NULL; struct hfa384x_tx_frame txdesc; show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR; local->stats.tx_errors++; res = hostap_tx_compl_read(local, 1, &txdesc, &payload); HFA384X_OUTW(HFA384X_EV_TXEXC, HFA384X_EVACK_OFF); if (res) return; status = le16_to_cpu(txdesc.status); /* We produce a TXDROP event only for retry or lifetime * exceeded, because that's the only status that really mean * that this particular node went away. * Other errors means that *we* screwed up. - Jean II */ if (status & (HFA384X_TX_STATUS_RETRYERR | HFA384X_TX_STATUS_AGEDERR)) { union iwreq_data wrqu; /* Copy 802.11 dest address. */ memcpy(wrqu.addr.sa_data, txdesc.addr1, ETH_ALEN); wrqu.addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); } else show_dump = 1; if (local->iw_mode == IW_MODE_MASTER || local->iw_mode == IW_MODE_REPEAT || local->wds_type & HOSTAP_WDS_AP_CLIENT) { struct sk_buff *skb; skb = dev_alloc_skb(sizeof(txdesc)); if (skb) { memcpy(skb_put(skb, sizeof(txdesc)), &txdesc, sizeof(txdesc));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -