📄 e100.c
字号:
cb->prev = (i == 0) ? nic->cbs + count - 1 : cb - 1; cb->dma_addr = nic->cbs_dma_addr + i * sizeof(struct cb); cb->link = cpu_to_le32(nic->cbs_dma_addr + ((i+1) % count) * sizeof(struct cb)); cb->skb = NULL; } nic->cb_to_use = nic->cb_to_send = nic->cb_to_clean = nic->cbs; nic->cbs_avail = count; return 0;}static inline void e100_start_receiver(struct nic *nic, struct rx *rx){ if(!nic->rxs) return; if(RU_SUSPENDED != nic->ru_running) return; /* handle init time starts */ if(!rx) rx = nic->rxs; /* (Re)start RU if suspended or idle and RFA is non-NULL */ if(rx->skb) { e100_exec_cmd(nic, ruc_start, rx->dma_addr); nic->ru_running = RU_RUNNING; }}#define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN)static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx){ if(!(rx->skb = dev_alloc_skb(RFD_BUF_LEN + NET_IP_ALIGN))) return -ENOMEM; /* Align, init, and map the RFD. */ rx->skb->dev = nic->netdev; skb_reserve(rx->skb, NET_IP_ALIGN); memcpy(rx->skb->data, &nic->blank_rfd, sizeof(struct rfd)); rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); if(pci_dma_mapping_error(rx->dma_addr)) { dev_kfree_skb_any(rx->skb); rx->skb = NULL; rx->dma_addr = 0; return -ENOMEM; } /* Link the RFD to end of RFA by linking previous RFD to * this one, and clearing EL bit of previous. */ if(rx->prev->skb) { struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data; put_unaligned(cpu_to_le32(rx->dma_addr), (u32 *)&prev_rfd->link); wmb(); prev_rfd->command &= ~cpu_to_le16(cb_el); pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr, sizeof(struct rfd), PCI_DMA_TODEVICE); } return 0;}static int e100_rx_indicate(struct nic *nic, struct rx *rx, unsigned int *work_done, unsigned int work_to_do){ struct sk_buff *skb = rx->skb; struct rfd *rfd = (struct rfd *)skb->data; u16 rfd_status, actual_size; if(unlikely(work_done && *work_done >= work_to_do)) return -EAGAIN; /* Need to sync before taking a peek at cb_complete bit */ pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr, sizeof(struct rfd), PCI_DMA_FROMDEVICE); rfd_status = le16_to_cpu(rfd->status); DPRINTK(RX_STATUS, DEBUG, "status=0x%04X\n", rfd_status); /* If data isn't ready, nothing to indicate */ if(unlikely(!(rfd_status & cb_complete))) return -ENODATA; /* Get actual data size */ actual_size = le16_to_cpu(rfd->actual_size) & 0x3FFF; if(unlikely(actual_size > RFD_BUF_LEN - sizeof(struct rfd))) actual_size = RFD_BUF_LEN - sizeof(struct rfd); /* Get data */ pci_unmap_single(nic->pdev, rx->dma_addr, RFD_BUF_LEN, PCI_DMA_FROMDEVICE); /* this allows for a fast restart without re-enabling interrupts */ if(le16_to_cpu(rfd->command) & cb_el) nic->ru_running = RU_SUSPENDED; /* Pull off the RFD and put the actual data (minus eth hdr) */ skb_reserve(skb, sizeof(struct rfd)); skb_put(skb, actual_size); skb->protocol = eth_type_trans(skb, nic->netdev); if(unlikely(!(rfd_status & cb_ok))) { /* Don't indicate if hardware indicates errors */ dev_kfree_skb_any(skb); } else if(actual_size > ETH_DATA_LEN + VLAN_ETH_HLEN) { /* Don't indicate oversized frames */ nic->rx_over_length_errors++; dev_kfree_skb_any(skb); } else { nic->net_stats.rx_packets++; nic->net_stats.rx_bytes += actual_size; nic->netdev->last_rx = jiffies;#ifdef CONFIG_E100_NAPI netif_receive_skb(skb);#else netif_rx(skb);#endif if(work_done) (*work_done)++; } rx->skb = NULL; return 0;}static void e100_rx_clean(struct nic *nic, unsigned int *work_done, unsigned int work_to_do){ struct rx *rx; int restart_required = 0; struct rx *rx_to_start = NULL; /* are we already rnr? then pay attention!!! this ensures that * the state machine progression never allows a start with a * partially cleaned list, avoiding a race between hardware * and rx_to_clean when in NAPI mode */ if(RU_SUSPENDED == nic->ru_running) restart_required = 1; /* Indicate newly arrived packets */ for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) { int err = e100_rx_indicate(nic, rx, work_done, work_to_do); if(-EAGAIN == err) { /* hit quota so have more work to do, restart once * cleanup is complete */ restart_required = 0; break; } else if(-ENODATA == err) break; /* No more to clean */ } /* save our starting point as the place we'll restart the receiver */ if(restart_required) rx_to_start = nic->rx_to_clean; /* Alloc new skbs to refill list */ for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) { if(unlikely(e100_rx_alloc_skb(nic, rx))) break; /* Better luck next time (see watchdog) */ } if(restart_required) { // ack the rnr? writeb(stat_ack_rnr, &nic->csr->scb.stat_ack); e100_start_receiver(nic, rx_to_start); if(work_done) (*work_done)++; }}static void e100_rx_clean_list(struct nic *nic){ struct rx *rx; unsigned int i, count = nic->params.rfds.count; nic->ru_running = RU_UNINITIALIZED; if(nic->rxs) { for(rx = nic->rxs, i = 0; i < count; rx++, i++) { if(rx->skb) { pci_unmap_single(nic->pdev, rx->dma_addr, RFD_BUF_LEN, PCI_DMA_FROMDEVICE); dev_kfree_skb(rx->skb); } } kfree(nic->rxs); nic->rxs = NULL; } nic->rx_to_use = nic->rx_to_clean = NULL;}static int e100_rx_alloc_list(struct nic *nic){ struct rx *rx; unsigned int i, count = nic->params.rfds.count; nic->rx_to_use = nic->rx_to_clean = NULL; nic->ru_running = RU_UNINITIALIZED; if(!(nic->rxs = kmalloc(sizeof(struct rx) * count, GFP_ATOMIC))) return -ENOMEM; memset(nic->rxs, 0, sizeof(struct rx) * count); for(rx = nic->rxs, i = 0; i < count; rx++, i++) { rx->next = (i + 1 < count) ? rx + 1 : nic->rxs; rx->prev = (i == 0) ? nic->rxs + count - 1 : rx - 1; if(e100_rx_alloc_skb(nic, rx)) { e100_rx_clean_list(nic); return -ENOMEM; } } nic->rx_to_use = nic->rx_to_clean = nic->rxs; nic->ru_running = RU_SUSPENDED; return 0;}static irqreturn_t e100_intr(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *netdev = dev_id; struct nic *nic = netdev_priv(netdev); u8 stat_ack = readb(&nic->csr->scb.stat_ack); DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack); if(stat_ack == stat_ack_not_ours || /* Not our interrupt */ stat_ack == stat_ack_not_present) /* Hardware is ejected */ return IRQ_NONE; /* Ack interrupt(s) */ writeb(stat_ack, &nic->csr->scb.stat_ack); /* We hit Receive No Resource (RNR); restart RU after cleaning */ if(stat_ack & stat_ack_rnr) nic->ru_running = RU_SUSPENDED;#ifdef CONFIG_E100_NAPI if(likely(netif_rx_schedule_prep(netdev))) { e100_disable_irq(nic); __netif_rx_schedule(netdev); }#else e100_rx_clean(nic, NULL, 0); e100_tx_clean(nic);#endif return IRQ_HANDLED;}#ifdef CONFIG_E100_NAPIstatic int e100_poll(struct net_device *netdev, int *budget){ struct nic *nic = netdev_priv(netdev); unsigned int work_to_do = min(netdev->quota, *budget); unsigned int work_done = 0; int tx_cleaned; e100_rx_clean(nic, &work_done, work_to_do); tx_cleaned = e100_tx_clean(nic); /* If no Rx and Tx cleanup work was done, exit polling mode. */ if((!tx_cleaned && (work_done == 0)) || !netif_running(netdev)) { netif_rx_complete(netdev); e100_enable_irq(nic); return 0; } *budget -= work_done; netdev->quota -= work_done; return 1;}#endif#ifdef CONFIG_NET_POLL_CONTROLLERstatic void e100_netpoll(struct net_device *netdev){ struct nic *nic = netdev_priv(netdev); e100_disable_irq(nic); e100_intr(nic->pdev->irq, netdev, NULL); e100_tx_clean(nic); e100_enable_irq(nic);}#endifstatic struct net_device_stats *e100_get_stats(struct net_device *netdev){ struct nic *nic = netdev_priv(netdev); return &nic->net_stats;}static int e100_set_mac_address(struct net_device *netdev, void *p){ struct nic *nic = netdev_priv(netdev); struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); e100_exec_cb(nic, NULL, e100_setup_iaaddr); return 0;}static int e100_change_mtu(struct net_device *netdev, int new_mtu){ if(new_mtu < ETH_ZLEN || new_mtu > ETH_DATA_LEN) return -EINVAL; netdev->mtu = new_mtu; return 0;}#ifdef CONFIG_PMstatic int e100_asf(struct nic *nic){ /* ASF can be enabled from eeprom */ return((nic->pdev->device >= 0x1050) && (nic->pdev->device <= 0x1057) && (nic->eeprom[eeprom_config_asf] & eeprom_asf) && !(nic->eeprom[eeprom_config_asf] & eeprom_gcl) && ((nic->eeprom[eeprom_smbus_addr] & 0xFF) != 0xFE));}#endifstatic int e100_up(struct nic *nic){ int err; if((err = e100_rx_alloc_list(nic))) return err; if((err = e100_alloc_cbs(nic))) goto err_rx_clean_list; if((err = e100_hw_init(nic))) goto err_clean_cbs; e100_set_multicast_list(nic->netdev); e100_start_receiver(nic, NULL); mod_timer(&nic->watchdog, jiffies); if((err = request_irq(nic->pdev->irq, e100_intr, SA_SHIRQ, nic->netdev->name, nic->netdev))) goto err_no_irq; netif_wake_queue(nic->netdev);#ifdef CONFIG_E100_NAPI netif_poll_enable(nic->netdev); /* enable ints _after_ enabling poll, preventing a race between * disable ints+schedule */#endif e100_enable_irq(nic); return 0;err_no_irq: del_timer_sync(&nic->watchdog);err_clean_cbs: e100_clean_cbs(nic);err_rx_clean_list: e100_rx_clean_list(nic); return err;}static void e100_down(struct nic *nic){#ifdef CONFIG_E100_NAPI /* wait here for poll to complete */ netif_poll_disable(nic->netdev);#endif netif_stop_queue(nic->netdev); e100_hw_reset(nic); free_irq(nic->pdev->irq, nic->netdev); del_timer_sync(&nic->watchdog); netif_carrier_off(nic->netdev); e100_clean_cbs(nic); e100_rx_clean_list(nic);}static void e100_tx_timeout(struct net_device *netdev){ struct nic *nic = netdev_priv(netdev); /* Reset outside of interrupt context, to avoid request_irq * in interrupt context */ schedule_work(&nic->tx_timeout_task);}static void e100_tx_timeout_task(struct net_device *netdev){ struct nic *nic = netdev_priv(netdev); DPRINTK(TX_ERR, DEBUG, "scb.status=0x%02X\n", readb(&nic->csr->scb.status)); e100_down(netdev_priv(netdev)); e100_up(netdev_priv(netdev));}static int e100_loopback_test(struct nic *nic, enum loopback loopback_mode){ int err; struct sk_buff *skb; /* Use driver resources to perform internal MAC or PHY * loopback test. A single packet is prepared and transmitted * in loopback mode, and the test passes if the received * packet compares byte-for-byte to the transmitted packet. */ if((err = e100_rx_alloc_list(nic))) return err; if((err = e100_alloc_cbs(nic))) goto err_clean_rx; /* ICH PHY loopback is broken so do MAC loopback instead */ if(nic->flags & ich && loopback_mode == lb_phy) loopback_mode = lb_mac; nic->loopback = loopback_mode; if((err = e100_hw_init(nic))) goto err_loopback_none; if(loopback_mode == lb_phy) mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR, BMCR_LOOPBACK); e100_start_receiver(nic, NULL); if(!(skb = dev_alloc_skb(ETH_DATA_LEN))) { err = -ENOMEM; goto err_loopback_none; } skb_put(skb, ETH_DATA_LEN); memset(skb->data, 0xFF, ETH_DATA_LEN); e100_xmit_frame(skb, nic->netdev); msleep(10)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -