(1)首先新建并编写了字符驱动设备chardev.c文件,文件代码如下:
/*
* 创建一个字符设备(读写)
*/
/* 必要的头文件,内核模块标准头文件 */
#include<linux/init.h>
#include<linux/kernel.h> /*内核工作*/
#include<linux/slab.h>/**/
#include<linux/vmalloc.h>
#include<linux/module.h> /*明确指定是模块*/
#include<linux/moduleparam.h>
/*对于字符设备*/
#include<linux/fs.h> /*字符设备定义*/
#include<linux/cdev.h>
#include<asm/system.h>
#include<asm/uaccess.h>
MODULE_AUTHOR("author");
MODULE_LICENSE("GPL");
struct char_dev *char_device;
int dev_major=0;
int dev_minor=0;
module_param(dev_major,int,S_IRUGO);
module_param(dev_minor,int,S_IRUGO);
//设备存储区的指针
char *p_mem=NULL;
//设备存储区的大小
long len=1000;
//表示设备的数据结构
struct char_dev
{
char * data; // 模块中的数据
long len; // 数据长度
struct cdev cdev; //Linux 字符设备结构,由系统定义
};
int char_open(struct inode *inode,struct file *filp)
{
struct char_dev *dev;
//从inode 获取设备结构体
dev=container_of(inode->i_cdev,struct char_dev,cdev);
//赋值给file 结构体
filp->private_data=dev;
return 0;
}
//读时将调用的函数
static ssize_t char_read(struct file * filp, char __user *buf, size_t count, loff_t *offset)
{
char * buffer = (char *)filp->private_data;
if(copy_to_user(buf, buffer, count))
{
printk("copy_to_user error/n");
return -EFAULT;
}
printk("You are using the read function!");
return count ;
}
//参数定义和char_read 类似
static ssize_t char_write(struct file * filp, const char __user *buf, size_t count, loff_t *offset)
{
char * buffer = (char *)filp->private_data;
//printk("new %p/n", buffer);
if(copy_from_user(buffer, buf, count))
{
printk("copy_from_user error/n");
return -EFAULT;
}
return count;
}
//读写完毕后调用的函数
int char_release(struct inode *inode,struct file *filp)
{
return 0;
}
//定义设备节点文件的操作
static struct file_operations char_ops={
.owner = THIS_MODULE,
.open = char_open,
.read = char_read,
.write = char_write,
.release =char_release
};
//设备初始化时调用的函数,用于获取存储区内存空间
int memory_init(void)
{
p_mem=vmalloc(len*sizeof(char));
if(!p_mem)
{
printk(KERN_ALERT "error-memory_init/n");
return -1;
}
return 1;
}
//设备初始化
static int dev_init(void)
{
int result=0;
dev_t dev=0;
dev_t devno=0;
int err=0;
//获取存储区
//如果定义了主设备号
if(dev_major)
{
//则按照定义的设备号注册设备
dev=MKDEV(dev_major,dev_minor);
result=register_chrdev_region(dev,1,"char_dev");
}else{
//否则分配新的设备号
result=alloc_chrdev_region(&dev,dev_minor,1,"char_dev");
dev_major=MAJOR(dev);
}
if(result < 0)
{
printk(KERN_ALERT "can't get major %d/n",dev_major);
return result;
}
//返回主设备号
printk("the dev_major %d/n",dev_major);
//获取全局char_dev 结构体
char_device = kmalloc(sizeof(struct char_dev),GFP_KERNEL);
if(!char_device)
{
result=-ENOMEM;
return result;
}
memset(char_device,0,sizeof(struct char_dev));
//使用定义了的文件操作char_ops 初始化cdev
cdev_init(&char_device->cdev, &char_ops);
char_device->cdev.owner = THIS_MODULE;
char_device->cdev.ops = &char_ops;
//使用后区的设备号注册设备
devno=MKDEV(dev_major,dev_minor);
//添加此字符设备到系统
err=cdev_add(&char_device->cdev,devno,1);
char_device->data=p_mem;
char_device->len=len;
return 0;
}
//设备被移出时调用
static void dev_exit(void)
{
dev_t devno=0;
devno = MKDEV(dev_major,dev_minor);
cdev_del(&char_device->cdev);
kfree(char_device);
unregister_chrdev_region(devno,1);
}
//注册模块初始化和卸载函数
module_init(dev_init);
module_exit(dev_exit);
2.写Makefile文件
obj-m := chardev.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD)
(3)使用make 命令,生成驱动程序chardev.ko
(4)用root 挂载设备:
insmod chardev.ko
(5)在文件系统为其创建一个代表节点(建立设备文件)。
#dmesg 寻找主设备号,找到后挂载,设备号要相同
创建节点命令格式如下:
mknod /dev/<dev_name><type><major_number><minor_number>
例如(若主设备号为249):
mknod mychardev0 c 250 0
(6)修改属性:
chmod 666 mychardev*
(7) test.cpp内容
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
int main()
{
int fd;
char buffer_write[20] = "Hello World!";
char buffer_read[20] = "Hello China!";
fd=open("mychardev0", O_RDWR);
if(fd < 0)
{
cout<<"open dev error!"<<endl;
exit(fd);
}
//向指定设备写入用户输入文本
cout<<"Please input the text:"<<endl;
cin>>buffer_write;
write(fd, buffer_write, 20);
//输出设备中的内容
read(fd, buffer_read, 20);
cout<<"指定设备中的内容为:"<<endl<<buffer_read<<endl;
close(fd);
return 0;
}
权限不够加sudo
g++ test.cpp -o test
./test即可运行
rmmod mychardev0
dmesg
【终于搞定了这个实验】