irdaWinsockCli.cpp raw source file Back to the Reference document Back to my Homepage
//
// Copyright (c) 2003-2005 Alan J. McFarlane
//
// A sample IrDA (TinyTP) client using the Winsock interface, for
// http://www.alanjmcf.me.uk/comms/infrared/Microsoft%20Windows%20IrDA%20programming.html
//
// Updated: 2004-Apr-16 -- Modifications to C compatible format, no
// warnings on compile and more suitable for GCC compilation.
//
// Example usage.
//
// C:\>irdaWinsockCli.exe
// 3 Devices found
// 0: Nokia 6210, addr: 00-00-37-15, type: p___MF_-T_I__O_.
// 1: Alan J. McFarlane, addr: 52-16-09-9c, type: _P_____-_____O_.
// 2: Palm III, addr: 46-76-14-17, type: _P_____-_____O_.
// Selected 0th one
// Gonna connect to, 00-00-37-15 OBEX
// Connected
// Sending some nonsense data now...
// WS Error=10054 WSAECONNRESET @ recv
//
// C:\>
//
//======================================================================
//Windows 2000
#define _WIN32_WINNT 0x0500
#define WINVER 0x0500
#define WIN32_LEAN_AND_MEAN
#define UNICODE 1
#define _UNICODE 1
//#include <Windows.h> //-implicit
#include <WinSock2.h>
#include <AF_Irda.h>
#include <STDIO.H> //wprintf etc
#include <STDLIB.H> //atoi
#include <strsafe.h> //StrSafe
//======================================================================
// Link with: WS2_32.Lib. This needs to be done explicitly on the
// command-line on other compilers e.g. GCC.
#pragma comment ( lib, "WS2_32.Lib" )
//======================================================================
int selectIrdaPeer ( SOCKET sock, u_char irdaDeviceID[4] );
int irlmpToWideChar (
u_char irlmpCharSet, LPCSTR lpMultiByteStr, int cbMultiByte,
LPWSTR lpWideCharStr, int cchWideChar );
WCHAR * printIrdaDeviceHint ( u_char byte1, u_char byte2 );
void ReportWSErrorNoExit ( const WCHAR *szMessage );
void ReportWSError ( int exitCode, const WCHAR *szMessage );
//======================================================================
//
// User selects the first peer! Do this in your UI...
//
#define SELECT_PEER_NUM 0
//
// The Service Name to use. This example uses "OBEX" here as it is widely
// supported. Other well known ones include "IrDA:IrCOMM", "IrLPT",
// "IrNetv1", "IrLAN" etc.
//
// Using IrCOMM/IrLPT of course would likely need checking the peer's
// Parameters IAS entry and setting of Cooked (_9WIRE_) mode or IrLMP
// (_IRLPT_) mode etc on the socket.
//
// Note, not in Unicode. See Section "Character set issues" in the doc.
//
#define SERVICE_NAME "OBEX"
//======================================================================
int main( void )
{
//--------------------------------------------
// Variables
//--------------------------------------------
WORD xWSAVerReq = MAKEWORD ( 2, 2 ); //The _highest_ version we can use
WSADATA xWSAData;
SOCKADDR_IRDA xPeerSockAddr = { AF_IRDA, 0, 0, 0, 0, SERVICE_NAME };
SOCKET xSock;
int xRet;
char xaDodgyData[] = "ThisIsNoAValidObexPdu";
char xaRecvBuf[10];
//--------------------------------------------
// Winsock startup
//--------------------------------------------
xRet = WSAStartup ( xWSAVerReq, &xWSAData );
if ( xRet != 0 )
{
wprintf ( L"-1: Winsock startup failed, error=%d\n", xRet );
exit ( (UINT)-1 );
}
//To ensure we're linking to the correct library check we got 2.2.
if ( xWSAData.wVersion != xWSAVerReq )
{
wprintf ( L"-1: Bad Winsock version" );
exit ( (UINT)-1 );
}
//--------------------------------------------
// Open socket
//--------------------------------------------
xSock = socket ( AF_IRDA, SOCK_STREAM, 0 );
if ( xSock == INVALID_SOCKET )
{
ReportWSError ( -2, L"socket AF_IRDA, SOCK_STREAM" );
}
//--------------------------------------------
// Enumerate peers and select one
//--------------------------------------------
xRet = selectIrdaPeer ( xSock, xPeerSockAddr.irdaDeviceID );
if ( xRet <= 0 )
{
wprintf ( L"-4: no peers discovered/error...\n" );
exit ( (UINT)-4 );
}
//--------------------------------------------
// Connect
//--------------------------------------------
wprintf ( L"Gonna connect to, %02x-%02x-%02x-%02x %hs\n",
xPeerSockAddr.irdaDeviceID[0],
xPeerSockAddr.irdaDeviceID[1],
xPeerSockAddr.irdaDeviceID[2],
xPeerSockAddr.irdaDeviceID[3],
xPeerSockAddr.irdaServiceName
);
xRet = connect ( xSock, (const struct sockaddr *) &xPeerSockAddr, sizeof ( SOCKADDR_IRDA ) );
if ( xRet == SOCKET_ERROR )
{
ReportWSError ( -5, L"connect" );
}
wprintf ( L"Connected\n" );
//--------------------------------------------
// Now use the connection...
//--------------------------------------------
//...
// send, recv etc etc
//...
// Sleep for 5 seconds to give the IrDA "connected" icon lots of time to
// appear.
Sleep ( 5000 );
// Now send some data to provoke the peer. :-)
wprintf ( L"Sending some nonsense data now...\n" );
xRet = send ( xSock, xaDodgyData, sizeof xaDodgyData, 0 );
if ( xRet < 0)
{
ReportWSErrorNoExit ( L"send" );
}
else
{
xRet = recv ( xSock, xaRecvBuf, sizeof xaRecvBuf, 0 );
if ( xRet < 0)
{
// This is the likely case here...
ReportWSErrorNoExit ( L"recv" );
}
else if ( xRet == 0)
{
wprintf ( L"Connection gracefuly closed\n" );
}
//else...
}
//--------------------------------------------
// Close and Shutdown
//--------------------------------------------
closesocket ( xSock );
WSACleanup ();
return 0;
}//wmain
//======================================================================
int selectIrdaPeer( SOCKET sock, u_char irdaDeviceID[4] )
{
//--------------------------------------------
// Variables
//--------------------------------------------
// Space for discovery, allow up to five (well six) peers.
#define DEVICE_LIST_LEN 5
unsigned char xDevListBuf[sizeof ( DEVICELIST ) + ( sizeof ( IRDA_DEVICE_INFO ) * DEVICE_LIST_LEN )];
int xDevListLen = sizeof xDevListBuf;
PDEVICELIST xpDevList = (PDEVICELIST) &xDevListBuf;
#define WDN_LEN 64
WCHAR xWideDeviceName[WDN_LEN];
int xSelectNum;
ULONG xCur; //For for loop, same type as .numDevice.
int xRet;
xpDevList->numDevice = 0; //just in case
//--------------------------------------------
// Enumerate devices
//--------------------------------------------
xRet = getsockopt ( sock, SOL_IRLMP, IRLMP_ENUMDEVICES, (char *) xpDevList, &xDevListLen );
if ( xRet == SOCKET_ERROR )
{
ReportWSErrorNoExit ( L"-3: getsockopt IRLMP_ENUMDEVICES" );
return -1; //error
}
if ( xpDevList->numDevice == 0 )
{
// No devices found.
return 0; //no peers
}
//--------------------------------------------.
// Display the discovered devices
//--------------------------------------------
wprintf ( L"%d Devices found\n" , xpDevList->numDevice );
for ( xCur = 0; xCur < xpDevList->numDevice ; xCur++ )
{
//debug-ish display of peer's info.
xRet = irlmpToWideChar (
xpDevList->Device[xCur].irdaCharSet,
xpDevList->Device[xCur].irdaDeviceName,
-1,
// The struct does not provide a length field
// so it's not possible to find the length of
// the given char array. Hope it's obvious
// to the subsequent code...
xWideDeviceName, WDN_LEN );
if ( xRet == 0 )
{
StringCchCopyW ( xWideDeviceName, WDN_LEN, L"Cannot convert DeviceName" );
}
wprintf ( L"%2d: %ls, addr: %02x-%02x-%02x-%02x, type: %ls\n", xCur, xWideDeviceName,
xpDevList->Device[xCur].irdaDeviceID[0],
xpDevList->Device[xCur].irdaDeviceID[1],
xpDevList->Device[xCur].irdaDeviceID[2],
xpDevList->Device[xCur].irdaDeviceID[3],
printIrdaDeviceHint (
xpDevList->Device[xCur].irdaDeviceHints1,
xpDevList->Device[xCur].irdaDeviceHints2 )
);
}//for
//--------------------------------------------
// Select which device
//--------------------------------------------
xSelectNum = SELECT_PEER_NUM; //Do this in your UI...
memcpy ( irdaDeviceID, xpDevList->Device[xSelectNum].irdaDeviceID, 4 );
wprintf ( L"Selected %dth one\n" , xSelectNum );
return xpDevList->numDevice; //success, number of peers
}//selectIrdaPeer
//======================================================================
int irlmpToWideChar(
u_char irlmpCharSet, LPCSTR lpMultiByteStr, int cbMultiByte,
LPWSTR lpWideCharStr, int cchWideChar )
{
UINT xCP;
switch ( irlmpCharSet )
{
// You can work out the rest of the character conversion code
// mappings. :-)
//...
//...
// BTW I've only seen ASCII use by peer devices, I'd be interested to
// hear of other charsets seen.
case LmCharSetASCII:
xCP = 20127; //US-ASCII (7-bit)
break;
case LmCharSetISO_8859_1:
xCP = 28591; //ISO 8859-1 Latin I
break;
//...
default:
// bad IrLMP CharSet value
SetLastError ( ERROR_INVALID_PARAMETER );
return 0; //Fail
}//switch
// Call the Win32 conversion function with the resultant Code page
// value and pass through the other parameters.
//
// MSDN: "If the function fails, the return value is zero."
return MultiByteToWideChar ( xCP, 0, lpMultiByteStr, cbMultiByte,
lpWideCharStr, cchWideChar );
}//irlmpToWideChar
//----------------------------------------------------------------------
// Note single thread use only.
WCHAR * printIrdaDeviceHint( u_char byte1, u_char byte2 )
{
static WCHAR xString[8+8+1];
//two sets of 7 bits plus separator plus null terminator
xString[0] = (WCHAR)( byte1 & LM_HB1_PnP ? L'p' : L'_' );
xString[1] = (WCHAR)( byte1 & LM_HB1_PDA_Palmtop ? L'P' : L'_' );
xString[2] = (WCHAR)( byte1 & LM_HB1_Computer ? L'C' : L'_' );
xString[3] = (WCHAR)( byte1 & LM_HB1_Printer ? L'P' : L'_' );
xString[4] = (WCHAR)( byte1 & LM_HB1_Modem ? L'M' : L'_' );
xString[5] = (WCHAR)( byte1 & LM_HB1_Fax ? L'F' : L'_' );
xString[6] = (WCHAR)( byte1 & LM_HB1_LANAccess ? L'L' : L'_' );
xString[7] = (WCHAR)( byte1 & LM_HB_Extension ? L'-' : L'.' );
xString[8] = (WCHAR)( byte2 & LM_HB2_Telephony ? L'T' : L'_' );
xString[9] = (WCHAR)( byte2 & LM_HB2_FileServer ? L'F' : L'_' );
xString[10] = (WCHAR)( byte2 & 0x04 /* IrCOMM */ ? L'I' : L'_' );
xString[11] = (WCHAR)( byte2 & 0x08 /* resv */ ? L'?' : L'_' );
xString[12] = (WCHAR)( byte2 & 0x10 /* resv */ ? L'?' : L'_' );
xString[13] = (WCHAR)( byte2 & 0x20 /* OBEX */ ? L'O' : L'_' );
xString[14] = (WCHAR)( byte2 & 0x40 /* resv */ ? L'?' : L'_' );
xString[15] = (WCHAR)( byte2 & LM_HB_Extension ? L'-' : L'.' );
xString[16] = '\0';
return xString;
}//printIrdaDeviceHint
//======================================================================
WCHAR* getWinsockErrorString( int errorCode )
{
WCHAR* xString;
switch( errorCode )
{
case WSAEADDRINUSE: //+48
xString = L"WSAEADDRINUSE";
break;
case WSAECONNRESET: //+54
xString = L"WSAECONNRESET";
break;
case WSAETIMEDOUT: //+60
xString = L"WSAETIMEDOUT";
break;
case WSAECONNREFUSED: //+61
xString = L"WSAECONNREFUSED";
break;
default:
xString = L"?";
break;
}//switch
return xString;
}//getWinsockErrorString
//----------------------------------------------------------------------
void ReportWSErrorNoExit( const WCHAR *szMessage )
{
int xErrorCode;
xErrorCode = WSAGetLastError();
wprintf( L" WS Error=%d %ls @ %s\n", xErrorCode,
getWinsockErrorString( xErrorCode ), szMessage );
}
void ReportWSError( int exitCode, const WCHAR *szMessage )
{
ReportWSErrorNoExit ( szMessage );
(void)WSACleanup ();
exit ( exitCode );
}
//======================================================================
//eof