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公司为了填补跨平 ...
随机推荐
- Oracle12c(未更新任何补丁) 使用compression=all 参数导出之后导入失败
1. 最近使用Oracle12c 进行相关的测试工作, 平台linux 和 windows 都有一个问题 备份恢复使用的 compression=all 时导入数据库不管是oracle12c还是 or ...
- MYsql备份恢复简单过程
1. 备份数据库 建完数据库更新完补丁之后进行数据库的备份操作. mysqldump -uroot --databases yourdatabase -p > /home/yourdatabas ...
- HTTPD 搭建正向代理 使无网络访问权限的服务器能够访问互联网服务的快捷办法
背景 公司有保密要求比较高,数据安全要求比较高的企业客户,要求核心业务服务器部允许直接访问互联网,但是因为我们有一些OCR识别以及发票查验等的场景需要连接云端的服务才可以正常使用, 所以这里面就存在安 ...
- Linux bridge使用dummy接口调用IPVS的问题
Linux bridge使用dummy接口调用IPVS的问题 在IPVS: How Kubernetes Services Direct Traffic to Pods一文中,作者给出了一个简单的组网 ...
- elementui 的tabs组件出现蓝色边框问题
elementui 的tabs组件出现蓝色边框问题 /deep/ .el-tabs__item:focus.is-active.is-focus:not(:active) { -webkit-box- ...
- js引起的 xxxx of null
在 vue 中操作 dom 元素的时候,报错 style of null 这个报错的原因,跟你代码的健壮性有关了; 这样就不会报错了 if( document.querySelectorAll(&qu ...
- 【K哥爬虫普法】网盘用的好,“艳照门”跑不了
我国目前并未出台专门针对网络爬虫技术的法律规范,但在司法实践中,相关判决已屡见不鲜,K哥特设了"K哥爬虫普法"专栏,本栏目通过对真实案例的分析,旨在提高广大爬虫工程师的法律意识,知 ...
- Go 泛型发展史与基本介绍
Go 泛型发展史与基本介绍 Go 1.18版本增加了对泛型的支持,泛型也是自 Go 语言开源以来所做的最大改变. 目录 Go 泛型发展史与基本介绍 一.为什么要加入泛型? 二.什么是泛型 三.泛型的来 ...
- MongoDB 选型介绍
什么是 MongoDB 前言 MongoDB 的主要特性 MongoDB 对比关系型数据库 MySQL 什么时候考虑 MongoDB 参考 什么是 MongoDB 前言 MongoDB 是一个开源.高 ...
- 火遍外网的Keychron测评,带你入坑;ps马上5.20了送一个给你的心爱的她/他。
那些年用过的机械键盘 如果你经常上YouTube或Instagram,然后你又对键盘感兴趣,我相信你肯定看到过他--Keychron K2,他真的是一款曝光量很高的键盘. 1.键盘keychron k ...