分类目录归档:Kernel

Linux Kernel

Android内核编译过程全解

之前编译过锤子的坚果手机内核,摸索了很长时间,遗憾的是没有把一些填坑的细节记录下来,免不了有些细节还得摸索一遍。这次要编译三星的Galaxy Note 5内核,加上已经有了一次成功经历,所以想把它系统化的记录下来,供自己或其他需要的人参考。

我这里主要讲内核的编译,至于关联到的一些其他工具的安装或配置,这里就不展开了,碰到这样的问题请移步问问Google。

1.  获取CPU信息

要为某手机编译内核,首先要了解手机所用的CPU,不同厂商生产的CPU,对应的linux内核是不一样的。

查看CPU信息的一种方法是利用adb,“adb shell cat /roc/cpuinfo”可以得到cpu架构和生产厂商。另外,利用“adb shell cat /proc/version”还可以得到手机中正在使用的内核信息。以下是我的Galaxy note 5的信息:将手机与PC通过usb线相连,首先查看adb是否已经可以访问、然后获取cpu信息、最后获取linux内核信息:

C:\Users\yuanhui>adb devices
List of devices attached
0715e7e408981f38 device

C:\Users\yuanhui>adb shell cat /proc/cpuinfo
Processor : AArch64 Processor rev 2 (aarch64)
processor : 0
processor : 1
processor : 2
processor : 3
processor : 4
processor : 5
processor : 6
processor : 7
Features : fp asimd aes pmull sha1 sha2 crc32
CPU implementer : 0x41
CPU architecture: AArch64
CPU variant : 0x0
CPU part : 0xd03
CPU revision : 2

Hardware : SAMSUNG Exynos7420

C:\Users\yuanhui>adb shell cat /proc/version
Linux version 3.10.61-6137732 (dpi@SWDC3312) (gcc version 4.9 20140514 (prerelea
se) (GCC) ) #1 SMP PREEMPT Fri Feb 5 13:33:23 KST 2016
另外,通过官网也可以获得比较详细的硬件信息及参数:

QQ截图20160401115714

2. 下载源代码

得到CPU信息后,就可以到Android网站去下载linux内核源码了,下载地址说明:

http://source.android.com/source/building-kernels.html#figuring-out-which-kernel-to-build

这里需要特别注意,三星的源码树有两个,一个是kernel/exynos,一个是kernel/samsung,Galaxy note 5 用的是exynos芯片,所以一定要下载exynos的源码。

QQ截图20160401115941

所以,git命令为:

$ git clone https://android.googlesource.com/kernel/exynos

这里稍微注意一下,由于国内googlesource.com被墙了,只能通过VPN才能下载。

我为了方便,所有下载及编译都是在Ubuntu里完成的:

hyh@ubuntu:~$ git clone https://android.googlesource.com/kernel/exynos
Cloning into ‘exynos’…
remote: Sending approximately 733.06 MiB …
remote: Counting objects: 9, done
remote: Finding sources: 100% (9/9)
Receiving objects: 100% (3159494/3159494), 733.07 MiB | 631.00 KiB/s, done.
remote: Total 3159494 (delta 2631328), reused 3159494 (delta 2631328)
Resolving deltas: 100% (2631328/2631328), done.
Checking connectivity… done.
hyh@ubuntu:~$

这样,在我的home下就生成了一个exynos目录,源码就在这个文件夹里。

为了后面使用方便,把目录改成了linux-kernel-exynos。

到里面看看都有些什么分支:

hyh@ubuntu:~$ cd linux-kernel-exynos/
hyh@ubuntu:~/linux-kernel-exynos$ git branch -r
origin/HEAD -> origin/master
origin/android-exynos-3.4
origin/android-exynos-koi-3.10-marshmallow-mr1-wear-release
origin/android-exynos-manta-3.4-adf
origin/android-exynos-manta-3.4-jb-mr1
origin/android-exynos-manta-3.4-jb-mr1-fr
origin/android-exynos-manta-3.4-jb-mr1.1
origin/android-exynos-manta-3.4-jb-mr2
origin/android-exynos-manta-3.4-kitkat-mr0
origin/android-exynos-manta-3.4-kitkat-mr1
origin/android-exynos-manta-3.4-kitkat-mr2
origin/android-exynos-manta-3.4-lollipop-mr1
origin/android-exynos-manta-3.4-lollipop-release
origin/master
hyh@ubuntu:~/linux-kernel-exynos$

之前看到手机用的是3.10的内核,那就把3.10的分支checkout出来:

hyh@ubuntu:~/linux-kernel-exynos$ git checkout origin/android-exynos-koi-3.10-marshmallow-mr1-wear-release
Checking out files: 100% (45351/45351), done.
Note: checking out ‘origin/android-exynos-koi-3.10-marshmallow-mr1-wear-release’.

You are in ‘detached HEAD’ state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b <new-branch-name>

HEAD is now at 0f9bded… Merge “Fix that wrong eint information is displayed” into android-exynos-koi-3.10
hyh@ubuntu:~/linux-kernel-exynos$

这时候才能看到目录下有了源代码(之前目录里面实际有个800多兆的.git文件夹,隐藏了,看不到而已)。

3. 配置/定制内核

通常情况下,各个厂商都会针对自己的手机做大量定制,如果编译内核时选择生成相应的配置文件,则我们可以从手机中直接得到。能拿到这个内核配置文件编译内核就会顺利很多。

C:\Users\yuanhui>adb pull /proc/config.gz

大部分情况都能拿到这个config文件,然后解压出其中的.config文件放到linux-kernel-exynos目录下,直接调用make ARCH=arm menuconfig即可定制内核。

但是很不幸,三星的这款手机没有此文件:

C:\Users\yuanhui>adb pull /proc/config.gz
remote object ‘/proc/config.gz’ does not exist

好吧,没有也没关系,直接make ARCH=arm menuconfig,通常情况也是能顺利编译的……

我这里为了把CP210x的驱动编译进去,从Silicon官网下载了Android内核编译CP210x驱动的文档,照着把CP210x驱动编译选项勾选好,保存退出,在linux-kernel-exynos目录下生成了一个.config的文件:

hyh@ubuntu:~/linux-kernel-exynos$ make ARCH=arm menuconfig
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
HOSTCC scripts/kconfig/lxdialog/checklist.o
HOSTCC scripts/kconfig/lxdialog/inputbox.o
HOSTCC scripts/kconfig/lxdialog/menubox.o
HOSTCC scripts/kconfig/lxdialog/textbox.o
HOSTCC scripts/kconfig/lxdialog/util.o
HOSTCC scripts/kconfig/lxdialog/yesno.o
HOSTCC scripts/kconfig/mconf.o
SHIPPED scripts/kconfig/zconf.tab.c
SHIPPED scripts/kconfig/zconf.lex.c
SHIPPED scripts/kconfig/zconf.hash.c
HOSTCC scripts/kconfig/zconf.tab.o
In file included from scripts/kconfig/zconf.tab.c:2503:0:
scripts/kconfig/menu.c: In function ‘get_symbol_str’:
scripts/kconfig/menu.c:567:18: warning: ‘jump’ may be used uninitialized in this function [-Wmaybe-uninitialized]
jump->offset = r->len – 1;
^
scripts/kconfig/menu.c:528:19: note: ‘jump’ was declared here
struct jump_key *jump;
^
HOSTLD scripts/kconfig/mconf
scripts/kconfig/mconf Kconfig
#
# using defaults found in /boot/config-4.2.0-23-generic
#
/boot/config-4.2.0-23-generic:928:warning: symbol value ‘m’ invalid for BRIDGE_NETFILTER
/boot/config-4.2.0-23-generic:2668:warning: symbol value ‘m’ invalid for STMMAC_PLATFORM
/boot/config-4.2.0-23-generic:3834:warning: symbol value ‘m’ invalid for GPIO_UCB1400
/boot/config-4.2.0-23-generic:4336:warning: symbol value ‘m’ invalid for MFD_WM8994
/boot/config-4.2.0-23-generic:4343:warning: symbol value ‘m’ invalid for REGULATOR_88PM8607
/boot/config-4.2.0-23-generic:4365:warning: symbol value ‘m’ invalid for REGULATOR_LP872X
/boot/config-4.2.0-23-generic:4367:warning: symbol value ‘m’ invalid for REGULATOR_LP8788
/boot/config-4.2.0-23-generic:4410:warning: symbol value ‘m’ invalid for REGULATOR_TWL4030
/boot/config-4.2.0-23-generic:5453:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_REALTEK
/boot/config-4.2.0-23-generic:5454:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_ANALOG
/boot/config-4.2.0-23-generic:5455:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_SIGMATEL
/boot/config-4.2.0-23-generic:5456:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_VIA
/boot/config-4.2.0-23-generic:5457:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_HDMI
/boot/config-4.2.0-23-generic:5458:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CIRRUS
/boot/config-4.2.0-23-generic:5459:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CONEXANT
/boot/config-4.2.0-23-generic:5460:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CA0110
/boot/config-4.2.0-23-generic:5461:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CA0132
/boot/config-4.2.0-23-generic:5463:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CMEDIA
/boot/config-4.2.0-23-generic:5464:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_SI3054
/boot/config-4.2.0-23-generic:5465:warning: symbol value ‘m’ invalid for SND_HDA_GENERIC
/boot/config-4.2.0-23-generic:6539:warning: symbol value ‘m’ invalid for COMEDI_PCI_DRIVERS
/boot/config-4.2.0-23-generic:6594:warning: symbol value ‘m’ invalid for COMEDI_PCMCIA_DRIVERS
/boot/config-4.2.0-23-generic:6602:warning: symbol value ‘m’ invalid for COMEDI_USB_DRIVERS
/boot/config-4.2.0-23-generic:7036:warning: symbol value ‘m’ invalid for LP8788_ADC
/boot/config-4.2.0-23-generic:8176:warning: symbol value ‘m’ invalid for KVM
configuration written to .config

*** End of the configuration.
*** Execute ‘make’ to start the build or try ‘make help’.

hyh@ubuntu:~/linux-kernel-exynos$

 

4. 编译内核

 

不同的CPU架构,就得选择不同架构的工具链。我们在刚开始的时候通过adb shell cat /proc/cpuinfo已经得到了CPU的家规信息,为aarch64,所以我们要选择的aarch64工具链来编译,Google Source 网站上同时提供了各个版本的编译工具:

https://android.googlesource.com/platform/prebuilts/

我们选择aarch64 gcc 4.9的版本来编译:

hyh@ubuntu:~$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9
Cloning into ‘aarch64-linux-android-4.9’…
remote: Sending approximately 180.15 MiB …
remote: Counting objects: 108, done
remote: Finding sources: 100% (108/108)
remote: Total 1641 (delta 990), reused 1641 (delta 990)
Receiving objects: 100% (1641/1641), 180.18 MiB | 620.00 KiB/s, done.
Resolving deltas: 100% (990/990), done.
Checking connectivity… done.
hyh@ubuntu:~$

把这个路径加入到$PATH中,以便编译时省去冗长的路径:

hyh@ubuntu:~/linux-kernel-exynos$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
hyh@ubuntu:~/linux-kernel-exynos$ export PATH=$PATH:~/aarch64-linux-android-4.9/bin
hyh@ubuntu:~/linux-kernel-exynos$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/hyh/aarch64-linux-android-4.9/bin

可以看到,用于编译内核的编译器路径已经添加到了$PATH中。

下面开始编译:
hyh@ubuntu:~/linux-kernel-exynos$ make ARCH=arm CROSS_COMPILE=arm-linux-androideabi- zImage
CHK include/generated/uapi/linux/version.h
CHK include/generated/utsrelease.h
make[1]: ‘include/generated/mach-types.h’ is up to date.
CC kernel/bounds.s
GEN include/generated/bounds.h
CC arch/arm/kernel/asm-offsets.s
In file included from include/linux/scatterlist.h:10:0,
from include/linux/dma-mapping.h:9,
from arch/arm/kernel/asm-offsets.c:15:
/home/hyh/linux-kernel-exynos/arch/arm/include/asm/io.h:30:28: fatal error: mach/exynos-ss.h: No such file or directory
#include <mach/exynos-ss.h>
^
compilation terminated.
/home/hyh/linux-kernel-exynos/./Kbuild:81: recipe for target ‘arch/arm/kernel/asm-offsets.s’ failed
make[1]: *** [arch/arm/kernel/asm-offsets.s] Error 1
Makefile:836: recipe for target ‘prepare0’ failed
make: *** [prepare0] Error 2
hyh@ubuntu:~/linux-kernel-exynos$

出错了,改个编译工具,网上下载了一个arm-eabi-4.8,放到hyh目录下,在$PATH中加入目录/home/hyh/arm-eabi-4.8/bin,编译。这次好点,编译了大部分代码,但是在编译驱动时出错了:

hyh@ubuntu:~/linux-kernel-exynos$ make ARCH=arm CROSS_COMPILE=arm-eabi- zImage
CHK include/linux/version.h
CHK include/generated/utsrelease.h
make[1]: ‘include/generated/mach-types.h’ is up to date.
CALL scripts/checksyscalls.sh
CHK include/generated/compile.h
CC drivers/mfd/ezx-pcap.o
drivers/mfd/ezx-pcap.c: In function ‘pcap_isr_work’:
drivers/mfd/ezx-pcap.c:205:2: error: implicit declaration of function ‘irq_to_gpio’ [-Werror=implicit-function-declaration]
} while (gpio_get_value(irq_to_gpio(pcap->spi->irq)));
^
cc1: some warnings being treated as errors
scripts/Makefile.build:305: recipe for target ‘drivers/mfd/ezx-pcap.o’ failed
make[2]: *** [drivers/mfd/ezx-pcap.o] Error 1
scripts/Makefile.build:441: recipe for target ‘drivers/mfd’ failed
make[1]: *** [drivers/mfd] Error 2
Makefile:945: recipe for target ‘drivers’ failed
make: *** [drivers] Error 2
hyh@ubuntu:~/linux-kernel-samsung$

发现ezx-pcap.c文件中有一段如下代码:

static void pcap_isr_work(struct work_struct *work)
{
struct pcap_chip *pcap = container_of(work, struct pcap_chip, isr_work);
struct pcap_platform_data *pdata = pcap->spi->dev.platform_data;
u32 msr, isr, int_sel, service;
int irq;

do {
ezx_pcap_read(pcap, PCAP_REG_MSR, &msr);
ezx_pcap_read(pcap, PCAP_REG_ISR, &isr);

/* We can’t service/ack irqs that are assigned to port 2 */
if (!(pdata->config & PCAP_SECOND_PORT)) {
ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel);
isr &= ~int_sel;
}

ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
ezx_pcap_write(pcap, PCAP_REG_ISR, isr);

local_irq_disable();
service = isr & ~msr;
for (irq = pcap->irq_base; service; service >>= 1, irq++) {
if (service & 1)
generic_handle_irq(irq);
}
local_irq_enable();
ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
} while (gpio_get_value(irq_to_gpio(pcap->spi->irq)));
}

定位到申明的位置:<linux/gpio.h>,在gpio.h文件中有这样一段申明:

static inline int irq_to_gpio(unsigned irq)
{
/* irq can never have been returned from gpio_to_irq() */
WARN_ON(1);
return -EINVAL;
}

可见被加了static,外部无法访问。而且,看到注释,以及返回值,可见这个函数是无用的。分析在ezx-pcap.c文件中的这段代码,do while会执行一次这段代码,但irq_to_gpio()永远只会返回失败,所以一定只会执行一次,所以果断把 “do{” 和 “}while”  这两行代码屏蔽:

static void pcap_isr_work(struct work_struct *work)
{
struct pcap_chip *pcap = container_of(work, struct pcap_chip, isr_work);
struct pcap_platform_data *pdata = pcap->spi->dev.platform_data;
u32 msr, isr, int_sel, service;
int irq;

//do {
ezx_pcap_read(pcap, PCAP_REG_MSR, &msr);
ezx_pcap_read(pcap, PCAP_REG_ISR, &isr);

/* We can’t service/ack irqs that are assigned to port 2 */
if (!(pdata->config & PCAP_SECOND_PORT)) {
ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel);
isr &= ~int_sel;
}

ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
ezx_pcap_write(pcap, PCAP_REG_ISR, isr);

local_irq_disable();
service = isr & ~msr;
for (irq = pcap->irq_base; service; service >>= 1, irq++) {
if (service & 1)
generic_handle_irq(irq);
}
local_irq_enable();
ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
//} while (gpio_get_value(irq_to_gpio(pcap->spi->irq)));
}

再次编译,过了:

……

LD vmlinux.o
MODPOST vmlinux.o
GEN .version
CHK include/generated/compile.h
UPD include/generated/compile.h
CC init/version.o
LD init/built-in.o
LD .tmp_vmlinux1
KSYM .tmp_kallsyms1.S
AS .tmp_kallsyms1.o
LD .tmp_vmlinux2
KSYM .tmp_kallsyms2.S
AS .tmp_kallsyms2.o
LD vmlinux
SYSMAP System.map
SYSMAP .tmp_System.map
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
GZIP arch/arm/boot/compressed/piggy.gzip
AS arch/arm/boot/compressed/piggy.gzip.o
SHIPPED arch/arm/boot/compressed/lib1funcs.S
AS arch/arm/boot/compressed/lib1funcs.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: arch/arm/boot/zImage is ready
hyh@ubuntu:~/linux-kernel-exynos$

5. 后记

最后一步的编译经常会出一些具体的问题,比如某个包含文件找不到、某段代码编译出错,这种错误需要仔细分析错误提示,往往能定位到问题。源码过于庞大,而实际的应用千差万别,导致自定义后的某些模块之间出现问题。所以这样的问题只能是具体问题具体分析,没用的代码甚至可以屏蔽。这恰恰也是导致部分自定义代码无法编译通过的原因。

 

Enable Secure Shell (SSH) On Your Raspberry Pi

Posted on May 31, 2012 by Matt

Secure Shell or SSH is a network protocol that allows you to run commands on a remote device. In the case of the Raspberry Pi can you execute commands over your network from another device such as a PC or laptop. This allows you to control the Pi without attaching a keyboard, mouse or even a monitor.

The free utility “PuTTY” can be used to connect to your Pi using SSH once it is enabled.

Update : The newer Debian “Wheezy” image enabled SSH by default so Methods 1 & 2 below are not required if you are using this image.

Method 1

To start the SSH server on a Pi you can open a terminal window (i.e. LXTerminal) and type :

sudo /etc/init.d/ssh start

followed by Enter. This needs to be done every time the Pi boots.

Method 2 

To enable SSH automatically every time the Pi starts you need to :

  • Prepare an SD card with the Debian “Squeeze” image
  • Insert the SD card in your PC
  • Rename ” boot_enable_ssh.rc” as “boot.rc”
  • Eject the SD card and insert into your Pi

When the Pi boots SSH is enabled by default.

Using SSH

You can now open a remote terminal window using PuTTY or any other SSH enabled client. This will allow you to type commands as if you were using the Pi directly.

You can even use SSH to transfer files using a file explorer style interface on your PC. I recommend the free utility WinSCP which will allow you to drag and drop. Transfering files is easy and similar to using FTP.

 

转自:http://www.raspberrypi-spy.co.uk/2012/05/enable-secure-shell-ssh-on-your-raspberry-pi/

 

嵌入式设计之:初学扫盲场

导语:

软硬结合才是王道!

搞了10多年的软件,始终抱有对底层机制的极大兴趣,不可避免的凯觎起硬件的地盘。苦于没有机会接触硬件设计方面的东西,所以只是纸上“看”兵,更别说动手做了。最近做了一些modbus协议方面的软件,加之对Linux还算比较熟悉,遂产生了使用硬件实现modbus数据集合的想法。

临渊羡鱼不如退而结网,说做就做吧,此时不动手更待老死墓中?

一、MUC简介和选型:RAM or Flash

一度分不清MCU中的Flash和RAM充当什么角色。

实际上,MCU中的RAM相当于PC的内存,程序都是装在在这里供MCU执行单元执行的。而Flash相当于PC的硬盘,如果是裸奔,那么写的固件都是烧在这个Flash里面的。MCU中的Flash一般都很小,只有几十K或者几百K,一度以为Flash才是内存或者二级缓存啥的,汗。

如果你规划中的系统需要运行Linux,那么你需要给它加个“硬盘”,而读写硬盘的地址就是通过Flash来映射的。具体的可能在后面会涉及到。

RAM:MCU的片上内存;

FLASH:MCU的片上“硬盘”,有擦写次数限制,一般为10^4次;

EEPROM:用来存储参数的区域,据说可以用Flash模拟EERPOM;

SRAM:通俗讲,就是扩展的内存。

二、电路设计:自定义元件库

有些元件在库中是找不到的,比如新出的MCU。这个时候只有自己做了,方法是在orCAD Capture中新建Library,制作完毕后就可以在电路设计时使用。

但是我碰到一个悲催的问题,设计完元件图,然后在电路设计时使用,添加完元件后,缺发现有个地方不对,需要到元件库中修改此元件,改完之后,再在电路设计图中使用,却总是提示:

Part is out of date with respect to the design cache.use update cache to synchronize the part in the cache with the library.

郁闷的是此问题卡了我两天!

解决俄方法也很简单,网上查到的(悲催的百度只能搜国内的内容,而且基本都是广告,真的是一点用的没有,纯粹一个无赖废物。今天在打开google主页之前默认三遍上帝保佑,还真打开了,感谢上帝顾念苍生!):

选择File目录树,点file.dsn前面的”+”,再点Design Cache前面的”+”,找到你所出错的Part名称,右击,Update一下,问题解决!!!

 

【转】Linux内核中进程上下文和中断上下文

概要:

处理器总处于以下状态中的一种:

1、内核态,运行于进程上下文,内核代表进程运行于内核空间;

2、内核态,运行于中断上下文,内核代表硬件运行于内核空间;

3、用户态,运行于用户空间。

用户空间的应用程序,通过系统调用,进入内核空间。这个时候用户空间的进程要传递很多变量、参数的值给内核,内核态运行的时候也要保存用户进程的一些寄存器值、变量等。所谓的“进程上下文”,可以看作是用户进程传递给内核的这些参数以及内核要保存的那一整套的变量和寄存器值和当时的环境等。

硬件通过触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理。所谓的 “中断上下文”,其实也可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境(主要是当前被打断执行的进程环境)。

在进程上下文中,可以用current宏关联当前进程,也可以睡眠,也可以调用调度程序。

看《Linux内核设计与实现 第二版》时,看到一句 “当执行一个中断处理程序或下半部时,内核处于中断上下文。”

Linux内核设计与实现 第二版下载地址:http://www.linuxidc.com/Linux/2011-05/35530.htm

中断上下文和进程上下文是两个没有瓜葛的概念,尽管中断上下文的current宏会被指向被中断的进程。

因为没有进程的背景,所以中断上下文不可以睡眠,也不能调用会引起睡眠的函数,如kmalloc。中断上下文中的代码应该尽可能迅速简洁。

分析:

内核空间和用户空间是操作系统理论的基础之一,即内核功能模块运行在内核空间,而应用程序运行在用户空间。现代的CPU 都具有不同的操作模式,代表不同的级别,不同的级别具有不同的功能,在较低的级别中将禁止某些操作。Linux系统设计时利用了这种硬件特性,使用了两个级别,最高级别和最低级别,内核运行在最高级别(内核态),这个级别可以进行所有操作,而应用程序运行在较低级别(用户态),在这个级别,处理器控制着对硬件的直接访问以及对内存的非授权访问。内核态和用户态有自己的内存映射,即自己的地址空间。

正是有了不同运行状态的划分,才有了上下文的概念。用 户 空间的应用程序,如果想要请求系统服务,比如操作一个物理设备,或者映射一段设备空间的地址到用户空间,就必须通过系统调用来(操作系统提供给用户空间的接口函数)实现 。如下图所示:

通过系统调用,用户空间的应用程序就会进入内核空间,由内核代表该进程运行于内核空间,这就涉及到上下文的切换,用户空间和内核空间具有不同的地址映射,通用或专用的寄存器组,而用户空间的进程要传递很多变量、参数给内核,内核也要保存用户进程的一些寄存器、变量等,以便系统调用结束后回到用户空间继续执行,所谓的进程上下文,就是一个进程在执行的时候,CPU的所有寄存器中的值、进程的状态以及堆栈中的内容 ,当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行。

同理,硬件通过触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理,中 断 上下文就可以理解为硬件传递过来的这些参数和内核需要保存的一些环境(主要是被中断的进程的环境)。

Linux内核工作在进程上下文或者中断上下文。提供系统调用服务的内核代码代表发起系统调用的应用程序运行在进程上下文 ;另一方面,中断处理程序,异步运行在中断上下文。中断上下文和特定进程无关。

运行在进程上下文的内核代码是可以被抢占的 (Linux2.6支持抢占)。但是一个中断上下文,通常都会始终占有CPU (当然中断可以嵌套,但我们一般不这样做),不可以被打断 。正因为如此,运行在中断上下文的代码就要受一些限制,不能做下面的事情:

1、睡眠或者放弃CPU。

这样做的后果是灾难性的,因为内核在进入中断之前会关闭进程调度,一旦睡眠或者放弃CPU,这时内核无法调度别的进程来执行,系统就会死掉

2、尝试获得信号量

如果获得不到信号量,代码就会睡眠,会产生和上面相同的情况

3、执行耗时的任务

中断处理应该尽可能快,因为内核要响应大量服务和请求,中断上下文占用CPU时间太长会严重影响系统功能。

4、访问用户空间的虚拟地址

因为中断上下文是和特定进程无关的,它是内核代表硬件运行在内核空间,所以在终端上下文无法访问用户空间的虚拟地址。

[日期:2011-05-05] 来源:Linux社区 作者:wohaoa
原文:http://www.linuxidc.com/Linux/2011-05/35529.htm

Linux Boot Disk Format

本文介绍了如何为DevKit8000格式化SD卡,以便通过SD卡启动安装在SD卡中的Linux操作系统!


How to create a dual-partition card for DevKit8000 to boot Linux from first partition and have root file system at second partition.

一、Introduction

This guide is meant for those looking to create a dual-partition card, booting from a FAT partition that can be read by the OMAP3 ROM bootloader and Linux/Windows, then utilizing an ext3 partition for the Linux root file system.

二、Details

Text marked with [] shows user input.

1Determine which device the SD Card Reader is on your system

Plug the SD Card into the SD Card Reader and then plug the SD Card Reader into your system. After doing that, do the following to determine which device it is on your system.

$ [dmesg | tail]

[ 6854.215650] sd 7:0:0:0: [sdc] Mode Sense: 0b 00 00 08 [ 6854.215653] sd 7:0:0:0: [sdc] Assuming drive cache: write through

[ 6854.215659] sdc: sdc1

[ 6854.218079] sd 7:0:0:0: [sdc] Attached SCSI removable disk

[ 6854.218135] sd 7:0:0:0: Attached scsi generic sg2 type 0

In this case, it shows up as /dev/sdc (note sdc inside the square brackets above).

2Check to see if the automounter has mounted the SD Card

Note there may be more than one partition (only one shown in the example below).

$ [df -h]

Filesystem Size Used Avail Use% Mounted on

/dev/sdc1 400M 94M 307M 24% /media/disk

Note the “Mounted on” field in the above and use that name in the umount commands below.

3If so, unmount it

$ [umount /media/disk]

4Start fdisk

Be sure to choose the whole device (/dev/sdc), not a single partition (/dev/sdc1).

$ [sudo fdisk /dev/sdc]

5Print the partition record

So you know your starting point. Make sure to write down the number of bytes on the card (in this example, 2021654528).

Command (m for help): [p]

Disk /dev/sdc: 2021 MB, 2021654528 bytes

255 heads, 63 sectors/track, 245 cylinders

Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System

/dev/sdc1 * 1 246 1974240+ c W95 FAT32 (LBA)

Partition 1 has different physical/logical endings:

phys=(244, 254, 63) logical=(245, 200, 19)

6Delete any partitions that are there already

Command (m for help): [d]

Selected partition 1

7Set the Geometry of the SD Card

If the print out above does not show 255 heads, 63 sectors/track, then do the following expert mode steps to redo the SD Card:

1)、Go into expert mode.

Command (m for help): [x]

2)、Set the number of heads to 255.

Expert Command (m for help): [h]

Number of heads (1-256, default xxx): [255]

3Set the number of sectors to 63.

Expert Command (m for help): [s]

Number of sectors (1-63, default xxx): [63]

4Now Calculate the number of Cylinders for your SD Card.

#cylinders = FLOOR (the number of Bytes on the SD Card (from above) / 255 / 63 / 512 )

So for this example: 2021654528 / 255 / 63 / 512 = 245.79. So we use 245 (i.e. truncate, don’t round).

5Set the number of cylinders to the number calculated.

Expert Command (m for help): [c]

Number of cylinders (1-256, default xxx): [enter the number you calculated]

6Return to Normal mode.

Expert Command (m for help): [r]

8Print the partition record to check your work

Command (m for help): [p]

Disk /dev/sdc: 2021 MB, 2021654528 bytes

255 heads, 63 sectors/track, 245 cylinders

Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System

9Create the FAT32 partition for booting and transferring files from Windows

Command (m for help): [n]

Command action

e extended

p primary partition (1-4)

[p]

Partition number (1-4): [1]

First cylinder (1-245, default 1): [(press Enter)]

Using default value 1

Last cylinder or +size or +sizeM or +sizeK (1-61, default 61): [+5]

Command (m for help): [t]

Selected partition 1

Hex code (type L to list codes): [c]

Changed system type of partition 1 to c (W95 FAT32 (LBA))

10Mark it as bootable

Command (m for help): [a]

Partition number (1-4): [1]

11Create the Linux partition for the root file system

Command (m for help): [n]

Command action

e extended

p primary partition (1-4)

[p]

Partition number (1-4): [2]

First cylinder (7-61, default 7): [(press Enter)]

Using default value 52

Last cylinder or +size or +sizeM or +sizeK (7-61, default 61): [(press Enter)]

Using default value 245

12Print to Check Your Work

Command (m for help): [p]

Disk /dev/sdc: 2021 MB, 2021654528 bytes

255 heads, 63 sectors/track, 245 cylinders

Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot Start End Blocks Id System

/dev/sdc1 * 1 6 409626 c W95 FAT32 (LBA)

/dev/sdc2 7 61 1558305 83 Linux

13Save the new partition records on the SD Card

This is an important step. All the work up to now has been temporary.

Command (m for help): [w]

The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 16: Device or resource busy.

The kernel still uses the old table.

The new table will be used at the next reboot.

WARNING: If you have created or modified any DOS 6.x

partitions, please see the fdisk manual page for additional

information.

Syncing disks.

14Format the partitions

The two partitions are given the volume names LABEL1 and LABEL2 by these commands. You can substitute your own volume labels.

$ [sudo mkfs.msdos -F 32 /dev/sdc1 -n LABEL1]

mkfs.msdos 2.11 (12 Mar 2005)

$ [sudo mkfs.ext3 -L LABEL2 /dev/sdc2]

mke2fs 1.40-WIP (14-Nov-2006)

Filesystem label=

OS type: Linux

Block size=4096 (log=2)

Fragment size=4096 (log=2)

195072 inodes, 389576 blocks

19478 blocks (5.00%) reserved for the super user

First data block=0

Maximum filesystem blocks=402653184

12 block groups

32768 blocks per group, 32768 fragments per group

16256 inodes per group

Superblock backups stored on blocks:

32768, 98304, 163840, 229376, 294912

Writing inode tables: done

Creating journal (8192 blocks): done

Writing superblocks and filesystem accounting information:

 

 

 

TI OMAP3530 数字媒体处理器

OMAP3530主要面向多媒体,此MCU片上集成了ARM9 Cortex-A8 600MHz内核、达芬奇C64X DSP 430MHz核、SX 2D/3D加速器,算是相当强悍的了,下面是一段来自TI官方的超声波扫面转换演示视频:

[flv]http://www.roboby.com/wp-content/uploads/2011/03/超声波扫描转换演示-OMAP3530-EVM-Basic-Small.flv[/flv]

TI还为此开发了DVSDK数字视频开发库,居然有500多兆,带源码,这对于那些基于此处理器开发DSP图像处理的人来说是相当宝贵的资源了!

从这里可以了解相关信息

http://www.ti.com

E文不好的还是从这里看中文的吧:

http://www.ti.com.cn

花了1680RMB拿下DevKit8000

今天终于拿到TI OMAP3530的开发板了,天漠的DevKit8000,花了1680人民币。

下一步,先熟悉熟悉Linux定制方面的知识。有了板子,也终于可以边看边实践了!

然后嘛,想做的东西就太多了,什么视觉系统啊、行走机构啊、蓝牙通讯啦……,每项都是重大课题,里面一点点东西都是好多公司花好多money在搞的东西,个人搞这个,有时候真觉得不现实。

但是,人不都得有点追求是不是?有追求,还得有点实现追求的实际行动是不是?搞就搞了,想这么多干嘛   :)~~~~~~~~~

 

 

看上TI 的 OMAP3530 开发板了

看上这块板子了:EVM3530-B3-1880-LUNC0

理由嘛:

1、CPU是TI的,TI OMAP3530 多核处理器,600MHz 的ARM Cortex-A8内核;430MHz TMS320C64x+ DSP POWERVR SG™2D/3D图形加速处理引擎。如果DSP资料开放,可以考虑移植OpenCV,利用DSP能力实现图像识别

2、一个10M/100M网卡、1个RS232、2个UART、4个USB2.0 Host、1个USB2.0 OTG

3、音频输入输出口、VGA口、视频输入输出口

4、SD卡槽

参数如下:
产品型号 技术参数 价格(RMB)
EVM3530-B3-1880-LUNC0 处理器:TI OMAP3530
内存:256M内存 + 256M Flash,
网络:采用LAN9221,10/100Mbps
USB接口:4个Host + 1个OTG
串口:1个RS232 + 2个串口插针
显示接口:标准DVI
1380

 

问题是1380大洋,还不带显示屏,如果加个4.3寸显示屏380,可就到1760了,nnd,喉贵!

选配表:

 
产品型号 技术参数 价格(RMB)
EVM3530-B3-1880-LUNC0 处理器:TI OMAP3530
内存:256M内存 + 256M Flash,
网络:采用LAN9221,10/100Mbps
USB接口:4个Host + 1个OTG
串口:1个RS232 + 2个串口插针
显示接口:标准DVI
1380
SOM-3530-B2-1880-C 1、处理器:TI OMAP3530
2、内存:256M内存 + 256M Flash
3、电源管理:高性能电源管理模块,支持USB
4、B2B接口:4x 0.5mm 60Pin B2B
600
SBC3530-A2-1880-LUAC0 处理器:TI OMAP3530
内存:256M内存 + 256M Flash,
网络:采用LAN9221,10/100Mbps
USB接口:4个Host + 1个OTG
串口:1个RS232 + 2个串口插针
显示接口:标准VGA
视频输入:四路模拟视频输入 + 1路数字摄像头
1580
DEV3530-128M 处理器:TI OMAP3530 ,内存:128M内存 + 128M Flash ,DVI视频接口:支持多种高分辨率,音频接口:2.1声道,网络:10/100Mbps 采用LAN9221,USB接口:1个OTG,串口:1个RS232 ,SD卡槽:最高支持32G容量 798
支持OMAP3530开发板的4.3寸群创液晶屏模块   380
支持OMAP3530开发板的7寸群创液晶屏模块   480
WIFI   250
 Camera   300
XDS100V2 仿真器   368
omap3530启动SD Card   55
适用于DEV3530系列   40

 

另两款:

DevKit8000评估套件+4.3触摸屏 OMAP3530开发板 256M

这个带了个4.3的屏幕,CPU与上一款一样,区别呢?

地址:http://item.taobao.com/item.htm?id=3781993929

报价¥1690,

256MB DevKit8000评估套件 配4.3寸屏 OMAP3530开发板

更上一款一样啊,报价多了9块钱 :)

地址:http://item.taobao.com/item.htm?id=3920551293

DevKit8000系列的参数及配置如下:

T1系列 主板型号 税前价 税后价 说明
OMAP3530 DevKit8000标准配置 888 1,050 单板+SD+电源+串口线
DevKit8000完全配置1 1,599 1,870 4.3”触摸屏及全部配件
DevKit8000完全配置2 1,799 2,100 7”触摸屏及全部配件
DevKit8000(2G)标准配置 988 1,160 单板+SD+电源+串口线
DevKit8000(2G)完全配置1 1,699 2,000 4.3”触摸屏及全部配件
DevKit8000(2G)完全配置2 1,899 2,220 7”触摸屏及全部配件
Mini8100 540 630 SBC8100核心板
SBC8100单板机 2,800 3,280 配512M SD卡、串口线、网线、5V/2A电源、USB转接线(Mini-B to A)、S-Video线。
SBC8100套件1 3,150 3,690 单板机配4.3寸触摸屏。
SBC8100套件2 3,300 3,860 单板机配7寸触摸屏。
Mini8100(2G) 640 750 SBC8100核心板
SBC8100(2G)单板机 2,900 3,400 配512M SD卡、串口线、网线、5V/2A电源、USB转接线(Mini-B to A)、S-Video线。
SBC8100(2G)套件1 3,250 3,800 单板机配4.3寸触摸屏。
SBC8100(2G)套件2 3,400 3,980 单板机配7寸触摸屏。
VSS3530(1G) 1299 1520 配12V/1.25A电源、串口线、串口转接板

 

配套模块型号 税前价 税后价 说明
CAM8000-A 250 290 模拟摄像头输入模块,配套DevKit8000和SBC8100
CAM8000-U 500 585 USB数字摄像头输入模块,配套DevKit8000和SBC8100
VGA8000-A 250 290 LCD转VGA输出模块,配套DevKit8000和SBC8100
模拟摄像头 250 290 提供配套电源,不带支架和连接线。
WF8000-U 250 290 USB WiFi模块,配套DevKit8000
GPRS8000-S 400 470 串口GPRS模块,配套DevKit8000
GPS8000-S 300 350 串口GPS模块,配套DevKit8000
CDMA8000-U 450 525 USB 3G模块,配套DevKit8000和SBC8100

 

仿真器 TDS560 Plus仿真器 4,800 4,800 适用于TI 公司C2000、C5000、C6000芯片及OMAP、DaVinci、ARM7、ARM9 
XDS100v2 380 445 支持多种处理器:TMS320C28x、TMS320C54x、TMS320C55x、TMS320C64x+、TMS320C674x、ARM 9、 ARM Cortex R4、ARM Cortex A8 

 

DevKit8000评估套件

 

– 基于TI OMAP3530处理器,板载256MByte DDR SDRAM、256MByte NAND Flash
– 支持DVI-D、VGA、TFT-LCD三种显示输出,可输出高清信号
– 支持摄像头输入、USB WiFi、GPS定位、GPRS通讯、CDMA
– 支持XDS100v2仿真器和TDS560 Puls仿真器调试
– 外扩USB OTG,串口,网口,摄像头,音频,SD/MMC,键盘等接口
– 支持WinCE 6.0及Linux2.6.28系统,完全兼容beagleboard
   

    DevKit8000评估主板为开发者使用TI OMAP35x系列处理器提供了完善的软件开发环境,支持linux-2.6.28操作系统及WinCE6.0操作系统,并包含完善的底层驱动程序,方便用户快速评估OMAP35x系列处理器、设计系统驱动及其定制应用软件,并提供有成熟的操作系统Google Android及Angstrom(GPE)的发布版本,方便用户快速体验OMAP35x处理器的强大的数据运算处理能力,也可降低产品开发周期,实现面向消费电子、医疗仪器、多媒体处理、视频监控、工业控制等领域的产品快速上市。
    DevKit8000评估套件是一个功能丰富的开发平台,为嵌入式设计人员提供快捷简单的实践方式来评估OMAP3530处理器。该套件提供了一个完整的实验评估平台,包括一个4.3″LCD屏、SD卡、电源及各种接口转接线(串口转接线,USB OTG转接线,S-Video转接线,HDMI转DVI-D转接线)等。该套件使设计者能够基于DevKit8000的板载系统快速开发出基于OMAP3530芯片的相关产品。

 
硬件特性
CPU单元: 

  • OMAP3530应用处理器(Pin to Pin兼容OMAP3503,OMAP3515,OMAP3525处理器)
  • 600-MHz ARM Cortex-A8 Core
  • 430MHz TMS320C64x+ DSP Core
  • 128MByte DDR SDRAM (可选配256MB DDR SDRAM)
  • 128MByte NAND Flash (可选配256MB NAND Flash)
  • 6层板

通讯接口: 

  • 1个S-VIDEO接口
  • 1个音频输入接口
  • 1个双声道音频输出接口
  • 1个10/100M以太网(芯片:DM9000,RJ45接口)
  • 1个高速USB2.0 OTG接口(芯片:TPS65930),速度达480Mbps
  • 1个高速USB2.0 Host接口
  • 2个串口(1个3线RS232电平串口,1个5线TTL电平串口)
  • 1个SD/MMC接口
  • 1个6*6键盘接口
  • 1个14针标准JTAG接口
  • 1个CAMERA接口(可外接CCD和CMOS的摄像头)
  • 4个按键(1个启动引导按键,1个复位按键及2个用户按键)
  • 扩展接口(包含McSPI,McBSP,I2C,HDQ及SD/MMC接口)

显示单元: 

  • 1个HDMI接口(芯片:TFP410),可输出分辨率1280×720,码率30fps的DVI-D高清信号
  • S-Video视频输出接口
  • 24bit真彩色LCD接口(含4线触摸屏接口,分辨率可支持2048*2048)

电气参数:

  • 工作温度:-40 ~ 85℃(芯片支持)
  • 环境湿度:20% ~ 90% ,非冷凝
  • 机械尺寸:110 mm * 95 mm
  • 电气指标:+5V电源供电,电流150mA
软件特性
Linux系统特性:

  • Linux版本:Linux2.6.28
  • 编译器版本:gcc version 4.2.1 (CodeSourcery Sourcery G++ Lite 2007q3-51)
  • 启动方式:支持网络、SD卡或NAND Flash中启动引导Linux系统
  • 更新方式:支持网络或SD卡更新映像
  • 文件系统格式:Ramdisk文件系统、UBI文件系统
  • 文件系统支持:ROM/CRAM/EXT2/EXT3/FAT/NFS/JFFS2/UBIFS
  • 设备管理支持:udev设备管理
  • 驱动支持:Serial、RTC、NAND、DVI、LCD、 触摸屏、SD卡、USB、键盘、音频、网络、LED、CAMERA、WiFi、GPS、GPRS、CDMA

 

Wince系统特性:

  • Wince版本:Wince6.0
  • 启动方式:网络、SD卡或NAND Flash中启动引导Wince系统
  • 更新方式:网络或SD卡更新映像
  • 文件系统:支持HIVE注册表支持及ROM文件系统(可读写)
  • 支持微软的远程调试工具对目标板的文件、注册表、进程及线程的管理
  • 系统特性:支持.NET Compact Framework 3.5及KITL内核调试
  • 电源管理:背光管理、电池管理、休眠\唤醒功能
  • 驱动支持:DVI、LCD、SD卡、键盘、音频、网络、NLED、USB、RTC、Watchdog、WiFi、GPS、GPRS、CDMA

Demo演示:

  • Angstrom系统:嵌入式操作系统,支持音频播放,网络浏览,图形编辑及文档处理
  • Android系统:Google开发的基于Linux平台的开源手机操作系统
  • DVSDK软件包:支持MPEG4,MPEG2,H264,mp3,aac音/视频格式DSP硬解码及2D/3D特效

源码及工具:

  • 提供u-boot 1.3.3源码及linux2.6.28内核源码(包含驱动源码)
  • 提供gcc交叉编译工具
  • 提供Wince6.0 BSP源码包(包含驱动源码)及SDK安装包

 

订购信息

产品编号 T6010074
产品名称 DevKit8000评估套件
标准配件  1) Devkit8000评估版 *1
 2) SD卡(512MByte) *1
 3) 交叉串口线 *1
 4) 5V、2A电源适配器 *1
 5) CD光盘(包含用户手册、PDF原理图、外设驱动、源代码等)*1
完全配件  1) 4.3寸 LCD屏,分辨率(480*272)LCD8000-43T  *1
 2) SD卡(512MByte) *1
 3) 交叉串口线 *1
 4) 触摸笔 *1
 5) Mini USB A线转USB A型母头 *1
 6) Mini USB B线转USB A型公头 *1
 7) USB HUB(包括HUN电源、连接线) *1
 8) 网线 *1
 9) HMDI转DVI-D转接线 *1
 10) S-Video线 *1
 11) 5V、2A电源适配器 *1
 12) 光盘(包含用户手册、PDF原理图、外设驱动、源代码等) *1
可选配件  1)10.4寸LCD屏(LCD8000-104T,分辨率为800*600)(30套起订)一般不出货,
 2)7寸LCD屏(LCD8000-70T,分辨率为800*480),
 3)XDS100v2 仿真器(XDS100v2 仿真器
 4)560仿真器(TDS560 Plus仿真器
 5)VGA液晶显示(VGA8000,分辨率 1024*768)
 6)模拟摄像头模块(CAM8000-A,分辨率为720*576(PAL制式))
 7)数字摄像头模块(CAM8000-U
 8)USB WiFi模块(WF8000-U
 9)GPRS无线通讯模块(GPRS8000-S
 10)GPS定位系统模块(GPS8000-S
 11)CDMA8000-U 3G模块(CDMA8000-U
 
备注   CAM8000-A 摄像头模块现只支持Linux

刚看完《嵌入式Linux基础教程》

昨晚刚刚看完《嵌入式Linux基础教程》,这本书帮助嵌入式Linux爱好者进驻这个领域、避免走太多弯路、澄清一些必须了解的概念、介绍必须知道的内容,相当的不错,推荐读!
《嵌入式Linux基础教程》
《嵌入式Linux基础教程》

申明一下,本人非托,只是刚刚读完这本书,觉得相当不错,推荐给大家:)

【转】Ubuntu 10.04建立源碼樹實現最簡單的驅動模塊

这篇文章写得相当不错,推荐阅读,同时在此感谢一下原作者 微笑

——hyh

 

飛帆網  http://www.pyoix.com  2010年09月17日 09:02

本文參考了很多網上的文章,在這裏先感謝網上的朋友們。編譯了一天終於可以導入Ubuntu 10.04 設備驅動程序最簡單的 Hello World。

其實我也是一個初學者,但衹要堅持學習,一定會理解的。

現在我講述在Ubuntu 10.04下安裝的過程:

1.安裝編譯內核所需要的軟件

build-essential、autoconf、automake、cvs、subversion

apt-get install build-essential kernel-package libncurses5-dev

libncurses5這個軟件包在使用menuconfig配置內核的時候會用到。

2.下載內核源碼

使用uname -r 命令查看當前的內核版本號,我的是2.6.32-25-generic,使用apt-cache search linux-source查看軟件庫的源碼包,我查詢到的源碼包有:
linux-source – Linux kernel source with Ubuntu patches
linux-source-2.6.32 – Linux kernel source for version 2.6.32 with Ubuntu patches

我選擇linux-source-2.6.32 – Linux kernel source for version 2.6.32 with Ubuntu patches

sudo apt-get install linux-source-2.6.32

下載好後cd /usr/src 目錄下就可以看見linux-source-2.6.32.tar.bz2,然後解壓到當前的目錄
sudo tar xjvf linux-source-2.6.32.tar.bz2

解壓完畢,會生成linux-source-2.6.32目錄

3.編譯內核源碼

在編譯之前我們需要 Ubuntu原來內核的一個配置文件

這是我/usr/src目錄下的文件預覽:

drwxr-xr-x  4 root root     4096 2010-09-04 21:31 fglrx-8.723.1
drwxr-xr-x 24 root root     4096 2010-09-04 20:35 linux-headers-2.6.32-25
drwxr-xr-x  7 root root     4096 2010-09-04 20:35 linux-headers-2.6.32-25-generic
drwxr-xr-x 25 root root     4096 2010-09-16 21:39 linux-source-2.6.32
-rw-r–r–  1 root root 65846876 2010-09-01 22:41 linux-source-2.6.32.tar.bz2

現在我們需要linux-headers-2.6.32-25-generic目錄下的.config文件,我們把它拷貝到我們剛下好解壓的目錄,也就是linux-source-2.6.32

sudo cp /usr/src/linux-headers-2.6.32-25-generic/.config /usr/src/linux-2.6.32

接下來切換到root用戶
sudo -i

cd /usr/src/linux-2.6.32

make menuconfig

終端會彈出一個配置界面

最後有兩項:load a kernel configuration…
save a kernel configuration…
選擇load a kernel configuration保存,然後在選擇save akernel configuration再保存退出,並退出配置環境。

接下來我們開始編譯

cd /usr/src/linux-2.6.32
make
記住一定要是管理員帳號運行,這個過程真的很久,如果你的cpu是雙核的可以在make後面加個參數,make -j4.

make bzImage 執行結束後,可以看到在當前目錄下生成了一個新的文件: vmlinux, 其屬性爲-rwxr-xr-x。
make modules /* 編譯 模塊 */
make modules_install  這條命令能在/lib/modules目錄下產生一個目錄。

4.測試驅動模塊

這裏就抄個程序測試了,初學者顧不了那麽多了。

我在 /home/shana/linux_q/ 目錄下創建2個文本文件 hello.c Makefile

//hello.c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE(“Dual BSD/GPL”);

static int hello_init(void)
{
printk(KERN_ALERT “Hello, worldn”);
return 0;
}

static void hello_exit(void)
{
printk(KERN_ALERT”Goodbye, cruel worldn”);
}

module_init(hello_init);
module_exit(hello_exit);

Makefile 文件

obj-m := hello.o
KERNELDIR := /lib/modules/2.6.20/build
PWD := $(shell pwd)

modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

需要註意的是makefile的格式$(MAKE)前面要加個tab.
make 編譯,產生如下文件:
hello.c   hello.mod.c  hello.o   modules.order
hello.ko  hello.mod.o  Makefile  Module.symvers

5.加載模塊到內核去

sudo insmod ./hello.ko 這個命令把hello.ko加載到內核

sudo rmmod hello 這個命令是把hello這個模塊移除掉

lsmod 這個命令可以查看當前所有的驅動模塊

程序的輸出結果可以在
/var/log/syslog文件中查看

Sep 16 21:50:10 wuyangyu-desktop kernel: [10428.551271] Hello,World
Sep 16 21:55:45 wuyangyu-desktop kernel: [10763.644605] Goodbye,cruel world

這是程序的輸出。

 

本文来自:http://www.pyoix.com/a/735.html

收藏备查

【转】Linux2.6.19内核源码目录树

 

[日期:2010-09-18]
来源:Linux社区  作者:Linux

这里简单的介绍下Linux 2.6.19内核源代码(最新内核源代码可从这里下载)目录树结构。

arch:包含和硬件体系结构相关的代码,每种平台占一个相应的目录。和32位PC相关的代码存放在i386目录下,其中比较重要的包括kernel(内核核心部分)、mm(内存管理)、math-emu(浮点单元仿真)、lib(硬件相关工具函数)、boot(引导程序)、pci(PCI总线)和power(CPU相关状态)。

block:部分块设备驱动程序。

crypto:常用加密和散列算法(如AES、SHA等),还有一些压缩和CRC校验算法。

Documentation:关于内核各部分的通用解释和注释。

drivers:设备驱动程序,每个不同的驱动占用一个子目录。

fs:各种支持的文件系统,如ext、fat、ntfs等。

include:头文件。其中,和系统相关的头文件被放置在linux子目录下。

init:内核初始化代码(注意不是系统引导代码)。

ipc:进程间通信的代码。

kernel:内核的最核心部分,包括进程调度、定时器等,和平台相关的一部分代码放在archmm目录下。

net:网络相关代码,实现了各种常见的网络协议。

scripts:用于配置内核文件的脚本文件。

security:主要是一个SELinux的模块。

sound:常用音频设备的驱动程序等。

usr:实现了一个cpio。

在i386体系下,系统引导将从arch/i386/kernel/head.s开始执行,并进而转移到init/main.c中的main()函数初始化内核。可按流程顺序进行阅读(主要数据结构定义要弄清),对于各模块代码粗略看看就可以了,重点应该放在你所要研究的那个方向。要知道,一个人掌握全部Linux内核源代码是很困难的!

 

本文来自:http://www.linuxidc.com/Linux/2010-09/28674.htm

【转】Ubuntu学习—编译源码包

 

以tree实用程序(以树型结构获取目录树)为例,介绍Ubuntu中如何管理源码包,包括查询,获取,编译源码包,直至安装。

1) 在获取源码包之前,确保在软件源配置文件/etc/apt/sources.list中添加了deb-src项

2) 使用如下命令获取tree源码包的详细信息:

sudo apt-cache showsrc tree

这用来查询当前镜像站点中是否有该源码包。

3)源码包中通常包含3个文件,分别以dsc,orig.tar.gz和diff.gz为后缀名。使用”apt-get source”命令来获取源码包,它会将源码包下载到用户当前目录,并在命令执行过程中,调用dpkg-source命令,根据dsc文件中的信息,将源码包解压到同名目录中,应用程序的源代码就在这里面。

sudo apt-get source tree

要强调的是,在下载源码包前,必须确保安装了dpkg-dev(执行”apt-get install dpkg-dev”来安装),否则,只会下载源码包的3个文件,但不会解压缩源码包。当然你也可以自己用dpkg-source命令去解压缩源码包。

4)在编译源码包前,需要安装具有依赖关系的相关软件包。使用”apt-get build-dep”命令可以主动获取并安装所有相关的软件包。

sudo apt-get build-dep tree

5)现在可以来编译源码包了,首先进入源码所在目录,使用dpkg-buildpackage命令来编译源码包,它会将生成的Deb软件包放置在上层目录中。

cd tree-1.5.1.2
sudo dpkg-buildpackage

这样就会编译生成tree-1.5.1.2-1_i386.deb

6)安装软件包。使用”dpkg –i”命令来安装生成的Deb软件包。

sudo dpkg –I tree-1.5.1.2-1_i386.deb

7)测试tree程序,我们用它来查看编译所在工作目录的内容。

tree –L 2

作者:洞庭散人

出处:http://phinecos.cnblogs.com/

本博客遵从Creative Commons Attribution 3.0 License,若用于非商业目的,您可以自由转载,但请保留原作者信息和文章链接URL。

【转】Linux 2.6.3x内核源码编译和安装

 

[日期:2011-02-25]
来源:Linux社区  作者:wdwbw

新版本Linux内核的编译特别简单
下载内核并解压后,先进行配置
make menuconfig
注意要选择General Setup-> enable deprecated sysfs features to support old…(即CONFIG_SYSFS_DEPRECATED_V2),否则会报错“mount:could not find filesystem ‘/dev/root’”

然后
make

再后(如果原有版本与此相同,先备份/lib/modules下该版本)
make modules_install

最后,备份/boot下现有内容,再执行
make install

有些机器启动会报重复插入dm-region-hash.ko模块
解决方法如下

解压修改内核:
    cd /tmp/
    mkdir newinitrd
    cd newinitrd/
    zcat /boot/initrd-2.6.35.1.img |cpio -i

    vim init
找到这2段一样的,去掉其中1段重复的:

    echo “Loading dm-region-hash.ko module”
    insmod /lib/dm-region-hash.ko
    echo “Loading dm-region-hash.ko module”
    insmod /lib/dm-region-hash.ko

从新打包新内核:

    find .|cpio -c -o > ../initrd
    cd ../
    gzip -9 < initrd > initrd-2.6.35.1.img
    rm -fr /boot/initrd-2.6.35.1.img
    cp initrd-2.6.35.1.img /boot

重启验证

 

本文来自:http://www.linuxidc.com/Linux/2011-02/32672.htm

【转】Linux内核源码树学习:Kconfig和Makefile

 

Linux内核源码树的每个目录下都有两个文档Kconfig和Makefile。分布到各目录的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录源文档相关的内核配置菜单。在执行内核配置make menuconfig时,从Kconfig中读出菜单,用户选择后保存到.config的内核配置文档中。在内核编译时,主Makefile调用这个.config,就知道了用户的选择。这个内容说明了,Kconfig就是对应着内核的每级配置菜单。

假如要想添加新的驱动到内核的源码中,要修改Kconfig,这样就能够选择这个驱动,假如想使这个驱动被编译,则要修改Makefile。添加新的驱动时需要修改的文档有两种(如果添加的只是文件,则只需修改当前层Kconfig和Makefile文件;如果添加的是目录,则需修改当前层和目录下的共一对Kconfig和Makefile)Kconfig和Makefile。要想知道怎么修改这两种文档,就要知道两种文档的语法结构。

Kconfig:每个菜单都有一个关键字标识,最常见的就是config。语法:config symbol,是个新的标记的菜单项,options是在这个新的菜单项下的属性和选项。

1,每个config菜单项都要有类型定义,bool布尔类型、 tristate三态:内建、模块、移除。bool类型的只能选中或不选中,tristate类型的菜单项多了编译成内核模块的选项,假如选择编译成内核模块,则会在.config中生成一个CONFIG_HELLO_MODULE=m的配置;假如选择内建,就是直接编译成内核映像,就会在.config中生成一个CONFIG_HELLO_MODULE=y的配置。

2,依赖型定义depends on或requires,指此菜单的出现和否依赖于另一个定义

config HELLO_MODULE

bool “hello test module”

depends on ARCH_PXA

这个例子表明HELLO_MODULE这个菜单项只对XScale处理器有效。

3,帮助性定义

只是增加帮助用关键字help或—help—。

举一个完整实例如下,例如添加一个I2C芯片:

config QL_VEE

tristate “QL Visual Enhancement Engine (VEE)”

default y

depends on I2C && EXPERIMENTAL

help

QL Visual Enhancement Engine (VEE) v1.0 with I2C-Compatible Interface and 24-Bit RGB Support Rev.

Makefile:内核的Makefile分为5个组成部分:

(1)Makefile     最顶层的Makefile

(2).config        内核的当前配置文档,编译时成为顶层Makefile的一部分

(3)arch/$(ARCH)/Makefile    和体系结构相关的Makefile

(4)Makefile.*      一些特定Makefile的规则

(5)kbuild级别Makefile      各级目录下的大概约500个文档,编译时根据上层Makefile传下来的宏定义和其他编译规则,将源代码编译成模块或编入内核。顶层的Makefile文档读取.config文档的内容,并总体上负责build内核和模块。Arch Makefile则提供补充体系结构相关的信息。其中.config的内容是在make menuconfig的时候,通过Kconfig文档配置的结果。

假设想把自己写的一个flash的驱动程式加载到工程中,而且能够通过menuconfig配置内核时选择该驱动该怎么办呢?如下:

第一:将您写的flashtest.c 文档添加到/driver/mtd/maps/ 目录下。

第二:修改/driver/mtd/maps目录下的kconfig文档:

config MTD_flashtest

tristate “ap71 flash”

这样当make menuconfig时 ,将会出现 ap71 flash选项。

第三:修改该目录下makefile文档。添加如下内容:obj-$(CONFIG_MTD_flashtest)       += flashtest.o

这样,当您运行make menucofnig时,您将发现ap71 flash选项,假如您选择了此项。该选择就会保存在.config文档中。当您编译内核时,将会读取.config文档,当发现ap71 flash 选项为yes,系统在调用/driver/mtd/maps/下的makefile 时,将会把 flashtest.o 加入到内核中。

 

本文来自:http://www.linuxidc.com/Linux/2009-11/23091p3.htm

[日期:2009-11-29]

 

作者:张成

 

 

VC MFC程序,在About对话框中获取并显示程序的版本号

 

=================================================
本文为HeYuanHui原作

转载必须确保本文完整并完整保留原作者信息及本文原始链接!

NN:      khler
E-mail: khler@163.com
QQ:     23381103
MSN:   pragmac@hotmail.com
=================================================

    用VC++写的MFC程序,不管是exe的或者dll,都有个’VERSION’资源,在里面可以指定程序的版本号,这样在程序文件上右键点击,查看属性,就可以看到内嵌的版本信息了。同样,所有程序都愿意在’About’对话框中显示程序的当前版本,但是这里如何显示,跟资源里的’VERSION’信息还是有好几毛钱的关系呢 :)

    如果我们从’VERSION’资源里获取版本信息并在about对话框中显示,那么我们每次发布的时候只要修改’VERSION’里的信息就行了,经过一定处理,就可以在about对话框中显示了。

    其实MSDN中也有说明,但是说实话,要想从’VERSION’中读取信息,还真不是件简单的事情,下面一一实现,代码大部分来自MSDN。

    结果如下图所示:

显示1.1.6 build 718

void CAboutDlg::OnShowWindow(BOOL bShow, UINT nStatus)
{
 CDialog::OnShowWindow(bShow, nStatus);

 // TODO: 在此处添加消息处理程序代码

 CString ver = GetAppVersion(L”TowerWatch.exe”);
 if(ver.IsEmpty()) return

;

 

 int pos = ver.ReverseFind(‘.’);
 CString mainVer = ver.Left(pos);
 CString build = ver.Right(ver.GetLength() – pos -1);
 GetDlgItem(IDC_STATIC_VER)->SetWindowText(mainVer);
 GetDlgItem(IDC_STATIC_BUILD)->

SetWindowText(build);

 

}

void ErrorExit(LPTSTR lpszFunction) 

 // Retrieve the system error message for the last-error code

 LPVOID lpMsgBuf;
 LPVOID lpDisplayBuf;
 DWORD dw =

 GetLastError(); 

 

 FormatMessage(
  FORMAT_MESSAGE_ALLOCATE_BUFFER | 
  FORMAT_MESSAGE_FROM_SYSTEM |
  FORMAT_MESSAGE_IGNORE_INSERTS,
  NULL,
  dw,
  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  (LPTSTR) &lpMsgBuf,
  0

, NULL );

 

 // Display the error message and exit the process

 lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
  (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
 StringCchPrintf((LPTSTR)lpDisplayBuf, 
  LocalSize(lpDisplayBuf),
  TEXT(“%s failed with error %d: %s”), 
  lpszFunction, dw, lpMsgBuf); 
 MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT(“Error”

), MB_OK); 

 

 LocalFree(lpMsgBuf);
 LocalFree(lpDisplayBuf);
}

CString CAboutDlg::GetAppVersion(WCHAR*

 AppName) 
{
 CString   AppVersion; 

 

 DWORD   RessourceVersionInfoSize; 
 DWORD   JustAJunkVariabel; 
 WCHAR*   VersionInfoPtr; 
 struct   LANGANDCODEPAGE
 { 
  WORD   wLanguage; 
  WORD   wCodePage; 
 }   *TranslationPtr; 
 WCHAR*     InformationPtr; 
 UINT      VersionInfoSize; 
 WCHAR     VersionValue[255

]; 

 

 RessourceVersionInfoSize=GetFileVersionInfoSize(AppName,&JustAJunkVariabel); 
 if(0!=RessourceVersionInfoSize) 
 {
  VersionInfoPtr = new WCHAR[RessourceVersionInfoSize];
  if(!GetFileVersionInfo(AppName,0,RessourceVersionInfoSize,VersionInfoPtr)) 
  {
    ErrorExit((LPTSTR)L”GetFileVersionInfo”);
   delete[]   VersionInfoPtr; 
   return

 NULL; 
  } 

 

  if(!VerQueryValue( VersionInfoPtr, L”VarFileInfoTranslation”, (LPVOID*)&TranslationPtr, &VersionInfoSize)) 
  { 
   ErrorExit((LPTSTR)L”VerQueryValue”);
   delete[]   VersionInfoPtr; 
   return

 NULL; 
  } 

 

  // retrieve product version
  wsprintf(VersionValue, L”StringFileInfo%04x%04xProductVersion”, TranslationPtr[0].wLanguage, TranslationPtr[0

].wCodePage); 

 

  if(!VerQueryValue( VersionInfoPtr, VersionValue, (LPVOID*)&InformationPtr, &VersionInfoSize)) 
  { 
   ErrorExit((LPTSTR)L”VerQueryValue”);
   delete[]   VersionInfoPtr; 
   return NULL; 
  } 
  if(wcslen(InformationPtr)> 0)   //Not   Null 
  { 
   AppVersion=

CString(InformationPtr); 
  } 

 

  delete[]   VersionInfoPtr; 
 } 
 return   AppVersion; 

当然,你可以将分解Version和build部分代码也封装到GetAppVersion函数中。

u-boot 移植步骤详解

 

1、U-Boot简介
U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目。从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。但是U-Boot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS。这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、NIOS、XScale等诸多常用系列的处理器。这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。就目前来看,U-Boot对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。其它系列的处理器和操作系统基本是在2002年11月PPCBOOT改名为U-Boot后逐步扩充的。从PPCBOOT向U-Boot的顺利过渡,很大程度上归功于U-Boot的维护人德国DENX软件工程中心Wolfgang Denk[以下简称W.D]本人精湛专业水平和持着不懈的努力。当前,U-Boot项目正在他的领军之下,众多有志于开放源码BOOT LOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多的嵌入式操作系统的装载与引导。
选择U-Boot的理由:
① 开放源码;
② 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS;
③ 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale;④ 较高的可靠性和稳定性;
⑤ 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等;
⑥ 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等;
⑦ 较为丰富的开发调试文档与强大的网络技术支持;

2、 U-Boot主要目录结构
– board 目标板相关文件,主要包含SDRAM、FLASH驱动;
– common 独立于处理器体系结构的通用代码,如内存大小探测与故障检测;
– cpu 与处理器相关的文件。如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件;
– driver 通用设备驱动,如CFI FLASH驱动(目前对INTEL FLASH支持较好)
– doc U-Boot的说明文档;
– examples可在U-Boot下运行的示例程序;如hello_world.c,timer.c;
– include U-Boot头文件;尤其configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件;
– lib_xxx 处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件;
– net 与网络功能相关的文件目录,如bootp,nfs,tftp;
– post 上电自检文件目录。尚有待于进一步完善;
– rtc RTC驱动程序;
– tools 用于创建U-Boot S-RECORD和BIN镜像文件的工具;

3、 U-Boot支持的主要功能
U-Boot可支持的主要功能列表
系统引导 支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统
支持NFS挂载、从FLASH中引导压缩或非压缩系统内核;
基本辅助功能 强大的操作系统接口功能;可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤对Linux支持最为强劲;
支持目标板环境参数多种存储方式,如FLASH、NVRAM、EEPROM;
CRC32校验,可校验FLASH中内核、RAMDISK镜像文件是否完好;
设备驱动 串口、SDRAM、FLASH、以太网、LCD、NVRAM、EEPROM、键盘、USB、PCMCIA、PCI、RTC等驱动支持;
上电自检功能 SDRAM、FLASH大小自动检测;SDRAM故障检测;CPU型号;
特殊功能 XIP内核引导;

4、移植前的准备

(1)、首先读读uboot自带的readme文件,了解了一个大概。
(2)、看看common.h,这个文件定义了一些基本的东西,并包含了一些必要的头文件。再看看flash.h,这个文件里面定义了flash_info_t为一个struct。包含了flash的一些属性定义。并且定义了所有的flash的属性,其中,AMD的有:AMD_ID_LV320B,定义为“#define AMD_ID_LV320B 0x22F922F9”。
(3)、对于“./borad/at91rm9200dk/flash.c”的修改,有以下的方面:
“void flash_identification(flash_info_t *info)”这个函数的目的是确认flash的型号。注意的是,这个函数里面有一些宏定义,直接读写了flash。并获得ID号。
(4)、修改:”./board/at91rm9200dk/config.mk”为
TEXT_BASE=0x21f80000 为TEXT_BASE=0x21f00000 (当然,你应该根据自己的板子来修改,和一级boot的定义的一致即可)。
(5)、再修改”./include/configs/at91rm9200dk.h”为
修改flash和SDRAM的大小。
(6)、另外一个要修改的文件是:
./borad/at91rm9200dk/flash.c。这个文件修改的部分比较的多。
a. 首先是OrgDef的定义,加上目前的flash。
b. 接下来,修改”#define FLASH_BANK_SIZE 0x200000”为自己flash的 容量
c. 在修改函数flash_identification(flash_info_t * info)里面的打印信息,这部分将在u-boot启动的时候显示。
d. 然后修改函数flash_init(void)里面对一些变量的赋值。
e. 最后修改的是函数flash_print_info(flash_info_t * info)里面实际打印的函数信息。
f. 还有一个函数需要修改,就是:“flash_erase”,这个函数要检测先前知道的flash类型是否匹配,否则,直接就返回了。把这里给注释掉。

(7)、接下来看看SDRAM的修改。
这个里面对于“SIZE”的定义都是基于字节计算的。
只要修改”./include/configs/at91rm9200dk.h”里面的
“#define PHYS_SDRAM_SIZE 0X200000”就可以了。注意,SIZE是以字节为单位的。
(8)、还有一个地方要注意
就是按照目前的设定,一级boot把u_boot加载到了SDRAM的空间为:21F00000 -> 21F16B10,这恰好是SDRAM的高端部分。另外,BSS为21F1AE34。

(9)、编译后,可以写入flash了。
a. 压缩这个u-boot.bin
“gzip –c u-boot.bin > u-boot.gz”
压缩后的文件大小为:
43Kbytes
b. 接着把boot.bin和u-boot.gz烧到flash里面去。
Boot.bin大约11kBytes,在flash的0x1000 0000 ~ 0x1000 3fff

5、U-Boot移植过程
① 获得发布的最新版本U-Boot源码,与Linux内核源码类似,也是 bzip2的压缩格式。可从U-Boot的官方网站http://sourceforge.net/projects/U-Boot上获得;
② 阅读相关文档,主要是U-Boot源码根目录下的README文档和U-Boot官方网站的DULG(The DENX U-Boot and Linux Guide)文档http://www.denx.de/twiki/bin/view/DULG/Manual。尤其是DULG文档,从如何安装建立交叉开发环境和解决U-Boot移植中常见问题都一一给出详尽的说明;
③ 订阅U-Boot用户邮件列表http://lists.sourceforge.net/lists/listinfo/u-boot-users。在移植U-Boot过程中遇有问题,在参考相关文档和搜索U-Boot-User邮件档案库http://sourceforge.net/mailarchive/forum.php?forum_id=12898仍不能解决的情况下,第一时间提交所遇到的这些问题,众多热心的U-Boot开发人员会乐于迅速排查问题,而且很有可能,W.D本人会直接参与指导;
④ 在建立的开发环境下进行移植工作。绝大多数的开发环境是交叉开发环境。在这方面,DENX 和MontaVista均提供了完整的开发工具集;
⑤ 在目标板与开发主机间接入硬件调试器。这是进行U-Boot移植应当具备且非常关键的调试工具。因为在整个U-Boot的移植工作中,尤其是初始阶段,硬件调试器是我们了解目标板真实运行状态的唯一途径。在这方面,W.D本人和众多嵌入式开发人员倾向于使用BDI2000。一方面,其价格不如ICE调试器昂贵,同时其可靠性高,功能强大,完全能胜任移植和调试U-Boot。另外,网上也有不少关于BDI2000调试方面的参考文档。
⑥ 如果在参考开发板上移植U-Boot,可能需要移除目标板上已有的BOOT LOADER。可以根据板上BOOT LOADER的说明文档,先着手解决在移除当前BOOT LOADER的情况下,如何进行恢复。以便今后在需要场合能重新装入原先的BOOT LOADER。

6. U-Boot移植方法
当前,对于U-Boot的移植方法,大致分为两种。一种是先用BDI2000创建目标板初始运行环境,将U-Boot镜像文件u-boot.bin下载到目标板RAM中的指定位置,然后,用BDI2000进行跟踪调试。其好处是不用将U-Boot镜像文件烧写到FLASH中去。但弊端在于对移植开发人员的移植调试技能要求较高,BDI2000的配置文件较为复杂。另外一种方法是用BDI2000先将U-Boot镜像文件烧写到FLASH中去,然后利用GDB和BDI2000进行调试。这种方法所用BDI2000的配置文件较为简单,调试过程与U-Boot移植后运行过程相吻合,即U-Boot先从FLASH中运行,再重载至RAM中相应位置,并从那里正式投入运行。唯一感到有些麻烦的就是需要不断烧写FLASH。但考虑到FLASH常规擦写次数基本为10万次左右,作为移植U-Boot,不会占用太多的次数,应该不会为FLASH烧写有什么担忧。同时,W. D本人也极力推荐使用后一种方法。笔者建议,除非U-Boot移植资深人士或有强有力的技术支持,建议采用第二种移植方法。

7. U-Boot移植主要修改的文件
从移植U-Boot最小要求-U-Boot能正常启动的角度出发,主要考虑修改如下文件:
① <目标板>.h头文件,如include/configs/RPXlite.h。可以是U-Boot源码中已有的目标板头文件,也可以是新命名的配置头文件;大多数的寄存器参数都是在这一文件中设置完成的;
② <目标板>.c文件,如board/RPXlite/RPXlite.c。它是SDRAM的驱动程序,主要完成SDRAM的UPM表设置,上电初始化。
③ FLASH的驱动程序,如board/RPXlite/flash.c,或common/cfi_flash.c。可在参考已有FLASH驱动的基础上,结合目标板FLASH数据手册,进行适当修改;
④ 串口驱动,如修改cpu/mpc8xx/serial.c串口收发器芯片使能部分。

8. U-Boot移植要点
① BDI2000的配置文件。如果采用第二种移植方法,即先烧入FLASH的方法,配置项只需很少几个,就可以进行U-Boot的烧写与调试了。对PPC 8xx系列的主板,可参考DULG文档中TQM8xx的配置文件进行相应的修改。下面,笔者以美国Embedded Planet公司的RPXlite DW板为例,给出在嵌入式Linux交叉开发环境下的BDI2000参考配置文件以作参考:

[code lang=”shell”]
; bdiGDB configuration file for RPXlite DW or LITE_DW
; ——————————————–
[INIT]
; init core register
WSPR 149 0x2002000F ;DER : set debug enable register
; WSPR 149 0x2006000F ;DER : enable SYSIE for BDI flash program
WSPR 638 0xFA200000 ;IMMR : internal memory at 0xFA200000
WM32 0xFA200004 0xFFFFFF89 ;SYPCR
[TARGET]
CPUCLOCK 40000000 ;the CPU clock rate after processing the init list
BDIMODE AGENT ;the BDI working mode (LOADONLY | AGENT)
BREAKMODE HARD ;SOFT or HARD, HARD uses PPC hardware breakpoints
[HOST]
IP 173.60.120.5
FILE uImage.litedw
FORMAT BIN
LOAD MANUAL ;load code MANUAL or AUTO after reset
DEBUGPORT 2001
START 0x0100
[FLASH]
CHIPTYPE AM29BX8 ;;Flash type (AM29F | AM29BX8 | AM29BX16 | I28BX8 | I28BX16)
CHIPSIZE 0x400000 ;;The size of one flash chip in bytes
BUSWIDTH 32 ;The width of the flash memory bus in bits (8 | 16 | 32)
WORKSPACE 0xFA202000 ; RAM buffer for fast flash programming
FILE u-boot.bin ;The file to program
FORMAT BIN 0x00000000
ERASE 0x00000000 BLOCK
ERASE 0x00008000 BLOCK
ERASE 0x00010000 BLOCK
ERASE 0x00018000 BLOCK
[REGS]
DMM1 0xFA200000
FILE reg823.def

&nbsp;

[/code]

② U-Boot移植参考板。这是进行U-Boot移植首先要明确的。可以根据目标板上CPU、FLASH、SDRAM的情况,以尽可能相一致为原则,先找出一个与所移植目标板为同一个或同一系列处理器的U-Boot支持板为移植参考板。如RPXlite DW板可选择U-Boot源码中RPXlite板作为U-Boot移植参考板。对U-Boot移植新手,建议依照循序渐进的原则,目标板文件名暂时先用移植参考板的名称,在逐步熟悉U-Boot移植基础上,再考虑给目标板重新命名。在实际移植过程中,可用Linux命令查找移植参考板的特定代码,如grep –r RPXlite ./ 可确定出在U-Boot中与RPXlite板有关的代码,依此对照目标板实际进行屏蔽或修改。同时应不局限于移植参考板中的代码,要广泛借鉴U-Boot中已有的代码更好地实现一些具体的功能。
③ U-Boot烧写地址。不同目标板,对U-Boot在FLASH中存放地址要求不尽相同。事实上,这是由处理器中断复位向量来决定的,与主板硬件相关,对MPC8xx主板来讲,就是由硬件配置字(HRCW)决定的。也就是说,U-Boot烧写具体位置是由硬件决定的,而不是程序设计来选择的。程序中相应U-Boot起始地址必须与硬件所确定的硬件复位向量相吻合;如RPXlite DW板的中断复位向量设置为0x00000100。因此, U-Boot 的BIN镜像文件必须烧写到FLASH的起始位置。事实上,大多数的PPC系列的处理器中断复位向量是0x00000100和0xfff00100。这也是一般所说的高位启动和低位启动的BOOT LOADER所在位置。可通过修改U-Boot源码<目标板>.h头文件中CFG_MONITOR_BASE 和board/<目标板>/config.mk中的TEXT_BASE的设置来与硬件配置相对应。
④ CPU寄存器参数设置。根据处理器系列、类型不同,寄存器名称与作用有一定差别。必须根据目标板的实际,进行合理配置。一个较为可行和有效的方法,就是借鉴参考移植板的配置,再根据目标板实际,进行合理修改。这是一个较费功夫和考验耐力的过程,需要仔细对照处理器各寄存器定义、参考设置、目标板实际作出选择并不断测试。MPC8xx处理器较为关键的寄存器设置为SIUMCR、PLPRCR、SCCR、BRx、ORx。
⑤ 串口调试。能从串口输出信息,即使是乱码,也可以说U-Boot移植取得了实质性突破。依据笔者调试经历,串口是否有输出,除了与串口驱动相关外,还与FLASH相关的寄存器设置有关。因为U-Boot是从FLASH中被引导启动的,如果FLASH设置不正确,U-Boot代码读取和执行就会出现一些问题。因此,还需要就FLASH的相关寄存器设置进行一些参数调试。同时,要注意串口收发芯片相关引脚工作波形。依据笔者调试情况,如果串口无输出或出现乱码,一种可能就是该芯片损坏或工作不正常。
⑥ 与启动 FLASH相关的寄存器BR0、OR0的参数设置。应根据目标板FLASH的数据手册与BR0和OR0的相关位含义进行合理设置。这不仅关系到FLASH能否正常工作,而且与串口调试有直接的关联。
⑦ 关于CPLD电路。目标板上是否有CPLD电路丝毫不会影响U-Boot的移植与嵌入式操作系统的正常运行。事实上,CPLD电路是一个集中将板上电路的一些逻辑关系可编程设置的一种实现方法。其本身所起的作用就是实现一些目标板所需的脉冲信号和电路逻辑,其功能完全可以用一些逻辑电路与CPU口线来实现。
⑧ SDRAM的驱动。串口能输出以后,U-Boot移植是否顺利基本取决于SDRAM的驱动是否正确。与串口调试相比,这部分工作更为核心,难度更大。MPC8xx目标板SDRAM驱动涉及三部分。一是相关寄存器的设置;二是UPM表;三是SDRAM上电初始化过程。任何一部分有问题,都会影响U-Boot、嵌入式操作系统甚至应用程序的稳定、可靠运行。所以说,SDRAM的驱动不仅关系到U-Boot本身能否正常运行,而且还与后续部分相关,是相当关键的部分。
⑨ 补充功能的添加。在获得一个能工作的U-Boot后,就可以根据目标板和实际开发需要,添加一些其它功能支持。如以太网、LCD、NVRAM等。与串口和SDRAM调试相比,在已有基础之上,这些功能添加还是较为容易的。大多只是在参考现有源码的基础上,进行一些修改和配置。
另外,如果在自主设计的主板上移植U-Boot,那么除了考虑上述软件因素以外,还需要排查目标板硬件可能存在的问题。如原理设计、PCB布线、元件好坏。在移植过程中,敏锐判断出故障态是硬件还是软件问题,往往是关系到项目进度甚至移植成败的关键,相应难度会增加许多。

下面以移植u-boot 到44B0开发板的步骤为例,移植中上仅需要修改和硬件相关的部分。在代码结构上:
1) 在board 目录下创建ev44b0ii 目录,创建ev44b0ii.c 以及flash.c,memsetup.S,u-boot.lds等。不需要从零开始,可选择一个相似的目录,直接复制过来,修改文件名以及内容。我在移植u-boot 过程中,选择的是ep7312 目录。由于u-boot 已经包含基于s3c24b0 的开发板目录,作为参考,也可以复制相应的目录。
2) 在cpu 目录下创建arm7tdmi 目录,主要包含start.S,interrupts.c 以及cpu.c,serial.c几个文件。同样不需要从零开始建立文件,直接从arm720t 复制,然后修改相应内容。
3) 在include/configs 目录下添加ev44b0ii.h,在这里放上全局的宏定义等。
4) 找到u-boot 根目录下Makefile 修改加入
ev44b0ii_config : unconfig
@./mkconfig $(@:_config=) arm arm7tdmi ev44b0ii
5) 运行make ev44bii_config,如果没有错误就可以开始硬件相关代码移植的工作

3. u-boot 的体系结构
1) 总体结构
u-boot 是一个层次式结构。从上图也可以看出,做移植工作的软件人员应当提供串口驱动(UART Driver),以太网驱动(Ethernet Driver),Flash 驱动(Flash 驱动),USB 驱动(USB Driver)。目前,通过USB 口下载程序显得不是十分必要,所以暂时没有移植USB 驱动。驱动层之上是u-boot 的应用,command 通过串口提供人机界面。我们可以使用一些命令做一些常用的工作,比如内存查看命令md。
Kermit 应用主要用来支持使用串口通过超级终端下载应用程序。TFTP 则是通过网络方式来下载应用程序,例如uclinux 操作系统。
2) 内存分布
在flash rom 中内存分布图ev44b0ii 的flash 大小2M(8bits),现在将0-40000 共256k 作为u-boot 的存储空间。由于u-boot 中有一些环境变量,例如ip 地址,引导文件名等,可在命令行通过setenv 配置好,通过saveenv 保存在40000-50000(共64k)这段空间里。如果存在保存好的环境变量,u-boot 引导将直接使用这些环境变量。正如从代码分析中可以看到,我们会把flash 引导代码搬移到DRAM 中运行。下图给出u-boot 的代码在DRAM中的位置。引导代码u-boot 将从0x0000 0000 处搬移到0x0C700000 处。特别注意的由于ev44b0ii uclinux 中断向量程序地址在0x0c00 0000 处,所以不能将程序下载到0x0c00 0000 出,通常下载到0x0c08 0000 处。

4. start.S 代码结构
1) 定义入口
一个可执行的Image 必须有一个入口点并且只能有一个唯一的全局入口,通常这个入口放在Rom(flash)的0x0 地址。例如start.S 中的
.globl _start
_start:
值得注意的是你必须告诉编译器知道这个入口,这个工作主要是修改连接器脚本文件(lds)。
2) 设置异常向量(Exception Vector)
异常向量表,也可称为中断向量表,必须是从0 地址开始,连续的存放。如下面的就包括了复位(reset),未定义处理(undef),软件中断(SWI),预去指令错误(Pabort),数据错误(Dabort),保留,以及IRQ,FIQ 等。注意这里的值必须与uclinux 的vector_base 一致。这就是说如果uclinux 中vector_base(include/armnommu/proc-armv/system.h)定义为0x0c00 0000,则HandleUndef 应该在
0x0c00 0004。
b reset //for debug
ldr pc,=HandleUndef
ldr pc,=HandleSWI
ldr pc,=HandlePabort
ldr pc,=HandleDabort
b .
ldr pc,=HandleIRQ
ldr pc,=HandleFIQ
ldr pc,=HandleEINT0 /*mGA H/W interrupt vector table*/
ldr pc,=HandleEINT1
ldr pc,=HandleEINT2
ldr pc,=HandleEINT3
ldr pc,=HandleEINT4567
ldr pc,=HandleTICK /*mGA*/
b .
b .
ldr pc,=HandleZDMA0 /*mGB*/
ldr pc,=HandleZDMA1
ldr pc,=HandleBDMA0
ldr pc,=HandleBDMA1
ldr pc,=HandleWDT
ldr pc,=HandleUERR01 /*mGB*/
b .
b .
ldr pc,=HandleTIMER0 /*mGC*/
ldr pc,=HandleTIMER1
ldr pc,=HandleTIMER2
ldr pc,=HandleTIMER3
ldr pc,=HandleTIMER4
ldr pc,=HandleTIMER5 /*mGC*/
b .
b .
ldr pc,=HandleURXD0 /*mGD*/
ldr pc,=HandleURXD1
ldr pc,=HandleIIC
ldr pc,=HandleSIO
ldr pc,=HandleUTXD0
ldr pc,=HandleUTXD1 /*mGD*/
b .
b .
ldr pc,=HandleRTC /*mGKA*/
b .
b .
b .
b .
b . /*mGKA*/
b .
b .
ldr pc,=HandleADC /*mGKB*/
b .
b .
b .
b .
b . /*mGKB*/
b .
b .
ldr pc,=EnterPWDN
作为对照:请看以上标记的值:

.equ HandleReset, 0xc000000
.equ HandleUndef,0xc000004
.equ HandleSWI, 0xc000008
.equ HandlePabort, 0xc00000c
.equ HandleDabort, 0xc000010
.equ HandleReserved, 0xc000014
.equ HandleIRQ, 0xc000018
.equ HandleFIQ, 0xc00001c
/*the value is different with an address you think it may be.
*IntVectorTable */
.equ HandleADC, 0xc000020
.equ HandleRTC, 0xc000024
.equ HandleUTXD1, 0xc000028
.equ HandleUTXD0, 0xc00002c
.equ HandleSIO, 0xc000030
.equ HandleIIC, 0xc000034
.equ HandleURXD1, 0xc000038
.equ HandleURXD0, 0xc00003c
.equ HandleTIMER5, 0xc000040
.equ HandleTIMER4, 0xc000044
.equ HandleTIMER3, 0xc000048
.equ HandleTIMER2, 0xc00004c
.equ HandleTIMER1, 0xc000050
.equ HandleTIMER0, 0xc000054
.equ HandleUERR01, 0xc000058
.equ HandleWDT, 0xc00005c
.equ HandleBDMA1, 0xc000060
.equ HandleBDMA0, 0xc000064
.equ HandleZDMA1, 0xc000068
.equ HandleZDMA0, 0xc00006c
.equ HandleTICK, 0xc000070
.equ HandleEINT4567, 0xc000074
.equ HandleEINT3, 0xc000078
.equ HandleEINT2, 0xc00007c
.equ HandleEINT1, 0xc000080
.equ HandleEINT0, 0xc000084
3) 初始化CPU 相关的pll,clock,中断控制寄存器
依次为关闭watch dog timer,关闭中断,设置LockTime,PLL(phase lock loop),以及时钟。
这些值(除了LOCKTIME)都可从Samsung 44b0 的手册中查到。
ldr r0,WTCON //watch dog disable
ldr r1,=0x0
str r1,[r0]
ldr r0,INTMSK
ldr r1,MASKALL //all interrupt disable
str r1,[r0]
/*****************************************************
* Set clock control registers *
*****************************************************/
ldr r0,LOCKTIME
ldr r1,=800 // count = t_lock * Fin (t_lock=200us, Fin=4MHz) = 800
str r1,[r0]
ldr r0,PLLCON /*temporary setting of PLL*/
ldr r1,PLLCON_DAT /*Fin=10MHz,Fout=40MHz or 60MHz*/
str r1,[r0]
ldr r0,CLKCON
ldr r1,=0x7ff8 //All unit block CLK enable
str r1,[r0]
4) 初始化内存控制器
内存控制器,主要通过设置13 个从1c80000 开始的寄存器来设置,包括总线宽度,
8 个内存bank,bank 大小,sclk,以及两个bank mode。
/*****************************************************
* Set memory control registers *
*****************************************************/
memsetup:
adr r0,SMRDATA
ldmia r0,{r1-r13}
ldr r0,=0x01c80000 //BWSCON Address
stmia r0,{r1-r13}
5) 将rom 中的程序复制到RAM 中
首先利用PC 取得bootloader 在flash 的起始地址,再通过标号之差计算出这个程序代
码的大小。这些标号,编译器会在连接(link)的时候生成正确的分布的值。取得正
确信息后,通过寄存器(r3 到r10)做为复制的中间媒介,将代码复制到RAM 中。
relocate:
/*
* relocate armboot to RAM
*/
adr r0, _start /* r0 <- current position of code */
ldr r2, _armboot_start
ldr r3, _armboot_end
sub r2, r3, r2 /* r2 <- size of armboot */
ldr r1, _TEXT_BASE /* r1 <- destination address */
add r2, r0, r2 /* r2 <- source end address */
/*
* r0 = source address
* r1 = target address
* r2 = source end address
*/
copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble copy_loop
6) 初始化堆栈
进入各种模式设置相应模式的堆栈。
InitStacks:
/*Don’t use DRAM,such as stmfd,ldmfd……
SVCstack is initialized before*/
mrs r0,cpsr
bic r0,r0,#0X1F
orr r1,r0,#0xDB /*UNDEFMODE|NOINT*/
msr cpsr,r1 /*UndefMode*/
ldr sp,UndefStack
orr r1,r0,#0XD7 /*ABORTMODE|NOINT*/
msr cpsr,r1 /*AbortMode*/
ldr sp,AbortStack
orr r1,r0,#0XD2 /*IRQMODE|NOINT*/
msr cpsr,r1 /*IRQMode*/
ldr sp,IRQStack
orr r1,r0,#0XD1 /*FIQMODE|NOINT*/
msr cpsr,r1 /*FIQMode*/
ldr sp,FIQStack
bic r0,r0,#0XDF /*MODEMASK|NOINT*/
orr r1,r0,#0X13
msr cpsr,r1 /*SVCMode*/
ldr sp,SVCStack
7) 转到RAM 中执行
使用指令ldr,pc,RAM 中C 函数地址就可以转到RAM 中去执行。
5. 系统初始化部分
1. 串口部分
串口的设置主要包括初始化串口部分,值得注意的串口的Baudrate 与时钟MCLK 有很大关系,是通过:rUBRDIV0=( (int)(MCLK/16./(gd ->baudrate) + 0.5) -1 )计算得出。这可以在手册中查到。其他的函数包括发送,接收。这个时候没有中断,是通过循环等待来判断是否动作完成。
例如,接收函数:
while(!(rUTRSTAT0 & 0x1)); //Receive data read
return RdURXH0();
2. 时钟部分
实现了延时函数udelay。
这里的get_timer 由于没有使用中断,是使用全局变量来累加的。
3. flash 部分
flash 作为内存的一部分,读肯定没有问题,关键是flash 的写部分。
Flash 的写必须先擦除,然后再写。
unsigned long flash_init (void)
{
int i;
u16 manId,devId;
//first we init it as unknown,even if you forget assign it below,it’s not a problem
for (i=0; i < CFG_MAX_FLASH_BANKS; ++i){
flash_info[i].flash_id = FLASH_UNKNOWN;
flash_info[i].sector_count=CFG_MAX_FLASH_SECT;
}
/*check manId,devId*/
_RESET();
_WR(0x555,0xaa);
_WR(0x2aa,0x55);
_WR(0x555,0x90);
manId=_RD(0x0);
_WR(0x555,0xaa);
_WR(0x2aa,0x55);
_WR(0x555,0x90);
devId=_RD(0x1);
_RESET();
printf(“flashn”);
printf(“Manufacture ID=%4x(0x0004), Device ID(0x22c4)=%4xn”,manId,devId);
if(manId!=0x0004 && devId!=0x22c4){
printf(“flash check faliluren”);
return 0;
}else{
for (i=0; i < CFG_MAX_FLASH_BANKS; ++i){
flash_info[i].flash_id=FLASH_AM160T;/*In fact it is fujitu,I only don’t want to
modify common files*/
}
}
/* Setup offsets */
flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]);
/* zhangyy comment
#if CFG_MONITOR_BASE >= CFG_FLASH_BASE
//onitor protection ON by default
flash_protect(FLAG_PROTECT_SET,
CFG_MONITOR_BASE,
CFG_MONITOR_BASE+monitor_flash_len-1,
&flash_info[0]);
#endif
*/
flash_info[0].size =PHYS_FLASH_SIZE;
return (PHYS_FLASH_SIZE);
}
flash_init 完成初始化部分,这里的主要目的是检验flash 的型号是否正确。
int flash_erase (flash_info_t *info, int s_first, int s_last)
{
volatile unsigned char *addr = (volatile unsigned char *)(info->start[0]);
int flag, prot, sect, l_sect;
//ulong start, now, last;
u32 targetAddr;
u32 targetSize;
/*zyy note:It is required and can’t be omitted*/
rNCACHBE0=( (0x2000000>>12)<<16 )|(0>>12); //flash area(Bank0) must be non-cachable
area.
rSYSCFG=rSYSCFG & (~0x8); //write buffer has to be off for proper timing.
if ((s_first < 0) || (s_first > s_last)) {
if (info->flash_id == FLASH_UNKNOWN) {
printf (“- missingn”);
} else {
printf (“- no sectors to erasen”);
}
return 1;
}
if ((info->flash_id == FLASH_UNKNOWN) ||
(info->flash_id > FLASH_AMD_COMP)) {
printf (“Can’t erase unknown flash type – abortedn”);
return 1;
}
prot = 0;
for (sect=s_first; sect<=s_last; ++sect) {
if (info->protect[sect]) {
prot++;
}
}
if (prot) {
printf (“- Warning: %d protected sectors will not be erased!n”,
prot);
} else {
printf (“n”);
}
l_sect = -1;
/* Disable interrupts which might cause a timeout here */
flag = disable_interrupts();
/* Start erase on unprotected sectors */
for (sect = s_first; sect<=s_last; sect++) {
if (info->protect[sect] == 0) {/* not protected */
targetAddr=0x10000*sect;
if(targetAddr<0x1F0000)
targetSize=0x10000;
else if(targetAddr<0x1F8000)
targetSize=0x8000;
else if(targetAddr<0x1FC000)
targetSize=0x2000;
else
targetSize=0x4000;
F29LV160_EraseSector(targetAddr);
l_sect = sect;
if(!BlankCheck(targetAddr, targetSize))
printf(“BlankCheck Errorn”);
}
}
/* re-enable interrupts if necessary */
if (flag)
enable_interrupts();
/* wait at least 80us – let’s wait 1 ms */
udelay (1000);
/*
*We wait for the last triggered sector
*/
if (l_sect < 0)
goto DONE;
DONE:
printf (” donen”);
return 0;
}
int BlankCheck(int targetAddr,int targetSize)
{
int i,j;
for(i=0;i{
j=*((u16 *)(i+targetAddr));
if( j!=0xffff)
{
printf(“E:%x=%xn”,(i+targetAddr),j);
return 0;
}
}
return 1;
}
flash_erase 擦除flash,BlankCheck 则检查该部分内容是否擦除成功。
/*———————————————————————–
*Write a word to Flash, returns:
* 0 – OK
* 1 – write timeout
* 2 – Flash not erased
*/
static int write_word (flash_info_t *info, ulong dest, ulong data)
{
volatile u16 *tempPt;
/*zhangyy note:because of compatiblity of function,I use low & hi*/
u16 low = data & 0xffff;
u16 high = (data >> 16) & 0xffff;
low=swap_16(low);
high=swap_16(high);
tempPt=(volatile u16 *)dest;
_WR(0x555,0xaa);
_WR(0x2aa,0x55);
_WR(0x555,0xa0);
*tempPt=high;
_WAIT();
_WR(0x555,0xaa);
_WR(0x2aa,0x55);
_WR(0x555,0xa0);
*(tempPt+1)=low;
_WAIT();
return 0;
}
wirte_word 则向flash 里面写入unsigned long 类型的data,因为flash 一次只能写入16bits,所以这里分两次写入。

 

 

 

本文引用地址:http://patton.spaces.eepw.com.cn/articles/article/item/23450

POSIX线程基本概念

线程编程在smp体系结构处理并发时会被提及的比较多。它可以实现并发多道操作,常被称为轻量级的进程,因为它可以共享进程资源,省去了多进程切换时内核的上下文切换所用的花销。
多数Linux编程教材里面对POSIX线程的介绍占的篇幅并不多。Linux的fork对多进程的有较好的优化技术,而__clone系统调用相当于使 用进程的方法实现线程。进程的另一个好处是稳定性,进程在退出时操作系统自动回收资源,而共享资源的线程需要程序员自己设计释放资源,积累的错误可能会使 得程序崩溃。
线程和进程如何使用的关系,在不少unix尤其是linux编程的相关社区依然是经久不息的话题。这篇文章大致说了一下linux编程为什么多用进程而少用线程。而Gentoo创始人Daniel Robbins的这一篇相当好的线程入门文章。还有这篇
我只是初学者,下面是我的学习笔记。

1、__clone函数调用

#include <sched.h>
int __clone(int (*fn)(void *fnarg), void *child_stack, int flags, void *arg);

Linux可以通过__clone函数调用创建子进程来实现线程,__clone同时也可以实现fork。__clone的第一个参数是执行子进程时的调 用函数指针,这个函数用一个void类型的指针作为参数,返回int整型;第二个参数为为子进程分配的堆栈指针,如果想让操作系统为它来分配,则设置为 NULL,第三个为CLONE标志,作为调用选项,flags包括:CLONE_VM(是否设置则共享内存镜像,否则就类似于fork,子进程拷贝一份父 进程的副本)、CLONE_FS(是否共享相同的根文件系统,当前动作目录和umask)、CLONE_FILES(是否共享文件描述符,否则子进程依然 会继承父进程的文件描述符表,但它们的读/写/打开/关闭操作互不影响)、CLONE_SIGHAND(是否共享设置在父进程上的信号处理器,否则子进程 自己拷贝一份副本,它们的sigaction互不影响)、CONE_PID(子进程是否继续使用父进程的pid)。最后一个参数arg可以设置为传递 给*fn子函数的参数,注意类型是void,这意味着它是什么类型可以自己选择,包括NULL。

2、POSIX线程库的pthread API

pthread_create:创建一个线程

#include <pthread.h>
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

第一个参数为要创建的线程结构pthread_t,第二个参数为线程的相关属性,第三个参数是该线程要执行的函数的指针,第四个则是传递给*start_routine的参数,可以自行选择它的取值和用法。
pthread_create执行成功返回0,并把tid线程标识符存放到pthread_t结构中;否则返回一个非零值并设置errno。

pthread_exit:终止当前线程

#include <pthread.h>
void pthread_exit(void *retval);

pthread_exit退出当前线程,退出之前将调用pthread_cleanup_push。它在线程的最上层函数的最后是被隐式调用的,这时可以 加一个retval参数显式调用之,以供pthread_join参考。

pthread_join:挂起当前线程

#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);

pthread_join把当前线程挂起直到指定的线程th终止,thread_return为th终止时的参数,如果没有显式指定,则为NULL。

pthread_cancel:撤消一个线程

#include <pthread.h>
int pthread_cancel(pthread_t thread);
int pthread_setcancelstate(int state, int *oldstate);
int pthread_setcanceltype(int type, int *oldtype);
void pthread_testcancel(void);

pthread_cancel用于撤消线程thread。
pthread_setcancelstate可以用于设置当前线程的撤消状态,包括PTHREAD_CANCEL_ENABLE和 PTHREAD_CANCEL_DISABLE,允许或者忽略撤消。在一些关键操作中可以设置它以避免被另外一个线程用pthread_cancel来撤 消。
pthread_setcanceltype设置当前线程的撤消类型,包括PTHREAD_CANCEL_ASYNCHRONOUS立即撤消、 PTHREAD_CANCEL_DEFERRED延迟至撤消点。可以通过调用pthread_testcancel或者使用条件变量来设置撤消点。

3、线程属性

线程属性包括:
detachstate ——分离或者切入状态,它的值有PTHREAD_CREATE_JOINABLE(默认值), PTHREAD_CREATE_DETACHED;
schedpolicy ——调度策略,取值有SCHED_OTHER(默认值), SCHED_FIFO, SCHED_FIFO;
schedparam ——跟调度策略有关;
inheritsched ——PTHREAD_EXPLICIT_SCHED(默认值), PTHREAD_INHERIT_SCHED;
scope —— 时间片,取值有PTHREAD_SCOPE_SYSTEM(默认值),每个线程一个系统时间片,PTHREAD_SCOPE_PROCESS线程共享系统时间片。
线程属性对象的类型为pthread_attr_t,通过pthread_attr_xxxxx函数族操作线程属性对象。

4、pthread cleanup宏

pthrad cleanup宏主要用于处理线程的退出状态,pthread_exit和pthread_join等可以用它来作参数,包括

#include <pthread.h>
void pthread_cleanup_push(void (*routine), (void *), void *arg);
void pthread_cleanup_pop(int execute);
void pthread_cleanup_push_defer_np(void (*routine), (void *), void *arg);
void pthread_cleanup_pop_restore_np(int execute);

这些宏主要用于线程结束时释放有关资源,用pthread_exit调用pthread_cleanup_push进行处理时,要注意用完后要用对应的pthread_cleanup_pop把它从堆栈弹出。

5、互斥mutex

由于线程是共享资源的,所以互斥就显得相当重要。互斥提供了对互斥对象上锁的方法以获得对此资源的独占,其它企图对此互斥加锁的线程则会被阻塞而挂起,直到对资源加锁的线程解锁为止。
在使用线程编程时,不必处处都使用互斥,否则会失去并发线程的意义,例如,在对使用只能串行单独访问的资源下方使用互斥。
互斥对象在pthead.h中定义为pthread_mutex_t。它的系统调用主要包括:

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

pthread_mutex_init创建一个互斥对象,并用指定的属性初始化这个互斥对象,初始化属性包 括:PTHREAD_MUTEX_INITIALIZER创建快速互斥,这种互斥执行简单的加锁和解锁,在加锁后阻塞另一个要给它上锁的线 程;PTHREAD_RECURSIVE_MUTEX_INITIALIZER创建递归互斥,这种互斥将给加锁计数,解锁时需要调用同样次数的 pthread_mutex_unlock;
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER创建检错互斥,这种互斥在被锁上后会向试图给它加锁的线程返回一个EDEADLK错误代码而不阻塞。

pthread_mutex_lock和pthread_mutex_unlock则为加锁和解锁指定的互斥对 象,pthread_mutex_trylock和pthread_mutex_lock的不同是如果互斥对象已上锁不会被阻塞而是返回一个EBUSY错 误代码,pthread_mutex_destory析构指针mutex并释放相关资源。这几个调用成功都返回0,失败返回一个非零的错误代码。

6、条件变量

线程用条件变量对象来阻塞自己以等待某个指定条件发生,条件对象在<pthread.h>中定义为pthread_cond_t。

#include <pthead.h>
pthread_cond_t cond = pthread_cond_initializer;
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
int pthread_cond_destroy(pthread_cond_t *cond);

pthread_cond_init创建一个指向条件变量的指针,并用一个参数初始化该变量,这个参数在Linux下被忽略而使用 PTHREAD_COND_INITIALIZER,而pthread_cond_t则析构该指针并释放相关系统资源。
线程在锁定一个互斥后,如果需要某个条件到来,则应调用等待所需条件,pthread_cond_wait将检查指定的条件,没有则把当前线程挂起,并解 锁指定的互斥,在条件到达后重新锁定该互斥,并唤醒被挂起的线程。pthread_cond_timedwait则为计时等待,使用的abstime这个 参数为和兼容time()返回值的绝对时间,即以经典的UNIX纪元时间1970-01-01起至今的秒数。如果这个时间前等待条件未发生,则结束等待并 返回ETIMEOUT代码。这两个函数都将被设置为撤消点。

pthread_cond_signal和pthread_cond_broadcast用在一个线程解锁互斥后唤醒等待条件cond的线程,不同之处在 于前者按顺序唤醒第一个进入等待队列的线程,后者唤醒等待该条件的所有线程。

 

linux下多线程的创建与等待详解

 

linux下多线程的创建与等待详解

 

所有线程都有一个线程号,也就是Thread ID。其类型为pthread_t。通过调用pthread_self()函数可以获得自身的线程号。
下面说一下如何创建一个线程。
通过创建线程,线程将会执行一个线程函数,该线程格式必须按照下面来声明:
void * Thread_Function(void *)
创建线程的函数如下:

int pthread_create(pthread_t *restrict thread,
      const pthread_attr_t *restrict attr,
      void *(*start_routine)(void*), void *restrict arg);

下面说明一下各个参数的含义:
thread:所创建的线程号。
attr:所创建的线程属性,这个将在后面详细说明。
start_routine:即将运行的线程函数。
art:传递给线程函数的参数。
下面是一个简单的创建线程例子:

#include <pthread.h>
#include <stdio.h>
/* Prints x’s to stderr. The parameter is unused. Does not return. */
void* print_xs (void* unused)
{
    while (1)
    fputc (‘x’, stderr);
    return NULL;
}
/* The main program. */
int main ()
{
    pthread_t thread_id;
    
/* Create a new thread. The new thread will run the print_xs
    function. */

    pthread_create (&thread_id, NULL, &print_xs, NULL);
    /* Print o’s continuously to stderr. */
    while (1)
    fputc (‘o’, stderr);
    return 0;
}

在编译的时候需要注意,由于线程创建函数在libpthread.so库中,所以在编译命令中需要将该库导入。命令如下:
gcc –o createthread –lpthread createthread.c
如果想传递参数给线程函数,可以通过其参数arg,其类型是void *。如果你需要传递多个参数的话,可以考虑将这些参数组成一个结构体来传递。另外,由于类型是void *,所以你的参数不可以被提前释放掉。
下面一个问题和前面创建线程类似,不过带来的问题回避进程要严重得多。如果你的主线程,也就是main函数执行的那个线程,在你其他线程推出之前就已经退出,那么带来的bug则不可估量。通过pthread_join函数会让主线程阻塞,直到所有线程都已经退出。

pthread_join:使一个线程等待另一个线程结束。
代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。

int pthread_join(pthread_t thread, void **value_ptr);

thread:等待退出线程的线程号。
value_ptr:退出线程的返回值。
下面一个例子结合上面的内容:

int main ()
{
    pthread_t thread1_id;
    pthread_t thread2_id;
    struct char_print_parms thread1_args;
    struct char_print_parms thread2_args;
    /* Create a new thread to print 30,000 x’s. */
    thread1_args.character = ’x’;
    thread1_args.count = 30000;
    pthread_create (&thread1_id, NULL, &char_print, &thread1_args);
    /* Create a new thread to print 20,000 o’s. */
    thread2_args.character = ’o’;
    thread2_args.count = 20000;
    pthread_create (&thread2_id, NULL, &char_print, &thread2_args);
    /* Make sure the first thread has finished. */
    pthread_join (thread1_id, NULL);
    /* Make sure the second thread has finished. */
    pthread_join (thread2_id, NULL);
    /* Now we can safely return. */
    return 0;
}

下面说一下前面提到的线程属性。
在我们前面提到,可以通过pthread_join()函数来使主线程阻塞等待其他线程退 出,这样主线程可以清理其他线程的环境。但是还有一些线程,更喜欢自己来清理退出的状态,他们也不愿意主线程调用pthread_join来等待他们。我 们将这一类线程的属性称为detached。如果我们在调用pthread_create()函数的时候将属性设置为NULL,则表明我们希望所创建的线 程采用默认的属性,也就是jionable。如果需要将属性设置为detached,则参考下面的例子:

#include <stdio.h>
#include <pthread.h>
void * start_run(void * arg)
{
//do some work
}
int main()
{
    pthread_t thread_id;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    pthread_create(&thread_id,&attr,start_run,NULL);
    pthread_attr_destroy(&attr);
    sleep(5);
    exit(0);
}

在线程设置为joinable后,可以调用pthread_detach()使之成为detached。但是相反的操作则不可以。还有,如果线程已经调用pthread_join()后,则再调用pthread_detach()则不会有任何效果。
线程可以通过自身执行结束来结束,也可以通过调用pthread_exit()来结束线程的执行。另外,线程甲可以被线程乙被动结束。这个通过调用pthread_cancel()来达到目的。

int pthread_cancel(pthread_t thread);

函数调用成功返回0。
当然,线程也不是被动的被别人结束。它可以通过设置自身的属性来决定如何结束。
线程的被动结束分为两种,一种是异步终结,另外一种是同步终结。异步终结就是当其他线程调用 pthread_cancel的时候,线程就立刻被结束。而同步终结则不会立刻终结,它会继续运行,直到到达下一个结束点(cancellation point)。当一个线程被按照默认的创建方式创建,那么它的属性是同步终结。
通过调用pthread_setcanceltype()来设置终结状态。

int pthread_setcanceltype(int type, int *oldtype);

state:要设置的状态,可以为PTHREAD_CANCEL_DEFERRED或者为PTHREAD_CANCEL_ASYNCHRONOUS。
那么前面提到的结束点又是如何设置了?最常用的创建终结点就是调用pthread_testcancel()的地方。该函数除了检查同步终结时的状态,其他什么也不做。
上面一个函数是用来设置终结状态的。还可以通过下面的函数来设置终结类型,即该线程可不可以被终结:

int pthread_setcancelstate(int state, int *oldstate);

state:终结状态,可以为PTHREAD_CANCEL_DISABLE或者PTHREAD_CANCEL_ENABLE。具体什么含义大家可以通过单词意思即可明白。
最后说一下线程的本质。其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过 一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。

转载请注明出处。http://www.vimer.cn/