• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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