驱动内核模块编写步骤

驱动内核模块编写步骤

一、模块编写

1.模块组成(以下为一个模板实例)
一个内核模块一共由四部分组成:头文件,实现函数,注册功能以及模块描述:
实例文件(该文件完成了最简单的led灯的驱动):vim mymod.c

//头文件:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/semaphore.h>
#include <linux/sched.h>
#include <linux/poll.h>

#include "mymod.h"

#define MA 500
#define MI 0
#define GPX2CON 0x11000c40
#define GPX2DAT 0x11000c44

static dev_t devno = 0;
static int   devNum = 1;
static char* devName = "myDev";
static struct cdev mydev;
static char kbuf[32]={0};
static int flag = 0;
static char* gpx2con = NULL;
static char* gpx2dat = NULL;

static const struct file_operations myops={
	.open = myOpen,
	.release = myRelease,
	.read    = myRead,
	.write   = myWrite,
	.unlocked_ioctl = myIoctl
};
//实现函数(至少拥有mymod_init和mymod_exit两个函数):
int myOpen(struct inode *pi,struct file *pf)
{
	printk("open OK\n");
	return 0;
}
int myRelease(struct inode *pi,struct file *pf)
{
	printk("release OK\n");
	return 0;
}
ssize_t myRead(struct file *pf,char __user *ubuf,size_t len,loff_t *poff)
{
	int ret = -1;
	ret = copy_to_user(ubuf,kbuf,len);
	if(ret != 0)
		return -1;
	return len;
}
ssize_t myWrite(struct file *pf, const char __user *ubuf, size_t len, loff_t *poff)
{
	int ret = -1;
	ret = copy_from_user(kbuf,ubuf,len);
	if(ret != 0)
		return -1;
	return len;
}
long myIoctl(struct file *pf,unsigned int cmd,unsigned long arg)
{
	if(cmd == LED_ON)
	{
		writel(readl(gpx2dat) | (0x1<<7) ,gpx2dat);
		printk("led on\n");
	}
	else if(cmd == LED_OFF)
	{
		writel(readl(gpx2dat) & (~0x1<<7) ,gpx2dat);
		printk("led off\n");
	}
	return 0;
}
static int mymod_init(void)
{
	int ret = -1;
	//内核相关的部分
	//1.注册
	devno = MKDEV(MA,MI);
	ret = register_chrdev_region(devno,devNum,devName);
	if(ret != 0)
		return ret;
	//2.初始化
	cdev_init(&mydev,&myops);
	//3.添加到系统
	ret = cdev_add(&mydev,devno,devNum);
	if(0!=ret)
	{
		unregister_chrdev_region(devno,devNum);
		return ret;
	}
	//硬件相关的部分
	//1.将实际的物理地址映射为虚拟地址
	gpx2con = ioremap(GPX2CON,4);
	gpx2dat = ioremap(GPX2DAT,4);
	//2.对硬件进行初始化操作
	writel((readl(gpx2con) & ~(0xf<<28)) | (0x1<<28),gpx2con);
	writel(readl(gpx2dat) & ~(0x1<<7) ,gpx2dat);
	printk("mod init OK\n");
	return 0;
}
static void mymod_exit(void)
{
	//内核部分
	//1.删除设备
	cdev_del(&mydev);
	//2.注销设备
	unregister_chrdev_region(devno,devNum);
	//硬件部分
	//1.复位(不是必须操作)
	writel(readl(gpx2dat) & ~(0xf<<7), gpx2dat);
	//2.地址回收(必须操作)
	iounmap(gpx2con);
	iounmap(gpx2dat);
	printk("mod exit OK\n");
	return;
}
//注册功能:
module_init(mymod_init);
module_exit(mymod_exit);
//模块信息描述:
MODULE_LICENSE("GPL");//该模块信息最为重要,表示免费开源."BSD"表示部分开源

实例头文件:vim mymod.h

#ifndef _MYMOD_H
#define _MYMOD_H

#define LED_ON _IOW('L',0,int)
#define LED_OFF _IOW('L',1,int)

#endif

测试文件1(不涉及硬件):vim main.c

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
static char rbuf[32] = {0};
static char wbuf[32] = "123456";
void func1(int *fd)
{
	int newfd = *fd;
	while(1)
	{
		write(newfd,wbuf,sizeof(wbuf));
		write(newfd,"abcdef",sizeof(wbuf));
	}
}
int main()
{
	int fd = open("/dev/mydev",O_RDWR);
	if(fd<0)
		return -1;
	pthread_t thread1;
	pthread_create(&thread1,NULL,(void *)func1,&fd);
	pthread_detach(thread1);
	while(1)
	{
		read(fd,rbuf,16);
		printf("rbuf %s\n",rbuf);
	}
	close(fd);
	return 0;

}

测试文件2:(涉及led灯)

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "mymod.h"
#include <pthread.h>


int main()
{
	int fd = open("/dev/mydev",O_RDWR);
	if(fd<0)
		return -1;
	while(1)
	{
		ioctl(fd,LED_ON);
		sleep(1);
		ioctl(fd,LED_OFF);
		sleep(1);
	}

	close(fd);
}

说明:驱动文件主要分为与内核相接的接口和与硬件相接两个部分,最主要的就是驱动文件中的init和exit函数其书写格式和实例代码一致,唯一可能不一样的地方在于,针对每一种硬件驱动的硬件接口部分需要结合芯片手册进行设计。

二、模块编译

制定一个Makefile文件:(内核文件必须为板子上移植的内核)

#module templet
ifeq($(KERNELRELEASE),)
KERNELDIR ?= /home/farsight/linux-3.14
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
	cp -af *.ko /home/farsight/arm
u:
	arm-linux-gcc main.c -lpthread
	cp -af a.out /home/farsight/arm
clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean

else
	obj -m := mymod.o
endif

解释说明:
KERNELDIR ?= /home/farsight/linux-3.14(这个路径必须是板子上跑起来的内核路径)
/home/farsight/arm(这个路径必须是nfs注册的路径,具体查看命令cat /etc/exports)
mymod.o(该文件名必须和自己写的驱动文件同名,也就是说你写的驱动文件必须为mymod.c)

三、模块移植

编译生成之后我们可以在板子的文件系统上面看见一个mymod.ko的文件
然后我们可以插入驱动文件:

insmod mymod.ko

注册字符设备:

mknod /dev/mydev c 500 0(/dev/mydev必须和测试文件中的打开文件命名相同)

查看驱动文件:

lsmod

删除驱动文件:

rmmod mymod

错误提示:

在这里插入图片描述
解决方法:

mkdir /lib/modules
mkdir /lib/modules/3.14.0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值