2025年Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo若该文为原创文章 转载请注明原文出处 本文章博客地址 https hpzwl blog csdn net article details 红胖子网络科技博文大全 开发技术集合 包含 Qt 实用技术 树莓派 三维 OpenCV OpenGL ffmpeg OSG 单片机 软硬结合等等 持续更新中

大家好,我是讯享网,很高兴认识大家。

红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…

Linux系统移植和驱动开发专栏


前言

  驱动的开发需要先熟悉基本概念类型,本篇讲解linux杂项设备基础,还是基于虚拟机ubuntu去制作驱动,只需要虚拟机就可以尝试编写注册杂项设备的基本流程。


linux三大设备驱动

  • 字符设备:IO的传输过程是以字符为单位的,没有缓冲,比如I2C(SDA、SCL),SPI(MISO、MOSI、SCLK、CS)。
  • 块设备:IO的传输过程是以块为单位的,跟存储相关的都属于块设备,比如tf卡,sd卡。
  • 网络设备:IO的传输以socket套接字来访问的。

杂项设备

  • 杂项设备是属于字符设备,可以自动生成设备节点,设备节点位于/dev/目录下,是设备名称,如/dev/ttyS9等。
  • 主设备号相同,统一为10,次设备号不同,主设备相同可以节省内核资源。
    通过下列指令,可以查看系统杂项设备
cat /proc/misc 

讯享网

  在虚拟机上测试,查看杂项:
  在这里插入图片描述
讯享网

  • 设备号分为主设备号和次设备号,主设备号是唯一的,次设备号不一定唯一。
    通过下列指令,可以查看系统主设备号:
讯享网cat /proc/devices 

  在这里插入图片描述

杂项设备描述结构体

  ubuntu来说,自带的/usr/src下的就是内核的头文件。

cd /usr/src/linux-headers-4.18.0-15 vi include/linux/miscdevice.h 

  定位到之前ubuntu自带的内核头文件下:
  在这里插入图片描述
  在这里插入图片描述

  查看到杂项设备的结构体:

讯享网struct miscdevice { 
    int minor; // 次设备号 const char *name; // 设备节点名称(如/dev/ttyS8,则ttyS是名称) const struct file_operations *fops; // 文件操作集(非常重要) struct list_head list; struct device *parent; struct device *this_device; const struct attribute_group **groups; const char *nodename; umode_t mode; }; 

  (注意:没打注释的,一般不管)

杂项设备文件操作集

cd /usr/src/linux-headers-4.18.0-15 vi include/linux/fs.h 

  搜索到(vi则直接使用“/”):
  在这里插入图片描述

讯享网struct file_operations { 
    struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); int (*iterate_shared) (struct file *, struct dir_context *); __poll_t (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); unsigned long mmap_supported_flags; int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*setfl)(struct file *, unsigned long); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **, void **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU unsigned (*mmap_capabilities)(struct file *); #endif ssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int); int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64); ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *, u64); } __randomize_layout; 

驱动编写空模板准备

  首先复制之前的hello world的驱动,改个名字为:registerMiscDev:

cd ~/work/drive cp -arf hellowolrd registerMiscDev 

  在这里插入图片描述

讯享网cd registerMiscDev/ rm *.ko *.o *.order *.symvers 

  这里删除起来麻烦,修改makefile,添加clean:
  在这里插入图片描述

  然后测试一下:
  在这里插入图片描述

  继续修改源码文件名称:

mv helloworld.c registerMiscDev.c 

  修改完如下:
  在这里插入图片描述

  然后修改makefile里面的(obj-m模块名称改下),模板准备好了
  在这里插入图片描述

  下面基于registerMiscDev.c文件进行注册杂项设备,在修改.c文件:
  在这里插入图片描述

讯享网#include <linux/init.h> #include <linux/module.h> static int registerMiscDev_init(void) { 
    // 在内核里面无法使用基础c库printf,需要使用内核库printk printk("Hello, I’m hongPangZi, registerMiscDev_init\n"); return 0; } static void registerMiscDev_exit(void) { 
    printk("bye-bye!!!\n"); } MODULE_LICENSE("GPL"); module_init(registerMiscDev_init); module_exit(registerMiscDev_exit); 

杂项设备注册流程Demo

步骤一:填充miscdevice结构体

#include <linux/miscdevice.h> #include <linux/fs.h> 

  在这里插入图片描述

  然后填充杂项设备结构体:
  在这里插入图片描述

  (注意:开始为“.”,结束为“,”,最后一行习惯加“,”了,这样可以全部统一复制啥的,省的加没加的)

讯享网struct miscdevice misc_dev { 
    .minor = MISC_DYNAMIC_MINRO, // 这个宏是动态分配次设备号,避免冲突 .name = "register_hongPangZi_misc, // 设备节点名称 .fops = misc_fops, // 这个变量记住,自己起的,步骤二使用 } 

  在这里插入图片描述

步骤二:填充file_operations结构体

  在编写驱动的时候,代码中填充文件操作结构体。
  在这里插入图片描述

struct file_operations misc_fops { 
    .owner = THIS_MODULE } 

  在这里插入图片描述

步骤三:注册杂项设备并生成设备节点

  注册到内核:

讯享网static int registerMiscDev_init(void) { 
    // 在内核里面无法使用基础c库printf,需要使用内核库printk printk("Hello, I’m hongPangZi, registerMiscDev_init\n"); int ret = 0; ret = misc_register(misc_dev); if(ret < 0) { 
    printk("Failed to misc_register(misc_dev)\n"); return -1; } return 0; } 

  在这里插入图片描述

  有注册就有注销:

static int registerMiscDev_init(void) { 
    // 在内核里面无法使用基础c库printf,需要使用内核库printk printk("Hello, I’m hongPangZi, registerMiscDev_init\n"); int ret = 0; ret = misc_register(&misc_dev); if(ret < 0) { 
    printk("Failed to misc_register(misc_dev)\n"); return -1; } return 0; } 

  在这里插入图片描述

  完整的文件源码:

讯享网#include <linux/init.h> #include <linux/module.h> #include <linux/miscdevice.h> #include <linux/fs.h> struct file_operations misc_fops = { 
    .owner = THIS_MODULE, }; struct miscdevice misc_dev = { 
    .minor = MISC_DYNAMIC_MINOR, // 这个宏是动态分配次设备号,避免冲突 .name = "register_hongPangZi_misc", // 设备节点名称 .fops = &misc_fops, // 这个变量记住,自己起的,步骤二使用 }; static int registerMiscDev_init(void) { 
    // 在内核里面无法使用基础c库printf,需要使用内核库printk printk("Hello, I’m hongPangZi, registerMiscDev_init\n"); int ret = 0; ret = misc_register(&misc_dev); if(ret < 0) { 
    printk("Failed to misc_register(&misc_dev)\n"); return -1; } return 0; } static void registerMiscDev_exit(void) { 
    misc_deregister(&misc_dev); printk("bye-bye!!!\n"); } MODULE_LICENSE("GPL"); module_init(registerMiscDev_init); module_exit(registerMiscDev_exit); 

步骤四:编译make

make 

  直接在驱动工程目录编译:
  在这里插入图片描述

  下面这个警告,实际上定义要在任何使用函数之前:
  在这里插入图片描述

  修改下:
  在这里插入图片描述

  在这里插入图片描述

  编译成功
  在这里插入图片描述

步骤五:加载卸载驱动测试

  将驱动拷贝到开发板或者目标系统,然后使用加载指令:

讯享网sudo insmod registerMiscDev.ko 

  会打印入口加载的printk输出。
  在这里插入图片描述

  出现问题可能原因一是内核编译使用的编译器和模块使用的编译器版本不一致。ubuntu中printk终端打入内核日志消息了,可以使用dmesg进行查看:

dmesg 

  在这里插入图片描述

  然后查看是否加入了杂项设备节点:
  在这里插入图片描述

  然后注销:

讯享网sudo rmmod registerMiscDev.ko 

  在这里插入图片描述

  跟随着,结点消失了:
  在这里插入图片描述


入坑

入坑一:编译报错,结构体之后未加分号

问题

  编译错误,结构体后面加分号

解决

  加分号,脑袋有点蒙
  在这里插入图片描述

入坑二:编译错误,文件操作指针问题

问题

  在这里插入图片描述

解决

  这是写错了,是指针,需要加取地址&。



小讯
上一篇 2025-03-07 16:30
下一篇 2025-02-19 22:00

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/54547.html