/* * Copyright 1995-2002 by Frederic Lepied, France. * Copyright 2002-2009 by Ping Cheng, Wacom Technology. * * 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 Lesser 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. */ #ifdef HAVE_CONFIG_H #include #endif #include "xf86Wacom.h" #include "wcmFilter.h" #define WC_ISDV4_QUERY "*" /* ISDV4 query command */ #define WC_ISDV4_TOUCH_QUERY "%" /* ISDV4 touch query command */ #define WC_ISDV4_STOP "0" /* ISDV4 stop command */ #define WC_ISDV4_SAMPLING "1" /* ISDV4 sampling command */ static Bool isdv4Detect(LocalDevicePtr); static Bool isdv4Init(LocalDevicePtr, char* id, float *version); static void isdv4InitISDV4(WacomCommonPtr, const char* id, float version); static int isdv4GetRanges(LocalDevicePtr); static int isdv4StartTablet(LocalDevicePtr); static int isdv4Parse(LocalDevicePtr, const unsigned char* data); static int xf86WcmSerialValidate(WacomCommonPtr common, const unsigned char* data); static int xf86WcmWaitForTablet(int fd, char * data, int size); static int xf86WcmWriteWait(int fd, const char* request); WacomDeviceClass gWacomISDV4Device = { isdv4Detect, isdv4Init, xf86WcmReadPacket, }; static WacomModel isdv4General = { "General ISDV4", isdv4InitISDV4, NULL, /* resolution not queried */ isdv4GetRanges, /* query ranges */ NULL, /* reset not supported */ NULL, /* tilt automatically enabled */ NULL, /* suppress implemented in software */ NULL, /* link speed unsupported */ isdv4StartTablet, /* start tablet */ isdv4Parse, }; /***************************************************************************** * XFree86 V4 Functions ****************************************************************************/ static int xf86WcmWait(int t) { int err = xf86WaitForInput(-1, ((t) * 1000)); if (err != -1) return Success; xf86Msg(X_ERROR, "Wacom select error : %s\n", strerror(errno)); return err; } /***************************************************************************** * xf86WcmSerialValidate -- validates serial packet; returns 0 on success, * positive number of bytes to skip on error. ****************************************************************************/ static int xf86WcmSerialValidate(WacomCommonPtr common, const unsigned char* data) { int i, bad = 0; /* check magic */ for (i=0; iwcmPktLength; ++i) { if ( ((i==0) && !(data[i] & HEADER_BIT)) || ((i!=0) && (data[i] & HEADER_BIT)) ) { bad = 1; if (i!=0 && (data[i] & HEADER_BIT)) { xf86Msg(X_WARNING, "xf86WcmSerialValidate: " "bad magic at %d v=%x l=%d\n", i, data[i], common->wcmPktLength); return i; } } } if (bad) return common->wcmPktLength; else return 0; } /***************************************************************************** * isdv4Detect -- Test if the attached device is ISDV4. ****************************************************************************/ static Bool isdv4Detect(LocalDevicePtr local) { WacomDevicePtr priv = (WacomDevicePtr) local->private; WacomCommonPtr common = priv->common; return (common->wcmForceDevice == DEVICE_ISDV4) ? 1 : 0; } /***************************************************************************** * isdv4Init -- ****************************************************************************/ static Bool isdv4Init(LocalDevicePtr local, char* id, float *version) { WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; DBG(1, priv->debugLevel, ErrorF("initializing ISDV4 tablet\n")); /* Initial baudrate is 38400 */ if (xf86SetSerialSpeed(local->fd, common->wcmISDV4Speed) < 0) return !Success; if(id) strcpy(id, "ISDV4"); if(version) *version = common->wcmVersion; /*set the model */ common->wcmModel = &isdv4General; return Success; } /***************************************************************************** * isdv4Query -- Query the device ****************************************************************************/ static int isdv4Query(LocalDevicePtr local, const char* query, char* data) { int err; WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; DBG(1, priv->debugLevel, ErrorF("Querying ISDV4 tablet\n")); /* Send stop command to the tablet */ err = xf86WriteSerial(local->fd, WC_ISDV4_STOP, strlen(WC_ISDV4_STOP)); if (err == -1) { xf86Msg(X_WARNING, "%s: xf86WcmWrite ISDV4_STOP error : %s\n", local->name, strerror(errno)); return !Success; } /* Wait 250 mSecs */ if (xf86WcmWait(250)) return !Success; /* Send query command to the tablet */ if (!xf86WcmWriteWait(local->fd, query)) { xf86Msg(X_WARNING, "%s: unable to xf86WcmWrite request %s " "ISDV4 query command after %d tries\n", local->name, query, MAXTRY); return !Success; } /* Read the control data */ if (!xf86WcmWaitForTablet(local->fd, data, WACOM_PKGLEN_TPCCTL)) { /* Try 19200 if it is not a touch query */ if (common->wcmISDV4Speed != 19200 && strcmp(query, WC_ISDV4_TOUCH_QUERY)) { common->wcmISDV4Speed = 19200; if (xf86SetSerialSpeed(local->fd, common->wcmISDV4Speed) < 0) return !Success; return isdv4Query(local, query, data); } else { xf86Msg(X_WARNING, "%s: unable to read ISDV4 %s data " "after %d tries at (%d)\n", local->name, query, MAXTRY, common->wcmISDV4Speed); return !Success; } } /* Control data bit check */ if ( !(data[0] & 0x40) ) { /* Try 19200 if it is not a touch query */ if (common->wcmISDV4Speed != 19200 && strcmp(query, WC_ISDV4_TOUCH_QUERY)) { common->wcmISDV4Speed = 19200; if (xf86SetSerialSpeed(local->fd, common->wcmISDV4Speed) < 0) return !Success; return isdv4Query(local, query, data); } else { /* Reread the control data since it may fail the first time */ xf86WcmWaitForTablet(local->fd, data, WACOM_PKGLEN_TPCCTL); if ( !(data[0] & 0x40) ) { xf86Msg(X_WARNING, "%s: ISDV4 control data " "(%x) error in %s query\n", local->name, data[0], query); return !Success; } } } return Success; } /***************************************************************************** * isdv4InitISDV4 -- Setup the device ****************************************************************************/ static void isdv4InitISDV4(WacomCommonPtr common, const char* id, float version) { /* set parameters */ common->wcmProtocolLevel = 4; /* length of a packet */ common->wcmPktLength = WACOM_PKGLEN_TPCPEN; /* digitizer X resolution in points/inch */ common->wcmResolX = 2540; /* digitizer Y resolution in points/inch */ common->wcmResolY = 2540; /* no touch */ common->tablet_id = 0x90; /* tilt disabled */ common->wcmFlags &= ~TILT_ENABLED_FLAG; } /***************************************************************************** * isdv4GetRanges -- get ranges of the device ****************************************************************************/ static int isdv4GetRanges(LocalDevicePtr local) { char data[BUFFER_SIZE]; WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; int ret = Success; DBG(2, priv->debugLevel, ErrorF("getting ISDV4 Ranges\n")); /* Send query command to the tablet */ ret = isdv4Query(local, WC_ISDV4_QUERY, data); if (ret == Success) { /* transducer data */ common->wcmMaxZ = ( data[5] | ((data[6] & 0x07) << 7) ); common->wcmMaxX = ( (data[1] << 9) | (data[2] << 2) | ( (data[6] & 0x60) >> 5) ); common->wcmMaxY = ( (data[3] << 9) | (data[4] << 2 ) | ( (data[6] & 0x18) >> 3) ); if (data[7] && data[8]) { common->wcmMaxtiltX = data[7] + 1; common->wcmMaxtiltY = data[8] + 1; common->wcmFlags |= TILT_ENABLED_FLAG; } common->wcmVersion = ( data[10] | (data[9] << 7) ); /* default to no pen 2FGT if size is undefined */ if (!common->wcmMaxX || !common->wcmMaxY) common->tablet_id = 0xE2; DBG(2, priv->debugLevel, ErrorF("isdv4GetRanges Pen speed=%d " "maxX=%d maxY=%d maxZ=%d resX=%d resY=%d \n", common->wcmISDV4Speed, common->wcmMaxX, common->wcmMaxY, common->wcmMaxZ, common->wcmResolX, common->wcmResolY)); } /* Touch might be supported. Send a touch query command */ common->wcmISDV4Speed = 38400; if (isdv4Query(local, WC_ISDV4_TOUCH_QUERY, data) == Success) { switch (data[2] & 0x07) { case 0x00: /* resistive touch & pen */ common->wcmPktLength = WACOM_PKGLEN_TOUCH93; common->tablet_id = 0x93; break; case 0x01: /* capacitive touch & pen */ common->wcmPktLength = WACOM_PKGLEN_TOUCH9A; common->tablet_id = 0x9A; break; case 0x02: /* resistive touch */ common->wcmPktLength = WACOM_PKGLEN_TOUCH93; common->tablet_id = 0x93; break; case 0x03: /* capacitive touch */ common->wcmPktLength = WACOM_PKGLEN_TOUCH9A; common->tablet_id = 0x9F; break; case 0x04: /* capacitive touch */ common->wcmPktLength = WACOM_PKGLEN_TOUCH9A; common->tablet_id = 0x9F; break; case 0x05: common->wcmPktLength = WACOM_PKGLEN_TOUCH2FG; /* a penabled */ if (common->tablet_id == 0x90) common->tablet_id = 0xE3; break; } switch(data[0] & 0x3f) { /* single finger touch */ case 0x01: if ((common->tablet_id != 0x93) && (common->tablet_id != 0x9A) && (common->tablet_id != 0x9F)) { xf86Msg(X_WARNING, "WACOM: %s tablet id(%x)" " mismatch with data id (0x01) \n", local->name, common->tablet_id); return ret; } break; /* 2FGT */ case 0x03: if ((common->tablet_id != 0xE2) && (common->tablet_id != 0xE3)) { xf86Msg(X_WARNING, "WACOM: %s tablet id(%x)" " mismatch with data id (0x03) \n", local->name, common->tablet_id); return ret; } break; } /* don't overwrite the default */ if (((data[2]& 0x78) | data[3] | data[4] | data[5] | data[6])) { common->wcmMaxTouchX = ((data[3] << 9) | (data[4] << 2) | ((data[2] & 0x60) >> 5)); common->wcmMaxTouchY = ((data[5] << 9) | (data[6] << 2) | ((data[2] & 0x18) >> 3)); } else if (data[1]) common->wcmMaxTouchX = common->wcmMaxTouchY = (int)(1 << data[1]); if (data[1]) { common->wcmTouchResolX = common->wcmTouchResolY = 10; common->wcmTouchDefault = 1; } else common->wcmTouchDefault = 0; /* update touch info */ common->wcmTouch = xf86SetBoolOption(local->options, "Touch", common->wcmTouchDefault); common->wcmVersion = ( data[10] | (data[9] << 7) ); ret = Success; DBG(2, priv->debugLevel, ErrorF("isdv4GetRanges touch speed=%d " "maxTouchX=%d maxTouchY=%d TouchresX=%d TouchresY=%d \n", common->wcmISDV4Speed, common->wcmMaxTouchX, common->wcmMaxTouchY, common->wcmTouchResolX, common->wcmTouchResolY)); } return ret; } static int isdv4StartTablet(LocalDevicePtr local) { int err; /* Tell the tablet to start sending coordinates */ err = xf86WriteSerial(local->fd, WC_ISDV4_SAMPLING, (strlen(WC_ISDV4_SAMPLING))); if (err == -1) { xf86Msg(X_ERROR, "%s: xf86WcmWrite error : %s\n", local->name, strerror(errno)); return !Success; } return Success; } static int isdv4Parse(LocalDevicePtr local, const unsigned char* data) { WacomDevicePtr priv = (WacomDevicePtr)local->private; WacomCommonPtr common = priv->common; WacomDeviceState* last = &common->wcmChannel[0].valid.state; WacomDeviceState* lastTemp = &common->wcmChannel[1].valid.state; WacomDeviceState* ds; int n, cur_type, channel = 0; DBG(10, common->debugLevel, ErrorF("isdv4Parse \n")); /* determine the type of message (touch or stylus) */ if (data[0] & 0x10) /* a touch data */ { if ((last->device_id != TOUCH_DEVICE_ID && last->device_id && last->proximity ) || !common->wcmTouch) { /* ignore touch event */ return common->wcmPktLength; } } else { /* touch was in control */ if (last->proximity && last->device_id == TOUCH_DEVICE_ID) { /* let touch go */ WacomDeviceState out = { 0 }; out.device_type = TOUCH_ID; xf86WcmEvent(common, channel, &out); } } if (common->buffer + common->bufpos - data < common->wcmPktLength) { /* we can't handle this yet */ return common->wcmPktLength; } /* Coordinate data bit check */ if (data[0] & 0x40) /* control data */ return common->wcmPktLength; else if ((n = xf86WcmSerialValidate(common,data)) > 0) return n; /* pick up where we left off, minus relative values */ ds = &common->wcmChannel[channel].work; RESET_RELATIVE(*ds); if (common->wcmPktLength != WACOM_PKGLEN_TPCPEN) /* a touch */ { ds->x = (((int)data[1]) << 7) | ((int)data[2]); ds->y = (((int)data[3]) << 7) | ((int)data[4]); if (common->wcmPktLength == WACOM_PKGLEN_TOUCH9A) { ds->capacity = (((int)data[5]) << 7) | ((int)data[6]); } ds->buttons = ds->proximity = data[0] & 0x01; ds->device_type = TOUCH_ID; ds->device_id = TOUCH_DEVICE_ID; DBG(8, priv->debugLevel, ErrorF("isdv4Parse MultiTouch " "%s proximity \n", ds->proximity ? "in" : "out of")); } else { ds->proximity = (data[0] & 0x20); /* x and y in "normal" orientetion (wide length is X) */ ds->x = (((int)data[6] & 0x60) >> 5) | ((int)data[2] << 2) | ((int)data[1] << 9); ds->y = (((int)data[6] & 0x18) >> 3) | ((int)data[4] << 2) | ((int)data[3] << 9); /* pressure */ ds->pressure = (((data[6] & 0x07) << 7) | data[5] ); /* buttons */ ds->buttons = (data[0] & 0x07); /* check which device we have */ cur_type = (ds->buttons & 4) ? ERASER_ID : STYLUS_ID; /* first time into prox */ if (!last->proximity && ds->proximity) ds->device_type = cur_type; /* check on previous proximity */ else if (ds->buttons && ds->proximity) { /* we might have been fooled by tip and second * sideswitch when it came into prox */ if ((ds->device_type != cur_type) && (ds->device_type == ERASER_ID)) { /* send a prox-out for old device */ WacomDeviceState out = { 0 }; xf86WcmEvent(common, 0, &out); ds->device_type = cur_type; } } ds->device_id = (ds->device_type == ERASER_ID) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID; /* don't send button 3 event for eraser * button 1 event will be sent by testing presure level */ if (ds->device_type == ERASER_ID && ds->buttons&4) { ds->buttons = 0; ds->device_id = ERASER_DEVICE_ID; } if (common->wcmPktLength == WACOM_PKGLEN_TOUCH2FG) { if ((data[0] & 0x02) || (!(data[0] & 0x02) && lastTemp->proximity)) { /* Got 2FGT. Send the first one if received */ if (ds->proximity || (!ds->proximity && last->proximity)) xf86WcmEvent(common, channel, ds); channel = 1; ds = &common->wcmChannel[channel].work; RESET_RELATIVE(*ds); ds->x = (((int)data[7]) << 7) | ((int)data[8]); ds->y = (((int)data[9]) << 7) | ((int)data[10]); ds->device_type = TOUCH_ID; ds->device_id = TOUCH_DEVICE_ID; ds->proximity = data[0] & 0x02; } } DBG(8, priv->debugLevel, ErrorF("isdv4Parse %s\n", ds->device_type == ERASER_ID ? "ERASER " : ds->device_type == STYLUS_ID ? "STYLUS" : "NONE")); } xf86WcmEvent(common, channel, ds); return common->wcmPktLength; } /***************************************************************************** * xf86WcmWrite -- * send a request ****************************************************************************/ static int xf86WcmWriteWait(int fd, const char* request) { int len, maxtry = MAXTRY; /* send request string */ do { len = xf86WriteSerial(fd, request, strlen(request)); if ((len == -1) && (errno != EAGAIN)) { xf86Msg(X_ERROR, "Wacom xf86WcmWriteWait error : %s", strerror(errno)); return 0; } maxtry--; } while ((len <= 0) && maxtry); return maxtry; } /***************************************************************************** * xf86WcmWaitForTablet -- * wait for tablet data ****************************************************************************/ static int xf86WcmWaitForTablet(int fd, char* answer, int size) { int len, maxtry = MAXTRY; /* Read size bytes of the answer */ do { if ((len = xf86WaitForInput(fd, 1000000)) > 0) { len = xf86ReadSerial(fd, answer, size); if ((len == -1) && (errno != EAGAIN)) { xf86Msg(X_ERROR, "Wacom xf86WcmRead error : %s\n", strerror(errno)); return 0; } } maxtry--; } while ((len <= 0) && maxtry); return maxtry; } /* vim: set noexpandtab shiftwidth=8: */