以下为你提供20个Linux驱动开发的实用案例,涵盖字符设备驱动、块设备驱动、网络设备驱动等不同类型,包含应用场景、技巧、代码示例和操作步骤。先赞再看后评论,腰缠万贯财进门
。
1. 简单字符设备驱动
应用场景
用于实现基本的设备文件读写操作,例如模拟一个简单的传感器设备。
技巧
使用cdev
结构体来注册字符设备,实现file_operations
结构体中的读写函数。
代码示例
#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");
操作步骤
- 编写上述代码保存为
simple_char.c
。 - 编写
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
- 编译驱动:
make
- 加载驱动:
sudo insmod simple_char.ko
- 创建设备文件:
sudo mknod /dev/simple_char c $(major) 0
- 测试驱动:
echo "Hello, World!" > /dev/simple_char
cat /dev/simple_char
- 卸载驱动:
sudo rmmod simple_char
2. 带互斥锁的字符设备驱动
应用场景
当多个进程可能同时访问设备时,需要使用互斥锁来保证数据的一致性。
技巧
使用mutex
结构体来实现互斥访问。
代码示例
#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
函数来实现定时器。
代码示例
#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");
操作步骤
- 编写代码保存为
timer.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod timer.ko
- 观察内核日志:
dmesg
- 卸载驱动:
sudo rmmod timer
4. 中断驱动
应用场景
处理硬件中断,例如按键按下等事件。
技巧
使用request_irq
函数来注册中断处理函数。
代码示例
#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");
操作步骤
- 编写代码保存为
irq.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod irq.ko
- 触发中断(根据实际硬件情况),观察内核日志:
dmesg
- 卸载驱动:
sudo rmmod irq
5. 内核线程驱动
应用场景
在后台执行一些长时间运行的任务,例如数据处理。
技巧
使用kthread_create
和wake_up_process
函数来创建和启动内核线程。
代码示例
#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");
操作步骤
- 编写代码保存为
kthread.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod kthread.ko
- 观察内核日志:
dmesg
- 卸载驱动:
sudo rmmod kthread
6. 简单块设备驱动
应用场景
模拟一个简单的块设备,例如虚拟磁盘。
技巧
使用gendisk
结构体和block_device_operations
结构体来实现块设备驱动。
代码示例
#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");
操作步骤
- 编写代码保存为
block.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod block.ko
- 查看块设备:
lsblk
- 格式化和挂载块设备:
sudo mkfs.ext4 /dev/my_block_dev
sudo mkdir /mnt/my_block
sudo mount /dev/my_block_dev /mnt/my_block
- 卸载驱动前先卸载块设备:
sudo umount /mnt/my_block
sudo rmmod block
7. 基于 PWM 的呼吸灯驱动
应用场景
用于控制 LED 实现呼吸灯效果,常见于智能家居、嵌入式设备的状态指示等场景。
技巧
利用 PWM(脉冲宽度调制)技术,通过调整占空比来改变 LED 的亮度。在 Linux 内核中,通常使用硬件的 PWM 控制器,并通过相关的内核接口进行配置和操作。
代码示例
#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");
操作步骤
- 编写代码保存为
pwm_led.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod pwm_led.ko
- 可以通过
/sys/class/pwm/pwmchipX/pwmY/duty_cycle
文件来动态调整占空比,例如:
echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
- 卸载驱动:
sudo rmmod pwm_led
8. I2C 设备驱动(读取传感器数据)
应用场景
用于与 I2C 接口的传感器进行通信,如温度传感器、加速度计等。
技巧
使用 Linux 内核的 I2C 子系统,通过 i2c_client
和 i2c_driver
结构体来实现与 I2C 设备的交互。
代码示例
#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");
操作步骤
- 编写代码保存为
i2c_driver.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod i2c_driver.ko
- 确保 I2C 设备已正确连接,观察内核日志查看读取的数据:
dmesg
- 卸载驱动:
sudo rmmod i2c_driver
9. SPI 设备驱动(读写数据)
应用场景
用于与 SPI 接口的设备进行通信,如 SPI 闪存、SPI 显示屏等。
技巧
使用 Linux 内核的 SPI 子系统,通过 spi_device
和 spi_driver
结构体来实现与 SPI 设备的交互。
代码示例
#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");
操作步骤
- 编写代码保存为
spi_driver.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod spi_driver.ko
- 确保 SPI 设备已正确连接,观察内核日志查看读取的数据:
dmesg
- 卸载驱动:
sudo rmmod spi_driver
10. USB 设备驱动(简单识别)
应用场景
用于识别连接到系统的 USB 设备,如 USB 鼠标、USB 键盘等。
技巧
使用 Linux 内核的 USB 子系统,通过 usb_driver
结构体来实现 USB 设备的识别和处理。
代码示例
#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");
操作步骤
- 编写代码保存为
usb_driver.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod usb_driver.ko
- 连接符合指定 Vendor ID 和 Product ID 的 USB 设备,观察内核日志:
dmesg
- 卸载驱动:
sudo rmmod usb_driver
11. 输入设备驱动(模拟键盘)
应用场景
用于模拟键盘输入,可用于自动化测试、远程控制等场景。
技巧
使用 Linux 内核的输入子系统,通过 input_dev
结构体来创建和注册输入设备。
代码示例
#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");
操作步骤
- 编写代码保存为
input_driver.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod input_driver.ko
- 观察系统中是否有模拟的键盘输入(如在文本编辑器中可能会出现字符 ‘A’)
- 卸载驱动:
sudo rmmod input_driver
12. 帧缓冲设备驱动(简单显示)
应用场景
用于在屏幕上进行简单的图形显示,如嵌入式设备的小屏幕显示。
技巧
使用 Linux 内核的帧缓冲子系统,通过 fb_info
结构体来实现帧缓冲设备驱动。
代码示例
#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");
操作步骤
- 编写代码保存为
fb_driver.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod fb_driver.ko
- 可以使用
fbset
命令查看帧缓冲设备信息:
sudo fbset -i /dev/fbX # X 为帧缓冲设备编号
- 卸载驱动:
sudo rmmod fb_driver
13. 看门狗设备驱动(简单实现)
应用场景
用于系统的硬件监控和故障恢复,当系统出现异常时,看门狗可以复位系统。
技巧
使用 Linux 内核的看门狗子系统,通过 watchdog_device
结构体来实现看门狗设备驱动。
代码示例
#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");
操作步骤
- 编写代码保存为
wdt_driver.c
,编写Makefile
并编译。 - 加载驱动:
sudo insmod wdt_driver.ko
- 可以使用
watchdog
命令来操作看门狗设备:
sudo echo 1 > /dev/watchdog # 启动看门狗
sudo echo V > /dev/watchdog # 停止看门狗
- 卸载驱动:
sudo rmmod wdt_driver
最后
以上是13个 Linux 驱动开发中常用的小案例,收藏起来,用得着的时候不会抓瞎,就像 V 哥经常会收集一些小案例在自己的知识库里,需要用的时候很方便可以检索到,你说用 AI?对 AI 很强大,但我自己调试好的小案例用起来也很方便,就像自己创建的一个智能体一样,你觉得呢。关注威哥爱编程,全栈开发就你行。