http://www.felixwoo.com/wp-content/uploads/attachments/200510/25_095534_net_software.jpg
1、符合标准的基本页面,语义化的结构和内容,分离的表现形式;
  此处争议由来已久,也不多说,首先,一切的一切,信息(一般看来包括文本、图片、音频、视频……)是最基本的,以及其所相关的元数据信息。

  页面的标准化本身并非对纯粹技术的盲目追求,更重要的在于对语义理想孜孜不倦的追求,只有将表现形式予以剥离,才能将真正“有效”的数据独立出来,有利于机器的处理,有利于对信息的检索和获取。
  进一步而言,内容和表现形式的分离,也体现了对部分弱势群体的关怀,比如使用纯文本浏览器(因为OS或者手机等原因),比如残障人士。
  最后一个最次的原因,才是可以减轻设计人员的劳动量,同时划清程序人员和设计人员的界限,加强劳动分工,提高专业效率,等等更加技术型的考量。
  因此,在对信息的处理中,首先使用语义化的结构页面将信息标记,然后使用样式构建各种各样的视觉形式。

  在传统的网页中,因为语义的限制,页面主要是用来传达多媒体信息,也由此造就了网络媒体化的错觉,现在,语义华的结构和内容给与机器进一步切入的机会,使得机器有机会以自己的方式检索、处理和交互数据,这一转变,是促使网站转变为网络软件的第一步,也是最基本的一步。

2、B/S的动态交互,以Browser为出发点的Ajax异步交互;
  有了结构化的文档(XHTML+DOM),Javascript才能对页面的元素像一个真正的程序那样加以控制;有了语义化的内容(XML),Javascript才能以程序的身份处理信息。这终于使我们可以构建足够强大的人机交互界面,而不再困扰与每一个交互都需要基于页面的提交和跳转,这是这个简单技术所带来的重要结果。
  实际上作为既成事实上的平台,Browser可以做更多的事情,Java、Flash都可以支持非常丰富的应用,只是目前JS最为普及,功能益眼下的需求也基本适用。

3、具备社会属性的Tag,以及不适于此的Catalog;
  Tag之所以如此重要,在我看来是因为它几乎可以视为整个网络软件目前最为核心的“网络化”(也可以称之为“社会性”,但是何必那么复杂地绕弯)的应用。
  此题另外撰文说明,此处不议,说一点想法:其实Tag才是最为“社会化”的事物,而Catalog才是最为“个人化”的事物,Tag是特殊而不是个性,是碎片状的经验,是每个人的第一反应都差不多的常识,而Catalog才是个性而不是特殊,是面向个人最为使用的定制套子。

4、API开始的应用层。

  如果你是一个菜鸟或者初学者,不妨直接跳到第(4)节,看看我提供的“应用程序能否工作于CMWAP的辨别方法”;如果你希望对CMWAP和CMNET有一个详细的了解,那么就读完全篇吧;如果你对这个问题有所研究的话,还请不吝赐教,解开仍旧困惑我的几个疑问。

(1) 为什么会有两个接入点?

  在网上查阅大量资料后并经过反复的尝试与探索后,我大致对中国移动提供的这两种接入方式有了初步了解。
  在国际上,通常只有一种GPRS接入方式,为什么在中国会有CMWAP和CMNET两兄弟呢?(彩信之所以单独配置接入点是因为彩信服务需要连接专用的服务器,在这里不作探讨。)
  其实,CMWAP 和 CMNET只是中国移动人为划分的两个GPRS接入方式。前者是为手机WAP上网而设立的,后者则主要是为PC、笔记本电脑、PDA等利用GPRS上网服务。它们在实现方式上并没有任何差别,但因为定位不同,所以和CMNET相比,CMWAP便有了部分限制,资费上也存在差别。

(2) 什么是WAP?

  WAP只是一种GPRS应用模式,它与GRPS的接入方式是无关的。WAP应用采用的实现方式是“终端+WAP网关+WAP服务器”的模式,不同于一般Internet的“终端+服务器”的工作模式。主要的目的是通过WAP网关完成WAP-WEB的协议转换以达到节省网络流量和兼容现有WEB应用的目的。
  WAP网关从技术的角度讲,只是一个提供代理服务的主机,它不一定由网络运营商提供。但据我所知,中国移动GPRS网络目前只有唯一的一个WAP网关:10.0.0.172,有中国移动提供,用于WAP浏览(HTTP)服务。有一点需要注意,WAP网关和一般意义上的局域网网关是有差别的,标准的WAP网关仅仅实现了HTTP代理的功能,并未完成路由、NAT等局域网网关的功能。这就决定了它在应用上所受到的限制。

(3) 中国移动对CMWAP的限制

  为了从应用中区别两者的定位,中国移动对CMWAP作了一定的限制,主要表现在CMWAP接入时只能访问GPRS网络内的IP(10.*.*.*),而无法通过路由访问Internet。(少数地区的移动网络可能不存在这一限制。)我们用CMWAP浏览Internet上的网页就是通过WAP网关协议或它提供的HTTP代理服务实现的。
  说到这里,就让我自然而然的联想到我们公司的网络,相信不少工作的朋友都有类似的体会。公司的网络在网关上不提供路由和NAT,仅仅提供一个可以访问外网的HTTP代理。这样,我们就无法直接使用QQ、MSN等非HTTP协议的应用软件了(好在它们还提供的有HTTP代理的连接方式)。CMWAP也正是

(4) 适用范围

  适用范围才是大家最关心的问题。CMNET拥有完全的Internet访问权,这里就不多说了,主要让我们来看看CMWAP。因为有了上面提到的限制,CMWAP的适用范围就要看WAP网关所提供的支持了。目前,中国移动的WAP网关对外只提供HTTP代理协议(80和8080端口)和WAP网关协议(9201端口)。(据有的网友提到1080端口也是开放的,但无法连接。这也许是移动内部使用的一个Socks后门吧^_^)。

  因此,只有满足以下两个条件的应用才能在中国移动的CMWAP接入方式下正常工作:
  1. 应用程序的网络请求基于HTTP协议。
  2. 应用程序支持HTTP代理协议或WAP网关协议。

如何辨别一个应用程序的网络请求是否基于HTTP协议?

  这个问题还真不好回答,要完全做到这一点需要通过拦截应用程序的通信数据包进行分析。这里提供几个简单的方法给广大菜鸟朋友:从表现上看,如果它的网络请求是网址(URL)的形式,那么通常是基于HTTP协议的,如Web浏览器;如果它连接的服务器端口是80,那么它可能是基于HTTP协议的。如果确实无法准确判断,那么请直接看下一个条件。(满足第二个条件的应用一定是基于HTTP协议的)

如何区别一个应用程序支持HTTP代理协议还是WAP网关协议呢?

  首先看它的设置中有没有代理服务器的选项(通常在S60上未特别说明的代理都是特指HTTP代理),如果有则表示它支持HTTP代理协议。如果没有,则需要按照以下步骤测试:
  在GPRS接入点设置的高级设置里去掉代理服务器的设置项:Server Address 和 Server Port(,如果应用程序可以正常工作,那么它是基于WAP网关协议,如Java程序、S60内置的浏览器。如果在此状态下不能正常工作,而恢复GPRS接入点高级设置中的代理服务器设置后能够正常工作,则应用程序支持HTTP代理协议(代理设置从系统中读取)。如果仍不能正常工作,那么这个应用程序一般来说是不支持HTTP代理协议或WAP网关协议的。

  这里需要特别说明的是JavaQQ,它有Socket和HTTP两种版本。现在网上流传的可用于CMWAP的JavaQQ就是基于HTTP协议的。就拿那个JavaQQ 2004来说,启动画面中就明确的写着“KJava QQ HTTP”。而SIS版的QQ和AgileMessenger(S60的MSN客户端)因为是采用的普通的Socket连接方式,因此无法用于CMWAP。
总结一下CMWAP下可以使用的常见软件的工作方式:

  (1) 手机内置的浏览器:WAP网关协议
  (2) Opera 浏览器:HTTP代理协议(有代理设置)
  (3) Java 程序:WAP网关协议
  (4) AvantGo:HTTP代理协议(有代理设置)

摘要
本文讨论了如何使用Windows Installer技术发布.NET程序,以及如何使用native代码判断目标机器上是否安装有.NET Framework; 如果没有,将自动安装.NET Framework然后安装作者自己的.NET程序。

--------------------------------------------------------------------------------

目录

本文内容

制作自己的安装程序

发布.NET Framework

制作自己的native安装程序

使用方法

总结

作者

本文内容

 
1. 使用VS.NET来制作安装程序。

2. 如何把.NET Framework 部署到目标机器。

3. 如何使用Native代码把.NET Framework和自己的安装程序制作为一个统一的安装程序。该程序做到如果目标机器上没有.NET Framework,将自动安装.NET Framework然后再自动安装作者自己的程序。

 
制作自己的安装程序

 
在VS.NET中,我们可以通过建立"Setup and Deployment Projects"项目,非常灵活方便的把自己的.NET程序制作为Windows Installer文件。比如,我们可以很方便的定制下面这些选项:
1. 是否在桌面上放置快捷方式。
2. 注册自己的文件类型,可以通过双击该文件来使用自己的程序打开。
3. 注册表的处理
在下面的MSDN站点,我们可以获得在VS.NET中通过Setup and Deployment Projects来制作自己的安装程序的示例:
http://msdn.microsoft.com/library/en-us/vsintro7/html/vbconDeploymentScenarios.asp

--------------------------------------------------------------------------------

发布.NET Framework
 
.NET Framework 1.0提供一个用来重新部署.NET的exe文件:Dotnetfx.exe. 它包含了Common Language Runtime和其它.NET程序运行时必不可少的内容。
我们可以从下面的站点下载该exe文件:
http://msdn.microsoft.com/downloads/sample.asp?url=/MSDN-FILES/027/001/829/msdncompositedoc.xml
同时,我们也可以在VS.NET安装CD或者DVD中找到该文件。
我们可以通过多种方式来通过运行Dotnetfx.exe把.NET Framework部署到目标机器上:
1. 通过Microsoft Systems Management Server部署。
2. 通过Active Directory部署。
3. 使用第三方工具。
具体的信息,我们可以参阅下面的文章:
http://msdn.microsoft.com/library/en-us/dnnetdep/html/redistdeploy.asp

--------------------------------------------------------------------------------

制作自己的native安装程序
 
如果我们要把自己的.NET程序发布到目标机器上,同时我们不确定该目标机器是否已经安装了.NET Framework, 那我们就需要自己设计一段unmanaged代码,来判断目标机器是否安装了.NET Framework, 如果没有,则运行Dotnetfx.exe安装.NET Framework, 然后利用Windows Installer安装自己的程序。
在MSDN的下面网页上,我们可以获得一个使用unmanaged C++实现的安装程序和它的源代码:
http://msdn.microsoft.com/downloads/default.asp?URL=/code/sample.asp?url=/msdn-files/027/001/830/msdncompositedoc.xml
1. 在CSettings class中,通过读取"settings.ini",获得您自己的MSI安装文件和dotnetfx.exe的路径,以及其他您自己的设置。(比如.NET Framework的语言版本)
GetCaptionText(void)
GetDialogText(void)
GetErrorCaptionText(void)
GetIniName(void)
GetProductName(void)
 
Parse()函数用来解析settings.ini文件。
2. 在Main.cpp文件中,全局函数FxInstallRequired()判断是否要在目标机器上安装.NET Framework. FxInstallRequired()会检测下面的注册表键值和dotnetfx.exe的版本和语言设置。
HKLM\SOFTWARE\Microsoft\.NETFramework\policy\v1.0
3.如果需要安装.NET Framework, 在全局函数ExecCmd()中调用下面的命令silent安装dotnetfx.exe:
dotnetfx.exe /q:a /c:"install /l /q"
4。 在ExecCmd()全局函数中调用下面的命令安装您自己的MSI文件:
msiexec /i <your MSI file> REBOOT=ReallySuppress

--------------------------------------------------------------------------------

使用方法
 
如果我们使用上面的native代码作为自己的安装程序,那么我们可以通过下面的步骤把自己的.NET程序和它结合在一起:
1.把您自己的.NET程序制作成为Windows Installer (.MSI)文件。
2.打开"settings.ini"文件,在"Msi" key中设置您自己的.NET程序的MSI文件路径和文件名;在"FxInstallerPath" Key中,设置dotnetfx.exe的路径。具体的参数信息,您可以从下面文章中得到:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetdep/html/redistdeploy.asp
3. 把"setup.exe", "settings.ini", "dotnetfx.exe"和您自己的MSI安装文件,发送到目标机器上,然后运行"setup.exe", 安装程序会自动检测是否有.NET Framwork, 如果没有,将首先运行dotnetfx.exe。
通过上述步骤,您可以将自己的.NET程序成功的部署到没有安装.NET Framwork环境的机器上。

--------------------------------------------------------------------------------

总结
 
通过上述步骤,您可以将自己的.NET程序成功的部署到没有安装.NET Framwork环境的机器上。

--------------------------------------------------------------------------------

作者
 
张广辉
2002年10月22日

1.引言
  液晶显示器(LCD)具有显示信息丰富、功耗低、体积小、重量轻、超薄等许多其他显示器无法比拟的优点,近几年来被广泛用于单片机控制的智能仪器、仪表和低功耗电子产品中。LCD可分为段位式LCD、字符式LCD和点阵式LCD。其中,段位式LCD和字符式LCD只能用于字符和数字的简单显示,不能满足图形曲线和汉字显示的要求;而点阵式LCD不仅可以显示字符、数字,还可以显示各种图形、曲线及汉字,并且可以实现屏幕上下左右滚动、动画、分区开窗口、反转、闪烁等功能,用途十分广泛。本文介绍点阵式液晶显示器HS12232-1、HS12232-9与单片机的接口及编程的方法,同时给出两种显示器常用的字符显示和汉字显示程序。

2.不带汉字库的HS12232-1下的汉字显示
  2.1 显示原理
  利用PC上的16×16点阵汉字库,提取后将点阵文件存入ROM,直接利用PC中汉字内码作为单片机系统的编码(不再形成新的汉字编码)。

  2.2 硬件组成
  首先要采用点阵图形液晶显示器。(例如一行为240点,总列数为128点,可以容纳16×16点阵的汉字15个[因为240/16=15],128列可以显示8行[因为128/16=8]),广州市千喜科技开发有限公司的HS12232-1内置SED1520驱动控制器的点阵为122×32点,每行7个半汉字,共2行。如果要扩展存储器,全部国标16×16点阵汉字、8×16点阵的ASCII码点阵数据及汉字语句编码数据要使用1片512KB的存储器来存储,多出部分可以留作数据存储器。本文只探讨液晶显示器与单片机的接口部分,扩展存储器部分略。下面给出HS-12232-1与单片机8031的一种接口,如图1所示(VDD=+5V)。

  2.3 汉字显示原理
  国家标准信息交换用汉字字符集GB 2312-80共收录了汉字、图形符号等共7445个,其中汉字6763个,按照汉字使用的频度分为两级,其中一级汉字3755个,二级汉字3008个。汉字、图形符号根据其位置将其分为94个“区”,每个区包含94个汉字字符,每个汉字字符又称为“位”。其中“区”的序号由01区至94区,“位”的序号也由01位至94位。若以横向表示“位”号,纵向表示“区” 号,则“区”和“位”构成一个二维坐标。给定一个“区”值和“位”值就可以确定一个惟一的汉字或图形符号。即4位阿拉伯数字就可以惟一地确定一个汉字或符号。如“北”字的区位码是“1717”,而京字的区位码是“3009”。前两位是“区”号,后两位是“位”号。其中1至15区是各种图形符号、制表符和一些主要国家的语言字母,16区至87区是汉字,其中16区至55区是一级汉字,56至87区是二级汉字。

  UCDOS软件中的文件HZK16和文件ASC16分别为16×16的国际汉字点阵文件和8×16的ASCII码点阵文件,HZK16中按汉字区位码从小到大依次存放国标区位码表中的所有汉字,每个汉字占用32字节,每个区为94个汉字。而asc16文件中按ascii码从小到大依次存有8×16的ASCII码点阵,每个ASCII码占用16字节。

  PC的文本文件中,汉字是用机内码的形式存储的,每个汉字占2字节,其中第一个字节为机内码的区码,汉字机内码的区码范围是从0A1H(十六进制)开始,对应区位码中区码的第一区;而机内码的第二个字节为机内码的位码,范围也是从0A1H(十六进制)开始,对应某区中的第一个位码。就是说将汉字机内码减去0A0AH就得到该汉字的区位码。例如汉字“北”的机内码是十六进制的“B1B1”,其中前两位“B1”表示机内码的区码,后两位“B1”表示机内码的位码。所以“北”的区位码为0B1B1H-0A0A0H=1111H,将区码和位码分别转换为十进制,得汉字“北”的区位码为“1717”。即“北”的点阵位于第17区的第17个字的位置,在文件HZK16中的位置为第32×[(17-1)×94+(17-1)]=48640D以后的32个字节为“北”的显示点阵。用RF-1800编程器读入二进制文件hzk16j.bin后利用其编辑功能中的缓冲区编辑查找到BE00 H (48640D是十进制,将其转变为十六进制后得BE00 H)开始的32个字节:04 80 04 80 04 88 04 98 04 A0 7C C0 04 80 04 80 04 80 04 80 04 80 04 80 1C 82 E4 82 44 7E 00 00(以上全为下十六进制),将其写在16×16点阵方格纸上,即得图2。由此可以理解其相互逻辑关系。

  在单片机系统中,连续取32个字节送到LCD的相应位置,就能正确显示汉字后的图形符号。从HS-12232-1使用的SED1520的控制原理得知,字模送显示前要旋转90°,例如“逢”的区位码是3778,在HZK16中的位置为第32*[(37-1)*94+(78-1)]=110752D以后的32个字节:04 44 FF FE 05 40 41 F8 33 10 14 E0 01 18 F6 46 1B F8 10 40 13 F8 10 40 17 FC 10 40 28 46 47 FC , 旋转90°后上16个字节:82 8A 92 B2 02 A7 92 5E 2A AF 2A 5A 4A 83 82 00,下16个字节;00 80 40 3F 04 90 95 95 95 FF 95 95 95 D0 40 00。

  2.4 ASCII码的显示原理
  ASCII码的显示与汉字的显示基本原理相同,在ASC16文件中不存在机内码的问题,其显示点阵直接按ASCII码从小到大依次排列,不过每个ASCII码在文本文件中只占1个字节并且小于80 H, 每个ASCII码为8×16点阵,即在ASCII16文件中,每个ASCII码的点阵也只占16个字节。

3.带汉字库的HS12232-9下的汉字显示
  广州市千喜科技开发有限公司阵的HS12232-9内置ST7920A驱动控制器,点阵为122×32点,每行7个半汉字,共2行。内部字型ROM 包括8192个16×16点阵的中文字型和126个16×8点阵的字母符号字型,另外还提供一个64×256点的绘图区域(GDRAM)及240点的ICON RAM,可以和文字画面混合显示。内含的CGRAM有4组可编程的1616点阵的造字功能。与单片机的接口有8位并行、4位并行、2/3线串行。它采用低功率电源消耗,电压范围2.7~5.5V,功能齐全,汉字、点阵图形、ASCII码、曲线同屏显示;上下左右移动当前显示屏幕、清屏、光标显示、闪烁、睡眠、唤醒、关闭显示功能齐备,适合许多场合应用。上面使用了并行方式,现在研究串行方式。

  3.1 串行时序与硬件接口电路
  HS12232-9与单片机的串行接口时序如图3所示,即24个时钟脉冲传送一个字节。单片机首先发送数据传输起始位5个“1”,HS12232-9收到连续的5个“1”,内部传输被重置,同时串行传输被同步,紧接着RW位用于决定数据的传输方向(读还是写),RS用来选择是内部数据寄存器还是指令寄存器,第8位固定为“0”。收到起始位、RW、RS、“0”组成的第一个字节后,一个字节的数据或指令被分成两个字节来串行传送或接收。数据或指令的高4位被放在第二个字节串行数据的高4位,低4位补4个“0”,数据或指令的低4位被放在第三个字节串行数据的高4位,低4位补4个“0”,这样完成一个字节或数据的传送。HS12232-9内部没有发送/接收缓冲区,传送节奏要注意,否则要丢指令或数据。

  PIC16C5X单片机与HS12232-9接口电路如图4所示,只占用RA0、RA1两根I/O口,或者用16f877的RD6、RD7两根I/O口,RS(CS)引脚是片选,只有一片时固定接高电平使片选始终有效,SID引脚作数据线,SCLK引脚作时钟线。单片机通过SID数据线在SCLK同步时钟线配合下完成数据(指令)传输的任务。接口电路十分简单。

4.结束语
  利用自带字库的中文液晶图形点阵模块串口接口方式的优势比并口明显,节省I/O了口,不占用ROM,接口电路简单,解决了显示汉字
字符数量有限的瓶颈问题和小体积非总线结构单片机的汉字显示问题。笔者相信今后其使用率将逐步提高,对照本文的串并口两种方式,希望对广大同行在使用液晶图形点阵模块时有所启发和帮助。

  在具体介绍gba编程之前,我想先感谢一个人,他就是 水银 兄。在我学习的过程中水银兄给了我很多有用的宝贵资料,在此我向他表示感谢。

  GBA是新一代的32位手掌机,强大的机能吸引了无数玩家和编程爱好者。现在用于开发的编译器有两种,一种是完全免费的GCC,和收费的ARM SDT,目前我在使用的是gcc,虽然网上也有D过来的arm sdt但我是无福享用的,23MB的大家伙啊,当初下载gcc这个12MB的东东时我就已经吐血了,可怜我的小猫啊。

  好了,废话少说,进入正题。GBA使用的是卡带也就是只读rom为存储载体虽然容量可以很大,但问题也不少。也就是说我们不可能动态的分配使用卡上的内存了尽管它可以有128MB。当然gba也为我们提供了ram,至于容量嘛……,看下面的资料知道了。

单词含义:
1. GBA - 'Game Boy Advance' ^_^
2. BG(Backgroud) - '背景'
3. Sprite - '精灵'
4. Tile - '地图图块'
5. RAM(Random Access Memory) - '随机访问存储器'

接口地址 :

  外部 RAM: 地址: 0x02000000 大小: 256Kb 作用: 存放程序及数据
  内部 RAM: 地址: 0x03000000 大小: 32Kb? 作用: 我想是高速内存,和cache作用一样吧
  IO RAM: 地址: 0x04000000 大小: 1Kb 作用: 控制图像,声音,DMA等内存映射IO
  Palette - '调色板'
  地址: 0x05000000 大小: 0x400 bytes 作用: 存放调色板数据
  VRAM(Video RAM) - '视频内存'
  地址: 0x06000000 大小: 0x20000 bytes 作用: 位图模式下存放帧缓冲数据,图块模式下存放图块数据及图块地图数据
  OAM(Object Attribute Memory) - '精灵对象属性内存'
  地址: 0x07000000 大小: 0x400 bytes 作用: 用于控制精灵

6. ROM(Read Only Memory) - '只读存储器'
地址: 0x08000000 大小: 看你的游戏卡的大小罗 作用: 存放所有的程序和数据
7. ARM - 32bit 指令系统 (RISC,精简指令集)
8. Thumb - 16bit 指令系统 (具体区别详见 http://gbadev.org/files/armthumb-romram.txt)
9. DMA(Direct Memory Access) - 直接内存访问
10.DISPCNT(Display Controller) - 显示控制(内存地址)

  当初看水银兄写的gba教程时一直不明白明明是256色模式,可指向VRAM的指针却是short型的。后来才知道,gba访问VRAM时一次必须读写2个字节,即16bit。我晕~~!所以我只好严格要求自己的blit函数。虽然也找到一个可以写8bit的putpixel函数代码,但速度太慢了不适合用在显示大量图片的地方。

  需要注意的地方大概都说完了,我们可以进入代码部分了。

  虽然是专用游戏机,但显示模式也需要设定才行。

#define REG_DISPCNT *(u16*)0x04000000// 显示寄存器地址
#define VRAM 0x06000000 // 图像缓冲区地址
#define M5_VRAM 0x0600A000 // M5缓冲区地址
#define BACKBUFFER 0x010// 双缓冲/背缓冲地址
#define PALETTE 0x5000000// 调色板地址
#define MODE_3 0x03 // 240*160 15位/单缓冲区
#define MODE_4 0x04 // 240*160 8位/双缓冲区
#define MODE_5 0x05 // 160*128 15位/双缓冲区
#define BG2_ENABLE 0x0400 // BG_2
#define SetMode(Mode) REG_DISPCNT=(Mode) // 设置显示模式的宏定义

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;

typedef struct BITMAP
{
int x,y,w,h;
u16 MskCol;
float bitx,bity;
u8 flag;
u16 *dat;
}BITMAP;

BITMAP screen;

u16 *palette_mem = (u16 *)PALETTE;

void grp_init()
{
SetMode(MODE_4|BG2_ENABLE);
screen.w=240;
screen.h=160;
screen.dat=(u16 *)VRAM;
}

这样我们就进入了240*160*256的模式了, SetMode中的BG2_ENABLE这个参数不能少否则将没有任何显示。为了能够正常的显示图象还需要设置调色板。

void set_palette(unsigned short *palette)
{
int loop;
for(loop=0;loop<256;loop++)
{
palette_mem[loop] = palette[loop];
}
}

调色板数据可以从bmp2gba转换bmp图象后的.h中得到,需要注意的是bmp2gba不能够正确的转换过大的图象,大概是240*160以上的图片转换后都有问题,所以我自己写了一个pic2gba,可以转换256色的pcx,bmp(未压缩,转换前还需要进行一下旋转处理),gif格式的程序,但我不知道bmp2gba是如何转换调色板数据的,所以这部分数据是不正确的。

pic2gba使用方法:pic2gba in-file out-file。

水银兄的教程中是直接使用图象数据的,这对以后写game可不是一个好的方法,所以我才定义了BITMAP结构既然要显示图象就需要将图象数据装入BITMAP:

BITMAP load_bitmap(int width,int height,const unsigned char *dat)
{
BITMAP bitmap;
bitmap.dat=(u16*)dat;
bitmap.x=bitmap.y =0;
bitmap.w=width;
bitmap.h=height;
bitmap.MskCol=*bitmap.dat;
bitmap.flag=TRUE;
return bitmap;
}

然后再blit:

void blit(BITMAP dst,BITMAP src,int x1,int y1,int x,int y,int w,int h)
{
register int i=0,j;
register u16 *_bak1,*_bak2;
_bak1=dst.dat;
_bak2=src.dat;
_bak1+=x1+y1*(dst.w>>1);
_bak2+=x+y*(src.w>>1);

while(i<h)
{
memcpy(_bak1,_bak2,w);
_bak1+=120;
_bak2+=src.w>>1;
i++;
}
}

这个是最快的方法了。哦,对了还有更快的方法:直接使用DMA,可惜我这没资料。

如果是要显示汉字,除了水银兄的方法外(比较适合游戏中使用)也可以把整个汉字库都放到rom中去,用这个rhzk程序来转换数据。(需要UCDOS的HZK16,ASC16两个字库。)因为这里要写点,速度要求也比较低可以使用这个函数:

inline void putpixel(BITMAP bmp,int x,int y,u8 col)
{
u16 *tc;
tc=bmp.dat+(y)*(bmp.w>>1)+(x>>1);
if(x&1)*tc=((*tc&255)+(col<<8));
else
*tc=(*tc&65280)+col;
}

下面的是显示汉字的主体部分了,

struct ChinaHzk{
short len;
const u8 *CHzkBuf,*AHzkBuf;
char Loadhzk,HZK;
ChinaHzk();
}ChinaHzk;

ChinaHzk::ChinaHzk()
{
CHzkBuf=_CHzkBuf;
AHzkBuf=_AHzkBuf;//这两个指针在rhzk程序转换后的.h中定义
}

void hzput(int x,int y,int col,BITMAP bitmap1)
{
u8 dot;
register int i,j,k,mask;
if(ChinaHzk.HZK)
{
for(i=0;i<=15;i++)
{
for(j=0;j<=1;j++)
{
dot=*ChinaHzk.CHzkBuf++;
mask=0x80;
for(k=0;k<=7;k++)
{
if(dot&mask)
putpixel(bitmap1,x+j*8+k,y+i,col);
mask>>=1;
}
}
}
}
else
for(i=0;i<=15;i++)
{
dot=*ChinaHzk.AHzkBuf++;
mask=0x80;
for(k=0;k<=7;k++)
{
if(dot&mask)
putpixel(bitmap1,x+k,y+i,col);
mask>>=1;
}
}

ChinaHzk.HZK=1;
}

//显示字符
void textout(BITMAP bitmap1,char *Str,int x,int y,unsigned char col)
{
register unsigned long offset1;<
br /> int oldx;
const u8 *_bakCHzkBuf,*_bakAHzkBuf;

_bakCHzkBuf=ChinaHzk.CHzkBuf;
_bakAHzkBuf=ChinaHzk.AHzkBuf;
oldx=x;
while(*Str)
{
if(*Str=='\n') {y+=17;x=oldx;*Str++;continue;}
if(*Str==' ') {x+=8;*Str++;continue;}

if(!(*Str&0x80))
{
offset1=*Str<<4;
ChinaHzk.HZK=0;
ChinaHzk.AHzkBuf+=offset1;
hzput(x,y,col,bitmap1);
if(x+8+ChinaHzk.len<bitmap1.w)
{
x+=(8+ChinaHzk.len);
}
else
{
y+=17;x=oldx;
}
Str++;
ChinaHzk.HZK=1;
ChinaHzk.AHzkBuf=_bakAHzkBuf;
continue;
}

offset1=(unsigned int)((((*Str+95)<<6)+((*Str+95)<<5))-((*Str+95)<<1)+(*(Str+1)+95))<<5;
ChinaHzk.CHzkBuf+=(unsigned int)(offset1-778240);
hzput(x,y,col,bitmap1);
if(x+16+ChinaHzk.len<bitmap1.w)
{
x+=(16+ChinaHzk.len);
}
else
{
y+=17;x=oldx;
}
Str+=2;
ChinaHzk.CHzkBuf=_bakCHzkBuf;
}
}

//显示字符
void OutText(BITMAP bitmap,int x,int y,char *str,...)
{
va_list ptr;char *strr;
strr=(char*)malloc(strlen(str)+2);
va_start(ptr,str);
vsprintf(strr,str,ptr);
va_end(ptr);
textout(bitmap,strr,x,y,_COLOR);
free(strr);
}

你可以直接使用textout来显示也可以用OutText显示 变量-》字符 这样的信息,例:

OutText(screen,100,100,"X=%d,Y=%d",x,y);

有了这些基本函数,我想你已经可以写出一些东西来了,更多的资料请下载我写的函数库 GBA.ZIP
也许你看了这些会觉得有些眼熟,是的,如果你以前曾用过我写的游戏开发库Beauty的话,会发现基本上代码没有改变,而我当初也的确是有着样的想法, 即使在不同的平台上,不同的系统但使用一套相似的开发库进行开发可以为以后的移植工作省下很大力气。我在进行gba程序编写时就一直在使用Beauty库做测试相比之下要比用gba模拟器方便的多。

Abstract
1 LCD Module\Driver\Controller
2 Linux Frame Buffer Driver
2.1 Why Frame Buffer?
2.2 What is Frame Buffer Devices?
2.3 How to Write Frame Buffer Device Drivers?
3 Analysis of Linux Frame Buffer Driver Source Codes
3.1 fb.h
3.2 fbmem.c
4 Skeleton of LCD controller rivers
4.1 Allocate a system memory as video memory
4.2 Implement the fb_ops functions
Reference

Abstract
This material discusses how to write a Linux frame buffer LCD device driver.

1 LCD Module\Driver\Controller

Besides the datasheet of LCD devices, there are two quite good books (.pdf format) on LCD technology. Both of them are written in Chinese. One is “液晶显示技术”, and the other is “液晶显示器件”. The two books give almost all needed LCD knowledge, including introductions to hardware implementation of LCD devices and low level software programming used to operate LCD devices. This helps LCD circuits design and low level LCD programming.

2 Linux Frame Buffer Driver

2.1 Why Frame Buffer?
If GUIs (Graphic User Interface) such as MiniGUI, MicroWindows are used, LCD device drivers must be implemented as Linux frame buffer device drivers in addition to those low level operations which only deal with commands provided by LCD controllers.

2.2 What is Frame Buffer Devices?
The frame buffer device provides an abstraction for the graphics hardware. It represents the frame buffer of some video hardware and allows application software to access the graphics hardware through a well-defined interface, so the software doesn't need to know anything about the low-level (hardware register) stuff.
The device is accessed through special device nodes, usually located in the /dev directory, i.e. /dev/fb*.
More description about frame buffer device can be found in two txt files: linux /Documentation /fb /framebuffer.txt and linux /Documentation /fb /interal.txt

2.3 How to Write Frame Buffer Device Drivers?
There are few instructive materials about writing frame buffer device drivers. You may find “Linux Frame buffer Driver Writing HOWTO” at this web page http: //linux-fbdev.sourceforge.net /HOWTO /index.html, but the HOWTO is somewhat curt and not enough. So having a look into Linux source codes is necessary. The next section is an analysis of the related source codes.

3 Analysis of Linux Frame Buffer Driver Source Codes

Linux implements Frame Buffer Drivers mainly based on the groundwork of these two files:
1) linux/include/linux/fb.h
2) linux/drivers/video/fbmem.c
It’s time to have a close look at them.

3.1 fb.h
Almost all important structures are defined in this file. First let me describe what each means and how they are used one by one.

1) fb_var_screeninfo
It is used to describe the features of a video card you normally can set. With fb_var_screeninfo, you can define such things as depth and the resolution you want.

struct fb_var_screeninfo {
__u32 xres; /* visible resolution */
__u32 yres;
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */

__u32 bits_per_pixel; /* guess what */
__u32 grayscale; /* != 0 Graylevels instead of colors */

struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */

__u32 nonstd; /* != 0 Non standard pixel format */

__u32 activate; /* see FB_ACTIVATE_* */

__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */

__u32 accel_flags; /* acceleration flags (hints) */

/* Timing: All values in pixclocks, except pixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 reserved[6]; /* Reserved for future compatibility */
};

2) fb_fix_screeninfon
It defines the properties of a card that are created when you set a mode and can't be changed otherwise. A good example is the start address of the framebuffer memory. This can depend on what mode is set. Now while using that mode, you don't want to have the memory position change on you. In this case, the video hardware tells you the memory location and you have no say about it.

struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem */
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */
unsigned long mmio_start; /* Start of Memory Mapped I/O */
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Type of acceleration available */
__u16 reserved[3]; /* Reserved for future compatibility */
};

3) fb_cmap
Device independent colormap information. You can get and set the colormap using the FBIOGETCMAP and FBIOPUTCMAP ioctls.

struct fb_cmap {
__u32 start; /* First entry */
__u32 len; /* Number of entries */
__u16 *red; /* Red values */
__u16 *green;
__u16 *blue;
__u16 *transp; /* transparency, can be NULL */
};

4) fb_info
It defines the current state of the video card. fb_info is only visible from the kernel. Inside of fb_info, there exist a fb_ops which is a collection of needed functions to make driver work.

struct fb_info {
char modename[40]; /* default video mode */
kdev_t node;
int flags;
int open; /* Has this been open already ? */
#define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
struct fb_monspecs monspecs; /* Current Monitor specs */
struct fb_cmap cmap; /* Current cmap */
struct fb_ops *fbops;
char *screen_base; /* Virtual address */
struct display *disp; /* initial display variable */
struct vc_data *display_fg; /* Console visible on this display */
char fontname[40]; /* default font name */
devfs_handle_t devfs_handle; /* Devfs handle for new name */
devfs_handle_t devfs_lhandle; /* Devfs handle for compat. symlink */
int (*changevar)(int); /* tell console var has changed */
int (*switch_con)(int, struct fb_info*);
/* tell fb to switch consoles */
int (*updatevar)(int, struct fb_info*);
/* tell fb to update the vars */
void (*blank)(int, struct fb_info*); /* tell fb to (un)blank the screen */
/* arg = 0: unblank */
/* arg > 0: VESA level (arg-1) */
void *pseudo_palette; /* Fake palette of 16 colors and
the cursor's color for non
palette mode */
/* From here on everything is device dependent */
void *par;
};

5) struct fb_ops <
br />User application program can use ioctl() system call to operate low LCD hardware. Methods defined in fb_ops structure are used to support these operations.

struct fb_ops {
/* open/release and usage marking */
struct module *owner;
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user);
/* get non settable parameters */
int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con,
struct fb_info *info);
/* get settable parameters */
int (*fb_get_var)(struct fb_var_screeninfo *var, int con,
struct fb_info *info);
/* set settable parameters */
int (*fb_set_var)(struct fb_var_screeninfo *var, int con,
struct fb_info *info);
/* get colormap */
int (*fb_get_cmap)(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info);
/* set colormap */
int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con,
struct fb_info *info);
/* pan display (optional) */
int (*fb_pan_display)(struct fb_var_screeninfo *var, int con,
struct fb_info *info);
/* perform fb specific ioctl (optional) */
int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg, int con, struct fb_info *info);
/* perform fb specific mmap */
int (*fb_mmap)(struct fb_info *info, struct file *file, struct vm_area_struct *vma);
/* switch to/from raster image mode */
int (*fb_rasterimg)(struct fb_info *info, int start);
};

6) structure map
struct fb_info_gen | struct fb_info | fb_var_screeninfo
| | fb_fix_screeninfo
| | fb_cmap
| | modename[40]
| | fb_ops ---|--->ops on var
| | ... | fb_open
| | | fb_release
| | | fb_ioctl
| | | fb_mmap
| struct fbgen_hwswitch -|-> detect
| | encode_fix
| | encode_var
| | decode_fix
| | decode_var
| | get_var
| | set_var
| | getcolreg
| | setcolreg
| | pan_display
| | blank
| | set_disp

[编排有点困难,第一行的第一条竖线和下面的第一列竖线对齐,第一行的第二条竖线和下面的第二列竖线对齐就可以了]
struct fbgen_hwswitch is an abstraction of hardware operations. It is not necessary, but sometimes useful.

3.2 fbmem.c
fbmem.c is at the middle place of frame buffer driver architecture. It provides system calls to support upper user application programs. It also provides an interface to low level drivers for specific hardware. Those low level frame buffer drivers can register themselves into the kernel by this interface. fbmem.c implements the common codes used by all drivers, thus avoids repeated work.

1) Global Varibles

struct fb_info *registered_fb[FB_MAX];
int num_registered_fb;
The two are used to record on-using instance of fb_info structure. fb_info represents the current state of video card. All fb_info structures are globally placed in an array. When a frame buffer registers itself to kernel, a new fb_info is added to this array and num_registered_fb increases 1.

static struct {
const char *name;
int (*init)(void);
int (*setup)(void);
} fb_drivers[] __initdata= { ....};
If frame buffer driver is static linked into kernel, a new entry must be added to this table. If use insmod/rmmod, don’t care this.

static struct file_operations fb_ops ={
owner: THIS_MODULE,
read: fb_read,
write: fb_write,
ioctl: fb_ioctl,
mmap: fb_mmap,
open: fb_open,
release: fb_release
};
This is the interface to user application programs. fbmem.c implements these functions here.

2) register_framebuffer(struct fb_info *fb_info)
unregister_framebuffer(struct fb_info *fb_info)
This is the interface to low level frame buffer device driver. The drivers use this pair of functions to register or unregister themselves.
Almost all the work those low level drivers needed to do is to fill in an fb_info structure and then to (un)register it.

4 Skeleton of LCD controller rivers
LCD drivers operate LCD device hardwares, while fbmem.c records and administrates these drivers. There is a skeleton frame buffer driver in linux /drivers /fb /skeleton.c. It shows how to implement a frame buffer driver with very few codes. Because it is too simple, it does nothing but filling an fb_info and then (un)registering it.
In order to implement a usable LCD controller driver, something else must be added into it. But what should be added? As we all know, device drivers abstract hardware and provide system call interface to user programs. So we can implements the low level hardware operations according to the need of the system call interface, namely the file_operations structure which is discussed in section 3.2.

4.1 Allocate a system memory as video memory
After a careful lookup in fbmem.c, we know that open() and release() method of file_operations structure do not need low level support, while read(), write() and mmap() need a common support function fb_get_fix(). So fb_get_fix() must be provided by driver writers.
Additionally the writer should allocate a system memory as video memory, for most LCD controllers have no their own video memory. The allocated memory start address and length are later placed in smem_start and smem_len fields of fb_fix_screeninfo structure. The allocated memory should be physically consecutive.

4.2 Implement the fb_ops functions
The only method of file_operations still not discussed is ioctl(). User application programs use ioctl() system call to operate LCD hardware. Methods defined in fb_ops structure(section 3.1) are used to support these operations. NOTE: fb_ops structure is NOT file_operations structure. fb_ops is for abstraction of low level operations, while file_operations is for upper level system call interface
Again we’d better first decide which methods should be implemented. ioctl() system call is implemented in fbmem.c, so we turn to it and quickly we can find out such relationship between ioctl() commands and fb_ops’s methods:
FBIOGET_VSCREENINFO fb_get_var
FBIOPUT_VSCREENINFO fb_set_var
FBIOGET_FSCREENINFO fb_get_fix
FBIOPUTCMAP fb_set_cmap
FBIOGETCMAP fb_get_cmap
FBIOPAN_DISPLAY fb_pan_display
Now we know that if we implement these fb_XXX_XXX functions, then user application writers can use FBIOXXXX micros to operate LCD hardwares. But how can we implement them?
It is fine to have a reference to linux/drivers/video/fbgen.c or other drivers under the linux/drivers/video directory.
Among these functions, fb_set_var() is the most important. It is used to set video mode and more. The following is the common steps performed by a fb_set_var() function:
1) Check if mode setting is necessary
2) Set the mode
3) Set colormap
4) Reconfigure the registers of LCD controller according former settings
The fourth step shows where low level operations are placed. Curious men may have such a question: how image data of user application are put onto the screen. Driver writer allocates a system memory as video memory, and later he sets the start address and length of the memory to LCD controller’s registers(often in fb_set_var() function). The content of the memory will be sent to screen automatically by LCD controller(for details, see specific LCD controller). On the other hand, the allocated system memory is mapped to user space by mmap(). Thereby, when a user sends data to the mapped memory, the data will be shown on LCD screen.

James Simmons, jsimmons@edgeglobal.com
v1.00, 9 October 1999

This document describes how to support a framebuffer video card for Linux.
It lists the supported video hardware, describes how to program the kernel
drivers, and answers frequently asked questions. The goal is to bring
current framebuffer driver writers as well as new ones up to speed on the
new developments accuring in the graphics system for linux.

______________________________________________________________________

Table of Contents

1. Introduction

1.1 Acknowledgments
1.2 Revision History
1.3 New versions of this document
1.4 Feedback
1.5 Distribution Policy

2. Framebuffer Video Card Technology

2.1 Monitor
2.2 Video Card

3. Setting a video mode

3.1 Fixed Frequency Monitors
3.2 Multi Frequency Monitors
3.3 Receipe for multisync monitors
3.4 Receipe for Monosync
3.5 Clocks
3.5.1 DCLK
3.5.2 MCLK
3.5.3 PLL
3.6 CRTC registers   
3.7 Colors

4. Framebuffer internal API

4.1 Data Structures
4.2 Driver layout

5. Answers To Frequently Asked Questions

5.1 Does fbdev support accels?

6. References

______________________________________________________________________

1. Introduction

This is the Linux Framebuffer driver HOWTO. It is intended as a quick
reference covering everything you need to know to write a framebuffer
video driver under Linux. Frequently asked questions about video mode
setting under Linux are answered, and references are given to some other
sources of information on a variety of topics related to computer
graphics. Also read this document not once, not twice but three times if
you are not familiar with video hardware.

The scope is limited to the aspects of writing a mode setting video card
framebuffer driver pertaining to Linux. See the other documents listed in
the References section for more general information on how to setup
framebuffer cards and setting up the XFB_Dev X server.

1.1. Acknowledgments

Much of this information came from the new framebuffer internal API being
developed by me for the upcoming next series of kernels. Originally this
was based on a patch by Fabrice Bellard. I learned of this patch and
was impressed by it. Later I took over development and improved it even
more. Much thanks goes to Fabrice for getting the ball rolling. This API is
a natural extension of the original API developed by Martin Schaller and
now maintained by Geert Uytterhoeven (geert@linux-m68k.org). Thanks
to you and the many others who developed the Linux framebuffer system,
drivers and utilities. A great amount of thanks goes to Andreas Beck of
the GGI project for helping me write this document and teaching me about
mode setting. Thanks also goes out to those who have supported my work.

Thanks to the SGML Tools package, this HOWTO is available in several
formats, all generated from a common source file.

1.2. Revision History

Version 1.0
first version; posted to fbdev mailing list only

1.3. New versions of this document

New versions of this document will be periodically posted to the
comp.os.linux.answers newsgroup. They will also be uploaded to various
anonymous ftp sites that archive such information including
<ftp://metalabs.unc.edu/pub/Linux/docs/HOWTO/>.

Hypertext versions of this and other Linux HOWTOs are available on
many World-Wide-Web sites, including <http://metalab.unc.edu/LDP/>.
Most Linux CD-ROM distributions include the HOWTOs, often under the
/usr/doc directory, and you can also buy printed copies from several
vendors. Sometimes the HOWTOs available from CD-ROM vendors, ftp
sites, and printed format are out of date. If the date on this HOWTO
is more than six months in the past, then a newer copy is probably
available on the Internet.

Most translations of this and other Linux HOWTOs can also be found at
<http://sunsite.unc.edu/pub/Linux/docs/HOWTO/translations/> and
<ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/translations/>.

If you make a translation of this document into another language, let
me know and I'll include a reference to it here. As of yet there are no
translations.

1.4. Feedback

I rely on you, the reader, to make this HOWTO useful. If you have any
suggestions, corrections, or comments, please send them to me,
jsimmons@edgeglobal.com, and I will try to incorporate them in the next
revision.

I am also willing to answer general questions on video cards and fbcon
under Linux, as best I can. Before doing so, please read all of the
information in this HOWTO, and send me detailed information about the
problem. Please do not ask me about using framebuffer cards under
operating systems other than Linux.

If you publish this document on a CD-ROM or in hardcopy form, a
complimentary copy would be appreciated. Mail me for my postal
address. Also consider making a donation to the Linux Documentation
Project to help support free documentation for Linux. Contact the
Linux HOWTO coordinator, Tim Bynum <mailto:linux-howto@sunsite.unc.edu>,
for more information.

1.5. Distribution Policy

Copyright (c) 1999 by James Simmons. This document may be
distributed under the terms set forth in the LDP license at
<http://sunsite.unc.edu/LDP/COPYRIGHT.html>.

2. Framebuffer Card Technology

This section gives a very cursory overview of graphics cards that have
accessible framebuffer technology, in order to help you understand the
concepts used later in the document. If you are considering writing a
driver for a video card please contact the manufacturer for documentation
on the card. Also please consider reading some books on video hardware in
order to learn more.

The way framebuffer devices behave under linux is something very similar
to /dev/mem. /dev/fb is in fact viewed as a memory device except in this case
the memory is video ram. Fbdev mmaps this memory to userspace for direct
access. This model is of course simplified for purpose of making programing
the frame buffer much easier as well as making it device and platform
independent. Since we are interested in building a driver we need to
userstand how exactly the video card itself works.

2.1 Monitor

First lets discribe one of the biggest but often overlooked components,
the monitor. Today there are many types of monitors. Flat screen to LED
and so on. For all the many types the basic principle behind the monitor is
the same. Basically a monitor builds an image sequentially from the data it
gets on its input lines. To achieve this a beam scans over the screen in a
kind of "zig-zag" pattern that covers the whole visible part of the screen
once per frame. It happens so fast the eye can't see this happening. Well we
hope. So which way does this beam go? All monitors have chosen to always
go left to right with a quick jump back to the far left when we hit the
right boundary of the monitor. Same for the top to bottom approach but at a
much slowe
r pace since most of our time is used to move left to right for
every single line. Obviously, the displayed data needs to be synchronized
with the current position of the beam to be able to build a steady picture.
This is what those HSYNC and VSYNC you see in your monitor manual are for.
These two lines that say "hey move the beam to the left now" and "move the
ray to the top now". Now some systems encode this information for example in
the green channel, which is called sync on green, but that doesn't change
the principle. All a monitor knows about a mode is what it gets that's
contained in the frequencies with which those signals return. These
frequencies are called the horizontal and vertical frequency (aka refresh
rate), as it determines how often per second a whole image is drawn. So a
monitor knows nothing about depth, clocks, borders. If two modes have the
same frequencies they will be the same to the monitor. This is why different
centering data for e.g. 640x480x16 and 640x480x32 are not stored in the
monitor. The monitor can't distinguish between those modes. Between two
HSYNC we get the RGB signals.

HSYNC __/~~~\______________________________________________/~~~\___
RGB ___________datadatadatadatadatadatadatadatadatad_____________
time 1 2 3 4 5

At 1, the HSYNC pulse gets raised. The beam will now quickly move to the
left. During that time, the rgb lines should be black (ray off), as
otherwise it would leave a noticeable trace while moving, which would look
ugly.

At 2, the HSYNC pulse ends. This point isn't of much interest, as you cannot
tell if the ray is already at the left edge. The only thing important
about point 2 is, that the time between point 1 and 2 must be sufficiently
high for the monitor to detect the HSYNC signal. Usually the HSYNC pulse can
be made very small.

At some point after 1, the ray will start flying to the right again. When
point 3 comes, it will actually start to display data. Point 3 can thus be
adjusted to change the left border location. If you wait longer until you
start sending data, the left border will move to the right.

When you have sent all data you reach point 4. As a HSYNC pulse should then
be sent, to start a new line, we set the RGB lines to black again.
At point 5 we have completed a cycle and start the next line.

2.2 Video card

Next we look at the video card point of view. The video card could send out
a steady stream of data to the monitor except for one thing. The monitor
needs time for retracing so the video card will be put into some "delay"
at specific times. To be precise between point 4 and point on the NEXT line)
on the previous diagram. For the video card the "natural" coordinate system
starts at point 3, when it starts emitting data. This point usually causes
some confusion with modeline-calculation:

HSYNC __/~~~\______________________________________________/~~~\___
RGB ___________datadatadatadatadatadatadatadatadatad_____________
time 1 2 3 4 5 6
grc SS SE 0 W SS SE

From the graphics card point of view (grc) a line starts at "0". From that
point onward, it will output the data in its video ram. There is a counter
that will limit the number of pixels that are put on one line. This is what
we call the width of the mode. On a 640x480 standard VESA mode, this is 640
pixels.

Now we will usually want a small right border to allow the monitor to
prepare for the following SYNC pulse we will generate. The aforementioned
counter will run on (but data output from video RAM will be suppressed)
until we reach the point SS (SyncStart). On a 640x480 standard VGA mode,
this happens at 664 pixels. That is, we left a border of 24 pixels.

Now we raise the HSYNC to tell the monitor to go left. This signal remains
asserted until we reach the point SE (SyncEnd). (760 pixels on VGA -
i.e. 96 pixels of sync pulse. This is pretty long, but VGA monitors weren't
very quick.)

We will also want some left border, so we wait until we reach the next "0"
point before starting to generate a signal again. On standard VGA this
happens at 800 pixels (40 pixels left border). At that point, we reset the
counter to 0 and start over. This point is usually called the "total"
for this reason.

Now let us look at how we can change the picture's appearance by changing
values in such a modeline.

Moving SE should not cause any difference at all, except if you make the
sync pulse too small for the monitor to recognize.

Moving SS and SE together will move the location of the sync pulse within
the picture. Let us assume we move them both to the "left", i.e. we
decrease their startpoints. What happens is, that we decrease the distance
W-SS (which determines the right border) and increase 0-SE (the left
border). As a result, the picture moves to the right.

Now what happens, if you change W ? You get extra pixels at the right
border. As usually borders are pretty large for standard VGA modes,
you can usually display something like 648x486 without a problem on a
standard VGA monitor. You will not be able to see the difference.

Of course there are limits to this: If you go too far, you will produce
pixels beyond the visiable area of the monitor which is useless, or
interfere with the retrace; that gives ugly stripes from the retracing
CRT ray.

Now let's shed some light on a few remaining terms:

Blankstart BS and blankend BE. Between SE and 0, you can put a BE point on
many graphics cards. At that point, the RGB lines are no longer clamped to
black (to avoid interfering with the retrace), but can be programmed to a
border color. The same goes for BS, which can be placed between W and SS.
Usually one doesn't use that feature nowadays, as we have tuneable monitors
that allow to stretch the mode to the physical limits of the monitor.

On old monitors, one used large borders to ensure the data was always
visible. There the border color made some sense as kind of eye candy.

Pixelclock. That is the rate at which pixels are output to the RGB lines.
It is usually the basic unit for all timing values in a graphics card.

3. Actually calculating a mode

If you look at the fbdev driver you think yikes. Yes it's complex but not as
much as you think. A side note about standard modes. It's a common
misconception that graphics cards cannot do anything but the VGA/VESA
induced "standard" modes like 640x480, 800x600, 1024x768, 1280x960, ...
With most cards, about any resolution can be programmed, though many
accelerators have been optimized for the abovementioned modes, so that it
is usually NOT wise to use other widths, as one might need to turn OFF
accelerator support. So if you write a driver, don't cling to these modes.
If the card can handle it, allow any mode.

Here the type of monitor has a big impact on what kind of modes we can have.
There are two basic types of monitors, fixed frequency (they usually can do
multiple vertical frequencies, though, which are much less critical, as they
are much lower) and multifrequency.

3.1 Fixed freq
uency monitors

The monitor manual says the horizontal frequency (hfreq) is 31.5 kHz.

And we want to display a given mode, say 640x480.

We can now already determine the absolute minimum dotclock we need, as

dotclock = horiz_total * hfreq

and

horiz_total = width + right_border + sync + left_border > width

The minimum dotclock computes to 20.16 MHz. We will probably need
something around 20% "slack" for borders and sync, so let's say we need
about a 24MHz clock. Now we look at the next higher clock our card can
handle, which is 25.175 MHz, as we assume we have a VGA compatible card.

Now we can compute the horizontal total:

horiz_total = dotclock / hfreq = 799.2

We can only program this as multiples of 8, so we round to 800.
Now we still need to determine the length and placement of the sync pulse,
which will give all remaining parameters.

There is no clean mathematical requirement for those. Technically, the sync
must be long enough to be detected, and placed in a way that the mode is
centered. The centering issue is not very pressing anymore, as digitally
controlled monitors are common, which allow to control that externally.
Generally you should place the sync pulse early (i.e. keep right_border
small), as this will usually not cause artifacts that would arise from
turning on the output again too early, when the sync pulse is placed too
late.

So if we as a "rule-of-thumb" use a half of the blanking period for the sync
and divide the rest as 1/3 right-border, 2/3 left border, we get a modeline
of

"640x480" 25.175 640 664 744 800 ??? ??? ??? ???

While this is not perfectly the same as a standard VGA timing, it should run
as well on VGA monitors. The sync is a bit shorter, but that shouldn't be a
real problem.

Now for the vertical timing. At 480 lines, a VGA monitor uses 60Hz.

hfreq = vfreq * vert_total

which yields vert_total=525. The vertical timings usually use much less
overhead than the horizontal ones, as here we count whole _lines_, which
means much longer delays than just pixels. 10% overhead suffice here, and
we again split the borders 1/3 : 2/3, with only a few lines (say 2) for
the sync pulse, as this is already much longer than a HSYNC and thus easily
detectable.

"640x480" 25.175 640 664 744 800 480 495 497 525

let us compare that to an XF86 Modeline that claims to be standard VGA:

Modeline "640x480" 25.175 640 664 760 800 480 491 493 525

Not much difference - right ? They should both work well, just a little
shifted against each other vertically.

3.2 multiscan monitor

Here we will consider a theorical monitor that can do hfreq 31-95kHz and
vfreq 50-130Hz. Now let's look at a 640x480 mode. Our heuristics say,
that we will need about 768x528 (20% and 10% more) for borders and sync.
We as well want maximum refresh rate, so let's look what limits the mode:

hfreq = vfreq * vtotal = 130 * 528 = 68.6 kHz

Oh - we cannot use the full hfreq of our monitor ... well no problem. What
counts is the vfreq, as it determines how much flicker we see.

O.K. - what pixelclock will we need ?

pixclock = hfreq * htotal.

The calculation yields 52.7MHz.

Now we look what the card can do. Say we have a fixed set of clocks. We look
what clocks we have close by. Assume the card can do 50 and 60 MHz.

Now we have the choice: We can either use a lower clock, thus scaling down
the refresh rate a bit (by 5% ... so what ...): This is what one usually
does.

Or we can use a higher clock, but this would exceed the monitor
specifications. That can be countered by adding more border, but this is
usually not done, as it is a waste of space on the screen
However keep it in mind as a trick for displaying 320x200, when you do not
have doubling features. It will display in a tiny window in the middle of
the screen, but it will display.

O.K. - what will our calculation yield ?

"640x480" 50 640 664 728 768 480 496 498 528 # 65kHz 123Hz

I just mentioned doubling features. This is how VGA does 320x200. It
displays each pixel twice in both directions. Thus it effectively is a
640x400 mode. If this would not be done, you would need a pixelclock of
12.59MHz and you would still have the problem of needing a 140Hz refresh, if
hsync should stay at 31.5kHz.

A horizontal doubling feature allows to use the 25.175MHz clock intended
for 640, and a line doubling feature keeps the vsync the same as 400 lines.
Actually the line-doubler is programmable, so you can as well use modes as
sick as 640x50.

O.K. - another example. Same monitor, 1280x1024.

Now we need about 1536x1126 total (same rule of thumb).
That yields 130Hz*1126lines = 146 kHz. We would exceed the hfreq with that,
so now the hfreq is the limit and we can only reach a refresh rate of about
(95kHz/1126) 84 Hz anymore.

The required clock is now 146MHz. That would yield:

"1280x1024" 146 1280 1320 1448 1536 1024 1058 1060 1126 # 95kHz 84Hz

Now the clock might be programmable, but keep in mind, that there may be
limits to the clock. DO NOT OVERCLOCK a graphics card. This will result in
the RAMDAC producing blurry images (as it cannot cope with the high speed),
and more importantly, the RAMDAC will OVERHEAT and might be destroyed.

Another issue is memory bandwidth. The video memory can only give a certain
amount of data per time unit. This often limits the maximum clock at modes
with high color depth (i.e. much data per pixel). In the case of my card it
limits the clock to 130MHz at 16 bit depth, what would produce:

"1280x1024" 130 1280 1320 1448 1536 1024 1058 1060 1126 # 85kHz 75Hz

which is pretty much, what my monitor shows now, if I ask it.

3.3 Recipe for multisync monitors

a) Determine the totals by calculating htotal = width*1.2 and
vtotal = height*1.1 .
b) Check what limits the refresh by calculating vfreq2 = hfreqmax/vtotal.
If that exceeds vfreqmax, the limit is on the vfreq side, and we use
vfre = vfreqmax and hfreq = vfreqmax*vtotal. If it doesn't, the mode is
limited by hfreq and we have to use vfreq = vfreq2.
Note, that if this is smaller than vfreqmin, the mode cannot be displayed.
In the vfreq-limited case, you might exceed hfreqmin, which can be
countered by using linedoubling facilities, if available. You can also
add extra blank lines (increase vtotal) as a last-resort alternative.
c) Now that you have hfreq and vfreq, calculate the pixel clock using
pixclock=hfreq*htotal. Use the next lower pixel clock. If you are below
the lowest clock, you might want to try horizontal doubling features or
you will have to pad the mode by increasing htotal.
d) Again check the monitor limits. You might be violating lower bounds now
... In that case you might need to resort to choosing a higher clock
and padding as well.
e) You now have pixclock, width, height, htotal and vtotal. Calculate the
sync start positions: hss=width+(htotal-width)/2/3 ;
vss=height+(vtotal-height)/3; Make sure to properly align them as
required by the video card hardware hss usually has to be a
multiple of
8.
f) SyncEnd is calculated similarly: hse=hss+(htotal-width)/2 and vse=vss+2.

3.4 Receipe for Monosync:
a) Calculate the number of lines. As hfreq and vfreq are given, vtotal is
fixed: vtotal=hfreq/vfreq. If there are multiple vfreqs allowed, choose
them according to your desired vtotal (i.e. one that gives the lowest
vtotal above what you really need).
b) Calculate the pixelclock. pixclock=hfreq*htotal. htotal starts at the
same estimate (width*1.2) we used above.
c) Adjust the pixelclock to hardware-limits. Adjust _UP_. Now recalculate
the htotal=pixclock/hfreq.
d) Go to 3.3. e)

A important final word. Most video card documentations gives you the exact
equations needed to set a mode. Here we give approached values. Use the
exact values given in the documents.

3.5 Clocks

Clocks on a video card ensure that different parts of the video hardware
happen at the correct time and in the correct order. For those not familiar
with computer clocks they work by generating a pulse at regular intervals
like what is shown below. You will something similar in your video
documentation.
___ ___ -  
| | | | V The period (T) represents the time between two
___| |___| |___ _ "ticks" of thee clock, and the frequency of the
clock is how many clock ticks happen per second.
| T |

The height of the pulse (V) is the difference in the voltage. This means
for T/2 units of time the voltage is at V1 then for the next T/2 it goes
to another voltage. Normally you don't have to worry about the amplitude
(height) of the pulse except for some cards that allow you to set the
height for powersaving mode. Normally you just want to change the frequency.
The hardware uses a clock for every part of the hardware except the bus
clock which is apart of the motherboard. No need to worry about that one.
Their exist many types of clocks. For video cards you can come across two
types of clocks. The first type you might come across are fixed clock
generators which are used in older video cards. The second type of clock
used in modern vidoe cards are called PLL (Phase Lock Loop).

In the documentation you might see terms about edge triggered devices. The
different types of edge triggers can be:

3.5.1 PLL
  
A PLL is composed of a multipler and a divider. They multiple a reference
frequency by a integer mulitplier, and then divide it by a integer divider.
Of course a PLL has a low

3.5.2 DCLK

3.5.3 MCLK

3.6 CRTC registers   

3.7 Colors

There is an endless number of colors but colors have a special property.
If you take two colors and mix them together you get a different color.
There are many models to represent colors but fbdev is based on what is
known as the RGB color model. Out of all the colors there are three colors
in this model which when mixed in different amounts can produce any color.
These colors are known as primary colors. There is a physical limit
to mixing colors. If you mix red, green, and blue in equal amounts
you get gray. Now if you make each color component equally brighter the final
color will become white. Now there is a point where making each component
brighter and brighter you will still end up with white. You can increase the
intensity of a color component by any amount from some inital value up to
this physical limit. This is where the image depth comes in. As you know on
most cards you can have an image depth from one bit to 32 bits. Image depth
is independent of the mapping from the pixel to the color components. It
also is independent of the memory layout to pixel mapping. Note some cards
have a complex mapping from the pixel values to the color components (red,
blue, green) as well as video memory to pixel mapping. If this is the case
you will have to consult your documentation on your video card to see what
the mapping exactly is. Here are the mappings defined from top to bottom in
fbdev starting with the value of the color components.

{red,blue,green}
|
FB_VISUAL_MONO01
FB_VISUAL_MONO10
FB_VISUAL_TRUECOLOR
FB_VISUAL_PSEUDOCOLOR
FB_VISUAL_DIRECTCOLOR
FB_VISUAL_STATIC_PSEUDOCOLOR
|
pixel value
FB_TYPE_PACKED_PIXELS
FB_TYPE_PLANES
FB_TYPE_INTERLEAVED_PLANES
FB_TYPE_TEXT
FB_TYPE_VGA_PLANES
|
value in video memory

The way fbdev tells what this video memory to pixel mapping is, is with
the type field in fb_fix_screeninfo. Here I'm going to describe the
FB_TYPE_PACKED_PIXELS layout since it is the most common. Here there is
a direct mapping from video memory to pixel value. If you place a 5 in video
memory then the pixel value at that position will be 5. This is important
when you have memory mapped the video memory to userland. Next we consider
the mapping from a pixel value to the colors. This is represented in the
fbdev API by the visual field in fb_fix_screeninfo. As you can see from the
above diagram this mapping is independent from the mapping from video memory
to pixel value. This means a FB_TYPE_PLANES could have FB_VISUAL_MONO01 just
like FB_TYPE_PACKED_PIXELS can. To understand visuals we have to go back to
the first types of video hardware. In the begining there was just monochrome
monitors. Here we could only display 2 colors. The foreground and background
color. Traditionally these colors are black and white but if you are old
enough you would remember the old green monitors. In fbdev API we define two
types of monochrome modes. The difference between the two is that the
foreground and background colors are swapped. Then computers began to
support color. The only problem was they could only display a small number of
colors at one time. What if you wanted to have an application display a
certain set of colors. Well the way that was developed to get around this
was the idea of a color map. A color map translated a pixel value to the
colors needed. If your application needs a specific set of colors it would
switch the color maps and you would get the needed colors. Of course this
also switches the other colors in the applications. That was the trade off.
This became what was known as pseudocolor mode. In fbdev API there are two
types of pseudocolor mappings. A static one and a dynamic one.
FB_VISUAL_STATIC_PSEUDOCOLOR defines a video card that has a non programmable
color map. What colors you get are what you are stuck with. The other type
of color map can be changed. Video cards in time started to support more
colors but this required having a larger color map. Also video memory prices
started to drop and video cards begain to sell with more of it. To properly
support 256 color intensity levels for each color component you would
need a
color map of 16 million colors. So new mappings were developed in which
specific fields of a pixel were directly proportional to the intensity of a
color component. Two types of mappings were developed. One was truecolor
and the other directcolor. In truecolor you cannot change the mappings from
the pixel value to color intensities. Setting a value of 64 to the red
component of the pixel will result in a red intensity of 64. How bright of a
red this is depends on the image depth. For directcolor you can control this.
You could make a pixel value in the red field of 64 equal 128 for the
intensity. Also some cards support an alpha value which is used in higher
graphics which for fbdev is of little importance than it should always be
set to the highest value it can have. For most cards alpha shows up for 15
bit modes where each color compenent can have up to 32 intensity levels (2^5)
and one bit represents the alpha component. It also shows up for 32 bit
modes where each component red, blue, green, and alpha are given 256
intensity levels (2^8). 24 bit mode is like 32 bit mode except it lacks the
alpha component. A important note is that some cards only support 24 bit
mode on certain architectures.

4. Framebuffer internal API

Now that we understand the basic ideas behind video card technology and mode
setting we can now look at how the framebuffer devices abstract them. Also
we will see that fbdev actually handles most of the mode setting issues for
you to make life much easier. In the older API the console code was heavily
linked to the framebuffer devices. The newer API has now moved nearly all
console handling code into fbcon itself. Now fbcon is a true wrapper around
the video card's abilities. This allowed for massive code reduction and
easier driver developement. A good example of a framebuffer driver is vfb.
The vfb driver is not a true framebuffer driver. All it does is map a chunk
of memory to userspace. It's used for demonstration purposes and testing.

4.1 Data Structures

The framebuffer drivers depend heavily on four data structures. These
structures are declared in fb.h. They are fb_var_screeninfo,
fb_fix_screeninfo, fb_monospecs, and fb_info. The first three can be made
available to and from userland. First let me describe what each means and
how they are used. Fb_var_screeninfo is used to describe the features of
a video card you normally can set. It's with fb_var_screeninfo you can define
such things as depth and the resolution you want. The next structure is
fb_fix_screeninfo. This defines the properties of a card that are created
when you set a mode and can't be changed otherwise. A good example is the
start of the framebuffer memory. This can depend on what mode is set. Now
while using that mode you don't want to have the memory position change on
you. In this case the video hardware tells you the memory location and you
have no say about it. The third structure is fb_monospecs. In the old API
the importance of fb_monospecs was very little. This allowed for forbidden
things such as setting a mode of 800x600 on a fixed-frequency monitor. With
the new API fb_monospecs prevents such things and if used correctly can
prevent a monitor from being cooked. The final data structure is fb_info.
This defines the current state of the video card. Fb_info is only visible
from the kernel. Inside of fb_info there is a fb_ops which is a collection
of needed functions to make fbdev and fbcon work.

4.2 Driver layout

Here I discribe a clean way to code your drivers. A good example of the
basic layout is in vfb.c. In the example driver we first present our data
structures in the begining of the file. Note there is no fb_monospecs since
this is handled by code in fbmon.c. This can be done since monitors are
independent in behavior from video cards. First we define our three basic
data structures. For all the data structures I defined them static and
declare the default values. The reason I do this is that it's less memory
intensive than allocating a piece of memory and filling in the default
values. Note for drivers that support multihead on the same card
the fb_info should be dynamically allocated for each card present. For
fb_var_screeninfo and fb_fix_screeninfo they still are declared static
since all the cards can be set to the same mode.

4.3 Initialization and boot time parameter handling

There are two functions that handle the video card at boot time.

int xxfb_init(void);
int xxfb_setup(char*);

In the example driver as with most drivers these functions are placed at
the end of the driver. Both are very card specific. In order to link
your driver directly into the kernel both of these functions you must add
the above definition with extern in front to fbmem.c. Then add these
functions to the following in fbmem.c

static struct {
const char *name;
int (*init)(void);
int (*setup)(char*);
} fb_drivers[] __initdata = {
#ifdef CONFIG_FB_YOURCARD
{ "driver_name", xxxfb_init, xxxfb_setup },
#endif
...

Setup is used to pass card specific options from the boot prompt of your
favorite boot loader. A good example is boot:video=matrox:vesa:443. The
basic setup function is

int __init xxxfb_setup(char *options)
{
char *this_opt;

if (!options || !*options)
return 0;

for (this_opt = strtok(options, ","); this_opt;
this_opt = strtok(NULL, ","))
if (!strcmp(this_opt, "my_option")) {
/* Do your stuff. Usually set some static flags that
the driver later uses */
} else if (!strncmp(this_opt, "Other_option:", 5))
strcpy(some_flag_driver_uese, this_opt+5);
} else ....
}

The xxxfb_init function sets the inital state of the video card. This
function has to consider bus and platform handling since today most cards
can exist on many platforms. For bus types we have to deal with there are
PCI, ISA, zorro. Also some platforms offer firmware that returns information
about the video card. In these cases we often don't need to deal with the
bus unless we need more control over the card. Let's look at Open Firmware
that's available on PowerPCs. If you are going to use Open Firmware to
initalize your card you need to add the following to offb.c.

#ifdef CONFIG_FB_YOUR_CARD
extern void xxxfb_of_init(struct device_node *dp);
#endif /* CONFIG_FB_YOUR_CARD */

Then in the function offb_init_driver you add something similar to the
following.

#ifdef CONFIG_FB_YOUR_CARD
if (!strncmp(dp->name,"Open Firmware number of your card ",size_of_name)) {
xxxfb_of_init(dp);
return 1;
}
#endif /* CONFIG_FB_YOUR_CARD */

If Open Firmware doesn't detect your card then Open Firmware sets up a
generic video mode for you. Now in your driver you really need two
initalization functions.

The next major part of the driver is declaring the functions of fb_ops
declared in fb_info for the driver.

The first two functions xxfb_open and xxfb_release can be called from both
fbcon and fbdev.
In fact that's the use of the user flag. If user equals zero
then fbcon wants to access this device else it's an explicit open of the
framebuffer device. This way you can handle the framebuffer device for the
console in a special way for a particular video card. For most drivers this
function just does a MOD_INC_USE_COUNT or MOD_DEC_USE_COUNT.

These are the functions at the heart of mode setting. There are a
few cards that don't support mode changing. For these we have this function
return a -EINVAL to let the user know he/she can't set the mode. Actually
set_var does more than just set modes. It can check them as well. In
fb_var_screeninfo there is a flag called activate. This flag can take on
the the following values. FB_ACTIVATE_NOW,FB_ACTIVATE_NXTOPEN, and
FB_ACTIVATE_TEST. FB_ACTIVATE_TEST tells us if the hardware can handle what
the user requested. FB_ACTIVATE_NXTOPEN sets the values wanted on the next
explicit open of fbdev. The final one FB_ACTIVATE_NOW checks the mode to see
if it can be done and then sets the mode. You MUST check the mode before all
things. Note this function is very card specific but I attempt to give you
the most general layout.. The basic layout then for xxxfb_set_var is

static int vfb_set_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
int line_length;

/* Basic setup test. Here we look at what the user passed in that he/she
wants. For example to test the fb_var_screeninfo field vmode like is
done in vfb.c. Here we see if the user has FB_VMODE_YWARP. Also we
should look to see if the user tried to pass in invalid values like 17
bpp (bits per pixel) */

/* Remember the above discussion on how monitors see a mode. They don't
care about bit depth. So you can divide the checking into two parts.
One is to see if the user changed a mode from say 640x480 at 8 bpp to
640x480 at 32 bpp. Remember the var in fb_info represents the current
video mode. Before we actually change any resolutions we have to make
sure the card has enough memory for the new mode. Discovering how much
memory a video card has varies from card to card. Also finding out how
much memory we have is done in xxxfb_init since this never changes
unless you add more memory to your card which requires a reboot of the
machine anyway. You might have to do other tests depending on the make of
your card. Note the par filed in fb_info. This is used to store card
specific data. This data can effect set_var. Also it is present to
allow other possible drivers that could affect the framebuffer device
such as a special driver for an accel engine or memory mapping the z
buffer on a card */

/* Summary. First look at any var fields to see if they are valid. Next
test hardware with these fields without setting the hardware. An
example of one is find what the line_lenght would be for the new
mode. Then test the following */

if ((line_length * var->yres_virtual) > info->fix.smem_len)
return -ENOMEM;

if (info->var.xres != var->xres || info->var.yres != var->yres ||
info->var.xres_virtual != var->xres_virtual ||
info->var.yres_vitual != var->yres_virtual) {
/* Resolution changed !!! */

/* Next you must check to see if the monitor can handle this mode.
Don't want to fry your monitor or mess up the display really
badly */
if (fbmon_valid_timings(u_int pixclock, u_int htotal, u_int vtotal,
      const struct fb_info *fb_info))
/* Can't handle these timings. */
return -EINVAL;

/* Timings are okay. Next we see if we really want to change
this mode */
if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
/* Now let's program the clocks on this card. Here the code is
very card specific. Remember to change any fields for fix in
info that might be effected by the changing of the resolution.
*/
...
info->fix.line_length = line_length;
...

/* Now that we have dealt with the possible changing resolutions
let's handle a possible change of bit depth. */
if (info->var.bits_per_pixel != var->bits_per_pixel) {
if ((err = fb_alloc_cmap(&info->cmap, 0, 0)))
return err;
}
}

/* We have shown that the monitor and video card can handle this mode or
have actually set the mode. Next the fb_bitfield structure in
fb_var_screeninfo is filled in. Even if you don't set the mode you get
a feel of the mode before you really set it. These are typical values
but may be different for your card. For truecolor modes all the fields
matter. For pseudocolor modes only the length matters. Thus all the
lengths should be the same (=bpp). */

switch (var->bits_per_pixel) {
case 1:
case 8:
/* Pseudocolor mode example */
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 0;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
var->transp.offset = 0;
var->transp.length = 0;
break;
case 16: /* RGB 565 */
var->red.offset = 0;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 6;
var->blue.offset = 11;
var->blue.length = 5;
var->transp.offset = 0;
var->transp.length = 0;
break;
case 24: /* RGB 888 */
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 16;
var->blue.length = 8;
var->transp.offset = 0;
var->transp.length = 0;
break;
case 32: /* RGBA 8888 */
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 16;
var->blue.length = 8;
var->transp.offset = 24;
var->transp.length = 8;
break;
}
/* Yeah. We are done !!! */
}

The function xxxfb_setcolreg is used to set a single color register for a
video card. To use this properly you must understand colors which is
discribed above. This routine sets a color map entry. The regno passed into
the routine represents the color map index which is equal to the color that's
composed of the amount of red, green, blue, and even alpha that are also
passed into the function. For psuedocolor modes this col
or map index (regno)
represents the pixel value. So if you place a pixel value of regno in video
memory you get the color that's made of the red, green, blue that you passed
into xxxfb_setcolreg. Now for truecolor and directcolor mode it's a little
different. In this case we simulate a psuedo color map. The reason for this
is the console system always has a color map which has 16 entries. In fb_info
there is the pseudo_palette which gives a mapping from a non color map
mode to a color map based system. The pseudo_palette always has 17 entries.
The first 16 for the console colors and the last one for the cursor. So if
we wanted to display the 4 entry in the color map of the console we would
placed the value of info->psuedo_palette[4] directly into the video memory.
This is of course taken care of by fbcon. You just need to code the
"formula" that does this translation. A example follows for 32 bit mode.

red >>= 8;
green >>= 8;
blue >>= 8;
info->pseudo_palette[regno] =
(red << info->var.red.offset) |
(green << info->var.green.offset) |
(blue << info->var.blue.offset);

Here we first scale down the color components. Each color passed to
set_colreg are 16 bits in size. For 32 bit mode each color is 8 bits in size.
Then we or the colors together after we offseted them. The offset is used
because the pixel layout in 32 bits could be RBGA, ARGBA etc. In setcol_reg
of vfb.c is the standard way to deal with packed pixel format of various
image depths. Regno is the index to get this particular color.

That does it for required functions besides the set of needed accel
functions which has not been discussed yet. If the video card doesn't
support the function then we just place a NULL in fb_ops. The next fuction
in fb_ops is xxxfb_blank. This function provides support for hardware
blanking. For xxxfb_blank the first parameter represents the blanking modes
available. They are VESA_NO_BLANKING, VESA_VSYNC_SUSPEND, VESA_HSYNC_SUSPEND,
and VESA_POWERDOWN. VESA_NO_BLANKING powers up the display again.
VESA_POWERDOWN turns off the display. This is great power saving feature on
a laptop.

The next optional function is xxxfb_pan_display. This function enables
panning. Panning is often used for scrolling.

The ioctl function gives you the power to take advantage of special features
other cards don't have. If your card is nothing special then just give this
fb_ops function a NULL pointer. The sky is the limit for defining your ioctl
calls.

There is a default memory map function for fbdev but sometimes it just
doesn't have the power you truly need. A good example of this is video
cards that work in sparc workstations. Those need their own mmap functions because
sparcs handle memory differently from other platforms. This is
true even for sparcs with PCI buses.

Now here is the next class of functions which are optional. The
xxxfb_accel_init and xxfb_accel_done. xxxfb_accel_init really depends on
the card. It is intended to initialize the engine or set the accel engine into
a state which you can use the acceleration engine. It also ensures that the
framebuffer is not accessed at the same time as the accel engine. This
can lock a system. Uusally there is a bit to test to see if an accel
engine is idle or the card generates an interrupt. For cards that used the
old fb_rasterimg this function replaces it. Some cards have a separate state
for 3D and 2D. This function insures that the card goes into a 2D state,
just in case a previous application set the accel engine into a 3D state
or made the accel engine very unhappy. The next function that encomposses
this set is xxxfb_accel_done. This function sets the video card in a state
such that you can write to the framebuffer again. You should provide both
functions if your driver uses even one hardware accelerated function. The
reason is to ensure that the framebuffer is not accessed at the same
time as the framebuffer.

Finally the third class of fb_op functions. Like the first they are required.
If your card does not support any of these accelerated functions there are
default functions for packed pixel framebuffer formats. They are
cfba_fillrect, cfba_copyarea, cfba_imgblit. If you supports some but not
all of the accels available you can still use some of these software emulated
accels. Each software emulated accel is stored in a seperate file. Now let's
describe each accel function. Before we discuss these functions we need to
note not to draw in areas past the video boundries. If it does you need to
adjust the width and height of the ares to avoid this problem. The first
function just fills in a rectangle starting at x1 and y1 of some width and
height with a pixel value of packed pixel format. If the video memory mapping
is not a direct mapping from the pixel value (not FB_TYPE_PACKED_PIXEL) you
will have to do some translating. There are two ways to fill in the
rectangle, FBA_ROP_COPY and FBA_ROP_XOR. FBA_ROP_XOR exclusive ors the pixel
value with the current pixel value. This allows things like quickly erasing
a rectangular area. The other function just directly copies the data. The
next function is xxxfb_copyarea. It just copies one area of the framebuffer
at source x and source y of some width and height to some destination x and
y. The final function is xxxfb_imageblt. This function copies an image from
system memory to video memory. You can get really fancy here but this is
fbdev which has the purpose of mode setting only. All the image blit
function does is draw bitmaps, images made of a foregound and background
color, and a color image of the same color depth as the framebuffer. The
second part is used to draw the little penguins. The drawing of bitmaps is
used to draw our fonts. That does it for the functions. Now you should be
set for writing your driver.

序言

Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别.在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便.本人这几周来为实验室自行研制的一块多媒体卡编制了驱动程序,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正.以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正.

一. Linux device driver 的概念

系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件, 应用程序可以象操作普通文件一样对硬件设备进行操作.设备驱动程序是内核的一部分,它完成以下的功能:
1.对设备初始化和释放.
2.把数据从内核传送到硬件和从硬件读取数据.
3.读取应用程序传送给设备文件的数据和回送应用程序请求的数据.
4.检测和处理设备出现的错误.

在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备.字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作.块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待.

已经提到,用户进程是通过设备文件来与实际的硬件打交道.每个设备文件都都有其文件属性(c/b),表示是字符设备还蔤强樯璞?另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序.

最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度.也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作.如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck.//hehe(请看下节,实例剖析)

读/写时,它首先察看缓冲区的内容,如果缓冲区的数据

二.实例剖析

我们来写一个最简单的字符设备驱动程序.虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备驱动程序.不过我的kernel是2.0.34,在低版本的kernel上可能会出现问题,我还没测试过.//xixi

#define __NO_VERSION__
#include <linux/modules.h>
#include <linux/version.h>
char kernel_version [] = UTS_RELEASE;

这一段定义了一些版本信息,虽然用处不是很大,但也必不可少.Johnsonm说所有的驱动程序的开头都要包含<linux/config.h>,但我看倒是未必.由于用户进程是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如 open,read,write,close...., 注意,不是fopen, fread.,但是如何把系统调用和驱动程序关联起来呢?这需要了解一个非常关键的数据结构:

struct file_operations {
int (*seek) (struct inode * ,struct file *, off_t ,int);
int (*read) (struct inode * ,struct file *, char ,int);
int (*write) (struct inode * ,struct file *, off_t ,int);
int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);
int (*select) (struct inode * ,struct file *, int ,select_table *);
int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long
int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);
int (*open) (struct inode * ,struct file *);
int (*release) (struct inode * ,struct file *);
int (*fsync) (struct inode * ,struct file *);
int (*fasync) (struct inode * ,struct file *,int);
int (*check_media_change) (struct inode * ,struct file *);
int (*revalidate) (dev_t dev);
}

这个结构的每一个成员的名字都对应着一个系统调用.用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数.这是linux的设备驱动程序工作的基本原理.既然是这样,则编写设备驱动程序的主要工作就是编写子函数,并填充file_operations的各个域.相当简单,不是吗?

下面就开始写子程序.
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <asm/segment.h>
unsigned int test_major = 0;
static int read_test(struct inode *node,struct file *file,char *buf,int count)
{
  int left;
  if (verify_area(VERIFY_WRITE,buf,count) == -EFAULT )
  return -EFAULT;
  for(left = count ; left > 0 ; left--)
  {
    __put_user(1,buf,1);
    buf++;
  }
  return count;
}

这个函数是为read调用准备的.当调用read时,read_test()被调用,它把用户的缓冲区全部写1.buf 是read调用的一个参数.它是用户进程空间的一个地址.但是在read_test被调用时,系统进入核心态.所以不能使用buf这个地址,必须用__put_user(),这是kernel提供的一个函数,用于向用户传送数据.另外还有很多类似功能的函数.请参考<linux/mm.h>.在向用户空间拷贝数据之前,必须验证buf是否可用.这就用到函数verify_area.

static int write_tibet(struct inode *inode,struct file *file,const char *buf,int count)
{
  return count;
}
static int open_tibet(struct inode *inode,struct file *file )
{
  MOD_INC_USE_COUNT;
  return 0;
}
static void release_tibet(struct inode *inode,struct file *file )
{
  MOD_DEC_USE_COUNT;
}

这几个函数都是空操作.实际调用发生时什么也不做,他们仅仅为下面的结构提供函数指针。

struct file_operations test_fops = {
NULL,
read_test,
write_test,
NULL, /* test_readdir */
NULL,
NULL, /* test_ioctl */
NULL, /* test_mmap */
open_test,
release_test, NULL, /* test_fsync */
NULL, /* test_fasync */
/* nothing more, fill with NULLs */
};

设备驱动程序的主体可以说是写好了。现在要把驱动程序嵌入内核。驱动程序可以按照两种方式编译。一种是编译进kernel,另一种是编译成模块(modules),如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态的卸载,不利于调试,所以推荐使用模块方式。

int init_module(void)
{
  int result;
  result = register_chrdev(0, "test", &test_fops);
  if (result < 0)
  {
    printk(KERN_INFO "test: can't get major number ");
    return result;
  }
  if (te
st_major == 0) test_major = result; /* dynamic */
  return 0;
}

在用insmod命令将编译好的模块调入内存时,init_module 函数被调用。在这里,init_module只做了一件事,就是向系统的字符设备表登记了一个字符设备。register_chrdev需要三个参数,参数一是希望获得的设备号,如果是零的话,系统将选择一个没有被占用的设备号返回。参数二是设备文件名,参数三用来登记驱动程序实际执行操作的函数的指针。如果登记成功,返回设备的主设备号,不成功,返回一个负值。

void cleanup_module(void)
{
  unregister_chrdev(test_major, "test");
}

在用rmmod卸载模块时,cleanup_module函数被调用,它释放字符设备test在系统字符设备表中占有的表项。

一个极其简单的字符设备可以说写好了,文件名就叫test.c吧。

下面编译

$ gcc -O2 -DMODULE -D__KERNEL__ -c test.c
得到文件test.o就是一个设备驱动程序。
如果设备驱动程序有多个文件,把每个文件按上面的命令行编译,然后
ld -r file1.o file2.o -o modulename.

驱动程序已经编译好了,现在把它安装到系统中去。
$ insmod -f test.o
如果安装成功,在/proc/devices文件中就可以看到设备test,
并可以看到它的主设备号,。
要卸载的话,运行
$ rmmod test

下一步要创建设备文件。
mknod /dev/test c major minor
c 是指字符设备,major是主设备号,就是在/proc/devices里看到的。
用shell命令
$ cat /proc/devices | awk "\$2=="test" {print \$1}"
就可以获得主设备号,可以把上面的命令行加入你的shell script中去。minor是从设备号,设置成0就可以了。

我们现在可以通过设备文件来访问我们的驱动程序。写一个小小的测试程序。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main()
{
int testdev;
int i;
char buf[10];

testdev = open("/dev/test",O_RDWR);
if ( testdev == -1 )
{
printf("Cann't open file ");
exit(0);
}
read(testdev,buf,10);
for (i = 0; i < 10;i++)
printf("%d ",buf[i]);
close(testdev);
}

编译运行,看看是不是打印出全1 ?

以上只是一个简单的演示。真正实用的驱动程序要复杂的多,要处理如中断,DMA,I/O port等问题。这些才是真正的难点。请看下节,实际情况的处理。

三 设备驱动程序中的一些具体问题。

1. I/O Port.
和硬件打交道离不开I/O Port,老的ISA设备经常是占用实际的I/O端口,在linux下,操作系统没有对I/O口屏蔽,也就是说,任何驱动程序都可以对任意的I/O口操作,这样就很容易引起混乱。每个驱动程序应该自己避免误用端口。

有两个重要的kernel函数可以保证驱动程序做到这一点。

1)check_region(int io_port, int off_set)

这个函数察看系统的I/O表,看是否有别的驱动程序占用某一段I/O口。
参数1:io端口的基地址,
参数2:io端口占用的范围。
返回值:0 没有占用, 非0,已经被占用。

2)request_region(int io_port, int off_set,char *devname)
如果这段I/O端口没有被占用,在我们的驱动程序中就可以使用它。在使用之前,必须向系统登记,以防止被其他程序占用。登记后,在/proc/ioports文件中可以看到你登记的io口。
参数1:io端口的基地址。
参数2:io端口占用的范围。
参数3:使用这段io地址的设备名。

在对I/O口登记后,就可以放心地用inb(), outb()之类的函来访问了。

在一些pci设备中,I/O端口被映射到一段内存中去,要访问这些端口就相当于访问一段内存。经常性的,我们要获得一块内存的物理地址。在dos环境下,(之所以不说是dos操作系统是因为我认为DOS根本就不是一个操作系统,它实在是太简单,太不安全了)只要用段:偏移就可以了。在window95中,95ddk提供了一个vmm 调用 _MapLinearToPhys,用以把线性地址转化为物理地址。但在Linux中是怎样做的呢?

2 内存操作
在设备驱动程序中动态开辟内存,不是用malloc,而是kmalloc,或者用get_free_pages直接申请页。释放内存用的是kfree,或free_pages. 请注意,kmalloc等函数返回的是物理地址!而malloc等返回的是线性地址!关于kmalloc返回的是物理地址这一点本人有点不太明白:既然从线性地址到物理地址的转换是由386cpu硬件完成的,那样汇编指令的操作数应该是线性地址,驱动程序同样也不能直接使用物理地址而是线性地址。但是事实上kmalloc返回的确实是物理地址,而且也可以直接通过它访问实际的RAM,我想这样可以由两种解释,一种是在核心态禁止分页,但是这好像不太现实;另一种是linux的页目录和页表项设计得正好使得物理地址等同于线性地址。我的想法不知对不对,还请高手指教。

言归正传,要注意kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。kmalloc用法参见khg.

内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用F0000000以上的地址空间。在驱动程序中不能直接访问,要通过kernel函数vremap获得重新映射以后的地址。

另外,很多硬件需要一块比较大的连续内存用作DMA传送。这块内存需要一直驻留在内存,不能被交换到文件中去。但是kmalloc最多只能开辟128k的内存。

这可以通过牺牲一些系统内存的方法来解决。

具体做法是:比如说你的机器由32M的内存,在lilo.conf的启动参数中加上mem=30M,这样linux就认为你的机器只有30M的内存,剩下的2M内存在vremap之后就可以为DMA所用了。

请记住,用vremap映射后的内存,不用时应用unremap释放,否则会浪费页表。

3 中断处理

同处理I/O端口一样,要使用一个中断,必须先向系统登记。
int request_irq(unsigned int irq ,
void(*handle)(int,void *,struct pt_regs *),
unsigned int long flags,
const char *device);

irq: 是要申请的中断。
handle:中断处理函数指针。
flags:SA_INTERRUPT 请求一个快速中断,0 正常中断。
device:设备名。

如果登记成功,返回0,这时在/proc/interrupts文件中可以看你请求的中断。

4一些常见的问题。
对硬件操作,有时时序很重要。但是如果用C语言写一些低级的硬件操作的话,gcc往往会对你的程序进行优化,这样时序就错掉了。如果用汇编写呢,gcc同样会对汇编代码进行优化,除非你用volatile关键字修饰。最保险的办法是禁止优化。这当然只能对一部分你自己编写的代码。如果对所有的代码都不优化,你会发现驱动程序根本无法装载。这是因为在编译驱动程序时要用到gcc的一些扩展特性,而这些扩展特性必须在加了优化选项之后才能体现出来。

关于kernel的调试工具,我现在还没有发现有合适的。有谁知道请告诉我,不胜感激。我一直都在printk打印调试信息,倒也还凑合。关于设备驱动程序还有很多内容,如等待/唤醒机制,块设备的编写等。我还不是很明白,不敢乱说。

欢迎大家批评指正。

1.声明:

安装环境为FC3

爽阿,终于把连放rm的问题解决了,同时修正了很多bug

如果要用mplayer播放流媒体
到live.com 下载流媒体协议支持包,
编译完后将整个目录cp到/ usr/lib下
在编译mplayer的时候,选项 ./configure --enable-live

2.下载:

MPlayer-1.0pre7
http://www4.mplayerhq.hu/MPlayer/releases/MPlayer-1.0pre7.tar.bz2
官方CODES-all
all-20050412.tar.bz2
官方win32_CODES-all
windows-all-20050412.zip
字体
gb2312-ming.tar.bz2
皮肤
Blue-1.4.tar.bz2

以上请到官方主页下载

3. 安装code

tar vjxf all-20050412.tar.bz2
mv all-20050412 /usr/lib/codes
chmod 644 /usr/lib/codes/*
chown root.root /usr/lib/codes/*

unzip windows-all-20050412.zip
mv windows-all-20050412 /usr/lib/wincodes
chmod 644 /usr/lib/wincodes/*
chown root.root /usr/lib/wincodes/*

4.安装MPlayer

tar vjxf MPlayer-1.0pre7.tar.bz2

cd MPlayer-1.0pre7

./configure --prefix=/usr/local/mplayer/ --enable-gui --enable-freetype --with-codecsdir=/usr/lib/codes/ --with-win32libdir=/usr/lib/wincodes/

补充:可以加上--disable-gcc-checking --language=zh_CN

参数说明:
./configure \ #详细说明见./configure --help
--prefix=/usr/local/mplayer/ \ #安装路径
--enable-gui \ #如果你只在命令下用可以不要这个,相信是不会的
--enable-freetype \ #方便以后调整字体(怎么调整这里不说 站里有)
--with-codecsdir=/usr/lib/codes/ \ #我们把下载的插件拷贝到这里
--with-win32libdir=/usr/lib/wincodes/ \ #指定win32代码目录

make

make install

时间较长,有耐心慢慢等吧Smile

cd etc
cp example.conf /usr/local/mplayer/etc/config
cp codecs.conf /usr/local/mplayer/etc/

5.安装字体
tar vjxf gb2312-ming.tar.bz2
mv gb2312-ming /usr/local/mplayer/share/mplayer/font/
cd /usr/local/mplayer/share/mplayer/font/
ln -s gb2312-ming font

6.安装皮肤
tar vjxf Blue-1.4.tar.bz2
mv Blue /usr/local/mplayer/share/mplayer/Skin/
cd /usr/local/mplayer/share/mplayer/Skin/
ln -s Blue default

桌面启动
创建启动器
名称:MPlayer
明令:gmplayer
然后选择一个你喜欢的图标.
也可以在终端用明令gmplayer启动.

优盘在Fedora Core下的默认编码是ascii,因此如果优盘中有中文文件的话就会造成死机,卸载优盘也会显示busy。解决方法是手动添加一个下面的配置文件。如果你的系统编码是utf8那么把下面的iocharset=cp936替换成utf8。

/usr/share/hal/fdi/95userpolicy/storage-policy.fdi

<?xml version="1.0" encoding="ISO-8859-1"?> <!-- -*- SGML -*- -->

<deviceinfo version="0.2">
<device>
<match key="@block.storage_device:storage.removable" bool="true">
<merge key="volume.policy.mount_option.fmask=111" type="bool">true</merge>
<merge key="volume.policy.mount_option.dmask=0" type="bool">true</merge>
<merge key="volume.policy.mount_option.users" type="bool">true</merge>
<merge key="volume.policy.mount_option.iocharset=cp936" type="bool">true</merge>
</match>
<match key="@block.storage_device:storage.hotpluggable" bool="true">
<merge key="volume.policy.mount_option.fmask=111" type="bool">true</merge>
<merge key="volume.policy.mount_option.dmask=0" type="bool">true</merge>
<merge key="volume.policy.mount_option.users" type="bool">true</merge>
<merge key="volume.policy.mount_option.iocharset=cp936" type="bool">true</merge>
</match>
</device>
</deviceinfo>

保存后运行service haldaemon restart重启服务

京ICP备05053527号
经过27次查询历时0.477秒终于生成了此页面
Powered by WordPress & Designed by Felix © 2012