/* This version pretty good. Still slow but uses fossil successfully */ /* and seems to be fairly robust. Needs to have command line args */ /* reduced to not require the darned baud rate as an argument. */ #include #include #include #include #include #include #include /* Dos-specific functions are used to get date/time. */ #include "common.h" /* Definitions/routines Al uses lots. */ #include "modem.h" /* Routines for accessing the modem. */ #include "protocol.h" /* Packet send/receive routines. */ /* Packet types: - Receiver: 'a'bort, 'e'of acknowledged, 's'end from byte x - Transmitter: 'a'bort, 'd'ata from byte x, 'f'ile header (size x) State types: - Receiver: waiting for 'h'eader, waiting for 'd'ata - Transmitter: waiting for header 'a'ck, 's'ending data, waiting for 'e'of ack */ #define PROTOCOL_STARTING_BLOCK_LENGTH 64 #define PROTOCOL_SUCCESSIVE_PACKETS_NEEDED 8 #define TIMEOUT_LONG 30 /* Longest timeout we're willing to endure. */ #define TIMEOUT_MODEM 7 /* Longest time we expect EC modems to falter. */ #define RETRY_LIMIT 5 /* Max. number of retries to restart an xfer. */ #define ERRORLEVEL_SUCCESS 0 #define ERRORLEVEL_BAD_PARAMETER 1 #define ERRORLEVEL_TRANSFER_FAILED 2 char packet_buffer[PROTOCOL_PACKET_MAX]; char current_state; /* Used by xmit/rcv routines to hold current state. */ unsigned short packet_length,retry_count,repeat_count; long bytes_in_file,temp_long,byte_no,next_byte_no_expected; long file_fptr=0; /* Position in the file we're at. */ time_t transfer_start_time; /* Hold time for CPS computation. */ int current_timeout,resent_packet_count=0; /*--------------------------- Subroutines ----------------------------------*/ long get_byte_no_from_packet_buffer() { long msb=0,msb2=0,lsb2=0,lsb=0; if (packet_buffer[1] < 0) msb = (unsigned long)packet_buffer[1] + 256; else msb = (unsigned long)packet_buffer[1]; msb <<= 24; if (packet_buffer[2] < 0) msb2 = (unsigned long)packet_buffer[2] + 256; else msb2 = (unsigned long)packet_buffer[2]; msb2 <<= 16; if (packet_buffer[3] < 0) lsb2 = (unsigned long)packet_buffer[3] + 256; else lsb2 = (unsigned long)packet_buffer[3]; lsb2 <<= 8; if (packet_buffer[4] < 0) lsb = (unsigned long)packet_buffer[4] + 256; else lsb = (unsigned long)packet_buffer[4]; /* printf("get_byte_from_packet_buffer() %d %d %d %d\n",packet_buffer[1],packet_buffer[2],packet_buffer[3],packet_buffer[4]); printf("msb msb2 lsb2 lsb: %ld %ld %ld %ld\n",msb,msb2,lsb2,lsb); */ return(msb | msb2 | lsb2 | lsb); } void put_byte_no_into_packet_buffer(long the_number) { unsigned char msb=0,msb2=0,lsb2=0,lsb=0; msb = (the_number >> 24) & 255; packet_buffer[1] = msb; msb2 = (the_number >> 16) & 255; packet_buffer[2] = msb2; lsb2 = (the_number >> 8) & 255; packet_buffer[3] = lsb2; lsb = the_number & 255; packet_buffer[4] = lsb; /* printf("put_byte_into_packet_buffer() %d %d %d %d\n",packet_buffer[1],packet_buffer[2],packet_buffer[3],packet_buffer[4]); */ } void tell_other_side_to_abort() { repeat_count = 10; /* Send lots of these; the extra ones will be trashed. */ packet_buffer[0] = 'a'; while (repeat_count--) protocol_send_packet(modem_port,packet_buffer,1,common_debug_level_current+1); } /*----------------------------- Routines -----------------------------------*/ int send_file_header_packet(char filename_only[]) { common_debug_message(common_debug_level_current,"Sending file header packet."); strcpy(packet_buffer,"f"); /* Signifies a file header packet. */ put_byte_no_into_packet_buffer(bytes_in_file); strcpy(packet_buffer+5,filename_only); /* Name.ext in string form..... */ if (protocol_send_packet(modem_port,packet_buffer,5+strlen(packet_buffer+5),common_debug_level_current+1)) return(1); return(0); } aom_file_send(char *filespec_to_send) { FILE *file_to_send; int current_block_length,successive_good_packet_count,maximum_block_length; unsigned short bytes_to_send; char filename_only[16],*temp_char_ptr; maximum_block_length = ((modem_speed / 10) * TIMEOUT_MODEM) / 2; if (maximum_block_length > 1024) maximum_block_length = 1024; printf("Maximum allowed block length to send is %d.\n",maximum_block_length); temp_char_ptr = strrchr(filespec_to_send,'\\'); /* Simplify filespec to just file.ext. */ if (temp_char_ptr == NULL) strcpy(filename_only,filespec_to_send); else strcpy(filename_only,temp_char_ptr+1); file_to_send = fopen(filespec_to_send,"rb"); /* Open the file. */ if (file_to_send == NULL) { printf("!File to send %s not found.\n",filespec_to_send); return(1); /* Error sending file. */ } fseek(file_to_send,0,SEEK_END); /* Move to EOF. */ bytes_in_file = ftell(file_to_send); /* Save filesize. */ printf("Sending file %s (%ld bytes).\n",filename_only,bytes_in_file); printf("Waiting %d seconds for receiver to come on-line before sending.\n",TIMEOUT_MODEM); printf("Hit ESC to abort transfer.\n"); wait(TIMEOUT_MODEM); if (send_file_header_packet(filename_only)) { printf("?Carrier lost; aborting....\n"); goto abort_transfer; } current_block_length = PROTOCOL_STARTING_BLOCK_LENGTH; successive_good_packet_count = 0; goto wait_for_header_ack; send_loop: if (kbhit()) /* Keyboard abort? */ { getch(); fclose(file_to_send); /* ...whether or not it's open! */ printf("!Keyboard abort; quitting transfer....\n"); goto abort_transfer; } if (!fossil_cd(fossil_current_port)) { fclose(file_to_send); printf("?Carrier lost; aborting....\n"); trash_modem_output(modem_port,0); return(1); } packet_length = protocol_get_packet(modem_port,current_timeout,packet_buffer,common_debug_level_current+1); switch (current_state) { case 'a': /* Waiting for file header ack. */ { if (!packet_length) /* Timeout on file header ack? */ { if (retry_count++ == RETRY_LIMIT) { fclose(file_to_send); printf("?Retry count exceeded while waiting for file header ack; aborting....\n"); return(1); } if (send_file_header_packet(filename_only)) { printf("?Carrier lost; aborting....\n"); goto abort_transfer; } goto send_loop; } switch (packet_buffer[0]) { case 'a': printf("!Remote aborted\n"); return(1); case 's': break; /* Correct type; fall through. */ default : printf("?Unexpected packet type %c received; aborting....\n",packet_buffer[0]); goto abort_transfer; } printf("Starting send of file data....\n"); transfer_start_time = time(NULL); /* For CPS calculation later. */ file_fptr = get_byte_no_from_packet_buffer(); goto send_data; } case 's': { if (!packet_length) /* Receiver had nothing to complain about? */ { if ((successive_good_packet_count++ >= PROTOCOL_SUCCESSIVE_PACKETS_NEEDED) && (current_block_length < maximum_block_length)) { printf("Doubling block length to %d.\n",current_block_length*2); current_block_length *= 2; successive_good_packet_count = 0; } goto send_data; } switch (packet_buffer[0]) /* What kind of packet did we receive? */ { case 's': break; case 'a': printf("?Remote requested abort; aborting....\n"); fclose(file_to_send); wait(TIMEOUT_MODEM); trash_modem_output(modem_port,0); return(1); default : printf("?Invalid packet type %c received; aborting....\n",packet_buffer[0]); return(1); } file_fptr = get_byte_no_from_packet_buffer(); printf("Resend requested at byte %ld\n",file_fptr); resent_packet_count++; successive_good_packet_count = 0; current_block_length = PROTOCOL_STARTING_BLOCK_LENGTH; goto send_data; } case 'e': { if (!packet_length) { if (retry_count++ == RETRY_LIMIT) /* Timed out. Retry-limit yet? */ { fclose(file_to_send); printf("?Timeout waiting for eof ack from receiver -- the status of\n"); printf(" the file on the receiver's end is unknown; aborting....\n"); goto abort_transfer; } /* Send a data packet with byte of -1 to ask receiver to request a */ /* resend at whatever byte it wants, since it hasn't acked EOF. */ packet_buffer[0] = 'd'; put_byte_no_into_packet_buffer(-1L); packet_buffer[5] = 0; /* Dummy data byte. */ bytes_to_send = 1; if (protocol_send_packet(modem_port,packet_buffer,5+bytes_to_send,common_debug_level_current+1)) { fclose(file_to_send); printf("?Carrier lost; aborting....\n"); trash_modem_output(modem_port,0); return(1); } goto send_loop; } switch (packet_buffer[0]) { case 'a': printf("?Remote requested abort; aborting....\n"); fclose(file_to_send); wait(TIMEOUT_MODEM); trash_modem_output(modem_port,0); return(1); case 'e': fclose(file_to_send); printf("File successfully acknowledged by receiver; %d packets resent.\n",resent_packet_count); printf("Transfer CPS: %ld\n",bytes_in_file/(time(NULL)-transfer_start_time)); wait(TIMEOUT_MODEM); trash_modem_output(modem_port,0); return(0); case 's': file_fptr = get_byte_no_from_packet_buffer(); printf("Resend requested at byte %ld\n",file_fptr); resent_packet_count++; successive_good_packet_count = 0; current_block_length = PROTOCOL_STARTING_BLOCK_LENGTH; goto send_data; default : printf("?Invalid packet type %c received while waiting for eof ack; aborting....\n",packet_buffer[0]); goto abort_transfer; } } default: { printf("?Invalid current state %d\n",(int)current_state); return(1); } } /*----- These paragraphs set up the proper state and go to the loop. -----*/ wait_for_header_ack: current_state = 'a'; current_timeout = TIMEOUT_MODEM; retry_count = 0; goto send_loop; send_data: printf("%ld / %ld %c",file_fptr,bytes_in_file,13); /* Provide tiny progress display. */ packet_buffer[0] = 'd'; /* Start building outgoing data packet. */ put_byte_no_into_packet_buffer(file_fptr); if ((bytes_in_file - file_fptr) >= current_block_length) /* Find size to send.*/ bytes_to_send = current_block_length; else bytes_to_send = bytes_in_file - file_fptr; fseek(file_to_send,file_fptr,SEEK_SET); fread(packet_buffer+5,bytes_to_send,1,file_to_send); if (protocol_send_packet(modem_port,packet_buffer,5+bytes_to_send,common_debug_level_current+1)) { fclose(file_to_send); printf("?Carrier lost; aborting....\n"); trash_modem_output(modem_port,0); return(1); } file_fptr += bytes_to_send; /* Data sent; adjust this accordingly. */ if (file_fptr < bytes_in_file) /* Have we sent the last byte of data yet? */ { current_state = 's'; current_timeout = 0; goto send_loop; } /* We have sent last data byte. This state can only occur at this point. */ current_state = 'e'; current_timeout = TIMEOUT_MODEM; goto send_loop; /*----- This is just for aborting transfers. -----*/ abort_transfer: tell_other_side_to_abort(); wait(TIMEOUT_MODEM); trash_modem_output(modem_port,0); return(1); } /*--------- Now the receive routine. --------*/ int aom_file_receive(char *file_to_receive) { FILE *incoming_file; char incoming_filename[128]; time_t time_of_last_send_request=0; printf("Waiting for file header packet from transmitter.\n"); goto wait_for_header_packet; receive_loop: if (kbhit()) /* Keyboard abort? */ { getch(); fclose(incoming_file); /* ...whether it's open or not! */ printf("!Keyboard abort; quitting transfer....\n"); goto abort_transfer; } if (!fossil_cd(fossil_current_port)) { fclose(incoming_file); printf("?Carrier lost; aborting....\n"); trash_modem_output(modem_port,0); return(1); } packet_length = protocol_get_packet(modem_port,current_timeout,packet_buffer,common_debug_level_current+1); switch (current_state) { case 'h': { if (!packet_length) /* Timeout on file header? */ return(1); switch (packet_buffer[0]) { case 'a': printf("!Remote aborted send; aborting....\n"); return(1); case 'f': break; /* Correct type; fall through. */ default : printf("?Unexpected packet type %c received; aborting....\n",packet_buffer[0]); goto abort_transfer; } transfer_start_time = time(NULL); /* For CPS calculation later. */ /* common_debug_message(common_debug_level_current,"Got file header packet from transmitter."); */ bytes_in_file = get_byte_no_from_packet_buffer(); if (file_to_receive[0]) strcpy(incoming_filename,file_to_receive); else strcpy(incoming_filename,packet_buffer+5); incoming_file = fopen(incoming_filename,"rb"); if (incoming_file != NULL) { fclose(incoming_file); printf("!Aborting receive of existing file %s.\n",incoming_filename); goto abort_transfer; } incoming_file = fopen(incoming_filename,"wb"); if (incoming_file == NULL) { printf("!Couldn't open incoming file %s for write.\n",incoming_filename); goto abort_transfer; } printf("Receiving file %s (%ld bytes)\n",incoming_filename,bytes_in_file); next_byte_no_expected = 0; /* We expect the file to start! */ printf("Syncing with sender.\n"); goto request_send_at_byte; } case 'd': { if (!packet_length) { if (retry_count++ == RETRY_LIMIT) /* Timed out. Retry-limit yet? */ { fclose(incoming_file); printf("?Retry count exceeded while waiting for data; aborting....\n"); goto abort_transfer; } goto receive_loop; } switch (packet_buffer[0]) /* What kind of packet did we receive? */ { case 'f': printf("Skipping extra file header packet.\n"); goto wait_for_data; /* Skip any late file header packets. */ case 'd': break; /* Correct type; fall through. */ case 'a': printf("?Remote requested abort; aborting....\n"); fclose(incoming_file); wait(TIMEOUT_MODEM); trash_modem_output(modem_port,0); return(1); default : printf("?Invalid packet type %c received; aborting....\n",packet_buffer[0]); return(1); } byte_no = get_byte_no_from_packet_buffer(); /* We got a data packet. */ if (byte_no == -1) { printf("Received position inquiry from transmitter; requesting resend at byte %ld.\n",next_byte_no_expected); goto request_send_at_byte; } printf("%ld / %ld %c",byte_no,bytes_in_file,13); /* Provide tiny progress display. */ if (byte_no < next_byte_no_expected) { printf("Skipping already-received data block.\n"); goto wait_for_data; /* Ignore. */ } if (byte_no != next_byte_no_expected) /* Not the right one? */ { /* Must be future packet. */ /* common_debug_message_w_no(common_debug_level_current+1,"Requesting resend at byte ",next_byte_no_expected); */ goto request_send_at_byte; } /* common_debug_message_w_no(common_debug_level_current,"Writing data block to incoming file at byte ",byte_no); */ fseek(incoming_file,byte_no,SEEK_SET); fwrite(packet_buffer+5,packet_length-5,1,incoming_file); if (ftell(incoming_file) == bytes_in_file) { fclose(incoming_file); printf("Sending file acknowledgement packet to transmitter.\n"); packet_buffer[0] = 'e'; repeat_count = 10; /* Sends lots; extras will be trashed. */ while (repeat_count--) protocol_send_packet(modem_port,packet_buffer,1,common_debug_level_current+1); printf("Successful receive of %s; %d resends requested.\n",incoming_filename,resent_packet_count-1); printf("Transfer CPS: %ld\n",bytes_in_file/(time(NULL)-transfer_start_time)); wait(TIMEOUT_MODEM); trash_modem_output(modem_port,0); return(0); } if (ftell(incoming_file) > bytes_in_file) { printf("?Incoming file has more bytes than original; aborting....\n"); return(1); } next_byte_no_expected += packet_length - 5; /* Packet_length - 5 = block size.*/ /* common_debug_message_w_no(common_debug_level_current,"Wrote to incoming file up to byte ",next_byte_no_expected); */ goto wait_for_data; } default: { printf("?Invalid current state %d\n",(int)current_state); return(1); } } /*----- These paragraphs set up the proper state and go to the loop. -----*/ request_send_at_byte: if ((time(NULL) - time_of_last_send_request) < TIMEOUT_MODEM) /* Too soon to do another resend request. */ { /* common_debug_message(common_debug_level_current,"Too early to request a resend."); */ goto wait_for_data; /* Just skip this resend request; buffers might be emptying. */ } printf("Requesting send from byte %ld.\n",next_byte_no_expected); packet_buffer[0] = 's'; put_byte_no_into_packet_buffer(next_byte_no_expected); if (protocol_send_packet(modem_port,packet_buffer,5,common_debug_level_current+1)) { fclose(incoming_file); printf("?Carrier lost; aborting....\n"); trash_modem_output(modem_port,0); return(1); } resent_packet_count++; time_of_last_send_request = time(NULL); goto wait_for_data; wait_for_header_packet: current_state = 'h'; current_timeout = TIMEOUT_LONG; goto receive_loop; wait_for_data: current_state = 'd'; current_timeout = TIMEOUT_MODEM; retry_count = 0; goto receive_loop; /*----- This is just for aborting transfers. -----*/ abort_transfer: tell_other_side_to_abort(); wait(TIMEOUT_MODEM); trash_modem_output(modem_port,0); return(1); } /*---------------------------- The Program ---------------------------------*/ int main(int argc, char *argv[]) { char arg_port[64],arg_baud[64],arg_function[64],arg_file_to_transfer[128]; int errcode; common_debug_level_screen = 0; /* Let's try this for now. */ printf("AOM Transfer Protocol, Version .01\n"); if (argc < 4) { printf("?Illegal number of arguments.\n"); goto bad_parameter; } strcpy(arg_port, argv[1]); strcpy(arg_baud, argv[2]); strcpy(arg_function,argv[3]); modem_port = atoi(arg_port); /* Open the modem port. There is no corresponding close */ modem_speed = atoi(arg_baud); /* of the port because the routines close automatically. */ if (!strcmp(arg_function,"s")) /* Sending a file? */ { if (argc < 5) { printf("?Send option specified, but no filename given!\n"); goto bad_parameter; } if (initSerial(modem_port,modem_speed,modem_parity,modem_bits,modem_stopbits)) { printf("?Unable to initialize modem port; is it correct?\n"); goto bad_parameter; } port_set_handshaking(1); /* Enable hardware handshaking. */ strcpy(arg_file_to_transfer,argv[4]); errcode = aom_file_send(arg_file_to_transfer); deinitSerial(1); wait(2); return(errcode); } if (!strcmp(arg_function,"r")) /* Receiving a file? */ { if (initSerial(modem_port,modem_speed,modem_parity,modem_bits,modem_stopbits)) { printf("?Unable to initialize modem port; is it correct?\n"); goto bad_parameter; } port_set_handshaking(1); /* Enable hardware handshaking. */ arg_file_to_transfer[0] = 0; if (argc == 5) /* Was file specified on command line? */ strcpy(arg_file_to_transfer,argv[4]); errcode = aom_file_receive(arg_file_to_transfer); deinitSerial(1); wait(1); return(errcode); } printf("?The invalid function code %s was received! 's' or 'r' are valid.\n",arg_function); bad_parameter: printf("\n"); printf("Command syntax: \n"); printf(" AOM [port(1|2)] [baud] [s|r] [file_to_xfer] \n"); printf(" where s is for send or r is for receive. \n"); printf(" e.g. AOM 1 19200 s help.doc \n"); printf(" The filename may be omitted when receiving. \n"); printf("Exiting with errorlevel %d.\n",ERRORLEVEL_BAD_PARAMETER); return(ERRORLEVEL_BAD_PARAMETER); }