Rockchip系列之VendorStorage uboot/kernel/user space 阶段接口使用介绍(2)

简介: Rockchip系列之VendorStorage uboot/kernel/user space 阶段接口使用介绍(2)

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(&ethaddr[i * ARP_HLEN])) {
      if (!randomed) {
        net_random_ethaddr(&ethaddr[i * ARP_HLEN]);
        randomed = true;
      } else {
        if (i > 0) {
          memcpy(&ethaddr[i * ARP_HLEN],
                 &ethaddr[(i - 1) * ARP_HLEN],
                 ARP_HLEN);
          ethaddr[i * ARP_HLEN] |= 0x02;
          ethaddr[i * ARP_HLEN] += (i << 2);
        }
      }
 
      need_write = true;
    }
 
    if (is_valid_ethaddr(&ethaddr[i * ARP_HLEN])) {
      sprintf(buf, "%pM", &ethaddr[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;
}

参考链接

  1. [RK3288] Vendor Storage区域知识及探讨 - 陌鉎こ城sHi - 博客园
  2. [Android O] [RK3399] -- Vendor Storage 功能探究 - CSDN博客

本篇文章将详细介绍如何在 Uboot,Kernel 和 User Space 阶段使用 VendorStorage。

在嵌入式系统的开发中,有时我们需要存储一些设备相关的信息,如序列号,MAC地址等。VendorStorage 是这样一个机制,它允许我们在固定的存储区域内保存和读取这些设备相关的信息。

相关文章
|
6月前
|
编解码 Shell 文件存储
Rockchip saveBaseParameter程序来设置显示器参数
Rockchip saveBaseParameter程序来设置显示器参数
372 50
|
6月前
|
Linux Android开发
AVB源码学习(六):AVB2.0 Device Mapper和Dm verity详解
AVB源码学习(六):AVB2.0 Device Mapper和Dm verity详解
477 0
|
6月前
|
存储 芯片
Rockchip 自定义vendorstorages数据再u-boot通过cmdline给kernel传递数据
Rockchip 自定义vendorstorages数据再u-boot通过cmdline给kernel传递数据
289 1
|
Linux
【Linux】关于Bad magic number in super-block 当尝试打开/dev/sda1 时找不到有效的文件系统超级块
【Linux】关于Bad magic number in super-block 当尝试打开/dev/sda1 时找不到有效的文件系统超级块
401 0
|
Unix Linux 内存技术
Buildroot系列开发(七)block device9(上)
Buildroot系列开发(七)block device
126 1
Buildroot系列开发(七)block device9(上)
|
Linux
LINUX虚拟机安装增强功能时报错: Kernel headers not found for target kernel. Please install them and execute
LINUX虚拟机安装增强功能时报错: Kernel headers not found for target kernel. Please install them and execute
297 0
|
内存技术
嵌入式 VFS: Cannot open root device "mtdblock2" or unknown-block(2,0)
系统启动后,虽然nand驱动表现正常,但是最后挂载rootfs时候出错: Kernel command line: root=/dev/mtdblock2 rw init=/linuxrc console=ttyAMA1,115200 mem=64M rootfstype=yaffs2。
2267 0