/* ** DEFINITIONS */ #include #include #include #include #include #include #include #include #include #include #include #include "protocol.h" #include "serial.h" #define DEBUG 0 /* ** Variables */ extern char *buffer_start; extern char *buffer_end; extern char *buffer_out; extern int count; extern int max_count; int line_speed = DEFAULT_SPEED; int window_size = DEFAULT_WINDOW_SIZE; int packet_size = DEFAULT_PACKET_SIZE; int timeout = DEFAULT_TIME_OUT; int error_flag[TN_MSG_TYPES][TN_ERROR_TYPES] = {0}; int error_count; void main() { int done = FALSE; int opt; char inbuffer[80]; /* ** Initialization stuff */ init_buf(); disable(); setvect( 0xc, serial_interrupt ); enable(); outportb( 0x21, inportb( 0x21 ) & 0xe7 ); outportb( 0x20, 0x20 ); init_port(); /* ** Main loop */ while( !done ) { printf( "\nMain Menu: \n\n" ); printf( "1) Transfer Options \n" ); printf( "2) Generate Errors\n" ); printf( "3) Help \n" ); printf( "q or x) Quit \n" ); printf( "Select Option > " ); // gets( inbuffer ); inbuffer[0] = getche(); inbuffer[1] = '\0'; opt = atoi( inbuffer ); switch( opt ) { case 1: set_options(); break; case 2: generate_errors(); break; case 3: help( 0 ); break; default: if( (*inbuffer == 'q') || (*inbuffer == 'Q') || (*inbuffer == 'x') || (*inbuffer == 'X') ) { done = TRUE; } else printf( "\n\nINVALID OPTION, TRY AGAIN \n\n" ); } } } void init_port() { disable(); outportb( (int)&COM->interrupt_enable, I_CHAR_IN ); outportb( (int)&COM->format, F_BAUD_LATCH | F_NO_BREAK | F_PARITY_NONE | F_STOP1 | F_DATA8 ); outportb( (int)&COM->baud_l, line_speed & 0xFF ); outportb( (int)&COM->baud_h, line_speed >> 8 ); outportb( (int)&COM->format, F_NORMAL | F_NO_BREAK | F_PARITY_NONE | F_STOP1 | F_DATA8 ); outportb( (int)&COM->out_control, O_OUT1 | O_OUT2 | O_RTS | O_DTR ); inportb( (int)&COM->data ); inportb( (int)&COM->interrupt_enable ); inportb( (int)&COM->interrupt_id ); inportb( (int)&COM->status ); outportb( 0x20, 0x20 ); enable(); } /* ** This function offers options for setting options and sending/ receiving ** files */ void set_options() { char inbuffer[80]; int done = FALSE; int opt, temp; int file_desc; while( !done ) { printf( "\n\nSet Options Menu\n" ); printf( "1) Speed\n" ); printf( "2) Window Size \n" ); printf( "3) Packet Size \n" ); printf( "4) Timout value\n" ); printf( "5) Send File \n" ); printf( "6) Receive File \n" ); printf( "7) Help \n" ); printf( "8) Back to Main Menu \n" ); printf( "Select Option > " ); // gets( inbuffer ); inbuffer[0] = getche(); inbuffer[1] = '\0'; opt = atoi( inbuffer ); switch( opt ) { case 1: printf( "\nEnter Speed (1200, 2400, 9600): " ); gets( inbuffer ); temp = atoi( inbuffer ); if( (temp != 1200) && (temp != 2400) && (temp != 9600) ) { printf( "\n\nInvalid Speed, not used \n\n" ); } else { if( temp == 1200 ) { line_speed = B1200; printf( "\n\nLine Speed set to 1200 \n\n" ); } else if( temp == 2400 ) { line_speed = B2400; printf( "\n\nLine Speed set to 2400 \n\n" ); } else if( temp == 9600 ) { line_speed = B9600; printf( "\n\nLine Speed set to 9600 \n\n" ); } } init_port(); break; case 2: printf( "\nSet Window Size (1 < size < 10):" ); gets( inbuffer ); temp = atoi( inbuffer ); if( (temp < 1) || (temp > 10) ) { printf( "\n\nInvalid Window Size, not used\n\n" ); } else { window_size = temp; printf( "\n\nWindow size set to %d\n\n", window_size ); } break; case 3: printf( "\nSet Packet Size (1 < size < 1000):\n" ); gets( inbuffer ); temp = atoi( inbuffer ); if( (temp < 1) || (temp > 256) ) { printf( "\n\nInvalid Packet Size, not used\n\n" ); } else { packet_size = temp; printf( "\n\nPacket size set to %d\n\n", packet_size ); } break; case 4: printf( "\nSet timeout value (seconds):\n" ); gets( inbuffer ); temp = atoi( inbuffer ); if( temp <= 0 ) { printf( "\\n Invalid timeout value, not used\n\n" ); } else { timeout = temp; printf( "\n\nTimeout value set to %d\n\n", timeout ); } break; case 5: printf( "\nFile name:" ); gets( inbuffer ); file_desc = open( inbuffer, O_RDONLY ); if( file_desc == -1 ) { printf( "\n\nFILE OPEN ERROR\n\n" ); } else { if( send_file( file_desc, inbuffer ) == -1 ) { printf( "file transfer interrupted\n" ); } else { printf( "file transferred \n" ); } close( file_desc ); } break; case 6: receive_file(); break; case 7: printf( "\nenter number of command (1-5):\n" ); gets( inbuffer ); temp = atoi( inbuffer ); if( (temp < 1) || (temp > 5) ) { printf( "\n\nInvalid command\n\n" ); } else { help( temp ); } break; case 8: done = TRUE; break; default: printf( "\n\nINVALID OPTION, TRY AGAIN\n\n" ); } } } void generate_errors() { char input; int msg_type = -1; int error_type = -1; memset( error_flag, 0, sizeof( error_flag ) ); printf( "\nChecksum errors? (Y/[N] \n" ); input = getche(); if( (input == 'Y') || (input == 'y') ) { error_type = CHECKSUM_ERROR; } else { printf( "\nTimeout errors ? (Y/[N]) \n" ); input = getche(); if( (input == 'Y') || (input == 'y') ) { error_type = TIMEOUT_ERROR; } } if( error_type != -1 ) { printf( "\nMessage type: (I,H,[D]) \n" ); input = getche(); if( (input == 'I') || (input == 'i') ) { msg_type = INIT_MSG_TYPE; } else if( (input == 'H') || (input == 'h') ) { msg_type = HEADER_MSG_TYPE; } else { msg_type = DATA_MSG_TYPE; } error_flag[msg_type][error_type] = TRUE; } error_count = 0; } /* ** This function processes requests for help information */ void help( int command ) { switch( command ) { case 0: printf( "Main Menu Commands\n\n" ); printf( "Transfer Options: \n" ); printf( "This menu allows the setting of various file transfer\n" ); printf( "options and the transfer of a particular file\n\n" ); printf( "Generate Errors:\n" ); printf( "This menu is for testing of the protocol only. It\n" ); printf( "allows various errors to be introduced.\n\n" ); break; case 1: printf( "Set speed:\n"); printf( "This command sets the baud rate for the serial \n" ); printf( "line. This command sets up the rate on the transfer\n"); printf( "side only. The receive side must also be changed \n"); printf( "when the transfer side is changed.\n" ); break; case 2: printf( "Set window size:\n" ); printf( "This command sets the window size for the \n" ); printf( "continuous ARQ protocol. The window size is the\n"); printf( "number of packets that can be sent before an ack\n"); printf( "is received.\n" ); break; case 3: printf( "Set packet size:\n" ); printf( "This command sets the maximum packet size that will\n"); printf( "be used in file transfers. The value is in bytes.\n"); break; case 4: printf( "Set timeout:\n" ); printf( "This command sets the timeout value. The timeout\n" ); printf( "value is the amount of time that the sender will\n"); printf( "wait to receive and acknowledgement before declaring\n"); printf( "the packet lost, and resending. This value is in\n"); printf( "seconds\n" ); break; case 5: printf( "Send file:\n" ); printf( "This command transfers a file from the sender \n"); printf( "machine to the receiver machine. Only text files\n"); printf( "may be transferred.\n" ); break; case 6: printf( "Receive file: \n" ); printf( "This command puts the receive file in receive mode.\n" ); printf( "Only text files may be received \n" ); } } /* ** This function transfers a file to the receiver */ int send_file( int file_desc, char *filename ) { int status = FALSE; char *xfr_buffer; char *buf_status; char ack_buffer[ sizeof( ACK_DATA ) + 2 ]; ACK_DATA_PTR ack_ptr = (ACK_DATA_PTR) ack_buffer; time_t start_time; time_t current_time; int done = TRUE; BUF_STATUS_PTR stat_ptr; int all_bufs_empty; int ack_received, nak_received; int i; ACK_DATA ack_buf; char key; char *bcc_ptr; char *type_ptr; char last_ack_received = window_size - 1; char next_ack_to_receive = 0; /* ** Allocate transfer buffer */ xfr_buffer = (char *)malloc( packet_size * window_size ); buf_status = (char *)malloc( window_size * sizeof( BUF_STATUS ) ); if( (xfr_buffer == NULL) || (buf_status == NULL) ) { printf( "\n\nMALLOC ERROR\n\n" ); exit( 1 ); } /* ** Initialize all buffers to empty */ memset( buf_status, 0, (window_size * sizeof( BUF_STATUS )) ); /* ** Transfer init frame */ status = -1; while( status == -1 ) { /* ** Check for interrupt selected */ if( kbhit() ) { key = getche(); if( key == ESC ) { send_special_msg( 0, 'F' ); return(-1); } } send_init_frame(); time( &start_time ); status = wait_for_ack( start_time, ack_buffer, sizeof( ack_buf ) + 2 ); if( (status != 0) ) { status = -1; printf( "ERROR GETTING INIT ACK\n" ); } else if( ack_ptr->type == 'N' ) { status = -1; printf( "NAK RECEIVED FOR INIT MESSAGE \n" ); } } /* ** Transfer file header */ status = -1; while( status == -1 ) { /* ** Check for interrupt selected */ if( kbhit() ) { key = getche(); if( key == ESC ) { send_special_msg( 1, 'F' ); return(-1); } } send_header_frame( filename ); time( &start_time ); status = wait_for_ack( start_time, (char *)&ack_buf, sizeof( ack_buf ) ); if( (status != 1) || (ack_buf.data != 'H') ) { status = -1; printf( "ERROR GETTING HEADER ACK\n" ); } else if( ack_buf.type == 'N' ) { status = -1; printf( "NAK RECEIVED FOR HEADER MESSAGE \n" ); } } /* ** Transfer file */ /* ** Initialize and send buffers */ status = TRUE; for( i=0; i 0 ) { done = FALSE; if( error_flag[DATA_MSG_TYPE][CHECKSUM_ERROR] && (error_count < 5) ) { if( DEBUG ) { printf( "sending error in bcc, error_count = %d\n", error_count ); } bcc_ptr = xfr_buffer + (i*packet_size); bcc_ptr += 4; bcc_ptr += stat_ptr->data_size; *bcc_ptr += 1; error_count++; output_bytes( xfr_buffer + (i*packet_size), stat_ptr->data_size + sizeof( OVER_DATA ) ); time( &stat_ptr->send_time ); *bcc_ptr -= 1; } else { if( !error_flag[DATA_MSG_TYPE][TIMEOUT_ERROR] || (error_count >= 5) ) { output_bytes( xfr_buffer + (i*packet_size), stat_ptr->data_size + sizeof( OVER_DATA ) ); time( &stat_ptr->send_time ); } } } } while( !done ) { /* ** Check for interrupt selected */ if( kbhit() ) { key = getche(); if( key == ESC ) { send_special_msg( next_ack_to_receive, 'F' ); return(-1); } } time( ¤t_time ); stat_ptr = (BUF_STATUS_PTR)buf_status + next_ack_to_receive; ack_received = nak_received = FALSE; while( (current_time < (stat_ptr->send_time + timeout)) && !ack_received && !nak_received ) { status = wait_for_ack( stat_ptr->send_time, (char *)&ack_buf, sizeof( ack_buf ) ); if( status >= 0 ) { if( next_ack_to_receive != status ) { /* ** Must processes previous acks that were lost */ for( i=next_ack_to_receive; i != status; ) { stat_ptr = (BUF_STATUS_PTR)buf_status; stat_ptr += i; /* ** PROCESS ACK */ stat_ptr->status = BUF_ACKED; if( ack_buf.data == 'D' ) { if( fill_buffer( file_desc, i, xfr_buffer + (i * packet_size), stat_ptr ) ) { output_bytes( xfr_buffer + (i*packet_size), stat_ptr->data_size + sizeof( OVER_DATA ) ); time( &stat_ptr->send_time ); } last_ack_received++; if( last_ack_received == window_size ) last_ack_received = 0; } i++; if( i == window_size ) i = 0; } } /* ** Now process current ack/ nak */ stat_ptr = (BUF_STATUS_PTR)buf_status; stat_ptr += status; stat_ptr->status = BUF_ACKED; if( (ack_buf.data == 'D') && (ack_buf.type != 'N') ) { if( fill_buffer( file_desc, status, xfr_buffer + (status * packet_size), stat_ptr ) ) { output_bytes( xfr_buffer + (status*packet_size), stat_ptr->data_size + sizeof( OVER_DATA ) ); time( &stat_ptr->send_time ); } ack_received = TRUE; last_ack_received = status; next_ack_to_receive = status + 1; if( next_ack_to_receive == window_size ) next_ack_to_receive = 0; } else if( (ack_buf.data == 'D') && (ack_buf.type == 'N') ) { nak_received = TRUE; } } else { printf( "ERROR IN DATA PACKET OR TIMEOUT" ); } time( ¤t_time ); } if( !ack_received ) { /* ** Timeout or nak occurred, resend packet */ stat_ptr = (BUF_STATUS_PTR)buf_status + next_ack_to_receive; /* ** Indicate that this is a retransmission */ type_ptr = xfr_buffer + (next_ack_to_receive * packet_size); type_ptr += 3; *type_ptr = 'R'; output_bytes( xfr_buffer + (next_ack_to_receive*packet_size), stat_ptr->data_size + sizeof( OVER_DATA ) ); time( &stat_ptr->send_time ); } all_bufs_empty = TRUE; for( i=0; istatus != BUF_EMPTY ) all_bufs_empty = FALSE; } if( all_bufs_empty ) { done = TRUE; last_ack_received++; if( last_ack_received == window_size ) last_ack_received = 0; send_special_msg( last_ack_received, 'E' ); } } free( xfr_buffer ); free( buf_status ); return( TRUE ); } void send_special_msg( char seq_num, char type ) { /* ** break transmission/ end file same size as ack */ ACK_DATA break_buf; break_buf.beg_char = BEGIN_CHAR; /* ** Output length (seq_num + type + (data) + checksum) */ break_buf.length = to_char( 1 + 1 + (1) + 1); break_buf.seq_num = to_char( seq_num ); break_buf.type = type; break_buf.data = ' '; break_buf.bcc = to_char( calc_check_sum( &break_buf.data, 1, break_buf.seq_num, break_buf.type ) ); break_buf.end_char = END_CHAR; output_bytes( (char *)&break_buf, sizeof( break_buf ) ); } void send_init_frame( ) { INIT_DATA init_buf; init_buf.beg_char = BEGIN_CHAR; /* ** Output length (seq_num + type + (data) + checksum) */ init_buf.length = to_char( 1 + 1 + (1 + 1 + 1) + 1 ); init_buf.seq_num = to_char( 0 ); init_buf.type = 'I'; init_buf.max_len = to_char( packet_size ); init_buf.timeout = to_char( timeout ); init_buf.packet_end = to_char( END_CHAR ); init_buf.bcc = calc_check_sum( &init_buf.max_len, 3, init_buf.seq_num, init_buf.type ); if( error_flag[INIT_MSG_TYPE][CHECKSUM_ERROR] && (error_count < 5) ) { init_buf.bcc++; error_count++; } init_buf.bcc = to_char( init_buf.bcc ); init_buf.end_char = END_CHAR; if( !error_flag[INIT_MSG_TYPE][TIMEOUT_ERROR] || (error_count >= 5) ) { output_bytes( (char *)&init_buf, sizeof( init_buf ) ); } } int send_header_frame( char * filename ) { char *send_buf; char *char_ptr; char *data_ptr; send_buf = (char *)malloc( sizeof( OVER_DATA ) + strlen( filename ) ); if( send_buf == NULL ) { printf( "MALLOC ERROR IN SEND_HEADER_FRAME \n" ); exit( 0 ); } char_ptr = send_buf; *char_ptr++ = BEGIN_CHAR; /* ** Output length (seq_num + type + (strlen( name ) ) + checksum) */ *char_ptr = to_char( 1 + 1 + (strlen( filename ) ) + 1 ); char_ptr++; *char_ptr = to_char( 1 ); char_ptr++; *char_ptr = 'H'; char_ptr++; data_ptr = char_ptr; memcpy( char_ptr, filename, strlen( filename ) ); char_ptr += strlen( filename ); *char_ptr= calc_check_sum( data_ptr, strlen( filename ), to_char( 1 ), 'H' ); if( error_flag[HEADER_MSG_TYPE][CHECKSUM_ERROR] && (error_count < 5) ) { *char_ptr += 1; error_count++; } *char_ptr = to_char( *char_ptr ); char_ptr++; *char_ptr = END_CHAR; char_ptr++; if( !error_flag[HEADER_MSG_TYPE][TIMEOUT_ERROR] || (error_count >= 5) ) { output_bytes( send_buf, char_ptr - send_buf ); } free( send_buf ); return( char_ptr - send_buf ); } void wait_for_out_port() { int status; while( TRUE ) { status = inportb( (int)&COM->status ); if( (status & (S_TXE | S_TBE)) != 0 ) return; } } void output_bytes( char *buffer, int num_bytes ) { int i; if( DEBUG ) { printf( "sending %d bytes- *start*", num_bytes ); } for( i=0; idata, buffer[i] ); if( DEBUG ) { printf( "%d/", buffer[i] ); } } if( DEBUG ) { printf("*end*\n" ); } } int wait_for_ack( time_t start_time, char *ack_buf, int size ) { int done = FALSE; int status = -1; time_t current_time; char *char_ptr = ack_buf; ACK_DATA_PTR ack_ptr = (ACK_DATA_PTR)ack_buf; char save_char; while( !done ) { if( count > 0 ) { save_char = get_char_from_buffer(); if( DEBUG ) { printf( " %d ", save_char ); } if( save_char == BEGIN_CHAR ) { char_ptr = ack_buf; *char_ptr++ = save_char; } else if( save_char == END_CHAR ) { if( DEBUG ) { printf( "ENDCHAR\n" ); } *char_ptr++ = save_char; if( char_ptr == (char *)(ack_buf + size) ) { done = TRUE; if( validate_ack_check_sum( (ACK_DATA_PTR)ack_buf, size - sizeof( ACK_DATA ) + 1 ) ) { /* ** Convert those items converted to characters */ ack_ptr->length = from_char( ack_ptr->length ); ack_ptr->seq_num = from_char( ack_ptr->seq_num ); status = ack_ptr->seq_num; if( DEBUG ) { if( ack_ptr->type == 'A' ) printf( "\n ack for seq_num %d\n", status ); else printf( "\n nak for seq_num %d\n", status ); } } } } else if( char_ptr == (char *)(ack_buf + size) ) { done = TRUE; } else { *char_ptr++ = save_char; } } time( ¤t_time ); if( (current_time - start_time) > timeout ) done = TRUE; } return( status ); } char get_char_from_buffer() { static char ret_char; ret_char = *buffer_out; buffer_out++; if( buffer_out == buffer_end ) buffer_out = buffer_start; disable(); count--; enable(); return( ret_char ); } char calc_check_sum( char *data, char length, char seq_num, char type ) { int i; char chksum; chksum = to_char( length + 3 ); chksum += seq_num; chksum += type; for( i = 0; i < length; i++ ) { chksum += data[i]; } chksum = (char)(((chksum&0300) >> 6) + chksum) & 077; return( chksum ); } int validate_ack_check_sum( ACK_DATA_PTR ack_ptr, int data_size ) { char chksum; char *char_ptr; int i; chksum = ack_ptr->length + ack_ptr->seq_num + ack_ptr->type; for( i=1, char_ptr = &ack_ptr->data; i<=data_size; i++, char_ptr++ ) chksum += *char_ptr; chksum = (char) (((chksum&0300) >> 6) + chksum) & 077; if( chksum == from_char( *char_ptr ) ) return( TRUE); else return( FALSE ); } int fill_buffer( int file_desc, int win_num, char *buf_ptr, BUF_STATUS_PTR stat_ptr ) { int status; int data_size = packet_size - sizeof( OVER_DATA ); char *tmp_buffer; char *char_ptr = buf_ptr; tmp_buffer = (char *)malloc( data_size ); if( !tmp_buffer ) { printf( "MALLOC ERROR \n"); printf( "in fill buffer, packet_size = %d data_size = %d \n", packet_size, data_size ); exit( 0 ); } status = read( file_desc, tmp_buffer, data_size ); if( status < 0 ) { printf( "FILE READ ERROR \n" ); exit( 0 ); } else if( status == 0 ) { /* ** file empty */ stat_ptr->status = BUF_EMPTY; } else { *char_ptr++ = BEGIN_CHAR; /* ** Output length (seq_num + type + (data) + checksum) */ *char_ptr++ = to_char( 1 + 1 + status + 1 ); *char_ptr++ = to_char( win_num ); *char_ptr++ = 'D'; memcpy( char_ptr, tmp_buffer, status ); char_ptr += status; *char_ptr = calc_check_sum( tmp_buffer, status, to_char( win_num ), 'D' ); *char_ptr = to_char( *char_ptr ); char_ptr++; *char_ptr = END_CHAR; stat_ptr->status = BUF_FILLED; stat_ptr->data_size = status; } free( tmp_buffer ); return( status ); }