字符设备驱动程序

简单做个模板框架

字符设备开发流程

在这里插入图片描述

  1. 确定设备号dev_t,动态分配 alloc_chrdev_region() 或静态分配 register_chrdev_region()
  2. 定义file_opeartion 结构体*fops *,在结构体成员中实现对应的 *open()、read()*等函数。
  3. cdev_init()fopscdev 绑定,cdev_add() 绑定设备号 dev_t 并注册进内核。
  4. class_create() 创建设备类型,device_create() 注册设备结点
代码实现
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

/*确定主设备号*/
//#define	DEV_MAJOR	11
#ifndef	DEV_MAJOR
#define	DEV_MAJOR 0
#endif
int dev_major = DEV_MAJOR;//主设备号					


#define	DEV_NAME	"chrdev"//设备名称

static struct cdev *chrtest_cdev;//cdev结构体

//static struct class *chrdev_class; //定义一个class用于自动创建类

static char kernel_buf[1024];


#define	MIN(a,b) (a < b ? a : b)

/*实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t chrtest_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n", __FILE__,  __FUNCTION__, __LINE__);
	err = copy_to_user(buf, kernel_buf, MIN(1024, size));//内核空间的数据到用户空间的复制													 
	return MIN(1024, size);
}

static ssize_t chrtest_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(kernel_buf, buf, MIN(1024, size));//将用户空间的buf复制到内核空间缓冲区kernel_buf中,因为用户空间内存不能直接访问内核空间的内存
	return MIN(1024, size);
}

static int chrtest_drv_open(struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int chrtest_drv_close(struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/*定义自己的file_operations结构体*/
static struct file_operations chrtest_fops = {
	.owner		= THIS_MODULE,
	.open		= chrtest_drv_open,
	.read		= chrtest_drv_read,
	.write		= chrtest_drv_write,
	.release	= chrtest_drv_close,
};

/*把file_operations结构体告诉内核:register_chrdev*/
/*注册驱动函数:写入口函数,安装驱动程序时就会调用这个入口函数*/
static int __init chrdev_init(void)
{
	int result;
	dev_t devno;/*定义一个dev_t变量来表示设备号*/

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/*字符设备驱动注册流程第二步:分配主次设备号,这里即支持静态指定,也支持动态申请*/
	if(0 != dev_major)
	{
		devno = MKDEV(dev_major, 0);
		result = register_chrdev_region(devno, 1, DEV_NAME);//"/proc/devices/chrdev"
	}
	else
	{
		result = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
		dev_major = MAJOR(devno);
	}

	/*自动分配设备号失败*/
	if(result < 0)
	{
		printk(KERN_ERR " %s driver can't use major %d\n", DEV_NAME, dev_major);
		return -ENODEV;
	}
	printk(KERN_DEBUG " %s driver can't use major %d\n", DEV_NAME, dev_major);
	
	/*字符设备驱动注册流程第三步:分配cdev结构体,我们这里使用动态申请的方式*/
	if(NULL == (chrtest_cdev = cdev_alloc()))
	{
		printk(KERN_ERR " %s driver can't alloc for the cdev\n", DEV_NAME);
		unregister_chrdev_region(devno, 1);
		return -ENOMEM;
	}

	/*字符设备驱动注册流程第四步:分配cdev结构体,绑定主次设备号、fops到cdev结构体中,并注册给Linux内核*/
	chrtest_cdev->owner = THIS_MODULE;//.owner表示谁拥有你这个驱动程序
	cdev_init(chrtest_cdev, &chrtest_fops);//初始化设备
	result = cdev_add(chrtest_cdev, devno, 1);
	if(0 != result)
	{
		printk(KERN_INFO "%s driver can't register cdev:result=%d\n", DEV_NAME, result);
		goto ERROR;
	}
	printk(KERN_INFO "%s driver can register cdev:result=%d\n", DEV_NAME, result);

	/*自动创建设备类型与/dev设备节点*/
	#if 0
	chrdev_class = class_create(THIS_MODULE, DEV_NAME);//创建设备类型 sys/class/chrdevbase
	if(IS_ERR(chrdev_class))
	{
		result = PTR_ERR(chrdev_class);
		goto ERROR;
	}
	device_create(chrdev_class, NULL, MKDEV(dev_major, 0), NULL, DEV_NAME);// /dev/chrdev 注册这个设备节点
	#endif


	return 0;


ERROR:
	printk(KERN_ERR" %s driver installed failure.\n", DEV_NAME);
    cdev_del(chrtest_cdev);
    unregister_chrdev_region(devno, 1);
    return result;

}

/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*/
static void __exit chrdev_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/*注销设备类型与/dev设备节点*/
	#if 0
	device_destroy(chrdev_class, NKDEV(dev_major, 0));//注销此设备节点
	class_destroy(chrdev_class);//删除这个设备类型
	#endif

	cdev_del(chrtest_cdev);//注销字符设备
	unregister_chrdev_region(MKDEV(dev_major, 0), 1);//释放设备号
													 

	printk(KERN_ERR"%s driver version 1.0.0 removed!\n", DEV_NAME);
	return;
}

module_init(chrdev_init);
module_exit(chrdev_exit);

MODULE_LICENSE("GPL");

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值