Hi,
I work with previously submitted DMC TSC-10 driver and rewrite its main
code blocks. Final code have thus improvements:
* Calibration feature. This code have two different calibration
algorithm.
* For calibration and data access, procfs interface added.
* X11 driver provided.
* Tested with 2.6.6 kernel.
But, this driver is very premature and need more work. Such as SMP
compability, two (or more) device support etc. don't work currently.
If this code is acceptable, may add SMP, multiple device support, auto
calibration etc. features. If you are send your things, i'm can be very
happy. See attachments (or expanded inline text) for calibration, usage
details etc.
After your oppinions, i make a diff file for latest kernel tree.
Code:
/*
* Driver for the DMC TSC-10 touchscreen interface.
*
* This module reports events via the 'input' system, on
/dev/input/mouse?, and install a procfs control/data interface..
*
* Developer by Serdar KOYLU, based from Marius Vollmer's work...
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#define TSC10_RATE_30 0x40
#define TSC10_RATE_50 0x41
#define TSC10_RATE_80 0x42
#define TSC10_RATE_100 0x43
#define TSC10_RATE_130 0x44
#define TSC10_RATE_150 0x45
#define TSC10_RATE_POINT 0x50
/*
* Version Information
*/
#define DRIVER_VERSION "0.0.1"
#define DRIVER_AUTHOR "Serdar KOYLU <[EMAIL PROTECTED]>"
#define DRIVER_DESC "DMC TSC-10 USB touchscreen driver"
#define _DECLARE_INTPRM(prm, desc) \
static int prm=0;\
MODULE_PARM(prm, "i")\
MODULE_PARM_DESC(prm, desc);
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
static int debug = 0;
MODULE_PARM(debug, "i")
MODULE_PARM_DESC(debug, "Debug Mode (0)");
static int fuzz = 4;
MODULE_PARM(fuzz, "i")
MODULE_PARM_DESC(fuzz, "Fuzzy Area Size (4)");
static int inv = 0;
MODULE_PARM(inv, "i")
MODULE_PARM_DESC(inv, "Invert X-Y Values (False = 0)");
static int invx = 0;
MODULE_PARM(invx, "i")
MODULE_PARM_DESC(invx, "Invert X Value (False = 0)");
static int invy = 0;
MODULE_PARM(invy, "i")
MODULE_PARM_DESC(invy, "Invert Y Value (False = 0)");
_DECLARE_INTPRM(p0x, "Point 0 X Value, for calibration (0)")
_DECLARE_INTPRM(p0y, "Point 0 Y Value, for calibration (0)")
_DECLARE_INTPRM(p1x, "Point 1 X Value, for calibration (0)")
_DECLARE_INTPRM(p1y, "Point 1 Y Value, for calibration (0)")
_DECLARE_INTPRM(p2x, "Point 2 X Value, for calibration (0)")
_DECLARE_INTPRM(p2y, "Point 2 Y Value, for calibration (0)")
_DECLARE_INTPRM(p3x, "Point 3 X Value, for calibration (0)")
_DECLARE_INTPRM(p3y, "Point 3 Y Value, for calibration (0)")
_DECLARE_INTPRM(sp0x, "Sample Point 0 X Value, for calibration (0)")
_DECLARE_INTPRM(sp0y, "Sample Point 0 Y Value, for calibration (0)")
_DECLARE_INTPRM(sp1x, "Sample Point 1 X Value, for calibration (0)")
_DECLARE_INTPRM(sp1y, "Sample Point 1 Y Value, for calibration (0)")
_DECLARE_INTPRM(sp2x, "Sample Point 2 X Value, for calibration (0)")
_DECLARE_INTPRM(sp2y, "Sample Point 2 Y Value, for calibration (0)")
_DECLARE_INTPRM(sp3x, "Sample Point 3 X Value, for calibration (0)")
_DECLARE_INTPRM(sp3y, "Sample Point 3 Y Value, for calibration (0)")
_DECLARE_INTPRM(calmode, "Calibration Model.\n1=Scalar. Set p0, p1, p2
and sp0, sp1, sp2 (pnx=spnx, pny=spny)\n2=Lineer Set p0 as UpperLeft
corner raw values and p3 as DownRight corner raw values\n0=None.")
static int in_calibrate = 0;
static struct proc_dir_entry *proc_dir, *proc_file;
static char file_write_buf[128];
static int _curr_x, _curr_y, _curr_x_raw, _curr_y_raw, _curr_button;
static int c_mode, minx, maxx, miny, maxy, midx, midy, ratex, ratey,
send_cdata;
#define USB_VENDOR_ID_DMC 0x0afa
#define TSC10_PACKET_LEN 5
struct tsc10 {
signed char data[TSC10_PACKET_LEN];
struct input_dev dev;
struct usb_device *usbdev;
struct urb *irq;
int open;
};
#define OK 0
#define NOT_OK 1
typedef int INT32;
typedef struct _point {
int x, y;
} point ;
typedef struct _matrix {
int An;
int Bn;
int Cn;
int Dn;
int En;
int Fn;
int Div;
} matrix ;
static void tsc10_control (struct tsc10 *tsc10,
__u8 requesttype, __u8 request, __u16 value,
__u16 retlen)
{
struct usb_device *dev = tsc10->usbdev;
int ret;
unsigned char buf[2];
if (retlen > 2) {
printk (KERN_ERR
"tsc10_control: retlen > 2 is not supported.");
retlen = 2;
}
buf[0] = buf[1] = 0xFF;
ret = usb_control_msg (dev, usb_rcvctrlpipe (dev, 0),
request, requesttype, value, 0,
buf, retlen, 2*HZ);
//printk(KERN_INFO "DMC SEQ RET: %02x %02x \n", buf[0], buf[1]);
}
static matrix calMatris;
static point calPts[6];
static point samplePts[6];
int setCalibrationMatrix(point * sample, point * real, matrix * matrix)
{
matrix->Div = ((real[0].x - real[2].x) *
(real[1].y - real[2].y)) -
((real[1].x - real[2].x) *
(real[0].y - real[2].y));
if( matrix->Div == 0 )
return 1;
matrix->An = ((sample[0].x - sample[2].x) * (real[1].y - real[2].y))
-
((sample[1].x - sample[2].x) * (real[0].y -
real[2].y));
matrix->Bn = ((real[0].x - real[2].x) * (sample[1].x - sample[2].x))
-
((sample[0].x - sample[2].x) * (real[1].x -
real[2].x));
matrix->Cn = (real[2].x * sample[1].x - real[1].x * sample[2].x) *
real[0].y +
(real[0].x * sample[2].x - real[2].x * sample[0].x)
* real[1].y +
(real[1].x * sample[0].x - real[0].x * sample[1].x)
* real[2].y;
matrix->Dn = ((sample[0].y - sample[2].y) * (real[1].y - real[2].y))
-
((sample[1].y - sample[2].y) * (real[0].y -
real[2].y));
matrix->En = ((real[0].x - real[2].x) * (sample[1].y - sample[2].y))
-
((sample[0].y - sample[2].y) * (real[1].x -
real[2].x));
matrix->Fn = (real[2].x * sample[1].y - real[1].x * sample[2].y) *
real[0].y +
(real[0].x * sample[2].y - real[2].x * sample[0].y)
* real[1].y +
(real[1].x * sample[0].y - real[0].x * sample[1].y)
* real[2].y;
return 0;
}
int getDisplayPoint(int *x, int *y, int raw_x, int raw_y, matrix *
matrix)
{
if( matrix->Div == 0 )
return 1;
*x = ((matrix->An * raw_x) + (matrix->Bn * raw_y) + matrix->Cn) /
matrix->Div;
*y = ((matrix->Dn * raw_x) + (matrix->En * raw_y) + matrix->Fn) /
matrix->Div;
return 0;
}
static void tsc10_reset (struct tsc10 *tsc10)
{
tsc10_control (tsc10, 0xC0, 0x55, 0, 2);
}
static void tsc10_set_rate (struct tsc10 *tsc10, int rate)
{
tsc10_control (tsc10, 0xC0, 0x05, rate, 2);
}
static void tsc10_start_1 (struct tsc10 *tsc10)
{
tsc10_control (tsc10, 0x40, 0x21, 0, 0);
}
struct usb_device_id tsc10_ids[] = {
{ USB_DEVICE (USB_VENDOR_ID_DMC, 0x03e8) },
{ }
};
MODULE_DEVICE_TABLE (usb, tsc10_ids);
static void tsc10_irq (struct urb *urb, struct pt_regs *regs)
{
struct tsc10 *tsc10 = urb->context;
unsigned char *data = tsc10->data;
struct input_dev *dev = &tsc10->dev;
int rep_x, rep_y, button;
if (urb->status)
return;
button = data[0];
rep_x = data[1]+256*data[2];
rep_y = data[3]+256*data[4];
if (inv || invx)
rep_x = 1023 - rep_x;
if (inv || invy)
rep_y = 1023 - rep_y;
_curr_x_raw = rep_x;
_curr_y_raw = rep_y;
if (in_calibrate != 0) {
/* On Calibration state, we only report Button Events.. */
if (_curr_button == 17 && button == 16) {
samplePts[in_calibrate-1].x = rep_x;
samplePts[in_calibrate-1].y = rep_y;
if (debug)
printk(KERN_INFO "Set Sample Point: %d on X:%d/Y:%d\n",
in_calibrate, rep_x, rep_y);
_curr_button = data[0];
if (debug)
printk(KERN_INFO "TSC10: Calibration mode ButtonPress\n");
input_report_key (dev, BTN_TOUCH, (data[0] & 0x01) != 0);
input_sync (dev);
} else {
if (_curr_button == 16 && button == 17) {
_curr_button = data[0];
input_report_key (dev, BTN_TOUCH,(data[0] & 0x01) != 0);
input_sync (dev);
if (debug)
printk(KERN_INFO "TSC10: Calibration mode ButtonRelease\n");
}
}
} else {
switch (c_mode) {
case 0:
break;
case 1:
getDisplayPoint(&rep_x, &rep_y, rep_x, rep_y, &calMatris);
break;
case 2:
/* Translate for min/max values */
rep_x = (rep_x - minx);
rep_y = (rep_y - miny);
if (rep_x <= 0) rep_x = 1;
if (rep_y <= 0) rep_y = 1;
rep_x = (ratex * rep_x) / 1000;
rep_y = (ratey * rep_y) / 1000;
if (rep_x > 1024) rep_x = 1024;
if (rep_x < 0) rep_x = 0;
if (rep_y > 1024) rep_y = 1024;
if (rep_y < 0) rep_y = 0;
if (debug && ((data[0] & 0x01) != 0))
printk(KERN_INFO "TSC-10: Button = %d Translate min/max: X: %d
-> %d
Y: %d -> %d\n", data[0],
_curr_x_raw, rep_x,
_curr_y_raw, rep_y);
break;
}
input_regs (dev, regs);
input_report_abs (dev, ABS_X, rep_x);
input_report_abs (dev, ABS_Y, rep_y);
input_report_key (dev, BTN_TOUCH, (data[0] & 0x01) != 0);
input_sync (dev);
_curr_button = data[0];
_curr_x = rep_x;
_curr_y = rep_y;
}
usb_submit_urb (urb, GFP_ATOMIC);
}
static int tsc10_open (struct input_dev *dev)
{
struct tsc10 *tsc10 = dev->private;
if (tsc10->open++)
return 0;
tsc10->irq->dev = tsc10->usbdev;
if (usb_submit_urb (tsc10->irq, GFP_ATOMIC))
return -EIO;
return 0;
}
static void tsc10_close (struct input_dev *dev)
{
struct tsc10 *tsc10 = dev->private;
if (!--tsc10->open)
usb_unlink_urb (tsc10->irq);
}
static int tsc10_probe (struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev (intf);
struct usb_endpoint_descriptor *endpoint;
struct tsc10 *tsc10;
tsc10 = kmalloc (sizeof(struct tsc10), GFP_KERNEL);
if (tsc10 == NULL)
return -ENOMEM;
memset (tsc10, 0, sizeof(struct tsc10));
tsc10->irq = usb_alloc_urb (0, GFP_KERNEL);
if (!tsc10->irq) {
kfree (tsc10);
return -ENOMEM;
}
tsc10->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
tsc10->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y);
tsc10->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
tsc10->dev.absmax[ABS_X] = 1024;
tsc10->dev.absmax[ABS_Y] = 1024;
tsc10->dev.absmin[ABS_X] = 0;
tsc10->dev.absmin[ABS_Y] = 0;
tsc10->dev.absfuzz[ABS_X] = fuzz;
tsc10->dev.absfuzz[ABS_Y] = fuzz;
tsc10->dev.private = tsc10;
tsc10->dev.open = tsc10_open;
tsc10->dev.close = tsc10_close;
tsc10->dev.name = "tsc-10";
tsc10->dev.id.bustype = BUS_USB;
tsc10->dev.id.vendor = dev->descriptor.idVendor;
tsc10->dev.id.product = dev->descriptor.idProduct;
tsc10->dev.id.version = dev->descriptor.bcdDevice;
tsc10->usbdev = dev;
endpoint = &intf->altsetting[0].endpoint[0].desc;
usb_fill_int_urb (tsc10->irq, dev,
usb_rcvintpipe (dev,
endpoint->bEndpointAddress),
tsc10->data, TSC10_PACKET_LEN, tsc10_irq,
tsc10,
endpoint->bInterval);
input_register_device (&tsc10->dev);
{
char path[64];
usb_make_path (dev, path, 64);
printk(KERN_INFO "input: %s on %s\n", tsc10->dev.name,
path);
}
tsc10_reset (tsc10);
tsc10_set_rate (tsc10, TSC10_RATE_30);
tsc10_start_1 (tsc10);
usb_set_intfdata (intf, tsc10);
return 0;
}
static void tsc10_disconnect (struct usb_interface *intf)
{
struct tsc10 *tsc10 = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL);
if (intf) {
usb_unlink_urb (tsc10->irq);
input_unregister_device (&tsc10->dev);
usb_free_urb (tsc10->irq);
kfree (tsc10);
}
}
static struct usb_driver tsc10_driver = {
.owner = THIS_MODULE,
.name = "tsc-10",
.probe = tsc10_probe,
.disconnect = tsc10_disconnect,
.id_table = tsc10_ids
};
struct tsc10packet {
char touch;
char x_hi;
char x_lo;
char y_hi;
char y_lo;
char raw_x_hi;
char raw_x_lo;
char raw_y_hi;
char raw_y_lo;
char cal_xmin_hi;
char cal_xmin_lo;
char cal_xmax_hi;
char cal_xmax_lo;
char cal_ymin_hi;
char cal_ymin_lo;
char cal_ymax_hi;
char cal_ymax_lo;
};
int proc_read(char *page, char **start, off_t off, int count, int *eof,
void *data)
{
struct tsc10packet *pk;
pk = (struct tsc10packetpage *) page;
pk->touch = _curr_button;
if (in_calibrate) {
pk->touch += 32;
if (debug)
printk(KERN_INFO "TSC10: Send Calibrate Packet: %d\n", pk->touch);
}
if (send_cdata) {
pk->touch = 64;
send_cdata = 0;
if (debug)
printk(KERN_INFO "TSC10: Send Calibration Values: %d\n",
pk->touch);
pk->cal_xmin_hi = minx / 256;
pk->cal_xmin_lo = minx % 256;
pk->cal_xmax_hi = maxx / 256;
pk->cal_xmax_lo = maxx % 256;
pk->cal_ymin_hi = miny / 256;
pk->cal_ymin_lo = miny % 256;
pk->cal_ymax_hi = maxy / 256;
pk->cal_ymax_lo = maxy % 256;
}
pk->x_hi = _curr_x / 256;
pk->x_lo = _curr_x % 256;
pk->y_hi = _curr_y / 256;
pk->y_lo = _curr_y % 256;
pk->raw_x_hi = _curr_x_raw / 256;
pk->raw_x_lo = _curr_x_raw % 256;
pk->raw_y_hi = _curr_y_raw / 256;
pk->raw_y_lo = _curr_y_raw % 256;
int len = sizeof(struct tsc10packet);
return len;
}
int proc_write(struct file *file, const char *buffer, unsigned long
count, void *data)
{
char *buf = file_write_buf;
if (count > 127)
count = 127;
int x, y, i;
copy_from_user(buf, buffer, count);
switch (buf[0]) {
case 'c':
/* Calibration Mode 1
In This mode, driver translate coordinates with internal calibrator
You must set 3 sample points. This Points can be any point, but
15 - 15, 50 - 85, 85 - 50 (all values in percent of 1024)
coordinates
a good start point..
This mode usefull for correct unaligned/cross aligned touchpanels.
Provided sample xtsc10cal.kernel uses this mode..
Command Format:
Cnxxx yyy
n = Point Number
xxx, yyy = X and Y coordinates for this point.
*/
c_mode = 1;
in_calibrate = buf[1] - '0';
if (in_calibrate < 1 || in_calibrate > 3) {
in_calibrate = 0;
break;
}
for (i = 2; i < count; i++) {
if (buf[i] == ' ') {
x = simple_strtol(&buf[2], NULL, 10);
y = simple_strtol(&(buf[i+1]), NULL, 10);
calPts[in_calibrate-1].x = x;
calPts[in_calibrate-1].y = y;
printk(KERN_INFO "CalPoint %d x: %d y:%d \n", in_calibrate,
x,y);
break;
}
}
break;
case 'C':
/*
Calibration Mode 2
In This mode, driver uses scaled calibrating.
Calibration tool set 4 point with replace Upper-Left,
Upper-Right, Lower-Left and Lower-Right corners.
This information used with mouse emulation device.
Command Format:
Cnxxx yyy
n = Point Number
xxx, yyy = X and Y coordinates for this point.
*/
c_mode = 2;
printk(KERN_INFO "TSC-10: Enter UserSpace Calibration mode: %d\n",
c_mode);
in_calibrate = buf[1] - '0';
if (in_calibrate < 1 || in_calibrate > 5) {
in_calibrate = 0;
break;
}
for (i = 2; i < count; i++) {
if (buf[i] == ' ') {
x = simple_strtol(&buf[2], NULL, 10);
y = simple_strtol(&(buf[i+1]), NULL, 10);
calPts[in_calibrate-1].x = x;
calPts[in_calibrate-1].y = y;
printk(KERN_INFO "TSC-10: CalPoint (mode %d) %d x: %d y:%d \n",
c_mode, in_calibrate, x,y);
break;
}
}
break;
case '2':
/* Read Mode 2 Calibration data
After this command, driver send touch = 64 and fill
calibration values from internal max/min table.
This command used with X11 driver.
*/
if (c_mode == 2)
send_cdata = 1;
break;
case 'z':
/* Process Calibration values
With this command, driver calculate new calibration values and
exit calibration mode.
if calibration mode = 2 (Min/Max mode), driver send calibration
values with subsequent read call in proc file..
*/
printk(KERN_INFO "TSC10: Calibrate (%d) Point: %d Pos: X:%d->%d
Y:%d->%d\n", 0, c_mode, calPts[0].x, samplePts[0].x, calPts[0].y,
samplePts[0].y);
printk(KERN_INFO "TSC10: Calibrate Point: %d Pos: X:%d->%d
Y:%d->%d\n", 1, calPts[1].x, samplePts[1].x, calPts[1].y,
samplePts[1].y);
printk(KERN_INFO "TSC10: Calibrate Point: %d Pos: X:%d->%d
Y:%d->%d\n", 2, calPts[2].x, samplePts[2].x, calPts[2].y,
samplePts[2].y);
printk(KERN_INFO "TSC10: Calibrate Point: %d Pos: X:%d->%d
Y:%d->%d\n", 3, calPts[3].x, samplePts[3].x, calPts[3].y,
samplePts[3].y);
printk(KERN_INFO "TSC10: Calibrate Point: %d Pos: X:%d->%d
Y:%d->%d\n", 4, calPts[4].x, samplePts[4].x, calPts[4].y,
samplePts[4].y);
if (c_mode == 1)
setCalibrationMatrix(&samplePts[0], &calPts[0], &calMatris);
else {
send_cdata = 1;
minx = samplePts[0].x;
miny = samplePts[0].y;
maxx = samplePts[3].x;
maxy = samplePts[3].y;
midx = (maxx - minx);
midy = (maxy - miny);
ratex = 1024000l / midx;
ratey = 1024000l / midy;
printk("TSC-10 Calibration: RateX = %d RateY = %d\n", ratex,
ratey);
}
in_calibrate = 0;
break;
}
return count;
}
static int __init tsc10_init (void)
{
int result;
result = usb_register (&tsc10_driver);
if (result == 0)
info (DRIVER_VERSION " : " DRIVER_DESC);
proc_dir = proc_mkdir("calibrate", NULL);
if (!proc_dir)
return -ENOMEM;
proc_file = create_proc_entry("tsc10usb", 0666, proc_dir);
if (!proc_file)
return -ENOMEM;
proc_file->read_proc = proc_read;
proc_file->write_proc = proc_write;
proc_file->owner = THIS_MODULE;
#define _SETSAMPLE(p) samplePts[p].x = sp##p##x;samplePts[p].y =
sp##p##y;
_SETSAMPLE(0)
_SETSAMPLE(1)
_SETSAMPLE(2)
_SETSAMPLE(3)
#define _SETPOINT(n) calPts[n].x = p##n##x;calPts[n].y = p##n##y;
_SETPOINT(0)
_SETPOINT(1)
_SETPOINT(2)
_SETPOINT(3)
minx = 0;
miny = 0;
maxx = 1024;
maxy = 1024;
midx = 1024;
midy = 1024;
ratex = 1000;
ratex = 1000;
if (calmode > -1 && calmode < 3) {
switch (calmode) {
case 0:
break;
case 1:
setCalibrationMatrix(&samplePts[0], &calPts[0], &calMatris);
break;
case 2:
send_cdata = 1;
minx = samplePts[0].x;
miny = samplePts[0].y;
maxx = samplePts[3].x;
maxy = samplePts[3].y;
midx = (maxx - minx);
midy = (maxy - miny);
ratex = 1024000l / midx;
ratey = 1024000l / midy;
printk("TSC-10 Calibration: RateX = %d RateY = %d\n", ratex,
ratey);
}
}
if (debug)
printk(KERN_INFO "TSC-10: Initialization completed..\n");
return result;
}
static void __exit tsc10_exit (void)
{
usb_deregister (&tsc10_driver);
remove_proc_entry("tsc10usb", proc_dir);
remove_proc_entry("calibrate", NULL);
}
module_init (tsc10_init);
module_exit (tsc10_exit);
Copyright (c) 2004 TUBITAK (The Scientific And Technical Research Council Of Turkey) UEKAE (National Research Institute of Electronics and Cryptology).
Written and maintained by: Serdar KOYLU <serdar at uludag.org.tr>
DMC TSC-10 USB TouchScreen Controller
Calibration Utilities and manual.
What is Calibration?
Calibration, in our scope, remapping reported touch points from TouchScreen Controller to right display/screen points. Main problems:
* In touchscreen, active area start don't make a 0,0 point. Resistive or capasitive panels always have a tolerance factor and physical start point not equal electrical start point.
* LCD/CRT Monitor sync phase shift. In your monitor, you move image to left, right, up, down with control panel buttons/pots.
* Monitor and Panel don't fit. Many panels is big than monitor active area. This reason, monitor corners always inside of TS panel. TS Coordinates always have an offset.
* Bad mechanical mounting. Panel corners must mount over screen corners. But tolerance and mechanical limits force shift in any direction to panel or screen. Result is: 0,0 Panel point is shift to 5,3 Screen point, 1024,768 point is shift to 1019, 765 point. Small (specially embedded) panels has relatively high resolution and solve this effect more difficult.
* Thermal and EMI changes effect touchscreen controller and panel.
Big panels easily mount with a LCD Screen. And low resolution provide low sensitivity. This reason, calibration off big panels is relatively easy. If you have a big panel and very precise calibration algorithms, your calibration can be difficult. Your sample points not accurate for right point. In generally, big panels uses more insensitive calibration methods. But this methods may can't solve small panel problems.
For this reasons, tsc-10 controller driver uses two calibration method. A Three point, angle sensitive "scalar" method and Two point non-angle sensitive offset shift "lineer" method.
For scaler method, you set three point of screen. This points can be positioned any coordinate. But, points for corresponds %15 / %15, %80 / %50 and %50 / %80 coordinates of screen is very useful. If you have a 800 x 600 screen, this points have (120,90), (640, 300) and (400, 480) coordinates. You scale this points to 1024 (eg. for X axis use (1024 / 800) * X formula) and report to driver with sequentially with defined in README.Kernel instructions. Kernel driver self read panel coordinates form controller and uses your provided values for mapping samples.
Scalar calibration cannot recommend for screen resolutions greater than 512 pixel.
For lineer method, you must set min and max values for screen. Use upperleft corner as min values and lowerright corner for max values. You must sure panel X/Y axis right position. For right position, upperleft corner always make low values, lowerright corner is highest values, if don't, you must use inv, invx or invy parameters. min values is send to kernel driver as point #0 and max values is send to kernel driver as point #3. Kernel driver self read coordinates from panel. For lineer mode, you set calibration points with not use any calibration tool. But, scalar mode, selecting right point very difficult with don't use a calibration tool.
All modes, kernel driver only set point with untouch event. For easy calibration, you touch near of calibration point and fine tune your hand and untouch panel.
For manual lineer calibration you must send "C10 0 " to command channel and press upperleft corner and untouch. Next, send "C31023 1023 " to command channel and press lowerright corner and untouch. Finally you send "z" to command channel. Manual calibration is complete. If you are look dmesg output, you show your calibration values.
TSC10: Calibrate (0) Point: 2 Pos: X:0->52 Y:0->97
TSC10: Calibrate Point: 1 Pos: X:0->0 Y:0->0
TSC10: Calibrate Point: 2 Pos: X:0->0 Y:0->0
TSC10: Calibrate Point: 3 Pos: X:0->940 Y:0->921
TSC10: Calibrate Point: 4 Pos: X:0->0 Y:0->0
TSC-10 Calibration: RateX = 1153 RateY = 1242
For this result, your min and max values readed in (X,Y) format as MIN=(52,97) MAX=(940,921)
For next kernel module loading, you can use this parameters for initial calibration:
modprobe tsc10 p0x=52 p0y=97 p3x=940 p3y=921 calmode=2
Or you must read raw data from X11 Driver (Set option "UseRaw=1" and "ReportingMode=scaled") and set MinX/MinY, MaxX/MaxY values in X Server conf file.
We provide two tool for X11 based graphical calibration tool.
xtsc10cal.lineer - Use for lineer mode calibration.
xtsc10cal.scalar - Use for scalar mode calibration.
This tools runned as fullscreen. First, you are see a small (5x5 pixel) white box. Touch and untouch this box. Box erased and redrawed with next location. Retry touch and untouch. You repeat this actions to end of application. After, your display calibrated. You can see calibration values on dmesg output.
For best results, in lineer mode calibration tool, you use outbound corner of small white box. For scalar mode calibration, you use upperleft corner of small white box. But, if you use fingers for calibration you are in wrong way. Use a silicon or plastic aparat (such as handheld pencil).
If your linux can't run this utilities, you can use "make" for recompile. But you must have gcc and X11/Xlib devel packets.
Add this utilities (only one) to xsession can be really good idea. Every start of X11, your screen calibrated. This method specially recommended.
Copyright (c) 2004 TUBITAK (The Scientific And Technical Research Council Of Turkey) UEKAE (National Research Institute of Electronics and Cryptology).
Written and maintained by: Serdar KOYLU <serdar at uludag.org.tr>
DMC TSC-10 USB TouchScreen Controller
Linux Kernel Driver Installation and User manual.
What is ?
TSC-10 USB TouchScreen Controller kernel interface. Provides a standart mouse interface and special control/data channel on procfs file calbirate/tsc10usb.
Installation or compile.
This packet contain a percompiled binary for 2.6.6 vanilla kernel. If your kernel match this, you use it directly. But, your linux distro use an other kernel (generally true condition) you must compile first your own module. If you have vanilla 2.6.6 kernel, you can skip compile instructions.
For compile this module, you must install your kernel sources. After installing sources, cd module directory (path/to/tsc10pkg/kernel) and must run make. Yes, compilation can be easy.
After compilation, you must copy and register (?!) this module.
# ./install-module
All that.
Loading.
Most distribution uses /etc/modules file for loading modules on boot process. If your distribution is use this way, you may use this command:
# echo tsc10 >>/etc/modules
If you want use manual loading, use
# modprobe tsc10
But, TSC-10 driver can be use many parameters:
debug:
Debug Mode (Default = 0)
For debug mode, you use debug=1. In this mode, driver send all debug info to dmesg file and fill logs faster. Only use really required.
fuzz:
Fuzzy Area Size (Default = 4)
Sensitivity of mouse pointer (for legacy mouse emulation).
inv:
Invert X-Y Values (Default = False = 0)
invx:
Invert X Value (Default = False = 0)
invy:
Invert Y Value (Default = False = 0)
If your mouse pointer (legacy mouse and native mode (such as XInput Driver)) move other side rather than touched side, you must set this values to 1.
p0x, p1x, p2x, p3x:
Point X Values, for calibration (0)
See calibration model.
p0y, p1y, p2y, p3y:
Point Y Values, for calibration (0)
See calibration model.
sp0x, sp1x, sp2x, sp2x :
Sample Points X Values, for calibration (0)
See calibration model.
sp0y, sp0y, sp0y, sp0y :
Sample Points Y Values, for calibration (0)
See calibration model.
calmode:
Calibration Model.
Select used calibration model.
For legacy mouse emulation, driver provide two essential calibration method. This methods can be change runtime if required with command channel.
0 = Default. No calibration.
1 = Scalar Calibration. This mode uses three points for calibration. This points can be any location in screen. For selecting points etc., see README.Calibration file. For this mode, you must select 3 sample point on screen ( symboled as Sn(x, y) ) and provide real touch points ( symboled as Tn(x, y) ) for this samples. And declare this points to driver with parameters.
sp0x = S1(x) p0x = T1(x)
sp0y = S1(y) p0y = T1(y)
sp1x = S2(x) p1x = T2(x)
sp1y = S2(y) p1y = T2(y)
sp2x = S3(x) p2x = T3(x)
sp2y = S3(y) p2y = T3(y)
If you can't set or illegal set of these points, driver may (must) produce invalid and crazy results, but no hang, only points corrupted.
2 = Lineer Calibration. This mode uses two points for calibration. This method is simple. You must set min and max values for X and Y axis. But, you must use standart calibration points. Sample points not used. For details, see README.Calibration.
p0x = minx
p0y = miny
p3x = maxx
p3y = maxy
Many environments has stable thermal and EMI considerations, such as office, home etc. In this case, calibration values cannot change frequently and stable for many days or weeks. On this state, you might use module loading parameters for calibration, shortly.
Mouse Interface:
Legacy mouse interface can be usable for legacy mouse drivers, such as GPM. If you are using X11, you must use XInput driver for perfect results.
Control Channel:
TSC-10 Control channel located as a file on procfs directory. Usually this device file found at "/proc/calibrate/tsc10usb".
Control channel can be use for runtime calibrating. Currently only calibration changes provided.
Usage:
Open this file and send commands to file with write() calls. No, you don't need C experience. You can use "echo" shell commands perfectly.
Command set:
"cPXXX YYY" - Set Scalar mode calibration point. Driver enter calibration state.
P = Point Number: 1..3
XXX = X Coordinate for real point as scaled with 1024.
YYY = Y Coordinate for real point as scaled with 1024
Sample: Sample point #2: X = 50, Y=40 for 800x600 resolution. Once, scale values:
X = 1024 / 800 * 50 = 64
Y = 1024 / 600 * 40 = 68
Send command to control channel:
For C:
int fd = open("/proc/calibrate/tsc10usb", O_RDWR);
char *cmd;
int len;
cmd = malloc(128);
len = snprintf(cmd, 127, "c%d%d %d ", pointNumber, X, Y);
write(fd, cmd, len);
For Console/Bash:
echo "c264 68 ">/proc/calibrate/tsc10usb
"CPXXX YYY" - Set lineer mode calibration point. Driver enter calibration state.
P = Point Number: 1..4
XXX = X Coordinate for real point as scaled with 1024.
YYY = Y Coordinate for real point as scaled with 1024.
Note: Coordinates ignored silently, not required for this mode. Currently, only 1th and 4th Points effective.
Sample: Sample point #2: X = 50, Y=40 for 800x600 resolution. Once, scale values:
X = 1024 / 800 * 50 = 64
Y = 1024 / 600 * 40 = 68
Send command to control channel:
For C:
int fd = open("/proc/calibrate/tsc10usb", O_RDWR);
char *cmd;
int len;
cmd = malloc(128);
len = snprintf(cmd, 127, "c%d%d %d ", pointNumber, X, Y);
write(fd, cmd, len);
For Console/Bash:
echo "c264 68 ">/proc/calibrate/tsc10usb
"z" - End of calibration.
After sending all point values, you send a "z" and driver exit calibration mode with set internal values. This command force next "data_read" packet as a "Cal_data" packet. Your next read data channel is a cal_data packet.
"2" - Read Mode 2 Calibration data.
If you send a "2", this command force next "data_read" packet as a "Cal_data" packet. Your next read data channel is a cal_data packet.
Data Interface:
Data Interface as read side of TSC-10 Control channel and located a file on procfs directory. Usually this device file found at "/proc/calibrate/tsc10usb". Control Channel and Data Interface device files is same file. For reading this file, file act as a data interface.
Data Interface Packet Format:
For read calls, you must set a 17 Byte (octet) size buffer. This buffer always has this data format:
Byte 0 : Button and status information.
Byte 1 : X-Hi
Byte 2 : X-Lo
Byte 3 : Y-Hi
Byte 4 : Y-Lo
Byte 5 : RAW-X-Hi
Byte 6 : RAW-X-Lo
Byte 7 : RAW-Y-Hi
Byte 8 : RAW-Y-Lo
Byte 9 : CAL-X-Min-hi
Byte 10 : CAL-X-Min-Lo
Byte 11 : CAL-X-Max-hi
Byte 12 : CAL-X-Max-Lo
Byte 13 : CAL-Y-Min-hi
Byte 14 : CAL-Y-Min-Lo
Byte 15 : CAL-Y-Max-hi
Byte 16 : CAL-Y-Max-Lo
For calculate X or Y value, use " Hi * 256 + Lo " formula.
RAW* Values, show raw data as readed from controller.
CAL* Values, only valid for Status (Byte 0) is 64 and store Mode 2 (Lineer) Calibration data Min and Max Values.
Byte 1, Button and Status:
Bit 1 (LSB) : (0x01, 1) TouchStatus. If user hit touchscreen, this bit is "1".
Bit 5 : (0x10, 16) If "1" then TouchStatus Valid.
Bit 6 : (0x20, 32) Driver in calibration mode.
Bit 7 : (0x40, 64) CAL* Values valid. Packet is a "CAL_DATA" Packet.
If you receive a CAL_DATA Packet, don't process Touch and Coordinate value. Coordinates show last reported value from controller.
If Bit 6 is set, only touch information is valid. Point values stored with Touch as released.
Driver report all raw packets to legacy mouse interface. Normally this event occurs 30 times for second. But driver state is calibration mode (Bit 6 = 1) only touch and release events reported.
All other bits as reserved and must be unuse.
Calibration Method:
You must use "c*" or "Cn" packets for start calibration. If you don't use a calibration tool, press and release points two times. After set all points, you send a "z" to control channel and read data interface for calibration point settings.
Control/Data interface is a shareable file. A Process can be open for writing and other process can open for reading. But, CAL_DATA packets only send one time. This reason can be make a race condition.
See README.Calibration file for details.