QNX---SPI驱动分析。

2024-06-21 13:58
文章标签 分析 驱动 spi qnx

本文主要是介绍QNX---SPI驱动分析。,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

QNX系统启动后,执行一系列脚本命令,然后加载SPI驱动。

脚本SPI启动文件

spi-master -u3 -d Touch-espi base=0x02010000,irq=65

        当QNX执行该脚本时,会自动到指定目录搜索是否存在spi-master驱动,然后后面一串是参数设置。这一串参数就像Linux设备树一样,指定相关的硬件参数。具体参数意义在驱动力在详细解释。

        QNX执行spi-master后,应该执行函数入口在哪里?其实QNX早就为我们分配好了,它把所有的驱动当做应用程序,一个应用程序就像VC编写的应用程序一样,都有一个main入口。因此分析QNX SPI驱动,可以从这个函数开始。

int main(int argc, char *argv[])
{spi_dev_t	*head = NULL, *tail = NULL, *dev;void		*drventry, *dlhdl;siginfo_t	info;sigset_t	set;int			i, c, devnum = 0;if (ThreadCtl(_NTO_TCTL_IO, 0) == -1) {perror("ThreadCtl");return (!EOK);}_spi_init_iofunc();//实例化io接口while ((c = getopt(argc, argv, "u:d:")) != -1) {switch (c) {case 'u':devnum = strtol(optarg, NULL, 0);break;case 'd':if ((drventry = _spi_dlload(&dlhdl, optarg)) == NULL) {perror("spi_load_driver() failed");return (-1);}do {if ((dev = calloc(1, sizeof(spi_dev_t))) == NULL)goto cleanup;if (argv[optind] == NULL || *argv[optind] == '-')dev->opts = NULL;elsedev->opts = strdup(argv[optind]);++optind;dev->funcs  = (spi_funcs_t *)drventry;dev->devnum = devnum++;dev->dlhdl  = dlhdl;i = _spi_create_instance(dev);if (i != EOK) {perror("spi_create_instance() failed");if (dev->opts)free(dev->opts);free(dev);goto cleanup;}if (head) {tail->next = dev;tail = dev;}elsehead = tail = dev;} while (optind < argc && *(optarg = argv[optind]) != '-'); /* * Now we only support one dll*/goto start_spi;break;}}start_spi:if (head) {/* background the process */procmgr_daemon(0, PROCMGR_DAEMON_NOCLOSE | PROCMGR_DAEMON_NODEVNULL);sigemptyset(&set);sigaddset(&set, SIGTERM);for (;;) {if (SignalWaitinfo(&set, &info) == -1)continue;if (info.si_signo == SIGTERM)break;}}cleanup:dev=head;while (dev) {if (dev->ctp) {dispatch_unblock(dev->ctp);}if (dev->drvhdl) {resmgr_detach(dev->dpp, dev->id, _RESMGR_DETACH_ALL);dev->funcs->fini(dev->drvhdl);}if (dev->dpp) {dispatch_destroy(dev->dpp);}head = dev->next;if (dev->opts)free(dev->opts);free(dev);dev=head;}dlclose(dlhdl);return (EOK);
}

     进入main后,执行ThreadCtl(_NTO_TCTL_IO, 0)函数,该函数使能超级锁定进程的内存和请求I/O特权;让线程在具有适当特权的架构上执行in、in、out、out、cli和sti I/O操作码,并让它附加IRQ处理程序。很多操作都需要进行寄存器操作,需要采用out32 in32接口等。

   调用_spi_init_iofunc();初始化连接函数,通过 iofunc_func_init()函数初始化,通过连接和POSIX默认IO结构层功能。有关默认函数的信息。

int _spi_init_iofunc(void)
{iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &_spi_connect_funcs, _RESMGR_IO_NFUNCS, &_spi_io_funcs);_spi_io_funcs.read      = _spi_read;_spi_io_funcs.write     = _spi_write;_spi_io_funcs.devctl    = _spi_devctl;_spi_io_funcs.close_ocb = _spi_close_ocb;_spi_io_funcs.msg       = _spi_iomsg;return EOK;
}

完成后,开始解析参数命令,也就是前面提到的spi-master后的参数“-u3 -d Touch-espi base=0x02010000,irq=65”,其中u表示设备号,定义为spi3,-d表示加载的驱动名称链接库,调用 _spi_dlload(&dlhdl, optarg)函数,加载动态库,而寄存器基地址和中断号,不在这个参数里设置,后面在叙述。

void *_spi_dlload(void **hdl, const char *optarg)
{char		dllpath[_POSIX_PATH_MAX + 1];void		*dlhdl, *entry;if (strchr(optarg, '/') != NULL)strcpy(dllpath, optarg);elsesprintf(dllpath, "spi-%s.so", optarg);dlhdl = dlopen(dllpath, 0);if (dlhdl != NULL) {entry = dlsym(dlhdl, "spi_drv_entry");if (entry != NULL) {*hdl = dlhdl;return entry;}dlclose(dlhdl);}return NULL;
}

       _spi_dlload调用dlsym函数,找到动态链接库内的匹配函数名称spi_drv_entry。这个函数作为SPI底层驱动入口加载。在主函数参数命令里,调用      dev->funcs  = (spi_funcs_t *)drventry;和  i = _spi_create_instance(dev);实例化驱动。最终完成系列的初始化过程,进入循环。

    调用procmgr_daemon函数,置PROCMGR_DAEMON_NOCLOSE | PROCMGR_DAEMON_NODEVNULL标志,把该进程运行于后台。

   sigemptyset(&set);//初始化一个不包含任何信号的集合

    sigaddset(&set, SIGTERM);//设置SIGTERM信号。是一个程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和 
处理. 通常用来要求程序自己正常退出. shell命令kill缺省产生这个信号。

     SignalWaitinfo() 内核调用从set指定的集合中选择挂起信号。如果在调用时集合中没有挂起信号,线程将阻塞,直到集合中的一个或多个信号成为挂起信号,或者直到被未阻塞的捕获信号中断。在这里主要是捕获SIGTERM信号,当收到该信号,退出该驱动。   如果其中一项加载不成功,将会卸掉以前初始化的过程,其实就是初始化逆过程,相当于linux模块卸载函数。至此SPI主函数的过程已分析完成,接下了分析SPI 具体过程。

       主要从_spi_create_instance(dev)函数开始,_spi_create_instance初始化线程参数,创建一个线程任务,这个线程函数为_spi_driver_thread。

int _spi_create_instance(spi_dev_t *dev)
{pthread_attr_t		pattr;struct sched_param	param;if (NULL == (dev->dpp = dispatch_create())) {//动态分配一个句柄perror("dispatch_create() failed");goto failed0;}pthread_attr_init(&pattr);//初始化线程属性pthread_attr_setschedpolicy(&pattr, SCHED_RR);//设置调度属性param.sched_priority = 21;//设置优先级pthread_attr_setschedparam(&pattr, &param);pthread_attr_setinheritsched(&pattr, PTHREAD_EXPLICIT_SCHED);// Create thread for this interfaceif (pthread_create(NULL, &pattr, (void *)_spi_driver_thread, dev) != EOK) {//创建线程perror("pthread_create() failed");goto failed1;}

            _spi_driver_thread调用_spi_register_interface后进入主线程任务。其中_spi_register_interface在dev目录下创建一个设备节点和初始化SPI驱动,调用 dev->funcs->init(dev, dev->opts),这个函数指针在spi_drv_entry结构体中,在主函数里通过链接库完成指针赋值,spi_drv_entry里实现真正的SPI操作。包括初始化、配置、设备信息获取以及数据传输等等操作。

spi_funcs_t spi_drv_entry = {
    sizeof(spi_funcs_t),
    mx51_init,        /* init() */
    mx51_dinit,        /* fini() */
    mx51_drvinfo,    /* drvinfo() */
    mx51_devinfo,    /* devinfo() */
    mx51_setcfg,    /* setcfg() */
    mx51_xfer,        /* xfer() */
    NULL            /* dma_xfer() */
};调用关系如下 dev->funcs->init(dev, dev->opts)-->  mx51_init(void *hdl, char *options)-->mx51_options(mx51_cspi_t *dev, char *optstring)。

static int _spi_register_interface(void *data)
{spi_dev_t		*dev = data;SPIDEV			*drvhdl;resmgr_attr_t	rattr;char			devname[PATH_MAX + 1];if ((drvhdl = dev->funcs->init(dev, dev->opts)) == NULL) {free(dev->opts);dev->opts = NULL;return (!EOK);}dev->drvhdl = drvhdl;/* set up i/o handler functions */memset(&rattr, 0, sizeof(rattr));rattr.nparts_max   = SPI_RESMGR_NPARTS_MIN;rattr.msg_max_size = SPI_RESMGR_MSGSIZE_MIN;iofunc_attr_init(&drvhdl->attr, S_IFCHR | 0666, NULL, NULL);drvhdl->attr.mount = &_spi_mount;/* register device name */snprintf(devname, PATH_MAX, "/dev/spi%d", dev->devnum);if (-1 == (dev->id = resmgr_attach(dev->dpp, &rattr, devname, _FTYPE_ANY, 0,&_spi_connect_funcs, &_spi_io_funcs, (void *)drvhdl))) {perror("resmgr_attach() failed");goto failed1;}resmgr_devino(dev->id, &drvhdl->attr.mount->dev, &drvhdl->attr.inode);if ((dev->ctp = dispatch_context_alloc(dev->dpp)) != NULL)return (EOK);perror("dispatch_context_alloc() failed");resmgr_detach(dev->dpp, dev->id, _RESMGR_DETACH_ALL);
failed1:dev->funcs->fini(drvhdl);return (!EOK);
}

     在开始参数配置时,还有两个参数没有看到具体方法,其实在mx51_options实现,包括如下参数如base\irq\clock\csdelay等等。从而达到动态设置参数的目的,

  

 

这篇关于QNX---SPI驱动分析。的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1081412

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除