Linux字符设备驱动开发一

news2024/12/28 4:21:21

linux字符设备驱动

    • 0 驱动介绍
    • 1 字符设备驱动
      • 1.1 字符设备相关概念和结构体
      • 1.2 实现简单的字符设备模块
      • 1.3 创建字符设备
      • 1.4 总结

应用程序调用文件系统的API(open、close、read、write) -> 文件系统根据访问的设备类型,调用对应设备的驱动API -> 驱动对硬件进行操作。
请添加图片描述

0 驱动介绍

驱动类型:

  1. 字符设备驱动:以一个字节一个字节读写的硬件对应的驱动。比如串口、watchdog、rtc、鼠标、触摸屏等。字符设备必须以串行顺序依次进行访问。
  2. 块设备驱动:以一个扇区一个扇区(512kb、4096kb)读写的硬件对应的驱动。比如硬盘。块设备可以按任意顺序进行访问。
  3. 网络设备驱动:走内核里的协议栈内存。比如网卡驱动、can驱动。网络设备面向数据包的接收和发送而设计。

除网络设备外,字符设备与块设备都被映射到Linux文件系统的文件和目录,通过文件系统的系统调用接口open()、write()、read()、close()等即可访问字符设备和块设备。

驱动的动态加载和静态加载区别:

  1. 编译方式不同:静态加载–obj-y,动态加载–obj-m
  2. 存在位置不同:静态加载,则直接编译到linux内核镜像中uimag;动态加载,则是独立的ko,驱动代码不在内核镜像中,而是独立存在于文件系统中
  3. 加载时机不同:静态加载,内核启动时就加载;动态加载,在系统启动的时候首先uboot的启动,然后linux内核启动,最后文件系统启动。静态加载,随linux内核一起加载启动,动态加载则需要等文件系统启动后手动insmod加载。

动态加载优势:

  1. 实现热插拔机制:硬件要实现热插拔,则驱动必须使用动态加载。设备插入时,加载驱动;设备移除时,卸载驱动。
  2. 驱动调试:调试更方便。单独编译驱动,通过insmod、rmmod加载和卸载进行单独调试。
  3. 开机优化:有的驱动加载非常耗时,影响设备启动速度。动态加载,可以等用户的图形界面起来后,再加载驱动,让用户感觉系统启动速度加快了。

1 字符设备驱动

字符设备必须以串行顺序依次进行访问,常见的字符设备:串口、watchdog、rtc、鼠标、触摸屏等。字符设备的读写以字节为单位。

1.1 字符设备相关概念和结构体

了解字符设备相关概念和结构体,为之后阅读字符设备驱动代码打下坚实基础。

1 设备号dev_t:类似一个人的身份证号。内核中通过dev_t来描述设备号,其实质是unsigned int 32位整数,其中高12位为主设备号,低20位为次设备号。

  • 为每一个外设设置一个独一无二的ID,让内核能够区分每一个设备。
  • 高12区分不同类别的设备
  • 低20位,区分同一类别的不同设备

linux如何为每一个设备生成设备号呢?
可以是驱动自行构造一个设备号,然后调用函数register_chrdev_region向内核注册。如果注册成功,该函数返回0;注册失败,返回一个负数,就不能用这个设备号。

int register_chrdev_region(dev_t from, unsigned count, const char *name);

2 设备描述cdev、file_operations:
一个linux设备,需要通过cdev结构体来描述设备信息,通过file_operations来描述如何操作设备。最后要通过cdev_add函数来注册设备,让linux内核知道这个设备。

/*设备信息的描述*/
struct cdev {
    struct kobject kobj; /**/
    struct module *owner;
    const struct file_operations *ops; /*一组函数集*/
    struct list_head list; /*链表*/
    dev_t dev; /*设备号*/
    unsigned int count; /*已支持的设备*/
}

/*设备行为的描述:驱动设备开发,就要实现这些函数*/
struct file_operations {
	struct module *owner;
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    int (*open) (struct inode *, struct file *);
    int (*release) (struct inode *, struct file *);
    ...
};

/*相关注册函数*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);

1.2 实现简单的字符设备模块

#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/fs.h>
#define OK (0)
#define ERROR (-1)
#define DEV_NUM (10)

/*
 创建设备ID:MKDEV
 注册设备ID:
 给设备行为结构体的成员函数赋值
 初始化设备:建立设备行为结构体file_operations和设备信息结构体cdev的关系;
 添加设备:向内核注册设备,使内核知道设备,建立设备ID和设备信息cdev的关系;
 加载设备:insmod
 创建设备文件:建立设备文件与设备ID的关系:mknod /dev/my_chr_dev c 232 0
 测试设备行为结构体的成员函数功能
*/

dev_t g_dev_num; /*设备号*/
struct cdev *g_cdev; /*设备信息*/
struct file_operations *g_fops; /*设备行为信息*/
const int g_major = 232; /*主设备号*/
const int g_minor = 0; /*次设备号*/
unsigned g_dev_count = 3;
const char g_dev_name[] = "my_char_dev"; /*设备名称,内核管理用的,随便写*/

ssize_t my_dev_read(struct file *my_file, char __user *my_user, size_t my_size, loff_t *my_loff)
{
    printk(KERN_EMERG "read my dev success\n");
    return OK;
}

ssize_t my_dev_write(struct file *my_file, const char __user *my_user, size_t my_size, loff_t *my_loff)
{
    printk(KERN_EMERG "write my dev success\n");
    return OK;
}

int my_dev_open(struct inode *my_inode, struct file *my_file)
{
    printk(KERN_EMERG "open my dev success\n");
    return OK;
}

int my_dev_release(struct inode *my_inode, struct file *my_file)
{
    printk(KERN_EMERG "release my dev success\n");
    return OK;
}

static int __init chr_init(void)
{
    g_dev_num = MKDEV(g_major, g_minor); /*生成设备号*/
    printk("dev id:%d\n", g_dev_num);

    /*注册设备号*/
    if (register_chrdev_region(g_dev_num, g_dev_count, g_dev_name) != 0) {
        printk(KERN_EMERG "register dev id failed\n");
        return ERROR;
    }

    /*内核函数:自动生成设备函数, 避免自定义设备号冲突,导致注册失败
    if (alloc_chrdev_region(&g_dev_num, g_major, g_dev_count, g_dev_name) != OK) {
        printk(KERN_EMERG "register dev id failed\n");
        return ERROR;
    }
    */

   /*为设备信息结构体cdev和设备行为结构体file_operations申请内存;kzalloc相比kmalloc,会将申请的内存地址置为0,*/
    g_cdev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
    g_fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);

    /*为设备行为结构体成员函数赋值*/
    g_fops->read = my_dev_read;
    g_fops->write = my_dev_write;
    g_fops->open = my_dev_open;
    g_fops->release = my_dev_release;
    g_fops->owner = THIS_MODULE; /*一般就赋值为THIS_MODULE宏*/

    cdev_init(g_cdev, g_fops); /*初始化设备:建立cdev和file_operations的联系*/
    cdev_add(g_cdev, g_dev_num, g_dev_count); /*向内核注册设备:建立cdev和设备号的联系*/
    printk(KERN_EMERG "add my_chr_dev success\n");
    return OK;
}

static void __exit chr_exit(void)
{
    cdev_del(g_cdev); /*删除设备*/
    unregister_chrdev_region(g_dev_num, g_dev_count); /*注销设备号*/
    printk(KERN_EMERG "unregister my_chr_dev success\n");
}

module_init(chr_init);
module_exit(chr_exit);
MODULE_LICENSE("GPL");


make编译模块后,insmod my_chr_dev加载字符设备驱动。
makefile文件解读:

  • ifneq,首先判断是否定义了内核宏,第一次执行由于没有定义内核宏,先执行else分支,然后执行all。
  • make -C,切换到内核目录内,-M指定编译的模块。此时进入-M指定的模块内,再次执行makefile文件。
  • 此时,内核宏定义了,执行if分支,编译内核模块。
ifneq ($(KERNELRELEASE),)
  obj-m := my_chr_dev.o
else
  KERDIR ?="/lib/modules/$(shell uname -r)"/build
  PWD := $(shell pwd)

all:
	$(MAKE) -C $(KERDIR) M=$(PWD)
clean:
	rm -rf *.o *.ko *mod.c 9.module.o *.order
endif

1.3 创建字符设备

mknod /dev/my_chr_dev c 232 0232 0为主、次设备号。

  • mknod,实际上就是建立设备文件my_chr_dev与设备号232 0之间的联系。

驱动测试程序:

#include <stdio.h>
#include <fcntl.h> /*O_RDWR*/
#include <unistd.h> /*read open write close usleep*/
#define DATA_NUM (10)

int main()
{
    int fd;
    int wd, rd;
    char my_buffer[DATA_NUM] = "12345";
    fd = open("/dev/my_chr_dev", O_RDWR);
    printf("fd=%d\n", fd);
    if (fd == -1){
        printf("open failed\n");
        return -1;
    }
    wd = write(fd, my_buffer, DATA_NUM);
    rd = read(fd, my_buffer, DATA_NUM);
    printf("wd=%d, rd=%d\n", wd, rd);
    return 0;
}

该程序用于测试驱动程序的open、write、read、release。编译执行该驱动程序后,执行dmesg,可以查看内核调用驱动程序对应的open、write、read、release函数时日志信息。

1.4 总结

开发字符设备驱动主要有7步:

  1. 创建设备号MKDEV、注册设备号register_chrdev_region;
  2. 编写设备行为结构体的成员函数:成员函数实现硬件的open/close/read/write等操作。
  3. 建立设备信息结构体cdev与设备行为结构体file_operations之间的联系:cdev_init
  4. 向内核注册设备,建立设备信息结构体与设备号之间的联系:cdev_add
  5. make编译、insmod加载设备驱动。
  6. 创建设备文件,建立设备文件与设备号之间的联系:mknod /dev/chrDev c 主设备号 次设备号
  7. 编写测试脚本,测试驱动程序的open/close/read/write功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1523349.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32/GD32——FreeRTOS任务管理与相关机制

芯片选型 Ciga Device — GD32F470系列 任务管理 任务处理API 操作 API 动态任务创建 xTaskCreate 任务删除 vTaskDelete 静态任务创建 vTaskCreateStatic 挂起任务 vTaskSuspend 恢复任务 vTaskResume 任务创建 BaseType_t xTaskCreate( TaskFunction_t pxTa…

叠加积分法计算电场强度

目录 电场强度 点电荷 体电荷 面电荷 ​编辑线电荷 基础知识&#xff1a;静电场--电场强度-CSDN博客 电场强度 点电荷 由于电场强度与产生电场的点电荷的电荷量成正比。场与源之间的这种线性关系可以用叠加原理来计算n个点电荷所形成的电场强度&#xff0c;即在电场中某一…

PyTorch学习笔记之激活函数篇(一)

文章目录 1、Sigmoid函数1.1 公式1.2 对应图像1.2 生成图像代码1.4 优点与不足1.5 torch.sigmoid()函数 1、Sigmoid函数 1.1 公式 Sigmoid函数的公式&#xff1a; f ( x ) 1 1 e − x f(x) \frac{1}{1e^{-x}} f(x)1e−x1​ Sigmoid函数的导函数&#xff1a; f ′ ( x ) e …

7.Java整合MongoDB—项目创建

整合MongoDB MongoDB的基本知识有所了解之后&#xff0c;我们开始着手上代码了&#xff0c;进来先来项目创建&#xff0c;如何引入mongodb&#xff0c;以及测试一下能否连接数据库。 1 新建springboot项目 其实只需要spring boot mongodb这个依赖就行&#xff0c;加那么多纯属…

ChatGLM3-6B独立部署提供HTTP服务failed to open nvrtc-builtins64_121.dll

背景 我在本地windoes部署ChatGLM3-bB&#xff0c;且希望部署后能提供HTTP server的能力。 模型部署且启动是成功了&#xff0c;但是在访问生成接口/v1/chat/completions时报错failed to open nvrtc-builtins64_121.dll。 问题详细描述 找不到nvrtc-builtins64_121.dll Runtime…

京瓷喷头官方参数

KJ4B-QA06NTB-STDV喷墨打印头专为高速单程、多程和多程水基油墨应用而设计。 它利用2656个直径为108mm的喷嘴&#xff0c;以75m/min的速度提供600dpi600dpi的分辨率。 由于采用宽印刷宽度、高密度喷嘴布置和高响应墨道设计&#xff0c;其生产效率和稳定性在纺织印刷、高速辊送印…

手机也能写前段代码,推荐一款万能编程软件

Python是一种强大的编程语言&#xff0c;广泛应用于各个领域&#xff0c;包括移动应用开发。如果你想在手机上进行Python编程&#xff0c;那么选择合适的软件工具就显得尤为重要。 一.python Pydroid 3 Pydroid 3是一款专为Android设备打造的Python IDE。它提供了一个完整的开…

【Canvas与艺术】下雪籽特效

【要点】 控制一个点的x,y坐标及下落速度&#xff0c;就能画出一个雪籽&#xff1b;创建n个雪籽&#xff0c;下雪籽的模拟效果就有了。 【效果图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content…

GEC6818——QT开发之两个UI界面切换与表格显示DHT11数据

GEC6818——QT开发之两个UI界面切换与表格显示DHT11数据 使用环境: ubantu16 QT5.7 开发板GEC6818 实现要求&#xff1a; 利用A53按键1、按键2与温湿度传感器完成QT界面动态显示温湿度记录&#xff0c;并指定温湿度记录超过指定范围&#xff0c;进行报警&#xff08;LED&#…

Grass推出Layer 2 Data Rollup

Grass推出Layer 2 Data Rollup Grass邀请链接最新资讯 Grass邀请链接 欢迎使用我的邀请码进行注册: 邀请链接 如果你还不知道注册流程&#xff1a;详见Grass: 出售闲置带宽实现被动收入 最新资讯 简讯&#xff1a;2024年3月13日&#xff0c;Grass宣布正在建立基于Solana的La…

【Linux系列】计算机系统中的架构与发行版:理解与区分

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【Linux】进程信号{初识信号/常见的信号/中断信号/信号的产生}

文章目录 0.浅谈中断信号1.初识信号2.中断信号3.信号的产生测试&#xff1a;SIGINT 4.core dump核心转储5.系统接口产生信号5.1kill给指定发5.2raise向自己发5.3abort自己给自己发6 6.由于软件条件不满足产生信号6.1SIGPIPE6.2SIGALRM 7. 硬件异常产生信号7.1除零错误7.2野指针…

Java代码基础算法练习-判断字符串是否为回文-2024.03.16

任务描述&#xff1a; 回文串是指一个正读和反读都一样的字符串&#xff0c;比如“level”或者“noon”等。要求输入 一个字符串&#xff0c;判断此字符串是否为回文。&#xff08;注&#xff1a;设字符串长度小于20&#xff09; 任务要求&#xff1a; package suanfa;import…

python--类和对象+类属性+实例属性+函数在类的调用

python--类和对象类属性实例属性函数在类的调用 类属性--创建、访问&#xff08;类、类实例&#xff09;实例属性--创建、访问&#xff08;类实例&#xff09;初始化实例属性__init__ 私有属性伪私有属性&#xff08;Pseudo-private Attributes&#xff09;私有属性&#xff08…

深入浅出理解 AI 生图模型

目录 引言 一、Stable Diffusion原理 首先 随后 最后 二、DDPM模型 1 资料 2 原理 扩散过程 反向过程 3 公式结论 三、优缺点 优点&#xff1a; 缺点&#xff1a; 四、改进与完事 LDM代表作 原理概括 Latent Space&#xff08;潜空间&#xff09; 五、总结 引…

zookeeper快速入门一:zookeeper安装与启动

本文是zookeeper系列之快速入门中的第一篇&#xff0c;欢迎大家观看与指出不足。 写在前面&#xff1a; 不影响教程&#xff0c;笔者安装zookeeper用的是WSL(windows下的linux子系统&#xff09;&#xff0c;当然你想直接在windows上用zookeeper也是可以的。 如果你也想用ws…

MinGW64 windows gcc编译器安装

下载编译好的文件包 https://sourceforge.net/projects/mingw-w64/ 打开网址 界面左上方 点File 滚轮 滚到下面 点 红框 解压 配置path 环境变量

西门子PLC常用底层逻辑块分享_调节阀

文章目录 前言一、功能概述二、调节阀程序编写1.创建自定义数据类型2.创建FC块“调节阀”3.编写程序 前言 本文分享一个自己编写的调节阀控制逻辑块。 一、功能概述 手动状态、自动状态、检修状态自由切换&#xff1b;手动状态下&#xff0c;手动输入阀门开度值&#xff1b;自…

不可能,看了这篇笔记还不会用例设计!

随着测试技术的提升&#xff0c;我们在不断学习跟进的同时&#xff0c;也不能把基础知识忘却了。 测试基础里面最重要的一个成果输出就是测试用例&#xff0c;也是测试思维的集中体现。现在虽然有些岗位不写用例了&#xff0c;但是要求还要测试执行覆盖全&#xff0c;不能漏测。…

我在技​​术面试中用 ChatGPT 作弊,没人知道

众所周知&#xff0c;ChatGPT 已经彻底改变了人们的工作方式。它既能帮助小型企业自动化管理任务&#xff0c;又能为 Web 开发人员编写整个 React 组件&#xff0c;它的作用可以说怎么夸都不过分。 在 interviewing.io&#xff0c;我们一直在思考 ChatGPT 将给技术面试带来什么…