嵌入式Linux 开发经验:编写用户态应用程序 ioctl 控制 misc 设备

news2024/9/20 18:38:26

参考文章

VSCode SSH 连接远程ubuntu Linux 主机

ubuntu 20.04 qemu linux6.0.1 开发环境搭建

ubuntu 20.04 qemu linux6.0.1 制作ext4根文件系统

嵌入式Linux 开发经验:platform_driver_register 的使用方法

嵌入式Linux 开发经验:注册一个 misc 设备

嵌入式Linux 开发经验:编写用户态应用程序打开 misc 设备

  • 通过以上的文章,应该可以搭建一个 基于 qemu 的 Linux 设备驱动开发验证平台,开发方法是 VS Code 远程连接 ubuntu 20.04,Linux 内核 在 ubuntu 主机上。

  • 上一篇已经编写了用户态的应用程序,实现了 open close 操作 Linux 内核注册的misc 设备,本篇增加 ioctl 控制操作,熟练掌握 ioctl 的工作原理,可以实现 misc 设备的各种控制操作

测试环境搭建

  • ubuntu 20.04

  • VMware Workstation Pro 16

  • 基于qemu(模拟器),vexpress-a9 平台

  • Linux 6.0.10 (当前最新版本)

  • 编写一个简单的用户态应用程序,ioctl 方式 控制 Linux 内核注册的misc 驱动设备,掌握misc 设备使用方法:ioctl 命令控制

ioctl 命令控制

  • Linux 的 misc 设备,用户态的应用程序,是通过文件 file 的方式进行控制的, file 有个 ioctl 的函数接口,可以传入用户自定义的命令,与内核的 misc 设备进行交互操作

  • Linux 的 ioctl 命令 控制实现其实不复杂,有一个命令列表,收到什么命令,进行什么操作,就像是【空调】控制一样,除了点击【电源按钮】进行开关外,调节温度、更改模式,都是下发各种指令,空调接收

  • ioctl 就是类似【空调】遥控器的各种命令,调节温度、设置各个模式等

测试例程

Linux 内核态 misc 设备首先需要支持 ioctl 命令

  • 编写或者修改 内核态的 设备驱动 led_misc.h,位置放在 linux-6.0.10/drivers/led_control/led_misc.h 目下

  • 通过使用 宏 _IO,组合出多个 ioctl 命令,注意这几个命令只用于 当前的 misc 设备,也就是说,不同的 misc 设备,ioctl 命令可以相同,也可以不同

  • 同一个 misc 设备的命令,不能相同,这里的命令逐个加一,保证不相同

#ifndef __LED_MISC_H__
#define __LED_MISC_H__

#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>

#define LED_CONTROL_IOCTL_MAGIC         'L'
#define LED_CONTROL_IOCTL_ON            _IO(LED_CONTROL_IOCTL_MAGIC, 1)
#define LED_CONTROL_IOCTL_OFF           _IO(LED_CONTROL_IOCTL_MAGIC, 2)
#define LED_CONTROL_IOCTL_GET_STATUS    _IO(LED_CONTROL_IOCTL_MAGIC, 3)

int led_miscdev_init(void);
void led_miscdev_exit(void);

#endif
  • 编写或者修改 内核态的 设备驱动 led_misc.c,位置放在 linux-6.0.10/drivers/led_control/led_misc.c 目下

  • 这里主要是丰富了 ioctl 函数,增加了几个命令执行操作

#include "led_misc.h"

#define LED_MISC_DEVICE_NAME        "led_misc"

struct led_misc_dev
{
	struct miscdevice misc;
};

struct led_misc_dev *led_miscdev;
static int led_status = 0x00;

static int led_misc_open(struct inode *inode, struct file *filp)
{
    printk(KERN_INFO "%s : enter\n", __func__);
    return 0;
}

static int led_misc_close(struct inode *inode, struct file *filp)
{
    printk(KERN_INFO "%s : enter\n", __func__);
    return 0;
}

static int led_misc_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int ret = 0;

    if (filp == NULL)
    {
        printk(KERN_ERR "invalid file!");
        return -EFAULT;
    }
    if (vma == NULL)
    {
        printk(KERN_ERR "invalid vma area");
        return -EFAULT;
    }

    ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
        vma->vm_end - vma->vm_start, vma->vm_page_prot);

    printk(KERN_INFO "%s : ret = %d\n", __func__, ret);

    return ret;
}

static int led_control_ioctl_on(struct file *filp, int __user *led_num)
{
    int ret;
    int led_nums = 0;

    ret = copy_from_user(&led_nums, led_num, sizeof(led_nums));
    printk(KERN_INFO "%s : led_nums = 0x%08x\n", __func__, led_nums);

    if (ret < 0) {
        printk(KERN_ERR "%s : copy_from_user failed!\n", __func__);
        return ret;
    }

    led_status |= led_nums;

    return ret;
}

static int led_control_ioctl_off(struct file *filp, int __user *led_num)
{
    int ret;
    int led_nums = 0;

    ret = copy_from_user(&led_nums, led_num, sizeof(led_nums));
    printk(KERN_INFO "%s : led_nums = 0x%08x\n", __func__, led_nums);

    if (ret < 0) {
        printk(KERN_ERR "%s : copy_from_user failed!\n", __func__);
        return ret;
    }

    led_status &= ~led_nums;
    return ret;
}

static int led_control_ioctl_get_status(struct file *filp, int __user *status)
{
    int ret;
    printk(KERN_INFO "%s : led_status = 0x%08x\n", __func__, led_status);

    ret = copy_to_user(status, &led_status, sizeof(led_status));
    if (ret < 0)
    {
        printk(KERN_ERR "%s : copy_to_user failed!\n", __func__);
    }

    return ret;
}

static long led_misc_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
    int ret = 0;

    printk(KERN_INFO "%s : enter, cmd = 0x%08x\n", __func__, cmd);

    if (filp == NULL)
    {
        printk(KERN_ERR "invalid file!");
        return -EFAULT;
    }

    switch(cmd)
    {
        case LED_CONTROL_IOCTL_ON:
            ret = led_control_ioctl_on(filp, (void __user *)args);
            break;
        case LED_CONTROL_IOCTL_OFF:
            ret = led_control_ioctl_off(filp, (void __user *)args);
            break;
        case LED_CONTROL_IOCTL_GET_STATUS:
            ret = led_control_ioctl_get_status(filp, (void __user *)args);
            break;
        default:
            ret = -EINVAL;
            break;
    }

    return 0;
}

static const struct file_operations led_misc_fops =
{
    .owner  = THIS_MODULE,
    .llseek = no_llseek,
    .unlocked_ioctl = led_misc_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = led_misc_ioctl,
#endif
    .mmap = led_misc_mmap,
    .open = led_misc_open,
    .release = led_misc_close,
};

int led_miscdev_init(void)
{
    int ret;

    led_miscdev = kzalloc(sizeof(*led_miscdev), GFP_KERNEL);
    if (!led_miscdev)
        return -ENOMEM;

    led_miscdev->misc.minor = MISC_DYNAMIC_MINOR;
    led_miscdev->misc.fops = &led_misc_fops;
    led_miscdev->misc.name = LED_MISC_DEVICE_NAME;
    led_miscdev->misc.nodename = LED_MISC_DEVICE_NAME;

    ret = misc_register(&led_miscdev->misc);
    if (ret < 0)
    {
        printk(KERN_INFO "%s : error\n", __func__);
    }
    else
    {
        printk(KERN_INFO "%s : ok\n", __func__);
    }

    return ret;
}

void led_miscdev_exit(void)
{
    misc_deregister(&led_miscdev->misc);
    printk(KERN_INFO "%s : ok\n", __func__);
}

Linux 用户态 应用,需要调用 ioctl 命令

  • 驱动注册后,用户不调用,设备就不会工作,用户不下发 ioctl 控制指令,设备就无法得到控制

  • 修改用户态 led_control.c 程序,位置在:apps/led_control/led_control.c,增加了 ioctl 命令的调用

#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define LED_CONTROL_DEVICE_NAME		"/dev/led_misc"

#define LED_CONTROL_IOCTL_MAGIC         'L'
#define LED_CONTROL_IOCTL_ON            _IO(LED_CONTROL_IOCTL_MAGIC, 1)
#define LED_CONTROL_IOCTL_OFF           _IO(LED_CONTROL_IOCTL_MAGIC, 2)
#define LED_CONTROL_IOCTL_GET_STATUS    _IO(LED_CONTROL_IOCTL_MAGIC, 3)

int led_dev_fd = -1;

int led_dev_init(void)
{
	int fd;
	fd = open(LED_CONTROL_DEVICE_NAME, O_RDWR);
	if (fd < 0)
	{
		printf("%s : open device error\n", __func__);
		return -1;
	}
	led_dev_fd = fd;
	
	printf("%s : ok\n", __func__);
	return 0;
}

int led_dev_deinit(void)
{
	if (close(led_dev_fd) != 0)
	{
		printf("%s : error\n", __func__);
		return -1;
	}
		
	printf("%s : ok\n", __func__);
	return 0;
}

int led_dev_on(int led_num)
{
    if (ioctl(led_dev_fd, LED_CONTROL_IOCTL_ON, &led_num) != 0)
    {
        printf("%s : error, led_num = 0x%08x\n", __func__, led_num);
        return -1;
    }

	printf("%s : ok\n", __func__);
	return 0;
}

int led_dev_off(int led_num)
{
    if (ioctl(led_dev_fd, LED_CONTROL_IOCTL_OFF, &led_num) != 0)
    {
        printf("%s : error, led_num = 0x%08x\n", __func__, led_num);
        return -1;
    }

	printf("%s : ok\n", __func__);
	return 0;
}

int led_dev_get_status(int *status)
{
    if (ioctl(led_dev_fd, LED_CONTROL_IOCTL_GET_STATUS, status) != 0)
    {
        printf("%s : error\n", __func__);
        return -1;
    }

	printf("%s : ok, led_status = 0x%08x\n", __func__, *status);
	return 0;
}

int main(int argc, char **argv)
{
    int led_status = 0;

	printf("%s : enter\n", __func__);
	led_dev_init();
    led_dev_on(0x0f);
    led_dev_get_status(&led_status);
    printf("step 1 : led_status = 0x%08x\n", led_status);
    led_dev_off(0x0f);
    led_dev_get_status(&led_status);
    printf("step 2 : led_status = 0x%08x\n", led_status);
	led_dev_deinit();
	printf("%s : exit\n", __func__);
	
	return 0;
}

编译与运行

内核代码编译

  • 编译方法参考前面的文章,输出 zImage

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4

在这里插入图片描述

用户态程序编译

  • 执行 make,Makefile 编写可以参考前面的文章
zhangsz@zhangsz:~/linux/apps/led_control$ make
arm-linux-gnueabihf-gcc led_control.c -o led_control
  • 生成 led_control,放在 qemu 根文件系统的 /home/root 目录下
/* apps led_control 路径 */
zhangsz@zhangsz:~/linux/apps/led_control$ ls
led_control  led_control.c  Makefile

zhangsz@zhangsz:~/linux/apps/led_control$ cd ../../rootfs/
zhangsz@zhangsz:~/linux/rootfs$ ls
1130  boot_qemu.sh  ext4_rootfs  make_rootfs.sh  rootfs.ext4.img  rootfs_mnt  vexpress-v2p-ca9.dtb  zImage

/* ext4 根文件系统镜像文件,使用 mount 挂载到一个目录 */
zhangsz@zhangsz:~/linux/rootfs$ sudo mount rootfs.ext4.img rootfs_mnt/
[sudo] password for zhangsz: 

/* led_control 复制到 根文件系统镜像文件挂载的目录内 */
zhangsz@zhangsz:~/linux/rootfs$ sudo cp ../apps/led_control/led_control rootfs_mnt/home/root/

/* umount 后,文件就复制进根文件系统镜像文件中了 */
zhangsz@zhangsz:~/linux/rootfs$ sudo umount rootfs_mnt 
  • 运行 qemu,运行 /home/root 目录下 的 led_control 用户程序,查看运行效果
qemu-system-arm -M vexpress-a9 -m 512M -dtb vexpress-v2p-ca9.dtb -kernel zImage -nographic -append "root=/dev/mmcblk0 rw console=ttyAMA0" -sd rootfs.ext4.img
  • 进入 shell ,进入 /home/root 目录
/home/root # ./led_control 
main : enter
led_misc_open : enter
led_dev_init : ok
led_misc_ioctl : enter, cmd = 0x00004c01
led_control_ioctl_on : led_nums = 0x0000000f
led_dev_on : ok
led_misc_ioctl : enter, cmd = 0x00004c03
led_control_ioctl_get_status : led_status = 0x0000000f
led_dev_get_status : ok, led_status = 0x0000000f
step 1 : led_status = 0x0000000f
led_misc_ioctl : enter, cmd = 0x00004c02
led_control_ioctl_off : led_nums = 0x0000000f
led_dev_off : ok
led_misc_ioctl : enter, cmd = 0x00004c03
led_control_ioctl_get_status : led_status = 0x00000000
led_dev_get_status : ok, led_status = 0x00000000
step 2 : led_status = 0x00000000
led_misc_close : enter
led_dev_deinit : ok
main : exit
/home/root # 
  • 通过运行日志,可以发现 misc 设备成功的 open ioctl 控制,读取状态, close 等

小结

  • 本篇接着上一篇,讲了一下 ioctl 的命令控制,用户态的应用程序,通过 ioctl 下发各个 控制命令,如设置、获取状态等,Linux 内核 misc 设备接收 ioctl 命令并解析执行

  • 由于内核态与用户态内存的隔离,所以在参数的传递时,需要使用 copy_from_user 把内核态的传参复制到内核态,通过 copy_to_user,把内核态的数据 复制到 用户态

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里巴巴专场——第322场周赛题解

目录 模拟法&#xff1a;6253.回环句 排序后模拟&#xff1a;6254. 划分技能点相等的团队 BFS&#xff1a;6255. 两个城市间路径的最小分数 BFS&#xff1a;6256. 将节点分成尽可能多的组 模拟法&#xff1a;6253.回环句 这道题直接按照题目的意思暴力模拟即可&#xff1a;…

Ubuntu20.04 安装配置 Ros2

记录一下折磨了一周的ros2配置qaq以及踩的无数坑 第一次按照一个教程安装后&#xff0c;命令行输入sudo apt-update 报错 The repository http://packages.ros.org/ros/ubuntu $(lsb_release-sc) Release does not have a Release file. 卸载后&#xff0c;按照第二个教程安装…

(十) 共享模型之内存【有序性】

JVM 会在不影响正确性的前提下&#xff0c;可以调整语句的执行顺序这种特性称之为『指令重排』&#xff0c;多线程下『指令重排』会影响正确性。为什么要有重排指令这项优化呢&#xff1f;从 CPU 执行指令的原理来理解一下吧 一、原理之指令级并行&#xff08;了解&#xff09;…

[附源码]Python计算机毕业设计Django企业人事管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

如何使用HTML制作个人网站( web期末大作业)

&#x1f4c2;文章目录一、&#x1f468;‍&#x1f393;网站题目二、✍️网站描述三、&#x1f4da;网站介绍四、&#x1f310;网站演示五、⚙️ 网站代码&#x1f9f1;HTML结构代码&#x1f492;CSS样式代码六、&#x1f947; 如何让学习不再盲目七、&#x1f381;更多干货一…

Linux安装mysql

1、 查看是否已经安装 Mysql rpm -qa | grep mysql 如果你查看出来有东西&#xff0c;可以使用下面命令将其删除 rpm -e 文件名 2 、下载官方 Mysql 包 wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm 如果安装有提示&#xff1a;Cannot…

HTML5期末大作业:北京旅游网页设计制作(1页) 简单静态HTML网页作品 我的旅游网页作业成品 学生旅游网站模板

&#x1f468;‍&#x1f393;学生HTML静态网页基础水平制作&#x1f469;‍&#x1f393;&#xff0c;页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码&#xff0c;这是一个不错的旅游网页制作&#xff0c;画面精明&#xff0c;排版整洁&#xff0c;内容…

gin 集成 Swagger

前言 一个好的项目工程&#xff0c;必然离不开一个好的 API 文档&#xff0c;如果要自己编写 API 文档&#xff0c;维护起来比较困难&#xff0c;而且难以保证一致性&#xff0c;因此我们要自动生成在线接口文档。 swaggo swagger 在 java 里面&#xff0c;是一个非常流行的…

后渗透之日志分析实验

目录 一、实验项目名称 二、实验目的 三、实验内容 四、实验环境 五、实验步骤 六、实验结果 七、实验总结 一、实验项目名称 后渗透之日志分析实验 二、实验目的 1.掌握meterpreter进行端口转发的方法 2.掌握网站日志的分析方法 三、实验内容 针对目标网站服务器…

AlphaFold2源码解析(7)--模型之Evoformer

AlphaFold2源码解析(7)–模型之Evoformer 这篇文章我们主要药讲解AlphaFold2的Evoformer的代码细节。 Evoformer Stack 该网络有一个双塔结构&#xff0c;在MSA堆栈中具有轴向的自我注意&#xff1b;在Pair堆栈中具有三角形的乘法更新和三角形的自我注意&#xff1b;以及外积…

Windows多线程编程

一、 实验内容或题目&#xff1a; 以多线程编程的方式完成&#xff1a; 1&#xff09;随机生成一个数组&#xff0c;求其平均值 2&#xff09;随机生成一个数组&#xff0c;求其最大值 3&#xff09;随机生成一个数组&#xff0c;求其最小值 二、 实验目的与要求&#xff1a;…

Kaggle Feedback Prize 3比赛总结:如何高效使用hidden states输出(2)

比赛链接&#xff1a;https://www.kaggle.com/competitions/feedback-prize-english-language-learning 在Kaggle Feedback Prize 3比赛总结&#xff1a;如何高效使用hidden states输出(2)中介绍了针对last layer hidden state的各种pooling的方法。 在利用Transformer类的预…

Vue学习:Hello小案例

使用Vue的目的&#xff1a;构建用户界面&#xff08;需要使用容器 摆放这个界面的内容&#xff09; favicon.ico:1 GET http://127.0.0.1:5500/favicon.ico 404 (Not Found) 没有页签图标 在者服务器中 http://127.0.0.1:5500没有/favicon.ico 强制刷新网页&#xff1a;s…

3大经典分布式存储算法

文章目录1、背景2、算法2.1 分布存储之哈希取余算法2.2 分布式存储之一致性哈希算法2.3 分布式存储之哈希槽算法1、背景 一个经典的面试题目&#xff1a;1&#xff5e;2亿条数据需要缓存&#xff0c;请问如何设计这个方案&#xff1f; 回答&#xff1a;单台单机肯定不可能&…

Musical Christmas Lights——一个圣诞树灯光✨随音乐节奏改变的前端开源项目

文章目录前言视频介绍项目截图项目地址项目源码以上就是本篇文章的全部内容&#xff0c;将你编写好的项目分享给你的朋友们或者那个TA吧&#xff01;制作不易&#xff0c;求个三连&#xff01;❤️ &#x1f4ac; ⭐️前言 今天博主在刷短视频时&#x1f610;&#xff0c;朋友推…

VMware 虚拟机系统 与 win10 共享文件夹问题的解决

环境描述 本地&#xff1a;Win10 64位 VMware Workstation Pro 16 虚拟机&#xff0c;安装的 ubuntu 20.04 文件夹共享 win10 与 虚拟机的 ubuntu 共享文件夹&#xff0c;之前低版本的 VMware &#xff0c;安装 VMware Tools&#xff0c;并且 win10 端设置好工作目录后&…

秒级使网站变灰,不改代码不上线,如何做到?

注意&#xff1a;文本不是讲如何将网站置灰的那个技术点&#xff0c;那个技术点之前汶川地震的时候说过。 本文不讲如何实现技术&#xff0c;而是讲如何在第一时间知道消息后&#xff0c;更快速的实现这个置灰需求的上线。 实现需求不是乐趣&#xff0c;指挥别人去实现需求才…

广域网技术——SR-MPLS隧道保护技术

目录 TI-LFA FRR保护技术 LFA FRR R-LFA FRR TI-LFA FRR Anycast FRR技术 Host-Standby技术 VPN FRR技术 SR-MPLS防微环技术 场景一 SR本地正切防微环 场景二 SR本地回切防微环 场景三 SR远端正切防微环 场景四 SR远端回切防微环 TI-LFA和防微环的对比 TI-LFA FRR…

41. set()函数:将可迭代对象转换为可变集合

41. set()函数&#xff1a;将可迭代对象转换为可变集合 文章目录41. set()函数&#xff1a;将可迭代对象转换为可变集合1. set( )函数的作用2. set( )函数的语法3. set函数创建空集合4. set函数的参数只能是可迭代对象4.1 将字符串转换为集合4.2 set( )函数的参数不能为整数4.3…

MIT 6.S081 Operating System Lecture8 (非常随意的笔记)

系列文章目录 文章目录系列文章目录Page FaultCOPY ON WRITEPage Fault eager allocation 通常&#xff0c;因为应用程序无法非常准确地估计自己要增加的内存有多少&#xff0c;所以通常申请的内存会比真实要使用的内存要多。 在XV6中&#xff0c;sbrk的实现默认是eager alloc…