668 lines
16 KiB
C
668 lines
16 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-*-
|
|
*
|
|
* $Revision$
|
|
* $Date$
|
|
*
|
|
*
|
|
* serdrv.c - Synchronous Serial Driver for Angel.
|
|
* This is nice and simple just to get something going.
|
|
*/
|
|
|
|
#ifdef __hpux
|
|
# define _POSIX_SOURCE 1
|
|
#endif
|
|
|
|
#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"
|
|
|
|
extern int baud_rate; /* From gdb/top.c */
|
|
|
|
#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 SERIAL_FC_SET ((1<<serial_XON)|(1<<serial_XOFF))
|
|
#define SERIAL_CTL_SET ((1<<serial_STX)|(1<<serial_ETX)|(1<<serial_ESC))
|
|
#define SERIAL_ESC_SET (SERIAL_FC_SET|SERIAL_CTL_SET)
|
|
|
|
static const struct re_config config = {
|
|
serial_STX, serial_ETX, serial_ESC, /* self-explanatory? */
|
|
SERIAL_FC_SET, /* set of flow-control characters */
|
|
SERIAL_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;
|
|
|
|
typedef struct writestate {
|
|
unsigned int wbindex;
|
|
/* static te_status testatus;*/
|
|
unsigned char writebuf[MAXWRITESIZE];
|
|
struct te_state txstate;
|
|
} writestate;
|
|
|
|
static struct writestate wstate;
|
|
|
|
/*
|
|
* The set of parameter options supported by the device
|
|
*/
|
|
static unsigned int baud_options[] = {
|
|
#if defined(B115200) || defined(__hpux)
|
|
115200,
|
|
#endif
|
|
#if defined(B57600) || defined(__hpux)
|
|
57600,
|
|
#endif
|
|
38400, 19200, 9600
|
|
};
|
|
|
|
static ParameterList param_list[] = {
|
|
{ AP_BAUD_RATE,
|
|
sizeof(baud_options)/sizeof(unsigned int),
|
|
baud_options }
|
|
};
|
|
|
|
static const ParameterOptions serial_options = {
|
|
sizeof(param_list)/sizeof(ParameterList), param_list };
|
|
|
|
/*
|
|
* The default parameter config for the device
|
|
*/
|
|
static Parameter param_default[] = {
|
|
{ AP_BAUD_RATE, 9600 }
|
|
};
|
|
|
|
static ParameterConfig serial_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)];
|
|
|
|
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 serial_reset( void );
|
|
static int serial_set_params( const ParameterConfig *config );
|
|
static int SerialMatch(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( &serial_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, &serial_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 SerialOpen(const char *name, const char *arg)
|
|
{
|
|
const char *port_name = name;
|
|
|
|
#ifdef DEBUG
|
|
printf("SerialOpen: name %s arg %s\n", name, arg ? arg : "<NULL>");
|
|
#endif
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
if (IsOpenSerial()) return -1;
|
|
#else
|
|
if (Unix_IsSerialInUse()) return -1;
|
|
#endif
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
if (SerialMatch(name, arg) != adp_ok)
|
|
return adp_failed;
|
|
#else
|
|
port_name = Unix_MatchValidSerialDevice(port_name);
|
|
# ifdef DEBUG
|
|
printf("translated port to %s\n", port_name == 0 ? "NULL" : port_name);
|
|
# endif
|
|
if (port_name == 0) return adp_failed;
|
|
#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
|
|
}
|
|
else if (baud_rate > 0)
|
|
{
|
|
/* If the user specified a baud rate on the command line "-b" or via
|
|
the "set remotebaud" command then try to use that one */
|
|
process_baud_rate( baud_rate );
|
|
}
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
{
|
|
int port = IsValidDevice(name);
|
|
if (OpenSerial(port, FALSE) != COM_OK)
|
|
return -1;
|
|
}
|
|
#else
|
|
if (Unix_OpenSerial(port_name) < 0)
|
|
return -1;
|
|
#endif
|
|
|
|
serial_reset();
|
|
|
|
#if defined(__unix) || defined(__CYGWIN32__)
|
|
Unix_ioctlNonBlocking();
|
|
#endif
|
|
|
|
Angel_RxEngineInit(&config, &rxstate);
|
|
/*
|
|
* DANGER!: passing in NULL as the packet is ok for now as it is just
|
|
* IGNOREd but this may well change
|
|
*/
|
|
Angel_TxEngineInit(&config, NULL, &wstate.txstate);
|
|
return 0;
|
|
}
|
|
|
|
static int SerialMatch(const char *name, const char *arg)
|
|
{
|
|
UNUSED(arg);
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
if (IsValidDevice(name) == COM_DEVICENOTVALID)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
#else
|
|
return Unix_MatchValidSerialDevice(name) == 0 ? -1 : 0;
|
|
#endif
|
|
}
|
|
|
|
static void SerialClose(void)
|
|
{
|
|
#ifdef DO_TRACE
|
|
printf("SerialClose()\n");
|
|
#endif
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
CloseSerial();
|
|
#else
|
|
Unix_CloseSerial();
|
|
#endif
|
|
}
|
|
|
|
static int SerialRead(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 */
|
|
|
|
/* 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;
|
|
|
|
if ((read_errno = ReadSerial(readbuf+rbindex, nread, &dummy)) == COM_READFAIL)
|
|
{
|
|
MessageBox(GetFocus(), "Read 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 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]);
|
|
if (!(++c % 16))
|
|
printf("\n");
|
|
#else
|
|
c++;
|
|
#endif
|
|
} while (c<rbindex &&
|
|
((restatus == RS_IN_PKT) || (restatus == RS_WAIT_PKT)));
|
|
|
|
#ifdef DO_TRACE
|
|
if (c % 16)
|
|
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("SerialRead() 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 serialRead()\n");
|
|
#endif
|
|
break;
|
|
}
|
|
} else if (nread == 0)
|
|
ret_code = 0; /* nothing to read */
|
|
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 serialRead()");
|
|
#endif
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
|
|
static int SerialWrite(DriverCall *dc) {
|
|
int nwritten = 0;
|
|
te_status testatus = TS_IN_PKT;
|
|
|
|
if (dc->dc_context == NULL) {
|
|
Angel_TxEngineInit(&config, &(dc->dc_packet), &(wstate.txstate));
|
|
wstate.wbindex = 0;
|
|
dc->dc_context = &wstate;
|
|
}
|
|
|
|
while ((testatus == TS_IN_PKT) && (wstate.wbindex < MAXWRITESIZE))
|
|
{
|
|
/* send the raw data through the tx engine to escape and encapsulate */
|
|
testatus = Angel_TxEngine(&(dc->dc_packet), &(wstate.txstate),
|
|
&(wstate.writebuf)[wstate.wbindex]);
|
|
if (testatus != TS_IDLE) wstate.wbindex++;
|
|
}
|
|
|
|
if (testatus == TS_IDLE) {
|
|
#ifdef DEBUG
|
|
printf("SerialWrite: testatus is TS_IDLE during preprocessing\n");
|
|
#endif
|
|
}
|
|
|
|
#ifdef DO_TRACE
|
|
{
|
|
int i = 0;
|
|
|
|
while (i<wstate.wbindex)
|
|
{
|
|
printf(">%02X ",wstate.writebuf[i]);
|
|
|
|
if (!(++i % 16))
|
|
printf("\n");
|
|
}
|
|
if (i % 16)
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
if (WriteSerial(wstate.writebuf, wstate.wbindex) == COM_OK)
|
|
{
|
|
nwritten = wstate.wbindex;
|
|
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_WriteSerial(wstate.writebuf, wstate.wbindex);
|
|
|
|
if (nwritten < 0) {
|
|
nwritten=0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
if (nwritten > 0)
|
|
printf("Wrote %#04x bytes\n", nwritten);
|
|
#endif
|
|
|
|
if ((unsigned) nwritten == wstate.wbindex &&
|
|
(testatus == TS_DONE_PKT || testatus == TS_IDLE)) {
|
|
|
|
/* finished sending the packet */
|
|
|
|
#ifdef DEBUG
|
|
printf("SerialWrite: calling Angel_TxEngineInit after sending packet (len=%i)\n",wstate.wbindex);
|
|
#endif
|
|
testatus = TS_IN_PKT;
|
|
wstate.wbindex = 0;
|
|
return 1;
|
|
}
|
|
else {
|
|
#ifdef DEBUG
|
|
printf("SerialWrite: Wrote part of packet wbindex=%i, nwritten=%i\n",
|
|
wstate.wbindex, nwritten);
|
|
#endif
|
|
|
|
/*
|
|
* still some data left to send shuffle whats left down and reset
|
|
* the ptr
|
|
*/
|
|
memmove((char *) wstate.writebuf, (char *) (wstate.writebuf+nwritten),
|
|
wstate.wbindex-nwritten);
|
|
wstate.wbindex -= nwritten;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int serial_reset( void )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "serial_reset\n" );
|
|
#endif
|
|
|
|
#ifdef COMPILING_ON_WINDOWS
|
|
FlushSerial();
|
|
#else
|
|
Unix_ResetSerial();
|
|
#endif
|
|
|
|
return serial_set_params( &serial_defaults );
|
|
}
|
|
|
|
|
|
static int find_baud_rate( unsigned int *speed )
|
|
{
|
|
static struct {
|
|
unsigned int baud;
|
|
int termiosValue;
|
|
} possibleBaudRates[] = {
|
|
#if defined(__hpux)
|
|
{115200,_B115200}, {57600,_B57600},
|
|
#else
|
|
#ifdef B115200
|
|
{115200,B115200},
|
|
#endif
|
|
#ifdef B57600
|
|
{57600,B57600},
|
|
#endif
|
|
#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 serial_set_params( const ParameterConfig *config )
|
|
{
|
|
unsigned int speed;
|
|
int termios_value;
|
|
|
|
#ifdef DEBUG
|
|
printf( "serial_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 serial_get_user_params( ParameterOptions **p_options )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "serial_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( ParameterConfig **p_config )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "serial_get_default_params\n" );
|
|
#endif
|
|
|
|
*p_config = (ParameterConfig *) &serial_defaults;
|
|
return DE_OKAY;
|
|
}
|
|
|
|
|
|
static int SerialIoctl(const int opcode, void *args) {
|
|
|
|
int ret_code;
|
|
|
|
#ifdef DEBUG
|
|
printf( "SerialIoctl: op %d arg %p\n", opcode, args ? args : "<NULL>");
|
|
#endif
|
|
|
|
switch (opcode)
|
|
{
|
|
case DC_RESET:
|
|
ret_code = serial_reset();
|
|
break;
|
|
|
|
case DC_SET_PARAMS:
|
|
ret_code = serial_set_params((const ParameterConfig *)args);
|
|
break;
|
|
|
|
case DC_GET_USER_PARAMS:
|
|
ret_code = serial_get_user_params((ParameterOptions **)args);
|
|
break;
|
|
|
|
case DC_GET_DEFAULT_PARAMS:
|
|
ret_code = serial_get_default_params((ParameterConfig **)args);
|
|
break;
|
|
|
|
default:
|
|
ret_code = DE_BAD_OP;
|
|
break;
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
DeviceDescr angel_SerialDevice = {
|
|
"SERIAL",
|
|
SerialOpen,
|
|
SerialMatch,
|
|
SerialClose,
|
|
SerialRead,
|
|
SerialWrite,
|
|
SerialIoctl
|
|
};
|