linux/drivers/staging/ced1401/userspace/use1401.c
<<
>>
Prefs
   1/****************************************************************************
   2** use1401.c
   3** Copyright (C) Cambridge Electronic Design Ltd, 1992-2010
   4**
   5** This program is free software; you can redistribute it and/or
   6** modify it under the terms of the GNU General Public License
   7** as published by the Free Software Foundation; either version 2
   8** of the License, or (at your option) any later version.
   9**
  10** This program is distributed in the hope that it will be useful,
  11** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13** GNU General Public License for more details.
  14**
  15** You should have received a copy of the GNU General Public License
  16** along with this program; if not, write to the Free Software
  17** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  18**
  19** Contact CED: Cambridge Electronic Design Limited, Science Park, Milton Road
  20**              Cambridge, CB6 0FE.
  21**              www.ced.co.uk
  22**              greg@ced.co.uk
  23**
  24**  Title:      USE1401.C
  25**  Version:    4.00
  26**  Author:     Paul Cox, Tim Bergel, Greg Smith
  27**
  28** The code was vigorously pruned in DEC 2010 to remove the macintosh options
  29** and to get rid of the 16-bit support. It has also been aligned with the
  30** Linux version. See CVS for revisions. This will work for Win 9x onwards.
  31****************************************************************************
  32**
  33** Notes on Windows interface to driver
  34** ************************************
  35**
  36** Under Windows 9x and NT, Use1401 uses DeviceIoControl to get access to
  37** the 1401 driver. This has parameters for the device handle, the function
  38** code, an input pointer and byte count, an output pointer and byte count
  39** and a pointer to a DWORD to hold the output byte count. Note that input
  40** and output are from the point-of-view of the driver, so the output stuff
  41** is used to read values from the 1401, not send to the 1401. The use of
  42** these parameters varies with the function in use and the operating
  43** system; there are five separate DIOC calls SendString, GetString and
  44** SetTransferArea all have their own specialised calls, the rest use the
  45** Status1401 or Control1401 functions.
  46**
  47** There are two basic styles of DIOC call used, one for Win9x VxD drivers
  48** and one for NT Kernel-mode and WDM drivers (see below for tables showing
  49** the different parameters used. The array bUseNTDIOC[] selects between
  50** these two calling styles.
  51**
  52** Function codes
  53** In Win3.x, simple function codes from 0 to 40 were used, shifted left 8
  54** bits with a sub-function code in the lower 8 bits. These were also used
  55** in the Windows 95 driver, though we had to add 1 to the code value to
  56** avoid problems (Open from CreateFile is zero), and the sub-function code
  57** is now unused. We found that this gave some problems with Windows 98
  58** as the function code values are reserved by microsoft, so we switched to
  59** using the NT function codes instead. The NT codes are generated using the
  60** CTL_CODE macro, essentially this gives 0x80012000 | (func << 2), where
  61** func is the original 0 to 34 value. The driver will handle both types of
  62** code and Use1432 only uses the NT codes if it knows the driver is new
  63** enough. The array bUseNTCodes[] holds flags on the type of codes required.
  64** GPS/TDB Dec 2010: we removed the bUseNTCodes array as this is always true
  65** as we no longer support ancient versions.
  66**
  67** The CreateFile and CloseFile function calls are also handled
  68** by DIOC, using the special function codes 0 and -1 respectively.
  69**
  70** Input pointer and buffer size
  71** These are intended for data sent to the device driver. In nearly all cases
  72** they are unused in calls to the Win95 driver, the NT driver uses them
  73** for all information sent to the driver. The table below shows the pointer
  74** and byte count used for the various calls:
  75**
  76**                      Win 95                  Win NT
  77** SendString           NULL, 0                 pStr, nStr
  78** GetString            NULL, 0                 NULL, 0
  79** SetTransferArea      pBuf, nBuf (unused?)    pDesc, nDesc
  80** GetTransfer          NULL, 0                 NULL, 0
  81** Status1401           NULL, 0                 NULL, 0
  82** Control1401          NULL, 0                 pBlk, nBlk
  83**
  84** pStr and nStr are pointers to a char buffer and the buffer length for
  85** string I/O, note that these are temporary buffers owned by the DLL, not
  86** application memory, pBuf and nBuf are the transfer area buffer (I think
  87** these are unused), pDesc and nDesc are the TRANSFERDESC structure, pBlk
  88** and nBlk are the TCSBLOCK structure.
  89**
  90**
  91** Output pointer and buffer size
  92** These are intended for data read from the device driver. These are used
  93** for almost all information sent to the Win95 driver, the NT driver uses
  94** them for information read from the driver, chiefly the error code. The
  95** table below shows the pointer and byte count used for the various calls:
  96**
  97**                      Win 95                  Win NT
  98** SendString           pStr, nStr              pPar, nPar
  99** GetString            pStr, nStr+2            pStr, nStr+2
 100** SetTransferArea      pDesc, nDesc            pPar, nPar
 101** GetTransfer          pGet, nGet              pGet, nGet
 102** Status1401           pBlk, nBlk              pPar, nPar
 103** Control1401          pBlk, nBlk              pPar, nPar
 104**
 105** pStr and nStr are pointers to a char buffer and the buffer length for
 106** string I/O, the +2 for GetString refers to two spare bytes at the start
 107** used to hold the string length and returning an error code for NT. Note
 108** again that these are (and must be) DLL-owned temporary buffers. pPar
 109** and nPar are a PARAM structure used in NT (it holds an error code and a 
 110** TCSBLOCK structure). pDesc and nDesc are the VXTRANSFERDESC structure,
 111** pBlk and nBlk are the TCSBLOCK structure. pGet and nGet indicate the
 112** TGET_TX_BLOCK structure used for GetTransfer.
 113**
 114**
 115** The output byte count
 116** Both drivers return the output buffer size here, regardless of the actual
 117** bytes output. This is used to check that we did get through to the driver.
 118**
 119** Multiple 1401s
 120** **************
 121**
 122** We have code that tries to support the use of multiple 1401s, but there
 123** are problems: The lDriverVersion and lDriverType variables are global, not
 124** per-1401 (a particular problem as the U14 functions that use them don't
 125** have a hand parameter). In addition, the mechansim for finding a free
 126** 1401 depends upon the 1401 device driver open operation failing if it's
 127** already in use, which doesn't always happen, particularly with the VxDs.
 128** The code in TryToOpen tries to fix this by relying on TYPEOF1401 to detect
 129** the 1401-in-use state - the VxDs contain special code to help this. This is
 130** working OK but multiple 1401 support works better with the Win2000 drivers.
 131**
 132** USB driver
 133** **********
 134**
 135** The USB driver, which runs on both Win98 and NT2000, uses the NT-style
 136** calling convention, both for the DIOC codes and the DIOC parameters. The
 137** TryToOpen function has been altered to look for an NT driver first in
 138** the appropriate circumstances, and to set the driver DIOC flags up in
 139** the correct state.
 140**
 141** Adding a new 1401 type - now almost nothing to do
 142** *************************************************
 143**
 144** The 1401 types are defined by a set of U14TYPExxxx codes in USE1401.H.
 145** You should add a new one of these to keep things tidy for applications.
 146**
 147** DRIVERET_MAX (below) specifies the maximum allowed type code from the
 148** 1401 driver; I have set this high to accommodate as yet undesigned 1401
 149** types. Similarly, as long as the command file names follow the ARM,
 150** ARN, ARO sequence, these are calculated by the ExtForType function, so
 151** you don't need to do anything here either.
 152**
 153** Version number
 154** **************
 155** The new U14InitLib() function returns 0 if the OS is incapable of use,
 156** otherwise is returns the version of the USE1401 library. This is done
 157** in three parts: Major(31-24).Minor(23-16).Revision.(15-0) (brackets are
 158** the bits used). The Major number starts at 2 for the first revision with
 159** the U14InitLib() function. Changes to the Major version means that we
 160** have broken backwards compatibility. Minor number changes mean that we
 161** have added new functionality that does not break backwards compatibility.
 162** we starts at 0. Revision changes mean we have fixed something. Each index
 163** returns to 0 when a higher one changes.
 164*/
 165#define U14LIB_MAJOR 4
 166#define U14LIB_MINOR 0
 167#define U14LIB_REVISION 0
 168#define U14LIB_VERSION ((U14LIB_MAJOR<<24) | (U14LIB_MINOR<<16) | U14LIB_REVISION)
 169
 170#include <stdlib.h>
 171#include <string.h>
 172#include <stdio.h>
 173
 174#include "USE1401.H"
 175
 176#ifdef _IS_WINDOWS_
 177#include <io.h>
 178#include <windows.h>
 179#pragma warning(disable: 4100) /* Disable "Unused formal parameter" warning */
 180#include <assert.h>
 181#include "process.h"
 182
 183
 184#define sprintf wsprintf
 185#define PATHSEP '\\'
 186#define PATHSEPSTR "\\"
 187#define DEFCMDPATH "\\1401\\"   // default command path if all else fails
 188#define MINDRIVERMAJREV 1       // minimum driver revision level we need
 189#define __packed                // does nothing in Windows
 190
 191#include "use14_ioc.h"          // links to device driver stuff
 192#endif
 193
 194#ifdef LINUX
 195#include <fcntl.h>
 196#include <unistd.h>
 197#include <sys/ioctl.h>
 198#include <errno.h>
 199#include <sys/time.h>
 200#include <sched.h>
 201#include <libgen.h>
 202#define PATHSEP '/'
 203#define PATHSEPSTR "/"
 204#define DEFCMDPATH "/var/1401/" // default command path if all else fails
 205#define MINDRIVERMAJREV 2       // minimum driver revision level we need
 206
 207#include "ced_ioctl.h"          // links to device driver stuff
 208#endif
 209
 210#define MAX1401         8       // The number of 1401s that can be supported
 211
 212/*
 213** These are the 1401 type codes returned by the driver, they are a slightly
 214** odd sequence & start for reasons of compatibility with the DOS driver.
 215** The maximum code value is the upper limit of 1401 device types.
 216*/
 217#define DRIVRET_STD     4       // Codes for 1401 types matching driver values
 218#define DRIVRET_U1401   5       // This table does not need extending, as
 219#define DRIVRET_PLUS    6       // we can calculate values now.
 220#define DRIVRET_POWER   7       // but we need all of these values still
 221#define DRIVRET_MAX     26      // Maximum tolerated code - future designs
 222
 223/*
 224** These variables store data that will be used to generate the last
 225** error string. For now, a string will hold the 1401 command file name.
 226*/
 227static char szLastName[20];     // additional text information
 228
 229/*
 230** Information stored per handle. NBNB, driverType and DriverVersion used to be
 231** only stored once for all handles... i.e. nonsensical. This change means that
 232** three U14...() calls now include handles that were previously void. We have
 233** set a constructor and a destructor call for the library (see the end) to
 234** initialise important structures, or call use1401_load().
 235*/
 236static short asDriverType[MAX1401] = {0};
 237static int lLastDriverVersion = U14ERR_NO1401DRIV;
 238static int lLastDriverType = U14TYPEUNKNOWN;
 239static int alDriverVersion[MAX1401];            // version/type of each driver
 240static int alTimeOutPeriod[MAX1401];            // timeout time in milliseconds
 241static short asLastRetCode[MAX1401];            // last code from a fn call
 242static short asType1401[MAX1401] = {0};         // The type of the 1401
 243static BOOL abGrabbed[MAX1401] = {0};           // Flag for grabbed, set true by grab1401
 244static int iAttached = 0;                       // counts process attaches so can let go
 245
 246#ifdef _IS_WINDOWS_
 247/****************************************************************************
 248** Windows NT Specific Variables and internal types
 249****************************************************************************/
 250static HANDLE aHand1401[MAX1401] = {0};         // handles for 1401s
 251static HANDLE aXferEvent[MAX1401] = {0};        // transfer events for the 1401s
 252static LPVOID apAreas[MAX1401][MAX_TRANSAREAS]; // Locked areas
 253static DWORD  auAreas[MAX1401][MAX_TRANSAREAS]; // Size of locked areas
 254static BOOL   bWindows9x = FALSE;               // if we are Windows 95 or better
 255#ifdef _WIN64
 256#define USE_NT_DIOC(ind) TRUE
 257#else
 258static BOOL   abUseNTDIOC[MAX1401];             // Use NT-style DIOC parameters */
 259#define USE_NT_DIOC(ind) abUseNTDIOC[ind]
 260#endif
 261
 262#endif
 263
 264#ifdef LINUX
 265static int aHand1401[MAX1401] = {0};    // handles for 1401s
 266#define INVALID_HANDLE_VALUE 0          // to avoid code differences
 267#endif
 268
 269
 270/*
 271** The CmdHead relates to backwards compatibility with ancient Microsoft (and Sperry!)
 272** versions of BASIC, where this header was needed so we could load a command into
 273** memory.
 274*/
 275#pragma pack(1)                 // pack our structure
 276typedef struct CmdHead          // defines header block on command
 277{                               // for PC commands
 278   char   acBasic[5];           // BASIC information - needed to align things
 279   WORD   wBasicSz;             // size as seen by BASIC
 280   WORD   wCmdSize;             // size of the following info
 281} __packed CMDHEAD;
 282#pragma pack()                  // back to normal
 283
 284/*
 285** The rest of the header looks like this...
 286**  int    iRelPnt;             relocation pointer... actual start
 287**  char   acName[8];           string holding the command name
 288**  BYTE   bMonRev;             monitor revision level
 289**  BYTE   bCmdRev;             command revision level
 290*/
 291
 292typedef CMDHEAD *LPCMDHEAD;     // pointer to a command header
 293
 294#define  MAXSTRLEN   255        // maximum string length we use
 295#define  TOHOST      FALSE
 296#define  TO1401      TRUE
 297
 298static short CheckHandle(short h)
 299{
 300    if ((h < 0) || (h >= MAX1401))  // must be legal range...
 301        return U14ERR_BADHAND;
 302    if (aHand1401[h] <= 0)          // must be open
 303        return U14ERR_BADHAND;
 304    return U14ERR_NOERROR;
 305}
 306
 307#ifdef _IS_WINDOWS_
 308/****************************************************************************
 309** U14Status1401    Used for functions which do not pass any data in but
 310**                  get data back
 311****************************************************************************/
 312static short U14Status1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
 313{
 314    DWORD dwBytes = 0;
 315
 316    if ((sHand < 0) || (sHand >= MAX1401))  /* Check parameters */
 317        return U14ERR_BADHAND;
 318#ifndef _WIN64
 319    if (!USE_NT_DIOC(sHand)) 
 320    {   /* Windows 9x DIOC methods? */
 321        if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk,sizeof(TCSBLOCK),&dwBytes,NULL))
 322            return (short)((dwBytes>=sizeof(TCSBLOCK)) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
 323        else
 324            return (short)GetLastError();
 325    }
 326    else
 327#endif
 328    {                                       /* Windows NT or USB driver */
 329        PARAMBLK rWork;
 330        rWork.sState = U14ERR_DRIVCOMMS;
 331        if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, &rWork,sizeof(PARAMBLK),&dwBytes,NULL) &&
 332            (dwBytes >= sizeof(PARAMBLK)))
 333        {
 334            *pBlk = rWork.csBlock;
 335            return rWork.sState;
 336        }
 337    }
 338
 339    return U14ERR_DRIVCOMMS;
 340}
 341
 342/****************************************************************************
 343** U14Control1401   Used for functions which pass data in and only expect
 344**                  an error code back
 345****************************************************************************/
 346static short U14Control1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
 347{
 348    DWORD dwBytes = 0;
 349
 350    if ((sHand < 0) || (sHand >= MAX1401))              /* Check parameters */
 351        return U14ERR_BADHAND;
 352
 353#ifndef _WIN64
 354    if (!USE_NT_DIOC(sHand))                    
 355    {                            /* Windows 9x DIOC methods */
 356        if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk, sizeof(TCSBLOCK), &dwBytes, NULL))
 357            return (short)(dwBytes >= sizeof(TCSBLOCK) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
 358        else
 359            return (short)GetLastError();
 360    }
 361    else
 362#endif
 363    {                            /* Windows NT or later */
 364        PARAMBLK rWork;
 365        rWork.sState = U14ERR_DRIVCOMMS;
 366        if (DeviceIoControl(aHand1401[sHand], lCode, pBlk, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
 367            (dwBytes >= sizeof(PARAMBLK)))
 368            return rWork.sState;
 369    }
 370
 371    return U14ERR_DRIVCOMMS;
 372}
 373#endif
 374
 375/****************************************************************************
 376** SafeTickCount
 377** Gets time in approximately units of a millisecond.
 378*****************************************************************************/
 379static long SafeTickCount()
 380{
 381#ifdef _IS_WINDOWS_
 382    return GetTickCount();
 383#endif
 384#ifdef LINUX
 385    struct timeval tv;
 386    gettimeofday(&tv, NULL);
 387    return (tv.tv_sec*1000 + tv.tv_usec/1000);
 388#endif
 389}
 390
 391/****************************************************************************
 392** A utility routine to get the command file extension for a given type
 393** of 1401. We assume the type code is vaguely legal.
 394****************************************************************************/
 395static int ExtForType(short sType, char* szExt)
 396{
 397    szExt[0] = 0;                       /* Default return is a blank string */
 398    switch (sType)
 399    {
 400    case U14TYPE1401: strcpy(szExt, ".CMD");  break;    // Standard 1401
 401    case U14TYPEPLUS: strcpy(szExt, ".GXC");  break;    // 1401 plus
 402    default:               // All others are in a predictable sequence
 403        strcpy(szExt, ".ARM");
 404            szExt[3] = (char)('M' + sType - U14TYPEU1401);
 405        if (szExt[3] > 'Z')             // Wrap round to ARA after ARZ
 406                szExt[3] = (char)(szExt[3] - 26);
 407    }
 408    return 0;
 409}
 410
 411/****************************************************************************
 412**   U14WhenToTimeOut
 413**       Returns the time to time out in time units suitable for the machine
 414** we are running on  ie millsecs for pc/linux, or Mac/
 415****************************************************************************/
 416U14API(int) U14WhenToTimeOut(short hand)
 417{
 418    int iNow = SafeTickCount();
 419    if ((hand >= 0) && (hand < MAX1401))
 420        iNow += alTimeOutPeriod[hand];
 421    return iNow;
 422}
 423
 424/****************************************************************************
 425** U14PassedTime
 426** Returns non zero if the timed passed in has been passed 0 if not
 427****************************************************************************/
 428U14API(short) U14PassedTime(int lCheckTime)
 429{
 430    return (short)((SafeTickCount()-lCheckTime) > 0);
 431}
 432
 433/****************************************************************************
 434** TranslateString
 435** Tidies up string that U14GetString returns. Converts all the commas in a
 436** string to spaces. Removes terminating CR character. May do more in future.
 437****************************************************************************/
 438static void TranslateString(char* pStr)
 439{
 440    int i = 0;
 441    while (pStr[i])
 442    {
 443        if (pStr[i] == ',')
 444            pStr[i] = ' ';              /* convert comma to space */
 445        ++i;
 446    }
 447
 448    if ((i > 0) && (pStr[i-1] == '\n'))  /* kill terminating LF */
 449        pStr[i-1] = (char)0;
 450}
 451
 452/****************************************************************************
 453** U14StrToLongs
 454** Converts a string to an array of longs and returns the number of values
 455****************************************************************************/
 456U14API(short) U14StrToLongs(const char* pszBuff, U14LONG *palNums, short sMaxLongs)
 457{
 458    WORD wChInd = 0;                // index into source
 459    short sLgInd = 0;               // index into result longs
 460
 461    while (pszBuff[wChInd] &&       // until we get to end of string...
 462           (sLgInd < sMaxLongs))    // ...or filled the buffer
 463    {
 464        // Why not use a C Library converter?
 465        switch (pszBuff[wChInd])
 466        {
 467        case '-':
 468        case '0': case '1':   case '2': case '3':   case '4':
 469        case '5': case '6':   case '7': case '8':   case '9':
 470            {
 471                BOOL bDone = FALSE; // true at end of number
 472                int iSign = 1;      // sign of number
 473                long lValue = 0;
 474
 475                while ((!bDone) && pszBuff[wChInd])
 476                {
 477                    switch (pszBuff[wChInd])
 478                    {
 479                    case '-':
 480                        iSign = -1; // swap sign
 481                        break;
 482
 483                    case '0': case '1':   case '2': case '3':   case '4':
 484                    case '5': case '6':   case '7': case '8':   case '9':
 485                        lValue *= 10;   // move to next digit base 10
 486                        lValue += ((int)pszBuff[wChInd]-(int)'0');
 487                        break;
 488
 489                    default:        // end of number
 490                        bDone = TRUE;
 491                        break;
 492                    }
 493                    wChInd++;       // move onto next character
 494                }
 495                palNums[sLgInd] = lValue * iSign;
 496                sLgInd++;
 497            }
 498            break;
 499
 500        default:
 501            wChInd++;               // look at next char
 502            break;
 503        }
 504    }
 505    return (sLgInd);
 506}
 507
 508
 509/****************************************************************************
 510** U14LongsFrom1401
 511** Gets the next waiting line from the 1401 and converts it longs
 512** Returns the number of numbers read or an error.
 513****************************************************************************/
 514U14API(short) U14LongsFrom1401(short hand, U14LONG *palBuff, short sMaxLongs)
 515{
 516    char szWork[MAXSTRLEN];
 517    short sResult = U14GetString(hand, szWork, MAXSTRLEN);/* get reply from 1401   */
 518    if (sResult == U14ERR_NOERROR)                  /* if no error convert   */
 519        sResult = U14StrToLongs(szWork, palBuff, sMaxLongs);
 520    return sResult;
 521}
 522
 523/****************************************************************************
 524**   U14CheckErr
 525**   Sends the ERR command to the 1401 and gets the result. Returns 0, a
 526**   negative error code, or the first error value.
 527****************************************************************************/
 528U14API(short) U14CheckErr(short hand)
 529{
 530    short sResult = U14SendString(hand, ";ERR;");
 531    if (sResult == U14ERR_NOERROR)
 532    {
 533        U14LONG er[3];
 534        sResult = U14LongsFrom1401(hand, er, 3);
 535        if (sResult > 0)
 536        {
 537            sResult = (short)er[0];        /* Either zero or an error value */
 538#ifdef _DEBUG
 539            if (er[0] != 0)
 540            {
 541                char szMsg[50];
 542                sprintf(szMsg, "U14CheckErr returned %d,%d\n", er[0], er[1]);
 543                OutputDebugString(szMsg);
 544            }
 545#endif
 546        }
 547        else
 548        {
 549            if (sResult == 0)
 550                sResult = U14ERR_TIMEOUT;      /* No numbers equals timeout */
 551        }
 552    }
 553
 554    return sResult;
 555}
 556
 557/****************************************************************************
 558** U14LastErrCode
 559** Returns the last code from the driver. This is for Windows where all calls
 560** go through the Control and Status routines, so we can save any error.
 561****************************************************************************/
 562U14API(short) U14LastErrCode(short hand)
 563{
 564    if ((hand < 0) || (hand >= MAX1401))
 565        return U14ERR_BADHAND;
 566    return asLastRetCode[hand];
 567}
 568
 569/****************************************************************************
 570** U14SetTimeout
 571** Set the timeout period for 1401 comms in milliseconds
 572****************************************************************************/
 573U14API(void) U14SetTimeout(short hand, int lTimeOut)
 574{
 575    if ((hand < 0) || (hand >= MAX1401))
 576        return;
 577    alTimeOutPeriod[hand] = lTimeOut;
 578}
 579
 580/****************************************************************************
 581** U14GetTimeout
 582** Get the timeout period for 1401 comms in milliseconds
 583****************************************************************************/
 584U14API(int) U14GetTimeout(short hand)
 585{
 586    if ((hand < 0) || (hand >= MAX1401))
 587        return U14ERR_BADHAND;
 588    return alTimeOutPeriod[hand];
 589}
 590
 591/****************************************************************************
 592** U14OutBufSpace
 593** Return the space in the output buffer, or an error.
 594****************************************************************************/
 595U14API(short) U14OutBufSpace(short hand)
 596{
 597#ifdef _IS_WINDOWS_
 598    TCSBLOCK csBlock;
 599    short sErr = U14Status1401(hand, U14_GETOUTBUFSPACE,&csBlock);
 600    if (sErr == U14ERR_NOERROR)
 601        sErr = csBlock.ints[0];
 602    return sErr;
 603#endif
 604#ifdef LINUX
 605    short sErr = CheckHandle(hand);
 606    return (sErr == U14ERR_NOERROR) ? CED_GetOutBufSpace(aHand1401[hand]) : sErr;
 607#endif
 608}
 609
 610
 611/****************************************************************************
 612** U14BaseAddr1401
 613** Returns the 1401 base address or an error code. Meaningless nowadays
 614****************************************************************************/
 615U14API(int) U14BaseAddr1401(short hand)
 616{
 617#ifdef _IS_WINDOWS_
 618    TCSBLOCK csBlock;
 619    int iError = U14Status1401(hand, U14_GETBASEADDRESS,&csBlock);
 620    if (iError == U14ERR_NOERROR)
 621        iError = csBlock.longs[0];
 622    return iError;
 623#endif
 624#ifdef LINUX
 625    short sErr = CheckHandle(hand);
 626    return (sErr == U14ERR_NOERROR) ? CED_GetBaseAddress(aHand1401[hand]) : sErr;
 627#endif
 628}
 629
 630/****************************************************************************
 631** U14StateOf1401
 632** Return error state, either NOERROR or a negative code.
 633****************************************************************************/
 634U14API(short) U14StateOf1401(short hand)
 635{
 636#ifdef _IS_WINDOWS_
 637    TCSBLOCK csBlock;
 638    short sErr = U14Status1401(hand, U14_STATEOF1401, &csBlock);
 639    if (sErr == U14ERR_NOERROR)
 640    {
 641        sErr = csBlock.ints[0];      // returned 1401 state
 642        if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
 643            sErr = U14ERR_NOERROR;
 644    }
 645#endif
 646#ifdef LINUX
 647    short sErr = CheckHandle(hand);
 648    if (sErr == U14ERR_NOERROR)
 649    {
 650        sErr = (short)CED_StateOf1401(aHand1401[hand]);
 651        if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
 652            sErr = U14ERR_NOERROR;
 653    }
 654#endif
 655    return sErr;
 656}
 657
 658/****************************************************************************
 659** U14DriverVersion
 660** Returns the driver version. Hi word is major revision, low word is minor.
 661** If you pass in a silly handle (like -1), we return the version of the last
 662** driver we know of (to cope with PCI and no 1401 attached).
 663****************************************************************************/
 664U14API(int) U14DriverVersion(short hand)
 665{
 666    return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverVersion : alDriverVersion[hand];
 667}
 668
 669/****************************************************************************
 670** U14DriverType
 671** Returns the driver type. The type, 0=ISA/NU-Bus, 1=PCI, 2=USB, 3=HSS
 672** If you pass in a silly handle (like -1), we return the type of the last
 673** driver we know of (to cope with PCI and no 1401 attached).
 674****************************************************************************/
 675U14API(int) U14DriverType(short hand)
 676{
 677    return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverType : asDriverType[hand];
 678}
 679
 680/****************************************************************************
 681** U14DriverName
 682** Returns the driver type as 3 character (ISA, PCI, USB or HSS))
 683****************************************************************************/
 684U14API(short) U14DriverName(short hand, char* pBuf, WORD wMax)
 685{
 686    char* pName;
 687    *pBuf = 0;                             // Start off with a blank string
 688    switch (U14DriverType(hand))           // Results according to type
 689    {
 690    case 0:  pName = "ISA"; break;
 691    case 1:  pName = "PCI"; break;
 692    case 2:  pName = "USB"; break;
 693    case 3:  pName = "HSS"; break;
 694    default: pName = "???"; break;
 695    }
 696    strncpy(pBuf, pName, wMax);            // Copy the correct name to return
 697
 698    return U14ERR_NOERROR;
 699}
 700
 701/****************************************************************************
 702** U14BlkTransState
 703** Returns 0 no transfer in progress, 1 transfer in progress or an error code
 704****************************************************************************/
 705U14API(short) U14BlkTransState(short hand)
 706{
 707#ifdef _IS_WINDOWS_
 708    TCSBLOCK csBlock;
 709    short sErr = U14Status1401(hand, U14_BLKTRANSSTATE, &csBlock);
 710    if (sErr == U14ERR_NOERROR)
 711        sErr = csBlock.ints[0];
 712    return sErr;
 713#endif
 714#ifdef LINUX
 715    short sErr = CheckHandle(hand);
 716    return (sErr == U14ERR_NOERROR) ? CED_BlkTransState(aHand1401[hand]) : sErr;
 717#endif
 718}
 719
 720/****************************************************************************
 721** U14Grab1401
 722** Take control of the 1401 for diagnostics purposes. USB does nothing.
 723****************************************************************************/
 724U14API(short) U14Grab1401(short hand)
 725{
 726    short sErr = CheckHandle(hand);
 727    if (sErr == U14ERR_NOERROR)
 728    {
 729#ifdef _IS_WINDOWS_
 730        if (abGrabbed[hand])            // 1401 should not have been grabbed
 731            sErr = U14ERR_ALREADYSET;   // Error code defined for this
 732        else
 733        {
 734            TCSBLOCK csBlock;
 735            sErr = U14Control1401(hand, U14_GRAB1401, &csBlock);
 736        }
 737#endif
 738#ifdef LINUX
 739        // 1401 should not have been grabbed
 740        sErr = abGrabbed[hand] ? U14ERR_ALREADYSET : CED_Grab1401(aHand1401[hand]);
 741#endif
 742        if (sErr == U14ERR_NOERROR)
 743            abGrabbed[hand] = TRUE;
 744    }
 745    return sErr;
 746}
 747
 748/****************************************************************************
 749** U14Free1401
 750****************************************************************************/
 751U14API(short)  U14Free1401(short hand)
 752{
 753    short sErr = CheckHandle(hand);
 754    if (sErr == U14ERR_NOERROR)
 755    {
 756#ifdef _IS_WINDOWS_
 757        if (abGrabbed[hand])    // 1401 should have been grabbed
 758        {
 759            TCSBLOCK csBlock;
 760            sErr = U14Control1401(hand, U14_FREE1401, &csBlock);
 761        }
 762        else
 763            sErr = U14ERR_NOTSET;
 764#endif
 765#ifdef LINUX
 766        // 1401 should not have been grabbed
 767        sErr = abGrabbed[hand] ? CED_Free1401(aHand1401[hand]) : U14ERR_NOTSET;
 768#endif
 769        if (sErr == U14ERR_NOERROR)
 770            abGrabbed[hand] = FALSE;
 771    }
 772    return sErr;
 773}
 774
 775/****************************************************************************
 776** U14Peek1401
 777** DESCRIPTION  Cause the 1401 to do one or more peek operations.
 778** If lRepeats is zero, the loop will continue until U14StopDebugLoop
 779** is called. After the peek is done, use U14GetDebugData to retrieve
 780** the results of the peek.
 781****************************************************************************/
 782U14API(short) U14Peek1401(short hand, DWORD dwAddr, int nSize, int nRepeats)
 783{
 784    short sErr = CheckHandle(hand);
 785    if (sErr == U14ERR_NOERROR)
 786    {
 787        if (abGrabbed[hand])    // 1401 should have been grabbed
 788        {
 789#ifdef _IS_WINDOWS_
 790            TCSBLOCK csBlock;
 791            csBlock.longs[0] = (long)dwAddr;
 792            csBlock.longs[1] = nSize;
 793            csBlock.longs[2] = nRepeats;
 794            sErr = U14Control1401(hand, U14_DBGPEEK, &csBlock);
 795#endif
 796#ifdef LINUX
 797            TDBGBLOCK dbb;
 798            dbb.iAddr = (int)dwAddr;
 799            dbb.iWidth = nSize;
 800            dbb.iRepeats = nRepeats;
 801            sErr = CED_DbgPeek(aHand1401[hand], &dbb);
 802#endif
 803        }
 804        else
 805            sErr = U14ERR_NOTSET;
 806    }
 807    return sErr;
 808}
 809
 810/****************************************************************************
 811** U14Poke1401
 812** DESCRIPTION  Cause the 1401 to do one or more poke operations.
 813** If lRepeats is zero, the loop will continue until U14StopDebugLoop
 814** is called.
 815****************************************************************************/
 816U14API(short) U14Poke1401(short hand, DWORD dwAddr, DWORD dwValue,
 817                                      int nSize, int nRepeats)
 818{
 819    short sErr = CheckHandle(hand);
 820    if (sErr == U14ERR_NOERROR)
 821    {
 822        if (abGrabbed[hand])    // 1401 should have been grabbed
 823        {
 824#ifdef _IS_WINDOWS_
 825            TCSBLOCK csBlock;
 826            csBlock.longs[0] = (long)dwAddr;
 827            csBlock.longs[1] = nSize;
 828            csBlock.longs[2] = nRepeats;
 829            csBlock.longs[3] = (long)dwValue;
 830            sErr = U14Control1401(hand, U14_DBGPOKE, &csBlock);
 831#endif
 832#ifdef LINUX
 833            TDBGBLOCK dbb;
 834            dbb.iAddr = (int)dwAddr;
 835            dbb.iWidth = nSize;
 836            dbb.iRepeats= nRepeats;
 837            dbb.iData = (int)dwValue;
 838            sErr = CED_DbgPoke(aHand1401[hand], &dbb);
 839#endif
 840        }
 841        else
 842            sErr = U14ERR_NOTSET;
 843    }
 844    return sErr;
 845}
 846
 847/****************************************************************************
 848** U14Ramp1401
 849** DESCRIPTION  Cause the 1401 to loop, writing a ramp to a location.
 850** If lRepeats is zero, the loop will continue until U14StopDebugLoop.
 851****************************************************************************/
 852U14API(short) U14Ramp1401(short hand, DWORD dwAddr, DWORD dwDef, DWORD dwEnable,
 853                                      int nSize, int nRepeats)
 854{
 855    short sErr = CheckHandle(hand);
 856    if (sErr == U14ERR_NOERROR)
 857    {
 858        if (abGrabbed[hand])    // 1401 should have been grabbed
 859        {
 860#ifdef _IS_WINDOWS_
 861            TCSBLOCK csBlock;
 862            csBlock.longs[0] = (long)dwAddr;
 863            csBlock.longs[1] = (long)dwDef;
 864            csBlock.longs[2] = (long)dwEnable;
 865            csBlock.longs[3] = nSize;
 866            csBlock.longs[4] = nRepeats;
 867            sErr = U14Control1401(hand, U14_DBGRAMPDATA, &csBlock);
 868#endif
 869#ifdef LINUX
 870            TDBGBLOCK dbb;
 871            dbb.iAddr = (int)dwAddr;
 872            dbb.iDefault = (int)dwDef;
 873            dbb.iMask = (int)dwEnable;
 874            dbb.iWidth = nSize;
 875            dbb.iRepeats = nRepeats;
 876            sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
 877#endif
 878        }
 879        else
 880            sErr = U14ERR_NOTSET;
 881    }
 882    return sErr;
 883}
 884
 885/****************************************************************************
 886** U14RampAddr
 887** DESCRIPTION  Cause the 1401 to loop, reading from a ramping location.
 888** If lRepeats is zero, the loop will continue until U14StopDebugLoop
 889****************************************************************************/
 890U14API(short) U14RampAddr(short hand, DWORD dwDef, DWORD dwEnable,
 891                                      int nSize, int nRepeats)
 892{
 893    short sErr = CheckHandle(hand);
 894    if (sErr == U14ERR_NOERROR)
 895    {
 896        if (abGrabbed[hand])    // 1401 should have been grabbed
 897        {
 898#ifdef _IS_WINDOWS_
 899            TCSBLOCK csBlock;
 900            csBlock.longs[0] = (long)dwDef;
 901            csBlock.longs[1] = (long)dwEnable;
 902            csBlock.longs[2] = nSize;
 903            csBlock.longs[3] = nRepeats;
 904            sErr = U14Control1401(hand, U14_DBGRAMPADDR, &csBlock);
 905#endif
 906#ifdef LINUX
 907            TDBGBLOCK dbb;
 908            dbb.iDefault = (int)dwDef;
 909            dbb.iMask = (int)dwEnable;
 910            dbb.iWidth = nSize;
 911            dbb.iRepeats = nRepeats;
 912            sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
 913#endif
 914        }
 915        else
 916            sErr = U14ERR_NOTSET;
 917    }
 918    return sErr;
 919}
 920
 921/****************************************************************************
 922**    U14StopDebugLoop
 923**    DESCRIPTION Stops a peek\poke\ramp that, with repeats set to zero,
 924**    will otherwise continue forever.
 925****************************************************************************/
 926U14API(short) U14StopDebugLoop(short hand)
 927{
 928    short sErr = CheckHandle(hand);
 929    if (sErr == U14ERR_NOERROR)
 930#ifdef _IS_WINDOWS_
 931    {
 932        if (abGrabbed[hand])    // 1401 should have been grabbed
 933        {
 934            TCSBLOCK csBlock;
 935            sErr = U14Control1401(hand, U14_DBGSTOPLOOP, &csBlock);
 936        }
 937        else
 938            sErr = U14ERR_NOTSET;
 939    }
 940#endif
 941#ifdef LINUX
 942        sErr = abGrabbed[hand] ? CED_DbgStopLoop(aHand1401[hand]) : U14ERR_NOTSET;
 943#endif
 944    return sErr;
 945}
 946
 947/****************************************************************************
 948** U14GetDebugData
 949** DESCRIPTION Returns the result from a previous peek operation.
 950****************************************************************************/
 951U14API(short) U14GetDebugData(short hand, U14LONG* plValue)
 952{
 953    short sErr = CheckHandle(hand);
 954    if (sErr == U14ERR_NOERROR)
 955    {
 956        if (abGrabbed[hand])    // 1401 should have been grabbed
 957        {
 958#ifdef _IS_WINDOWS_
 959            TCSBLOCK csBlock;
 960            sErr = U14Status1401(hand, U14_DBGGETDATA, &csBlock);
 961            if (sErr == U14ERR_NOERROR)
 962                *plValue = csBlock.longs[0];    // Return the data
 963#endif
 964#ifdef LINUX
 965            TDBGBLOCK dbb;
 966            sErr = CED_DbgGetData(aHand1401[hand], &dbb);
 967            if (sErr == U14ERR_NOERROR)
 968                *plValue = dbb.iData;                     /* Return the data */
 969#endif
 970        }
 971        else
 972            sErr = U14ERR_NOTSET;
 973    }
 974    return sErr;
 975}
 976
 977/****************************************************************************
 978** U14StartSelfTest
 979****************************************************************************/
 980U14API(short) U14StartSelfTest(short hand)
 981{
 982#ifdef _IS_WINDOWS_
 983    TCSBLOCK csBlock;
 984    return U14Control1401(hand, U14_STARTSELFTEST, &csBlock);
 985#endif
 986#ifdef LINUX
 987    short sErr = CheckHandle(hand);
 988    return (sErr == U14ERR_NOERROR) ? CED_StartSelfTest(aHand1401[hand]) : sErr;
 989#endif
 990}
 991
 992/****************************************************************************
 993** U14CheckSelfTest
 994****************************************************************************/
 995U14API(short) U14CheckSelfTest(short hand, U14LONG *pData)
 996{
 997#ifdef _IS_WINDOWS_
 998    TCSBLOCK csBlock;
 999    short sErr = U14Status1401(hand, U14_CHECKSELFTEST, &csBlock);
1000    if (sErr == U14ERR_NOERROR)
1001    {
1002        pData[0] = csBlock.longs[0];        /* Return the results to user */
1003        pData[1] = csBlock.longs[1];
1004        pData[2] = csBlock.longs[2];
1005    }
1006#endif
1007#ifdef LINUX
1008    short sErr = CheckHandle(hand);
1009    if (sErr == U14ERR_NOERROR)                /* Check parameters */
1010    {
1011        TGET_SELFTEST gst;
1012        sErr = CED_CheckSelfTest(aHand1401[hand], &gst);
1013        if (sErr == U14ERR_NOERROR)
1014        {
1015            pData[0] = gst.code;        /* Return the results to user */
1016            pData[1] = gst.x;
1017            pData[2] = gst.y;
1018        }
1019    }
1020#endif
1021    return sErr;
1022}
1023
1024/****************************************************************************
1025** U14GetUserMemorySize
1026****************************************************************************/
1027U14API(short) U14GetUserMemorySize(short hand, DWORD *pMemorySize)
1028{
1029    // The original 1401 used a different command for getting the size
1030    short sErr = U14SendString(hand, (asType1401[hand] == U14TYPE1401) ? "MEMTOP;" : "MEMTOP,?;");
1031    *pMemorySize = 0;         /* if we get error then leave size set at 0  */
1032    if (sErr == U14ERR_NOERROR)
1033    {
1034        U14LONG alLimits[4];
1035        sErr = U14LongsFrom1401(hand, alLimits, 4);
1036        if (sErr > 0)              /* +ve sErr is the number of values read */
1037        {
1038            sErr = U14ERR_NOERROR;                  /* All OK, flag success */
1039            if (asType1401[hand] == U14TYPE1401)    /* result for standard  */
1040                *pMemorySize = alLimits[0] - alLimits[1]; /* memtop-membot */
1041            else
1042                *pMemorySize = alLimits[0];   /* result for plus or u1401  */
1043        }
1044    }
1045    return sErr;
1046}
1047
1048/****************************************************************************
1049** U14TypeOf1401
1050** Returns the type of the 1401, maybe unknown
1051****************************************************************************/
1052U14API(short) U14TypeOf1401(short hand)
1053{
1054    if ((hand < 0) || (hand >= MAX1401))                /* Check parameters */
1055        return U14ERR_BADHAND;
1056    else
1057        return asType1401[hand];
1058}
1059
1060/****************************************************************************
1061** U14NameOf1401
1062** Returns the type of the 1401 as a string, blank if unknown
1063****************************************************************************/
1064U14API(short) U14NameOf1401(short hand, char* pBuf, WORD wMax)
1065{
1066    short sErr = CheckHandle(hand);
1067    if (sErr == U14ERR_NOERROR)
1068    {
1069    char* pName;
1070    switch (asType1401[hand])               // Results according to type
1071    {
1072    case U14TYPE1401:  pName = "Std 1401"; break;
1073    case U14TYPEPLUS:  pName = "1401plus"; break;
1074    case U14TYPEU1401: pName = "micro1401"; break;
1075    case U14TYPEPOWER: pName = "Power1401"; break;
1076    case U14TYPEU14012:pName = "Micro1401 mk II"; break;
1077    case U14TYPEPOWER2:pName = "Power1401 mk II"; break;
1078    case U14TYPEU14013:pName = "Micro1401-3"; break;
1079    case U14TYPEPOWER3:pName = "Power1401-3"; break;
1080    default:           pName = "Unknown";
1081    }
1082        strncpy(pBuf, pName, wMax);
1083    }
1084    return sErr;
1085}
1086
1087/****************************************************************************
1088** U14TransferFlags
1089**  Returns the driver block transfer flags.
1090**  Bits can be set - see U14TF_ constants in use1401.h
1091*****************************************************************************/
1092U14API(short) U14TransferFlags(short hand)
1093{
1094#ifdef _IS_WINDOWS_
1095    TCSBLOCK csBlock;
1096    short sErr = U14Status1401(hand, U14_TRANSFERFLAGS, &csBlock);
1097    return (sErr == U14ERR_NOERROR) ? (short)csBlock.ints[0] : sErr;
1098#endif
1099#ifdef LINUX
1100    short sErr = CheckHandle(hand);
1101    return (sErr == U14ERR_NOERROR) ? CED_TransferFlags(aHand1401[hand]) : sErr;
1102#endif
1103}
1104
1105/****************************************************************************
1106** GetDriverVersion
1107** Actually reads driver version from the device driver.
1108** Hi word is major revision, low word is minor revision.
1109** Assumes that hand has been checked. Also codes driver type in bits 24 up.
1110*****************************************************************************/
1111static int GetDriverVersion(short hand)
1112{
1113#ifdef _IS_WINDOWS_
1114    TCSBLOCK csBlock;
1115    int iErr = U14Status1401(hand, U14_GETDRIVERREVISION, &csBlock);
1116    if (iErr == U14ERR_NOERROR)
1117        iErr = csBlock.longs[0];
1118    return iErr;
1119#endif
1120#ifdef LINUX
1121    return CED_GetDriverRevision(aHand1401[hand]);
1122#endif
1123}
1124
1125/****************************************************************************
1126** U14MonitorRev
1127** Returns the 1401 monitor revision number.
1128** The number returned is the minor revision - the part after the
1129** decimal point - plus the major revision times 1000.
1130*****************************************************************************/
1131U14API(int) U14MonitorRev(short hand)
1132{
1133    int iRev = 0;
1134    int iErr = CheckHandle(hand);
1135    if (iErr != U14ERR_NOERROR)                 // Check open and in use
1136        return iErr;
1137
1138    if (asType1401[hand] >= U14TYPEPOWER2)      // The Power2 onwards can give us the monitor
1139    {                                           //  revision directly for all versions
1140        iErr = U14SendString(hand, "INFO,S,28;");
1141        if (iErr == U14ERR_NOERROR)
1142        {
1143            U14LONG lVals[2];                   // Read a single number being the revision
1144            iErr = U14LongsFrom1401(hand, lVals, 1);
1145            if (iErr > 0)
1146            {
1147                iErr = U14ERR_NOERROR;
1148                iRev = lVals[0];                // This is the minor part of the revision
1149                iRev += asType1401[hand] * 10000;
1150            }
1151        }
1152    }
1153    else
1154    {                                           /* Do it the hard way for older hardware */
1155        iErr = U14SendString(hand, ";CLIST;");     /* ask for command levels */
1156        if (iErr == U14ERR_NOERROR)
1157        {     
1158            while (iErr == U14ERR_NOERROR)
1159            {
1160                char wstr[50];
1161                iErr = U14GetString(hand, wstr, 45);
1162                if (iErr == U14ERR_NOERROR)
1163                {
1164                    char *pstr = strstr(wstr,"RESET");  /* Is this the RESET command? */
1165                    if ((pstr == wstr) && (wstr[5] == ' '))
1166                    {
1167                        char *pstr2;
1168                        size_t l;
1169                        pstr += 6;       /* Move past RESET and followinmg char */
1170                        l = strlen(pstr);       /* The length of text remaining */
1171                        while (((pstr[l-1] == ' ') || (pstr[l-1] == 13)) && (l > 0))
1172                        {
1173                            pstr[l-1] = 0;         /* Tidy up string at the end */
1174                            l--;                  /* by removing spaces and CRs */
1175                        }
1176                        pstr2 = strchr(pstr, '.');    /* Find the decimal point */
1177                        if (pstr2 != NULL)                /* If we found the DP */
1178                        {
1179                            *pstr2 = 0;                /* End pstr string at DP */
1180                            pstr2++;              /* Now past the decimal point */
1181                            iRev = atoi(pstr2);   /* Get the number after point */
1182                        }
1183                        iRev += (atoi(pstr) * 1000);    /* Add first bit * 1000 */
1184                    }
1185                    if ((strlen(wstr) < 3) && (wstr[0] == ' '))
1186                        break;              /* Spot the last line of results */
1187                }
1188            }
1189        }
1190    }
1191    if (iErr == U14ERR_NOERROR)            /* Return revision if no error */
1192        iErr = iRev;
1193
1194    return iErr;
1195}
1196
1197/****************************************************************************
1198** U14TryToOpen     Tries to open the 1401 number passed
1199**  Note : This will succeed with NT driver even if no I/F card or
1200**         1401 switched off, so we check state and close the driver
1201**         if the state is unsatisfactory in U14Open1401.
1202****************************************************************************/
1203#ifdef _IS_WINDOWS_
1204#define U14NAMEOLD "\\\\.\\CED_140%d"
1205#define U14NAMENEW "\\\\.\\CED%d"
1206static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
1207{
1208    short sErr = U14ERR_NOERROR;
1209    HANDLE hDevice = INVALID_HANDLE_VALUE;
1210    DWORD dwErr = 0;
1211    int nFirst, nLast, nDev = 0;        /* Used for the search for a 1401 */
1212    BOOL bOldName = FALSE;               /* start by looking for a modern driver */
1213
1214    if (n1401 == 0)                             /* If we need to look for a 1401 */
1215    {
1216        nFirst = 1;                             /* Set the search range */
1217        nLast = MAX1401;                        /* through all the possible 1401s */
1218    }
1219    else
1220        nFirst = nLast = n1401;                 /* Otherwise just one 1401 */
1221
1222    while (hDevice == INVALID_HANDLE_VALUE)     /* Loop to try for a 1401 */
1223    {
1224        for (nDev = nFirst; nDev <= nLast; nDev++)
1225        {
1226            char szDevName[40];                 /* name of the device to open */
1227            sprintf(szDevName, bOldName ? U14NAMEOLD : U14NAMENEW, nDev);
1228            hDevice = CreateFile(szDevName, GENERIC_WRITE | GENERIC_READ,
1229                                 0, 0,          /* Unshared mode does nothing as this is a device */
1230                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1231
1232            if (hDevice != INVALID_HANDLE_VALUE)/* Check 1401 if opened */
1233            {
1234                TCSBLOCK csBlock;
1235                assert(aHand1401[nDev-1] == INVALID_HANDLE_VALUE);  // assert if already open
1236                aHand1401[nDev-1] = hDevice;    /* Save handle for now */
1237
1238#ifndef _WIN64
1239                // Use DIOC method if not windows 9x or if using new device name
1240                abUseNTDIOC[nDev-1] = (BOOL)(!bWindows9x || !bOldName);
1241#endif
1242                sErr = U14Status1401((short)(nDev-1), U14_TYPEOF1401, &csBlock);
1243                if (sErr == U14ERR_NOERROR)
1244                {
1245                    *plRetVal = csBlock.ints[0];
1246                    if (csBlock.ints[0] == U14ERR_INUSE)/* Prevent multi opens */
1247                    {
1248                        CloseHandle(hDevice);   /* treat as open failure */
1249                        hDevice = INVALID_HANDLE_VALUE;
1250                        aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
1251                        sErr = U14ERR_INUSE;
1252                    }
1253                    else
1254                        break;                  /* Exit from for loop on success */
1255                }
1256                else
1257                {
1258                    CloseHandle(hDevice);       /* Give up if func fails */
1259                    hDevice = INVALID_HANDLE_VALUE;
1260                    aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
1261                }
1262            }
1263            else
1264            {
1265                DWORD dwe = GetLastError();     /* Get error code otherwise */
1266                if ((dwe != ERROR_FILE_NOT_FOUND) || (dwErr == 0))
1267                    dwErr = dwe;                /* Ignore repeats of 'not found' */
1268            }
1269        }
1270
1271        if ((hDevice == INVALID_HANDLE_VALUE) &&/* No device found, and... */
1272            (bWindows9x) &&                     /* ...old names are allowed, and... */
1273            (bOldName == FALSE))                /* ...not tried old names yet */
1274            bOldName = TRUE;                    /* Set flag and go round again */
1275        else
1276            break;                              /* otherwise that's all folks */
1277    }
1278
1279    if (hDevice != INVALID_HANDLE_VALUE)        /* If we got our device open */
1280        *psHandle = (short)(nDev-1);            /* return 1401 number opened */
1281    else
1282    {
1283        if (dwErr == ERROR_FILE_NOT_FOUND)      /* Sort out the error codes */
1284            sErr = U14ERR_NO1401DRIV;           /* if file not found */
1285        else if (dwErr == ERROR_NOT_SUPPORTED)
1286            sErr = U14ERR_DRIVTOOOLD;           /* if DIOC not supported */
1287        else if (dwErr == ERROR_ACCESS_DENIED)
1288            sErr = U14ERR_INUSE;
1289        else
1290            sErr = U14ERR_DRIVCOMMS;            /* otherwise assume comms problem */
1291    }
1292    return sErr;
1293}
1294#endif
1295#ifdef LINUX
1296static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
1297{
1298    short sErr = U14ERR_NOERROR;
1299    int fh = 0;                             // will be 1401 handle
1300    int iErr = 0;
1301    int nFirst, nLast, nDev = 0;            // Used for the search for a 1401
1302
1303    if (n1401 == 0)                         // If we need to look for a 1401
1304    {
1305        nFirst = 1;                             /* Set the search range */
1306        nLast = MAX1401;                        /* through all the possible 1401s */
1307    }
1308    else
1309        nFirst = nLast = n1401;                 /* Otherwise just one 1401 */
1310
1311    for (nDev = nFirst; nDev <= nLast; nDev++)
1312    {
1313        char szDevName[40];                 // name of the device to open
1314        sprintf(szDevName,"/dev/cedusb/%d", nDev-1);
1315        fh = open(szDevName, O_RDWR);       // can only be opened once at a time
1316        if (fh > 0)                         // Check 1401 if opened
1317        {
1318            int iType1401 = CED_TypeOf1401(fh); // get 1401 type
1319            aHand1401[nDev-1] = fh;         // Save handle for now
1320            if (iType1401 >= 0)
1321            {
1322                *plRetVal = iType1401;
1323                 break;                     // Exit from for loop on success
1324            }
1325            else
1326            {
1327                close(fh);                  // Give up if func fails
1328                fh = 0;
1329                aHand1401[nDev-1] = 0;
1330            }
1331        }
1332        else
1333        {
1334            if (((errno != ENODEV) && (errno != ENOENT)) || (iErr == 0))
1335                iErr = errno;                // Ignore repeats of 'not found'
1336        }
1337    }
1338
1339
1340    if (fh)                                 // If we got our device open
1341        *psHandle = (short)(nDev-1);        // return 1401 number opened
1342    else
1343    {
1344        if ((iErr == ENODEV) || (iErr == ENOENT)) // Sort out the error codes
1345            sErr = U14ERR_NO1401DRIV;       // if file not found
1346        else if (iErr == EBUSY)
1347            sErr = U14ERR_INUSE;
1348        else
1349            sErr = U14ERR_DRIVCOMMS;        // otherwise assume comms problem
1350    }
1351
1352    return sErr;
1353}
1354#endif
1355/****************************************************************************
1356** U14Open1401
1357** Tries to get the 1401 for use by this application
1358*****************************************************************************/
1359U14API(short) U14Open1401(short n1401)
1360{
1361    long     lRetVal = -1;
1362    short    sErr;
1363    short    hand = 0;
1364    
1365    if ((n1401 < 0) || (n1401 > MAX1401))       // must check the 1401 number
1366        return U14ERR_BAD1401NUM;
1367
1368    szLastName[0] = 0;          /* initialise the error info string */
1369
1370    sErr = U14TryToOpen(n1401, &lRetVal, &hand);
1371    if (sErr == U14ERR_NOERROR)
1372    {
1373        long lDriverVersion = GetDriverVersion(hand);   /* get driver revision */
1374        long lDriverRev = -1;
1375                if (lDriverVersion >= 0)                    /* can use it if all OK */
1376        {
1377            lLastDriverType = (lDriverVersion >> 24) & 0x000000FF;
1378            asDriverType[hand] = (short)lLastDriverType;    /* Drv type */
1379            lLastDriverVersion = lDriverVersion & 0x00FFFFFF;
1380            alDriverVersion[hand] = lLastDriverVersion;     /* Actual version */
1381            lDriverRev = ((lDriverVersion>>16) & 0x00FF);    /* use hi word */
1382        }
1383        else
1384        {
1385            U14Close1401(hand);    /* If there is a problem we should close */
1386            return (short)lDriverVersion;      /* and return the error code */
1387        }
1388    
1389        if (lDriverRev < MINDRIVERMAJREV)       /* late enough version?     */
1390        {
1391            U14Close1401(hand);    /* If there is a problem we should close */
1392            return U14ERR_DRIVTOOOLD;           /* too old                  */
1393        }
1394    
1395        asLastRetCode[hand] = U14ERR_NOERROR; /* Initialise this 1401s info */
1396        abGrabbed[hand] = FALSE;          /* we are not in single step mode */
1397        U14SetTimeout(hand, 3000);      /* set 3 seconds as default timeout */
1398
1399        switch (lRetVal)
1400        {
1401        case DRIVRET_STD:  asType1401[hand] = U14TYPE1401; break;      /* Some we do by hand */
1402        case DRIVRET_U1401:asType1401[hand] = U14TYPEU1401; break;
1403        case DRIVRET_PLUS: asType1401[hand] = U14TYPEPLUS; break;
1404        default:  // For the power upwards, we can calculate the codes
1405                if ((lRetVal >= DRIVRET_POWER) && (lRetVal <= DRIVRET_MAX))
1406                    asType1401[hand] = (short)(lRetVal - (DRIVRET_POWER - U14TYPEPOWER));
1407                else
1408                    asType1401[hand] = U14TYPEUNKNOWN;
1409                break;
1410            }
1411        U14KillIO1401(hand);                     /* resets the 1401 buffers */
1412
1413        if (asType1401[hand] != U14TYPEUNKNOWN)   /* If all seems OK so far */
1414        {
1415            sErr = U14CheckErr(hand);        /* we can check 1401 comms now */
1416            if (sErr != 0)                       /* If this failed to go OK */
1417                U14Reset1401(hand); /* Reset the 1401 to try to sort it out */
1418        }
1419
1420        sErr = U14StateOf1401(hand);/* Get the state of the 1401 for return */
1421        if (sErr == U14ERR_NOERROR)
1422            sErr = hand;                 /* return the handle if no problem */
1423        else
1424            U14Close1401(hand);    /* If there is a problem we should close */
1425    }
1426
1427    return sErr;
1428}
1429
1430
1431/****************************************************************************
1432** U14Close1401
1433** Closes the 1401 so someone else can use it.
1434****************************************************************************/
1435U14API(short) U14Close1401(short hand)
1436{
1437    int j;
1438    int iAreaMask = 0;                          // Mask for active areas
1439    short sErr = CheckHandle(hand);
1440    if (sErr != U14ERR_NOERROR)                 // Check open and in use
1441        return sErr;
1442
1443    for (j = 0; j<MAX_TRANSAREAS; ++j)
1444    {
1445        TGET_TX_BLOCK gtb;
1446        int iReturn = U14GetTransfer(hand, &gtb);   // get area information
1447        if (iReturn == U14ERR_NOERROR)          // ignore if any problem
1448            if (gtb.used)
1449                iAreaMask |= (1 << j);          // set a bit for each used area
1450    }
1451
1452    if (iAreaMask)                              // if any areas are in use
1453    {
1454        U14Reset1401(hand);                     // in case an active transfer running
1455        for (j = 0; j < MAX_TRANSAREAS; ++j)    // Locate locked areas
1456            if (iAreaMask & (1 << j))           // And kill off any transfers
1457                U14UnSetTransfer(hand, (WORD)j);
1458    }
1459
1460#ifdef _IS_WINDOWS_
1461    if (aXferEvent[hand])                       // if this 1401 has an open event handle
1462    {
1463        CloseHandle(aXferEvent[hand]);          // close down the handle
1464        aXferEvent[hand] = NULL;                // and mark it as gone
1465    }
1466
1467    if (CloseHandle(aHand1401[hand]))
1468#endif
1469#ifdef LINUX
1470    if (close(aHand1401[hand]) == 0)            // make sure that close works
1471#endif
1472    {
1473        aHand1401[hand] = INVALID_HANDLE_VALUE;
1474        asType1401[hand] = U14TYPEUNKNOWN;
1475        return U14ERR_NOERROR;
1476    }
1477    else
1478        return U14ERR_BADHAND;     /* BUGBUG GetLastError() ? */
1479}
1480
1481/**************************************************************************
1482**
1483** Look for open 1401s and attempt to close them down. 32-bit windows only.
1484**************************************************************************/
1485U14API(void) U14CloseAll(void)
1486{
1487    int i;
1488    for (i = 0; i < MAX1401; i++)       // Tidy up and make safe
1489        if (aHand1401[i] != INVALID_HANDLE_VALUE)
1490            U14Close1401((short)i);     // Last ditch close 1401
1491}
1492
1493/****************************************************************************
1494** U14Reset1401
1495** Resets the 1401
1496****************************************************************************/
1497U14API(short) U14Reset1401(short hand)
1498{
1499#ifdef _IS_WINDOWS_
1500    TCSBLOCK csBlock;
1501    return U14Control1401(hand, U14_RESET1401, &csBlock);
1502#endif
1503#ifdef LINUX
1504    short sErr = CheckHandle(hand);
1505    return (sErr == U14ERR_NOERROR) ? CED_Reset1401(aHand1401[hand]) : sErr;
1506#endif
1507}
1508
1509/****************************************************************************
1510** U14ForceReset
1511**    Sets the 1401 full reset flag, so that next call to Reset1401 will
1512**     always cause a genuine reset.
1513*****************************************************************************/
1514U14API(short) U14ForceReset(short hand)
1515{
1516#ifdef _IS_WINDOWS_
1517    TCSBLOCK csBlock;
1518    return U14Control1401(hand, U14_FULLRESET, &csBlock);
1519#endif
1520#ifdef LINUX
1521    short sErr = CheckHandle(hand);
1522    return (sErr == U14ERR_NOERROR) ? CED_FullReset(aHand1401[hand]) : sErr;
1523#endif
1524}
1525
1526/****************************************************************************
1527** U14KillIO1401
1528**    Removes any pending IO from the buffers.
1529*****************************************************************************/
1530U14API(short) U14KillIO1401(short hand)
1531{
1532#ifdef _IS_WINDOWS_
1533    TCSBLOCK csBlock;
1534    return U14Control1401(hand, U14_KILLIO1401, &csBlock);
1535#endif
1536#ifdef LINUX
1537    short sErr = CheckHandle(hand);
1538    return (sErr == U14ERR_NOERROR) ? CED_KillIO1401(aHand1401[hand]) : sErr;
1539#endif
1540}
1541
1542
1543/****************************************************************************
1544** U14SendString
1545** Send characters to the 1401
1546*****************************************************************************/
1547U14API(short) U14SendString(short hand, const char* pString)
1548{
1549    int nChars;                     // length we are sending
1550    long lTimeOutTicks;             // when to time out
1551    BOOL bSpaceToSend;              // space to send yet
1552    short sErr = CheckHandle(hand);
1553    if (sErr != U14ERR_NOERROR)
1554        return sErr;
1555
1556    nChars = (int)strlen(pString);  // get string length we want to send
1557    if (nChars > MAXSTRLEN)
1558        return U14ERR_STRLEN;       // String too long
1559
1560#ifdef _IS_WINDOWS_
1561    // To get here we must wait for the buffer to have some space
1562    lTimeOutTicks = U14WhenToTimeOut(hand);
1563    do
1564    {
1565        bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
1566    }
1567    while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
1568
1569    if (!bSpaceToSend)             /* Last-ditch attempt to avoid timeout */
1570    {           /* This can happen with anti-virus or network activity! */
1571        int i;
1572        for (i = 0; (i < 4) && (!bSpaceToSend); ++i)
1573        {
1574            Sleep(25);       /* Give other threads a chance for a while */
1575            bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
1576        }
1577    }
1578
1579    if (asLastRetCode[hand] == U14ERR_NOERROR)      /* no errors? */
1580    {
1581        if (bSpaceToSend)
1582        {
1583            PARAMBLK    rData;
1584            DWORD       dwBytes;
1585            char        tstr[MAXSTRLEN+5];          /* Buffer for chars */
1586
1587            if ((hand < 0) || (hand >= MAX1401))
1588                sErr = U14ERR_BADHAND;
1589            else
1590            {
1591                strcpy(tstr, pString);              /* Into local buf */
1592#ifndef _WIN64
1593                if (!USE_NT_DIOC(hand))             /* Using WIN 95 driver access? */
1594                {
1595                    int iOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_SENDSTRING,
1596                                    NULL, 0, tstr, nChars,
1597                                    &dwBytes, NULL);
1598                    if (iOK)
1599                        sErr = (dwBytes >= (DWORD)nChars) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS;
1600                    else
1601                        sErr = (short)GetLastError();
1602                }
1603                else
1604#endif
1605                {
1606                    int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_SENDSTRING,
1607                                    tstr, nChars,
1608                                    &rData,sizeof(PARAMBLK),&dwBytes,NULL);
1609                    if (iOK && (dwBytes >= sizeof(PARAMBLK)))
1610                        sErr = rData.sState;
1611                    else
1612                        sErr = U14ERR_DRIVCOMMS;
1613                }
1614
1615                if (sErr != U14ERR_NOERROR) // If we have had a comms error
1616                    U14ForceReset(hand);    //  make sure we get real reset
1617            }
1618
1619            return sErr;
1620
1621        }
1622        else
1623        {
1624            U14ForceReset(hand);                //  make sure we get real reset
1625            return U14ERR_TIMEOUT;
1626        }
1627    }
1628    else
1629        return asLastRetCode[hand];
1630#endif
1631#ifdef LINUX
1632    // Just try to send it and see what happens!
1633    sErr = CED_SendString(aHand1401[hand], pString, nChars);
1634    if (sErr != U14ERR_NOOUT)       // if any result except "no room in output"...
1635    {
1636        if (sErr != U14ERR_NOERROR) // if a problem...
1637             U14ForceReset(hand);   // ...make sure we get real reset next time
1638        return sErr;                // ... we are done as nothing we can do
1639    }
1640
1641    // To get here we must wait for the buffer to have some space
1642    lTimeOutTicks = U14WhenToTimeOut(hand);
1643    do
1644    {
1645        bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
1646        if (!bSpaceToSend)
1647            sched_yield();          // let others have fun while we wait
1648    }
1649    while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
1650
1651    if (asLastRetCode[hand] == U14ERR_NOERROR)                /* no errors? */
1652    {
1653        if (bSpaceToSend)
1654        {
1655            sErr = CED_SendString(aHand1401[hand], pString, nChars);
1656            if (sErr != U14ERR_NOERROR) // If we have had a comms error
1657                U14ForceReset(hand);    //  make sure we get real reset
1658            return sErr;
1659        }
1660        else
1661        {
1662            U14ForceReset(hand);                //  make sure we get real reset
1663            return U14ERR_TIMEOUT;
1664        }
1665    }
1666    else
1667        return asLastRetCode[hand];
1668#endif
1669}
1670
1671/****************************************************************************
1672** U14SendChar
1673** Send character to the 1401
1674*****************************************************************************/
1675U14API(short) U14SendChar(short hand, char cChar)
1676{
1677#ifdef _IS_WINDOWS_
1678    char sz[2]=" ";                         // convert to a string and send
1679    sz[0] = cChar;
1680    sz[1] = 0;
1681    return(U14SendString(hand, sz));        // String routines are better
1682#endif
1683#ifdef LINUX
1684    short sErr = CheckHandle(hand);
1685    return (sErr == U14ERR_NOERROR) ? CED_SendChar(aHand1401[hand], cChar) : sErr;
1686#endif
1687}
1688
1689/****************************************************************************
1690** U14GetString
1691** Get a string from the 1401. Returns a null terminated string.
1692** The string is all the characters up to the next CR in the buffer
1693** or the end of the buffer if that comes first. This only returns text
1694** if there is a CR in the buffer. The terminating CR character is removed.
1695** wMaxLen  Is the size of the buffer and must be at least 2 or an error.
1696** Returns  U14ERR_NOERR if OK with the result in the string or a negative
1697**          error code. Any error from the device causes us to set up for
1698**          a full reset.
1699****************************************************************************/
1700U14API(short) U14GetString(short hand, char* pBuffer, WORD wMaxLen)
1701{
1702    short sErr = CheckHandle(hand);
1703    if (sErr != U14ERR_NOERROR)             // If an error...
1704        return sErr;                        // ...bail out!
1705
1706#ifdef _IS_WINDOWS_
1707    if (wMaxLen>1)                          // we need space for terminating 0
1708    {
1709        BOOL bLineToGet;                    // true when a line to get
1710        long lTimeOutTicks = U14WhenToTimeOut(hand);
1711        do
1712            bLineToGet = (BOOL)(U14LineCount(hand) != 0);
1713        while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
1714
1715        if (!bLineToGet)             /* Last-ditch attempt to avoid timeout */
1716        {           /* This can happen with anti-virus or network activity! */
1717            int i;
1718            for (i = 0; (i < 4) && (!bLineToGet); ++i)
1719            {
1720                Sleep(25);       /* Give other threads a chance for a while */
1721                bLineToGet = (BOOL)(U14LineCount(hand) != 0);
1722            }
1723        }
1724
1725        if (bLineToGet)
1726        {
1727            if (asLastRetCode[hand] == U14ERR_NOERROR)     /* all ok so far */
1728            {
1729                DWORD       dwBytes = 0;
1730                *((WORD *)pBuffer) = wMaxLen;       /* set up length */
1731#ifndef _WIN64
1732                if (!USE_NT_DIOC(hand))             /* Win 95 DIOC here ? */
1733                {
1734                    char tstr[MAXSTRLEN+5];         /* Buffer for Win95 chars */
1735                    int iOK;
1736
1737                    if (wMaxLen > MAXSTRLEN)        /* Truncate length */
1738                        wMaxLen = MAXSTRLEN;    
1739
1740                    *((WORD *)tstr) = wMaxLen;      /* set len */
1741
1742                    iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
1743                                    NULL, 0, tstr, wMaxLen+sizeof(short),
1744                                    &dwBytes, NULL);
1745                    if (iOK)                        /* Device IO control OK ? */
1746                    {
1747                        if (dwBytes >= 0)           /* If driver OK */
1748                        {
1749                            strcpy(pBuffer, tstr);
1750                            sErr = U14ERR_NOERROR;
1751                        }
1752                        else
1753                            sErr = U14ERR_DRIVCOMMS;
1754                    }
1755                    else
1756                    {
1757                        sErr = (short)GetLastError();
1758                        if (sErr > 0)               /* Errors are -ve */
1759                            sErr = (short)-sErr;
1760                    }
1761                }
1762                else
1763#endif
1764                {       /* Here for NT, the DLL must own the buffer */
1765                    HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE,wMaxLen+sizeof(short));
1766                    if (hMem)
1767                    {
1768                        char* pMem = (char*)GlobalLock(hMem);
1769                        if (pMem)
1770                        {
1771                            int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
1772                                            NULL, 0, pMem, wMaxLen+sizeof(short),
1773                                            &dwBytes, NULL);
1774                            if (iOK)                /* Device IO control OK ? */
1775                            {
1776                                if (dwBytes >= wMaxLen)
1777                                {
1778                                    strcpy(pBuffer, pMem+sizeof(short));
1779                                    sErr = *((SHORT*)pMem);
1780                                }
1781                                else
1782                                    sErr = U14ERR_DRIVCOMMS;
1783                            }
1784                            else
1785                                sErr = U14ERR_DRIVCOMMS;
1786
1787                            GlobalUnlock(hMem);
1788                        }
1789                        else
1790                            sErr = U14ERR_OUTOFMEMORY;
1791
1792                        GlobalFree(hMem);
1793                    }
1794                    else
1795                        sErr = U14ERR_OUTOFMEMORY;
1796                }
1797
1798                if (sErr == U14ERR_NOERROR)     // If all OK...
1799                    TranslateString(pBuffer);   // ...convert any commas to spaces
1800                else                            // If we have had a comms error...
1801                    U14ForceReset(hand);        // ...make sure we get real reset
1802
1803            }
1804            else
1805                sErr = asLastRetCode[hand];
1806        }
1807        else
1808        {
1809            sErr = U14ERR_TIMEOUT;
1810            U14ForceReset(hand);            //  make sure we get real reset
1811        }
1812    }
1813    else
1814        sErr = U14ERR_BUFF_SMALL;
1815    return sErr;
1816#endif
1817#ifdef LINUX
1818    if (wMaxLen>1)                          // we need space for terminating 0
1819    {
1820        BOOL bLineToGet;                    // true when a line to get
1821        long lTimeOutTicks = U14WhenToTimeOut(hand);
1822        do
1823        {
1824            bLineToGet = (BOOL)(U14LineCount(hand) != 0);
1825            if (!bLineToGet)
1826                sched_yield();
1827
1828        }
1829        while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
1830
1831        if (bLineToGet)
1832        {
1833            sErr = CED_GetString(aHand1401[hand], pBuffer, wMaxLen-1);   // space for terminator
1834            if (sErr >=0)                    // if we were OK...
1835            {
1836                if (sErr >= wMaxLen)         // this should NOT happen unless
1837                    sErr = U14ERR_DRIVCOMMS; // ...driver Comms are very bad
1838                else
1839                {
1840                    pBuffer[sErr] = 0;      // OK, so terminate the string...
1841                    TranslateString(pBuffer);  // ...and convert commas to spaces.
1842                }
1843            }
1844
1845            if (sErr < U14ERR_NOERROR)       // If we have had a comms error
1846                U14ForceReset(hand);            //  make sure we get real reset
1847        }
1848        else
1849        {
1850            sErr = U14ERR_TIMEOUT;
1851            U14ForceReset(hand);            //  make sure we get real reset
1852        }
1853    }
1854    else
1855        sErr = U14ERR_BUFF_SMALL;
1856
1857    return sErr >= U14ERR_NOERROR ? U14ERR_NOERROR : sErr;
1858#endif
1859}
1860
1861/****************************************************************************
1862** U14GetChar
1863** Get a character from the 1401. CR returned as CR.
1864*****************************************************************************/
1865U14API(short) U14GetChar(short hand, char* pcChar)
1866{
1867#ifdef _IS_WINDOWS_
1868    char sz[2];                             // read a very short string
1869    short sErr = U14GetString(hand, sz, 2); // read one char and nul terminate it
1870    *pcChar = sz[0];    // copy to result, NB char translate done by GetString
1871    if (sErr == U14ERR_NOERROR)
1872    {                                       // undo translate of CR to zero
1873        if (*pcChar == '\0')                // by converting back
1874            *pcChar = '\n';                 // What a nasty thing to have to do
1875    }
1876    return sErr;
1877#endif
1878#ifdef LINUX
1879    short sErr = CheckHandle(hand);
1880    if (sErr != U14ERR_NOERROR)             // Check parameters
1881        return sErr;
1882    sErr = CED_GetChar(aHand1401[hand]);    // get one char, if available
1883    if (sErr >= 0)
1884    {
1885        *pcChar = (char)sErr;              // return if it we have one
1886        return U14ERR_NOERROR;              // say all OK
1887    }
1888    else
1889        return sErr;
1890#endif
1891}
1892
1893/****************************************************************************
1894** U14Stat1401
1895** Returns 0 for no lines or error or non zero for something waiting
1896****************************************************************************/
1897U14API(short) U14Stat1401(short hand)
1898{
1899    return ((short)(U14LineCount(hand) > 0));
1900}
1901
1902/****************************************************************************
1903** U14CharCount
1904** Returns the number of characters in the input buffer
1905*****************************************************************************/
1906U14API(short) U14CharCount(short hand)
1907{
1908#ifdef _IS_WINDOWS_
1909    TCSBLOCK csBlock;
1910    short sErr = U14Status1401(hand, U14_STAT1401, &csBlock);
1911    if (sErr == U14ERR_NOERROR)
1912        sErr = csBlock.ints[0];
1913    return sErr;
1914#endif
1915#ifdef LINUX
1916    short sErr = CheckHandle(hand);
1917    return (sErr == U14ERR_NOERROR) ? CED_Stat1401(aHand1401[hand]) : sErr;
1918#endif
1919}
1920
1921/****************************************************************************
1922** U14LineCount
1923** Returns the number of CR characters in the input buffer
1924*****************************************************************************/
1925U14API(short) U14LineCount(short hand)
1926{
1927#ifdef _IS_WINDOWS_
1928    TCSBLOCK csBlock;
1929    short sErr = U14Status1401(hand, U14_LINECOUNT, &csBlock);
1930    if (sErr == U14ERR_NOERROR)
1931        sErr = csBlock.ints[0];
1932    return sErr;
1933#endif
1934#ifdef LINUX
1935    short sErr = CheckHandle(hand);
1936    return (sErr == U14ERR_NOERROR) ? CED_LineCount(aHand1401[hand]) : sErr;
1937#endif
1938}
1939
1940/****************************************************************************
1941** U14GetErrorString
1942** Converts error code supplied to a decent descriptive string.
1943** NOTE: This function may use some extra information stored
1944**       internally in the DLL. This information is stored on a
1945**       per-process basis, but it might be altered if you call
1946**       other functions after getting an error and before using
1947**       this function.
1948****************************************************************************/
1949U14API(void)  U14GetErrorString(short nErr, char* pStr, WORD wMax)
1950{
1951    char    wstr[150];
1952
1953    switch (nErr)              /* Basically, we do this with a switch block */
1954    {
1955    case U14ERR_OFF:
1956        sprintf(wstr, "The 1401 is apparently switched off (code %d)", nErr);
1957        break;
1958
1959    case U14ERR_NC:
1960        sprintf(wstr, "The 1401 is not connected to the interface card (code %d)", nErr);
1961        break;
1962
1963    case U14ERR_ILL:
1964        sprintf(wstr, "The 1401 is not working correctly (code %d)", nErr);
1965        break;
1966
1967    case U14ERR_NOIF:
1968        sprintf(wstr, "The 1401 interface card was not detected (code %d)", nErr);
1969        break;
1970
1971    case U14ERR_TIME:
1972        sprintf(wstr, "The 1401 fails to become ready for use (code %d)", nErr);
1973        break;
1974
1975    case U14ERR_BADSW:
1976        sprintf(wstr, "The 1401 interface card jumpers are incorrect (code %d)", nErr);
1977        break;
1978
1979    case U14ERR_NOINT:
1980        sprintf(wstr, "The 1401 interrupt is not available for use (code %d)", nErr);
1981        break;
1982
1983    case U14ERR_INUSE:
1984        sprintf(wstr, "The 1401 is already in use by another program (code %d)", nErr);
1985        break;
1986
1987    case U14ERR_NODMA:
1988        sprintf(wstr, "The 1401 DMA channel is not available for use (code %d)", nErr);
1989        break;
1990
1991    case U14ERR_BADHAND:
1992        sprintf(wstr, "The application supplied an incorrect 1401 handle (code %d)", nErr);
1993        break;
1994
1995    case U14ERR_BAD1401NUM:
1996        sprintf(wstr, "The application used an incorrect 1401 number (code %d)", nErr);
1997        break;
1998
1999    case U14ERR_NO_SUCH_FN:
2000        sprintf(wstr, "The code passed to the 1401 driver is invalid (code %d)", nErr);
2001        break;
2002
2003    case U14ERR_NO_SUCH_SUBFN:
2004        sprintf(wstr, "The sub-code passed to the 1401 driver is invalid (code %d)", nErr);
2005        break;
2006
2007    case U14ERR_NOOUT:
2008        sprintf(wstr, "No room in buffer for characters for the 1401 (code %d)", nErr);
2009        break;
2010
2011    case U14ERR_NOIN:
2012        sprintf(wstr, "No characters from the 1401 are available (code %d)", nErr);
2013        break;
2014
2015    case U14ERR_STRLEN:
2016        sprintf(wstr, "A string sent to or read from the 1401 was too long (code %d)", nErr);
2017        break;
2018
2019    case U14ERR_LOCKFAIL:
2020        sprintf(wstr, "Failed to lock host memory for data transfer (code %d)", nErr);
2021        break;
2022
2023    case U14ERR_UNLOCKFAIL:
2024        sprintf(wstr, "Failed to unlock host memory after data transfer (code %d)", nErr);
2025        break;
2026
2027    case U14ERR_ALREADYSET:
2028        sprintf(wstr, "The transfer area used is already set up (code %d)", nErr);
2029        break;
2030
2031    case U14ERR_NOTSET:
2032        sprintf(wstr, "The transfer area used has not been set up (code %d)", nErr);
2033        break;
2034
2035    case U14ERR_BADAREA:
2036        sprintf(wstr, "The transfer area number is incorrect (code %d)", nErr);
2037        break;
2038
2039    case U14ERR_NOFILE:
2040        sprintf(wstr, "The command file %s could not be opened (code %d)", szLastName, nErr);
2041        break;
2042
2043    case U14ERR_READERR:
2044        sprintf(wstr, "The command file %s could not be read (code %d)", szLastName, nErr);
2045        break;
2046
2047    case U14ERR_UNKNOWN:
2048        sprintf(wstr, "The %s command resource could not be found (code %d)", szLastName, nErr);
2049        break;
2050
2051    case U14ERR_HOSTSPACE:
2052        sprintf(wstr, "Unable to allocate memory for loading command %s (code %d)", szLastName, nErr);
2053        break;
2054
2055    case U14ERR_LOCKERR:
2056        sprintf(wstr, "Unable to lock memory for loading command %s (code %d)", szLastName, nErr);
2057        break;
2058
2059    case U14ERR_CLOADERR:
2060        sprintf(wstr, "Error in loading command %s, bad command format (code %d)", szLastName, nErr);
2061        break;
2062
2063    case U14ERR_TOXXXERR:
2064        sprintf(wstr, "Error detected after data transfer to or from the 1401 (code %d)", nErr);
2065        break;
2066
2067    case U14ERR_NO386ENH:
2068        sprintf(wstr, "Windows 3.1 is not running in 386 enhanced mode (code %d)", nErr);
2069        break;
2070
2071    case U14ERR_NO1401DRIV:
2072        sprintf(wstr, "The 1401 device driver cannot be found (code %d)\nUSB:   check plugged in and powered\nOther: not installed?", nErr);
2073        break;
2074
2075    case U14ERR_DRIVTOOOLD:
2076        sprintf(wstr, "The 1401 device driver is too old for use (code %d)", nErr);
2077        break;
2078
2079    case U14ERR_TIMEOUT:
2080        sprintf(wstr, "Character transmissions to the 1401 timed-out (code %d)", nErr);
2081        break;
2082
2083    case U14ERR_BUFF_SMALL:
2084        sprintf(wstr, "Buffer for text from the 1401 was too small (code %d)", nErr);
2085        break;
2086
2087    case U14ERR_CBALREADY:
2088        sprintf(wstr, "1401 monitor callback already set up (code %d)", nErr);
2089        break;
2090
2091    case U14ERR_BADDEREG:
2092        sprintf(wstr, "1401 monitor callback deregister invalid (code %d)", nErr);
2093        break;
2094
2095    case U14ERR_DRIVCOMMS:
2096        sprintf(wstr, "1401 device driver communications failed (code %d)", nErr);
2097        break;
2098
2099    case U14ERR_OUTOFMEMORY:
2100        sprintf(wstr, "Failed to allocate or lock memory for text from the 1401 (code %d)", nErr);
2101        break;
2102
2103    default:
2104        sprintf(wstr, "1401 error code %d returned; this code is unknown", nErr);
2105        break;
2106
2107    }
2108    if ((WORD)strlen(wstr) >= wMax-1)  /* Check for string being too long */
2109        wstr[wMax-1] = 0;                          /* and truncate it if so */
2110    strcpy(pStr, wstr);                       /* Return the error string */
2111}
2112
2113/***************************************************************************
2114** U14GetTransfer
2115** Get a TGET_TX_BLOCK describing a transfer area (held in the block)
2116***************************************************************************/
2117U14API(short) U14GetTransfer(short hand, TGET_TX_BLOCK *pTransBlock)
2118{
2119    short sErr = CheckHandle(hand);
2120#ifdef _IS_WINDOWS_
2121    if (sErr == U14ERR_NOERROR)
2122    { 
2123        DWORD dwBytes = 0;
2124        BOOL bOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_GETTRANSFER, NULL, 0, pTransBlock,
2125                              sizeof(TGET_TX_BLOCK), &dwBytes, NULL);
2126    
2127        if (bOK && (dwBytes >= sizeof(TGET_TX_BLOCK)))
2128            sErr = U14ERR_NOERROR;
2129        else
2130            sErr = U14ERR_DRIVCOMMS;
2131    }
2132    return sErr;
2133#endif
2134#ifdef LINUX
2135    return (sErr == U14ERR_NOERROR) ? CED_GetTransfer(aHand1401[hand], pTransBlock) : sErr;
2136#endif
2137}
2138/////////////////////////////////////////////////////////////////////////////
2139// U14WorkingSet
2140// For Win32 only, adjusts process working set so that minimum is at least
2141//  dwMinKb and maximum is at least dwMaxKb.
2142// Return value is zero if all went OK, or a code from 1 to 3 indicating the
2143//  cause of the failure:
2144//
2145//     1 unable to access process (insufficient rights?)
2146//     2 unable to read process working set
2147//     3 unable to set process working set - bad parameters?
2148U14API(short) U14WorkingSet(DWORD dwMinKb, DWORD dwMaxKb)
2149{
2150#ifdef _IS_WINDOWS_
2151    short sRetVal = 0;                      // 0 means all is OK
2152    HANDLE hProcess;
2153    DWORD dwVer = GetVersion();
2154        if (dwVer & 0x80000000)                 // is this not NT?
2155        return 0;                           // then give up right now
2156
2157    // Now attempt to get information on working set size
2158    hProcess = OpenProcess(STANDARD_RIGHTS_REQUIRED |
2159                                  PROCESS_QUERY_INFORMATION |
2160                                  PROCESS_SET_QUOTA,
2161                                  FALSE, _getpid());
2162    if (hProcess)
2163    {
2164        SIZE_T dwMinSize,dwMaxSize;
2165        if (GetProcessWorkingSetSize(hProcess, &dwMinSize, &dwMaxSize))
2166        {
2167            DWORD dwMin = dwMinKb << 10;    // convert from kb to bytes
2168            DWORD dwMax = dwMaxKb << 10;
2169
2170            // if we get here, we have managed to read the current size
2171            if (dwMin > dwMinSize)          // need to change sizes?
2172                dwMinSize = dwMin;
2173
2174            if (dwMax > dwMaxSize)
2175                dwMaxSize = dwMax;
2176
2177            if (!SetProcessWorkingSetSize(hProcess, dwMinSize, dwMaxSize))
2178                sRetVal = 3;                // failed to change size
2179        }
2180        else
2181            sRetVal = 2;                    // failed to read original size
2182
2183        CloseHandle(hProcess);
2184    }
2185    else
2186        sRetVal = 1;            // failed to get handle
2187
2188    return sRetVal;
2189#endif
2190#ifdef LINUX
2191    if (dwMinKb | dwMaxKb)
2192    {
2193        // to stop compiler moaning
2194    }
2195    return U14ERR_NOERROR;
2196#endif
2197}
2198
2199/****************************************************************************
2200** U14UnSetTransfer  Cancels a transfer area
2201** wArea    The index of a block previously used in by SetTransfer
2202*****************************************************************************/
2203U14API(short) U14UnSetTransfer(short hand, WORD wArea)
2204{
2205    short sErr = CheckHandle(hand);
2206#ifdef _IS_WINDOWS_
2207    if (sErr == U14ERR_NOERROR)
2208    {
2209       TCSBLOCK csBlock;
2210       csBlock.ints[0] = (short)wArea;       /* Area number into control block */
2211       sErr = U14Control1401(hand, U14_UNSETTRANSFER, &csBlock);  /* Free area */
2212   
2213       VirtualUnlock(apAreas[hand][wArea], auAreas[hand][wArea]);/* Unlock */
2214       apAreas[hand][wArea] = NULL;                         /* Clear locations */
2215       auAreas[hand][wArea] = 0;
2216    }
2217    return sErr;
2218#endif
2219#ifdef LINUX
2220    return (sErr == U14ERR_NOERROR) ? CED_UnsetTransfer(aHand1401[hand], wArea) : sErr;
2221#endif
2222}
2223
2224/****************************************************************************
2225** U14SetTransArea      Sets an area up to be used for transfers
2226** WORD  wArea     The area number to set up
2227** void *pvBuff    The address of the buffer for the data.
2228** DWORD dwLength  The length of the buffer for the data
2229** short eSz       The element size (used for byte swapping on the Mac)
2230****************************************************************************/
2231U14API(short) U14SetTransArea(short hand, WORD wArea, void *pvBuff,
2232                                          DWORD dwLength, short eSz)
2233{
2234    TRANSFERDESC td;
2235    short sErr = CheckHandle(hand);
2236    if (sErr != U14ERR_NOERROR)
2237        return sErr;
2238    if (wArea >= MAX_TRANSAREAS)                    // Is this a valid area number
2239        return U14ERR_BADAREA;
2240
2241#ifdef _IS_WINDOWS_
2242    assert(apAreas[hand][wArea] == NULL);
2243    assert(auAreas[hand][wArea] == 0);
2244
2245    apAreas[hand][wArea] = pvBuff;                  /* Save data for later */
2246    auAreas[hand][wArea] = dwLength;
2247
2248    if (!VirtualLock(pvBuff, dwLength))             /* Lock using WIN32 calls */
2249    {
2250        apAreas[hand][wArea] = NULL;                /* Clear locations */
2251        auAreas[hand][wArea] = 0;
2252        return U14ERR_LOCKERR;                      /* VirtualLock failed */
2253    }
2254#ifndef _WIN64
2255    if (!USE_NT_DIOC(hand))                         /* Use Win 9x DIOC? */
2256    {
2257        DWORD dwBytes;
2258        VXTRANSFERDESC vxDesc;                      /* Structure to pass to VXD */
2259        vxDesc.wArea = wArea;                       /* Copy across simple params */
2260        vxDesc.dwLength = dwLength;
2261
2262        // Check we are not asking an old driver for more than area 0
2263        if ((wArea != 0) && (U14DriverVersion(hand) < 0x00010002L))
2264            sErr = U14ERR_DRIVTOOOLD;
2265        else
2266        {
2267            vxDesc.dwAddrOfs = (DWORD)pvBuff;       /* 32 bit offset */
2268            vxDesc.wAddrSel  = 0;
2269
2270            if (DeviceIoControl(aHand1401[hand], (DWORD)U14_SETTRANSFER,
2271                                pvBuff,dwLength,    /* Will translate pointer */
2272                                &vxDesc,sizeof(VXTRANSFERDESC),
2273                                &dwBytes,NULL))
2274            {
2275                if (dwBytes >= sizeof(VXTRANSFERDESC)) /* Driver OK ? */
2276                    sErr = U14ERR_NOERROR;
2277                else
2278                    sErr = U14ERR_DRIVCOMMS;        /* Else never got there */
2279            }
2280            else
2281                sErr = (short)GetLastError();
2282        }
2283    }
2284    else
2285#endif
2286    {
2287        PARAMBLK rWork;
2288        DWORD dwBytes;
2289        td.wArea = wArea;     /* Pure NT - put data into struct */
2290        td.lpvBuff = pvBuff;
2291        td.dwLength = dwLength;
2292        td.eSize = 0;                // Dummy element size
2293
2294        if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETTRANSFER,
2295                            &td,sizeof(TRANSFERDESC),
2296                            &rWork,sizeof(PARAMBLK),&dwBytes,NULL))
2297        {
2298            if (dwBytes >= sizeof(PARAMBLK))    // maybe error from driver?
2299                sErr = rWork.sState;            // will report any error
2300            else
2301                sErr = U14ERR_DRIVCOMMS;        // Else never got there
2302        }
2303        else
2304            sErr = U14ERR_DRIVCOMMS;
2305    }
2306
2307    if (sErr != U14ERR_NOERROR)
2308    {
2309        if (sErr != U14ERR_LOCKERR)             // unless lock failed...
2310            VirtualUnlock(pvBuff, dwLength);    // ...release the lock
2311        apAreas[hand][wArea] = NULL;            // Clear locations
2312        auAreas[hand][wArea] = 0;
2313    }
2314
2315    return sErr;
2316#endif
2317#ifdef LINUX
2318    // The strange cast is so that it works in 64 and 32-bit linux as long is 64-bits
2319    // in the 64 bit version.
2320    td.lpvBuff = (long long)((unsigned long)pvBuff);
2321    td.wAreaNum = wArea;
2322    td.dwLength = dwLength;
2323    td.eSize = eSz;                // Dummy element size
2324    return CED_SetTransfer(aHand1401[hand], &td);
2325#endif
2326}
2327
2328/****************************************************************************
2329** U14SetTransferEvent  Sets an event for notification of application
2330** wArea       The transfer area index, from 0 to MAXAREAS-1
2331**    bEvent      True to create an event, false to remove it
2332**    bToHost     Set 0 for notification on to1401 tranfers, 1 for
2333**                notification of transfers to the host PC
2334**    dwStart     The offset of the sub-area of interest
2335**    dwLength    The size of the sub-area of interest
2336**
2337** The device driver will set the event supplied to the signalled state
2338** whenever a DMA transfer to/from the specified area is completed. The
2339** transfer has to be in the direction specified by bToHost, and overlap
2340** that part of the whole transfer area specified by dwStart and dwLength.
2341** It is important that this function is called with bEvent false to release
2342** the event once 1401 activity is finished.
2343**
2344** Returns 1 if an event handle exists, 0 if all OK and no event handle or
2345** a negative code for an error.
2346****************************************************************************/
2347U14API(short) U14SetTransferEvent(short hand, WORD wArea, BOOL bEvent,
2348                                  BOOL bToHost, DWORD dwStart, DWORD dwLength)
2349{
2350#ifdef _IS_WINDOWS_
2351    TCSBLOCK csBlock;
2352    short sErr = U14TransferFlags(hand);        // see if we can handle events
2353    if (sErr >= U14ERR_NOERROR)                 // check handle is OK
2354    {
2355        bEvent = bEvent && ((sErr & U14TF_NOTIFY) != 0); // remove request if we cannot do events
2356        if (wArea >= MAX_TRANSAREAS)            // Check a valid area...
2357            return U14ERR_BADAREA;              // ...and bail of not
2358
2359        // We can hold an event for each area, so see if we need to change the
2360        // state of the event.
2361        if ((bEvent != 0) != (aXferEvent[hand] != 0))    // change of event state?
2362        {
2363            if (bEvent)                         // want one and none present
2364                aXferEvent[hand] = CreateEvent(NULL, FALSE, FALSE, NULL);
2365            else
2366            {
2367                CloseHandle(aXferEvent[hand]);  // clear the existing event
2368                aXferEvent[hand] = NULL;        // and clear handle
2369            }
2370        }
2371
2372        // We have to store the parameters differently for 64-bit operations
2373        //  because a handle is 64 bits long. The drivers know of this and
2374        //  handle the information appropriately.
2375#ifdef _WIN64
2376        csBlock.longs[0] = wArea;               // Pass paramaters into the driver...
2377        if (bToHost != 0)                       // The direction flag is held in the
2378            csBlock.longs[0] |= 0x10000;        //  upper word of the transfer area value
2379        *((HANDLE*)&csBlock.longs[1]) = aXferEvent[hand];  // The event handle is 64-bits
2380        csBlock.longs[3] = dwStart;             // Thankfully these two remain
2381        csBlock.longs[4] = dwLength;            //  as unsigned 32-bit values
2382#else
2383        csBlock.longs[0] = wArea;               // pass paramaters into the driver...
2384        csBlock.longs[1] = (long)aXferEvent[hand];    // ...especially the event handle
2385        csBlock.longs[2] = bToHost;
2386        csBlock.longs[3] = dwStart;
2387        csBlock.longs[4] = dwLength;
2388#endif
2389        sErr = U14Control1401(hand, U14_SETTRANSEVENT, &csBlock);
2390        if (sErr == U14ERR_NOERROR)
2391            sErr = (short)(aXferEvent[hand] != NULL);    // report if we have a flag
2392    }
2393
2394    return sErr;
2395#endif
2396#ifdef LINUX
2397    TRANSFEREVENT te;
2398    short sErr = CheckHandle(hand);
2399    if (sErr != U14ERR_NOERROR)
2400        return sErr;
2401
2402    if (wArea >= MAX_TRANSAREAS)            // Is this a valid area number
2403        return U14ERR_BADAREA;
2404
2405    te.wAreaNum = wArea;                    // copy parameters to the control block
2406    te.wFlags = bToHost ? 1 : 0;            // bit 0 sets the direction
2407    te.dwStart = dwStart;                   // start offset of the event area
2408    te.dwLength = dwLength;                 // size of the event area
2409    te.iSetEvent = bEvent;                  // in Windows, this creates/destroys the event
2410    return CED_SetEvent(aHand1401[hand], &te);
2411#endif
2412}
2413
2414/****************************************************************************
2415** U14TestTransferEvent
2416** Would a U14WaitTransferEvent() call return immediately? return 1 if so,
2417** 0 if not or a negative code if a problem.
2418****************************************************************************/
2419U14API(int) U14TestTransferEvent(short hand, WORD wArea)
2420{
2421#ifdef _IS_WINDOWS_
2422    int iErr = CheckHandle(hand);
2423    if (iErr == U14ERR_NOERROR)
2424    {
2425        if (aXferEvent[hand])           // if a handle is set...
2426            iErr = WaitForSingleObject(aXferEvent[hand], 0) == WAIT_OBJECT_0;
2427    }
2428    return iErr;
2429#endif
2430#ifdef LINUX
2431    short sErr = CheckHandle(hand);
2432    return (sErr == U14ERR_NOERROR) ? CED_TestEvent(aHand1401[hand], wArea) : sErr;
2433#endif
2434}
2435
2436/****************************************************************************
2437** U14WaitTransferEvent
2438** Wait for a transfer event with a timeout.
2439** msTimeOut is 0 for an infinite wait, else it is the maximum time to wait
2440**           in milliseconds in range 0-0x00ffffff.
2441** Returns   If no event handle then return immediately. Else return 1 if
2442**           timed out or 0=event, and a negative code if a problem.
2443****************************************************************************/
2444U14API(int) U14WaitTransferEvent(short hand, WORD wArea, int msTimeOut)
2445{
2446#ifdef _IS_WINDOWS_
2447    int iErr = CheckHandle(hand);
2448    if (iErr == U14ERR_NOERROR)
2449    {
2450        if (aXferEvent[hand])
2451        {
2452            if (msTimeOut == 0)
2453                msTimeOut = INFINITE;
2454            iErr = WaitForSingleObject(aXferEvent[hand], msTimeOut) != WAIT_OBJECT_0;
2455        }
2456        else
2457            iErr = TRUE;                // say we timed out if no event
2458    }
2459    return iErr;
2460#endif
2461#ifdef LINUX
2462    short sErr = CheckHandle(hand);
2463    return (sErr == U14ERR_NOERROR) ? CED_WaitEvent(aHand1401[hand], wArea, msTimeOut) : sErr;
2464#endif
2465}
2466
2467/****************************************************************************
2468** U14SetCircular    Sets an area up for circular DMA transfers
2469** WORD  wArea          The area number to set up
2470** BOOL  bToHost        Sets the direction of data transfer
2471** void *pvBuff        The address of the buffer for the data
2472** DWORD dwLength       The length of the buffer for the data
2473****************************************************************************/
2474U14API(short) U14SetCircular(short hand, WORD wArea, BOOL bToHost,
2475                                                                        void *pvBuff, DWORD dwLength)
2476{
2477    short sErr = CheckHandle(hand);
2478    if (sErr != U14ERR_NOERROR)
2479        return sErr;
2480
2481    if (wArea >= MAX_TRANSAREAS)         /* Is this a valid area number */
2482        return U14ERR_BADAREA;
2483
2484        if (!bToHost)             /* For now, support tohost transfers only */
2485        return U14ERR_BADAREA;            /* best error code I can find */
2486#ifdef _IS_WINDOWS_
2487    assert(apAreas[hand][wArea] == NULL);
2488    assert(auAreas[hand][wArea] == 0);
2489
2490    apAreas[hand][wArea] = pvBuff;              /* Save data for later */
2491    auAreas[hand][wArea] = dwLength;
2492
2493    if (!VirtualLock(pvBuff, dwLength))      /* Lock using WIN32 calls */
2494        sErr = U14ERR_LOCKERR;                    /* VirtualLock failed */
2495    else
2496    {
2497        PARAMBLK rWork;
2498        DWORD dwBytes;
2499        TRANSFERDESC txDesc;
2500        txDesc.wArea = wArea;             /* Pure NT - put data into struct */
2501        txDesc.lpvBuff = pvBuff;
2502        txDesc.dwLength = dwLength;
2503        txDesc.eSize = (short)bToHost;       /* Use this for direction flag */
2504   
2505        if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETCIRCULAR,
2506                           &txDesc, sizeof(TRANSFERDESC),
2507                           &rWork, sizeof(PARAMBLK),&dwBytes,NULL))
2508        {
2509           if (dwBytes >= sizeof(PARAMBLK))          /* error from driver? */
2510               sErr = rWork.sState;         /* No, just return driver data */
2511           else
2512               sErr = U14ERR_DRIVCOMMS;            /* Else never got there */
2513        }
2514        else
2515            sErr = U14ERR_DRIVCOMMS;
2516    }
2517
2518    if (sErr != U14ERR_NOERROR)
2519    {
2520        if (sErr != U14ERR_LOCKERR)
2521            VirtualUnlock(pvBuff, dwLength);         /* Release NT lock */
2522        apAreas[hand][wArea] = NULL;                 /* Clear locations */
2523        auAreas[hand][wArea] = 0;
2524    }
2525
2526    return sErr;
2527#endif
2528#ifdef LINUX
2529    else
2530    {
2531        TRANSFERDESC td;
2532        td.lpvBuff = (long long)((unsigned long)pvBuff);
2533        td.wAreaNum = wArea;
2534        td.dwLength = dwLength;
2535        td.eSize = (short)bToHost;       /* Use this for direction flag */
2536        return CED_SetCircular(aHand1401[hand], &td);
2537    }
2538#endif
2539}
2540
2541/****************************************************************************
2542** Function  GetCircBlk returns the size (& start offset) of the next
2543**           available block of circular data.
2544****************************************************************************/
2545U14API(int) U14GetCircBlk(short hand, WORD wArea, DWORD *pdwOffs)
2546{
2547    int lErr = CheckHandle(hand);
2548    if (lErr != U14ERR_NOERROR)
2549        return lErr;
2550
2551    if (wArea >= MAX_TRANSAREAS)            // Is this a valid area number?
2552        return U14ERR_BADAREA;
2553    else
2554    {
2555#ifdef _IS_WINDOWS_
2556        PARAMBLK rWork;
2557        TCSBLOCK csBlock;
2558        DWORD dwBytes;
2559        csBlock.longs[0] = wArea;               // Area number into control block
2560        rWork.sState = U14ERR_DRIVCOMMS;
2561        if (DeviceIoControl(aHand1401[hand], (DWORD)U14_GETCIRCBLK, &csBlock, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
2562           (dwBytes >= sizeof(PARAMBLK)))
2563            lErr = rWork.sState;
2564        else
2565            lErr = U14ERR_DRIVCOMMS;
2566   
2567        if (lErr == U14ERR_NOERROR)             // Did everything go OK?
2568        {                                       // Yes, we can pass the results back
2569            lErr = rWork.csBlock.longs[1];      // Return the block information
2570            *pdwOffs = rWork.csBlock.longs[0];  // Offset is first in array
2571        }
2572#endif
2573#ifdef LINUX
2574        TCIRCBLOCK cb;
2575        cb.nArea = wArea;                       // Area number into control block
2576        cb.dwOffset = 0;
2577        cb.dwSize = 0;
2578        lErr = CED_GetCircBlock(aHand1401[hand], &cb);
2579        if (lErr == U14ERR_NOERROR)             // Did everything go OK?
2580        {                                       // Yes, we can pass the results back
2581            lErr = cb.dwSize;                   // return the size
2582            *pdwOffs = cb.dwOffset;             // and the offset
2583        }
2584#endif
2585    }
2586    return lErr;
2587}
2588
2589/****************************************************************************
2590** Function  FreeCircBlk marks the specified area of memory as free for
2591**           resuse for circular transfers and returns the size (& start
2592**           offset) of the next available block of circular data.
2593****************************************************************************/
2594U14API(int) U14FreeCircBlk(short hand, WORD wArea, DWORD dwOffs, DWORD dwSize,
2595                                        DWORD *pdwOffs)
2596{
2597    int lErr = CheckHandle(hand);
2598    if (lErr != U14ERR_NOERROR)
2599        return lErr;
2600
2601    if (wArea < MAX_TRANSAREAS)                 // Is this a valid area number
2602    {
2603#ifdef _IS_WINDOWS_
2604        PARAMBLK rWork;
2605        TCSBLOCK csBlock;
2606        DWORD dwBytes;
2607        csBlock.longs[0] = wArea;               // Area number into control block
2608        csBlock.longs[1] = dwOffs;
2609        csBlock.longs[2] = dwSize;
2610        rWork.sState = U14ERR_DRIVCOMMS;
2611        if (DeviceIoControl(aHand1401[hand], (DWORD)U14_FREECIRCBLK, &csBlock, sizeof(TCSBLOCK),
2612                           &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
2613           (dwBytes >= sizeof(PARAMBLK)))
2614           lErr = rWork.sState;
2615        else
2616           lErr = U14ERR_DRIVCOMMS;
2617       if (lErr == U14ERR_NOERROR)             // Did everything work OK?
2618       {                                       // Yes, we can pass the results back
2619           lErr = rWork.csBlock.longs[1];      // Return the block information
2620           *pdwOffs = rWork.csBlock.longs[0];  // Offset is first in array
2621       }
2622#endif
2623#ifdef LINUX
2624        TCIRCBLOCK cb;
2625        cb.nArea = wArea;                       // Area number into control block
2626        cb.dwOffset = dwOffs;
2627        cb.dwSize = dwSize;
2628    
2629        lErr = CED_FreeCircBlock(aHand1401[hand], &cb);
2630        if (lErr == U14ERR_NOERROR)             // Did everything work OK?
2631        {                                       // Yes, we can pass the results back
2632            lErr = cb.dwSize;                   // Return the block information
2633            *pdwOffs = cb.dwOffset;             // Offset is first in array
2634        }
2635#endif
2636    }
2637    else
2638        lErr = U14ERR_BADAREA;
2639
2640    return lErr;
2641}
2642
2643/****************************************************************************
2644** Transfer
2645** Transfer moves data to 1401 or to host
2646** Assumes memory is allocated and locked,
2647** which it should be to get a pointer
2648*****************************************************************************/
2649static short Transfer(short hand, BOOL bTo1401, char* pData,
2650                       DWORD dwSize, DWORD dw1401, short eSz)
2651{
2652    char strcopy[MAXSTRLEN+1];          // to hold copy of work string
2653    short sResult = U14SetTransArea(hand, 0, (void *)pData, dwSize, eSz);
2654    if (sResult == U14ERR_NOERROR)      // no error
2655    {
2656        sprintf(strcopy,                // data offset is always 0
2657                "TO%s,$%X,$%X,0;", bTo1401 ? "1401" : "HOST", dw1401, dwSize);
2658
2659        U14SendString(hand, strcopy);   // send transfer string
2660
2661        sResult = U14CheckErr(hand);    // Use ERR command to check for done
2662        if (sResult > 0)
2663            sResult = U14ERR_TOXXXERR;  // If a 1401 error, use this code
2664
2665        U14UnSetTransfer(hand, 0);
2666    }
2667    return sResult;
2668}
2669
2670/****************************************************************************
2671** Function  ToHost transfers data into the host from the 1401
2672****************************************************************************/
2673U14API(short) U14ToHost(short hand, char* pAddrHost, DWORD dwSize,
2674                                            DWORD dw1401, short eSz)
2675{
2676    short sErr = CheckHandle(hand);
2677    if ((sErr == U14ERR_NOERROR) && dwSize) // TOHOST is a constant
2678        sErr = Transfer(hand, TOHOST, pAddrHost, dwSize, dw1401, eSz);
2679    return sErr;
2680}
2681
2682/****************************************************************************
2683** Function  To1401 transfers data into the 1401 from the host
2684****************************************************************************/
2685U14API(short) U14To1401(short hand, const char* pAddrHost,DWORD dwSize,
2686                                    DWORD dw1401, short eSz)
2687{
2688    short sErr = CheckHandle(hand);
2689    if ((sErr == U14ERR_NOERROR) && dwSize) // TO1401 is a constant
2690        sErr = Transfer(hand, TO1401, (char*)pAddrHost, dwSize, dw1401, eSz);
2691    return sErr;
2692}
2693
2694/****************************************************************************
2695** Function  LdCmd    Loads a command from a full path or just a file
2696*****************************************************************************/
2697#ifdef _IS_WINDOWS_
2698#define file_exist(name) (_access(name, 0) != -1)
2699#define file_open(name) _lopen(name, OF_READ)
2700#define file_close(h)   _lclose(h)
2701#define file_seek(h, pos) _llseek(h, pos, FILE_BEGIN) 
2702#define file_read(h, buffer, size) (_lread(h, buffer, size) == size)
2703#endif
2704#ifdef LINUX
2705#define file_exist(name) (access(name, F_OK) != -1)
2706#define file_open(name) open(name, O_RDONLY)
2707#define file_close(h)   close(h)
2708#define file_seek(h, pos) lseek(h, pos, SEEK_SET) 
2709#define file_read(h, buffer, size) (read(h, buffer, size) == (ssize_t)size)
2710static DWORD GetModuleFileName(void* dummy, char* buffer, int max)
2711{
2712    // The following works for Linux systems with a /proc file system.
2713    char szProcPath[32];
2714    sprintf(szProcPath, "/proc/%d/exe", getpid());  // attempt to read link
2715    if (readlink(szProcPath, buffer, max) != -1)
2716    {
2717        dirname (buffer);
2718        strcat  (buffer, "/");
2719        return strlen(buffer);
2720    }
2721    return 0;
2722}
2723#endif
2724
2725U14API(short) U14LdCmd(short hand, const char* command)
2726{
2727    char strcopy[MAXSTRLEN+1];      // to hold copy of work string
2728    BOOL bGotIt = FALSE;            // have we found the command file?
2729    int iFHandle;                   // file handle of command
2730#define FNSZ 260
2731    char filnam[FNSZ];              // space to build name in
2732    char szCmd[25];                 // just the command name with extension
2733
2734    short sErr = CheckHandle(hand);
2735    if (sErr != U14ERR_NOERROR)
2736        return sErr;
2737
2738    if (strchr(command, '.') != NULL)       // see if we have full name
2739    {
2740        if (file_exist(command))            // If the file exists
2741        {
2742            strcpy(filnam, command);        // use name as is
2743            bGotIt = TRUE;                  // Flag no more searching
2744        }
2745        else                                // not found, get file name for search
2746        {
2747            char* pStr = strrchr(command, PATHSEP);  // Point to last separator
2748            if (pStr != NULL)               // Check we got it
2749            {
2750                pStr++;                     // move past the backslash
2751                strcpy(szCmd, pStr);        // copy file name as is
2752            }
2753            else
2754                strcpy(szCmd, command);     // use as is
2755        }
2756    }
2757    else    // File extension not supplied, so build the command file name
2758    {
2759        char szExt[8];
2760        strcpy(szCmd, command);             // Build command file name
2761        ExtForType(asType1401[hand], szExt);// File extension string
2762        strcat(szCmd, szExt);               // add it to the end
2763    }
2764
2765    // Next place to look is in the 1401 folder in the same place as the
2766    // application was run from.
2767    if (!bGotIt)                            // Still not got it?
2768    {
2769        DWORD dwLen = GetModuleFileName(NULL, filnam, FNSZ); // Get app path
2770        if (dwLen > 0)                      // and use it as path if found
2771        {
2772            char* pStr = strrchr(filnam, PATHSEP);    // Point to last separator
2773            if (pStr != NULL)
2774            {
2775                *(++pStr) = 0;                  // Terminate string there
2776                if (strlen(filnam) < FNSZ-6)    // make sure we have space
2777                {
2778                    strcat(filnam, "1401" PATHSEPSTR);  // add in 1401 subdir
2779                    strcat(filnam,szCmd);
2780                    bGotIt = (BOOL)file_exist(filnam);  // See if file exists
2781                }
2782            }
2783        }
2784    }
2785
2786    // Next place to look is in whatever path is set by the 1401DIR environment
2787    // variable, if it exists.
2788    if (!bGotIt)                            // Need to do more searches?/
2789    {
2790        char* pStr = getenv("1401DIR");     // Try to find environment var
2791        if (pStr != NULL)                   // and use it as path if found
2792        {
2793            strcpy(filnam, pStr);                   // Use path in environment
2794            if (filnam[strlen(filnam)-1] != PATHSEP)// We need separator
2795                strcat(filnam, PATHSEPSTR);
2796            strcat(filnam, szCmd);
2797            bGotIt = (BOOL)file_exist(filnam); // Got this one?
2798        }
2799    }
2800
2801    // Last place to look is the default location.
2802    if (!bGotIt)                        // Need to do more searches?
2803    {
2804        strcpy(filnam, DEFCMDPATH);     // Use default path
2805        strcat(filnam, szCmd);
2806        bGotIt = file_exist(filnam);    // Got this one?
2807    }
2808
2809    iFHandle = file_open(filnam);
2810    if (iFHandle == -1)
2811        sErr = U14ERR_NOFILE;
2812    else
2813    {                                   // first read in the header block
2814        CMDHEAD rCmdHead;               // to hold the command header
2815        if (file_read(iFHandle, &rCmdHead, sizeof(CMDHEAD)))
2816        {
2817            size_t nComSize = rCmdHead.wCmdSize;
2818            char* pMem = malloc(nComSize);
2819            if (pMem != NULL)
2820            {
2821                file_seek(iFHandle, sizeof(CMDHEAD));
2822                if (file_read(iFHandle, pMem, (UINT)nComSize))
2823                {
2824                    sErr = U14SetTransArea(hand, 0, (void *)pMem, (DWORD)nComSize, ESZBYTES);
2825                    if (sErr == U14ERR_NOERROR)
2826                    {
2827                        sprintf(strcopy, "CLOAD,0,$%X;", (int)nComSize);
2828                        sErr = U14SendString(hand, strcopy);
2829                        if (sErr == U14ERR_NOERROR)
2830                        {
2831                            sErr = U14CheckErr(hand);     // Use ERR to check for done
2832                            if (sErr > 0)
2833                                sErr = U14ERR_CLOADERR;   // If an error, this code
2834                        }
2835                        U14UnSetTransfer(hand, 0);  // release transfer area
2836                    }
2837                }
2838                else
2839                    sErr = U14ERR_READERR;
2840                free(pMem);
2841            }
2842            else
2843                sErr = U14ERR_HOSTSPACE;    // memory allocate failed
2844        }
2845        else
2846            sErr = U14ERR_READERR;
2847
2848        file_close(iFHandle);               // close the file
2849    }
2850
2851    return sErr;
2852}
2853
2854
2855/****************************************************************************
2856** Ld
2857** Loads a command into the 1401
2858** Returns NOERROR code or a long with error in lo word and index of
2859** command that failed in high word
2860****************************************************************************/
2861U14API(DWORD) U14Ld(short hand, const char* vl, const char* str)
2862{
2863    DWORD dwIndex = 0;              // index to current command
2864    long lErr = U14ERR_NOERROR;     // what the error was that went wrong
2865    char strcopy[MAXSTRLEN+1];      // stores unmodified str parameter
2866    char szFExt[8];                 // The command file extension
2867    short sErr = CheckHandle(hand);
2868    if (sErr != U14ERR_NOERROR)
2869        return sErr;
2870
2871    ExtForType(asType1401[hand], szFExt);   // File extension string
2872    strcpy(strcopy, str);               // to avoid changing original
2873
2874    // now break out one command at a time and see if loaded
2875    if (*str)                           // if anything there
2876    {
2877        BOOL bDone = FALSE;             // true when finished all commands
2878        int iLoop1 = 0;                 // Point at start of string for command name
2879        int iLoop2 = 0;                 // and at start of str parameter
2880        do                              // repeat until end of str
2881        {
2882            char filnam[MAXSTRLEN+1];   // filename to use
2883            char szFName[MAXSTRLEN+1];  // filename work string
2884
2885            if (!strcopy[iLoop1])       // at the end of the string?
2886                bDone = TRUE;           // set the finish flag
2887
2888            if (bDone || (strcopy[iLoop1] == ','))  // end of cmd?
2889            {
2890                U14LONG er[5];                  // Used to read back error results
2891                ++dwIndex;                      // Keep count of command number, first is 1
2892                szFName[iLoop2]=(char)0;        // null terminate name of command
2893
2894                strncpy(szLastName, szFName, sizeof(szLastName));    // Save for error info
2895                szLastName[sizeof(szLastName)-1] = 0;
2896                strncat(szLastName, szFExt, sizeof(szLastName));     // with extension included
2897                szLastName[sizeof(szLastName)-1] = 0;
2898
2899                U14SendString(hand, szFName);   // ask if loaded
2900                U14SendString(hand, ";ERR;");   // add err return
2901
2902                lErr = U14LongsFrom1401(hand, er, 5);
2903                if (lErr > 0)
2904                {
2905                    lErr = U14ERR_NOERROR;
2906                    if (er[0] == 255)           // if command not loaded at all
2907                    {
2908                        if (vl && *vl)          // if we have a path name
2909                        {
2910                            strcpy(filnam, vl);
2911                            if (strchr("\\/:", filnam[strlen(filnam)-1]) == NULL)
2912                                strcat(filnam, PATHSEPSTR); // add separator if none found
2913                            strcat(filnam, szFName);    // add the file name
2914                            strcat(filnam, szFExt);     // and extension
2915                        }
2916                        else
2917                            strcpy(filnam, szFName);    // simple name
2918
2919                        lErr = U14LdCmd(hand, filnam);  // load cmd
2920                        if (lErr != U14ERR_NOERROR)     // spot any errors
2921                            bDone = TRUE;               // give up if an error
2922                    }
2923                }
2924                else
2925                    bDone = TRUE;       // give up if an error
2926
2927                iLoop2 = 0;             // Reset pointer to command name string
2928                ++iLoop1;               // and move on through str parameter
2929            }
2930            else
2931                szFName[iLoop2++] = strcopy[iLoop1++];  // no command end, so copy 1 char
2932        }
2933        while (!bDone);
2934    }
2935
2936    if (lErr == U14ERR_NOERROR)
2937    {
2938        szLastName[0] = 0;      // No error, so clean out command name here
2939        return lErr;
2940    }
2941    else
2942        return ((dwIndex<<16) | ((DWORD)lErr & 0x0000FFFF));
2943}
2944
2945// Initialise the library (if not initialised) and return the library version
2946U14API(int) U14InitLib(void)
2947{
2948    int iRetVal = U14LIB_VERSION;
2949    if (iAttached == 0)         // only do this the first time please
2950    {
2951        int i;
2952#ifdef _IS_WINDOWS_
2953        int j;
2954        DWORD   dwVersion = GetVersion();
2955        bWindows9x = FALSE;                  // Assume not Win9x
2956
2957        if (dwVersion & 0x80000000)                 // if not windows NT
2958        {
2959            if ((LOBYTE(LOWORD(dwVersion)) < 4) &&  // if Win32s or...
2960                 (HIBYTE(LOWORD(dwVersion)) < 95))  // ...below Windows 95
2961            iRetVal = 0;                            // We do not support this
2962        else
2963            bWindows9x = TRUE;                      // Flag we have Win9x
2964        }
2965#endif
2966        
2967        for (i = 0; i < MAX1401; i++)               // initialise the device area
2968        {
2969            aHand1401[i] = INVALID_HANDLE_VALUE;    // Clear handle values
2970            asType1401[i] = U14TYPEUNKNOWN;         // and 1401 type codes
2971            alTimeOutPeriod[i] = 3000;              // 3 second timeouts
2972#ifdef _IS_WINDOWS_
2973#ifndef _WIN64
2974            abUseNTDIOC[i] = (BOOL)!bWindows9x;
2975#endif
2976            aXferEvent[i] = NULL;                   // there are no Xfer events
2977            for (j = 0; j < MAX_TRANSAREAS; j++)    // Clear out locked area info
2978            {
2979                apAreas[i][j] = NULL;
2980                auAreas[i][j] = 0;
2981            }
2982#endif
2983        }
2984    }
2985    return iRetVal;
2986}
2987
2988///--------------------------------------------------------------------------------
2989/// Functions called when the library is loaded and unloaded to give us a chance to
2990/// setup the library.
2991
2992
2993#ifdef _IS_WINDOWS_
2994#ifndef U14_NOT_DLL
2995/****************************************************************************
2996** FUNCTION: DllMain(HANDLE, DWORD, LPVOID)
2997** LibMain is called by Windows when the DLL is initialized, Thread Attached,
2998** and other times. Refer to SDK documentation, as to the different ways this
2999** may be called.
3000****************************************************************************/
3001INT APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved)
3002{
3003    int iRetVal = 1;
3004
3005    switch (ul_reason_being_called)
3006    {
3007    case DLL_PROCESS_ATTACH:
3008        iRetVal = U14InitLib() > 0;         // does nothing if iAttached != 0
3009        ++iAttached;                        // count times attached
3010        break;
3011
3012    case DLL_PROCESS_DETACH:
3013        if (--iAttached == 0)               // last man out?
3014            U14CloseAll();                  // release all open handles
3015        break;
3016    }
3017    return iRetVal;
3018
3019    UNREFERENCED_PARAMETER(lpReserved);
3020}
3021#endif
3022#endif
3023#ifdef LINUX
3024void __attribute__((constructor)) use1401_load(void)
3025{
3026    U14InitLib();
3027    ++iAttached;
3028}
3029
3030void __attribute__((destructor)) use1401_unload(void)
3031{
3032        if (--iAttached == 0)               // last man out?
3033            U14CloseAll();                  // release all open handles
3034}
3035#endif
3036