[arm驱动]globalmem虚拟设备实例描述

简介:

一、概念
   “virtualdisk  虚拟内存”的概念又指"GLOBALMEM 全局内存"

二、globalmem虚拟设备的作用

   (1)、globalmem字符设备驱动中,分配一片内存大小为GLOBALMEM_SIZE(4K)的空间

   (2)、提供对该片内存的读写、控制和定位函数(read,write,llseek)

   (3)、用户进程能够通过linux系统调用访问这篇内存
三、实例解释,假设已经创建了一dev/globalmem 

1
2
3
4
5
6
7
8
9
# echo 'hello world!' > /dev/globalmem //写入 hello  world
    written 13 bytes(s) from 0
    # cat /dev/
    /dev/console     /dev/globalmem   /dev/null
    # cat /dev/globalmem //读取
    read  4096 bytes(s) from 0
    hello world!
    cat read  error: No such device or address // 出现这样这里正确
    #

   由上面的实验可以看出,dev/globalmem 在内核中有了固定内存
四、使用方法
1、全局变量
#define VIRTUALDISK_SIZE  0x1000//内存为4k
#define MEM_CLEAR 0x1 //定义清除指令为0x01
#define VIRTUALDISK_MAJOR 250 //主设备号 250
int VirtualDisk_major = VIRTUALDISK_MAJOR;

struct VirtualDisk{
   struct cdev cdev;//详细看cdev机制
   unsigned char mem[VIRTUALDISK_SIZE ];
   long count;          /*记录设备目前被多少设备打开*/
};


struct VirtualDisk *VirtualDiskp;//定义全局指针变量VirtualDiskp,关键变量


2、在init()函数中:

   1)、分配主设备号,

1
2
3
4
5
6
7
8
9
10
11
int  result;
     dev_t devno = MKDEV(VirtualDisk_major, 0);
     if (VirtualDisk_major){
     result = register_chrdev_region(devno, 1,  "module" );
     } else {
     result = alloc_chrdev_region(&devno, 0, 1,  "module" );
     VirtualDisk_major = MAJOR(devno);
    
     if (result < 0 ){
     return  result;
     }


   2)、分配VirtualDiskp的内存空间关键

1
2
3
4
5
6
7
8
9
10
11
VirtualDiskp = kmalloc( sizeof ( struct  VirtualDisk), GFP_KERNEL);
    if (!VirtualDiskp){
    result = -ENOMEM;
    goto  fail_malloc;
    }
    memset (VirtualDiskp, 0,  sizeof ( struct  VirtualDisk));
    VirtualDisk_setup_cdev(VirtualDiskp, 0); //初始化VirtualDiskp的cdev成员
    //..........................中间其他代码,包括return 0;
    fail_malloc: //写在init函数中最后一行
        unregister_chrdev_region(devno, 1);
        return  result;



3、    VirtualDisk_setup_cdev()//添加cdev设备,初始化VirtualDiskp的cdev成员

1
2
3
4
5
6
7
8
9
10
static  void  VirtualDisk_setup_cdev( struct  VirtualDisk *dev,  int  minorIndex){
     int  err;
     int  devno = MKDEV(VirtualDisk_major, minorIndex);
     cdev_init(&dev->cdev, &module_drv_fops);
     dev->cdev.owner = THIS_MODULE;
     err = cdev_add(&dev->cdev, devno, 1); //将cdev内存与dev_t dev 关联
     if (err){
     printk( "error %d cdev file added\n" , err);
     }
}

4、exit()函数中//1、删除cdev设备,释放VirtualDiskp空间,注销设备号

1
2
3
cdev_del(&VirtualDiskp->cdev);
     kfree(VirtualDiskp);
     unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);


总结,从2与4中可以看出只要设备不移除(rmmod ***.ko),VirtualDiskp就一直存在
5、设置访问,获取访问VirtualDiskp

1)、open(struct inode *inode, struct file *file)函数//设置通过设备文件(/dev/***)私有数据private_data指向设备结构体

1
2
file ->private_data = VirtualDiskp; // 将文件的私有数据private_data指向设备的结构体,在 read 、write、ioctl、llseek等函数通过private_data访问设备结构体
     VirtualDiskp->count++;    /*增加设备打开次数*/

2)、release(struct inode *inode, struct file *file)函数

1
VirtualDiskp->count--;  /*减少设备打开次数*/

3)、write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

1
2
struct  VirtualDisk *devp = file->private_data;  /*获得设备结构体指针*/
     copy_from_user(devp->mem + p, buf, countt) //将字符存与VirtualDiskp中

4)、read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

1
2
struct  VirtualDisk *devp = file->private_data;  /*获得设备结构体指针*/
    copy_to_user(buf, ( void *)(devp->mem + p), countt)

5)、ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

1
2
3
4
5
6
7
8
9
10
struct  VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/
        switch  (cmd)
     {
     case  MEM_CLEAR: /*设备内存清零*/
       memset (devp->mem, 0, VIRTUALDISK_SIZE);  
       printk(KERN_INFO  "VirtualDisk is set to zero\n" );
       break ;
     default :
       return   - EINVAL;
     }

总结: 可以看出file->private_data是个关键的词,它是设备文件的私有空间(/dev/***只是这个文件的映射,真正的空间在内核中,详细见《[arm驱动]busybox根文件系统mdev的详解》)
五、模板实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
//“module_drv”,"module_","module"
#include <linux/module.h>//模块所需的大量符号和函数定义
#include <linux/kernel.h>
#include <linux/fs.h>//文件系统相关的函数和头文件
#include <linux/init.h> //指定初始化和清除函数
#include <linux/delay.h>
#include <linux/cdev.h> //cdev结构的头文件包含<linux/kdev_t.h>
#include <linux/device.h>
#include <linux/mm.h>
//#include <linux/sched.h>//包含驱动程序使用的大部分内核API的定义,包括睡眠函数以及各种变量声明
#include <asm/uaccess.h>//在内核和用户空间中移动数据的函数
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#define VIRTUALDISK_SIZE  0x1000//4k
#define MEM_CLEAR 0x1
#define VIRTUALDISK_MAJOR 250
int  VirtualDisk_major = VIRTUALDISK_MAJOR;
struct  VirtualDisk{
     struct  cdev cdev; //详细看cdev机制
     unsigned  char  mem[VIRTUALDISK_SIZE ];
     long  count;           /*记录设备目前被多少设备打开*/
};
static  struct  class  *module_class;
static  struct  class_device    *module_class_dev;
struct  VirtualDisk *VirtualDiskp;
static  int  module_drv_open( struct  inode *inode,  struct  file *file)
{
     printk( "module_dev read\n" ); 
     file->private_data = VirtualDiskp;
     VirtualDiskp->count++;     /*增加设备打开次数*/
     return  0;
}
static  int  module_drv_release( struct  inode *inode,  struct  file *file)
{
     printk( "module_dev release\n" ); 
     VirtualDiskp->count--;   /*减少设备打开次数*/
     return  0;
}
/*seek文件定位函数:seek()函数对文件定位的起始地址可以是文件开头(SEEK_SET,0)、当前位置(SEEK_CUR,1)、文件尾(SEEK_END,2)*/
static  loff_t module_drv_llseek( struct  file *file, loff_t offset,  int  origin){
     loff_t ret = 0; /*返回的位置偏移*/
   switch  (origin)
   {
     case  SEEK_SET:    /*相对文件开始位置偏移*/
       if  (offset < 0) /*offset不合法*/
       {
         ret =  - EINVAL;     /*无效的指针*/
         break ;
       }
       if  ((unsigned  int )offset > VIRTUALDISK_SIZE) /*偏移大于设备内存*/
       {
         ret =  - EINVAL;     /*无效的指针*/
         break ;
       }
       file->f_pos = (unsigned  int )offset;   /*更新文件指针位置*/
       ret = file->f_pos; /*返回的位置偏移*/
       break ;
     case  SEEK_CUR:    /*相对文件当前位置偏移*/
       if  ((file->f_pos + offset) > VIRTUALDISK_SIZE) /*偏移大于设备内存*/
       {
         ret =  - EINVAL; /*无效的指针*/
         break ;
       }
       if  ((file->f_pos + offset) < 0) /*指针不合法*/
       {
         ret =  - EINVAL; /*无效的指针*/
         break ;
       }
       file->f_pos += offset; /*更新文件指针位置*/
       ret = file->f_pos; /*返回的位置偏移*/
       break ;
     default :
       ret =  - EINVAL; /*无效的指针*/
       break ;
   }
   return  ret;
}
/*设备控制函数:ioctl()函数接受的MEM_CLEAR命令,这个命令将全局内存的有效数据长度清零,对于设备不支持的命令,ioctl()函数应该返回-EINVAL*/
static  int  module_drv_ioctl( struct  inode *inode,  struct  file *file, unsigned  int  cmd, unsigned  long  arg){
      struct  VirtualDisk *devp = file->private_data; /*获得设备结构体指针*/
     switch  (cmd)
     {
     case  MEM_CLEAR: /*设备内存清零*/
       memset (devp->mem, 0, VIRTUALDISK_SIZE);  
       printk(KERN_INFO  "VirtualDisk is set to zero\n" );
       break ;
     default :
       return   - EINVAL;
     }
     return  0;
}
/*读函数:读写函数主要是让设备结构体的mem[]数组与用户空间交互数据,并随着访问字节数变更返回用户的文件读写偏移位置*/
static  ssize_t module_drv_read( struct  file *file,  const  char  __user *buf,  size_t  count, loff_t * ppos)
{
    printk( "module_dev read\n" );
    unsigned  long  p =  *ppos;  /*记录文件指针偏移位置*/
   unsigned  int  countt = count; /*记录需要读取的字节数*/
   int  ret = 0;     /*返回值*/
   struct  VirtualDisk *devp = file->private_data;  /*获得设备结构体指针*/
   /*分析和获取有效的读长度*/
   if  (p >= VIRTUALDISK_SIZE )   /*要读取的偏移大于设备的内存空间*/
     return  countt ?  - ENXIO: 0; /*读取地址错误*/
   if  (countt > VIRTUALDISK_SIZE  - p) /*要读取的字节大于设备的内存空间*/
     countt = VIRTUALDISK_SIZE  - p; /*将要读取的字节数设为剩余的字节数*/
  /*内核空间->用户空间交换数据*/
   if  (copy_to_user(buf, ( void *)(devp->mem + p), countt))
   {
     ret =  - EFAULT;
   }
   else
   {
     *ppos += countt;
     ret = countt;
     printk( "read %d bytes(s) is  %ld\n" , countt, p);
   }
     printk( "bytes(s) is  %s\n" , buf);
   return  ret;
}
/*
  file 是文件指针,count 是请求的传输数据长度,buff 参数是指向用户空间的缓冲区,这个缓冲区或者保存要写入的数据,或者是一个存放新读入数据的空缓冲区,该地址在内核空间不能直接读写,ppos 是一个指针指向一个"long offset type"对象, 它指出用户正在存取的文件位置. 返回值是一个"signed size type。写的位置相对于文件开头的偏移。
  */
static  ssize_t module_drv_write( struct  file *file,  const  char  __user *buf,  size_t  count, loff_t * ppos)
{
     printk( "module_dev write\n" );
      unsigned  long  p =  *ppos;  /*记录文件指针偏移位置*/
   int  ret = 0;   /*返回值*/
   unsigned  int  countt = count; /*记录需要写入的字节数*/
   struct  VirtualDisk *devp = file->private_data;  /*获得设备结构体指针*/
   /*分析和获取有效的写长度*/
   if  (p >= VIRTUALDISK_SIZE ) /*要写入的偏移大于设备的内存空间*/
     return  countt ?  - ENXIO: 0; /*写入地址错误*/
   if  (countt > VIRTUALDISK_SIZE  - p) /*要写入的字节大于设备的内存空间*/
     countt = VIRTUALDISK_SIZE  - p; /*将要写入的字节数设为剩余的字节数*/
   /*用户空间->内核空间*/
   if  (copy_from_user(devp->mem + p, buf, countt))
     ret =  - EFAULT;
   else
   {
     *ppos += countt; /*增加偏移位置*/
     ret = countt; /*返回实际的写入字节数*/
     printk( "written %d bytes(s) from%ld\n" , countt, p);
   }
   return  ret;
     return  0;
}
static  struct  file_operations module_drv_fops = {
     .owner  =   THIS_MODULE,     /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
     .open   =   module_drv_open,  
     .read = module_drv_read,
     .write    =    module_drv_write,   
     .release = module_drv_release,
     .llseek = module_drv_llseek,
     .ioctl = module_drv_ioctl,
};
  /*将 cdev 结构嵌入一个你自己的设备特定的结构,你应当初始化你已经分配的结构使用以上函数,有一个其他的 struct cdev 成员你需要初始化. 象 file_operations 结构,struct cdev 有一个拥有者成员,应当设置为 THIS_MODULE,一旦 cdev 结构建立, 最后的步骤是把它告诉内核, 调用:
    cdev_add(&dev->cdev, devno, 1);*/
static  void  VirtualDisk_setup_cdev( struct  VirtualDisk *dev,  int  minorIndex){
     int  err;
     int  devno = MKDEV(VirtualDisk_major, minorIndex);
     cdev_init(&dev->cdev, &module_drv_fops);
     dev->cdev.owner = THIS_MODULE;
     err = cdev_add(&dev->cdev, devno, 1);
     if (err){
     printk( "error %d cdev file added\n" , err);
     }
}
static  int  module_drv_init( void )
{
     int  result;
     dev_t devno = MKDEV(VirtualDisk_major, 0);
     if (VirtualDisk_major){
     result = register_chrdev_region(devno, 1,  "module" );
     } else {
     result = alloc_chrdev_region(&devno, 0, 1,  "module" );
     VirtualDisk_major = MAJOR(devno);
    
     if (result < 0 ){
     return  result;
     }
     VirtualDiskp = kmalloc( sizeof ( struct  VirtualDisk), GFP_KERNEL);
     if (!VirtualDiskp){
     result = -ENOMEM;
     goto  fail_malloc;
     }
     memset (VirtualDiskp, 0,  sizeof ( struct  VirtualDisk));
     VirtualDisk_setup_cdev(VirtualDiskp, 0);
     module_class = class_create(THIS_MODULE,  "module_drv" );
     if  (IS_ERR(module_class))
         return  PTR_ERR(module_class);
     module_class_dev = class_device_create(module_class, NULL, MKDEV(VirtualDisk_major, 0), NULL,  "module" );  /* /dev/xyz */
     if  (IS_ERR(module_class_dev))
         return  PTR_ERR(module_class_dev);
     return  0;
     fail_malloc:
         unregister_chrdev_region(devno, 1);
         return  result;
}
static  void  module_drv_exit( void )
{
     cdev_del(&VirtualDiskp->cdev);
     kfree(VirtualDiskp);
     unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);
     class_device_unregister(module_class_dev);
     class_destroy(module_class);
}
module_init(module_drv_init);
module_exit(module_drv_exit);
MODULE_LICENSE( "GPL" );

六、Makefile中是你要用的编译过的内核路径

1
2
3
4
5
6
7
8
9
#module_dev.c
KERN_DIR =  /workspacearm/linux-2 .6.2.6
all:
     make  -C $(KERN_DIR) M=` pwd ` modules
     #cp module_dev.ko /opt/fsmini/
clean:
     make  -C $(KERN_DIR) M=` pwd ` modules clean
     rm  -rf modules.order
obj-m    += module_dev.o




本文转自lilin9105 51CTO博客,原文链接:http://blog.51cto.com/7071976/1391073,如需转载请自行联系原作者

相关文章
|
7月前
|
传感器 物联网 网络架构
ENS、IoT设备、X86、ARM
ENS(Enterprise Name Service)是企业名称服务,是一种为物联网设备提供命名和寻址服务的技术。ENS通过为物联网设备分配唯一的名称和地址,使得物联网设备可以被网络中的其他设备和服务所识别和访问。 IoT设备(Internet of Things device)是连接到互联网的物理设备,可以收集和共享数据,用于监测、控制和优化各种业务流程和操作。IoT设备包括各种传感器、执行器、网关和路由器等。
101 2
|
存储 固态存储 关系型数据库
基于ARM的AWS EC2实例上的PG跑起来性能怎么样?
基于ARM的AWS EC2实例上的PG跑起来性能怎么样?
146 0
|
缓存 C++
基于ARM-contexA9-蜂鸣器pwm驱动开发
基于ARM-contexA9-蜂鸣器pwm驱动开发
118 0
基于ARM-contexA9蜂鸣器驱动开发
基于ARM-contexA9蜂鸣器驱动开发
107 0
|
Linux C++ 流计算
基于ARM_contexA9 led驱动编程
基于ARM_contexA9 led驱动编程
76 0
|
编译器 vr&ar C语言
如何保护自己知识产权,建立代码护城河——建立自己的静态库,x86和arm平台的实例讲解
如何保护自己知识产权,建立代码护城河——建立自己的静态库,x86和arm平台的实例讲解
320 0
|
机器学习/深度学习 弹性计算 人工智能
阿里云服务器x86、ARM计算、弹性裸金属服务器、超级计算集群实例架构有何不同?
阿里云服务器在架构上有x86计算、ARM 计算架构、异构计算GPU/FPGA/NPU、弹性裸金属服务器(神龙),超级计算集群之分,对于很多新手用户来说,并不清楚这些云服务器实例架构有何不同,不是很了解他们各自有什么特点和适用场景,本文来为大家简单介绍下这些云服务器实例架构的主要特点和适用场景,以供大家参考选择。
671 0
阿里云服务器x86、ARM计算、弹性裸金属服务器、超级计算集群实例架构有何不同?
|
iOS开发
iOS设备支持的ARM平台
iOS设备支持的ARM平台
90 0
|
弹性计算 Cloud Native Android开发
阿里云CPU处理器倚天Yitian 710,2.75 GHz主频ECS ARM架构c8y、g8y和g8y实例
阿里云CPU处理器倚天Yitian 710,2.75 GHz主频ECS ARM架构c8y、g8y和g8y实例,阿里云自研CPU处理器倚天Yitian 710,2.75 GHz主频,搭载倚天710处理器的云服务器ECS有计算型c8y、通用型g8y和内存型r8y,云服务器吧分享阿里云自研CPU处理器倚天Yitian 710性能测评:
344 0
|
弹性计算 Cloud Native Android开发
阿里云服务器架构ARM计算c8y、g8y和g8y实例采用倚天Yitian 710
阿里云服务器架构ARM计算c8y、g8y和g8y实例采用倚天Yitian 710,阿里云自研CPU处理器倚天Yitian 710,2.75 GHz主频,搭载倚天710处理器的云服务器ECS有计算型c8y、通用型g8y和内存型g8y,云服务器吧分享阿里云自研CPU处理器倚天Yitian 710性能测评:
244 0