看到rk有篇文档是介绍vendor storage,用于存储SN, MAC, LAN,BT等data,主要特性是不会丢失以及系统启动各个阶段都可以访问,包括uboot, kernel, linux用户空间以及pc端,如下图

(出自Rockchip Vendor Storage Application Note.pdf)

 

 

 

存储数据结构


 

 

 在代码中用struct vendor_info表示:(路径u-boot\arch\arm\mach-rockchip\vendor.c)

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 */
};

/*
 * Different types of Flash vendor info are different.
 * EMMC:EMMC_VENDOR_PART_BLKS * BLOCK_SIZE(512) = 64KB;
 * Spi Nor/Spi Nand/SLC/MLC: FLASH_VENDOR_PART_BLKS *
 * BLOCK_SIZE(512) = 4KB.
 * hash: For future expansion.
 * version2: Together with hdr->version, it is used to
 * ensure the current Vendor block content integrity.
 *   (version2 == hdr->version):Data valid;
 *   (version2 != hdr->version):Data invalid.
 */
struct vendor_info {
    struct vendor_hdr *hdr;
    struct vendor_item *item;
    u8 *data;
    u32 *hash;
    u32 *version2;
};

id的定义如下:

 

 

 (路径u-boot\arch\arm\include\asm\arch\vendor.h)

#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 */

 

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

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

 

u-boot\arch\arm\mach-rockchip\vendor.c

u-boot\arch\arm\include\asm\arch\vendor.h

/*
 * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
 *
 * SPDX-License-Identifier:    GPL-2.0+
 */

#include <common.h>
#include <malloc.h>
#include <asm/arch/vendor.h>
#include <boot_rkimg.h>

/* tag for vendor check */
#define VENDOR_TAG        0x524B5644
/* The Vendor partition contains the number of Vendor blocks */
#define NAND_VENDOR_PART_NUM    2
#define VENDOR_PART_NUM        4
/* align to 64 bytes */
#define VENDOR_BTYE_ALIGN    0x3F
#define VENDOR_BLOCK_SIZE    512

/* --- Emmc define --- */
/* Starting address of the Vendor in memory. */
#define EMMC_VENDOR_PART_OFFSET        (1024 * 7)
/*
 * The number of memory blocks used by each
 * Vendor structure(128 * 512B = 64KB)
 */
#define EMMC_VENDOR_PART_BLKS        128
/* The maximum number of items in each Vendor block */
#define EMMC_VENDOR_ITEM_NUM        126

/* --- Spi Nand/SLC/MLC large capacity case define --- */
/* The Vendor partition contains the number of Vendor blocks */
#define NAND_VENDOR_PART_OFFSET        0
/*
 * The number of memory blocks used by each
 * Vendor structure(8 * 512B = 4KB)
 */
#define NAND_VENDOR_PART_BLKS        128
/* The maximum number of items in each Vendor block */
#define NAND_VENDOR_ITEM_NUM        126

/* --- Spi/Spi Nand/SLC/MLC small capacity case define --- */
/* The Vendor partition contains the number of Vendor blocks */
#define    FLASH_VENDOR_PART_OFFSET    8
/*
 * The number of memory blocks used by each
 * Vendor structure(8 * 512B = 4KB)
 */
#define FLASH_VENDOR_PART_BLKS        8
/* The maximum number of items in each Vendor block */
#define FLASH_VENDOR_ITEM_NUM        62

/* Vendor uinit test define */
int vendor_storage_test(void);

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 */
};

/*
 * Different types of Flash vendor info are different.
 * EMMC:EMMC_VENDOR_PART_BLKS * BLOCK_SIZE(512) = 64KB;
 * Spi Nor/Spi Nand/SLC/MLC: FLASH_VENDOR_PART_BLKS *
 * BLOCK_SIZE(512) = 4KB.
 * hash: For future expansion.
 * version2: Together with hdr->version, it is used to
 * ensure the current Vendor block content integrity.
 *   (version2 == hdr->version):Data valid;
 *   (version2 != hdr->version):Data invalid.
 */
struct vendor_info {
    struct vendor_hdr *hdr;
    struct vendor_item *item;
    u8 *data;
    u32 *hash;
    u32 *version2;
};

/*
 * Calculate the offset of each field for emmc.
 * Emmc vendor info size: 64KB
 */
#define EMMC_VENDOR_INFO_SIZE    (EMMC_VENDOR_PART_BLKS * VENDOR_BLOCK_SIZE)
#define EMMC_VENDOR_DATA_OFFSET    (sizeof(struct vendor_hdr) + EMMC_VENDOR_ITEM_NUM * sizeof(struct vendor_item))
#define EMMC_VENDOR_HASH_OFFSET (EMMC_VENDOR_INFO_SIZE - 8)
#define EMMC_VENDOR_VERSION2_OFFSET (EMMC_VENDOR_INFO_SIZE - 4)

/*
 * Calculate the offset of each field for spi nand/slc/mlc large capacity case.
 * Flash vendor info size: 4KB
 */
#define NAND_VENDOR_INFO_SIZE    (NAND_VENDOR_PART_BLKS * VENDOR_BLOCK_SIZE)
#define NAND_VENDOR_DATA_OFFSET    (sizeof(struct vendor_hdr) + NAND_VENDOR_ITEM_NUM * sizeof(struct vendor_item))
#define NAND_VENDOR_HASH_OFFSET (NAND_VENDOR_INFO_SIZE - 8)
#define NAND_VENDOR_VERSION2_OFFSET (NAND_VENDOR_INFO_SIZE - 4)

/*
 * Calculate the offset of each field for spi nor/spi nand/slc/mlc large small capacity case.
 * Flash vendor info size: 4KB
 */
#define FLASH_VENDOR_INFO_SIZE    (FLASH_VENDOR_PART_BLKS * VENDOR_BLOCK_SIZE)
#define FLASH_VENDOR_DATA_OFFSET (sizeof(struct vendor_hdr) + FLASH_VENDOR_ITEM_NUM * sizeof(struct vendor_item))
#define FLASH_VENDOR_HASH_OFFSET (FLASH_VENDOR_INFO_SIZE - 8)
#define FLASH_VENDOR_VERSION2_OFFSET (FLASH_VENDOR_INFO_SIZE - 4)

/* vendor info */
static struct vendor_info vendor_info;
/* The storage type of the device */
static int bootdev_type;

/* vendor private read write ops*/
static    int (*_flash_read)(struct blk_desc *dev_desc,
               u32 sec,
               u32 n_sec,
               void *buffer);
static    int (*_flash_write)(struct blk_desc *dev_desc,
                u32 sec,
                u32 n_sec,
                void *buffer);

int flash_vendor_dev_ops_register(int (*read)(struct blk_desc *dev_desc,
                          u32 sec,
                          u32 n_sec,
                          void *p_data),
                  int (*write)(struct blk_desc *dev_desc,
                           u32 sec,
                           u32 n_sec,
                           void *p_data))
{
    if (!_flash_read) {
        _flash_read = read;
        _flash_write = write;
        return 0;
    }

    return -EPERM;
}

/**********************************************************/
/*              vendor API implementation                 */
/**********************************************************/
static int vendor_ops(u8 *buffer, u32 addr, u32 n_sec, int write)
{
    struct blk_desc *dev_desc;
    unsigned int lba = 0;
    int ret = 0;

    dev_desc = rockchip_get_bootdev();
    if (!dev_desc) {
        printf("%s: dev_desc is NULL!\n", __func__);
        return -ENODEV;
    }
    /* Get the offset address according to the device type */
    switch (dev_desc->if_type) {
    case IF_TYPE_MMC:
        /*
         * The location of VendorStorage in Flash is shown in the
         * following figure. The starting address of the VendorStorage
         * partition offset is 3.5MB(EMMC_VENDOR_PART_OFFSET*BLOCK_SIZE(512)),
         * and the partition size is 256KB.
         * ----------------------------------------------------
         * |   3.5MB    |  VendorStorage  |                   |
         * ----------------------------------------------------
         */
        lba = EMMC_VENDOR_PART_OFFSET;
        debug("[Vendor INFO]:VendorStorage offset address=0x%x\n", lba);
        break;
    case IF_TYPE_RKNAND:
    case IF_TYPE_SPINAND:
        /*
         * The location of VendorStorage in Flash is shown in the
         * following figure. The starting address of the VendorStorage
         * partition offset is 0KB in FTL vendor block,
         * and the partition size is 128KB.
         * ----------------------------------------------------
         * |  VendorStorage  |                     |
         * ----------------------------------------------------
         */
        lba = NAND_VENDOR_PART_OFFSET;
        debug("[Vendor INFO]:VendorStorage offset address=0x%x\n", lba);
        break;
    case IF_TYPE_SPINOR:
        /*
         * The location of VendorStorage in Flash is shown in the
         * following figure. The starting address of the VendorStorage
         * partition offset is 4KB (FLASH_VENDOR_PART_OFFSET * BLOCK_SIZE),
         * and the partition size is 16KB.
         * ----------------------------------------------------
         * |   4KB    |  VendorStorage  |                     |
         * ----------------------------------------------------
         */
        lba = FLASH_VENDOR_PART_OFFSET;
        debug("[Vendor INFO]:VendorStorage offset address=0x%x\n", lba);
        break;
    default:
        printf("[Vendor ERROR]:Boot device type is invalid!\n");
        return -ENODEV;
    }
    if (write) {
        if (_flash_write)
            ret = _flash_write(dev_desc, lba + addr, n_sec, buffer);
        else
            ret = blk_dwrite(dev_desc, lba + addr, n_sec, buffer);
    } else {
        if (_flash_read)
            ret = _flash_read(dev_desc, lba + addr, n_sec, buffer);
        else
            ret = blk_dread(dev_desc, lba + addr, n_sec, buffer);
    }

    debug("[Vendor INFO]:op=%s, ret=%d\n", write ? "write" : "read", ret);

    return ret;
}

/*
 * The VendorStorage partition is divided into four parts
 * (vendor 0-3) and its structure is shown in the following figure.
 * The init function is used to select the latest and valid vendor.
 *
 * |******************** FLASH ********************|
 * -------------------------------------------------
 * |  vendor0  |  vendor1  |  vendor2  |  vendor3  |
 * -------------------------------------------------
 * Notices:
 *   1. "version" and "version2" are used to verify that the vendor
 *      is valid (equal is valid).
 *   2. the "version" value is larger, indicating that the current
 *      verndor data is new.
 */
int vendor_storage_init(void)
{
    int ret = 0;
    int ret_size;
    u8 *buffer;
    u32 size, i;
    u32 max_ver = 0;
    u32 max_index = 0;
    u16 data_offset, hash_offset, part_num;
    u16 version2_offset, part_size;
    struct blk_desc *dev_desc;

    dev_desc = rockchip_get_bootdev();
    if (!dev_desc) {
        printf("[Vendor ERROR]:Invalid boot device type(%d)\n",
               bootdev_type);
        return -ENODEV;
    }

    switch (dev_desc->if_type) {
    case IF_TYPE_MMC:
        size = EMMC_VENDOR_INFO_SIZE;
        part_size = EMMC_VENDOR_PART_BLKS;
        data_offset = EMMC_VENDOR_DATA_OFFSET;
        hash_offset = EMMC_VENDOR_HASH_OFFSET;
        version2_offset = EMMC_VENDOR_VERSION2_OFFSET;
        part_num = VENDOR_PART_NUM;
        break;
    case IF_TYPE_RKNAND:
    case IF_TYPE_SPINAND:
        size = NAND_VENDOR_INFO_SIZE;
        part_size = NAND_VENDOR_PART_BLKS;
        data_offset = NAND_VENDOR_DATA_OFFSET;
        hash_offset = NAND_VENDOR_HASH_OFFSET;
        version2_offset = NAND_VENDOR_VERSION2_OFFSET;
        part_num = NAND_VENDOR_PART_NUM;
        break;
    case IF_TYPE_SPINOR:
        size = FLASH_VENDOR_INFO_SIZE;
        part_size = FLASH_VENDOR_PART_BLKS;
        data_offset = FLASH_VENDOR_DATA_OFFSET;
        hash_offset = FLASH_VENDOR_HASH_OFFSET;
        version2_offset = FLASH_VENDOR_VERSION2_OFFSET;
        part_num = VENDOR_PART_NUM;
        break;
    default:
        debug("[Vendor ERROR]:Boot device type is invalid!\n");
        ret = -ENODEV;
        break;
    }
    /* Invalid bootdev type */
    if (ret)
        return ret;

    /* Initialize */
    bootdev_type = dev_desc->if_type;

    /* Always use, no need to release */
    buffer = (u8 *)malloc(size);
    if (!buffer) {
        printf("[Vendor ERROR]:Malloc failed!\n");
        return -ENOMEM;
    }
    /* Pointer initialization */
    vendor_info.hdr = (struct vendor_hdr *)buffer;
    vendor_info.item = (struct vendor_item *)(buffer + sizeof(struct vendor_hdr));
    vendor_info.data = buffer + data_offset;
    vendor_info.hash = (u32 *)(buffer + hash_offset);
    vendor_info.version2 = (u32 *)(buffer + version2_offset);

    /* Find valid and up-to-date one from (vendor0 - vendor3) */
    for (i = 0; i < part_num; i++) {
        ret_size = vendor_ops((u8 *)vendor_info.hdr,
                      part_size * i, part_size, 0);
        if (ret_size != part_size) {
            ret = -EIO;
            goto out;
        }

        if ((vendor_info.hdr->tag == VENDOR_TAG) &&
            (*(vendor_info.version2) == vendor_info.hdr->version)) {
            if (max_ver < vendor_info.hdr->version) {
                max_index = i;
                max_ver = vendor_info.hdr->version;
            }
        }
    }

    if (max_ver) {
        debug("[Vendor INFO]:max_ver=%d, vendor_id=%d.\n", max_ver, max_index);
        /*
         * Keep vendor_info the same as the largest
         * version of vendor
         */
        if (max_index != (part_num - 1)) {
            ret_size = vendor_ops((u8 *)vendor_info.hdr,
                           part_size * max_index, part_size, 0);
            if (ret_size != part_size) {
                ret = -EIO;
                goto out;
            }
        }
    } else {
        debug("[Vendor INFO]:Reset vendor info...\n");
        memset((u8 *)vendor_info.hdr, 0, size);
        vendor_info.hdr->version = 1;
        vendor_info.hdr->tag = VENDOR_TAG;
        /* data field length */
        vendor_info.hdr->free_size =
            ((u32)(size_t)vendor_info.hash
            - (u32)(size_t)vendor_info.data);
        *(vendor_info.version2) = vendor_info.hdr->version;
    }
    debug("[Vendor INFO]:ret=%d.\n", ret);

out:
    return ret;
}

/*
 * @id: item id, first 4 id is occupied:
 *    VENDOR_SN_ID
 *    VENDOR_WIFI_MAC_ID
 *    VENDOR_LAN_MAC_ID
 *    VENDOR_BLUETOOTH_ID
 * @pbuf: read data buffer;
 * @size: read bytes;
 *
 * return: bytes equal to @size is success, other fail;
 */
int vendor_storage_read(u16 id, void *pbuf, u16 size)
{
    int ret = 0;
    u32 i;
    u16 offset;
    struct vendor_item *item;

    /* init vendor storage */
    if (!bootdev_type) {
        ret = vendor_storage_init();
        if (ret < 0)
            return ret;
    }

    item = vendor_info.item;
    for (i = 0; i < vendor_info.hdr->item_num; i++) {
        if ((item + i)->id == id) {
            debug("[Vendor INFO]:Find the matching item, id=%d\n", id);
            /* Correct the size value */
            if (size > (item + i)->size)
                size = (item + i)->size;
            offset = (item + i)->offset;
            memcpy(pbuf, (vendor_info.data + offset), size);
            return size;
        }
    }
    debug("[Vendor ERROR]:No matching item, id=%d\n", id);

    return -EINVAL;
}

/*
 * @id: item id, first 4 id is occupied:
 *    VENDOR_SN_ID
 *    VENDOR_WIFI_MAC_ID
 *    VENDOR_LAN_MAC_ID
 *    VENDOR_BLUETOOTH_ID
 * @pbuf: write data buffer;
 * @size: write bytes;
 *
 * return: bytes equal to @size is success, other fail;
 */
int vendor_storage_write(u16 id, void *pbuf, u16 size)
{
    int cnt, ret = 0;
    u32 i, next_index, align_size;
    struct vendor_item *item;
    u16 part_size, max_item_num, offset, part_num;

    /* init vendor storage */
    if (!bootdev_type) {
        ret = vendor_storage_init();
        if (ret < 0)
            return ret;
    }

    switch (bootdev_type) {
    case IF_TYPE_MMC:
        part_size = EMMC_VENDOR_PART_BLKS;
        max_item_num = EMMC_VENDOR_ITEM_NUM;
        part_num = VENDOR_PART_NUM;
        break;
    case IF_TYPE_RKNAND:
    case IF_TYPE_SPINAND:
        part_size = NAND_VENDOR_PART_BLKS;
        max_item_num = NAND_VENDOR_ITEM_NUM;
        part_num = NAND_VENDOR_PART_NUM;
        break;
    case IF_TYPE_SPINOR:
        part_size = FLASH_VENDOR_PART_BLKS;
        max_item_num = FLASH_VENDOR_ITEM_NUM;
        part_num = VENDOR_PART_NUM;
        break;
    default:
        ret = -ENODEV;
        break;
    }
    /* Invalid bootdev? */
    if (ret < 0)
        return ret;

    next_index = vendor_info.hdr->next_index;
    /* algin to 64 bytes*/
    align_size = (size + VENDOR_BTYE_ALIGN) & (~VENDOR_BTYE_ALIGN);
    if (size > align_size)
        return -EINVAL;

    item = vendor_info.item;
    /* If item already exist, update the item data */
    for (i = 0; i < vendor_info.hdr->item_num; i++) {
        if ((item + i)->id == id) {
            debug("[Vendor INFO]:Find the matching item, id=%d\n", id);
            offset = (item + i)->offset;
            memcpy((vendor_info.data + offset), pbuf, size);
            (item + i)->size = size;
            vendor_info.hdr->version++;
            *(vendor_info.version2) = vendor_info.hdr->version;
            vendor_info.hdr->next_index++;
            if (vendor_info.hdr->next_index >= part_num)
                vendor_info.hdr->next_index = 0;
            cnt = vendor_ops((u8 *)vendor_info.hdr, part_size * next_index, part_size, 1);
            return (cnt == part_size) ? size : -EIO;
        }
    }
    /*
     * If item does not exist, and free size is enough,
     * creat a new one
     */
    if ((vendor_info.hdr->item_num < max_item_num) &&
        (vendor_info.hdr->free_size >= align_size)) {
        debug("[Vendor INFO]:Create new Item, id=%d\n", id);
        item = vendor_info.item + vendor_info.hdr->item_num;
        item->id = id;
        item->offset = vendor_info.hdr->free_offset;
        item->size = size;

        vendor_info.hdr->free_offset += align_size;
        vendor_info.hdr->free_size -= align_size;
        memcpy((vendor_info.data + item->offset), pbuf, size);
        vendor_info.hdr->item_num++;
        vendor_info.hdr->version++;
        vendor_info.hdr->next_index++;
        *(vendor_info.version2) = vendor_info.hdr->version;
        if (vendor_info.hdr->next_index >= part_num)
            vendor_info.hdr->next_index = 0;

        cnt = vendor_ops((u8 *)vendor_info.hdr, part_size * next_index, part_size, 1);
        return (cnt == part_size) ? size : -EIO;
    }
    debug("[Vendor ERROR]:Vendor has no space left!\n");

    return -ENOMEM;
}

/**********************************************************/
/*              vendor API uinit test                      */
/**********************************************************/
/* Reset the vendor storage space to the initial state */
static void vendor_test_reset(void)
{
    u16 i, part_size, part_num;
    u32 size;

    switch (bootdev_type) {
    case IF_TYPE_MMC:
        size = EMMC_VENDOR_INFO_SIZE;
        part_size = EMMC_VENDOR_PART_BLKS;
        part_num = VENDOR_PART_NUM;
        break;
    case IF_TYPE_RKNAND:
    case IF_TYPE_SPINAND:
        size = NAND_VENDOR_INFO_SIZE;
        part_size = NAND_VENDOR_PART_BLKS;
        part_num = NAND_VENDOR_PART_NUM;
        break;
    case IF_TYPE_SPINOR:
        size = FLASH_VENDOR_INFO_SIZE;
        part_size = FLASH_VENDOR_PART_BLKS;
        part_num = VENDOR_PART_NUM;
        break;
    default:
        size = 0;
        part_size = 0;
        break;
    }
    /* Invalid bootdev? */
    if (!size)
        return;

    memset((u8 *)vendor_info.hdr, 0, size);
    vendor_info.hdr->version = 1;
    vendor_info.hdr->tag = VENDOR_TAG;
    /* data field length */
    vendor_info.hdr->free_size = (unsigned long)vendor_info.hash -
                     (unsigned long)vendor_info.data;
    *(vendor_info.version2) = vendor_info.hdr->version;
    /* write to flash. */
    for (i = 0; i < part_num; i++)
        vendor_ops((u8 *)vendor_info.hdr, part_size * i, part_size, 1);
}

/*
 * A total of four tests
 * 1.All items test.
 * 2.Overrides the maximum number of items test.
 * 3.Single Item memory overflow test.
 * 4.Total memory overflow test.
 */
int vendor_storage_test(void)
{
    u16 id, size, j, item_num;
    u32 total_size;
    u8 *buffer = NULL;
    int ret = 0;

    if (!bootdev_type) {
        ret = vendor_storage_init();
        if (ret) {
            printf("%s: vendor storage init failed, ret=%d\n",
                   __func__, ret);
            return ret;
        }
    }

    /*
     * Calculate the maximum number of items and the maximum
     * allocable memory for each item.
     */
    switch (bootdev_type) {
    case IF_TYPE_MMC:
        item_num = EMMC_VENDOR_ITEM_NUM;
        total_size = (unsigned long)vendor_info.hash -
                 (unsigned long)vendor_info.data;
        size = total_size/item_num;
        break;
    case IF_TYPE_RKNAND:
    case IF_TYPE_SPINAND:
        item_num = NAND_VENDOR_ITEM_NUM;
        total_size = (unsigned long)vendor_info.hash -
                 (unsigned long)vendor_info.data;
        size = total_size/item_num;
        break;
    case IF_TYPE_SPINOR:
        item_num = FLASH_VENDOR_ITEM_NUM;
        total_size = (unsigned long)vendor_info.hash -
                 (unsigned long)vendor_info.data;
        size = total_size/item_num;
        break;
    default:
        total_size = 0;
        break;
    }
    /* Invalid bootdev? */
    if (!total_size)
        return -ENODEV;
    /* 64 bytes are aligned and rounded down */
    size = (size/64)*64;
    /* malloc memory */
    buffer = (u8 *)malloc(size);
    if (!buffer) {
        printf("[Vendor Test]:Malloc failed(size=%d)!\n", size);
        return -ENOMEM;
    }
    printf("[Vendor Test]:Test Start...\n");
    printf("[Vendor Test]:Before Test, Vendor Resetting.\n");
    vendor_test_reset();

    /* FIRST TEST: test all items can be used correctly */
    printf("[Vendor Test]:<All Items Used> Test Start...\n");
    printf("[Vendor Test]:item_num=%d, size=%d.\n",
           item_num, size);
    /*
     * Write data, then read the data, and compare the
     * data consistency
     */
    for (id = 0; id < item_num; id++) {
        memset(buffer, id, size);
        ret = vendor_storage_write(id, buffer, size);
        if (ret < 0) {
            printf("[Vendor Test]:vendor write failed(id=%d)!\n", id);
            free(buffer);
            return ret;
        }
    }
    /* Read data */
    for (id = 0; id < item_num; id++) {
        memset(buffer, 0, size);
        ret = vendor_storage_read(id, buffer, size);
        if (ret < 0) {
            printf("[Vendor Test]:vendor read failed(id=%d)!\n", id);
            free(buffer);
            return ret;
        }
        /* check data Correctness */
        for (j = 0; j < size; j++) {
            if (*(buffer + j) != id) {
                printf("[Vendor Test]:Unexpected error occurs(id=%d)\n", id);
                printf("the data content is:\n");
                print_buffer(0, buffer, 1, size, 16);

                free(buffer);
                return -1;
            }
        }
        debug("\t#id=%03d success,data=0x%02x,size=%d.\n", id, *buffer, size);
    }
    printf("[Vendor Test]:<All Items Used> Test End,States:OK\n");

    /*
     * SECOND TEST: Overrides the maximum number of items to see if the
     * return value matches the expectation
     */
    printf("[Vendor Test]:<Overflow Items Cnt> Test Start...\n");
    /* Any id value that was not used before */
    id = item_num;
    printf("[Vendor Test]:id=%d, size=%d.\n", id, size);
    ret = vendor_storage_write(id, buffer, size);
    if (ret == -ENOMEM)
        printf("[Vendor Test]:<Overflow Items Cnt> Test End,States:OK\n");
    else
        printf("[Vendor Test]:<Overflow Items Cnt> Test End,States:Failed\n");

    /* free buffer, remalloc later */
    free(buffer);
    buffer = NULL;
    /*
     * remalloc memory and recalculate size to test memory overflow
     * (1) item_num > 10: Memory is divided into 10 blocks,
     * 11th memory will overflow.
     * (2) 10 > item_num > 1: Memory is divided into item_num-1
     * blocks. item_num block, memory will overflow.
     * (3) item_num = 1: size = total_size + 512 Bytes, The first
     * block, memory will overflow.
     * The reason to do so is to minimize the size of the memory,
     * making malloc easier to perform successfully.
     */
    item_num = (item_num > 10) ? 10 : (item_num - 1);
    size = item_num ? (total_size / item_num) : (total_size + 512);
    size = (size + VENDOR_BTYE_ALIGN) & (~VENDOR_BTYE_ALIGN);
    /* Find item_num value that can make the memory overflow */
    for (id = 0; id <= item_num; id++) {
        if (((id + 1) * size) > total_size) {
            item_num = id;
            break;
        }
    }
    /* malloc */
    buffer = (u8 *)malloc(size);
    if (buffer == NULL) {
        printf("[Vendor Test]:Malloc failed(size=%d)!\n", size);
        return -ENOMEM;
    }

    /* THIRD TEST: Single Item memory overflow test */
    printf("[Vendor Test]:<Single Item Memory Overflow> Test Start...\n");
    /* The value can be arbitrary */
    memset(buffer, 'a', size);
    /* Any id value that was used before */
    id = 0;
    printf("[Vendor Test]:id=%d, size=%d.\n", id, size);
    ret = vendor_storage_write(id, buffer, size);
    if (ret == size)
        printf("[Vendor Test]:<Single Item Memory Overflow> Test End, States:OK\n");
    else
        printf("[Vendor Test]:<Single Item Memory Overflow> Test End, States:Failed\n");

    /* FORTH TEST: Total memory overflow test */
    printf("[Vendor Test]:<Total memory overflow> Test Start...\n");
    printf("[Vendor Test]:item_num=%d, size=%d.\n", item_num, size);

    vendor_test_reset();
    for (id = 0; id < item_num; id++) {
        memset(buffer, id, size);
        ret = vendor_storage_write(id, buffer, size);
        if (ret < 0) {
            if ((id == item_num) && (ret == -ENOMEM)) {
                printf("[Vendor Test]:<Total memory overflow> Test End, States:OK\n");
                break;
            } else {
                printf("[Vendor Test]:<Total memory overflow> Test End, States:Failed\n");
                break;
            }
        }
        debug("\t#id=%03d success,data=0x%02x,size=%d.\n", id, *buffer, size);
    }

    /* Test end */
    printf("[Vendor Test]:After Test, Vendor Resetting...\n");
    vendor_test_reset();
    printf("[Vendor Test]:Test End.\n");
    free(buffer);

    return 0;
}
vendor.C
/*
 * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd
 *
 * SPDX-License-Identifier:    GPL-2.0+
 */
#ifndef __ROCKCHIP_VENDOR_
#define __ROCKCHIP_VENDOR_

#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 */

struct vendor_item {
    u16  id;
    u16  offset;
    u16  size;
    u16  flag;
};

int vendor_storage_test(void);
int vendor_storage_read(u16 id, void *pbuf, u16 size);
int vendor_storage_write(u16 id, void *pbuf, u16 size);
int flash_vendor_dev_ops_register(int (*read)(struct blk_desc *dev_desc,
                          u32 sec,
                          u32 n_sec,
                          void *p_data),
                  int (*write)(struct blk_desc *dev_desc,
                           u32 sec,
                           u32 n_sec,
                           void *p_data));
#endif /* _ROCKCHIP_VENDOR_ */
vendor.h

 

 

驱动文件: 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.

kernel\drivers\soc\rockchip\sdmmc_vendor_storage.c

/*
 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 */

#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/debugfs.h>
#include <linux/mempolicy.h>
#include <linux/sched.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/soc/rockchip/rk_vendor_storage.h>
#include <linux/kthread.h>
#include <linux/delay.h>

#define EMMC_IDB_PART_OFFSET        64
#define EMMC_SYS_PART_OFFSET        8064
#define EMMC_BOOT_PART_SIZE        1024
#define EMMC_VENDOR_PART_START        (1024 * 7)
#define EMMC_VENDOR_PART_SIZE        128
#define EMMC_VENDOR_PART_NUM        4
#define EMMC_VENDOR_TAG            0x524B5644

struct rk_vendor_req {
    u32 tag;
    u16 id;
    u16 len;
    u8 data[1024];
};

struct vendor_item {
    u16  id;
    u16  offset;
    u16  size;
    u16  flag;
};

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;
};

#ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER
#define READ_SECTOR_IO        _IOW('r', 0x04, unsigned int)
#define WRITE_SECTOR_IO        _IOW('r', 0x05, unsigned int)
#define END_WRITE_SECTOR_IO    _IOW('r', 0x52, unsigned int)
#define GET_FLASH_INFO_IO    _IOW('r', 0x1A, unsigned int)
#define GET_BAD_BLOCK_IO    _IOW('r', 0x03, unsigned int)
#define GET_LOCK_FLAG_IO    _IOW('r', 0x53, unsigned int)
#endif

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

static u8 *g_idb_buffer;
static struct vendor_info *g_vendor;
static DEFINE_MUTEX(vendor_ops_mutex);
extern int rk_emmc_transfer(u8 *buffer, unsigned addr, unsigned blksz,
                int write);

static int emmc_vendor_ops(u8 *buffer, u32 addr, u32 n_sec, int write)
{
    u32 i, ret = 0;

    for (i = 0; i < n_sec; i++)
        ret = rk_emmc_transfer(buffer + i * 512, addr + i, 512, write);

    return ret;
}

static int emmc_vendor_storage_init(void)
{
    u32 i, max_ver, max_index;
    u8 *p_buf;

    g_vendor = kmalloc(sizeof(*g_vendor), GFP_KERNEL | GFP_DMA);
    if (!g_vendor)
        return -ENOMEM;

    max_ver = 0;
    max_index = 0;
    for (i = 0; i < EMMC_VENDOR_PART_NUM; i++) {
        /* read first 512 bytes */
        p_buf = (u8 *)g_vendor;
        if (rk_emmc_transfer(p_buf, EMMC_VENDOR_PART_START +
                 EMMC_VENDOR_PART_SIZE * i, 512, 0))
            goto error_exit;
        /* read last 512 bytes */
        p_buf += (EMMC_VENDOR_PART_SIZE - 1) * 512;
        if (rk_emmc_transfer(p_buf, EMMC_VENDOR_PART_START +
                 EMMC_VENDOR_PART_SIZE * (i + 1) - 1,
                 512, 0))
            goto error_exit;

        if (g_vendor->tag == EMMC_VENDOR_TAG &&
            g_vendor->version2 == g_vendor->version) {
            if (max_ver < g_vendor->version) {
                max_index = i;
                max_ver = g_vendor->version;
            }
        }
    }
    if (max_ver) {
        if (emmc_vendor_ops((u8 *)g_vendor, EMMC_VENDOR_PART_START +
                EMMC_VENDOR_PART_SIZE * max_index,
                EMMC_VENDOR_PART_SIZE, 0))
            goto error_exit;
    } else {
        memset((void *)g_vendor, 0, sizeof(*g_vendor));
        g_vendor->version = 1;
        g_vendor->tag = EMMC_VENDOR_TAG;
        g_vendor->version2 = g_vendor->version;
        g_vendor->free_offset = 0;
        g_vendor->free_size = sizeof(g_vendor->data);
    }
    return 0;
error_exit:
    kfree(g_vendor);
    g_vendor = NULL;
    return -1;
}

static int emmc_vendor_read(u32 id, void *pbuf, u32 size)
{
    u32 i;

    if (!g_vendor)
        return -ENOMEM;

    for (i = 0; i < g_vendor->item_num; i++) {
        if (g_vendor->item[i].id == id) {
            if (size > g_vendor->item[i].size)
                size = g_vendor->item[i].size;
            memcpy(pbuf,
                   &g_vendor->data[g_vendor->item[i].offset],
                   size);
            return size;
        }
    }
    return (-1);
}

static int emmc_vendor_write(u32 id, void *pbuf, u32 size)
{
    u32 i, j, next_index, align_size, alloc_size, item_num;
    u32 offset, next_size;
    u8 *p_data;
    struct vendor_item *item;
    struct vendor_item *next_item;

    if (!g_vendor)
        return -ENOMEM;

    p_data = g_vendor->data;
    item_num = g_vendor->item_num;
    align_size = ALIGN(size, 0x40); /* align to 64 bytes*/
    next_index = g_vendor->next_index;
    for (i = 0; i < item_num; i++) {
        item = &g_vendor->item[i];
        if (item->id == id) {
            alloc_size = ALIGN(item->size, 0x40);
            if (size > alloc_size) {
                if (g_vendor->free_size < align_size)
                    return -1;
                offset = item->offset;
                for (j = i; j < item_num - 1; j++) {
                    item = &g_vendor->item[j];
                    next_item = &g_vendor->item[j + 1];
                    item->id = next_item->id;
                    item->size = next_item->size;
                    item->offset = offset;
                    next_size = ALIGN(next_item->size,
                              0x40);
                    memcpy(&p_data[offset],
                           &p_data[next_item->offset],
                           next_size);
                    offset += next_size;
                }
                item = &g_vendor->item[j];
                item->id = id;
                item->offset = offset;
                item->size = size;
                memcpy(&p_data[item->offset], pbuf, size);
                g_vendor->free_offset = offset + align_size;
                g_vendor->free_size -= (align_size -
                            alloc_size);
            } else {
                memcpy(&p_data[item->offset],
                       pbuf,
                       size);
                g_vendor->item[i].size = size;
            }
            g_vendor->version++;
            g_vendor->version2 = g_vendor->version;
            g_vendor->next_index++;
            if (g_vendor->next_index >= EMMC_VENDOR_PART_NUM)
                g_vendor->next_index = 0;
            emmc_vendor_ops((u8 *)g_vendor, EMMC_VENDOR_PART_START +
                    EMMC_VENDOR_PART_SIZE * next_index,
                    EMMC_VENDOR_PART_SIZE, 1);
            return 0;
        }
    }

    if (g_vendor->free_size >= align_size) {
        item = &g_vendor->item[g_vendor->item_num];
        item->id = id;
        item->offset = g_vendor->free_offset;
        item->size = size;
        g_vendor->free_offset += align_size;
        g_vendor->free_size -= align_size;
        memcpy(&g_vendor->data[item->offset], pbuf, size);
        g_vendor->item_num++;
        g_vendor->version++;
        g_vendor->version2 = g_vendor->version;
        g_vendor->next_index++;
        if (g_vendor->next_index >= EMMC_VENDOR_PART_NUM)
            g_vendor->next_index = 0;
        emmc_vendor_ops((u8 *)g_vendor, EMMC_VENDOR_PART_START +
                EMMC_VENDOR_PART_SIZE * next_index,
                EMMC_VENDOR_PART_SIZE, 1);
        return 0;
    }
    return(-1);
}

#ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER
static int id_blk_read_data(u32 index, u32 n_sec, u8 *buf)
{
    u32 i;
    u32 ret = 0;

    if (index + n_sec >= 1024 * 5)
        return 0;
    index = index + EMMC_IDB_PART_OFFSET;
    for (i = 0; i < n_sec; i++) {
        ret = rk_emmc_transfer(buf + i * 512, index + i, 512, 0);
        if (ret)
            return ret;
    }
    return ret;
}

static int id_blk_write_data(u32 index, u32 n_sec, u8 *buf)
{
    u32 i;
    u32 ret = 0;

    if (index + n_sec >= 1024 * 5)
        return 0;
    index = index + EMMC_IDB_PART_OFFSET;
    for (i = 0; i < n_sec; i++) {
        ret = rk_emmc_transfer(buf + i * 512, index + i, 512, 1);
        if (ret)
            return ret;
    }
    return ret;
}

static int emmc_write_idblock(u32 size, u8 *buf, u32 *id_blk_tbl)
{
    u32 i, totle_sec, j;
    u32 totle_write_count = 0;
    u32 *p_raw_data = (u32 *)buf;
    u32 *p_check_buf = kmalloc(EMMC_BOOT_PART_SIZE * 512, GFP_KERNEL);

    if (!p_check_buf)
        return -ENOMEM;

    totle_sec = (size + 511) >> 9;
    if (totle_sec <= 8)
        totle_sec = 8;

    for (i = 0; i < 5; i++) {
        memset(p_check_buf, 0, 512);
        id_blk_write_data(EMMC_BOOT_PART_SIZE * i, 1,
                  (u8 *)p_check_buf);
        id_blk_write_data(EMMC_BOOT_PART_SIZE * i + 1,
                  totle_sec - 1, buf + 512);
        id_blk_write_data(EMMC_BOOT_PART_SIZE * i, 1, buf);
        id_blk_read_data(EMMC_BOOT_PART_SIZE * i, totle_sec,
                 (u8 *)p_check_buf);
        for (j = 0; j < totle_sec * 128; j++) {
            if (p_check_buf[j] != p_raw_data[j]) {
                memset(p_check_buf, 0, 512);
                id_blk_write_data(EMMC_BOOT_PART_SIZE * i, 1,
                          (u8 *)p_check_buf);
                break;
            }
        }
        if (j >= totle_sec * 128)
            totle_write_count++;
    }
    kfree(p_check_buf);
    if (totle_write_count)
        return 0;
    return (-1);
}
#endif

static int vendor_storage_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int vendor_storage_release(struct inode *inode, struct file *file)
{
    return 0;
}

#ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER
static const u32 g_crc32_tbl[256] = {
    0x00000000, 0x04c10db7, 0x09821b6e, 0x0d4316d9,
    0x130436dc, 0x17c53b6b, 0x1a862db2, 0x1e472005,
    0x26086db8, 0x22c9600f, 0x2f8a76d6, 0x2b4b7b61,
    0x350c5b64, 0x31cd56d3, 0x3c8e400a, 0x384f4dbd,
    0x4c10db70, 0x48d1d6c7, 0x4592c01e, 0x4153cda9,
    0x5f14edac, 0x5bd5e01b, 0x5696f6c2, 0x5257fb75,
    0x6a18b6c8, 0x6ed9bb7f, 0x639aada6, 0x675ba011,
    0x791c8014, 0x7ddd8da3, 0x709e9b7a, 0x745f96cd,
    0x9821b6e0, 0x9ce0bb57, 0x91a3ad8e, 0x9562a039,
    0x8b25803c, 0x8fe48d8b, 0x82a79b52, 0x866696e5,
    0xbe29db58, 0xbae8d6ef, 0xb7abc036, 0xb36acd81,
    0xad2ded84, 0xa9ece033, 0xa4aff6ea, 0xa06efb5d,
    0xd4316d90, 0xd0f06027, 0xddb376fe, 0xd9727b49,
    0xc7355b4c, 0xc3f456fb, 0xceb74022, 0xca764d95,
    0xf2390028, 0xf6f80d9f, 0xfbbb1b46, 0xff7a16f1,
    0xe13d36f4, 0xe5fc3b43, 0xe8bf2d9a, 0xec7e202d,
    0x34826077, 0x30436dc0, 0x3d007b19, 0x39c176ae,
    0x278656ab, 0x23475b1c, 0x2e044dc5, 0x2ac54072,
    0x128a0dcf, 0x164b0078, 0x1b0816a1, 0x1fc91b16,
    0x018e3b13, 0x054f36a4, 0x080c207d, 0x0ccd2dca,
    0x7892bb07, 0x7c53b6b0, 0x7110a069, 0x75d1adde,
    0x6b968ddb, 0x6f57806c, 0x621496b5, 0x66d59b02,
    0x5e9ad6bf, 0x5a5bdb08, 0x5718cdd1, 0x53d9c066,
    0x4d9ee063, 0x495fedd4, 0x441cfb0d, 0x40ddf6ba,
    0xaca3d697, 0xa862db20, 0xa521cdf9, 0xa1e0c04e,
    0xbfa7e04b, 0xbb66edfc, 0xb625fb25, 0xb2e4f692,
    0x8aabbb2f, 0x8e6ab698, 0x8329a041, 0x87e8adf6,
    0x99af8df3, 0x9d6e8044, 0x902d969d, 0x94ec9b2a,
    0xe0b30de7, 0xe4720050, 0xe9311689, 0xedf01b3e,
    0xf3b73b3b, 0xf776368c, 0xfa352055, 0xfef42de2,
    0xc6bb605f, 0xc27a6de8, 0xcf397b31, 0xcbf87686,
    0xd5bf5683, 0xd17e5b34, 0xdc3d4ded, 0xd8fc405a,
    0x6904c0ee, 0x6dc5cd59, 0x6086db80, 0x6447d637,
    0x7a00f632, 0x7ec1fb85, 0x7382ed5c, 0x7743e0eb,
    0x4f0cad56, 0x4bcda0e1, 0x468eb638, 0x424fbb8f,
    0x5c089b8a, 0x58c9963d, 0x558a80e4, 0x514b8d53,
    0x25141b9e, 0x21d51629, 0x2c9600f0, 0x28570d47,
    0x36102d42, 0x32d120f5, 0x3f92362c, 0x3b533b9b,
    0x031c7626, 0x07dd7b91, 0x0a9e6d48, 0x0e5f60ff,
    0x101840fa, 0x14d94d4d, 0x199a5b94, 0x1d5b5623,
    0xf125760e, 0xf5e47bb9, 0xf8a76d60, 0xfc6660d7,
    0xe22140d2, 0xe6e04d65, 0xeba35bbc, 0xef62560b,
    0xd72d1bb6, 0xd3ec1601, 0xdeaf00d8, 0xda6e0d6f,
    0xc4292d6a, 0xc0e820dd, 0xcdab3604, 0xc96a3bb3,
    0xbd35ad7e, 0xb9f4a0c9, 0xb4b7b610, 0xb076bba7,
    0xae319ba2, 0xaaf09615, 0xa7b380cc, 0xa3728d7b,
    0x9b3dc0c6, 0x9ffccd71, 0x92bfdba8, 0x967ed61f,
    0x8839f61a, 0x8cf8fbad, 0x81bbed74, 0x857ae0c3,
    0x5d86a099, 0x5947ad2e, 0x5404bbf7, 0x50c5b640,
    0x4e829645, 0x4a439bf2, 0x47008d2b, 0x43c1809c,
    0x7b8ecd21, 0x7f4fc096, 0x720cd64f, 0x76cddbf8,
    0x688afbfd, 0x6c4bf64a, 0x6108e093, 0x65c9ed24,
    0x11967be9, 0x1557765e, 0x18146087, 0x1cd56d30,
    0x02924d35, 0x06534082, 0x0b10565b, 0x0fd15bec,
    0x379e1651, 0x335f1be6, 0x3e1c0d3f, 0x3add0088,
    0x249a208d, 0x205b2d3a, 0x2d183be3, 0x29d93654,
    0xc5a71679, 0xc1661bce, 0xcc250d17, 0xc8e400a0,
    0xd6a320a5, 0xd2622d12, 0xdf213bcb, 0xdbe0367c,
    0xe3af7bc1, 0xe76e7676, 0xea2d60af, 0xeeec6d18,
    0xf0ab4d1d, 0xf46a40aa, 0xf9295673, 0xfde85bc4,
    0x89b7cd09, 0x8d76c0be, 0x8035d667, 0x84f4dbd0,
    0x9ab3fbd5, 0x9e72f662, 0x9331e0bb, 0x97f0ed0c,
    0xafbfa0b1, 0xab7ead06, 0xa63dbbdf, 0xa2fcb668,
    0xbcbb966d, 0xb87a9bda, 0xb5398d03, 0xb1f880b4,
};

static u32 rk_crc_32(unsigned char *buf, u32 len)
{
    u32 i;
    u32 crc = 0;

    for (i = 0; i < len; i++)
        crc = (crc << 8) ^ g_crc32_tbl[(crc >> 24) ^ *buf++];
    return crc;
}
#endif

static long vendor_storage_ioctl(struct file *file, unsigned int cmd,
                 unsigned long arg)
{
    long ret = -1;
    int size;
    struct rk_vendor_req *v_req;
    u32 *page_buf;

    page_buf = kmalloc(4096, GFP_KERNEL);
    if (!page_buf)
        return -ENOMEM;

    mutex_lock(&vendor_ops_mutex);

    v_req = (struct rk_vendor_req *)page_buf;

    switch (cmd) {
    case VENDOR_READ_IO:
    {
        if (copy_from_user(page_buf, (void __user *)arg, 8)) {
            ret = -EFAULT;
            break;
        }
        if (v_req->tag == VENDOR_REQ_TAG) {
            size = emmc_vendor_read(v_req->id, v_req->data,
                        v_req->len);
            if (size != -1) {
                v_req->len = size;
                ret = 0;
                if (copy_to_user((void __user *)arg,
                         page_buf,
                         v_req->len + 8))
                    ret = -EFAULT;
            }
        }
    } break;
    case VENDOR_WRITE_IO:
    {
        if (copy_from_user(page_buf, (void __user *)arg, 8)) {
            ret = -EFAULT;
            break;
        }
        if (v_req->tag == VENDOR_REQ_TAG && (v_req->len < 4096 - 8)) {
            if (copy_from_user(page_buf, (void __user *)arg,
                       v_req->len + 8)) {
                ret = -EFAULT;
                break;
            }
            ret = emmc_vendor_write(v_req->id,
                        v_req->data,
                        v_req->len);
        }
    } break;

#ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER
    case READ_SECTOR_IO:
    {
        if (copy_from_user(page_buf, (void __user *)arg, 512)) {
            ret = -EFAULT;
            goto exit;
        }

        size = page_buf[1];
        if (size <= 8) {
            id_blk_read_data(page_buf[0], size, (u8 *)page_buf);
            if (copy_to_user((void __user *)arg, page_buf,
                     size * 512)) {
                ret = -EFAULT;
                goto exit;
            }
        } else {
            ret = -EFAULT;
            goto exit;
        }
        ret = 0;
    } break;

    case WRITE_SECTOR_IO:
    {
        if (copy_from_user(page_buf, (void __user *)arg, 4096)) {
            ret = -EFAULT;
            goto exit;
        }
        if (!g_idb_buffer) {
            g_idb_buffer = kmalloc(4096 + EMMC_BOOT_PART_SIZE * 512,
                           GFP_KERNEL);
            if (!g_idb_buffer) {
                ret = -EFAULT;
                goto exit;
            }
        }
        if (page_buf[1] <= 4088 && page_buf[0] <=
            (EMMC_BOOT_PART_SIZE * 512 - 4096)) {
            memcpy(g_idb_buffer + page_buf[0], page_buf + 2,
                   page_buf[1]);
        } else {
            ret = -EFAULT;
            goto exit;
        }
        ret = 0;
    } break;

    case END_WRITE_SECTOR_IO:
    {
        if (copy_from_user(page_buf, (void __user *)arg, 28)) {
            ret = -EFAULT;
            goto exit;
        }
        if (page_buf[0] <= (EMMC_BOOT_PART_SIZE * 512)) {
            if (!g_idb_buffer) {
                ret = -EFAULT;
                goto exit;
            }
            if (page_buf[1] !=
                rk_crc_32(g_idb_buffer, page_buf[0])) {
                ret = -2;
                goto exit;
            }
            ret =  emmc_write_idblock(page_buf[0],
                          (u8 *)g_idb_buffer,
                          &page_buf[2]);
            kfree(g_idb_buffer);
            g_idb_buffer = NULL;
        } else {
            ret = -EFAULT;
            goto exit;
        }
        ret = 0;
    } break;

    case GET_BAD_BLOCK_IO:
    {
        memset(page_buf, 0, 64);
        if (copy_to_user((void __user *)arg, page_buf, 64)) {
            ret = -EFAULT;
            goto exit;
        }
        ret = 0;
    } break;

    case GET_LOCK_FLAG_IO:
    {
        page_buf[0] = 0;
        if (copy_to_user((void __user *)arg, page_buf, 4)) {
            ret = -EFAULT;
            goto exit;
        }
        ret = 0;
    } break;

    case GET_FLASH_INFO_IO:
    {
        page_buf[0] = 0x00800000;
        page_buf[1] = 0x00040400;
        page_buf[2] = 0x00010028;
        if (copy_to_user((void __user *)arg, page_buf, 11)) {
            ret = -EFAULT;
            goto exit;
        }
        ret = 0;
    } break;
#endif

    default:
        ret = -EINVAL;
        goto exit;
    }
exit:
    mutex_unlock(&vendor_ops_mutex);
    kfree(page_buf);
    return ret;
}

const struct file_operations vendor_storage_fops = {
    .open = vendor_storage_open,
    .compat_ioctl    = vendor_storage_ioctl,
    .unlocked_ioctl = vendor_storage_ioctl,
    .release = vendor_storage_release,
};

static struct miscdevice vender_storage_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name  = "vendor_storage",
    .fops  = &vendor_storage_fops,
};

static int vendor_init_thread(void *arg)
{
    int ret, try_count = 5;

    do {
        ret = emmc_vendor_storage_init();
        if (!ret) {
            break;
        }
        /* sleep 500ms wait emmc initialize completed */
        msleep(500);
    } while (try_count--);

    if (!ret) {
        ret = misc_register(&vender_storage_dev);
        rk_vendor_register(emmc_vendor_read, emmc_vendor_write);
    }
    pr_info("vendor storage:20190527 ret = %d\n", ret);
    return ret;
}

static int __init vendor_storage_init(void)
{
    g_idb_buffer = NULL;
    kthread_run(vendor_init_thread, (void *)NULL, "vendor_storage_init");
    return 0;
}

static __exit void vendor_storage_deinit(void)
{
    if (g_vendor)
        misc_deregister(&vender_storage_dev);
}

device_initcall_sync(vendor_storage_init);
module_exit(vendor_storage_deinit);
sdmmc_vendor_storage.c

kernel\include\linux\soc\rockchip\rk_vendor_storage.h

/*
 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 */

#ifndef __PLAT_RK_VENDOR_STORAGE_H
#define __PLAT_RK_VENDOR_STORAGE_H

#define RSV_ID                0
#define SN_ID                1
#define WIFI_MAC_ID            2
#define LAN_MAC_ID            3
#define BT_MAC_ID            4
#define HDCP_14_HDMI_ID            5
#define HDCP_14_DP_ID            6
#define HDCP_2X_ID            7
#define DRM_KEY_ID            8
#define PLAYREADY_CERT_ID        9
#define ATTENTION_KEY_ID        10
#define PLAYREADY_ROOT_KEY_0_ID        11
#define PLAYREADY_ROOT_KEY_1_ID        12
#define SENSOR_CALIBRATION_ID        13
#define IMEI_ID                15

int rk_vendor_read(u32 id, void *pbuf, u32 size);
int rk_vendor_write(u32 id, void *pbuf, u32 size);
int rk_vendor_register(void *read, void *write);
bool is_rk_vendor_ready(void);

#endif
rk_vendor_storage.h

用户空间:
可参考 bt_addr_vendor_storage_read_or_write()

Y:\aosp\rk3288\hardware\interfaces\bluetooth\1.0\default\bluetooth_address.cc

// rw: 0, read; 1, write
// return 0 is success, other fail
static int bt_addr_vendor_storage_read_or_write(int rw, char *buf, int len)
{
    int ret ;
    uint8 p_buf[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){
        ALOGE("vendor_storage open fail\n");
        return -1;
    }

    req->tag = VENDOR_REQ_TAG;
    req->id = VENDOR_BLUETOOTH_ID;

    if (rw == 0) {
        req->len = len;
        ret = ioctl(sys_fd, VENDOR_READ_IO, req);
        if (!ret) {
            memcpy(buf, req->data, 6);
        }
    } else {
        req->len = len;
        for (int i = 0; i < 6; i++)
            req->data[i] = buf[i];
        ret = ioctl(sys_fd, VENDOR_WRITE_IO, req);
    }
    if(ret){
        ALOGE("vendor storage %s error\n", rw ? "write":"read");
        close(sys_fd);
        return -2;
    }

    ALOGE("vendor storage %s success\n", rw ? "write":"read");
    close(sys_fd);
    return 0;
}
//
// Copyright 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "bluetooth_address.h"

#include <cutils/properties.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <utils/Log.h>
#include <sys/time.h>

namespace android {
namespace hardware {
namespace bluetooth {
namespace V1_0 {
namespace implementation {

void BluetoothAddress::bytes_to_string(const uint8_t* addr, char* addr_str) {
  sprintf(addr_str, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2],
          addr[3], addr[4], addr[5]);
}

bool BluetoothAddress::string_to_bytes(const char* addr_str, uint8_t* addr) {
  if (addr_str == NULL) return false;
  if (strnlen(addr_str, kStringLength) != kStringLength) return false;
  unsigned char trailing_char = '\0';

  return (sscanf(addr_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx%1c",
                 &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5],
                 &trailing_char) == kBytes);
}

#include <sys/ioctl.h>

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)

#define VENDOR_SN_ID        1
#define VENDOR_WIFI_MAC_ID  2
#define VENDOR_LAN_MAC_ID   3
#define VENDOR_BLUETOOTH_ID 4

struct rk_vendor_req {
    uint32 tag;
    uint16 id;
    uint16 len;
    uint8 data[1];
};

// rw: 0, read; 1, write
// return 0 is success, other fail
static int bt_addr_vendor_storage_read_or_write(int rw, char *buf, int len)
{
    int ret ;
    uint8 p_buf[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){
        ALOGE("vendor_storage open fail\n");
        return -1;
    }

    req->tag = VENDOR_REQ_TAG;
    req->id = VENDOR_BLUETOOTH_ID;

    if (rw == 0) {
        req->len = len;
        ret = ioctl(sys_fd, VENDOR_READ_IO, req);
        if (!ret) {
            memcpy(buf, req->data, 6);
        }
    } else {
        req->len = len;
        for (int i = 0; i < 6; i++)
            req->data[i] = buf[i];
        ret = ioctl(sys_fd, VENDOR_WRITE_IO, req);
    }
    if(ret){
        ALOGE("vendor storage %s error\n", rw ? "write":"read");
        close(sys_fd);
        return -2;
    }

    ALOGE("vendor storage %s success\n", rw ? "write":"read");
    close(sys_fd);
    return 0;
}

bool BluetoothAddress::get_local_address(uint8_t* local_addr) {
  char property[PROPERTY_VALUE_MAX] = {0};
  bool valid_bda = false;
  const uint8_t null_bdaddr[kBytes] = {0,0,0,0,0,0};

      if (!valid_bda) {
        int ret;
        char bd_addr[6] = {0};
        ret = bt_addr_vendor_storage_read_or_write(0, bd_addr, 6);
        if (ret == 0) {
            memcpy(local_addr, bd_addr, 6);

            valid_bda = true;
            ALOGE("Got local bdaddr for vendor storage %02X:%02X:%02X:%02X:%02X:%02X",
                local_addr[0], local_addr[1], local_addr[2],
                local_addr[3], local_addr[4], local_addr[5]);
        } /*else if (ret == -2) {
            local_addr[0] = 0x22;
            local_addr[1] = 0x22;
        local_addr[2] = (uint8_t)rand();
        local_addr[3] = (uint8_t)rand();
        local_addr[4] = (uint8_t)rand();
        local_addr[5] = (uint8_t)rand();

            valid_bda = true;
            memcpy(bd_addr, local_addr, 6);
            ALOGE("Generate random bdaddr and save to vendor storage %02X:%02X:%02X:%02X:%02X:%02X",
                local_addr[0], local_addr[1], local_addr[2],
                local_addr[3], local_addr[4], local_addr[5]);
            bt_addr_vendor_storage_read_or_write(1, bd_addr, 6);
        }*/
    }

    if (!valid_bda) {// cmy@2012-11-28: Get local bdaddr from vflash
        int vflash_fd = open("/dev/vflash", O_RDONLY);
        if (vflash_fd > 0)
        {
            char bd_addr[6] = {0};
            ALOGE("Get local bdaddr from vflash");
            #define VFLASH_READ_BDA  0x01
            if(ioctl(vflash_fd, VFLASH_READ_BDA, (unsigned long)bd_addr) >= 0
                && memcmp(bd_addr, null_bdaddr, kBytes) != 0)
            {
                local_addr[0] = bd_addr[5];
                local_addr[1] = bd_addr[4];
                local_addr[2] = bd_addr[3];
                local_addr[3] = bd_addr[2];
                local_addr[4] = bd_addr[1];
                local_addr[5] = bd_addr[0];

                valid_bda = true;
                ALOGE("Got Factory BDA %02X:%02X:%02X:%02X:%02X:%02X",
                    local_addr[0], local_addr[1], local_addr[2],
                    local_addr[3], local_addr[4], local_addr[5]);
            }
            close(vflash_fd);
        }
    }

  // Get local bdaddr storage path from a system property.
  if (property_get(PROPERTY_BT_BDADDR_PATH, property, NULL)) {
    int addr_fd;

    ALOGD("%s: Trying %s", __func__, property);

    addr_fd = open(property, O_RDONLY);
    if (addr_fd != -1) {
      char address[kStringLength + 1] = {0};
      int bytes_read = read(addr_fd, address, kStringLength);
      if (bytes_read == -1) {
        ALOGE("%s: Error reading address from %s: %s", __func__, property,
              strerror(errno));
      }
      close(addr_fd);

      // Null terminate the string.
      address[kStringLength] = '\0';

      // If the address is not all zeros, then use it.
      const uint8_t zero_bdaddr[kBytes] = {0, 0, 0, 0, 0, 0};
      if ((string_to_bytes(address, local_addr)) &&
          (memcmp(local_addr, zero_bdaddr, kBytes) != 0)) {
        valid_bda = true;
        ALOGD("%s: Got Factory BDA %s", __func__, address);
      } else {
        ALOGE("%s: Got Invalid BDA '%s' from %s", __func__, address, property);
      }
    }
  }

  // No BDADDR found in the file. Look for BDA in a factory property.
  if (!valid_bda && property_get(FACTORY_BDADDR_PROPERTY, property, NULL) &&
      string_to_bytes(property, local_addr)) {
    valid_bda = true;
  }

  // No factory BDADDR found. Look for a previously stored BDA.
  if (!valid_bda && property_get(PERSIST_BDADDR_PROPERTY, property, NULL) &&
      string_to_bytes(property, local_addr)) {
    valid_bda = true;
  }

  /* Generate new BDA if necessary */
  if (!valid_bda) {
    char bdstr[kStringLength + 1];
    struct    timeval    tv;
    struct    timezone   tz;
    gettimeofday(&tv,&tz);

    /* No autogen BDA. Generate one now. */
    local_addr[0] = 0x22;
    local_addr[1] = 0x22;
    //local_addr[2] = (uint8_t)rand();
    //local_addr[3] = (uint8_t)rand();
    //local_addr[4] = (uint8_t)rand();
    //local_addr[5] = (uint8_t)rand();
    local_addr[2]    = (uint8_t) (tv.tv_usec & 0xFF);
    local_addr[3]    = (uint8_t) ((tv.tv_usec >> 8) & 0xFF);
    local_addr[4] = (uint8_t) ((tv.tv_usec >> 16) & 0xFF);
    local_addr[5] = (uint8_t) ((tv.tv_usec >> 24) & 0xFF);

    /* Convert to ascii, and store as a persistent property */
    bytes_to_string(local_addr, bdstr);

    ALOGE("%s: No preset BDA! Generating BDA: %s for prop %s", __func__,
          (char*)bdstr, PERSIST_BDADDR_PROPERTY);
    ALOGE("%s: This is a bug in the platform!  Please fix!", __func__);

    if (property_set(PERSIST_BDADDR_PROPERTY, (char*)bdstr) < 0) {
      ALOGE("%s: Failed to set random BDA in prop %s", __func__,
            PERSIST_BDADDR_PROPERTY);
      valid_bda = false;
    } else {
      valid_bda = true;
    }
  }

  return valid_bda;
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace bluetooth
}  // namespace hardware
}  // namespace android
bluetooth_address.cc
//
// Copyright 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#pragma once

#include <fcntl.h>

#include <cstdint>
#include <string>
#include <vector>

namespace android {
namespace hardware {
namespace bluetooth {
namespace V1_0 {
namespace implementation {

// The property key stores the storage location of Bluetooth Device Address
static constexpr char PROPERTY_BT_BDADDR_PATH[] = "ro.bt.bdaddr_path";

// Check for a legacy address stored as a property.
static constexpr char PERSIST_BDADDR_PROPERTY[] =
    "persist.service.bdroid.bdaddr";

// If there is no valid bdaddr available from PROPERTY_BT_BDADDR_PATH and there
// is no available persistent bdaddr available from PERSIST_BDADDR_PROPERTY,
// use a factory set address.
static constexpr char FACTORY_BDADDR_PROPERTY[] = "ro.boot.btmacaddr";

// Encapsulate handling for Bluetooth Addresses:
class BluetoothAddress {
 public:
  // Conversion constants
  static constexpr size_t kStringLength = sizeof("XX:XX:XX:XX:XX:XX") - 1;
  static constexpr size_t kBytes = (kStringLength + 1) / 3;

  static void bytes_to_string(const uint8_t* addr, char* addr_str);

  static bool string_to_bytes(const char* addr_str, uint8_t* addr);

  static bool get_local_address(uint8_t* addr);
};

} // namespace implementation
} // namespace V1_0
} // namespace bluetooth
} // namespace hardware
} // namespace android
bluetooth_address.h

 


参考链接https://blog.csdn.net/kris_fei/article/details/79580845

 

posted on 2021-02-26 16:12  陌鉎こ城sHi  阅读(1717)  评论(0编辑  收藏  举报