/** * Handle the case where the device unexpectedly fails. * @param device The device the failed. * @param ec The error code from GetLastError() * @return The device error code. */ int handleDeviceFailure(struct FreespaceDeviceStruct* device, int ec) { int rc = FREESPACE_ERROR_UNEXPECTED; int formatRc = 0; LPVOID lpMsgBuf; // Retrieve the system error message for the last-error code formatRc = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, ec, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); if (formatRc == 0) { DEBUG_WPRINTF(L"handleDeviceFailure: %d\n", ec); } else { // Display the error message and exit the process DEBUG_WPRINTF(L"handleDeviceFailure: %d => %s\n", ec, lpMsgBuf); LocalFree(lpMsgBuf); } // Clean up this device. freespace_private_removeDevice(device); freespace_private_forceCloseDevice(device); freespace_private_requestDeviceRescan(); if (ec == ERROR_DEVICE_NOT_CONNECTED) { rc = FREESPACE_ERROR_NOT_FOUND; } return rc; }
/* * Remove the devices that are no longer present in the system. */ int checkDiscoveryRemoveDevices() { int i; struct FreespaceDeviceStruct* list[FREESPACE_MAXIMUM_DEVICE_COUNT]; int listLength = 0; // Collect the removed devices. freespace_private_filterDevices(list, FREESPACE_MAXIMUM_DEVICE_COUNT, &listLength, filterSweep); // Remove them from the device list so that future API calls fail to this device // fail. See callbacks after this loop. for (i = 0; i < listLength; i++) { int idx; DEBUG_WPRINTF(L"device %d removed\n", list[i]->id_); for (idx = 0; idx < freespace_instance_->deviceCount_; idx++) { if (list[i] == freespace_instance_->devices_[idx]) { memmove(&freespace_instance_->devices_[idx], &freespace_instance_->devices_[idx + 1], (freespace_instance_->deviceCount_ - idx - 1) * sizeof(struct FreespaceDeviceStruct*)); freespace_instance_->deviceCount_--; } } } // Call the removal callbacks. for (i = 0; i < listLength; i++) { freespace_private_removeDevice(list[i]); } // Free the device structure. for (i = 0; i < listLength; i++) { freespace_private_freeDevice(list[i]); } return listLength; }
int checkDiscovery() { if (freespace_private_discoveryStatusChanged()) { int rc; int totalChanges = 0; // Wait for system to stabilize before scanning. if (CMP_WaitNoPendingInstallEvents(0) == WAIT_TIMEOUT) { DEBUG_PRINTF("Pending install events. Wait for resolution.\n"); freespace_private_requestDeviceRescan(); return FREESPACE_ERROR_BUSY; } DEBUG_WPRINTF(L"Scanning devices\n"); // Mark and sweep the device list. freespace_private_filterDevices(NULL, 0, NULL, filterInitialize); // Mark everything that still exists and add new devices rc = freespace_private_scanAndAddDevices(); if (rc != FREESPACE_SUCCESS) { // Unexpected error. Schedule a rescan. DEBUG_WPRINTF(L"Error %d while scanning devices. Request a rescan.\n", rc); freespace_private_requestDeviceRescan(); return rc; } // Handle all changes. totalChanges += checkDiscoveryRemoveDevices(); totalChanges += checkDiscoveryPartiallyRemovedDevices(); totalChanges += checkDiscoveryAddedDevices(); /* * Continue to schedule a rescan until no changes are detected. * Although this should not be necessary, rescanning until a * stable state is reached should increase robustness. */ if (totalChanges != 0) { DEBUG_WPRINTF(L"Detected %d changes. Schedule rescan\n", totalChanges); freespace_private_requestDeviceRescan(); } } return freespace_private_discoveryGetThreadStatus(); }
/* * Process devices with multiple handles that are not fully complete. */ int checkDiscoveryAddedDevices() { struct FreespaceDeviceStruct* list[FREESPACE_MAXIMUM_DEVICE_COUNT]; int listLength = 0; int i; // Collect the added devices and call the insertion callbacks freespace_private_filterDevices(list, FREESPACE_MAXIMUM_DEVICE_COUNT, &listLength, filterReady); for (i = 0; i < listLength; i++) { DEBUG_WPRINTF(L"device %d added\n", list[i]->id_); } for (i = 0; i < listLength; i++) { freespace_private_insertDevice(list[i]); } return listLength; }
/* * Process devices with multiple handles that are not fully complete. */ int checkDiscoveryPartiallyRemovedDevices() { struct FreespaceDeviceStruct* list[FREESPACE_MAXIMUM_DEVICE_COUNT]; int listLength = 0; int i; // Collect the removed devices and call the removed callbacks. freespace_private_filterDevices(list, FREESPACE_MAXIMUM_DEVICE_COUNT, &listLength, filterPartiallyRemoved); for (i = 0; i < listLength; i++) { DEBUG_WPRINTF(L"device %d partially removed\n", list[i]->id_); } for (i = 0; i < listLength; i++) { freespace_private_removeDevice(list[i]); } // Close file handles of devices that are open. for (i = 0; i < listLength; i++) { freespace_closeDevice(list[i]->id_); } return listLength; }
LIBFREESPACE_API int freespace_openDevice(FreespaceDeviceId id) { int idx; struct FreespaceDeviceStruct* device = freespace_private_getDeviceById(id); if (device == NULL) { return FREESPACE_ERROR_NO_DEVICE; } if (device->isOpened_) { // Each device can only be opened once. return FREESPACE_ERROR_BUSY; } for (idx = 0; idx < device->handleCount_; idx++) { struct FreespaceSubStruct* s = &device->handle_[idx]; if (s->handle_ != NULL) { // Device was partially (incorrectly) opened. freespace_private_forceCloseDevice(device); return FREESPACE_ERROR_BUSY; } if (s->devicePath == NULL) { // Device was not fully enumerated. freespace_private_forceCloseDevice(device); return FREESPACE_ERROR_NO_DEVICE; } DEBUG_WPRINTF(L"Open %s\n", s->devicePath); s->handle_ = CreateFile(s->devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); { DWORD d; if (!GetHandleInformation(s->handle_, &d)) { // We do not have the correct handle. DEBUG_PRINTF("freespace_openDevice failed with code %d\n", GetLastError()); } } if (s->handle_ == INVALID_HANDLE_VALUE) { freespace_private_forceCloseDevice(device); return FREESPACE_ERROR_NO_DEVICE; } if (!BindIoCompletionCallback(s->handle_, freespace_private_overlappedCallback, 0)) { freespace_private_forceCloseDevice(device); return FREESPACE_ERROR_UNEXPECTED; } if (!HidD_SetNumInputBuffers(s->handle_, HID_NUM_INPUT_BUFFERS)) { freespace_private_forceCloseDevice(device); return FREESPACE_ERROR_NO_DEVICE; } // Create the read event. s->readOverlapped_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (s->readOverlapped_.hEvent == NULL) { freespace_private_forceCloseDevice(device); return FREESPACE_ERROR_UNEXPECTED; } s->readOverlapped_.Offset = 0; s->readOverlapped_.OffsetHigh = 0; s->readStatus_ = FALSE; } device->isOpened_ = TRUE; // Enable send by initializing all send events. for (idx = 0; idx < FREESPACE_MAXIMUM_SEND_MESSAGE_COUNT; idx++) { device->send_[idx].overlapped_.hEvent = NULL; if (initializeSendStruct(&device->send_[idx]) != FREESPACE_SUCCESS) { freespace_private_forceCloseDevice(device); return FREESPACE_ERROR_UNEXPECTED; } } // If async mode has been enabled already, then start the receive // process going. if (freespace_instance_->fdAddedCallback_) { int rc; rc = initiateAsyncReceives(device); if (rc != FREESPACE_SUCCESS) { freespace_private_forceCloseDevice(device); return rc; } } return FREESPACE_SUCCESS; }
LRESULT CALLBACK discoveryCallback(HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { if (nMsg == WM_CLOSE) { DestroyWindow(hwnd); return DefWindowProc(hwnd, nMsg, wParam, lParam); } if (nMsg == WM_DESTROY) { DEBUG_WPRINTF(L"discoveryCallback on WM_DESTROY\n"); // Remove the device notification if (freespace_instance_->windowEvent_) { UnregisterDeviceNotification(freespace_instance_->windowEvent_); freespace_instance_->windowEvent_ = NULL; } CancelWaitableTimer(freespace_instance_->discoveryEvent_); CloseHandle(freespace_instance_->discoveryEvent_); freespace_instance_->discoveryEvent_ = NULL; PostQuitMessage(0); return DefWindowProc(hwnd, nMsg, wParam, lParam); } if (nMsg == WM_DEVICECHANGE) { // Should start with DEV_BROADCAST_HDR and validate. DEV_BROADCAST_DEVICEINTERFACE* hdr; hdr = (DEV_BROADCAST_DEVICEINTERFACE*) lParam; // Schedule a device list rescan. freespace_private_requestDeviceRescan(); // Only handle all devices arrived or all devices removed. if ((LOWORD(wParam) != DBT_DEVICEARRIVAL) && (LOWORD(wParam) != DBT_DEVICEREMOVECOMPLETE)) { DEBUG_WPRINTF(L"WM_DEVICECHANGE => %d\n", LOWORD(wParam)); return TRUE; } /* * NOTE: Device scan is only performed once changes have stabilized. * The scan routine must be able to handled a device being removed * and reinserted without scan calls between events. */ if (hdr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE) { return TRUE; } if (LOWORD(wParam) == DBT_DEVICEARRIVAL) { DEBUG_WPRINTF(L"DBT_DEVICEARRIVAL => %s\n", hdr->dbcc_name); } else if (LOWORD(wParam) == DBT_DEVICEREMOVECOMPLETE) { DEBUG_WPRINTF(L"DBT_DEVICEREMOVECOMPLETE => %s\n", hdr->dbcc_name); } else { DEBUG_WPRINTF(L"discoveryCallback on unexpected change (%d) => %s\n", LOWORD(wParam), hdr->dbcc_name); } return TRUE; } return DefWindowProc(hwnd, nMsg, wParam, lParam); }