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