国嵌内核驱动进阶班-7-3(阻塞型字符设备驱动)

  • 为什么阻塞?

在阻塞方式下,写没有足够的空间或读时候没有数据。

※ 阻塞方式是文件读写的默认方式。没有空间或者读时没有数据返回错误。



※残留问题:

驱动程序中全局变量 在不同进程间是共享的吗?


  • 阻塞型设备驱动程序


 驱动代码: 

 1 #ifndef _MEMDEV_H_
 2 #define _MEMDEV_H_
 3
 4 #ifndef MEMDEV_MAJOR
 5 #define MEMDEV_MAJOR 0   /*预设的mem的主设备号*/
 6 #endif
 7
 8 #ifndef MEMDEV_NR_DEVS
 9 #define MEMDEV_NR_DEVS 2    /*设备数*/
10 #endif
11
12 #ifndef MEMDEV_SIZE
13 #define MEMDEV_SIZE 4096
14 #endif
15
16 /*mem设备描述结构体*/
17 struct mem_dev
18 {
19   char *data;
20   unsigned long size;
21   wait_queue_head_t inq;
22 };
23
24 #endif /* _MEMDEV_H_ */
  1 #include <linux/module.h>
  2 #include <linux/types.h>
  3 #include <linux/fs.h>
  4 #include <linux/errno.h>
  5 #include <linux/mm.h>
  6 #include <linux/sched.h>
  7 #include <linux/init.h>
  8 #include <linux/cdev.h>
  9 #include <asm/io.h>
 10 #include <linux/slab.h>
 11 #include <asm/uaccess.h>
 12
 13 #include "memdev.h"
 14
 15 static int mem_major = MEMDEV_MAJOR;
 16 bool have_data = false; /*表明设备有足够数据可供读*/
 17
 18 module_param(mem_major, int, S_IRUGO);
 19
 20 struct mem_dev *mem_devp; /*设备结构体指针*/
 21
 22 struct cdev cdev;
 23
 24 /*文件打开函数*/
 25 int mem_open(struct inode *inode, struct file *filp)
 26 {
 27     struct mem_dev *dev;
 28
 29     /*获取次设备号*/
 30     int num = MINOR(inode->i_rdev);
 31
 32     if (num >= MEMDEV_NR_DEVS)
 33             return -ENODEV;
 34     dev = &mem_devp[num];
 35
 36     /*将设备描述结构指针赋值给文件私有数据指针*/
 37     filp->private_data = dev;
 38
 39     return 0;
 40 }
 41
 42 /*文件释放函数*/
 43 int mem_release(struct inode *inode, struct file *filp)
 44 {
 45   return 0;
 46 }
 47
 48 /*读函数*/
 49 static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
 50 {
 51   unsigned long p =  *ppos;
 52   unsigned int count = size;
 53   int ret = 0;
 54   struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
 55
 56   /*判断读位置是否有效*/
 57   if (p >= MEMDEV_SIZE)
 58     return 0;
 59   if (count > MEMDEV_SIZE - p)
 60     count = MEMDEV_SIZE - p;
 61
 62 while (!have_data) /* 没有数据可读,考虑为什么不用if,而用while,中断信号唤醒 */
 63 {
 64         if (filp->f_flags & O_NONBLOCK)
 65             return -EAGAIN;
 66
 67     wait_event_interruptible(dev->inq,have_data);
 68 }
 69
 70
 71   /*读数据到用户空间*/
 72   if (copy_to_user(buf, (void*)(dev->data + p), count))
 73   {
 74     ret =  - EFAULT;
 75   }
 76   else
 77   {
 78     *ppos += count;
 79     ret = count;
 80
 81     printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);
 82   }
 83
 84   have_data = false; /* 表明不再有数据可读 */
 85   return ret;
 86 }
 87
 88 /*写函数*/
 89 static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
 90 {
 91   unsigned long p =  *ppos;
 92   unsigned int count = size;
 93   int ret = 0;
 94   struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
 95
 96   /*分析和获取有效的写长度*/
 97   if (p >= MEMDEV_SIZE)
 98     return 0;
 99   if (count > MEMDEV_SIZE - p)
100     count = MEMDEV_SIZE - p;
101
102   /*从用户空间写入数据*/
103   if (copy_from_user(dev->data + p, buf, count))
104     ret =  - EFAULT;
105   else
106   {
107     *ppos += count;
108     ret = count;
109
110     printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);
111   }
112
113   have_data = true; /* 有新的数据可读 */
114
115     /* 唤醒读进程 */
116     wake_up(&(dev->inq));
117
118   return ret;
119 }
120
121 /* seek文件定位函数 */
122 static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
123 {
124     loff_t newpos;
125
126     switch(whence) {
127       case 0: /* SEEK_SET */
128         newpos = offset;
129         break;
130
131       case 1: /* SEEK_CUR */
132         newpos = filp->f_pos + offset;
133         break;
134
135       case 2: /* SEEK_END */
136         newpos = MEMDEV_SIZE -1 + offset;
137         break;
138
139       default: /* can‘t happen */
140         return -EINVAL;
141     }
142     if ((newpos<0) || (newpos>MEMDEV_SIZE))
143         return -EINVAL;
144
145     filp->f_pos = newpos;
146     return newpos;
147
148 }
149
150 /*文件操作结构体*/
151 static const struct file_operations mem_fops =
152 {
153   .owner = THIS_MODULE,
154   .llseek = mem_llseek,
155   .read = mem_read,
156   .write = mem_write,
157   .open = mem_open,
158   .release = mem_release,
159 };
160
161 /*设备驱动模块加载函数*/
162 static int memdev_init(void)
163 {
164   int result;
165   int i;
166
167   dev_t devno = MKDEV(mem_major, 0);
168
169   /* 静态申请设备号*/
170   if (mem_major)
171     result = register_chrdev_region(devno, 2, "memdev");
172   else  /* 动态分配设备号 */
173   {
174     result = alloc_chrdev_region(&devno, 0, 2, "memdev");
175     mem_major = MAJOR(devno);
176   }
177
178   if (result < 0)
179     return result;
180
181   /*初始化cdev结构*/
182   cdev_init(&cdev, &mem_fops);
183   cdev.owner = THIS_MODULE;
184   cdev.ops = &mem_fops;
185
186   /* 注册字符设备 */
187   cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
188
189   /* 为设备描述结构分配内存*/
190   mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);
191   if (!mem_devp)    /*申请失败*/
192   {
193     result =  - ENOMEM;
194     goto fail_malloc;
195   }
196   memset(mem_devp, 0, sizeof(struct mem_dev));
197
198   /*为设备分配内存*/
199   for (i=0; i < MEMDEV_NR_DEVS; i++)
200   {
201         mem_devp[i].size = MEMDEV_SIZE;
202         mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
203         memset(mem_devp[i].data, 0, MEMDEV_SIZE);
204
205       /*初始化等待队列*/
206      init_waitqueue_head(&(mem_devp[i].inq));
207   }
208
209   return 0;
210
211   fail_malloc:
212   unregister_chrdev_region(devno, 1);
213
214   return result;
215 }
216
217 /*模块卸载函数*/
218 static void memdev_exit(void)
219 {
220   cdev_del(&cdev);   /*注销设备*/
221   kfree(mem_devp);     /*释放设备结构体内存*/
222   unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
223 }
224
225 MODULE_AUTHOR("David Xie");
226 MODULE_LICENSE("GPL");
227
228 module_init(memdev_init);
229 module_exit(memdev_exit);
ifneq ($(KERNELRELEASE),)

obj-m := memdev.o

else

KDIR := /home/rpi/RpiLinux
all:
    make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=/home/rpi/RpiTools/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin/arm-bcm2708hardfp-linux-gnueabi-

clean:
    rm -f *.ko *.o *.mod.o *.mod.c *.symvers  modul*

endif

测试程序:

 1 #include <stdio.h>
 2
 3 int main()
 4 {
 5     FILE *fp = NULL;
 6     char Buf[128];
 7
 8
 9     /*打开设备文件*/
10     fp = fopen("/dev/memdev0","r+");
11     if (fp == NULL)
12     {
13         printf("Open Dev memdev0 Error!\n");
14         return -1;
15     }
16
17     /*写入设备*/
18     strcpy(Buf,"memdev is char dev!");
19     printf("Write BUF: %s\n",Buf);
20     fwrite(Buf, sizeof(Buf), 1, fp);
21
22     sleep(5);
23     fclose(fp);
24
25     return 0;
26
27 }
 1 #include <stdio.h>
 2
 3 int main()
 4 {
 5     FILE *fp = NULL;
 6     char Buf[128];
 7
 8     /*初始化Buf*/
 9     strcpy(Buf,"memdev is char dev!");
10     printf("BUF: %s\n",Buf);
11
12     /*打开设备文件*/
13     fp = fopen("/dev/memdev0","r+");
14     if (fp == NULL)
15     {
16         printf("Open memdev0 Error!\n");
17         return -1;
18     }
19
20     /*清除Buf*/
21     strcpy(Buf,"Buf is NULL!");
22     printf("Read BUF1: %s\n",Buf);
23
24     /*读出数据*/
25     fread(Buf, sizeof(Buf), 1, fp);
26
27     /*检测结果*/
28     printf("Read BUF2: %s\n",Buf);
29
30     fclose(fp);
31
32     return 0;
33
34 }

测试结果:

时间: 2024-08-21 22:25:07

国嵌内核驱动进阶班-7-3(阻塞型字符设备驱动)的相关文章

Linux 设备驱动--- 阻塞型字符设备驱动 --- O_NONBLOCK --- 非阻塞标志【转】

转自:http://blog.csdn.net/yikai2009/article/details/8653697 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 阻塞 阻塞操作 非阻塞操作 阻塞方式-read- 实现 阻塞方式-write- 实现 非阻塞方式的读写操作 实例 --- 读阻塞的实现 实例 --- 按键驱动阻塞实现 1在 open 函数 查看看是 阻塞方式 还是 非阻塞方式 2在 read 函数中同样查看 3应用程序中 1以阻塞方式运行 2以非阻塞方式运行

字符设备驱动(程序设计)—①

via:http://blog.sina.com.cn/s/blog_7ec8fc2c010157lc.html 1.驱动程序设计 1)驱动分类 驱动这里分为 字符设备驱动.网络接口驱动.块设备驱动!这三类,其中前两者是重点. ①.字符设备 字符设备是一种 按自己来访问 的设备,字符驱动则负责驱动字符设备,这样的驱动通常是先 open.close.read和write 系统调用! ②.块设备 在大部分 Unix 系统中,块设备不能按照字节处理数据,只能一次传送一个或则会多个长度是 512 字节(

linux驱动学习(1)——字符设备驱动开发

(一)驱动程序介绍 (a)Linux驱动程序学习 知识结构: 1. Linux驱动程序设计模式(40%) 2. 内核相关知识(30%) 3. 硬件相关知识(30%) (b)驱动分类: ①字符设备: 字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,这样的驱动通常实现 open, close,read和 write 系统调用. ②块设备: 在大部分的 Unix 系统, 块设备不能按字节处理数据,只能一次传送一个或多个长度是512字节( 或一个更大的 2 次幂的数 )的整块数据,而Lin

从Linux内核LED驱动来理解字符设备驱动开发流程

目录 博客说明 开发环境 1. Linux字符设备驱动的组成 1.1 字符设备驱动模块加载与卸载函数 1.2 字符设备驱动的file_operations 结构体中的成员函数 2. 字符设备驱动--设备号注册卸载 2.1 设备号注册 2.2 设备号注销 3. 字符设备驱动--文件操作 参考资料 示例代码 @(从Linux内核LED驱动来理解字符设备驱动开发流程) 博客说明 撰写日期 2018.12.08 完稿日期 2019.10.06 最近维护 暂无 本文作者 multimicro 联系方式 [

linux设备驱动第三篇:写一个简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [plain] vie

字符设备驱动、平台设备驱动、设备驱动模型、sysfs的关系

Linux驱动开发的童鞋们来膜拜吧:-)  学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关联的分析.对于开发者而言,能够熟悉某一点并分享出来已很难得,但对于专注传授技术和经验给

linux设备驱动第三篇:如何写一个简单的字符设备驱动?

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [email prot

linux设备驱动第三篇:如何实现简单的字符设备驱动

在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存. 下面就开始学习如何写一个简单的字符设备驱动.首先我们来分解一下字符设备驱动 都有那些结构或者方法组成,也就是说实现一个可以使用的字符设备驱动我们必须做些什么工作. 1.主设备号和次设备号 对于字符设备的访问是通过文件系统中的设备名称进行的.他们通常位于/dev目录下.如下: [email pro

[kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术.对于初学者来说会非常困惑,甚至对Linux有一定基础的工程师而言,能够较好理解这些相关技术也相对不错了.要深刻理解其中的原理需要非常熟悉设备驱动相关的框架和模型代码.网络上有关这些技术的文章不少,但多是对其中的某一点进行阐述,很难找到对这些技术进行比较和关