v67.03 鸿蒙内核源码分析(字符设备篇) | 字节为单位读写的设备 | 百篇博客分析OpenHarmony源码

原创
08/24 17:17
阅读数 3.8K

曾子曰:“君子以文会友,以友辅仁。” 《论语》:颜渊篇

在这里插入图片描述

百篇博客系列篇.本篇为:

v67.xx 鸿蒙内核源码分析(字符设备篇) | 字节为单位读写的设备

文件系统相关篇为:

什么是设备

设备(device): 是提供输入或输出功能的一种载体,其包括物理设备(对实际存在的物理硬件的抽象)例如,键盘是一种输入设备,硬盘是输入和输出设备。也包括虚拟设备(不依赖于特定的物理硬件,仅是内核自身提供的模拟/虚拟功能). 例如:虚拟控制台是输入和输出设备。每个设备都对应一个文件(设备文件),这些设备文件统一放在一个公共位置/dev下管理,通过设备文件(或称设备节点)来使用驱动程序操作设备。

设备按照存取方式的不同,分为两类:

  • 字符设备: 字符设备按字符处理。最明显的例子是键盘,其中每个键在设备上生成一个字符。还有鼠标,每一个动作或按钮点击都会发送一个字符到 /dev/mouse 设备。字符设备可理解为商品零售商,可以一件一件的卖,卖的细自然就卖的少.

  • 块设备:以更大的块读取数据的存储设备,如IDE硬盘也叫机械硬盘(/dev/hd)、SCSI硬盘也叫固态硬盘(/dev/sd) 和 CD-ROM (/dev/cdrom) 是块设备。输入输出与块设备的交互处理数据块,允许大量数据来回移动提高效率。块设备可理解为商品批发商,必须一箱一箱的卖,卖的粗但吞吐量大.

  • 字符设备还是块设备的定义属于内核设备访问层,与实际物理设备的特性无必然联系。设备访问层下面是驱动程序,存取方式取决于驱动程序是否支持,也可以同时支持两种方式访问物理设备,是块设备还是字符设备由使用者(往往是内核)决定。

鸿蒙系统中常见的字符设备如下:

mem | 内存设备

  /dev/mem         物理内存的全镜像。可以用来直接存取物理内存。
  /dev/kmem        内核看到的虚拟内存的全镜像。其访问的是虚拟内存而不是物理内存。
  /dev/null        空设备。也叫黑洞设备,任何写入都将被直接丢弃(但返回"成功");任何读取都将得到EOF(文件结束标志)。
  /dev/port        存取I/O端口
  /dev/zero        零流源。任何写入都将被直接丢弃(但返回"成功");任何读取都将得到无限多的二进制零流。
  /dev/full        满设备。任何写入都将失败,并把errno设为ENOSPC(没有剩余空间);任何读取都将得到无限多的二进制零流。
                   这个设备通常被用来测试程序在遇到磁盘无剩余空间错误时的行为。
  /dev/random      真随机数发生器。以背景噪声数据或硬件随机数发生器作为熵池,读取时会返回小于熵池噪声总数的随机字节。
                   若熵池空了,读操作将会被阻塞,直到收集到了足够的环境噪声为止。建议用于需要生成高强度密钥的场合。
                   [注意]虽然允许写入,但企图通过写入此文件来"预存"随机数是徒劳的,因为写入的数据对输出并无影响。
  /dev/urandom     伪随机数发生器。更快,但是不够安全。仅用于对安全性要求不高的场合。
                   即使熵池空了,读操作也不会被阻塞,而是把已经产生的随机数做为种子来产生新的随机数。
                   [注意]虽然允许写入,但企图通过写入此文件来"预存"随机数是徒劳的,因为写入的数据对输出并无影响。
  /dev/aio         异步I/O通知接口
  /dev/kmsg        任何对该文件的写入都将作为printk的输出;而读取则得到printk的输出缓冲区内容。
  • 在鸿蒙,/dev/mem 是一个字符设备, 源文件是 kernel/drivers/char/mem/src/mem.c, 这个设备文件是专门用来读写物理地址用的。里面的内容是所有物理内存的地址以及内容信息。通常只有root用户对其有读写权限。利用 mmap/dev/mem建立起直接读写系统物理内存的渠道。利用/dev/memmmap导出系统物理地址,免去了用户虚拟地址到内核逻辑地址的繁琐拷贝,提升效率。
    //文件和线性区的映射关系
    static ssize_t MemMap(struct file *filep, LosVmMapRegion *region)
    {
    #ifdef LOSCFG_KERNEL_VM
        size_t size = region->range.size;
        PADDR_T paddr = region->pgOff << PAGE_SHIFT;
        VADDR_T vaddr = region->range.base;
        LosVmSpace *space = LOS_SpaceGet(vaddr);
    
        if ((paddr >= SYS_MEM_BASE) && (paddr < SYS_MEM_END)) {
            return -EINVAL;
        }
    
        /* Peripheral register memory adds strongly ordered attributes */
        region->regionFlags |= VM_MAP_REGION_FLAG_STRONGLY_ORDERED;
    
        if (space == NULL) {
            return -EAGAIN;
        }//映射
        if (LOS_ArchMmuMap(&space->archMmu, vaddr, paddr, size >> PAGE_SHIFT, region->regionFlags) <= 0) {
            return -EAGAIN;
        }
    #else
        UNUSED(filep);
        UNUSED(region);
    #endif
        return 0;
    }
    // vfs 接口实现
      static const struct file_operations_vfs g_memDevOps = {
          MemOpen,  /* open */
          MemClose, /* close */
          MemRead,  /* read */
          MemWrite, /* write */
          NULL,      /* seek */
          NULL,      /* ioctl */
          MemMap,   /* mmap */
      #ifndef CONFIG_DISABLE_POLL
          NULL,      /* poll */
      #endif
          NULL,      /* unlink */
      };
      // 注册/dev/mem 的驱动程序
      int DevMemRegister(void)
      {
          return register_driver("/dev/mem", &g_memDevOps, 0666, 0); /* 0666: file mode */
      }
    

tty | 终端设备

TTY 是 Teletype 或 Teletypewriter 的缩写,原来是指电传打字机,后来这种设备逐渐键盘和显示器取代。不管是电传打字机还是键盘显示器,都是作为计算机的终端设备存在的,所以 TTY 也泛指计算机的终端(terminal)设备,一般分成以下几种

  • 串行端口终端(/dev/ttySn)
  • 伪终端(/dev/pty/)
  • 控制终端(/dev/tty)
  • 控制台终端(/dev/ttyn, /dev/console),将在后续 控制台篇 中详细说明

RTC | 时钟设备

/dev/rtc         实时时钟(Real Time Clock)
  • RTC(real-time clock)为操作系统中的实时时钟设备,为操作系统提供精准的实时时间和定时报警功能。当设备下电后,通过外置电池供电,RTC继续记录操作系统时间;设备上电后,RTC提供实时时钟给操作系统,确保断电后系统时间的连续性
  • RTC它可以用于产生年、月、日、时、分、秒等信息。目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。有些时钟芯片为了在主电源掉电时还可以工作,会外加电池供电,使时间信息一直保持有效。
  • 鸿蒙RTC设备API接口功能介绍
    RtcOpen         获取RTC设备驱动句柄
    RtcClose        释放RTC设备驱动句柄
    RtcReadTime     读RTC时间信息,包括年、月、星期、日、时、分、秒、毫秒
    RtcWriteTime    写RTC时间信息,包括年、月、星期、日、时、分、秒、毫秒
    RtcReadAlarm    读RTC报警时间信息
    RtcWriteAlarm   写RTC报警时间信息
    RtcRegisterAlarmCallback    注册报警超时回调函数
    RtcAlarmInterruptEnable     使能/去使能RTC报警中断
    RtcGetFreq      读RTC外接晶振频率
    RtcSetFreq      配置RTC外接晶振频率
    RtcReset        RTC复位
    RtcReadReg      读用户自定义寄存器
    RtcWriteReg     写用户自定义寄存器
    

I2C | 总线设备

/dev/i2c-0       第1个 I2C 适配器
...
/dev/i2c-n       第n-1个 I2C 适配器
  • I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单、双向二线制同步串行总线。
  • I2C以主从方式工作,通常有一个主设备和一个或者多个从设备,主从设备通过SDA(SerialData)串行数据线以及SCL(SerialClock)串行时钟线两根线相连,如下图所示。
  • I2C数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。数据传输以字节为单位,高位在前,逐个bit进行传输。
  • I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址,当主设备需要和某一个从设备通信时,通过广播的方式,将从设备地址写到总线上,如果某个从设备符合此地址,将会发出应答信号,建立传输。
  • I2C接口定义了完成I2C传输的通用方法集合,包括:
    • I2C控制器管理: 打开或关闭I2C控制器
    • I2C消息传输:通过消息传输结构体数组进行自定义传输
  • 鸿蒙I2C驱动API接口功能介绍
    I2cOpen     打开I2C控制器
    I2cClose    关闭I2C控制器
    I2cTransfer 自定义传输,I2c消息传输接口
    

SPI | 串行外设设备

  • SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线。
  • SPI是由Motorola公司开发,用于在主设备和从设备之间进行通信,常用于与闪存、实时时钟、传感器以及模数转换器等进行通信。
  • SPI以主从方式工作,通常有一个主设备和一个或者多个从设备。主设备和从设备之间一般用4根线相连,它们分别是:
    • SCLK – 时钟信号,由主设备产生;(Serial Clock)
    • MOSI – 主设备数据输出,从设备数据输入;(SPI Bus Master Output/Slave Input)
    • MISO – 主设备数据输入,从设备数据输出;(SPI Bus Master Input/Slave Output)。
    • CS – 片选,从设备使能信号,由主设备控制。(Chip select)
    • SPI主从设备连接示意图
  • SPI通信通常由主设备发起,通过以下步骤完成一次通信:
    • 通过CS选中要通信的从设备,在任意时刻,一个主设备上最多只能有一个从设备被选中。
    • 通过SCLK给选中的从设备提供时钟信号。
    • 基于SCLK时钟信号,主设备数据通过MOSI发送给从设备,同时通过MISO接收从设备发送的数据,完成通信。
  • 根据SCLK时钟信号的CPOL(Clock Polarity,时钟极性)和CPHA(Clock Phase,时钟相位)的不同组合,SPI有以下四种工作模式:
    • CPOL=0,CPHA=0 时钟信号idle状态为低电平,第一个时钟边沿采样数据。
    • CPOL=0,CPHA=1 时钟信号idle状态为低电平,第二个时钟边沿采样数据。
    • CPOL=1,CPHA=0 时钟信号idle状态为高电平,第一个时钟边沿采样数据。
    • CPOL=1,CPHA=1 时钟信号idle状态为高电平,第二个时钟边沿采样数据。
  • SPI接口定义了操作SPI设备的通用方法集合,包括:
    • SPI设备句柄获取和释放。
    • SPI读写: 从SPI设备读取或写入指定长度数据。
    • SPI自定义传输:通过消息传输结构体执行任意读写组合过程。
    • SPI设备配置:获取和设置SPI设备属性。
  • 鸿蒙SPI驱动API接口功能介绍
    SpiOpen     获取SPI设备句柄
    SpiClose    释放SPI设备句柄
    SpiRead     读取指定长度的数据
    SpiWrite    写入指定长度的数据
    SpiTransfer SPI数据传输接口
    SpiSetCfg   根据指定参数,配置SPI设备
    SpiGetCfg   获取SPI设备配置参数
    

UART | 串口设备

/dev/ttyS0       第1个UART串口(Serial port)
...
/dev/ttyS200     第199个UART串口
  • 串口设备是终端设备的一种,采用 /dev/ttySn 或 /dev/tts/n 来表示,分别对应于windows系统下的COM1、COM2等。若要向一个端口发送数据,可以在命令行上把标准输出重定向到这些特殊文件名上即可,例如,在命令行提示符下键入:echo test > /dev/ttyS1会把单词”test”发送到连接在ttyS1(COM2)端口的设备上。

  • UART(Universal Asynchronous Receiver/Transmitter)通用异步收发传输器,UART 作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。是在应用程序开发过程中使用频率最高的数据总线。

  • UART应用比较广泛,常用于输出打印信息,也可以外接各种模块,如GPS、蓝牙等。

  • UART 串口的特点是将数据一位一位地顺序传送,只要 2 根传输线就可以实现双向通信,一根线发送数据的同时用另一根线接收数据。UART 串口通信有几个重要的参数,分别是波特率、起始位、数据位、停止位和奇偶检验位,对于两个使用 UART 串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。UART 串口传输的数据格式如下图所示:

    • 起始位:表示数据传输的开始,电平逻辑为 “0” 。
    • 数据位:可能值有 5、6、7、8、9,表示传输这几个 bit 位数据。一般取值为 8,因为一个 ASCII 字符值为 8 位。
    • 奇偶校验位:用于接收方对接收到的数据进行校验,校验 “1” 的位数为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性,使用时不需要此位也可以。
    • 停止位: 表示一帧数据的结束。电平逻辑为 “1”。
    • 波特率:串口通信时的速率,它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为每秒比特数 bit/s(bps)。常见的波特率值有 4800、9600、14400、38400、115200等,数值越大数据传输的越快,波特率为 115200 表示每秒钟传输 115200 位数据。
  • UART接口定义了操作UART端口的通用方法集合,包括获取、释放设备句柄、读写数据、获取和设置波特率、获取和设置设备属性。

  • 鸿蒙UART驱动API接口功能介绍

    UartOpen  UART获取设备句柄
    UartClose UART释放设备句柄
    UartRead  从UART设备中读取指定长度的数据
    UartWrite 向UART设备中写入指定长度的数据
    UartGetBaud   UART获取波特率
    UartSetBaud   UART设置波特率
    UartGetAttribute  UART获取设备属性
    UartSetAttribute  UART设置设备属性
    UartSetTransMode  UART设置传输模式
    

LCD | 屏显设备

/dev/lcd         液晶(LCD)显示屏
  • LCD(Liquid Crystal Display)液晶显示驱动,对LCD进行上电,并通过接口初始化LCD内部寄存器,使LCD正常工作。Display驱动模型基于HDF( Hardware Driver Foundation)驱动框架开发,实现跨OS、跨平台,为LCD硬件提供上下电功能、发送初始化序列功能,使LCD进入正常的工作模式,显示芯片平台侧的图像数据.
  • 基于HDF驱动框架的Display驱动模型

Touchscreen | 触摸设备

  • Touch(触摸芯片)是 UI 设计中进行人机交互重要的一部分,一个完整的 UI 设计应该包括输入信息和输出信息,LCD 等屏幕设备负责显示输出,那么 Touch 设备就负责触点信息采集作为信息输入。

  • Touch 设备与主机通讯一般都是采用 I2C 总线协议来进行数据交互,所以一个 Touch 设备,就是一个标准的 I2C 从设备,而且为了提高接收 Touch 数据的实时性,触摸芯片都会提供中断支持,当有触摸事件(抬起,按下,移动)发生时,会触发中断通知 MCU 有触摸事件。主机可以通过中断回调函数去读取触摸点信息。

  • Touchscreen器件的硬件接口相对简单,根据PIN脚的属性,可以简单分为如下三类:

    • 电源接口
    • IO控制接口
    • 通信接口
  • Touchscreen驱动用于驱动触摸屏使其正常工作,该驱动主要完成如下工作:对触摸屏驱动IC进行上电、配置硬件管脚并初始化其状态、注册中断、配置通信接口(I2C或SPI)、设定input相关配置、下载及更新固件等操作。

  • 鸿蒙基于input驱动模型开发touchscreen器件驱动。Input驱动模型基于HDF驱动框架、PLATFORM接口、OSAL接口进行开发,向上对接规范化的驱动接口HDI(Hardware Driver Interface)层,通过Input-HDI层对外提供硬件能力,即上层input service可以通过HDI接口层获取相应的驱动能力,进而操控touchscreen等输入设备。

  • 基于HDF驱动框架的input驱动模型

sensor | 传感设备

/dev/biometric/sensor0/fingerprint  第1个设备的第1个指纹传感器
/dev/biometric/sensor0/iris         第1个设备的第1个虹膜传感器
/dev/biometric/sensor0/retina       第1个设备的第1个视网膜传感器
/dev/biometric/sensor0/voiceprint   第1个设备的第1个声波传感器
/dev/biometric/sensor0/facial       第1个设备的第1个面部传感器
/dev/biometric/sensor0/hand         第1个设备的第1个手掌传感器
/dev/biometric/sensor1/fingerprint  第2个设备的第1个指纹传感器
/dev/biometric/sensor2/fingerprint  第3个设备的第1个指纹传感器
  • Sensor(传感器)是物联网重要的一部分,Sensor 之于物联网”就相当于“眼睛之于人类"。人类如果没有了眼睛就看不到这大千的花花世界,对于物联网来说也是一样。

  • 如今随着物联网的发展,已经有大量的 Sensor 被开发出来供开发者选择了,如:加速度计(Accelerometer)、磁力计(Magnetometer)、陀螺仪(Gyroscope)、气压计(Barometer/pressure)、湿度计(Humidometer)等。这些传感器,世界上的各大半导体厂商都有生产,虽然增加了市场的可选择性,同时也加大了应用程序开发的难度。因为不同的传感器厂商、不同的传感器都需要配套自己独有的驱动才能运转起来,这样在开发应用程序的时候就需要针对不同的传感器做适配,自然加大了开发难度。

  • 为了降低应用开发的难度,增加传感器驱动的可复用性, 鸿蒙Sensor(传感器)驱动模块为上层Sensor服务系统提供稳定的Sensor基础能力API,包括Sensor列表查询、Sensor启停、Sensor订阅及去订阅,Sensor参数配置等功能;基于HDF(Hardware Driver Foundation)驱动框架开发的Sensor驱动模型,实现跨操作系统迁移,器件差异配置等功能。

  • 鸿蒙Sensor驱动模型图

watchdog | 看门狗

/dev/watchdog    看门狗(CONFIG_WATCHDOG)
/dev/watchdogs/0 第一只看门狗
...
/dev/watchdogs/n 第n-1只看门狗
  • 硬件看门狗(watchdog timer)是一个定时器,其定时输出连接到电路的复位端。在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗。

  • 当看门狗启动后,计数器开始自动计数,在计数器溢出前如果没有被复位,计数器溢出就会对 CPU 产生一个复位信号使系统重启(俗称 “被狗咬”)。系统正常运行时,需要在看门狗允许的时间间隔内对看门狗计数器清零(俗称“喂狗“),不让复位信号产生。如果系统不出问题,程序能够按时“喂狗”。一旦程序跑飞,没有“喂狗”,系统“被咬” 复位。

  • 鸿蒙看门狗 API接口功能介绍

    WatchdogOpen  打开看门狗设备
    WatchdogClose 关闭看门狗设备
    WatchdogStart 启动看门狗
    WatchdogStop  停止看门狗
    WatchdogSetTimeout  设置看门狗超时时间
    WatchdogGetTimeout  获取看门狗超时时间
    WatchdogGetStatus 获取看门狗状态
    WatchdogFeed      清除看门狗定时器(喂狗)
    

WLAN | 无线网络设备

  • WLAN是基于HDF(Hardware Driver Foundation)驱动框架开发的模块,该模块可实现跨操作系统迁移,自适应器件差异,模块化拼装编译等功能。各WLAN厂商驱动开发人员可根据WLAN模块提供的向下统一接口适配各自的驱动代码,实现如下能力:建立/关闭WLAN热点、扫描、关联WLAN热点等;对HDI层向上提供能力如下:设置MAC地址、设置发射功率、获取设备的MAC地址等。
  • WLAN框架
  • WLAN模块有三部分对外开放的API接口
    • 对HDI层提供的能力接口。
    • 驱动直接调用WLAN模块能力接口。
    • 提供给各厂商实现的能力接口。

百篇博客分析.深挖内核地基

  • 给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
  • 与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。

按功能模块:

基础工具 加载运行 进程管理 编译构建
双向链表 位图管理 用栈方式 定时器 原子操作 时间管理 ELF格式 ELF解析 静态链接 重定位 进程映像 进程管理 进程概念 Fork 特殊进程 进程回收 信号生产 信号消费 Shell编辑 Shell解析 编译环境 编译过程 环境脚本 构建工具 gn应用 忍者ninja
进程通讯 内存管理 前因后果 任务管理
自旋锁 互斥锁 进程通讯 信号量 事件控制 消息队列 内存分配 内存管理 内存汇编 内存映射 内存规则 物理内存 总目录 调度故事 内存主奴 源码注释 源码结构 静态站点 时钟任务 任务调度 任务管理 调度队列 调度机制 线程概念 并发并行 CPU 系统调用 任务切换
文件系统 硬件架构
文件概念 文件系统 索引节点 挂载目录 根文件系统 字符设备 VFS 文件句柄 管道文件 汇编基础 汇编传参 工作模式 寄存器 异常接管 汇编汇总 中断切换 中断概念 中断管理

百万汉字注解.精读内核源码

WeHarmony/kernel_liteos_a_note

鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。

展开阅读全文
打赏
1
3 收藏
分享
加载中
更多评论
打赏
0 评论
3 收藏
1
分享
返回顶部
顶部