linux 间调用 驱动_linux驱动调用 - CSDN
精华内容
参与话题
  • linux驱动程序调用(运行/执行)应用程序,即驱动调用用户空间的应用程序。本文主要是从系统API的使用角度讲述。 API声明的位置: 声明在include/linux/kmod.h里面,相应函数实现在kernel/kmod.c里面。 函数...

    概述:

    linux驱动程序调用(运行/执行)应用程序,即驱动调用用户空间的应用程序。本文主要是从系统API的使用角度讲述。


    API声明的位置:

    声明在include/linux/kmod.h里面,相应函数实现在kernel/kmod.c里面。


    函数call_usermodehelper()使用:

    函数call_usermodehelper()声明如下:

    extern int call_usermodehelper(char *path, char **argv, char **envp, int wait);

    参数说明:
    path:需要执行的应用程序,及路径。
    argv:传递给应用程序的参数,如果没有参数用NULL。
    envp:环境变量。如果在path指定的地方没有找到应用程序,那么将会在envp环 境变量中,查找应用程序。
    wait:控制标志,可用下面的4个宏定义其中之一。在kmod.h中定义如下控制标志:

    //驱动程序不等待应用程序的返回
    #define UMH_NO_WAIT     0       /* don't wait at all */
    //驱动程序等待应用程序执行完毕返回,但不处理返回的值
    #define UMH_WAIT_EXEC   1       /* wait for the exec, but not the process */
    //驱动程序等待应用程序执行完毕返回,并处理返回值
    #define UMH_WAIT_PROC   2       /* wait for the process to complete */
    //个人理解,应用程序不会返回时,驱动等待,直到应用程序进程被杀死后返回
    #define UMH_KILLABLE    4       /* wait for EXEC/PROC killable */

    用法例子:

    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/init.h>
    #include <linux/kmod.h>
    
    MODULE_AUTHOR("zimu");
    MODULE_LICENSE("GPL");
    
    static int __init practiceCall(void)
    {
            char *path = "/home/zimu/practice/a.out";
            char *arv[] = {path,"",NULL};//无参数,注意要按照这个格式path在 第一个位置,必须NULL结束
            char *env[] = {"/home/zimu","/usr/local/sbin",NULL};//多路径可以用逗号隔开,也可以在一对引号中用分号隔开,必须NULL结束标志
    
            printk("%s:call_usermodehelper\n",__func__);
            call_usermodehelper(path,arv,env,UMH_WAIT_PROC);
            return 0;
    }
    
    static void __exit practiceCallExit(void)
    {
            printk("%s:call_usermodehelper\n",__func__);
    }
    
    module_init(practiceCall);
    module_exit(practiceCallExit);

    驱动执行在/home/zimu/practice/路径下的应用程序a.out,如果在指定路径找不到a.out,将会在环境变量/home/zimu,/usr/local/sbin路径下查找a.out。a.out无参数。注意:arv,env最后一个一定要是NULL,否则会无法调用到应用程序,env如果没有参数,必须写成char *env[] = {NULL}。

    a.out的源码如下:

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    main()
    {
            int fd = -1;
    
            fd = open("/home/zimu/practice/file.txt",O_RDWR|O_CREAT);
            write(fd,":practice\n",10);
            close(fd);
    }

    a.out的功能是在/home/zimu/practice/路径下,打开file.txt文件,如果文件不存在,就创建file.txt文件,并向文件中写入“:practice”字符,以回车键结束。

    加载例子中的驱动模块时,就会在/home/zimu/practice/路径下创建file.txt文件。

    注意:驱动调用的应用程序,都是在root用户下运行的

    展开全文
  • Linux内核调用SPI驱动_实现OLED显示功能 0. 导语 进入Linux的世界,发现真的是无比的有趣,也发现搞Linux驱动从底层嵌入式搞起真的是很有益处。我们在单片机、DSP这些无操作系统的裸机中学习了这些最基本的驱动...

    Linux内核调用SPI驱动_实现OLED显示功能

    0. 导语

    进入Linux的世界,发现真的是无比的有趣,也发现搞Linux驱动从底层嵌入式搞起真的是很有益处。我们在单片机、DSP这些无操作系统的裸机中学习了这些最基本的驱动,然后用过GPIO时序去模拟、然后用那个芯片平台的外设去配置参数,到Linux的世界,对于底层的时序心中有数,做起来就容易很多。学习的过程就是不断的给自己出难题,然后去解决他,在未来工程里面遇到这个问题,就瞬间可以解决了,这就是经验的积累吧。

    Linux驱动目录,包含了底层写好的SPI驱动,我们需要想办法调用人家写好的SPI驱动,就不需要写IO口模拟SPI时序了。在网络上,对于SPI应用级的驱动倒是很多,平台级驱动很少,而我们想把平台级驱动二次包装在我们的字符设备驱动中,对于用户,无需考虑SPI通信写协议还是写命令,只需要使用read和write函数写显示的内容就好了。

    基于这样的想法,我们找了一个使用SPI协议的从器件来实现,我手里面有OLED设备,是支持SPI协议在OLED显示面板上显示字符的。所以搭建一个实验平台,做一个OLED的demo,未来所有的从SPI设备都遵循这个框架(而且我们在这个驱动中加入 了内核机制的驱动的自旋锁、互斥体的内核操作)。

    实验平台如下:

    • ARM板子: 友善之臂Nano-T3 (CortexA53架构, Samsung s5c6818)
    • **ARM的Linux系统:**Ubuntu 16.04.2 LTS
    • **编译调试Linux:**Ubuntu 16.04.3 LTS amd64版本
    • **编译器:**arm-cortexa9-linux-gnueabihf-gcc (64位版本)
    • **从设备:**OLED (SPI模式)

    1. 驱动架构模型

    总体驱动架构模型如图所示,对于OLED驱动的表述,主要包含两个方面,一个是OLED这个传感器的抽象;一个是,misc字符驱动的注册,里面有read和write函数,供用户接口调用,(在read和write函数里面使用OLED设备表述里面的master控制oled的行为就好了,比如显示,清除,复位之类的)。

    oled设备表述,为OLED设备的抽象,里面包含对硬件的描述和SPI的描述,还有对于写时序的时候使用自旋锁和互斥体对时序进行的保护,master为对oled设备的基本操作,包含复位,写字节等等。

    在本博客中最重要的就是SPI平台驱动的使用,问题也非常的清晰,我们如何使用linux内核驱动里面写好的spi,参考Linux SPI API文档里面,那么复杂的结构体,哪些是在驱动中要使用,哪些是在应用级程序中使用的。网络上的资料大部分都是应用级的,没有讲述在字符驱动中二级注册spi驱动的,而我们对于OLED这样的SPI设备,则需要在驱动中调用,让用户无需关心任何SPI的调用。

    在驱动模型中,master操作结构体里面,oled_write_byte这样的函数里面则需要调用系统级SPI,问题就非常明确,就在写byte的时候使用SPI。

    那么我们就需要在注册完字符设备的时候,向内核注册spi,然后我们使用该SPI对OLED操作。

    2. linux SPI驱动的注册

    Linux Drivers目录具备一定的通用性也具备各个架构区别不同,在包含头文件的时候,要包含

    1. 通用性的linux spi文件 #include <linux/spi/spi.h>
    2. mach级特性文件#include <mach/slsi-spi.h>

    同时也要关注:

    1. plat级的device.c文件,里面包含了spi_board信息的模板,用这个可以省去了很多麻烦。

    我们使用的oled_hw_t, 图上的结构(OLED->hw)的具体定义,里面定义了io口的编号和spi的各种机制,注意谁是指针,谁是实体。

    struct oled_hw_t 
    {
        unsigned int res_io_num;
        unsigned int dc_io_num;
        struct spi_transfer     spi_trans;
        struct spi_message      spi_msg;
        struct spi_driver       *spi_drv;
        struct spi_device       *spi_dev;
        struct spi_master       *spi_master_bus;
    };
    

    我需要定义以下机制:

    • spi_driver

      spi_driver会向内核申请总线处理的权限,当我们加载驱动的时候,在ARM机器的linux上的/sys/bus/spi/drivers目录下会看到申请SPI驱动内核的名字。

      static const struct spi_device_id oled_spi_id[] =
      {
          {“oledspi”, 1},
          {},
      };
      static struct spi_driver sp6818_spi_driver = 
      {
          .driver             =   
          {
                  .name       =   "oled_spi",
                  .bus        =   &spi_bus_type,
                  .owner      =   THIS_MODULE,
          },
          .probe              =   oled_bus_spi_probe,
          .remove             =   __devexit_p(oled_bus_spi_remove),
          .suspend            =   oled_bus_spi_suspend,
          .id_table           =   oled_spi_id,
      };
      MODULE_DEVICE_TABLE( spi, oled_spi_id );

      按照spi_driver驱动的格式进行,补充好probe和remove,suspend函数,但是这里存在一个问题,当我们应该spi_register_driver的时候,正常应该执行probe函数里面的内容,但是这个不执行,怀疑是因为二级包装问题,我们的主调还是使用misc驱动的字符设备 __init标示在 misc的初始化函数上,而导致不进入spi_driver的probe函数。

    • spi_device

      spi_device和spi_driver是成对出现的,在spi_driver注册完之后,则需要对spi_deivce进行配置,我们首先要声明一个spi_device,一会儿借助linux 的drivers 里面的platform级的deivce.c文件中的spi_board来注册我们的spi_device。

      定义spi_device驱动,这里面的配置信息可以瞎填,我们使用spi_board中的配置信息会覆盖这些信息。

      static struct spi_device sp6818_spi_device = 
      {
          .mode               =   SPI_MODE_3,
          .bits_per_word      =   16,
          .chip_select        =   SPI_CS_HIGH,
          .max_speed_hz       =   100000,
      };

      然后现在的工作就是如何spi_device和我们刚才spi_driver进行绑定了。

      定义下面的信息:

      static struct s3c64xx_spi_csinfo sp6818_csi = 
      {
            .line             =   OLED_CS_IO,
            .set_level        =   gpio_set_value,
            .fb_delay         =   0x2,
      };
      
      struct spi_board_info sp6818_board_info = 
      {
            .modalias         =   "oled",
            .platform_data    =   NULL,
            .max_speed_hz     =   10 * 1000 * 1000,
            .bus_num          =   0,
            .chip_select      =   2,
            .mode             =   SPI_MODE_3,
            .controller_data    =     &sp6818_csi,
      };

      这个模板就定义在platform级文件夹的device.c里面,我们按照模板的定义方式在我们的驱动文件里面也定义一个,在s3c64xx_spi_csinfo sp6818_csi中定义的是片选信号的IO口,这个IO口根据硬件原理图来的,然后定义spi_board_info结构体,这些都是为spi_device做准备的,spi的配置信息也由此写入。

      按照这个顺序进行:程序就如同下面的参考,后面会给出完成程序。

      static void oled_module_hw_init( OLED *self )
      {
      int ret,i;
      struct spi_master *master;
      struct spi_device *spi;
      
      self->hw.res_io_num = OLED_RES_IO;
      self->hw.dc_io_num  = OLED_DC_IO;
      printk( DRV_NAME "\tregister spi driver...\n" );
      self->hw.spi_drv = &sp6818_spi_driver;
      ret = spi_register_driver( self->hw.spi_drv );
      if ( ret < 0 ) {
          printk( DRV_NAME "\terror: spi driver register failed" );
      }
      printk( DRV_NAME "\tmaster blind spi bus.\n" );
      master = spi_busnum_to_master( 0 );
      master->num_chipselect = 4;
      if ( !master ) {
          printk( DRV_NAME "\terror: master blind spi bus.\n" );
          ret = -ENODEV;
          return ret;
      }
      printk( DRV_NAME "\tnew spi device...\n" );
      spi =   spi_new_device( master, &sp6818_board_info );
      if ( !spi ) {
          printk( DRV_NAME "\terror: spi occupy.\n" );        
          return -EBUSY;
      }
      self->hw.spi_master_bus = master;
      self->hw.spi_dev = spi;
      printk( DRV_NAME "\thw init succussful...\n" );
      }

    到此,完成,spi的注册。

    spi_device的注册里面,会在ARM上面的Linux的/sys/bus/spi/devices下面出现我们注册的device设备,如图:

    spi0.2就是我们所注册的device设备,这个命名就和我们的spi_board_info有关系了,

    如果,bus_num = 5, chip_select = 20, 那么注册的device就是spi5.20了。这里还有个坑,就是片选信号的数值大小和master里面的片选num的问题,linux的spi api要求,master的num-chipselect必须大于 spi_board_info里面chip_select的数值。你也看到上面初始化程序,为什么master->num_chipselect = 4; 这个语句了

    3. SPI 的使用

    在驱动里面对于spi的使用就非常简单了。例如我们oled的write_byte函数:

    static void oled_module_write_byte( OLED* self,             \ 
                                        unsigned int dat,       \
                                        enum data_type_t type)
    {
        int status;
        unsigned int write_buffer[1];
    
        if ( type == ENUM_WRITE_TYPE_CMD ) 
            self->master->set_dc_low( self );
        else 
            self->master->set_dc_high( self );
        write_buffer[0] = dat;
        write_buffer[1] = 0xFF;
        status = spi_write( self->hw.spi_dev, write_buffer, 1 );
        if ( status  )
            dev_err( &self->hw.spi_dev->dev, "%s error %d\n", __FUNCTION__, status );
    }

    使用spi_write函数就好了。

    4. 结语

    探索Linux SPI真是很费劲,这些花了好多时间,经历了无数次的实验,因为是驱动,经常在调试过程中出现暴栈、指针乱指,这些对于Linux内核都是毁灭性的错误,只能重启ARM Linux。光重启Linux就好几百次。不过总算是有成果,对于Linux驱动的学习还在进行,下次可能要实验I2C的平台驱动,找到规律和不同,再加上一些内核的操作,比如并发和IO等,在学习中成长。

    源代码

    Github地址:https://github.com/lifimlt/carlosdriver

    见 oled.c oled.h 和oledfont.h三个文件

    参考文献:

    [1] Linux org, Serial Peripheral Interface (SPI),

    [2] 郝过, Linux设备驱动模型SPI之二, 2016年2月28日

    [3] invo-tronics , SPI Driver for Linux Based Embedded System, 2014年9月30日

    [4] Linux学习之路, spi驱动框架全面分析,从master驱动到设备驱动, 2016年6月22日

    展开全文
  • linux上层app调用驱动底层的过程详解

    千次阅读 2017-05-27 14:44:16
    APP应用程序->应用框架层->硬件抽象层-> 硬件驱动程序 一、硬件驱动层  进入kernel/drivers文件夹中,创建一文件夹,放入驱动程序。包括头文件,C文件,Makefile,Kconfig。同时对drivers下的Makefile跟Kconfig...
     APP应用程序->应用框架层->硬件抽象层-> 硬件驱动程序
    

    一、硬件驱动层

                进入kernel/drivers文件夹中,创建一文件夹,放入驱动程序。包括头文件,C文件,Makefile,Kconfig。同时对drivers下的Makefile跟Kconfig进行相应的添加,这样配置编译选项后,即可编译。编译完后,可以在/dev,/proc,/sys/class中得相应的文件,其中dev下的文件即为该设备文件。

    二、硬件抽象层

                进入源码根目录下的hardware/libhardware/include/hardware新建头文件,在hardware/libhardware/modules中新建目录,在该目录下放入C文件调用设备文件(open函数打开/dev/XXX设备文件),最后在该目录下创建Android.mk文件,编译后得XXX.default.so文件。重新打包后,system.img就包含我们定义的硬件抽象层模块XXX.default。

    三、应用框架层

                进入frameworks/base/services/jni目录,创建com_android_server_XXXService.cpp文件(#include <hardware/hello.h> 以此调用抽象层方法 ),实现jni方法。com_android_server前缀表示的是包名,表示硬件服务XXXService是放在frameworks/base/services/java目录下的com/android/server目录的。对同目录下的onload.cpp文件进行修改,这样,在Android系统初始化时,就会自动加载该JNI方法调用表。同时修改该目录下的Android.mk。

               进入frameworks/base/core/java/android/os目录,新增IXXXService.aidl接口定义文件,该文件提供功能函数。在frameworks/base目录下的Android.mk添加该aidl文件。进入frameworks/base/services/java/com/android/server目录,新增XXXService.java(主要是通过调用JNI方法来提供硬件服务),修改同目录的SystemServer.java文件,在ServerThread::run函数中增加加载HelloService的代码。

              这样,重新打包后的system.img系统镜像文件就在Application Frameworks层中包含了我们自定义的硬件服务XXXService了,并且会在系统启动的时候,自动加载XXXService。这时,应用程序就可以通过Java接口来访问Hello硬件服务了。

    四、APP应用层

               用eclipse编写应用层的APP。程序通过ServiceManager.getService("XXX")来获得XXXService,接着通过IXXXService.Stub.asInterface函数转换为IXXXService接口。然后把出R文件的其他文件都拷/packages/experimental下,在该APP目录下创建Android.mk文件。编译后安装该生成的apk即可使用该程序调用底层硬件驱动。

            

              大概整个过程就这样,上层app调用框架层的java接口,java接口通过jni调用硬件抽象层,硬件抽象层则通过打开设备文件调用底层硬件驱动。
    展开全文
  • 浅析Linux从API调用到底层驱动的过程

    千次阅读 2018-11-20 09:45:02
    本篇只是嵌入式Linux的开篇,仅仅通过如下结构来简单说说调用流程。  分析:应用层调用open()函数打开鼠标文件时的调用过程。  当用户通过API中的open()函数试图去打开一个鼠标文件时,open()函数会有一个返回值...

    转自:https://blog.csdn.net/u014294166/article/details/52155913

     

    一、结构流程图

    本篇只是嵌入式Linux的开篇,仅仅通过如下结构来简单说说调用流程。 
    这里写图片描述

    分析:应用层调用open()函数打开鼠标文件时的调用过程。 
    当用户通过API中的open()函数试图去打开一个鼠标文件时,open()函数会有一个返回值,这个返回值即为文件描述符,文件描述符被定义为file结构体,file结构体关联到硬件驱动,然后硬件驱动去关联到相应的硬件设备。 
    再具体一点说,API的open()函数会通过系统调用,而系统调用进入到系统内核即VFS。然后根据文件描述符File来对应硬件驱动。文件描述符实际上是一个结构体,里面是一些函数指针,会提前注册到内核,linux驱动和windows驱动的模型不同,根据不同的模型进行不同的函数封装,当需要调用哪个就是通过回调进行调用驱动。 
    其中File结构体中有一个inode成员,inode是磁盘索引,可以理解为标记了文件在磁盘的位置。

    ============无聊分割线============== 
    突然发现自己一不小心迈入了所谓的“嵌入式”开发,曾经准备走应用层的开发,准备玩转网络协议的,可是谁都计划不了真正的未来O(∩_∩)O。。。。好吧,既然到了底层,何不借此机会好好的去深究一下编程的本质呢?!现在有时有种“错觉”,觉得所做的应用层开发实际上只是在模仿底层行为给了个UI而已。弄懂底层的实现原理或许可以把问题看得更透彻,更利于去分析问题,升华问题。

     

    展开全文
  • 然后,我们考察了Linux内核如何实现系统调用,以及执行系统调用的连锁反应:陷入内核,传递系统调用号和参数,执行正确的系统调用函数,并把返回值带回用户空间。最后讨论了如何增加系统调用,并提供了从用户空间...
  • ioctl作用:应用层的ioctl函数传入的cmd和arg参数会直接传入驱动层的ioctl接口,在对应驱动文件里会对相应的命令进行操作 对于传递的ioctl命令有一定的规范,具体可以参考:/include/asm/ioctl.h,/Documentation/...
  • Linux应用程序访问驱动程序过程

    千次阅读 2018-05-24 20:43:23
    当应用程序有访问字符设备或者说有访问字符设备驱动程序需求的时候,主要是通过系统调用达到这一目的的。例如,应用程序读取设备,应用程序使用read函数,read函数通过系统调用通过内核,使得驱动程序中的设备方法xx...
  • Linux下usb驱动调用流程

    千次阅读 2011-08-19 10:12:32
    driver/usb/core/hub.c : usb->hun_thread()->hub->events()->hub_port_connect_change() driver/usb/core/hub.c hub_port_connect_change()
  • linux 驱动probe 被调用流程分析

    千次阅读 2018-08-26 17:11:51
    前言: 对于linux platform device 和driver,一个driver可对应多个device,通过名字进行匹配,调用驱动里边实现的probe函数,本文以一个i2c设备为例,从驱动的i2c_add_driver()开始看源码以及用比较笨的打log的方式...
  • 本博实时更新《Linux设备驱动开发详解(第3版)》(即《Linux设备驱动开发详解:基于最新的Linux 4.0内核》)的最新进展。 目前已经完成稿件。 2015年8月9日,china-pub开始上线预售: ... 2015年8月20日,各路朋友报喜...
  •   驱动的动态加载指的是利用了linux的module特性,可以在系统启动后通过insmod或modprobe命令挂载.ko内核目标文件,对模块进行加载,成功后可通过mknod指令进行挂载节点,在不需要的时候可通过rmmod命令来卸载模块...
  • 浅谈Linux PCI设备驱动(一)

    万次阅读 热门讨论 2013-04-24 17:56:32
    要弄清楚Linux PCI设备驱动,首先要明白,所谓的Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分。不知道读者理不理解这句话,本人觉得这句话很重要,对于PCI、USB这样的驱动来说,必须要理解这个...
  • linux驱动中probe函数是怎么调用

    千次阅读 2018-10-17 19:07:06
    probe何时被调用:在总线上驱动和设备的名字匹配,就会调用驱动的probe函数 probe函数被调用后,系统就调用platform设备的probe函数完成驱动注册最后工作。下面是probe被调用前的一些流程。 device一般是先于...
  • 在Ubuntu上为Android系统编写Linux内核驱动程序

    万次阅读 多人点赞 2017-01-06 14:45:02
    在智能手机时代,每个品牌的手机都有自己的个性特点。正是依靠这种与众不同的个性来吸引用户,营造品牌凝聚力和用户忠城度,典型的代表非iphone莫属了。据统计,截止2011年5月,AppStore的应用软件数量达381062个,...
  • 一直都想亲自做一次使用android应用程序访问Linux内核驱动的尝试,但总是没能做到。最近抽出时间,下决心重新尝试一次。尝试的开始当然是先写一个Linux内核驱动了。 我希望写一个简单测驱动程序,实现写一个字符串...
  • 在网卡模块上添加自己构建协议栈代码(比如LTE),遇到不少问题,
  • Linux驱动 probe函数调用

    万次阅读 2011-12-21 11:44:09
    参考: http://blog.chinaunix.net/space.php?uid=15887868&do=blog&id=2758294 http://www.cnblogs.com/hoys/archive/2011/04/01/2002299.html 1,driver_register把驱动注册到... ... * driver_register - registe
  • linux驱动程序接口

    千次阅读 2017-03-16 18:25:27
    1. Linux驱动程序接口 系统调用是操作系统内核与应用程序之间的接口,设备驱动程序则是操作系统内核与机器硬件的接口。几乎所有的系统操作最终映射到物理设备,除了CPU、内存 和少数其它设备,所有的设备控制操作都...
  • 1. reboot:#include <linux/reboot.h> kernel_restart(NULL); 2. reboot -p或者shutdown:#include <linux/reboot.h> kernel_power_off();
1 2 3 4 5 ... 20
收藏数 190,848
精华内容 76,339
关键字:

linux 间调用 驱动