team-ebi

技術系の情報共有

GPIO割込みシグナルドライバコンパイルや使い方 のソース

mkdev.sh


#!/bin/bash

SDK_DIR=$HOME/sdk1
KERNEL_DIR=$SDK_DIR/board-support/linux-4.4.12+gitAUTOINC+3639bea54a-g3639bea54a
TOOLCHAIN=$SDK_DIR/linux-devkit/sysroots/x86_64-arago-linux/usr/bin/arm-linux-gnueabihf-

make -C $KERNEL_DIR M=`pwd` ARCH=arm CROSS_COMPILE=$TOOLCHAIN  modules


Makefile


obj-m := gpiisig.o

insdev.sh


#!/bin/sh

# usage :
# ./insdev.sh ./gpiisig.ko gpii gpii devmajor=60 pin=7 sig=10

ins=$1
module=$2
device=$3
mode="664"
/sbin/insmod $ins $* || exit 1
rm -f /dev/${device}[0-1]
major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)
mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
chmod $mode /dev/${device}[0-1]

gpiisig.c ソース

GPIO割込みをシグナル(ソフト割り込み)でもらったら応答速度改善

gpiisig.c



/*********************************************************************************

    gpiisig : GPIO Interrupt signal driver
    
    Copyright (C) 2016 team-ebi

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    
*********************************************************************************/

#ifndef MODULE
#define MODULE
#endif
#ifndef __KERNEL__
#define __KERNEL__
#endif

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


static char *devname="gpii";
module_param(devname, charp, S_IRUGO);
static int devmajor=60;
module_param(devmajor, int, S_IRUGO);

#define GPII_MAX    (10)
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10))
    #define GPII_MAX_v  GPII_MAX
#else
    static int i_GPII_MAX = GPII_MAX;
    #define GPII_MAX_v  &i_GPII_MAX
#endif

static int pin[GPII_MAX] = {0};
module_param_array(pin, int, GPII_MAX_v, S_IRUGO);
static int sig[GPII_MAX] = {SIGIO,SIGIO,SIGIO,SIGIO,};
module_param_array(sig, int, GPII_MAX_v, S_IRUGO);
static int pins = 0;
module_param(pins, int, S_IRUGO);


typedef struct stGpiiParam
{
    int             int_enable;
    int             minor;
    unsigned long   handle_counter;
    struct fasync_struct    *my_asqueue;
}GPII_PARAM;

GPII_PARAM  gpii_param[GPII_MAX];


static irqreturn_t my_int_handler(int irq, void *devid)
{
    GPII_PARAM  *p= (GPII_PARAM*)devid;
    if((p->int_enable) && (p == &gpii_param[p->minor]))
    {
        // int val = gpio_get_value(pin[minor]);
        int minor = p->minor;
        if(p->handle_counter < 3)
        {
            p->handle_counter++;
            printk(KERN_INFO"gpii%d(%d) interrupt %d\n", minor, pin[minor], sig[minor]);
        }

        kill_fasync(&p->my_asqueue, sig[minor], POLL_IN);

        return IRQ_HANDLED;
    }
    return IRQ_NONE;
}

static int my_open(struct inode * inode, struct file * fp)
{
    int minor = MINOR(inode->i_rdev);
    if(minor < GPII_MAX)
    {
        int retval = 0;
        char msg[20];
        int err = 0;
        int irq=0;
        GPII_PARAM *p = &gpii_param[minor];
        printk("gpii%d(%d) open(%lX)\n", minor, pin[minor], (unsigned long)fp->f_version);
        p->minor = minor;
        fp->private_data = p;

        retval = gpio_request(pin[minor], NULL);
        if(retval)
            printk(KERN_ERR"gpii%d(%d) gpi req failed(%d)\n", minor, pin[minor], retval);

        retval = gpio_direction_input(pin[minor]);
        if (retval != 0)
            printk(KERN_ERR"gpii%d(%d) dir input failed(%d)\n", minor, pin[minor], retval);

        irq = gpio_to_irq(pin[minor]);
        if (irq < 0) {
            printk(KERN_ERR"gpii%d(%d) get irq failed(%d)\n", minor, pin[minor], irq);
            err = irq;
            return -EBUSY;
        }

        retval = irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
        if (retval != 0)
            printk(KERN_ERR"gpii%d(%d) edge failed(%d)\n", minor, pin[minor], retval);

        sprintf(msg, "gpii%d_%d", minor, pin[minor]);
        retval = request_irq(irq, my_int_handler, 
                0,
                msg, &gpii_param[minor]);

        if (retval) {
            printk(KERN_ERR"gpii%d(%d) req irq failed(%d)\n", minor, pin[minor], retval);
            err = retval;
            return -EBUSY;
        }
    }

    return 0;
}

static int my_close(struct inode * inode, struct file * fp)
{
    int minor = MINOR(inode->i_rdev);
    if(minor < GPII_MAX)
    {
        int irq=0;
        GPII_PARAM *p = &gpii_param[minor];

        p->int_enable = 0;
        printk("gpii%d(%d) close(%lX)\n", minor, pin[minor], (unsigned long)fp->f_version);
        fasync_helper( -1, fp, 0, &p->my_asqueue );

        irq = gpio_to_irq(pin[minor]);
        if (irq < 0)
        {
            printk(KERN_ERR"gpii%d(%d) irq err(%d)\n", minor, pin[minor], irq);
            return -EBUSY;
        }

        printk(KERN_INFO"gpii%d(%d) close\n", minor, pin[minor]);

        gpio_free(pin[minor]);
        free_irq(irq, &gpii_param[minor]);

        return 0;
    }
    return -EBUSY;
}

static int my_fasync(int fd, struct file *fp, int on)
{
    GPII_PARAM  *p= (GPII_PARAM*)fp->private_data;
    int minor = p->minor;
    int retval;
    if(p == &gpii_param[minor])
    {
        printk("gpii%d(%d) fasync %d %p %d(%lX)\n", minor, pin[minor], fd, fp, on, (unsigned long)fp->f_version);
        retval = fasync_helper( fd, fp, on, &p->my_asqueue );
        p->int_enable = 1;
        return retval;
    }
    else
    {
        for(minor=0; minor < pins; minor++)
        {
            if(p == &gpii_param[minor])
            {
                printk("gpii%d(%d) (fasync) %d %p %d(%lX)\n", minor, pin[minor], fd, fp, on, (unsigned long)fp->f_version);
                retval = fasync_helper( fd, fp, on, &p->my_asqueue );
                p->int_enable = 1;
                return retval;
            }
        }
    }
    return -EBUSY;
}


static struct file_operations my_ope =
{
    //              NULL,       // loff_t   llseek(struct file *, loff_t, int)
    //              NULL,       // ssize_t  read(struct file *, char *, size_t, loff_t *)
    //              NULL,       // ssize_t  write(struct file *, const char *, size_t, loff_t *)
    //              NULL,       // int  readdir(struct file *, void *, filldir_t)
    //              my_poll,    // uint poll(struct file *, struct poll_table_struct *)
    //              my_ioctl,   // int  ioctl(struct inode *, struct file *, uint, ulong)
    //              NULL,       // int  mmap(struct file *, struct vm_area_struct *)
    .open       =   my_open,    // int  open(struct inode *, struct file *)
    //              NULL,       // int  flush(struct file *)
    .release    =   my_close,   // int  release(struct inode *, struct file *)
    //              NULL,       // int  fsync(struct file *, struct dentry *)
    .fasync     =   my_fasync,  // int  fasync(int, struct file *, int)
    //              NULL,       // int  check_media_change(kdev_t dev)
    //              NULL,       // int  revalidate(kdev_t dev)
    //              NULL,       // int  lock(struct file *, int, struct file_lock *)
};


int init_module(void)
{

    memset(&gpii_param, 0, sizeof(gpii_param));

    printk(KERN_ERR"gpii%d-%d registration done\n", 0, pins);

    printk("install '%s' into major %d\n", devname, devmajor);
    if(register_chrdev(devmajor,devname,&my_ope))
    {
        printk("device registration error\n");
        return -EBUSY;
    }

    return 0;
}

void cleanup_module(void)
{
    printk("remove '%.40s' from major %d\n", devname, devmajor);
    unregister_chrdev(devmajor, devname);
};


MODULE_LICENSE("GPL");
MODULE_AUTHOR("team-ebi");

GPIO割込みシグナルドライバコンパイルや使い方

gpiisig.cはKernel​ ​module(デバイスドライバ)だからコンパイルすると *.ko になる。 コンパイル手順はスクリプト見たほうが早い。

mkdev.sh

で環境をつくって、

Makefile

コンパイル。 Mは大文字なので注意。

こうすることにより、Kernel丸ごとコンパイルすることなく、Kernelの部品であるKernel​ ​moduleだけをコンパイルできる。

Kernel​ ​module(デバイスドライバ)*.koを実行する手順は次の通り。

まず、デバイスを登録する。

insdev.sh

ここで作られるデバイスファイル/dev/*をアプリからopenすることによって使える。

そして、アプリからopenしてアプリ上に用意したsignal​ ​handlerを登録すると、割込み発生時にこのシグナルハンドラが呼ばれる。

次はアプリの書き方かな?

GPIO割込みをシグナル(ソフト割り込み)でもらったら応答速度改善

結構必要としてる人はいるはず。

GPIOからの割込み信号を使う場合、楽なのはgpioディレクトリopenしてpollかselect。
そしたら遅延がひどい。ミニマムは良いのにワーストが酷いらしい。
じゃあやっぱりデバドラで割込み拾わんと、って言っても、
割込みもらった後の処理がかなり膨大でデバドラでは無理そうだ、
だそうだ。シグナルならいわゆるソフト割込みだから遅延少ないはず。しらんけど。
て言ったら書く羽目になった。なんでだ。

というわけで、これはGPIO入力をハードウェア割込みで受けて、アプリにソフトウェア割込み=シグナルで渡すドライバ。
今まで数十msあった最大遅延が1ms以下になった、まじかとのお言葉。

はい。マジすか頂きました。

使い方は、、、他のdriver群と一緒にmakeしてもらって、
insdevして、それをアプリからopen("/dev/gpii"...)云々したら
シグナルハンドラが呼ばれるようになる、、、
アプリはいいか。また今度ね。


追記:組込みLinuxの話でした。


GPLにしといたから使ってみて。

ソース gpiisig.c