Linux中的三大类驱动:字符设备、块和网络设备驱动

I2C、SPI、音频等都属于字符设备驱动 的类型

EMMC、NAND、SD卡和 U盘等存储都属于块设备

网卡,WIFI等都属于网络驱动

一个设备可以属于多种设备驱动,如USB WIFI,其USB接口属于字符设备驱动,但WIFI功能同时属于网络驱动。

字符设备驱动模型

应用程序运行在用户空间,而Linux 驱动属于内核的一部分,当在用户空间使用open()函数打开/dev/eth0这个驱动设备文件时,必须使用系统调用的方法来实现,open,close,write,read这些函数都是由C库提供的具有系统调用功能的函数。

Linux内核文件 include/linux/fs.h中 ,有个叫做 file_operations的结构体,就是 Linux内核驱动操作函数集合

linux驱动模块的加载和卸载

两种方式:直接编译进内核,或者编译成模块.ko文件,然后使用insmod或者modprobe加载,使用rmmod或者modprobe -r卸载

# insmod drv.ko
# rmmod drv.ko# modprobe drv.ko
# modprobe -r drv.lo// 建议使用modprobe drv.ko来加载驱动模块,使用rmmod来卸载模块
// modprobe 会根据依赖关系加载相关模块,而insmod只加载指定的模块
// modprobe -r也会根据依赖关系卸载相关模块,rmmod只卸载指定模块
//驱动入口函数
static int __init xxx_init(void)
{return 0;
}//驱动出口函数
static void __exit xxx_exit(void)
{}//将上面两个函数指定为出口函数和出口函数
module_init(xxx_init);
module_exit(xxx_exit);

字符设备注册和注销

当模块加载成功以后需要注册字符设备,卸载后也需要注销字符设备

static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
// major为主设备号, name 为设备名, fops 为操作函数集合static inline void unregister_chrdev(unsigned int major, const char *name)

一般字符设备的注册在驱动模块入口函数 xxx_init 中进行,注销在出口函数 xxx_exit中进行

static struct file_operations test_fops;//驱动入口函数
static int __init xxx_init(void)
{retvalue = register_chrdev(200, "chrtest", &test_fops);    if(retvalue < 0){}return 0;
}//驱动出口函数
static void __exit xxx_exit(void)
{unregister_chrdev(200, "chrtest");
}//将上面两个函数指定为出口函数和出口函数
module_init(xxx_init);
module_exit(xxx_exit);

通过cat /proc/devices命令查看当前已经被使用的主设备号。

初始化 fops结构体操作函数集合

static int chrtest_open(struct inode *inode, struct file *filp)
{return 0;
}static ssize_t chrtest_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}static ssize_t chrtest_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}static int chrtest_release(struct inode *inode, struct file *filp)
{return 0;
}static struct file_operations test_fops =
{.owner = THIS_MODULE,.open = chrtest_open,.read = chrtest_read,.write = chrtest_write,.release = chrtest_release,
};//驱动入口函数
static int __init xxx_init(void)
{retvalue = register_chrdev(200, "chrtest", &test_fops);    if(retvalue < 0){}return 0;
}//驱动出口函数
static void __exit xxx_exit(void)
{unregister_chrdev(200, "chrtest");
}//将上面两个函数指定为出口函数和出口函数
module_init(xxx_init);
module_exit(xxx_exit);//添加license信息和作者信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jack");

添加LICENSE和作者信息

license信息必须添加,否则编译会报错,如上图。

Linux设备号

Linux中每个设备都有一号,由主和次两部分组成,主设备号(major)表示某一个具体的驱动,此设备号(minor)表示使用这个驱动的各个设备。

Linux提供一个数据结构dev_t表示设备号,定义在include/linux/types.h中。

typedef __u32 __kernel_dev_t;
typedef __kernel_dev_t dev_t;
typedef unsigned int __u32;
// 因此,dev_t就是unsigned int类型,32位的数据类型

设备号位32位,高12位为主设备号,低20位为此设备号。

因此,主设备号范围为0~4095

在 include/linux/kdev_t.h中定义了几个宏定义的设备号的函数。

#define MINORBITS 20
// MINORBITS表示次设备号位数,一共是20#define MINORMASK ((1U << MINORBITS) - 1)
// MINORMASK表示次设备号掩码。#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
// MAJOR用于从 dev_t中获取主设备号,将 dev_t右移 20位即可#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
// MINOR用于从dev_t中获取次设备号,取 dev_t的低 20位的值即可#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
// MKDEV用于将给定的主设备号和次值组合成 dev_t类型的设备号

动态分配设备号

静态手动申请设备号,容易带来冲突问题。

Linux推荐使用动态分配设备号,在注册字符设备之前先申请一个设备号,系统会自动生成一个不冲突的设备号。

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)// dev:保存申请到的设备号。
// baseminor:此设备号起始地址
// count:要申请的设备号数量
// name: 设备名字void unregister_chrdev_region(dev_t from, unsigned count)// from:要释放的设备号
// count:表示从from开始,要释放的设备号数量

正点原子-驱动开发-字符设备驱动相关推荐

  1. <Linux开发>--驱动开发-- 字符设备驱动(3) 过程详细记录

    <Linux开发>–驱动开发-- 字符设备驱动(3) 过程详细记录 驱动开发是建立再系统之上的,前面作者也记录了系统移植的过程记录,如果有兴趣,可进入博主的主页查看相关文章,这里就不添加链 ...

  2. linux的驱动开发——字符设备驱动

    1.字符设备驱动 \qquad字符设备驱动是最基本,最常用的设备.它将千差万别的硬件设备采用统一的接口封装起来,屏蔽了硬件的差异,简化了应用层的操作. 2.描述所有字符设备的结构体 \qquad描述所 ...

  3. STM32MP157驱动开发——字符设备驱动

    一.简介 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节 流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键. IIC. SPI, LCD ...

  4. linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1)

    哈喽,我是老吴,继续记录我的学习心得. 一.保持专注的几个技巧 将最重要的事放在早上做. 待在无干扰环境下,比如图书馆. 意识到刚坐下开始投入工作前,有点负面小情绪是特别正常的现象. 让"开 ...

  5. _Linux驱动开发 / 字符设备驱动内幕 (1)

    哈喽,我是老吴,继续记录我的学习心得. 一.保持专注的几个技巧 将最重要的事放在早上做. 待在无干扰环境下,比如图书馆. 意识到刚坐下开始投入工作前,有点负面小情绪是特别正常的现象. 让"开 ...

  6. STM32MP157驱动开发——蜂鸣器设备驱动

    STM32MP157驱动开发--蜂鸣器设备驱动 0.相关知识 一.驱动程序开发 1.设备树修改 2.启动程序编写 3.测试程序编写 二.编译及运行测试 0.相关知识   蜂鸣器常用于计算机.打印机.报 ...

  7. STM32MP157驱动开发——USB设备驱动

    STM32MP157驱动开发--USB设备驱动 一.简介 1.电气属性 2.USB OTG 3.STM32MP1 USB 接口简介 4.Type-C 电气属性 二.USB HOST 驱动开发 1.US ...

  8. 设备驱动,字符设备驱动、(总线)设备驱动模型、sysfs文件系统、平台设备驱动

    以下内容转载于微信公众号:嵌入式企鹅圈.如有侵权,请告知删除. 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sysfs等相关概念和技术. 对于初学者来说会非常 ...

  9. 【linux驱动之字符设备驱动基础】

    linux驱动之字符设备驱动基础 文章目录 linux驱动之字符设备驱动基础 前言 一.开启驱动学习之路 二.驱动预备知识 三.什么是驱动? 3.1 驱动概念 3.2 linux 体系架构 3.3 模 ...

最新文章

  1. ITK:将所有像素的总和缩放为常数
  2. Linkify 添加链接
  3. 源代码下载 第六章 注解式控制器详解
  4. target not created怎么解决_怎么才能最短时、高效、踏实地学习 Python(附链接)...
  5. Java中的证书透明度验证
  6. 【渝粤题库】陕西师范大学200681C语言程序设计 作业(高起专、高起本)
  7. 算法设计与分析_算法设计与分析(第2版)第2章分治策略回顾
  8. php 静态 成员属性,[已解决]php中静态成员方法和静态成员变量是不是不支持多态?...
  9. C++类继承内部类实例
  10. UVA10063 Knuth‘s Permutation【排列组合】
  11. httpclient案例一(调用识别接口)
  12. 添加数据要顺着来,删除数据要反着去。
  13. php的一些编码问题
  14. cad一直正在加载_CAD总是打开要加载好久,卡到不行。
  15. 如何在Ubuntu上安装Couch DB 1.5
  16. Fractions to Decimals_usaco2.4.5_暴力
  17. 团队作业第二次——需求分析
  18. [黑苹果]炫龙毁灭者DC显卡无解
  19. 财务数据填报怎样做?用这个报表工具轻松搞定!_光点科技
  20. Catalan number卡特兰数

热门文章

  1. 【程序员日记】泰国之行,做个记录!
  2. 参考文献类型字母的含义
  3. 物理渲染学习笔记(二)——光的传播
  4. 对一个加壳的可执行文件进行脱壳三种方法
  5. Kotlin中使用handler
  6. 在WSUS服务中“放行”微软拼音词典更新
  7. 推荐几款基于Bootstrap的响应式后台管理模板
  8. IIS 绑定多个证书错误解决方法
  9. PHP aws-sdk-php文件存储的实现与应用
  10. casperjs evaluate函数传参数问题