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 命令控制
Linux 的 misc 设备,用户态的应用程序,是通过文件 file 的方式进行控制的, file 有个 ioctl 的函数接口,可以传入用户自定义的命令,与内核的 misc 设备进行交互操作
Linux 的 ioctl 命令 控制实现其实不复杂,有一个命令列表,收到什么命令,进行什么操作,就像是【空调】控制一样,除了点击【电源按钮】进行开关外,调节温度、更改模式,都是下发各种指令,空调接收
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__);
}
驱动注册后,用户不调用,设备就不会工作,用户不下发 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;
}
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4

zhangsz@zhangsz:~/linux/apps/led_control$ make
arm-linux-gnueabihf-gcc led_control.c -o led_control
/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
/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
/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 #
本篇接着上一篇,讲了一下 ioctl 的命令控制,用户态的应用程序,通过 ioctl 下发各个 控制命令,如设置、获取状态等,Linux 内核 misc 设备接收 ioctl 命令并解析执行
由于内核态与用户态内存的隔离,所以在参数的传递时,需要使用 copy_from_user 把内核态的传参复制到内核态,通过 copy_to_user,把内核态的数据 复制到 用户态
上一篇:JVM学习-- JVM调优