目录
1. 使用应用层控制灯
1.1. 撰写驱动的.c文件
1.2. 撰写应用层的.c文件
1.3. 撰写makefile文件
1.4. 测验
2. 自动创建设备节点
2.1. 函数分析
2.2. 撰写驱动.c文件
2.3. 修改应用层.c文件
2.4. 编写makefile文件
2.5. 在开发板安装.ko驱动
3. ioctl函数(学到这里就可以完成简单的项目了)
3.1. 函数原型
3.2. 撰写.h文件
3.3. 撰写应用层.c文件
3.4. 撰写驱动的.c文件
3.5. 编写Makefile文件
4. 蜂鸣器原理图(不做过多讲解)
1. 使用应用层控制灯
1.1. 撰写驱动的.c文件
驱动是开发板上的驱动,编译依赖开发板上的内核
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>//添加内存映射的函数
#include <linux/string.h>
unsigned int major =0;
#define NAME "hello"
int error_num=0;
char kbuf[128]={0};
#define RED_BASE 0XC001A000
#define BLUE_BASE 0XC001B000
#define GREEN_BASE 0XC001E000
unsigned int * red_base=NULL;
unsigned int * blue_base=NULL;
unsigned int * green_base=NULL;
//控制红灯的宏
#define RED_ON *red_base |=1<<28;
#define RED_OFF *red_base &=~(1<<28);
//控制蓝灯的宏
#define BLUE_ON *blue_base |=1<<12;
#define BLUE_OFF *blue_base &=~(1<<12);
//控制绿灯的宏
#define GREEN_ON *green_base |=1<<13;
#define GREEN_OFF *green_base &=~(1<<13);
ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t * offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
error_num=copy_to_user(ubuf,kbuf,size);
if(error_num)
{
printk("copy_to_user error\n");
return -1;
}
return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
//因为是用户层控制内核层,来自用户控制在write中进行判断
error_num=copy_from_user(kbuf,ubuf,size);
if(error_num)
{
printk("copy_from_user error\n");
return -1;
}
//判断用户发来的数据
if(strncmp("RED_ON",kbuf,6)==0)
{
RED_ON;
return 0;
}else if(strncmp("RED_OFF",kbuf,7)==0)
{
RED_OFF;
return 0;
}else if(strncmp("BLUE_ON", kbuf,7)==0)
{
BLUE_ON;
return 0;
}else if(strncmp("BLUE_OFF",kbuf,8)==0)
{
BLUE_OFF;
return 0;
}else if(strncmp("GREEN_OFF",kbuf,9)==0)
{
GREEN_OFF;
return 0;
}else if(strncmp("GREEN_ON",kbuf,8)==0)
{
GREEN_ON;
return 0;
}
return 0;
}
int mycdev_open(struct inode *inode, struct file *file)
{
printk("hello open\n");
return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{
printk("hello close\n");
return 0;
}
struct file_operations fops={
.open=mycdev_open,
.write=mycdev_write,
.release=mycdev_release,
.read=mycdev_read,
};
static int __init hello_init(void)
{
major= register_chrdev(major, NAME, &fops);
if(major<0)
{
printk("register_chrdev error\n");
return major;
}
//在入口处将物理地址映射成虚拟地址
red_base=ioremap(RED_BASE, 36);//大小最大就是36
if(red_base ==NULL)
{
printk("ioremap error RED_BASE\n");
return -ENOMEM; //返回内存溢出
}
//注意解引用
*red_base&=~(1<<28); //灭红灯
*(red_base+1)|=(1<<28);//设置为输出模式
*(red_base+9)&=~(3<<24);//选择function 0
green_base=ioremap(GREEN_BASE,36);
if(green_base==NULL)
{
printk("ioremap error GREEN_BASE\n");
return -ENOMEM;
}
*green_base&=~(1<<13);//灭绿灯
*(green_base+1)|=(1<<13);//设置为输出模式
*(green_base+8)&=~(3<<26);//选择function 0
blue_base=ioremap(BLUE_BASE,36);
if(blue_base==NULL)
{
printk("ioremap error BLUE_BASE\n");
return -ENOMEM;
}
*blue_base&=~(1<<12);//灭蓝灯
*(blue_base+1)|=(1<<12);//设置为输出模式
*(blue_base+8)|=(1<<25);//将25位置为1
*(blue_base+8)&=~(1<<24);//将24位置为0
return 0;
}
static void __exit hello_exit(void)
{
//注销内存
iounmap(blue_base);//先注册的后注销
iounmap(green_base);
iounmap(red_base);
unregister_chrdev(major, NAME);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
1.2. 撰写应用层的.c文件
生成的可执行文件是在arm开发板上运行,所以编译使用交叉编译工具链
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
char buf[128] = {"my name is southernbird"}; //写入数据
int main(int argc, const char *argv[])
{
int fd;
int chose_numh;
fd = open("./hello", O_RDWR);
if (fd == -1)
{
perror("open error");
return -1;
}
while (1)
{
printf("请输入对应选项:\n");
printf("1.红开 2.红关\n");
printf("3.绿开 4.绿关\n");
printf("5.蓝开 6.蓝关\n");
scanf("%d", &chose_numh);
switch (chose_numh)
{
case 1:
strcpy(buf, "RED_ON");
break;
case 2:
strcpy(buf, "RED_OFF");
break;
case 3:
strcpy(buf, "GREEN_ON");
break;
case 4:
strcpy(buf, "GREEN_OFF");
break;
case 5:
strcpy(buf, "BLUE_ON");
break;
case 6:
strcpy(buf, "BLUE_OFF");
break;
default:
printf("输入错误\n");
continue;
}
write(fd, buf, sizeof(buf));
memset(buf, 0, sizeof(buf)); //清空
read(fd, buf, sizeof(buf));
printf("%s\n", buf); //用来验证
}
close(fd);
return 0;
}
1.3. 撰写makefile文件
在开发板上进行运行的时候每次都需要将文件移动到nfs挂载的文件下,耗时耗力,修改makefile能解决这个问题
#KERNELDIR:=/lib/modules/$(shell uname -r)/build
KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
arm-none-linux-gnueabi-gcc test.c
cp *.ko a.out /opt/6818/rootfs/rootfs
clean:
make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o
此时执行make命令能把生成的.ko文件和a.out文件加载到/opt/6818/rootfs/rootfs下
同步完成
1.4. 测验
在SecureCRTPortable输入下列命令
insmod hello.ko 安装驱动文件
mknod hello c 244 1 创建字符设备驱动
./a.out 执行应用层程序
2. 自动创建设备节点
每次我们都需要自己创建设备节点,站在用户层面想,用户是不会自己创建设备节点的,所以上面驱动还是需要优化
2.1. 函数分析
》1.函数原型:class_create(owner, name)
功能:向用户空间提交目录信息
参数:
owner:默认填写THIS_MODULE
name :要创建的目录的名字
返回值:
成功:返回struct class*指针
失败:返回错误码指针
》2.函数原型:void class_destroy(struct class *cls)
功能:销毁目录
参数:struct class*指针
返回值:
成功:返回struct class*指针
失败:返回错误码指针
》3.函数原型:IS_ERR(cls)
功能:将错误码指针转换为错误码
》4.函数原型:struct device *device_create
(struct class *class,
struct device *parent,
dev_t devt,
void *drvdata,
const char *fmt, ...)
功能:向用户空间提交文件信息
参数:
class:class_create返回的struct class*指针
parent:默认填NULL
devt:设备号 MKDEV(major,0)
fmt:文件名字
返回值:
成功:返回struct device *指针
失败:返回返回错误码指针
》5.函数原型:void device_destroy(struct class *class, dev_t devt)
功能:销毁文件
参数:
class:class_create返回的struct class*指针
devt:设备号 MKDEV(major,0)
2.2. 撰写驱动.c文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>//添加内存映射的函数
#include <linux/string.h>
#include <linux/device.h>//添加头文件
unsigned int major =0;
#define NAME "hello"
int error_num=0;
char kbuf[128]={0};
#define RED_BASE 0XC001A000
#define BLUE_BASE 0XC001B000
#define GREEN_BASE 0XC001E000
unsigned int * red_base=NULL;
unsigned int * blue_base=NULL;
unsigned int * green_base=NULL;
//控制红灯的宏
#define RED_ON *red_base |=1<<28;
#define RED_OFF *red_base &=~(1<<28);
//控制蓝灯的宏
#define BLUE_ON *blue_base |=1<<12;
#define BLUE_OFF *blue_base &=~(1<<12);
//控制绿灯的宏
#define GREEN_ON *green_base |=1<<13;
#define GREEN_OFF *green_base &=~(1<<13);
//书写返回值声明
struct class*cls;
struct device *dev;
ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t * offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
error_num=copy_to_user(ubuf,kbuf,size);
if(error_num)
{
printk("copy_to_user error\n");
return -1;
}
return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
//因为是用户层控制内核层,来自用户控制在write中进行判断
error_num=copy_from_user(kbuf,ubuf,size);
if(error_num)
{
printk("copy_from_user error\n");
return -1;
}
//判断用户发来的数据
if(strncmp("RED_ON",kbuf,6)==0)
{
RED_ON;
return 0;
}else if(strncmp("RED_OFF",kbuf,7)==0)
{
RED_OFF;
return 0;
}else if(strncmp("BLUE_ON", kbuf,7)==0)
{
BLUE_ON;
return 0;
}else if(strncmp("BLUE_OFF",kbuf,8)==0)
{
BLUE_OFF;
return 0;
}else if(strncmp("GREEN_OFF",kbuf,9)==0)
{
GREEN_OFF;
return 0;
}else if(strncmp("GREEN_ON",kbuf,8)==0)
{
GREEN_ON;
return 0;
}
return 0;
}
int mycdev_open(struct inode *inode, struct file *file)
{
printk("hello open\n");
return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{
printk("hello close\n");
return 0;
}
struct file_operations fops={
.open=mycdev_open,
.write=mycdev_write,
.release=mycdev_release,
.read=mycdev_read,
};
static int __init hello_init(void)
{
major= register_chrdev(major, NAME, &fops);
if(major<0)
{
printk("register_chrdev error\n");
return major;
}
//在入口处将物理地址映射成虚拟地址
red_base=ioremap(RED_BASE, 36);//大小最大就是36
if(red_base ==NULL)
{
printk("ioremap error RED_BASE\n");
return -ENOMEM; //返回内存溢出
}
//注意解引用
*red_base&=~(1<<28); //灭红灯
*(red_base+1)|=(1<<28);//设置为输出模式
*(red_base+9)&=~(3<<24);//选择function 0
green_base=ioremap(GREEN_BASE,36);
if(green_base==NULL)
{
printk("ioremap error GREEN_BASE\n");
return -ENOMEM;
}
*green_base&=~(1<<13);//灭绿灯
*(green_base+1)|=(1<<13);//设置为输出模式
*(green_base+8)&=~(3<<26);//选择function 0
blue_base=ioremap(BLUE_BASE,36);
if(blue_base==NULL)
{
printk("ioremap error BLUE_BASE\n");
return -ENOMEM;
}
*blue_base&=~(1<<12);//灭蓝灯
*(blue_base+1)|=(1<<12);//设置为输出模式
*(blue_base+8)|=(1<<25);//将25位置为1
*(blue_base+8)&=~(1<<24);//将24位置为0
//所有资源申请完成后创建设备节点
cls=class_create(THIS_MODULE, NAME);
if(IS_ERR(cls))
{
printk("class_create error\n");
return IS_ERR(cls);
}
//向用户空间提交文件信息
dev=device_create(cls,NULL,MKDEV(major,1),NULL,NAME);
if(IS_ERR(dev))
{
printk("device_create error\n");
return IS_ERR(dev);
}
printk("this is ok\n");
return 0;
}
static void __exit hello_exit(void)
{
//拆卸销毁内存
device_destroy(cls,MKDEV(major,1));
class_destroy(cls);
//注销内存
iounmap(blue_base);//先注册的后注销
iounmap(green_base);
iounmap(red_base);
unregister_chrdev(major, NAME);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
2.3. 修改应用层.c文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
char buf[128] = {"my name is southernbird"}; //写入数据
int main(int argc, const char *argv[])
{
int fd;
int chose_numh;
fd = open("./dev/hello", O_RDWR);
if (fd == -1)
{
perror("open error");
return -1;
}
while (1)
{
printf("请输入对应选项:\n");
printf("1.红开 2.红关\n");
printf("3.绿开 4.绿关\n");
printf("5.蓝开 6.蓝关\n");
scanf("%d", &chose_numh);
switch (chose_numh)
{
case 1:
strcpy(buf, "RED_ON");
break;
case 2:
strcpy(buf, "RED_OFF");
break;
case 3:
strcpy(buf, "GREEN_ON");
break;
case 4:
strcpy(buf, "GREEN_OFF");
break;
case 5:
strcpy(buf, "BLUE_ON");
break;
case 6:
strcpy(buf, "BLUE_OFF");
break;
default:
printf("输入错误\n");
continue;
}
write(fd, buf, sizeof(buf));
memset(buf, 0, sizeof(buf)); //清空
read(fd, buf, sizeof(buf));
printf("%s\n", buf); //用来验证
}
close(fd);
return 0;
}
2.4. 编写makefile文件
#KERNELDIR:=/lib/modules/$(shell uname -r)/build
KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
arm-none-linux-gnueabi-gcc test.c
cp *.ko a.out /opt/6818/rootfs/rootfs
clean:
make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o
2.5. 在开发板安装.ko驱动
insmod hello.ko 安装
安装后自动生成了hello文件(在dev目录下)
在开发板根目录下运行a.out 测试成功
ps:我不得不吐槽了,这板子是真的慢
3. ioctl函数(学到这里就可以完成简单的项目了)
3.1. 函数原型
用户使用:
》1.函数原型:int ioctl(int fd, int request, ...)
功能:操作文件描述符,用于设置硬件设备的一些配置信息
参数:
fd:文件描述符
request:请求码 #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
...:可写可不写,要写写一个内存地址
内核使用:
》2.函数原型:long (*unlocked_ioctl) (struct file *, unsigned int,unsigned long)
功能:执行未加锁的 I/O 控制操作,接受文件指针和其他控制参数
3.2. 撰写.h文件
#ifndef __HEAD_H__
#define __HEAD_H__
#define type_red 'a' //红灯
#define type_green 'b' //绿灯
#define type_blue 'c' //蓝灯
#define type_buzzer 'd'//蜂鸣器
#define RED_ON _IO(type_red,0)
#define RED_OFF _IO(type_red,1)
#define GREEN_ON _IO(type_green,0)
#define GREEN_OFF _IO(type_green,1)
#define BLUE_ON _IO(type_blue,0)
#define BLUE_OFF _IO(type_blue,1)
#define BUZZ_ON _IO(type_buzzer,0)
#define BUZZ_OFF _IO(type_buzzer,1)
#endif
3.3. 撰写应用层.c文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include "head.h"
int LedCtl(int fd); //控制灯函数
int BuzzerCtl(int fd); //控制蜂鸣器函数
int FindLed(int fd); //读取灯状态
char buf[128] = "QUERY_LED"; //写入数据
int main(int argc, const char *argv[])
{
int fd;
int chose_num;
fd = open("./dev/hello", O_RDWR);
if (fd == -1)
{
perror("open error");
return -1;
}
while (1)
{
printf("请输入对应选项:\n");
printf("1.控制灯状态\n");
printf("2.控制蜂鸣器\n");
printf("3.查询灯得状态\n");
printf("4.退出\n");
scanf("%d", &chose_num);
if (chose_num == 1)
{
LedCtl(fd);
}
else if (chose_num == 2)
{
BuzzerCtl(fd);
}
else if (chose_num == 3)
{
FindLed(fd);
}
else if (chose_num == 4)
{
return 1;
}
else
{
printf("输入错误请重新输入\n");
}
}
close(fd);
return 0;
}
//读取灯状态
int FindLed(int fd)
{
strcpy(buf, "QUERY_LED");
write(fd, buf, sizeof(buf));
memset(buf, 0, sizeof(buf)); //清空
read(fd, buf, sizeof(buf));
printf("%s\n", buf); //打印灯状态
}
//控制蜂鸣器函数
int BuzzerCtl(int fd)
{
while (1)
{
printf("1.开启蜂鸣器 2.关闭蜂鸣器\n");
printf("3.返回上层\n");
printf("请选择:\n");
int buzzer_num;
scanf("%d", &buzzer_num);
if (buzzer_num == 1)
{
ioctl(fd, BUZZ_ON);
}
else if (buzzer_num == 2)
{
ioctl(fd, BUZZ_OFF);
}
else if (buzzer_num == 3)
{
return 1;
}
else
{
printf("输入错误\n");
}
}
}
//控制灯函数
int LedCtl(int fd)
{
while (1)
{
printf("1.红开 2.红关\n");
printf("3.绿开 4.绿关\n");
printf("5.蓝开 6.蓝关\n");
printf("7.返回上层\n");
printf("请选择:\n");
int led_num;
scanf("%d", &led_num);
switch (led_num)
{
case 1:
ioctl(fd, RED_ON);
break;
case 2:
ioctl(fd, RED_OFF);
break;
case 3:
ioctl(fd, GREEN_ON);
break;
case 4:
ioctl(fd, GREEN_OFF);
break;
case 5:
ioctl(fd, BLUE_ON);
break;
case 6:
ioctl(fd, BLUE_OFF);
break;
case 7:
return 1;
default:
printf("输入错误\n");
continue;
}
}
}
3.4. 撰写驱动的.c文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>//添加内存映射的函数
#include <linux/device.h>//添加头文件
#include <linux/string.h>
#include "head.h"
static int redflag=0;
static int blueflag=0;
static int greenflag=0;
unsigned int major =0;
#define NAME "hello"
int error_num=0;
char kbuf[128]={0};
#define RED_BASE 0XC001A000
#define BLUE_BASE 0XC001B000
#define GREEN_BASE 0XC001E000
#define BUZZ_BASE 0XC001C000
unsigned int * red_base=NULL;
unsigned int * blue_base=NULL;
unsigned int * green_base=NULL;
unsigned int * buzz_base=NULL;
//书写返回值声明
struct class*cls;
struct device *dev;
//ioctl函数自己写
long mycdev_ioctl(struct file*file,unsigned int request,unsigned long args)
{
int ret;
switch(request)
{
case RED_ON:
*red_base |=(1<<28);
redflag=1;
break;
case RED_OFF:
*red_base &=~(1<<28);
redflag=0;
break;
case BLUE_ON:
*blue_base |=(1<<12);
blueflag=1;
break;
case BLUE_OFF:
*blue_base &=~(1<<12);
blueflag=0;
break;
case GREEN_ON:
*green_base |=(1<<13);
greenflag=1;
break;
case GREEN_OFF:
*green_base &=~(1<<13);
greenflag=0;
break;
case BUZZ_ON:
* buzz_base |=(1<<14);
break;
case BUZZ_OFF:
* buzz_base &=~(1<<14);
break;
}
}
ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t * offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
error_num=copy_to_user(ubuf,kbuf,size);
if(error_num)
{
printk("copy_to_user error\n");
return -1;
}
return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
//因为是用户层控制内核层,来自用户控制在write中进行判断
error_num=copy_from_user(kbuf,ubuf,size);
if(error_num)
{
printk("copy_from_user error\n");
return -1;
}
//每次写的时候清理下内核数组
memset(kbuf,0,sizeof(kbuf));
if(strncmp(ubuf,"QUERY_LED",9)==0)
{
sprintf(kbuf, "红:%s--蓝:%s--绿:%s",
(redflag == 0) ? "灭" : "亮",
(blueflag == 0) ? "灭" : "亮",
(greenflag == 0) ? "灭" : "亮");
}
return 0;
}
int mycdev_open(struct inode *inode, struct file *file)
{
printk("hello open\n");
return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{
printk("hello close\n");
return 0;
}
struct file_operations fops={
.open=mycdev_open,
.write=mycdev_write,
.release=mycdev_release,
.read=mycdev_read,
.unlocked_ioctl=mycdev_ioctl,
};
static int __init hello_init(void)
{
major= register_chrdev(major, NAME, &fops);
if(major<0)
{
printk("register_chrdev error\n");
return major;
}
//在入口处将物理地址映射成虚拟地址
red_base=ioremap(RED_BASE, 36);//大小最大就是36
if(red_base ==NULL)
{
printk("ioremap error RED_BASE\n");
return -ENOMEM; //返回内存溢出
}
//注意解引用
redflag=0;
*red_base&=~(1<<28); //灭红灯
*(red_base+1)|=(1<<28);//设置为输出模式
*(red_base+9)&=~(3<<24);//选择function 0
green_base=ioremap(GREEN_BASE,36);
if(green_base==NULL)
{
printk("ioremap error GREEN_BASE\n");
return -ENOMEM;
}
greenflag=0;
*green_base&=~(1<<13);//灭绿灯
*(green_base+1)|=(1<<13);//设置为输出模式
*(green_base+8)&=~(3<<26);//选择function 0
blue_base=ioremap(BLUE_BASE,36);
if(blue_base==NULL)
{
printk("ioremap error BLUE_BASE\n");
return -ENOMEM;
}
blueflag=0;
*blue_base&=~(1<<12);//灭蓝灯
*(blue_base+1)|=(1<<12);//设置为输出模式
*(blue_base+8)|=(1<<25);//将25位置为1
*(blue_base+8)&=~(1<<24);//将24位置为0
//添加蜂鸣器模块
buzz_base=ioremap(BUZZ_BASE,36);
if(buzz_base==NULL)
{
printk("ioremap error BUZZER_BASE\n");
return -ENOMEM;
}
*buzz_base&=~(1<<14);//设置为低电平
*(buzz_base+1)|=(1<<14);//设置为输出模式
*(buzz_base+8)|=~(1<<28); //注意顺序,颠倒会对结果产生影响
*(buzz_base+8)&=~(1<<29);//注意顺序,颠倒会对结果产生影响
//所有资源申请完成后创建设备节点
cls=class_create(THIS_MODULE, NAME);
if(IS_ERR(cls))
{
printk("class_create error\n");
return IS_ERR(cls);
}
//向用户空间提交文件信息
dev=device_create(cls,NULL,MKDEV(major,1),NULL,NAME);
if(IS_ERR(dev))
{
printk("device_create error\n");
return IS_ERR(dev);
}
printk("this is ok\n");
return 0;
}
static void __exit hello_exit(void)
{
//拆卸销毁内存
device_destroy(cls,MKDEV(major,1));
class_destroy(cls);
//注销内存
iounmap(blue_base);//先注册的后注销
iounmap(green_base);
iounmap(red_base);
unregister_chrdev(major, NAME);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
3.5. 编写Makefile文件
#KERNELDIR:=/lib/modules/$(shell uname -r)/build
KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
arm-none-linux-gnueabi-gcc test.c
cp *.ko a.out /opt/6818/rootfs/rootfs
clean:
make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o