EZVIZ BD-2402B1后台工程和SNES9X端口

admin 2022年4月15日10:27:47EZVIZ BD-2402B1后台工程和SNES9X端口已关闭评论34 views字数 16357阅读54分31秒阅读模式

备注

原文地址:https://back.engineering/20/01/2022/

简介

EZVIZ BD-2402B1是一个使用hisilicon SoC的监控系统DVR。我的目标是重新利用硬件来运行一个自定义的snes9x分叉。为了实现这一目标,需要进行大量的逆向工程和学习。这不仅是一个基于硬件的项目,也是一个需要大量软件的项目。

我决定尝试在这个硬件上运行我自己的snes9x fork,而不是做与安全有关的研究,因为我看了许多详细介绍使用hisilicon SoC的产品的严重安全漏洞的文章。我觉得已经有足够多的人在关注这些板块上运行的软件,再多些关注也不会增加任何价值。。此外,hisilicon最近在美国被禁止使用,因此使用这些摄像系统的人正在减少。我发现一个新的漏洞的机会与不断下降的影响(使用量)相结合,暗示了对硬件的重新利用。

因此,我决定利用这个机会来扩展我的硬件黑客技能,并熟悉我用了很多年的snes模拟器(snes9x)。

硬件详细信息

首先,应该对电路板上的元件/外设进行描述。只有对移植snes9x和在设备上获得代码执行感兴趣的组件才会被描述,其余的组件在这里托管处理器数据表PDF中都有详细的记录

EZVIZ BD-2402B1后台工程和SNES9X端口

电路板上的引脚头是NC(无连接),因为它们的电阻已经从PCB上移除。有些引脚不是NC,如地线,因为它们直接连接到一个地线。

值得注意的组成部分

其他I/O的完整、详细的清单可以在Brief数据表中找到(该表是公开的)。 你可以在这里找到该PDF的转载版本。

SoC - Hi3520DV300

"Hi3520D V300是一款针对多通道高清(1080p/720p)或标清(D1/960H)DVR的专业SoC。Hi3520D V300提供了一个ARM A7处理器,一个高性能的H.264视频编码/解码引擎,一个具有各种复杂图形处理算法的高性能视频/图形处理引擎,HDMI/VGA高清输出,以及各种外围接口。这些特点使Hi3520D V300能够为客户的产品提供高性能、高画质、低成本的模拟HD/SDI解决方案,同时降低eBOM成本"

EZVIZ BD-2402B1后台工程和SNES9X端口

TDE - Two Dimensional 引擎

TDE是在arm处理器外部运行的图形处理引擎。这允许在不使用主处理器的情况下减轻复杂的位图操作。。这个引擎在TDE程序员用户手册中有所记载。该引擎有助于加速操作,例如将位图复制到另一个位图中,并考虑间距(每水平像素行的字节数)。它还可以进行像素格式转换(正如你在后面的文章中所看到的那样,这是必须的)。位图的缩放也是在这个引擎中完成的。

UART - 通用异步接收发送器

当我收到它时,主板上的uboot将UART波特率配置为115200波特。发送引脚完好无损/已连接。我能够很容易地中断uboot,这允许我为内核cmdline (bootargs)设置rdinit=/bin/sh。

我在设置init=/bin/sh时,有一段时间没有看到效果,因为我没有意识到内核在使用initramfs/initrd配置。整个文件系统包含在内核映像中,未被压缩并在内存中显示。这也意味着,如果不重新编译整个Linux内核,就不可能对根文件系统进行持久的修改。

setenv bootargs mem=128M console=ttyAMA0,115200n8 rdinit=/bin/sh

这让我在启动内核后看到了一个root shell,而不是PSH shell。PSH外壳是一个受限制的外壳,它不允许你与操作系统正常互动。它更多的是用来检查状态之类的。

有了对该设备的root权限,我得到了有用的信息,如SoC产品编号。这些信息使我能够找到与该SoC有关的泄露的源代码。

JTAG - 硬件调试

EZVIZ BD-2402B1后台工程和SNES9X端口

SoC确实有JTAG,但是所有的引脚头都没有连接,因为制造商已经去掉了电阻。SoC使用85-91号引脚来实现JTAG相关功能。这是进一步感兴趣的东西,但是它对这个项目并不直接有用,因为我已经完全控制了一切。

EZVIZ BD-2402B1后台工程和SNES9X端口

Hisilicon源代码

可以在这里找到,转载在我的服务器

注意:

如果Hisilicon联系我并要求我删除这些内容,我就会删除。如果链接已经失效,那么你可以认为前者已经发生。

SDK的某些部分已经在github上了。

https://github.com/Axl-zhang/hi3520dv300-uboot-tool
https://github.com/Axl-zhang/hisilicon-hi3520d-uboot
这些仓库几乎包含了所有内容。

https://github.com/rgmabs19357/Hi3520D_V100R001C01SPC022
https://github.com/rgmabs19357/Hi3520D_V100R001C01SPC022_osdrv

如果不是我的一个中国朋友能够从https://www.csdn.net,下载整个泄露的SDK,这个项目是不可能的。这个网站需要一个中国的电话号码(非VOIP)来注册账户,而且你也需要一个账户来下载源代码。他对这个项目的贡献是无价的。为了他的利益(并应他的要求),他将不透露姓名。

泄露的源代码包含uboot、linux kernel 3.10、极其详细的SoC文档、原理图、MPP源代码(可公开获得)以及更多。这个SDK在简要数据表中被提及。如果没有这个源代码,也没有gpio/pinmux内存布局的文档,就无法配置通过UART的通信,以及许多其他外设(包括HDMI输出)。

破坏设备 & Bus Pirate

这个SDK有两个平台的预编译的二进制文件。我想确保我找到了与我的硬件相关的正确的SDK,所以我决定用SDK中的一个预编译的uboot镜像覆盖uboot。我使用了uboot提供的一些命令来做这件事。

这些命令是这样使用的:

  • 使用tftp通过以太网将预先编译好的uboot图像读到内存中的0x82000000处。
  • 探测第一个SPI设备,并将其设置为默认设备(初始化功能,如sf读取和sf写入)。
  • 将uboot图像从内存写入闪存,偏移量为0,arm处理器将从地址0开始执行(复位向量)。最后重置处理器。

tftp uboot-from-sdk.bin 0x82000000
sf probe 0
sf write 0x82000000 0 0x100000
reset

在执行了这些uboot命令后,我没有收到任何来自UART的信息,因此我得出结论,我刷错了uboot镜像。实际上SDK里有两个预编译的uboot镜像。然后我开始直接使用我的Bus Pirate来处理SPI NOR闪存。

然而,我的Bus Pirate上的固件与flashrom有已知的问题...

在Bus Pirate的固件升级和引导程序升级过程中,我拔掉了它,因为我这样做是为了关闭我打开的查看Bus Pirate的屏幕。

```

if you unplug the bus pirate the screen program terminates

screen /dev/ttyUSB0 115200
```

所以,我拔掉设备关闭屏幕的坏习惯,最终让我损失了35美元和几天的项目时间。

Flashing SPI NOR Flash Chip (MX25L12835F)

EZVIZ BD-2402B1后台工程和SNES9X端口

在Bus Pirate和flash chip之间必须连接VCC、CS#、SO、SI和GND。WP#引脚必须保持高电平,以使写保护功能被禁用。这将允许flashrom发送命令来改变flash的状态寄存器,然后允许flashrom禁用对所有内存块的写保护。

EZVIZ BD-2402B1后台工程和SNES9X端口

配置完成后,它应该看起来像这样:

EZVIZ BD-2402B1后台工程和SNES9X端口

最后,将第二个预编译的uboot镜像刷到 nor flash chip上,我能够看到 UART 输出。 这证实了我最初的怀疑,即我无意中将错误的预编译 uboot 镜像刷到了芯片上。

EZVIZ BD-2402B1后台工程和SNES9X端口

现在我知道我可以在NOR闪存中闪现uboot图像,是时候开始开发一个自定义uboot和内核镜像了。

自定义uboot和自定义内核

为了让我加载一个自定义的内核镜像,我需要对启动顺序进行更多的控制。因此,我发现有必要编辑uboot配置中的一些变量。首先,我不会在flash chip上存储CRAMFS文件系统,而是将内核存储在闪存的一个预定义位置。重新编译内核的唯一原因是,linux使用的文件系统将被存储在linux内核内。Init.d脚本因此被编译到内核中,因此对文件系统的任何改变都需要重新编译整个内核。

EZVIZ BD-2402B1后台工程和SNES9X端口

Uboot只有~200KB,因此为了舍入起见,它将被截断为1MB。然后,生成的自定义内核映像将被截断为15MB。uboot和linux内核将作为一个16MB的大文件连接在一起。最后,刷新结果文件(u-boot-done.bin)。

truncate –size=1M u-boot.bin
truncate –size=15M uImage
cat u-boot.bin uImage > u-boot-done.bin

flashrom -V -p buspirate_spi:dev=/dev/ttyUSB0,spispeed=2M \
-c MX25L12835F/MX25L12845E/MX25L12865E -w u-boot-done.bin

uboot的自定义启动命令如下:

sf probe 0 # sf initialization
sf read 0x82000000 0x100000 0x700000 # reads 7mb from offset=1MB into address 0x82000000
bootm 0x82000000 # boot the custom kernel

这使我不再需要CRAMFS文件系统或存储在闪存本身上的任何文件系统。

系统启动

SDK包含一个预先配置好的Busybox,它基本上是将操作系统所需的所有可执行文件存储在一个可执行文件中。Busybox将作为init程序运行,它是第一个运行的usermode可执行程序(PID 1)。Init将解析并执行/etc/inittab文件中的命令。

/etc/inittab执行bash脚本/etc/init.d/rcS。/etc/init.d/rcS将执行mount -a,然后执行同一文件夹中的其他bash脚本。这些其他的bash脚本的命名方式必须是这样的。SXX,其中XX是一个数字,这个数字代表执行的优先级。

系统启动概述

  • Uboot
  • uImage loaded
  • Kernel decompressed
  • Control passed to the kernel
  • Kernel startup
  • Execution passed to /init
  • /init parses and executes commands from /etc/inittab
  • /etc/inittab says to execute /etc/init.d/rcS
  • /etc/init.d/rcS executes mount -a and then executes other bash scripts.
  • mount -a will mount file systems according to /etc/fstab.
  • echo /sbin/mdev > /proc/sys/kernel/hotplug
  • mdev -s
  • pinmux’s are configured and hisilicon kernel modules are loaded
  • Lastly a getty executable is launched and the root user is logged in without authentication.
  • Root user /root/.profile configures environment variables and lastly executes the snes9x fork.

第一个插入设备的USB将是/dev/sda,第一个可识别的分区是/dev/sda1。启动脚本将通过mount /dev/sda1 /mnt/usb-drive/来装载USB。我的启动脚本执行的最后一条命令是在USB上执行一个文件名为 "snes9x "的脚本。这将允许我在init.d下运行一些东西,而不需要重新编译和刷到内核镜像到闪存上。

配置Pinmux和加载内核模块

当系统启动时,某些引脚的配置方式会使内核模块的功能无法操作。HDMI输出就是这样的情况。SDK包含了设置pinmux配置的shell脚本,这样当内核模块被加载时,它们的功能就能成功实现。这些都很重要,所以我将在这里把它们全部列出。

```

pinmux configuration

I2C

himm 0x120F00E0 0x1; # 0:GPIO12_6 1:I2C_SDA
himm 0x120F00E4 0x1; # 0:GPIO12_7 1:I2C_SCL

VICAP

himm 0x120F0000 0x2; # 00:GPIO5_7 01:VI0_CLK 10:VI_ADC_REFCLK0

himm 0x120F0004 0x1; # 0:GPIO1_0 1:VI0_DAT7
himm 0x120F0008 0x1; # 0:GPIO1_1 1:VI0_DAT6
himm 0x120F000C 0x1; # 0:GPIO1_2 1:VI0_DAT5
himm 0x120F0010 0x1; # 0:GPIO1_3 1:VI0_DAT4
himm 0x120F0014 0x1; # 0:GPIO1_4 1:VI0_DAT3
himm 0x120F0018 0x1; # 0:GPIO1_5 1:VI0_DAT2
himm 0x120F001C 0x1; # 0:GPIO1_6 1:VI0_DAT1
himm 0x120F0020 0x1; # 0:GPIO1_7 1:VI0_DAT0

himm 0x120F0024 0x2; # 00:GPIO10_6 01:VI1_CLK 10:VI0_CLK

himm 0x120F0028 0x1 # 0:GPIO2_0 1:VI1_DAT7
himm 0x120F002C 0x1 # 0:GPIO2_1 1:VI1_DAT6
himm 0x120F0030 0x1 # 0:GPIO2_2 1:VI1_DAT5
himm 0x120F0034 0x1 # 0:GPIO2_3 1:VI1_DAT4
himm 0x120F0038 0x1 # 0:GPIO2_4 1:VI1_DAT3
himm 0x120F003C 0x1 # 0:GPIO2_5 1:VI1_DAT2
himm 0x120F0040 0x1 # 0:GPIO2_6 1:VI1_DAT1
himm 0x120F0044 0x1 # 0:GPIO2_7 1:VI1_DAT0

himm 0x120F0048 0x2; # 00:GPIO6_0 01:VI_ADC_REFCLK0 10:VI1_CLK
himm 0x120F004C 0x2; # 00:GPIO11_7 01:VI2_CLK 10:VI_ADC_REFCLK1

himm 0x120F0050 0x1 # 0:GPIO3_0 1:VI2_DAT7
himm 0x120F0054 0x1 # 0:GPIO3_1 1:VI2_DAT6
himm 0x120F0058 0x1 # 0:GPIO3_2 1:VI2_DAT5
himm 0x120F005C 0x1 # 0:GPIO3_3 1:VI2_DAT4
himm 0x120F0060 0x1 # 0:GPIO3_4 1:VI2_DAT3
himm 0x120F0064 0x1 # 0:GPIO3_5 1:VI2_DAT2
himm 0x120F0068 0x1 # 0:GPIO3_6 1:VI2_DAT1
himm 0x120F006C 0x1 # 0:GPIO3_7 1:VI2_DAT0

himm 0x120F0070 0x2; # 00:GPIO10_5 01:VI3_CLK 10:VI2_CLK

himm 0x120F0074 0x1 # 0:GPIO4_0 1:VI3_DAT7
himm 0x120F0078 0x1 # 0:GPIO4_1 1:VI3_DAT6
himm 0x120F007C 0x1 # 0:GPIO4_2 1:VI3_DAT5
himm 0x120F0080 0x1 # 0:GPIO4_3 1:VI3_DAT4
himm 0x120F0084 0x1 # 0:GPIO4_4 1:VI3_DAT3
himm 0x120F0088 0x1 # 0:GPIO4_5 1:VI3_DAT2
himm 0x120F008C 0x1 # 0:GPIO4_6 1:VI3_DAT1
himm 0x120F0090 0x1 # 0:GPIO4_7 1:VI3_DAT0

himm 0x120F0094 0x2; # 00:GPIO6_1 01:VI_ADC_REFCLK1 10:VI3_CLK

VGA

himm 0x120F0098 0x1; # 0: GPIO11_6 1: VGA_HS
himm 0x120F009C 0x1; # 0: GPIO11_3 1: VGA_VS

HDMI

himm 0x120F0174 0x1; # 0: GPIO13_4 1:HDMI_HOTPLUG
himm 0x120F0178 0x1; # 0: GPIO13_5 1:HDMI_CEC
himm 0x120F017C 0x1; # 0: GPIO13_6 1:HDMI_SDA
himm 0x120F0180 0x1; # 0: GPIO13_7 1:HDMI_SCL

SPI

himm 0x120F00C4 0x1; # 00:TEST_CLK 01:SPI_SCLK 10:GPIO5_0
himm 0x120F00C8 0x1; # 0: GPIO5_1 1:SPI_SDO
himm 0x120F00CC 0x1; # 0: GPIO5_2 1:SPI_SDI
himm 0x120F00D0 0x1; # 0: GPIO5_3 1:SPI_CSN0
himm 0x120F00D4 0x1; # 0: GPIO5_4 1:SPI_CSN1

I2C

himm 0x120F00E0 0x1; # 0:GPIO12_6 1:I2C_SDA
himm 0x120F00E4 0x1; # 0:GPIO12_7 1:I2C_SCL

I2S

himm 0x120F00A0 0x1; # 0: GPIO9_0 1: I2S0_BCLK_RX
himm 0x120F00A4 0x1; # 0: GPIO9_1 1: I2S0_WS_RX
himm 0x120F00A8 0x1; # 0: GPIO9_2 1: I2S0_SD_RX

himm 0x120F00AC 0x2; # 00: GPIO9_3 01: I2S1_BCLK_RX 10:I2S2_MCLK
himm 0x120F00B0 0x1; # 0: GPIO9_4 1: I2S1_WS_RX
himm 0x120F00B4 0x1; # 0: GPIO9_5 1: I2S1_SD_RX

himm 0x120F00B8 0x1; # 0: GPIO9_6 1: I2S2_BCLK_TX
himm 0x120F00BC 0x1; # 0: GPIO9_7 1: I2S2_WS_TX
himm 0x120F00C0 0x1; # 0: GPIO5_4 1: I2S2_SD_TX

crg configuration

VI(0x5c001111--150M,0x3c001111--324M,0x1c001111--300M)

himm 0x1204002c 0x1c001111 #720p

VI ADC REF0/REF1

himm 0x120400B4 0x00000035

TDE

himm 0x12040048 0x00000002

IVE

himm 0x1204005C 0x00000002

CIPHER

himm 0x12040060 0x00000002

system configuration

### MISC QOS setting!

himm 0x1212007c 0x44443201 ## VGS-JPGD-IVE -TDE -AVC0- A7 - VO -VI
himm 0x12120080 0x26334444 ## GSF-DDRT-AVC1-VPSS-VOIE-JPGE-AIO-MDU
himm 0x12120084 0x66666426 ## ###-DMAm0-DMAm1-FMC-USB2-CIPHER-SCD-SATA

#VIVO 总线优先级 7优先级最大

himm 0x12120094 0x65 ###【2:0】VI 【6:4】VO

mddrc0 pri&timeout setting

himm 0x12110020 0x00000001 # AXI_ACTION[19:8]:wr_rcv_mode=0,12ports

himm 0x12110200 0x00370000 # ports0 选择随路QOS模式
himm 0x12110210 0x00370000 # ports1
himm 0x1211021c 0x08300830 # ports1读写自适应优先级
himm 0x12110220 0x00370000 # ports2
himm 0x1211022c 0x08300830 # port2读写自适应优先级
himm 0x12110230 0x00370000 # ports3
himm 0x1211023c 0x08300830 # port3读写自适应优先级
himm 0x12110240 0x00370000 # ports4
himm 0x12110250 0x00370000 # ports5
himm 0x12110260 0x00370000 # ports6
himm 0x12110270 0x00370000 # ports7

DDRC AXI pri ports0 - 7

######## WR pri

himm 0x12110204 0x76543210 # ports0

himm 0x12110214 0x76543210 # ports1

himm 0x12110224 0x76543210 # ports2
himm 0x12110234 0x76543210 # ports3
himm 0x12110244 0x76543210 # ports4
himm 0x12110254 0x76543210 # ports5
himm 0x12110264 0x76543210 # ports6

himm 0x12110274 0x76543210 # ports7

######## RD pri

himm 0x12110208 0x76543210 # ports0

himm 0x12110218 0x76543210 # ports1

himm 0x12110228 0x76543210 # ports2
himm 0x12110238 0x76543210 # ports3
himm 0x12110248 0x76543210 # ports4
himm 0x12110258 0x76543210 # ports5
himm 0x12110268 0x76543210 # ports6
himm 0x12110278 0x76543210 # ports7

######## qosbuf

himm 0x12114000 0x00000002 #qosb_push_ctrl
himm 0x12114004 0x000007F1 #cycle
himm 0x1211410c 0x0000000a #qosb_dmc_lvl
himm 0x12114110 0x0000000a #qosb_dmc_lvl
himm 0x1211408c 0xb3032010 #qosb_wbuf_ctrl
himm 0x12114090 0xb3032010 #qosb_wbuf_ctrl
himm 0x121140f4 0x00000033 #row-hit enable
himm 0x121140ec 0x00000044 #row-hit
himm 0x121140f0 0x00003333 #row-hit
himm 0x121141f4 0x00000000 #qosb_wbuf_pri_ctrl

himm 0x121141f0 0x00000001 #enable qosbuf timeout,through prilvl to remap timeout level

######## WR timeout

himm 0x1211409c 0x00000010 # wr_tout3 ~wr_tout0

himm 0x121140a0 0x00000000 # wr_tout7 ~wr_tout4

himm 0x121140a4 0x00000000 # wr_tout11~wr_tout8
himm 0x121140a8 0x00000000 # wr_tout15~wr_tout12

######## RD timeout

himm 0x121140ac 0x00000010 # rd_tout3 ~rd_tout0

himm 0x121140b0 0x00000000 # rd_tout7 ~rd_tout4

himm 0x121140b4 0x00000000 # rd_tout11~rd_tout8
himm 0x121140b8 0x00000000 # rd_tout15~rd_tout12

himm 0x121141f8 0x00800002 # qosb_rhit_ctrl,open_window=128,close_window=2
```

每个地址的功能在SoC的PDF中都有明确的定义。可执行的himm是一个简单的mmap包装器,允许用命令行编辑物理内存。这个工具的源代码包含在SDK中。

SNES9X端口

这个项目的最后一部分是实际编写snes9x的移植。首先,需要好好读一下porting.html。该文件指出,有一些方法必须被实现,但是大多数方法可以简单地成为空子程序。

为了使事情更简单,现有的unix代码用于snes9x unix端口。然而,这个unix代码包含x11代码,不适用于我的端口。

下面的例程是简单地从unix.cpp文件中复制过来的。

bool8 S9xMapInput(const char *n, s9xcommand_t *cmd);
void InitJoysticks(void);
bool8 ReadJoysticks(void);
const char *S9xStringInput(const char *message);
const char *S9xGetFilename(const char *ex, s9x_getdirtype dirtype);
const char *S9xGetFilenameInc(const char *ex, enum s9x_getdirtype dirtype);
const char *S9xGetDirectory(enum s9x_getdirtype dirtype);
const char *S9xBasename(const char *path);

真正感兴趣的唯一函数是S9xDeinitUpdate,每次计算一帧并准备渲染时都会调用它。所有缩放和像素转换逻辑都必须在该功能内部执行。

SNES9X效果图

由于尺寸的限制,移植x11/xorg以在这个嵌入式设备上运行是不可能的。系统上也不会有图形库(没有SDL、OpenGL等)。我的snes9 分支将简单地直接渲染到frame buffer设备/dev/fb0。hisilicon关于帧缓冲器的文档很详细,SDK甚至包含了如何操作frame buffer的例子。HiFB API Reference.pdf, HiFB开发指南

hisilicon的SDK包含了关于如何向frame buffer显示图像的示例代码。其步骤是:初始化显示设备,然后设置一个显示层。我想模拟的游戏(超级马里奥世界)的像素分辨率为256x240。我的想法是初始化视频输出设备,以尽可能低的分辨率显示,这样提高SNES的输出就需要较少的工作。Hisilicon的HDMI输出支持的最低分辨率是680x480,频率为60HZ。这意味着升频将只需略微超过x2。

下面的代码用于在640x480分辨率下设置HDMI:

```
/******
step 1: init variable
******/
memset(&stVbConf, 0, sizeof(VB_CONF_S));

u32BlkSize = CEILING_2_POWER(u32PicWidth, SAMPLE_SYS_ALIGN_WIDTH) *
CEILING_2_POWER(u32PicHeight, SAMPLE_SYS_ALIGN_WIDTH) * 2;

stVbConf.u32MaxPoolCnt = 128;
stVbConf.astCommPool[0].u32BlkSize = u32BlkSize;
stVbConf.astCommPool[0].u32BlkCnt = 6;

/******
step 2: mpp system init.
******/
s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
if (HI_SUCCESS != s32Ret) {
SAMPLE_PRT("system init failed with %d!\n", s32Ret);
return HI_FALSE;
}

/******
step 3: start vo hd0.
*******/
s32Ret = HI_MPI_VO_UnBindGraphicLayer(GRAPHICS_LAYER_HC0, SAMPLE_VO_DEV_DHD0);
if (HI_SUCCESS != s32Ret) {
SAMPLE_PRT("UnBindGraphicLayer failed with %d!\n", s32Ret);
return HI_FALSE;
}

s32Ret = HI_MPI_VO_BindGraphicLayer(GRAPHICS_LAYER_HC0, SAMPLE_VO_DEV_DHD0);
if (HI_SUCCESS != s32Ret) {
SAMPLE_PRT("BindGraphicLayer failed with %d!\n", s32Ret);
return HI_FALSE;
}

stPubAttr.enIntfSync = VO_OUTPUT_640x480_60;
stPubAttr.enIntfType = VO_INTF_HDMI;
stPubAttr.stSyncInfo.bSynm = HI_TRUE;
stPubAttr.u32BgColor = 0x000000; // background will be black

stLayerAttr.bClusterMode = HI_FALSE;
stLayerAttr.bDoubleFrame = HI_FALSE;
stLayerAttr.enPixFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420;

u32VoFrmRate = 60;
stSize.u32Width = SCREEN_WIDTH;
stSize.u32Height = SCREEN_HEIGHT;

memcpy(&stLayerAttr.stImageSize, &stSize, sizeof(stSize));

stLayerAttr.u32DispFrmRt = 60;
stLayerAttr.stDispRect.s32X = 0;
stLayerAttr.stDispRect.s32Y = 0;
stLayerAttr.stDispRect.u32Width = stSize.u32Width;
stLayerAttr.stDispRect.u32Height = stSize.u32Height;

s32Ret = SAMPLE_COMM_VO_StartDev(SAMPLE_VO_DEV_DHD0, &stPubAttr);
if (HI_SUCCESS != s32Ret) {
SAMPLE_PRT("start vo dev failed with %d!\n", s32Ret);
return HI_FALSE;
}

s32Ret = SAMPLE_COMM_VO_StartLayer(VoLayer, &stLayerAttr);
if (HI_SUCCESS != s32Ret) {
SAMPLE_PRT("start vo layer failed with %d!\n", s32Ret);
return HI_FALSE;
}

if (stPubAttr.enIntfType & VO_INTF_HDMI) {
s32Ret = SAMPLE_COMM_VO_HdmiStart(stPubAttr.enIntfSync);
if (HI_SUCCESS != s32Ret) {
SAMPLE_PRT("start HDMI failed with %d!\n", s32Ret);
return HI_FALSE;
}
}
```

相关的图形概念

在进入正题之前,最好先解释一下一些基础知识。在图形渲染中,有一个术语 "pitch",简单地说就是 "单个垂直行的像素的字节数"。这一点很重要,因为重叠的位图图像需要在写下一个垂直行的像素前跳过X个字节(假设两个位图的大小不同)。Hisilicon TDE处理重叠,并为我们处理间距,所以我们不需要这样做!

EZVIZ BD-2402B1后台工程和SNES9X端口

像素格式化也是需要了解的。SNES9X模拟器默认使用RGB565,hisilicon帧缓冲设备默认使用ARGB1555,因此需要进行像素转换。TDE引擎也在一定程度上处理像素转换。

使用TDE的硬件加速SNES9X

现在这些基本的图形概念已经过时了,我们可以看看TDE了。本文前面解释的TDE是一个二维图形引擎,可用于切换缩放比例缩放、重叠和像素转换操作,因此主ARM处理器不需要这样做。以下是一些有趣的应用编程接口函数。这些函数在libtde.a内部,libtde.a内部的代码本质上是ioctl的包装函数。TDE驱动程序必须加载到linux内核中,这个TDE驱动程序依赖于MMZ(媒体内存区域)驱动程序。

```
HI_S32 HI_TDE2_Open(HI_VOID);

TDE_HANDLE HI_TDE2_BeginJob(HI_VOID);

HI_S32 HI_TDE2_QuickCopy(TDE_HANDLE s32Handle,
TDE2_SURFACE_S pstSrc,
TDE2_RECT_S
pstSrcRect,
TDE2_SURFACE_S pstDst,
TDE2_RECT_S
pstDstRect);

HI_S32 HI_TDE2_Bitblit(TDE_HANDLE s32Handle,
TDE2_SURFACE_S pstBackGround,
TDE2_RECT_S
pstBackGroundRect,
TDE2_SURFACE_S pstForeGround,
TDE2_RECT_S
pstForeGroundRect,
TDE2_SURFACE_S pstDst,
TDE2_RECT_S
pstDstRect,
TDE2_OPT_S *pstOpt);

HI_S32 HI_TDE2_EndJob(TDE_HANDLE s32Handle,
HI_BOOL bSync, HI_BOOL bBlock,
HI_U32 u32TimeOut);

HI_S32 HI_TDE2_WaitAllDone(HI_VOID);
```

这些函数的完整文档在TDE API参考手册中提供。

这些函数中最引人注目的是HI_TDE2_Bitblit函数,它允许各种操作的发生。对于snes9x端口最重要的操作是像素转换和缩放。这两个操作都可以通过HI_TDE2_Bitblit在同一时间完成。

```
stSrcRect.s32Xpos = 0;
stSrcRect.s32Ypos = 0;
stSrcRect.u32Height = SNES_HEIGHT;
stSrcRect.u32Width = SNES_WIDTH;

stSrc.enColorFmt = TDE2_COLOR_FMT_RGB565;
stSrc.u32Width = SNES_WIDTH;
stSrc.u32Height = SNES_HEIGHT;
stSrc.u32Stride = 2 * SNES_WIDTH;
stSrc.u32PhyAddr = g_pSnesBackBufferPhys;

stScaleRect.s32Xpos = 0;
stScaleRect.s32Ypos = 0;
stScaleRect.u32Height = SCALE_HEIGHT;
stScaleRect.u32Width = SCALE_WIDTH;

stScale.enColorFmt = TDE2_COLOR_FMT_ARGB1555;
stScale.u32Width = SCALE_WIDTH;
stScale.u32Height = SCALE_HEIGHT;
stScale.u32Stride = SCALE_WIDTH * 2;
stScale.u32PhyAddr = g_pScaleBufferPhys;

s32Ret = HI_TDE2_Bitblit(s32Handle, &stScale, &stScaleRect, &stSrc, &stSrcRect,
&stScale, &stScaleRect, &stOpt);
```

当SNES绘制黑色像素时,它将R、G和B值设置为0。这导致像素的整数值为0。如果像素值为0,TDE引擎无法将RGB565转换为ARGB1555。我不知道为什么,但是为了解决这个问题,我简单地做了一点错误处理:

```
// if the screen is black all pixel values will be 0. this will cause bitblit
// to fail because it doesn't understand pixel conversions with pixels that are
// all 0... so i just make R = 1, G = 1, and B = 1... simple fix... lol...
if (s32Ret < 0) {
for (int i = 0; i < SNES_WIDTH * SNES_HEIGHT; ++i) {
if ((((uint16_t)g_pSnesBackBufferVirt) + i) == NULL) {
(((uint16_t)g_pSnesBackBufferVirt) + i) = BUILD_PIXEL2_RGB565(1, 1, 1);
}
}

s32Ret = HI_TDE2_Bitblit(s32Handle, &stScale, &stScaleRect, &stSrc,
&stSrcRect, &stScale, &stScaleRect, &stOpt);

// if we fail here then its a legit issue and we should print the reason and
// cancel the job
if (s32Ret < 0) {
SAMPLE_PRT("HI_TDE2_Bitblit:%d failed,ret=0x%x!\n", LINE, s32Ret);
HI_TDE2_CancelJob(s32Handle);
return false;
}
}
```

一旦像素转换和图像升格完成,我们现在可以做一个HI_TDE2_QuickCopy,它将重叠计算SNES帧的升格和像素转换。以下代码将重叠、复制并向屏幕显示放大的帧:

```
s32Ret =
HI_TDE2_QuickCopy(s32Handle, &stScale, &stScaleRect, &stDst, &stDstRect);

if (s32Ret < 0) {
SAMPLE_PRT("HI_TDE2_QuickCopy:%d failed,ret=0x%x!\n", LINE, s32Ret);
HI_TDE2_CancelJob(s32Handle);
return false;
}

/ 3. submit job /
s32Ret = HI_TDE2_EndJob(s32Handle, HI_FALSE, HI_TRUE, 10);
if (s32Ret < 0) {
SAMPLE_PRT("Line:%d,HI_TDE2_EndJob failed,ret=0x%x!\n", LINE, s32Ret);
HI_TDE2_CancelJob(s32Handle);
return false;
}

HI_TDE2_WaitAllDone(); // just in case EndJob returns
// before the TDE computation is finished

ioctl(g_hFrameBuffer, FBIO_REFRESH, &g_stCanvasBuf);
```

总结

最终的结果是一个SNES9X端口,可以运行没有声音的超级马里奥世界。然而,默认情况下是支持手柄的,因为linux内核编译时就支持这些设备。/dev/js0被配置为玩家1的控制器。这段代码是从snes9x的unix代码中复制的。

这个SNES9X的分支是非常原始的,甚至不支持SRAM(游戏保存),但是它是一个非常有趣的项目。

EZVIZ BD-2402B1后台工程和SNES9X端口

这是有史以来第一次为了查看显示器是否正常工作而进行的渲染。请注意,还没有完成像素转换或放大代码。

EZVIZ BD-2402B1后台工程和SNES9X端口

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月15日10:27:47
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   EZVIZ BD-2402B1后台工程和SNES9X端口https://cn-sec.com/archives/913060.html