• 转)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, ' ');
            if (!e) {
                ts_error("%d: line too long", line);
                break;
            }

            /*
             * Chomp.
             */
            *e = '';

            tok = strsep(&p, " ");

            /*
             * 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, " ");

        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!='') {
                    lin->a[index] = atoi(tokptr);
                    index++;
                }
            }
    #ifdef DEBUG
            printf("Linear calibration constants: ");
            for(index=0;index<7;index++) printf("%d ",lin->a[index]);
            printf(" ");
    #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 ", 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 ",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://sjj0412.cublog.cn/

     

    *******************************************************************************************************

    下面就Tslib的环境变量、配置文件等进行简要的分析

    1. 环境变量

    为了实现Tslib的正确运行,需要对如下的Tslib的环境变量进行配置:

    TSLIB_TSDEVICE //触摸屏设备文件名。

    Default (no inputapi): /dev/touchscreen/ucb1x00

    Default (inputapi): /dev/input/event0

    TSLIB_CALIBFILE //校准的数据文件,由ts_calibrate校准程序生成。

    Default: ${sysconfdir}/pointercal

    TSLIB_CONFFILE //配置文件名。

    Default: ${sysconfdir}/ts.conf

    TSLIB_PLUGINDIR //插件目录

    Default: ${datadir}/plugins

    TSLIB_CONSOLEDEVICE //控制台设备文件名

    Default: /dev/tty

    TSLIB_FBDEVICE //设备名

    Default: /dev/fb0

    以上环境变量在实际开发中的实际配置可以根据实际情况决定。

    2. 配置文件

    除了环境变量以外,Tslib的配置文件ts.conf同样是个十分重要的部分,在ts.conf中配置了需要加载的插件、插件加载顺序以及插件的一些约束参数,这些配置参数对触摸屏的触摸效果具有十分重要的影响。

    下面是Tslib 1.4中的ts.conf的参考配置:(tslib 13.3中采用其默认的配置文件即可完成一般的功能,本人在实践时没有对其做任何改变)

    # Uncomment if you wish to use the linux input layer event interface

    # module_raw input

    # Uncomment if you're using a Sharp Zaurus SL-5500/SL-5000d

    # module_raw collie

    # Uncomment if you're using a Sharp Zaurus SL-C700/C750/C760/C860

    # module_raw corgi

    # Uncomment if you're using a device with a UCB1200/1300/1400 TS interface

    # module_raw ucb1x00

    # Uncomment if you're using an HP iPaq h3600 or similar

    # module_raw h3600

    # Uncomment if you're using a Hitachi Webpad

    # module_raw mk712

    # Uncomment if you're using an IBM Arctic II

    # module_raw arctic2

    module pthres pmin=1

    module variance delta=30

    module dejitter delta=100

    module linear

    其中pthres 为Tslib提供的触摸屏灵敏度门槛插件;variance 为Tslib提供的触摸屏滤波算法插件;dejitter 为Tslib提供的触摸屏去噪算法插件;linear为Tslib提供的触摸屏坐标变换插件。

    在Qtopia中,就触摸屏的调试问题主要涉及到以下三个部分:触摸屏驱动、Tslib、QTslibTPanelHandlerPrivate封装。

    触摸屏驱动为与硬件直接接触部分,为上层的Tslib提供最原始的设备坐标数据,并可以配置采样间隔、屏幕灵敏度等。采样间隔决定了单位时间内的采 样数量,在其他参数不变的情况下,采样间隔越小意味着单位时间内的采样数量越多,也就意味着采样越逼真、越不容易出现采样信息丢失如输入法书写时丢笔划的 情况,但因为噪声的影响,采样间隔越小同时也意味着显示出来的图形的效果越差。

    Tslib为触摸屏驱动和应用层之间的适配层,其从驱动处获得原始的设备坐标数据,通过一系列的去噪、去抖、坐标变换等操作,来去除噪声并将原始的设备坐标转换为相应的屏幕坐标。

    在tslib中为应用层提供了2个主要的接口ts_read()和ts_read_raw(),其中ts_read()为正常情况下的借口,ts_read_raw()为校准情况下的接口。

    正常情况下,tslib对驱动采样到的设备坐标进行处理的一般过程如下:

    raw device --> variance --> dejitter --> linear --> application

    module module module

    校准情况下,tslib对驱动采样到的数据进行处理的一般过程如下:

    raw device--> Calibrate

    QTslibTPanelHandlerPrivate为tslib提供了应用层封装,为tslib与应用层的接口部分。

    在触摸屏调试过程中,涉及到的参数主要有采样间隔(驱动)、灵敏度(驱动)、去噪算法及约束(tslib)、去抖算法及约束(tslib)、ts门槛值(tslib)。

    由于各种相关期间的影响,在不同的硬件平台上,相关参数可能需要调整。以上参数的相互关系为:采样间隔越大,采样点越少,采样越失真,但因为信息量 少,容易出现丢笔划等丢失信息情况,但表现出来的图形效果将会越好;去噪算法跟采样间隔应密切互动,采样间隔越大,去噪约束应越小,反之采样间隔越小,去 噪约束应越大。去抖算法为相对独立的部分,去抖算法越复杂,带来的计算量将会变大,系统负载将会变重,但良好的去抖算法可以更好的去除抖动,在进行图形绘 制时将会得到更好的效果;灵敏度和ts门槛值为触摸屏的灵敏指标,一般不需要进行变动,参考参考值即可。

    *************************************************************

     

    出现这个错误
    Couldnt load module input
    No raw modules loaded
    tsconfig: Success

    第一行告诉tslib从linux的输入设备读取数据,需要用到input这个模块,也就是plugin目录下的input.so文件,
    所以你的TSLIB_PLUGINDIR一定要配置正确,让tslib能够找到模块文件。
    其他参数分别控制触摸点的连续下压、变化宽度、轨迹变化和线性校准。
    也是要把其对应的需要的几个库文件复制到/tslib/plugins中

    第二行导致"No raw modules loaded."的原因有两个:
    注意:一是你的ts.conf中没有指定module_raw,自带的ts.conf的所有module_raw都被注释掉了,
    # Uncomment if you wish to use the linux input layer event interface
    # module_raw input

    # Uncomment if you're using a Sharp Zaurus SL-5500/SL-5000d
    # module_raw collie

    # Uncomment if you're using a Sharp Zaurus SL-C700/C750/C760/C860
    # module_raw corgi

    # Uncomment if you're using a device with a UCB1200/1300/1400 TS interface
    # module_raw ucb1x00

    # Uncomment if you're using an HP iPaq h3600 or similar
    #module_raw h3600

    # Uncomment if you're using a Hitachi Webpad
    # module_raw mk712

    # Uncomment if you're using an IBM Arctic II
    # module_raw arctic2

    你应该打开至少一个module_raw选项,有人就选择了input,去掉了它的注释
    得到module_raw input

    另一个原因就是你没有正确配置TSLIB_PLUGINDIR,tslib从你指定的目录中没有找到plugin需要的模块文件,
    请检查你的路径和文件。

     
     
     
     
     
  • 相关阅读:
    AtCoder Beginner Contest 184 ABCDE 题解
    Codeforces Round #685 (Div. 2) C. String Equality 思维
    Codeforces Round #682 (Div. 2) ABC 题解
    Gym 102215 & 队内训练#5
    【题解】 CF436E Cardboard Box 带悔贪心
    【题解】 「NOI2019」序列 模拟费用流 LOJ3158
    【笔记】 exlucas
    【题解】 「HNOI2018」毒瘤 树形dp+动态dp+容斥+格雷码 LOJ2496
    【笔记】 异或高斯消元方案数
    【笔记】 用生成函数推二项式反演
  • 原文地址:https://www.cnblogs.com/lihaiping/p/tslib.html
Copyright © 2020-2023  润新知