字符设备驱动模型【转】

news/2024/7/6 6:31:41

本文转载自:http://blog.csdn.net/coding__madman/article/details/51347290

字符驱动编程模型:

1. 设备描述结构cdev

    1.1  结构定义

    1.2  设备号

    1.3  设备操作集

在Linux系统中,设备的类型非常繁多,如:字符设备,块设备,网络接口设备,USB设备,PCI设备,平台设备,混杂设备……,而设备类型不同,也意味着其对应的驱动程序模型不同,这样就导致了我们需要去掌握众多的驱动程序模型。那么能不能从这些众多的驱动模型中提炼出一些具有共性的规则,则是我们能不能学好Linux驱动的关键。

 

查看设备号可以通过查看/dev目录下设备号看到

次设备号

设备号的操作:

设备号的分配:

设备号的注销:

不论使用何种方法分配设备号,都应该在驱动退出时,使用unregister_chrdev_region函数释放这些设备号。

操作函数集:

操作函数集:

 

2. 字符设备驱动模型

描述结构的分配:

cdev变量的定义可以采用静态和动态两种方法

静态分配: struct cdev  mdev;

动态分配: struct cdev *pdev = cdev_alloc();

描述结构的初始化:

描述结构的注册:

字符设备的注册使用cdev_add函数来完成,函数原型:

cdev_add(struct cdev *p, dev_t dev, unsigned count)

参数:

p: 待添加到内核的字符设备结构

dev: 设备号

count: 该类设备的设备个数

剩下的就是根据相应的芯片手册,完成初始化。

实现设备操作:

设备操作原型:

打开设备,响应open系统调用

int  (*open)(struct inode *, struct file *)

 

 

关闭设备,响应close系统调用

int  (*release)(struct inode *, struct file *)

 

重定位读写指针,响应lseek系统调用

loff_t (*llseek)(struct file*, loff_t, int)

 

从设备读取数据,响应read系统调用

ssize_t(*read)(struct file*, char __user *, size_t, lofft *)

 

 

向设备写入 数据,响应write系统调用

ssize_t(*write)(struct file*, char __user *, size_t, lofft *)

 

Struct file: 在linux系统中,每一个打开的文件,在内核中都会关联一个struct file,它由在内核打开时创建,在文件关闭后释放。

重要成员:

loff_t  f_pos /* 文件读写指针 */

struct file_operations *f_op /* 读文件所对应的操作 */

 

struct inode

每一个存在于文件系统里面的文件都会关联一个inode结构,该结构主要用来记录文件物理上的信息。因此,它和代表打开文件的file结构是不同的。一个文件没有被打开时不会关联file结构,但是会关联一个inode结构。

重要成员: dev_t i_rdev /* 设备号 */

 

设备操作---open

open设备方法是驱动程序用来为以后的操作完成初始化准备工作的,在大部分驱动程序中,open完成如下工作:

    1> 标明次设备号

    2.> 启动设备

设备操作---release

release方法的作用正好与open相反。这个设备方法有时也称为close, 它的作用是关闭设备

设备操作---read

设备操作---write

最后就是驱动的注销了:当我们从内核中卸载驱动程序的时候,需要使cdev_del函数来完成字符设备的注销。

 

3. 范例驱动分析

这里给一个编写好的字符驱动示例代码:

memdev.c文件

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/init.h>  
  4. #include <linux/cdev.h>  
  5. #include <asm/uaccess.h>  
  6.   
  7. int dev1_registers[5];  
  8. int dev2_registers[5];  
  9.   
  10. struct cdev cdev;   
  11. dev_t devno;  
  12.   
  13. /*文件打开函数*/  
  14. int mem_open(struct inode *inode, struct file *filp)  
  15. {  
  16.       
  17.     /*获取次设备号*/  
  18.     int num = MINOR(inode->i_rdev);  
  19.       
  20.     if (num==0)  
  21.         filp->private_data = dev1_registers;  
  22.     else if(num == 1)  
  23.         filp->private_data = dev2_registers;  
  24.     else  
  25.         return -ENODEV;  //无效的次设备号  
  26.       
  27.     return 0;   
  28. }  
  29.   
  30. /*文件释放函数*/  
  31. int mem_release(struct inode *inode, struct file *filp)  
  32. {  
  33.   return 0;  
  34. }  
  35.   
  36. /*读函数*/  
  37. static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)  
  38. {  
  39.   unsigned long p =  *ppos;  
  40.   unsigned int count = size;  
  41.   int ret = 0;  
  42.   int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/  
  43.   
  44.   /*判断读位置是否有效*/  
  45.   if (p >= 5*sizeof(int))  
  46.     return 0;  
  47.   if (count > 5*sizeof(int) - p)  
  48.     count = 5*sizeof(int) - p;  
  49.   
  50.   /*读数据到用户空间*/  
  51.   if (copy_to_user(buf, register_addr+p, count))  
  52.   {  
  53.     ret = -EFAULT;  
  54.   }  
  55.   else  
  56.   {  
  57.     *ppos += count;  
  58.     ret = count;  
  59.   }  
  60.   
  61.   return ret;  
  62. }  
  63.   
  64. /*写函数*/  
  65. static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)  
  66. {  
  67.   unsigned long p =  *ppos;  
  68.   unsigned int count = size;  
  69.   int ret = 0;  
  70.   int *register_addr = filp->private_data; /*获取设备的寄存器地址*/  
  71.     
  72.   /*分析和获取有效的写长度*/  
  73.   if (p >= 5*sizeof(int))  
  74.     return 0;  
  75.   if (count > 5*sizeof(int) - p)  
  76.     count = 5*sizeof(int) - p;  
  77.       
  78.   /*从用户空间写入数据*/  
  79.   if (copy_from_user(register_addr + p, buf, count))  
  80.     ret = -EFAULT;  
  81.   else  
  82.   {  
  83.     *ppos += count;  
  84.     ret = count;  
  85.   }  
  86.   
  87.   return ret;  
  88. }  
  89.   
  90. /* seek文件定位函数 */  
  91. static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)  
  92. {   
  93.     loff_t newpos;  
  94.   
  95.     switch(whence) {  
  96.       case SEEK_SET:   
  97.         newpos = offset;  
  98.         break;  
  99.   
  100.       case SEEK_CUR:   
  101.         newpos = filp->f_pos + offset;  
  102.         break;  
  103.   
  104.       case SEEK_END:   
  105.         newpos = 5*sizeof(int)-1 + offset;  
  106.         break;  
  107.   
  108.       default:   
  109.         return -EINVAL;  
  110.     }  
  111.     if ((newpos<0) || (newpos>5*sizeof(int)))  
  112.         return -EINVAL;  
  113.           
  114.     filp->f_pos = newpos;  
  115.     return newpos;  
  116.   
  117. }  
  118.   
  119. /*文件操作结构体*/  
  120. static const struct file_operations mem_fops =  
  121. {  
  122.   .llseek = mem_llseek,  
  123.   .read = mem_read,  
  124.   .write = mem_write,  
  125.   .open = mem_open,  
  126.   .release = mem_release,  
  127. };  
  128.   
  129. /*设备驱动模块加载函数*/  
  130. static int memdev_init(void)  
  131. {  
  132.   /*初始化cdev结构*/  
  133.   cdev_init(&cdev, &mem_fops);  
  134.     
  135.   /* 注册字符设备 */  
  136.   alloc_chrdev_region(&devno, 0, 2, "memdev");  
  137.   cdev_add(&cdev, devno, 2);  
  138. }  
  139.   
  140. /*模块卸载函数*/  
  141. static void memdev_exit(void)  
  142. {  
  143.   cdev_del(&cdev);   /*注销设备*/  
  144.   unregister_chrdev_region(devno, 2); /*释放设备号*/  
  145. }  
  146.   
  147. MODULE_LICENSE("GPL");  
  148.   
  149. module_init(memdev_init);  
  150. module_exit(memdev_exit);  


Makefile 文件

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. obj-m := memdev.o  
  2. KDIR := /home/S5-driver/lesson7/linux-tiny6410/  
  3. all:  
  4.     make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm  
  5. clean:  
  6.     rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order  


编译驱动模块,通过NFS挂载开发板的根文件系统同步到开发板上

 

安装驱动模块 insmod memdev.ko

创建字符设备文件:(因为应用程序是通过操作设备文件来和相应的设备驱动通讯的,上面的说明有讲到)

字符设备文件和字符设备驱动又是通过主设备号联系起来的!

cat /proc/device 可以看到主设备号和设备驱动的名字!

从上面可以看到设备驱动memdev对应的设备驱动号为252

下面来通过mknod创建字符设备文件

mdmdev0 为给设备文件取的名字(这里只要不和其他的名字重复就行)

c 代表创建的是字符设备文件

252 是上面设备驱动所对应的主设备号 0代表的是次设备号 ,这里为非负就行

应用程序来通过字符设备文件来访问字符设备驱动程序:(这里操作的是虚拟的字符设备文件,通过上面的memdev.c可以看出)

read-memdev.c

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>  
  2. #include <sys/types.h>  
  3. #include <sys/stat.h>  
  4. #include <fcntl.h>  
  5.   
  6. int main()  
  7. {  
  8.     int fd = 0;  
  9.     int dst = 0;  
  10.       
  11.     /*打开设备文件*/  
  12.     fd = open("/dev/memdev0",O_RDWR);  
  13.       
  14.     /*写入数据*/  
  15.     read(fd, &dst, sizeof(int));  
  16.       
  17.     printf("dst is %d\n",dst);  
  18.       
  19.     /*关闭设备*/  
  20.     close(fd);  
  21.       
  22.     return 0;     
  23.   
  24. }  

write-memdev.c

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>  
  2. #include <sys/types.h>  
  3. #include <sys/stat.h>  
  4. #include <fcntl.h>  
  5.   
  6. int main()  
  7. {  
  8.     int fd = 0;  
  9.     int src = 2013;  
  10.       
  11.     /*打开设备文件*/  
  12.     fd = open("/dev/memdev0",O_RDWR);  
  13.       
  14.     /*写入数据*/  
  15.     write(fd, &src, sizeof(int));  
  16.       
  17.     /*关闭设备*/  
  18.     close(fd);  
  19.       
  20.     return 0;     
  21.   
  22. }  



交叉编译,这里使用静态编译!因为一些库开发板上还没有移植过去!

 

比如上面不使用-static选项编译write-mem.c 在开发板上运行应用程序就会出现

这里可以看到这个应用程序的运行需要有libc.so.6这个动态链接库!然后去开发板的系统上lib目录下去查看有没有这个库。

空的,什么也没有,所以解决这个问题的有两个办法,一个是把这个库复制到lib目录下,一种方法是在编译的时候加上-static选项 选择静态编译的方法编译程序!

至此整个字符设备驱动和应用程序是如何联系在一起的以及整个字符驱动设备模型简介over!

转载于:https://www.cnblogs.com/zzb-Dream-90Time/p/6254993.html


http://www.niftyadmin.cn/n/3336792.html

相关文章

LINUX编程实战指发送UDP消息

最近调试媒体服务器&#xff0c;发现被叫的媒体流总是抖动的厉害&#xff0c;不清楚是网络原因还是媒体服务器的代码问题。 为了方便排查问题&#xff0c;我编写了一个UDP发送的小工具。根据传入目的地的IP、PORT和发送的时长。该工具就可以按照20ms的间隔进行重复发送RTP流。…

ioctl--字符设备的控制技术【转】

本文转载自&#xff1a;http://blog.csdn.net/coding__madman/article/details/51356313 字符设备的控制 1. 字符设备控制理论 1.1 作用 大部分驱动程序除了需要提供读写设备的能力外&#xff0c;还需要具备控制设备的能力。比如&#xff1a;改变波特率 1.2 应用程序接口 在用户…

linux中oops信息的调试及栈回溯【转】

本文转载自&#xff1a;http://blog.csdn.net/kangear/article/details/8217329 原文地址&#xff1a;http://blog.micro-studios.com/?p615#comment-1069 看后感想&#xff1a;這点比 ldd3上讲的都仔细 2012年11月29日11:24:17&#xff1a;有BUG_ON就不用反汇编了。。。 201…

如何关闭CenOS/RedHat的X Windows

因为要安装CUDA驱动&#xff0c;CUDA驱动不能X Windows下安装&#xff0c;所以搜索关闭X Windows的方法。 现在总结如下&#xff1a; root用户使用init 3&#xff0c;切换到命令行模式&#xff1b; root用户使用init 5,切换到X Windows图形界面模式

总线设备驱动模型【转】

本文转载自&#xff1a;http://blog.csdn.net/coding__madman/article/details/51428400 总线驱动设备模型&#xff1a; 1. 总线设备驱动模型概述 随着技术的不断进步&#xff0c;系统的拓扑结构也越来越复杂&#xff0c;对热插拔&#xff0c;跨平台移植性的要求也越来越高&…

混杂设备动态次设备号分析【转】

本文转载自&#xff1a;http://blog.csdn.net/yongan1006/article/details/6778285 今天看驱动源码时&#xff0c;发现一个MISC_DYNAMIC_MINOR宏&#xff0c;于是分析了一下内核源码。先粘出源码。在misc_register函数中&#xff0c;有如下语句&#xff1a; if (misc->minor…

Linux的fasync驱动异步通知详解【转】

本文转载自&#xff1a;http://blog.csdn.net/coding__madman/article/details/51851338 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 工作项目用有个需求是监测某个GPIO输入方波的频率&#xff01;通俗的讲就是一个最最简单的测方波频率的示波…

如何将.m4a转.wav文件

昨天现场交付的同事找到我&#xff0c;想把一个.m4a格式的文件转为.wav文件&#xff0c;目的是可以使得现场的媒体服务器将语音播放出来。 我习惯性的打开了adobe audition文件打开语音进行转换&#xff0c;但是该软件提示找不到acc.dll文件。我下载acc.dll文件后&#xff0c;通…