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 + -
显示快捷键?