字符设备驱动
来源:互联网 发布:视频剪刀手软件 编辑:程序博客网 时间:2024/06/08 11:27
1:本文实现简单的字符设备的驱动,并且提供两种访问设备资源的两种方法:一是传统的利用文件接口来访问,二是利用devfs文件系统来访问;
2:首先来看hello.h文件,其中实现了用户自定义的数据结构体
#include <linux/cdev.h>#include <linux/semaphore.h>struct hello_dev{int val; /*共用的设备资源,4字节数据*/struct semaphore sem; /*同步和互斥信号量*/struct cdev cdev; /*字符设备结构体*/};其中val变量我们模拟它是设备的资源,可以理解为用户访问设备的一个4字节大小的寄存器,sem实现对val变量访问的同步,cdev是代表一个字符设备;
3:模块加载和卸载函数
/*注册字符设备和初始化信号量*/static int hello_setup_chardev(void){int err;dev_t devno = 0;devno = MKDEV(hello_major,hello_minor);memset(hello_dev,0,sizeof(struct hello_dev));cdev_init(&(hello_dev->cdev),&hello_device_fops);hello_dev->cdev.owner = THIS_MODULE;hello_dev->cdev.ops = &hello_device_fops;err = cdev_add(&(hello_dev->cdev),devno,1); if(err){return err;}sema_init(&(hello_dev->sem),1);hello_dev->val = 41;return 0;}static int __init hello_init(void){int result = 0;dev_t devno = 0;struct device *dev = NULL;/*申请字符设备号*/if(hello_major){devno = MKDEV(hello_major,hello_minor);result = register_chrdev_region(devno,1,"hello_device"); /*hello_device是字符设备名字,在proc/devices下可见*/}else{result = alloc_chrdev_region(&devno,0,1,"hello_device"); /*动态申请一个字符设备,从设备号为0*/}if(result < 0){printk(KERN_ALERT "Failed register char dev region\n");goto fail;}hello_major = MAJOR(devno);hello_minor = MINOR(devno);/*动态分配hello_dev结构体*/hello_dev = kmalloc(sizeof(struct hello_dev),GFP_KERNEL);if(hello_dev == NULL){result = -ENOMEM;printk(KERN_ALERT "Failed alloc hello_dev\n");goto unregister;}/*注册字符设备,初始化信号量*/result = hello_setup_chardev();if(result){printk(KERN_ALERT "Failed setup char dev\n");goto free;}/*在sys/class/目录下创建hello目录*/hello_class = class_create(THIS_MODULE,"hello");if(IS_ERR(hello_class)){result = PTR_ERR(hello_class);printk(KERN_ALERT "Failed to create hello class.\n");goto cdev_destroy;}/*在/dev/目录和/sys/class/hello目录下分别创建设备文件hello*/dev = device_create(hello_class,NULL,devno,NULL,"hello");if(IS_ERR(dev)){result = PTR_ERR(dev);printk(KERN_ALERT "Failed to create hello device.\n");goto class_destroy;}/*在sys/class/hello/hello目录下创建属性文件*/result = device_create_file(dev,&dev_attr_val);if(result < 0){printk(KERN_ALERT "Failed to create arrribute val.\n");goto device_destroy;}printk(KERN_ALERT "hello char device init ok\n");return 0;device_destroy:device_destroy(hello_class,devno);class_destroy:class_destroy(hello_class);cdev_destroy:cdev_del(&(hello_dev->cdev));free:kfree(hello_dev);unregister:unregister_chrdev_region(devno,1);fail:return result;}static void __exit hello_exit(void){dev_t devno = 0;printk(KERN_ALERT "hello char device exit\n");devno = MKDEV(hello_major,hello_minor);device_destroy(hello_class,devno);class_destroy(hello_class);cdev_del(&(hello_dev->cdev));kfree(hello_dev);unregister_chrdev_region(devno,1);}module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("Dual BSD/GPL");MODULE_AUTHOR("GOLF/FXB,<1029930509@qq.com>");MODULE_DESCRIPTION("A SIMPLE CHAR DEVICE DRIVER");模块加载函数主要完成:字符设备设备好申请,自定义设备结构体hello_dev动态申请内存空间,注册字符设备,创建sys节点;
4:文件操作函数的实现
static int hello_open(struct inode *inode, struct file *filp){struct hello_dev *dev;/*将文件节点的私有数据指针指向用户自定义的数据结构体,方便read和write方法中使用*/dev = container_of(inode->i_cdev, struct hello_dev, cdev);filp->private_data = dev;return 0;}static ssize_t hello_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos){ssize_t result = 0;int cnt = count;/*取出用户自定义设备结构体*/struct hello_dev *dev = filp->private_data;/*信号量使用,用于多个写进程之间互斥以及读进程同步*/if(down_interruptible(&(dev->sem))){return -ERESTARTSYS;}/*写界限控制*/if(*f_pos >= sizeof(dev->val))goto out;/*写数据字节数控制*/if(count > sizeof(dev->val) - *f_pos){count = sizeof(dev->val)- *f_pos;}/*从用户空间读取count个字节的数据给val变量*/if(copy_from_user(&(dev->val),buf,count)){result = -EFAULT;goto out;}/*每次跟新pos指针*/*f_pos += count;out:up(&(dev->sem));return cnt; /*使用echo写设备节点的时候,必须返回cnt,否则进程会死循环*/}static ssize_t hello_read(struct file *filp, char __user *buf, size_t count,loff_t *f_pos){ssize_t result = 0;struct hello_dev *dev = filp->private_data; if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(*f_pos > sizeof(dev->val)) goto out; if(count > sizeof(dev->val) - *f_pos) {count = sizeof(dev->val) - *f_pos; } if(copy_to_user(buf,&(dev->val),count)) { result = -EFAULT; goto out; } *f_pos += count; result = count;out:up(&(dev->sem));return result;}/*设备文件释放时调用*/int hello_release(struct inode *inode, struct file *filp){return 0;}/*字符设备文件操作结构体*/static struct file_operations hello_device_fops ={.owner = THIS_MODULE,.open = hello_open,.release = hello_release,.read = hello_read,.write = hello_write,};分别实现文件的open,read,write方法,注意:在使用cat指令读取文件节点的时候,read方法中一定要有判断文件读结束的语句(也就是read函数返回0字节数据),这样cat进程才会终止,否则,进程死循环;在使用echo写文件节点的时候,write函数一定要返回echo进程调用write函数时参数count的值,否则echo进程不会终止,具体见上面代码;
5:文件节点属性和方法的实现
/*val属性的读方法,读取val的值到缓冲区buf中,内核态使用*/static ssize_t hello_val_show(struct device *dev,struct device_attribute *attr, char *buf){int val = 0;if(down_interruptible(&(hello_dev->sem))){return -ERESTARTSYS;}val = hello_dev->val;up(&(hello_dev->sem));return sprintf(buf,"%x\n",val); /*必须返回转换的字节数*/}/*var属性的写方法,将缓冲区buf中的值写到val中,内核态使用*/static ssize_t hello_val_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t size){if(down_interruptible(&(hello_dev->sem))) /*同步和互斥*/{return -ERESTARTSYS;}hello_dev->val = simple_strtol(buf,NULL,16); /*将buf缓冲中的字符转换成10进制数*/up(&(hello_dev->sem)); /*释放信号量*/return size; /*必须返回size*/}/*dev_attr_val结构体定义*/static DEVICE_ATTR(val, S_IRUGO | S_IWUSR,hello_val_show, hello_val_store);模块初始化函数中创建了文件节点val,上面代码实现了val属性对应的方法,面向对象的思想,show和store函数分别实现val属性的读和写的方法;
0 0
- 字符设备驱动--- 设备操作
- 字符设备驱动更新
- 字符设备驱动模板
- 字符设备驱动模板
- 字符设备驱动1
- 字符设备驱动编写
- LINUX--字符设备驱动
- 字符设备驱动01
- 字符设备驱动02
- LED字符设备驱动
- led字符设备驱动
- Linux字符设备驱动
- 字符设备驱动
- Linux字符设备驱动
- Linux字符设备驱动
- 字符设备驱动分析
- 字符设备驱动详解
- 字符设备驱动实验
- ReactJs 学习
- 简单的echart图表实现
- RPG项目开发总结
- YII2框架表单-model(验证)-HTML_help部件 URL_help部件 以注册页面为实例
- nginx系统真正有效的图片防盗链完整设置详解
- 字符设备驱动
- char * strchr (const char *str, int c);
- css3中word-break与word-break的区别
- 蓝桥杯第七届 平方怪圈
- Android的Fragment使用总结
- 几个小方法,菲波那切数列,定时器超时验证,十六进制字符串判断
- Hakodate shopping advice
- STM32F4读写内部FLASH【使用库函数】
- Android Studio 安装问题- unable to access android sdk add-on list