Ubuntu编译并安装新驱动程序

Ubuntu编译并安装新驱动程序

内容

以编译模块的方法在ubuntu内核中增加一个新的设备驱动程序,功能为实现简单的字符设备(如键盘)读写。

环境

ubuntu版本:16.04.6
内核版本:4.15.0-74-generic
虚拟机:VMware

步骤

1.安装源码和工具包

执行命令查看是否安装源码。

ls -l /usr/src

执行命令查看内核版本。

uname -r

查看当前内核是否有对应的源码,若无需要去官网下载当前内核版本对应的源码,并将其解压至/usr/src文件夹中。清华开源镜像站

下载工具包:

apt-get install build-essential 
2.编译驱动程序hello.c

hello.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");
static int device_file_major_number = 0;
static const char device_name[] = "hello_driver";

int my_open(struct inode *, struct file *);
int my_release(struct inode *, struct file *);
ssize_t my_read(struct file *, char __user *, size_t , loff_t *);
ssize_t my_write(struct file *, const char __user *, size_t , loff_t *);

struct file_operations fops = {
	.owner=THIS_MODULE,
	.read=my_read,
	.write=my_write,
	.release=my_release,
	.open=my_open,
};
#define BUF_SIZE 100
struct cdd_cdev{
	struct cdev cdev;
	struct device *dev_device;
	u8 led;
	char kbuf[BUF_SIZE];
};

int my_open(struct inode *inode, struct file *file)
{
	struct cdd_cdev *pcdevp = NULL;
	printk("my_open() execute successfully!\n");
	pcdevp = container_of(inode->i_cdev, struct cdd_cdev, cdev);
	printk("led = %d\n", pcdevp->led);
	file->private_data = pcdevp;
	return 0;
}

int my_release(struct inode *inode, struct file *file)
{
	printk("my_release() execute successfully!\n");
	return 0;
}

ssize_t my_read (struct file *file, char __user *buf, size_t count, loff_t *offset)
{
	int ret = 0;
	struct cdd_cdev *cdevp = file->private_data;
	printk("my_read() execute successfully!\n");
	ret = copy_to_user(buf, cdevp->kbuf, count);
	printk("kernel kbuf content:%s\n", cdevp->kbuf);
	return ret;
}
ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
	int ret = 0;
	struct cdd_cdev *cdevp = file->private_data;	
	printk("my_write() execute successfully!\n");
	ret = copy_from_user(cdevp->kbuf, buf, count);
	return ret;
}



int __init my_init(void)
{
	int result = 0;
	printk( KERN_NOTICE "hello-driver: my_init() is called." );
	result = register_chrdev( 0, device_name, &fops );
	if( result < 0 )
	{
		printk( KERN_WARNING "hello-driver:  can\'t register character device with errorcode = %i", result );
		return result;
	}
	device_file_major_number = result;
	printk( KERN_NOTICE "hello-driver: registered character device with major number = %i and minor numbers 0...255"
  , device_file_major_number );
	return 0;	
}

void __exit my_exit(void)
{
	printk( KERN_NOTICE "hello-driver: my_exit() is called" );
	if(device_file_major_number != 0)
	{
		unregister_chrdev(device_file_major_number, device_name);
	}
}

module_init(my_init);
module_exit(my_exit);
  • 编写Makefile文件
ifeq ($(KERNELRELEASE),) #ifeq后面一定要加空格
#非命令行前面不能以tab开头,必须以空格开头
 KERNELDIR ?= /lib/modules/$(shell uname -r)/build  
 M=$(PWD) modules
 PWD := $(shell pwd)
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules #必须以tab开头
modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install #必须以tab开头
clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules* #必须以tab开头
.PHONY: modules modules_install clean
else
 obj-m :=hello.o #空格开头
endif

备注:

  1. gcc编译方式只适用于2.4版本以下的内核,编译2.4以上版本的内核需使用make编译。
  2. 源文件需以.c为后缀,笔者曾尝试编译.cc文件,结果报错。
  3. Makefile文件名的M必须为大写,否则编译出错。
  4. 编写Makefile文件时只需把上述代码中的hello替换为自己编写的源文件名除去.c后缀。
  5. 以root用户执行make命令。
3.执行make,编译源码
make

make

编译成功后输出如图,若输出结果与图示不同,则极大可能编译失败。如若输出很多的Entering directory xxx 以及 Leaving directory directory xxx,最后并显示Error时,很可能是因为内核源码版本有问题。笔者在最开始使用的是最新版Ubuntu18.04和5.3.0版本的内核,但make的时候一直出错,在几度崩溃后笔者选择在凌晨2点的夜里带着无尽的怒火关闭了电脑上床入睡(顺带做了一晚上的梦…),并在第二天决定更换稍微旧一点的版本,即本文开端的环境所述,最终make顺利编译成功。

4.安装模块

执行命令,安装成功后应无任何输出。

insmod hello.ko
5.创建设备文件

因为笔者在源码的注册设备文件函数register_chrdev()中传入的第一个参数为0,系统会为新设备随机分配一个未使用的主设备号,所以在创建设备文件前需要获得文件主设备号。

cat /proc/dev | grep hello

得到文件主设备号后我们接着创建设备文件

mknod /dev/hello c 主设备号 从设备号(可设为0)
6.编写测试程序

当做到这一步时,即将接近尾声!

test.c

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>

#define N 512
char buf[N];

int main(){
	int fd;
	if((fd=open("/dev/hello",O_RDWR))<0){
		printf("open error!\n");
		return -1;
	}
	if(read(fd,buf,N)<0){
		printf("read error!\n");
		return -1;	
	}
	printf("read from /dev/hello is : %s\n",buf);
	printf("input second buf:");
	scanf("%[^\n]",buf);	//忽略空格直至换行
	if(write(fd,buf,N+1)<0){
		printf("write error!\n");
		return -1;
	}
	if(read(fd,buf,N)<0){
		printf("read error!\n");
		return -1;	
	}
	printf("second read from /dev/hello is : %s\n",buf);
	close(fd);
	return 0;
}

编译测试文件并执行

gcc test.c -o test
./test

Congratulation !

End


  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值