📄 ethernet.c
字号:
myFirstTxDesc = phys_to_virt(myFirstTxDesc->descr.next); } if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { /* acknowledge the eop interrupt and wake up queue */ *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do); *R_IRQ_MASK2_CLR = IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr); netif_wake_queue(dev); }}static voide100nw_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct net_device *dev = (struct net_device *)dev_id; struct net_local *np = (struct net_local *)dev->priv; unsigned long irqbits = *R_IRQ_MASK0_RD; /* check for underrun irq */ if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { *R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr); np->stats.tx_errors++; D(printk("ethernet receiver underrun!\n")); } /* check for overrun irq */ if (irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) { update_rx_stats(&np->stats); /* this will ack the irq */ D(printk("ethernet receiver overrun!\n")); } /* check for excessive collision irq */ if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { *R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr); np->stats.tx_errors++; D(printk("ethernet excessive collisions!\n")); }}/* We have a good packet(s), get it/them out of the buffers. */static voide100_rx(struct net_device *dev){ struct sk_buff *skb; int length = 0; struct net_local *np = (struct net_local *)dev->priv; unsigned char *skb_data_ptr;#ifdef ETHDEBUG int i;#endif if (!led_active && time_after(jiffies, led_next_time)) { /* light the network leds depending on the current speed. */ e100_set_network_leds(NETWORK_ACTIVITY); /* Set the earliest time we may clear the LED */ led_next_time = jiffies + NET_FLASH_TIME; led_active = 1; mod_timer(&clear_led_timer, jiffies + HZ/10); } length = myNextRxDesc->descr.hw_len - 4; ((struct net_local *)dev->priv)->stats.rx_bytes += length;#ifdef ETHDEBUG printk("Got a packet of length %d:\n", length); /* dump the first bytes in the packet */ skb_data_ptr = (unsigned char *)phys_to_virt(myNextRxDesc->descr.buf); for (i = 0; i < 8; i++) { printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8, skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3], skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]); skb_data_ptr += 8; }#endif if (length < RX_COPYBREAK) { /* Small packet, copy data */ skb = dev_alloc_skb(length - ETHER_HEAD_LEN); if (!skb) { np->stats.rx_errors++; printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); return; } skb_put(skb, length - ETHER_HEAD_LEN); /* allocate room for the packet body */ skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */#ifdef ETHDEBUG printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n", skb->head, skb->data, skb->tail, skb->end); printk("copying packet to 0x%x.\n", skb_data_ptr);#endif memcpy(skb_data_ptr, phys_to_virt(myNextRxDesc->descr.buf), length); } else { /* Large packet, send directly to upper layers and allocate new * memory (aligned to cache line boundary to avoid bug). * Before sending the skb to upper layers we must make sure that * skb->data points to the aligned start of the packet. */ int align; struct sk_buff *new_skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE + 2 * L1_CACHE_BYTES); if (!new_skb) { np->stats.rx_errors++; printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); return; } skb = myNextRxDesc->skb; align = (int)phys_to_virt(myNextRxDesc->descr.buf) - (int)skb->data; skb_put(skb, length + align); skb_pull(skb, align); /* Remove alignment bytes */ myNextRxDesc->skb = new_skb; myNextRxDesc->descr.buf = L1_CACHE_ALIGN(virt_to_phys(myNextRxDesc->skb->data)); } skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); /* Send the packet to the upper layers */ netif_rx(skb); /* Prepare for next packet */ myNextRxDesc->descr.status = 0; myPrevRxDesc = myNextRxDesc; myNextRxDesc = phys_to_virt(myNextRxDesc->descr.next); rx_queue_len++; /* Check if descriptors should be returned */ if (rx_queue_len == RX_QUEUE_THRESHOLD) { flush_etrax_cache(); myPrevRxDesc->descr.ctrl |= d_eol; myLastRxDesc->descr.ctrl &= ~d_eol; myLastRxDesc = myPrevRxDesc; rx_queue_len = 0; }}/* The inverse routine to net_open(). */static inte100_close(struct net_device *dev){ struct net_local *np = (struct net_local *)dev->priv; printk("Closing %s.\n", dev->name); netif_stop_queue(dev); *R_NETWORK_GEN_CONFIG = IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) | IO_STATE(R_NETWORK_GEN_CONFIG, enable, off); *R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) | IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) | IO_STATE(R_IRQ_MASK0_CLR, excessive_col, clr); *R_IRQ_MASK2_CLR = IO_STATE(R_IRQ_MASK2_CLR, dma0_descr, clr) | IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) | IO_STATE(R_IRQ_MASK2_CLR, dma1_descr, clr) | IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr); /* Stop the receiver and the transmitter */ RESET_DMA(NETWORK_TX_DMA_NBR); RESET_DMA(NETWORK_RX_DMA_NBR); /* Flush the Tx and disable Rx here. */ free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev); free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev); free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev); free_dma(NETWORK_TX_DMA_NBR); free_dma(NETWORK_RX_DMA_NBR); /* Update the statistics here. */ update_rx_stats(&np->stats); update_tx_stats(&np->stats); return 0;}static inte100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data; switch (cmd) { case SIOCETHTOOL: return e100_ethtool_ioctl(dev,ifr); case SIOCGMIIPHY: /* Get PHY address */ data->phy_id = MDIO_PHYS_ADDR; break; case SIOCGMIIREG: /* Read MII register */ data->val_out = e100_get_mdio_reg(data->reg_num); break; case SIOCSMIIREG: /* Write MII register */ e100_set_mdio_reg(data->reg_num, data->val_in); break; /* The ioctls below should be considered obsolete but are */ /* still present for compatability with old scripts/apps */ case SET_ETH_SPEED_10: /* 10 Mbps */ e100_set_speed(10); break; case SET_ETH_SPEED_100: /* 100 Mbps */ e100_set_speed(100); break; case SET_ETH_SPEED_AUTO: /* Auto negotiate speed */ e100_set_speed(0); break; case SET_ETH_DUPLEX_HALF: /* Hhalf duplex. */ e100_set_duplex(half); break; case SET_ETH_DUPLEX_FULL: /* Full duplex. */ e100_set_duplex(full); break; case SET_ETH_DUPLEX_AUTO: /* Autonegotiate duplex*/ e100_set_duplex(autoneg); break; default: return -EINVAL; } return 0;}static inte100_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr){ struct ethtool_cmd ecmd; if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) return -EFAULT; switch (ecmd.cmd) { case ETHTOOL_GSET: { memset((void *) &ecmd, 0, sizeof (ecmd)); ecmd.supported = SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; ecmd.port = PORT_TP; ecmd.transceiver = XCVR_EXTERNAL; ecmd.phy_address = MDIO_PHYS_ADDR; ecmd.speed = current_speed; ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; ecmd.advertising = ADVERTISED_TP; if (current_duplex == autoneg && current_speed_selection == 0) ecmd.advertising |= ADVERTISED_Autoneg; else { ecmd.advertising |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; if (current_speed_selection == 10) ecmd.advertising &= ~(ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full); else if (current_speed_selection == 100) ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full); if (current_duplex == half) ecmd.advertising &= ~(ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Full); else if (current_duplex == full) ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half); } ecmd.autoneg = AUTONEG_ENABLE; if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd))) return -EFAULT; } break; case ETHTOOL_SSET: { if (!capable(CAP_NET_ADMIN)) { return -EPERM; } if (ecmd.autoneg == AUTONEG_ENABLE) { e100_set_duplex(autoneg); e100_set_speed(0); } else { e100_set_duplex(ecmd.duplex == DUPLEX_HALF ? half : full); e100_set_speed(ecmd.speed == SPEED_10 ? 10: 100); } } break; case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info; memset((void *) &info, 0, sizeof (info)); strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1); strncpy(info.version, "$Rev$", sizeof(info.version) - 1); strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1); strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1); info.regdump_len = 0; info.eedump_len = 0; info.testinfo_len = 0; if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) return -EFAULT; } break; case ETHTOOL_NWAY_RST: if (current_duplex == autoneg && current_speed_selection == 0) e100_negotiate(); break; default: return -EOPNOTSUPP; break; } return 0;}static voidupdate_rx_stats(struct net_device_stats *es){ unsigned long r = *R_REC_COUNTERS; /* update stats relevant to reception errors */ es->rx_fifo_errors += IO_EXTRACT(R_REC_COUNTERS, congestion, r); es->rx_crc_errors += IO_EXTRACT(R_REC_COUNTERS, crc_error, r); es->rx_frame_errors += IO_EXTRACT(R_REC_COUNTERS, alignment_error, r); es->rx_length_errors += IO_EXTRACT(R_REC_COUNTERS, oversize, r);}static voidupdate_tx_stats(struct net_device_stats *es){ unsigned long r = *R_TR_COUNTERS; /* update stats relevant to transmission errors */ es->collisions += IO_EXTRACT(R_TR_COUNTERS, single_col, r) + IO_EXTRACT(R_TR_COUNTERS, multiple_col, r); es->tx_errors += IO_EXTRACT(R_TR_COUNTERS, deferred, r);}/* * Get the current statistics. * This may be called with the card open or closed. */static struct net_device_stats *e100_get_stats(struct net_device *dev){ struct net_local *lp = (struct net_local *)dev->priv; update_rx_stats(&lp->stats); update_tx_stats(&lp->stats); return &lp->stats;}/* * Set or clear the multicast filter for this adaptor. * num_addrs == -1 Promiscuous mode, receive all packets * num_addrs == 0 Normal mode, clear multicast list * num_addrs > 0 Multicast mode, receive normal and MC packets, * and do best-effort filtering. */static voidset_multicast_list(struct net_device *dev){ int num_addr = dev->mc_count; unsigned long int lo_bits; unsigned long int hi_bits; if (dev->flags & IFF_PROMISC) { /* promiscuous mode */ lo_bits = 0xfffffffful; hi_bits = 0xfffffffful; /* Enable individual receive */ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, receive); *R_NETWORK_REC_CONFIG = network_rec_config_shadow; } else if (dev->flags & IFF_ALLMULTI) { /* enable all multicasts */ lo_bits = 0xfffffffful; hi_bits = 0xfffffffful; /* Disable individual receive */ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard); *R_NETWORK_REC_CONFIG = network_rec_config_shadow; } else if (num_addr == 0) { /* Normal, clear the mc list */ lo_bits = 0x00000000ul; hi_bits = 0x00000000ul; /* Disable individual receive */ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard); *R_NETWORK_REC_CONFIG = network_rec_config_shadow; } else { /* MC mode, receive normal and MC packets */ char hash_ix; struct dev_mc_list *dmi = dev->mc_list; int i; char *baddr; lo_bits = 0x00000000ul; hi_bits = 0x00000000ul; for (i=0; i<num_addr; i++) { /* Calculate the hash index for the GA registers */ hash_ix = 0; baddr = dmi->dmi_addr; hash_ix ^= (*baddr) & 0x3f; hash_ix ^= ((*baddr) >> 6) & 0x03; ++baddr; hash_ix ^= ((*baddr) << 2) & 0x03c; hash_ix ^= ((*baddr) >> 4) & 0xf; ++baddr; hash_ix ^= ((*baddr) << 4) & 0x30; hash_ix ^= ((*baddr) >> 2) & 0x3f; ++baddr; hash_ix ^= (*baddr) & 0x3f; hash_ix ^= ((*baddr) >> 6) & 0x03; ++baddr; hash_ix ^= ((*baddr) << 2) & 0x03c; hash_ix ^= ((*baddr) >> 4) & 0xf; ++baddr; hash_ix ^= ((*baddr) << 4) & 0x30; hash_ix ^= ((*baddr) >> 2) & 0x3f; hash_ix &= 0x3f; if (hash_ix > 32) { hi_bits |= (1 << (hash_ix-32)); } else { lo_bits |= (1 << hash_ix); } dmi = dmi->next; } /* Disable individual receive */ SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard); *R_NETWORK_REC_CONFIG = network_rec_config_shadow; } *R_NETWORK_GA_0 = lo_bits; *R_NETWORK_GA_1 = hi_bits;}voide100_hardware_send_packet(char *buf, int length){ D(printk("e100 send pack, buf 0x%x len %d\n", buf, length)); if (!led_active && time_after(jiffies, led_next_time)) { /* light the network leds depending on the current speed. */ e100_set_network_leds(NETWORK_ACTIVITY); /* Set the earliest time we may clear the LED */ led_next_time = jiffies + NET_FLASH_TIME; led_active = 1; mod_timer(&clear_led_timer, jiffies + HZ/10); } /* configure the tx dma descriptor */ myNextTxDesc->descr.sw_len = length; myNextTxDesc->descr.ctrl = d_eop | d_eol | d_wait; myNextTxDesc->descr.buf = virt_to_phys(buf); /* Move end of list */ myLastTxDesc->descr.ctrl &= ~d_eol; myLastTxDesc = myNextTxDesc; /* Restart DMA channel */ *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, restart);}static voide100_clear_network_leds(unsigned long dummy){ if (led_active && time_after(jiffies, led_next_time)) { e100_set_network_leds(NO_NETWORK_ACTIVITY); /* Set the earliest time we may set the LED */ led_next_time = jiffies + NET_FLASH_PAUSE; led_active = 0; }}static voide100_set_network_leds(int active){#if defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK) int light_leds = (active == NO_NETWORK_ACTIVITY);#elif defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY) int light_leds = (active == NETWORK_ACTIVITY);#else#error "Define either CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK or CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY"#endif if (!current_speed) { /* Make LED red, link is down */ LED_NETWORK_SET(LED_RED); } else if (light_leds) { if (current_speed == 10) { LED_NETWORK_SET(LED_ORANGE); } else { LED_NETWORK_SET(LED_GREEN); } } else { LED_NETWORK_SET(LED_OFF); }}static struct net_device dev_etrax_ethernet; /* only got one */static intetrax_init_module(void){ struct net_device *d = &dev_etrax_ethernet; d->init = etrax_ethernet_init; if (register_netdev(d) == 0) return 0; else return -ENODEV;}module_init(etrax_init_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -