Raspberry PIのGPIOのデバイスドライバを作成します。「bcm2835 ライブラリによるスイッチ入力とLEDの点滅 」で使用したタクトスイッチから入力させます。

GPIOデバイスドライバ

GPIOポートに接続されたタクトスイッチから接点信号を入力するため、「Raspberry PIのGPIOのデバイスドライバ – Open 」で作成したデバイスドライバーのファイル操作構造体「file_operations」に、GPIOの読み込みデバイスドライバ関数「raspi_gpio_read」のアドレスと、GPIOの読み込みデバイスドライバ関数「raspi_gpio_read」を追加します。
デバイスのマイナー番号をGPIOポート番号とする設計ですが、今回は強制的にタクトスイッチから接点信号を入力するため、GPIOポート「24」とし、raspi_gpio_read関数の7行目に「gpio = 24;」を挿入しました。
raspi_gpio_read関数で使用しているgpio_direction_input関数とgpio_get_value関数について次に示します。

gpio_direction_input
入出力制御を入力側にする。
gpio_get_value
パラメータで指定されたGPIO番号から信号レベルを入力して戻り値で呼び出し元に通知する。

また、システム・コールの引数を、ユーザー空間に対して出し入れするために使用できる複数の関数が、用意されています。オプションには、基本型に対応した単純な関数 (get_user や put_user など) や、構造体や配列などのデータ・ブロックを移動させるための関数 (copy_from_user と copy_to_user) が含まれます。
今回使用するput_user 関数は、 呼び出し方法が「put_user(x,ptr);」となり、カーネル変数 (x) が指定する値を、指定されたユーザー空間アドレス (ptr) に移動させます。

ファイル操作構造体file_operations

/* File operation structure */
static struct file_operations raspi_gpio_fops = {
	.owner = THIS_MODULE,
	.open = raspi_gpio_open,
	.read = raspi_gpio_read,
	.write = raspi_gpio_write,
};

GPIOの読み込みデバイスドライバ関数raspi_gpio_read

static ssize_t
raspi_gpio_read ( struct file *filp,char *buf,size_t count,loff_t *f_pos){
	unsigned int gpio;
	char byte;
	
//	gpio = iminor(filp->f_path.dentry->d_inode);
	gpio = 24;
	byte = '0' + gpio_get_value(gpio);
	put_user(byte, buf);
	return 1;
}

GPIOの書き込みデバイスドライバ関数raspi_gpio_write

static ssize_t raspi_gpio_write ( struct file *filp,
	const char *buf,
	size_t count,
	loff_t *f_pos)
{

	if (strcmp(kbuf, "in") == 0) {
		if (raspi_gpio_devp->dir != in) {
			printk(KERN_INFO "Set gpio[%d] direction: input\n", gpio);
			spin_lock_irqsave(&raspi_gpio_devp->lock, flags);
			gpio_direction_input(gpio);
			raspi_gpio_devp->dir = in;
			spin_unlock_irqrestore(&raspi_gpio_devp->lock, flags);
		}
	} else {
               ・
               ・
               ・

ユーザープログラム

ユーザプログラムではWrite関数とread関数をそれぞれ1回づつ呼び出します。最初にGPIOからタクトスイッチのデータの入力するため、Write関数で”in”を書き込みます。次に、タクトスイッチから、read関数を用いてスイッチ情報を入力します。タクトスイッチを押してユーザープログラムを実行すると、レベル情報が「Logic level: 1」と表示されます。押さないでユーザープログラムを実行すると、レベル情報が「Logic level: 0」と表示されます。

/*
* Name : input_test.c
*/
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#define NUM_GPIO_PINS 21
#define MAX_GPIO_NUMBER 32
#define BUF_SIZE 3
#define PATH_SIZE 20
int main(int argc, char **argv)
{
	int i = 0, index = 0;
	int fd;
	char path[PATH_SIZE];
	char buf[BUF_SIZE];
	char readBuf[2];
	
	printf("gpio open\n");
	// Open all GPIO pins
	snprintf(path, sizeof(path), "/dev/raspiGpio0");
	fd = open(path, O_RDWR);
	if (fd < 0) {
		perror("Error opening GPIO pin");
		exit(EXIT_FAILURE);
	}
	
	// Set direction of GPIO pins to input
	printf("Set pins to input\n");
	strncpy(buf, "in", 2);
	buf[2] = '\0';
	if (write(fd, buf, sizeof(buf)) < 0) {
		perror("write, set pin input");
		exit(EXIT_FAILURE);
	}
	
	// Read logic level of GPIO pins and display them to the terminal
	printf("gpio read\n");
	if (read(fd, readBuf, 1) < 1) {
		perror("read");
		exit(EXIT_FAILURE);
	}
	readBuf[1] = '\0';
	printf("GPIO pin: 24 Logic level: %s\n", readBuf);

	return EXIT_SUCCESS;
}

実行したデバイスドライバのログを次に示します。

# dmesg
     ・
     ・
[ 2500.431564] GPIOstart open
[ 2500.431602] GPIO[0] opened
[ 2500.433749] Request from user: in
[ 2500.433788] Set gpio[24] direction: input

デバイスドライバのraspi_gpio_write関数により、”[ 2500.433788] Set gpio[24] direction: input”がログされており、GPIOポート24がin制御されていることが確認できます。

タクトスイッチを押さないでユーザープログラムを実行すると、次のように「Logic level: 0」が表示されます。

# ./input_test
gpio open
Set pins to input
gpio read
GPIO pin: 24 Logic level: 0

タクトスイッチを押してユーザープログラムを実行すると、次のように「Logic level: 1」が表示されます。

# ./input_test
gpio open
Set pins to input
gpio read
GPIO pin: 24 Logic level: 1