Microsoft Corporation
December 2, 1998
Contents
What Is an Ad-Hoc Networking-Enabled Application?
What Is IrDA?
What Is IrDA-C (Previously Known as IrBus)?
What Is Unique About IrDA?
IrDA Core Protocols and Services
IrDA and the WinSock API
IrDA and WinSock Reference
Windows 2000 IrDA Architecture
What Is an Ad-Hoc Networking-Enabled Application?
Imagine the following Microsoft® Windows® application: two notebook computers are placed beside each other. A computer icon appears on both desktops with the name of the peer computer below it. Open one of the icons to display a folder with the contents of the peer computer's desktop. Drag and drop between your desktop and the open folder to move files between the two computers.
Imagine that the only configuration this application required was a checkbox for the user to enable or disable it. Imagine that several similar applications could be running at the same time without interfering with each other.
Imagine that this application could run on millions of existing notebook computers at transfer speeds of up to 4 megabits per second (Mb/s). Imagine that instances of the application, regardless of the speed of the underlying hardware, would work with all other instances at a common fastest speed.
Imagine that the other notebook computer in this example was a digital still camera, a handheld personal computer, a data capture device, or an electronic commerce device.
As a bonus, assume that the two computers did not need to be cabled together.
This application is possible today under Windows 2000, Windows 98, and Windows CE. The underlying technology is based on inexpensive, widely available short-range infrared transceivers that adhere to the Infrared Data Association (IrDA) standards. IrDA standards also enable communications between non–Windows devices and Windows applications; these standards are freely available from the IrDA Web site, located at www.irda.org/.
What Is IrDA?
IrDA is an international organization that creates and promotes interoperable, low cost, infrared data interconnection standards that support a walk-up, point-to-point user model.
IrDA is a protocol suite designed to support transmission of data between two devices over short-range point-to-point infrared at speeds between 9.6 kilobits per second (Kb/s) and 4Mb/s.
IrDA is that small semitransparent red window you may have wondered about on your notebook computer.
Over the last three years, the members of the IrDA have been very successful at getting IrDA hardware deployed in a large number of new notebook computers. One of the reasons for this has been the simplicity and low cost of IrDA hardware. Unfortunately, until recently, the hardware has not been available for applications programmers to use because of a lack of suitable protocol drivers.
Microsoft Windows CE 1.0 was the first Windows operating system to provide built-in IrDA support. Windows 2000 and Windows 98 now also include support for the same IrDA programming APIs that have enabled file sharing applications and games on Windows CE.
IrDA implementations are becoming available on several popular embedded operation systems.
SIR and FIR IrDA hardware can easily be added to a desktop computer by attaching a dongle to a serial port (SIR) or by adding a card and a dongle (FIR). In the future, USB-attached FIR IrDA devices will be available.
What Is IrDA-C (Previously Known as IrBus)?
IrDA-C is a standard from the IrDA organization that is intended to support low speed wireless PC peripheral devices such as keyboards and joysticks. IrDA-C is a low speed (75 Kb/s shared among several devices), low latency, unreliable, long distance (20 feet), wide angle, multiple device protocol. IrDA-C is a PC-peripheral-style bus protocol, and is not exposed to the application developer. In this article, the term "IrDA" refers to the IrDA1.1 protocols, which are also known as IrDA-D.
IrDA-C does not interoperate with IrDA-D. The IrDA-C specification includes a mechanism that could allow IrDA-C and IrDA-D to share hardware, but this mechanism is incompatible with the IrDA protocols in Windows 2000 and Windows CE due to performance enhancements in these implementations of the IrDA-D protocols. IrDA-C and IrDA-D can still exist in the same device, but must be physically separate.
What Is Unique about IrDA?
IrDA is uniquely suited to ad-hoc point-to-point networking:
IrDA is a great noncable. Mismatched connectors and wiring are impossible with IrDA. Speed and configuration parameters are transparently negotiated at connect time and a common set is used for the connection. IrDA at 4 Mb/s is compatible with 9.6 Kb/s IrDA. Additionally, the IrDA connector is completely sealed, inexpensive, and available from multiple vendors.
Common user-space APIs. The combination of IrDA and Windows Sockets presents the application programmer with a powerful yet simple Win32® user-space API that exposes multiple, fully error-corrected data streams. Serial and parallel ports are the only other point-to-point technologies that have a commonly available user space API. IrDA defines rich functionality that does not exist with serial and parallel cables. IrDA borrows from the very successful client/server connection and programming model defined by the TCP/IP family of protocols and the WinSock APIs.
Open protocols support non–Windows devices. WinSock exposes the IrDA TinyTP protocol to the application writer. A non–Windows device that implements the TinyTP protocol will be able to easily exchange data with Windows applications.
IrDA and WinSock support the implementation of easy to use, zero configuration, always works data sharing applications—ad-hoc point-to-point networking.
IrDA Core Protocols and Services
The core IrDA services are similar to those exposed by the popular TCP protocol. Applications running on two different machines are able to easily open multiple reliable connections between themselves for the purpose of sending and receiving data. Like TCP, client applications connect to a server application by specifying a device (TCP host) address and an application (TCP port) address.
Serial IrDA (SIR) Physical Layer (115 Kb/s)
The SIR specification defines a short-range infrared asynchronous serial transmission mode with one start bit, eight data bits and one stop bit. The maximum data rate is 115.2 Kb/s (half duplex). The primary benefit of this scheme is that existing serial hardware can be used very cheaply. This is one of the reasons for the widespread availability of IrDA.
Fast IrDA (FIR) Physical Layer (4 Mb/s)
The FIR specification defines short-range low power operation at 4 Mb/s (half duplex). All FIR devices are also required to support SIR operation.
IrLAP Data Link Layer
Data rates are negotiated and changed during IrLAP connection establishment. This is completely transparent to the application.
IrLAP defines two protocols. The first is a discovery protocol that is used directly by an application to learn about currently visible devices, their nicknames and device (MAC) addresses. Discovery is an area where IrDA differs from other common bus or networking protocols. It is not expected that clients have advance knowledge of the device addresses of servers—they simply ask for a list of visible servers and then select one.
The information returned from a discovery is a list of device MAC addresses, human-readable device nicknames, and a bitmask of hints that suggest the nature of peer devices. MAC addresses are randomly generated 32 bit values.
The IrLAP data link protocol is based on the widely implemented HDLC data link protocol. IrLAP provides a simple, reliable connection between two devices. If the HDLC connection is broken for any reason, an error is quickly reported back to applications.
IrLMP and TinyTP
The IrLMP and TinyTP layers add multiple session support to IrLAP. In theory, this support exists to allow multiple applications to have multiple concurrent connections active. While this operation is fully supported, often only one application can be active at once. IrLMP and TinyTP still provide significant value in that they allow multiple applications to be listening for incoming connections without interfering with each other. A single application might also choose to open a control connection and a data connection at the same time. This is made possible by the IrLMP and TinyTP layers.
IrLMP and TinyTP also add per-connection flow control to the flow control provided by the single IrLAP connection. This allows an application to offer data in large blocks to the IrDA stack and allow the stack to send it at optimal speeds. It is not necessary for the application to be concerned about lost data or flow control.
WinSock exposes the TinyTP service interface to application programs.
IrLMP includes a directory services protocol, Information Advertising Service (IAS), that runs directly on top of IrLMP. IAS is commonly used to map an ASCII service name to a LSAP-SEL. LSAP-SEL is a protocol element used to select one from possibly many applications running on the server. The service name is a friendlier abstraction that is exposed to applications. Non–Windows devices must be aware of the conventions used by Windows. These are described below.
IrLMP defines a mode of service called exclusive mode. In this mode, only a single IrLMP connection is supported and the flow control features of IrLAP are used. TinyTP is not used. Exclusive mode is used by the IrLPT protocol that talks to IrLPT printers.
.gif)
Figure 1. Core IrDA Protocols
IrCOMM
IrCOMM was the most frequently used programming interface for IrDA on Windows 95. Windows 98 continues to support IrCOMM, but WinSock is the strategic interface for IrDA on all Windows platforms. There are several reasons behind this decision.
IrCOMM is a family of protocols that run on top of the core IrDA protocol suite. IrCOMM supports the emulation of a peer device connected by a serial or parallel cable. This emulation is from the perspective of applications that are accessing serial and parallel ports through the operating system API.
An IrCOMM implementation generally takes the form of a system installable serial port driver. Rather than talking to real serial hardware, it uses the services exported by the core IrDA protocols.
The basic mechanism of IrCOMM serial-connected device emulation is to convert RS-232 serial line state change requests generated by one application into protocol messages that are communicated to the peer application through the native serial API.
In the case of modems, some of line state change protocol messages correspond to established conventions for relaying state changes on the analog side of the modem back to the computer (drop DCD to signal a line carrier drop). Other line state changes are used to stop the computer from overrunning buffers in the modem (CTS, RTS).
When two systems are connected together by an RS-232 cable (no modems), ad-hoc application conventions are used to map line state changes into bidirectional flow control and application specific signaling. The Windows 95 Direct Cable Connect feature is an example of such an application.
The use of line state changes to implement flow control is largely a legacy application to application concept. The underlying IrDA protocols fully support flow control between systems; even an IrDA modem could dispense with lead state changes and simply use the underlying IrDA protocol flow control mechanisms to stop the computer from sending more data.
IrCOMM modes
IrCOMM is actually a family of protocols. Only 9 Wire Mode supports the propagation of line state change information. The IrLPT protocol is used to talk to printers. 3 Wire Raw Mode and IrLPT run the core IrDA stack in exclusive mode, which precludes other applications from using the stack at the same time. 3 Wire Cooked Mode uses the services of IrLMP and TinyTP, does not preclude other applications from using the stack, and does not propagate line state change information. 9 Wire Mode is like 3 Wire Cooked Mode but also supports line state change messages.
In order to achieve IrCOMM communication, a common mode must be negotiated at IrCOMM connection setup time.
.gif)
Figure 2. IrCOMM Internal Architecture
The philosophy behind IrCOMM was to support IrDA modems and legacy applications built on the serial API. In practice, because the serial API was the most commonly available IrDA user space API, new IrDA applications were designed around IrCOMM.
IrCOMM runs on top of a reliable protocol layer, but session establishment and release services are not exposed through the serial API. Additionally, the underlying IrDA connection can be broken and reestablished without this information being communicated to the application through the serial API. The result of this is that IrCOMM is not a reliable protocol in practice, and IrCOMM applications must be prepared to add yet anther layer of reliability.
No IrCOMM virtual serial ports on Windows 2000
Windows 2000 does not expose IrCOMM virtual serial ports. There are several reasons for this.
Many customers have difficulty with the concept of virtual ports. This is especially confusing when the SIR IrDA hardware itself may need to be configured on a real serial port, and then the customer must further configure their application to use a virtual serial port.
Multiple applications cannot share a virtual serial port. This is particularly troublesome if, for example, an IrCOMM-based application opens the single virtual serial port and holds it open until system shutdown. An example of this would be an IrTran-P file transfer application running as a background service. No other IrDA application or driver will be able to run on that system, even though the underlying IrDA protocols provide support allowing multiple applications to wait for incoming connections and clients to select a target application at connect time through established protocol mechanisms.
Windows 2000's support for multiple concurrent adapters and IrDA connections to different devices cannot be well supported under an API and protocol that assumes only a single device connection.
The complexity and various modes of IrCOMM make real world interoperability a very tough problem.
Windows 2000 support for IrCOMM through WinSock
In order to support certain legacy IrDA devices, including IrTran-P based cameras, Windows 2000 implements a subset of the IrCOMM protocol, but exposes this protocol through the WinSock API rather than through the serial API. In particular, only 9 Wire Mode IrCOMM is supported; line state change information is not supported. Windows 2000 only advertises that it supports the IrCOMM 9 Wire Mode. Devices must be able to initiate the IrDA connection, and not to expect Windows to initiate the connection as a side effect of discovering new devices. IrCOMM through WinSock programming details are found later in this article.
The fundamental limitation of the IrCOMM protocol, that you cannot have multiple servers listening for incoming connections, is still exposed in this implementation. If one application is listening for incoming IrCOMM connections, another application trying to do so will get an error from WinSock. For this reason it is recommended that all new applications either avoid IrCOMM or support multiple mode concurrently.
IrDA and the WinSock API
Good WinSock applications are critical to the easy to use, zero configuration, always works IrDA vision. IrDA and WinSock present a unique opportunity for applications that can fill the ever-increasing need for simple data exchange between Windows and non–Windows devices.
Talking to Non–Windows Devices
The WinSock programming API adds only a basic application-level naming convention (supported through IAS) to the IrDA specified protocol suite. A device that implements the core IrDA protocol and adheres to the simple naming conventions should be able to interoperate easily with Windows.
Devices are free to implement the required functionality of a WinSock client or server, or both. Client and server refer only to which one initiates the connection. Once a connection is established, data can be reliably exchanged in both directions. Since server side functionality requires slightly more IrDA stack functionality, Windows will often be the server. It is up to the application writer to choose the trigger that initiates the IrDA connection. The connection can be driven from a user-initiated action or can be the result of discovering a device in a discovery polling loop. The application programmer is completely unconstrained as to client/server roles and connection establishment.
Devices that are battery-constrained should refrain from continuous polling to drive connection initiation.
Either side can close the connection, although closing a connection is generally coordinated by application level protocols. A receiver will receive all data that has been sent to WinSock by the peer application before it receives notification of the peer connection closure. If the connection closes abortively, both sides of the connection will receive an error through WinSock. It is always possible to tell the difference between graceful closes and abortive closes.
Well-designed servers can often support multiple incoming connections concurrently, although there is no requirement that simple servers implement this functionality.
Application Addressing
Application-level addressing is the ability of a client application to request a connection to a particular server application (actually to a particular socket or communications endpoint). Multiple server applications can wait concurrently for incoming connections without interfering with one another.
The actual protocol, IrLMP, directly supports the concept of application-level addressing through an 8-bit protocol field called an LSAP-SEL.
Since LSAP-SELs are only 8 bits wide and no authority coordinates this space, using a well-known LSAP-SEL to identify an application is not recommended. Windows uses the GetValueByClass IAS service to map an ASCII service name to an LSAP-SEL at connect time. This means that a server can register itself with an ASCII name and a client can connect to this server by using the same name.
Non–Windows devices can connect to a Windows server by performing an IAS GetValueByClass query on the attribute IrDA:TinyTP:LsapSel with the desired service name as the class. The result of this query will be an LSAP-SEL, which the device can then use to initiate a TinyTP connection. The correct server will receive the incoming connection. The actual LSAP-SELs used in this scheme may change every time a server is restarted.
Non–Windows devices can support an inbound connection from a Windows system by supporting the same query initiated by the Windows side.
.gif)
Figure 3. Application Addressing
Data Transfer and Connection Close
Once a connection is established, WinSock send() and recv() calls translate into TinyTP sends and receives. Even though IrLAP itself is half-duplex, the application is not aware of this. send() and recv() can be called at the same time, on the same connection, on two different threads.
The stack manages TinyTP credits on behalf of the application. When the peer stops issuing TinyTP credits, the sender will block in the send() call. A non–Windows device is required to issue TinyTP credits as it is able to consume new data. Windows will stop issuing credits when the receiver stops calling recv() to consume data.
A WinSock application can send a large buffer of data on a send() call and the stack will segment it as required. Applications will get substantially higher performance if they pass in at least 8 kilobytes (KB) of data on a single send() call.
IrDA through WinSock supports the SOCK_STREAM data stream semantics, which means that any notion of message boundaries is not preserved. Applications commonly add a length field to the head of messages to pass this information to a peer.
When one side of a connection is closed with the closesocket() call, all data that was previously sent will be delivered to the peer. When the peer has consumed all data, the next recv() call it issues will return with a length of zero, indicating a normal socket close. Any error that prevents data from being sent correctly will result in a WinSock error being returned to the application.
.gif)
Figure 4. WinSock / IrDA Protocol Mapping
IrDA and WinSock Reference
WSAStartup()
Call WSAStartup before making any other IrDA calls.
WORD WSAVerReq = MAKEWORD(1,1);
WSADATA WSAData;
if (WSAStartup(WSAVerReq, &WSAData) != 0)
{
// wrong winsock dlls?
}
af_irda.h
This header file must be included by WinSock applications to support IrDA. There are several incompatible versions of af_irda.h that have been distributed with Windows CE and Windows 95 Ir3.0 DDKs and SDKs. A common af_irda.h that supports all three platforms is available with the Windows 2000 IrDA DDK, which can be accessed at the Web site www.microsoft.com/ddk/. This file may continue to evolve as the APIs of the systems grow closer together.
In order to compile for one of the target platforms, one of the following must be defined:
_WIN32_WINNT
_WIN32_WCE
_WIN32_WINDOWS
socket()
The WinSock socket() call is used to create a connection endpoint of type SOCKET. This is nothing more than an application anchor for future references to a connection. The connection is not yet established. Both clients and servers begin all communication by opening a socket.
SOCKET ServSock;
if ((ServSock = socket(AF_IRDA, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
// WSAGetLastError()
}
SOCKADDR_IRDA Structure
The following sockaddr structure is used for AF_IRDA sockets:
typedef struct _SOCKADDR_IRDA
{
u_short irdaAddressFamily;
u_char irdaDeviceID[4];
char irdaServiceName[25];
} SOCKADDR_IRDA, *PSOCKADDR_IRDA, FAR *LPSOCKADDR_IRDA;
irdaAddressFamily is always AF_IRDA.
Server applications use the irdaServiceName field to specify their well-known service name in a bind() call. The irdaDeviceID field is ignored by the server application.
Client applications fill in all fields. The irdaDeviceID is filled in with the device address of the device that the client wishes to connect() to. This address is returned from a previous discovery operation initiated by a getsockopt() call with the IRLMP_ENUMDEVICES option. The irdaServiceName field is initialized to the well-known value that the server specified in its bind() call.
It is an error for an IrDA application to issue a connect() call after issuing a bind() call.
bind()
The bind() call is used by server applications to register that they with to receive incoming connections that are addressed to a specified service name on the specified socket. bind() associates a server socket with an application level address.
SOCKADDR_IRDA ServSockAddr = { AF_IRDA, 0, 0, 0, 0, "SampleIrDAService" };
int SizeOfSockAddr;
if (bind(ServSock, (const struct sockaddr *) &ServSockAddr,
sizeof(SOCKADDR_IRDA)) == SOCKET_ERROR)
{
// WSAGetLastError()
}
A WinSock bind () will cause the stack to generate a new local LSAP-SEL and to add it to the IAS database associated with the service name supplied in the SOCKADDR_IRDA.
listen()
The listen() call is used by server applications to place the stack into a mode where it will receive incoming connections on that socket. listen() does not block. The backlog parameter tells the stack how many inbound connections to accept on behalf of the application before the application is able to further process (accept()) these connections.
if (listen(ServSock, 2) == SOCKET_ERROR)
{
// WSAGetLastError()
}
accept()
Once a server application has put its server socket into listen mode, it calls accept() and blocks until an incoming connection is received. An unusual characteristic of accept() is that it returns a new socket. The reason for this is that the server application might need to continue accepting new inbound connections. To support this, the server typically creates a new thread to handle the new connection, then blocks again on another accept(). There is no requirement that a simple server support multiple connections, and it is free to ignore the old listening socket until it is done with the newly created socket. The SOCKADDR_IRDA passed to accept is filled in with the peer's addresses and can usually be ignored.
SOCKET NewSock;
while(1)
{
sizeofSockAddr = sizeof(SOCKADDR_IRDA);
if ((NewSock = accept(ServSock, (struct sockaddr *) &PeerSockAddr,
&sizeofSockAddr)) == INVALID_SOCKET)
{
// WSAGetLastError()
// exit
}
// NewSock is a connected socket.
// Create a new thread and pass it NewSock, return to accept() on main
// or use NewSock here until done, then close it.
}
send() and recv()
These calls are used to transfer data. recv() will block until there is data available and send() will normally not block. send() can block if the peer is not receiving data (has stopped calling recv()). send() will unblock when the peer resumes recv()s. A recv() of length zero has the special meaning that the client has performed a graceful close on the socket. The application can assume that it has received all data that was sent by the peer. If any unrecoverable protocol error occurs during the connection, send() or recv() will return an error code and the connection will be aborted.
int BytesRead, BytesSent;
char Buffer[4096];
// recv() example
if ((BytesRead = recv(Sock, Buffer, sizeof(Buffer), 0)) == SOCKET_ERROR)
{
// WSAGetLastError()
}
if (BytesRead == 0)
{
// Peer has closed the connection and I have all the data.
// Close the socket now.
}
// send() example
if ((BytesSent = send(Sock, Buffer, sizeof(Buffer), 0)) == SOCKET_ERROR)
{
// WSAGetLastError()
}
// Check that BytesSent == sizeof(Buffer).
closesocket()
closesocket() initiates a graceful close on the connection and releases the socket handle.
if (closesocket(Sock) == SOCKET_ERROR)
{
// WSAGetLastError()
}
getsockopt(,, IRLMP_ENUMDEVICES,,) and connect()
This is the call used to run a discovery. Before a connection can be initiated, a device address must be obtained by doing a discovery operation. An extension to the WinSock getsockopt() call returns a list of all currently visible IrDA devices. A device address returned from this list is copied into a SOCKADDR_IRDA to be used by the connect() call.
Discovery can be run in one of two ways. Performing a getsockopt() call with the IRLMP_ENUMDEVICES option will cause a single discovery to be run on each idle adapter. The list of discovered devices and cached devices (on active adapters) will be returned immediately. The following code demonstrates this:
SOCKADDR_IRDA DestSockAddr = { AF_IRDA, 0, 0, 0, 0, "SampleIrDAService" };
#define DEVICE_LIST_LEN 10
unsigned char DevListBuff[sizeof(DEVICELIST) -
sizeof(IRDA_DEVICE_INFO) +
(sizeof(IRDA_DEVICE_INFO) * DEVICE_LIST_LEN)];
int DevListLen = sizeof(DevListBuff);
PDEVICELIST pDevList = (PDEVICELIST) &DevListBuff;
pDevList->numDevice = 0;
// Sock is not in connected state
if (getsockopt(Sock, SOL_IRLMP, IRLMP_ENUMDEVICES,
(char *) pDevList, &DevListLen) == SOCKET_ERROR)
{
// WSAGetLastError
}
if (pDevList->numDevice == 0)
{
// No devices discovered or cached.
// Not a bad idea to run a couple of times.
}
else
{
// one per discovered device
for (i = 0; i < (int) pDevList->numDevice; i++)
{
// typedef struct _IRDA_DEVICE_INFO
// {
// u_char irdaDeviceID[4];
// char irdaDeviceName[22];
// u_char irdaDeviceHints1;
// u_char irdaDeviceHints2;
// u_char irdaCharSet;
// } _IRDA_DEVICE_INFO;
// pDevList->Device[i]. see _IRDA_DEVICE_INFO for fields
// Display the device names and let the user select one.
}
}
// Assume the user selected the first device [0].
memcpy(&DestSockAddr.irdaDeviceID[0], &pDevList->Device[0].irdaDeviceID[0], 4);
if (connect(Sock, (const struct sockaddr *) &DestSockAddr,
sizeof(SOCKADDR_IRDA)) == SOCKET_ERROR)
{
// WSAGetLastError
}
It is also possible to run a lazy discovery—the application will not be notified until the discovered device list changes from the last discovery run by the stack.
IAS
Limited access to the IAS database is available through WinSock, but this is not normally used by applications. It exists to support connections to non–Windows devices that are not compliant with the WinSock/IrDA conventions.
This code is used with the IRLMP_IAS_SET setsockopt() to manage the local IAS database:
typedef struct _IAS_SET
{
char irdaClassName[IAS_MAX_CLASSNAME];
char irdaAttribName[IAS_MAX_ATTRIBNAME];
u_long irdaAttribType;
union
{
LONG irdaAttribInt;
struct
{
u_short Len;
u_char OctetSeq[IAS_MAX_OCTET_STRING];
} irdaAttribOctetSeq;
struct
{
u_char Len;
u_char CharSet;
u_char UsrStr[IAS_MAX_USER_STRING];
} irdaAttribUsrStr;
} irdaAttribute;
} IAS_SET, *PIAS_SET, FAR *LPIAS_SET;
This code is used with the IRLMP_IAS_QUERY getsockopt() to query a peer's IAS database:
typedef struct _WINDOWS_IAS_QUERY
{
u_char irdaDeviceID[4];
char irdaClassName[IAS_MAX_CLASSNAME];
char irdaAttribName[IAS_MAX_ATTRIBNAME];
u_long irdaAttribType;
union
{
LONG irdaAttribInt;
struct
{
u_long Len;
u_char OctetSeq[IAS_MAX_OCTET_STRING];
} irdaAttribOctetSeq;
struct
{
u_long Len;
u_long CharSet;
u_char UsrStr[IAS_MAX_USER_STRING];
} irdaAttribUsrStr;
} irdaAttribute;
} IAS_QUERY, *PIAS_QUERY, FAR *LPIAS_QUERY;
IrCOMM Server
The following code shows the steps necessary to build a server that listens for incoming IrCOMM connections:
#define IAS_SET_ATTRIB_MAX_LEN 32
// buffer for IAS set
BYTE IASSetBuff[sizeof(IAS_SET) - 3 + IAS_SET_ATTRIB_MAX_LEN];
Int IASSetLen = sizeof(IASSetBuff);
PIAS_SET pIASSet = (PIAS_SET) &IASSetBuff;
// for the setsockopt call to enable 9 Wire IrCOMM
int Enable9WireMode = 1;
// server sockaddr with IrCOMM name
SOCKADDR_IRDA ServSockAddr = { AF_IRDA, 0, 0, 0, 0, "IrDA:IrCOMM" };
SOCKADDR_IRDA PeerSockAddr;
int sizeofSockAddr;
SOCKET ServSock;
SOCKET NewSock;
if ((ServSock = socket(AF_IRDA, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
// WSAGetLastError
}
// Add IrCOMM IAS attributes for 3 Wire cooked and 9 Wire raw, see IrCOMM spec.
memcpy(&pIASSet->irdaClassName[0], "IrDA:IrCOMM", 12);
memcpy(&pIASSet->irdaAttribName[0], "Parameters", 11);
pIASSet->irdaAttribType = IAS_ATTRIB_OCTETSEQ;
pIASSet->irdaAttribute.irdaAttribOctetSeq.Len = 6;
memcpy(&pIASSet->irdaAttribute.irdaAttribOctetSeq.OctetSeq[0],
"\000\001\006\001\001\001", 6);
if (setsockopt(ServSock, SOL_IRLMP, IRLMP_IAS_SET, (const char *) pIASSet, IASSetLen)
== SOCKET_ERROR)
{
// WSAGetLastError
}
// Enable 9 Wire mode before bind().
if (setsockopt(ServSock, SOL_IRLMP, IRLMP_9WIRE_MODE, (const char *) &Enable9WireMode,
sizeof(int)) == SOCKET_ERROR)
{
// WSAGetLastError
}
if (bind(ServSock, (const struct sockaddr *) &ServSockAddr, sizeof(SOCKADDR_IRDA))
== SOCKET_ERROR)
{
// WSAGetLastError
}
// Nothing special for IrCOMM from now on...
if (listen(ServSock, SERV_BACKLOG) == SOCKET_ERROR)
{
// WSAGetLastError
}
IrCOMM Client
The following code shows the steps necessary to build a client that connects through 9 Wire IrCOMM:
#define DEVICE_LIST_LEN 5
// discovery buffer
BYTE DevListBuff[sizeof(DEVICELIST) - sizeof(IRDA_DEVICE_INFO) +
(sizeof(IRDA_DEVICE_INFO) * DEVICE_LIST_LEN)];
int DevListLen = sizeof(DevListBuff);
PDEVICELIST pDevList = (PDEVICELIST) &DevListBuff;
int DevNum;
#define IAS_QUERY_ATTRIB_MAX_LEN 32
// buffer for IAS query
BYTE IASQueryBuff[sizeof(IAS_QUERY) - 3 + IAS_QUERY_ATTRIB_MAX_LEN];
Int IASQueryLen = sizeof(IASQueryBuff);
PIAS_QUERY pIASQuery = (PIAS_QUERY) &IASQueryBuff;
// for searching through peers IAS response
BOOL Found = FALSE;
UCHAR *pPI, *pPL, *pPV;
// for the setsockopt call to enbale 9 Wire IrCOMM
int Enable9WireMode = 1;
SOCKADDR_IRDA DstAddrIR = { AF_IRDA, 0, 0, 0, 0, "IrDA:IrCOMM" };
if ((pConn->Sock = socket(AF_IRDA, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
// WSAGetLastError
}
// search for the peer device
pDevList->numDevice = 0;
if (getsockopt(pConn->Sock, SOL_IRLMP, IRLMP_ENUMDEVICES, (CHAR *) pDevList, &DevListLen)
== SOCKET_ERROR)
{
// WSAGetLastError
}
// if (pDevList->numDevice == 0)
{
// No devices found, tell the user.
}
// Assume first device, we should have a common dialog here.
memcpy(&DstAddrIR.irdaDeviceID[0], &pDevList->Device[0].irdaDeviceID[0], 4);
// Query the peer to check for 9 Wire IrCOMM support.
memcpy(&pIASQuery->irdaDeviceID[0], &pDevList->Device[0].irdaDeviceID[0], 4);
// IrCOMM IAS attributes
memcpy(&pIASQuery->irdaClassName[0], "IrDA:IrCOMM", 12);
memcpy(&pIASQuery->irdaAttribName[0], "Parameters", 11);
if (getsockopt(pConn->Sock, SOL_IRLMP, IRLMP_IAS_QUERY, (char *) pIASQuery,
&IASQueryLen) == SOCKET_ERROR)
{
// WSAGetLastError
}
if (pIASQuery->irdaAttribType != IAS_ATTRIB_OCTETSEQ)
{
// Peer's IAS database entry for IrCOMM is bad.
// error
}
if (pIASQuery->irdaAttribute.irdaAttribOctetSeq.Len < 3)
{
// Peer's IAS database entry for IrCOMM is bad.
// error
}
// Search for the PI value 0x00 and check 9 Wire, see IrCOMM spec.
pPI = pIASQuery->irdaAttribute.irdaAttribOctetSeq.OctetSeq;
pPL = pPI + 1;
pPV = pPI + 2;
while (1)
{
if (*pPI == 0 && (*pPV & 0x04))
{
Found = TRUE;
break;
}
if (pPL + *pPL >= pIASQuery->irdaAttribute.irdaAttribOctetSeq.OctetSeq +
pIASQuery->irdaAttribute.irdaAttribOctetSeq.Len)
{
break;
}
pPI = pPL + *pPL;
pPL = pPI + 1;
pPV = pPI + 2;
}
if (! Found)
{
// Peer doesn't support 9 Wire mode.
// error
}
// Enable 9 Wire mode before connect().
if (setsockopt(ServSock, SOL_IRLMP, IRLMP_9WIRE_MODE, (const char *) &Enable9WireMode,
sizeof(int)) == SOCKET_ERROR)
{
// WSAGetLastError
}
// Nothing special for IrCOMM from now on...
if (connect(pConn->Sock, (const struct sockaddr *) &DstAddrIR, sizeof(SOCKADDR_IRDA))
== SOCKET_ERROR)
{
// WSAGetLastError
}
Windows 2000 IrDA Architecture
Windows 2000 provides some unique support for IrDA.
IrDA Hardware Drivers
SIR UART based serial adapters are supported by the Windows 2000 component IrSIR.SYS. IrSIR uses the services of the Windows NT serial driver SERIAL.SYS or a SERIAL.SYS-compatible serial driver to communicate with the IrDA hardware. Built-in SIR hardware should expose itself through the system BIOS as Plug and Play ID PNP0510 or PNP0511.
FIR IrDA hardware must be exposed as an NDIS4.0 miniport driver. FIR drivers can expose as many NDIS adapters as the driver can support. Each adapter is a unique IrDA transceiver that can support a unique instance of IrLAP. FIR hardware should have a unique Plug and Play ID and have an associated vendor supplied driver. FIR hardware that is also compatible with SIR can also expose an alias Plug and Play ID of PNP0510 or PNP0511 to allow SIR only operation using IrSIR.
.gif)
Figure 5. Windows 2000 IrDA Architecture
Windows 2000 Multiple-Adapter Support
The Windows 2000 IrDA stack supports concurrent operation of several NDIS4.0 FIR/SIR miniport adapters. This support allows a single server to support multiple inbound connections in a way that is transparent to both client and server applications.
An adapter is defined as the hardware/software needed to support a single IrLAP connection.
Since IrDA is not a routable protocol, multiple-adapter support is limited to connections to a single server by multiple clients. Peer devices cannot talk to each other through the server.
Each adapter and IrLAP instance will have a unique IrDA MAC address (DeviceID).
Discovery operations are run on every idle adapter in sequence. A global list of discovered devices is returned. Cached discovery information is maintained on a per-IrLAP instance and returned for each adapter that has a connection active.
The Windows 2000 IrDA stack maintains a mapping between device address and last seen adapter. When a connection is requested to a peer device, the stack routes the connection to the correct adapter. Incoming connections are delivered to a single listening transport endpoint. The listening client will not receive any per-adapter information. The mapping between the new connection and the adapter is maintained by the IrDA stack.