linux设备驱动之字符设备

来源:互联网 发布:vb中dim是什么意思 编辑:程序博客网 时间:2024/05/29 09:31
/*
 1. 如何写字符设备驱动,添加字符设备
  // 1 手动添加字符设备,cat/proc/divices ==> major 获得主设备号
    已知主次设备号添加设备 cd /dev; mknod name c major minor
  2 自动添加设备节点udev
  class_create();
  device_create();
 
 2. 如何写设备fops
    1 参考宋宝华《linux设备驱动开发详解》第6章p149 globalmem的设备驱动
    本程序用到了内核封装api,可简化fops的操作,其实是一样的程序
   
simple_read_from_buffer();
simple_write_to_buffer ();
注意:注意返回值,cnt读写个数
如果是0的话,会重复读写(cat echo 命令下),如果是自己的写app,可以自己操作
当然,如果你想测试一下,只需要在fops中,打印一个log,就可以了
 3. 了解procfs,sysfs文件系统,学会操作相关文件系统节点
1 可以参考宋宝华第5章文件系统5.4.2,21章21.5 使用/proc
  /proc 和 文件fops 有点相似
  /sys 写法比较简单
   注意 /sys /proc 注意返回值count, 如果是0的话,会重复读写(cat echo 命令下),如果是自己的写app,可以自己操作
   
 4. 写完驱动如何添加到内核里 
  1 Kconfig,注意source path/Kconfig,不能写完,一扔 
  2 Makefile 
  3 配置config文件
 
  copyright(c)
  author lxf
  date   20160908
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/uaccess.h> 
#include <linux/printk.h>
#include <linux/err.h>
#include <linux/miscdevice.h>
#include <linux/sysfs.h>
#include "freg.h"
//#define  FREG_DEV_T   
#ifdef   FREG_DEV_T 
#define  FREG_MAJOR  243
#define  FREG_MINOR  0

#endif 


// 主设备号和从设备号变量


static int freg_major = 0;
static int freg_minor = 0; 


// 设备类型和设备变量


//static struct class* freg_class = NULL;
static struct fake_reg_dev*  freg_dev = NULL;


//
static struct proc_dir_entry* freg_dir, *freg;

// 传统的设备文件操作方法
static int freg_open(struct inode* inode, struct file* filp);
static int freg_release(struct inode* inode, struct file* filp);
static ssize_t freg_read(struct file * filp, char __user * buf, size_t size, loff_t * ppos);
static ssize_t freg_write(struct file * filp, const char __user * buf, size_t size, loff_t * ppos);


static int freg_open(struct inode* inode, struct file* filp)
{
filp->private_data = freg_dev;
pr_info("lxf %s\n",__func__);
return 0;
}

static int freg_release(struct inode* inode, struct file* filp)
{
pr_info("lxf %s\n",__func__);


return 0;
}


static  ssize_t freg_read(struct file * filp, char __user * buf, size_t size, loff_t * ppos)
{
      int cnt =0;
char val[4]={0};
pr_info("lxf %s bf lock\n",__func__);
mutex_lock(&freg_dev->freg_mutex);
cnt = sprintf(val,"%d\n",freg_dev->val);
cnt = simple_read_from_buffer(buf, size, ppos, val, cnt); 
mutex_unlock(&freg_dev->freg_mutex);
      pr_info("lxf %s af lock\n",__func__);

return cnt;

}


static  ssize_t freg_write(struct file * filp, const char __user * buf, size_t size, loff_t * ppos)
{
 char mem[10];
 int cnt;
 pr_info("lxf %s\n",__func__);
         mutex_lock(&freg_dev->freg_mutex);
/*
simple_write_to_buffer(mem, size_t available, loff_t *ppos,
const void __user *from, size_t count)
*/
 
 cnt =  simple_write_to_buffer(mem, size, ppos, buf, sizeof(freg_dev->val));
 freg_dev->val = (int)simple_strtoul(mem, NULL, 10);
 mutex_unlock(&freg_dev->freg_mutex);
return cnt;
}


// 传统的设备文件操作方法表


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


/*
节点操作
*/
ssize_t freg_proc_read(struct file *file, char __user * buf, size_t size, loff_t * ppos)
{


/*
  int cnt=0;
    char *page = NULL;


page = kzalloc(128, GFP_KERNEL);


    if((0 == strlen(module_name)) && (0 == tp_ver_show) && (0 == strlen(tp_ver_show_str)))
        cnt = sprintf(page, "no tp\n");
else
{
// xuke @ 20140811
cnt = sprintf(page, "[Vendor]%s,%s\n", (strlen(module_name) ? module_name : "Unknown"),
(strlen(tp_ver_show_str) ? tp_ver_show_str : "Unknown product"));
}

cnt = simple_read_from_buffer(buf, size, ppos, page, cnt);
// printk("%s, page=%s, cnt=%d\n", __func__, page, cnt);

kfree(page);
    return cnt;

*/


  int cnt =0;
    char* page ;
page = kzalloc(128,GFP_KERNEL);
cnt = sprintf(page,"%d\n",freg_dev->val);
pr_info("lxf %d\n", freg_dev->val);
cnt = simple_read_from_buffer(buf, size, ppos, page, cnt);                                            
kfree(page);
return cnt;
}
ssize_t freg_proc_write(struct file *file, const char __user * buf, size_t count, loff_t * ppos)
{
             pr_info("lxf %s\n",__func__);

freg_dev->val = (int)simple_strtoul(buf, NULL, 10);
pr_info("lxf %d\n", freg_dev->val);
return count;
}


static struct file_operations proc_fops =
{
.owner = THIS_MODULE,
.read    = freg_proc_read,
.write   = freg_proc_write,
};


int  freg_creat_proc(void)
{
freg_dir =  proc_mkdir("freg_dir", NULL);
freg     = proc_create_data("freg", 0666, freg_dir, &proc_fops, freg_dev);
if (!freg)
{
return -1;
}
return 0;
}


static struct kobject* freg_device; //sysfs kobject
//static DEVICE_ATTR(tp_info, 0444, msm_tp_module_id_show, NULL);

static ssize_t  freg_sys_show(struct device *dev,
struct device_attribute * attr, char *buf)
{
pr_info("lxf %s\n",__func__);


return  sprintf(buf,"%d\n",freg_dev->val);

}


ssize_t freg_sys_store (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
pr_info("lxf %s\n",__func__);


 freg_dev->val = (int)simple_strtoul(buf, NULL, 10);
return  count;
}

 static  DEVICE_ATTR(freg_info,  0666, freg_sys_show,  freg_sys_store);


static int  freg_creat_sysfs(void)
{
int ret = 0;
freg_device = kobject_create_and_add("freg_sys",NULL);
if (!freg_device)
{
pr_info("lxf subsystem_register failed\n");
ret = -ENOMEM;
return ret;
}
ret = sysfs_create_file(freg_device, &dev_attr_freg_info.attr);
if (ret)
{
pr_info("lxf sysfs_create_file fail\n");
kobject_del(freg_device);
}
return 0;

}


static int __init freg_init(void)
{
int ret;
dev_t devno;
struct class *myclass;
 //创建字符设备
#ifdef  FREG_DEV_T
freg_major = FREG_MAJOR;
freg_minor = FREG_MAJOR;
#endif 


devno = MKDEV(freg_major, freg_minor);


if (freg_major)
{
ret = register_chrdev_region(devno, 1, "freg_cdev");
if (ret)
{
printk(KERN_INFO "lxf register_chrdev_region fail\n");
return ret;
}
}
else 
{
ret  = alloc_chrdev_region(&devno, 0, 1, "freg_cdev");
if (ret)
{
printk(KERN_INFO "lxf alloc_chrdev_region fail\n");
return ret;
}
freg_major = MAJOR(devno);
freg_minor = MINOR(devno);
}
freg_dev = kzalloc(sizeof(struct fake_reg_dev), GFP_KERNEL);
if (IS_ERR_OR_NULL(freg_dev))
{
printk(KERN_INFO "lxf kzalloc fail\n");
ret = -ENOMEM;
goto fail_malloc;
}

cdev_init(&freg_dev->dev,&freg_fops);
freg_dev->dev.owner = THIS_MODULE;

//freg_dev->val = 521;
mutex_init(&freg_dev->freg_mutex);

// 添加字符设备
ret = cdev_add(&freg_dev->dev, devno, 1);
if (ret)
{
printk(KERN_INFO "lxf register_chrdev_region fail\n");
goto cdev_del;
}

//创建相关节点
   myclass = class_create(THIS_MODULE, "freg_cdev_driver");
/*
 struct device *device_create(struct class *class, struct device *parent,
    dev_t devt, void *drvdata, const char *fmt, ...)
*/
   device_create(myclass, NULL, devno, NULL,"freg_cdev");
 
freg_creat_proc();
freg_creat_sysfs();
pr_info("lxf freg_init sucessfully\n");
return ret;


cdev_del

cdev_del(&freg_dev->dev);
fail_malloc:
unregister_chrdev_region(devno, 1);
return ret;
}


static void __exit freg_exit(void)
{
//注销设备
cdev_del(&freg_dev->dev);
kfree(freg_dev);
unregister_chrdev_region(freg_dev->dev.dev, 1);
//删除节点
remove_proc_entry("freg", freg_dir);
remove_proc_entry("freg_dir", NULL);
sysfs_remove_file(freg_device, &dev_attr_freg_info.attr);
kobject_del(freg_device);

}


module_init(freg_init);
module_exit(freg_exit);
MODULE_AUTHOR("lxf");
MODULE_DESCRIPTION("this driver just for test");
MODULE_LICENSE("GPL v2");



1 0