看到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; }
/* * (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_ */
驱动文件: 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);
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
用户空间:
可参考 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
// // 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
参考链接https://blog.csdn.net/kris_fei/article/details/79580845