linux網卡驅動中mac地址隨機數變化

    mac地址在一般情況下是從網卡的eeprom中讀取的,一般廠商會在出廠的時候固化在eeprom中,在有些時候,廠商爲了省錢不會去買一個固定的唯一的mac,而是始終用一個固定的mac地址,這樣就會在後續用戶使用的時候造成mac地址衝突。

本文以qf9700網卡驅動爲例,講解添加mac地址以隨機數變化的驅動程序。

#define qf9700_AUTOMAC
#ifdef    qf9700_AUTOMAC
/* Global variables for file-based MAC address Machenism */
int mac_used[129] = {0};
int dev_addr[129] = {0};
DEFINE_SPINLOCK(qf9700_lock);
#endif

定義一個內核自旋鎖qf9700_lock。


mac地址的讀寫主要在驅動的bind函數中

static const struct driver_info qf9700_info = {
	.description	= "QF9700 USB Ethernet",
	.flags		= FLAG_ETHER,
	.bind		= qf9700_bind,
	.unbind     = qf9700_unbind,
	.rx_fixup	= qf9700_rx_fixup,
	.tx_fixup	= qf9700_tx_fixup,
	.status		= qf9700_status,
	.link_reset	= qf9700_link_reset,
	.reset		= qf9700_link_reset,
};

以下是bind函數,宏qf9700_AUTOMAC定義包含的就是需要添加的功能

<pre name="code" class="cpp">static int qf9700_bind(struct usbnet *dev, struct usb_interface *intf)
{
	int ret;
#ifdef	qf9700_AUTOMAC	
	int i;	
	u8 defaultAddress[] = {0x00,0xe0,0x4c,0x53,0x44,0x58};	
	struct file *fp;   
	mm_segment_t fs;		
	loff_t pos;
#endif

	ret = usbnet_get_endpoints(dev, intf);
	if (ret)
		goto out;

    dev->net->netdev_ops = &qf9700_netdev_ops;
	dev->net->ethtool_ops = &qf9700_ethtool_ops;
	dev->net->hard_header_len += QF_TX_OVERHEAD;
	dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
	dev->rx_urb_size = dev->net->mtu + ETH_HLEN + QF_RX_OVERHEAD;

	dev->mii.dev = dev->net;
	dev->mii.mdio_read = qf9700_mdio_read;
	dev->mii.mdio_write = qf9700_mdio_write;
	dev->mii.phy_id_mask = 0x1f;
	dev->mii.reg_num_mask = 0x1f;

	/* reset the qf9700 */
	qf_write_reg(dev, NCR, 1);
	udelay(20);

	/* read MAC */
	if (qf_read(dev, PAR, ETH_ALEN, dev->net->dev_addr) < 0) {
		printk(KERN_ERR "Error reading MAC address\n");
		ret = -ENODEV;
		goto out;
	}

	#ifdef	qf9700_AUTOMAC	
	/* Compare with the file-based MAC address	 * This is for dongle or product without EEPROM condition	 */	
	if (memcmp( dev->net->dev_addr, defaultAddress, ETH_ALEN ) == 0)	
	{		
	    spin_lock(&qf9700_lock);		
		for(i=0; i<129; i++)		
		{			
		    if(mac_used[i] == 0)		
			{				
			    fp = filp_open("/usr/mac", O_RDONLY, 0); 				
				if (IS_ERR(fp)) 			
				{ 					
				    netdev_err(dev->net, "open file error");				
					fp = filp_open("/usr/mac", O_RDWR | O_CREAT, 0644); 	
					if (IS_ERR(fp)) 					
					{						
					    netdev_err(dev->net, "creat file error");        		
						goto out; 					
					}					
					fs = get_fs();     				
					set_fs(KERNEL_DS);					
					random_ether_addr(dev->net->dev_addr);					
					*(u8 *)(dev->net->dev_addr + 0) = 0;					
					pos = 0; 					
					vfs_write(fp, dev->net->dev_addr, ETH_ALEN, &pos); 	    				
				} 				
				else				
				{					
				    fs = get_fs();    					
					set_fs(KERNEL_DS);				
				}						
				pos = 0; 				
				vfs_read(fp, dev->net->dev_addr, ETH_ALEN, &pos);				
				filp_close(fp, NULL);     			
				set_fs(fs); 						
				dev_addr[i] = dev->udev->devnum;				
				mac_used[i] = 1;		
				/*		*(u8*)(dev->net->dev_addr + 0) += i;				
				*(u8*)(dev->net->dev_addr + 1) += i;				
				*(u8*)(dev->net->dev_addr + 2) += i;				
				*(u8*)(dev->net->dev_addr + 3) += i;				
				*(u8*)(dev->net->dev_addr + 4) += i;*/				
				*(u8*)(dev->net->dev_addr + 5) += i;				
				break;			
			}			
		}		
		spin_unlock(&qf9700_lock);		
		/* Set the MAC address */		
		if ((ret = qf_write (dev, PAR, ETH_ALEN, dev->net->dev_addr)) < 0) 
		{			
		    netdev_err(dev->net, "set MAC address failed: %d", ret);			
			goto out;		
		}	
		
	}	
	netif_carrier_off(dev->net);
	#endif

	/* power up and reset phy */
	qf_write_reg(dev, PRR, 1);
	mdelay(20);	// at least 10ms, here 20ms for safe
	qf_write_reg(dev, PRR, 0);
	mdelay(2);	// at least 1ms, here 2ms for reading right register

	/* receive broadcast packets */
	qf9700_set_multicast(dev->net);

	qf9700_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
	qf9700_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
	mii_nway_restart(&dev->mii);

out:
	return ret;
}





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章