mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-07-26 02:29:51 +02:00
d79382bbfe
* chore: run codespell on source/header files Fixes most reported typos discovered by codespell (while trying to be unopinionated about American vs. British English). * chore: run codespell on build/readme files
1034 lines
33 KiB
C
1034 lines
33 KiB
C
/* CARDRDR.C (C) Copyright Roger Bowler, 1999-2012 */
|
|
/* ESA/390 Card Reader Device Handler */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module contains device handling functions for emulated */
|
|
/* card reader devices. */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
|
|
#include "hstdinc.h"
|
|
#include "hercules.h"
|
|
|
|
#include "devtype.h"
|
|
#include "sockdev.h"
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Debugging */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
//#define ENABLE_RDR_DEBUG 1 // 1:enable, 0:disable, #undef:default
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Activate debugging when requested */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/* (enable debugging if DEBUG build or specifically requested) */
|
|
#if (!defined(ENABLE_RDR_DEBUG) && defined(DEBUG)) || \
|
|
( defined(ENABLE_RDR_DEBUG) && ENABLE_RDR_DEBUG)
|
|
#define RDR_DEBUG
|
|
#endif
|
|
|
|
/* (activate TRACE/VERIFY statements if debugging is enabled) */
|
|
#if defined(RDR_DEBUG)
|
|
#define ENABLE_TRACING_STMTS 1
|
|
#include "dbgtrace.h"
|
|
#define NO_RDR_OPTIMIZE
|
|
#endif
|
|
|
|
/* (disable optimizations if debugging) */
|
|
#if defined( _MSVC_ ) && defined( NO_RDR_OPTIMIZE )
|
|
#pragma optimize( "", off ) // disable optimizations for reliable breakpoints
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Hercules Dynamic Loader */
|
|
/*-------------------------------------------------------------------*/
|
|
#if defined(WIN32) && !defined(HDL_USE_LIBTOOL) && !defined(_MSVC_)
|
|
SYSBLK *psysblk;
|
|
#define sysblk (*psysblk)
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* ISW 2003/03/07 */
|
|
/* 3505 Byte 1 Sense Codes */
|
|
/*-------------------------------------------------------------------*/
|
|
#define SENSE1_RDR_PERM 0x80 /* Permanent Err key depressed */
|
|
#define SENSE1_RDR_AUTORETRY 0x40 /* Don't know */
|
|
#define SENSE1_RDR_MOTIONMF 0x20 /* Motion Malfunction */
|
|
#define SENSE1_RDR_RAIC 0x10 /* Retry After Intreq Cleared */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Internal macro definitions */
|
|
/*-------------------------------------------------------------------*/
|
|
#define CARD_SIZE 80
|
|
#ifdef _DEBUG
|
|
#define TRAY_CARDS 3 /* (for easier debugging) */
|
|
#else
|
|
#define TRAY_CARDS 2000 /* one full box of cards */
|
|
#endif
|
|
#define HEX40 ((BYTE)0x40)
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize the device handler */
|
|
/*-------------------------------------------------------------------*/
|
|
static int cardrdr_init_handler ( DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
int i; /* Array subscript */
|
|
int fc; /* File counter */
|
|
char pathname[MAX_PATH]; /* file path in host format */
|
|
int sockdev = 0;
|
|
int attn = 0;
|
|
|
|
|
|
/* Raise attention for re-init */
|
|
if(dev->devtype)
|
|
attn = 1;
|
|
|
|
/* For re-initialisation close the existing file */
|
|
if (dev->fd >= 0)
|
|
(dev->hnd->close)(dev);
|
|
|
|
if (dev->bs)
|
|
{
|
|
if (!unbind_device(dev))
|
|
{
|
|
// (error message already issued)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Initialize device dependent fields */
|
|
|
|
dev->fd = -1;
|
|
dev->fh = NULL;
|
|
dev->multifile = 0;
|
|
dev->ebcdic = 0;
|
|
dev->ascii = 0;
|
|
dev->trunc = 0;
|
|
dev->cardpos = 0;
|
|
dev->cardrem = 0;
|
|
dev->autopad = 0;
|
|
|
|
dev->excps = 0;
|
|
|
|
if(!sscanf(dev->typname,"%hx",&(dev->devtype)))
|
|
dev->devtype = 0x2501;
|
|
|
|
fc = 0;
|
|
|
|
if (dev->more_files) free (dev->more_files);
|
|
|
|
dev->more_files = malloc(sizeof(char*) * (fc + 1));
|
|
|
|
if (!dev->more_files)
|
|
{
|
|
char buf[40];
|
|
MSGBUF(buf, "malloc(%d)", (int)(sizeof(char) * (fc + 1)) );
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", buf, strerror( errno ));
|
|
return -1;
|
|
}
|
|
|
|
dev->more_files[fc] = NULL;
|
|
|
|
/* Process the driver arguments starting with the SECOND
|
|
argument. (The FIRST argument is the filename and is
|
|
checked later further below.) */
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
/* sockdev means the device file is actually
|
|
a connected socket instead of a disk file.
|
|
The file name is the socket_spec (host:port)
|
|
to listen for connections on. */
|
|
|
|
if (strcasecmp(argv[i], "sockdev") == 0)
|
|
{
|
|
sockdev = 1;
|
|
continue;
|
|
}
|
|
|
|
/* multifile means to automatically open the next
|
|
i/p file if multiple i/p files are defined. */
|
|
|
|
if (strcasecmp(argv[i], "multifile") == 0)
|
|
{
|
|
dev->multifile = 1;
|
|
continue;
|
|
}
|
|
|
|
/* eof means that unit exception will be returned at
|
|
end of file, instead of intervention required */
|
|
|
|
if (strcasecmp(argv[i], "eof") == 0)
|
|
{
|
|
dev->rdreof = 1;
|
|
continue;
|
|
}
|
|
|
|
/* intrq means that intervention required will be returned at
|
|
end of file, instead of unit exception */
|
|
|
|
if (strcasecmp(argv[i], "intrq") == 0)
|
|
{
|
|
dev->rdreof = 0;
|
|
continue;
|
|
}
|
|
|
|
/* ebcdic means that the card image file consists of
|
|
fixed length 80-byte EBCDIC card images with no
|
|
line-end delimiters */
|
|
|
|
if (strcasecmp(argv[i], "ebcdic") == 0)
|
|
{
|
|
dev->ebcdic = 1;
|
|
continue;
|
|
}
|
|
|
|
/* ascii means that the card image file consists of
|
|
variable length ASCII records delimited by either
|
|
line-feed or carriage-return line-feed sequences */
|
|
|
|
if (strcasecmp(argv[i], "ascii") == 0)
|
|
{
|
|
dev->ascii = 1;
|
|
continue;
|
|
}
|
|
|
|
/* trunc means that records longer than 80 bytes will
|
|
be silently truncated to 80 bytes when processing a
|
|
variable length ASCII file. The default behaviour
|
|
is to present a data check if an overlength record
|
|
is encountered. The trunc option is ignored except
|
|
when processing an ASCII card image file. */
|
|
|
|
if (strcasecmp(argv[i], "trunc") == 0)
|
|
{
|
|
dev->trunc = 1;
|
|
continue;
|
|
}
|
|
|
|
/* autopad means that if reading fixed sized records
|
|
* (ebcdic) and end of file is reached in the middle of
|
|
* a record, the record is automatically padded to 80 bytes.
|
|
*/
|
|
|
|
if (strcasecmp(argv[i], "autopad") == 0)
|
|
{
|
|
dev->autopad = 1;
|
|
continue;
|
|
}
|
|
|
|
// add additional file arguments
|
|
|
|
if (strlen(argv[i]) >= sizeof(dev->filename))
|
|
{
|
|
WRMSG (HHC01201, "E", LCSS_DEVNUM, argv[i], (unsigned int)sizeof(dev->filename)-1);
|
|
return -1;
|
|
}
|
|
|
|
if (access(argv[i], R_OK | F_OK) != 0)
|
|
{
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "access()", strerror( errno ));
|
|
return -1;
|
|
}
|
|
hostpath(pathname, argv[i], sizeof(pathname));
|
|
dev->more_files[fc++] = strdup(pathname);
|
|
dev->more_files = realloc(dev->more_files, sizeof(char*) * (fc + 1));
|
|
|
|
if (!dev->more_files)
|
|
{
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "strdup()", strerror( errno ));
|
|
return -1;
|
|
}
|
|
|
|
dev->more_files[fc] = NULL;
|
|
}
|
|
|
|
dev->current_file = dev->more_files;
|
|
|
|
/* Check for conflicting arguments */
|
|
|
|
if (dev->ebcdic && dev->ascii)
|
|
{
|
|
WRMSG (HHC01202, "E", LCSS_DEVNUM);
|
|
return -1;
|
|
}
|
|
|
|
if (sockdev)
|
|
{
|
|
if (fc)
|
|
{
|
|
WRMSG (HHC01203, "E", LCSS_DEVNUM);
|
|
return -1;
|
|
}
|
|
|
|
// If neither ascii nor ebcdic is specified, default to ascii.
|
|
// This is required for socket devices because the open logic,
|
|
// if neither is specified, attempts to determine whether the data
|
|
// is actually ascii or ebcdic by reading the 1st 160 bytes of
|
|
// data and then rewinding to the beginning of the file afterwards.
|
|
// Since you can't "rewind" a socket, we must therefore default
|
|
// to one of them.
|
|
|
|
if (!dev->ebcdic && !dev->ascii)
|
|
{
|
|
WRMSG (HHC01204, "I", LCSS_DEVNUM);
|
|
dev->ascii = 1;
|
|
}
|
|
}
|
|
|
|
if (dev->multifile && !fc)
|
|
{
|
|
WRMSG (HHC01205, "W", LCSS_DEVNUM);
|
|
dev->multifile = 0;
|
|
}
|
|
|
|
/* The first argument is the file name */
|
|
|
|
if (argc > 0)
|
|
{
|
|
/* Check for valid file name */
|
|
|
|
if (strlen(argv[0]) >= sizeof(dev->filename))
|
|
{
|
|
WRMSG (HHC01201, "E", LCSS_DEVNUM, argv[0], (unsigned int)sizeof(dev->filename)-1);
|
|
return -1;
|
|
}
|
|
|
|
if (!sockdev)
|
|
{
|
|
/* Check for specification of no file mounted on reader */
|
|
if (argv[0][0] == '*')
|
|
{
|
|
dev->filename[0] = '\0';
|
|
}
|
|
else if (access(argv[0], R_OK | F_OK) != 0)
|
|
{
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "access()", strerror( errno ));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Save the file name in the device block */
|
|
hostpath(dev->filename, argv[0], sizeof(dev->filename));
|
|
}
|
|
else
|
|
{
|
|
dev->filename[0] = '\0';
|
|
}
|
|
|
|
/* Set size of i/o buffer */
|
|
dev->bufsize = CARD_SIZE;
|
|
|
|
if (sockdev && dev->ascii)
|
|
{
|
|
/* Allocate extra room for socket i/o buffer */
|
|
dev->bufsize += (TRAY_CARDS * CARD_SIZE);
|
|
dev->buflen = dev->bufsize;
|
|
dev->bufoff = dev->buflen;
|
|
}
|
|
|
|
/* Set number of sense bytes */
|
|
|
|
/* ISW 20030307 : Empirical knowledge : DOS/VS R34 Erep */
|
|
/* indicates 4 bytes in 3505 sense */
|
|
dev->numsense = 4;
|
|
|
|
/* Initialize the device identifier bytes */
|
|
|
|
dev->devid[0] = 0xFF;
|
|
if (0x3505 == dev->devtype || 0x2501 == dev->devtype)
|
|
{
|
|
dev->devid[1] = dev->devtype >> 8;
|
|
dev->devid[2] = dev->devtype;
|
|
}
|
|
else /* 2540 and unknown */
|
|
{
|
|
dev->devid[1] = 0x28; /* Control unit type is 2821-1 */
|
|
dev->devid[2] = 0x21;
|
|
}
|
|
dev->devid[3] = 0x01;
|
|
dev->devid[4] = dev->devtype >> 8;
|
|
dev->devid[5] = dev->devtype & 0xFF;
|
|
dev->devid[6] = 0x01;
|
|
dev->numdevid = 7;
|
|
|
|
// If socket device, create a listening socket
|
|
// to accept connections on.
|
|
|
|
if (attn)
|
|
{
|
|
RELEASE_DEVLOCK( dev );
|
|
{
|
|
device_attention( dev, CSW_DE );
|
|
}
|
|
OBTAIN_DEVLOCK( dev );
|
|
}
|
|
|
|
if (sockdev && !bind_device(dev,dev->filename))
|
|
{
|
|
// (error message already issued)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
} /* end function cardrdr_init_handler */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Query the device definition */
|
|
/*-------------------------------------------------------------------*/
|
|
static void cardrdr_query_device (DEVBLK *dev, char **devclass,
|
|
int buflen, char *buffer)
|
|
{
|
|
char filename[ PATH_MAX + 1 ]; /* full path or just name */
|
|
|
|
BEGIN_DEVICE_CLASS_QUERY( "RDR", dev, devclass, buflen, buffer );
|
|
|
|
snprintf (buffer, buflen, "%s%s%s%s%s%s%s%s IO[%"PRIu64"]",
|
|
((filename[0] == '\0') ? "*" : (char*)filename),
|
|
(dev->bs ? " sockdev" : ""),
|
|
(dev->multifile ? " multifile" : ""),
|
|
(dev->ascii ? " ascii" : ""),
|
|
(dev->ebcdic ? " ebcdic" : ""),
|
|
(dev->autopad ? " autopad" : ""),
|
|
((dev->ascii && dev->trunc) ? " trunc" : ""),
|
|
(dev->rdreof ? " eof" : " intrq"),
|
|
dev->excps );
|
|
|
|
} /* end function cardrdr_query_device */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Close the device */
|
|
/*-------------------------------------------------------------------*/
|
|
static int cardrdr_close_device ( DEVBLK *dev )
|
|
{
|
|
/* Close the device file */
|
|
|
|
if (0
|
|
|| ( dev->bs && dev->fd >= 0 && close_socket( dev->fd ) < 0 )
|
|
|| ( !dev->bs && dev->fh != NULL && fclose( dev->fh ) != 0 )
|
|
)
|
|
{
|
|
int errnum = dev->bs ? get_HSO_errno() : errno;
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "close_socket() or fclose()", strerror( errnum ));
|
|
dev->fd = -1;
|
|
dev->fh = NULL;
|
|
return -1;
|
|
}
|
|
|
|
if (dev->bs && (dev->bs->clientip || dev->bs->clientname))
|
|
{
|
|
WRMSG (HHC01206, "I", LCSS_DEVNUM, dev->bs->clientname, dev->bs->clientip, dev->bs->spec);
|
|
}
|
|
|
|
dev->fd = -1;
|
|
dev->fh = NULL;
|
|
|
|
return 0;
|
|
} /* end function cardrdr_close_device */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Clear the card reader */
|
|
/*-------------------------------------------------------------------*/
|
|
static int clear_cardrdr ( DEVBLK *dev )
|
|
{
|
|
/* Close the card image file */
|
|
if (cardrdr_close_device(dev) != 0) return -1;
|
|
|
|
if (dev->bs) return 0;
|
|
|
|
/* Clear the file name */
|
|
dev->filename[0] = '\0';
|
|
|
|
/* If next file is available, open it */
|
|
if (dev->current_file && *(dev->current_file))
|
|
{
|
|
hostpath(dev->filename, *(dev->current_file++), sizeof(dev->filename));
|
|
}
|
|
else
|
|
{
|
|
/* Reset the device dependent flags */
|
|
dev->multifile = 0;
|
|
dev->ascii = 0;
|
|
dev->ebcdic = 0;
|
|
// dev->rdreof = 0;
|
|
dev->trunc = 0;
|
|
dev->autopad = 0;
|
|
}
|
|
|
|
return 0;
|
|
} /* end function clear_cardrdr */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Open the card image file */
|
|
/*-------------------------------------------------------------------*/
|
|
static int open_cardrdr ( DEVBLK *dev, BYTE *unitstat )
|
|
{
|
|
int rc; /* Return code */
|
|
int i; /* Array subscript */
|
|
int len; /* Length of data */
|
|
BYTE buf[160]; /* Auto-detection buffer */
|
|
|
|
*unitstat = 0;
|
|
|
|
// Socket device?
|
|
|
|
if (dev->bs)
|
|
{
|
|
// Intervention required if no one has connected yet
|
|
|
|
if (dev->fd == -1)
|
|
{
|
|
if(dev->rdreof)
|
|
{
|
|
*unitstat=CSW_CE|CSW_DE|CSW_UX;
|
|
return -1;
|
|
}
|
|
dev->sense[0] = SENSE_IR;
|
|
dev->sense[1] = SENSE1_RDR_RAIC; /* Retry when IntReq Cleared */
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Intervention required if device has no file name */
|
|
if (dev->filename[0] == '\0' || ( strlen(dev->filename) == 1 && dev->filename[0] == '*') )
|
|
{
|
|
if(dev->rdreof)
|
|
{
|
|
*unitstat=CSW_CE|CSW_DE|CSW_UX;
|
|
return -1;
|
|
}
|
|
dev->sense[0] = SENSE_IR;
|
|
dev->sense[1] = SENSE1_RDR_RAIC; /* Retry when IntReq Cleared */
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
/* Open the device file */
|
|
rc = HOPEN (dev->filename, O_RDONLY | O_BINARY);
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "open()", strerror( errno ));
|
|
|
|
/* Set unit check with equipment check */
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
/* Save the file descriptor in the device block */
|
|
dev->fd = rc;
|
|
dev->fh = fdopen(dev->fd, "rb");
|
|
|
|
/* If neither EBCDIC nor ASCII was specified, attempt to
|
|
detect the format by inspecting the first 160 bytes */
|
|
if (dev->ebcdic == 0 && dev->ascii == 0)
|
|
{
|
|
/* Read first 160 bytes of file into the buffer */
|
|
len = (int)fread(buf, 1, sizeof(buf), dev->fh);
|
|
if (len < 0)
|
|
{
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "fread()", strerror( errno ));
|
|
|
|
/* Close the file */
|
|
fclose(dev->fh);
|
|
dev->fd = -1;
|
|
dev->fh = NULL;
|
|
|
|
/* Set unit check with equipment check */
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
/* Assume ASCII format if first 160 bytes contain only ASCII
|
|
characters, carriage return, line feed, tab, or EOF */
|
|
for (i = 0, dev->ascii = 1; i < len && buf[i] != '\x1A'; i++)
|
|
{
|
|
if ((buf[i] < 0x20 || buf[i] > 0x7F)
|
|
&& buf[i] != '\r' && buf[i] != '\n'
|
|
&& buf[i] != '\t')
|
|
{
|
|
dev->ascii = 0;
|
|
dev->ebcdic = 1;
|
|
break;
|
|
}
|
|
} /* end for(i) */
|
|
|
|
/* Rewind to start of file */
|
|
rc = fseek (dev->fh, 0, SEEK_SET);
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "fseek()", strerror( errno ));
|
|
|
|
/* Close the file */
|
|
fclose (dev->fh);
|
|
dev->fd = -1;
|
|
dev->fh = NULL;
|
|
|
|
/* Set unit check with equipment check */
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
} /* end if(auto-detect) */
|
|
|
|
ASSERT(dev->fd != -1 && dev->fh);
|
|
|
|
return 0;
|
|
} /* end function open_cardrdr */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read an 80-byte EBCDIC card image into the device buffer */
|
|
/*-------------------------------------------------------------------*/
|
|
static int read_ebcdic ( DEVBLK *dev, BYTE *unitstat )
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
/* Read 80 bytes of card image data into the device buffer */
|
|
if (dev->bs)
|
|
rc = read_socket( dev->fd, dev->buf, CARD_SIZE );
|
|
else
|
|
rc = (int)fread(dev->buf, 1, CARD_SIZE, dev->fh);
|
|
|
|
if ((rc > 0) && (rc < CARD_SIZE) && dev->autopad)
|
|
{
|
|
memset(&dev->buf[rc], 0, CARD_SIZE - rc);
|
|
rc = CARD_SIZE;
|
|
}
|
|
else if /* Check for End of file */
|
|
(0
|
|
|| ( dev->bs && rc <= 0)
|
|
|| (!dev->bs && feof(dev->fh))
|
|
)
|
|
{
|
|
/* Return unit exception or intervention required */
|
|
if (dev->rdreof)
|
|
{
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UX;
|
|
}
|
|
else
|
|
{
|
|
dev->sense[0] = SENSE_IR;
|
|
dev->sense[1] = SENSE1_RDR_RAIC; /* Retry when IntReq Cleared */
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
}
|
|
|
|
/* Close the file and clear the file name and flags */
|
|
if (clear_cardrdr(dev) != 0)
|
|
{
|
|
/* Set unit check with equipment check */
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
return -2;
|
|
}
|
|
|
|
/* Handle read error condition */
|
|
if (rc < CARD_SIZE)
|
|
{
|
|
if (rc < 0)
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "read_socket() or fread()", strerror( errno ));
|
|
else
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "read_socket() or fread()", "unexpected end of file");
|
|
|
|
/* Set unit check with equipment check */
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
} /* end function read_ebcdic */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read a variable length ASCII card image into the device buffer */
|
|
/*-------------------------------------------------------------------*/
|
|
static int read_ascii ( DEVBLK *dev, BYTE *unitstat )
|
|
{
|
|
int rc = 0; /* Return code */
|
|
int i = 0; /* Array subscript */
|
|
BYTE c = 0; /* Input character */
|
|
|
|
/* Prefill the card image with EBCDIC blanks */
|
|
memset (dev->buf, HEX40, CARD_SIZE);
|
|
|
|
/* Read up to 80 bytes into device buffer */
|
|
for (;;)
|
|
{
|
|
/* Read next byte of card image */
|
|
if (dev->bs)
|
|
{
|
|
/* Out of data? */
|
|
if (dev->bufoff >= dev->buflen)
|
|
{
|
|
// Read more data */
|
|
rc = read_socket( dev->fd, &dev->buf[ CARD_SIZE ],
|
|
TRAY_CARDS * CARD_SIZE );
|
|
if (rc <= 0)
|
|
rc = EOF;
|
|
else
|
|
{
|
|
dev->buflen = CARD_SIZE + rc;
|
|
dev->bufoff = CARD_SIZE;
|
|
c = dev->buf[ dev->bufoff++ ];
|
|
}
|
|
}
|
|
else /* grab next byte of data */
|
|
{
|
|
rc = 0;
|
|
c = dev->buf[ dev->bufoff++ ];
|
|
}
|
|
}
|
|
else /* grab next byte of data */
|
|
{
|
|
rc = getc(dev->fh);
|
|
c = (BYTE)rc;
|
|
}
|
|
|
|
/* Handle end-of-file condition */
|
|
if (rc == EOF || c == '\x1A')
|
|
{
|
|
/* End of record if there is any data in buffer */
|
|
if (i > 0)
|
|
break;
|
|
|
|
/* Return unit exception or intervention required */
|
|
if (dev->rdreof)
|
|
{
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UX;
|
|
}
|
|
else
|
|
{
|
|
dev->sense[0] = SENSE_IR;
|
|
dev->sense[1] = SENSE1_RDR_RAIC; /* Retry when IntReq Cleared */
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
}
|
|
|
|
/* Close the file and clear the file name and flags */
|
|
if (clear_cardrdr(dev) != 0)
|
|
{
|
|
/* Set unit check with equipment check */
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
return -2; /* EOF */
|
|
}
|
|
|
|
/* Handle read error condition */
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X %s: error in function %s: %s"
|
|
WRMSG( HHC01250, "E", LCSS_DEVNUM,
|
|
"Card", "read_socket() or getc()", strerror( errno ));
|
|
|
|
/* Set unit check with equipment check */
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
/* Ignore carriage return */
|
|
if (c == '\r')
|
|
continue;
|
|
|
|
/* Line-feed indicates end of variable length record */
|
|
if (c == '\n')
|
|
break;
|
|
|
|
/* Expand tabs to spaces */
|
|
if (c == '\t')
|
|
{
|
|
do {i++;} while ((i & 7) && (i < CARD_SIZE));
|
|
continue;
|
|
}
|
|
|
|
/* Test for overlength record */
|
|
if (i >= CARD_SIZE)
|
|
{
|
|
/* Ignore excess characters if trunc option specified */
|
|
if (dev->trunc)
|
|
continue;
|
|
|
|
WRMSG (HHC01207, "E", LCSS_DEVNUM, dev->filename, CARD_SIZE);
|
|
|
|
/* Set unit check with data check */
|
|
dev->sense[0] = SENSE_DC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
/* Convert character to EBCDIC and store in device buffer */
|
|
dev->buf[i++] = host_to_guest(c);
|
|
|
|
} /* end for(;;) */
|
|
|
|
return 0;
|
|
} /* end function read_ascii */
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Execute a Channel Command Word */
|
|
/*-------------------------------------------------------------------*/
|
|
static void cardrdr_execute_ccw ( DEVBLK *dev, BYTE code, BYTE flags,
|
|
BYTE chained, U32 count, BYTE prevcode, int ccwseq,
|
|
BYTE *iobuf, BYTE *more, BYTE *unitstat, U32 *residual )
|
|
{
|
|
int rc; /* Return code */
|
|
U32 num; /* Number of bytes to move */
|
|
|
|
UNREFERENCED(flags);
|
|
UNREFERENCED(prevcode);
|
|
UNREFERENCED(ccwseq);
|
|
|
|
/* Open the device file if necessary */
|
|
if ( !IS_CCW_SENSE(code) &&
|
|
(dev->fd < 0 || (!dev->bs && !dev->fh)))
|
|
{
|
|
rc = open_cardrdr (dev, unitstat);
|
|
if (rc) return;
|
|
}
|
|
|
|
/* Turn all read/feed commands into read, feed, select stacker 1 */
|
|
if ((code & 0x17) == 0x02) code = 0x02;
|
|
|
|
/* Turn all feed-only commands into NOP. This is ugly, and should
|
|
really be thought out more. --JRM */
|
|
if ((code & 0x37) == 0x23) code = 0x03;
|
|
|
|
/* Process depending on CCW opcode */
|
|
switch (code) {
|
|
|
|
case 0x02:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ */
|
|
/*---------------------------------------------------------------*/
|
|
/* Read next card if not data-chained from previous CCW */
|
|
if ((chained & CCW_FLAGS_CD) == 0)
|
|
{
|
|
for (;;)
|
|
{
|
|
/* Read ASCII or EBCDIC card image */
|
|
if (dev->ascii)
|
|
rc = read_ascii (dev, unitstat);
|
|
else
|
|
rc = read_ebcdic (dev, unitstat);
|
|
|
|
if (0
|
|
|| rc != -2
|
|
|| !dev->multifile
|
|
|| open_cardrdr (dev, unitstat) != 0
|
|
)
|
|
break;
|
|
}
|
|
|
|
/* Return error status if read was unsuccessful */
|
|
if (rc) break;
|
|
|
|
/* Initialize number of bytes in current card */
|
|
dev->cardpos = 0;
|
|
dev->cardrem = CARD_SIZE;
|
|
|
|
} /* end if(!data-chained) */
|
|
|
|
/* Calculate number of bytes to read and set residual count */
|
|
num = (count < (U32)dev->cardrem) ? count : (U32)dev->cardrem;
|
|
*residual = count - num;
|
|
if (count < (U32)dev->cardrem) *more = 1;
|
|
|
|
/* Copy data from card image buffer into channel buffer */
|
|
memcpy (iobuf, dev->buf + dev->cardpos, num);
|
|
|
|
/* Update number of bytes remaining in card image buffer */
|
|
dev->cardpos += num;
|
|
dev->cardrem -= num;
|
|
|
|
/* Return normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x03:
|
|
/*---------------------------------------------------------------*/
|
|
/* CONTROL NO-OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
*residual = 0;
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x04:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numsense) ? count : dev->numsense;
|
|
*residual = count - num;
|
|
if (count < dev->numsense) *more = 1;
|
|
|
|
/* If sense is clear AND filename = "" OR sockdev and fd=-1 */
|
|
/* Put an IR sense - so that an unsolicited sense can see the intreq */
|
|
if(dev->sense[0]==0)
|
|
{
|
|
if(dev->filename[0]==0x00 ||
|
|
(dev->bs && dev->fd==-1))
|
|
{
|
|
dev->sense[0] = SENSE_IR;
|
|
dev->sense[1] = SENSE1_RDR_RAIC; /* Retry when IntReq Cleared */
|
|
}
|
|
}
|
|
|
|
/* Copy device sense bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->sense, num);
|
|
|
|
/* Clear the device sense bytes */
|
|
memset (dev->sense, 0, sizeof(dev->sense));
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0xE4:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE ID */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/* SENSE ID is only supported if LEGACYSENSEID is ON;
|
|
* otherwise, fall through to invalid operation.
|
|
*/
|
|
if (sysblk.legacysenseid)
|
|
{
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numdevid) ? count : dev->numdevid;
|
|
*residual = count - num;
|
|
if (count < dev->numdevid) *more = 1;
|
|
|
|
/* Copy device identifier bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->devid, num);
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
}
|
|
/* FALLTHRU */
|
|
|
|
default:
|
|
/*---------------------------------------------------------------*/
|
|
/* INVALID OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
/* Set command reject sense byte, and unit check status */
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
|
|
} /* end switch(code) */
|
|
|
|
} /* end function cardrdr_execute_ccw */
|
|
|
|
|
|
static DEVHND cardrdr_device_hndinfo =
|
|
{
|
|
&cardrdr_init_handler, /* Device Initialization */
|
|
&cardrdr_execute_ccw, /* Device CCW execute */
|
|
&cardrdr_close_device, /* Device Close */
|
|
&cardrdr_query_device, /* Device Query */
|
|
NULL, /* Device Extended Query */
|
|
NULL, /* Device Start channel pgm */
|
|
NULL, /* Device End channel pgm */
|
|
NULL, /* Device Resume channel pgm */
|
|
NULL, /* Device Suspend channel pgm */
|
|
NULL, /* Device Halt channel pgm */
|
|
NULL, /* Device Read */
|
|
NULL, /* Device Write */
|
|
NULL, /* Device Query used */
|
|
NULL, /* Device Reserve */
|
|
NULL, /* Device Release */
|
|
NULL, /* Device Attention */
|
|
NULL, /* Immediate CCW Codes */
|
|
NULL, /* Signal Adapter Input */
|
|
NULL, /* Signal Adapter Output */
|
|
NULL, /* Signal Adapter Sync */
|
|
NULL, /* Signal Adapter Output Mult */
|
|
NULL, /* QDIO subsys desc */
|
|
NULL, /* QDIO set subchan ind */
|
|
NULL, /* Hercules suspend */
|
|
NULL /* Hercules resume */
|
|
};
|
|
|
|
/* Libtool static name collision resolution */
|
|
/* note : lt_dlopen will look for symbol & modulename_LTX_symbol */
|
|
|
|
#if defined( HDL_USE_LIBTOOL )
|
|
#define hdl_ddev hdt3505_LTX_hdl_ddev
|
|
#define hdl_depc hdt3505_LTX_hdl_depc
|
|
#define hdl_reso hdt3505_LTX_hdl_reso
|
|
#define hdl_init hdt3505_LTX_hdl_init
|
|
#define hdl_fini hdt3505_LTX_hdl_fini
|
|
#endif
|
|
|
|
HDL_DEPENDENCY_SECTION;
|
|
{
|
|
HDL_DEPENDENCY(HERCULES);
|
|
HDL_DEPENDENCY(DEVBLK);
|
|
HDL_DEPENDENCY(SYSBLK);
|
|
}
|
|
END_DEPENDENCY_SECTION
|
|
|
|
|
|
#if defined(WIN32) && !defined(HDL_USE_LIBTOOL) && !defined(_MSVC_)
|
|
#undef sysblk
|
|
HDL_RESOLVER_SECTION;
|
|
{
|
|
HDL_RESOLVE_SYMPTR( psysblk, sysblk );
|
|
}
|
|
END_RESOLVER_SECTION
|
|
#endif
|
|
|
|
|
|
HDL_DEVICE_SECTION;
|
|
{
|
|
HDL_DEVICE(1442, cardrdr_device_hndinfo );
|
|
HDL_DEVICE(2501, cardrdr_device_hndinfo );
|
|
HDL_DEVICE(3505, cardrdr_device_hndinfo );
|
|
}
|
|
END_DEVICE_SECTION
|
|
|
|
#if defined( _MSVC_ ) && defined( NO_RDR_OPTIMIZE )
|
|
#pragma optimize( "", on ) // restore previous settings
|
|
#endif
|