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