mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-06 02:25:23 +00:00
add scatter gather buffer
scatter gather buffer is a buffer with segments. CQ-DEPEND=CL:1510816 BUG=chromium:831850 TEST=cargo test Change-Id: Iea54d5b3d6f9194113f6237eb14aafab71ca2ff9 Reviewed-on: https://chromium-review.googlesource.com/1510817 Commit-Ready: Jingkui Wang <jkwang@google.com> Tested-by: kokoro <noreply+kokoro@google.com> Tested-by: Zach Reizner <zachr@chromium.org> Reviewed-by: Jingkui Wang <jkwang@google.com> Reviewed-by: Zach Reizner <zachr@chromium.org>
This commit is contained in:
parent
115fe4e233
commit
fa13275942
2 changed files with 181 additions and 0 deletions
|
@ -5,6 +5,7 @@
|
|||
mod event_ring;
|
||||
mod interrupter;
|
||||
mod intr_resample_handler;
|
||||
mod scatter_gather_buffer;
|
||||
mod xhci_abi;
|
||||
mod xhci_abi_schema;
|
||||
mod xhci_regs;
|
||||
|
|
180
devices/src/usb/xhci/scatter_gather_buffer.rs
Normal file
180
devices/src/usb/xhci/scatter_gather_buffer.rs
Normal file
|
@ -0,0 +1,180 @@
|
|||
// Copyright 2019 The Chromium OS Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
use super::xhci_abi::{Error as TrbError, NormalTrb, TransferDescriptor, TrbCast, TrbType};
|
||||
use std::fmt::{self, Display};
|
||||
use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
ReadGuestMemory(GuestMemoryError),
|
||||
WriteGuestMemory(GuestMemoryError),
|
||||
UnknownTrbType(TrbError),
|
||||
CastTrb(TrbError),
|
||||
BadTrbType(TrbType),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
|
||||
match self {
|
||||
ReadGuestMemory(e) => write!(f, "cannot read guest memory: {}", e),
|
||||
WriteGuestMemory(e) => write!(f, "cannot write guest memory: {}", e),
|
||||
UnknownTrbType(e) => write!(f, "unknown trb type: {}", e),
|
||||
CastTrb(e) => write!(f, "cannot cast trb: {}", e),
|
||||
BadTrbType(t) => write!(f, "should not build buffer from trb type: {:?}", t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See xHCI spec 3.2.8 for scatter/gather transfer. It's used in bulk/interrupt transfers. See
|
||||
/// 3.2.10 for details.
|
||||
pub struct ScatterGatherBuffer {
|
||||
mem: GuestMemory,
|
||||
td: TransferDescriptor,
|
||||
}
|
||||
|
||||
impl ScatterGatherBuffer {
|
||||
/// Create a new buffer from transfer descriptor.
|
||||
pub fn new(mem: GuestMemory, td: TransferDescriptor) -> Result<ScatterGatherBuffer> {
|
||||
for atrb in &td {
|
||||
let trb_type = atrb.trb.trb_type().map_err(Error::UnknownTrbType)?;
|
||||
if trb_type != TrbType::Normal
|
||||
&& trb_type != TrbType::DataStage
|
||||
&& trb_type != TrbType::Isoch
|
||||
{
|
||||
return Err(Error::BadTrbType(trb_type));
|
||||
}
|
||||
}
|
||||
Ok(ScatterGatherBuffer { mem, td })
|
||||
}
|
||||
|
||||
/// Total len of this buffer.
|
||||
pub fn len(&self) -> Result<usize> {
|
||||
let mut total_len = 0usize;
|
||||
for atrb in &self.td {
|
||||
total_len += atrb
|
||||
.trb
|
||||
.cast::<NormalTrb>()
|
||||
.map_err(Error::CastTrb)?
|
||||
.get_trb_transfer_length() as usize;
|
||||
}
|
||||
Ok(total_len)
|
||||
}
|
||||
|
||||
/// Read content to buffer, return number of bytes read.
|
||||
pub fn read(&self, buffer: &mut [u8]) -> Result<usize> {
|
||||
let mut total_size = 0usize;
|
||||
let mut offset = 0;
|
||||
for atrb in &self.td {
|
||||
let normal_trb = atrb.trb.cast::<NormalTrb>().map_err(Error::CastTrb)?;
|
||||
let len = normal_trb.get_trb_transfer_length() as usize;
|
||||
let buffer_len = {
|
||||
if offset == buffer.len() {
|
||||
return Ok(total_size);
|
||||
}
|
||||
if buffer.len() > offset + len {
|
||||
len
|
||||
} else {
|
||||
buffer.len() - offset
|
||||
}
|
||||
};
|
||||
let buffer_end = offset + buffer_len;
|
||||
let cur_buffer = &mut buffer[offset..buffer_end];
|
||||
offset = buffer_end;
|
||||
total_size += self
|
||||
.mem
|
||||
.read_at_addr(cur_buffer, GuestAddress(normal_trb.get_data_buffer()))
|
||||
.map_err(Error::ReadGuestMemory)?;
|
||||
}
|
||||
Ok(total_size)
|
||||
}
|
||||
|
||||
/// Write content from buffer, return number of bytes written.
|
||||
pub fn write(&self, buffer: &[u8]) -> Result<usize> {
|
||||
let mut total_size = 0usize;
|
||||
let mut offset = 0;
|
||||
for atrb in &self.td {
|
||||
let normal_trb = atrb.trb.cast::<NormalTrb>().map_err(Error::CastTrb)?;
|
||||
let len = normal_trb.get_trb_transfer_length() as usize;
|
||||
let buffer_len = {
|
||||
if offset == buffer.len() {
|
||||
return Ok(total_size);
|
||||
}
|
||||
if buffer.len() > offset + len {
|
||||
len
|
||||
} else {
|
||||
buffer.len() - offset
|
||||
}
|
||||
};
|
||||
let buffer_end = offset + buffer_len;
|
||||
let cur_buffer = &buffer[offset..buffer_end];
|
||||
offset = buffer_end;
|
||||
total_size += self
|
||||
.mem
|
||||
.write_at_addr(cur_buffer, GuestAddress(normal_trb.get_data_buffer()))
|
||||
.map_err(Error::WriteGuestMemory)?;
|
||||
}
|
||||
Ok(total_size)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use usb::xhci::xhci_abi::{AddressedTrb, Trb};
|
||||
|
||||
#[test]
|
||||
fn scatter_gather_buffer_test() {
|
||||
let gm = GuestMemory::new(&vec![(GuestAddress(0), 0x1000)]).unwrap();
|
||||
let mut td = TransferDescriptor::new();
|
||||
// In this td, we are going to have scatter buffer at 0x100, length 4, 0x200 length 2 and
|
||||
// 0x300 length 1.
|
||||
let mut trb = Trb::new();
|
||||
{
|
||||
let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
|
||||
ntrb.set_trb_type(TrbType::Normal as u8);
|
||||
ntrb.set_data_buffer(0x100);
|
||||
ntrb.set_trb_transfer_length(4);
|
||||
}
|
||||
td.push(AddressedTrb { trb, gpa: 0 });
|
||||
let mut trb = Trb::new();
|
||||
{
|
||||
let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
|
||||
ntrb.set_trb_type(TrbType::Normal as u8);
|
||||
ntrb.set_data_buffer(0x200);
|
||||
ntrb.set_trb_transfer_length(2);
|
||||
}
|
||||
td.push(AddressedTrb { trb, gpa: 0 });
|
||||
let mut trb = Trb::new();
|
||||
{
|
||||
let ntrb = trb.cast_mut::<NormalTrb>().unwrap();
|
||||
ntrb.set_trb_type(TrbType::Normal as u8);
|
||||
ntrb.set_data_buffer(0x300);
|
||||
ntrb.set_trb_transfer_length(1);
|
||||
}
|
||||
td.push(AddressedTrb { trb, gpa: 0 });
|
||||
|
||||
let buffer = ScatterGatherBuffer::new(gm.clone(), td).unwrap();
|
||||
|
||||
assert_eq!(buffer.len().unwrap(), 7);
|
||||
let data_to_write: [u8; 7] = [7, 6, 5, 4, 3, 2, 1];
|
||||
buffer.write(&data_to_write).unwrap();
|
||||
|
||||
let mut d = [0; 4];
|
||||
gm.read_exact_at_addr(&mut d, GuestAddress(0x100)).unwrap();
|
||||
assert_eq!(d, [7, 6, 5, 4]);;
|
||||
gm.read_exact_at_addr(&mut d, GuestAddress(0x200)).unwrap();
|
||||
assert_eq!(d, [3, 2, 0, 0]);;
|
||||
gm.read_exact_at_addr(&mut d, GuestAddress(0x300)).unwrap();
|
||||
assert_eq!(d, [1, 0, 0, 0]);;
|
||||
|
||||
let mut data_read = [0; 7];
|
||||
buffer.read(&mut data_read).unwrap();
|
||||
assert_eq!(data_to_write, data_read);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue