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