Linux MISCデバイスドライバの概要と実装

MISCデバイスドライバの基本概念 MISC(Miscellaneous)は「雑多な」ことを意味し、特定の分類に当てはまらないハードウェアデバイスに対して使用されます。このドライバは通常、platformバスドライバのサブシステムとして実装されます。

MISCデバイスはすべて主デバイス番号10を使用し、異なるデバイスごとに独自のサブデバイス番号を割り当てます。cdev構造体の自動生成により、従来の手動登録プロセスが不要になります。

以下はmiscdevice構造体の定義です:

struct miscdevice {
    int minor;                          /* サブデバイス番号 */
    const char *name;                   /* デバイス名 */
    const struct file_operations *fops; /* ファイル操作関数ポインタ */
    struct list_head list;
    struct device *parent;
    struct device *this_device;
    const struct attribute_group **groups;
    const char *nodename;
    umode_t mode;
};

ドライバ開発では、minornamefopsの3つのメンバ変数を初期化する必要があります。misc_register()関数でデバイスを登録すると、以下の処理が自動的に実行されます:

alloc_chrdev_region();
cdev_init();
cdev_add();
class_create();
device_create();

アンロード時にはmisc_deregister()関数を使用します:

cdev_del();
unregister_chrdev_region();
device_destroy();
class_destroy();

実装例:Beepデバイスドライバ

  1. デバイスツリーの設定 stm32mp15-pinctrl.dtsiにピンコンフィギュレーションを追加:
beep_pins_a: beep-pins {
    pins {
        pinmux = <STM32_PINMUX('C', 7, GPIO)>;
        drive-push-pull;
        bias-pull-up;
        output-high;
        slew-rate = <0>;
    };
};

stm32mp157d-atk.dtsにデバイスノードを追加:

beep {
    compatible = "alientek,beep";
    status = "okay";
    pinctrl-0 = <&beep_pins_a>;
    pinctrl-names = "default";
    beep-gpio = <&gpioc 7 GPIO_ACTIVE_LOW>;
};
  1. ドライバコード 以下はリファクタリング後の実装例です:
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>

#define DRIVER_NAME "gpio_beep"
#define MINOR_NUM 144

struct gpio_beep_data {
    int gpio_pin;
};

static int gpio_beep_init(struct device_node *np)
{
    int ret;
    struct gpio_beep_data *data = kmalloc(sizeof(*data), GFP_KERNEL);
    
    data->gpio_pin = of_get_named_gpio(np, "beep-gpio", 0);
    if (!gpio_is_valid(data->gpio_pin)) {
        return -EINVAL;
    }
    
    ret = gpio_request(data->gpio_pin, "beep");
    if (ret) return ret;
    
    gpio_direction_output(data->gpio_pin, 1);
    return 0;
}

static ssize_t gpio_beep_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    char val;
    if (copy_from_user(&val, buf, 1)) return -EFAULT;
    
    gpio_set_value(gpio_beep_data->gpio_pin, val ? 0 : 1);
    return 1;
}

static const struct file_operations gpio_beep_fops = {
    .owner = THIS_MODULE,
    .write = gpio_beep_write,
};

static struct miscdevice gpio_beep_dev = {
    .minor = MINOR_NUM,
    .name = DRIVER_NAME,
    .fops = &gpio_beep_fops,
};

static int gpio_beep_probe(struct platform_device *pdev)
{
    int ret;
    ret = gpio_beep_init(pdev->dev.of_node);
    if (ret) return ret;
    
    return misc_register(&gpio_beep_dev);
}

static int gpio_beep_remove(struct platform_device *pdev)
{
    misc_deregister(&gpio_beep_dev);
    gpio_free(gpio_beep_data->gpio_pin);
    return 0;
}

static const struct of_device_id gpio_beep_of_match[] = {
    { .compatible = "alientek,beep" },
    {}
};

static struct platform_driver gpio_beep_driver = {
    .probe = gpio_beep_probe,
    .remove = gpio_beep_remove,
    .driver = {
        .name = "gpio_beep_driver",
        .of_match_table = gpio_beep_of_match,
    },
};

module_platform_driver(gpio_beep_driver);
MODULE_LICENSE("GPL");
  1. テストアプリケーション
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int fd, val;
    char cmd;
    
    if (argc != 3) {
        printf("Usage: %s <device> <0|1>\n", argv[0]);
        return -1;
    }
    
    fd = open(argv[1], O_RDWR);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    
    cmd = atoi(argv[2]);
    write(fd, &cmd, 1);
    close(fd);
    return 0;
}

実行手順

  1. Makefile作成:
obj-m := gpio_beep.o
  1. コンパイル:
make -C /lib/modules/$(uname -r)/build M=$(PWD) modules
arm-linux-gnueabihf-gcc beep_test.c -o beep_test
  1. テスト:
insmod gpio_beep.ko
./beep_test /dev/gpio_beep 1
./beep_test /dev/gpio_beep 0
rmmod gpio_beep

タグ: linux MISCドライバ platformバス GPIO操作

7月16日 20:19 投稿