at91_ether.c
来自「linux 内核源代码」· C语言 代码 · 共 1,249 行 · 第 1/3 页
C
1,249 行
* continues to do so, for bug-compatibility). */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; DECLARE_MAC_BUF(mac); 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 %s\n", dev->name, print_mac(mac, dev->dev_addr)); 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_HSL, mc_filter[0]); at91_emac_write(AT91_EMAC_HSH, 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 = netdev_priv(dev); 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 = netdev_priv(dev); 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 = netdev_priv(dev); 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->dev.parent->bus_id, sizeof(info->bus_info));}static const 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,};static int at91ether_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){ struct at91_private *lp = netdev_priv(dev); int res; if (!netif_running(dev)) return -EINVAL; spin_lock_irq(&lp->lock); enable_mdi(); res = generic_mii_ioctl(&lp->mii, if_mii(rq), cmd, NULL); disable_mdi(); spin_unlock_irq(&lp->lock); return res;}/* ................................ MAC ................................ *//* * Initialize and start the Receiver and Transmit subsystems */static void at91ether_start(struct net_device *dev){ struct at91_private *lp = netdev_priv(dev); 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 = netdev_priv(dev); unsigned long ctl; if (!is_valid_ether_addr(dev->dev_addr)) return -EADDRNOTAVAIL; clk_enable(lp->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, 0); 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){ struct at91_private *lp = netdev_priv(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(lp->ether_clk); /* Disable Peripheral clock */ return 0;}/* * Transmit packet. */static int at91ether_tx(struct sk_buff *skb, struct net_device *dev){ struct at91_private *lp = netdev_priv(dev); if (at91_emac_read(AT91_EMAC_TSR) & AT91_EMAC_TSR_BNQ) { netif_stop_queue(dev); /* Store packet information (to free when Tx completed) */ lp->skb = skb; lp->skb_length = skb->len; lp->skb_physaddr = dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE); lp->stats.tx_bytes += skb->len; /* Set address of the data in the Transmit Address register */ at91_emac_write(AT91_EMAC_TAR, lp->skb_physaddr); /* Set length of the packet in the Transmit Control register */ at91_emac_write(AT91_EMAC_TCR, skb->len); dev->trans_start = jiffies; } else { printk(KERN_ERR "at91_ether.c: at91ether_tx() called, but device is busy!\n"); return 1; /* if we return anything but zero, dev.c:1055 calls kfree_skb(skb) on this skb, he also reports -ENETDOWN and printk's, so either
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?