该系列的文章主要是介绍实现从应用层(app)实现对硬件驱动层的访问,也就是通过读写文件节点的方式,访问Linux内核驱动程序。
在这便文章中介绍的是第一部分:即如何在硬件抽象层中增加硬件模块来和内核驱动程序交互。主要通过android项目翻盖机如果有子屏的话对后屏(sublcd)进行驱动操作(打开关闭)。
一、添加sublcd.h文件
1.1、文件目录:
/hardware/libhardware/include/hardware/
1.2、sublcd.h文件内容:
#ifndef ANDROID_SUBLCD_INTERFACE_H
#define ANDROID_SUBLCD_INTERFACE_H
#include
__BEGIN_DECLS
/*定义模块 ID*/
#define SUBLCD_HARDWARE_MODULE_ID "sublcd"
/*硬件模块结构体*/
struct sublcd_module_t {
struct hw_module_t common;
};
/*硬件接口结构体*/
struct sublcd_device_t {
struct hw_device_t common;
int fd;
int (*set_val)(struct sublcd_device_t* dev, int val);
int (*get_val)(struct sublcd_device_t* dev, int* val);
};
__END_DECLS
#endif
1.3、代码解释:
这里的数据结构定义完全按照Android硬件抽象层规范的要求,分别定义模块ID、模块结构体以及硬件接口结构体。在硬件接口结构体中,fd表示设备文件描述符,对应我们将要处理的设备文件节点"/sys/class/ktd20xx/ktd2026/back_light_led",set_val和get_val为该HAL对上提供的函数接口。
模块ID的名字是生成的.so文件的前缀名,在framework层就是通过模块ID(SUBLCD_HARDWARE_MODULE_ID)去寻找生成的.so文件。所以该ID必须和后面要介绍到的sublcd目录下的Android.mk中定义的LOCAL_MODULE名字(前缀)一致,否则会导致在framework层往下调用HAL层定义的函数接口时失败。
二、添加sublcd文件夹
2.1、文件目录:
/hardware/libhardware/modules/
2.2、/sublcd/sublcd.c文件内容:
//相关头文件
#define LOG_TAG "SubLcdStub"
#include
#include
#include
#include
#include
#include
//相关数据结构与宏的定义及函数声明
#define DEVICE_NAME "/sys/class/ktd20xx/ktd2026/back_light_led"
#define MODULE_NAME "Sublcd"
#define MODULE_AUTHOR "[email protected]"
static int light_sublcd();
static int died_sublcd();
/*Open and close sublcd device*/
static int sublcd_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int sublcd_device_close(struct hw_device_t* device);
/*Point to Interface*/
static int sublcd_set_val(struct sublcd_device_t* dev, int val);
static int sublcd_get_val(struct sublcd_device_t* dev, int* val);
/*Table of Module*/
static struct hw_module_methods_t sublcd_module_methods = {
open: sublcd_device_open
};
struct sublcd_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: SUBLCD_HARDWARE_MODULE_ID,
name: MODULE_NAME,
author: MODULE_AUTHOR,
methods: &sublcd_module_methods,
}
};
//打开和关闭文件节点方法定义(包括相关数据结构初始化)
static int sublcd_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device)
{
ALOGI("SublcdStub: Sublcd_device_open");
struct sublcd_device_t* dev;
dev = (struct sublcd_device_t*)malloc(sizeof(struct sublcd_device_t));
if(!dev) {
ALOGI("SublcdStub: failed to alloc space");
return -EFAULT;
}
memset(dev, 0, sizeof(struct sublcd_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t*)module;
dev->common.close = sublcd_device_close;
dev->set_val = sublcd_set_val;
dev->get_val = sublcd_get_val;
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
ALOGI("SublcdStub: failed to open /dev/graphics/fb2 -- %s.", strerror(errno));
free(dev);
return -EFAULT;
}
*device = &(dev->common);
ALOGI("SublcdStub: open /dev/graphics/fb2 successfully.");
return 0;
}
static int sublcd_device_close(struct hw_device_t* device)
{
ALOGI("SublcdStub: sublcd_device_close");
struct sublcd_device_t* sublcd_device = (struct sublcd_device_t*)device;
if(sublcd_device) {
close(sublcd_device->fd);
free(sublcd_device);
}
return 0;
}
//往上(HAL)接口函数和文件系统节点操作函数具体定义
static int sublcd_set_val(struct sublcd_device_t* dev, int val) {
int iflight,ifsublcdTest;
if(iflight = light_sublcd() == -1){
ALOGI("SublcdStub: iflight == -1");
return -EFAULT;
}
return 0;
}
static int sublcd_get_val(struct sublcd_device_t* dev, int* val) {
int ifdied_sublcd,ifsublcdTest_exit;
if(ifdied_sublcd = died_sublcd() == -1){
ALOGI("SublcdStub: iflight == -1");
return -EFAULT;
}
return 0;
}
static int light_sublcd(){
char *aa = "200";
int back_fd = -1;
back_fd = open(DEVICE_NAME, O_RDWR);
if (back_fd < 0) {
ALOGI("SublcdStub: light_sublcd failed");
return -EFAULT;
}
write(back_fd, aa, 3);
if(back_fd){
close(back_fd);
}
return 0;
}
static int died_sublcd(){
char *aa = "0";
int back_fd = -1;
back_fd = open(DEVICE_NAME, O_RDWR);
if (back_fd < 0) {
ALOGI("SublcdStub: died_sublcd failed");
return -EFAULT;
}
write(back_fd, aa, 1);
if(back_fd){
close(back_fd);
}
return 0;
}
以上便是sublcd.c的全部内容,主要是定义相关方法接口供framework层调用驱动,并实现对内核驱动程序的访问。
需要注意的是:
open函数直接打开的是文件系统节点,当打开失败的时候返回的值<0,可以此判断open函数是否成功。open函数的第二个参数是文件系统节点的打开方式权限定义,其中:O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(可读可写)……
write函数是对文件系统节点写入需要写入的值,需要注意的是第一个参数是open函数的返回,第二个参数是需要写入的值,是字符串类型,最后一个参数是该段字符串的长度。Write函数的返回值是该函数的最后一个参数。
2.3、/sublcd/Android.mk文件内容:
#android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := sublcd.c
LOCAL_MODULE := sublcd.default
include $(BUILD_SHARED_LIBRARY)
需要注意的是LOCAL_MODULE:= sublcd.default中sublcd便是上面所提到的模块ID,需要对应。
三、修改Android.mk文件
3.1、文件目录
/hardware/libhardware/modules/Android.mk
3.2、修改内容:
hardware_modules := ……sublcd ……
说明:在hardware_modules下添加对新增文件sublcd的编译。
至此便实现了硬件抽象层的方法接口,到hardware/libhardware/modules/sublcd下执行mm编译会在out/target/product/(projectName)/system/lib/hw生成sublcd.default.so。可使用adbpush直接导入到手机的/system/lib/hw/下,或者执行makesnod重新打包system.img,导入手机。
附:
问题:HelloStub: failed to open /dev/hello -- Permission denied.
解决:解决办法是类似于Linux的udev规则,打开Android源代码工程目录下,进入到system/core/rootdir目录,里面有一个名为ueventd.rc文件,往里面添加一行:
/dev/hello0666 root root
以上是在驱动层的接口实现,在上层(应用层)怎么去调用这边封装好的接口呢?请参加下篇Bolg介绍:
· 应用层到驱动层的接口实现(二) ---- 应用框架层&&app应用层