字符设备驱动1:一个简单的字符设备驱动示例

字符设备驱动的注册主要分为三步:


1.注册主次设备号:register_chrdev_region()和 alloc_chrdev_region()

2.注册字符设备驱动:cdev_init()初始化,cdev_add()添加,注册设备驱动,cdev_alloc()申请空间,cdev_del()注销驱动

3.创建驱动的设备文件:class_create()

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 


/**********    宏定义变量     ***********************************/
#define my_MAJOR      230                   //代表自己分配的那个设备号
#define MAX_MINORS    1                     //次设备号数量
#define device_name  "module_test"         //驱动的名字


/****************  全局变量   *********************************/
static struct cdev test_cdev;          //驱动中重要的一个结构体变量cdev
static dev_t  mydev;                    //注册设备号相关的结构体
static struct class *test_class;       //创建驱动设备文件相关的结构体变量


static int test_chrdev_open(struct inode *inode, struct file *file)
{
	printk(KERN_ERR "open is ok \n");
	return 0;
}

static ssize_t test_chrdev_write(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	printk(KERN_ERR "write is ok \n");
	return 0;
}

static ssize_t test_chardev_reaed(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	printk(KERN_ERR "read is ok \n");
	return 0;
}


/*****   驱动的硬件实现函数    ******/
static const struct file_operations test_fops =
{
	.owner =	THIS_MODULE,
	.open  =    test_chrdev_open,
	.write =    test_chrdev_write,
	.read  =    test_chardev_reaed,
};



static int __init chrdev_init(void)
{
	int retval;
	mydev = MKDEV(my_MAJOR, 0);   //由自己分配的设备号得到dev变量。

	//第一步:注册主次设备号
	if ((retval = register_chrdev_region(mydev,MAX_MINORS,device_name)) != 0)
	{
		printk(KERN_ERR "dvb-core: unable to get major \n");
		return retval;
	}

	//第二步:注册字符设备驱动
	cdev_init(&test_cdev, &test_fops);
	if ((retval = cdev_add(&test_cdev, mydev, MAX_MINORS)) != 0) 
	{
		printk(KERN_ERR "dvb-core: unable register character device\n");
		goto error;
	}

	//第三步:创建驱动的设备文件
	test_class = class_create(THIS_MODULE, "cryil_class");
	if (IS_ERR(test_class)) 
	{
		retval = PTR_ERR(test_class);
		goto error;
	}
	device_create(test_class,NULL,mydev,NULL,"test");    //test就是将来在/dev中创建的设备文件名
	return 0;

	//错误处理函数:先回收申请的空间,然后删除驱动
error:
	cdev_del(&test_cdev);
	unregister_chrdev_region(mydev, MAX_MINORS);
	return retval;
}


static void __exit chardev_exit(void)
{
	
	device_destroy(test_class,mydev);  //销毁创建的驱动文件
	class_destroy(test_class);  //销毁创建的类
	cdev_del(&test_cdev);      //注销驱动
	unregister_chrdev_region(MKDEV(my_MAJOR, 0), MAX_MINORS);
}

module_init(chrdev_init);  //驱动的入口,insmod时会调用此函数
module_exit(chardev_exit);  //驱动的出口,rmmod时会调用此函数  

MODULE_DESCRIPTION("cryil test Driver");     //模块的介绍信息
MODULE_AUTHOR("cryil");                      //模块的作者
MODULE_LICENSE("GPL");                        //模块的许可证,固定的。

主次设备号的注册方法:

1.register_chrdev_region()自己分配:register_chrdev_region(mydev,MAX_MINORS,device_name) 。内部是调用了__register_chrdev_region函数,不过自己指定了主设备号

2.alloc_chrdev_region()使用系统分配的主次设备号:result = alloc_chrdev_region(&mydev, 0, MAX_MINORS, device_name),同样的实际上内部是调用了__register_chrdev_region函数,只不过将__register_chrdev_region函数的第一个参数设置为0,且次设备号只能从0开始分配。

3.但其实两个函数其内部都是调用了 MKDEV()函数,来指定主次设备号的。

MKDEV是将主设备号和次设备号转换成dev_t类型(dev_t类型是unsigned int 类型,32位)它是定义在中的宏    

形式:MKDEV(int major,int minor)    major为主设备号   minor为次设备号宏定义:#define MKDEV(major,minor) (((major) << MINORBITS) | (minor)),成功执行返回dev_t类型的设备编号,除此之外可以通过MAJOR()函数 和MINOR()函数 来获得主设备号和次设备号。
 

test.c中未使用cdev_alloc()函数,直接定义一个cdev结构体类型的变量,而使用cdev_alloc()函数的方法如下:

1.定义一个dev_t结构体类型的指针: static dev_t *p

2.对结构体指针进行实例化,为其分配内存: p = cdev_alloc()

3.后面的cdev_init() cdev_add() 函数就可以直接使用。

优点:cdev结构体类型占用的空间过大,在这里先定义一个指针类型的变量,然后再需要使用时动态刚分配内存,减少栈的使用。

cdev结构体分析:

	struct cdev {
		struct kobject kobj;
		struct module *owner;
		const struct file_operations *ops;
		struct list_head list;
		dev_t dev;
		unsigned int count;
	};

这是字符设备驱动中最重要的一个结构体,包含着绝大多数的信息。其实注册驱动的步骤就是填充这个结构体,然后在应用层操纵硬件完成具体的任务时,调用其中的函数指针完成具体的操作。

End。。。。。。

你可能感兴趣的:(驱动开发)