MTK Android平台Nvram与Gensor数据获取

在MTK的机器中,如果不用特定的工具烧写MAC地址,在开机后打开WIFI后会显示: “NVRAM WARNING: Err=0x10” 这就是没有烧写mac地址的原因,所以每次打开wifi,wifi的MAC地址都是一个随机产生的值,为什么会这样?

答案在:

1vendor/mediatek/proprietary/packages/apps/CdsInfo/src/com/mediatek/connnectivity/CdsWifiInfoActivity.java 

源码描述如下:

 1public class CdsWifiInfoActivity extends Activity {
 2
 3    private static final String TAG = "CDSINFO/WifiInfo";
 4
 5    private static final int MAC_ADDRESS_ID = 30;
 6    private static final int MAC_ADDRESS_DIGITS = 6;
 7    private static final int MAX_ADDRESS_VALUE = 0xff;
 8    private static final int INVALID_RSSI = -200;
 9//定义了MAC地址存储的文件的绝对路径
10    private static final String MAC_ADDRESS_FILENAME = "/data/nvram/APCFG/APRDEB/WIFI";
11
12    private static final String[] WIFI_SYSTEM_PROPERTY = new String[] {
13        "net.hostname",
14        "dhcp.wlan0.ipaddress",
15        "net.dns1",
16        "net.dns2",
17。。。。。

以下是获取mac地址的方法:

 1//获取mac地址的方法
 2    private void getMacAddr() {
 3
 4
 5        try {
 6            IBinder binder = ServiceManager.getService("NvRAMAgent");
 7            NvRAMAgent agent = NvRAMAgent.Stub.asInterface(binder);
 8
 9            mRandomMacAddr = new short[MAC_ADDRESS_DIGITS];
10
11            if (mUserMode) {
12                mMacAddrLabel.setVisibility(View.GONE);
13                mMacAddrEdit.setVisibility(View.GONE);
14                mMacAddBtn.setVisibility(View.GONE);
15            } else {
16                StringBuilder sb = new StringBuilder();
17                Random rand = new Random();
18                NumberFormat formatter = new DecimalFormat("00");
19                int end1 = rand.nextInt(100);
20                int end2 = rand.nextInt(100);
21                String num1 = formatter.format(end1);
22                String num2 = formatter.format(end2);
23        //这几位是固定的值
24                sb.append("00:08:22:11:");
25                sb.append(num1).append(":").append(num2);
26
27                mMacAddrLabel.setVisibility(View.VISIBLE);
28                mMacAddrEdit.setVisibility(View.VISIBLE);
29                mMacAddBtn.setVisibility(View.VISIBLE);
30                System.out.println("string buffer:" + sb);
31                mMacAddrEdit.setText(sb);
32                MacAddressRandom = sb.toString();
33
34            }
35        } catch (Exception e) {
36            e.printStackTrace();
37        }
38    }

更新mac地址的方法:

 1//更新mac地址
 2    private void updateMacAddr() {
 3
 4        try {
 5            int i = 0;
 6            IBinder binder = ServiceManager.getService("NvRAMAgent");
 7            NvRAMAgent agent = NvRAMAgent.Stub.asInterface(binder);
 8
 9            //parse mac address firstly
10            StringTokenizer txtBuffer = new StringTokenizer(mMacAddrEdit.getText().toString(), ":");
11
12            while (txtBuffer.hasMoreTokens()) {
13                mRandomMacAddr[i] = (short) Integer.parseInt(txtBuffer.nextToken(), 16);
14                System.out.println(i + ":" + mRandomMacAddr[i]);
15                i++;
16            }
17
18            if (i != 6) {
19                mToast.setText("The format of mac address is not correct");
20                mToast.show();
21                return;
22            }
23
24            byte[] buff = null;
25
26            try {
27                buff = agent.readFileByName(MAC_ADDRESS_FILENAME);
28            } catch (Exception e) {
29                e.printStackTrace();
30            }
31
32            //随机产生的buff[i+4]开始就是mac地址存储的位置
33            for (i = 0; i < MAC_ADDRESS_DIGITS; i ++) {
34                buff[i + 4] = (byte) mRandomMacAddr[i];
35            }
36
37            int flag = 0;
38
39            try {
40                flag = agent.writeFileByName(MAC_ADDRESS_FILENAME, buff);
41            } catch (Exception e) {
42                e.printStackTrace();
43            }
44
45            if (flag > 0) {
46                mToast.setText("Update successfully.\r\nPlease reboot this device");
47                mToast.show();
48            } else {
49                mToast.setText("Update failed");
50                mToast.show();
51            }
52
53        } catch (Exception e) {
54            mToast.setText(e.getMessage() + ":" + e.getCause());
55            mToast.show();
56            e.printStackTrace();
57        }
58    }

从这个代码中可以分析得知,此时的Wifi MAC地址除了前面几位是固定值,而后面都是随机产生的。 但只有一个文件才是正确的WIFI MAC地址保存的值。如果没有烧写WIFI MAC地址,那么这个文件的第4到第9个字节是固定为0的,只有烧写了MAC地址,这6个字节才是有数据的。 通过代码分析,得知烧写mac地址后的文件是保存在: /data/nvram/APCFG/APRDEB/WIFI 这个文件中。

通过adb pull /data/nvram/APCFG/APRDEB/WIFI获取这个文件到我当前的系统,打开一看:

是一堆乱码,那么如何正确打开查看呢?可以上百度去下一个WinHex打开,其实这个文件里面保存的是十六进制的数据。打开后可以看到:

从这段数据中:,格式是这样的:04 01 00 00 CC 79 CF FF 35 54 44 。。偏移从0开始一直往后依次类推,分析代码得知:

CC 79 CF FF 35 54 44就是通过特定工具刷写进去的WIFI MAC地址,如果不刷,那么这6个字节的数据默认为0。关于这个表,我们可以参考MTK的文档得知:

NVRAM在EMMC中是只读数据,一般存储在data分区中,所以格式化机器是会将NVRAM中的数据擦除的。

当然Nvram中不止存放wifi的MAC地址,也存放Gsensor校准的数据,这点以后我们再来写怎么获取。

下面就是NVRAM,WIFI的春初数值对应的。

我的机器上对应的是MT6628这个驱动模块,所以是下面这个宏:

在MAC地址没有刷写的时候,默认的6个字节都是0x00。

 1#if defined(MT6628)
 2WIFI_CFG_PARAM_STRUCT stWifiCfgDefault =
 3{
 4    0x0104, /* Own Version For MT6628*/
 5    0x0000, /* Peer Version */
 6    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* MAC ADDRESS */
        ........................

现在我们可以写一个程序将它读出来,很简单:

 1#include <stdio.h>  
 2#include <fcntl.h>  
 3#include <unistd.h>  
 4#include <string.h>  
 5#include <ctype.h>
 6#define  WIFI_FILE  "WIFI"  
 7int main(void)  
 8{  
 9    int fd = -1 ;   
10    int ret ;  
11    int i ;  
12    char buffer[512] = {0};  
13    char Nvram_wifi_mac_address[6] = {0};  
14    char d_buf[100] = {0};  
15    char dd_buf[100] = {0};
16    fd = open(WIFI_FILE,O_RDWR);  
17    if(fd < 0){  
18        printf("open fair!\n");  
19        return -1 ;  
20    }  
21
22    ret = read(fd , buffer , 512);  
23    if(ret < 0){  
24        printf("read wifi mac fair!\n");   
25    }  
26
27    for(i = 4; i < 10 ; i++)  
28        Nvram_wifi_mac_address[i-4] = buffer[i] ;  
29    //为什么要 & 0xff,因为有些机器是64位的,为了保证和32位的机器显示一致,故只取低8位。  
30    sprintf(d_buf,"%02x:%02x:%02x:%02x:%02x:%02x",  
31        Nvram_wifi_mac_address[0]&0xff,Nvram_wifi_mac_address[1]&0xff,  
32        Nvram_wifi_mac_address[2]&0xff,Nvram_wifi_mac_address[3]&0xff,  
33        Nvram_wifi_mac_address[4]&0xff,Nvram_wifi_mac_address[5]&0xff  
34    );  
35    printf("%s\n",d_buf);  
36    for(i = 0 ; i < strlen(d_buf) ; i++)
37    {
38        dd_buf[i] = toupper(d_buf[i]);  //字符串中小写转大写
39    } 
40    printf("dd_buf:%s\n",dd_buf) ;
41
42    return 0 ;  
43}  

MTKAndroid系统的Gsensor校准的数据其实也是存储在NVRAM中的,Gsensor隶属于传感器系统架构。

接下来我们来看下Gsensor校准的基准图像:

那么如何来校准Gsensor的X,Y,Z三个方向呢?我们可以参考MTK提供的工厂测试factroymode的代码:

位置在:vendor\mediatek\proprietary\factory\src\test\ftm_gs_cali.c ftm_gsensor.c

ftm_gs_cali.c就是校准的源码,我们可以打开来看看它具体的实现原理:

在ftm_gs_cali.c的static void*gs_cali_update_iv_thread(void *priv)这个函数中,我们可以分析得知如何校准Gsensor数值的过程,由于源码太长,这里只说流程:

(1) 打开Gsensor

(2) 使能Gsensor

(3) 执行校准的动作

(4) 设置校准的Cail,让校准的数据生效

(5) 将校准得到的数据写入到nvram中

(6) 关闭Gsensor

核心流程我们已经清楚了,那么接下来如何来写这个程序呢?我们要弄明白,这些函数上哪个文件里去找这是第一步:

通过grep命令搜索相关函数,最终确定,这些函数的头文件在:

./pskyed/libs/em_emmc_comm/libhwm/include/libhwm.h这里,在这个头文件中有相关的函数可以给我们使用:

 1extern int gsensor_calibration(int fd, int period, int count, int tolerance, int trace, HwmData *cali);
 2extern int gsensor_write_nvram(HwmData *dat);
 3extern int gsensor_read_nvram(HwmData *dat);
 4extern int gsensor_rst_cali(int fd);
 5extern int gsensor_set_cali(int fd, HwmData *dat);
 6extern int gsensor_get_cali(int fd, HwmData *dat);
 7extern int gsensor_read(int fd, HwmData *dat);
 8extern int gsensor_init(int fd);
 9extern int gsensor_close(int fd);
10extern int gsensor_open(int *fd);
11extern int gyroscope_calibration(int fd, int period, int count, int tolerance, int trace, HwmData *cali);
12extern int gyroscope_write_nvram(HwmData *dat);
13extern int gyroscope_read_nvram(HwmData *dat);
14extern int gyroscope_rst_cali(int fd);
15extern int gyroscope_set_cali(int fd, HwmData *dat);
16extern int gyroscope_get_cali(int fd, HwmData *dat);
17extern int gyroscope_read(int fd, HwmData *dat);
18extern int gyroscope_close(int fd);
19extern int gyroscope_open(int *fd);
20extern int gyroscope_init(int fd);

那么这些函数的源码在哪里呢?源码是没有的,因为MTK厂商将这部分代码给封装成了so动态库文件,所以,我们需要找到这个头文件对应的so文件,这样我们才能使用这个头文件,调用到so动态库中的函数。

通过搜索得知,这个so动态库文件在以下路径:

./pskyed/libs/em_emmc_comm/libhwm/libhwm.so

知道这些以后,下面我们就可以写一个简单的程序来验证这个过程了,这个留给读者自己去测试,接口我已经写好了,我的项目源码不便于公开,请读者自己拿去修改验证,流程是一样的,接口没有改过,至于想实现什么样的效果请读者自己去尝试添加,移植我的程序进行修改。

下面实现这个校准程序:

gs_cali.c

  1#include <stdio.h>
  2#include <stdlib.h>
  3#include <string.h>
  4#include <fcntl.h>
  5#include <unistd.h>
  6#include <pthread.h>
  7#include <sys/mount.h>
  8#include <sys/statfs.h>
  9#include <sys/reboot.h>
 10#include <dirent.h>
 11#include <linux/input.h>
 12#include <math.h>
 13#include <dirent.h>
 14#include <ctype.h>
 15#include <errno.h>
 16#include <linux/hwmsensor.h>
 17#include <linux/sensors_io.h>
 18#include "Keypad.h"
 19#include "libhwm.h"
 20
 21#define GSENSOR_NAME  "/dev/gsensor"
 22#define OPEN_FILE_FAIR -1 
 23#define OPEN_SUCCESS 0
 24#define RETURE_SUCCESS 0
 25#define ENABLED_SUCCESS 0
 26#define GSENSOR_CALIBRATION_SUCCESS 0
 27#define ENABLED_FAIR -2 
 28#define CALIBRATION_FAIR -3 
 29#define SET_CALIBRATION_FAIR -4
 30#define WRITE_NVRAM_FAIR  -5 
 31
 32int gs_fd ; 
 33
 34//打开gsensor
 35int gs_open(char *gs_name) ;
 36//关闭gsensor
 37int gs_close(int fd) ;
 38//gsensor开始工作
 39int gs_enable(unsigned int command) ;
 40//校准gsensor
 41int gs_calibration(unsigned int cali_delay ,unsigned int cali_num ,unsigned int cali_tolerance) ;
 42
 43int main(void)
 44{
 45    int gs_ret = 0 ;
 46    int  cali_delay = 50;
 47int  cali_num = 20;
 48//这里的20表示把校准的数值做20次平均
 49//如果想要更精确,也可以做40次平均计算
 50    int  cali_tolerance = 20 ; //40
 51    //打开gsensor
 52    gs_ret = gs_open(GSENSOR_NAME);
 53    if(gs_ret != 0){
 54        printf("gs open fair!\n") ;
 55        return -1 ;
 56    }
 57    //使能gsensor
 58    gs_ret = gs_enable(GSENSOR_IOCTL_INIT);
 59    if(gs_ret != 0){
 60        printf("gs enable fair!\n") ;
 61        return -2 ;
 62    }
 63    //校准---->包括:执行校准的动作、设置校准数值、将校准数值写入nvram
 64    gs_ret = gs_calibration(cali_delay,cali_num,cali_tolerance);
 65    if(gs_ret != 0){
 66        printf("gs_calibration fair!\n") ;
 67        return -3 ;
 68    }
 69    //关闭gsensor
 70    gs_ret = gs_close(gs_fd);
 71    if(gs_ret != 0){
 72        printf("gs_close fair!\n");
 73        return -4 ;
 74    }
 75    printf("runexec call gsensorCalibrate end\n");
 76    return 0 ;
 77}
 78
 79//1、open
 80int gs_open(char *gs_name)
 81{
 82    gs_fd = open(gs_name,O_RDONLY) ;
 83    if(gs_fd < 0)
 84    {
 85        printf("open gsensor dev fair!\n") ;
 86        return OPEN_FILE_FAIR ;
 87    }
 88    printf("gsensor open success!\n");
 89    return OPEN_SUCCESS ;
 90}
 91
 92//2、enable gsensor
 93int gs_enable(unsigned int command)
 94{
 95    int err = 0; 
 96    unsigned int flags = 1;
 97    int  max_retry = 3, retry_period = 100, retry=0;
 98    while ((err = ioctl(gs_fd, command, &flags)) && (retry ++ < max_retry)) ;
 99    usleep(retry_period*1000);
100    if (err) {
101        printf("enable g-sensor fail: %s", strerror(errno));
102        return ENABLED_FAIR;
103    }
104    printf("enable gsensor success!\n");
105    return ENABLED_SUCCESS ;
106}
107//3、校准
108int gs_calibration(unsigned int cali_delay ,unsigned int cali_num ,unsigned cali_tolerance)
109{
110    int  err ;
111    int flag = 0;
112    HwmData dat; //dat.x  dat.y  dat.z 
113    HwmData cali;
114    HwmData cali_nvram;
115    while(1)
116    {
117        //执行校准的动作
118        err = gsensor_calibration(gs_fd , cali_delay , cali_num , cali_tolerance , 0 , &cali);
119        if(err != 0)
120        {
121            printf("calibrate acc: %d\n", err); 
122            return CALIBRATION_FAIR ;
123        }
124        //设置校准cali,让校准数据生效
125        err = gsensor_set_cali(gs_fd,&cali) ;
126        if(err != 0)
127        {
128            printf("set calibration fail: (%s) %d\n", strerror(errno), err);
129            return SET_CALIBRATION_FAIR ;
130        }
131        //将校准数据写入nvram中
132        err = gsensor_write_nvram(&cali) ;
133        if(err != 0)
134        {
135            printf ("write nvram fail: (%s) %d\n", strerror(errno), err);
136            return WRITE_NVRAM_FAIR ;
137        }
138        flag = 1 ;
139        if(flag == 1)
140        {
141            printf("Gsensor calibrate success!\n") ;
142            break ;
143        }
144    }
145    return GSENSOR_CALIBRATION_SUCCESS ;
146}
147
148//关闭
149int gs_close(int fd)
150{
151    close(fd) ;
152    return 0 ;
153}

然后写一个简单的Android.mk

 1LOCAL_PATH:=$(call my-dir)
 2include $(CLEAR_VARS)
 3LOCAL_MODULE_TAGS := optional
 4LOCAL_SHARED_LIBRARIES += libcutils libutils   libhwm
 5LOCAL_STATIC_LIBRARIES += libz libstdc++ 
 6LOCAL_SRC_FILES:= \
 7    gs_cali.c 
 8
 9LOCAL_MODULE:= gsensor_calibrate
10include $(BUILD_EXECUTABLE)

将.c和Android.mk一并放在external下的一个自己定义的文件夹中,比如gs_cali文件夹,然后最后编译会在out目录下生成gsensor_calibrate这个二进制文件。

特别需要注意一点:使用这种方法进行校准后,需要将gsensor驱动的自动校准功能屏蔽,具体屏蔽方法还需要分析驱动源代码。

但这仅仅只是校准数值而已,如何把校准完的数值读出来呢?参考ftm_gsensor.c这个文件源代码,最终得知是通过打开/dev/gsensor这个节点,然后通过总线访问机制获取:

/sys/bus/platform/drivers/gsensor/sensordata这个文件存放的数据。

读取Gsensor的x,y,z的数据的方法可以参考static int gsensor_read(struct acc_priv *acc)这个函数的实现方法,但它的方法和我的是有区别的,MTK中实现的gsensor_read方法是读取从驱动中读取已经转化成十进制的数据,而我读取的是原始数据十六进制,所以必须要做进制转换,我们可以来看下这个函数:

 1static int gsensor_read(struct acc_priv *acc)
 2{
 3    static char buf[128];    
 4    int x, y, z, err;
 5
 6    if(acc->fd == -1)
 7    {
 8        FTGLOGE("invalid file descriptor\n");
 9        err = -EINVAL;
10    }
11    else if((acc->support_selftest == 1) && (!acc->selftest) && (err = gsensor_selftest(acc, 10, 20)))
12    {    
13        FTGLOGE("selftest fail: %s(%d)\n", strerror(errno), errno);
14    }
15    else
16    {
17        //使用GSENSOR_IOCTL_READ_SENSORDATA命令获取sensor的数据,并存储在buf里
18        err = ioctl(acc->fd, GSENSOR_IOCTL_READ_SENSORDATA, buf);
19        if(err)
20        {
21            FTGLOGE("read data fail: %s(%d)\n", strerror(errno), errno);
22        }
23        //从buf中将x,y,z三个值取出来
24        else if(3 != sscanf(buf, "%x %x %x", &x, &y, &z))
25        {
26            FTGLOGE("read format fail: %s(%d)\n", strerror(errno), errno);
27        }
28        else
29        {
30            //除以1000,并转成浮点数
31            acc->evt.x = (float)(x)/1000;
32            acc->evt.y = (float)(y)/1000;
33            acc->evt.z = (float)(z)/1000;
34            err = 0;
35            gsensor_statistic(acc);
36            //返回gsensor数据
37            //add sensor data to struct sp_ata_data for PC side
38            return_data.gsensor.g_sensor_x = acc->evt.x;
39            return_data.gsensor.g_sensor_y = acc->evt.y;
40            return_data.gsensor.g_sensor_z = acc->evt.z;
41            return_data.gsensor.accuracy = 3;
42
43        }
44    }
45    return err;    
46}

预知详情,可以去分析MTK factory工厂测试的源码,看看具体实现,这里就简单的一笔带过了。

当然我们也可以采用:

./pskyed/libs/em_emmc_comm/libhwm/include/libhwm.h这里面的读取gsensor数据的接口,如果要用就需要在Android.mk中包含相关的动态库。

使用adb进入Android根文件系统,此时,通过cat命令可以得知/sys/bus/platform/drivers/gsensor/sensordata这个文件存放的数据并不是字符串,而是十六进制数据,以下是机器平放在桌子上读取的x,y,z的数值:

Sensordata文件的数值: 算法转换:

x,y,z数值的校准范围是(0,0,9.8),以上数值说明机器校准后的数值是正确的,误差比较小。

在Window上用Notepad++打开这个文件看到里面的数据如下。

以下是我实现的方法:

//Gsensor读取

//这个文件存放的数据不是字符串,而是十六进制数据,所以必须做进制转换,然后才能转成浮点数

 1#define    GSENSOR_NAME "/sys/bus/platform/drivers/gsensor/sensordata"
 2//存储gsensor节点数据的结构体
 3struct gsensor_info
 4{
 5    //x,y,z坐标值
 6    char x[10] ;
 7    char y[10] ;
 8    char z[10] ;
 9};
10int Hex_to_dec(const char* str)  ;
11static int check_flag ;
12int Gsensor_Test()
13{
14    struct gsensor_info g ;
15    char ln[80];//用于存取读出数据的数组
16    FILE *f;
17    int i,n;
18    char *p;
19    int x,y,z ;
20    float x1,y1,z1 ;
21    int xv = 0 , yv = 0 , zv = 0 ;
22    while(1)
23    {
24        f=fopen(GSENSOR_NAME,"r");//打开txt文件
25        if(NULL==f){
26            printf("Can not open file sensordata!\n");//判断是否可以打开文件
27            return 1;
28        }
29        i=0;
30        while (1)
31        {
32           //从文件中读取一行数据
33           if (NULL==fgets(ln,80,f)) 
34                break;
35           p = ln ; 
36           sscanf(p,"%s%s%s",g.x,g.y,g.z);
37           i++;
38        }
39        //存储的数据是十六进制,需要做数据转换
40        x = Hex_to_dec(g.x);
41        y = Hex_to_dec(g.y);
42        z = Hex_to_dec(g.z);
43        x1 = (float)x / 1000 ;
44        y1 = (float)y / 1000 ;
45        z1 = (float)z / 1000 ;
46        printf("x: %4.2f   y:%4.2f  z:%4.2f\n",x1,y1,z1);
47        break ;
48    }
49    return 0 ;
50    while_r:
51    while(1);
52}
53//16进制转10进制算法实现
54int Hex_to_dec(const char* str)  
55{  
56    int value;  
57
58    if (! str)  
59    {  
60        return 0;  
61    }  
62    value = 0;  
63    while (1)  
64    {  
65        if ((*str >= '0') && (*str <= '9'))  
66        {  
67            value = value*16 + (*str - '0');  
68        }  
69        else if ((*str >= 'A') && (*str <= 'F'))  
70        {  
71            value = value*16 + (*str - 'A') + 10;  
72        }  
73        else if ((*str >= 'a') && (*str <= 'f'))  
74        {  
75            value = value*16 + (*str - 'a') + 10;  
76        }  
77        else  
78        {  
79            break;  
80        }  
81        str++;  
82    }  
83    return value;  
84} 

原文发布于微信公众号 - 嵌入式开发圈(gh_d6ff851b4069)

原文发表时间:2019-05-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券