打印
[嵌入式linux]

开始学linux驱动,三个月熟悉,立帖为证!每日汇报进展

[复制链接]
楼主: tiger84
手机看帖
扫描二维码
随时随地手机跟帖
61
tiger84|  楼主 | 2009-12-31 18:58 | 只看该作者 回帖奖励 |倒序浏览
摘要:《ARM嵌入式Linux系统开发详解》第20章Linux内核移植,本章的重点是Linux内核移植,需要考虑硬件平台的差异,涉及较多知识。本节为大家介绍Linux内核移植要点。
第20章  Linux内核移植

软件移植的概念简单地说就是让一套软件在指定的硬件平台上正常运行。移植至少包括了两个不同的硬件或者软件平台。对于应用软件来说,移植主要考虑操作系统的差异,重点在修改系统调用。本章的重点是Linux内核移植,需要考虑硬件平台的差异,涉及较多知识。主要内容如下:

Linux内核移植要点;

内核体系结构框架;

从现有代码移植内核。

20.1  Linux内核移植要点

Linux的代码完全开放以及其良好的结构设计非常适于嵌入式系统。移植Linux系统包括内核、程序库和应用程序,其中最主要的就是内核移植。由于Linux内核的开放性,出现了许多针对嵌入式硬件系统的内核版本,其中著名的包括μcLinux、RT-Linux等。

Linux本身对内存管理(MMU)有很好的支持。因此,在移植的时候首先要考虑到目标硬件平台是否支持MMU。以ARM平台为例,ARM7内核的CPU不支持MMU,无法直接把Linux内核代码移植到ARM7核的硬件平台上。μcLinux是专门针对ARM7这类没有MMU的硬件平台上设计的,它精简了MMU部分代码。本书的目标平台是S3C2440A,该处理器基于ARM9核,支持MMU,可以直接移植Linux 2.6版本的内核代码。

一个硬件平台最主要的是处理器,因此在移植之前需要了解目标平台的处理器。ARM处理器内部采用32位的精简指令架构(RISC),核心结构设计相对简单,有低耗电量的优势,被广泛应用到各种领域。下面介绍一下移植Linux内核对硬件平台需要考虑的几个问题。

1.目标平台

目标平台包括了嵌入式处理器和周围器件,处理器可能整合了一些周围器件,如中断控制器、定时器、总线控制器等。在移植之前需要确定被移植系统对外部设备和总线的支持情况。本书的ARM开发板采用mini2440平台,在S3C2440A外围连接了许多外围设备,包括NOR Flash存储器、NAND Flash存储器、网络接口芯片、USB控制器等。在S3C2440A处理器内部集成了许多常用的控制器以及嵌入式领域常用的总线控制器。对于移植Linux内核来说,操作处理器内部的控制器要比外部的设备容易得多。

2.内存管理单元(MMU)

前面提到过MMU,对于现代计算机来说,MMU负责内存地址保护、虚拟地址和物理地址相互转换工作。在使用MMU的硬件平台上,操作系统通过MMU可以向应用程序提供大于实际物理内存的地址空间,使应用程序获得更高性能。Linux的虚拟内存管理功能就是借助MMU实现的。在移植的时候要考虑目标平台的MMU操作机制,这部分代码是较难理解的,最好能在相似代码基础上修改,降低开发难度。

3.内存映射

嵌入式系统大多都没有配备硬盘,外部存储器只有Flash,并且系统内存也非常有限。内存控制器(Memory Controller)负责内部和外部存储器在处理器地址空间的映射,由于硬件预设的地址不同导致每种平台内存映射的地址也不同。在移植时需要参考硬件的用户手册,得到内存地址的映射方法。

4.存储器

由于嵌入式系统多用Flash存储器作为存储装置。对于文件系统来说,在PC流行的ext2、ext3文件系统在嵌入式系统无法发挥作用。幸好Linux支持许多文件系统,针对Flash存储器可以使用JFFS2文件系统。在移植的时候,不必要的文件系统都可以裁剪掉。
20.2  平台相关代码结构

移植Linux是一项复杂的工作,不仅对目标硬件平台的资源要充分了解,还需要了解Linux内核代码,尤其是与体系结构有关的部分。本节从内核的平台相关代码入手,先介绍内核的工作原理,然后讲解如何移植一个普通的Linux内核到S3C2440A为目标平台的开发板。

在第15章介绍过Linux内核代码结构,与平台相关的代码主要存放在arch目录下,对应的头文件在include目录下。以ARM平台为例,在arch目录下有一个arm子目录,存放所有与ARM体系有关的内核代码。

Linux内核代码目录基本是按照功能块划分的,每个功能块的代码存放在一个目录下。如mm目录存放内存管理单元相关代码;ipc存放了进程间通信相关的代码;kernel存放进程调度相关代码等。

arch目录下每个平台的代码都采用了与内核代码相同的目录结构。以arch/arm目录为例,该目录下mm、lib、kernel、boot目录与内核目录下对应目录的功能相同。此外,还有一些以字符串mach开头的目录,对应不同处理器特定的代码。从arch目录结构可以看出,平台相关的代码都存放到arch目录下,并且使用与内核目录相同的结构。使用SourceInsight工具可以看到许多的同名函数,原因就是内核代码调用的函数是平台相关的,每个平台都有自己的实现方法。对于内核来说,使用相同的名字调用,通过编译选项选择对应平台的代码。

移植内核到新的平台主要任务是修改arch目录下对应体系结构的代码。一般来说,已有的体系结构提供了完整的代码框架,移植只需要按照代码框架编写对应具体硬件平台的代码即可。在编写代码过程中,需要参考硬件的设计包括图纸、引脚连线、操作手册等。

20.3  建立目标平台工程框架

Linux内核2.6版本已经对ARM处理器有很好的支持,并且对三星公司的S3C2440提供一定支持。但是,嵌入式硬件系统的差别很大,移植Linux内核到新的开发板仍然需要修改或者增加针对特定硬件的代码。

Linux内核使用了复杂的工程文件结构,向内核添加新的代码文件需要让内核工程文件知道才行。对于ARM处理器来说,相关的文件都存放在arch/arm目录下:

在arch/arm目录下有许多的子目录和文件。其中以mach字符串开头的子目录存放某种特定的ARM内核处理器相关文件,如mach-s3c2410目录存放S3C2410、S3C2440相关的文件。另外,在mach目录下还会存放针对特定开发板硬件的代码。

boot目录存放了ARM内核通用的启动相关的文件;kernel是与ARM处理器相关的内核代码;mm目录是与ARM处理器相关的内存管理部分代码。以上这些目录的代码一般不需要修改,除非处理器有特殊的地方,只要是基于ARM内核的处理一般都使用相同的内核管理代码。

Kconfig文件是内核使用的选项菜单配置文件,在执行make menuconfig命令的时候会显示出菜单。Kconfig文件描述了菜单项,包括菜单项的属性,与其他菜单项的依赖关系等。通过修改Kconfig文件可以告知内核有关编译的宏,内核顶层的Makefile通过Kconfig文件知道需要编译哪些文件,以及连接关系。

Makefile文件是一个工程文件,每个体系结构的代码中都有该文件。Makefile文件描述了当前体系结构目录下需要编译的文件以及对应的宏的名称。内核顶层Makefile通过Kconfig文件配置的宏,结合Makfile定义的宏关联的代码文件去链接用户编写的代码。

通过分析ARM处理器体系目录的结构,加入针对mini2440开发板的代码主要是修改Kconfig文件、Makeifle文件、以及向mach-s3c2410目录加入针对特定硬件的代码。

使用特权

评论回复
62
tiger84|  楼主 | 2009-12-31 19:01 | 只看该作者
20.3.1  加入编译菜单项

修改arch/arm/mach-s3c2410/Kconfig文件,在endmenu之前加入下面的内容:

87 config ARCH_MINI2440     // 开发板名称宏定义  88   bool "mini2440"        // 开发板名称  89   select CPU_S3C2440     // 开发板使用的处理器类型  90   help  91     Say Y here if you are using the mini2440.    // 帮助信息 在笔者的机器上是在第87行加入,读者可以根据自己机器的配置在正确位置加入配置代码。Kconfig文件与开发板有关的代码定义在startmenu和endmenu之间,使用config关键字标示一个配置选项。使用config配置的选项会出现在make menuconfig的菜单项中。

20.3.2  设置宏与代码文件的对应关系

在设置宏与代码文件对应关系之前,首先建立一个空的代码文件。在arch/arm/mach-s3c 2410目录下建立mach-mini2440.c文件,用于存放与mini2440开发板相关的代码。

建立mach-mini2440.c文件后,修改arch/arm/mach-s3c2410/Makefile文件,在文件最后加入mach-mini2440.c文件的编译信息:

43 obj-$(CONFIG_ARCH_MINI2440) += mach-mini2440.o 在笔者的机器上是在第43行加入CONFIG_ARCH_MINI2440宏,对应了mach-mini 2440.o目标文件。make工具在解析该Makefile的时候,会找到mach-mini2440.o目标文件对应的mach-mini2440.c文件并且编译,同时会建立一个名为obj-CONFIG_ARCH_ MINI2440的宏。

注意:在Makefile中设置的宏名称是有规则的,要使用CONFIG开头,并且后面要与Kconfig菜单项配置的名称对应,否则在编译内核的时候无法找到对应的代码文件。

20.3.3  测试工程框架

工程框架配置修改完毕后,需要进行简单的测试,根据测试结果判断框架是否搭建成功。回到内核代码顶层目录,输入make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig命令,出现内核设置图形界面。

在内核配置界面选择Load an Alternate Configuration File菜单,进入后输入"arch/arm/ configs/s3c2410_defconfig"命令,确定后会加载s3c2410默认的配置文件。加载默认配置文件的好处是已经经过验证,用户只需要在默认配置文件的基础上修改自己的配置,减轻了配置的工作量。

加载默认配置文件后,可以开始配置新增加的菜单。进入System Types菜单项,打开S3C24XX Implementations菜单,出现一个目标开发板的列表:

[ ] Simtec Electronics BAST (EB2410ITX)
[ ] IPAQ H1940  [ ] Acer N30
[ ] SMDK2410/A9M2410  
[ ] SMDK2440  
[ ] AESOP2440  
[ ] QQ2440/mini2440  
[ ] Thorcom VR1000  
[ ] HP iPAQ rx3715  
[ ] NexVision OTOM Board  
[ ] NexVision NEXCODER 2440 Light Board  
[ ] mini2440
列表最后一项是在20.3.1节中添加的mini2440菜单项。把光标移到mini2440菜单项使用回车选中。选择mini2440开发板完毕后,保存退出内核配置界面。在命令行输入"make ARCH=arm CROSS_COMPILE=arm-linux- bzImage"命令编译内核代码。请注意,此时编译内核代码可能会有好多的错误,并且不会编译通过,问题是虽然建立了目标板工程框架,但是在源代码文件中没有任何内容。

arch/arm/kernel/traps.c: In function '__bug':  arch/arm/kernel/traps.c:627: warning: 'noreturn' function does return  kernel/intermodule.c:179: warning: 'inter_module_register' is deprecated (declared at kernel/intermodule.c:38)  kernel/intermodule.c:180: warning: 'inter_module_unregister' is deprecated (declared at kernel/intermodule.c:79)  kernel/intermodule.c:182: warning: 'inter_module_put' is deprecated (declared at kernel/intermodule.c:160)  fs/yaffs2/yaffs_fs.c:198: warning: initialization from incompatible pointer type  fs/yaffs2/yaffs_fs.c:228: warning: initializationfrom incompatible pointer type  fs/yaffs2/yaffs_fs.c:229: warning: initialization from incompatible pointer type  fs/yaffs2/yaffs_fs.c: In function 'yaffs_proc_write':  fs/yaffs2/yaffs_fs.c:1865: warning: 'len' might be used uninitialized in this function  fs/yaffs2/yaffs_fs.c: At top level:  fs/yaffs2/yaffs_fs.c:1305: warning: 'yaffs_do_sync_fs' defined but not used  fs/yaffs2/yaffs_guts.c: In function 'yaffs_ObjectHasCachedWriteData':  fs/yaffs2/yaffs_guts.c:2997: warning: unused variable 'cache'  fs/yaffs2/yaffs_guts.c: In function 'yaffs_Scan':  fs/yaffs2/yaffs_guts.c:4490: warning: unused variable 'hl'  fs/yaffs2/yaffs_qsort.c:77:1: warning: "min" redefined  In file included from fs/yaffs2/yportenv.h:35,                   from fs/yaffs2/yaffs_qsort.c:30:  include/linux/kernel.h:243:1: warning: this is the location of the previous definition  drivers/char/keyboard.c:1018:2: warning: #warning "Cannot generate rawmode keyboard for your architecture yet."  drivers/net/dm9000x.c: In function 'dmfe_probe':  drivers/net/dm9000x.c:288: warning: assignment makes integer from pointer without a cast  drivers/serial/serial_core.c:2427: warning: 'uart_register_port' is deprecated (declared at drivers/serial/serial_core.c:2348)  drivers/serial/serial_core.c:2428: warning: 'uart_unregister_port' is deprecated (declared at drivers/serial/serial_core.c:2405)

从报错信息看,主要集中在函数未定义和函数重定义两种错误。在编译的时候提示出错的函数名称需要关注,可能需要在新增的代码文件中重新定义或者调用。内核编译会出错退出,报错信息如下:
arm-linux-ld:arch/arm/kernel/vmlinux.lds:815: parse error  make: *** [.tmp_vmlinux1] Error 1

该信息提示解析arch/arm/kernel/vmlinux.lds文件第815行出错,出现该错误表示已经完成内核代码编译,在链接代码时产生问题,在后面章节会详细分析出错原因。

到目前为止,向内核新增的代码框架已经可以正常工作,第20.4节将介绍如何编写对应开发板的代码。

使用特权

评论回复
63
123654789| | 2009-12-31 19:03 | 只看该作者
楼上tiger同志
你不用上班啊??
这么多时间在学习的 啊???

使用特权

评论回复
64
tiger84|  楼主 | 2009-12-31 19:04 | 只看该作者
其实我也一直很纳闷?一般的CPU不都是自带了吗?为什么还需要专门的时钟芯片呢?

至于我为什么要?呵呵,就是练手而已。

产品中也打算加时钟芯片。

使用特权

评论回复
65
tiger84|  楼主 | 2009-12-31 19:06 | 只看该作者
后来想想,专门的时钟芯片是否会可靠点?或者说功耗小点?
希望高手可以解惑。

有一点,可以肯定的是,我打算使用一个ram大一点的时钟芯片,用于以后的产品加密。

使用特权

评论回复
66
tiger84|  楼主 | 2009-12-31 19:09 | 只看该作者
呵呵,现在的产品属于预研阶段,前段时间预研完,所以这大半个月时间很多。下个月就没这些时间了,要开始产品了

使用特权

评论回复
67
123654789| | 2009-12-31 19:11 | 只看该作者
楼上tiger84 同志
给个DS1302驱动看看
我准备要用这个芯片
谢谢

使用特权

评论回复
68
tiger84|  楼主 | 2009-12-31 19:11 | 只看该作者
时间的确很多。
我6点下班,晚上12点睡觉,吃饭半个小时,休息半个小时,跑步洗澡洗衣服1个小时,还有4个小时可以学习,哈哈。

使用特权

评论回复
69
tiger84|  楼主 | 2009-12-31 19:12 | 只看该作者
大哥,我还没有弄完,打算元旦假期弄的,等元旦过了,若弄出来了,俺贴出来。

使用特权

评论回复
70
123654789| | 2009-12-31 19:14 | 只看该作者
现在就给拉
给个read write程序拉

使用特权

评论回复
71
tiger84|  楼主 | 2009-12-31 19:15 | 只看该作者
今天晚上去理个光头,看过年头发能长多长?
然后看电影,
明天好好休息。

使用特权

评论回复
72
tiger84|  楼主 | 2009-12-31 19:21 | 只看该作者
本帖最后由 tiger84 于 2009-12-31 19:22 编辑

晕,大哥,我没弄出来啊。这是参考源代码的,还没试验呢,不知道对错。等我元旦后再说吧。它的read write都放到class里面去了,基本上不用管。
/*
* Dallas DS1302 RTC Support
*
*  Copyright (C) 2002  David McCullough
*  Copyright (C) 2003 - 2007  Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License version 2.  See the file "COPYING" in the main directory of
* this archive for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/time.h>
#include <linux/rtc.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/bcd.h>
//#include <asm/rtc.h>
#define DRV_NAME "rtc-ds1302"
#define DRV_VERSION "0.1.0"
#define RTC_CMD_READ 0x81  /* Read command */
#define RTC_CMD_WRITE 0x80  /* Write command */
#define RTC_ADDR_RAM0 0x20  /* Address of RAM0 */
#define RTC_ADDR_TCR 0x08  /* Address of trickle charge register */
#define RTC_ADDR_YEAR 0x06  /* Address of year register */
#define RTC_ADDR_DAY 0x05  /* Address of day of week register */
#define RTC_ADDR_MON 0x04  /* Address of month register */
#define RTC_ADDR_DATE 0x03  /* Address of day of month register */
#define RTC_ADDR_HOUR 0x02  /* Address of hour register */
#define RTC_ADDR_MIN 0x01  /* Address of minute register */
#define RTC_ADDR_SEC 0x00  /* Address of second register */
#define RTC_RESET 0x1000
#define RTC_IODATA 0x0800
#define RTC_SCLK 0x0400
#ifdef CONFIG_SH_SECUREEDGE5410
#include <asm/snapgear.h>
#define set_dp(x) SECUREEDGE_WRITE_IOPORT(x, 0x1c00)
#define get_dp() SECUREEDGE_READ_IOPORT()
#else
//#error "Add support for your platform"
#include <mach/gpio.h>
#endif
struct ds1302_rtc {
struct rtc_device *rtc_dev;
spinlock_t lock;
};
static void ds1302_sendbits(unsigned int val)
{
int i;
for (i = 8; (i); i--, val >>= 1)
{
     /*
  set_dp((get_dp() & ~RTC_IODATA) | ((val & 0x1) ?
   RTC_IODATA : 0));
  set_dp(get_dp() | RTC_SCLK); // clock high
  set_dp(get_dp() & ~RTC_SCLK); // clock low
  */
  at91_set_gpio_value(AT91_PIN_PA26,((val & 0x1)?1:0));  
        at91_set_gpio_value(AT91_PIN_PA28,1);
        at91_set_gpio_value(AT91_PIN_PA28,0);
  
}
}
static unsigned int ds1302_recvbits(void)
{
unsigned int val;
int i;
for (i = 0, val = 0; (i < 8); i++) {
/*
  val |= (((get_dp() & RTC_IODATA) ? 1 : 0) << i);
  set_dp(get_dp() | RTC_SCLK); // clock high
  set_dp(get_dp() & ~RTC_SCLK); // clock low
  */
  val |=( ( (at91_get_gpio_value(AT91_PIN_PA26))?1:0 ) << i );
        at91_set_gpio_value(AT91_PIN_PA28,1);
        at91_set_gpio_value(AT91_PIN_PA28,0);
}
return val;
}
static unsigned int ds1302_readbyte(unsigned int addr)
{
unsigned int val;
// set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
   
    at91_set_gpio_value( AT91_PIN_PA26,0);
    at91_set_gpio_value( AT91_PIN_PA27,0);
    at91_set_gpio_value( AT91_PIN_PA28,0);
// set_dp(get_dp() | RTC_RESET);
    at91_set_gpio_value( AT91_PIN_PA27,1);
   
ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_READ);
val = ds1302_recvbits();
// set_dp(get_dp() & ~RTC_RESET);
    at91_set_gpio_value( AT91_PIN_PA27,0);
return val;
}
static void ds1302_writebyte(unsigned int addr, unsigned int val)
{
//set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
at91_set_gpio_value( AT91_PIN_PA26,0);
    at91_set_gpio_value( AT91_PIN_PA27,0);
    at91_set_gpio_value( AT91_PIN_PA28,0);
   
//set_dp(get_dp() | RTC_RESET);
at91_set_gpio_value( AT91_PIN_PA27,1);

ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_WRITE);
ds1302_sendbits(val);
//set_dp(get_dp() & ~RTC_RESET);
at91_set_gpio_value( AT91_PIN_PA27,0);
}
static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct ds1302_rtc *rtc = dev_get_drvdata(dev);
spin_lock_irq(&rtc->lock);
tm->tm_sec = BCD2BIN(ds1302_readbyte(RTC_ADDR_SEC));
tm->tm_min = BCD2BIN(ds1302_readbyte(RTC_ADDR_MIN));
tm->tm_hour = BCD2BIN(ds1302_readbyte(RTC_ADDR_HOUR));
tm->tm_wday = BCD2BIN(ds1302_readbyte(RTC_ADDR_DAY));
tm->tm_mday = BCD2BIN(ds1302_readbyte(RTC_ADDR_DATE));
tm->tm_mon = BCD2BIN(ds1302_readbyte(RTC_ADDR_MON)) - 1;
tm->tm_year = BCD2BIN(ds1302_readbyte(RTC_ADDR_YEAR));
if (tm->tm_year < 70)
  tm->tm_year += 100;
spin_unlock_irq(&rtc->lock);
dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
  "mday=%d, mon=%d, year=%d, wday=%d\n",
  __func__,
  tm->tm_sec, tm->tm_min, tm->tm_hour,
  tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
if (rtc_valid_tm(tm) < 0)
  dev_err(dev, "invalid date\n");
return 0;
}
static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct ds1302_rtc *rtc = dev_get_drvdata(dev);
spin_lock_irq(&rtc->lock);
/* Stop RTC */
ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) | 0x80);
ds1302_writebyte(RTC_ADDR_SEC, BIN2BCD(tm->tm_sec));
ds1302_writebyte(RTC_ADDR_MIN, BIN2BCD(tm->tm_min));
ds1302_writebyte(RTC_ADDR_HOUR, BIN2BCD(tm->tm_hour));
ds1302_writebyte(RTC_ADDR_DAY, BIN2BCD(tm->tm_wday));
ds1302_writebyte(RTC_ADDR_DATE, BIN2BCD(tm->tm_mday));
ds1302_writebyte(RTC_ADDR_MON, BIN2BCD(tm->tm_mon + 1));
ds1302_writebyte(RTC_ADDR_YEAR, BIN2BCD(tm->tm_year % 100));
/* Start RTC */
ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) & ~0x80);
spin_unlock_irq(&rtc->lock);
return 0;
}
static int ds1302_rtc_ioctl(struct device *dev, unsigned int cmd,
       unsigned long arg)
{
switch (cmd) {
#ifdef RTC_SET_CHARGE
case RTC_SET_CHARGE:
{
  struct ds1302_rtc *rtc = dev_get_drvdata(dev);
  int tcs_val;
  if (copy_from_user(&tcs_val, (int __user *)arg, sizeof(int)))
   return -EFAULT;
  spin_lock_irq(&rtc->lock);
  ds1302_writebyte(RTC_ADDR_TCR, (0xa0 | tcs_val * 0xf));
  spin_unlock_irq(&rtc->lock);
  return 0;
}
#endif
}
return -ENOIOCTLCMD;
}
static struct rtc_class_ops ds1302_rtc_ops = {
.read_time = ds1302_rtc_read_time,
.set_time = ds1302_rtc_set_time,
.ioctl  = ds1302_rtc_ioctl,
};
static int __devinit ds1302_rtc_probe(struct platform_device *pdev)
{
struct ds1302_rtc *rtc;
int ret;
/* Reset */
//set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
    at91_set_gpio_value( AT91_PIN_PA26,0);
    at91_set_gpio_value( AT91_PIN_PA27,0);
    at91_set_gpio_value( AT91_PIN_PA28,0);
/* Write a magic value to the DS1302 RAM, and see if it sticks. */
ds1302_writebyte(RTC_ADDR_RAM0, 0x42);
if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42)
  return -ENODEV;
rtc = kzalloc(sizeof(struct ds1302_rtc), GFP_KERNEL);
if (unlikely(!rtc))
  return -ENOMEM;
spin_lock_init(&rtc->lock);
rtc->rtc_dev = rtc_device_register("ds1302", &pdev->dev,
        &ds1302_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc->rtc_dev)) {
  ret = PTR_ERR(rtc->rtc_dev);
  goto out;
}
platform_set_drvdata(pdev, rtc);
return 0;
out:
kfree(rtc);
return ret;
}
static int __devexit ds1302_rtc_remove(struct platform_device *pdev)
{
struct ds1302_rtc *rtc = platform_get_drvdata(pdev);
if (likely(rtc->rtc_dev))
  rtc_device_unregister(rtc->rtc_dev);
platform_set_drvdata(pdev, NULL);
kfree(rtc);
return 0;
}
static struct platform_driver ds1302_platform_driver = {
.driver  = {
  .name = DRV_NAME,
  .owner = THIS_MODULE,
},
.probe  = ds1302_rtc_probe,
.remove  = __devexit_p(ds1302_rtc_remove),
};
static int __init ds1302_rtc_init(void)
{
    printk(KERN_INFO "hello,init\n");
return platform_driver_register(&ds1302_platform_driver);
}
static void __exit ds1302_rtc_exit(void)
{
platform_driver_unregister(&ds1302_platform_driver);
}
module_init(ds1302_rtc_init);
module_exit(ds1302_rtc_exit);
MODULE_DESCRIPTION("Dallas DS1302 RTC driver");
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR("Paul Mundt, David McCullough");
MODULE_LICENSE("GPL v2");

使用特权

评论回复
73
tiger84|  楼主 | 2009-12-31 19:23 | 只看该作者
赶快闪人,理光头去了。

使用特权

评论回复
74
牛牛特工| | 2010-1-2 13:56 | 只看该作者
郁闷死~~  整的人很郁闷~~

使用特权

评论回复
75
tiger84|  楼主 | 2010-1-3 09:58 | 只看该作者
小牛,同感。俺现在仍然一头雾水。

使用特权

评论回复
76
tiger84|  楼主 | 2010-1-3 20:38 | 只看该作者
元旦玩了2天,给玩病了。
任务没有如期进行,惭愧。
:'(

使用特权

评论回复
77
tiger84|  楼主 | 2010-1-3 20:40 | 只看该作者
感觉无法前进了,就是感觉有道坎摆在面前。明天去拜访下技术支持看看。

使用特权

评论回复
78
sinanjj| | 2010-1-4 15:41 | 只看该作者
没有任何坎.

先把什么类不类的忘掉. 从read write看起是个很好的开始. 理解文件描述符, 就理解了unix设计开始的思想.




别玩太长的程序. 我自己学习的时候做的例程从来都30行内. 因为核心思路最后都很简单. 说明问题就够了.
要是找到的资料例程长, 就自己缩减.
目前最牛的web服务器nginx, 核心思路也就百行左右.

使用特权

评论回复
79
icecut| | 2010-1-5 13:22 | 只看该作者
呵呵.iic的是bus驱动.然后上面加device驱动1302.我记得是iic的.不清楚了

使用特权

评论回复
80
牛牛特工| | 2010-1-5 14:28 | 只看该作者
变更一下计划 先用现成的Boot把linux跑起来 学学怎么写驱动了 我的Uboot始终没跑起来 没办法~~

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则