diff --git a/ax25_link.c b/ax25_link.c index 565c11e..a085f34 100644 --- a/ax25_link.c +++ b/ax25_link.c @@ -887,6 +887,7 @@ static ax25_dlsm_t *get_link_handle (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LE // // dl_connect_request // dl_disconnect_request +// dl_outstanding_frames_request - (mine) Ask about outgoing queue for a link. // dl_data_request - send connected data // dl_unit_data_request - not implemented. APRS & KISS bypass this // dl_flow_off - not implemented. Not in AGW API. @@ -1505,6 +1506,80 @@ void dl_unregister_callsign (dlq_item_t *E) +/*------------------------------------------------------------------------------ + * + * Name: dl_outstanding_frames_request + * + * Purpose: Client app wants to know how many frames are still on their way + * to other station. This is handy for flow control. We would like + * to keep the pipeline filled sufficiently to take advantage of a + * large window size (MAXFRAMES). It is also good to know that the + * the last packet sent was actually received before we commence + * the disconnect. + * + * Inputs: E - Event from the queue. + * The caller will free it. + * + * Outputs: This gets back to the AGW server which sends the 'Y' reply. + * + * Description: This is the sum of: + * - Incoming connected data, from application still in the queue. + * - I frames which have been transmitted but not yet acknowleged. + * + *------------------------------------------------------------------------------*/ + +void dl_outstanding_frames_request (dlq_item_t *E) +{ + ax25_dlsm_t *S; + int ok_to_create = 0; // must exist already. + + + if (s_debug_client_app) { + text_color_set(DW_COLOR_DEBUG); + dw_printf ("dl_outstanding_frames_request ( to %s )\n", E->addrs[PEERCALL]); + } + + S = get_link_handle (E->addrs, E->num_addr, E->chan, E->client, ok_to_create); + + if (S == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Can't get outstanding frames for %s -> %s, chan %d\n", E->addrs[OWNCALL], E->addrs[PEERCALL], E->chan); + server_outstanding_frames_reply (E->chan, E->client, E->addrs[OWNCALL], E->addrs[PEERCALL], 0); + return; + } + +// Add up these +// +// cdata_t *i_frame_queue; // Connected data from client which has not been transmitted yet. +// // Linked list. +// // The name is misleading because these are just blocks of +// // data, not "I frames" at this point. The name comes from +// // the protocol specification. +// +// cdata_t *txdata_by_ns[128]; // Data which has already been transmitted. +// // Indexed by N(S) in case it gets lost and needs to be sent again. +// // Cleared out when we get ACK for it. + + int count1 = 0; + cdata_t *incoming; + for (incoming = S->i_frame_queue; incoming != NULL; incoming = incoming->next) { + count1++; + } + + int count2 = 0; + int k; + for (k = 0; k < S->modulo; k++) { + if (S->txdata_by_ns[k] != NULL) { + count2++; + } + } + + server_outstanding_frames_reply (S->chan, S->client, S->addrs[OWNCALL], S->addrs[PEERCALL], count1 + count2); + +} // end dl_outstanding_frames_request + + + /*------------------------------------------------------------------------------ * * Name: dl_client_cleanup diff --git a/dlq.c b/dlq.c index 5131674..732a32b 100644 --- a/dlq.c +++ b/dlq.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2014, 2015, 2016 John Langner, WB2OSZ +// Copyright (C) 2014, 2015, 2016, 2018 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -553,7 +553,69 @@ void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int append_to_queue (pnew); -} /* end dlq_connect_request */ +} /* end dlq_disconnect_request */ + + + +/*------------------------------------------------------------------- + * + * Name: dlq_outstanding_frames_request + * + * Purpose: Client application wants to know number of outstanding information + * frames supplied, supplied by the client, that have not yet been + * delivered to the remote station. + * + * Inputs: addrs - Source (owncall), destination (peercall) + * + * num_addr - Number of addresses. Should be 2. + * If more they will be ignored. + * + * chan - Channel, 0 is first. + * + * client - Client application instance. We could have multiple + * applications, all on the same channel, connecting + * to different stations. We need to know which one + * should get the results. + * + * Outputs: Request is appended to queue for processing by + * the data link state machine. + * + * Description: The data link state machine will count up all information frames + * for the given source(mycall) / destination(remote) / channel link. + * A 'Y' response will be sent back to the client application. + * + *--------------------------------------------------------------------*/ + +void dlq_outstanding_frames_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client) +{ + struct dlq_item_s *pnew; +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + dw_printf ("dlq_outstanding_frames_request (...)\n"); +#endif + + assert (chan >= 0 && chan < MAX_CHANS); + +/* Allocate a new queue item. */ + + pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1); + s_new_count++; + + pnew->type = DLQ_OUTSTANDING_FRAMES_REQUEST; + pnew->chan = chan; + memcpy (pnew->addrs, addrs, sizeof(pnew->addrs)); + pnew->num_addr = num_addr; + pnew->client = client; + +/* Put it into queue. */ + + append_to_queue (pnew); + +} /* end dlq_outstanding_frames_request */ + + + + /*------------------------------------------------------------------- diff --git a/dlq.h b/dlq.h index 336870c..3e7cf87 100644 --- a/dlq.h +++ b/dlq.h @@ -35,7 +35,7 @@ typedef struct cdata_s { /* Types of things that can be in queue. */ -typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_CHANNEL_BUSY, DLQ_SEIZE_CONFIRM, DLQ_CLIENT_CLEANUP} dlq_type_t; +typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_OUTSTANDING_FRAMES_REQUEST, DLQ_CHANNEL_BUSY, DLQ_SEIZE_CONFIRM, DLQ_CLIENT_CLEANUP} dlq_type_t; /* A queue item. */ @@ -108,6 +108,8 @@ void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client); +void dlq_outstanding_frames_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client); + void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len); void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client); diff --git a/server.c b/server.c index 0a74ff3..6e2c3c7 100644 --- a/server.c +++ b/server.c @@ -136,11 +136,7 @@ #include #include #include -#ifdef __OpenBSD__ #include -#else -#include -#endif #endif #include @@ -1121,6 +1117,50 @@ void server_rec_conn_data (int chan, int client, char *remote_call, char *own_ca } /* end server_rec_conn_data */ +/*------------------------------------------------------------------- + * + * Name: server_outstanding_frames_reply + * + * Purpose: Send 'Y' Outstanding frames for connected data to the application. + * + * Inputs: chan - Which radio channel. + * + * client - Which one of potentially several clients. + * + * own_call - Callsign[-ssid] of my end. + * + * remote_call - Callsign[-ssid] of remote station. + * + * count - Number of frames sent from the application but + * not yet received by the other station. + * + *--------------------------------------------------------------------*/ + +void server_outstanding_frames_reply (int chan, int client, char *own_call, char *remote_call, int count) +{ + + struct { + struct agwpe_s hdr; + int count_NETLE; + } reply; + + + memset (&reply.hdr, 0, sizeof(reply.hdr)); + + reply.hdr.portx = chan; + reply.hdr.datakind = 'Y'; + + strlcpy (reply.hdr.call_from, own_call, sizeof(reply.hdr.call_from)); + strlcpy (reply.hdr.call_to, remote_call, sizeof(reply.hdr.call_to)); + + reply.hdr.data_len_NETLE = host2netle(4); + reply.count_NETLE = host2netle(count); + + send_to_client (client, &reply); + +} /* end server_outstanding_frames_reply */ + + /*------------------------------------------------------------------- * * Name: read_from_socket @@ -1220,8 +1260,9 @@ static THREAD_F cmd_listen_thread (void *arg) struct { struct agwpe_s hdr; /* Command header. */ - char data[512]; /* Additional data used by some commands. */ + char data[AX25_MAX_PACKET_LEN]; /* Additional data used by some commands. */ /* Maximum for 'V': 1 + 8*10 + 256 */ + /* Maximum for 'D': Info part length + 1 */ } cmd; int client = (int)(long)arg; @@ -1864,6 +1905,7 @@ static THREAD_F cmd_listen_thread (void *arg) int n = 0; if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_CHANS) { + // Count both normal and expedited in transmit queue for given channel. n = tq_count (cmd.hdr.portx, -1, "", "", 0); } reply.data_NETLE = host2netle(n); @@ -1874,34 +1916,53 @@ static THREAD_F cmd_listen_thread (void *arg) case 'Y': /* How Many Outstanding frames wait for tx for a particular station */ - /* Number of frames sitting in transmit queue for given channel, */ - /* source (optional) and destination addresses. */ + // This is different than the above 'y' because this refers to a specific + // link in connected mode. + + // This would be useful for a couple different purposes. + + // When sending bulk data, we want to keep a fair amount queued up to take + // advantage of large window sizes (MAXFRAME, EMAXFRAME). On the other + // hand we don't want to get TOO far ahead when transferring a large file. + + // Before disconnecting from another station, it would be good to know + // that it actually received the last message we sent. For this reason, + // I think it would be good for this to include information frames that were + // transmitted but not yet acknowleged. + // You could say that a particular frame is still waiting to be sent even + // if was already sent because it could be sent again if lost previously. + + // The documentation is inconsistent about the address order. + // One place says "callfrom" is my callsign and "callto" is the other guy. + // That would make sense. We are asking about frames going to the other guy. + + // But another place says it depends on who initiated the connection. + // + // "If we started the connection CallFrom=US and CallTo=THEM + // If the other end started the connection CallFrom=THEM and CallTo=US" + // + // The response description says nothing about the order; it just mentions two addresses. + // If you are writing a client or server application, the order would + // be clear but right here it could be either case. + // + // Another version of the documentation mentioned the source address being optional. + // + + // The only way to get this information is from inside the data link state machine. + // We will send a request to it and the result coming out will be used to + // send the reply back to the client application. + { - char source[AX25_MAX_ADDR_LEN]; - char dest[AX25_MAX_ADDR_LEN]; - struct { - struct agwpe_s hdr; - int data_NETLE; // Little endian order. - } reply; + char callsigns[2][AX25_MAX_ADDR_LEN]; + const int num_calls = 2; - strlcpy (source, cmd.hdr.call_from, sizeof(source)); - strlcpy (dest, cmd.hdr.call_to, sizeof(dest)); + strlcpy (callsigns[AX25_SOURCE], cmd.hdr.call_from, sizeof(callsigns[AX25_SOURCE])); + strlcpy (callsigns[AX25_DESTINATION], cmd.hdr.call_to, sizeof(callsigns[AX25_SOURCE])); - memset (&reply, 0, sizeof(reply)); - reply.hdr.portx = cmd.hdr.portx; /* Reply with same port number, addresses. */ - reply.hdr.datakind = 'Y'; - strlcpy (reply.hdr.call_from, source, sizeof(reply.hdr.call_from)); - strlcpy (reply.hdr.call_to, dest, sizeof(reply.hdr.call_to)); - reply.hdr.data_len_NETLE = host2netle(4); + // Issue 169. Proper implementation for 'Y'. + dlq_outstanding_frames_request (callsigns, num_calls, cmd.hdr.portx, client); - int n = 0; - if (cmd.hdr.portx >= 0 && cmd.hdr.portx < MAX_CHANS) { - n = tq_count (cmd.hdr.portx, -1, source, dest, 0); - } - reply.data_NETLE = host2netle(n); - - send_to_client (client, &reply); } break; diff --git a/server.h b/server.h index 08db571..6e4eaa6 100644 --- a/server.h +++ b/server.h @@ -24,5 +24,7 @@ void server_link_terminated (int chan, int client, char *remote_call, char *own_ void server_rec_conn_data (int chan, int client, char *remote_call, char *own_call, int pid, char *data_ptr, int data_len); +void server_outstanding_frames_reply (int chan, int client, char *own_call, char *remote_call, int count); + /* end server.h */