--- /dev/null
+obj-m := cedusb.o
+cedusb-objs := usb1401.o ced_ioc.o
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+KBUILD_EXTRA_SYMBOLS := $(PWD)
+EXTRA_CFLAGS = -I$(HOME)/src/ced1401
+all:
+ $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
+
+clean:
+ $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
+
--- /dev/null
+/* ced_ioc.c
+ ioctl part of the 1401 usb device driver for linux.
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ 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.
+*/
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/page-flags.h>
+#include <linux/pagemap.h>
+#include <linux/jiffies.h>
+
+#include "usb1401.h"
+
+/****************************************************************************
+** FlushOutBuff
+**
+** Empties the Output buffer and sets int lines. Used from user level only
+****************************************************************************/
+void FlushOutBuff(DEVICE_EXTENSION *pdx)
+{
+ dev_dbg(&pdx->interface->dev, "%s currentState=%d", __func__, pdx->sCurrentState);
+ if (pdx->sCurrentState == U14ERR_TIME) /* Do nothing if hardware in trouble */
+ return;
+// CharSend_Cancel(pdx); /* Kill off any pending I/O */
+ spin_lock_irq(&pdx->charOutLock);
+ pdx->dwNumOutput = 0;
+ pdx->dwOutBuffGet = 0;
+ pdx->dwOutBuffPut = 0;
+ spin_unlock_irq(&pdx->charOutLock);
+}
+
+/****************************************************************************
+**
+** FlushInBuff
+**
+** Empties the input buffer and sets int lines
+****************************************************************************/
+void FlushInBuff(DEVICE_EXTENSION *pdx)
+{
+ dev_dbg(&pdx->interface->dev, "%s currentState=%d", __func__, pdx->sCurrentState);
+ if (pdx->sCurrentState == U14ERR_TIME) /* Do nothing if hardware in trouble */
+ return;
+// CharRead_Cancel(pDevObject); /* Kill off any pending I/O */
+ spin_lock_irq(&pdx->charInLock);
+ pdx->dwNumInput = 0;
+ pdx->dwInBuffGet = 0;
+ pdx->dwInBuffPut = 0;
+ spin_unlock_irq(&pdx->charInLock);
+}
+
+/****************************************************************************
+** PutChars
+**
+** Utility routine to copy chars into the output buffer and fire them off.
+** called from user mode, holds charOutLock.
+****************************************************************************/
+static int PutChars(DEVICE_EXTENSION* pdx, const char* pCh, unsigned int uCount)
+{
+ int iReturn;
+ spin_lock_irq(&pdx->charOutLock); // get the output spin lock
+ if ((OUTBUF_SZ - pdx->dwNumOutput) >= uCount)
+ {
+ unsigned int u;
+ for (u=0; u<uCount; u++)
+ {
+ pdx->outputBuffer[pdx->dwOutBuffPut++] = pCh[u];
+ if (pdx->dwOutBuffPut >= OUTBUF_SZ)
+ pdx->dwOutBuffPut = 0;
+ }
+ pdx->dwNumOutput += uCount;
+ spin_unlock_irq(&pdx->charOutLock);
+ iReturn = SendChars(pdx); // ...give a chance to transmit data
+ }
+ else
+ {
+ iReturn = U14ERR_NOOUT; // no room at the out (ha-ha)
+ spin_unlock_irq(&pdx->charOutLock);
+ }
+ return iReturn;
+}
+
+/*****************************************************************************
+** Add the data in pData (local pointer) of length n to the output buffer, and
+** trigger an output transfer if this is appropriate. User mode.
+** Holds the io_mutex
+*****************************************************************************/
+int SendString(DEVICE_EXTENSION* pdx, const char __user* pData, unsigned int n)
+{
+ int iReturn = U14ERR_NOERROR; // assume all will be well
+ char buffer[OUTBUF_SZ+1]; // space in our address space for characters
+ if (n > OUTBUF_SZ) // check space in local buffer...
+ return U14ERR_NOOUT; // ...too many characters
+ if (copy_from_user(buffer, pData, n))
+ return -ENOMEM; // could not copy
+ buffer[n] = 0; // terminate for debug purposes
+
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ if (n > 0) // do nothing if nowt to do!
+ {
+ dev_dbg(&pdx->interface->dev, "%s n=%d>%s<", __func__, n, buffer);
+ iReturn = PutChars(pdx, buffer, n);
+ }
+
+ Allowi(pdx, false); // make sure we have input int
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** SendChar
+**
+** Sends a single character to the 1401. User mode, holds io_mutex.
+****************************************************************************/
+int SendChar(DEVICE_EXTENSION *pdx, char c)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ iReturn = PutChars(pdx, &c, 1);
+ dev_dbg(&pdx->interface->dev,"SendChar >%c< (0x%02x)", c, c);
+ Allowi(pdx, false); // Make sure char reads are running
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/***************************************************************************
+**
+** Get1401State
+**
+** Retrieves state information from the 1401, adjusts the 1401 state held
+** in the device extension to indicate the current 1401 type.
+**
+** *state is updated with information about the 1401 state as returned by the
+** 1401. The low byte is a code for what 1401 is doing:
+**
+** 0 normal 1401 operation
+** 1 sending chars to host
+** 2 sending block data to host
+** 3 reading block data from host
+** 4 sending an escape sequence to the host
+** 0x80 1401 is executing self-test, in which case the upper word
+** is the last error code seen (or zero for no new error).
+**
+** *error is updated with error information if a self-test error code
+** is returned in the upper word of state.
+**
+** both state and error are set to -1 if there are comms problems, and
+** to zero if there is a simple failure.
+**
+** return error code (U14ERR_NOERROR for OK)
+*/
+int Get1401State(DEVICE_EXTENSION* pdx, __u32* state, __u32* error)
+{
+ int nGot;
+ dev_dbg(&pdx->interface->dev, "Get1401State() entry");
+
+ *state = 0xFFFFFFFF; // Start off with invalid state
+ nGot = usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0),
+ GET_STATUS, (D_TO_H|VENDOR|DEVREQ), 0,0,
+ pdx->statBuf, sizeof(pdx->statBuf), HZ);
+ if (nGot != sizeof(pdx->statBuf))
+ {
+ dev_err(&pdx->interface->dev, "Get1401State() FAILED, return code %d", nGot);
+ pdx->sCurrentState = U14ERR_TIME; // Indicate that things are very wrong indeed
+ *state = 0; // Force status values to a known state
+ *error = 0;
+ }
+ else
+ {
+ int nDevice;
+ dev_dbg(&pdx->interface->dev, "Get1401State() Success, state: 0x%x, 0x%x",
+ pdx->statBuf[0], pdx->statBuf[1]);
+
+ *state = pdx->statBuf[0]; // Return the state values to the calling code
+ *error = pdx->statBuf[1];
+
+ nDevice = pdx->udev->descriptor.bcdDevice >> 8; // 1401 type code value
+ switch (nDevice) // so we can clean up current state
+ {
+ case 0:
+ pdx->sCurrentState = U14ERR_U1401;
+ break;
+
+ default: // allow lots of device codes for future 1401s
+ if ((nDevice >= 1) && (nDevice <= 23))
+ pdx->sCurrentState = (short)(nDevice + 6);
+ else
+ pdx->sCurrentState = U14ERR_ILL;
+ break;
+ }
+ }
+
+ return pdx->sCurrentState >= 0 ? U14ERR_NOERROR : pdx->sCurrentState;
+}
+
+/****************************************************************************
+** ReadWrite_Cancel
+**
+** Kills off staged read\write request from the USB if one is pending.
+****************************************************************************/
+int ReadWrite_Cancel(DEVICE_EXTENSION *pdx)
+{
+ dev_dbg(&pdx->interface->dev, "ReadWrite_Cancel entry %d", pdx->bStagedUrbPending);
+#ifdef NOT_WRITTEN_YET
+ int ntStatus = STATUS_SUCCESS;
+ bool bResult = false;
+ unsigned int i;
+ // We can fill this in when we know how we will implement the staged transfer stuff
+ spin_lock_irq(&pdx->stagedLock);
+
+ if (pdx->bStagedUrbPending) // anything to be cancelled? May need more...
+ {
+ dev_info(&pdx->interface-dev, "ReadWrite_Cancel about to cancel Urb");
+
+ // KeClearEvent(&pdx->StagingDoneEvent); // Clear the staging done flag
+ USB_ASSERT(pdx->pStagedIrp != NULL);
+
+ // Release the spinlock first otherwise the completion routine may hang
+ // on the spinlock while this function hands waiting for the event.
+ spin_unlock_irq(&pdx->stagedLock);
+ bResult = IoCancelIrp(pdx->pStagedIrp); // Actually do the cancel
+ if (bResult)
+ {
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = -10000000; // Use a timeout of 1 second
+ dev_info(&pdx->interface-dev, "ReadWrite_Cancel about to wait till done");
+ ntStatus = KeWaitForSingleObject(&pdx->StagingDoneEvent, Executive,
+ KernelMode, FALSE, &timeout);
+ }
+ else
+ {
+ dev_info(&pdx->interface-dev, "ReadWrite_Cancel, cancellation failed");
+ ntStatus = U14ERR_FAIL;
+ }
+ USB_KdPrint(DBGLVL_DEFAULT, ("ReadWrite_Cancel ntStatus = 0x%x decimal %d\n", ntStatus, ntStatus));
+ }
+ else
+ spin_unlock_irq(&pdx->stagedLock);
+
+ dev_info(&pdx->interface-dev, "ReadWrite_Cancel done");
+ return ntStatus;
+#else
+ return U14ERR_NOERROR;
+#endif
+
+}
+
+/***************************************************************************
+** InSelfTest - utility to check in self test. Return 1 for ST, 0 for not or
+** a -ve error code if we failed for some reason.
+***************************************************************************/
+static int InSelfTest(DEVICE_EXTENSION* pdx, unsigned int* pState)
+{
+ unsigned int state, error;
+ int iReturn = Get1401State(pdx, &state, &error); // see if in self-test
+ if (iReturn == U14ERR_NOERROR) // if all still OK
+ iReturn = (state == (unsigned int)-1) || // TX problem or...
+ ((state & 0xff) == 0x80); // ...self test
+ *pState = state; // return actual state
+ return iReturn;
+}
+
+/***************************************************************************
+** Is1401 - ALWAYS CALLED HOLDING THE io_mutex
+**
+** Tests for the current state of the 1401. Sets sCurrentState:
+**
+** U14ERR_NOIF 1401 i/f card not installed (not done here)
+** U14ERR_OFF 1401 apparently not switched on
+** U14ERR_NC 1401 appears to be not connected
+** U14ERR_ILL 1401 if it is there its not very well at all
+** U14ERR_TIME 1401 appears OK, but doesn't communicate - very bad
+** U14ERR_STD 1401 OK and ready for use
+** U14ERR_PLUS 1401+ OK and ready for use
+** U14ERR_U1401 Micro1401 OK and ready for use
+** U14ERR_POWER Power1401 OK and ready for use
+** U14ERR_U14012 Micro1401 mkII OK and ready for use
+**
+** Returns TRUE if a 1401 detected and OK, else FALSE
+****************************************************************************/
+bool Is1401(DEVICE_EXTENSION* pdx)
+{
+ int iReturn;
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ ced_draw_down(pdx); // wait for, then kill outstanding Urbs
+ FlushInBuff(pdx); // Clear out input buffer & pipe
+ FlushOutBuff(pdx); // Clear output buffer & pipe
+
+ // The next call returns 0 if OK, but has returned 1 in the past, meaning that
+ // usb_unlock_device() is needed... now it always is
+ iReturn = usb_lock_device_for_reset(pdx->udev, pdx->interface);
+
+ // release the io_mutex because if we don't, we will deadlock due to system
+ // calls back into the driver.
+ mutex_unlock(&pdx->io_mutex); // locked, so we will not get system calls
+ if (iReturn >= 0) // if we failed
+ {
+ iReturn = usb_reset_device(pdx->udev); // try to do the reset
+ usb_unlock_device(pdx->udev); // undo the lock
+ }
+
+ mutex_lock(&pdx->io_mutex); // hold stuff off while we wait
+ pdx->dwDMAFlag = MODE_CHAR; // Clear DMA mode flag regardless!
+ if (iReturn == 0) // if all is OK still
+ {
+ unsigned int state;
+ iReturn = InSelfTest(pdx, &state); // see if likely in self test
+ if (iReturn > 0) // do we need to wait for self-test?
+ {
+ unsigned long ulTimeOut = jiffies + 30*HZ; // when to give up
+ while((iReturn > 0) && time_before(jiffies, ulTimeOut))
+ {
+ schedule(); // let other stuff run
+ iReturn = InSelfTest(pdx, &state); // see if done yet
+ }
+ }
+
+ if (iReturn == 0) // if all is OK...
+ iReturn = state == 0; // then sucess is that the state is 0
+ }
+ else
+ iReturn = 0; // we failed
+ pdx->bForceReset = false; // Clear forced reset flag now
+
+ return iReturn > 0;
+}
+
+/****************************************************************************
+** QuickCheck - ALWAYS CALLED HOLDING THE io_mutex
+** This is used to test for a 1401. It will try to do a quick check if all is
+** OK, that is the 1401 was OK the last time it was asked, and there is no DMA
+** in progress, and if the bTestBuff flag is set, the character buffers must be
+** empty too. If the quick check shows that the state is still the same, then
+** all is OK.
+**
+** If any of the above conditions are not met, or if the state or type of the
+** 1401 has changed since the previous test, the full Is1401 test is done, but
+** only if bCanReset is also TRUE.
+**
+** The return value is TRUE if a useable 1401 is found, FALSE if not
+*/
+bool QuickCheck(DEVICE_EXTENSION* pdx, bool bTestBuff, bool bCanReset)
+{
+ bool bRet = false; // assume it will fail and we will reset
+ bool bShortTest;
+
+ bShortTest = ((pdx->dwDMAFlag == MODE_CHAR) && // no DMA running
+ (!pdx->bForceReset) && // Not had a real reset forced
+ (pdx->sCurrentState >= U14ERR_STD)); // No 1401 errors stored
+
+ dev_dbg(&pdx->interface->dev, "%s DMAFlag:%d, state:%d, force:%d, testBuff:%d, short:%d",
+ __func__, pdx->dwDMAFlag, pdx->sCurrentState, pdx->bForceReset, bTestBuff, bShortTest);
+
+ if ((bTestBuff) && // Buffer check requested, and...
+ (pdx->dwNumInput || pdx->dwNumOutput)) // ...characters were in the buffer?
+ {
+ bShortTest = false; // Then do the full test
+ dev_dbg(&pdx->interface->dev, "%s will reset as buffers not empty", __func__);
+ }
+
+ if (bShortTest || !bCanReset) // Still OK to try the short test?
+ { // Always test if no reset - we want state update
+ unsigned int state, error;
+ dev_dbg(&pdx->interface->dev, "%s->Get1401State", __func__);
+ if (Get1401State(pdx, &state, &error) == U14ERR_NOERROR) // Check on the 1401 state
+ {
+ if ((state & 0xFF) == 0) // If call worked, check the status value
+ bRet = true; // If that was zero, all is OK, no reset needed
+ }
+ }
+
+ if (!bRet && bCanReset) // If all not OK, then
+ {
+ dev_info(&pdx->interface->dev, "%s->Is1401 %d %d %d %d",
+ __func__, bShortTest, pdx->sCurrentState, bTestBuff, pdx->bForceReset);
+ bRet = Is1401(pdx); // do full test
+ }
+
+ return bRet;
+}
+
+/****************************************************************************
+** Reset1401
+**
+** Resets the 1401 and empties the i/o buffers
+*****************************************************************************/
+int Reset1401(DEVICE_EXTENSION *pdx)
+{
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ dev_dbg(&pdx->interface->dev,"ABout to call QuickCheck");
+ QuickCheck(pdx, true, true); // Check 1401, reset if not OK
+ mutex_unlock(&pdx->io_mutex);
+ return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** GetChar
+**
+** Gets a single character from the 1401
+****************************************************************************/
+int GetChar(DEVICE_EXTENSION *pdx)
+{
+ int iReturn = U14ERR_NOIN; // assume we will get nothing
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+
+ dev_dbg(&pdx->interface->dev, "GetChar");
+
+ Allowi(pdx, false); // Make sure char reads are running
+ SendChars(pdx); // and send any buffered chars
+
+ spin_lock_irq(&pdx->charInLock);
+ if (pdx->dwNumInput > 0) // worth looking
+ {
+ iReturn = pdx->inputBuffer[pdx->dwInBuffGet++];
+ if (pdx->dwInBuffGet >= INBUF_SZ)
+ pdx->dwInBuffGet = 0;
+ pdx->dwNumInput--;
+ }
+ else
+ iReturn = U14ERR_NOIN; // no input data to read
+ spin_unlock_irq(&pdx->charInLock);
+
+ Allowi(pdx, false); // Make sure char reads are running
+
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+ return iReturn;
+}
+
+/****************************************************************************
+** GetString
+**
+** Gets a string from the 1401. Returns chars up to the next CR or when
+** there are no more to read or nowhere to put them. CR is translated to
+** 0 and counted as a character. If the string does not end in a 0, we will
+** add one, if there is room, but it is not counted as a character.
+**
+** returns the count of characters (including the terminator, or 0 if none
+** or a negative error code.
+****************************************************************************/
+int GetString(DEVICE_EXTENSION *pdx, char __user* pUser, int n)
+{
+ int nAvailable; // character in the buffer
+ int iReturn = U14ERR_NOIN;
+ if (n <= 0)
+ return -ENOMEM;
+
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ Allowi(pdx, false); // Make sure char reads are running
+ SendChars(pdx); // and send any buffered chars
+
+ spin_lock_irq(&pdx->charInLock);
+ nAvailable = pdx->dwNumInput; // characters available now
+ if (nAvailable > n) // read max of space in pUser...
+ nAvailable = n; // ...or input characters
+
+ if (nAvailable > 0) // worth looking?
+ {
+ char buffer[INBUF_SZ+1]; // space for a linear copy of data
+ int nGot = 0;
+ int nCopyToUser; // number to copy to user
+ char cData;
+ do
+ {
+ cData = pdx->inputBuffer[pdx->dwInBuffGet++];
+ if (cData == CR_CHAR) // replace CR with zero
+ cData = (char)0;
+
+ if (pdx->dwInBuffGet >= INBUF_SZ)
+ pdx->dwInBuffGet = 0; // wrap buffer pointer
+
+ buffer[nGot++] = cData; // save the output
+ }
+ while((nGot < nAvailable) && cData);
+
+ nCopyToUser = nGot; // what to copy...
+ if (cData) // do we need null
+ {
+ buffer[nGot] = (char)0; // make it tidy
+ if (nGot < n) // if space in user buffer...
+ ++nCopyToUser; // ...copy the 0 as well.
+ }
+
+ pdx->dwNumInput -= nGot;
+ spin_unlock_irq(&pdx->charInLock);
+
+ dev_dbg(&pdx->interface->dev,"GetString read %d characters >%s<", nGot, buffer);
+ copy_to_user(pUser, buffer, nCopyToUser);
+
+ iReturn = nGot; // report characters read
+ }
+ else
+ spin_unlock_irq(&pdx->charInLock);
+
+ Allowi(pdx, false); // Make sure char reads are running
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+
+ return iReturn;
+}
+
+/*******************************************************************************
+** Get count of characters in the inout buffer.
+*******************************************************************************/
+int Stat1401(DEVICE_EXTENSION *pdx)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ Allowi(pdx, false); // make sure we allow pending chars
+ SendChars(pdx); // in both directions
+ iReturn = pdx->dwNumInput; // no lock as single read
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+ return iReturn;
+}
+
+/****************************************************************************
+** LineCount
+**
+** Returns the number of newline chars in the buffer. There is no need for
+** any fancy interlocks as we only read the interrupt routine data, and the
+** system is arranged so nothing can be destroyed.
+****************************************************************************/
+int LineCount(DEVICE_EXTENSION *pdx)
+{
+ int iReturn = 0; // will be count of line ends
+
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ Allowi(pdx, false); // Make sure char reads are running
+ SendChars(pdx); // and send any buffered chars
+ spin_lock_irq(&pdx->charInLock); // Get protection
+
+ if (pdx->dwNumInput > 0) // worth looking?
+ {
+ unsigned int dwIndex = pdx->dwInBuffGet;// start at first available
+ unsigned int dwEnd = pdx->dwInBuffPut; // Position for search end
+ do
+ {
+ if (pdx->inputBuffer[dwIndex++] == CR_CHAR)
+ ++iReturn; // inc count if CR
+
+ if (dwIndex >= INBUF_SZ) // see if we fall off buff
+ dwIndex = 0;
+ }
+ while (dwIndex != dwEnd); // go to last avaliable
+ }
+
+ spin_unlock_irq(&pdx->charInLock);
+ dev_dbg(&pdx->interface->dev,"LineCount returned %d", iReturn);
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+ return iReturn;
+}
+
+/****************************************************************************
+** GetOutBufSpace
+**
+** Gets the space in the output buffer. Called from user code.
+*****************************************************************************/
+int GetOutBufSpace(DEVICE_EXTENSION *pdx)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ SendChars(pdx); // send any buffered chars
+ iReturn = (int)(OUTBUF_SZ - pdx->dwNumOutput); // no lock needed for single read
+ dev_dbg(&pdx->interface->dev,"OutBufSpace %d", iReturn);
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+ return iReturn;
+}
+
+/****************************************************************************
+**
+** ClearArea
+**
+** Clears up a transfer area. This is always called in the context of a user
+** request, never from a call-back.
+****************************************************************************/
+int ClearArea(DEVICE_EXTENSION *pdx, int nArea)
+{
+ int iReturn = U14ERR_NOERROR;
+
+ if ((nArea < 0) || (nArea >= MAX_TRANSAREAS))
+ {
+ iReturn = U14ERR_BADAREA;
+ dev_err(&pdx->interface->dev, "%s Attempt to clear area %d", __func__, nArea);
+ }
+ else
+ {
+ TRANSAREA *pTA = &pdx->rTransDef[nArea]; // to save typing
+ if (!pTA->bUsed) // if not used...
+ iReturn = U14ERR_NOTSET; // ...nothing to be done
+ else
+ {
+ // We must save the memory we return as we shouldn't mess with memory while
+ // holding a spin lock.
+ struct page **pPages = 0; // save page address list
+ int nPages = 0; // and number of pages
+ int np;
+
+ dev_dbg(&pdx->interface->dev, "%s area %d", __func__, nArea);
+ spin_lock_irq(&pdx->stagedLock);
+ if ((pdx->StagedId == nArea) && (pdx->dwDMAFlag > MODE_CHAR))
+ {
+ iReturn = U14ERR_UNLOCKFAIL; // cannot delete as in use
+ dev_err(&pdx->interface->dev, "%s call on area %d while active", __func__, nArea);
+ }
+ else
+ {
+ pPages = pTA->pPages; // save page address list
+ nPages = pTA->nPages; // and page count
+ if (pTA->dwEventSz) // if events flagging in use
+ wake_up_interruptible(&pTA->wqEvent); // release anything that was waiting
+
+ if (pdx->bXFerWaiting && (pdx->rDMAInfo.wIdent == nArea))
+ pdx->bXFerWaiting = false; // Cannot have pending xfer if area cleared
+
+ // Clean out the TRANSAREA except for the wait queue, which is at the end
+ // This sets bUsed to false and dwEventSz to 0 to say area not used and no events.
+ memset(pTA, 0, sizeof(TRANSAREA)-sizeof(wait_queue_head_t));
+ }
+ spin_unlock_irq(&pdx->stagedLock);
+
+ if (pPages) // if we decided to release the memory
+ {
+ // Now we must undo the pinning down of the pages. We will assume the worst and mark
+ // all the pages as dirty. Don't be tempted to move this up above as you must not be
+ // holding a spin lock to do this stuff as it is not atomic.
+ dev_dbg(&pdx->interface->dev, "%s nPages=%d", __func__, nPages);
+
+ for (np = 0; np < nPages; ++np)
+ {
+ if (pPages[np])
+ {
+ SetPageDirty(pPages[np]);
+ page_cache_release(pPages[np]);
+ }
+ }
+
+ kfree(pPages);
+ dev_dbg(&pdx->interface->dev, "%s kfree(pPages) done", __func__);
+ }
+ }
+ }
+
+ return iReturn;
+}
+
+/****************************************************************************
+** SetArea
+**
+** Sets up a transfer area - the functional part. Called by both
+** SetTransfer and SetCircular.
+****************************************************************************/
+static int SetArea(DEVICE_EXTENSION *pdx, int nArea, char __user* puBuf,
+ unsigned int dwLength, bool bCircular, bool bCircToHost)
+{
+ // Start by working out the page aligned start of the area and the size
+ // of the area in pages, allowing for the start not being aligned and the
+ // end needing to be rounded up to a page boundary.
+ unsigned long ulStart = ((unsigned long)puBuf) & PAGE_MASK;
+ unsigned int ulOffset = ((unsigned long)puBuf) & (PAGE_SIZE-1);
+ int len = (dwLength + ulOffset+PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ TRANSAREA *pTA = &pdx->rTransDef[nArea]; // to save typing
+ struct page **pPages = 0; // space for page tables
+ int nPages = 0; // and number of pages
+
+ int iReturn = ClearArea(pdx, nArea); // see if OK to use this area
+ if ((iReturn != U14ERR_NOTSET) && // if not area unused and...
+ (iReturn != U14ERR_NOERROR)) // ...not all OK, then...
+ return iReturn; // ...we cannot use this area
+
+ if (!access_ok(VERIFY_WRITE, puBuf, dwLength)) // if we cannot access the memory...
+ return -EFAULT; // ...then we are done
+
+ // Now allocate space to hold the page pointer and virtual address pointer tables
+ pPages = (struct page **)kmalloc(len*sizeof(struct page *), GFP_KERNEL);
+ if (!pPages)
+ {
+ iReturn = U14ERR_NOMEMORY;
+ goto error;
+ }
+ dev_dbg(&pdx->interface->dev, "%s %p, length=%06x, circular %d", __func__, puBuf, dwLength, bCircular);
+
+ // To pin down user pages we must first acquire the mapping semaphore.
+ down_read(¤t->mm->mmap_sem); // get memory map semaphore
+ nPages = get_user_pages(current, current->mm, ulStart, len, 1, 0, pPages, 0);
+ up_read(¤t->mm->mmap_sem); // release the semaphore
+ dev_dbg(&pdx->interface->dev, "%s nPages = %d", __func__, nPages);
+
+ if (nPages > 0) // if we succeeded
+ {
+ // If you are tempted to use page_address (form LDD3), forget it. You MUST use
+ // kmap() or kmap_atomic() to get a virtual address. page_address will give you
+ // (null) or at least it does in this context with an x86 machine.
+ spin_lock_irq(&pdx->stagedLock);
+ pTA->lpvBuff = puBuf; // keep start of region (user address)
+ pTA->dwBaseOffset = ulOffset; // save offset in first page to start of xfer
+ pTA->dwLength = dwLength; // Size if the region in bytes
+ pTA->pPages = pPages; // list of pages that are used by buffer
+ pTA->nPages = nPages; // number of pages
+
+ pTA->bCircular = bCircular;
+ pTA->bCircToHost = bCircToHost;
+
+ pTA->aBlocks[0].dwOffset = 0;
+ pTA->aBlocks[0].dwSize = 0;
+ pTA->aBlocks[1].dwOffset = 0;
+ pTA->aBlocks[1].dwSize = 0;
+ pTA->bUsed = true; // This is now a used block
+
+ spin_unlock_irq(&pdx->stagedLock);
+ iReturn = U14ERR_NOERROR; // say all was well
+ }
+ else
+ {
+ iReturn = U14ERR_LOCKFAIL;
+ goto error;
+ }
+
+ return iReturn;
+
+error:
+ kfree(pPages);
+ return iReturn;
+}
+
+/****************************************************************************
+** SetTransfer
+**
+** Sets up a transfer area record. If the area is already set, we attempt to
+** unset it. Unsetting will fail if the area is booked, and a transfer to that
+** area is in progress. Otherwise, we will release the area and re-assign it.
+****************************************************************************/
+int SetTransfer(DEVICE_EXTENSION *pdx, TRANSFERDESC __user *pTD)
+{
+ int iReturn;
+ TRANSFERDESC td;
+ copy_from_user(&td, pTD, sizeof(td));
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev,"%s area:%d, size:%08x", __func__, td.wAreaNum, td.dwLength);
+ // The strange cast is done so that we don't get warnings in 32-bit linux about the size of the
+ // pointer. The pointer is always passed as a 64-bit object so that we don't have problems using
+ // a 32-bit program on a 64-bit system. unsigned long is 64-bits on a 64-bit system.
+ iReturn = SetArea(pdx, td.wAreaNum, (char __user *)((unsigned long)td.lpvBuff), td.dwLength, false, false);
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/****************************************************************************
+** UnSetTransfer
+** Erases a transfer area record
+****************************************************************************/
+int UnsetTransfer(DEVICE_EXTENSION *pdx, int nArea)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex);
+ iReturn = ClearArea(pdx, nArea);
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/****************************************************************************
+** SetEvent
+** Creates an event that we can test for based on a transfer to/from an area.
+** The area must be setup for a transfer. We attempt to simulate the Windows
+** driver behavior for events (as we don't actually use them), which is to
+** pretend that whatever the user asked for was achieved, so we return 1 if
+** try to create one, and 0 if they ask to remove (assuming all else was OK).
+****************************************************************************/
+int SetEvent(DEVICE_EXTENSION *pdx, TRANSFEREVENT __user*pTE)
+{
+ int iReturn = U14ERR_NOERROR;
+ TRANSFEREVENT te;
+ copy_from_user(&te, pTE, sizeof(te)); // get a local copy of the data
+ if (te.wAreaNum >= MAX_TRANSAREAS) // the area must exist
+ return U14ERR_BADAREA;
+ else
+ {
+ TRANSAREA *pTA = &pdx->rTransDef[te.wAreaNum];
+ mutex_lock(&pdx->io_mutex); // make sure we have no competitor
+ spin_lock_irq(&pdx->stagedLock);
+ if (pTA->bUsed) // area must be in use
+ {
+ pTA->dwEventSt = te.dwStart; // set area regions
+ pTA->dwEventSz = te.dwLength; // set size (0 cancels it)
+ pTA->bEventToHost = te.wFlags & 1; // set the direction
+ pTA->iWakeUp = 0; // zero the wake up count
+ }
+ else
+ iReturn = U14ERR_NOTSET;
+ spin_unlock_irq(&pdx->stagedLock);
+ mutex_unlock(&pdx->io_mutex);
+ }
+ return iReturn == U14ERR_NOERROR ? (te.iSetEvent ? 1 : U14ERR_NOERROR) : iReturn;
+}
+
+/****************************************************************************
+** WaitEvent
+** Sleep the process with a timeout waiting for an event. Returns the number
+** of times that a block met the event condition since we last cleared it or
+** 0 if timed out, or -ve error (bad area or not set, or signal).
+****************************************************************************/
+int WaitEvent(DEVICE_EXTENSION *pdx, int nArea, int msTimeOut)
+{
+ int iReturn;
+ if ((unsigned)nArea > MAX_TRANSAREAS)
+ return U14ERR_BADAREA;
+ else
+ {
+ int iWait;
+ TRANSAREA *pTA = &pdx->rTransDef[nArea];
+ msTimeOut = (msTimeOut * HZ + 999)/1000; // convert timeout to jiffies
+
+ // We cannot wait holding the mutex, but we check the flags while holding
+ // it. This may well be pointless as another thread could get in between
+ // releasing it and the wait call. However, this would have to clear the
+ // iWakeUp flag. However, the !pTA-bUsed may help us in this case.
+ mutex_lock(&pdx->io_mutex); // make sure we have no competitor
+ if (!pTA->bUsed || !pTA->dwEventSz) // check something to wait for...
+ return U14ERR_NOTSET; // ...else we do nothing
+ mutex_unlock(&pdx->io_mutex);
+
+ if (msTimeOut)
+ iWait = wait_event_interruptible_timeout(pTA->wqEvent, pTA->iWakeUp || !pTA->bUsed, msTimeOut);
+ else
+ iWait = wait_event_interruptible(pTA->wqEvent, pTA->iWakeUp || !pTA->bUsed);
+ if (iWait)
+ iReturn = -ERESTARTSYS; // oops - we have had a SIGNAL
+ else
+ iReturn = pTA->iWakeUp; // else the wakeup count
+
+ spin_lock_irq(&pdx->stagedLock);
+ pTA->iWakeUp = 0; // clear the flag
+ spin_unlock_irq(&pdx->stagedLock);
+ }
+ return iReturn;
+}
+
+/****************************************************************************
+** TestEvent
+** Test the event to see if a WaitEvent would return immediately. Returns the
+** number of times a block completed since the last call, or 0 if none or a
+** negative error.
+****************************************************************************/
+int TestEvent(DEVICE_EXTENSION *pdx, int nArea)
+{
+ int iReturn;
+ if ((unsigned)nArea > MAX_TRANSAREAS)
+ iReturn = U14ERR_BADAREA;
+ else
+ {
+ TRANSAREA *pTA = &pdx->rTransDef[nArea];
+ mutex_lock(&pdx->io_mutex); // make sure we have no competitor
+ spin_lock_irq(&pdx->stagedLock);
+ iReturn = pTA->iWakeUp; // get wakeup count since last call
+ pTA->iWakeUp = 0; // clear the count
+ spin_unlock_irq(&pdx->stagedLock);
+ mutex_unlock(&pdx->io_mutex);
+ }
+ return iReturn;
+}
+
+/****************************************************************************
+** GetTransferInfo
+** Puts the current state of the 1401 in a TGET_TX_BLOCK.
+*****************************************************************************/
+int GetTransfer(DEVICE_EXTENSION *pdx, TGET_TX_BLOCK __user *pTX)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned int dwIdent;
+
+ mutex_lock(&pdx->io_mutex);
+ dwIdent = pdx->StagedId; // area ident for last xfer
+ if (dwIdent >= MAX_TRANSAREAS)
+ iReturn = U14ERR_BADAREA;
+ else
+ {
+ // Return the best information we have - we don't have physical addresses
+ TGET_TX_BLOCK tx;
+ memset(&tx, 0, sizeof(tx)); // clean out local work structure
+ tx.size = pdx->rTransDef[dwIdent].dwLength;
+ tx.linear = (long long)((long)pdx->rTransDef[dwIdent].lpvBuff);
+ tx.avail = GET_TX_MAXENTRIES; // how many blocks we could return
+ tx.used = 1; // number we actually return
+ tx.entries[0].physical = (long long)(tx.linear+pdx->StagedOffset);
+ tx.entries[0].size = tx.size;
+ copy_to_user(pTX, &tx, sizeof(tx));
+ }
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/****************************************************************************
+** KillIO1401
+**
+** Empties the host i/o buffers
+****************************************************************************/
+int KillIO1401(DEVICE_EXTENSION *pdx)
+{
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ mutex_lock(&pdx->io_mutex);
+ FlushOutBuff(pdx);
+ FlushInBuff(pdx);
+ mutex_unlock(&pdx->io_mutex);
+ return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** BlkTransState
+** Returns a 0 or a 1 for whether DMA is happening. No point holding a mutex
+** for this as it only does one read.
+*****************************************************************************/
+int BlkTransState(DEVICE_EXTENSION *pdx)
+{
+ int iReturn = pdx->dwDMAFlag != MODE_CHAR;
+ dev_dbg(&pdx->interface->dev, "%s = %d", __func__, iReturn);
+ return iReturn;
+}
+
+/****************************************************************************
+** StateOf1401
+**
+** Puts the current state of the 1401 in the Irp return buffer.
+*****************************************************************************/
+int StateOf1401(DEVICE_EXTENSION *pdx)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex);
+
+ QuickCheck(pdx, false, false); // get state up to date, no reset
+ iReturn = pdx->sCurrentState;
+
+ mutex_unlock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s = %d", __func__, iReturn);
+
+ return iReturn;
+}
+/****************************************************************************
+** StartSelfTest
+**
+** Initiates a self-test cycle. The assumption is that we have no interrupts
+** active, so we should make sure that this is the case.
+*****************************************************************************/
+int StartSelfTest(DEVICE_EXTENSION *pdx)
+{
+ int nGot;
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ ced_draw_down(pdx); // wait for, then kill outstanding Urbs
+ FlushInBuff(pdx); // Clear out input buffer & pipe
+ FlushOutBuff(pdx); // Clear output buffer & pipe
+// ReadWrite_Cancel(pDeviceObject); /* so things stay tidy */
+ pdx->dwDMAFlag = MODE_CHAR; /* Clear DMA mode flags here */
+
+ nGot = usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0),
+ DB_SELFTEST, (H_TO_D|VENDOR|DEVREQ), 0, 0,
+ 0, 0, HZ); // allow 1 second timeout
+ pdx->ulSelfTestTime = jiffies + HZ*30; // 30 seconds into the future
+
+ mutex_unlock(&pdx->io_mutex);
+ if (nGot < 0)
+ dev_err(&pdx->interface->dev, "%s err=%d", __func__, nGot);
+ return nGot < 0 ? U14ERR_FAIL : U14ERR_NOERROR;
+}
+
+
+/****************************************************************************
+** CheckSelfTest
+**
+** Check progress of a self-test cycle
+****************************************************************************/
+int CheckSelfTest(DEVICE_EXTENSION *pdx, TGET_SELFTEST __user *pGST)
+{
+ unsigned int state, error;
+ int iReturn;
+ TGET_SELFTEST gst; // local work space
+ memset(&gst, 0, sizeof(gst)); // clear out the space (sets code 0)
+
+ mutex_lock(&pdx->io_mutex);
+
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ iReturn = Get1401State(pdx, &state, &error);
+ if (iReturn == U14ERR_NOERROR) // Only accept zero if it happens twice
+ iReturn = Get1401State(pdx, &state, &error);
+
+ if (iReturn != U14ERR_NOERROR) // Self-test can cause comms errors
+ { // so we assume still testing
+ dev_err(&pdx->interface->dev, "%s Get1401State=%d, assuming still testing", __func__, iReturn);
+ state = 0x80; // Force still-testing, no error
+ error = 0;
+ iReturn = U14ERR_NOERROR;
+ }
+
+ if ((state == -1) && (error == -1)) // If Get1401State had problems
+ {
+ dev_err(&pdx->interface->dev, "%s Get1401State failed, assuming still testing", __func__);
+ state = 0x80; // Force still-testing, no error
+ error = 0;
+ }
+
+ if ((state & 0xFF) == 0x80) // If we are still in self-test
+ {
+ if (state & 0x00FF0000) // Have we got an error?
+ {
+ gst.code = (state & 0x00FF0000) >> 16; // read the error code
+ gst.x = error & 0x0000FFFF; // Error data X
+ gst.y = (error & 0xFFFF0000) >> 16; // and data Y
+ dev_dbg(&pdx->interface->dev,"Self-test error code %d", gst.code);
+ }
+ else // No error, check for timeout
+ {
+ unsigned long ulNow = jiffies; // get current time
+ if (time_after(ulNow, pdx->ulSelfTestTime))
+ {
+ gst.code = -2; // Flag the timeout
+ dev_dbg(&pdx->interface->dev, "Self-test timed-out");
+ }
+ else
+ dev_dbg(&pdx->interface->dev, "Self-test on-going");
+ }
+ }
+ else
+ {
+ gst.code = -1; // Flag the test is done
+ dev_dbg(&pdx->interface->dev, "Self-test done");
+ }
+
+ if (gst.code < 0) // If we have a problem or finished
+ { // If using the 2890 we should reset properly
+ if ((pdx->nPipes == 4) && (pdx->s1401Type <= TYPEPOWER))
+ Is1401(pdx); // Get 1401 reset and OK
+ else
+ QuickCheck(pdx, true, true); // Otherwise check without reset unless problems
+ }
+ mutex_unlock(&pdx->io_mutex);
+
+ copy_to_user(pGST, &gst, sizeof(gst)); // copy result to user space
+ return iReturn;
+}
+
+/****************************************************************************
+** TypeOf1401
+**
+** Returns code for standard, plus, micro1401, power1401 or none
+****************************************************************************/
+int TypeOf1401(DEVICE_EXTENSION *pdx)
+{
+ int iReturn = TYPEUNKNOWN;
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ switch (pdx->s1401Type)
+ {
+ case TYPE1401: iReturn = U14ERR_STD; break; // Handle these types directly
+ case TYPEPLUS: iReturn = U14ERR_PLUS; break;
+ case TYPEU1401:iReturn = U14ERR_U1401;break;
+ default:
+ if ((pdx->s1401Type >= TYPEPOWER) &&
+ (pdx->s1401Type <= 25))
+ iReturn = pdx->s1401Type + 4; // We can calculate types
+ else // for up-coming 1401 designs
+ iReturn = TYPEUNKNOWN; // Don't know or not there
+ }
+ dev_dbg(&pdx->interface->dev, "%s %d", __func__, iReturn);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** TransferFlags
+**
+** Returns flags on block transfer abilities
+****************************************************************************/
+int TransferFlags(DEVICE_EXTENSION *pdx)
+{
+ int iReturn = U14TF_MULTIA | U14TF_DIAG | // we always have multiple DMA area
+ U14TF_NOTIFY | U14TF_CIRCTH; // diagnostics, notify and circular
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ mutex_lock(&pdx->io_mutex);
+ if (pdx->bIsUSB2) // Set flag for USB2 if appropriate
+ iReturn |= U14TF_USB2;
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/***************************************************************************
+** DbgCmd1401
+** Issues a debug\diagnostic command to the 1401 along with a 32-bit datum
+** This is a utility command used for dbg operations.
+*/
+static int DbgCmd1401(DEVICE_EXTENSION *pdx, unsigned char cmd, unsigned int data)
+{
+ int iReturn;
+ dev_dbg(&pdx->interface->dev, "%s entry", __func__);
+ iReturn = usb_control_msg(pdx->udev, usb_sndctrlpipe(pdx->udev, 0),
+ cmd, (H_TO_D|VENDOR|DEVREQ),
+ (unsigned short)data, (unsigned short)(data >> 16),
+ 0, 0, HZ); // allow 1 second timeout
+ if (iReturn < 0)
+ dev_err(&pdx->interface->dev, "%s fail code=%d", __func__, iReturn);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** DbgPeek
+**
+** Execute the diagnostic peek operation. Uses address, width and repeats.
+****************************************************************************/
+int DbgPeek(DEVICE_EXTENSION *pdx, TDBGBLOCK __user* pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+ copy_from_user(&db, pDB, sizeof(db)); // get the data
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s @ %08x", __func__, db.iAddr);
+
+ iReturn = DbgCmd1401(pdx, DB_SETADD, db.iAddr);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_PEEK, 0);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+
+/****************************************************************************
+** DbgPoke
+**
+** Execute the diagnostic poke operation. Parameters are in the CSBLOCK struct
+** in order address, size, repeats and value to poke.
+****************************************************************************/
+int DbgPoke(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+ copy_from_user(&db, pDB, sizeof(db)); // get the data
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s @ %08x", __func__, db.iAddr);
+
+ iReturn = DbgCmd1401(pdx, DB_SETADD, db.iAddr);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_POKE, db.iData);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+
+/****************************************************************************
+** DbgRampData
+**
+** Execute the diagnostic ramp data operation. Parameters are in the CSBLOCK struct
+** in order address, default, enable mask, size and repeats.
+****************************************************************************/
+int DbgRampData(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+ copy_from_user(&db, pDB, sizeof(db)); // get the data
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s @ %08x", __func__, db.iAddr);
+
+ iReturn = DbgCmd1401(pdx, DB_SETADD, db.iAddr);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_SETDEF, db.iDefault);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_SETMASK, db.iMask);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_RAMPD, 0);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+
+/****************************************************************************
+** DbgRampAddr
+**
+** Execute the diagnostic ramp address operation
+****************************************************************************/
+int DbgRampAddr(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+ copy_from_user(&db, pDB, sizeof(db)); // get the data
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ iReturn = DbgCmd1401(pdx, DB_SETDEF, db.iDefault);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_SETMASK, db.iMask);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_RAMPA, 0);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+
+/****************************************************************************
+** DbgGetData
+**
+** Retrieve the data resulting from the last debug Peek operation
+****************************************************************************/
+int DbgGetData(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+ memset(&db, 0, sizeof(db)); // fill returned block with 0s
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ // Read back the last peeked value from the 1401.
+ iReturn = usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0),
+ DB_DATA, (D_TO_H|VENDOR|DEVREQ), 0,0,
+ &db.iData, sizeof(db.iData), HZ);
+ if (iReturn == sizeof(db.iData))
+ {
+ copy_to_user(pDB, &db, sizeof(db));
+ iReturn = U14ERR_NOERROR;
+ }
+ else
+ dev_err(&pdx->interface->dev, "%s failed, code %d", __func__, iReturn);
+
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** DbgStopLoop
+**
+** Stop any never-ending debug loop, we just call Get1401State for USB
+**
+****************************************************************************/
+int DbgStopLoop(DEVICE_EXTENSION *pdx)
+{
+ int iReturn;
+ unsigned int uState, uErr;
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ iReturn = Get1401State(pdx, &uState, &uErr);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+
+/****************************************************************************
+** SetCircular
+**
+** Sets up a transfer area record for circular transfers. If the area is
+** already set, we attempt to unset it. Unsetting will fail if the area is
+** booked and a transfer to that area is in progress. Otherwise, we will
+** release the area and re-assign it.
+****************************************************************************/
+int SetCircular(DEVICE_EXTENSION *pdx, TRANSFERDESC __user *pTD)
+{
+ int iReturn;
+ bool bToHost;
+ TRANSFERDESC td;
+ copy_from_user(&td, pTD, sizeof(td));
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev,"%s area:%d, size:%08x", __func__, td.wAreaNum, td.dwLength);
+ bToHost = td.eSize != 0; // this is used as the tohost flag
+
+ // The strange cast is done so that we don't get warnings in 32-bit linux about the size of the
+ // pointer. The pointer is always passed as a 64-bit object so that we don't have problems using
+ // a 32-bit program on a 64-bit system. unsigned long is 64-bits on a 64-bit system.
+ iReturn = SetArea(pdx, td.wAreaNum, (char __user *)((unsigned long)td.lpvBuff), td.dwLength, true, bToHost);
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/****************************************************************************
+** GetCircBlock
+**
+** Return the next available block of circularly-transferred data.
+****************************************************************************/
+int GetCircBlock(DEVICE_EXTENSION *pdx, TCIRCBLOCK __user* pCB)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned int nArea;
+ TCIRCBLOCK cb;
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ copy_from_user(&cb, pCB, sizeof(cb));
+ mutex_lock(&pdx->io_mutex);
+
+ nArea = cb.nArea; // Retrieve parameters first
+ cb.dwOffset = 0; // set default result (nothing)
+ cb.dwSize = 0;
+
+ if (nArea < MAX_TRANSAREAS) // The area number must be OK
+ {
+ TRANSAREA* pArea = &pdx->rTransDef[nArea]; // Pointer to relevant info
+ spin_lock_irq(&pdx->stagedLock); // Lock others out
+
+ if ((pArea->bUsed) && (pArea->bCircular) && // Must be circular area
+ (pArea->bCircToHost)) // For now at least must be to host
+ {
+ if (pArea->aBlocks[0].dwSize > 0) // Got anything?
+ {
+ cb.dwOffset = pArea->aBlocks[0].dwOffset;
+ cb.dwSize = pArea->aBlocks[0].dwSize;
+ dev_dbg(&pdx->interface->dev, "%s return block 0: %d bytes at %d", __func__, cb.dwSize, cb.dwOffset);
+ }
+ }
+ else
+ iReturn = U14ERR_NOTSET;
+
+ spin_unlock_irq(&pdx->stagedLock);
+ }
+ else
+ iReturn = U14ERR_BADAREA;
+
+ copy_to_user(pCB, &cb, sizeof(cb));
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+
+/****************************************************************************
+** FreeCircBlock
+**
+** Frees a block of circularly-transferred data and returns the next one.
+****************************************************************************/
+int FreeCircBlock(DEVICE_EXTENSION *pdx, TCIRCBLOCK __user* pCB)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned int nArea, uStart, uSize;
+ TCIRCBLOCK cb;
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ copy_from_user(&cb, pCB, sizeof(cb));
+ mutex_lock(&pdx->io_mutex);
+
+ nArea = cb.nArea; // Retrieve parameters first
+ uStart = cb.dwOffset;
+ uSize = cb.dwSize;
+ cb.dwOffset = 0; // then set default result (nothing)
+ cb.dwSize = 0;
+
+ if (nArea < MAX_TRANSAREAS) // The area number must be OK
+ {
+ TRANSAREA* pArea = &pdx->rTransDef[nArea]; // Pointer to relevant info
+ spin_lock_irq(&pdx->stagedLock); // Lock others out
+
+ if ((pArea->bUsed) && (pArea->bCircular) && // Must be circular area
+ (pArea->bCircToHost)) // For now at least must be to host
+ {
+ bool bWaiting = false;
+
+ if ((pArea->aBlocks[0].dwSize >= uSize) && // Got anything?
+ (pArea->aBlocks[0].dwOffset == uStart)) // Must be legal data
+ {
+ pArea->aBlocks[0].dwSize -= uSize;
+ pArea->aBlocks[0].dwOffset += uSize;
+ if (pArea->aBlocks[0].dwSize == 0) // Have we emptied this block?
+ {
+ if (pArea->aBlocks[1].dwSize) // Is there a second block?
+ {
+ pArea->aBlocks[0] = pArea->aBlocks[1]; // Copy down block 2 data
+ pArea->aBlocks[1].dwSize = 0; // and mark the second block as unused
+ pArea->aBlocks[1].dwOffset = 0;
+ }
+ else
+ pArea->aBlocks[0].dwOffset = 0;
+ }
+
+ dev_dbg(&pdx->interface->dev, "%s free %d bytes at %d, return %d bytes at %d, wait=%d",
+ __func__, uSize, uStart, pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset, pdx->bXFerWaiting);
+
+ // Return the next available block of memory as well
+ if (pArea->aBlocks[0].dwSize > 0) // Got anything?
+ {
+ cb.dwOffset = pArea->aBlocks[0].dwOffset;
+ cb.dwSize = pArea->aBlocks[0].dwSize;
+ }
+
+ bWaiting = pdx->bXFerWaiting;
+ if (bWaiting && pdx->bStagedUrbPending)
+ {
+ dev_err(&pdx->interface->dev, "%s ERROR: waiting xfer and staged Urb pending!", __func__);
+ bWaiting = false;
+ }
+ }
+ else
+ {
+ dev_err(&pdx->interface->dev, "%s ERROR: freeing %d bytes at %d, block 0 is %d bytes at %d",
+ __func__, uSize, uStart, pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset);
+ iReturn = U14ERR_NOMEMORY;
+ }
+
+ // If we have one, kick off pending transfer
+ if (bWaiting) // Got a block xfer waiting?
+ {
+ int RWMStat = ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard,
+ pdx->rDMAInfo.wIdent, pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize);
+ if (RWMStat != U14ERR_NOERROR)
+ dev_err(&pdx->interface->dev, "%s rw setup failed %d", __func__, RWMStat);
+ }
+ }
+ else
+ iReturn = U14ERR_NOTSET;
+
+ spin_unlock_irq(&pdx->stagedLock);
+ }
+ else
+ iReturn = U14ERR_BADAREA;
+
+ copy_to_user(pCB, &cb, sizeof(cb));
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+
+
--- /dev/null
+/* ced_ioctl.h
+ IOCTL calls for the CED1401 driver
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ 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.
+*/
+#ifndef __CED_IOCTL_H__
+#define __CED_IOCTL_H__
+#include <asm/ioctl.h>
+
+/// dma modes, only MODE_CHAR and MODE_LINEAR are used in this driver
+#define MODE_CHAR 0
+#define MODE_LINEAR 1
+
+/****************************************************************************
+** TypeDefs
+*****************************************************************************/
+
+typedef unsigned short TBLOCKENTRY; // index the blk transfer table 0-7
+
+typedef struct TransferDesc
+{
+ long long lpvBuff; // address of transfer area (for 64 or 32 bit)
+ unsigned int dwLength; // length of the area
+ TBLOCKENTRY wAreaNum; // number of transfer area to set up
+ short eSize; // element size - is tohost flag for circular
+} TRANSFERDESC;
+
+typedef TRANSFERDESC* LPTRANSFERDESC;
+
+typedef struct TransferEvent
+{
+ unsigned int dwStart; // offset into the area
+ unsigned int dwLength; // length of the region
+ unsigned short wAreaNum; // the area number
+ unsigned short wFlags; // bit 0 set for toHost
+ int iSetEvent; // could be dummy in LINUX
+} TRANSFEREVENT;
+
+#define MAX_TRANSFER_SIZE 0x4000 /* Maximum data bytes per IRP */
+#define MAX_AREA_LENGTH 0x100000 /* Maximum size of transfer area */
+#define MAX_TRANSAREAS 8 /* definitions for dma set up */
+
+typedef struct TGetSelfTest
+{
+ int code; // self-test error code
+ int x,y; // additional information
+} TGET_SELFTEST;
+
+/// Debug block used for several commands. Not all fields are used for all commands.
+typedef struct TDbgBlock
+{
+ int iAddr; // the address in the 1401
+ int iRepeats; // number of repeats
+ int iWidth; // width in bytes 1, 2, 4
+ int iDefault; // default value
+ int iMask; // mask to apply
+ int iData; // data for poke, result for peek
+} TDBGBLOCK;
+
+/// Used to collect information about a circular block from the device driver
+typedef struct TCircBlock
+{
+ unsigned int nArea; // the area to collect information from
+ unsigned int dwOffset; // offset into the area to the available block
+ unsigned int dwSize; // size of the area
+} TCIRCBLOCK;
+
+/// Used to clollect the 1401 status
+typedef struct TCSBlock
+{
+ unsigned int uiState;
+ unsigned int uiError;
+} TCSBLOCK;
+
+// As seen by the user, an ioctl call looks like:
+// int ioctl(int fd, unsigned long cmd, char* argp);
+// We will then have all sorts of variants on this that can be used
+// to pass stuff to our driver. We will generate macros for each type
+// of call so as to provide some sort of type safety in the calling:
+#define CED_MAGIC_IOC 0xce
+
+// NBNB: READ and WRITE are from the point of view of the device, not user.
+typedef struct ced_ioc_string
+{
+ int nChars;
+ char buffer[256];
+} CED_IOC_STRING;
+
+#define IOCTL_CED_SENDSTRING(n) _IOC(_IOC_WRITE, CED_MAGIC_IOC, 2, n)
+
+#define IOCTL_CED_RESET1401 _IO(CED_MAGIC_IOC, 3)
+#define IOCTL_CED_GETCHAR _IO(CED_MAGIC_IOC, 4)
+#define IOCTL_CED_SENDCHAR _IO(CED_MAGIC_IOC, 5)
+#define IOCTL_CED_STAT1401 _IO(CED_MAGIC_IOC, 6)
+#define IOCTL_CED_LINECOUNT _IO(CED_MAGIC_IOC, 7)
+#define IOCTL_CED_GETSTRING(nMax) _IOC(_IOC_READ, CED_MAGIC_IOC, 8, nMax)
+
+#define IOCTL_CED_SETTRANSFER _IOW(CED_MAGIC_IOC, 11, TRANSFERDESC)
+#define IOCTL_CED_UNSETTRANSFER _IO(CED_MAGIC_IOC, 12)
+#define IOCTL_CED_SETEVENT _IOW(CED_MAGIC_IOC,13, TRANSFEREVENT)
+#define IOCTL_CED_GETOUTBUFSPACE _IO(CED_MAGIC_IOC, 14)
+#define IOCTL_CED_GETBASEADDRESS _IO(CED_MAGIC_IOC, 15)
+#define IOCTL_CED_GETDRIVERREVISION _IO(CED_MAGIC_IOC, 16)
+
+#define IOCTL_CED_GETTRANSFER _IOR(CED_MAGIC_IOC,17, TGET_TX_BLOCK)
+#define IOCTL_CED_KILLIO1401 _IO(CED_MAGIC_IOC,18)
+#define IOCTL_CED_BLKTRANSSTATE _IO(CED_MAGIC_IOC,19)
+
+#define IOCTL_CED_STATEOF1401 _IO(CED_MAGIC_IOC,23)
+#define IOCTL_CED_GRAB1401 _IO(CED_MAGIC_IOC,25)
+#define IOCTL_CED_FREE1401 _IO(CED_MAGIC_IOC,26)
+#define IOCTL_CED_STARTSELFTEST _IO(CED_MAGIC_IOC,31)
+#define IOCTL_CED_CHECKSELFTEST _IOR(CED_MAGIC_IOC,32, TGET_SELFTEST)
+#define IOCTL_CED_TYPEOF1401 _IO(CED_MAGIC_IOC,33)
+#define IOCTL_CED_TRANSFERFLAGS _IO(CED_MAGIC_IOC,34)
+
+#define IOCTL_CED_DBGPEEK _IOW(CED_MAGIC_IOC,35, TDBGBLOCK)
+#define IOCTL_CED_DBGPOKE _IOW(CED_MAGIC_IOC,36, TDBGBLOCK)
+#define IOCTL_CED_DBGRAMPDATA _IOW(CED_MAGIC_IOC,37, TDBGBLOCK)
+#define IOCTL_CED_DBGRAMPADDR _IOW(CED_MAGIC_IOC,38, TDBGBLOCK)
+#define IOCTL_CED_DBGGETDATA _IOR(CED_MAGIC_IOC,39, TDBGBLOCK)
+#define IOCTL_CED_DBGSTOPLOOP _IO(CED_MAGIC_IOC,40)
+#define IOCTL_CED_FULLRESET _IO(CED_MAGIC_IOC,41)
+#define IOCTL_CED_SETCIRCULAR _IOW(CED_MAGIC_IOC,42, TRANSFERDESC)
+#define IOCTL_CED_GETCIRCBLOCK _IOWR(CED_MAGIC_IOC,43, TCIRCBLOCK)
+#define IOCTL_CED_FREECIRCBLOCK _IOWR(CED_MAGIC_IOC,44, TCIRCBLOCK)
+#define IOCTL_CED_WAITEVENT _IO(CED_MAGIC_IOC, 45)
+#define IOCTL_CED_TESTEVENT _IO(CED_MAGIC_IOC, 46)
+
+#ifndef __KERNEL__
+// If nothing said about return value, it is a U14ERR_... error code (U14ERR_NOERROR for none)
+inline int CED_SendString(int fh, const char* szText, int n){return ioctl(fh, IOCTL_CED_SENDSTRING(n), szText);}
+
+inline int CED_Reset1401(int fh){return ioctl(fh, IOCTL_CED_RESET1401);}
+
+inline int CED_GetChar(int fh){return ioctl(fh, IOCTL_CED_GETCHAR);}
+// Return the singe character or a -ve error code.
+
+inline int CED_Stat1401(int fh){return ioctl(fh, IOCTL_CED_STAT1401);}
+// Return character count in input buffer
+
+inline int CED_SendChar(int fh, char c){return ioctl(fh, IOCTL_CED_SENDCHAR, c);}
+
+inline int CED_LineCount(int fh){return ioctl(fh, IOCTL_CED_LINECOUNT);}
+
+inline int CED_GetString(int fh, char* szText, int nMax){return ioctl(fh, IOCTL_CED_GETSTRING(nMax), szText);}
+// return the count of characters returned. If the string was terminated by CR or 0, then the 0 is part
+// of the count. Otherwise, we will add a zero if there is room, but it is not included in the count.
+// The return value is 0 if there was nothing to read.
+
+inline int CED_GetOutBufSpace(int fh){return ioctl(fh, IOCTL_CED_GETOUTBUFSPACE);}
+// returns space in the output buffer.
+
+inline int CED_GetBaseAddress(int fh){return ioctl(fh, IOCTL_CED_GETBASEADDRESS);}
+// This always returns -1 as not implemented.
+
+inline int CED_GetDriverRevision(int fh){return ioctl(fh, IOCTL_CED_GETDRIVERREVISION);}
+// returns the major revision <<16 | minor revision.
+
+inline int CED_SetTransfer(int fh, TRANSFERDESC* pTD){return ioctl(fh, IOCTL_CED_SETTRANSFER, pTD);}
+
+inline int CED_UnsetTransfer(int fh, int nArea){return ioctl(fh, IOCTL_CED_UNSETTRANSFER, nArea);}
+
+inline int CED_SetEvent(int fh, TRANSFEREVENT* pTE){return ioctl(fh, IOCTL_CED_SETEVENT, pTE);}
+
+inline int CED_GetTransfer(int fh, TGET_TX_BLOCK* pTX){return ioctl(fh, IOCTL_CED_GETTRANSFER, pTX);}
+
+inline int CED_KillIO1401(int fh){return ioctl(fh, IOCTL_CED_KILLIO1401);}
+
+inline int CED_BlkTransState(int fh){return ioctl(fh, IOCTL_CED_BLKTRANSSTATE);}
+// returns 0 if no active DMA, 1 if active
+
+inline int CED_StateOf1401(int fh){return ioctl(fh, IOCTL_CED_STATEOF1401);}
+
+inline int CED_Grab1401(int fh){return ioctl(fh, IOCTL_CED_GRAB1401);}
+inline int CED_Free1401(int fh){return ioctl(fh, IOCTL_CED_FREE1401);}
+
+inline int CED_StartSelfTest(int fh){return ioctl(fh, IOCTL_CED_STARTSELFTEST);}
+inline int CED_CheckSelfTest(int fh, TGET_SELFTEST* pGST){return ioctl(fh, IOCTL_CED_CHECKSELFTEST, pGST);}
+
+inline int CED_TypeOf1401(int fh){return ioctl(fh, IOCTL_CED_TYPEOF1401);}
+inline int CED_TransferFlags(int fh){return ioctl(fh, IOCTL_CED_TRANSFERFLAGS);}
+
+inline int CED_DbgPeek(int fh, TDBGBLOCK* pDB){return ioctl(fh, IOCTL_CED_DBGPEEK, pDB);}
+inline int CED_DbgPoke(int fh, TDBGBLOCK* pDB){return ioctl(fh, IOCTL_CED_DBGPOKE, pDB);}
+inline int CED_DbgRampData(int fh, TDBGBLOCK* pDB){return ioctl(fh, IOCTL_CED_DBGRAMPDATA, pDB);}
+inline int CED_DbgRampAddr(int fh, TDBGBLOCK* pDB){return ioctl(fh, IOCTL_CED_DBGRAMPADDR, pDB);}
+inline int CED_DbgGetData(int fh, TDBGBLOCK* pDB){return ioctl(fh, IOCTL_CED_DBGGETDATA, pDB);}
+inline int CED_DbgStopLoop(int fh){return ioctl(fh, IOCTL_CED_DBGSTOPLOOP);}
+
+inline int CED_FullReset(int fh){return ioctl(fh, IOCTL_CED_FULLRESET);}
+
+inline int CED_SetCircular(int fh, TRANSFERDESC* pTD){return ioctl(fh, IOCTL_CED_SETCIRCULAR, pTD);}
+inline int CED_GetCircBlock(int fh, TCIRCBLOCK* pCB){return ioctl(fh, IOCTL_CED_GETCIRCBLOCK, pCB);}
+inline int CED_FreeCircBlock(int fh, TCIRCBLOCK* pCB){return ioctl(fh, IOCTL_CED_FREECIRCBLOCK, pCB);}
+
+inline int CED_WaitEvent(int fh, int nArea, int msTimeOut){return ioctl(fh, IOCTL_CED_WAITEVENT, (nArea & 0xff)|(msTimeOut << 8));}
+inline int CED_TestEvent(int fh, int nArea){return ioctl(fh, IOCTL_CED_TESTEVENT, nArea);}
+#endif
+
+#ifdef NOTWANTEDYET
+#define IOCTL_CED_REGCALLBACK _IO(CED_MAGIC_IOC,9) // Not used
+#define IOCTL_CED_GETMONITORBUF _IO(CED_MAGIC_IOC,10) // Not used
+
+#define IOCTL_CED_BYTECOUNT _IO(CED_MAGIC_IOC,20) // Not used
+#define IOCTL_CED_ZEROBLOCKCOUNT _IO(CED_MAGIC_IOC,21) // Not used
+#define IOCTL_CED_STOPCIRCULAR _IO(CED_MAGIC_IOC,22) // Not used
+
+#define IOCTL_CED_REGISTERS1401 _IO(CED_MAGIC_IOC,24) // Not used
+#define IOCTL_CED_STEP1401 _IO(CED_MAGIC_IOC,27) // Not used
+#define IOCTL_CED_SET1401REGISTERS _IO(CED_MAGIC_IOC,28) // Not used
+#define IOCTL_CED_STEPTILL1401 _IO(CED_MAGIC_IOC,29) // Not used
+#define IOCTL_CED_SETORIN _IO(CED_MAGIC_IOC,30) // Not used
+
+#endif
+
+// __CED_IOCTL_H__
+#endif
--- /dev/null
+/*****************************************************************************
+**
+** machine.h
+**
+** Copyright (c) Cambridge Electronic Design Limited 1991,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
+**
+** This file is included at the start of 'C' or 'C++' source file to define
+** things for cross-platform/compiler interoperability. This used to deal with
+** MSDOS/16-bit stuff, but this was all removed in Decemeber 2010. There are
+** three things to consider: Windows, LINUX, mac OSX (BSD Unix) and 32 vs 64
+** bit. At the time of writing (DEC 2010) there is a consensus on the following
+** and their unsigned equivalents:
+**
+** type bits
+** char 8
+** short 16
+** int 32
+** long long 64
+**
+** long is a problem as it is always 64 bits on linux/unix and is always 32 bits
+** on windows.
+** On windows, we define _IS_WINDOWS_ and one of WIN32 or WIN64.
+** On linux we define LINUX
+** On Max OSX we define MACOSX
+**
+*/
+
+#ifndef __MACHINE_H__
+#define __MACHINE_H__
+#ifndef __KERNEL__
+#include <float.h>
+#include <limits.h>
+#endif
+
+/*
+** The initial section is to identify the operating system
+*/
+#if (defined(__linux__) || defined(_linux) || defined(__linux)) && !defined(LINUX)
+#define LINUX 1
+#endif
+
+#if (defined(__WIN32__) || defined(_WIN32)) && !defined(WIN32)
+#define WIN32 1
+#endif
+
+#if defined(__APPLE__)
+#define MACOSX
+#endif
+
+#if defined(_WIN64)
+#undef WIN32
+#undef WIN64
+#define WIN64 1
+#endif
+
+#if defined(WIN32) || defined(WIN64)
+#define _IS_WINDOWS_ 1
+#endif
+
+#if defined(LINUX) || defined(MAXOSX)
+ #define FAR
+
+ typedef int BOOL; // To match Windows
+ typedef char * LPSTR;
+ typedef const char * LPCSTR;
+ typedef unsigned short WORD;
+ typedef unsigned int DWORD;
+ typedef unsigned char BYTE;
+ typedef BYTE BOOLEAN;
+ typedef unsigned char UCHAR;
+ #define __packed __attribute__((packed))
+ typedef BYTE * LPBYTE;
+ #define HIWORD(x) (WORD)(((x)>>16) & 0xffff)
+ #define LOWORD(x) (WORD)((x) & 0xffff)
+#endif
+
+#ifdef _IS_WINDOWS_
+#include <windows.h>
+#define __packed
+#endif
+
+/*
+** Sort out the DllExport and DllImport macros. The GCC compiler has its own
+** syntax for this, though it also supports the MS specific __declspec() as
+** a synonym.
+*/
+#ifdef GNUC
+ #define DllExport __attribute__((dllexport))
+ #define DllImport __attribute__((dllimport))
+#endif
+
+#ifndef DllExport
+#ifdef _IS_WINDOWS_
+ #define DllExport __declspec(dllexport)
+ #define DllImport __declspec(dllimport)
+#else
+ #define DllExport
+ #define DllImport
+#endif
+#endif /* _IS_WINDOWS_ */
+
+
+#ifndef TRUE
+ #define TRUE 1
+ #define FALSE 0
+#endif
+
+#endif
--- /dev/null
+/***********************************************************************************
+ CED1401 usb driver. This basic loading is based on the usb-skeleton.c code that is:
+ Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ Copyright (C) 2012 Alois Schloegl <alois.schloegl@ist.ac.at>
+ There is not a great deal of the skeleton left.
+
+ All the remainder dealing specifically with the CED1401 is based on drivers written
+ by CED for other systems (mainly Windows) and is:
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ 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.
+
+
+Endpoints
+*********
+There are 4 endpoints plus the control endpoint in the standard interface
+provided by most 1401s. The control endpoint is used for standard USB requests,
+plus various CED-specific transactions such as start self test, debug and get
+the 1401 status. The other endpoints are:
+
+ 1 Characters to the 1401
+ 2 Characters from the 1401
+ 3 Block data to the 1401
+ 4 Block data to the host.
+
+inside the driver these are indexed as an array from 0 to 3, transactions
+over the control endpoint are carried out using a separate mechanism. The
+use of the endpoints is mostly straightforward, with the driver issuing
+IO request packets (IRPs) as required to transfer data to and from the 1401.
+The handling of endpoint 2 is different because it is used for characters
+from the 1401, which can appear spontaneously and without any other driver
+activity - for example to repeatedly request DMA transfers in Spike2. The
+desired effect is achieved by using an interrupt endpoint which can be
+polled to see if it has data available, and writing the driver so that it
+always maintains a pending read IRP from that endpoint which will read the
+character data and terminate as soon as the 1401 makes data available. This
+works very well, some care is taken with when you kick off this character
+read IRP to avoid it being active when it is not wanted but generally it
+is running all the time.
+
+In the 2270, there are only three endpoints plus the control endpoint. In
+addition to the transactions mentioned above, the control endpoint is used
+to transfer character data to the 1401. The other endpoints are used as:
+
+ 1 Characters from the 1401
+ 2 Block data to the 1401
+ 3 Block data to the host.
+
+The type of interface available is specified by the interface subclass field
+in the interface descriptor provided by the 1401. See the USB_INT_ constants
+for the values that this field can hold.
+
+****************************************************************************
+Linux implementation
+
+Although Linux Device Drivers (3rd Edition) was a major source of information,
+it is very out of date. A lot of information was gleaned from the latest
+usb_skeleton.c code (you need to download the kernel sources to get this).
+
+To match the Windows version, everything is done using ioctl calls. All the
+device state is held in the DEVICE_EXTENSION (named to match Windows use).
+Block transfers are done by using get_user_pages() to pin down a list of
+pages that we hold a pointer to in the device driver. We also allocate a
+coherent transfer buffer of size STAGED_SZ (this must be a multiple of the
+bulk endpoint size so that the 1401 does not realise that we break large
+transfers down into smaller pieces). We use kmap_atomic() to get a kernel
+va for each page, as it is required, for copying; see CopyUserSpace().
+
+All character and data transfers are done using asynchronous IO. All Urbs are
+tracked by anchoring them. Status and debug ioctls are implemented with the
+synchronous non-Urb based transfers.
+*/
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/version.h>
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) )
+ #include <linux/init.h>
+ #include <linux/slab.h>
+ #include <linux/module.h>
+ #include <linux/kref.h>
+ #include <linux/uaccess.h>
+#endif
+
+
+#include "usb1401.h"
+
+/* Define these values to match your devices */
+#define USB_CED_VENDOR_ID 0x0525
+#define USB_CED_PRODUCT_ID 0xa0f0
+
+/* table of devices that work with this driver */
+static const struct usb_device_id ced_table[] =
+{
+ { USB_DEVICE(USB_CED_VENDOR_ID, USB_CED_PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, ced_table);
+
+
+/* Get a minor range for your devices from the usb maintainer */
+#define USB_CED_MINOR_BASE 192
+
+/* our private defines. if this grows any larger, use your own .h file */
+#define MAX_TRANSFER (PAGE_SIZE - 512)
+/* MAX_TRANSFER is chosen so that the VM is not stressed by
+ allocations > PAGE_SIZE and the number of packets in a page
+ is an integer 512 is the largest possible packet on EHCI */
+#define WRITES_IN_FLIGHT 8
+/* arbitrarily chosen */
+
+/*
+The cause for these errors is that the driver makes use of the functions usb_buffer_alloc() and usb_buffer_free() which got renamed in kernel 2.6.35. This is stated in the Changelog: USB: rename usb_buffer_alloc() and usb_buffer_free() users
+ For more clearance what the functions actually do,
+ usb_buffer_alloc() is renamed to usb_alloc_coherent()
+ usb_buffer_free() is renamed to usb_free_coherent()
+ This is needed on Debian 2.6.32-5-amd64
+*/
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) )
+ #define usb_alloc_coherent usb_buffer_alloc
+ #define usb_free_coherent usb_buffer_free
+ #define noop_llseek NULL
+#endif
+
+static struct usb_driver ced_driver;
+
+static void ced_delete(struct kref *kref)
+{
+ DEVICE_EXTENSION *pdx = to_DEVICE_EXTENSION(kref);
+
+ // Free up the output buffer, then free the output urb. Note that the interface member
+ // of pdx will probably be NULL, so cannot be used to get to dev.
+ usb_free_coherent(pdx->udev, OUTBUF_SZ, pdx->pCoherCharOut, pdx->pUrbCharOut->transfer_dma);
+ usb_free_urb(pdx->pUrbCharOut);
+
+ // Do the same for chan input
+ usb_free_coherent(pdx->udev, INBUF_SZ, pdx->pCoherCharIn, pdx->pUrbCharIn->transfer_dma);
+ usb_free_urb(pdx->pUrbCharIn);
+
+ // Do the same for the block transfers
+ usb_free_coherent(pdx->udev, STAGED_SZ, pdx->pCoherStagedIO, pdx->pStagedUrb->transfer_dma);
+ usb_free_urb(pdx->pStagedUrb);
+
+ usb_put_dev(pdx->udev);
+ kfree(pdx);
+}
+
+// This is the driver end of the open() call from user space.
+static int ced_open(struct inode *inode, struct file *file)
+{
+ DEVICE_EXTENSION *pdx;
+ int retval = 0;
+ int subminor = iminor(inode);
+ struct usb_interface* interface = usb_find_interface(&ced_driver, subminor);
+ if (!interface)
+ {
+ err("%s - error, can't find device for minor %d", __func__, subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ pdx = usb_get_intfdata(interface);
+ if (!pdx)
+ {
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ dev_dbg(&interface->dev, "%s got pdx", __func__);
+
+ /* increment our usage count for the device */
+ kref_get(&pdx->kref);
+
+ /* lock the device to allow correctly handling errors
+ * in resumption */
+ mutex_lock(&pdx->io_mutex);
+
+ if (!pdx->open_count++)
+ {
+ retval = usb_autopm_get_interface(interface);
+ if (retval)
+ {
+ pdx->open_count--;
+ mutex_unlock(&pdx->io_mutex);
+ kref_put(&pdx->kref, ced_delete);
+ goto exit;
+ }
+ }
+ else
+ { //uncomment this block if you want exclusive open
+ dev_err(&interface->dev, "%s fail: already open", __func__);
+ retval = -EBUSY;
+ pdx->open_count--;
+ mutex_unlock(&pdx->io_mutex);
+ kref_put(&pdx->kref, ced_delete);
+ goto exit;
+ }
+ /* prevent the device from being autosuspended */
+
+ /* save our object in the file's private structure */
+ file->private_data = pdx;
+ mutex_unlock(&pdx->io_mutex);
+
+exit:
+ return retval;
+}
+
+static int ced_release(struct inode *inode, struct file *file)
+{
+ DEVICE_EXTENSION *pdx = file->private_data;
+ if (pdx == NULL)
+ return -ENODEV;
+
+ dev_dbg(&pdx->interface->dev,"%s called", __func__);
+ mutex_lock(&pdx->io_mutex);
+ if (!--pdx->open_count && pdx->interface) // Allow autosuspend
+ usb_autopm_put_interface(pdx->interface);
+ mutex_unlock(&pdx->io_mutex);
+
+ kref_put(&pdx->kref, ced_delete); // decrement the count on our device
+ return 0;
+}
+
+static int ced_flush(struct file *file, fl_owner_t id)
+{
+ int res;
+ DEVICE_EXTENSION *pdx = file->private_data;
+ if (pdx == NULL)
+ return -ENODEV;
+
+ dev_dbg(&pdx->interface->dev,"%s char in pend=%d", __func__, pdx->bReadCharsPending);
+
+ /* wait for io to stop */
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev,"%s got io_mutex", __func__);
+ ced_draw_down(pdx);
+
+ /* read out errors, leave subsequent opens a clean slate */
+ spin_lock_irq(&pdx->err_lock);
+ res = pdx->errors ? (pdx->errors == -EPIPE ? -EPIPE : -EIO) : 0;
+ pdx->errors = 0;
+ spin_unlock_irq(&pdx->err_lock);
+
+ mutex_unlock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev,"%s exit reached", __func__);
+
+ return res;
+}
+
+
+static ssize_t ced_read(struct file *file, char *buffer, size_t count,
+ loff_t *ppos)
+{
+ DEVICE_EXTENSION *pdx = file->private_data;
+ dev_err(&pdx->interface->dev, "%s called: use ioctl for cedusb", __func__);
+ return 0; // as we do not do reads this way
+}
+
+static ssize_t ced_write(struct file *file, const char *user_buffer,
+ size_t count, loff_t *ppos)
+{
+ DEVICE_EXTENSION *pdx = file->private_data;
+ dev_err(&pdx->interface->dev, "%s called: use ioctl for cedusb", __func__);
+ return 0;
+}
+
+/***************************************************************************
+** CanAcceptIoRequests
+** If the device is removed, interface is set NULL. We also clear our pointer
+** from the interface, so we should make sure that pdx is not NULL. This will
+** not help with a device extension held by a file.
+** return true if can accept new io requests, else false
+*/
+static bool CanAcceptIoRequests(DEVICE_EXTENSION* pdx)
+{
+ return pdx && pdx->interface; // Can we accept IO requests
+}
+
+/****************************************************************************
+** Callback routine to complete writes. This may need to fire off another
+** urb to complete the transfer.
+****************************************************************************/
+static void ced_writechar_callback(struct urb* pUrb)
+{
+ DEVICE_EXTENSION *pdx = pUrb->context;
+ int nGot = pUrb->actual_length; // what we transferred
+
+ if (pUrb->status)
+ { // sync/async unlink faults aren't errors
+ if (!(pUrb->status == -ENOENT || pUrb->status == -ECONNRESET || pUrb->status == -ESHUTDOWN))
+ {
+ dev_err(&pdx->interface->dev, "%s - nonzero write bulk status received: %d", __func__, pUrb->status);
+ }
+
+ spin_lock(&pdx->err_lock);
+ pdx->errors = pUrb->status;
+ spin_unlock(&pdx->err_lock);
+ nGot = 0; // and tidy up again if so
+
+ spin_lock(&pdx->charOutLock); // already at irq level
+ pdx->dwOutBuffGet = 0; // Reset the output buffer
+ pdx->dwOutBuffPut = 0;
+ pdx->dwNumOutput = 0; // Clear the char count
+ pdx->bPipeError[0] = 1; // Flag an error for later
+ pdx->bSendCharsPending = false; // Allow other threads again
+ spin_unlock(&pdx->charOutLock); // already at irq level
+ dev_dbg(&pdx->interface->dev, "%s - char out done, 0 chars sent", __func__);
+ }
+ else
+ {
+ dev_dbg(&pdx->interface->dev, "%s - char out done, %d chars sent", __func__, nGot);
+ spin_lock(&pdx->charOutLock); // already at irq level
+ pdx->dwNumOutput -= nGot; // Now adjust the char send buffer
+ pdx->dwOutBuffGet += nGot; // to match what we did
+ if (pdx->dwOutBuffGet >= OUTBUF_SZ) // Can't do this any earlier as data could be overwritten
+ pdx->dwOutBuffGet = 0;
+
+ if (pdx->dwNumOutput > 0) // if more to be done...
+ {
+ int nPipe = 0; // The pipe number to use
+ int iReturn;
+ char* pDat = &pdx->outputBuffer[pdx->dwOutBuffGet];
+ unsigned int dwCount = pdx->dwNumOutput; // maximum to send
+ if ((pdx->dwOutBuffGet+dwCount) > OUTBUF_SZ) // does it cross buffer end?
+ dwCount = OUTBUF_SZ - pdx->dwOutBuffGet;
+ spin_unlock(&pdx->charOutLock); // we are done with stuff that changes
+ memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer
+ usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev,
+ usb_sndbulkpipe(pdx->udev, pdx->epAddr[0]),
+ pdx->pCoherCharOut, dwCount, ced_writechar_callback, pdx);
+ pdx->pUrbCharOut->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted); // in case we need to kill it
+ iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_ATOMIC);
+ dev_dbg(&pdx->interface->dev, "%s n=%d>%s<", __func__, dwCount, pDat);
+ spin_lock(&pdx->charOutLock); // grab lock for errors
+ if (iReturn)
+ {
+ pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
+ pdx->bSendCharsPending = false; // Allow other threads again
+ usb_unanchor_urb(pdx->pUrbCharOut);
+ dev_err(&pdx->interface->dev, "%s usb_submit_urb() returned %d", __func__, iReturn);
+ }
+ }
+ else
+ pdx->bSendCharsPending = false; // Allow other threads again
+ spin_unlock(&pdx->charOutLock); // already at irq level
+ }
+}
+
+/****************************************************************************
+** SendChars
+** Transmit the characters in the output buffer to the 1401. This may need
+** breaking down into multiple transfers.
+****************************************************************************/
+int SendChars(DEVICE_EXTENSION* pdx)
+{
+ int iReturn = U14ERR_NOERROR;
+
+ spin_lock_irq(&pdx->charOutLock); // Protect ourselves
+
+ if ((!pdx->bSendCharsPending) && // Not currently sending
+ (pdx->dwNumOutput > 0) && // has characters to output
+ (CanAcceptIoRequests(pdx))) // and current activity is OK
+ {
+ unsigned int dwCount = pdx->dwNumOutput; // Get a copy of the character count
+ pdx->bSendCharsPending = true; // Set flag to lock out other threads
+
+ dev_dbg(&pdx->interface->dev, "Send %d chars to 1401, EP0 flag %d\n", dwCount, pdx->nPipes == 3);
+ // If we have only 3 end points we must send the characters to the 1401 using EP0.
+ if (pdx->nPipes == 3)
+ {
+ // For EP0 character transmissions to the 1401, we have to hang about until they
+ // are gone, as otherwise without more character IO activity they will never go.
+ unsigned int count = dwCount; // Local char counter
+ unsigned int index = 0; // The index into the char buffer
+
+ spin_unlock_irq(&pdx->charOutLock); // Free spinlock as we call USBD
+
+ while ((count > 0) && (iReturn == U14ERR_NOERROR))
+ {
+ // We have to break the transfer up into 64-byte chunks because of a 2270 problem
+ int n = count > 64 ? 64 : count; // Chars for this xfer, max of 64
+ int nSent = usb_control_msg(pdx->udev,
+ usb_sndctrlpipe(pdx->udev,0), // use end point 0
+ DB_CHARS, // bRequest
+ (H_TO_D|VENDOR|DEVREQ), // to the device, vendor request to the device
+ 0,0, // value and index are both 0
+ &pdx->outputBuffer[index], // where to send from
+ n, // how much to send
+ 1000); // timeout in jiffies
+ if (nSent <= 0)
+ {
+ iReturn = nSent ? nSent : -ETIMEDOUT; // if 0 chars says we timed out
+ dev_err(&pdx->interface->dev, "Send %d chars by EP0 failed: %d", n, iReturn);
+ }
+ else
+ {
+ dev_dbg(&pdx->interface->dev, "Sent %d chars by EP0", n);
+ count -= nSent;
+ index += nSent;
+ }
+ }
+
+ spin_lock_irq(&pdx->charOutLock); // Protect pdx changes, released by general code
+ pdx->dwOutBuffGet = 0; // so reset the output buffer
+ pdx->dwOutBuffPut = 0;
+ pdx->dwNumOutput = 0; // and clear the buffer count
+ pdx->bSendCharsPending = false; // Allow other threads again
+ }
+ else
+ { // Here for sending chars normally - we hold the spin lock
+ int nPipe = 0; // The pipe number to use
+ char* pDat = &pdx->outputBuffer[pdx->dwOutBuffGet];
+
+ if ((pdx->dwOutBuffGet+dwCount) > OUTBUF_SZ) // does it cross buffer end?
+ dwCount = OUTBUF_SZ - pdx->dwOutBuffGet;
+ spin_unlock_irq(&pdx->charOutLock); // we are done with stuff that changes
+ memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer
+ usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev,
+ usb_sndbulkpipe(pdx->udev, pdx->epAddr[0]),
+ pdx->pCoherCharOut, dwCount, ced_writechar_callback, pdx);
+ pdx->pUrbCharOut->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted);
+ iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_KERNEL);
+ spin_lock_irq(&pdx->charOutLock); // grab lock for errors
+ if (iReturn)
+ {
+ pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
+ pdx->bSendCharsPending = false; // Allow other threads again
+ usb_unanchor_urb(pdx->pUrbCharOut); // remove from list of active urbs
+ }
+ }
+ }
+ else
+ if (pdx->bSendCharsPending && (pdx->dwNumOutput > 0))
+ dev_dbg(&pdx->interface->dev, "SendChars bSendCharsPending:true");
+
+
+ dev_dbg(&pdx->interface->dev, "SendChars exit code: %d", iReturn);
+ spin_unlock_irq(&pdx->charOutLock); // Now let go of the spinlock
+ return iReturn;
+}
+
+/***************************************************************************
+** CopyUserSpace
+** This moves memory between pinned down user space and the pCoherStagedIO
+** memory buffer we use for transfers. Copy n bytes in the directions that
+** is defined by pdx->StagedRead. The user space is determined by the area
+** in pdx->StagedId and the offset in pdx->StagedDone. The user
+** area may well not start on a page boundary, so allow for that.
+**
+** We have a table of physical pages that describe the area, so we can use
+** this to get a virtual address that the kernel can use.
+**
+** pdx Is our device extension which holds all we know about the transfer.
+** n The number of bytes to move one way or the other.
+***************************************************************************/
+static void CopyUserSpace(DEVICE_EXTENSION *pdx, int n)
+{
+ unsigned int nArea = pdx->StagedId;
+ if (nArea < MAX_TRANSAREAS)
+ {
+ TRANSAREA *pArea = &pdx->rTransDef[nArea]; // area to be used
+ unsigned int dwOffset = pdx->StagedDone + pdx->StagedOffset + pArea->dwBaseOffset;
+ char* pCoherBuf = pdx->pCoherStagedIO; // coherent buffer
+ if (!pArea->bUsed)
+ {
+ dev_err(&pdx->interface->dev, "%s area %d unused", __func__, nArea);
+ return;
+ }
+
+ while (n)
+ {
+ int nPage = dwOffset >> PAGE_SHIFT; // page number in table
+ if (nPage < pArea->nPages)
+ {
+ char *pvAddress = (char*)kmap_atomic(pArea->pPages[nPage], KM_IRQ0);
+ if (pvAddress)
+ {
+ unsigned int uiPageOff = dwOffset & (PAGE_SIZE-1); // offset into the page
+ size_t uiXfer = PAGE_SIZE - uiPageOff; // max to transfer on this page
+ if (uiXfer > n) // limit byte count if too much
+ uiXfer = n; // for the page
+ if (pdx->StagedRead)
+ memcpy(pvAddress+uiPageOff, pCoherBuf, uiXfer);
+ else
+ memcpy(pCoherBuf, pvAddress+uiPageOff, uiXfer);
+ kunmap_atomic(pvAddress, KM_IRQ0);
+ dwOffset += uiXfer;
+ pCoherBuf += uiXfer;
+ n -= uiXfer;
+ }
+ else
+ {
+ dev_err(&pdx->interface->dev, "%s did not map page %d", __func__, nPage);
+ return;
+ }
+
+ }
+ else
+ {
+ dev_err(&pdx->interface->dev, "%s exceeded pages %d", __func__, nPage);
+ return;
+ }
+ }
+ }
+ else
+ dev_err(&pdx->interface->dev, "%s bad area %d", __func__, nArea);
+}
+
+// Forward declarations for stuff used circularly
+static int StageChunk(DEVICE_EXTENSION *pdx);
+/***************************************************************************
+** ReadWrite_Complete
+**
+** Completion routine for our staged read/write Irps
+*/
+static void staged_callback(struct urb* pUrb)
+{
+ DEVICE_EXTENSION *pdx = pUrb->context;
+ unsigned int nGot = pUrb->actual_length; // what we transferred
+ bool bCancel = false;
+ bool bRestartCharInput; // used at the end
+
+ spin_lock(&pdx->stagedLock); // stop ReadWriteMem() action while this routine is running
+ pdx->bStagedUrbPending = false; // clear the flag for staged IRP pending
+
+ if (pUrb->status)
+ { // sync/async unlink faults aren't errors
+ if (!(pUrb->status == -ENOENT || pUrb->status == -ECONNRESET || pUrb->status == -ESHUTDOWN))
+ {
+ dev_err(&pdx->interface->dev, "%s - nonzero write bulk status received: %d", __func__, pUrb->status);
+ }
+ else
+ dev_info(&pdx->interface->dev, "%s - staged xfer cancelled", __func__);
+
+ spin_lock(&pdx->err_lock);
+ pdx->errors = pUrb->status;
+ spin_unlock(&pdx->err_lock);
+ nGot = 0; // and tidy up again if so
+ bCancel = true;
+ }
+ else
+ {
+ dev_dbg(&pdx->interface->dev, "%s %d chars xferred", __func__, nGot);
+ if (pdx->StagedRead) // if reading, save to user space
+ CopyUserSpace(pdx, nGot); // copy from buffer to user
+ if (nGot == 0)
+ dev_dbg(&pdx->interface->dev, "%s ZLP", __func__);
+ }
+
+ // Update the transfer length based on the TransferBufferLength value in the URB
+ pdx->StagedDone += nGot;
+
+ dev_dbg(&pdx->interface->dev, "%s, done %d bytes of %d", __func__, pdx->StagedDone, pdx->StagedLength);
+
+ if ((pdx->StagedDone == pdx->StagedLength) || // If no more to do
+ (bCancel)) // or this IRP was cancelled
+ {
+ TRANSAREA* pArea = &pdx->rTransDef[pdx->StagedId]; // Transfer area info
+ dev_dbg(&pdx->interface->dev, "%s transfer done, bytes %d, cancel %d", __func__, pdx->StagedDone, bCancel);
+
+ // Here is where we sort out what to do with this transfer if using a circular buffer. We have
+ // a completed transfer that can be assumed to fit into the transfer area. We should be able to
+ // add this to the end of a growing block or to use it to start a new block unless the code
+ // that calculates the offset to use (in ReadWriteMem) is totally duff.
+ if ((pArea->bCircular) && (pArea->bCircToHost) && (!bCancel) && // Time to sort out circular buffer info?
+ (pdx->StagedRead)) // Only for tohost transfers for now
+ {
+ if (pArea->aBlocks[1].dwSize > 0) // If block 1 is in use we must append to it
+ {
+ if (pdx->StagedOffset == (pArea->aBlocks[1].dwOffset + pArea->aBlocks[1].dwSize))
+ {
+ pArea->aBlocks[1].dwSize += pdx->StagedLength;
+ dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 1 now %d bytes at %d",
+ pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
+ }
+ else
+ {
+ // Here things have gone very, very, wrong, but I cannot see how this can actually be achieved
+ pArea->aBlocks[1].dwOffset = pdx->StagedOffset;
+ pArea->aBlocks[1].dwSize = pdx->StagedLength;
+ dev_err(&pdx->interface->dev, "%s ERROR, circ block 1 re-started %d bytes at %d",
+ __func__, pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
+ }
+ }
+ else // If block 1 is not used, we try to add to block 0
+ {
+ if (pArea->aBlocks[0].dwSize > 0) // Got stored block 0 information?
+ { // Must append onto the existing block 0
+ if (pdx->StagedOffset == (pArea->aBlocks[0].dwOffset + pArea->aBlocks[0].dwSize))
+ {
+ pArea->aBlocks[0].dwSize += pdx->StagedLength; // Just add this transfer in
+ dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 0 now %d bytes at %d",
+ pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset);
+ }
+ else // If it doesn't append, put into new block 1
+ {
+ pArea->aBlocks[1].dwOffset = pdx->StagedOffset;
+ pArea->aBlocks[1].dwSize = pdx->StagedLength;
+ dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 1 started %d bytes at %d",
+ pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
+ }
+ }
+ else // No info stored yet, just save in block 0
+ {
+ pArea->aBlocks[0].dwOffset = pdx->StagedOffset;
+ pArea->aBlocks[0].dwSize = pdx->StagedLength;
+ dev_dbg(&pdx->interface->dev, "RWM_Complete, circ block 0 started %d bytes at %d",
+ pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset);
+ }
+ }
+ }
+
+ if (!bCancel) // Don't generate an event if cancelled
+ {
+ dev_dbg(&pdx->interface->dev, "RWM_Complete, bCircular %d, bToHost %d, eStart %d, eSize %d",
+ pArea->bCircular, pArea->bEventToHost, pArea->dwEventSt, pArea->dwEventSz);
+ if ((pArea->dwEventSz) && // Set a user-mode event...
+ (pdx->StagedRead == pArea->bEventToHost)) // ...on transfers in this direction?
+ {
+ int iWakeUp = 0; // assume
+ // If we have completed the right sort of DMA transfer then set the event to notify
+ // the user code to wake up anyone that is waiting.
+ if ((pArea->bCircular) && // Circular areas use a simpler test
+ (pArea->bCircToHost)) // only in supported direction
+ { // Is total data waiting up to size limit?
+ unsigned int dwTotal = pArea->aBlocks[0].dwSize + pArea->aBlocks[1].dwSize;
+ iWakeUp = (dwTotal >= pArea->dwEventSz);
+ }
+ else
+ {
+ unsigned int transEnd = pdx->StagedOffset + pdx->StagedLength;
+ unsigned int eventEnd = pArea->dwEventSt + pArea->dwEventSz;
+ iWakeUp = (pdx->StagedOffset < eventEnd) && (transEnd > pArea->dwEventSt);
+ }
+
+ if (iWakeUp)
+ {
+ dev_dbg(&pdx->interface->dev, "About to set event to notify app");
+ wake_up_interruptible(&pArea->wqEvent); // wake up waiting processes
+ ++pArea->iWakeUp; // increment wakeup count
+ }
+ }
+ }
+
+ pdx->dwDMAFlag = MODE_CHAR; // Switch back to char mode before ReadWriteMem call
+
+ if (!bCancel) // Don't look for waiting transfer if cancelled
+ {
+ // If we have a transfer waiting, kick it off
+ if (pdx->bXFerWaiting) // Got a block xfer waiting?
+ {
+ int iReturn;
+ dev_info(&pdx->interface->dev, "*** RWM_Complete *** pending transfer will now be set up!!!");
+ iReturn = ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard, pdx->rDMAInfo.wIdent, pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize);
+
+ if (iReturn)
+ dev_err(&pdx->interface->dev, "RWM_Complete rw setup failed %d", iReturn);
+ }
+ }
+
+ }
+ else // Here for more to do
+ StageChunk(pdx); // fire off the next bit
+
+ // While we hold the stagedLock, see if we should reallow character input ints
+ // Don't allow if cancelled, or if a new block has started or if there is a waiting block.
+ // This feels wrong as we should ask which spin lock protects dwDMAFlag.
+ bRestartCharInput = !bCancel && (pdx->dwDMAFlag == MODE_CHAR) && !pdx->bXFerWaiting;
+
+ spin_unlock(&pdx->stagedLock); // Finally release the lock again
+
+ // This is not correct as dwDMAFlag is protected by the staged lock, but it is treated
+ // in Allowi as if it were protected by the char lock. In any case, most systems will
+ // not be upset by char input during DMA... sigh. Needs sorting out.
+ if (bRestartCharInput) // may be out of date, but...
+ Allowi(pdx, true); // ...Allowi tests a lock too.
+ dev_dbg(&pdx->interface->dev, "%s done", __func__);
+}
+
+/****************************************************************************
+** StageChunk
+**
+** Generates the next chunk of data making up a staged transfer.
+**
+** The calling code must have acquired the staging spinlock before calling
+** this function, and is responsible for releasing it. We are at callback level.
+****************************************************************************/
+static int StageChunk(DEVICE_EXTENSION *pdx)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned int ChunkSize;
+ int nPipe = pdx->StagedRead ? 3 : 2; // The pipe number to use for reads or writes
+ if (pdx->nPipes == 3) nPipe--; // Adjust for the 3-pipe case
+ if (nPipe < 0) // and trap case that should never happen
+ return U14ERR_FAIL;
+
+ if (!CanAcceptIoRequests(pdx)) // got sudden remove?
+ {
+ dev_info(&pdx->interface->dev, "%s sudden remove, giving up", __func__);
+ return U14ERR_FAIL; // could do with a better error
+ }
+
+ ChunkSize = (pdx->StagedLength - pdx->StagedDone); // transfer length remaining
+ if (ChunkSize > STAGED_SZ) // make sure to keep legal
+ ChunkSize = STAGED_SZ; // limit to max allowed
+
+ if (!pdx->StagedRead) // if writing...
+ CopyUserSpace(pdx, ChunkSize); // ...copy data into the buffer
+
+ usb_fill_bulk_urb(pdx->pStagedUrb, pdx->udev,
+ pdx->StagedRead ? usb_rcvbulkpipe(pdx->udev, pdx->epAddr[nPipe]):
+ usb_sndbulkpipe(pdx->udev, pdx->epAddr[nPipe]),
+ pdx->pCoherStagedIO, ChunkSize, staged_callback, pdx);
+ pdx->pStagedUrb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(pdx->pStagedUrb, &pdx->submitted); // in case we need to kill it
+ iReturn = usb_submit_urb(pdx->pStagedUrb, GFP_ATOMIC);
+ if (iReturn)
+ {
+ usb_unanchor_urb(pdx->pStagedUrb); // kill it
+ pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
+ dev_err(&pdx->interface->dev, "%s submit urb failed, code %d", __func__, iReturn);
+ }
+ else
+ pdx->bStagedUrbPending = true; // Set the flag for staged URB pending
+ dev_dbg(&pdx->interface->dev, "%s done so far:%d, this size:%d", __func__, pdx->StagedDone, ChunkSize);
+
+ return iReturn;
+}
+
+/***************************************************************************
+** ReadWriteMem
+**
+** This routine is used generally for block read and write operations.
+** Breaks up a read or write in to specified sized chunks, as specified by pipe
+** information on maximum transfer size.
+**
+** Any code that calls this must be holding the stagedLock
+**
+** Arguments:
+** DeviceObject - pointer to our FDO (Functional Device Object)
+** Read - TRUE for read, FALSE for write. This is from POV of the driver
+** wIdent - the transfer area number - defines memory area and more.
+** dwOffs - the start offset within the transfer area of the start of this
+** transfer.
+** dwLen - the number of bytes to transfer.
+*/
+int ReadWriteMem(DEVICE_EXTENSION *pdx, bool Read, unsigned short wIdent,
+ unsigned int dwOffs, unsigned int dwLen)
+{
+ TRANSAREA* pArea = &pdx->rTransDef[wIdent]; // Transfer area info
+
+ if (!CanAcceptIoRequests(pdx)) // Are we in a state to accept new requests?
+ {
+ dev_err(&pdx->interface->dev, "%s can't accept requests", __func__);
+ return U14ERR_FAIL;
+ }
+
+ dev_dbg(&pdx->interface->dev, "%s xfer %d bytes to %s, offset %d, area %d",
+ __func__, dwLen, Read ? "host" : "1401", dwOffs, wIdent);
+
+ // Amazingly, we can get an escape sequence back before the current staged Urb is done, so we
+ // have to check for this situation and, if so, wait until all is OK.
+ if (pdx->bStagedUrbPending)
+ {
+ pdx->bXFerWaiting = true; // Flag we are waiting
+ dev_info(&pdx->interface->dev, "%s xfer is waiting, as previous staged pending", __func__);
+ return U14ERR_NOERROR;
+ }
+
+ if (dwLen == 0) // allow 0-len read or write; just return success
+ {
+ dev_dbg(&pdx->interface->dev, "%s OK; zero-len read/write request", __func__);
+ return U14ERR_NOERROR;
+ }
+
+ if ((pArea->bCircular) && // Circular transfer?
+ (pArea->bCircToHost) && (Read)) // In a supported direction
+ { // If so, we sort out offset ourself
+ bool bWait = false; // Flag for transfer having to wait
+
+ dev_dbg(&pdx->interface->dev, "Circular buffers are %d at %d and %d at %d",
+ pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset, pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
+ if (pArea->aBlocks[1].dwSize > 0) // Using the second block already?
+ {
+ dwOffs = pArea->aBlocks[1].dwOffset + pArea->aBlocks[1].dwSize; // take offset from that
+ bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0?
+ bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer
+ }
+ else // Area 1 not in use, try to use area 0
+ {
+ if (pArea->aBlocks[0].dwSize == 0) // Reset block 0 if not in use
+ pArea->aBlocks[0].dwOffset = 0;
+ dwOffs = pArea->aBlocks[0].dwOffset + pArea->aBlocks[0].dwSize;
+ if ((dwOffs+dwLen) > pArea->dwLength) // Off the end of the buffer?
+ {
+ pArea->aBlocks[1].dwOffset = 0; // Set up to use second block
+ dwOffs = 0;
+ bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0?
+ bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer
+ }
+ }
+
+ if (bWait) // This transfer will have to wait?
+ {
+ pdx->bXFerWaiting = true; // Flag we are waiting
+ dev_dbg(&pdx->interface->dev, "%s xfer waiting for circular buffer space", __func__);
+ return U14ERR_NOERROR;
+ }
+
+ dev_dbg(&pdx->interface->dev, "%s circular xfer, %d bytes starting at %d", __func__, dwLen, dwOffs);
+ }
+
+ // Save the parameters for the read\write transfer
+ pdx->StagedRead = Read; // Save the parameters for this read
+ pdx->StagedId = wIdent; // ID allows us to get transfer area info
+ pdx->StagedOffset = dwOffs; // The area within the transfer area
+ pdx->StagedLength = dwLen;
+ pdx->StagedDone = 0; // Initialise the byte count
+ pdx->dwDMAFlag = MODE_LINEAR; // Set DMA mode flag at this point
+ pdx->bXFerWaiting = false; // Clearly not a transfer waiting now
+
+// KeClearEvent(&pdx->StagingDoneEvent); // Clear the transfer done event
+ StageChunk(pdx); // fire off the first chunk
+
+ return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+**
+** ReadChar
+**
+** Reads a character a buffer. If there is no more
+** data we return FALSE. Used as part of decoding a DMA request.
+**
+****************************************************************************/
+static bool ReadChar(unsigned char* pChar, char* pBuf, unsigned int* pdDone, unsigned int dGot)
+{
+ bool bRead = false;
+ unsigned int dDone = *pdDone;
+
+ if (dDone < dGot) // If there is more data
+ {
+ *pChar = (unsigned char)pBuf[dDone];// Extract the next char
+ dDone++; // Increment the done count
+ *pdDone = dDone;
+ bRead = true; // and flag success
+ }
+
+ return bRead;
+}
+
+#ifdef NOTUSED
+/****************************************************************************
+**
+** ReadWord
+**
+** Reads a word from the 1401, just uses ReadChar twice; passes on any error
+**
+*****************************************************************************/
+static bool ReadWord(unsigned short* pWord, char* pBuf, unsigned int* pdDone, unsigned int dGot)
+{
+ if (ReadChar((unsigned char*)pWord, pBuf, pdDone, dGot))
+ return ReadChar(((unsigned char*)pWord)+1, pBuf, pdDone, dGot);
+ else
+ return false;
+}
+#endif
+
+/****************************************************************************
+** ReadHuff
+**
+** Reads a coded number in and returns it, Code is:
+** If data is in range 0..127 we recieve 1 byte. If data in range 128-16383
+** we recieve two bytes, top bit of first indicates another on its way. If
+** data in range 16383-4194303 we get three bytes, top two bits of first set
+** to indicate three byte total.
+**
+*****************************************************************************/
+static bool ReadHuff(volatile unsigned int* pDWord, char* pBuf, unsigned int* pdDone, unsigned int dGot)
+{
+ unsigned char ucData; /* for each read to ReadChar */
+ bool bReturn = true; /* assume we will succeed */
+ unsigned int dwData = 0; /* Accumulator for the data */
+
+ if (ReadChar(&ucData, pBuf, pdDone, dGot))
+ {
+ dwData = ucData; /* copy the data */
+ if ((dwData & 0x00000080) != 0) /* Bit set for more data ? */
+ {
+ dwData &= 0x0000007F; /* Clear the relevant bit */
+ if (ReadChar(&ucData, pBuf, pdDone, dGot))
+ {
+ dwData = (dwData << 8) | ucData;
+ if ((dwData & 0x00004000) != 0) /* three byte sequence ? */
+ {
+ dwData &= 0x00003FFF; /* Clear the relevant bit */
+ if (ReadChar(&ucData, pBuf, pdDone, dGot))
+ dwData = (dwData << 8) | ucData;
+ else
+ bReturn = false;
+ }
+ }
+ else
+ bReturn = false; /* couldn't read data */
+ }
+ }
+ else
+ bReturn = false;
+
+ *pDWord = dwData; /* return the data */
+ return bReturn;
+}
+
+/***************************************************************************
+**
+** ReadDMAInfo
+**
+** Tries to read info about the dma request from the 1401 and decode it into
+** the dma descriptor block. We have at this point had the escape character
+** from the 1401 and now we must read in the rest of the information about
+** the transfer request. Returns FALSE if 1401 fails to respond or obselete
+** code from 1401 or bad parameters.
+**
+** The pBuf char pointer does not include the initial escape character, so
+** we start handling the data at offset zero.
+**
+*****************************************************************************/
+static bool ReadDMAInfo(volatile DMADESC* pDmaDesc, DEVICE_EXTENSION *pdx,
+ char* pBuf, unsigned int dwCount)
+{
+ bool bResult = false; // assume we won't succeed
+ unsigned char ucData;
+ unsigned int dDone = 0; // We haven't parsed anything so far
+
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ if (ReadChar(&ucData, pBuf, &dDone, dwCount))
+ {
+ unsigned char ucTransCode = (ucData & 0x0F); // get code for transfer type
+ unsigned short wIdent = ((ucData >> 4) & 0x07); // and area identifier
+
+ // fill in the structure we were given
+ pDmaDesc->wTransType = ucTransCode; // type of transfer
+ pDmaDesc->wIdent = wIdent; // area to use
+ pDmaDesc->dwSize = 0; // initialise other bits
+ pDmaDesc->dwOffset = 0;
+
+ dev_dbg(&pdx->interface->dev, "%s type: %d ident: %d", __func__, pDmaDesc->wTransType, pDmaDesc->wIdent);
+
+ pDmaDesc->bOutWard = (ucTransCode != TM_EXTTOHOST); // set transfer direction
+
+ switch (ucTransCode)
+ {
+ case TM_EXTTOHOST: // Extended linear transfer modes (the only ones!)
+ case TM_EXTTO1401:
+ {
+ bResult = ReadHuff(&(pDmaDesc->dwOffset), pBuf, &dDone, dwCount) &&
+ ReadHuff(&(pDmaDesc->dwSize), pBuf, &dDone, dwCount);
+ if (bResult)
+ {
+ dev_dbg(&pdx->interface->dev, "%s xfer offset & size %d %d",
+ __func__, pDmaDesc->dwOffset, pDmaDesc->dwSize);
+
+ if ((wIdent >= MAX_TRANSAREAS) || // Illegal area number, or...
+ (!pdx->rTransDef[wIdent].bUsed) || // area not set up, or...
+ (pDmaDesc->dwOffset > pdx->rTransDef[wIdent].dwLength) || // range/size
+ ((pDmaDesc->dwOffset + pDmaDesc->dwSize) > (pdx->rTransDef[wIdent].dwLength)))
+ {
+ bResult = false; // bad parameter(s)
+ dev_dbg(&pdx->interface->dev, "%s bad param - id %d, bUsed %d, offset %d, size %d, area length %d",
+ __func__, wIdent, pdx->rTransDef[wIdent].bUsed, pDmaDesc->dwOffset, pDmaDesc->dwSize,
+ pdx->rTransDef[wIdent].dwLength);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else
+ bResult = false;
+
+ if (!bResult) // now check parameters for validity
+ dev_err(&pdx->interface->dev, "%s error reading Esc sequence", __func__);
+
+ return bResult;
+}
+
+/****************************************************************************
+**
+** Handle1401Esc
+**
+** Deals with an escape sequence coming from the 1401. This can either be
+** a DMA transfer request of various types or a response to an escape sequence
+** sent to the 1401. This is called from a callback.
+**
+** Parameters are
+**
+** dwCount - the number of characters in the device extension char in buffer,
+** this is known to be at least 2 or we will not be called.
+**
+****************************************************************************/
+static int Handle1401Esc(DEVICE_EXTENSION* pdx, char* pCh, unsigned int dwCount)
+{
+ int iReturn = U14ERR_FAIL;
+
+ // I have no idea what this next test is about. '?' is 0x3f, which is area 3, code
+ // 15. At the moment, this is not used, so it does no harm, but unless someone can
+ // tell me what this is for, it should be removed from this and the Windows driver.
+ if (pCh[0] == '?') // Is this an information response
+ { // Parse and save the information
+ }
+ else
+ {
+ spin_lock(&pdx->stagedLock); // Lock others out
+
+ if (ReadDMAInfo(&pdx->rDMAInfo, pdx, pCh, dwCount)) // Get DMA parameters
+ {
+ unsigned short wTransType = pdx->rDMAInfo.wTransType; // check transfer type
+
+ dev_dbg(&pdx->interface->dev, "%s xfer to %s, offset %d, length %d", __func__,
+ pdx->rDMAInfo.bOutWard ? "1401" : "host",
+ pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize);
+
+ if (pdx->bXFerWaiting) // Check here for badly out of kilter...
+ { // This can never happen, really
+ dev_err(&pdx->interface->dev, "ERROR: DMA setup while transfer still waiting");
+ spin_unlock(&pdx->stagedLock);
+ }
+ else
+ {
+ if ((wTransType == TM_EXTTOHOST) || (wTransType == TM_EXTTO1401))
+ {
+ iReturn = ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard, pdx->rDMAInfo.wIdent, pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize);
+ if (iReturn != U14ERR_NOERROR)
+ dev_err(&pdx->interface->dev, "%s ReadWriteMem() failed %d", __func__, iReturn);
+ }
+ else // This covers non-linear transfer setup
+ dev_err(&pdx->interface->dev, "%s Unknown block xfer type %d", __func__, wTransType);
+ }
+ }
+ else // Failed to read parameters
+ dev_err(&pdx->interface->dev, "%s ReadDMAInfo() fail", __func__);
+
+ spin_unlock(&pdx->stagedLock); // OK here
+ }
+
+ dev_dbg(&pdx->interface->dev, "%s returns %d", __func__, iReturn);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** Callback for the character read complete or error
+****************************************************************************/
+static void ced_readchar_callback(struct urb* pUrb)
+{
+ DEVICE_EXTENSION *pdx = pUrb->context;
+ int nGot = pUrb->actual_length; // what we transferred
+
+ if (pUrb->status) // Do we have a problem to handle?
+ {
+ int nPipe = pdx->nPipes == 4 ? 1 : 0; // The pipe number to use for error
+ // sync/async unlink faults aren't errors... just saying device removed or stopped
+ if (!(pUrb->status == -ENOENT || pUrb->status == -ECONNRESET || pUrb->status == -ESHUTDOWN))
+ {
+ dev_err(&pdx->interface->dev, "%s - nonzero write bulk status received: %d", __func__, pUrb->status);
+ }
+ else
+ dev_dbg(&pdx->interface->dev, "%s - 0 chars pUrb->status=%d (shutdown?)", __func__, pUrb->status);
+
+ spin_lock(&pdx->err_lock);
+ pdx->errors = pUrb->status;
+ spin_unlock(&pdx->err_lock);
+ nGot = 0; // and tidy up again if so
+
+ spin_lock(&pdx->charInLock); // already at irq level
+ pdx->bPipeError[nPipe] = 1; // Flag an error for later
+ }
+ else
+ {
+ if ((nGot > 1) && ((pdx->pCoherCharIn[0] & 0x7f) == 0x1b)) // Esc sequence?
+ {
+ Handle1401Esc(pdx, &pdx->pCoherCharIn[1], nGot-1); // handle it
+ spin_lock(&pdx->charInLock); // already at irq level
+ }
+ else
+ {
+ spin_lock(&pdx->charInLock); // already at irq level
+ if (nGot > 0)
+ {
+ unsigned int i;
+ if (nGot < INBUF_SZ)
+ {
+ pdx->pCoherCharIn[nGot] = 0; // tidy the string
+ dev_dbg(&pdx->interface->dev, "%s got %d chars >%s<", __func__, nGot, pdx->pCoherCharIn);
+ }
+
+ // We know that whatever we read must fit in the input buffer
+ for (i = 0; i < nGot; i++)
+ {
+ pdx->inputBuffer[pdx->dwInBuffPut++] = pdx->pCoherCharIn[i] & 0x7F;
+ if (pdx->dwInBuffPut >= INBUF_SZ)
+ pdx->dwInBuffPut = 0;
+ }
+
+ if ((pdx->dwNumInput + nGot) <= INBUF_SZ)
+ pdx->dwNumInput += nGot; // Adjust the buffer count accordingly
+ }
+ else
+ dev_dbg(&pdx->interface->dev, "%s read ZLP", __func__);
+ }
+ }
+
+ pdx->bReadCharsPending = false; // No longer have a pending read
+ spin_unlock(&pdx->charInLock); // already at irq level
+
+ Allowi(pdx, true); // see if we can do the next one
+}
+
+/****************************************************************************
+** Allowi
+**
+** This is used to make sure that there is always a pending input transfer so
+** we can pick up any inward transfers. This can be called in multiple contexts
+** so we use the irqsave version of the spinlock.
+****************************************************************************/
+int Allowi(DEVICE_EXTENSION* pdx, bool bInCallback)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned long flags;
+ spin_lock_irqsave(&pdx->charInLock, flags); // can be called in multiple contexts
+
+ // We don't want char input running while DMA is in progress as we know that this
+ // can cause sequencing problems for the 2270. So don't. It will also allow the
+ // ERR response to get back to the host code too early on some PCs, even if there
+ // is no actual driver failure, so we don't allow this at all.
+ if (!pdx->bInDrawDown && // stop input if
+ !pdx->bReadCharsPending && // If no read request outstanding
+ (pdx->dwNumInput < (INBUF_SZ/2)) && // and there is some space
+ (pdx->dwDMAFlag == MODE_CHAR) && // not doing any DMA
+ (!pdx->bXFerWaiting) && // no xfer waiting to start
+ (CanAcceptIoRequests(pdx))) // and activity is generally OK
+ { // then off we go
+ unsigned int nMax = INBUF_SZ-pdx->dwNumInput; // max we could read
+ int nPipe = pdx->nPipes == 4 ? 1 : 0; // The pipe number to use
+
+ dev_dbg(&pdx->interface->dev, "%s %d chars in input buffer", __func__, pdx->dwNumInput);
+
+ usb_fill_int_urb(pdx->pUrbCharIn, pdx->udev,
+ usb_rcvintpipe(pdx->udev, pdx->epAddr[nPipe]),
+ pdx->pCoherCharIn, nMax, ced_readchar_callback,
+ pdx, pdx->bInterval);
+ pdx->pUrbCharIn->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; // short xfers are OK by default
+ usb_anchor_urb(pdx->pUrbCharIn, &pdx->submitted); // in case we need to kill it
+ iReturn = usb_submit_urb(pdx->pUrbCharIn, bInCallback ? GFP_ATOMIC : GFP_KERNEL);
+ if (iReturn)
+ {
+ usb_unanchor_urb(pdx->pUrbCharIn); // remove from list of active Urbs
+ pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
+ dev_err(&pdx->interface->dev,"%s submit urb failed: %d", __func__, iReturn);
+ }
+ else
+ pdx->bReadCharsPending = true; // Flag that we are active here
+ }
+
+ spin_unlock_irqrestore(&pdx->charInLock, flags);
+
+ return iReturn;
+
+}
+
+/*****************************************************************************
+** The ioctl entry point to the driver that is used by us to talk to it.
+** inode The device node (no longer in 3.0.0 kernels)
+** file The file that is open, which holds our pdx pointer
+** ulArg The argument passed in. Note that long is 64-bits in 64-bit system, i.e. it is big
+** enough for a 64-bit pointer.
+*****************************************************************************/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+static int ced_ioctl(struct file * file, unsigned int cmd, unsigned long ulArg)
+#else
+static int ced_ioctl(struct inode * node, struct file * file, unsigned int cmd, unsigned long ulArg)
+#endif
+{
+ int err = 0;
+ DEVICE_EXTENSION *pdx = file->private_data;
+ if (!CanAcceptIoRequests(pdx)) // check we still exist
+ return -ENODEV;
+
+ // Check that access is allowed, where is is needed. Anything that would have an indeterminate
+ // size will be checked by the specific command.
+ if (_IOC_DIR(cmd) & _IOC_READ) // read from point of view of user...
+ err = !access_ok(VERIFY_WRITE, (void __user *)ulArg, _IOC_SIZE(cmd)); // is kernel write
+ else if (_IOC_DIR(cmd) & _IOC_WRITE) // and write from point of view of user...
+ err = !access_ok(VERIFY_READ, (void __user *)ulArg, _IOC_SIZE(cmd)); // is kernel read
+ if (err)
+ return -EFAULT;
+
+ switch (_IOC_NR(cmd))
+ {
+ case _IOC_NR(IOCTL_CED_SENDSTRING(0)):
+ return SendString(pdx, (const char __user*)ulArg, _IOC_SIZE(cmd));
+
+ case _IOC_NR(IOCTL_CED_RESET1401):
+ return Reset1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_GETCHAR):
+ return GetChar(pdx);
+
+ case _IOC_NR(IOCTL_CED_SENDCHAR):
+ return SendChar(pdx, (char)ulArg);
+
+ case _IOC_NR(IOCTL_CED_STAT1401):
+ return Stat1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_LINECOUNT):
+ return LineCount(pdx);
+
+ case _IOC_NR(IOCTL_CED_GETSTRING(0)):
+ return GetString(pdx, (char __user*)ulArg, _IOC_SIZE(cmd));
+
+ case _IOC_NR(IOCTL_CED_SETTRANSFER):
+ return SetTransfer(pdx, (TRANSFERDESC __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_UNSETTRANSFER):
+ return UnsetTransfer(pdx, (int)ulArg);
+
+ case _IOC_NR(IOCTL_CED_SETEVENT):
+ return SetEvent(pdx, (TRANSFEREVENT __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_GETOUTBUFSPACE):
+ return GetOutBufSpace(pdx);
+
+ case _IOC_NR(IOCTL_CED_GETBASEADDRESS):
+ return -1;
+
+ case _IOC_NR(IOCTL_CED_GETDRIVERREVISION):
+ return (2<<24)|(DRIVERMAJREV<<16) | DRIVERMINREV; // USB | MAJOR | MINOR
+
+ case _IOC_NR(IOCTL_CED_GETTRANSFER):
+ return GetTransfer(pdx, (TGET_TX_BLOCK __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_KILLIO1401):
+ return KillIO1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_STATEOF1401):
+ return StateOf1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_GRAB1401):
+ case _IOC_NR(IOCTL_CED_FREE1401):
+ return U14ERR_NOERROR;
+
+ case _IOC_NR(IOCTL_CED_STARTSELFTEST):
+ return StartSelfTest(pdx);
+
+ case _IOC_NR(IOCTL_CED_CHECKSELFTEST):
+ return CheckSelfTest(pdx, (TGET_SELFTEST __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_TYPEOF1401):
+ return TypeOf1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_TRANSFERFLAGS):
+ return TransferFlags(pdx);
+
+ case _IOC_NR(IOCTL_CED_DBGPEEK):
+ return DbgPeek(pdx, (TDBGBLOCK __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGPOKE):
+ return DbgPoke(pdx, (TDBGBLOCK __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGRAMPDATA):
+ return DbgRampData(pdx, (TDBGBLOCK __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGRAMPADDR):
+ return DbgRampAddr(pdx, (TDBGBLOCK __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGGETDATA):
+ return DbgGetData(pdx, (TDBGBLOCK __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGSTOPLOOP):
+ return DbgStopLoop(pdx);
+
+ case _IOC_NR(IOCTL_CED_FULLRESET):
+ pdx->bForceReset = true; // Set a flag for a full reset
+ break;
+
+ case _IOC_NR(IOCTL_CED_SETCIRCULAR):
+ return SetCircular(pdx, (TRANSFERDESC __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_GETCIRCBLOCK):
+ return GetCircBlock(pdx, (TCIRCBLOCK __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_FREECIRCBLOCK):
+ return FreeCircBlock(pdx, (TCIRCBLOCK __user*)ulArg);
+
+ case _IOC_NR(IOCTL_CED_WAITEVENT):
+ return WaitEvent(pdx, (int)(ulArg & 0xff), (int)(ulArg >> 8));
+
+ case _IOC_NR(IOCTL_CED_TESTEVENT):
+ return TestEvent(pdx, (int)ulArg);
+
+ default:
+ return U14ERR_NO_SUCH_FN;
+ }
+ return U14ERR_NOERROR;
+}
+
+static const struct file_operations ced_fops =
+{
+ .owner = THIS_MODULE,
+ .read = ced_read,
+ .write = ced_write,
+ .open = ced_open,
+ .release = ced_release,
+ .flush = ced_flush,
+ .llseek = noop_llseek,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+ .unlocked_ioctl = ced_ioctl,
+#else
+ .ioctl = ced_ioctl,
+#endif
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver ced_class =
+{
+ .name = "cedusb%d",
+ .fops = &ced_fops,
+ .minor_base = USB_CED_MINOR_BASE,
+};
+
+// Check that the device that matches a 1401 vendor and product ID is OK to use and
+// initialise our DEVICE_EXTENSION.
+static int ced_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ DEVICE_EXTENSION *pdx;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i, bcdDevice;
+ int retval = -ENOMEM;
+
+ // allocate memory for our device extension and initialize it
+ pdx = kzalloc(sizeof(*pdx), GFP_KERNEL);
+ if (!pdx)
+ {
+ err("Out of memory");
+ goto error;
+ }
+
+ for (i=0; i<MAX_TRANSAREAS; ++i) // Initialise the wait queues
+ {
+ init_waitqueue_head(&pdx->rTransDef[i].wqEvent);
+ }
+
+ // Put initialises for our stuff here. Note that all of *pdx is zero, so
+ // no need to explicitly zero it.
+ spin_lock_init(&pdx->charOutLock);
+ spin_lock_init(&pdx->charInLock);
+ spin_lock_init(&pdx->stagedLock);
+
+ // Initialises from the skeleton stuff
+ kref_init(&pdx->kref);
+ mutex_init(&pdx->io_mutex);
+ spin_lock_init(&pdx->err_lock);
+ init_usb_anchor(&pdx->submitted);
+
+ pdx->udev = usb_get_dev(interface_to_usbdev(interface));
+ pdx->interface = interface;
+
+ // Attempt to identify the device
+ bcdDevice = pdx->udev->descriptor.bcdDevice;
+ i = (bcdDevice >> 8);
+ if (i == 0)
+ pdx->s1401Type = TYPEU1401;
+ else if ((i>=1) && (i<=23))
+ pdx->s1401Type = i+2;
+ else
+ {
+ dev_err(&interface->dev, "%s Unknown device. bcdDevice = %d", __func__, bcdDevice);
+ goto error;
+ }
+ // set up the endpoint information. We only care about the number of EP as
+ // we know that we are dealing with a 1401 device.
+ iface_desc = interface->cur_altsetting;
+ pdx->nPipes = iface_desc->desc.bNumEndpoints;
+ dev_info(&interface->dev, "1401Type=%d with %d End Points", pdx->s1401Type, pdx->nPipes);
+ if ((pdx->nPipes < 3) || (pdx->nPipes > 4))
+ goto error;
+
+ // Allocate the URBs we hold for performing transfers
+ pdx->pUrbCharOut = usb_alloc_urb(0, GFP_KERNEL); // character output URB
+ pdx->pUrbCharIn = usb_alloc_urb(0, GFP_KERNEL); // character input URB
+ pdx->pStagedUrb = usb_alloc_urb(0, GFP_KERNEL); // block transfer URB
+ if (!pdx->pUrbCharOut || !pdx->pUrbCharIn || !pdx->pStagedUrb)
+ {
+ dev_err(&interface->dev, "%s URB alloc failed", __func__);
+ goto error;
+ }
+
+ pdx->pCoherStagedIO = usb_alloc_coherent(pdx->udev, STAGED_SZ, GFP_KERNEL, &pdx->pStagedUrb->transfer_dma);
+ pdx->pCoherCharOut = usb_alloc_coherent(pdx->udev, OUTBUF_SZ, GFP_KERNEL, &pdx->pUrbCharOut->transfer_dma);
+ pdx->pCoherCharIn = usb_alloc_coherent(pdx->udev, INBUF_SZ, GFP_KERNEL, &pdx->pUrbCharIn->transfer_dma);
+ if (!pdx->pCoherCharOut || !pdx->pCoherCharIn || !pdx->pCoherStagedIO)
+ {
+ dev_err(&interface->dev, "%s Coherent buffer alloc failed", __func__);
+ goto error;
+ }
+
+ for (i = 0; i < pdx->nPipes; ++i)
+ {
+ endpoint = &iface_desc->endpoint[i].desc;
+ pdx->epAddr[i] = endpoint->bEndpointAddress;
+ dev_info(&interface->dev, "Pipe %d, ep address %02x", i, pdx->epAddr[i]);
+ if (((pdx->nPipes==3) && (i==0)) || // if char input end point
+ ((pdx->nPipes==4) && (i==1)))
+ {
+ pdx->bInterval = endpoint->bInterval; // save the endpoint interrupt interval
+ dev_info(&interface->dev, "Pipe %d, bInterval = %d", i, pdx->bInterval);
+ }
+
+ // Detect USB2 by checking last ep size (64 if USB1)
+ if (i == pdx->nPipes-1) // if this is the last ep (bulk)
+ {
+ pdx->bIsUSB2 = le16_to_cpu(endpoint->wMaxPacketSize) > 64;
+ dev_info(&pdx->interface->dev, "USB%d", pdx->bIsUSB2 + 1);
+ }
+ }
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, pdx);
+
+ /* we can register the device now, as it is ready */
+ retval = usb_register_dev(interface, &ced_class);
+ if (retval)
+ {
+ /* something prevented us from registering this driver */
+ err("Not able to get a minor for this device.");
+ usb_set_intfdata(interface, NULL);
+ goto error;
+ }
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&interface->dev,
+ "USB CEDUSB device now attached to cedusb #%d",
+ interface->minor);
+ return 0;
+
+error:
+ if (pdx)
+ kref_put(&pdx->kref, ced_delete); // frees allocated memory
+ return retval;
+}
+
+static void ced_disconnect(struct usb_interface *interface)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(interface);
+ int minor = interface->minor; // save for message at the end
+ int i;
+
+ usb_set_intfdata(interface, NULL); // remove the pdx from the interface
+ usb_deregister_dev(interface, &ced_class); // give back our minor device number
+
+ mutex_lock(&pdx->io_mutex); // stop more I/O starting while...
+ ced_draw_down(pdx); // ...wait for then kill any io
+ for (i=0; i<MAX_TRANSAREAS; ++i)
+ {
+ int iErr = ClearArea(pdx, i); // ...release any used memory
+ if (iErr == U14ERR_UNLOCKFAIL)
+ dev_err(&pdx->interface->dev, "%s Area %d was in used", __func__, i);
+ }
+ pdx->interface = NULL; // ...we kill off link to interface
+ mutex_unlock(&pdx->io_mutex);
+
+ usb_kill_anchored_urbs(&pdx->submitted);
+
+ kref_put(&pdx->kref, ced_delete); // decrement our usage count
+
+ dev_info(&interface->dev, "USB cedusb #%d now disconnected", minor);
+}
+
+// Wait for all the urbs we know of to be done with, then kill off any that
+// are left. NBNB we will need to have a mechanism to stop circular xfers
+// from trying to fire off more urbs. We will wait up to 3 seconds for Urbs
+// to be done.
+void ced_draw_down(DEVICE_EXTENSION *pdx)
+{
+ int time;
+ dev_dbg(&pdx->interface->dev,"%s called", __func__);
+
+ pdx->bInDrawDown = true;
+ time = usb_wait_anchor_empty_timeout(&pdx->submitted, 3000);
+ if (!time) // if we timed out we kill the urbs
+ {
+ usb_kill_anchored_urbs(&pdx->submitted);
+ dev_err(&pdx->interface->dev,"%s timed out", __func__);
+ }
+ pdx->bInDrawDown = false;
+ }
+
+static int ced_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+ if (!pdx)
+ return 0;
+ ced_draw_down(pdx);
+
+ dev_dbg(&pdx->interface->dev,"%s called", __func__);
+ return 0;
+}
+
+static int ced_resume(struct usb_interface *intf)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+ if (!pdx)
+ return 0;
+ dev_dbg(&pdx->interface->dev,"%s called", __func__);
+ return 0;
+}
+
+static int ced_pre_reset(struct usb_interface *intf)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ mutex_lock(&pdx->io_mutex);
+ ced_draw_down(pdx);
+ return 0;
+}
+
+static int ced_post_reset(struct usb_interface *intf)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ /* we are sure no URBs are active - no locking needed */
+ pdx->errors = -EPIPE;
+ mutex_unlock(&pdx->io_mutex);
+
+ return 0;
+}
+
+static struct usb_driver ced_driver =
+{
+ .name = "cedusb",
+ .probe = ced_probe,
+ .disconnect = ced_disconnect,
+ .suspend = ced_suspend,
+ .resume = ced_resume,
+ .pre_reset = ced_pre_reset,
+ .post_reset = ced_post_reset,
+ .id_table = ced_table,
+ .supports_autosuspend = 1,
+};
+
+static int __init usb_skel_init(void)
+{
+ /* register this driver with the USB subsystem */
+ int result = usb_register(&ced_driver);
+ if (result)
+ err("usb_register failed. Error number %d", result);
+
+ return result;
+}
+
+static void __exit usb_skel_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&ced_driver);
+}
+
+module_init(usb_skel_init);
+module_exit(usb_skel_exit);
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/* usb1401.h
+ Header file for the CED 1401 USB device driver for Linux
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ 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.
+*/
+#ifndef __USB1401_H__
+#define __USB1401_H__
+#include "use1401.h"
+#include "ced_ioctl.h"
+
+#ifndef UINT
+#define UINT unsigned int
+#endif
+
+/// Device type codes, but these don't need to be extended - a succession is assumed
+/// These are set for usb from the bcdDevice field (suitably mangled). Future devices
+/// will be added in order of device creation to the list, so the names here are just
+/// to help use remember which device is which. The U14ERR_... values follow the same
+/// pattern for modern devices.
+#define TYPEUNKNOWN -1 // dont know
+#define TYPE1401 0 // standard 1401
+#define TYPEPLUS 1 // 1401 plus
+#define TYPEU1401 2 // u1401
+#define TYPEPOWER 3 // Power1401
+#define TYPEU14012 4 // u1401 mkII
+#define TYPEPOWER2 5 // Power1401 mk II
+#define TYPEMICRO3 6 // Micro1401-3
+#define TYPEPOWER3 7 // Power1401-3
+
+/// Some useful defines of constants. DONT FORGET to change the version in the
+/// resources whenever you change it here!.
+#define DRIVERMAJREV 2 // driver revision level major (match windows)
+#define DRIVERMINREV 0 // driver revision level minor
+
+/// Definitions of the various block transfer command codes
+#define TM_EXTTOHOST 8 // extended tohost
+#define TM_EXTTO1401 9 // extended to1401
+
+/// Definitions of values in usbReqtype. Used in sorting out setup actions
+#define H_TO_D 0x00
+#define D_TO_H 0x80
+#define VENDOR 0x40
+#define DEVREQ 0x00
+#define INTREQ 0x01
+#define ENDREQ 0x02
+
+/// Definition of values in usbRequest, again used to sort out setup
+#define GET_STATUS 0x00
+#define CLEAR_FEATURE 0x01
+#define SET_FEATURE 0x03
+#define SET_ADDRESS 0x05
+#define GET_DESC 0x06
+#define SET_DESC 0x07
+#define GET_CONF 0x08
+#define SET_CONF 0x09
+#define GET_INTERFACE 0x0a
+#define SET_INTERFACE 0x0b
+#define SYNCH_FRAME 0x0c
+
+/// Definitions of the various debug command codes understood by the 1401. These
+/// are used in various vendor-specific commands to achieve the desired effect
+#define DB_GRAB 0x50 /* Grab is a NOP for USB */
+#define DB_FREE 0x51 /* Free is a NOP for the USB */
+#define DB_SETADD 0x52 /* Set debug address (double) */
+#define DB_SELFTEST 0x53 /* Start self test */
+#define DB_SETMASK 0x54 /* Set enable mask (double) */
+#define DB_SETDEF 0x55 /* Set default mask (double) */
+#define DB_PEEK 0x56 /* Peek address, save result */
+#define DB_POKE 0x57 /* Poke address with data (double) */
+#define DB_RAMPD 0x58 /* Ramp data at debug address */
+#define DB_RAMPA 0x59 /* Ramp address bus */
+#define DB_REPEATS 0x5A /* Set repeats for operations (double) */
+#define DB_WIDTH 0x5B /* Set width for operations (byte) */
+#define DB_DATA 0x5C /* Get 4-byte data read by PEEK */
+#define DB_CHARS 0x5D /* Send chars via EP0 control write */
+
+#define CR_CHAR 0x0D /* The carriage return character */
+#define CR_CHAR_80 0x8d /* and with bit 7 set */
+
+/// A structure holding information about a block of memory for use in circular transfers
+typedef struct circBlk
+{
+ volatile UINT dwOffset; /* Offset within area of block start */
+ volatile UINT dwSize; /* Size of the block, in bytes (0 = unused) */
+} CIRCBLK;
+
+/// A structure holding all of the information about a transfer area - an area of
+/// memory set up for use either as a source or destination in DMA transfers.
+typedef struct transarea
+{
+ void* lpvBuff; // User address of xfer area saved for completeness
+ UINT dwBaseOffset; // offset to start of xfer area in first page
+ UINT dwLength; // Length of xfer area, in bytes
+ struct page **pPages; // Points at array of locked down pages
+ int nPages; // number of pages that are locked down
+ bool bUsed; // Is this structure in use?
+ bool bCircular; // Is this area for circular transfers?
+ bool bCircToHost; // Flag for direction of circular transfer
+ bool bEventToHost; // Set event on transfer to host?
+ int iWakeUp; // Set 1 on event, cleared by TestEvent()
+ UINT dwEventSt; // Defines section within xfer area for...
+ UINT dwEventSz; // ...notification by the event SZ is 0 if unset
+ CIRCBLK aBlocks[2]; // Info on a pair of circular blocks
+ wait_queue_head_t wqEvent; // The wait queue for events in this area MUST BE LAST
+} TRANSAREA;
+
+/// The DMADESC structure is used to hold information on the transfer in progress. It
+/// is set up by ReadDMAInfo, using information sent by the 1401 in an escape sequence.
+typedef struct dmadesc
+{
+ unsigned short wTransType; /* transfer type as TM_xxx above */
+ unsigned short wIdent; /* identifier word */
+ unsigned int dwSize; /* bytes to transfer */
+ unsigned int dwOffset; /* offset into transfer area for trans */
+ bool bOutWard; /* true when data is going TO 1401 */
+} DMADESC;
+
+#define INBUF_SZ 256 /* input buffer size */
+#define OUTBUF_SZ 256 /* output buffer size */
+#define STAGED_SZ 0x10000 // size of coherent buffer for staged transfers
+
+/// Structure to hold all of our device specific stuff. We are making this as similar as we
+/// can to the Windows driver to help in our understanding of what is going on.
+typedef struct _DEVICE_EXTENSION
+{
+ char inputBuffer[INBUF_SZ]; /* The two buffers */
+ char outputBuffer[OUTBUF_SZ]; /* accessed by the host functions */
+ volatile unsigned int dwNumInput; /* num of chars in input buffer */
+ volatile unsigned int dwInBuffGet; /* where to get from input buffer */
+ volatile unsigned int dwInBuffPut; /* where to put into input buffer */
+ volatile unsigned int dwNumOutput; /* num of chars in output buffer */
+ volatile unsigned int dwOutBuffGet; /* where to get from output buffer*/
+ volatile unsigned int dwOutBuffPut; /* where to put into output buffer*/
+
+ volatile bool bSendCharsPending; /* Flag to indicate sendchar active */
+ volatile bool bReadCharsPending; /* Flag to indicate a read is primed */
+ char* pCoherCharOut; /* special aligned buffer for chars to 1401 */
+ struct urb* pUrbCharOut; /* urb used for chars to 1401 */
+ char* pCoherCharIn; /* special aligned buffer for chars to host */
+ struct urb* pUrbCharIn; /* urb used for chars to host */
+
+ spinlock_t charOutLock; /* to protect the outputBuffer and outputting */
+ spinlock_t charInLock; /* to protect the inputBuffer and char reads */
+ __u8 bInterval; /* Interrupt end point interval */
+
+ volatile unsigned int dwDMAFlag; /* state of DMA */
+ TRANSAREA rTransDef[MAX_TRANSAREAS];/* transfer area info */
+ volatile DMADESC rDMAInfo; // info on current DMA transfer
+ volatile bool bXFerWaiting; // Flag set if DMA transfer stalled
+ volatile bool bInDrawDown; // Flag that we want to halt transfers
+
+ // Parameters relating to a block read\write that is in progress. Some of these values
+ // are equivalent to values in rDMAInfo. The values here are those in use, while those
+ // in rDMAInfo are those recieved from the 1401 via an escape sequence. If another
+ // escape sequence arrives before the previous xfer ends, rDMAInfo values are updated while these
+ // are used to finish off the current transfer.
+ volatile short StagedId; // The transfer area id for this transfer
+ volatile bool StagedRead; // Flag TRUE for read from 1401, FALSE for write
+ volatile unsigned int StagedLength; // Total length of this transfer
+ volatile unsigned int StagedOffset; // Offset within memory area for transfer start
+ volatile unsigned int StagedDone; // Bytes transferred so far
+ volatile bool bStagedUrbPending; // Flag to indicate active
+ char* pCoherStagedIO; // buffer used for block transfers
+ struct urb* pStagedUrb; // The URB to use
+ spinlock_t stagedLock; // protects ReadWriteMem() and circular buffer stuff
+
+ short s1401Type; // type of 1401 attached
+ short sCurrentState; // current error state
+ bool bIsUSB2; // type of the interface we connect to
+ bool bForceReset; // Flag to make sure we get a real reset
+ __u32 statBuf[2]; // buffer for 1401 state info
+
+ unsigned long ulSelfTestTime; // used to timeout self test
+
+ int nPipes; // Should be 3 or 4 depending on 1401 usb chip
+ int bPipeError[4]; // set non-zero if an error on one of the pipe
+ __u8 epAddr[4]; // addresses of the 3/4 end points
+
+ struct usb_device *udev; // the usb device for this device
+ struct usb_interface *interface; // the interface for this device, NULL if removed
+ struct usb_anchor submitted; // in case we need to retract our submissions
+ struct mutex io_mutex; // synchronize I/O with disconnect, one user-mode caller at a time
+
+ int errors; // the last request tanked
+ int open_count; // count the number of openers
+ spinlock_t err_lock; // lock for errors
+ struct kref kref;
+}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
+#define to_DEVICE_EXTENSION(d) container_of(d, DEVICE_EXTENSION, kref)
+
+/// Definitions of routimes used between compilation object files
+// in usb1401.c
+extern int Allowi(DEVICE_EXTENSION* pdx, bool bInCallback);
+extern int SendChars(DEVICE_EXTENSION* pdx);
+extern void ced_draw_down(DEVICE_EXTENSION *pdx);
+extern int ReadWriteMem(DEVICE_EXTENSION *pdx, bool Read, unsigned short wIdent,
+ unsigned int dwOffs, unsigned int dwLen);
+
+// in ced_ioc.c
+extern int ClearArea(DEVICE_EXTENSION *pdx, int nArea);
+extern int SendString(DEVICE_EXTENSION* pdx, const char __user* pData, unsigned int n);
+extern int SendChar(DEVICE_EXTENSION *pdx, char c);
+extern int Get1401State(DEVICE_EXTENSION* pdx, __u32* state, __u32* error);
+extern int ReadWrite_Cancel(DEVICE_EXTENSION *pdx);
+extern bool Is1401(DEVICE_EXTENSION* pdx);
+extern bool QuickCheck(DEVICE_EXTENSION* pdx, bool bTestBuff, bool bCanReset);
+extern int Reset1401(DEVICE_EXTENSION *pdx);
+extern int GetChar(DEVICE_EXTENSION *pdx);
+extern int GetString(DEVICE_EXTENSION *pdx, char __user* pUser, int n);
+extern int SetTransfer(DEVICE_EXTENSION *pdx, TRANSFERDESC __user *pTD);
+extern int UnsetTransfer(DEVICE_EXTENSION *pdx, int nArea);
+extern int SetEvent(DEVICE_EXTENSION *pdx, TRANSFEREVENT __user*pTE);
+extern int Stat1401(DEVICE_EXTENSION *pdx);
+extern int LineCount(DEVICE_EXTENSION *pdx);
+extern int GetOutBufSpace(DEVICE_EXTENSION *pdx);
+extern int GetTransfer(DEVICE_EXTENSION *pdx, TGET_TX_BLOCK __user *pGTB);
+extern int KillIO1401(DEVICE_EXTENSION *pdx);
+extern int BlkTransState(DEVICE_EXTENSION *pdx);
+extern int StateOf1401(DEVICE_EXTENSION *pdx);
+extern int StartSelfTest(DEVICE_EXTENSION *pdx);
+extern int CheckSelfTest(DEVICE_EXTENSION *pdx, TGET_SELFTEST __user *pGST);
+extern int TypeOf1401(DEVICE_EXTENSION *pdx);
+extern int TransferFlags(DEVICE_EXTENSION *pdx);
+extern int DbgPeek(DEVICE_EXTENSION *pdx, TDBGBLOCK __user* pDB);
+extern int DbgPoke(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgRampData(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgRampAddr(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgGetData(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgStopLoop(DEVICE_EXTENSION *pdx);
+extern int SetCircular(DEVICE_EXTENSION *pdx, TRANSFERDESC __user *pTD);
+extern int GetCircBlock(DEVICE_EXTENSION *pdx, TCIRCBLOCK __user* pCB);
+extern int FreeCircBlock(DEVICE_EXTENSION *pdx, TCIRCBLOCK __user* pCB);
+extern int WaitEvent(DEVICE_EXTENSION *pdx, int nArea, int msTimeOut);
+extern int TestEvent(DEVICE_EXTENSION *pdx, int nArea);
+#endif
--- /dev/null
+/****************************************************************************
+** use1401.h
+** Copyright (C) Cambridge Electronic Design Ltd, 1992-2010
+** Authors: Paul Cox, Tim Bergel, Greg Smith
+** See CVS for revisions.
+**
+** Because the size of a long is different between 32-bit and 64-bit on some
+** systems, we avoid this in this interface.
+****************************************************************************/
+#ifndef __USE1401_H__
+#define __USE1401_H__
+#include "machine.h"
+
+// Some definitions to make things compatible. If you want to use Use1401 directly
+// from a Windows program you should define U14_NOT_DLL, in which case you also
+// MUST make sure that your application startup code calls U14InitLib().
+// DLL_USE1401 is defined when you are building the Use1401 dll, not otherwise.
+#ifdef _IS_WINDOWS_
+#ifndef U14_NOT_DLL
+#ifdef DLL_USE1401
+#define U14API(retType) retType DllExport __stdcall
+#else
+#define U14API(retType) retType DllImport __stdcall
+#endif
+#endif
+
+#define U14ERRBASE -500
+#define U14LONG long
+#endif
+
+#ifdef LINUX
+#define U14ERRBASE -1000
+#define U14LONG int
+#endif
+
+#ifdef _QT
+#ifndef U14_NOT_DLL
+#undef U14API
+#define U14API(retType) retType __declspec(dllimport) __stdcall
+#endif
+#undef U14LONG
+#define U14LONG int
+#endif
+
+#ifndef U14API
+#define U14API(retType) retType
+#endif
+
+#ifndef U14LONG
+#define U14LONG long
+#endif
+
+/// Error codes: We need them here as user space can see them.
+#define U14ERR_NOERROR 0 // no problems
+
+/// Device error codes, but these don't need to be extended - a succession is assumed
+#define U14ERR_STD 4 // standard 1401 connected
+#define U14ERR_U1401 5 // u1401 connected
+#define U14ERR_PLUS 6 // 1401 plus connected
+#define U14ERR_POWER 7 // Power1401 connected
+#define U14ERR_U14012 8 // u1401 mkII connected
+#define U14ERR_POWER2 9
+#define U14ERR_U14013 10
+#define U14ERR_POWER3 11
+
+/// NBNB Error numbers need shifting as some linux error codes start at 512
+#define U14ERR(n) (n+U14ERRBASE)
+#define U14ERR_OFF U14ERR(0) /* 1401 there but switched off */
+#define U14ERR_NC U14ERR(-1) /* 1401 not connected */
+#define U14ERR_ILL U14ERR(-2) /* if present it is ill */
+#define U14ERR_NOIF U14ERR(-3) /* I/F card missing */
+#define U14ERR_TIME U14ERR(-4) /* 1401 failed to come ready */
+#define U14ERR_BADSW U14ERR(-5) /* I/F card bad switches */
+#define U14ERR_PTIME U14ERR(-6) /* 1401plus failed to come ready */
+#define U14ERR_NOINT U14ERR(-7) /* couldn't grab the int vector */
+#define U14ERR_INUSE U14ERR(-8) /* 1401 is already in use */
+#define U14ERR_NODMA U14ERR(-9) /* couldn't get DMA channel */
+#define U14ERR_BADHAND U14ERR(-10) /* handle provided was bad */
+#define U14ERR_BAD1401NUM U14ERR(-11) /* 1401 number provided was bad */
+
+#define U14ERR_NO_SUCH_FN U14ERR(-20) /* no such function */
+#define U14ERR_NO_SUCH_SUBFN U14ERR(-21) /* no such sub function */
+#define U14ERR_NOOUT U14ERR(-22) /* no room in output buffer */
+#define U14ERR_NOIN U14ERR(-23) /* no input in buffer */
+#define U14ERR_STRLEN U14ERR(-24) /* string longer than buffer */
+#define U14ERR_ERR_STRLEN U14ERR(-24) /* string longer than buffer */
+#define U14ERR_LOCKFAIL U14ERR(-25) /* failed to lock memory */
+#define U14ERR_UNLOCKFAIL U14ERR(-26) /* failed to unlock memory */
+#define U14ERR_ALREADYSET U14ERR(-27) /* area already set up */
+#define U14ERR_NOTSET U14ERR(-28) /* area not set up */
+#define U14ERR_BADAREA U14ERR(-29) /* illegal area number */
+#define U14ERR_FAIL U14ERR(-30) /* we failed for some other reason*/
+
+#define U14ERR_NOFILE U14ERR(-40) /* command file not found */
+#define U14ERR_READERR U14ERR(-41) /* error reading command file */
+#define U14ERR_UNKNOWN U14ERR(-42) /* unknown command */
+#define U14ERR_HOSTSPACE U14ERR(-43) /* not enough host space to load */
+#define U14ERR_LOCKERR U14ERR(-44) /* could not lock resource/command*/
+#define U14ERR_CLOADERR U14ERR(-45) /* CLOAD command failed */
+
+#define U14ERR_TOXXXERR U14ERR(-60) /* tohost/1401 failed */
+#define U14ERR_NO386ENH U14ERR(-80) /* not 386 enhanced mode */
+#define U14ERR_NO1401DRIV U14ERR(-81) /* no device driver */
+#define U14ERR_DRIVTOOOLD U14ERR(-82) /* device driver too old */
+
+#define U14ERR_TIMEOUT U14ERR(-90) /* timeout occurred */
+
+#define U14ERR_BUFF_SMALL U14ERR(-100) /* buffer for getstring too small */
+#define U14ERR_CBALREADY U14ERR(-101) /* there is already a callback */
+#define U14ERR_BADDEREG U14ERR(-102) /* bad parameter to deregcallback */
+#define U14ERR_NOMEMORY U14ERR(-103) /* no memory for allocation */
+
+#define U14ERR_DRIVCOMMS U14ERR(-110) /* failed talking to driver */
+#define U14ERR_OUTOFMEMORY U14ERR(-111) /* needed memory and couldnt get it*/
+
+/// 1401 type codes.
+#define U14TYPE1401 0 /* standard 1401 */
+#define U14TYPEPLUS 1 /* 1401 plus */
+#define U14TYPEU1401 2 /* u1401 */
+#define U14TYPEPOWER 3 /* power1401 */
+#define U14TYPEU14012 4 /* u1401 mk II */
+#define U14TYPEPOWER2 5 /* power1401 mk II */
+#define U14TYPEU14013 6 /* u1401-3 */
+#define U14TYPEPOWER3 7 /* power1401-3 */
+#define U14TYPEUNKNOWN -1 /* dont know */
+
+/// Transfer flags to allow driver capabilities to be interrogated
+
+/// Constants for transfer flags
+#define U14TF_USEDMA 1 /* Transfer flag for use DMA */
+#define U14TF_MULTIA 2 /* Transfer flag for multi areas */
+#define U14TF_FIFO 4 /* for FIFO interface card */
+#define U14TF_USB2 8 /* for USB2 interface and 1401 */
+#define U14TF_NOTIFY 16 /* for event notifications */
+#define U14TF_SHORT 32 /* for PCI can short cycle */
+#define U14TF_PCI2 64 /* for new PCI card 1401-70 */
+#define U14TF_CIRCTH 128 /* Circular-mode to host */
+#define U14TF_DIAG 256 /* Diagnostics/debug functions */
+#define U14TF_CIRC14 512 /* Circular-mode to 1401 */
+
+/// Definitions of element sizes for DMA transfers - to allow byte-swapping
+#define ESZBYTES 0 /* BYTE element size value */
+#define ESZWORDS 1 /* WORD element size value */
+#define ESZLONGS 2 /* long element size value */
+#define ESZUNKNOWN 0 /* unknown element size value */
+
+/// These define required access types for the debug/diagnostics function
+#define BYTE_SIZE 1 /* 8-bit access */
+#define WORD_SIZE 2 /* 16-bit access */
+#define LONG_SIZE 3 /* 32-bit access */
+
+/// Stuff used by U14_GetTransfer
+#define GET_TX_MAXENTRIES 257 /* (max length / page size + 1) */
+
+#ifdef _IS_WINDOWS_
+#pragma pack(1)
+
+typedef struct /* used for U14_GetTransfer results */
+{ /* Info on a single mapped block */
+ U14LONG physical;
+ U14LONG size;
+} TXENTRY;
+
+typedef struct TGetTxBlock /* used for U14_GetTransfer results */
+{ /* matches structure in VXD */
+ U14LONG size;
+ U14LONG linear;
+ short seg;
+ short reserved;
+ short avail; /* number of available entries */
+ short used; /* number of used entries */
+ TXENTRY entries[GET_TX_MAXENTRIES]; /* Array of mapped block info */
+} TGET_TX_BLOCK;
+
+typedef TGET_TX_BLOCK *LPGET_TX_BLOCK;
+
+#pragma pack()
+#endif
+
+#ifdef LINUX
+typedef struct /* used for U14_GetTransfer results */
+{ /* Info on a single mapped block */
+ long long physical;
+ long size;
+} TXENTRY;
+
+typedef struct TGetTxBlock /* used for U14_GetTransfer results */
+{ /* matches structure in VXD */
+ long long linear; /* linear address */
+ long size; /* total size of the mapped area, holds id when called */
+ short seg; /* segment of the address for Win16 */
+ short reserved;
+ short avail; /* number of available entries */
+ short used; /* number of used entries */
+ TXENTRY entries[GET_TX_MAXENTRIES]; /* Array of mapped block info */
+} TGET_TX_BLOCK;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+U14API(int) U14WhenToTimeOut(short hand); // when to timeout in ms
+U14API(short) U14PassedTime(int iTime); // non-zero if iTime passed
+
+U14API(short) U14LastErrCode(short hand);
+
+U14API(short) U14Open1401(short n1401);
+U14API(short) U14Close1401(short hand);
+U14API(short) U14Reset1401(short hand);
+U14API(short) U14ForceReset(short hand);
+U14API(short) U14TypeOf1401(short hand);
+U14API(short) U14NameOf1401(short hand, char* pBuf, WORD wMax);
+
+U14API(short) U14Stat1401(short hand);
+U14API(short) U14CharCount(short hand);
+U14API(short) U14LineCount(short hand);
+
+U14API(short) U14SendString(short hand, const char* pString);
+U14API(short) U14GetString(short hand, char* pBuffer, WORD wMaxLen);
+U14API(short) U14SendChar(short hand, char cChar);
+U14API(short) U14GetChar(short hand, char* pcChar);
+
+U14API(short) U14LdCmd(short hand, const char* command);
+U14API(DWORD) U14Ld(short hand, const char* vl, const char* str);
+
+U14API(short) U14SetTransArea(short hand, WORD wArea, void *pvBuff,
+ DWORD dwLength, short eSz);
+U14API(short) U14UnSetTransfer(short hand, WORD wArea);
+U14API(short) U14SetTransferEvent(short hand, WORD wArea, BOOL bEvent,
+ BOOL bToHost, DWORD dwStart, DWORD dwLength);
+U14API(int) U14TestTransferEvent(short hand, WORD wArea);
+U14API(int) U14WaitTransferEvent(short hand, WORD wArea, int msTimeOut);
+U14API(short) U14GetTransfer(short hand, TGET_TX_BLOCK *pTransBlock);
+
+U14API(short) U14ToHost(short hand, char* pAddrHost,DWORD dwSize,DWORD dw1401,
+ short eSz);
+U14API(short) U14To1401(short hand, const char* pAddrHost,DWORD dwSize,DWORD dw1401,
+ short eSz);
+
+U14API(short) U14SetCircular(short hand, WORD wArea, BOOL bToHost, void *pvBuff,
+ DWORD dwLength);
+
+U14API(int) U14GetCircBlk(short hand, WORD wArea, DWORD *pdwOffs);
+U14API(int) U14FreeCircBlk(short hand, WORD wArea, DWORD dwOffs, DWORD dwSize,
+ DWORD *pdwOffs);
+
+U14API(short) U14StrToLongs(const char* pszBuff, U14LONG *palNums, short sMaxLongs);
+U14API(short) U14LongsFrom1401(short hand, U14LONG *palBuff, short sMaxLongs);
+
+U14API(void) U14SetTimeout(short hand, int lTimeout);
+U14API(int) U14GetTimeout(short hand);
+U14API(short) U14OutBufSpace(short hand);
+U14API(int) U14BaseAddr1401(short hand);
+U14API(int) U14DriverVersion(short hand);
+U14API(int) U14DriverType(short hand);
+U14API(short) U14DriverName(short hand, char* pBuf, WORD wMax);
+U14API(short) U14GetUserMemorySize(short hand, DWORD *pMemorySize);
+U14API(short) U14KillIO1401(short hand);
+
+U14API(short) U14BlkTransState(short hand);
+U14API(short) U14StateOf1401(short hand);
+
+U14API(short) U14Grab1401(short hand);
+U14API(short) U14Free1401(short hand);
+U14API(short) U14Peek1401(short hand, DWORD dwAddr, int nSize, int nRepeats);
+U14API(short) U14Poke1401(short hand, DWORD dwAddr, DWORD dwValue, int nSize, int nRepeats);
+U14API(short) U14Ramp1401(short hand, DWORD dwAddr, DWORD dwDef, DWORD dwEnable, int nSize, int nRepeats);
+U14API(short) U14RampAddr(short hand, DWORD dwDef, DWORD dwEnable, int nSize, int nRepeats);
+U14API(short) U14StopDebugLoop(short hand);
+U14API(short) U14GetDebugData(short hand, U14LONG *plValue);
+
+U14API(short) U14StartSelfTest(short hand);
+U14API(short) U14CheckSelfTest(short hand, U14LONG *pData);
+U14API(short) U14TransferFlags(short hand);
+U14API(void) U14GetErrorString(short nErr, char* pStr, WORD wMax);
+U14API(int) U14MonitorRev(short hand);
+U14API(void) U14CloseAll(void);
+
+U14API(short) U14WorkingSet(DWORD dwMinKb, DWORD dwMaxKb);
+U14API(int) U14InitLib(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* End of ifndef __USE1401_H__ */
--- /dev/null
+/* use14_ioc.h
+** definitions of use1401 module stuff that is shared between use1401 and the driver.
+** Copyright (C) Cambridge Electronic Design Limited 2010
+** Author Greg P Smith
+************************************************************************************/
+#ifndef __USE14_IOC_H__
+#define __USE14_IOC_H__
+
+#define MAX_TRANSAREAS 8 /* The number of transfer areas supported by driver */
+
+#define i386
+#include "winioctl.h" /* needed so we can access driver */
+
+/*
+** Defines for IOCTL functions to ask driver to perform. These must be matched
+** in both use1401 and in the driver. The IOCTL code contains a command
+** identifier, plus other information about the device, the type of access
+** with which the file must have been opened, and the type of buffering.
+** The IOCTL function codes from 0x80 to 0xFF are for developer use.
+*/
+#define FILE_DEVICE_CED1401 0x8001
+#define FNNUMBASE 0x800
+
+#define U14_OPEN1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_CLOSE1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+1, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SENDSTRING CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+2, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_RESET1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+3, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETCHAR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+4, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SENDCHAR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+5, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STAT1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+6, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_LINECOUNT CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+7, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETSTRING CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+8, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_REGCALLBACK CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+9, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETMONITORBUF CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+10, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SETTRANSFER CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+11, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_UNSETTRANSFER CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+12, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SETTRANSEVENT CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+13, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETOUTBUFSPACE CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+14, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETBASEADDRESS CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+15, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETDRIVERREVISION CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+16, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETTRANSFER CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+17, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_KILLIO1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+18, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_BLKTRANSSTATE CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+19, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_BYTECOUNT CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+20, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_ZEROBLOCKCOUNT CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+21, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STOPCIRCULAR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+22, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STATEOF1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+23, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_REGISTERS1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+24, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GRAB1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+25, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_FREE1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+26, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STEP1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+27, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SET1401REGISTERS CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+28, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STEPTILL1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+29, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SETORIN CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+30, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STARTSELFTEST CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+31, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_CHECKSELFTEST CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+32, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_TYPEOF1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+33, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_TRANSFERFLAGS CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+34, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGPEEK CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+35, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGPOKE CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+36, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGRAMPDATA CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+37, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGRAMPADDR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+38, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGGETDATA CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+39, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGSTOPLOOP CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+40, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_FULLRESET CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+41, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SETCIRCULAR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+42, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETCIRCBLK CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+43, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_FREECIRCBLK CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+44, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+//--------------- Structures that are shared with the driver -------------
+#pragma pack(1)
+
+typedef struct /* used for get/set standard 1401 registers */
+{
+ short sPC;
+ char A;
+ char X;
+ char Y;
+ char stat;
+ char rubbish;
+} T1401REGISTERS;
+
+typedef union /* to communicate with 1401 driver status & control funcs */
+{
+ char chrs[22];
+ short ints[11];
+ long longs[5];
+ T1401REGISTERS registers;
+} TCSBLOCK;
+
+typedef TCSBLOCK* LPTCSBLOCK;
+
+typedef struct paramBlk
+{
+ short sState;
+ TCSBLOCK csBlock;
+} PARAMBLK;
+
+typedef PARAMBLK* PPARAMBLK;
+
+typedef struct TransferDesc /* Structure and type for SetTransArea */
+{
+ WORD wArea; /* number of transfer area to set up */
+ void FAR * lpvBuff; /* address of transfer area */
+ DWORD dwLength; /* length of area to set up */
+ short eSize; /* size to move (for swapping on MAC) */
+} TRANSFERDESC;
+
+typedef TRANSFERDESC FAR * LPTRANSFERDESC;
+
+/* This is the structure used to set up a transfer area */
+typedef struct VXTransferDesc /* use1401.c and use1432x.x use only */
+{
+ WORD wArea; /* number of transfer area to set up */
+ WORD wAddrSel; /* 16 bit selector for area */
+ DWORD dwAddrOfs; /* 32 bit offset for area start */
+ DWORD dwLength; /* length of area to set up */
+} VXTRANSFERDESC;
+
+#pragma pack()
+
+#endif
\ No newline at end of file
--- /dev/null
+/****************************************************************************
+** use1401.c
+** Copyright (C) Cambridge Electronic Design Ltd, 1992-2010
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the GNU General Public License
+** as published by the Free Software Foundation; either version 2
+** of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**
+** Contact CED: Cambridge Electronic Design Limited, Science Park, Milton Road
+** Cambridge, CB6 0FE.
+** www.ced.co.uk
+** greg@ced.co.uk
+**
+** Title: USE1401.C
+** Version: 4.00
+** Author: Paul Cox, Tim Bergel, Greg Smith
+**
+** The code was vigorously pruned in DEC 2010 to remove the macintosh options
+** and to get rid of the 16-bit support. It has also been aligned with the
+** Linux version. See CVS for revisions. This will work for Win 9x onwards.
+****************************************************************************
+**
+** Notes on Windows interface to driver
+** ************************************
+**
+** Under Windows 9x and NT, Use1401 uses DeviceIoControl to get access to
+** the 1401 driver. This has parameters for the device handle, the function
+** code, an input pointer and byte count, an output pointer and byte count
+** and a pointer to a DWORD to hold the output byte count. Note that input
+** and output are from the point-of-view of the driver, so the output stuff
+** is used to read values from the 1401, not send to the 1401. The use of
+** these parameters varies with the function in use and the operating
+** system; there are five separate DIOC calls SendString, GetString and
+** SetTransferArea all have their own specialised calls, the rest use the
+** Status1401 or Control1401 functions.
+**
+** There are two basic styles of DIOC call used, one for Win9x VxD drivers
+** and one for NT Kernel-mode and WDM drivers (see below for tables showing
+** the different parameters used. The array bUseNTDIOC[] selects between
+** these two calling styles.
+**
+** Function codes
+** In Win3.x, simple function codes from 0 to 40 were used, shifted left 8
+** bits with a sub-function code in the lower 8 bits. These were also used
+** in the Windows 95 driver, though we had to add 1 to the code value to
+** avoid problems (Open from CreateFile is zero), and the sub-function code
+** is now unused. We found that this gave some problems with Windows 98
+** as the function code values are reserved by microsoft, so we switched to
+** using the NT function codes instead. The NT codes are generated using the
+** CTL_CODE macro, essentially this gives 0x80012000 | (func << 2), where
+** func is the original 0 to 34 value. The driver will handle both types of
+** code and Use1432 only uses the NT codes if it knows the driver is new
+** enough. The array bUseNTCodes[] holds flags on the type of codes required.
+** GPS/TDB Dec 2010: we removed the bUseNTCodes array as this is always true
+** as we no longer support ancient versions.
+**
+** The CreateFile and CloseFile function calls are also handled
+** by DIOC, using the special function codes 0 and -1 respectively.
+**
+** Input pointer and buffer size
+** These are intended for data sent to the device driver. In nearly all cases
+** they are unused in calls to the Win95 driver, the NT driver uses them
+** for all information sent to the driver. The table below shows the pointer
+** and byte count used for the various calls:
+**
+** Win 95 Win NT
+** SendString NULL, 0 pStr, nStr
+** GetString NULL, 0 NULL, 0
+** SetTransferArea pBuf, nBuf (unused?) pDesc, nDesc
+** GetTransfer NULL, 0 NULL, 0
+** Status1401 NULL, 0 NULL, 0
+** Control1401 NULL, 0 pBlk, nBlk
+**
+** pStr and nStr are pointers to a char buffer and the buffer length for
+** string I/O, note that these are temporary buffers owned by the DLL, not
+** application memory, pBuf and nBuf are the transfer area buffer (I think
+** these are unused), pDesc and nDesc are the TRANSFERDESC structure, pBlk
+** and nBlk are the TCSBLOCK structure.
+**
+**
+** Output pointer and buffer size
+** These are intended for data read from the device driver. These are used
+** for almost all information sent to the Win95 driver, the NT driver uses
+** them for information read from the driver, chiefly the error code. The
+** table below shows the pointer and byte count used for the various calls:
+**
+** Win 95 Win NT
+** SendString pStr, nStr pPar, nPar
+** GetString pStr, nStr+2 pStr, nStr+2
+** SetTransferArea pDesc, nDesc pPar, nPar
+** GetTransfer pGet, nGet pGet, nGet
+** Status1401 pBlk, nBlk pPar, nPar
+** Control1401 pBlk, nBlk pPar, nPar
+**
+** pStr and nStr are pointers to a char buffer and the buffer length for
+** string I/O, the +2 for GetString refers to two spare bytes at the start
+** used to hold the string length and returning an error code for NT. Note
+** again that these are (and must be) DLL-owned temporary buffers. pPar
+** and nPar are a PARAM structure used in NT (it holds an error code and a
+** TCSBLOCK structure). pDesc and nDesc are the VXTRANSFERDESC structure,
+** pBlk and nBlk are the TCSBLOCK structure. pGet and nGet indicate the
+** TGET_TX_BLOCK structure used for GetTransfer.
+**
+**
+** The output byte count
+** Both drivers return the output buffer size here, regardless of the actual
+** bytes output. This is used to check that we did get through to the driver.
+**
+** Multiple 1401s
+** **************
+**
+** We have code that tries to support the use of multiple 1401s, but there
+** are problems: The lDriverVersion and lDriverType variables are global, not
+** per-1401 (a particular problem as the U14 functions that use them don't
+** have a hand parameter). In addition, the mechansim for finding a free
+** 1401 depends upon the 1401 device driver open operation failing if it's
+** already in use, which doesn't always happen, particularly with the VxDs.
+** The code in TryToOpen tries to fix this by relying on TYPEOF1401 to detect
+** the 1401-in-use state - the VxDs contain special code to help this. This is
+** working OK but multiple 1401 support works better with the Win2000 drivers.
+**
+** USB driver
+** **********
+**
+** The USB driver, which runs on both Win98 and NT2000, uses the NT-style
+** calling convention, both for the DIOC codes and the DIOC parameters. The
+** TryToOpen function has been altered to look for an NT driver first in
+** the appropriate circumstances, and to set the driver DIOC flags up in
+** the correct state.
+**
+** Adding a new 1401 type - now almost nothing to do
+** *************************************************
+**
+** The 1401 types are defined by a set of U14TYPExxxx codes in USE1401.H.
+** You should add a new one of these to keep things tidy for applications.
+**
+** DRIVERET_MAX (below) specifies the maximum allowed type code from the
+** 1401 driver; I have set this high to accomodate as yet undesigned 1401
+** types. Similarly, as long as the command file names follow the ARM,
+** ARN, ARO sequence, these are calculated by the ExtForType function, so
+** you don't need to do anything here either.
+**
+** Version number
+** **************
+** The new U14InitLib() function returns 0 if the OS is incapable of use,
+** otherwise is returns the version of the USE1401 library. This is done
+** in three parts: Major(31-24).Minor(23-16).Revision.(15-0) (brackets are
+** the bits used). The Major number starts at 2 for the first revision with
+** the U14InitLib() function. Changes to the Major version means that we
+** have broken backwards compatibility. Minor number changes mean that we
+** have added new functionality that does not break backwards compatibility.
+** we starts at 0. Revision changes mean we have fixed something. Each index
+** returns to 0 when a higer one changes.
+*/
+#define U14LIB_MAJOR 4
+#define U14LIB_MINOR 0
+#define U14LIB_REVISION 0
+#define U14LIB_VERSION ((U14LIB_MAJOR<<24) | (U14LIB_MINOR<<16) | U14LIB_REVISION)
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "USE1401.H"
+
+#ifdef _IS_WINDOWS_
+#include <io.h>
+#include <windows.h>
+#pragma warning(disable: 4100) /* Disable "Unused formal parameter" warning */
+#include <assert.h>
+#include "process.h"
+
+
+#define sprintf wsprintf
+#define PATHSEP '\\'
+#define PATHSEPSTR "\\"
+#define DEFCMDPATH "\\1401\\" // default command path if all else fails
+#define MINDRIVERMAJREV 1 // minimum driver revision level we need
+#define __packed // does nothing in Windows
+
+#include "use14_ioc.h" // links to device driver stuff
+#endif
+
+#ifdef LINUX
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sched.h>
+#include <libgen.h>
+#define PATHSEP '/'
+#define PATHSEPSTR "/"
+#define DEFCMDPATH "/var/1401/" // default command path if all else fails
+#define MINDRIVERMAJREV 2 // minimum driver revision level we need
+
+#include "ced_ioctl.h" // links to device driver stuff
+#endif
+
+#define MAX1401 8 // The number of 1401s that can be supported
+
+/*
+** These are the 1401 type codes returned by the driver, they are a slightly
+** odd sequence & start for reasons of compatability with the DOS driver.
+** The maximum code value is the upper limit of 1401 device types.
+*/
+#define DRIVRET_STD 4 // Codes for 1401 types matching driver values
+#define DRIVRET_U1401 5 // This table does not need extending, as
+#define DRIVRET_PLUS 6 // we can calculate values now.
+#define DRIVRET_POWER 7 // but we need all of these values still
+#define DRIVRET_MAX 26 // Maximum tolerated code - future designs
+
+/*
+** These variables store data that will be used to generate the last
+** error string. For now, a string will hold the 1401 command file name.
+*/
+static char szLastName[20]; // additional text information
+
+/*
+** Information stored per handle. NBNB, driverType and DriverVersion used to be
+** only stored once for all handles... i.e. nonsensical. This change means that
+** three U14...() calls now include handles that were previously void. We have
+** set a constructor and a destructor call for the library (see the end) to
+** initialise important structures, or call use1401_load().
+*/
+static short asDriverType[MAX1401] = {0};
+static int lLastDriverVersion = U14ERR_NO1401DRIV;
+static int lLastDriverType = U14TYPEUNKNOWN;
+static int alDriverVersion[MAX1401]; // version/type of each driver
+static int alTimeOutPeriod[MAX1401]; // timeout time in milliseconds
+static short asLastRetCode[MAX1401]; // last code from a fn call
+static short asType1401[MAX1401] = {0}; // The type of the 1401
+static BOOL abGrabbed[MAX1401] = {0}; // Flag for grabbed, set true by grab1401
+static int iAttached = 0; // counts process attaches so can let go
+
+#ifdef _IS_WINDOWS_
+/****************************************************************************
+** Windows NT Specific Variables and internal types
+****************************************************************************/
+static HANDLE aHand1401[MAX1401] = {0}; // handles for 1401s
+static HANDLE aXferEvent[MAX1401] = {0}; // transfer events for the 1401s
+static LPVOID apAreas[MAX1401][MAX_TRANSAREAS]; // Locked areas
+static DWORD auAreas[MAX1401][MAX_TRANSAREAS]; // Size of locked areas
+static BOOL bWindows9x = FALSE; // if we are Windows 95 or better
+#ifdef _WIN64
+#define USE_NT_DIOC(ind) TRUE
+#else
+static BOOL abUseNTDIOC[MAX1401]; // Use NT-style DIOC parameters */
+#define USE_NT_DIOC(ind) abUseNTDIOC[ind]
+#endif
+
+#endif
+
+#ifdef LINUX
+static int aHand1401[MAX1401] = {0}; // handles for 1401s
+#define INVALID_HANDLE_VALUE 0 // to avoid code differences
+#endif
+
+
+/*
+** The CmdHead relates to backwards compatibility with ancient Microsoft (and Sperry!)
+** versions of BASIC, where this header was needed so we could load a command into
+** memory.
+*/
+#pragma pack(1) // pack our structure
+typedef struct CmdHead // defines header block on command
+{ // for PC commands
+ char acBasic[5]; // BASIC information - needed to align things
+ WORD wBasicSz; // size as seen by BASIC
+ WORD wCmdSize; // size of the following info
+} __packed CMDHEAD;
+#pragma pack() // back to normal
+
+/*
+** The rest of the header looks like this...
+** int iRelPnt; relocation pointer... actual start
+** char acName[8]; string holding the command name
+** BYTE bMonRev; monitor revision level
+** BYTE bCmdRev; command revision level
+*/
+
+typedef CMDHEAD *LPCMDHEAD; // pointer to a command header
+
+#define MAXSTRLEN 255 // maximum string length we use
+#define TOHOST FALSE
+#define TO1401 TRUE
+
+static short CheckHandle(short h)
+{
+ if ((h < 0) || (h >= MAX1401)) // must be legal range...
+ return U14ERR_BADHAND;
+ if (aHand1401[h] <= 0) // must be open
+ return U14ERR_BADHAND;
+ return U14ERR_NOERROR;
+}
+
+#ifdef _IS_WINDOWS_
+/****************************************************************************
+** U14Status1401 Used for functions which do not pass any data in but
+** get data back
+****************************************************************************/
+static short U14Status1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
+{
+ DWORD dwBytes = 0;
+
+ if ((sHand < 0) || (sHand >= MAX1401)) /* Check parameters */
+ return U14ERR_BADHAND;
+#ifndef _WIN64
+ if (!USE_NT_DIOC(sHand))
+ { /* Windows 9x DIOC methods? */
+ if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk,sizeof(TCSBLOCK),&dwBytes,NULL))
+ return (short)((dwBytes>=sizeof(TCSBLOCK)) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
+ else
+ return (short)GetLastError();
+ }
+ else
+#endif
+ { /* Windows NT or USB driver */
+ PARAMBLK rWork;
+ rWork.sState = U14ERR_DRIVCOMMS;
+ if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, &rWork,sizeof(PARAMBLK),&dwBytes,NULL) &&
+ (dwBytes >= sizeof(PARAMBLK)))
+ {
+ *pBlk = rWork.csBlock;
+ return rWork.sState;
+ }
+ }
+
+ return U14ERR_DRIVCOMMS;
+}
+
+/****************************************************************************
+** U14Control1401 Used for functions which pass data in and only expect
+** an error code back
+****************************************************************************/
+static short U14Control1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
+{
+ DWORD dwBytes = 0;
+
+ if ((sHand < 0) || (sHand >= MAX1401)) /* Check parameters */
+ return U14ERR_BADHAND;
+
+#ifndef _WIN64
+ if (!USE_NT_DIOC(sHand))
+ { /* Windows 9x DIOC methods */
+ if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk, sizeof(TCSBLOCK), &dwBytes, NULL))
+ return (short)(dwBytes >= sizeof(TCSBLOCK) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
+ else
+ return (short)GetLastError();
+ }
+ else
+#endif
+ { /* Windows NT or later */
+ PARAMBLK rWork;
+ rWork.sState = U14ERR_DRIVCOMMS;
+ if (DeviceIoControl(aHand1401[sHand], lCode, pBlk, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
+ (dwBytes >= sizeof(PARAMBLK)))
+ return rWork.sState;
+ }
+
+ return U14ERR_DRIVCOMMS;
+}
+#endif
+
+/****************************************************************************
+** SafeTickCount
+** Gets time in approximately units of a millisecond.
+*****************************************************************************/
+static long SafeTickCount()
+{
+#ifdef _IS_WINDOWS_
+ return GetTickCount();
+#endif
+#ifdef LINUX
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec*1000 + tv.tv_usec/1000);
+#endif
+}
+
+/****************************************************************************
+** A utility routine to get the command file extension for a given type
+** of 1401. We assume the type code is vaguely legal.
+****************************************************************************/
+static int ExtForType(short sType, char* szExt)
+{
+ szExt[0] = 0; /* Default return is a blank string */
+ switch (sType)
+ {
+ case U14TYPE1401: strcpy(szExt, ".CMD"); break; // Standard 1401
+ case U14TYPEPLUS: strcpy(szExt, ".GXC"); break; // 1401 plus
+ default: // All others are in a predictable sequence
+ strcpy(szExt, ".ARM");
+ szExt[3] = (char)('M' + sType - U14TYPEU1401);
+ if (szExt[3] > 'Z') // Wrap round to ARA after ARZ
+ szExt[3] = (char)(szExt[3] - 26);
+ }
+ return 0;
+}
+
+/****************************************************************************
+** U14WhenToTimeOut
+** Returns the time to time out in time units suitable for the machine
+** we are running on ie millsecs for pc/linux, or Mac/
+****************************************************************************/
+U14API(int) U14WhenToTimeOut(short hand)
+{
+ int iNow = SafeTickCount();
+ if ((hand >= 0) && (hand < MAX1401))
+ iNow += alTimeOutPeriod[hand];
+ return iNow;
+}
+
+/****************************************************************************
+** U14PassedTime
+** Returns non zero if the timed passed in has been passed 0 if not
+****************************************************************************/
+U14API(short) U14PassedTime(int lCheckTime)
+{
+ return (short)((SafeTickCount()-lCheckTime) > 0);
+}
+
+/****************************************************************************
+** TranslateString
+** Tidies up string that U14GetString returns. Converts all the commas in a
+** string to spaces. Removes terminating CR character. May do more in future.
+****************************************************************************/
+static void TranslateString(char* pStr)
+{
+ int i = 0;
+ while (pStr[i])
+ {
+ if (pStr[i] == ',')
+ pStr[i] = ' '; /* convert comma to space */
+ ++i;
+ }
+
+ if ((i > 0) && (pStr[i-1] == '\n')) /* kill terminating LF */
+ pStr[i-1] = (char)0;
+}
+
+/****************************************************************************
+** U14StrToLongs
+** Converts a string to an array of longs and returns the number of values
+****************************************************************************/
+U14API(short) U14StrToLongs(const char* pszBuff, U14LONG *palNums, short sMaxLongs)
+{
+ WORD wChInd = 0; // index into source
+ short sLgInd = 0; // index into result longs
+
+ while (pszBuff[wChInd] && // until we get to end of string...
+ (sLgInd < sMaxLongs)) // ...or filled the buffer
+ {
+ // Why not use a C Library converter?
+ switch (pszBuff[wChInd])
+ {
+ case '-':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ BOOL bDone = FALSE; // true at end of number
+ int iSign = 1; // sign of number
+ long lValue = 0;
+
+ while ((!bDone) && pszBuff[wChInd])
+ {
+ switch (pszBuff[wChInd])
+ {
+ case '-':
+ iSign = -1; // swap sign
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ lValue *= 10; // move to next digit base 10
+ lValue += ((int)pszBuff[wChInd]-(int)'0');
+ break;
+
+ default: // end of number
+ bDone = TRUE;
+ break;
+ }
+ wChInd++; // move onto next character
+ }
+ palNums[sLgInd] = lValue * iSign;
+ sLgInd++;
+ }
+ break;
+
+ default:
+ wChInd++; // look at next char
+ break;
+ }
+ }
+ return (sLgInd);
+}
+
+
+/****************************************************************************
+** U14LongsFrom1401
+** Gets the next waiting line from the 1401 and converts it longs
+** Returns the number of numbers read or an error.
+****************************************************************************/
+U14API(short) U14LongsFrom1401(short hand, U14LONG *palBuff, short sMaxLongs)
+{
+ char szWork[MAXSTRLEN];
+ short sResult = U14GetString(hand, szWork, MAXSTRLEN);/* get reply from 1401 */
+ if (sResult == U14ERR_NOERROR) /* if no error convert */
+ sResult = U14StrToLongs(szWork, palBuff, sMaxLongs);
+ return sResult;
+}
+
+/****************************************************************************
+** U14CheckErr
+** Sends the ERR command to the 1401 and gets the result. Returns 0, a
+** negative error code, or the first error value.
+****************************************************************************/
+U14API(short) U14CheckErr(short hand)
+{
+ short sResult = U14SendString(hand, ";ERR;");
+ if (sResult == U14ERR_NOERROR)
+ {
+ U14LONG er[3];
+ sResult = U14LongsFrom1401(hand, er, 3);
+ if (sResult > 0)
+ {
+ sResult = (short)er[0]; /* Either zero or an error value */
+#ifdef _DEBUG
+ if (er[0] != 0)
+ {
+ char szMsg[50];
+ sprintf(szMsg, "U14CheckErr returned %d,%d\n", er[0], er[1]);
+ OutputDebugString(szMsg);
+ }
+#endif
+ }
+ else
+ {
+ if (sResult == 0)
+ sResult = U14ERR_TIMEOUT; /* No numbers equals timeout */
+ }
+ }
+
+ return sResult;
+}
+
+/****************************************************************************
+** U14LastErrCode
+** Returns the last code from the driver. This is for Windows where all calls
+** go through the Control and Status routines, so we can save any error.
+****************************************************************************/
+U14API(short) U14LastErrCode(short hand)
+{
+ if ((hand < 0) || (hand >= MAX1401))
+ return U14ERR_BADHAND;
+ return asLastRetCode[hand];
+}
+
+/****************************************************************************
+** U14SetTimeout
+** Set the timeout period for 1401 comms in milliseconds
+****************************************************************************/
+U14API(void) U14SetTimeout(short hand, int lTimeOut)
+{
+ if ((hand < 0) || (hand >= MAX1401))
+ return;
+ alTimeOutPeriod[hand] = lTimeOut;
+}
+
+/****************************************************************************
+** U14GetTimeout
+** Get the timeout period for 1401 comms in milliseconds
+****************************************************************************/
+U14API(int) U14GetTimeout(short hand)
+{
+ if ((hand < 0) || (hand >= MAX1401))
+ return U14ERR_BADHAND;
+ return alTimeOutPeriod[hand];
+}
+
+/****************************************************************************
+** U14OutBufSpace
+** Return the space in the output buffer, or an error.
+****************************************************************************/
+U14API(short) U14OutBufSpace(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_GETOUTBUFSPACE,&csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = csBlock.ints[0];
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_GetOutBufSpace(aHand1401[hand]) : sErr;
+#endif
+}
+
+
+/****************************************************************************
+** U14BaseAddr1401
+** Returns the 1401 base address or an error code. Meaningless nowadays
+****************************************************************************/
+U14API(int) U14BaseAddr1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ int iError = U14Status1401(hand, U14_GETBASEADDRESS,&csBlock);
+ if (iError == U14ERR_NOERROR)
+ iError = csBlock.longs[0];
+ return iError;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_GetBaseAddress(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14StateOf1401
+** Return error state, either NOERROR or a negative code.
+****************************************************************************/
+U14API(short) U14StateOf1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_STATEOF1401, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ {
+ sErr = csBlock.ints[0]; // returned 1401 state
+ if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
+ sErr = U14ERR_NOERROR;
+ }
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ sErr = (short)CED_StateOf1401(aHand1401[hand]);
+ if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
+ sErr = U14ERR_NOERROR;
+ }
+#endif
+ return sErr;
+}
+
+/****************************************************************************
+** U14DriverVersion
+** Returns the driver version. Hi word is major revision, low word is minor.
+** If you pass in a silly handle (like -1), we return the version of the last
+** driver we know of (to cope with PCI and no 1401 attached).
+****************************************************************************/
+U14API(int) U14DriverVersion(short hand)
+{
+ return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverVersion : alDriverVersion[hand];
+}
+
+/****************************************************************************
+** U14DriverType
+** Returns the driver type. The type, 0=ISA/NU-Bus, 1=PCI, 2=USB, 3=HSS
+** If you pass in a silly handle (like -1), we return the type of the last
+** driver we know of (to cope with PCI and no 1401 attached).
+****************************************************************************/
+U14API(int) U14DriverType(short hand)
+{
+ return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverType : asDriverType[hand];
+}
+
+/****************************************************************************
+** U14DriverName
+** Returns the driver type as 3 character (ISA, PCI, USB or HSS))
+****************************************************************************/
+U14API(short) U14DriverName(short hand, char* pBuf, WORD wMax)
+{
+ char* pName;
+ *pBuf = 0; // Start off with a blank string
+ switch (U14DriverType(hand)) // Results according to type
+ {
+ case 0: pName = "ISA"; break;
+ case 1: pName = "PCI"; break;
+ case 2: pName = "USB"; break;
+ case 3: pName = "HSS"; break;
+ default: pName = "???"; break;
+ }
+ strncpy(pBuf, pName, wMax); // Copy the correct name to return
+
+ return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** U14BlkTransState
+** Returns 0 no transfer in progress, 1 transfer in progress or an error code
+****************************************************************************/
+U14API(short) U14BlkTransState(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_BLKTRANSSTATE, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = csBlock.ints[0];
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_BlkTransState(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14Grab1401
+** Take control of the 1401 for diagnostics purposes. USB does nothing.
+****************************************************************************/
+U14API(short) U14Grab1401(short hand)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+#ifdef _IS_WINDOWS_
+ if (abGrabbed[hand]) // 1401 should not have been grabbed
+ sErr = U14ERR_ALREADYSET; // Error code defined for this
+ else
+ {
+ TCSBLOCK csBlock;
+ sErr = U14Control1401(hand, U14_GRAB1401, &csBlock);
+ }
+#endif
+#ifdef LINUX
+ // 1401 should not have been grabbed
+ sErr = abGrabbed[hand] ? U14ERR_ALREADYSET : CED_Grab1401(aHand1401[hand]);
+#endif
+ if (sErr == U14ERR_NOERROR)
+ abGrabbed[hand] = TRUE;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14Free1401
+****************************************************************************/
+U14API(short) U14Free1401(short hand)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+#ifdef _IS_WINDOWS_
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+ TCSBLOCK csBlock;
+ sErr = U14Control1401(hand, U14_FREE1401, &csBlock);
+ }
+ else
+ sErr = U14ERR_NOTSET;
+#endif
+#ifdef LINUX
+ // 1401 should not have been grabbed
+ sErr = abGrabbed[hand] ? CED_Free1401(aHand1401[hand]) : U14ERR_NOTSET;
+#endif
+ if (sErr == U14ERR_NOERROR)
+ abGrabbed[hand] = FALSE;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14Peek1401
+** DESCRIPTION Cause the 1401 to do one or more peek operations.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop
+** is called. After the peek is done, use U14GetDebugData to retrieve
+** the results of the peek.
+****************************************************************************/
+U14API(short) U14Peek1401(short hand, DWORD dwAddr, int nSize, int nRepeats)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ csBlock.longs[0] = (long)dwAddr;
+ csBlock.longs[1] = nSize;
+ csBlock.longs[2] = nRepeats;
+ sErr = U14Control1401(hand, U14_DBGPEEK, &csBlock);
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ dbb.iAddr = (int)dwAddr;
+ dbb.iWidth = nSize;
+ dbb.iRepeats = nRepeats;
+ sErr = CED_DbgPeek(aHand1401[hand], &dbb);
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14Poke1401
+** DESCRIPTION Cause the 1401 to do one or more poke operations.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop
+** is called.
+****************************************************************************/
+U14API(short) U14Poke1401(short hand, DWORD dwAddr, DWORD dwValue,
+ int nSize, int nRepeats)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ csBlock.longs[0] = (long)dwAddr;
+ csBlock.longs[1] = nSize;
+ csBlock.longs[2] = nRepeats;
+ csBlock.longs[3] = (long)dwValue;
+ sErr = U14Control1401(hand, U14_DBGPOKE, &csBlock);
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ dbb.iAddr = (int)dwAddr;
+ dbb.iWidth = nSize;
+ dbb.iRepeats= nRepeats;
+ dbb.iData = (int)dwValue;
+ sErr = CED_DbgPoke(aHand1401[hand], &dbb);
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14Ramp1401
+** DESCRIPTION Cause the 1401 to loop, writing a ramp to a location.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop.
+****************************************************************************/
+U14API(short) U14Ramp1401(short hand, DWORD dwAddr, DWORD dwDef, DWORD dwEnable,
+ int nSize, int nRepeats)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ csBlock.longs[0] = (long)dwAddr;
+ csBlock.longs[1] = (long)dwDef;
+ csBlock.longs[2] = (long)dwEnable;
+ csBlock.longs[3] = nSize;
+ csBlock.longs[4] = nRepeats;
+ sErr = U14Control1401(hand, U14_DBGRAMPDATA, &csBlock);
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ dbb.iAddr = (int)dwAddr;
+ dbb.iDefault = (int)dwDef;
+ dbb.iMask = (int)dwEnable;
+ dbb.iWidth = nSize;
+ dbb.iRepeats = nRepeats;
+ sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14RampAddr
+** DESCRIPTION Cause the 1401 to loop, reading from a ramping location.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop
+****************************************************************************/
+U14API(short) U14RampAddr(short hand, DWORD dwDef, DWORD dwEnable,
+ int nSize, int nRepeats)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ csBlock.longs[0] = (long)dwDef;
+ csBlock.longs[1] = (long)dwEnable;
+ csBlock.longs[2] = nSize;
+ csBlock.longs[3] = nRepeats;
+ sErr = U14Control1401(hand, U14_DBGRAMPADDR, &csBlock);
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ dbb.iDefault = (int)dwDef;
+ dbb.iMask = (int)dwEnable;
+ dbb.iWidth = nSize;
+ dbb.iRepeats = nRepeats;
+ sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14StopDebugLoop
+** DESCRIPTION Stops a peek\poke\ramp that, with repeats set to zero,
+** will otherwise continue forever.
+****************************************************************************/
+U14API(short) U14StopDebugLoop(short hand)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+#ifdef _IS_WINDOWS_
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+ TCSBLOCK csBlock;
+ sErr = U14Control1401(hand, U14_DBGSTOPLOOP, &csBlock);
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+#endif
+#ifdef LINUX
+ sErr = abGrabbed[hand] ? CED_DbgStopLoop(aHand1401[hand]) : U14ERR_NOTSET;
+#endif
+ return sErr;
+}
+
+/****************************************************************************
+** U14GetDebugData
+** DESCRIPTION Returns the result from a previous peek operation.
+****************************************************************************/
+U14API(short) U14GetDebugData(short hand, U14LONG* plValue)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ sErr = U14Status1401(hand, U14_DBGGETDATA, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ *plValue = csBlock.longs[0]; // Return the data
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ sErr = CED_DbgGetData(aHand1401[hand], &dbb);
+ if (sErr == U14ERR_NOERROR)
+ *plValue = dbb.iData; /* Return the data */
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14StartSelfTest
+****************************************************************************/
+U14API(short) U14StartSelfTest(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ return U14Control1401(hand, U14_STARTSELFTEST, &csBlock);
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_StartSelfTest(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14CheckSelfTest
+****************************************************************************/
+U14API(short) U14CheckSelfTest(short hand, U14LONG *pData)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_CHECKSELFTEST, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ {
+ pData[0] = csBlock.longs[0]; /* Return the results to user */
+ pData[1] = csBlock.longs[1];
+ pData[2] = csBlock.longs[2];
+ }
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR) /* Check parameters */
+ {
+ TGET_SELFTEST gst;
+ sErr = CED_CheckSelfTest(aHand1401[hand], &gst);
+ if (sErr == U14ERR_NOERROR)
+ {
+ pData[0] = gst.code; /* Return the results to user */
+ pData[1] = gst.x;
+ pData[2] = gst.y;
+ }
+ }
+#endif
+ return sErr;
+}
+
+/****************************************************************************
+** U14GetUserMemorySize
+****************************************************************************/
+U14API(short) U14GetUserMemorySize(short hand, DWORD *pMemorySize)
+{
+ // The original 1401 used a different command for getting the size
+ short sErr = U14SendString(hand, (asType1401[hand] == U14TYPE1401) ? "MEMTOP;" : "MEMTOP,?;");
+ *pMemorySize = 0; /* if we get error then leave size set at 0 */
+ if (sErr == U14ERR_NOERROR)
+ {
+ U14LONG alLimits[4];
+ sErr = U14LongsFrom1401(hand, alLimits, 4);
+ if (sErr > 0) /* +ve sErr is the number of values read */
+ {
+ sErr = U14ERR_NOERROR; /* All OK, flag success */
+ if (asType1401[hand] == U14TYPE1401) /* result for standard */
+ *pMemorySize = alLimits[0] - alLimits[1]; /* memtop-membot */
+ else
+ *pMemorySize = alLimits[0]; /* result for plus or u1401 */
+ }
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14TypeOf1401
+** Returns the type of the 1401, maybe unknown
+****************************************************************************/
+U14API(short) U14TypeOf1401(short hand)
+{
+ if ((hand < 0) || (hand >= MAX1401)) /* Check parameters */
+ return U14ERR_BADHAND;
+ else
+ return asType1401[hand];
+}
+
+/****************************************************************************
+** U14NameOf1401
+** Returns the type of the 1401 as a string, blank if unknown
+****************************************************************************/
+U14API(short) U14NameOf1401(short hand, char* pBuf, WORD wMax)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ char* pName;
+ switch (asType1401[hand]) // Results according to type
+ {
+ case U14TYPE1401: pName = "Std 1401"; break;
+ case U14TYPEPLUS: pName = "1401plus"; break;
+ case U14TYPEU1401: pName = "micro1401"; break;
+ case U14TYPEPOWER: pName = "Power1401"; break;
+ case U14TYPEU14012:pName = "Micro1401 mk II"; break;
+ case U14TYPEPOWER2:pName = "Power1401 mk II"; break;
+ case U14TYPEU14013:pName = "Micro1401-3"; break;
+ case U14TYPEPOWER3:pName = "Power1401-3"; break;
+ default: pName = "Unknown";
+ }
+ strncpy(pBuf, pName, wMax);
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14TransferFlags
+** Returns the driver block transfer flags.
+** Bits can be set - see U14TF_ constants in use1401.h
+*****************************************************************************/
+U14API(short) U14TransferFlags(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_TRANSFERFLAGS, &csBlock);
+ return (sErr == U14ERR_NOERROR) ? (short)csBlock.ints[0] : sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_TransferFlags(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** GetDriverVersion
+** Actually reads driver version from the device driver.
+** Hi word is major revision, low word is minor revision.
+** Assumes that hand has been checked. Also codes driver type in bits 24 up.
+*****************************************************************************/
+static int GetDriverVersion(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ int iErr = U14Status1401(hand, U14_GETDRIVERREVISION, &csBlock);
+ if (iErr == U14ERR_NOERROR)
+ iErr = csBlock.longs[0];
+ return iErr;
+#endif
+#ifdef LINUX
+ return CED_GetDriverRevision(aHand1401[hand]);
+#endif
+}
+
+/****************************************************************************
+** U14MonitorRev
+** Returns the 1401 monitor revision number.
+** The number returned is the minor revision - the part after the
+** decimal point - plus the major revision times 1000.
+*****************************************************************************/
+U14API(int) U14MonitorRev(short hand)
+{
+ int iRev = 0;
+ int iErr = CheckHandle(hand);
+ if (iErr != U14ERR_NOERROR) // Check open and in use
+ return iErr;
+
+ if (asType1401[hand] >= U14TYPEPOWER2) // The Power2 onwards can give us the monitor
+ { // revision directly for all versions
+ iErr = U14SendString(hand, "INFO,S,28;");
+ if (iErr == U14ERR_NOERROR)
+ {
+ U14LONG lVals[2]; // Read a single number being the revision
+ iErr = U14LongsFrom1401(hand, lVals, 1);
+ if (iErr > 0)
+ {
+ iErr = U14ERR_NOERROR;
+ iRev = lVals[0]; // This is the minor part of the revision
+ iRev += asType1401[hand] * 10000;
+ }
+ }
+ }
+ else
+ { /* Do it the hard way for older hardware */
+ iErr = U14SendString(hand, ";CLIST;"); /* ask for command levels */
+ if (iErr == U14ERR_NOERROR)
+ {
+ while (iErr == U14ERR_NOERROR)
+ {
+ char wstr[50];
+ iErr = U14GetString(hand, wstr, 45);
+ if (iErr == U14ERR_NOERROR)
+ {
+ char *pstr = strstr(wstr,"RESET"); /* Is this the RESET command? */
+ if ((pstr == wstr) && (wstr[5] == ' '))
+ {
+ char *pstr2;
+ size_t l;
+ pstr += 6; /* Move past RESET and followinmg char */
+ l = strlen(pstr); /* The length of text remaining */
+ while (((pstr[l-1] == ' ') || (pstr[l-1] == 13)) && (l > 0))
+ {
+ pstr[l-1] = 0; /* Tidy up string at the end */
+ l--; /* by removing spaces and CRs */
+ }
+ pstr2 = strchr(pstr, '.'); /* Find the decimal point */
+ if (pstr2 != NULL) /* If we found the DP */
+ {
+ *pstr2 = 0; /* End pstr string at DP */
+ pstr2++; /* Now past the decimal point */
+ iRev = atoi(pstr2); /* Get the number after point */
+ }
+ iRev += (atoi(pstr) * 1000); /* Add first bit * 1000 */
+ }
+ if ((strlen(wstr) < 3) && (wstr[0] == ' '))
+ break; /* Spot the last line of results */
+ }
+ }
+ }
+ }
+ if (iErr == U14ERR_NOERROR) /* Return revision if no error */
+ iErr = iRev;
+
+ return iErr;
+}
+
+/****************************************************************************
+** U14TryToOpen Tries to open the 1401 number passed
+** Note : This will succeed with NT driver even if no I/F card or
+** 1401 switched off, so we check state and close the driver
+** if the state is unsatisfactory in U14Open1401.
+****************************************************************************/
+#ifdef _IS_WINDOWS_
+#define U14NAMEOLD "\\\\.\\CED_140%d"
+#define U14NAMENEW "\\\\.\\CED%d"
+static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
+{
+ short sErr = U14ERR_NOERROR;
+ HANDLE hDevice = INVALID_HANDLE_VALUE;
+ DWORD dwErr = 0;
+ int nFirst, nLast, nDev = 0; /* Used for the search for a 1401 */
+ BOOL bOldName = FALSE; /* start by looking for a modern driver */
+
+ if (n1401 == 0) /* If we need to look for a 1401 */
+ {
+ nFirst = 1; /* Set the search range */
+ nLast = MAX1401; /* through all the possible 1401s */
+ }
+ else
+ nFirst = nLast = n1401; /* Otherwise just one 1401 */
+
+ while (hDevice == INVALID_HANDLE_VALUE) /* Loop to try for a 1401 */
+ {
+ for (nDev = nFirst; nDev <= nLast; nDev++)
+ {
+ char szDevName[40]; /* name of the device to open */
+ sprintf(szDevName, bOldName ? U14NAMEOLD : U14NAMENEW, nDev);
+ hDevice = CreateFile(szDevName, GENERIC_WRITE | GENERIC_READ,
+ 0, 0, /* Unshared mode does nothing as this is a device */
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hDevice != INVALID_HANDLE_VALUE)/* Check 1401 if opened */
+ {
+ TCSBLOCK csBlock;
+ assert(aHand1401[nDev-1] == INVALID_HANDLE_VALUE); // assert if already open
+ aHand1401[nDev-1] = hDevice; /* Save handle for now */
+
+#ifndef _WIN64
+ // Use DIOC method if not windows 9x or if using new device name
+ abUseNTDIOC[nDev-1] = (BOOL)(!bWindows9x || !bOldName);
+#endif
+ sErr = U14Status1401((short)(nDev-1), U14_TYPEOF1401, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ {
+ *plRetVal = csBlock.ints[0];
+ if (csBlock.ints[0] == U14ERR_INUSE)/* Prevent multi opens */
+ {
+ CloseHandle(hDevice); /* treat as open failure */
+ hDevice = INVALID_HANDLE_VALUE;
+ aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
+ sErr = U14ERR_INUSE;
+ }
+ else
+ break; /* Exit from for loop on success */
+ }
+ else
+ {
+ CloseHandle(hDevice); /* Give up if func fails */
+ hDevice = INVALID_HANDLE_VALUE;
+ aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
+ }
+ }
+ else
+ {
+ DWORD dwe = GetLastError(); /* Get error code otherwise */
+ if ((dwe != ERROR_FILE_NOT_FOUND) || (dwErr == 0))
+ dwErr = dwe; /* Ignore repeats of 'not found' */
+ }
+ }
+
+ if ((hDevice == INVALID_HANDLE_VALUE) &&/* No device found, and... */
+ (bWindows9x) && /* ...old names are allowed, and... */
+ (bOldName == FALSE)) /* ...not tried old names yet */
+ bOldName = TRUE; /* Set flag and go round again */
+ else
+ break; /* otherwise that's all folks */
+ }
+
+ if (hDevice != INVALID_HANDLE_VALUE) /* If we got our device open */
+ *psHandle = (short)(nDev-1); /* return 1401 number opened */
+ else
+ {
+ if (dwErr == ERROR_FILE_NOT_FOUND) /* Sort out the error codes */
+ sErr = U14ERR_NO1401DRIV; /* if file not found */
+ else if (dwErr == ERROR_NOT_SUPPORTED)
+ sErr = U14ERR_DRIVTOOOLD; /* if DIOC not supported */
+ else if (dwErr == ERROR_ACCESS_DENIED)
+ sErr = U14ERR_INUSE;
+ else
+ sErr = U14ERR_DRIVCOMMS; /* otherwise assume comms problem */
+ }
+ return sErr;
+}
+#endif
+#ifdef LINUX
+static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
+{
+ short sErr = U14ERR_NOERROR;
+ int fh = 0; // will be 1401 handle
+ int iErr = 0;
+ int nFirst, nLast, nDev = 0; // Used for the search for a 1401
+
+ if (n1401 == 0) // If we need to look for a 1401
+ {
+ nFirst = 1; /* Set the search range */
+ nLast = MAX1401; /* through all the possible 1401s */
+ }
+ else
+ nFirst = nLast = n1401; /* Otherwise just one 1401 */
+
+ for (nDev = nFirst; nDev <= nLast; nDev++)
+ {
+ char szDevName[40]; // name of the device to open
+ sprintf(szDevName,"/dev/cedusb/%d", nDev-1);
+ fh = open(szDevName, O_RDWR); // can only be opened once at a time
+ if (fh > 0) // Check 1401 if opened
+ {
+ int iType1401 = CED_TypeOf1401(fh); // get 1401 type
+ aHand1401[nDev-1] = fh; // Save handle for now
+ if (iType1401 >= 0)
+ {
+ *plRetVal = iType1401;
+ break; // Exit from for loop on success
+ }
+ else
+ {
+ close(fh); // Give up if func fails
+ fh = 0;
+ aHand1401[nDev-1] = 0;
+ }
+ }
+ else
+ {
+ if (((errno != ENODEV) && (errno != ENOENT)) || (iErr == 0))
+ iErr = errno; // Ignore repeats of 'not found'
+ }
+ }
+
+
+ if (fh) // If we got our device open
+ *psHandle = (short)(nDev-1); // return 1401 number opened
+ else
+ {
+ if ((iErr == ENODEV) || (iErr == ENOENT)) // Sort out the error codes
+ sErr = U14ERR_NO1401DRIV; // if file not found
+ else if (iErr == EBUSY)
+ sErr = U14ERR_INUSE;
+ else
+ sErr = U14ERR_DRIVCOMMS; // otherwise assume comms problem
+ }
+
+ return sErr;
+}
+#endif
+/****************************************************************************
+** U14Open1401
+** Tries to get the 1401 for use by this application
+*****************************************************************************/
+U14API(short) U14Open1401(short n1401)
+{
+ long lRetVal = -1;
+ short sErr;
+ short hand = 0;
+
+ if ((n1401 < 0) || (n1401 > MAX1401)) // must check the 1401 number
+ return U14ERR_BAD1401NUM;
+
+ szLastName[0] = 0; /* initialise the error info string */
+
+ sErr = U14TryToOpen(n1401, &lRetVal, &hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ long lDriverVersion = GetDriverVersion(hand); /* get driver revision */
+ long lDriverRev = -1;
+ if (lDriverVersion >= 0) /* can use it if all OK */
+ {
+ lLastDriverType = (lDriverVersion >> 24) & 0x000000FF;
+ asDriverType[hand] = (short)lLastDriverType; /* Drv type */
+ lLastDriverVersion = lDriverVersion & 0x00FFFFFF;
+ alDriverVersion[hand] = lLastDriverVersion; /* Actual version */
+ lDriverRev = ((lDriverVersion>>16) & 0x00FF); /* use hi word */
+ }
+ else
+ {
+ U14Close1401(hand); /* If there is a problem we should close */
+ return (short)lDriverVersion; /* and return the error code */
+ }
+
+ if (lDriverRev < MINDRIVERMAJREV) /* late enough version? */
+ {
+ U14Close1401(hand); /* If there is a problem we should close */
+ return U14ERR_DRIVTOOOLD; /* too old */
+ }
+
+ asLastRetCode[hand] = U14ERR_NOERROR; /* Initialise this 1401s info */
+ abGrabbed[hand] = FALSE; /* we are not in single step mode */
+ U14SetTimeout(hand, 3000); /* set 3 seconds as default timeout */
+
+ switch (lRetVal)
+ {
+ case DRIVRET_STD: asType1401[hand] = U14TYPE1401; break; /* Some we do by hand */
+ case DRIVRET_U1401:asType1401[hand] = U14TYPEU1401; break;
+ case DRIVRET_PLUS: asType1401[hand] = U14TYPEPLUS; break;
+ default: // For the power upwards, we can calculate the codes
+ if ((lRetVal >= DRIVRET_POWER) && (lRetVal <= DRIVRET_MAX))
+ asType1401[hand] = (short)(lRetVal - (DRIVRET_POWER - U14TYPEPOWER));
+ else
+ asType1401[hand] = U14TYPEUNKNOWN;
+ break;
+ }
+ U14KillIO1401(hand); /* resets the 1401 buffers */
+
+ if (asType1401[hand] != U14TYPEUNKNOWN) /* If all seems OK so far */
+ {
+ sErr = U14CheckErr(hand); /* we can check 1401 comms now */
+ if (sErr != 0) /* If this failed to go OK */
+ U14Reset1401(hand); /* Reset the 1401 to try to sort it out */
+ }
+
+ sErr = U14StateOf1401(hand);/* Get the state of the 1401 for return */
+ if (sErr == U14ERR_NOERROR)
+ sErr = hand; /* return the handle if no problem */
+ else
+ U14Close1401(hand); /* If there is a problem we should close */
+ }
+
+ return sErr;
+}
+
+
+/****************************************************************************
+** U14Close1401
+** Closes the 1401 so someone else can use it.
+****************************************************************************/
+U14API(short) U14Close1401(short hand)
+{
+ int j;
+ int iAreaMask = 0; // Mask for active areas
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR) // Check open and in use
+ return sErr;
+
+ for (j = 0; j<MAX_TRANSAREAS; ++j)
+ {
+ TGET_TX_BLOCK gtb;
+ int iReturn = U14GetTransfer(hand, >b); // get area information
+ if (iReturn == U14ERR_NOERROR) // ignore if any problem
+ if (gtb.used)
+ iAreaMask |= (1 << j); // set a bit for each used area
+ }
+
+ if (iAreaMask) // if any areas are in use
+ {
+ U14Reset1401(hand); // in case an active transfer running
+ for (j = 0; j < MAX_TRANSAREAS; ++j) // Locate locked areas
+ if (iAreaMask & (1 << j)) // And kill off any transfers
+ U14UnSetTransfer(hand, (WORD)j);
+ }
+
+#ifdef _IS_WINDOWS_
+ if (aXferEvent[hand]) // if this 1401 has an open event handle
+ {
+ CloseHandle(aXferEvent[hand]); // close down the handle
+ aXferEvent[hand] = NULL; // and mark it as gone
+ }
+
+ if (CloseHandle(aHand1401[hand]))
+#endif
+#ifdef LINUX
+ if (close(aHand1401[hand]) == 0) // make sure that close works
+#endif
+ {
+ aHand1401[hand] = INVALID_HANDLE_VALUE;
+ asType1401[hand] = U14TYPEUNKNOWN;
+ return U14ERR_NOERROR;
+ }
+ else
+ return U14ERR_BADHAND; /* BUGBUG GetLastError() ? */
+}
+
+/**************************************************************************
+**
+** Look for open 1401s and attempt to close them down. 32-bit windows only.
+**************************************************************************/
+U14API(void) U14CloseAll(void)
+{
+ int i;
+ for (i = 0; i < MAX1401; i++) // Tidy up and make safe
+ if (aHand1401[i] != INVALID_HANDLE_VALUE)
+ U14Close1401((short)i); // Last ditch close 1401
+}
+
+/****************************************************************************
+** U14Reset1401
+** Resets the 1401
+****************************************************************************/
+U14API(short) U14Reset1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ return U14Control1401(hand, U14_RESET1401, &csBlock);
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_Reset1401(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14ForceReset
+** Sets the 1401 full reset flag, so that next call to Reset1401 will
+** always cause a genuine reset.
+*****************************************************************************/
+U14API(short) U14ForceReset(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ return U14Control1401(hand, U14_FULLRESET, &csBlock);
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_FullReset(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14KillIO1401
+** Removes any pending IO from the buffers.
+*****************************************************************************/
+U14API(short) U14KillIO1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ return U14Control1401(hand, U14_KILLIO1401, &csBlock);
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_KillIO1401(aHand1401[hand]) : sErr;
+#endif
+}
+
+
+/****************************************************************************
+** U14SendString
+** Send characters to the 1401
+*****************************************************************************/
+U14API(short) U14SendString(short hand, const char* pString)
+{
+ int nChars; // length we are sending
+ long lTimeOutTicks; // when to time out
+ BOOL bSpaceToSend; // space to send yet
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ nChars = (int)strlen(pString); // get string length we want to send
+ if (nChars > MAXSTRLEN)
+ return U14ERR_STRLEN; // String too long
+
+#ifdef _IS_WINDOWS_
+ // To get here we must wait for the buffer to have some space
+ lTimeOutTicks = U14WhenToTimeOut(hand);
+ do
+ {
+ bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
+ }
+ while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
+
+ if (!bSpaceToSend) /* Last-ditch attempt to avoid timeout */
+ { /* This can happen with anti-virus or network activity! */
+ int i;
+ for (i = 0; (i < 4) && (!bSpaceToSend); ++i)
+ {
+ Sleep(25); /* Give other threads a chance for a while */
+ bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
+ }
+ }
+
+ if (asLastRetCode[hand] == U14ERR_NOERROR) /* no errors? */
+ {
+ if (bSpaceToSend)
+ {
+ PARAMBLK rData;
+ DWORD dwBytes;
+ char tstr[MAXSTRLEN+5]; /* Buffer for chars */
+
+ if ((hand < 0) || (hand >= MAX1401))
+ sErr = U14ERR_BADHAND;
+ else
+ {
+ strcpy(tstr, pString); /* Into local buf */
+#ifndef _WIN64
+ if (!USE_NT_DIOC(hand)) /* Using WIN 95 driver access? */
+ {
+ int iOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_SENDSTRING,
+ NULL, 0, tstr, nChars,
+ &dwBytes, NULL);
+ if (iOK)
+ sErr = (dwBytes >= (DWORD)nChars) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS;
+ else
+ sErr = (short)GetLastError();
+ }
+ else
+#endif
+ {
+ int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_SENDSTRING,
+ tstr, nChars,
+ &rData,sizeof(PARAMBLK),&dwBytes,NULL);
+ if (iOK && (dwBytes >= sizeof(PARAMBLK)))
+ sErr = rData.sState;
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+
+ if (sErr != U14ERR_NOERROR) // If we have had a comms error
+ U14ForceReset(hand); // make sure we get real reset
+ }
+
+ return sErr;
+
+ }
+ else
+ {
+ U14ForceReset(hand); // make sure we get real reset
+ return U14ERR_TIMEOUT;
+ }
+ }
+ else
+ return asLastRetCode[hand];
+#endif
+#ifdef LINUX
+ // Just try to send it and see what happens!
+ sErr = CED_SendString(aHand1401[hand], pString, nChars);
+ if (sErr != U14ERR_NOOUT) // if any result except "no room in output"...
+ {
+ if (sErr != U14ERR_NOERROR) // if a problem...
+ U14ForceReset(hand); // ...make sure we get real reset next time
+ return sErr; // ... we are done as nothing we can do
+ }
+
+ // To get here we must wait for the buffer to have some space
+ lTimeOutTicks = U14WhenToTimeOut(hand);
+ do
+ {
+ bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
+ if (!bSpaceToSend)
+ sched_yield(); // let others have fun while we wait
+ }
+ while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
+
+ if (asLastRetCode[hand] == U14ERR_NOERROR) /* no errors? */
+ {
+ if (bSpaceToSend)
+ {
+ sErr = CED_SendString(aHand1401[hand], pString, nChars);
+ if (sErr != U14ERR_NOERROR) // If we have had a comms error
+ U14ForceReset(hand); // make sure we get real reset
+ return sErr;
+ }
+ else
+ {
+ U14ForceReset(hand); // make sure we get real reset
+ return U14ERR_TIMEOUT;
+ }
+ }
+ else
+ return asLastRetCode[hand];
+#endif
+}
+
+/****************************************************************************
+** U14SendChar
+** Send character to the 1401
+*****************************************************************************/
+U14API(short) U14SendChar(short hand, char cChar)
+{
+#ifdef _IS_WINDOWS_
+ char sz[2]=" "; // convert to a string and send
+ sz[0] = cChar;
+ sz[1] = 0;
+ return(U14SendString(hand, sz)); // String routines are better
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_SendChar(aHand1401[hand], cChar) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14GetString
+** Get a string from the 1401. Returns a null terminated string.
+** The string is all the characters up to the next CR in the buffer
+** or the end of the buffer if that comes first. This only returns text
+** if there is a CR in the buffer. The terminating CR character is removed.
+** wMaxLen Is the size of the buffer and must be at least 2 or an error.
+** Returns U14ERR_NOERR if OK with the result in the string or a negative
+** error code. Any error from the device causes us to set up for
+** a full reset.
+****************************************************************************/
+U14API(short) U14GetString(short hand, char* pBuffer, WORD wMaxLen)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR) // If an error...
+ return sErr; // ...bail out!
+
+#ifdef _IS_WINDOWS_
+ if (wMaxLen>1) // we need space for terminating 0
+ {
+ BOOL bLineToGet; // true when a line to get
+ long lTimeOutTicks = U14WhenToTimeOut(hand);
+ do
+ bLineToGet = (BOOL)(U14LineCount(hand) != 0);
+ while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
+
+ if (!bLineToGet) /* Last-ditch attempt to avoid timeout */
+ { /* This can happen with anti-virus or network activity! */
+ int i;
+ for (i = 0; (i < 4) && (!bLineToGet); ++i)
+ {
+ Sleep(25); /* Give other threads a chance for a while */
+ bLineToGet = (BOOL)(U14LineCount(hand) != 0);
+ }
+ }
+
+ if (bLineToGet)
+ {
+ if (asLastRetCode[hand] == U14ERR_NOERROR) /* all ok so far */
+ {
+ DWORD dwBytes = 0;
+ *((WORD *)pBuffer) = wMaxLen; /* set up length */
+#ifndef _WIN64
+ if (!USE_NT_DIOC(hand)) /* Win 95 DIOC here ? */
+ {
+ char tstr[MAXSTRLEN+5]; /* Buffer for Win95 chars */
+ int iOK;
+
+ if (wMaxLen > MAXSTRLEN) /* Truncate length */
+ wMaxLen = MAXSTRLEN;
+
+ *((WORD *)tstr) = wMaxLen; /* set len */
+
+ iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
+ NULL, 0, tstr, wMaxLen+sizeof(short),
+ &dwBytes, NULL);
+ if (iOK) /* Device IO control OK ? */
+ {
+ if (dwBytes >= 0) /* If driver OK */
+ {
+ strcpy(pBuffer, tstr);
+ sErr = U14ERR_NOERROR;
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+ else
+ {
+ sErr = (short)GetLastError();
+ if (sErr > 0) /* Errors are -ve */
+ sErr = (short)-sErr;
+ }
+ }
+ else
+#endif
+ { /* Here for NT, the DLL must own the buffer */
+ HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE,wMaxLen+sizeof(short));
+ if (hMem)
+ {
+ char* pMem = (char*)GlobalLock(hMem);
+ if (pMem)
+ {
+ int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
+ NULL, 0, pMem, wMaxLen+sizeof(short),
+ &dwBytes, NULL);
+ if (iOK) /* Device IO control OK ? */
+ {
+ if (dwBytes >= wMaxLen)
+ {
+ strcpy(pBuffer, pMem+sizeof(short));
+ sErr = *((SHORT*)pMem);
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+
+ GlobalUnlock(hMem);
+ }
+ else
+ sErr = U14ERR_OUTOFMEMORY;
+
+ GlobalFree(hMem);
+ }
+ else
+ sErr = U14ERR_OUTOFMEMORY;
+ }
+
+ if (sErr == U14ERR_NOERROR) // If all OK...
+ TranslateString(pBuffer); // ...convert any commas to spaces
+ else // If we have had a comms error...
+ U14ForceReset(hand); // ...make sure we get real reset
+
+ }
+ else
+ sErr = asLastRetCode[hand];
+ }
+ else
+ {
+ sErr = U14ERR_TIMEOUT;
+ U14ForceReset(hand); // make sure we get real reset
+ }
+ }
+ else
+ sErr = U14ERR_BUFF_SMALL;
+ return sErr;
+#endif
+#ifdef LINUX
+ if (wMaxLen>1) // we need space for terminating 0
+ {
+ BOOL bLineToGet; // true when a line to get
+ long lTimeOutTicks = U14WhenToTimeOut(hand);
+ do
+ {
+ bLineToGet = (BOOL)(U14LineCount(hand) != 0);
+ if (!bLineToGet)
+ sched_yield();
+
+ }
+ while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
+
+ if (bLineToGet)
+ {
+ sErr = CED_GetString(aHand1401[hand], pBuffer, wMaxLen-1); // space for terminator
+ if (sErr >=0) // if we were OK...
+ {
+ if (sErr >= wMaxLen) // this should NOT happen unless
+ sErr = U14ERR_DRIVCOMMS; // ...driver Comms are very bad
+ else
+ {
+ pBuffer[sErr] = 0; // OK, so terminate the string...
+ TranslateString(pBuffer); // ...and convert commas to spaces.
+ }
+ }
+
+ if (sErr < U14ERR_NOERROR) // If we have had a comms error
+ U14ForceReset(hand); // make sure we get real reset
+ }
+ else
+ {
+ sErr = U14ERR_TIMEOUT;
+ U14ForceReset(hand); // make sure we get real reset
+ }
+ }
+ else
+ sErr = U14ERR_BUFF_SMALL;
+
+ return sErr >= U14ERR_NOERROR ? U14ERR_NOERROR : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14GetChar
+** Get a character from the 1401. CR returned as CR.
+*****************************************************************************/
+U14API(short) U14GetChar(short hand, char* pcChar)
+{
+#ifdef _IS_WINDOWS_
+ char sz[2]; // read a very short string
+ short sErr = U14GetString(hand, sz, 2); // read one char and nul terminate it
+ *pcChar = sz[0]; // copy to result, NB char translate done by GetString
+ if (sErr == U14ERR_NOERROR)
+ { // undo translate of CR to zero
+ if (*pcChar == '\0') // by converting back
+ *pcChar = '\n'; // What a nasty thing to have to do
+ }
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR) // Check parameters
+ return sErr;
+ sErr = CED_GetChar(aHand1401[hand]); // get one char, if available
+ if (sErr >= 0)
+ {
+ *pcChar = (char)sErr; // return if it we have one
+ return U14ERR_NOERROR; // say all OK
+ }
+ else
+ return sErr;
+#endif
+}
+
+/****************************************************************************
+** U14Stat1401
+** Returns 0 for no lines or error or non zero for something waiting
+****************************************************************************/
+U14API(short) U14Stat1401(short hand)
+{
+ return ((short)(U14LineCount(hand) > 0));
+}
+
+/****************************************************************************
+** U14CharCount
+** Returns the number of characters in the input buffer
+*****************************************************************************/
+U14API(short) U14CharCount(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_STAT1401, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = csBlock.ints[0];
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_Stat1401(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14LineCount
+** Returns the number of CR characters in the input buffer
+*****************************************************************************/
+U14API(short) U14LineCount(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_LINECOUNT, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = csBlock.ints[0];
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_LineCount(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14GetErrorString
+** Converts error code supplied to a decent descriptive string.
+** NOTE: This function may use some extra information stored
+** internally in the DLL. This information is stored on a
+** per-process basis, but it might be altered if you call
+** other functions after getting an error and before using
+** this function.
+****************************************************************************/
+U14API(void) U14GetErrorString(short nErr, char* pStr, WORD wMax)
+{
+ char wstr[150];
+
+ switch (nErr) /* Basically, we do this with a switch block */
+ {
+ case U14ERR_OFF:
+ sprintf(wstr, "The 1401 is apparently switched off (code %d)", nErr);
+ break;
+
+ case U14ERR_NC:
+ sprintf(wstr, "The 1401 is not connected to the interface card (code %d)", nErr);
+ break;
+
+ case U14ERR_ILL:
+ sprintf(wstr, "The 1401 is not working correctly (code %d)", nErr);
+ break;
+
+ case U14ERR_NOIF:
+ sprintf(wstr, "The 1401 interface card was not detected (code %d)", nErr);
+ break;
+
+ case U14ERR_TIME:
+ sprintf(wstr, "The 1401 fails to become ready for use (code %d)", nErr);
+ break;
+
+ case U14ERR_BADSW:
+ sprintf(wstr, "The 1401 interface card jumpers are incorrect (code %d)", nErr);
+ break;
+
+ case U14ERR_NOINT:
+ sprintf(wstr, "The 1401 interrupt is not available for use (code %d)", nErr);
+ break;
+
+ case U14ERR_INUSE:
+ sprintf(wstr, "The 1401 is already in use by another program (code %d)", nErr);
+ break;
+
+ case U14ERR_NODMA:
+ sprintf(wstr, "The 1401 DMA channel is not available for use (code %d)", nErr);
+ break;
+
+ case U14ERR_BADHAND:
+ sprintf(wstr, "The application supplied an incorrect 1401 handle (code %d)", nErr);
+ break;
+
+ case U14ERR_BAD1401NUM:
+ sprintf(wstr, "The application used an incorrect 1401 number (code %d)", nErr);
+ break;
+
+ case U14ERR_NO_SUCH_FN:
+ sprintf(wstr, "The code passed to the 1401 driver is invalid (code %d)", nErr);
+ break;
+
+ case U14ERR_NO_SUCH_SUBFN:
+ sprintf(wstr, "The sub-code passed to the 1401 driver is invalid (code %d)", nErr);
+ break;
+
+ case U14ERR_NOOUT:
+ sprintf(wstr, "No room in buffer for characters for the 1401 (code %d)", nErr);
+ break;
+
+ case U14ERR_NOIN:
+ sprintf(wstr, "No characters from the 1401 are available (code %d)", nErr);
+ break;
+
+ case U14ERR_STRLEN:
+ sprintf(wstr, "A string sent to or read from the 1401 was too long (code %d)", nErr);
+ break;
+
+ case U14ERR_LOCKFAIL:
+ sprintf(wstr, "Failed to lock host memory for data transfer (code %d)", nErr);
+ break;
+
+ case U14ERR_UNLOCKFAIL:
+ sprintf(wstr, "Failed to unlock host memory after data transfer (code %d)", nErr);
+ break;
+
+ case U14ERR_ALREADYSET:
+ sprintf(wstr, "The transfer area used is already set up (code %d)", nErr);
+ break;
+
+ case U14ERR_NOTSET:
+ sprintf(wstr, "The transfer area used has not been set up (code %d)", nErr);
+ break;
+
+ case U14ERR_BADAREA:
+ sprintf(wstr, "The transfer area number is incorrect (code %d)", nErr);
+ break;
+
+ case U14ERR_NOFILE:
+ sprintf(wstr, "The command file %s could not be opened (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_READERR:
+ sprintf(wstr, "The command file %s could not be read (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_UNKNOWN:
+ sprintf(wstr, "The %s command resource could not be found (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_HOSTSPACE:
+ sprintf(wstr, "Unable to allocate memory for loading command %s (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_LOCKERR:
+ sprintf(wstr, "Unable to lock memory for loading command %s (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_CLOADERR:
+ sprintf(wstr, "Error in loading command %s, bad command format (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_TOXXXERR:
+ sprintf(wstr, "Error detected after data transfer to or from the 1401 (code %d)", nErr);
+ break;
+
+ case U14ERR_NO386ENH:
+ sprintf(wstr, "Windows 3.1 is not running in 386 enhanced mode (code %d)", nErr);
+ break;
+
+ case U14ERR_NO1401DRIV:
+ sprintf(wstr, "The 1401 device driver cannot be found (code %d)\nUSB: check plugged in and powered\nOther: not installed?", nErr);
+ break;
+
+ case U14ERR_DRIVTOOOLD:
+ sprintf(wstr, "The 1401 device driver is too old for use (code %d)", nErr);
+ break;
+
+ case U14ERR_TIMEOUT:
+ sprintf(wstr, "Character transmissions to the 1401 timed-out (code %d)", nErr);
+ break;
+
+ case U14ERR_BUFF_SMALL:
+ sprintf(wstr, "Buffer for text from the 1401 was too small (code %d)", nErr);
+ break;
+
+ case U14ERR_CBALREADY:
+ sprintf(wstr, "1401 monitor callback already set up (code %d)", nErr);
+ break;
+
+ case U14ERR_BADDEREG:
+ sprintf(wstr, "1401 monitor callback deregister invalid (code %d)", nErr);
+ break;
+
+ case U14ERR_DRIVCOMMS:
+ sprintf(wstr, "1401 device driver communications failed (code %d)", nErr);
+ break;
+
+ case U14ERR_OUTOFMEMORY:
+ sprintf(wstr, "Failed to allocate or lock memory for text from the 1401 (code %d)", nErr);
+ break;
+
+ default:
+ sprintf(wstr, "1401 error code %d returned; this code is unknown", nErr);
+ break;
+
+ }
+ if ((WORD)strlen(wstr) >= wMax-1) /* Check for string being too long */
+ wstr[wMax-1] = 0; /* and truncate it if so */
+ strcpy(pStr, wstr); /* Return the error string */
+}
+
+/***************************************************************************
+** U14GetTransfer
+** Get a TGET_TX_BLOCK describing a transfer area (held in the block)
+***************************************************************************/
+U14API(short) U14GetTransfer(short hand, TGET_TX_BLOCK *pTransBlock)
+{
+ short sErr = CheckHandle(hand);
+#ifdef _IS_WINDOWS_
+ if (sErr == U14ERR_NOERROR)
+ {
+ DWORD dwBytes = 0;
+ BOOL bOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_GETTRANSFER, NULL, 0, pTransBlock,
+ sizeof(TGET_TX_BLOCK), &dwBytes, NULL);
+
+ if (bOK && (dwBytes >= sizeof(TGET_TX_BLOCK)))
+ sErr = U14ERR_NOERROR;
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+ return sErr;
+#endif
+#ifdef LINUX
+ return (sErr == U14ERR_NOERROR) ? CED_GetTransfer(aHand1401[hand], pTransBlock) : sErr;
+#endif
+}
+/////////////////////////////////////////////////////////////////////////////
+// U14WorkingSet
+// For Win32 only, adjusts process working set so that minimum is at least
+// dwMinKb and maximum is at least dwMaxKb.
+// Return value is zero if all went OK, or a code from 1 to 3 indicating the
+// cause of the failure:
+//
+// 1 unable to access process (insufficient rights?)
+// 2 unable to read process working set
+// 3 unable to set process working set - bad parameters?
+U14API(short) U14WorkingSet(DWORD dwMinKb, DWORD dwMaxKb)
+{
+#ifdef _IS_WINDOWS_
+ short sRetVal = 0; // 0 means all is OK
+ HANDLE hProcess;
+ DWORD dwVer = GetVersion();
+ if (dwVer & 0x80000000) // is this not NT?
+ return 0; // then give up right now
+
+ // Now attempt to get information on working set size
+ hProcess = OpenProcess(STANDARD_RIGHTS_REQUIRED |
+ PROCESS_QUERY_INFORMATION |
+ PROCESS_SET_QUOTA,
+ FALSE, _getpid());
+ if (hProcess)
+ {
+ SIZE_T dwMinSize,dwMaxSize;
+ if (GetProcessWorkingSetSize(hProcess, &dwMinSize, &dwMaxSize))
+ {
+ DWORD dwMin = dwMinKb << 10; // convert from kb to bytes
+ DWORD dwMax = dwMaxKb << 10;
+
+ // if we get here, we have managed to read the current size
+ if (dwMin > dwMinSize) // need to change sizes?
+ dwMinSize = dwMin;
+
+ if (dwMax > dwMaxSize)
+ dwMaxSize = dwMax;
+
+ if (!SetProcessWorkingSetSize(hProcess, dwMinSize, dwMaxSize))
+ sRetVal = 3; // failed to change size
+ }
+ else
+ sRetVal = 2; // failed to read original size
+
+ CloseHandle(hProcess);
+ }
+ else
+ sRetVal = 1; // failed to get handle
+
+ return sRetVal;
+#endif
+#ifdef LINUX
+ if (dwMinKb | dwMaxKb)
+ {
+ // to stop compiler moaning
+ }
+ return U14ERR_NOERROR;
+#endif
+}
+
+/****************************************************************************
+** U14UnSetTransfer Cancels a transfer area
+** wArea The index of a block previously used in by SetTransfer
+*****************************************************************************/
+U14API(short) U14UnSetTransfer(short hand, WORD wArea)
+{
+ short sErr = CheckHandle(hand);
+#ifdef _IS_WINDOWS_
+ if (sErr == U14ERR_NOERROR)
+ {
+ TCSBLOCK csBlock;
+ csBlock.ints[0] = (short)wArea; /* Area number into control block */
+ sErr = U14Control1401(hand, U14_UNSETTRANSFER, &csBlock); /* Free area */
+
+ VirtualUnlock(apAreas[hand][wArea], auAreas[hand][wArea]);/* Unlock */
+ apAreas[hand][wArea] = NULL; /* Clear locations */
+ auAreas[hand][wArea] = 0;
+ }
+ return sErr;
+#endif
+#ifdef LINUX
+ return (sErr == U14ERR_NOERROR) ? CED_UnsetTransfer(aHand1401[hand], wArea) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14SetTransArea Sets an area up to be used for transfers
+** WORD wArea The area number to set up
+** void *pvBuff The address of the buffer for the data.
+** DWORD dwLength The length of the buffer for the data
+** short eSz The element size (used for byte swapping on the Mac)
+****************************************************************************/
+U14API(short) U14SetTransArea(short hand, WORD wArea, void *pvBuff,
+ DWORD dwLength, short eSz)
+{
+ TRANSFERDESC td;
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+ if (wArea >= MAX_TRANSAREAS) // Is this a valid area number
+ return U14ERR_BADAREA;
+
+#ifdef _IS_WINDOWS_
+ assert(apAreas[hand][wArea] == NULL);
+ assert(auAreas[hand][wArea] == 0);
+
+ apAreas[hand][wArea] = pvBuff; /* Save data for later */
+ auAreas[hand][wArea] = dwLength;
+
+ if (!VirtualLock(pvBuff, dwLength)) /* Lock using WIN32 calls */
+ {
+ apAreas[hand][wArea] = NULL; /* Clear locations */
+ auAreas[hand][wArea] = 0;
+ return U14ERR_LOCKERR; /* VirtualLock failed */
+ }
+#ifndef _WIN64
+ if (!USE_NT_DIOC(hand)) /* Use Win 9x DIOC? */
+ {
+ DWORD dwBytes;
+ VXTRANSFERDESC vxDesc; /* Structure to pass to VXD */
+ vxDesc.wArea = wArea; /* Copy across simple params */
+ vxDesc.dwLength = dwLength;
+
+ // Check we are not asking an old driver for more than area 0
+ if ((wArea != 0) && (U14DriverVersion(hand) < 0x00010002L))
+ sErr = U14ERR_DRIVTOOOLD;
+ else
+ {
+ vxDesc.dwAddrOfs = (DWORD)pvBuff; /* 32 bit offset */
+ vxDesc.wAddrSel = 0;
+
+ if (DeviceIoControl(aHand1401[hand], (DWORD)U14_SETTRANSFER,
+ pvBuff,dwLength, /* Will translate pointer */
+ &vxDesc,sizeof(VXTRANSFERDESC),
+ &dwBytes,NULL))
+ {
+ if (dwBytes >= sizeof(VXTRANSFERDESC)) /* Driver OK ? */
+ sErr = U14ERR_NOERROR;
+ else
+ sErr = U14ERR_DRIVCOMMS; /* Else never got there */
+ }
+ else
+ sErr = (short)GetLastError();
+ }
+ }
+ else
+#endif
+ {
+ PARAMBLK rWork;
+ DWORD dwBytes;
+ td.wArea = wArea; /* Pure NT - put data into struct */
+ td.lpvBuff = pvBuff;
+ td.dwLength = dwLength;
+ td.eSize = 0; // Dummy element size
+
+ if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETTRANSFER,
+ &td,sizeof(TRANSFERDESC),
+ &rWork,sizeof(PARAMBLK),&dwBytes,NULL))
+ {
+ if (dwBytes >= sizeof(PARAMBLK)) // maybe error from driver?
+ sErr = rWork.sState; // will report any error
+ else
+ sErr = U14ERR_DRIVCOMMS; // Else never got there
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+
+ if (sErr != U14ERR_NOERROR)
+ {
+ if (sErr != U14ERR_LOCKERR) // unless lock failed...
+ VirtualUnlock(pvBuff, dwLength); // ...release the lock
+ apAreas[hand][wArea] = NULL; // Clear locations
+ auAreas[hand][wArea] = 0;
+ }
+
+ return sErr;
+#endif
+#ifdef LINUX
+ // The strange cast is so that it works in 64 and 32-bit linux as long is 64-bits
+ // in the 64 bit version.
+ td.lpvBuff = (long long)((unsigned long)pvBuff);
+ td.wAreaNum = wArea;
+ td.dwLength = dwLength;
+ td.eSize = eSz; // Dummy element size
+ return CED_SetTransfer(aHand1401[hand], &td);
+#endif
+}
+
+/****************************************************************************
+** U14SetTransferEvent Sets an event for notification of application
+** wArea The tranfer area index, from 0 to MAXAREAS-1
+** bEvent True to create an event, false to remove it
+** bToHost Set 0 for notification on to1401 tranfers, 1 for
+** notification of transfers to the host PC
+** dwStart The offset of the sub-area of interest
+** dwLength The size of the sub-area of interest
+**
+** The device driver will set the event supplied to the signalled state
+** whenever a DMA transfer to/from the specified area is completed. The
+** transfer has to be in the direction specified by bToHost, and overlap
+** that part of the whole transfer area specified by dwStart and dwLength.
+** It is important that this function is called with bEvent false to release
+** the event once 1401 activity is finished.
+**
+** Returns 1 if an event handle exists, 0 if all OK and no event handle or
+** a negative code for an error.
+****************************************************************************/
+U14API(short) U14SetTransferEvent(short hand, WORD wArea, BOOL bEvent,
+ BOOL bToHost, DWORD dwStart, DWORD dwLength)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14TransferFlags(hand); // see if we can handle events
+ if (sErr >= U14ERR_NOERROR) // check handle is OK
+ {
+ bEvent = bEvent && ((sErr & U14TF_NOTIFY) != 0); // remove request if we cannot do events
+ if (wArea >= MAX_TRANSAREAS) // Check a valid area...
+ return U14ERR_BADAREA; // ...and bail of not
+
+ // We can hold an event for each area, so see if we need to change the
+ // state of the event.
+ if ((bEvent != 0) != (aXferEvent[hand] != 0)) // change of event state?
+ {
+ if (bEvent) // want one and none present
+ aXferEvent[hand] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ else
+ {
+ CloseHandle(aXferEvent[hand]); // clear the existing event
+ aXferEvent[hand] = NULL; // and clear handle
+ }
+ }
+
+ // We have to store the parameters differently for 64-bit operations
+ // because a handle is 64 bits long. The drivers know of this and
+ // handle the information appropriately.
+#ifdef _WIN64
+ csBlock.longs[0] = wArea; // Pass paramaters into the driver...
+ if (bToHost != 0) // The direction flag is held in the
+ csBlock.longs[0] |= 0x10000; // upper word of the transfer area value
+ *((HANDLE*)&csBlock.longs[1]) = aXferEvent[hand]; // The event handle is 64-bits
+ csBlock.longs[3] = dwStart; // Thankfully these two remain
+ csBlock.longs[4] = dwLength; // as unsigned 32-bit values
+#else
+ csBlock.longs[0] = wArea; // pass paramaters into the driver...
+ csBlock.longs[1] = (long)aXferEvent[hand]; // ...especially the event handle
+ csBlock.longs[2] = bToHost;
+ csBlock.longs[3] = dwStart;
+ csBlock.longs[4] = dwLength;
+#endif
+ sErr = U14Control1401(hand, U14_SETTRANSEVENT, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = (short)(aXferEvent[hand] != NULL); // report if we have a flag
+ }
+
+ return sErr;
+#endif
+#ifdef LINUX
+ TRANSFEREVENT te;
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ if (wArea >= MAX_TRANSAREAS) // Is this a valid area number
+ return U14ERR_BADAREA;
+
+ te.wAreaNum = wArea; // copy parameters to the control block
+ te.wFlags = bToHost ? 1 : 0; // bit 0 sets the direction
+ te.dwStart = dwStart; // start offset of the event area
+ te.dwLength = dwLength; // size of the event area
+ te.iSetEvent = bEvent; // in Windows, this creates/destroys the event
+ return CED_SetEvent(aHand1401[hand], &te);
+#endif
+}
+
+/****************************************************************************
+** U14TestTransferEvent
+** Would a U14WaitTransferEvent() call return immediately? return 1 if so,
+** 0 if not or a negative code if a problem.
+****************************************************************************/
+U14API(int) U14TestTransferEvent(short hand, WORD wArea)
+{
+#ifdef _IS_WINDOWS_
+ int iErr = CheckHandle(hand);
+ if (iErr == U14ERR_NOERROR)
+ {
+ if (aXferEvent[hand]) // if a handle is set...
+ iErr = WaitForSingleObject(aXferEvent[hand], 0) == WAIT_OBJECT_0;
+ }
+ return iErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_TestEvent(aHand1401[hand], wArea) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14WaitTransferEvent
+** Wait for a transfer event with a timeout.
+** msTimeOut is 0 for an infinite wait, else it is the maximum time to wait
+** in milliseconds in range 0-0x00ffffff.
+** Returns If no event handle then return immediately. Else return 1 if
+** timed out or 0=event, and a negative code if a problem.
+****************************************************************************/
+U14API(int) U14WaitTransferEvent(short hand, WORD wArea, int msTimeOut)
+{
+#ifdef _IS_WINDOWS_
+ int iErr = CheckHandle(hand);
+ if (iErr == U14ERR_NOERROR)
+ {
+ if (aXferEvent[hand])
+ {
+ if (msTimeOut == 0)
+ msTimeOut = INFINITE;
+ iErr = WaitForSingleObject(aXferEvent[hand], msTimeOut) != WAIT_OBJECT_0;
+ }
+ else
+ iErr = TRUE; // say we timed out if no event
+ }
+ return iErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_WaitEvent(aHand1401[hand], wArea, msTimeOut) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14SetCircular Sets an area up for circular DMA transfers
+** WORD wArea The area number to set up
+** BOOL bToHost Sets the direction of data transfer
+** void *pvBuff The address of the buffer for the data
+** DWORD dwLength The length of the buffer for the data
+****************************************************************************/
+U14API(short) U14SetCircular(short hand, WORD wArea, BOOL bToHost,
+ void *pvBuff, DWORD dwLength)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ if (wArea >= MAX_TRANSAREAS) /* Is this a valid area number */
+ return U14ERR_BADAREA;
+
+ if (!bToHost) /* For now, support tohost transfers only */
+ return U14ERR_BADAREA; /* best error code I can find */
+#ifdef _IS_WINDOWS_
+ assert(apAreas[hand][wArea] == NULL);
+ assert(auAreas[hand][wArea] == 0);
+
+ apAreas[hand][wArea] = pvBuff; /* Save data for later */
+ auAreas[hand][wArea] = dwLength;
+
+ if (!VirtualLock(pvBuff, dwLength)) /* Lock using WIN32 calls */
+ sErr = U14ERR_LOCKERR; /* VirtualLock failed */
+ else
+ {
+ PARAMBLK rWork;
+ DWORD dwBytes;
+ TRANSFERDESC txDesc;
+ txDesc.wArea = wArea; /* Pure NT - put data into struct */
+ txDesc.lpvBuff = pvBuff;
+ txDesc.dwLength = dwLength;
+ txDesc.eSize = (short)bToHost; /* Use this for direction flag */
+
+ if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETCIRCULAR,
+ &txDesc, sizeof(TRANSFERDESC),
+ &rWork, sizeof(PARAMBLK),&dwBytes,NULL))
+ {
+ if (dwBytes >= sizeof(PARAMBLK)) /* error from driver? */
+ sErr = rWork.sState; /* No, just return driver data */
+ else
+ sErr = U14ERR_DRIVCOMMS; /* Else never got there */
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+
+ if (sErr != U14ERR_NOERROR)
+ {
+ if (sErr != U14ERR_LOCKERR)
+ VirtualUnlock(pvBuff, dwLength); /* Release NT lock */
+ apAreas[hand][wArea] = NULL; /* Clear locations */
+ auAreas[hand][wArea] = 0;
+ }
+
+ return sErr;
+#endif
+#ifdef LINUX
+ else
+ {
+ TRANSFERDESC td;
+ td.lpvBuff = (long long)((unsigned long)pvBuff);
+ td.wAreaNum = wArea;
+ td.dwLength = dwLength;
+ td.eSize = (short)bToHost; /* Use this for direction flag */
+ return CED_SetCircular(aHand1401[hand], &td);
+ }
+#endif
+}
+
+/****************************************************************************
+** Function GetCircBlk returns the size (& start offset) of the next
+** available block of circular data.
+****************************************************************************/
+U14API(int) U14GetCircBlk(short hand, WORD wArea, DWORD *pdwOffs)
+{
+ int lErr = CheckHandle(hand);
+ if (lErr != U14ERR_NOERROR)
+ return lErr;
+
+ if (wArea >= MAX_TRANSAREAS) // Is this a valid area number?
+ return U14ERR_BADAREA;
+ else
+ {
+#ifdef _IS_WINDOWS_
+ PARAMBLK rWork;
+ TCSBLOCK csBlock;
+ DWORD dwBytes;
+ csBlock.longs[0] = wArea; // Area number into control block
+ rWork.sState = U14ERR_DRIVCOMMS;
+ if (DeviceIoControl(aHand1401[hand], (DWORD)U14_GETCIRCBLK, &csBlock, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
+ (dwBytes >= sizeof(PARAMBLK)))
+ lErr = rWork.sState;
+ else
+ lErr = U14ERR_DRIVCOMMS;
+
+ if (lErr == U14ERR_NOERROR) // Did everything go OK?
+ { // Yes, we can pass the results back
+ lErr = rWork.csBlock.longs[1]; // Return the block information
+ *pdwOffs = rWork.csBlock.longs[0]; // Offset is first in array
+ }
+#endif
+#ifdef LINUX
+ TCIRCBLOCK cb;
+ cb.nArea = wArea; // Area number into control block
+ cb.dwOffset = 0;
+ cb.dwSize = 0;
+ lErr = CED_GetCircBlock(aHand1401[hand], &cb);
+ if (lErr == U14ERR_NOERROR) // Did everything go OK?
+ { // Yes, we can pass the results back
+ lErr = cb.dwSize; // return the size
+ *pdwOffs = cb.dwOffset; // and the offset
+ }
+#endif
+ }
+ return lErr;
+}
+
+/****************************************************************************
+** Function FreeCircBlk marks the specified area of memory as free for
+** resuse for circular transfers and returns the size (& start
+** offset) of the next available block of circular data.
+****************************************************************************/
+U14API(int) U14FreeCircBlk(short hand, WORD wArea, DWORD dwOffs, DWORD dwSize,
+ DWORD *pdwOffs)
+{
+ int lErr = CheckHandle(hand);
+ if (lErr != U14ERR_NOERROR)
+ return lErr;
+
+ if (wArea < MAX_TRANSAREAS) // Is this a valid area number
+ {
+#ifdef _IS_WINDOWS_
+ PARAMBLK rWork;
+ TCSBLOCK csBlock;
+ DWORD dwBytes;
+ csBlock.longs[0] = wArea; // Area number into control block
+ csBlock.longs[1] = dwOffs;
+ csBlock.longs[2] = dwSize;
+ rWork.sState = U14ERR_DRIVCOMMS;
+ if (DeviceIoControl(aHand1401[hand], (DWORD)U14_FREECIRCBLK, &csBlock, sizeof(TCSBLOCK),
+ &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
+ (dwBytes >= sizeof(PARAMBLK)))
+ lErr = rWork.sState;
+ else
+ lErr = U14ERR_DRIVCOMMS;
+ if (lErr == U14ERR_NOERROR) // Did everything work OK?
+ { // Yes, we can pass the results back
+ lErr = rWork.csBlock.longs[1]; // Return the block information
+ *pdwOffs = rWork.csBlock.longs[0]; // Offset is first in array
+ }
+#endif
+#ifdef LINUX
+ TCIRCBLOCK cb;
+ cb.nArea = wArea; // Area number into control block
+ cb.dwOffset = dwOffs;
+ cb.dwSize = dwSize;
+
+ lErr = CED_FreeCircBlock(aHand1401[hand], &cb);
+ if (lErr == U14ERR_NOERROR) // Did everything work OK?
+ { // Yes, we can pass the results back
+ lErr = cb.dwSize; // Return the block information
+ *pdwOffs = cb.dwOffset; // Offset is first in array
+ }
+#endif
+ }
+ else
+ lErr = U14ERR_BADAREA;
+
+ return lErr;
+}
+
+/****************************************************************************
+** Transfer
+** Transfer moves data to 1401 or to host
+** Assumes memory is allocated and locked,
+** which it should be to get a pointer
+*****************************************************************************/
+static short Transfer(short hand, BOOL bTo1401, char* pData,
+ DWORD dwSize, DWORD dw1401, short eSz)
+{
+ char strcopy[MAXSTRLEN+1]; // to hold copy of work string
+ short sResult = U14SetTransArea(hand, 0, (void *)pData, dwSize, eSz);
+ if (sResult == U14ERR_NOERROR) // no error
+ {
+ sprintf(strcopy, // data offset is always 0
+ "TO%s,$%X,$%X,0;", bTo1401 ? "1401" : "HOST", dw1401, dwSize);
+
+ U14SendString(hand, strcopy); // send transfer string
+
+ sResult = U14CheckErr(hand); // Use ERR command to check for done
+ if (sResult > 0)
+ sResult = U14ERR_TOXXXERR; // If a 1401 error, use this code
+
+ U14UnSetTransfer(hand, 0);
+ }
+ return sResult;
+}
+
+/****************************************************************************
+** Function ToHost transfers data into the host from the 1401
+****************************************************************************/
+U14API(short) U14ToHost(short hand, char* pAddrHost, DWORD dwSize,
+ DWORD dw1401, short eSz)
+{
+ short sErr = CheckHandle(hand);
+ if ((sErr == U14ERR_NOERROR) && dwSize) // TOHOST is a constant
+ sErr = Transfer(hand, TOHOST, pAddrHost, dwSize, dw1401, eSz);
+ return sErr;
+}
+
+/****************************************************************************
+** Function To1401 transfers data into the 1401 from the host
+****************************************************************************/
+U14API(short) U14To1401(short hand, const char* pAddrHost,DWORD dwSize,
+ DWORD dw1401, short eSz)
+{
+ short sErr = CheckHandle(hand);
+ if ((sErr == U14ERR_NOERROR) && dwSize) // TO1401 is a constant
+ sErr = Transfer(hand, TO1401, (char*)pAddrHost, dwSize, dw1401, eSz);
+ return sErr;
+}
+
+/****************************************************************************
+** Function LdCmd Loads a command from a full path or just a file
+*****************************************************************************/
+#ifdef _IS_WINDOWS_
+#define file_exist(name) (_access(name, 0) != -1)
+#define file_open(name) _lopen(name, OF_READ)
+#define file_close(h) _lclose(h)
+#define file_seek(h, pos) _llseek(h, pos, FILE_BEGIN)
+#define file_read(h, buffer, size) (_lread(h, buffer, size) == size)
+#endif
+#ifdef LINUX
+#define file_exist(name) (access(name, F_OK) != -1)
+#define file_open(name) open(name, O_RDONLY)
+#define file_close(h) close(h)
+#define file_seek(h, pos) lseek(h, pos, SEEK_SET)
+#define file_read(h, buffer, size) (read(h, buffer, size) == (ssize_t)size)
+static DWORD GetModuleFileName(void* dummy, char* buffer, int max)
+{
+ // The following works for Linux systems with a /proc file system.
+ char szProcPath[32];
+ sprintf(szProcPath, "/proc/%d/exe", getpid()); // attempt to read link
+ if (readlink(szProcPath, buffer, max) != -1)
+ {
+ dirname (buffer);
+ strcat (buffer, "/");
+ return strlen(buffer);
+ }
+ return 0;
+}
+#endif
+
+U14API(short) U14LdCmd(short hand, const char* command)
+{
+ char strcopy[MAXSTRLEN+1]; // to hold copy of work string
+ BOOL bGotIt = FALSE; // have we found the command file?
+ int iFHandle; // file handle of command
+#define FNSZ 260
+ char filnam[FNSZ]; // space to build name in
+ char szCmd[25]; // just the command name with extension
+
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ if (strchr(command, '.') != NULL) // see if we have full name
+ {
+ if (file_exist(command)) // If the file exists
+ {
+ strcpy(filnam, command); // use name as is
+ bGotIt = TRUE; // Flag no more searching
+ }
+ else // not found, get file name for search
+ {
+ char* pStr = strrchr(command, PATHSEP); // Point to last separator
+ if (pStr != NULL) // Check we got it
+ {
+ pStr++; // move past the backslash
+ strcpy(szCmd, pStr); // copy file name as is
+ }
+ else
+ strcpy(szCmd, command); // use as is
+ }
+ }
+ else // File extension not supplied, so build the command file name
+ {
+ char szExt[8];
+ strcpy(szCmd, command); // Build command file name
+ ExtForType(asType1401[hand], szExt);// File extension string
+ strcat(szCmd, szExt); // add it to the end
+ }
+
+ // Next place to look is in the 1401 folder in the same place as the
+ // application was run from.
+ if (!bGotIt) // Still not got it?
+ {
+ DWORD dwLen = GetModuleFileName(NULL, filnam, FNSZ); // Get app path
+ if (dwLen > 0) // and use it as path if found
+ {
+ char* pStr = strrchr(filnam, PATHSEP); // Point to last separator
+ if (pStr != NULL)
+ {
+ *(++pStr) = 0; // Terminate string there
+ if (strlen(filnam) < FNSZ-6) // make sure we have space
+ {
+ strcat(filnam, "1401" PATHSEPSTR); // add in 1401 subdir
+ strcat(filnam,szCmd);
+ bGotIt = (BOOL)file_exist(filnam); // See if file exists
+ }
+ }
+ }
+ }
+
+ // Next place to look is in whatever path is set by the 1401DIR environment
+ // variable, if it exists.
+ if (!bGotIt) // Need to do more searches?/
+ {
+ char* pStr = getenv("1401DIR"); // Try to find environment var
+ if (pStr != NULL) // and use it as path if found
+ {
+ strcpy(filnam, pStr); // Use path in environment
+ if (filnam[strlen(filnam)-1] != PATHSEP)// We need separator
+ strcat(filnam, PATHSEPSTR);
+ strcat(filnam, szCmd);
+ bGotIt = (BOOL)file_exist(filnam); // Got this one?
+ }
+ }
+
+ // Last place to look is the default location.
+ if (!bGotIt) // Need to do more searches?
+ {
+ strcpy(filnam, DEFCMDPATH); // Use default path
+ strcat(filnam, szCmd);
+ bGotIt = file_exist(filnam); // Got this one?
+ }
+
+ iFHandle = file_open(filnam);
+ if (iFHandle == -1)
+ sErr = U14ERR_NOFILE;
+ else
+ { // first read in the header block
+ CMDHEAD rCmdHead; // to hold the command header
+ if (file_read(iFHandle, &rCmdHead, sizeof(CMDHEAD)))
+ {
+ size_t nComSize = rCmdHead.wCmdSize;
+ char* pMem = malloc(nComSize);
+ if (pMem != NULL)
+ {
+ file_seek(iFHandle, sizeof(CMDHEAD));
+ if (file_read(iFHandle, pMem, (UINT)nComSize))
+ {
+ sErr = U14SetTransArea(hand, 0, (void *)pMem, (DWORD)nComSize, ESZBYTES);
+ if (sErr == U14ERR_NOERROR)
+ {
+ sprintf(strcopy, "CLOAD,0,$%X;", (int)nComSize);
+ sErr = U14SendString(hand, strcopy);
+ if (sErr == U14ERR_NOERROR)
+ {
+ sErr = U14CheckErr(hand); // Use ERR to check for done
+ if (sErr > 0)
+ sErr = U14ERR_CLOADERR; // If an error, this code
+ }
+ U14UnSetTransfer(hand, 0); // release transfer area
+ }
+ }
+ else
+ sErr = U14ERR_READERR;
+ free(pMem);
+ }
+ else
+ sErr = U14ERR_HOSTSPACE; // memory allocate failed
+ }
+ else
+ sErr = U14ERR_READERR;
+
+ file_close(iFHandle); // close the file
+ }
+
+ return sErr;
+}
+
+
+/****************************************************************************
+** Ld
+** Loads a command into the 1401
+** Returns NOERROR code or a long with error in lo word and index of
+** command that failed in high word
+****************************************************************************/
+U14API(DWORD) U14Ld(short hand, const char* vl, const char* str)
+{
+ DWORD dwIndex = 0; // index to current command
+ long lErr = U14ERR_NOERROR; // what the error was that went wrong
+ char strcopy[MAXSTRLEN+1]; // stores unmodified str parameter
+ char szFExt[8]; // The command file extension
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ ExtForType(asType1401[hand], szFExt); // File extension string
+ strcpy(strcopy, str); // to avoid changing original
+
+ // now break out one command at a time and see if loaded
+ if (*str) // if anything there
+ {
+ BOOL bDone = FALSE; // true when finished all commands
+ int iLoop1 = 0; // Point at start of string for command name
+ int iLoop2 = 0; // and at start of str parameter
+ do // repeat until end of str
+ {
+ char filnam[MAXSTRLEN+1]; // filename to use
+ char szFName[MAXSTRLEN+1]; // filename work string
+
+ if (!strcopy[iLoop1]) // at the end of the string?
+ bDone = TRUE; // set the finish flag
+
+ if (bDone || (strcopy[iLoop1] == ',')) // end of cmd?
+ {
+ U14LONG er[5]; // Used to read back error results
+ ++dwIndex; // Keep count of command number, first is 1
+ szFName[iLoop2]=(char)0; // null terminate name of command
+
+ strncpy(szLastName, szFName, sizeof(szLastName)); // Save for error info
+ szLastName[sizeof(szLastName)-1] = 0;
+ strncat(szLastName, szFExt, sizeof(szLastName)); // with extension included
+ szLastName[sizeof(szLastName)-1] = 0;
+
+ U14SendString(hand, szFName); // ask if loaded
+ U14SendString(hand, ";ERR;"); // add err return
+
+ lErr = U14LongsFrom1401(hand, er, 5);
+ if (lErr > 0)
+ {
+ lErr = U14ERR_NOERROR;
+ if (er[0] == 255) // if command not loaded at all
+ {
+ if (vl && *vl) // if we have a path name
+ {
+ strcpy(filnam, vl);
+ if (strchr("\\/:", filnam[strlen(filnam)-1]) == NULL)
+ strcat(filnam, PATHSEPSTR); // add separator if none found
+ strcat(filnam, szFName); // add the file name
+ strcat(filnam, szFExt); // and extension
+ }
+ else
+ strcpy(filnam, szFName); // simple name
+
+ lErr = U14LdCmd(hand, filnam); // load cmd
+ if (lErr != U14ERR_NOERROR) // spot any errors
+ bDone = TRUE; // give up if an error
+ }
+ }
+ else
+ bDone = TRUE; // give up if an error
+
+ iLoop2 = 0; // Reset pointer to command name string
+ ++iLoop1; // and move on through str parameter
+ }
+ else
+ szFName[iLoop2++] = strcopy[iLoop1++]; // no command end, so copy 1 char
+ }
+ while (!bDone);
+ }
+
+ if (lErr == U14ERR_NOERROR)
+ {
+ szLastName[0] = 0; // No error, so clean out command name here
+ return lErr;
+ }
+ else
+ return ((dwIndex<<16) | ((DWORD)lErr & 0x0000FFFF));
+}
+
+// Initialise the library (if not initialised) and return the library version
+U14API(int) U14InitLib(void)
+{
+ int iRetVal = U14LIB_VERSION;
+ if (iAttached == 0) // only do this the first time please
+ {
+ int i;
+#ifdef _IS_WINDOWS_
+ int j;
+ DWORD dwVersion = GetVersion();
+ bWindows9x = FALSE; // Assume not Win9x
+
+ if (dwVersion & 0x80000000) // if not windows NT
+ {
+ if ((LOBYTE(LOWORD(dwVersion)) < 4) && // if Win32s or...
+ (HIBYTE(LOWORD(dwVersion)) < 95)) // ...below Windows 95
+ iRetVal = 0; // We do not support this
+ else
+ bWindows9x = TRUE; // Flag we have Win9x
+ }
+#endif
+
+ for (i = 0; i < MAX1401; i++) // initialise the device area
+ {
+ aHand1401[i] = INVALID_HANDLE_VALUE; // Clear handle values
+ asType1401[i] = U14TYPEUNKNOWN; // and 1401 type codes
+ alTimeOutPeriod[i] = 3000; // 3 second timeouts
+#ifdef _IS_WINDOWS_
+#ifndef _WIN64
+ abUseNTDIOC[i] = (BOOL)!bWindows9x;
+#endif
+ aXferEvent[i] = NULL; // there are no Xfer events
+ for (j = 0; j < MAX_TRANSAREAS; j++) // Clear out locked area info
+ {
+ apAreas[i][j] = NULL;
+ auAreas[i][j] = 0;
+ }
+#endif
+ }
+ }
+ return iRetVal;
+}
+
+///--------------------------------------------------------------------------------
+/// Functions called when the library is loaded and unloaded to give us a chance to
+/// setup the library.
+
+
+#ifdef _IS_WINDOWS_
+#ifndef U14_NOT_DLL
+/****************************************************************************
+** FUNCTION: DllMain(HANDLE, DWORD, LPVOID)
+** LibMain is called by Windows when the DLL is initialized, Thread Attached,
+** and other times. Refer to SDK documentation, as to the different ways this
+** may be called.
+****************************************************************************/
+INT APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved)
+{
+ int iRetVal = 1;
+
+ switch (ul_reason_being_called)
+ {
+ case DLL_PROCESS_ATTACH:
+ iRetVal = U14InitLib() > 0; // does nothing if iAttached != 0
+ ++iAttached; // count times attached
+ break;
+
+ case DLL_PROCESS_DETACH:
+ if (--iAttached == 0) // last man out?
+ U14CloseAll(); // release all open handles
+ break;
+ }
+ return iRetVal;
+
+ UNREFERENCED_PARAMETER(lpReserved);
+}
+#endif
+#endif
+#ifdef LINUX
+void __attribute__((constructor)) use1401_load(void)
+{
+ U14InitLib();
+ ++iAttached;
+}
+
+void __attribute__((destructor)) use1401_unload(void)
+{
+ if (--iAttached == 0) // last man out?
+ U14CloseAll(); // release all open handles
+}
+#endif