platform驱动总线基于设备树信息的匹配

一.细节要求

/*功能实现 在stm32开发板上实现功能

           1.使用阻塞IO读取number变量的值,当number的值改变时打印number的值

           2.注册KEY1按键的驱动和LED1的驱动以及对应的设备文件,

           3.按键和指示灯设备信息放在同一个设备树的节点中

           4.当KEY1按下时LED1灯的状态取反,number的值取反,number值为0或1

             5.使用paltform_device驱动总线匹配设备树节点硬件信息进行控制

           */

二.应用层代码

        主要打开设备文件,通过阻塞的方式读取文件中number变量的值

#include "user.h"

int main(int argc, const char* argv[])
{
    int fd;
    int status;

    if ((fd = open("/dev/myled1", O_RDWR)) == -1) {
        perror("open error");
        exit(EXIT_FAILURE);
    }

    while (1) {
        read(fd, &status, sizeof(status));
        printf("status = %d\n", status);
    }
    close(fd);
    return 0;
}

三.驱动代码

        1.设备文件的注册 2.中断,GPIO的注册 3.驱动总线匹配设备树节点信息获取信息 4.实现变量以及LED状态的改变

#include "cdev.h"
/*功能实现 在stm32开发板上实现功能
           1.使用阻塞IO读取number变量的值,当number的值改变时打印number的值
           2.注册KEY1按键的驱动和LED1的驱动以及对应的设备文件,
           3.按键和指示灯设备信息放在同一个设备树的节点中
           4.当KEY1按下时LED1灯的状态取反,number的值取反,number值为0或1
           */


// 中断处理函数
int led_work(void);
irqreturn_t myirq_handler(int irqno, void *dev_id)
{
    printk("软中断号%d的处理\n", irqno);
    // 改变LED灯的状态
    gpiod_set_value(gpiono_led1, !gpiod_get_value(gpiono_led1));
    // 改变number的值
    //  改变标志
    condition = 1;
    // 唤醒可中断进程
    wake_up_interruptible(&wq_head);
    return IRQ_HANDLED;
}
// 中断信息初始化函数
int request_interrupts(struct platform_device *pdev)
{
    // 准备:在设备树中添加gpiof组控制按键的节点信息
    // 2.根据节点地址寻找key对应的软中断号 irq_of_parse_and_map
    // 获取KEY按键对应的软中断号 参2的索引的值在于你的自定义节点GPIO控制所在位置,
    // myirq{
    //     interrupts-extended=<&gpiof 9 0>,<&gpiof 7 0>,<&gpiof8 0>;
    // };KEDY1对应<&gpiof 9 0>索引为0,KEY2对应<&gpiof 7 0>,索引为1,类似数组依次对应

    myirq_key[0] = irq_of_parse_and_map(pdev->dev.of_node, 0);
    if (!myirq_key[0])
    {
        printk("key1软中断号获取失败\n");
        return ENOMEM;
    }
    printk("key1软中断号获取成功\n");

    // 3.注册中断号 包括对应的软中断号,中断执行的处理函数,中断的检测方式,中断名
    // 注册KEY对应的软中断号
    // 参1:中断号对应的软中断号 参2:中断的处理函数 参3:中断触发的方式 参4:为中断起一个名字 参5:给中断函数传递的值

    // KEY1
    ret = request_irq(myirq_key[0], myirq_handler, IRQF_TRIGGER_FALLING, irq_key1_name, 0);
    if (ret < 0)
    {
        printk("key1中断注册失败\n");
        return ret;
    }
    printk("key1中断注册成功\n");
    return 0;
}
// LED灯的初始化
int request_LED(struct platform_device *pdev)
{
    // 2.通过节点信息获取GPIO对象
    // 2.1申请LED1对应资源
    gpiono_led1 = gpiod_get_from_of_node(pdev->dev.of_node, "led1", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono_led1))
    {
        printk("LED1资源申请失败\n");
        return -PTR_ERR(gpiono_led1);
    }
    printk("LED1资源申请成功\n");
    return 0;
}


//在驱动中创建一个用于设备树匹配的表
struct of_device_id oftable[]={
    {.compatible="hqyj,myplatform",},
    {.compatible="hqyj,myplatform1",},
    {.compatible="hqyj,myplatform2",},
    {},
};


int pdrv_probe(struct platform_device *pdev)
{
    printk("匹配成功:%d\n",__LINE__);

    printk("设备名=%s\n",pdev->name);
    /****初始化区域_begin****/
    // 中断初始化
    request_interrupts(pdev);
    // LED灯初始化
    request_LED(pdev);
    // 初始化等待队列头
    init_waitqueue_head(&wq_head);
    //设备文件的初始化
    led_work();
    return 0;
}
int prdv_remove(struct platform_device *pdev)
{
    // 关灯
    gpiod_set_value(gpiono_led1, 0);
 
    // 释放申请的LED资源
    gpiod_put(gpiono_led1);
    // 销毁设备信息
    device_destroy(cls, MKDEV(major, 0));
    // 销毁目录
    class_destroy(cls);
    // 注销字符设备驱动
    unregister_chrdev(major, "myled");
    return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
   

    // 向用户空间读取拷贝
    if (size > sizeof(kbuf)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size = sizeof(kbuf);
  
    // 判断condition的值,为1则改变number的值
    wait_event_interruptible(wq_head, condition);
    if (number==1)
    {
        number = 0;
    }
    else if(number==0)
    {
        number = 1;
    }
    ret = copy_to_user(ubuf, &number, size);
    if (ret) // 拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
     printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
struct file_operations fops = {
    .read = mycdev_read,
};
int led_work(void)
{
    /*****LED灯的驱动安装流程*****/
    // 1.安装驱动获取mojor设备号
    printk("LED灯驱动安装\n");
    major = register_chrdev(0, "myled", &fops); // 系统动态申请主设备号
    if (major < 0)
    {
        printk("主设备号申请失败\n");
        return major;
    }
    printk("主设备号申请成功:major=%d\n", major);

    // 3.向上提交目录
    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return PTR_ERR(cls);
    }
    printk("向上提交目录成功\n");
    /*
    cls=class_create(THIS_MODULE,"mychrdev1");
    cls=class_create(THIS_MODULE,"mychrdev2");
    */
    // 4.向上提交设备节点信息,分别为每一个灯创建设备文件

   
        dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "myled1");
        if (IS_ERR(dev))
        {
            printk("向上提交设备信息myled1失败\n");
            return PTR_ERR(dev);
        }
        printk("向上提交设备信息myled1成功\n");
        return 0;
}
//驱动信息初始化
struct platform_driver pdrv={
    .probe=pdrv_probe,
    .remove=pdrv_probe,
    .driver={
        .name="pdrv",
        .of_match_table=oftable,//指点使用设备树表
    },
};

module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

你可能感兴趣的:(驱动,驱动开发)