devices/usb/xhci/
xhci_transfer.rs

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
5use std::cmp::min;
6use std::collections::VecDeque;
7use std::fmt;
8use std::fmt::Display;
9use std::mem;
10use std::sync::Arc;
11use std::sync::Weak;
12
13use base::debug;
14use base::error;
15use base::info;
16use base::warn;
17use base::Error as SysError;
18use base::Event;
19use bit_field::Error as BitFieldError;
20use remain::sorted;
21use sync::Mutex;
22use thiserror::Error;
23use usb_util::TransferStatus;
24use usb_util::UsbRequestSetup;
25use vm_memory::GuestMemory;
26use vm_memory::GuestMemoryError;
27
28use super::device_slot::DeviceSlot;
29use super::interrupter::Error as InterrupterError;
30use super::interrupter::Interrupter;
31use super::ring_buffer_stop_cb::RingBufferStopCallback;
32use super::scatter_gather_buffer::Error as BufferError;
33use super::scatter_gather_buffer::ScatterGatherBuffer;
34use super::usb_hub::Error as HubError;
35use super::usb_hub::UsbPort;
36use super::xhci_abi::AddressedTrb;
37use super::xhci_abi::Error as TrbError;
38use super::xhci_abi::EventDataTrb;
39use super::xhci_abi::SetupStageTrb;
40use super::xhci_abi::TransferDescriptor;
41use super::xhci_abi::TrbCast;
42use super::xhci_abi::TrbCompletionCode;
43use super::xhci_abi::TrbType;
44use super::xhci_regs::MAX_INTERRUPTER;
45
46#[sorted]
47#[derive(Error, Debug)]
48pub enum Error {
49    #[error("unexpected trb type: {0:?}")]
50    BadTrbType(TrbType),
51    #[error("failed to cancel transfer")]
52    CancelTransfer,
53    #[error("cannot cast trb: {0}")]
54    CastTrb(TrbError),
55    #[error("cannot create transfer buffer: {0}")]
56    CreateBuffer(BufferError),
57    #[error("cannot detach from port: {0}")]
58    DetachPort(HubError),
59    #[error("failed to halt the endpoint: {0}")]
60    HaltEndpoint(u8),
61    #[error("failed to read guest memory: {0}")]
62    ReadGuestMemory(GuestMemoryError),
63    #[error("cannot send interrupt: {0}")]
64    SendInterrupt(InterrupterError),
65    #[error("failed to submit transfer to backend")]
66    SubmitTransfer,
67    #[error("cannot get transfer length: {0}")]
68    TransferLength(TrbError),
69    #[error("cannot get trb type: {0}")]
70    TrbType(BitFieldError),
71    #[error("cannot write completion event: {0}")]
72    WriteCompletionEvent(SysError),
73    #[error("failed to write guest memory: {0}")]
74    WriteGuestMemory(GuestMemoryError),
75}
76
77type Result<T> = std::result::Result<T, Error>;
78
79/// Type of usb endpoints.
80#[derive(PartialEq, Eq, Clone, Copy, Debug)]
81pub enum TransferDirection {
82    In,
83    Out,
84    Control,
85}
86
87type CancelCallback = Box<dyn FnOnce() -> Result<()> + Send>;
88
89/// Current state of xhci transfer. The transfer in a Submitted or Cancelling state is owned by the
90/// host and should always be reaped to prevent memory leak.
91pub enum XhciTransferState {
92    Created,
93    /// When transfer is submitted, it will contain a transfer callback, which should be invoked
94    /// when the transfer is cancelled.
95    Submitted {
96        cancel_callback: CancelCallback,
97    },
98    Cancelling,
99    Cancelled,
100    Completed,
101}
102
103impl XhciTransferState {
104    /// Try to cancel this transfer, if it's possible.
105    pub fn try_cancel(&mut self, force: bool) -> bool {
106        let mut cancelled = true;
107        match mem::replace(self, XhciTransferState::Created) {
108            XhciTransferState::Submitted { cancel_callback } => {
109                // If we fail to cancel, there are two cases: the URB has already completed
110                // (EINVAL) or the device is gone (ENODEV). For both cases, we put back the state
111                // to Submitted and check the URB status in the completion handler, to report the
112                // already completed one properly to the guest. However, we can't do that once we
113                // have cancelled a preceding request, because the request must be processed in the
114                // order of submission.
115                match cancel_callback() {
116                    Ok(()) => {
117                        *self = XhciTransferState::Cancelling;
118                    }
119                    Err(_e) => {
120                        if force {
121                            *self = XhciTransferState::Cancelling;
122                        } else {
123                            let error_callback = Box::new(move || Err(Error::CancelTransfer));
124                            *self = XhciTransferState::Submitted {
125                                cancel_callback: error_callback,
126                            };
127                            cancelled = false;
128                        }
129                    }
130                }
131            }
132            XhciTransferState::Cancelling => {
133                error!("Another cancellation is already issued.");
134                *self = XhciTransferState::Cancelling;
135            }
136            _ => {
137                *self = XhciTransferState::Cancelled;
138            }
139        }
140        cancelled
141    }
142}
143
144/// Type of a transfer received handled by transfer ring.
145pub enum XhciTransferType {
146    // Normal means bulk transfer or interrupt transfer, depending on endpoint type.
147    // See spec 4.11.2.1.
148    Normal,
149    // See usb spec for setup stage, data stage and status stage,
150    // see xHCI spec 4.11.2.2 for corresponding trbs.
151    SetupStage,
152    DataStage,
153    StatusStage,
154    // See xHCI spec 4.11.2.3.
155    Isochronous,
156    // See xHCI spec 6.4.1.4.
157    Noop,
158}
159
160impl Display for XhciTransferType {
161    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162        use self::XhciTransferType::*;
163
164        match self {
165            Normal => write!(f, "Normal"),
166            SetupStage => write!(f, "SetupStage"),
167            DataStage => write!(f, "DataStage"),
168            StatusStage => write!(f, "StatusStage"),
169            Isochronous => write!(f, "Isochronous"),
170            Noop => write!(f, "Noop"),
171        }
172    }
173}
174
175/// Xhci Transfer manager holds reference to all ongoing transfers. Can cancel them all if
176/// needed.
177#[derive(Clone)]
178pub struct XhciTransferManager {
179    transfers: Arc<Mutex<VecDeque<Weak<Mutex<XhciTransferState>>>>>,
180    device_slot: Weak<DeviceSlot>,
181    stop_callback: Arc<Mutex<Vec<RingBufferStopCallback>>>,
182}
183
184impl XhciTransferManager {
185    /// Create a new manager.
186    pub fn new(device_slot: Weak<DeviceSlot>) -> XhciTransferManager {
187        XhciTransferManager {
188            transfers: Arc::new(Mutex::new(VecDeque::new())),
189            device_slot,
190            stop_callback: Arc::new(Mutex::new(Vec::new())),
191        }
192    }
193
194    /// Build a new XhciTransfer. Endpoint id is the id in xHCI device slot.
195    pub fn create_transfer(
196        &self,
197        mem: GuestMemory,
198        port: Arc<UsbPort>,
199        interrupter: Arc<Mutex<Interrupter>>,
200        slot_id: u8,
201        endpoint_id: u8,
202        transfer_descriptor: TransferDescriptor,
203        completion_event: Event,
204        stream_id: Option<u16>,
205    ) -> XhciTransfer {
206        let transfer_dir = {
207            if endpoint_id == 1 {
208                TransferDirection::Control
209            } else if (endpoint_id % 2) == 0 {
210                TransferDirection::Out
211            } else {
212                TransferDirection::In
213            }
214        };
215        let t = XhciTransfer {
216            manager: self.clone(),
217            state: Arc::new(Mutex::new(XhciTransferState::Created)),
218            mem,
219            port,
220            interrupter,
221            transfer_completion_event: completion_event,
222            slot_id,
223            endpoint_id,
224            transfer_dir,
225            transfer_descriptor,
226            device_slot: self.device_slot.clone(),
227            stream_id,
228        };
229        self.transfers.lock().push_back(Arc::downgrade(&t.state));
230        t
231    }
232
233    /// Cancel all current transfers and execute the callback once completed.
234    pub fn cancel_all(&self, callback: RingBufferStopCallback) {
235        let locked_transfers = self.transfers.lock();
236        if !locked_transfers.is_empty() {
237            self.stop_callback.lock().push(callback);
238        }
239
240        let mut force_cancel = false;
241        locked_transfers.iter().for_each(|t| {
242            let state = match t.upgrade() {
243                Some(state) => state,
244                None => {
245                    error!("transfer is already cancelled or finished");
246                    return;
247                }
248            };
249            force_cancel |= state.lock().try_cancel(force_cancel);
250        });
251    }
252
253    fn remove_transfer(&self, t: &Arc<Mutex<XhciTransferState>>) {
254        let mut transfers = self.transfers.lock();
255        match transfers.iter().position(|wt| match wt.upgrade() {
256            Some(wt) => Arc::ptr_eq(&wt, t),
257            None => false,
258        }) {
259            None => error!("attempted to remove unknown transfer"),
260            Some(i) => {
261                transfers.remove(i);
262            }
263        }
264        if transfers.is_empty() {
265            self.stop_callback.lock().clear();
266        }
267    }
268}
269
270impl Default for XhciTransferManager {
271    fn default() -> Self {
272        Self::new(Weak::new())
273    }
274}
275
276/// Xhci transfer denotes a transfer initiated by guest os driver. It will be submitted to a
277/// XhciBackendDevice.
278pub struct XhciTransfer {
279    manager: XhciTransferManager,
280    state: Arc<Mutex<XhciTransferState>>,
281    mem: GuestMemory,
282    port: Arc<UsbPort>,
283    interrupter: Arc<Mutex<Interrupter>>,
284    slot_id: u8,
285    // id of endpoint in device slot.
286    endpoint_id: u8,
287    transfer_dir: TransferDirection,
288    transfer_descriptor: TransferDescriptor,
289    transfer_completion_event: Event,
290    device_slot: Weak<DeviceSlot>,
291    stream_id: Option<u16>,
292}
293
294impl Drop for XhciTransfer {
295    fn drop(&mut self) {
296        self.manager.remove_transfer(&self.state);
297    }
298}
299
300impl fmt::Debug for XhciTransfer {
301    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302        write!(
303            f,
304            "xhci_transfer slot id: {}, endpoint id {}, transfer_dir {:?}, transfer_descriptor {:?}",
305            self.slot_id, self.endpoint_id, self.transfer_dir, self.transfer_descriptor
306        )
307    }
308}
309
310#[derive(Debug, PartialEq, Clone, Copy)]
311enum TransferAction {
312    HaltEndpoint,
313    SendEvent {
314        code: TrbCompletionCode,
315        gpa: u64,
316        residual_or_edtla: u32,
317        event_data: bool,
318    },
319}
320
321impl XhciTransfer {
322    /// Get state of this transfer.
323    pub fn state(&self) -> &Arc<Mutex<XhciTransferState>> {
324        &self.state
325    }
326
327    /// Get transfer type.
328    pub fn get_transfer_type(&self) -> Result<XhciTransferType> {
329        // We can figure out transfer type from the first trb.
330        // See transfer descriptor description in xhci spec for more details.
331        match self
332            .transfer_descriptor
333            .first_atrb()
334            .trb
335            .get_trb_type()
336            .map_err(Error::TrbType)?
337        {
338            TrbType::Normal => Ok(XhciTransferType::Normal),
339            TrbType::SetupStage => Ok(XhciTransferType::SetupStage),
340            TrbType::DataStage => Ok(XhciTransferType::DataStage),
341            TrbType::StatusStage => Ok(XhciTransferType::StatusStage),
342            TrbType::Isoch => Ok(XhciTransferType::Isochronous),
343            TrbType::Noop => Ok(XhciTransferType::Noop),
344            t => Err(Error::BadTrbType(t)),
345        }
346    }
347
348    /// Create a scatter gather buffer for the given xhci transfer
349    pub fn create_buffer(&self) -> Result<ScatterGatherBuffer> {
350        ScatterGatherBuffer::new(self.mem.clone(), self.transfer_descriptor.clone())
351            .map_err(Error::CreateBuffer)
352    }
353
354    /// Create a usb request setup for the control transfer buffer
355    pub fn create_usb_request_setup(&self) -> Result<UsbRequestSetup> {
356        let first_atrb = self.transfer_descriptor.first_atrb();
357        let trb = first_atrb
358            .trb
359            .checked_cast::<SetupStageTrb>()
360            .map_err(Error::CastTrb)?;
361        Ok(UsbRequestSetup::new(
362            trb.get_request_type(),
363            trb.get_request(),
364            trb.get_value(),
365            trb.get_index(),
366            trb.get_length(),
367        ))
368    }
369
370    /// Get endpoint number.
371    pub fn get_endpoint_number(&self) -> u8 {
372        // See spec 4.5.1 for dci.
373        self.endpoint_id / 2
374    }
375
376    /// get transfer direction.
377    pub fn get_transfer_dir(&self) -> TransferDirection {
378        self.transfer_dir
379    }
380
381    /// get stream id.
382    pub fn get_stream_id(&self) -> Option<u16> {
383        self.stream_id
384    }
385
386    fn process_td_results(
387        &self,
388        status: &TransferStatus,
389        bytes_transferred: u32,
390    ) -> Result<Vec<TransferAction>> {
391        let mut actions = Vec::new();
392        if *status == TransferStatus::Stalled {
393            warn!("xhci: endpoint is stalled. set state to Halted");
394            actions.push(TransferAction::HaltEndpoint);
395        }
396
397        let mut edtla: u32 = 0;
398        let mut remaining_transferred = bytes_transferred;
399        let mut retiring_on_short: bool = false;
400        let mut residual_on_short: u32 = 0;
401        let last_atrb_gpa = self.transfer_descriptor.last_atrb().gpa;
402
403        // As noted in xHCI spec 4.11.3.1
404        // Transfer Event TRB only occurs under the following conditions:
405        //   1. If the Interrupt On Completion flag is set.
406        //   2. When a short transfer occurs during the execution of a Transfer TRB and the
407        //      Interrupt-on-Short Packet flag is set.
408        //   3. If an error occurs during the execution of a Transfer TRB.
409        for atrb in &self.transfer_descriptor {
410            // For details about event data trb and EDTLA, see spec 4.11.5.2.
411            if atrb.trb.get_trb_type().map_err(Error::TrbType)? == TrbType::EventData {
412                let code = if retiring_on_short {
413                    TrbCompletionCode::ShortPacket
414                } else {
415                    TrbCompletionCode::Success
416                };
417                actions.push(TransferAction::SendEvent {
418                    code,
419                    gpa: atrb
420                        .trb
421                        .cast::<EventDataTrb>()
422                        .map_err(Error::CastTrb)?
423                        .get_event_data(),
424                    residual_or_edtla: edtla,
425                    event_data: true,
426                });
427                edtla = 0;
428                self.report_completion(atrb);
429                continue;
430            }
431
432            let length = atrb.trb.transfer_length().map_err(Error::TransferLength)?;
433            let transferred = min(length, remaining_transferred);
434            remaining_transferred -= transferred;
435
436            let residual = length - transferred;
437            edtla += transferred;
438
439            // Report StallError for the TRB with residual data or the last TRB of the TD.
440            // The latter condition covers a stall on the Status Stage. The TRB that caused the
441            // stall is considered as not completed.
442            if *status == TransferStatus::Stalled && (residual > 0 || atrb.gpa == last_atrb_gpa) {
443                debug!("xhci: on transfer complete stalled");
444                actions.push(TransferAction::SendEvent {
445                    code: TrbCompletionCode::StallError,
446                    gpa: atrb.gpa,
447                    residual_or_edtla: residual,
448                    event_data: false,
449                });
450                break;
451            }
452
453            // If Short Packet is detected, the rest of the TRBs in the same TD are not executed.
454            // However, events are still generated for the EventData TRBs (handled above) or other
455            // TRBs with IOC (handled below).
456            if retiring_on_short {
457                if atrb.trb.interrupt_on_completion() {
458                    actions.push(TransferAction::SendEvent {
459                        code: TrbCompletionCode::ShortPacket,
460                        gpa: atrb.gpa,
461                        residual_or_edtla: residual_on_short,
462                        event_data: false,
463                    });
464                }
465            } else if residual > 0 {
466                retiring_on_short = true;
467                residual_on_short = residual;
468                if atrb.trb.interrupt_on_completion() || atrb.trb.interrupt_on_short_packet() {
469                    debug!("xhci: on transfer complete short packet");
470                    actions.push(TransferAction::SendEvent {
471                        code: TrbCompletionCode::ShortPacket,
472                        gpa: atrb.gpa,
473                        residual_or_edtla: residual,
474                        event_data: false,
475                    });
476                }
477            } else if atrb.trb.interrupt_on_completion() {
478                debug!("xhci: on transfer complete success");
479                actions.push(TransferAction::SendEvent {
480                    code: TrbCompletionCode::Success,
481                    gpa: atrb.gpa,
482                    residual_or_edtla: 0,
483                    event_data: false,
484                });
485            }
486
487            // The dequeue pointer still needs to be advanced after a Short Packet.
488            self.report_completion(atrb);
489        }
490        Ok(actions)
491    }
492
493    fn report_completion(&self, trb: &AddressedTrb) {
494        if let Some(device_slot) = self.device_slot.upgrade() {
495            device_slot.report_trb_completion(self.endpoint_id, self.stream_id, trb);
496        }
497    }
498
499    /// This functions should be invoked when transfer is completed (or failed).
500    pub fn on_transfer_complete(
501        &self,
502        status: &TransferStatus,
503        bytes_transferred: u32,
504    ) -> Result<()> {
505        match status {
506            TransferStatus::NoDevice => {
507                info!("xhci: device disconnected, detaching from port");
508                // Actual port detachment is handled by the UsbUtilEventHandler.
509                return self
510                    .transfer_completion_event
511                    .signal()
512                    .map_err(Error::WriteCompletionEvent);
513            }
514            TransferStatus::Cancelled => {
515                return self
516                    .transfer_completion_event
517                    .signal()
518                    .map_err(Error::WriteCompletionEvent);
519            }
520            TransferStatus::Completed => {}
521            TransferStatus::Stalled => {
522                // This is not a critical error, especially during the enumeration. Some devices
523                // takes time to become ready and may return StallError until then. A mass storage
524                // may also return StallError on checking write-protection.
525            }
526            TransferStatus::Error => {
527                // Transfer failed, we are not handling this correctly yet. Guest kernel might see
528                // short packets for in transfer and might think control transfer is successful. It
529                // will eventually find out device is in a wrong state.
530            }
531        }
532
533        let mut halted = false;
534        let actions = self.process_td_results(status, bytes_transferred)?;
535        for action in actions {
536            match action {
537                TransferAction::SendEvent {
538                    code,
539                    gpa,
540                    residual_or_edtla,
541                    event_data,
542                } => {
543                    self.interrupter
544                        .lock()
545                        .send_transfer_event_trb(
546                            code,
547                            gpa,
548                            residual_or_edtla,
549                            event_data,
550                            self.slot_id,
551                            self.endpoint_id,
552                        )
553                        .map_err(Error::SendInterrupt)?;
554                }
555                TransferAction::HaltEndpoint => {
556                    if let Some(device_slot) = self.device_slot.upgrade() {
557                        device_slot
558                            .halt_endpoint(self.endpoint_id)
559                            .map_err(|_| Error::HaltEndpoint(self.endpoint_id))?;
560                        halted = true;
561                    }
562                }
563            }
564        }
565
566        // Since the event loop is single threaded, there's no need to trigger it early. We delay
567        // it to the end so that its error, if it ever occurs, won't cause the above transfer
568        // events to be omitted.
569        if !halted {
570            self.transfer_completion_event
571                .signal()
572                .map_err(Error::WriteCompletionEvent)
573        } else {
574            Ok(())
575        }
576    }
577
578    /// Send this transfer to backend if it's a valid transfer.
579    pub fn send_to_backend_if_valid(self) -> Result<()> {
580        if self.validate_transfer()? {
581            // Backend should invoke on transfer complete when transfer is completed.
582            let port = self.port.clone();
583            let mut backend = port.backend_device();
584            match &mut *backend {
585                Some(backend) => backend
586                    .lock()
587                    .submit_xhci_transfer(self)
588                    .map_err(|_| Error::SubmitTransfer)?,
589                None => {
590                    error!("backend is already disconnected");
591                    self.transfer_completion_event
592                        .signal()
593                        .map_err(Error::WriteCompletionEvent)?;
594                }
595            }
596        } else {
597            error!("invalid td on transfer ring");
598            self.transfer_completion_event
599                .signal()
600                .map_err(Error::WriteCompletionEvent)?;
601        }
602        Ok(())
603    }
604
605    // Check each trb in the transfer descriptor for invalid or out of bounds
606    // parameters. Returns true iff the transfer descriptor is valid.
607    fn validate_transfer(&self) -> Result<bool> {
608        let mut valid = true;
609        for atrb in &self.transfer_descriptor {
610            if !trb_is_valid(atrb) {
611                self.interrupter
612                    .lock()
613                    .send_transfer_event_trb(
614                        TrbCompletionCode::TrbError,
615                        atrb.gpa,
616                        0,
617                        false,
618                        self.slot_id,
619                        self.endpoint_id,
620                    )
621                    .map_err(Error::SendInterrupt)?;
622                valid = false;
623            }
624        }
625        Ok(valid)
626    }
627}
628
629fn trb_is_valid(atrb: &AddressedTrb) -> bool {
630    let can_be_in_transfer_ring = match atrb.trb.can_be_in_transfer_ring() {
631        Ok(v) => v,
632        Err(e) => {
633            error!("unknown error {:?}", e);
634            return false;
635        }
636    };
637    can_be_in_transfer_ring && (atrb.trb.interrupter_target() < MAX_INTERRUPTER)
638}
639
640#[cfg(test)]
641mod tests {
642    use base::pagesize;
643    use vm_memory::GuestAddress;
644
645    use super::*;
646    use crate::usb::xhci::xhci_abi::NormalTrb;
647    use crate::usb::xhci::xhci_abi::StatusStageTrb;
648    use crate::usb::xhci::xhci_abi::Trb;
649    use crate::usb::xhci::xhci_backend_device::BackendType;
650    use crate::usb::xhci::XhciRegs;
651
652    fn create_test_transfer(trbs: Vec<Trb>) -> XhciTransfer {
653        let mem = GuestMemory::new(&[(GuestAddress(0), pagesize() as u64)]).unwrap();
654        let mut gpa = 0x100;
655        let mut atrbs = Vec::new();
656        for trb in trbs {
657            mem.write_obj_at_addr(trb, GuestAddress(gpa)).unwrap();
658            atrbs.push(AddressedTrb { trb, gpa });
659            gpa += 16;
660        }
661
662        let td = TransferDescriptor::new(atrbs).unwrap();
663        let manager = XhciTransferManager::new(Weak::new());
664
665        let test_reg32 = register!(
666            name: "test",
667            ty: u32,
668            offset: 0x0,
669            reset_value: 0,
670            guest_writeable_mask: 0x0,
671            guest_write_1_to_clear_mask: 0,
672        );
673        let test_reg64 = register!(
674            name: "test",
675            ty: u64,
676            offset: 0x0,
677            reset_value: 0,
678            guest_writeable_mask: 0x0,
679            guest_write_1_to_clear_mask: 0,
680        );
681        let xhci_regs = XhciRegs {
682            usbcmd: test_reg32.clone(),
683            usbsts: test_reg32.clone(),
684            dnctrl: test_reg32.clone(),
685            crcr: test_reg64.clone(),
686            dcbaap: test_reg64.clone(),
687            config: test_reg64.clone(),
688            portsc: vec![test_reg32.clone(); 16],
689            doorbells: Vec::new(),
690            iman: test_reg32.clone(),
691            imod: test_reg32.clone(),
692            erstsz: test_reg32.clone(),
693            erstba: test_reg64.clone(),
694            erdp: test_reg64.clone(),
695        };
696
697        XhciTransfer {
698            manager,
699            state: Arc::new(Mutex::new(XhciTransferState::Created)),
700            mem,
701            port: Arc::new(UsbPort::new(
702                BackendType::Usb2,
703                1,
704                test_reg32.clone(),
705                test_reg32.clone(),
706                Arc::new(Mutex::new(Interrupter::new(
707                    GuestMemory::new(&[]).unwrap(),
708                    Event::new().unwrap(),
709                    &xhci_regs,
710                ))),
711            )),
712            interrupter: Arc::new(Mutex::new(Interrupter::new(
713                GuestMemory::new(&[]).unwrap(),
714                Event::new().unwrap(),
715                &xhci_regs,
716            ))),
717            transfer_completion_event: Event::new().unwrap(),
718            slot_id: 1,
719            endpoint_id: 2,
720            transfer_dir: TransferDirection::Out,
721            transfer_descriptor: td,
722            device_slot: Weak::new(),
723            stream_id: None,
724        }
725    }
726
727    #[test]
728    fn test_bulk_success() {
729        let mut trb = Trb::new();
730        let normal_trb = trb.cast_mut::<NormalTrb>().unwrap();
731        normal_trb.set_trb_type(TrbType::Normal);
732        normal_trb.set_trb_transfer_length(100);
733        normal_trb.set_interrupt_on_completion(1);
734        let transfer = create_test_transfer(vec![trb]);
735
736        let actions = transfer
737            .process_td_results(&TransferStatus::Completed, 100)
738            .unwrap();
739        assert_eq!(
740            actions,
741            vec![TransferAction::SendEvent {
742                code: TrbCompletionCode::Success,
743                gpa: 0x100,
744                residual_or_edtla: 0,
745                event_data: false,
746            }]
747        );
748    }
749
750    #[test]
751    fn test_bulk_short_with_isp() {
752        // xHCI 4.10.1.1 Short Transfers states that an event should be generated if ISP or IOC is
753        // set to 1.
754        let mut trb = Trb::new();
755        let normal_trb = trb.cast_mut::<NormalTrb>().unwrap();
756        normal_trb.set_trb_type(TrbType::Normal);
757        normal_trb.set_trb_transfer_length(100);
758        normal_trb.set_interrupt_on_short_packet(1);
759        let transfer = create_test_transfer(vec![trb]);
760
761        let actions = transfer
762            .process_td_results(&TransferStatus::Completed, 40)
763            .unwrap();
764        assert_eq!(
765            actions,
766            vec![TransferAction::SendEvent {
767                code: TrbCompletionCode::ShortPacket,
768                gpa: 0x100,
769                residual_or_edtla: 60,
770                event_data: false,
771            }]
772        );
773    }
774
775    #[test]
776    fn test_bulk_short_with_ioc() {
777        // xHCI 4.10.1.1 Short Transfers states that an event should be generated if ISP or IOC is
778        // set to 1.
779        let mut trb = Trb::new();
780        let normal_trb = trb.cast_mut::<NormalTrb>().unwrap();
781        normal_trb.set_trb_type(TrbType::Normal);
782        normal_trb.set_trb_transfer_length(100);
783        normal_trb.set_interrupt_on_completion(1);
784        let transfer = create_test_transfer(vec![trb]);
785
786        let actions = transfer
787            .process_td_results(&TransferStatus::Completed, 40)
788            .unwrap();
789        assert_eq!(
790            actions,
791            vec![TransferAction::SendEvent {
792                code: TrbCompletionCode::ShortPacket,
793                gpa: 0x100,
794                residual_or_edtla: 60,
795                event_data: false,
796            }]
797        );
798    }
799
800    #[test]
801    fn test_bulk_without_evendata_retiring_after_short() {
802        // xHCI 4.9.1 Transfer Descriptors states that the TD should retire after detecting a short
803        // packet condition, but xHCI 4.10.1.1 Short Transfers states it should still generate an
804        // event for a TRB with IOC.
805        let mut trb1 = Trb::new();
806        let normal_trb1 = trb1.cast_mut::<NormalTrb>().unwrap();
807        normal_trb1.set_trb_type(TrbType::Normal);
808        normal_trb1.set_trb_transfer_length(100);
809        normal_trb1.set_interrupt_on_short_packet(1);
810
811        let mut trb2 = Trb::new();
812        let normal_trb2 = trb2.cast_mut::<NormalTrb>().unwrap();
813        normal_trb2.set_trb_type(TrbType::Normal);
814        normal_trb2.set_trb_transfer_length(100);
815        normal_trb2.set_interrupt_on_completion(1);
816
817        let mut trb3 = Trb::new();
818        let normal_trb3 = trb3.cast_mut::<NormalTrb>().unwrap();
819        normal_trb3.set_trb_type(TrbType::Normal);
820        normal_trb3.set_trb_transfer_length(100);
821        normal_trb3.set_interrupt_on_completion(1);
822
823        let transfer = create_test_transfer(vec![trb1, trb2, trb3]);
824
825        let actions = transfer
826            .process_td_results(&TransferStatus::Completed, 40)
827            .unwrap();
828        assert_eq!(
829            actions,
830            vec![
831                TransferAction::SendEvent {
832                    code: TrbCompletionCode::ShortPacket,
833                    gpa: 0x100,
834                    residual_or_edtla: 60,
835                    event_data: false,
836                },
837                TransferAction::SendEvent {
838                    code: TrbCompletionCode::ShortPacket,
839                    gpa: 0x110,
840                    residual_or_edtla: 60,
841                    event_data: false,
842                },
843                TransferAction::SendEvent {
844                    code: TrbCompletionCode::ShortPacket,
845                    gpa: 0x120,
846                    residual_or_edtla: 60,
847                    event_data: false,
848                },
849            ]
850        );
851    }
852
853    #[test]
854    fn test_bulk_with_evendata_retiring_after_short() {
855        // xHCI 4.9.1 Transfer Descriptors states that the TD should retire after detecting a short
856        // packet condition, but xHCI 4.10.1.1 Short Transfers states it should still generate an
857        // event for EventData TRB. This test assumes that we have PAE=1 in HCCPARAMS1 that forces
858        // all the EventData TRBs to generate an event even after the Short Packet.
859        let mut trb1 = Trb::new();
860        let normal_trb1 = trb1.cast_mut::<NormalTrb>().unwrap();
861        normal_trb1.set_trb_type(TrbType::Normal);
862        normal_trb1.set_trb_transfer_length(100);
863        normal_trb1.set_interrupt_on_short_packet(1);
864
865        let mut trb2 = Trb::new();
866        let event_trb = trb2.cast_mut::<EventDataTrb>().unwrap();
867        event_trb.set_trb_type(TrbType::EventData);
868        event_trb.set_event_data(0x12345678abcdef0);
869        event_trb.set_interrupt_on_completion(1);
870
871        let mut trb3 = Trb::new();
872        let event_trb = trb3.cast_mut::<EventDataTrb>().unwrap();
873        event_trb.set_trb_type(TrbType::EventData);
874        event_trb.set_event_data(0x12345678abcdef1);
875        event_trb.set_interrupt_on_completion(1);
876
877        let transfer = create_test_transfer(vec![trb1, trb2, trb3]);
878
879        let actions = transfer
880            .process_td_results(&TransferStatus::Completed, 40)
881            .unwrap();
882        assert_eq!(
883            actions,
884            vec![
885                TransferAction::SendEvent {
886                    code: TrbCompletionCode::ShortPacket,
887                    gpa: 0x100,
888                    residual_or_edtla: 60,
889                    event_data: false,
890                },
891                TransferAction::SendEvent {
892                    code: TrbCompletionCode::ShortPacket,
893                    gpa: 0x12345678abcdef0,
894                    residual_or_edtla: 40, // EDTLA
895                    event_data: true,
896                },
897                TransferAction::SendEvent {
898                    code: TrbCompletionCode::ShortPacket,
899                    gpa: 0x12345678abcdef1,
900                    residual_or_edtla: 0, // EDTLA
901                    event_data: true,
902                },
903            ]
904        );
905    }
906
907    #[test]
908    fn test_bulk_stall_partial() {
909        // xHCI 4.10.2 Errors state that an error during a transfer shall always generate an event,
910        // irrespective of whether ISP or IOC is set to 1. Also, 4.10.2.1 Stall Error states that
911        // the endpoint state should transition to Halted.
912        let mut trb = Trb::new();
913        let normal_trb = trb.cast_mut::<NormalTrb>().unwrap();
914        normal_trb.set_trb_type(TrbType::Normal);
915        normal_trb.set_trb_transfer_length(100);
916        let transfer = create_test_transfer(vec![trb]);
917
918        // Stall at 40 bytes.
919        let actions = transfer
920            .process_td_results(&TransferStatus::Stalled, 40)
921            .unwrap();
922        assert_eq!(
923            actions,
924            vec![
925                TransferAction::HaltEndpoint,
926                TransferAction::SendEvent {
927                    code: TrbCompletionCode::StallError,
928                    gpa: 0x100,
929                    residual_or_edtla: 60,
930                    event_data: false,
931                }
932            ]
933        );
934    }
935
936    #[test]
937    fn test_control_stall_no_data_stage() {
938        let mut trb = Trb::new();
939        let status_trb = trb.cast_mut::<StatusStageTrb>().unwrap();
940        status_trb.set_trb_type(TrbType::StatusStage);
941        status_trb.set_interrupt_on_completion(1);
942
943        let transfer = create_test_transfer(vec![trb]);
944
945        // Stall at Status Stage (length 0). bytes_transferred = 0.
946        let actions = transfer
947            .process_td_results(&TransferStatus::Stalled, 0)
948            .unwrap();
949
950        assert_eq!(
951            actions,
952            vec![
953                TransferAction::HaltEndpoint,
954                TransferAction::SendEvent {
955                    code: TrbCompletionCode::StallError,
956                    gpa: 0x100,
957                    residual_or_edtla: 0,
958                    event_data: false,
959                }
960            ]
961        );
962    }
963
964    #[test]
965    fn test_bulk_stall_at_trb_start() {
966        // xHCI 6.4.2.1 Transfer Event TRB states that if an error occurs during a transfer TRB,
967        // the event TRB shall point to the offending TRB.
968        let mut trb1 = Trb::new();
969        let normal_trb1 = trb1.cast_mut::<NormalTrb>().unwrap();
970        normal_trb1.set_trb_type(TrbType::Normal);
971        normal_trb1.set_trb_transfer_length(100);
972
973        let mut trb2 = Trb::new();
974        let normal_trb2 = trb2.cast_mut::<NormalTrb>().unwrap();
975        normal_trb2.set_trb_type(TrbType::Normal);
976        normal_trb2.set_trb_transfer_length(100);
977
978        let transfer = create_test_transfer(vec![trb1, trb2]);
979
980        // Stall after 100 bytes (immediately when the second TRB is started).
981        let actions = transfer
982            .process_td_results(&TransferStatus::Stalled, 100)
983            .unwrap();
984
985        assert_eq!(
986            actions,
987            vec![
988                TransferAction::HaltEndpoint,
989                TransferAction::SendEvent {
990                    code: TrbCompletionCode::StallError,
991                    gpa: 0x110, // Second TRB GPA
992                    residual_or_edtla: 100,
993                    event_data: false,
994                }
995            ]
996        );
997    }
998
999    #[test]
1000    fn test_event_data_single() {
1001        // xHCI 4.11.5.2 Event Data TRB states that the event should report EDTLA and set Event
1002        // Data (ED) field to 1.
1003        let mut trb1 = Trb::new();
1004        let normal_trb = trb1.cast_mut::<NormalTrb>().unwrap();
1005        normal_trb.set_trb_type(TrbType::Normal);
1006        normal_trb.set_trb_transfer_length(100);
1007
1008        let mut trb2 = Trb::new();
1009        let event_trb = trb2.cast_mut::<EventDataTrb>().unwrap();
1010        event_trb.set_trb_type(TrbType::EventData);
1011        event_trb.set_event_data(0x12345678abcdef0);
1012        event_trb.set_interrupt_on_completion(1);
1013
1014        let transfer = create_test_transfer(vec![trb1, trb2]);
1015
1016        // Successful transfer.
1017        let actions = transfer
1018            .process_td_results(&TransferStatus::Completed, 100)
1019            .unwrap();
1020        assert_eq!(
1021            actions,
1022            vec![TransferAction::SendEvent {
1023                code: TrbCompletionCode::Success,
1024                gpa: 0x12345678abcdef0,
1025                residual_or_edtla: 100,
1026                event_data: true,
1027            }]
1028        );
1029
1030        // Short packet.
1031        let actions = transfer
1032            .process_td_results(&TransferStatus::Completed, 40)
1033            .unwrap();
1034        assert_eq!(
1035            actions,
1036            vec![TransferAction::SendEvent {
1037                code: TrbCompletionCode::ShortPacket,
1038                gpa: 0x12345678abcdef0,
1039                residual_or_edtla: 40,
1040                event_data: true,
1041            }]
1042        );
1043    }
1044
1045    #[test]
1046    fn test_event_data_multiple() {
1047        // xHCI 4.11.5.2 Event Data TRB states that EDTLA should be cleared to 0 when an EventData
1048        // TRB is encountered.
1049        let mut trb1 = Trb::new();
1050        let normal_trb = trb1.cast_mut::<NormalTrb>().unwrap();
1051        normal_trb.set_trb_type(TrbType::Normal);
1052        normal_trb.set_trb_transfer_length(100);
1053        normal_trb.set_interrupt_on_short_packet(1);
1054
1055        let mut trb2 = Trb::new();
1056        let event_trb = trb2.cast_mut::<EventDataTrb>().unwrap();
1057        event_trb.set_trb_type(TrbType::EventData);
1058        event_trb.set_event_data(0x12345678abcdef0);
1059        event_trb.set_interrupt_on_completion(1);
1060
1061        let transfer = create_test_transfer(vec![trb1, trb2, trb1, trb2]);
1062
1063        // Successful transfer.
1064        let actions = transfer
1065            .process_td_results(&TransferStatus::Completed, 200)
1066            .unwrap();
1067        assert_eq!(
1068            actions,
1069            vec![
1070                TransferAction::SendEvent {
1071                    code: TrbCompletionCode::Success,
1072                    gpa: 0x12345678abcdef0,
1073                    residual_or_edtla: 100, // EDTLA
1074                    event_data: true,
1075                },
1076                TransferAction::SendEvent {
1077                    code: TrbCompletionCode::Success,
1078                    gpa: 0x12345678abcdef0,
1079                    residual_or_edtla: 100, // EDTLA
1080                    event_data: true,
1081                },
1082            ]
1083        );
1084
1085        // Short packet. xHCI 5.3.6 Capability Parameters 1 (HCCPARAMS1) states that if Parse All
1086        // Event Data (PAE) field is 1, then it should parse all the EventData TRBs while advancing
1087        // to the next TD after a Short Packet. See also 4.10.1.1 Short Transfers.
1088        let actions = transfer
1089            .process_td_results(&TransferStatus::Completed, 40)
1090            .unwrap();
1091        assert_eq!(
1092            actions,
1093            vec![
1094                TransferAction::SendEvent {
1095                    code: TrbCompletionCode::ShortPacket,
1096                    gpa: 0x100,
1097                    residual_or_edtla: 60, // residual
1098                    event_data: false,
1099                },
1100                TransferAction::SendEvent {
1101                    code: TrbCompletionCode::ShortPacket,
1102                    gpa: 0x12345678abcdef0,
1103                    residual_or_edtla: 40, // EDTLA
1104                    event_data: true,
1105                },
1106                TransferAction::SendEvent {
1107                    code: TrbCompletionCode::ShortPacket,
1108                    gpa: 0x12345678abcdef0,
1109                    residual_or_edtla: 0, // EDTLA (from last Event)
1110                    event_data: true,
1111                },
1112            ]
1113        );
1114    }
1115}