自学或者看别人已经写好的代码有时会有疑惑,几百行几千行的代码是怎么敲出来的,对于初学者时常有这样的疑惑。看过别人敲代码才知道,程序不是一句一句写的,而是一个函数一个函数写,这个道理我学了好久才懂,看这边的文章的人如果你是初学者,希望你少走弯路。
-----------------------------------------------------------------------------------------------
应用程序、库、内核 、驱动程序之间的关系
1) 应用程序调用应用程序函数库完成功能
2) 应用程序以文件形式访问各种资源
3) 应用程序函数库
4) 部分函数直接完成功能
部分函数通过系统调用由内核完成
5) 内核处理系统调用,调用设备驱动程序
6) 设备驱动直接与硬件通信
注意:
1. 系统调用是内核与应用程序之间的接口
2. 设备驱动程序是内核与硬件之间的接口
-----------------------------------------------------------------------------------------------
这个程序包含的文件包括:
test.c // 驱动程序
app.c //应用程序
io_cmd.h //头文件
Makefile //这个你懂的....
install.sh //脚本文件
-----------------------------------------------------------------------------------------------
test.c 的编写如下 :
1. 最基本的框架:
#include
#include
module_init();
module_exit();
MODULE_LICENSE("GPL");
#include
#include
int test_init(void)
{
return 0;
}
void test_exit(void)
{
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
3. 定义一些要用到的变量:
#include
#include
unsigned long virt, phys; //定义两个变量
//宏定义两个寄存器的地址
#define GPJ2CON *((volatile unsigned long *) virt)
#define GPJ2DAT *((volatile unsigned long *) (virt + 4))
int test_init(void)
{
phys = 0xE0200280; //LED灯的控制地址
return 0;
}
void test_exit(void)
{
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
4. 填充两个 test 函数:
#include
#include
unsigned long virt, phys; //定义两个变量
//宏定义两个寄存器的地址
#define GPJ2CON *((volatile unsigned long *) virt)
#define GPJ2DAT *((volatile unsigned long *) (virt + 4))
int test_init(void)
{
int ret;
phys = 0xE0200280; // LED灯的控制地址
alloc_io_address(); // I/O内存映射申请函数
init_led(); //初始化LED硬件寄存器用
ret = register_chrdev(241, "led-driver", test_fops); //向内核注册字符设备
if(ret)
{
printk("register char failed\n");
}
return 0;
}
void test_exit(void)
{
destroy_io_address(); // I/O内存映射释放函数
unregister_chrdev(241, "test_driver"); //从内核注销字符设备
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
5. 第四步中两个 test 函数增加了不少函数, 写的时候注意要对应来写,申请VS释放 / 注册VS注销,下面一个个实例化 test 里面的内容。
#include
#include
unsigned long virt, phys; //定义两个变量 phys 为硬件地址, virt 映射的虚拟地址
//宏定义两个寄存器的地址
#define GPJ2CON *((volatile unsigned long *) virt)
#define GPJ2DAT *((volatile unsigned long *) (virt + 4))
int alloc_io_address(void)
{
request_mem_region(phys, 8, "led-driver"); // 申请虚拟内存
virt = ioremap(phys, 8); // 将一个IO地址空间映射到内核的虚拟地址空间上去
}
void destroy_io_address(void)
{
iounmap(virt); // 释放映射出啦的虚拟地址空间
release_mem_region(phys, 8); // 释放虚拟内存
}
//初始化硬件寄存器
void init_led(void)
{
GPJ2CON &= ~(0xffff << 0);
GPJ2CON |= (0x1111 << 0);
}
//这个字符设备驱动的函数集合的结构体,用来连接驱动程序与系统调用
struct file_operations test_fops = {
.ioctl = test_inctl,
};
int test_init(void)
{
int ret;
phys = 0xE0200280; // LED灯的控制地址
alloc_io_address(); // I/O内存映射申请函数
init_led(); //初始化LED硬件寄存器用
ret = register_chrdev(241, "led-driver", test_fops); //向内核注册字符设备
if(ret)
{
printk("register char failed\n");
}
return 0;
}
void test_exit(void)
{
destroy_io_address(); // I/O内存映射释放函数
unregister_chrdev(241, "test_driver"); //从内核注销字符设备
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
6. 第五步中除了 实例化 两个 test 里面的函数,还加了一个 struct file_operations test_fops 的结构体。然后,要实例化这个结构体里面的 I/O 控制函数。
#include
#include
#include
#include
#include
#include
#include "io_cmd.h"
unsigned long virt, phys; //定义两个变量 phys 为硬件地址, virt 映射的虚拟地址
//宏定义两个寄存器的地址
#define GPJ2CON *((volatile unsigned long *) virt)
#define GPJ2DAT *((volatile unsigned long *) (virt + 4))
int alloc_io_address(void)
{
request_mem_region(phys, 8, "led-driver"); // 申请虚拟内存
virt = ioremap(phys, 8); // 将一个IO地址空间映射到内核的虚拟地址空间上去
}
void destroy_io_address(void)
{
iounmap(virt); // 释放映射出啦的虚拟地址空间
release_mem_region(phys, 8); // 释放虚拟内存
}
//初始化硬件寄存器
void init_led(void)
{
GPJ2CON &= ~(0xffff << 0);
GPJ2CON |= (0x1111 << 0);
}
//四个LED灯亮
void led_on(void)
{
GPJ2DAT &= ~(0xf << 0);
}
//四个LED灯灭了
void led_off(void)
{
GPJ2DAT |= (0xf << 0);
}
//延时函数
void delay(volatile int time)
{
volatile int i, j;
for(; time > 0; time--)
{
for(i = 0; i < 1000; i++)
for(j = 0; i < 1000; j++);
}
}
//流水灯
void led_water(void)
{
char buf[4] = {0xe, 0xd, 0xb, 0x7};
int time = 20;
volatile int i;
for(; time > 0; time--)
{
for(i = 0; i < 4; i++)
{
GPJ2DAT = buf[i];
delay(10);
}
}
}
int test_ioctl(struct inode *node, struct file *filp, unsigned int cmd, unsigned long data)
{
switch(cmd)
{
case LED_ON: //这里用了三个宏定义,参考 io_cmd.h 文件
led_on();
break;
case LED_OFF:
led_off();
break;
case LED_WATER:
led_water();
break;
default:
printk("unknow io cmd\n");
return -1;
}
return 0;
}
//这个字符设备驱动的函数集合的结构体,用来连接驱动程序与系统调用
struct file_operations test_fops = {
.ioctl = test_inctl,
};
int test_init(void)
{
int ret;
phys = 0xE0200280; // LED灯的控制地址
alloc_io_address(); // I/O内存映射申请函数
init_led(); //初始化LED硬件寄存器用
ret = register_chrdev(241, "led-driver", test_fops); //向内核注册字符设备
if(ret)
{
printk("register char failed\n");
}
return 0;
}
void test_exit(void)
{
destroy_io_address(); // I/O内存映射释放函数
unregister_chrdev(241, "test_driver"); //从内核注销字符设备
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
补充 led 函数的实现,test.c 已经编写完成。
-----------------------------------------------------------------------------------
app.c 的代码如下:
#include
#include
#include
#include
#include
#include
#include "io_cmd.h"
int main(int argc, char *argv[])
{
int fd, ret;
char buf[40];
fd = open("/dev/test", O_RDWR);
if(fd < 0)
{
perror(" open failed\n");
}
if(!strncasecmp("on", argv[1], 2))
ioctl(fd, LED_ON);
if(!strncasecmp("off", argv[1], 3))
ioctl(fd, LED_OFF);
if(!strncasecmp("water", argv[1], 5))
ioctl(fd, LED_WATER);
return 0;
}
------------------------------------------------------------------------------------------
io_cmd.h 的代码如下:
#ifndef _IO_CMD_H
#define _IO_CMD_H
#define LED_ON 0
#define LED_OFF 1
#define LED_WATER 4
#endif /* _IO_CMD_H*
Makefile 的代码如下:
obj-m +=test.o
KERN = /home/android-kernel-samsung-dev/
ROOTFS = /nfs/mini_rootfs
all:
make -C $(KERN) M=`pwd` modules
clean:
make -C $(KERN) M=`pwd` modules clean
install:
make -C $(KERN) M=`pwd` modules_install INSTALL_MOD_PATH=$(ROOTFS
install.sh 脚本文件的代码如下
#!/bin/sh
make
make install
arm-linux-gcc -o app app.c
cp app /nfs/mini_rootfs
-------------------------------------------------------------------------------------------------------------------