• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 AndroidUsbPipeFileObject that
19   encapsulates a common extension for pipe file objects.
20 */
21 #pragma data_seg()
22 #pragma code_seg()
23 
24 #include "precomp.h"
25 #include "android_usb_pipe_file_object.h"
26 
27 #pragma data_seg()
28 #pragma code_seg("PAGE")
29 
AndroidUsbPipeFileObject(AndroidUsbDeviceObject * dev_obj,WDFFILEOBJECT wdf_fo,WDFUSBPIPE wdf_pipe_obj)30 AndroidUsbPipeFileObject::AndroidUsbPipeFileObject(
31     AndroidUsbDeviceObject* dev_obj,
32     WDFFILEOBJECT wdf_fo,
33     WDFUSBPIPE wdf_pipe_obj)
34     : AndroidUsbFileObject(AndroidUsbFileObjectTypePipe, dev_obj, wdf_fo),
35       wdf_pipe_(wdf_pipe_obj) {
36   ASSERT_IRQL_PASSIVE();
37   ASSERT(NULL != wdf_pipe_obj);
38 }
39 
40 #pragma code_seg()
41 
~AndroidUsbPipeFileObject()42 AndroidUsbPipeFileObject::~AndroidUsbPipeFileObject() {
43   ASSERT_IRQL_LOW_OR_DISPATCH();
44 }
45 
46 #pragma code_seg("PAGE")
47 
InitializePipe(const WDF_USB_PIPE_INFORMATION * pipe_info)48 NTSTATUS AndroidUsbPipeFileObject::InitializePipe(
49     const WDF_USB_PIPE_INFORMATION* pipe_info) {
50   ASSERT_IRQL_LOW();
51   ASSERT(IsPipeAttached());
52   if (!IsPipeAttached())
53     return STATUS_INTERNAL_ERROR;
54 
55   // Initialize base class
56   NTSTATUS status = AndroidUsbFileObject::Initialize();
57   ASSERT(NT_SUCCESS(status));
58   if (!NT_SUCCESS(status))
59     return status;
60 
61   // Save pipe information
62   pipe_information_ = *pipe_info;
63 
64   // We will provide size check ourselves (less surprizes always better)
65   WdfUsbTargetPipeSetNoMaximumPacketSizeCheck(wdf_pipe());
66 
67   GoogleDbgPrint("\n===== File %p for %s pipe. max_transfer_size = %X, max_packet_size = %X",
68                  this, is_input_pipe() ? "read" : "write",
69                  max_transfer_size(), max_packet_size());
70   return STATUS_SUCCESS;
71 }
72 
73 #pragma code_seg()
74 
OnEvtIoRead(WDFREQUEST request,size_t length)75 void AndroidUsbPipeFileObject::OnEvtIoRead(WDFREQUEST request,
76                                            size_t length) {
77   ASSERT_IRQL_LOW_OR_DISPATCH();
78 
79   // Make sure that this is an input pipe
80   if (is_output_pipe()) {
81     GoogleDbgPrint("\n!!!! Attempt to read from output pipe %p", this);
82     WdfRequestComplete(request, STATUS_ACCESS_DENIED);
83     return;
84   }
85 
86   // Make sure zero length I/O doesn't go through
87   if (0 == length) {
88     WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, 0);
89     return;
90   }
91 
92   // Get MDL for this request.
93   PMDL request_mdl = NULL;
94   NTSTATUS status = WdfRequestRetrieveOutputWdmMdl(request, &request_mdl);
95   ASSERT(NT_SUCCESS(status) && (NULL != request_mdl));
96   if (NT_SUCCESS(status)) {
97     CommonBulkReadWrite(request,
98                         request_mdl,
99                         static_cast<ULONG>(length),
100                         true,
101                         0,
102                         false);
103   } else {
104     WdfRequestComplete(request, status);
105   }
106 }
107 
OnEvtIoWrite(WDFREQUEST request,size_t length)108 void AndroidUsbPipeFileObject::OnEvtIoWrite(WDFREQUEST request,
109                                             size_t length) {
110 
111   // Make sure that this is an output pipe
112   if (is_input_pipe()) {
113     GoogleDbgPrint("\n!!!! Attempt to write to input pipe %p", this);
114     WdfRequestComplete(request, STATUS_ACCESS_DENIED);
115     return;
116   }
117 
118   // Make sure zero length I/O doesn't go through
119   if (0 == length) {
120     WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, 0);
121     return;
122   }
123 
124   // Get MDL for this request.
125   PMDL request_mdl = NULL;
126   NTSTATUS status = WdfRequestRetrieveInputWdmMdl(request, &request_mdl);
127   ASSERT(NT_SUCCESS(status) && (NULL != request_mdl));
128   if (NT_SUCCESS(status)) {
129     CommonBulkReadWrite(request,
130                         request_mdl,
131                         static_cast<ULONG>(length),
132                         false,
133                         0,
134                         false);
135   } else {
136     WdfRequestComplete(request, status);
137   }
138 }
139 
OnEvtIoDeviceControl(WDFREQUEST request,size_t output_buf_len,size_t input_buf_len,ULONG ioctl_code)140 void AndroidUsbPipeFileObject::OnEvtIoDeviceControl(WDFREQUEST request,
141                                                     size_t output_buf_len,
142                                                     size_t input_buf_len,
143                                                     ULONG ioctl_code) {
144   ASSERT_IRQL_LOW_OR_DISPATCH();
145 
146   switch (ioctl_code) {
147     case ADB_IOCTL_GET_ENDPOINT_INFORMATION:
148       OnCtlGetEndpointInformation(request, output_buf_len);
149       break;
150 
151     case ADB_IOCTL_BULK_READ:
152       OnCtlBulkRead(request, output_buf_len, input_buf_len);
153       break;
154 
155     case ADB_IOCTL_BULK_WRITE:
156       OnCtlBulkWrite(request, output_buf_len, input_buf_len);
157       break;
158 
159     default:
160       AndroidUsbFileObject::OnEvtIoDeviceControl(request,
161                                                  output_buf_len,
162                                                  input_buf_len,
163                                                  ioctl_code);
164       break;
165   }
166 }
167 
OnCtlGetEndpointInformation(WDFREQUEST request,size_t output_buf_len)168 void AndroidUsbPipeFileObject::OnCtlGetEndpointInformation(
169     WDFREQUEST request,
170     size_t output_buf_len) {
171   ASSERT_IRQL_LOW_OR_DISPATCH();
172 
173   // Verify output buffer
174   if (output_buf_len < sizeof(AdbEndpointInformation)) {
175     WdfRequestCompleteWithInformation(request,
176                                       STATUS_BUFFER_TOO_SMALL,
177                                       sizeof(AdbEndpointInformation));
178     return;
179   }
180 
181   // Get the output buffer
182   NTSTATUS status;
183   AdbEndpointInformation* ret_info =
184     reinterpret_cast<AdbEndpointInformation*>(OutAddress(request, &status));
185   ASSERT(NT_SUCCESS(status) && (NULL != ret_info));
186   if (!NT_SUCCESS(status)) {
187     WdfRequestComplete(request, status);
188     return;
189   }
190 
191   // Copy endpoint info to the output
192   ret_info->max_packet_size = pipe_information_.MaximumPacketSize;
193   ret_info->endpoint_address = pipe_information_.EndpointAddress;
194   ret_info->polling_interval = pipe_information_.Interval;
195   ret_info->setting_index = pipe_information_.SettingIndex;
196   ret_info->endpoint_type =
197     static_cast<AdbEndpointType>(pipe_information_.PipeType);
198   ret_info->max_transfer_size = pipe_information_.MaximumTransferSize;
199 
200   WdfRequestCompleteWithInformation(request,
201                                     STATUS_SUCCESS,
202                                     sizeof(AdbEndpointInformation));
203 }
204 
OnCtlBulkRead(WDFREQUEST request,size_t output_buf_len,size_t input_buf_len)205 void AndroidUsbPipeFileObject::OnCtlBulkRead(WDFREQUEST request,
206                                              size_t output_buf_len,
207                                              size_t input_buf_len) {
208   ASSERT_IRQL_LOW_OR_DISPATCH();
209 
210   // Make sure that this is an input pipe
211   if (is_output_pipe()) {
212     GoogleDbgPrint("\n!!!! Attempt to IOCTL read from output pipe %p", this);
213     WdfRequestComplete(request, STATUS_ACCESS_DENIED);
214     return;
215   }
216 
217   // Make sure zero length I/O doesn't go through
218   if (0 == output_buf_len) {
219     WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, 0);
220     return;
221   }
222 
223   // Verify buffers
224   ASSERT(input_buf_len >= sizeof(AdbBulkTransfer));
225   if (input_buf_len < sizeof(AdbBulkTransfer)) {
226     WdfRequestComplete(request, STATUS_INVALID_BUFFER_SIZE);
227     return;
228   }
229 
230   // Get the input buffer
231   NTSTATUS status;
232   AdbBulkTransfer* transfer_param =
233     reinterpret_cast<AdbBulkTransfer*>(InAddress(request, &status));
234   ASSERT(NT_SUCCESS(status) && (NULL != transfer_param));
235   if (!NT_SUCCESS(status)) {
236     WdfRequestComplete(request, status);
237     return;
238   }
239 
240   // Get MDL for this request.
241   PMDL request_mdl = NULL;
242   status = WdfRequestRetrieveOutputWdmMdl(request, &request_mdl);
243   ASSERT(NT_SUCCESS(status) && (NULL != request_mdl));
244   if (NT_SUCCESS(status)) {
245     // Perform the read
246     CommonBulkReadWrite(request,
247                         request_mdl,
248                         static_cast<ULONG>(output_buf_len),
249                         true,
250                         transfer_param->time_out,
251                         true);
252   } else {
253     WdfRequestComplete(request, status);
254   }
255 }
256 
OnCtlBulkWrite(WDFREQUEST request,size_t output_buf_len,size_t input_buf_len)257 void AndroidUsbPipeFileObject::OnCtlBulkWrite(WDFREQUEST request,
258                                               size_t output_buf_len,
259                                               size_t input_buf_len) {
260   ASSERT_IRQL_LOW_OR_DISPATCH();
261 
262   // Make sure that this is an output pipe
263   if (is_input_pipe()) {
264     GoogleDbgPrint("\n!!!! Attempt to IOCTL write to input pipe %p", this);
265     WdfRequestComplete(request, STATUS_ACCESS_DENIED);
266     return;
267   }
268 
269   // Verify buffers
270   ASSERT(input_buf_len >= sizeof(AdbBulkTransfer));
271   // Output buffer points to ULONG that receives number of transferred bytes
272   ASSERT(output_buf_len >= sizeof(ULONG));
273   if ((input_buf_len < sizeof(AdbBulkTransfer)) ||
274       (output_buf_len < sizeof(ULONG))) {
275     WdfRequestComplete(request, STATUS_INVALID_BUFFER_SIZE);
276     return;
277   }
278 
279   // Get the input buffer
280   NTSTATUS status = STATUS_SUCCESS;
281   AdbBulkTransfer* transfer_param =
282     reinterpret_cast<AdbBulkTransfer*>(InAddress(request, &status));
283   ASSERT(NT_SUCCESS(status) && (NULL != transfer_param));
284   if (!NT_SUCCESS(status)) {
285     WdfRequestComplete(request, status);
286     return;
287   }
288 
289   // Get the output buffer
290   ULONG* ret_transfer =
291     reinterpret_cast<ULONG*>(OutAddress(request, &status));
292   ASSERT(NT_SUCCESS(status) && (NULL != ret_transfer));
293   if (!NT_SUCCESS(status)) {
294     WdfRequestComplete(request, status);
295     return;
296   }
297 
298   // Cache these param to prevent us from sudden change after we've chacked it.
299   // This is common practice in protecting ourselves from malicious code:
300   // 1. Never trust anything that comes from the User Mode.
301   // 2. Never assume that anything that User Mode buffer has will remain
302   // unchanged.
303   void* transfer_buffer = transfer_param->GetWriteBuffer();
304   ULONG transfer_size = transfer_param->transfer_size;
305 
306   // Make sure zero length I/O doesn't go through
307   if (0 == transfer_size) {
308     *ret_transfer = 0;
309     WdfRequestCompleteWithInformation(request, STATUS_SUCCESS, sizeof(ULONG));
310     return;
311   }
312 
313   // Make sure that buffer is not NULL
314   ASSERT(NULL != transfer_buffer);
315   if (NULL == transfer_buffer) {
316     WdfRequestComplete(request, STATUS_INVALID_PARAMETER);
317     return;
318   }
319 
320   // At this point we are ready to build MDL for the user buffer.
321   PMDL write_mdl =
322     IoAllocateMdl(transfer_buffer, transfer_size, FALSE, FALSE, NULL);
323   ASSERT(NULL != write_mdl);
324   if (NULL == write_mdl) {
325     WdfRequestComplete(request, STATUS_INSUFFICIENT_RESOURCES);
326     return;
327   }
328 
329   // Now we need to probe/lock this mdl
330   __try {
331     MmProbeAndLockPages(write_mdl,
332                         WdfRequestGetRequestorMode(request),
333                         IoReadAccess);
334     status = STATUS_SUCCESS;
335   } __except (EXCEPTION_EXECUTE_HANDLER) {
336     status = GetExceptionCode();
337     ASSERTMSG("\n!!!!! AndroidUsbPipeFileObject::OnCtlBulkWrite exception",
338               false);
339   }
340 
341   if (!NT_SUCCESS(status)) {
342     IoFreeMdl(write_mdl);
343     WdfRequestComplete(request, status);
344     return;
345   }
346 
347   // Perform the write
348   status = CommonBulkReadWrite(request,
349                                write_mdl,
350                                transfer_size,
351                                false,
352                                transfer_param->time_out,
353                                true);
354   if (!NT_SUCCESS(status)) {
355     // If CommonBulkReadWrite failed we need to unlock and free MDL here
356     MmUnlockPages(write_mdl);
357     IoFreeMdl(write_mdl);
358   }
359 }
360 
CommonBulkReadWrite(WDFREQUEST request,PMDL transfer_mdl,ULONG length,bool is_read,ULONG time_out,bool is_ioctl)361 NTSTATUS AndroidUsbPipeFileObject::CommonBulkReadWrite(
362     WDFREQUEST request,
363     PMDL transfer_mdl,
364     ULONG length,
365     bool is_read,
366     ULONG time_out,
367     bool is_ioctl) {
368   ASSERT_IRQL_LOW_OR_DISPATCH();
369 
370   ASSERT(IsPipeAttached());
371   if (!IsPipeAttached()) {
372     WdfRequestComplete(request, STATUS_INVALID_DEVICE_STATE);
373     return STATUS_INVALID_DEVICE_STATE;
374   }
375 
376   // Quick access check. Might be redundant though...
377   ASSERT((is_read && is_input_pipe()) || (!is_read && is_output_pipe()));
378   if ((is_read && is_output_pipe()) || (!is_read && is_input_pipe())) {
379     WdfRequestComplete(request, STATUS_ACCESS_DENIED);
380     return STATUS_ACCESS_DENIED;
381   }
382 
383   // Set URB flags
384   ULONG urb_flags = USBD_SHORT_TRANSFER_OK | (is_read ?
385                                                 USBD_TRANSFER_DIRECTION_IN :
386                                                 USBD_TRANSFER_DIRECTION_OUT);
387 
388   // Calculate transfer length for this stage.
389   ULONG stage_len =
390     (length > GetTransferGranularity()) ? GetTransferGranularity() : length;
391 
392   // Get virtual address that we're gonna use in the transfer.
393   // We rely here on the fact that we're in the context of the calling thread.
394   void* virtual_address = MmGetMdlVirtualAddress(transfer_mdl);
395 
396   // Allocate our private MDL for this address which we will use for the transfer
397   PMDL new_mdl = IoAllocateMdl(virtual_address, length, FALSE, FALSE, NULL);
398   ASSERT(NULL != new_mdl);
399   if (NULL == new_mdl) {
400     WdfRequestComplete(request, STATUS_INSUFFICIENT_RESOURCES);
401     return STATUS_INSUFFICIENT_RESOURCES;
402   }
403 
404   // Map the portion of user buffer that we're going to transfer at this stage
405   // to our mdl.
406   IoBuildPartialMdl(transfer_mdl, new_mdl, virtual_address, stage_len);
407 
408   // Allocate memory for URB and associate it with this request
409   WDF_OBJECT_ATTRIBUTES mem_attrib;
410   WDF_OBJECT_ATTRIBUTES_INIT(&mem_attrib);
411   mem_attrib.ParentObject = request;
412 
413   WDFMEMORY urb_mem = NULL;
414   PURB urb = NULL;
415   NTSTATUS status =
416     WdfMemoryCreate(&mem_attrib,
417                     NonPagedPool,
418                     GANDR_POOL_TAG_BULKRW_URB,
419                     sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
420                     &urb_mem,
421                     reinterpret_cast<PVOID*>(&urb));
422   ASSERT(NT_SUCCESS(status) && (NULL != urb));
423   if (!NT_SUCCESS(status)) {
424     IoFreeMdl(new_mdl);
425     WdfRequestComplete(request, STATUS_INSUFFICIENT_RESOURCES);
426     return STATUS_INSUFFICIENT_RESOURCES;
427   }
428 
429   // Get USB pipe handle for our pipe and initialize transfer request for it
430   USBD_PIPE_HANDLE usbd_pipe_hndl = usbd_pipe();
431   ASSERT(NULL != usbd_pipe_hndl);
432   if (NULL == usbd_pipe_hndl) {
433     IoFreeMdl(new_mdl);
434     WdfRequestComplete(request, STATUS_INTERNAL_ERROR);
435     return STATUS_INTERNAL_ERROR;
436   }
437 
438   // Initialize URB with request information
439   UsbBuildInterruptOrBulkTransferRequest(
440     urb,
441     sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
442     usbd_pipe_hndl,
443     NULL,
444     new_mdl,
445     stage_len,
446     urb_flags,
447     NULL);
448 
449   // Build transfer request
450   status = WdfUsbTargetPipeFormatRequestForUrb(wdf_pipe(),
451                                                request,
452                                                urb_mem,
453                                                NULL);
454   ASSERT(NT_SUCCESS(status));
455   if (!NT_SUCCESS(status)) {
456     IoFreeMdl(new_mdl);
457     WdfRequestComplete(request, status);
458     return status;
459   }
460 
461   // Initialize our request context.
462   AndroidUsbWdfRequestContext* context =
463     GetAndroidUsbWdfRequestContext(request);
464   ASSERT(NULL != context);
465   if (NULL == context) {
466     IoFreeMdl(new_mdl);
467     WdfRequestComplete(request, STATUS_INTERNAL_ERROR);
468     return STATUS_INTERNAL_ERROR;
469   }
470 
471   context->object_type = AndroidUsbWdfObjectTypeRequest;
472   context->urb_mem = urb_mem;
473   context->transfer_mdl = transfer_mdl;
474   context->mdl = new_mdl;
475   context->length = length;
476   context->transfer_size = stage_len;
477   context->num_xfer = 0;
478   context->virtual_address = virtual_address;
479   context->is_read = is_read;
480   context->initial_time_out = time_out;
481   context->is_ioctl = is_ioctl;
482 
483   // Set our completion routine
484   WdfRequestSetCompletionRoutine(request,
485                                  CommonReadWriteCompletionEntry,
486                                  this);
487 
488   // Init send options (our timeout goes here)
489   WDF_REQUEST_SEND_OPTIONS send_options;
490   if (0 != time_out) {
491     WDF_REQUEST_SEND_OPTIONS_INIT(&send_options, WDF_REQUEST_SEND_OPTION_TIMEOUT);
492     WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&send_options, WDF_REL_TIMEOUT_IN_MS(time_out));
493   }
494 
495   // Timestamp first WdfRequestSend
496   KeQuerySystemTime(&context->sent_at);
497 
498   // Send request asynchronously.
499   if (WdfRequestSend(request, wdf_pipe_io_target(),
500                      (0 == time_out) ? WDF_NO_SEND_OPTIONS : &send_options)) {
501     return STATUS_SUCCESS;
502   }
503 
504   // Something went wrong here
505   status = WdfRequestGetStatus(request);
506   ASSERT(!NT_SUCCESS(status));
507   GoogleDbgPrint("\n!!!!! CommonBulkReadWrite: WdfRequestGetStatus (is_read = %u) failed: %08X",
508            is_read, status);
509   WdfRequestCompleteWithInformation(request, status, 0);
510 
511   return status;
512 }
513 
OnCommonReadWriteCompletion(WDFREQUEST request,PWDF_REQUEST_COMPLETION_PARAMS completion_params,AndroidUsbWdfRequestContext * context)514 void AndroidUsbPipeFileObject::OnCommonReadWriteCompletion(
515     WDFREQUEST request,
516     PWDF_REQUEST_COMPLETION_PARAMS completion_params,
517     AndroidUsbWdfRequestContext* context) {
518   ASSERT_IRQL_LOW_OR_DISPATCH();
519 
520   NTSTATUS status = completion_params->IoStatus.Status;
521   if (!NT_SUCCESS(status)){
522     GoogleDbgPrint("\n========== Request completed with failure: %X", status);
523     IoFreeMdl(context->mdl);
524     // If this was IOCTL-originated write we must unlock and free
525     // our transfer MDL.
526     if (context->is_ioctl && !context->is_read) {
527       MmUnlockPages(context->transfer_mdl);
528       IoFreeMdl(context->transfer_mdl);
529     }
530     WdfRequestComplete(request, status);
531     return;
532   }
533 
534   // Get our URB buffer
535   PURB urb
536     = reinterpret_cast<PURB>(WdfMemoryGetBuffer(context->urb_mem, NULL));
537   ASSERT(NULL != urb);
538 
539   // Lets see how much has been transfered and update our counters accordingly
540   ULONG bytes_transfered =
541     urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
542   // We expect writes to transfer entire packet
543   ASSERT((bytes_transfered == context->transfer_size) || context->is_read);
544   context->num_xfer += bytes_transfered;
545   context->length -= bytes_transfered;
546 
547   // Is there anything left to transfer? Now, by the protocol we should
548   // successfuly complete partial reads, instead of waiting on full set
549   // of requested bytes being accumulated in the read buffer.
550   if ((0 == context->length) || context->is_read) {
551     status = STATUS_SUCCESS;
552 
553     // This was the last transfer
554     if (context->is_ioctl && !context->is_read) {
555       // For IOCTL-originated writes we have to return transfer size through
556       // the IOCTL's output buffer.
557       ULONG* ret_transfer =
558         reinterpret_cast<ULONG*>(OutAddress(request, NULL));
559       ASSERT(NULL != ret_transfer);
560       if (NULL != ret_transfer)
561         *ret_transfer = context->num_xfer;
562       WdfRequestSetInformation(request, sizeof(ULONG));
563 
564       // We also must unlock / free transfer MDL
565       MmUnlockPages(context->transfer_mdl);
566       IoFreeMdl(context->transfer_mdl);
567     } else {
568       // For other requests we report transfer size through the request I/O
569       // completion status.
570       WdfRequestSetInformation(request, context->num_xfer);
571     }
572     IoFreeMdl(context->mdl);
573     WdfRequestComplete(request, status);
574     return;
575   }
576 
577   // There are something left for the transfer. Prepare for it.
578   // Required to free any mapping made on the partial MDL and
579   // reset internal MDL state.
580   MmPrepareMdlForReuse(context->mdl);
581 
582   // Update our virtual address
583   context->virtual_address =
584     reinterpret_cast<char*>(context->virtual_address) + bytes_transfered;
585 
586   // Calculate size of this transfer
587   ULONG stage_len =
588     (context->length > GetTransferGranularity()) ? GetTransferGranularity() :
589                                                    context->length;
590 
591   IoBuildPartialMdl(context->transfer_mdl,
592                     context->mdl,
593                     context->virtual_address,
594                     stage_len);
595 
596   // Reinitialize the urb and context
597   urb->UrbBulkOrInterruptTransfer.TransferBufferLength = stage_len;
598   context->transfer_size = stage_len;
599 
600   // Format the request to send a URB to a USB pipe.
601   status = WdfUsbTargetPipeFormatRequestForUrb(wdf_pipe(),
602                                                request,
603                                                context->urb_mem,
604                                                NULL);
605   ASSERT(NT_SUCCESS(status));
606   if (!NT_SUCCESS(status)) {
607     if (context->is_ioctl && !context->is_read) {
608       MmUnlockPages(context->transfer_mdl);
609       IoFreeMdl(context->transfer_mdl);
610     }
611     IoFreeMdl(context->mdl);
612     WdfRequestComplete(request, status);
613     return;
614   }
615 
616   // Reset the completion routine
617   WdfRequestSetCompletionRoutine(request,
618                                  CommonReadWriteCompletionEntry,
619                                  this);
620 
621   // Send the request asynchronously.
622   if (!WdfRequestSend(request, wdf_pipe_io_target(), WDF_NO_SEND_OPTIONS)) {
623     if (context->is_ioctl && !context->is_read) {
624       MmUnlockPages(context->transfer_mdl);
625       IoFreeMdl(context->transfer_mdl);
626     }
627     status = WdfRequestGetStatus(request);
628     IoFreeMdl(context->mdl);
629     WdfRequestComplete(request, status);
630   }
631 }
632 
ResetPipe()633 NTSTATUS AndroidUsbPipeFileObject::ResetPipe() {
634   ASSERT_IRQL_PASSIVE();
635 
636   // This routine synchronously submits a URB_FUNCTION_RESET_PIPE
637   // request down the stack.
638   NTSTATUS status = WdfUsbTargetPipeAbortSynchronously(wdf_pipe(),
639                                                        WDF_NO_HANDLE,
640                                                        NULL);
641   if (NT_SUCCESS(status)) {
642     status = WdfUsbTargetPipeResetSynchronously(wdf_pipe(),
643                                                 WDF_NO_HANDLE,
644                                                 NULL);
645     if (!NT_SUCCESS(status))
646       GoogleDbgPrint("\n!!!!! AndroidUsbPipeFileObject::ResetPipe failed %X", status);
647   } else {
648       GoogleDbgPrint("\n!!!!! WdfUsbTargetPipeAbortSynchronously failed %X", status);
649   }
650 
651   return status;
652 }
653 
QueueResetPipePassiveCallback()654 NTSTATUS AndroidUsbPipeFileObject::QueueResetPipePassiveCallback() {
655   ASSERT_IRQL_LOW_OR_DISPATCH();
656 
657   // Initialize workitem
658   WDF_OBJECT_ATTRIBUTES attr;
659   WDF_OBJECT_ATTRIBUTES_INIT(&attr);
660   WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attr, AndroidUsbWorkitemContext);
661   attr.ParentObject = wdf_device();
662 
663   WDFWORKITEM wdf_work_item = NULL;
664   WDF_WORKITEM_CONFIG workitem_config;
665   WDF_WORKITEM_CONFIG_INIT(&workitem_config, ResetPipePassiveCallbackEntry);
666   NTSTATUS status = WdfWorkItemCreate(&workitem_config,
667                                       &attr,
668                                       &wdf_work_item);
669   ASSERT(NT_SUCCESS(status) && (NULL != wdf_work_item));
670   if (!NT_SUCCESS(status))
671     return status;
672 
673   // Initialize our extension to work item
674   AndroidUsbWorkitemContext* context =
675     GetAndroidUsbWorkitemContext(wdf_work_item);
676   ASSERT(NULL != context);
677   if (NULL == context) {
678     WdfObjectDelete(wdf_work_item);
679     return STATUS_INTERNAL_ERROR;
680   }
681 
682   context->object_type = AndroidUsbWdfObjectTypeWorkitem;
683   context->pipe_file_ext = this;
684 
685   // Enqueue this work item.
686   WdfWorkItemEnqueue(wdf_work_item);
687 
688   return STATUS_SUCCESS;
689 }
690 
CommonReadWriteCompletionEntry(WDFREQUEST request,WDFIOTARGET wdf_target,PWDF_REQUEST_COMPLETION_PARAMS completion_params,WDFCONTEXT completion_context)691 void AndroidUsbPipeFileObject::CommonReadWriteCompletionEntry(
692     WDFREQUEST request,
693     WDFIOTARGET wdf_target,
694     PWDF_REQUEST_COMPLETION_PARAMS completion_params,
695     WDFCONTEXT completion_context) {
696   ASSERT_IRQL_LOW_OR_DISPATCH();
697 
698   AndroidUsbWdfRequestContext*
699     context = GetAndroidUsbWdfRequestContext(request);
700   ASSERT((NULL != context) && (AndroidUsbWdfObjectTypeRequest == context->object_type));
701 
702   AndroidUsbPipeFileObject* pipe_file_ext =
703     reinterpret_cast<AndroidUsbPipeFileObject*>(completion_context);
704   ASSERT((NULL != pipe_file_ext) &&
705          (pipe_file_ext->wdf_pipe() == (WDFUSBPIPE)wdf_target));
706 
707   pipe_file_ext->OnCommonReadWriteCompletion(request,
708                                              completion_params,
709                                              context);
710 }
711 
ResetPipePassiveCallbackEntry(WDFWORKITEM wdf_work_item)712 void AndroidUsbPipeFileObject::ResetPipePassiveCallbackEntry(
713     WDFWORKITEM wdf_work_item) {
714   ASSERT_IRQL_PASSIVE();
715 
716   AndroidUsbWorkitemContext* context =
717     GetAndroidUsbWorkitemContext(wdf_work_item);
718   ASSERT((NULL != context) &&
719          (AndroidUsbWdfObjectTypeWorkitem == context->object_type));
720   if ((NULL == context) ||
721       (AndroidUsbWdfObjectTypeWorkitem != context->object_type)) {
722     WdfObjectDelete(wdf_work_item);
723     return;
724   }
725 
726   // In the sample they reset the device if pipe reset failed
727   AndroidUsbDeviceObject* wdf_device_ext =
728     context->pipe_file_ext->device_object();
729 
730   NTSTATUS status = context->pipe_file_ext->ResetPipe();
731   if (!NT_SUCCESS(status))
732     status = wdf_device_ext->ResetDevice();
733 
734   WdfObjectDelete(wdf_work_item);
735 }
736 
737 #pragma data_seg()
738 #pragma code_seg()
739