📄 adm8211.c
字号:
priv->mode = IEEE80211_IF_TYPE_MNTR; adm8211_update_mode(dev); ADM8211_CSR_WRITE(RDR, 0); adm8211_set_interval(dev, 100, 10); return 0;fail: return retval;}static void adm8211_stop(struct ieee80211_hw *dev){ struct adm8211_priv *priv = dev->priv; priv->mode = IEEE80211_IF_TYPE_INVALID; priv->nar = 0; ADM8211_CSR_WRITE(NAR, 0); ADM8211_CSR_WRITE(IER, 0); ADM8211_CSR_READ(NAR); free_irq(priv->pdev->irq, dev); adm8211_free_rings(dev);}static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len, int plcp_signal, int short_preamble){ /* Alternative calculation from NetBSD: *//* IEEE 802.11b durations for DSSS PHY in microseconds */#define IEEE80211_DUR_DS_LONG_PREAMBLE 144#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72#define IEEE80211_DUR_DS_FAST_PLCPHDR 24#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48#define IEEE80211_DUR_DS_SLOW_ACK 112#define IEEE80211_DUR_DS_FAST_ACK 56#define IEEE80211_DUR_DS_SLOW_CTS 112#define IEEE80211_DUR_DS_FAST_CTS 56#define IEEE80211_DUR_DS_SLOT 20#define IEEE80211_DUR_DS_SIFS 10 int remainder; *dur = (80 * (24 + payload_len) + plcp_signal - 1) / plcp_signal; if (plcp_signal <= PLCP_SIGNAL_2M) /* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */ *dur += 3 * (IEEE80211_DUR_DS_SIFS + IEEE80211_DUR_DS_SHORT_PREAMBLE + IEEE80211_DUR_DS_FAST_PLCPHDR) + IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK; else /* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */ *dur += 3 * (IEEE80211_DUR_DS_SIFS + IEEE80211_DUR_DS_SHORT_PREAMBLE + IEEE80211_DUR_DS_FAST_PLCPHDR) + IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK; /* lengthen duration if long preamble */ if (!short_preamble) *dur += 3 * (IEEE80211_DUR_DS_LONG_PREAMBLE - IEEE80211_DUR_DS_SHORT_PREAMBLE) + 3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR - IEEE80211_DUR_DS_FAST_PLCPHDR); *plcp = (80 * len) / plcp_signal; remainder = (80 * len) % plcp_signal; if (plcp_signal == PLCP_SIGNAL_11M && remainder <= 30 && remainder > 0) *plcp = (*plcp | 0x8000) + 1; else if (remainder) (*plcp)++;}/* Transmit skb w/adm8211_tx_hdr (802.11 header created by hardware) */static void adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, u16 plcp_signal, struct ieee80211_tx_control *control, size_t hdrlen){ struct adm8211_priv *priv = dev->priv; unsigned long flags; dma_addr_t mapping; unsigned int entry; u32 flag; mapping = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); spin_lock_irqsave(&priv->lock, flags); if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size / 2) flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS; else flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS; if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size - 2) ieee80211_stop_queue(dev, 0); entry = priv->cur_tx % priv->tx_ring_size; priv->tx_buffers[entry].skb = skb; priv->tx_buffers[entry].mapping = mapping; memcpy(&priv->tx_buffers[entry].tx_control, control, sizeof(*control)); priv->tx_buffers[entry].hdrlen = hdrlen; priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping); if (entry == priv->tx_ring_size - 1) flag |= TDES1_CONTROL_TER; priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len); /* Set TX rate (SIGNAL field in PLCP PPDU format) */ flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */; priv->tx_ring[entry].status = cpu_to_le32(flag); priv->cur_tx++; spin_unlock_irqrestore(&priv->lock, flags); /* Trigger transmit poll */ ADM8211_CSR_WRITE(TDR, 0);}/* Put adm8211_tx_hdr on skb and transmit */static int adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb, struct ieee80211_tx_control *control){ struct adm8211_tx_hdr *txhdr; u16 fc; size_t payload_len, hdrlen; int plcp, dur, len, plcp_signal, short_preamble; struct ieee80211_hdr *hdr; if (control->tx_rate < 0) { short_preamble = 1; plcp_signal = -control->tx_rate; } else { short_preamble = 0; plcp_signal = control->tx_rate; } hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control) & ~IEEE80211_FCTL_PROTECTED; hdrlen = ieee80211_get_hdrlen(fc); memcpy(skb->cb, skb->data, hdrlen); hdr = (struct ieee80211_hdr *)skb->cb; skb_pull(skb, hdrlen); payload_len = skb->len; txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr)); memset(txhdr, 0, sizeof(*txhdr)); memcpy(txhdr->da, ieee80211_get_DA(hdr), ETH_ALEN); txhdr->signal = plcp_signal; txhdr->frame_body_size = cpu_to_le16(payload_len); txhdr->frame_control = hdr->frame_control; len = hdrlen + payload_len + FCS_LEN; if (fc & IEEE80211_FCTL_PROTECTED) len += 8; txhdr->frag = cpu_to_le16(0x0FFF); adm8211_calc_durations(&dur, &plcp, payload_len, len, plcp_signal, short_preamble); txhdr->plcp_frag_head_len = cpu_to_le16(plcp); txhdr->plcp_frag_tail_len = cpu_to_le16(plcp); txhdr->dur_frag_head = cpu_to_le16(dur); txhdr->dur_frag_tail = cpu_to_le16(dur); txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER); if (short_preamble) txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE); if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS) txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS); if (fc & IEEE80211_FCTL_PROTECTED) txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE); txhdr->retry_limit = control->retry_limit; adm8211_tx_raw(dev, skb, plcp_signal, control, hdrlen); return NETDEV_TX_OK;}static int adm8211_alloc_rings(struct ieee80211_hw *dev){ struct adm8211_priv *priv = dev->priv; unsigned int ring_size; priv->rx_buffers = kmalloc(sizeof(*priv->rx_buffers) * priv->rx_ring_size + sizeof(*priv->tx_buffers) * priv->tx_ring_size, GFP_KERNEL); if (!priv->rx_buffers) return -ENOMEM; priv->tx_buffers = (void *)priv->rx_buffers + sizeof(*priv->rx_buffers) * priv->rx_ring_size; /* Allocate TX/RX descriptors */ ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size + sizeof(struct adm8211_desc) * priv->tx_ring_size; priv->rx_ring = pci_alloc_consistent(priv->pdev, ring_size, &priv->rx_ring_dma); if (!priv->rx_ring) { kfree(priv->rx_buffers); priv->rx_buffers = NULL; priv->tx_buffers = NULL; return -ENOMEM; } priv->tx_ring = (struct adm8211_desc *)(priv->rx_ring + priv->rx_ring_size); priv->tx_ring_dma = priv->rx_ring_dma + sizeof(struct adm8211_desc) * priv->rx_ring_size; return 0;}static const struct ieee80211_ops adm8211_ops = { .tx = adm8211_tx, .start = adm8211_start, .stop = adm8211_stop, .add_interface = adm8211_add_interface, .remove_interface = adm8211_remove_interface, .config = adm8211_config, .config_interface = adm8211_config_interface, .configure_filter = adm8211_configure_filter, .get_stats = adm8211_get_stats, .get_tx_stats = adm8211_get_tx_stats, .get_tsf = adm8211_get_tsft};static int __devinit adm8211_probe(struct pci_dev *pdev, const struct pci_device_id *id){ struct ieee80211_hw *dev; struct adm8211_priv *priv; unsigned long mem_addr, mem_len; unsigned int io_addr, io_len; int err; u32 reg; u8 perm_addr[ETH_ALEN]; DECLARE_MAC_BUF(mac); err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", pci_name(pdev)); return err; } io_addr = pci_resource_start(pdev, 0); io_len = pci_resource_len(pdev, 0); mem_addr = pci_resource_start(pdev, 1); mem_len = pci_resource_len(pdev, 1); if (io_len < 256 || mem_len < 1024) { printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", pci_name(pdev)); goto err_disable_pdev; } /* check signature */ pci_read_config_dword(pdev, 0x80 /* CR32 */, ®); if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) { printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", pci_name(pdev), reg); goto err_disable_pdev; } err = pci_request_regions(pdev, "adm8211"); if (err) { printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", pci_name(pdev)); return err; /* someone else grabbed it? don't disable it */ } if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) || pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", pci_name(pdev)); goto err_free_reg; } pci_set_master(pdev); dev = ieee80211_alloc_hw(sizeof(*priv), &adm8211_ops); if (!dev) { printk(KERN_ERR "%s (adm8211): ieee80211 alloc failed\n", pci_name(pdev)); err = -ENOMEM; goto err_free_reg; } priv = dev->priv; priv->pdev = pdev; spin_lock_init(&priv->lock); SET_IEEE80211_DEV(dev, &pdev->dev); pci_set_drvdata(pdev, dev); priv->map = pci_iomap(pdev, 1, mem_len); if (!priv->map) priv->map = pci_iomap(pdev, 0, io_len); if (!priv->map) { printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", pci_name(pdev)); goto err_free_dev; } priv->rx_ring_size = rx_ring_size; priv->tx_ring_size = tx_ring_size; if (adm8211_alloc_rings(dev)) { printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", pci_name(pdev)); goto err_iounmap; } *(u32 *)perm_addr = le32_to_cpu((__force __le32)ADM8211_CSR_READ(PAR0)); *(u16 *)&perm_addr[4] = le16_to_cpu((__force __le16)ADM8211_CSR_READ(PAR1) & 0xFFFF); if (!is_valid_ether_addr(perm_addr)) { printk(KERN_WARNING "%s (adm8211): Invalid hwaddr in EEPROM!\n", pci_name(pdev)); random_ether_addr(perm_addr); } SET_IEEE80211_PERM_ADDR(dev, perm_addr); dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr); dev->flags = IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED; /* IEEE80211_HW_RX_INCLUDES_FCS in promisc mode */ dev->channel_change_time = 1000; dev->max_rssi = 100; /* FIXME: find better value */ priv->modes[0].mode = MODE_IEEE80211B; /* channel info filled in by adm8211_read_eeprom */ memcpy(priv->rates, adm8211_rates, sizeof(adm8211_rates)); priv->modes[0].num_rates = ARRAY_SIZE(adm8211_rates); priv->modes[0].rates = priv->rates; dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */ priv->retry_limit = 3; priv->ant_power = 0x40; priv->tx_power = 0x40; priv->lpf_cutoff = 0xFF; priv->lnags_threshold = 0xFF; priv->mode = IEEE80211_IF_TYPE_INVALID; /* Power-on issue. EEPROM won't read correctly without */ if (pdev->revision >= ADM8211_REV_BA) { ADM8211_CSR_WRITE(FRCTL, 0); ADM8211_CSR_READ(FRCTL); ADM8211_CSR_WRITE(FRCTL, 1); ADM8211_CSR_READ(FRCTL); msleep(100); } err = adm8211_read_eeprom(dev); if (err) { printk(KERN_ERR "%s (adm8211): Can't alloc eeprom buffer\n", pci_name(pdev)); goto err_free_desc; } priv->channel = priv->modes[0].channels[0].chan; err = ieee80211_register_hwmode(dev, &priv->modes[0]); if (err) { printk(KERN_ERR "%s (adm8211): Can't register hwmode\n", pci_name(pdev)); goto err_free_desc; } err = ieee80211_register_hw(dev); if (err) { printk(KERN_ERR "%s (adm8211): Cannot register device\n", pci_name(pdev)); goto err_free_desc; } printk(KERN_INFO "%s: hwaddr %s, Rev 0x%02x\n", wiphy_name(dev->wiphy), print_mac(mac, dev->wiphy->perm_addr), pdev->revision); return 0; err_free_desc: pci_free_consistent(pdev, sizeof(struct adm8211_desc) * priv->rx_ring_size + sizeof(struct adm8211_desc) * priv->tx_ring_size, priv->rx_ring, priv->rx_ring_dma); kfree(priv->rx_buffers); err_iounmap: pci_iounmap(pdev, priv->map); err_free_dev: pci_set_drvdata(pdev, NULL); ieee80211_free_hw(dev); err_free_reg: pci_release_regions(pdev); err_disable_pdev: pci_disable_device(pdev); return err;}static void __devexit adm8211_remove(struct pci_dev *pdev){ struct ieee80211_hw *dev = pci_get_drvdata(pdev); struct adm8211_priv *priv; if (!dev) return; ieee80211_unregister_hw(dev); priv = dev->priv; pci_free_consistent(pdev, sizeof(struct adm8211_desc) * priv->rx_ring_size + sizeof(struct adm8211_desc) * priv->tx_ring_size, priv->rx_ring, priv->rx_ring_dma); kfree(priv->rx_buffers); kfree(priv->eeprom); pci_iounmap(pdev, priv->map); pci_release_regions(pdev); pci_disable_device(pdev); ieee80211_free_hw(dev);}#ifdef CONFIG_PMstatic int adm8211_suspend(struct pci_dev *pdev, pm_message_t state){ struct ieee80211_hw *dev = pci_get_drvdata(pdev); struct adm8211_priv *priv = dev->priv; if (priv->mode != IEEE80211_IF_TYPE_INVALID) { ieee80211_stop_queues(dev); adm8211_stop(dev); } pci_save_state(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); return 0;}static int adm8211_resume(struct pci_dev *pdev){ struct ieee80211_hw *dev = pci_get_drvdata(pdev); struct adm8211_priv *priv = dev->priv; pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); if (priv->mode != IEEE80211_IF_TYPE_INVALID) { adm8211_start(dev); ieee80211_start_queues(dev); } return 0;}#endif /* CONFIG_PM */MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table);/* TODO: implement enable_wake */static struct pci_driver adm8211_driver = { .name = "adm8211", .id_table = adm8211_pci_id_table, .probe = adm8211_probe, .remove = __devexit_p(adm8211_remove),#ifdef CONFIG_PM .suspend = adm8211_suspend, .resume = adm8211_resume,#endif /* CONFIG_PM */};static int __init adm8211_init(void){ return pci_register_driver(&adm8211_driver);}static void __exit adm8211_exit(void){ pci_unregister_driver(&adm8211_driver);}module_init(adm8211_init);module_exit(adm8211_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -