打通驱动层到应用层--(一)driver层

前言

本节为添加内核驱动程序。

一、添加

我们添加的驱动为虚拟字符设备,一般使用hello来命名该驱动,因为我使用的工程源码中已经存在了test驱动,注册为hello。因此我的驱动命名为helloxjq。
编译方式为静态编译,将驱动编入内核。
在kernel/msm-4.9/drivers/目录下创建helloxjq文件夹,在helloxjq文件夹下创建helloxjq.c、Makefile、Kconfig三个文件。
Kconfig代码如下:

config HELLOXJQ
tristate "helloxjq Android test Driver"
default y
help
It is a Android test driver.
obj-$(CONFIG_HELLOXJQ) += helloxjq.o

helloxjq.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 <linux/sched.h>
#include <linux/errno.h>
#include <linux/fcntl.h>

#include <linux/poll.h>
#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>

#include <asm/uaccess.h>
#include <linux/slab.h>


#include "helloxjq.h"  

/*定义主设备和从设备号变量*/  
static int helloxjq_major = 0;  
static int helloxjq_minor = 0;  

/*设备类别和设备变量*/  
static struct class* helloxjq_class = NULL;  
static struct helloxjq_dev* helloxjq_dev = NULL;  

/*传统的设备文件操作方法*/  
static int helloxjq_open(struct inode* inode, struct file* filp);  
static int helloxjq_release(struct inode* inode, struct file* filp);  
static ssize_t helloxjq_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);  
static ssize_t helloxjq_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);  

/*设备文件操作方法表*/  
static struct file_operations helloxjq_fops = {  
    .owner = THIS_MODULE,  
    .open = helloxjq_open,  
    .release = helloxjq_release,  
    .read = helloxjq_read,  
    .write = helloxjq_write,   
};  

/*访问设置属性方法*/  
static ssize_t helloxjq_val_show(struct device* dev, struct device_attribute* attr,  char* buf);  
static ssize_t helloxjq_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);  

/*定义设备属性*/  
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, helloxjq_val_show, helloxjq_val_store); 

/*打开设备方法*/  
static int helloxjq_open(struct inode* inode, struct file* filp) {  
    struct helloxjq_dev* dev;          

    /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/  
    dev = container_of(inode->i_cdev, struct helloxjq_dev, dev);  
    filp->private_data = dev;  

    return 0;  
}  

/*设备文件释放时调用,空实现*/  
static int helloxjq_release(struct inode* inode, struct file* filp) {  
    return 0;  
}  

/*读内存*/  
static ssize_t helloxjq_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {  
    ssize_t err = 0;  
    struct helloxjq_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;  
}  

/*写字符串*/  
static ssize_t helloxjq_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {  
    struct helloxjq_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 ssize_t __helloxjq_set_val(struct helloxjq_dev* dev, const char* buf, size_t count) {       

    /*同步访问*/          
    if(down_interruptible(&(dev->sem))) {                  
        return -ERESTARTSYS;          
    }          
    printk(KERN_ALERT"__helloxjq_set_val.buf: %s  count:%d\n",buf,count);
    printk(KERN_ALERT"__helloxjq_set_val.dev->val: %s  count:%d\n",dev->val,count);
    strncpy(dev->val,buf, count);
    printk(KERN_ALERT"__helloxjq_set_val.dev->val: %s  count:%d\n",dev->val,count);
    up(&(dev->sem));  

    return count;  
}  

/*读取设备属性val*/  
static ssize_t helloxjq_val_show(struct device* dev, struct device_attribute* attr, char* buf) {  
    struct helloxjq_dev* hdev = (struct helloxjq_dev*)dev_get_drvdata(dev);          
    printk(KERN_ALERT"helloxjq_val_show.\n");
    printk(KERN_ALERT"%s\n",hdev->val);
    return 0;
}  

/*写设备属性val*/  
static ssize_t helloxjq_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {   
    struct helloxjq_dev* hdev = (struct helloxjq_dev*)dev_get_drvdata(dev);    
    printk(KERN_ALERT"helloxjq_val_store.buf: %s  count:%d\n",buf,count);

    return __helloxjq_set_val(hdev, buf, count);  
}  


/*初始化设备*/  
static int  __helloxjq_setup_dev(struct helloxjq_dev* dev) {  
    int err;  
    dev_t devno = MKDEV(helloxjq_major, helloxjq_minor);  

    memset(dev, 0, sizeof(struct helloxjq_dev));  

    cdev_init(&(dev->dev), &helloxjq_fops);  
    dev->dev.owner = THIS_MODULE;  
    dev->dev.ops = &helloxjq_fops;          

    /*注册字符设备*/  
    err = cdev_add(&(dev->dev),devno, 1);  
    if(err) {  
        return err;  
    }          

    /*初始化信号量和寄存器val的值*/  
    sema_init(&(dev->sem),1);
    dev->val = kmalloc(10,GFP_KERNEL);  
    strncpy(dev->val,"helloxjq",sizeof("helloxjq"));
    return 0;  
}  

/*模块加载方法*/  
static int __init helloxjq_init(void){   
    int err = -1;  
    dev_t dev = 0;  
    struct device* temp = NULL;  

    printk(KERN_ALERT"helloxjq_init.\n");          

    /*动态分配主设备和从设备号*/  
    err = alloc_chrdev_region(&dev, 0, HELLOXJQ_DEVICE_NUM, HELLOXJQ_DEVICE_NODE_NAME);  
    if(err < 0) {  
        printk(KERN_ALERT"Failed to alloc char dev region.\n");  
        goto fail;  
    }  

    helloxjq_major = MAJOR(dev);  
    helloxjq_minor = MINOR(dev);          

    /*分配helo设备结构体变量*/  
    helloxjq_dev = kmalloc(sizeof(struct helloxjq_dev), GFP_KERNEL);  
    if(!helloxjq_dev) {  
        err = -ENOMEM;  
        printk(KERN_ALERT"Failed to alloc helloxjq_dev.\n");  
        goto unregister;  
    }          

    /*初始化设备*/  
    err = __helloxjq_setup_dev(helloxjq_dev);  
    if(err) {  
        printk(KERN_ALERT"Failed to setup dev: %d.\n", err);  
        goto cleanup;  
    }          

    /*在/sys/class/目录下创建设备类别目录helloxjq*/  
    helloxjq_class = class_create(THIS_MODULE, HELLOXJQ_DEVICE_CLASS_NAME);  
    if(IS_ERR(helloxjq_class)) {  
        err = PTR_ERR(helloxjq_class);  
        printk(KERN_ALERT"Failed to create helloxjq class.\n");  
        goto destroy_cdev;  
    }          

    /*在/dev/目录和/sys/class/helloxjq目录下分别创建设备文件helloxjq*/  
    temp = device_create(helloxjq_class, NULL, dev, "%s", HELLOXJQ_DEVICE_FILE_NAME);  
    if(IS_ERR(temp)) {  
        err = PTR_ERR(temp);  
        printk(KERN_ALERT"Failed to create helloxjq device.");  
        goto destroy_class;  
    }          

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

    printk(KERN_ALERT"Succedded to initialize helloxjq device.\n");  
    return 0;  

destroy_device:  
    device_destroy(helloxjq_class, dev);  

destroy_class:  
    class_destroy(helloxjq_class);  

destroy_cdev:  
    cdev_del(&(helloxjq_dev->dev));  

cleanup:  
    kfree(helloxjq_dev);  

unregister:  
    unregister_chrdev_region(MKDEV(helloxjq_major, helloxjq_minor), 1);  

fail:  
    return err;  
}  

/*模块卸载方法*/  
static void __exit helloxjq_exit(void) {  
    dev_t devno = MKDEV(helloxjq_major, helloxjq_minor);  

    printk(KERN_ALERT"helloxjq_exit\n");          

    /*销毁设备类别和设备*/  
    if(helloxjq_class) {  
        device_destroy(helloxjq_class, MKDEV(helloxjq_major, helloxjq_minor));  
        class_destroy(helloxjq_class);  
    }          

    /*删除字符设备和释放设备内存*/  
    if(helloxjq_dev) {  
        cdev_del(&(helloxjq_dev->dev));  
        kfree(helloxjq_dev);  
    }          
    if(helloxjq_dev->val != NULL){
     kfree(helloxjq_dev->val);
    }
    /*释放设备号*/  
    unregister_chrdev_region(devno, 1);  
}  

MODULE_LICENSE("GPL");  
MODULE_DESCRIPTION("Test Driver");  

module_init(helloxjq_init);  
module_exit(helloxjq_exit);  

二、修改

  1. 修改arch/arm/Kconfig和drivers/kconfig两个文件,在menu "Device Drivers"和endmenu之间添加一行:
    source "drivers/helloxjq/Kconfig"
    这样,执行make menuconfig时,就可以配置helloxjq模块的编译选项了,而我们在配置Kconfig时写了default y,默认加载驱动模块,因此不用去menuconfig中配置编译选项。

  2. 修改drivers/Makefile文件,添加一行:
    obj-$(CONFIG_HELLOXJQ) += helloxjq/

  3. 在kernel/arch/arm/configs文件夹中的spyro_defconfig和spyro-perf_defconfig中添加
    CONFIG_HELLOXJQ=y
    一般教程没有这最后一步,但是我这个工程必须添加后才能编入内核。

三、编译

在源码主目录(HLOS)下编译。

source ./build/envsetup.sh
lunch 
make -j32 (也可以使用make -j32 make -j32 2>&1 | tee build.log,会在主目
录下生成build.log日志文件,便于查找错误)

四、验证

将代码工程烧录进安卓设备,通过ADB连接设备,
1、通过cd dev 查看是否有helloxjq文件夹
2、通过cat /proc/devices查看是否存在设备和对应的设备号
3、进入到sys/class目录,查看是否有helloxjq目录

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值