2019独角兽企业重金招聘Python工程师标准>>>
前言:
android module编译环境搭建及简单设备驱动编写,最后写一个测试POC与驱动通信
一、编译环境搭建
- android编译好的内核源码(主要是Kernel相关的头文件)
- android NDK编译环境(>4.8)
二、例子
2.1 android驱动程序编写
在Linux机器上新建个目录,添加hello.h、hello.c、Makefile。
hello.h
#ifndef _HELLO_ANDROID_H_
#define _HELLO_ANDROID_H_
#include
#include
#define HELLO_DEVICE_NODE_NAME "hello"
#define HELLO_DEVICE_FILE_NAME "hello"
#define HELLO_DEVICE_PROC_NAME "hello"
#define HELLO_DEVICE_CLASS_NAME "hello"
struct hello_android_dev {
int val;
struct semaphore sem;
struct cdev dev;
};
#endif
hello.c
#include
#include
#include
#include
#include
#include
#include
#include "hello.h"
//模数
#define MEMDEV_IOC_MAGIC 'k'
//命令ID
#define MEMDEV_IOCPRINT _IO(MEMDEV_IOC_MAGIC, 1) //IO
#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int) //读
#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int) //写
//命令总数
#define MEMDEV_IOC_MAXNR 3
//设备版本
static int hello_major = 0;
static int hello_minor = 0;
static struct class* hello_class = NULL;
static struct hello_android_dev* hello_dev = NULL;
//打开设备的函数
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);
//ioctl控制函数
static long hello_ioctl (struct file *filp, unsigned int cmd, unsigned long arg);
//设备操作选项(各种函数指针)
static struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.read = hello_read,
.write = hello_write,
.unlocked_ioctl = hello_ioctl
};
//设备信息展示
static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf);
//设备信息存储
static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);
//设备属性
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store);
ioctl控制
static long hello_ioctl (struct file *filp, unsigned int cmd, unsigned long arg) {
int err = 0;
int ret = 0;
int ioarg = 0;
//判断命令是否合法
if (_IOC_NR(cmd) > MEMDEV_IOC_MAXNR)
return -EINVAL;
//判断地址是否有权限读写
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
switch(cmd) {
//打印命令
case MEMDEV_IOCPRINT:
printk("<--- CMD MEMDEV_IOCPRINT Done--->\n\n");
break;
//获取数据处理
case MEMDEV_IOCGETDATA:
ioarg = 1101;
ret = __put_user(ioarg, (int *)arg);
break;
//写数据处理
case MEMDEV_IOCSETDATA:
ret = __get_user(ioarg, (int *)arg);
printk("<--- In Kernel MEMDEV_IOCSETDATA ioarg = %d --->\n\n",ioarg);
break;
default:
return -EINVAL;
}
return ret;
}
其它操作函数
//打开设备,将设备信息保存
static int hello_open(struct inode* inode, struct file* filp) {
struct hello_android_dev* dev;
dev = container_of(inode->i_cdev, struct hello_android_dev, dev);
filp->private_data = dev;
return 0;
}
//释放没处理
static int hello_release(struct inode* inode, struct file* filp) {
return 0;
}
//读取设备中数据 file->private_data为dev设备数据
static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {
ssize_t err = 0;
struct hello_android_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;
}
//写设备中数据 file-.private_data为dev设备数据
static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {
struct hello_android_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;
}
val读写相关
static ssize_t __hello_get_val(struct hello_android_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);
}
err = sizeof(dev->val);
out:
up(&(dev->sem));
return err;
}
static ssize_t __hello_get_val(struct hello_android_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 __hello_set_val(struct hello_android_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 hello_val_show(struct device* dev, struct device_attribute* attr, char* buf) {
struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev);
return __hello_get_val(hdev, buf);
}
static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {
struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev);
return __hello_set_val(hdev, buf, count);
}
设备安装函数
static int __hello_setup_dev(struct hello_android_dev* dev) {
int err;
dev_t devno = MKDEV(hello_major, hello_minor);
memset(dev, 0, sizeof(struct hello_android_dev));
cdev_init(&(dev->dev), &hello_fops);
dev->dev.owner = THIS_MODULE;
dev->dev.ops = &hello_fops;
err = cdev_add(&(dev->dev),devno, 1);
if(err) {
return err;
}
//init_MUTEX(&(dev->sem));
sema_init(&(dev->sem), 1);
dev->val = 0;
return 0;
}
static int __init hello_init(void){
int err = -1;
dev_t dev = 0;
struct device* temp = NULL;
printk(KERN_ALERT"Initializing hello device.\n");
err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME);
if(err < 0) {
printk(KERN_ALERT"Failed to alloc char dev region.\n");
goto fail;
}
hello_major = MAJOR(dev);
hello_minor = MINOR(dev);
hello_dev = kmalloc(sizeof(struct hello_android_dev), GFP_KERNEL);
if(!hello_dev) {
err = -ENOMEM;
printk(KERN_ALERT"Failed to alloc hello_dev.\n");
goto unregister;
}
err = __hello_setup_dev(hello_dev);
if(err) {
printk(KERN_ALERT"Failed to setup dev: %d.\n", err);
goto cleanup;
}
hello_class = class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME);
if(IS_ERR(hello_class)) {
err = PTR_ERR(hello_class);
printk(KERN_ALERT"Failed to create hello class.\n");
goto destroy_cdev;
}
temp = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME);
if(IS_ERR(temp)) {
err = PTR_ERR(temp);
printk(KERN_ALERT"Failed to create hello device.");
goto destroy_class;
}
err = device_create_file(temp, &dev_attr_val);
if(err < 0) {
printk(KERN_ALERT"Failed to create attribute val.");
goto destroy_device;
}
dev_set_drvdata(temp, hello_dev);
printk(KERN_ALERT"Succedded to initialize hello device.\n");
return 0;
destroy_device:
device_destroy(hello_class, dev);
destroy_class:
class_destroy(hello_class);
destroy_cdev:
cdev_del(&(hello_dev->dev));
cleanup:
kfree(hello_dev);
unregister:
unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1);
fail:
return err;
}
static void __exit hello_exit(void) {
dev_t devno = MKDEV(hello_major, hello_minor);
printk(KERN_ALERT"Destroy hello device.\n");
hello_remove_proc();
if(hello_class) {
device_destroy(hello_class, MKDEV(hello_major, hello_minor));
class_destroy(hello_class);
}
if(hello_dev) {
cdev_del(&(hello_dev->dev));
kfree(hello_dev);
}
unregister_chrdev_region(devno, 1);
}
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("First Android Driver");
module_init(hello_init);
module_exit(hello_exit);
Makefile
obj-m := hello.o
KERNELDIR := /media/sda5/android/kernel/msm/msm/
PWD :=$(shell pwd)
ARCH=arm
CROSS_COMPILE=/media/android/kernel/arm-eabi-4.8/bin/arm-eabi-
CC=$(CROSS_COMPILE)gcc
LD=$(CROSS_COMPILE)ld
#No-pic否则报segment error
CFLAGS_MODULE=-fno-pic
modules:
make -C $(KERNELDIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) modules
clean:
rm *.o *.ko *.mod.c *.order *.symvers
切换到程序根目录,然后编译即可生成ko模块文件,如下:
$ make
make -C /media/sda5/android/kernel/msm/msm/ ARCH=arm CROSS_COMPILE=/media/sda5/android/kernel/arm-eabi-4.8/bin/arm-eabi- M=/media/jowto/sda5/android/kernel/module modules
make[1]: Entering directory '/media/sda5/android/kernel/msm/msm'
CC [M] /media/sda5/android/kernel/module/hello.o
Building modules, stage 2.
MODPOST 1 modules
CC /media/sda5/android/kernel/module/hello.mod.o
LD [M] /media/sda5/android/kernel/module/hello.ko
make[1]: Leaving directory `/media/sda5/android/kernel/msm/msm'
#### make completed successfully (1 seconds) ####
将编译完的hello.ko传到手机设备上,加载模块如下:
#切换到root用户
$ su
#安装模块
$ insmod /data/local/tmp/hello.ko
#当前模块列表
$ lsmod
Module Size Used by
hello 4064 0
#查看日志
$ dmesg |grep hello
[ 8889.108997] Destroy hello device.
[ 8903.052067] Initializing hello device.
[ 8903.053140] Succedded to initialize hello device.
2.2 android测试程序与驱动通信
test_conn.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 定义幻数 */
#define MEMDEV_IOC_MAGIC 'k'
/* 定义命令 */
#define MEMDEV_IOCPRINT _IO(MEMDEV_IOC_MAGIC, 1)
#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)
#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)
void read_ioctl(int fd)
{
int ret;
int cmd;
int read_i;
cmd = MEMDEV_IOCGETDATA;
ret = ioctl(fd, cmd, &read_i);
if (ret < 0) {
printf("read ioctl fail %s\n",strerror(errno));
}
else
{
printf("config %d %d \n", read_i);
}
}
int main(int argc, char *argv[])
{
char* path = "/dev/hello";
int fd;
fd = open(path, O_RDWR);
if(fd<0) {
printf("open fail %s\n",strerror(errno));
return -1;
}
printf("open %s succ\n",path);
//直接IO
ioctl(fd, MEMDEV_IOCPRINT);
int xx = 999;
//向驱动设备写
ioctl(fd, MEMDEV_IOCSETDATA, &xx);
//读取设备
read_ioctl(fd);
close(fd);
return 0;
}
编译成可执行文件test_conn后放入手机进行测试,如下:
# ./test_conn
open /dev/hello succ
config 1101
查看日志
# dmesg |grep MEMDEV
[ 9166.054062] <--- CMD MEMDEV_IOCPRINT Done--->
[ 9166.054173] <--- In Kernel MEMDEV_IOCSETDATA ioarg = 999 --->