zoukankan      html  css  js  c++  java
  • 从LINUX 驱动 到APK (1)

       实现一个简单的内核驱动

      此次实现基于MTK8173平台Android 6.0 实现。

      此次使用一个虚拟的硬件设备,这个设备只有一个4字节的寄存器,它可读可写,移植的角度来实现我们将其命名为welcome。在kernel部分要实现一个驱动主要分为,添加编译选择选项,实现对应的相关驱动,添加进入内核编译。

      1,、添加编译选项

        此项选择的源码存放的路径为 kernel-3.18driversmiscmediatek目录

      1.1 修改Kconfig文件 再合适的位置添加 

    config MTK_WELCOME
        bool "CONFIG_MTK_WELCOME"
        default n
        help
         just for driver test.

      1.2 修改同目录下的makefile文件

        添加:obj-$(CONFIG_MTK_WELCOME) += welcome/

      1.3 新建目录welcome 用于单独存放此次的驱动代码,方便管理

      2、实现对应的welcome驱动

      2.1 添加相应的头文件 welcome.h

    #ifndef _WELCOME_KERNEL_H_
    #define _WELCOME_KERNEL_H_

    //选择是否需要时会用信号量 用于访问控制
    //#define CONFIG_WELCOME_SEM_MUTEX
    #include <linux/cdev.h>
    #include <linux/semaphore.h>
    
    // /sys/class/welcomc/welcomf
    
    #define WELCOME_DEVICE_NODE_NAME "welcom"
    // /dev/WELCOME_DEVICE_FILE_NAME
    #define WELCOME_DEVICE_FILE_NAME "welcomf"
    // /proc/WELCOME_DEVICE_PROC_NAME
    #define WELCOME_DEVICE_PROC_NAME "welcomp"
    // /sys/class/WELCOME_DEVICE_CLASS_NAME
    #define WELCOME_DEVICE_CLASS_NAME "welcomc"
    
    #define CONFIG_WELCOME_PROC
    //定义私有结构体
    struct welcome_kernel_dev{
        int val;
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        struct semaphore sem;
    #endif
        struct cdev dev;
    };
    
    
    #endif//_WELCOME_KERNEL_H_

      2.2 新建welcome.c

        

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/types.h>
    #include <linux/device.h>
    #include <linux/slab.h>
    #include <asm/uaccess.h>
    #include <linux/fs.h>
    
    //#include <linux/ioctl.h>
    #include "bug_log.h"
    #include "welcome.h"
    
    #ifdef CONFIG_WELCOME_PROC
    #include <linux/proc_fs.h>
    #endif
    
    // 定义主从设备号变量
    static int welcome_major = 0;
    static int welcome_minor = 0;
    
    //设备类别和设备变量
    static struct class* welcome_class = NULL;
    static struct  welcome_kernel_dev* welcome_dev = NULL;
    // 传统的设备文件操作方法
    static int welcome_open(struct inode* inode,struct file* filp);
    static int welcome_release(struct inode* inode,struct file* filp);
    static ssize_t welcome_read(struct file* filp,char __user *buf,size_t count,loff_t* f_pos);
    static ssize_t welcome_write(struct file* filp,const char __user *buf,size_t count,loff_t* f_pos);
    
    //设备文件操作方法表
    static struct file_operations welcome_fops = {
        .owner = THIS_MODULE,
        .open = welcome_open,
        .release = welcome_release,
        .read = welcome_read,
        .write = welcome_write,
    };
    
    // 属性访问设置
    static ssize_t welcome_val_show(struct device* dev,struct device_attribute* attr,char* buf);
    static ssize_t welcome_val_store(struct device* dev,struct device_attribute* attr,const char* buf,size_t count);
    
    /*
    定义两个内部使用的访问 val值的方法
    */
    //读寄存器val 的值到缓冲区buf中
    static ssize_t _welcome_get_val(struct welcome_kernel_dev* dev, char* buf){
        int val = 0;
        int ret = 0;
        //同步访问
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        if(down_interruptible(&(dev->sem))){
            return -ERESTARTSYS;
        }
    #endif
        val = dev->val;
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        up(&(dev->sem));
    #endif
        sprintf(buf,"%d
    ",val);
        ret = 2;//strlen(buf) + 1;
    //ret 决定输出字符个数
        return ret;
    }
    //把缓冲区buf的值写到寄存器val中
    static ssize_t _welcome_set_val(struct welcome_kernel_dev* dev, const char* buf, size_t count) {
        int val = 0;
        //将字符串转换成数字
        val = simple_strtol(buf,NULL,10);
        //同步访问
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        if(down_interruptible(&(dev->sem))){
            return -ERESTARTSYS;
        }
    #endif
        dev->val = val;
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        up(&(dev->sem));
    #endif
        return count;
    }
    //读取设备属性
    static ssize_t welcome_val_show(struct device* dev,struct device_attribute* attr,char* buf){
    #if 1 //打印数字方案
        struct welcome_kernel_dev* hdev = welcome_dev;//(struct welcome_kernel_dev*)dev_get_drvdata(dev);
        return _welcome_get_val(hdev,buf);
    #else
        //打印字符串方案
        ssize_t ret = 0;
        unsigned int uc_reg_value;
    
        uc_reg_value = welcome_dev->val;
    
        sprintf(buf, "gt5x , firmware id : 0x%02x
    ", uc_reg_value);
    
        ret = strlen(buf) + 1;
    
        return ret;
    #endif
    }
    //写设备属性
    static ssize_t welcome_val_store(struct device* dev,struct device_attribute* attr,const char* buf,size_t count){
        struct welcome_kernel_dev* hdev = welcome_dev;
        return _welcome_set_val(hdev,buf,count);    
    }
    
     
    //定义设备属性不能是666 会报bug.h 错误
    static DEVICE_ATTR(val,S_IRUGO | S_IWUSR,welcome_val_show,welcome_val_store);
    #if ENABLE_BUG_LOG
    //读取设备属性
    static ssize_t bugLevel_val_show(struct device* dev,struct device_attribute* attr,char* buf){
        //打印字符串方案
        ssize_t ret = 0;
        sprintf(buf, " DBUG_E:3  DBUG_D:2 DBUG_I:1: Kernel_BugLevel=%d
    ", Kernel_BugLevel);
    
        ret = strlen(buf) + 1;
    
        return ret;
    }
    //写设备属性
    static ssize_t bugLevel_val_store(struct device* dev,struct device_attribute* attr,const char* buf,size_t count){
        int val = 0;
        //将字符串转换成数字
        Kernel_BugLevel = simple_strtol(buf,NULL,10);
    
        return count;    
    }
    
     
    //定义设备属性不能是666 会报bug.h 错误
    static DEVICE_ATTR(buglevel,S_IRUGO | S_IWUSR,bugLevel_val_show,bugLevel_val_store);
    
    #endif
    //打开设备方法
    static int welcome_open(struct inode* inode,struct file* filp){
        struct welcome_kernel_dev* dev;
        //将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用
        dev = container_of(inode->i_cdev,struct welcome_kernel_dev,dev);
        filp->private_data = dev;
    
        return 0;
    
    }
    //设备文件释放时调用
    static int welcome_release(struct inode* inode,struct file* filp){
        return 0;
    }
    //读取设备文件寄存器val 的值
    static ssize_t welcome_read(struct file* filp,char __user *buf,size_t count,loff_t* f_pos){
        ssize_t err = 0;
        struct welcome_kernel_dev* dev = filp->private_data;
    
        //同步访问
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        if(down_interruptible(&(dev->sem))){
            return -ERESTARTSYS;
        }
    #endif
        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:
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        up(&(dev->sem));
    #endif
        return err;
    }
    
    //写设备的寄存器val
    static ssize_t welcome_write(struct file* filp,const char __user *buf,size_t count,loff_t* f_pos){
        struct welcome_kernel_dev* dev = filp->private_data;
        ssize_t err = 0;
        //同步访问
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        if(down_interruptible(&(dev->sem))){
            return -ERESTARTSYS;
        }
    #endif
        if(count != sizeof(dev->val)){
            goto out;
        }
        // 将用户提供的缓冲区写到设备寄存器
        if(copy_from_user(&(dev->val),buf,count)){
            err = -EFAULT;
            goto out;
    
        }
        //返回数据大小
        err = sizeof(dev->val);
    out:
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        up(&(dev->sem));
    #endif
        return err;
    }
    
    
    
    /*********************proc 文件系统访问方法*********************/
    //读取设备寄存器val 的值 保存在page 缓冲区
    #ifdef CONFIG_WELCOME_PROC
    static ssize_t welcome_proc_read(struct file* filp,char __user *buf,size_t count,loff_t* f_pos){
        ssize_t err = 0;
        char* page = NULL;
        int val = welcome_dev->val;
        struct welcome_kernel_dev* dev = welcome_dev;
    
        //同步访问
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        if(down_interruptible(&(dev->sem))){
            return -ERESTARTSYS;
        }
    #endif
        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:
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        up(&(dev->sem));
    #endif
        return snprintf(buf, sizeof(val)+1, "%d
    ", val);
    }
    
    //把缓冲区的值buff 保存到设备寄存器val 中
    static ssize_t welcome_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) {
        int err = 0;
        char* page = NULL;
        DBUG_E("DBUG_E welcome_write loglevel = %d 
    ",Kernel_BugLevel);
        DBUG_I("DBUG_I welcome_write loglevel = %d 
    ",Kernel_BugLevel);
        DBUG_D("DBUG_D welcome_write loglevel = %d 
    ",Kernel_BugLevel);
        if(len > PAGE_SIZE){
            DBUG_E("The buff is too large:%lu.
    ",len);
            return -EFAULT;
        }
        page = (char*)__get_free_page(GFP_KERNEL);
        if(!page){
            DBUG_E("Failed to alloc page. 
    ");
            return -ENOMEM;
        }
        //先把用户提供的缓冲区的值拷贝到内核缓冲区
        if(copy_from_user(page,buff,len)){
            DBUG_E("Failed to copy buff from user.
    ");
            err = -EFAULT;
            goto out;
        }
        err = _welcome_set_val(welcome_dev,page,len);
    out:
        free_page((unsigned long) page);
        return err;
    }
    static int welcome_proc_open(struct inode *inode, struct file *file)
    {
        return single_open(file, NULL, NULL);
    }
    
    static const struct file_operations welcome_proc_fops = {
    //    .open = welcome_proc_open,
        .read = welcome_proc_read,
        .write = welcome_proc_write,
    };
    
    // 创建/proc/welcome 文件
    static void welcome_create_proc(void){
        struct proc_dir_entry* entry ;
    
        //entry = create_proc_entry(WELCOME_DEVICE_PROC_NAME,0,NULL);
        entry = proc_create(WELCOME_DEVICE_PROC_NAME, 0666, NULL,&welcome_proc_fops);  
    
    }
    //删除/proc/welcome 文件
    static void welcome_remove_proc(void){
        remove_proc_entry(WELCOME_DEVICE_PROC_NAME,NULL);
    }
    
    #endif
    /*********************proc 文件系统访问方法*********************/
    
    //定义模块加载和卸载方法 
    // 初始化设备
    
    static int __welcome_setup_dev(struct welcome_kernel_dev* dev){
        int err;
        dev_t devno = MKDEV(welcome_major,welcome_minor);
        memset(dev,0,sizeof(struct welcome_kernel_dev));
        cdev_init(&(dev->dev),&welcome_fops);
        dev->dev.owner = THIS_MODULE;
        dev->dev.ops = &welcome_fops;
        //注册字符设备
        err = cdev_add(&(dev->dev),devno,1);
        if(err){
            return err;
        }
        //初始化信号量 和寄存器val
    #ifdef CONFIG_WELCOME_SEM_MUTEX
        //init_MUTEX(&(dev->sem));  编译报错 新内核没有该API
        sema_init(&(dev->sem),1);
    #endif
        dev->val = 0;
        return 0;
    }
    //模块加载方法
    static int __init welcome_init(void){
        int err = -1;
        dev_t devno = 0;
        struct device* device_temp = NULL;
        DBUG_I("Initializing welcome device.
    ");
        //动态分配主设备号 和从设备号
        err = alloc_chrdev_region(&devno,0,1,WELCOME_DEVICE_NODE_NAME);
        if(err < 0){
            printk(KERN_ALERT"Failed to alloc char dev region.
    ");
            goto fail;
        }
        welcome_major = MAJOR(devno);
        welcome_minor = MINOR(devno);
    
        //分配welcome 设备机构体变量
        welcome_dev = kmalloc(sizeof(struct welcome_kernel_dev),GFP_KERNEL);
        if(!welcome_dev){
            err = -ENOMEM;
            DBUG_E("Failed to alloc welcome_dev.
    ");
            goto unregister;
        }
        //初始化设备
        err = __welcome_setup_dev(welcome_dev);
        if(err){
            DBUG_E("Failed to setup dev :%d .
    ",err);
            goto cleanup;
        }
    
        //在/sys/class 目录下创建设备类别目录welcome
        welcome_class = class_create(THIS_MODULE,WELCOME_DEVICE_CLASS_NAME);
        if(IS_ERR(welcome_class)){
            err = PTR_ERR(welcome_class);
            DBUG_E("Failed to create welcome class.
    ");
            goto destroy_cdev;
        }
        //在/dev 目录和/sys/class/welcome  目录下分别创建设备文件welcome
        //device_temp = device_create(welcome_class,NULL,0,NULL,WELCOME_DEVICE_FILE_NAME);// 不会在dev目录下创建节点
        device_temp = device_create(welcome_class,NULL,devno,NULL,WELCOME_DEVICE_FILE_NAME);
        if(IS_ERR(device_temp)){
            err = PTR_ERR(device_temp);
            DBUG_E("Failed to create welcome device.
    ");
            goto destroy_class;
        }
        //在/sys/class/welcome/welcome 目录下创建属性文件val
        err =device_create_file(device_temp,&dev_attr_val);
        if(err < 0){
            DBUG_E("Failed to create attribute val. 
    ");
            goto destroy_device;
        }
        #if ENABLE_BUG_LOG
        err =device_create_file(device_temp,&dev_attr_buglevel);
        if(err < 0){
            DBUG_E("Failed to create attribute buglevel. 
    ");
            goto destroy_device;
        }
        #endif
        dev_set_drvdata(device_temp,NULL);
        //device_temp->driver_data =(void*) welcome_dev;
        //创建/proc/welcome 文件
    #ifdef CONFIG_WELCOME_PROC
        welcome_create_proc();
    #endif
        DBUG_I("Succed to inintialize welcome device . 
    ");
    
        return 0;//正常退出
        
    destroy_device:
        device_destroy(welcome_class,devno);
    destroy_class:
        class_destroy(welcome_class);
    destroy_cdev:
        cdev_del(&(welcome_dev->dev));
    cleanup:
        kfree(welcome_dev);
    unregister:
            //释放设备号
        unregister_chrdev_region(devno,1);
    fail:
        return err;
    }
    //模块卸载方法
    static void __exit welcome_exit(void){
        dev_t devno = MKDEV(welcome_major,welcome_minor);
        DBUG_I("Destroy hello device.
    ");
        //删除/proc/welcome 文件
    #ifdef CONFIG_WELCOME_PROC
        welcome_remove_proc();
    #endif
        //销毁设备类别和设备
        if(welcome_class){
            device_destroy(welcome_class,devno);
            class_destroy(welcome_class);
        }
        //删除字符设备和释放设备内存
        if(welcome_dev){
            cdev_del(&(welcome_dev->dev));
            kfree(welcome_dev);
        }
        //释放设备号
        unregister_chrdev_region(devno,1);
    }
    
    
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("Weclcome Kernel Driver");
    
    module_init(welcome_init);
    module_exit(welcome_exit);

      2.3 此次驱动的打印信息采用分层打印的思想在 sys 下的节点控制打印登机 添加 bug_log.h

    #ifndef __BUG_KERNEL_LOG_H__
    #define __BUG_KERNEL_LOG_H__
    
    //atlas 是否使能log  enable:1 disable:0
    #define ENABLE_BUG_LOG 1
    
    /** DBUG_E:3  DBUG_D:2 DBUG_I:1**/
    #if ENABLE_BUG_LOG
    unsigned char Kernel_BugLevel=4;
    // 注意if 后面的行数不带{}
    #define  DBUG_E(fmt, args...)        
    do {
        if (Kernel_BugLevel >= 3) 
            printk(KERN_ALERT fmt, ##args); 
    } while (0)  
    
    #define  DBUG_D(fmt, args...)        
    do {
        if (Kernel_BugLevel >= 2) 
            printk(KERN_ALERT fmt, ##args); 
    } while (0)  
    
    #define  DBUG_I(fmt, args...)        
    do {
        if (Kernel_BugLevel >= 1) 
            printk(KERN_ALERT fmt, ##args); 
    } while (0)  
    
    #else
    #define  DBUG_E(fmt, args...)  
    #define  DBUG_D(fmt, args...)  
    #define  DBUG_I(fmt, args...)  
    #endif
    
    #if 0
    //kernel 对应的printk 等级
    #define KERN_EMERG        "<0>" /* system is unusable */
    #define KERN_ALERT         "<1>" /* action must be taken immediately */
    #define KERN_CRIT            "<2>" /* critical conditions */
    #define KERN_ERR             "<3>" /* error conditions */
    #define KERN_WARNING   "<4>" /* warning conditions */
    #define KERN_NOTICE       "<5>" /* normal but significant condition */
    #define KERN_INFO            "<6>" /* informational */
    #define KERN_DEBUG       "<7>" /* debug-level messages */
    #endif
    
    #endif

      使能 ENABLE_BUG_LOG  觉得是否参与编译 

      Kernel_BugLevel 设置默认log等级

      2.4 在welcome目录下添加Makefile 文件 添加笔译规则

        obj-$(CONFIG_MTK_WELCOME) += welcome.o

        如果还有其他c文件加入在后面添加即可。

      3、修改对应的deconfig文件,让文件参与编译,将驱动编译近内核 添加

        CONFIG_MTK_WELCOME=y 

      4、编译内核 make bootimage  然后下载入手机

       最好是同时修改掉对应节点的权限,否则将没有权限访问对应的节点,找到对应的rc文件 添加类似 chmod 666 /dev/welcomf 即可

    ===============================================================

      自此内核驱动编写完成 会在相应的目录下生存如下文件:  

      /sys/class/welcomc/welcomf

      /dev/welcomf

      /proc/welcomp

      /sys/class/welcomc

      在/sys/class/welcomc/welcomf val 节点可以直接操作查看相应的值

      在/sys/class/welcomc/welcomf  buglevel 节点可以修改对应的log等级

  • 相关阅读:
    ajax三级联动
    ajax基础
    pdo连接数据
    jquery选择器和基本语句
    会话
    封装连接类
    1218数据访问
    php登陆与注册
    数据库连接和乱码问题
    mysql 严格模式 Strict Mode说明(text 字段不能加默认或者 不能加null值得修改方法)
  • 原文地址:https://www.cnblogs.com/atlas2016/p/7574324.html
Copyright © 2011-2022 走看看