内核与驱动移植记录

来源:互联网 发布:net和java有什么区别 编辑:程序博客网 时间:2024/06/05 03:19

内核选用linux2.6.34
编译器为4.3.2和uboot一样

运行命令make mrproper清除相关信息
接下来,要测试一下 linux 的编译是否能正常通过。
执行:
make s3c2410_defconfig ;使用缺省内核配置文件
make

arch/arm/tools/mach_types存放了开发板机器码
查看和uboot设置的是否相同

修改/arch/arm/mach-s3c2410/mach-smdk2410.c中111行启动信息:
MACHINE_START(SMDK2410, "xiaohe-UP-tech SMDK2410") /* @TODO: request a new identifier and switch
修改第100行:s3c24xx_init_clocks(12000000);//12000000为我们开发板使用的外部晶振12MHZ
注释108行://smdk_machine_init();
运行:make s3c2410_defconfig
make zImage ;编译内核,时间较长,最后会生成 zImage
把生成的内核文件 zImage(位于 arch/arm/boot 目录)下到板子中

修改arch/arm/plat-s3c24xx/common_smdk.c中分区信息
static struct mtd_partition smdk_default_nand_part[] = {
 [0] = {
  .name = "uboot",
  .size = 0x80000,
  .offset = 0,
 },
 [1] = {
  .name = "kernel",
  .offset = 0x80000,
  .size = 0x300000,
 },
 [2] = {
  .name = "cramfs",
  .offset = 0x380000,
  .size = SZ_4M,
 },
 [3] = {
  .name = "software",
  .offset = 0x780000,
  .size = 0x3880000,//正好64MB空间
 }
};

修改drives/mtd/nand/s3c2410.c
842行:
chip->ecc.mode     = NAND_ECC_NONE;

错误:
  arch/arm/mach-s3c2410/built-in.o:(.init.data+0x30): undefined reference to `s3c_device_ohci' 
    make: *** [.tmp_vmlinux1] Error 1 
解决方法:
在mach-s3c2410 kconfig中添加:
config ARCH_SMDK2410
 bool "SMDK2410/A9M2410"
 select CPU_S3C2410
+ select S3C_DEV_USB_HOST
 select MACH_SMDK
 help
错误:
root/kernel/linux-2.6.34/arch/arm/plat-s3c24xx/common-smdk.c:181: undefined reference to `s3c_nand_set_platdata'
arch/arm/plat-s3c24xx/built-in.o:(.init.data+0x178): undefined reference to `s3c_device_nand'
解决方法:
配置内核,支持NandFlash

Device Drivers --->

<*> Memory Technology Device (MTD) support --->

[*] MTD partitioning support

<*> NAND Device Support --->

<*> NAND Flash support for S3C/S3C SoC
同时在mach-s3c2410 kconfig中添加:
config ARCH_SMDK2410
 bool "SMDK2410/A9M2410"
 select CPU_S3C2410
 select S3C_DEV_USB_HOST
 select S3C_DEV_NAND
 select MACH_SMDK
 help

使用mkimage -n 'linux-2.6.34-xiaohe' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage

制作uImage镜像
下载进去没有挂在文件系统
此时使用的博创的文件系统
以为是nand不支持
配置nand:
#接下来要做的是对内核MTD子系统的设置
Device Drivers >
     Memory Technology Devices (MTD) >
     [*] MTD partitioning support
  #支持MTD分区,这样我们在前面设置的分区才有意义
    [*] Command line partition table parsing
         #支持从命令行设置flash分区信息,灵活
   RAM/ROM/Flash chip drivers >
    <*> Detect flash chips by Common Flash Interface (CFI) probe
   <*> Detect nonCFI AMD/JEDECcompatible flash chips
    <*> Support for Intel/Sharp flash chips
     <*> Support for AMD/Fujitsu flash chips
     <*> Support for ROM chips in bus mapping
NAND Flash Device Drivers >
     <*> NAND Device Support
   <*> NAND Flash support for S3C2410/S3C2440 SoC
Character devices >
  [*] Nonstandard serial port support
    [*] S3C2410 RTC Driver

#接下来做的是针对文件系统的设置
File systems >
 <> Second extended fs support #去除对ext2的支持
 Pseudo filesystems >
 [*] /proc file system support
 [*] Virtual memory file system support (former shm fs)
 [*] /dev file system support (OBSOLETE)
 [*] Automatically mount at boot (NEW)
Miscellaneous filesystems >
   <*> Compressed ROM file system support (cramfs)
                      #支持cramfs
Network File Systems >
    <*> NFS file system support
保存退出,产生.config文件.
生成内核镜像
#make zImage
编译成功后将在arch/arm/boot 下找到 zImage

内核移植完后移植文件系统
使用busybox1.15.3创建cramfs根文件系统,然后使用网上的方法创建了linuxrc、fstab、等文件。
使用 mkcramfs rootfs root.cramfs生成文件系统镜像
下载到板子上能正常挂载。
此时因为cramfs是只读系统,还没想到怎么挂载用户程序
而接下来要做的是移植网卡驱动。


由于在mch-smdk2410.c中定义的网卡name和驱动代码中的不一致,导致初始化代码不能被执行,
修改后现在时获取资源时发现资源不足。

下午换了一个版本的内核,同样的代码资源不足的问题就没了,可能真是内核版本的问题。
使用了2.6.35.13的内核,
使用下面代码打补丁
patch -N p1 -i /root/2.6.35/linux-2.6.35.13 patch-2.6.35.13

在mach-smdk2410.c中添加:
#include<linux/dm9000.h>
//add by xiaohe
static struct resource s3c_dm9ks_resource[] = {
 [0] = {
  .start = 0x10000000,
  .end = 0x10000001,
  .flags = IORESOURCE_MEM,
 },
 [1] = {
  .start = 0x10000002,
  .end = 0x10000003,
  .flags = IORESOURCE_MEM,
 },
 [2] = {
  .start = IRQ_EINT2,
  .end = IRQ_EINT2,
  .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,//DM9000_PLATF_NO_EEPROM,这个标志信息不能缺,缺了以后会出现各种问题,中断上升沿触发
 },
};
static struct dm9000_plat_data  s3c_device_dm9ks_pdata = {
 .flags= (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
};

struct platform_device s3c_device_dm9ks = {
 .name = "s3c2410-dm9ks",
 .id = -1,
 .num_resources = ARRAY_SIZE(s3c_dm9ks_resource),
 .resource = s3c_dm9ks_resource,
 .dev ={
  .platform_data = &s3c_device_dm9ks_pdata,
 },

};

//end by xiaohe
static struct platform_device *smdk2410_devices[] __initdata = {
 &s3c_device_ohci,
 &s3c_device_lcd,
 &s3c_device_wdt,
 &s3c_device_i2c0,
 &s3c_device_iis, 
 //add by xiaohe
 &s3c_device_dm9ks,
 //end
};

然后在dm9000.c中修改设备的名字为s3c2410-dm9ks
static struct platform_driver dm9000_driver = {
 .driver = {
  .name    = "s3c2410-dm9ks",
  .owner  = THIS_MODULE,
  .pm  = &dm9000_drv_pm_ops,
 },
 .probe   = dm9000_probe,
 .remove  = __devexit_p(dm9000_drv_remove),
};
在初始化函数中,设置总线宽度和中断寄存器值
dm9000_init(void)
{//added by xiaohe
 u32 bwscon;
 bwscon = __raw_readl(S3C2410_BWSCON);
 bwscon &= ~(S3C2410_BWSCON_WS2|S3C2410_BWSCON_ST2|S3C2410_BWSCON_DW2_32);
 bwscon |= (S3C2410_BWSCON_ST2|S3C2410_BWSCON_DW2_16);
 __raw_writel(bwscon, S3C2410_BWSCON);
 __raw_writel(S3C2410_BANKCON_Tacs4|S3C2410_BANKCON_Tcos4|S3C2410_BANKCON_Tacc14|S3C2410_BANKCON_Tcoh4|S3C2410_BANKCON_Tcah4|S3C2410_BANKCON_Tacp6|S3C2410_BANKCON_PMCnorm, S3C2410_BANKCON2);
 set_irq_type(IRQ_EINT2,IRQ_TYPE_LEVEL_HIGH);
 s3c2410_gpio_cfgpin(S3C2410_GPF(2), 2);//配置为中断
   s3c2410_gpio_pullup(S3C2410_GPF(2), 0);//上拉允许
//end added

 printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);

 return platform_driver_register(&dm9000_driver);
}

在移植文件系统时博创的方法是根文件系统使用cramfs,因为cramfs是只读的系统,可以实现对系统文件的有效保护,
在root目录挂载mtdblock3为yaffs文件系统,这样可以实现对nand的读写操作,
开始的时候出现不能挂载yaffs文件系统,发现是我的内核没有打上yaffs的补丁
使用git clone git://www.aleph1.co.uk/yaffs2
获取yaffs2文件系统源码
后来打上后按照博创的内核配置,发现还是不行,最后查看两个内核的kconfig和makefile文件发现不一致,所以最后改了配置就OK了。

文件系统移植步骤:
首先下载busybox,我用的是1.20.1
首先修改makefile文件,arch,还有编译器。
编译后从copy到自己建的rootfs目录中,然后添加etc下的脚本文件,具体的不再分析,
主要是挂载yaffs文件系统。还有设置一些东西
然后就基本上可以运行了,
挂载nfs后发现不能运行自己的程序,编译为静态链接就可以,分析是没有共享库,所以添加arm-linux-gcc4.3.2的库文件到rootfs/lib

从usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib拷贝库文件到rootfs/lib中
用arm-linux-strip -s /root/busybox/rootfs/lib/lib*压缩共享库
此时运行的文件必须是同一个版本的编译器编译器的文件
在dev目录必须创建两个设备节点
mknod console c 5 1
mknod null c 1 3
在文件系统里面本来想加入vstfpd的功能,结果发现网上下载的vstfpd编译的时候都有问题,并且找不到解决方法,所以决定不再搞了,
文件系统和xp传输文件可以使用tftp功能,方法:tftp -g -r root.cramfs 172.30.128.185,同时在xp启动tftpd程序
文件系统和虚拟机传输文件可以使用nfs挂载共享方式。


LCD移植:
s3c2410fb_display在mach/fb.h中定义
从博创内核代码中copy
//added by xiaohe

//for 2410 LCD
static struct s3c2410fb_display up2410_fb[] __initdata = {
{
 .lcdcon5 = (1<<12)|(1<<11)|(1<<9)|(1<<8)|(1<<0),//设置LCD控制寄存器5 1<<12 MSB高24位有效,1<<11 5:6:5格式 1<<9 VLINE反转,1<<8 VFRAME反转 1<<0 半字交换允许
 .type = (3<<5),//LCD控制寄存器1,类型为TFT LCD 面板
 .width = 640,//屏宽度
 .height = 480,//屏高度
 .pixclock = 39721,//时钟
 .xres = 640,
 .yres = 480,
 .bpp = 16,
 .left_margin = 40,
 .right_margin = 32,
 .hsync_len = 96,
 .vsync_len = 2,
 .upper_margin = 24,
 .lower_margin = 11,
 },
};

static struct s3c2410fb_mach_info up2410_fb_info __initdata = {
 .displays = up2410_fb,
 .num_displays = 1,
 .default_display = 0,
 .gpcup = 0xffffffff,
 .gpcup_mask = 0x0,
 .gpccon = 0xaaaaaaaa,
 .gpccon_mask = 0x0,
 .gpdup = 0xffffffff,
 .gpdup_mask = 0x0,
 .gpdcon = 0xaaaaaaaa,
 .gpdcon_mask = 0x0,

 .lpcsel = 0,
};
//added end

static void __init smdk2410_init(void)
{
 s3c_i2c0_set_platdata(NULL);
 platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
 s3c24xx_fb_set_platdata(&up2410_fb_info);//added by xiaohe
 smdk_machine_init();
}
编译错误:
arch/arm/mach-s3c2410/mach-smdk2410.c:184: error: implicit declaration of function 's3c24xx_fb_set_platdata'
解决方法:
添加:
#include <mach/fb.h>//added by xiaohe
然后加载成功

问题:
 启动后几分钟显示屏就关闭了。
解决方法:
进入drivers目录
使用搜索命令grep blankinterval ./* -R
修改char/vt.c
180行
//alter by xiaohe
//static int blankinterval = 10*60;
static int blankinterval = 0;
//end alter

当然还有其他的解决方法,总结如下:

1.修改LCD驱动,把关闭LCD控制器的函数变为空(不推荐)
2.修改vt.c中的blank_screen_t()函数,www.linuxidc.com让其为空(在系统不需要使用关闭显示功能时推荐)
3.修改vt.c中的blankinterval,让其为0(系统可能需要使用关闭显示功能,而且希望系统上电后正常状态下不会关闭显示时推荐)
4.修改用户程序,加入设置blankinterval的代码(推荐)

触摸屏驱动:
mach-s3c2410.c中添加:
#include <plat/ts.h>//added by xiaohe
static struct s3c2410_ts_mach_info s3c2410_ts_cfg __initdata = {
.delay = 10000,
.presc = 49,
.oversampling_shift = 2,
.cfg_gpio = s3c24xx_ts_cfg_gpio,//查看源码,发现初始化时这个并没有被配置,可能会出错,因没没有初始化
};
static struct platform_device *smdk2410_devices[] __initdata = {
 &s3c_device_ohci,
 &s3c_device_lcd,
 &s3c_device_wdt,
 &s3c_device_i2c0,
 &s3c_device_iis, 
 //add by xiaohe
 &s3c_device_dm9ks,
 &s3c_device_ts, //此次添加
 //end
};
static void __init smdk2410_map_io(void)
{
 s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc));
 s3c24xx_init_clocks(12000000);
 s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs));
 s3c24xx_ts_set_platdata(&s3c2410_ts_cfg);//added bu xiaohe/////新加的
}
编译下载出现错误:
控指针,
查看devc.c发现
struct platform_device s3c_device_ts = {
 .name    = "s3c2410-ts",
 .id    = -1,
 .dev.parent = &s3c_device_adc.dev,
 .num_resources   = ARRAY_SIZE(s3c_ts_resource),
 .resource   = s3c_ts_resource,
};
依赖设备s3c_device_adc,在
static struct platform_device *smdk2410_devices[] __initdata = {
 &s3c_device_ohci,
 &s3c_device_lcd,
 &s3c_device_wdt,
 &s3c_device_i2c0,
 &s3c_device_iis, 
 //add by xiaohe
 &s3c_device_dm9ks,
 &s3c_device_adc;//后来添加
 &s3c_device_ts, //此次添加
 //end
};
编译下载成功,但是没有测试。

错误:arch/arm/mach-s3c2410/built-in.o:(.init.data+0xe0): undefined reference to `s3c24xx_ts_cfg_gpio'
make: *** [.tmp_vmlinux1] 错误 1
解决方法:在driver/input/touchscreen/kconfig中添加
config TOUCHSCREEN_S3C2410
 tristate "Samsung S3C2410/generic touchscreen input driver"
 depends on ARCH_S3C2410 || SAMSUNG_DEV_TS
 select S3C_ADC
 select S3C2410_SETUP_TS//添加
 help
   Say Y here if you have the s3c2410 touchscreen.

   If unsure, say N.

   To compile this driver as a module, choose M here: the
   module will be called s3c2410_ts.


声卡移植:
在mach-s3c2410.c中添加:
#include <sound/s3c24xx_uda134x.h>//added by xiaohe
//sound
static struct s3c24xx_uda134x_platform_data s3c24xx_uda134x_data = {
 .l3_clk = S3C2410_GPG(9),
 .l3_data = S3C2410_GPB(10),
 .l3_mode = S3C2410_GPB(8),
 .model = UDA134X_UDA1341,
};
static struct platform_device s3c24xx_uda134x = {
 .name = "s3c24xx_uda134x",
 .dev = {
  .platform_data
  = &s3c24xx_uda134x_data,
 }
};
static struct platform_device *smdk2410_devices[] __initdata = {
 &s3c_device_ohci,
 &s3c_device_lcd,
 &s3c_device_wdt,
 &s3c_device_i2c0,
 &s3c_device_iis, 
 //add by xiaohe
 &s3c_device_rtc,
 &s3c_device_dm9ks,
 &s3c_device_adc,
 &s3c_device_ts, 
 &s3c24xx_uda134x,
 //end
};

错误:
arch/arm/mach-s3c2410/mach-smdk2410.c:148: error: implicit declaration of function 'S3C2410_GPB'
arch/arm/mach-s3c2410/mach-smdk2410.c:148: error: initializer element is not constant
arch/arm/mach-s3c2410/mach-smdk2410.c:148: error: (near initialization for 's3c24xx_uda134x_data.l3_clk')
arch/arm/mach-s3c2410/mach-smdk2410.c:149: error: initializer element is not constant
arch/arm/mach-s3c2410/mach-smdk2410.c:149: error: (near initialization for 's3c24xx_uda134x_data.l3_data')
arch/arm/mach-s3c2410/mach-smdk2410.c:150: error: initializer element is not constant
arch/arm/mach-s3c2410/mach-smdk2410.c:150: error: (near initialization for 's3c24xx_uda134x_data.l3_mode')
arch/arm/mach-s3c2410/mach-smdk2410.c:196: error: expected '}' before ';' token
解决方法:
在mach-s3c2410.c中添加:#include <mach/gpio.h>//added by xiaohe


LCD加入双缓冲
为了提高LCD的显示效果,如果只是使用单缓冲,即一帧数据作为缓冲区的话,就会出现写数据的时候有闪屏现象,为了解决这个问题,
我们为LCD缓冲区设置了两个帧的大小,这样如果此时LCD控制器帧缓冲区地址指向的是第一帧的数据的话我们可以写第二帧数据,
写完以后把第二帧的首址赋给LCD控制器,S3C2410A中有专门的LCDDMA,所有我们只要在内存把帧缓冲区内存设为DMA格式的内存,LCD
控制器就会自动发送,当然我们要写寄存器控制显示的频率。在使用时我们要和用户程序结合起来,用户程序映射缓冲区的时候也要映射两个帧的
大小

下面是修改LCD驱动的过程。
我们使用的是linux2.6.35.13中内核中原有的LCD驱动,
首先在fb.h修改fb_info结构体
这个结构体包含了framebuffer以后LCD的所有信息
我们加入 unsigned long smem_start_buffer;//开始缓冲区物理地址
这个元素的作用后面会提到。
然后设置yres_virtual
在static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
          struct fb_info *info)
函数中修改为var->yres_virtual = display->yres*2;//modiby bu xiaohe
这个是提供给framebuffer驱动说明使用了虚拟y坐标,如果不设置我们没法通过后来的程序修改LCD控制器帧缓冲区的地址

之后修改static int __devinit s3c24xxfb_probe(struct platform_device *pdev,
      enum s3c_drv_type drv_type)
函数中的fbinfo->fix.ypanstep     = 1;//modify bu xiaohe

在函数static int __devinit s3c2410fb_map_video_memory(struct fb_info *info)中
给info->smem_start_buffer=map_dma;赋值,这个值的作用就是存储要赋给LCD帧缓冲区地址的地址

在函数static void s3c2410fb_set_lcdaddr(struct fb_info *info)
中修改要赋给LCD帧缓冲区地址的地址,修改info->fix.smem_start为 info->smem_start_buffer
如:
saddr1  = info->smem_start_buffer >> 1;
saddr2  = info->smem_start_buffer;
这里说明一下:
fix结构体的smem_start和fb_info结构体的screen_base,以后表示分配的DMA缓冲区的物理地址和虚拟地址,两个地址
都在framebuffer驱动中映射给了用户空间,所以我们不能使用它们作为LCD帧缓冲区地址(默认的使用smem_start),
所以我另加了一个fb_info结构体元素smem_start_buffer,也就是在处理双缓冲的函数中只改变这个值就OK了。

再之后在static struct fb_ops s3c2410fb_ops 结构体中加入双缓冲的处理函数
.fb_pan_display =mvfb_pan_display,
函数内容如下:
//fb->screen_base_start//虚拟起始地址
//info->smem_start_star;//存储内存分配的DMA起始地址
//int yoffset_origin;//存储上一次的y偏移地址
static int mvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb)
{
 //printk("fb->smem_start_buffer %x\n",fb->smem_start_buffer);
 if(var->yoffset==var->yres)//第二帧地址
 {
  fb->smem_start_buffer=fb->fix.smem_start+var->yres*var->xres*2;
 }
 else//第一帧地址
 {
  if(var->yoffset>=0&&var->yoffset<var->yres)
  {
   fb->smem_start_buffer=fb->fix.smem_start+var->xres*2*var->yoffset;
  }
 } 
 s3c2410fb_lcd_enable(fb->par, 0);//关闭LCD
 s3c2410fb_set_lcdaddr(fb);//设置帧缓冲区起始地址
 s3c2410fb_lcd_enable(fb->par, 1);//启动LCD
 //printk("fb->smem_start_buffer %x\n",fb->smem_start_buffer);
 return 0;
}
这时候就可以通过
if (ioctl(fd,FBIOPAN_DISPLAY, &fb_vinfo)) {////返回framebuffer可变信息
  perror(__func__);
  return (-1);
}
来传递var结构体参数,它里面包含我们要设置的yoffset,我们写屏之后通过这个函数来改变帧缓冲区起址。
这样就实现了我们在用户程序映射两个帧空间后先写入想要写入的区域,然后在改变yoffset,消除闪屏的现象。
我们页可以不使用双缓冲,只要写入的位置与yoffset相同,就会和使用单缓冲效果一样。

不足:
s3c2410中的LCD控制器已经实现了硬件上的虚拟显示,而我这个并没有使用,只是软件上的双缓冲,同时没有使用xoffset,由于时间的原因就先不加了。


 

 

原创粉丝点击