如何编写字符设备驱动

0.包含必须的头文件

#include 
#include 
#include             /*kfree ,kmalloc */
#include 

1.编写字符设备函数

1.1、分配核心结构

struct cdev *cdev_alloc(void)

功能:分配一个核心结构。
返回值:成功,返回核心结构;失败:返回负数
说明:可以不用该函数分配,直接定义如下:

   struct cdev pcdev;

1.2、申请设备号的相关函数
1)静态申请设备号函数

int register_chrdev_region( dev_t from,unsigned count,const char *name ) 

功能:注册一个设备号范围。
参数:
from:起始设备号(主、次)
count:连续的次设备号数量
name:设备名,不需要和/dev/的设备文件名相同,是/proc/device文件的名字
返回值:成功,返回0;失败:返回负数
2)动态申请设备号函数

int alloc_chrdev_region(dev_t *dev,unsigned int baseminor,unsigned int count,const char *name)  

功能:注册一个设备号范围。
参数:
dev:存放分配到的第一个设备(包含主次设备号)。
baseminor:要分配起始次设备号
count:连续的次设备号数量
name:设备名,不需要和/dev/的设备文件名相同
返回值:成功,返回0;失败:返回负数

1.3、初始化核心结构

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

功能:初始化核心结构;具体做的是清0核心结构,初始化核心结构的list,kobj,ops成员。
参数:
cdev:需要初始化的核心结构指针。
fops:文件操作方法结构指针。

1.4、注册核心结构

int cdev_add( struct cdev *p, dev_t dev,unsigned int count) 

功能 注册一个cdev结构
参数
p:已经初始化的核心结构指针
dev:起始设备号(包含主次设备号在内);前面申请设备号函数的返回值。
count:连续次设备号数量
返回值 成功:返回0 ;失败:返回负数

1.5、注销核心结构

void cdev_del(struct cdev *p)

功能:注销一个cdev结构
参数:
p:前面注册的struct cdev结构指针

1.6、释放设备号

void unregister_chrdev_region(dev_t from, unsigned count)

功能:释放设备号
参数:
from:起始设备号(主、次)
count:连续的次设备号数量
1.7、释放核心结构空间
void kfree(struct cdev *p);
功能:注销一个cdev结构
参数:
p:前面注册的struct cdev结构指针

结构体struct cdev 已经在cdev.h定义 好,具体如下:

struct cdev {
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;  	   /* 设备文件操作方法 */
	struct list_head list;
	dev_t dev;                						/* 32位设备号,包含主和次 */
	unsigned int count;       						/* 占用多少个连续的次设备号 */
}

2.自动创建/dev/目录设备文件函数

2.1创建一个设备类

#define class_create(owner, name)		\
({						\
	static struct lock_class_key __key;	\
	__class_create(owner, name, &__key);	\
})

功能:创建一个设备类
参数:
owner:类的所有者,固定是:THIS_MODULE
name:类名,随便,能有含义最好,不是 /dev/下设备的名字。
返回值:成功:返回有效 struct class *指针;失败:返回错误指针,可以使用 ERR_PTR()宏转换成错误代码。

2.2创建一个设备,报告设备信息

struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)

功能:创建一个设备
参数:
class:指定所要创建的设备所从属的类, class_create的返回值。
parent:这个设备的父设备,如果没有就指定为NULL
devt:设备号。
drvdata:设备信息,一般为NULL。
fmt:设备名称,而且是缓冲形式。

2.3删除一个设备
void device_destroy(struct class *class, dev_t devt)
功能:删除一个设备
参数:
class:指定所要创建的设备所从属的类, class_create的返回值。
devt:设备号。
2.4删除一个设备类
void class_destroy(struct class *cls)
功能:删除一个设备类
参数:
cls:指定所要创建的设备所从属的类, class_create的返回值。

代码例子:

#include 
#include 

//包含必须的头文件
#include 
#include 
#include             /*kfree ,kmalloc */
#include 

//以下是文件操作方法的具体实现代码
static int xxx_open (struct inode *pinode, struct file *pfile)
{
	printk("line:%d,%s is call\n",__LINE__,__FUNCTION__);
	return 0;
}
static ssize_t xxx_read (struct file *pfile,  char __user *buf,  size_t count,  loff_t *poff)
{
	printk("line:%d,%s is call\n",__LINE__,__FUNCTION__);
	return count;
}
static ssize_t xxx_write (struct file *pfile, const char __user *buf, size_t count,  loff_t *poff)
{
	printk("line:%d,%s is call\n",__LINE__,__FUNCTION__);
	return count;
}
static loff_t xxx_llseek (struct file *pfile, loff_t off, int whence)
{
	printk("line:%d,%s is call\n",__LINE__,__FUNCTION__);
	return off;
}
static long xxx_unlocked_ioctl (struct file *pfile, unsigned int cmd, unsigned long args)
{
	printk("line:%d,%s is call\n",__LINE__,__FUNCTION__);
	return 0;
}

static int xxx_release (struct inode *pinode, struct file *pfile)
{
	printk("line:%d,%s is call\n",__LINE__,__FUNCTION__);
	return 0;
}

//文件操作方法集合指针
static const struct file_operations char_dev_fops={
	.open            =   xxx_open,
	.write           =   xxx_write,
	.read            =   xxx_read,
	.llseek          =   xxx_llseek,
	.unlocked_ioctl  =   xxx_unlocked_ioctl,
	.release         =   xxx_release,
};

//定义核心数据结构
static struct cdev *p_cdv;
//定义第一个设备号(包含主和次)
static dev_t dev_no;
//定义设备主设备号
static unsigned int major=0;
//定义设备名
#define MYCHAR_DEV  "auto_linux26"

#define CLASS_NAME  "chardev26"
static struct class *linux26_class = NULL;
static struct device *this_device = NULL;

static int __init chrdev_test_init(void)
{
	int ret = -1;
	//1.使用cdev_alloc函数分配p_cdv空间
	p_cdv=cdev_alloc();
	if(p_cdv == NULL)
	{
		printk("cdev_alloc error!\n");
		ret = -ENOMEM;/* 分配失败一般是内存不足导致的  */
		goto cdev_alloc_error;	
	}
	
	//2.申请设备号:动态或者静态
	ret = alloc_chrdev_region(&dev_no, 0, 2,MYCHAR_DEV);
	if(ret < 0)
	{
		printk("alloc_chrdev_region error!\n");	
		goto alloc_chrdev_region_error;
	}

	//3.初始化p_cdv结构
	cdev_init(p_cdv, &char_dev_fops);
	//4.注册已经初始化好的c_dev结构
	ret = cdev_add(p_cdv, dev_no, 2);
    if ( ret < 0 ) 
	{	
		printk(KERN_EMERG "cdev_add  error\n");
        goto cdev_add_error;
    }
	
	/* 增加自动创建设备文件功能 */
    //5.创建一个设备类
    // linux26_class = class_create(THIS_MODULE,MYCHAR_DEV);//可以和设备名相同
    linux26_class = class_create(THIS_MODULE, CLASS_NAME); //取一个不同于设备名的类名
    if ( IS_ERR(linux26_class) ) {   
        ret = PTR_ERR(linux26_class);
        goto class_create_err;
    }
	
	//6.创建一个设备,报告设备信息
    this_device =  device_create(linux26_class,NULL , dev_no,   NULL, "%s", MYCHAR_DEV);
    if ( IS_ERR(this_device) ) {
        ret = PTR_ERR(this_device);
        goto device_create_err;
    }
	major=MAJOR(dev_no);
	printk(KERN_EMERG "major = %d \n",major);
    return 0;
	
	device_create_err:
		class_destroy(linux26_class);
		
	class_create_err:
		cdev_del(p_cdv);
	
	cdev_add_error:
		unregister_chrdev_region(dev_no, 2);

	alloc_chrdev_region_error:
	
		kfree(p_cdv);/*释放p_cdv结构空间*/
	
	cdev_alloc_error:
		return ret;
}

static void __exit chrdev_test_exit(void)
{
	device_destroy(linux26_class, dev_no);
	class_destroy(linux26_class);

	//1.注销p_cdv结构
   	cdev_del(p_cdv);
	//2.释放设备号
    unregister_chrdev_region(dev_no, 2);
	//3.释放p_cdv结构空间
    kfree(p_cdv);
	//4.删除设备
	device_destroy(linux26_class, devnr);
	//5.删除设备类
	class_destroy(linux26_class);
}

module_init(chrdev_test_init);
module_exit(chrdev_test_exit);

MODULE_LICENSE("GPL");

你可能感兴趣的:(Linux学习之路)