服务器之家:专注于服务器技术及软件下载分享
分类导航

Linux|Centos|Ubuntu|系统进程|Fedora|注册表|Bios|Solaris|Windows7|Windows10|Windows11|windows server|

服务器之家 - 服务器系统 - Linux - Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

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

2023-11-21 18:24未知服务器之家 Linux

前言   驱动的开发需要先熟悉基本概念类型,本篇讲解linux杂项设备基础,还是基于虚拟机ubuntu去制作驱动,只需要虚拟机就可以尝试编写注册杂项设备的基本流程。 linux三大设备驱动 字符设备 :IO的传输过程是以字符为单位的

前言

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

linux三大设备驱动

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

杂项设备

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

  在虚拟机上测试,查看杂项:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

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

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

杂项设备描述结构体

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

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

  定位到之前ubuntu自带的内核头文件下:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

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

structmiscdevice{
intminor;//次设备号
constchar*name;//设备节点名称(如/dev/ttyS8,则ttyS是名称)
conststructfile_operations*fops;//文件操作集(非常重要)
structlist_headlist;
structdevice*parent;
structdevice*this_device;
conststructattribute_group**groups;
constchar*nodename;
umode_tmode;};

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

杂项设备文件操作集

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

  搜索到(vi则直接使用“/”):
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

structfile_operations{
structmodule*owner;
loff_t(*llseek)(structfile*,loff_t,int);
ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);
ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);
ssize_t(*read_iter)(structkiocb*,structiov_iter*);
ssize_t(*write_iter)(structkiocb*,structiov_iter*);
int(*iterate)(structfile*,structdir_context*);
int(*iterate_shared)(structfile*,structdir_context*);
__poll_t(*poll)(structfile*,structpoll_table_struct*);
long(*unlocked_ioctl)(structfile*,unsignedint,unsignedlong);
long(*compat_ioctl)(structfile*,unsignedint,unsignedlong);
int(*mmap)(structfile*,structvm_area_struct*);
unsignedlongmmap_supported_flags;
int(*open)(structinode*,structfile*);
int(*flush)(structfile*,fl_owner_tid);
int(*release)(structinode*,structfile*);
int(*fsync)(structfile*,loff_t,loff_t,intdatasync);
int(*fasync)(int,structfile*,int);
int(*lock)(structfile*,int,structfile_lock*);
ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);
unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);
int(*check_flags)(int);
int(*setfl)(structfile*,unsignedlong);
int(*flock)(structfile*,int,structfile_lock*);
ssize_t(*splice_write)(structpipe_inode_info*,structfile*,loff_t*,size_t,unsignedint);
ssize_t(*splice_read)(structfile*,loff_t*,structpipe_inode_info*,size_t,unsignedint);
int(*setlease)(structfile*,long,structfile_lock**,void**);
long(*fallocate)(structfile*file,intmode,loff_toffset,
loff_tlen);
void(*show_fdinfo)(structseq_file*m,structfile*f);#ifndefCONFIG_MMU
unsigned(*mmap_capabilities)(structfile*);#endif
ssize_t(*copy_file_range)(structfile*,loff_t,structfile*,
loff_t,size_t,unsignedint);
int(*clone_file_range)(structfile*,loff_t,structfile*,loff_t,
u64);
ssize_t(*dedupe_file_range)(structfile*,u64,u64,structfile*,
u64);}__randomize_layout;

  例如read函数,那么就是打开驱动使用系统read,打开这个设备驱动的句柄,那么久会调用read函数,其他的以此类推,还比较好理解。
  以我们一个registerHelloWorld为例子,来简单说明。

驱动编写空模板准备

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

cd~/work/drivecp-arfhellowolrdregisterMiscDev

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

cdregisterMiscDev/rm*.ko*.o*.order*.symvers

  这里删除起来麻烦,修改makefile,添加clean:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

  然后测试一下:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

  继续修改源码文件名称:

mvhelloworld.cregisterMiscDev.c

  修改完如下:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

  然后修改makefile里面的(obj-m模块名称改下),模板准备好了
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

  下面基于registerMiscDev.c文件进行注册杂项设备,在修改.c文件:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

#include<linux/init.h>#include<linux/module.h>staticintregisterMiscDev_init(void){
//在内核里面无法使用基础c库printf,需要使用内核库printk
printk("Hello,I’mhongPangZi,registerMiscDev_init\n");	
return0;}staticvoidregisterMiscDev_exit(void){
printk("bye-bye!!!\n");}MODULE_LICENSE("GPL");module_init(registerMiscDev_init);module_exit(registerMiscDev_exit);

杂项设备注册流程Demo

步骤一:填充miscdevice结构体

  在编写驱动的时候,代码中填充信息结构体。
  添加头文件miscdevice.h

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

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

  然后填充杂项设备结构体:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

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

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

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

步骤二:填充file_operations结构体

  在编写驱动的时候,代码中填充文件操作结构体。
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

structfile_operationsmisc_fops{
.owner=THIS_MODULE}

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

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

  注册到内核:

staticintregisterMiscDev_init(void){
//在内核里面无法使用基础c库printf,需要使用内核库printk
printk("Hello,I’mhongPangZi,registerMiscDev_init\n");	
intret=0;
ret=misc_register(misc_dev);
if(ret<0)
{
printk("Failedtomisc_register(misc_dev)\n");	
return-1;
}
return0;}

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

  有注册就有注销:

staticintregisterMiscDev_init(void){
//在内核里面无法使用基础c库printf,需要使用内核库printk
printk("Hello,I’mhongPangZi,registerMiscDev_init\n");	
intret=0;
ret=misc_register(&misc_dev);
if(ret<0)
{
printk("Failedtomisc_register(misc_dev)\n");	
return-1;
}
return0;}

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

  完整的文件源码:

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

步骤四:编译make

make

  直接在驱动工程目录编译:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

  下面这个警告,实际上定义要在任何使用函数之前:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

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

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

  编译成功
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

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

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

sudoinsmodregisterMiscDev.ko

  会打印入口加载的printk输出。
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

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

dmesg

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

  然后查看是否加入了杂项设备节点:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

  然后注销:

sudormmodregisterMiscDev.ko

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

  跟随着,结点消失了:
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

入坑

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

问题

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

解决

  加分号,脑袋有点蒙
   Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo

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

问题

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

解决

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


延伸 · 阅读

精彩推荐
  • Linuxlinux驱动程序开发详细介绍

    linux驱动程序开发详细介绍

    前提,一般来说内核代码的错误可能会引起一个用户进程的死亡,或者整个系统的瘫痪,更严重的后果,可能导致磁盘损伤~因此建议最好有一台实验机进行...

    Linux教程网5392019-12-17
  • Linuxssh 登录很慢该如何解决

    ssh 登录很慢该如何解决

    这篇文章主要介绍了ssh 登录很慢该如何解决的相关资料,这里提供了两种方法,DNS反向解析及关闭ssh的gssapi认证的解决办法,需要的朋友可以参考下...

    linuxeye9922021-12-16
  • LinuxLinux系统下无法卸载挂载的目录怎么办?

    Linux系统下无法卸载挂载的目录怎么办?

    我们在日常运维中经常性会遇到需要进行磁盘的扩容、卸载、挂载等操作,但是有时候这个系统上跑的应用并没有停止或者有其他的运维同事在操作这个目...

    今日头条10302020-12-30
  • Linux理解 Linux/Unix 登录脚本的技巧

    理解 Linux/Unix 登录脚本的技巧

    有一些常见的情况,例如从Debian的包管理程序到Iaas的管理中,很多任务需要设置环境变量才能正常运行。 有时,程序通常只需要在 登陆时运行一次,例如...

    未知1042023-05-12
  • Linux在Linux系统中创建新的亚马逊AWS访问密钥的方法

    在Linux系统中创建新的亚马逊AWS访问密钥的方法

    如何在Linux系统中创建新的亚马逊AWS访问密钥?我在配置一个需要访问我的亚马逊AWS帐号的应用时被要求提供AWS访问密钥ID和秘密访问密钥,我怎样创建一个...

    Linux教程网6182019-10-30
  • Linux将 Linux 终端与 Nautilus 文件管理器结合起来

    将 Linux 终端与 Nautilus 文件管理器结合起来

    Nautilus 是 GNOME 桌面环境中的图形化文件浏览器。你可以使用它来访问和管理系统中的文件和文件夹。 尽管并非所有人都喜欢使用终端来管理文件和目录,...

    未知812023-08-08
  • Linuxlinux设置tomcat自启动的方法

    linux设置tomcat自启动的方法

    这篇文章主要介绍了linux设置tomcat自启动的方法,需要的朋友可以参考下...

    Linux教程网8512021-10-10
  • Linuxlinux top命令详解

    linux top命令详解

    这篇文章主要介绍了linux top命令详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    sparkdev5622022-03-01