team-ebi

技術系の情報共有

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

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

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

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

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

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


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


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


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

	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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/moduleparam.h>
#include <asm/siginfo.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>


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");