#include "misc.h"
#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86Xinput.h"
#include "exevents.h"
#include "os.h"
#ifdef XFree86LOADER
#include "xf86Module.h"
#endif
#define DEFAULT_MAX_X 63104
#define DEFAULT_MIN_X 8786
#define DEFAULT_MAX_Y 61592
#define DEFAULT_MIN_Y 7608
#define XI_STYLUS "TOUCHSCREEH"
#define PMX_REPORT_SIZE 3
#define PMX_BUFFER_SIZE 256
#define PMX_PACKET_SIZE 5
#ifdef XFree86LOADER
static const char *reqSymbols[] = {
"AddEnabledDevice",
"ErrorF",
"InitButtonClassDeviceStruct",
"InitProximityClassDeviceStruct",
"InitValuatorAxisStruct",
"InitValuatorClassDeviceStruct",
"InitPtrFeedbackClassDeviceStruct",
"RemoveEnabledDevice",
"Xcalloc",
"Xfree",
"XisbBlockDuration",
"XisbFree",
"XisbNew",
"XisbRead",
"XisbTrace",
"screenInfo",
"xf86AddInputDriver",
"xf86AllocateInput",
"xf86CloseSerial",
"xf86CollectInputOptions",
"xf86ErrorFVerb",
"xf86FindOptionValue",
"xf86GetMotionEvents",
"xf86GetVerbosity",
"xf86MotionHistoryAllocate",
"xf86NameCmp",
"xf86OpenSerial",
"xf86OptionListCreate",
"xf86OptionListMerge",
"xf86OptionListReport",
"xf86PostButtonEvent",
"xf86PostMotionEvent",
"xf86PostProximityEvent",
"xf86ProcessCommonOptions",
"xf86ScaleAxis",
"xf86SetIntOption",
"xf86SetStrOption",
"xf86XInputSetScreen",
"xf86XInputSetSendCoreEvents",
NULL
};
#endif
typedef struct _PMXPrivateRec {
char *input_dev;
OsTimerPtr uptimer;
OsTimerPtr polltimer;
int min_x;
int max_x;
int min_y;
int max_y;
int cur_x;
int cur_y;
int button1;
int button2;
int button3;
int screen_no;
int screen_width;
int screen_height;
Bool inited;
char state;
int num_old_bytes;
LocalDevicePtr stylus;
int swap_axes;
int tap_button;
unsigned char rec_buf[PMX_BUFFER_SIZE];
} PMXPrivateRec, *PMXPrivatePtr;
static Bool xf86PmxConvert(LocalDevicePtr local, int first, int num,
int v0,int v1,int v2,int v3, int v4, int v5, int *x, int *y)
{
PMXPrivatePtr priv = (PMXPrivatePtr) local->private;
int width = priv->max_x - priv->min_x;
int height = priv->max_y - priv->min_y;
int input_x, input_y;
int tmp;
input_x = v0;
input_y = v1;
if (priv->swap_axes) {
tmp = input_x;
input_x = input_y;
input_y = tmp;
}
*x = (priv->screen_width * (input_x - priv->min_x)) / width;
*y = (priv->screen_height * (input_y - priv->min_y)) / height;
xf86XInputSetScreen(local, priv->screen_no, *x, *y);
return TRUE;
}
static int unpack(unsigned int v1, unsigned int v2)
{
return (v2 << 8) | (v1);
}
static CARD32 PalmaxUpTimeout(OsTimerPtr timer, CARD32 now, pointer arg)
{
PMXPrivatePtr priv = arg;
if(priv->state)
{
int sigstate = xf86BlockSIGIO ();
xf86PostButtonEvent(priv->stylus->dev, TRUE, 1, 0,
0, 2, priv->cur_x >> 4, priv->cur_y >> 4);
priv->state = 0;
xf86UnblockSIGIO (sigstate);
}
return 0;
}
static CARD32 PalmaxPollTimeout(OsTimerPtr timer, CARD32 now, pointer arg)
{
LocalDevicePtr local = (LocalDevicePtr) arg;
PMXPrivatePtr priv = (PMXPrivatePtr)(local->private);
int modembits;
int button1 = 0, button2 = 0;
int sigstate = xf86BlockSIGIO ();
modembits = xf86GetSerialModemState(local->fd);
if (modembits & XF86_M_CTS)
button1 = 1;
if (modembits & XF86_M_DSR)
button2 = 1;
if(button1 == 1 && button2 == 1 && priv->button1 == 0 && priv->button2 == 0)
{
if(!priv->button3)
{
xf86PostButtonEvent(priv->stylus->dev, TRUE, 3, 1,
0, 2, priv->cur_x >> 4, priv->cur_y >> 4);
priv->button3 = 1;
}
}
if(priv->button3 && (button1 == 0 || button2 == 0))
{
if(priv->button3 != 2)
{
xf86PostButtonEvent(priv->stylus->dev, TRUE, 3, 0,
0, 2, priv->cur_x >> 4, priv->cur_y >> 4);
priv->button3 = 2;
}
if(button1 || button2)
goto out;
priv->button3 = 0;
}
if(button1 != priv->button1)
{
xf86PostButtonEvent(priv->stylus->dev, TRUE, 1, button1,
0, 2, priv->cur_x >> 4, priv->cur_y >> 4);
priv->button1 = button1;
}
if(button2 != priv->button2)
{
xf86PostButtonEvent(priv->stylus->dev, TRUE, 2, button2,
0, 2, priv->cur_x >> 4, priv->cur_y >> 4);
priv->button2 = button2;
}
out:
xf86UnblockSIGIO (sigstate);
return 100;
}
static void xf86PmxReadInput(LocalDevicePtr local)
{
PMXPrivatePtr priv = (PMXPrivatePtr)(local->private);
int cur_x, cur_y;
int state;
int num_bytes;
int bytes_in_packet;
unsigned char *ptr, *start_ptr;
int report_size = 3;
num_bytes = xf86ReadSerial(local->fd, (char *) (priv->rec_buf + priv->num_old_bytes),
PMX_BUFFER_SIZE - priv->num_old_bytes);
if (num_bytes < 0) {
Error("System error while reading from Palmax touchscreen.");
return;
}
num_bytes += priv->num_old_bytes;
ptr = priv->rec_buf;
bytes_in_packet = 0;
start_ptr = ptr;
while (num_bytes >= report_size)
{
switch(bytes_in_packet)
{
case 0:
if(ptr[0] != 0xFF)
start_ptr++;
else
bytes_in_packet++;
break;
case 1:
if(ptr[0] == 0xFE)
report_size = 3;
else
report_size = 5;
bytes_in_packet++;
break;
default:
bytes_in_packet++;
break;
}
num_bytes--;
ptr++;
if (bytes_in_packet == report_size)
{
if(priv->uptimer)
{
TimerFree(priv->uptimer);
priv->uptimer = NULL;
}
state = 1;
if(start_ptr[1] == 0xFE)
state = 0;
else
{
int new_x = unpack(start_ptr[1], start_ptr[2]);
int new_y = unpack(start_ptr[3], start_ptr[4]);
int shift = abs(new_x - (priv->cur_x >> 4));
shift += abs(new_y - (priv->cur_y >> 4));
if(shift < 1400)
{
cur_y = ((priv->cur_y * 15) >> 4) + new_y;
cur_x = ((priv->cur_x * 15) >> 4) + new_x;
}
else if(shift < 3000)
{
cur_y = ((priv->cur_y * 7) >> 3) + (new_y << 1);
cur_x = ((priv->cur_x * 7) >> 3) + (new_x << 1);
}
else if(shift < 6000)
{
cur_y = ((priv->cur_y * 3) >> 2) + (new_y << 2);
cur_x = ((priv->cur_x * 3) >> 2) + (new_x << 2);
state = priv->state;
}
else
{
cur_y = new_y << 4;
cur_x = new_x << 4;
state = priv->state;
}
xf86PostMotionEvent(priv->stylus->dev, TRUE, 0, 2, cur_x >> 4, cur_y >> 4);
priv->cur_x = cur_x;
priv->cur_y = cur_y;
}
start_ptr = ptr;
bytes_in_packet = 0;
if (state != priv->state && priv->tap_button)
{
xf86PostButtonEvent(priv->stylus->dev, TRUE, 1, state|priv->button1,
0, 2, priv->cur_x >> 4, priv->cur_y >> 4);
priv->state = state;
if(state == 1)
priv->uptimer = TimerSet(priv->uptimer, 0, 100, PalmaxUpTimeout, priv);
}
}
}
if (num_bytes != 0) {
memcpy(priv->rec_buf, ptr, num_bytes);
priv->num_old_bytes = num_bytes;
}
else
{
priv->num_old_bytes = 0;
}
}
static void PMXPtrCtrl(DeviceIntPtr device, PtrCtrl *ctrl)
{
}
static Bool
xf86PmxControl(DeviceIntPtr dev,
int mode)
{
static unsigned char map[] = { 0, 1, 3, 2};
LocalDevicePtr local = (LocalDevicePtr) dev->public.devicePrivate;
PMXPrivatePtr priv = (PMXPrivatePtr)(local->private);
switch(mode)
{
case DEVICE_INIT:
if (priv->screen_no >= screenInfo.numScreens || priv->screen_no < 0) {
priv->screen_no = 0;
}
priv->screen_width = screenInfo.screens[priv->screen_no]->width;
priv->screen_height = screenInfo.screens[priv->screen_no]->height;
if (InitButtonClassDeviceStruct(dev, 3, map) == FALSE) {
ErrorF("Unable to allocate ButtonClassDeviceStruct\n");
return !Success;
}
if (InitValuatorClassDeviceStruct(dev, 2, xf86GetMotionEvents,
local->history_size, Absolute) == FALSE) {
ErrorF("Unable to allocate ValuatorClassDeviceStruct\n");
return !Success;
}
else
{
InitValuatorAxisStruct(dev, 0, priv->min_x, priv->max_x,
65535,
0 ,
65535 );
InitValuatorAxisStruct(dev, 1, priv->min_y, priv->max_y,
65535,
0 ,
65535 );
}
if (InitFocusClassDeviceStruct(dev) == FALSE) {
ErrorF("Unable to allocate FocusClassDeviceStruct\n");
}
if(InitPtrFeedbackClassDeviceStruct(dev, PMXPtrCtrl) == FALSE) {
ErrorF("Unable to allocate PtrFeedBackClassDeviceStruct\n");
}
xf86MotionHistoryAllocate(local);
return Success;
case DEVICE_ON:
if (local->fd < 0) {
local->fd = xf86OpenSerial(local->options);
if (local->fd < 0) {
Error("Unable to open Palmax touchscreen device");
return !Success;
}
AddEnabledDevice(local->fd);
}
priv->polltimer = TimerSet(priv->polltimer, 0, 100, PalmaxPollTimeout, local);
dev->public.on = TRUE;
return Success;
case DEVICE_OFF:
if(priv->polltimer)
{
TimerFree(priv->polltimer);
priv->polltimer = NULL;
}
dev->public.on = FALSE;
return Success;
case DEVICE_CLOSE:
dev->public.on = FALSE;
if(priv->uptimer)
{
TimerFree(priv->uptimer);
priv->uptimer = NULL;
}
if (local->fd >= 0) {
xf86RemoveEnabledDevice(local);
xf86CloseSerial(local->fd);
local->fd = -1;
}
return Success;
default:
ErrorF("unsupported mode=%d\n", mode);
return !Success;
}
}
static int xf86PmxControlProc (InputInfoPtr pInfo, xDeviceCtl * control)
{
#if 0
xDeviceTSCalibrationCtl *c = (xDeviceTSCalibrationCtl *) control;
PMXPrivatePtr priv = (PMXPrivatePtr) (pInfo->private);
priv->min_x = c->min_x;
priv->max_x = c->max_x;
priv->min_y = c->min_y;
priv->max_y = c->max_y;
#endif
return (Success);
}
static LocalDevicePtr xf86PmxAllocate(InputDriverPtr drv, char *name, char *type_name, int flag)
{
LocalDevicePtr local = xf86AllocateInput(drv, 0);
PMXPrivatePtr priv = (PMXPrivatePtr) xalloc(sizeof(PMXPrivateRec));
if (!local || !priv)
{
if(priv)
xfree(priv);
if(local)
xfree(local);
return NULL;
}
priv->input_dev = strdup("/dev/ttyS0");
priv->min_x = 0;
priv->max_x = 0;
priv->min_y = 0;
priv->max_y = 0;
priv->screen_no = 0;
priv->screen_width = -1;
priv->screen_height = -1;
priv->inited = 0;
priv->state = 0;
priv->num_old_bytes = 0;
priv->swap_axes = 0;
priv->tap_button = 0;
priv->uptimer = NULL;
priv->polltimer = NULL;
priv->button1 = 0;
priv->button2 = 0;
local->name = name;
local->flags = 0 ;
local->device_control = xf86PmxControl;
local->read_input = xf86PmxReadInput;
local->control_proc = xf86PmxControlProc;
local->close_proc = NULL;
local->switch_mode = NULL;
local->conversion_proc = xf86PmxConvert;
local->reverse_conversion_proc = NULL;
local->fd = -1;
local->atom = 0;
local->dev = NULL;
local->private = priv;
local->private_flags = flag;
local->type_name = type_name;
local->history_size = 0;
return local;
}
static LocalDevicePtr xf86PmxAllocateStylus(InputDriverPtr drv)
{
LocalDevicePtr local = xf86PmxAllocate(drv, XI_STYLUS, "Palmax Stylus", 1);
if (local)
((PMXPrivatePtr) local->private)->stylus = local;
return local;
}
static void xf86PmxUninit(InputDriverPtr drv, LocalDevicePtr local, int flags)
{
PMXPrivatePtr priv = (PMXPrivatePtr) local->private;
xf86PmxControl(local->dev, DEVICE_OFF);
if (priv) {
priv->stylus->private = NULL;
xfree(priv->input_dev);
xfree(priv);
}
xfree(local->name);
xfree(local);
xf86DeleteInput(local, 0);
}
static const char *default_options[] = {
"BaudRate", "19200",
"StopBits", "1",
"DataBits", "8",
"Parity", "None",
"Vmin", "1",
"Vtime", "1",
"FlowControl", "None",
NULL
};
static InputInfoPtr xf86PmxInit(InputDriverPtr drv, IDevPtr dev, int flags)
{
LocalDevicePtr local=NULL, fake_local=NULL;
PMXPrivatePtr priv=NULL;
char *str;
int portrait=0;
fake_local = (LocalDevicePtr) xcalloc(1, sizeof(LocalDeviceRec));
if (!fake_local) {
goto init_err;
}
fake_local->conf_idev = dev;
xf86CollectInputOptions(fake_local, default_options, NULL);
local = xf86PmxAllocateStylus(drv);
priv = local->private;
local->options = fake_local->options;
local->conf_idev = fake_local->conf_idev;
xfree(fake_local);
fake_local = NULL;
str = xf86FindOptionValue(local->options, "Device");
if (!str) {
xf86Msg(X_ERROR, "%s: No Device specified in Palmax module config.\n", dev->identifier);
goto init_err;
}
priv->input_dev = strdup(str);
priv->stylus = local;
xf86ProcessCommonOptions(local, local->options);
str = xf86FindOptionValue(local->options, "DeviceName");
if (str) {
local->name = strdup(str);
}
xf86Msg(X_CONFIG, "Palmax X device name: %s\n", local->name);
priv->screen_no = xf86SetIntOption(local->options, "ScreenNo", 0);
xf86Msg(X_CONFIG, "Palmax associated screen: %d\n", priv->screen_no);
priv->max_x = xf86SetIntOption(local->options, "MaxX", DEFAULT_MAX_X);
xf86Msg(X_CONFIG, "Palmax maximum x position: %d\n", priv->max_x);
priv->min_x = xf86SetIntOption(local->options, "MinX", DEFAULT_MIN_X);
xf86Msg(X_CONFIG, "Palmax minimum x position: %d\n", priv->min_x);
priv->max_y = xf86SetIntOption(local->options, "MaxY", DEFAULT_MAX_Y);
xf86Msg(X_CONFIG, "Palmax maximum y position: %d\n", priv->max_y);
priv->min_y = xf86SetIntOption(local->options, "MinY", DEFAULT_MIN_Y);
xf86Msg(X_CONFIG, "Palmax minimum y position: %d\n", priv->min_y);
priv->tap_button = xf86SetBoolOption(local->options, "TapButton", 0);
if(priv->tap_button)
xf86Msg(X_CONFIG, "Palmax touchpad acts as button\n");
priv->swap_axes = xf86SetBoolOption(local->options, "SwapXY", 0);
if (priv->swap_axes) {
xf86Msg(X_CONFIG, "Palmax %s device will work with X and Y axes swapped\n",
local->name);
}
str = xf86SetStrOption(local->options, "PortraitMode", "Landscape");
if (strcmp(str, "Portrait") == 0) {
portrait = 1;
}
else if (strcmp(str, "PortraitCCW") == 0) {
portrait = -1;
}
else if (strcmp(str, "Landscape") != 0) {
xf86Msg(X_ERROR, "Palmax portrait mode should be: Portrait, Landscape or PortraitCCW");
str = "Landscape";
}
xf86Msg(X_CONFIG, "Palmax device will work in %s mode\n", str);
if (priv->max_x - priv->min_x <= 0) {
xf86Msg(X_INFO, "Palmax: reverse x mode (minimum x position >= maximum x position)\n");
}
if (priv->max_y - priv->min_y <= 0) {
xf86Msg(X_INFO, "Palmax: reverse y mode (minimum y position >= maximum y position)\n");
}
if (portrait == 1) {
int tmp;
tmp = priv->min_y;
priv->min_y = priv->max_y;
priv->max_y = tmp;
priv->swap_axes = (priv->swap_axes==0) ? 1 : 0;
}
else if (portrait == -1) {
int tmp;
tmp = priv->min_x;
priv->min_x = priv->max_x;
priv->max_x = tmp;
priv->swap_axes = (priv->swap_axes==0) ? 1 : 0;
}
local->flags |= XI86_CONFIGURED;
return local;
init_err:
if (fake_local)
xfree(fake_local);
if (priv) {
if (priv->input_dev)
xfree(priv->input_dev);
xfree(priv);
}
if (local)
xfree(local);
return NULL;
}
#ifdef XFree86LOADER
static
#endif
InputDriverRec PALMAX = {
1,
"palmax",
NULL,
xf86PmxInit,
xf86PmxUninit,
NULL,
0
};
#ifdef XFree86LOADER
static pointer Plug(pointer module, pointer options, int *errmaj,int *errmin)
{
xf86LoaderReqSymLists(reqSymbols, NULL);
xf86AddInputDriver(&PALMAX, module, 0);
return module;
}
static void Unplug(pointer p)
{
}
static XF86ModuleVersionInfo version_rec = {
"palmax",
MODULEVENDORSTRING,
MODINFOSTRING1,
MODINFOSTRING2,
XF86_VERSION_CURRENT,
1, 0, 0,
ABI_CLASS_XINPUT,
ABI_XINPUT_VERSION,
MOD_CLASS_XINPUT,
{ 0, 0, 0, 0 }
};
XF86ModuleData palmaxModuleData = { &version_rec, Plug, Unplug };
#endif