root用户新建一个单独文件夹用于存放驱动源码和Makefile。
驱动代码zhangchen_test.c:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#define __DEV_NUM__ 200
static unsigned char g_a_buffer[4096]; //驱动内存,与用户空间交互使用
static struct class *g_p_class;
/* 打开设备 */
static int zhangchen_test_open(struct inode *inode, struct file *filp)
{
/* 用户实现具体功能 */
printk("Enter func zhangchen_test_open open.\n");
return 0;
}
/* 关闭/释放设备 */
static int zhangchen_test_release(struct inode *inode, struct file *filp)
{
/* 用户实现具体功能 */
printk("Enter func zhangchen_test_release open.\n");
return 0;
}
/* 从设备读取 */
static ssize_t zhangchen_test_read(struct file *filp, char __user *buf,
size_t count, loff_t *p_offset)
{
/* 用户实现具体功能 */
if(count > sizeof(g_a_buffer))
{
printk(KERN_ALERT "Read count is too long!\n");
return -EMSGSIZE;
}
if(copy_to_user(buf,g_a_buffer+*p_offset,count))
{
printk(KERN_ALERT "Func copy_to_user failed!\n");
return -EFAULT;
}
g_a_buffer[4095] = 0;
printk("Read buffer[%s]\n" , g_a_buffer);
return 0;
}
/* 向设备写数据 */
static ssize_t zhangchen_test_write(struct file *filp,
const char __user *buf,
size_t count, loff_t *p_offset)
{
/* 用户实现具体功能 */
if(count > sizeof(g_a_buffer))
{
printk(KERN_ALERT "Read count is too long!\n");
return -EMSGSIZE;
}
if(copy_from_user(g_a_buffer+*p_offset,buf,count))
{
printk(KERN_ALERT "Func copy_from_user failed!\n");
return -EFAULT;
}
g_a_buffer[4095] = 0;
printk("Write buffer[%s]\n" , g_a_buffer);
return 0;
}
//定义系统调用与驱动内部函数映射关系
static struct file_operations g_test_fops = {
.owner = THIS_MODULE,
.open = zhangchen_test_open,
.read = zhangchen_test_read,
.write = zhangchen_test_write,
.release = zhangchen_test_release,
};
/* 驱动入口函数 */
static int __init zhangchen_test_init(void)
{
int ret = 0;
struct device *p_device = NULL;
printk("Enter func zhangchen_test module init!\n");
/* 注册字符设备驱动 */
ret = register_chrdev(__DEV_NUM__, "zhangchen_test", &g_test_fops); //参数1为0表示由系统分配,为其他表示自己静态分配。静态分配主设备号可从/proc/devices文件查看未使用的id(主设备号代表类型,此设备号代表该类型设备的序号)
//注册后,可已使用命令mknod在/dev/下创建设备文件,也可以在驱动程序入口直接调用class_create、device_create创建
if(ret < 0)
{
/* 字符设备注册失败,自行处理 */
printk(KERN_ALERT "Func register_chrdev failed! Ret[%d]\n" , ret);
return ret;
}
else
{
//创建设备文件
g_p_class = class_create(THIS_MODULE, "zhangchen_dev");
if(IS_ERR(g_p_class))
{
printk(KERN_ALERT "Failed to create device.\n");
unregister_chrdev(__DEV_NUM__, "zhangchen_test");
return -1;
}
p_device = device_create(g_p_class, NULL, MKDEV(__DEV_NUM__, 0), NULL, "zhangchen_dev");
if(IS_ERR(p_device))
{
printk(KERN_ALERT "Failed to create device.\n");
class_destroy(g_p_class);
unregister_chrdev(__DEV_NUM__, "zhangchen_test");
return -2;
}
}
printk("zhangchen_test module init successful!\n");
return 0;
}
/* 驱动出口函数 */
static void __exit zhangchen_test_exit(void)
{
printk("Enter func zhangchen_test module exit!\n");
//注销设备文件
device_destroy(g_p_class , MKDEV(200, 0));
class_destroy(g_p_class);
/* 注销字符设备驱动 */
unregister_chrdev(__DEV_NUM__, "zhangchen_test");
printk("zhangchen_test module exit successful!\n");
}
/* 将上面两个函数指定为驱动的入口和出口函数 */
module_init(zhangchen_test_init);
module_exit(zhangchen_test_exit);
MODULE_LICENSE("GPL");//LICENSE 采用 GPL 协议。
MODULE_AUTHOR("zhangchen");//添加作者名字
编译需要安装依赖(以centos为例):
yum -y install kernel_devel-$(uname -r)
yum -y install kernel_headers-$(uname -r)
确认/usr/src/kernels/下存在对应内核版本的目录。
(注:若yum install找不到对应版本的包,需要网页到阿里镜像站对应的过期源中下载rpm包再传到linux中手动安装。例centos7.4到https://mirrors.aliyun.com/centos-vault/7.4.1708/os/x86_64/Packages/目录下找)
Makefile:
KERNEL_DIR = /usr/src/kernels/`uname -r`
obj-m += zhangchen_test.o
all:
make -C $(KERNEL_DIR) M=`pwd` modules
clean:
make -C $(KERNEL_DIR) M=`pwd` modules clean
rm -rf *o *.mod.c *.order *.symvers
装载驱动: make编译后目录下出现对应ko,insmod zhangchen_test.ko不报错则执行成功。(也可以使用modprobe进行装载。卸载驱动为rmmod zhangchen_test.ko。)
校验结果: cat /proc/devices查看200设备id是否出现。 lsmod | grep zhangchen_test查看是否显示对应驱动。 ls /dev/查看字符设备文件zhangchen_dev有没有产生。查看/var/log/messages打印内容(驱动为zhangchen_test,设备为zhangchen_dev)
测试程序test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char **argv)
{
int fd;
char buf[1024];
fd = open("/dev/zhangchen_dev",O_RDWR);
if(-1==fd)
{
printf("Func open failed!\n");
return -1;
}
write(fd, "aabbcc" , 7);
printf("Write [%s] to driver zhangchen_test. Please check from /var/log/messages\n" , "aabbcc");
read(fd , buf , 7);
printf("Read [%s] from driver zhangchen_test\n",buf);
sleep(1);
write(fd, "test end!" , 10);
printf("Write [%s] to driver zhangchen_test. Please check from /var/log/messages\n" , "test end!");
read(fd , buf , 10);
printf("Read [%s] from driver zhangchen_test\n",buf);
close(fd);
puts("Exit");
return 0;
}