| /**************************************************************************** |
| ** 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 accommodate 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 higher 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 compatibility 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 |
| unsigned short wBasicSz; // size as seen by BASIC |
| unsigned short 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) |
| { |
| unsigned short 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, unsigned short 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, unsigned short 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, (unsigned short)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, unsigned short 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; |
| *((unsigned short *)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; |
| |
| *((unsigned short *)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, unsigned short 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 ((unsigned short)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, unsigned short 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 |
| ** unsigned short 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, unsigned short 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 transfer 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, unsigned short 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, unsigned short 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, unsigned short 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 |
| ** unsigned short 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, unsigned short 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, unsigned short 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, unsigned short 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 |