読者です 読者をやめる 読者になる 読者になる

Linuxデバイスドライバ開発

オライリー第三版(2.6系)を参考にドライバ開発を行います。動作確認はカーネル3.2で実施します。

デバイスドライバにreadを実装する

前回はデバイスドライバにopenとcloseを実装した。

public2016.hatenablog.com


さすがにそれだけじゃ寂しいからreadを実装してみる。

ただ個人的にはread(とwriteも)を真面目に実装するよりは、「ioctl」を使えばいいじゃないって思ってる。もちろんドライバによってはread/writeが最適なものもあるんだろうけどハードウェアを制御しようと思うとreadの「前回読み込んだ続きを読み込む」って挙動は少し使いにくく感じる。

まぁとりあえず見てきましょ。


readを実装するためには・・・

file_operations構造体にreadに対応する関数を追加

前回はopenとcloseだけだったけど色々追加。

static struct file_operations test_driver_fops ={
    .owner          = THIS_MODULE,
    .open           = test_driver_open,
    .release        = test_driver_release,
    .read           = test_driver_read,
    .write          = test_driver_write,
    .unlocked_ioctl = test_driver_ioctl,
};

今回は使わないけどwriteとunlocked_ioctlも入れておく


次は〜

なんでもいいから読み込むデータを用意する

普通はハードウェアのデータを読み込むんだけど適当なハードがないからグローバル変数で代替しとく。

char gb_buf[101];


ドライバをロードした時の処理内で値を詰めておく。
データの終わりを示すために−1を入れておく。

static int __init test_driver_init(void)
{
  //
    //色々な処理
    //

    int i;
    for(i=0;i<100;i++)
    {
        gb_buf[i]=(char)i;
    }
    gb_buf[99]=-1;
   return 0;
}

お次は・・・

データの先頭アドレスをprivate_dataに突っ込む

private_dataとはfile構造体(fopenの戻り値のfile構造体とは別物!!)のメンバで、ドライバ開発者が好き勝手使用出来る領域として用意されている(と思う)。


このfile構造体変数はfile_operationsに定義した関数をカーネルが呼び出すときに渡してくれるのだ。
つまりこの仕組みを使うと、各関数間で簡単にデータの共有ができるのだ。


もっと具体的にいえば関連付けられた以下の関数群が呼び出される時、

test_driver_open
test_driver_release
test_driver_read
test_driver_write
test_driver_ioctl

これらの関数の間で1つのfile構造体変数を共有できる、ということなのだね。


処理の流れとしては・・・

open時にprivate_dataにグローバル配列の先頭を突っ込んで

//open
filp->private_data = gb_buf;
   //(char gb_buf[101]の先頭)

read開始時はprivate_dataを読みだして、読み出しの先頭として扱って

//read{
char* p_gb_buf=file->private_data;

read終了時はprivate_dataを最後のポインタで更新する

file->private_data=&p_gb_buf[count];
//}read

※注意※

readだけならこの実装でも問題無いけどwriteがあると問題!write関数に渡されるfile->private_dataも当然共有しているからreadした分writeが影響を受けてしまう。read/writeを真面目に実装しようと何らかの手段でread用インデックスと、write用インデックスを管理しないといけなさそう


最後は〜

read関数を実装する

かなり雑な作りだけどとりあえず書いたread関数。

ssize_t test_driver_read(struct file * file, char * buff, size_t count, loff_t *pos)
{
    char* p_gb_buf=file->private_data;
    char* p_temp=kmalloc(count, GFP_KERNEL);
    int i;

    printk(KERN_DEBUG "read call count=%d\n",count);

    for(i=0;i<count;++i)
    {
        if(p_gb_buf[i] ==-1)
        {
            break;
        }
        p_temp[i]=p_gb_buf[i];
    }
   if (copy_to_user(buff, p_temp, i)) {
      kfree(p_temp);
      return -EFAULT;
   }
    file->private_data=&p_gb_buf[i];
    kfree(p_temp);
    return count;
}

重要なポイントは test_driver_readの引数の「buff」の扱い。

buffはユーザランドのデータに紐付いてるもので、readの結果はこのbuffに書き込む必要があるんだけど、実はカーネルのデータをユーザランドに常に正しく転送するのはムヅカシイ


例えば・・・

buff[0]=100;

とすることでユーザランドに転送することもできるんだけどはてさてこのコード、成功したのか?

・・・

答えは、このコードだけでは転送に成功したのか失敗したのか分からない、だね。
だいたいの場合は成功するんだけど、データ転送なんて常に正しくできるか分からない。


だから転送時は先人の開発したcopy_to_user関数を使いましょ。転送失敗時の処理が書けるしいいね。

copy_to_user(user_buff, const data, count)

ちなみにcopy_to_userは排他処理必須なんだけど、このコードでは排他してなにので注意です。

一応全ソース類をのっけておく。バグってるかもしれないので実行は自己責任でオナシャス
全部同じ階層にぶちこんで上から説明通りに実行すれば出来るはずデス

次回は「ioctl」の説明をするよ!

test_driver.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/uaccess.h>    

#define TEST_DRIVER_MINOR_COUNT 4

//===================================
//プロトタイプ宣言
//===================================
//デバイスドライバの初期化・終了関数
static __init int test_driver_init(void);
static __exit void test_driver_exit(void);


//ファイル操作に紐付いた関数群
int test_driver_open(struct inode *inode, struct file *filp);
int test_driver_release(struct inode *inode, struct file *filp);
ssize_t test_driver_read(struct file * file, char * buff, size_t count, loff_t *pos);
ssize_t test_driver_write(struct file * file, const char * buff, size_t count, loff_t *pos);
long test_driver_ioctl( struct file *filp,unsigned int cmd, unsigned long arg);

//===================================
//グローバル変数
//===================================
static dev_t dev_id; 
static struct cdev c_dev; 

static struct file_operations test_driver_fops ={
    .owner          = THIS_MODULE,
    .open           = test_driver_open,
    .release        = test_driver_release,
    .read           = test_driver_read,
    .write          = test_driver_write,
    .unlocked_ioctl = test_driver_ioctl,
};


char gb_buf[101];
//===================================
//ファイル操作に紐付いた関数群
//===================================
//openに対応する関数
int test_driver_open(struct inode *inode, struct file *filp) 
{
    int dev_id_tmp = inode->i_rdev;
    filp->private_data = gb_buf;
    //filp->private_data = &inode->i_rdev;
    printk(KERN_DEBUG "open call minor no= %d\n",MINOR(dev_id_tmp));
    return 0;
}

//closeに対応する関数
int test_driver_release(struct inode *inode, struct file *filp)
{
    printk(KERN_DEBUG "release call\n");
    return 0;
}

//readに対応する関数
ssize_t test_driver_read(struct file * file, char * buff, size_t count, loff_t *pos)
{
    printk(KERN_DEBUG "read call count=%d\n",count);
    char* p_gb_buf=file->private_data;
    char* p_temp=kmalloc(count, GFP_KERNEL);
    
    int i;
    for(i=0;i<count;++i)
    {
        if(p_gb_buf[i] ==-1)
        {
            break;
        }
        p_temp[i]=p_gb_buf[i];
        //buff[i]=p_gb_buf[i];
    }
   if (copy_to_user(buff, p_temp, i)) {
      kfree(p_temp);
      return -EFAULT;
   }
    file->private_data=&p_gb_buf[i];
    kfree(p_temp);
    return i;
}

//writeに対応する関数
ssize_t test_driver_write(struct file * file, const char * buff, size_t count, loff_t *pos)
{
    return 0;
}


//ioctlに対応する関数
long test_driver_ioctl( struct file *filp,unsigned int cmd, unsigned long arg)
{
    printk(KERN_DEBUG "ioctl call\n");

    return 0;
}
//===================================
//デバイスドライバの初期化・終了関数
//===================================
//モジュール初期化関数
static int __init test_driver_init(void)
{
   int ret=0;
   printk(KERN_DEBUG "↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\n");
   printk(KERN_DEBUG "モジュール初期化関数呼び出し\n");
   ret = alloc_chrdev_region(&dev_id, 0, TEST_DRIVER_MINOR_COUNT, "test_driver_basic");
   if(ret<0)
    {
       printk(KERN_DEBUG "alloc_chrdev_region fail!!\n");
        return -1;
    }
    printk(KERN_DEBUG "獲得したメジャー番号 = %d\n",MAJOR(dev_id));
    
    
    cdev_init(&c_dev, &test_driver_fops);
    c_dev.owner = THIS_MODULE;
    ret = cdev_add(&c_dev, dev_id, TEST_DRIVER_MINOR_COUNT);
   if(ret!=0)
    {
       printk(KERN_DEBUG "cdev_add fail!!\n");
        return -1;
    }
    int i;
    for(i=0;i<100;i++)
    {
        gb_buf[i]=(char)i;
    }
    gb_buf[99]=-1;
   return 0;
}

//モジュール終了関数
static void __exit test_driver_exit(void)
{
    printk(KERN_DEBUG "モジュール終了処理呼び出し\n");
    cdev_del(&c_dev);
    unregister_chrdev_region(dev_id, TEST_DRIVER_MINOR_COUNT);
    printk(KERN_DEBUG "↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑\n");
}

//===================================
// マクロ
//===================================
module_init(test_driver_init);
module_exit(test_driver_exit);
MODULE_DESCRIPTION("device_driver_test");
MODULE_LICENSE("GPL2");


make でビルドして
sudo make install でロードしてくださいな

Makefile

obj-m := driverModule.o
driverModule-objs := test_driver.o
PWD := $(shell pwd)
all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
install:
	insmod driverModule.ko
uninstall:
	rmmod driverModule.ko
clean:
	rm -f *.o *.ko *.mod.c Module.* module* *~

バイスファイルを作るシェル
sudo ./make_device_file.sh でOk
make_device_file.sh

#!/bin/bash
DEVICE="test_driver_basic"
MAJOR=`awk "\\$2==\"$DEVICE\" {print \\$1}" /proc/devices`
echo $MAJOR
rm -rf ./test_dev
mknod ./test_dev c $MAJOR 0
exit 0

ユーザランドのテストプログラム。雑・・・
gcc test.c
でビルドしてa.outを実行してね。
test.c

#include <stdio.h>
#include <fcntl.h>      // O_RDWR
#include <unistd.h>
#include <string.h>
int main()
{
    ssize_t read_size;
    int i;
    char buf[10];
    int fd=open("./test_dev",O_RDWR);
    printf("get fd =%d \n",fd);
    if(fd<0)
    {
        return -1;
    }
    int k;
    for(k=0;k<20;k++)
    {
        if(k==5)
        {
            close(fd);
            printf("close/open\n");
            fd=open("./test_dev",O_RDWR);
        }
        read_size = read(fd, buf, 7);
        if(read_size==0)
        {
            break;
        }
        printf("read size = %d\n ",read_size);
        for(i=0;i<10;++i)
        {
            printf("read buf[%d] = %d\n",i,buf[i]);
        }
        memset(buf, '\0', 10);
        printf("======%d=====\n",k);
    }

    close(fd);
    return 0;
}


実行結果

=======================
get fd =3 
read size = 7
 read buf[0] = 0
read buf[1] = 1
read buf[2] = 2
read buf[3] = 3
read buf[4] = 4
read buf[5] = 5
read buf[6] = 6
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======0=====
read size = 7
 read buf[0] = 7
read buf[1] = 8
read buf[2] = 9
read buf[3] = 10
read buf[4] = 11
read buf[5] = 12
read buf[6] = 13
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======1=====
read size = 7
 read buf[0] = 14
read buf[1] = 15
read buf[2] = 16
read buf[3] = 17
read buf[4] = 18
read buf[5] = 19
read buf[6] = 20
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======2=====
read size = 7
 read buf[0] = 21
read buf[1] = 22
read buf[2] = 23
read buf[3] = 24
read buf[4] = 25
read buf[5] = 26
read buf[6] = 27
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======3=====
read size = 7
 read buf[0] = 28
read buf[1] = 29
read buf[2] = 30
read buf[3] = 31
read buf[4] = 32
read buf[5] = 33
read buf[6] = 34
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======4=====
close/open
read size = 7
 read buf[0] = 0
read buf[1] = 1
read buf[2] = 2
read buf[3] = 3
read buf[4] = 4
read buf[5] = 5
read buf[6] = 6
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======5=====
read size = 7
 read buf[0] = 7
read buf[1] = 8
read buf[2] = 9
read buf[3] = 10
read buf[4] = 11
read buf[5] = 12
read buf[6] = 13
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======6=====
read size = 7
 read buf[0] = 14
read buf[1] = 15
read buf[2] = 16
read buf[3] = 17
read buf[4] = 18
read buf[5] = 19
read buf[6] = 20
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======7=====
read size = 7
 read buf[0] = 21
read buf[1] = 22
read buf[2] = 23
read buf[3] = 24
read buf[4] = 25
read buf[5] = 26
read buf[6] = 27
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======8=====
read size = 7
 read buf[0] = 28
read buf[1] = 29
read buf[2] = 30
read buf[3] = 31
read buf[4] = 32
read buf[5] = 33
read buf[6] = 34
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======9=====
read size = 7
 read buf[0] = 35
read buf[1] = 36
read buf[2] = 37
read buf[3] = 38
read buf[4] = 39
read buf[5] = 40
read buf[6] = 41
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======10=====
read size = 7
 read buf[0] = 42
read buf[1] = 43
read buf[2] = 44
read buf[3] = 45
read buf[4] = 46
read buf[5] = 47
read buf[6] = 48
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======11=====
read size = 7
 read buf[0] = 49
read buf[1] = 50
read buf[2] = 51
read buf[3] = 52
read buf[4] = 53
read buf[5] = 54
read buf[6] = 55
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======12=====
read size = 7
 read buf[0] = 56
read buf[1] = 57
read buf[2] = 58
read buf[3] = 59
read buf[4] = 60
read buf[5] = 61
read buf[6] = 62
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======13=====
read size = 7
 read buf[0] = 63
read buf[1] = 64
read buf[2] = 65
read buf[3] = 66
read buf[4] = 67
read buf[5] = 68
read buf[6] = 69
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======14=====
read size = 7
 read buf[0] = 70
read buf[1] = 71
read buf[2] = 72
read buf[3] = 73
read buf[4] = 74
read buf[5] = 75
read buf[6] = 76
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======15=====
read size = 7
 read buf[0] = 77
read buf[1] = 78
read buf[2] = 79
read buf[3] = 80
read buf[4] = 81
read buf[5] = 82
read buf[6] = 83
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======16=====
read size = 7
 read buf[0] = 84
read buf[1] = 85
read buf[2] = 86
read buf[3] = 87
read buf[4] = 88
read buf[5] = 89
read buf[6] = 90
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======17=====
read size = 7
 read buf[0] = 91
read buf[1] = 92
read buf[2] = 93
read buf[3] = 94
read buf[4] = 95
read buf[5] = 96
read buf[6] = 97
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======18=====
read size = 1
 read buf[0] = 98
read buf[1] = 0
read buf[2] = 0
read buf[3] = 0
read buf[4] = 0
read buf[5] = 0
read buf[6] = 0
read buf[7] = 0
read buf[8] = 0
read buf[9] = 0
======19=====