2023-3-28作业

自己编写驱动,应用层程序,在应用层通过ioctl控制LED灯流水,当按键KEY1按下,让风扇转动


新增设备树结点如下:

 

mycdev.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.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 device_node *dnode;  // 用于记录设备树结点信息
struct device_node *dnode2; // 用于记录设备树结点信息
int irqno;                  // 用于记录获取到的软中断号
struct gpio_desc *gpion1;   // 用于记录申请到的gpio编号
struct gpio_desc *gpion2;
struct gpio_desc *gpion3;
struct gpio_desc *gpion4;

// 中断处理函数
irqreturn_t irq_handler(int irqno, void *arg)
{
    gpiod_set_value(gpion4, !gpiod_get_value(gpion4));
    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,
};

static int __init mycdev_init(void)
{
    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");

    // 解析设备树节点
    dnode = of_find_node_by_name(NULL, "myleds");
    if (dnode == NULL)
    {
        printk("解析设备树节点失败\n");
        return -ENXIO;
    }
    dnode2 = of_find_node_by_name(NULL, "myirqs");
    if (dnode2 == NULL)
    {
        printk("解析设备树节点失败\n");
        return -ENXIO;
    }
    printk("解析设备树节点成功\n");
    // 根据设备树节点解析gpio编号
    gpion1 = gpiod_get_from_of_node(dnode, "led1", 0, GPIOD_OUT_LOW, NULL); // LED1:gpioe 10
    if (IS_ERR(gpion1))
    {
        printk("解析gpio编号失败\n");
        return PTR_ERR(gpion1);
    }
    gpion2 = gpiod_get_from_of_node(dnode, "led2", 0, GPIOD_OUT_LOW, NULL); // LED2:gpiof 10
    if (IS_ERR(gpion2))
    {
        printk("解析gpio编号失败\n");
        return PTR_ERR(gpion2);
    }
    gpion3 = gpiod_get_from_of_node(dnode, "led3", 0, GPIOD_OUT_LOW, NULL); // LED3:gpioe 8
    if (IS_ERR(gpion3))
    {
        printk("解析gpio编号失败\n");
        return PTR_ERR(gpion3);
    }
    gpion4 = gpiod_get_from_of_node(dnode, "fan", 0, GPIOD_OUT_LOW, NULL); // fan:gpioe 9
    if (IS_ERR(gpion4))
    {
        printk("解析gpio编号失败\n");
        return PTR_ERR(gpion4);
    }
    printk("解析gpio编号成功\n");

    // 根据设备树结点获取软中断号
    irqno = irq_of_parse_and_map(dnode2, 0);
    if (!irqno)
    {
        printk("获取软中断号失败\n");
        return -ENXIO;
    }
    printk("根据设备树结点获取软中断号成功\n");

    // 注册要使用的中断
    ret = request_irq(irqno, irq_handler, IRQF_TRIGGER_FALLING, "myirq", NULL);
    if (ret)
    {
        printk("注册中断失败\n");
        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;
}

static void __exit mycdev_exit(void)
{
    int i;
    // 关闭风扇
    gpiod_set_value(gpion4, 0);
    // 注销中断
    free_irq(irqno, NULL);
    // 将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);
    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");
}

module_init(mycdev_init);
module_exit(mycdev_exit);
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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值