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

来源:互联网 发布:淘宝js跳转楼层 编辑:程序博客网 时间:2024/05/17 03:24
//misc混杂设备驱动框架是对cdev字符设备驱动驱动的进一步封装,用的更方便/**********************************************内核源码里字符设备驱动结构体和函数分析************************************//*****************cdev.h********************//*****************/博客原文地址:http://blog.csdn.net/oyhb_1992/article/details/77596411struct cdev {struct kobject kobj;struct module *owner;const struct file_operations *ops;struct list_head list;dev_t dev;   //设备号(包括主次设备号)unsigned int count;   //设备个数};//初始化cdevvoid 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设备号成员,并向内核注册cdevint 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.hclass_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-3GPM4DAT = (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/77596411static 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/devprintk(“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();//注册cdevret = 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用哪个都可以,接口太过随意,你想用哪个用哪个,这样如果驱动层修改了接口层,应用层也得跟着改,说白了就是太过灵活随意,没有一套规范导致应用层也得跟着驱动层改变而改变,像输入设备框架则统一了驱动和引用的接口,他们之间通过事件发送的数据包来通信。


 

原创粉丝点击