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