Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/usb/xhci/xhci_transfer.rs
5394 views
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::cmp::min;
6
use std::fmt;
7
use std::fmt::Display;
8
use std::mem;
9
use std::sync::Arc;
10
use std::sync::Weak;
11
12
use base::debug;
13
use base::error;
14
use base::info;
15
use base::warn;
16
use base::Error as SysError;
17
use base::Event;
18
use bit_field::Error as BitFieldError;
19
use remain::sorted;
20
use sync::Mutex;
21
use thiserror::Error;
22
use usb_util::TransferStatus;
23
use usb_util::UsbRequestSetup;
24
use vm_memory::GuestMemory;
25
use vm_memory::GuestMemoryError;
26
27
use super::device_slot::DeviceSlot;
28
use super::interrupter::Error as InterrupterError;
29
use super::interrupter::Interrupter;
30
use super::scatter_gather_buffer::Error as BufferError;
31
use super::scatter_gather_buffer::ScatterGatherBuffer;
32
use super::usb_hub::Error as HubError;
33
use super::usb_hub::UsbPort;
34
use super::xhci_abi::AddressedTrb;
35
use super::xhci_abi::Error as TrbError;
36
use super::xhci_abi::EventDataTrb;
37
use super::xhci_abi::SetupStageTrb;
38
use super::xhci_abi::TransferDescriptor;
39
use super::xhci_abi::TrbCast;
40
use super::xhci_abi::TrbCompletionCode;
41
use super::xhci_abi::TrbType;
42
use super::xhci_regs::MAX_INTERRUPTER;
43
44
#[sorted]
45
#[derive(Error, Debug)]
46
pub 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
73
type Result<T> = std::result::Result<T, Error>;
74
75
/// Type of usb endpoints.
76
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
77
pub enum TransferDirection {
78
In,
79
Out,
80
Control,
81
}
82
83
/// Current state of xhci transfer.
84
pub 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
96
impl 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.
115
pub 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
130
impl 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)]
148
pub struct XhciTransferManager {
149
transfers: Arc<Mutex<Vec<Weak<Mutex<XhciTransferState>>>>>,
150
device_slot: Weak<DeviceSlot>,
151
}
152
153
impl 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
229
impl 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.
237
pub 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
253
impl Drop for XhciTransfer {
254
fn drop(&mut self) {
255
self.manager.remove_transfer(&self.state);
256
}
257
}
258
259
impl 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)]
270
enum TransferAction {
271
HaltEndpoint,
272
SendEvent {
273
code: TrbCompletionCode,
274
gpa: u64,
275
residual_or_edtla: u32,
276
event_data: bool,
277
},
278
}
279
280
impl 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
574
fn 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)]
586
mod 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
}
1061
1062