(二)
学号:14130140377
Email:[email protected]
教师:朱光明
##模块参数及Proc文件系统 ###一、实验目的
掌握简单字符设备驱动程序中模块参数及Proc文件系统的编写方法。
学习利用模块参数进行驱动程序参数传递,学习利用Proc文件系统进行数据读写。
###二、实验要求
按实验内容编写驱动程序
编译驱动程序
在嵌入式设备上加载驱动程序并利用Proc文件系统的读写进行驱动程序测试
###三、实验内容
写一个简单的字符设备驱动程序,要求:
- 定义一个全局结构指针,初始值为NULL,该数据结构中包含一个大小为N的buffer;
- 在open中对该全局结构进行NULL判断,为NULL则为其分配内存,并将buffer初始化为0;
- 在release中释放buffer;
- 通过读proc文件系统对该buffer进行读取;
- 通过写proc文件系统对该buffer进行赋值;
- Buffer的大小N是模块参数,在加载过程中指定。
###四、实验步骤
参考《嵌入式系统设计》课程第05讲内容,根据实验内容要求设计编写驱动程序
编译和加载驱动程序
进行驱动程序测试
卸载驱动程序
在docker上make成功
将文件通过网线和路由器将文件发送到树莓派中
insmod 动态加载模块
查看当前的模块
测试模块
open:对该全局结构进行NULL判断,当buffer为NULL时,则为其分配内存,并将buffer初始化为0,将count自加
static int qled_open(struct inode* inode, struct file *file) {
printk("open %u\n",count);
if(count ==0 || buffer == NULL) {
if(buffer_size == 0) {
buffer_size = BUF_SIZE;
}
buffer = kmalloc(buffer_size,GFP_KERNEL);
if(buffer == NULL) {
printk("cannot alloc");
return -1;
}
printk("alloc\n");
}
try_module_get(THIS_MODULE);
++ count;
return 0;
}
close:在release中如果count为0,则释放,否则进行count自减
// proc close
static int qled_close(struct inode *inode, struct file *filep)
{
--count;
printk("close %u",count);
if(count == 0) {
if(buffer) {
kfree(buffer);
printk("free\n");
buffer = NULL;
}
}
module_put(THIS_MODULE);
return 0;
}
write:在write里面对该buffer进行赋值,若count和文件指针来判断写入文件的大小
static ssize_t qled_write(struct file *filep, char *buf, size_t count, loff_t *f_pos)
{
int len = -ENOMEM;
if(buffer) {
if (*f_pos < buffer_size) {
len = count + *f_pos > buffer_size ? buffer_size - *f_pos : count;
copy_from_user(buffer + *f_pos,buf,len);
}
else {
len = count > buffer_size ? buffer_size : count;
copy_from_user(buffer + *f_pos,buf,len);
}
*f_pos += len;
}
return len;
}
read:在read里面对该buffer进行读取,若count和文件指针来判断读取内容的大小。
static ssize_t qled_read(struct file *filep, char *buf, size_t count, loff_t *f_pos)
{
int len = 0;
if(buffer) {
if (*f_pos < buffer_size) {
len = count + *f_pos > buffer_size ? buffer_size - *f_pos : count;
copy_to_user(buf,buffer + *f_pos,len);
}
*f_pos += len;
}
return len;
}
加载模块
static int init_ledc(void) {
printk("start!\n");
qled = proc_create_data("proc",0644,NULL,&qled_fops,"123");
return 0;
}
卸载模块
static void exit_ledc(void) {
printk("exit!\n");
remove_proc_entry("proc",NULL);
if(buffer) {
kfree(buffer);
printk("free");
}
}
Makefile:
obj-m:=proc.o
ledmodp-y:=proc.o
KERNELBUILD:=../../linux-rpi-4.4.y/
ccflags-y := -std=gnu99
module: proc.c
$(MAKE) ARCH=arm -C $(KERNELBUILD) M=$(shell pwd) modules ARCH=arm CROSS_COMPILE=armv7-rpi2-linux-gnueabihf-
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
测试程序:
#include <unistd.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fs.h>
int main() {
int fd = open("/proc/proc",O_RDWR);
char buf[1024] = "123456";
int k;
write(fd,buf,1024);
bzero(buf,1024);
scanf("%d",&k);
read(fd,buf,1024);
perror("error");
printf("%s\n",buf);
close(fd);
}
代码清单:
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include<linux/seq_file.h>
#include<linux/proc_fs.h>
#include<linux/sched.h>
#include<linux/uaccess.h>
#include<linux/string.h>
#include<linux/moduleparam.h>
#define BUF_SIZE (1024)
static unsigned long buffer_size = 0;
///// Datas
static char *buffer;
struct proc_dir_entty *qled;
static unsigned int count=0;
// proc open
static int qled_open(struct inode* inode, struct file *file) {
printk("open %u\n",count);
if(count ==0 || buffer == NULL) {
if(buffer_size == 0) {
buffer_size = BUF_SIZE;
}
buffer = kmalloc(buffer_size,GFP_KERNEL);
if(buffer == NULL) {
printk("cannot alloc");
return -1;
}
printk("alloc\n");
}
try_module_get(THIS_MODULE);
++ count;
return 0;
}
// proc close
static int qled_close(struct inode *inode, struct file *filep)
{
--count;
printk("close %u",count);
if(count == 0) {
if(buffer) {
kfree(buffer);
printk("free\n");
buffer = NULL;
}
}
module_put(THIS_MODULE);
return 0;
}
static ssize_t qled_write(struct file *filep, char *buf, size_t count, loff_t *f_pos)
{
int len = -ENOMEM;
if(buffer) {
if (*f_pos < buffer_size) {
len = count + *f_pos > buffer_size ? buffer_size - *f_pos : count;
copy_from_user(buffer + *f_pos,buf,len);
}
else {
len = count > buffer_size ? buffer_size : count;
copy_from_user(buffer + *f_pos,buf,len);
}
*f_pos += len;
}
return len;
}
static ssize_t qled_read(struct file *filep, char *buf, size_t count, loff_t *f_pos)
{
int len = 0;
if(buffer) {
if (*f_pos < buffer_size) {
len = count + *f_pos > buffer_size ? buffer_size - *f_pos : count;
copy_to_user(buf,buffer + *f_pos,len);
}
*f_pos += len;
}
return len;
}
// file operations
static struct file_operations qled_fops = {
open: qled_open,
release: qled_close,
read: qled_read,
write: qled_write,
};
static int init_ledc(void) {
printk("start!\n");
qled = proc_create_data("proc",0644,NULL,&qled_fops,"123");
return 0;
}
static void exit_ledc(void) {
printk("exit!\n");
remove_proc_entry("proc",NULL);
if(buffer) {
kfree(buffer);
printk("free");
}
}
module_init(init_ledc);
module_exit(exit_ledc);
module_param(buffer_size,ulong,S_IRUGO);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kangkang");