2009年4月28日星期二

Linux kernel的Makefile和Kconfig

作者:Sam(甄峰) sam_code@hotmail.com


Sam需要看看2.6 kernel中USB Mouse的代码。顺便谈谈Kernel中Makefile和Kconfig文件的关系以及配合使用。




背景知识:
背景知识一:Kconfig介绍:
#make menuconfig 时,所显示的Menu list是由各层Kconfig组成的。
最底层Kconfig存放在 ~/arch/i386/Kconfig. 以此为头,它会一层层使用source来把需要加入的各个目录中Keconfig添加近来。
例如:source "drivers/Kconfig"
则将~/drivers/Kconfig添加进Menu list中。

背景知识二:Kconfig写法语义:
config HID
tristate "Generic HID support"
depends on INPUT
default y
---help---
A human interface device (HID) is a type of computer device that interacts directly with and takes input from humans. The term "HID" most commonly used to refer to the USB-HID specification, but other devices (such as, but not strictly limited to, Bluetooth) are designed using HID specification (this involves certain keyboards, mice, tablets, etc). This option compiles into kernel the generic HID layer code (parser, usages, etc.), which can then be used by transport-specific HID implementation (like USB or Bluetooth).
For docs and specs, see http://www.usb.org/developers/hidpage/
If unsure, say Y

解释如下:
config HID :表示此条目与CONFIG-HID对应。CONFIG-HID会在Makefile中用到。

tristate "Generic HID support" 引号内的内容是会显示到Menu list中的。tristate表示这一项是三态的。

depends on INPUT:依赖于INPUT这一项。如果没有选中INPUT,则Menu list不会显示这项。

default y :缺省被选中。



背景知识三:built-in.o
vmlinux是Linux源码编译后未压缩的内核, vmlinux是由arch/i386/kernel/head.o和arch/i386/kernel/init_task.o以及各个相关子目录下的built-in.o链接而成的。


背景知识四:Kernel Makefile
Kernel中Makefile的体系以及如何编译的,其实Sam一直是一知半解的。
其中,kernel目录中的Makefile被称为底层Makefile
当使用类似#make menuconfig配置内核成功后,会生成 .config文件。
换句话说:make menuconfig 时,Makefile会从~/arch/i386/Kconfig读取Kconfig.然后根据用户的选择。生成.config文件。
例如:在drivers/hid/Kconfig:
config HID
tristate "Generic HID support"
如果用户选中Y,则在.config中会反映出来:
CONFIG_HID=y
则在~/drivers/Makefile中可以看到:
obj-$(CONFIG_HID) += hid/
表明:如果CONFIG_HID是Y,则把hid目录添加到要编译的目录中了。
进入到/driver/hid目录,则看到:
hid-objs := hid-core.o hid-input.o
表明这两个.o文件是一定会被编译出的。
obj-$(CONFIG_HID) += hid.o
表明:如果CONFIG_HID是Y,则hid.o会被编译出来。并built-in.
如果是 =m. 则hid.o被编译出来,但最后被做成modules(ko)


背景知识五:KBuild Make:
Linux内核的Makefile与我们平时写的Makefile有所不同,它由五部分组成:
1.Makefile : 顶层Makefile。
2. .config: kernel配置文件。
3. arch/xxx/Makefile: 具体架构的Makefile。
4. scripts/Makefile.xxx : 通用规则。
5. kbuild Makefile: 整个kernel中大约有数百个这种文件。

#make menuconfig后,生成 kernel配置文件: .config。
顶层Makefile读取.config.
顶层Makefile通过解析 .config来决定递归访问哪些目录中的Kbuild Makefile .
这个过程中,Kbuild Makefile会按.config的设置,逐个添加文件列表,以供最后的编译使用。
最简单的KBuild Makefile如下:
obj-y += foo.o
表明:Kbuild在这目录里,有一个名为foo.o的目标文件。foo.o将从foo.c或foo.S文件编译得到。并且它会被包入built-in中去。
所有编译进内核的目标文件都存在$(obj-y)列表中。而这些列表依赖内核的配置。Kbuild编译所有的$(obj-y)文件。然后,调用"$(LD) -r"将它们合并到一个build-in.o文件中。稍后,该build-in.o会被其父Makefile联接进vmlinux中。

如果foo.o要编译成一模块,那就要用obj-m了。所采用的形式如下:
obj-m += foo.o



例一:
在 ~/driver/hid/hid-core.c中,有以下语句,即内核insmod接口hid_init.
module_init(hid_init);
也就是说,当此模块被buildin或者作为module insmod时,kernel会自动调用hid_init.
然后查看 ~/driver/hid/Makefile,发现
hid-objs := hid-core.o hid-input.o
表明只要hid这个目录被加入,就会生成hid-core.o.
只好去看上一层目录中怎样会进入hid目录:
obj-$(CONFIG_HID) += hid/
表明只要CONFIG_HID=Y,m. 则hid目录被加入。
但用户作了什么,hid目录被加入编译呢?则看~/drivers/hid/Kconfig
config HID
tristate "Generic HID support"
以此得之只要在make menuconfig中选中此项,则hid-core.o被编译出来。


例2:
Sam想要研究USB Keyboard & Mouse driver. 在 make menuconfig时,需要选中:
config USB_HID
tristate "USB Human Interface Device (full HID) support"
则查看Makefile。发现只要选中CONFIG_USB_HID.则会编译出usb_hid.o
但~/drivers/hid/usbhid目录中却没有usbhid.c。那usbhid.o如何生成的呢?
drivers/hid/usbhid目录中,有个.usbhid.o.cmd文件。

2009年4月20日星期一

X5平台上几个问题的思考

作者:Sam(甄峰) sam_code@hotmail.com



问题1:
Sam在使用X5平台时,发现如果kernel升级一次(kernel版本没有变化),则之前编译好的modules insmod时会出现错误:
Unknown symbol in module (-1): No such file or directory

Sam刚开始想当然的认为是因为有人对kernel中版本信息作了变化。于是打开:include/linux/version.h。却发现其中UTS_RELEASE并没变化。
感觉非常奇怪。这个问题等待之后解决。

问题2:
Sam在使用X5平台时。通常是使用芯片提供商提供的Makefile直接编译出kernel.后来有一次无意中自己进入kernel目录,make menuconfig;make clean;make dep;make bzImage
得到的Image直接烧入,发现出错。
HiBoot 1.1.0.0 (Jan 8 2009 - 12:16:13)
HiBoot code: 60E00000 -> 60E37254
BSS: -> 60E62888
RAM Configuration: Bank #0: 60000000 128 MB Flash: 16 MB MAC: 00-00-00-00-00-02
Hit any key to stop autoboot: 0
Bad Magic Number

Sam检查多次,也没发现自己做错了什么。于是去查看芯片提供商给的外部Makefile.
其实很简单,就是copy 他们的config到linux-2.6.14/.config
然后编译kernel.最后则作了个特殊处理:
#mk-ulinux $(LINUXDIR) 0x60a00000 kernel-2.6.14-hi
Sam怀疑这是对kernel Image做了重新编排,加入一些标志然后作成kernel-2.6.14-hi。然后在u-boot中检查这些标志。
这是嵌入式系统中常用的一些做法,Sam竟然没想到,真不应该。


问题3:
在X5平台上,Sam使用动态库,编译时没问题,但运行时会报错:
Can't modify xxx.so's text section. Use GCC option -fPIC for shared objects, please.
但Sam其实在编译SO时使用了参数 -shared -fpic.
所以怀疑是编译器的问题。
Sam之前做交叉编译器都是用工具作的,对里面很多东西并不了解。看来什么时候需要具体作一个了。把类似:缺省 -I, -L目录,CPU类型等等全搞清楚。

2009年4月14日星期二

RedBoot常用命令(转载)

常用命令:
1 cache
使用格式:cache [on off]
功能描述:cache命令用于管理微处理器的cache。在传输大容量的文件时,最好是把cache打开。
Redboot>cache //显示系统当前cache状态
Redboot>cache on //打开cache
Redboot>cache off //关闭cache

2 channel
使用格式:channel [-l channel number]
功能描述:如果不带任何参数,channel命令会显示当前的控制台通道号;如果参数为-1,则将控制台通道切换到默认的控制台通道;若参数为硬件平台所支持的其他控制台号,则channel命令就对控制台作相应的切换。

3 dump
使用格式:dump [-b location] [-l length] [-s] [-1 -2 -4]
功能描述: 显示参数指定区域的数据,显示方式由参数指定。
-b 存储器的起始位置
-l 显示的长度
-s 使用Motorala S-reconds格式显示数据
-1 按单字节显示数据
-2 按双字节显示数据
-4 按四字节显示数据

4 exec
使用格式:exec [-w timeout] [-r ramdisk_address] [-s ramdisk_length] [-b load_address] [-l load_length] [-c kernel_command_line] [entry_point]
功能描述:执行一个映象文件,如引导Linux内核
-w 执行映象文件之前的等待时间
-r 传递给内核的ramdisk_address起始地址
-s 传递给内核的ramdisk_address长度
-b 内核映象文件地址
-l 内核映象文件长度
-c 传递给内核的命令行

5 fis creat
使用格式:fis creat [-b data_address] [-l length] [-f flash_address] [-e entry] [-r relocation_address] [-s data_length] [-n] [name]
功能描述:在FIS(Flash Image System)目录中创建一个映象,将当前RAM中的数据写入FLASH存储器中。因此,在使用该命令之前,映象文件数据必须已经保存在RAM中。
-b 待写入flash数据的存放地址
-f flash地址
-e 可执行映象地址
-r 执行fis load命令时,可执行映象的重定位地址
-s 写入flash中的可执行映象的实际长度
-n 用于更新FIS目录
name 创建映象的名称

6 fis init
使用格式:fis init [-f]
功能描述:初始化FIS目录,-f表示将所有的flash空间初始化

7 fis list
使用格式:fis list [-c] [-d]
功能描述:显示FIS中当前的所有映象文件
-c 显示映象的校验和
-d 显示映象的长度

8 fis free
使用格式:fis free
功能描述:显示flash当前的空闲空间

9 fis delete
使用格式:fis delete [name]
功能描述:删除FIS目录中的映象。name为需要删除映象的名称。
举例:
Redboot>fis delete ramdisk.gz

10 fis lock
使用格式:fis lock [-f flash_address] [-l length]
功能描述:锁定flash空间
-f 锁定flash空间的起始地址

11 fis unlock
使用格式:fis unlock [-f flash_address] [-l length]
功能描述:解除flash空间的锁定

12 fis erase
使用格式:fis erase [-f flash_address] [-l length]
功能描述:擦除指定的flash空间

13 fis write
使用格式:fis write [-b mem_address] [-l length] [-f flash_address]
功能描述:将数据由RAM写入FLASH中
-b 待写数据在RAM中的起始地址
-f 写入Flash的起始地址

14 fconfig
使用格式:fconfig
功能描述:对已保存在flash中的配置选项进行管理和重配置。

15 go
使用格式:go [-w timeout] [start_address]
功能描述:执行放在某一位置的可执行代码
-w 执行代码前的等待时间
start_address 可执行代码的起始地址

16 ip_address
使用格式:ip_address [-l local_ip_address] [-h server_ip_address] [-d DNS_server_ip_address]
功能描述:设置或改变系统使用的IP地址

17 load
使用格式:load [-r] [-v] [-h host] [-m varies] [-c channel_number] [-b base_address] [file_name]
功能描述:下载数据到目标系统RAM中
-r 下载未处理的数据到RAM
-v 下载过程显示进度
-b 数据下载到RAM的地址
file_name 下载的文件名

18 mcmp
使用格式:mcmp [-s location] [-d location] [-l length] [-1 -2 -4]
功能描述:比较两个存储区域的内容
-s 源区域起始地址
-d 目的区域起始地址
-l 需要比较数据的长度
-1 单字节读取
-2 双字节读取
-4 四字节读取

19 mcopy
使用格式同mcmp,功能就是将数据从一个存储区域复制到另一个存储区域

20 mfill
使用格式:mfill [-b location] [-l length] [-p value] [-1 -2 -4]
功能描述:将给定的数值填充到指定的存储区域

21 reset
功能描述:复位系统

22 ping
使用格式:ping [-v] [-n count] [-l length] [-t timeout] [-r rate] [-i IP_addr] [-h IP_addr]
功能描述:向指定主机发送ICMP报文,用于检查网络是否正常。
-v 显示数据包信息
-n 发送数据包的数目
-l 发送报文的长度
-t 设置超时时间
-r 发送数据包的间隔时间
-i 本机IP地址
-h 远端主机IP地址

23 help

24 version

2009年4月12日星期日

Intel CE2110上Redboot的使用

作者:Sam(甄峰) sam_code@hotmail.com


Sam很早之前使用过redboot。后来很快忘记如何使用了。 之后在Intel CE2110以及 CE3100上使用了redboot.结果现在有段时间不用,又忘记了。只好再看一遍。这次能记得住吗?光头葛说:我看(不)行!

Redboot简介:
Redboot是Redhat公司随eCos发布的一个BOOT方案,是一个开源项目。
Redboot支持的处理器构架有ARM,MIPS,MN10300,PowerPC, Renesas SHx,v850,x86等,是一个完善的嵌入式系统Boot Loader。
Redboot是在ECOS的基础上剥离出来的,继承了ECOS的简洁、轻巧、可灵活配置、稳定可靠等
品质优点。它可以使用X-modem或Y-modem协议经由串口下载,也可以经由以太网口通过
BOOTP/DHCP服务获得IP参数,使用TFTP方式下载程序映像文件,常用于调试支持和系统初始化。


RedBoot的基本用法:
1. 进入Redboot 设置界面:
在Intel-CE2110重新启动时,按下Ctrl+C。 则进入Redboot配置模式下。
出现以下提示符,表明进入RedBoot设置界面。
RedBoot>
在此界面下,可以使用RedBoot命令执行操作。

2. 修改RedBoot boot script。此script将被RedBoot在启动时使用。
首先进入RedBoot boot script edit界面。
RedBoot> fconfig
则出现:
>>
在这里输入: RedBoot boot script内容。

若发现fconfig后,出现莫名其妙的错误,可以清空boot script,方法:
RedBoot>fconfig -i



Intel CE2110上,按照kernel和rootfs放置的不同方式,有3种启动方式(与CE3100很类似):
1. kernel和rootfs全部放置在tftp server上。
2. kernel烧入到NOR flash中,rootfs则使用 nfs server中的。
3. kernel和rootfs全部烧入NOR flash中
前2种方式在开发阶段非常有用。可以快速修改kernel和rootfs.



1. kernel和rootfs全部放置在tftp server上:
1.1: copy zImage(kernel)和initrd_media.gz(rootfs)到/tftpboot
1.2:
RedBoot> fconfig
>>load –v –r –m tftp –h 172.16.1.61 –b 0x200000 zImage_olo
>>load –v –r –m tftp –h 172.16.1.61 –b 0x1000000 initrd_media.gz
>>exec –r 0x1000000 –s 0x12B2BDD –l 0x300000 –c “console=ttyS0, 115200 mem=100M@0 root=/dev/ram0 init=/linuxrc” 0x200000
则可以使用了。(但Sam使用这个办法时总有问题。显示乱码。)
现讲解命令如下:
load: 下载数据到目标系统RAM中.
-v: 下载过程显示进度
-r: 下载未处理的数据到RAM
-b: 数据下载到RAM的地址
-m: 方式
-h: host

exec: 执行一个映象文件,如引导Linux内核
-r: 传递给内核的ramdisk_address起始地址
-s: 传递给内核的ramdisk_address长度
-b: 内核映象文件地址
-l: 内核映象文件长度
-c: 传递给内核的命令行

2. kernel烧入到NOR flash中,rootfs则使用 nfs server中的。 (Sam还是有问题)
2.1. kernel burn 到flash。
2.1.1 把新kernel--zImage放到/tftpboot
2.1.2 Ctrl-c 进入redboot模式。
2.1.3烧入:
RedBoot>fis init
RedBoot>load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
RedBoot>fis unlock -f 0xc0040000 -l 0x00200000
RedBoot>fis create -b 0x00200000 -l 0x00200000 zImage

注解如下:
fis 是RedBoot FIS(Flash Image System)相关命令。
RedBoot>fis init
初始化FIS目录,-f表示将所有的flash空间初始化

RedBoot>load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
将zImage_olo这个文件从tftp上copy到RAM 0x00200000 处。

RedBoot>fis unlock -f 0xc0040000 -l 0x00200000
解除flash空间的锁定,-f flash_address,-l length

RedBoot>fis create -b 0x00200000 -l 0x00200000 zImage
在FIS(Flash Image System)目录中创建一个映象,将当前RAM中的数据写入FLASH存储器中。因此,在使用该命令之前,映象文件数据必须已经保存在RAM中。
-b:待写入flash数据的存放地址(RAM地址)
-l:长度
zImage:创建映象的名称
Sam想,为什么没有用 -f(flash地址),是因为上一句指令指定了unlock的flash.所以缺省放在那了。

2.2 准备rootfs在nfs中:


进入redboot config
RedBoot>fconfig
>>fis load zImage
>>exec -c "console=ttyS0,115200 mem=100M@0 root=/dev/nfs nfsroot=172.16.1.61:/home/sam/Intel, nolock ip=dhcp" 0x200000


3. kernel和rootfs全部烧入NOR flash中(重点)
3.1把Kernel(zImage)和rootfs(busybox_media.jffs2)放到/tftpboot中。

3.2ctrl-c进入redboot模式。
3.3烧入:
RedBoot〉fis init
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x01000000 busybox_media.jffs2
RedBoot〉fis unlock -f 0xc0040000 -l 0x00200000
RedBoot〉fis create -b 0x00200000 -l 0x00200000 zImage
RedBoot〉fis create -f 0xc0240000 -l 0x01d80000 -n root
RedBoot〉fis unlock -f 0xc0240000 -l 0x01d80000
RedBoot〉fis erase -f 0xc0240000 -l 0x01d80000
RedBoot〉fis write -f 0xc0240000 -l xxxxxx -b 0x01000000


注意:xxxxxx是rootfs的长度,需要经过计算得到
也就是在load -v -r -m tftp -h 172.16.1.61 -b 0x01000000 busybox_media.jffs2之后,
看到其输出:Raw file loaded 0x01000000-0x01e5ffff
则长度为:0x01e5ffff-0x01000000+1=0xE60000
RedBoot〉fis write -f 0xc0240000 -l 0xE60000 -b 0x01000000


讲解如下:
RedBoot〉fis init
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x00200000 zImage_olo
RedBoot〉load -v -r -m tftp -h 172.16.1.61 -b 0x01000000 busybox_media.jffs2

初始化FIS目录。并把zImage_olo和rootfs分别放到RAM 0x200000和0x1000000位置。

fis unlock -f 0xc0040000 -l 0x00200000
解除flash空间的锁定

RedBoot〉fis create -b 0x00200000 -l 0x00200000 zImage
RedBoot〉fis create -f 0xc0240000 -l 0x01d80000 -n root

将RAM中0x00200000处的东西(zImage)放到Flash xC0040000处. 并创建名为zImage的分区。
在Flash起始地址0xc0240000 创建为root分区。

RedBoot〉fis unlock -f 0xc0240000 -l 0x01d80000
RedBoot〉fis erase -f 0xc0240000 -l 0x01d80000
RedBoot〉fis write -f 0xc0240000 -l xxxxxx -b 0x01000000

把root分区所在flash解压缩。
把root分区所在Flash Erase.
把RAM 0x01000000的东西(rootfs)放到0xc0240000的FLASH中。

至此:把kernel和rootfs放到flash各自区域中了。


3.4进入redboot config
RedBoot〉fconfig
>>fis load zImage
>> exec -c "console=ttyS0,115200 mem=100M@0 rootfstype=jffs2 rootflags=noatime root=/dev/mtdblock2 rw" 0x200000

一切正常。

2009年4月10日星期五

Intel CE2110 (Olo) PDK Install and system build

作者:Sam(甄峰) sam_code@hotmail.com

Sam之前在Intel CE2110(Olo)上作程序。后来转到Intel CE3100(Canmore) 上。可天有不测风云,今天又需要回到Intel CE2110上编译一个程序。却发现连编译环境都忘记了。只好老老实实再作一次。

1. 安装PDK
如果为ISO格式文件:
#mount -t iso9660 -o loop Intel_CE_2110-2-1[1].358.iso mount_dir
#sh installer.sh /home/sam/work/current/Intel_CE_2110/Intel_CE_2110_Dev/
于是PDK被安装到指定目录。

2. 安装Toolchain。
toolchain为:gcc-3.4.5-glibc-2.3.6.tar.gz。
把它解压后放到/usr/local/下即可。

3. 整体编译:
#make all

4. 编译我们所需要的driver, protocol module.
#cd ~/Intel_CE_2110-1.x.xxx/linux-2.6.16.16
#make menuconfig
network->bluetooth中选中Modules

5. 编译Bluetooth 库文件:
Sam暂时使用老的bluetooth lib库。编译出 bluetooth.so

6. 编译我们的程序:
修改Makefile。很容易就可以编译了。

7. 架设Olo板:

2009年4月1日星期三

MTK平台程序开发-3

作者:Sam(甄峰) sam_code@hotmail.com

MTK平台程序开发-2

作者:Sam(甄峰) sam_code@hotmail.com

MTK程序将数据分为动态和静态数据。
动态数据指程序运行时才知道,由程序动态生成的。
静态数据指固定不变的数据,可以在程序编译时转换为二进制数据存入烧录文件中。

静态数据被称为资源。常见的资源有以下几种:字串,图像,菜单,字库,主题,声音等。

添加一个资源需要3个步骤:原料,ID,装载
原料其实就是数据源,如图片,字串等。
ID:资源的别名,程序只能通过ID得到资源。
装载:将原料转换为二进制数据,并与ID联系起来。

资源装载预编译程序是plutommi\Customer\ResGenerator\mtk_resgenerator.exe,这个程序在每次编译目标烧入文件之前临时编译生成。

添加资源步骤1:添加增加资源入口:
在plutommi\Customer\CustResource\PLUTO_MMI\Res_MMI添加一个Res_BluetoothUI.c 。
这个程序提供一个如何添加以及添加何种资源的function.

void PopulateBluetoothUIRes(void)
{
}
注意,这个C文件是预编译时提供给预编译程序。用来指定怎么添加以及添加何种资源的。

添加资源步骤2:修改Makefile
在plutommi\Customer\ResGenerator\Makefile中。
-I "plutommi/mmi/MainMenu/MainMenuInc" \
-I "plutommi\mmi\BluetoothUI\BluetoothUIInc" \
注意:此Makefile是资源预编译程序mtk_resgenerator.exe的Makefile

添加资源步骤3:修改PopulateRes.c
把PopulateBluetoothUIRes() 添加到PopulateResDate()中。当预编译程序mtk_resgenerator.exe运行时,会按照我们提供的接口PopulateBluetoothUIRes()中的内容把资源添加上去。

添加资源步骤4:添加资源ID:
因为每个类型资源的ID都在同一个取值空间。所以不能重复。
plutommi\mmi\Inc\MMIDataType.h
它实际是是提供了每个程序的BASE-ID.


现在以字串资源为例:
1. 在BluetoothUIDefs.h中添加字串ID:
STR_BLUETOOTHUI_STRING = BLUETOOTHUI_BASE+1,
注意:BLUETOOTHUI_BASE是在上面plutommi\mmi\Inc\MMIDataType.h中添加的。

2. 将ID与资源内容对应起来:
plutommi\Customer\CustResource\PLUTO_MMI\ref_list.txt中添加:
STR_BLUETOOTHUI_STRING 3Dijoy 鼎亿科技
表示:STR_BLUETOOTHUI_STRING这个ID与3Dijoy(英文) ,鼎亿科技(中文对应)

3. 字串装载:
在plutommi\Customer\CustResource\PLUTO_MMI\Res_MMI\Res_BluetoothUI.c

void PopulateBluetoothUIRes(void)
{
ADD_APPLICATION_STRING2(STR_BLUETOOTHUI_STRING,"3DiJoy", "");
}
第一个参数为:ID
第二个参数为:缺省资源(当ref_list.txt中没有此ID对应的资源时,使用这个)
第三个参数:字串描述,可以忽略

4. 字串读取:
GetString()可以从ID中读取数据。

gui_print_text((UI_string_type)GetString(STR_BLUETOOTHUI_STRING));



以下添加菜单: