/*****************************************************************************/ /* */ /* Copyright Digital Equipment Corporation 1996. All rights reserved */ /* */ /* Restricted Rights: Use, duplication, or disclosure by the U.S. Government*/ /* is subject to restrictions as set forth in subparagraph (c) (1) (ii) of */ /* DFARS 252.227-7013, or in FAR 52.227-19, or in FAR 52.227-14 Alt. III, as */ /* applicable. */ /* */ /* This software is proprietary to and embodies the confidential technology */ /* of Digital Equipment Corporation. Possession, use, or copying of this */ /* software and media is authorized only pursuant to a valid written license */ /* from Digital or an authorized sublicensor. */ /* */ /* spi_example_resp.c */ /*****************************************************************************/ #ifdef VMS #ifndef SUCCESS #define SUCCESS 1 #endif #ifndef FAILURE #define FAILURE 0 #endif #endif #ifdef unix #ifndef SUCCESS #define SUCCESS 0 #endif #ifndef FAILURE #define FAILURE 1 #endif #endif #include #include #include #include #include #include #include #define TRUE 1 #define FALSE 0 #define OSAK_EXAMPLE_WS_SIZE 1024 #define OSAK_EXAMPLE_BUFFER_SIZE 2048 #define OSAK_EXAMPLE_TIMEOUT 60 void open_responder (osak_port *port, struct osak_parameter_block *pb); unsigned long int connect_rsp (osak_port port, struct osak_parameter_block *pb); unsigned long int release_rsp (osak_port port, struct osak_parameter_block *pb); void wait_for_outbound_completion (osak_port port, struct osak_parameter_block *pb); void wait_for_inbound (osak_port port, struct osak_parameter_block *pb); void wait_for_TDISind (osak_port port, struct osak_parameter_block *pb); unsigned long int give_buffer (osak_port port); void reuse_buffers (struct osak_buffer **ptr); void display_data (struct osak_buffer *buf); void *alloc_memory (unsigned long int size); unsigned long int free_memory (unsigned long int size, void *ptr); extern unsigned long int spi_open_responder (); extern unsigned long int spi_accept_rsp (); extern unsigned long int spi_release_rsp (); extern unsigned long int spi_close_port (); extern unsigned long int spi_select (); extern unsigned long int spi_get_event (); extern unsigned long int spi_collect_pb (); extern unsigned long int spi_give_buffers (); /*---------------------------------------------------------------------------*/ /* Global variables */ /*---------------------------------------------------------------------------*/ /* Structure to hold functional units. Functional units include things */ /* like what kind of data we're using, whether the data transfer is */ /* duplex or half duplex, etc. */ struct osak_fus fus; struct osak_aei local_address; /* Queue of buffers for spi_give_buffers. These are used in the */ /* routines give_buffer and reuse_buffers. */ struct osak_buffer *free_buffers = NULL; struct osak_buffer *free_buffers_end = NULL; /****************************************************************************/ /* FUNCTION: main */ /****************************************************************************/ main (int argc, char *argv[]) { unsigned long int status; /* To hold return values from */ /* routines */ struct osak_parameter_block *in_pb; /* Parameter block to use in */ /* calls to spi_get_event */ struct osak_parameter_block *out_pb; /* Parameter block to use in */ /* outbound calls */ osak_port port; /* To hold the port handle */ /* returned from */ /* spi_open_responder */ struct osak_buffer *buffer_ptr; /* To hold list of buffers */ /* returned from */ /* spi_close_port */ struct osak_parameter_block *pb_ptr; /* To hold list of parameter */ /* blocks returned from */ /* spi_close_port */ /*----------------------------------------------------------------------*/ /* Allocate a parameter block. The same parameter block is used on all */ /* outbound calls to OSAK. Any second failure to allocate an out_pb */ /* (or in_pb) may warrant some memory clean-up. /*----------------------------------------------------------------------*/ out_pb = (struct osak_parameter_block *) malloc (sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE); if (out_pb == NULL) { printf("Failed to allocate outbound parameter block.\n"); exit(FAILURE); } /*-----------------------------------------------------------------------*/ /* Allocate a parameter block. The parameter block is used on all */ /* inbound calls to OSAK. Any second failure to allocate an in_pb */ /* (or out_pb) may warrant some memory clean-up. */ /*-----------------------------------------------------------------------*/ in_pb = (struct osak_parameter_block *) malloc (sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE); if (in_pb == NULL) { printf("Failed to allocate inbound parameter block.\n"); exit(FAILURE); } /*-----------------------------------------------------------------------*/ /* Open Responder /*-----------------------------------------------------------------------*/ printf ("Opening a responder port.\n"); open_responder (&port, out_pb); /*-----------------------------------------------------------------------*/ /* Wait for connect-ind */ /*-----------------------------------------------------------------------*/ printf("Waiting for S-CONNECT indication.\n"); wait_for_inbound (port, in_pb); if (in_pb->event_type != OSAK_C_ASSOC_IND) { printf("Expected S-CONNECT indication. Received some other event.\n"); exit(FAILURE); } printf ("Received S-CONNECT indication.\n"); /* Display the user-information sent by the initiator */ if (in_pb->peer_data != NULL) { printf("Data Received:\n"); display_data (in_pb->peer_data); } reuse_buffers (&in_pb->tsdu_ptr); /* Here a real application would need to check that the application context*/ /* name, proposed presentation contexts and proposed functional units */ /* If these were not acceptable to the application then it would need */ /* to abort or reject the association. For simplicity this example will */ /* accept the association without doing these checks. */ /*-----------------------------------------------------------------------*/ /* Send accept response */ /*-----------------------------------------------------------------------*/ printf("Sending S-CONNECT-ACCEPT response.\n"); status = connect_rsp (port, out_pb); if (status == OSAK_S_QUEUED) { /* If OSAK returned OSAK_S_QUEUED then it is still using the */ /* parameter block and the structures that the parameter block */ /* parameters point to. The following routine waits for OSAK */ /* to be finished with the parameter block (using spi_collect_pb). */ printf("OSAK returned QUEUED status.\n"); printf("Waiting for request to complete.\n"); wait_for_outbound_completion (port, out_pb); printf("Outbound request completed.\n"); } /*---------------------------------------------------------------------*/ /* Wait for release-ind */ /*---------------------------------------------------------------------*/ printf ("Waiting for S-RELEASE indication.\n"); wait_for_inbound (port, in_pb); if (in_pb->event_type != OSAK_C_RELEASE_IND) { printf("Expected S-RELEASE indication. Received some other event.\n"); exit(FAILURE); } printf("Received S-RELEASE indication.\n"); reuse_buffers (&in_pb->tsdu_ptr); /*---------------------------------------------------------------------*/ /* Send Release Response */ /*---------------------------------------------------------------------*/ printf("Sending S-RELEASE response.\n"); status = release_rsp (port, out_pb); if (status == OSAK_S_QUEUED) { /* If OSAK returned OSAK_S_QUEUED then it is still using the */ /* parameter block and the structures that the parameter block */ /* parameters point to. The following routine waits for OSAK */ /* to be finished with the parameter block (using spi_select) and */ /* gets the parameter block back from OSAK (using spi_collect_pb) */ printf("OSAK returned QUEUED status.\n"); printf("Waiting for request to complete.\n"); wait_for_outbound_completion (port, out_pb); printf("Outbound request completed.\n"); } /*-----------------------------------------------------------------------*/ /* Wait for transport disconnect */ /*-----------------------------------------------------------------------*/ printf("Waiting for T-DISCONNECT indication.\n"); wait_for_TDISind (port, in_pb); if (in_pb->event_type != OSAK_C_TDIS) { printf("Expected T-DISCONNECT indication. Received some other event.\n"); exit(FAILURE); } printf("Received T-DISCONNECT indication.\n"); /*-----------------------------------------------------------------------*/ /* Close the port */ /*-----------------------------------------------------------------------*/ /* The spi_close_port is OSAK_C_DESTRUCTIVE rather than */ /* OSAK_C_NON_DESTRUCTIVE. This ensures that the transport connection */ /* is disconnected. The call to wait_for_TDISind above may have timed */ /* out if the peer did not send a transport disconnect. This may not */ /* be necessary on OpenVMS since OSAK implements the session disconnect */ /* timer on that operating system. However the session disconnect */ /* timer is not implemented in OSAK on Digital UNIX. That is why the */ /* peer does not send one before a certain timeout. The code in this */ /* example will work on all operating systems. */ printf("Closing responder port.\n"); status = spi_close_port (port, &buffer_ptr, &pb_ptr, OSAK_C_DESTRUCTIVE); if (status != OSAK_S_NORMAL) { printf("Call to spi_close_port failed.\n"); exit(FAILURE); } reuse_buffers (&buffer_ptr); } /****************************************************************************/ /* FUNCTION: open_responder */ /* */ /* This routine sets up the parameter block for a call to */ /* spi_open_responder and makes the call. */ /* */ /****************************************************************************/ void open_responder (osak_port *port, struct osak_parameter_block *pb) { unsigned long int status; /* Set up the local address */ memset ((void *)&local_address, 0, sizeof(struct osak_aei)); /* Initialize presentation address */ local_address.paddress.psel.size = sizeof(unsigned long int); local_address.paddress.psel.pointer = (unsigned char *)"RESP-PSEL"; local_address.paddress.ssel.size = sizeof(unsigned long int); local_address.paddress.ssel.pointer = (unsigned char *)"RESP-SSEL"; local_address.paddress.tsel.size = sizeof(unsigned long int); local_address.paddress.tsel.pointer = (unsigned char *)"RESP-TSEL"; local_address.paddress.nsap.type = OSAK_C_CLNS; /* initialize parameter block */ memset ((void *)pb, 0, sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE); pb->pb_length = sizeof(struct osak_parameter_block); pb->ws_length = OSAK_EXAMPLE_WS_SIZE; pb->api_version = OSAK_C_API_VERSION_3; pb->local_aei = &local_address; pb->alloc_rtn = (osak_rtn)alloc_memory; pb->dealloc_rtn = (osak_rtn)free_memory; status = spi_open_responder (port, pb); if (status != OSAK_S_NORMAL) { printf("Call to spi_open_responder failed.\n"); exit(FAILURE); } return; } /*****************************************************************************/ /* FUNCTION: connect_rsp */ /* */ /* This routine sets up the parameter block for a call to spi_accept_rsp */ /* and makes the call. */ /* */ /*****************************************************************************/ unsigned long int connect_rsp (osak_port port, struct osak_parameter_block *pb) { unsigned long int status; /* Set up functional units */ /* A real application would need to check which functional units were */ /* proposed by the initiator and negotiate a common set of functional */ /* units. This simple example assumes that the duplex functional unit */ /* was proposed. It is only going to accept the duplex functional unit. */ memset ((void *)&fus, 0, sizeof(struct osak_fus)); fus.duplex = 1; /* initialize parameter block */ memset ((void *)pb, 0, sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE); pb->pb_length = sizeof(struct osak_parameter_block); pb->ws_length = OSAK_EXAMPLE_WS_SIZE; pb->functional_units = &fus; /* All other fields using default values */ status = spi_accept_rsp (port, pb); if ((status != OSAK_S_NORMAL) && (status != OSAK_S_QUEUED)) { printf("Call to spi_accept_rsp failed.\n"); exit(FAILURE); } return status; } /*****************************************************************************/ /* FUNCTION: release_rsp */ /* This routine sets up the parameter block for a call to spi_release_rsp */ /* and makes the call. */ /* */ /*****************************************************************************/ unsigned long int release_rsp (osak_port port, struct osak_parameter_block *pb) { unsigned long int status; /* initialize parameter block */ memset ((void *)pb, 0, sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE); pb->pb_length = sizeof(struct osak_parameter_block); pb->ws_length = OSAK_EXAMPLE_WS_SIZE; pb->release_reason = OSAK_C_RLRE_NORMAL; status = spi_release_rsp (port, pb); if ((status != OSAK_S_NORMAL) && (status != OSAK_S_QUEUED)) { printf("Call to spi_release_rsp failed.\n"); exit(FAILURE); } return status; } /*****************************************************************************/ /* FUNCTION: wait_for_outbound_completion */ /* */ /* This routine is called when OSAK_S_QUEUED is returned by OSAK. */ /* It uses spi_select to wait for OSAK to finish with the parameter */ /* block, and calls spi_collect_pb to get back ownership of the parameter */ /* block. */ /* */ /* This routine uses the mask OSAK_C_WRITEEVENT in the call to spi_select. */ /* If we wanted to wait for an inbound event (such as receiving the release */ /* indication) at the same time we should have used the mask */ /* OSAK_C_WRITEEVENT | OSAK_C_READEVENT. We would then have to check the */ /* returned mask from spi_select to see what action to take: whether to */ /* call spi_collect_pb or spi_get_event or both. The example */ /* spi_example_init.c does that in its routine wait_for_event. */ /*****************************************************************************/ void wait_for_outbound_completion (osak_port port, struct osak_parameter_block *pb) { struct osak_parameter_block *ret_pb; osak_handle_count handlecount; osak_handle handle; unsigned long int status; osak_time select_time; /* Set up the parameter to call spi_select() */ handlecount = 1; handle.id = (unsigned long int) port; handle.request_mask = OSAK_C_WRITEEVENT; handle.returned_mask = 0; select_time = OSAK_EXAMPLE_TIMEOUT; status = spi_select (handlecount, &handle, &select_time); if (status != OSAK_S_NORMAL) { printf("Call to spi_select failed.\n"); exit(FAILURE); } /* See if there is an inbound event. If so call spi_get_event() */ ret_pb = NULL; status = spi_collect_pb (port, &ret_pb); if (status != OSAK_S_NORMAL) { printf("Call to spi_collect_pb failed.\n"); exit(FAILURE); } /* The parameter block returned must be the same as that given in */ /* the OSAK call. We check here for sanity. */ if (ret_pb != pb) { printf("Parameter block returned from spi_collect_pb is\n"); printf("different from that expected.\n"); exit(FAILURE); } /* The parameter block returned will have the status block filled */ /* in by OSAK. This needs to be checked to find out if an error */ /* occured. */ if (ret_pb->status_block.osak_status_1 != OSAK_S_NORMAL) { printf("An error has been reported in the status block of the\n"); printf("parameter block returned by OSAK.\n"); exit(FAILURE); } } /****************************************************************************/ /* FUNCTION: wait_for_inbound */ /* */ /* This routine waits for an inbound event, i.e. it waits to receive an */ /* event from the initiator process. */ /* */ /* It uses spi_select to wait for the event (using the mask */ /* OSAK_C_READEVENT) and picks up the event using spi_get_event. Buffers */ /* need to be given to OSAK before an event can be read. The routine */ /* give_buffer is used to pass buffers to OSAK when they are needed (it */ /* calls spi_give_buffers to do this). */ /* */ /* If we wanted to wait for an outbound completion (if OSAK returned */ /* OSAK_S_QUEUED from a previous call) as well as an inbound event then */ /* we should have used the mask OSAK_C_WRITEEVENT | OSAK_C_READEVENT. */ /* We would then have to check the returned mask from spi_select to see */ /* what action to take: whether to call spi_collect_pb or spi_get_event */ /* or both. The example spi_example_init.c does that in its routine */ /* wait_for_event. */ /****************************************************************************/ void wait_for_inbound (osak_port port, struct osak_parameter_block *pb) { osak_handle_count handlecount; osak_handle handle; unsigned long int status; osak_time select_time; /* The following declaration is used for the status returned from */ /* give_buffer. */ unsigned long int buf_status; /* Give a buffer to OSAK to get inbound event */ buf_status = give_buffer (port); if (buf_status != OSAK_S_NORMAL) { printf("Call to spi_give_buffers failed.\n"); exit(FAILURE); } /* Loop until an event is received */ do { /* Set up parameter block to call spi_select() */ handlecount = 1; handle.id = (unsigned long int)port; handle.request_mask = OSAK_C_READEVENT; handle.returned_mask = 0; select_time = OSAK_EXAMPLE_TIMEOUT; status = spi_select (handlecount, &handle, &select_time); if (status != OSAK_S_NORMAL) { printf("Call to spi_select failed.\n"); exit(FAILURE); } /* See if there is an inbound event. If so call spi_get_event() */ do { /* Initialize parameter block */ memset ((void *)pb, 0, sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE); pb->pb_length = sizeof(struct osak_parameter_block); pb->ws_length = OSAK_EXAMPLE_WS_SIZE; status = spi_get_event (port, pb); /* If OSAK needs more buffers to decode the event then give */ /* more buffers. */ if (status == OSAK_S_NOBUFFERS) { buf_status = give_buffer (port); if (buf_status != OSAK_S_NORMAL) { printf("Call to spi_give_buffers failed.\n"); exit(FAILURE); } } } while (status == OSAK_S_NOBUFFERS); if ((status != OSAK_S_NORMAL) && (status != OSAK_S_NOEVENT)) { printf("Call to spi_get_event failed.\n"); exit(FAILURE); } } while (status == OSAK_S_NOEVENT); /* pb will now contain the decoded event */ } /****************************************************************************/ /* FUNCTION: wait_for_TDISind */ /* */ /* This routine uses spi_select to wait for a transport disconnect */ /* indication after the release-response has been sent. */ /* */ /* It does not check the event since OSAK_S_NOEVENT may be returned. This */ /* would be the case when the peer did not send a disconnect. spi_select */ /* would return either when it has timed out, or (on OpenVMS only) when the */ /* session disconnect timer fired. */ /****************************************************************************/ void wait_for_TDISind (osak_port port, struct osak_parameter_block *pb) { osak_handle_count handlecount; osak_handle handle; unsigned long int status; osak_time select_time; /* The following declaration is used for the status returned from */ /* give_buffer. */ unsigned long int buf_status; /* Give a buffer to OSAK to get inbound event */ buf_status = give_buffer (port); if (buf_status != OSAK_S_NORMAL) { printf("Call to spi_give_buffers failed.\n"); exit(FAILURE); } /* Set up parameter to call spi_select() */ handlecount = 1; handle.id = (unsigned long int)port; handle.request_mask = OSAK_C_READEVENT; handle.returned_mask = 0; select_time = OSAK_EXAMPLE_TIMEOUT; status = spi_select (handlecount, &handle, &select_time); if (status != OSAK_S_NORMAL) { printf("Call to spi_select failed.\n"); exit(FAILURE); } /* See if there is an inbound event. If so call spi_get_event() */ /* initialize parameter block */ memset ((void *)pb, 0, sizeof(struct osak_parameter_block) + OSAK_EXAMPLE_WS_SIZE); pb->pb_length = sizeof(struct osak_parameter_block); pb->ws_length = OSAK_EXAMPLE_WS_SIZE; status = spi_get_event (port, pb); if ((status != OSAK_S_NORMAL) && ( status != OSAK_S_NOEVENT)) { printf("Call to spi_get_event failed.\n"); exit(FAILURE); } } /*************************************************************************/ /* FUNCTION: give_buffer */ /* */ /* This routine is called to pass a buffer to OSAK for OSAK to use to */ /* receive inbound events. */ /* */ /* A list of unused buffers is maintained. One buffer from this list is */ /* passed to OSAK using spi_give_buffers. If the list is empty a new */ /* buffer is allocated. */ /*************************************************************************/ unsigned long int give_buffer (osak_port port) { unsigned long int status; struct osak_buffer *give_buf; /* Give a buffer to OSAK */ if (free_buffers == NULL) { give_buf = (struct osak_buffer *)malloc (sizeof( struct osak_buffer)); if (give_buf == NULL ) { printf("Failed to allocate an osak buffer.\n"); exit(FAILURE); } give_buf->next = NULL; give_buf->buffer_length = OSAK_EXAMPLE_BUFFER_SIZE; give_buf->buffer_ptr = (unsigned char *)malloc (OSAK_EXAMPLE_BUFFER_SIZE); if (give_buf->buffer_ptr == NULL) { printf("Failed to allocate buffer.\n"); exit(FAILURE); } } else { give_buf = free_buffers; free_buffers = free_buffers->next; give_buf->next = NULL; } status = spi_give_buffers (port, give_buf); return status; } /**************************************************************************/ /* FUNCTION: reuse_buffers */ /* */ /* This routine is called to place buffers returned by OSAK onto the list */ /* of unused buffers. */ /**************************************************************************/ void reuse_buffers (struct osak_buffer **buf_ptr) { struct osak_buffer *buf, *last_buf; buf = *buf_ptr; if (buf == NULL) return; last_buf = buf; while (last_buf->next != NULL) last_buf = last_buf->next; if (free_buffers == NULL) { free_buffers = buf; } else { free_buffers_end->next = buf; } free_buffers_end = last_buf; *buf_ptr = NULL; } /************************************************************************/ /* FUNCTION: display_data */ /* */ /* Print out the data in the buffer 'buf'. */ /************************************************************************/ void display_data (struct osak_buffer *buf) { unsigned char *ptr; unsigned long int length; int count = 0; if (buf == NULL) { printf("We were passed a null pointer.\n"); exit(FAILURE); } ptr = buf->data_ptr; length = buf->data_length; while (length--) { if (count == 0) { count = 12; printf("\n\t"); } count--; printf("0x%02X ", *ptr++); } printf("\n\n"); } /*****************************************************************************/ /* FUNCTION: alloc_memory */ /* */ /* Memory allocation routine expected by OSAK */ /*****************************************************************************/ void *alloc_memory (unsigned long int size) { return (void *)malloc(size); } /*****************************************************************************/ /* FUNCTION: free_memory */ /* */ /* Memory deallocation routine expected by OSAK */ /*****************************************************************************/ unsigned long int free_memory (unsigned long int size, void *ptr) { free(ptr); return(0); }