0. 我的开发板
===============================================
我的开发板是 TINY210V2 MLC 2G 的这个。
内核版本是 光盘自带的 3.0.8
屏幕是 7存的 电容触摸屏, superboot 在烧写程序的时候显示 S70(auto)。
1. 内核配置文件 - mini210_linux_defconfig
===============================================
CONFIG_TOUCHSCREEN_1WIRE=y
CONFIG_TOUCHSCREEN_IF=y
CONFIG_TOUCHSCREEN_GOODIX=y
CONFIG_TOUCHSCREEN_FT5X0X=y无论是 android 还是 linux 的配置文件,这几个和touchpanel相关的选项都是 y,也就是编译到内核里。
2. 模块的Makefile - drivers/input/touchscreen/Makefile
===============================================
obj-$(CONFIG_TOUCHSCREEN_1WIRE) += mini210_1wire_host.o
obj-$(CONFIG_TOUCHSCREEN_IF) += ts-if.o
ifneq ($(wildcard drivers/input/touchscreen/goodix_touch.c),)
ifeq ($(FA),1)
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_touch.o
else
obj-m += goodix_touch.o
endif #FA
endif
ifneq ($(wildcard drivers/input/touchscreen/ft5x06_ts.c),)
ifeq ($(FA),1)
obj-$(CONFIG_TOUCHSCREEN_FT5X0X) += ft5x06_ts.o
else
obj-m += ft5x06_ts.o
endif #FA
endif这里就很奇怪了,我的理解是 如果存在 drivers/input/touchscreen/ft5x06_ts.c 和 drivers/input/touchscreen/goodix_touch.c 这两个 C
文件的话,就将他们编译成 ft5x06_ts.o 和 goodix_touch.o ,否则什么都不做。
但内核源代码中,我还真的没找到这两个源文件,( 友善不开源吗? )
虽然这两个头文件没有了,但 Makefile 仍然会将 “#define CONFIG_TOUCHSCREEN_GOODIX y"传递给所有的内核源代码,这个宏定义在后来的代码中有用到。
3. 是不是CTP的判断 - arch/arm/mach-s5pv210/mini210-lcds.c
===============================================
#if defined(CONFIG_TOUCHSCREEN_GOODIX) || \
defined(CONFIG_TOUCHSCREEN_FT5X0X)
static unsigned int ctp_type = CTP_NONE;
static int __init mini210_set_ctp(char *str)
{
unsigned int val;
char *p, *end;
printk( "+%s( %s )\n", __FUNCTION__, str );
p = str;
val = simple_strtoul(p, &end, 10);
if (end <= p) {
return 1;
}
if (val < CTP_MAX && mini210_lcd_config[lcd_idx].ctp) {
ctp_type = val;
}
printk( "-%s( ctp_type = %d)\n", __FUNCTION__, ctp_type );
return 1;
}
__setup("ctp=", mini210_set_ctp);
unsigned int mini210_get_ctp(void)
{
printk( "+%s()\n", __FUNCTION__ );
printk( "-%s( ctp_type = %d)\n", __FUNCTION__, ctp_type );
return ctp_type;
}
EXPORT_SYMBOL(mini210_get_ctp);
#endif在用Superboot烧写内核参数的时候,比如:
Linux-CommandLine = root=/dev/mtdblock4 console=ttySAC0,115200 init=/linuxrc skipcali=yes ctp=3
如果传递了 ctp=XX ,就会触发 __setup("ctp=", mini210_set_ctp) , mini210_set_ctp就会被调用
这个函数获取 ctp=XX 中的 XX 进行判断,并且还判断了 mini210_lcd_config 表格中的 ctp 信息。然后设置 ctp_type。
其他模块可以调用 mini210_get_ctp 来判断是不是 ctp。
我猜测 , ctp 是 capacitance touch panel 的意思。( 电容触摸屏 )
4. 1wire内核驱动 - drivers/input/touchscreen/mini210_1wire_host.c
===============================================
在模块初始化的代码中, 调用了 mini210_get_ctp 来判断是不是 ctp。如下:
if (mini210_get_ctp() != CTP_NONE) {
has_ts_data = 0;
timer_interval = HZ / 20;
printk( "*%s(): set has_ts_data = 0, timer_interval = 50ms \n", __FUNCTION__ );
}如果是 ctp 的话,就会将 has_ts_data 设置为 0.
注意这个 has_ts_data , 如果它是0的话, 1wire 驱动就不会去获取 TS 信息了。如下:
void one_wire_timer_proc(unsigned long v)
{
unsigned char req;
// printk( "+%s()\n", __FUNCTION__ );
if (exitting) {
printk( "*%s(): exitting \n", __FUNCTION__ );
return;
}
one_wire_timer.expires = jiffies + timer_interval;
add_timer(&one_wire_timer);
if (lcd_type == 0) {
req = REQ_INFO; /* 0x60 */
} else if (!backlight_init_success) {
req = 127; /* 0x7F */
} else if (backlight_req) {
req = backlight_req; /* 0x8? */
backlight_req = 0;
} else if (has_ts_data) {
req = REQ_TS; /* 0x40 */
} else {
// printk( "*%s(): do nothing\n", __FUNCTION__ );
return;
}
start_one_wire_session(req);
// printk( "-%s()\n", __FUNCTION__ );
}这个函数就是 probe 函数中启动的 timer 处理函数,我理解是每隔一段时间就会被调用一下。
这个函数中默认有一个处理顺序, 先进行 REQ_INFO 的处理,处理完以后才会向下处理 blacklight 背光,然后才是 REQ_TS 。
这个 REQ_TS 我猜测是去获取 touch screen 的信息吧。
但如果 has_ts_data 被设置为 0 的话,就不会去获取了。
在现有代码中,好像 如果传递了 ctp = XX 给内核的话, 就会导致 驱动程序获取完 INFO 和 backlight 信息以后,就再也不去获取 REQ_TS 信息了。
1wire 驱动程序 和 touchpanel 之间的通信,是有时序的, 这个时序应该可以参考 timer_for_1wire_interrupt 这个函数。
如果是 REQ_TS 信息获取到的话, 可以参考 one_wire_session_complete 函数理解数据格式:如下:
static void one_wire_session_complete(unsigned char req, unsigned int res)
{
unsigned char crc;
const unsigned char *p = (const unsigned char*)&res;
//printk( "+%s( req = %02X, res = %08X )\n", __FUNCTION__, req, res );
total_received ++;
last_res = res;
crc8_init(crc);
crc8(crc, p[3]);
crc8(crc, p[2]);
crc8(crc, p[1]);
if (crc != p[0]) {
// CRC dismatch
if (total_received > 100) {
total_error++;
}
//printk( "*%s( ): CRC check ERROR\n", __FUNCTION__ );
return;
}
switch(req) {
case REQ_TS:
{
unsigned short x,y;
unsigned pressed;
x = ((p[3] >> 4U) << 8U) + p[2];
y = ((p[3] & 0xFU) << 8U) + p[1];
pressed = (x != 0xFFFU) && (y != 0xFFFU);
printk( "*%s( ): REQ_TS done : x=%04X y=%04X pressed=%08X\n", __FUNCTION__, x, y, pressed );
notify_ts_data(x, y, pressed);
}
break;
case REQ_INFO:
printk( "*%s( ): REQ_INFO done : %02X-%02X-%02X \n", __FUNCTION__, p[3], p[2], p[1] );
notify_info_data(p[3], p[2], p[1]);
break;
default:
printk( "*%s( ): REQ_BL done : %02X-%02X-%02X \n", __FUNCTION__, p[3], p[2], p[1] );
notify_bl_data(p[3], p[2], p[1]);
break;
}
//printk( "-%s( )\n", __FUNCTION__ );
}在one_wire_session_complete这个函数中, 我们可以看到如果是 REQ_TS 的话, 驱动程序会上报 ( x,y )这个地方是压下还是抬起。
我理解, 坐标信息是通过 刚才的 通信从 外设获取到了。一共4个字节。
第3字节的高4位+第2字节 是 X。
第3字节的低4位+第1字节 是 Y。
如果 X 和 Y 都不是 0xFFF 的话,就是压下。也就是说 如果没有压下的话, X 和 Y 就都是 0xFFF。
通知 用户空间的应用程序 的时候, 通过唤醒等待队列 ts_waitq 中的进程进行的。如下:
static inline void notify_ts_data(unsigned x, unsigned y, unsigned down)
{
if (!down && !(ts_status &(1U << 31))) {
// up repeat, give it up
return;
}
ts_status = ((x << 16) | (y)) | (down << 31);
ts_ready = 1;
wake_up_interruptible(&ts_waitq);
}
static ssize_t ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
unsigned long err;
printk( "+%s()\n", __FUNCTION__ );
if (!ts_ready) {
if (filp->f_flags & O_NONBLOCK){
printk( "*%s() : NONBLOCK -> EAGAIN\n", __FUNCTION__ );
return -EAGAIN;
}
else{
wait_event_interruptible(ts_waitq, ts_ready);
}
}
ts_ready = 0;
if (count < sizeof ts_status) {
printk( "*%s() : ERROR : count < sizeof(ts_status) \n", __FUNCTION__ );
return -EINVAL;
} else {
count = sizeof ts_status;
}
printk( "*%s() : copy_to_user : %08X \n", __FUNCTION__, ts_status );
err = copy_to_user((void *)buffer, (const void *)(&ts_status), sizeof ts_status);
printk( "-%s()\n", __FUNCTION__ );
return err ? -EFAULT : sizeof ts_status;
}
static struct file_operations ts_fops = {
owner: THIS_MODULE,
read: ts_read,
poll: ts_poll,
};
static struct miscdevice ts_misc = {
.minor = 181,
.name = TOUCH_DEVICE_NAME,
.fops = &ts_fops,
};
#define TOUCH_DEVICE_NAME "touchscreen-1wire"通过上面的代码,我理解是对 /dev/touchscreen-1wire 这个文件进行 read 操作的时候,
会被放到 ts_waitq 队列上进行睡眠。
1wire 驱动程序 会定周期获取 touch screen 信息,如果获取到了,就唤醒 刚才 read 的那个进程,并将 (x,y)touch ? 信息告诉它。
问题是 传递了 ctp = X 以后, 1wire 的定周期处理,一直走不到 REQ_TS 的处理。导致我的 ts_calibrate 程序一直 block 在 read 里面。
另外, 如果去 读取 /proc/driver/one-wire-info 这个文件的话, 会调用 read_proc 函数获取信息:如下:
static int read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data)
{
int len;
printk( "+%s()\n", __FUNCTION__ );
len = sprintf(buf, "lcd_type %u, firmware_ver %u, total_received %u, total_error %u, last_req %04X, last_res %08X\n",
lcd_type, firmware_ver, total_received, total_error, last_req, last_res);
*eof = 1;
printk( "-%s()\n", __FUNCTION__ );
return len;
}这几个信息,是在 定周期获取信息的处理中,第一步获取 REQ_INFO 中获取到的。
5. TSLIB的1wire plugin - tslib-1.0.0/plugins/one-wire-ts-input.c
===============================================
neechard 文档中的 [ 一线触摸tslib移植完全说明 ] 中,
http://www.aiothome.net/read.php?tid-20248.html针对 1wire 的 TSLIB 的驱动,就能够和 内核中的 这个部分对应上:
static int misc_read(struct tslib_module_info *inf, struct ts_sample *samp, int nr)
{
struct tsdev *ts = inf->dev;
int ret;
unsigned ts_status;
ret = read(ts->fd, &ts_status, sizeof ts_status);
if (ret < 0) {
return 0;
}
if (ret == 0) {
return 0;
}
samp->x = ((ts_status) >> 16) & 0x7FFF;
samp->y = ts_status & 0x7FFF;
samp->pressure = ts_status >> 31;
gettimeofday(&samp->tv,NULL);
nr = nr;
return 1;
}
misc_read是 TSLIB 读取 /dev/touchscreen-1wire 设备文件的处理函数,
ts_read 是 /dev/touchscreen-1wire 给应用程序 touch screen 的 坐标信息。
我粗略的比较了一下 数据 格式,应该是可以对上的。X Y touch?