Linux字符设备驱动程序的一个简单示例

摘自:http://www.cnblogs.com/fangyu/archive/2010/09/10/1823662.html

一.开发环境:

  机:VMWare--Fedora 9

开发板:友善之臂mini2440--256MB Nandflash 

编译器:arm-linux-gcc-4.3.2

 

二.驱动源码:

该源码很浅显易懂,非常适合初学者。

memdev.h

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef _MEMDEV_H_
#define _MEMDEV_H_
 
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 254   /*预设的mem的主设备号*/
#endif
 
#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2    /*设备数*/
#endif
 
#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif
 
/*mem设备描述结构体*/
struct  mem_dev                                    
{                                                       
   char  *data;                     
   unsigned long  size;      
};
 
#endif /* _MEMDEV_H_ */

memdev.c

?
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
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
 
#include "memdev.h"
 
static  mem_major = MEMDEV_MAJOR;
 
module_param(mem_major, int , S_IRUGO);
 
struct  mem_dev *mem_devp; /*设备结构体指针*/
 
struct  cdev cdev;
 
/*文件打开函数*/
int  mem_open( struct  inode *inode, struct  file *filp)
{
     struct  mem_dev *dev;
     
     /*获取次设备号*/
     int  num = MINOR(inode->i_rdev);
 
     if  (num >= MEMDEV_NR_DEVS)
             return  -ENODEV;
     dev = &mem_devp[num];
     
     /*将设备描述结构指针赋值给文件私有数据指针*/
     filp->private_data = dev; //方便以后对该指针的使用
     
     return  0;
}
 
/*文件释放函数*/
int  mem_release( struct  inode *inode, struct  file *filp)
{
   return  0;
}
 
/*读函数*/
static  ssize_t mem_read( struct  file *filp, char  __user *buf, size_t  size, loff_t *ppos)
{
   unsigned long  p =  *ppos;
   unsigned int  count = size;
   int  ret = 0;
   struct  mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
 
   /*判断读位置是否有效*/
   if  (p >= MEMDEV_SIZE) //超出读取范围,返回0表示读取不到数据
     return  0;
   if  (count > MEMDEV_SIZE - p)
     count = MEMDEV_SIZE - p;
 
   /*读数据到用户空间*/
   if  (copy_to_user(buf, ( void *)(dev->data + p), count))
   {
     ret =  - EFAULT;
   }
   else
   {
     *ppos += count;
     ret = count;
     
     printk(KERN_INFO "read %d bytes(s) from %d\n" , count, p);
   }
 
   return  ret;
}
 
/*写函数*/
static  ssize_t mem_write( struct  file *filp, const  char  __user *buf, size_t  size, loff_t *ppos)
{
   unsigned long  p =  *ppos;
   unsigned int  count = size;
   int  ret = 0;
   struct  mem_dev *dev = filp->private_data; /*获得设备结构体指针*/
   
   /*分析和获取有效的写长度*/
   if  (p >= MEMDEV_SIZE)
     return  0;
   if  (count > MEMDEV_SIZE - p)
     count = MEMDEV_SIZE - p;
     
   /*从用户空间写入数据*/
   if  (copy_from_user(dev->data + p, buf, count))
     ret =  - EFAULT;
   else
   {
     *ppos += count;
     ret = count;
     
     printk(KERN_INFO "written %d bytes(s) from %d\n" , count, p);
   }
 
   return  ret;
}
 
/* seek文件定位函数 */
static  loff_t mem_llseek( struct  file *filp, loff_t offset, int  whence)
{
     loff_t newpos;
 
     switch (whence) {
       case  0: /* SEEK_SET */
         newpos = offset;
         break ;
 
       case  1: /* SEEK_CUR */
         newpos = filp->f_pos + offset;
         break ;
 
       case  2: /* SEEK_END */
         newpos = MEMDEV_SIZE -1 + offset;
         break ;
 
       default : /* can't happen */
         return  -EINVAL;
     }
     if  ((newpos<0) || (newpos>MEMDEV_SIZE))
         return  -EINVAL;
         
     filp->f_pos = newpos;
     return  newpos;
 
}
 
/*文件操作结构体*/
static  const  struct  file_operations mem_fops =
{
   .owner = THIS_MODULE,
   .llseek = mem_llseek,
   .read = mem_read,
   .write = mem_write,
   .open = mem_open,
   .release = mem_release,
};
 
/*设备驱动模块加载函数*/
static  int  memdev_init( void )
{
   int  result;
   int  i;
 
   dev_t devno = MKDEV(mem_major, 0);
 
   /* 静态申请设备号*/
   if  (mem_major)
     result = register_chrdev_region(devno, 2, "memdev" );
   else   /* 动态分配设备号 */
   {
     result = alloc_chrdev_region(&devno, 0, 2, "memdev" );
     mem_major = MAJOR(devno);
  
   
   if  (result < 0)
     return  result;
 
   /*初始化cdev结构*/
   cdev_init(&cdev, &mem_fops); //使cdev与mem_fops联系起来
   cdev.owner = THIS_MODULE; //owner成员表示谁拥有这个驱动程序,使“内核引用模块计数”加1;THIS_MODULE表示现在这个模块被内核使用,这是内核定义的一个宏
   cdev.ops = &mem_fops;
   
   /* 注册字符设备 */
   cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);
    
   /* 为设备描述结构分配内存*/
   mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof ( struct  mem_dev), GFP_KERNEL); //目前为止我们始终用GFP_KERNEL
   if  (!mem_devp)    /*申请失败*/
   {
     result =  - ENOMEM;
     goto  fail_malloc;
   }
   memset (mem_devp, 0, sizeof ( struct  mem_dev));
   
   /*为设备分配内存*/
   for  (i=0; i < MEMDEV_NR_DEVS; i++)
   {
         mem_devp[i].size = MEMDEV_SIZE;
         mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL); //分配出来的地址存在此
         memset (mem_devp[i].data, 0, MEMDEV_SIZE);
   }
     
   return  0;
 
   fail_malloc:
   unregister_chrdev_region(devno, 1);
   
   return  result;
}
 
/*模块卸载函数*/
static  void  memdev_exit( void )
{
   cdev_del(&cdev);   /*注销设备*/
   kfree(mem_devp);     /*释放设备结构体内存*/
   unregister_chrdev_region(MKDEV(mem_major, 0), 2); /*释放设备号*/
}
 
MODULE_LICENSE( "GPL" );
 
module_init(memdev_init);
module_exit(memdev_exit);
三.编译源码

  1.把这两个驱动源文件复制进内核linux-2.6.32.2/drivers/char目录下

  2.修改该目录下的Kconfig文件添加

?
1
2
config MEMDEV_DRIVER
         tristate "memdev driver"

  3.修改该目录下的Makefile文件,依葫芦画瓢,添加

    obj-$(CONFIG_HELLO_DRIVER)   +=  memdev.o

    至此,文件以添加进内核。

  4.到Linux-2.6.32.2源代码根目录下执行

    make menuconfig

    在字符设备中找到菜单项“memdev driver“,就是我们刚才添加的驱动模块,选为M

  5.在Linux-2.6.32.2源代码根目录下执行

    make modules

    就能生成内核模块文件memdev.ko

  至此,我们已经完成驱动模块的编译。

四.把驱动下载到开发版并安装

  1.把memdev.ko 下载到开发板,并移到/lib/modules/2.6.29.4-FriendlyARM目录下,然后在开发板中执行 

  #modprobe memdev

  (注意用modprobe命令不需要.ko后缀,rmmod也是如此,这个经常会忘记)

  当然你也可以用insmod命令:insmod memdev.ko

  2.创建设备文件节的

    #mknod  /dev/memdev0  c  254  0

五.测试

测试代码如下:

app_mem.c

?
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
#include <stdio.h>
 
int  main()
{
     FILE  *fp0 = NULL;
     char  Buf[4096];
     
     /*初始化Buf*/
     strcpy (Buf, "Mem is char dev!" );
     printf ( "BUF: %s\n" ,Buf);
     
     /*打开设备文件*/
     fp0 = fopen ( "/dev/memdev0" , "r+" );
     if  (fp0 == NULL)
     {
         printf ( "Open Memdev0 Error!\n" );
         return  -1;
     }
     
     /*写入设备*/
     fwrite (Buf, sizeof (Buf), 1, fp0);
     
     /*重新定位文件位置(思考没有该指令,会有何后果)*/
     fseek (fp0,0,SEEK_SET);
     
     /*清除Buf*/
     strcpy (Buf, "Buf is NULL!" );
     printf ( "BUF: %s\n" ,Buf);
     
     
     /*读出设备*/
     fread (Buf, sizeof (Buf), 1, fp0);
     
     /*检测结果*/
     printf ( "BUF: %s\n" ,Buf);
     
     return  0;  
 
}

把程序交叉编译后传到板子上执行

程序输出结果如下:

  

结果和预想的一样。

 

 

六.总结

  我原本并没打算把他下载到开发板上运行,以为在虚拟机上就可以运行了,但是insmod的时候老是出现一个错误

insmod error inserting 'memdev.ko': -1 Invalid module format

  后来在网上查了一些资料,这个错误可能是因为内核代码树与内核不一样,我用uname -a 查看了一下,发现我的内核版本是2.6.25-14。另外我用file命令查看了一下生成的memdev.ko 文件,发现是编译成了ARM平台上的文件。我的开发板的内核是和内核代码树一样的,于是还是下载到了板子上实验,在板子上没有出现刚才的错误。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值