👹
Carlos's Tech Blog
  • 🧔ECUs
    • ZYNQ_Documents
      • [ZYNQ] 构建ZYNQ的BSP工程
      • [ZYNQ] 启动流程
      • [ZYNQ] Secure Boot Flow
      • [ZYNQ] Provisioning Guideline
      • [ZYNQ] Decrypting Partition by the Decrypt Agent Using PUF key
      • [ZYNQ] enabling the cryptsetup on ramdisk
      • [ZYNQ] Encrypt external files based on file system using PUF key
      • [ZYNQ] Loading an Encrypted Linux kernel at U-Boot with a KUP Key
      • [ZYNQ] cross-compile the cryptsetup on Xilinx ZYNQ aarch64 platform
      • [ZYNQ] Linux Linaro系统镜像制作SD卡启动
    • S32G_Documents
      • [S32G] Going through the s32g hard/soft platform
      • [S32G] S32g247's Secure Boot using HSE firmware
        • S32g2 HSE key config
        • How S32g verify secure boot image
        • S32g secure boot signature generation
        • How to download and build S32g Secure boot image
        • [S32G] OTA with Secure Boot
    • RT117x_Documents
      • [RT-117x]IMX RT1170 Provisioning Guideline
      • [RT-117x] Going through the MX-RT1170 hard/soft platform
      • [RT-117x] i.MX-RT1170's Secure Boot
        • [RT-117x]Signing image with the HSM (SignServer)
    • LS104x_Documents
      • [LS104x] bsp project
      • [LS104x] boot flow
      • [LS104x] secure boot
      • [LS104x] Application Note, Using the PKCS#11 in TCU platform
      • [LS104x] 使用ostree更新rootfs
      • [LS104x] ostree的移植
      • [LS104x] Starting with Yocto
      • [LS104x] 使用FIT的kernel格式和initramfs
    • IMX6/8_Documents
      • [IMX6] Defining A U-Boot Command
      • NXP IMX6 嵌入式板子一些笔记
      • NXP-imx6 initialization
    • Vehicle_Apps
      • [SecOC] Tree
        • [SecOC] SecOC Freshness and MAC Truncation
  • 😾TECH
    • Rust Arm OS
      • ARMv7m_Using_The_RUST_Cross_Compiler
    • ARM
      • ARM-v7-M
        • 01_ARMv7-M_处理器架构技术综述
        • 02_ARMv7-M_编程模型与模式
        • 03_ARMv7-M_存储系统结构
        • 04_ARMv7-M_异常处理及中断处理
      • ARM-v8-A
        • 02_ARMv8_基本概念
        • 03_ARMv8_指令集介绍_加载指令集和存储指令集
        • 04_ARMv8_指令集_运算指令集
        • 05_ARMv8_指令集_跳转_比较与返回指令
        • 06_ARMv8_指令集_一些重要的指令
        • 0X_ARMv8_指令集_基于汇编的UART驱动
        • 07_ARMv8_汇编器Using as
        • 08_ARMv8_链接器和链接脚本
        • 09_ARMv8_内嵌汇编(内联汇编)Inline assembly
        • 10_ARMv8_异常处理(一) - 入口与返回、栈选择、异常向量表
        • 11_ARMv8_异常处理(二)- Legacy 中断处理
        • 12_ARMv8_异常处理(三)- GICv1/v2中断处理
        • 13_ARMv8_内存管理(一)-内存管理要素
        • 14_ARMv8_内存管理(二)-ARM的MMU设计
        • 15_ARMv8_内存管理(三)-MMU恒等映射及Linux实现
        • 16_ARMv8_高速缓存(一)cache要素
        • 17_ARMv8_高速缓存(二)ARM cache设计
        • 18_ARMv8_高速缓存(三)多核与一致性要素
        • 19_ARMv8_TLB管理(Translation Lookaside buffer)
        • 20_ARMv8_barrier(一)流水线和一致性模型
        • 21_ARMv8_barrier(二)内存屏障案例
      • ARM Boot Flow
        • 01_Embedded_ARMv7/v8 non-secure Boot Flow
        • 02_Embedded_ARMv8 ATF Secure Boot Flow (BL1/BL2/BL31)
        • 03_Embedded_ARMv8 BL33 Uboot Booting Flow
      • ARM Compiler
        • Compiler optimization and the volatile keyword
      • ARM Development
        • 在MACBOOK上搭建ARMv8架构的ARM开发环境
        • Starting with JLink debugger or QEMU
    • Linux
      • Kernel
        • 0x01_LinuxKernel_内核的启动(一)之启动前准备
        • 0x02_LinuxKernel_内核的启动(二)SMP多核处理器启动过程分析
        • 0x21_LinuxKernel_内核活动(一)之系统调用
        • 0x22_LinuxKernel_内核活动(二)中断体系结构(中断上文)
        • 0x23_LinuxKernel_内核活动(三)中断体系结构(中断下文)
        • 0x24_LinuxKernel_进程(一)进程的管理(生命周期、进程表示)
        • 0x25_LinuxKernel_进程(二)进程的调度器的实现
        • 0x26_LinuxKernel_设备驱动(一)综述与文件系统关联
        • 0x27_LinuxKernel_设备驱动(二)字符设备操作
        • 0x28_LinuxKernel_设备驱动(三)块设备操作
        • 0x29_LinuxKernel_设备驱动(四)资源与总线系统
        • 0x30_LinuxKernel_设备驱动(五)模块
        • 0x31_LinuxKernel_内存管理(一)物理页面、伙伴系统和slab分配器
        • 0x32_LinuxKernel_内存管理(二)虚拟内存管理、缺页与调试工具
        • 0x33_LinuxKernel_同步管理_原子操作_内存屏障_锁机制等
        • 01_LinuxDebug_调试理论和基础综述
      • Userspace
        • Linux-用户空间-多线程与同步
        • Linux进程之间的通信-管道(上)
        • Linux进程之间的通信-管道(下)
        • Linux进程之间的通信-信号量(System V)
        • Linux进程之间的通信-内存共享(System V)
        • Linux进程之间的通信-消息队列(System V)
        • Linux应用调试(一)方法、技巧和工具 - 综述
        • Linux应用调试(二)工具之coredump
        • Linux应用调试(三)工具之Valgrind
        • Linux机制之内存池
        • Linux机制之对象管理和引用计数(kobject/ktype/kset)
        • Linux机制copy_{to, from}_user
        • Linux设备树 - DTS语法、节点、设备树解析等
        • Linux System : Managing Linux Services - inittab & init.d
        • Linux System : Managing Linux Services - initramfs
      • Kernel Examples
        • Linux Driver - GPIO键盘驱动开发记录_OMAPL138
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read、write
        • 基于OMAPL138的字符驱动_GPIO驱动AD9833(三)之中断申请IRQ
        • Linux内核调用SPI驱动_实现OLED显示功能
        • Linux内核调用I2C驱动_驱动嵌套驱动方法MPU6050
    • OPTEE
      • 01_OPTEE-OS_基础之(一)功能综述、简要介绍
      • 02_OPTEE-OS_基础之(二)TrustZone和ATF功能综述、简要介绍
      • 03_OPTEE-OS_系统集成之(一)编译、实例、在QEMU上执行
      • 05_OPTEE-OS_系统集成之(三)ATF启动过程
      • 06_OPTEE-OS_系统集成之(四)OPTEE镜像启动过程
      • 07_OPTEE-OS_系统集成之(五)REE侧上层软件
      • 08_OPTEE-OS_系统集成之(六)TEE的驱动
      • 09_OPTEE-OS_内核之(一)ARM核安全态和非安全态的切换
      • 10_OPTEE-OS_内核之(二)对安全监控模式的调用的处理
      • 11_OPTEE-OS_内核之(三)中断与异常的处理
      • 12_OPTEE-OS_内核之(四)对TA请求的处理
      • 13_OPTEE-OS_内核之(五)内存和cache管理
      • 14_OPTEE-OS_内核之(六)线程管理与并发
      • 15_OPTEE-OS_内核之(七)系统调用及IPC机制
      • 16_OPTEE-OS_应用之(一)TA镜像的签名和加载
      • 17_OPTEE-OS_应用之(二)密码学算法和安全存储
      • 18_OPTEE-OS_应用之(三)可信应用的开发
      • 19_OPTEE-OS_应用之(四)安全驱动开发
      • 20_OPTEE-OS_应用之(五)终端密钥在线下发系统
    • Binary
      • 01_ELF文件_目标文件格式
      • 02_ELF文件结构_浅析内部文件结构
      • 03_ELF文件_静态链接
      • 04_ELF文件_加载进程虚拟地址空间
      • 05_ELF文件_动态链接
      • 06_Linux的动态共享库
      • 07_ELF文件_堆和栈调用惯例以ARMv8为例
      • 08_ELF文件_运行库(入口、库、多线程)
      • 09_ELF文件_基于ARMv7的Linux系统调用原理
      • 10_ELF文件_ARM的镜像文件(.bin/.hex/.s19)
    • Build
      • 01_Script_makefile_summary
    • Rust
      • 02_SYS_RUST_文件IO
    • Security
      • Crypto
        • 1.0_Security_计算机安全概述及安全需求
        • 2.0_Security_随机数(伪随机数)
        • 3.0_Security_对称密钥算法加解密
        • 3.1_Security_对称密钥算法之AES
        • 3.2_Security_对称密钥算法之MAC(CMAC/HMAC)
        • 3.3_Security_对称密钥算法之AEAD
        • 8.0_Security_pkcs7(CMS)_embedded
        • 9.0_Security_pkcs11(HSM)_embedded
      • Tools
        • Openssl EVP to implement RSA and SM2 en/dec sign/verify
        • 基于Mac Silicon M1 的OpenSSL 编译
        • How to compile mbedtls library on Linux/Mac/Windows
    • Embedded
      • eMMC启动介质
  • 😃Design
    • Secure Boot
      • JY Secure Boot Desgin
    • FOTA
      • [FOTA] Module of ECUs' FOTA unit design
        • [FOTA] Tech key point: OSTree Deployment
        • [FOTA] Tech key point: repositories role for onboard
        • [FOTA] Tech key point: metadata management
        • [FOTA] Tech key point: ECU verifying and Decrpting
        • [FOTA] Tech key point: time server
      • [FOTA] Local-OTA for Embedded Linux System
    • Provisioning
      • [X-Shield] Module of the Embedded Boards initialization
    • Report
由 GitBook 提供支持
在本页
  • Linux GPIO键盘驱动开发记录_OMAPL138
  • 0.开发准备
  • 1.键盘的接线图
  • 3 编译内核或者以模块形式加载
  • 4 应用程序开发
  • 5 调试结果
  • 附录:源程序
  1. TECH
  2. Linux
  3. Kernel Examples

Linux Driver - GPIO键盘驱动开发记录_OMAPL138

https://github.com/carloscn/blog/issues/32

上一页Kernel Examples下一页基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl

最后更新于1年前

Linux GPIO键盘驱动开发记录_OMAPL138

Linux基本配置完毕了,这几天开始着手Linux驱动的开发,从一个最简单的键盘驱动开始,逐步的了解开发驱动的过程有哪些。看了一下Linux3.3内核文件下的driver目录,点开里面的C文件,感觉底层的Linux驱动机制还是很复杂的,还需要一段漫长时间的学习。现在开发的也不能说是叫做驱动,也只能说是驱动的应用,我们学习驱动也从应用逐步开始,往里面深入吧。

0.开发准备

  • 内核源文件(当时我们编译内核时候的目录,很重要,编译驱动的时候需要依赖这些内核源文件)

  • Makefile文件(编译驱动的Makefile文件)

  • 驱动源程序

  • 应用程序(有main函数的)

1.键盘的接线图

我们主要使用USER0和USER1 KEY,两个按键,完成Linux GPIO键盘驱动开发。从图中可以看出GPIO0_6和GPIO6_1主要采集键盘按下的信息。

2. key.c驱动文件

驱动结构可以看上图,主要是在注册Linux内核设备,我们使用platform_device进行内核注册。我们从思维到图上看,从后往前的顺序进行一个一个定义。

2.1 gpio_key_buttons结构体

**Linux提供的标准结构体,在#include <linux/gpio_key.h>头文件中。**这里面就要定义按键的行为信息和指定GPIO口,这个是和上面的硬件原理图打交道的一个结构体。以下是和这个结构体有关的定义。

#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <mach/da8xx.h>						// 板子的头文件

#define             OMAPL138_KEYS_BEBOUNCE          10
#define             OMAPL138_GPIO_KEYS_POLL_MS      200

#define             OMAPL138_USER_KEY0              GPIO_TO_PIN( 0,6 )
#define             OMAPL138_USER_KEY1              GPIO_TO_PIN( 6,1 )

static const short  omapl138_user_key_pins[] = {
    DA850_GPIO0_6, DA850_GPIO6_1, -1
};

static struct   gpio_keys_button omapl138_user_keys[]   =   {
    [0] = {
        .type               =   EV_KEY,
        .active_low         =   1,
        .wakeup             =   0,
        .debounce_interval  =   OMAPL138_KEYS_BEBOUNCE,
        .code               =   KEY_PROG1,
        .desc               =   "user_key0",
        .gpio               =   OMAPL138_USER_KEY0
    },
    [1] = {
        .type               =   EV_KEY,
        .active_low         =   1,
        .wakeup             =   0,
        .debounce_interval  =   OMAPL138_KEYS_BEBOUNCE,
        .code               =   KEY_PROG2,
        .desc               =   "user_key1",
        .gpio               =   OMAPL138_USER_KEY1
    }
};

在其他的平台可能GPIO口的定义不同,我用的是OMAPL138,使用的内核文件是OMAPL138提供的,他的里面定义了da8xx.h定义了相关GPIO的宏定义,你需要找到你自己平台GPIO结构体的定义,修改这个宏定义即可。这个结构提十分具备可读性,一看就能看懂了,其中的.code就是我们写应用程序的时候,按键读出来的值,就可以判断了。

2.2 gpio_keys_platform_data结构体

platform_data结构体顾名思义,基本上就是和我们整个驱动开发数据相关的。

static struct   gpio_keys_platform_data     omapl138_user_keys_pdata    =   {
    .buttons                =   omapl138_user_keys,
    .nbuttons               =   ARRAY_SIZE( omapl138_user_keys )
};

里面的成员,.buttons就是刚才我们定义gpio_keys_button数组,.nbuttons就是长度,我们使用宏函数ARRAY_SIZE完成取值。

2.3 platform_device结构体

思维导图在向前,我们就需要定义platform_device结构体了,这个结构体就是要和Linux内核打交道的结构提了。里面有设备名称.name,.id,dev。

static void     omapl138_user_keys_release( struct device *dev ) {

}
static struct   platform_device     omapl138_user_keys_device   =   {
        // you can find mount root by command "dmesg | grep gpio-keys ";
        // cat /proc/bus/input/devices  look the where the device is.
        .name               =   "gpio-keys",
        .id                 =   1,
        .dev                =   {
                            .platform_data  =   &omapl138_user_keys_pdata,
                            .release    =   omapl138_user_keys_release,
        },
};

.name中当驱动程序向linux注册后通过dmesg | grep gpio-keys命令就可以看到这个名字,在内核调试输出中可以看到。.id设备挂载节点的id,同样的设备id不能重复。.dev给定data和release函数(可以为空)。

2.4 初始化函数和退出函数

最后就是初始化函数和退出函数,最终要的就是resigster这个函数了。向内核注册设备。

static int __init omapl138_user_keys_init( void )
{
    int reg;
    reg     =   platform_device_register( &omapl138_user_keys_device );
    if( reg ) {
        pr_warning( "Could not register baseboard GPIO tronlong keys!" );
    }else {
        printk( KERN_INFO "User keys register successful!" );
    }
    return reg;
}

static void __exit omapl138_user_keys_exit( void )
{
    platform_device_unregister( &omapl138_user_keys_device );
    printk( KERN_INFO   "user keys unregister ! \n" );
}


module_init( omapl138_user_keys_init );
module_exit( omapl138_user_keys_exit );

MODULE_DESCRIPTION( "user keys platform driver," );
MODULE_AUTHOR("Carlos Wei");
MODULE_LICENSE( "GPL" );

当我们使用insmod name.ko的时候,自动调用module_init()里面写入的函数了,当我们rmmod name.ko的时候,自动调用module_exit()里面写入的函数。

3 编译内核或者以模块形式加载

我们可以把这个驱动程序静态编译到内核代码树中也可以以模块的形式加载到内核中,我建议一开始开发的时候使用模块的形式加入到内核中,等着编译成熟之后,在编译到内核里面。

3.1 编译

制定内核源码文件的路径,写好Makefile文件,编译后上传到目标板的任意路径。

3.1.1 Makefile文件

ifneq ($(KERNELRELEASE),)

obj-m := key.o

else

all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-arago-linux-gnueabi-

clean:
	rm -rf *.ko *.o *.mod.o *.mod.c *.symvers  modul* .button.* .tmp_versions

KDIR=/usr/src/linux-headers-3.3.0-omapl138

endif

这里比较重要的:

  • KDIR=? 这个位置就是你Linux源码文件的路径,(内核事先编译一定要正确)

  • CROSS_COMPILE= 这个指定交叉编译器,我的交叉编译器名字比较怪异arm-arago-linux-gnueabi-

3.1.2 Make一下生成key.ko

编译完成之后,通过ls命令,查看生成了key.ko文件。

通过scp或者ftp把key.ko文件传输到目标板子上。

3.2 加载驱动与查看挂载节点

1) insmod key.ko

2) 通过dmesg查看内核输出是否成功

3)通过cat /proc/bus/input/devices命令查看挂在详情

重点是event1,一会儿我们编辑应用程序需要打开/dev/input/event1这个设备节点进行对GPIO键盘操作。

4)测试键盘输入

在没有编写应用程序的时候,我们也可以通过简单的方法对GPIO键盘进行一个简单的测试。GPIO挂载节点是Handlers = event1 则输入:

cat /dev/input/event1

然后我们按键盘,如果当我们点击键盘的时候 终端输出乱码则代表我们的驱动是编写成功的。

5) 解挂驱动

如果我们不需要驱动了,则需要进行解挂驱动:

rmmod key.ko

也可以通过dmesg命令查看内核输出日志。

4 应用程序开发

建立文件:key_app.c


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <time.h>
#include <fcntl.h>
#include <linux/input.h>

int main( int argc, char **argv  )
{
    int key_state;
    int fd;
    int ret;
    int code;
    struct input_event buf;

    fd = open( "/dev/input/event1", O_RDONLY );
    if( fd  < 0 ) {
        printf( "Open GPIO_keys failed!!\n" );
        return -1;
    }

    printf( "Open GPIO keys successful! \n" );
    while( 1 ) {
        ret = read( fd, &buf, sizeof( struct input_event ) );
        if( ret <= 0 ) {
            printf( "read failed! \n" );
            return -1;
        }
        code = buf.code;
        key_state   =   buf.value;
        printf("wait...... \n");
        switch( code ) {
            case KEY_PROG1:

                code = '1';
                printf( "KEY1 state = %d\n", key_state );
                break;
            case KEY_PROG2:

                code = '2';
                printf( "KEY2 state = %d\n", key_state );
                break;
        }


    }
    printf("key test finished.\n");
    close(fd);
    return 0;
}

一个非常简单的程序,就是输出案件之。我们编译它:

arm-arago-linux-gnueabi-gcc key_app.c -o key_app.o

生成的key_app.o文件放入目标板的Linux目录,然后运行即可。

5 调试结果

通过按键就可以输出这些值,意味着我们的驱动开发成功了。

附录:源程序

链接: https://pan.baidu.com/s/1pNcEBEj 密码: xpsq


😾