Android标准架构实例分析之编写最简单的hello驱动
来源:互联网 发布:linux alias 多条命令 编辑:程序博客网 时间:2024/06/05 17:35
Android标准架构实例分析之编写最简单的hello驱动
摘要:
本文主要实现了一个虚拟的字符设备驱动–hello_device 。这个设备驱动会创建相关的cdev数据结构和file_operations
,并通过class_create
和device_create
在sys文件系统上创建相关的目录和文件,为udev创建相关的设备文件提供资源。最终会在/dev/下面创建/dev/hello这个文件节点。并提供文件节点测试程序和测试方法。
- udev工作原理
- 创建并初始化cdev加入到链表中
- 测试代码分析和编译
- 详细代码列表
udev工作原理
udev实现了用户空间动态的方法管理/dev目录。/dev目录是设备目录,里面的文件就是设备文件。udev文件系统在用户空间工作,它可以根据sysfs文件系统导出的信息(设备号(dev)等),动态建立和删除设备文件。而不再需要使用mknod来手动建立设备文件,也不必为查找设备号(尤其是驱动中动态申请产生的设备号)而头疼。
为了能让udev能够正常工作,一个设备驱动程序要做的事情是:通过sysfs将驱动程序所控制设备的主设备号和此设备号导出到用户空间。对于那些使用子系统分配主设备号和次设备号的驱动程序,该工作已经有子系统完成,驱动程序不做任何事情。这样的子系统有:tty,misc,usb,input,scsi,block,i2c,network,framebuffer子系统。如果驱动程序通过调用cdev_init函数,自己处理获得主设备号和次设备号,那么为了能正确的使用udev,需要对驱动程序进行修改。Udev在sysfs中的/class/目录树下搜索名为dev的文件,这样内核通过/sbin/hotplug接口调用它的时候,就能获得分配给特定设备的主设备号和次设备号。
*注:
/dev,设备文件存储目录,应用程序通过对这些文件的读写和控制,可以访问实际的设备;
/sys/devices目录,按照设备挂接的总线类型,组织成层次结构,保存了系统所有的设备;是文件系统管理设备的最重要的目录结构;
/sys/dev下有两个子目录,block和char,存放的是块设备和字符设备的主次号码,形式为(major:minor),它指向/sys/devices目录下的设备。*
字符设备驱动的分析和编译
①主设备编号和次设备编号的申请:
传统上, 主编号标识设备相连的驱动,大部分设备仍然按照一个主编号一个驱动的原则来组织.
次编号被内核用来决定引用哪个设备.
在内核中, dev_t 类型,用来持有设备编号 – 主次部分都包括.
下面的例子是一个动态申请设备编号的过程代码:
/* the major device number */static int hello_major = 0;//主设备编号static int hello_minor = 0;//次设备编号dev_t devno = 0;//devno = MKDEV(int major, int minor); /*动态分配主设备和从设备号*/ret = alloc_chrdev_region(&devno, hello_minor, DEVICE_SUM, "hello");hello_major = MAJOR(devno);hello_minor = MINOR(devno);
通过 /proc/devices 或者 ls -l /dev/ 可以查看设备号和设备信息。
②创建并初始化cdev加入到链表中
struct cdev 是内核的内部结构, 代表字符设备。
下面是一个字符设备最简单的初始化和添加到链表的方式:
/* init the file_operations structure */struct file_operations hello_fops ={ .owner = THIS_MODULE, .open = hello_open, .release = hello_release, .read = hello_read, .write = hello_write,};struct cdev *cdev;cdev = cdev_alloc();cdev->owner = THIS_MODULE;cdev->ops = &hello_fops;
但是, 偶尔你会想将 cdev 结构嵌入一个你自己的设备特定的结构; scull 这样做了. 在这种情况下, 你应当初始化你已经分配的结构, 使用:
void cdev_init(struct cdev *cdev, struct file_operations *fops);
下面是将初始化好的cdev添加到字符设备到链表:
if ((ret = cdev_add(cdev, devno, 1))){ printk(KERN_NOTICE "hello:Error %d adding hello.\n", ret); return 0;}
这里, dev 是 cdev 结构, num 是这个设备响应的第一个设备号, count 是应当关联到设备的设备号的数目. 常常 count 是 1。
③在sysfs中创建一些必要的数据结构
前面已经分析了udev的工作原理,只是①②两个操作是不足以完成设备节点在/dev下面生成,必须要操作sysfs进行一些必要的设备目录和文件的创建,下面是具体的代码:
struct device* temp = NULL;static struct class* hello_class = NULL;hello_class = class_create(THIS_MODULE, "hello"); /*在/dev/目录和/sys/class/hello目录下分别创建设备文件hello*/temp = device_create(hello_class, NULL, devno, "%s", "hello");
如果/sys/class/hello/hello创建成功,udev会导入这些信息并创建/dev/hello设备节点。
测试结果如下图:
④ copy_to_user & copy_from_user
#include <asm/uaccess.h>
这个包含文件声明内核代码使用的函数来移动数据到和从用户空间.
unsigned long copy_from_user (void *to, const void *from, unsigned long count);unsigned long copy_to_user (void *to, const void *from, unsigned long count);
在用户空间和内核空间拷贝数据.
示例代码:
static int global_var = 0; /* global var */if(copy_to_user(buf, &global_var, sizeof(int))){return -EFAULT;}if(copy_from_user(&global_var, buf, sizeof(int))){ return -EFAULT;}
⑤编译
创建同级目录下的Makefile文件,文件内容为:
obj-y += hello.o
在上一级Makefile中添加:
obj-y += hello/
测试代码分析和编译
测试代码主要是对文件节点/dev/hello进行open,read,write操作,主要注意文件的权限问题,使用
adb shell chmod 777 /dev/hello //修改权限
文件的操作详细参考:百度或者谷歌,搜索:Linux 文件操作 系统调用;
编译过程:
将测试代码放在Android /external/hello/文件夹下,编写Android.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)
执行:mmm external/hello/ 会在system/bin/下面生成测试程序可执行文件:hello
执行: make snod打包system.img 下载后,执行测试程序结果:
详细代码列表
驱动文件hello.c
/* * hello.c -- A simple virtual char device driver */#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/slab.h>#include <asm/io.h> #include <linux/device.h>#include <asm/uaccess.h> MODULE_LICENSE("GPL");MODULE_AUTHOR("eliot shao");#define DEVICE_SUM 1static int hello_open(struct inode *inode, struct file *filp);static int hello_release(struct inode *, struct file *filp);static ssize_t hello_read(struct file*, char*, size_t, loff_t*);static ssize_t hello_write(struct file*, const char*, size_t, loff_t*);/* the major device number */static int hello_major = 0;static int hello_minor = 0;static struct class* hello_class = NULL;/* init the file_operations structure */struct file_operations hello_fops ={ .owner = THIS_MODULE, .open = hello_open, .release = hello_release, .read = hello_read, .write = hello_write,};/* define a cdev device */struct cdev *cdev;static int global_var = 0; /* global var *//* module init */static int __init hello_init(void){ int ret = 0; struct device* temp = NULL; dev_t devno = 0; printk("hello:hello_init .\n"); /*动态分配主设备和从设备号*/ ret = alloc_chrdev_region(&devno, hello_minor, DEVICE_SUM, "hello"); if(ret < 0) { printk(KERN_ALERT"hello:Failed to alloc char dev region.\n"); goto fail; } hello_major = MAJOR(devno); hello_minor = MINOR(devno); cdev = cdev_alloc(); cdev->owner = THIS_MODULE; cdev->ops = &hello_fops; if ((ret = cdev_add(cdev, devno, 1))) { printk(KERN_NOTICE "hello:Error %d adding hello.\n", ret); return 0; } else printk("hello:hello register success.\n"); /*在/sys/class/目录下创建设备类别目录hello*/ hello_class = class_create(THIS_MODULE, "hello"); if(IS_ERR(hello_class)) { ret = PTR_ERR(hello_class); printk(KERN_ALERT"Failed to create hello class.\n"); goto destroy_cdev; } /*在/dev/目录和/sys/class/hello目录下分别创建设备文件hello*/ temp = device_create(hello_class, NULL, devno, "%s", "hello"); if(IS_ERR(temp)) { ret = PTR_ERR(temp); printk(KERN_ALERT"Failed to create hello device."); goto destroy_class; } return ret;destroy_class: class_destroy(hello_class);destroy_cdev: cdev_del(cdev);fail: return ret;}/* module exit */static void __exit hello_exit(void){ dev_t devno = MKDEV(hello_major, 0); /* remove cdev from kernel */ cdev_del(cdev); /* unregister the device driver */ unregister_chrdev_region(devno, 1); /* free the dev structure */ if(cdev) kfree(cdev); cdev = NULL;}/* open device */static int hello_open(struct inode *inode, struct file *filp){ int ret = 0; printk("KERNEL:open success.\n"); return ret;}/* release device */static int hello_release(struct inode *inode, struct file *filp){ printk("KERNEL:release success.\n"); return 0;}/* read device */static ssize_t hello_read(struct file *filp, char *buf, size_t len, loff_t *off){ printk("KERNEL:reading...\n"); if(copy_to_user(buf, &global_var, sizeof(int))) { return -EFAULT; } return sizeof(int);}/* write device */static ssize_t hello_write(struct file *filp, const char *buf, size_t len, loff_t *off){ printk("KERNEL:writing...\n"); if(copy_from_user(&global_var, buf, sizeof(int))) { return -EFAULT; } return sizeof(int);}/* module register */module_init(hello_init);module_exit(hello_exit);
测试程序hello.c
#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#define DEVICE_NAME "/dev/hello"int main(int argc, char** argv){ int fd = -1; int val = 0; fd = open(DEVICE_NAME, O_RDWR); if(fd == -1) { printf("Failed to open device %s\n", DEVICE_NAME); return -1; } printf("Read original value:\n"); read(fd, &val, sizeof(val)); printf("%d\n", val); val = 5; printf("Write value %d to %s.\n\n", val, DEVICE_NAME); write(fd, &val, sizeof(val)); printf("Read the value again:\n"); read(fd, &val, sizeof(val)); printf("%d\n", val); close(fd); return 0;}
- Android标准架构实例分析之编写最简单的hello驱动
- Android架构实例分析之编写hello驱动的HAL层代码
- Android架构实例分析之编写hello驱动的系统硬件服务
- 一步一步编写最简单的linux驱动 hello world
- Android架构实例分析之注册hello HAL的JNI方法表
- (OK) Android架构实例分析之注册hello HAL的JNI方法表
- 0704最简单的驱动hello
- Android驱动开发之Hello实例
- 自己动手写最简单的Android驱动---LED驱动的编写
- 自己动手写最简单的Android驱动---LED驱动的编写
- 自己动手写最简单的Android驱动---LED驱动的编写
- 自己动手写最简单的Android驱动---LED驱动的编写
- Linux之hello驱动编写
- C# 最简单的三层架构实例
- 驱动开发第一步,入门,最简单的驱动代码编写
- 《Linux驱动》最简单的驱动编写与makefile
- 教你完成最简单的linux驱动 hello world
- 最简单的驱动hello.c与Makefile模板
- BlockingQueue
- TFS性能测试
- 小技巧,logo使用<img>嵌入
- POJ 1742 Coins
- C++合成默认构造函数的真相
- Android标准架构实例分析之编写最简单的hello驱动
- scala spark hbase 操作案例
- (原创软件)CnCrypt 密码生成工具,提取自CnCrypt的一个独立版本
- 初识DEV控件
- Android View动效地址
- jQuery选择器总结
- HDU-3449 Consumer(依赖背包)
- SQLite数据库操作
- [mt6735]10.1寸 LCD 驱动添加