字符设备驱动框架及测试程序

2023-11-14

1. 驱动框架

1.1 驱动文件: my_cdev.c

#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>


#define MAX_DEV_CNT 65535 


/*define my device*/
struct my_cdev {
	int len;
	unsigned char buf[100];
	struct cdev cdev;
};
struct my_cdev *my_cdev;

static dev_t dev_num;

struct class *my_cdev_class;

static ssize_t my_cdev_read(struct file *file, char __user *buffer, size_t count,
			  loff_t *ppos)
{
	struct my_cdev *dev = file->private_data;

	if (copy_to_user(buffer, &dev->buf, count)) 
		printk("read failed.\n");

	return sizeof(int);
}

static ssize_t my_cdev_write(struct file *file, const char __user *buffer,
			   size_t count, loff_t *ppos)
{
	struct my_cdev *dev = file->private_data;
	int i;

	if (copy_from_user(&dev->buf, buffer, count))
		printk("write failed.\n");
	for (i = 0; i < count; i++)
		printk("dev->buf[%d] = %c\n", i, dev->buf[i]);

	return sizeof(int);
}


	
static int my_cdev_open(struct inode *inode, struct file *file)
{

	struct my_cdev *mycd;

	mycd  = container_of(inode->i_cdev, struct my_cdev, cdev);
    	file->private_data = mycd;

	printk("my char device is opened.\n");

	return 0;
}


static int my_cdev_release(struct inode *inode, struct file *filp)
{	
	printk("my char device is closed.\n");
	return 0;
}

static const struct file_operations my_cdev_fops = {
	.owner		= THIS_MODULE,
	.read		= my_cdev_read,
	.write		= my_cdev_write,
	.open		= my_cdev_open,
	.release	= my_cdev_release,
};

int __init my_cdev_init(void)
{
	int ret = 0;

	/*1. alloc memory for my device defined.*/
	my_cdev = kzalloc(sizeof(struct my_cdev), GFP_KERNEL);	
	if (!my_cdev) {
		printk("can't alloc memory for this device.\n");
		goto err;
		ret = -ENOMEM;
	}

	/*2. register device number for the char device.*/
	ret = alloc_chrdev_region(&dev_num, 0, MAX_DEV_CNT, "my_cdev"); //a dynamic way.
	if (ret < 0) {
		printk("register the device number failed.\n");
		goto err;
	}

	/*3. initialize and add the char device.*/ 
	cdev_init(&my_cdev->cdev, &my_cdev_fops);
	ret = cdev_add(&my_cdev->cdev, dev_num, MAX_DEV_CNT);
	if (ret < 0){
		printk("add the device failed.\n");
		goto cdev_add_failed;
	}

	/*4. create a device class and export the device's info into userspace.*/ 
	my_cdev_class = class_create(THIS_MODULE, "mycdev");	//create a class under /sys/class/ dir.

	if (IS_ERR(my_cdev_class)) {
		printk(KERN_ERR "Error creating mycdev class.\n");
		cdev_del(&my_cdev->cdev);
		goto cdev_add_failed;
	}
	device_create(my_cdev_class, NULL, dev_num, NULL, "my_cdev");


	printk("my chardev initialized.\n");
	return ret;

cdev_add_failed:
	unregister_chrdev_region(dev_num, MAX_DEV_CNT);	
err:
	kfree(my_cdev);

	return ret;
}

void __exit my_cdev_exit(void)
{
	device_destroy(my_cdev_class, dev_num);
	class_destroy(my_cdev_class);
	cdev_del(&my_cdev->cdev);
	unregister_chrdev_region(dev_num, MAX_DEV_CNT);	
	printk("my chardev removed.\n");

}

module_init(my_cdev_init);
module_exit(my_cdev_exit);

MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("2247720389@qq.com");
MODULE_DESCRIPTION("A char device driver frame");
  

1.2 Makefile

obj-m += my_cdev.o
KERN_DIR := /lib/modules/$(shell uname -r)/build

all:
	$(MAKE) -C $(KERN_DIR) M=`pwd` modules

.PHONY: clean
clean:
	make -C $(KERN_DIR) M=`pwd` clean

2. 驱动测试程序

2.1 测试文件: my_cdev_test.c

/* a test for my_cdev driver */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

#define W_SIZE 10
#define R_SIZE 8

int main(int argc, char **argv)
{

	int fd;
	unsigned char w_buffer[100] = {'h','e','l','l','o','w','o','r','l','d'};
	unsigned char r_buffer[100] = {0};
	int i;
	int ret;

	fd = open("/dev/my_cdev", O_RDWR);
	if (fd < 0)
		printf("can't open the device!\n");
	printf("***hello world*** fd = %d\n", fd);

	ret = write(fd, w_buffer, W_SIZE);
	if (ret == -1)
		printf("sorry, write failed.\n");
	
	ret = read(fd, r_buffer, R_SIZE);
	if (ret == -1)
		printf("sorry, read failed.\n");
	for (i = 0; i < R_SIZE; i++)
		printf("r_buffer[%d] = %c\n", i, r_buffer[i]);

	close(fd);
	
	return 0;
	
}

2.2 Makefile

all: 
	gcc -o my_cdev_test my_cdev_test.c

clean:
	rm -rf *.o
	rm -rf my_cdev_test

3. 测试结果(全程打开dmesg)

3.0 全程打开dmesg

# dmesg -w

3.1 加载driver

# insmod my_cdev.ko
# cat /proc/devices

在这里插入图片描述

# ls -a /sys/class/mycdev/my_cdev/

在这里插入图片描述

3.2 运行test程序

# ./my_cdev_test

在这里插入图片描述

3.3 卸载driver

# rmmod my_cdev
driver卸载后,再次查看/proc/devices和/sys/class,则无my_cdev信息。

4. 附dmesg

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

字符设备驱动框架及测试程序 的相关文章

随机推荐

  • Caffeine缓存的使用

    1 springboot集成Caffeine
  • KeePass搭建一个私人密码库

    文章作者 GoodBoyboy 文章链接 https blog goodboyboy top posts 2546190081 html 版权声明 本博客所有文章除特别声明外 均采用 CC BY NC SA 4 0 许可协议 转载请注明来自
  • 线程(进程)的同步与互斥实例

    1 有一个队列 线程1负责从网络接收分组 并将收到的分组放到队列尾 然后再次从网络中接收下一个到达的分组 并进行同样的队列操作 线程2从此队列头中取出一个分组进行处理 处理完毕后 再次从队列中取出一个分组进行处理 处理完毕后再次从队列头取出
  • [转]Python实现多功能音乐播放器

    前言 就是用Python做一个简易的音乐播放器 废话不多说 咱们直接开干 当然 今天做这个肯定不是最简单的 最简单的音乐播放器 9行代码足以 import time import pygame file r 歌曲路径 pygame mixe
  • torch.hub.load()解析,如何加载本地权重

    用yolov5训练了一个权重 项目只能部署在本地 官方文档 torch hub load repo or dir model args source github force reload False verbose True skip v
  • Python 第一阶段

    第一章 安装 1 1 开发环境 官网 https www python org稳定版 Stable Releases检验 cmd 命令 python version 1 2 开发工具 PyCharm官网 https www jetbrain
  • (Struts2学习篇) Struts2配置文件之 struts-default.xml

    对struts default xml的一些注释
  • zmq+protobuf 的坑点难点

    zmq protobuf 的坑点难点 之前项目要用到zmq protobuf的方式传递数据 软件采用前后端分离的方式开发 其中前端是异地同事用python开发的 后端是我们这边用C 开发的 1 中间有遇到问题是前后端传送zmq信息时 发现字
  • DRM驱动(七)之atomic_commit

    上节已经把应用的参数check了一遍 这次就可以把对应的参数配置到硬件里进行刷图操作了 int drm atomic commit struct drm atomic state state struct drm mode config c
  • Qt 控制台运行无法弹出小黑框

    Qt Console Application Qt 主要是GUI界面的设计 但在学习的时候控制台运行显得更加方便一些 小编在第一次新建控制台运行的时候 点击运行没有弹出小黑框 解决方法 主要是因为没有执行qmake 就需要在Qt的pro文件
  • RocketMQ 用法详解,你学会了吗?

    大家好 我是指北君 消息中间件是我们工作中使用最频繁的一类中间件 它具有低耦合 可靠投递 广播 流量控制 最终一致性等一系列功能 成为异步RPC的主要手段之一 当今市面上有很多主流的消息中间件 如老牌的ActiveMQ RabbitMQ 炙
  • C++的使用小教程2——基于范围的for循环

    C 的使用小教程2 基于范围的for循环 1 常规的for循环 2 基于范围的for循环 在学习了python之后 一直觉得python的语法很方便 特别是python中遍历数组的方式 无需配置特别的参数就可以完全遍历 C语言是很难实现的
  • zookeeper和Eureka

    简介 Zookeeper 主要为大型分布式计算提供开源的分布式配置服务 同步服务和命名注册 曾经是Hadoop项目中的一个子项目 用来控制集群中的数据 目前已经升级为独立的顶级项目 很多场景下也用它作为Service发现服务解决方案 Eur
  • freessl生成免费ssl

    利用freessl免费生成https https freessl cn acme deploy 官网说明地址 https blog freessl cn acme quick start 下载acme工具包 curl https get a
  • 拟合是什么意思

    在机器学习和统计学中 拟合 Fitting 是指使用模型来适应数据的过程 它表示通过对模型参数进行调整 使模型能够最好地拟合给定的数据集 当我们说一个模型 拟合 了数据时 意味着该模型通过学习数据的模式和结构 能够产生与实际观测数据相匹配的
  • Spring Cloud Alibaba之配置管理组件 - Nacos

    TOC 使用Nacos管理配置 为什么要需要配置管理 在微服务架构中 每个微服务都有可能会存在多个实例 为了保证同一微服务不同实例的配置文件内容一致 我们就需要有一个服务可以对微服务项目的配置文件进行统一管理 通常我们将其称之为统一配置管理
  • Qt系列文章之 QDataStream

    上一篇文章介绍了如何使用QFileDialog来选择文件路径并读入项目界面 本文介绍如何使用Qt的序列化技术来将用户定义的一些变量保存到文件 用到的主要模块是QDataStream 对象序列化 QDataStream Qt提供了两个关键的二
  • 【STM32】HAL库实现定时器多通道输入捕获频率

    原理说明 捕获信号的频率其实有很多中实现方式 外部中断 输入捕获 使用外部时钟计数器等 对STM32有一定了解的朋友们在测量频率的问题上往往都会想到利用输入捕获 输入捕获的方式在中低频率段 1HZ 200KHZ 的测量还是比较准确的 在高频
  • Qt如何画圆或椭圆 QPainter

    paint gt drawEllipse 20 20 210 160 将圆或椭圆框在一个矩形中 括号中的四个参数指定矩形的参数 左上角坐标 20 20 宽高为 210 160 更新 这里的paint类型为QPainter 画椭圆 该椭圆内切
  • 字符设备驱动框架及测试程序

    字符设备驱动框架及测试程序 1 驱动框架 1 1 驱动文件 my cdev c 1 2 Makefile 2 驱动测试程序 2 1 测试文件 my cdev test c 2 2 Makefile 3 测试结果 全程打开dmesg 3 0