2023-3-29作业

自己编写驱动,应用层程序,在应用层通过ioctl控制LED灯流水,当按键KEY1按下,让风扇转动(paltform框架的驱动形式)


新增设备树结点如下:

mycdev.h:

#ifndef __MYCDEV_H__
#define __MYCDEV_H__

//ioctl命令码
#define LED_ON _IOW('a',1,int)
#define LED_OFF _IOW('a',0,int)

#endif

pdrv.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include "mycdev.h"

struct cdev *cdev;
dev_t devno;        //设备号
unsigned int minor = 0;
unsigned int major = 0;
struct class *cls;
struct device *dev;

struct gpio_desc *gpion1;   //用于记录gpio编号
struct gpio_desc *gpion2;
struct gpio_desc *gpion3;
struct gpio_desc *gpio_fan;
struct gpio_desc *gpio_buzzer;
struct gpio_desc *gpio_motor;

int irqno[3];         // 用于记录获取到的软中断号

// 中断处理函数
irqreturn_t irq_handler(int irqno, void *arg)
{
    switch((unsigned int)arg)
    {
        case 1:
            gpiod_set_value(gpio_fan, !gpiod_get_value(gpio_fan));  //风扇状态反转
            break;
        case 2:
            gpiod_set_value(gpio_buzzer, !gpiod_get_value(gpio_buzzer));   //蜂鸣器状态反转
            break;
        case 3:
            gpiod_set_value(gpio_motor, !gpiod_get_value(gpio_motor));  //马达状态反转
            break;
    }
    return IRQ_HANDLED;
}

int myled_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
long myled_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
    // 判断应用层传递过来的命令码 cmd
    // 判断操作哪盏灯进行点亮

    switch (MINOR(file->f_inode->i_rdev)) // 判断次设备号
    {
    case 0: // 规定次设备号为0的设备文件只能控制LED1
        if (cmd == LED_ON)
            gpiod_set_value(gpion1, 1);
        else if (cmd == LED_OFF)
            gpiod_set_value(gpion1, 0);
        break;
    case 1: // 规定次设备号为1的设备文件只能控制LED2
        if (cmd == LED_ON)
            gpiod_set_value(gpion2, 1);
        else if (cmd == LED_OFF)
            gpiod_set_value(gpion2, 0);
        break;
    case 2: // 规定次设备号为2的设备文件只能控制LED3
        if (cmd == LED_ON)
            gpiod_set_value(gpion3, 1);
        else if (cmd == LED_OFF)
            gpiod_set_value(gpion3, 0);
        break;
    }
    return 0;
}
int myled_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

const struct file_operations fops = {
    .open = myled_open,
    .unlocked_ioctl = myled_ioctl,
    .release = myled_close,
};

//probe函数
int pdrv_probe(struct platform_device *pdev)
{
    int ret, i;

    // 分配字符设备驱动对象
    cdev = cdev_alloc();
    if (NULL == cdev)
    {
        printk("分配对象空间失败\n");
        ret = -ENOMEM;
        goto ERR1;
    }
    printk("1.分配对象空间成功\n");

    // 初始化驱动对象结构体
    cdev_init(cdev, &fops);

    // 动态申请设备号
    ret = alloc_chrdev_region(&devno, minor, 3, "mycdev");
    if (ret)
    {
        printk("动态申请设备号失败\n");
        goto ERR2;
    }
    major = MAJOR(devno); // 获取主设备号
    minor = MINOR(devno); // 获取次设备号
    printk("2.动态申请设备号成功\n");

    // 注册对象
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("驱动对象注册进内核失败\n");
        goto ERR3;
    }
    printk("3.驱动对象注册进内核成功\n");

    // 向上提交目录
    cls = class_create(THIS_MODULE, "mycdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        goto ERR4;
    }
    printk("4.向上提交目录成功\n");

    // 向上提交设备节点
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交节点信息失败\n");
            goto ERR5;
        }
    }
    printk("5.向上提交设备节点成功\n");
    
    //获取gpio编号
    gpion1 = gpiod_get_from_of_node(pdev->dev.of_node,"led1",0,GPIOD_OUT_LOW,NULL);
    if(IS_ERR(gpion1))
    {
        printk("获取gpio编号失败\n");
        return PTR_ERR(gpion1);
    }
    gpion2 = gpiod_get_from_of_node(pdev->dev.of_node,"led2",0,GPIOD_OUT_LOW,NULL);
    if(IS_ERR(gpion2))
    {
        printk("获取gpio编号失败\n");
        return PTR_ERR(gpion2);
    }
    gpion3 = gpiod_get_from_of_node(pdev->dev.of_node,"led3",0,GPIOD_OUT_LOW,NULL);
    if(IS_ERR(gpion3))
    {
        printk("获取gpio编号失败\n");
        return PTR_ERR(gpion3);
    }
    gpio_fan = gpiod_get_from_of_node(pdev->dev.of_node,"fan",0,GPIOD_OUT_LOW,NULL);
    if(IS_ERR(gpio_fan))
    {
        printk("获取gpio编号失败\n");
        return PTR_ERR(gpio_fan);
    }
    gpio_buzzer = gpiod_get_from_of_node(pdev->dev.of_node,"buzzer",0,GPIOD_OUT_LOW,NULL);
    if(IS_ERR(gpio_buzzer))
    {
        printk("获取gpio编号失败\n");
        return PTR_ERR(gpio_buzzer);
    }
    gpio_motor = gpiod_get_from_of_node(pdev->dev.of_node,"motor",0,GPIOD_OUT_LOW,NULL);
    if(IS_ERR(gpio_motor))
    {
        printk("获取gpio编号失败\n");
        return PTR_ERR(gpio_motor);
    }
    printk("解析gpio编号成功\n");
    
    // 根据设备树结点获取软中断号
    for (i = 1; i <= 3; i++)
    {
        irqno[i-1] = irq_of_parse_and_map(pdev->dev.of_node, i-1);
        if (!irqno[i-1])
        {
            printk("获取软中断号%d失败\n",i);
            return -ENXIO;
        }
        printk("根据设备树结点获取软中断号成功\n");

        // 注册要使用的中断
        ret = request_irq(irqno[i-1], irq_handler, IRQF_TRIGGER_FALLING, "myirq", (void*)i);
        if (ret)
        {
            printk("注册中断%d失败\n",i);
            return ret;
        }
        printk("注册中断成功\n");
    }

    return 0;

ERR5:
    // 将前面提交成功的设备信息销毁
    for (--i; i >= 0; i--)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁目录
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(MKDEV(major, minor), 3);
ERR2:
    kfree(cdev);
ERR1:
    return ret;
}
//remove函数
int pdrv_remove(struct platform_device *pdev)
{
    int i;
    // 关闭风扇、蜂鸣器、马达
    gpiod_set_value(gpio_fan, 0);
    gpiod_set_value(gpio_buzzer, 0);
    gpiod_set_value(gpio_motor, 0);
    // 注销中断
    for(i = 1; i <= 3; i++)
    {
        free_irq(irqno[i-1], (void*)i);
    }
    // 将LED灯全部熄灭
    gpiod_set_value(gpion1, 0);
    gpiod_set_value(gpion2, 0);
    gpiod_set_value(gpion3, 0);
    // 注销gpio编号
    gpiod_put(gpion1);
    gpiod_put(gpion2);
    gpiod_put(gpion3);
    gpiod_put(gpio_fan);
    gpiod_put(gpio_buzzer);
    gpiod_put(gpio_motor);
    printk("注销gpio编号成功\n");

    // 1.注销向上提交的字符设备信息
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    printk("1.注销向上提交的字符设备信息成功\n");

    // 2.注销向上提交的目录信息
    class_destroy(cls);
    printk("2.注销向上提交的目录信息成功\n");

    // 3.注销字符设备驱动对象
    cdev_del(cdev);
    printk("3.注销字符设备驱动对象成功\n");

    // 4.释放申请到的设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
    printk("4.释放申请到的设备号成功\n");

    // 5.释放申请到的字符设备驱动对象空间
    kfree(cdev);
    printk("5.释放申请到的字符设备驱动对象空间成功\n");

    return 0;
}

//构建设备树匹配
struct of_device_id device_tree_table[] = {
    {.compatible = "hqyj,platform"},
    {},
};

//分配对象并初始化
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,  
    .driver = {
        .name = "aaaaa",
        .of_match_table = device_tree_table,
    },
};

//一键注册宏
module_platform_driver(pdrv);

MODULE_LICENSE("GPL");

text.c:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "mycdev.h"

int main(int argc, const char *argv[])
{
char buf[128] = {0};
    int fd[3] = {0};
    char *dir[3] = {"/dev/mycdev0","/dev/mycdev1","/dev/mycdev2"};
    for (int i = 0; i < 3; i++)
    {
        fd[i] = open(dir[i], O_RDWR);
        if (-1 == fd[i])
        {
            perror("open is error\n");
            return -1;
        }
    }

    while (1)
    {
        for (int i = 0; i < 3; i++)
        {
            ioctl(fd[i], LED_ON);
            sleep(1);
            ioctl(fd[i], LED_OFF);
            sleep(1);
        }
    }

    for (int i = 0; i < 3; i++)
        close(fd[i]);

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值