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公司为了填补跨平 ...
随机推荐
- [转帖]使用 TiFlash
TiDB试用 来源:TiDB 浏览 490 扫码 分享 2021-04-20 20:57:48 使用 TiFlash 按表构建 TiFlash 副本 查看表同步进度 使用 TiDB 读取 TiFla ...
- [转帖]jmeter之foreach循环控制器-03篇
上篇我们通过正则表达式获取到了一组数据,那么怎么来用呢?下面就用foreach控制器来使用结果,如下图所示 然后再foreach控制器里添加要循环的请求,我们模拟百度搜索,value填入${id} 然 ...
- [转帖]SQL Server超过了每行的最大字节数(8060)的原因和解决办法
一.现象 出现这种错误都发生在SQL语句建表时,错误提示: "警告: 已创建表 'XXXX,但其最大行大小(10438)超过了每行的最大字节数(8060).如果结果行长度超过 806 ...
- 【转帖】Lua,LuaJIT,Luarocks的安装与配置-史上最详细【Linux】
目录 一,lunux下lua安装 二,安装luarocks---lua包管理工具 三,LuaJIT的安装 既然各位都点开看了,那么Lua语言不用我介绍了吧,LuaJIT是lua的一个Just-In-T ...
- js数组修改后会互相影响
// 假设httpServe 是服务器返回来的数据 // 我们这里有一个需求, // 某一个区域需要对这一份数据进行展示 // 另一个区域需要只需要展示前1条数据 let httpServe = [ ...
- echarts柱状图圆角实现
series: [{ name: '销量', type: 'bar', barWidth : 30,//柱图宽度 data: [5, 20, 36, 10, 10, 20], itemStyle: { ...
- HarmonyOS实战[二]—超级详细的原子化服务体验[可编辑的卡片交互]快来尝试吧
相关文章: HarmonyOS实战[一]--原理概念介绍安装:基础篇 [本文正在参与"有奖征文|HarmoneyOS征文大赛"活动] 1.创建HarmonyOS应用 选择Java程 ...
- 纪念JDBC
技术总是在不断更新变化的,尤其是在IT编程领域. 有时候我们理所当然的用着现成的框架,以至于用的太过于顺手,更要时不时的骂一句: 什么垃圾框架?我家狗都不会用! 如果那些被拍死在沙滩的"前浪 ...
- centos7多网口配置同网段IP解决方案
环境 CentOS Linux release 7.9.2009 (Core) 需求 服务器eth0和eth1配置同网段IP地址.掩码不配网关,同时连接两根网线,对端是两台物理隔离的交换机. 现象 给 ...
- 给textarea添加行号,textarea使用代码风格的一些思考
背景 项目有个需求是 在textarea中编辑脚本并显示为代码风格样式,显示行号: textarea显示行号 思路: 1.监听textarea内容变化,执行一个change函数,解析内容里面有多少个换 ...