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