ioctl操作实现

 ioctl,避免使用三个全局变量,因此写进一个结构体里面

 ioctl对文件属性进行操作

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

#define BUF_LEN 100
int major = 11;//主设备号
int minor =0;//次设备号
int mychar_num = 1;//次设备数量
//ioctrl
struct mychar_dev
{
struct cdev mydev;
char mydev_buf[BUF_LEN];
int curlen;//100个字节中已经存有的数据
};

struct mychar_dev gmydev;

int mychar_open(struct inode *pnode,struct file *pfile)
{//inode * pnode 中有一个成员 i_rdev存放这mydev的地址,现在用struct成员的地址求出结构体的地址
     //求出gmydev的地址
	pfile->private_data = (void *)container_of(pnode->i_cdev,struct mychar_dev,mydev);
	printk("mychar_open is called\n");
	return 0;
}

int mychar_close(struct inode *pnode,struct file *pfile)
{
	printk("mychar_close is called\n");
	return 0;
}
ssize_t mychar_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{//内核空间mydev_buf  向   用户空间puser  进行copy
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	//定义一个指针pmydev
 int size = 0;
 int ret = 0;
 if(count >pmydev->curlen)//如果期望读取的数据大小大于了原本数据大小
 {
   size = pmydev->curlen;//读取的数据为被读取数据的大小
 }
 else
 {
    size = count;//被读取的数据大小为期望读取的数据大小

 }
ret = copy_to_user(puser,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);
  pmydev->curlen =pmydev-> curlen -size;//被读取后剩余这么多字节
  return size;

}

ssize_t mychar_write(struct file *pfile,const char __user *puser,size_t count,loff_t *p_pos)
{//用户空间puser  向 内核空间mydev_buf  copy
	int ret=0;
	int size = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
    if(count>BUF_LEN - pmydev->curlen)//如果期望写入的数据大小大于100个字节剩余的空间
	{
		size = BUF_LEN - pmydev->curlen;
	}
	else 
	{
		size = count;
	}
	ret = copy_from_user(pmydev->mydev_buf,puser,size);
	//将用户空间puser中 size大小的数据写入到内核空间mydev_buf为始的地址中去
	if(ret)
	{
		printk("copy_from_user is failed\n");
		return -1;
	}
	pmydev->curlen = pmydev->curlen +size;
	//mydev_buf中存在的数据大小
	return size;

}
struct file_operations myops = {

	.owner = THIS_MODULE,
	.open = mychar_open,
     .release = mychar_close,
	 .read = mychar_read,
	 .write = mychar_write,
};

int __init mychar_init(void)
{
   int ret = 0;
   dev_t devno = MKDEV(major,minor);//组合成完整的设备号
   /*申请设备号*/
   ret = register_chrdev_region(devno,mychar_num,"mychar");
if(ret)//ret非0,表示失败
{
  ret = alloc_chrdev_region(&devno,minor,mychar_num,"mychar");
//此设备号申请后填写到devno地址中去,从minor开始申请mychar_num个
    if(ret)
	{
		printk("get devno failed\n");
		return -1;
	}
	major = MAJOR(devno);//获取新的设备号,不要遗漏
     //次设备号都是0,所以不用再次提取
}
    //给struct_cdev对象制定操作函数集
	 cdev_init(&gmydev.mydev,&myops);    
    //将struct_cdev对象添加到内核对应的数据结构里
	gmydev.mydev.owner = THIS_MODULE;
    cdev_add(&gmydev.mydev,devno,1);

	return 0;
}

void __exit mychar_exit(void)
{
	dev_t devno = MKDEV(major,minor);
    cdev_del(&gmydev.mydev);

    unregister_chrdev_region(devno,mychar_num);

}


MODULE_LICENSE("GPL");

module_init(mychar_init);
module_exit(mychar_exit);

一、ioctl操作实现

已知成员的地址获得所在结构体变量的地址:container_of(成员地址,结构体类型名,成员在结构体中的名称)

long xxx_ioctl (struct file *filp, unsigned int cmd, unsigned long arg);
功能:对相应设备做指定的控制操作(各种属性的设置获取等等)
参数:
    filp:指向open产生的struct file类型的对象,表示本次ioctl对应的那次open
    cmd:用来表示做的是哪一个操作
    arg:和cmd配合用的参数
返回值:成功为0,失败-1

cmd组成

  1. dir(direction),ioctl 命令访问模式(属性数据传输方向),占据 2 bit,可以为 IOC_NONE、IOC_READ、IOC_WRITE、IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;

  2. type(device type),设备类型,占据 8 bit,在一些文献中翻译为 “幻数” 或者 “魔数”,可以为任意 char 型字符,例如 ‘a’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的设备标识;

  3. nr(number),命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;

  4. size,涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;

#define _IOC(dir,type,nr,size) (((dir)<<_IOC_DIRSHIFT)| \
                               ((type)<<_IOC_TYPESHIFT)| \
                               ((nr)<<_IOC_NRSHIFT)| \
                               ((size)<<_IOC_SIZESHIFT))
/* used to create numbers */
​
// 定义不带参数的 ioctl 命令
#define _IO(type,nr)   _IOC(_IOC_NONE,(type),(nr),0)
​
//定义带读参数的ioctl命令(copy_to_user) size为类型名
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
​
//定义带写参数的 ioctl 命令(copy_from_user) size为类型名
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
​
//定义带读写参数的 ioctl 命令 size为类型名
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
​
/* used to decode ioctl numbers */
#define _IOC_DIR(nr)        (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr)       (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr)     (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr)      (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
#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
int major = 11;//主设备号
int minor =0;//次设备号
int mychar_num = 1;//次设备数量
//ioctrl
struct mychar_dev
{
struct cdev mydev;
char mydev_buf[BUF_LEN];
int curlen;//100个字节中已经存有的数据
};

struct mychar_dev gmydev;

int mychar_open(struct inode *pnode,struct file *pfile)
{//inode * pnode 中有一个成员 i_rdev存放这mydev的地址,现在用struct成员的地址求出结构体的地址
     //求出gmydev的地址
	pfile->private_data = (void *)container_of(pnode->i_cdev,struct mychar_dev,mydev);
	printk("mychar_open is called\n");
	return 0;
}

int mychar_close(struct inode *pnode,struct file *pfile)
{
	printk("mychar_close is called\n");
	return 0;
}
ssize_t mychar_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{//内核空间mydev_buf  向   用户空间puser  进行copy
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
	//定义一个指针pmydev
 int size = 0;
 int ret = 0;
 if(count >pmydev->curlen)//如果期望读取的数据大小大于了原本数据大小
 {
   size = pmydev->curlen;//读取的数据为被读取数据的大小
 }
 else
 {
    size = count;//被读取的数据大小为期望读取的数据大小

 }
ret = copy_to_user(puser,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);
  pmydev->curlen =pmydev-> curlen -size;//被读取后剩余这么多字节
  return size;

}

ssize_t mychar_write(struct file *pfile,const char __user *puser,size_t count,loff_t *p_pos)
{//用户空间puser  向 内核空间mydev_buf  copy
	int ret=0;
	int size = 0;
	struct mychar_dev *pmydev = (struct mychar_dev *)pfile->private_data;
    if(count>BUF_LEN - pmydev->curlen)//如果期望写入的数据大小大于100个字节剩余的空间
	{
		size = BUF_LEN - pmydev->curlen;
	}
	else 
	{
		size = count;
	}
	ret = copy_from_user(pmydev->mydev_buf,puser,size);
	//将用户空间puser中 size大小的数据写入到内核空间mydev_buf为始的地址中去
	if(ret)
	{
		printk("copy_from_user is failed\n");
		return -1;
	}
	pmydev->curlen = pmydev->curlen +size;
	//mydev_buf中存在的数据大小
	return size;

}
//实现ioctl
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:
		//把最大值copy到用户空间去
		ret = copy_to_user(pret,&maxlen,sizeof(int));
		if(ret)
		{
           printk("copy_to_user maxlen failed\n ");
			return -1;
		}
		break;
	case MYCHAR_IOCTL_GET_CURLEN:
		//把当前值copy到用户空间去
		ret = copy_to_user(pret,&pmydev->curlen,sizeof(int));
		if(ret)
		{
           printk("copy_to_user curlen failed\n ");
			return -1;
		}
		break;
	default:
		printk("the cmd is unknow\n");
		return -1;
   }
   return 0;
}
struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = mychar_open,
     .release = mychar_close,
	 .read = mychar_read,
	 .write = mychar_write,
	 .unlocked_ioctl = mychar_ioctl,
};

int __init mychar_init(void)
{
   int ret = 0;
   dev_t devno = MKDEV(major,minor);//组合成完整的设备号
   /*申请设备号*/
   ret = register_chrdev_region(devno,mychar_num,"mychar");
if(ret)//ret非0,表示失败
{
  ret = alloc_chrdev_region(&devno,minor,mychar_num,"mychar");
//此设备号申请后填写到devno地址中去,从minor开始申请mychar_num个
    if(ret)
	{
		printk("get devno failed\n");
		return -1;
	}
	major = MAJOR(devno);//获取新的设备号,不要遗漏
     //次设备号都是0,所以不用再次提取
}
    //给struct_cdev对象制定操作函数集
	 cdev_init(&gmydev.mydev,&myops);    
    //将struct_cdev对象添加到内核对应的数据结构里
	gmydev.mydev.owner = THIS_MODULE;
    cdev_add(&gmydev.mydev,devno,1);

	return 0;
}

void __exit mychar_exit(void)
{
	dev_t devno = MKDEV(major,minor);
    cdev_del(&gmydev.mydev);

    unregister_chrdev_region(devno,mychar_num);

}


MODULE_LICENSE("GPL");

module_init(mychar_init);
module_exit(mychar_exit);

二、printk

//日志级别
#define KERN_EMERG  "<0>"   /* system is unusable           */
#define KERN_ALERT  "<1>"   /* action must be taken immediately */
#define KERN_CRIT   "<2>"   /* critical conditions          */
#define KERN_ERR    "<3>"   /* error conditions         */
​
#define KERN_WARNING    "<4>"   /* warning conditions           */
​
#define KERN_NOTICE "<5>"   /* normal but significant condition */
#define KERN_INFO   "<6>"   /* informational            */
#define KERN_DEBUG  "<7>"   /* debug-level messages         */
​
用法:printk(KERN_INFO"....",....)
    
    printk(KERN_INFO"Hello World"); =====> printk("<6>""Hello World") ====> printk("<6>Hello World")
  

dmesg --level=emerg,alert,crit,err,warn,notice,info,debug

#define HELLO_DEBUG
#undef PDEBUG
#ifdef HELLO_DEBUG
#define PDEBUG(fmt, args...) printk(KERN_DEBUG fmt, ##args)
#else
#define PDEBUG(fmt, args...)
#endif
​

三、多个次设备的支持

每一个具体设备(次设备不一样的设备),必须有一个struct cdev来代表它

cdev_init

cdev.owner赋值

cdev_add

以上三个操作对每个具体设备都要进行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/328330.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS之sqlite数据库的使用

从API Version 9开始&#xff0c;鸿蒙开发中sqlite使用新接口ohos.data.relationalStore 但是 relationalStore在 getRdbStore操作时&#xff0c;在预览模式运行或者远程模拟器运行都会报错&#xff0c;导致无法使用。查了一圈说只有在真机上可以正常使用&#xff0c;因此这里…

【SpringBoot框架篇】35.kafka环境搭建和收发消息

kafka环境搭建 kafka依赖java环境,如果没有则需要安装jdk yum install java-1.8.0-openjdk* -y1.下载安装kafka kafka3.0版本后默认自带了zookeeper&#xff0c;3.0之前的版本需要单独再安装zookeeper,我使用的最新的3.6.1版本。 cd /usr/local wget https://dlcdn.apache.…

无监督去噪的一个变迁(1)——N2N→N2V→HQ-SSL

目录 1. 前沿2. N2N3. N2V——盲点网络&#xff08;BSNs&#xff0c;Blind Spot Networks&#xff09;开创者3.1. N2V实际是如何训练的&#xff1f; 4. HQ-SSL——认为N2V效率不够高4.1. HQ-SSL的理论架构4.1.1. 对卷积的改进4.1.2. 对下采样的改进4.1.3. 比N2V好在哪&#xff…

计算机毕业设计 基于Java的美食信息推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

ARM day2、day3 汇编

一、汇编学习&#xff1a;可以向上理解软件、向下感知硬件 二、符号&#xff08;注释&#xff09; 注释#注释&#xff08;放在行首表示注释一行&#xff09;/* */注释#数字立即数&#xff1a;一种标号&#xff08;比如main: loop:&#xff09;.text .end换行…

spingboot 集成identityserver4身份验证

一、新建项目&#xff1a;com.saas.swaggerdemo 详情见&#xff1a;spring-boot2.7.8添加swagger-CSDN博客 在之前项目基础上添加如下依赖 <dependency><groupId>com.nimbusds</groupId><artifactId>nimbus-jose-jwt</artifactId><version&…

java版微信小程序商城 免 费 搭 建 java版直播商城平台规划及常见的营销模式有哪些?电商源码/小程序/三级分销

涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis …

7.5 MySQL对数据的基本操作(❤❤❤)

7.5 MySQL对数据的基本操作 1. 提要2. 数据添加2.1 insert语法2.2 insert 子查询2.3 ignore关键字 3. 数据修改3.1 update语句3.2 update表连接 4. 数据删除4.1 delete语句4.2 delete表连接4.3 快速删除数据表全部数据 1. 提要 2. 数据添加 2.1 insert语法 2.2 insert 子查询 …

Java实现在线编辑预览office文档

文章目录 1 在线编辑1.1 PageOffice简介1.2 前端项目1.2.1 配置1.2.2 页面部分 1.3 后端项目1.3.1 pom.xml1.3.2 添加配置1.3.3 controller 2 在线预览2.1 引言2.2 市面上现有的文件预览服务2.2.1 微软2.2.2 Google Drive查看器2.2.3 阿里云 IMM2.2.4 XDOC 文档预览2.2.5 Offic…

8个Python必备的PyCharm插件

大家好&#xff0c;在PyCharm中浏览插件列表并尝试很多人推荐的插件后&#xff0c;总结了几个瑰宝插件&#xff0c;它们各自以独特的方式帮助开发者快速、简便、愉悦地开发&#xff0c;接下来将逐个介绍它们。 1. Key Promoter X 【下载链接】&#xff1a;https://plugins.je…

Enzo Life Sciences--DNA损伤酶联免疫检测试剂盒DNA damage ELISA kit

——用于肿瘤、细胞凋亡和氧化应激研究中DNA损伤的快速检测 细胞暴露于氧化和环境应激经常导致基因组DNA的分解或氧化&#xff0c;评价基因组DNA完整性或评估氧化DNA存在的测定法经常用作验证凋亡或DNA损伤开始的手段。8-羟基-2 -脱氧鸟苷(8-OHdG)是一种修饰的核苷碱基&#xf…

vba设置excel单元格背景色

vba设置excel单元格背景色位蓝色 Sheet1.Cells(hang, 2).Interior.Color RGB(0, 0, 255) 参考链接 【VBA】给单元格设置背景色_vba 将一行底色置绿色-CSDN博客https://blog.csdn.net/s_h_m114_2/article/details/105787093 参考2 知乎 VBA--单元格的背景色设置 特此…

potplayer在投屏中的使用

视频播放完成之后自动停止 配置/语言/其他->收尾处理->播放完当前后停止 任务栏控制播放 快捷键 Enter 屏幕->全屏 CtrlEnter 屏幕->全屏(拉伸) CtrlShiftEnter 屏幕->全屏(其他显示器) AltEnter 屏幕->全屏 CtrlAltEnter 屏幕->全屏(保持比例) Space…

flink1.15 维表join guava cache和mysql方面优化

优化前 mysql响应慢,导致算子中数据输出追不上输入,导致显示cpu busy:100% 优化后效果两个图对应两个时刻: - - -- 优化前 select l.id,JSON_EXTRACT(r.msg,$$.key1) as msgv (select id,uid from tb1 l where id?) join (select uid,msg from tb2) r on l.uidr.uid;-- 优化…

STC51+TLC2543+ADXL335+proteus

51单片机解析adxl335振动检测蜂鸣器报警课设 通过按键调整振动检测阈值 传感器介绍 TLC2543&#xff1a;12 位精密模数转换器&#xff0c;原理图与引脚功能描述如下所示&#xff1a; 引脚功能1~9、11、12模拟量输入通道10GND电源地13REF-为负基准电压端14REF为正基准电压端…

腾讯云服务器入门教程——从0到1新手必看

腾讯云服务器入门教程包括云服务器CPU内存带宽配置选择&#xff0c;选择云服务器CVM或轻量应用服务器&#xff0c;云服务器创建后重置密码、远程连接、搭建程序环境、部署Web网站应用等&#xff0c;腾讯云服务器网txyfwq.com分享从0到1腾讯云服务器入门教程&#xff1a; 腾讯云…

记录在PyCharm中编辑配置自己的项目环境或路径

我们在复现或则跑项目时往往会遇到“设置参数” # 设置参数 ap argparse.ArgumentParser() ap.add_argument("-i", "--image", requiredTrue, help"path to input image") ap.add_argument("-t", "--template", requiredT…

【已解决】c语言const/指针学习笔记

本博文源于笔者正在复习const在左与在右&#xff0c;指针优先级、a,&a,*a的区别。 1、const在左与在右 int const *p const int *p int * const p int const * const p const int * const p* 在const右边&#xff0c;指向的数据不可以改变&#xff0c;可以改变地址 * 在c…

史上最全的数据科学与艺术

1.背景介绍 数据分析是一种将数据转化为价值的艺术和科学。它涉及到大量的数学、统计、编程、数据库、机器学习等多个领域的知识。数据分析的目的是从数据中提取有用的信息&#xff0c;以便做出明智的决策。 数据分析的艺术体现在数据分析师需要具备丰富的经验和洞察力&#…

SAP 客制化增强查找

相信各位在做日常的实施和运维中&#xff0c;经常会遇到这样一种情况&#xff0c;系统出现了非常规问题&#xff0c; 每次哼哧哼哧看半天源码&#xff0c;各种打watch point, 最后发现是以前别人写的增强导致的&#xff0c; 真的非常浪费时间。 那么我就想&#xff0c;有没有…