Logo Search packages:      
Sourcecode: wacom-tools version File versions

wacserial.c

/*****************************************************************************
** wacserial.c
**
** Copyright (C) 2002, 2003 - John E. Joganic
**
** 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.
**
****************************************************************************/

#include "wacserial.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <termios.h>
#include <ctype.h>
#include <unistd.h>
#include <assert.h>

/*****************************************************************************
** Serial Tablet Object
*****************************************************************************/

typedef struct _SERIALTABLET SERIALTABLET;
typedef struct _SERIALSUBTYPE SERIALSUBTYPE;
typedef struct _SERIALDEVICE SERIALDEVICE;
typedef struct _SERIALVENDOR SERIALVENDOR;

typedef int (*IDENTFUNC)(SERIALTABLET* pSerial);
typedef int (*INITFUNC)(SERIALTABLET* pSerial);
typedef int (*PARSEFUNC)(SERIALTABLET* pSerial, const unsigned char* puchData,
            unsigned int uLength, WACOMSTATE* pState);

struct _SERIALTABLET
{
      WACOMTABLET_PRIV tablet;
      WACOMENGINE hEngine;
      int fd;
      SERIALVENDOR* pVendor;
      SERIALDEVICE* pDevice;
      SERIALSUBTYPE* pSubType;
      unsigned int uPacketLength;
      int nVerMajor, nVerMinor, nVerRelease;
      IDENTFUNC pfnIdent;
      INITFUNC pfnInit;
      PARSEFUNC pfnParse;
      int nToolID;
      WACOMSTATE state;
      int nBitErrors;
      WACOMMODEL modelRequested;
};

/*****************************************************************************
** Internal structures
*****************************************************************************/

struct _SERIALSUBTYPE
{
      const char* pszName;
      const char* pszDesc;
      unsigned int uSubType;
      const char* pszIdent;
      INITFUNC pfnInit;
};

struct _SERIALDEVICE
{
      const char* pszName;
      const char* pszDesc;
      unsigned int uDevice;
      SERIALSUBTYPE* pSubTypes;
      int nProtocol;
      unsigned int uPacketLength;
      unsigned int uCaps;
      int nMinBaudRate;
      IDENTFUNC pfnIdent;
};

struct _SERIALVENDOR
{
      const char* pszName;
      const char* pszDesc;
      unsigned int uVendor;
      SERIALDEVICE* pDevices;
};

/*****************************************************************************
** Static operations
*****************************************************************************/

static void SerialClose(WACOMTABLET_PRIV* pTablet);
static WACOMMODEL SerialGetModel(WACOMTABLET_PRIV* pTablet);
static const char* SerialGetVendorName(WACOMTABLET_PRIV* pTablet);
static const char* SerialGetClassName(WACOMTABLET_PRIV* pTablet);
static const char* SerialGetDeviceName(WACOMTABLET_PRIV* pTablet);
static const char* SerialGetSubTypeName(WACOMTABLET_PRIV* pTablet);
static const char* SerialGetModelName(WACOMTABLET_PRIV* pTablet);
static int SerialGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor,
            int* pnMinor, int* pnRelease);
static int SerialGetCaps(WACOMTABLET_PRIV* pTablet);
static int SerialGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState);
static int SerialGetFD(WACOMTABLET_PRIV* pTablet);
static int SerialReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData,
            unsigned int uSize);
static int SerialParseData(WACOMTABLET_PRIV* pTablet,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState);

static int SerialReset(SERIALTABLET* pSerial, WACOMMODEL* pModel);
static int SerialConfigTTY(SERIALTABLET* pSerial);
static int SerialResetAtBaud(SERIALTABLET* pSerial, struct termios* pTIOS,
            int nBaud);
static int SerialSetDevice(SERIALTABLET* pSerial, SERIALVENDOR* pVendor,
            SERIALDEVICE* pDevice, SERIALSUBTYPE* pSubType);

static int SerialIdentDefault(SERIALTABLET* pSerial);
static int SerialIdentAcerC100(SERIALTABLET* pSerial);
static int SerialInitAcerC100(SERIALTABLET* pSerial);
static int SerialIdentWacom(SERIALTABLET* pSerial);
static int SerialInitWacom(SERIALTABLET* pSerial);

static int SerialParseWacomV(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState);
static int SerialParseWacomIV_1_4(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState);
static int SerialParseWacomIV_1_3(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState);
static int SerialParseWacomIV_1_2(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState);
static int SerialParseAcerC100(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState);

static void SerialError(SERIALTABLET* pSerial, const char* pszFmt, ...);
static void SerialWarn(SERIALTABLET* pSerial, const char* pszFmt, ...);
static void SerialInfo(SERIALTABLET* pSerial, const char* pszFmt, ...);
static void SerialDump(SERIALTABLET* pSerial, const void* pvData, int nCnt);
/* NOT USED, YET
static void SerialCritical(SERIALTABLET* pSerial, const char* pszFmt, ...);
static void SerialDebug(SERIALTABLET* pSerial, const char* pszFmt, ...);
static void SerialTrace(SERIALTABLET* pSerial, const char* pszFmt, ...);
*/

/*****************************************************************************
** Defines
*****************************************************************************/

#ifndef BIT
#undef BIT
#define BIT(x) (1<<(x))
#endif

#define WACOMVALID(x) BIT(WACOMFIELD_##x)

#define ARTPAD_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
            WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
            WACOMVALID(PRESSURE))

#define ARTPADII_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
            WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
            WACOMVALID(PRESSURE))

#define DIGITIZER_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
            WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
            WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y))

#define DIGITIZERII_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
            WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
            WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y))

#define PENPARTNER_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
            WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
            WACOMVALID(PRESSURE))

#define GRAPHIRE_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
            WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
            WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y))

#define CINTIQ_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(PROXIMITY)| \
            WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)|WACOMVALID(POSITION_Y)| \
            WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y))

#define INTUOS_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(SERIAL)| \
            WACOMVALID(PROXIMITY)|WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)| \
            WACOMVALID(POSITION_Y)|WACOMVALID(ROTATION_Z)|WACOMVALID(DISTANCE)| \
            WACOMVALID(PRESSURE)|WACOMVALID(TILT_X)|WACOMVALID(TILT_Y)| \
            WACOMVALID(ABSWHEEL)|WACOMVALID(RELWHEEL)|WACOMVALID(THROTTLE))

#define ACERC100_CAPS (WACOMVALID(TOOLTYPE)|WACOMVALID(SERIAL)| \
            WACOMVALID(PROXIMITY)|WACOMVALID(BUTTONS)|WACOMVALID(POSITION_X)| \
            WACOMVALID(POSITION_Y)|WACOMVALID(PRESSURE))

#define INTUOS2_CAPS INTUOS_CAPS

#define PROTOCOL_4 4
#define PROTOCOL_5 5

#define WACOM_SUBTYPE(id,d,s) \
      { id, d, s, id, SerialInitWacom }
#define ACER_SUBTYPE(id,d,s) \
      { id, d, s, id, SerialInitAcerC100 }

#define WACOM_DEVICE_P4(n,d,i,s,c) \
      { n, d, i, s, PROTOCOL_4, 7, c, 9600, SerialIdentWacom }
#define WACOM_DEVICE_P5(n,d,i,s,c) \
      { n, d, i, s, PROTOCOL_5, 9, c, 9600, SerialIdentWacom }
#define ACER_DEVICE(n,d,i,s,c) \
      { n, d, i, s, 0, 9, c, 19200, SerialIdentAcerC100 }

/*****************************************************************************
** Globals
*****************************************************************************/

      static SERIALSUBTYPE xArtPadII[] =
      {
            WACOM_SUBTYPE("KT-0405-R", "Wacom ArtPadII 4x5", 1),
            { NULL }
      };

      static SERIALSUBTYPE xDigitizerII[] =
      {
            WACOM_SUBTYPE("UD-0608-R", "Wacom DigitizerII 6x8",   1),
            WACOM_SUBTYPE("UD-1212-R", "Wacom DigitizerII 12x12", 2),
            WACOM_SUBTYPE("UD-1218-R", "Wacom DigitizerII 12x18", 3),
            WACOM_SUBTYPE("UD-1825-R", "Wacom DigitizerII 18x25", 4),
            { NULL }
      };

      static SERIALSUBTYPE xPenPartner[] =
      {
            WACOM_SUBTYPE("CT-0405-R", "Wacom PenPartner", 1),
            { NULL }
      };

      static SERIALSUBTYPE xGraphire[] =
      {
            WACOM_SUBTYPE("ET-0405-R", "Wacom Graphire", 1),
            { NULL }
      };

      static SERIALSUBTYPE xIntuos[] =
      {
            WACOM_SUBTYPE("GD-0405-R", "Wacom Intuos 4x5",   1),
            WACOM_SUBTYPE("GD-0608-R", "Wacom Intuos 6x8",   2),
            WACOM_SUBTYPE("GD-0912-R", "Wacom Intuos 9x12",  3),
            WACOM_SUBTYPE("GD-1212-R", "Wacom Intuos 12x12", 4),
            WACOM_SUBTYPE("GD-1218-R", "Wacom Intuos 12x18", 5),
            { NULL }
      };

      static SERIALSUBTYPE xIntuos2[] =
      {
            WACOM_SUBTYPE("XD-0405-R", "Wacom Intuos2 4x5",   1),
            WACOM_SUBTYPE("XD-0608-R", "Wacom Intuos2 6x8",   2),
            WACOM_SUBTYPE("XD-0912-R", "Wacom Intuos2 9x12",  3),
            WACOM_SUBTYPE("XD-1212-R", "Wacom Intuos2 12x12", 4),
            WACOM_SUBTYPE("XD-1218-R", "Wacom Intuos2 12x18", 5),
            { NULL }
      };

      static SERIALSUBTYPE xCintiq[] =
      {
            WACOM_SUBTYPE("PL-250", "Wacom PL-250",     1),
            WACOM_SUBTYPE("PL-270", "Wacom PL-270",     2),
            WACOM_SUBTYPE("PL-400", "Wacom PL-400",     3),
            WACOM_SUBTYPE("PL-500", "Wacom PL-500",     4),
            WACOM_SUBTYPE("PL-550", "Wacom PL-550",     5),
            WACOM_SUBTYPE("PL-600", "Wacom PL-600",     6),
            WACOM_SUBTYPE("PL-600SX", "Wacom PL-600SX", 7),
            WACOM_SUBTYPE("PL-800", "Wacom PL-800",     8),
            { NULL }
      };

      static SERIALDEVICE xWacomDevices[] =
      {
            WACOM_DEVICE_P4("art", "ArtPad", WACOMDEVICE_ARTPAD,
                        NULL, ARTPAD_CAPS),
            WACOM_DEVICE_P4("art2", "ArtPadII", WACOMDEVICE_ARTPADII,
                        xArtPadII, ARTPADII_CAPS),
            WACOM_DEVICE_P4("dig", "Digitizer", WACOMDEVICE_DIGITIZER,
                        NULL, DIGITIZERII_CAPS),
            WACOM_DEVICE_P4("dig2", "Digitizer II", WACOMDEVICE_DIGITIZERII,
                        xDigitizerII, DIGITIZERII_CAPS),
            WACOM_DEVICE_P4("pp", "PenPartner", WACOMDEVICE_PENPARTNER,
                        xPenPartner, PENPARTNER_CAPS),
            WACOM_DEVICE_P4("gr", "Graphire", WACOMDEVICE_GRAPHIRE,
                        xGraphire, GRAPHIRE_CAPS),
            WACOM_DEVICE_P4("pl", "Cintiq (PL)", WACOMDEVICE_CINTIQ,
                        xCintiq, CINTIQ_CAPS),
            WACOM_DEVICE_P5("int", "Intuos", WACOMDEVICE_INTUOS,
                        xIntuos, INTUOS_CAPS),
            WACOM_DEVICE_P5("int2", "Intuos2", WACOMDEVICE_INTUOS2,
                        xIntuos2, INTUOS2_CAPS),
            { NULL }
      };

      /* This one is reverse engineered at this point */
      static SERIALSUBTYPE xAcerC100[] =
      {
            ACER_SUBTYPE("C100", "Acer C100 Tablet PC Screen", 1),
            { NULL }
      };

      static SERIALDEVICE xAcerDevices[] =
      {
            ACER_DEVICE("c100", "C100", WACOMDEVICE_ACERC100,
                        xAcerC100, ACERC100_CAPS),
            { NULL }
      };

      static SERIALVENDOR xWacomVendor =
      { "wacom", "Wacom", WACOMVENDOR_WACOM, xWacomDevices };

      static SERIALVENDOR xAcerVendor =
      { "acer", "Acer", WACOMVENDOR_ACER, xAcerDevices };

      static SERIALVENDOR* xVendors[] =
      {
            &xWacomVendor,
            &xAcerVendor,
            NULL
      };

/*****************************************************************************
** Static Prototypes
*****************************************************************************/

static int SerialSendReset(SERIALTABLET* pSerial);
static int SerialSendStop(SERIALTABLET* pSerial);
static int SerialSendStart(SERIALTABLET* pSerial);

static int SerialSend(SERIALTABLET* pSerial, const char* pszData);
static int SerialSendRaw(SERIALTABLET* pSerial, const void* pvData,
            unsigned int uSize);
static int WacomFlush(SERIALTABLET* pSerial);
static int SerialSendRequest(SERIALTABLET* pSerial, const char* pszRequest,
            char* pchResponse, unsigned int uSize);

/*****************************************************************************
** Public Functions
*****************************************************************************/

typedef struct
{
      void (*pfnFree)(void* pv);
} DEVLIST_INTERNAL;

static void SerialFreeDeviceList(void* pv)
{
      DEVLIST_INTERNAL* pInt = ((DEVLIST_INTERNAL*)pv) - 1;
      free(pInt);
}

int WacomGetSupportedSerialDeviceList(WACOMDEVICEREC** ppList, int* pnSize)
{
      int nIndex=0, nCnt=0;
      DEVLIST_INTERNAL* pInt;
      SERIALDEVICE* pDev;
      SERIALVENDOR** ppVendor;
      WACOMDEVICEREC* pRec;

      if (!ppList || !pnSize) { errno = EINVAL; return 1; }

      /* for each vendor, count up devices */
      for (ppVendor=xVendors; *ppVendor; ++ppVendor)
      {
            /* count up devices */
            for (pDev=(*ppVendor)->pDevices; pDev->pszName; ++pDev, ++nCnt) ;
      }
   
      /* allocate enough memory to hold internal structure and all records */
      pInt = (DEVLIST_INTERNAL*)malloc(sizeof(DEVLIST_INTERNAL) +
                              (sizeof(WACOMDEVICEREC) * nCnt));

      pInt->pfnFree = SerialFreeDeviceList;
      pRec = (WACOMDEVICEREC*)(pInt + 1);

      /* for each vendor, add devices */
      for (ppVendor=xVendors; *ppVendor; ++ppVendor)
      {
            for (pDev=(*ppVendor)->pDevices; pDev->pszName; ++pDev, ++nIndex)
            {
                  pRec[nIndex].pszName = pDev->pszName;
                  pRec[nIndex].pszDesc = pDev->pszDesc;
                  pRec[nIndex].pszVendorName = (*ppVendor)->pszName;
                  pRec[nIndex].pszVendorDesc = (*ppVendor)->pszDesc;
                  pRec[nIndex].pszClass = "serial";
                  pRec[nIndex].model.uClass = WACOMCLASS_SERIAL;
                  pRec[nIndex].model.uVendor = (*ppVendor)->uVendor;
                  pRec[nIndex].model.uDevice = pDev->uDevice;
                  pRec[nIndex].model.uSubType = 0;
            }
      }
      assert(nIndex == nCnt);

      *ppList = pRec;
      *pnSize = nCnt;
      return 0;
}

unsigned int WacomGetSerialDeviceFromName(const char* pszName)
{
      SERIALDEVICE* pDev;
      SERIALVENDOR** ppVendor;

      if (!pszName) { errno = EINVAL; return 0; }

      /* for each vendor, look for device */
      for (ppVendor=xVendors; *ppVendor; ++ppVendor)
      {
            /* count up devices */
            for (pDev=(*ppVendor)->pDevices; pDev->pszName; ++pDev)
            {
                  if (strcasecmp(pszName,pDev->pszName) == 0)
                        return pDev->uDevice;
            }
      }

      errno = ENOENT;
      return 0;
}

static int SerialFindModel(WACOMMODEL* pModel, SERIALVENDOR** ppVendor,
            SERIALDEVICE** ppDevice, SERIALSUBTYPE** ppSubType)
{
      SERIALVENDOR** ppPos;
      SERIALDEVICE* pDev;
      SERIALSUBTYPE* pSub;

      /* device type must be specified */
      if (!pModel)
            { errno = EINVAL; return 1; }

      /* no device specified, nothing found. */
      if (!pModel->uDevice)
      {
            *ppVendor = NULL;
            *ppDevice = NULL;
            *ppSubType = NULL;
            return 0;
      }

      /* for each vendor */
      for (ppPos=xVendors; *ppPos; ++ppPos)
      {
            /* check vendor */
            if (!pModel->uVendor || (pModel->uVendor == (*ppPos)->uVendor))
            {
                  /* for each device */
                  for (pDev=(*ppPos)->pDevices; pDev->pszName; ++pDev)
                  {
                        /* if device matches */
                        if (pModel->uDevice == pDev->uDevice)
                        {
                              /* no subtype specified, use it */
                              if (!pModel->uSubType)
                              {
                                    *ppVendor = *ppPos;
                                    *ppDevice = pDev;
                                    *ppSubType = NULL;
                                    return 0;
                              }
                              
                              /* for each subtype */
                              for (pSub=pDev->pSubTypes; pSub->pszName; ++pSub)
                              {
                                    /* if subtype matches */
                                    if (pModel->uSubType == pSub->uSubType)
                                    {
                                          *ppVendor = *ppPos;
                                          *ppDevice = pDev;
                                          *ppSubType = pSub;
                                          return 0;
                                    }
                              }

                              /* wrong subtype? maybe try another vendor */
                              if (!pModel->uVendor) break;

                              /* otherwise, no match. */
                              errno = ENOENT;
                              return 1;
                        }
                  } /* next device */

                  /* if vendor matches, but device does not, no match. */
                  if (pModel->uVendor)
                  {
                        errno = ENOENT;
                        return 1;
                  }
            }
      } /* next vendor */

      /* no match */
      errno = ENOENT;
      return 1;
}

WACOMTABLET WacomOpenSerialTablet(WACOMENGINE hEngine, int fd,
      WACOMMODEL* pModel)
{
      SERIALTABLET* pSerial = NULL;

      /* Allocate tablet */
      pSerial = (SERIALTABLET*)malloc(sizeof(SERIALTABLET));
      memset(pSerial,0,sizeof(*pSerial));
      pSerial->tablet.Close = SerialClose;
      pSerial->tablet.GetModel = SerialGetModel;
      pSerial->tablet.GetVendorName = SerialGetVendorName;
      pSerial->tablet.GetClassName = SerialGetClassName;
      pSerial->tablet.GetDeviceName = SerialGetDeviceName;
      pSerial->tablet.GetSubTypeName = SerialGetSubTypeName;
      pSerial->tablet.GetModelName = SerialGetModelName;
      pSerial->tablet.GetROMVer = SerialGetROMVer;
      pSerial->tablet.GetCaps = SerialGetCaps;
      pSerial->tablet.GetState = SerialGetState;
      pSerial->tablet.GetFD = SerialGetFD;
      pSerial->tablet.ReadRaw = SerialReadRaw;
      pSerial->tablet.ParseData = SerialParseData;

      pSerial->hEngine = hEngine;
      pSerial->fd = fd;
      pSerial->state.uValueCnt = WACOMFIELD_MAX;

      /* remember what model was request */
      if (pModel)
            pSerial->modelRequested = *pModel;

      if (SerialReset(pSerial,pModel))
      {
            free(pSerial);
            return NULL;
      }

      return (WACOMTABLET)pSerial;
}

static int SerialReset(SERIALTABLET* pSerial, WACOMMODEL* pModel)
{
      SERIALVENDOR* pVendor = NULL;
      SERIALDEVICE* pDevice = NULL;
      SERIALSUBTYPE* pSubType = NULL;

      /* If model is specified, break it down into vendor, device, and subtype */
      if (pModel && SerialFindModel(pModel,&pVendor,&pDevice,&pSubType))
            return 1;

      /* Set the tablet device */
      if (SerialSetDevice(pSerial,pVendor,pDevice,pSubType))
            return 1;

      /* configure the TTY for initial operation */
      if (SerialConfigTTY(pSerial))
            return 1;

      /* Identify the tablet */
      if (!pSerial->pfnIdent || pSerial->pfnIdent(pSerial))
            return 1;

      /* Initialize the tablet */
      if (!pSerial->pfnInit || pSerial->pfnInit(pSerial))
            return 1;

      /* Send start */
      SerialSendStart(pSerial);

      return 0;
}

/*****************************************************************************
** Serial Tablet Functions
*****************************************************************************/

static int SerialConfigTTY(SERIALTABLET* pSerial)
{
      struct termios tios;
      int nBaudRate = 9600;

      /* configure tty */
      if (isatty(pSerial->fd))
      {
            /* set up default port parameters */
            if (tcgetattr (pSerial->fd, &tios))
            {
                  SerialError(pSerial,"Failed to get port params: %s",
                              strerror(errno));
                  return 1;
            }

            tios.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
            tios.c_oflag &= ~OPOST;
            tios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
            tios.c_cflag &= ~(CSIZE|PARENB);
            tios.c_cflag |= CS8|CLOCAL;
            tios.c_cflag &= ~(CSTOPB); /* 1 stop bit */
            tios.c_cflag &= ~(CSIZE); /* 8 data bits */
            tios.c_cflag |= CS8;
      tios.c_cflag &= ~(PARENB); /* no parity */
            tios.c_iflag |= IXOFF;        /* flow control XOff */
            tios.c_cc[VMIN] = 1;          /* vmin value */
            tios.c_cc[VTIME] = 0;         /* vtime value */

            if (tcsetattr (pSerial->fd, TCSANOW, &tios))
            {
                  SerialError(pSerial,"Failed to set port params: %s",
                              strerror(errno));
                  return 1;
            }

            /* get minumum baud rate for given device, if specified */
            if (pSerial->pDevice)
                  nBaudRate = pSerial->pDevice->nMinBaudRate;

            /* set 38400 baud and reset */
            if (SerialResetAtBaud(pSerial,&tios,38400))
                  return 1;

            /* if valid, set 19200 baud and reset */
            if ((nBaudRate <= 19200) && (SerialResetAtBaud(pSerial,&tios,19200)))
                  return 1;
            
            /* if valid, set 9600 baud and reset */
            if ((nBaudRate <= 9600) && (SerialResetAtBaud(pSerial,&tios,9600)))
                  return 1;

            /* lower than 9600 baud? for testing, maybe */
            if ((nBaudRate < 9600) && (SerialResetAtBaud(pSerial,&tios,nBaudRate)))
                  return 1;
      }
      else /* not tty */
      {
            if (SerialSendReset(pSerial)) return 1;
      }

      /* Send stop */
      if (SerialSendStop(pSerial) || WacomFlush(pSerial))
            return 1;

      return 0;
}


static int SerialSetDevice(SERIALTABLET* pSerial, SERIALVENDOR* pVendor,
            SERIALDEVICE* pDevice, SERIALSUBTYPE* pSubType)
{
      pSerial->pVendor = pVendor;
      pSerial->pDevice = pDevice;
      pSerial->pSubType = pSubType;

      /* if we know the device, use its functions */
      if (pSerial->pDevice)
      {
            pSerial->pfnIdent = pSerial->pDevice->pfnIdent;
            if (!pSerial->pfnIdent) { errno = EPERM; return 1; }
      }
      else
            pSerial->pfnIdent = SerialIdentDefault;

      return 0;
}

static int SerialIdentDefault(SERIALTABLET* pSerial)
{
      return SerialIdentWacom(pSerial);
}

static int SerialIdentWacom(SERIALTABLET* pSerial)
{
      char* pszPos;
      SERIALVENDOR* pVendor = &xWacomVendor;
      SERIALDEVICE* pDev;
      SERIALSUBTYPE* pSub;
      char chResp[64];

      /* send wacom identification request */
      if (SerialSendRequest(pSerial,"~#\r",chResp,sizeof(chResp)))
      {
            if (errno != ETIMEDOUT) return 1;

            /* try again, sometimes the first one gets garbled */
            if (SerialSendRequest(pSerial,"~#\r",chResp,sizeof(chResp)))
                  return 1;
      }

      /* look through device table for information */
      for (pDev=pVendor->pDevices; pDev->pszName; ++pDev)
      {
            for (pSub=pDev->pSubTypes; pSub && pSub->pszName; ++pSub)
            {
                  if (strncmp(chResp,pSub->pszIdent, strlen(pSub->pszIdent)) == 0)
                  {
                        pSerial->pVendor = pVendor;
                        pSerial->pDevice = pDev;
                        pSerial->pSubType = pSub;
                        pSerial->state.uValid = pDev->uCaps;
                        pSerial->uPacketLength = pDev->uPacketLength;
                        pSerial->pfnInit = pSub->pfnInit ?
                                    pSub->pfnInit : SerialInitWacom;
      
                        /* get version number */
                        pszPos = chResp;
                        while (*pszPos) ++pszPos;
                        while ((pszPos > chResp) && (pszPos[-1] != 'V')) --pszPos;
                        if (sscanf(pszPos,"%d.%d-%d",&pSerial->nVerMajor,
                                    &pSerial->nVerMinor,&pSerial->nVerRelease) != 3)
                        {
                              pSerial->nVerRelease = 0;
                              if (sscanf(pszPos,"%d.%d",&pSerial->nVerMajor,
                                    &pSerial->nVerMinor) != 2)
                              {
                                    errno = EINVAL;
                                    SerialError(pSerial,"bad version number: %s",pszPos);
                                    return 1;
                              }
                        }
                        return 0;
                  }
            }
      }

      SerialError(pSerial,"UNIDENTIFIED TABLET: %s",chResp);
      return 1;
}

static int SerialInitWacom(SERIALTABLET* pSerial)
{
      char chResp[32];

      /* Request tablet dimensions */
      if (SerialSendRequest(pSerial,"~C\r",chResp,sizeof(chResp)))
            return 1;

      /* parse position range */
      if (sscanf(chResp,"%d,%d",
                  &pSerial->state.values[WACOMFIELD_POSITION_X].nMax,
                  &pSerial->state.values[WACOMFIELD_POSITION_Y].nMax) != 2)
      {
            errno=EINVAL;
            SerialError(pSerial,"Bad dimension response [%s]",chResp);
            return 1;
      }

      /* tablet specific initialization */
      switch (pSerial->pDevice->uDevice)
      {
            case WACOMDEVICE_PENPARTNER:
                  /* pressure mode */
                  SerialSend(pSerial, "PH1\r");
                  break;

            case WACOMDEVICE_INTUOS:
            case WACOMDEVICE_INTUOS2:
                  /* multi-mode, max-rate */ 
                  SerialSend(pSerial, "MT1\rID1\rIT0\r");
      }
      
      if (pSerial->pDevice->nProtocol == PROTOCOL_4)
      {
            /* multi-mode (MU), upper-origin (OC), all-macro (M0),
             * no-macro1 (M1), max-rate (IT), no-inc (IN),
             * stream-mode (SR), Z-filter (ZF) */

/*          if (SerialSend(pSerial->fd, "MU1\rOC1\r~M0\r~M1\rIT0\rIN0\rSR\rZF1\r"))
                  return 1;
                  */

            if (pSerial->nVerMajor == 1)
            {
                  if (pSerial->nVerMinor >= 4)
                  {
                        /* enable tilt mode */
                        if (SerialSend(pSerial,"FM1\r")) return 1;

                        pSerial->pfnParse = SerialParseWacomIV_1_4;
                        pSerial->uPacketLength = 9;
                        pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255;
                        pSerial->state.values[WACOMFIELD_TILT_X].nMin = -64;
                        pSerial->state.values[WACOMFIELD_TILT_X].nMax = 63;
                        pSerial->state.values[WACOMFIELD_TILT_Y].nMin = -64;
                        pSerial->state.values[WACOMFIELD_TILT_Y].nMax = 63;
                  }
                  else if (pSerial->nVerMinor == 3)
                  {
                        pSerial->pfnParse = SerialParseWacomIV_1_3;
                        pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255;
                  }
                  else if (pSerial->nVerMinor == 2)
                  {
                        pSerial->pfnParse = SerialParseWacomIV_1_2;
                        pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255;
                  }
                  else if (pSerial->nVerMinor < 2)
                  {
                        pSerial->pfnParse = SerialParseWacomIV_1_2;
                        pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 120;
                  }
            }
      }
      else if (pSerial->pDevice->nProtocol == PROTOCOL_5)
      {
            pSerial->pfnParse = SerialParseWacomV;
            pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 1023;
            pSerial->state.values[WACOMFIELD_ABSWHEEL].nMax = 1023;
            pSerial->state.values[WACOMFIELD_ROTATION_Z].nMin = -900;
            pSerial->state.values[WACOMFIELD_ROTATION_Z].nMax = 899;
            pSerial->state.values[WACOMFIELD_THROTTLE].nMin = -1023;
            pSerial->state.values[WACOMFIELD_THROTTLE].nMax = 1023;
            pSerial->state.values[WACOMFIELD_TILT_X].nMin = -64;
            pSerial->state.values[WACOMFIELD_TILT_X].nMax = 63;
            pSerial->state.values[WACOMFIELD_TILT_Y].nMin = -64;
            pSerial->state.values[WACOMFIELD_TILT_Y].nMax = 63;
      }
      else { errno=EINVAL; return 1; }

      return 0;
}


static int SerialIdentAcerC100(SERIALTABLET* pSerial)
{
      /* sanity check */
      if ((pSerial->pVendor != &xAcerVendor) ||
            (pSerial->pDevice == NULL)) { return EPERM; return 1; }

      /* use first one */
      pSerial->pSubType = pSerial->pDevice->pSubTypes;

      /* sanity check again */
      if (pSerial->pSubType->pszName == NULL) { return EPERM; return 1; }

      pSerial->state.uValid = pSerial->pDevice->uCaps;
      pSerial->uPacketLength = pSerial->pDevice->uPacketLength;
      pSerial->pfnInit = pSerial->pSubType->pfnInit;
      pSerial->nVerMajor = 0;
      pSerial->nVerMinor = 0;
      pSerial->nVerRelease = 0;

      return 0;
}

static int SerialInitAcerC100(SERIALTABLET* pSerial)
{
      pSerial->pfnParse = SerialParseAcerC100;
      pSerial->state.values[WACOMFIELD_POSITION_X].nMax = 21136;
      pSerial->state.values[WACOMFIELD_POSITION_Y].nMax = 15900;
      pSerial->state.values[WACOMFIELD_PRESSURE].nMax = 255;
      return 0;
}

static int SerialParseWacomV(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState)
{
      int x=0, y=0, rot=0, tiltx=0, tilty=0, wheel=0,
                  tool=WACOMTOOLTYPE_NONE, button=0, press=0, throttle=0,
                  nButtonValue;

      /* Wacom V
       * Supports: 1024 pressure, eraser, 2 side-switch, tilt, throttle, wheel
       * Limitation: no tilt */

      if (uLength != 9) { errno=EINVAL; return 1; }

      /* in */
      if ((puchData[0] & 0xFC) == 0xC0)
      {
            int toolid = (((int)puchData[1]&0x7F) << 5) |
                        (((int)puchData[2]&0x7C) >> 2);

            int serial = ((((int)puchData[2] & 0x03) << 30) |
                      (((int)puchData[3] & 0x7f) << 23) |
                        (((int)puchData[4] & 0x7f) << 16) |
                        (((int)puchData[5] & 0x7f) << 9) |
                      (((int)puchData[6] & 0x7f) << 2) |
                  (((int)puchData[7] & 0x60) >> 5));

            switch (toolid)
            {
                  case 0x812: /* Intuos2 ink pen XP-110-00A */
                  case 0x012: /* Inking pen */
                        tool = WACOMTOOLTYPE_PENCIL; break;

                  case 0x822: /* Intuos Pen GP-300E-01H */
                  case 0x852: /* Intuos2 Grip Pen XP-501E-00A */
                  case 0x842: /* added from Cheng */
                  case 0x022:
                        tool = WACOMTOOLTYPE_PEN; break;

                  case 0x832: /* Intuos2 stroke pen XP-120-00A */
                  case 0x032: /* Stroke pen */
                        tool = WACOMTOOLTYPE_BRUSH; break;

                  case 0x007: /* 2D Mouse */
                  case 0x09C: /* ?? Mouse */
                  case 0x094: /* 4D Mouse */
                        tool = WACOMTOOLTYPE_MOUSE; break;

                  case 0x096: /* Lens cursor */
                        tool = WACOMTOOLTYPE_LENS; break;

                  case 0x82a:
                  case 0x85a:
                  case 0x91a:
                  case 0x0fa: /* Eraser */
                        tool = WACOMTOOLTYPE_ERASER; break;

                  case 0x112: /* Airbrush */
                        tool = WACOMTOOLTYPE_AIRBRUSH; break;

                  default: /* Unknown tool */
                        tool = WACOMTOOLTYPE_PEN; break;
            }
                  
            pSerial->nToolID = toolid;
            pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = 1;
            pSerial->state.values[WACOMFIELD_SERIAL].nValue = serial;
            pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool;
            return pState ? WacomCopyState(pState,&pSerial->state) : 0;
      }

      /* out */
      if ((puchData[0] & 0xFE) == 0x80)
      {
            pSerial->nToolID = 0;
            memset(&pSerial->state.values, 0,
                        pSerial->state.uValueCnt * sizeof(WACOMVALUE));
            return pState ? WacomCopyState(pState,&pSerial->state) : 0;
      }

      /* pen data */
      if (((puchData[0] & 0xB8) == 0xA0) || ((puchData[0] & 0xBE) == 0xB4))
      {
            x = ((((int)puchData[1] & 0x7f) << 9) |
                        (((int)puchData[2] & 0x7f) << 2) |
                        (((int)puchData[3] & 0x60) >> 5));
            y = ((((int)puchData[3] & 0x1f) << 11) |
                        (((int)puchData[4] & 0x7f) << 4) |
                        (((int)puchData[5] & 0x78) >> 3));
            tiltx = (puchData[7] & 0x3F);
            tilty = (puchData[8] & 0x3F);
            if (puchData[7] & 0x40) tiltx -= 0x40;
            if (puchData[8] & 0x40) tilty -= 0x40;

            /* pen packet */
            if ((puchData[0] & 0xB8) == 0xA0)
            {
                  press = ((((int)puchData[5] & 0x07) << 7) | ((int)puchData[6] & 0x7f));
                  button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0;
                  button |= (puchData[0] & 0x02) ? BIT(WACOMBUTTON_STYLUS) : 0;
                  button |= (puchData[0] & 0x04) ? BIT(WACOMBUTTON_STYLUS2) : 0;
            }

            /* 2nd airbrush packet */
            else
            {
                  wheel = ((((int)puchData[5] & 0x07) << 7) |
                              ((int)puchData[6] & 0x7f));
            }

            pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
            pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
            pSerial->state.values[WACOMFIELD_TILT_X].nValue = tiltx;
            pSerial->state.values[WACOMFIELD_TILT_Y].nValue = tilty;
            pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press;
            pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button;
            pSerial->state.values[WACOMFIELD_ABSWHEEL].nValue = wheel;
            return pState ? WacomCopyState(pState,&pSerial->state) : 0;
      }

      /* mouse packet */
      if (((puchData[0] & 0xBE) == 0xA8) || ((puchData[0] & 0xBE) == 0xB0))
      {
            x = ((((int)puchData[1] & 0x7f) << 9) |
                        (((int)puchData[2] & 0x7f) << 2) |
                        (((int)puchData[3] & 0x60) >> 5));
            y = ((((int)puchData[3] & 0x1f) << 11) |
                        (((int)puchData[4] & 0x7f) << 4) |
                        (((int)puchData[5] & 0x78) >> 3));
            throttle = ((((int)puchData[5] & 0x07) << 7) | (puchData[6] & 0x7f));
            if (puchData[8] & 0x08) throttle = -throttle;

            /* 4D mouse */
            if (pSerial->nToolID == 0x094)
            {
                  button = (((puchData[8] & 0x70) >> 1) | (puchData[8] & 0x07));
            }
            /* lens cursor */
            else if (pSerial->nToolID == 0x096)
            {
                  button = puchData[8] & 0x1F;
            }
            /* 2D mouse */
            else
            {
                  button = (puchData[8] & 0x1C) >> 2;
                  wheel = - (puchData[8] & 1) + ((puchData[8] & 2) >> 1);
            }

            pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
            pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
            pSerial->state.values[WACOMFIELD_RELWHEEL].nValue = wheel;
            pSerial->state.values[WACOMFIELD_THROTTLE].nValue = throttle;

            /* button values */
            nButtonValue = pSerial->state.values[WACOMFIELD_BUTTONS].nValue &
                        ~(BIT(WACOMBUTTON_LEFT) |
                        BIT(WACOMBUTTON_RIGHT) | BIT(WACOMBUTTON_MIDDLE) |
                        BIT(WACOMBUTTON_EXTRA) | BIT(WACOMBUTTON_SIDE));
            if (button & 1) nButtonValue |= BIT(WACOMBUTTON_LEFT);
            if (button & 2) nButtonValue |= BIT(WACOMBUTTON_MIDDLE);
            if (button & 4) nButtonValue |= BIT(WACOMBUTTON_RIGHT);
            if (button & 8) nButtonValue |= BIT(WACOMBUTTON_EXTRA);
            if (button & 16) nButtonValue |= BIT(WACOMBUTTON_SIDE);
            pSerial->state.values[WACOMFIELD_BUTTONS].nValue = nButtonValue;

            return pState ? WacomCopyState(pState,&pSerial->state) : 0;
      }

      /* 2nd 4D mouse packet */
      if ((puchData[0] & 0xBE) == 0xAA)
      {
            x = ((((int)puchData[1] & 0x7f) << 9) |
                        (((int)puchData[2] & 0x7f) << 2) |
                        (((int)puchData[3] & 0x60) >> 5));
            y = ((((int)puchData[3] & 0x1f) << 11) |
                        (((int)puchData[4] & 0x7f) << 4) |
                        (((int)puchData[5] & 0x78) >> 3));
            rot = ((((int)puchData[6] & 0x0f) << 7) |
                        ((int)puchData[7] & 0x7f));

            /* FIX ROT */
            if (rot < 900) rot = -rot;
            else rot = 1799 - rot;

            pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
            pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
            pSerial->state.values[WACOMFIELD_ROTATION_Z].nValue = rot;
            return pState ? WacomCopyState(pState,&pSerial->state) : 0;
      }

      errno = EINVAL;
      return 1;
}

static int SerialParseWacomIV_1_4(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState)
{
      /* Wacom IV, Rom 1.4
       * Supports: 256 pressure, eraser, 2 side-switch, tilt */

      if ((uLength != 7) && (uLength != 9)) { errno=EINVAL; return 1; }

      if (SerialParseWacomIV_1_3(pSerial,puchData,7,pState))
            return 1;

      /* tilt mode */
      if (uLength == 9)
      {
            int tiltx, tilty;

            tiltx = puchData[7] & 0x3F;
            tilty = puchData[8] & 0x3F;
            if (puchData[7] & 0x40) tiltx -= 64;
            if (puchData[8] & 0x40) tilty -= 64;

            pSerial->state.values[WACOMFIELD_TILT_X].nValue = tiltx;
            pSerial->state.values[WACOMFIELD_TILT_Y].nValue = tilty;
            
            if (pState)
            {
                  pState->values[WACOMFIELD_TILT_X].nValue = tiltx;
                  pState->values[WACOMFIELD_TILT_Y].nValue = tilty;
            }
      }

      return 0;
}

static int SerialParseWacomIV_1_3(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState)
{
      int x=0, y=0, prox=0, tool=WACOMTOOLTYPE_NONE,
                  button=0, press=0, stylus, eraser;

      /* Wacom IV, Rom 1.3 (ArtPadII)
       * Supports: 256 pressure, eraser, 2 side-switch
       * Limitation: no tilt */

      if (uLength != 7) { errno=EINVAL; return 1; }

      prox = puchData[0] & 0x40 ? 1 : 0;
      if (prox)
      {
            stylus = puchData[0] & 0x20 ? 1 : 0;
            press = (puchData[6] & 0x3F) << 1 | ((puchData[3] & 0x4) >> 2);
            press |= (puchData[6] & 0x40) ? 0 : 0x80;
            eraser = (puchData[3] & 0x20) ? 1 : 0;

            if (stylus)
            {
                  /* if entering proximity, choose eraser or stylus2 for bit */
                  if (pSerial->state.values[WACOMFIELD_PROXIMITY].nValue == 0)
                  {
                        if (eraser) tool = WACOMTOOLTYPE_ERASER;
                        else tool = WACOMTOOLTYPE_PEN;
                  }

                  /* otherwise, keep the last tool */
                  else tool = pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue;
                  
                  button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0;

                  /* pen has 2 side-switch, eraser has none */
                  if (tool == WACOMTOOLTYPE_PEN)
                  {
                        button |= (puchData[3] & 0x10) ?
                                    BIT(WACOMBUTTON_STYLUS) : 0;
                        button |= (eraser) ? BIT(WACOMBUTTON_STYLUS2) : 0;
                  }
            }
            else
            {
                  tool = WACOMTOOLTYPE_MOUSE;
                  button = (puchData[3] & 0x78) >> 3; /* not tested */
            }

            x = puchData[2] | ((int)puchData[1] << 7) |
                        (((int)puchData[0] & 0x3) << 14);
            y = puchData[5] | ((int)puchData[4] << 7) |
                        (((int)puchData[3] & 0x3) << 14);
      }

      /* set valid fields */
      pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = prox;
      pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool;
      pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
      pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
      pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press;
      pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button;

      return pState ? WacomCopyState(pState,&pSerial->state) : 0;
}

static int SerialParseWacomIV_1_2(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState)
{
      int x=0, y=0, prox=0, tool=0, button=WACOMTOOLTYPE_NONE,
                  press=0, stylus;

      /* Wacom IV, Rom 1.2, 1.1, and 1.0
       * Supports: 256 pressure (120 for 1.1 and 1.0), multi-mode
       * Limitation: no stylus2, no tilt, no eraser */

      if (uLength != 7) { errno=EINVAL; return 1; }

      prox = puchData[0] & 0x40 ? 1 : 0;
      if (prox)
      {
            stylus = puchData[0] & 0x20 ? 1 : 0;
            if (pSerial->nVerMinor == 2)
                  press = (puchData[6] & 0x3F) << 1 | ((puchData[3] & 0x4) >> 2) |
                              (puchData[6] & 0x40) ? 0 : 0x80;
            else
                  press = (puchData[6] & 0x3F) + (puchData[6] & 0x40) ? 0 : 64;

            if (stylus)
            {
                  tool = WACOMTOOLTYPE_PEN;
                  button = (press > 10) ? BIT(WACOMBUTTON_TOUCH) : 0;
                  button |= (puchData[3] & 0x10) ? BIT(WACOMBUTTON_STYLUS) : 0;
            }
            else
            {
                  tool = WACOMTOOLTYPE_MOUSE;
                  button = (puchData[3] & 0x78) >> 3; /* not tested */
            }

            x = puchData[2] | ((int)puchData[1] << 7) |
                        (((int)puchData[0] & 0x3) << 14);
            y = puchData[5] | ((int)puchData[4] << 7) |
                        (((int)puchData[3] & 0x3) << 14);
      }

      /* set valid fields */
      pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = prox;
      pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool;
      pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
      pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
      pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press;
      pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button;

      return pState ? WacomCopyState(pState,&pSerial->state) : 0;
}

static int SerialParseAcerC100(SERIALTABLET* pSerial,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState)
{
      int x=0, y=0, prox=0, tool=WACOMTOOLTYPE_NONE,
                  button=0, press=0, eraser;

      /* Acer C100 Tablet PC (reverse-engineered)
       * Supports: 256 pressure, eraser, 1 side-switch
       * Limitation: no tilt (is all zeros, planned?)*/

      if (uLength != 9) { errno=EINVAL; return 1; }

      prox = puchData[0] & 0x20 ? 1 : 0;
      if (prox)
      {
            eraser = (puchData[0] & 0x04) ? 1 : 0;
            press = ((puchData[6] & 0x01) << 7) | (puchData[5] & 0x7F);

            /* tools are distinguishable */
            if (eraser) tool = WACOMTOOLTYPE_ERASER;
            else tool = WACOMTOOLTYPE_PEN;
            
            button = (puchData[0] & 0x01) ? BIT(WACOMBUTTON_TOUCH) : 0;

            /* pen has 1 side-switch, eraser has none */
            if (tool == WACOMTOOLTYPE_PEN)
            {
                  button |= (puchData[0] & 0x02) ?
                              BIT(WACOMBUTTON_STYLUS) : 0;
            }

            x = (((int)puchData[6] & 0x60) >> 5) |
                  ((int)puchData[2] << 2) |
                  ((int)puchData[1] << 9);
            y = (((int)puchData[6] & 0x18) >> 3) |
                  ((int)puchData[4] << 2) |
                  ((int)puchData[3] << 9);
      }

      /* set valid fields */
      pSerial->state.values[WACOMFIELD_PROXIMITY].nValue = prox;
      pSerial->state.values[WACOMFIELD_TOOLTYPE].nValue = tool;
      pSerial->state.values[WACOMFIELD_POSITION_X].nValue = x;
      pSerial->state.values[WACOMFIELD_POSITION_Y].nValue = y;
      pSerial->state.values[WACOMFIELD_PRESSURE].nValue = press;
      pSerial->state.values[WACOMFIELD_BUTTONS].nValue = button;

      return pState ? WacomCopyState(pState,&pSerial->state) : 0;
}



/*****************************************************************************
** Internal Functions
*****************************************************************************/

static int SerialSendReset(SERIALTABLET* pSerial)
{
      SerialInfo(pSerial,"Sending reset");

      /* reset to Wacom II-S command set, and factory defaults */
      if (SerialSend(pSerial,"\r$\r")) return 1;
      usleep(250000); /* 250 milliseconds */

      /* reset tablet to Wacom IV command set */
      if (SerialSend(pSerial,"#\r")) return 1;
      usleep(75000); /* 75 milliseconds */

      return 0;
}

static int SerialSendStop(SERIALTABLET* pSerial)
{
      if (SerialSend(pSerial,"\rSP\r")) return 1;
      usleep(100000);
      return 0;
}

static int SerialSendStart(SERIALTABLET* pSerial)
{
      return SerialSend(pSerial,"ST\r");
}
 
static int SerialSend(SERIALTABLET* pSerial, const char* pszMsg)
{
      return SerialSendRaw(pSerial,pszMsg,(unsigned)strlen(pszMsg));
}

static int SerialSendRaw(SERIALTABLET* pSerial, const void* pvData,
            unsigned int uSize)
{
      int nXfer;
      unsigned int uCnt=0;
      unsigned char* puchData = (unsigned char*)pvData;

      while (uCnt < uSize)
      {
            nXfer = write(pSerial->fd,puchData+uCnt,uSize-uCnt);
            if (nXfer <= 0)
            {
                  SerialError(pSerial,"Failed to write to port: %s",strerror(errno));
                  return 1;
            }
            uCnt += nXfer;
      }

      return 0;
}

static int WacomFlush(SERIALTABLET* pSerial)
{
      char ch[16];
      fd_set fdsRead;
      struct timeval timeout;

      if (tcflush(pSerial->fd, TCIFLUSH) == 0)
            return 0;

      timeout.tv_sec = 0;
      timeout.tv_usec = 0;

      while (1)
      {
            FD_ZERO(&fdsRead);
            FD_SET(pSerial->fd, &fdsRead);
            if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0)
                  break;
            read(pSerial->fd,&ch,sizeof(ch));
      }

      return 0;
}

static int SerialSendRequest(SERIALTABLET* pSerial, const char* pszRequest,
            char* pchResponse, unsigned int uSize)
{
      int nXfer;
      fd_set fdsRead;
      unsigned int uLen, uCnt;
      struct timeval timeout;

      uLen = strlen(pszRequest);
      if (SerialSendRaw(pSerial,pszRequest,uLen)) return 1;
      --uLen;

      if (uSize < uLen)
      {
            errno=EINVAL;
            SerialError(pSerial,"Invalid size to SerialSendRequest: %u < %u",
                        uSize,uLen);
            return 1;
      }

      /* read until first header character */
      while (1)
      {
            timeout.tv_sec = 0;
            timeout.tv_usec = 500000;

            FD_ZERO(&fdsRead);
            FD_SET(pSerial->fd, &fdsRead);
            if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0)
            {
                  errno = ETIMEDOUT;
                  return 1;
            }

            nXfer = read(pSerial->fd,pchResponse,1);
            if (nXfer <= 0)
            {
                  SerialError(pSerial,"Truncated response header");
                  return 1;
            }
            if (*pchResponse == *pszRequest) break;
            SerialWarn(pSerial,"Discarding %02X", *((unsigned char*)pchResponse));
      }

      /* read response header */
      for (uCnt=1; uCnt<uLen; uCnt+=nXfer)
      {
            nXfer = read(pSerial->fd,pchResponse+uCnt,uLen-uCnt);
            if (nXfer <= 0)
            {
                  SerialError(pSerial,"Truncated response header (2)");
                  return 1;
            }
      }

      /* check the header */
      if (strncmp(pszRequest,pchResponse,uLen) != 0)
      {
            SerialError(pSerial,"Incorrect response [%s,%s]",
                        pszRequest,pchResponse);
            return 1;
      }

      /* get the rest of the response */
      for (uCnt=0; uCnt<uSize; ++uCnt)
      {
            nXfer = read(pSerial->fd,pchResponse+uCnt,1);
            if (nXfer <= 0)
            {
                  SerialError(pSerial,"Failed to read response: %s",strerror(errno));
                  return 1;
            }

            /* stop on CR */
            if (pchResponse[uCnt] == '\r')
            {
                  pchResponse[uCnt] = '\0';
                  return 0;
            }
      }

      errno = EINVAL;
      SerialError(pSerial,"Invalid response");
      return 1;
}

/*****************************************************************************
** Virtual Functions
*****************************************************************************/

static void SerialClose(WACOMTABLET_PRIV* pTablet)
{
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      close(pSerial->fd);
      free(pSerial);
}

static WACOMMODEL SerialGetModel(WACOMTABLET_PRIV* pTablet)
{
      WACOMMODEL model = { 0 };
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      model.uClass = WACOMCLASS_SERIAL;
      model.uVendor = pSerial->pVendor->uVendor;
      model.uDevice = pSerial->pDevice->uDevice;
      model.uSubType = pSerial->pSubType->uSubType;
      return model;
}

static const char* SerialGetVendorName(WACOMTABLET_PRIV* pTablet)
{
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      return pSerial->pVendor->pszDesc;
}

static const char* SerialGetClassName(WACOMTABLET_PRIV* pTablet)
{
      return "Serial";
}

static const char* SerialGetDeviceName(WACOMTABLET_PRIV* pTablet)
{
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      return pSerial->pDevice->pszDesc;
}

static const char* SerialGetSubTypeName(WACOMTABLET_PRIV* pTablet)
{
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      return pSerial->pSubType->pszName;
}

static const char* SerialGetModelName(WACOMTABLET_PRIV* pTablet)
{
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      return pSerial->pSubType->pszDesc;
}

static int SerialGetROMVer(WACOMTABLET_PRIV* pTablet, int* pnMajor,
            int* pnMinor, int* pnRelease)
{
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      if (!pnMajor) { errno=EINVAL; return 1; }
      *pnMajor = pSerial->nVerMajor;
      if (pnMinor) *pnMinor = pSerial->nVerMinor;
      if (pnRelease) *pnRelease = pSerial->nVerRelease;
      return 0;
}

static int SerialGetCaps(WACOMTABLET_PRIV* pTablet)
{
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      return pSerial->pDevice->uCaps;
}

static int SerialGetState(WACOMTABLET_PRIV* pTablet, WACOMSTATE* pState)
{
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      return WacomCopyState(pState,&pSerial->state);
}

static int SerialGetFD(WACOMTABLET_PRIV* pTablet)
{
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      return pSerial->fd;
}

static int SerialReadRaw(WACOMTABLET_PRIV* pTablet, unsigned char* puchData,
            unsigned int uSize)
{
      int nXfer;
      fd_set fdsRead;
      unsigned char* pPos, *pEnd;
      struct timeval timeout;
      unsigned int uCnt, uPacketLength;
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;

      if (!pSerial) { errno=EBADF; return 0; }
      uPacketLength = pSerial->uPacketLength;

      /* check size of buffer */
      if (uSize < uPacketLength) { errno=EINVAL; return 0; }

      /* check for errors, reset after 3 */
      if (pSerial->nBitErrors > 3)
      {
            pSerial->nBitErrors = 0;
            SerialWarn(pSerial,"Resetting tablet due to bit errors");
            (void)SerialReset(pSerial,&pSerial->modelRequested);
      }

      timeout.tv_sec = 0;
      timeout.tv_usec = 500000; /* 0.5 seconds for now */

      for (uCnt=0; uCnt<uPacketLength; uCnt+=nXfer)
      {
            FD_ZERO(&fdsRead);
            FD_SET(pSerial->fd, &fdsRead);
            if (select(FD_SETSIZE,&fdsRead,NULL,NULL,&timeout) <= 0)
                  break;

            nXfer = read(pSerial->fd,puchData+uCnt,uPacketLength-uCnt);
            if (nXfer <= 0) return nXfer;

            /* look for high-bit */
            pEnd = puchData + nXfer;
            for (pPos=puchData; pPos<pEnd; ++pPos)
            {
                  if (*pPos & 0x80) break;
            }

            /* not where it was expected? fix it. */
            if (pPos != puchData) 
            {
                  ++pSerial->nBitErrors;
                  SerialWarn(pSerial,"Bad high bit, discarding %d bytes",
                        pPos - puchData);

                  /* copy remaining bytes down, if any */
                  memmove(puchData,pPos,(nXfer - (pPos-puchData)));
                  nXfer -= pPos - puchData;
            }
      }

      if (uCnt < uPacketLength)
      {
            errno = ETIMEDOUT;
            return -1;
      }

      return (signed)uCnt;
}

static int SerialParseData(WACOMTABLET_PRIV* pTablet,
            const unsigned char* puchData, unsigned int uLength,
            WACOMSTATE* pState)
{
      int i;
      SERIALTABLET* pSerial = (SERIALTABLET*)pTablet;
      if (!pSerial) { errno=EBADF; return 1; }

      /* check synchronization */
      if (!(puchData[0] & 0x80))
      {
            ++pSerial->nBitErrors;
            SerialError(pSerial,"HIBIT FAIL");
            errno=EINVAL;
            return 1;
      }
      for (i=1; i<uLength; ++i)
      {
            if (puchData[i] & 0x80)
            {
                  ++pSerial->nBitErrors;
                  SerialError(pSerial,"LOBIT FAIL");
                  SerialDump(pSerial,puchData,uLength);
                  errno=EINVAL;
                  return 1;
            }
      }

      /* reset bit error count */
      pSerial->nBitErrors = 0;

      /* dispatch to parser */
      if (pSerial->pfnParse)
            return (*pSerial->pfnParse)(pSerial,puchData,uLength,pState);

      errno=EINVAL;
      return 1;
}

static int SerialResetAtBaud(SERIALTABLET* pSerial, struct termios* pTIOS,
            int nBaud)
{
      /* conver baud rate to tios macro */
      int baudRate = B9600;
      switch (nBaud)
      {
            case 38400: baudRate = B38400; break;
            case 19200: baudRate = B19200; break;
            case 9600: baudRate = B9600; break;
            case 4800: baudRate = B4800; break; /* for testing, maybe */
            case 2400: baudRate = B2400; break; /* for testing, maybe */
            case 1200: baudRate = B1200; break; /* for testing, maybe */
      }

      SerialInfo(pSerial,"Setting baud rate to %d",nBaud);

      /* change baud rate */
      cfsetispeed(pTIOS, baudRate);
      cfsetospeed(pTIOS, baudRate);
      if (tcsetattr (pSerial->fd, TCSANOW, pTIOS))
            return 1;

      /* send reset command */
      return SerialSendReset(pSerial);
}

/*****************************************************************************
** Log Functions
*****************************************************************************/

#define SERIALLOG(l) do { \
      va_list a; \
      va_start(a,pszFmt); \
      WacomLogV(pSerial->hEngine,l,pszFmt,a); \
      va_end(a); } while (0)

static void SerialError(SERIALTABLET* pSerial, const char* pszFmt, ...)
      { SERIALLOG(WACOMLOGLEVEL_ERROR); }
static void SerialWarn(SERIALTABLET* pSerial, const char* pszFmt, ...)
      { SERIALLOG(WACOMLOGLEVEL_WARN); }
static void SerialInfo(SERIALTABLET* pSerial, const char* pszFmt, ...)
      { SERIALLOG(WACOMLOGLEVEL_INFO); }

static void SerialDump(SERIALTABLET* pSerial, const void* pvData, int nCnt)
{
      int i;
      const unsigned char* pData = (const unsigned char*)pvData;
      char chLine[80];
      unsigned int uAddr = 0;
      int nPos = 0;

      while (nCnt > 0)
      {
            nPos = 0;
            for (i=0; i<16; ++i)
            {
                  if (i < nCnt)
                        nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos,
                                    "%02X",pData[i]);
                  else
                        nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos,
                                    "  ");
            }
            nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos," - ");
            for (i=0; i<16; ++i)
            {
                  if (i < nCnt)
                        nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos,
                                    "%c",isprint(pData[i]) ? pData[i] : '.');
                  else
                        nPos += snprintf(chLine+nPos,sizeof(chLine)-nPos,
                                    " ");
            }

            WacomLog(pSerial->hEngine,WACOMLOGLEVEL_DEBUG,"%04X: %s",uAddr,chLine);
            uAddr += 16;
            nCnt -= 16;
            pData += 16;
      }
}

/* NOT USED, YET
static void SerialCritical(SERIALTABLET* pSerial, const char* pszFmt, ...)
      { SERIALLOG(WACOMLOGLEVEL_CRITICAL); }
static void SerialDebug(SERIALTABLET* pSerial, const char* pszFmt, ...)
      { SERIALLOG(WACOMLOGLEVEL_DEBUG); }
static void SerialTrace(SERIALTABLET* pSerial, const char* pszFmt, ...)
      { SERIALLOG(WACOMLOGLEVEL_TRACE); }
*/

Generated by  Doxygen 1.6.0   Back to index