Android 学习-Driver in kernel-1

参考老罗的安卓之旅,基本一样:

http://blog.csdn.net/luoshengyang/article/details/6568411

平台是mt8163 , Android M0 ,kernel-3.18


1,驱动

创建目录kernel-3.18\drivers\misc\mediatek\hello,hello目录就是本次的简易驱动。

在该目录内创建:hello.c  和 hello.h

hello.c如下,注释了proc/hello部分,添加了IOCTL:

#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 <linux/slab.h>
#include <asm/uaccess.h>

#include "hello.h"

/*主设备和从设备号变量*/
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);
static long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static long hello_ioctl_compat(struct file *file, 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,
#ifdef CONFIG_COMPAT
	.compat_ioctl = hello_ioctl_compat,
#endif
};

/*访问设置属性方法*/
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);



/*打开设备方法*/
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;
}

/*读取设备的寄存器val的值*/
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;
	}        

	/*将寄存器val的值拷贝到用户提供的缓冲区*/
	if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {
		err = -EFAULT;
		goto out;
	}

	err = sizeof(dev->val);

out:
	up(&(dev->sem));
	return err;
}

/*写设备的寄存器值val*/
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;
}

static long hello_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	void __user *argp = (void __user *)arg;
	HELLO_ARG hello_arg;
	static int val = 0;
	int ret = 0;

	pr_err("[hello]ioctl=%d, arg = %lu\n", cmd, arg);

	switch (cmd) {
	case HELLO_READ_WRITE: {
			if (copy_from_user(&hello_arg, argp, sizeof(HELLO_ARG))) {
				pr_err("[hello]ioctl copy_from_user failed! line:%d\n", __LINE__);
				ret = -EFAULT;
			} else {
				hello_arg.times = hello_arg.times + 1;
				if(hello_arg.write_data != 0) {
					val += hello_arg.write_data;
				}
				hello_arg.read_data = val;

				if (copy_to_user(argp, &hello_arg, sizeof(HELLO_ARG))) {
						pr_err("[hello]ioctl copy_to_user failed! line:%d\n", __LINE__);
						ret = -EFAULT;
				}
			}
			pr_err("[hello]ioctl hello_arg.read_data=%d,hello_arg.write_data=%d,hello_arg.times=%d\n", hello_arg.read_data, hello_arg.write_data, hello_arg.times);
			break;
		}
	default: {
			pr_err("[hello]ioctl(%d) arguments is not support\n", cmd);
			ret = -EFAULT;
			break;
		}
	}
	return ret;
}

static long hello_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) {
	return 0;
}


/*读取寄存器val的值到缓冲区buf中,内部使用*/
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);
}

/*把缓冲区buf的值写到设备寄存器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;
}

/*读取设备属性val*/
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);
}

/*写设备属性val*/
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);
}



#if 0
/*读取设备寄存器val的值,保存在page缓冲区中*/
static ssize_t hello_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) {
	if(off > 0) {
		*eof = 1;
		return 0;
	}

	return __hello_get_val(hello_dev, page);
}

/*把缓冲区的值buff保存到设备寄存器val中去*/
static ssize_t hello_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) {
	int err = 0;
	char* page = NULL;

	if(len > PAGE_SIZE) {
		printk(KERN_ALERT"The buff is too large: %lu.\n", len);
		return -EFAULT;
	}

	page = (char*)__get_free_page(GFP_KERNEL);
	if(!page) {                
		printk(KERN_ALERT"Failed to alloc page.\n");
		return -ENOMEM;
	}        

	/*先把用户提供的缓冲区值拷贝到内核缓冲区中去*/
	if(copy_from_user(page, buff, len)) {
		printk(KERN_ALERT"Failed to copy buff from user.\n");                
		err = -EFAULT;
		goto out;
	}

	err = __hello_set_val(hello_dev, page, len);

out:
	free_page((unsigned long)page);
	return err;
}

/*创建/proc/hello文件*/
static void hello_create_proc(void) {
	struct proc_dir_entry* entry;
	
	entry = create_proc_entry(HELLO_DEVICE_PROC_NAME, 0, NULL);
	if(entry) {
		entry->owner = THIS_MODULE;
		entry->read_proc = hello_proc_read;
		entry->write_proc = hello_proc_write;
	}
}

/*删除/proc/hello文件*/
static void hello_remove_proc(void) {
	remove_proc_entry(HELLO_DEVICE_PROC_NAME, NULL);
}

#endif

/*初始化设备*/
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;
	}        

	/*初始化信号量和寄存器val的值*/
	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);        

	/*分配helo设备结构体变量*/
	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;
	}        

	/*在/sys/class/目录下创建设备类别目录hello*/
	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;
	}        

	/*在/dev/目录和/sys/class/hello目录下分别创建设备文件hello*/
	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;
	}        

	/*在/sys/class/hello/hello目录下创建属性文件val*/
	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);        

	/*创建/proc/hello文件*/
//	hello_create_proc();

	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");        

	/*删除/proc/hello文件*/
//	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);


hello.h如下,添加了一些下次要用的IOCTL的东西:

#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;
};

typedef struct _HELLO_ARG {
	int times;
	int read_data;
	int write_data;
}HELLO_ARG;

#define HELLO_IOW(num, dtype)     _IOW('h', num, dtype)
#define HELLO_IOR(num, dtype)     _IOR('h', num, dtype)
#define HELLO_IOWR(num, dtype)    _IOWR('h', num, dtype)
#define HELLO_IO(num)             _IO('h', num)

#define HELLO_READ_WRITE                 HELLO_IOWR(1, HELLO_ARG)

#endif


2,Makefile和Kconfig

2.1,要让“hello”整个目录参与编译。

修改kernel-3.18\drivers\misc\mediatek目录下的Makefile,在末尾添加一行  obj-y += hello/


2.2,添加“hello”目录内的Makefile

在kernel-3.18\drivers\misc\mediatek\hello目录下,添加Makefile和Kconfig。

Makefile如下,CONFIG_MTK_HELLO_SUPPORT没吃到,所以注释了:

#obj-$(CONFIG_MTK_HELLO_SUPPORT) += hello.o
obj-y += hello.o


Kconfig如下

config MTK_HELLO_SUPPORT
tristate "First Android Driver"
default n
help
	This is the first android driver.


3,编译

source build/envsetup.sh && lunch full-tb8163p1_64-eng && make -j24 2>&1 | tee build.log

4,初步测试

按照老罗的方式检验驱动是否加载成功,本文注释了proc/hello部分。

老罗地址:http://blog.csdn.net/luoshengyang/article/details/6568411


5,使用可执行bin测试

5.1  可执行bin代码

创建external\hello目录,在该目录内创建“hello_program.c”和“Android.MK”

hello_program.c如下:

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string.h>

typedef unsigned char bool;

/*#include "hello.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"


typedef struct _HELLO_ARG {
	int times;
	int read_data;
	int write_data;
}HELLO_ARG;

#define HELLO_IOW(num, dtype)     _IOW('h', num, dtype)
#define HELLO_IOR(num, dtype)     _IOR('h', num, dtype)
#define HELLO_IOWR(num, dtype)    _IOWR('h', num, dtype)
#define HELLO_IO(num)             _IO('h', num)

#define HELLO_READ_WRITE                 HELLO_IOWR(1, HELLO_ARG)

static int hello_read_write(HELLO_ARG *hello_arg);

static const char *pHelloDev = "/dev/hello";
static char *pFilePath;
static unsigned int u4Addr;
static unsigned int u4Data;


typedef  enum
{
   write_register_cmd=0,
   read_write_cmd,
   cmd_nubmer
}   hdmitx_debug_mode;

typedef struct _TEXT2ENUM_T
{
    char* szText;
    unsigned int i4Cmd;
	char* szHelp;
} TEXT2ENUM_T;

static TEXT2ENUM_T _arDebugModeEnumTbl[] =
{
	{ "rw",     read_write_cmd,     "(hello_read_write)res (resolution/help)1/0xff"},
    { NULL, -1, NULL }
};

static unsigned int comparestr(const char *szText, const TEXT2ENUM_T* prText2Enum)
{
    if ((NULL == szText) ||
        (NULL == prText2Enum))
    {
        return 0;
    }

    while (prText2Enum->szText)
    {
        if((strlen(prText2Enum->szText)==strlen(szText))&&(strncmp(prText2Enum->szText, szText,  strlen(prText2Enum->szText)) == 0))
        {
            break;
        }
        else
        {
            prText2Enum++;
        }
    }

    return prText2Enum->i4Cmd;
}

int main(int argc, char *argv[])
{
    int ret = 0;
    unsigned int read, write, times, i4cmd, i;
    char *pEnd;
    TEXT2ENUM_T* prTextEnum;
    HELLO_ARG hello_arg;

		prTextEnum = _arDebugModeEnumTbl;
	
    if (argc > 2) {
		i4cmd = comparestr(argv[1], prTextEnum);
		
        switch(i4cmd) {
				case read_write_cmd:
					read = strtoul(argv[2],&pEnd,16);
					write = strtoul(argv[3],&pEnd,16);
					times = strtoul(argv[4],&pEnd,16);
					printf("[hello_read_write] read:0x%04x,write:0x%04x,times:%d\n",read,write,times);
					hello_arg.read_data = read;
					hello_arg.write_data = write;
					hello_arg.times = 0;
					for(i = 0; i < times; i++) {
						hello_read_write(&hello_arg);
						printf("[hello_read_write] done i=%d,hello_arg.read_data:0x%04x,hello_arg.write_data:0x%04x,times=%d\n",i, hello_arg.read_data,hello_arg.write_data,hello_arg.times);
					}
					break;
	
	    	default:
	    		printf("please input correct mode\n");
	    		ret = -1;
	    		break;			
        }
    } else {
        printf("==============================================================\n");
        ret = -1;
    } 

	return ret;
}



static int hello_read_write(HELLO_ARG *hello_arg)
{
	int fpHello;
	int ret;
	fpHello = open(pHelloDev, O_RDWR, 0);
	
	if (fpHello >= 0) {
	    ret = ioctl(fpHello, HELLO_READ_WRITE, hello_arg);
	    if (ret == -1) {
	        return 0;
	    }
	    close(fpHello);
	} else {
	    return 0;
	}
	return ret;	
}

Android.MK如下:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) $(LOCAL_PATH) device/mediatek/common/kernel-headers/linux
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:= hello_program.c
LOCAL_MODULE:= hello
include $(BUILD_EXECUTABLE)
OUTSYSTEMBINTEST := $(TARGET_OUT)/bin/hello


5.2  编译可执行bin

在根目录且已经source 和 lunch过,编译命令如下:

mmm ./external/hello

生成的“hello”在目录“output/xxxxx/xxx/hello”内

5.2  测试可执行bin

将hello的执行档拷贝到平板上测试

adb push hello /data
adb shell
cd /data
chmod 777 hello
./hello rw 1 2 5



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值