3036 lines
114 KiB
C
3036 lines
114 KiB
C
/****************************************************************************
|
|
** use1401.c
|
|
** Copyright (C) Cambridge Electronic Design Ltd, 1992-2010
|
|
**
|
|
** This program is free software; you can redistribute it and/or
|
|
** modify it under the terms of the GNU General Public License
|
|
** as published by the Free Software Foundation; either version 2
|
|
** of the License, or (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**
|
|
** Contact CED: Cambridge Electronic Design Limited, Science Park, Milton Road
|
|
** Cambridge, CB6 0FE.
|
|
** www.ced.co.uk
|
|
** greg@ced.co.uk
|
|
**
|
|
** Title: USE1401.C
|
|
** Version: 4.00
|
|
** Author: Paul Cox, Tim Bergel, Greg Smith
|
|
**
|
|
** The code was vigorously pruned in DEC 2010 to remove the macintosh options
|
|
** and to get rid of the 16-bit support. It has also been aligned with the
|
|
** Linux version. See CVS for revisions. This will work for Win 9x onwards.
|
|
****************************************************************************
|
|
**
|
|
** Notes on Windows interface to driver
|
|
** ************************************
|
|
**
|
|
** Under Windows 9x and NT, Use1401 uses DeviceIoControl to get access to
|
|
** the 1401 driver. This has parameters for the device handle, the function
|
|
** code, an input pointer and byte count, an output pointer and byte count
|
|
** and a pointer to a DWORD to hold the output byte count. Note that input
|
|
** and output are from the point-of-view of the driver, so the output stuff
|
|
** is used to read values from the 1401, not send to the 1401. The use of
|
|
** these parameters varies with the function in use and the operating
|
|
** system; there are five separate DIOC calls SendString, GetString and
|
|
** SetTransferArea all have their own specialised calls, the rest use the
|
|
** Status1401 or Control1401 functions.
|
|
**
|
|
** There are two basic styles of DIOC call used, one for Win9x VxD drivers
|
|
** and one for NT Kernel-mode and WDM drivers (see below for tables showing
|
|
** the different parameters used. The array bUseNTDIOC[] selects between
|
|
** these two calling styles.
|
|
**
|
|
** Function codes
|
|
** In Win3.x, simple function codes from 0 to 40 were used, shifted left 8
|
|
** bits with a sub-function code in the lower 8 bits. These were also used
|
|
** in the Windows 95 driver, though we had to add 1 to the code value to
|
|
** avoid problems (Open from CreateFile is zero), and the sub-function code
|
|
** is now unused. We found that this gave some problems with Windows 98
|
|
** as the function code values are reserved by microsoft, so we switched to
|
|
** using the NT function codes instead. The NT codes are generated using the
|
|
** CTL_CODE macro, essentially this gives 0x80012000 | (func << 2), where
|
|
** func is the original 0 to 34 value. The driver will handle both types of
|
|
** code and Use1432 only uses the NT codes if it knows the driver is new
|
|
** enough. The array bUseNTCodes[] holds flags on the type of codes required.
|
|
** GPS/TDB Dec 2010: we removed the bUseNTCodes array as this is always true
|
|
** as we no longer support ancient versions.
|
|
**
|
|
** The CreateFile and CloseFile function calls are also handled
|
|
** by DIOC, using the special function codes 0 and -1 respectively.
|
|
**
|
|
** Input pointer and buffer size
|
|
** These are intended for data sent to the device driver. In nearly all cases
|
|
** they are unused in calls to the Win95 driver, the NT driver uses them
|
|
** for all information sent to the driver. The table below shows the pointer
|
|
** and byte count used for the various calls:
|
|
**
|
|
** Win 95 Win NT
|
|
** SendString NULL, 0 pStr, nStr
|
|
** GetString NULL, 0 NULL, 0
|
|
** SetTransferArea pBuf, nBuf (unused?) pDesc, nDesc
|
|
** GetTransfer NULL, 0 NULL, 0
|
|
** Status1401 NULL, 0 NULL, 0
|
|
** Control1401 NULL, 0 pBlk, nBlk
|
|
**
|
|
** pStr and nStr are pointers to a char buffer and the buffer length for
|
|
** string I/O, note that these are temporary buffers owned by the DLL, not
|
|
** application memory, pBuf and nBuf are the transfer area buffer (I think
|
|
** these are unused), pDesc and nDesc are the TRANSFERDESC structure, pBlk
|
|
** and nBlk are the TCSBLOCK structure.
|
|
**
|
|
**
|
|
** Output pointer and buffer size
|
|
** These are intended for data read from the device driver. These are used
|
|
** for almost all information sent to the Win95 driver, the NT driver uses
|
|
** them for information read from the driver, chiefly the error code. The
|
|
** table below shows the pointer and byte count used for the various calls:
|
|
**
|
|
** Win 95 Win NT
|
|
** SendString pStr, nStr pPar, nPar
|
|
** GetString pStr, nStr+2 pStr, nStr+2
|
|
** SetTransferArea pDesc, nDesc pPar, nPar
|
|
** GetTransfer pGet, nGet pGet, nGet
|
|
** Status1401 pBlk, nBlk pPar, nPar
|
|
** Control1401 pBlk, nBlk pPar, nPar
|
|
**
|
|
** pStr and nStr are pointers to a char buffer and the buffer length for
|
|
** string I/O, the +2 for GetString refers to two spare bytes at the start
|
|
** used to hold the string length and returning an error code for NT. Note
|
|
** again that these are (and must be) DLL-owned temporary buffers. pPar
|
|
** and nPar are a PARAM structure used in NT (it holds an error code and a
|
|
** TCSBLOCK structure). pDesc and nDesc are the VXTRANSFERDESC structure,
|
|
** pBlk and nBlk are the TCSBLOCK structure. pGet and nGet indicate the
|
|
** TGET_TX_BLOCK structure used for GetTransfer.
|
|
**
|
|
**
|
|
** The output byte count
|
|
** Both drivers return the output buffer size here, regardless of the actual
|
|
** bytes output. This is used to check that we did get through to the driver.
|
|
**
|
|
** Multiple 1401s
|
|
** **************
|
|
**
|
|
** We have code that tries to support the use of multiple 1401s, but there
|
|
** are problems: The lDriverVersion and lDriverType variables are global, not
|
|
** per-1401 (a particular problem as the U14 functions that use them don't
|
|
** have a hand parameter). In addition, the mechansim for finding a free
|
|
** 1401 depends upon the 1401 device driver open operation failing if it's
|
|
** already in use, which doesn't always happen, particularly with the VxDs.
|
|
** The code in TryToOpen tries to fix this by relying on TYPEOF1401 to detect
|
|
** the 1401-in-use state - the VxDs contain special code to help this. This is
|
|
** working OK but multiple 1401 support works better with the Win2000 drivers.
|
|
**
|
|
** USB driver
|
|
** **********
|
|
**
|
|
** The USB driver, which runs on both Win98 and NT2000, uses the NT-style
|
|
** calling convention, both for the DIOC codes and the DIOC parameters. The
|
|
** TryToOpen function has been altered to look for an NT driver first in
|
|
** the appropriate circumstances, and to set the driver DIOC flags up in
|
|
** the correct state.
|
|
**
|
|
** Adding a new 1401 type - now almost nothing to do
|
|
** *************************************************
|
|
**
|
|
** The 1401 types are defined by a set of U14TYPExxxx codes in USE1401.H.
|
|
** You should add a new one of these to keep things tidy for applications.
|
|
**
|
|
** DRIVERET_MAX (below) specifies the maximum allowed type code from the
|
|
** 1401 driver; I have set this high to accomodate as yet undesigned 1401
|
|
** types. Similarly, as long as the command file names follow the ARM,
|
|
** ARN, ARO sequence, these are calculated by the ExtForType function, so
|
|
** you don't need to do anything here either.
|
|
**
|
|
** Version number
|
|
** **************
|
|
** The new U14InitLib() function returns 0 if the OS is incapable of use,
|
|
** otherwise is returns the version of the USE1401 library. This is done
|
|
** in three parts: Major(31-24).Minor(23-16).Revision.(15-0) (brackets are
|
|
** the bits used). The Major number starts at 2 for the first revision with
|
|
** the U14InitLib() function. Changes to the Major version means that we
|
|
** have broken backwards compatibility. Minor number changes mean that we
|
|
** have added new functionality that does not break backwards compatibility.
|
|
** we starts at 0. Revision changes mean we have fixed something. Each index
|
|
** returns to 0 when a higer one changes.
|
|
*/
|
|
#define U14LIB_MAJOR 4
|
|
#define U14LIB_MINOR 0
|
|
#define U14LIB_REVISION 0
|
|
#define U14LIB_VERSION ((U14LIB_MAJOR<<24) | (U14LIB_MINOR<<16) | U14LIB_REVISION)
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "USE1401.H"
|
|
|
|
#ifdef _IS_WINDOWS_
|
|
#include <io.h>
|
|
#include <windows.h>
|
|
#pragma warning(disable: 4100) /* Disable "Unused formal parameter" warning */
|
|
#include <assert.h>
|
|
#include "process.h"
|
|
|
|
|
|
#define sprintf wsprintf
|
|
#define PATHSEP '\\'
|
|
#define PATHSEPSTR "\\"
|
|
#define DEFCMDPATH "\\1401\\" // default command path if all else fails
|
|
#define MINDRIVERMAJREV 1 // minimum driver revision level we need
|
|
#define __packed // does nothing in Windows
|
|
|
|
#include "use14_ioc.h" // links to device driver stuff
|
|
#endif
|
|
|
|
#ifdef LINUX
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#include <errno.h>
|
|
#include <sys/time.h>
|
|
#include <sched.h>
|
|
#include <libgen.h>
|
|
#define PATHSEP '/'
|
|
#define PATHSEPSTR "/"
|
|
#define DEFCMDPATH "/var/1401/" // default command path if all else fails
|
|
#define MINDRIVERMAJREV 2 // minimum driver revision level we need
|
|
|
|
#include "ced_ioctl.h" // links to device driver stuff
|
|
#endif
|
|
|
|
#define MAX1401 8 // The number of 1401s that can be supported
|
|
|
|
/*
|
|
** These are the 1401 type codes returned by the driver, they are a slightly
|
|
** odd sequence & start for reasons of compatability with the DOS driver.
|
|
** The maximum code value is the upper limit of 1401 device types.
|
|
*/
|
|
#define DRIVRET_STD 4 // Codes for 1401 types matching driver values
|
|
#define DRIVRET_U1401 5 // This table does not need extending, as
|
|
#define DRIVRET_PLUS 6 // we can calculate values now.
|
|
#define DRIVRET_POWER 7 // but we need all of these values still
|
|
#define DRIVRET_MAX 26 // Maximum tolerated code - future designs
|
|
|
|
/*
|
|
** These variables store data that will be used to generate the last
|
|
** error string. For now, a string will hold the 1401 command file name.
|
|
*/
|
|
static char szLastName[20]; // additional text information
|
|
|
|
/*
|
|
** Information stored per handle. NBNB, driverType and DriverVersion used to be
|
|
** only stored once for all handles... i.e. nonsensical. This change means that
|
|
** three U14...() calls now include handles that were previously void. We have
|
|
** set a constructor and a destructor call for the library (see the end) to
|
|
** initialise important structures, or call use1401_load().
|
|
*/
|
|
static short asDriverType[MAX1401] = {0};
|
|
static int lLastDriverVersion = U14ERR_NO1401DRIV;
|
|
static int lLastDriverType = U14TYPEUNKNOWN;
|
|
static int alDriverVersion[MAX1401]; // version/type of each driver
|
|
static int alTimeOutPeriod[MAX1401]; // timeout time in milliseconds
|
|
static short asLastRetCode[MAX1401]; // last code from a fn call
|
|
static short asType1401[MAX1401] = {0}; // The type of the 1401
|
|
static BOOL abGrabbed[MAX1401] = {0}; // Flag for grabbed, set true by grab1401
|
|
static int iAttached = 0; // counts process attaches so can let go
|
|
|
|
#ifdef _IS_WINDOWS_
|
|
/****************************************************************************
|
|
** Windows NT Specific Variables and internal types
|
|
****************************************************************************/
|
|
static HANDLE aHand1401[MAX1401] = {0}; // handles for 1401s
|
|
static HANDLE aXferEvent[MAX1401] = {0}; // transfer events for the 1401s
|
|
static LPVOID apAreas[MAX1401][MAX_TRANSAREAS]; // Locked areas
|
|
static DWORD auAreas[MAX1401][MAX_TRANSAREAS]; // Size of locked areas
|
|
static BOOL bWindows9x = FALSE; // if we are Windows 95 or better
|
|
#ifdef _WIN64
|
|
#define USE_NT_DIOC(ind) TRUE
|
|
#else
|
|
static BOOL abUseNTDIOC[MAX1401]; // Use NT-style DIOC parameters */
|
|
#define USE_NT_DIOC(ind) abUseNTDIOC[ind]
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#ifdef LINUX
|
|
static int aHand1401[MAX1401] = {0}; // handles for 1401s
|
|
#define INVALID_HANDLE_VALUE 0 // to avoid code differences
|
|
#endif
|
|
|
|
|
|
/*
|
|
** The CmdHead relates to backwards compatibility with ancient Microsoft (and Sperry!)
|
|
** versions of BASIC, where this header was needed so we could load a command into
|
|
** memory.
|
|
*/
|
|
#pragma pack(1) // pack our structure
|
|
typedef struct CmdHead // defines header block on command
|
|
{ // for PC commands
|
|
char acBasic[5]; // BASIC information - needed to align things
|
|
WORD wBasicSz; // size as seen by BASIC
|
|
WORD wCmdSize; // size of the following info
|
|
} __packed CMDHEAD;
|
|
#pragma pack() // back to normal
|
|
|
|
/*
|
|
** The rest of the header looks like this...
|
|
** int iRelPnt; relocation pointer... actual start
|
|
** char acName[8]; string holding the command name
|
|
** BYTE bMonRev; monitor revision level
|
|
** BYTE bCmdRev; command revision level
|
|
*/
|
|
|
|
typedef CMDHEAD *LPCMDHEAD; // pointer to a command header
|
|
|
|
#define MAXSTRLEN 255 // maximum string length we use
|
|
#define TOHOST FALSE
|
|
#define TO1401 TRUE
|
|
|
|
static short CheckHandle(short h)
|
|
{
|
|
if ((h < 0) || (h >= MAX1401)) // must be legal range...
|
|
return U14ERR_BADHAND;
|
|
if (aHand1401[h] <= 0) // must be open
|
|
return U14ERR_BADHAND;
|
|
return U14ERR_NOERROR;
|
|
}
|
|
|
|
#ifdef _IS_WINDOWS_
|
|
/****************************************************************************
|
|
** U14Status1401 Used for functions which do not pass any data in but
|
|
** get data back
|
|
****************************************************************************/
|
|
static short U14Status1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
|
|
{
|
|
DWORD dwBytes = 0;
|
|
|
|
if ((sHand < 0) || (sHand >= MAX1401)) /* Check parameters */
|
|
return U14ERR_BADHAND;
|
|
#ifndef _WIN64
|
|
if (!USE_NT_DIOC(sHand))
|
|
{ /* Windows 9x DIOC methods? */
|
|
if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk,sizeof(TCSBLOCK),&dwBytes,NULL))
|
|
return (short)((dwBytes>=sizeof(TCSBLOCK)) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
|
|
else
|
|
return (short)GetLastError();
|
|
}
|
|
else
|
|
#endif
|
|
{ /* Windows NT or USB driver */
|
|
PARAMBLK rWork;
|
|
rWork.sState = U14ERR_DRIVCOMMS;
|
|
if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, &rWork,sizeof(PARAMBLK),&dwBytes,NULL) &&
|
|
(dwBytes >= sizeof(PARAMBLK)))
|
|
{
|
|
*pBlk = rWork.csBlock;
|
|
return rWork.sState;
|
|
}
|
|
}
|
|
|
|
return U14ERR_DRIVCOMMS;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14Control1401 Used for functions which pass data in and only expect
|
|
** an error code back
|
|
****************************************************************************/
|
|
static short U14Control1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
|
|
{
|
|
DWORD dwBytes = 0;
|
|
|
|
if ((sHand < 0) || (sHand >= MAX1401)) /* Check parameters */
|
|
return U14ERR_BADHAND;
|
|
|
|
#ifndef _WIN64
|
|
if (!USE_NT_DIOC(sHand))
|
|
{ /* Windows 9x DIOC methods */
|
|
if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk, sizeof(TCSBLOCK), &dwBytes, NULL))
|
|
return (short)(dwBytes >= sizeof(TCSBLOCK) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
|
|
else
|
|
return (short)GetLastError();
|
|
}
|
|
else
|
|
#endif
|
|
{ /* Windows NT or later */
|
|
PARAMBLK rWork;
|
|
rWork.sState = U14ERR_DRIVCOMMS;
|
|
if (DeviceIoControl(aHand1401[sHand], lCode, pBlk, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
|
|
(dwBytes >= sizeof(PARAMBLK)))
|
|
return rWork.sState;
|
|
}
|
|
|
|
return U14ERR_DRIVCOMMS;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
** SafeTickCount
|
|
** Gets time in approximately units of a millisecond.
|
|
*****************************************************************************/
|
|
static long SafeTickCount()
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
return GetTickCount();
|
|
#endif
|
|
#ifdef LINUX
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
return (tv.tv_sec*1000 + tv.tv_usec/1000);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** A utility routine to get the command file extension for a given type
|
|
** of 1401. We assume the type code is vaguely legal.
|
|
****************************************************************************/
|
|
static int ExtForType(short sType, char* szExt)
|
|
{
|
|
szExt[0] = 0; /* Default return is a blank string */
|
|
switch (sType)
|
|
{
|
|
case U14TYPE1401: strcpy(szExt, ".CMD"); break; // Standard 1401
|
|
case U14TYPEPLUS: strcpy(szExt, ".GXC"); break; // 1401 plus
|
|
default: // All others are in a predictable sequence
|
|
strcpy(szExt, ".ARM");
|
|
szExt[3] = (char)('M' + sType - U14TYPEU1401);
|
|
if (szExt[3] > 'Z') // Wrap round to ARA after ARZ
|
|
szExt[3] = (char)(szExt[3] - 26);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14WhenToTimeOut
|
|
** Returns the time to time out in time units suitable for the machine
|
|
** we are running on ie millsecs for pc/linux, or Mac/
|
|
****************************************************************************/
|
|
U14API(int) U14WhenToTimeOut(short hand)
|
|
{
|
|
int iNow = SafeTickCount();
|
|
if ((hand >= 0) && (hand < MAX1401))
|
|
iNow += alTimeOutPeriod[hand];
|
|
return iNow;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14PassedTime
|
|
** Returns non zero if the timed passed in has been passed 0 if not
|
|
****************************************************************************/
|
|
U14API(short) U14PassedTime(int lCheckTime)
|
|
{
|
|
return (short)((SafeTickCount()-lCheckTime) > 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
** TranslateString
|
|
** Tidies up string that U14GetString returns. Converts all the commas in a
|
|
** string to spaces. Removes terminating CR character. May do more in future.
|
|
****************************************************************************/
|
|
static void TranslateString(char* pStr)
|
|
{
|
|
int i = 0;
|
|
while (pStr[i])
|
|
{
|
|
if (pStr[i] == ',')
|
|
pStr[i] = ' '; /* convert comma to space */
|
|
++i;
|
|
}
|
|
|
|
if ((i > 0) && (pStr[i-1] == '\n')) /* kill terminating LF */
|
|
pStr[i-1] = (char)0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14StrToLongs
|
|
** Converts a string to an array of longs and returns the number of values
|
|
****************************************************************************/
|
|
U14API(short) U14StrToLongs(const char* pszBuff, U14LONG *palNums, short sMaxLongs)
|
|
{
|
|
WORD wChInd = 0; // index into source
|
|
short sLgInd = 0; // index into result longs
|
|
|
|
while (pszBuff[wChInd] && // until we get to end of string...
|
|
(sLgInd < sMaxLongs)) // ...or filled the buffer
|
|
{
|
|
// Why not use a C Library converter?
|
|
switch (pszBuff[wChInd])
|
|
{
|
|
case '-':
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
{
|
|
BOOL bDone = FALSE; // true at end of number
|
|
int iSign = 1; // sign of number
|
|
long lValue = 0;
|
|
|
|
while ((!bDone) && pszBuff[wChInd])
|
|
{
|
|
switch (pszBuff[wChInd])
|
|
{
|
|
case '-':
|
|
iSign = -1; // swap sign
|
|
break;
|
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
lValue *= 10; // move to next digit base 10
|
|
lValue += ((int)pszBuff[wChInd]-(int)'0');
|
|
break;
|
|
|
|
default: // end of number
|
|
bDone = TRUE;
|
|
break;
|
|
}
|
|
wChInd++; // move onto next character
|
|
}
|
|
palNums[sLgInd] = lValue * iSign;
|
|
sLgInd++;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
wChInd++; // look at next char
|
|
break;
|
|
}
|
|
}
|
|
return (sLgInd);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** U14LongsFrom1401
|
|
** Gets the next waiting line from the 1401 and converts it longs
|
|
** Returns the number of numbers read or an error.
|
|
****************************************************************************/
|
|
U14API(short) U14LongsFrom1401(short hand, U14LONG *palBuff, short sMaxLongs)
|
|
{
|
|
char szWork[MAXSTRLEN];
|
|
short sResult = U14GetString(hand, szWork, MAXSTRLEN);/* get reply from 1401 */
|
|
if (sResult == U14ERR_NOERROR) /* if no error convert */
|
|
sResult = U14StrToLongs(szWork, palBuff, sMaxLongs);
|
|
return sResult;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14CheckErr
|
|
** Sends the ERR command to the 1401 and gets the result. Returns 0, a
|
|
** negative error code, or the first error value.
|
|
****************************************************************************/
|
|
U14API(short) U14CheckErr(short hand)
|
|
{
|
|
short sResult = U14SendString(hand, ";ERR;");
|
|
if (sResult == U14ERR_NOERROR)
|
|
{
|
|
U14LONG er[3];
|
|
sResult = U14LongsFrom1401(hand, er, 3);
|
|
if (sResult > 0)
|
|
{
|
|
sResult = (short)er[0]; /* Either zero or an error value */
|
|
#ifdef _DEBUG
|
|
if (er[0] != 0)
|
|
{
|
|
char szMsg[50];
|
|
sprintf(szMsg, "U14CheckErr returned %d,%d\n", er[0], er[1]);
|
|
OutputDebugString(szMsg);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (sResult == 0)
|
|
sResult = U14ERR_TIMEOUT; /* No numbers equals timeout */
|
|
}
|
|
}
|
|
|
|
return sResult;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14LastErrCode
|
|
** Returns the last code from the driver. This is for Windows where all calls
|
|
** go through the Control and Status routines, so we can save any error.
|
|
****************************************************************************/
|
|
U14API(short) U14LastErrCode(short hand)
|
|
{
|
|
if ((hand < 0) || (hand >= MAX1401))
|
|
return U14ERR_BADHAND;
|
|
return asLastRetCode[hand];
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14SetTimeout
|
|
** Set the timeout period for 1401 comms in milliseconds
|
|
****************************************************************************/
|
|
U14API(void) U14SetTimeout(short hand, int lTimeOut)
|
|
{
|
|
if ((hand < 0) || (hand >= MAX1401))
|
|
return;
|
|
alTimeOutPeriod[hand] = lTimeOut;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14GetTimeout
|
|
** Get the timeout period for 1401 comms in milliseconds
|
|
****************************************************************************/
|
|
U14API(int) U14GetTimeout(short hand)
|
|
{
|
|
if ((hand < 0) || (hand >= MAX1401))
|
|
return U14ERR_BADHAND;
|
|
return alTimeOutPeriod[hand];
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14OutBufSpace
|
|
** Return the space in the output buffer, or an error.
|
|
****************************************************************************/
|
|
U14API(short) U14OutBufSpace(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
short sErr = U14Status1401(hand, U14_GETOUTBUFSPACE,&csBlock);
|
|
if (sErr == U14ERR_NOERROR)
|
|
sErr = csBlock.ints[0];
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_GetOutBufSpace(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** U14BaseAddr1401
|
|
** Returns the 1401 base address or an error code. Meaningless nowadays
|
|
****************************************************************************/
|
|
U14API(int) U14BaseAddr1401(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
int iError = U14Status1401(hand, U14_GETBASEADDRESS,&csBlock);
|
|
if (iError == U14ERR_NOERROR)
|
|
iError = csBlock.longs[0];
|
|
return iError;
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_GetBaseAddress(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14StateOf1401
|
|
** Return error state, either NOERROR or a negative code.
|
|
****************************************************************************/
|
|
U14API(short) U14StateOf1401(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
short sErr = U14Status1401(hand, U14_STATEOF1401, &csBlock);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
sErr = csBlock.ints[0]; // returned 1401 state
|
|
if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
|
|
sErr = U14ERR_NOERROR;
|
|
}
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
sErr = (short)CED_StateOf1401(aHand1401[hand]);
|
|
if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
|
|
sErr = U14ERR_NOERROR;
|
|
}
|
|
#endif
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14DriverVersion
|
|
** Returns the driver version. Hi word is major revision, low word is minor.
|
|
** If you pass in a silly handle (like -1), we return the version of the last
|
|
** driver we know of (to cope with PCI and no 1401 attached).
|
|
****************************************************************************/
|
|
U14API(int) U14DriverVersion(short hand)
|
|
{
|
|
return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverVersion : alDriverVersion[hand];
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14DriverType
|
|
** Returns the driver type. The type, 0=ISA/NU-Bus, 1=PCI, 2=USB, 3=HSS
|
|
** If you pass in a silly handle (like -1), we return the type of the last
|
|
** driver we know of (to cope with PCI and no 1401 attached).
|
|
****************************************************************************/
|
|
U14API(int) U14DriverType(short hand)
|
|
{
|
|
return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverType : asDriverType[hand];
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14DriverName
|
|
** Returns the driver type as 3 character (ISA, PCI, USB or HSS))
|
|
****************************************************************************/
|
|
U14API(short) U14DriverName(short hand, char* pBuf, WORD wMax)
|
|
{
|
|
char* pName;
|
|
*pBuf = 0; // Start off with a blank string
|
|
switch (U14DriverType(hand)) // Results according to type
|
|
{
|
|
case 0: pName = "ISA"; break;
|
|
case 1: pName = "PCI"; break;
|
|
case 2: pName = "USB"; break;
|
|
case 3: pName = "HSS"; break;
|
|
default: pName = "???"; break;
|
|
}
|
|
strncpy(pBuf, pName, wMax); // Copy the correct name to return
|
|
|
|
return U14ERR_NOERROR;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14BlkTransState
|
|
** Returns 0 no transfer in progress, 1 transfer in progress or an error code
|
|
****************************************************************************/
|
|
U14API(short) U14BlkTransState(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
short sErr = U14Status1401(hand, U14_BLKTRANSSTATE, &csBlock);
|
|
if (sErr == U14ERR_NOERROR)
|
|
sErr = csBlock.ints[0];
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_BlkTransState(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14Grab1401
|
|
** Take control of the 1401 for diagnostics purposes. USB does nothing.
|
|
****************************************************************************/
|
|
U14API(short) U14Grab1401(short hand)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
if (abGrabbed[hand]) // 1401 should not have been grabbed
|
|
sErr = U14ERR_ALREADYSET; // Error code defined for this
|
|
else
|
|
{
|
|
TCSBLOCK csBlock;
|
|
sErr = U14Control1401(hand, U14_GRAB1401, &csBlock);
|
|
}
|
|
#endif
|
|
#ifdef LINUX
|
|
// 1401 should not have been grabbed
|
|
sErr = abGrabbed[hand] ? U14ERR_ALREADYSET : CED_Grab1401(aHand1401[hand]);
|
|
#endif
|
|
if (sErr == U14ERR_NOERROR)
|
|
abGrabbed[hand] = TRUE;
|
|
}
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14Free1401
|
|
****************************************************************************/
|
|
U14API(short) U14Free1401(short hand)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
if (abGrabbed[hand]) // 1401 should have been grabbed
|
|
{
|
|
TCSBLOCK csBlock;
|
|
sErr = U14Control1401(hand, U14_FREE1401, &csBlock);
|
|
}
|
|
else
|
|
sErr = U14ERR_NOTSET;
|
|
#endif
|
|
#ifdef LINUX
|
|
// 1401 should not have been grabbed
|
|
sErr = abGrabbed[hand] ? CED_Free1401(aHand1401[hand]) : U14ERR_NOTSET;
|
|
#endif
|
|
if (sErr == U14ERR_NOERROR)
|
|
abGrabbed[hand] = FALSE;
|
|
}
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14Peek1401
|
|
** DESCRIPTION Cause the 1401 to do one or more peek operations.
|
|
** If lRepeats is zero, the loop will continue until U14StopDebugLoop
|
|
** is called. After the peek is done, use U14GetDebugData to retrieve
|
|
** the results of the peek.
|
|
****************************************************************************/
|
|
U14API(short) U14Peek1401(short hand, DWORD dwAddr, int nSize, int nRepeats)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
if (abGrabbed[hand]) // 1401 should have been grabbed
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
csBlock.longs[0] = (long)dwAddr;
|
|
csBlock.longs[1] = nSize;
|
|
csBlock.longs[2] = nRepeats;
|
|
sErr = U14Control1401(hand, U14_DBGPEEK, &csBlock);
|
|
#endif
|
|
#ifdef LINUX
|
|
TDBGBLOCK dbb;
|
|
dbb.iAddr = (int)dwAddr;
|
|
dbb.iWidth = nSize;
|
|
dbb.iRepeats = nRepeats;
|
|
sErr = CED_DbgPeek(aHand1401[hand], &dbb);
|
|
#endif
|
|
}
|
|
else
|
|
sErr = U14ERR_NOTSET;
|
|
}
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14Poke1401
|
|
** DESCRIPTION Cause the 1401 to do one or more poke operations.
|
|
** If lRepeats is zero, the loop will continue until U14StopDebugLoop
|
|
** is called.
|
|
****************************************************************************/
|
|
U14API(short) U14Poke1401(short hand, DWORD dwAddr, DWORD dwValue,
|
|
int nSize, int nRepeats)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
if (abGrabbed[hand]) // 1401 should have been grabbed
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
csBlock.longs[0] = (long)dwAddr;
|
|
csBlock.longs[1] = nSize;
|
|
csBlock.longs[2] = nRepeats;
|
|
csBlock.longs[3] = (long)dwValue;
|
|
sErr = U14Control1401(hand, U14_DBGPOKE, &csBlock);
|
|
#endif
|
|
#ifdef LINUX
|
|
TDBGBLOCK dbb;
|
|
dbb.iAddr = (int)dwAddr;
|
|
dbb.iWidth = nSize;
|
|
dbb.iRepeats= nRepeats;
|
|
dbb.iData = (int)dwValue;
|
|
sErr = CED_DbgPoke(aHand1401[hand], &dbb);
|
|
#endif
|
|
}
|
|
else
|
|
sErr = U14ERR_NOTSET;
|
|
}
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14Ramp1401
|
|
** DESCRIPTION Cause the 1401 to loop, writing a ramp to a location.
|
|
** If lRepeats is zero, the loop will continue until U14StopDebugLoop.
|
|
****************************************************************************/
|
|
U14API(short) U14Ramp1401(short hand, DWORD dwAddr, DWORD dwDef, DWORD dwEnable,
|
|
int nSize, int nRepeats)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
if (abGrabbed[hand]) // 1401 should have been grabbed
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
csBlock.longs[0] = (long)dwAddr;
|
|
csBlock.longs[1] = (long)dwDef;
|
|
csBlock.longs[2] = (long)dwEnable;
|
|
csBlock.longs[3] = nSize;
|
|
csBlock.longs[4] = nRepeats;
|
|
sErr = U14Control1401(hand, U14_DBGRAMPDATA, &csBlock);
|
|
#endif
|
|
#ifdef LINUX
|
|
TDBGBLOCK dbb;
|
|
dbb.iAddr = (int)dwAddr;
|
|
dbb.iDefault = (int)dwDef;
|
|
dbb.iMask = (int)dwEnable;
|
|
dbb.iWidth = nSize;
|
|
dbb.iRepeats = nRepeats;
|
|
sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
|
|
#endif
|
|
}
|
|
else
|
|
sErr = U14ERR_NOTSET;
|
|
}
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14RampAddr
|
|
** DESCRIPTION Cause the 1401 to loop, reading from a ramping location.
|
|
** If lRepeats is zero, the loop will continue until U14StopDebugLoop
|
|
****************************************************************************/
|
|
U14API(short) U14RampAddr(short hand, DWORD dwDef, DWORD dwEnable,
|
|
int nSize, int nRepeats)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
if (abGrabbed[hand]) // 1401 should have been grabbed
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
csBlock.longs[0] = (long)dwDef;
|
|
csBlock.longs[1] = (long)dwEnable;
|
|
csBlock.longs[2] = nSize;
|
|
csBlock.longs[3] = nRepeats;
|
|
sErr = U14Control1401(hand, U14_DBGRAMPADDR, &csBlock);
|
|
#endif
|
|
#ifdef LINUX
|
|
TDBGBLOCK dbb;
|
|
dbb.iDefault = (int)dwDef;
|
|
dbb.iMask = (int)dwEnable;
|
|
dbb.iWidth = nSize;
|
|
dbb.iRepeats = nRepeats;
|
|
sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
|
|
#endif
|
|
}
|
|
else
|
|
sErr = U14ERR_NOTSET;
|
|
}
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14StopDebugLoop
|
|
** DESCRIPTION Stops a peek\poke\ramp that, with repeats set to zero,
|
|
** will otherwise continue forever.
|
|
****************************************************************************/
|
|
U14API(short) U14StopDebugLoop(short hand)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
#ifdef _IS_WINDOWS_
|
|
{
|
|
if (abGrabbed[hand]) // 1401 should have been grabbed
|
|
{
|
|
TCSBLOCK csBlock;
|
|
sErr = U14Control1401(hand, U14_DBGSTOPLOOP, &csBlock);
|
|
}
|
|
else
|
|
sErr = U14ERR_NOTSET;
|
|
}
|
|
#endif
|
|
#ifdef LINUX
|
|
sErr = abGrabbed[hand] ? CED_DbgStopLoop(aHand1401[hand]) : U14ERR_NOTSET;
|
|
#endif
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14GetDebugData
|
|
** DESCRIPTION Returns the result from a previous peek operation.
|
|
****************************************************************************/
|
|
U14API(short) U14GetDebugData(short hand, U14LONG* plValue)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
if (abGrabbed[hand]) // 1401 should have been grabbed
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
sErr = U14Status1401(hand, U14_DBGGETDATA, &csBlock);
|
|
if (sErr == U14ERR_NOERROR)
|
|
*plValue = csBlock.longs[0]; // Return the data
|
|
#endif
|
|
#ifdef LINUX
|
|
TDBGBLOCK dbb;
|
|
sErr = CED_DbgGetData(aHand1401[hand], &dbb);
|
|
if (sErr == U14ERR_NOERROR)
|
|
*plValue = dbb.iData; /* Return the data */
|
|
#endif
|
|
}
|
|
else
|
|
sErr = U14ERR_NOTSET;
|
|
}
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14StartSelfTest
|
|
****************************************************************************/
|
|
U14API(short) U14StartSelfTest(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
return U14Control1401(hand, U14_STARTSELFTEST, &csBlock);
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_StartSelfTest(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14CheckSelfTest
|
|
****************************************************************************/
|
|
U14API(short) U14CheckSelfTest(short hand, U14LONG *pData)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
short sErr = U14Status1401(hand, U14_CHECKSELFTEST, &csBlock);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
pData[0] = csBlock.longs[0]; /* Return the results to user */
|
|
pData[1] = csBlock.longs[1];
|
|
pData[2] = csBlock.longs[2];
|
|
}
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR) /* Check parameters */
|
|
{
|
|
TGET_SELFTEST gst;
|
|
sErr = CED_CheckSelfTest(aHand1401[hand], &gst);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
pData[0] = gst.code; /* Return the results to user */
|
|
pData[1] = gst.x;
|
|
pData[2] = gst.y;
|
|
}
|
|
}
|
|
#endif
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14GetUserMemorySize
|
|
****************************************************************************/
|
|
U14API(short) U14GetUserMemorySize(short hand, DWORD *pMemorySize)
|
|
{
|
|
// The original 1401 used a different command for getting the size
|
|
short sErr = U14SendString(hand, (asType1401[hand] == U14TYPE1401) ? "MEMTOP;" : "MEMTOP,?;");
|
|
*pMemorySize = 0; /* if we get error then leave size set at 0 */
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
U14LONG alLimits[4];
|
|
sErr = U14LongsFrom1401(hand, alLimits, 4);
|
|
if (sErr > 0) /* +ve sErr is the number of values read */
|
|
{
|
|
sErr = U14ERR_NOERROR; /* All OK, flag success */
|
|
if (asType1401[hand] == U14TYPE1401) /* result for standard */
|
|
*pMemorySize = alLimits[0] - alLimits[1]; /* memtop-membot */
|
|
else
|
|
*pMemorySize = alLimits[0]; /* result for plus or u1401 */
|
|
}
|
|
}
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14TypeOf1401
|
|
** Returns the type of the 1401, maybe unknown
|
|
****************************************************************************/
|
|
U14API(short) U14TypeOf1401(short hand)
|
|
{
|
|
if ((hand < 0) || (hand >= MAX1401)) /* Check parameters */
|
|
return U14ERR_BADHAND;
|
|
else
|
|
return asType1401[hand];
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14NameOf1401
|
|
** Returns the type of the 1401 as a string, blank if unknown
|
|
****************************************************************************/
|
|
U14API(short) U14NameOf1401(short hand, char* pBuf, WORD wMax)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
char* pName;
|
|
switch (asType1401[hand]) // Results according to type
|
|
{
|
|
case U14TYPE1401: pName = "Std 1401"; break;
|
|
case U14TYPEPLUS: pName = "1401plus"; break;
|
|
case U14TYPEU1401: pName = "micro1401"; break;
|
|
case U14TYPEPOWER: pName = "Power1401"; break;
|
|
case U14TYPEU14012:pName = "Micro1401 mk II"; break;
|
|
case U14TYPEPOWER2:pName = "Power1401 mk II"; break;
|
|
case U14TYPEU14013:pName = "Micro1401-3"; break;
|
|
case U14TYPEPOWER3:pName = "Power1401-3"; break;
|
|
default: pName = "Unknown";
|
|
}
|
|
strncpy(pBuf, pName, wMax);
|
|
}
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14TransferFlags
|
|
** Returns the driver block transfer flags.
|
|
** Bits can be set - see U14TF_ constants in use1401.h
|
|
*****************************************************************************/
|
|
U14API(short) U14TransferFlags(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
short sErr = U14Status1401(hand, U14_TRANSFERFLAGS, &csBlock);
|
|
return (sErr == U14ERR_NOERROR) ? (short)csBlock.ints[0] : sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_TransferFlags(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** GetDriverVersion
|
|
** Actually reads driver version from the device driver.
|
|
** Hi word is major revision, low word is minor revision.
|
|
** Assumes that hand has been checked. Also codes driver type in bits 24 up.
|
|
*****************************************************************************/
|
|
static int GetDriverVersion(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
int iErr = U14Status1401(hand, U14_GETDRIVERREVISION, &csBlock);
|
|
if (iErr == U14ERR_NOERROR)
|
|
iErr = csBlock.longs[0];
|
|
return iErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
return CED_GetDriverRevision(aHand1401[hand]);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14MonitorRev
|
|
** Returns the 1401 monitor revision number.
|
|
** The number returned is the minor revision - the part after the
|
|
** decimal point - plus the major revision times 1000.
|
|
*****************************************************************************/
|
|
U14API(int) U14MonitorRev(short hand)
|
|
{
|
|
int iRev = 0;
|
|
int iErr = CheckHandle(hand);
|
|
if (iErr != U14ERR_NOERROR) // Check open and in use
|
|
return iErr;
|
|
|
|
if (asType1401[hand] >= U14TYPEPOWER2) // The Power2 onwards can give us the monitor
|
|
{ // revision directly for all versions
|
|
iErr = U14SendString(hand, "INFO,S,28;");
|
|
if (iErr == U14ERR_NOERROR)
|
|
{
|
|
U14LONG lVals[2]; // Read a single number being the revision
|
|
iErr = U14LongsFrom1401(hand, lVals, 1);
|
|
if (iErr > 0)
|
|
{
|
|
iErr = U14ERR_NOERROR;
|
|
iRev = lVals[0]; // This is the minor part of the revision
|
|
iRev += asType1401[hand] * 10000;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ /* Do it the hard way for older hardware */
|
|
iErr = U14SendString(hand, ";CLIST;"); /* ask for command levels */
|
|
if (iErr == U14ERR_NOERROR)
|
|
{
|
|
while (iErr == U14ERR_NOERROR)
|
|
{
|
|
char wstr[50];
|
|
iErr = U14GetString(hand, wstr, 45);
|
|
if (iErr == U14ERR_NOERROR)
|
|
{
|
|
char *pstr = strstr(wstr,"RESET"); /* Is this the RESET command? */
|
|
if ((pstr == wstr) && (wstr[5] == ' '))
|
|
{
|
|
char *pstr2;
|
|
size_t l;
|
|
pstr += 6; /* Move past RESET and followinmg char */
|
|
l = strlen(pstr); /* The length of text remaining */
|
|
while (((pstr[l-1] == ' ') || (pstr[l-1] == 13)) && (l > 0))
|
|
{
|
|
pstr[l-1] = 0; /* Tidy up string at the end */
|
|
l--; /* by removing spaces and CRs */
|
|
}
|
|
pstr2 = strchr(pstr, '.'); /* Find the decimal point */
|
|
if (pstr2 != NULL) /* If we found the DP */
|
|
{
|
|
*pstr2 = 0; /* End pstr string at DP */
|
|
pstr2++; /* Now past the decimal point */
|
|
iRev = atoi(pstr2); /* Get the number after point */
|
|
}
|
|
iRev += (atoi(pstr) * 1000); /* Add first bit * 1000 */
|
|
}
|
|
if ((strlen(wstr) < 3) && (wstr[0] == ' '))
|
|
break; /* Spot the last line of results */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (iErr == U14ERR_NOERROR) /* Return revision if no error */
|
|
iErr = iRev;
|
|
|
|
return iErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14TryToOpen Tries to open the 1401 number passed
|
|
** Note : This will succeed with NT driver even if no I/F card or
|
|
** 1401 switched off, so we check state and close the driver
|
|
** if the state is unsatisfactory in U14Open1401.
|
|
****************************************************************************/
|
|
#ifdef _IS_WINDOWS_
|
|
#define U14NAMEOLD "\\\\.\\CED_140%d"
|
|
#define U14NAMENEW "\\\\.\\CED%d"
|
|
static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
|
|
{
|
|
short sErr = U14ERR_NOERROR;
|
|
HANDLE hDevice = INVALID_HANDLE_VALUE;
|
|
DWORD dwErr = 0;
|
|
int nFirst, nLast, nDev = 0; /* Used for the search for a 1401 */
|
|
BOOL bOldName = FALSE; /* start by looking for a modern driver */
|
|
|
|
if (n1401 == 0) /* If we need to look for a 1401 */
|
|
{
|
|
nFirst = 1; /* Set the search range */
|
|
nLast = MAX1401; /* through all the possible 1401s */
|
|
}
|
|
else
|
|
nFirst = nLast = n1401; /* Otherwise just one 1401 */
|
|
|
|
while (hDevice == INVALID_HANDLE_VALUE) /* Loop to try for a 1401 */
|
|
{
|
|
for (nDev = nFirst; nDev <= nLast; nDev++)
|
|
{
|
|
char szDevName[40]; /* name of the device to open */
|
|
sprintf(szDevName, bOldName ? U14NAMEOLD : U14NAMENEW, nDev);
|
|
hDevice = CreateFile(szDevName, GENERIC_WRITE | GENERIC_READ,
|
|
0, 0, /* Unshared mode does nothing as this is a device */
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (hDevice != INVALID_HANDLE_VALUE)/* Check 1401 if opened */
|
|
{
|
|
TCSBLOCK csBlock;
|
|
assert(aHand1401[nDev-1] == INVALID_HANDLE_VALUE); // assert if already open
|
|
aHand1401[nDev-1] = hDevice; /* Save handle for now */
|
|
|
|
#ifndef _WIN64
|
|
// Use DIOC method if not windows 9x or if using new device name
|
|
abUseNTDIOC[nDev-1] = (BOOL)(!bWindows9x || !bOldName);
|
|
#endif
|
|
sErr = U14Status1401((short)(nDev-1), U14_TYPEOF1401, &csBlock);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
*plRetVal = csBlock.ints[0];
|
|
if (csBlock.ints[0] == U14ERR_INUSE)/* Prevent multi opens */
|
|
{
|
|
CloseHandle(hDevice); /* treat as open failure */
|
|
hDevice = INVALID_HANDLE_VALUE;
|
|
aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
|
|
sErr = U14ERR_INUSE;
|
|
}
|
|
else
|
|
break; /* Exit from for loop on success */
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(hDevice); /* Give up if func fails */
|
|
hDevice = INVALID_HANDLE_VALUE;
|
|
aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD dwe = GetLastError(); /* Get error code otherwise */
|
|
if ((dwe != ERROR_FILE_NOT_FOUND) || (dwErr == 0))
|
|
dwErr = dwe; /* Ignore repeats of 'not found' */
|
|
}
|
|
}
|
|
|
|
if ((hDevice == INVALID_HANDLE_VALUE) &&/* No device found, and... */
|
|
(bWindows9x) && /* ...old names are allowed, and... */
|
|
(bOldName == FALSE)) /* ...not tried old names yet */
|
|
bOldName = TRUE; /* Set flag and go round again */
|
|
else
|
|
break; /* otherwise that's all folks */
|
|
}
|
|
|
|
if (hDevice != INVALID_HANDLE_VALUE) /* If we got our device open */
|
|
*psHandle = (short)(nDev-1); /* return 1401 number opened */
|
|
else
|
|
{
|
|
if (dwErr == ERROR_FILE_NOT_FOUND) /* Sort out the error codes */
|
|
sErr = U14ERR_NO1401DRIV; /* if file not found */
|
|
else if (dwErr == ERROR_NOT_SUPPORTED)
|
|
sErr = U14ERR_DRIVTOOOLD; /* if DIOC not supported */
|
|
else if (dwErr == ERROR_ACCESS_DENIED)
|
|
sErr = U14ERR_INUSE;
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS; /* otherwise assume comms problem */
|
|
}
|
|
return sErr;
|
|
}
|
|
#endif
|
|
#ifdef LINUX
|
|
static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
|
|
{
|
|
short sErr = U14ERR_NOERROR;
|
|
int fh = 0; // will be 1401 handle
|
|
int iErr = 0;
|
|
int nFirst, nLast, nDev = 0; // Used for the search for a 1401
|
|
|
|
if (n1401 == 0) // If we need to look for a 1401
|
|
{
|
|
nFirst = 1; /* Set the search range */
|
|
nLast = MAX1401; /* through all the possible 1401s */
|
|
}
|
|
else
|
|
nFirst = nLast = n1401; /* Otherwise just one 1401 */
|
|
|
|
for (nDev = nFirst; nDev <= nLast; nDev++)
|
|
{
|
|
char szDevName[40]; // name of the device to open
|
|
sprintf(szDevName,"/dev/cedusb/%d", nDev-1);
|
|
fh = open(szDevName, O_RDWR); // can only be opened once at a time
|
|
if (fh > 0) // Check 1401 if opened
|
|
{
|
|
int iType1401 = CED_TypeOf1401(fh); // get 1401 type
|
|
aHand1401[nDev-1] = fh; // Save handle for now
|
|
if (iType1401 >= 0)
|
|
{
|
|
*plRetVal = iType1401;
|
|
break; // Exit from for loop on success
|
|
}
|
|
else
|
|
{
|
|
close(fh); // Give up if func fails
|
|
fh = 0;
|
|
aHand1401[nDev-1] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (((errno != ENODEV) && (errno != ENOENT)) || (iErr == 0))
|
|
iErr = errno; // Ignore repeats of 'not found'
|
|
}
|
|
}
|
|
|
|
|
|
if (fh) // If we got our device open
|
|
*psHandle = (short)(nDev-1); // return 1401 number opened
|
|
else
|
|
{
|
|
if ((iErr == ENODEV) || (iErr == ENOENT)) // Sort out the error codes
|
|
sErr = U14ERR_NO1401DRIV; // if file not found
|
|
else if (iErr == EBUSY)
|
|
sErr = U14ERR_INUSE;
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS; // otherwise assume comms problem
|
|
}
|
|
|
|
return sErr;
|
|
}
|
|
#endif
|
|
/****************************************************************************
|
|
** U14Open1401
|
|
** Tries to get the 1401 for use by this application
|
|
*****************************************************************************/
|
|
U14API(short) U14Open1401(short n1401)
|
|
{
|
|
long lRetVal = -1;
|
|
short sErr;
|
|
short hand = 0;
|
|
|
|
if ((n1401 < 0) || (n1401 > MAX1401)) // must check the 1401 number
|
|
return U14ERR_BAD1401NUM;
|
|
|
|
szLastName[0] = 0; /* initialise the error info string */
|
|
|
|
sErr = U14TryToOpen(n1401, &lRetVal, &hand);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
long lDriverVersion = GetDriverVersion(hand); /* get driver revision */
|
|
long lDriverRev = -1;
|
|
if (lDriverVersion >= 0) /* can use it if all OK */
|
|
{
|
|
lLastDriverType = (lDriverVersion >> 24) & 0x000000FF;
|
|
asDriverType[hand] = (short)lLastDriverType; /* Drv type */
|
|
lLastDriverVersion = lDriverVersion & 0x00FFFFFF;
|
|
alDriverVersion[hand] = lLastDriverVersion; /* Actual version */
|
|
lDriverRev = ((lDriverVersion>>16) & 0x00FF); /* use hi word */
|
|
}
|
|
else
|
|
{
|
|
U14Close1401(hand); /* If there is a problem we should close */
|
|
return (short)lDriverVersion; /* and return the error code */
|
|
}
|
|
|
|
if (lDriverRev < MINDRIVERMAJREV) /* late enough version? */
|
|
{
|
|
U14Close1401(hand); /* If there is a problem we should close */
|
|
return U14ERR_DRIVTOOOLD; /* too old */
|
|
}
|
|
|
|
asLastRetCode[hand] = U14ERR_NOERROR; /* Initialise this 1401s info */
|
|
abGrabbed[hand] = FALSE; /* we are not in single step mode */
|
|
U14SetTimeout(hand, 3000); /* set 3 seconds as default timeout */
|
|
|
|
switch (lRetVal)
|
|
{
|
|
case DRIVRET_STD: asType1401[hand] = U14TYPE1401; break; /* Some we do by hand */
|
|
case DRIVRET_U1401:asType1401[hand] = U14TYPEU1401; break;
|
|
case DRIVRET_PLUS: asType1401[hand] = U14TYPEPLUS; break;
|
|
default: // For the power upwards, we can calculate the codes
|
|
if ((lRetVal >= DRIVRET_POWER) && (lRetVal <= DRIVRET_MAX))
|
|
asType1401[hand] = (short)(lRetVal - (DRIVRET_POWER - U14TYPEPOWER));
|
|
else
|
|
asType1401[hand] = U14TYPEUNKNOWN;
|
|
break;
|
|
}
|
|
U14KillIO1401(hand); /* resets the 1401 buffers */
|
|
|
|
if (asType1401[hand] != U14TYPEUNKNOWN) /* If all seems OK so far */
|
|
{
|
|
sErr = U14CheckErr(hand); /* we can check 1401 comms now */
|
|
if (sErr != 0) /* If this failed to go OK */
|
|
U14Reset1401(hand); /* Reset the 1401 to try to sort it out */
|
|
}
|
|
|
|
sErr = U14StateOf1401(hand);/* Get the state of the 1401 for return */
|
|
if (sErr == U14ERR_NOERROR)
|
|
sErr = hand; /* return the handle if no problem */
|
|
else
|
|
U14Close1401(hand); /* If there is a problem we should close */
|
|
}
|
|
|
|
return sErr;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** U14Close1401
|
|
** Closes the 1401 so someone else can use it.
|
|
****************************************************************************/
|
|
U14API(short) U14Close1401(short hand)
|
|
{
|
|
int j;
|
|
int iAreaMask = 0; // Mask for active areas
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr != U14ERR_NOERROR) // Check open and in use
|
|
return sErr;
|
|
|
|
for (j = 0; j<MAX_TRANSAREAS; ++j)
|
|
{
|
|
TGET_TX_BLOCK gtb;
|
|
int iReturn = U14GetTransfer(hand, >b); // get area information
|
|
if (iReturn == U14ERR_NOERROR) // ignore if any problem
|
|
if (gtb.used)
|
|
iAreaMask |= (1 << j); // set a bit for each used area
|
|
}
|
|
|
|
if (iAreaMask) // if any areas are in use
|
|
{
|
|
U14Reset1401(hand); // in case an active transfer running
|
|
for (j = 0; j < MAX_TRANSAREAS; ++j) // Locate locked areas
|
|
if (iAreaMask & (1 << j)) // And kill off any transfers
|
|
U14UnSetTransfer(hand, (WORD)j);
|
|
}
|
|
|
|
#ifdef _IS_WINDOWS_
|
|
if (aXferEvent[hand]) // if this 1401 has an open event handle
|
|
{
|
|
CloseHandle(aXferEvent[hand]); // close down the handle
|
|
aXferEvent[hand] = NULL; // and mark it as gone
|
|
}
|
|
|
|
if (CloseHandle(aHand1401[hand]))
|
|
#endif
|
|
#ifdef LINUX
|
|
if (close(aHand1401[hand]) == 0) // make sure that close works
|
|
#endif
|
|
{
|
|
aHand1401[hand] = INVALID_HANDLE_VALUE;
|
|
asType1401[hand] = U14TYPEUNKNOWN;
|
|
return U14ERR_NOERROR;
|
|
}
|
|
else
|
|
return U14ERR_BADHAND; /* BUGBUG GetLastError() ? */
|
|
}
|
|
|
|
/**************************************************************************
|
|
**
|
|
** Look for open 1401s and attempt to close them down. 32-bit windows only.
|
|
**************************************************************************/
|
|
U14API(void) U14CloseAll(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX1401; i++) // Tidy up and make safe
|
|
if (aHand1401[i] != INVALID_HANDLE_VALUE)
|
|
U14Close1401((short)i); // Last ditch close 1401
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14Reset1401
|
|
** Resets the 1401
|
|
****************************************************************************/
|
|
U14API(short) U14Reset1401(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
return U14Control1401(hand, U14_RESET1401, &csBlock);
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_Reset1401(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14ForceReset
|
|
** Sets the 1401 full reset flag, so that next call to Reset1401 will
|
|
** always cause a genuine reset.
|
|
*****************************************************************************/
|
|
U14API(short) U14ForceReset(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
return U14Control1401(hand, U14_FULLRESET, &csBlock);
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_FullReset(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14KillIO1401
|
|
** Removes any pending IO from the buffers.
|
|
*****************************************************************************/
|
|
U14API(short) U14KillIO1401(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
return U14Control1401(hand, U14_KILLIO1401, &csBlock);
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_KillIO1401(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** U14SendString
|
|
** Send characters to the 1401
|
|
*****************************************************************************/
|
|
U14API(short) U14SendString(short hand, const char* pString)
|
|
{
|
|
int nChars; // length we are sending
|
|
long lTimeOutTicks; // when to time out
|
|
BOOL bSpaceToSend; // space to send yet
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr != U14ERR_NOERROR)
|
|
return sErr;
|
|
|
|
nChars = (int)strlen(pString); // get string length we want to send
|
|
if (nChars > MAXSTRLEN)
|
|
return U14ERR_STRLEN; // String too long
|
|
|
|
#ifdef _IS_WINDOWS_
|
|
// To get here we must wait for the buffer to have some space
|
|
lTimeOutTicks = U14WhenToTimeOut(hand);
|
|
do
|
|
{
|
|
bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
|
|
}
|
|
while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
|
|
|
|
if (!bSpaceToSend) /* Last-ditch attempt to avoid timeout */
|
|
{ /* This can happen with anti-virus or network activity! */
|
|
int i;
|
|
for (i = 0; (i < 4) && (!bSpaceToSend); ++i)
|
|
{
|
|
Sleep(25); /* Give other threads a chance for a while */
|
|
bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
|
|
}
|
|
}
|
|
|
|
if (asLastRetCode[hand] == U14ERR_NOERROR) /* no errors? */
|
|
{
|
|
if (bSpaceToSend)
|
|
{
|
|
PARAMBLK rData;
|
|
DWORD dwBytes;
|
|
char tstr[MAXSTRLEN+5]; /* Buffer for chars */
|
|
|
|
if ((hand < 0) || (hand >= MAX1401))
|
|
sErr = U14ERR_BADHAND;
|
|
else
|
|
{
|
|
strcpy(tstr, pString); /* Into local buf */
|
|
#ifndef _WIN64
|
|
if (!USE_NT_DIOC(hand)) /* Using WIN 95 driver access? */
|
|
{
|
|
int iOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_SENDSTRING,
|
|
NULL, 0, tstr, nChars,
|
|
&dwBytes, NULL);
|
|
if (iOK)
|
|
sErr = (dwBytes >= (DWORD)nChars) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS;
|
|
else
|
|
sErr = (short)GetLastError();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_SENDSTRING,
|
|
tstr, nChars,
|
|
&rData,sizeof(PARAMBLK),&dwBytes,NULL);
|
|
if (iOK && (dwBytes >= sizeof(PARAMBLK)))
|
|
sErr = rData.sState;
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS;
|
|
}
|
|
|
|
if (sErr != U14ERR_NOERROR) // If we have had a comms error
|
|
U14ForceReset(hand); // make sure we get real reset
|
|
}
|
|
|
|
return sErr;
|
|
|
|
}
|
|
else
|
|
{
|
|
U14ForceReset(hand); // make sure we get real reset
|
|
return U14ERR_TIMEOUT;
|
|
}
|
|
}
|
|
else
|
|
return asLastRetCode[hand];
|
|
#endif
|
|
#ifdef LINUX
|
|
// Just try to send it and see what happens!
|
|
sErr = CED_SendString(aHand1401[hand], pString, nChars);
|
|
if (sErr != U14ERR_NOOUT) // if any result except "no room in output"...
|
|
{
|
|
if (sErr != U14ERR_NOERROR) // if a problem...
|
|
U14ForceReset(hand); // ...make sure we get real reset next time
|
|
return sErr; // ... we are done as nothing we can do
|
|
}
|
|
|
|
// To get here we must wait for the buffer to have some space
|
|
lTimeOutTicks = U14WhenToTimeOut(hand);
|
|
do
|
|
{
|
|
bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
|
|
if (!bSpaceToSend)
|
|
sched_yield(); // let others have fun while we wait
|
|
}
|
|
while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
|
|
|
|
if (asLastRetCode[hand] == U14ERR_NOERROR) /* no errors? */
|
|
{
|
|
if (bSpaceToSend)
|
|
{
|
|
sErr = CED_SendString(aHand1401[hand], pString, nChars);
|
|
if (sErr != U14ERR_NOERROR) // If we have had a comms error
|
|
U14ForceReset(hand); // make sure we get real reset
|
|
return sErr;
|
|
}
|
|
else
|
|
{
|
|
U14ForceReset(hand); // make sure we get real reset
|
|
return U14ERR_TIMEOUT;
|
|
}
|
|
}
|
|
else
|
|
return asLastRetCode[hand];
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14SendChar
|
|
** Send character to the 1401
|
|
*****************************************************************************/
|
|
U14API(short) U14SendChar(short hand, char cChar)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
char sz[2]=" "; // convert to a string and send
|
|
sz[0] = cChar;
|
|
sz[1] = 0;
|
|
return(U14SendString(hand, sz)); // String routines are better
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_SendChar(aHand1401[hand], cChar) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14GetString
|
|
** Get a string from the 1401. Returns a null terminated string.
|
|
** The string is all the characters up to the next CR in the buffer
|
|
** or the end of the buffer if that comes first. This only returns text
|
|
** if there is a CR in the buffer. The terminating CR character is removed.
|
|
** wMaxLen Is the size of the buffer and must be at least 2 or an error.
|
|
** Returns U14ERR_NOERR if OK with the result in the string or a negative
|
|
** error code. Any error from the device causes us to set up for
|
|
** a full reset.
|
|
****************************************************************************/
|
|
U14API(short) U14GetString(short hand, char* pBuffer, WORD wMaxLen)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr != U14ERR_NOERROR) // If an error...
|
|
return sErr; // ...bail out!
|
|
|
|
#ifdef _IS_WINDOWS_
|
|
if (wMaxLen>1) // we need space for terminating 0
|
|
{
|
|
BOOL bLineToGet; // true when a line to get
|
|
long lTimeOutTicks = U14WhenToTimeOut(hand);
|
|
do
|
|
bLineToGet = (BOOL)(U14LineCount(hand) != 0);
|
|
while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
|
|
|
|
if (!bLineToGet) /* Last-ditch attempt to avoid timeout */
|
|
{ /* This can happen with anti-virus or network activity! */
|
|
int i;
|
|
for (i = 0; (i < 4) && (!bLineToGet); ++i)
|
|
{
|
|
Sleep(25); /* Give other threads a chance for a while */
|
|
bLineToGet = (BOOL)(U14LineCount(hand) != 0);
|
|
}
|
|
}
|
|
|
|
if (bLineToGet)
|
|
{
|
|
if (asLastRetCode[hand] == U14ERR_NOERROR) /* all ok so far */
|
|
{
|
|
DWORD dwBytes = 0;
|
|
*((WORD *)pBuffer) = wMaxLen; /* set up length */
|
|
#ifndef _WIN64
|
|
if (!USE_NT_DIOC(hand)) /* Win 95 DIOC here ? */
|
|
{
|
|
char tstr[MAXSTRLEN+5]; /* Buffer for Win95 chars */
|
|
int iOK;
|
|
|
|
if (wMaxLen > MAXSTRLEN) /* Truncate length */
|
|
wMaxLen = MAXSTRLEN;
|
|
|
|
*((WORD *)tstr) = wMaxLen; /* set len */
|
|
|
|
iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
|
|
NULL, 0, tstr, wMaxLen+sizeof(short),
|
|
&dwBytes, NULL);
|
|
if (iOK) /* Device IO control OK ? */
|
|
{
|
|
if (dwBytes >= 0) /* If driver OK */
|
|
{
|
|
strcpy(pBuffer, tstr);
|
|
sErr = U14ERR_NOERROR;
|
|
}
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS;
|
|
}
|
|
else
|
|
{
|
|
sErr = (short)GetLastError();
|
|
if (sErr > 0) /* Errors are -ve */
|
|
sErr = (short)-sErr;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{ /* Here for NT, the DLL must own the buffer */
|
|
HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE,wMaxLen+sizeof(short));
|
|
if (hMem)
|
|
{
|
|
char* pMem = (char*)GlobalLock(hMem);
|
|
if (pMem)
|
|
{
|
|
int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
|
|
NULL, 0, pMem, wMaxLen+sizeof(short),
|
|
&dwBytes, NULL);
|
|
if (iOK) /* Device IO control OK ? */
|
|
{
|
|
if (dwBytes >= wMaxLen)
|
|
{
|
|
strcpy(pBuffer, pMem+sizeof(short));
|
|
sErr = *((SHORT*)pMem);
|
|
}
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS;
|
|
}
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS;
|
|
|
|
GlobalUnlock(hMem);
|
|
}
|
|
else
|
|
sErr = U14ERR_OUTOFMEMORY;
|
|
|
|
GlobalFree(hMem);
|
|
}
|
|
else
|
|
sErr = U14ERR_OUTOFMEMORY;
|
|
}
|
|
|
|
if (sErr == U14ERR_NOERROR) // If all OK...
|
|
TranslateString(pBuffer); // ...convert any commas to spaces
|
|
else // If we have had a comms error...
|
|
U14ForceReset(hand); // ...make sure we get real reset
|
|
|
|
}
|
|
else
|
|
sErr = asLastRetCode[hand];
|
|
}
|
|
else
|
|
{
|
|
sErr = U14ERR_TIMEOUT;
|
|
U14ForceReset(hand); // make sure we get real reset
|
|
}
|
|
}
|
|
else
|
|
sErr = U14ERR_BUFF_SMALL;
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
if (wMaxLen>1) // we need space for terminating 0
|
|
{
|
|
BOOL bLineToGet; // true when a line to get
|
|
long lTimeOutTicks = U14WhenToTimeOut(hand);
|
|
do
|
|
{
|
|
bLineToGet = (BOOL)(U14LineCount(hand) != 0);
|
|
if (!bLineToGet)
|
|
sched_yield();
|
|
|
|
}
|
|
while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
|
|
|
|
if (bLineToGet)
|
|
{
|
|
sErr = CED_GetString(aHand1401[hand], pBuffer, wMaxLen-1); // space for terminator
|
|
if (sErr >=0) // if we were OK...
|
|
{
|
|
if (sErr >= wMaxLen) // this should NOT happen unless
|
|
sErr = U14ERR_DRIVCOMMS; // ...driver Comms are very bad
|
|
else
|
|
{
|
|
pBuffer[sErr] = 0; // OK, so terminate the string...
|
|
TranslateString(pBuffer); // ...and convert commas to spaces.
|
|
}
|
|
}
|
|
|
|
if (sErr < U14ERR_NOERROR) // If we have had a comms error
|
|
U14ForceReset(hand); // make sure we get real reset
|
|
}
|
|
else
|
|
{
|
|
sErr = U14ERR_TIMEOUT;
|
|
U14ForceReset(hand); // make sure we get real reset
|
|
}
|
|
}
|
|
else
|
|
sErr = U14ERR_BUFF_SMALL;
|
|
|
|
return sErr >= U14ERR_NOERROR ? U14ERR_NOERROR : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14GetChar
|
|
** Get a character from the 1401. CR returned as CR.
|
|
*****************************************************************************/
|
|
U14API(short) U14GetChar(short hand, char* pcChar)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
char sz[2]; // read a very short string
|
|
short sErr = U14GetString(hand, sz, 2); // read one char and nul terminate it
|
|
*pcChar = sz[0]; // copy to result, NB char translate done by GetString
|
|
if (sErr == U14ERR_NOERROR)
|
|
{ // undo translate of CR to zero
|
|
if (*pcChar == '\0') // by converting back
|
|
*pcChar = '\n'; // What a nasty thing to have to do
|
|
}
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr != U14ERR_NOERROR) // Check parameters
|
|
return sErr;
|
|
sErr = CED_GetChar(aHand1401[hand]); // get one char, if available
|
|
if (sErr >= 0)
|
|
{
|
|
*pcChar = (char)sErr; // return if it we have one
|
|
return U14ERR_NOERROR; // say all OK
|
|
}
|
|
else
|
|
return sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14Stat1401
|
|
** Returns 0 for no lines or error or non zero for something waiting
|
|
****************************************************************************/
|
|
U14API(short) U14Stat1401(short hand)
|
|
{
|
|
return ((short)(U14LineCount(hand) > 0));
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14CharCount
|
|
** Returns the number of characters in the input buffer
|
|
*****************************************************************************/
|
|
U14API(short) U14CharCount(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
short sErr = U14Status1401(hand, U14_STAT1401, &csBlock);
|
|
if (sErr == U14ERR_NOERROR)
|
|
sErr = csBlock.ints[0];
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_Stat1401(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14LineCount
|
|
** Returns the number of CR characters in the input buffer
|
|
*****************************************************************************/
|
|
U14API(short) U14LineCount(short hand)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
short sErr = U14Status1401(hand, U14_LINECOUNT, &csBlock);
|
|
if (sErr == U14ERR_NOERROR)
|
|
sErr = csBlock.ints[0];
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_LineCount(aHand1401[hand]) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14GetErrorString
|
|
** Converts error code supplied to a decent descriptive string.
|
|
** NOTE: This function may use some extra information stored
|
|
** internally in the DLL. This information is stored on a
|
|
** per-process basis, but it might be altered if you call
|
|
** other functions after getting an error and before using
|
|
** this function.
|
|
****************************************************************************/
|
|
U14API(void) U14GetErrorString(short nErr, char* pStr, WORD wMax)
|
|
{
|
|
char wstr[150];
|
|
|
|
switch (nErr) /* Basically, we do this with a switch block */
|
|
{
|
|
case U14ERR_OFF:
|
|
sprintf(wstr, "The 1401 is apparently switched off (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NC:
|
|
sprintf(wstr, "The 1401 is not connected to the interface card (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_ILL:
|
|
sprintf(wstr, "The 1401 is not working correctly (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NOIF:
|
|
sprintf(wstr, "The 1401 interface card was not detected (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_TIME:
|
|
sprintf(wstr, "The 1401 fails to become ready for use (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_BADSW:
|
|
sprintf(wstr, "The 1401 interface card jumpers are incorrect (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NOINT:
|
|
sprintf(wstr, "The 1401 interrupt is not available for use (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_INUSE:
|
|
sprintf(wstr, "The 1401 is already in use by another program (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NODMA:
|
|
sprintf(wstr, "The 1401 DMA channel is not available for use (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_BADHAND:
|
|
sprintf(wstr, "The application supplied an incorrect 1401 handle (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_BAD1401NUM:
|
|
sprintf(wstr, "The application used an incorrect 1401 number (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NO_SUCH_FN:
|
|
sprintf(wstr, "The code passed to the 1401 driver is invalid (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NO_SUCH_SUBFN:
|
|
sprintf(wstr, "The sub-code passed to the 1401 driver is invalid (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NOOUT:
|
|
sprintf(wstr, "No room in buffer for characters for the 1401 (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NOIN:
|
|
sprintf(wstr, "No characters from the 1401 are available (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_STRLEN:
|
|
sprintf(wstr, "A string sent to or read from the 1401 was too long (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_LOCKFAIL:
|
|
sprintf(wstr, "Failed to lock host memory for data transfer (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_UNLOCKFAIL:
|
|
sprintf(wstr, "Failed to unlock host memory after data transfer (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_ALREADYSET:
|
|
sprintf(wstr, "The transfer area used is already set up (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NOTSET:
|
|
sprintf(wstr, "The transfer area used has not been set up (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_BADAREA:
|
|
sprintf(wstr, "The transfer area number is incorrect (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NOFILE:
|
|
sprintf(wstr, "The command file %s could not be opened (code %d)", szLastName, nErr);
|
|
break;
|
|
|
|
case U14ERR_READERR:
|
|
sprintf(wstr, "The command file %s could not be read (code %d)", szLastName, nErr);
|
|
break;
|
|
|
|
case U14ERR_UNKNOWN:
|
|
sprintf(wstr, "The %s command resource could not be found (code %d)", szLastName, nErr);
|
|
break;
|
|
|
|
case U14ERR_HOSTSPACE:
|
|
sprintf(wstr, "Unable to allocate memory for loading command %s (code %d)", szLastName, nErr);
|
|
break;
|
|
|
|
case U14ERR_LOCKERR:
|
|
sprintf(wstr, "Unable to lock memory for loading command %s (code %d)", szLastName, nErr);
|
|
break;
|
|
|
|
case U14ERR_CLOADERR:
|
|
sprintf(wstr, "Error in loading command %s, bad command format (code %d)", szLastName, nErr);
|
|
break;
|
|
|
|
case U14ERR_TOXXXERR:
|
|
sprintf(wstr, "Error detected after data transfer to or from the 1401 (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NO386ENH:
|
|
sprintf(wstr, "Windows 3.1 is not running in 386 enhanced mode (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_NO1401DRIV:
|
|
sprintf(wstr, "The 1401 device driver cannot be found (code %d)\nUSB: check plugged in and powered\nOther: not installed?", nErr);
|
|
break;
|
|
|
|
case U14ERR_DRIVTOOOLD:
|
|
sprintf(wstr, "The 1401 device driver is too old for use (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_TIMEOUT:
|
|
sprintf(wstr, "Character transmissions to the 1401 timed-out (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_BUFF_SMALL:
|
|
sprintf(wstr, "Buffer for text from the 1401 was too small (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_CBALREADY:
|
|
sprintf(wstr, "1401 monitor callback already set up (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_BADDEREG:
|
|
sprintf(wstr, "1401 monitor callback deregister invalid (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_DRIVCOMMS:
|
|
sprintf(wstr, "1401 device driver communications failed (code %d)", nErr);
|
|
break;
|
|
|
|
case U14ERR_OUTOFMEMORY:
|
|
sprintf(wstr, "Failed to allocate or lock memory for text from the 1401 (code %d)", nErr);
|
|
break;
|
|
|
|
default:
|
|
sprintf(wstr, "1401 error code %d returned; this code is unknown", nErr);
|
|
break;
|
|
|
|
}
|
|
if ((WORD)strlen(wstr) >= wMax-1) /* Check for string being too long */
|
|
wstr[wMax-1] = 0; /* and truncate it if so */
|
|
strcpy(pStr, wstr); /* Return the error string */
|
|
}
|
|
|
|
/***************************************************************************
|
|
** U14GetTransfer
|
|
** Get a TGET_TX_BLOCK describing a transfer area (held in the block)
|
|
***************************************************************************/
|
|
U14API(short) U14GetTransfer(short hand, TGET_TX_BLOCK *pTransBlock)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
#ifdef _IS_WINDOWS_
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
DWORD dwBytes = 0;
|
|
BOOL bOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_GETTRANSFER, NULL, 0, pTransBlock,
|
|
sizeof(TGET_TX_BLOCK), &dwBytes, NULL);
|
|
|
|
if (bOK && (dwBytes >= sizeof(TGET_TX_BLOCK)))
|
|
sErr = U14ERR_NOERROR;
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS;
|
|
}
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
return (sErr == U14ERR_NOERROR) ? CED_GetTransfer(aHand1401[hand], pTransBlock) : sErr;
|
|
#endif
|
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// U14WorkingSet
|
|
// For Win32 only, adjusts process working set so that minimum is at least
|
|
// dwMinKb and maximum is at least dwMaxKb.
|
|
// Return value is zero if all went OK, or a code from 1 to 3 indicating the
|
|
// cause of the failure:
|
|
//
|
|
// 1 unable to access process (insufficient rights?)
|
|
// 2 unable to read process working set
|
|
// 3 unable to set process working set - bad parameters?
|
|
U14API(short) U14WorkingSet(DWORD dwMinKb, DWORD dwMaxKb)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
short sRetVal = 0; // 0 means all is OK
|
|
HANDLE hProcess;
|
|
DWORD dwVer = GetVersion();
|
|
if (dwVer & 0x80000000) // is this not NT?
|
|
return 0; // then give up right now
|
|
|
|
// Now attempt to get information on working set size
|
|
hProcess = OpenProcess(STANDARD_RIGHTS_REQUIRED |
|
|
PROCESS_QUERY_INFORMATION |
|
|
PROCESS_SET_QUOTA,
|
|
FALSE, _getpid());
|
|
if (hProcess)
|
|
{
|
|
SIZE_T dwMinSize,dwMaxSize;
|
|
if (GetProcessWorkingSetSize(hProcess, &dwMinSize, &dwMaxSize))
|
|
{
|
|
DWORD dwMin = dwMinKb << 10; // convert from kb to bytes
|
|
DWORD dwMax = dwMaxKb << 10;
|
|
|
|
// if we get here, we have managed to read the current size
|
|
if (dwMin > dwMinSize) // need to change sizes?
|
|
dwMinSize = dwMin;
|
|
|
|
if (dwMax > dwMaxSize)
|
|
dwMaxSize = dwMax;
|
|
|
|
if (!SetProcessWorkingSetSize(hProcess, dwMinSize, dwMaxSize))
|
|
sRetVal = 3; // failed to change size
|
|
}
|
|
else
|
|
sRetVal = 2; // failed to read original size
|
|
|
|
CloseHandle(hProcess);
|
|
}
|
|
else
|
|
sRetVal = 1; // failed to get handle
|
|
|
|
return sRetVal;
|
|
#endif
|
|
#ifdef LINUX
|
|
if (dwMinKb | dwMaxKb)
|
|
{
|
|
// to stop compiler moaning
|
|
}
|
|
return U14ERR_NOERROR;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14UnSetTransfer Cancels a transfer area
|
|
** wArea The index of a block previously used in by SetTransfer
|
|
*****************************************************************************/
|
|
U14API(short) U14UnSetTransfer(short hand, WORD wArea)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
#ifdef _IS_WINDOWS_
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
TCSBLOCK csBlock;
|
|
csBlock.ints[0] = (short)wArea; /* Area number into control block */
|
|
sErr = U14Control1401(hand, U14_UNSETTRANSFER, &csBlock); /* Free area */
|
|
|
|
VirtualUnlock(apAreas[hand][wArea], auAreas[hand][wArea]);/* Unlock */
|
|
apAreas[hand][wArea] = NULL; /* Clear locations */
|
|
auAreas[hand][wArea] = 0;
|
|
}
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
return (sErr == U14ERR_NOERROR) ? CED_UnsetTransfer(aHand1401[hand], wArea) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14SetTransArea Sets an area up to be used for transfers
|
|
** WORD wArea The area number to set up
|
|
** void *pvBuff The address of the buffer for the data.
|
|
** DWORD dwLength The length of the buffer for the data
|
|
** short eSz The element size (used for byte swapping on the Mac)
|
|
****************************************************************************/
|
|
U14API(short) U14SetTransArea(short hand, WORD wArea, void *pvBuff,
|
|
DWORD dwLength, short eSz)
|
|
{
|
|
TRANSFERDESC td;
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr != U14ERR_NOERROR)
|
|
return sErr;
|
|
if (wArea >= MAX_TRANSAREAS) // Is this a valid area number
|
|
return U14ERR_BADAREA;
|
|
|
|
#ifdef _IS_WINDOWS_
|
|
assert(apAreas[hand][wArea] == NULL);
|
|
assert(auAreas[hand][wArea] == 0);
|
|
|
|
apAreas[hand][wArea] = pvBuff; /* Save data for later */
|
|
auAreas[hand][wArea] = dwLength;
|
|
|
|
if (!VirtualLock(pvBuff, dwLength)) /* Lock using WIN32 calls */
|
|
{
|
|
apAreas[hand][wArea] = NULL; /* Clear locations */
|
|
auAreas[hand][wArea] = 0;
|
|
return U14ERR_LOCKERR; /* VirtualLock failed */
|
|
}
|
|
#ifndef _WIN64
|
|
if (!USE_NT_DIOC(hand)) /* Use Win 9x DIOC? */
|
|
{
|
|
DWORD dwBytes;
|
|
VXTRANSFERDESC vxDesc; /* Structure to pass to VXD */
|
|
vxDesc.wArea = wArea; /* Copy across simple params */
|
|
vxDesc.dwLength = dwLength;
|
|
|
|
// Check we are not asking an old driver for more than area 0
|
|
if ((wArea != 0) && (U14DriverVersion(hand) < 0x00010002L))
|
|
sErr = U14ERR_DRIVTOOOLD;
|
|
else
|
|
{
|
|
vxDesc.dwAddrOfs = (DWORD)pvBuff; /* 32 bit offset */
|
|
vxDesc.wAddrSel = 0;
|
|
|
|
if (DeviceIoControl(aHand1401[hand], (DWORD)U14_SETTRANSFER,
|
|
pvBuff,dwLength, /* Will translate pointer */
|
|
&vxDesc,sizeof(VXTRANSFERDESC),
|
|
&dwBytes,NULL))
|
|
{
|
|
if (dwBytes >= sizeof(VXTRANSFERDESC)) /* Driver OK ? */
|
|
sErr = U14ERR_NOERROR;
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS; /* Else never got there */
|
|
}
|
|
else
|
|
sErr = (short)GetLastError();
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
PARAMBLK rWork;
|
|
DWORD dwBytes;
|
|
td.wArea = wArea; /* Pure NT - put data into struct */
|
|
td.lpvBuff = pvBuff;
|
|
td.dwLength = dwLength;
|
|
td.eSize = 0; // Dummy element size
|
|
|
|
if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETTRANSFER,
|
|
&td,sizeof(TRANSFERDESC),
|
|
&rWork,sizeof(PARAMBLK),&dwBytes,NULL))
|
|
{
|
|
if (dwBytes >= sizeof(PARAMBLK)) // maybe error from driver?
|
|
sErr = rWork.sState; // will report any error
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS; // Else never got there
|
|
}
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS;
|
|
}
|
|
|
|
if (sErr != U14ERR_NOERROR)
|
|
{
|
|
if (sErr != U14ERR_LOCKERR) // unless lock failed...
|
|
VirtualUnlock(pvBuff, dwLength); // ...release the lock
|
|
apAreas[hand][wArea] = NULL; // Clear locations
|
|
auAreas[hand][wArea] = 0;
|
|
}
|
|
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
// The strange cast is so that it works in 64 and 32-bit linux as long is 64-bits
|
|
// in the 64 bit version.
|
|
td.lpvBuff = (long long)((unsigned long)pvBuff);
|
|
td.wAreaNum = wArea;
|
|
td.dwLength = dwLength;
|
|
td.eSize = eSz; // Dummy element size
|
|
return CED_SetTransfer(aHand1401[hand], &td);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14SetTransferEvent Sets an event for notification of application
|
|
** wArea The tranfer area index, from 0 to MAXAREAS-1
|
|
** bEvent True to create an event, false to remove it
|
|
** bToHost Set 0 for notification on to1401 tranfers, 1 for
|
|
** notification of transfers to the host PC
|
|
** dwStart The offset of the sub-area of interest
|
|
** dwLength The size of the sub-area of interest
|
|
**
|
|
** The device driver will set the event supplied to the signalled state
|
|
** whenever a DMA transfer to/from the specified area is completed. The
|
|
** transfer has to be in the direction specified by bToHost, and overlap
|
|
** that part of the whole transfer area specified by dwStart and dwLength.
|
|
** It is important that this function is called with bEvent false to release
|
|
** the event once 1401 activity is finished.
|
|
**
|
|
** Returns 1 if an event handle exists, 0 if all OK and no event handle or
|
|
** a negative code for an error.
|
|
****************************************************************************/
|
|
U14API(short) U14SetTransferEvent(short hand, WORD wArea, BOOL bEvent,
|
|
BOOL bToHost, DWORD dwStart, DWORD dwLength)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
TCSBLOCK csBlock;
|
|
short sErr = U14TransferFlags(hand); // see if we can handle events
|
|
if (sErr >= U14ERR_NOERROR) // check handle is OK
|
|
{
|
|
bEvent = bEvent && ((sErr & U14TF_NOTIFY) != 0); // remove request if we cannot do events
|
|
if (wArea >= MAX_TRANSAREAS) // Check a valid area...
|
|
return U14ERR_BADAREA; // ...and bail of not
|
|
|
|
// We can hold an event for each area, so see if we need to change the
|
|
// state of the event.
|
|
if ((bEvent != 0) != (aXferEvent[hand] != 0)) // change of event state?
|
|
{
|
|
if (bEvent) // want one and none present
|
|
aXferEvent[hand] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
else
|
|
{
|
|
CloseHandle(aXferEvent[hand]); // clear the existing event
|
|
aXferEvent[hand] = NULL; // and clear handle
|
|
}
|
|
}
|
|
|
|
// We have to store the parameters differently for 64-bit operations
|
|
// because a handle is 64 bits long. The drivers know of this and
|
|
// handle the information appropriately.
|
|
#ifdef _WIN64
|
|
csBlock.longs[0] = wArea; // Pass paramaters into the driver...
|
|
if (bToHost != 0) // The direction flag is held in the
|
|
csBlock.longs[0] |= 0x10000; // upper word of the transfer area value
|
|
*((HANDLE*)&csBlock.longs[1]) = aXferEvent[hand]; // The event handle is 64-bits
|
|
csBlock.longs[3] = dwStart; // Thankfully these two remain
|
|
csBlock.longs[4] = dwLength; // as unsigned 32-bit values
|
|
#else
|
|
csBlock.longs[0] = wArea; // pass paramaters into the driver...
|
|
csBlock.longs[1] = (long)aXferEvent[hand]; // ...especially the event handle
|
|
csBlock.longs[2] = bToHost;
|
|
csBlock.longs[3] = dwStart;
|
|
csBlock.longs[4] = dwLength;
|
|
#endif
|
|
sErr = U14Control1401(hand, U14_SETTRANSEVENT, &csBlock);
|
|
if (sErr == U14ERR_NOERROR)
|
|
sErr = (short)(aXferEvent[hand] != NULL); // report if we have a flag
|
|
}
|
|
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
TRANSFEREVENT te;
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr != U14ERR_NOERROR)
|
|
return sErr;
|
|
|
|
if (wArea >= MAX_TRANSAREAS) // Is this a valid area number
|
|
return U14ERR_BADAREA;
|
|
|
|
te.wAreaNum = wArea; // copy parameters to the control block
|
|
te.wFlags = bToHost ? 1 : 0; // bit 0 sets the direction
|
|
te.dwStart = dwStart; // start offset of the event area
|
|
te.dwLength = dwLength; // size of the event area
|
|
te.iSetEvent = bEvent; // in Windows, this creates/destroys the event
|
|
return CED_SetEvent(aHand1401[hand], &te);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14TestTransferEvent
|
|
** Would a U14WaitTransferEvent() call return immediately? return 1 if so,
|
|
** 0 if not or a negative code if a problem.
|
|
****************************************************************************/
|
|
U14API(int) U14TestTransferEvent(short hand, WORD wArea)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
int iErr = CheckHandle(hand);
|
|
if (iErr == U14ERR_NOERROR)
|
|
{
|
|
if (aXferEvent[hand]) // if a handle is set...
|
|
iErr = WaitForSingleObject(aXferEvent[hand], 0) == WAIT_OBJECT_0;
|
|
}
|
|
return iErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_TestEvent(aHand1401[hand], wArea) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14WaitTransferEvent
|
|
** Wait for a transfer event with a timeout.
|
|
** msTimeOut is 0 for an infinite wait, else it is the maximum time to wait
|
|
** in milliseconds in range 0-0x00ffffff.
|
|
** Returns If no event handle then return immediately. Else return 1 if
|
|
** timed out or 0=event, and a negative code if a problem.
|
|
****************************************************************************/
|
|
U14API(int) U14WaitTransferEvent(short hand, WORD wArea, int msTimeOut)
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
int iErr = CheckHandle(hand);
|
|
if (iErr == U14ERR_NOERROR)
|
|
{
|
|
if (aXferEvent[hand])
|
|
{
|
|
if (msTimeOut == 0)
|
|
msTimeOut = INFINITE;
|
|
iErr = WaitForSingleObject(aXferEvent[hand], msTimeOut) != WAIT_OBJECT_0;
|
|
}
|
|
else
|
|
iErr = TRUE; // say we timed out if no event
|
|
}
|
|
return iErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
short sErr = CheckHandle(hand);
|
|
return (sErr == U14ERR_NOERROR) ? CED_WaitEvent(aHand1401[hand], wArea, msTimeOut) : sErr;
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** U14SetCircular Sets an area up for circular DMA transfers
|
|
** WORD wArea The area number to set up
|
|
** BOOL bToHost Sets the direction of data transfer
|
|
** void *pvBuff The address of the buffer for the data
|
|
** DWORD dwLength The length of the buffer for the data
|
|
****************************************************************************/
|
|
U14API(short) U14SetCircular(short hand, WORD wArea, BOOL bToHost,
|
|
void *pvBuff, DWORD dwLength)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr != U14ERR_NOERROR)
|
|
return sErr;
|
|
|
|
if (wArea >= MAX_TRANSAREAS) /* Is this a valid area number */
|
|
return U14ERR_BADAREA;
|
|
|
|
if (!bToHost) /* For now, support tohost transfers only */
|
|
return U14ERR_BADAREA; /* best error code I can find */
|
|
#ifdef _IS_WINDOWS_
|
|
assert(apAreas[hand][wArea] == NULL);
|
|
assert(auAreas[hand][wArea] == 0);
|
|
|
|
apAreas[hand][wArea] = pvBuff; /* Save data for later */
|
|
auAreas[hand][wArea] = dwLength;
|
|
|
|
if (!VirtualLock(pvBuff, dwLength)) /* Lock using WIN32 calls */
|
|
sErr = U14ERR_LOCKERR; /* VirtualLock failed */
|
|
else
|
|
{
|
|
PARAMBLK rWork;
|
|
DWORD dwBytes;
|
|
TRANSFERDESC txDesc;
|
|
txDesc.wArea = wArea; /* Pure NT - put data into struct */
|
|
txDesc.lpvBuff = pvBuff;
|
|
txDesc.dwLength = dwLength;
|
|
txDesc.eSize = (short)bToHost; /* Use this for direction flag */
|
|
|
|
if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETCIRCULAR,
|
|
&txDesc, sizeof(TRANSFERDESC),
|
|
&rWork, sizeof(PARAMBLK),&dwBytes,NULL))
|
|
{
|
|
if (dwBytes >= sizeof(PARAMBLK)) /* error from driver? */
|
|
sErr = rWork.sState; /* No, just return driver data */
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS; /* Else never got there */
|
|
}
|
|
else
|
|
sErr = U14ERR_DRIVCOMMS;
|
|
}
|
|
|
|
if (sErr != U14ERR_NOERROR)
|
|
{
|
|
if (sErr != U14ERR_LOCKERR)
|
|
VirtualUnlock(pvBuff, dwLength); /* Release NT lock */
|
|
apAreas[hand][wArea] = NULL; /* Clear locations */
|
|
auAreas[hand][wArea] = 0;
|
|
}
|
|
|
|
return sErr;
|
|
#endif
|
|
#ifdef LINUX
|
|
else
|
|
{
|
|
TRANSFERDESC td;
|
|
td.lpvBuff = (long long)((unsigned long)pvBuff);
|
|
td.wAreaNum = wArea;
|
|
td.dwLength = dwLength;
|
|
td.eSize = (short)bToHost; /* Use this for direction flag */
|
|
return CED_SetCircular(aHand1401[hand], &td);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
** Function GetCircBlk returns the size (& start offset) of the next
|
|
** available block of circular data.
|
|
****************************************************************************/
|
|
U14API(int) U14GetCircBlk(short hand, WORD wArea, DWORD *pdwOffs)
|
|
{
|
|
int lErr = CheckHandle(hand);
|
|
if (lErr != U14ERR_NOERROR)
|
|
return lErr;
|
|
|
|
if (wArea >= MAX_TRANSAREAS) // Is this a valid area number?
|
|
return U14ERR_BADAREA;
|
|
else
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
PARAMBLK rWork;
|
|
TCSBLOCK csBlock;
|
|
DWORD dwBytes;
|
|
csBlock.longs[0] = wArea; // Area number into control block
|
|
rWork.sState = U14ERR_DRIVCOMMS;
|
|
if (DeviceIoControl(aHand1401[hand], (DWORD)U14_GETCIRCBLK, &csBlock, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
|
|
(dwBytes >= sizeof(PARAMBLK)))
|
|
lErr = rWork.sState;
|
|
else
|
|
lErr = U14ERR_DRIVCOMMS;
|
|
|
|
if (lErr == U14ERR_NOERROR) // Did everything go OK?
|
|
{ // Yes, we can pass the results back
|
|
lErr = rWork.csBlock.longs[1]; // Return the block information
|
|
*pdwOffs = rWork.csBlock.longs[0]; // Offset is first in array
|
|
}
|
|
#endif
|
|
#ifdef LINUX
|
|
TCIRCBLOCK cb;
|
|
cb.nArea = wArea; // Area number into control block
|
|
cb.dwOffset = 0;
|
|
cb.dwSize = 0;
|
|
lErr = CED_GetCircBlock(aHand1401[hand], &cb);
|
|
if (lErr == U14ERR_NOERROR) // Did everything go OK?
|
|
{ // Yes, we can pass the results back
|
|
lErr = cb.dwSize; // return the size
|
|
*pdwOffs = cb.dwOffset; // and the offset
|
|
}
|
|
#endif
|
|
}
|
|
return lErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** Function FreeCircBlk marks the specified area of memory as free for
|
|
** resuse for circular transfers and returns the size (& start
|
|
** offset) of the next available block of circular data.
|
|
****************************************************************************/
|
|
U14API(int) U14FreeCircBlk(short hand, WORD wArea, DWORD dwOffs, DWORD dwSize,
|
|
DWORD *pdwOffs)
|
|
{
|
|
int lErr = CheckHandle(hand);
|
|
if (lErr != U14ERR_NOERROR)
|
|
return lErr;
|
|
|
|
if (wArea < MAX_TRANSAREAS) // Is this a valid area number
|
|
{
|
|
#ifdef _IS_WINDOWS_
|
|
PARAMBLK rWork;
|
|
TCSBLOCK csBlock;
|
|
DWORD dwBytes;
|
|
csBlock.longs[0] = wArea; // Area number into control block
|
|
csBlock.longs[1] = dwOffs;
|
|
csBlock.longs[2] = dwSize;
|
|
rWork.sState = U14ERR_DRIVCOMMS;
|
|
if (DeviceIoControl(aHand1401[hand], (DWORD)U14_FREECIRCBLK, &csBlock, sizeof(TCSBLOCK),
|
|
&rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
|
|
(dwBytes >= sizeof(PARAMBLK)))
|
|
lErr = rWork.sState;
|
|
else
|
|
lErr = U14ERR_DRIVCOMMS;
|
|
if (lErr == U14ERR_NOERROR) // Did everything work OK?
|
|
{ // Yes, we can pass the results back
|
|
lErr = rWork.csBlock.longs[1]; // Return the block information
|
|
*pdwOffs = rWork.csBlock.longs[0]; // Offset is first in array
|
|
}
|
|
#endif
|
|
#ifdef LINUX
|
|
TCIRCBLOCK cb;
|
|
cb.nArea = wArea; // Area number into control block
|
|
cb.dwOffset = dwOffs;
|
|
cb.dwSize = dwSize;
|
|
|
|
lErr = CED_FreeCircBlock(aHand1401[hand], &cb);
|
|
if (lErr == U14ERR_NOERROR) // Did everything work OK?
|
|
{ // Yes, we can pass the results back
|
|
lErr = cb.dwSize; // Return the block information
|
|
*pdwOffs = cb.dwOffset; // Offset is first in array
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
lErr = U14ERR_BADAREA;
|
|
|
|
return lErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** Transfer
|
|
** Transfer moves data to 1401 or to host
|
|
** Assumes memory is allocated and locked,
|
|
** which it should be to get a pointer
|
|
*****************************************************************************/
|
|
static short Transfer(short hand, BOOL bTo1401, char* pData,
|
|
DWORD dwSize, DWORD dw1401, short eSz)
|
|
{
|
|
char strcopy[MAXSTRLEN+1]; // to hold copy of work string
|
|
short sResult = U14SetTransArea(hand, 0, (void *)pData, dwSize, eSz);
|
|
if (sResult == U14ERR_NOERROR) // no error
|
|
{
|
|
sprintf(strcopy, // data offset is always 0
|
|
"TO%s,$%X,$%X,0;", bTo1401 ? "1401" : "HOST", dw1401, dwSize);
|
|
|
|
U14SendString(hand, strcopy); // send transfer string
|
|
|
|
sResult = U14CheckErr(hand); // Use ERR command to check for done
|
|
if (sResult > 0)
|
|
sResult = U14ERR_TOXXXERR; // If a 1401 error, use this code
|
|
|
|
U14UnSetTransfer(hand, 0);
|
|
}
|
|
return sResult;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** Function ToHost transfers data into the host from the 1401
|
|
****************************************************************************/
|
|
U14API(short) U14ToHost(short hand, char* pAddrHost, DWORD dwSize,
|
|
DWORD dw1401, short eSz)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if ((sErr == U14ERR_NOERROR) && dwSize) // TOHOST is a constant
|
|
sErr = Transfer(hand, TOHOST, pAddrHost, dwSize, dw1401, eSz);
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** Function To1401 transfers data into the 1401 from the host
|
|
****************************************************************************/
|
|
U14API(short) U14To1401(short hand, const char* pAddrHost,DWORD dwSize,
|
|
DWORD dw1401, short eSz)
|
|
{
|
|
short sErr = CheckHandle(hand);
|
|
if ((sErr == U14ERR_NOERROR) && dwSize) // TO1401 is a constant
|
|
sErr = Transfer(hand, TO1401, (char*)pAddrHost, dwSize, dw1401, eSz);
|
|
return sErr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
** Function LdCmd Loads a command from a full path or just a file
|
|
*****************************************************************************/
|
|
#ifdef _IS_WINDOWS_
|
|
#define file_exist(name) (_access(name, 0) != -1)
|
|
#define file_open(name) _lopen(name, OF_READ)
|
|
#define file_close(h) _lclose(h)
|
|
#define file_seek(h, pos) _llseek(h, pos, FILE_BEGIN)
|
|
#define file_read(h, buffer, size) (_lread(h, buffer, size) == size)
|
|
#endif
|
|
#ifdef LINUX
|
|
#define file_exist(name) (access(name, F_OK) != -1)
|
|
#define file_open(name) open(name, O_RDONLY)
|
|
#define file_close(h) close(h)
|
|
#define file_seek(h, pos) lseek(h, pos, SEEK_SET)
|
|
#define file_read(h, buffer, size) (read(h, buffer, size) == (ssize_t)size)
|
|
static DWORD GetModuleFileName(void* dummy, char* buffer, int max)
|
|
{
|
|
// The following works for Linux systems with a /proc file system.
|
|
char szProcPath[32];
|
|
sprintf(szProcPath, "/proc/%d/exe", getpid()); // attempt to read link
|
|
if (readlink(szProcPath, buffer, max) != -1)
|
|
{
|
|
dirname (buffer);
|
|
strcat (buffer, "/");
|
|
return strlen(buffer);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
U14API(short) U14LdCmd(short hand, const char* command)
|
|
{
|
|
char strcopy[MAXSTRLEN+1]; // to hold copy of work string
|
|
BOOL bGotIt = FALSE; // have we found the command file?
|
|
int iFHandle; // file handle of command
|
|
#define FNSZ 260
|
|
char filnam[FNSZ]; // space to build name in
|
|
char szCmd[25]; // just the command name with extension
|
|
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr != U14ERR_NOERROR)
|
|
return sErr;
|
|
|
|
if (strchr(command, '.') != NULL) // see if we have full name
|
|
{
|
|
if (file_exist(command)) // If the file exists
|
|
{
|
|
strcpy(filnam, command); // use name as is
|
|
bGotIt = TRUE; // Flag no more searching
|
|
}
|
|
else // not found, get file name for search
|
|
{
|
|
char* pStr = strrchr(command, PATHSEP); // Point to last separator
|
|
if (pStr != NULL) // Check we got it
|
|
{
|
|
pStr++; // move past the backslash
|
|
strcpy(szCmd, pStr); // copy file name as is
|
|
}
|
|
else
|
|
strcpy(szCmd, command); // use as is
|
|
}
|
|
}
|
|
else // File extension not supplied, so build the command file name
|
|
{
|
|
char szExt[8];
|
|
strcpy(szCmd, command); // Build command file name
|
|
ExtForType(asType1401[hand], szExt);// File extension string
|
|
strcat(szCmd, szExt); // add it to the end
|
|
}
|
|
|
|
// Next place to look is in the 1401 folder in the same place as the
|
|
// application was run from.
|
|
if (!bGotIt) // Still not got it?
|
|
{
|
|
DWORD dwLen = GetModuleFileName(NULL, filnam, FNSZ); // Get app path
|
|
if (dwLen > 0) // and use it as path if found
|
|
{
|
|
char* pStr = strrchr(filnam, PATHSEP); // Point to last separator
|
|
if (pStr != NULL)
|
|
{
|
|
*(++pStr) = 0; // Terminate string there
|
|
if (strlen(filnam) < FNSZ-6) // make sure we have space
|
|
{
|
|
strcat(filnam, "1401" PATHSEPSTR); // add in 1401 subdir
|
|
strcat(filnam,szCmd);
|
|
bGotIt = (BOOL)file_exist(filnam); // See if file exists
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Next place to look is in whatever path is set by the 1401DIR environment
|
|
// variable, if it exists.
|
|
if (!bGotIt) // Need to do more searches?/
|
|
{
|
|
char* pStr = getenv("1401DIR"); // Try to find environment var
|
|
if (pStr != NULL) // and use it as path if found
|
|
{
|
|
strcpy(filnam, pStr); // Use path in environment
|
|
if (filnam[strlen(filnam)-1] != PATHSEP)// We need separator
|
|
strcat(filnam, PATHSEPSTR);
|
|
strcat(filnam, szCmd);
|
|
bGotIt = (BOOL)file_exist(filnam); // Got this one?
|
|
}
|
|
}
|
|
|
|
// Last place to look is the default location.
|
|
if (!bGotIt) // Need to do more searches?
|
|
{
|
|
strcpy(filnam, DEFCMDPATH); // Use default path
|
|
strcat(filnam, szCmd);
|
|
bGotIt = file_exist(filnam); // Got this one?
|
|
}
|
|
|
|
iFHandle = file_open(filnam);
|
|
if (iFHandle == -1)
|
|
sErr = U14ERR_NOFILE;
|
|
else
|
|
{ // first read in the header block
|
|
CMDHEAD rCmdHead; // to hold the command header
|
|
if (file_read(iFHandle, &rCmdHead, sizeof(CMDHEAD)))
|
|
{
|
|
size_t nComSize = rCmdHead.wCmdSize;
|
|
char* pMem = malloc(nComSize);
|
|
if (pMem != NULL)
|
|
{
|
|
file_seek(iFHandle, sizeof(CMDHEAD));
|
|
if (file_read(iFHandle, pMem, (UINT)nComSize))
|
|
{
|
|
sErr = U14SetTransArea(hand, 0, (void *)pMem, (DWORD)nComSize, ESZBYTES);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
sprintf(strcopy, "CLOAD,0,$%X;", (int)nComSize);
|
|
sErr = U14SendString(hand, strcopy);
|
|
if (sErr == U14ERR_NOERROR)
|
|
{
|
|
sErr = U14CheckErr(hand); // Use ERR to check for done
|
|
if (sErr > 0)
|
|
sErr = U14ERR_CLOADERR; // If an error, this code
|
|
}
|
|
U14UnSetTransfer(hand, 0); // release transfer area
|
|
}
|
|
}
|
|
else
|
|
sErr = U14ERR_READERR;
|
|
free(pMem);
|
|
}
|
|
else
|
|
sErr = U14ERR_HOSTSPACE; // memory allocate failed
|
|
}
|
|
else
|
|
sErr = U14ERR_READERR;
|
|
|
|
file_close(iFHandle); // close the file
|
|
}
|
|
|
|
return sErr;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
** Ld
|
|
** Loads a command into the 1401
|
|
** Returns NOERROR code or a long with error in lo word and index of
|
|
** command that failed in high word
|
|
****************************************************************************/
|
|
U14API(DWORD) U14Ld(short hand, const char* vl, const char* str)
|
|
{
|
|
DWORD dwIndex = 0; // index to current command
|
|
long lErr = U14ERR_NOERROR; // what the error was that went wrong
|
|
char strcopy[MAXSTRLEN+1]; // stores unmodified str parameter
|
|
char szFExt[8]; // The command file extension
|
|
short sErr = CheckHandle(hand);
|
|
if (sErr != U14ERR_NOERROR)
|
|
return sErr;
|
|
|
|
ExtForType(asType1401[hand], szFExt); // File extension string
|
|
strcpy(strcopy, str); // to avoid changing original
|
|
|
|
// now break out one command at a time and see if loaded
|
|
if (*str) // if anything there
|
|
{
|
|
BOOL bDone = FALSE; // true when finished all commands
|
|
int iLoop1 = 0; // Point at start of string for command name
|
|
int iLoop2 = 0; // and at start of str parameter
|
|
do // repeat until end of str
|
|
{
|
|
char filnam[MAXSTRLEN+1]; // filename to use
|
|
char szFName[MAXSTRLEN+1]; // filename work string
|
|
|
|
if (!strcopy[iLoop1]) // at the end of the string?
|
|
bDone = TRUE; // set the finish flag
|
|
|
|
if (bDone || (strcopy[iLoop1] == ',')) // end of cmd?
|
|
{
|
|
U14LONG er[5]; // Used to read back error results
|
|
++dwIndex; // Keep count of command number, first is 1
|
|
szFName[iLoop2]=(char)0; // null terminate name of command
|
|
|
|
strncpy(szLastName, szFName, sizeof(szLastName)); // Save for error info
|
|
szLastName[sizeof(szLastName)-1] = 0;
|
|
strncat(szLastName, szFExt, sizeof(szLastName)); // with extension included
|
|
szLastName[sizeof(szLastName)-1] = 0;
|
|
|
|
U14SendString(hand, szFName); // ask if loaded
|
|
U14SendString(hand, ";ERR;"); // add err return
|
|
|
|
lErr = U14LongsFrom1401(hand, er, 5);
|
|
if (lErr > 0)
|
|
{
|
|
lErr = U14ERR_NOERROR;
|
|
if (er[0] == 255) // if command not loaded at all
|
|
{
|
|
if (vl && *vl) // if we have a path name
|
|
{
|
|
strcpy(filnam, vl);
|
|
if (strchr("\\/:", filnam[strlen(filnam)-1]) == NULL)
|
|
strcat(filnam, PATHSEPSTR); // add separator if none found
|
|
strcat(filnam, szFName); // add the file name
|
|
strcat(filnam, szFExt); // and extension
|
|
}
|
|
else
|
|
strcpy(filnam, szFName); // simple name
|
|
|
|
lErr = U14LdCmd(hand, filnam); // load cmd
|
|
if (lErr != U14ERR_NOERROR) // spot any errors
|
|
bDone = TRUE; // give up if an error
|
|
}
|
|
}
|
|
else
|
|
bDone = TRUE; // give up if an error
|
|
|
|
iLoop2 = 0; // Reset pointer to command name string
|
|
++iLoop1; // and move on through str parameter
|
|
}
|
|
else
|
|
szFName[iLoop2++] = strcopy[iLoop1++]; // no command end, so copy 1 char
|
|
}
|
|
while (!bDone);
|
|
}
|
|
|
|
if (lErr == U14ERR_NOERROR)
|
|
{
|
|
szLastName[0] = 0; // No error, so clean out command name here
|
|
return lErr;
|
|
}
|
|
else
|
|
return ((dwIndex<<16) | ((DWORD)lErr & 0x0000FFFF));
|
|
}
|
|
|
|
// Initialise the library (if not initialised) and return the library version
|
|
U14API(int) U14InitLib(void)
|
|
{
|
|
int iRetVal = U14LIB_VERSION;
|
|
if (iAttached == 0) // only do this the first time please
|
|
{
|
|
int i;
|
|
#ifdef _IS_WINDOWS_
|
|
int j;
|
|
DWORD dwVersion = GetVersion();
|
|
bWindows9x = FALSE; // Assume not Win9x
|
|
|
|
if (dwVersion & 0x80000000) // if not windows NT
|
|
{
|
|
if ((LOBYTE(LOWORD(dwVersion)) < 4) && // if Win32s or...
|
|
(HIBYTE(LOWORD(dwVersion)) < 95)) // ...below Windows 95
|
|
iRetVal = 0; // We do not support this
|
|
else
|
|
bWindows9x = TRUE; // Flag we have Win9x
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < MAX1401; i++) // initialise the device area
|
|
{
|
|
aHand1401[i] = INVALID_HANDLE_VALUE; // Clear handle values
|
|
asType1401[i] = U14TYPEUNKNOWN; // and 1401 type codes
|
|
alTimeOutPeriod[i] = 3000; // 3 second timeouts
|
|
#ifdef _IS_WINDOWS_
|
|
#ifndef _WIN64
|
|
abUseNTDIOC[i] = (BOOL)!bWindows9x;
|
|
#endif
|
|
aXferEvent[i] = NULL; // there are no Xfer events
|
|
for (j = 0; j < MAX_TRANSAREAS; j++) // Clear out locked area info
|
|
{
|
|
apAreas[i][j] = NULL;
|
|
auAreas[i][j] = 0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
return iRetVal;
|
|
}
|
|
|
|
///--------------------------------------------------------------------------------
|
|
/// Functions called when the library is loaded and unloaded to give us a chance to
|
|
/// setup the library.
|
|
|
|
|
|
#ifdef _IS_WINDOWS_
|
|
#ifndef U14_NOT_DLL
|
|
/****************************************************************************
|
|
** FUNCTION: DllMain(HANDLE, DWORD, LPVOID)
|
|
** LibMain is called by Windows when the DLL is initialized, Thread Attached,
|
|
** and other times. Refer to SDK documentation, as to the different ways this
|
|
** may be called.
|
|
****************************************************************************/
|
|
INT APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved)
|
|
{
|
|
int iRetVal = 1;
|
|
|
|
switch (ul_reason_being_called)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
iRetVal = U14InitLib() > 0; // does nothing if iAttached != 0
|
|
++iAttached; // count times attached
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
if (--iAttached == 0) // last man out?
|
|
U14CloseAll(); // release all open handles
|
|
break;
|
|
}
|
|
return iRetVal;
|
|
|
|
UNREFERENCED_PARAMETER(lpReserved);
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef LINUX
|
|
void __attribute__((constructor)) use1401_load(void)
|
|
{
|
|
U14InitLib();
|
|
++iAttached;
|
|
}
|
|
|
|
void __attribute__((destructor)) use1401_unload(void)
|
|
{
|
|
if (--iAttached == 0) // last man out?
|
|
U14CloseAll(); // release all open handles
|
|
}
|
|
#endif
|