irdaWinsockCliIrLpt.c raw source file Back to the Reference document Back to my Homepage


//
// Copyright (c) 2003-2005 Alan J. McFarlane
//
// irdaWinsockCliIrLpt.c
//
// A sample (IrDA) IrLPT client using the Winsock interface, for
// http://www.alanjmcf.me.uk/comms/infrared/Microsoft%20Windows%20IrDA%20programming.html
//

// Created: 2004-Nov-21 -- Created from irdaWinsockCli.cpp.
// 

// Example usage.
// 
// C:\>irdaWinsockCliIrLpt
// 1 Devices found
//  0: Fake Printer, addr: 00-00-7c-61, type: p__P___-_______.
// Selected #0
// Gonna connect to, 00-00-7c-61 IrLPT
// Connected
// 
// Maximum send size is: 122
// Sleeping...
// Sending some nonsense data now...
// Sending a maximum size pdu-worth (122 bytes) of data now...
// Sending a 1 byte longer (123 bytes) than maximum size pdu-worth of data now...
//   WS Error=10053 WSAECONNABORTED @ send
// Sending some over-long nonsense data now...
//   WS Error=10053 WSAECONNABORTED @ send
// 
// 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.  We are using IrLPT, which has the 
// Service Name "IrLPT" and it also doesn't use the TinyTP layer so
// we disable it with setsockopt ( IRLMP_IRLPT_MODE ).
//
// See the sample irdaWinsockCli.cpp for a standard TinyTP client.
//
// Note, not in Unicode.  See Section "Character set issues" in the doc.
//
#define SERVICE_NAME		"IrLPT"


//======================================================================

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;

	int	xOn;
	int xMaxSendSize, xMaxSendSizeLen;
	char	xaDodgyData[] = "Print some text to the first page\r\nand another line\fAnd a second page.";
	char	xaDodgyLongData[4096];


	//--------------------------------------------
	// 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" );
	}


	//--------------------------------------------
	// Set connection/socket options
	//--------------------------------------------
	// Set IrLMP (non-TinyTP) mode.
	xOn	= 1;
	xRet = setsockopt ( xSock, SOL_IRLMP, IRLMP_IRLPT_MODE, (char *) & xOn, sizeof xOn );
	if ( xRet == SOCKET_ERROR )
	{
		ReportWSError ( -3, L"-3:	setsockopt IRLMP_IRLPT_MODE" );
	}


	//--------------------------------------------
	// Enumerate peers and select one
	//--------------------------------------------
	//
	// This should really filter (prefer) those peers with the 
	// Printer Hint Bit set (see below).
	// ... = selectIrdaPeer( xSock, xPeerSockAddr.irdaDeviceID, 
	//         IRDA_HINTS_PREFER | IRDA_HINTS_ONE_OR, 
	//         LM_HB1_Printer, 0 /*none in 2nd Hint Byte*/ );
	//
	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\n" );


	//--------------------------------------------
	// Get the maximum send size (limited by an IrLAP PDU).
	//--------------------------------------------
	xMaxSendSizeLen	= sizeof xMaxSendSize;
	xRet = getsockopt ( xSock, SOL_IRLMP, IRLMP_SEND_PDU_LEN, (char *) & xMaxSendSize, & xMaxSendSizeLen );
	if ( xRet == SOCKET_ERROR )
	{
		ReportWSError ( -6, L"Failed to get maximum send size! (IRLMP_SEND_PDU_LEN)" );
	}//if
	wprintf ( L"Maximum send size is: %d\n", xMaxSendSize );



	//--------------------------------------------
	// Now use the connection...
	//--------------------------------------------

	//...
	// send, recv etc etc
	//...
	
	// Sleep for 5 seconds to give the IrDA "connected" icon lots of time to 
	// appear.
	wprintf ( L"Sleeping for 5 seconds...\n" );
	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" );
	}

	// Now send some maximum size.
	wprintf ( L"Sending a maximum size pdu-worth (%d bytes) of data now...\n", xMaxSendSize );
	xRet = send ( xSock, xaDodgyLongData, xMaxSendSize, 0 );
	if ( xRet < 0)
	{
		ReportWSErrorNoExit ( L"send" );
	}

	// Now send some over-long data to show the connection failing! :-)
	wprintf ( L"Sending a 1 byte longer (%d bytes) than maximum size pdu-worth of data now...\n", xMaxSendSize + 1 );
	xRet = send ( xSock, xaDodgyLongData, xMaxSendSize + 1, 0 );
	if ( xRet < 0)
	{
		ReportWSErrorNoExit ( L"send" );
	}

	// Now send some over-long data to show the connection failing! :-)
	wprintf ( L"Sending some over-long nonsense data now...\n" );
	xRet = send ( xSock, xaDodgyLongData, sizeof xaDodgyLongData, 0 );
	if ( xRet < 0)
	{
		ReportWSErrorNoExit ( L"send" );
	}


	//--------------------------------------------
	// Close and Shutdown
	//--------------------------------------------
	closesocket ( xSock );
	WSACleanup ();
	return 0;
}//wmain


//======================================================================

//
// As above, we should add filtering/prefering based on Hint Bits to 
// this function.  Giving:
// int selectIrdaPeer( SOCKET sock, u_char irdaDeviceID[4], 
//       int flags, u_char hints1, u_char hints2 )
// with flags being a union of:
// IRDA_HINTS_PREFER  Re-order the device list preferring those matching.
// or
// IRDA_HINTS_ONLY    Only keep devices that have the given hint(s).
// This should only be use in special cases, the Hint bytes are just 
// that, their existence shouldn't be mandatory.
// and,
// IRDA_HINTS_ONE_OR  Require only one, or more, of the given hints.
// or 
// IRDA_HINTS_ALL     All the given hints must be present.
// The default is IRDA_HINTS_PREFER and IRDA_HINTS_ONE_OR.
// The hints1 and hint2 are for the first and second Hint Bytes
// respectively, the values used as as defined in AF_Irda.h etc.
// 
// #define IRDA_HINTS_PREFER	0x00
// #define IRDA_HINTS_ONLY		0x01
//
// #define IRDA_HINTS_ONE_OR	0x00
// #define IRDA_HINTS_ALL		0x10
//

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
	//--------------------------------------------
	xDevListLen	= sizeof xDevListBuf;
	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 #%d\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 WSAEFAULT:	//+14
			xString = L"WSAEFAULT";
			break;
		case WSAEADDRINUSE:	//+48
			xString = L"WSAEADDRINUSE";
			break;
		case WSAECONNABORTED:	//+53
			xString = L"WSAECONNABORTED";
			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