基于MK802 MiniPC的扩展开发应用-软/硬件修改和扩展


这是一个文章系列的一部分,介绍基于MK802这类MiniPC的扩展开发,并展现他在计算机视觉、机器人控制方面的潜能php

欢迎转载,但请保留原始做者信息(Shikai Chen, http://www.csksoft.net),以及指向本文原始出处的连接!html

访问目录:基于MK802 MiniPC的扩展开发应用-简介篇(http://www.csksoft.net/blog/post/mk802_dev_intro.html)linux

 

revision: 1android

这部分将介绍各类针对MK802硬件以及软件的修改技巧,相对来讲,这部分介绍的都属于各类杂碎的小技巧,文章构成上比较零碎,而且相对于整个系列文章其余部分独立。
虽然这些都是各类小技巧,有些甚至属于雕虫小技,不过在必要的地方我也会经过问题自己作一些对linux kernel的进一步介绍。
git

在本文中将介绍的例子有:

  1. 引出MK802的内部调试串口信号,用于kernel的调试和开发github

  2. 经过fex脚本配置hdmi的输出分辨率至1080p和其余尺寸以及色彩深度shell

  3. 在自制系统内利用MK802的内置Flash存储文件编程

  4. 将CPU主频超频至1.1G,以及实现动态频率调节下降功耗
    bash

 

1. MK802硬件系统介绍

这么短的文章不期望能把整个硬件构架状况交代清楚,这里仅给出对本文后续Hack所须要的背景信息。网络

彻底了解MK802的硬件构成,须要拥有厂家的相关文档,而这些文档每每是须要签署NDA协议后提供的,并不能公开获取,因此这里的介绍只是透过个人拆解分析,以及基于网上相关资料(见参考文献)汇总得出的。他们可能与真实状况存在误差。

 

1.1. CPU和SOC资源


这里介绍CPU和相关SOC的目的是为了让你们了解MK802的性能状况。就目前厂家提供的固件以及咱们自制的固件而言,大部分的SOC资源都没有获得有效的利用。就像在前一篇文章中我也提到,目前在传统Linux发行版上还大部分没有上述SOC的驱动,就好比Mali 400 GPU和G2D 2D图形加速的支持,以及视频编解码的驱动支持。不过目前社区也有人在尝试着经过逆向工程的手段,实如今传统linux发行版上使用这类SOC。

透过厂家以及以前文章的介绍,MK802是采用Allwinner A10 CPU芯片的系统方案,该芯片基于ARM Cortex A8构架,工做在1Ghz左右的频率,GPU SOC采用了ARM Mali-400 MP[2]。
其余的片上资源包括[1]:
 支持2160p(支持3D视频格式输出)的HD视频decoder,包含对H.264,MPEG-1/2/4的格式的硬件解码支持,以及对应的硬件编码器
 硬件2D图像加速(G2D)
 USB2.0高速的HOST接口
 USB2.0高速的OTG接口
 SDIO总线x4
 CSI总线
 100Mbps以太网控制器
 SATA2.0接口 (3Gbps)
 IDE(PATA)接口
 Can总线
 I2S、SPDIF、AC97音频接口
 PS二、SPI、TWI(I2C)、UART(串口)接口
 LVDS、VGA、HDMI视频输出接口
 电阻触摸屏控制器
这块芯片的SOC资源仍是比较不错的,连SATA2.0也有,不过咱们的MK802并无把这些资源的信号都提供出来。因此像SATA口咱们是没法再MK802上使用到了。不过市面上也有其余很多基于Allwinner A10的机器,其中不乏这些资源均可用的机型[3]。本系列文章的内容一样对他们有效。

1.2. MK802的硬件构成


MK802的硬件组成以下图所示,诸如HDMI这类你们都已经清楚地接口,这里就不标出了。

图:MK802硬件设备

资源/设备/接口

图中标号

描述

Nand Flash

1

4Gb

Allwinner A10 CPU

2

见上文

DDR3 RAM

3

按配置有1G/512M

DCDC供电

4

简单的DCDC模块,非PMIC

USB WIFI模组

5

采用rtl8192芯片的USB WIFI模块

USB 2.0 OTG

6

能够用做USB host链接外设

调试串口触点

7

后文将介绍将其信号引出用于调试

能够看出MK802的PCB布局比较紧凑,可以作信号扩展的机会很少,能够进行的有:
 将内部的调试串口信号引出
 利用USB WIFI模组额外的一条USB总线,外接更多的设备
 替换DCDC模组,或者将其输出引出

本文接下来将介绍这里列举的第一条,其余若是你们有兴趣能够本身尝试。

1.3. 功耗状况:


参考文献[1]给出了一样采用Allwinner A10芯片的另外一台机器Mele A1000的功耗状况,因为它与MK802的硬件类似度极高,所以能够基本认为与MK802一致。这里我就直接引用[1]的数据:

使用场景

功耗状况

u-boot idle (uboot自动启动被打断)

1.15W (0.23A, 5V)

Android系统空闲状态

1.7W (0.34A, 5V)

Android频繁浏览网页时

2.75W (0.55A, 5V)

Androidyoutube播放720p视频

4.6W

Ubuntu 12.04 + SATA HDD 下载bt,不接显示器输出

5.0 – 6.5W

这个功耗水平仍是很使人满意的,须要注意上表的数据是基于Mele A1000采集获得的,因为MK802具备更少的硬件外设,所以功耗水平应该更低。另外上表的最后一项还包含SATA硬盘,所以5W的功耗中应该也包含了硬盘消耗。

若是但愿下降MK802的功耗,除了在运行期间禁用相关的硬件功能(好比关闭hdmi图形输出),还有一个有效措施是按照需求下降CPU主频,这部分将在后文介绍。

2. MK802的设备配置(fex文件)和工做机制

在前一篇文章[4]的uboot/linux kernel编译环节,我提到了Allwinner A10 CPU的kernel使用了一个fex脚本的配置文件,用于对于系统各类参数的配置。这里将专门介绍一下这个文件。


图:在自制系统过程当中须要编译的配置脚本


该文件的原始形式是如上图所示的文本格式的配置文件,在前文[4]中提到了在实际系统制做中,该文本格式的文件将使用fex2bin的程序,将他转化成二进制的格式,并最终以文件名script.bin同linux kernel镜像(uImage)一块儿发布在SD卡的FAT分区当中。

在MK802启动阶段,该fex脚本的二进制形式首先会经过uboot脚本boot.scr加载到一个固定的内存地址。随后在Linux kernel启动的不一样时期,各类相关的驱动程序在初始化阶段将读取该配置脚本,完成对设备的初始化。具体过程将在下文的fex script深刻讨论部分介绍。这里须要了解的是咱们能够经过对该文件进行修改达到对某些设备的配置。

若是感兴趣,你们能够先下载该文件观察一下:

https://github.com/cnxsoft/a10-config/blob/master/script.fex/mk802.fex

能够观察到其中有诸如对于TWI(I2C)、DDR RAM、串口、SPI、HDMI各种设备的参数配置。以及还有一些MK802上并不存在的设备的配置,好比LCD、加速度传感器等。
这多是因为该配置文件是社区基于其余的Allwinner A10方案的设备修改而来的,另外经过分析MK802的linux kernel能够知道,其中包含了不少应该是来自Allwinner A10 的参考开发系统的代码,其中诸如加速度传感器、LCD等的驱动程序仍旧包含在如今的代码中。这些代码虽然不会起到实际做用,但在加载的过程当中仍旧可能会从fex脚本中读取对应的配置信息。所以并非其中全部的配置项目都是会起做用的。

那么如何进行配置呢?该配置文件的具体介绍网上并无公开的文档进行解释(在文献[1]同提到有泄露的厂家文档,能够自行验证),所以主要经过以下2个途径:
1. 猜想
2. 查看Linux kernel代码

对于一些显而易见的配置项目,好比这条:

[target]
boot_clock = 1008
dcdc2_vol = 1400
dcdc3_vol = 1250
ldo2_vol = 3000
ldo3_vol = 2800
ldo4_vol = 2800


其中这类XXX_vol的项目对于懂行的朋友而言一看就知道是配置PMIC芯片的供电输出的。可是即便这样猜想知道了这个配置项目的意义,但也不能保证该配置项就能够起到做用。正如上文所说,MK802并无带有PMIC,DCDC的供电应该是没法配置的。另外Linux kernel代码中或许就没有对这项配置项进行读取(好比配置项的DDR内存大小,目前的kernel就是忽略的,须要修改uboot代码实现1Gb内存的使用,见上一篇文章[4])。

对于没法猜想含义或者不肯定是否有效的配置项,就须要阅读Linux kernel的代码来了解他的具体配置方法和是否起做用了。考虑到不是全部人对这个过程感兴趣,所以我将他的介绍安排到了文章模块的深刻探讨部分。我将在那部分以实际的代码为基础介绍。

 

3. 给MK802引出调试串口信号


我blog早些时候曾介绍过这个部分[5],这里介绍的内容和以前的基本一致,若是已经看过该文章,能够略过此小节。

3.1. 目的和意义


使用该串口信号咱们能够即时的获取MK802的底层工做状况(uboot和kernel的log),以及可以获得一个Shell终端,用于登录linux系统,在没有网络或者显示器的状况下就能够对MK802进行操做。而且若是在自制系统时候出现问题,致使MK802没法正常启动时,使用调试串口几乎就是惟一的查找缘由的方式了。另外这个调试串口也能够做为与外部设备通信的接口,但我不推荐这种作法,所以本文也不会介绍。

通常作linux kenrel和设备驱动的开发,没有VC IDE debugger这种好用的工具,就连gdb server不少时候都无论用。最多见的办法就是经过printf把日志从串口打印出来调试。(固然若是正在开发串口驱动,那只好经过点亮几个LED灯来调试了,这听上去很疯狂,但这是事实)。虽然也有ICE/JTAG这类硬件调试器,但对于linux kernel这类OS的调试,硬件调试器就显得很不直观,并且不少time critical的逻辑没法经过下断点复现。所以,通常作硬件/kernel/驱动层次开发,有一个用于打印printk信息的串口是很是必要的。


 

3.2. 硬件修改


MK802内部含有调试用的串口,但并无将他的信号链接出来。经过简单的焊接工做,咱们能够把该信号链接出来,并使用usb转串口模块与pc相连。

图:将MK802外壳当心拆开

这里须要将MK802拆开,拆开过程须要比较当心,外壳之间是采用卡口相互固定的,可用硬质的塑料板或者专门的拆机撬棒打开,随后能够观察到在A10芯片的那侧有以下图所示的几个金色的焊盘:


图:MK802 PCB上测试点的信号定义


这些测试点没有丝印标出信号含义,但其实很容易猜到:右起第二个肉眼就能看出是GND。最右侧经过万用表测量是3.3V。那天然是VCC。左边2个天然有很大嫌疑是TXD和RXD的TTL电平的串口信号。那么怎么肯定那个是TX哪一个是RX? 注意左起第二个有一个上拉电阻。通常输入信号才要上拉/下拉。那极可能就是RX。用示波器看了下,果真最左侧有信号发出。

这个咱们仅须要用到的信号就是TX/RX/GND。VCC也能够接出来给外部一些低功耗的设备提供3.3V的电能。

当心的将上述信号用导线焊接连出来,而且使用诸如基于FT232/CP2102等芯片的usb转串口(TTL)的适配器上。注意MK802的RX信号须要与适配器的TX端链接。

 

图:在测试点上焊接导线,并用热熔胶固定

图:使用usb转串口链接


到这里对硬件的修改工做就完成了。

3.3. 经过调试串口查看kernel log以及登录shell


将上文提到的usb转串口适配器链接电脑。使用Putty(Windows)[6]、高级终端(Windows)、Minicom(Linux\Mac)等支持串口终端的工具打开此串口。并将MK802通电,就能看到启动过程当中从uboot以及linux kernel输出的各类log了。

这里的串口设置是比较通用的115200bps 8bit 1 stopbit的设置。对于绝多大多数的嵌入式设备(以及PC),串口调试均采用该配置。

 

 

图:使用Putty做为串口调试终端

图:从串口输出的启动一开始的uboot log

图:串口输出的kernel log

对于经过上一篇文章[4]制做的系统,咱们能够在系统启动完毕后在串口终端获得一个bash shell,对于熟悉命令行操做的人来讲,已经能够很自如的使用起MK802了。在后续的系列文章中,我也将介绍如何在没有HDMI接口的显示器且没有配置过网络的状况下,仅使用串口完成wifi网络的配置,以及设置vnc供后续远程图形桌面登录的技巧。

 图:在启动完毕后将显示bash shell的提示符,能够进行命令行操做


4. 软件部分的修改和扩展

这里开始将介绍软件部分的各类修改或者扩展,主要围绕前文提到的fex脚本的修改和Linux kernel代码修改展开。

4.1. 修改显示分辨率至1080p


前文介绍过MK802所使用的Allwinner A10 CPU带有在HDMI上输出1080p的画面的能力,可是官方的Android系统以及前一篇文章介绍的自制系统仍旧以720p(1280x720)做为输出分辨率。
这并非硬件问题,而是软件(驱动)的配置问题。不过对于Android,目前的4.0版本在设计上并不能支持1080p的画面,所以咱们没法对这个系统进行修改。而对于咱们自制的基于传统linux发行版的系统,就能够很简单的修改fex脚本的一行参数达到1080p的配置:
打开上文提到的mk802.fex文件。(具体请参考上文对fex脚本介绍的章节),定位到314行附近,找到screen0_output_mode参数的设置语句,将他赋值为9(以前是5)。


图:修改mk802.fex,实现1080p输出


完成修改之后,使用上一篇文章[4]提到的fex2bin程序将改fex脚本编译成bin格式:

fex2bin mk802.fex script.bin


这里提供编译好的bin文件,方便你们:

http://www.csksoft.net/data/mk802/script.1080p.7z

接下来,只要将该文件把存放自制系统SD卡的第一个分区的对应文件替换,从新启动mk802,就能够获得1080p分辨率的hdmi输出画面了。

若是不熟悉这个过程,可在linux下使用以下命令:

mkdir –p sdcard
sudo mount /dev/sdd1 sdcard
sudo cp script.bin sdcard
sudo umount /dev/sdd1

请将上述代码的下划线部分替换成符合你实际状况的sd卡的设备名,若是不清楚,能够参考[4]的 1.8. 将系统部署到SD卡 章节。

对于为什么这里要将screen0_output_mode设置为9,若是对此感兴趣,能够参考本文后文的深刻讨论部分。


4.2. 使用内部Flash空间存储数据


MK802自己具备4Gbyte的NAND flash用于存放自身的android系统以及用户数据。当咱们使用从SD卡启动的自制系统之后,也能够将这自身带有的4Gb的flash空间加以利用保存本身的数据。
不过这个过程会将内置flash中的原有数据破坏,所以这里也将介绍如何去备份自身flash的数据并恢复。另外咱们不须要担忧由于破坏了自身android系统致使系统变砖的问题,正像前文[4]提到的,Allwinner A10芯片支持从usb修复的模式,任什么时候候咱们均可以利用官方提供的修复包恢复到出厂时候的状况,不须要担忧变砖。

内置的Nand flash在linux下的设备名:


在咱们的自制系统中,使用sudo fdisk –l查看目前系统可被做为文件系统的设备列表。(若是提示fdisk命令不存在,可使用apt-get安装)

sudo fdisk –l

在输出数据中能够找到相似下图的信息:

图:利用fdisk查看可用的块设备

经过fdisk的输出,咱们能够看到诸如/dev/nandX格式的设备,一共有以下的几个:

Nand Flash设备名

容量

标签

/dev/nanda

16 Mbyte

bootloader

/dev/nandb

2 Mbyte

env

/dev/nandc

33 Mbyte

boot

/dev/nandd

536 Mbyte

system

/dev/nande

1073 Mbyte

data

/dev/nandf

1 Mbyte

misc

/dev/nandg

33 Mbyte

recovery

/dev/nandh

134 Mbyte

cache

/dev/nandi

1 Mbyte

private

/dev/nandj

335 Mbyte

sysrecovery

/dev/nandk

1815 Mbyte

UDISK


上表的设备名和容量是能够从前面的fdisk –l命令中知道的,而对应的标签,能够经过dmesg命令查看系统启动时的log获得:

dmesg | less


而后搜索“nand”字符串,能够找到这块log:

图:kernel驱动的输出信息包含了内部flash设备的分区信息

经过对应的标签名,咱们大体能够猜到这些分区在原生Android系统中的做用。不过相比这个,咱们更关心如何使用这些分区。

数据备份和恢复


咱们接下来对这些内部flash的使用会破坏原始的数据。若是你须要保留这些数据的话,不妨按照这里的操做备份他们。这里如下如今须要备份上表中UDISK标签的那个nand flash块(/dev/nandk)为例子,若是你须要备份全部的分块内容,须要用这里给的命令对每一个分块进行操做。
首先须要明确用于存放备份的目的地有足够的空间,假设咱们保存在自制系统的SD卡上,能够用df –h命令查看如今的剩余空间:

root@linaro-alip:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/mmcblk0p2  7.3G  1.7G  5.3G  24% /
devtmpfs        408M  4.0K  408M   1% /dev
none            408M  4.0K  408M   1% /tmp
none             82M  264K   82M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            408M     0  408M   0% /run/shm
none            408M     0  408M   0% /var/tmp

上面粗体字的一行是当前根文件系统的总大小(7.3G),已用空间和剩余空间(5.3G)。而咱们要备份的分块只要1.8G左右,足够存放。

接下来就是用dd命令备份:

sudo dd if=/dev/nandk of=nandk_backup.img bs=1M

这段命令将/dev/nandk分块设备的内容保存到当前目录下名为nandk_backup.img的文件。

在备份完成后,就能够放心修改这个分块的数据了。从此若是须要将他恢复,一样使用dd命令:

sudo dd of=/dev/nandk if= nandk_backup.img bs=64k



图:恢复以前的分块备份数据

这里能够看到dd这个命令的灵活性,只要对调of(output flow)/if(input flow)参数的内容,就能实现备份和恢复。所以使用上必须小心,若是弄错方向,不但破坏了要备份的数据,更可能有其余灾难后果(写错了地方)。另外你们可能注意到恢复命令的参数(bs=64k)与以前备份的(bs=1M)不一样。这是由于nand flash的特性形成的。Nand flash不像DDR内存那样能够随意读写修改,他的写操做老是伴随着页擦除动做。而一个页通常为64kbyte(或者是其约数),所以制定64kbyte为写入单位能够显著提升性能。

另外若是以为这样直接将原始的分块数据备份太浪费空间,能够在备份的同时加上压缩,使用以下的命令进行备份便可直接产生gzip压缩的备份镜像:

sudo dd if=/dev/nandk | gzip –c > nandk_backup.img.gz

在恢复时使用以下命令:

gunzip –c nandk_backup.img.gz | sudo dd of=/dev/nandk

若是要备份的分块实际内容较少,则能够产生体积相对小的多的备份镜像。

从新格式化而且挂载


要使用每一个分块做为存储设备其实和使用移动硬盘或者u盘操做同样:进行分区、格式化以及挂载。若是熟悉linux下这些操做,就能够跳过这里的介绍了
咱们使用cfdisk这个分区工具简化操做,你也可使用前一篇文章用到的parted命令。
若是系统没有这个程序,能够用apt-get安装:

sudo apt-get install cfdisk

这里仍旧以/dev/nandk这个分块做为例子:

sudo cfdisk /dev/nandk


此时出现以下画面:


图:cfdisk进入画面

 

这里咱们打算将这个分块做为一个分区,则直接选择[NEW],按回车,随后选择[Primary],再逐步确认。最后出现以下画面:

 

图:完成预先分区意向


虽然此时显示已经为分区后的结果,但其实并无真的操做nand flash写入咱们的分区设置,所以此时退出程序还能够撤销。咱们选择[Write]写入咱们的设定,并肯定该操做后(输入yes),选择[Quit]退出便可。

此时能够再运行fdisk确认咱们的分区动做已经执行了:

sudo fdisk –l /dev/nandk


 

图:fdisk提示分区动做已生效

随后咱们将该分区(/dev/nandk1)格式化为ext4文件系统:


图:将分区格式化为ext4文件系统

此时咱们就可使用mount进行挂载了,假设挂在到/media/internal目录上:

sudo mkdir –p /media/internal
mount /dev/nandk1 /media/internal

这样一来,目录/media/internal中的任何文件都会保存在MK802内部的flash空间上了,这个空间大小是大约是1.8G。咱们能够用df –h命令来验证:


图:使用df –h验证内部flash空间已经挂载

能够看到最后一条显示,/media/internal目录属于/dev/nandk1设备,而且有1.7G空间。

从新分块


你们可能会有疑问,前面介绍中MK802使用的是一整块nand flash芯片,为什么如今会有那么多的分块设备?是否能够将这些分块设备合并起来组成一个完整的4Gb分块呢?
答案是确定的。其实这里的分块也是人为设定的,分块的配置一样也保存在这个nand flash芯片上。在linux kernel启动时,由维护nand flash芯片操做的驱动读取分块配置,才有了如今看到的分块组织。
所以咱们只要改写这个分块配置,就能够达到从新分块的效果。这部分的原理和操做将在本文后面的深刻讨论部分介绍。

4.3. CPU超频和频率控制与节能


 

给CPU超频:

MK802这类基于ARM CPU的设备其实均可以超频,只是没有PC那样简单。这里将介绍将MK802 CPU频率从原先的1.008Ghz提升到1.104Ghz。虽然提升的比例幅度不高,只有100Mhz左右,可是从实际体验中,仍是能明显感觉到速度提升的。若是对MK802的性能有所不满,不妨作下尝试,这里也给出我作过超频修改过的hardware pack包,能够按照上一篇文章[4]中的操做,给自制系统加上。

http://www.csksoft.net/data/mk802/mk802_hwpack_cskbuild_overclock.7z

不过须要注意的是这样的超频可能会形成不稳定因素,毕竟官方将他的主频设定在如今的数值上也是通过周密考虑的。所以对于稳定性又要求的应用,请不要用这里的修改。

这里咱们须要修改Linux kernel进行超频,kernel代码采用前一篇文章[4]采用的版本。因为Allwinner A10的kernel带有了cpufreq特性支持,而且代码自己其实已经支持更高频率的设定,所以这里的超频修改显得十分容易:

修改位于kernel代码树下arch/arm/mach-sun4i/cpu-freq/cpu-freq.h这个文件,定位到第43行,将SUN4I_CPUFREQ_MAX宏定义为目标频率1104000000便可:


图:修改cpu-freq.h提升频率变动上限


这个宏用于对于allwinner a10 cpu频率调节(cpufreq.c)模块中的最高频率上限的定义,也就是说cpu的主频能够达到这个宏所定义的频率。

至于为什么是1104000000这样一个数值,主要是根据芯片时钟的PLL倍频和分频参数得来的,对于接触过数字电路时钟部分设计或者单片机时钟编程的朋友会比较熟悉这个话题。不过咱们目前没有a10 cpu的手册,分频参数其实在kernel代码中找到的,见cpu-freq同目录下的cpu-freq-table.c


图:对于不一样a10有效频率范围的定义

 

原来目前的linux kernel已经包含了对更高频率的定义支持,咱们这里的选值正是经过这里的定义得出的。该代码还有更加高的频率能够选择,好比1.2G,个人测试显示,目前1.104Ghz已是能够长期工做的最高频率了,更高的频率会致使系统运行中的随机crash。

在修改了上述代码后还不够,若是是按照上一篇文章介绍的方法编译的kernel,则须要从新设置config,将以前禁用的cpufreq支持从新开启:

 

 

图:增长CPU Freq的支持


随后从新编译kernel,并部署到SD卡上的系统,便可以使用超频了。

不过目前我给出的镜像会在系统系统后将CPU频率模式从新设置成ondemand governor[8]。该模式会随着CPU负载动态调节频率,所以大部分状况下咱们设定的1.104Ghz频率并不会达到。为了强制CPU工做在最高频率,使用下面的命令:

echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor


该命令将设置performance governor,此时查看cpu当前主频,已经显示工做在咱们指望的1.104Ghz了。另外也能够经过/proc/cpuinfo获得验证:


图:验证CPU已经工做在超频

 

节能和性能考虑:


这里从CPU主频的角度考虑MK802的节能问题。这其实天然是主频越高,能耗越大了。所以若是须要节能,MK802的频率就应该下降。上文介绍的超频方案同时也开启了CPU动态调整的支持(CPU Freq)。同时例子中也展现了MK802在运行当中是能够动态改变主频的。
若是打算将MK802用于对能耗有所考虑的场合,则能够考虑加入cpufreq特性的支持,并将系统运行在ondemand的scaling governor上。这样CPU的主频将按当前的负载状态动态调整。而若是须要工做在最大频率,则使用performance governor。对应的,有powersaving governor能够将cpu主频设置到最低主频上。这能够用于实现系统休眠。
不过因为CPU主频调整自己也会消耗性能和时间,并且ondemand模式并不能很是有效的将主频调节到一个正好合适的地步。在实际使用中(主要是图形化桌面操做中),将发现采用了ondemand governor模式,系统会变得很是不流畅。

 5. 小结


本文主要介绍的hack技巧就介绍到这里。在接下来的系列文章将开始介绍如何为MK802平台开发应用程序,而且会介绍一些会用到的工具。一样本文到到此并未结束,下边的部分是为想更进一步了解hack背后细节的朋友准备的。

6. 深刻讨论


这里将结合Kernel代码深刻介绍上述每一个hack的具体细节。这里我就假设读者具备相关的经验了,不过多介绍背景知识。

6.1. Fex脚本的加载和解析过程

uboot阶段


fex的binary形式文件首先会在uboot执行脚本阶段被加载到固定的内存地址上。这部分的代码能够参考uboot脚本mk20050pxd:


setenv boot_mmc 'fatload mmc 0 0x43000000 script.bin; fatload mmc 0 0x48000000 ${kernel}; if fatload mmc 0 0x43100000 uInitrd; then bootm 0x48000000 0x43100000; else bootm 0x48000000; fi'
setenv bootcmd 'run boot.scr setargs boot_mmc'
setenv bootdelay '3'
setenv console 'ttyS0,115200'
setenv extraargs 'rootwait'

上述代码将script.bin(fex脚本)加载到内存的0x43000000区域,随后uboot负责加载linux kernel并跳转至kernel的入口函数。

kernel启动


在Linux kernel启动的前期会执行arch/arm相关的平台初始化代码,其中位于arch/arm/mach-sun4i/sys_config.c文件为fex脚本的对应操做函数代码。其中包含了初始化过程,见LN:586:

int gpio_init(void)
{
    printk("Init eGon pin module V2.0\n");
    gpio_g_pioMemBase = (u32)CSP_OSAL_PHY_2_VIRT(CSP_PIN_PHY_ADDR_BASE , CSP_PIN_PHY_ADDR_SIZE);
    #ifdef FPGA_RUNTIME_ENV
    return script_parser_init((char *)(sys_cofig_data));
    #else
    return script_parser_init((char *)__va(SYS_CONFIG_MEMBASE));
    #endif
}


其中,SYS_CONFIG_MEMBASE宏的定义:

#define   SYS_CONFIG_MEMBASE                 (PLAT_PHYS_OFFSET + SZ_32M + SZ_16M)


恰好能够计算获得地址:0x43000000。也就是script.bin的加载地址。此时fex的操做函数库初始化完毕,该函数库提供了以下主要接口,供其余的驱动模块调用查询fex中的对应配置项:

int script_parser_fetch(char *main_name, char *sub_name, int value[], int count);

所以,只要搜索对函数script_parser_fetch的引用,就能明确整个kernel中对fex脚本的读取代码的位置,以及具体哪些fex项目会被真正生效。


图:经过查找对script_parser_fetch的引用,明确对应的驱动代码

 

各驱动启动


这里之后文接着提到的framebuffer驱动为例,该驱动位于drivers/video/sun4i/disp/dev_fb.c。其中对于script_parser_fetch采用了一个wrapper函数:OSAL_Script_FetchParser_Data:
见LN:42:


图:framebuffer驱动对于fex配置读取的代码

 

这部分的代码将在该设备驱动的加载初始化被执行,从而完成对fex配置的读取和对应模式的设置工做。

6.2. framebuffer设备对Fex脚本的解析配置

上文已经交代了这里所说的framebuffer设备的驱动代码位置以及负责解析fex脚本的代码块。这里回到前文提出的问题,为什么screen0_output_mode设置成9便可?
咱们能够定位到LN:95:

    if(OSAL_Script_FetchParser_Data("disp_init""screen0_output_mode", &value, 1) < 0)
    {
        __wrn("fetch script data disp_init.screen0_output_mode fail\n");
        return -1;
    }
    if(init_para->output_type[0] == DISP_OUTPUT_TYPE_TV || init_para->output_type[0] == DISP_OUTPUT_TYPE_HDMI)
    {
        init_para->tv_mode[0]= (__disp_tv_mode_t)value;
    }
    else if(init_para->output_type[0] == DISP_OUTPUT_TYPE_VGA)
    {
        init_para->vga_mode[0]= (__disp_vga_mode_t)value;
    }

这部分代码即为对screen0_output_mode配置的读取,并按照screen0_output_type设置到对应的域中,这里咱们真正关心的是tv_mode[0]对应类型__disp_tv_mode_t的定义:

typedef enum
{
    DISP_TV_MOD_480I                = 0,
    DISP_TV_MOD_576I                = 1,
    DISP_TV_MOD_480P                = 2,
    DISP_TV_MOD_576P                = 3,
    DISP_TV_MOD_720P_50HZ           = 4,
    DISP_TV_MOD_720P_60HZ           = 5,
    DISP_TV_MOD_1080I_50HZ          = 6,
    DISP_TV_MOD_1080I_60HZ          = 7,
    DISP_TV_MOD_1080P_24HZ          = 8,
    DISP_TV_MOD_1080P_50HZ          = 9,
    DISP_TV_MOD_1080P_60HZ          = 0xa,
    DISP_TV_MOD_1080P_24HZ_3D_FP    = 0x17,
    DISP_TV_MOD_720P_50HZ_3D_FP     = 0x18,
    DISP_TV_MOD_720P_60HZ_3D_FP     = 0x19,
    DISP_TV_MOD_PAL                 = 0xb,
    DISP_TV_MOD_PAL_SVIDEO          = 0xc,
    DISP_TV_MOD_NTSC                = 0xe,
    DISP_TV_MOD_NTSC_SVIDEO         = 0xf,
    DISP_TV_MOD_PAL_M               = 0x11,
    DISP_TV_MOD_PAL_M_SVIDEO        = 0x12,
    DISP_TV_MOD_PAL_NC              = 0x14,
    DISP_TV_MOD_PAL_NC_SVIDEO       = 0x15,
    DISP_TV_MODE_NUM               = 0x1a,
}__disp_tv_mode_t;


这里贴出了该枚举型的定义(include/linux/drv_display_sun4i.h LN:171),能够看到加粗的一行:

DISP_TV_MOD_1080P_50HZ          = 9,

 

 这即是9对应的实际显示模式:1080p 50hz。一样,按照这个枚举定义表,还有更多的配置,诸如DISP_TV_MOD_1080P_24HZ_3D_FP(3D画面?)可使用,这就等待你们挖掘了。对于上面的3D画面模式,我猜测一旦开启后,他会将原先对应2个显示器输出的framebuffer分别用于左右眼的画面,这个有待验证。

经过分析这些代码,咱们还能够知道fex脚本中更多配置的含义和对应的配置值。所以给咱们hack的空间还有不少。

6.3. Nand Flash的驱动部分解读和从新分块


能够经过以前提到的dmesg中的log追查到Nand Flash的驱动代码。它位于:drivers/block/sun4i_nand/。能够看出这是Allwinner专门设计的一个Nandflash驱动库。
从代码结构上看,这个驱动库完成了从NAND flash芯片基本的读写、擦出操做的坏块标注等高级的功能的实现,感受仍是比较高级的。
这里重点关注的是drivers/block/sun4i_nand/nfd/mbr.c这个文件,位于LN:126

 for(part_cnt = 0; part_cnt < mbr->PartCount && part_cnt < MAX_PART_COUNT; part_cnt++)
 {
     if((mbr->array[part_cnt].user_type == 2) || (mbr->array[part_cnt].user_type == 0))
     {
   PRINT("The %d disk name = %s, class name = %s, disk size = %d\n", part_index, mbr->array[part_cnt].name,
      mbr->array[part_cnt].classname, mbr->array[part_cnt].lenlo);

         disk_array[part_index].offset = mbr->array[part_cnt].addrlo;
   disk_array[part_index].size = mbr->array[part_cnt].lenlo;
   part_index ++;
     }
 }

加粗代码就是产生以前提到的nand分块和对应标签名称log的部分。分析代码能够知道这些信息来自于全局结构mbr,而该结构体数据在该文件LN:82获得加载:

if(LML_Read((MBR_START_ADDRESS + MBR_SIZE*i)/512,MBR_SIZE/512,mbr) == 0)


从函数LML_Read的定义注释(sun4i_nand/src/logic/logic_ctl.c) :

Description: Read data from logic disk area to buffer

能够知道,这个函数的做用就是把(MBR_START_ADDRESS + MBR_SIZE*i)/512地址的flash空间数据读取到mbr指向的内存中,而mbr的结构定义是:

/* mbr info */
typedef struct tag_MBR{
 __u32 crc32;     // crc, from byte 4 to mbr tail
 __u32 version;     // version
 __u8  magic[8];     // magic number
 __u8  copy;      // mbr backup count
 __u8  index;     // current part no
 __u16   PartCount;    // part counter
 PARTITION array[MAX_PART_COUNT];// part info
 __u8 res[MBR_RESERVED];         // reserved space
}MBR;

以及其中的PARTITION array的类型定义:
/* part info */
typedef struct tag_PARTITION{
 __u32 addrhi;    //start address high 32 bit
 __u32 addrlo;    //start address low 32 bit
 __u32 lenhi;    //size high 32 bit
 __u32 lenlo;    //size low 32 bit
 __u8  classname[12];  //major device name
 __u8  name[12];    //minor device name
 unsigned  int       user_type;          
 unsigned  int       ro;               
 __u8  res[16];    //reserved
}PARTITION;

这里咱们很明确的能够知道控制nand flash区块的数据存在地址,以及如何来修改它了:只要本身构造一系列PARTITION array对象,并会写到这段区域便可。

不过我发现其实这个工做社区中已经有人作了[7]。能够将它的代码作必要修改后并编译后,修改MK802 内部nand flash的分区表,这样就能够实现咱们前面所说的区块合并了。


7. 参考文献


[1] Allwinner A10 - ARM Cortex A8 SoC
http://rhombus-tech.net/allwinner_a10/

 

[2] Mali (GPU)
http://en.wikipedia.org/wiki/Mali_(GPU)

 

[3] Allwinner A10 devices
http://wiki.xbmc.org/index.php?title=Allwinner_A10_devices

 

[4] 基于MK802 MiniPC的扩展开发应用--系统自制
http://www.csksoft.net/blog/post/mk802_dev_sysbuild.html

 

[5] 给MK802(USB大小的Android4.0小PC)引出串口信号,变成ARM开发版
http://www.csksoft.net/blog/post/288.html

 

[6] PuTTY: A Free Telnet/SSH Client
http://www.chiark.greenend.org.uk/~sgtatham/putty/

 

[7] 修改内部nand flash分区的工具(git commit)
https://github.com/amery/sunxi-tools/commit/c8ae7438a79e8973cf572b4cbf6206c658f8e943

 

[8] CPU Governors explained
http://forum.xda-developers.com/showthread.php?t=1736168

相关文章
相关标签/搜索