Quick introduction
The IR support provided by the Palm OS is compliant with the IrDA specifications. As usual, an IR stack consists of several protocol layers. You will find more information at http://www.IrDA.org/.
In a couple of words, there is a low hardware level (SerialIR/FastIR); then, over it is the IR Link Access Protocol (IrLAP) that provides a data pipe between IrDA devices. IrLMP, the IR Link Management Protocol, manages multiple sessions using the IrLAP. Tiny TP is a lightweight transfer protocol on which some higher-level IrDA layers are built. All these layers are a must.
IrComm and IrLAN are optional; hence, their support is not guaranteed under Palm OS. But OBEX is actually available through the Exchange Manager API. In addition, The Palm OS IR Library supports IAS, a database service devices may use to publish information about themselves and services provided and get the same info about others.
How it all works
After such an academic start, let's consider some practical aspects. Serial communications is the simplest case. All you need to do is specify sysFileCVirtIrComm as the serial port name. The next sample shows all the stuff:
static void DoPDA()
{
Char szError[128];
MemSet(szError,sizeof(szError),0);
Char szBuffer[128];
StrCopy(szBuffer,"Sample Message");
Char szAnswer[128];
MemSet(szAnswer,sizeof(szAnswer),0);
UInt16 dwSize = StrLen(szBuffer) + 1;
UInt16 nPortId;
Err err = SrmOpen(sysFileCVirtIrComm,115200,&nPortId);
if ( err != errNone )
{
StrPrintF(_szInfo,"SrmOpen returned error = 0x%X - %s\n",err,
GetSerialErrorText(err, szError));
Log(_szInfo);
return;
}
// Send buffer size
UInt32 nBytes = SrmSend(nPortId, &dwSize, sizeof(dwSize), &err);
StrPrintF(_szInfo,"1. SrmSend sent %lu bytes\n",nBytes);
Log(_szInfo);
if ( err != errNone )
{
StrPrintF(_szInfo,"1. SrmSend returned error = 0x%X - %s\n",
err,GetSerialErrorText(err, szError));
Log(_szInfo);
goto CleanupSer;
}
// Receive ACK
err = SrmReceiveWait(nPortId,3,5 * SysTicksPerSecond());
if ( err != errNone )
{
StrPrintF(_szInfo,"1. SrmReceiveWait returned error =
0x%X - %s\n",err,GetSerialErrorText(err, szError));
Log(_szInfo);
goto CleanupSer;
}
Log("1. SrmReceiveWait is OK\n");
nBytes = SrmReceive(nPortId, (void*)szAnswer, 3,
5 * SysTicksPerSecond(), &err);
if ( err != errNone )
{
StrPrintF(_szInfo,"1. SrmReceive returned error =
0x%X - %s\n", err,GetSerialErrorText(err, szError));
Log(_szInfo);
goto CleanupSer;
}
StrPrintF(_szInfo,"1. SrmReceive returned buffer %s\n",szAnswer);
Log(_szInfo);
// Send buffer
nBytes = SrmSend(nPortId, szBuffer, StrLen(szBuffer) + 1, &err);
StrPrintF(_szInfo,"2. SrmSend sent %lu bytes\n",nBytes);
Log(_szInfo);
if ( err != errNone )
{
StrPrintF(_szInfo,"2. SrmSend returned error = 0x%X - %s\n",
err,GetSerialErrorText(err, szError));
Log(_szInfo);
goto CleanupSer;
}
CleanupSer:
SrmClose(nPortId);
}
That's just a slightly modified example from the first article in this cycle. Using similar code at the 'server' side, we will be able to exchange data over a virtual serial port.
Coming back to IR Library itself, let's first overview a typical scenario. Usually, you'll be required to do the following steps: Most of the IR Library functions return control immediately. Your application will be notified about events occurring in the system via a callback mechanism. You're passing a pointer to the callback function as the last parameter of IrBind API. The same model also works for IAS queries. As a result, your application must support some method of waiting for the result of the IR API called. Besides, it'd be nice not to hang up the UI while communications are in a waitable state, so you have to take care about event processing when it's needed. Below, we will consider all stages step by step. This part is relatively simple. Take a look at following code snippet:Initialization
Byte _deviceInfoXID[] = { IR_HINT_PDA, 0, 'P', 'a', 'l', 'm' };
static void MainFormInit(FormType *frmP)
{
...
if(SysLibFind(irLibName, &_IrLibraryRefNum)!=0)
{
StrPrintF(_szInfo,"SysLibFind(%s) failed\n", irLibName);
Log(_szInfo);
_bIsIrLibAvailable = false;
}
StrPrintF(_szInfo,"IR library reference: %d\n", _IrLibraryRefNum);
Log(_szInfo);
if ( IrOpen(_IrLibraryRefNum, irOpenOptSpeed115200) != 0 )
{
Log("IrOpen() failed\n");
return;
}
if( IrBind(_IrLibraryRefNum, &_IrConnection, IrCallback) !=
IR_STATUS_SUCCESS )
{
Log("IrBind() failed\n");
IrClose(_IrLibraryRefNum);
return;
}
if( IrSetDeviceInfo(_IrLibraryRefNum, _deviceInfoXID,
sizeof(_deviceInfoXID)) != IR_STATUS_SUCCESS )
{
Log("IrSetDeviceInfo() failed\n");
return;
}
if ( !waitUntilMediaReady() )
{
Log("IR media is busy!\n");
return;
}
...
}
It does exactly those calls we discussed above. The only thing that was not mentioned before is the IrSetDeviceInfo function. According its name, it sets the XID part of IrDeviceInfo struct. This information will be used during the discovery process; thus, other devices can know who are you. The XID struct contains the device hints (first two bytes) and nickname. By using the attached sample project, you can get some examples for different types of discovered IR devices, such as printers or mobile phones. Finally, after all initialization is completed, we wait for the media to be ready to carry out the next IR calls. Throughout the rest of this article, we will refer to different events occuring at different stages of an IR session. The following table gives you short explanation of each of them:Callback Events
Event Description LEVENT_TEST_IND This event indicates that a TEST command frame has been received by the IR library. The received data is in rxBuff with size rxLen. The default response packet is set up with the received data, but it can be changed by modifying the packet. No explicit action is required for the TEST command to be responded to. LEVENT_TEST_CNF This event indicates that a TEST command has completed. The status field indicates whether the test was successful. IR_STATUS_SUCCESS indicates success, and the response can be found in rxBuff with size rxLen. IR_STATUS_FAILED indicates that no TEST response was received. The packet passed to perform the test command is passed back in the packet field, and is now available. No separate packet handled event will occur. LEVENT_DISCOVERY_CNF This event indicates that a discovery operation has completed. The field deviceList points to the discovery list. This list may be empty. Discovery can never fail, so this event always occurs after a discovery is initiated. LEVENT_LAP_CON_IND This event occurs when the other side is trying to make a LAP connection. In practice, this event may be immediately followed by an LEVENT_LAP_DISCON_IND if the connection attempt fails. LEVENT_LAP_CON_CNF This event occurs when a requested IrLAP connection has been successfully made. LEVENT_LAP_DISCON_IND This event occurs when a request to initiate an IrLAP connection fails. It also occurs when the IrLAP connection has gone down. The event occurs on both sides. In practice, this event may immediately follow an LEVENT_LAP_CON_IND if the connection attempt fails. This event implies that the IrLMP connection has also gone down, if it was up. No separate event occurs for the IrLMP connection, except on Palm OS 4.0. LEVENT_LM_CON_IND This event occurs when the other side has initiated an IrLMP connection. If we want to accept this connection, we must respond by calling IrConnectRsp(). The connection request includes arbitrary data that can be found at rxBuff with size rxLen. This data can be used to decide whether to accept the connection. The response also includes data to be sent back. LEVENT_LM_CON_CNF This event indicates that the requested IrLMP connection has been made successfully. The connection response includes arbitrary data that can be found at rxBuff with size rxLen. LEVENT_LM_DISCON_IND This event indicates that the IrLMP connection has been disconnected. Any data associated with the disconnection indication can be found at rxBuff with size rxLen. In practice, this event does not occur when the connection is broken, but does occur when this side attempts to initiate an IrLMP connection and the attempt fails. On Palm OS 4.0, this event also occurs when this side disconnects at the LAP level. The documentation is vague on this issue. In particular, the documentation says that LEVENT_LAP_DISCON_IND implies that the IrLMP connection has gone down, and that no other event will occur for this. LEVENT_PACKET_HANDLED This event occurs when a packet passed in earlier becomes free again. This event occurs after IrDataReq, IrConnectReq, and IrConnectRsp, but not after IrTestReq. The test packet is considered handled when LEVENT_TEST_IND occurs. LEVENT_DATA_IND This event occurs when data has been received. It can be accessed via rxBuff and rxLen. This event only occurs when data is sent by the other side using IrDataReq. It doesn't occur when data is received as part of an IrLMP connection request or response. LEVENT_STATUS_IND This event occurs when the status changes. In practice, it is possible to get consecutive status events with the same status. The status field contains the new status, which can be one of:
You'll be responsible for providing the required code in response to incoming events. To to make any connection, you should first know what your target is. This process is called 'discovering'. The following sample code gives a simple example of how to do it:Discovering devices in range
static void DoDiscover()
{
IrStatus ret;
if ( !_bIsIrLibAvailable )
return;
_pendingCommand = DiscoveryCommand;
if( (ret=IrDiscoverReq(_IrLibraryRefNum, &_IrConnection)) !=
IR_STATUS_PENDING )
{
StrPrintF(_szInfo,"IrDiscoverReq() failed: %s\n",
StatusName(ret));
Log(_szInfo);
return;
}
if ( !waitForResult() )
{
Log("Discovery has failed due to timeout or error\n");
}
}
void IrCallback(IrConnect *connect, IrCallBackParms *parms)
{
int i;
switch(parms->event)
{
...
case LEVENT_DISCOVERY_CNF:
if(parms->deviceList->nItems > 0)
{
MemMove(&_deviceInfo,&parms->deviceList->dev[0],
sizeof(IrDeviceInfo));
for(i=0;i
Actually, the main part is happening in callback. You just save the received info about discovered devices somewhere for possible future usage. As we have noted earlier, you can use IAS queries to get information about devices within range. It's necessary to make a LAP connection before query execution. IAS is some kind of database where devices may publish info about themselves and their services. It accumulates records based on device class, protocol, parameter type, and so forth. The IR Library contains about a dozen IAS functions. The core one is IrIAS_Query. It enables you to retrieve requested information according to device class and parameter name. You will find some basical implementation of such queries in the article's sample project. If you need more details about IAS, you may find them at the IrDA site. This short article is just not a good place to discuss it. After your application has gotten a remote LSAP value, it's possible to start a connection request. It is carried out by several steps. First, according to IrDA specifications, you should establish a LAP connection and then work over either a LMP or Tiny TP session. This last phase requires to know remote LSAP; thus, you should execute the IAS query after a successfull LAP connection. The following sample will help you to get the first glance on the whole process:Executing IAS queries
Making a connection
static void DoPrint()
{
_pendingCommand = ConnectLapCommand;
// _deviceInfo must be set by discovery command
if (IrConnectIrLap(_IrLibraryRefNum, _deviceInfo.hDevice) !=
IR_STATUS_PENDING)
{
Log("Fail to establish LAP connection\n");
return;
}
if ( !waitForResult() )
{
Log("Timeout is over for IrConnectIrLap\n");
return;
}
if ( !connectIrLPT() )
{
Log("Fail to establish connection to printer\n");
return;
}
// Do data transmission here
....
if (IrDisconnectIrLap(_IrLibraryRefNum) != IR_STATUS_PENDING)
{
Log("Fail to disconnect via IrDisconnectIrLap\n");
return;
}
}
static bool connectIrLPT()
{
Byte remoteLsap;
IasQuery iasQuery(_IrLibraryRefNum);
// Try connect to printer. LPT port uses LMP protocol and has
// fixed IrLPT class
if (iasQuery.getRemoteLsap(IrLptClass, IrLmpProtocol, remoteLsap))
{
StrPrintF(_szInfo,"remote LSAP is %u\n",remoteLsap);
Log(_szInfo);
if ( !connectLmp(remoteLsap) )
{
Log("Failed to connectLmp\n");
return false;
}
return true;
}
Log("Fail to obtain remote LSAP\n");
return false;
}
static bool connectLmp(Byte remoteLsap)
{
_pendingCommand = ConnectLmCommand;
IrSetConTypeLMP(&_IrConnection);
_IrConnection.rLsap = remoteLsap;
_packet.buff = NULL;
_packet.len = 0;
if (IrConnectReq(_IrLibraryRefNum, &_IrConnection, &_packet, 0)
!= IR_STATUS_PENDING)
{
Log("Fail to establish LMP connection\n");
return false;
}
return waitForResult();
}
In fact, there is nothing too complicated here, except possibly the error handling, disconnection events, and so forth. Because the connection is established, you can transmit data. That's quite a trivial task. The following code snippet shows all the story:Transmitting the data
...
char szTestData[] =
"*******************\r\n"
"IrLib Sample Packet\r\n"
"*******************\r\n\r\n";
_packet.buff = (Byte*)szTestData;
_packet.len = StrLen(szTestData);
_pendingCommand = SendDataCommand;
if (IrDataReq(_IrLibraryRefNum, &_IrConnection, &_packet) !=
IR_STATUS_PENDING)
{
Log("Fail to send data\n");
return;
}
if ( !waitForResult() )
{
Log("Timeout is over for sending data\n");
return;
}
...
All in all, you should unbind an existing connection from the IR protocol stack and close the IR Library:Shutting down
static void AppStop(void)
{
IrUnbind(_IrLibraryRefNum, &_IrConnection);
IrClose(_IrLibraryRefNum);
/* Close all the open forms. */
FrmCloseAllForms();
}
As you might see, IR communications under Palm OS are a doable thing. A bit complicated from the very first touch, it's pretty usable later. I hope this article will help you to solve most of the common programming IR tasks. Download the accompanying code's zip file here.Summary
Download
|