writing-char-drivers
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWriting Character Drivers
编写字符驱动
Purpose
目的
Guide agents through Linux character device implementation: , registration, safe userspace copies, design, and basic — focused depth beyond .
struct file_operationscdevioctlmmapskills/kernel/device-drivers指导Agent完成Linux字符设备的实现:、注册、安全的用户空间拷贝、设计以及基础——相比更具深度。
struct file_operationscdevioctlmmapskills/kernel/device-driversWhen to Use
适用场景
- Exposing hardware to
/dev/mydev - Implementing /
read/writefrom kernelpoll - Defining commands with type-safe macros
ioctl - Mapping device MMIO to userspace (carefully)
- 将硬件暴露到
/dev/mydev - 在内核中实现/
read/writepoll - 使用类型安全宏定义命令
ioctl - (谨慎地)将设备MMIO映射到用户空间
Workflow
工作流程
1. Char device registration
1. 字符设备注册
c
#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;
}Modern drivers often use variants inside .
devm_*probec
#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;
}现代驱动通常在中使用变体。
probedevm_*2. Safe userspace I/O
2. 安全的用户空间I/O
c
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;
}Never dereference pointers directly.
__userc
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;
}切勿直接解引用指针。
__user3. ioctl pattern
3. ioctl模式
c
#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;
}
}Use with fixed-size structs; prefer on bi-arch.
_IOWRcompat_ioctlc
#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;
}
}对固定大小的结构体使用;在双架构系统中优先使用。
_IOWRcompat_ioctl4. mmap (device memory)
4. mmap(设备内存)
c
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);
}Prefer of DMA buffers only with explicit size limits and permission checks.
mmapc
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缓冲区使用。
mmap5. poll / async I/O
5. poll / 异步I/O
Implement + for blocking reads; use for SIGIO.
pollwake_up_interruptiblefasync_helper为阻塞式读取实现 + ;为SIGIO使用。
pollwake_up_interruptiblefasync_helper6. Agent usage
6. Agent使用方式
/writing-char-drivers Add unlocked_ioctl SET_SPEED to existing platform driver/writing-char-drivers Add unlocked_ioctl SET_SPEED to existing platform driverCommon Problems
常见问题
| Symptom | Cause | Fix |
|---|---|---|
| Bad user pointer | Validate |
| Wrong ioctl magic | Match userspace |
| Major conflict | Static major taken | Use |
| mmap SIGSEGV | Cached mapping to device | |
| Sleep in ioctl | Holding spinlock | Drop lock before blocking |
| 症状 | 原因 | 修复方案 |
|---|---|---|
| 用户指针无效 | 验证 |
| ioctl魔数不匹配 | 与用户空间的 |
| 主设备号冲突 | 静态主设备号已被占用 | 使用 |
| mmap导致SIGSEGV | 设备映射使用了缓存 | 使用 |
| ioctl中休眠 | 持有自旋锁 | 阻塞前释放锁 |
Related Skills
相关技能
- — probe context
skills/kernel-dev/platform-device-model - — full driver lifecycle
skills/kernel/device-drivers - — locking in file ops
skills/kernel/kernel-concurrency - — trace ioctl path
skills/kernel-dev/kernel-debugging-advanced - — module boilerplate
skills/low-level-programming/linux-kernel-modules
- — 探测上下文
skills/kernel-dev/platform-device-model - — 完整驱动生命周期
skills/kernel/device-drivers - — 文件操作中的锁机制
skills/kernel/kernel-concurrency - — 追踪ioctl路径
skills/kernel-dev/kernel-debugging-advanced - — 模块模板
skills/low-level-programming/linux-kernel-modules