Linux驱动开发3——自动创建字符设备驱动的设备文件

——————————————————————  学习记录—————————————————————————

1 问题背景

前面手动使用mknod /dev/test c 主设备号 次设备号,进行设备文件的创建,能不能使用代码对设备文件进行创建。

2 使用udev(嵌入式中用的是mdev)
(1)应用层的一个应用程序
(2)内核驱动和应用层udev之间有一套信息传输机制(netlink协议)
(3)应用层启用udev,内核驱动中使用相应接口
(4)驱动注册和注销时信息会被传给udev,由udev在应用层进行设备文件的创建和删除

3 示例代码

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>		// arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#include <linux/string.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/cdev.h>
#include <linux/device.h>


//#define MYMAJOR		200
#define MYCNT		1
#define MYNAME		"testchar"

#define GPJ0CON_PA	0xe0200240
#define GPJ0DAT_PA 	0xe0200244

unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;


//int mymajor;
static dev_t mydev;
//static struct cdev test_cdev;
static struct cdev *pcdev;
static struct class *test_class;

char kbuf[100];			// 内核空间的buf


static int test_chrdev_open(struct inode *inode, struct file *file)
{
	// 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分
	// 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来做代表。
	printk(KERN_INFO "test_chrdev_open\n");
	
	rGPJ0CON = 0x11111111;
	rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));		// 亮
	
	return 0;
}

static int test_chrdev_release(struct inode *inode, struct file *file)
{
	printk(KERN_INFO "test_chrdev_release\n");
	
	rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
	
	return 0;
}

// 自定义一个file_operations结构体变量,并且去填充
static const struct file_operations test_fops = {
	.owner		= THIS_MODULE,				// 惯例,直接写即可
	
	.open		= test_chrdev_open,			// 将来应用open打开这个设备时实际调用的
	.release	= test_chrdev_release,		// 就是这个.open对应的函数
};


// 模块安装函数
static int __init chrdev_init(void)
{	
	int retval;
	
	printk(KERN_INFO "chrdev_init helloworld init\n");

	// 使用新的cdev接口来注册字符设备驱动
	// 新的接口注册字符设备驱动需要2步
	
	// 第1步:分配主次设备号
	retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);
	if (retval < 0) 
	{
		printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);
		goto flag1;
	}
	printk(KERN_INFO "alloc_chrdev_region success\n");
	printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev));
	
	
	// 第2步:注册字符设备驱动
	pcdev = cdev_alloc();			// 给pcdev分配内存,指针实例化
	//cdev_init(pcdev, &test_fops);
	pcdev->owner = THIS_MODULE;
	pcdev->ops = &test_fops;

	retval = cdev_add(pcdev, mydev, MYCNT);
	if (retval) {
		printk(KERN_ERR "Unable to cdev_add\n");
		goto flag2;
	}
	printk(KERN_INFO "cdev_add success\n");
	
	// 注册字符设备驱动完成后,添加设备类的操作,以让内核帮我们发信息
	// 给udev,让udev自动创建和删除设备文件
	test_class = class_create(THIS_MODULE, "aston_class");
	if (IS_ERR(test_class))
		return -EINVAL;
	// 最后1个参数字符串,就是我们将来要在/dev目录下创建的设备文件的名字
	// 所以我们这里要的文件名是/dev/test
	device_create(test_class, NULL, mydev, NULL, "test");
	
	
	// 使用动态映射的方式来操作寄存器
	if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
//		return -EINVAL;
		goto flag3;
	if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))
//		return -EINVAL;
		goto flag3;
	
	pGPJ0CON = ioremap(GPJ0CON_PA, 4);
	pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);
	
	*pGPJ0CON = 0x11111111;
	*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));		// 亮
	
	//goto flag0:
	return 0;
	
// 如果第4步才出错跳转到这里来	
	release_mem_region(GPJ0CON_PA, 4);
	release_mem_region(GPJ0DAT_PA, 4);

// 如果第3步才出错跳转到这里来
flag3:
	cdev_del(pcdev);

// 如果第2步才出错跳转到这里来
flag2:
	// 在这里把第1步做成功的东西给注销掉
	unregister_chrdev_region(mydev, MYCNT);
// 如果第1步才出错跳转到这里来
flag1:	
	return -EINVAL;
//flag0:	
//	return 0;
}

// 模块下载函数
static void __exit chrdev_exit(void)
{
	printk(KERN_INFO "chrdev_exit helloworld exit\n");

	*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));	
	
	// 解除映射
	iounmap(pGPJ0CON);
	iounmap(pGPJ0DAT);
	release_mem_region(GPJ0CON_PA, 4);
	release_mem_region(GPJ0DAT_PA, 4);

/*	
	// 在module_exit宏调用的函数中去注销字符设备驱动
	unregister_chrdev(mymajor, MYNAME);
*/	


	device_destroy(test_class, mydev);
	class_destroy(test_class);

	// 使用新的接口来注销字符设备驱动
	// 注销分2步:
	// 第一步真正注销字符设备驱动用cdev_del
	cdev_del(pcdev);
	// 第二步去注销申请的主次设备号
	unregister_chrdev_region(mydev, MYCNT);
}


module_init(chrdev_init);
module_exit(chrdev_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");				// 描述模块的许可证
MODULE_AUTHOR("aston");				// 描述模块的作者
MODULE_DESCRIPTION("module test");	// 描述模块的介绍信息
MODULE_ALIAS("alias xxx");			// 描述模块的别名信息

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值