前言:
android module编译环境搭建及简单设备驱动编写,最后写一个测试POC与驱动通讯node
在Linux机器上新建个目录,添加hello.h、hello.c、Makefile。linux
hello.handroid
#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.cshell
#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控制bash
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); }
设备安装函数ui
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);
Makefilespa
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.
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 --->