一个完整的字符驱动编写

字符驱动demo

测试内核版本为linux-4.4
proc 接口有些变化,使用了seq_file.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/cdev.h>
#include <linux/semaphore.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/slab.h>

#define FREG_DEVICE_NAME "freg"
#define FREG_DEV_NAME "freg"

//#include "freg.h"

struct freg_dev{
    struct cdev dev;
    struct semaphore sem;
    int val;
};



static struct freg_dev * _freg_dev;
static struct class * _freg_class;

static void freg_create_proc(void);

static int freg_open(struct inode * inode,struct file * flip)
{
    struct freg_dev * dev = NULL;

    dev = container_of(inode->i_cdev,struct freg_dev,dev);
    flip->private_data = dev;
    pr_info("1. freg_open .\n");
    return 0;
}

static int freg_close(struct inode*inode,struct file * flip)
{
    return 0;
}

static ssize_t freg_read(struct file * filp,char __user * buffer,size_t count,loff_t * off)
{
    int err = 0;
    struct freg_dev * dev = (struct freg_dev*)filp->private_data;
    
    if(down_interruptible(&(dev->sem))){
        return -ERESTARTSYS;
    }

    if(copy_to_user(buffer,&(dev->val),sizeof(dev->val))){
        err = -EFAULT;
        goto out;
    }

    err = sizeof(dev->val);

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

static ssize_t freg_write(struct file * filp,const char __user * buffer,size_t count,loff_t * off)
{
    int err = 0;
    struct freg_dev * dev = (struct freg_dev *)filp->private_data;

    if(down_interruptible(&(dev->sem))){
        return -ERESTARTSYS;
    }

    if(count < sizeof(dev->val)){
        pr_err("freg user buffer is to small !!! \n");
        err = -EFAULT;
        goto out;
    }
    if(copy_from_user(&(dev->val),buffer,count)){
        pr_err("freg copy from user failed,count %ld \n",count);
        err = -EFAULT;
        goto out;
    }

    err = sizeof(dev->val);
out:
    up(&(dev->sem));
    return err;

}

static struct file_operations freg_fops = {
    .owner = THIS_MODULE,
    .open  = freg_open,
    .release = freg_close,
    .read   = freg_read,
    .write  = freg_write,
};



static ssize_t _freg_set_val(struct freg_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 _freg_get_val(struct freg_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 int seq_show(struct seq_file * file,void * data)
{
    struct freg_dev * dev = file->private;
    seq_printf(file,"freg value : %d \n",dev->val);
    return 0;
}

static int __freg_proc_open(struct inode * inode,struct file * filp)
{
    // single_open 已经创建了struct seq_operateions
    // 如果直接用seq_open 打开需要创建 一个seq_operations
    return single_open(filp,seq_show,_freg_dev);
}

static ssize_t  __freg_proc_write(struct file * filp,const char __user * buffer,size_t count,loff_t * off)
{
    int val = 0;
    char * page = NULL;
    struct seq_file * file = (struct seq_file *)filp->private_data;
    struct freg_dev * dev = (struct freg_dev *)file->private;

    if(!dev)
    {
        pr_err("freg proc failed ,Not init freg_dev\n");
        return -EFAULT;
    }


    if(count > PAGE_SIZE){
        pr_err("freg proc write data size is to large !!! \n");
        return -EFAULT;
    }
    
    if(seq_write(file,buffer,count) < 0)
    {
        pr_err("freg seq_write failed \n");
        return -EFAULT;
    }   

    page =(char *) __get_free_page(GFP_KERNEL);
    if(!page){
        pr_err("freg failed to alloc page \n");
        return -ENOMEM;
    }
    
    if(copy_from_user(page,buffer,count)) {
        pr_err("freg proc copy from user failed \n");
        free_page((unsigned long)page);
        return -EFAULT;
    }   

    val = _freg_set_val(dev,page,count);

    return 0;
}

static const struct file_operations proc_fops = {
    .owner = THIS_MODULE,
    .open  = __freg_proc_open,
    .release = seq_release,
    .read  = seq_read,
    .write = __freg_proc_write,//seq_write,
    .llseek = seq_lseek, 
};

static void freg_create_proc(void)
{
    struct proc_dir_entry * entry = NULL;
    //entry = proc_mkdir(FREG_DEV_NAME,NULL);
    // if(entry){
        //entry->owner = THIS_MODULE;
    //    entry->proc_fops = fops;      
   // }

    entry = proc_create(FREG_DEV_NAME,0,NULL,&proc_fops);
    if(!entry){
        pr_err("proc create failed \n");
    }
}

static void freg_remove_proc(void)
{
    remove_proc_entry(FREG_DEV_NAME,NULL);
}

/*
    class attribute
*/

static ssize_t freg_val_show(struct device * device,struct device_attribute * attr,char * buf)
{
    struct freg_dev * dev = (struct freg_dev *)dev_get_drvdata(device);

    return _freg_get_val(dev,buf);
}

static ssize_t freg_val_store(struct device * device,struct device_attribute * attr,const char *buf,size_t count)
{
    struct freg_dev * dev = (struct freg_dev *)dev_get_drvdata(device);
    return _freg_set_val(dev,buf,count);
}

/*define struct device_attribute dev_attr_val*/
static DEVICE_ATTR(val,S_IRUGO | S_IWUSR,freg_val_show,freg_val_store);


static int __init freg_init(void)
{
    int err = -1;
    dev_t dev;
    struct device * device = NULL;
    
    //dev_info("");
    pr_info("freg_init ... \n");

    /* 分配设备号*/
    err = alloc_chrdev_region(&dev,0,1,FREG_DEV_NAME);
    if(err < 0){
        pr_err("freg_dev alloc_chrdev_region failed \n");
        goto fail;
    }
    
    /* malloc freg_dev */
    _freg_dev = kmalloc(sizeof(struct freg_dev),GFP_KERNEL);
    if(!_freg_dev){
        pr_err("freg_dev kmalloc failed ..");
        err = -ENOMEM;
        goto unregister;       
    }

    memset(_freg_dev,0,sizeof(struct freg_dev));
    /*初始化cdev设备*/
    cdev_init(&(_freg_dev->dev),&freg_fops);
    _freg_dev->dev.owner = THIS_MODULE;
    _freg_dev->dev.ops = &freg_fops;

    err = cdev_add(&(_freg_dev->dev),dev,1);
    if(err < 0){
        pr_err("cdev add failed \n");
        goto register_cdev_fail;
    }
    
    sema_init(&(_freg_dev->sem),0);
    //init_MUTEX(&(_freg_dev->sem));
    _freg_dev->val = 0;
    
    /*初始化freg sys class*/
    _freg_class = class_create(THIS_MODULE,FREG_DEV_NAME);
    if(IS_ERR(_freg_class)){
        err = PTR_ERR(_freg_class);
        pr_err("freg class create failed\n");
        goto register_class;
    }
    
    /*创建 /sys/class/freg 和 /dev/freg */
    device = device_create(_freg_class,NULL,dev,NULL,"%s",FREG_DEV_NAME);
    if(IS_ERR(device)){
        err = PTR_ERR(device);
        pr_err("freg device create failed \n");
        goto register_dev;
    }
    /*创建/sys/class/freg/val 属性文件*/
  
    err = device_create_file(device,&dev_attr_val);
    if(err < 0){
        pr_err("freg device create file failed \n");
        goto register_attr;
    }

    dev_set_drvdata(device,_freg_dev);

    /*初始化proc fs*/
    //static void freg_create_proc(void)
    freg_create_proc();
    
    return 0;
register_attr:
    device_destroy(_freg_class,dev);
register_dev:
    class_destroy(_freg_class);  
register_class:
    cdev_del(&(_freg_dev->dev));
register_cdev_fail:
    kfree(_freg_dev);
unregister:
    unregister_chrdev_region(dev,1);
fail:
    return err;
    
}

static void __exit freg_exit(void)
{

    freg_remove_proc();
    cdev_del(&(_freg_dev->dev));

    unregister_chrdev_region(_freg_dev->dev.dev,1);
   
    if(_freg_class)
        class_destroy(_freg_class);
    if(_freg_dev)
        kfree(_freg_dev);
}

module_init(freg_init);
module_exit(freg_exit);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值