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