📄 ethernet.c
字号:
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(KERN_INFO "Closing %s.\n", dev->name); netif_stop_queue(dev); *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); /* Update the statistics here. */ update_rx_stats(&np->stats); update_tx_stats(&np->stats); /* Stop speed/duplex timers */ del_timer(&speed_timer); del_timer(&duplex_timer); return 0;}static inte100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ struct mii_ioctl_data *data = if_mii(ifr); struct net_local *np = netdev_priv(dev); spin_lock(&np->lock); /* Preempt protection */ switch (cmd) { case SIOCETHTOOL: return e100_ethtool_ioctl(dev,ifr); case SIOCGMIIPHY: /* Get PHY address */ data->phy_id = mdio_phy_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: /* Half 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; } spin_unlock(&np->lock); 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_phy_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, "$Revision: 1.22 $", 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 inte100_set_config(struct net_device *dev, struct ifmap *map){ struct net_local *np = (struct net_local *)dev->priv; spin_lock(&np->lock); /* Preempt protection */ switch(map->port) { case IF_PORT_UNKNOWN: /* Use autoneg */ e100_set_speed(0); e100_set_duplex(autoneg); break; case IF_PORT_10BASET: e100_set_speed(10); e100_set_duplex(autoneg); break; case IF_PORT_100BASET: case IF_PORT_100BASETX: e100_set_speed(100); e100_set_duplex(autoneg); break; case IF_PORT_100BASEFX: case IF_PORT_10BASE2: case IF_PORT_AUI: spin_unlock(&np->lock); return -EOPNOTSUPP; break; default: printk(KERN_ERR "%s: Invalid media selected", dev->name); spin_unlock(&np->lock); return -EINVAL; } spin_unlock(&np->lock); 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; unsigned long flags; spin_lock_irqsave(&lp->lock, flags); update_rx_stats(&lp->stats); update_tx_stats(&lp->stats); spin_unlock_irqrestore(&lp->lock, flags); 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){ struct net_local *lp = (struct net_local *)dev->priv; int num_addr = dev->mc_count; unsigned long int lo_bits; unsigned long int hi_bits; spin_lock(&lp->lock); 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; spin_unlock(&lp->lock);}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 */#if defined(CONFIG_ETRAX_NETWORK_RED_ON_NO_CONNECTION) LED_NETWORK_SET(LED_RED);#else LED_NETWORK_SET(LED_OFF);#endif } 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 intetrax_init_module(void){ return etrax_ethernet_init();}static int __inite100_boot_setup(char* str){ struct sockaddr sa = {0}; int i; /* Parse the colon separated Ethernet station address */ for (i = 0; i < ETH_ALEN; i++) { unsigned int tmp; if (sscanf(str + 3*i, "%2x", &tmp) != 1) { printk(KERN_WARNING "Malformed station address"); return 0; } sa.sa_data[i] = (char)tmp; } default_mac = sa; return 1;}__setup("etrax100_eth=", e100_boot_setup);module_init(etrax_init_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -