android module驱动编程及通信

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

前言:
android module编译环境搭建及简单设备驱动编写,最后写一个测试POC与驱动通信

一、编译环境搭建

  1. android编译好的内核源码(主要是Kernel相关的头文件)
  2. 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 --->

 

转载于:https://my.oschina.net/u/2424583/blog/775025

你可能感兴趣的:(android module驱动编程及通信)