驱动开发-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;
}
相关推荐
cxr8283 天前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot4 天前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶4 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot5 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot5 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday5 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot6 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot6 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr8287 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday7 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发