这些天一直在想Android驱动框架层的实现,本文借助老罗老师的博客和现有通过过分析台湾的Jollen的mokoid 工程代码,并尝试在Android开发板上驱动一个简单的蜂鸣器,来解析Android下驱动的调用。
HAL架构由Patrick Brady (Google) 在2008 Google I/O演讲中提出的,如下图。
其实可以考虑先编写驱动代码,然后使用NDK编译库层的代码,最后在Android应用层来直接完成对硬件驱动层的调用!
先编写一个简单的蜂鸣器驱动程序,根据开发板上的硬件IO接口和Android驱动规范,在kernel中的driver目录中新建一个Buzzer文件,新建一个buzzer.c的源文件
#include
#include
#include
#include
#include
#include
#include
#define DRIVER_AUTHOR "canjianfantasy"
#define DRIVER_DESCRIBLE "buzzer test program"
#define BUZZER_MAJOR 248 //Device Driver Major Number
#define BUZZER_NAME "BUZZER_IO_PORT" //Device Name
#define BUZZER_DRIVER_VERSION "BUZZER IO PORT V0.1" //Device Driver Version
#define BUZZER_ADDRESS 0x88000050 //physical address
#define BUZZER_ADDRESS_RANGE 0x1000 // I/O region range
//Glable variable
static int buzzer_usage = 0; //if device is already used ?
static unsigned long *buzzer_ioremap ; //io address region
//It is called when program called open() system call
int buzzer_open(struct inode *minode,struct file *mfile)
{
//if device is already opened
if(buzzer_usage != 0)
return -EBUSY;
//mapping physical address to virtual address
buzzer_ioremap = ioremap(BUZZER_ADDRESS,BUZZER_ADDRESS_RANGE);
//check if it is usable virtual address.
if(!check_mem_region((unsigned long)buzzer_ioremao,BUZZER_ADDRESS_RANGE))
{
//register I/O address region
request_mem_region((unsigned long)buzzer_ioremap,BUZZER_ADDRESS_RANGE,BUZZER_NAME);
}
else {
printk(KERN_WARNING"Can't get I/O region 0x%x\n",(unsigned int)buzzer_ioremap);
}
buzzer_usage = 1;
return 0;
}
//It is called when program called close() system call
int buzzer_release(struct inode *mnode,struct file *mfile)
{
//Release mapped virtual address
iounmap(buzzer_ioremap);
//Release registed I/O memory region
release_mem_region((unsigned long)buzzer_ioremap,BUZZER_ADDRESS_RANGE);
buzzer_usage = 0;
return 0;
}
//It is called when program calls write() system call
size_t buzzer_write_byte(struct file *inode, const char* gdata, size_t length,
loff_t *off_what)
{
unsigned char *addr;
unsigned char c;
//copy data from gdata to c
get_user(c,gdata);
addr = (unsigned char *)(buzzer_ioremap);
*addr = c;
return length;
}
//file operations structure
static struct file_operations buzzer_fops = {
.owner = THIS_MODULE,
.open = buzzer_open,
.write = buzzer_write_byte,
.release = buzzer_release,
};
// insert module to kernel
int buzzer_init( void )
{
int result;
//Register character device drvier
result = register_chrdev(BUZZER_MAJOR,BUZZER_NAME,&buzzer_fops);
if(result < 0)
{
//registration failed
printk(KERN_WARNING "Can't get any major \n");
return result;
}
//print MAJOR_NUMBER
printk(KERN_WARNING "Init Module ,Buzzer Major number %d \n",BUZZER_MAJOR);
return 0 ;
}
// remove module from kernel
void buzzer_exit(void)
{
//Unregister character device driver
unregister_chardev(BUZZER_MAJOR,BUZZER_NAME);
printk(KERN_INFO"driver: %s DRIVER EXIT \n",BUZZER_NAME);
}
module_init(buzzer_init);
module_exit(buzzer_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
MODULE_LICENSE("Dual BSD/GPL");
在目录下创建一个Makefile文件,文件内容如下
CC = /usr/local/arm-2009q3/bin/arm-none-linux-gnueabi-gcc
obj-m := buzzer.o
KDIR := /Android/linux-2.6.32
PWD := $(shell pwd)
default :
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD modules
rm -f default
clean:
rm -f *.ko
rm -f *.o
rm -f *.mod.*
rm -f .*.cmd
先运行make clean命令清楚到编译生成的一些工程文件,执行make命令,重新编译驱动程序。将编译生成的buzzer.ko内核镜像文件拷贝出来,adb push buzzer.ko /fpga/moudles
然后插入内核镜像文件到系统中,insmod buzzer.ko,并且装载设备驱动程序,mknod /dev/buzzer c 248 0
现在就可以在库层直接编写C/C++文件来调用这个驱动程序,这个可以在ubuntu中安装NDK编译环境,新建buzzer文件,编译生成一个库文件,那样就可以直接在应用层使用这个库提供的JNI方法,那样调用会显得更加的简单,当然这个调用方式google并不提倡,我在这里只是想展示这种调用方式而已
构建一个jni工程,新建一个buzzer.c的源文件,内容如下:
#include
#include
#define LOG_TAG "BuzzerDriver"
//#define LOGW(a) __android_log_write(ANDROID_LOG_WARN,LOG_TAG,a)
#define LOGW(a) __android_log_write(ANDROID_LOG_WARN,LOG_TAG,a)
jint java_com_fantasy_buzzer_BuzzerActivity_BuzzerControl(JNIEnv *env,jobject thiz,jint value)
{
int fd,ret;
int data=value;
fd=open("/dev/buzzer",O_WRONLY);
if(fd < 0)
{
LOGW("Android Open Driver buzzer failed!");
return -errno;
}
ret=write(fd ,&data ,1);
if(ret < 0)
close(fd);
if(ret == 1)
return 0;
进行NDK编译,生成libbuzzer.so,这个库就可以直接在应用层进行调用
在eclipse中编写应用程序直接调用这个库,实现对底层驱动的调用!!
主要源代码:
package com.fantasy.buzzer;
//@ canjianfantasy 2013.6.22
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
public class BuzzerActivity extends Activity {
Button BuzzerButton;
int BuzzerData =0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buzzer);
// BuzzerControl(BuzzerData);
BuzzerButton =(Button)findViewById(R.id.On);
BuzzerButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
if(BuzzerData == 0){
BuzzerData = 1;
BuzzerButton.setText("Buzzer off");
}
else{
BuzzerData = 0;
BuzzerButton.setText("Buzzer On");
}
// BuzzerControl(BuzzerData);
}
});
}
static{
System.loadLibrary("buzzer");
}
public native int BuzzerControl(int Value);
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_buzzer, menu);
return true;
}
}
整个工程目录:
直接运行,在开发板上查看运行结果。。。