驱动开发-day10

驱动代码:

cs 复制代码
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
struct cdev* cdev;
unsigned int major = 500;
unsigned int minor = 0;
dev_t devno;
struct class* cls;
struct device* dev;

struct resource* res;
unsigned int irqno;
struct gpio_desc* gpiono;

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

ssize_t mycdev_read(struct file* file, char* ubuf, size_t size, loff_t* lof)
{
    int ret;
    // 将进程切换到休眠状态
    wait_event_interruptible(wq_head, condition);

    if (size > sizeof(number)) {
        size = sizeof(number);
    }

    ret = copy_to_user(ubuf, &number, size);
    if (ret) {
        printk("copy_to_user filed\n");
        return -EIO;
    }
    condition = 0; // 下次硬件的数据没有准备好
    return size;
}
irqreturn_t myirq_led_handler(int irqno, void* dev_id)
{

    gpiod_set_value(gpiono, !gpiod_get_value(gpiono));
    number = gpiod_get_value(gpiono);
    condition = 1; // 数据准备就绪
    // 唤醒休眠的进程
    wake_up_interruptible(&wq_head);

    return IRQ_HANDLED;
}

struct file_operations fops = {

    .read = mycdev_read,

};
int pdrv_probe(struct platform_device* pdev)
{

    int ret, i;
    // 初始化等待队列头
    init_waitqueue_head(&wq_head);
    // 分配字符设备驱动对象空间
    cdev = cdev_alloc();
    if (cdev == NULL) {
        printk("申请字符设备驱动空间失败\n");
        ret = -EFAULT;
        goto out1;
    }
    // 字符设备驱动对象部分初始化
    cdev_init(cdev, &fops);
    // 静态申请设备号
    if (major > 0) {

        ret = register_chrdev_region(MKDEV(major, minor), 3, "mykey");
        if (ret) {
            printk("静态指定设备号失败\n");
            goto out2;
        }
    } else { // 动态申请设备号
        ret = alloc_chrdev_region(&devno, minor, 3, "mykey");
        if (ret) {
            printk("动态指定设备号失败\n");
            goto out2;
        }
        major = MAJOR(devno);
        minor = MINOR(devno);
    }
    // 注册字符设备驱动对象
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret) {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    // 向上提交目录
    cls = class_create(THIS_MODULE, "mykey");
    if (IS_ERR(cls)) {
        printk("向上提交目录失败\n");
        ret = -PTR_ERR(cls);
        goto out4;
    }
    printk("向上提交目录成功\n");
    // 向上提交设备节点

    for (i = 0; i < 3; i++) {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mykey%d", i);
        if (IS_ERR(dev)) {
            printk("向上提交节点信息失败\n");
            ret = -PTR_ERR(dev);
            goto out5;
        }
    }
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    // printk("向上提交设备节点信息成功\n");

    // 获取设备信息
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (res == NULL) {
        printk("获取资源失败\n");
        return -ENXIO;
    }
    printk("获取资源信息成功 %x\n", res->start);
    irqno = platform_get_irq(pdev, 0);
    if (irqno < 0) {
        printk("获取中断资源失败\n");
        return irqno;
    }
    printk("中断类型资源为%d\n", irqno);
    // 获取gpio信息
    gpiono = gpiod_get_from_of_node(pdev->dev.of_node, "led1", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono)) {
        printk("解析GPIO信息失败\n");
        return -PTR_ERR(gpiono);
    }

    // 注册中断
    ret = request_irq(irqno, myirq_led_handler, IRQF_TRIGGER_FALLING, "key1", NULL);
    if (ret) {
        printk("注册失败\n");
        return ret;
    }
    return 0;
out5:

    // 销毁提交的设备信息
    device_destroy(cls, MKDEV(major, minor));

    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)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    // 灭灯
    gpiod_set_value(gpiono, 0);
    // 释放gpio信息
    free_irq(irqno, NULL);
    gpiod_put(gpiono);
    // 销毁设备信息
    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);
    // 释放对象空间
    kfree(cdev);
    return 0;
}
// 构建设备树匹配表
struct of_device_id oftable[] = {
    {
        .compatible = "hqyj,myplatform",
    },
    {
        .compatible = "hqyj,myplatform1",
    },
    {},
};
struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "aaaaa",
        .of_match_table = oftable,
    },

};
// 一键注册宏
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

测试代码:

cs 复制代码
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char const* argv[])
{
    int fd;

    unsigned int number;
    // 打开LED
    fd = open("/dev/mykey0", O_RDWR);
    if (fd < 0) {
        printf("打开设备文件失败\n");
        exit(-1);
    }

    while (1) {

        read(fd, &number, sizeof(number));
        printf("number=:%d\n", number);
    }
    close(fd);
    return 0;
}
相关推荐
TangDuoduo00052 小时前
【Linux字符设备驱动】
linux·驱动开发
Max_uuc2 小时前
【C++ 硬核】摆脱开发板:用 Google Test + Mock 构建嵌入式 TDD (测试驱动开发) 体系
驱动开发·tdd
小龙报4 小时前
【51单片机】串口通讯从入门到精通:原理拆解 + 参数详解 + 51 单片机实战指南
c语言·驱动开发·stm32·单片机·嵌入式硬件·物联网·51单片机
dlz083620 小时前
POE驱动开发流程
驱动开发
嵌入式-老费1 天前
Linux camera驱动开发(DVP接口的camera sensor)
驱动开发
VernonJsn2 天前
visual studio 2022的windows驱动开发
ide·驱动开发·visual studio
嵌入式郑工2 天前
RK3566 LubanCat 开发板 USB Gadget 配置完整复盘
linux·驱动开发·ubuntu
雾削木3 天前
树莓派 ESPHome 固件编译与烧录全攻略(解决超时与串口识别问题)
驱动开发
春日见4 天前
win11 分屏设置
java·开发语言·驱动开发·docker·单例模式·计算机外设
DarkAthena4 天前
【GaussDB】手动编译不同python版本的psycopg2驱动以适配airflow
驱动开发·python·gaussdb