Linux驱动开发——platform平台总线

bus_type

一、主要作用

  1. 设备管理

    • bus_type负责管理连接在特定总线上的设备。它维护一个设备链表,其中包含了所有注册到该总线上的设备。通过这个链表,内核可以方便地遍历和管理连接在该总线上的设备。
    • 例如,对于 PCI 总线,bus_type会管理所有连接在 PCI 总线上的设备,包括显卡、网卡、声卡等。
  2. 驱动匹配

    • bus_type在设备和驱动程序之间进行匹配。当一个设备注册到总线上时,bus_type会尝试将其与注册在该总线上的驱动程序进行匹配。如果找到了匹配的驱动程序,就会调用驱动程序的探测函数来初始化设备。
    • 例如,当一个新的 USB 设备插入系统时,USB 总线的bus_type会查找已注册的 USB 驱动程序,看是否有能够支持该设备的驱动。如果找到了匹配的驱动,就会调用驱动程序的探测函数来初始化设备,使设备能够正常工作。

二、结构组成

  1. 名称和属性

    • bus_type包含了总线的名称和一些属性标志。总线的名称用于标识不同类型的总线,例如 “pci” 表示 PCI 总线,“usb” 表示 USB 总线等。属性标志可以表示总线的一些特性,例如是否支持热插拔、是否支持电源管理等。
  2. 设备和驱动注册函数

    • bus_type提供了设备和驱动注册的函数指针。这些函数用于将设备和驱动程序注册到总线上。例如,bus_register函数用于注册总线,device_register函数用于注册设备,driver_register函数用于注册驱动程序。
  3. 匹配函数

    • bus_type包含了一个匹配函数指针,用于在设备和驱动程序之间进行匹配。这个匹配函数通常会比较设备和驱动程序的一些属性,例如设备的 ID、驱动程序支持的设备类型等,以确定它们是否匹配。

三、使用示例

以 PCI 总线为例,当一个 PCI 设备插入系统时,内核会调用 PCI 总线的bus_type中的设备注册函数将设备注册到 PCI 总线上。然后,内核会遍历已注册的 PCI 驱动程序,通过bus_type中的匹配函数来查找能够支持该设备的驱动程序。如果找到了匹配的驱动程序,就会调用驱动程序的探测函数来初始化设备。

bus_type与platform的关系

两者都是 Linux 内核设备驱动模型的重要组成部分,目的都是实现设备与驱动程序的有效管理和匹配,使得硬件设备能够正常工作。

bus_type主要针对传统的硬件总线,管理那些遵循特定总线协议的设备。例如 PCI 总线上的设备通常有明确的总线规范和配置方式。

platform则适用于一些不直接依附于传统硬件总线的设备,如一些定制的硬件模块或嵌入式系统中的特定设备。

platform平台总线

一、平台总线的作用

平台总线是一种虚拟的总线,用于连接那些不直接依附于传统硬件总线(如 PCI、USB 等)的设备。它为这些设备提供了一种统一的管理和交互方式,使得设备驱动程序能够以一种相对独立的方式进行开发和注册。

二、平台设备(platform_device)

  1. 定义与属性

    • 平台设备通常用struct platform_device结构体来描述。这个结构体包含了设备的名称、设备 ID、所使用的硬件资源(如内存地址范围、中断号等)以及设备的释放函数等信息。
    • 例如,设备的名称用于与驱动程序进行匹配,当驱动程序的名称与设备名称相同时,内核会尝试进行设备与驱动的匹配操作。
  2. 注册与管理

    • 平台设备可以通过platform_device_register函数注册到内核中,此时内核会将其添加到平台总线管理的设备链表中。
    • 一旦注册成功,内核就可以根据设备的属性进行资源分配和管理,并在合适的时候与相应的驱动程序进行匹配。

三、平台驱动(platform_driver)

  1. 定义与属性

    • 平台驱动用struct platform_driver结构体来描述。它包含了驱动程序的探测函数(probe)、移除函数(remove)以及驱动程序的名称等属性。
    • 探测函数在设备与驱动程序匹配成功后被调用,用于初始化设备并建立设备与驱动程序之间的关联。移除函数则在设备从系统中移除时被调用,用于执行清理操作。
  2. 注册与匹配

    • 平台驱动可以通过platform_driver_register函数注册到内核中,此时内核会将其添加到平台总线管理的驱动链表中。
    • 当内核在设备链表和驱动链表中进行查找时,如果发现一个设备的名称与一个驱动程序的名称相同,就会尝试进行匹配。如果匹配成功,内核会调用驱动程序的探测函数来进一步确认设备的兼容性,并进行设备的初始化操作。

四、应用场景

平台总线和相关的设备驱动架构在嵌入式系统和各种特定硬件设备的驱动开发中非常常见。例如,在嵌入式系统中,一些定制的硬件设备可能没有标准的硬件总线接口,这时就可以使用平台总线来实现设备的驱动和管理。此外,一些简单的外设设备,如 GPIO、UART 等,也可以通过平台总线进行驱动开发,提高代码的可移植性和可维护性。

完整代码

下面的代码是platform总线控制ADC驱动的示例

adc_device.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/platform_device.h>//device里只保留设备资源
#define DEV_NAME "adc"
#define ADCCON  0x58000000
#define ADCDAT0 0x5800000C
#define CLKCON  0x4C00000C
#define IRQ_NUM IRQ_ADC//资源结构体数组赋值
static struct resource res[] = 
{[0] = {.start = ADCCON,		//资源的起始地址.end = ADCCON + 4 - 1,  //资源的结束地址,表示从0x58000000到0x58000003,共四个字节.name = "adccon",		//资源的名称.flags = IORESOURCE_IO  //设备的类型和属性,IO端口资源},[1] = {.start = ADCDAT0,.end = ADCDAT0 + 4 - 1,.name = "adcdat0",.flags = IORESOURCE_IO},[2] = {.start = CLKCON,.end = CLKCON + 4 - 1,.name = "clkcon",.flags = IORESOURCE_IO},[3] = {.start = IRQ_NUM,.end = IRQ_NUM,.name = "irq_adc",.flags = IORESOURCE_IRQ		//中断资源}
};static void release(struct device* dev)
{//关闭设备硬件资源或取消注册中断printk("adc_device release ... \n");
}//该结构体表示平台设备,描述硬件设备基本信息
static struct platform_device dev = 
{.name = DEV_NAME,		//设备名,用于与驱动程序进行匹配.id = -1,				//设备id,区分相同名称下的不同设备,-1为默认值,表示通用的,不需要特定的id来区分.dev =					//struct device 包含设备基本属性和操作{.release = release	//指定设备释放时要调用的函数},.num_resources = sizeof(res) / sizeof(res[0]),  //资源数组的元素个数.resource = res			//资源结构体数组,描述设备使用的硬件资源
};static int __init adc_device_init(void)
{//向内核注册平台设备int ret = platform_device_register(&dev);if( ret < 0 )goto err_platform_device_register;printk("platform_device_register ...\n");return 0;err_platform_device_register:printk("platform_device_register faied\n");platform_device_unregister(&dev);return ret;
}static void __exit adc_device_exit(void)
{platform_device_unregister(&dev);printk("platform_device_unregister ...\n");
}module_init(adc_device_init);
module_exit(adc_device_exit);
MODULE_LICENSE("GPL");

adc_driver.c

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/string.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>//driver里把描述资源的去掉
#define DEV_NAME "adc"
static wait_queue_head_t wq;
static int condition = 0;static volatile unsigned long* adccon;
static volatile unsigned long* adcdat0;
static volatile unsigned long* clkcon;#define ADC_MAGIC_NUM 'x'	//幻数:代表不同的设备
#define SET_CHANNEL 1		//命令编号,这里表示要设置通道
#define CMD_SET_CHENNEL _IOW(ADC_MAGIC_NUM, SET_CHANNEL, unsigned int)	//组合结果static struct work_struct work;static void work_handler(struct work_struct * work)
{ssleep(1);condition = 1;wake_up_interruptible(&wq);printk("workqueue_handler\n");
}static irqreturn_t irq_handler(int irq_num, void* dev)
{schedule_work(&work);printk("irq_handler  irq_num = %d\n",irq_num);return IRQ_HANDLED;
}static inline void init_adc(void)
{*adccon = (1 << 14) | (19 << 6);
}static inline void adc_start(void)
{*adccon |= (0x1 << 0);
}static inline unsigned short adc_read(void)
{unsigned short value = *adcdat0 & 0x3ff;return value;	 
}//设置输入通道
static inline void set_channel(unsigned char num)
{*adccon &= ~(0x7 << 3);*adccon |= (num << 3);
}static int open(struct inode * node, struct file * file)
{init_adc();printk("adc open ...\n");return 0;
}static ssize_t read(struct file * file, char __user * buf, size_t len, loff_t * offset)
{unsigned short data = 0;size_t len_cp = (sizeof(data) < len) ? sizeof(data) : len;condition = 0;adc_start();wait_event_interruptible(wq, condition);data = adc_read();copy_to_user(buf, &data, len_cp);//printk("adc read ...\n");return len_cp;
}//cmd作为特定的命令标识符,用于指示要执行的具体设备特定操作.
//arg作为命令的参数传递给驱动程序
static long ioctl(struct file * file, unsigned int cmd, unsigned long arg)
{unsigned char num = 0;if(CMD_SET_CHENNEL == cmd)	//传进来的命令是否是设置通道的{num = arg;				//设置通道的参数,设置通道几set_channel(num);}elsereturn -EINVAL;			//返回-1,在应用层打印错误号return 0;
}static int close(struct inode * node, struct file * file)
{printk("adc close ...\n");return 0;
}static struct file_operations fops = 
{.owner = THIS_MODULE,.open = open,.read = read,.unlocked_ioctl = ioctl,.release = close
};static struct miscdevice misc = 
{.minor = MISC_DYNAMIC_MINOR,.name = DEV_NAME,.fops = &fops
};static int probe(struct platform_device * pdev)
{int ret = 0;ret = misc_register(&misc);if ( ret < 0 )goto error_misc_register;ret = request_irq(pdev->resource[3].start, irq_handler, IRQF_DISABLED, "irq_adc", NULL);if ( ret < 0 )goto error_request_irq;init_waitqueue_head(&wq);//计算要映射的物理地址范围的大小(字节数)								adccon = ioremap(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1);adcdat0 = ioremap(pdev->resource[1].start, pdev->resource[1].end - pdev->resource[1].start + 1);clkcon = ioremap(pdev->resource[2].start, pdev->resource[2].end - pdev->resource[2].start + 1);*clkcon |= (1 << 15);INIT_WORK(&work, work_handler);printk("adc_driver probe *clkcon = %lx	##############################\n", *clkcon);return 0;error_misc_register:misc_deregister(&misc);printk("misc_register error...\n");return ret;error_request_irq:disable_irq(pdev->resource[3].start);free_irq(pdev->resource[3].start, NULL);misc_deregister(&misc);printk("error_request_irq...\n");return ret;
}static int remove(struct platform_device * pdev)
{iounmap(clkcon);iounmap(adcdat0);iounmap(adccon);disable_irq(pdev->resource[3].start);free_irq(pdev->resource[3].start, NULL);misc_deregister(&misc);printk("adc_driver remove	##############################\n");return 0;
}//该结构体用于描述一个平台设备的驱动程序
static struct platform_driver dri = 
{.probe = probe,.remove = remove,.driver = {.name = DEV_NAME	//驱动程序的名称//当内核发现一个平台设备的名称与一个驱动程序的名称相同时//会尝试将它们进行匹配并调用驱动程序的探测函数}
};//probe 是一个指向驱动程序中探测函数的指针//当内核发现一个设备与这个驱动程序匹配时,//会调用这个探测函数来初始化设备并建立设备与驱动程序之间的关联。//remove 指向驱动程序中的移除函数。当设备从系统中移除时,//内核会调用这个函数来执行一些清理操作,释放设备占用的资源。static int __init adc_driver_init(void)
{//向内核注册注册平台设备驱动程序int ret = platform_driver_register(&dri);if( ret < 0 )goto err_platform_driver_register;printk("platform_driver_register ...\n");return 0;err_platform_driver_register:printk("platform_driver_register failed\n");platform_driver_unregister(&dri);return ret;
}static void __exit adc_driver_exit(void)
{platform_driver_unregister(&dri);printk("platform_driver_unregister ...\n");
}module_init(adc_driver_init);
module_exit(adc_driver_exit);
MODULE_LICENSE("GPL");

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

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

相关文章

无人机之视觉技术篇

一、视觉传感器的类型 摄像头&#xff1a; 最常见的视觉传感器&#xff0c;能够捕捉可见光图像和视频。 通过单目、双目或多目摄像头的组合&#xff0c;无人机能够实现立体视觉&#xff0c;从而估算距离、深度&#xff0c;并进行物体识别和追踪。 红外传感器&#xff1a; …

【汇编语言】寄存器(内存访问)(七)—— CPU提供的栈机制

文章目录 前言1. CPU提供的栈机制2. push指令3. 问题4. 问题的分析与解答5. pop指令结语 前言 &#x1f4cc; 汇编语言是很多相关课程&#xff08;如数据结构、操作系统、微机原理&#xff09;的重要基础。但仅仅从课程的角度出发就太片面了&#xff0c;其实学习汇编语言可以深…

基于Java的茶叶商城设计与实现(源码+定制+开发)茶叶电商系统开发、茶叶电商平台开发、茶叶在线销售平台设计与开发

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

英伟达开源最新大模型Nemotron 70B后,只有OpenAI o1一个对手了

来源 | 机器之心 今天&#xff0c;英伟达又开源了一个性能超级强大的模型 —— Llama-3.1-Nemotron-70B-Instruct&#xff0c;它击败了 OpenAI 的 GPT-4o 和 Anthropic 的 Claude-3.5 Sonnet 等多个开闭源模型。 从命名来看&#xff0c;显然 Llama-3.1-Nemotron-70B-Instruct …

MySQL-15.DQL-排序查询

一.DQL-排序查询 -- 排序查询 -- 1.根据入职时间&#xff0c;对员工进行升序排序 select * from tb_emp order by entrydate asc ;-- 2.根据入职时间&#xff0c;对员工进行降序排序 select * from tb_emp order by entrydate desc ;-- 3.根据 入职时间 对公司员工进行 升序排序…

scala 高阶函数(2)上

学习目录 一.reduce reduce的含义 idea实例 二.reduceLeft-reduceRight reduceLeft-reduceRight的含义 idea实例 练习&#xff1a;求最值

word建立目录以及修改页码

1、为word建立新的目录 &#xff08;1&#xff09;选中word中的标题设置为第几级标题&#xff0c;将所有的标题均设置完成。最后可以鼠标右击标题&#xff0c;对不同的标题字体大小等进行设置。右击-->修改-->格式-->段落 &#xff08;2&#xff09;在word中插入新的…

【多模态大模型】 端侧多模态模型 Qwen2-VL-2B-Instruct

【多模态大模型】 端侧多模态模型 Qwen2-VL-2B-Instruct Qwen2-VL-2B-Instruct 模型介绍模型测评运行环境安装运行模型Image Resolution for performance boosttwo methods for fine-grained control over the image size input to the model: 下载开源协议参考 Qwen2-VL-2B-In…

专题十二_floodfill(洪水灌溉)算法_算法专题详细总结

目录 1. 图像渲染&#xff08;medium&#xff09; 解析&#xff1a; 函数头&#xff1a; 函数体&#xff1a;固定模板 设置全局变量&#xff1a; 总结&#xff1a; 2. 岛屿数量&#xff08;medium&#xff09; 解析&#xff1a; 注意&#xff1a; 总结&#xff1a; …

利用由 Search AI 提供支持的自动导入功能加速 Elastic Observability 中的日志分析

作者&#xff1a;来自 Elastic Bahubali Shetti 通过自动化自定义数据集成&#xff0c;以创纪录的速度将日志迁移到 AI 驱动的日志分析。 Elastic 正在通过自动提取自定义日志来加速采用 AI 驱动的日志分析&#xff08;AI-driven log analytics&#xff09;&#xff0c;随着基…

时间序列预测(六)——循环神经网络(RNN)

目录 一、RNN的基本原理 1、正向传播&#xff08;Forward Pass&#xff09;&#xff1a; 2、计算损失&#xff08;Loss Calculation&#xff09; 3、反向传播——反向传播通过时间&#xff08;Backpropagation Through Time&#xff0c;BPTT&#xff09; 4、梯度更新&…

Flink时间语义和时间窗口

前言 在实际的流计算业务场景中&#xff0c;我们会发现&#xff0c;数据和数据的计算往往都和时间具有相关性。 举几个例子&#xff1a; 直播间右上角通常会显示观看直播的人数&#xff0c;并且这个数字每隔一段时间就会更新一次&#xff0c;比如10秒。电商平台的商品列表&a…

MySQL-15.DQL-分页查询

一.DQL-分页查询 -- 分页查询 -- 1. 从 起始索引0 开始查询员工数据&#xff0c;每页展示5条记录 select * from tb_emp limit 0,5; -- 2.查询 第1页 员工数据&#xff0c;每页展示5条记录 select * from tb_emp limit 0,5; -- 3.查询 第2页 员工数据&#xff0c;每页展示5条记…

6.计算机网络_UDP

UDP的主要特点&#xff1a; 无连接&#xff0c;发送数据之前不需要建立连接。不保证可靠交付。面向报文。应用层给UDP报文后&#xff0c;UDP并不会抽象为一个一个的字节&#xff0c;而是整个报文一起发送。没有拥塞控制。网络拥堵时&#xff0c;发送端并不会降低发送速率。可以…

Chromium 前端window对象c++实现定义

前端中window.document window.alert()等一些列方法和对象在c对应定义如下&#xff1a; 1、window对象接口定义文件window.idl third_party\blink\renderer\core\frame\window.idl // https://html.spec.whatwg.org/C/#the-window-object// FIXME: explain all uses of [Cros…

git 报错 SSL certificate problem: certificate has expired

git小乌龟 报错 SSL certificate problem: certificate has expired 场景复现&#xff1a; 原因&#xff1a; 这个错误表明你在使用Git时尝试通过HTTPS进行通信&#xff0c;但是SSL证书已经过期。这通常发生在使用自签名证书或证书有效期已到期的情况下。 解决方法: 1.如果是…

【思维导图】C语言—常见概念

hello&#xff0c;友友们&#xff0c;今天我们进入一个新的专栏——思维导图&#xff01; 思维导图帮助我们复习知识的同时建构出一个清晰的框架&#xff0c;我往后会不断更新各个专栏的思维导图&#xff0c;关注我&#xff0c;一起加油&#xff01; 今天我们回顾C语言中的常见…

智慧社区服务平台:基于Spring Boot的实现

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于web的智慧社区设计与实现的相关信息成…

意外发现!AI写作这样用,热点文章轻松超越同行90%!

做自媒体&#xff0c;写热点文章很重要。 热点自带流量&#xff0c;能很快吸引不少读者。 可很多自媒体新手很犯愁。 干货文还能勉强写出来&#xff0c;碰到热点文就不知咋办了。 为啥写热点文章这么难呢&#xff1f; 关键是得找个新颖角度切入。 要是只在网上反复复制粘贴那些…

R语言机器学习算法实战系列(九)决策树分类算法 (Decision Trees Classifier)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍教程下载数据加载R包导入数据数据预处理数据描述数据切割调节参数构建模型模型的决策树预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性保存模…