writing-char-drivers について
Linux キャラクターデバイス実装を支援するエージェント向けスキルです。登録、ユーザー空間 I/O、ioctl、mmap、ポーリングの実用パターンと注意点を扱います。
主な機能
- 01キャラクターデバイスの登録
- 02安全なユーザー空間コピー
- 03ioctl コマンド設計
- 04デバイスメモリの mmap
- 05ポーリングと非同期 I/O
活用シーン
- 01/dev ノードを介したハードウェア公開
- 02カーネルの read、write、poll の実装
- 03型安全な ioctl インターフェースの設計
- 04検査を伴うデバイスメモリのマッピング
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill writing-char-driversインストール後、Claude Code で/writing-char-driversを入力して読み込まれたか確認します。
詳細
キャラクタードライバーの作成
目的
Linux のキャラクターデバイス実装について、エージェントをガイドします。struct file_operations、cdev の登録、安全なユーザー空間へのコピー、ioctl の設計、基本的な mmap を扱い、skills/kernel/device-drivers より踏み込んだ内容に焦点を当てます。
使用する場面
- ハードウェアを
/dev/mydevとして公開する - カーネルから
read/write/pollを実装する - 型安全なマクロを使って
ioctlコマンドを定義する - デバイスの MMIO をユーザー空間にマッピングする(慎重に行ってください)
ワークフロー
1. キャラクターデバイスの登録
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define MY_MAJOR 0 /* 0 = dynamic alloc */
#define MY_MINOR 0
static dev_t devno;
static struct cdev my_cdev;
static struct class *class;
static const struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
.unlocked_ioctl = my_ioctl,
.llseek = no_llseek,
};
static int __init my_init(void)
{
int ret = alloc_chrdev_region(&devno, MY_MINOR, 1, "mydev");
if (ret)
return ret;
cdev_init(&my_cdev, &my_fops);
ret = cdev_add(&my_cdev, devno, 1);
if (ret)
goto err_cdev;
class = class_create("mydev");
device_create(class, NULL, devno, NULL, "mydev");
return 0;
err_cdev:
unregister_chrdev_region(devno, 1);
return ret;
}
最近のドライバーでは、probe 内で devm_* バリアントを使うことがよくあります。
2. 安全なユーザー空間 I/O
static ssize_t my_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
char kbuf[128];
ssize_t len;
if (*ppos >= sizeof(kbuf))
return 0;
len = min(count, sizeof(kbuf) - *ppos);
memcpy(kbuf, "data", 4);
if (copy_to_user(buf, kbuf + *ppos, len))
return -EFAULT;
*ppos += len;
return len;
}
__user ポインターを直接参照しないでください。
3. ioctl のパターン
#include <linux/ioctl.h>
#define MY_IOC_MAGIC 'k'
#define MY_IOC_RESET _IO(MY_IOC_MAGIC, 0)
#define MY_IOC_SET _IOW(MY_IOC_MAGIC, 1, int)
static long my_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case MY_IOC_RESET:
return 0;
case MY_IOC_SET: {
int val;
if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
return -EFAULT;
return 0;
}
default:
return -ENOTTY;
}
}
固定サイズの構造体には _IOWR を使用してください。bi-arch では compat_ioctl を優先してください。
4. mmap(デバイスメモリ)
static int my_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long size = vma->vm_end - vma->vm_start;
phys_addr_t phys = device_phys_base;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
return remap_pfn_range(vma, vma->vm_start, phys >> PAGE_SHIFT,
size, vma->vm_page_prot);
}
明示的なサイズ制限と権限チェックを設けた場合に限り、DMA バッファーの mmap を優先してください。
5. poll/非同期 I/O
ブロッキング読み取りには poll と wake_up_interruptible を実装してください。SIGIO には fasync_helper を使用してください。
6. エージェントの使用方法
/writing-char-drivers Add unlocked_ioctl SET_SPEED to existing platform driver
よくある問題
| 症状 | 原因 | 対処法 |
|---|---|---|
-EFAULT | 不正なユーザーポインター | access_ok(古いカーネルの場合)を検証するか、copy_* に任せる |
ENOTTY | ioctl のマジック値が不一致 | ユーザー空間の ioctl.h に合わせる |
| メジャー番号の競合 | 静的メジャー番号が使用済み | alloc_chrdev_region を使用する |
| mmap で SIGSEGV | デバイスへのキャッシュ済みマッピング | pgprot_noncached |
| ioctl 内でスリープする | スピンロックを保持している | ブロックする前にロックを解除する |
関連スキル
skills/kernel-dev/platform-device-model— probe のコンテキストskills/kernel/device-drivers— ドライバーのライフサイクル全体skills/kernel/kernel-concurrency— file ops におけるロックskills/kernel-dev/kernel-debugging-advanced— ioctl の経路をトレースするskills/low-level-programming/linux-kernel-modules— モジュールのひな形
コメント
Markdown 対応