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;
}
相关推荐
gopher951113 小时前
linux驱动开发-中断子系统
linux·运维·驱动开发
gopher95111 天前
linux驱动开发-设备树
linux·驱动开发
三菱-Liu2 天前
三菱变频器以模拟量电流进行频率设定(电流输入)
驱动开发·单片机·嵌入式硬件·硬件工程·制造
三菱-Liu2 天前
三菱FX5U CPU 内置以太网功能
网络·驱动开发·硬件工程·制造·mr
让开,我要吃人了2 天前
OpenHarmony鸿蒙( Beta5.0)摄像头实践开发详解
驱动开发·华为·移动开发·harmonyos·鸿蒙·鸿蒙系统·openharmony
OH五星上将3 天前
如何更换OpenHarmony SDK API 10
驱动开发·嵌入式硬件·sdk·harmonyos·openharmony·鸿蒙开发
OH五星上将5 天前
OpenHarmony(鸿蒙南向开发)——标准系统移植指南(二)Linux内核
linux·驱动开发·嵌入式硬件·移动开发·harmonyos·鸿蒙开发·鸿蒙内核
芊言芊语5 天前
蓝牙驱动开发详解
驱动开发
让开,我要吃人了5 天前
OpenHarmony鸿蒙( Beta5.0)RTSPServer实现播放视频详解
驱动开发·嵌入式硬件·华为·移动开发·harmonyos·鸿蒙·openharmony
OH五星上将5 天前
OpenHarmony(鸿蒙南向开发)——轻量和小型系统三方库移植指南(二)
驱动开发·移动开发·harmonyos·内存管理·openharmony·鸿蒙内核·鸿蒙移植