Linux字符设备驱动中同类型多设备节点的创建---一个驱动程序支持多个同类型设备


前言

本期分享的内容相对比较简单,那就是同时注册多个同类型的字符设备驱动,那么这样我们就可以同时支持多个同类型的设备了!下面来带大家看一下:


1 代码解析

1.1 驱动层

//本驱动程序支持主设备号major = 11,次设备号为0,1,2的三个设备
表明驱动程序支持三个同类型的设备,在使用时需要创建真实的设备节点
mknod /dev/mydev0 c 11 0
mknod /dev/mydev1 c 11 1
mknod /dev/mydev2 c 11 2
编写一个驱动程序,可以分别驱动三个设备节点

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include "mychar.h"

#define BUF_LEN 		100
#define MYCHAR_DEV_CNT 	3
int major = 11; 		//主设备号
int minor = 0; 			//次设备号
int mychar_num = MYCHAR_DEV_CNT; 	//设备数量

struct mychar_dev
{
	struct cdev mydev; 			//每一类设备都有一个cdev结构体

	char mydev_buf[BUF_LEN];  	//内核空间
	int curlen; 				//有效数字从零开始
};

//创建MYCHAR_DEV_CNT个设备结构体
struct mychar_dev gmydev_arr[MYCHAR_DEV_CNT];

int mychar_open(struct inode *pnode, struct file *pfile)
{
	pfile->private_data = (void *)container_of(pnode->i_cdev, struct mychar_dev, mydev);


	printk("mychar open is called!!!\n");
	return 0;
}

ssize_t mychar_read(struct file *pfile, char __user *pbuf, size_t count, loff_t *ppos)
{
	int size = 0;
	int ret = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;

	if (count > pmydev->curlen)
	{
		size = pmydev->curlen;
	}
	else
	{
		size = count;
	}

	ret = copy_to_user(pbuf, pmydev->mydev_buf, size);
	if (ret)
	{
		printk("copy_to_user failed!\n");
		return -1;
	}

	memcpy(pmydev->mydev_buf, pmydev->mydev_buf + size, pmydev->curlen - size);   //把在mydev_buf中剩下有效数据存放在以mydev_buf的首地址中
	pmydev->curlen -= size; 			//读走的字节要被减去

	return size;
}

ssize_t mychar_write(struct file *pfile, const char __user *pbuf, size_t count, loff_t *ppos)
{
	int size = 0;
	int ret = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;

	if (count < BUF_LEN - pmydev->curlen)
	{
		size = count;
	}
	else
	{
		size = BUF_LEN - pmydev->curlen;
	}

	ret = copy_from_user(pmydev->mydev_buf + pmydev->curlen, pbuf, size);
	if (ret)
	{
		printk("copy_from_user failed!\n");
		return -1;
	}

	pmydev->curlen += size;

	return size;
}

long mychar_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
	int __user *pret = (int *)arg;
	int maxlen = BUF_LEN;
	int ret = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;


	switch(cmd)
	{
		case MYCHAR_IOCTL_GET_MAXLEN:
			ret = copy_to_user(pret, &maxlen, sizeof(int));
			if (ret)
			{
				printk("fail to copy_to_user!\n");
				return -1;
			}
			break;
		case MYCHAR_IOCTL_GET_CURLEN:
			ret = copy_to_user(pret, &pmydev->curlen, sizeof(int));
			if (ret)
			{
				printk("fail to copy_from_user!\n");
				return -1;
			}
			break;
		default:
			printk("the cmd is unknow!\n");
			return -1;
	}

	return 0;
}

int mychar_close(struct inode *pnode, struct file *pfile)
{

	printk("mychar clsoe is called!!!\n");
	return 0;
}

/* 对字符设备的操作函数 */
struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mychar_open,
	.write = mychar_write,
	.read = mychar_read,
	.unlocked_ioctl = mychar_ioctl,	
	.release = mychar_close,
};

int __init mychar_init(void)
{
	int ret = 0;
	int i = 0;

	dev_t devno = MKDEV(major, minor); 										//组合设备号

	ret = register_chrdev_region(devno, mychar_num, "mychar"); 				//手动申请设备号
	if (ret) 																//返回值为0表示申请成功
	{
		ret = alloc_chrdev_region(&devno, 0, mychar_num, "mychar"); 	//申请失败则系统自动分配
		if (ret)
		{
			printk("get devno failed!\n");
			return -1;
		}
		major = MAJOR(devno); 												//从系统分配的设备号中取出主设备号
		minor = MINOR(devno); 												//从系统分配的设备号中取出次设备号
		devno = MKDEV(major, minor); 										//组合设备号
	}

	for (i = 0;i < MYCHAR_DEV_CNT;++i)
	{
		devno = MKDEV(major, minor + i); 										//组合设备号
		/* 使得设备具有myops中的函数操作方法 */
		cdev_init(&gmydev_arr[i].mydev, &myops);
		gmydev_arr[i].mydev.owner = THIS_MODULE;

		/* 将设备号为devno的这个设备(mydev)添加到内核(内核hash链表中) */
		cdev_add(&gmydev_arr[i].mydev, devno, 1);
	}

	printk("hello world!\n");
	return 0;
}

void __exit mychar_exit(void)
{
	int i = 0;
	dev_t devno = MKDEV(major, minor); 						//组合设备号
	
	for (i = 0;i < MYCHAR_DEV_CNT;++i)
	{
		/* 从内核中删除mydev这个设备 */
		cdev_del(&gmydev_arr[i].mydev);
	}
	unregister_chrdev_region(devno, mychar_num); 		//注销设备号
	printk("bye bye!!!\n");
}

MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);

1.2 应用层

应用层的代码没有任何修改!

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "mychar.h"

int main(int argc, const char *argv[])
{
	int fd = -1;
	char buf[6];
	int max = 0;
	int cur = 0;

	if (argc < 2)
	{
		printf("the arguement is too few!\n");
		return -1;
	}

	fd = open(argv[1], O_RDWR);
	if(fd < 0)
	{
		printf("fail to open %s\n", argv[1]);
		return -1;
	}

	ioctl(fd, MYCHAR_IOCTL_GET_MAXLEN, &max);
	printf("max = %d\n", max);

	write(fd, "hello", 6);

	printf("max = %d\n", max);
	ioctl(fd, MYCHAR_IOCTL_GET_CURLEN, &cur);
	printf("cur = %d\n", cur);

	read(fd, buf, 6);

	printf("buf = %s\n", buf);

	close(fd);
	fd = -1;
	return 0;
}

2 运行结果

在这里插入图片描述
每一个设备文件都支持这样的操作,因此运行后的结果是完全一致的!


总结

本期的分享相对来讲比较简单,就是需要将以前的设备修改为数组,那么在驱动程序的入口和出口函数中都需要进行着重修改,也就是循环创建和删除!
最后,各位小伙伴们如果有收获,可以点赞收藏哦,你们的认可是我创作的动力,一起加油!

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值