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