前言:
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 --->