Blogger字距與行距調整
不論是Blogger或Wordpress,預設的字體都是以英文為最佳化的,英文相對中文來說,適合閱讀的字體比較小,字距也比較小,雖然新版Blogger提供一些調整字體的UI,不過功能還是陽春了一點。
在一些預設的Blogger範本中要調整本文的字體大小、字距與行距可以編輯.post或.post div這類的CSS欄位,加入以下三行進行微調:font-size: 115%;letter-spacing: 1.4px;line-height: 1.3em;
路漫漫其修远兮,吾将上下而求索。
字体美化,其实我自己看着这两字我都麻木了,还是这老掉牙的东西,不过现在对 Linux 下字体的调整总算是入门了。
从一开始的工具 链、到后来的内核、X Server 、桌面环境、应用软件基本上搞定后,花了一天时间来仔细研究关于 Linux 下的字体调整,总算是把 Gentoo 下搞得漂漂亮亮的了,现在已经基本上开始取代 Ubuntu 成为主要应用的操作系统了。不过 Ubuntu 也坐了一把顺风车,嘿嘿。
一直以来,所做的几次美化,都没有关注 dpi (Dot Per Inch) 这个设置,通常把它设置为 96dpi ( Windows 上的默认设置 ),以前也知道这个设置对于字体的调整有很大关系,但是都没有深入,这次恶补了几下。
现在写日志越来越懒了,还是照旧吧,写重点,要了解具体的选项及作用,看文章结尾的相关链接吧。
1. 设置正确的 DPI (Dot Per Inch)
在 Linux 里,有很多地方都可以设置 DPI ,而且不同地方设置的 DPI 分别管理不同的地方(开始晕了)。不过,我可不管它哪里是管哪的,我只知道,我需要全部都使用正确的 DPI 不就行了,所以一个个的查看一下,再一个一个的改吧。
比较完整的各种显示器的 DPI 设置可以查看这里:
Notebook LCD Display Comparison
Desktop LCD Display Comparison
以我的为例讲解,14.1“ 的显示器,分辨率为 1280 x 800 ,可以查出正确的 DPI 应该是107.1Size Resolution Density Pixel Size Ratio Pixels Gain
14.1" 1280 x 800 107.1 ppi 0.2373 mm 16:10 0.98 MP 130%
A. 首先来查看一下 X Server 使用的 DPI ,一看,完全正确, Ubuntu 拷贝过来的 Xorg.conf 设置得还是八错的。# xdpyinfo | grep resolution
resolution: 107x107 dots per inch
假如这个地方不正确,可以通过修改 Xorg.conf 来解决,方法如下:
* 非常老实的拿一块尺子,把你的显示器长宽量下来,单位为毫米,例如我的 303×190,把它加到 /etc/X11/xorg.conf# vim /etc/X11/xorg.conf 加入
Section "Monitor"
......
DisplaySize 303 190 # 1280x800 107dpi
......
EndSection
* 什么,你真的拿尺子去了? 我败给你了,如果仔细的话,在上面 Notebook LCD Display Comparison 里是可以查到的。Size Diagonal Width Height Ratio Area Gain
14.1" 35.81 cm 30.37 cm 18.98 cm 16:10 576 cm2 127%
* 记得修改好了重启一下 X ,然后执行下面的命令,看看是否生效
dimensions: 1280x800 pixels (303x190 millimeters)
# xdpyinfo | grep resolution
resolution: 107x107 dots per inch
B. fontconfig/xft 也有一个 DPI 的设置,如果大家都做过美化的话,对 language-selector.conf / local.conf 等配置文件,再熟悉不过了,嗯,这个 DPI 就在这里设置。不过别问我这个设置对哪些地方有效,因为我…………也不清楚。
这个的设置非常简单,在配置文件里面加上下面这么一段,就可以了。# vim /etc/fonts/local.conf 或 vim ~/.fonts.conf 加入
<!--
dots per inch
-->
<match target="pattern">
<edit name="dpi" mode="assign" >
<double>107</double>
</edit>
</match>
什么,你已经加入了? 其实,这个设置没多大用的,为什么?请看C
C. dpi in gnome/xfce
为什么说上面 B 所说的没多大用,是因为,现在的桌面环境都比较 BT 了,都提供接口来对 fontconfig/xft 程序设置 dpi ,就算你配置文件写好了,这里一改,配置文件还是瞎搞,所以还是图形化修改吧。
gnome 的设置在 Settings - Fonts 里
xfce 的设置在 Settings - User Interface Settings 里,自己填吧。
在 gnome 下,立即就可以看到效果,在 xfce 里,需要重启一下 X 。
查看 Xft.dpi 值的方法:# xrdb -query | grep Xft
......
Xft.antialias: 1
Xft.dpi: 107.00000
Xft.hinting: 1
Xft.hintstyle: hintfull
Xft.rgba: rgb
顺便对对用 KDE 的兄弟说声抱歉了,没环境。
2. 配置文件
偷懒开始了,粘贴了事。
配置文件写得非常简单,去掉了一切可以去掉的东西,反正我的环境特简单,中文用 Microsoft YaHei (咳咳,什么什么?),英文用 Redhat 提供的开源字体 Liberation 。
在 Ubuntu 里,把这个保存到 /usr/share/language-selector/fontconfig/zh_CN
在 Gentoo 里,可以保存到 /etc/font/local.conf
当然你也可以保存到 ~/.font.conf ,不过这样就只对本用户有效而已。
---------------------- local.conf 12.06 更新 -----------------------------<fontconfig>
<!--
# 设置 serif 衬线字体的读取顺序
-->
<alias>
<family>serif</family>
<prefer>
<family>Liberation Serif</family>
<family>Microsoft YaHei</family>
<family>SimSun</family>
</prefer>
</alias>
<!--
# 设置 sans-serif 无衬线字体的读取顺序
-->
<alias>
<family>sans-serif</family>
<prefer>
<family>Liberation Sans</family>
<family>Microsoft YaHei</family>
<family>Simsun</family>
</prefer>
</alias>
<!--
# 设置 monospace 等宽字体的读取顺序
-->
<alias>
<family>monospace</family>
<prefer>
<family>Liberation Mono</family>
<family>Microsoft YaHei</family>
<family>Simsun</family>
</prefer>
</alias>
<!--
# 全局字体的设置
-->
<match target="font">
<test name="family" compare="contains">
<string>Tahoma</string>
<string>verdana</string>
<string>Arial</string>
<string>Times New Roman</string>
</test>
<edit name="globaladvance">
<bool>false</bool>
</edit>
<edit name="spacing">
<int>0</int>
</edit>
<edit name="hinting" mode="assign">
<bool>true</bool>
</edit>
<edit name="autohint" mode="assign">
<bool>false</bool>
</edit>
<edit name="antialias" mode="assign">
<bool>true</bool>
</edit>
<edit name="hintstyle" mode="assign" >
<const>hintfull</const>
</edit>
</match>
<!--
# 单独设置微软雅黑字体的显示
-->
<match target="font">
<test name="family" compare="contains">
<string>Microsoft YaHei</string>
<string>微软雅黑</string>
</test>
<edit name="hinting" mode="assign">
<bool>true</bool>
</edit>
<edit name="autohint" mode="assign">
<bool>false</bool>
</edit>
<edit name="antialias" mode="assign">
<bool>true</bool>
</edit>
<edit name="hintstyle" mode="assign" >
<const>hintfull</const>
</edit>
<edit name="rgba" mode="assign">
<const>gba</const>
</edit>
</match>
<!--
# 单独设置 Liberation Sans 字体的显示
-->
<match target="font">
<test name="family" compare="contains">
<string>Liberation Sans</string>
</test>
<edit name="hinting" mode="assign">
<bool>false</bool>
</edit>
<edit name="autohint" mode="assign">
<bool>false</bool>
</edit>
<edit name="antialias" mode="assign">
<bool>true</bool>
</edit>
<edit name="hintstyle" mode="assign" >
<const>hintfull</const>
</edit>
<edit name="rgba" mode="assign">
<const>none</const>
</edit>
</match>
<!--
# 单独设置 Liberation Mono 字体的显示
-->
<match target="font">
<test name="family" compare="contains">
<string>Liberation Mono</string>
</test>
<edit name="hinting" mode="assign">
<bool>false</bool>
</edit>
<edit name="autohint" mode="assign">
<bool>false</bool>
</edit>
<edit name="antialias" mode="assign">
<bool>true</bool>
</edit>
<edit name="hintstyle" mode="assign" >
<const>hintfull</const>
</edit>
<edit name="rgba" mode="assign">
<const>none</const>
</edit>
</match>
</fontconfig>
---------------------- local.conf 12.06 更新 -----------------------------
3. 系统设置。
gnome : Fonts 里,把 AA、 hinting (Full) 、 Sub-pixel hinting (RGB) 选上
xfce: User Interface Settings 里面把 AA、 hinting (Full) 、 Sub-pixel hinting (RGB) 全部选上
4. 后续
现在你会发现系统基本上已经显示得很完美了,除了 firefox ,firefox 有自己的一套管理方式,会跟你作对的。
For Ubuntu:
用 Ubuntu 可以用下面的方法关闭 firefox 默认使用的 Pango ,从而使用系统里的 fontconfig/xft 来控制# vi /etc/firefox/firefoxrc
MOZ_DISABLE_PANGO=1
---------------------- gentoo 下 firefox 安装 12.06 更新 -----------------------------
For Gentoo:
在 Gentoo 的 mozilla-firefox 包里,有一个 moznopango 的 USE ,加上这个 USE ,可以关闭 pango 的使用。
# echo " www-client/mozilla-firefox moznopango" >> /etc/portage/package.use
# emerge -av mozilla-firefox
---------------------- gentoo 下 firefox 安装12.06 更新 -----------------------------
如果你在 Gentoo 里,你会发现明显还是不如在 Ubuntu 下漂亮,为什么呢?
那是因为 Gentoo 官方源里的 Cairo 、libXft 两个基础库都不带次像素渲染,这个对字体的显示有着重要影响,所以我们必须将 Cairo 、libXft 两个基础库利用 overlay 进行升级。
# layman -L
# layman -f -a xeffects # 需要 svn 支持
# echo 'source /usr/portage/local/layman/make.conf' >> /etc/make.conf
# env-update && source /etc/profile
# euse -E newspr # 关键就在 newspr 这个 flag,官方源里的 Cairo 、 libXft 是没有这个可选的 Flag , 所以需要 overlay 里的来进行升级
# emerge -av1 cairo libXft
重启 X 后,再看看,雅黑字体不模糊过头了吧,清晰了吧,不会出现彩色光蕴的问题了吧。
OK,顺便上个图看看吧,Gentoo Xfce4 下。
相关链接:
Linux 字体微调 - windows 效果版
字体设置 (Arch Wiki)
http://www.linuxsir.org/bbs/showthread.php?t=317779
SubpixelHinting
这个地方可以测试一下微软的字体
http://www.microsoft.com/typography/web/fonts/default.htm
----------------------12.06 更新 -----------------------------
这次的更新,使英文字体显示更为完美,中文的雅黑无变动。
----------------------12.06 更新 -----------------------------
这个驱动程序是典型char型设备驱动,它涉及到了如何使用共享中断、相同功能的设备共用驱动程序等一些技巧。个人的愚见,欢迎来讨论。
/****************************************************************************
直放站 UART3 driver
shanghai xinmin telecom std.
diming.feng 2007-05
*****************************************************************************/
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/errno.h>
#include <asm/delay.h>
//#include <asm/fcntl.h>
#include <asm/arch/irqs.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/serial_reg.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include "expuart.h"
#define GPIO_CFG0 VPint(0xFFF83000)
#define GPIO_DIR0 VPint(0xFFF83004)
#define GPIO_DATAOUT0 VPint(0xFFF83008)
#define GPIO_CFG5 VPint(0xFFF83050)
#define GPIO_DIR5 VPint(0xFFF83054)
#define GPIO_DATAOUT5 VPint(0xFFF83058)
#define GPIO_XICFG VPint(0xFFF83074)
#define GPIO_XISTATUS VPint(0xFFF83078)
#define UART_TX VPchar(COM_TX_3)
#define UART_RX VPchar(COM_RX_3)
#define UART_DLL VPchar(COM_DLL_3)
#define UART_DLM VPchar(COM_DLM_3)
#define UART_IER VPchar(COM_IER_3)
#define UART_IIR VPchar(COM_IIR_3)
#define UART_FCR VPchar(COM_FCR_3)
#define UART_LCR VPchar(COM_LCR_3)
#define UART_MCR VPchar(COM_MCR_3)
#define UART_LSR VPchar(COM_LSR_3)
#define UART_MSR VPchar(COM_MSR_3)
#define UART_TOR VPchar(COM_TOR_3)
#define LSR_TE 0x40
#define LSR_THRE 0x20
#define LSR_RFDR 0x01
#define nIRQ2 31
#define uart3_IRQ 12
#define NOIRQ 0x01
#define RSTIRQ 0x06
#define RFVIRQ 0x04
#define RFTIRQ 0x0C
#define THRIRQ 0x02
#define MSTIRQ 0x00
#define RX_FIFO_LEVEL 14
#define UART_FCR_LEVEL 0xc0
#define CMD_BANDRATE 0 // set bandrate
#define CMD_UARTLCR 1 // set data bit
#define CMD_RXTIMEOUT 2
#define CMD_MODE 3 // set RS-485/RS-232 mode
#define CMD_MODEM_SET 4 // set internal modem
#define MAJOR_NUM 248
#define YES 1
#define NO 0
#define TIME_OUT 20000
#define uart3_int(band) do{ \
UART_LCR = 0; \
UART_IER = 0; \
UART_LCR = DLAB;\
UART_DLL = band;\
UART_DLM = 0; \
UART_LCR = 0x03;\
UART_FCR = 0x07;\
UART_TOR = 0x00;\
}while(0)
#define write_reg(reg,dat) do{ \
UART_TX = 0x80+reg; \
while((UART_LSR&LSR_TE)!=LSR_TE); \
UART_TX = dat; \
while((UART_LSR&LSR_TE)!=LSR_TE); \
}while(0)
#define read_reg(reg,dat) do{ \
UART_TX = reg; \
while((UART_LSR & LSR_RFDR)!=LSR_RFDR); \
dat = UART_RX; \
}while(0)
static int uart3tx_busy;
static uart3_dev exp_uart[EXPUART_NUM]={
{248, "uart3", 0},
{247, "uart4", 1},
{246, "uart5", 2}, // internal mode
{245, "uart6", 3},
};
DECLARE_WAIT_QUEUE_HEAD(expuart1_queue_Rd);
DECLARE_WAIT_QUEUE_HEAD(expuart1_queue_Wr);
DECLARE_WAIT_QUEUE_HEAD(expuart2_queue_Rd);
DECLARE_WAIT_QUEUE_HEAD(expuart2_queue_Wr);
DECLARE_WAIT_QUEUE_HEAD(expuart3_queue_Rd);
DECLARE_WAIT_QUEUE_HEAD(expuart3_queue_Wr);
DECLARE_WAIT_QUEUE_HEAD(expuart4_queue_Rd);
DECLARE_WAIT_QUEUE_HEAD(expuart4_queue_Wr);
static void ms_delay(int ms)
{
while(ms--){
udelay(1000);
}
}
/*
static void write_reg(char reg_addr, char reg_dat)
{
char temp;
UART_TX = 0x80+reg_addr;
do{
temp = UART_LSR;
}while((temp&LSR_TE)!=LSR_TE);
UART_TX = reg_dat;
do{
temp = UART_LSR;
}while((temp&LSR_TE)!=LSR_TE);
}
*/
static int uart3_chkbuf(int write, int read, int reqnum)
{
int temp = write-read;
if(temp >= 0){
if(temp >= reqnum){
return 1;
}
}
else{
if((temp + RX_BUFF_SIZE)>= reqnum){
return 1;
}
}
return 0;
}
static void expuart_handler(int irq, void *dev_id, struct pt_regs * regs)
{
char sub_sifr, int_no, sub_ssr;
int expnum;
int ctn;
GPIO_XISTATUS = 0;
uart3_dev *dev = (uart3_dev *)dev_id;
expnum = dev->no;
/* disable VK3214 all interrupt */
write_reg(gIR,0x00);
read_reg(gIR,int_no); // read current interrupt uart
int_no = int_no & 0x0f;
if(1<<expnum != int_no){
goto out;
}
/* read sub uart of VK3214 interrupt flags */
read_reg(SSR(expnum),sub_ssr);
// send data to uart
if((dev->usr_wr_num>0)&&((sub_ssr&4)==4)){
ctn = dev->usr_wr_num - dev->tx_ctn;
if(ctn >15){
ctn = 15;
}
UART_TX = 0xc0+(expnum<<4)+(ctn-1);
while(ctn--){
UART_TX = dev->txbuf[dev->tx_ctn++];
}
while((UART_LSR & LSR_TE)!=LSR_TE);
do{
UART_TX = SSR(expnum);
while((UART_LSR & LSR_RFDR)!=LSR_RFDR);
}while((UART_RX&4)!=4);
if(dev->tx_ctn == dev->usr_wr_num){
write_reg(SIER(expnum),0x01); // tx interrupt disable, rx interrupt enable
dev->usr_wr_num = 0;
dev->tran_flag = 1;
uart3tx_busy = NO;
if(exp_uart[0].wait_flag == 1){
// printk("wake 0\n");
exp_uart[0].wait_flag = 0;
wake_up_interruptible(&expuart1_queue_Wr); //
}
if(exp_uart[1].wait_flag == 1){
// printk("wake 1\n");
exp_uart[1].wait_flag = 0;
wake_up_interruptible(&expuart2_queue_Wr); //
}
if(exp_uart[2].wait_flag == 1){
// printk("wake 2\n");
exp_uart[2].wait_flag = 0;
wake_up_interruptible(&expuart3_queue_Wr); //
}
if(exp_uart[3].wait_flag == 1){
// printk("wake 3\n");
exp_uart[3].wait_flag = 0;
wake_up_interruptible(&expuart4_queue_Wr); //
}
}
goto out;
}
// receive data from uart
if((sub_ssr&1) == 1){
goto out;
}
read_reg(SFSR(expnum), ctn);
ctn = UART_RX & 0x0f;
while(ctn--){
read_reg(SFDR(expnum), dev->rxbuf[dev->Recv_wr_index++]);
if(dev->Recv_wr_index == RX_BUFF_SIZE){
dev->Recv_wr_index = 0;
}
}
dev->recv_flag = uart3_chkbuf(dev->Recv_wr_index,dev->Recv_rd_index,dev->usr_rd_num);
if(dev->recv_flag){
switch(expnum){
case 0:
wake_up_interruptible(&expuart1_queue_Rd); //
break;
case 1:
wake_up_interruptible(&expuart2_queue_Rd); //
break;
case 2:
wake_up_interruptible(&expuart3_queue_Rd); //
break;
case 3:
wake_up_interruptible(&expuart4_queue_Rd); //
break;
default:
break;
}
}
out:
write_reg(gIR,0xf0);
}
ssize_t uart3_read(struct file *flip, char *buf, size_t count, loff_t *offset)
{
int i;
int expnum;
wait_queue_t wait;
uart3_dev *dev = (uart3_dev *)flip->private_data;
expnum = dev->no;
dev->usr_rd_num = count;
dev->recv_flag = uart3_chkbuf(dev->Recv_wr_index,dev->Recv_rd_index,dev->usr_rd_num);
if(dev->recv_flag==0){
init_waitqueue_entry(&wait, current); //
switch(expnum){
case 0:
add_wait_queue(&expuart1_queue_Rd, &wait); //
break;
case 1:
add_wait_queue(&expuart2_queue_Rd, &wait); //
break;
case 2:
add_wait_queue(&expuart3_queue_Rd, &wait); //
break;
case 3:
add_wait_queue(&expuart4_queue_Rd, &wait); //
break;
default:
break;
}
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(dev->time_out*HZ);
set_current_state(TASK_RUNNING);
switch(expnum){
case 0:
remove_wait_queue(&expuart1_queue_Rd, &wait); //
break;
case 1:
remove_wait_queue(&expuart2_queue_Rd, &wait); //
break;
case 2:
remove_wait_queue(&expuart3_queue_Rd, &wait); //
break;
case 3:
remove_wait_queue(&expuart4_queue_Rd, &wait); //
break;
default:
break;
}
}
count = dev->Recv_wr_index - dev->Recv_rd_index;
if(count<0){
count += RX_BUFF_SIZE;
}
if(count > dev->usr_rd_num){
count = dev->usr_rd_num;
}
dev->usr_rd_num = 0;
for(i=0; i<count; i++){
buf[i] = dev->rxbuf[dev->Recv_rd_index];
dev->Recv_rd_index += 1;
if(dev->Recv_rd_index == RX_BUFF_SIZE){
dev->Recv_rd_index = 0;
}
}
return count;
}
static int uart3_write(struct file *flip, int *buf, size_t count, loff_t *offset)
{
int expnum;
wait_queue_t wait;
uart3_dev *dev = (uart3_dev *)flip->private_data;
if(count==0){
return 0;
}
dev->tx_ctn = 0;
expnum = dev->no;
if((dev->tran_flag==1)&&(uart3tx_busy==NO)){
goto copy_data;
}
// printk("wait %d\n",expnum);
dev->wait_flag = 1;
init_waitqueue_entry(&wait, current); //
switch(expnum){
case 0:
add_wait_queue(&expuart1_queue_Wr, &wait); //
break;
case 1:
add_wait_queue(&expuart2_queue_Wr, &wait); //
break;
case 2:
add_wait_queue(&expuart3_queue_Wr, &wait); //
break;
case 3:
add_wait_queue(&expuart4_queue_Wr, &wait); //
break;
default:
break;
}
set_current_state(TASK_INTERRUPTIBLE);
schedule();
set_current_state(TASK_RUNNING);
switch(expnum){
case 0:
remove_wait_queue(&expuart1_queue_Wr, &wait); //
break;
case 1:
remove_wait_queue(&expuart2_queue_Wr, &wait); //
break;
case 2:
remove_wait_queue(&expuart3_queue_Wr, &wait); //
break;
case 3:
remove_wait_queue(&expuart4_queue_Wr, &wait); //
break;
default:
break;
}
copy_data:
if(count > TX_BUFF_SIZE){
count = TX_BUFF_SIZE;
}
copy_to_user(dev->txbuf, buf, count);
dev->tran_flag = 0;
dev->usr_wr_num = count;
uart3tx_busy = YES;
write_reg(SIER(expnum),0x03); // rx and tx interrupt enable
return count;
}
static int uart3_open(struct inode *inode,struct file* flip)
{
int result;
int expnum;
uart3_dev *dev;
expnum = 248 - MAJOR(inode->i_rdev);
dev =(uart3_dev *)(&exp_uart[expnum]);
result = request_irq(nIRQ2, expuart_handler, SA_SHIRQ, dev->dev_name, dev);
if(result == -1){
printk("register the expand uart irq failed!\n");
return -EIO;
}
dev->no = expnum;
dev->Recv_wr_index = 0;
dev->Recv_rd_index = 0;
dev->usr_rd_num = 0;
dev->recv_flag = 0;
dev->time_out = 10;
dev->tx_ctn = 0;
dev->usr_wr_num = 0;
dev->tran_flag = 1;
MOD_INC_USE_COUNT;
flip->private_data = dev;
// default
write_reg(SCTLR(expnum),B1200+0x08); // enable uart
write_reg(SFOCR(expnum),0xff);
write_reg(SFOCR(expnum),0x0c);
write_reg(SIER(expnum),0x01); // rx interrupt enable, tx interrupt disable
return 0;
}
static int uart3_close(struct inode* i,struct file* flip)
{
uart3_dev *dev = (uart3_dev *)flip->private_data;
free_irq(nIRQ2, dev);
MOD_DEC_USE_COUNT;
return 0;
}
static int uart3_ioctl(struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg)
{
char temp;
int expnum;
uart3_dev *dev = (uart3_dev *)flip->private_data;
expnum = dev->no;
switch(cmd)
{
case CMD_BANDRATE: // set bandrate
if(arg==115200)
temp = B115200;
else if(arg==57600)
temp = B57600;
else if(arg==38400)
temp = B38400;
else if(arg==19200)
temp = B19200;
else if(arg==9600)
temp = B9600;
else if(arg==4800)
temp = B4800;
else if(arg==2400)
temp = B2400;
else if(arg==1200)
temp = B1200;
else
temp = B9600;
// printk("set expuart %d bandrate:%d\n",expnum,arg);
write_reg(SCTLR(expnum),temp+0x08); // enable uart
break;
case CMD_UARTLCR: // set uart
write_reg(SCONR(expnum),(char)arg);
break;
case CMD_RXTIMEOUT:
dev->time_out = arg;
break;
case CMD_MODEM_SET:
if((arg == 1)&&(expnum==2)){
GPIO_DATAOUT5 |= 0x200;
printk("high\n");
}
if((arg == 0)&&(expnum==2)){
GPIO_DATAOUT5 &= 0xFFFFFDFF;
printk("low\n");
}
break;
default:
break;
}
return 0;
}
struct file_operations uart3_fops =
{
owner: THIS_MODULE,
open: uart3_open,
write: uart3_write,
read: uart3_read,
ioctl: uart3_ioctl,
release: uart3_close,
};
static int __init uart3_init(void)
{
int i;
char temp;
int result;
int cfg;
int time,time1;
time1 = 1;
cfg = GPIO_CFG0;
cfg &= 0xFFFFFC00;
cfg |= 0x3C2; // GPIO.0=nIRQ2, GPIO.1=output, GPIO.3=TXD3,GPIO.4=RXD3
GPIO_CFG0 = cfg;
GPIO_DIR0 |= 0x60006;
GPIO_DATAOUT0 |= 0x4;
cfg = GPIO_XICFG;
cfg &= 0xf0;
// cfg |= 0x8; // low level sensitive
cfg |= 0xa; // negative edge triggered
GPIO_XICFG = cfg;
cfg = GPIO_CFG5; // GPIO.14=output
cfg &= 0x3F3FFFF;
GPIO_CFG5 = cfg;
GPIO_DIR5 = 0x2000200;
GPIO_DATAOUT5 |= 0x200;
uart3_int(96); // default 9600
GPIO_DATAOUT0 |= 0x02; // gpio.1=1
ms_delay(10);
GPIO_DATAOUT0 &= ~(0x02); // gpio.1=0
ms_delay(20);
GPIO_DATAOUT0 |= 0x02; // gpio.1=1
write_reg(gIR,0xf0); // enable all uart port interrupt
loop:
uart3_int(96); // default 9600
write_reg(gMUCR,B115200); // main uart bandrate 115200
uart3_int(6); // 230400bps@15MHz
for(i=0;i<time1;i++){
ms_delay(10);
}
time = TIME_OUT;
UART_TX = gMUCR;
do{
temp = UART_LSR;
time--;
if(time == 0){
time1++;
goto loop;
}
}while((temp & LSR_RFDR)!=LSR_RFDR);
temp = UART_RX;
if(temp != B115200){
time1++;
// printk("temp = %02x\n",temp);
goto loop;
}
for(i=0;i<EXPUART_NUM;i++){
result = register_chrdev(exp_uart[i].major_num,exp_uart[i].dev_name,&uart3_fops);
if(result == 0)
printk("expand uart %d at 0xfff80300 (irq = 31) is a W90N745!\n",i+1);
}
uart3tx_busy = NO;
return 0;
}
static void uart3_release(void)
{
int i;
for(i=0;i<EXPUART_NUM;i++){
unregister_chrdev(exp_uart[i].major_num,exp_uart[i].dev_name);
}
printk("cancel the device expand uart1,2,3,4!\n");
return;
}
module_init(uart3_init);
module_exit(uart3_release);
locale 是国际化与本土化过程中的一个非常重要的概念,个人认为,对于中文用户来说,通常会涉及到的国际化或者本土化,大致包含三个方面:看中文,写中文,与 window中文系统的兼容和通信。从实际经验上看来,locale的设定与看中文关系不大,但是与写中文,及window分区的挂载方式有很密切的关 系。本人认为就像一个纯英文的Windows能够浏览中文,日文或者意大利文网页一样,你不需要设定locale就可以看中文。那么,为什么要设定 locale呢?什么时候会用到locale呢?
一、为什么要设定locale 正如前面我所讲的,设定locale与你能否浏览中文的网页没有直接的关系,即便你把locale设置成en_US.ISO-8859-1这样一个标准的 英文locale你照样可以浏览中文的网页,只要你的系统里面有相应的字符集(这个都不一定需要)和合适的字体(如simsun),浏览器就可以把网页翻 译成中文给你看。具体的过程是网络把网页传送到你的机器上之后,浏览器会判断相应的编码的字符集,根据网页采用的字符集,去字体库里面找合适的字体,然后 由文字渲染工具把相应的文字在屏幕上显示出来。
在下文本人会偶尔把字符集比喻成密码本,个人觉得对于一些东西比较容易理解,假如你不习惯的话,把全文copy到任何文本编辑器,用字符集替换密码本即可。
那 有时候网页显示乱码或者都是方框是怎么回事呢?个人认为,显示乱码是因为设定的字符集不对(或者没有相应的字符集),例如网页是用UTF-8编码的,你非 要用GB2312去看,而系统根据GB2312去找字体,然后在屏幕上显示,当然是一堆的乱码,也就是说你用一个错误的密码本去翻译发给你的电报,当然内 容那叫一个乱;至于有些时候浏览的网页能显示一部分汉字,但有很多的地方是方框,能够显示汉字说明浏览器已经正确的判断出了网页的编码,并在字体库里面找 到了相应的文字,但是并不是每个字体库都包含某个字符集全部的字体的缘故,有些时候会显示不完全,找一个比较全的支持较多字符集的字体就可以了。
既然我能够浏览中文网页,那为什么我还要设定locale呢?
其 实你有没有想过这么一个问题,为什么gentoo官方论坛上中文论坛的网页是用UTF-8编码的(虽然大家一直强烈建议用GB2312编码),但是新浪网 就是用GB2312编码的呢?而Xorg的官方网页竟然是ISO-8859-15编码的,我没有设定这个locale怎么一样的能浏览呢?这个问题就像是 你有所有的密码本,不论某个网站是用什么字符集编码的,你都可以用你手里的密码本把他们翻译过来,但问题是虽然你能浏览中文网页,但是在整个操作系统里面 流动的还是英文字符。所以,就像你能听懂英语,也能听懂中文。 最根本的问题是:你不可以写中文。
当你决定要写什么东西的时候,首先 要决定的一件事情是用那种语言,对于计算机来说就是你要是用哪一种字符集,你就必须告诉你的linux系统,你想用那一本密码本去写你想要写的东西。知道 为什么需要用GB2312字符集去浏览新浪了吧,因为新浪的网页是用GB2312写的。
为了让你的Linux能够输入中文,就需要把 系统的locale设定成中文的(严格说来是locale中的语言类别LC_CTYPE ),例如zh_CN.GB2312、zh_CN.GB18030或者zh_CN.UTF-8。很多人都不明白这些古里古怪的表达方式。这个外星表达式规定 了什么东西呢?这个问题稍后详述,现在只需要知道,这是locale的表达方式就可以了。
二、到底什么是locale? locale这个单词中文翻译成地区或者地域,其实这个单词包含的意义要宽泛很多。Locale是根据计算机用户所使用的语言,所在国家或者地区,以及当地的文化传统所定义的一个软件运行时的语言环境。
这 个用户环境可以按照所涉及到的文化传统的各个方面分成几个大类,通常包括用户所使用的语言符号及其分类(LC_CTYPE),数字 (LC_NUMERIC),比较和排序习惯(LC_COLLATE),时间显示格式(LC_TIME),货币单位(LC_MONETARY),信息主要是 提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES),姓名书写方式(LC_NAME),地址书写方式(LC_ADDRESS),电话号码书写方式 (LC_TELEPHONE),度量衡表达方式(LC_MEASUREMENT),默认纸张尺寸大小(LC_PAPER)和locale对自身包含信息的 概述(LC_IDENTIFICATION)。
所以说,locale就是某一个地域内的人们的语言习惯和文化传统和生活习惯。一个地 区的locale就是根据这几大类的习惯定义的,这些locale定义文件放在/usr/share/i18n/locales目录下面,例如 en_US, zh_CN and de_DE@euro都是locale的定义文件,这些文件都是用文本格式书写的,你可以用写字板打开,看看里边的内容,当然出了有限的注释以外,大部分 东西可能你都看不懂,因为是用的Unicode的字符索引方式。
对于de_DE@euro的一点说明,@后边是修正项,也就是说你可 以看到两个德国的locale: /usr/share/i18n/locales/de_DE@euro /usr/share/i18n/locales/de_DE 打开这两个locale定义,你就会知道它们的差别在于de_DE@euro使用的是欧洲的排序、比较和缩进习惯,而de_DE用的是德国的标准习惯。
上面我们说到了zh_CN.GB18030的前半部分,后半部分是什么呢?大部分Linux用户都知道是系统采用的字符集。
三、 什么是字符集? 字符集就是字符,尤其是非英语字符在系统内的编码方式,也就是通常所说的内码,所有的字符集都放在/usr/share/i18n/charmaps,所 有的字符集也都是用Unicode编号索引的。Unicode用统一的编号来索引目前已知的全部的符号。而字符集则是这些符号的编码方式,或者说是在网络 传输,计算机内部通信的时候,对于不同字符的表达方式,Unicode是一个静态的概念,字符集是一个动态的概念,是每一个字符传递或传输的具体形式。就 像 Unicode编号U59D0是代表姐姐的“姐”字,但是具体的这个字是用两个字节表示,三个字节,还是四个字节表示,是字符集的问题。例如:UTF-8 字符集就是目前流行的对字符的编码方式,UTF-8用一个字节表示常用的拉丁字母,用两个字节表示常用的符号,包括常用的中文字符,用三个表示不常用的字 符,用四个字节表示其他的古灵精怪的字符。而GB2312字符集就是用两个字节表示所有的字符。需要提到一点的是Unicode除了用编号索引全部字符以 外,本身是用四个字节存储全部字符,这一点在谈到挂载windows分区的时候是非常重要的一个概念。所以说你也可以把Unicode看作是一种字符集 (我不知道它和UTF-32的关系,反正UTF-32就是用四个字节表示所有的字符的),但是这样表述符号是非常浪费资源的,因为在计算机世界绝大部分时 候用到的是一个字节就可以搞定的 26个字母而已。所以才会有UTF-8,UTF-16等等,要不然大同世界多好,省了这许多麻烦。
四、zh_CN.GB2312到底是在说什么? Locale 是软件在运行时的语言环境, 它包括语言(Language), 地域 (Territory) 和字符集(Codeset)。一个locale的书写格式为: 语言[_地域[.字符集]]. 所以说呢,locale总是和一定的字符集相联系的。下面举几个例子:
1、我说中文,身处中华人民共和国,使用国标2312字符集来表达字符。 zh_CN.GB2312=中文_中华人民共和国+国标2312字符集。
2、我说中文,身处中华人民共和国,使用国标18030字符集来表达字符。 zh_CN.GB18030=中文_中华人民共和国+国标18030字符集。
3、我说中文,身处中华人民共和国台湾省,使用国标Big5字符集来表达字符。 zh_TW.BIG5=中文_台湾.大五码字符集
4、我说英文,身处大不列颠,使用ISO-8859-1字符集来表达字符。 en_GB.ISO-8859-1=英文_大不列颠.ISO-8859-1字符集
5、我说德语,身处德国,使用UTF-8字符集,习惯了欧洲风格。 de_DE.UTF-8@euro=德语_德国.UTF-8字符集@按照欧洲习惯加以修正
注意不是de_DE@euro.UTF-8,所以完全的locale表达方式是 [语言[_地域][.字符集] [@修正值]
生成的locale放在/usr/lib/locale/目录中,并且每个locale都对应一个文件夹,也就是说创建了de_DE@euro.UTF-8 locale之后,就生成/usr/lib/locale/de_DE@euro.UTF-8/目录,里面是具体的每个locale的内容。
五、 怎样去自定义locale 在gentoo生成locale还是很容易的,首先要在USE里面加入userlocales支持,然后编辑locales.build文件,这个文件用 来指示glibc生成locale文件。 很多人不明白每一个条目是什么意思。 其实根据上面的说明现在应该很明确了。
File: /etc/locales.build en_US/ISO-8859-1 en_US.UTF-8/UTF-8
zh_CN/GB18030 zh_CN.GBK/GBK zh_CN.GB2312/GB2312 zh_CN.UTF-8/UTF-8
上面是我的locales.build文件,依次的说明是这样的:
en_US/ISO-8859-1:生成名为en_US的locale,采用ISO-8859-1字符集,并且把这个locale作为英文_美国locale类的默认值,其实它和en_US.ISO-8859-1/ISO-8859-1没有任何区别。
en_US.UTF-8/UTF-8:生成名为en_US.UTF-8的locale,采用UTF-8字符集。
zh_CN/GB18030:生成名为zh_CN的locale,采用GB18030字符集,并且把这个locale作为中文_中国locale类的默认值,其实它和zh_CN.GB18030/GB18030没有任何区别。
zh_CN.GBK/GBK: 生成名为zh_CN.GBK的locale,采用GBK字符集。 zh_CN.GB2312/GB2312:生成名为zh_CN.GB2312的locale,采用GB2312字符集。 zh_CN.UTF-8/UTF-8:生成名为zh_CN.UTF-8的locale,采用UTF-8字符集。
关于默认locale,默认locale可以简写成en_US或者zh_CN的形式,只是为了表达简单而已没有特别的意义。
Gentoo 在locale定义的时候掩盖了一些东西,也就是locale的生成工具:localedef。 在编译完glibc之后你可以用这个localedef 再补充一些locale,就会更加理解locale了。具体的可以看 localedef 的manpage。
$localedef -f 字符集 -i locale定义文件 生成的locale的名称 例如 $localedef -f UTF-8 -i zh_CN zh_CN.UTF-8
上面的定义方法和在locales.build中设定zh_CN.UTF-8/UTF-8的结果是一样一样的。
六、locale的五脏六腑
刚 刚生成了几个locale,但是为了让它们生效,必须告诉Linux系统使用那(几)个locale。这就需要对locale的内部机制有一点点的了解。 在前面我已经提到过,locale把按照所涉及到的文化传统的各个方面分成12个大类,这12个大类分别是: 1、语言符号及其分类(LC_CTYPE) 2、数字(LC_NUMERIC) 3、比较和排序习惯(LC_COLLATE) 4、时间显示格式(LC_TIME) 5、货币单位(LC_MONETARY) 6、信息主要是提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES) 7、姓名书写方式(LC_NAME) 8、地址书写方式(LC_ADDRESS) 9、电话号码书写方式(LC_TELEPHONE) 10、度量衡表达方式(LC_MEASUREMENT) 11、默认纸张尺寸大小(LC_PAPER) 12、对locale自身包含信息的概述(LC_IDENTIFICATION)。
其 中,与中文输入关系最密切的就是 LC_CTYPE, LC_CTYPE 规定了系统内有效的字符以及这些字符的分类,诸如什么是大写字母,小写字母,大小写转换,标点符号、可打印字符和其他的字符属性等方面。而locale定 义zh_CN中最最重要的一项就是定义了汉字(Class “hanzi”)这一个大类,当然也是用Unicode描述的,这就让中文字符在Linux系统中成为合法的有效字符,而且不论它们是用什么字符集编码 的。
LC_CTYPE % This is a copy of the "i18n" LC_CTYPE with the following modifications: - Additional classes: hanzi
copy "i18n"
class "hanzi"; / % ..;/ ..;/ ;;;;;;;;/ ;;;;;;;;/ ;;;; END LC_CTYPE
在en_US的locale定义中,并没有定义汉字,所以汉字不是有效字符。所以如果要输入中文必须使用支持中文的locale,也就是zh_XX,如zh_CN,zh_TW,zh_HK等等。
另 外非常重要的一点就是这些分类是彼此独立的,也就是说LC_CTYPE,LC_COLLATE和 LC_MESSAGES等等分类彼此之间是独立的,可以根据用户的需要设定成不同的值。这一点对很多用户是有利的,甚至是必须的。例如,我就需要一个能够 输入中文的英文环境,所以我可以把LC_CTYPE设定成zh_CN.GB18030,而其他所有的项都是en_US.UTF-8。
七、怎样设定locale呢?
设 定locale就是设定12大类的locale分类属性,即 12个LC_*。除了这12个变量可以设定以外,为了简便起见,还有两个变量:LC_ALL和LANG。它们之间有一个优先级的关系: LC_ALL>LC_*>LANG 可以这么说,LC_ALL是最上级设定或者强制设定,而LANG是默认设定值。 1、如果你设定了LC_ALL=zh_CN.UTF-8,那么不管LC_*和LANG设定成什么值,它们都会被强制服从LC_ALL的设定,成为 zh_CN.UTF-8。 2、假如你设定了LANG=zh_CN.UTF-8,而其他的LC_*=en_US.UTF-8,并且没有设定LC_ALL的话,那么系统的locale 设定以LC_*=en_US.UTF-8。 3、假如你设定了LANG=zh_CN.UTF-8,而其他的LC_*,和LC_ALL均未设定的话,系统会将LC_*设定成默认值,也就是LANG的值 zh_CN.UTF-8 。 4、假如你设定了LANG=zh_CN.UTF-8,而其他的LC_CTYPE=en_US.UTF-8,其他的LC_*,和LC_ALL均未设定的话, 那么系统的locale设定将是:LC_CTYPE=en_US.UTF-8,其余的 LC_COLLATE,LC_MESSAGES等等均会采用默认值,也就是LANG的值,也就是LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=zh_CN.UTF-8。
所以,locale是这样设定的: 1、如果你需要一个纯中文的系统的话,设定LC_ALL= zh_CN.XXXX,或者LANG= zh_CN.XXXX都可以,当然你可以两个都设定,但正如上面所讲,LC_ALL的值将覆盖所有其他的locale设定,不要作无用功。 2、如果你只想要一个可以输入中文的环境,而保持菜单、标题,系统信息等等为英文界面,那么只需要设定LC_CTYPE=zh_CN.XXXX,LANG = en_US.XXXX就可以了。这样LC_CTYPE=zh_CN.XXXX,而LC_COLLATE=LC_MESSAGES=……= LC_PAPER=LANG=en_US.XXXX。 3、假如你高兴的话,可以把12个LC_*一一设定成你需要的值,打造一个古灵精怪的系统: LC_CTYPE=zh_CN.GBK/GBK(使用中文编码内码GBK字符集); LC_NUMERIC=en_GB.ISO-8859-1(使用大不列颠的数字系统) LC_MEASUREMEN=de_DE@euro.ISO-8859-15(德国的度量衡使用ISO-8859-15字符集) 罗马的地址书写方式,美国的纸张设定……。估计没人这么干吧。 4、假如你什么也不做的话,也就是LC_ALL,LANG和LC_*均不指定特定值的话,系统将采用POSIX作为lcoale,也就是C locale。
为方便客户调试SHARP LQ035Q7DH01 LCD,现在我们将该款LCD主要的几个参数公布如下:
/* Sharp LQ035 portrait mode ADTFT display parameters */
const LCD_PARAM_T sharp_lq035 =
{
21, /* Horizontal back porch */
11, /* Horizontal front porch */
13, /* HSYNC pulse width */
240, /* Pixels per line */
3, /* Vertical back porch */
4, /* Vertical front porch */
2, /* VSYNC pulse width */
320, /* Lines per panel */
0, /* Do not invert output enable */
1, /* Invert panel clock */
1, /* Invert HSYNC */
0, /* Do not invert VSYNC */
1, /* AC bias frequency (not used) */
16, /* Bits per pixel */
標籤: LCD, LQ035Q7DH01
|
|
|
|
|
uclinux的启动主要分为两个阶段: - CPU: S3C2410 64MB RAM (DRAM)
第一部分bootloader启动阶段
第二部分linux 内核初始化和启动阶段
第一节:start_kernel
第二节:用户模式( user_mode )开始,start_kernel结束
第三节:加载linux内核完毕,转入cpu_idle进程
Specification of the machine
- MEM: 64MB (bank 0)
- ROM: 64MB (Inte Starata Flash Memory)
- got UART ports
0x3400 0000 +----------------------+
|VIVI RAM (size: 1M) |
0x33F0 0000 +----------------------+ VIVI_RAM_BASE
|HEAP AREA(size: 1M) |
0x33E0 0000 +----------------------+ HEAP_BASE
|MMU TABLE(size: 16k) |
0x33DF C000 +----------------------+ MMU_TABLE_BASE
| |
mtd partition table (size: 16k)| MTD_PART_SIZE -----\
0x33DF 8000 +----------------------+ |
vivi parameter table(size: 16k)| PARAMETER_TLB_SIZE | vivi pri data
0x33DF 4000 |----------------------+ |
linux command line (size: 16k) | LINUX_CMD_SIZE -----/
| |
0x33DF 0000 +----------------------+ VIVI_PRIV_RAM_BASE
| |
| free memory |
| |
0x3010 8000 +----------------------+
| kernel (size: 1MB) |
0x3000 8000 +----------------------+
| |
| free memory |
| |
0x3000 0100 +----------------------+
kernel param (size 256Byte) |
0x3000 0000 +----------------------+ DRAM_BASE
64MB ROM (NAND Flash)
* 0x0400 0000 +-----------------------------+
* | |
* | jffs2 (size: 61M) | user
* | |
* 0x0030 0000 +-----------------------------+
* | root (size: 2048k) |
* 0x0010 0000 +-----------------------------+
* | kernel (size: 832k) |
* 0x0003 0000 +-----------------------------+
* | param (size: 64k) |
* 0x0002 0000 +-----------------------------+
* | vivi (size: 128k) |
* 0x0000 0000 +-----------------------------+
Welcome to minicom 2.00.0
OPTIONS: History Buffer, F-key Macros, Search History Buffer, I18nCompiled on Jan 25 2003, 00:15:18.Press CTRL-A Z for help on special keys
minicom信息
VIVI version 0.1.4 (root@BC) (gcc version 2.95.2 20000516 (release) [Rebel.com]5
Bootloader头信息,版本等,这个因不同的bootloader的设计而有所不同,由此你能看出bootloader的版本信息,有很多使用的是通用的bootloader,如u-boot,redboot等。
源代码: vivi/lib/version.c vivi/main.c
#define VIVI_RELEASE "0.1.4"
#define VIVI_COMPILE_BY "root"
#define VIVI_COMPILE_HOST "localhost.localdomain"
#define VIVI_COMPILER "gcc version 2.95.2 20000516 (release)[Rebel.com]"
#define UTS_VERSION "#0.1.4 六 6月 5 05:27:07 CST 2004"
const char *vivi_banner = "VIVI version " VIVI_RELEASE " (" VIVI_COMPILE_BY "@" VIVI_COMPILE_HOST ") (" VIVI_COMPILER ") " UTS_VERSION "\r\n";putstr(vivi_banner);
MMU table base address = 0x33DFC000
Succeed memory mapping.
链接 MMU
内存映射(memory map)就是指在整个4GB物理地址空间中有哪些地址范围被分配用来寻址系统的RAM单元。比如,在SA-1100CPU中,从0xC000,0000开 始的512M地址空间被用作系统的RAM地址空间,而在Samsung S3C44B0X CPU中,从0x0c00,0000到0x1000,0000之间的64M地址空间被用作系统的RAM地址空间。虽然 CPU 通常预留出一大段足够的地址空间给系统 RAM,但是在搭建具体的嵌入式系统时却不一定会实现CPU预留的全部RAM地址空间。也就是说,具体的嵌入式系统往往只把CPU预留的全部RAM地址空 间中的一部分映射到RAM单元上,而让剩下的那部分预留RAM地址空间处于未使用(unused)状态。由于上述这个事实,因此Boot Loader的stage2必须在它想干点什么之前——比如,将存储在flash上的内核映像读到RAM空间中——检测整个系统的内存映射情况,也即它必 须知道CPU预留的全部RAM地址空间中的哪些被真正映射到RAM地址单元,哪些是处于unused状态的。
VIVI中用CONFIG_BOOTUP_MEMTEST中的memtest完成基本的RAM检测,针对Samsung S3C2410 CPU,从0x3000,0000到0x3400,0000之间的64M地址空间被用作系统的RAM地址空间。
源代码:vivi/arch/s3c2410/mmu.c
#define MMU_TABLE_BASE (HEAP_BASE - MMU_TABLE_SIZE)
#define HEAP_BASE (VIVI_RAM_BASE - HEAP_SIZE)
#define VIVI_RAM_BASE (DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE)
#define DRAM_BASE DRAM_BASE0
#define DRAM_BASE0 0x30000000 /* base address of dram bank 0 */
#define DRAM_SIZE SZ_64M
#define SZ_8M 0x00800000
#define VIVI_RAM_SIZE SZ_1M
#define HEAP_SIZE SZ_1M
#define SZ_1M 0x00100000
#define MMU_TABLE_SIZE SZ_16K
#define SZ_16K 0x00004000static
unsigned long *mmu_tlb_base = (unsigned long *) MMU_TABLE_BASE;
putstr_hex("MMU table base address = 0x", (unsigned long)mmu_tlb_base);
源代码:vivi/main.c
mem_map_init();
mmu_init();
putstr("Succeed memory mapping.\r\n");
NAND device: Manufacture ID: 0xec, Chip ID: 0x76 (Samsung K9D1208V0M)
链接:闪存芯片
NAND Flash:1989年,东芝公司发明。是以块和页为单位来读写的,不能随机访问某个指定的点。因而相对来说读取速度较慢,而擦除和写入的速度则比较快。一般适用在大容量的多媒体应用中,如:CF,SM。
NOR Flash:Intel于1988年发明.随机读取的速度比较快,随机按字节写,写入和擦除速度很低。一般适合应用于数据/程序的存贮应用中,如:手机,机顶盒。NOR还可以片内执行(execute-in-place)XIP。
简单的说,NAND类似于硬盘,NOR类似于内存
源代码:vivi/drivers/mtd/nand/smc_core.c
struct nand_flash_dev {
char * name;
int manufacture_id;
int model_id;
int chipshift;
char page256;
char pageadrlen;
unsigned long erasesize;
};
static struct nand_flash_dev nand_flash_ids[] = {
{"Toshiba TC5816BDC", NAND_MFR_TOSHIBA, 0x64, 21, 1, 2, 0x1000}, // 2Mb 5V
{"Toshiba TC58V16BDC", NAND_MFR_TOSHIBA, 0xea, 21, 1, 2, 0x1000}, // 2Mb 3.3V
{"Toshiba TC5832DC", NAND_MFR_TOSHIBA, 0x6b, 22, 0, 2, 0x2000}, // 4Mb 5V
{"Toshiba TC58V32DC", NAND_MFR_TOSHIBA, 0xe5, 22, 0, 2, 0x2000}, // 4Mb 3.3V
{"Toshiba TC58V64AFT/DC", NAND_MFR_TOSHIBA, 0xe6, 23, 0, 2, 0x2000}, // 8Mb 3.3V
{"Toshiba TH58V128DC", NAND_MFR_TOSHIBA, 0x73, 24, 0, 2, 0x4000}, // 16Mb
{"Toshiba TC58256FT/DC", NAND_MFR_TOSHIBA, 0x75, 25, 0, 2, 0x4000}, // 32Mb
{"Toshiba TH58512FT", NAND_MFR_TOSHIBA, 0x76, 26, 0, 3, 0x4000}, // 64Mb
{"Toshiba TH58NS100/DC", NAND_MFR_TOSHIBA, 0x79, 27, 0, 3, 0x4000}, // 128Mb
{"Samsung KM29N16000", NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000}, // 2Mb 5V
{"Samsung KM29W16000", NAND_MFR_SAMSUNG, 0xea, 21, 1, 2, 0x1000}, // 2Mb 3.3V
{"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000}, // 4Mb 5V
{"Samsung KM29W32000", NAND_MFR_SAMSUNG, 0xe3, 22, 0, 2, 0x2000}, // 4Mb 3.3V
{"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0xe5, 22, 0, 2, 0x2000}, // 4Mb 3.3V
{"Samsung KM29U64000", NAND_MFR_SAMSUNG, 0xe6, 23, 0, 2, 0x2000}, // 8Mb 3.3V
{"Samsung KM29U128T", NAND_MFR_SAMSUNG, 0x73, 24, 0, 2, 0x4000}, // 16Mb
{"Samsung KM29U256T", NAND_MFR_SAMSUNG, 0x75, 25, 0, 2, 0x4000}, // 32Mb
{"Samsung K9D1208V0M", NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000}, // 64Mb
{"Samsung K9D1G08V0M", NAND_MFR_SAMSUNG, 0x79, 27, 0, 3, 0x4000}, // 128Mb
{NULL,}
};
mtd->name = nand_flash_ids[i].name;
printk("NAND device: Manufacture ID:" \
" 0x%02x, Chip ID: 0x%02x (%s)\n",
nand_maf_id, nand_dev_id, mtd->name);
Could not found stored vivi parameters. Use default vivi parameters.
vivi参数
vivi_parameter_t default_vivi_parameters[] = {
{ "mach_type", MACH_TYPE, NULL },
{ "media_type", MT_S3C2410, NULL },
{ "boot_mem_base", 0x30000000, NULL },
{ "baudrate", UART_BAUD_RATE, NULL },
{ "xmodem_one_nak", 0, NULL },
{ "xmodem_initial_timeout", 300000, NULL },
{ "xmodem_timeout", 1000000, NULL },
{ "ymodem_initial_timeout", 1500000, NULL },
{ "boot_delay", 0x1000000, NULL }
};
#define MACH_TYPE 193
#define MT_S3C2410 MT_SMC_S3C2410
#define UART_BAUD_RATE 115200
源代码:vivi/lib/priv_data.c
int init_priv_data(void)
{
int ret_def;
#ifdef CONFIG_PARSE_PRIV_DATA //#define CONFIG_PARSE_PRIV_DATA 1
int ret_saved;
#endif
ret_def = get_default_priv_data();
#ifdef CONFIG_PARSE_PRIV_DATA
ret_saved = load_saved_priv_data();
if (ret_def && ret_saved) {
printk("Could not found vivi parameters.\n");
return -1;
} else if (ret_saved && !ret_def) {
printk("Could not found stored vivi parameters.");
printk(" Use default vivi parameters.\n");
} else {
printk("Found saved vivi parameters.\n");
}
#else
if (ret_def) {
printk("Could not found vivi parameters\n");
return -1;
} else {
printk("Found default vivi parameters\n");
}
#endif
#ifdef CONFIG_DEBUG_VIVI_PRIV
display_param_tlb();
display_mtd_partition();
#endif
return 0;
Press Return to start the LINUX now, any other key for vivi
按回车进入linux,其它键进vivi。当然,从vivi中boot也可以。
源代码:vivi/main.c
void boot_or_vivi(void)
{
char c;
int ret;
ulong boot_delay;
boot_delay = get_param_value("boot_delay", &ret);
if (ret) boot_delay = DEFAULT_BOOT_DELAY;
/* If a value of boot_delay is zero,
* unconditionally call vivi shell */
if (boot_delay == 0) vivi_shell();
/*
* wait for a keystroke (or a button press if you want.)
*/
printk("Press Return to start the LINUX now, any other key for vivi\n");
c = awaitkey(boot_delay, NULL);
if (((c != '\r') && (c != '\n') && (c != '\0'))) {
printk("type \"help\" for help.\n");
vivi_shell();
} // 有问题,'\r' '\n'都是回车,'\0'是什么?
run_autoboot();
return;
}【注:这里还有个小问题。就是在这里添加的时候,\n\r要合起来用,不能只用\n。原因如下:
计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。
于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。
这就是“换行”和“回车”的来历,从它们的英语名字上也可以看出一二。
后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。Unix 系统里,每行结尾只有“<换行>”,即“\n”;Windows系统里面,每行结尾是“<换行><回车>”,即“\ n\r”;Mac系统里,每行结尾是“<回车>”。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
这几个地方我都遇到过,不过一直没有搞清楚。现在才算是找到根源了。】
Copy linux kernel from 0x00030000 to 0x30008000, size = 0x00100000 ... done
启 动linux kernel,kernel映像必须被放到MTD设备的一个分区中,from、size分别表示linux kernel起始地址和kernel的大小。为什么要指定kernel大小呢?因为kernel首先要被copy到boot_mem_base + 0x8000的地方,然后在boot_mem_base + 0x100开始的地方设置内核启动参数。要拷贝 kernel,当然需要知道kernel的大小啦,这个大小不一定非要和kernel实际大小一样,但是必须大于等于kernel的大小。(单位字节)
media_type是指定的媒介类型,因为boot命令对不同媒介的处理方式是不同的,例如如果kernel在 SDRAM中,那么boot执行的过程中就可以跳过拷贝kernel映像到SDRAM中这一步骤了
Boot命令识别的媒介类型有以下三种:
ram 表示从RAM中启动linux kernel,linux kernel必须要放在RAM中,我的S3C2410平台就是如此。
nor 表示从NOR Flash中启动linux kernel,linux kernel必须已经被烧写到了NOR Flash中
smc 表示从NAND Flash中启动linux kernel,linux kernel必须已经被烧写到了NAND Flash中
源代码:vivi/lib/boot-kernel.c
int boot_kernel(ulong from, size_t size, int media_type)
{
int ret;
ulong boot_mem_base; /* base address of bootable memory */
ulong to;
boot_mem_base = get_param_value("boot_mem_base", &ret);
if (ret) {
printk("Can't get base address of bootable memory\n");
printk("Get default DRAM address. (0x%08lx\n", DRAM_BASE);
boot_mem_base = DRAM_BASE;
} //boot_mem_base = 0x30000000
/* copy kerne image */
to = boot_mem_base + LINUX_KERNEL_OFFSET;
// #define LINUX_KERNEL_OFFSET 0x8000
// to = 0x30008000
printk("Copy linux kernel from 0x%08lx to 0x%08lx, size = 0x%08lx ... ",from, to, size);
ret = copy_kernel_img(to, (char *)from, size, media_type);
// 将内核镜像从NAND FLASH拷入RAM
// media_type=MT_SMC_S3C2410
if (ret) {
printk("failed\n");
return -1;
} else {
printk("done\n");
}
return 0;
}
void command_boot(int argc, const char **argv)
{
int media_type = 0;
ulong from = 0;
size_t size = 0;
mtd_partition_t *kernel_part;
int ret;
media_type = get_param_value("media_type", &ret);
if (ret) {
printk("Can't get default 'media_type'\n");
return;
}
kernel_part = get_mtd_partition("kernel");
if (kernel_part == NULL) {
printk("Can't find default 'kernel' partition\n");
return;
}
from = kernel_part->offset;
size = kernel_part->size;
boot_kernel(from, size, media_type);
}
user_command_t boot_cmd = {
"boot",
command_boot,
NULL,
"boot [{cmds}] \t\t\t-- Booting linux kernel"
};
zImage magic = 0x016f2818
判断内核文件是否为压缩镜像,而当前文件是压缩镜像
源文件: vivi/lib/boot-kernel.c
#define LINUX_ZIMAGE_MAGIC 0x016f2818
if (*(ulong *)(to + 9*4) != LINUX_ZIMAGE_MAGIC) {
printk("Warning: this binary is not compressed linux kernel image\n");
printk("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
} else {
printk("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
}
Setup linux parameters at 0x30000100
linux command line is: "noinitrd root=/dev/bon/3 init=/linuxrc console=ttyS0"
设置linux参数和控制行
static void setup_linux_param(ulong param_base)
{
/*linux parameters*/
struct param_struct *params = (struct param_struct *)param_base;
char *linux_cmd;
printk("Setup linux parameters at 0x%08lx\n", param_base);
memset(params, 0, sizeof(struct param_struct));
params->u1.s.page_size = LINUX_PAGE_SIZE;
params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT);
/* set linux command line */
linux_cmd = get_linux_cmd_line();//linux_cmd = 0x33DF8008
if (linux_cmd == NULL) {
printk("Wrong magic: could not found linux command line\n");
} else {
memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
printk("linux command line is: \"%s\"\n", linux_cmd);
}
}
//#define boot_mem_base 0x30000000
//#define LINUX_PARAM_OFFSET 0x100
setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET);
MACH_TYPE = 193
/* Get machine type */
mach_type = get_param_value("mach_type", &ret);
printk("MACH_TYPE = %d\n", mach_type);
NOW, Booting Linux......
激动人心的消息,终于可以引导linux了。。。
源代码: vivi/lib/boot-kernel.c
printk("NOW, Booting Linux......\n");
call_linux(0, mach_type, to);
#elif defined(CONFIG_ARCH_S3C2410)
void call_linux(long a0, long a1, long a2)
{
cache_clean_invalidate();
tlb_invalidate();
__asm__(
"mov r0, %0\n"
"mov r1, %1\n"
"mov r2, %2\n"
"mov ip, #0\n"
"mcr p15, 0, ip, c13, c0, 0\n" /* zero PID */
"mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */
"mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */
"mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */
"mrc p15, 0, ip, c1, c0, 0\n" /* get control register */
"bic ip, ip, #0x0001\n" /* disable MMU */
"mcr p15, 0, ip, c1, c0, 0\n" /* write control register */
"mov pc, r2\n"
"nop\n"
"nop\n"
: /* no outpus */
: "r" (a0), "r" (a1), "r" (a2)
);
//全是汇编,有空再研究
Boot Loader 调用 Linux 内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000 地址处。在跳转时,下列条件要满足:
1. CPU 寄存器的设置:
R0=0;
R1=机器类型 ID;关于 Machine Type Number,可以参见 linux/arch/arm/tools/mach-types。
R2=启动参数标记列表在 RAM 中起始基地址;
2. CPU 模式:
必须禁止中断(IRQs和FIQs);
CPU 必须 SVC 模式;
3. Cache 和 MMU 的设置:
MMU 必须关闭;
指令 Cache 可以打开也可以关闭;
数据 Cache 必须关闭;