Android Fingerprint -- HAL层的初始化工作
来源:互联网 发布:java 畅言评论插件 编辑:程序博客网 时间:2024/06/08 06:20
序文:如何调用Hal层库文件
每个Hal层库文件有一个入口,即HAL_MODULE_INFO_SYM,上层在调用hal层库文件时会在/system/lib/hw/下面寻找对应库文件,找到对应库文件后便从入口HAL_MODULE_INFO_SYM调用Hal层里面的open, init, write, read等接口,Hal层再通过这个接口去读写设备节点。
一、 fingerprint.default.so
1、上一篇讲 Frameworks层初始化指纹模块的时候,Fingerprintd 调用hw_get_module函数获取了一个fingerprint_module_t类型的数据结构。 这个就是在fingerprint.default.so中,由指纹芯片厂商填充实现的。
//根据名称获取指纹hal层模块。hw_module这个一般由指纹芯片厂商根据 fingerprint.h实现if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) { ALOGE("Can't open fingerprint HW Module, error: %d", err); return 0;}
我们继续往下看fingerprint.default.so。
static struct hw_module_methods_t fingerprint_module_methods = {.open = fingerprint_open,};fingerprint_module_t HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, .module_api_version = FINGERPRINT_MODULE_API_VERSION_2_0, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = FINGERPRINT_HARDWARE_MODULE_ID, .name = "Fingerprint HAL", .author = "xxx", .methods = &fingerprint_module_methods, .dso = NULL },};
hw_get_module就是根据.id = FINGERPRINT_HARDWARE_MODULE_ID这个id来找到对应的fingerprint_module_t。hal层可能有多个指纹芯片厂商的模块,可以根据这个id来做兼容,选择性的加载不同的指纹模组。
2、fingerprintd得到了相应的fingerprint_module_t,之后就会去调用它的open函数。我们来看一下初始化指纹最核心的fingerprint_open。
static int fingerprint_open(const hw_module_t* module, const char __unused *id, hw_device_t** device){ ALOGV("fingerprint_open"); if (device == NULL) { ALOGE("NULL device on open"); return -EINVAL; } fingerprint_device_t *dev = (fingerprint_device_t *) malloc(sizeof(fingerprint_device_t)); memset(dev, 0, sizeof(fingerprint_device_t)); dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = FINGERPRINT_MODULE_API_VERSION_2_0; dev->common.module = (struct hw_module_t*) module; dev->common.close = fingerprint_close; dev->pre_enroll = fingerprint_pre_enroll; dev->enroll = fingerprint_enroll; dev->post_enroll = fingerprint_post_enroll; dev->get_authenticator_id = fingerprint_get_auth_id; dev->cancel = fingerprint_cancel; dev->remove = fingerprint_remove; dev->set_active_group = fingerprint_set_active_group; dev->authenticate = fingerprint_authenticate; dev->set_notify = set_notify_callback; dev->notify = NULL; g_device = dev; if(g_device == NULL) { ALOGV("g_device is NULL"); } else { ALOGV("g_device is not NULL"); } *device = (hw_device_t*) dev; hal_init(mDevice); return 0;}
就是填充实现Android 在fingerprint_device.h定义fingerprint_device_t需要实现的这些接口。然后赋给指针device。上层,也就是fingerprintd,就能用这个device来操作hal层的指纹模块了。
二、重要的hal_init函数。
hal init有如下几个重要的工作要做:
1、 hal_device_open()的工作很简单,就是打开指纹驱动层的设备节点,然后初始化一个用来接收驱动层消息的消息队列。当然在此之前,指纹的驱动层肯定已经正常probe,生成了相应的设备节点。
fd = open(/dev/xxx_fp, O_RDWR);...TAILQ_INIT(&head);...
2、检查指纹芯片是否已经正常工作了(在驱动层probe阶段,就会给芯片上电复位,并且加载相应的指纹固件和配置,正常指纹芯片已经开始正常工作了)。如果没有正常工作,就会给芯片复位。将其重新拉到正常的工作状态。
err = hal_get_fw_info(&download_fw_flag); if (err != GF_SUCCESS) { LOG_E(LOG_TAG "[%s] failed to get firmware info", __func__); } if (!download_fw_flag) { hal_reset_chip(); }
3、与指纹ta建立session,然后调用接口初始化指纹ta。
result = TEEC_OpenSession(g_context, g_session, &UUID, TEEC_LOGIN_PUBLIC, NULL, &operation, NULL);...TEEC_Operation operation = { 0 };operation.paramTypes = GF_CMD_TEEC_PARAM_TYPES;operation.params[0].tmpref.buffer = GF_CMD_INIT;operation.params[0].tmpref.size = len;ret = TEEC_InvokeCommand(g_session, GF_OPERATION_ID, &operation, NULL);...
对android指纹模块不了解的人可能会问指纹ta是什么?我们先说一下TEE, Trusted Execution Environment (TEE)是主控芯片厂商(mtk,高通等)提供的一个安全的硬件运行环境。指纹ta就是运行在这样一个硬件安全环境下的程序。它保证了指纹敏感数据的安全性。
4、与指纹驱动层建立通信。这里给大家看一种基于netlink,巧妙而简洁的方式。
4.1.1、通信的接收端(hal层)做了哪些处理?我们往下看
//初始化信号量 g_sem,配合消息队列,用于从消息接受者hal_netlink_recv//到消息处理者handle_thread的消息传递if (0 != sem_init(&g_sem, 0, 0)) { LOG_E(LOG_TAG, "[%s] init semaphore failed", __func__); break;}//消息处理线程handle_threadif (pthread_create(&g_handle_thread, NULL, handle_thread, NULL) != 0) { LOG_E(LOG_TAG, "[%s] pthread_create failed", __func__); break;}//用ioctl的方式将netlink描述符g_netlink_route传递给驱动层。//这样驱动层就能用这个g_netlink_route与hal层建立消息管道if (ioctl(fd, GF_IOC_INIT, &g_netlink_route) != 0) { LOG_E(LOG_TAG, "[%s] GF_IOC_INIT ioctl failed", __func__); err = GF_ERROR_OPEN_DEVICE_FAILED; break;}LOG_I(LOG_TAG, "[%s] g_netlink_route = %d", __func__, g_netlink_route);//消息接收线程hal_netlink_recvif (pthread_create(&g_netlink_thread, NULL, hal_netlink_recv, NULL) != 0) { LOG_E(LOG_TAG, "[%s] pthread_create failed", __func__); break;}
4.1.2、我们先看消息接收线程hal_netlink_recv做了什么。
/* 初始化netlink并binder 下面这些都是netlink的标准流程*/ g_netlink_sock_id = socket(AF_NETLINK, SOCK_RAW, g_netlink_route); if (g_netlink_sock_id < 0) { break; } memset(&local, 0, sizeof(struct sockaddr_nl)); local.nl_family = AF_NETLINK; local.nl_pid = getpid();/*local process id*/ local.nl_groups = 0; ret = bind(g_netlink_sock_id, (struct sockaddr*) &local, sizeof(struct sockaddr_nl)); if (ret != 0) { break; } /* send init message */ memset(&dest, 0, sizeof(struct sockaddr_nl)); dest.nl_family = AF_NETLINK; dest.nl_pid = 0; /*destination is kernel so set to 0*/ dest.nl_groups = 0; nlh = (struct nlmsghdr *) malloc(NLMSG_SPACE(MAX_NL_MSG_LEN)); if (NULL == nlh) { LOG_E(LOG_TAG, "[%s] nlh out of memery", __func__); break; } nlh->nlmsg_len = NLMSG_SPACE(MAX_NL_MSG_LEN); nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; strcpy(NLMSG_DATA(nlh), "GF"); iov.iov_base = (void*) nlh; iov.iov_len = nlh->nlmsg_len; memset(&msg, 0, sizeof(struct msghdr)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = (void*) &dest; msg.msg_namelen = sizeof(struct sockaddr_nl); //发送一个包含pid的消息给驱动层,相当于握手,告诉驱动层,我这边已经准备ok了。 if (sendmsg(g_netlink_sock_id, &msg, 0) < 0) { break; } LOG_D(LOG_TAG, "[%s] send init msg to kernel", __func__); /* 开启一个循环,接收来自驱动层的消息 */ memset(nlh, 0, NLMSG_SPACE(MAX_NL_MSG_LEN)); while (1) { //LOG_D(LOG_TAG, "here wait message from kernel"); ret = recvmsg(g_netlink_sock_id, &msg, 0); if (ret < 0) { LOG_E(LOG_TAG, "[%s] recvmsg failed, ret %d", __func__, ret); continue; } if (0 == ret) { LOG_E(LOG_TAG, "[%s] recvmsg failed, ret %d", __func__, ret); continue; } value = *((char *) NLMSG_DATA(nlh)); //根据消息类别做处理 if (GF_NETLINK_TEST == value) { LOG_D(LOG_TAG, "[%s] received GF_NETLINK_TEST command", __func__); } else if (NETLINK_IRQ == value || NETLINK_SCREEN_OFF == value || NETLINK_SCREEN_ON == value) { //如果是中断消息,或者亮灭屏事件,就把消息值push到消息队列。 //然后post信号量,让消息处理线程去处理了。 enqueue(value); sem_post(&g_netlink_sem); LOG_D(LOG_TAG, "[%s] send message : %d", __func__, value); } else { LOG_E(LOG_TAG, "[%s] wrong netlink command %d", __func__, value); } }
4.1.3、再看处理线程,等待信号量,收到之后就从消息队列里边取出消息。然后根据不同的值调用相应的处理函数。
void *handle_thread(void *handle) { while (1) { sem_wait(&g_netlink_sem); err = dequeue(&value); if (err != GF_SUCCESS) { continue; } if (GF_NETLINK_IRQ == value) { hal_irq(); } else if (GF_NETLINK_SCREEN_OFF == value) { hal_screen_off(); } else if (GF_NETLINK_SCREEN_ON == value) { hal_screen_on(); } } }
hal层的设计很清晰。由于中断来的很快,频率也很高,所以这边使用快速接收中断,缓存起来,再慢慢处理的方式处理中断事件,类似于内核中断上下文的处理方式。
4.2.1、讲到这里,肯定对驱动层怎么发送接收消息产生了好奇?本来打算在写驱动层的地方讲的,但是这样这部分内容就中断了,还是现在这里写完吧。很简单,直接看下面的代码注释就能理解。
static int netlink_init(void){ struct netlink_kernel_cfg cfg; memset(&cfg, 0, sizeof(struct netlink_kernel_cfg)); cfg.input = netlink_recv; //创建netlink 驱动层的接收hal层消息函数,注意NETLINK_ROUTE要与hal层一致。 g_dev->nl_sk = netlink_kernel_create(&init_net, NETLINK_ROUTE, &cfg);}
4.2.2、接收消息的处理:
static void netlink_recv(struct sk_buff *__skb){ skb = skb_get(__skb); //消息大于5byte才做处理 if (skb->len >= NLMSG_SPACE(0)) { nlh = nlmsg_hdr(skb); memcpy(str, NLMSG_DATA(nlh), sizeof(str)); //拿到了hal层穿下来的pid,保存起来。 g_gf_dev->pid = nlh->nlmsg_pid; } else { debug(ERR_LOG, "[%s] : not enough data length\n", __func__); } kfree_skb(skb);}
4.2.3、收到中断或者亮灭屏事件,就调用netlink_send通知hal层:
void netlink_send(const int command){ //netlink kernel层发送消息的典型流程,就是构造一个消息结构体,然后 //用api netlink_unicast发出去 skb = alloc_skb(MAX_NL_MSG_LEN, GFP_ATOMIC); if (skb == NULL) { gf_debug(ERR_LOG, "[%s] : allocate skb failed\n", __func__); return; } nlh = nlmsg_put(skb, 0, 0, 0, MAX_NL_MSG_LEN, 0); if (!nlh) { kfree_skb(skb); return; } NETLINK_CB(skb).portid = 0; NETLINK_CB(skb).dst_group = 0; //消息类型的赋值,中断,亮灭屏等 *(char *)NLMSG_DATA(nlh) = command; ret = netlink_unicast(g_gf_dev->nl_sk, skb, g_gf_dev->pid, MSG_DONTWAIT);}
这样,hal层和驱动层就建立好了通信管道。以后中断等事件就能从驱动层报给hal层,hal层会根据事件类型,做相应处理。
5、调用ta init,初始化ta。
6、开启看门狗,监听ic状态,如果ic挂了就重启ic。
至此,hal层就算初始化完毕了。接下来,上层就可以开始注册指纹了。
- Android Fingerprint -- HAL层的初始化工作
- Android Fingerprint -- HAL层的初始化工作
- Android Fingerprint -- HAL层的初始化工作
- Android Fingerprint -- HAL层的初始化工作
- Android Fingerprint完全解析(三) :Fingerprint Hal层分析
- Android Fingerprint完全解析(三) :Fingerprint Hal层分析
- Android Camera从App层到framework层到HAL层的初始化过程
- Android中wifi的HAL层
- Android的HAL(硬件抽象层)
- 总结Android HAL层的使用方法
- android关于GPS hal层的分析
- android关于GPS hal层的分析
- 总结Android HAL层的使用方法
- 总结Android HAL层的使用方法
- android关于GPS hal层的分析
- Android HAL层hardware module的设计
- android关于GPS hal层的分析
- android关于GPS hal层的分析
- iOS crash日志符号化
- Centos 7.0 下hadoop集群模式安装(以3个节点为例,master,slave1,slave2)超详细
- BeagleBone Black从入门到放弃(一)连接及系统更新
- 工程经济有何难,思维导图来助阵
- 我不知道的前端知识
- Android Fingerprint -- HAL层的初始化工作
- numpy tolist()的用法
- 原生js获取元素样式
- 【Openjudge】重建二叉树
- 源码安装pip
- Exception message: /bin/bash: line 0: fg: no job control Stack trace: ExitCodeException exitCode=1:
- 关于ListView中adapter调用notifyDataSetChanged失效的原因总结(转改)
- spribngBoot前端html页面返回参数到后台
- vue使用自定义事件的表单输入组件(日期组件与货币组件)