• 自动创建字符设备的设备文件

目前尚不是最终版本,还望有心人自己学习的时候,把自己整合的知识点相关的答案也好问题也好,或者实践过程中的一些操作截图,再或者其他的一些想要分享材料发给笔者邮箱:uestc_ganlin@163.com,我们一起完善这篇博客!笔者写这篇博客的时候已经工作第四个年头了,目前是在整理之前有过的学习资料,仅作为笔记,供同志们参考!短时间内可能不会去全部完善。

  • 自动创建字符设备的设备文件

问题描述:

整体流程回顾?

使用mknod创建设备文件的缺点?

能否自动生成和删除设备文件?

解决方案:udev(嵌入式中用的是mdev);

什么是udev?应用层的一个应用程序!

内核驱动和应用层udev之间有一套信息传输机制(netlink协议);

应用层启用udev,内核驱动中使用相应接口;

驱动注册和注销时信息会被传给udev,由udev在应用层进行设备文件的创建和删除;

内核驱动设备类相关函数:class_create,device_create;

编程实践?

完整的相关代码见下述文件:

module_test.c

// 为了module_init,module_exit相关的,加入下面头文件
#include <linux/module.h>
// 为了__init,__exit相关的,加入下面头文件
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
// arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#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       250
#define MYCNT       1
#define MYNAME      "test_chrdev"// #define GPJ0CON       S5PV210_GPJ0CON
// #define GPJ0DAT      S5PV210_GPJ0DAT// #define rGPJ0CON  *((volatile unsigned int *)GPJ0CON)
// #define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)#define GPJ0CON_PA   0xe0200240
#define GPJ0DAT_PA  0xe0200244unsigned 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;// 内核空间的buf
char kbuf[100]; static int test_chrdev_open(struct inode *inode, struct file *file)
{// 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分// 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来意思意思。printk(KERN_INFO "test_chrdev_open\n");// rGPJ0CON = 0x11111111;*pGPJ0CON = 0x11111111;// 三个灯亮//rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));*pGPJ0DAT = ((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));*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); return 0;
}ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{int ret = -1;printk(KERN_INFO "test_chrdev_read\n");ret = copy_to_user(ubuf, kbuf, count);if (ret){printk(KERN_ERR "copy_to_user fail\n");return -EINVAL;}printk(KERN_INFO "copy_to_user success..\n");return 0;
}// 写函数的本质就是将应用层传递过来的数据先复制到内核中,然后将之以正确的方式写入硬件完成操作。
static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf,size_t count, loff_t *ppos)
{int ret = -1;printk(KERN_INFO "test_chrdev_write\n");// 使用该函数将应用层传过来的ubuf中的内容拷贝到驱动空间中的一个buf中//memcpy(kbuf, ubuf);不行,因为2个buf不在一个地址空间中memset(kbuf, 0, sizeof(kbuf));ret = copy_from_user(kbuf, ubuf, count);if (ret){printk(KERN_ERR "copy_from_user fail\n");return -EINVAL;}printk(KERN_INFO "copy_from_user success..\n");/*// 真正的驱动中,数据从应用层复制到驱动中后,我们就要根据这个数据// 去写硬件完成硬件的操作。所以这下面就应该是操作硬件的代码if (!strcmp(kbuf, "on")){rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));}else if (!strcmp(kbuf, "off")){rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));}*/if (kbuf[0] == '1'){// rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));    }else if (kbuf[0] == '0'){// rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));    }return 0;
}// 自定义一个file_operations结构体变量,并且去填充
static const struct file_operations test_fops = {// 惯例,直接写即可.owner      = THIS_MODULE,     // 将来应用open打开这个设备时实际调用的// 就是这个.open对应的函数.open       = test_chrdev_open,            .release    = test_chrdev_release,     .write      = test_chrdev_write,.read      = test_chrdev_read,
};// 模块安装函数
static int __init chrdev_init(void)
{   int retval;printk(KERN_INFO "chrdev_init helloworld init\n");/*// 在module_init宏调用的函数中去注册字符设备驱动// major传0进去表示要让内核帮我们自动分配一个合适的空白的没被使用的主设备号// 内核如果成功分配就会返回分配的主设备号;如果分配失败会返回负数mymajor = register_chrdev(0, MYNAME, &test_fops);if (mymajor < 0){printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);*/// 使用新的cdev接口来注册字符设备驱动// 新的接口注册字符设备驱动需要2步// 第1步:注册/分配主次设备号// mydev = MKDEV(MYMAJOR, 0);// retval = register_chrdev_region(mydev, MYCNT, MYNAME);retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);if (retval) {// printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);// return -EINVAL;printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);goto flag1;}// printk(KERN_INFO "register_chrdev_region success\n");printk(KERN_INFO "alloc_chrdev_region success\n");printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev));// 第2步:注册字符设备驱动// 给pcdev分配内存,指针实例化pcdev = cdev_alloc();// cdev_init(&test_cdev, &test_fops);      // cdev_init(pcdev, &test_fops);pcdev->owner = THIS_MODULE;pcdev->ops = &test_fops;// retval = cdev_add(&test_cdev, mydev, MYCNT);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/testdevice_create(test_class, NULL, mydev, NULL, "test_chrdev");// 使用动态映射的方式来操作寄存器if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))// return -EINVAL;goto flag3;if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0DAT"))// return -EINVAL;goto flag3;pGPJ0CON = ioremap(GPJ0CON_PA, 4);pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);*pGPJ0CON = 0x11111111;*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));       /*// 模块安装命令insmod时执行的硬件操作rGPJ0CON = 0x11111111;rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));printk(KERN_INFO "GPJ0CON = %p.\n", GPJ0CON);printk(KERN_INFO "GPJ0DAT = %p.\n", GPJ0DAT);*/// goto flag0:return 0;// 如果第4步才出错跳转到这里来
flag4:release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4);// 如果第3步才出错跳转到这里来
flag3:// cdev_del(&test_cdev);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");// rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); // 解除映射iounmap(pGPJ0CON);iounmap(pGPJ0DAT);release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4);device_destroy(test_class, mydev);class_destroy(test_class);/*// 在module_exit宏调用的函数中去注销字符设备驱动unregister_chrdev(mymajor, MYNAME);*/// 使用新的接口来注销字符设备驱动// 注销分2步:// 第一步真正注销字符设备驱动用cdev_del// cdev_del(&test_cdev);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");    

Makefile

# ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个
# KERN_VER = $(shell uname -r)
# KERN_DIR = /lib/modules/$(KERN_VER)/build    # 开发板的linux内核的源码树目录
KERN_DIR = /root/qt/kernelobj-m    += module_test.oall:make -C $(KERN_DIR) M=`pwd` modules
#   arm-none-linux-gnueabi-gcc app.c -o appcp:cp *.ko /root/removal/rootfs/root/driver_test
#   cp app /root/removal/rootfs/root/driver_test.PHONY: clean
clean:make -C $(KERN_DIR) M=`pwd` modules clean

app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>#define FILE   "/dev/test_chrdev"char buf[100];int main(void)
{// 打开文件int fd = -1;int i = 0;fd = open(FILE, O_RDWR);if (fd < 0){printf("open %s error.\n", FILE);return -1;}printf("open %s success..\n", FILE);// 读写文件while (1){memset(buf, 0 , sizeof(buf));printf("请输入 on | off \n");scanf("%s", buf);if (!strcmp(buf, "on")){write(fd, "1", 1);}else if (!strcmp(buf, "off")){write(fd, "0", 1);}else if (!strcmp(buf, "flash")){for (i=0; i<3; i++){write(fd, "1", 1);sleep(1);write(fd, "0", 1);sleep(1);}}   else if (!strcmp(buf, "quit")){break;}}// 关闭文件close(fd);return 0;
}
  • 设备类相关代码分析

sys文件系统简介:

sys文件系统的设计思想?

设备类的概念?

/sys/class/xxx/中的文件的作用?

源码分析?为了加深功力可以去看相关内核源码,这里仅以分析出来的主要函数调用关系作为简单记录:

类的创建分析:

class_create__class_create__class_registerkset_registerkobject_uevent

设备的创建分析:

device_createdevice_create_vargskobject_set_name_vargsdevice_registerdevice_addkobject_adddevice_create_filedevice_create_sys_dev_entrydevtmpfs_create_nodedevice_add_class_symlinksdevice_add_attrsdevice_pm_addkobject_uevent

Linux:驱动之自动创建字符设备的设备文件(未完)相关推荐

  1. linux驱动:自动创建设备节点

    在加载驱动模块后,就要自己使用mknod创建设备节点,这样虽然是可行的,但是比较麻烦.我们可以在__init()函数里面添加一些函数,自动创建设备节点.创建设备节点使用了两个函数 class_crea ...

  2. linux 设备节点 驱动,【Linux驱动】自动创建设备节点

    开始学习驱动的时候,是将驱动程序编译成模块然后用mknod命令手动建立设备节点以提供给应用程序调用.这对于刚开始调试驱动程序的时候常用的一种方法.但是,当有种需要必须在系统启动的时候就将驱动程序就绪, ...

  3. linux驱动开发5之字符设备驱动新接口

    1.新接口与老接口 1)老接口:register_chrdev static inline int register_chrdev(unsigned int major, const char *na ...

  4. linux驱动开发篇(三)—— 总线设备驱动模型

    linux系列目录: linux基础篇(一)--GCC和Makefile编译过程 linux基础篇(二)--静态和动态链接 ARM裸机篇(一)--i.MX6ULL介绍 ARM裸机篇(二)--i.MX6 ...

  5. linux内核创建字符节点,Tiny6410学习ing—(四)、嵌入式Linux内核驱动进阶—(7)、高级字符设备驱动(自动创建节点)—#931...

    按照国嵌的视频教程上来说的,最后就是-自动创建设备文件! 其实我感觉以前完全可以直接是手动创建了设备文件,然后就可以直接讲述自动创建设备文件,为啥非要拖到最后来讲述,我也就不清楚了!! 不管了,写完收 ...

  6. linux字符驱动之自动创建设备节点

    上一节中,我们是手工创建设备节点,大家肯定也会觉得这样做太麻烦了. 上一节文章链接:https://blog.csdn.net/qq_37659294/article/details/10430270 ...

  7. 字符设备驱动高级篇3——自动创建字符设备驱动的设备文件

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 1.问题描述 使用mknod创建设备文件的缺点: 能否自动生成和删除设备文件: 2.解决方案:udev(嵌入式中用 ...

  8. Linux 驱动开发 三:字符设备驱动框架

    一.参考 (3条消息) Linux 字符设备驱动结构(一)-- cdev 结构体.设备号相关知识解析_知秋一叶-CSDN博客 (3条消息) linux设备驱动框架_不忘初心-CSDN博客_linux设 ...

  9. linux驱动编写(虚拟字符设备编写)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 昨天我们说了一些简单模块编写方法,但是终归没有涉及到设备的编写内容,今天我们就可以了解一下相关 ...

最新文章

  1. Linux 2.6.32-279.el6.x86_64 ANDROID SDK碰到”LIBC.SO.6: VERSION `GLIBC_2.14′ NOT FOUND”的解决方法(2)
  2. 20220209-CTF MISC-BUUCTF-难易程度:简单
  3. Windows Server 2016之RDS配置证书
  4. 联想一体机电源键不亮_联想电脑一体机B505拆机经验
  5. mysql定时作业_mysql 让一个存储过程定时作业的代码(转)
  6. 电脑、手机装机必备软件的抉择[土豆-侃天下]
  7. 阈值分割法——最佳阈值的选择问题
  8. 太阳直射点纬度计算公式_利用旗杆影子——判断日出日落、季节、昼长、经纬度、太阳高度角...
  9. 10.2项目干系人管理+信息系统项目管理+野马合集
  10. 算法系列之十九:用天文方法计算日月合朔(新月)
  11. 【无法完成更新 正在撤销更改 请不要关闭你的计算机】更新失败解决方案
  12. POI操作excel基础用法详解
  13. 广州工作2个月!差人的公司可以call我
  14. Nginx重启时提示nginx: [emerg] bind() to 0.0.0.0解决方法 老蒋 发布于 2019-06-03 分类:运维笔记 评论(0) 2000+站长交流QQ群: 59
  15. P2P模式分析之资金池与第三方托管
  16. Fri Oct 7 10:08:00 UTC 0800 2016日期格式转换为 yyyy-mm-dd hh24:mi:ss (Map实现版)
  17. 给mongodb设置密码
  18. 新一代PHP MYSQL DREAMWEAVER网站建设典型案例pdf
  19. TP框架下设置静态资源缓存
  20. 如何高效实现客户服务自助,打造在线产品帮助中心即可

热门文章

  1. arcgis server 服务发布搭建总流程(以本机作为服务器)
  2. UDP 打洞 java demo
  3. 聊一聊,如何做好垂直域稳定性
  4. 旋转rot和翻转flip
  5. 不同坐标系数据切片统一方法
  6. 第十周实验指导--任务3--先建立一个Point(点)类,再派生出一个Circle(圆)类,再派生出一个Cylinder(圆柱体)类...
  7. Sublimenbsp;Textnbsp;2nbsp;快键健
  8. Maven更换远程仓库
  9. itext模板生成pdf 后添加动态表格
  10. DeepFashion2服饰数据集下载(附解压密码)