Developer Starting

 

OpenMoko介绍

  1. 项目介绍
    Openmoko™ 是一个开源软件项目,目的是建立全球第一个自由的移动通讯操作系统平台,此平台运行在X server之上,并能运行大多数的X应用程序。OpenMoko 环境提供了一个完全免费的开发环境,可以在受支持的手机硬件上运行应用程序和系统代码,消除对私有代码的依赖性。现在的版本Neo FreeRunner 已经在其网络商店 开始销售了。
  2. 硬件特点

    Neo FreeRunner 规格
    Neo FreeRunnerNeo1973 共有的特质如下:

    • 高分辨率的触控式屏幕 (1.7" x 2.27" - 43mm x 58mm) 480x640 像素
    • 128MB SDRAM内存,允许同时控制多个应用程序。
    • 内建地图、追踪程序的GPS 模块
    • 与本地数据相互交换的蓝芽模块
    • 外观与Neo1973相仿。请参考 openmoko.com取得更多信息。
    但FreeRunner将会提供以下的其它功能:
    • 802.11 b/g WiFi ,提供WEB浏览及数据传输
    • 400Mhz高速处理器(升级自266MHz)
    • 硬件图形加速器芯片,允许包括影片播放等快速图片处理
    • 2加速器,让手机能自动转换到横向模式
    • 2 LED 可个由机款外围的按钮启动。(一为双色 [blue(蓝色)|orange(橘色)] 位于电源按钮后方,1单色 [red(红色)] 位于aux按钮后方)
    • 用于北美区之三频GSM 及GPRS,其它区域为 (850/1800/1900 Mhz) 等频率
    • 100mA 主USB插槽,提供使用者能在短时间内自USB装置中充电。(但先将FreeRunner电池用尽)

      请点选Neo FreeRunner GTA02 Hardware(内部代号为GTA02)取得更多更完整的Neo FreeRunner规 格。 您可以在here 取得Neo 1973 及Neo FreeRunner两产品规格之比较

  3. 参考资料
    学习
    • Wikipedia OpenMoko 内容 提供了丰富的链接和信息。
    • 访问 OpenEmbedded 站点,详细了解这个交叉编译的开发环境。

开发环境安装配置

  • 安装所需软件包
$sudo  apt-get install subversion build-essential help2man diffstat texi2html texinfo cvs   gawk cogito libncurses5-dev zlib1g-dev libssl-dev libgtk2.0-dev ca-certificates  python-pysqlite2 sqlite3 sqlite3-doc python-pysqlite2-dbg quilt python-psyco ccache gcc-3.4 g++-3.4 libsdl1.2-dev lynx netpbm dosfstools git git-core gcc-3.4 lynx netpbm libsdl1.2-dev dosfstools subversion zlib1g-dev 
  • 创建工作目录和下载Makefile文件
$mkdir ~/moko
$cd ~/moko
$wget http://svn.projects.openmoko.org/svnroot/mokomakefile/trunk/Makefile

这个版本的Makefile 不直接支持USB gadget方式连接Qemu emulation,就需要给这个Makefile打上这个 makefile.patch 就不会出现 *"couldn't add device gadget:1"*这个问题了。USB gadget模式的支持,可將USB slave转包到Linux 2.6的内核gadgetfs,如此一來,我们就可以建立USB (emulated) network,两端也可以用NFS或sshfs来作文档存取。
$patch -p0 < makefile.patch
  • 通过Makefile来安装配置开发环境
初学者或者只做应用开发就可以
$make setup && make build-qemu

如果想完整的编译开发环境,这个可能需要5~7个小时
$make setup && make 

编译内核模块支持gadgetfs

  • 下载机器对应的内核原代码和相应的头文件
$sudo apt-get install linux-source linux-headers-`uname -r`
  • 解压源代码和创建工作目录
$mkdir ~/usb_module_dir
$sudo tar jxf /usr/src/linux-source-2.6.##.tar.bz2
  • 进入工作目录编译dummy_hcd.ko and gadgetfs.ko两个模块
$cd ~/usb_module_dir
$sudo cp -a /usr/src/linux-source-2.6.24-16-generic/drivers/usb/* .
$cd gadgetfs
  • 编辑Makefile文件,加入如下内容
echo "KDIR := /lib/modules/`uname -r`/build" >> Makefile
echo "PWD := `pwd`" >> Makefile
echo "obj-m := dummy_hcd.o gadgetfs.o" >> Makefile
echo "default: " >> Makefile
echo -e "\t\$(MAKE) -C \$(KDIR) SUBDIRS=\$(PWD) modules" >> Makefile
make

如果编译顺利,则会在当前目录下,发现有dummy_hcd.ko and gadgetfs.ko两个模块
  • 安装dummy_hcd.ko and gadgetfs.ko两个模块
sudo insmod ./dummy_hcd.ko
sudo insmod ./gadgetfs.ko default_uid=`id -u`

配置Host主机端的USB Network

编辑 /etc/network/interfaces 文件,添加如下内容
auto usb0
mapping hotplug
script grep
map usb0
iface usb0 inet static
address 192.168.0.200
netmask 255.255.255.0
network 192.168.0.0
up iptables -A POSTROUTING -t nat -j MASQUERADE -s 192.168.0.0/24 &
up echo 1 > /proc/sys/net/ipv4/ip_forward &
up iptables -P FORWARD ACCEPT &
down iptables -D POSTROUTING -t nat -j MASQUERADE -s 192.168.0.0/24 &

启动Qemu emulation
注意:编译与加载flash的Makefile一定要是打完patch的makefile.

$make run-qemu

等X Window的界面都出现,就是运用gadgetfs的时候。按下Ctrl-Alt-2组合键切入Qemu Monitor(Ctrl-Alt-1退出终端),我们就可以监控管理Qemu的状态,当我们打入"info usbslave"指令时候,应该出现如下输出:
USB2.2 device 1457:5122:
Manufacturer: Linux 2.6.24/s3c2410_udc
Product: RNDIS/Ethernet Gadget
Configuration 0: RNDIS
Configuration 1: CDC Ethernet

openmoko在启动X的时候会顺便将上面配置的USB network带起,这时候我们就可以通过gadgetfs去让Host与(emulated) target互联。同样在Qemu Monitor终端,打入 "usb_add gadget:1" 指令或者进入qemu编译目录 "openmoko/qemu-gadget.sh",若无错误信息,表示已成功。

* 在Host上的终端检查USB gadgetfs的状态:
sudo lsusb -vvv | grep s3c2410
iManufacturer 1 Linux 2.6.24/s3c2410_udc

sudo dmesg | tail
[ 8865.019170] gadgetfs: connected
[ 8865.019473] gadgetfs: disconnected
[ 8865.154839] gadgetfs: connected
[ 8865.191111] usb 5-1: configuration #1 chosen from 1 choice
[ 8865.194771] gadgetfs: configuration #1
[ 8866.587207] usb0: register 'cdc_ether' at usb-dummy_hcd-1, CDC Ethernet Device, 46:80:02:a5:0c:de
[ 8866.587249] usbcore: registered new interface driver cdc_ether
[ 8867.058152] usb0: CDC: unexpected notification 70!
[ 8867.354782] nf_conntrack version 0.5.0 (8192 buckets, 32768 max)
[ 8877.201114] usb0: no IPv6 routers present
* 现在查看ifconfig 就会发现多一个usb0网络设备被加载了
ifconfig
eth0 Link encap:以太网 硬件地址 00:16:e6:91:89:4c
inet 地址:172.16.16.100 广播:172.16.19.255 掩码:255.255.252.0
inet6 地址: fe80::216:e6ff:fe91:894c/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:33910 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:10474 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:1000
接收字节:11603153 (11.0 MB) 发送字节:2566683 (2.4 MB)
中断:21 基本地址:0xa800

lo Link encap:本地环回
inet 地址:127.0.0.1 掩码:255.0.0.0
inet6 地址: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 跃点数:1
接收数据包:2864 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:2864 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:0
接收字节:143200 (139.8 KB) 发送字节:143200 (139.8 KB)

usb0 Link encap:以太网 硬件地址 46:80:02:a5:0c:de
inet 地址:192.168.0.200 广播:192.168.0.255 掩码:255.255.255.0
inet6 地址: fe80::4480:2ff:fea5:cde/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:0 错误:1 丢弃:0 过载:0 帧数:1
发送数据包:33 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:1000
接收字节:0 (0.0 B) 发送字节:5005 (4.8 KB)


这是表明我们的qemu emulated已经与Host PC 建立了USB Network连接了,现在就可以通过ssh与qemu emulated连接了,emulated 默认是没有密码的。
 ssh root@192.168.0.202
The authenticity of host '192.168.0.202 (192.168.0.202)' can't be established.
RSA key fingerprint is b2:a0:77:58:f2:05:8a:8e:6c:8e:49:22:7a:43:af:2f.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.0.202' (RSA) to the list of known hosts.
root@192.168.0.202's password:
root@om-gta01:~# ls
Applications Settings log packages
root@om-gta01:~#

开发Openmoko手机软件

  1. 构建一个简单的应用程序helloworld
  2. 构建一个小型Gtk应用程序
  3. 获取系统信息
  4. 添加更多应用

由于 OpenMoko 是使用 OpenEmbedded 构建的,因此,构建测试应用程序的正确方法是使用 OpenEmbedded。

构建一个简单的应用程序helloworld

  • 创建一个包目录

    在前面一节中,我们介绍了使用Makefile安装配置OpenMoko的开发环境,在~/moko的工作目录下,我们可以发现有 openembedded/packages的目录,其中包含已有的包。现在,我们来添加自己的包。我们通常将示例程序命名为 hello,这里也不例外;在 openembedded/packages 中创建一个名为 hello 的目录,并为指定的文件创建一个子目录。您基本上只需要两个文 件;一个* BitBake recipe* 和*一个程序的源文件*。

  • hell 示例程序

    示例程序也是我们经典的"hello world!",只是用来说明在OpenMoko上开发软件的基本步骤,下面是我的示例程序,保存为 hello/files/hello.c:

#include <stdio.h>

int main(int argc, char *argv[]){
puts("Hello world!");
return 0;
}
  • bitbake recipe
    来审查一下它的内容:一个 bitbake recipe 文件。我们做了大量简化工作。实质上,bitbake recipe 有点类似于高度专
    门化的 shell 脚本(也许更准确地说是包含 shell 命令的 python 脚本);下面提供了一个例子,与上面的程序相对应;
    我将其命名为 hello_0.0.bb:
        DESCRIPTION = "Tutorial example" 
    PR = "r0"
    SRC_URI = "file://hello.c"
    do_compile() {
    ${CC} ${CFLAGS} ${LDFLAGS} ${WORKDIR}/hello.c -o hello
    }
    do_install() {
    install -m 0755 -d ${D}${bindir}
    install -m 0755 ${S}/hello ${D}${bindir}
    }
  • 变量分配

    这个 recipe 首先对 3 个变量进行设置。其中的说明简单明了。PR 设置修订 —— 我稍后将解释。SRC_URI 是指向文件的
    URI。您将注意到,在 file:// URI 中,没有使用第三个斜杠 —— 这是因为 bitbake 非常聪明,它可以自动查找文件目录。
    如果使用了第三个斜杠,将在本地驱动器的根目录中查找名为 hello.c 的文件。

  • 修订

    最棘手的部分就是最短小的部分:PR 变量,它用来表示项目的修订。如果对文件做了其他修改并且没有保存到 PR 变量中,
    bitbake 可能认为您不愿意更新这个修改。初始值为 r0 并可以任意累加。比如,即使构建失败(假如由于源文件的输入错
    误),如果没有修改 PR 的话,那么更新文件时不会对新版本进行复制。

  • do_this,do_that

    如果存在 do_compile() 函数,它将提供程序编译指令。这些指令或简单或复杂;在本例中,它们非常简单。注意,可以使用大
    部分预定义的变量自动获得正确的构建设置。类似地,程序构建完毕后,将运行 do_install(),以将文件复制到目标根文件系统
    中的对应位置。要留心模式;我刚开始使用的示例 bitbake 文件以 0644(所有者可以读/写,组用户和其他人只拥有读权限)
    作为目标二进制程序的模式,但是如果没有执行权限,则不是十分有用。

  • 添加包

    要添加自己的包,回到 local.conf 文件并添加以下代码:

    DISTRO_EXTRA_RDEPENDS += "hello"

    注意,变量名以 RDEPENDS 结尾,而不是 DEPENDS。这行代码令 bitbake 将 hello 包(由上文的 bitbake 文件描

    述)添加到发行版中。
  • 重新构建

    要使用新添加的包重新构建,依次运行 make update 和 make openmoko-devel-image。完成之后,可以在实际的手机或
    QEMU 中使用新的映像(对于 QEMU,使用 make flash-qemu-local “刷新” 虚拟手机而使用 make
    run-qemu 运行)。在终端运行 hello 将输出一条消息

    这样就完成了一个简单的示例程序,觉得如何呢?

构建一个小型Gtk应用程序

* 一个最简单的GTK程序
 #include <gtk/gtk.h>

int main(int argc, char *argv[]){
GtkWidget *window;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(window);

gtk_main();

return 0;
}
* 更新bitbake recipe
现在,构建 bitbake recipe 并不是很复杂,但是还是有一点难度。首先要指定应用程序依赖于 GTK+。可以通过 DEPENDS = "gtk+" 完成。您还需要更新修订(PR),否则 bitbake 不会注意到您执行的改动 —— 当然对源文件的改动除外!另一处改动是编译器行,这有一点长。不能仅仅链接 libgtk —— 必须获得所有的 include 目录,并且涉及到的每个包都有一个独立的 include 包。最终的 do_compile() 规则类似于:
do_compile() {
${CC} ${CFLAGS} ${LDFLAGS} -Wl,-O1 -g -o hello ${WORKDIR}/hello.c \
-I ${STAGING_INCDIR}/pango-1.0 -I ${STAGING_INCDIR}/gtk-2.0 \
-I ${STAGING_INCDIR}/cairo -I ${STAGING_LIBDIR}/gtk-2.0/include \
-I ${STAGING_INCDIR}/glib-2.0 -I ${STAGING_INCDIR}/glib-2.0/glib \
-I ${STAGING_INCDIR}/atk-1.0 -lgtk-x11-2.0
}
* 为何有这么多 include 目录?

对于一个只包括 <gtk/gtk.h> 的程序,需要指定 7 个额外的 include 目录,这似乎有点太多了。这是由两个问题引起的。第一个问题是, GTK+ 主头文件包含一些其他的头文件,而这些头文件又包含了更多的头文件。第二个问题是,和其它系统一样,为了减少冲突,OpenMoko 构建环境将每个包的 include 树放置在自己的子目录中。在本例中,<gtk/gtk.h> 引用 /usr/include/gtk-2.0/gtk/gtk.h,但是如果您安装了多个版本的 GTK+,改变这些 include 路径将随之改动您获得的头文件,而不需要您更新大量的源文件。

* 再次运行测试 可以运行make build-package-hello,单独编译文件,然后通过ssh安装在设备或者模拟器中!也可以像上面介绍的一样,把其他整个打包在rootfs里面,然后刷新。
注意:如果在运行gtk程序时候,出现这样的错误,做如下的设置就可以。具体情况看Linux DISPLAY作用
(hello:2219): Gtk-WARNING **: cannot open display:  
root@om-gta01:/etc# echo $DISPLAY

root@om-gta01:/etc# DISPLAY=localhost:0.0
root@om-gta01:/etc# export DISPLAY

获取系统信息

  • 获取版本系统
#include <sys/utsname.h>
int uname(struct utsname *name);
struct utsname u;
[...]
uname(&u);
label = gtk_label_new(u.release);
  • 查询磁盘空间
#include <sys/vfs.h> /* or <sys/statfs.h> */
int statfs(const char *path, struct statfs *buf);
 static char *
diskfree(void)
{
static char capacity[16];
struct statfs buf;
if (statfs("/", &buf) < 0) {
strcpy(capacity, "unavailable");
} else {
double used = 1.0 - ((double) buf.f_bavail / buf.f_blocks);
sprintf(capacity, "%.1f%%", (used * 100));
}
return capacity;
}
  • 显示内存使用情况和系统负载情况
#include <sys/sysinfo.h>//returns information on overall system statistics
int sysinfo(struct sysinfo *info);
    sysinfo(&si);

sprintf(buffer, "%.1f%%",
(100 * (1.0 - ((double) si.freeram / si.totalram))));
label_in_table(table, 2, "Memory usage:", buffer);

sprintf(buffer, "%.2f %.2f %.2f",
scaled_load(si.loads[0]), scaled_load(si.loads[1]),
scaled_load(si.loads[2]));
label_in_table(table, 3, "Load:", buffer);

//获取平均负载
static double
scaled_load(long int unscaled) {
return (double) unscaled / (1 << SI_LOAD_SHIFT);
/* 系统负载应该很简单;1 分钟、5 分钟、15 分钟的平均负载以无符号长整型的方式保存在 struct sysinfo 中。
然而,平均负载通常作为运行队列的平均长度导出给用户,一般会生成一个浮点数。如果平均长度为 1.00,表示系统处于完
整负载的较低一端。因此,如何将无符号的长整型转换为用户更熟悉的值?sysinfo() 手册没有详细说明这点 ,但是关键
在于使用称为 SI_LOAD_SHIFT 的宏,它持有换算系数;事实上,无符号的长整型被作为定点小数值处理。通常,
SI_LOAD_SHIFT 为 16,表示平均负载 1.0 被表示为 65,536。*/
}

相关源代码:hello.tar.gz

hello在模拟器上运行的界面