目录
在实际开发中,需要针对高通NV分区进行读写操作,比如IMEI/MAC地址等等.这些都是约定俗成约定在固定NV分区地址.NV在正常刷机,只要不勾选erase all是不会擦除NV.所以在使用QFIL刷机需要注意不要勾选erase all,假使需要全擦,则需要提前备份好NV数据.
1. NV类型
本次所试验的是读写NV_FACTORY_DATA_1分区,分区地址为2497,详细的NV分区描述可以用QXDM连接进行查看NV内容.或者在头文件,在该头文件中nv_items_enum_type该枚举变量中定义有不同NV地址和含义.
{$PROJECT_PATH}/modem_proc/core/api/services/nv_items.h
NV_GSM_1900_VH_TH_PRDI_14_I = 2495,
NV_GSM_1900_VH_TH_PRDI_13_I = 2496,
NV_FACTORY_DATA_1_I = 2497,
NV_FACTORY_DATA_2_I = 2498,
NV_FACTORY_DATA_3_I = 2499,
NV_FACTORY_DATA_4_I = 2500,
NV_GSM_PRUI_11_I = 2501,
NV_GSM_1900_PRDI_00_I = 2502,
2. 读写NV API
高通mmi中已经封装了一层读写NV的C方法,我们可以直接进行调用,直接在HAL代码包含头文件"nv.h",路径如下:
{$PROJECT_PATH}/LINUX/android/vendor/qcom/proprietary/fastmmi/libmmi/nv.cpp
{$PROJECT_PATH}/LINUX/android/vendor/qcom/proprietary/fastmmi/libmmi/nv.h
当前针对的是在Android O上面的路径,在Android R以及之后如下路径
{$PROJECT_PATH}/LINUX/android/vendor/qcom/proprietary/commonsys/fastmmi/libmmi.nv.cpp
{$PROJECT_PATH}/LINUX/android/vendor/qcom/proprietary/commonsys/fastmmi/libmmi.nv.h
2.1. diag_nv_read方法
int diag_nv_read(nv_items_enum_type item, /*!< Which NV item to read */
unsigned char *data_ptr, /*!< buffer pointer to put the read data */
int len) {
unsigned char nv_read[NV_PKT_SIZE];
memset(&nv_read, 0, sizeof(nv_read));
nv_read[0] = DIAG_NV_READ_F;
nv_read[1] = BYTE_PTR(item)[0];
nv_read[2] = BYTE_PTR(item)[1];
send_comand(nv_read, NV_PKT_SIZE, data_ptr, len);
return 0;
}
item为NV的分区地址
data_ptr为读取出来的数据存放的缓冲区
len为需要读取的长度
在代码中有DIAG_NV_READ_F为NV Read的标志符,在{$PROJECT_PATH}/modem_proc/core/api/services/diagcmd.h中有定义
2.1. diag_nv_write方法
int diag_nv_write(nv_items_enum_type item, unsigned char *data_ptr, int len) {
unsigned char nv_write[NV_PKT_SIZE];
int data_size = NV_PKT_SIZE - 3;
if(len < NV_PKT_SIZE - 3)
data_size = len;
memset(&nv_write, 0, sizeof(nv_write));
nv_write[0] = DIAG_NV_WRITE_F;
nv_write[1] = BYTE_PTR(item)[0];
nv_write[2] = BYTE_PTR(item)[1];
memcpy(&nv_write[3], data_ptr, data_size);
send_comand(nv_write, data_size + 3, data_ptr, len);
/*sync */
efs_sync();
return 0;
}
item为NV的分区地址
data_ptr为需要写入的数据缓冲区
len为需要写入的长度
在代码中有DIAG_NV_WRITE_F为NV Read的标志符,在{$PROJECT_PATH}/modem_proc/core/api/services/diagcmd.h中有定义
3. Diag初始化和去初始化
在操作读写NV分区之前需要调用Diag_LSM相关的操作,其函数定义如下代码路径.
{$PROJECT_PATH}/adsp_proc/core/services/diag/LSM/qurt/src/Diag_LSM.c
- 先调用Diag_LSM_Init()初始化
- 调用register_callback() ----- 经过验证这个必须加上,否则会卡死
- 操作NV Write/Read API
- 调用Diag_LSM_DeInit()关闭相关句柄,否则后续无法操作NV
boolean Diag_LSM_Init (byte* pParam)
{
if(qurt_atomic_compare_and_set(&diag_lsm_ref_count_mutex_init, FALSE, TRUE))
{
qurt_rmutex_init(&diag_lsm_ref_count_mutex);
}
qurt_rmutex_lock(&diag_lsm_ref_count_mutex);
if (0 == gnDiag_LSM_Ref_Count)
{
diag_pid = qurt_getpid();
if (0 > diag_qdi_handle)
{
diag_qdi_handle = qurt_qdi_open("/dev/diag");
if(diag_qdi_handle<0)
{
printf(" Diag_LSM_Init : QDI open failed\n");
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return FALSE;
}
}
if(!DiagSvc_Malloc_Init())
{
printf("Diag_LSM_Init : SVC malloc failed\n");
Diag_LSM_DeInit();
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return FALSE;
}
if(!CreateWaitThread())
{
printf("Diag_LSM_Init : createwaitthread failed\n");
Diag_LSM_DeInit();
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return FALSE;
}
if(!Diag_LSM_Log_Init())
{
printf("Diag_LSM_Init : lsm log init failed\n");
Diag_LSM_DeInit();
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return FALSE;
}
if(!Diag_LSM_Event_Init())
{
printf("Diag_LSM_Init : lsm event init failed\n");
Diag_LSM_DeInit();
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return FALSE;
}
if(!Diag_LSM_Msg_Init())
{
printf("Diag_LSM_Init : lsm msg init failed\n");
Diag_LSM_DeInit();
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return FALSE;
}
if(!Diag_LSM_Pkt_Init())
{
printf("Diag_LSM_Init : lsm pkt init failed\n");
Diag_LSM_DeInit();
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return FALSE;
}
#ifdef FEATURE_DIAG_STM
diag_stm_init();
#endif
if(FALSE == diag_time_initialized_lsm)
{
diag_time_init_LSM();
}
if(!RegisterForMaskChange(MASK_CHANGE_REGISTER))
{
printf("Diag_LSM_Init : registerformaskchange failed\n");
Diag_LSM_DeInit();
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return FALSE;
}
#ifdef USER_PD_STRESS_TEST_CMD_CODE
DIAGPKT_DISPATCH_TABLE_REGISTER (DIAG_SUBSYS_DIAG_SERV, diag_userpd_test_tbl);
#endif
} /*end if(!InterlockedCompareExchange(&gnDiag_LSM_Ref_Count,1,0) */
gnDiag_LSM_Ref_Count += 1;
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return TRUE;
} /* Diag_LSM_Init */
boolean Diag_LSM_DeInit (void)
{
boolean bReturn = TRUE;
qurt_rmutex_lock(&diag_lsm_ref_count_mutex);
if(1 < gnDiag_LSM_Ref_Count)
{
//Someone's still using diag, so just count down and go on with business...
gnDiag_LSM_Ref_Count -= 1;
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return bReturn;
}
else if(0 == gnDiag_LSM_Ref_Count)
{
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return TRUE;
}
DiagSvc_Malloc_Exit();
if( !Diag_LSM_Event_DeInit() || !Diag_LSM_Pkt_DeInit() || !Diag_LSM_Log_DeInit() || !Diag_LSM_Msg_DeInit())
bReturn = FALSE;
if(!RegisterForMaskChange(MASK_CHANGE_DEREGISTER))
bReturn = FALSE;
if(!TerminateWaitThread())
{
bReturn = FALSE;
}
if(0 != qurt_qdi_close(diag_qdi_handle))
{
bReturn = FALSE;
}
diag_qdi_handle = -1;
//We uninitialized things because it's the last one, so mark this as having 0 references.
gnDiag_LSM_Ref_Count = 0;
qurt_rmutex_unlock(&diag_lsm_ref_count_mutex);
return bReturn;
} /* Diag_LSM_DeInit */
4. 在Android.mk添加对应的库和头文件
LOCAL_C_INCLUDES += \
vendor/qcom/proprietary/fastmmi/libmmi \
external/libcxx/include \
external/skia/include/core \
external/libxml2/include \
external/icu/icu4c/source/common \
$(QC_PROP_ROOT)/diag/include \
$(QC_PROP_ROOT)/diag/src/ \
$(TARGET_OUT_HEADERS)/common/inc
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
libstagefright_foundation \
libhardware_legacy \
libhardware \
libc \
libmmi \
libdiag
5. 实例操作
- 使用QXDM提前写入数据到NV2497
- 读取NV数据
- 写入到property属性中
- 重新写入NV
int get_sn_data(void) {
int64_t ret;
int property_ret;
int i;
int callback_len = 0;
char device_serial_number[MAX_SN_SIZE] = { 0x00 };
uint8_t ptr[MAX_SN_SIZE] = { 0x00 };
/* Calling LSM init */
if (!Diag_LSM_Init(NULL)) {
ALOGI("Diag_LSM_Init() failed.");
return -1;
} else {
ALOGI("Diag_LSM_Init succeeded. \n");
}
/* Register the callback for the primary processor */
register_callback();
ret = diag_nv_read(NV_FACTORY_DATA_1_I, ptr, sizeof(ptr));
ALOGI("diag_nv_read ret = %ld", ret);
for (i = 0; i < MAX_SN_SIZE; i++) {
ALOGI("i = %d, %02x ", i, ptr[i]);
}
for (i = 0; i < MAX_SN_SIZE; i++) {
if (ptr[i+3] == '\0') {
callback_len = i;
break;
}
device_serial_number[i] = ptr[i+3];
}
ALOGI("Device_serial_number: %s", device_serial_number);
if (callback_len == 0) {
ALOGI("callback_len = 0\n");
return -1;
}
property_ret = property_set("persist.ro.sn", device_serial_number);
ALOGI("property_set, ret = %d", property_ret);
ret = diag_nv_write(NV_FACTORY_DATA_1_I, reinterpret_cast<uint8_t *>(&device_serial_number[0]),
sizeof(device_serial_number));
ALOGI("ret = %ld", ret);
if (!Diag_LSM_DeInit()) {
ALOGI("sn_nv_reader: Unable to close handle to diag driver, err: %d\n", errno);
}
return 0;
}