Raspberry PIのGPIOデバイスドライバを作ってみました。作成したデバイスドライバの登録とユーザープログラムからのGPIOのOpenが行えます。ドライバを作成するにあたって、作成環境は「Raspberry Piのデバイスドライバ開発環境の構築 」を使用しましました。また、Raspberry PIのバージョンは次のようになっています。

# uname -a
Linux raspberrypi 3.18.0-trunk-rpi #1 PREEMPT Debian 3.18.5-1~exp1+rpi16 (2015-03-28) armv6l GNU/Linux

このページで示すGPIOデバイスドライバの作成および登録・実行はすべてルート権限で行ってください。

GPIOデバイスドライバ

最初にGPIOデバイスドライバを示します。ユーザーがデバイスドライバをアクセスするときに使用するのメジャー番号とマイナー番号を説明します。

  • メジャー番号: カーネルがドライバを識別するための番号
  • マイナー番号: ドライバがデバイスを識別するための番号

デバイスドライバを作成するときは、alloc_chrdev_region関数を使用します。alloc_chrdev_region関数の説明を次に示します。

alloc_chrdev_region関数
他のデバイスとデバイス番号が被らないように、メジャー番号を動的に確保する。


linuxではデバイス番号をdev_t型で扱います。dev_t型変数からメジャー番号を取得する場合にはMAJOR(dev_t)を使います。dev_t型変数からマイナー番号を取得する場合にはMINOR(dev_t)を使います。整数型変数のメジャー番号とマイナー番号からdev_t型のデバイス番号を作成するには、MKDEV()を使います。
マイナー番号は登録しませんが、デバイスドライバが扱うデバイスの総数として指定する必要があります。

/*  
 *  gpiodrv_out.c - The GPIO device driver. 
 */
#include <linux/module.h>   /* Needed by all modules MODULE macro, THIS_MODULE */
#include <linux/kernel.h>   /* Needed for KERN_INFO  printk()*/
#include <linux/init.h>
#include <linux/types.h>   	/* dev_t */
#include <linux/kdev_t.h>  	/* MAJOR() */
#include <linux/fs.h>		/* alloc_chrdev_region(), ...  */
#include <linux/device.h>
#include <linux/cdev.h>		// cdev_*() * */
#include <asm/uaccess.h>	// copy_from_user, copy_to_user
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/errno.h>	/* error codes */
#include <uapi/asm-generic/errno-base.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/time.h>
 
/* User-defined macros */
#define NUM_GPIO_PINS 			1
#define MAX_GPIO_NUMBER 		1
#define DEVICE_NAME 			"raspi-gpio"
#define BUF_SIZE 				512

/* User-defined data types */
enum state 		{low, high};
enum direction	{in, out};
 
struct raspi_gpio_dev {
	struct cdev cdev;
	enum state state;
	enum direction dir;
	spinlock_t lock; 
};

/* Declaration of entry points */
static int raspi_gpio_open(struct inode *inode, struct file *filp);
static ssize_t raspi_gpio_write (struct file *filp,
	const char *buf,
	size_t count,
	loff_t *f_pos);

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

/* Forward declaration of functions */
static int raspi_gpio_init(void);
static void raspi_gpio_exit(void);
unsigned int millis (void);
/* Global varibles for GPIO driver */
struct raspi_gpio_dev *raspi_gpio_devp[NUM_GPIO_PINS];
static dev_t first;
static struct class *raspi_gpio_class;


static int raspi_gpio_open (struct inode *inode, struct file *filp) {
	struct raspi_gpio_dev *raspi_gpio_devp;
	unsigned int gpio;

	printk(KERN_INFO "GPIOstart open \n");
	gpio = iminor(inode);
	printk(KERN_INFO "GPIO[%d] opened\n", gpio);
	raspi_gpio_devp = container_of(inode->i_cdev,struct raspi_gpio_dev,cdev);
	
	filp->private_data = raspi_gpio_devp;
	return 0;
}
static int __init raspi_gpio_init(void){
	int i = 0, ret, index = 0;
	
	printk(KERN_DEBUG "start gpio_init\n");
	
	// キャラクタデバイス番号の動的取得
	if (alloc_chrdev_region(
		&first,			// 最初のデバイス番号が入る
		0,				// マイナー番号の開始番号	
		NUM_GPIO_PINS,	// 取得するマイナー番号数
		DEVICE_NAME		// モジュール名(/proc/devicesに出力する名前)
		) < 0) {
		printk(KERN_DEBUG "Cannot register device\n");
		return -1;
	}
	
	// ドライバのクラス登録
	// /sys/class 配下にドライバ情報を作成
	if ((raspi_gpio_class = class_create( 
		THIS_MODULE,	// 固定
		DEVICE_NAME		// クラス名。/sys/class/(クラス名)
	)) == NULL) {
		printk(KERN_DEBUG "Cannot create class %s\n", DEVICE_NAME);
		unregister_chrdev_region(first, NUM_GPIO_PINS);
		return -EINVAL;
	}
	
	raspi_gpio_devp[index] = kmalloc(sizeof(struct raspi_gpio_dev),GFP_KERNEL);
	if (!raspi_gpio_devp[index]) {
		printk("Bad kmalloc\n");
		return -ENOMEM;
	}
	// シングルGPIO要求-初期構成
	if (gpio_request_one(
		25,						// Gpio
		GPIOF_OUT_INIT_LOW,		// 初期構成
		NULL					// ラベル名
	) < 0) {
		printk(KERN_ALERT "Error requesting GPIO %d\n", i);
		return -ENODEV;
	}
	raspi_gpio_devp[index]->dir = out;
	raspi_gpio_devp[index]->state = low;
	raspi_gpio_devp[index]->cdev.owner = THIS_MODULE;
	
	// スピンロックは、ビジー・ウェイトによるロックを使って排他制御を保証する
	spin_lock_init(&raspi_gpio_devp[index]->lock);
	
	// キャラクタデバイス初期化
	// ファイルオペレーション構造体の指定もする
	cdev_init(&raspi_gpio_devp[index]->cdev, &raspi_gpio_fops);
	
	// デバイスに追加する
	if ((ret = cdev_add( &raspi_gpio_devp[index]->cdev,(first + i),1))) {
		printk (KERN_ALERT "Error %d adding cdev\n", ret);
		device_destroy (raspi_gpio_class,
		MKDEV(MAJOR(first),
		MINOR(first) + i));
		class_destroy(raspi_gpio_class);
		unregister_chrdev_region(first, NUM_GPIO_PINS);
		return ret;
	}
	
	// マイナー番号に基づくデバイスの作成
	//devlist[]に対応して、それぞれのデバイスを設定しています。
	if (device_create( raspi_gpio_class,NULL,MKDEV(MAJOR(first), MINOR(first)+i),NULL,"raspiGpio%d",i) == NULL) {
		class_destroy(raspi_gpio_class);
		unregister_chrdev_region(first, NUM_GPIO_PINS);
		return -1;
	}

	
	printk("RaspberryPi GPIO driver initialized\n");
	return 0;
}	
	
static void __exit raspi_gpio_exit(void)
{
	int i = 0;
	
	// デバイス番号の返却
	unregister_chrdev_region(first, NUM_GPIO_PINS);
	
	kfree(raspi_gpio_devp[i]);
	
	gpio_direction_output(25, 0);
	device_destroy ( raspi_gpio_class,MKDEV(MAJOR(first), MINOR(first) + i));
	gpio_free(25);
	class_destroy(raspi_gpio_class);
	printk(KERN_INFO "RaspberryPi GPIO driver removed\n");
}
module_init(raspi_gpio_init);
module_exit(raspi_gpio_exit);
	
MODULE_LICENSE("GPL");
MODULE_AUTHOR("info@tomosoft.jp");
MODULE_DESCRIPTION("GPIO device driver");

GPIOデバイスドライバの登録は次のコマンドを使います。

# insmod gpiodrvout.ko

デバイスドライバの削除は次のコマンドを使います。
# rmmod gpiodrvout.ko

GPIOデバイスドライバの登録が完了すると、デバイス番号が登録されているかを次のコマンドで確認します。

# ls /dev
raspiGpio0

GPIOデバイスドライバの登録状況を確認します。GPIOのデバイスドライバ「gpiodrvout 」が登録されていることが確認できます。

# lsmod
Module                  Size  Used by
gpiodrvout              2147  0
snd_bcm2835            18961  0
snd_pcm                76716  1 snd_bcm2835
snd_seq                56172  0
snd_seq_device          5605  1 snd_seq
snd_timer              19204  2 snd_pcm,snd_seq
snd                    53596  5 snd_bcm2835,snd_timer,snd_pcm,snd_seq,snd_seq_device
soundcore               5579  1 snd
spidev                  6533  0
spi_bcm2708             6578  0

実行時のログを次のコマンドで確認します。

# dmesg
     ・
     ・
     ・
[  234.041575] start gpio_init
[  234.044875] RaspberryPi GPIO driver initialized
[  238.669997] GPIOstart open
[  238.670038] GPIO[0] opened

GPIOのポート番号「0」でオープンしています。これはデバイス番号を「0」にしたためです。

ユーザープログラム

このGPIOデバイスドライバを使用してアクセスするユーザープログラムを次に示します。

/*
* Name : output_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 4
#define PATH_SIZE 20
int main(int argc, char **argv)
{
	int i = 0, index = 0, value;
	int fd;
	char path[PATH_SIZE];
	char buf[BUF_SIZE];
	
	if (argc != 2) {
		printf("Option low/high must be used\n");
		exit(EXIT_FAILURE);
	}
	// Open all GPIO pins
	snprintf(path, sizeof(path), "/dev/raspiGpio0");
	fd = open(path, O_WRONLY);
	if (fd < 0) {
		perror("Error opening GPIO");
		exit(EXIT_FAILURE);
	}
}

“]