Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo
前言
驱动的开发需要先熟悉基本概念类型,本篇讲解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;
例如read函数,那么就是打开驱动使用系统read,打开这个设备驱动的句柄,那么久会调用read函数,其他的以此类推,还比较好理解。
以我们一个registerHelloWorld为例子,来简单说明。
驱动编写空模板准备
首先复制之前的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结构体
在编写驱动的时候,代码中填充信息结构体。
添加头文件miscdevice.h
#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

跟随着,结点消失了:

入坑
入坑一:编译报错,结构体之后未加分号
问题
编译错误,结构体后面加分号
解决
加分号,脑袋有点蒙

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

解决
这是写错了,是指针,需要加取地址&。
Linux驱动开发笔记(四):设备驱动介绍、熟悉杂项设备驱动和ubuntu开发杂项设备Demo的更多相关文章
- Django开发笔记四
Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.邮箱激活 users app下,models.py: ...
- OsgEarth开发笔记(一):Osg3.6.3+OsgEarth3.1+vs2019x64开发环境搭建(上)
前言 OSG研究之后,做地理GIS显示了地球:<项目实战:Qt+OSG教育学科工具之地理三维星球>,这一文章是基于OSG做的,而基于OsgEarth是可以进一步对地球进行深度操作,所以 ...
- OsgEarth开发笔记(三):Osg3.6.3+OsgEarth3.1+vs2019x64开发环境搭建(下)
前言 上一篇编译了proj6.2.0.gdal3.2.1,本篇继续. OsgEarth编译过程简介 OsgEarth的编译,是基于Osg和OsgEarth结合在一起的,先要编译Osg,然后 ...
- Android移动APP开发笔记——最新版Cordova 5.3.1(PhoneGap)搭建开发环境
引言 简单介绍一下Cordova的来历,Cordova的前身叫PhoneGap,自被Adobe收购后交由Apache管理,并将其核心功能开源改名为Cordova.它能让你使用HTML5轻松调用本地AP ...
- 地图开发笔记(一):百度地图介绍、使用和Qt内嵌地图Demo
前言 Qt在地图方面的研发. 百度地图 介绍 百度的地图分为多个开发,都是在线的(离线的需要自己提取,本篇解说在线地图). 百度地图JavaScript API支持HTTP和HTTPS, ...
- 张高兴的 Windows 10 IoT 开发笔记:使用 Lightning 中的软件 PWM 驱动 RGB LED
感觉又帮 Windows 10 IoT 开荒了,所以呢,正儿八经的写篇博客吧.其实大概半年前就想写的,那时候想做个基于 Windows 10 IoT 的小车,但树莓派原生不支持 PWM 啊.百度也搜不 ...
- STM32F412应用开发笔记之八:迪文串口屏显示驱动
迪文的显示屏使用起来比较方便,其使用串口通讯,即可支持RS232,又可以支持TTL电平.在NUCLEO-F412ZG实验板上,USART2已经引到了CN9上,我们就利用USART2来实现与迪文串口屏的 ...
- Qt开发笔记:OpenSSL库介绍、windows上mingw32版本的OpenSSL编译模块化
前言 Windows上mingw32版本的openssl的编译是属于比较棘手的,OpenSSL本身不提供支持.. OpenSSL 介绍 OpenSSL是一个开放源代码的软件库包,应用程序可 ...
- Linux内核分析 笔记四 系统调用的三个层次 ——by王玥
一.知识点总结 (一)用户态.内核态和中断 1.内核态:在高的执行级别下,代码可以执行特权指令,访问任意的物理地址,这时的CPU就对应内核态 2.用户态:在低级别的指令状态下,代码 只能在级别允许的特 ...
- Gif开发笔记(一):gif介绍、编译和工程模板
前言 实现gif图片的解码和生成. Gif 简介 GIF格式的名称是Graphics Interchange Format的缩写,是在1987年由Compu Serve公司为了填补跨平 ...
随机推荐
- [转帖]关于https://goproxy.cn,direct与https://proxy.golang.org的问题,国内无法访问https://proxy.golang.org设置了GOPROXY仍不可行
关于https://goproxy.cn,direct与https://proxy.golang.org的问题,国内无法访问https://proxy.golang.org设置了GOPROXY仍不可行 ...
- Mysql8.0.32 union all创建视图无法使用中文模糊查询的坑
Mysql8.0.32 union all创建视图无法使用中文模糊查询的坑 摘要 本周研发同事反馈现场有一个问题. 客户使用mysql的数据库(Windows平台) 然后在多表union all 创建 ...
- 【转帖】【奇技淫巧】Linux | 安全保障防火墙-iptables
虽然说Linux在安全方面确实相当于windows要更加可靠一些,但一般使用其作为服务器的我们,也不能大意,也是需要严格限制网络传输过程中的出入规则.上篇文章我们有聊到统计网络的信息,这篇文章来学习一 ...
- Linux下面sysstat的安装与简介
https://blog.51cto.com/smoke520/2160073 在Linux系统下获取sysstat-10.0.5.tar.gz的两种方式: 方式一: 下载sysstat-10.0 ...
- 【K哥爬虫普法】辛苦钱被中间商抽走八成,还因此锒铛入狱
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K 哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识, ...
- 【JS 逆向百例】网洛者反爬练习平台第五题:控制台反调试
关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...
- minIO系列文章04---windows下安装及在.netcore使用
一.minio下载与启动 下载后会有一个minio.exe文件,放到指定的目录 在该目录下运行:minio.exe server D:\minio\file 出现如下的提示代码启动动成功: 浏览器中 ...
- gRPC基本教程
原文在这里. 本教程为Go程序员提供了使用gRPC的基本介绍. 通过跟随本示例,你将学会如何: 在.proto文件中定义一个服务. 使用协议缓冲编译器生成服务器和客户端代码. 使用Go gRPC AP ...
- 手撕Vuex-实现共享数据
经过上一篇章介绍,完成了添加全局 $store,接下来就是实现共享数据的功能. 在 Vuex 中,共享数据是通过 state 来实现的,所以我们需要在 Nuex.js 文件中实现 state 的功能. ...
- 【一】分布式训练---单机多卡多机多卡(飞桨paddle1.8)
1.分布式训练简介 分布式训练的核心目的: 加快模型的训练速度.通过对训练任务按照一定方法拆分分配到多个计算节点进行计算,再按照一定的方法对需要汇总的信息进行聚合,从而实现加快训练速度的目的. 1.1 ...