at91_ether.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,111 行 · 第 1/3 页
C
1,111 行
static short __init unpack_mac_address(struct net_device *dev, unsigned int hi, unsigned int lo){ char addr[6]; if (machine_is_csb337()) { addr[5] = (lo & 0xff); /* The CSB337 bootloader stores the MAC the wrong-way around */ addr[4] = (lo & 0xff00) >> 8; addr[3] = (lo & 0xff0000) >> 16; addr[2] = (lo & 0xff000000) >> 24; addr[1] = (hi & 0xff); addr[0] = (hi & 0xff00) >> 8; } else { addr[0] = (lo & 0xff); addr[1] = (lo & 0xff00) >> 8; addr[2] = (lo & 0xff0000) >> 16; addr[3] = (lo & 0xff000000) >> 24; addr[4] = (hi & 0xff); addr[5] = (hi & 0xff00) >> 8; } if (is_valid_ether_addr(addr)) { memcpy(dev->dev_addr, &addr, 6); return 1; } return 0;}/* * Set the ethernet MAC address in dev->dev_addr */static void __init get_mac_address(struct net_device *dev){ /* Check Specific-Address 1 */ if (unpack_mac_address(dev, at91_emac_read(AT91_EMAC_SA1H), at91_emac_read(AT91_EMAC_SA1L))) return; /* Check Specific-Address 2 */ if (unpack_mac_address(dev, at91_emac_read(AT91_EMAC_SA2H), at91_emac_read(AT91_EMAC_SA2L))) return; /* Check Specific-Address 3 */ if (unpack_mac_address(dev, at91_emac_read(AT91_EMAC_SA3H), at91_emac_read(AT91_EMAC_SA3L))) return; /* Check Specific-Address 4 */ if (unpack_mac_address(dev, at91_emac_read(AT91_EMAC_SA4H), at91_emac_read(AT91_EMAC_SA4L))) return; printk(KERN_ERR "at91_ether: Your bootloader did not configure a MAC address.\n");}/* * Program the hardware MAC address from dev->dev_addr. */static void update_mac_address(struct net_device *dev){ at91_emac_write(AT91_EMAC_SA1L, (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) | (dev->dev_addr[1] << 8) | (dev->dev_addr[0])); at91_emac_write(AT91_EMAC_SA1H, (dev->dev_addr[5] << 8) | (dev->dev_addr[4])); at91_emac_write(AT91_EMAC_SA2L, 0); at91_emac_write(AT91_EMAC_SA2H, 0);}/* * Store the new hardware address in dev->dev_addr, and update the MAC. */static int set_mac_address(struct net_device *dev, void* addr){ struct sockaddr *address = addr; if (!is_valid_ether_addr(address->sa_data)) return -EADDRNOTAVAIL; memcpy(dev->dev_addr, address->sa_data, dev->addr_len); update_mac_address(dev); printk("%s: Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); return 0;}static int inline hash_bit_value(int bitnr, __u8 *addr){ if (addr[bitnr / 8] & (1 << (bitnr % 8))) return 1; return 0;}/* * The hash address register is 64 bits long and takes up two locations in the memory map. * The least significant bits are stored in EMAC_HSL and the most significant * bits in EMAC_HSH. * * The unicast hash enable and the multicast hash enable bits in the network configuration * register enable the reception of hash matched frames. The destination address is * reduced to a 6 bit index into the 64 bit hash register using the following hash function. * The hash function is an exclusive or of every sixth bit of the destination address. * hash_index[5] = da[5] ^ da[11] ^ da[17] ^ da[23] ^ da[29] ^ da[35] ^ da[41] ^ da[47] * hash_index[4] = da[4] ^ da[10] ^ da[16] ^ da[22] ^ da[28] ^ da[34] ^ da[40] ^ da[46] * hash_index[3] = da[3] ^ da[09] ^ da[15] ^ da[21] ^ da[27] ^ da[33] ^ da[39] ^ da[45] * hash_index[2] = da[2] ^ da[08] ^ da[14] ^ da[20] ^ da[26] ^ da[32] ^ da[38] ^ da[44] * hash_index[1] = da[1] ^ da[07] ^ da[13] ^ da[19] ^ da[25] ^ da[31] ^ da[37] ^ da[43] * hash_index[0] = da[0] ^ da[06] ^ da[12] ^ da[18] ^ da[24] ^ da[30] ^ da[36] ^ da[42] * da[0] represents the least significant bit of the first byte received, that is, the multicast/ * unicast indicator, and da[47] represents the most significant bit of the last byte * received. * If the hash index points to a bit that is set in the hash register then the frame will be * matched according to whether the frame is multicast or unicast. * A multicast match will be signalled if the multicast hash enable bit is set, da[0] is 1 and * the hash index points to a bit set in the hash register. * A unicast match will be signalled if the unicast hash enable bit is set, da[0] is 0 and the * hash index points to a bit set in the hash register. * To receive all multicast frames, the hash register should be set with all ones and the * multicast hash enable bit should be set in the network configuration register. *//* * Return the hash index value for the specified address. */static int hash_get_index(__u8 *addr){ int i, j, bitval; int hash_index = 0; for (j = 0; j < 6; j++) { for (i = 0, bitval = 0; i < 8; i++) bitval ^= hash_bit_value(i*6 + j, addr); hash_index |= (bitval << j); } return hash_index;}/* * Add multicast addresses to the internal multicast-hash table. */static void at91ether_sethashtable(struct net_device *dev){ struct dev_mc_list *curr; unsigned long mc_filter[2]; unsigned int i, bitnr; mc_filter[0] = mc_filter[1] = 0; curr = dev->mc_list; for (i = 0; i < dev->mc_count; i++, curr = curr->next) { if (!curr) break; /* unexpected end of list */ bitnr = hash_get_index(curr->dmi_addr); mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); } at91_emac_write(AT91_EMAC_HSH, mc_filter[0]); at91_emac_write(AT91_EMAC_HSL, mc_filter[1]);}/* * Enable/Disable promiscuous and multicast modes. */static void at91ether_set_rx_mode(struct net_device *dev){ unsigned long cfg; cfg = at91_emac_read(AT91_EMAC_CFG); if (dev->flags & IFF_PROMISC) /* Enable promiscuous mode */ cfg |= AT91_EMAC_CAF; else if (dev->flags & (~IFF_PROMISC)) /* Disable promiscuous mode */ cfg &= ~AT91_EMAC_CAF; if (dev->flags & IFF_ALLMULTI) { /* Enable all multicast mode */ at91_emac_write(AT91_EMAC_HSH, -1); at91_emac_write(AT91_EMAC_HSL, -1); cfg |= AT91_EMAC_MTI; } else if (dev->mc_count > 0) { /* Enable specific multicasts */ at91ether_sethashtable(dev); cfg |= AT91_EMAC_MTI; } else if (dev->flags & (~IFF_ALLMULTI)) { /* Disable all multicast mode */ at91_emac_write(AT91_EMAC_HSH, 0); at91_emac_write(AT91_EMAC_HSL, 0); cfg &= ~AT91_EMAC_MTI; } at91_emac_write(AT91_EMAC_CFG, cfg);}/* ......................... ETHTOOL SUPPORT ........................... */static int mdio_read(struct net_device *dev, int phy_id, int location){ unsigned int value; read_phy(phy_id, location, &value); return value;}static void mdio_write(struct net_device *dev, int phy_id, int location, int value){ write_phy(phy_id, location, value);}static int at91ether_get_settings(struct net_device *dev, struct ethtool_cmd *cmd){ struct at91_private *lp = (struct at91_private *) dev->priv; int ret; spin_lock_irq(&lp->lock); enable_mdi(); ret = mii_ethtool_gset(&lp->mii, cmd); disable_mdi(); spin_unlock_irq(&lp->lock); if (lp->phy_media == PORT_FIBRE) { /* override media type since mii.c doesn't know */ cmd->supported = SUPPORTED_FIBRE; cmd->port = PORT_FIBRE; } return ret;}static int at91ether_set_settings(struct net_device *dev, struct ethtool_cmd *cmd){ struct at91_private *lp = (struct at91_private *) dev->priv; int ret; spin_lock_irq(&lp->lock); enable_mdi(); ret = mii_ethtool_sset(&lp->mii, cmd); disable_mdi(); spin_unlock_irq(&lp->lock); return ret;}static int at91ether_nwayreset(struct net_device *dev){ struct at91_private *lp = (struct at91_private *) dev->priv; int ret; spin_lock_irq(&lp->lock); enable_mdi(); ret = mii_nway_restart(&lp->mii); disable_mdi(); spin_unlock_irq(&lp->lock); return ret;}static void at91ether_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); strlcpy(info->bus_info, dev->class_dev.dev->bus_id, sizeof(info->bus_info));}static struct ethtool_ops at91ether_ethtool_ops = { .get_settings = at91ether_get_settings, .set_settings = at91ether_set_settings, .get_drvinfo = at91ether_get_drvinfo, .nway_reset = at91ether_nwayreset, .get_link = ethtool_op_get_link,};/* ................................ MAC ................................ *//* * Initialize and start the Receiver and Transmit subsystems */static void at91ether_start(struct net_device *dev){ struct at91_private *lp = (struct at91_private *) dev->priv; struct recv_desc_bufs *dlist, *dlist_phys; int i; unsigned long ctl; dlist = lp->dlist; dlist_phys = lp->dlist_phys; for (i = 0; i < MAX_RX_DESCR; i++) { dlist->descriptors[i].addr = (unsigned int) &dlist_phys->recv_buf[i][0]; dlist->descriptors[i].size = 0; } /* Set the Wrap bit on the last descriptor */ dlist->descriptors[i-1].addr |= EMAC_DESC_WRAP; /* Reset buffer index */ lp->rxBuffIndex = 0; /* Program address of descriptor list in Rx Buffer Queue register */ at91_emac_write(AT91_EMAC_RBQP, (unsigned long) dlist_phys); /* Enable Receive and Transmit */ ctl = at91_emac_read(AT91_EMAC_CTL); at91_emac_write(AT91_EMAC_CTL, ctl | AT91_EMAC_RE | AT91_EMAC_TE);}/* * Open the ethernet interface */static int at91ether_open(struct net_device *dev){ struct at91_private *lp = (struct at91_private *) dev->priv; unsigned long ctl; if (!is_valid_ether_addr(dev->dev_addr)) return -EADDRNOTAVAIL; clk_enable(ether_clk); /* Re-enable Peripheral clock */ /* Clear internal statistics */ ctl = at91_emac_read(AT91_EMAC_CTL); at91_emac_write(AT91_EMAC_CTL, ctl | AT91_EMAC_CSR); /* Update the MAC address (incase user has changed it) */ update_mac_address(dev); /* Enable PHY interrupt */ enable_phyirq(dev); /* Enable MAC interrupts */ at91_emac_write(AT91_EMAC_IER, AT91_EMAC_RCOM | AT91_EMAC_RBNA | AT91_EMAC_TUND | AT91_EMAC_RTRY | AT91_EMAC_TCOM | AT91_EMAC_ROVR | AT91_EMAC_ABT); /* Determine current link speed */ spin_lock_irq(&lp->lock); enable_mdi(); update_linkspeed(dev); disable_mdi(); spin_unlock_irq(&lp->lock); at91ether_start(dev); netif_start_queue(dev); return 0;}/* * Close the interface */static int at91ether_close(struct net_device *dev){ unsigned long ctl; /* Disable Receiver and Transmitter */ ctl = at91_emac_read(AT91_EMAC_CTL); at91_emac_write(AT91_EMAC_CTL, ctl & ~(AT91_EMAC_TE | AT91_EMAC_RE)); /* Disable PHY interrupt */ disable_phyirq(dev); /* Disable MAC interrupts */ at91_emac_write(AT91_EMAC_IDR, AT91_EMAC_RCOM | AT91_EMAC_RBNA | AT91_EMAC_TUND | AT91_EMAC_RTRY | AT91_EMAC_TCOM | AT91_EMAC_ROVR | AT91_EMAC_ABT); netif_stop_queue(dev); clk_disable(ether_clk); /* Disable Peripheral clock */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?