バージョン管理方法についての個人的備忘録～７セグLED用デバイスドライバ開発



##はじめに
ラズパイROS１桁７セグLEDテストボードのドライバの開発をするのに、まだ`git`を使えていないので、とりあえず自分で簡単なバージョン管理方法を自分だけのために決めてみることにしました。

１．バージョンの表記は、「A.B.C」形式の十進数で行う。
２．ボード上のデバイス機能用のコードをすべて変更したら、 A の桁を繰り上げる。
３．ボード上のデバイス機能ごとのコード変更は、Ｂ の桁を繰り上げる。[^2]
４．機能コードごとの開発途中の変更は、Ｃ の桁を繰り上げる。
５．バージョン表記の横のコメントにＢの桁の変更点を簡潔に入れる。

[^2]: Quittaでの実験報告は、Ａ桁かＢ桁の変更をしたときに行います。

##ソースコードの変更点と履歴

var.0.2では、LEDの数字表示とドット表示を別デバイスとする操作を実装しました。[^1]

[^1]: デバイスのメジャー番号をそれぞれに付与する形式です。

【履歴】
・[ver.0.1 LED表示全体をまとめて操作するドライバの作成](https://qiita.com/ivvakanni/items/ef872eb1120ea0a10355)
　（開発環境、参考図書等もこの記事に記載しています）

・[テストボードの制作](https://qiita.com/ivvakanni/items/e29378dc666d8204fa58)
　（ブレッドボードは使用していません）
 
【関連記事】
・[テストボードの７セグＬＥＤの数字表示のROS操作用`c++`のコード(ドライバ ver.0.1用)](https://qiita.com/ivvakanni/items/424d3e80b4df23a60121)

##コード

```c:odssled-0-2.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>

MODULE_AUTHOR("Ivvakanni");
MODULE_DESCRIPTION("one digit seven segment LED control simple driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION("0.2.4");    // change dev name

/* for raspbery pi */
#define BCM2708_PERI_BASE   0x20000000 
/* for raspberry pi 2 or 3 */
#define BCM2837_PERI_BASE   0x3F000000
#define MYDEV_GPIO_BASE     (BCM2837_PERI_BASE + 0x200000)

#define MAP_SPACE           1024
#define GPIO_CLR            10
#define GPIO_OUT            7

#define SS_DEV_NAME         "odledss"
#define SS_MAJOR_NUM        0
#define SS_MINOR_NUM        1
#define SS_UNIT_CNT         1

#define DP_DEV_NAME         "odleddp"
#define DP_MAJOR_NUM        0
#define DP_MINOR_NUM        1
#define DP_UNIT_CNT         1

static volatile u32 *gpio_register = NULL;

static dev_t ss_dev;
static struct cdev ss_cdv;
static struct class *ss_cls = NULL;

static dev_t dp_dev;
static struct cdev dp_cdv;
static struct class *dp_cls = NULL;

static void gpio_function_setup(int n){
        const u32 gpio_number = n;
        const u32 port = gpio_number / 10;
        const u32 shift = (gpio_number % 10) * 3;
        const u32 zero_mask = ~(0x07 << shift);
        gpio_register[port] = (gpio_register[port] & zero_mask) | (0x01 << shift);
}

static int gpio_put(int arg){
        int v = 0;
        v = arg;
        return v;
}

/* ss */
static ssize_t ss_write(struct file* filp, const char* buf, size_t count, loff_t* pos)
{
        char c;
        if(raw_copy_from_user(&c,buf,sizeof(char)))
                return -EFAULT;

        if(c == '0')
                gpio_register[GPIO_CLR] = 0x0F << 7;
        else if(c > '0'){
                int val = gpio_put(c);
                gpio_register[GPIO_CLR] = 0x0F << 7;
                gpio_register[GPIO_OUT] = ( val & 0x0F ) << 7;
        }
        return 1;
}

static struct file_operations ss_fops = {
        .owner = THIS_MODULE,
        .write = ss_write
};

/* dp */
static ssize_t dp_write(struct file* filp, const char* buf, size_t count, loff_t* pos)
{
        char c;
        if(raw_copy_from_user(&c,buf,sizeof(char)))
                return -EFAULT;

        if(c == '0')
                gpio_register[GPIO_CLR] = 0x0F << 11;
        else if(c == '1'){
                gpio_register[GPIO_OUT] = 0x0F << 11;
        }
        return 1;
}

static struct file_operations dp_fops = {
        .owner = THIS_MODULE,
        .write = dp_write
};

static int __init init_mod(void)
{
        gpio_register = ioremap_nocache(MYDEV_GPIO_BASE, MAP_SPACE);

        gpio_function_setup(7);
        gpio_function_setup(8);
        gpio_function_setup(9);
        gpio_function_setup(10);
        gpio_function_setup(11);

        /* ss */
        alloc_chrdev_region(&ss_dev,SS_MAJOR_NUM,SS_MINOR_NUM,SS_DEV_NAME);
        printk(KERN_INFO "%s load success at %d\n",__FILE__,MAJOR(ss_dev));

        cdev_init(&ss_cdv, &ss_fops);
        ss_cdv.owner = THIS_MODULE;
        cdev_add(&ss_cdv, ss_dev, SS_UNIT_CNT);
        ss_cls = class_create(THIS_MODULE, SS_DEV_NAME);
        device_create(ss_cls, NULL, ss_dev, NULL, SS_DEV_NAME"%u",MINOR(dp_dev));

        /* dp */
        alloc_chrdev_region(&dp_dev,DP_MAJOR_NUM,DP_MINOR_NUM,DP_DEV_NAME);
        printk(KERN_INFO "%s load success at %d\n",__FILE__,MAJOR(dp_dev));

        cdev_init(&dp_cdv, &dp_fops);
        dp_cdv.owner = THIS_MODULE;
        cdev_add(&dp_cdv, dp_dev, DP_UNIT_CNT);
        dp_cls = class_create(THIS_MODULE, DP_DEV_NAME);
        device_create(dp_cls, NULL, dp_dev, NULL, DP_DEV_NAME"%u",MINOR(dp_dev));

        return 0;
}

static void __exit exit_mod(void)
{
        /* ss */
        cdev_del(&ss_cdv);
        device_destroy(ss_cls, ss_dev);
        class_destroy(ss_cls);
        unregister_chrdev_region(ss_dev, 1);
        printk(KERN_INFO "%s unloaded from %d\n",__FILE__,MAJOR(ss_dev));

        /* dp */
        cdev_del(&dp_cdv);
        device_destroy(dp_cls, dp_dev);
        class_destroy(dp_cls);
        unregister_chrdev_region(dp_dev, 1);
        printk(KERN_INFO "%s unloaded from %d\n",__FILE__,MAJOR(dp_dev));
}

module_init(init_mod);
module_exit(exit_mod);
```
##おわりに

機能ごとにデバイスを分けたので、コードは書きやすかったです。ただし、同じような記載が繰り返されているので少し読みにくさとスマートさには欠けます。ただ、素人が作っているものなので、まずは、ちゃんと動作することを優先していきたいので、しばらくはこの形式で作成していきます。

プログラム記載上の変更点としては、GPIOピンの操作をするうえで、ver.0.1では、マクロでGPIO番号を管理していましたが直接番号を記載する方法にしました。これは、ラズパイのGPIOの場合は、もともといろいろな専用機能がピンにデフォルトで配置されているので、完全に「空き」状態のGPIOは限られていてピンへの機能割り当てを頻繁に変える余地もないのでマクロで指定するメリットが余り感じられないことが理由です。将来的には、テストボードの設計そのものを変えていくつもりですが、その時にまとめて作業をすればよいだけなので、ここでもソースコードから動作の理解がしやすい方法を優先しました。

次回は、タクトスイッチの読み込みを実装します。欲張らずに、まずは、二つのボタンのうちの一つの状態を読めるようにしてみたいです。