mcpField
D
device-drivers

device-drivers

github.com/mohitmishra786開発269370 回インストール18 週間前に更新
リポジトリ

device-drivers について

Linuxカーネルのデバイスドライバー開発を支援するエージェント向けスキルです。一般的なドライバーモデルに加え、キャラクターデバイス、割り込み、DMA、レジスター管理、電源管理、udevを扱います。

主な機能

  1. 01Platform、I2C、SPIのドライバーモデル
  2. 02キャラクターデバイスのライフサイクル
  3. 03ハードIRQとスレッドIRQの処理
  4. 04DMAマッピングと同期
  5. 05regmapとランタイム電源管理
  6. 06デバイスノード用udevルール

活用シーン

  1. 01メモリーマップドハードウェアのドライバー作成
  2. 02キャラクターデバイスインターフェースの実装
  3. 03デバイス割り込みやDMAの処理
  4. 04デバイスノードへのユーザー空間アクセス設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill device-drivers

インストール後、Claude Code で/device-driversを入力して読み込まれたか確認します。

詳細

デバイスドライバー

目的

Linux カーネルのデバイスドライバー開発について、エージェントをガイドします。対象は、ドライバーモデル(platform_driver、i2c_driver、spi_driver)、キャラクターデバイスのライフサイクル、スレッド化 IRQ を含む IRQ 処理、DMA エンジン API、レジスターを抽象化する regmap、ランタイム電源管理、ユーザー空間のデバイスノードに対する udev ルールです。

使用する場面

  • メモリーマップドハードウェア向けのプラットフォームドライバーを作成する
  • read/write/ioctl を備えたキャラクターデバイスを実装する
  • ハードウェア割り込み(ハード IRQ とスレッド化 IRQ)を処理する
  • DMA のコヒーレントマッピングまたはストリーミングマッピングを設定する
  • regmap でレジスターアクセスを抽象化する
  • /dev ノードのアクセス権限に関する udev ルールを設定する

ワークフロー

1. ドライバーモデルの概要
Device tree / ACPI → bus (platform, i2c, spi, pci)
    → struct device → struct device_driver
        → probe() / remove()
// platform_driver.c — minimal platform driver
#include <linux/module.h>
#include <linux/platform_device.h>

static int my_probe(struct platform_device *pdev)
{
    struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    void __iomem *base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(base))
        return PTR_ERR(base);
    dev_info(&pdev->dev, "probed at %pa\n", &res->start);
    return 0;
}

static void my_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "removed\n");
}

static struct platform_driver my_driver = {
    .probe  = my_probe,
    .remove = my_remove,
    .driver = { .name = "my-device", .owner = THIS_MODULE },
};

module_platform_driver(my_driver);
MODULE_LICENSE("GPL");
2. キャラクターデバイスのライフサイクル
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "mydev"
#define MINOR_BASE  0
#define MINOR_COUNT 1

static dev_t dev_num;
static struct cdev my_cdev;
static struct class *dev_class;

static ssize_t my_read(struct file *filp, char __user *buf,
                       size_t count, loff_t *ppos)
{
    char kbuf[64] = "hello from kernel\n";
    size_t len = strlen(kbuf);
    if (*ppos >= len)
        return 0;
    if (count > len - *ppos)
        count = len - *ppos;
    if (copy_to_user(buf, kbuf + *ppos, count))
        return -EFAULT;
    *ppos += count;
    return count;
}

static const struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .read  = my_read,
};

static int __init mydev_init(void)
{
    int ret;
    ret = alloc_chrdev_region(&dev_num, MINOR_BASE, MINOR_COUNT, DEVICE_NAME);
    if (ret)
        return ret;

    cdev_init(&my_cdev, &my_fops);
    my_cdev.owner = THIS_MODULE;
    ret = cdev_add(&my_cdev, dev_num, MINOR_COUNT);
    if (ret)
        goto err_cdev;

    dev_class = class_create(DEVICE_NAME);
    device_create(dev_class, NULL, dev_num, NULL, DEVICE_NAME);
    return 0;

err_cdev:
    unregister_chrdev_region(dev_num, MINOR_COUNT);
    return ret;
}
# After loading module
ls -l /dev/mydev
cat /dev/mydev
3. IRQ 処理
#include <linux/interrupt.h>

static irqreturn_t my_hardirq(int irq, void *dev_id)
{
    // Minimal work: acknowledge, schedule bottom half
    return IRQ_WAKE_THREAD;
}

static irqreturn_t my_threaded(int irq, void *dev_id)
{
    // Process data — can sleep
    process_ring_buffer();
    return IRQ_HANDLED;
}

static int request_device_irq(struct device *dev, int irq)
{
    return devm_request_threaded_irq(dev, irq, my_hardirq, my_threaded,
                                     IRQF_ONESHOT, "mydev", dev);
}
パターン使用する場面
ハード IRQ のみマイクロ秒単位の処理で、ブロッキングが不要な場合
スレッド化 IRQI2C/SPI の読み取り、スケジューリング、ミューテックスを扱う場合
IRQF_ONESHOTレベルトリガー方式の場合。スレッド化ハンドラーが戻るまで IRQ をマスクします
4. DMA エンジン API
#include <linux/dma-mapping.h>

// Coherent (consistent) mapping — CPU and device see same memory
void *cpu_addr;
dma_addr_t dma_handle;
cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, GFP_KERNEL);

// Streaming (single) mapping for already-allocated buffers
dma_addr_t dma = dma_map_single(dev, kernel_buf, size, DMA_TO_DEVICE);
dma_sync_single_for_device(dev, dma, size, DMA_TO_DEVICE);
// ... device reads ...
dma_unmap_single(dev, dma, size, DMA_TO_DEVICE);
# Debug DMA mappings
cat /sys/kernel/debug/dma_buf/bufinfo 2>/dev/null
5. regmap による抽象化
#include <linux/regmap.h>

static const struct regmap_config my_regmap_config = {
    .reg_bits   = 8,
    .val_bits   = 8,
    .max_register = 0xFF,
};

// I2C regmap (typical for sensors)
static struct regmap *regmap;

regmap_write(regmap, 0x01, 0x80);  // set config register
regmap_read(regmap, 0x02, &val);   // read status
regmap_update_bits(regmap, 0x03, MASK, VALUE);

regmap はロック、キャッシュ、一括アクセスを処理します。新しいドライバーでは、生の i2c_smbus_* よりも regmap を優先してください。

6. 電源管理
#include <linux/pm_runtime.h>

static int my_runtime_suspend(struct device *dev)
{
    // Gate clocks, put hardware in low-power state
    return 0;
}

static int my_runtime_resume(struct device *dev)
{
    // Restore registers, enable clocks
    return 0;
}

static const struct dev_pm_ops my_pm_ops = {
    .runtime_suspend = my_runtime_suspend,
    .runtime_resume  = my_runtime_resume,
};

// In probe:
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);  // ensure powered on
7. udev ルール
# /etc/udev/rules.d/99-mydev.rules
KERNEL=="mydev", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="i2c-dev", KERNEL=="i2c-1", GROUP="i2c", MODE="0660"
sudo udevadm control --reload-rules
sudo udevadm trigger
udevadm info -a -n /dev/mydev
8. I2C および SPI ドライバーのスタブ
// i2c_driver
static const struct i2c_device_id my_i2c_id[] = {
    { "sensor-chip", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, my_i2c_id);

static struct i2c_driver my_i2c_driver = {
    .driver = { .name = "sensor-chip" },
    .probe  = my_i2c_probe,
    .remove = my_i2c_remove,
    .id_table = my_i2c_id,
};
module_i2c_driver(my_i2c_driver);

よくある問題

症状原因修正方法
probe failed with -EBUSYリソースの競合、または二重プローブデバイスツリーの status を確認します。devm_* でクリーンアップします
IRQ ストームハンドラー内で ACK を行っていないハード IRQ 内で割り込みを ACK します。IRQF_ONESHOT を使用します
DMA コヒーレンシーのバグ同期方向が誤っている境界で dma_sync_single_for_cpu/device を呼び出します
/dev/node が見つからないclass_create/device_create が失敗したdmesg を確認し、cdev_add の戻り値を検証します
copy_to_user のフォールトユーザー空間ポインターが無効access_ok で検証します
ランタイム PM のハングpm_runtime_put が不足しているget/put の数を合わせ、devm_pm_runtime_enable を使用します

関連スキル

  • skills/kernel/kernel-internals — ドライバーの基盤となる VFS、メモリーアロケーター
  • skills/low-level-programming/linux-kernel-modules — Kbuild、モジュールの読み込み、署名
  • skills/kernel/kernel-debugging — ドライバーのバグを調査するための kgdb、ftrace
  • skills/embedded/zephyr — 組み込み RTOS のドライバーモデルとの比較
  • skills/observability/ebpf — カーネルを変更せずにドライバーイベントをトレース
  • skills/kernel/kernel-testing — ドライバーロジック向けの KUnit テスト

コメント