Android HAL读写Qualcomm NV分区

目录

1. NV类型

2. 读写NV API

2.1. diag_nv_read方法

2.1. diag_nv_write方法

3. Diag初始化和去初始化

4. 在Android.mk添加对应的库和头文件

5. 实例操作


在实际开发中,需要针对高通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. 实例操作

  1. 使用QXDM提前写入数据到NV2497
  2. 读取NV数据
  3. 写入到property属性中
  4. 重新写入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;
}

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
Android HAL(硬件抽象层)是一种用于将Android操作系统与硬件设备进行交互的接口。PDF(Portable Document Format)是一种跨平台的文档格式,用于呈现和交流电子文档。 Android HAL提供了一组硬件相关的函数和驱动程序接口,使得Android操作系统能够与硬件设备进行通信和交互。它充当了操作系统和硬件之间的桥梁,使得开发者能够方便地访问和控制各种类型的硬件功能,如摄像头、触摸屏、传感器等。通过HALAndroid操作系统可以与不同厂商的硬件设备进行兼容,提供统一的开发接口,简化开发流程。 而PDF作为一种跨平台的文档格式,能够在不同设备和操作系统上保持文档的一致性。Android HAL中没有直接与PDF相关的接口或功能,但是Android操作系统本身提供了对PDF的支持。开发者可以使用Android提供的API和库来读取、渲染、创建和编辑PDF文档。 Android操作系统通过提供PDF相关的API,使得开发者能够在应用中实现对PDF文档的读取、展示和编辑。开发者可以使用Android的PDF库来打开PDF文档,获取文档的各种属性和信息,并将其呈现在应用中的界面上。同时,也可以通过API实现对PDF的编辑功能,如添加注释、修改内容等。 总之,Android HALAndroid操作系统与硬件设备进行交互的接口,而PDF是一种跨平台的文档格式。Android HAL通过提供硬件相关的函数和驱动程序接口,提供了对各种硬件设备的访问和控制功能。而Android操作系统本身通过API和库,为开发者提供了对PDF文档的读取、渲染、创建和编辑的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值