//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用哪个都可以,接口太过随意,你想用哪个用哪个,
这样如果驱动层修改了接口层,应用层也得跟着改,说白了就是太过灵活随意,没有一套规范导致应用层也得跟着驱动层改变而改变
,像输入设备框架则统一了驱动和引用的接口,他们之间通过事件发送的数据包来通信。