驱动开发day8(8.1)

编写LED灯的驱动,使用GPIO子系统,里面添加按键的中断处理

1.应用程序发送指令控制LED亮灭

2.按键1 按下,led1电位反转 按键2按下,led2电位反转 按键3 按下,led3电位反转

驱动程序

复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>

//功能码
#define LED_ON _IO('l',1)
#define LED_OFF _IO('l',0)

// 定义一个变量保存主设备号
unsigned int major;
char kbuf[128] = {0};
unsigned int led1;
unsigned int led2;
unsigned int led3;

struct class *cls;
struct device *dev;
struct device_node *devn;
unsigned int irqno1;
unsigned int irqno2;
unsigned int irqno3;

// 中断处理函数
irqreturn_t myirq_handle1(int irq, void *dev)
{
    printk("key1 press\n");
    gpio_set_value(led1, !gpio_get_value(led1));
    return IRQ_HANDLED;
}
irqreturn_t myirq_handle2(int irq, void *dev)
{
    printk("key2 press\n");
    gpio_set_value(led2, !gpio_get_value(led2));
    return IRQ_HANDLED;
}
irqreturn_t myirq_handle3(int irq, void *dev)
{
    printk("key3 press\n");
    gpio_set_value(led3, !gpio_get_value(led3));
    return IRQ_HANDLED;
}

int mycdev_open(struct inode *inode, struct file *file)
{
    int a = inode->i_rdev;
    file->private_data = (void *)MINOR(a);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *loff)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    unsigned long ret;
    if (size > sizeof(kbuf))
    {
        size = sizeof(kbuf);
    }
    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy_to_user failed\n");
        return ret;
    }
    return 0;
}

ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *loff)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    if (size > sizeof(kbuf))
    {
        size = sizeof(kbuf);
    }
    long ret;
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret)
    {
        printk("copy_from_user failed\n");
        return -EIO;
    }

    return 0;
}

int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    unsigned int a = (unsigned int)file->private_data;
    switch (a)
    {
    case 0:
        switch (cmd)
        {
        case LED_ON:
            gpio_set_value(led1, 1);
            break;
        case LED_OFF:
            gpio_set_value(led1, 0);
            break;
        }
        break;
    case 1:
        switch (cmd)
        {
        case LED_ON:
            gpio_set_value(led2, 1);
            break;
        case LED_OFF:
            gpio_set_value(led2, 0);
            break;
        }
        break;
    case 2:
        switch (cmd)
        {
        case LED_ON:
            gpio_set_value(led3, 1);
            break;
        case LED_OFF:
            gpio_set_value(led3, 0);
            break;
        }
        break;
    }
    return 0;
}
struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
    .unlocked_ioctl = mycdev_ioctl,
};
int irq_init(void)
{
    int ret;
    // 解析按键的设备树节点
    devn = of_find_node_by_path("/myirq");
    if (NULL == devn)
    {
        printk("解析设备树节点失败\n");
        return -EFAULT;
    }
    printk("解析设备树节点成功\n");
    // 根据设备树节点解析出软中断号
    irqno1 = irq_of_parse_and_map(devn, 0);
    if (!irqno1)
    {
        printk("解析软中断号失败\n");
        return -ENXIO;
    }
    printk("解析软中断号成功 irqno1=%d\n", irqno1);

    irqno2 = irq_of_parse_and_map(devn, 1);
    if (!irqno2)
    {
        printk("解析软中断号失败\n");
        return -ENXIO;
    }
    printk("解析软中断号成功 irqno2=%d\n", irqno2);

    irqno3 = irq_of_parse_and_map(devn, 2);
    if (!irqno3)
    {
        printk("解析软中断号失败\n");
        return -ENXIO;
    }
    printk("解析软中断号成功 irqno3=%d\n", irqno3);

    // 注册中断
    ret = request_irq(irqno1, myirq_handle1, IRQF_TRIGGER_FALLING, "key1", NULL);
    if (ret)
    {
        printk("注册中断失败\n");
        return ret;
    }
    printk("注册中断成功\n");
    ret = request_irq(irqno2, myirq_handle2, IRQF_TRIGGER_FALLING, "key2", NULL);
    if (ret)
    {
        printk("注册中断失败\n");
        return ret;
    }
    printk("注册中断成功\n");
    ret = request_irq(irqno3, myirq_handle3, IRQF_TRIGGER_FALLING, "key3", NULL);
    if (ret)
    {
        printk("注册中断失败\n");
        return ret;
    }
    printk("注册中断成功\n");
    return 0;
}

static int __init mycdev_init(void)
{
    int i, ret;
    irq_init();
    devn = of_find_node_by_path("/leds");
    if (NULL == devn)
    {
        printk("解析设备树节点失败\n");
        return -EFAULT;
    }
    printk("解析设备树节点成功\n");
    // 根据解析到的设备树节点指针解析出LED的gpio编号
    led1 = of_get_named_gpio(devn, "led1-gpios", 0);
    if (led1 < 0)
    {
        printk("解析led1编号失败\n");
        return led1;
    }
    printk("解析led1编号成功\n");
    // 申请led编号
    ret = gpio_request(led1, NULL);
    if (ret)
    {
        printk("申请led编号失败\n");
        return ret;
    }
    printk("申请led编号成功\n");
    led2 = of_get_named_gpio(devn, "led2-gpios", 0);
    if (led2 < 0)
    {
        printk("解析led2编号失败\n");
        return led2;
    }
    printk("解析led1编号成功\n");
    // 申请led编号
    ret = gpio_request(led2, NULL);
    if (ret)
    {
        printk("申请led编号失败\n");
        return ret;
    }
    printk("申请led编号成功\n");
    led3 = of_get_named_gpio(devn, "led3-gpios", 0);
    if (led3 < 0)
    {
        printk("解析led3编号失败\n");
        return led3;
    }
    printk("解析led3编号成功\n");
    // 申请led编号
    ret = gpio_request(led3, NULL);
    if (ret)
    {
        printk("申请led编号失败\n");
        return ret;
    }
    printk("申请led编号成功\n");
    // 注册字符设备驱动
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("register error\n");
        return major;
    }
    printk("register success\n");

    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录信息成功]\n");

    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交结点设备信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点成功\n");

    return 0;
}

static void __exit mycdev_exit(void)
{
    // 注销中断
    free_irq(irqno1, NULL);
    free_irq(irqno2, NULL);
    free_irq(irqno3, NULL);

    // 释放led编号
    gpio_put(led1);
    gpio_put(led2);
    gpio_put(led3);
    // 销毁节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }

    // 销毁目录信息
    class_destroy(cls);
    // 注销字符设备驱动
    unregister_chrdev(major, "mycdev");
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用程序

复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
//功能码
#define LED_ON _IO('l',1)
#define LED_OFF _IO('l',0)

int main(int argc, char const *argv[])
{
    int a, b, c;
    char buf[128] = {0};
    printf("invoking open\n");
    int fd1 = open("/dev/myled0", O_RDWR);
    if (fd1 < 0)
    {
        printf("open file failed");
        exit(-1);
    }
    int fd2 = open("/dev/myled1", O_RDWR);
    if (fd2 < 0)
    {
        printf("open file failed");
        exit(-1);
    }
    int fd3 = open("/dev/myled2", O_RDWR);
    if (fd3 < 0)
    {
        printf("open file failed");
        exit(-1);
    }

    while (1)
    {

        printf("请选择LED灯1(LED1)2(LED2) 3(LED3)>");
        scanf("%d", &b);
        printf("请输入控制指令:0(关灯) 1(开灯)>");
        scanf("%d", &a);
        switch (b)
        {
        case 1:
            switch (a)
            {
            case 1:
                ioctl(fd1, LED_ON);
                break;
            case 0:
                ioctl(fd1, LED_OFF);
                break;
            }
            break;
        case 2:
            switch (a)
            {
            case 1:
                ioctl(fd2, LED_ON);
                break;
            case 0:
                ioctl(fd2, LED_OFF);
                break;
            }
            break;
        case 3:
            switch (a)
            {
            case 1:
                ioctl(fd3, LED_ON);
                break;
            case 0:
                ioctl(fd3, LED_OFF);
                break;
            }
            break;
        }
    }
    printf("invoking close\n");
    close(fd1);
    close(fd2);
    close(fd3);
    return 0;
}
相关推荐
cxr8283 天前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot3 天前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶4 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot5 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot5 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday5 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot6 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot6 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr8287 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday7 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发