Linux驱动开发13个实用案例

以下为你提供20个Linux驱动开发的实用案例,涵盖字符设备驱动、块设备驱动、网络设备驱动等不同类型,包含应用场景、技巧、代码示例和操作步骤。先赞再看后评论,腰缠万贯财进门

1. 简单字符设备驱动

应用场景

用于实现基本的设备文件读写操作,例如模拟一个简单的传感器设备。

技巧

使用cdev结构体来注册字符设备,实现file_operations结构体中的读写函数。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "simple_char_dev"
#define BUFFER_SIZE 100

static char buffer[BUFFER_SIZE];
static int major;

static ssize_t simple_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
    if (*f_pos >= BUFFER_SIZE)
        return 0;
    if (*f_pos + count > BUFFER_SIZE)
        count = BUFFER_SIZE - *f_pos;
    if (copy_to_user(buf, buffer + *f_pos, count))
        return -EFAULT;
    *f_pos += count;
    return count;
}

static ssize_t simple_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
    if (*f_pos >= BUFFER_SIZE)
        return -ENOSPC;
    if (*f_pos + count > BUFFER_SIZE)
        count = BUFFER_SIZE - *f_pos;
    if (copy_from_user(buffer + *f_pos, buf, count))
        return -EFAULT;
    *f_pos += count;
    return count;
}

static struct file_operations fops = {
    .read = simple_read,
    .write = simple_write,
};

static int __init simple_char_init(void) {
    major = register_chrdev(0, DEVICE_NAME, &fops);
    if (major < 0) {
        printk(KERN_ALERT "Registering char device failed with %d\n", major);
        return major;
    }
    printk(KERN_INFO "I was assigned major number %d.\n", major);
    return 0;
}

static void __exit simple_char_exit(void) {
    unregister_chrdev(major, DEVICE_NAME);
    printk(KERN_INFO "Simple char device module unloaded.\n");
}

module_init(simple_char_init);
module_exit(simple_char_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写上述代码保存为simple_char.c
  2. 编写Makefile
makefile 复制代码
obj-m += simple_char.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  1. 编译驱动:make
  2. 加载驱动:sudo insmod simple_char.ko
  3. 创建设备文件:sudo mknod /dev/simple_char c $(major) 0
  4. 测试驱动:
bash 复制代码
echo "Hello, World!" > /dev/simple_char
cat /dev/simple_char
  1. 卸载驱动:sudo rmmod simple_char

2. 带互斥锁的字符设备驱动

应用场景

当多个进程可能同时访问设备时,需要使用互斥锁来保证数据的一致性。

技巧

使用mutex结构体来实现互斥访问。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>

#define DEVICE_NAME "mutex_char_dev"
#define BUFFER_SIZE 100

static char buffer[BUFFER_SIZE];
static int major;
static DEFINE_MUTEX(mutex);

static ssize_t mutex_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
    ssize_t ret;
    if (!mutex_trylock(&mutex))
        return -EBUSY;
    if (*f_pos >= BUFFER_SIZE)
        ret = 0;
    else {
        if (*f_pos + count > BUFFER_SIZE)
            count = BUFFER_SIZE - *f_pos;
        if (copy_to_user(buf, buffer + *f_pos, count))
            ret = -EFAULT;
        else {
            *f_pos += count;
            ret = count;
        }
    }
    mutex_unlock(&mutex);
    return ret;
}

static ssize_t mutex_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
    ssize_t ret;
    if (!mutex_trylock(&mutex))
        return -EBUSY;
    if (*f_pos >= BUFFER_SIZE)
        ret = -ENOSPC;
    else {
        if (*f_pos + count > BUFFER_SIZE)
            count = BUFFER_SIZE - *f_pos;
        if (copy_from_user(buffer + *f_pos, buf, count))
            ret = -EFAULT;
        else {
            *f_pos += count;
            ret = count;
        }
    }
    mutex_unlock(&mutex);
    return ret;
}

static struct file_operations fops = {
    .read = mutex_read,
    .write = mutex_write,
};

static int __init mutex_char_init(void) {
    major = register_chrdev(0, DEVICE_NAME, &fops);
    if (major < 0) {
        printk(KERN_ALERT "Registering char device failed with %d\n", major);
        return major;
    }
    printk(KERN_INFO "I was assigned major number %d.\n", major);
    return 0;
}

static void __exit mutex_char_exit(void) {
    unregister_chrdev(major, DEVICE_NAME);
    printk(KERN_INFO "Mutex char device module unloaded.\n");
}

module_init(mutex_char_init);
module_exit(mutex_char_exit);
MODULE_LICENSE("GPL");

操作步骤

与简单字符设备驱动类似,只是编译和加载的是mutex_char.ko

3. 定时器驱动

应用场景

周期性地执行某个任务,例如定时采集传感器数据。

技巧

使用timer_list结构体和mod_timer函数来实现定时器。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>

#define TIMEOUT 5000  // 5 seconds

static struct timer_list my_timer;

static void timer_callback(unsigned long data) {
    printk(KERN_INFO "Timer callback fired!\n");
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(TIMEOUT));
}

static int __init timer_init(void) {
    setup_timer(&my_timer, timer_callback, 0);
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(TIMEOUT));
    printk(KERN_INFO "Timer initialized.\n");
    return 0;
}

static void __exit timer_exit(void) {
    del_timer(&my_timer);
    printk(KERN_INFO "Timer removed.\n");
}

module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为timer.c,编写Makefile并编译。
  2. 加载驱动:sudo insmod timer.ko
  3. 观察内核日志:dmesg
  4. 卸载驱动:sudo rmmod timer

4. 中断驱动

应用场景

处理硬件中断,例如按键按下等事件。

技巧

使用request_irq函数来注册中断处理函数。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>

#define IRQ_NUMBER 1

static irqreturn_t irq_handler(int irq, void *dev_id) {
    printk(KERN_INFO "Interrupt received!\n");
    return IRQ_HANDLED;
}

static int __init irq_init(void) {
    int ret;
    ret = request_irq(IRQ_NUMBER, irq_handler, IRQF_SHARED, "my_irq", (void *)(irq_handler));
    if (ret) {
        printk(KERN_ALERT "Failed to register IRQ %d\n", IRQ_NUMBER);
        return ret;
    }
    printk(KERN_INFO "IRQ registered successfully.\n");
    return 0;
}

static void __exit irq_exit(void) {
    free_irq(IRQ_NUMBER, (void *)(irq_handler));
    printk(KERN_INFO "IRQ freed.\n");
}

module_init(irq_init);
module_exit(irq_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为irq.c,编写Makefile并编译。
  2. 加载驱动:sudo insmod irq.ko
  3. 触发中断(根据实际硬件情况),观察内核日志:dmesg
  4. 卸载驱动:sudo rmmod irq

5. 内核线程驱动

应用场景

在后台执行一些长时间运行的任务,例如数据处理。

技巧

使用kthread_createwake_up_process函数来创建和启动内核线程。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>

static struct task_struct *my_thread;

static int thread_function(void *data) {
    while (!kthread_should_stop()) {
        printk(KERN_INFO "Kernel thread is running...\n");
        msleep(1000);
    }
    return 0;
}

static int __init kthread_init(void) {
    my_thread = kthread_create(thread_function, NULL, "my_thread");
    if (IS_ERR(my_thread)) {
        printk(KERN_ALERT "Failed to create kernel thread.\n");
        return PTR_ERR(my_thread);
    }
    wake_up_process(my_thread);
    printk(KERN_INFO "Kernel thread created and started.\n");
    return 0;
}

static void __exit kthread_exit(void) {
    kthread_stop(my_thread);
    printk(KERN_INFO "Kernel thread stopped.\n");
}

module_init(kthread_init);
module_exit(kthread_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为kthread.c,编写Makefile并编译。
  2. 加载驱动:sudo insmod kthread.ko
  3. 观察内核日志:dmesg
  4. 卸载驱动:sudo rmmod kthread

6. 简单块设备驱动

应用场景

模拟一个简单的块设备,例如虚拟磁盘。

技巧

使用gendisk结构体和block_device_operations结构体来实现块设备驱动。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/blkdev.h>

#define KERNEL_SECTOR_SIZE 512
#define DISK_SIZE (1024 * 1024)  // 1MB

static struct gendisk *my_disk;
static struct request_queue *my_queue;
static unsigned char *disk_data;

static void my_request(struct request_queue *q) {
    struct request *rq;
    while ((rq = blk_fetch_request(q)) != NULL) {
        if (blk_rq_is_passthrough(rq)) {
            printk(KERN_ERR "Skip non-fs request\n");
            __blk_end_request_all(rq, -EIO);
            continue;
        }
        sector_t start_sector = blk_rq_pos(rq);
        unsigned int sectors = blk_rq_sectors(rq);
        void *buffer = bio_data(rq->bio);
        if (rq_data_dir(rq) == READ) {
            memcpy(buffer, disk_data + start_sector * KERNEL_SECTOR_SIZE, sectors * KERNEL_SECTOR_SIZE);
        } else {
            memcpy(disk_data + start_sector * KERNEL_SECTOR_SIZE, buffer, sectors * KERNEL_SECTOR_SIZE);
        }
        __blk_end_request_all(rq, 0);
    }
}

static struct block_device_operations my_fops = {
    .owner = THIS_MODULE,
};

static int __init block_init(void) {
    disk_data = vmalloc(DISK_SIZE);
    if (!disk_data) {
        printk(KERN_ALERT "Failed to allocate disk data.\n");
        return -ENOMEM;
    }
    my_queue = blk_init_queue(my_request, NULL);
    if (!my_queue) {
        printk(KERN_ALERT "Failed to initialize request queue.\n");
        vfree(disk_data);
        return -ENOMEM;
    }
    my_disk = alloc_disk(1);
    if (!my_disk) {
        printk(KERN_ALERT "Failed to allocate gendisk.\n");
        blk_cleanup_queue(my_queue);
        vfree(disk_data);
        return -ENOMEM;
    }
    my_disk->major = register_blkdev(0, "my_block_dev");
    my_disk->first_minor = 0;
    my_disk->fops = &my_fops;
    my_disk->queue = my_queue;
    sprintf(my_disk->disk_name, "my_block_dev");
    set_capacity(my_disk, DISK_SIZE / KERNEL_SECTOR_SIZE);
    add_disk(my_disk);
    printk(KERN_INFO "Block device initialized.\n");
    return 0;
}

static void __exit block_exit(void) {
    del_gendisk(my_disk);
    put_disk(my_disk);
    unregister_blkdev(my_disk->major, "my_block_dev");
    blk_cleanup_queue(my_queue);
    vfree(disk_data);
    printk(KERN_INFO "Block device removed.\n");
}

module_init(block_init);
module_exit(block_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为block.c,编写Makefile并编译。
  2. 加载驱动:sudo insmod block.ko
  3. 查看块设备:lsblk
  4. 格式化和挂载块设备:
bash 复制代码
sudo mkfs.ext4 /dev/my_block_dev
sudo mkdir /mnt/my_block
sudo mount /dev/my_block_dev /mnt/my_block
  1. 卸载驱动前先卸载块设备:
bash 复制代码
sudo umount /mnt/my_block
sudo rmmod block

7. 基于 PWM 的呼吸灯驱动

应用场景

用于控制 LED 实现呼吸灯效果,常见于智能家居、嵌入式设备的状态指示等场景。

技巧

利用 PWM(脉冲宽度调制)技术,通过调整占空比来改变 LED 的亮度。在 Linux 内核中,通常使用硬件的 PWM 控制器,并通过相关的内核接口进行配置和操作。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pwm.h>

#define PWM_CHIP_ID 0
#define PWM_PERIOD_NS 1000000  // 1ms period
#define PWM_MIN_DUTY_NS 0
#define PWM_MAX_DUTY_NS 1000000

static struct pwm_device *pwm_dev;

static int __init pwm_led_init(void) {
    pwm_dev = pwm_request(PWM_CHIP_ID, "pwm_led");
    if (IS_ERR(pwm_dev)) {
        printk(KERN_ALERT "Failed to request PWM device.\n");
        return PTR_ERR(pwm_dev);
    }
    pwm_config(pwm_dev, PWM_MIN_DUTY_NS, PWM_PERIOD_NS);
    pwm_enable(pwm_dev);
    printk(KERN_INFO "PWM LED initialized.\n");
    return 0;
}

static void __exit pwm_led_exit(void) {
    pwm_disable(pwm_dev);
    pwm_free(pwm_dev);
    printk(KERN_INFO "PWM LED removed.\n");
}

module_init(pwm_led_init);
module_exit(pwm_led_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为 pwm_led.c,编写 Makefile 并编译。
  2. 加载驱动:sudo insmod pwm_led.ko
  3. 可以通过 /sys/class/pwm/pwmchipX/pwmY/duty_cycle 文件来动态调整占空比,例如:
bash 复制代码
echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
  1. 卸载驱动:sudo rmmod pwm_led

8. I2C 设备驱动(读取传感器数据)

应用场景

用于与 I2C 接口的传感器进行通信,如温度传感器、加速度计等。

技巧

使用 Linux 内核的 I2C 子系统,通过 i2c_clienti2c_driver 结构体来实现与 I2C 设备的交互。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>

#define I2C_DEVICE_ADDR 0x50

static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) {
    u8 data;
    int ret;
    ret = i2c_smbus_read_byte(client);
    if (ret < 0) {
        printk(KERN_ALERT "Failed to read from I2C device.\n");
        return ret;
    }
    data = (u8)ret;
    printk(KERN_INFO "Read data from I2C device: 0x%02x\n", data);
    return 0;
}

static int my_i2c_remove(struct i2c_client *client) {
    printk(KERN_INFO "I2C device removed.\n");
    return 0;
}

static const struct i2c_device_id my_i2c_id[] = {
    { "my_i2c_device", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, my_i2c_id);

static struct i2c_driver my_i2c_driver = {
    .driver = {
        .name = "my_i2c_driver",
        .owner = THIS_MODULE,
    },
    .probe = my_i2c_probe,
    .remove = my_i2c_remove,
    .id_table = my_i2c_id,
};

static int __init i2c_driver_init(void) {
    return i2c_add_driver(&my_i2c_driver);
}

static void __exit i2c_driver_exit(void) {
    i2c_del_driver(&my_i2c_driver);
}

module_init(i2c_driver_init);
module_exit(i2c_driver_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为 i2c_driver.c,编写 Makefile 并编译。
  2. 加载驱动:sudo insmod i2c_driver.ko
  3. 确保 I2C 设备已正确连接,观察内核日志查看读取的数据:dmesg
  4. 卸载驱动:sudo rmmod i2c_driver

9. SPI 设备驱动(读写数据)

应用场景

用于与 SPI 接口的设备进行通信,如 SPI 闪存、SPI 显示屏等。

技巧

使用 Linux 内核的 SPI 子系统,通过 spi_devicespi_driver 结构体来实现与 SPI 设备的交互。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>

#define SPI_DEVICE_MODE SPI_MODE_0
#define SPI_DEVICE_BITS_PER_WORD 8
#define SPI_DEVICE_MAX_SPEED_HZ 1000000

static int my_spi_probe(struct spi_device *spi) {
    u8 tx_buf[1] = { 0xAA };
    u8 rx_buf[1];
    struct spi_transfer xfer = {
       .tx_buf = tx_buf,
       .rx_buf = rx_buf,
       .len = 1,
    };
    struct spi_message msg;
    spi_message_init(&msg);
    spi_message_add_tail(&xfer, &msg);
    if (spi_sync(spi, &msg)) {
        printk(KERN_ALERT "SPI transfer failed.\n");
        return -EIO;
    }
    printk(KERN_INFO "Received data from SPI device: 0x%02x\n", rx_buf[0]);
    return 0;
}

static int my_spi_remove(struct spi_device *spi) {
    printk(KERN_INFO "SPI device removed.\n");
    return 0;
}

static const struct spi_device_id my_spi_id[] = {
    { "my_spi_device", 0 },
    { }
};
MODULE_DEVICE_TABLE(spi, my_spi_id);

static struct spi_driver my_spi_driver = {
    .driver = {
        .name = "my_spi_driver",
        .owner = THIS_MODULE,
    },
    .probe = my_spi_probe,
    .remove = my_spi_remove,
    .id_table = my_spi_id,
};

static int __init spi_driver_init(void) {
    return spi_register_driver(&my_spi_driver);
}

static void __exit spi_driver_exit(void) {
    spi_unregister_driver(&my_spi_driver);
}

module_init(spi_driver_init);
module_exit(spi_driver_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为 spi_driver.c,编写 Makefile 并编译。
  2. 加载驱动:sudo insmod spi_driver.ko
  3. 确保 SPI 设备已正确连接,观察内核日志查看读取的数据:dmesg
  4. 卸载驱动:sudo rmmod spi_driver

10. USB 设备驱动(简单识别)

应用场景

用于识别连接到系统的 USB 设备,如 USB 鼠标、USB 键盘等。

技巧

使用 Linux 内核的 USB 子系统,通过 usb_driver 结构体来实现 USB 设备的识别和处理。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>

#define USB_VENDOR_ID 0x1234
#define USB_PRODUCT_ID 0x5678

static int my_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) {
    printk(KERN_INFO "USB device connected: Vendor ID 0x%04x, Product ID 0x%04x\n",
           id->idVendor, id->idProduct);
    return 0;
}

static void my_usb_disconnect(struct usb_interface *intf) {
    printk(KERN_INFO "USB device disconnected.\n");
}

static struct usb_device_id my_usb_id_table[] = {
    { USB_DEVICE(USB_VENDOR_ID, USB_PRODUCT_ID) },
    { }
};
MODULE_DEVICE_TABLE(usb, my_usb_id_table);

static struct usb_driver my_usb_driver = {
    .name = "my_usb_driver",
    .probe = my_usb_probe,
    .disconnect = my_usb_disconnect,
    .id_table = my_usb_id_table,
};

static int __init usb_driver_init(void) {
    return usb_register(&my_usb_driver);
}

static void __exit usb_driver_exit(void) {
    usb_deregister(&my_usb_driver);
}

module_init(usb_driver_init);
module_exit(usb_driver_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为 usb_driver.c,编写 Makefile 并编译。
  2. 加载驱动:sudo insmod usb_driver.ko
  3. 连接符合指定 Vendor ID 和 Product ID 的 USB 设备,观察内核日志:dmesg
  4. 卸载驱动:sudo rmmod usb_driver

11. 输入设备驱动(模拟键盘)

应用场景

用于模拟键盘输入,可用于自动化测试、远程控制等场景。

技巧

使用 Linux 内核的输入子系统,通过 input_dev 结构体来创建和注册输入设备。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>

static struct input_dev *my_input_dev;

static int __init input_driver_init(void) {
    int err;
    my_input_dev = input_allocate_device();
    if (!my_input_dev) {
        printk(KERN_ALERT "Failed to allocate input device.\n");
        return -ENOMEM;
    }
    my_input_dev->name = "my_keyboard";
    set_bit(EV_KEY, my_input_dev->evbit);
    set_bit(KEY_A, my_input_dev->keybit);
    err = input_register_device(my_input_dev);
    if (err) {
        printk(KERN_ALERT "Failed to register input device.\n");
        input_free_device(my_input_dev);
        return err;
    }
    input_report_key(my_input_dev, KEY_A, 1);
    input_sync(my_input_dev);
    input_report_key(my_input_dev, KEY_A, 0);
    input_sync(my_input_dev);
    printk(KERN_INFO "Input device initialized and sent 'A' key event.\n");
    return 0;
}

static void __exit input_driver_exit(void) {
    input_unregister_device(my_input_dev);
    printk(KERN_INFO "Input device removed.\n");
}

module_init(input_driver_init);
module_exit(input_driver_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为 input_driver.c,编写 Makefile 并编译。
  2. 加载驱动:sudo insmod input_driver.ko
  3. 观察系统中是否有模拟的键盘输入(如在文本编辑器中可能会出现字符 'A')
  4. 卸载驱动:sudo rmmod input_driver

12. 帧缓冲设备驱动(简单显示)

应用场景

用于在屏幕上进行简单的图形显示,如嵌入式设备的小屏幕显示。

技巧

使用 Linux 内核的帧缓冲子系统,通过 fb_info 结构体来实现帧缓冲设备驱动。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fb.h>

static struct fb_info *my_fb_info;

static int my_fb_set_par(struct fb_info *info) {
    return 0;
}

static int my_fb_blank(int blank, struct fb_info *info) {
    return 0;
}

static struct fb_ops my_fb_ops = {
    .owner = THIS_MODULE,
    .fb_set_par = my_fb_set_par,
    .fb_blank = my_fb_blank,
};

static int __init fb_driver_init(void) {
    my_fb_info = framebuffer_alloc(0, NULL);
    if (!my_fb_info) {
        printk(KERN_ALERT "Failed to allocate framebuffer info.\n");
        return -ENOMEM;
    }
    my_fb_info->fbops = &my_fb_ops;
    my_fb_info->var.xres = 640;
    my_fb_info->var.yres = 480;
    my_fb_info->var.bits_per_pixel = 16;
    my_fb_info->fix.smem_len = my_fb_info->var.xres * my_fb_info->var.yres *
                               (my_fb_info->var.bits_per_pixel / 8);
    if (register_framebuffer(my_fb_info)) {
        printk(KERN_ALERT "Failed to register framebuffer.\n");
        framebuffer_release(my_fb_info);
        return -EFAULT;
    }
    printk(KERN_INFO "Framebuffer device initialized.\n");
    return 0;
}

static void __exit fb_driver_exit(void) {
    unregister_framebuffer(my_fb_info);
    framebuffer_release(my_fb_info);
    printk(KERN_INFO "Framebuffer device removed.\n");
}

module_init(fb_driver_init);
module_exit(fb_driver_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为 fb_driver.c,编写 Makefile 并编译。
  2. 加载驱动:sudo insmod fb_driver.ko
  3. 可以使用 fbset 命令查看帧缓冲设备信息:
bash 复制代码
sudo fbset -i /dev/fbX  # X 为帧缓冲设备编号
  1. 卸载驱动:sudo rmmod fb_driver

13. 看门狗设备驱动(简单实现)

应用场景

用于系统的硬件监控和故障恢复,当系统出现异常时,看门狗可以复位系统。

技巧

使用 Linux 内核的看门狗子系统,通过 watchdog_device 结构体来实现看门狗设备驱动。

代码示例

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/watchdog.h>

static int my_wdt_start(struct watchdog_device *wdd) {
    printk(KERN_INFO "Watchdog started.\n");
    return 0;
}

static int my_wdt_stop(struct watchdog_device *wdd) {
    printk(KERN_INFO "Watchdog stopped.\n");
    return 0;
}

static int my_wdt_ping(struct watchdog_device *wdd) {
    printk(KERN_INFO "Watchdog pinged.\n");
    return 0;
}

static struct watchdog_info my_wdt_info = {
    .options = WDIOF_KEEPALIVEPING,
    .firmware_version = 1,
    .identity = "My Watchdog",
};

static struct watchdog_ops my_wdt_ops = {
    .owner = THIS_MODULE,
    .start = my_wdt_start,
    .stop = my_wdt_stop,
    .ping = my_wdt_ping,
};

static struct watchdog_device my_wdt_dev = {
    .info = &my_wdt_info,
    .ops = &my_wdt_ops,
};

static int __init wdt_driver_init(void) {
    return watchdog_register_device(&my_wdt_dev);
}

static void __exit wdt_driver_exit(void) {
    watchdog_unregister_device(&my_wdt_dev);
}

module_init(wdt_driver_init);
module_exit(wdt_driver_exit);
MODULE_LICENSE("GPL");

操作步骤

  1. 编写代码保存为 wdt_driver.c,编写 Makefile 并编译。
  2. 加载驱动:sudo insmod wdt_driver.ko
  3. 可以使用 watchdog 命令来操作看门狗设备:
bash 复制代码
sudo echo 1 > /dev/watchdog  # 启动看门狗
sudo echo V > /dev/watchdog  # 停止看门狗
  1. 卸载驱动:sudo rmmod wdt_driver

最后

以上是13个 Linux 驱动开发中常用的小案例,收藏起来,用得着的时候不会抓瞎,就像 V 哥经常会收集一些小案例在自己的知识库里,需要用的时候很方便可以检索到,你说用 AI?对 AI 很强大,但我自己调试好的小案例用起来也很方便,就像自己创建的一个智能体一样,你觉得呢。关注威哥爱编程,全栈开发就你行。

相关推荐
A小辣椒19 小时前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式