729 lines
17 KiB
C
729 lines
17 KiB
C
/*
|
|
* Copyright (C) 1995 Advanced RISC Machines Limited. All rights reserved.
|
|
*
|
|
* This software may be freely used, copied, modified, and distributed
|
|
* provided that the above copyright notice is preserved in all copies of the
|
|
* software.
|
|
*/
|
|
|
|
/* -*-C-*-
|
|
*
|
|
*
|
|
*
|
|
* serpardv.c - Serial/Parallel Driver for Angel.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "crc.h"
|
|
#include "devices.h"
|
|
#include "buffers.h"
|
|
#include "rxtx.h"
|
|
#include "hostchan.h"
|
|
#include "params.h"
|
|
#include "logging.h"
|
|
#include "hsys.h"
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
# undef ERROR
|
|
# undef IGNORE
|
|
# include <windows.h>
|
|
# include "angeldll.h"
|
|
# include "comb_api.h"
|
|
#else
|
|
# ifdef __hpux
|
|
# define _TERMIOS_INCLUDED
|
|
# include <sys/termio.h>
|
|
# undef _TERMIOS_INCLUDED
|
|
# else
|
|
# include <termios.h>
|
|
# endif
|
|
# include "unixcomm.h"
|
|
#endif
|
|
|
|
#ifndef UNUSED
|
|
# define UNUSED(x) (x = x) /* Silence compiler warnings */
|
|
#endif
|
|
|
|
#define MAXREADSIZE 512
|
|
#define MAXWRITESIZE 512
|
|
|
|
#define SERPAR_FC_SET ((1 << serial_XON) | (1 << serial_XOFF))
|
|
#define SERPAR_CTL_SET ((1 << serial_STX) | (1 << serial_ETX) | \
|
|
(1 << serial_ESC))
|
|
#define SERPAR_ESC_SET (SERPAR_FC_SET | SERPAR_CTL_SET)
|
|
|
|
static const struct re_config config = {
|
|
serial_STX, serial_ETX, serial_ESC, /* self-explanatory? */
|
|
SERPAR_FC_SET, /* set of flow-control characters */
|
|
SERPAR_ESC_SET, /* set of characters to be escaped */
|
|
NULL, /* serial_flow_control */
|
|
NULL, /* what to do with FC chars */
|
|
angel_DD_RxEng_BufferAlloc, NULL /* how to get a buffer */
|
|
};
|
|
|
|
static struct re_state rxstate;
|
|
|
|
/*
|
|
* structure used for manipulating transmit data
|
|
*/
|
|
typedef struct TxState
|
|
{
|
|
struct te_state state;
|
|
unsigned int index;
|
|
unsigned char writebuf[MAXWRITESIZE];
|
|
} TxState;
|
|
|
|
/*
|
|
* The set of parameter options supported by the device
|
|
*/
|
|
static unsigned int baud_options[] =
|
|
{
|
|
#ifdef __hpux
|
|
115200, 57600,
|
|
#endif
|
|
38400, 19200, 9600
|
|
};
|
|
|
|
static ParameterList param_list[] =
|
|
{
|
|
{
|
|
AP_BAUD_RATE,
|
|
sizeof(baud_options) / sizeof(unsigned int),
|
|
baud_options
|
|
}
|
|
};
|
|
|
|
static const ParameterOptions serpar_options =
|
|
{
|
|
sizeof(param_list) / sizeof(ParameterList),
|
|
param_list
|
|
};
|
|
|
|
/*
|
|
* The default parameter config for the device
|
|
*/
|
|
static Parameter param_default[] =
|
|
{
|
|
{ AP_BAUD_RATE, 9600 }
|
|
};
|
|
|
|
static const ParameterConfig serpar_defaults =
|
|
{
|
|
sizeof(param_default)/sizeof(Parameter),
|
|
param_default
|
|
};
|
|
|
|
/*
|
|
* The user-modified options for the device
|
|
*/
|
|
static unsigned int user_baud_options[sizeof(baud_options) /
|
|
sizeof(unsigned int)];
|
|
|
|
static ParameterList param_user_list[] =
|
|
{
|
|
{
|
|
AP_BAUD_RATE,
|
|
sizeof(user_baud_options) / sizeof(unsigned),
|
|
user_baud_options
|
|
}
|
|
};
|
|
|
|
static ParameterOptions user_options =
|
|
{
|
|
sizeof(param_user_list) / sizeof(ParameterList),
|
|
param_user_list
|
|
};
|
|
|
|
static bool user_options_set;
|
|
|
|
/* forward declarations */
|
|
static int serpar_reset(void);
|
|
static int serpar_set_params(const ParameterConfig *config);
|
|
static int SerparMatch(const char *name, const char *arg);
|
|
|
|
static void process_baud_rate(unsigned int target_baud_rate)
|
|
{
|
|
const ParameterList *full_list;
|
|
ParameterList *user_list;
|
|
|
|
/* create subset of full options */
|
|
full_list = Angel_FindParamList(&serpar_options, AP_BAUD_RATE);
|
|
user_list = Angel_FindParamList(&user_options, AP_BAUD_RATE);
|
|
|
|
if (full_list != NULL && user_list != NULL)
|
|
{
|
|
unsigned int i, j;
|
|
unsigned int def_baud = 0;
|
|
|
|
/* find lower or equal to */
|
|
for (i = 0; i < full_list->num_options; ++i)
|
|
if (target_baud_rate >= full_list->option[i])
|
|
{
|
|
/* copy remaining */
|
|
for (j = 0; j < (full_list->num_options - i); ++j)
|
|
user_list->option[j] = full_list->option[i+j];
|
|
user_list->num_options = j;
|
|
|
|
/* check this is not the default */
|
|
Angel_FindParam(AP_BAUD_RATE, &serpar_defaults, &def_baud);
|
|
if ((j == 1) && (user_list->option[0] == def_baud))
|
|
{
|
|
#ifdef DEBUG
|
|
printf("user selected default\n");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
user_options_set = TRUE;
|
|
#ifdef DEBUG
|
|
printf("user options are: ");
|
|
for (j = 0; j < user_list->num_options; ++j)
|
|
printf("%u ", user_list->option[j]);
|
|
printf("\n");
|
|
#endif
|
|
}
|
|
|
|
break; /* out of i loop */
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (i >= full_list->num_options)
|
|
printf("couldn't match baud rate %u\n", target_baud_rate);
|
|
#endif
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
printf("failed to find lists\n");
|
|
#endif
|
|
}
|
|
|
|
static int SerparOpen(const char *name, const char *arg)
|
|
{
|
|
char *sername = NULL;
|
|
char *parname = NULL;
|
|
|
|
#ifdef DEBUG
|
|
printf("SerparOpen: name %s arg %s\n", name, arg ? arg : "<NULL>");
|
|
#endif
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
if (IsOpenSerial() || IsOpenParallel()) return -1;
|
|
#else
|
|
if (Unix_IsSerialInUse() || Unix_IsParallelInUse()) return -1;
|
|
#endif
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
if (SerparMatch(name, arg) == -1)
|
|
return -1;
|
|
#else
|
|
Unix_IsValidParallelDevice(name,&sername,&parname);
|
|
# ifdef DEBUG
|
|
printf("translated %s to serial %s and parallel %s\n",
|
|
name==0 ? "NULL" : name,
|
|
sername==0 ? "NULL" : sername,
|
|
parname==0 ? "NULL" : parname);
|
|
# endif
|
|
if (sername==NULL || parname==NULL) return -1;
|
|
#endif
|
|
|
|
user_options_set = FALSE;
|
|
|
|
/* interpret and store the arguments */
|
|
if (arg != NULL)
|
|
{
|
|
unsigned int target_baud_rate;
|
|
|
|
target_baud_rate = (unsigned int)strtoul(arg, NULL, 10);
|
|
|
|
if (target_baud_rate > 0)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("user selected baud rate %u\n", target_baud_rate);
|
|
#endif
|
|
process_baud_rate(target_baud_rate);
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
printf("could not understand baud rate %s\n", arg);
|
|
#endif
|
|
}
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
{
|
|
/*
|
|
* The serial port number is in name[0] followed by
|
|
* the parallel port number in name[1]
|
|
*/
|
|
|
|
int sport = name[0] - '0';
|
|
int pport = name[1] - '0';
|
|
|
|
if (OpenParallel(pport) != COM_OK)
|
|
return -1;
|
|
|
|
if (OpenSerial(sport, FALSE) != COM_OK)
|
|
{
|
|
CloseParallel();
|
|
return -1;
|
|
}
|
|
}
|
|
#else
|
|
Unix_OpenParallel(parname);
|
|
Unix_OpenSerial(sername);
|
|
#endif
|
|
|
|
serpar_reset();
|
|
|
|
#if defined(__unix) || defined(__CYGWIN32__)
|
|
Unix_ioctlNonBlocking();
|
|
#endif
|
|
|
|
Angel_RxEngineInit(&config, &rxstate);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
static int SerparMatch(const char *name, const char *arg)
|
|
{
|
|
char sername[2];
|
|
char parname[2];
|
|
|
|
UNUSED(arg);
|
|
|
|
sername[0] = name[0];
|
|
parname[0] = name[1];
|
|
sername[1] = parname[1] = 0;
|
|
|
|
if (IsValidDevice(sername) == COM_DEVICENOTVALID ||
|
|
IsValidDevice(parname) == COM_DEVICENOTVALID)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
#else
|
|
static int SerparMatch(const char *portstring, const char *arg)
|
|
{
|
|
char *sername=NULL, *parname=NULL;
|
|
UNUSED(arg);
|
|
|
|
Unix_IsValidParallelDevice(portstring,&sername,&parname);
|
|
|
|
/* Match failed if either sername or parname are still NULL */
|
|
if (sername==NULL || parname==NULL) return -1;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void SerparClose(void)
|
|
{
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
CloseParallel();
|
|
CloseSerial();
|
|
#else
|
|
Unix_CloseParallel();
|
|
Unix_CloseSerial();
|
|
#endif
|
|
}
|
|
|
|
static int SerparRead(DriverCall *dc, bool block)
|
|
{
|
|
static unsigned char readbuf[MAXREADSIZE];
|
|
static int rbindex = 0;
|
|
|
|
int nread;
|
|
int read_errno;
|
|
int c = 0;
|
|
re_status restatus;
|
|
int ret_code = -1; /* assume bad packet or error */
|
|
|
|
/*
|
|
* we must not overflow buffer, and must start after
|
|
* the existing data
|
|
*/
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
{
|
|
BOOL dummy = FALSE;
|
|
nread = BytesInRXBufferSerial();
|
|
|
|
if (nread > MAXREADSIZE - rbindex)
|
|
nread = MAXREADSIZE - rbindex;
|
|
read_errno = ReadSerial(readbuf+rbindex, nread, &dummy);
|
|
if (pfnProgressCallback != NULL && read_errno == COM_OK)
|
|
{
|
|
progressInfo.nRead += nread;
|
|
(*pfnProgressCallback)(&progressInfo);
|
|
}
|
|
}
|
|
#else
|
|
nread = Unix_ReadSerial(readbuf+rbindex, MAXREADSIZE-rbindex, block);
|
|
read_errno = errno;
|
|
#endif
|
|
|
|
if ((nread > 0) || (rbindex > 0))
|
|
{
|
|
#ifdef DO_TRACE
|
|
printf("[%d@%d] ", nread, rbindex);
|
|
#endif
|
|
|
|
if (nread > 0)
|
|
rbindex = rbindex + nread;
|
|
|
|
do
|
|
{
|
|
restatus = Angel_RxEngine(readbuf[c], &(dc->dc_packet), &rxstate);
|
|
|
|
#ifdef DO_TRACE
|
|
printf("<%02X ",readbuf[c]);
|
|
#endif
|
|
c++;
|
|
} while (c < rbindex &&
|
|
((restatus == RS_IN_PKT) || (restatus == RS_WAIT_PKT)));
|
|
|
|
#ifdef DO_TRACE
|
|
printf("\n");
|
|
#endif
|
|
|
|
switch(restatus)
|
|
{
|
|
case RS_GOOD_PKT:
|
|
ret_code = 1;
|
|
/* fall through to: */
|
|
|
|
case RS_BAD_PKT:
|
|
/*
|
|
* We now need to shuffle any left over data down to the
|
|
* beginning of our private buffer ready to be used
|
|
*for the next packet
|
|
*/
|
|
#ifdef DO_TRACE
|
|
printf("SerparRead() processed %d, moving down %d\n",
|
|
c, rbindex - c);
|
|
#endif
|
|
|
|
if (c != rbindex)
|
|
memmove((char *) readbuf, (char *) (readbuf + c), rbindex - c);
|
|
|
|
rbindex -= c;
|
|
|
|
break;
|
|
|
|
case RS_IN_PKT:
|
|
case RS_WAIT_PKT:
|
|
rbindex = 0; /* will have processed all we had */
|
|
ret_code = 0;
|
|
break;
|
|
|
|
default:
|
|
#ifdef DEBUG
|
|
printf("Bad re_status in SerparRead()\n");
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
else if (nread == 0)
|
|
/* nothing to read */
|
|
ret_code = 0;
|
|
else if (read_errno == ERRNO_FOR_BLOCKED_IO) /* nread < 0 */
|
|
ret_code = 0;
|
|
|
|
#ifdef DEBUG
|
|
if ((nread < 0) && (read_errno != ERRNO_FOR_BLOCKED_IO))
|
|
perror("read() error in SerparRead()");
|
|
#endif
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
/*
|
|
* Function: send_packet
|
|
* Purpose: Send a stream of bytes to Angel through the parallel port
|
|
*
|
|
* Algorithm: We need to present the data in a form that all boards can
|
|
* swallow. With the PID board, this is a problem: for reasons
|
|
* described in the driver (angel/pid/st16c552.c), data are
|
|
* sent a nybble at a time on D0-D2 and D4; D3 is wired to ACK,
|
|
* which generates an interrupt when it goes low. This routine
|
|
* fills in an array of nybbles, with ACK clear in all but the
|
|
* last one. If, for whatever reason, the write fails, then
|
|
* ACK is forced high (thereby enabling the next write a chance
|
|
* to be noticed when the falling edge of ACK generates an
|
|
* interrupt (hopefully).
|
|
*
|
|
* Params:
|
|
* Input: txstate Contains the packet to be sent
|
|
*
|
|
* Returns: Number of *complete* bytes written
|
|
*/
|
|
|
|
static int SerparWrite(DriverCall *dc)
|
|
{
|
|
te_status status;
|
|
int nwritten = 0;
|
|
static TxState txstate;
|
|
|
|
/*
|
|
* is this a new packet?
|
|
*/
|
|
if (dc->dc_context == NULL)
|
|
{
|
|
/*
|
|
* yes - initialise TxEngine
|
|
*/
|
|
Angel_TxEngineInit(&config, &dc->dc_packet, &txstate.state);
|
|
|
|
txstate.index = 0;
|
|
dc->dc_context = &txstate;
|
|
}
|
|
|
|
/*
|
|
* fill the buffer using the Tx Engine
|
|
*/
|
|
do
|
|
{
|
|
status = Angel_TxEngine(&dc->dc_packet, &txstate.state,
|
|
&txstate.writebuf[txstate.index]);
|
|
if (status != TS_IDLE) txstate.index++;
|
|
|
|
} while (status == TS_IN_PKT && txstate.index < MAXWRITESIZE);
|
|
|
|
#ifdef DO_TRACE
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
while (i < txstate.index)
|
|
{
|
|
printf(">%02X ", txstate.writebuf[i]);
|
|
|
|
if (!(++i % 16))
|
|
putc('\n', stdout);
|
|
}
|
|
|
|
if (i % 16)
|
|
putc('\n', stdout);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* the data are ready, all we need now is to send them out
|
|
* in a form that Angel can swallow.
|
|
*/
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
if (WriteParallel(txstate.writebuf, txstate.index) == COM_OK)
|
|
{
|
|
nwritten = txstate.index;
|
|
if (pfnProgressCallback != NULL)
|
|
{
|
|
progressInfo.nWritten += nwritten;
|
|
(*pfnProgressCallback)(&progressInfo);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MessageBox(GetFocus(), "Write error\n", "Angel", MB_OK | MB_ICONSTOP);
|
|
return -1; /* SJ - This really needs to return a value, which is picked up in */
|
|
/* DevSW_Read as meaning stop debugger but don't kill. */
|
|
}
|
|
#else
|
|
nwritten = Unix_WriteParallel(txstate.writebuf, txstate.index);
|
|
#endif
|
|
|
|
if (nwritten < 0) nwritten = 0;
|
|
|
|
#ifdef DO_TRACE
|
|
printf("SerparWrite: wrote %d out of %d bytes\n",
|
|
nwritten, txstate.index);
|
|
#endif
|
|
|
|
/*
|
|
* has the whole packet gone?
|
|
*/
|
|
if (nwritten == (int)txstate.index &&
|
|
(status == TS_DONE_PKT || status == TS_IDLE))
|
|
/*
|
|
* yes it has
|
|
*/
|
|
return 1;
|
|
else
|
|
{
|
|
/*
|
|
* if some data are left, shuffle them
|
|
* to the start of the buffer
|
|
*/
|
|
if (nwritten != (int)txstate.index && nwritten != 0)
|
|
{
|
|
txstate.index -= nwritten;
|
|
(void)memmove((char *) txstate.writebuf,
|
|
(char *) (txstate.writebuf + nwritten),
|
|
txstate.index);
|
|
}
|
|
else if (nwritten == (int)txstate.index)
|
|
txstate.index = 0;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int serpar_reset(void)
|
|
{
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
FlushParallel();
|
|
FlushSerial();
|
|
#else
|
|
Unix_ResetParallel();
|
|
Unix_ResetSerial();
|
|
#endif
|
|
|
|
return serpar_set_params(&serpar_defaults);
|
|
}
|
|
|
|
static int find_baud_rate(unsigned int *speed)
|
|
{
|
|
static struct
|
|
{
|
|
unsigned int baud;
|
|
int termiosValue;
|
|
} possibleBaudRates[] =
|
|
{
|
|
#if defined(__hpux)
|
|
{115200, _B115200}, {57600, _B57600},
|
|
#endif
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
{38400, CBR_38400}, {19200, CBR_19200}, {9600, CBR_9600}, {0, 0}
|
|
#else
|
|
{38400, B38400}, {19200, B19200}, {9600, B9600}, {0, 0}
|
|
#endif
|
|
};
|
|
unsigned int i;
|
|
|
|
/* look for lower or matching -- will always terminate at 0 end marker */
|
|
for (i = 0; possibleBaudRates[i].baud > *speed; ++i)
|
|
/* do nothing */
|
|
;
|
|
|
|
if (possibleBaudRates[i].baud > 0)
|
|
*speed = possibleBaudRates[i].baud;
|
|
|
|
return possibleBaudRates[i].termiosValue;
|
|
}
|
|
|
|
static int serpar_set_params(const ParameterConfig *config)
|
|
{
|
|
unsigned int speed;
|
|
int termios_value;
|
|
|
|
#ifdef DEBUG
|
|
printf("serpar_set_params\n");
|
|
#endif
|
|
|
|
if (!Angel_FindParam(AP_BAUD_RATE, config, &speed))
|
|
{
|
|
#ifdef DEBUG
|
|
printf("speed not found in config\n");
|
|
#endif
|
|
return DE_OKAY;
|
|
}
|
|
|
|
termios_value = find_baud_rate(&speed);
|
|
if (termios_value == 0)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("speed not valid: %u\n", speed);
|
|
#endif
|
|
return DE_OKAY;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf("setting speed to %u\n", speed);
|
|
#endif
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
SetBaudRate((WORD)termios_value);
|
|
#else
|
|
Unix_SetSerialBaudRate(termios_value);
|
|
#endif
|
|
|
|
return DE_OKAY;
|
|
}
|
|
|
|
|
|
static int serpar_get_user_params(ParameterOptions **p_options)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("serpar_get_user_params\n");
|
|
#endif
|
|
|
|
if (user_options_set)
|
|
{
|
|
*p_options = &user_options;
|
|
}
|
|
else
|
|
{
|
|
*p_options = NULL;
|
|
}
|
|
|
|
return DE_OKAY;
|
|
}
|
|
|
|
|
|
static int serial_get_default_params( const ParameterConfig **p_config )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "serial_get_default_params\n" );
|
|
#endif
|
|
|
|
*p_config = &serpar_defaults;
|
|
return DE_OKAY;
|
|
}
|
|
|
|
|
|
static int SerparIoctl(const int opcode, void *args)
|
|
{
|
|
int ret_code;
|
|
|
|
#ifdef DEBUG
|
|
printf("SerparIoctl: op %d arg %p\n", opcode, args ? args : "<NULL>");
|
|
#endif
|
|
|
|
switch (opcode)
|
|
{
|
|
case DC_RESET:
|
|
ret_code = serpar_reset();
|
|
break;
|
|
|
|
case DC_SET_PARAMS:
|
|
ret_code = serpar_set_params((const ParameterConfig *)args);
|
|
break;
|
|
|
|
case DC_GET_USER_PARAMS:
|
|
ret_code = serpar_get_user_params((ParameterOptions **)args);
|
|
break;
|
|
|
|
case DC_GET_DEFAULT_PARAMS:
|
|
ret_code =
|
|
serial_get_default_params((const ParameterConfig **)args);
|
|
break;
|
|
|
|
default:
|
|
ret_code = DE_BAD_OP;
|
|
break;
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
DeviceDescr angel_SerparDevice =
|
|
{
|
|
"SERPAR",
|
|
SerparOpen,
|
|
SerparMatch,
|
|
SerparClose,
|
|
SerparRead,
|
|
SerparWrite,
|
|
SerparIoctl
|
|
};
|
|
|
|
/* EOF serpardr.c */
|