Important: The information in this document is obsolete and should not be used for new development.
Using Options
This section explains how you use endpoint functions to set and retrieve option values and how you use Open Transport utility functions to construct an options buffer and parse through an options buffer.Determining Which Function to Use to Negotiate Options
You can negotiate options using theOTOptionManagementfunction or using any one of the endpoint functions used to transfer data or establish a connection. The basic distinction between setting option values using theOTOptionManagementfunction and using any of the other endpoint functions is that options negotiated with theOTOptionManagementfunction affect all functions called by an endpoint, whereas options negotiated using any other function affect only the connection, transaction, or datagram for which they are set. For more detailed information about these differences, see "XTI Option Summary".Obtaining the Maximum Size of an Options Buffer
Different types of endpoints support different numbers of options. For example, an ATP endpoint might support more options than a DDP endpoint and might need a larger buffer to hold the options. When you call theOTOptionManagementfunction to change option values, the function returns in theretparameter a pointer to the buffer containing the negotiated option values. You must have allocated the buffer used to store these options before calling the function. Likewise, when you call theOTListen,OTRcvUData,OTRcvURequest,orOTRcvConnectfunctions, you can allocate a buffer in which current option values are to be placed when these functions return. In either case, you must specify the size of the buffer, and the buffer must be large enough to hold all of the endpoint's options. Otherwise, the function fails with akOTBufferOverflowresult. You can obtain the maximum size of a buffer used to store options for your endpoint by examining theoptionsfield of theTEndpointInfostructure for the endpoint. You can get a pointer to this structure when you open the endpoint, when you bind the endpoint, or when you call theOTGetEndpointInfofunction.Setting Option Values
You can use theOTOptionManagement,OTAccept,OTSndUData,OTSndURequest, andOTConnectfunctions to set option values. Setting option values results in a negotiation process between you (the client application) and the endpoint provider or, in the case of association-related options, between local and remote clients and their endpoint providers. Appendix D describes the rules that govern an option negotiation that you have initiated using theOTOptionManagement,OTConnect,OTSndUData, orOTSndURequestfunctions. The section "Retrieving Values for Connection-Oriented Endpoints" describes the negotiation rules that hold when you use theOTOptionManagementorOTAcceptfunctions to respond to a negotiation. This section describes ways in which you can build the options buffer used to specify the options you want to change.Specifying Option Values
No matter which function you use to set option values, you must allocate a buffer that contains the option value or values you want to change. The options in this buffer are described byTOptionstructures; the format of this structure is illustrated in Figure 7-1 . You can concatenate several structures in the buffer, as shown by Figure 7-2 , so long as each structure begins on a long-word boundary. The buffer itself is described by aTNetbufstructure that specifies the location of the buffer and its size.You can create a buffer that contains the option values you want to set in one of two ways: manually or by using the
OTCreateOptionsfunction. If you construct the buffer manually, you must do the following:
To have Open Transport create a buffer for you, you must call the
- Allocate the buffer.
- Create a
TOptionstructure for each option you want to change.- Initialize each field of the
TOptionstructure except for thestatusfield.- Place the
TOptionstructures in the buffer, making sure that each begins on a long-word boundary. This enables Open Transport to parse the buffer.
OTCreateOptionsfunction and pass it a string containing one or more option values. This method saves time and trouble, but you can only use it if all the options in the buffer are for the same level and that level is the same as the top-level protocol for the endpoint provider. That is to say, you could not use this method to construct a buffer that contains DDP-level options for an ATP endpoint. In addition, this method is only guaranteed to work if you are building an options buffer for theOTOptionManagementfunction.Listing 7-1 shows how you construct an options buffer by using the
OTCreateOptionsfunction. The code initializes a string array,myStr, to hold option values. It then creates aTOptMgmtstructure, which would later be passed to theOTOptionManagementfunction to request the option values specified in the string. Finally, it calls theOTCreateOptionsfunction to create the options buffer. TheOTCreateOptionsfunction creates theTOptionstructures and places them in the buffer, making sure that the structures are properly aligned.Listing 7-1 Constructing an options buffer using the
OTCreateOptionsfunction
char* myStr = "BaudRate = 9650 DataBits = 8 Parity = 0 StopBits = 10"; UInt8 buffer[512]; TOptMgmt cmd; cmd.opt.len = 0; cmd.opt.maxlen = sizeof(buffer); cmd.opt.buf = buffer; cmd.flags = T_NEGOTIATE err = OTCreateOptions("SerialA", &myStr, &cmd.opt)In this case, the initial value ofcmd.opt.len, which is 0, tells theOTCreateOptionsfunction at what offset it should begin to append option information in the buffer. When the function returns, this field specifies the actual length of the buffer.Setting Default Values
To set all of an endpoint's options to their default values, call theOTOptionManagementfunction, specifyingT_NEGOTIATEfor theflagsfield and allocating a buffer containing only one option namedT_ALLOPT. Doing this saves you the trouble of constructing aTOptionstructure for every option the endpoint supports. However, there is no guarantee that the provider can honor your request simply because you request default values. Therefore, you must allocate a buffer that is large enough to hold the option values returned in theretparameter.Retrieving Option Values
This section describes how you can retrieve information about options, including obtaining current and default option values for an endpoint and obtaining current option values related to a connection, transaction, or datagram.When retrieving option values, you must allocate a buffer that is large enough to contain the options when the function returns. The section "Obtaining the Maximum Size of an Options Buffer" explains how you do this.
Obtaining Current and Default Values
To obtain some of an endpoint's default or current option values, you call theOTOptionManagementfunction. You specifyT_DEFAULTorT_CURRENTfor theflagsfield of thereqparameter, and you use theoption.buffield to specify the option names in which you are interested. When the function returns, it placesTOptionstructures, describing the default or current option values, in the buffer referenced by theopt.buffield of theretparameter.If you are interested in obtaining all of an endpoint's default or current values, you can use the following methods:
Using
- To obtain an endpoint's default values, call the
OTOptionManagementfunction, specifyingT_DEFAULTfor the flags field andT_ALLOPTfor the option name.
- To obtain an endpoint's current option values, call the
OTOptionManagementfunction, specifyingT_CURRENTfor theflagsfield andT_ALLOPTfor the option name.
T_ALLOPTfor the option name allows you to construct an input buffer that contains only one option. Remember, however, that you must allocate an output buffer that is large enough to hold all of an endpoint's option values when the function returns.Parsing an Options Buffer
If you use theOTOptionManagementfunction to set, verify, or retrieve values, the function returns in theretparameter a pointer to a buffer containing option information. You can use theOTCreateOptionStringfunction to parse this buffer and create a string that lists all options and their current values.The code fragment shown in Listing 7-2 calls the
OTOptionManagementfunction to retrieve the option values currently effective for an endpoint. On return, theOTOptionManagementfunction stores these in thecmdstructure. Next, the code calls theOTCreateOptionStringfunction. The first input parameter,"SerialA", specifies the name of the protocol. The next input parameter,opts, is a pointer to the buffer containing the option values returned by theOTOptionManagementfunction. The expressioncmd.opt.buf + cmd.opt.len, which provides the next input parameter, specifies the length of the buffer. Using this information, theOTCreateOptionStringfunction returns a string containing each option name and its respective value. The final parameter to theOTCreateOptionStringfunction specifies the length of the string.Listing 7-2 Using the
OTCreateOptionStringfunction to parse through a buffer
TOptMgmt cmd; UINt8 myBuffer[512]; char myString[256]; cmd.opt.len = sizeof(TOption); cmd.opt.maxlen = sizeof(myBuffer); cmd.opt.buf = myBuffer; ((TOption*) buffer)->len = sizeof(TOption); ((TOption*) buffer)->level = COM_SERIAL; ((TOption*) buffer)->name = T_ALLOPT; ((TOption*) buffer)->status = 0; cmd.flags = T_CURRENT; OTOptionManagement(theEndpt, &cmd, &cmd); TOption* opts = (TOption*)cmd.opt.buf; err = OTCreateOptionString("SerialA", &opts, cmd.opt.buf + cmd.opt.len, string, sizeof(string)); printf("Options = \"%s\"", string);
- Note
- The
OTCreateOptionStringfunction is supplied solely as a debugging aid. You should not include the function in a production version of your application because there is no provision made for localizing string information.![]()
Verifying Option Values
In addition to obtaining default or current values and negotiating new values, you can use theOTOptionManagementfunction to verify whether an endpoint supports one or more options. To do this, you construct a buffer containingTOptionstructures describing the options you are interested in and pass this buffer in thereqparameter to theOTOptionManagementfunction, specifyingT_CHECKfor the action flag. When the function returns, you can examine thestatusfield of theTOptionstructures for the options passed back to you in theretparameter to determine whether the specified options are supported.Sample Code: Getting and Setting Options
The code listings discussed in this section furnish examples of how you can use the Open Transport API to get, set, and display the values of options.Listing 7-3 shows a main function that calls a number of other functions (defined in subsequent listings) to set, get, and display option values.
Listing 7-3 Calling functions that get, set, and display options
#ifndef qDebug / *OT debugging macros need this var */ #define qDebug 1 #endif #include <OpenTransport.h> #include <OpenTptInternet.h> /* for TCP/IP */ #include <OpenTptSerial.h>/* for serial endpoints */ #include <OTDebug.h>/* Need OTDebugBreak and OTAssert macros */ #include <stdio.h>/* Standard C prototypes */ /* OTDebugStr is not defined in any OT header files, but it is exported by the libraries, so we define the prototype here. */ extern pascal void OTDebugStr(const char* str); void main(void) { OSStatus err; OSStatus junk; EndpointRef ep; UInt32 value; printf("HelloWorld!\n"); err = InitOpenTransport(); if (err == noErr) { ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err); if (err == noErr) { printf("\nGetting and Setting IP_REUSEADDR.\n"); err = GetFourByteOption(ep, INET_IP, IP_REUSEADDR, &value); if (err == noErr) printf("Default value = %d\n", value); if (err == noErr) err = SetFourByteOption(ep, INET_IP, IP_REUSEADDR, true); if (err == noErr){ err = GetFourByteOption(ep, INET_IP, IP_REUSEADDR, &value); if (err == noErr) printf("New value = %d\n", value) } if (err == noErr) { printf("\nPrinting Options Piecemeal at Level INET_IP.\n"); err = PrintAllOptionsAtLevel(ep, INET_IP); } if (err == noErr){ printf("\nPrinting Formatted Options at Level COM_SERIAL.\n"); err = PrintOptionsForConfiguration(kSerialName, COM_SERIAL); } if (err == noErr) { printf("\nBuilding Options for COM_SERIAL.\n"); err = BuildAndPrintOptions(kSerialName, "BaudRate=9600, DataBits=7, StopBits=15"); } junk = OTCloseProvider(ep); OTAssert("GetSetOptions: Closing the endpoint failed", junk == noErr); } CloseOpenTransport(); } if (err == noErr) printf("Success.\n"); else printf("Failed with error %d.\n", err); printf("Done. Press command-Q to Quit.\n"); }This main function initializes Open Transport and then creates a TCP endpoint. Then, it calls the functionGetFourByteOptionto obtain the value of theIP_REUSEADDRoption, which governs whether you can bind multiple endpoints to addresses with the same port number. Listing 7-4 contains the definition of theGetFourByteOptionfunction.Listing 7-4 Getting an option value
static OTResult GetFourByteOption(EndpointRef ep, OTXTILevel level, OTXTIName name, UInt32 *value) { OTResult err; TOption option; TOptMgmt request; TOptMgmt result; /* Set up the option buffer */ option.len = kOTFourByteOptionSize; option.level= level; option.name = name; option.status = 0; option.value[0] = 0;// Ignored because we're getting the value. /* Set up the request parameter for OTOptionManagement to point to the option buffer we just filled out */ request.opt.buf= (UInt8 *) &option; request.opt.len= sizeof(option); request.flags= T_CURRENT; /* Set up the reply parameter for OTOptionManagement. */ result.opt.buf = (UInt8 *) &option; result.opt.maxlen = sizeof(option); err = OTOptionManagement(ep, &request, &result); if (err == noErr) { switch (option.status) { case T_SUCCESS: case T_READONLY: *value = option.value[0]; break; default: err = option.status; break; } } return (err); }The functionGetFourByteOptiongets the current option setting and assigns it to the location referenced byvalue. The endpoint is assumed to be in synchronous mode. If an error occurs, the function returns a negative result. If the option could not be read, a positive result (eitherT_FAILURE, T_PARTSUCCESS, orT_NOTSUPPORT) is returned.Within the body of the function, the fields of the option buffer are set up to specify the option and value we want to get. The
TOptionstructure is used to represent the option buffer. This structure is defined to allow easy construction of 4-byte options.Next, the
requestparameter for theOTOptionManagementfunction is defined to reference the option buffer that was just initialized. Therequest.flagsfield is initialized toT_CURRENT, specifying that we want to get the current value of the option. Thereplyparameter for theOTOptionManagementfunction is then initialized. This is where the function stores the result of the negotiation. Finally, theOTOptionManagementfunction is called, and its result is checked to see that the option value was read successfully. Any status other thanT_SUCCESSorT_READONLYis stored in theerrvariable.The next function called by
mainisSetFourByteOption, shown in Listing 7-5. This routine sets an option and assigns it to the location referenced byvalue. The endpoint is assumed to be in synchronous mode. If an error occurs, the function returns a negative result. If the option could not be read, a positive result (T_FAILURE,T_PARTSUCCESS,TREADONLY, orT_NOTSUPPORT) is returned.Listing 7-5 Setting an option value
static OTResult SetFourByteOption(EndpointRef ep, OTXTILevel level, OTXTIName name, UInt32 value) { OTResult err; TOption option; TOptMgmt request; TOptMgmt result; /* Set up the option buffer to specify the option and value to set. */ option.len = kOTFourByteOptionSize; option.level= level; option.name = name; option.status = 0; option.value[0] = value; /* Set up request parameter for OTOptionManagement */ request.opt.buf= (UInt8 *) &option; request.opt.len= sizeof(option); request.flags = T_NEGOTIATE; /* Set up reply parameter for OTOptionManagement. */ result.opt.buf = (UInt8 *) &option; result.opt.maxlen = sizeof(option); err = OTOptionManagement(ep, &request, &result); if (err == noErr) { if (option.status != T_SUCCESS) err = option.status; } return (err); }TheSetFourByteOptionfunction is very similar in structure to theGetFourByteOptionfunction, shown in the previous listing. Once again, we use aTOptionstructure to represent the option buffer and initialize its fields to specify the option and value we want to set. Next, we initialize therequestparameter of theOTOptionManagementfunction to reference the option buffer we just initialized. Thelengthfield is set to the size of the option buffer and theflagsfield is set toT_NEGOTIATEto specify that we want to set the option value specified in the option buffer.The
replyparameter for theOTOptionManagementfunction is then set up. This is where the function will store the negotiated value of the option when it returns. Finally, theOTOptionManagementfunction is invoked and its result is checked to make sure the option was successfully negotiated.After calling the
SetFourByteOptionfunction,maincalls theGetFourByteOptionfunction again to check the newly set value. The next three functions,PrintAllOptionsAtLevel,PrintOptionsForConfig, andBuildAndPrintOptionsdemonstrate various ways of displaying option values for an endpoint. Two of those functions call the functionPrintOptionBuffershown in Listing 7-6. This function calls the Open Transport functionOTNextOptionto parse through an options buffer and then usesprintfstatements to display the results.Listing 7-6 Parsing an options buffer
static OSStatus PrintOptionBuffer(const TNetbuf *optionBuffer) { OSStatus err; TOption *currentOption; currentOption = nil; do {err = OTNextOption(optionBuffer->buf, optionBuffer->len, ¤tOption); if (err == noErr && currentOption != nil) printf("Level = $%08x, Name = $%08x, Data Length = %d, Status = $%08x\n", currentOption->level, currentOption->name, currentOption->len - kOTOptionHeaderSize, currentOption->status); } while (err == noErr && currentOption != nil); return (err); }The PrintOptionBuffer function displays the level, name, size, and status for each option in the option buffer. ThePrintAllOptionsAtLevelfunction, whichmaincalls next, uses this function to display all the options for an endpoint that are set at a specified level.Listing 7-7 Obtaining options for a specific level
static OSStatus PrintAllOptionsAtLevel(EndpointRef ep, OTXTILevel level) { OSStatus err; TEndpointInfoepInfo; TOptionHeaderrequestOption; void *resultOptionBuffer; TOptMgmt request; TOptMgmt result; resultOptionBuffer = nil; /* Find max size of options of endpoint and allocate buffer */ err = OTGetEndpointInfo(ep, &epInfo); if (err == noErr) { resultOptionBuffer = OTAllocMem(epInfo.options); if (resultOptionBuffer == nil) err = kENOMEMErr; } /* Call OTOptionManagement to get current option values */ if (err == noErr) { requestOption.len= kOTOptionHeaderSize; requestOption.level= level; requestOption.name= T_ALLOPT; requestOption.status = 0; request.opt.buf= (UInt8 *) &requestOption; request.opt.len= sizeof(requestOption); request.flags = T_CURRENT; result.opt.buf = resultOptionBuffer; result.opt.maxlen = epInfo.options; err = OTOptionManagement(ep, &request, &result); } /* Print options to stdout. */ if (err == noErr) { err = PrintOptionBuffer(&result.opt); printf("\n"); } if (resultOptionBuffer != nil) OTFreeMem(resultOptionBuffer); return (err); }The functionPrintAllOptionsAtLeveltakes two parameters, an endpoint reference and a level value. The function first callsOTGetEndpointInfoto determine the maximum size of options for the endpoint and then allocates a buffer to hold the options after they are read. Next, theOTOptionManagementfunction is called to get the current value (T_CURRENT) of all the options (T_ALLOPT) set for the endpoint. The option values that are returned are stored in the buffer referenced by theresultparameter. A pointer to the result parameter is passed to the functionPrintOptionsBuffer, which displays option values. After the values are displayed, the memory allocated for the result options buffer is freed.The next function called by
mainisPrintOptionsForConfiguration, shown in Listing 7-8. This function gets all the options associated with a level of a provider, converts those options to a formatted string, and then displays that string. The function demonstrates one common use of the functionOTCreateOptionString.Listing 7-8 Using the
OTCreateOptionStringfunction
static OSStatus PrintOptionsForConfiguration(const char *configStr, OTXTILevel level) { OSStatus err; OSStatus junk; EndpointRef ep; TEndpointInfoepInfo; TOptionHeaderrequestOption; void *resultOptionBuffer; TOptMgmt request; TOptMgmt result; TOption *resultOption; char optionsString[1024]; resultOptionBuffer = nil; ep = kOTInvalidEndpointRef; /* Create an endpoint using the specified configuration. */ ep = OTOpenEndpoint(OTCreateConfiguration(configStr), 0, &epInfo, &err); /* Allocate a buffer to store option info */ if (err == noErr) { resultOptionBuffer = OTAllocMem(epInfo.options); if (resultOptionBuffer == nil) err = kENOMEMErr; } /* Get the current value of all options at the specified level. */ if (err == noErr) { requestOption.len = kOTOptionHeaderSize; requestOption.level= level; requestOption.name= T_ALLOPT; requestOption.status = 0; request.opt.buf = (UInt8 *) &requestOption; request.opt.len = sizeof(requestOption); request.flags = T_CURRENT; result.opt.buf = resultOptionBuffer; result.opt.maxlen = epInfo.options; err = OTOptionManagement(ep, &request, &result); } /* Convert the options bufferinto a formatted string, and display */ if (err == noErr) { resultOption = (TOption *) result.opt.buf; err = OTCreateOptionString(configStr, &resultOption, result.opt.buf + result.opt.len, optionsString, 1024); if (err == noErr) printf("Formatted Options = "%s"\n\n", optionsString); } /* Clean up. */ if (resultOptionBuffer != nil) OTFreeMem(resultOptionBuffer); if (ep != kOTInvalidEndpointRef) { junk = OTCloseProvider(ep); OTAssert("PrintOptionsForConfiguration: Closing the endpoint failed", junk == noErr); } return (err); }ThePrintOptionsForConfigurationfunction takes two parameters, a configuration string that describes a specific endpoint provider and a level for which we are interested in getting option information. The function first opens an endpoint using the configuration information passed in. Note that the address of a buffer to hold endpoint information (&epInfo) is also specified in the call. We need the endpoint information structure in order to determine the maximum size of the options buffer for the endpoint.Next, the function allocates a buffer to hold option values passed back by the
OTOptionManagementfunction. The value ofT_ALLOPTfor thenamefield andT_CURRENTfor theflagsfield specify that we are interested in getting currently set values for all options set for the endpoint. After the buffer is allocated, theOTOptionManagementfunction is invoked. Finally, theOTCreateOptionStringfunction is invoked; this function converts the option buffer passed back by theOTOptionManagementfunction into a formatted string, which is then displayed using aprintfstatement.The last function called by
main,BuildAndPrintOptions, accomplishes the reverse of thePrintOptionsForConfigurationfunction: it takes a configuration string and a set of formatted options, converts the options to their binary format (that is, an options buffer), and then displays the contents of that buffer. TheBuildAndPrintOptionsfunction is shown in Listing 7-9.Listing 7-9 Building an options buffer from a configuration string
static OSStatus BuildAndPrintOptions(const char *configStr, const char *optionsString) { OSStatus err; OSStatus junk; void *resultOptionBuffer; EndpointRef ep; TEndpointInfo epInfo; TNetbuf optionsNetbuf; resultOptionBuffer = nil; ep = kOTInvalidEndpointRef; /* Create an endpoint using the specified configuration. */ ep = OTOpenEndpoint(OTCreateConfiguration(configStr), 0, &epInfo, &err); /* Allocate a buffer of the maximum option buffer size. */ if (err == noErr) { resultOptionBuffer = OTAllocMem(epInfo.options); if (resultOptionBuffer == nil) err = kENOMEMErr; } /* Parse formatted optionsString into the binary format */ if (err == noErr) { optionsNetbuf.buf = resultOptionBuffer; optionsNetbuf.len = 0; optionsNetbuf.maxlen = epInfo.options; err = OTCreateOptions(configStr, (char **) &optionsString, &optionsNetbuf); if (err == noErr) { err = PrintOptionBuffer(&optionsNetbuf); printf("\n"); } } /* Clean up. */ if (resultOptionBuffer != nil) OTFreeMem(resultOptionBuffer); if (ep != kOTInvalidEndpointRef) { junk = OTCloseProvider(ep); OTAssert("BuildAndPrintOptions: Closing the endpoint failed", junk == noErr); } return (err); }The functionBuildAndPrintOptionscreates an endpoint using the specified configuration. When opening the endpoint, the address of an endpoint information structure (&epInfo) is passed in; the endpoint provider fills in this structure with information about the endpoint, including its maximum option buffer size. The function then allocates a buffer that is large enough to contain option information for the endpoint.Next the function
BuildAndPrintOptionscalls theOTCreateOptionsfunction to parse the formattedoptionsStringinto the binary format (optionsNetbuf). Then the function callsPrintOptionBufferto display the contents of the options buffer. Finally, the function frees memory allocated for the options buffer and returns.
Subtopics
- Determining Which Function to Use to Negotiate Options
- Obtaining the Maximum Size of an Options Buffer
- Setting Option Values
- Specifying Option Values
- Setting Default Values
- Retrieving Option Values
- Obtaining Current and Default Values
- Parsing an Options Buffer
- Verifying Option Values
- Sample Code: Getting and Setting Options