编写一个虚拟字符设备驱动程序char.c,
- 以内核模块的形式插入内核,编译方法与内核编译方法一致。
- 创建设备节点,然后通过编写一个测试程序,
- 功能:首先向设备中写入数据,再从设备中读出数据,并把数据显示在屏幕上。
4. 要求:设备名为:demo,主设务号为250,次设备号为0。
1、首先,要确保环境已经安装好了
开始写
写三个文件
char.c
Makefile ps:M必须大写
test.c 用于测试
vi char.c
vi Makefile
vi test.c
这三个文件的具体代码我放在文末
写好了之后执行make命令
make
然后加载驱动程序
sudo insmod char.ko
lsmod //查看是否加载成功
必须使用管理员权限
在dev目录下创建设备文件
sudo mknod /dev/mychar c 250 0
//要使用管理员权限
//1. 命令中的数字要和驱动程序定义的major,minor保持一致。
//2. Mychar文件名与测试程序 中的名字一致
编译运行测试程序
gcc test.c -o test
sudo ./test
#include
#include
#include
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Bob Geng");
MODULE_DESCRIPTION("a simple driver");
#define N 128
int major = 250;
int minor = 0;
struct cdev mycdev; //字符型结构体
char buf[N] ={"hello world !!"};
int char_open (struct inode * myinode, struct file *fp)
{
printk("char is opened\n");
return 0;
}
int char_release (struct inode *myinode, struct file *fp)
{
printk("char is closeed\n");
return 0;
}
static ssize_t char_read (struct file *filep, char __user *user_buf, size_t count, loff_t * off)
{ // 1. ssize_t :ssize_t是signed size_t;size_t: unsigned int
//2. Off:当前文件的偏移量
ssize_t ret =0;
long num =0;
printk("char_read is called\n");
printk("count is %d\n",count);
num = copy_to_user(user_buf,buf,count);
if(num < 0 )
{
printk("copy_to_user is failed\n");
return ret;
}
return ret;
}
ssize_t char_write (struct file *filep, const char __user *from, size_t count, loff_t *off)
{
ssize_t ret =0;
long num =0;
printk("char_write is called \n");
printk("count is %d\n",count);
// if(count > N ) return -ENOMEM;
if(count > N ) count = N ;
num = copy_from_user(buf,from,count);
if(num < 0 )
{
printk("copy_to_user is failed\n");
return ret;
}
printk("from user is %s\n",buf);
return ret;
}
struct file_operations fops={
.owner = THIS_MODULE,
.open = char_open,
.release = char_release,
.read = char_read,
.write = char_write,
};
static int __init char_init(void)
{
int ret;
dev_t devno = MKDEV(major,minor);
ret = register_chrdev_region(devno,1,"char"); //静态申请设备号
if(ret < 0 )
{
printk("fail to get devno\n");
return ret;
}
mycdev.owner = THIS_MODULE;
cdev_init(&mycdev,&fops);
ret = cdev_add(&mycdev,devno,1);
if(ret < 0 )
{
printk("cdev_add fail to system\n");
return ret;
}
printk("init_module\n");
return 0;
}
static void __exit char_exit(void)
{
dev_t devno = MKDEV(major,minor);
cdev_del(&mycdev);
unregister_chrdev_region(devno, 1);
printk("cleanup_module\n");
}
module_init(char_init);
module_exit(char_exit);
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
#KERNELDIR ?= /home/linux/workdir/source-pack/linux-3.2-net/ (交叉编译)
M=$(PWD) modules
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: modules modules_install clean
else
obj-m :=char.o
endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define N 128
char buf[N] ;
int main()
{
int fd;
if ( (fd = open("/dev/mychar",O_RDWR)) < 0)
{
perror("open");
exit(-1);
}
if(read(fd,buf,N ) < 0 )
{
perror("read");
exit(-1);
}
printf("read from mychar is %s\n",buf);
// memset(buf,0,sizeof(buf));
//strcpy(buf,"goddbye\0");
printf("please input second buf:\n");
scanf("%s",buf);
if(write(fd,buf,N+1) < 0)
{
perror("write");
exit(-1);
}
if(read(fd,buf,N ) < 0 )
{
perror("read");
exit(-1);
}
printf("second read from mychar is %s\n",buf);
getchar();
printf("mychar is opened\n");
close(fd);
}