iTOP-4412全能版采用四核Cortex-A9,主频为1.4GHz-1.6GHz,配备S5M8767 电源管理,集成USB HUB,选用高品质板对板连接器稳定可靠,大厂生产,做工精良。接口一应俱全,开发更简单,搭载全网通4G、支持WIFI、蓝牙、陀螺仪、CAN总线、RS485总线、500万摄像头等模块,稳定运行Android 4.0.3/Android 4.4操作,系统通用Linux-3.0.15+Qt操作系统(QT支持5.7版本),Ubuntu版本:12.04,接口智能分配 方便好用。
【交流群2】258811263(获取开源资料)
【迅为B站】北京迅为电子的个人空间-北京迅为电子个人主页-哔哩哔哩视频
第九十章 Android系统开发
90.1 Android4.0硬件抽象层HAL控制LED灯
90.1.1 原理简单介绍
配套资料在网盘资料的“iTOP4412开发板资料汇总(不含光盘内容)\iTOP-4412开发板系统开发资料\iTOP-4412-Android4.0-硬件抽象层HAL之led例程.zip”
在 Android 中,我们使用 Android 的 SystemServer 向 ServiceManager 来将硬件的功能添加为一个服务,这样当一个 APP 需要使用硬件的时候就向SystemServer 发出请求 service 服务,然后由 ServiceMnager 统一提供服务,提供统一的接口与硬件控制,即相当于多添加了一层,从而实现解耦。
1.SystemServer 会加载 Cpp lib
2.在 JNI_OnLoad 中注册各个 Service,SystemServer 向 ServiceManager 添加服务
3.这些 service 就包括像串口/LED 等硬件相关的服务
90.1.2 使用过程
- AddService:SystemServer 向 ServiceManager 添加服务 addServeice
- getservice:通过 getservice 来从 SystemServer 注册了的 service 中获取服务所具有的功能,例如 ledctrl
- 使用 Service 的方法:APP 使用一个 Interface(即以 i 开头的对象)来使用service 提供的功能,将服务请求到 SystemServer 去。
90.1.3 定义硬件访问服务接口
文件:/home/4412elite/iTop4412_ICS_git/frameworks/base/core/java/android/os/ILedService.aidl
ILedService.aidl 文件的代码如下
package android.os;
/*
* {@hide}
*/
interface ILedService
{
int ledCtrl(int which, int status);
}
进入到 frameworks/base 目录中打开里面的 Android.mk 文件进行修改(如图 1 所示),后使用 mmm ./frameworks/base/命令编译(如图 2 所示),如果在使用 mmm 提示如图 3 所示时表示识别不了 mmm 命令,此时你可以先执行source ./build/envsetup.sh 激活 mmm 命令如图 4 所示
LOCAL_SRC_FILES +=\
......
90.1.4 实现硬件访问服务层
frameworks/base/services/java/com/android/server/LedService.java
LedService.java 代码如下
package com.android.server;
import android.os.ILedService;
public class LedService extends ILedService.Stub
{
private static final String TAG = "LedService";
/* call native c function to access hardware */
public int ledCtrl(int which, int status) throws android.os.RemoteException
{
return native_ledCtrl(which, status);
}
public LedService() {
native_ledOpen();
}
public static native int native_ledOpen();
public static native void native_ledClose();
public static native int native_ledCtrl(int which, int status);
}
然后在 root@ubuntu:/home/4412elite/iTop4412_ICS_git# 目录下使用
mmm ./frameworks/base/services/java/命令,编译后得到services.jar
90.1.5 实现硬件访问服务的 JNI 方法
frameworks/base/services/jni/com_android_server_LedService.cpp 代码如下
#define LOG_TAG "LedService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <hardware/led_hal.h>
namespace android
{
static led_device_t* led_device;
jint ledOpen(JNIEnv *env, jobject cls)
{
jint err;
hw_module_t* module;
hw_device_t* device;
LOGI("native ledOpen ...");
/* 1. hw_get_module */
err = hw_get_module("led", (hw_module_t const**)&module);
if (err == 0) {
/* 2. get device : module->methods->open */
err = module->methods->open(module, NULL, &device);
if (err == 0) {
/* 3. call led_open */
led_device = (led_device_t *)device;
return led_device->led_open(led_device);
} else {
return -1;
}
}
return -1;
}
void ledClose(JNIEnv *env, jobject cls)
{
//ALOGI("native ledClose ...");
//close(fd);
}
jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
LOGI("native ledCtrl %d, %d", which, status);
return led_device->led_ctrl(led_device, which, status);
}
static const JNINativeMethod methods[] = {
{"native_ledOpen", "()I", (void *)ledOpen},
{"native_ledClose", "()V", (void *)ledClose},
{"native_ledCtrl", "(II)I", (void *)ledCtrl},
};
int register_android_server_LedService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/LedService",
methods, NELEM(methods));
}
}
同时修改同一目录下(frameworks/base/services/jni/onload.cpp)的onload.cpp 和 Android.mk,如图 7,8,9 所示(修改内参考文后给出的链接) 在这里编译会得到 libandroid_servers.so 文件
编译成功后如图 11 所示、
如果出现如下图所示的报错情况,跟随如下操作
解决办法 找到这个 SufaceFlinger.cpp 文件,注释掉这个 mHdmiClient 这个变量就好了如图10 所示
在你编译 jni 文件中的内容是可能会遇到的问题。
Copying: out/target/common/obj/APPS/SystemUI_intermediates/noproguard.classes.jar target Dex: SystemUI
target Symbolic: libandroid_servers (out/target/product/smdk4x12/symbols/system/lib/libandroid_servers.so)
target Strip: libandroid_servers (out/target/product/smdk4x12/obj/lib/libandroid_servers.so) Install: out/target/product/smdk4x12/system/lib/libandroid_servers.so out/target/common/obj/PACKAGING/public_api.txt:14807: error 3: Added class ILedService to package android.os
out/target/common/obj/PACKAGING/public_api.txt:14811: error 3: Added class ILedService.Stub to package android.os
******************************
You have tried to change the API from what has been previously approved.
To make these errors go away, you have two choices:
1)You can add "@hide" javadoc comments to the methods, etc. listed in the errors above.
2)You can update current.txt by executing the following command: make update-api
To submit the revised current.txt to the main Android repository, you will need approval.
解决方法:使用 make update-api
90.1.6 启动硬件访问服务
就是修改目录下 frameworks/base/services/java/com/android/server 中的SystemServer.java 文件,如图 12 所示(修改内容参考文后链接)然后使用mmm ./frameworks/base/services/java 编译文件(在这里不用修改 Android.mk 文件)
90.1.7 开发Android应用程序来使用硬件访问服务
到了最后就可以使用这些服务了, 但是要使用之前还需要添加包含了LedService 的模块,这个模块其实是 framework,但是因为我们是 java,而framework 属于 dex 格式,因此我们需要添加 jar 格式的包,这个包编译完成后, 位于,
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
如图 13 所示,把这个 class.jar 复制出来
如果你用的是 Android Studio 导入过程可以参考文后给的链接,如果你用的是eclipse 请照如下操作(跟我们平时导入 jar 包一样)
1)鼠标右击创建好的 Android 项目,悬着 BuildPath
2)选中 Add External...那个选项,然后添加 classes.jar 就可以了
如图 14 所示
关键代码如下
private ILedService iledService=null;
iledService=ILedService.Stub.asInterface(ServiceManager.getService("led"));
iledService.ledCtrl(0, 1);//第一个参数是哪一个灯,第二参数是灯的状态
90.1.8 编写硬件抽象层模块接口
hardware/libhardware/include/hardware/led.h
hardware/libhardware/Modules/led/led.cpp
Android.mk
使用 mmm 命令编译后会的到 led.default.so 这个动态库在out/target/producet/generic/system/lib/hw 目录下可以找到然后把这个动态库 push 到控制台下的 system/lib/hw 目录下
如下图所示:(eclipse 中的 DDMS 下)
如果你在测试自己写的 APP 时出现如下错误(图 15),是因为你在编译生成 led.default.so 动态库后没有把其放入到 system\lib\hw 目录下(控制台模式下)
Push 的方法如下
adb remount
adb push led.default.so system/lib/hw
adb reboot
相关的参考链接
http://blog.sina.com.cn/s/blog_9ac48e3c0102xfah.html
http://www.kancloud.cn/digest/imx6-android/148867
90.2 iTOP-4412-Android4.4系统HDMI移植教程
90.2.1 概述
近期需要把 Android4.4 KitKat 的 HDMI 高清输出功能移植到 iTOP-4412 开发平台, 查阅了相关资料,经过一段时间的研究、调试,终于成功输出 1080P 的图像到电视,当然也支持 720P 的输出了,这里笔者记录移植过程及注意事项,方便有相同需求的客户作为技术参考。
HDMI 基础知识
HDMI(High-DefinitionMultimedia Interface)又被称为高清晰度多媒体接口,是首个支持在单线缆上传输,不经过压缩的全数字高清晰度、多声道音频和智能格式与控制命令数据的数字接口。HDMI 接口由 Silicon Image 美国晶像公司倡导,联合索尼、日立、松下、飞利浦、汤姆逊、东芝等八家著名的消费类电子制造商联合成立的工作组共同开发的。
HDMI 传输原理
HDMI 采用 TMDS (TimeMinimized Differential Signal)最小化传输差分信号传输技术 , TMDS 是一种微分信号机制,采用的是差分传动方式 ,是一种利用 2 个引脚间电压差来传送信号的技术。每一个标准的 HDMI 连接,都包含了 3 个用于传输数据的 TMDS 传输通道,还有 1 个独立的 TMDS 时钟通道,以保证传输时所需的统一时序。在一个时钟周期内,每个 TMDS 通道都能传送 10bit 的数据流。而这 10bit 数据,可以由若干种不同的编码格式构成。
所用到的术语:
HDMI 把视频信号分为 R、G、B、H、V 五种信号用 TMDS 技术编码。
TMDS:这三个通道传输 R、G、B 三原色,HV 编码在 B 信号通道里面传输,R、G 的多余位置用来传输音频信号。
DDC :即显示数据通道,用来向视频接收装置发送配置信息和数据格式信息,接收装置读取这些
E-EDID(增强扩展显示识别数据)的信息。
CEC:即消费电子控制通道,通过这条通道可以控制视听设备的工作。
HDMI 数据容量
HDMI 电路中的时钟频率,在 1.0 版本规定为 25MHz-165MHz 之间,也就是说一个 TMDS 通道每秒最多能传输 165MHz×10bit=1.65Gbit 的数据,3 个 TMDS 通道一秒就可以传输 1.65×3=4.95Gbit 的数据,再加上控制数据,用标准方法表示就是 4.96Gbps 的带宽;若传输信号的比率小于 25MHz,HDMI 会采用自动循环技术填补码率,将信号的码率提升到 25MHz 的水平。
如果用像素点来表示,那就是一秒可以传输显示 1.65G 个像素点(一个完整的像素点信息由R/G/B 三原色信息构成)所需要的数据量。
在 1.3 版本规格中,TMDS 连接带宽从原来最高 165MHz 提升到 340MHz,数据传输率也从 4.96Gbps 提升到了 10.2Gbps,可以支持支持更高数据量的高清数字流量,如果采用 Type B 型双路 TMDS 连接,则可以在此基础上再提升一倍系统带宽。
HDMI 数据传输
HDMI 输入的源编码格式包括视频像素数据(8 位)、控制数据(2 位)和数据包(4 位)。其中数据包中包含有音频数据和辅助信息数据。数据传输过程可以分成三个部分:视频数据传输期、岛屿数据传输期和控制数据传输期。
视频数据传输期:HDMI 数据线上传送视频像素信号,视频信号经过编码,生成 3 路(即 3 个TMDS 数据信息通道,每路 8 位)共 24 位的视频数据流,输入到 HDMI 发射器中。24 位像素的视频信号通过 TMDS 通道传输,将每通道 8 位的信号编码转换为 10 位,在每个 10 位像素时钟周期传送一个最小化的信号序列,视频信号被调制为 TMDS 数据信号传送出去,最后到接受器中接收。
HDMI 音频功能
传统的数字音频信号的传输主要依靠两种途径:同轴电缆和光纤传输。
同轴电缆传输数字音频信号是一种非常成熟且高质量的方式。这种接口标准对设备端的硬件要求较低,但是在传输高频信号时,容易发生比较大的衰减,影响到最终音质。
光纤对设备接收、发射端的同步时许要求非常严格,在技术上比同轴要难于实现,但是光纤技术在长距离传输方面的优势非常明显,不会出现同轴电缆长距离衰减过大的问题,因此也得到了很多有距离限制以及新装修用户的青睐。
HDMI 技术则综合了以上两者的优点:物理层采用成熟的电缆连接。HDMI 理论上可以实现最高 20 米的无损耗数字音频信号传播,那些对距离有要求的用户也能较好接受。
HDMI 接口类型
常见的 HDMI 类型有 A、B、C 三种类型。其中 A 型是标准的 19 针 HDMI 接口,普及率最高;B 型接口尺寸稍大,但是有 29 个引脚,可以提供双 TMDS 传输通道。而 C 型接口和 A 型接口性能一致,但是体积较小,更加适合紧凑型便携设备使用。接口 A、接口 B、接口 C
注: iTOP4412 开发板 HDMI 采用的是 TYPE C 接 口
HDMI特点
1、更好的抗干扰性能,能实现最长 20 米的无增益传输。
2、针对大尺寸数字平板电视分辨率进行优化,兼容性好。
3、支持 EDID 和 DDC2B 标准,设备之间可以智能选择最佳匹配的连接方式。
4、拥有强大的版权保护机制(HDCP),有效防止盗版现象。
5、支持 24bit 色深处理,(RGB、YCbCr4-4-4、YCbCr4-2-2)。
6、接口体积小,各种设备都能轻松安装。
7、一根线缆实现数字音频、视频信号同步传输,有效降低使用成本和繁杂程度。
8、完全兼容 DVI 接口标准,用户不用担心新旧系统不匹配。
9、支持热插拔技术。
90.2.2 移植环境
1、iTOP-4412 开发平台
2、kernel 3.0.15 version
3、Android4.4.4
4、Ubuntu12.04 64BIt 开发环境
注:笔者移植过程中查询了 HDMI 相关的一些技术资料,在此感谢 优快云 博主对 HDMI 的基础分析:http://blog.youkuaiyun.com/xubin341719/article/details/7713450,以上 HDMI 基本概念描述转载自此博客.
90.2.3 硬件相关部分
下图为 iTop4412 开发板底板 HDMI 接口引脚定义:
下图为 iTop4412 开发板核心板 HDMI 相关引脚原理图:
原理图结合 HDMI 接口定义标准我们可以获知:
1、TMDS_D0- TMDS_D0+, TMDS_D1- TMDS_D1+, TMDS_D2- TMDS_D2+ 三对数据线用于传输视频和音频及控制信号;
2、TMDS_CLK+ TMDS_CLK- 为 HDMI 传输提供时钟源;
3、SCL,SDA 为 I2C 控制信号,用于 EDID 协议传输,主要用于板卡与 HDMI 显示设备之间进行协商,比如查询 HDMI 显示设备支持的最大分辨率,板卡设置 HDMI 输出分辨率等等均通过 I2C 总线传输,iTop4412 开发平台采用 I2C0 传输 EDID。
I2C 作为 HDMI 的 DDC 通道,用于设备之间的沟通。
4、HDMI_HPD 引脚用于产生热插拔中断信号,CPU 端通过此信号可以知道有 HDMI 设备插入或者拔出,驱动程序会处理中断,告知到 Android 层。
5、CEC引脚用于 HDMI 的高级客户定制功能,用于传输厂商自定义的命令,属于HDMI 的拓展功能,比如 HDMI 发送端设备可以通过 CEC 引脚告知 HDMI 显示设备随同发送设备开机,关机等操作。
90.2.4 Kernel
概述
iTOP-4412 开发板采用的内核是 Linux 3.0.15 版本,我们这边没有三星官方的关于HDMI 的 PortingGuid, 只能是自己根据 Exynos4412 的 Datasheet 结合三星提供的内核代码进行分析。
我们先看一下 Exynos4412 Datasheet 中关于 HDMI 功能的属性支持:
The features of HDMI are:
· Complies with HDMI 1.4 (3D feature), HDCP 1.1, and DVI 1.0
· The video formats that HDMI supports are:
- 480p 59.94 Hz/60 Hz, 576p @ 50 Hz
- 720p @ 50 Hz/59.94 Hz/60 Hz
- 1080i @ 50 Hz/59.94 Hz/60 Hz
- 1080p @ 50 Hz/59.94 Hz/60 Hz
· Supports other various formats up to 148.5 MHz Pixel Clock
· Supports Color Format: 4:4:4 RGB/YCbCr
· Supports 8-bit precision per color only
· Supports CEC function
· Contains an Integrated HDCP Encryption Engine for video/ audio content protection
· Does not include DDC. There is a dedicated Inter-Integrated Circuit (I2C) for DDC in Exynos 4412 SCP
peripheral Bus
可以知道 HDMI 控制器支持 HDMI1.4,HDCP1.1,DVI1.0 规范,另外支持最高 1080P 60HZ 的显示,当然也支持最常见的 480P ,720P,1080i 输出,另外 HDMI 输出的颜色格式可以是 RGB 的也可以是 YUV 的,这个可以通过软件界面进行输出设置。
DDC 全文为 Display Data Channel,用于 HDMI 设备之间的协议沟通,Exynos4412 内部没有专用的 DDC 控制器,而是采用 I2C 总线完成这部分工作,驱动部分使用 I2C 传输控制命令到显示终端设备。
系统框图:
HDMI 的视频数据是通过 MIXER 输入到 HDMI CORE 核心,然后通过 PHY 发送出去,MIXER 是视频混合器,用于图层的混合。音频数据源有两路,一路是 SPDIF 总线输入, 另外一路是 I2S 音频总线输入,我们的开发板采用的是 I2S 的音频源,故音频输入是通过 I2S 传输到 HDMI CORE 的。
另外需要注意一下 HDMI PHY,Exynos4412 集成了 HDMI PHY,PHY 用于产生 pixel 时钟和 TMDS 时钟,我们不再需要额外的 PHY 芯片,这样可以省去 PCB 布线,当然还有cost,软件通过 CPU 内部的专用的 I2C 总线配置 Phy 寄存器,针对 Phy 进行控制,比如开启,关闭 PHY 电源等等。
HDMI 功能在 Exynos4412 平台中属于 TVOUT 子系统的一部分,图形图像数据可以输出到 TV 显示设备,也可以输出到 HDMI 显示设备 :
VideoProcessor 硬件模块从内存获取到 YUV420 格式的图像数据进行裁剪,及空间色彩转换,然后把数据传输到 MIXER 硬件模块,Dataheet 是这样解释 VideoProcessor 的功能定义:
Video Processor (VP) is responsible for video scaling, de-interlacing, and video post processing of TV-out data path. VP reads reconstructed YCbCr 4:2:0 video sequences from DRAM. It then processes the sequence, and sends it to on-the- fly Mixer.
MIXER 模块主要是对VideoProcessor输入的图形,视频,背景进行混合叠加,形成完成的窗口显示,然后把数据传输到TVENC进行编码,数模转换,输出到TV设备,或者把数据传输到HDMI模块,由HDMI的PHY把数据传输到HDMI接收显示设备。
Mixer overlaps or blends the input data such as graphic, video, background and sends the resulting data to the TVOUT module. The TVOUT module generates all the video control signals.
如果您对MIXER的功能作用不是很清楚,可以看一下Datasheet中对MIXER混合器描述的图例:
通过上面的解释,我们知道内存数据是如何显示到HDMI设备上的,这样对HDMI就有了一个框架性的认识和理解,方便我们分析 Linux HDMI驱动结构,及驱动模块在HDMI使用中扮演怎么样的角色。
内核代码
这里我们把 HDMI 驱动相关划分为两部分,一部分是驱动文件,驱动文件实现了HDMI 的驱动架构。另外一部分是板级支持文件,与 iTop4412 开发板相关的文件。
驱动文件
首先我们看一下 HDMI 驱动相关文件夹,因为 HDMI 属于 TVOUT 子系统的一部分,那么HDMI 驱动是离不开 TV 输出系统的:
路径:iTop4412_Kernel_3.0/drivers/media/video/Samsung/tvout
TVOUT 文件夹为 TVOUT 子系统的驱动,HDMI 驱动就位于其中:
这里我们看到了 s5p_tvout_hpd.c 关于 HDMI 热插拔事件相关驱动,该驱动文件会产生
/dev/HPD 设备节点,用户态软件会打开设备节点,用于监控 HDMI 设备的插入和拔出。
s5p_mixer_ctrl.c 视频混合器,s5p_vp_ctrl.c 图形裁剪驱动,这些文件是 TV 驱动调用的接口文件,实际的实现位于 hw_if 文件夹下面,hw_if 文件夹下面的这些文件会操作最底层的寄存器配置:
比如 hdmi.c 文件包含对 HDMI 控制寄存器,状态寄存器,及其他功能寄存器的配置, 当然也包含了对 HDMI PHY 配置:
voids5p_hdmi_reg_enable(boolen)
{
u8reg; reg=readb(hdmi_base+S5P_HDMI_CON_0);
if(en)
reg|=S5P_HDMI_EN;
else
reg&=~(S5P_HDMI_EN|S5P_HDMI_ASP_EN);
writeb(reg,hdmi_base+S5P_HDMI_CON_0);
if(!en){
do{
reg=readb(hdmi_base+S5P_HDMI_CON_0);
}while(reg&S5P_HDMI_EN);
}
}
s32s5p_hdmi_phy_config(enumphy_freqfreq,enums5p_hdmi_color_depthcd)
{
s32index; s32size;
u8buffer[32]={0,};
u8reg; intloop=0;
switch(cd){
caseHDMI_CD_24:
index=0;
break;
caseHDMI_CD_30:
index=1;
break;
caseHDMI_CD_36:
index=2;
break;
default:
return-1;
}
buffer[0]=PHY_REG_MODE_SET_DONE; buffer[1]=0x00;
if(s5p_hdmi_i2c_phy_write(PHY_I2C_ADDRESS,2,buffer)!=0){
tvout_err("s5p_hdmi_i2c_phy_writefailed.\n");
return-1;
}
writeb(0x5,i2c_hdmi_phy_base+HDMI_I2C_LC);
size=sizeof(phy_config[freq][index])/sizeof(phy_config[freq][index][0]);
memcpy(buffer,phy_config[freq][index],sizeof(buffer));
if(s5p_hdmi_i2c_phy_write(PHY_I2C_ADDRESS,size,buffer)!=0)
return-1;
#ifdefCONFIG_HDMI_PHY_32N
buffer[0]=PHY_REG_MODE_SET_DONE;
buffer[1]=0x80;
if(s5p_hdmi_i2c_phy_write(PHY_I2C_ADDRESS,2,buffer)!=0){
tvout_err("s5p_hdmi_i2c_phy_writefailed.\n");
return-1;
}
#else buffer[0]=0x01;
if(s5p_hdmi_i2c_phy_write(PHY_I2C_ADDRESS,1,buffer)!=0){
tvout_err("s5p_hdmi_i2c_phy_writefailed.\n");
return-1;
}
#endif s5p_hdmi_print_phy_config();
#ifndefCONFIG_HDMI_PHY_32N s5p_hdmi_reg_core_reset(); #endif
#ifdefCONFIG_HDMI_PHY_32N
do{
reg=readb(hdmi_base+S5P_HDMI_PHY_STATUS0);
}while(!(reg&S5P_HDMI_PHY_STATUS_READY));
#else
do{
reg=readb(hdmi_base+S5P_HDMI_PHY_STATUS);
mdelay(5);
loop++;
if(loop==100)
return-1; //addedyqf,forrobust
}while(!(reg&S5P_HDMI_PHY_STATUS_READY));
#endif
writeb(I2C_CLK_PEND_INT,i2c_hdmi_phy_base+HDMI_I2C_CON);
writeb(I2C_IDLE,i2c_hdmi_phy_base+HDMI_I2C_STAT);
return0;
}
Mixer.c 文件包含了对视频混合器的底层配置:
voids5p_mixer_start(void)
{
writel((readl(mixer_base+S5P_MXR_STATUS)|S5P_MXR_STATUS_RUN), mixer_base+S5P_MXR_STATUS);
}
voids5p_mixer_stop(void)
{
u32reg=readl(mixer_base+S5P_MXR_STATUS);
reg&=~S5P_MXR_STATUS_RUN;
writel(reg,mixer_base+S5P_MXR_STATUS);
do{
reg=readl(mixer_base+S5P_MXR_STATUS);
}while(!(reg&S5P_MXR_STATUS_IDLE_MODE));
}
这些驱动文件中当然有一个主文件,作为驱动的入口文件,他就是 s5p_tvout.c 文件, 该文件提供了驱动注册函数,另外构建了 VideoProcessor 对象,MIXER 视频混淆器对象,还有非常重要的 V4L2 用户态调用接口,用户态程序是通过 V4L2 接口控制 HDMI 的输出的。
static int __devinit s5p_tvout_probe(struct platform_device *pdev)
{
s5p_tvout_pm_runtime_enable(&pdev->dev);
#if defined(CONFIG_S5P_SYSMMU_TV) && defined(CONFIG_VCM)
if (s5p_tvout_vcm_create_unified() < 0)
goto err;
if (s5p_tvout_vcm_init() < 0)
goto err;
#elif defined(CONFIG_S5P_SYSMMU_TV) && defined(CONFIG_S5P_VMEM)
s5p_sysmmu_enable(&pdev->dev);
printk("sysmmu on\n");
s5p_sysmmu_set_tablebase_pgd(&pdev->dev, __pa(swapper_pg_dir));
#endif
#ifndef CONFIG_TC4_EVT //yqf
tv_regulator_vdd18 = regulator_get(NULL, "vdd18_mipi");
if (IS_ERR(tv_regulator_vdd18)) {
printk("%s: failed to get %s\n", __func__, "vdd18_mipi");
goto err_regulator;
}
//regulator_enable(tv_regulator_vdd18);
tv_regulator_vdd10 = regulator_get(NULL, "vdd10_mipi");
if (IS_ERR(tv_regulator_vdd10)) {
printk("%s: failed to get %s\n", __func__, "vdd10_mipi");
goto err_regulator;
}
//regulator_enable(tv_regulator_vdd10);
#endif
if (s5p_tvout_clk_get(pdev, &s5ptv_status) < 0)
goto err;
if (s5p_vp_ctrl_constructor(pdev) < 0)
goto err;
/* s5p_mixer_ctrl_constructor must be called
before s5p_tvif_ctrl_constructor */
if (s5p_mixer_ctrl_constructor(pdev) < 0)
goto err;
if (s5p_tvif_ctrl_constructor(pdev) < 0)
goto err;
if (s5p_tvout_v4l2_constructor(pdev) < 0)
goto err;
#ifdef CONFIG_HAS_EARLYSUSPEND
spin_lock_init(&s5ptv_status.tvout_lock);
s5ptv_early_suspend.suspend = s5p_tvout_early_suspend;
s5ptv_early_suspend.resume = s5p_tvout_late_resume;
s5ptv_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; //addedyqf, suspend before LCD
register_early_suspend(&s5ptv_early_suspend);
suspend_status = 0;
#endif
#ifdef CONFIG_TV_FB
#ifndef CONFIG_USER_ALLOC_TVOUT
clk_enable(s5ptv_status.i2c_phy_clk); //added yqf
s5p_hdmi_phy_power(true);
if (s5p_tvif_ctrl_start(TVOUT_720P_60, TVOUT_HDMI) < 0) //yqf, back later
goto err;
#endif
/* prepare memory */
if (s5p_tvout_fb_alloc_framebuffer(&pdev->dev))
goto err;
if (s5p_tvout_fb_register_framebuffer(&pdev->dev))
goto err;
#endif
on_stop_process = false;
on_start_process = false;
return 0;
#ifndef CONFIG_TC4_EVT
err_regulator:
regulator_put(tv_regulator_vdd18);
regulator_put(tv_regulator_vdd10);
#endif
err:
return -ENODEV;
}
static const struct dev_pm_ops s5p_tvout_pm_ops = {
.suspend = s5p_tvout_suspend,
.resume = s5p_tvout_resume,
.runtime_suspend = s5p_tvout_runtime_suspend,
.runtime_resume = s5p_tvout_runtime_resume
};
static struct platform_driver s5p_tvout_driver = {
.probe = s5p_tvout_probe,
.remove = s5p_tvout_remove,
.driver= {
.name = "s5p-tvout",
.owner = THIS_MODULE,
.pm = &s5p_tvout_pm_ops
},
};
static char banner[] __initdata =KERN_INFO "S5P TVOUT Driver v3.0 (c) 2010 Samsung Electronics\n";
static int __init s5p_tvout_init(void)
{
int ret;
printk(banner);
ret = platform_driver_register(&s5p_tvout_driver);
if (ret) {
printk(KERN_ERR "Platform Device Register Failed %d\n", ret);
return -1;
}
#ifdef CONFIG_PM
tvout_resume_wq = create_freezable_workqueue("tvout resume work");
if (!tvout_resume_wq) {
printk(KERN_ERR "Platform Device Register Failed %d\n", ret);
platform_driver_unregister(&s5p_tvout_driver);
return -1;
}
INIT_WORK(&tvout_resume_work, (work_func_t) s5p_tvout_resume_work);
#endif
return 0;
}
static void __exit s5p_tvout_exit(void)
{
#ifdef CONFIG_HAS_EARLYSUSPEND
mutex_destroy(&s5p_tvout_mutex);
#endif
platform_driver_unregister(&s5p_tvout_driver);
}
late_initcall(s5p_tvout_init);
module_exit(s5p_tvout_exit);
板级相关文件,文件列表:
iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
iTop4412_Kernel_3.0/arch/arm/plat-s5p/dev-tvout.c
iTop4412_Kernel_3.0/arch/arm/mach-exynos/setup-tvout.c
mach-itop4412.c 文件为 iTop4412 开发板的入口文件,该文件定义了 iTop4412 开发板的所有板载资源,当然也包括 HDMI 相关的设备:
static struct platform_device *smdk4x12_devices[] __initdata = {
…
#ifdef CONFIG_VIDEO_TVOUT
&s5p_device_tvout,
&s5p_device_cec,
&s5p_device_hpd,
#endif
……
}
static void __init smdk4x12_machine_init(void)
{
……
#if defined(CONFIG_VIDEO_TVOUT)
s5p_hdmi_hpd_set_platdata(&hdmi_hpd_data);
s5p_hdmi_cec_set_platdata(&hdmi_cec_data);
#ifdef CONFIG_EXYNOS_DEV_PD
s5p_device_tvout.dev.parent = &exynos4_device_pd[PD_TV].dev;
exynos4_device_pd[PD_TV].dev.parent= &exynos4_device_pd[PD_LCD0].dev;
#endif
…….
}
■ dev-tvout.c 文件定义了 HDMI 系统相关的设备,如 VideoProcess,Mixer 视频混
淆器占用的系统资源,如寄存器地址,中断
/* TVOUT interface */
static struct resource s5p_tvout_resources[] = {
[0] = {
.start = S5P_PA_TVENC,
.end = S5P_PA_TVENC + S5P_SZ_TVENC - 1,
.flags = IORESOURCE_MEM,
.name = "s5p-sdo"
},
[1] = {
.start = S5P_PA_VP,
.end = S5P_PA_VP + S5P_SZ_VP - 1,
.flags = IORESOURCE_MEM,
.name = "s5p-vp"
},
[2] = {
.start = S5P_PA_MIXER,
.end = S5P_PA_MIXER + S5P_SZ_MIXER - 1,
.flags = IORESOURCE_MEM,
.name = "s5p-mixer"
},
[3] = {
.start = S5P_PA_HDMI,
.end = S5P_PA_HDMI + S5P_SZ_HDMI - 1,
.flags = IORESOURCE_MEM,
.name = "s5p-hdmi"
},
[4] = {
.start = S5P_I2C_HDMI_PHY,
.end = S5P_I2C_HDMI_PHY + S5P_I2C_HDMI_SZ_PHY - 1,
.flags = IORESOURCE_MEM,
.name = "s5p-i2c-hdmi-phy"
},
[5] = {
.start = IRQ_MIXER,
.end = IRQ_MIXER,
.flags = IORESOURCE_IRQ,
.name = "s5p-mixer"
},
[6] = {
.start = IRQ_HDMI,
.end = IRQ_HDMI,
.flags = IORESOURCE_IRQ,
.name = "s5p-hdmi"
},
[7] = {
.start = IRQ_TVENC,
.end = IRQ_TVENC,
.flags = IORESOURCE_IRQ,
.name = "s5p-sdo"
},
};
struct platform_device s5p_device_tvout = {
.name = "s5p-tvout",
.id = -1,
.num_resources = ARRAY_SIZE(s5p_tvout_resources),
.resource = s5p_tvout_resources,
};
EXPORT_SYMBOL(s5p_device_tvout);
当然也包括设备节点 /dev/HPD 所使用的资源:
/* HPD */
static struct resource s5p_hpd_resources[] = {
[0] = {
.start = IRQ_TVOUT_HPD,
.end = IRQ_TVOUT_HPD,
flags = IORESOURCE_IRQ,
},
};
struct platform_device s5p_device_hpd = {
.name = "s5p-tvout-hpd",
.id = -1,
.num_resources = ARRAY_SIZE(s5p_hpd_resources),
.resource = s5p_hpd_resources,
};
EXPORT_SYMBOL(s5p_device_hpd);
■ setup-tvout.c 文件主要提供了 GPIO 引脚功能配置:
void s5p_int_src_hdmi_hpd(struct platform_device *pdev)
{
s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0x3));
s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_DOWN);
}
void s5p_int_src_ext_hpd(struct platform_device *pdev)
{
s3c_gpio_cfgpin(EXYNOS4_GPX3(7), S3C_GPIO_SFN(0xf));
s3c_gpio_setpull(EXYNOS4_GPX3(7), S3C_GPIO_PULL_DOWN);
}
int s5p_hpd_read_gpio(struct platform_device *pdev)
{
return gpio_get_value(EXYNOS4_GPX3(7));
}
void s5p_cec_cfg_gpio(struct platform_device *pdev)
{
s3c_gpio_cfgpin(EXYNOS4_GPX3(6), S3C_GPIO_SFN(0x3));
s3c_gpio_setpull(EXYNOS4_GPX3(6), S3C_GPIO_PULL_NONE);
}
setup-tvout.c 文件提供的功能函数会被其他文件的函数体调用。
90.2.5 内核配置
我们以 POP 核心的内核配置为例来说明:
cp config_for_android_pop .config
make menuconfig->Device Drivers->Multimedia support->video capture adapters
è Samsung TVOUT Driver 使能该驱动选项,然后同样打开 HDMI CEC ,HDMI HPD,HDMI 14A Driver, HDMI PHY, TVOUT frame buffer driver support, Support pre allocate frame buffer memory.
如果需要获得更多的调试信息,需要使能 TVOUT driver debug message, 这样内核会输出 HDMI 的调试信息到调试串口,为我们学习 HDMI 驱动提供有力的帮助,
HDMI 功能调试完成后需要关闭该选项,因为频繁的打印调试信息会影响到系统的性能, 严重的情况下会导致 HDMI 输出界面出现卡顿的现象。
90.2.6 Android空间
Android4.4 系统是支持 HDMI 输出显示的,主要体现 Androd 框架层的支持,及用户设置界面关于 HDMI 参数设置。
下面我们看一下 Android 的图形显示系统框架:
HDMI 的输出是由 SurfaceFlinger 控制 Mali Display(HW Composer)输出的,我们会从 HDMI HAL 层代码看到 HW Composer 硬件合成器输出图像到 HDMI 显示设备,硬件合成器是 Honeycomb(android 发布版本)引入的一个 HAL,SurfaceFlinger 使用它,利用硬件资源来加速 Surface 的合成,比如 3D GPU 和 2D 的图形引擎。
Android 的 Framwork 层已经支持 HDMI 的输出显示,我们重点关注的是 HDMI HAL 层的实现代码,这也是我们 Porting 工作最重要的部分。
90.2.7 HAL层移植
首先我们看一下 HDMI HAL 层相关文件夹:
iTop4412_KK4.4/hardware/samsung_slsi/exynos4/libhdmi iTop4412_KK4.4/hardware/samsung_slsi/exynos4/libhwc
libhdmi 文件夹是我们重点关注的对象,里面共有三个子文件夹:
iTop4412_KK4.4/hardware/samsung_slsi/exynos4/libhdmi/libhdmiservice iTop4412_KK4.4/hardware/samsung_slsi/exynos4/libhdmi/libsForhdmi iTop4412_KK4.4/hardware/samsung_slsi/exynos4/libhdmi/ SecHdmi
libhdmiservice: 该文件夹会编译形成 libTVOut.so, libhdmiclient.so 库文件。
libhdmiservice:文件夹提供了 SecHdmiClient 类的实现,及 SecHdmiClient 类对象的创建函数 SecHdmiClient::getInstance(),硬件合成器 libhwc 会调用SecHdmiClient::getInstance()函数创建全局唯一的 SecHdmiClient 对象,使用对象指针mHdmiClient 指向该对象。
libhwc 文件夹的关键文件 hwc.cpp,也是硬件合成器 libhwc 的 HAL 层文件,该文件会通过对象指针调用 SecHdmiClient 的接口函数,比如使能 HDMI,设置 HDMI 的分辨率等等:
hwc.cpp 调用 SecHdmiClient 类接口函数的相关代码片段:
#ifdefined(BOARD_USES_HDMI)
android::SecHdmiClient*mHdmiClient=android::SecHdmiClient::getInstance();
if(skip_hdmi_rendering==1)
return0;
if(contents==NULL){
//Don'tdisplayunnecessaryimage
mHdmiClient->setHdmiEnable(0); return0;
}else{
mHdmiClient->setHdmiEnable(1);
}
#ifdefSUPPORT_AUTO_UI_ROTATE
#if0//yqf,movetoFramebufferNativeWindow
cur=&list->hwLayers[0];
//LOGE("%s,cur->tran:%d\n", func ,cur->transform);//addedyqf
if(cur->transform==HAL_TRANSFORM_ROT_90)//addedyqffortest mHdmiClient->setHdmiRotate(90,ctx->num_of_hwc_layer);
elseif(cur->transform==HAL_TRANSFORM_ROT_270)
mHdmiClient->setHdmiRotate(270,ctx->num_of_hwc_layer);
elseif(cur->transform==HAL_TRANSFORM_ROT_180)
mHdmiClient->setHdmiRotate(180,ctx->num_of_hwc_layer);
else /*if(cur->transform==HAL_TRANSFORM_ROT_0)*/
mHdmiClient->setHdmiRotate(0,ctx->num_of_hwc_layer);
#endif
#endif
■libhdmi 文件夹里面包含了三个字文件夹 libcec,libddc,libedid,通过名字我们也就知道这个文件夹负责与 HDMI 显示设备进行 I2C 通信,查询 HDMI 显示设备的显示能力,设置HDMI 显示设备的分辨率。
EDID: Extended display identification data,简称 EDID,是指屏幕分辨率的信息, 包括厂商名称与序号。一般 EDID 存在于显示器的 PROM 或 EEPROM 内。一般如要读取EDID 都是透过 I2C,slave address 是 0x50。
EDID 的获取是通过 DDC 进行的,DDC 就是开发板与 HDMI 显示设备之间进行通信的通道,HDMI 发送端设备会通过 DDC 读取显示器的 EDID 信息,然后根据 EDID 信息判断显示能力等显示参数。
查看开发板的硬件原理图,我们可以知道 HDMI 使用 I2C0 与显示设备通信,这里我们需要正确设置 I2C 通道:
#define DEV_NAME "/dev/i2c-0"
intDDCOpen()
{
intret=1;
//checkalreadyopen??
if(ref_cnt>0){
ref_cnt++;
return1;
}
//open if((ddc_fd=open(DEV_NAME,O_RDWR))<0){
LOGE("%s:CannotopenI2C_DDC:%s", func ,DEV_NAME); ret=0;
}
ref_cnt++;
return ret;
}
HDMI 的 CEC 属于 HDMI 的扩展功能,我们没有使用到,这里不再解释。
■SecHdmi 文件夹提供最底层的 V4L2 的调用。
HDMI 的设备操作是通过 Kernel HDMI 驱动提供的设备节点来进行的,HDMI 相关的设备节点有:
/dev/video16 Graphics0 层设备节点
/dev/video17 Graphics1 层设备节点
/dev/video20 Video 层设备节点
/dev/graphics/fb0 frambuffer 设备节点
/dev/HPD HDMI 热插拔检测设备节点
SecHdmi 文件夹实现这些设备节点的打开,控制,关闭操作。
SecHdmi 文件夹对外提供 SecHdmi 类对象调用接口,libhdmiservice 文件夹会调用该对象的接口函数,用于底层设备节点的控制。
SecHdmi 文件夹也用于调用 libsForhdmi 文件夹通过的 EDID 接口,来获取 HDMI 显示设备的显示能力,及设置显示设备的分辨率。
libhwc 模块及 libhdmi 子模块调用关系:
另外 Android4.4 提供了 libhdmiservice_jni.so 库文件,该库文件提供了 Java 界面HDMI 参数设置的接口实现,libhdmiservice_jni.so 文件最后也会调用到 libsForhdmi 层用于控制 HDMI 显示设备参数。
90.2.8 HDMI参数设置
Android4.4 Setting 界面可以控制 HDMI 输出参数:
90.2.9 HDMI编译选项
如果需要 Android4.4 编译生成的镜像支持 HDMI 显示,那么编译 Android 源代码前必须配置好 HDMI 相关的宏定义:
配置文件:iTop4412_KK4.4/device/samsung/smdk4x12/BoardConfig.mk 添加以下宏定义:
BOARD_USES_HDMI_SUBTITLES := true
BOARD_USES_HDMI := true
BOARD_HDMI_STD := STD_720P
BOARD_HDMI_DDC_CH := DDC_CH_I2C_0
BOARD_USES_FIMGAPI := true
BOARD_USES_HDMI_EDID := true
BOARD_USES_HDMI_JUMPER := false
这样我们的 Android4.4 即可支持 HDMI 显示,且默认输出 720P 分辨率,DDC 采用I2C0 进行通信,支持 HDMI 显示设备 EDID 信息的获取与配置。
90.2.10 总结
以上作为 iTOP-4412 开发平台移植 HDMI 功能的过程总结,Android4.4-2015-07-02 发版本的 Kernel 及 Android 层代码均包含 Porting 后的代码,也就是 HDMI 正常工作的代码,方便大家学习和产品研发.
HDMI 功能支持音视频同步输出,这里我们重点讲解的是 HDMI 的 Porting 工作,如果对音视频同步输出有研究的朋友,可以自己阅读相关的代码,iTop4412 开发板的 HDMI 是支持 1080P,720P 分辨率,同样支持音视频同步输出。
如果您在实际的项目中需要 HDMI 功能,请参考我们的原理图设计硬件,尽量使用相同的 HDMI 资源,这样您只需要关注硬件部分,驱动使用我们移植好的即可,否则需要您修改HDMI 相关引脚配置,进行必要的调试工作,增加自己的工作量。
90.3 iTOP-4412-Android蓝牙移植教程
90.3.1 概述
近期需要把 Bluetooth 移植到 iTOP-4412 开发平台,查阅了相关资料,经过一段时间的研究、调试,终于成功的将蓝牙功能移植到了开发板上面,这里笔者记录移植过程及注意事项,方便以后工作需要。
iTOP-4412 开发板的 Bletooth 模块与板卡之间的连接采用 UART 接口,Bletooth 硬件模块使用的是 MTK 的 MT6620 芯片,MTK 提供了 Android4.0 及 Android4.4 的 driver, Porting Guid,有了这些就为我们的移植工作做了总体性的指导。
但是仅仅有 MTK 提供的文档还是远远不够的,毕竟硬件接口定义不同,kernel 版本也不同,Android 层与 MTK 提供的代码也有差异,这就需要我们在 MTK 文档的指导下, Step by Step 进行 Porting 工作.
移植环境:
1. iTOP-4412 精英版 + MT6620 Bletooth 模块
2. kernel 3.0.15 version
3. Android4.4
4. Ubuntu12.04 64BIt 开发环境
90.3.2 硬件相关部分
由于 MT6620 芯片集成了 Bluetooth,WiFi, FM,GPS 功能,所以 Bluetooth 的原理图可查看 MT6620 WiFi 原理图部分,下图为 Bluetooth 模块与开发板连接的引脚定义,通过该接口可以知道 Bluetooth 模块与 CPU 的交互接口.
▆UART 串口: Bluetooth 与 CPU 之间的通信接口,命令和数据的传输都是经过串口交换的。另外 MT6620 固件补丁的下载也是通过串口进行的,所以说串口是必不可少的硬件接口。上图中 Pin8,Pin10 为串口部分,与 CPU 的串口 0 连接.
▆MT6620 芯片使能引脚: Pin16 PMU_EN,使能引脚,默认低电平状态,高电平有效, 启用蓝牙功能之前需要先使能该引脚,这样 MT6620 才开始工作。
▆中断引脚:Pin 14 BGF_INT ,Blutooh, GPS,FM共用的中断引脚,用于告知CPU 有数据来了,或者说有请求到了.
▆复位引脚: Pin15 WiFi_RESET ,该引脚与 MT6620 的 SYSRST_B 连接,用于芯片的复位操作.
▆芯片工作电压:Pin12 提供1.8V的电压输入,Pin19 提供 3.3V,Pin20提供 5V 电压输入.
下图为 MT6620 模块原理图部分:
MT6620的引脚定义如下 :
以上是进行 Porting 前的准备工作,当然需要万用表,示波器工具进行辅助的检测,查看Bluetooth 模块的工作电压是否正常,GPIO 的当前状态,串口的数据波形.
另外需要强调的是,MT6620 Bluetootch 支持蓝牙 4.0 规范,蓝牙 4.0 规范包括常规蓝牙,高速蓝牙,还有很重要的低功耗蓝牙,即 BLE 的支持,下面是 MT6620 蓝牙特性:
▆支持Bluetooth V2.1 +EDR
▆支持Bluetooth 3.0+HS compliance
▆支持Bluetooth V4.0 Low Energy (LE)
▆使用UART / PCM 接口
▆模組包含PA with 13 dBm (class 1) transmit power
▆Better WiFi/BT coexistence performance
▆Low power scan function to reduce the power consumption in scan modes
关于 MT6620 的更多硬件特性,请参考 MTK 官方发布的 MT6620 硬件使用指导相关文
档.
90.3.3 Kernel
概述
iTOP4412 开发板采用的内核是 Linux 3.0.15 版本,MTK 官方给的移植 Porting 没有说明针对具体的 kernel 版本,由于是 Android4.4,所以 kernel 应该是 3.0 以后的版本或者更高支持;
首先按照 PoringGuid 的指导说明,把 New 和 Modify 文件夹下面关于 kernel 部分的修改放到我们的 kernel 代码里面, 把 MT6620 的驱动放在 driver/misc/目录下面,文件夹名称 mediatek,里面存放的是 WMT,既 wireless manage tools, 里面提供了与 MT6620 download firmware patch ,enable /disable MT6620 芯片,power on, power off 操作的相关驱动部分, 及 SDIO 总线设备接口驱动 Host Interface drivers. 蓝牙驱动mtk_stp_bt.ko 的源码也在这个目录下面,我们以驱动库 .ko 的形式编译驱动模块.
driver/misc/mediatek/ 库文件列表如下:
mtk_hif_sdio.ko ------- mmc 总线相关接口,mmc 总线发现 SDIO 设备,分配总线地址后,会与该驱动进行适配.适配成功后该驱动会调用 WiFi 网络驱动,另外 Bluetooth 也使用了该驱动库文件提供的相应接口;
mtk_stp_uart.ko ------- 串口相关驱动,通过串口下载固件补丁,设置芯片参数;
mtk_stp_wmt.ko------- core 部分,提供 Bluetooth 上电,断电,PSM 省电管理操作等mtk_stp_bt.ko 字符设备驱动,创建 Bluetooth 设备节点用于与用户空间交互;另外这里附一张 Blutooth Kernel 层驱动层次图:
上图红色部分 BlueZ, Hci_stp.ko 为 Android4.0 Bluetooth 使用的蓝牙协议栈与Kernel 库文件。蓝色部分 Bluedroid, mtk_stp.bt.ko 为 Android4.4 版本使用的蓝牙协议栈与 kernel 库文件。Android4.0 与 Android4.4 关于 Bluetooth 部分变化比较大,请大家移植过程中注意这一点,他们之间的具体差异这里不再讲解。
从上图可以看到 bluetooth 驱动调用关系:
mtk_stp_bt.ko -> mtk_stp_wmt.ko -> mtk_stp_uart.ko -> uart driver
最后最底层的串口驱动负责蓝牙命令,数据的发送与接收.
代码修改
根据硬件连接情况配置必要的平台资源,修改文件:
kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
关键函数:
该函数配置 MT6620 相关的 GPIO 引脚为初始化输出状态,或者配置为中断状态,Bluetooth 驱动会改变这些引脚的状态,这里仅仅是初始化.
static void __init mtk_combo_init(void)
{
//MT66XX PMUEN
if(gpio_request(EXYNOS4_GPC1(0), "GPC1_0"))
{
printk(KERN_ERR "failed to request GPC1_0 for MT6620 PMUEN control\n");
}
//MT66XX SYSRST
if(gpio_request(EXYNOS4_GPC1(1), "GPC1_1"))
{
printk(KERN_ERR "failed to request GPC1_1 for MT6620 SYSRST control\n");
}
s3c_gpio_cfgpin(EXYNOS4_GPC1(0), S3C_GPIO_OUTPUT);
s3c_gpio_cfgpin(EXYNOS4_GPC1(1), S3C_GPIO_OUTPUT);
gpio_direction_output(EXYNOS4_GPC1(0), 0);
gpio_direction_output(EXYNOS4_GPC1(1), 0);
gpio_free(EXYNOS4_GPC1(0));
gpio_free(EXYNOS4_GPC1(1));
mdelay(5);
//need config eint models for Wifi & BGA Interupt
if (gpio_request(EXYNOS4_GPX2(5), "WiFi INT"))
printk(KERN_WARNING "MT6620 WiFi INT(GPX2.5) Port request error!!!\n");
else {
s3c_gpio_setpull(EXYNOS4_GPX2(5), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(EXYNOS4_GPX2(5), S3C_GPIO_SFN(0xF));
gpio_free(EXYNOS4_GPX2(5));
}
if (gpio_request(EXYNOS4_GPX2(4), "BGF INT"))
printk(KERN_WARNING "MT6620 BGA INT(GPX2.4) Port request error!!!\n");
else {
s3c_gpio_setpull(EXYNOS4_GPX2(4), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(EXYNOS4_GPX2(4), S3C_GPIO_SFN(0xF));
gpio_free(EXYNOS4_GPX2(4));
}
//normal it is high level
if (gpio_request(EXYNOS4_GPX3(2), "6260_GPIO2")!=0) {
printk("[mt6620] ERROR:Cannot request 6260_GPIO2\n");
} else {
gpio_direction_output(EXYNOS4_GPX3(2), 1);/* WLAN_CHIP_PWD */
gpio_set_value(EXYNOS4_GPX3(2), 1);
mdelay(100);
gpio_free(EXYNOS4_GPX3(2));
}
return;
}
关键结构体: 该结构体告诉 MT6620 驱动相关部分使用了平台的哪些 GPIO 资源. 结构体所属文件: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
static struct mtk_wmt_platform_data mtk_wmt_pdata = {
.pmu =EXYNOS4_GPC1(0), //RK30SDK_WIFI_GPIO_POWER_N,//RK30_PIN0_PB5, //MUST set to pin
num in target system
.rst = EXYNOS4_GPC1(1),//RK30SDK_WIFI_GPIO_RESET_N,//RK30_PIN3_PD0, //MUST set to pin num in
target system
.bgf_int=EXYNOS4_GPX2(4),
//IRQ_EINT(20),//RK30SDK_WIFI_GPIO_BGF_INT_B,//RK30_PIN0_PA5,//MUST set to pin num in target system if use
UART interface.
.urt_cts = -EINVAL, // set it to the correct GPIO num if use common SDIO, otherwise set it to -EINVAL.
.rtc = -EINVAL, //Optipnal. refer to HW design.
.gps_sync = -EINVAL, //Optional. refer to HW design.
.gps_lna = -EINVAL, //Optional. refer to HW design.
};
static struct mtk_sdio_eint_platform_data mtk_sdio_eint_pdata = {
.sdio_eint = EXYNOS4_GPX2(5),//IRQ_EINT(21) ,//RK30SDK_WIFI_GPIO_WIFI_INT_B,//53, //MUST set
pin num in target system.
};
static struct platform_device mtk_wmt_dev = {
.name = "mtk_wmt",
.id = 1,
.dev = {
.platform_data = &mtk_wmt_pdata,
},
};
static struct platform_device mtk_sdio_eint_dev = {
.name = "mtk_sdio_eint",
.id = 1,
.dev = {
.platform_data = &mtk_sdio_eint_pdata,
},
};
MTK 官方移植文档中会告诉我们需要在原始内核代码里面增加哪些文件,如何在 make menuconfig 中配置相关部分,这里就不再详细描述.
注意:蓝牙驱动虽然没有使用到 mmc 总线,但是 mtp_hif_sdio.ko 驱动导出了接口函数,mtk_stp_wmt.ko 核心驱动库会调用导出函数,所以 mtp_hif_sdio.ko 需要加载到内核空间,如果您的产品中不含有 WiFi, 仅仅需要 Bluetooth 功能,那么完全可以不用配置MMC 总线部分,不影响蓝牙的使用,但是务必加载 mtp_hif_sdio.ko 文件。
另外 MT6620 具备功耗控制功能,默认情况下如果 MT6620 处于 idle 空闲状态,空闲时间大于 60 秒后,会自动进入 sleep 状态,有中断触发时会唤醒,继续工作,您可以使用"echo 0 0 > /proc/driver/wmt_dbg "命令关闭功耗控制,这样 MT6620 会始终不会进入sleep 状态。另外也可以修改 MT6620 mtk_stp_wmt.ko 驱动中的参数,更改 ide to sleep 的时间.
MTK 官方提供的驱动代码中芯片默认 idle 5 秒后会进入 sleep 状态,使用蓝牙接收文件时,如果用户响应时间超过了 5 秒(用户没有单击接收或者拒绝按钮),MT6620 会休眠,驱动会导致 MT6620 会进入 RESET 状态,从而 Bluetooth 服务会出现问题, Bluetooth 功能不可使用,为了避免这样的问题发生,我们把休眠时间由默认的 5 秒调整到了 60 秒,给用户足够多的响应时间,用户在 60 秒内 接收或者拒绝蓝牙手机发给板卡的文件,另外文件接收完成后,需要单击状态栏中的文件接收完成提示,尽量在 Android4.4 状态栏中不含有关于蓝牙消息的提醒。
90.3.4 用户态空间
下面我们描述一下采用 Linux 系统和 Android 系统的用户都需要注意的地方:
驱动层移植完成后, MTK 的 Porting Guid 会告诉你需要在用户态运行 wmt_launcher 工具,作为后台的一个服务程序运行,该服务会配置串口的工作参数,下载固件补丁到MT6620 中,他的源代码相对比较简单,只有一个.c 文件,原 始 文 件 位 于 MTK 发 布 包 :
APEX_Android_4.4_MP_SW_package_V2.0/APEX_Android_4.4_MP_001_panda_combo_mt66xx_Package_Common/New/hardware/mediatek/wmt/stp_uart_launcher.c
修 改 后 的 文 件 位 于 iTOP-4412 Android4.4 发 布 包 :
iTop4412_KK4.4/hardware/mediatek/wmt/stp_uart_launcher.c
修改点主要在串口参数配置上,由于内核版本不同,串口设置参数也略有不同. 具体修改可以使用代码比对工具进行比较。
另外需要说明的是运行 wmt_launcher 的运行参数 跟 MTK 给的移植文档有点不同, Porting Guid 里面推荐串口波特率使用 921600, 而在 iTOP-4412 的板子上面采用该值会工作不正常,导致固件补丁无法下载,开始怀疑板卡不支持该波特率,后使用串口测试工具专门针对这个串口进行 921600 测试,也没发现问题,后没有继续查找,而是运行 wmt_launcher 时采用 115200 波特率:
wmt_launcher -b 115200 -d /dev/ttySAC0 -p /system/etc/firmware &
注意: 如果您的操作系统使用的是 Linux 而不是 Android,需要修改stp_uart_launcher.c
原始代码里面有 Android 特有的属性相关部分,Linux 系统不具有这个特性,我们提供了修改好的文件:stp_uart_launcher-linux-ok.c ,用户可以作为参考,该文件与原始文件stp_uart_launcher-ori.c,及正常工作的文件 stp_uart_launcher.c 位于相同目录下面.
无论是 Linux 系统还是 Android 系统,挂载根文件系统以后需要运行 wmt_launcher 服务,该服务在后台运行,打开 /dev/ttySAC0 串口,监控着串口上报的事件,对事件进行响应,比如内核上报 "下载固件补丁" 事件,那么 wmt_launcher 会打开/system/etc/firmware 下面的固件补丁,然后下载到 MT6620 模块中,该服务不可以被用户终止运行.
90.3.5 HAL 层移植
Bluetooth 的 HAL 层主要有 Bluedroid 协议栈与 MT6620 vendor 提供底层库文件组成。
需要我们重点关注的有 bluetooth.default.so 该动态库文件提供了 Framework 层调用 HAL 层的 API 接口,同时向下调用 libbt-hci.so 。
bluetooth.default.so 包含两个静态库:libbt-brm_stack.a, libbt-brm_bta.a
libbt-brm_stack.a 提供 Bluetooth 各种 profiles 的支持, 比如 a2dp,hid,pan 等等, libbt- brm_bta.a 用于与 framdwork 层进行通信.
libbt-hci.so 该动态库提供 bluetooth.default.so 的底层支持,另外他会调用 libbt- vendor.so 接口,通过串口发送,接收命令,数据的操作也是通过该动态库实现的。他位于 bluedroid 源码目录 hci 文件夹下。
libbt-vendor.so 厂商库文件,用于实现厂商提供的蓝牙模块特性支持,库文件位于android4.4/iTop4412_KK4.4/hardware/mediatek/bt/combo_mt66 目录下。
Bluedroid
从 Android 4.2 开始,Bluetooth stack 发生了重大改变:从 Bluez 换成了由 Google 和 Broadcom 联合开发的 Bluedroid(当然,核心的部分还是 Broadcom 的,Google 主要是做了和上层 Framework 相关的部分)。
Bluedroid 和 Bluez 相比,有如下优点:
■层次结构清晰。各个 profile 对上层接口统一,便于增加新的 profile;
■增加了 HAL 层,便于移植。
■去掉了 DBus,Framework 的 Java 代码直接调用到 Bluedroid 的 Native 代码。
但是 Android 4.2 中的 Bluedroid 与 Android 4.0 中的 Bluez 相比,功能要少,例如不支持 AVRCP 1.3, Bug 较多,例如某些蓝牙耳机不能重拨最后一个电话。最重要的是 4.2 的Bluedroid 不支持 BLE。不过在刚刚发布的 Android 4.3 中已经有了很多改进,AVRCP 1.3 和 BLE 都得到了支持。
目前有一些 Android 4.1 或 4.2 的设备是支持 BLE 的,但是都是采用的 Vendor 自己的解决方案,比如 Bluetooth stack 采用 Bluez 5.x,再提供 Vendor BLE Android SDK. 现在 Android 4.4 已经发布,从未来发展趋势来看,如果有人要学习 Bluetooth in Android, 建议不要再研究 Bluez,最好转向 Bluedroid。
以下是 Android 4.4 中 Bluetooth 相关代码之分布:
笔者在进行 Bluetooth 的调试过程中,使用 Logcat 命令输出 Android 的调试信息,在Android 的 Setting 界面开启蓝牙功能,根据打印信息的输出,发现 Enable Bluetooth 的过程中出现了问题,最后查找原因是因为 MT6620 芯片工作前需要需要下载固件补丁,这个过程需要花费一定的时间,然后 bluetooth.default.so 库文件代码中对开启 Bluetooth 有一定的时间限制,默认情况下是 3000 毫秒的超时时间,超时后会 disable 蓝牙,后经笔者修改为20000 毫秒,这样即可 enable 蓝牙芯片,从而进入工作状态.
修改文件: iTop4412_KK4.4/external/bluetooth/bluedroid/Android.mk
init.connectivity.rc 文件
init.connectivity.rc 文件路径:
iTop4412_KK4.4/hardware/mediatek/config/combo_mt66xx/ init.combo_mt66xx.rc
原始文件名称为 init.combo_mt66xx.rc,拷贝到 ramdisk 的 root 目录下面名称变更为init.connectivity.rc 文 件 。
我们在该文件增加了加载驱动模块库操作,运行 wmt_lanucher 服务操作,另外需要注意文件原有的创建 bluetooth 相关目录操作,及修改权限,变更拥有者,这些 command 非常的重要,比如:
# Load Blue module
insmod /system/lib/modules/mtk_stp_bt.ko
chmod 0660 /dev/stpbt
chown bluetooth system /dev/stpbt
mkdir /data/bluetooth 0711 bluetooth bluetooth
mkdir /data/misc/bluedroid 0771 bluetooth bluetooth
总 结
用户在移植 HAL 层之前可以先使用 MTK 提供的蓝牙测试工具 autobt 进行测试,该测试工具脱离复杂的 HAL 层,直接调用 libbt-vendor.so 也就是厂商自己的库文件,在 Android 的命令行中执行即可,可以使用该命令查询其他的蓝牙设备,发送,接收文件等等。是判断蓝牙硬件是否正常工作的有力工具,使用 autobt 测试通过后,再调试 Android HAL 及上层部分。
autobt 工 具 的 源 码 位 于 :
iTop4412_KK4.4/hardware/mediatek/utility/hw_test_tool/bluetooth/src/tool
另 外 MTK 官 方 提 供 了 Bluetooth 的 多 个 补 丁 , 路 径 :
APEX_Android_4.4_MP_SW_package_V2.0/Document/BT_patch_description(must read)
这些补丁是比较重要的,有关于蓝牙基础连接方面的,也有关于蓝牙文件发送接收,蓝牙鼠标,蓝牙耳机方面的,需要根据您的产品需求打对应的补丁,我们发布的 Android4.4 代码中已经打上了蓝牙基础连接,和文件发送接收方面的补丁,其他的补丁未处理.
HAL 层修改完成后在 Android4.4 的 Setting 里面打开 Bluetooth,就可以扫描到其他蓝牙设备,然后进行配对,配对完成后就可以进行文件的传输操作了,笔者仅测试了蓝牙的文件发送与接收功能,其他的蓝牙功能比如蓝牙耳机,蓝牙鼠标的使用均未进行测试,不过有了文件发送接收的测试基本说明了蓝牙的 Porting 是 OK 的,如果您有兴趣或者需求,可以移植蓝牙其他更丰富的功能与应用。
90.4 iTOP-4412-Android-SDIO接口WIFI模块的移植
近期需要把 WiFi 无线网络功能移植到 iTOP-4412 开发平台,查阅了相关资料,经过一段时间的研究、调试,终于成功的将 WiFi 功能移植到了开发板上面,这里笔者记录移植过程及注意事项,方便以后工作需要。
iTOP-4412 开发板的 WiFi 模块与板卡之间的连接采用 SDIO 接口,WiFi 硬件模块使用的是 MTK 的 MT6620 芯片,MTK 提供了 Android4.0 及 Android4.4 的 driver, Porting Guid,有了这些就为我们的移植工作做了总体性的指导。
但是仅仅有 MTK 提供的文档还是远远不够的,毕竟硬件接口定义不同,kernel 版本也不同,Android 层与 MTK 提供的代码也有差异,这就需要我们在 MTK 文档的指导下, Step by Step 进行 Porting 工作.
移植环境:
- iTOP-4412 精英版 + MT6620 WiFi 模块
- kernel 3.0.15 version
- Android4.4.
- Ubuntu12.04 64BIt 开发环境
90.4.1 硬件相关部分
下图为 WiFi 模块与开发板连接的引脚定义,通过该接口可以看出 WiFi 模块与 CPU 的交互接口.
查看 WiFi 模块的原理图可知,WiFi 模块与 CPU 之间采用 SDIO 接口和串口进行数据和命令的交互工作,Pin1,2,3,5,6,7 为 SDIO 接口,另外还需要 Pin8,Pin10 UART 串口, 另外Pin18,Pin17 用于串口流控,实际是可以不使用流控功能。 MT6620 芯片是复合芯片,除了具备 WiFi 功能以外,还支持蓝牙,FM,GPS 功能,他们与 CPU 之间的通信需要串口,另外MT6620 固件补丁的下载也是通过串口进行的,所以说串口是必不可少的硬件接口,即使您只使用该芯片的 WiFi 功能。
除了 SDIO 接口,UART 接口以外,还需要与 CPU 进行交互的接口包括:
Pin16 PMU_EN ,使能引脚,默认低电平状态,高电平有效。
Pin15 WiFI_RESET 引脚,默认低电平状态,高电平有效,用于复位 WiFi 芯片;
Pin 13 WiFi INT WiFI 的中断引脚,用于告知 CPU 有数据来了;
以上这些引脚需要在 BSP 中配置,WiFI 的驱动会通过这些引脚与 MT6620 芯片进行通信; 另外需要强调的是 WiFi 模块占用 CPU 的 MMC3 端口, 也可以说就是 SDIO 总线,关于MMC,SD,SDIO 总线的来历这里不再描述,下图为核心板 WiFi 相关部分:
在 iTOP-4412 精英版中 mmc0,mmc1 总线用于 eMMC,mmc2 用于 TF 卡,mmc3 用于 SDIO WiFi,关于 wifi 的其他硬件连接比如串口,使能(GPIO),复位(GPIO),中断(GPIO)这里不再贴出,具体的请查看开发板的原理图。
这里重点强调一下 HUB_CONNECT,也就是上图中的 L5 引脚,该引脚复用为 mmc 总线的 CD 引脚,也就是 Card Detect 引脚,用于告知 MMC3 控制器有设备插入,属于中断引脚,我们需要把该引脚与 CPU 的 某个 GPIO 引脚连接:
以上为 iTOP4412 底板原理图 WiFi 接口部分,请注意 HUB_CONNECT 引脚通过 0 欧电阻与 6060_GPIO2 进行了连接,6060_GPIO2 为核心板 GPIO 引脚。
这样连接的目的是通过软件输出 6060_GPIO2 低电平,从而 HUB_CONNECT 引脚为低电平输入状态,MMC3 控制器认为有设备插入到了 MMC3 总线上面,原理同 TF 卡,SD 卡的检测。
以上为进行 Porting 前的准备工作,当然需要万用表,示波器工具进行辅助的检测,查看WiFi 模块的工作电压是否正常,GPIO 的当前状态,MMC 总线上面的时钟及是否有数据从MMC3 控制器输出等等.
90.4.2 内核修改
Kernel
iTO-P4412 开发板采用的内核是 Linux 3.0.15 版本,MTK 官方给的移植 Porting 没有说明针对具体的 kernel 版本,由于是 Android4.4,所以 kernel 应该是 3.0 以后的版本或者更高支持;
首先按照 PoringGuid 的指导说明,把 New 和 Modify 文件夹下面关于 kernel 部分的修改放到我们的 kernel 代码里面,MT6620 的驱动分两个部分, 一部分放在 driver/misc/目录下面,文件夹名称 mediatek,里面存放的是 WMT,既 wireless manage tools, 里面提供了与 MT6620 download firmware patch ,enable /disable WIFi 芯 片 ,power on, power off 操作的相关驱动部分, 及 SDIO 总线设备接口驱动 Host Interface drivers,这些驱动工作正常后才开始加载 WiFi 网络相关驱动。
我们以驱动库 .ko 的形式编译驱动模块,driver/misc/mediatek/ 库文件与 WiFi 网络库文件 列表如下:
mtk_hif_sdio.ko mmc 总线相关接口,mmc 总线发现 SDIO 设备,分配总线地址后,会与该驱动进行适配.适配成功后该驱动会调用 WiFi 网络驱动;
mtk_stp_uart.ko 串口相关驱动,通过串口下载固件补丁,设置芯片参数;
mtk_stp_wmt.ko core 部分,提供 WiFi 上电,断电等等相关操作;
mtk_wmt_wifi.ko 字符设备驱动,创建设备节点用于与用户空间交互;
wlan_mt6620.ko WiFi 网络相关驱动,不需要我们进行修改;
另外这里附一张 MMC 驱动框架图:
因为我们的 MT6620 模块挂载到了 MMC 总线上面,属于 MMC 子系统的工作实例,我们非常有必要熟悉一下 mmc 驱动架构,是我们移植工作非常重要的一部分,关于 MMC 子系统的详细介绍这里不再说明.
代码修改
根据硬件连接情况配置必要的平台资源。
修改文件: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
关键函数 1: 该函数配置 WiFi 相关的 GPIO 引脚为初始化输出状态,或者配置为中断状态WIFI 驱动会改变这些引脚的状态,这里仅仅是初始化.
static void __init mtk_combo_init(void)
{
//MT66XX PMUEN
if(gpio_request(EXYNOS4_GPC1(0), "GPC1_0"))
{
printk(KERN_ERR "failed to request GPC1_0 for MT6620 PMUEN control\n");
}
//MT66XX SYSRST
if(gpio_request(EXYNOS4_GPC1(1), "GPC1_1"))
{
printk(KERN_ERR "failed to request GPC1_1 for MT6620 SYSRST control\n");
}
s3c_gpio_cfgpin(EXYNOS4_GPC1(0), S3C_GPIO_OUTPUT);
s3c_gpio_cfgpin(EXYNOS4_GPC1(1), S3C_GPIO_OUTPUT);
gpio_direction_output(EXYNOS4_GPC1(0), 0);
gpio_direction_output(EXYNOS4_GPC1(1), 0);
gpio_free(EXYNOS4_GPC1(0));
gpio_free(EXYNOS4_GPC1(1));
mdelay(5);
//need config eint models for Wifi & BGA Interupt
if (gpio_request(EXYNOS4_GPX2(5), "WiFi INT"))
printk(KERN_WARNING "MT6620 WiFi INT(GPX2.5) Port request error!!!\n");
else {
s3c_gpio_setpull(EXYNOS4_GPX2(5), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(EXYNOS4_GPX2(5), S3C_GPIO_SFN(0xF));
gpio_free(EXYNOS4_GPX2(5));
}
if (gpio_request(EXYNOS4_GPX2(4), "BGF INT"))
printk(KERN_WARNING "MT6620 BGA INT(GPX2.4) Port request error!!!\n");
else {
s3c_gpio_setpull(EXYNOS4_GPX2(4), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(EXYNOS4_GPX2(4), S3C_GPIO_SFN(0xF));
gpio_free(EXYNOS4_GPX2(4));
}
//normal it is high level
if (gpio_request(EXYNOS4_GPX3(2), "6260_GPIO2")!=0) {
printk("[mt6620] ERROR:Cannot request 6260_GPIO2\n");
} else {
gpio_direction_output(EXYNOS4_GPX3(2), 1);/* WLAN_CHIP_PWD */
gpio_set_value(EXYNOS4_GPX3(2), 1);
mdelay(100);
gpio_free(EXYNOS4_GPX3(2));
}
return;
}
关键函数 2: setup_mt6620_wlan_power_for_onoff
该函数为导出函数,WIFi 驱动会调用该函数,该函数关键地方是让 MMC 控制器驱动扫描 MMC 总线上面的设备,MMC 扫描到了 WiFI 模块才会加载相应的 WiFi 驱动,这里是主动让 MMC 扫描,我们的 SD 卡是采用中断触发的方式扫描,他们本质上都是扫描 MMC 总线上面的新设备,然后加载对应的设备驱动,具体的可以看一下 MMC 子系统相关内容.
函数所属文件: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/machitop4412.c
void setup_mt6620_wlan_power_for_onoff(int on)
{
int chip_pwd_low_val;
int outValue;
printk("[mt6620] +++ %s : wlan power %s\n",__func__, on?"on":"off");
#if 1
if (on) {
outValue = 0;
} else {
outValue = 1;
}
if (gpio_request(EXYNOS4_GPX3(2), "6260_GPIO2")!=0) {
printk("[mt6620] ERROR:Cannot request 6260_GPIO2\n");
} else {
gpio_direction_output(EXYNOS4_GPX3(2), 1);/* WLAN_CHIP_PWD */
gpio_set_value(EXYNOS4_GPX3(2), outValue);
mdelay(100);
gpio_free(EXYNOS4_GPX3(2));
}
if(on)
{
//need reset on mt6620 ? need test......
}
#endif
extern void sdhci_s3c_sdio_card_detect(struct platform_device *pdev);
// mdelay(200);
//need sdhc controler check wifi catd states......
sdhci_s3c_sdio_card_detect(&s3c_device_hsmmc3);
printk("[mt6620] --- %s\n",__func__);
}
EXPORT_SYMBOL(setup_mt6620_wlan_power_for_onoff);
关键结构体: 该结构体告诉 WiFi 驱动相关部分使用了平台的哪些 GPIO 资源.
结构体所属文件: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
void setup_mt6620_wlan_power_for_onoff(int on)
{
int chip_pwd_low_val;
int outValue;
printk("[mt6620] +++ %s : wlan power %s\n",__func__, on?"on":"off");
#if 1
if (on) {
outValue = 0;
} else {
outValue = 1;
}
if (gpio_request(EXYNOS4_GPX3(2), "6260_GPIO2")!=0) {
printk("[mt6620] ERROR:Cannot request 6260_GPIO2\n");
} else {
gpio_direction_output(EXYNOS4_GPX3(2), 1);/* WLAN_CHIP_PWD */
gpio_set_value(EXYNOS4_GPX3(2), outValue);
mdelay(100);
gpio_free(EXYNOS4_GPX3(2));
}
if(on)
{
//need reset on mt6620 ? need test......
}
#endif
extern void sdhci_s3c_sdio_card_detect(struct platform_device *pdev);
// mdelay(200);
//need sdhc controler check wifi catd states......
sdhci_s3c_sdio_card_detect(&s3c_device_hsmmc3);
printk("[mt6620] --- %s\n",__func__);
}
EXPORT_SYMBOL(setup_mt6620_wlan_power_for_onoff);
关键结构体: 该结构体告诉 WiFi 驱动相关部分使用了平台的哪些 GPIO 资源.
结构体所属文件: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
static struct mtk_wmt_platform_data mtk_wmt_pdata = {
.pmu =EXYNOS4_GPC1(0), //RK30SDK_WIFI_GPIO_POWER_N,//RK30_PIN0_PB5, //MUST set to pin
num in target system
.rst = EXYNOS4_GPC1(1),//RK30SDK_WIFI_GPIO_RESET_N,//RK30_PIN3_PD0, //MUST set to pin num in
target system
.bgf_int=EXYNOS4_GPX2(4),
//IRQ_EINT(20),//RK30SDK_WIFI_GPIO_BGF_INT_B,//RK30_PIN0_PA5,//MUST set to pin num in target system if use UART interface.
.urt_cts = -EINVAL, // set it to the correct GPIO num if use common SDIO, otherwise set it to -EINVAL.
.rtc = -EINVAL, //Optipnal. refer to HW design.
.gps_sync = -EINVAL, //Optional. refer to HW design.
.gps_lna = -EINVAL, //Optional. refer to HW design.
};
static struct mtk_sdio_eint_platform_data mtk_sdio_eint_pdata = {
.sdio_eint = EXYNOS4_GPX2(5),//IRQ_EINT(21) ,//RK30SDK_WIFI_GPIO_WIFI_INT_B,//53, //MUST set
pin num in target system.
};
static struct platform_device mtk_wmt_dev = {
.name = "mtk_wmt",
.id = 1,
.dev = {
.platform_data = &mtk_wmt_pdata,
},
};
static struct platform_device mtk_sdio_eint_dev = {
.name = "mtk_sdio_eint",
.id = 1,
.dev = {
.platform_data = &mtk_sdio_eint_pdata,
},
};
WIFI 驱动导出函数
文件:kernel/iTop4412_Kernel_3.0/drivers/misc/mediatek/combo_mt66xx/wmt/platform/vendor/wmt_plat.c
修改函数: wmt_plat_sdio_ctrl
函数说明: 该函数会调用我们上面导出的接口,让 MMC 总线控制器扫描新设备
INT32 wmt_plat_sdio_ctrl (WMT_SDIO_SLOT_NUM sdioPortType, ENUM_FUNC_STATE on)
{
int ret = 0;
extern void setup_mt6620_wlan_power_for_onoff(int on);
if (FUNC_OFF == on) {
/* add control logic here to generate SDIO CARD REMOVAL event to mmc/sd
* controller. SDIO card removal operation and remove success messages
* are expected.
*/
//add by dg 2015-04-14
setup_mt6620_wlan_power_for_onoff(0);
}
else {
/* add control logic here to generate SDIO CARD INSERTION event to mmc/sd
* controller. SDIO card detection operation and detect success messages
* are expected.
*/
//add by dg 2015-04-14
setup_mt6620_wlan_power_for_onoff(1);
}
//extern int omap_mmc_update_mtk_card_status(int state);
//ret = omap_mmc_update_mtk_card_status((FUNC_OFF == on)? 0: 1);
WMT_INFO_FUNC(KERN_INFO "%s, on=%d, ret=%d\n", __FUNCTION__, on, ret);
return ret;
}
以上两个文件的修改最为关键,当然您还需要配置 MMC3 的相关引脚为 MMC 工作状态,默认情况下面 MMC3 相关引脚为复用引脚中的 GPIO 状态,我们需要配置为 MMC 总线状态,笔者在调试过程中总是发现 MMC 总线上面没有命令或者数据输出,后发现默认情况下MMC3 相关引脚并没有配置成 MMC 工作模式,查看 Exynos4412 Datasheet 后才发现这一问题.修改工作模式后,此问题得到解决.
MTK 官方给的移植文档中会告诉你需要在原始内核代码里面增加哪些文件,如何在make menuconfig 中配置相关部分,这里就不再详细描述.
90.4.3 用户态空间
下面我们描述一下采用 Linux 系统和 Android 系统的用户都需要注意的地方:
驱动层移植完成后, MTK 的 Porting Guid 会告诉你需要在用户态运行wmt_launcher 工具,作为后台的一个服务程序运行,该服务会配置串口的工作参数,下载固件补丁到 MT6620 中,他的源代码相对比较简单,只有一个.c 文件:
原始文件位于MTK 发布包 :
APEX_Android_4.4_MP_SW_package_V2.0/APEX_Android_4.4_MP_001_panda_combo_mt66xx_Package_Common/New/hardware/mediatek/wmt/stp_uart_launcher.c
修改后的文件位于 iTOP-4412 Android4.4 发 布 包 :
iTop4412_KK4.4/hardware/mediatek/wmt/stp_uart_launcher.c
修改点主要在串口参数配置上,由于内核版本不同,串口设置参数也略有不同. 具体修改可以使用代码比对工具进行比较。
另外需要说明的是运行 wmt_launcher 的运行参数 跟 MTK 给的移植文档有点不同,Porting Guid 里面推荐串口波特率使用 921600, 而在 iTOP-4412 的板子上面采用该值会工作不正常,导致固件补丁无法下载,开始怀疑板卡不支持该波特率,后使用串口测试工具专门针对这个串口进行 921600 测试,也没发现问题,后没有继续查找,而是运行wmt_launcher 时采用 115200 波特率:
wmt_launcher -b 115200 -d /dev/ttySAC0 -p /system/etc/firmware &
注意: 如果您的操作系统使用的是 Linux 而不是 Android,需要修改stp_uart_launcher.c
原始代码里面有 Android 特有的属性相关部分,Linux 系统不具有这个特性,我们提供了修改好的文件:stp_uart_launcher-linux-ok.c ,用户可以作为参考,该文件与原始文件stp_uart_launcher-ori.c,及正常工作的文件 stp_uart_launcher.c 位于相同目录下面.
运行wmt_launcher 服务后,然后执行 "echo 1 > /dev/wmtWifi"命令,如果工作正常,会产生 wlan0 网络节点,如果没有产生设备节点,中间会提示出错信息,需要根据信息查找相关问题,默认情况下 WiFi 驱动的调试级别为 DEBUG 级别,可以提升调试级别为更高,当然不要忘记把 Kernel 控制台输出级别也设置的高一些,驱动的输出信息依赖于驱动代码设置的调试级别及 Kernel 的控制台级别两部分。
调试信息多一些,方便定位与分析问题,驱动工作正常后需要把调试级别恢复为正常状态,过多的调试信息输出会影响驱动的工作效率和工作的结果,笔者在调试 MMC 部分由于把 MMC 总线的调试信息全部放开,导致 MMC 工作效率降低,WiFi 相关驱动总是适配不到SDIO 设备,因为 WiFI 驱动会按一定的循环次数查找 SDIO 设备,由于 SDIO 相关驱动工作效率很低(大量的调试信息输出引起),导致 WiFi 驱动轮询次数结束了都没有匹配到设备。
如果产生了 wlan0 设备节点,那么下一步就是移植 wpa_supplicant 及 wpa_cli 程序了,Android4.4 采用的 wpa_supplicanat_8 版本,而不是以前 Android4.0 采用的wpa_supplicant 版本,他们之间的差异还是比较大的,显著的一个区别是 Android4.4 里面wpa_supplicant_8 使用的是 NL80211 驱动库。而 Android4.0 中的 wpa_supplicant 采用的是 WEXT 驱动库,如果您使用的是 Android4.4 的内核版本,运行的是 Linux 系统,那么需要您移植 wpa_supplicanat_8 到 Linux 文件系统中。
Android4.4 系统包含 wpa_supplicant_8 代码,编译 Android4.4 时会编译产生wpa_supplicant_8.
wpa_cli 为 wpa_supplicant 的客户端程序,可以使用该程序扫描无线网络,设置网络的ESSID 和密码,连接到无线网络,Linux 用户需要使用 wpa_cli 进行网络连接。Android 用户也可以在命令行中使用该工具验证 WiFi 驱动及 wpa_supplicant_8 是否工作正常.
这些都没有问题后我们需要移植 HAL 层相关代码。
90.4.4 HAL层移植
HAL 层移植相对简单,MT6620 采用的是 Android 的 WiFi 架构,没有经过修改,按照MTK 的指导文档移植即可,这里需要注意的是 wifi.c 文件和 init.connectivity.rc 文件.
wifi.c 文件
wifi.c 文件的路径iTop4412_KK4.4/hardware/libhardware_legacy/wifi.c
该文件会与 wpa_supplicnat 服务进行通信,是 Android 进行 WIFi 控制的 HAL 层的实现,根据 logcat 输出信息判断 WiFi 工作流程哪里出了问题,笔者修改了 wifi.c 文件的宏定义:
static char primary_iface[PROPERTY_VALUE_MAX];
// TODO: use new ANDROID_SOCKET mechanism, once support for multiple
// sockets is in
//dg cancel for mt6620
//#define WIFI_DRIVER_MODULE_NAME1 "rtl8188eu"
//#define WIFI_DRIVER_MODULE_PATH1 "/system/lib/modules/rtl8188eu.ko"
//#define WIFI_DRIVER_MODULE_NAME2 "rtl8192cu"
//#define WIFI_DRIVER_MODULE_PATH2 "/system/lib/modules/rtl8192cu.ko"
//#define WIFI_DRIVER_MODULE_NAME3 "rt5370sta"
//#define WIFI_DRIVER_MODULE_PATH3 "/system/lib/modules/rt5370sta.ko"
由于我们在 init.connectivity.rc 里面加载了 WiFi 驱动库及运行 wmt_launcher 服务,所以不再需要 wif.c 加载驱动了,直接注释掉相关宏即可.
wifi.c 的 int wifi_load_driver() 函数会设置 wifi 的相关属性:
static const char DRIVER_PROP_NAME[] = "wlan.driver.status";
property_set(DRIVER_PROP_NAME, "ok");
设置属性后会触发 WiFi 上电操作,因为我们在 init.connectivity.rc 设置了属性触发:
# monitor property and power on/off wlan
on property:wlan.driver.status=ok
write /dev/wmtWifi "1"
on property:wlan.driver.status=unloaded
write /dev/wmtWifi "0"
wifi.c 会启动 wpa_suplicant 服务:
int wifi_start_supplicant(int p2p_supported)
该函数会查找 wpa_supplicant 服务是否已经运行,如没有运行会启动该服务.
init.connectivity.rc 文 件
init.connectivity.rc 原 始 文 件 有 MTK 提 供 :
iTop4412_KK4.4/hardware/mediatek/config/combo_mt66xx/init.combo_mt66xx.rc
原始文件名称为 init.combo_mt66xx.rc,拷贝到 ramdisk 的 root 目录下面名称变更为init.connectivity.rc 文 件 。
我们在该文件增加了加载驱动模块库操作,运行 wmt_lanucher 服务操作,另外需要注意文件原有的创建 wifi 相关目录操作,及修改权限,变更拥有者,这些 command 非常的重
要,比如:
mkdir /data/misc/wifi 0770 wifi wifi
mkdir /data/misc/wifi/sockets 0770 wifi wifi
mkdir /data/misc/wpa_supplicant 0770 wifi wifi
mkdir /data/misc/p2p_supplicant 0770 wifi wifi
chown wifi wifi /data/misc/wifi/wpa_supplicant.conf
chown wifi wifi /data/misc/wifi/p2p_supplicant.conf
chmod 0660 /data/misc/wifi/wpa_supplicant.conf
chmod 0660 /data/misc/wifi/p2p_supplicant.conf
wpa_supplicant 会在/data/misc/wifi/sockets 目录下面创建 wlan0 文件节点用于与外部程序 wpa_cli 或者 Android 层服务进行通信.
这样在 Android4.4 的 Setting 里面打开 WiFi,就可以扫描到热点,连接互联网了,启动 WiFi 之前注意关闭有线连接,否则会存在网络访问冲突.
注意: Android 系统第一次运行是没有开启 WiFi 功能的,如果您的开发板上面有 WiFi 模块,且手动开启了 Android Setting 界面的 WiFi 功能 ,那么请不要把 WiFi 模块从底板上面拆除,否则 Android 启动过程中因为找不到 WiFi 模块 ,会频繁 打印出调试信息, 导致 Android 启动失败 ,如果确实产生了这个问题,请重新烧写 Android 系统此问题即可解决.
总 结
以上作为 iTOP-4412 开发平台移植 WiFi 功能的过程总结,即将发布的 Android4.4 的 Kernel 及 Android 层代码均包含 Porting 后的代码,也就是 Wifi 正常工作的代码,方便大家学习和产品研发.
如果您在实际的项目中需要 WiFi 功能,请参考我们的原理图设计硬件,尽量使用相同的 WiFi 资源,比如 WiFi 使能配置引脚,WiFi 中断配置引脚,WiFi 复位引脚,串口配置引脚等等,这样您只需要关注硬件部分,驱动使用我们移植好的即可,否则需要您修改 WiFi 引脚配置,进行必要的调试工作,增加自己的工作量。
90.5 iTOP-4412-Android4.4 SDK编译和使用教程
90.5.1 概述
Androd4.4 的 SDK 可以从 Android 开发网站下载,也可以从 Andorid4.4 的源码中编译形成,如果您的产品对 Android 的 Framework 层进行了修改,增加了自己的 API 调用函数,那么就需要编译 Andorid 源码形成自己的 SDK。另外想学习一下 SDK 的生成过程,那么本篇文章也会对您有所帮助。
90.5.2 下载
我们发布的 Android4.4 源码可以成功编译 Andorid 镜像文件,烧写到板卡中运行, 但是如果自己编译 SDK,缺省的 Android 源码缺少 SDK 编译的必要文件,我们可以从Android 的国内镜像使用 repo 工具进行下载。
Android 代码有 N 多个仓库组成,使用 repo 工具进行仓库的统一管理,假设需要下载的repo 工具存放于 /media/data/gitHub 目录:
cd /media/data/GitHub/
git clone git://aosp.tuna.tsinghua.edu.cn/android/git-repo.git/
git-repo 仓库下载完成后会看到该文件夹内有 repo 脚本文件:
假设我们的 Android4.4 代码存放在 /media/data/GitHub/iTop4412_KK4.4_git 目录,输入以下命令:
# cd /media/data/GitHub/iTop4412_KK4.4_git
#../git-repo/repo init –u https://github.com/TOPEET-Develop/android.git -b exynos4412_android4.4.4_master -g all, -notdefault,tools
注: repo init 命令中–u 参数指定 android 仓库下载地址,-b 参数指定仓库中的下载分支.
这里我下载的是 exynos4412_android4.4.4_master 分支.
另外务必要注意 “-g all, -notdefault,tools” 参数,如果没有这些参数下载的Android 源码可以编译形成目标板卡运行的二进制文件,但是无法编译形成 SDK 文件,如果您需要编译 SDK 请务必包含这些参数,包含这些参数以后,将下载 Android 所有源码。
下载过程中需要输入一些信息:
Your Name 输 入 root
Your Email: 直接回车即可,然后在输入 y, 回车继续:
#../git-repo/repo sync
注: repo sync 开始下载 Android 代码,下载过程与网络环境有一定的关系,笔者这边下载了 4 个小时的代码,如果下载过程中长时间没有进度显示,可以 ctrl+c 终止下载, 然后执行 repo sync 命令重新开始,repo 命令支持断点续传,重新执行 repo sync 命令会继续上次的下载,下图为源码下载过程中的进度显示:
下图为 Android 源码全部下载完成:
代码下载完成后会在 /media/data/GitHub/ iTop4412_KK4.4_git 目录下面显示Android 系统源码目录。Android4.4 的源码大概 20G,第一次下载过程比较漫长,请耐心等待。代码下载完成后,再次使用 repo sync 命令时,只更新升级代码,速度就比较快了。
每个文件夹目录下面都会有一个或者多个 Git 仓库,默认这些仓库是没有分支的,我们需要使用 repo start 命令创建分支,这里我们创建 master 分支,您也可以使用别的名字来定义分支名称,repo branch 命令可以查看创建的分支:
#../git-repo/repo start master --all
#../git-repo/repo branch
90.5.3 SDK编译
我们在使用 Eclipse 或者 Android Develop Tools 进行 APP 开发时需要设置 SDK,一般情况下我们在 Android 的官方网站下载 Google 编译好的 SDK,这里我们介绍如何编译 Android 源码生成 SDK。
开始编译
执行命令开始编译 Linux 版本的 SDK:
# cd /media/data/GitHub/iTop4412_KK4.4_git
#export JAVA_HOME=/usr/lib/jvm/jdk1.6.0_43
#source ./build/envsetup.sh
#lunch sdk-eng
#make -j4 sdk 2>&1 | tee log.txt
■执行命令开始编译 Windows 版本的 SDK:
#cd /media/data/GitHub/iTop4412_KK4.4_git
#export JAVA_HOME=/usr/lib/jvm/jdk1.6.0_43
#source ./build/envsetup.sh
#lunch sdk-eng
#make -j4 win_sdk 2>&1 | tee log.txt
注意:
编译 Windows 版本的 SDK 首先会自动编译形成 Linux 版本的 SDK,然后替代 Linux SDK的部分文件最终形成 Windows 版本的 SDK,而编译 Linux 版本的 SDK 不会自动编译Windows 版本的 SDK。
编译 Windows 版本的 SDK 务必在 Linux 的机器或者虚拟机进行,不支持 Windows 环境下
面编译 SDK,笔者在 Ubuntu12.04 虚拟机状态下编译 Linux 版本的 SDK 及 Windows 版本的
SDK。
笔者这里采用的是 make –j4,这里的 j4 表示启动四个线程进行编译工作,笔者的机器是四核的,理想状态下是一个核心运行一个线程,您可以根据自己的 CPU 状况调整这个值,采用多线程方式可以加快 SDK 的编译过程,当然主机的 CPU 利用率也会很高。
编译时间与您的 PC 配置有直接的关系,笔者采用主机 Win7 64bit + Ubuntu 虚拟机方式进行,CPU 是 Core I5 处理器,8G 内存,Android4.4 SDK 编译花费大约 4 个小时的时间。
生成 SDK
Linux版本的SDK生成路径:/media/data/GitHub/iTop4412_KK4.4_git/out/host/linux-x86/sdk
Windows 版本的 SDK 生成路径:
/media/data/GitHub/iTop4412_KK4.4_git/out/host/windows/sdk
可以看到 SDK 里面包含常用的 Android 工具,比如 fastboot,adb,当然也包含 Android 模拟器:
解决编译问题
理想状态下我们执行了编译 SDK 的指令,只需要等待 SDK 的生成,而在实际的过程中编译会出现问题,笔者记录了编译过程中出现的错误信息,需要解决这些问题才可以生成目标 SDK。
我们在执行编译 SDK 的指令时,会把编译输出信息记录到日志文件 log.txt 中,如果编译过程出现问题,我们需要打开 log.txt 文件,查找一下 error 信息,根据 error 信息判断是哪个环节出现了问题。
另外笔者采用的是 TOPEET 发布的 Ubuntu12.04 集成开发环境,集成编译环境的Ubuntu12.04 已经安装好了必要的库文件和工具文件,这样编译 Android SDK 过程中会减少出错信息。
问题 1:
build/core/main.mk:662:***system/core/libmincrypt/Android.mk system/core/libmincrypt/Android.mk:
Module 'libmincrypt' in PRODUCT_PACKAGES has nothing to install!. Stop.
解决办法:修改 /media/data/GitHub/iTop4412_KK4.4_git/build/target/product/base.mk 文件,去掉libmincrypt 库的编译:
问题 2:
make: *** [out/target/product/generic/obj/SHARED_LIBRARIES/gralloc.goldfish_intermediates/gralloc.o] Error 1
解决办法:修改 /media/data/GitHub/iTop4412_KK4.4_git/build/target/product/emulator.mk 文件去掉 gralloc.goldfish 的编译。
注意:删除 gralloc.goldfish 可能会导致模拟器运行 Android 时无法启动,如果您需要在 PC 的模拟器运行生成的 Android,请注意这一点。
问题 3:
device/samsung/common/libaudio/AudioHardware.h:35:25: fatal error: audio_codec.h: No such file or
directory
compilation terminated
make: ***
[out/target/product/generic/obj/SHARED_LIBRARIES/audio.primary.generic_intermediates/AudioHardware.o] Error 1
解决办法:
/media/data/GitHub/iTop4412_KK4.4_git/device/samsung/smdk4x12/conf/audio_codec.h 文件拷贝到
/media/data/GitHub/iTop4412_KK4.4_git/device/samsung/common/libaudio/目录下。
问题 4:
frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp:820:9: error: 'SecHdmiClient' is
not a member of 'android'
frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp:820:33: error: 'mHdmiClient'
was not declared in this scope
frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp:820:56: error:
'android::SecHdmiClient' has not been declared
make: ***
[out/target/product/generic/obj/SHARED_LIBRARIES/libsurfaceflinger_intermediates/DisplayHardware/HWComposer.o] Error 1
解决办法:修改frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp 第820 行,注释 :
问题 5:
make: *** [out/target/common/docs/online-sdk-timestamp] Error 45
make: *** Waiting for unfinished jobs....
make: *** [out/target/common/docs/hidden-timestamp] Error 45
DroidDoc took 199 sec. to write docs to out/target/common/docs/ds
编译过程中如果出现 XXXX-timestamp Error 的问题,统一采用建立相同名称的空文件方式,这些文件属于帮助文件,不影响 SDK 的使用。
比如这里使用 touch 命令建立两个文件:
#touch out/target/common/docs/online-sdk-timestamp
#touch out/target/common/docs/hidden-timestamp
笔者在编译 Android 的 Linxu 版本的 SDK 和 Windows 版本的 SDK 过程中遇到了上面的问题,解决后均可以通过编译。
另外 Android 源码路径 :/media/data/GitHub/iTop4412_KK4.4_git/sdk/docs/目录下面有howto_build_SDK.txt 文件,该文件是编译 SDK 的帮助文件,需要的时候可以打开阅读。
90.6 iTOP-4412-Android-修改开机动画
文档全称是“iTOP-4412-Android-修改开机动画 Android 部分”,文档介绍了Android 系统下修改开机动画的方法。配套资料在网盘资料的“iTOP4412开发板资料汇总(不含光盘内容)\iTOP-4412开发板系统开发资料\iTOP-4412-Android-修改开机动画Android部分.zip”。
一、上传bootanimation.zip到开发板
将拷贝到烧写工具文件夹(4412是platform-tools、4418是fastboot)
打开cmd.exe,执行下列三条指令
adb root
adb remount
adb push bootanimation.zip /system/media/
二、修改权限
adb shell
chmod 777 /system/media/bootanimation.zip
三、查看开机动画。(不重启开发板)在adb shell中执行下列两条指令。
cd /system/bin
bootanimation
四、注意
1、制作bootanimation.zip时,文件中的图片必须是.png或.jpg格式。
2、把文件压缩成bootanimation.zip时,必须把压缩方式改为“存储”。
3、压缩的时候是同时选中这几个文件(而不是先把文件放在一个文件夹里,再压缩这个文件夹)。
4、把该文件删除这变成系统自带的默认开机动画
5、修改开机动画的另外方式:直接修改/frameworks/base/core/res/assets/images目录下的图片
90.7 iTOP-4412开发板屏幕背光亮度调整
本章主要介绍 iTOP-4412 开发板的屏幕亮度调整方法,适用于 Android4.0.3、Android4.4、QtE、Ubuntu 系统。
可以在文件系统启动之后,通过控制台命令来实现背光调整,参考命令如下。关闭屏幕背光:
echo 0 > /sys/devices/platform/s3c24xx-pwm.1/pwm-backlight.0/backlight/pwm- backlight.0/brightness
使屏幕显示背光:
echo 102 > /sys/devices/platform/s3c24xx-pwm.1/pwm-backlight.0/backlight/pwm- backlight.0/brightness
通过修改屏幕背光亮度文件的值,来修改屏幕亮度,最大值是 255。具体的值,用户可以
根据自己的需求进行更改。
90.8 iTOP-4412-Android4.4设置MAC地址
本章主要介绍在 iTOP-4412 开发板的 Android4.4 系统上,通过修改脚本文件来修改MAC 地址的过程。主要分为两个部分:在源码中添加脚本文件以及修改配置脚本文件。适用于精英版以及全能版。
90.8.1 源码修改
在 android4.4 源码目录,使用“ vi system/core/rootdir/init.rc”命令打开初始化脚本init.rc。如下图所示。
在 init.rc 文件最后的位置,添加如下代码:
# added by neo for mac_test
service start_test /system/bin/test.sh
class main
user root
group root
oneshot
on property:sys.boot_completed=1
start start_test
如下图所示。
保存,退出。然后在源码目录使用“ vi out/target/product/smdk4x12/system/bin/test.sh”命令,新建“test.sh”脚本文件。如下图。
填充“test.sh”文件内容如下所示。
#!/system/bin/sh
sleep 10
netcfg eth0 down
netcfg eth0 hwaddr 00:11:22:11:33:00
netcfg eth0 up
保存退出。使用“chmod 777 out/target/product/smdk4x12/system/bin/test.sh”命令修改“test.sh”文件的权限。
然后在源码目录,使用“./build_android.sh”命令,重新编译 Android4.4 文件系统。将生成的“system.img”和“ramdisk-uboot.img”重新烧写到开发板。
90.8.2 修改配置文件
系统烧写并启动后,使用“netcfg”命令可以查看 mac 地址。如下图所示。
上图所示的 mac 地址是在“/system/bin/test.sh”文件中设置的(系统默认的 mac 地址是:“00:00:ff:ff:00:00”)。使用“vi /system/bin/test.sh”命令打开该文件,如下图所示。
用户可以自己修改上图红色方框中的 mac 地址来实现修改 mac 地址,固定 mac 地址的功能。
这里我们把 mac 地址修改为“00:00:11:11:11:00”,然后保存退出,如下图所示。重新启动开发板。
重新上电启动开发板后,再次使用“netcfg”命令查看 mac 地址,可以查看到 mac 地址已经成功的修改过来了。
使用“ping 192.168.2.1”测试一下网络,如下图。
由上图可知,网络正常。
经测试,wifi 功能也可以正常使用。
用户使用时,要注意 mac 地址的范围。
90.9 iTOP-4412-Android4.4设置静态IP
本章主要介绍在 iTOP-4412 开发板的 Android4.4 系统上,通过在 Android4.4 系统中设置,来实现固定 IP、Gatewa、DNS 等功能。适用于精英版以及全能版的 Android4.4。
Android4.4 系统,自带以太网功能,以太网功能默认采用的是 DHCP 方式获取动态IP。Android4.4 的设置里面,也带有 Static IP 功能。
如下图所示,进入“设置”->“无线和网络”->“更多”->“Ethernet”。
进入上图所示“Ethernet”->“Static IP Settings”,如下图所示。
勾选“Use Static IP”(其它参数全部配置完,Save 保存之后,才能勾选生效)
IP--用户想配置的 IP
Gateway 设置路由器的网关,作者这里路由器网关是 192.168.2.1
Netmask 一般是 255.255.255.0,用户根据路由器的配置设置。
DNS1 和 DNS2,这里需要注意的是,如果路由器没有额外设置,DNS1 和 DNS2 均设置和网关相同即可,作者这里 192.168.2.1。
需要注意的是,全部配置之后,才能“save”保存,一定要全部填上并 save,否则无法设置成功静态 IP,下图是作者设置之后的截图。如果保存不成功,多半是某个参数没有填写导致的。
当然,上图界面的上一级“Ethernet”中,需要选上“Use Ethernet”,如下图所示。
设置之后,会马上生效,拔插以太网并不会触发 DHCP 功能重新生成 IP(不设置静态 IP 功能,拔插网线可能会重新分配 IP)。
重启之后 IP 等参数仍然生效,参数并没有改变,静态设置仍然生效。网络也是可以正常使用。至此,静态 IP 设置完成。
90.10 iTOP-4412-Android通过脚本来设置静态 IP
本节介绍另一种设置 IP 地址的方法,即通过修改源码,使系统在启动时执行一个脚本来设置有线网 ip:
编写 shell 脚本 iTop4412_ICS/device/samsung/smdk4x12/myinit.sh,注意开头一定要是”#! /system/bin/sh“,内容如下:
#! /system/bin/sh
echo “This shell added by Pojun”
ifconfig eth0 192.168.1.130
修改脚本 iTop4412_ICS/device/samsung/smdk4x12/device.mk,增加一行,使编过程中将 myinit.sh 拷贝到镜像文件中,目标路径为/system/bin:
PRODUCT_COPY_FILES += \
device/samsung/smdk4x12/myinit.sh:system/bin/myinit.sh
修改源代码中的启动脚本“iTop4412_ICS/system/core/rootdir/init.rc “,在末尾添加 service 定义:
service myinit /system/bin/myinit.sh
class main
user root
group root
oneshot
n重新编译只要两分钟
将重新编译的 android 镜像烧写到开发板(注意,只需要烧写 android 系统镜像就可以了),重新启动后,查看 ip 地址:
Ip a
显示 eth0 的 ip 地址为 192.168.1.130,OK!
如果脚本没有成功运行,可以在超级终端手动运行服务来查看问题:
setprop ctl.start myinit
90.11 iTOP-4412-Android修改系统文件夹权限
安卓系统在默认情况下只有 data 目录是可读写的,别的目录都是只读操作,所以在做一些软件的调试的时候,需要传输库文件或者二进制文件到这些目录,下边我就介绍怎么修改文件夹的权限,以免需要重新编译 android 源码。
90.11.1 控制台修改
首先我们要进入安卓的最高权限,开机时默认不是最高权限,在终端中输入 su 这个命令,可以看到$编程#号,如下图所示。
然后我们以 system 文件夹为例,首先进入 data 文件夹,新建一个 hello.c 文件,我们尝试将 hello.c 文件拷贝到根目录的 sbin 文件夹下,会发现显示 Read-only file system,具体如下图。
在超级终端中输入 mount -o rw,remount / 这个命令,如下图所示。
进入 sbin 目录会发现有一个 hello.c 文件,如下图
但是有一些问题,只有 data 目录和 system 目录中的东西可以保存,其它的重启后就没了。
90.11.2 adb修改
前面说的是在超级终端中操作的,下边简单的接收一下在 adb 中操作的命令,这里就不一一截图了,在用户手册第三章有详细的 adb 使用文档。
#adb shell
#su
#mount -o rw,remount /
#exit