| /* |
| * 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(__CYGWIN__) |
| 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 |
| }; |