一个驱动控制多个设备&每个设备拥有私有数据

  本文主要记录一下使用单一驱动,控制多个设备,且每个设备拥有私有数据。

  使每个设备拥有私有数据的主要思路为:在驱动侧 open 函数中,读取 inode 的设备号,通过次设备号,从而判断打开的是哪一个设备,从而将其对应的私有数据传递给 file 结构体。在 write/read 函数中就可以通过 file->private_data 获取到本设备的私有数据。

驱动源码

./Driver/chrdevs.h

#ifndef __CHRDEVS_H__
#define __CHRDEVS_H__

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
//#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/string.h>

#define NEWCHRDEV_CNT		3			//要申请的设备号个数
#define CHRDEVBASE_NAME		"chrdev"	//设备class名
#define BASEMINOR			0			//次设备号偏移

struct chrdev;
struct dataType;

struct chrdev{
    
    
	dev_t base_devid;								//该类设备的基设备号
	struct cdev cdev;								//cdev
	struct class *class;							//class
	struct device *device_list[NEWCHRDEV_CNT];		//多个设备
	struct device *parent;							//父设备
	struct dataType *drvdata[NEWCHRDEV_CNT];		//设备私有数据
	char *drvname[NEWCHRDEV_CNT];					//设备名
};

struct dataType{
    
    
	int num;
	char name[20];
	float score;
};

static int chrdev_open(struct inode *inode, struct file *filp);
static ssize_t chrdev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt);
static ssize_t chrdev_write(struct file *filp, const char *buf, size_t cnt, loff_t *offt);
static int chrdev_release(struct inode *inode, struct file *filp);
static int __init chrdev_init(void);
static void __exit chrdev_exit(void);

#endif

./Driver/chrdevs.c

/* 
 * file name	: chrdevs.c
 * description	: 一个驱动,管理多个设备,每个设备拥有独立的私有数据
 * author		: 今朝无言
 * date			: 2022.9.27
 */
#include "chrdevs.h"

struct dataType d1	= {
    
    
	.num	= 202201,
	.name	= "Zhang San",
	.score	= 78
};
struct dataType d2	= {
    
    
	.num	= 202202,
	.name	= "Li Si",
	.score	= 69
};
struct dataType d3	= {
    
    
	.num	= 202203,
	.name	= "Wang Wu",
	.score	= 92
};

struct chrdev chrdev_inst	= {
    
    
	.drvname	= {
    
    "chrdev1","chrdev2","chrdev3"},	//设备名,必须不同
	.drvdata	= {
    
    &d1, &d2, &d3}					//私有数据
};

static char readbuf[100];							//读缓冲区
static char writebuf[100];							//写缓冲区

static int chrdev_open(struct inode *inode, struct file *filp){
    
    
	printk("chrdev open!%d:%d\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
    //inode->i_rdev 为该 inode 的设备号
    
	struct dataType* data	= (struct dataType*)dev_get_drvdata(
			chrdev_inst.device_list[MINOR(inode->i_rdev)-BASEMINOR]);	//根据次设备号,判断打开的是哪一个设备,将其私有数据传递给filp
	filp->private_data		= (void*)data;

	return 0;
}

static ssize_t chrdev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){
    
    
	int retvalue = 0;
	
	struct dataType* data	= (struct dataType*)(filp->private_data);
	
	memcpy(readbuf, data->name, sizeof(data->name));	//将设备的私有数据传递给用户
	retvalue = copy_to_user(buf, readbuf, cnt);
	
	if(retvalue == 0){
    
    
		printk("kernel send data ok!\n");
	}
	else {
    
    
		printk("kernel send data failed!\n");
	}
	
	return 0;
}

static ssize_t chrdev_write(struct file *filp, const char *buf, size_t cnt, loff_t *offt){
    
    
	int retvalue = copy_from_user(writebuf, buf, cnt);
	
	retvalue = copy_from_user(writebuf, buf, cnt);
	if(retvalue == 0){
    
    
		printk("kernel receive data: %s \n",writebuf);
	}
	else {
    
    
		printk("kernel receive data failed!\n");
	}
	
	return 0;
}

static int chrdev_release(struct inode *inode, struct file *filp){
    
    
	//printk("chrdev release! \n");
	return 0;
}

static struct file_operations chrdev_fops = {
    
    
	.owner		= THIS_MODULE,
	.open		= chrdev_open,
	.read		= chrdev_read,
	.write		= chrdev_write,
	.release	= chrdev_release
};

static int __init chrdev_init(void){
    
    
	//申请设备号
	alloc_chrdev_region(&chrdev_inst.base_devid, BASEMINOR, NEWCHRDEV_CNT, CHRDEVBASE_NAME);

	//初始化 cdev
	chrdev_inst.cdev.owner	= THIS_MODULE;
	cdev_init(&chrdev_inst.cdev, &chrdev_fops);

	//添加 cdev
	cdev_add(&chrdev_inst.cdev, chrdev_inst.base_devid, NEWCHRDEV_CNT);

	//创建 class
	chrdev_inst.class	= class_create(THIS_MODULE, CHRDEVBASE_NAME);
	if (IS_ERR(chrdev_inst.class)){
    
    
		return PTR_ERR(chrdev_inst.class);
	}

	//动态创建设备
	chrdev_inst.parent		= NULL;
	int i;
	for(i=0; i<NEWCHRDEV_CNT; i++){
    
    
		chrdev_inst.device_list[i]	= device_create(chrdev_inst.class, chrdev_inst.parent, 
				chrdev_inst.base_devid+i, (void*)chrdev_inst.drvdata[i], chrdev_inst.drvname[i]);
	}

	printk("chrdev init...\n");
	return 0;
}

static void __exit chrdev_exit(void){
    
    
	//注销cdev
	cdev_del(&chrdev_inst.cdev);

	//释放设备号
	unregister_chrdev_region(chrdev_inst.base_devid, NEWCHRDEV_CNT);

	//注销设备
	int i;
	for(i=0; i<NEWCHRDEV_CNT; i++){
    
    
		device_destroy(chrdev_inst.class, chrdev_inst.base_devid+i);
	}

	//注销class
	class_destroy(chrdev_inst.class);
	
	printk("chrdev exit...\n");
	return;
}

//指定驱动入口和出口函数
module_init(chrdev_init);
module_exit(chrdev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("今朝无言");

./Driver/makefile

KERNELDIR := /lib/modules/`uname -r`/build

CURRENT_PATH := $(shell pwd)

obj-m := chrdevs.o

chrdevs.ko: chrdevs.c chrdevs.h
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean: rm_ko
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

load_ko: chrdevs.ko
	cp $< /lib/modules/$(shell uname -r)/$<
	depmod
	modprobe $(basename $<)

rm_ko:
	-rmmod chrdevs.ko
# load/rm_ko 记得给予 sudo 权限

test_ko:
	make -C ../App/ test
# dmesg | tail -6

用户测试代码

./App/chrdevbaseAPP.c

/* 
 * file name	: chrdevbaseAPP.c
 * description	: chrdevbase驱动的测试程序
 * author		: 今朝无言
 * date			: 2022.9.27
 */

#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 *argv[]){
    
    
	int fd, retvalue;
	char *filename;
	char readbuf[100], writebuf[100]; //读写缓冲
	
	//检查参数
	if(argc != 2){
    
    
		printf("Error Usage!\n");
		return -1;
	}
	
	filename = argv[1];
	
	//打开驱动文件
	fd = open(filename, O_RDWR);
	if(fd < 0){
    
    
		printf("Can't open file %s!\n", filename);
		return -2;
	}
	
	//从驱动文件读取数据
	retvalue = read(fd, readbuf, 50);
	if(retvalue < 0){
    
    
		printf("read file %s failed!\n", filename);
	}
	else {
    
    
		printf("read data: %s\n", readbuf);
	}
	
	//向驱动写数据
	memcpy(writebuf, readbuf, sizeof(readbuf));		//回传读到的数据
	retvalue = write(fd, writebuf, 50);
	if(retvalue < 0){
    
    
		printf("write file %s failed!\n", filename);
	}
	else {
    
    
		printf("write file success!\n");
	}
	
	//关闭设备
	retvalue = close(fd);
	if(retvalue < 0){
    
    
		printf("Can't close file %s!\n", filename);
		return -3;
	}
	
	return 0;
}

./App/makefile

build:
	gcc chrdevbaseAPP.c -o chrdevbaseAPP

clean:
	rm chrdevbaseAPP

test: chrdevbaseAPP
	./$< /dev/chrdev1
	./$< /dev/chrdev2

测试

  首先在 ./App/ 中进行编译,获得测试程序 chrdevbaseAPP

在这里插入图片描述

  在 ./Driver/ 中执行

sudo make load_ko

进行编译以及驱动加载,然后执行

sudo make test_ko

进行测试,测试结果如下

在这里插入图片描述

可以看到用户顺利读到了设备的私有数据,并将数据进行了回传。

 查看设备,可以看到一个驱动创建并管理了多个驱动设备

在这里插入图片描述

几个设备的主设备相同(243),次设备号分别为 0、1、2 。

猜你喜欢

转载自blog.csdn.net/qq_43557686/article/details/127075219