精华内容
下载资源
问答
  • Linux PWM接口

    千次阅读 2018-12-23 22:36:23
    这提供了有关Linux PWM接口的概述 PWM通常用于控制手机中的LED,风扇或振动器。具有固定目的的PWM不需要实现Linux PWM API(尽管它们可以)。然而,PWM通常被发现作为SoC上的分立器件,没有固定的目的。电...

    0.脉冲宽度调制(PWM)接口

    这提供了有关Linux PWM接口的概述

    PWM通常用于控制手机中的LED,风扇或振动器。具有固定目的的PWM不需要实现Linux PWM API(尽管它们可以)。然而,PWM通常被发现作为SoC上的分立器件,没有固定的目的。电路板设计师可以将它们连接到LED或风扇。为了提供这种灵活性,通用PWM API诞生了。


    1.识别PWM

    传统PWM API的用户使用唯一ID来指代PWM设备。

    不应该通过其唯一ID引用PWM器件,电路板设置代码应该注册静态映射,该映射将PWM使用者与提供者匹配,如以下示例中所示。

    static struct pwm_lookup board_pwm_lookup [] = {
    PWM_LOOKUP(“tegra-pwm”,0,“pwm-backlight”,NULL,
    50000,PWM_POLARITY_NORMAL),
    };
    
    static void __init board_init(void)
    {
    ...
    pwm_add_table(board_pwm_lookup,ARRAY_SIZE(board_pwm_lookup));
    ...
    }
    

    2.使用PWM

    传统用户可以使用pwm_request()请求PWM设备,并在使用pwm_free()后将其释放。

    新用户应使用pwm_get()函数请求PWM设备,并将消费者设备(使用者)名称传递给它。 pwm_put()用于释放PWM器件。这些函数的变体,devm_pwm_get()和devm_pwm_put()也存在。

    请求后,必须使用如下API进行PWM配置:

    int pwm_apply_state(struct pwm_device * pwm,struct pwm_state * state);
    
    此API控制PWM周期/ duty_cycle配置和启用/禁用状态。
    

    pwm_config(),pwm_enable()和pwm_disable()函数只是pwm_apply_state()的封装,如果用户想要一次更改多个参数,则不应使用它。例如,如果你看到pwm_config()和pwm_ {enable,disable}()调用同一个函数,这可能意味着你应该改为使用pwm_apply_state()。

    PWM API还允许用户通过pwm_get_state()查询PWM状态。

    除PWM状态外,PWM API还会公开PWM参数,这是在PWM上应该使用的参考PWM配置。PWM参数通常是特定于平台的,并且允许PWM用户仅关注相对于整个周期的占空比(例如,占空比=周期的50%)。struct pwm_args包含2个字段(周期和极性),应该用于设置初始PWM配置(通常在PWM用户探测函数中完成)。使用pwm_get_args()检索PWM参数。


    3.将PWM与sysfs接口配合使用

    如果在内核配置中启用了CONFIG_SYSFS,则会提供一个简单的sysfs接口来使用用户空间的PWM。它在/ sys / class / pwm /中公开。每个被探测的PWM控制器/芯片将被输出为pwmchipN,其中N是PWM芯片的基础。你在目录里面会发现:

      npwm
        该芯片支持的PWM通道数(只读)。
    
      export
        导出用于sysfs的PWM通道(只写)。
    
      unexport
       从sysfs中取消导出PWM通道(只写)。
    

    PWM通道使用从0到npwm-1的每芯片索引编号。

    导出PWM通道时,将在与其关联的pwmchipN目录中创建pwmX目录,其中X是导出的通道编号。然后将提供以下属性:

      period
        PWM信号的总周期(读/写)。
        值以纳秒为单位,是活动和非活动的总和
        PWM的时间。
    
      duty_cycle(占空比)
        PWM信号的有效时间(读/写)。
        值以纳秒为单位,且必须小于周期。
    	在NORMAL模式下,表示一个周期内高电平持续的时间
    	在INVERTED模式下,表示一个周期中低电平持续的时间
      
      polarity
        改变PWM信号的极性(读/写)。
        写入此属性仅在PWM芯片支持更改时才有效
        极性。只有PWM不能改变极性
        启用。值是字符串“normal”或“inversed”。
    
      enable
        启用/禁用PWM信号(读/写)。
    
     -  0  - 禁用
     -  1  - 启用
    

    4.实现PWM驱动程序

    目前有两种方法可以实现pwm驱动程序。以前每个驱动程序必须自己实现pwm _ *()函数。这意味着系统中不可能有多个PWM驱动器。因此,新驱动程序必须使用通用PWM框架。

    可以使用pwmchip_add()添加新的PWM控制器/芯片,并使用pwmchip_remove()再次删除。 pwmchip_add()将填充的struct pwm_chip(PWM芯片的描述、芯片提供的PWM器件数量以及支持的PWM操作的芯片)作为参数,特定实现到框架。

    在PWM驱动器中实现极性支持时,请确保遵守PWM框架中的信号约定。根据定义,正常极性表征信号在占空比持续时间内开始为高电平,在剩余的时间内变低。相反,具有反转极性的信号在占空比的持续时间内开始为低电平,并在该周期的剩余时间内变为高电平。
    在这里插入图片描述
    上图分别为INVERTED和NORMAL模式。

    鼓励驱动程序实现 - > apply()而不是legacy-> enable(), - > disable()和 - > config()方法。这样做应该在PWM配置工作流程中提供原子性,这在PWM控制关键设备(如稳压器)时是必需的。

    由于同样的原因,也鼓励实现 - > get_state()(用于检索初始PWM状态的方法):让PWM用户了解当前的PWM状态将允许他避免毛刺。


    5.锁

    PWM核心列表操作受互斥锁保护,因此可能无法从原子上下文调用pwm_request()和pwm_free()。 目前,PWM内核不对pwm_enable(),pwm_disable()和pwm_config()强制执行任何锁,因此调用上下文当前是特定于驱动程序的。 这是旧API派生的问题,应尽快修复。


    6.求助

    目前,PWM只能配置period_ns和duty_ns。 对于部分情况,freq_hz和duty_percent可能会更好。 请考虑在框架中添加适当的帮助程序,而不是在驱动程序中计算。

    展开全文
  • 嵌入式Linux中在用户态中操作GPIO接口的代码及相应的测试程序
  • Linux系统的shell作为操作系统的外壳,为用户提供使用操作系统的接口。它是命令语言、命令解释程序及程序设计语言的统称。shell是用户Linux内核之间的接口程序,如果把Linux内核想象成一个球体的中心,shell就是...
  • linux proc接口

    千次阅读 2010-12-15 00:22:00
    linux proc接口的建立与使用 /proc 文件系统是一个虚拟文件系统,通过它可以使用一种新的方法在 Linux® 内核空间和用户空间之间进行通信。在 /proc 文件系统中,我们可以将对虚拟文件的读写作为与内核中实体进行...

    linux proc接口的建立与使用

     /proc 文件系统是一个虚拟文件系统,通过它可以使用一种新的方法在 Linux® 内核空间和用户空间之间进行通信。在 /proc 文件系统中,我们可以将对虚拟文件的读写作为与内核中实体进行通信的一种手段,但是与普通文件不同的是,这些虚拟文件的内容都是动态创建的。

    /proc 文件系统是为了提供有关系统中进程的信息。但是由于这个文件系统非常有用,因此内核中的很多元素也开始使用它来报告信息,或启用动态运行时配置。

    /proc 文件系统包含了一些目录(用作组织信息的方式)和虚拟文件。虚拟文件可以向用户呈现内核中的一些信息,也可以用作一种从用户空间向内核发送信息的手段。

    尽管像本文这样短小的一篇文章无法详细介绍 /proc 的所有用法,但是它依然对这两种用法进行了展示,从而可以让我们体会一下 /proc 是多么强大。清单 1 是对 /proc 中部分元素进行一次交互查询的结果。它显示的是 /proc 文件系统的根目录中的内容。注意,在左边是一系列数字编号的文件。每个实际上都是一个目录,表示系统中的一个进程。由于在 GNU/Linux 中创建的第一个进程是 init 进程,因此它的 process-id1。然后对这个目录执行一个 ls 命令,这会显示很多文件。每个文件都提供了有关这个特殊进程的详细信息。例如,要查看 init 的 command-line 项的内容,只需对 cmdline 文件执行 cat 命令。

    /proc 中另外一些有趣的文件有:cpuinfo,它标识了处理器的类型和速度;pci,显示在 PCI 总线上找到的设备;modules,标识了当前加载到内核中的模块。


    清单 1. 对 /proc 的交互过程
            
    [root@plato]# ls /proc
    1     2040  2347  2874  474          fb           mdstat      sys
    104   2061  2356  2930  9            filesystems  meminfo     sysrq-trigger
    113   2073  2375  2933  acpi         fs           misc        sysvipc
    1375  21    2409  2934  buddyinfo    ide          modules     tty
    1395  2189  2445  2935  bus          interrupts   mounts      uptime
    1706  2201  2514  2938  cmdline      iomem        mtrr        version
    179   2211  2515  2947  cpuinfo      ioports      net         vmstat
    180   2223  2607  3     crypto       irq          partitions
    181   2278  2608  3004  devices      kallsyms     pci
    182   2291  2609  3008  diskstats    kcore        self
    2     2301  263   3056  dma          kmsg         slabinfo
    2015  2311  2805  394   driver       loadavg      stat
    2019  2337  2821  4     execdomains  locks        swaps
    [root@plato 1]# ls /proc/1
    auxv     cwd      exe  loginuid  mem     oom_adj    root  statm   task
    cmdline  environ  fd   maps      mounts  oom_score  stat  status  wchan
    [root@plato]# cat /proc/1/cmdline
    init [5]
    [root@plato]#
    

    清单 2 展示了对 /proc 中的一个虚拟文件进行读写的过程。这个例子首先检查内核的 TCP/IP 栈中的 IP 转发的目前设置,然后再启用这种功能。


    清单 2. 对 /proc 进行读写(配置内核)
            
    [root@plato]# cat /proc/sys/net/ipv4/ip_forward
    0
    [root@plato]# echo "1" > /proc/sys/net/ipv4/ip_forward
    [root@plato]# cat /proc/sys/net/ipv4/ip_forward
    1
    [root@plato]#
    

    另外,我们还可以使用 sysctl 来配置这些内核条目。有关这个问题的更多信息,请参阅 参考资料 一节的内容。

    顺便说一下,/proc 文件系统并不是 GNU/Linux 系统中的惟一一个虚拟文件系统。在这种系统上,sysfs 是一个与 /proc 类似的文件系统,但是它的组织更好(从 /proc 中学习了很多教训)。不过 /proc 已经确立了自己的地位,因此即使 sysfs 与 /proc 相比有一些优点,/proc 也依然会存在。还有一个 debugfs 文件系统,不过(顾名思义)它提供的更多是调试接口。debugfs 的一个优点是它将一个值导出给用户空间非常简单(实际上这不过是一个调用而已)。

    内核模块简介

    可加载内核模块(LKM)是用来展示 /proc 文件系统的一种简单方法,这是因为这是一种用来动态地向 Linux 内核添加或删除代码的新方法。LKM 也是 Linux 内核中为设备驱动程序和文件系统使用的一种流行机制。

    如果您曾经重新编译过 Linux 内核,就可能会发现在内核的配置过程中,有很多设备驱动程序和其他内核元素都被编译成了模块。如果一个驱动程序被直接编译到了内核中,那么即使这个驱动程序没有运行,它的代码和静态数据也会占据一部分空间。但是如果这个驱动程序被编译成一个模块,就只有在需要内存并将其加载到内核时才会真正占用内存空间。有趣的是,对于 LKM 来说,我们不会注意到有什么性能方面的差异,因此这对于创建一个适应于自己环境的内核来说是一种功能强大的手段,这样可以根据可用硬件和连接的设备来加载对应的模块。

    下面是一个简单的 LKM,可以帮助您理解它与在 Linux 内核中看到的标准(非动态可加载的)代码之间的区别。清单 3 给出了一个最简单的 LKM。(可以从本文的 下载 一节中下载这个代码)。

    清单 3 包括了必须的模块头(它定义了模块的 API、类型和宏)。然后使用 MODULE_LICENSE 定义了这个模块使用的许可证。此处,我们定义的是 GPL,从而防止会污染到内核。

    清单 3 然后又定义了这个模块的 initcleanup 函数。my_module_init 函数是在加载这个模块时被调用的,它用来进行一些初始化方面的工作。my_module_cleanup 函数是在卸载这个模块时被调用的,它用来释放内存并清除这个模块的踪迹。注意此处 printk 的用法:这是内核的 printf 函数。KERN_INFO 符号是一个字符串,可以用来对进入内核回环缓冲区的信息进行过滤(非常类似于 syslog)。

    最后,清单 3 使用 module_initmodule_exit 宏声明了入口函数和出口函数。这样我们就可以按照自己的意愿来对这个模块的 initcleanup 函数进行命名了,不过我们最终要告诉内核维护函数就是这些函数。


    清单 3. 一个简单的但可以正常工作的 LKM(simple-lkm.c)
            
    #include <linux/module.h>
    /* Defines the license for this LKM */
    MODULE_LICENSE("GPL");
    /* Init function called on module entry */
    int my_module_init( void )
    {
      printk(KERN_INFO "my_module_init called.  Module is now loaded./n");
      return 0;
    }
    /* Cleanup function called on module exit */
    void my_module_cleanup( void )
    {
      printk(KERN_INFO "my_module_cleanup called.  Module is now unloaded./n");
      return;
    }
    /* Declare entry and exit functions */
    module_init( my_module_init );
    module_exit( my_module_cleanup );
    

    清单 3 尽管非常简单,但它却是一个真正的 LKM。现在让我们对其进行编译并在一个 2.6 版本的内核上进行测试。2.6 版本的内核为内核模块的编译引入了一种新方法,我发现这种方法比原来的方法简单了很多。对于文件 simple-lkm.c,我们可以创建一个 makefile,其惟一内容如下:

    obj-m += simple-lkm.o
    

    要编译 LKM,请使用 make 命令,如清单 4 所示。


    清单 4. 编译 LKM
            
    [root@plato]# make -C /usr/src/linux-`uname -r` SUBDIRS=$PWD modules
    make: Entering directory `/usr/src/linux-2.6.11'
      CC [M]  /root/projects/misc/module2.6/simple/simple-lkm.o
      Building modules, stage 2.
      MODPOST
      CC      /root/projects/misc/module2.6/simple/simple-lkm.mod.o
      LD [M]  /root/projects/misc/module2.6/simple/simple-lkm.ko
    make: Leaving directory `/usr/src/linux-2.6.11'
    [root@plato]#
    

    结果会生成一个 simple-lkm.ko 文件。这个新的命名约定可以帮助将这些内核对象(LKM)与标准对象区分开来。现在可以加载或卸载这个模块了,然后可以查看它的输出。要加载这个模块,请使用 insmod 命令;反之,要卸载这个模块,请使用 rmmod 命令。lsmod 可以显示当前加载的 LKM(参见清单 5)。


    清单 5. 插入、检查和删除 LKM
            
    [root@plato]# insmod simple-lkm.ko
    [root@plato]# lsmod
    Module                  Size  Used by
    simple_lkm              1536  0
    autofs4                26244  0
    video                  13956  0
    button                  5264  0
    battery                 7684  0
    ac                      3716  0
    yenta_socket           18952  3
    rsrc_nonstatic          9472  1 yenta_socket
    uhci_hcd               32144  0
    i2c_piix4               7824  0
    dm_mod                 56468  3
    [root@plato]# rmmod simple-lkm
    [root@plato]#
    

    注意,内核的输出进到了内核回环缓冲区中,而不是打印到 stdout 上,这是因为 stdout 是进程特有的环境。要查看内核回环缓冲区中的消息,可以使用 dmesg 工具(或者通过 /proc 本身使用 cat /proc/kmsg 命令)。清单 6 给出了 dmesg 显示的最后几条消息。


    清单 6. 查看来自 LKM 的内核输出
            
    [root@plato]# dmesg | tail -5
    cs: IO port probe 0xa00-0xaff: clean.
    eth0: Link is down
    eth0: Link is up, running at 100Mbit half-duplex
    my_module_init called.  Module is now loaded.
    my_module_cleanup called.  Module is now unloaded.
    [root@plato]#
    

    可以在内核输出中看到这个模块的消息。现在让我们暂时离开这个简单的例子,来看几个可以用来开发有用 LKM 的内核 API。


    集成到 /proc 文件系统中

    内核程序员可以使用的标准 API,LKM 程序员也可以使用。LKM 甚至可以导出内核使用的新变量和函数。有关 API 的完整介绍已经超出了本文的范围,因此我们在这里只是简单地介绍后面在展示一个更有用的 LKM 时所使用的几个元素。

    创建并删除 /proc 项

    要在 /proc 文件系统中创建一个虚拟文件,请使用 create_proc_entry 函数。这个函数可以接收一个文件名、一组权限和这个文件在 /proc 文件系统中出现的位置。create_proc_entry 的返回值是一个 proc_dir_entry 指针(或者为 NULL,说明在 create 时发生了错误)。然后就可以使用这个返回的指针来配置这个虚拟文件的其他参数,例如在对该文件执行读操作时应该调用的函数。create_proc_entry 的原型和 proc_dir_entry 结构中的一部分如清单 7 所示。


    清单 7. 用来管理 /proc 文件系统项的元素
            
    struct proc_dir_entry *create_proc_entry( const char *name, mode_t mode,
                                                 struct proc_dir_entry *parent );
    struct proc_dir_entry {
    	const char *name;			// virtual file name
    	mode_t mode;				// mode permissions
    	uid_t uid;				// File's user id
    	gid_t gid;				// File's group id
    	struct inode_operations *proc_iops;	// Inode operations functions
    	struct file_operations *proc_fops;	// File operations functions
    	struct proc_dir_entry *parent;		// Parent directory
    	...
    	read_proc_t *read_proc;			// /proc read function
    	write_proc_t *write_proc;		// /proc write function
    	void *data;				// Pointer to private data
    	atomic_t count;				// use count
    	...
    };
    void remove_proc_entry( const char *name, struct proc_dir_entry *parent );
    

    稍后我们就可以看到如何使用 read_procwrite_proc 命令来插入对这个虚拟文件进行读写的函数。

    要从 /proc 中删除一个文件,可以使用 remove_proc_entry 函数。要使用这个函数,我们需要提供文件名字符串,以及这个文件在 /proc 文件系统中的位置(parent)。这个函数原型如清单 7 所示。

    parent 参数可以为 NULL(表示 /proc 根目录),也可以是很多其他值,这取决于我们希望将这个文件放到什么地方。表 1 列出了可以使用的其他一些父 proc_dir_entry,以及它们在这个文件系统中的位置。


    表 1. proc_dir_entry 快捷变量
    proc_dir_entry在文件系统中的位置
    proc_root_fs/proc
    proc_net/proc/net
    proc_bus/proc/bus
    proc_root_driver/proc/driver

    回调函数

    我们可以使用 write_proc 函数向 /proc 中写入一项。这个函数的原型如下:

    int mod_write( struct file *filp, const char __user *buff,
                   unsigned long len, void *data );
    

    filp 参数实际上是一个打开文件结构(我们可以忽略这个参数)。buff 参数是传递给您的字符串数据。缓冲区地址实际上是一个用户空间的缓冲区,因此我们不能直接读取它。len 参数定义了在 buff 中有多少数据要被写入。data 参数是一个指向私有数据的指针(参见 清单 7)。在这个模块中,我们声明了一个这种类型的函数来处理到达的数据。

    Linux 提供了一组 API 来在用户空间和内核空间之间移动数据。对于 write_proc 的情况来说,我们使用了 copy_from_user 函数来维护用户空间的数据。

    读回调函数

    我们可以使用 read_proc 函数从一个 /proc 项中读取数据(从内核空间到用户空间)。这个函数的原型如下:

    int mod_read( char *page, char **start, off_t off,
                  int count, int *eof, void *data );
    

    page 参数是这些数据写入到的位置,其中 count 定义了可以写入的最大字符数。在返回多页数据(通常一页是 4KB)时,我们需要使用 startoff 参数。当所有数据全部写入之后,就需要设置 eof(文件结束参数)。与 write 类似,data 表示的也是私有数据。此处提供的 page 缓冲区在内核空间中。因此,我们可以直接写入,而不用调用 copy_to_user

    其他有用的函数

    我们还可以使用 proc_mkdirsymlinks 以及 proc_symlink 在 /proc 文件系统中创建目录。对于只需要一个 read 函数的简单 /proc 项来说,可以使用 create_proc_read_entry,这会创建一个 /proc 项,并在一个调用中对 read_proc 函数进行初始化。这些函数的原型如清单 8 所示。


    清单 8. 其他有用的 /proc 函数
            
    /* Create a directory in the proc filesystem */
    struct proc_dir_entry *proc_mkdir( const char *name,
                                         struct proc_dir_entry *parent );
    /* Create a symlink in the proc filesystem */
    struct proc_dir_entry *proc_symlink( const char *name,
                                           struct proc_dir_entry *parent,
                                           const char *dest );
    /* Create a proc_dir_entry with a read_proc_t in one call */
    struct proc_dir_entry *create_proc_read_entry( const char *name,
                                                      mode_t mode,
                                                      struct proc_dir_entry *base,
                                                      read_proc_t *read_proc,
                                                      void *data );
    /* Copy buffer to user-space from kernel-space */
    unsigned long copy_to_user( void __user *to,
                                  const void *from,
                                  unsigned long n );
    /* Copy buffer to kernel-space from user-space */
    unsigned long copy_from_user( void *to,
                                    const void __user *from,
                                    unsigned long n );
    /* Allocate a 'virtually' contiguous block of memory */
    void *vmalloc( unsigned long size );
    /* Free a vmalloc'd block of memory */
    void vfree( void *addr );
    /* Export a symbol to the kernel (make it visible to the kernel) */
    EXPORT_SYMBOL( symbol );
    /* Export all symbols in a file to the kernel (declare before module.h) */
    EXPORT_SYMTAB
    


    通过 /proc 文件系统实现财富分发

    下面是一个可以支持读写的 LKM。这个简单的程序提供了一个财富甜点分发。在加载这个模块之后,用户就可以使用 echo 命令向其中导入文本财富,然后再使用 cat 命令逐一读出。

    清单 9 给出了基本的模块函数和变量。init 函数(init_fortune_module)负责使用 vmalloc 来为这个点心罐分配空间,然后使用 memset 将其全部清零。使用所分配并已经清空的 cookie_pot 内存,我们在 /proc 中创建了一个 proc_dir_entry 项,并将其称为 fortune。当 proc_entry 成功创建之后,对自己的本地变量和 proc_entry 结构进行了初始化。我们加载了 /proc readwrite 函数(如清单 9 和清单 10 所示),并确定这个模块的所有者。cleanup 函数简单地从 /proc 文件系统中删除这一项,然后释放 cookie_pot 所占据的内存。

    cookie_pot 是一个固定大小(4KB)的页,它使用两个索引进行管理。第一个是 cookie_index,标识了要将下一个 cookie 写到哪里去。变量 next_fortune 标识了下一个 cookie 应该从哪里读取以便进行输出。在所有的 fortune 项都读取之后,我们简单地回到了 next_fortune


    清单 9. 模块的 init/cleanup 和变量
            
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/proc_fs.h>
    #include <linux/string.h>
    #include <linux/vmalloc.h>
    #include <asm/uaccess.h>
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("Fortune Cookie Kernel Module");
    MODULE_AUTHOR("M. Tim Jones");
    #define MAX_COOKIE_LENGTH       PAGE_SIZE
    static struct proc_dir_entry *proc_entry;
    static char *cookie_pot;  // Space for fortune strings
    static int cookie_index;  // Index to write next fortune
    static int next_fortune;  // Index to read next fortune
    int init_fortune_module( void )
    {
      int ret = 0;
      cookie_pot = (char *)vmalloc( MAX_COOKIE_LENGTH );
      if (!cookie_pot) {
        ret = -ENOMEM;
      } else {
        memset( cookie_pot, 0, MAX_COOKIE_LENGTH );
        proc_entry = create_proc_entry( "fortune", 0644, NULL );
        if (proc_entry == NULL) {
          ret = -ENOMEM;
          vfree(cookie_pot);
          printk(KERN_INFO "fortune: Couldn't create proc entry/n");
        } else {
          cookie_index = 0;
          next_fortune = 0;
          proc_entry->read_proc = fortune_read;
          proc_entry->write_proc = fortune_write;
          proc_entry->owner = THIS_MODULE;
          printk(KERN_INFO "fortune: Module loaded./n");
        }
      }
      return ret;
    }
    void cleanup_fortune_module( void )
    {
      remove_proc_entry("fortune", &proc_root);
      vfree(cookie_pot);
      printk(KERN_INFO "fortune: Module unloaded./n");
    }
    module_init( init_fortune_module );
    module_exit( cleanup_fortune_module );
    

    向这个罐中新写入一个 cookie 非常简单(如清单 10 所示)。使用这个写入 cookie 的长度,我们可以检查是否有这么多空间可用。如果没有,就返回 -ENOSPC,它会返回给用户空间。否则,就说明空间存在,我们使用 copy_from_user 将用户缓冲区中的数据直接拷贝到 cookie_pot 中。然后增大 cookie_index(基于用户缓冲区的长度)并使用 NULL 来结束这个字符串。最后,返回实际写入 cookie_pot 的字符的个数,它会返回到用户进程。


    清单 10. 对 fortune 进行写入操作所使用的函数
            
    ssize_t fortune_write( struct file *filp, const char __user *buff,
                            unsigned long len, void *data )
    {
      int space_available = (MAX_COOKIE_LENGTH-cookie_index)+1;
      if (len > space_available) {
        printk(KERN_INFO "fortune: cookie pot is full!/n");
        return -ENOSPC;
      }
      if (copy_from_user( &cookie_pot[cookie_index], buff, len )) {
        return -EFAULT;
      }
      cookie_index += len;
      cookie_pot[cookie_index-1] = 0;
      return len;
    }
    

    对 fortune 进行读取也非常简单,如清单 11 所示。由于我们刚才写入数据的缓冲区(page)已经在内核空间中了,因此可以直接对其进行操作,并使用 sprintf 来写入下一个 fortune。如果 next_fortune 索引大于 cookie_index(要写入的下一个位置),那么我们就将 next_fortune 返回为 0,这是第一个 fortune 的索引。在将这个 fortune 写入用户缓冲区之后,在 next_fortune 索引上增加刚才写入的 fortune 的长度。这样就变成了下一个可用 fortune 的索引。这个 fortune 的长度会被返回并传递给用户。


    清单 11. 对 fortune 进行读取操作所使用的函数
            
    int fortune_read( char *page, char **start, off_t off,
                       int count, int *eof, void *data )
    {
      int len;
      if (off > 0) {
        *eof = 1;
        return 0;
      }
      /* Wrap-around */
      if (next_fortune >= cookie_index) next_fortune = 0;
      len = sprintf(page, "%s/n", &cookie_pot[next_fortune]);
      next_fortune += len;
      return len;
    }
    

    从这个简单的例子中,我们可以看出通过 /proc 文件系统与内核进行通信实际上是件非常简单的事情。现在让我们来看一下这个 fortune 模块的用法(参见清单 12)。


    清单 12. 展示 fortune cookie LKM 的用法
            
    [root@plato]# insmod fortune.ko
    [root@plato]# echo "Success is an individual proposition.  
              Thomas Watson" > /proc/fortune
    [root@plato]# echo "If a man does his best, what else is there?  
                    Gen. Patton" > /proc/fortune
    [root@plato]# echo "Cats: All your base are belong to us.  
                          Zero Wing" > /proc/fortune
    [root@plato]# cat /proc/fortune
    Success is an individual proposition.  Thomas Watson
    [root@plato]# cat /proc/fortune
    If a man does his best, what else is there?  General Patton
    [root@plato]#
    

    /proc 虚拟文件系统可以广泛地用来报告内核的信息,也可以用来进行动态配置。我们会发现它对于驱动程序和模块编程来说都是非常完整的。

    例如:

    1,/proc/cmdline 的例子

    static int cmdline_proc_show(struct seq_file *m, void *v)
    {
     seq_printf(m, "%s/n", saved_command_line);
     return 0;
    }

    static int cmdline_proc_open(struct inode *inode, struct file *file)
    {
     return single_open(file, cmdline_proc_show, NULL);
    }

    static const struct file_operations cmdline_proc_fops = {
     .open  = cmdline_proc_open,
     .read  = seq_read,
     .llseek  = seq_lseek,
     .release = single_release,
    };

    static int __init proc_cmdline_init(void)
    {
     proc_create("cmdline", 0, NULL, &cmdline_proc_fops);
     return 0;
    }

     

    2, 在proc目录产生pdc子目录和led,lcd文件

    static int __init led_create_procfs(void)
    {
     struct proc_dir_entry *proc_pdc_root = NULL;
     struct proc_dir_entry *ent;

     if (led_type == -1) return -1;

     proc_pdc_root = proc_mkdir("pdc", 0);
     if (!proc_pdc_root) return -1;
     proc_pdc_root->owner = THIS_MODULE;
     ent = create_proc_entry("led", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);
     if (!ent) return -1;
     ent->data = (void *)LED_NOLCD; /* LED */
     ent->read_proc = led_proc_read;
     ent->write_proc = led_proc_write;
     ent->owner = THIS_MODULE;

     if (led_type == LED_HASLCD)
     {
      ent = create_proc_entry("lcd", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);
      if (!ent) return -1;
      ent->data = (void *)LED_HASLCD; /* LCD */
      ent->read_proc = led_proc_read;
      ent->write_proc = led_proc_write;
      ent->owner = THIS_MODULE;
     }

     return 0;
    }

     

     

     

     

    其实自己一直感觉linux系统中的proc接口很好用,不需要写应用程序就可以测试那些驱动接口,只需要在超级终端中敲入命令,爽。今天把一直以来寄存在其他程序中的proc接口移植到自己的程序中,自己写了个proc接口,分享一下。

    #include <linux/proc_fs.h>-----这个库文件必须
    要在/proc目录下建立一个proc接口,需要调用create_proc_entry这个函数,〕
    eg:
    void test_proc_create(void)
    {
     proc_test_cmd = create_proc_entry("test",S_IWUSR,NULL);
     if (proc_expgpio_cmd)
       proc_test_cmd->write_proc = write_proc_test_cmd;
    }
    这个短短的函数就是用来在/proc目录下建立一个test,如果你用命令
    ehco command > /proc/test
    的话,就会调用到write_proc_test_cmd中,当然这个echo语句是可以传递参数进去的,
    echo command parm1 parm2 > /proc/test
     
    今天我的代码中,我是通过parm1来选择不同的函数,用来测试不同的驱动接口,通过parm2甚至parm3等等用来传递参数到驱动接口,这样就间接地调用到了驱动接口中。
    展开全文
  • 所谓系统调用是指操作系统提供给用户程序调用的一组“特殊”接口用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。 例如用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等...
    Linux系统调用及用户编程接口(API)


    所谓系统调用是指操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。
    例如用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。





    为什么用户程序不能直接访问系统内核提供的服务呢?

    • 这是由于在Linux中,为了更好地保护内核空间,将程序的运行空间分为内核空间用户空间(也就是常称的内核态和用户态),它们分别运行在不同的级别上,在逻辑上是相互隔离的

    • 用户进程在通常情况下不允许访问内核数据,也无法直接调用内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数。

    • 当用户空间的进程需要获得一定的系统服务时,应用程序调用系统调用,这时操作系统就根据系统调用号(每个系统调用被赋予一个系统调用号)使用户进程进入内核空间的具体位置调用相应的内核代码。

    • 进行系统调用时,程序运行空间需要从用户空间进入内核空间,处理完后再返回到用户空间。 




    系统调用按照功能逻辑大致可分为

    • 进程控制
    • 进程间通信
    • 文件系统控制
    • 系统控制
    • 存储管理
    • 网络管理
    • socket 控制
    • 用户管理等


    • 系统调用并不是直接与程序员进行交互的,它仅仅是一个通过软中断机制向内核提交请求,以获取内核服务的接口。在实际使用中程序员调用的通常是:用户编程接口——API ,API遵循了UNIX中最流行的应用编程界面标—POSIX编程标准。

    • Linux中的系统调用包含在Linux的libc库中,通过标准的C函数调用方法可以调用这些系统调用。

    • Linux为每个系统调用在标准C库中设置一个具有同样名字的函数。用户进程用标准C调用序列来调用这些函数,然后,函数又用系统所要求的技术调用相应的内核服务。


    例如函数可将一个或多个C参数送入通用寄存器,然后执行某个产生软中断进入内核的机器指令。从应用角度考虑,可将系统调用视作为C函数




    系统命令相对API更高了一层,它实际上是一个可执行程序,它的内部引用了一个或多个用户编程接口(API)来实现相应的功能。

    如:cp命令调用了用户编程接口(API)open()、write()与read() 



    展开全文
  • /* 导出 GPIO 到用户空间 */ int gpio_export(unsigned gpio, bool direction_may_change); /* gpio_export()的逆操作 */ void gpio_unexport(); /* 创建一个 sysfs 连接到已导出的 GPIO 节点 */ int gpio_...
  • 重用Linux loopback接口地址

    千次阅读 2017-04-29 21:00:29
    loopback接口Linux系统中特殊的虚拟接口,通常不需要对地址和掩码进行特殊设置。由于项目特殊需求,需要使用此网段地址作(127.x.0.0/16)作为设备内部板卡间通信管理地址使用,因此需要对loopback接口的地址和掩码...

    简介

    loopback接口是Linux系统中特殊的虚拟接口,通常不需要对地址和掩码进行特殊设置。由于项目特殊需求,需要使用此网段地址作(127.x.0.0/16)作为设备内部板卡间通信管理地址使用,因此需要对loopback接口的地址和掩码进行修改。本文在Ubuntu系统中进行方案的验证。

    loopback接口简介

    TCP/IP协议中Lookback接口是一个通过软件实现的虚拟网络接口,它不与任何硬件相关联。
    RFC2606中明确指出了loopback地址的标准域名为localhost。在IPv4中,其对应的IP地址一直是127.0.0.1;理论上,整个127 IP段(127.0.0.0~127.255.255.255)的IP地址都为loopback地址,与localhost对应。在IPv6中,localhost对应的IP地址为0:0:0:0:0:0:0:1,一般写作::1。
    [root@localhost ~]# ifconfig lo
    lo        Link encap:Local Loopback  
              inet addr:127.0.0.1  Mask:255.0.0.0
              inet6 addr: ::1/128 Scope:Host
              UP LOOPBACK RUNNING  MTU:65536  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)
    
    [root@localhost ~]# cat /etc/hosts
    127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

    loopback地址和掩码修改

    IP协议规定loopback数据包是不允许在网络中传输的,网络网络接口必须丢弃接收到的loopback数据包。要想改变此缺省规则,需要对loopback地址的判断规则进行修改,对内核进行修改和定制:
    diff --git a/include/linux/in.h b/include/linux/in.h
    index d60122a..32d52db 100644
    --- a/include/linux/in.h
    +++ b/include/linux/in.h
    @@ -238,7 +238,7 @@ struct sockaddr_in {
     
     /* Address to loopback in software to local host.  */
     #define	INADDR_LOOPBACK		0x7f000001	/* 127.0.0.1   */
    -#define	IN_LOOPBACK(a)		((((long int) (a)) & 0xff000000) == 0x7f000000)
    +#define	IN_LOOPBACK(a)		((((long int) (a)) & 0xffff0000) == 0x7f000000)
     
     /* Defines for Multicast INADDR */
     #define INADDR_UNSPEC_GROUP   	0xe0000000U	/* 224.0.0.0   */
    @@ -254,7 +254,7 @@ struct sockaddr_in {
     
     static inline bool ipv4_is_loopback(__be32 addr)
     {
    -	return (addr & htonl(0xff000000)) == htonl(0x7f000000);
    +	return (addr & htonl(0xffff0000)) == htonl(0x7f000000);
     }
     
     static inline bool ipv4_is_multicast(__be32 addr)
    在内核的include/linux/in.h中对loopback地址的掩码进行修改,并修改了loopback地址的判断。按照上述修改,仅127.0.0.0-127.0.255.255为loopback地址,其余127.1.0.0-127.255.255.255均可作为正常接口地址使用。
    配置修改完需要对内核进行重新编译与安装。新内核启动后,可以对loopback地址掩码进行修改:
    [root@localhost ~]# ifconfig lo netmask 255.255.0.0
    [root@localhost ~]# ifconfig lo
    lo        Link encap:Local Loopback  
              inet addr:127.0.0.1  Mask:255.255.0.0
              inet6 addr: ::1/128 Scope:Host
              UP LOOPBACK RUNNING  MTU:16436  Metric:1
              RX packets:4812 errors:0 dropped:0 overruns:0 frame:0
              TX packets:4812 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:2394109 (2.3 MB)  TX bytes:2394109 (2.3 MB)

    配置VLAN子接口

    添加VLAN子接口需要安装VLAN包:
    [root@localhost ~]# apt-get install vlan
    在当前的eth1接口上添加vlan11的子接口,并配置127.11.x.x网段地址。
    [root@localhost ~]# vconfig add eth1 11
    [root@localhost ~]# ifconfig eth1.11 127.11.254.1 netmask 255.255.0.0
    [root@localhost ~]# ifconfig vlan11
    vlan11    Link encap:Ethernet  HWaddr 00:0c:29:22:cf:ac  
              inet addr:127.11.254.1  Bcast:127.11.255.255  Mask:255.255.0.0
              inet6 addr: fe80::20c:29ff:fe22:cfac/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1496  Metric:1
              RX packets:246546 errors:0 dropped:0 overruns:0 frame:0
              TX packets:933262 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:21837553 (21.8 MB)  TX bytes:3719833865 (3.7 GB)
    在虚拟化环境下,我们可以通过virtual switch来连接两个Linux系统,验证此vlan11接口之间的通信。

    接口配置文件

    根据上述对loopback接口的修改,最终相关的接口配置文件如下:
    [root@localhost ~]# cat /etc/network/interfaces
    auto lo
    iface lo inet loopback
            address 127.0.0.1
            netmask 255.255.0.0
            network 127.0.0.0
            broadcast 127.0.255.255
    
    auto vlan11
    iface vlan11 inet static
            address 127.11.254.1
            netmask 255.255.0.0
            network 127.11.0.0
            broadcast 127.11.255.255
            mtu 1496
            vlan_raw_device eth1
    进行到这里,系统已经开始支持127.x.x.x/16的接口地址,相关的报文可以通过网口发出。

    loopback接口掩码加载问题的规避

    在实际测试中,发现系统重启后,loopback地址掩码并不能正常加载,仍为255.0.0.0。这应该是由于NetworkManager加载/etc/network/interfaces配置时,忽略了用户的配置导致。为规避这个问题,可以在/etc/rc.local中对loopback地址的掩码进行配置,在/etc/rc.local中追加下面的语句:
    ifconfig lo netmask 255.255.0.0
    展开全文
  •  系统调用指操作系统提供给用户程序调用的一组“特殊”接口用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。  为什么用户程序不能直接访问系统内核提供的服务呢?这是由于在Linux中,为了更好...
  • Linux系统调用及用户编程接口(API)

    千次阅读 2015-07-27 22:09:45
    所谓系统调用是指操作系统提供给用户程序调用的一组“特殊”接口用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。例如用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。...
  • linux无线驱动接口简介

    千次阅读 2014-03-01 23:23:02
    Linux无线驱动接口有两种标准接口,wext(Wireless ...wext是由开发者Jean Tourrilhes (惠普实验室软件工程师)定义的一套供用户层软件访问无线驱动以及驱动事件通知用户层的接口。当时无线驱动的协议栈软件比较多
  • #!/bin/sh CROSS=arm-linux- CC=$(CROSS)gcc FLAGS=-lpthread -wall objects = main.o gpio_ctrl.o gpio_test:$(objects) $(CC) -o gpio_test $(objects) rm -f gpio_test *.o
  • linux usb接口的音频设备

    万次阅读 2013-11-06 18:43:05
    本章导读 在Linux中,先后出现了音频设备的两种框架OSS和ALSA,本节将在介绍数字...17.3节阐述了Linux OSS音频设备驱动的组成、mixer接口、dsp接口用户空间编程方法。 17.4节阐述了Linux ALSA音频设备驱动的组成、ca
  • Linux 网络接口管理相关命令介绍

    千次阅读 2010-03-04 14:36:00
    Unix/Linux网络接口管理相关命令介绍Unix/Linux 网络接口管理相关命令介绍获取网络适配器列表信息在获知网络接口使用状态之前,你可能需要知道你当前的系统使用的网络适配器,在 Linux 和 AIX 中查看网络设备器的...
  • 用户接口

    万次阅读 2017-04-14 10:46:39
    用户接口(User Interface) 博文目录:  1,接口含义  2,接口分类  3,界面分类  4,系统调用 一、接口含义   用户接口(User Interface,简称 UI)是系统和用户之间进行交互和信息交换的媒介,它...
  • 内核提供接口用户空间程序,便于用户进行进行信息的读取和配置。procfs和sysctl都可以导出内部信息,其中procfs主要用于导出只读信息,sysctl导出可写的。此外还有sysfs,ioctl1. procfsprocfs是内核提供的,一种...
  •  所谓系统调用是操作系统提供给用户程序调用的一组“特殊”接口用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。 用户可以通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。 ...
  • 就是用户空间应用程序和内核提供的服务之间的一个接口。由于服务是在内核中提供的,因此无法执行直接调用;相反,您必须使用一个进程来跨越用户空间与内核之间的界限。在特定架构中实现此功能的方法会有所不同。因此...
  • Linux网络接口配置文件ifcfg-eth0解析

    万次阅读 2013-12-12 22:27:54
    在Windows上配置网络比较容易,有图形化界面可操作。在Linux中往往是通过命令修改文件...而在SLES 10中却是/etc/sysconfig/network/ifcfg-eth-id-xx:xx:xx:xx:xx:xx(后面是该网络接口的MAC地址);在SLES 11中是/etc/
  • Linux中往往是通过命令修改文件的方式配置网络,因此不仅需要知道配置哪个文件,还要知道文件中每个配置参数的功能。在Redhat/Fedora等Linux中,网络配置文件一般是/etc/sysconfig/network-scripts/ifcfg-eth0;而...
  • 系统调用:操作系统提供给用户程序调用的一组“特殊接口,可以通过这组接口获得操作系统内核提供的服务。例如:创建进程、实现进程调度、进程管理,进程间通信,文件系统控制,系统控制,存储管理,网络管理,socket...
  • linux syslog 调用接口

    千次阅读 2015-09-07 11:40:23
    在实际的使用过程中,我们可以通过配置文件和查看相应的日志文件来使用syslog。然而,在许多应用场景下,...因此,下面将详细介绍如何通过syslog日志系统提供的API调用接口,来使用程序实现对syslog的使用。主要涉及
  • Linux用户、用户组、文件权限设置

    千次阅读 2015-09-16 10:26:30
    最近打算更仔细学习一下linux操作系统。先是恶补了一下用户用户组、文件...linux的权限系统主要是由用户用户组和权限组成。 用户就是一个个的登录并使用linux用户linux内部用UID表示。 用户组就是用户的分组
  • Linux ifconfig命令:配置网络接口

    千次阅读 2019-03-22 18:59:13
    ifconfig 是 Linux 中査看和临时修改 IP 地址的命令,其基本信息如下: 命令名称:ifconfig。 英文原意:configure a network interface。 所在路径:/sbin/ifconfig。 执行权限:超级用户。 功能描述:配置网络接口...
  • Linux开始支持Thunderbolt 3接口

    千次阅读 2016-03-07 08:59:06
    随着Windows PC和MAC逐渐开始支持新一代高速接口Thunderbolt 3(雷电接口),Linux PC也即将迎来这一科技。戴尔最新推出的XPS 13开发者版本搭载Ubuntu Linux系统,适配了这种接口。通过USB Type-C接口提供支持。...
  • linux系统调用接口整理

    千次阅读 2016-03-10 20:58:00
     以下是Linux系统调用的一个...这可能是你在互联网上所能看到的唯一一篇中文注释的Linux系统调用列表,即使是简单的字母序英文列表,能做到这么完全也是很罕见的。  按照惯例,这个列表以man pages第2节,即
  • linux中常用API详细介绍,包含应用例程 1 字符测试篇 2 2 字符串转换篇 10 3 内存控制篇 17 4 日期时间篇 22 5 内存及字符串操作篇 28 6 常用数学函数篇 45 7 用户组篇 56 8 数据结构及算法篇 78 9 文件操作篇 85 10...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 423,290
精华内容 169,316
关键字:

linux用户接口

linux 订阅