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系统驱动程序添加硬件抽象层
相关文章推荐
- Android平台下驱动的开发及测试框架概述(一)
- Android平台下驱动的开发及测试框架概述(一)
- Android平台下驱动的开发及测试框架概述(二)
- Android平台下驱动的开发及测试框架概述(二)
- Android平台下驱动的开发及测试框架概述(一)
- Android平台下驱动的开发及测试框架概述(二)
- Android平台下驱动的开发及测试框架概述(二)
- Android平台下驱动的开发及测试框架概述(二)
- Android平台下驱动的开发及测试框架概述(二)
- Android平台下驱动的开发及测试框架概述(一)
- Android平台下驱动的开发及测试框架概述(一)
- Android平台下驱动的开发及测试框架概述(一)
- Android平台下驱动的开发及测试框架概述(二)
- Android平台下驱动的开发及测试框架概述(一)
- Android平台下驱动的开发及测试框架概述(一)
- Android平台下驱动的开发及测试框架概述(一)
- Android平台下驱动的开发及测试框架概述(二)
- Android平台下驱动的开发及测试框架概述(二)