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