一.唠嗑
无论学习什么,我都坚守一个原则:动手实践。
毕竟,纸上得来终觉浅,绝知此事要躬行!
为了加深对HAL层的理解,打算站在前辈-老罗等的肩膀上,动手实践一波。
只有你动手了,你才会犯错,犯错了才能学到东西,理解也更深。
在目前手上的项目进行实践!
平台:高通8908w
知识点
- 1.博客推荐
- 2.在kernel层实现简单的字符驱动。
HAL层博客推荐
1.深入浅出 - Android系统移植与平台开发(八)- HAL Stub框架分析
2.深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
这个博主的博客写的也挺好的。推荐优先看
3. Android硬件抽象层(HAL)概要介绍和学习计划
老罗的良心博客,跟着前辈一步步敲。
和当年我学Android一样,跟着郭霖的书一步步敲。
如果你看不懂老罗的博客,请你跟随以下步骤学习:
- 1.学习LDD这本书,多读几遍
- 2.学习我推荐博客1和2
- 3.学习JNI知识
- 4.学习AIDL知识
AIDL-小白成长记 - 5.学习Android知识,可以看郭霖的《第一行代码》
本文目的在于抛砖引玉,未来如果正在迷茫的年轻人,如果想学习HAL层。
你可以按照我说的,自己跟着老罗的博客敲完整个代码学习!
二.如何在kernel层实现一个简单的字符驱动
俗话说,麻雀虽小,五脏六腑俱全。
本系列只是动手系列,目的只是为了加深对HAL层的理解。
因此,所有的代码,我们力求最简单的实现,不会考虑代码健壮性等问题。
如果你不知道怎么写一个简单的字符驱动,
请阅读LDD(Linux Device Driver)第三章
实现步骤:
1.定义并实现设备文件操作集合
static struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
}
实现省略
2.动态申请设备号
alloc_chrdev_region(&dev, 0, 1, "hello");
3.初始化且注册字符设备(绑定操作集合)-传统方式
cdev_init(&(dev->dev), &hello_fops);
dev->dev.owner = THIS_MODULE;
dev->dev.ops = &hello_fops;
/*注册字符设备*/
err = cdev_add(&(dev->dev),devno, 1);
4.访问设备
- a.通过proc文件系统来访问
- b.通过传统的设备文件的方法来访问,需要自己实现main函数进行访问
- c.通过devfs文件系统来访问
本文通过方式a进行访问。
具体实现
- 新增加以下文件
kernel/drivers/misc/hello
- hello.c
- Makefile
hello.c文件实现
//引入相关头文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZE 10
//定义字符设备
typedef struct hello_dev {
int val;//当做一个寄存器变量
struct cdev dev;//字符设备,"继承"关系
}hello_dev_t;
//hello_dev_t 相当于 struct hello_dev
hello_dev_t *hello_dev;
dev_t devNum;//设备号
//步骤1.定义并实现设备文件操作集合
/*传统的设备文件操作方法*/
static int hello_open(struct inode* inode, struct file* filp);
static int hello_release(struct inode* inode, struct file* filp);
static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);
static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);
static struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
};
static int hello_open(struct inode* inode, struct file* filp) {
//找到hello_dev_t类型
hello_dev_t *dev = container_of(inode->i_cdev,hello_dev_t,dev);
filp->private_data = dev;//保存到私有数据中,方便后续访问
return 0;
}
static int hello_release(struct inode* inode, struct file* filp) {
//释放资源,这里空实现
return 0;
}
static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {
int ret = 0;
hello_dev_t *dev = filp->private_data;//拿到字符设备
if(copy_to_user(buf,&(dev->val),sizeof(dev->val))) {
ret = -EFAULT;
printk("zcf copy to userspace failed");
return ret;
}
return sizeof(dev->val);
}
static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {
int ret = 0;
hello_dev_t *dev = filp->private_data;//拿到字符设备
/*将用户提供的缓冲区的值写到设备寄存器去*/
if(copy_from_user(&(dev->val), buf, count)) {
ret = -EFAULT;
printk("zcf copy from userspace failed");
return ret;
}
return sizeof(dev->val);
}
static ssize_t proc_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {
//*f_pos在文件中的位置 count表示读取的字节数
int val = 0;
return snprintf(buf, PAGE_SIZE, "%d\n", val);
}
static ssize_t proc_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {
int ret = 0;
int val =0;
/*将用户提供的缓冲区的值写到设备寄存器去*/
if(copy_from_user(&(val), buf, count)) {
ret = -EFAULT;
printk("zcf copy from userspace failed");
return ret;
}
/*将字符串转换成数字*/
//val = simple_strtol(buf, NULL, 10);
hello_dev->val = val;
return sizeof(val);
}
static struct file_operations proc_fops = {
.owner = THIS_MODULE,
.read = proc_read,
.write = proc_write,
};
static int __init proc_hello_init(void) {
proc_create("hello", 0666, NULL, &proc_fops);
return 0;
}
static int __init hello_init(void) {
int ret = 0;
struct class * cls;
//动态分配设备号,保存到devNum
ret = alloc_chrdev_region(&devNum, 0, 1, "hello");
//申请内存
hello_dev = kmalloc(sizeof(hello_dev_t), GFP_KERNEL);
//初始化字符设备
cdev_init(&(hello_dev->dev),&hello_fops);
hello_dev->dev.owner = THIS_MODULE;
hello_dev->dev.ops = &hello_fops;
//注册字符设备
ret = cdev_add(&(hello_dev->dev),devNum,1);
hello_dev->val = 0;
//在/dev中创建节点hello
cls = class_create(THIS_MODULE,"hello");
device_create(cls,NULL,devNum,NULL,"hello");
//创建proc/hello
proc_hello_init();
return ret;
}
static void __exit hello_exit(void) {
//删除字符设备
if(hello_dev) {
cdev_del(&(hello_dev->dev));
kfree(hello_dev);
}
}
module_init(hello_init);
module_exit(hello_exit);
Makefile文件实现
obj-y = hello.o
- 修改上级目录的Makefile
kernel/drivers/misc/Makefile
obj-y += hello/
让系统编译hello文件夹
结果:
- dev目录下生成hello节点
- prco目录下生成hello节点
三.自己写c文件访问hello节点
实现步骤
1.创建hello文件夹
路径:external/hello/
hello.c
Android.mk
其实你在哪个路径创建hello文件夹都可以,这里就在external下创建吧!
2.实现hello.c
#include
#include
#include
int main(int argc,char **argv)
{
int fd = -1;
int val = 0;
fd = open("/dev/hello",O_RDWR);
if(fd == -1) {
printf("Failed to open device!\n");
return -1;
}
read(fd,&val,sizeof(val));
printf("read val = %d\n",val);
val = 8;
printf("write 8 to val\n");
write(fd,&val,sizeof(val));
read(fd,&val,sizeof(val));
printf("read again val = %d\n",val);
close(fd);
return 0;
}
逻辑也很简单,就是open,read,write!
3.编写Andoid.mk文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := hello
LOCAL_SRC_FILES := $(call all-subdir-c-files)
include $(BUILD_EXECUTABLE
4.编译生成hello可执行文件
mmm external/hello/
mmm + 你的路径
编译完成后,会生成到:
out/target/product/【项目名】/system/bin/hello
5.使用adb命令push到系统中,并执行
我们把步骤4生成的hello文件拷贝到桌面,并且push到/system/bin目录下
adb root
adb remount
adb push hello /system/bin/
如果你没有权限push到 /syste/bin目录下。请执行以下操作:
adb root
adb remount
adb disable-verity
adb reboot
adb root
adb remount
adb disable-verity
adb push hello /system/bin/
adb chmod 777 /system/bin/hello【添加可执行权限】
adb shell
/system/bin/hello 【运行hello文件】
6.结果