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
|-- NEWS
|-- README
|-- aclocal.m4
|-- autogen-clean.sh
|-- autogen.sh
|-- autom4te.cache
|-- depcomp
|-- etc
|-- install-sh
|-- libtool
|-- ltmain.sh
|-- missing
|-- plugins
|-- src
|-- stamp-h1
|-- tags
`-- tests


 


  上面的 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`



 
 原文地址  http://blog.chinaunix.net/u2/89957/showart_2018020.html 

立你斯 發表在 痞客邦 留言(0) 人氣()