zoukankan      html  css  js  c++  java
  • [RK3288] Vendor Storage区域知识及探讨

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

    (出自Rockchip Vendor Storage Application Note.pdf)

     

    存储数据结构


     在代码中用struct vendor_info表示:(路径u-bootarcharmmach-rockchipvendor.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-bootarcharmincludeasmarchvendor.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-bootarcharmmach-rockchipvendor.c

    u-bootarcharmincludeasmarchvendor.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!
    ", __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
    ", 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
    ", 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
    ", lba);
            break;
        default:
            printf("[Vendor ERROR]:Boot device type is invalid!
    ");
            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
    ", 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)
    ",
                   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!
    ");
            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!
    ");
            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.
    ", 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...
    ");
            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.
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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!
    ");
    
        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
    ",
                       __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)!
    ", size);
            return -ENOMEM;
        }
        printf("[Vendor Test]:Test Start...
    ");
        printf("[Vendor Test]:Before Test, Vendor Resetting.
    ");
        vendor_test_reset();
    
        /* FIRST TEST: test all items can be used correctly */
        printf("[Vendor Test]:<All Items Used> Test Start...
    ");
        printf("[Vendor Test]:item_num=%d, size=%d.
    ",
               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)!
    ", 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)!
    ", 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)
    ", id);
                    printf("the data content is:
    ");
                    print_buffer(0, buffer, 1, size, 16);
    
                    free(buffer);
                    return -1;
                }
            }
            debug("	#id=%03d success,data=0x%02x,size=%d.
    ", id, *buffer, size);
        }
        printf("[Vendor Test]:<All Items Used> Test End,States:OK
    ");
    
        /*
         * 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...
    ");
        /* Any id value that was not used before */
        id = item_num;
        printf("[Vendor Test]:id=%d, size=%d.
    ", id, size);
        ret = vendor_storage_write(id, buffer, size);
        if (ret == -ENOMEM)
            printf("[Vendor Test]:<Overflow Items Cnt> Test End,States:OK
    ");
        else
            printf("[Vendor Test]:<Overflow Items Cnt> Test End,States:Failed
    ");
    
        /* 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)!
    ", size);
            return -ENOMEM;
        }
    
        /* THIRD TEST: Single Item memory overflow test */
        printf("[Vendor Test]:<Single Item Memory Overflow> Test Start...
    ");
        /* 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.
    ", id, size);
        ret = vendor_storage_write(id, buffer, size);
        if (ret == size)
            printf("[Vendor Test]:<Single Item Memory Overflow> Test End, States:OK
    ");
        else
            printf("[Vendor Test]:<Single Item Memory Overflow> Test End, States:Failed
    ");
    
        /* FORTH TEST: Total memory overflow test */
        printf("[Vendor Test]:<Total memory overflow> Test Start...
    ");
        printf("[Vendor Test]:item_num=%d, size=%d.
    ", 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
    ");
                    break;
                } else {
                    printf("[Vendor Test]:<Total memory overflow> Test End, States:Failed
    ");
                    break;
                }
            }
            debug("	#id=%03d success,data=0x%02x,size=%d.
    ", id, *buffer, size);
        }
    
        /* Test end */
        printf("[Vendor Test]:After Test, Vendor Resetting...
    ");
        vendor_test_reset();
        printf("[Vendor Test]:Test End.
    ");
        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.

    kerneldriverssoc ockchipsdmmc_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
    ", 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

    kernelincludelinuxsoc ockchip k_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 k3288hardwareinterfacesluetooth1.0defaultluetooth_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
    ");
            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
    ", rw ? "write":"read");
            close(sys_fd);
            return -2;
        }
    
        ALOGE("vendor storage %s success
    ", 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 = '';
    
      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
    ");
            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
    ", rw ? "write":"read");
            close(sys_fd);
            return -2;
        }
    
        ALOGE("vendor storage %s success
    ", 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] = '';
    
          // 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

  • 相关阅读:
    [原]在Solaris 10/09上静默安装和升级Oracle 10g和Oracle 11g(一)
    [原]不祥的CPU——Alpha
    [原]单核、双核、四核、八核、十六核......(更新于2010年7月11日,增加了32核的截图)
    [原]记一次处理Oracle死会话的过程
    [原]记一次使用flashback恢复数据,警惕自己不要浮躁,还是太嫩了
    在Solaris中如何解开tar.gz的文件
    说说Oracle的rowid
    [原]SQL解决“俯瞰金字塔”矩阵
    百度搜索支持手写输入法,我爸妈终于可以用上搜索引擎了
    [原] 在Solaris 10/09上静默安装和升级Oracle 10g和Oracle 11g(四)
  • 原文地址:https://www.cnblogs.com/libra13179/p/14452865.html
Copyright © 2011-2022 走看看