平台:FriendARM —— mini2440
内核:2.6.32.2
交叉编译工具:arm-linux-gcc-4.4.3
驱动测试程序如下:
测试过程如下:
1. 先将驱动程序first_drv.ko和驱动测试程序拷贝到板子中,或者拷贝到挂载的nfs网络文件系统
2. 使用insmod first_drv.ko命令加载驱动
3. 使用lsmod命令查看当前已经加载的驱动,若查询结果中有first_drv出现,说明该驱动程序已经加载成功。
我的板子的查询结果是:
我的板子执行这条命令结果是:
5.使用./fisrt_drv_test on命令,板子上的灯会全部亮
6.使用./fisrt_drv_test off命令,板子上的灯会全部灭
内核:2.6.32.2
交叉编译工具:arm-linux-gcc-4.4.3
CPU芯片:S3C2440 A
驱动程序代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
/* 2.6.3x.x内核较2.6.2x.x内核驱动的架构有所改变,
* 所以这里所包含的头文件写法和代码的写法有所改变
*/
/* 2.6.3x.x 内核需要包含的头完文件 */
#include <linux/device.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
static struct class *firstdrv_class;
static struct device *firstdrv_class_dev;
volatile unsigned long* gpbcon = NULL;
volatile unsigned long* gpbdat = NULL;
static int first_drv_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/*
* LED1、LED2、LED3、LED4分别对应引脚GPB5、GPB6、GPB7、GPB8
*/
// 清零全部引脚GPB5、GPB6、GPB7、GPB8
*gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));
// 设置GPB5、GPB6、GPB7、GPB8为输出引脚(低电平)
*gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
return 0;
}
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
//printk("first_drv_write\n");
copy_from_user(&val, buf, count); // 从用户空间拷贝值到内核空间,与只相反的函数是澹?copy_to_user();
if (val == 1) {
// LED ON,全部清零
*gpbdat &= ~(1<<5 | 1<<6 | 1<<7 | 1<<8);
} else {
// LED OFF
*gpbdat |= (1<<5 | 1<<6 | 1<<7 | 1<<8);
}
return 0;
}
static struct file_operations first_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open,
.write = first_drv_write,
};
int major;
static int first_drv_init(void)
{
// 参数为0表示然系统自动分配设备号
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册驱动程序, 告诉内核
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
// 自动创建设备节点
/* 2.6.3x.x 内核不能用class_device_create,要用device_create*/
firstdrv_class_dev = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
/* 建立物理地址和虚拟地址的映射 */
gpbcon = (volatile unsigned long*)ioremap(0x56000010,16);
gpbdat = gpbcon + 1;
return 0;
}
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); // 卸载驱动程序
/* 新版的内核不能用class_device_unregister 要用device_unregister*/
device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
/* 取消(卸载)映射 */
iounmap(gpbcon);
}
module_init(first_drv_init); /* 想上层VFS告知此设备的初始化函数 */
module_exit(first_drv_exit); /* 想上层VFS告知此设备的卸载函数 */
MODULE_LICENSE("GPL"); /* 遵循模块的的GPL协议,可以解决一些警告提示和一些代码的识别 */
驱动测试程序如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/*
* ./first_drv_test on
* ./first_drv_test off
*/
int main(int argc, char** argv)
{
int fd;
int val = 1;
fd = open("/dev/xyz", O_RDWR);
if (fd < 0)
{
printf("can not open!\n");
} else {
if (argc != 2) {
printf("Usage :\n");
printf("%s <on|off>\n", argv[0]);
return 0;
} else {
if(strcmp(argv[1], "on") == 0) {
val = 1;
} else if(strcmp(argv[1], "off") == 0) {
val = 0;
} else {
printf("Usage :\n");
printf("%s <on|off>\n", argv[0]);
return 0;
}
}
}
write(fd, &val, 4);
return 0;
}
测试过程如下:
1. 先将驱动程序first_drv.ko和驱动测试程序拷贝到板子中,或者拷贝到挂载的nfs网络文件系统
2. 使用insmod first_drv.ko命令加载驱动
3. 使用lsmod命令查看当前已经加载的驱动,若查询结果中有first_drv出现,说明该驱动程序已经加载成功。
我的板子的查询结果是:
[root@FriendlyARM /]# lsmod
first_drv 1267 0 - Live 0xbf006000
也可以用cat /proc/device命令显示当前板子中有哪些设备以及对应的设备号,在显示这结果中的字符设备中出现first_drv设备也表示该驱动程序已经加载成功。我的板子执行这条命令结果是:
[root@FriendlyARM /]# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 s3c2410_serial
253 first_drv
254 rtc
Block devices:
259 blkext
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
4. 使用./fisrt_drv_test 或者 ./fisrt_drv_test 后跟除on或者off外的任意字符会出现如下结果:
[root@FriendlyARM /]# ./first_drv_test.o
Usage :
./first_drv_test.o <on|off>
[root@FriendlyARM /]# ./first_drv_test.o abc
Usage :
./first_drv_test.o <on|off>
[root@FriendlyARM /]#
5.使用./fisrt_drv_test on命令,板子上的灯会全部亮
6.使用./fisrt_drv_test off命令,板子上的灯会全部灭
总结:
写驱动的过程和要点为:
1.搭建驱动程序的基本框架
2.注意在不同内核版本之上写驱动程序的代码差别,否则报错都很难找出来
3.操作硬件时要使用ioremap函数来设置物理地址和虚拟地址的隐身,最后在消除这个映射
3.注意编译驱动程序时一定要用跟板子一样的内核,和板子契合的交叉编译链接器