📄 hostap_hw.c
字号:
txdesc.tx_control = cpu_to_le16(tx_control); txdesc.tx_rate = meta->rate; data_len = skb->len - hdr_len; txdesc.data_len = cpu_to_le16(data_len); txdesc.len = cpu_to_be16(data_len); idx = prism2_get_txfid_idx(local); if (idx < 0) goto fail; if (local->frame_dump & PRISM2_DUMP_TX_HDR) hostap_dump_tx_header(dev->name, &txdesc); spin_lock(&local->baplock); res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0);#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) if (!res && skb->len >= local->bus_master_threshold_tx) { u8 *pos; int buf_len; local->bus_m0_tx_idx = idx; /* FIX: BAP0 should be locked during bus master transfer, but * baplock with BH's disabled is not OK for this; netif queue * stopping is not enough since BAP0 is used also for RID * read/write */ /* stop the queue for the time that bus mastering on BAP0 is * in use */ netif_stop_queue(dev); spin_unlock(&local->baplock); /* Copy frame data to bus_m0_buf */ pos = local->bus_m0_buf; memcpy(pos, &txdesc, sizeof(txdesc)); pos += sizeof(txdesc); memcpy(pos, skb->data + hdr_len, skb->len - hdr_len); pos += skb->len - hdr_len; buf_len = pos - local->bus_m0_buf; if (buf_len & 1) buf_len++;#ifdef PRISM2_ENABLE_BEFORE_TX_BUS_MASTER /* Any RX packet seems to break something with TX bus * mastering; enable command is enough to fix this.. */ if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_ENABLE, 0, prism2_tx_cb, (long) buf_len)) { printk(KERN_DEBUG "%s: TX: enable port0 failed\n", dev->name); }#else /* PRISM2_ENABLE_BEFORE_TX_BUS_MASTER */ prism2_tx_cb(dev, (void *) buf_len, 0, 0);#endif /* PRISM2_ENABLE_BEFORE_TX_BUS_MASTER */ /* Bus master transfer will be started from command completion * event handler and TX handling will be finished by calling * prism2_transmit() from bus master event handler */ goto tx_stats; }#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ if (!res) res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc)); if (!res) res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len, skb->len - hdr_len); spin_unlock(&local->baplock); if (!res) res = prism2_transmit(dev, idx); if (res) { printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n", dev->name); local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; PRISM2_SCHEDULE_TASK(&local->reset_queue); goto fail; } ret = 0;fail: prism2_callback(local, PRISM2_CALLBACK_TX_END); return ret;}/* Some SMP systems have reported number of odd errors with hostap_pci. fid * 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) { PRISM2_SCHEDULE_TASK(&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) { PRISM2_SCHEDULE_TASK(&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 defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) if (len >= local->bus_master_threshold_rx) { unsigned long addr; hfa384x_events_no_bap1(dev); local->rx_skb = skb; /* Internal BAP0 offset points to the byte following rxdesc; * copy rest of the data using bus master */ addr = virt_to_phys(skb_put(skb, len)); HFA384X_OUTW((addr & 0xffff0000) >> 16, HFA384X_PCI_M0_ADDRH_OFF); HFA384X_OUTW(addr & 0x0000ffff, HFA384X_PCI_M0_ADDRL_OFF); if (len & 1) len++; HFA384X_OUTW(len / 2, HFA384X_PCI_M0_LEN_OFF); HFA384X_OUTW(HFA384X_PCI_CTL_FROM_BAP, HFA384X_PCI_M0_CTL_OFF); /* pci_bus_m1 event will be generated when data transfer is * complete and the frame will then be added to rx_list and * rx_tasklet is scheduled */ rx_pending = 1; /* Have to release baplock before returning, although BAP0 * should really not be used before DMA transfer has been * completed. */ spin_unlock(&local->baplock); } else#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ { 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 = dev->priv; local_info_t *local = iface->local; int idx; u16 fid; 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) -
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -