[Embedded linux] Modify the MAC address of the network port

When using devices with network functions, if many devices may be connected in the LAN, in order to ensure normal network communication, it is necessary to ensure that the hardware MAC address of each device is different. sn number) to fix the MAC address of each device to prevent MAC conflicts.

Method of modifying MAC address:
1. Application layer
1. Modify by command ifconfig:
ifconfig xxx(network port name) down
ifconfig xxx(network port name) hw ether xx:xx:xx:xx:xx:xx(MAC address)
ifconfig xxx up (restart network card)

as shown below
insert image description here

2. Through the socket interface modification of linux (the above command line is essentially implemented in this way)
the following part implements the code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/route.h>


int fd = -1;
struct ifreq ifr;

fd = socket(AF_INET, SOCK_DGRAM, 0);
if(fd < 0)
{
    
    
	printf("socket error\n");
}
memset(&ifr,0,sizeof(ifr));
strcpy(ifr.ifr_name,eth_name);
setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,(char*)&ifr,sizeof(ifr));
if(ioctl(fd,SIOCGIFHWADDR,&ifr) < 0)  //获得MAC地址
{
    
    
	printf("ioctl SIOCSIFHWADDR error\n");
}
unsigned char mac[6]={
    
    12,23,34,45,56,67};
memcpy(ifr.ifr_hwaddr,mac,6);
if(ioctl(fd,SIOCSIFHWADDR,&ifr) < 0)  //设置MAC地址
{
    
    
	printf("ioctl SIOCSIFHWADDR error\n");
}

It is more flexible and convenient to modify the MAC at the application layer, and the MAC address can be set and managed by the app at any time.

2. Kernel layer modification
Modify the device driver of the network card, the principle is similar to the following uboot modification. Not described here.

3. Uboot modifies
the kernel's network card device driver and finally transmits the MAC address to the upper layer according to the MAC address register of the read network card. Therefore, modifying the relevant registers in uboot can also modify the MAC address.

Here we take the uboot of 2019.01 as an example. First find the core layer of the network device driver in the uboot source code to see how to configure and initialize the network device.
uboot/ /net/eth-uclass.c (The new driver basically uses the DM framework, the driver core layer is generally in xxx-uclass.c, and the old one is generally not used in xxx_legacy.c). Find the configuration interface about the MAC address in the entry function of the network port eth_post_probe.

/* Check if the device has a MAC address in ROM */
if (eth_get_ops(dev)->read_rom_hwaddr)
	eth_get_ops(dev)->read_rom_hwaddr(dev);

eth_env_get_enetaddr_by_index("eth", dev->seq, env_enetaddr);
if (!is_zero_ethaddr(env_enetaddr)) {
    
    
	if (!is_zero_ethaddr(pdata->enetaddr) &&
	memcmp(pdata->enetaddr, env_enetaddr, ARP_HLEN)) {
    
    
		printf("\nWarning: %s MAC addresses don't match:\n",
		dev->name);
		printf("Address in ROM is %pM\n",
		pdata->enetaddr);
		printf("Address in environment is %pM\n",
		env_enetaddr);
}

/* Override the ROM MAC address */
memcpy(pdata->enetaddr, env_enetaddr, ARP_HLEN);
} else if (is_valid_ethaddr(pdata->enetaddr)) {
    
    
	eth_env_set_enetaddr_by_index("eth", dev->seq, pdata->enetaddr);
	printf("\nWarning: %s using MAC address from ROM\n",
	dev->name);
} else if (is_zero_ethaddr(pdata->enetaddr) ||
!is_valid_ethaddr(pdata->enetaddr)) {
    
    
#ifdef CONFIG_NET_RANDOM_ETHADDR
net_random_ethaddr(pdata->enetaddr);
printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
dev->name, dev->seq, pdata->enetaddr);
#else
printf("\nError: %s address not set.\n",
dev->name);
return -EINVAL;
#endif
}

It can be concluded that the general process of setting the MAC address in uboot is: by reading from the external storage medium (ROM), environment variable env or randomly generating the MAC address and saving it to the driver data dev->platdata->enetaddr(6 bytes, stored in hexadecimal number format), when the subsequent uboot initializes the hardware peripherals, the interface for writing the MAC address to the relevant registers of the MAC controller is called to realize the writing of enetaddr to the hardware. Here are three ways to obtain the MAC address:
1. If the device driver implements the read_rom_hwaddr interface, execute it to read the MAC address from the ROM and save it to the device's driver data eth_pdata structure. Here we take the zynq-7010 platform and the ROM as qspi flash W25q512 as an example. The device driver of the network port is implemented in uboot/drivers/net/zynq_gem.c.

static const struct eth_ops zynq_gem_ops = {
    
    
.start = zynq_gem_init,
.send = zynq_gem_send,
.recv = zynq_gem_recv,
.free_pkt = zynq_gem_free_pkt,
.stop = zynq_gem_halt,
.write_hwaddr = zynq_gem_setup_mac,
.read_rom_hwaddr = zynq_gem_read_rom_mac,
};

__weak int zynq_board_read_rom_ethaddr(unsigned char *ethaddr)
{
    
    
	return -ENOSYS;
}


static int zynq_gem_read_rom_mac(struct udevice *dev)
{
    
    
	struct eth_pdata *pdata = dev_get_platdata(dev);
	
	if (!pdata)
		return -ENOSYS;
	
	return zynq_board_read_rom_ethaddr(pdata->enetaddr);
}

* If there is a definition and implementation of int zynq_board_read_rom_ethaddr(unsigned char ethaddr
), this interface will be called to read the MAC address from the rom, otherwise, the above interface with the default weak link will be executed. Here
customize the implementation of this interface in uboot/board/xilinx/zynq/board.c.

int zynq_board_read_rom_ethaddr(unsigned char *ethaddr)
{
    
    
	int ret = 0;
	/*这里为了方便测试,手动设置MAC地址*/
	#if 1
	ethaddr[0]==0x00
	ethaddr[1]==0xa8
	ethaddr[2]==0x22
	ethaddr[3]==0x12
	ethaddr[4]==0x34
	ethaddr[5]==0x56#endif
	/*实际使用下面接口读取spi flash特定区域的内容,可自由添加读写flash、emmc、epprom等ROM的接口实现在ROM读取mac地址*/
	#if 0 
	unsigned int mac_offset=0x210000;//在flash的0x210000存取MAC地址
	unsigned int len=6;
	ret = board_spi_flash_probe();  //匹配spi flash
	if (ret)
	{
    
    
		printf("spi_flash_probe fail \n");
		return -1;
	}
	
	ret = spi_flash_read(board_spi_flash, mac_offset, len, ethaddr); //从spi flash
	if (ret)
	{
    
    
		return -2;
	}
	
	
	#endif
	return ret;
}

2. The MAC address is obtained from the environment variable env. The above eth_env_get_enetaddr_by_index("eth", dev->seq, env_enetaddr) interface is implemented to obtain the MAC address. This method needs to define ethaddr=xx:xx:xx:xx:xx:xx in env.

int eth_env_get_enetaddr_by_index(const char *base_name, int index,
uchar *enetaddr)
{
    
    
	char enetvar[32];
	sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
	return eth_env_get_enetaddr(enetvar, enetaddr);
}

3. When none of the above can obtain a valid MAC address, you can use net_random_ethaddr(pdata->enetaddr); generate a valid and random MAC address according to the random number generated by the timer.

static inline void net_random_ethaddr(uchar *addr)
{
    
    
	int i;
	unsigned int seed = get_timer(0);
	
	for (i = 0; i < 6; i++)
	addr[i] = rand_r(&seed);
	
	addr[0] &= 0xfe; /* clear multicast bit */
	addr[0] |= 0x02; /* set local assignment bit (IEEE802) */
}

After correctly obtaining the MAC address, write to the relevant registers of the MAC controller. Call flow:
initr_net() of uboot/common/board_r.c -> eth_initialize() of eth-uclass.c -> eth_write_hwaddr(dev) of eth-uclass.c ;-> zynq_gem_ops.write_hwaddr of the device driver zynq_gem.c → Finally, the operation of writing to the MAC address register of the MAC controller is realized through zynq_gem_setup_mac.

static int zynq_gem_setup_mac(struct udevice *dev)
{
    
    
	u32 i, macaddrlow, macaddrhigh;
	struct eth_pdata *pdata = dev_get_platdata(dev);
	struct zynq_gem_priv *priv = dev_get_priv(dev);
	struct zynq_gem_regs *regs = priv->iobase;
	
	/* Set the MAC bits [31:0] in BOT */
	macaddrlow = pdata->enetaddr[0];
	macaddrlow |= pdata->enetaddr[1] << 8;
	macaddrlow |= pdata->enetaddr[2] << 16;
	macaddrlow |= pdata->enetaddr[3] << 24;
	
	/* Set MAC bits [47:32] in TOP */
	macaddrhigh = pdata->enetaddr[4];
	macaddrhigh |= pdata->enetaddr[5] << 8;
	
	for (i = 0; i < 4; i++) {
    
    
	writel(0, &regs->laddr[i][LADDR_LOW]);
	writel(0, &regs->laddr[i][LADDR_HIGH]);
	/* Do not use MATCHx register */
	writel(0, &regs->match[i]);
	}
	
	writel(macaddrlow, &regs->laddr[0][LADDR_LOW]);
	writel(macaddrhigh, &regs->laddr[0][LADDR_HIGH]);
	
	return 0;
}
寄存器结构体
/* Device registers */
struct zynq_gem_regs {
    
    
u32 nwctrl; /* 0x0 - Network Control reg */
u32 nwcfg; /* 0x4 - Network Config reg */
u32 nwsr; /* 0x8 - Network Status reg */
u32 reserved1;
u32 dmacr; /* 0x10 - DMA Control reg */
u32 txsr; /* 0x14 - TX Status reg */
u32 rxqbase; /* 0x18 - RX Q Base address reg */
u32 txqbase; /* 0x1c - TX Q Base address reg */
u32 rxsr; /* 0x20 - RX Status reg */
u32 reserved2[2];
u32 idr; /* 0x2c - Interrupt Disable reg */
u32 reserved3;
u32 phymntnc; /* 0x34 - Phy Maintaince reg */
u32 reserved4[18];
u32 hashl; /* 0x80 - Hash Low address reg */
u32 hashh; /* 0x84 - Hash High address reg */
#define LADDR_LOW 0
#define LADDR_HIGH 1
u32 laddr[4][LADDR_HIGH + 1]; /* 0x8c - Specific1 addr low/high reg */
u32 match[4]; /* 0xa8 - Type ID1 Match reg */
u32 reserved6[18];
#define STAT_SIZE 44
u32 stat[STAT_SIZE]; /* 0x100 - Octects transmitted Low reg */
u32 reserved9[20];
u32 pcscntrl;
u32 rserved12[36];
u32 dcfg6; /* 0x294 Design config reg6 */
u32 reserved7[106];
u32 transmit_q1_ptr; /* 0x440 - Transmit priority queue 1 */
u32 reserved8[15];
u32 receive_q1_ptr; /* 0x480 - Receive priority queue 1 */
u32 reserved10[17];
u32 upper_txqbase; /* 0x4C8 - Upper tx_q base addr */
u32 reserved11[2];
u32 upper_rxqbase; /* 0x4D4 - Upper rx_q base addr */
};

The following figure shows the register table of the MAC controller
insert image description here

Two cases of reading from ROM and defining the MAC address in env are tested here.
1. Reading the MAC address in flash (method 1 above). Recompile and burn UBOOT. After the system starts, the network card configuration is as follows, and the MAC has been successfully modified to 00:A8:22:12:34:56 as shown in Figure
insert image description here
2. Add the MAC address 00:0A:22:11:22:33 to the env image. as follows. Recompile and generate the env image and store it in flash, then restart.
Define the ethaddr environment variable attribute as shown in the figure below.
insert image description here
When uboot starts, it will prompt a warning of MAC definition conflict. From the previous process analysis, the MAC address of env will be used first.
insert image description here
After the system starts, the following MAC address is ethaddr defined by env, and the verification is successful.
insert image description here

Supongo que te gusta

Origin blog.csdn.net/yechongbinbin/article/details/128295501
Recomendado
Clasificación