About 23 min

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

在这里插入图片描述

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

v67.xx 鸿蒙内核源码分析(字符设备篇) | 绝大多数设备都是这类

下载 >> 离线文档.鸿蒙内核源码分析(百篇博客分析.挖透鸿蒙内核).pdf

文件系统相关篇为:

什么是设备

设备(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的输出缓冲区内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 在鸿蒙,/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 */
      }
    
    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

tty | 终端设备

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

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

RTC | 时钟设备

/dev/rtc         实时时钟(Real Time Clock)
1
  • 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     写用户自定义寄存器
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

I2C | 总线设备

/dev/i2c-0       第1个 I2C 适配器
...
/dev/i2c-n       第n-1个 I2C 适配器
1
2
3
  • 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消息传输接口
    
    1
    2
    3

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设备配置参数
    
    1
    2
    3
    4
    5
    6
    7

UART | 串口设备

/dev/ttyS0       第1个UART串口(Serial port)
...
/dev/ttyS200     第199个UART串口
1
2
3
  • 串口设备是终端设备的一种,采用 /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设置传输模式
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

LCD | 屏显设备

/dev/lcd         液晶(LCD)显示屏
1
  • 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个指纹传感器
1
2
3
4
5
6
7
8
  • 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只看门狗
1
2
3
4
  • 硬件看门狗(watchdog timer)是一个定时器,其定时输出连接到电路的复位端。在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗。

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

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

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

WLAN | 无线网络设备

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

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

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

按功能模块:

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

四大码仓中文注解 . 定期同步官方代码

WeHarmony/kernel_liteos_a_note

关注不迷路.代码即人生

鸿蒙内核源码分析

QQ群 790015635 | 入群密码 666

鸿蒙研究站( weharmonyos ) | 每天死磕一点点,原创不易,欢迎转载,请注明出处。若能支持点赞则更佳,感谢每一份支持。