android驱动到硬件抽象层

来源:互联网 发布:sql服务器拒绝访问 编辑:程序博客网 时间:2024/05/16 15:34

#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 "shyluo@gmail.com"

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));
 }
};

 

 

 

0 0
原创粉丝点击