首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux驱动开发13个实用案例

Linux驱动开发13个实用案例

原创
作者头像
威哥爱编程
发布2025-02-21 18:07:35
发布2025-02-21 18:07:35
43400
代码可运行
举报
文章被收录于专栏:V哥原创技术栈V哥原创技术栈
运行总次数:0
代码可运行

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

1. 简单字符设备驱动

应用场景

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

技巧

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

代码示例

代码语言:c
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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
代码运行次数:0
运行
复制
#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 很强大,但我自己调试好的小案例用起来也很方便,就像自己创建的一个智能体一样,你觉得呢。关注威哥爱编程,全栈开发就你行。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 简单字符设备驱动
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 2. 带互斥锁的字符设备驱动
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 3. 定时器驱动
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 4. 中断驱动
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 5. 内核线程驱动
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 6. 简单块设备驱动
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 7. 基于 PWM 的呼吸灯驱动
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 8. I2C 设备驱动(读取传感器数据)
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 9. SPI 设备驱动(读写数据)
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 10. USB 设备驱动(简单识别)
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 11. 输入设备驱动(模拟键盘)
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 12. 帧缓冲设备驱动(简单显示)
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 13. 看门狗设备驱动(简单实现)
    • 应用场景
    • 技巧
    • 代码示例
    • 操作步骤
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档