前言
本文以 PX30 USB网卡设置自定义MAC为例,分享如何在bootargs参数基础上添加自定义字段,实现uboot向kernel传递参数。
一、关于bootargs
bootargs 是 uboot 在启动 Linux 内核时传递给内核的引导参数,参数中一般包含启动存储介质、文件系统分区及挂载方式和终端串口等参数。
在系统中我们可以通过 /proc/cmdline 来查看 uboot 给 kernel 传递的 bootargs 参数内容。
# cat /proc/cmdline
storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal
androidboot.slot_suffix= androidboot.serialno=c3d9b8674f4b94f6 rw rootwait
earlycon=uart8250,mmio32,0xff160000 swiotlb=1 console=ttyFIQ0 root=PARTUUID=614e0000-0000
二、网卡自定义MAC
Rockchip PX30芯片只有一路以太网,为了满足一些多网口的应用场景,会使用 USB 转以太网口芯片来扩展以太网接口。以Realtek r8152芯片为例,转换芯片自身不带硬件MAC地址,这将导致每次系统启动,网卡的MAC也会随机变化而非固定;可以在驱动加载时将固定的自定义的MAC地址设置给网卡,以解决MAC地址不固定的问题。
Rockchip在eMMC的首部保留了一块用于存储序列号、MAC等自定义内容的Vendor Storage存储空间。
Vendor Storage在eMMC中的位置:
Vendor Storage 结构
Vendors Storage 各ID内容序号如下图所示:
使用Rockchip写号工具向Vendor Storage区域写入自定义的MAC地址,之后在程序中读取出Vendor Storage中保存的MAC地址来实现MAC地址的设置。PX30芯片自带网卡的MAC设置使用的是LAN MAC存储段的数据,给 USB转网口芯片的网卡设置固定MAC,可以选择Vendor Storage中未被使用的BT MAC ID段来保存MAC地址。
三、代码实现示例
本章以Rockchip PX30 linux SDK为基础添加Vendors Storage BT MAC 字段的读取,并通过 bootargs 参数传递给kernel。
Uboot中读取Vendor Storage
uboot源码的 arch/arm/mach-rockchip/ 为rockchip平台相关代码目录,在 arch/arm/mach-rockchip/board.c 文件中添加Vendor Storage BT MAC内容的读取,并将读取出MAC地址设置给变量 bt_mac。详细代码如下:
--- a/arch/arm/mach-rockchip/board.c
+++ b/arch/arm/mach-rockchip/board.c
@@ -74,6 +74,24 @@ __weak int rk_board_init(void)
#define CPUID_LEN 0x10
#define CPUID_OFF 0x07
+static int rockchip_get_bt_mac(void)
+{
+#ifdef CONFIG_ROCKCHIP_VENDOR_PARTITION
+ char buf[ARP_HLEN_ASCII + 1];
+ u8 ethaddr[ARP_HLEN];
+ int ret;
+
+ ret = vendor_storage_read(VENDOR_BLUETOOTH_ID, ethaddr, sizeof(ethaddr));
+ if (ret > 0 && is_valid_ethaddr(ethaddr)) {
+ sprintf(buf, "%pM", ethaddr);
+ env_set("bt_mac", buf);
+ }
+#endif
+ return 0;
+
+}
+
static int rockchip_set_ethaddr(void)
{
#ifdef CONFIG_ROCKCHIP_VENDOR_PARTITION
@@ -161,6 +179,7 @@ int fb_set_reboot_flag(void)
int board_late_init(void)
{
+ rockchip_get_bt_mac();
rockchip_set_ethaddr();
rockchip_set_serialno();
#if (CONFIG_ROCKCHIP_BOOT_MODE_REG > 0)
在bootargs参数中添加MAC地址字段
在uboot的 arch/arm/lib/bootm.c 文件中找到 boot_prep_linux 函数,此函数包含读取 bootargs 参数并将其保存至指定的位置,在最后跳转执行内核时作为参数传递给内核入口函数。只需在bootargs传递之前,给bootargs添加一个bt_mac字段即可。详细代码如下:
--- a/arch/arm/lib/bootm.c
+++ b/arch/arm/lib/bootm.c
@@ -224,8 +224,27 @@ static void do_nonsec_virt_switch(void)
/* Subcommand: PREP */
static void boot_prep_linux(bootm_headers_t *images)
{
- char *commandline = env_get("bootargs");
+#ifdef CONFIG_ROCKCHIP_VENDOR_PARTITION
+ char *commandline = NULL;
+ char new_bootargs[512] = {0};
+ char* old_bootargs = env_get("bootargs");
+ int old_len = strlen(old_bootargs);
+ char *bt_mac = env_get("bt_mac");
+
+ if(bt_mac){
+ strncpy(new_bootargs, old_bootargs, old_len);
+ sprintf(new_bootargs, "%s %s%s", new_bootargs, "bt_mac=", env_get("bt_mac"));
+ env_set("bootargs", new_bootargs);
+ }
+ commandline = env_get("bootargs");
+#else
+ char *commandline = env_get("bootargs");
+#endif
if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
#ifdef CONFIG_OF_LIBFDT
debug("using: FDT\n");
kernel中获取MAC地址并设置
在Linux 内核 r8152 的驱动 drivers\net\usb\r8152.c 中,使用 __setup 函数来获取bootargs中的bt_mac字段并检查MAC的合法性;并在网卡MAC地址设置函数中,将合法的MAC设置给USB网卡。详细代码如下:
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -27,6 +27,15 @@
#include <linux/usb/cdc.h>
#include <linux/suspend.h>
+#define R8152_USE_BT_MAC_ADDR
+#ifdef R8152_USE_BT_MAC_ADDR
+#define BT_MAC_STR_LEN 17 //e.g. AA:BB:CC:DD:EE:FF
+static unsigned char uboot_get_btmac_flag = 0;
+static unsigned char mac_addr_str[18]= {0};
+static char bt_mac_addr_hex[6] = {0};
+#endif
+
/* Information for net-next */
#define NETNEXT_VERSION "08"
@@ -1042,6 +1051,16 @@ static int set_ethernet_addr(struct r8152 *tp)
else
ret = pla_ocp_read(tp, PLA_BACKUP, 8, sa.sa_data);
+#ifdef R8152_USE_BT_MAC_ADDR
+ if(uboot_get_btmac_flag && is_valid_ether_addr(bt_mac_addr_hex)){
+ ether_addr_copy(sa.sa_data, bt_mac_addr_hex);
+
+ ret = rtl8152_set_mac_address(dev, &sa);
+ netif_info(tp, probe, dev, "bt_mac ether addr %pM\n", sa.sa_data);
+ return ret;
+ }
+#endif
+
if (ret < 0) {
netif_err(tp, probe, dev, "Get ether addr fail\n");
} else if (!is_valid_ether_addr(sa.sa_data)) {
@@ -4357,6 +4376,25 @@ static void rtl8152_disconnect(struct usb_interface *intf)
free_netdev(tp->netdev);
}
}
+#ifdef R8152_USE_BT_MAC_ADDR
+static int __init get_bt_mac_addr(char* str)
+{
+ if(!strcmp(str,"<NULL>") || (BT_MAC_STR_LEN != strlen(str))){
+ printk("bt_mac get failed\n");
+ return 0;
+ }
+
+ strncpy(mac_addr_str,str, BT_MAC_STR_LEN);
+ sscanf(mac_addr_str, "%02x:%02x:%02x:%02x:%02x:%02x", \
+ (int*)&bt_mac_addr_hex[0], (int*)&bt_mac_addr_hex[1], \
+ (int*)&bt_mac_addr_hex[2], (int*)&bt_mac_addr_hex[3], \
+ (int*)&bt_mac_addr_hex[4], (int*)&bt_mac_addr_hex[5]);
+
+ if(is_valid_ether_addr(bt_mac_addr_hex)){
+ uboot_get_btmac_flag = 1;
+ }
+ return 0;
+}
+__setup("bt_mac=",get_bt_mac_addr);
+#endif
#define REALTEK_USB_DEVICE(vend, prod) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
四、测试验证
使用RKDevInfoWriteTool软件向BT MAC地址写入0A0C11223345
系统启动后,查看/proc/cmdline 可看到 bt_mac=0a:0c:11:22:33:45 ,说明bootargs修改成功。
# cat /proc/cmdline
storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal
androidboot.slot_suffix= androidboot.serialno=A5303202110A0000001
bt_mac=0a:0c:11:22:33:45 rw rootwait earlycon=uart8250,mmio32,0xff160000
swiotlb=1 console=ttyFIQ0 root=PARTUUID=614e0000-0000
查看网卡MAC地址也已设置为烧录的MAC
# ifconfig eth1
eth1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether 0a:0c:11:22:33:45 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0