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;
}