以下代码并未编译测试,代码编译过程中可能有语法出错,代码仅供参考,只是提供一个字符设备设计的思路。
//led.c
#include<linux/module.h>//内核模块头文件
#include<linux/init.h>//内核模块头文件
#include<linux/cdev.h>//cdev file_operations 头文件
#include<linux/fs.h>//cdev file_operations 头文件
#include<linux/io.h> //ioremap头文件
#inculde"led.h"
MOUDLE_LICENSE("GPL");//模块遵守的协议
int led_init()
{
return 0;
}
void led_exit()
{
}
module_init(led_init);
module_exit(led_exit());
以上为内核模块,任何一个驱动程序必须要有的部分,接下来完成对模块的扩充。
//makefile 用于编译内核模块,编译led.c文件
obj-m := led.o //注意led.o对应led.c
KDIR := 内核代码路径 //注:你开发板所用的内核
all:
make -C $(KDIR) M=(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
rm -f *.o *.ko *.order *.symvers
//led.c
#include<linux/module.h>//内核模块头文件
#include<linux/init.h>//内核模块头文件
#include<linux/cdev.h>//cdev file_operations 头文件
#include<linux/fs.h>//cdev file_operations 头文件
#include<linux/io.h> //ioremap头文件
MOUDLE_LICENSE("GPL");//模块遵守的协议
struct cdev led_cdev; //驱动初始化第一步:分配设备描述结构
dev_t dev_number; //定义设备号变量
#define GPNCON 0x7f008800
#define GPNDTA 0x7f008808
unsigned int * led_config;
unsigned int * led_data;
//通常打开文件时完成对LED的控制和数据寄存器的初始化
int len_open(struct inode *node, struct file *filp)
{
//linux系统中使用的是虚拟地址因此要用ioremap把实际地址转化为虚拟地址
led_config = ioremap(GPNCON,4);
writel(0x11110000,led_config); //把0x11110000写入led_config指向的地址空间中,完成对控制寄存器的初始化
led_data = ipremap(GPNDTA,4);
return 0;
}
long len_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
switch(cmd)
{
case LED_ON:
writew(0x00,led_data);
return 0;
case LED_OFF:
writew(0xff,led_data);
return 0;
defaulet:
printf("cmd error!\n");
return cmd;
}
}
struct file_operations led_fops=
{
open = len_open,
.unlocked_ioctl = len_ioctl,
};
int led_init()
{
cdev_init(&led_cdev,&led_fops);//驱动初始化第二步:初始化设备描述结构 注:参数1:设备描述结构参数2:文件操作函数集
//动态分配主设备号
alloc_chrdev_region(&dev_number,0,1,"myled"); // 参数1.返回主设备号保存于dev_number中参数2.次设备号的基准,从第几个次设备号开始分配。参数3.分配的次设备号个数 参数4.驱动的名字
cdev_add(&led_cdev,dev_number,1); //第三步:注册设备描述结构
return 0;
}
void led_exit()
{
cdev_del(&led_cdev); //第四步:注销设备描述结构
unregister)chrdev(dev_number,1);//释放设备号
}
module_init(led_init);
module_exit(led_exit);
//led.h
#define led_MAGIC 'L' //第一幻术
#define LED_ON _IO(led_MAGIC ,0)
#define LED_OFF _IO(led_MAGIC,1)
//编写应用程序led_app.c 对驱动程序进行测试
#include<stdio.h>
int main(int argc,char *argv)
{
int fd;
int cmd = 0
fd = open("dev/myled",O_RDWR); //这里的myled要与mknod 创建的字符设备文件名一样
cmd = atoi(argv[1]); //将字符转化为int型
if(cmd==1)
{
ioctl(fd,LED_ON);
}
if(cmd==2)
{
ioctl(fd,LED_OFF);
}
return 0;
}
//编译led_app.c 注意使用arm-linux-gcc 且最好使用静态编译,因为防止开发板中没有我们所需的动态链接库
arm-linux-gcc -static led_app.c -o led_app
make 后生成led.ko
把led.ko和led_app复制到开发板的文件系统下,
insmod led.ko
如果安装模块出错,则执行如下命令
//////////////////////////////////
mkdir -p /lib/modules/$(uname -r)
///////////////////////////////
//创建字符设备文件 创建的目的:应用程序首先通过文件名myled找到字符设备文件,然后字符设备文件通过主设备号找到设备驱动程序
mknod /dev/myled c 主设备号 次设备号
主设备号可以在cat /dev/devices查看 注:是在开发板系统中,不是虚拟机中
./led_app 1 //led_app点亮
./led_app 2 //led_app熄灭
rmmod led 卸载led