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 theOTOptionManagement
function or using any one of the endpoint functions used to transfer data or establish a connection. The basic distinction between setting option values using theOTOptionManagement
function and using any of the other endpoint functions is that options negotiated with theOTOptionManagement
function 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 theOTOptionManagement
function to change option values, the function returns in theret
parameter 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,
orOTRcvConnect
functions, 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 akOTBufferOverflow
result. You can obtain the maximum size of a buffer used to store options for your endpoint by examining theoptions
field of theTEndpointInfo
structure 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 theOTGetEndpointInfo
function.Setting Option Values
You can use theOTOptionManagement
,OTAccept
,OTSndUData
,OTSndURequest
, andOTConnect
functions 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
, orOTSndURequest
functions. The section "Retrieving Values for Connection-Oriented Endpoints" describes the negotiation rules that hold when you use theOTOptionManagement
orOTAccept
functions 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 byTOption
structures; 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 aTNetbuf
structure 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
OTCreateOptions
function. 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
TOption
structure for each option you want to change.- Initialize each field of the
TOption
structure except for thestatus
field.- Place the
TOption
structures in the buffer, making sure that each begins on a long-word boundary. This enables Open Transport to parse the buffer.
OTCreateOptions
function 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 theOTOptionManagement
function.Listing 7-1 shows how you construct an options buffer by using the
OTCreateOptions
function. The code initializes a string array,myStr
, to hold option values. It then creates aTOptMgmt
structure, which would later be passed to theOTOptionManagement
function to request the option values specified in the string. Finally, it calls theOTCreateOptions
function to create the options buffer. TheOTCreateOptions
function creates theTOption
structures and places them in the buffer, making sure that the structures are properly aligned.Listing 7-1 Constructing an options buffer using the
OTCreateOptions
function
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 theOTCreateOptions
function 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 theOTOptionManagement
function, specifyingT_NEGOTIATE
for theflags
field and allocating a buffer containing only one option namedT_ALLOPT
. Doing this saves you the trouble of constructing aTOption
structure 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 theret
parameter.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 theOTOptionManagement
function. You specifyT_DEFAULT
orT_CURRENT
for theflags
field of thereq
parameter, and you use theoption.buf
field to specify the option names in which you are interested. When the function returns, it placesTOption
structures, describing the default or current option values, in the buffer referenced by theopt.buf
field of theret
parameter.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
OTOptionManagement
function, specifyingT_DEFAULT
for the flags field andT_ALLOPT
for the option name.
- To obtain an endpoint's current option values, call the
OTOptionManagement
function, specifyingT_CURRENT
for theflags
field andT_ALLOPT
for the option name.
T_ALLOPT
for 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 theOTOptionManagement
function to set, verify, or retrieve values, the function returns in theret
parameter a pointer to a buffer containing option information. You can use theOTCreateOptionString
function 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
OTOptionManagement
function to retrieve the option values currently effective for an endpoint. On return, theOTOptionManagement
function stores these in thecmd
structure. Next, the code calls theOTCreateOptionString
function. 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 theOTOptionManagement
function. The expressioncmd.opt.buf + cmd.opt.len
, which provides the next input parameter, specifies the length of the buffer. Using this information, theOTCreateOptionString
function returns a string containing each option name and its respective value. The final parameter to theOTCreateOptionString
function specifies the length of the string.Listing 7-2 Using the
OTCreateOptionString
function 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
OTCreateOptionString
function 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 theOTOptionManagement
function to verify whether an endpoint supports one or more options. To do this, you construct a buffer containingTOption
structures describing the options you are interested in and pass this buffer in thereq
parameter to theOTOptionManagement
function, specifyingT_CHECK
for the action flag. When the function returns, you can examine thestatus
field of theTOption
structures for the options passed back to you in theret
parameter 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 functionGetFourByteOption
to obtain the value of theIP_REUSEADDR
option, which governs whether you can bind multiple endpoints to addresses with the same port number. Listing 7-4 contains the definition of theGetFourByteOption
function.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 functionGetFourByteOption
gets 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
TOption
structure is used to represent the option buffer. This structure is defined to allow easy construction of 4-byte options.Next, the
request
parameter for theOTOptionManagement
function is defined to reference the option buffer that was just initialized. Therequest.flags
field is initialized toT_CURRENT
, specifying that we want to get the current value of the option. Thereply
parameter for theOTOptionManagement
function is then initialized. This is where the function stores the result of the negotiation. Finally, theOTOptionManagement
function is called, and its result is checked to see that the option value was read successfully. Any status other thanT_SUCCESS
orT_READONLY
is stored in theerr
variable.The next function called by
main
isSetFourByteOption
, 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); }TheSetFourByteOption
function is very similar in structure to theGetFourByteOption
function, shown in the previous listing. Once again, we use aTOption
structure to represent the option buffer and initialize its fields to specify the option and value we want to set. Next, we initialize therequest
parameter of theOTOptionManagement
function to reference the option buffer we just initialized. Thelength
field is set to the size of the option buffer and theflags
field is set toT_NEGOTIATE
to specify that we want to set the option value specified in the option buffer.The
reply
parameter for theOTOptionManagement
function is then set up. This is where the function will store the negotiated value of the option when it returns. Finally, theOTOptionManagement
function is invoked and its result is checked to make sure the option was successfully negotiated.After calling the
SetFourByteOption
function,main
calls theGetFourByteOption
function again to check the newly set value. The next three functions,PrintAllOptionsAtLevel
,PrintOptionsForConfig
, andBuildAndPrintOptions
demonstrate various ways of displaying option values for an endpoint. Two of those functions call the functionPrintOptionBuffer
shown in Listing 7-6. This function calls the Open Transport functionOTNextOption
to parse through an options buffer and then usesprintf
statements 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. ThePrintAllOptionsAtLevel
function, whichmain
calls 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 functionPrintAllOptionsAtLevel
takes two parameters, an endpoint reference and a level value. The function first callsOTGetEndpointInfo
to determine the maximum size of options for the endpoint and then allocates a buffer to hold the options after they are read. Next, theOTOptionManagement
function 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 theresult
parameter. 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
main
isPrintOptionsForConfiguration
, 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
OTCreateOptionString
function
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); }ThePrintOptionsForConfiguration
function 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
OTOptionManagement
function. The value ofT_ALLOPT
for thename
field andT_CURRENT
for theflags
field specify that we are interested in getting currently set values for all options set for the endpoint. After the buffer is allocated, theOTOptionManagement
function is invoked. Finally, theOTCreateOptionString
function is invoked; this function converts the option buffer passed back by theOTOptionManagement
function into a formatted string, which is then displayed using aprintf
statement.The last function called by
main
,BuildAndPrintOptions
, accomplishes the reverse of thePrintOptionsForConfiguration
function: 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. TheBuildAndPrintOptions
function 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 functionBuildAndPrintOptions
creates 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
BuildAndPrintOptions
calls theOTCreateOptions
function to parse the formattedoptionsString
into the binary format (optionsNetbuf
). Then the function callsPrintOptionBuffer
to 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