2023.08.01 驱动开发day8

驱动层

复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>

#define LED_ON  _IO('l', 1)
#define LED_OFF _IO('l', 0)

struct class *cls;
struct device *dev;
struct device_node *dev_irq, *dev_led;
unsigned int major;
unsigned int irqno1, irqno2, irqno3;
struct gpio_desc *gpiono1, *gpiono2, *gpiono3;

//中断处理函数
irqreturn_t myirq_handler(int irq, void *dev)
{
    if(irq == irqno1) {
		gpiod_set_value(gpiono1, !gpiod_get_value(gpiono1));
	}
	else if(irq == irqno2) {
		gpiod_set_value(gpiono2, !gpiod_get_value(gpiono2));
	}
	else if(irq == irqno3) {
		gpiod_set_value(gpiono3, !gpiod_get_value(gpiono3));
	}
    return IRQ_HANDLED;
}

int mycdev_open(struct inode *inode, struct file *file)
{
    int a = inode->i_rdev;  //获取当前设备文件对应的设备号
    file->private_data = (void *)MINOR(a);  //将次设备号保存到当前文件的file结构中
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    unsigned int a = (unsigned int)file->private_data;
    switch(a)
    {
        case 0:
            switch(cmd)
            {
                case LED_ON:
                    gpiod_set_value(gpiono1, 1);
                    break;
                case LED_OFF:
                    gpiod_set_value(gpiono1, 0);
                    break;
            }
            break;   
        case 1:
            switch(cmd)
            {
                case LED_ON:
                    gpiod_set_value(gpiono2, 1);
                    break;
                case LED_OFF:
                    gpiod_set_value(gpiono2, 0);
                    break;
            }
            break; 
        case 2:
            switch(cmd)
            {
                case LED_ON:
                    gpiod_set_value(gpiono3, 1);
                    break;
                case LED_OFF:
                    gpiod_set_value(gpiono3, 0);
                    break;
            }
            break;                          
    }
    return 0;
}

struct file_operations fops = {
    .open = mycdev_open,
    .unlocked_ioctl = mycdev_ioctl,
};

static int __init mycdev_init(void)
{
    // 注册字符设备驱动
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("注册字符设备驱动失败\n");
        return major;
    }
    printk("注册字符设备驱动成功major=%d\n", major);

    // 向上提交目录
    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录信息成功\n");
    
    // 向上提交设备节点信息
    int i;
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(dev);
        }
    }
    printk("向上提交设备节点成功\n");
	
	int ret;
    //解析设备树节点
    dev_irq = of_find_node_by_path("/myirq");
    if(dev_irq == NULL)
    {
        printk("解析irq设备树节点失败\n");
        return -EFAULT;
    }
	dev_led = of_find_node_by_path("/leds");
    if(dev_led == NULL)
	{
        printk("解析led设备树节点失败\n");
        return -EIO;
    }
    printk("解析设备树节点成功\n");

    // 申请gpio_desc对象并设置输出为低电平
    gpiono1 = gpiod_get_from_of_node(dev_led, "led1-gpios", 0, GPIOD_OUT_LOW, NULL);
    if(IS_ERR(gpiono1))
    {
        printk("申请gpio对象失败\n");
        return -PTR_ERR(gpiono1);
    }
	gpiono2 = gpiod_get_from_of_node(dev_led, "led2-gpios", 0, GPIOD_OUT_LOW, NULL);
    if(IS_ERR(gpiono1))
    {
        printk("申请gpio对象失败\n");
        return -PTR_ERR(gpiono1);
    }
	gpiono3 = gpiod_get_from_of_node(dev_led, "led3-gpios", 0, GPIOD_OUT_LOW, NULL);
    if(IS_ERR(gpiono1))
    {
        printk("申请gpio对象失败\n");
        return -PTR_ERR(gpiono1);
    }
    printk("申请gpio对象成功\n");

    //根据设备树节点解析出软中断号s
    irqno1 = irq_of_parse_and_map(dev_irq, 0); //按键1索引号为0
    if(!irqno1)
    {
        printk("解析软中断号失败\n");
        return -ENXIO;
    }
	irqno2 = irq_of_parse_and_map(dev_irq, 1);
    if(!irqno2)
    {
        printk("解析软中断号失败\n");
        return -ENXIO;
    }
	irqno3 = irq_of_parse_and_map(dev_irq, 2);
    if(!irqno3)
    {
        printk("解析软中断号失败\n");
        return -ENXIO;
    }
    printk("解析软中断号成功 irqno123=%d,%d,%d\n",irqno1,irqno2,irqno3);

    //注册中断
    ret = request_irq(irqno1, myirq_handler, IRQF_TRIGGER_FALLING, "key1", NULL);
    if(ret)
    {
        printk("注册中断失败\n");
        return ret;
    }
	ret = request_irq(irqno2, myirq_handler, IRQF_TRIGGER_FALLING, "key2", NULL);
    if(ret)
    {
        printk("注册中断失败\n");
        return ret;
    }
	ret = request_irq(irqno3, myirq_handler, IRQF_TRIGGER_FALLING, "key3", NULL);
    if(ret)
    {
        printk("注册中断失败\n");
        return ret;
    }
    printk("注册中断成功\n");
    return 0;
}

static void __exit mycdev_exit(void)
{
    //注销中断
    free_irq(irqno1, NULL);
	free_irq(irqno2, NULL);
	free_irq(irqno3, NULL);
	//灭灯
	gpiod_set_value(gpiono1, 0);
	gpiod_set_value(gpiono2, 0);
	gpiod_set_value(gpiono3, 0);
	//释放gpio编号
	gpiod_put(gpiono1);
	gpiod_put(gpiono2);
	gpiod_put(gpiono3);
	//销毁节点信息
	int i;
	for (i = 0; i < 3; i++)
	{
		device_destroy(cls, MKDEV(major, i));
	}
	//销毁目录信息
	class_destroy(cls);
	//注销字符设备驱动
	unregister_chrdev(major, "mycdev");
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用层

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

#define LED_ON  _IO('l', 1)
#define LED_OFF _IO('l', 0)

int main(int argc,const char * argv[])
{
    int sel;
    int fd1 = open("/dev/myled0", O_RDWR);
    if(fd1 < 0)
    {
        perror("open");
        printf("%s : %s : %d\n", __FILE__, __func__, __LINE__);
        return -1;
    }
    int fd2 = open("/dev/myled1", O_RDWR);
    if(fd2 < 0)
    {
        perror("open");
        printf("%s : %s : %d\n", __FILE__, __func__, __LINE__);
        return -1;
    }
    int fd3 = open("/dev/myled2", O_RDWR);
    if(fd3 < 0)
    {
        perror("open");
        printf("%s : %s : %d\n", __FILE__, __func__, __LINE__);
        return -1;
    }

    while(1)
    {
        printf("1> LED1 ON\n");
        printf("2> LED1 OFF\n");
        printf("3> LED2 ON\n");
        printf("4> LED2 OFF\n");
        printf("5> LED3 ON\n");
        printf("6> LED3 OFF\n");
        printf("请选择功能>>>");
        scanf("%d", &sel);
        getchar();
        switch(sel)
        {
            case 1:
                ioctl(fd1, LED_ON); 
                break;
            case 2:
                ioctl(fd1, LED_OFF);
                break;
            case 3:
                ioctl(fd2, LED_ON); 
                break;
            case 4:
                ioctl(fd2, LED_OFF);
                break;
            case 5:
                ioctl(fd3, LED_ON); 
                break;
            case 6:
                ioctl(fd3, LED_OFF);
                break;
        }
    }
    close(fd1);
    close(fd2);
    close(fd3);
    return 0;
}
相关推荐
cxr8282 天前
SPARC方法论在Claude Code基于规则驱动开发中的应用
人工智能·驱动开发·claude·智能体
sukalot2 天前
window显示驱动开发—显示适配器的子设备
驱动开发
Evan_ZGYF丶2 天前
【RK3576】【Android14】如何在Android14下单独编译kernel-6.1?
linux·驱动开发·android14·rk3576
sukalot3 天前
window显示驱动开发—视频呈现网络简介
驱动开发
sukalot4 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(二)
驱动开发
zwhSunday4 天前
Linux驱动开发(1)概念、环境与代码框架
linux·运维·驱动开发
sukalot4 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(三)
驱动开发
sukalot4 天前
window显示驱动开发—为头装载和专用监视器生成自定义合成器应用(一)
驱动开发
cxr8286 天前
基于Claude Code的 规范驱动开发(SDD)指南
人工智能·hive·驱动开发·敏捷流程·智能体
zwhSunday6 天前
Linux驱动开发(2)进一步理解驱动
linux·驱动开发