#ifndef _FAKE_REG_H_
#define _FAKE_REG_H_
#include <linux/cdev.h>
#include <linux/semaphore.h>
#define FREG_DEVICE_NODE_NAME "freg"
#define FREG_DEVICE_FILE_NAME "freg"
#define FREG_DEVICE_PROC_NAME "freg"
#define FREG_DEVICE_CLASS_NAME "freg"
struct fake_reg_dev {
int val;
struct semaphore sem;
struct cdev dev;
};
#endif
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include "freg.h"
static int freg_major = 0;
static int freg_minor = 0;
/*设备类别和设备变量*/
static struct class* freg_class = NULL;
static struct fake_reg_dev* freg_dev = NULL;
static int freg_open(struct inode* inode, struct file* filp);
static int freg_release(struct inode* inode, struct file* filp);
static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);
static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);
static struct file_operations freg_fops = {
.owner = THIS_MODULE,
.open = freg_open,
.release = freg_release,
.read = freg_read,
.write = freg_write,
};
/*访问设置属性方法*/
static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr, char* buf);
static ssize_t freg_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);
/*定义设备属性*/
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, freg_val_show, freg_val_store);
static int freg_open(struct inode* inode, struct file* filp) {
struct fake_reg_dev* dev;
/*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/
dev = container_of(inode->i_cdev, struct fake_reg_dev, dev);
filp->private_data = dev;
return 0;
}
static int freg_release(struct inode* inode, struct file* filp) {
return 0;
}
static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {
ssize_t err = 0;
struct fake_reg_dev* dev = filp->private_data;
/*同步访问*/
if(down_interruptible(&(dev->sem))) {
return -ERESTARTSYS;
}
if(count < sizeof(dev->val)) {
goto out;
}
if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {
err = -EFAULT;
goto out;
}
err = sizeof(dev->val);
out:
up(&(dev->sem));
return err;
}
static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {
struct fake_reg_dev* dev = filp->private_data;
ssize_t err = 0;
if(down_interruptible(&(dev->sem))) {
return -ERESTARTSYS;
}
if(count != sizeof(dev->val)) {
goto out;
}
if(copy_from_user(&(dev->val), buf, count)) {
err = -EFAULT;
goto out;
}
err = sizeof(dev->val);
out:
up(&(dev->sem));
return err;
}
devfs文件系统访问方法,这里把设备的寄存器val看成是设备的一个属性,通过读写这个属性来对设备进行访问
static ssize_t __freg_get_val(struct fake_reg_dev* dev, char* buf) {
int val = 0;
if(down_interruptible(&(dev->sem))) {
return -ERESTARTSYS;
}
val = dev->val;
up(&(dev->sem));
return snprintf(buf, PAGE_SIZE, "%d\n", val);
}
static ssize_t __freg_set_val(struct fake_reg_dev* dev, const char* buf, size_t count) {
int val = 0;
val = simple_strtol(buf, NULL, 10);
if(down_interruptible(&(dev->sem))) {
return -ERESTARTSYS;
}
dev->val = val;
up(&(dev->sem));
return count;
}
static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr, char* buf) {
struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev);
return __freg_get_val(hdev, buf);
}
static ssize_t freg_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {
struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev);
return __freg_set_val(hdev, buf, count);
}
定义通过proc文件系统访问方法,主要实现了hello_proc_read和hello_proc_write两个方法,同时定义了在proc文件系统创建和删除文件的方法hello_create_proc和hello_remove_proc:
static ssize_t freg_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) {
if(off > 0) {
*eof = 1;
return 0;
}
return __freg_get_val(freg_dev, page);
}
static ssize_t freg_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) {
int err = 0;
char* page = NULL;
if(len > PAGE_SIZE) {
printk(KERN_ALERT"The buff is too large: %lu.\n", len);
return -EFAULT;
}
page = (char*)__get_free_page(GFP_KERNEL);
if(!page) {
printk(KERN_ALERT"Failed to alloc page.\n");
return -ENOMEM;
}
if(copy_from_user(page, buff, len)) {
printk(KERN_ALERT"Failed to copy buff from user.\n");
err = -EFAULT;
goto out;
}
err = __freg_set_val(freg_dev, page, len);
out:
free_page((unsigned long)page);
return err;
}
static void freg_create_proc(void) {
struct proc_dir_entry* entry;
entry = create_proc_entry(FREG_DEVICE_PROC_NAME, 0, NULL);
if(entry) {
entry->owner = THIS_MODULE;
entry->read_proc = freg_proc_read;
entry->write_proc = freg_proc_write;
}
}
static void freg_remove_proc(void) {
remove_proc_entry(FREG_DEVICE_PROC_NAME, NULL);
}
最后,定义模块加载和卸载方法,这里只要是执行设备注册和初始化操作:
static int __freg_setup_dev(struct fake_reg_dev* dev) {
int err;
dev_t devno = MKDEV(freg_major, freg_minor);
memset(dev, 0, sizeof(struct fake_reg_dev));
cdev_init(&(dev->dev), &freg_fops);
dev->dev.owner = THIS_MODULE;
dev->dev.ops = &freg_fops;
err = cdev_add(&(dev->dev),devno, 1);
if(err) {
return err;
}
init_MUTEX(&(dev->sem));
dev->val = 0;
return 0;
}
static int __init freg_init(void) {
int err = -1;
dev_t dev = 0;
struct device* temp = NULL;
printk(KERN_ALERT"Initializing freg device.\n");
err = alloc_chrdev_region(&dev, 0, 1, FREG_DEVICE_NODE_NAME);
if(err < 0) {
printk(KERN_ALERT"Failed to alloc char dev region.\n");
goto fail;
}
freg_major = MAJOR(dev);
freg_minor = MINOR(dev);
freg_dev = kmalloc(sizeof(struct fake_reg_dev), GFP_KERNEL);
if(!freg_dev) {
err = -ENOMEM;
printk(KERN_ALERT"Failed to alloc freg device.\n");
goto unregister;
}
err = __freg_setup_dev(freg_dev);
if(err) {
printk(KERN_ALERT"Failed to setup freg device: %d.\n", err);
goto cleanup;
}
freg_class = class_create(THIS_MODULE, FREG_DEVICE_CLASS_NAME);
if(IS_ERR(freg_class)) {
err = PTR_ERR(freg_class);
printk(KERN_ALERT"Failed to create freg device class.\n");
goto destroy_cdev;
}
temp = device_create(freg_class, NULL, dev, "%s", FREG_DEVICE_FILE_NAME);
if(IS_ERR(temp)) {
err = PTR_ERR(temp);
printk(KERN_ALERT"Failed to create freg device.\n");
goto destroy_class;
}
/*在/sys/class/目录下创建设备类别目录hello*/
err = device_create_file(temp, &dev_attr_val);
if(err < 0) {
printk(KERN_ALERT"Failed to create attribute val of freg device.\n");
goto destroy_device;
}
dev_set_drvdata(temp, freg_dev);
freg_create_proc();
printk(KERN_ALERT"Succedded to initialize freg device.\n");
return 0;
destroy_device:
device_destroy(freg_class, dev);
destroy_class:
class_destroy(freg_class);
destroy_cdev:
cdev_del(&(freg_dev->dev));
cleanup:
kfree(freg_dev);
unregister:
unregister_chrdev_region(MKDEV(freg_major, freg_minor), 1);
fail:
return err;
}
static void __exit freg_exit(void) {
dev_t devno = MKDEV(freg_major, freg_minor);
printk(KERN_ALERT"Destroy freg device.\n");
/*删除/proc/hello文件*/
freg_remove_proc();
/*销毁设备类别和设备*/
if(freg_class) {
device_destroy(freg_class, MKDEV(freg_major, freg_minor));
class_destroy(freg_class);
}
if(freg_dev) {
cdev_del(&(freg_dev->dev));
kfree(freg_dev);
}
unregister_chrdev_region(devno, 1);
}
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Fake Register Driver");
module_init(freg_init);
module_exit(freg_exit);
完成这个内核驱动程序后,便可以在Android系统中得到三个文件,分别是/dev/hello、/sys/class/hello/hello/val和/proc/hello。
Kconfig文件中,tristate表示编译选项HELLO支持在编译内核时,hello模块支持以模块、内建和不编译三种编译方法,默认是不编译
config FREG
tristate "Fake Register Driver"
default n
help
This is the freg driver for android system.
在hello目录中新增Kconfig和Makefile两个文件,其中Kconfig是在编译前执行配置命令make menuconfig时用到的,而Makefile是执行编译命令make是用到的:
obj-$(CONFIG_FREG) += freg.o
修改arch/arm/Kconfig和drivers/kconfig两个文件,在menu "Device Drivers"和endmenu之间添加一行:
source "drivers/hello/Kconfig"
这样,执行make menuconfig时,就可以配置hello模块的编译选项了。.
七. 修改drivers/Makefile文件,添加一行:
obj-$(CONFIG_HELLO) += hello/
八. 配置编译选项:
Android源代码工程的external目录,创建hello目录:
编写调用的bin文件
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define FREG_DEVICE_NAME "/dev/freg"
int main(int argc, char** argv)
{
int fd = -1;
int val = 0;
fd = open(FREG_DEVICE_NAME, O_RDWR);
if(fd == -1)
{
printf("Failed to open device %s.\n", FREG_DEVICE_NAME);
return -1;
}
printf("Read original value:\n");
read(fd, &val, sizeof(val));
printf("%d.\n\n", val);
val = 5;
printf("Write value %d to %s.\n\n", val, FREG_DEVICE_NAME);
write(fd, &val, sizeof(val));
printf("Read the value again:\n");
read(fd, &val, sizeof(val));
printf("%d.\n\n", val);
close(fd);
return 0;
}
重新打包Android系统文件system.img:
USER-NAME@MACHINE-NAME:~/Android$ make snod
如何在硬件抽象层中增加硬件模块来和内核驱动程序交互。?
#ifndef ANDROID_FREG_INTERFACE_H
#define ANDROID_FREG_INTERFACE_H
#include <hardware/hardware.h>
__BEGIN_DECLS
/**
* The id of this module
*/
#define FREG_HARDWARE_MODULE_ID "freg"
/**
* 定义模块ID
*/
#define FREG_HARDWARE_DEVICE_ID "freg"
/*硬件模块结构体*/
struct freg_module_t {
struct hw_module_t common;
};
struct freg_device_t {
struct hw_device_t common;
int fd;
int (*set_val)(struct freg_device_t* dev, int val);
int (*get_val)(struct freg_device_t* dev, int* val);
};
fd表示设备文件描述符,对应我们将要处理的设备文件"/dev/hello",
__END_DECLS
#endif
Android硬件抽象层规范的要求,分别定义模块ID、模块结构体以及硬件接口结构体。在硬件接口结构体中,fd表示设备文件描述符,对应我们将要处理的设备文件"/dev/hello",set_val和get_val为该HAL对上提供的函数接口。
三. 进入到hardware/libhardware/modules目录,新建hello目录,并添加hello.c文件。
#define LOG_TAG "FregHALStub"
#include <hardware/hardware.h>
#include <hardware/freg.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#define DEVICE_NAME "/dev/freg"
#define MODULE_NAME "Freg"
#define MODULE_AUTHOR "[email protected]"
static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device);
static int freg_device_close(struct hw_device_t* device);
/*设备访问接口*/
static int freg_set_val(struct freg_device_t* dev, int val);
static int freg_get_val(struct freg_device_t* dev, int* val);
/*模块方法表*/
static struct hw_module_methods_t freg_module_methods = {
open: freg_device_open
};
这里,实例变量名必须为HAL_MODULE_INFO_SYM,tag也必须为HARDWARE_MODULE_TAG,这是Android硬件抽象层规范规定的。
struct freg_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: FREG_HARDWARE_MODULE_ID,
name: MODULE_NAME,
author: MODULE_AUTHOR,
methods: &freg_module_methods,
}
};
static int freg_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device) {
if(!strcmp(id, FREG_HARDWARE_DEVICE_ID)) {
struct freg_device_t* dev;
dev = (struct freg_device_t*)malloc(sizeof(struct freg_device_t));
if(!dev) {
LOGE("Failed to alloc space for freg_device_t.");
return -EFAULT;
}
memset(dev, 0, sizeof(struct freg_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t*)module;
dev->common.close = freg_device_close;
dev->set_val = freg_set_val;
dev->get_val = freg_get_val;
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
LOGE("Failed to open device file /dev/freg -- %s.", strerror(errno));
free(dev);
return -EFAULT;
}
*device = &(dev->common);
LOGI("Open device file /dev/freg successfully.");
return 0;
}
return -EFAULT;
}
DEVICE_NAME定义为"/dev/hello"。由于设备文件是在内核驱动里面通过device_create创建的,而device_create创建的设备文件默认只有root用户可读写,而hello_device_open一般是由上层APP来调用的,这些APP一般不具有root权限,这时候就导致打开设备文件失败:
Hello Stub: failed to open /dev/hello -- Permission denied.
解决办法是类似于Linux的udev规则,打开Android源代码工程目录下,进入到system/core/rootdir目录,里面有一个名为ueventd.rc文件,往里面添加一行:
/dev/hello 0666 root root
static int freg_device_close(struct hw_device_t* device) {
struct freg_device_t* freg_device = (struct freg_device_t*)device;
if(freg_device) {
close(freg_device->fd);
free(freg_device);
}
return 0;
}
static int freg_set_val(struct freg_device_t* dev, int val) {
if(!dev) {
LOGE("Null dev pointer.");
return -EFAULT;
}
LOGI("Set value %d to device file /dev/freg.", val);
write(dev->fd, &val, sizeof(val));
return 0;
}
static int freg_get_val(struct freg_device_t* dev, int* val) {
if(!dev) {
LOGE("Null dev pointer.");
return -EFAULT;
}
if(!val) {
LOGE("Null val pointer.");
return -EFAULT;
}
read(dev->fd, val, sizeof(*val));
LOGI("Get value %d from device file /dev/freg.", *val);
return 0;
}
四. 继续在hello目录下新建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 := hello.c
LOCAL_MODULE := hello.default
include $(BUILD_SHARED_LIBRARY)
注意,LOCAL_MODULE的定义规则,hello后面跟有default,hello.default能够保证我们的模块总能被硬象抽象层加载到。
五. 编译:
USER-NAME@MACHINE-NAME:~/Android$ mmm hardware/libhardware/modules/hello
编译成功后,就可以在out/target/product/generic/system/lib/hw目录下看到hello.default.so文件了。
六. 重新打包Android系统镜像system.img:
USER-NAME@MACHINE-NAME:~/Android$ make snod
重新打包后,system.img就包含我们定义的硬件抽象层模块hello.default了。
虽然我们在Android系统为我们自己的硬件增加了一个硬件抽象层模块,但是现在Java应用程序还不能访问到我们的硬件。我们还必须编写JNI方法和在Android的Application Frameworks层增加API接口,才能让上层Application访问我们的硬件。在接下来的文章中,我们还将完成这一系统过程,使得我们能够在Java应用程序中访问我们自己定制的硬件。
#define LOG_TAG "FregServiceJNI"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/freg.h>
#include <stdio.h>
namespace android
{
static void freg_setVal(JNIEnv* env, jobject clazz, jint ptr, jint value) {
freg_device_t* device = (freg_device_t*)ptr;
if(!device) {
LOGE("Device freg is not open.");
return;
}
int val = value;
LOGI("Set value %d to device freg.", val);
device->set_val(device, val);
}
static jint freg_getVal(JNIEnv* env, jobject clazz, jint ptr) {
freg_device_t* device = (freg_device_t*)ptr;
if(!device) {
LOGE("Device freg is not open.");
return 0;
}
int val = 0;
device->get_val(device, &val);
LOGI("Get value %d from device freg.", val);
return val;
}
static inline int freg_device_open(const hw_module_t* module, struct freg_device_t** device) {
return module->methods->open(module, FREG_HARDWARE_DEVICE_ID, (struct hw_device_t**)device);
}
static jint freg_init(JNIEnv* env, jclass clazz) {
freg_module_t* module;
freg_device_t* device;
LOGI("Initializing HAL stub freg......");
/*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/
if(hw_get_module(FREG_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {
LOGI("Device freg found.");
if(freg_device_open(&(module->common), &device) == 0) {
LOGI("Device freg is open.");
return (jint)device;
}
hello_init函数中,通过Android硬件抽象层提供的hw_get_module方法来加载模块ID为HELLO_HARDWARE_MODULE_ID的硬件抽象层模块,其中,
HELLO_HARDWARE_MODULE_ID是在<hardware/hello.h>中定义的。Android硬件抽象层会根据HELLO_HARDWARE_MODULE_ID的值在Android系统的/system/lib/hw
目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。
在jniRegisterNativeMethods函数中,第二个参数的值必须对应HelloService所在的包的路径,
即com.android.server.HelloService。
LOGE("Failed to open device freg.");
return 0;
}
LOGE("Failed to get HAL stub freg.");
return 0;
}
static const JNINativeMethod method_table[] = {
{"init_native", "()I", (void*)freg_init},
{"setVal_native", "(II)V", (void*)freg_setVal},
{"getVal_native", "(I)I", (void*)freg_getVal},
};
int register_android_server_FregService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/FregService", method_table, NELEM(method_table));
}
};