1 /*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /** \file
18 This file consists of implementation of class AdbWinUsbEndpointObject that
19 encapsulates a handle opened to a WinUsb endpoint on our device.
20 */
21
22 #include "stdafx.h"
23 #include "adb_winusb_endpoint_object.h"
24 #include "adb_winusb_io_completion.h"
25
AdbWinUsbEndpointObject(AdbWinUsbInterfaceObject * parent_interf,UCHAR endpoint_id,UCHAR endpoint_index)26 AdbWinUsbEndpointObject::AdbWinUsbEndpointObject(
27 AdbWinUsbInterfaceObject* parent_interf,
28 UCHAR endpoint_id,
29 UCHAR endpoint_index)
30 : AdbEndpointObject(parent_interf, endpoint_id, endpoint_index),
31 lock_(), is_closing_(false), pending_io_count_(0) {
32 }
33
~AdbWinUsbEndpointObject()34 AdbWinUsbEndpointObject::~AdbWinUsbEndpointObject() {
35 }
36
Release()37 LONG AdbWinUsbEndpointObject::Release() {
38 ATLASSERT(ref_count_ > 0);
39 LONG ret = InterlockedDecrement(&ref_count_);
40 ATLASSERT(ret >= 0);
41 if (0 == ret) {
42 LastReferenceReleased();
43 delete this;
44 }
45 return ret;
46 }
47
CloseHandle()48 bool AdbWinUsbEndpointObject::CloseHandle() {
49 // This method only returns once all pending IOs are aborted and after
50 // preventing future pending IOs. This means that once CloseHandle()
51 // returns, threads using this object won't be using
52 // parent_winusb_interface()->winusb_handle(), so it can then be safely
53 // released.
54 lock_.Lock();
55 if (!is_closing_) {
56 // Set flag to prevent new I/Os from starting up.
57 is_closing_ = true;
58 }
59
60 // While there are pending IOs, keep aborting the pipe. We have to do this
61 // repeatedly because pending_ios_ is incremented before the IO has actually
62 // started, and abort (probably) only works if the IO has been started.
63 while (pending_io_count_ > 0) {
64 lock_.Unlock();
65
66 // It has been noticed that on Windows 7, if you only call
67 // WinUsb_AbortPipe(), without first calling WinUsb_ResetPipe(), the call
68 // to WinUsb_AbortPipe() hangs.
69 if (!WinUsb_ResetPipe(parent_winusb_interface()->winusb_handle(),
70 endpoint_id()) ||
71 !WinUsb_AbortPipe(parent_winusb_interface()->winusb_handle(),
72 endpoint_id())) {
73 // Reset or Abort failed for unexpected reason. We might not be able to
74 // abort pending IOs, so we shouldn't keep polling pending_io_count_ or
75 // else we might hang forever waiting for the IOs to abort. In this
76 // situation it is preferable to risk a race condition (which may or may
77 // not crash) and just break now.
78 lock_.Lock();
79 break;
80 }
81
82 // Give the IO threads time to break out of I/O calls and decrement
83 // pending_io_count_. They should finish up pretty quick. The amount of time
84 // "wasted" here (as opposed to if we did synchronization with an event)
85 // doesn't really matter since this is an uncommon corner-case.
86 Sleep(16); // 16 ms, old default OS scheduler granularity
87
88 lock_.Lock();
89 }
90
91 lock_.Unlock();
92
93 return AdbEndpointObject::CloseHandle();
94 }
95
CommonAsyncReadWrite(bool is_read,void * buffer,ULONG bytes_to_transfer,ULONG * bytes_transferred,HANDLE event_handle,ULONG time_out)96 ADBAPIHANDLE AdbWinUsbEndpointObject::CommonAsyncReadWrite(
97 bool is_read,
98 void* buffer,
99 ULONG bytes_to_transfer,
100 ULONG* bytes_transferred,
101 HANDLE event_handle,
102 ULONG time_out) {
103 // TODO: Do synchronization with is_closing_ and pending_io_count_ like
104 // CommonSyncReadWrite(). This is not yet implemented because there are no
105 // callers to Adb{Read,Write}EndpointAsync() in AOSP, and hence no testing.
106 if (!SetTimeout(time_out))
107 return false;
108
109 // Create completion i/o object
110 AdbIOCompletion* adb_io_completion = NULL;
111
112 try {
113 adb_io_completion = new AdbWinUsbIOCompletion(this,
114 bytes_to_transfer,
115 event_handle);
116 } catch (... ) {
117 SetLastError(ERROR_OUTOFMEMORY);
118 return NULL;
119 }
120
121 // Create a handle for it
122 ADBAPIHANDLE ret = adb_io_completion->CreateHandle();
123 ULONG transferred = 0;
124 if (NULL != ret) {
125 BOOL res = TRUE;
126 // Go the read / write file way
127 res = is_read ?
128 WinUsb_ReadPipe(parent_winusb_interface()->winusb_handle(),
129 endpoint_id(),
130 reinterpret_cast<PUCHAR>(buffer),
131 bytes_to_transfer,
132 &transferred,
133 adb_io_completion->overlapped()) :
134 WinUsb_WritePipe(parent_winusb_interface()->winusb_handle(),
135 endpoint_id(),
136 reinterpret_cast<PUCHAR>(buffer),
137 bytes_to_transfer,
138 &transferred,
139 adb_io_completion->overlapped());
140
141 if (NULL != bytes_transferred)
142 *bytes_transferred = transferred;
143
144 ULONG error = GetLastError();
145 if (!res && (ERROR_IO_PENDING != error)) {
146 // I/O failed immediatelly. We need to close i/o completion object
147 // before we return NULL to the caller.
148 adb_io_completion->CloseHandle();
149 ret = NULL;
150 SetLastError(error);
151 }
152 }
153
154 // Offseting 'new'
155 adb_io_completion->Release();
156
157 return ret;
158 }
159
CommonSyncReadWrite(bool is_read,void * buffer,ULONG bytes_to_transfer,ULONG * bytes_transferred,ULONG time_out)160 bool AdbWinUsbEndpointObject::CommonSyncReadWrite(bool is_read,
161 void* buffer,
162 ULONG bytes_to_transfer,
163 ULONG* bytes_transferred,
164 ULONG time_out) {
165 lock_.Lock();
166 if (is_closing_) {
167 lock_.Unlock();
168 // AdbCloseHandle() is in progress, so don't start up any new IOs.
169 SetLastError(ERROR_HANDLES_CLOSED);
170 return false;
171 } else {
172 // Not closing down, so record the fact that we're doing IO. This will
173 // prevent CloseHandle() from returning until our IO completes or it aborts
174 // our IO.
175 ++pending_io_count_;
176 lock_.Unlock();
177 }
178
179 // Because we've incremented pending_ios_, do the matching decrement when this
180 // object goes out of scope.
181 DecrementPendingIO dec(this);
182
183 if (!SetTimeout(time_out))
184 return false;
185
186 // This is synchronous I/O. Since we always open I/O items for
187 // overlapped I/O we're obligated to always provide OVERLAPPED
188 // structure to read / write routines. Prepare it now.
189 OVERLAPPED overlapped;
190 ZeroMemory(&overlapped, sizeof(overlapped));
191 overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
192
193 BOOL ret = TRUE;
194 ULONG transferred = 0;
195 // Go the read / write file way
196 ret = is_read ?
197 WinUsb_ReadPipe(parent_winusb_interface()->winusb_handle(),
198 endpoint_id(),
199 reinterpret_cast<PUCHAR>(buffer),
200 bytes_to_transfer,
201 &transferred,
202 &overlapped) :
203 WinUsb_WritePipe(parent_winusb_interface()->winusb_handle(),
204 endpoint_id(),
205 reinterpret_cast<PUCHAR>(buffer),
206 bytes_to_transfer,
207 &transferred,
208 &overlapped);
209
210 // Lets see the result
211 if (!ret && (ERROR_IO_PENDING != GetLastError())) {
212 // I/O failed.
213 if (NULL != overlapped.hEvent)
214 ::CloseHandle(overlapped.hEvent);
215 return false;
216 }
217
218 // Lets wait till I/O completes
219 ret = WinUsb_GetOverlappedResult(parent_winusb_interface()->winusb_handle(), &overlapped,
220 &transferred, TRUE);
221 if (ret && (NULL != bytes_transferred)) {
222 *bytes_transferred = transferred;
223 }
224
225 if (NULL != overlapped.hEvent)
226 ::CloseHandle(overlapped.hEvent);
227
228 return ret ? true : false;
229 }
230
SetTimeout(ULONG timeout)231 bool AdbWinUsbEndpointObject::SetTimeout(ULONG timeout) {
232 if (!WinUsb_SetPipePolicy(parent_winusb_interface()->winusb_handle(),
233 endpoint_id(), PIPE_TRANSFER_TIMEOUT,
234 sizeof(ULONG), &timeout)) {
235 return false;
236 }
237
238 return true;
239 }
240