tslib
,很早以前用了,深入瞭解了它的原理,但是當時忙,就沒寫成文檔了,今天發現對它有點陌生了,覺得如果再不記錄下來的話,估計以後就忘了。估計大家都聽說過tslib,其實從他的名字就可以看出它的,它是touchscreen的lib,其實這樣還不夠具體,其實它開始確實是爲了touchscreen的鼠標驅動而發展起來的,且只是一箇中間處理庫,即將原始數據進行調整,比如觸摸屏定位。只不過後來不知道什麼原因,它火了,其他圖形都支持這種方式,像高級版本的minigui,qt等成熟嵌入式圖形系統。正因爲如此,它也就不再侷限於touchsrceen,只要是輸入設備,只需在tslib裏實現,標準的圖形系統只需調用tslib的函數即可。
剛開始時,我曾認爲tslib屬於驅動層,它將其他的輸入設備數據處理後成爲一個虛擬設備的數據,其他的圖形系統只需使用這個虛擬的設備即可實現輸入設備的讀寫操作了。後來發現tslib只不過是一個應用程軟件,其他的圖形系統使用的是tslib的函數,因此需要依賴這個庫.如果大家移植過qt,估計還記得-no-mouse-linuxtp -qt-mouse-tslib -I/mnt/nfs/tslib/include -L/mnt/nfs/tslib/lib
其實這裏就是用到tslib的庫及頭文件。
下面說一下,tslib的原理。
首先來看下tslib的源碼結構
.
|-- AUTHORS
|-- COPYING
|-- ChangeLog
|-- INSTALL
|-- Makefile
|-- Makefile.am
|-- Makefile.in
|-- NEWS
|-- README
|-- aclocal.m4
|-- autogen-clean.sh
|-- autogen.sh
|-- autom4te.cache
| |-- output.0
| |-- output.1
| |-- requests
| |-- traces.0
| `-- traces.1
|-- config.guess
|-- config.h
|-- config.h.in
|-- config.h.in~
|-- config.log
|-- config.status
|-- config.sub
|-- configure
|-- configure.in
|-- cscope.files
|-- cscope.out
|-- depcomp
|-- etc
| |-- Makefile
| |-- Makefile.am
| |-- Makefile.in
| `-- ts.conf
|-- install-sh
|-- libtool
|-- ltmain.sh
|-- missing
|-- plugins
| |-- Makefile
| |-- Makefile.am
| |-- Makefile.in
| |-- dejitter.c
| |-- linear.c
| |-- mousebuts.c
| `-- variance.c
|-- src
| |-- Makefile
| |-- Makefile.am
| |-- Makefile.in
| |-- cscope.files
| |-- cscope.out
| |-- ts_attach.c
| |-- ts_close.c
| |-- ts_config.c
| |-- ts_error.c
| |-- ts_fd.c
| |-- ts_load_module.c
| |-- ts_open.c
| |-- ts_parse_vars.c
| |-- ts_read.c
| |-- ts_read_raw.c
| |-- tslib-filter.h
| |-- tslib-private.h
| `-- tslib.h
|-- stamp-h1
|-- tags
`-- tests
|-- Makefile
|-- Makefile.am
|-- Makefile.in
|-- fbutils.c
|-- fbutils.h
|-- font.h
|-- font_8x16.c
|-- font_8x8.c
|-- ts_calibrate.c
|-- ts_print.c
|-- ts_print_raw.c
`-- ts_test.c
上面的src是核心,plugin是處理規則的實現,etc中是默認配置文件。
我們一個tslib的具體實例來說明tslib的過程。
比如我在Minigui1.6實現的tslib輸入模塊。
BOOL Init2410Input (INPUT* input, const char* mdev, const char* mtype)
{
/* mdev should be /dev/ts */
printf("in tslib engineer");
tsd = ts_open(mdev, 0);
if (!tsd) {
perror("ts_open");
exit(1);
}
if (ts_config(tsd)) {
perror("ts_config");
exit(1);
}
input->update_mouse = mouse_update;
input->get_mouse_xy = mouse_getxy;
input->set_mouse_xy = NULL;
input->get_mouse_button = mouse_getbutton;
input->set_mouse_range = NULL;
//-----------------------------------
input->update_keyboard = NULL;
input->get_keyboard_state = NULL;
input->set_leds = NULL;
//-----------------------------------
input->wait_event = wait_event;
mousex = 0;
mousey = 0;
ts_event.x = ts_event.y = ts_event.pressure = 0;
return TRUE;
}
在初始化函數裏打開設備這個是ts_open
struct tsdev *ts_open(const char *name, int nonblock)
{
struct tsdev *ts;
int flags = O_RDONLY;
if (nonblock)
flags |= O_NONBLOCK;
ts = malloc(sizeof(struct tsdev));
if (ts) {
#ifdef USE_INPUT_API
int version;
long bit;
#endif /* USE_INPUT_API */
memset(ts, 0, sizeof(struct tsdev));
ts->fd = open(name, flags);
if (ts->fd == -1)
goto free;
#ifdef USE_INPUT_API
/* make sure we're dealing with a touchscreen device */
if (ioctl(ts->fd, EVIOCGVERSION, &version) < 0 ||
version != EV_VERSION ||
ioctl(ts->fd, EVIOCGBIT(0, sizeof(bit)*8), &bit) < 0 ||
!(bit & (1 << EV_ABS)) ||
ioctl(ts->fd, EVIOCGBIT(EV_ABS, sizeof(bit)*8), &bit) < 0 ||
!(bit & (1 << ABS_X)) ||
!(bit & (1 << ABS_Y)) ||
!(bit & (1 << ABS_PRESSURE)))
goto close;
#endif /* USE_INPUT_API */
__ts_attach(ts, &__ts_raw);
}
return ts;
#ifdef USE_INPUT_API
close:
close(ts->fd);
#endif /* USE_INPUT_API */
free:
free(ts);
return NULL;
}
上面的就是直接使用一般open打開設備文件。大家請注意上面調用的__ts_attach這個函數。這個函數非常重要。
int __ts_attach(struct tsdev *ts, struct tslib_module_info *info)
{
info->dev = ts;
info->next = ts->list;
ts->list = info;
return 0;
}
它其實就是將輸入設備的處理規則掛在設備的list鏈表上,然後當圖形系統調用ts_read時,就按順序執行了數據處理函數。上面我們可以看到添加了raw規則,就是讀取raw數據,等下會詳細講解規則的執行流程。
上面的ts_config函數就是添加其他規則的函數。
int ts_config(struct tsdev *ts)
{
char buf[80], *p;
FILE *f;
int line = 0, ret = 0;
char *conffile;
if( (conffile = getenv("TSLIB_CONFFILE")) != NULL) {
f = fopen(conffile,"r");
} else {
f = fopen(TS_CONF, "r");//TS_CONF是默認值/etc/ts.conf
}
if (!f)
return -1;
while ((p = fgets(buf, sizeof(buf), f)) != NULL && ret == 0) {
struct opt *opt;
char *e, *tok;
line++;
/*
* Did we read a whole line?
*/
e = strchr(p, '\n');
if (!e) {
ts_error("%d: line too long", line);
break;
}
/*
* Chomp.
*/
*e = '\0';
tok = strsep(&p, " \t");
/*
* Ignore comments or blank lines.
*/
if (!tok || *tok == '#')
continue;
/*
* Search for the option.
*/
for (opt = options; opt < options + NR_OPTS; opt++)
if (strcasecmp(tok, opt->str) == 0) {
ret = opt->fn(ts, p);
break;
}
if (opt == options + NR_OPTS) {
ts_error("%d: option `%s' not recognised", line, tok);
ret = -1;
}
}
fclose(f);
return ret;
}
ts_config首先讀取配置文件,比如前面的ts.conf文件。
#module mousebuts
module variance xlimit=50 ylimit=50 pthreshold=3
module dejitter xdelta=1 ydelta=1 pthreshold=3
module linear
然後解釋,比如檢測到有module關鍵詞,就會執行ts_load_modules
static struct opt options[] = {
{ "module", ts_opt_module },
};
static int ts_opt_module(struct tsdev *ts, char *rest)
{
char *tok = strsep(&rest, " \t");
return ts_load_module(ts, tok, rest);
}
int ts_load_module(struct tsdev *ts, const char *module, const char *params)
{
struct tslib_module_info * (*init)(struct tsdev *, const char *);
struct tslib_module_info *info;
char fn[1024];
void *handle;
int ret;
char *plugin_directory=NULL;
if( (plugin_directory = getenv("TSLIB_PLUGINDIR")) != NULL ) {
//fn = alloca(sizeof(plugin_directory) + strlen(module) + 4);
strcpy(fn,plugin_directory);
} else {
//fn = alloca(sizeof(PLUGIN_DIR) + strlen(module) + 4);
strcpy(fn, PLUGIN_DIR);//默認TSLIB_HOME/share/plugin
}
strcat(fn, "/");
strcat(fn, module);
strcat(fn, ".so");
handle = dlopen(fn, RTLD_NOW);
if (!handle)
return -1;
init = dlsym(handle, "mod_init");
if (!init) {
dlclose(handle);
return -1;
}
info = init(ts, params);
if (!info) {
dlclose(handle);
return -1;
}
info->handle = handle;
ret = __ts_attach(ts, info);
if (ret) {
info->ops->fini(info);
dlclose(handle);
}
return ret;
}
ts_load_module就是根據ts.conf的內容加在具體模塊,比如上面的具體的處理規則linear規則,這個就是大名鼎鼎的觸摸屏校正處理插件,這是就會加載liner模塊,並掛在到ts的list鏈表上,同時初始化這個插件模塊。
struct tslib_module_info *mod_init(struct tsdev *dev, const char *params)
{
struct tslib_linear *lin;
struct stat sbuf;
int pcal_fd;
int a[7];
char pcalbuf[200];
int index;
char *tokptr;
char *calfile=NULL;
char *defaultcalfile = "/etc/pointercal";
lin = malloc(sizeof(struct tslib_linear));
if (lin == NULL)
return NULL;
lin->module.ops = &linear_ops;
// Use default values that leave ts numbers unchanged after transform
lin->a[0] = 1;
lin->a[1] = 0;
lin->a[2] = 0;
lin->a[3] = 0;
lin->a[4] = 1;
lin->a[5] = 0;
lin->a[6] = 1;
lin->p_offset = 0;
lin->p_mult = 1;
lin->p_div = 1;
lin->swap_xy = 0;
/*
* Check calibration file
*/
if( (calfile = getenv("TSLIB_CALIBFILE")) == NULL) calfile = defaultcalfile;
if(stat(calfile,&sbuf)==0) {
pcal_fd = open(calfile,O_RDONLY);
read(pcal_fd,pcalbuf,200);
lin->a[0] = atoi(strtok(pcalbuf," "));
index=1;
while(index<7) {
tokptr = strtok(NULL," ");
if(*tokptr!='\0') {
lin->a[index] = atoi(tokptr);
index++;
}
}
#ifdef DEBUG
printf("Linear calibration constants: ");
for(index=0;index<7;index++) printf("%d ",lin->a[index]);
printf("\n");
#endif /*DEBUG*/
close(pcal_fd);
}
/*
* Parse the parameters.
*/
if (tslib_parse_vars(&lin->module, linear_vars, NR_VARS, params)) {
free(lin);
return NULL;
}
return &lin->module;
}
大家對這個/etc/pointercal不陌生吧,這個就是touchscreen校正時生成的文件。
到此tslib配置完成。
當我們要讀取鼠標值時,就要執行ts_read,
int ts_read(struct tsdev *ts, struct ts_sample *samp, int nr)
{
int result;
// int i;
// result = ts->list->ops->read(ts->list, ts_read_private_samples, nr);
result = ts->list->ops->read(ts->list, samp, nr);
// for(i=0;i<nr;i++) {
// samp[i] = ts_read_private_samples[i];
// }
#ifdef DEBUG
fprintf(stderr,"TS_READ----> x = %d, y = %d, pressure = %d\n", samp->x, samp->y, samp->pressure);
#endif
return result;
}
我們可以看出,ts_read就是調用ts的list上的read函數。
比如我們前面添加Liner處理規則就會添加liner的處理函數到list上,這時就執行linear_read函數。
static int
linear_read(struct tslib_module_info *info, struct ts_sample *samp, int nr)
{
struct tslib_linear *lin = (struct tslib_linear *)info;
int ret;
int xtemp,ytemp;
ret = info->next->ops->read(info->next, samp, nr);
if (ret >= 0) {
int nr;
for (nr = 0; nr < ret; nr++, samp++) {
#ifdef DEBUG
fprintf(stderr,"BEFORE CALIB--------------------> %d %d %d\n",samp->x, samp->y, samp->pressure);
#endif /*DEBUG*/
xtemp = samp->x; ytemp = samp->y;
samp->x = ( lin->a[2] +
lin->a[0]*xtemp +
lin->a[1]*ytemp ) / lin->a[6];
samp->y = ( lin->a[5] +
lin->a[3]*xtemp +
lin->a[4]*ytemp ) / lin->a[6];
samp->pressure = ((samp->pressure + lin->p_offset)
* lin->p_mult) / lin->p_div;
if (lin->swap_xy) {
int tmp = samp->x;
samp->x = samp->y;
samp->y = tmp;
}
}
}
return ret;
}
大家請注意 info->next->ops->read,這個就相當於調用list的下一個處理函數。其實在這裏它就會調用ts_raw(這個是ts_open添加的)規則的處理函數,因爲ts_raw是最先添加的,所以它是最先執行的,其實也必須這樣,因爲ts_raw就是讀取raw數據,肯定要先執行,要不後面的規則何來數據執行啊。
這個就是執行ts_read_raw,這個用來讀取raw數據。
然後一級一級讓後面的處理規則處理數據,比如liner就是使用校正程序生成的數據處理源數據然後返回給圖形系統,達到校正目的了。
到此將完了。
其實上面可能大家看到#ifdef USE_INPUT_API宏,其實這個是用來告訴tslib這個輸入設備是event設備,還是其他設備,因爲他們的數據結構不一樣。
還有生成的plugin默認在tslib/share/plugin下
event是Linux設備驅動的輸入設備統一數據結構,比如當一個H360格式的usb鼠標接上時,它會生成兩個設備,一個是H360設備驅動生成的,一個是input設備子系統生成的,其實他們就是同一個設備。比如我們常看見的event0,event1等就是input設備子系統生成的,他們肯定對應一個鼠標或鍵盤設備(mice,ts,keyboard)。
下面說下編譯:
./autogen.sh
./configure CC=arm-linux-gcc --build=i686-pc-linux --target=arm-linux --host=arm-linux --prefix=/mnt/nfs/tslib --enable-inputapi=no
make
make install
在交叉編譯TSLIB的時候出現了libtool:link: only absolute run-paths are allowed錯誤
解決方法:要修改/tslib/plugins/Makefile裏面找rpath,找到將其註釋並加上絕對路徑。
找到:LDFLAGS :=$(LDFLAGS) -rpath $(PLUGIN_DIR)
修改爲:
LDFLAGS :=$(LDFLAGS) -rpath `cd $(PLUGIN_DIR) && pwd`