驱动开发 作业 day9 9/20

基于platform实现

head.h

复制代码
#ifndef __HEAD_H__
#define __HEAD_H__ 

//构建LED开关的功能码,不添加ioctl第三个参数
#define LED_ON _IO('l',1)
#define LED_OFF _IO('l',0)

#endif 

test.c

复制代码
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include "head.h"


int main(int argc, char const *argv[])
{
    int a;
    char buf[128] = {0};
    int fd = open("/dev/myplatform0", O_RDWR);
    if (fd < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    while (1)
    {
        read(fd, buf, 1);  
        switch (buf[0])
        {
        case 1:
            ioctl(fd, LED_ON); // 开灯
            break;
        case 0:
            ioctl(fd, LED_OFF); // 关灯
        }
    }

    close(fd);

    return 0;
}

pdr.c

复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include "head.h"

char number = 0;

struct cdev *cdev;
char kbuf[128] = {0};
unsigned int major = 0;
unsigned int minor = 0;
dev_t devno;
module_param(major, uint, 0664); // 方便在命令行传递major的值
struct class *cls;
struct device *dev;
unsigned int irqno[3];       // 软中断号
struct gpio_desc *gpiono[3]; // gpio对象

unsigned int condition = 0;
// 定义一个等待队列头
wait_queue_head_t wq_head;

// 定义中断处理函数
irqreturn_t key_handler(int irq, void *dev)
{
    int which = (int)dev;
    switch (which)
    {
    case 0:
        printk("KEY1\n");
        number ^= 1;
        condition = 1; // 表示硬件数据就绪
        wake_up_interruptible(&wq_head);
        break;
    case 1:
        
        break;
    case 2:
        
        break;
    default:
        break;
    }
    return IRQ_HANDLED;
}

// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    int min = MINOR(inode->i_rdev); // 根据打开的文件对应的设备号获取次设备号
    file->private_data = (void *)min;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    // 判断IO方式
    if (file->f_flags & O_NONBLOCK) // 非阻塞
    {
    }
    else // 阻塞
    {
        wait_event_interruptible(wq_head, condition); // 先检查condition再将进程休眠
    }
    ret = copy_to_user(ubuf, &number, size);
    if (ret)
    {
        printk("copy_to_user err\n");
        return -EIO;
    }
    condition = 0; // 下一次硬件数据没有就绪
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{

    int min = (int)file->private_data;
    switch (min)
    {
    case 0: // 控制LED1
        switch (cmd)
        {
        case LED_ON:
            // 开灯
            gpiod_set_value(gpiono[0], 1);
            break;
        case LED_OFF:
            // 关灯
            gpiod_set_value(gpiono[0], 0);
            break;
        }
        break;
    case 1: // 控制LED2
        switch (cmd)
        {
        case LED_ON:
            // 开灯
            gpiod_set_value(gpiono[1], 1);
            break;
        case LED_OFF:
            // 关灯
            gpiod_set_value(gpiono[1], 0);
            break;
        }
        break;
    case 2: // 控制LED3
        switch (cmd)
        {
        case LED_ON:
            // 开灯
            gpiod_set_value(gpiono[2], 1);
            break;
        case LED_OFF:
            // 关灯
            gpiod_set_value(gpiono[2], 0);
            break;
        }
        break;
    }
    return 0;
}

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

struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .unlocked_ioctl = mycdev_ioctl,
    .release = mycdev_close,
};

// 封装probe函数
int pdrv_probe(struct platform_device *pdev)
{
    int ret;
    // 初始化等待队列
    init_waitqueue_head(&wq_head);
    // 字符设备注册
    // 1.申请驱动对象
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("申请对象空间失败!\n");
        ret = -EFAULT;
        goto out1;
    }
    printk("申请对象成功!\n");
    // 2.初始化驱动对象
    cdev_init(cdev, &fops);
    printk("初始化对象成功!\n");

    // 3.申请主设备号和一定数量设备资源
    if (major > 0) // 静态指定设备号
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "myplatform");
        if (ret)
        {
            printk("静态申请设备号失败!\n");
            goto out2;
        }
    }
    else if (major == 0) // 动态申请设备号
    {
        ret = alloc_chrdev_region(&devno, 0, 3, "myplatform");
        if (ret)
        {
            printk("动态申请设备号失败!\n");
            goto out2;
        }
        major = MAJOR(devno); // 获取主设备号
        minor = MINOR(devno); // 获取此设备号
    }
    printk("申请设备号成功!\n");

    // 4.根据申请的设备号和驱动对象注册驱动
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("驱动注册失败!\n");
        goto out3;
    }
    printk("注册驱动成功!\n");

    // 5.向上提交目录信息
    cls = class_create(THIS_MODULE, "myplatform");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败!\n");
        ret = -PTR_ERR(cls);
        goto out4;
    }
    printk("向上提交目录成功!\n");

    // 6.向上提交设备信息文件
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myplatform%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备信息失败!\n");
            ret = -PTR_ERR(dev);
            goto out5;
        }
    }
    printk("向上提交设备信息文件成功!\n");

    // 获取中断类型的资源

    for (i = 0; i < 3; i++)
    {
        // 根据节点解析软中断号
        irqno[i] = platform_get_irq(pdev, i);
        if (!irqno[i])
        {
            printk("获取软中断号失败!\n");
            return -ENXIO;
        }
        printk("获取软中断号成功!%d\n", irqno[i]);

        // 注册软中断号
        ret = request_irq(irqno[i], key_handler, IRQF_TRIGGER_FALLING, "key_int", (void *)i);
        if (ret < 0)
        {
            printk("注册软中断号失败!\n");
            return ret;
        }
        printk("注册软中断号成功!\n");

        gpiono[i] = gpiod_get_from_of_node(pdev->dev.of_node, "led-gpios", i, GPIOD_OUT_LOW, NULL);
        if (IS_ERR(gpiono[i]))
        {
            printk("解析GPIO管脚信息失败\n");
            return -ENXIO;
        }
        printk("解析GPIO管脚信息成功\n");
    }
    return 0;
out5:
    for (--i; i > -1; i--)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);
out4:
    cdev_del(cdev);
out3:
    unregister_chrdev_region(MKDEV(major, minor), 3);
out2:
    kfree(cdev);
out1:
    return ret;
}
// 封装remove函数
int pdrv_remove(struct platform_device *pdev)
{
    // 销毁设备信息文件
    int i;
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    // 销毁设备类目录
    class_destroy(cls);
    // 注销驱动
    cdev_del(cdev);
    // 注销设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
    // 释放cdev对象
    kfree(cdev);

    for (i = 0; i < 3; i++)
    {
        // 注销中断
        free_irq(irqno[i], (void *)i);

        // 熄灭led灯
        gpiod_set_value(gpiono[i], 0);

        // 释放gpio对象
        gpiod_put(gpiono[i]);
    }
    printk("注销中断成功!\n");
    return 0;
}

// 构建设备树匹配表
struct of_device_id oftable[] = {
    {.compatible = "hqyj,myplatform"},
    {/* end node */}, // 防止数组越界
};

// 定义驱动信息对象并初始化
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "aaa",
        .of_match_table = oftable, // 用于设备树匹配
    },
};

// 一键注册宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
相关推荐
学编程的小程27 分钟前
从“单模冲锋”到“多模共生”——2026 国产时序数据库新物种进化图谱
数据库·时序数据库
卓怡学长28 分钟前
m111基于MVC的舞蹈网站的设计与实现
java·前端·数据库·spring boot·spring·mvc
存在的五月雨33 分钟前
Redis的一些使用
java·数据库·redis
小冷coding8 小时前
【MySQL】MySQL 插入一条数据的完整流程(InnoDB 引擎)
数据库·mysql
鲨莎分不晴8 小时前
Redis 基本指令与命令详解
数据库·redis·缓存
专注echarts研发20年9 小时前
工业级 Qt 业务窗体标杆实现・ResearchForm 类深度解析
数据库·qt·系统架构
自由的好好干活9 小时前
PLX 9x5x PCI 驱动程序执行流程详解(Qoder生成)
驱动开发·ai编程
周杰伦的稻香11 小时前
MySQL中常见的慢查询与优化
android·数据库·mysql
冉冰学姐11 小时前
SSM学生社团管理系统jcjyw(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·学生社团管理系统·多角色管理
nvd1112 小时前
深入分析:Pytest异步测试中的数据库会话事件循环问题
数据库·pytest