#define UNICODE #include #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" #define MAX_ATTRIB_LEN 64 #define DEVICE_LIST_LEN 5 #define IRDA_DEVICE_NAME TEXT("\\Device\\IrDA") #define DISCOVERY_BUFFER_SIZE (sizeof(DEVICELIST) - \ sizeof(IRDA_DEVICE_INFO) + \ (sizeof(IRDA_DEVICE_INFO) * DEVICE_LIST_LEN)) typedef struct _IR_DISCOVERY_OBJECT { BOOL Closing; LONG ReferenceCount; HANDLE DeviceHandle; SOCKET Socket; HWND WindowHandle; UINT DiscoveryWindowMessage; UINT LinkWindowMessage; HANDLE TimerHandle; IO_STATUS_BLOCK DiscoveryStatusBlock; IO_STATUS_BLOCK LinkStateStatusBlock; BYTE IoDeviceListBuffer[DISCOVERY_BUFFER_SIZE]; BYTE CurrentDeviceListBuffer[DISCOVERY_BUFFER_SIZE]; IRLINK_STATUS IoLinkStatus; IRLINK_STATUS CurrentLinkStatus; } IR_DISCOVERY_OBJECT, *PIR_DISCOVERY_OBJECT; VOID WINAPI TimerApcRoutine( PIR_DISCOVERY_OBJECT DiscoveryObject, DWORD LowTime, DWORD HighTime ); VOID WINAPI DiscoverComplete( PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, DWORD Reserved ); VOID WINAPI LinkStatusComplete( PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, DWORD Reserved ); int QueryIASForInteger(SOCKET QuerySock, u_char *pirdaDeviceID, char *pClassName, int ClassNameLen, // including trailing NULL char *pAttribute, int AttributeLen, // including trailing NULL int *pValue) { BYTE IASQueryBuff[sizeof(IAS_QUERY) - 3 + MAX_ATTRIB_LEN]; int IASQueryLen = sizeof(IASQueryBuff); PIAS_QUERY pIASQuery = (PIAS_QUERY) &IASQueryBuff; #if DBG if (!((ClassNameLen > 0 && ClassNameLen <= IAS_MAX_CLASSNAME) && (AttributeLen > 0 && AttributeLen <= IAS_MAX_ATTRIBNAME))) { DEBUGMSG(("IRMON: QueryIASForInteger, bad parms\n")); return(SOCKET_ERROR); } #endif RtlCopyMemory(&pIASQuery->irdaDeviceID[0], pirdaDeviceID, 4); RtlCopyMemory(&pIASQuery->irdaClassName[0], pClassName, ClassNameLen); RtlCopyMemory(&pIASQuery->irdaAttribName[0], pAttribute, AttributeLen); if (getsockopt(QuerySock, SOL_IRLMP, IRLMP_IAS_QUERY, (char *) pIASQuery, &IASQueryLen) == SOCKET_ERROR) { #if 0 DEBUGMSG(("IRMON: IAS Query [\"%s\",\"%s\"] failed %ws\n", pIASQuery->irdaClassName, pIASQuery->irdaAttribName, GetLastErrorText())); #endif return SOCKET_ERROR; } if (pIASQuery->irdaAttribType != IAS_ATTRIB_INT) { DEBUGMSG(("IRMON: IAS Query [\"%s\",\"%s\"] irdaAttribType not int (%d)\n", pIASQuery->irdaClassName, pIASQuery->irdaAttribName, pIASQuery->irdaAttribType)); return SOCKET_ERROR; } *pValue = pIASQuery->irdaAttribute.irdaAttribInt; return(0); } HANDLE CreateIrDiscoveryObject( HWND WindowHandle, UINT DiscoveryWindowMessage, UINT LinkWindowMessage ) { PIR_DISCOVERY_OBJECT DiscoveryObject; LONGLONG DueTime=Int32x32To64(2000,-10000); DiscoveryObject=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*DiscoveryObject)); if (DiscoveryObject == NULL) { return NULL; } DiscoveryObject->WindowHandle=WindowHandle; DiscoveryObject->DiscoveryWindowMessage=DiscoveryWindowMessage; DiscoveryObject->LinkWindowMessage=LinkWindowMessage; DiscoveryObject->DeviceHandle=INVALID_HANDLE_VALUE; DiscoveryObject->Socket=INVALID_SOCKET; DiscoveryObject->TimerHandle=CreateWaitableTimer(NULL,FALSE,NULL); if (DiscoveryObject->TimerHandle == NULL) { HeapFree(GetProcessHeap(),0,DiscoveryObject); return NULL; } DiscoveryObject->ReferenceCount=1; SetWaitableTimer( DiscoveryObject->TimerHandle, (LARGE_INTEGER*)&DueTime, 0, TimerApcRoutine, DiscoveryObject, FALSE ); return (HANDLE)DiscoveryObject; } VOID RemoveRefCount( PIR_DISCOVERY_OBJECT DiscoveryObject ) { LONG Count=InterlockedDecrement(&DiscoveryObject->ReferenceCount); if (Count == 0) { CancelWaitableTimer(DiscoveryObject->TimerHandle); CloseHandle(DiscoveryObject->TimerHandle); if (DiscoveryObject->DeviceHandle != INVALID_HANDLE_VALUE) { CancelIo(DiscoveryObject->DeviceHandle); CloseHandle(DiscoveryObject->DeviceHandle); } if (DiscoveryObject->Socket != INVALID_SOCKET) { closesocket(DiscoveryObject->Socket); } DbgPrint("irmon: discovery object closed\n"); HeapFree(GetProcessHeap(),0,DiscoveryObject); } return; } VOID CloseIrDiscoveryObject( HANDLE Object ) { PIR_DISCOVERY_OBJECT DiscoveryObject=Object; DiscoveryObject->Closing=TRUE; if (DiscoveryObject->DeviceHandle != INVALID_HANDLE_VALUE) { CancelIo(DiscoveryObject->DeviceHandle); } return; } VOID WINAPI TimerApcRoutine( PIR_DISCOVERY_OBJECT DiscoveryObject, DWORD LowTime, DWORD HighTime ) { IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING DeviceName; OBJECT_ATTRIBUTES ObjAttr; NTSTATUS Status; LONGLONG DueTime=Int32x32To64(10000,-10000); if (DiscoveryObject->Closing) { RemoveRefCount(DiscoveryObject); return; } if (DiscoveryObject->DeviceHandle == INVALID_HANDLE_VALUE) { // Open the stack and issue lazy discovery and status ioctls RtlInitUnicodeString(&DeviceName, IRDA_DEVICE_NAME); InitializeObjectAttributes( &ObjAttr, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtCreateFile( &DiscoveryObject->DeviceHandle, // PHANDLE FileHandle GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, // ACCESS_MASK DesiredAccess &ObjAttr, // POBJECT_ATTRIBUTES ObjAttr &IoStatusBlock, // PIO_STATUS_BLOCK IoStatusBlock NULL, // PLARGE_INTEGER AllocationSize FILE_ATTRIBUTE_NORMAL, // ULONG FileAttributes FILE_SHARE_READ | FILE_SHARE_WRITE, // ULONG ShareAccess FILE_OPEN_IF, // ULONG CreateDisposition 0, // ULONG CreateOptions NULL, // PVOID EaBuffer 0); // ULONG EaLength if (!NT_SUCCESS(Status)) { DEBUGMSG(("IRMON: NtCreateFile irda.sys failed\n")); DiscoveryObject->DeviceHandle=INVALID_HANDLE_VALUE; SetWaitableTimer( DiscoveryObject->TimerHandle, (LARGE_INTEGER*)&DueTime, 0, TimerApcRoutine, DiscoveryObject, FALSE ); return; } // Flush IrDA's discovery cache because the user may log out // and devices will remain in the cache. When they log back in // the device would then appear briefly. NtDeviceIoControlFile( DiscoveryObject->DeviceHandle, // HANDLE FileHandle NULL, // HANDLE Event OPTIONAL NULL, // PIO_APC_ROUTINE ApcRoutine NULL, // PVOID ApcContext &IoStatusBlock, // PIO_STATUS_BLOCK IoStatusBlock IOCTL_IRDA_FLUSH_DISCOVERY_CACHE,// ULONG IoControlCode NULL, // PVOID InputBuffer 0, // ULONG InputBufferLength NULL, // PVOID OutputBuffer 0); // ULONG OutputBufferLength DiscoveryObject->Socket = socket(AF_IRDA, SOCK_STREAM, 0); if (DiscoveryObject->Socket == INVALID_SOCKET) { // DEBUGMSG(("IRMON: socket() error: %ws\n", GetLastErrorText())); CloseHandle(DiscoveryObject->DeviceHandle); DiscoveryObject->DeviceHandle=INVALID_HANDLE_VALUE; SetWaitableTimer( DiscoveryObject->TimerHandle, (LARGE_INTEGER*)&DueTime, 0, TimerApcRoutine, DiscoveryObject, FALSE ); return; } else { DEBUGMSG(("IRMON: socket created (%d).\n", DiscoveryObject->Socket)); } } Status = NtDeviceIoControlFile( DiscoveryObject->DeviceHandle, // HANDLE FileHandle NULL, // HANDLE Event OPTIONAL DiscoverComplete,// PIO_APC_ROUTINE ApcRoutine DiscoveryObject, // PVOID ApcContext &DiscoveryObject->DiscoveryStatusBlock, // PIO_STATUS_BLOCK IoStatusBlock IOCTL_IRDA_LAZY_DISCOVERY, NULL, // PVOID InputBuffer 0, // ULONG InputBufferLength &DiscoveryObject->IoDeviceListBuffer[0], // PVOID OutputBuffer sizeof(DiscoveryObject->IoDeviceListBuffer) // ULONG OutputBufferLength ); if (!NT_SUCCESS(Status)) { SetWaitableTimer( DiscoveryObject->TimerHandle, (LARGE_INTEGER*)&DueTime, 0, TimerApcRoutine, DiscoveryObject, FALSE ); } InterlockedIncrement(&DiscoveryObject->ReferenceCount); Status = NtDeviceIoControlFile( DiscoveryObject->DeviceHandle, // HANDLE FileHandle NULL, // HANDLE Event OPTIONAL LinkStatusComplete,// PIO_APC_ROUTINE ApcRoutine DiscoveryObject, // PVOID ApcContext &DiscoveryObject->LinkStateStatusBlock, // PIO_STATUS_BLOCK IoStatusBlock IOCTL_IRDA_LINK_STATUS, // ULONG IoControlCode NULL, // PVOID InputBuffer 0, // ULONG InputBufferLength &DiscoveryObject->IoLinkStatus, // PVOID OutputBuffer sizeof(DiscoveryObject->IoLinkStatus) // ULONG OutputBufferLength ); if (!NT_SUCCESS(Status)) { RemoveRefCount(DiscoveryObject); } return; } VOID WINAPI DiscoverComplete( PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, DWORD Reserved ) { NTSTATUS Status; LONGLONG DueTime=Int32x32To64(10000,-10000); PIR_DISCOVERY_OBJECT DiscoveryObject=ApcContext; PDEVICELIST devices=(PDEVICELIST)&DiscoveryObject->CurrentDeviceListBuffer[0]; CopyMemory( &DiscoveryObject->CurrentDeviceListBuffer[0], &DiscoveryObject->IoDeviceListBuffer[0], sizeof(DiscoveryObject->IoDeviceListBuffer) ); if (DiscoveryObject->Closing) { RemoveRefCount(DiscoveryObject); return; } if (NT_SUCCESS(IoStatusBlock->Status) && (IoStatusBlock->Information >= sizeof(ULONG))) { PostMessage( DiscoveryObject->WindowHandle, DiscoveryObject->DiscoveryWindowMessage, 0, 0 ); } else { devices->numDevice=0; } Status = NtDeviceIoControlFile( DiscoveryObject->DeviceHandle, // HANDLE FileHandle NULL, // HANDLE Event OPTIONAL DiscoverComplete,// PIO_APC_ROUTINE ApcRoutine DiscoveryObject, // PVOID ApcContext &DiscoveryObject->DiscoveryStatusBlock, // PIO_STATUS_BLOCK IoStatusBlock IOCTL_IRDA_LAZY_DISCOVERY, NULL, // PVOID InputBuffer 0, // ULONG InputBufferLength &DiscoveryObject->IoDeviceListBuffer[0], // PVOID OutputBuffer sizeof(DiscoveryObject->IoDeviceListBuffer) // ULONG OutputBufferLength ); if (!NT_SUCCESS(Status)) { SetWaitableTimer( DiscoveryObject->TimerHandle, (LARGE_INTEGER*)&DueTime, 0, TimerApcRoutine, DiscoveryObject, FALSE ); } return; } VOID WINAPI LinkStatusComplete( PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, DWORD Reserved ) { NTSTATUS Status; PIR_DISCOVERY_OBJECT DiscoveryObject=ApcContext; CopyMemory( &DiscoveryObject->CurrentLinkStatus, &DiscoveryObject->IoLinkStatus, sizeof(DiscoveryObject->IoLinkStatus) ); PostMessage( DiscoveryObject->WindowHandle, DiscoveryObject->LinkWindowMessage, 0, 0 ); Status = NtDeviceIoControlFile( DiscoveryObject->DeviceHandle, // HANDLE FileHandle NULL, // HANDLE Event OPTIONAL LinkStatusComplete,// PIO_APC_ROUTINE ApcRoutine DiscoveryObject, // PVOID ApcContext &DiscoveryObject->LinkStateStatusBlock, // PIO_STATUS_BLOCK IoStatusBlock IOCTL_IRDA_LINK_STATUS, // ULONG IoControlCode NULL, // PVOID InputBuffer 0, // ULONG InputBufferLength &DiscoveryObject->IoLinkStatus, // PVOID OutputBuffer sizeof(DiscoveryObject->IoLinkStatus) // ULONG OutputBufferLength ); if (!NT_SUCCESS(Status)) { RemoveRefCount(DiscoveryObject); } } LONG GetDeviceList( HANDLE Object, OBEX_DEVICE_LIST * List, ULONG *ListBufferSize ) { PIR_DISCOVERY_OBJECT DiscoveryObject=Object; ULONG BufferSizeNeeded; ULONG i; PDEVICELIST devices=(PDEVICELIST)&DiscoveryObject->CurrentDeviceListBuffer[0]; BufferSizeNeeded=(devices->numDevice * sizeof(OBEX_DEVICE)) + FIELD_OFFSET(OBEX_DEVICE_LIST,DeviceList); if (*ListBufferSize < BufferSizeNeeded) { *ListBufferSize= BufferSizeNeeded; return ERROR_INSUFFICIENT_BUFFER; } ZeroMemory(List,*ListBufferSize); for (i=0; inumDevice; i++) { // // the irda device name buffer is 23 bytes in size and may ahve either ascii or // unicode chars. Add enough bytes to round up the an even number of unicode chars // plus a null terminator. // UCHAR TempBuffer[sizeof(devices->Device[i].irdaDeviceName)+3]; unsigned MaxCharCount; CopyMemory( &List->DeviceList[i].DeviceSpecific.s.Irda.DeviceId, &devices->Device[i].irdaDeviceID, sizeof(ULONG) ); // List->DeviceList[i].DeviceSpecific.s.Irda.DeviceId= *(unsigned long *) devices->Device[i].irdaDeviceID; List->DeviceList[i].DeviceType=TYPE_IRDA; // // zero out the whole buffer and then copy the string from the device to make sure it // is null terminated // ZeroMemory(&TempBuffer[0],sizeof(TempBuffer)); CopyMemory(&TempBuffer[0],devices->Device[i].irdaDeviceName,sizeof(devices->Device[i].irdaDeviceName)); // // get the character count of unicode destination buffer // MaxCharCount = sizeof(List->DeviceList[i].DeviceName)/sizeof(wchar_t); if (devices->Device[i].irdaCharSet != LmCharSetUNICODE) { MultiByteToWideChar(CP_ACP, 0, &TempBuffer[0], -1, // NULL terminated string List->DeviceList[i].DeviceName, MaxCharCount ); } else { // // the name is in unicode // wcsncpy( List->DeviceList[i].DeviceName, (wchar_t *)&TempBuffer[0], MaxCharCount ); // // Assure that it is NULL-terminated. // List->DeviceList[i].DeviceName[ MaxCharCount-1 ] = 0; } // lstrcat(List->DeviceList[i].DeviceName,TEXT("(IR)")); { int LSapSel; int Attempt; LONG status; for (Attempt=1; Attempt < 5; ++Attempt) { status = QueryIASForInteger(DiscoveryObject->Socket, devices->Device[i].irdaDeviceID, "OBEX:IrXfer", 12, "IrDA:TinyTP:LsapSel", 20, &LSapSel); if (status != ERROR_SUCCESS) { status = QueryIASForInteger(DiscoveryObject->Socket, devices->Device[i].irdaDeviceID, "OBEX", 5, "IrDA:TinyTP:LsapSel", 20, &LSapSel); } if (status == WSAETIMEDOUT || status == WSAECONNRESET) { Sleep(250); continue; } break; } if (!status) { List->DeviceList[i].DeviceSpecific.s.Irda.ObexSupport=TRUE; } } List->DeviceCount++; } return ERROR_SUCCESS; } VOID GetLinkStatus( HANDLE Object, IRLINK_STATUS *LinkStatus ) { PIR_DISCOVERY_OBJECT DiscoveryObject=Object; *LinkStatus=DiscoveryObject->CurrentLinkStatus; return; }