顯示具有 Linux 參考資料篇 標籤的文章。 顯示所有文章
顯示具有 Linux 參考資料篇 標籤的文章。 顯示所有文章

星期六, 4月 19, 2008

Linux 2.6 驱动设计快速入门

引用自: http://dev.csdn.net/article/67/67489.shtm


Linux 2.6 和 2.4 的比较我不想废话,总体来说 2.6 功能更强,但是资源消耗更多。

由于 2.6 内核在驱动框架,底层调用上和 2.4 内核有很多差别,所以本文主要是为程序员提供 2.4 到 2.6 迁移的指导。

2.6 和 2.4 主要的不同在于

• 内核的 API 变化,增加了不少新功能(例如 mem pool )

• 提供 sysfs 用于描述设备树

• 驱动模块从 .o 变为 .ko

移植 hello word

下面是一个最简单的 2.4 驱动:

#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
int init_module(void)
{
printk(KERN_INFO "Hello, worldn");
return 0;
}
void cleanup_module(void)
{
printk(KERN_INFO "Goodbye cruel worldn");
}

2.6的hello world版本!

#include < linux/module.h>
#include < linux/config.h>
#include < linux/init.h>
MODULE_LICENSE("GPL");// 新,否则有 waring, 去掉了 #define MODULE, 自动定义
static int hello_init(void)
{
printk(KERN_ALERT "Hello, worldn");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel worldn");
}
module_init(hello_init);// 必须!!
module_exit(hello_exit); // 必须!!

注意,在 2.4 中 module_init 不是必须的,只要驱动的初始化函数以及析沟函数命名使用了缺省的 init_module 和 cleanup_module

编译生成:

2.4

gcc -D__KERNEL__ -DMODULE -I/usr/src/linux- 2.4.27 /include -O2 -c testmod.c

2.6

makefile 中要有 obj-m:= hello.o

然后:

make -C /usr/src/linux- 2.6.11 SUBDIRS=$PWD modules (当然简单的 make 也可以)

哈哈够简单!!

其他不同:

计数器:

以前 2.4 内核使用 MOD_INC_USE_COUNT 增加计数例如:

void

inc_mod(void)

{

MOD_INC_USE_COUNT;

} /* end inc_mod */

/************************************************************************

* Calculator DEC

************************************************************************/

void

dec_mod(void)

{

MOD_DEC_USE_COUNT;

} /* end dec_mod */

现在这个也过时了 !!

2.6 ,用户函数要加载模块,使用:

int try_module_get(&module);

函数卸载模块使用

module_put()

而驱动中不必显示定义 inc 和 dec

没有 kdev_t 了

现在都流行用 dev_t 了 , 而且是 32 位的

结构体初始化

老版本使用:

static struct some_structure = {

field1: value,

field2: value

};

现在流行用:

static struct some_structure = {

.field1 = value,

.field2 = value,

...

};

malloc.h

要用核态内存?用 <linux/slab.h> 好了,

内存池

内 存管理变化不大,添加了memory pool*(#include<linux/mempool.h> )。(现在什么都是pool,内存 线程 ....)这是为块设备提供的,使用mempool最大的优点是分配内存不会错,也不会等待(怎么这个也和RTEMS学)。对于embed的设备还是有些 用处的。标准调用方式:

mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data);

使用mempool的函数 :

mempool_alloc_t / mempool_free_t

mempool_alloc_slab / mempool_free_slab

mempool_alloc / mempool_free

mempool_resize

结构体赋值

以前,驱动中结构体赋值使用:

static struct some_structure = {

field1: value,

field2: value

};

现在要用:

static struct some_structure = {

.field1 = value,

.field2 = value,

...

};

例如 :

static struct file_operations yourdev_file_ops = {

.open = yourdev_open,

.read = yourdev_read_file,

.write = yourdev_write_file,

};

min() , max()

不少程序员定义自己的 min 和 max 宏,大家也知道宏不安全, Linux 定义了 min 和 max 函数(注意不是模板函数,需要自己制定比较类型!)

原型变化

call_usermodehelper()

request_module()

函数原型变化了,用到的赶快查 !!!

Devfs

唉,还没怎么用,就过时了的技术,这就是 linux 。不是我们落伍,是世界变化快

字符设备

2.6 中主从设备编号不再局限于原来的 8bit

register_chrdev() 可以升级为:

int register_chrdev_region(dev_t from, // 设备号 unsigned count, // 注册号 char *name); // 名称

该函数的动态版本(不知道主设备号时使用)

int alloc_chrdev_region(dev_t *dev, unsigned baseminor,

unsigned count, char *name);

新的 file_operations

register_chrdev_region 没有使用 register_chrdev_region 参数,因为设备现在使用 struct cdev 来定义他在 <linux/cdev.h> 中定义。

struct cdev {

struct kobject kobj;

struct module *owner;

struct file_operations *ops;

struct list_head list;

dev_t dev;

unsigned int count;

};

使用时首先需要分配空间: struct cdev *cdev_alloc(void);

然后对其初始化:

void cdev_init(struct cdev *cdev, struct file_operations *fops);

cdev 使用 kobject_set_name 设置名称:例如:

struct cdev *my_cdev = cdev_alloc(); kobject_set_name(&cdev->kobj, "my_cdev%d", devnum);

此后就可以使用 int cdev_add(struct cdev *cdev, dev_t dev, unsigned count); 将 cdev 加入到系统中。

删除 cdev 使用

void cdev_del(struct cdev *cdev);

对 于没有使用 cdev_add 添加的 cdev 使用 kobject_put(&cdev->kobj); 删除 也就是使用: struct kobject *cdev_get(struct cdev *cdev); void cdev_put(struct cdev *cdev);

太极链;

struct inode -> i_cdev -> cdev

这样就从 inode 链接到 cdev

驱动体系:

/dev 到 /sys

/dev 也过时了,设备文件都跑到 /sys 里了!

# cd /sys

# ls

block bus class devices firmware


星期六, 4月 12, 2008

arm linux 启动流程之 解压内核

引用自: http://blog.csdn.net/dansen_xu/archive/2007/08/13/1740738.aspx

Author-------Dansen-----xzd2734@163.com

从后往前看下编译生成zImage的过程,我们可以找到程序的入口还是那个很重要
链接文件,找到它,生成zImage所在的目录是kernelarcharmbootcompressed
Make过程为....ld -p -X -T vmlinux.lds head.o misc.o head-s3c2410.o piggy.o
libgcc.o -o vmlinux
然后是用二进制工具objcopy把vmlinux制作成可执行的二进制映像文件zImage
这样在我们就去kernelarcharmbootcompressed目录下去找到vmlinux.lds文件
如果没有编译就不会有这个文件,因为它也是在编译过程生成的,由同一目录下的
vmlinux.lds.in生成,打开这个文件
ENTRY(_start)
SECTIONS
{
. = LOAD_ADDR;
_load_addr = .;

. = TEXT_START;
_text = .;

.text : {
_start = .;
*(.start)
*(.text)
........
入口是_start,而且入口就直接定义在这个文件中了
入口直接接着.start段,所以程序开始是从.start段开始执行的
如果看看vmlinux.lds的生成过程就应该能找到LOAD_ADDR和TEXT_START的值
实际上这两个值是由其他两个变量赋给的 ZRELADDR 和 ZTEXTADDR
在kernelarcharmbootMakefile中我们可以找到这两个变量的值
ifeq ($(CONFIG_ARCH_S3C2410),y)
ZTEXTADDR = 0x30008000
ZRELADDR = 0x30008000
endif
所以
LOAD_ADDR = 0x30008000
TEXT_START = 0x30008000
看一下vmlinux.lds吧
ENTRY(_start)
SECTIONS
{
. = 0x30008000;
_load_addr = .;

. = 0;
_text = .;
显然LOAD_ADDR被赋值了0x30008000
看一下TEXT_START怎么成0了,我想这应该是一个偏移吧,偏移是0
所以它还是0x30008000
接着下来就从head.s来开始看代码吧
.section ".start", #alloc, #execinstr
/*
* sort out different calling conventions
*/
.align
start:
.type start,#function
.rept 8
mov r0, r0
.endr

b 1f
.word 0x016f2818 @ Magic numbers to help the loader
.word start @ absolute load/run zImage address
.word _edata @ zImage end address
1: mov r7, r1 @ save architecture ID
这里一定就是程序的入口了,一般汇编程序的含义就看看英文注释就是了
有一个要注意的地方,不是一个汇编文件就是属于一个段的,不是说先执行完了
head.s再去执行head-s3c2410.s,还是要注意链接的段,显然head.s
不一会就开始了另一个段.text
.text
adr r0, LC0
ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}
subs r0, r0, r1 @ calculate the delta offset
而我们的head-s3c2410.s呢
.section ".start", #alloc, #execinstr
__S3C2410_start:
bic r2, pc, #0x1f
add r3, r2, #0x4000 @ 16 kb is quite enough...
还是属于.start段的,所以顺序执行下来时先执行head-s3c2410.s,然后再去执行
.text段。head-s3c2410.s主要是cpu的一些初始化工作。接着下来我们会需要把内核
接压缩,先说说为什么吧。还是注意到上面生成zImage的文件中有一个piggy.o,往上
追寻可以看到是piggy.o由那个真正的内核vmlinux生成的,这个vmlinux才是启动后一直在
运行的内核,原本很大,压缩以后可以方便地放在flash中,当然其实不压缩跳到它的
入口也就可以运行了。解压的内核是准备从LOAD_ADDR = 0x30008000开始的4M空间,会覆盖
我们的当前运行的代码,那样就先把内核解压到我们这个zImage+分配堆栈0x10000的最后
cmp r4, r2 //r4 是LOAD_ADDR=0x30008000
bhs wont_overwrite //r2 是当前代码的最底部 这里当然不会跳转
add r0, r4, #4096*1024 @ 4MB largest kernel size
cmp r0, r5 //r5 也是0x30008000
bls wont_overwrite //不会跳转

mov r5, r2 //r2是(user_stack+4096)在zImage的最后+0x10000
mov r0, r5
mov r3, r7 //machine type
bl decompress_kernel
有了r5,r0,r7作为参数,就可以调用misc.c中的decompress_kernel函数进行解压缩了
这个函数调用的gunzip函数时gcc的库函数,所以在源码中找不到的
解压在r5开始的地方,函数返回的是r0解压得到的长度。这时候我们需要对代码经行调整
add r1, r5, r0 @ end of decompressed kernel
adr r2, reloc_start
ldr r3, LC1 //LC1: .word reloc_end - reloc_start
add r3, r2, r3
1: ldmia r2!, {r8 - r13} @ copy relocation code
stmia r1!, {r8 - r13}
ldmia r2!, {r8 - r13}
stmia r1!, {r8 - r13}
cmp r2, r3 //这里就把从reloc_start到reloc_end这段我们需要的代码放到了
blo 1b //解压内核的最后,而在下面我们会将zImage都覆盖掉
bl cache_clean_flush
add pc, r5, r0 //调到调整后的reloc_start,在decompressed kernel后
reloc_start: add r8, r5, r0 //r5解压内核开始的地方 r0解压内核的长度
debug_reloc_start
mov r1, r4 //r4=0x30008000
1:
.rept 4
ldmia r5!, {r0, r2, r3, r9 - r13} @ relocate kernel
stmia r1!, {r0, r2, r3, r9 - r13}
.endr

cmp r5, r8

blo 1b //这样就又把解压的真正内核移到了0x30008000处

call_kernel: bl cache_clean_flush

bl cache_off

mov r0, #0

mov r1, r7 @ restore architecture number

mov pc, r4 @ call kernel

上面就是跳到0x30008000这里去执行真正的内核了吧


星期六, 3月 29, 2008

Linux 下透過 GPRS 上網


引用自: http://blog.linym.net/archives/171

基本需求:

  • 可連上 GPRS 的硬體設備
  • 編譯 Kernel 使其支援 PPP
  • 擁有 pppd、chat 兩個程式
  • PPP 連線的 script 檔案

1. 可連上 GPRS 的硬體設備
可以是專用的 GPRS Modem 或是手機,不過當然要先配置好相關設定及驅動,我是使用 Wavecom Q2403A 這個 GSM/GPRS 模組透過 com port 來實驗。

2. 編譯 Kernel 使其支援 PPP
# make menuconfig
選擇 Device Drivers —> Network device support —> PPP (point-to-point protocol) support,底下的子項目如果不確定就全選即可。
核心更新後請檢查 /dev/ppp 是否存在,若無可用 mknod /dev/ppp c 108 0 建立。

3. 擁有 pppd、chat 兩個程式
如果是一般 PC 版本應該都已經有內建了;Embedded 平台則可以考慮使用 busybox,裡面也有包含這兩個程式;再不然就自行下載 source code 來編譯。

4. PPP 連線的 script 檔案
建立 script 來做 PPP 連線,通常可以在 /usr/share/doc/ppp/examples/scripts 底下找到 ppp-on、ppp-off、ppp-on-dialer 三個範例檔案,不過用範例檔的設定不一定能成功,請參考我的檔案,已測試中華電信可以成功。

相關的設定及原理可以參考:



移植KVM到arm-linux


引用自: http://tech.jayya.com/2007/11/28/porting-kvm-to-armlinux.html

上午kvm能正常在板子上运行了,并且还解决了preverify的问题(感谢一下陶老师的帮助和提醒),今天一并完整的写出来整个过程吧

  1. 建立交叉编译环境:
    • 下载xscale-arm-linux-toolchain.tgz,tar zxfv xscale-arm-linux-toolchain.tgz,将解压后的文件在/usr/local/arm-linux下,将他击入当前的路径下, 比如修改你的.bash_profile,PATH=$PATH:/usr/local/arm-linux/bin,退出,在登陆使之生效,输入arm -linux-gcc -v可以查看gcc的版本;
    • 很抱歉,写得时候漏写了一个重要的设置步骤,多谢雨里来的提醒,现补充如下:我们还需要安装交叉编译环境的标准C函数库,进入系统的/usr/目录并解压库函数软件包
      [user@host]# cd /usr
      [user@host]# tar zvxf /root/gnupro-bin-glibc2.2.4.tgz
      上述命令会在系统目录/usr/cygnus/xscale-020726/H-i686-pc-linux-gnulibc2.4/目录下建立一系列的子目录和文件,它们是交叉编译时需要的标准C函数库和各种工具。
  2. 下载sun的cldc的src
  3. 解压,里面包含有相关的源码。我这里使用了j2me_cldc-1_0_4-src-winunix.zip,j2me_cldc-1_1-fcs -src-winunix.zip这两个都可以在sun的网站下载,不过推荐大家抓紧时间下载,因为今天去下载midp2.0就已经不能下载了(sun 阿,令人失望!)。

  4. 安装jdk并设置后相关路径
  5. 这里不推荐使用jdk1.5,因为他对中文的支持有问题,使用1.4或者更低的都可以(希望可以在sun的官方找到)
    我使用的是1.4.2_01。修改.bash_profile或者/etc/profile添加PATH或者CLASSPATH。这里说一些为什么要用到 jdk,主要是要编译api里面的java文件,当然我们只一致kvm就不需要这个了,详细地大家可以看看解开的文件cldc文件夹里面的make文件

  6. 修改make文件
  7. 进入j2me_cldc/kvm/VmUnix/build/,vi修改Makefile,主要修改将原来使用的x86的编译器GCC改为我们的 arm-linux-gcc,这样才能得到我们的所需,我主要是修改下面的句子:ifeq ($(GCC), true) CC =arm-linux-gcc,另外我又在文件的开始指定export PLATFORM=linux,当然你也可以在make的时候加参数PLATFORM=linux。

  8. 编译
  9. 先编译preverify,进入tools/preverfiyer/build/linux下,make,这里没有必要对这个makefile进 行修改,况且你的编译平台是x86,应该不能进行修改,否则编译出来的preverify是不运行的,那么kvm的编译也将会不能通过。
    编译kvm,进入kvm/VmUnix/build,make,就可以得到kvm,注意这个是在kvm的build目录下生成的,file kvm会的到以下信息:kvm: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.4.18, dynamically linked (uses shared libs), not stripped

  10. 在板子上运行并进行相应的测试
  11. 将得到的kvm,烧到板子上,x86下kvm运行所需文件: libm.so.6 => /lib/tls/libm.so.6 (0xb75c0000)
    libnsl.so.1 => /lib/libnsl.so.1 (0xb75ab000)
    libc.so.6 => /lib/tls/libc.so.6 (0xb7473000)
    /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb75eb000)
    大家可以参考以下,以便能使板子环境符合要求.更改属性为a+x,运行kvm,没有任何参数的情况下将会出现以下信息:ALERT: Must provide class name
    Usage: kvm < -options>
    Options:
    -version
    -classpath
    -heapsize (e.g. 65536 or 128k or 1M)

  12. 运行helloworld并正确使用preverify
  13. 这一步准确来说不是移植要做的事情,因为这是j2me开发者要注意的事情.我们先来看看kvm的执行.class 文件的机制:
    portingVerifiera.gif
    如图示:我们javac编译完的生成的.class文件必须经过preverify预验证后才能被kvm正确的执行,否则会出现 java/class/Verifyerror,为什么要采用预验证机制,是因为sun考虑到嵌入式设备资源有限,所以将jvm的庞大字节码验证机制进行 严重的”缩水”,也就用了一个轻量级机制
    来保证安全.说了这么多,讲讲具体怎么使用(这个流程开发j2me程序的人应该都知道点,虽然大家都不常用这个来开发).
    1编译javac -classpath kvm/classes hello.java 这里kvm/classes指你编译生成的classes文件夹
    2预验证preverify -classpath kvm/classes -d . mydir
    preverify有几个参数: -classpath 同上 -d 输出验证后的class文件,默认的是当前目录下的output文件夹下,还有检测是否使用了 finalizers ,float,native methods等
    将检验后的classs文件放到板子上,就可以执行了!


CLDC 1.0.4 Fix for Red_Hat

引用自: http://forum.java.sun.com/thread.jspa?threadID=468349


The following works for J2ME CLDC 1.1 (it may be the same for 1.0.4, I didn't try that).

If you don't care to verify that you are having the same problem, skip ahead a bit to what I did to fix the problem I was having.

Rebuild the kvm with debugging on. Goto the j2me_cldc/build/linux directory
edit Makefile around the 4th line add a line that says "export DEBUG=true"
If you have previously run make then do "make clean" then "make"

Im assuming your PATH and CLASSPATH are set appropriately so you can simply prevreify a class by typing "preverify HelloWorld" and you see "Segmentation fault"

At this point run "preverify -verbose HelloWorld" and you will see that it is indeed running and loading classes but it craps out after a few classes.

To run the debugger on this type "gdb --args preverify HelloWorld"
at the (gdb) prompt type "run". You will see
"Program received signal SIGSEGV, Segmentation fault."
Type bt (or backtrace) in the debuger. If you see something like this

#0 0x4207c1ac in memcpy () from /lib/tls/libc.so.6
#1 0x080520ef in utf2native ()
#2 0x0805b9ab in Locked_InitializeClass ()
...

then you either didn't get the "export DEBUG=true" part right or your didn't "make clean" or something else happened. You should see something more like this (more detail):

#0 0x4207c1ac in memcpy () from /lib/tls/libc.so.6
#1 0x080520ef in utf2native (from=0x8069740 "?_006b", to=0xbfff6ba0 "",
buflen=-1073779800) at ../../src/convert_md.c:100
#2 0x0805b9ab in Locked_InitializeClass (cb=0x8069740, detail=0xbfff6ba0)
at ../../src/classresolver.c:685
#3 0x0805b57a in InitializeClass (cb=0x8069740, detail=0xbfff6ba0)
at ../../src/classresolver.c:506
#4 0x0805c2af in InitializeAndResolveClass (cb=0x8069740, resolve=FALSE)
at ../../src/classresolver.c:991
...

what this is telling you is that the glibc memcpy function is where the segfault happened. memcpy is trying to copy memory but it looks like the buffer length (buflen) is bogus. memcpy is called from utf2native which is called by Locked_InitializerClass in classresolver.c

Quit the debugger (“quit”). Goto j2me_cldc/tools/preverifier/src/

Look at the file classresolver.c, at the Locked_InitializerClass function, around line 572. See the lines

char buff[BUFSIZ];
char *nativeName = &buff[0];

remember them. Look down to line 685, see the lines

utf2native(cbSuperName(cb), nativeName, BUFSIZ);
super = FindClassFromClass(ee, nativeName, FALSE, cb);

Nowehere else in classresolver.c is this buff or *nativeName used (so Im assuming we are free to mess with without significantly impacting anything else). What this is doing is creating a buffer on the stack that is BUFSIZ big (8192 bytes). utf2native gets the buffer and size and calls memcpy. Im guessing that because the preverifier seems to be loading classes recursively that there are lots of buffers like this on the stack hanging around and Im running out of stack space which results in a segmentation fault. I don’t know why EVERYONE running linux doesn’t have this problem.

How to fix this? We are now going to change the program to put the buffer in dynamic memory not on the stack.

In classresolver.c, around line 572, change the buff and nativeName declarations to this (comment out the old ones, put in new ones):

//char buff[BUFSIZ];
//char *nativeName = &buff[0];
char *buff;
char *nativeName;

The skip down to around line 685. Add a few lines around the call to utf2native to look like this:

buff = (char*)malloc(BUFSIZ);
if (buff == NULL)
fprintf( stderr, "Bogus, malloc failed." );
nativeName = buff;

utf2native(cbSuperName(cb), nativeName, BUFSIZ);
super = FindClassFromClass(ee, nativeName, FALSE, cb);

free(buff);

We added 4 lines before the utf2native call and the free(buff) after it. What we are doing is allocating dynamic memory for the buffer (not on the stack this time) and freeing it right after we are done with it.

Save your work. Now "make clean" and "make". When you try to run the preverifier, turn on -verbose. You will get the preverifier to run a little further but it may still die with a segmentation fault, this time in a different place, same problem. I found four c files that declare and use buff[BUFSIZ] (classloader.c, classresolver.c, file.c and jar_support.c). I also found that if you have your CLASSPATH is set to point to the j2me_cldc/api/classes directory or j2me_cldc/api/classes.zip file you will get the same error but different calling functions. My CLASSPATH originally pointed to the classes directory and the classloader.c and classresolver.c were crapping out. I switched to having the CLASSPATH point to the classes.zip file and then the jar_support.c functions were the problem. I fixed all 4 just to be sure. Im not guaranteeing this will solve your problem. But it solved mine.

Dan

星期六, 1月 05, 2008

有趣的主題

Linux Package

Packman: http://packman.links2linux.org/ : 此網站存放 Suse Linux 軟體套件,讓使用者能很輕易地安裝與移除軟體。
LinuxToy: http://linuxtoy.org/

Ericsk 的 Ubuntu 筆記: http://blog.xuite.net/ericsk0313/ubuntu

Ericsk 的個人網站: http://blog.ericsk.org/

阿駕零零壹 © 學習筆記: http://twntwn.info/blog/ajer001

Linux 的粉絲衝上浪頭 2.0: http://blog.ilc.edu.tw/blog/blog/673/

Device Driver

The GNU/Linux "usbnet" Driver Framework: http://www.linux-usb.org/usbnet/

JVM

Java Midlets Tutorial (PocketPC): http://www.pocketpcmag.com/blogs/index.php?blog=3&p=644&more=1&c=1&tb=1&pb=1
JVMs, JDKs and JREs: http://java-virtual-machine.net/other.html

Network

Boa + PHP for uClinux: http://www.menie.org/georges/uClinux/boa-php.html

PHP for IIS: http://blog.raienet.com/188?TSSESSION=5d9fb85e5c0162885e12924128ef1838


Web Page Editor

AceHTML: http://software.visicommedia.com/en/products/acehtmlfreeware/
HomeSite: http://www.adobe.com/products/homesite/
NotePad++: http://notepad-plus.sourceforge.net/tw/site.htm
EditPlus: http://www.editplus.com/
Crimson: http://www.crimsoneditor.com/

Forum

大鳥的實驗室: http://www.armlabs.com/phpBB2/viewforum.php?f=7

星期日, 5月 13, 2007

Ubuntu 在 Sony Ecrisson P990i 啟動

Ubuntu 在 Sony Ecrisson P990i 啟動!