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

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

解决

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


延伸 · 阅读

精彩推荐