字符设备驱动

字符设备驱动开发的流程 字符设备驱动开发,开发流程,驱动框架,具体实现步骤。 阅读详情

字符设备驱动程序是由一个cdev结构描述的,其定义为:
 struct cdev {
      struct kobject kobj;//内嵌的kobject
         struct module *owner;
         const struct file_operations *ops;
         struct list_head list;//与字符设备文件对应的索引节点链表的头,该链表用于收集相同字符设备驱动程序
         //所对应的设备文件的索引节点。
         dev_t dev;//初始主设备号和次设备号
         unsigned int count;//设备号的范围大小
 };
内核提供了cdev_alloc()函数,用于动态的分配cdev描述符,并初始化内嵌的kobject数据结构,因此引用计数器的值变为0
时会自动释放该描述符。
struct cdev *cdev_alloc(void)
 {
         struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
         if (p) {
                 INIT_LIST_HEAD(&p->list);
                 kobject_init(&p->kobj, &ktype_cdev_dynamic);
         }
         return p;
 }
在添加进系统之前则要调用cdev_init()函数来对cdev进行初始化。
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
        memset(cdev, 0, sizeof *cdev);
        INIT_LIST_HEAD(&cdev->list);
        kobject_init(&cdev->kobj, &ktype_cdev_default);
        cdev->ops = fops;
}   
cdev_add()函数向系统添加一个cdev字符设备。
 int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
     p->dev = dev;
     p->count = count;
  return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
使用alloc_chrdev_region()和register_chrdev_region()函数为驱动分配任意范围内的设备号。
使用register_chrdev()函数分配一个固定的设备号范围.在这种情形下,设备驱动程序不必调用cdev_add()函数。
unregister_chrdev_region(dev_t from,unsigned count)
<b>2.常见函数</b>
copy_to_user(buf, &global_var, sizeof(int));
copy_from_user(&global_var, buf, sizeof(int));
可以通过filp->private_data传递私有数据。
3.驱动中的并发操作
(1)屏蔽中断
local_irq_disable() ;          //屏蔽中断
local_irq_enabale();     //打开中断
只能禁止和使能本CPU的中断,所以并不能解决SMP引发的竞争。
(2)原子操作
// 设置原子变量的值
void atomic_set(atomic_t *v, int i);  // 设置原子变量的值为i
atomic_t v = ATOMIC_INIT(0);  // 定义原子变量v,并初始化为0
// 获取原子变量的值
atomic_read(atomic_t *v);  // 返回原子变量的值
// 原子变量加/减
void atomic_add(int i, atomic_t *v);  // 原子变量加i
void atomic_sub(int i, atomic_t *v);  // 原子变量减i
// 原子变量自增/自减
void atomic_inc(atomic_t *v);  // 原子变量增加1
void atomic_dec(atomic_t *v);  // 原子变量减少1
 // 操作并测试:对原子变量进行自增、自减和减操作后(没有加)测试其是否为0,为0则返回true,否则返回false
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
// 操作并返回: 对原子变量进行加/减和自增/自减操作,并返回新的值
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
  位原子操作:
// 设置位
void set_bit(nr, void *addr);  // 设置addr地址的第nr位,即将位写1
// 清除位
void clear_bit(nr, void *addr);  // 清除addr地址的第nr位,即将位写0
// 改变位
void change_bit(nr, void *addr);  // 对addr地址的第nr位取反
// 测试位
test_bit(nr, void *addr); // 返回addr地址的第nr位
// 测试并操作:等同于执行test_bit(nr, void *addr)后再执行xxx_bit(nr, void *addr)
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
(3)自旋锁(spin lock)——“在原地打转”
// 定义自旋锁
spinlock_t spin;
// 初始化自旋锁
spin_lock_init(lock);
// 获得自旋锁:若能立即获得锁,它获得锁并返回,否则,自旋,直到该锁持有者释放
spin_lock(lock);
// 尝试获得自旋锁:若能立即获得锁,它获得并返回真,否则立即返回假,不再自旋
spin_trylock(lock);
// 释放自旋锁: 与spin_lock(lock)和spin_trylock(lock)配对使用
spin_unlock(lock);
自旋锁可以保证临界区不受别的CPU和本CPU内的抢占进程打扰,但是得到锁的代码路径在执行临界区的时候还可能受到中断和底半部(BH)的影响。
为防止这种影响,需要用到自旋锁的衍生:
spin_lock_irq() = spin_lock() + local_irq_disable()
spin_unlock_irq() = spin_unlock() + local_irq_enable()
spin_lock_irqsave() = spin_lock() + local_irq_save()
spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()
spin_lock_bh() = spin_lock() + local_bh_disable()
spin_unlock_bh() = spin_unlock() + local_bh_enable()
(4)信号量
当获取不到信号量时,进程不会原地打转而是进入休眠等待状态。
// 定义信号量
struct semaphore sem;
// 初始化信号量:
// 初始化信号量,并设置sem的值为val
void sema_init(struct semaphore *sem, int val);
// 初始化一个用于互斥的信号量,sem的值设置为1。等同于sema_init(struct semaphore *sem, 1)
void init_MUTEX(struct semaphore *sem);
// 等同于sema_init(struct semaphore *sem, 0)
void init_MUTEX_LOCKED(struct semaphore *sem);
// 下面两个宏是定义并初始化信号量的“快捷方式”:
DECLEAR_MUTEX(name)
DECLEAR_MUTEX_LOCKED(name)
//  获得信号量:
//  用于获得信号量,它会导致睡眠,不能在中断上下文使用
void down(struct semaphore *sem);
// 类似down(),因为down()而进入休眠的进程不能被信号打断,而因为down_interruptible()而进入休眠的进程能被信号打断,
// 信号也会导致该函数返回,此时返回值非0
void down_interruptible(struct semaphore *sem);
// 尝试获得信号量sem,若立即获得,它就获得该信号量并返回0,否则,返回非0.它不会导致调用者睡眠,可在中断上下文使用
int down_trylock(struct semaphore *sem);
//  使用down_interruptible()获取信号量时,对返回值一般会进行检查,若非0,通常立即返回-ERESTARTSYS
// 释放信号量
// 释放信号量sem, 唤醒等待者
void up(struct semaphore *sem);
(5)完成量(completion)提供了一种比信号量更好的同步机制,它用于一个执行单元等待另一个执行单元执行完某事。
completion相关操作:
// 定义完成量
struct completion my_completion;
// 初始化completion
init_completion(&my_completion);
// 定义和初始化快捷方式:
DECLEAR_COMPLETION(my_completion);
// 等待一个completion被唤醒
void wait_for_completion(struct completion *c);
// 唤醒完成量
void cmplete(struct completion *c);
void cmplete_all(struct completion *c);
3.阻塞和非阻塞
(1)阻塞
使用等待队列实现阻塞I/O。信号量在内核中就是依赖等待队列来实现的。
/定义初始化‘等待队列头’
Wait_queue_head_t  my_queue;
Init_wait_queue_head(&my_queue);
DECLARE_WAIT_QUEUE_HEAD (name);
//定义并初始化一个等待队列
DECLARE_WAITQUEUE(name, tsk);
//将等待队列添加到等待队列头Q指向的等待队列链表中
Void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
//将等待队列从等待队列头q指向的等待队列链表中移除
Void fastcall remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
//等待事件,queue为等待队列头,timeout以jiffy为单位
Wait_event(queue, condition);
Wait_event_interruptible(queue, condition);
Wait_event_timeout(queue, condition, timeout);
Wait_event_interruptible_timeout(queue, condition, timeout);
//唤醒队列
Void wake_up(wait_queue_head_t *q);
Void wake_up_interruptible (wait_queu_head_t *q);
注意:wake_up可唤醒wait_event或者wait_event_interruptible,但是    wake_up_interruptible只能唤醒wait_event_interruptible。
//在等待队列上睡眠
Sleep_on(wait_queue_head_t *q);
Interruptible_sleep_on(wait_queue_head_t *q);
注意:sleep_on或者interruptible_sleep_on函数的作用就是将目前进程的状态设置成TASK_UNINTERRUPTIBLE或者INTERRUPTIBLE。并定义
一个等待队列,之后把它附属到等待队列头q,直到资源可获得,q引导的队列别唤醒或者接到信号。Sleep_on函数应该与wake_up成对使用,
interruptible_sleep_on应该与wake_up_interruptble 成对使用。
set_current_state()(设置当前进程的运行状态)和signal_pending();current代表当前进程。
(2)非阻塞使用select()轮询
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
struct fd_set为一个集合,存放的是文件描述符(filedescriptor),毫无疑问一个socket就是一个文件描述符。
fd_set操作,比如:
FD_ZERO(fd_set *); 清空集合
FD_SET(int, fd_set *); 将一个给定的文件描述符加入集合之中
FD_CLR(int, fd_set*); 将一个给定的文件描述符从集合中删除
检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。

struct timeval 是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒。 具体解释select的参数:
int maxfdp:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!
fd_set* readfds:是指向fd_set结构的指针,监视这些文件描述符的读变化的,可以传入NULL值,表示不关心任何文件的读变化。
fd_set* writefds:是指向fd_set结构的指针,监视这些文件描述符的写变化的。可以传入NULL值,表示不关心任何文件的写变化。
fd_set * errorfds:同上面两个参数的意图,用来监视文件错误异常。
struct timeval* timeout:是select的超时时间,这个参数至关重要,它可以使select处于三种状态:
若将NULL以形参传入,就将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了。
select 返回值:
负值:select错误
正值:某些文件可读写或出错
0:等待超时,没有可读写或错误的文件

深入理解Linux字符设备驱动 文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次、组成框架和交互、如何编写驱动、设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解。 阅读详情

相关推荐

字符设备驱动开发

字符设备驱动开发

m0_69211839的博客 2533

Linux字符设备驱动入门(一)

先亮一下装备: 平台:VMware 7.0 + Linux ubuntu 3.0.0-12-generic 编译器:gcc 参考资料:LDD 3 功能:实现简单的字符操作(从用户空间向内核空间写入一串字符;从内核空间读一个字符到内核空间)         众所周知,字符设备是linux下最基本,也是最常用到的设备,它是学习Linux驱动入门最好的选择,计算机的东西很多都是相通的,掌握

My embedded world 5825

Nuttx 字符设备驱动

Nuttx采用VFS,和linux一样的设计思路,一切设备皆文件。Nuttx系统可以在nsh终端下通过”ls /dev”查看系统已注册的设备。对设备的操作就是对文件的操作。Nuttx下的设备驱动就是实现这种对文件操作的接口,屏蔽了对设备本身的访问的复杂性,呈现简单的接口,如open(), read(), write()等。字符设备是指在I/O传输过程中以字符为单位进行传输的设备,如键盘、鼠标、以及一

chengwenyang的专栏 4666

虚拟文件系统VFS的内核数据结构及联系

VFS: Linux支持各种各样的文件系统格式,如ext2、ext3、reiserfs、FAT、NTFS、iso9660等等,不同的磁盘分区、光盘或其它存储设备都有不同的文件系统格式,然而这些文件系统都可以mount到某个目录下,使我们看到一个统一的目录树,各种文件系统上的目录和文件我们用ls命 令看起来是一样的,读写操作用起来也都是一样的,这是怎么做到的呢?Linux内核在各种不同的文件系统格

heiworld的专栏 1018

Transformer是什么?看完这篇你就醍醐灌顶

前言 由谷歌团队提出的预训练语言模型BERT近年来正在各大自然语言处理任务中屠榜(话说学者们也挺有意思的,模型名都强行凑个芝麻街的人物名,哈哈哈)。 BERT算法的最重要的部分便是Transformer的概念,它本质上是Transformer的编码器部分。 而Transformer是什么呢?transformer是永远的神,自从transformer使用了抛弃rnncnn的纯attention机制之后,各种基于transformer结构的预训练模型都如雨后春笋般雄起,而transformer中最重要就是使用

fs1341825137的博客 6万+

Delta并联机构运动学正逆解:从理论建模到Matlab实战

本文深入解析了Delta并联机构运动学正逆解的核心原理与实战方法。通过几何建模,清晰阐述了其“逆解简单、正解复杂”的特性,并详细推导了基于一元二次方程的逆解解析算法与采用牛顿-拉弗森迭代法的正解数值算法。文章结合完整的Matlab代码实例,从符号推导到数值求解,提供了从理论到实践的完整实现路径,助力机器人开发者掌握Delta并联机构的运动学分析。

weixin_29096377的博客 432

linux中文件描述符fd和文件指针flip的理解

linux中文件描述符fd和文件指针flip的理解 简单归纳:fd只是一个整数,在open时产生。起到一个索引的作用,进程通过PCB中的文件描述符表找到该fd所指向的文件指针filp。 文件描述符的操作(如: open)返回的是一个文件描述符,内核会在每个进程空间中维护一个文件描述符表, 所有打开的文件都将通过此表中的文件描述符来引用;  而流(如: fopen)返回的是

woxinyijiuw的专栏 910

无源低通滤波器

无源RC滤波器“滤除”不需要的信号,因为它们分离并允许仅通过其频率的正弦输入信号,最简单的是无源低通滤波器网络。在低频应用(高达100kHz)中,无源滤波器通常使用简单的RC(电阻-电容)网络构建,而高频滤波器(高于100kHz)通常由RLC(电阻-电感-电容)元件制成。无源滤波器由电阻器、电容器和电感器等无源元件组成,没有放大元件(晶体管、运算放大器等),因此没有信号增益,因此它们的输出电平始终小于输入。滤波器是根据它们允许通过它们的信号的频率范围来命名的,同时阻挡或“衰减”其余部分。

weixin_44448586的博客 1万+

【Linux驱动】字符设备驱动

字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI,LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。编写一个驱动程序的时候需要调用上层驱动函数的时候需要调用底层实现,并且底层需要留给上层接口,并暴露出去。Linux内核文件中有一个叫做。#endif上层程序使用open。

Julian的博客 1174

【Unity动画系统】详解Root Motion动画在Unity中的应用(一)

什么时候勾选Bake into pose,当不希望动画带动游戏对象旋转时。在旋转的动画中一般是不用勾选的,因为就需要用它来带动我们的角色对象旋转。但在比如前进、后退、待机等动画来说,我们就可以勾选这里,以避免因动画文件本身的瑕疵而给我们带来的旋转。loop match:代表了这个动画在角度上的吻合度。如果这个动画在播放中转角过大,那么就会显示红色,告诉我们如果勾选bake into pose,则会带来不好的效果。

weixin_60154963的博客 6802

反应动力学仿真软件:Kintecus_(12).高级功能与技巧

Kintecus允许用户自定义复杂的反应方程,包括非线性反应和多相反应。通过自定义反应方程,用户可以更准确地描述实际反应系统。多尺度仿真是指在同一模型中同时考虑不同时间和空间尺度的反应过程。Kintecus提供了工具来处理从微观分子动力学到宏观反应动力学的多尺度仿真。通过本节的学习,您已经掌握了Kintecus的高级功能与技巧,包括复杂反应网络的构建、参数优化与敏感性分析、自定义反应机制、多尺度仿真、并行计算与批处理,以及数据可视化与后处理。

kkchenjj的博客 1047

字符设备驱动实验

一、字符设备基础 字符设备:是指只能一个字节一个字节进行读写操作的设备,不能随机读取设备中的某一数据、读取数据要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED等。 一般每个字符设备或者块设备都会在/dev目录(可以是任意目录,这样是为了统一)下对应一个设备文件。linux用户层程序通过设备文件来使用驱动程序操作字符设备或块设备 二、驱动设备基础 驱动...

Wanggj的博客 3789

工业预测性维护实战:振动分析+Transformer预警系统构建指南

摘要:本文聚焦工业设备预测性维护难题,提出基于振动分析与Transformer模型的全栈解决方案。通过低成本硬件选型(单传感器¥380)实现设备振动信号实时采集,利用边缘计算单元完成特征工程(RMS、峰度、峰值频率提取),并在云端部署Transformer时序模型进行异常预警。某风电龙头企业实践显示,系统将故障误报率从38%降至6%,维护成本降低58%,非计划停机时长减少87%。文中提供完整的边缘端特征提取代码、云端模型架构及实施流程,适配ISO 13373振动分析标准,为制造业设备健康管理提供可复用的工程

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 625

【Linux 驱动篇(一)】字符设备驱动开发

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

K3169的博客 5665

C#与大数据分析:如何通过上位机进行设备故障诊断与预测

设备故障诊断是通过对设备的运行状态数据进行分析,识别和确认故障的类型和位置。而设备故障预测则是通过对设备的历史数据和运行状态进行分析,预测设备何时可能出现故障,从而进行预防性维护。数据采集:通过各种传感器、PLC、嵌入式设备等采集设备运行数据,如温度、压力、振动、转速、功率等。数据预处理:对采集到的数据进行清洗、去噪、缺失值处理、标准化等操作,以确保数据质量。特征工程:从原始数据中提取出有助于故障诊断和预测的特征,例如统计特征、时域特征、频域特征等。数据分析与建模。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1060

AUTOSAR进阶图解==>AUTOSAR_SWS_COM

AUTOSAR COM模块是AUTOSAR通信堆栈的核心组件,提供信号导向的通信服务。该模块位于通信服务层,连接RTE与PDU Router,主要功能包括信号封装/提取、传输模式管理、信号路由、超时监控和字节序转换等。COM模块架构分为应用层、通信服务层和通信抽象层,包含信号管理、I-PDU处理、传输模式管理等组件。其配置结构包括顶层容器、I-PDU组、I-PDU和信号等多层次配置对象,支持丰富的参数设置。模块采用严格的状态管理机制,包括模块状态和I-PDU组状态转换,确保通信可靠性。

一名留德华的,康德的信徒的,专注于汽车电子领域的博客 1242

嵌入式linux驱动开发-字符设备驱动

驱动字符设备驱动简介字符设备驱动开发步骤驱动模块的加载和卸载 之前在嵌入式系统移植停留了一段时间,枯燥地看uboot、kernel和跟文件系统的源码感觉没有成就感,在这之前已经对嵌入式linux的启动流程有了大体的了解,因此先跳到驱动开发,体验学习一下子哈!!。 Linux 中的三大类驱动:字符设备驱动、块设备驱动和网络设备驱动。 字符设备驱动:IO口的驱动,比如点灯、 I2C、 SPI、音频等。 块设备驱动:存储器设备的驱动,比如 EMMC、 NAND、 SD 卡和 U 盘等存储设备 网络设备驱动:网络驱

平平酱的博客 3651

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

linux驱动——字符设备驱动总结

定义一个目标设备结构体,这个结构体中包含如下变量待分配的设备号dev_t;该字符设备对应的struct cdev结构体;设备类class;struct device设备结构体主次设备号整型变量int major;int minor;定义文件操作函数file_operations结构体。自定义实现设备需要的操作函数实现驱动入口函数xxx_init,在驱动入口函数完成具体字符设备的初始化,譬如相关寄存器位的设置。然后开始注册字符设备驱动。。。。1、

weixin_46066367的博客 910

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

字符设备驱动—1

字符设备通常缩写为cdev,它是不可寻址的,仅提供数据的流式访问,通过字符(一个接一个的字符)以字节流方式向用户程序传递数据。字符设备驱动通过/dev目录下的特殊文件公开设备的属性和功能,通过这个文件可以在设备和用户程序之间进行数据的交换。

m0_72622181的博客 1760

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Linux 字符设备驱动开发模板

介绍字符驱动开发的基本模式

安迪西_嵌入式攻城狮 3699

Linux驱动——字符设备驱动

1.使用什么工具来写驱动代码:安装source insight工具(1)找到软件提示把工具安装激活(2)把Linux内核代码解压到windows目录中(3)打开工具添加查看的项目第一个对话框:第一个文本框:输入工程名字第二个文本框:路径(默认)第二个对话框: 在项目源码位置,选择 Linux内核代码位置 点击OK 第三个对话框: 选择需要查看的Linux内核代码的包含的源码文件(要查看的内核目录)需要添加的目录:driver/iicinclude。

Linux驱动开发工程师,熟悉Linux/Android底层驱动、TP、显示、IIC、网络等通用外设调试适配,Linux上层应用开发。常用平台瑞芯微、全志、恩智浦等。 3439

字符设备驱动基础知识

一、字符设备结构体(cdev) 在Linux内核中, 使用cdev结构体来描述一个字符设备。 struct cdev { struct kobject kobj; //内嵌的kobject对象 struct module *owner;//所属模块 const struct file_operations *ops; //文件操作结构体 struct list_head list; de...

嵌入式软件开发交流 2万+
上一篇: linux中.config,makefile,kconfig
下一篇: 中断/IO/DMA
快乐的小鸟
博客等级 码龄14年 25粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值