| /* | |
| * Copyright (C) 2009 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /** \file | |
| This file consists of implementation of class AdbWinUsbEndpointObject that | |
| encapsulates a handle opened to a WinUsb endpoint on our device. | |
| */ | |
| #include "stdafx.h" | |
| #include "adb_winusb_endpoint_object.h" | |
| #include "adb_winusb_io_completion.h" | |
| AdbWinUsbEndpointObject::AdbWinUsbEndpointObject( | |
| AdbWinUsbInterfaceObject* parent_interf, | |
| UCHAR endpoint_id, | |
| UCHAR endpoint_index) | |
| : AdbEndpointObject(parent_interf, endpoint_id, endpoint_index), | |
| lock_(), is_closing_(false), pending_io_count_(0) { | |
| } | |
| AdbWinUsbEndpointObject::~AdbWinUsbEndpointObject() { | |
| } | |
| LONG AdbWinUsbEndpointObject::Release() { | |
| ATLASSERT(ref_count_ > 0); | |
| LONG ret = InterlockedDecrement(&ref_count_); | |
| ATLASSERT(ret >= 0); | |
| if (0 == ret) { | |
| LastReferenceReleased(); | |
| delete this; | |
| } | |
| return ret; | |
| } | |
| bool AdbWinUsbEndpointObject::CloseHandle() { | |
| // This method only returns once all pending IOs are aborted and after | |
| // preventing future pending IOs. This means that once CloseHandle() | |
| // returns, threads using this object won't be using | |
| // parent_winusb_interface()->winusb_handle(), so it can then be safely | |
| // released. | |
| lock_.Lock(); | |
| if (!is_closing_) { | |
| // Set flag to prevent new I/Os from starting up. | |
| is_closing_ = true; | |
| } | |
| // While there are pending IOs, keep aborting the pipe. We have to do this | |
| // repeatedly because pending_ios_ is incremented before the IO has actually | |
| // started, and abort (probably) only works if the IO has been started. | |
| while (pending_io_count_ > 0) { | |
| lock_.Unlock(); | |
| // It has been noticed that on Windows 7, if you only call | |
| // WinUsb_AbortPipe(), without first calling WinUsb_ResetPipe(), the call | |
| // to WinUsb_AbortPipe() hangs. | |
| if (!WinUsb_ResetPipe(parent_winusb_interface()->winusb_handle(), | |
| endpoint_id()) || | |
| !WinUsb_AbortPipe(parent_winusb_interface()->winusb_handle(), | |
| endpoint_id())) { | |
| // Reset or Abort failed for unexpected reason. We might not be able to | |
| // abort pending IOs, so we shouldn't keep polling pending_io_count_ or | |
| // else we might hang forever waiting for the IOs to abort. In this | |
| // situation it is preferable to risk a race condition (which may or may | |
| // not crash) and just break now. | |
| lock_.Lock(); | |
| break; | |
| } | |
| // Give the IO threads time to break out of I/O calls and decrement | |
| // pending_io_count_. They should finish up pretty quick. The amount of time | |
| // "wasted" here (as opposed to if we did synchronization with an event) | |
| // doesn't really matter since this is an uncommon corner-case. | |
| Sleep(16); // 16 ms, old default OS scheduler granularity | |
| lock_.Lock(); | |
| } | |
| lock_.Unlock(); | |
| return AdbEndpointObject::CloseHandle(); | |
| } | |
| ADBAPIHANDLE AdbWinUsbEndpointObject::CommonAsyncReadWrite( | |
| bool is_read, | |
| void* buffer, | |
| ULONG bytes_to_transfer, | |
| ULONG* bytes_transferred, | |
| HANDLE event_handle, | |
| ULONG time_out) { | |
| // TODO: Do synchronization with is_closing_ and pending_io_count_ like | |
| // CommonSyncReadWrite(). This is not yet implemented because there are no | |
| // callers to Adb{Read,Write}EndpointAsync() in AOSP, and hence no testing. | |
| if (!SetTimeout(time_out)) | |
| return false; | |
| // Create completion i/o object | |
| AdbIOCompletion* adb_io_completion = NULL; | |
| try { | |
| adb_io_completion = new AdbWinUsbIOCompletion(this, | |
| bytes_to_transfer, | |
| event_handle); | |
| } catch (... ) { | |
| SetLastError(ERROR_OUTOFMEMORY); | |
| return NULL; | |
| } | |
| // Create a handle for it | |
| ADBAPIHANDLE ret = adb_io_completion->CreateHandle(); | |
| ULONG transferred = 0; | |
| if (NULL != ret) { | |
| BOOL res = TRUE; | |
| // Go the read / write file way | |
| res = is_read ? | |
| WinUsb_ReadPipe(parent_winusb_interface()->winusb_handle(), | |
| endpoint_id(), | |
| reinterpret_cast<PUCHAR>(buffer), | |
| bytes_to_transfer, | |
| &transferred, | |
| adb_io_completion->overlapped()) : | |
| WinUsb_WritePipe(parent_winusb_interface()->winusb_handle(), | |
| endpoint_id(), | |
| reinterpret_cast<PUCHAR>(buffer), | |
| bytes_to_transfer, | |
| &transferred, | |
| adb_io_completion->overlapped()); | |
| if (NULL != bytes_transferred) | |
| *bytes_transferred = transferred; | |
| ULONG error = GetLastError(); | |
| if (!res && (ERROR_IO_PENDING != error)) { | |
| // I/O failed immediatelly. We need to close i/o completion object | |
| // before we return NULL to the caller. | |
| adb_io_completion->CloseHandle(); | |
| ret = NULL; | |
| SetLastError(error); | |
| } | |
| } | |
| // Offseting 'new' | |
| adb_io_completion->Release(); | |
| return ret; | |
| } | |
| bool AdbWinUsbEndpointObject::CommonSyncReadWrite(bool is_read, | |
| void* buffer, | |
| ULONG bytes_to_transfer, | |
| ULONG* bytes_transferred, | |
| ULONG time_out) { | |
| lock_.Lock(); | |
| if (is_closing_) { | |
| lock_.Unlock(); | |
| // AdbCloseHandle() is in progress, so don't start up any new IOs. | |
| SetLastError(ERROR_HANDLES_CLOSED); | |
| return false; | |
| } else { | |
| // Not closing down, so record the fact that we're doing IO. This will | |
| // prevent CloseHandle() from returning until our IO completes or it aborts | |
| // our IO. | |
| ++pending_io_count_; | |
| lock_.Unlock(); | |
| } | |
| // Because we've incremented pending_ios_, do the matching decrement when this | |
| // object goes out of scope. | |
| DecrementPendingIO dec(this); | |
| if (!SetTimeout(time_out)) | |
| return false; | |
| // This is synchronous I/O. Since we always open I/O items for | |
| // overlapped I/O we're obligated to always provide OVERLAPPED | |
| // structure to read / write routines. Prepare it now. | |
| OVERLAPPED overlapped; | |
| ZeroMemory(&overlapped, sizeof(overlapped)); | |
| overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); | |
| BOOL ret = TRUE; | |
| ULONG transferred = 0; | |
| // Go the read / write file way | |
| ret = is_read ? | |
| WinUsb_ReadPipe(parent_winusb_interface()->winusb_handle(), | |
| endpoint_id(), | |
| reinterpret_cast<PUCHAR>(buffer), | |
| bytes_to_transfer, | |
| &transferred, | |
| &overlapped) : | |
| WinUsb_WritePipe(parent_winusb_interface()->winusb_handle(), | |
| endpoint_id(), | |
| reinterpret_cast<PUCHAR>(buffer), | |
| bytes_to_transfer, | |
| &transferred, | |
| &overlapped); | |
| // Lets see the result | |
| if (!ret && (ERROR_IO_PENDING != GetLastError())) { | |
| // I/O failed. | |
| if (NULL != overlapped.hEvent) | |
| ::CloseHandle(overlapped.hEvent); | |
| return false; | |
| } | |
| // Lets wait till I/O completes | |
| ret = WinUsb_GetOverlappedResult(parent_winusb_interface()->winusb_handle(), &overlapped, | |
| &transferred, TRUE); | |
| if (ret && (NULL != bytes_transferred)) { | |
| *bytes_transferred = transferred; | |
| } | |
| if (NULL != overlapped.hEvent) | |
| ::CloseHandle(overlapped.hEvent); | |
| return ret ? true : false; | |
| } | |
| bool AdbWinUsbEndpointObject::SetTimeout(ULONG timeout) { | |
| if (!WinUsb_SetPipePolicy(parent_winusb_interface()->winusb_handle(), | |
| endpoint_id(), PIPE_TRANSFER_TIMEOUT, | |
| sizeof(ULONG), &timeout)) { | |
| return false; | |
| } | |
| return true; | |
| } |