TQ2440(S3C2440)移植Linux-4.0.1内核全过程

TQ2440(S3C2440)移植Linux-4.0.1内核全过程


文件系统yaffs2下载地址:https://yaffs.net/get-yaffs
linux内核下载地址:https://mirror.bjtu.edu.cn/kernel/linux/kernel/


构建根文件系统在这里:嵌入式Linux构建yaffs根文件系统


(一)初步移植启动linux内核

解压linux内核源码:

tar xvf linux-4.0.1.tar.xz

获取yaffs2文件系统源码并向linux内核打包使内核支持yaffs2文件系统:

git clone git://www.aleph1.co.uk/yaffs2
cd yaffs2
./patch-ker.sh c m ../linux-4.0.1

进入linux内核源码目录并修改顶层Makefile:

cd ../linux-4.0.1
gedit Makefile

找到:

ARCH        ?= $(SUBARCH)
CROSS_COMPILE   ?= $(CONFIG_CROSS_COMPILE:"%"=%)

改为:

ARCH        ?= arm
CROSS_COMPILE   ?= arm-linux-

加载mini2440的配置项:

make mini2440_defconfig

打开图形化配置内核:

make menuconfig

相关配置如下:

 Device Drivers  --->
 	Input device support  --->
 		[*]   Touchscreens  --->
 			<*>   Samsung S3C2410/generic touchscreen input driver  # 开启触摸屏支持
 	[*] Watchdog Timer Support  --->
 		< >   S3C2410 Watchdog  # 关闭看门狗,要不然系统会一直重启
 	 Graphics support  --->
 	 	[*] Bootup logo  --->
 	 		 [*]   Standard 16-color Linux logo  # 选中该项,取消其他项
 	<*> Memory Technology Device (MTD) support  --->
 		 < >   FTL (Flash Translation Layer) support  # 取消选中,要不然会出现警告:ftl_cs: FTL header not found
 		 < >   NFTL (NAND Flash Translation Layer) support # 取消选中,要不然会出现警告:ftl_cs: 
 		 < >   INFTL (Inverse NAND Flash Translation Layer) support # 取消选中,要不然会出现警告:ftl_cs: 
 		 <*>   NAND Device Support  --->
 		 	<*>   NAND Flash support for Samsung S3C SoCs
 		 		[*]     Samsung S3C NAND Hardware ECC  # 开启NAND的硬件ECC校验
 File systems  ---> 
 	[*] Miscellaneous filesystems  --->
 		<*>   yaffs2 file system support  # 开启yaffs2文件系统支持

注:开启NAND的硬件ECC校验是选做,如果不开启那么默认使用软件ECC,也可以手动修改文件drivers/mtd/nand/s3c2410.c中的s3c2410_nand_init_chip函数,将图片框出的一行的宏改为NAND_ECC_NONE,即不适用ECC功能:
在这里插入图片描述

修改mini2440的机器码:

gedit arch/arm/tools/mach-types

找到:

mini2440		MACH_MINI2440		MINI2440		1999

改为:

mini2440		MACH_MINI2440		MINI2440		168

168是我的u-boot里的,这个机器码需要跟u-boot中的机器码相对应,要不然u-boot无法引导启动内核,如果你不知道uboot中的机器码是多少,在uboot命令行中输入命令bdinfo查看:
在这里插入图片描述

进行初次编译:

make -j4 

说明:参数4表示4个核心,是CPU的核数,目的为了加快编译速度。

出错:fs/yaffs2/yaffs_vfs.c:321: error: 'struct file' has no member named 'f_dentry'
看错误信息是说结构体file没有名为f_dentry的成员,我们查看一下fs/yaffs2/yaffs_vfs.c这个文件,定位到第321行是这么一句obj = yaffs_dentry_to_obj(Y_GET_DENTRY(f));,也就是说出错的是这个宏Y_GET_DENTRY,我们在定位到这个宏的定义位置:*

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
#define PAGE_CACHE_SIZE PAGE_SIZE
#define PAGE_CACHE_SHIFT PAGE_SHIFT
#define Y_GET_DENTRY(f) ((f)->f_path.dentry)
#define YAFFS_NEW_XATTR 1
#define YAFFS_NEW_GET_LINK 1
#else
#define Y_GET_DENTRY(f) ((f)->f_dentry)
#define YAFFS_NEW_XATTR 0
#define YAFFS_NEW_GET_LINK 0
#endif

这里根据linux的版本宏定义了Y_GET_DENTRY,如果linux版本低于4.4那么使用f->f_dentry,否则使用f->f_path.dentry,执行的出错的原因是找不到f_dentry,现在就要去看一下linux-4.0.1内核中的struct fle结构体的成员中有关键字dentry的是什么,因为这是文件系统相关的,所以我们到include/linux/fs.h中去找:

struct file {
	union {
		struct llist_node	fu_llist;
		struct rcu_head 	fu_rcuhead;
	} f_u;
	struct path		f_path;
	struct inode		*f_inode;	/* cached value */
	const struct file_operations	*f_op;

	/*
	 * Protects f_ep_links, f_flags.
	 * Must not be taken from IRQ context.
	 */
	spinlock_t		f_lock;
	atomic_long_t		f_count;
	unsigned int 		f_flags;
	fmode_t			f_mode;
	struct mutex		f_pos_lock;
	loff_t			f_pos;
	struct fown_struct	f_owner;
	const struct cred	*f_cred;
	struct file_ra_state	f_ra;

	u64			f_version;
#ifdef CONFIG_SECURITY
	void			*f_security;
#endif
	/* needed for tty driver, and maybe others */
	void			*private_data;

#ifdef CONFIG_EPOLL
	/* Used by fs/eventpoll.c to link all the hooks to this file */
	struct list_head	f_ep_links;
	struct list_head	f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
	struct address_space	*f_mapping;
} __attribute__((aligned(4)));	/* lest something weird decides that 2 is OK */

struct file_handle {
	__u32 handle_bytes;
	int handle_type;
	/* file identifier */
	unsigned char f_handle[0];
};

没有发现dentry的字样,但是看到了struct path f_path;,再去这个结构体中看看:

struct path {
	struct vfsmount *mnt;
	struct dentry *dentry;
};

果然找到了dentry成员!那么就将文件fs/yaffs2/yaffs_vfs.c中的宏:

#define Y_GET_DENTRY(f) ((f)->f_dentry)

改为:

#define Y_GET_DENTRY(f) ((f)->f_path.dentry)

重新make一下,编译顺利通过!

接下来编译出用于u-boot引导的内核镜像:

make uImage

在这里插入图片描述
成功!

下面开始修改内核代码:

gedit arch/arm/mach-s3c24xx/mach-mini2440.c

修改NAND默认分区表为:

/* NAND Flash on MINI2440 board */

static struct mtd_partition mini2440_default_nand_part[] __initdata = {
	[0] = {
		.name	= "u-boot-spl",
		.size	= SZ_128K,
		.offset	= 0,
	},
	[1] = {
		.name	= "u-boot",
		.offset = SZ_128K,
		.size	= SZ_1M - SZ_128K,
	},
	[2] = {
		.name	= "u-boot-env",
		.offset = SZ_1M,
		.size	= SZ_256K,
	},
	[3] = {
		.name	= "kernel",
		.offset	= SZ_1M + SZ_256K,
		.size	= SZ_4M,
	},
	[4] = {
		.name	= "yaffs2",
		.offset = SZ_4M + SZ_1M + SZ_256K,
		.size	= MTDPART_SIZ_FULL,
	},
};


下面运行内核进行测试一下,这里我提前做好了yaffs2文件系统(制作方法见本博客首部链接),启动内核后开发板使用nfs的方式挂载文件系统。

首先设置uboot启动参数为:

setenv bootargs noinitrd console=ttySAC0,115200 rootfstype=yaffs2 init=/linuxrc root=/dev/nfs rw nfsroot=192.168.166.254:/opt/embeded/yaffs2 ip=192.168.166.253:192.168.166.254:192.168.166.1:255.255.255.0::eth0:off

保存环境变量并重启:

saveenv
reset

参数说明:

  • nfsroot=192.168.166.254:/opt/embeded/yaffs2:这是ubuntu的IP地址以及制作的yaffs2文件系统的目录
  • ip=192.168.166.253:192.168.166.254:192.168.166.1:255.255.255.0::eth0:off:ip=[开发板IP地址]:[ubuntu的IP地址]:[网关]:[掩码]:eth0:off

使用tftp下载系统内核镜像并启动:

tftp 0x32000000 uImage; bootm 0x32000000

成功启动:
在这里插入图片描述

但是通过nfs挂载不上根文件系统,出现了问题:

  • nfs: server 192.168.166.254 not responding, still trying
  • mount: server 192.168.166.254 not responding, timed out

原因:BWSCON和BANKCON4寄存器关于网卡的配置项有问题。

修改代码:

gedit arch/arm/mach-s3c24xx/mach-mini2440.c

添加头文件:

#include "regs-mem.h"
#include <linux/platform_data/touchscreen-s3c2410.h>

定位到mini2440_init函数,在该函数尾部添加以下代码:

	/* 清除bank4的总线宽度设置 */
    *((volatile unsigned int *)S3C2410_BWSCON) &= ~(3 << 16);

	/* 设置BANK4的总线宽度为16bits、设置开启BANK4等待状态、设置SRAM使用BANK4的UB/LB */
	*((volatile unsigned int *)S3C2410_BWSCON) |= (1 << 16) | (1 << 18) | (1 << 19);

	/* 设置BANK4控制寄存器 */
	*((volatile unsigned int *)S3C2410_BANKCON4) = 0X1F7C;

重新编译后使用tftp下载内核并运行:
在这里插入图片描述
成功挂载!

(二)驱动修改

TQ2440板载一个AT24C02,接在I2C0上。

gedit arch/arm/mach-s3c24xx/mach-mini2440.c

找到I2C设备的相关配置,替换为:

/*
 * I2C devices
 */
static struct at24_platform_data at24c02 = {
	.byte_len	= SZ_2K / 8,   /* 2Kbits / 8 = 256bytes */
	.page_size	= 8,           /* at24c02 page size = 8 */
	.flags = 0,
};

static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
	{
	    /*
	     * at24c02的地址由8位组成,高四位固定为1010,低四位为A2A1A0R/W,
	     * A2A1A0三个引脚一般拉低与高四位组成7bit的地址码,R/W位为读写控制位,
	     * 为0时表示芯片写操作,组合起来的地址就是0xA0(写)和0xA1(读),
	     * 但是在Linux系统中I2C设备地址的最高位为1,而剩余7位地址就是去掉R/W位后的剩余7位,
	     * 所以就是0x50。
	     */
		I2C_BOARD_INFO("24c02", 0x50),
		.platform_data = &at24c02,
	},
};

TQ2440板载4个LED灯,位于GPB5-GPB8,找到LED设备的相关配置,替换为:

/* LEDS */

static struct s3c24xx_led_platdata mini2440_led1_pdata = {
	.name		= "led1",
	.gpio		= S3C2410_GPB(5),
	.flags		= S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
	.def_trigger	= "heartbeat",
};

static struct s3c24xx_led_platdata mini2440_led2_pdata = {
	.name		= "led2",
	.gpio		= S3C2410_GPB(6),
	.flags		= S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
	.def_trigger	= "nand-disk",
};

static struct s3c24xx_led_platdata mini2440_led3_pdata = {
	.name		= "led3",
	.gpio		= S3C2410_GPB(7),
	.flags		= S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
	.def_trigger	= "mmc0",
};

static struct s3c24xx_led_platdata mini2440_led4_pdata = {
	.name		= "led4",
	.gpio		= S3C2410_GPB(8),
	.flags		= S3C24XX_LEDF_ACTLOW | S3C24XX_LEDF_TRISTATE,
	.def_trigger	= "",
};

static struct platform_device mini2440_led1 = {
	.name		= "s3c24xx_led",
	.id		= 1,
	.dev		= {
		.platform_data	= &mini2440_led1_pdata,
	},
};

static struct platform_device mini2440_led2 = {
	.name		= "s3c24xx_led",
	.id		= 2,
	.dev		= {
		.platform_data	= &mini2440_led2_pdata,
	},
};

static struct platform_device mini2440_led3 = {
	.name		= "s3c24xx_led",
	.id		= 3,
	.dev		= {
		.platform_data	= &mini2440_led3_pdata,
	},
};

static struct platform_device mini2440_led4 = {
	.name		= "s3c24xx_led",
	.id		= 4,
	.dev		= {
		.platform_data	= &mini2440_led4_pdata,
	},
};

并删除这两个结构,这两个结构数据未使用,编译的时候会出现警告,当然不删除也不影响:

static struct platform_device mini2440_led_backlight = {
	.name		= "s3c24xx_led",
	.id		= 5,
	.dev		= {
		.platform_data	= &mini2440_led_backlight_pdata,
	},
};

static struct s3c24xx_led_platdata mini2440_led_backlight_pdata = {
	.name		= "backlight",
	.gpio		= S3C2410_GPG(4),
	.def_trigger	= "backlight",
};

修改LCD设备驱动(我的屏幕是W43),找到_LCD_DECLARE这个宏并删除,修改mini2440_lcd_cfg这个结构为:

/* LCD timing and setup */

static struct s3c2410fb_display mini2440_lcd_cfg[] __initdata = {

    [0] = {
		.lcdcon5	= S3C2410_LCDCON5_FRM565 |
				  S3C2410_LCDCON5_INVVLINE |
				  S3C2410_LCDCON5_INVVFRAME |
				  S3C2410_LCDCON5_PWREN |
				  S3C2410_LCDCON5_HWSWP,

		.type		= S3C2410_LCDCON1_TFT,
		.width		= 480,
		.height		= 272,
		.pixclock	= 40000, /* HCLK 60 MHz, divisor 10 */
		.xres		= 480,
		.yres		= 272,
		.bpp		= 16,
		.left_margin	= 19,
		.right_margin	= 10,
		.hsync_len	= 30,
		.upper_margin	= 4,
		.lower_margin	= 2,
		.vsync_len	= 8,
    },
};

TQ2440板载一个蜂鸣器,接在了GPB0上,这里我们将beep以led平台设备驱动的方式进行注册,添加代码:

/* BEEP */

static struct s3c24xx_led_platdata mini2440_beep_pdata = {
	.name		= "beep",
	.gpio		= S3C2410_GPB(0),
	.flags		= S3C24XX_LEDF_TRISTATE,
};

static struct platform_device mini2440_beep = {
	.name		= "s3c24xx_led",
	.id		= 5,
	.dev		= {
		.platform_data	= &mini2440_beep_pdata,
	},
};

TQ2440板载4个按键,位于GPF0、1、2、4,将按键相关配置改为:

/* KEYS */

static struct gpio_keys_button mini2440_buttons[] = {
	{
		.gpio		= S3C2410_GPF(1),
		.code		= KEY_UP,
		.desc		= "Button Up",
		.active_low	= 0,
	},
	{
		.gpio		= S3C2410_GPF(4),
		.code		= KEY_DOWN,
		.desc		= "Button Down",
		.active_low	= 0,
	},
	{
		.gpio		= S3C2410_GPF(2),
		.code		= KEY_LEFT,
		.desc		= "Button Left",
		.active_low	= 0,
	},
	{
		.gpio		= S3C2410_GPF(0),
		.code		= KEY_RIGHT,
		.desc		= "Button Right",
		.active_low	= 0,
	},
};

修改触摸屏驱动,添加这个结构:

/* Tcouch Screen  */

static struct s3c2410_ts_mach_info mini2440_ts_info __initdata = {
    .delay = 10000,
	.presc = 49,
	.oversampling_shift = 2,
};

删除:


/*
 * mini2440_features string
 *
 * t = Touchscreen present
 * b = backlight control
 * c = camera [TODO]
 * 0-9 LCD configuration
 *
 */
static char mini2440_features_str[12] __initdata = "0tb";

static int __init mini2440_features_setup(char *str)
{
	if (str)
		strlcpy(mini2440_features_str, str, sizeof(mini2440_features_str));
	return 1;
}

__setup("mini2440=", mini2440_features_setup);

#define FEATURE_SCREEN (1 << 0)
#define FEATURE_BACKLIGHT (1 << 1)
#define FEATURE_TOUCH (1 << 2)
#define FEATURE_CAMERA (1 << 3)

struct mini2440_features_t {
	int count;
	int done;
	int lcd_index;
	struct platform_device *optional[8];
};

static void __init mini2440_parse_features(
		struct mini2440_features_t * features,
		const char * features_str )
{
	const char * fp = features_str;

	features->count = 0;
	features->done = 0;
	features->lcd_index = -1;

	while (*fp) {
		char f = *fp++;

		switch (f) {
		case '0'...'9':	/* tft screen */
			if (features->done & FEATURE_SCREEN) {
				printk(KERN_INFO "MINI2440: '%c' ignored, "
					"screen type already set\n", f);
			} else {
				int li = f - '0';
				if (li >= ARRAY_SIZE(mini2440_lcd_cfg))
					printk(KERN_INFO "MINI2440: "
						"'%c' out of range LCD mode\n", f);
				else {
					features->optional[features->count++] =
							&s3c_device_lcd;
					features->lcd_index = li;
				}
			}
			features->done |= FEATURE_SCREEN;
			break;
		case 'b':
			if (features->done & FEATURE_BACKLIGHT)
				printk(KERN_INFO "MINI2440: '%c' ignored, "
					"backlight already set\n", f);
			else {
				features->optional[features->count++] =
						&mini2440_led_backlight;
			}
			features->done |= FEATURE_BACKLIGHT;
			break;
		case 't':
			printk(KERN_INFO "MINI2440: '%c' ignored, "
				"touchscreen not compiled in\n", f);
			break;
		case 'c':
			if (features->done & FEATURE_CAMERA)
				printk(KERN_INFO "MINI2440: '%c' ignored, "
					"camera already registered\n", f);
			else
				features->optional[features->count++] =
					&s3c_device_camif;
			features->done |= FEATURE_CAMERA;
			break;
		}
	}
}

找到mini2440_init函数,删除以下部分:
在这里插入图片描述
在for循环下添加设置触摸屏和显示的平台数据:

    s3c24xx_fb_set_platdata(&mini2440_fb_info);
	s3c24xx_ts_set_platdata(&mini2440_ts_info);

在这里插入图片描述
找到mini2440_devices这个结构,添加:

	&s3c_device_lcd,
	&s3c_device_adc,
	&s3c_device_ts,
	&mini2440_beep,

在这里插入图片描述

修改内核自带的触摸屏驱动:

gedit drivers/input/touchscreen/s3c2410_ts.c

找到touch_timer_fire函数,定位到上报触摸事件这一句:

input_report_key(ts.input, BTN_TOUCH, 1);

下方添加按压事件上报:

input_report_abs(ts.input, ABS_PRESSURE, 1);

找到:

input_report_key(ts.input, BTN_TOUCH, 0);

下放添加:

input_report_abs(ts.input, ABS_PRESSURE, 0);

如下图所示:
在这里插入图片描述
找到s3c2410ts_probe函数,将:

ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

改为:

ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT(EV_SYN);

找到这一句:

input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);

下方添加:

input_set_abs_params(ts.input, ABS_PRESSURE, 0, 1, 0, 0);

如下图所示:
在这里插入图片描述

重新make编译一下然后下载到开发板运行,LCD屏幕会显示一个linux的企鹅logo,LED1会闪烁!

(三)驱动测试

(1)测试LED

LED是以平台总线设备驱动的方式进行注册的,LED的相关代码已经标明了ID是1-4,BEEP是以led平台设备驱动进行方式注册的,ID为5,linux的led子系统会在/sys/class目录下生成leds文件夹,进入该目录就可以看到我们以led平台总线驱动方式注册的所有设备:
在这里插入图片描述
可以看到有led1-4和beep,我们使用led4进行测试,进入led4文件夹:
在这里插入图片描述
brightness这个文件就是led4的设备文件,操作这个文件就可以实现led4的亮和灭:

  • 点亮:echo 1 > brightness
  • 熄灭:echo 0 > brightness
(2)测试BEEP

同led,进入beep文件夹:
在这里插入图片描述
操作方式跟led一样:

  • 打开蜂鸣器:echo 1 > brightness
  • 熄灭蜂鸣器:echo 0 > brightness
(3)测试LCD显示

这里我使用Img2Lcd这个软件将衣服图片转成了bin文件,然后将该文件拷贝到了开发板文件系统上的/app/目录下:
在这里插入图片描述
lcd帧缓冲的设备文件是/dev/fb0,在开发板上使用cat命令将图片bin输出到显示设备上:

cat /app/logo.bin > /dev/fb0

在这里插入图片描述

(4)测试触摸屏和按键

使用以下命令触摸屏幕或按下按键查看有无乱码输出:

cat /dev/event0
cat /dev/event1

注意:如果在内核代码中没有修改名字的话,输入设备文件的默认名就是/dev/eventX,X是数字,在本次移植中有两个输入设备:①触摸屏,②按键,所以在/dev目录下有event0和event1这两个设备文件。


(四)USB驱动:U盘、USB鼠标键盘

make meunconfig

关于USB键盘和U盘的配置如下

 Device Drivers  ---> 
	SCSI device support  --->
		<*> SCSI device support
		[*] legacy /proc/scsi/ support
		<*> SCSI disk support
	[*] USB support  ---> 
		<*>   Support for Host-side USB
		[*]     Dynamic USB minor allocation
		<*>     USB Monitor
		<*>     OHCI HCD (USB 1.1) support
			<*>       OHCI support for Samsung S3C24xx/S3C64xx SoC series
		<*>     USB Mass Storage support
	HID support  --->
		USB HID support  --->
			<*> USB HID transport layer 
			[*] PID device support
			[*] /dev/hiddev raw HID device support

除此之外还要将字符级编译进内核,你可以使用拿个就编译进哪个,我为了方便将基本的字符级全都编译进去了,缺点是内核大小大概增加了500KB的大小:

 Device Drivers  ---> 
 	File systems  --->
 		-*- Native language support  --->

在这里插入图片描述

启动内核,插入U盘后成功识别,可以看到设备节点是sda4,下面挂载U盘:
在这里插入图片描述
我的U盘文件系统格式是FAT32,挂载U盘命令:

mount -t vfat -o iocharset=cp936 /dev/sda4 /mnt/udisk

命令解释:

  • -t:文件系统类型,vfat代表挂载的U盘格式为FAT/FAT32
  • -o:字符集
  • /dev/sda4:U盘的设备文件
  • /mnt/udisk:挂载点

注:一般情况下当有USB或者SD设备插入时,linux内核会首先扫描/etc/mdev.conf的配置文件,从其中获取挂载命令和挂载点从而实现自动挂载。

插入USB鼠标:
在这里插入图片描述

测试usb鼠标:

 cat /dev/mouseN

注意:/dev目录下可能有多个mouseN设备文件,N为数字,键入命令后移动鼠标查看有无乱码输出,若没有则不是鼠标的设备文件,挨个试试。


完!

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

觉皇嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值