一.开发环境:
主 机: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);
|
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平台上的文件。我的开发板的内核是和内核代码树一样的,于是还是下载到了板子上实验,在板子上没有出现刚才的错误。