字符设备驱动框架3:深入探讨—完整的驱动代码工程

//misc混杂设备驱动框架是对cdev字符设备驱动驱动的进一步封装,用的更方便
/**********************************************内核源码里字符设备驱动结构体和函数分析************************************/


/*****************cdev.h********************/
/*****************/博客原文地址:http://blog.csdn.net/oyhb_1992/article/details/77596411
struct cdev {
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;
	struct list_head list;
	dev_t dev;   //设备号(包括主次设备号)
	unsigned int count;   //设备个数
};
//初始化cdev
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev);   //在初始化cdev时会将所有的cdev成员清零
	INIT_LIST_HEAD(&cdev->list);
	kobject_init(&cdev->kobj, &ktype_cdev_default);
	cdev->ops = fops;  //将cdev和操作函数集关联
}
//将设备号添加进cdev里的dev设备号成员,并向内核注册cdev
int cdev_add(struct cdev *, dev_t, unsigned);


/*****************char_dev.h********************/
/*****************博客原文地址:http://blog.csdn.net/oyhb_1992/article/details/77596411********************/
//分配设备号

//静态分配设备号,不推荐
int register_chrdev_region(dev_t from, unsigned count, const char *name)
from:起始设备号(包括主次设备号)
count:分配设备号数量

//动态分配设备号,只要函数名带alloc单词就是动态分配
dev_t *dev:返回的主设备号,指针型参数用于返回值
baseminor:次设备号可以指定,主设备号不能指定只能内核动态分配
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
{
	struct char_device_struct *cd;
	cd = __register_chrdev_region(0, baseminor, count, name);//0表示动态分配主设备号
	if (IS_ERR(cd))
		return PTR_ERR(cd);
	*dev = MKDEV(cd->major, cd->baseminor);
	return 0;
}

//这三个宏用于拼接主次设备号
#define MAJOR(dev)	((dev)>>8)
#define MINOR(dev)	((dev) & 0xff)
#define MKDEV(ma,mi)	((ma)<<8 | (mi))


/*****************创建设备节点的方法********************/
/*****************博客原文地址:http://blog.csdn.net/oyhb_1992/article/details/77596411********************/

//方法1:手动创建
#mknod  /dev/***  c  主设备号   次设备号

方法2:自动创建
include\linux\device.h
class_create(THIS_MODULE)
extern struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...)
__attribute__((format(printf, 5, 6)));


/**********************************************字符设备驱动代码实例:************************************************/
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<mach/regs-gpio.h>
#include <linux/device.h>
#define DEVNAME “ldm”   //设备名

#define GPM4CON (*(volatile u32*)((u32)S5P_VA_GPIO2+0x02E0))    //静态映射控制寄存器
#define GPM4DAT (*(volatile u8*)((u32)S5P_VA_GPIO2+0x02E4))
//自定义一个结构体描述
struct ldm_info{
				struct cdev dev;  //cdev设备
				struct file_operations fops;	//操作函数集
				dev_t devno;	//设备号
				struct class *cls;//用于自动创建设备节点
}
static struct ldm_info ldm;

static void led_init(void)
{
	//GPM4_0-3设为输出
	GPM4CON = (GPM4CON &~0XFFFF) | 0X1111;
}
static void led_on(u8 stat)
{
	//GPM4_0-3
	GPM4DAT = (GPM4DAT &~0XFF) | stat;
}
static size_t ldm_write (struct file *fp, const char __user *buf, size_t size, loff_t *offset);
{
	led_on(*buf);
	return size;
}
/*****************/博客原文地址:http://blog.csdn.net/oyhb_1992/article/details/77596411
static int test_init(void)
{
	int ret;
	printk(“%s: %s\n”,__FUNCTION__,__FILE__);
	//初始化cdev对象
	cdev_init(ldm.dev,& ldm.fops);

	//先尝试静态申请设备号
	ldm.devno = MKDEV(1,0);
	ret = register_chrdev_region(MKDEV(1,0), 1, DEVNAME);
	if(ret<0)
	{	//静态申请失败则用动态申请
		ret =alloc_chrdev_region(&ldm.dev, 0, 1, DEVNAME);
		if(ret<0)
		{
			printk(KERN_ERR” alloc_chrdev_region failed\n”);
			goto  err_ alloc_chrdev_region;
		}
	}

	//打印出设备号,方便手工创建设备节点
	//也可以用命令 #cat /proc/dev
	printk(“major = %d, minor= %d\n” ,MAJOR(ldm.devno), MINOR(ldm.devno));
	
	//填充file_operation中的操作方法
	//cdev_init函数里将cdev结构体都清零,所以要先cdev_init调用这个函数后再对结构体初始化memset(cdev, 0, sizeof *cdev);
	ldm.fops.write =  ldm_write;
	led_init();

	//注册cdev
	ret = cdev_add(&ldm.dev, ldm.devno, 1);
	if(ret<0)
	{
		printk(KERN_ERR” cdev_add failed\n”);
		goto  err_ cdev_add;
	}

	//自动创建设备节点
	ldm.cls = class_create(THIS_MODULE,DEVNAME);
	if(IS_ERR(ldm.cls))
	{
		printk(KERN_ERR “class_create failed\n“);
		ret = PTR_ERR(ldm.cls);      //将指针强转为错误码
		goto class_create failed;
	}
	struct device * dev =  device_create(ldm.cls, NULL,251, ldm.devno, NULL, DEVNAME);//创建设备节点
	if(IS_ERR(dev))
	{
		printk(KERN_ERR “device_create failed\n“);
		ret = PTR_ERR(dev);      //将指针强转为错误码
		goto device_create failed;
	}



	class_create failed:
		class_del(&ldm.dev);
	err_ cdev_add:
		unregister_chrdev_region(ldm.devno,1);
	err_ alloc_chrdev_region:
	return ret;
}

static void test_exit(void)
{
	printk(“%s: %s\n”,__FUNCTION__,__FILE__);
	//卸载模块时自动销毁设备节点
	device_destroy(ldm.cls, ldm.devno);
	class_destory(ldm.cls);
	cdev_del();
	unregister_chrdev_region(MKDEV(1,0), 1, DEVNAME);

}

module_init(test_init)
module_exit(test_exit)
MODULE_LICENSE("GPL");


	misc混杂设备驱动,frame buff ,输入设备框架都是基于字符设备驱动框架cdev进行进一步封装的设备框架
	区别在于它们的应用层的接口不同:
	字符设备驱动框架cdev的接口你可以随意使用,如write,read,ioctl用哪个都可以,接口太过随意,你想用哪个用哪个,
这样如果驱动层修改了接口层,应用层也得跟着改,说白了就是太过灵活随意,没有一套规范导致应用层也得跟着驱动层改变而改变
,像输入设备框架则统一了驱动和引用的接口,他们之间通过事件发送的数据包来通信。








 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值