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