[Android O] [RK3399] -- Vendor Storage 功能探究

前言:

        Vendor storage 被设计用来存储 SN,MAC,LAN,BT 等 vendor data。

        特征:

  • 唯一的访问 ID;
  • 可靠的数据验证;
  • 掉电不会丢失;
  • 系统启动各个阶段都可访问;
  • PC 端可读写;
  • Kernel 可读写;
  • Linux Application 可读写;

        如下图:

[Android O] [RK3399] -- Vendor Storage 功能探究_第1张图片

         Data Layout:

[Android O] [RK3399] -- Vendor Storage 功能探究_第2张图片

        也就是说 vendor storage 是从 eMMC 3.5M 之后开始存放,每个元素是 64K,一共 4 块总共 256K。

        系统一共把 vendor 的存储块分成 4 个分区,vendor0、vendor1、vendor2、vendor3。每个 vendorX 的 hdr 里都有一个单调递增的 Version 字段用于表示 vendorX 被更新的时刻点。每次读操作只读取新的 vendorX(即 vendor 大),写操作的时候会更新 Version、并且把整个原有信息和新增信息搬移到下一个 vendor 分区里。例如当前 从 vendor2读取到信息,经过修改后再回写,此时写入的是 vendor3。这样做只是为了起到一个简单的安全防护作用。

         存储数据结构:

[Android O] [RK3399] -- Vendor Storage 功能探究_第3张图片

         在代码中用 struct vendor_info 表示:

struct vendor_info {
	u32	tag;
	u32	version;
	u16	next_index;
	u16	item_num;
	u16	free_offset;
	u16	free_size;
	struct	vendor_item item[126]; /* 126 * 8*/
	u8	data[EMMC_VENDOR_PART_SIZE * 512 - 1024 - 8];
	u32	hash;
	u32	version2;
};

        各个阶段的驱动文件及接口如下, 两个阶段的初始化以及读写接口的本质实现是一样的。

        uboot:
        驱动文件: storage.c
        初始化接口: vendor_storage_init();
        读写接口: vendor_storage_read()/vendor_storage_write();

        接口会在rockusb也就是loader模式被使用,可查看rkusb_handle_datatx()以及rkusb_handle_datarx()两个函数。

        kernel:
        驱动文件: sdmmc_vendor_storage.c和rk_vendor_storage.c
        初始化接口:emmc_vendor_storage_init(), 它被放在线程vendor_init_thread()中实现是因为
        1.不能阻塞kernel驱动初始化过程
        2.需要等待eMMC初始化完成。

        接口一方面提供给用户空间使用,通过vendor_storage_ioctl()接口实现。
        另外读写函数也被注册为公共接口(通过rk_vendor_register()实现),供kernel使用,比如wifi模块的get_wifi_addr_vendor()会去读写Wifi MAC ID.

用户空间:

        在 /system/eyeknowproj/vendoroper/ 目录下,有我们的项目源码:

        Android.mk:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := vendorlib.c
LOCAL_SHARED_LIBRARIES := liblog libm libc libprocessgroup
LOCAL_CFLAGS := -Werror
LOCAL_MODULE := libvendoroper
LOCAL_C_INCLUDES := $(LOCAL_PATH)/
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_SRC_FILES := vendor_test.c
LOCAL_SHARED_LIBRARIES := libvendoroper liblog libm libc libprocessgroup
LOCAL_CFLAGS := -Werror
LOCAL_MODULE := vendor_test
LOCAL_C_INCLUDES := $(LOCAL_PATH)/
include $(BUILD_EXECUTABLE)

        vendorlib.c:

#include "vendorlib.h"
#include 
#include 
#include 
#include 
#include 


int vendor_read(enum VENDOR_ID vid,char* buf,int len)
{
	int ret;
	uint8 p_buf[sizeof(struct rk_vendor_req)+64];
	struct rk_vendor_req *req;
	req = (struct rk_vendor_req *)p_buf;
	int sys_fd = open("/dev/vendor_storage",O_RDWR,0); 
	if(sys_fd < 0)
	{
		printf("vendor storage open fail");
		return -1;
	}
	req->tag = VENDOR_REQ_TAG;
	req->id = vid;
	
	req->len = len;
        ret = ioctl(sys_fd, VENDOR_READ_IO, req);
        if (!ret) {
            memcpy(buf, req->data, len);
        }
	close(sys_fd);
	return 0;
}
int vendor_write(enum VENDOR_ID vid,char* buf,int len)
{
        int ret;
	uint8 p_buf[sizeof(struct rk_vendor_req)+64];
	struct rk_vendor_req *req;
	int i;
        int sys_fd = open("/dev/vendor_storage",O_RDWR,0); 
	req = (struct rk_vendor_req *)p_buf;
        if(sys_fd < 0)
        {
                printf("vendor storage open fail");
                return -1;
        }
        req->tag = VENDOR_REQ_TAG;
        req->id = vid;
        req->len = len;
        for (int i = 0; i < len; i++)
            req->data[i] = buf[i];
        ret = ioctl(sys_fd, VENDOR_WRITE_IO, req);
        if (ret) {
            printf("vendor write fail\n");
	    return -1;
        }
        close(sys_fd);
	return 0;
}

        vendorlib.h:

#include 

typedef     unsigned short      uint16;
typedef     unsigned int       uint32;
typedef     unsigned char       uint8;

#define VENDOR_REQ_TAG      0x56524551
#define VENDOR_READ_IO      _IOW('v', 0x01, unsigned int)
#define VENDOR_WRITE_IO     _IOW('v', 0x02, unsigned int)

enum VENDOR_ID 
{
	VENDOR_SN_ID = 1,
	VENDOR_WIFI_MAC_ID = 2,
	VENDOR_LAN_MAC_ID = 3,
	VENDOR_BLUETOOTH_ID = 4,
	VENDOR_USER1 = 16,
	VENDOR_USER2 = 17,
};

#define VENDOR_USER_LENGTH 32
struct rk_vendor_req {
    uint32 tag;
    uint16 id;
    uint16 len;
    uint8 data[1];
};
int vendor_read(enum VENDOR_ID vid,char* buf,int len);
int vendor_write(enum VENDOR_ID vid,char* buf,int len);

        vendor_test.c:

#include "vendorlib.h"
#include 
#include 
#include 
#include 
#include 

int main()
{

	char temp[64] = {'k','e','l','l','a','n','d','s',};
	char temp1[64] = {};
	vendor_write(VENDOR_USER1,temp,VENDOR_USER_LENGTH);
	vendor_read(VENDOR_USER1,temp1,VENDOR_USER_LENGTH);
	printf("%c,%c,%c,%c,%c,%c,%c\n",temp1[0],temp1[1],temp1[2],temp1[3],temp1[4],temp1[5],temp1[6]);
	return 0;
}

小结:

  1. 由于 Vendor Storage 位于 eMMC 上,所以做擦除的时候也会一并丢失;
  2. 重新烧写固件或者擦除 idb 是不会丢失的。
  3. 工具中目前是不支持添加新 id 的,如果要添加,那就要改工具源代码,或者直接通过驱动提供给用户空间的接口来添加新 id。

参考:

Rockchip Vendor Storage Application Note.pdf
Rockchip-Developer-Guide-UBoot-nextdev.pdf

[RK3399][Android7.1] Vendor Storage区域知识及探讨_Kris Fei's blog-CSDN博客

你可能感兴趣的:(AndroidO,RK3399,android,vendor,storage)