驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#define CNAME "myled"
unsigned int major = 0; //分配的主设备号
char kbuf[128] = "";
struct class *cls = NULL;
struct device *dev = NULL;
int myled_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); //打印一句话
return 0;
}
ssize_t myled_read(struct file *file, char __user *ubuf, size_t size, loff_t *loffs)
{
int ret;
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);//打印一句话
//如果用户空间想读的大小,大于内核空间的大小,更正写大小
if(size > sizeof(kbuf)) size = sizeof(kbuf);
ret = copy_to_user(ubuf,kbuf,size); //将内核空间的数据拷贝到用户空间
if(ret){
printk("copy to user is error\n");
return -EIO;
}
return size; //!!!!!!!!!!
}
ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loffs)
{
int ret;
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);//打印一句话
//如果用户空间写的大小,大于内核空间的大小,更正写大小
if(size > sizeof(kbuf)) size = sizeof(kbuf);
ret = copy_from_user(kbuf,ubuf,size); //将用户空间的数据拷贝到内核空间
if(ret){ //判断错误
printk("copy from user is error\n");
return -EIO;
}
//打印kbuf中的内容,kbuf中存储的内容,就是用户空间拷贝过来的内容
printk("kernel kbuf = %s\n",kbuf);
return size; //!!!!!!!!!!
}
int myled_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);//打印一句话
return 0;
}
//操作方法结构体
const struct file_operations fops = {
.open = myled_open,
.read = myled_read,
.write = myled_write,
.release = myled_close,
};
//入口
static int __init mycdev_init(void)
{
//1.注册字符设备驱动
major = register_chrdev(0,CNAME,&fops);
if(major <= 0){
printk("register chrdev is error\n");
return -EIO;
}
//2.打印分配成功主设备号
printk("major = %d\n",major);
//3.提交目录信息 class_create
cls = class_create(THIS_MODULE,CNAME);
if(IS_ERR(cls)) //判断错误码转换为地址是否在4K空间内
{
printk("class create is error\n");
return PTR_ERR(cls);//将地址转换为错误码
}
//4.提交设备节点信息 device_create 节点名字myled
dev = device_create(cls,NULL,MKDEV(major,0),NULL,CNAME);
if(IS_ERR(dev)) //判断错误码转换为地址是否在4K空间内
{
printk("device create is error\n");
return PTR_ERR(dev);//将地址转换为错误码
}
return 0;
}
//出口
static void __exit mycdev_exit(void)
{
device_destroy(cls,MKDEV(major,0));//取消向上层提交设备节点信息
class_destroy(cls);//取消向上层提交目录信息
//注销字符设备驱动
unregister_chrdev(major,CNAME);
}
module_init(mycdev_init);//指定入口地址
module_exit(mycdev_exit);//指定出口地址
MODULE_LICENSE("GPL");//遵循GPL协议
应用层代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int fd = -1;
char buf[128] = "";
fd = open("/dev/myled",O_RDWR); //打开
if(fd == -1){
perror("open is error\n");
exit(1);
}
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';
write(fd,buf,sizeof(buf)); //将buf内容拷贝到内核空间
memset(buf,0,sizeof(buf)); //将buf内容清空
read(fd,buf,sizeof(buf)); //将内核空间的数据读到buf中
printf("user buf = %s\n",buf);
close(fd);//关闭
return 0;
}
Makefile
ARCH ?= arm
MODNAME ?= mycdev
#编译ARM架构
ifeq (arm,$(ARCH))
KERNEDIR := /home/ubuntu/linux-5.10.61/
else
#编译X86架构
KERNEDIR := /lib/modules/$(shell uname -r)/build
endif
#打开终端,在当前终端执行pwd命令,赋值给PWD
PWD := $(shell pwd)
all:
@#采用模块化编译
@#-C : 跳转到内核源码顶层目录,读取该目录下的Makefile
@#M 跳转到当前编写驱动的目录,读取该目录下的Makefile
make -C $(KERNEDIR) M=$(PWD) modules
clean:
make -C $(KERNEDIR) M=$(PWD) clean
#制定编译文件
obj-m := $(MODNAME).o
测试
1.编译驱动代码:linux@ubuntu:~/DC23081/02_chrdev$ make ARCH=x86 MODNAME=mycdev
2.清除打印信息:linux@ubuntu:~/DC23081/02_chrdev$ sudo dmesg -C
3.安装驱动:linux@ubuntu:~/DC23081/02_chrdev$ sudo insmod mycdev.ko
4.查看分配到主设备号信息:linux@ubuntu:~/DC23081/02_chrdev$ dmesg ===>[ 9885.250251] major = 236
5.编译应用层程序
6.编译应用层程序 ===> linux@ubuntu:~/DC23081/02_chrdev$ gcc test.c
7.运行应用层程序 ===> linux@ubuntu:~/DC23081/02_chrdev$ sudo ./a.out
8.查看内核层打印信息 ===> dmesg