驱动开发--字符设备驱动

news2024/12/24 9:13:17

目录

1.驱动模块 

hello.c

Makefile

2.内核中的打印函数(编写第一个驱动程序)

Source Insight 使用:

3.打印函数编写

分析

4、驱动的多文件编译

5、模块传递参数

6、安装好驱动之后如何传参?

 7、字符设备驱动

8、字符设备驱动的注册

9、总结归纳:


1.驱动模块 

入口(安装):资源的申请

出口(卸载):资源的释放

许可证:GPL

hello.c

//声明函数

static int  __init hello_init(void) //入口,static :只能在当前程序使用,防止其他驱动重名

{
   return 0;
}

static void __exit hello_exit(void)//出口
{

}
module_init(hello_init);//告诉内核驱动的入口

module_exit(hello_exit);//告诉内核驱动的出口

MODULE_LICENSE("GPL");

Makefile

                     ==/lib/modules/4.15.0-142-generic/build

KERNELDIR:= /lib/modules/$(shell uname -r)/build/              Ubuntu内核目录

#KERNELDIR:= /home/hq/fs6818_uboot/kernel-3.4.39/        开发板内核目录

PWD:=$(shell pwd)

all:

    make -C $(KERNELDIR) M=$(PWD) modules

clean:

    make -C $(KERNELDIR) M=$(PWD) clean

obj-m:=hello.o 

 编写完后执行make编译,sudo insmod hello.ko安装

$make

$sudo insmod hello.ko

$sudo rmmod hello  //卸载

 

2.内核中的打印函数(编写第一个驱动程序)

Source Insight 使用:

1)打开Source Insight,新建项目 Project-->New Project

2)添加自己的工程名与存放路径 ,点击OK

3)选择解压后的内核代码,点击Add All全部添加 

 

3.打印函数编写

分析

--------------------------------------------------------打印级别-----------------------------------------------------

#define KERN_EMERG "<0>" /* system is unusable */

#define KERN_ALERT "<1>" /* action must be taken immediately */

#define KERN_CRIT "<2>" /* critical conditions */

#define KERN_ERR "<3>" /* error conditions */

#define KERN_WARNING "<4>" /* warning conditions */

#define KERN_NOTICE "<5>" /* normal but significant condition */

#define KERN_INFO "<6>" /* informational */

#define KERN_DEBUG "<7>" /* debug-level messages */

       4                        4                                 1                          7

终端的级别          消息的默认级别          终端的最大级别     终端的最小级别     

#define console_loglevel (console_printk[0])

#define default_message_loglevel (console_printk[1])

#define minimum_console_loglevel (console_printk[2])

#define default_console_loglevel (console_printk[3])

------------------------------------------------------打印函数-------------------------------------------------------

printk(KERN_ERR "BFS-fs: %s(): " format, __func__, ## args)

功能:消息打印

参数:

   第一个参数:打印的级别

   第二个参数:打印的内容

   第三个参数:和printf一样,需要打印的参数

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
static int __init hello_init(void)//入口
{
  printk(KERN_ERR "hello word\n");
  return 0;
}
static void __exit hello_exit(void)//出口
{
  printk(KERN_ERR "baibai\n");
}
module_init(hello_init);//告诉内核驱动的入口
module_exit(hello_exit);//告诉内核驱动的出口
MODULE_LICENSE("GPL");

没有打印信息,解决方法如下: 

PM:

方法一:虚拟控制台

       ctrl+alt+F5 或者ctrl+Fn+alt+F5(F1-F5)(进入虚拟控制台)

       ctrl+alt+F7或者ctrl+Fn+alt+F7(退出虚拟控制台)-》有的退出是F2

       sudo insmod  hello.ko(安装驱动)sudo rmmod  hello(卸载驱动)

方法二:在终端输入

  dmesg (查看消息的回显)  dmesg -c  (查看回显并清空)dmesg -C  (清空回显)

 cat /proc/sys/kernel/printk(查看ubuntu终端显示级别 消息默认级别 消息最大级别  消息最小级别)

  su root  echo 4 3 1 7 > /proc/sys/kernel/printk(修改ubuntu终端显示级别 消息默认级别 消息最大级别  消息最小级别)

       echo  4 3 1 7 > /proc/sys/kernel/printk (修改开发板)

       =     赋值 需要等待其他文件全部执行完,才执行调用的

      :=   立即赋值

      +=   附加赋值

      ?= 询问变量之前是否被赋值过,如果赋值过本次赋值不成立,否则成立

4、驱动的多文件编译

hello . c   add . c

 Makefile

 obj-m:=demo.o

 demo-y+=hello.add.o

最终生成demo.ko文件

5、模块传递参数

module_param(name, type, perm) 

功能:接收命令行传递的参数

参数:

name: 变量的名字  type:变量的类型  perm:权限 0664 0775 

name: 变量的名字  type:变量的类型  perm:权限 0664 0775 

#include <linux/init.h>
#include <linux/module.h>
int a=10;
module_param(a,int,0664);                   
static int __init hello_init(void)
{
 printk("sum= %d\n",a);
 return 0;
}
static void __exit hello_exit(void)
{
 printk(KERN_ERR"bai");                
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

 

传递两个参数程序

int  a = 10 ;

module_param(a,int,0664);

int b=10;

module_param(b,int,0664);

问:安装时怎么区分a和b的作用,自己写的知道那如果移植其他厂商的呢比如LCD屏

使用modinfo hello.ko查看

且需要程序里使用如下函数进行配置:

MODULE_PARM_DESC(_parm, desc)

功能:对变量的功能进行描述

参数:@_parm:变量

           @desc :描述字段

int a=10;
module_param(a,int,0664);
MODULE_PARM_DESC(a,"light");//描述信息
short b=123;
module_param(b,short,0664);
MODULE_PARM_DESC(b,"color");
char c='c';
module_param(c,byte,0664);
MODULE_PARM_DESC(c,"light_c");
 static int __init hello_init(void)
{ 
    printk("sum= %d\n",a);
    printk("sum= %d\n",b);
    printk("sum= %c\n",c);                                                                                                                                                                                
return 0;
}
static void __exit hello_exit(void)
 {
  printk(KERN_ERR"bai");
}
 module_init(hello_init);
 module_exit(hello_exit);
 MODULE_LICENSE("GPL");

练习:其他类型

数组传参:

module_param_array(name,type,nump,perm)

功能:接收命令行传递的数组

参数:name:数组名 

           type :数组类型  

           nump:参数的个数,变量的地址 

           perm 权限

int ww[10]={0}; 
int num;
module_param_array(ww,int,&num,0664)
static int __init hello_init(void)
{
    int i;
    for(i=0;i<num;i++)
    {
     printk("ww[%d]=%d\n",i,ww[i]);
    }
}

sudo insmod hello.ko ww=1,2,3,4,5 

 

6、安装好驱动之后如何传参?

  1. lsmod查看驱动名字

2、找路径 /sys/module/驱动模块的名字/parameters

3、修改-》su root-》echo  需要改为多少> 需要修改的参数名

4、cat 需要修改的参数名  (查看是否修改成功)多驱动之间调用(导出符号表)

假如有两个驱动模块,demo和demo2 ,假设demo调用demo2中的函数

cp  -r  demo/  demo2/    先拷贝demo到demo2

mv hello.c  add.c   将demo2中hello.c重命名为add.c

在被调用者add.c中添加代码:

EXPORT_SYMBOL_GPL(add);//导出符号表 

#include <linux/intt.h>
#include <linux/module.h>
int add(int a,int b)
{
 return (a+b);
}
EXPORT_SYMBOL_GPL(add);//导出符号表
static int __init hello_init(void)
{
 return 0;
}
staic int __exit hello_exit(void)
{
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

执行 $make 生成Module.symvers,将其cp到demo调用者中

$vi hello.c打开调用者,添加外部引用。

extern int add ( int  a , int  b );

static int __init hello_init(void)

{

printk("sum = %d\n",add(2,3));

}

编译: make         发现报错,提示add没有定义

 所以在执行之前把add.c里面M开头文件复制到demo下   

cp  Module.symvers  ../demo             然后再make

安装: 先安装提供者 sudo insmod add.ko

            再安装调用者 sudo insmod  hello.ko

查看信息:dmesg

卸载:先卸载 调用者hello.ko  再卸载提供者add.ko

 7、字符设备驱动

8、字符设备驱动的注册  

int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

  功能:注册一个字符设备驱动

参数: @major:主设备号  

            :如果你填写的值大于0,它认为这个就是主设备号

            :如果你填写的值为0,操作系统给你分配一个主设备号   

           @name :名字    cat /proc/devices 查看设备名和主设备号

           @fops :操作方法结构体

返回值:major>0 ,成功返回0,失败返回错误码(负数) (vi -t EIO 可以查看错误码)

              major=0,成功主设备号,失败返回错误码(负数)      

void unregister_chrdev(unsigned int major, const char *name)

功能:注销一个字符设备驱动

参数:@major:主设备号

          @name:名字

返回值:无

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
int major=0;
#define CNAME "hello"
ssize_t mycdev_read (struct file *file, char __user *user, size_t size, loff_t * loff)
{
   printk("this is read");
   return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *user, size_t, loff_t *loff)
{
   printk("this is write");
   return 0;
}
int mycdev_open (struct inode *inode, struct file *file)
{
   printk("this is open");
   return 0;
}
int mycdev_release (struct inode *inode, struct file *file)
{
   printk("this is close");
   return 0;
}
	
const struct file_operations fops={
   .open=mycdev_open,
   .read=mycdev_read,
   .write=mycdev_write,
   .release=mycdev_release,
};

static int __init hello_init(void)//入口
{
  major=register_chrdev(major,CNAME,&fops);
  if(major<0)
  {
   printk("register chrdev error");
  }
  return 0;
}
static void __exit hello_exit(void)//出口
{
  unregister_chrdev(major,CNAME);
}
module_init(hello_init);//告诉内核驱动的入口
module_exit(hello_exit);//告诉内核驱动的出口
MODULE_LICENSE("GPL");

9、总结归纳:

字符设备驱动:

 1、注册驱动register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

major=0  自动分配设备号  name:驱动的名字   fops:结构体

 2、声明结构体-》注册驱动时第三个参数

 3、open、read、write、release-》按照内核的格式自己写的

 4、把自己写的这些函数->给到结构体里-》.open .read .write .release

 5、注销设备驱动

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

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

相关文章

云上VPC网络规划实战

新钛云服已累计为您分享750篇技术干货 什么是VPC 虚拟专有网络&#xff08;Virtual Private Cloud&#xff0c;简称VPC&#xff09;是阿里云提供的一种云上私有网络&#xff0c;为用户提供独立且可控的网络环境。用户可以自主定义VPC的IP地址范围、配置路由表和网关等&#xff…

chatgpt赋能python:Python在Win7上的安装教程

Python在Win7上的安装教程 如果你是一名Win7用户&#xff0c;并且打算开始学习或者使用Python编程语言&#xff0c;那么本文将会为你提供一个简单易懂的Python安装教程。 1. 下载Python 在安装Python之前&#xff0c;你需要先去Python的官方网站&#xff08;https://www.pyt…

chatgpt赋能python:Python输出0到9:从基础到高阶

Python 输出 0 到 9&#xff1a;从基础到高阶 在Python中&#xff0c;输出0到9这样的数字非常简单&#xff0c;你可以使用内置的range()函数或循环进行实现。在本篇文章中&#xff0c;我们将介绍几种不同的方法来输出0到9的数字。 使用range()函数输出0到9 range()函数是Pyt…

夜天之书 #84 国产开源社群的运营,为何总是画风奇特?

在过去几年的投入和关注下&#xff0c;国产开源社群如雨后春笋一般冒了出来。今天&#xff0c;以 GPT 为首的 AI 新势力接过话题度的接力棒&#xff0c;我们可以在降温周期里回顾一下过去几年间冒出来的国产开源社群都有什么样的成绩&#xff0c;有些什么样共性的问题可以改进。…

苹果宣布最新操作系统:visionOS

今天凌晨&#xff0c;WWDC23 全球开发者大会正式开幕。 大会上&#xff0c;苹果展示了包括 iOS 17、iPadOS 17、watchOS 10 和 macOS Sonoma 在内的新系统。硬件方面&#xff0c;苹果发布了 15 英寸的 MacBook Air、搭载 M2 Ultra 的 Mac Studio 以及 Mac Pro。 此外&#xff0…

sqlserver练习----涉及多个表的连接查询

等值联接 多表查询语句中的连接条件使用的是等号,例&#xff1a;Student.SnoSC.Sno 例&#xff1a; Student 学号 Sno 姓名 Sname 性别 Ssex 年龄 Sage 所在系 Sdept 202015121李勇男20CS202015122刘晨女10 CS 202015123 王敏女18 MA 202015125张力男19IS SC: 学号 Sn…

秋招面试腹稿

1、自我介绍 你好&#xff0c;我叫熊志君&#xff0c;是就读于电子信息专业的24届研究生。在校期间获得过两次一等奖学金、两次省级竞赛一等奖&#xff0c;英语过了6级&#xff0c;我的研究方向是水下slam多传感器融合方向&#xff0c;用过c/c/python三种编程语言。 2、系统移植…

如何缓解高考前紧张的情绪,ChatGPT这么说......

明天就要高考了&#xff0c;看到家长有各种打气的做法&#xff0c;既有上灵隐寺的&#xff0c;也有穿着旗袍希望旗开得胜的&#xff0c;还有说什么失败了不要紧的......&#xff0c;反正都是焦虑的不行。 面对高考&#xff0c;大多考生都会紧张&#xff0c;但适度的紧张对发挥出…

解码器 | 基于 Transformers 的编码器-解码器模型

基于 transformer 的编码器-解码器模型是 表征学习 和 模型架构 这两个领域多年研究成果的结晶。本文简要介绍了神经编码器-解码器模型的历史&#xff0c;更多背景知识&#xff0c;建议读者阅读由 Sebastion Ruder 撰写的这篇精彩 博文。此外&#xff0c;建议读者对 自注意力 (…

Mocha AE:AdjustTrack 模块

跟踪时由于缺乏细节或有障碍物阻挡&#xff0c;跟踪点会发生漂移&#xff0c;此时可考虑使用 AdjustTrack &#xff08;调整跟踪&#xff09;模块手动设置表面区域 Planar Surface关键帧来获得更可靠的表面跟踪数据。 但是&#xff0c;如果需要设置较多的关键帧时&#xff0c;建…

Linux计划任务

常见的计划任务&#xff1a;进行日志的轮替&#xff08;log rotate&#xff09;&#xff1b;日志文件分析&#xff08;logwatch&#xff09;任务&#xff1b;建立locate数据库&#xff1b;man page查询数据库的建立&#xff1b;RPM软件登录文件的建立&#xff1b;移除暂存档&am…

尺度悖论解析费米悖论:从夜郎自大到揭秘宇宙中智慧生命的谜团

费米悖论是一个引人入胜的问题&#xff0c;它引发了人们对宇宙中是否存在其他智慧生命体的思考。然而&#xff0c;尺度悖论提供了一个可能的解释角度&#xff0c;即我们对宇宙的观测和推断尺度可能太小&#xff0c;无法涵盖整个宇宙范围。下面深入探讨尺度悖论以及费米悖论的具…

Linux系统一般用来干嘛

Linux系统是一种开源的操作系统&#xff0c;广泛应用于服务器、嵌入式设备、超级计算机等领域。它具有高度的稳定性、安全性和灵活性&#xff0c;可以用来进行各种各样的任务&#xff0c;例如&#xff1a; 1、服务器操作系统 Linux系统在服务器领域应用广泛&#xff0c;可以用…

Maven继承

Maven 在设计时&#xff0c;借鉴了 Java 面向对象中的继承思想&#xff0c;提出了 POM 继承思想。 当一个项目包含多个模块时&#xff0c;可以在该项目中再创建一个父模块&#xff0c;并在其 POM 中声明依赖&#xff0c;其他模块的 POM 可通过继承父模块的 POM 来获得对相关依赖…

微信小程序自定义导航栏

微信小程序自定义导航栏 业务需求&#xff1a; 点击小房子进行跳转指定的页面 、更改小房子的样式、或者是自定义导航栏 首先我们需要找到pages.json这个文件 如果是原生的微信小程序文件名字是 app.json其实就是找到配置路由的文件在代码里面添加属性"navigationStyle&qu…

java设计模式(十四)模板方法

目录 定义模式结构角色职责代码实现适用场景优缺点 定义 模板方法模式(Template Method Pattern)&#xff0c;又叫模板模式(Template Pattern)&#xff0c; 指在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现&#xff0c;但调用将以抽象类中定义的…

1. Hadoop 入门

1. Hadoop 入门 1. 大数据概述 1. 大数据相关说明 大数据由来&#xff1a; 传统数据处理应用软件不足以处理&#xff08;存储和计算&#xff09;它们大而复杂的数据集 大数据面临的两大问题&#xff1a; 针对海量数据的 存储、计算 大数据的特性&#xff1a;容量大、种类多…

VFP使用BLOB字段存取图片到SQL2000,显示出来也EASY

首先来看一下BLOB这个数据类型的介绍&#xff1a; 大二进制对象(Blob)数据类型&#xff0c;若要存储一个任何种类的二进制数据&#xff0c;如 ASCII 码文本、一个可执行文件(.exe) 或一个带有不确定长度的字节字符串&#xff0c;可使用大二进制对象数据类型。对于从 SQL Serve…

c++11 标准模板(STL)(std::bitset)(六)

定义于头文件 <bitset> template< std::size_t N > class bitset; 类模板 bitset 表示一个 N 位的固定大小序列。可以用标准逻辑运算符操作位集&#xff0c;并将它与字符串和整数相互转换。 bitset 满足可复制构造 (CopyConstructible) 及可复制赋值 (CopyAssign…

港科夜闻|海南省教育厅党委书记曹献坤到访香港科大(广州)开展实地调研

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、海南省教育厅党委书记曹献坤到访香港科大(广州)开展实地调研。香港科大(广州)临时党委书记屈哨兵从政治建设、思想建设、组织建设、制度建设及工作机制等方面&#xff0c;为曹献坤书记详细介绍了学校的党建工作体系构建&…