• Linux驱动基础篇(一)GPIO(上)LED驱动


    Linux驱动基础(一)GPIO(上)LED驱动

    一、开发环境准备

    ubuntu22.04 + Vscode + OrangePi+

    1.安装交叉编译工具+编译内核

    (1)安装交叉编译工具

    📌选择交叉编译工具链

    以官网下载页面为例

    在这里插入图片描述

    在这里插入图片描述

    📌下载交叉编译工具

    交叉编译工具链官方下载地址(国外)
    交叉编译工具链清华开源镜像站下载地址(国内)

    📌解压、添加环境变量、测试安装结果

    sudo tar -xvf arm-none-linux-gnueabihf-12.3-x86_64-.tar.xz -C /mnt	# 解压到/mnt目录
    
    • 1
    # 添加环境变量,在原有的双引号前输入:后粘贴复制工具链的路径
    # 例如这里是/mnt/arm-none-linux-gnueabihf-12.3-x86_64/bin
    # 保存后重新登录或重启生效,输入arm-尝试tab能否补全命令以及arm-none-linux-gnueabihf-gcc -v进行测试
    sudo vi /etc/environment
    
    • 1
    • 2
    • 3
    • 4
    (2)修改Makefile指定编译器和架构
    # 在内核源码顶层目录的Makefile,搜索CROSS_COMPILE或ARCH,添加以下内容
    # 要配置上面步骤后,CROSS_COMPILE可以这样写;没有的话要写绝对路径
    ARCH  := arm
    CROSS_COMPILE	:= arm-none-linux-gnueabihf-
    
    • 1
    • 2
    • 3
    • 4
    (3)生成配置文件.config
    # 生成默认的配置文件,也可以make menuconfig进行配置,最后会保存到.config
    make defconfig
    
    • 1
    • 2

    ❓❓❓ 报错1:arch/arm是一个目录 ❓❓❓

    解决:ARCH := arm (arm后面多了空格)

    ❓❓❓ 报错2:/bin/sh: 1: flex: not found ❓❓❓

    sudo apt-get install flex

    ❓❓❓ 报错3:/bin/sh: 1: bison: not found ❓❓❓

    sudo apt-get install bison

    ❓❓❓ 报错4:make menuconfig 打开失败❓❓❓

    sudo apt-get install libncurses5-dev


    (4)编译内核
    # 建议这里可以将所有的CPU核心分配给虚拟机,提高速度
    make -j24
    
    • 1
    • 2

    ❓❓❓ 报错5:scripts/extract-cert.c:21:10: fatal error: openssl/bio.h: 没有那个文件或目录❓❓❓

    原因:没有安装libssl-dev或者安装版本过高
    解决:(未安装)sudo apt-get install libssl-dev
    (版本过高)sudo apt-get install aptitude(安装aptitude软件包管理器)
    sudo aptitude install libssl-dev(使用aptitude安装libssl-dev)
    选择不保持当前版本,出现提示输出n,确认y后降级


    2.安装配置vscode

    二、第一个驱动程序HelloWorld

    1.简单的驱动框架使用

    简单的驱动框架分为三步:①装载驱动、②操作驱动、③卸载驱动。个人认为这样划分容易理解和记忆!
    在这里插入图片描述

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define   DEV_NAME   "hello"
    
    static struct class *hello_cls;
    static int ERR;
    static struct device *device;
    
    static int hello_open(struct inode *inode, struct file *file){
        printk("%s  %s  %d\n",__FILE__,__FUNCTION__ ,__LINE__);
        return 0;
    }
    static ssize_t hello_read (struct file *file, char __user *buf, size_t size, loff_t *offset){
        printk("%s  %s  %d\n",__FILE__,__FUNCTION__ ,__LINE__);
        
        return 0;
    }
    
    static ssize_t hello_write (struct file *file, const char __user *buf, size_t size, loff_t * offset){
        printk("%s  %s  %d\n",__FILE__,__FUNCTION__ ,__LINE__);
        return 0;
    }
    static int hello_release(struct inode *inode, struct file *file){
        printk("%s  %s  %d\n",__FILE__,__FUNCTION__ ,__LINE__);
        return 0;
    }
    
    static unsigned int major = 0;
    static struct file_operations hello_fop = {
            .owner = THIS_MODULE,
            .open = hello_open,
            .read = hello_read,
            .write = hello_write,
            .release = hello_release
    };
    
    /**
     * @brief 初始化驱动(注册设备register_chrdev()、创建设备节点device_create())
     * 注册设备需要:设备号、设备名、设备file_operaction
     * 创建设备节点需要:class、devtype-由设备号确定、设备节点名
     **/
    static int  __init hello_Driver_Init(void){
        printk("%s  %s  %d\n",__FILE__,__FUNCTION__ ,__LINE__);
        major = register_chrdev(major,DEV_NAME,&hello_fop);
        hello_cls = class_create(THIS_MODULE,DEV_NAME);
        if(IS_ERR(hello_cls)){
            ERR = PTR_ERR(hello_cls);
            pr_err("class create failed.(error code:%d)\n",ERR);
            unregister_chrdev(major,DEV_NAME);
            return ERR;
        }
        device = device_create(hello_cls,NULL, MKDEV(major,0),NULL,"hello");
        if(IS_ERR(device)){
            ERR = PTR_ERR(device);
            pr_err("device create failed.(error code:%d)\n",ERR);
            class_destroy(hello_cls);
            unregister_chrdev(major,DEV_NAME);
            return ERR;
        }
        return 1;
    }
    
    /**
     * @brief 卸载驱动(删除设备节点device_destroy()、注销设备unregister_chrdev())
     **/
    static void __exit hello_Driver_Exit(void){
        printk("%s  %s  %d\n",__FILE__,__FUNCTION__ ,__LINE__);
        device_destroy(hello_cls, MKDEV(major,0));
        class_destroy(hello_cls);
        unregister_chrdev(major,DEV_NAME);
    }
    
    module_init(hello_Driver_Init);
    module_exit(hello_Driver_Exit);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    2.重要的几个宏/函数

    2.1 错误指针判断IS_ERR( )、PTR_ERR( )和pr_err( )

    2.2 用户和内核之间的数据拷贝copy_from_user( )、copy_to_user( )和get_user( )、put_user( )

    三、第二个驱动程序点亮LED

    1.阅读原理图和芯片手册

    (1)找到OrangePi PC+的两个LED

    📌在原理图中搜索"LED",找到关于OrangePi+ LED部分的原理图

    在这里插入图片描述

    • 从原理图关于LED的部分可以看到,OrangePi PC+有两个LED:PWR-LED(供电时闪烁LED)、STATUS-LED(开机运行时状态LED)。
    (2)找到两个LED和CPU连接的引脚

    在这里插入图片描述

    • PWR-LED连接到PL10、STATUS_LED连接到PA15
    (3)在芯片手册找到PL10和PA15

    在这里插入图片描述

    (4)找到对应的配置寄存器

    在这里插入图片描述

    (5)找到对应的数据寄存器

    在这里插入图片描述

    2.从驱动开始点亮香橙派orangepi plus的LED

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define PIO_BASE    0x01c20800
    
    static volatile unsigned int *PA_CFG1_REG;
    static volatile unsigned int *PA_DATA_REG;
    static unsigned int major = 0;
    static struct class *led_class;
    
    static int led_open(struct inode *inode, struct file *file){
        /* output: bit30-28 = 001 */
        *PA_CFG1_REG &= ~(0x01 << 30);
        *PA_CFG1_REG &= ~(0x01 << 29);
        *PA_CFG1_REG |= (0x01 << 28);
        printk("======= led_open():PA_CFG_REG = 0x%x =========\n",*PA_CFG1_REG);
        return 0;
    }
    static int led_close(struct inode *inode, struct file *file){
        /* default: bit30-28 = 111 */
        *PA_CFG1_REG |= (0x01 << 30);
        *PA_CFG1_REG |= (0x01 << 29);
        *PA_CFG1_REG |= (0x01 << 28);
        printk("======= led_close():PA_CFG_REG = 0x%x =========\n",*PA_CFG1_REG);
        return 0;
    }
    static ssize_t led_read (struct file *file, char __user *buf, size_t size, loff_t *off){
        return 0;
    }
    static ssize_t led_write (struct file *file, const char __user *buf, size_t size, loff_t *off){
        char val;   // 0-OFF 1-ON
        int ret = copy_from_user(&val,buf,1);
        ret = 3;
        if(val){
            *PA_DATA_REG |= 0x1<<15; //bit15 = 1
        }else{
            *PA_DATA_REG &= ~(0x1<<15); //bit15 = 0
        }
        return 1;
    }
    static struct file_operations led_ops = {
        .owner = THIS_MODULE,
        .open = led_open,
        .read = led_read,
        .write = led_write,
        .release = led_close,
    };
    static int  __init led_driver_init(void){
        printk("======= %s %s %d ========\n",__FILE__,__FUNCTION__ ,__LINE__);
        major = register_chrdev(major,"led_driver",&led_ops);
        led_class = class_create(THIS_MODULE,"led_driver");
        device_create(led_class,NULL, MKDEV(major,0),NULL,"led1");
        PA_CFG1_REG = ioremap(PIO_BASE+0x04,4);
        PA_DATA_REG = ioremap(PIO_BASE+0x10,4);
        *PA_CFG1_REG &= ~(0x1<<30);
        *PA_CFG1_REG &= ~(0x1<<29);
        *PA_CFG1_REG |= 0x1<<28;
        return 0;
    }
    
    static void  __exit led_driver_exit(void){
        printk("======= %s %s %d ========\n",__FILE__,__FUNCTION__ ,__LINE__);
        iounmap(PA_CFG1_REG);
        iounmap(PA_DATA_REG);
        device_destroy(led_class, MKDEV(major,0));
        class_destroy(led_class);
        unregister_chrdev(major,"led_driver");
    
    }
    
    module_init(led_driver_init);
    module_exit(led_driver_exit);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    这里为了没有写应用层的测试程序,只简单粗暴点个LED,加载驱动后led亮,香橙派OrangePi Plus上是红灯

    四、面向对象设计思想

    上面所写的LED驱动程序,驱动和硬件操作绑定在一起,更换开发板平台所有的代码就完全不适用,不仅可维护性和可读性差,而且代码难以移植到其他平台,增加了代码冗余和维护的工作量。因此,内核驱动程序中采用面向对象的编程思想,将驱动和硬件操作的代码分离并进行合理的抽象和封装,通常是更好的选择

    📌改写LED驱动,实现应用层程序控制两个LED亮灭

    【led_test_app.c】

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    /** ./test_led_app /dev/led1 on **/
    /** ./test_led_app /dev/led2 off **/
    
    int main(int argc,char **argv){
        char *buf = (char *)malloc(36);
        int nwrite = 0;
        if(argc < 3){
            printf("Usage:%s [dev-led1/led2]  [status-on/off]  \n",argv[0]);
            return -1;
        }
        int fd = open(argv[1],O_RDWR);
        if(fd < 0){
            printf("open device error\n");
            return -1;
        }
    
        int val_on = 1;
        int val_off = 0;
    
        if( 0 == strcmp(argv[2],"on")){
            nwrite = write(fd,&val_on,sizeof(val_on));
        }else if( 0 == strcmp(argv[2],"off")){
            nwrite = write(fd,&val_off,sizeof(val_off));
        }else{
            printf("syntax error\n");
        }
        close(fd);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    【orangepi_plus_leds.h】

    #ifndef _ORANGEPI_PLUS_LEDS_H
    #define _ORANGEPI_PLUS_LEDS_H
    
    #define MIN(a,b)    (a < b ? a : b)
    #define PIN_GROUP(pin)  (pin >> 22)
    #define PIN_NUM(pin)    (pin & 0x3FFFFF)
    
    struct GPIOx_PIN {   
        /** bit[21:0] pin_num **/
        /** bit[31:22] pin_group **/
        int pin; 
        int count;
        volatile unsigned int *GPIOx_CFG_REG;        //配置寄存器
        volatile unsigned int *GPIOx_DAT_REG;        //数据寄存器
        int (*gpio_init)(int pin);             //初始化函数
        int (*gpio_control)(int pin,int status);   //控制函数
        struct GPIOx_PIN *next;
    };
    
    extern struct GPIOx_PIN *get_GPIOA_PIN15(void);
    extern struct GPIOx_PIN *get_GPIOL_PIN10(void);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    【orangepi_plus_led1.c】

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "orangepi_plus_leds.h"
    
    #define BASE_ADDR   (0x01c20800)
    
    #define WHICH_CFG(pin_num)  \
    	((pin_num >= 0 && pin_num <= 7)   ?  0x00 : \
    	 (pin_num >= 8 && pin_num <= 15)  ?  0x04 : \
    	 (pin_num >= 15 && pin_num <= 21) ?  0x08 : 0x0c)
    
    
    int led1_init(int pin);
    int led1_control(int pin,int status);
    
    static struct GPIOx_PIN GPIOA_PIN15 = {
    	.pin = (0 << 22)|(15),          //第0组第15pin(GPIOA_15)
    	.gpio_init = led1_init,
    	.gpio_control = led1_control,
    	.next = NULL
    };
    
    int led1_init(int pin){
    	int pin_num = PIN_NUM(pin);
    	GPIOA_PIN15.GPIOx_CFG_REG = ioremap(BASE_ADDR + PIN_GROUP(pin)* 0x24 + WHICH_CFG(pin_num),4);
    	GPIOA_PIN15.GPIOx_DAT_REG = ioremap(BASE_ADDR + PIN_GROUP(pin)* 0x24 + 0x10,4);
    	if(GPIOA_PIN15.GPIOx_CFG_REG == NULL || GPIOA_PIN15.GPIOx_DAT_REG == NULL){	
    		pr_err("======== %s:ioremap address error =======\n",__FUNCTION__);
    		return -1;
    	}
    	*(GPIOA_PIN15.GPIOx_CFG_REG) &= ~(0x1<<29);
    	*(GPIOA_PIN15.GPIOx_CFG_REG) &= ~(0x1<<30);
    	*(GPIOA_PIN15.GPIOx_CFG_REG) |= (0x1<<28);
    	return 0;
    }
    
    int led1_control(int pin,int status){   //status 0-off 1-on
    	switch(status){
    		case 0: //off
    			*(GPIOA_PIN15.GPIOx_DAT_REG) &= ~(0x1 << PIN_NUM(pin));
    			break;
    		case 1: //on
    			*(GPIOA_PIN15.GPIOx_DAT_REG) |= (0x1 << PIN_NUM(pin));
    			break;
    		default:
    			break;
    	}
    	return 0;
    }
    
    struct GPIOx_PIN *get_GPIOA_PIN15(void){
    	return &GPIOA_PIN15;
    }
    
    EXPORT_SYMBOL(get_GPIOL_PIN10);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    【orangepi_plus_led2.c】

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "orangepi_plus_leds.h"
    
    #define BASE_ADDR   (0x01f02c00)
    
    #define WHICH_CFG(pin_num)  \
            ((pin_num >= 0 && pin_num <= 7)   ?  0x00 : \
             (pin_num >= 8 && pin_num <= 15)  ?  0x04 : \
             (pin_num >= 15 && pin_num <= 21) ?  0x08 : 0x0c)
    
    int led2_init(int pin);
    int led2_control(int pin,int status);
    
    static struct GPIOx_PIN GPIOL_PIN10 = {
        .pin = (0 << 22)|(10),         //第0组第10pin(GPIOL_10)
        .gpio_init = led2_init,
        .gpio_control = led2_control,
        .next = NULL
    };
    
    
    int led2_init(int pin){
        int pin_num = PIN_NUM(pin);
        GPIOL_PIN10.GPIOx_CFG_REG = ioremap(BASE_ADDR + PIN_GROUP(pin)*0x24 + WHICH_CFG(pin_num),4);
        GPIOL_PIN10.GPIOx_DAT_REG = ioremap(BASE_ADDR + PIN_GROUP(pin)*0x24 + 0x10,4);
        if(GPIOL_PIN10.GPIOx_CFG_REG == NULL || GPIOL_PIN10.GPIOx_DAT_REG == NULL){
             pr_err("======= %s ioremap address error ========\n",__FUNCTION__);
             return -1;
         }
        *(GPIOL_PIN10.GPIOx_CFG_REG) &= ~(0x1<<29);
        *(GPIOL_PIN10.GPIOx_CFG_REG) &= ~(0x1<<30);
        *(GPIOL_PIN10.GPIOx_CFG_REG) |= (0x1<<28);
        return 0;
    }
    
    int led2_control(int pin,int status){   //status 0-off 1-on
        switch(status){
            case 0: //off
                *(GPIOL_PIN10.GPIOx_DAT_REG) &= ~(0x1<<PIN_NUM(pin));
                break;
            case 1: //on
                *(GPIOL_PIN10.GPIOx_DAT_REG) |= (0x1<<PIN_NUM(pin));
                break;
            default:
                break;
        }
        return 0;
    }
    
    
    struct GPIOx_PIN *get_GPIOL_PIN10(void){
        return &GPIOL_PIN10;
    }
    
    EXPORT_SYMBOL(get_GPIOL_PIN10);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    【led_driver.c】

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "orangepi_plus_leds.h"
    
    
    static int major = 0;
    static int error_check = 0;
    static int i = 0;
    static struct device *device_check;
    static unsigned int minor;
    static int kernel_buf = 0;
    
    static volatile int *GPIOx_base_address;
    static volatile int *PA_CFGx_REG;
    static volatile int *PA_DATA_REG;
    
    static struct GPIOx_PIN *led = NULL;
    
    static int gpio_open(struct inode *inode, struct file *file){
        printk("========== %s  %s  %d ===============\n",__FILE__,__FUNCTION__ ,__LINE__);
        dev_t dev = inode->i_rdev;  //应用层open文件的设备号
        minor = MINOR(dev);  //获取次设备号    0-1 
        switch (minor){
            case 0:
                led = get_GPIOA_PIN15();
                break;
            case 1:
                led = get_GPIOL_PIN10();
            default:
                break;
        }
        led->gpio_init(led->pin);
        return 0;
    }
    
    static ssize_t gpio_read (struct file *file, char __user *buf, size_t size, loff_t *offset){
        printk("========== %s  %s  %d =============\n",__FILE__,__FUNCTION__ ,__LINE__);
        return 0;
    }
    
    static ssize_t gpio_write (struct file *file, const char __user *buf, size_t size, loff_t * offset){
        printk("========== %s  %s  %d  =============\n",__FILE__,__FUNCTION__ ,__LINE__);
        if(copy_from_user(&kernel_buf,buf,4)){
            pr_err("copy_from_user error.(%s)\n",__FUNCTION__);
            return -1;
        }
        led->gpio_control(led->pin,kernel_buf);
        return 0;
    }
    static int gpio_close(struct inode *inode, struct file *file){
        printk("========== %s  %s  %d ===============\n",__FILE__,__FUNCTION__ ,__LINE__);
        iounmap(PA_DATA_REG);
        iounmap(PA_CFGx_REG);
        iounmap(GPIOx_base_address);
        return 0;
    }
    
    
    static struct file_operations gpio_ops = {
        .owner = THIS_MODULE,
        .open = gpio_open,
        .release = gpio_close,
        .write = gpio_write,
        .read = gpio_read,
    };
    
    static struct class *gpio_cls;
     
    static int __init gpio_driver_init(void){
        printk("============= %s  %s  %d ==================\n",__FILE__,__FUNCTION__ ,__LINE__);
        /* 1.注册字符设备 */
        major = register_chrdev(major,"GPIO",&gpio_ops);
    
        /* 2.创建设备节点 */
        gpio_cls = class_create(THIS_MODULE,"gpio_class");
        if(IS_ERR(gpio_cls)){
            error_check = PTR_ERR(gpio_cls);
            pr_err("class create failed.(error code:%d)\n",error_check);
            unregister_chrdev(major,"GPIO");
            return -1;
        }
    
        for(i=0;i<2;i++){
            device_check = device_create(gpio_cls,NULL,MKDEV(major,i),NULL,"led%d",i+1);
            if(IS_ERR(device_check)){
                error_check = PTR_ERR(device_check);
                pr_err("device create failed.(error code:%d)\n",error_check);
                class_destroy(gpio_cls);
                unregister_chrdev(major,"GPIO");
                return -1;
            }
        }
        return 0;
    }
    
    static void __exit gpio_driver_exit(void){
        printk("============= %s  %s  %d ==================\n",__FILE__,__FUNCTION__ ,__LINE__);
        iounmap(PA_DATA_REG);
        iounmap(PA_CFGx_REG);
        iounmap(GPIOx_base_address);
        device_destroy(gpio_cls, MKDEV(major,0));
        device_destroy(gpio_cls, MKDEV(major,1));
        class_destroy(gpio_cls);
        unregister_chrdev(major,"GPIO");
    }
    
    module_init(gpio_driver_init);
    module_exit(gpio_driver_exit);
    MODULE_LICENSE("GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

    【Makefile】

    # KERNEL_DIR = /usr/src/linux-headers-5.4.65-sunxi/
    KERNEL_DIR =/home/socket/Desktop/linux-5.4-orangepi
    
    # CROSS_COMPILE = arm-linux-gnueabihf-
    CROSS_COMPILE = arm-none-linux-gnueabihf-
    
    
    all:
    	make -C $(KERNEL_DIR) M=`pwd` modules
    	$(CROSS_COMPILE)gcc test_led_app.c -o test_led_app
    clean:
    	make -C $(KERNEL_DIR) M=`pwd` modules clean
    	rm -rf modules.order test_led_app
    
    OrangePiPlus_leds-y	:= led_driver.o orangepi_plus_led1.o orangepi_plus_led2.o
    obj-m	+= OrangePiPlus_leds.o
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

  • 相关阅读:
    为什么会性格内向?如何改变对内向性格的认知?
    Vue.$nextTick的原理是什么-vue面试进阶
    odoo 开发入门教程系列-添加修饰
    Java中的异常
    webpack--加载器(loader)
    VIO深蓝学院-第二章
    工业CT之三维重建技术
    【CSS预处理语言】less快速入门
    记一次 .Net+SqlSugar 查询超时的问题排查过程
    基于微信公众号的图书借阅平台设计与实现
  • 原文地址:https://blog.csdn.net/weixin_54429787/article/details/134007340