首页 > 系统相关 >Linux驱动开发入门实验

Linux驱动开发入门实验

时间:2024-04-10 22:34:09浏览次数:24  
标签:入门 mydev cdev dev 模块 Linux 驱动 设备

目录

一、驱动模块的加载和卸载

二、分配和释放设备号

三、字符设备注册与注销

四、实现设备的具体操作函数

五、添加 LICENSE和作者信息

六、驱动程序完整代码

七、编译驱动程序

八、加载驱动模块


一、驱动模块的加载和卸载

Linux驱动有两种运行方式:

第一种即是将驱动编译进 Linux内核中,Linux内核启动时自动运行驱动程序。

第二种即是将驱动编译成模块 (Linux下模块扩展名为 .ko),Linux内核启动以后使用“ insmod xxx.ko”命令加载驱动模块。

调试驱动一般选用第二种。这样更改完驱动时只用编译模块而不用编译整个linux导致费时。

模块有加载和卸载两种操作:

module_init(xxx_init);           //注册模块加载函数

module_exit(xxx_exit);         //注册模块卸载函数

module_init()函数被调用时,会向Linux内核注册一个模块加载函数,参数xxx_init即是注册的具体函数,当使用“ insmod xxx.ko”命令加载驱动的时候xxx_init这个函数就会被调用。

module_exit()函数被调用时,会向Linux内核注册一个模块卸载函数,参数xxx_exit即是注册的具体函数,当使用“ rmmod xxx.ko”命令卸载具体驱动的时候xxx_exit函数就会被调用。


二、分配和释放设备号

Linux中每个设备都有一个设备号,类似于身份ID。设备号由主设备号和次设备号两部分
组成,主设备号表示某一个具体的驱动次设备号表示使用这个驱动的各个设备。 Linux提供了
一个名为 dev_t的数据类型表示设备号, dev_t定义在文件 include/linux/types.h里面。

如果没有指定设备号的话就使用如下函数来申请设备号:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

如果给定了设备的主设备号和次设备号就使用如下所示函数来注册设备号即可:

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

参数dev是保存申请到的设备号;参数baseminor是次设备号起始地址,一般为 0,也就是说次设备号从0开始;参数from是要申请的已经给定的起始设备号;参数count是要申请的数量,一般都是一个;参数name是设备名字。

注销字符设备之后要释放掉设备号:

void unregister_chrdev_region(dev_t from, unsigned count)


三、字符设备注册与注销

在 Linux中使用cdev结构体表示一个字符设备, cdev结构体的定义在include/linux/cdev.h里面。

在cdev中有两个重要的成员变量:opsdev,这两个就是字符设备文件操作函数集合file_operations以及设备号 dev_t。编写字符设备驱动之前需要定义一个 cdev结构体变量,这个变量就表示一个字符设备,如下所示:

struct cdev test_cdev;

定义好cdev变量以后就要使用cdev_init函数对其进行初始化, cdev_init函数原型如下:

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

参数cdev即是要初始化的cdev结构体变量,参数fops即是字符设备文件操作函数集合。

然后使用cdev_add函数向Linux系统添加这个字符设备,cdev_add函数原型如下:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

参数p指向要添加的字符设备 (cdev结构体变量),参数dev就是设备所使用的设备号,参数count是要添加的设备数量。

最后卸载驱动时使用cdev_del函数从Linux内核中删除相应的字符设备,cdev_del函数原型如下:

void cdev_del(struct cdev *p)


四、实现设备的具体操作函数

file_operations结构体即是设备的具体操作函数,我们需要初始化其中的open、release、 read和 write等具体的设备操作函数。

在初始化fops之前我们要分析一下需求,也就是要对这个设备进行哪些操作。由于是入门实验,这里不多做要求。

struct file_operations file_operations = {
    .owner = THIS_MODULE,
};


五、添加 LICENSE和作者信息

我们需要在驱动中加入LICENSE信息和作者信息,其中LICENSE是必须添加的,否则编译会报错。

MODULE_LICENSE();
MODULE_AUTHOR();


六、驱动程序完整代码

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>


static dev_t dev_id;
static struct cdev mydev;


/* 文件操作集合 */
static struct file_operations mydev_fops = {
	.owner = THIS_MODULE,
};

static __init int mydev_init(void)
{
    /* 申请设备号 */
    alloc_chrdev_region(&dev_id, 1, 1, "mydev");

    /* 设置字符设备 */
    cdev_init(&mydev, &mydev_fops);

    /* 注册字符设备 */
    cdev_add(&mydev, dev_id, 1);

    /* 打印申请到的主次设备号 */
    printk("mydev_init!\n");

    return 0;
}

static __exit void mydev_exit(void)
{
    cdev_del(&mydev);

    unregister_chrdev_region(dev_id, 1);
}

module_init(mydev_init);
module_exit(mydev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("wJen");

Linux内核里没有printf,printf运行在用户态,printk运行在内核态。


七、编译驱动程序

编写Makefile文件:

KERNELDIR := /home/karudo/linux/IMX6ULL/linux/alientek_linux

CURRENT_PATH := $(shell pwd)

obj-m := mydev.o

build : kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

KERNELDIR表示开发平台所使用的Linux内核源码的绝对路径。这里我已经修改过Linux顶层Makefile的ARCH和CROSS_COMPILE

modules表示编译模块 。

-C表示将当前的工作目录切换到指定目录中,这里即是KERNERLDIR目录。

M表示模块源码目录,这里即是CURRENT_PATH目录。程序会自动到指定的目录中读取模块的源码并将其编译为 .ko文件。

编写完成后,make编译出mydev.ko。


八、加载驱动模块

将mydev.ko cp到你的开发平台下,用lsmod查看已挂载模块:

可以看到目前没有任何模块挂载,用insmod将我们的模块挂载:

可以看到提示消息mydev_init。再用lsmod查看已挂载模块:

 可以看到mydev模块挂载成功。再用cat /proc/devices查看当前设备:

可以看到mydev被分配到的主设备号为248。最后记得不使用该设备时可以用rmmod卸载该设备。

标签:入门,mydev,cdev,dev,模块,Linux,驱动,设备
From: https://blog.csdn.net/weixin_61979510/article/details/137563263

相关文章

  • 如何在windows环境和linux环境运行jar包
    功能:实现对字符串小写转大写java代码如下:packagea_od_test;importjava.util.Locale;/*实现小写转大写打jar包分别在windows环境和liunx环境运行*/publicclassMain28_To_Upper{publicstaticvoidmain(String[]args){if(args.length==......
  • Linux的重要命令(一)
    目录一.查看当前的工作目录-pwd二.切换工作目录-cd三.列表显示目录内容或文件本身-ls四.通配符通配符用法五.设置别名-alias六.统计文件或目录空间占用情况七.创建新目录-mkdir八.创建新文件-touch九.创建链接文件-ln十.复制文件或目录十一.删除文件或目录-rm ......
  • 【Linux系统编程】libevent库事件驱动
    libevent库事件驱动libevent库使用创建并初始化event_base结构体。创建并初始化event结构体,并设置文件描述符、监听事件、回调函数、回调函数参数。将event添加到event_base中。开始事件处理循环,监听事件是否发生,并在满足条件时自动调用回调函数。事件处理完成后,释放event......
  • 领域驱动设计DDD实现工具
    ​领域驱动设计(Domain-DrivenDesign,简称DDD)是一种软件设计方法,它强调以业务领域(Domain)为中心的软件开发。DDD的目的是创建理解业务需求的丰富模型,并确保软件的结构能够清晰反映这些业务需求。实现DDD时,有许多工具和技术可以帮助开发者设计和维护领域模型,以及实现领域逻辑。参考......
  • SQL SERVER 从入门到精通 第5版 第二篇 第9章 视图的使用 读书笔记
      第9章视图的使用视图是一种常用的数据库对象,它将查询的结果以虚拟表的形式存储在数据中,视图并不在数据库中以存储数据集的形式存在.视图的结构和内容是建立在对表的查询基础之上的,和表一样包括行和列,这些行,列数据都来源于其所引用的表,并且是在引用视图过程中动......
  • 在Linux终端查找指定类型的文件并统计数量
    下面举例说明:find/path/to/directory-typef-execfile{}\;|grep"MIDI"它的作用是在指定的目录(/path/to/directory)中搜索所有的文件(-typef),然后使用file命令检查每个文件的类型,并将结果通过管道传递给grep命令,用于过滤出包含"MIDI"关键词的文件信息。find:......
  • Linux文件和目录管理及文本搜索命令find grep
    在Linux操作系统中,“find”和“grep”是两个非常常用的命令,它们在文件和目录管理以及文本搜索方面提供了强大的功能。首先,让我们来看一下"find"命令。“find"命令用于在文件系统中搜索文件和目录。它可以根据指定的条件来搜索文件,如文件名、文件大小、权限等。例如,如果......
  • 后端开发之SpringBootWeb入门介绍及简单测试 2024详解
    SpringBoot介绍官网spring.ioSpring是最流行的Java框架Spring发展到今天已经形成了一种开发生态圈Spring提供了若干个子项目每个项目用于完成特定的功能企业开发框架之间的整合会很容易所以我们选择Spring全家桶基于基础的SpringFramework框架但是配置繁琐入门......
  • Linux之文件查找
    一.find  路径  条件  动作1.按文件名查找eg:find/etc-name"zxy"查找所有以8开头以9结尾的文件eg: find/ -name"8*"-o-name"*9" 2.按文件类型查找find/dev-typef查找普通文件d目录l链接b块设备c字符设备s套接字p管道文件3.按文件大小来......
  • SQL SERVER 从入门到精通 第5版 第二篇 第8章 SQL数据高级查询 读书笔记
     第8章SQL数据高级查询 >.子查询与嵌套查询>.子查询概述:子查询是一个嵌套在SELECT,INSERT,UPDATE和DELETE语句或者其他子查询中的查询,任何允许使用表达式的地方都可以使用子查询.子查询语法规则如下:>.子查询的SELECT查询总使用圆括号......