首页 > 系统相关 >0-ARM Linux驱动开发-字符设备

0-ARM Linux驱动开发-字符设备

时间:2024-11-03 16:51:29浏览次数:3  
标签:字符 KernelPrint return ---- static Linux printk ARM 设备

image

0-ARM Linux驱动开发-字符设备

一、字符设备概述

   Linux 系统中,设备被分为字符设备、块设备和网络设备等。字符设备以字节流的方式进行数据传输,数据的访问是按顺序的,一个字节一个字节地进行读取和写入操作,没有缓冲区。例如,终端(/dev/tty)、鼠标、键盘等设备都是典型的字符设备。

  字符设备通过特殊的设备文件来表示。这些设备文件通常位于/dev​目录下。设备文件有主设备号(major number)和次设备号(minor number)。主设备号用于标识设备驱动程序,内核通过主设备号来查找对应的驱动程序;次设备号用于标识同一类型设备中的不同个体。例如,系统中可能有多个串口设备,它们的主设备号相同(表示使用相同的驱动程序),但次设备号不同,用于区分不同的串口。

image

image

二、字符设备驱动开发

1、驱动文件

//添加头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ide.h>

static char readbuf[100];  // 读缓冲区
static char writebuf[100]; // 写缓冲区
static char message[] = {"This message comes from kernel."};

static int drive_major; //设备号
static struct class *KernelPrint_cls;

//1.5.5 驱动程序的打开,读取,写入,关闭。
static int KernelPrint_open(struct inode *inode, struct file *filp) //打开函数
{
	//本DEMO无需申请资源,此处留白
	printk("-KernelPrint open-\n");
	return 0;
}
static ssize_t KernelPrint_read(struct file *filp, char __user *buf, size_t count, loff_t *fops) //用户读取,内核发送信息
{
	int flag = 0;
	memcpy(readbuf, message, sizeof(message)); //使用memcpy将内核中要发送的内容写入读缓冲区
	flag = copy_to_user(buf, readbuf, count);  //使用copy_to_user函数将读缓冲区的内容发送到用户态
	if (flag == 0)							   //返回0成功,否则失败
	{
		printk("Kernel send data success!\n");
	}
	else
	{
		printk("Kernel send data failed!\n");
	}
	printk("-KernelPrint read-\n");
	return 0;
}
static ssize_t KernelPrint_write(struct file *filp, const char __user *buf, size_t count, loff_t *fops) //用户发送,内核读取信息并打印
{
	int flag = 0;
	flag = copy_from_user(writebuf, buf, count); //使用copy_from_user读取用户态发送过来的数据
	if (flag == 0)
	{
		printk(KERN_CRIT "Kernel receive data: %s\n", writebuf);
	}
	else
	{
		printk("Kernel receive data failed!\n");
	}
	printk("-KernelPrint write-\n");
	return 0;
}
static int KernelPrint_release(struct inode *inode, struct file *filp) //释放设备
{
	//由于open函数并没有占用什么资源,因此无需释放
	printk("-KernelPrint release-\n");
	return 0;
}

//1.5.1 驱动文件描述集合
static struct file_operations drive_fops = {
	.owner = THIS_MODULE,
	.open = KernelPrint_open,
	.read = KernelPrint_read,
	.write = KernelPrint_write,
	.release = KernelPrint_release,
};

//1.5.2 装载入口函数
static __init int KernelPrint_init(void)
{
	printk("-------^v^-------\n");
	printk("-KernelPrint init-\n");

	//1.5.3 设备的申请
	//申请主设备号
	//参数1----需要的主设备号,>0静态分配, ==0自动分配
	//参数2----设备的描述 信息,体现在cat /proc/devices, 一般自定义
	//参数3----文件描述集合
	//返回值,小于0报错
	drive_major = register_chrdev(0, "KernelPrint", &drive_fops);
	if (drive_major < 0) //判断是否申请成功
	{
		printk("register chrdev faile!\n");
		return drive_major;
	}
	else
	{
		printk("register chrdev ok!\n");
	}


	//1.5.4 
	//自动创建设备节点
	//创建设备的类别
	//参数1----设备的拥有者,当前模块,直接填THIS_MODULE
	//参数2----设备类别的名字,自定义
	//返回值:类别结构体指针,其实就是分配了一个结构体空间
	KernelPrint_cls = class_create(THIS_MODULE, "KernelPrint_class");
	printk("class create ok!\n");

	//创建设备
	//参数1----设备对应的类别
	//参数2----当前设备的父类,直接填NULL
	//参数3----设备节点关联的设备号
	//参数4----私有数据直接填NULL
	//参数5----设备节点的名字
	device_create(KernelPrint_cls, NULL, MKDEV(drive_major, 0), NULL, "KernelPrint_%d", 0);
	printk("device create ok!\n");

	return 0;
}

//1.5.2 卸载入口函数
static __exit void KernelPrint_exit(void)
{
	//1.5.3 设备的注销
	device_destroy(KernelPrint_cls, MKDEV(drive_major, 0)); //删除设备
	class_destroy(KernelPrint_cls);							//删除类
	unregister_chrdev(drive_major, "KernelPrint");			//注销主设备号
	printk("-------^v^-------\n");
	printk("-KernelPrint exit-\n");
}

//申明装载入口函数和卸载入口函数
module_init(KernelPrint_init);
module_exit(KernelPrint_exit);

//添加各类信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Popeye");

2、应用文件

#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];

	filename = argv[1];

	fd = open(filename, O_RDWR); //打开设备
	if (fd < 0)
	{
		printf("Can't open file %s\n", filename);
		return -1;
	}

	switch (*argv[2]) //对操作数进行解析
	{
	case 'r':
		if (argc != 3) //进行鲁棒性检查
		{
			printf("Unknow operation, use the formate: ./APPNAME /dev/DRIVENAME r to read date from kernel.\n");
			return -1;
		}
		retvalue = read(fd, readbuf, 100);
		if (retvalue < 0) //检查是否读取成功
		{
			printf("Read file %s failed!\n", filename);
		}
		else
		{
			printf("User receive data: %s\n", readbuf);
		}
		break;
	case 'w':
		if (argc != 4) //进行鲁棒性检查
		{
			printf("Unknow operation, use the formate: ./APPNAME /dev/DRIVENAME w \"USERDATE\" to write date to kernel.\n");
			return -2;
		}
		memcpy(writebuf, argv[3], strlen(argv[3])); //将内容拷贝到缓冲区
		retvalue = write(fd, writebuf, 50);			//写数据
		if (retvalue < 0)
		{
			printf("Write file %s failed!\n", filename);
		}
		else
		{
			printf("Write file success!\n");
		}
		break;
	default:
		printf("Unknow Operation: %d\n", *argv[2]);
		break;
	}

	retvalue = close(fd); //关闭设备
	if (retvalue < 0)
	{
		printf("Can't close file %s\n", filename);
		return -1;
	}

	return 0;
}

三、上板验证

  安装驱动insmod kernel_print.ko​,查看驱动安装是否成功ls /dev/​,kernel_print内核打印测试./kernel_print_app /dev/KernelPrint_0 w "Popeye"

image

image

  ‍

标签:字符,KernelPrint,return,----,static,Linux,printk,ARM,设备
From: https://www.cnblogs.com/popepy/p/18523599/0rm-linux-drive-developmentcharacter-equipment-z

相关文章

  • 0-ARM Linux驱动开发-字符设备
    0-ARMLinux驱动开发-字符设备一、字符设备概述Linux系统中,设备被分为字符设备、块设备和网络设备等。字符设备以字节流的方式进行数据传输,数据的访问是按顺序的,一个字节一个字节地进行读取和写入操作,没有缓冲区。例如,终端(/dev/tty)、鼠标、键盘等设备都是典型的字符设备......
  • JAVA--java的转义字符(6个常用的 DAY 4*拌面版
    前言:今天,我们来了解一下java语言中常用的6个转义字符(最后附有完整代码)------------------------------分割线--------------------------------------OS:记得每次输入新代码要保存并重新编译一下哦~,(而且是要重新打开控制台编译的)源代码如下: 1.\t:一个制表位,实现对齐的功......
  • python小白入手之——字符串、集合
    数据容器的视角学习字符串:字符串是字符的容器字符串支持正向下标索引和反向下标索引同元组一样,字符串也是一个无法修改的数据容器1.index()2.字符串的替换:语法:字符串.replace(字符串1,字符串2),功能:将字符串1中的全部内容更换成字符串2,但要注意,并不是修改字符串本身,而是得到了......
  • riscv64-unknown-linux-gnu-strip 的功能
    riscv64-unknown-linux-gnu-strip 是针对RISC-V架构的GNUstrip工具的一个版本,用于处理RISC-V架构下的可执行文件、共享库文件以及目标文件。strip 命令的主要作用是去除这些文件中的符号表和调试信息。具体来说,strip 命令的用处包括以下几个方面:减小文件大小:通过去......
  • Linux常见指令大全(必要+知识点)
    目录 ls指令☑️在Windows中会自动显示当前目录当中的所有子目录与文件,但是在Linux中要用到ls指令。语法:ls[选项][目录或文件] 功能:对于目录,该命令列出该目录下的所有子目录与文件。对于文件,将列出文件名以及其他信息。 目录下所有文件(蓝色为目录f1,文件为t1) 常用选......
  • 现代谱分析方法——ARMA过程详解
    现代谱分析方法——ARMA(AutoregressiveMovingAverageprocess)过程详解目录简介ARMA过程的基本概念ARMA的定义AR与MA的区别ARMA过程的数学模型自回归模型(AR模型)移动平均模型(MA模型)ARMA模型的参数估计最小二乘法最大似然估计ARMA模型的性质平稳性白噪声ARMA模型的......
  • HarmonyOS:使用本地真机运行应用/服务
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤博客园地址:为敢技术(https://www.cnblogs.com/strengthen/ )➤GitHub地址:https://github.com/strengthen➤原文地址:https://www.cnblogs.com/strengthen/p/18523167➤如果链接不是为敢技术的博客园地址,则可能......
  • RT-Thread、RT-Linux与Zephyr实时性对比
    在嵌入式系统领域,实时性是一个至关重要的指标,它直接关系到系统对外部事件的响应速度和处理效率。在众多实时操作系统(RTOS)中,RT-Thread、RT-Linux和Zephyr因其各自的特点和优势,成为业界广泛关注的焦点。本文将从实时性角度出发,对这三款RTOS进行详细对比。 一、引言随着物联网、......
  • Linux中 文字界面、X Window系统以及图形界面的关系
    Linux中文字界面、XWindow系统以及图形界面的关系在Linux系统中,文字界面(TTY)、XWindow系统(X11)以及图形界面(GUI)之间有明确的关系。下面分别解释它们的功能和相互之间的联系:1.文字界面(TTY)TTY(Teletypewriter)是Linux系统中的文本控制台。Linux系统默认提供了多个TTY,通常通过Ctr......