您的位置:首页 > 移动开发 > Android开发

Android平台下驱动的开发及测试框架概述(一)

2014-08-08 22:21 471 查看
       
Android平台下驱动的开发及简单测试程序的编写


/****************************************************************************************************************
 *1:主要简述android平台下驱动的开发与测试步骤,涵盖安卓系统四层框架

 *2:主要参考老罗的博客《Android硬件抽象层(HAL)概要介绍和学习计划》系列文章

 *                                                       原博文地址

 *                    http://blog.csdn.net/luoshengyang/article/details/6567257
 *3: 其次参考老罗《Android系统源代码情景分析》一书,参考代码也来自此书

 ****************************************************************************************************************/

    

    
本章主要介绍Android系统的最底层--Linux设备驱动层,以及编写一个简单的测试程序检测驱动是否达到目的。在此之前,先了解下Android整个框架结构图:

                                      


  
   Android可分为四层,从上到下可分为应用层、应用架构层、系统运行库层以及Linux内核层。

     Linux内核层:Android的核心系统服务基于Linux内核,如安全性、内存管理、进程管理、网络协议栈、和驱动模型等都是依赖linux内核。

    系统运行库层:当使用Android应用框架的时,Android系统通过一些C/C++库来支持我们使用各种组件,使其能更好的为我们服务。

   应用程序框架层:这一层即是编写Google发布的核心应用时所使用的API框架,开发人员同样可以使用这些框架来开发自己的应用,这样便简化了程序开发的架构设计,但是必须遵守其框架的开发原则。

   Android应用程序层:所有的应用程序都是使用java语言编写的,通过调用应用程序框架层(Application Framework)所提供的API来完成。

   

   
1: 开发Android硬件驱动程序

       为方便描述,我们将为一个虚拟的字符设备开发驱动程序。这个虚拟的字符硬件设备只有一个寄存器,它的大小的为四字节,可读可写。由于这个字符设备是虚拟的,且只有一个寄存器,因此我们称之为“Fake Register”,对应驱动名为freg.在Android系统中开发硬件驱动程序的方法与一般的Linux系统是一样的,所以这里不再详细叙述。

    1.1:  实现内核驱动模块

        我的开发环境为Ubuntu10.04,源代码为MTK 6589平台release源码包,android系统为4.2,linux3.4.5内核版本。驱动程序freg目录如下:

   ~/alps/kernel/drivers

     -----char

           ----freg.c

           ----makefile

 这里为了简化和平台考虑,只用了上面两个源文件。下面首先看下freg.c:

Freg.c
#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 <linux/cdev.h>
#include <linux/semaphore.h>

/*主设备号和此设备号*/
static int freg_major = 0;
static int freg_minor = 0;

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

/*设备类别和设备变量*/
static struct class* freg_class = NULL;
static struct fake_reg_dev* freg_dev = NULL;

/*devfs文件系统的设备属性操作方法*/
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);

/*devfs文件系统的设备属性*/
static DEVICE_ATTR(val, 0666, 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;
}

/*文件的传统操作方法*/
static struct file_operations freg_fops = {
.owner = THIS_MODULE,
.open = freg_open,
.release = freg_release,
.read = freg_read,
.write = freg_write,
};

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;
}
sema_init(&(dev->sem),1);
dev->val = 0;
return 0;

}

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

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*
bd20
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;
}

/*创建/proc/freg文件*/
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 __init freg_init(void)
{
int err = -1;
dev_t dev = 0;
struct device *temp = NULL;
printk("Initializing freg device.\n");

err = alloc_chrdev_region(&dev,0,1,FREG_DEVICE_NODE_NAME);
if(err < 0){
printk("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("Failed to kmalloc freg devive.\n");
goto unregister;
}

err = __freg_setup_dev(freg_dev);
if(err){
printk("Freg setup cdev failed.\n");
goto cleanup;
}

freg_class = class_create(THIS_MODULE, FREG_DEVICE_CLASS_NAME);
if(IS_ERR(freg_class)){
err = PTR_ERR(freg_class);
printk("Create freg_class failed.\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;
}

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

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);
1.2: Makefile

   只需在当前目录下的makefile末尾添加:

   #add for freg

   obj-y            +=freg

 驱动和makefile完成后,即可进行编译下载,命令如下:

  ./mk r k

  ./mk bootimage 

  原版代码在编译时会出一些错误,稍加修改即可编译通过。编译成功后会在/out/target/product/${project}目录下生成boot.img文件。下载boot.img后,打开手机,并通过adb连接到电脑,运行adb shell,即可对驱动程序进行检查。

1.3: 检查驱动程序

  检查/dev目录下设备节点是否存在:  
  ls -l /dev/freg
  


 接下来查看devfs文件系统及读写函数,如下图所示:
  


 在看proc文件系统及读写函数,如下图所示:
 


2:编写简单的应用程序测试

   由上面可知,驱动程序基本达到目的。其次我们还可以通过简单的测试程序打开/dev/freg节点进行测试,在嵌入式linux中是直接编写测试层序,然后通过交叉编译工具链生成可执行程序,这里我们使用android.mk来编译生成可执行文件,原理是一样的。

 测试程序的结构如下

alps/external/

        ---freg

             ---freg.c

             ---android.mk

简单的测试程序如下:

 
#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 = 10;
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.mk如下所示:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := freg
LOCAL_SRC_FILES := $(call all-subdir-c-files)
include $(BUILD_EXECUTABLE)
编译生成可执行程序,可将此可执行程序直接push到手机的/system/bin目录下执行,效果如下图:



至此,Linux的驱动程序及简单的测试已经完成!接下来介绍:

   Android平台下驱动的开发及测试框架概述(二)

        ---为android系统驱动程序添加硬件抽象层

              



      

     
   

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息