diff options
Diffstat (limited to 'drivers/staging/ced1401/userspace/use1401.c')
-rw-r--r-- | drivers/staging/ced1401/userspace/use1401.c | 3035 |
1 files changed, 3035 insertions, 0 deletions
diff --git a/drivers/staging/ced1401/userspace/use1401.c b/drivers/staging/ced1401/userspace/use1401.c new file mode 100644 index 00000000000..d4c63168ea2 --- /dev/null +++ b/drivers/staging/ced1401/userspace/use1401.c @@ -0,0 +1,3035 @@ +/**************************************************************************** +** 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 |