2. 注册Camera设备到内核

任务说明

本案例实现一个最小可用的Camera设备驱动注册模块,目标是将Camera设备注册到Linux内核设备模型,并通过platform驱动框架自动创建设备节点。最终效果:加载模块后,/dev/camera0节点出现,卸载后节点消失。

核心实现

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

#define CAMERA_DEVICE_NAME "camera"
#define CAMERA_CLASS_NAME  "camera_class"

static dev_t camera_dev_num;
static struct cdev camera_cdev;
static struct class *camera_class;
static struct device *camera_device;

/* 设备节点操作集(最小实现) */
static int camera_open(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t camera_read(struct file *filp, char __user *buf,
                           size_t count, loff_t *pos)
{
    return 0;
}

static const struct file_operations camera_fops = {
    .owner   = THIS_MODULE,
    .open    = camera_open,
    .read    = camera_read,
};

/* platform驱动匹配成功后调用 */
static int camera_probe(struct platform_device *pdev)
{
    int ret;

    /* 1. 动态分配主设备号 */
    ret = alloc_chrdev_region(&camera_dev_num, 0, 1, CAMERA_DEVICE_NAME);
    if (ret < 0)
        return ret;

    /* 2. 初始化并添加cdev */
    cdev_init(&camera_cdev, &camera_fops);
    camera_cdev.owner = THIS_MODULE;
    ret = cdev_add(&camera_cdev, camera_dev_num, 1);
    if (ret < 0)
        goto err_cdev_add;

    /* 3. 创建设备类 */
    camera_class = class_create(CAMERA_CLASS_NAME);
    if (IS_ERR(camera_class)) {
        ret = PTR_ERR(camera_class);
        goto err_class_create;
    }

    /* 4. 创建设备节点 /dev/camera0 */
    camera_device = device_create(camera_class, &pdev->dev,
                                  camera_dev_num, NULL,
                                  CAMERA_DEVICE_NAME "%d", 0);
    if (IS_ERR(camera_device)) {
        ret = PTR_ERR(camera_device);
        goto err_device_create;
    }

    dev_info(&pdev->dev, "camera device registered, node: /dev/camera0\n");
    return 0;

err_device_create:
    class_destroy(camera_class);
err_class_create:
    cdev_del(&camera_cdev);
err_cdev_add:
    unregister_chrdev_region(camera_dev_num, 1);
    return ret;
}

static int camera_remove(struct platform_device *pdev)
{
    device_destroy(camera_class, camera_dev_num);
    class_destroy(camera_class);
    cdev_del(&camera_cdev);
    unregister_chrdev_region(camera_dev_num, 1);
    dev_info(&pdev->dev, "camera device removed\n");
    return 0;
}

static struct platform_driver camera_platform_driver = {
    .probe  = camera_probe,
    .remove = camera_remove,
    .driver = {
        .name = CAMERA_DEVICE_NAME,
    },
};

module_platform_driver(camera_platform_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kernel Camera Course");
MODULE_DESCRIPTION("Minimal camera device registration example");

编译与加载

创建Makefile:

obj-m := camera_device.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

执行编译与加载:

make
sudo insmod camera_device.ko
ls -l /dev/camera0

验证方法

  • 设备节点存在:ls -l /dev/camera0应显示字符设备,主设备号为动态分配值(如240+),次设备号为0。
  • 设备模型注册:cat /sys/devices/virtual/camera_class/camera0/dev应输出主设备号:0。
  • 卸载清理:sudo rmmod camera_device后,/dev/camera0消失,dmesg尾部出现camera device removed日志。

若insmod后/dev/camera0未出现,执行dmesg | tail查看错误信息,常见原因为设备号分配失败或class_create返回错误。