字符设备 -----LDD3

来源:互联网 发布:中国消防网站域名 编辑:程序博客网 时间:2024/05/17 02:23

水平有限,若有错误亦或者不当的地方,请大家不吝值出。谢谢~


字符设备创建流程

1、申请字符设备设备号

2、注册字符设备


一、字符设备主次设备号的申请

dev_t        --->linux/types.h

dev_t  设备号变量声明, 32 bit

bit31-20 主设备号

bit19- 0   次设备号


1、静态申请

         int register_chrdev_region(dev_tfirst, unsigned int count, char *name)

         first: 设备号的起始值(主设备号,起始次设备号)

         count:申请的次设备号个数

         name: 与设备号关联的设备名称,出现在/proc/devices

         返回值 : 0 : 成功   <0 :失败

 

2、动态申请

         int alloc_chrdev_region(dev_t*dev, unsigned intfirstminor, unsigned int count, char *neme)

         dev:输出值,起始设备号

         firstminor:起始次设备号

         count:次设备号个数

         name:与设备号关联的设备名称,出现在/proc/devices

         返回值 : 0 : 成功   <0 :失败

                           

3、释放设备号

         voidunregister_chrdev_region(dev_tfirst, unsigned int count)

         first: 起始设备号

         count:次设备号个数

 

二、字符设备注册

//字符设备结构体

struct cdev {                                          -----><linux/cdev.h>

structkobjectkobj;

struct module *owner;

conststructfile_operations *ops;             //文件操作

structlist_head list;

dev_tdev;

unsignedint count;

};


1、字符设备注册          

方法一:

//分配一个字符设备结构体

structcdev *my_cdev = cdev_alloc();      

 

//初始化字符设备结构体

my_cdev->ops = *my_fops;                        //关联file_operations             

my_cdev->ower = THIS_MODULE;

 

//添加设备及其关联设备号、到内核中

int cdev_add(structcdev *dev, dev_tnum;unsigned int count);

dev:字符设备结构体

num:字符设备起始设备号

count: 次设备号个数

返回值: 0 成功   <0:失败

 

方法二:

structcdevcdev;

 

cdev_init(&dev->cdev,&scull_fops); //关联字符设备及file_operation

dev->cdev.owner = THIS_MODULE;

 

int cdev_add(structcdev *dev, dev_tnum;unsigned int count);

 

2、移除字符设备

voidcdev_del(structcdev *dev)



三个重要结构体-----><linux/fs.h>

structfile_operations                ---->文件操作结构体

struct file                                      ---->每个打开的文件都有一个structfile

structinode                                  --->每个文件都对应着一个inode->structcdev

 

每个打开的文件都对应着一个struct file. 每个文件在文件系统中都对应着一个inode.

当多次打开同一文件时,内核中会存在多个struct file,但都对应一个inode.

 

struct inode

{       

         dev_ti_rdev;                       --->字符设备设备号

         structcdev* i_cdev;         --->字符设备结构体

......

}

 

打开一个字符设备结构体关联流程

Open(“/dev/xxx”, O_RDWR)–->struct file->inode->cdev->structfile_operations

 

//2.6版本前字符设备注册与释放

intregister_chrdev(unsigned int major,const char *name, structfile_operations *fops)

major : 主设备号

name : 驱动程序,出现在/proc/devices中

fops :文件操作相关, file_operations

返回值:> 0主设备号。次设备号会默认0-255中的某一个

                   <0, 调用失败

//释放

intunregister_chrdev(unsigned int major, constchar *name)

major:主设备号

name : 设备名称


Demo

/*
* cdev demo create by Yxl @20150406
*/


#include <linux/init.h>
#include <linux/module.h>


#include <linux/moduleparam.h>


#include <linux/errno.h>


#include <linux/types.h>
#include <linux/cdev.h> //char devices
#include <linux/fs.h> //struct file, struct inode, struct file_operation


#include <linux/proc_fs.h> //----> procfs
#include <linux/device.h> // class & device
#include <asm/uaccess.h>        //copy_to_user, copy_from_user


unsigned char* gPMem = NULL; 


/*模块参数*/
static int gData = 0;
static char *gStr = "this is my cdev.ko";
module_param(gData, int, S_IRUGO); 
module_param(gStr, charp, S_IRUGO);


/****************************字符设备*******************************/
/*设备号*/
dev_t gDev = 0;


/*字符设备设备*/
struct cdev *gCdev = NULL;


static int Mycdev_open(struct inode * inode, struct file * filp)
{
printk(KERN_INFO "%s", __FUNCTION__);
try_module_get(THIS_MODULE);
return 0;
}


static ssize_t Mycdev_read(struct file* filp, char __user *buffer, size_t count, loff_t *offp)
{
printk(KERN_INFO "%s", __FUNCTION__);
return 0;
}


static ssize_t Mycdev_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
printk(KERN_INFO "%s", __FUNCTION__);
return 0;
}
static int Mycdev_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
printk(KERN_INFO "%s", __FUNCTION__);
return 0;
}
static int Mycdev_release(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "%s", __FUNCTION__);

module_put(THIS_MODULE);
return 0;
}

/*字符设备文件操作*/
struct file_operations Mycdev_fops = {
.owner = THIS_MODULE,
.open = Mycdev_open,
.read = Mycdev_read,
.write = Mycdev_write,
.unlocked_ioctl = Mycdev_ioctl,
.release = Mycdev_release,
};


/******************************class&device*******************************/
struct class *gcdev_class = NULL;
struct device *gcdev_device = NULL;


/*********************************proc操作*****************************/
/*Proc 调试信息*/
struct proc_dir_entry *gPentry = NULL;
struct proc_dir_entry *gentry = NULL;


/*proc_read/ proc_write*/
static int cdevDump_read_proc(char *page, char **start,  off_t offset, int count, int *eof, void *data)
{
int len = sprintf(page,"%s\n","hello world");      
return len;  
}

static int cdevDump_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data)
{
return 0;
}

//proc file_operations 操作
struct file_operations CdevProc_fpos={
};
/*********************************proc操作**************************/


static int __init Mycdev_init(void)
{
int ret = 0;

printk(KERN_INFO "%s", __FUNCTION__);
printk(KERN_INFO "gStr = %s, module_param gData = %d\n",gStr,  gData);

// 1
/*注册设备号, /proc/devices可以看到Mycdev名字*/
ret = alloc_chrdev_region(&gDev, 0, 1, "Mycdev");
if(ret < 0)
{
printk(KERN_ERR "alloc_chrdev_region failed, ret = %d\n", ret);
return ret;
}

/*注册字符设备*/
gCdev = cdev_alloc();

cdev_init(gCdev, &Mycdev_fops);
gCdev->owner = THIS_MODULE;
gCdev->ops = &Mycdev_fops;/*可以不做,cdev_init里已经做了*/


ret = cdev_add(gCdev, gDev, 1);
if(ret < 0)
{
printk(KERN_ERR "cdev_add failed, ret = %d\n", ret);
return ret;
}


// 2  /sys/class/cdev/DevCdev
/*自动创建/dev/设备节点,  cdev : /sys/class下类名文件夹*/
gcdev_class = class_create(THIS_MODULE, "cdev");
if(gcdev_class == NULL)
{
printk(KERN_ERR "class_create failed...\n");
return -1;
}


/*向sysfs注册class, 并关联设备号, DevCdev: /dev/下节点名字*/
gcdev_device = device_create(gcdev_class, NULL, gDev, NULL, "DevCdev");
if(gcdev_device == NULL)
{
printk(KERN_ERR "device_create failed...\n");
return -1;
}



// 3
/*创建/proc/media 调试信息*/
// 1 创建/proc/media目录
gPentry = proc_mkdir("media", NULL);
if(gPentry == NULL)
{
printk(KERN_ERR "proc_mkdir failed...\n");
}


// 创建/proc/media/cdevDump文件
#if 1
gentry = create_proc_entry("cdevDump", 0666, gPentry);
if(gentry)
{
gentry->proc_fops = &CdevProc_fpos;
gentry->read_proc = cdevDump_read_proc;
gentry->write_proc = cdevDump_write_proc;
}
else
{
printk(KERN_ERR "create_proc_entry failed...\n");
}
#else
/*在高版本中, create_proc替换了create_proc_entry*/
gentry = proc_create("cdevDump", 0666, gPentry, &CdevProc_fpos);
if(gentry == NULL)
{
printk(KERN_ERR "create_proc failed...\n");
}
#endif

return 0;
}


static void __exit Mycdev_exit(void)
{
printk(KERN_INFO "%s", __FUNCTION__);

// 1
/*销毁proc 文件*/
remove_proc_entry("cdevDump", gPentry);
remove_proc_entry("media", NULL);


// 2
/*销毁class & device*/
device_destroy(gcdev_class, gDev);
class_destroy(gcdev_class);

// 3
/*销毁字符设备(先销毁设备,再销毁设备号)*/
cdev_del(gCdev);
/*销毁设备号*/
unregister_chrdev_region(gDev, 1);
}


module_init(Mycdev_init);
module_exit(Mycdev_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yxl");



0 0
原创粉丝点击