| /* |
| * 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$ |
| * |
| * |
| * 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 */ |