文档章节

android module驱动编程及通信

stormm
 stormm
发布于 2016/10/24 19:17
字数 1602
阅读 216
收藏 1

前言:
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 <linux/cdev.h>
#include <linux/semaphore.h>

#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 <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>

#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 <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <asm/ioctl.h>

/* 定义幻数 */
#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 --->

 

© 著作权归作者所有

stormm
粉丝 32
博文 22
码字总数 14557
作品 0
昌平
运维
私信 提问
Android WIFI 架构和控制流程

一、WIFI的基本架构 1、wifi用户空间的程序和库: external/wpa_supplicant/ 生成库libwpaclient.so和守护进程wpa_supplicant 2、hardware/libhardware_legary/wifi/是wifi管理库 3、JNI部分:...

鉴客
2011/08/24
9.9K
4
ARetrofit组建化最佳路由

Android组件化超级路由,为简单而生。 Github 源码: ARetrofit 原文 demo demo apk 下载 一 介绍 从命名来看,做Android开发的小伙伴们感觉是不是似曾相识…是的,Retrofit,一款优秀的网络框...

CharlesZhu
2018/06/05
0
0
一篇读懂:Android手机如何通过USB接口与外设通信(附原理分析及方案选型)

0背景 1.手机USB接口通信特点 - 1.1 使用方便 - 1.2 通用性强 - 1.3 速度快 - 1.4 可采用总线供电 2. 手机USB接口通信原理 - 2.1 常见手机USB接口 - 2.2 通信过程 - 2.3 体系架构 2.4软件层次...

HowieXue
2018/01/25
0
0
Android组件化最佳实践 ARetrofit原理

来源丨安卓巴士Android开发者门户 Hello,各位伟大的程序猿们,提前给各位拜年了,祝大家在新的一年收获满满、挣钱多多、猪年吉祥。 开源ARetrofit大约半年左右的时间,没有任何推广和介绍,...

程序员之家_
02/22
0
0
Android HAL模块实现

HAL介绍 Android的HAL(Hardware Abstract Layer硬件抽象层)是为了保护一些硬件提供商的知识产权而提出的,是为了避开linux的GPL束缚。思路是把控制硬件的动作都放到了Android HAL中,而lin...

HAOMCU
2015/07/03
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot2单元测试

引入maven依赖 引入powermock是为了解决静态方法mock的问题。 <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.......

一刀
14分钟前
2
0
webpack优化公司内部分享ppt小记

Webpack整个的构建过程是基于事件流 Webpack 就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关...

莫西摩西
15分钟前
1
0
DevOps落地实践,BAT系列,敏捷看板

DevOps 自 2009 年诞生以来,至今整整过去了十年,从最初的摸索,逐步变成一种主流的软件开发交付模式。BAT在2014年左右,甚至更早的时候,内部的DevOps系统就已经差不多成型了,比如腾讯的织...

cs平台
20分钟前
2
0
Stylus 简单应用

1、安装 cnpm install stylus 2、基本语法 stylus中文版参考文档 3、命令行下编译 styl 文件,生成压缩的css文件 编译src目录下styl文件 stylus --compress /src 更多命令可以执行查找 styl...

微笑吔彷徨
21分钟前
2
0
Java正则表达式大全

1、匹配中文:[\u4e00-\u9fa5] 2、英文字母:[a-zA-Z] 3、数字:[0-9] 4、匹配中文,英文字母和数字及下划线:^[\u4e00-\u9fa5_a-zA-Z0-9]+$ 同时判断输入长度: [\u4e00-\u9fa5_a-zA-Z0-9_]{4...

叶湘伦
29分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部