硬件抽像hal
1 android 中添加hal层是为了
统一硬件的调用接口
解决了GPL的版权问题
针对特殊要求’
2 android 的框架
Android 最初的架构如下图所示
新的hal架构
Hal源代码文件存储目录不固定,一般存放在/hardware目录中,
其中/hardware/libhardware_legacy存放着旧的hal源代码文件
最终编译生成的.so库存放在system/lib/hw
3 为LED驱动增加HAL
编写支持hal的linux驱动程序的步骤:
a编写linux驱动程序,包含以下向个文件:
mini6410_leds_hal.c mini6410_leds_hal.h leds_hal_define.h Makefile
build.sh build_mini6410.sh
mini6410_leds_hal.h 文件内容如下:
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-k.h>
#define DEVICE_NAME "mini6410_leds_hal"
#define DEVICE_COUNT 1
#define MINI6410_LEDS_MAJOR 0
#define MINI6410_LEDS_MINOR 244
leds_hal_define.h文件的内容如下:
//写入数据
#define MINI6410_LEDS_HAL_WRITE_GPKPUD 1
#define MINI6410_LEDS_HAL_WRITE_GPKCON 2
#define MINI6410_LEDS_HAL_WRITE_GPKDAT 3
//读取数据
#define MINI6410_LEDS_HAL_READ_GPKPUD 4
#define MINI6410_LEDS_HAL_READ_GPKCON 5
#define MINI6410_LEDS_HAL_READ_GPKDAT 6
mini6410_leds_hal.c文件内容如下:
#include "mini6410_leds_hal.h"
#include "leds_hal_define.h"
//读写寄存器的数据
//第一个字节保存gpk寄存器类型,后四个字节保存gpk寄存器的值
static unsigned char mem[5];
//主设备号
static int major=MINI6410_LEDS_MAJOR;
//次设备号
static int minor=MINI6410_LEDS_MINOR;
//设备号
static dev_t dev_number;
//struct class表示led字符设备的结构体
static struct class *leds_class=NULL;
//描述字符设备的struct cdev
static struct cdev leds_cdev;
//将四个字节转换成int类型的数据
//只处理从start开始的四个字节
static int bytes_to_int(unsigned char buf[],int start){
int n = 0;
n = ((int) buf[start]) << 24 | //
((int) buf[start + 1]) << 16 |//
((int) buf[start + 2]) << 8 | //
((int) buf[start + 3]);
return n;
}
//将int类型数据转换成byte数组类型
//n为待转换的数,buf存储转换结果,start将结果存放在buf的指定位置
static void int_to_bytes(int n,unsigned char buf[],int start){
buf[start] = n >> 24;
buf[start + 1] = n >> 16;
buf[start + 2] = n >> 8;
buf[start + 3] = n;
}
//设备文件的write函数
static ssize_t mini6410_leds_hal_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos){
//从用户空间复制数据到内核空间
if(copy_from_user(mem,buf,5)){
return -EFAULT;
}else{
//取得gpk寄存器类型
int gpk_type = mem[0];
//向寄存器写入数据
switch(gpk_type){
case MINI6410_LEDS_HAL_WRITE_GPKPUD:
iowrite32(bytes_to_int(mem,1),S3C64XX_GPKPUD);
break;
case MINI6410_LEDS_HAL_WRITE_GPKCON:
iowrite32(bytes_to_int(mem,1),S3C64XX_GPKCON);
break;
case MINI6410_LEDS_HAL_WRITE_GPKDAT:
iowrite32(bytes_to_int(mem,1),S3C64XX_GPKDAT);
break;
}
}
//必须返回大于等于5?否则会调用多次
return 5;
}
//设备文件的read函数
static ssize_t mini6410_leds_hal_read(struct file *file,char __user *buf,size_t count,loff_t *ppos){
//取得gpk类型
int gpk_type = mem[0];
int gpk_value = 0;
//从寄存器读取一个int类型的数据
switch(gpk_type){
case MINI6410_LEDS_HAL_READ_GPKPUD:
gpk_value=ioread32(S3C64XX_GPKPUD);
break;
case MINI6410_LEDS_HAL_READ_GPKCON:
gpk_value=ioread32(S3C64XX_GPKCON);
break;
case MINI6410_LEDS_HAL_READ_GPKDAT:
gpk_value=ioread32(S3C64XX_GPKDAT);
break;
}
//将int型数据转换为byte数组
int_to_bytes(gpk_value,mem,1);
//将转换后的数据复制到用户空间
if(copy_to_user(buf,(void*)mem,5)){
return -EFAULT;
}
return 5;
}
//file_operations 结构体
static struct file_operations dev_fops={
.owner = THIS_MODULE,
.read = mini6410_leds_hal_read,
.write = mini6410_leds_hal_write
};
//-------创建设备文件
static int leds_create_device(void){
int ret =0;
int err=0;
//初始化cdev成员
cdev_init(&leds_cdev,&dev_fops);
//创建的设备文件属于当前的驱动模块
leds_cdev.owner=THIS_MODULE;
//主设备号大于0,通过指定设备号的方式注册字符设备
if(major>0){
//获取设备号
dev_number=MKDEV(major,minor);
//通过指定设备号的方式注册字符设备区域
err=register_chrdev_region(dev_number,DEVICE_COUNT,DEVICE_NAME);
//注册失败
if(err<0){
printk(KERN_WARNING "register_chrdev_region() failed\n");
return err;
}
}
else{//主设备号为0,自动分配主设备号和次设备号
//通过自动分配主设备号和次设备号的方式
//10表示起始次设备号
err=alloc_chrdev_region(&leds_cdev.dev,10,DEVICE_COUNT,DEVICE_NAME);
//注册失败
if(err<0){
printk(KERN_WARNING "alloc_chrdev_region() failed\n");
return err;
}
//获取主设备号
major=MAJOR(leds_cdev.dev);
//获取次设备号
minor=MINOR(leds_cdev.dev);
//自动分配的设备号
dev_number= leds_cdev.dev;
}
//将字符设备添加到内核中的字符设备数组中
ret=cdev_add(&leds_cdev,dev_number,DEVICE_COUNT);
//创建struct class
leds_class=class_create(THIS_MODULE,DEVICE_NAME);
//创建设备文件
device_create(leds_class,NULL,dev_number,NULL,DEVICE_NAME);
return ret;
}
//初始化led驱动
static int leds_init(void){
int ret;
//创建led驱动的设备文件
ret=leds_create_device();
printk(DEVICE_NAME"\t initialized \n");
return ret;
}
//销毁字符设备
static void leds_destroy_device(void){
//销毁字符设备
device_destroy(leds_class,dev_number);
//销毁class结构体
if(leds_class){
class_destroy(leds_class);
}
//注销字符设备区
unregister_chrdev_region(dev_number,DEVICE_COUNT);
return;
}
//卸载led驱动
static void leds_exit(void){
leds_destroy_device();
printk(DEVICE_NAME"\t exit! \n");
}
module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("retacn");
Makefile文件的内容如下:
obj-m := mini6410_leds_hal.o
DEBUG = y
Build.sh文件的内容如下:
source ./build_mini6410.sh
Build_mini6410.sh文件内容如下:
source /opt/linux/driver/common.sh
make -C $MINI6410_ANDROID_KERNEL_PATH M=${PWD}
find_devices
if [ "$selected_device" == "" ]; then
exit
else
adb -s $selected_device push ${PWD}/mini6410_leds_hal.ko /data/local
testing=$(adb -s $selected_device shell lsmod | grep "mini6410_leds_hal")
if [ "$testing" != "" ]; then
adb -s $selected_device shell rmmod mini6410_leds_hal
fi
adb -s $selected_device shell "insmod /data/local/mini6410_leds_hal.ko"
adb -s $selected_device shell "chmod 777 /dev/mini6410_leds_hal"
fi
测试写寄存器操作
向设备文件发送字节类型的测试程序
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
int main(int argc,char **argv){
int file_handler=0;
char *usage = "Usage: rwdev <r|w> <dev_file> <byte_count> <byet1> <byte2> ... <byten>";
//如果输出参数不满足条件,则输出rwdev命令的语法
if (argc < 4){
printf("%s\n",usage);
return 0;
}
//必需指定读或是写,否则输出提示语法
char *cmd=argv[1];
if(strcmp("r",cmd)!=0 && strcmp("w",cmd)!=0){
printf("%s\n",usage);
return 0;
}
//设备文件名
char *dev_filename=argv[2];
//将要读写的数据转换为int类型
int byte_count=atoi(argv[3]);
//用于读写数据的数组
unsigned char buf[byte_count];
int i=0;
//从设备读数据
if(strcmp("r",cmd)==0){
//以读写的方式打开设备文件
file_handler= open(dev_filename,O_RDWR);
//表示读取寄存器的类型
buf[0]=atoi(argv[4]);
//写入要读取哪个寄存器的数据
write(file_handler,buf,1);
//读取寄存器数据
read(file_handler,buf,byte_count);
//输出读取的字节数
printf("%d bytes: ",byte_count);
//以十进制形式输出读取的字节数
for(;i<byte_count;i++){
printf("%d ",buf[i]);
}
printf("\n");
}else if(strcmp("w",cmd)==0){//向设备写数据
//将要写入的数据存储到buf中
for(;i<byte_count;i++){
buf[i]=atoi(argv[4+i]);
}
//以只写方式打开设备文件
file_handler=open(dev_filename,O_WRONLY);
//向设备文件写义数据
write(file_handler,buf,byte_count);
}
//关闭设备文件
close(file_handler);
return 0;
}
Build.sh文件内容如下:
#利用Android源代码编译,未实现????
# 直接利用交叉编译器进行编译
arm-linux-gcc -static -o /opt/linux/driver/read_write_dev/rwdev /opt/linux/driver/read_write_dev/rw_dev.c
adb push /opt/linux/driver/read_write_dev/rwdev /data/local/rwdev
编译安装测试结果:
//点亮四个led
/data/local # ./rwdev w /dev/mini6410_leds_hal 5 3 0 0 0 0