1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::mem;
6 use std::sync::Arc;
7
8 use super::error::*;
9 use crate::usb::xhci::xhci_transfer::{XhciTransfer, XhciTransferState};
10 use crate::utils::AsyncJobQueue;
11 use crate::utils::FailHandle;
12 use base::{error, warn};
13 use usb_util::{Device, Transfer, TransferStatus};
14
15 /// Helper function to update xhci_transfer state.
update_transfer_state( xhci_transfer: &Arc<XhciTransfer>, usb_transfer: &Transfer, ) -> Result<()>16 pub fn update_transfer_state(
17 xhci_transfer: &Arc<XhciTransfer>,
18 usb_transfer: &Transfer,
19 ) -> Result<()> {
20 let status = usb_transfer.status();
21 let mut state = xhci_transfer.state().lock();
22
23 if status == TransferStatus::Cancelled {
24 *state = XhciTransferState::Cancelled;
25 return Ok(());
26 }
27
28 match *state {
29 XhciTransferState::Cancelling => {
30 *state = XhciTransferState::Cancelled;
31 }
32 XhciTransferState::Submitted { .. } => {
33 *state = XhciTransferState::Completed;
34 }
35 _ => {
36 error!("xhci trasfer state is invalid");
37 *state = XhciTransferState::Completed;
38 return Err(Error::BadXhciTransferState);
39 }
40 }
41 Ok(())
42 }
43
44 /// Helper function to submit usb_transfer to device handle.
submit_transfer( fail_handle: Arc<dyn FailHandle>, job_queue: &Arc<AsyncJobQueue>, xhci_transfer: Arc<XhciTransfer>, device: &mut Device, usb_transfer: Transfer, ) -> Result<()>45 pub fn submit_transfer(
46 fail_handle: Arc<dyn FailHandle>,
47 job_queue: &Arc<AsyncJobQueue>,
48 xhci_transfer: Arc<XhciTransfer>,
49 device: &mut Device,
50 usb_transfer: Transfer,
51 ) -> Result<()> {
52 let transfer_status = {
53 // We need to hold the lock to avoid race condition.
54 // While we are trying to submit the transfer, another thread might want to cancel the same
55 // transfer. Holding the lock here makes sure one of them is cancelled.
56 let mut state = xhci_transfer.state().lock();
57 match mem::replace(&mut *state, XhciTransferState::Cancelled) {
58 XhciTransferState::Created => {
59 match device.submit_transfer(usb_transfer) {
60 Err(e) => {
61 error!("fail to submit transfer {:?}", e);
62 *state = XhciTransferState::Completed;
63 TransferStatus::NoDevice
64 }
65 // If it's submitted, we don't need to send on_transfer_complete now.
66 Ok(canceller) => {
67 let cancel_callback = Box::new(move || match canceller.cancel() {
68 Ok(()) => {
69 usb_debug!("cancel issued to kernel");
70 }
71 Err(e) => {
72 usb_debug!("fail to cancel: {}", e);
73 }
74 });
75 *state = XhciTransferState::Submitted { cancel_callback };
76 return Ok(());
77 }
78 }
79 }
80 XhciTransferState::Cancelled => {
81 warn!("Transfer is already cancelled");
82 TransferStatus::Cancelled
83 }
84 _ => {
85 // The transfer could not be in the following states:
86 // Submitted: A transfer should only be submitted once.
87 // Cancelling: Transfer is cancelling only when it's submitted and someone is
88 // trying to cancel it.
89 // Completed: A completed transfer should not be submitted again.
90 error!("xhci trasfer state is invalid");
91 return Err(Error::BadXhciTransferState);
92 }
93 }
94 };
95 // We are holding locks to of backends, we want to call on_transfer_complete
96 // without any lock.
97 job_queue
98 .queue_job(
99 move || match xhci_transfer.on_transfer_complete(&transfer_status, 0) {
100 Ok(_) => {}
101 Err(e) => {
102 error!("transfer complete failed: {:?}", e);
103 fail_handle.fail();
104 }
105 },
106 )
107 .map_err(Error::QueueAsyncJob)
108 }
109