本文全过程为自己亲自试验,成功移植了一个最基本功能的Kernel、根文件系统,现在将这个一耗时、耗精力 的过程写下来,希望对遇到相同问题的朋友们有所帮助!
平台:飞凌FL2440
windows xp sp2
vmware 6.5.build-203739
ubuntu 9.10
交叉编译器: 4.3.2
内核:linux-2.6.28.7.tar.bz2
根文件系统:busybox-1.19.4 busybox-1.19.4.tar.bz2
一、Kernel的移植:
整个过程参考的是这篇文章http://wenku.baidu.com/view/6cf79631eefdc8d376ee32d2.html,但该文章有些问题,下面的步骤对其进行了更改。
移植步骤:
1,我们首先在根目录下新建FL2440目录,
mkdir /FL2440
然后到Linux 内核官网下载Linux-2.6.28.7 的内核源码包。地址如下
http://www.kernel.org/pub/linux/kernel/v2.6/
或者
ftp://ftp.kernel.org/pub/linux/kernel/v2.6/
下载完毕,将内核源码包放到/FL2440目录下,然后解压缩
tar –jxvf linux-2.6.28.7.tar.bz2
FL2440目录下会多一个linux-2.6.28.7 的目录
2,由于该内核源码包中,没有yaffs2 驱动,所以我们要将yaffs2 文件系统的驱动添加到内核
源码中。yaffs2的补丁包需要登陆http://www.yaffs.net/download-yaffs-using-git,通过GIt方式获取(在Ubuntu中git clone git://www.aleph1.co.uk/yaffs2)或者到Linuxidc网站下载压缩包,由于时间有点长,记不住自己用的那种方式了,下载完后解压,执行./patch-ker.sh
c m /FL2440/linux-2.6.28.7 为kernel打上补丁,显示:
Updating /FL2440/linux-2.6.28.7/fs/Kconfig
Updating /FL2440/linux-2.6.28.7/fs/Makefile
如果上面的过程还需要其他的库或工具,需要通过sudo apt-get install ***来安装相应的工具
3. 修改linux-2.6.28.7/Makefile,将
ARCH ?=$ (SUBARCH)
CROSS_COMPILE ?=
修改为
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
注意,这里是在交叉编译环境已经建立完成的情况下的,并且,arm-linux-已经添加进入了环
境变量中。如果没做完这步,那么你要先建立交叉编译环境,并把arm-linux-导入到环境变量
中。这里也可以直接添加,如CROSS_COMPILE ?=/FL2440/usr/local/arm/4.3.2/bin/arm-none-linux-gnueabi-
我采用的后一种,如果用前面一种,需要更改/etc/bash.bashrc文件
#vim /etc/bash.bashrc
在最后加上:
export PATH=$PATH:/usr/local/arm/3.4.1/bin
export PATH
另外还有其他几种方法,百度一下吧, 安装成功的标志arm-linux-gcc -v 以及 echo $PATH会返回相应信息。好像需要注销生效,或者source /root/.bashrc不用重启生效
4,修改机器码。飞凌开发板的bootloader 默认的机器码是193,所以我们在使用FL2440 机器
的时候,需要修改机器码。修改arch/arm/tools/mach-types。将
s3c2440 ARCH_S3C2440 S3C2440 362
修改为193。
这里如果在配置时使用
[*] SMDK2410/A9M2410
则这里就不用修改了,当然了,我们一般不选SMDK2410/A9M2410 ,所以要修改。
5,由于板子使用的是12MHz 的晶振,而我们使用的SMDK2440 的晶振频率不是这个值,所
以我们要修改,要不然,会出现打印信息乱码的现象。
文件:arch/arm/mach-s3c2440/mach-smdk2440.c
函数:static void __init smdk2440_map_io(void)
/*s3c24xx_init_clocks(16934400);*/ s3c24xx_init_clocks(12000000);
6,修改MTD 分区。由于fl2440 和smdk2440 的nandflash 分区不同,所以这里要修改,要不
然会在启动的过程中会出现错误。需要和bootloader 中的分区信息相同(128M),否则,启
动时出错。
文件: arch/arm/plat-s3c24xx/common-smdk.c
结构体:static struct mtd_partition smdk_default_nand_part[]
将源结构体删除,或者注释掉,修改为以下形式
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "Boot",
.size = 0x00100000,
.offset = 0
},
[1] = {
.name = "MyApp",
.size = 0x003c0000,
.offset = 0x00140000,
},
[2] = {
.name = "Kernel",
.size = 0x00300000,
.offset = 0x00500000,
},
[3] = {
.name = "fs_yaffs",
.size = 0x03c00000, //30M
.offset = 0x00800000,
},
[4] = {
.name = "WINCE",
.size = 0x03c00000,
.offset = 0x04400000,
}
};
这里注意,一定要将fs_yaffs 对应到3 上面,并且内容要和板子上面的bootloader 一致,要不
然会出现kernel panic 的错误。其他可以随意。
7,关闭ECC 校验。
文件:drivers/mtd/nand/s3c2410.c
函数:s3c2410_nand_init_chip
/*chip->ecc.mode = NAND_ECC_SOFT; */ chip->ecc.mode = NAND_ECC_NONE;
8,修改nandflash 驱动,支持fl2440 开发板的nandflash
文件:drivers/mtd/nand/nand_bbt.c
static struct nand_bbt_descr largepage_memorybased = {
.options = 0,
.offs = 0,
.len = 1, // 原数值为2,支持2K 每页的flash 修改为1。K9F1G08,K9F2G08
是2k 每页的flash
.pattern = scan_ff_pattern
};
static struct nand_bbt_descr largepage_flashbased = {
.options = NAND_BBT_SCAN2NDPAGE,
.offs = 0,
.len = 1, //原数值为2,支持2K 每页的flash 修改为1。K9F1G08,K9F2G08
是2k 每页的flash
.pattern = scan_ff_pattern
};
9,增加devfs 文件管理器的支持。我们所用的文件系统使用的是devfs 文件管理器。
文件:fs/Kconfig,
添加
config DEVFS_FS
bool "/dev file system support (OBSOLETE)"
default y
config DEVFS_MOUNT
bool "Automatically mount at boot"
default y
depends on DEVFS_FS
10, 如果不考虑网络设备、LCD 显示屏、触摸屏等外围设备,这时就可以进行配置内核了。
我们把s3c2410 的默认配置写入config 文件。命令如下
make s3c2410_defconfig
输入如下命令进行内核配置,配置的过程,只将提到的修改,其他默认。
make menuconfig
配置文件系统选项,配置yaffs2 文件系统 ,修改配置如下
File systems --->
[*] Miscellaneous filesystems --->
<*> yaffs2 file system support
-*- 512 byte / page devices
[ ] Use older-style on-NAND data format with pageStatus byte
[ ] Lets yaffs do its own ECC (NEW)
-*- 2048 byte (or larger) / page devices
[*] Autoselect yaffs2 format (NEW)
[ ] Disable yaffs from doing ECC on tags by default (NEW)
配置cpu相关选项,修改配置如下:
System Type --->
S3C2440 Machines --->
[*] SMDK2440
[*] SMDK2440 with S3C2440 CPU module
去掉S3C2400 Machines、S3C2410 Machines、S3C2412 Machines、S3C2442 Machines的所有
选项。如果现在编译内核,下载到开发板中,内核就可以正常启动了。
编译内核使用命令如下:
make zImage
生成的内核文件在arch/arm/boot下面,文件为zImage。现在烧写到板子上面,就能够启动
内核了,可是这还不行,还会出现lcd显示不正常,触屏不能使用等情况,这是由于没有移植
相应的驱动或者驱动程序的参数不正确的原因。下面就对其进行改正。
11, 移植USB host 驱动。
在这个版本的linux 内核,已经对USB 驱动进行来很好的支持,仅仅需要修改配置。
Device Drivers --->
[*] USB support --->
{*} Support for Host-side USB
[*] USB device filesystem (DEPRECATED)
[*] USB device class-devices (DEPRECATED)
<*> OHCI HCD support
<*> USB Mass Storage support
[*] HID Devices --->
{*} Generic HID support
[*] /dev/hidraw raw HID device support
SCSI device support --->
<*> SCSI device support
[*] legacy /proc/scsi/ support
<*> SCSI disk support
<*> SCSI tape support
如果从来没有配置过内核的话,基本上上面的都是默认的。不过 <M> USB Mass Storage
support 改为<*>,而默认[ ] /dev/hidraw raw HID device support 要改为<*>,而<M>
SCSI tape support 改为<*>。
目前2.6.28.7 版本的根文件系统的设备管理器是静态生成节点的,所以需要添加节点。开发板
启动以后,在/dev 目录下输入一下命令:
mknod sda1 b 8 1
这个命令中的sda1 是设备节点名
12, 移植RTC 驱动
在这个版本的linux 内核,已经对RTC 驱动进行来很好的支持,不需要修改配置。相应配置如
下
Device Drivers --->
<*> Real Time Clock --->
[*] Set system time from RTC on startup and resume
(rtc0) RTC used to set the system time
[ ] RTC debug support
*** RTC interfaces ***
[*] /sys/class/rtc/rtcN (sysfs)
[*] /proc/driver/rtc (procfs for rtc0)
[*] /dev/rtcN (character devices)
<*> Samsung S3C series SoC RTC
以上都是默认的。
然后添加对设备的支持
打开arch/arm/mach-s3c2440/mach-smdk2440.c,添加设备,代码如下:
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
};
13, 移植UDA1341 驱动
由于这里要用到几个头文件,而Linux2.6.28.7 内核源码中没有,所以要创建。
首先是在include/sound 下建立s3c24xx_uda134x.h,内容为
#ifndef _S3C24XX_UDA134X_H_
#define _S3C24XX_UDA134X_H_ 1
#include <sound/uda134x.h>
struct s3c24xx_uda134x_platform_data {
int l3_clk;
int l3_mode;
int l3_data;
void (*power) (int);
int model;
};
#endif
然后在include/sound/下建立uda134x.h 文件,内容为:
#ifndef _UDA134X_H
#define _UDA134X_H
#include <sound/l3.h>
struct uda134x_platform_data {
struct l3_pins l3;
void (*power) (int);
int model; } //原文有错误,括号打错了
#define UDA134X_UDA1340 1
#define UDA134X_UDA1341 2
#define UDA134X_UDA1344 3
#endif /* _UDA134X_H */
接着在include/sound 下建立l3.h 文件,内容为
#ifndef _L3_H_
#define _L3_H_ 1
struct l3_pins {
void (*setdat)(int);
void (*setclk)(int);
void (*setmode)(int);
int data_hold;
int data_setup;
int clock_high;
int mode_hold;
int mode;
int mode_setup;
};
int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len);
#endif
在arch/arm/mach-s3c2410/include/mach/下建立gpio-fns.h,内容为
/* s3c2410_gpio_cfgpin
*
* set the configuration of the given pin to the value passed.
*
* eg:
* s3c2410_gpio_cfgpin(S3C2410_GPA(0), S3C2410_GPA0_ADDR0);
* s3c2410_gpio_cfgpin(S3C2410_GPE(8), S3C2410_GPE8_SDDAT1);
*/
extern void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function);
extern unsigned int s3c2410_gpio_getcfg(unsigned int pin);
/* s3c2410_gpio_getirq
*
* turn the given pin number into the corresponding IRQ number
*
* returns:
* < 0 = no interrupt for this pin
* >=0 = interrupt number for the pin
*/
extern int s3c2410_gpio_getirq(unsigned int pin);
#ifdef CONFIG_CPU_S3C2400
extern int s3c2400_gpio_getirq(unsigned int pin);
#endif /* CONFIG_CPU_S3C2400 */
/* s3c2410_gpio_irqfilter
*
* set the irq filtering on the given pin
*
* on = 0 => disable filtering
* 1 => enable filtering
*
* config = S3C2410_EINTFLT_PCLK or S3C2410_EINTFLT_EXTCLK orred with
* width of filter (0 through 63)
*
*
*/
extern int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on,
unsigned int config);
/* s3c2410_gpio_pullup
*
* configure the pull-up control on the given pin
*
* to = 1 => disable the pull-up
* 0 => enable the pull-up
*
* eg;
*
* s3c2410_gpio_pullup(S3C2410_GPB(0), 0);
* s3c2410_gpio_pullup(S3C2410_GPE(8), 0);
*/
extern void s3c2410_gpio_pullup(unsigned int pin, unsigned int to);
/* s3c2410_gpio_getpull
*
* Read the state of the pull-up on a given pin
*
* return:
* < 0 => error code
* 0 => enabled
* 1 => disabled
*/
extern int s3c2410_gpio_getpull(unsigned int pin);
extern void s3c2410_gpio_setpin(unsigned int pin, unsigned int to);
extern unsigned int s3c2410_gpio_getpin(unsigned int pin);
在平台上添加和配置UDA1341:
修改arch/arm/mach-s3c2440/mach-smdk2440.c。在开始添加头文件
#include <sound/s3c24xx_uda134x.h>
#include <mach/gpio-fns.h>
添加设备配置
static struct s3c24xx_uda134x_platform_data s3c24xx_uda134x_data = {
.l3_clk = S3C2410_GPB4,
.l3_data = S3C2410_GPB3, //原文有错误
.l3_mode = S3C2410_GPB2,
.model = UDA134X_UDA1341,
};
static struct platform_device s3c24xx_uda134x = {
.name = "s3c24xx_uda134x",
.dev = {
.platform_data = &s3c24xx_uda134x_data,
}
};
把设备添加到平台当中
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
&s3c24xx_uda134x,
};
配置如下
Device Drivers --->
<*> Sound card support --->
<*> Advanced Linux Sound Architecture --->
<*> OSS Mixer API
<*> OSS PCM (digital audio) API
[*] OSS PCM (digital audio) API - Include plugin system
[*] Support old ALSA API
[*] Verbose procfs contents
[*] Verbose printk
[*] Generic sound devices --->
<*> ALSA for SoC audio support --->
<*> SoC Audio for the Samsung S3C24XX chips
<*> SoC I2S Audio support UDA134X wired to a S3C24XX
配置前还有改变sound/soc/s3c24xx/Kconfig 文件
在里面添加如下内容
config SND_S3C24XX_SOC_S3C24XX_UDA134X
tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
depends on SND_S3C24XX_SOC && ARCH_S3C2410
select SND_S3C24XX_SOC_I2S
select SND_SOC_L3
select SND_SOC_UDA134X
只有这样,最后蓝色部分才会出现
14, 移植DM9000 驱动
修改 drivers/net/dm9000.c 文件,头文件增加
#include <mach/regs-gpio.h>
#include <mach/irqs.h>
#include <mach/hardware.h>
在dm9000_probe 函数开始增加:
unsigned char ne_def_eth_mac_addr[]={0x00,0x12,0x34,0x56,0x80,0x49};
static void *bwscon;
static void *gpfcon;
static void *extint0;
static void *intmsk;
#define BWSCON (0x48000000)
#define GPFCON (0x56000050)
#define EXTINT0 (0x56000088)
#define INTMSK (0x4A000008)
bwscon=ioremap_nocache(BWSCON,0x0000004);
gpfcon=ioremap_nocache(GPFCON,0x0000004);
extint0=ioremap_nocache(EXTINT0,0x0000004);
intmsk=ioremap_nocache(INTMSK,0x0000004);
writel(readl(bwscon)|0xc0000,bwscon);
writel( (readl(gpfcon) & ~(0x3 << 14)) | (0x2 << 14), gpfcon);
writel( readl(gpfcon) | (0x1 << 7), gpfcon); // Disable pull-up
writel( (readl(extint0) & ~(0xf << 28)) | (0x4 << 28), extint0); //rising edge
writel( (readl(intmsk)) & ~0x80, intmsk);
在这个函数的最后需要修改:
if (!is_valid_ether_addr(ndev->dev_addr)) {
/* try reading from mac */
mac_src = "chip";
for (i = 0; i < 6; i++)
//ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
ndev->dev_addr[i] = ne_def_eth_mac_addr[i];
}
修改arch/arm/mach-s3c2440/mach-smdk2440.c,添加设备
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
&s3c24xx_uda134x,
&s3c_device_dm9000,
修改 arch/arm/plat-s3c24xx/devs.c,添加头文件
#include <linux/dm9000.h>
static struct resource s3c_dm9000_resource[] = {
[0] = {
.start = S3C24XX_PA_DM9000,
.end = S3C24XX_PA_DM9000+ 0x3,
.flags = IORESOURCE_MEM
},
[1]={
.start = S3C24XX_PA_DM9000 + 0x4, //CMD pin is A2
.end = S3C24XX_PA_DM9000 + 0x4 + 0x7c,
.flags = IORESOURCE_MEM
},
[2] = {
.start = IRQ_EINT7,
.end = IRQ_EINT7,
.flags = IORESOURCE_IRQ
},
};
static struct dm9000_plat_data s3c_device_dm9000_platdata = {
.flags= DM9000_PLATF_16BITONLY,
};
struct platform_device s3c_device_dm9000 = {
.name= "dm9000",
.id= 0,
.num_resources= ARRAY_SIZE(s3c_dm9000_resource),
.resource= s3c_dm9000_resource,
.dev= {
.platform_data = &s3c_device_dm9000_platdata,
}
};
EXPORT_SYMBOL(s3c_device_dm9000);
最后蓝色的部分,似乎其他的版本没有
修改 arch/arm/plat-s3c24xx/include/plat/devs.h
或者有的版本是arch/arm/plat-s3c/include/plat/devs.h
40 行附近,添加
extern struct platform_device s3c_device_dm9000;
修改arch/arm/mach-s3c2410/include/mach/map.h 文件118 行左右
/* DM9000 */
#define S3C24XX_PA_DM9000 0x20000300
#define S3C24XX_VA_DM9000 0xE0000000
或者是
/* DM9000 */
#define S3C24XX_PA_DM9000 0x20000300
#define S3C24XX_VA_DM9000 S3C2410_ADDR(0x02100300)
15, 可能你会看到有的板子上面能够在开机的过程中显示一个企鹅或者其他的图像,如果
要是显示企鹅的话,很简单,只需在配置内核过程中将下面进行配置就可以了。
Graphics support --->
<*> Support for frame buffer devices---à
<*> S3C2410 LCD framebuffer support ,multi support!
Console display driver support --->
<*> Framebuffer Console support
[*] Bootup logo ---à
[*] Standard 224-color Linux logo
这样配置后,编译的内核文件就能显示一个企鹅
16, 3.5 寸LCD 显示的移植 (注意,这里要根据LCD此村进行更改)
更改arch/arm/mach-s3c2440/mach-smdk2440.c
/* LCD driver info */
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
.type = S3C2410_LCDCON1_TFT,
.width = 320,
.height = 240,
.pixclock = 156250,
.xres = 320,
.yres = 240,
.bpp = 16,
.left_margin = 20,//or8,
.right_margin = 38,//or5,
.hsync_len = 30,//or63,
.upper_margin = 12,//or15,
.lower_margin = 15,//or3,
.vsync_len =3,//or5,
};
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
.displays = &smdk2440_lcd_cfg,
.num_displays = 1,//或者ARRAY_SIZE(smdk2440_lcd_cfg),
.default_display = 0,
#if 0
/* currently setup by downloader */
.gpccon = 0xaa940659,
.gpccon_mask = 0xffffffff,
.gpcup = 0x0000ffff,
.gpcup_mask = 0xffffffff,
.gpdcon = 0xaa84aaa0,
.gpdcon_mask = 0xffffffff,
.gpdup = 0x0000faff,
.gpdup_mask = 0xffffffff,
#endif
};
上面红色部分必须修改,否则显示会不正常。其他参数默认。当然这里不是随便改的,具体如
何改,稍后详解。注意红色参数后面的注释
LCD 的参数设定是需要根据LCD 的手册来设定arch/arm/mach-s3c2440/mach-smdk2440.c 里面
的s3c2410fb_display smdk2440_lcd_cfg 结构体
例如从LQ035NC111 的手册可以得到如下一个表
该表描述了该款并行LCD 的所有时钟需求,在这里我参照的全是典型值“Typ”栏
一个很具有参考价值的文档文件是 Documentation/fb/framebuffer.txt 文件,里面给我们描述了
一个架构
还有一个很有用的公式 Pixelclock:
xfree: in MHz
fb: in picoseconds (ps)
pixclock = 1000000 / DCF
再结合结构体 static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
.type = S3C2410_LCDCON1_TFT,
.width = LCD_WIDTH,
.height = LCD_HEIGHT,
.pixclock = LCD_PIXCLOCK,
.xres = LCD_WIDTH,
.yres = LCD_HEIGHT,
.bpp = 16,
.left_margin = LCD_LEFT_MARGIN,
.right_margin = LCD_RIGHT_MARGIN,
.hsync_len = LCD_HSYNC_LEN,
.upper_margin = LCD_UPPER_MARGIN ,
.lower_margin = LCD_LOWER_MARGIN,
.vsync_len = LCD_VSYNC_LEN,
};
pixclock:现在我们就可以开始设置这个结构体的参数了,有上面的表我们可以知道LCD 的
时钟Dclk 应该是156ns,这个对应结构体里面的像素点时钟pixclock,在来看看第四节提到的
一个公式 pixclock=1000000/DCF,这个DCF 就是LCD 的Dclk 对应的频率,注意,单位为MHz,
所以 DCF=1000 000 000/156 Hz=1000/156 MHz;可以得到 pixclock=1000000/(1000/156)
=156000;
width、height 的设定这个就没什么歧义了,对应320 和240
bpp:其实我的这个LCD 手册上说该屏是支持24 位色的,但是这里填写16 位,有空可以试试
24 位
其他的参数:其他参数对应第3 节的表填写
xres <===========> TEP(Thd)(Hsync Display Period) LCD_WIDTH
yres <===========> Tvd(Vsync Display Period)LCD_HEIGHT,
left_margin <===========> Thf(Hsync Front-Porch)LCD_LEFT_MARGIN,
right_margin <===========> Thb(Hsync Back-Porch)LCD_RIGHT_MARGIN
hsync_len <===========> THS(Thp)(Hsync Pulse Width)
upper_margin <===========> Tvf(Vsync Front-Porch)
lower_margin <===========> Tvb(Vsync Back-Porch )
vsync_len <===========> Tvs(Tvp)(Vsync Pulse Width)
fl2440 用的lcd 手册中的表格如下
从而可知,
参数对应为
.width = 320,
.height = 240,
.pixclock = 156000,
.xres =320,
.yres = 240,
.bpp = 16,
.left_margin = 20,
.right_margin = 38,
.hsync_len = 30,
.upper_margin = 12,
.lower_margin = 15,
.vsync_len = 3,
17, 移植看门狗
修改配置
Device Drivers --->
[*] Watchdog Timer Support --->
<*> S3C2410 Watchdog
即可。其实默认配置就是这样的。但是要是打开看门狗还需要修改源码。
18, 触摸屏驱动移植
文件:arch/arm/plat-s3c24xx/devs.c
头文件添加
#include<mach/s3c2410_ts.h> //y原文有误
在最后一行的上面添加如下代码
/* Touchscreen */
struct platform_device s3c_device_ts = {
.name = "s3c2410-ts",
.id = -1,
};
EXPORT_SYMBOL(s3c_device_ts);
static struct s3c2410_ts_mach_info s3c2410ts_info;
void set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info)
{
memcpy(&s3c2410ts_info,hard_s3c2410ts_info,sizeof(struct
s3c2410_ts_mach_info));
s3c_device_ts.dev.platform_data = &s3c2410ts_info;
}
EXPORT_SYMBOL(set_s3c2410ts_info);
在arch/arm/mach-s3c2410/include/mach/下建立文件s3c2410_ts.h,内容为:
/* linux/include/asm/arch-s3c2410/s3c2410_ts.h
*
* Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*
* Changelog:
* 24-Mar-2005 RTP Created file
*/
#ifndef __ASM_ARM_S3C2410_TS_H
#define __ASM_ARM_S3C2410_TS_H
struct s3c2410_ts_mach_info {
int delay;
int presc;
int oversampling_shift;
};
void __init set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info);
#endif /* __ASM_ARM_S3C2410_TS_H */
修改arch/arm/mach-s3c2440/mach-smdk2440.c
添加头文件#include<mach/s3c2410_ts.h>
并在static struct platform_device *smdk2440_devices[] __initdata 结构体数组内添加
&s3c_device_adc,(触摸屏需要ADC 的支持)
&s3c_device_ts, //最好将adc 添加到ts 前面,否则触摸屏初始化时可能会出错//
并在上面结构体后面添加:
/*Config for TouchScreen*/
static struct s3c2410_ts_mach_info smdk2410_ts_cfg __initdata = {
.delay = 20000,
.presc = 49,
.oversampling_shift = 2,
};
最后在static void __init smdk2440_machine_init(void)函数中增加下列代码:
set_s3c2410ts_info(&smdk2410_ts_cfg); //原文有误
由于2.6.22.7 中没有触屏驱动,所以要添加上。而在2.6.33 上面就有了,所以这里改变的要大
些。
在drivers/input/touchscreen 下建立ts.h 和s3c2410_ts.c 文件,内容分别为:
ts.h 内容:
/* linux/include/asm/arch-s3c2410/ts.h
*
* Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*
* Changelog:
* 24-Mar-2005 RTP Created file
* 03-Aug-2005 RTP Renamed to ts.h
*/
#ifndef __ASM_ARM_TS_H
#define __ASM_ARM_TS_H
struct EmbedSky_ts_mach_info {
int delay;
int presc;
int oversampling_shift;
};
void __init set_EmbedSky_ts_info(struct EmbedSky_ts_mach_info *hard_EmbedSky_ts_info);
#endif /* __ASM_ARM_TS_H */
s3c2410_ts.c 内容为:
/#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/s3c2410_ts.h>
#include <plat/regs-adc.h>
#define TRUE 1 //CoAsia added
#define FALSE 0 //CoAsia added
#define FILTER_LIMIT 25 //CoAsia added
/* For ts.dev.id.version */
#define S3C2410TSVERSION 0x0101
#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE |
S3C2410_ADCTSC_XY_PST(0))
#define WAIT4INT(x) (((x)<<8) | \
S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN |
S3C2410_ADCTSC_XP_SEN | \
S3C2410_ADCTSC_XY_PST(3))
#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN |
S3C2410_ADCTSC_XP_SEN | \
S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
#define DEBUG_LVL "<3>" //KERN_DEBUG
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("s3c2410 touchscreen driver");
MODULE_LICENSE("GPL");
/*
* Definitions & global arrays.
*/
static char *s3c2410ts_name = "s3c2410 TouchScreen";
/*
* Per-touchscreen data.
*/
struct s3c2410ts {
struct input_dev *dev;
long xp;
long yp;
int count;
int shift;
};
static struct s3c2410ts ts;
static void __iomem *base_addr;
static inline void s3c2410_ts_connect(void)
{
s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
}
static void touch_timer_fire(unsigned long data)
{
unsigned long data0;
unsigned long data1;
int updown;
data0 = readl(base_addr+S3C2410_ADCDAT0);
data1 = readl(base_addr+S3C2410_ADCDAT1);
updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 &
S3C2410_ADCDAT0_UPDOWN));
if (updown) {
if (ts.count != 0) {
ts.xp >>= ts.shift;
ts.yp >>= ts.shift;
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
{
struct timeval tv;
do_gettimeofday(&tv);
printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n",
(int)tv.tv_usec, ts.xp, ts.yp);
}
#endif
#if 0
if (touch_pen_filtering(&ts.xp, &ts.yp)) { //CoAsia added
input_report_abs(ts.dev, ABS_X, ts.xp); //CoAsia added
input_report_abs(ts.dev, ABS_Y, ts.yp); //CoAsia added
input_report_key(ts.dev, BTN_TOUCH, 1); //CoAsia added
input_report_abs(ts.dev, ABS_PRESSURE, 1); //CoAsia added
input_sync(ts.dev); //CoAsia added
} //CoAsia added
#endif
input_report_abs(ts.dev, ABS_X, ts.xp);
input_report_abs(ts.dev, ABS_Y, ts.yp);
input_report_key(ts.dev, BTN_TOUCH, 1);
input_report_abs(ts.dev, ABS_PRESSURE, 1);
input_sync(ts.dev);
}
ts.xp = 0;
ts.yp = 0;
ts.count = 0;
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
base_addr+S3C2410_ADCTSC);
writel(readl(base_addr+S3C2410_ADCCON) |
S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
} else {
ts.count = 0;
input_report_key(ts.dev, BTN_TOUCH, 0);
input_report_abs(ts.dev, ABS_PRESSURE, 0);
input_sync(ts.dev);
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
}
}
static struct timer_list touch_timer =
TIMER_INITIALIZER(touch_timer_fire, 0, 0);
static irqreturn_t stylus_updown(int irq, void *dev_id)
{
unsigned long data0;
unsigned long data1;
int updown;
data0 = readl(base_addr+S3C2410_ADCDAT0);
data1 = readl(base_addr+S3C2410_ADCDAT1);
updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 &
S3C2410_ADCDAT0_UPDOWN));
/* TODO we should never get an interrupt with updown set while
* the timer is running, but maybe we ought to verify that the
* timer isn't running anyways. */
if (updown)
touch_timer_fire(0);
return IRQ_HANDLED;
}
static irqreturn_t stylus_action(int irq, void *dev_id)
{
unsigned long data0;
unsigned long data1;
data0 = readl(base_addr+S3C2410_ADCDAT0);
data1 = readl(base_addr+S3C2410_ADCDAT1);
ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
ts.count++;
if (ts.count < (1<<ts.shift)) {
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
base_addr+S3C2410_ADCTSC);
writel(readl(base_addr+S3C2410_ADCCON) |
S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
} else {
mod_timer(&touch_timer, jiffies+1);
writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
}
return IRQ_HANDLED;
}
static struct clk *adc_clock;
/*
* The functions for inserting/removing us as a module.
*/
static int __init s3c2410ts_probe(struct platform_device *pdev)
{
int rc;
struct s3c2410_ts_mach_info *info;
struct input_dev *input_dev;
info = ( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
if (!info)
{
printk(KERN_ERR "Hm... too bad : no platform data for ts\n");
return -EINVAL;
}
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
printk(DEBUG_LVL "Entering s3c2410ts_init\n");
#endif
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
printk(KERN_ERR "failed to get adc clock source\n");
return -ENOENT;
}
clk_enable(adc_clock);
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
printk(DEBUG_LVL "got and enabled clock\n");
#endif
base_addr = ioremap(S3C2410_PA_ADC,0x20);
if (base_addr == NULL) {
printk(KERN_ERR "Failed to remap register block\n");
return -ENOMEM;
}
/* If we acutally are a S3C2410: Configure GPIOs */
if (!strcmp(pdev->name, "s3c2410-ts"))
s3c2410_ts_connect();
if ((info->presc&0xff) > 0)
writel(S3C2410_ADCCON_PRSCEN |
S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
base_addr+S3C2410_ADCCON);
else
writel(0,base_addr+S3C2410_ADCCON);
/* Initialise registers */
if ((info->delay&0xffff) > 0)
writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY);
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
/* Initialise input stuff */
memset(&ts, 0, sizeof(struct s3c2410ts));
input_dev = input_allocate_device();
if (!input_dev) {
printk(KERN_ERR "Unable to allocate the input device !!\n");
return -ENOMEM;
}
ts.dev = input_dev;
ts.dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) |
BIT_MASK(EV_ABS);
ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
//ts.dev->private = &ts;
ts.dev->name = s3c2410ts_name;
ts.dev->id.bustype = BUS_RS232;
ts.dev->id.vendor = 0xDEAD;
ts.dev->id.product = 0xBEEF;
ts.dev->id.version = S3C2410TSVERSION;
ts.shift = info->oversampling_shift;
/* Get irqs */
if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
"s3c2410_action", ts.dev)) {
printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");
iounmap(base_addr);
return -EIO;
}
if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
"s3c2410_action", ts.dev)) {
printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
free_irq(IRQ_ADC, ts.dev);
iounmap(base_addr);
return -EIO;
}
printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);
/* All went ok, so register to the input system */
rc = input_register_device(ts.dev);
if (rc) {
free_irq(IRQ_TC, ts.dev);
free_irq(IRQ_ADC, ts.dev);
clk_disable(adc_clock);
iounmap(base_addr);
return -EIO;
}
return 0;
}
static int s3c2410ts_remove(struct platform_device *pdev)
{
disable_irq(IRQ_ADC);
disable_irq(IRQ_TC);
free_irq(IRQ_TC,ts.dev);
free_irq(IRQ_ADC,ts.dev);
if (adc_clock) {
clk_disable(adc_clock);
clk_put(adc_clock);
adc_clock = NULL;
}
input_unregister_device(ts.dev);
iounmap(base_addr);
return 0;
}
#ifdef CONFIG_PM
static int s3c2410ts_suspend(struct platform_device *pdev, pm_message_t state)
{
writel(TSC_SLEEP, base_addr+S3C2410_ADCTSC);
writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_STDBM,
base_addr+S3C2410_ADCCON);
disable_irq(IRQ_ADC);
disable_irq(IRQ_TC);
clk_disable(adc_clock);
return 0;
}
static int s3c2410ts_resume(struct platform_device *pdev)
{
struct s3c2410_ts_mach_info *info =
( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
clk_enable(adc_clock);
msleep(1);
enable_irq(IRQ_ADC);
enable_irq(IRQ_TC);
if ((info->presc&0xff) > 0)
writel(S3C2410_ADCCON_PRSCEN |
S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
base_addr+S3C2410_ADCCON);
else
writel(0,base_addr+S3C2410_ADCCON);
/* Initialise registers */
if ((info->delay&0xffff) > 0)
writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY);
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
return 0;
}
#else
#define s3c2410ts_suspend NULL
#define s3c2410ts_resume NULL
#endif
static struct platform_driver s3c2410ts_driver = {
.driver = {
.name = "s3c2410-ts",
.owner = THIS_MODULE,
},
.probe = s3c2410ts_probe,
.remove = s3c2410ts_remove,
.suspend = s3c2410ts_suspend,
.resume = s3c2410ts_resume,
};
static struct platform_driver s3c2440ts_driver = {
.driver = {
.name = "s3c2440-ts",
.owner = THIS_MODULE,
},
.probe = s3c2410ts_probe,
.remove = s3c2410ts_remove,
.suspend = s3c2410ts_suspend,
.resume = s3c2410ts_resume,
};
static int __init s3c2410ts_init(void)
{
int rc;
rc = platform_driver_register(&s3c2410ts_driver);
if (rc < 0)
return rc;
rc = platform_driver_register(&s3c2440ts_driver);
if (rc < 0)
platform_driver_unregister(&s3c2410ts_driver);
return rc;
}
static void __exit s3c2410ts_exit(void)
{
platform_driver_unregister(&s3c2440ts_driver);
platform_driver_unregister(&s3c2410ts_driver);
}
module_init(s3c2410ts_init);
module_exit(s3c2410ts_exit);
修改drivers/input/touchscreen 下的Kconfig 文件
在if INPUT_TOUCHSCREEN 下添加如下内容
config TOUCHSCREEN_S3C2410
tristate "Samsung S3C2410 touchscreen input driver"
depends on ARCH_S3C2410 && INPUT && INPUT_TOUCHSCREEN
select SERIO
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.
config TOUCHSCREEN_S3C2410_DEBUG
boolean "Samsung S3C2410 touchscreen debug messages"
depends on TOUCHSCREEN_S3C2410
help
Select this if you want debug messages
修改drivers/input/touchscreen 下的Makefile 文件
在最后添加
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
修改drivers/input 下的Kconfig 文件
在config INPUT_EVDEV 上面添加如下内容
config INPUT_TSDEV
tristate "Touchscreen interface"
---help---
Say Y here if you have an application that only can understand the
Compaq touchscreen protocol for absolute pointer data. This is
useful namely for embedded configurations.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called tsdev.
config INPUT_TSDEV_SCREEN_X
int "Horizontal screen resolution"
depends on INPUT_TSDEV
default "1024"
config INPUT_TSDEV_SCREEN_Y
int "Vertical screen resolution"
depends on INPUT_TSDEV
default "768"
修改drivers/input/下Makefile 文件
在obj-$(CONFIG_INPUT_EVBUG) += evbug.o 上面添加如下内容
obj-$(CONFIG_INPUT_TSDEV) += tsdev.o
在drivers/input 下创建文件tsdev.c,内容为:
#define TSDEV_MINOR_BASE 128
#define TSDEV_MINORS 32
/* First 16 devices are h3600_ts compatible; second 16 are h3600_tsraw */
#define TSDEV_MINOR_MASK 15
#define TSDEV_BUFFER_SIZE 64
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/major.h>
#include <linux/random.h>
#include <linux/time.h>
#include <linux/device.h>
#ifndef CONFIG_INPUT_TSDEV_SCREEN_X
#define CONFIG_INPUT_TSDEV_SCREEN_X 240
#endif
#ifndef CONFIG_INPUT_TSDEV_SCREEN_Y
#define CONFIG_INPUT_TSDEV_SCREEN_Y 320
#endif
MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
MODULE_DESCRIPTION("Input driver to touchscreen converter");
MODULE_LICENSE("GPL");
static int xres = CONFIG_INPUT_TSDEV_SCREEN_X;
module_param(xres, uint, 0);
MODULE_PARM_DESC(xres, "Horizontal screen resolution (can be negative for X-mirror)");
static int yres = CONFIG_INPUT_TSDEV_SCREEN_Y;
module_param(yres, uint, 0);
MODULE_PARM_DESC(yres, "Vertical screen resolution (can be negative for Y-mirror)");
/* From Compaq's Touch Screen Specification version 0.2 (draft) */
struct ts_event {
short pressure;
short x;
short y;
short millisecs;
};
struct ts_calibration {
int xscale;
int xtrans;
int yscale;
int ytrans;
int xyswap;
};
struct tsdev {
int exist;
int open;
int minor;
char name[8];
struct input_handle handle;
wait_queue_head_t wait;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
int x, y, pressure;
struct ts_calibration cal;
};
struct tsdev_client {
struct fasync_struct *fasync;
struct list_head node;
struct tsdev *tsdev;
struct ts_event buffer[TSDEV_BUFFER_SIZE];
int head, tail;
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
int raw;
};
/* The following ioctl codes are defined ONLY for backward compatibility.
* Don't use tsdev for new developement; use the tslib library instead.
* Touchscreen calibration is a fully userspace task.
*/
/* Use 'f' as magic number */
#define IOC_H3600_TS_MAGIC 'f'
#define TS_GET_CAL _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration)
#define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration)
static struct tsdev *tsdev_table[TSDEV_MINORS/2];
static DEFINE_MUTEX(tsdev_table_mutex);
static int tsdev_fasync(int fd, struct file *file, int on)
{
struct tsdev_client *client = file->private_data;
int retval;
retval = fasync_helper(fd, file, on, &client->fasync);
return retval < 0 ? retval : 0;
}
static void tsdev_free(struct device *dev)
{
struct tsdev *tsdev = container_of(dev, struct tsdev, dev);
kfree(tsdev);
}
static void tsdev_attach_client(struct tsdev *tsdev, struct tsdev_client *client)
{
spin_lock(&tsdev->client_lock);
list_add_tail_rcu(&client->node, &tsdev->client_list);
spin_unlock(&tsdev->client_lock);
synchronize_sched();
}
static void tsdev_detach_client(struct tsdev *tsdev, struct tsdev_client *client)
{
spin_lock(&tsdev->client_lock);
list_del_rcu(&client->node);
spin_unlock(&tsdev->client_lock);
synchronize_sched();
}
static int tsdev_open_device(struct tsdev *tsdev)
{
int retval;
retval = mutex_lock_interruptible(&tsdev->mutex);
if (retval)
return retval;
if (!tsdev->exist)
retval = -ENODEV;
else if (!tsdev->open++) {
retval = input_open_device(&tsdev->handle);
if (retval)
tsdev->open--;
}
mutex_unlock(&tsdev->mutex);
return retval;
}
static void tsdev_close_device(struct tsdev *tsdev)
{
mutex_lock(&tsdev->mutex);
if (tsdev->exist && !--tsdev->open)
input_close_device(&tsdev->handle);
mutex_unlock(&tsdev->mutex);
}
/*
* Wake up users waiting for IO so they can disconnect from
* dead device.
*/
static void tsdev_hangup(struct tsdev *tsdev)
{
struct tsdev_client *client;
spin_lock(&tsdev->client_lock);
list_for_each_entry(client, &tsdev->client_list, node)
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
spin_unlock(&tsdev->client_lock);
wake_up_interruptible(&tsdev->wait);
}
static int tsdev_release(struct inode *inode, struct file *file)
{
struct tsdev_client *client = file->private_data;
struct tsdev *tsdev = client->tsdev;
tsdev_fasync(-1, file, 0);
tsdev_detach_client(tsdev, client);
kfree(client);
tsdev_close_device(tsdev);
put_device(&tsdev->dev);
return 0;
}
static int tsdev_open(struct inode *inode, struct file *file)
{
int i = iminor(inode) - TSDEV_MINOR_BASE;
struct tsdev_client *client;
struct tsdev *tsdev;
int error;
if (i >= TSDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&tsdev_table_mutex);
if (error)
return error;
tsdev = tsdev_table[i & TSDEV_MINOR_MASK];
if (tsdev)
get_device(&tsdev->dev);
mutex_unlock(&tsdev_table_mutex);
if (!tsdev)
return -ENODEV;
client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL);
if (!client) {
error = -ENOMEM;
goto err_put_tsdev;
}
spin_lock_init(&client->buffer_lock);
client->tsdev = tsdev;
client->raw = i >= TSDEV_MINORS / 2;
tsdev_attach_client(tsdev, client);
error = tsdev_open_device(tsdev);
if (error)
goto err_free_client;
file->private_data = client;
return 0;
err_free_client:
tsdev_detach_client(tsdev, client);
kfree(client);
err_put_tsdev:
put_device(&tsdev->dev);
return error;
}
static int tsdev_fetch_next_event(struct tsdev_client *client,
struct ts_event *event)
{
int have_event;
spin_lock_irq(&client->buffer_lock);
have_event = client->head != client->tail;
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= TSDEV_BUFFER_SIZE - 1;
}
spin_unlock_irq(&client->buffer_lock);
return have_event;
}
static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
loff_t *ppos)
{
struct tsdev_client *client = file->private_data;
struct tsdev *tsdev = client->tsdev;
struct ts_event event;
int retval;
if (client->head == client->tail && tsdev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(tsdev->wait,
client->head != client->tail || !tsdev->exist);
if (retval)
return retval;
if (!tsdev->exist)
return -ENODEV;
while (retval + sizeof(struct ts_event) <= count &&
tsdev_fetch_next_event(client, &event)) {
if (copy_to_user(buffer + retval, &event,
sizeof(struct ts_event)))
return -EFAULT;
retval += sizeof(struct ts_event);
}
return retval;
}
/* No kernel lock - fine */
static unsigned int tsdev_poll(struct file *file, poll_table *wait)
{
struct tsdev_client *client = file->private_data;
struct tsdev *tsdev = client->tsdev;
poll_wait(file, &tsdev->wait, wait);
return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) |
(tsdev->exist ? 0 : (POLLHUP | POLLERR));
}
static long tsdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct tsdev_client *client = file->private_data;
struct tsdev *tsdev = client->tsdev;
int retval = 0;
retval = mutex_lock_interruptible(&tsdev->mutex);
if (retval)
return retval;
if (!tsdev->exist) {
retval = -ENODEV;
goto out;
}
switch (cmd) {
case TS_GET_CAL:
if (copy_to_user((void __user *)arg, &tsdev->cal,
sizeof (struct ts_calibration)))
retval = -EFAULT;
break;
case TS_SET_CAL:
if (copy_from_user(&tsdev->cal, (void __user *)arg,
sizeof(struct ts_calibration)))
retval = -EFAULT;
break;
default:
retval = -EINVAL;
break;
}
out:
mutex_unlock(&tsdev->mutex);
return retval;
}
static const struct file_operations tsdev_fops = {
.owner = THIS_MODULE,
.open = tsdev_open,
.release = tsdev_release,
.read = tsdev_read,
.poll = tsdev_poll,
.fasync = tsdev_fasync,
.unlocked_ioctl = tsdev_ioctl,
};
static void tsdev_pass_event(struct tsdev *tsdev, struct tsdev_client *client,
int x, int y, int pressure, int millisecs)
{
struct ts_event *event;
int tmp;
/* Interrupts are already disabled, just acquire the lock */
spin_lock(&client->buffer_lock);
event = &client->buffer[client->head++];
client->head &= TSDEV_BUFFER_SIZE - 1;
/* Calibration */
if (!client->raw) {
x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans;
y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans;
if (tsdev->cal.xyswap) {
tmp = x; x = y; y = tmp;
}
}
event->millisecs = millisecs;
event->x = x;
event->y = y;
event->pressure = pressure;
spin_unlock(&client->buffer_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
static void tsdev_distribute_event(struct tsdev *tsdev)
{
struct tsdev_client *client;
struct timeval time;
int millisecs;
do_gettimeofday(&time);
millisecs = time.tv_usec / 1000;
list_for_each_entry_rcu(client, &tsdev->client_list, node)
tsdev_pass_event(tsdev, client,
tsdev->x, tsdev->y,
tsdev->pressure, millisecs);
}
static void tsdev_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
struct tsdev *tsdev = handle->private;
struct input_dev *dev = handle->dev;
int wake_up_readers = 0;
switch (type) {
case EV_ABS:
switch (code) {
case ABS_X:
tsdev->x = value;
break;
case ABS_Y:
tsdev->y = value;
break;
case ABS_PRESSURE:
if (value > dev->absmax[ABS_PRESSURE])
value = dev->absmax[ABS_PRESSURE];
value -= dev->absmin[ABS_PRESSURE];
if (value < 0)
value = 0;
tsdev->pressure = value;
break;
}
break;
case EV_REL:
switch (code) {
case REL_X:
tsdev->x += value;
if (tsdev->x < 0)
tsdev->x = 0;
else if (tsdev->x > xres)
tsdev->x = xres;
break;
case REL_Y:
tsdev->y += value;
if (tsdev->y < 0)
tsdev->y = 0;
else if (tsdev->y > yres)
tsdev->y = yres;
break;
}
break;
case EV_KEY:
if (code == BTN_TOUCH || code == BTN_MOUSE) {
switch (value) {
case 0:
tsdev->pressure = 0;
break;
case 1:
if (!tsdev->pressure)
tsdev->pressure = 1;
break;
}
}
break;
case EV_SYN:
if (code == SYN_REPORT) {
tsdev_distribute_event(tsdev);
wake_up_readers = 1;
}
break;
}
if (wake_up_readers)
wake_up_interruptible(&tsdev->wait);
}
static int tsdev_install_chrdev(struct tsdev *tsdev)
{
tsdev_table[tsdev->minor] = tsdev;
return 0;
}
static void tsdev_remove_chrdev(struct tsdev *tsdev)
{
mutex_lock(&tsdev_table_mutex);
tsdev_table[tsdev->minor] = NULL;
mutex_unlock(&tsdev_table_mutex);
}
/*
* Mark device non-existant. This disables writes, ioctls and
* prevents new users from opening the device. Already posted
* blocking reads will stay, however new ones will fail.
*/
static void tsdev_mark_dead(struct tsdev *tsdev)
{
mutex_lock(&tsdev->mutex);
tsdev->exist = 0;
mutex_unlock(&tsdev->mutex);
}
static void tsdev_cleanup(struct tsdev *tsdev)
{
struct input_handle *handle = &tsdev->handle;
tsdev_mark_dead(tsdev);
tsdev_hangup(tsdev);
tsdev_remove_chrdev(tsdev);
/* tsdev is marked dead so noone else accesses tsdev->open */
if (tsdev->open)
input_close_device(handle);
}
static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct tsdev *tsdev;
int delta;
int minor;
int error;
for (minor = 0; minor < TSDEV_MINORS / 2; minor++)
if (!tsdev_table[minor])
break;
if (minor == TSDEV_MINORS) {
printk(KERN_ERR "tsdev: no more free tsdev devices\n");
return -ENFILE;
}
tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL);
if (!tsdev)
return -ENOMEM;
INIT_LIST_HEAD(&tsdev->client_list);
spin_lock_init(&tsdev->client_lock);
mutex_init(&tsdev->mutex);
init_waitqueue_head(&tsdev->wait);
snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor);
tsdev->exist = 1;
tsdev->minor = minor;
tsdev->handle.dev = dev;
tsdev->handle.name = tsdev->name;
tsdev->handle.handler = handler;
tsdev->handle.private = tsdev;
/* Precompute the rough calibration matrix */
delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1;
if (delta == 0)
delta = 1;
tsdev->cal.xscale = (xres << 8) / delta;
tsdev->cal.xtrans = - ((dev->absmin [ABS_X] * tsdev->cal.xscale) >> 8);
delta = dev->absmax [ABS_Y] - dev->absmin [ABS_Y] + 1;
if (delta == 0)
delta = 1;
tsdev->cal.yscale = (yres << 8) / delta;
tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8);
strlcpy(tsdev->dev.bus_id, tsdev->name, sizeof(tsdev->dev.bus_id));
tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);
tsdev->dev.class = &input_class;
tsdev->dev.parent = &dev->dev;
tsdev->dev.release = tsdev_free;
device_initialize(&tsdev->dev);
error = input_register_handle(&tsdev->handle);
if (error)
goto err_free_tsdev;
error = tsdev_install_chrdev(tsdev);
if (error)
goto err_unregister_handle;
error = device_add(&tsdev->dev);
if (error)
goto err_cleanup_tsdev;
return 0;
err_cleanup_tsdev:
tsdev_cleanup(tsdev);
err_unregister_handle:
input_unregister_handle(&tsdev->handle);
err_free_tsdev:
put_device(&tsdev->dev);
return error;
}
static void tsdev_disconnect(struct input_handle *handle)
{
struct tsdev *tsdev = handle->private;
device_del(&tsdev->dev);
tsdev_cleanup(tsdev);
input_unregister_handle(handle);
put_device(&tsdev->dev);
}
static const struct input_device_id tsdev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [BTN_LEFT/32] = BIT(BTN_LEFT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) },
}, /* A mouse like device, at least one button, two relative axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [BTN_TOUCH/32] = BIT(BTN_TOUCH) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
}, /* A tablet like device, at least touch detection, two absolute axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) },
}, /* A tablet like device with several gradations of pressure */
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(input, tsdev_ids);
static struct input_handler tsdev_handler = {
.event = tsdev_event,
.connect = tsdev_connect,
.disconnect = tsdev_disconnect,
.fops = &tsdev_fops,
.minor = TSDEV_MINOR_BASE,
.name = "tsdev",
.id_table = tsdev_ids,
};
static int __init tsdev_init(void)
{
return input_register_handler(&tsdev_handler);
}
static void __exit tsdev_exit(void)
{
input_unregister_handler(&tsdev_handler);
}
module_init(tsdev_init);
module_exit(tsdev_exit);
修改arch/arm/plat-s3c24xx/s3c244x.c
在void __init s3c244x_map_io(struct map_desc *mach_desc, int size)函数中,添加如下内容
s3c_device_ts.name = "s3c2440-ts";
修改arch/arm/plat-s3c24xx/include/plat/devs.h,添加如下内容
extern struct platform_device s3c_device_ts;
在配置菜单中添加如下项:
Device Drivers --->
<*>Input device support --->
<*> Touchscreen interface
(1024) Horizontal screen resolution
(768) Vertical screen resolution
<*>Touchscreens --->
<*> Samsung S3C2410 touchscreen input driver
这里如果选择配置含有debug 字样的那一选项,则内核在烧写到板子上,你每次
触屏都会打印触屏的坐标值。为了不让打印,我们不选择该项。
19, 至此,移植基本完成,其他驱动可以自己根据具体情况进行添加。