Rockchip系列之VendorStorage 浅浅的介绍(1)-CSDN博客
Rockchip系列之VendorStorage uboot/kernel/user space 阶段接口使用介绍(2)_一歲抬頭的博客-CSDN博客
Rockchip系列之VendorStorage 新增framework系统jni+service接口访问(3)-CSDN博客
Rockchip系列之VendorStorage 新增framework封装VendorStorageManager访问(4)-CSDN博客
Rockchip 自定义vendorstorages数据再u-boot通过cmdline给kernel传递数据_一歲抬頭的博客-CSDN博客
VendorStorage是用于存储SN, MAC, LAN, BT等vendor data的一个功能,它具有以下特点:
- 唯一的访问ID
- 可靠的数据验证
- 掉电不会丢失
- 系统启动各个阶段都可访问
- PC端可读写
- Kernel可读写
- Linux Application可读写
VendorStorage的数据结构
VendorStorage的头部结构包含标签,版本,索引,项目数,空闲偏移和空闲大小,其定义如下:
struct vendor_hdr { u32 tag; u32 version; u16 next_index; u16 item_num; u16 free_offset; /* Free space offset */ u16 free_size; /* Free space size */ };
VendorStorage的信息结构包含头部,项目,数据,哈希和版本2。版本2和头部的版本一起用于确保当前Vendor块内容的完整性,其定义如下:
struct vendor_info { struct vendor_hdr * hdr; struct vendor_item * item; u8 * data; u32 * hash; u32 * version2; };
VendorStorage的ID用于区分不同的项目,其定义如下:
#define VENDOR_SN_ID 1 /* serialno */ #define VENDOR_WIFI_MAC_ID 2 /* wifi mac */ #define VENDOR_LAN_MAC_ID 3 /* lan mac */ #define VENDOR_BLUETOOTH_ID 4 /* bluetooth mac */ //..........
VendorStorage在uboot, kernel和user不同阶段的驱动文件和接口
阶段 |
驱动文件 |
初始化接口 |
读写接口 |
uboot |
vendor.c |
vendor_storage_init() |
vendor_storage_read()/vendor_storage_write() |
kernel |
sdmmc_vendor_storage.c和rk_vendor_storage.c |
emmc_vendor_storage_init() |
vendor_storage_ioctl()或rk_vendor_register() |
user |
vendorlib.c和vendor_test.c |
无 |
vendor_read()/vendor_write() |
uboot阶段
驱动文件
文件路径:/u-boot/arch/arm/mach-rockchip/vendor.c
初始化接口
vendor_storage_init()
在Uboot启动阶段,VendorStorage会被初始化,具体实现在vendor_storage_init()函数中。此函数主要的功能是在Uboot启动时,读取VendorStorage中的数据,检查数据的完整性,并加载到内存中,供系统中的其他部分使用。
读写接口
vendor_storage_read()/vendor_storage_write()
在Uboot阶段,VendorStorage的读写由vendor_storage_read()和vendor_storage_write()函数实现。例如,rockchip_set_ethaddr()函数就是通过vendor_storage_read()和vendor_storage_write()读取和更新以太网地址。
接口调用位置参考如下:
... static int rockchip_set_ethaddr(void) { #ifdef CONFIG_ROCKCHIP_VENDOR_PARTITION char buf[ARP_HLEN_ASCII + 1], mac[16]; u8 ethaddr[ARP_HLEN * MAX_ETHERNET] = {0}; int ret, i; bool need_write = false, randomed = false; ret = vendor_storage_read(LAN_MAC_ID, ethaddr, sizeof(ethaddr)); for (i = 0; i < MAX_ETHERNET; i++) { if (ret <= 0 || !is_valid_ethaddr(ðaddr[i * ARP_HLEN])) { if (!randomed) { net_random_ethaddr(ðaddr[i * ARP_HLEN]); randomed = true; } else { if (i > 0) { memcpy(ðaddr[i * ARP_HLEN], ðaddr[(i - 1) * ARP_HLEN], ARP_HLEN); ethaddr[i * ARP_HLEN] |= 0x02; ethaddr[i * ARP_HLEN] += (i << 2); } } need_write = true; } if (is_valid_ethaddr(ðaddr[i * ARP_HLEN])) { sprintf(buf, "%pM", ðaddr[i * ARP_HLEN]); if (i == 0) memcpy(mac, "ethaddr", sizeof("ethaddr")); else sprintf(mac, "eth%daddr", i); env_set(mac, buf); } } if (need_write) { ret = vendor_storage_write(LAN_MAC_ID, ethaddr, sizeof(ethaddr)); if (ret < 0) printf("%s: vendor_storage_write failed %d\n", __func__, ret); } #endif return 0; } ...
kernel阶段
驱动文件
文件路径:
- /kernel/drivers/soc/rockchip/sdmmc_vendor_storage.c
- /kernel/drivers/soc/rockchip/rk_vendor_storage.c
初始化接口
emmc_vendor_storage_init()
在内核启动阶段,VendorStorage的初始化由emmc_vendor_storage_init()函数完成。此函数的功能类似于Uboot阶段的vendor_storage_init(),也是读取VendorStorage中的数据,检查数据的完整性,并加载到内存中。
读写接口
vendor_storage_ioctl()或rk_vendor_register()
在内核阶段,VendorStorage的读写操作由vendor_storage_ioctl()和rk_vendor_register()函数完成。例如,get_wifi_addr_vendor()函数就是通过rk_vendor_read()读取WIFI MAC地址,如果读取失败或者MAC地址无效,会生成随机的MAC地址并通过rk_vendor_write()写入。
接口调用参考如下:
/* rfkill-wlan.c file */ ... static int get_wifi_addr_vendor(unsigned char *addr) { int ret; int count = 5; while (count-- > 0) { if (is_rk_vendor_ready()) break; /* sleep 500ms wait rk vendor driver ready */ msleep(500); } ret = rk_vendor_read(WIFI_MAC_ID, addr, 6); if (ret != 6 || is_zero_ether_addr(addr)) { LOG("%s: rk_vendor_read wifi mac address failed (%d)\n", __func__, ret); #ifdef CONFIG_WIFI_GENERATE_RANDOM_MAC_ADDR random_ether_addr(addr); LOG("%s: generate random wifi mac address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", __func__, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); ret = rk_vendor_write(WIFI_MAC_ID, addr, 6); if (ret != 0) { LOG("%s: rk_vendor_write failed %d\n" __func__, ret); memset(addr, 0, 6); return -1; } #else return -1; #endif } else { LOG("%s: rk_vendor_read wifi mac address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", __func__, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } return 0; } ...
用户空间阶段
驱动文件
调用接口位置文件路径:/hardware/rockchip/drmservice/test/vendor_storage_test.c
此文件定义了一个简单的用户空间应用,用于测试VendorStorage的读写接口。
读写接口
vendor_read()/vendor_write()
用户空间应用通过打开设备文件/dev/vendor_storage
,然后通过ioctl系统调用调用VENDOR_READ_IO和VENDOR_WRITE_IO命令来读写VendorStorage。例如,vendor_storage_read_sn()函数和vendor_storage_write_sn()函数分别实现了读取和写入序列号的功能。
示例代码如下:
/* vendor_storage_test.c file */ #include <errno.h> #include <fcntl.h> #include <log/log.h> #include <string.h> #include <stdio.h> #include <sys/ioctl.h> #include <unistd.h> #define LOG_TAG "VENDOR_STORAGE_TEST" #define SERIALNO_BUF_LEN 33 #define RKNAND_SYS_STORGAE_DATA_LEN 512 #define DEBUG_LOG 1 //open debug info #define VENDOR_REQ_TAG 0x56524551 #define VENDOR_READ_IO _IOW('v', 0x01, unsigned int) #define VENDOR_WRITE_IO _IOW('v', 0x02, unsigned int) #define VENDOR_SN_ID 1 #define VENDOR_WIFI_MAC_ID 2 #define VENDOR_LAN_MAC_ID 3 #define VENDOR_BLUETOOTH_ID 4 typedef unsigned short uint16; typedef unsigned long uint32; typedef unsigned char uint8; static char sn_buf_idb[SERIALNO_BUF_LEN] = {0}; struct rk_vendor_req { uint32 tag; uint16 id; uint16 len; uint8 data[RKNAND_SYS_STORGAE_DATA_LEN]; }; void dump_hex_data(const char *s,uint8 *buf,uint32 len) { SLOGE("%s",s); for(int i=0;i<len;i+=4) { SLOGE("0x%x 0x%x 0x%x 0x%x",buf[i],buf[i+1],buf[i+2],buf[i+3]); } } int vendor_storage_read_sn(void) { int ret ; uint16 len; struct rk_vendor_req req; memset(sn_buf_idb,0,sizeof(sn_buf_idb)); int sys_fd = open("/dev/vendor_storage",O_RDONLY,0); if(sys_fd < 0){ SLOGE("vendor_storage open fail %s\n", strerror(errno)); return -1; } req.tag = VENDOR_REQ_TAG; req.id = VENDOR_SN_ID; req.len = RKNAND_SYS_STORGAE_DATA_LEN; /* max read length to read*/ ret = ioctl(sys_fd, VENDOR_READ_IO, &req); close(sys_fd); if (DEBUG_LOG) dump_hex_data("vendor read:", req.data, req.len/4 + 3); /* return req->len is the real data length stored in the NV-storage */ if(ret){ SLOGE("vendor read error\n"); return -1; } //get the sn length len = req.len; if(len > 30) { len =30; } if(len <= 0) { SLOGE("vendor read error, len = 0\n"); } memcpy(sn_buf_idb,req.data,len); if (DEBUG_LOG) SLOGD("vendor read sn_buf_idb:%s\n",sn_buf_idb); return 0; } int vendor_storage_write_sn(const char* sn) { if (DEBUG_LOG) SLOGD("save SN: %s to IDB.\n", sn); int ret ; struct rk_vendor_req req; int sys_fd = open("/dev/vendor_storage",O_RDONLY,0); if(sys_fd < 0){ SLOGE("vendor_storage open fail %s\n", strerror(errno)); return -1; } memset(&req, 0, sizeof(req)); req.tag = VENDOR_REQ_TAG; req.id = VENDOR_SN_ID; req.len = strlen(sn); memcpy(req.data, sn, strlen(sn)); if (DEBUG_LOG) dump_hex_data("vendor write:", req.data, req.len/4+3); ret = ioctl(sys_fd, VENDOR_WRITE_IO, &req); close(sys_fd); if(ret){ SLOGE("error in saving SN to IDB.\n"); return -1; } return 0; } /** * Program entry pointer * * @return 0 for success, -1 for SLOGE */ int main( int argc, char *argv[] ) { SLOGD("----------------running vendor storage test---------------"); if (argc > 1) { const char* vendor_sn = argv[1]; vendor_storage_write_sn(vendor_sn); } else vendor_storage_read_sn(); return 0; }
参考链接
- [RK3288] Vendor Storage区域知识及探讨 - 陌鉎こ城sHi - 博客园
- [Android O] [RK3399] -- Vendor Storage 功能探究 - CSDN博客
本篇文章将详细介绍如何在 Uboot,Kernel 和 User Space 阶段使用 VendorStorage。
在嵌入式系统的开发中,有时我们需要存储一些设备相关的信息,如序列号,MAC地址等。VendorStorage 是这样一个机制,它允许我们在固定的存储区域内保存和读取这些设备相关的信息。