Ubuntu环境驱动编写
基本上分为两部分:
- Linux内核下载、编译、加载、新内核Ubuntu启动;
- 测试驱动编写(包括:driver.c、测试.c、Makefile)、编译、加载、测试、卸载;
Linux内核下载、编译、加载、新内核Ubuntu启动
有许多坑!
内核下载
内核source下载,有两类:
- kernel.org 网站下载[link];
- apt命令安装
sudo apt-cache search linux-source //查看版本号
sudo apt install linux-source-xxxx版本号 //一般下载到、usr/src/ 目录下
tar -jxvf … -C /解压目录 //.tar.bz2文件解压
内核编译、加载、重启
进入解压source目录:
- 编译内核(一),安装工具:
sudo apt-get install libncurses5-dev libssl-dev
sudo apt-get install build-essential openssl
sudo apt-get install zlibc minizip
sudo apt-get install libidn11-dev libidn11
- 编译内核(二),预处理:1
sudo make mrproper
sudo make clean
sudo make menuconfig
- 编译内核(三),加载、重启
sudo make –j8 //-j8指编译机器有8个核,-j4指4个核,可加快编译速度,不加为1个核
sudo make bzImage //在source的目录./arch/x86/boot/下生产bzImage文件,也有文档不使用此命令
sudo make modules
sudo make modules_install //安装内核模块
sudo make install //安装内核,此步可生产启动选项中的内核选项,如下条描述
sudo reboot //
- /usr/src目录下有“linux-header-xxxx版本号”的目录,对应Linux启动时,advanced(按shift键可进入)里的内核启动选项;
- 系统重启后,需要判断运行内核版本:
uname -r //后续驱动开发中,Makefile中定义KERNELDIR要指明内核source文件所在目录,有就是和编译的驱动和内核版本挂钩,如何版本不对,内核不能完成insmod;
驱动编写等
- 用户目录下,编写:Makefile、chardev.c、main.c(驱动测试程序)
make //依靠Makefile的内容,生成启动文件.ko;
gcc -o test main.c //生成可执行测试程序
- 驱动加载等
sudo insmod chardev.ko //加载,如果chardev.c中有printk打印信息,需要使用命令查看
sudo lsmod //查看模块是否加载
sudo rmmod //卸载模块
- 驱动模块添加到/dev/目录下:[link]
gedit /proc/devices //查看 chardev 驱动模块的主设备号
mknod /dev/chardev c 250 0 //在/dev/目录下,创建设备 chardev,其中,c代码字符驱动,250是驱动主设备号,0是次设备号。次设备号一般都为0
rm -rf /dev/chardev //删除 /dev/目录下的 chardev 设备
- kernel打印信息查看
cat /var/log/syslog //查看打印信息;
dmesg //-c 清除;
cat /proc/kmsg & //这时候能动态显示调试信息;
参考代码 [link]
- Makefile
#//
#Makefile
#make -C $(KDIR) M=$(SRCPWD) modules此语句前必须要一个Tab键
#rm -rf chardev.o此语句前必须要一个Tab键
#//
obj-m:=chardev.o
KDIR:=/lib/modules/$(shell uname -r)/build
#Kernel_Dir
SRCPWD:=$(shell pwd)
all:
make -C $(KDIR) M=$(SRCPWD) modules
clean:
rm -rf chardev.o
#//
- chardev.c
//
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
//
static int char_read(struct file *filp,char __user *buffer,size_t,loff_t *);
static int char_open(struct inode *,struct file *);
static int char_write(struct file *filp,const char __user *buffer,size_t ,loff_t*);
static int char_release(struct inode *,struct file *);
//
//static int device_open_count;
static int major;
//
static const struct file_operations file_ops = {
.read = char_read,
.write = char_write,
.open = char_open,
.release = char_release,
};
static int __init char_init(void)
{
int value;
major = 0;
value = register_chrdev(major, "chardev", &file_ops);
if (value < 0) {
printk("register dev failed!\n");
return value;
}
if (major == 0)
major = value;
return 0;
}
static int char_open(struct inode *inode,struct file *file)
{
// if(device_open_count == 0)
// device_open_count++;
// else{
// printk("设备已经被打开\n");
// return -1;
// }
try_module_get(THIS_MODULE);
return 0;
}
static int char_release(struct inode *inode,struct file *file)
{
// device_open_count--;
module_put(THIS_MODULE);
return 0;
}
static int char_read(struct file *filp,char __user *buffer,size_t length,loff_t *offset)
{
if(length < 0)
return -1;
else if(length > 12)
length = 12;
if(0 == copy_to_user(buffer,"Hello Linux!",length))
return length;
return -1;
}
static int char_write(struct file *filp,const char __user *buffer,size_t length,loff_t *offset)
{
return 0;
}
static void __exit module_close(void)
{
unregister_chrdev(major, "chardev");
}
//
module_init(char_init);
module_exit(module_close);
//
- main.c
///
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
///
int main(void)
{
char ch = 0;
int testdev;
int i,rf=0;
char buf[15];
memset(buf, 0, sizeof(buf));
testdev = open("/dev/chardev",O_RDWR);
if ( testdev == -1 )
{
perror("open error!\n");
exit(0);
}
while ('q' != ch)
{
rf=read(testdev,buf,11);
if(rf<0)
{
printf("read error[%d]!\n",rf);
}
else
{
printf("R:%s\n",buf);
}
scanf("%c",&ch);
}
close(testdev);
return 0;
}
///
mrproper为清除编译过程中产生的所有中间文件,clean为清除上一次产生的编译中间文件,在menuconfig中出现选择的图形化界面后,直接按右方向键选择到exit退出,退出提示中选择保存(生产.config文件),实现内核的默认配置。 ↩︎