嵌入式Linux 开发经验:编写用户态应用程序 ioctl 控制 misc 设备
创始人
2024-03-17 05:02:42
0

参考文章

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 
#include 
#include #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 
#include 
#include 
#include #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  Makefilezhangsz@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,把内核态的数据 复制到 用户态

相关内容

热门资讯

专业文章丨跨境模具纠纷高效和解... 【珠海律师、珠海法律咨询、珠海律师事务所、京师律所、京师珠海律所】 (本文转载自北京市京师律师事务所...
全总等三部门联合发布2025年... 新华社北京12月25日电(记者樊曦、冯家顺)记者12月25日从全国总工会了解到,全国总工会与最高人民...
对“问题法规”要及时纠正 从事网约车经营要求车辆购置总价不得低于12万元、残疾人机动轮椅车登记需有本市常住户籍……日前提请全国...
专业文章丨相对不起诉:一起交通... 【珠海律师、珠海法律咨询、珠海律师事务所、京师律所、京师珠海律所】 (本文转载自北京市京师郑州律师事...
巡回审判进商场 两起纠纷就地解 央广网长春12月26日消息(记者舒震)“真没想到法官能把法庭‘搬’到商场里来,更没想到当庭就把我们的...
专业文章丨赠与车辆未交付 可以... 【珠海律师、珠海法律咨询、珠海律师事务所、京师律所、京师珠海律所】 (本文转载自北京市京师合肥律师事...
广西重拳打击制售假劣农资犯罪 ... 中新网南宁12月26日电 (韦小婷)广西壮族自治区农业农村厅25日介绍,今年开春以来,该厅在广西开展...
政策面前瞻:多元工具下的宽松红... 我们来聊聊今年政策的整体变化。感受特别明显的是,今年政策利率的锚发生了一些切换,包括货币政策投放工具...
政策力挺消费!这波逢低布局机会... 板块轮动已经成为A股常态。被看作“长坡厚雪”的消费板块,已经走过了一段漫长的调整路,估值也跌回了历史...