#include #include #include #include #include #include "udrive.h" #include "crc.h" #include "comm_int.h" #define INT_TIME 0x1A #define PAUSE 10 long t_counts() /* This routine uses interrupts to get the system clock ticks value. */ { union REGS rin, rout; long tc; rin.h.ah = 0; int86(INT_TIME, &rin, &rout); tc = ( (long) rout.x.cx) << 16; tc += rout.x.dx; return tc; } void timo_reset(int *start_time) { *start_time = (int)(t_counts() & 0xffff); } int timo_check(int start_time, int timo_time) { /* This routine returns 1 if time out. Otherwise it returns 0. */ return ( ((int)(t_counts() & 0xffff)) - start_time) > timo_time; } /************************************************************************/ /** Low Level Functions **/ /** The following functions make up the lowest level of the universal **/ /** driver. All functions return -1 if there is an error and 0 if no **/ /** error. **/ /************************************************************************/ /**************************************************************************/ /* udgetchar() This routine is called by get_mess_char(). It reads a */ /* character from a buffer internal to the UD_CONT struct. */ /* When the buffer is empty, more characters are read from */ /* the device handle. The character is returned via the */ /* second argument, a ushort. */ /* */ /* Return value: 0 no error */ /* -1 error */ /**************************************************************************/ int udgetchar( UD_CONT *ct, ushort *inchar ) { int ret = 0; int read_cnt; int char_read; if ( ct->rv.timo && ct->rv.start ) { /* Grab a start time. */ timo_reset(&(ct->rv.s_time)); ct->rv.start = FALSE; /* Reset so we don't continue */ } /* grab start times. */ if ( ct->rv.buf_i == ct->rv.num_read ) { /* Need to read more */ /* characters. */ read_cnt = 0; do { char_read = read_comm(&com_port); while ( char_read != -1 ) { ct->rv.wbuf[read_cnt] = char_read; read_cnt++; char_read = read_comm(&com_port); } } while ( (read_cnt == 0) && (ct->rv.timo ? !timo_check(ct->rv.s_time,ct->rv.to_time) : TRUE) ); if ( read_cnt == 0 ) /* Timed out or error. */ ret = -1; else { ct->rv.buf_i = 0; /* Reset index to first position in */ ct->rv.num_read = read_cnt; /* buffer and set number read. */ } } /* If need to read more characters. */ /* Need to do time out check here. If we didn't read from the device */ /* handle then this is the only time out check. If we did then we're */ /* checking the timeout again. */ if ( ret != -1 && (ct->rv.timo ? !timo_check(ct->rv.s_time,ct->rv.to_time) : TRUE) ) { *inchar = ct->rv.wbuf[ct->rv.buf_i++]; } else ret = -1; return ret; } /* udgetchar() */ /****************************************************************************/ /* get_mess_char() - gets a single byte of a Universal Driver message. */ /* This routine does any necessary conversions of the input */ /* stream needed to create one byte of an incoming UDriver */ /* message. The conversions that may be necessary include: */ /* - reading 2 bytes from the input stream if the message */ /* format is HEX. */ /* - reading 2 bytes if we're trying to get a SPECIAL char. */ /* Therefore, a maximum of 4 bytes may be read from the input */ /* stream to come up with the one byte that may be returned. */ /* When this routine returns a SPECIAL char a ushort is */ /* actually returned. The byte (or ushort) is returned via */ /* the second argument. */ /* This routine is called by receive_mess(). */ /* Return values: -1 on time out or error */ /* 0 no error, char placed in *inchar */ /* NOTE: This routine returns from a number of places. */ /* Returning from many places is used to make error */ /* processing easier. */ /****************************************************************************/ int get_mess_char( UD_CONT *ct, ushort *inchar, ushort control ) { short lead_char = control & GET_LEAD_CHAR, special = control & GET_SPECIAL; uchar temp; if ( udgetchar(ct,inchar) == -1 ) return -1; else if ( ! lead_char ) { /* If we are reading the lead character, */ /* don't do any of this, just return. */ temp = (uchar)*inchar; ct->rv.crc = crccitt(ct->rv.crc,temp); ct->rv.len--; if (ct->rv.len < 0) return -1; if ( special ) { short work,extended_byte; if (temp < 0x80) work = temp; /* Normal 1 byte field. */ else if (temp >= 0xb0) work = (temp & 0x3f) + 0x6000; /* Special value such as ANY_STAT. */ else { work = temp & 0x7f; /* Extended 2 byte field. */ if ( get_mess_char(ct,&extended_byte,0) == -1 ) /* Get 2nd Byte. */ return -1; work = (work << 8) + extended_byte; } *inchar = work; } /* if special */ } /* if !lead_char */ return 0; } /* get_mess_char() */ /***************************************************************************/ /* write_cmd_buf() */ /* Writes the character to the command buffer. It first checks */ /* if the character must be written as a two-byte character. It */ /* then computes the CRC and writes it to the buffer. It will */ /* then call flush_cmd_buf if the buffer is full. */ /* Returns: 0 no error */ /***************************************************************************/ short write_cmd_buf( UD_CONT *ct, ushort outval, ushort control ) { short special = control & IS_SPECIAL, lead_char = control & LEAD_CHAR; uchar outbyte; if ( lead_char ) { ct->tx.wbuf[ct->tx.buf_i++] = (uchar)outval; } else { if ( special ) { /* Handle 'special' bytes that may need to be sent as two bytes. */ /* Special station values are 0x6000 - 0x603f. */ if ((outval >= 0x6000) && (outval < 0x6040)) outbyte = (uchar)(0xc0 + (outval - 0x6000)); else if (outval < 0x80) outbyte = (uchar)outval; else if (outval < 0x3fff) { outbyte = (uchar)(0x80 + (outval >> 8)); write_cmd_buf(ct,outbyte,0); outbyte = (uchar)(outval & 0x00ff); } } else outbyte = (uchar)outval; /* Now put the byte. */ /* First compute crc on the byte. */ ct->tx.crc = crccitt(ct->tx.crc,outbyte); ct->tx.wbuf[ct->tx.buf_i++] = outbyte; /* Check if buffer is full and needs to be written out the device */ /* handle. */ if ( ct->tx.buf_i >= WORK_BUF_SIZE ) return (flush_cmd_buf(ct)); /* 0 == no error; -1 == error */ } return 0; } /* write_cmd_buf() */ /***************************************************************************/ /* flush_cmd_buf(ct) */ /* Writes the command buffer out the device handle. This routine */ /* must change when Master code is to run on an operating system */ /* other than MS-DOS. Transmit timeout is used if it is enabled. */ /* Returns: 0 no error */ /* -1 error */ /***************************************************************************/ short flush_cmd_buf( UD_CONT *ct ) { int ret, num_to_write; /* Actual number of characters that */ /* we'll attempt to write. */ char *c; num_to_write = ct->tx.buf_i; c = &(ct->tx.wbuf[0]); if ( ct->tx.timo && ct->tx.start ) { /* Grab a start time. */ timo_reset(&(ct->tx.s_time)); ct->tx.start = FALSE; } while ( num_to_write > 0 && (ct->tx.timo ? !timo_check(ct->tx.s_time,ct->tx.to_time) : TRUE) ) { if ( num_to_write ) { do ret = write_comm(&com_port,(int)c[0]); while (ret != 0); num_to_write --; c ++; } } /* reinitialize tx.wbuf index. */ ct->tx.buf_i = 0; return num_to_write ? -1 : 0; } /* flush_cmd_buf */ /****************************************************************************/ /* set_transmit_timo() Set the transmit timeout time based on either */ /* message length or the fixed time out time. */ /* This routine is called from transmit_mess() and */ /* receive_mess(). receive_mess calls it to echo back */ /* '\r' and '\n' when in HEX mode. This routine */ /* always returns 0 to indicate no error. */ /****************************************************************************/ short set_transmit_timo( UD_CONT *ct, ushort mess_len ) { /* This routine is called when transmit timeout is enabled. */ /* Transmit time out is set based on fixed timeout time or the */ /* message length depending on which timeout method is chosen. */ ct->tx.start = TRUE; /* Signal to flush_cmd_buf() to grab a start time. */ /* Set time out time. Timeout time is either just the fixed time out */ /* time or fixed timeout time + (CHARS_PER_SEC * num bytes transferred). */ /* start by setting the FIXED_TIMEOUT_TIME */ ct->tx.to_time = (ushort)(FIXED_TIMEOUT_TIME * (TICKS_PER_SEC / 1000)); if ( CHARS_PER_SEC != -1 ) { /* Add in variable time based on message */ /* length. */ ct->tx.to_time += (ushort) ((((mess_len * 100) / CHARS_PER_SEC) * 10) * (TICKS_PER_SEC / 1000)); } /* if CHAR_PER_SEC != -1 */ return 0; } /* set_transmit_timo() */ /****************************************************************************/ /** Middle Level Functions **/ /** The following functions make up the middle level of the universal **/ /** driver. These functions return 0 if there were no errors and -1 if **/ /** there were errors. **/ /****************************************************************************/ /****************************************************************************/ /* transmit_mess() - This function sends a universal driver message. */ /* The following fields in 'ct' must be set properly: */ /* ct->tx.dlen, ct->tx.hlen, ct->tx.form, ct->tx.orig, */ /* ct->tx.stat, ct->tx.sess, ct->tx.id, ct->tx.cmd. */ /* ct->tx.dlen defines the number of bytes in dbuff[] */ /* to send. ct->tx.hlen defines the number of bytes in */ /* hbuff[] to send. This routine can time out if */ /* ct->tx.timo is enabled. A timeout time is computed */ /* and the message must transmit is the computed time */ /* out time. */ /* Returns 0 no error */ /* -1 error */ /****************************************************************************/ int transmit_mess( UD_CONT *ct, uchar *hbuff, uchar *dbuff ) { int i; short err = FALSE; ushort crc_put; /* Compute total message length. */ ct->tx.len = ct->tx.hlen+ ct->tx.dlen + 7; /* Initialize working buffer index. */ ct->tx.buf_i = 0; /* Index to next available location */ /* in tx.wbuf[] to write a char to. */ /* If tranmit timeout is enabled, we need to set the transmit time out */ /* value and do a few other things. */ if ( ct->tx.timo ) set_transmit_timo(ct,ct->tx.len + 2); /* + 2 for crc bytes */ err = write_cmd_buf(ct,')',LEAD_CHAR); ct->tx.crc = CRC_INIT; if ( !err ) err = write_cmd_buf(ct,ct->tx.len,0); if ( !err ) err = write_cmd_buf(ct,ct->tx.stat,IS_SPECIAL); if ( !err ) err = write_cmd_buf(ct,ct->tx.orig,IS_SPECIAL); if ( !err ) err = write_cmd_buf(ct,ct->tx.sess,IS_SPECIAL); if ( !err ) err = write_cmd_buf(ct,ct->tx.id,0); if ( !err ) err = write_cmd_buf(ct,ct->tx.cmd,IS_SPECIAL); for (i=0; !err && i < ct->tx.hlen; i++) err = write_cmd_buf(ct,hbuff[i],0); for (i=0; !err && i < ct->tx.dlen; i++) err = write_cmd_buf(ct,dbuff[i],0); if ( !err) crc_put = ~(ct->tx.crc); if ( !err) err = write_cmd_buf(ct,crc_put >> 8,0); if ( !err) write_cmd_buf(ct,crc_put & 0x00ff,0); return (err ? -1 : flush_cmd_buf(ct)); /* 0 == no error; -1 == error */ } /* transmit_mess */ /****************************************************************************/ /* receive_mess() - This function waits for a valid Universal Driver */ /* message. A valid message is one who's fields up to */ /* but not including the COMMAND field are valid and */ /* who's crc is ok. COMMAND byte recognition is left up */ /* to the calling routine. If the time out function is */ /* not selected, (ie. ct->rv.timo == FALSE) then this */ /* function will only return on receipt of a valid */ /* message. If the time out function is enabled then */ /* a -1 is returned if the function times out. If */ /* time out based on message length is used then the */ /* FIXED_TIME_OUT time is used while waiting for the lead */ /* char and the length to come in. Once the length is */ /* received, a message time out time is computed and that */ /* time is used to receive the rest of the message. . */ /****************************************************************************/ int receive_mess( UD_CONT *ct, uchar *buff ) { int ret,i; ushort in; int done = FALSE; int timo = FALSE; /* If receive timeout is enabled, we need to flag udgetchar() to grab */ /* a start time. We also need to set the time out time. Time out time */ /* is set at the FIXED_TIMEOUT_TIME while waiting to get the lead */ /* character and the length byte. When the length is received, if */ /* CHARS_PER_SEC != -1 then a timeout is computed based on message */ /* length and that time out is set for the rest of the message */ /* reception. After message length timeout is computed, if the form is */ /* HEX, the len is doubled before the timeout time is computed. */ if ( ct->rv.timo ) { ct->rv.start = TRUE; /* Signal to udgetchar() to grab a start time. */ /* Set time out time. Timeout time for reception of lead and length */ /* is just the FIXED_TIMEOUT_TIME. */ /* Start by setting the FIXED_TIMEOUT_TIME. */ ct->rv.to_time = (ushort)(FIXED_TIMEOUT_TIME * (TICKS_PER_SEC / 1000)); } /* if rv.timo */ while (!done && !timo) { /* Wait for SOM (start of message character). */ do { timo = get_mess_char(ct,&in,GET_LEAD_CHAR); } while ((in != ']') && (in != ')') && !timo); if (!timo) { ct->rv.len = 10; /* Bogus length value just to get us started. */ ct->rv.crc = CRC_INIT; /* CRC initial value. */ if (!get_mess_char(ct,&in,0)) { /* Get total message length. */ ct->rv.len = in; /* If necessary, compute new timeout time based on message length. */ if ( CHARS_PER_SEC != -1 ) { /* len+2 is used for the message length because we need to add */ /* in the 2 crc bytes. Use 'in' rather than ct->rv.len to avoid */ /* indirection. Use '+=' because FIXED Timeout time is already */ /* in there. If in HEX mode, double the length because twice as */ /* many characters will be received as is reflected by 'len'. */ in += 2; ct->rv.to_time += (ushort) ((((in * 100) / CHARS_PER_SEC) * 10) * (TICKS_PER_SEC / 1000)); } /* if CHARS_PER_SEC */ if ( !get_mess_char(ct,&in,GET_SPECIAL) ) { if ((ct->rv.stat = in) == ANY_STAT) { /* Is it any station? */ if ( !get_mess_char(ct,&in,GET_SPECIAL) ) { ct->rv.orig = in; /* Get origin stat. */ if (TRUE) { /* Any origin will do. */ /* Get session # */ if ( !get_mess_char(ct,&in,GET_SPECIAL) ) { /* Set session # */ if ( (ct->rv.sess = in) == 0) { /* Is it o.k. */ if (!get_mess_char(ct,&in,0)) { /* Get id byte. */ ct->rv.id = (uchar)in; /* Set id byte. */ if ( !get_mess_char(ct,&in,GET_SPECIAL) ) { ct->rv.cmd = in; /* Get command byte. */ ct->rv.dlen = ct->rv.len; /* Data length is */ /* what's left. */ i = 0; /* Initialize the array */ /* index. */ ret = 0; while ((ct->rv.len > 0) && !ret){ /* Read remainder of bytes. */ ret = get_mess_char(ct,&in,0); /* Get byte and return value. */ buff[i++] = (uchar)in; /* Put byte in buffer. */ } if (ret == 0) { /* Return val ok do crc. */ if (ct->rv.crc == CRC_MAGIC) { /* If crc OK? */ done = TRUE; /* If so, then done. */ } } } /* get_mess_char for rv.cmd */ } /* get_mess_char for rv.id */ } /* is sess == 0 */ } /* get_mess_char for rd.sess */ } /* if TRUE */ } /* get_mess_char for rv.orig */ } /* ANY_STAT */ } /* get_mess_char for rv.stat */ } } /* if !timo */ } /* while !done && !timo */ if (done) { return 0; } else { return -1; } } /* receive_mess() */ /****************************************************************************/ /* init_udr_ct(ct,handle, pt, tx_timo, rv_timo form, orig, stat, sess) */ /* Initializes the control structures, ct, ct.tx and ct.rv. */ /* Returns 1 if successful, 0 if error. */ /* If any argument is -1, a default value is used. */ /* handle and ct cannot be -1. */ /* The form argument sets both the tx and rv control blocks. */ /****************************************************************************/ int init_udr_ct( /* Function initializes ct block. -1 means use default. */ UD_CONT *ct /* Control structure */ ) { if ( ct == NULL ) return 0; else { ct->rv.buf_i = 0; /* Initialize these two to 0. This forces a */ ct->rv.num_read = 0; /* read from the device the first time a */ /* character is requested. */ ct->tx.id = 0; /* Initialize transmit id (used as seq. num) */ ct->tx.timo = TRUE; ct->rv.timo = TRUE; /* Although the rv.form is initialized here, after the 1st message */ /* is received, it is set by incoming messages. */ ct->tx.orig = ANY_STAT; ct->tx.stat = DEST_STAT; ct->tx.sess = 0; /* Initialize control word to 0. */ ct->control = 0; } return 1; } /* end init_udr_ct */ /****************************************************************************/ /** High Level Functions **/ /** The following functions make up the highest level of the universal **/ /** driver. These functions return 1 if there were no errors and 0 if **/ /** there were errors. **/ /****************************************************************************/ short get_d( /* Function gets discrete inputs from slave station. */ UD_CONT *ct, /* Control structure. */ uchar type, /* Type of discrete to get. (See driver documentation). */ ushort start, /* Starting discrete address on slave station. */ ushort num, /* Number of discretes to get (rounded up mod 8). */ uchar *dest /* Address of I/O or array to receive the data. */ ) { uchar hbuff[8], /* Header info buffer. */ dbuff[256]; /* Working data buffer. */ ushort tnum; /* Number of I/O per message. */ short ok = FALSE; /* No good message yet. */ int err = FALSE; start = start & 0xfff8; /* Round start down mod 8. */ while ( num > 0 && !err ) { if (num > 1600) /* 1600 = 200 byte max. */ tnum = 1600; else tnum = num; hbuff[0] = (uchar)type; hbuff[1] = (uchar)(start & 0xff); /* Send lo byte first. */ hbuff[2] = (uchar)((start >> 8) & 0xff); /* Send hi byte second. */ hbuff[3] = (uchar)(tnum & 0xff); /* Send lo byte first. */ hbuff[4] = (uchar)((tnum >> 8) & 0xff); /* Send hi byte second. */ ++(ct->tx.id); /* Increment sequence number. */ ct->tx.cmd = GETD; /* Send GETD command. */ ct->tx.hlen = 5; /* Header length is 5 (type,start,num). */ ct->tx.dlen = 0; /* 'gets' have 0 data bytes. */ err = transmit_mess(ct,hbuff,dbuff); /* Send the message. */ ok = FALSE; /* Reset for each pass. */ while ( !err && !ok ) { if ( !(err = receive_mess(ct,dbuff))) { /* Get reply. Is it ok? */ if ( ct->rv.id == ct->tx.id ) { /* If seq id's match, it's */ /* a reply to the mesg that */ /* was sent. */ if ( ct->rv.cmd == NAK ) { /* Cmd wasn't acknowledged */ err = -1; /* -1 signals error */ } else if ( ct->rv.cmd == ACK ) { /* Accept only ACK messages.*/ memcpy(dest,&(dbuff[5]),(tnum+7)/8);/* Copy inputs to dest. */ num -= tnum; /* Keep track of bytes left.*/ start += tnum; /* New starting address. */ dest += (tnum+7)/8; /* New destination address. */ ok = TRUE; } } /* id's are equal. */ } } /* while (!err && !ok) */ } return ok; /* Return either 0 or 1. */ } short get_b( /* Function gets discrete inputs from slave station. */ UD_CONT *ct, /* Control structure. */ uchar type, /* Type of discrete to get (see driver documentation). */ ushort start, /* Starting byte address on slave station. */ ushort num, /* Number of bytes to get. */ uchar *dest /* Address of I/O or array to receive the data. */ ) { uchar hbuff[8], /* Header info buffer. */ dbuff[256]; /* Working data buffer. */ ushort tnum; /* Number of I/O per message. */ short ok = FALSE; /* No good message yet. */ int err = FALSE; while ( num > 0 && !err ) { if (num > 200) /* 200 byte maximum. */ tnum = 200; else tnum = num; hbuff[0] = (uchar)type; hbuff[1] = (uchar)(start & 0xff); /* Send lo byte first. */ hbuff[2] = (uchar)((start >> 8) & 0xff); /* Send hi byte second. */ hbuff[3] = (uchar)(tnum & 0xff); /* Send lo byte first. */ hbuff[4] = (uchar)((tnum >> 8) & 0xff); /* Send hi byte second. */ ++(ct->tx.id); /* Increment sequence number. */ ct->tx.cmd = GETB; /* Send GETB command. */ ct->tx.hlen = 5; /* Header length is 5 (type,start,num). */ ct->tx.dlen = 0; /* Length of data is 0. */ err = transmit_mess(ct,hbuff,dbuff); /* Send the message. */ ok = FALSE; /* Reset for each pass. */ while ( !err && !ok ) { if ( !(err = receive_mess(ct,dbuff))) { /* Get reply. Is it ok? */ if ( ct->rv.id == ct->tx.id ) { /* If seq id's match, it's */ /* a reply to the mesg that */ /* was sent. */ if ( ct->rv.cmd == NAK ) { /* Cmd wasn't acknowledged */ err = -1; /* -1 signals error */ } else if ( ct->rv.cmd == ACK ) { /* Accept only ACK messages.*/ memcpy(dest,&(dbuff[5]),tnum); /* Copy inputs to dest. */ num -= tnum; /* Keep track of bytes left.*/ start += tnum; /* New starting address. */ dest += tnum; /* New destination address. */ ok = TRUE; } } /* id's are equal. */ } } /* while (!err && !ok) */ } return ok; /* Either 0 or 1. */ } short put_b( /* Function sends discrete outputs to slave station. */ UD_CONT *ct, /* Control structure. */ uchar type, /* Type of discrete to put (see driver documentation). */ ushort start, /* Starting byte address on remote station. */ ushort num, /* Number of byte to put. */ uchar *src /* Address of I/O or array to send. */ ) { uchar hbuff[8], /* Header info buffer. */ dbuff[256]; /* Working data buffer. */ ushort tnum; /* number of I/O per message. */ short ok; /* No good message yet. */ int err = FALSE; while (num > 0 && !err) { if (num > 200) /* 200 byte maximum. */ tnum = 200; else tnum = num; hbuff[0] = (uchar)type; hbuff[1] = (uchar)(start & 0xff); /* Send lo byte first. */ hbuff[2] = (uchar)((start >> 8) & 0xff); /* Send hi byte second. */ hbuff[3] = (uchar)(tnum & 0xff); /* Send lo byte first. */ hbuff[4] = (uchar)((tnum >> 8) & 0xff); /* Send hi byte second. */ ct->tx.hlen = 5; /* Header length is 5 (type,start,num) */ ct->tx.dlen = tnum; /* Length of data is tnum. */ ++(ct->tx.id); /* Increment sequence number. */ ct->tx.cmd = PUTB; /* Send PUTB command. */ err = transmit_mess(ct,hbuff,src); /* Send the message. */ ok = FALSE; /* Reset for each pass. */ while ( !err && !ok ) { if ( !(err = receive_mess(ct,dbuff))) { /* Get reply. Is it ok? */ if ( ct->rv.id == ct->tx.id ) { /* If seq id's match, it's */ /* a reply to the mesg that */ /* was sent. */ if ( ct->rv.cmd == NAK ) { /* Cmd wasn't acknowledged */ err = -1; /* -1 signals error */ } else if ( ct->rv.cmd == ACK ) { /* Accept only ACK messages.*/ num -= tnum; start += tnum; src += tnum; /* New source address. */ ok = TRUE; } } /* id's are equal. */ } } /* while (!err && !ok) */ } return ok; /* Either 0 or 1. */ } short get_a( /* Function gets analog inputs from slave station. */ UD_CONT *ct, /* Control structure. */ uchar type, /* Type of analog to get (see driver documentation). */ ushort start, /* Starting analog address on slave station. */ ushort num, /* Number of analog values to get. */ aio_t *dest /* Address of I/O or array to receive the data. */ ) { uchar hbuff[8], /* Header info buffer. */ dbuff[256]; /* Working data buffer. */ ushort tnum; /* Number of I/O per message. */ short ok = FALSE; /* No good message yet. */ int err = FALSE; while (num > 0 && !err ) { if (num > 100) tnum = 100; else tnum = num; hbuff[0] = (uchar)type; hbuff[1] = (uchar)(start & 0xff); /* Send lo byte first. */ hbuff[2] = (uchar)((start >> 8) & 0xff); /* Send hi byte second. */ hbuff[3] = (uchar)(tnum & 0xff); /* Send lo byte first. */ hbuff[4] = (uchar)((tnum >> 8) & 0xff); /* Send hi byte second. */ ++(ct->tx.id); /* Increment sequence number. */ ct->tx.cmd = GETA; /* Send GETA command. */ ct->tx.hlen = 5; /* Header length is 5(type,start,num). */ ct->tx.dlen = 0; /* Length of data is 0 */ err = transmit_mess(ct,hbuff,dbuff); /* Send the message. */ ok = FALSE; /* Reset for each pass. */ while ( !err && !ok ) { if ( !(err = receive_mess(ct,dbuff))) { /* Get reply. Is it ok? */ if ( ct->rv.id == ct->tx.id ) { /* If seq id's match, it's */ /* a reply to the mesg that */ /* was sent. */ if ( ct->rv.cmd == NAK ) { /* Cmd wasn't acknowledged */ err = -1; /* -1 signals error */ } else if ( ct->rv.cmd == ACK ) { /* Accept only ACK messages.*/ memcpy((char*)dest,&(dbuff[5]),tnum*2); /* Copy ins into dest.*/ num -= tnum; start += tnum; dest += tnum; /* New destination address. */ ok = TRUE; } } /* id's are equal. */ } } /* while (!err && !ok) */ } /* num > 0 && !err */ return ok; /* Either 0 or 1. */ } short put_a( /* Function sends analog outputs to slave station. */ UD_CONT *ct, /* Control structure. */ uchar type, /* Type of analog to put (see driver documentation). */ ushort start, /* Starting analog address on slave station. */ ushort num, /* Number of analog outputs to send. */ aio_t *src /* Address of I/O or array to send. */ ) { uchar hbuff[8], /* Header info buffer. */ dbuff[256]; /* Working data buffer. */ ushort tnum; /* Number of I/O per message. */ short ok; /* No good message yet. */ int err = FALSE; while (num > 0 && !err) { if (num > 100) tnum = 100; else tnum = num; hbuff[0] = (uchar)type; hbuff[1] = (uchar)(start & 0xff); /* Send lo byte first. */ hbuff[2] = (uchar)((start >> 8) & 0xff); /* Send hi byte second. */ hbuff[3] = (uchar)(tnum & 0xff); /* Send lo byte first. */ hbuff[4] = (uchar)((tnum >> 8) & 0xff); /* Send hi byte second. */ ++(ct->tx.id); /* Increment sequence number. */ ct->tx.hlen = 5; /* Header length is 5 (type,start,num). */ ct->tx.dlen = tnum * 2; /* Length of data is 4 (start,num). */ ct->tx.cmd = PUTA; /* Send PUTA command. */ err = transmit_mess(ct,hbuff,(uchar*)src);/* Send the message. */ ok = FALSE; /* Reset for each pass. */ while ( !err && !ok ) { if ( !(err = receive_mess(ct,dbuff))) { /* Get reply. Is it ok? */ if ( ct->rv.id == ct->tx.id ) { /* If seq id's match, it's */ /* a reply to the mesg that */ /* was sent. */ if ( ct->rv.cmd == NAK ) { /* Cmd wasn't acknowledged */ err = -1; /* -1 signals error */ } else if ( ct->rv.cmd == ACK ) { /* Accept only ACK messages.*/ num -= tnum; start += tnum; src += tnum; /* New source address. */ ok = TRUE; } } /* id's are equal. */ } } /* while ( !err && !ok) */ } return ok; /* Either 0 or 1. */ } short set_d( /* Function sends discrete outputs to slave station. */ UD_CONT *ct, /* Control structure. */ uchar type, /* Type of discrete to put (see driver documentation). */ ushort start, /* Starting discrete address on remote station. */ ushort num, /* Number of discretes to put, rounded up mod 8. */ uchar *src /* Address of I/O or array to send. */ ) { uchar hbuff[8], /* Header info buffer. */ dbuff[256]; /* Working data buffer */ ushort tnum; /* Number of I/O per message. */ short ok; /* No good message yet. */ int err = FALSE; start = start & 0xfff8; /* Round down mod 8. */ while (num > 0 && !err ) { if (num > 1600) /* 1600 = 200 byte maximum. */ tnum = 1600; else tnum = num; hbuff[0] = (uchar)type; hbuff[1] = (uchar)(start & 0xff); /* Send lo byte first. */ hbuff[2] = (uchar)((start >> 8) & 0xff); /* Send hi byte second. */ hbuff[3] = (uchar)(tnum & 0xff); /* Send lo byte first. */ hbuff[4] = (uchar)((tnum >> 8) & 0xff); /* Send hi byte second. */ ct->tx.hlen = 5; /* Header len is 5 (type,start,num). */ ct->tx.dlen = ((tnum+7)/8); /* Data len is ... */ ++(ct->tx.id); /* Increment sequence number. */ ct->tx.cmd = SETD; /* Send SETD command. */ err = transmit_mess(ct,hbuff,src); /* Send the message. */ ok = FALSE; /* Reset for each pass. */ while ( !err && !ok ) { if ( !(err = receive_mess(ct,dbuff))) { /* Get reply. Is it ok? */ if ( ct->rv.id == ct->tx.id ) { /* If seq id's match, it's */ /* a reply to the mesg that */ /* was sent. */ if ( ct->rv.cmd == NAK ) { /* Cmd wasn't acknowledged */ err = -1; /* -1 signals error */ } else if ( ct->rv.cmd == ACK ) { /* Accept only ACK messages.*/ num -= tnum; start += tnum; src += (tnum+7)/8; /* New source address. */ ok = TRUE; } } /* id's are equal. */ } } /* while (!err && !ok) */ } return ok; /* Either 0 or 1. */ } short clr_d( /* Function sends discrete outputs to slave station. */ UD_CONT *ct, /* Control structure. */ uchar type, /* Type of discrete to put (see driver documentation). */ ushort start, /* Starting discrete address on remote station. */ ushort num, /* Number of discretes to put, rounded up mod 8. */ uchar *src /* Address of I/O or array to send. */ ) { uchar hbuff[8], /* Header info buffer. */ dbuff[256]; /* Working data buffer. */ ushort tnum; /* Number of I/O per message. */ short ok = FALSE; /* No good message yet. */ int err = FALSE; start = start & 0xfff8; /* Round down mod 8. */ while (num > 0 && !err ) { if (num > 1600) /* 1600 = 200 byte maximum */ tnum = 1600; else tnum = num; hbuff[0] = (uchar)type; hbuff[1] = (uchar)(start & 0xff); /* Send lo byte first. */ hbuff[2] = (uchar)((start >> 8) & 0xff); /* Send hi byte second. */ hbuff[3] = (uchar)(tnum & 0xff); /* Send lo byte first. */ hbuff[4] = (uchar)((tnum >> 8) & 0xff); /* Send hi byte second. */ ct->tx.hlen = 5; /* Header length 5 (type,start,num). */ ct->tx.dlen = ((tnum+7)/8); /* Length of data is ... */ ++(ct->tx.id); /* Increment sequence number. */ ct->tx.cmd = CLRD; /* Send CLRD command. */ err = transmit_mess(ct,hbuff,src); /* Send the message. */ ok = FALSE; /* Reset for each pass. */ while ( !err && !ok ) { if ( !(err = receive_mess(ct,dbuff))) { /* Get reply. Is it ok? */ if ( ct->rv.id == ct->tx.id ) { /* If seq id's match, it's */ /* a reply to the mesg that */ /* was sent. */ if ( ct->rv.cmd == NAK ) { /* Cmd wasn't acknowledged */ err = -1; /* -1 signals error */ } else if ( ct->rv.cmd == ACK ) { /* Accept only ACK messages.*/ num -= tnum; start += tnum; src += (tnum+7)/8; /* New source address. */ ok = TRUE; } } /* id's are equal. */ } } /* while ( !err && !ok) */ } return ok; /* Either 0 or 1. */ }