Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/usb/backend/endpoint.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;
6
use std::sync::Arc;
7
8
use base::debug;
9
use base::error;
10
use usb_util::EndpointDirection;
11
use usb_util::EndpointType;
12
use usb_util::TransferBuffer;
13
use usb_util::TransferStatus;
14
use usb_util::ENDPOINT_DIRECTION_OFFSET;
15
16
use crate::usb::backend::device::BackendDevice;
17
use crate::usb::backend::device::BackendDeviceType;
18
use crate::usb::backend::error::Error;
19
use crate::usb::backend::error::Result;
20
use crate::usb::backend::transfer::BackendTransfer;
21
use crate::usb::backend::transfer::BackendTransferType;
22
use crate::usb::backend::utils::update_transfer_state;
23
use crate::usb::xhci::scatter_gather_buffer::ScatterGatherBuffer;
24
use crate::usb::xhci::xhci_transfer::TransferDirection;
25
use crate::usb::xhci::xhci_transfer::XhciTransfer;
26
use crate::usb::xhci::xhci_transfer::XhciTransferState;
27
use crate::usb::xhci::xhci_transfer::XhciTransferType;
28
use crate::utils::AsyncJobQueue;
29
use crate::utils::FailHandle;
30
31
#[derive(Copy, Clone, PartialEq, Eq)]
32
pub enum ControlEndpointState {
33
/// Control endpoint should receive setup stage next.
34
SetupStage,
35
/// Control endpoint should receive data stage next.
36
DataStage,
37
/// Control endpoint should receive status stage next.
38
StatusStage,
39
}
40
41
/// Isochronous, Bulk or Interrupt endpoint.
42
pub struct UsbEndpoint {
43
fail_handle: Arc<dyn FailHandle>,
44
job_queue: Arc<AsyncJobQueue>,
45
endpoint_number: u8,
46
direction: EndpointDirection,
47
ty: EndpointType,
48
}
49
50
impl UsbEndpoint {
51
/// Create new endpoint. This function will panic if endpoint type is control.
52
pub fn new(
53
fail_handle: Arc<dyn FailHandle>,
54
job_queue: Arc<AsyncJobQueue>,
55
endpoint_number: u8,
56
direction: EndpointDirection,
57
ty: EndpointType,
58
) -> UsbEndpoint {
59
assert!(ty != EndpointType::Control);
60
UsbEndpoint {
61
fail_handle,
62
job_queue,
63
endpoint_number,
64
direction,
65
ty,
66
}
67
}
68
69
fn ep_addr(&self) -> u8 {
70
self.endpoint_number | ((self.direction as u8) << ENDPOINT_DIRECTION_OFFSET)
71
}
72
73
/// Returns true if this endpoint matches number and direction.
74
pub fn match_ep(&self, endpoint_number: u8, dir: TransferDirection) -> bool {
75
let self_dir = match self.direction {
76
EndpointDirection::HostToDevice => TransferDirection::Out,
77
EndpointDirection::DeviceToHost => TransferDirection::In,
78
};
79
self.endpoint_number == endpoint_number && self_dir == dir
80
}
81
82
/// Handle a xhci transfer.
83
// This function should return an error only when we want to remove the ring handler from the
84
// event loop. For any error that might originate from the guest driver, it should report an
85
// error to the guest and return Ok.
86
pub fn handle_transfer(
87
&self,
88
device: &mut BackendDeviceType,
89
transfer: XhciTransfer,
90
) -> Result<()> {
91
let transfer_type = match transfer.get_transfer_type() {
92
Ok(ty) => ty,
93
Err(e) => {
94
error!("failed to get transfer type: {}", e);
95
return transfer
96
.on_transfer_complete(&TransferStatus::Error, 0)
97
.map_err(Error::TransferComplete);
98
}
99
};
100
let buffer = match transfer_type {
101
XhciTransferType::Normal => transfer.create_buffer().map_err(Error::CreateBuffer)?,
102
XhciTransferType::Noop => {
103
return transfer
104
.on_transfer_complete(&TransferStatus::Completed, 0)
105
.map_err(Error::TransferComplete);
106
}
107
_ => {
108
error!("unhandled xhci transfer type by usb endpoint");
109
return transfer
110
.on_transfer_complete(&TransferStatus::Error, 0)
111
.map_err(Error::TransferComplete);
112
}
113
};
114
115
match self.ty {
116
EndpointType::Bulk => {
117
self.handle_bulk_transfer(device, transfer, buffer)?;
118
}
119
EndpointType::Interrupt => {
120
self.handle_interrupt_transfer(device, transfer, buffer)?;
121
}
122
_ => {
123
return transfer
124
.on_transfer_complete(&TransferStatus::Error, 0)
125
.map_err(Error::TransferComplete);
126
}
127
}
128
Ok(())
129
}
130
131
fn get_transfer_buffer(
132
&self,
133
buffer: &ScatterGatherBuffer,
134
device: &mut BackendDeviceType,
135
) -> Result<TransferBuffer> {
136
let len = buffer.len().map_err(Error::BufferLen)?;
137
let mut buf = device.request_transfer_buffer(len);
138
if self.direction == EndpointDirection::HostToDevice {
139
// Read data from ScatterGatherBuffer to a continuous memory.
140
match &mut buf {
141
TransferBuffer::Dma(dmabuf) => {
142
if let Some(buf) = dmabuf.upgrade() {
143
buffer
144
.read(buf.lock().as_mut_slice())
145
.map_err(Error::ReadBuffer)?;
146
} else {
147
return Err(Error::GetDmaBuffer);
148
}
149
}
150
TransferBuffer::Vector(v) => {
151
buffer.read(v.as_mut_slice()).map_err(Error::ReadBuffer)?;
152
}
153
}
154
}
155
Ok(buf)
156
}
157
158
fn handle_bulk_transfer(
159
&self,
160
device: &mut BackendDeviceType,
161
xhci_transfer: XhciTransfer,
162
buffer: ScatterGatherBuffer,
163
) -> Result<()> {
164
let transfer_buffer = self.get_transfer_buffer(&buffer, device)?;
165
let usb_transfer = device.build_bulk_transfer(
166
self.ep_addr(),
167
transfer_buffer,
168
xhci_transfer.get_stream_id(),
169
)?;
170
self.do_handle_transfer(device, xhci_transfer, usb_transfer, buffer)
171
}
172
173
fn handle_interrupt_transfer(
174
&self,
175
device: &mut BackendDeviceType,
176
xhci_transfer: XhciTransfer,
177
buffer: ScatterGatherBuffer,
178
) -> Result<()> {
179
let transfer_buffer = self.get_transfer_buffer(&buffer, device)?;
180
let usb_transfer = device.build_interrupt_transfer(self.ep_addr(), transfer_buffer)?;
181
self.do_handle_transfer(device, xhci_transfer, usb_transfer, buffer)
182
}
183
184
fn do_handle_transfer(
185
&self,
186
device: &mut BackendDeviceType,
187
xhci_transfer: XhciTransfer,
188
mut usb_transfer: BackendTransferType,
189
buffer: ScatterGatherBuffer,
190
) -> Result<()> {
191
let xhci_transfer = Arc::new(xhci_transfer);
192
let tmp_transfer = xhci_transfer.clone();
193
match self.direction {
194
EndpointDirection::HostToDevice => {
195
let _trace = cros_tracing::trace_event!(
196
USB,
197
"Endpoint out transfer",
198
self.ep_addr(),
199
buffer.len()
200
);
201
let callback = move |t: BackendTransferType| {
202
let mut state = xhci_transfer.state().lock();
203
update_transfer_state(&mut state, t.status())?;
204
match *state {
205
XhciTransferState::Cancelled => {
206
debug!("Xhci transfer has been cancelled");
207
drop(state);
208
xhci_transfer
209
.on_transfer_complete(&TransferStatus::Cancelled, 0)
210
.map_err(Error::TransferComplete)
211
}
212
XhciTransferState::Completed => {
213
let status = t.status();
214
let actual_length = t.actual_length();
215
drop(state);
216
xhci_transfer
217
.on_transfer_complete(&status, actual_length as u32)
218
.map_err(Error::TransferComplete)
219
}
220
_ => {
221
error!("xhci trasfer state (host to device) is invalid");
222
Err(Error::BadXhciTransferState)
223
}
224
}
225
};
226
let fail_handle = self.fail_handle.clone();
227
usb_transfer.set_callback(move |t: BackendTransferType| match callback(t) {
228
Ok(_) => {}
229
Err(e) => {
230
error!("bulk transfer callback failed: {:?}", e);
231
fail_handle.fail();
232
}
233
});
234
device.submit_transfer(
235
self.fail_handle.clone(),
236
&self.job_queue,
237
tmp_transfer,
238
usb_transfer,
239
)?;
240
}
241
EndpointDirection::DeviceToHost => {
242
let _trace = cros_tracing::trace_event!(
243
USB,
244
"Endpoint in transfer",
245
self.ep_addr(),
246
buffer.len()
247
);
248
let _addr = self.ep_addr();
249
let callback = move |t: BackendTransferType| {
250
let mut state = xhci_transfer.state().lock();
251
update_transfer_state(&mut state, t.status())?;
252
match *state {
253
XhciTransferState::Cancelled => {
254
debug!("Xhci transfer has been cancelled");
255
drop(state);
256
xhci_transfer
257
.on_transfer_complete(&TransferStatus::Cancelled, 0)
258
.map_err(Error::TransferComplete)
259
}
260
XhciTransferState::Completed => {
261
let status = t.status();
262
let actual_length = t.actual_length();
263
let copied_length = match t.buffer() {
264
TransferBuffer::Vector(v) => {
265
buffer.write(v.as_slice()).map_err(Error::WriteBuffer)?
266
}
267
TransferBuffer::Dma(buf) => {
268
if let Some(buf) = buf.upgrade() {
269
buffer
270
.write(buf.lock().as_slice())
271
.map_err(Error::WriteBuffer)?
272
} else {
273
return Err(Error::GetDmaBuffer);
274
}
275
}
276
};
277
let actual_length = cmp::min(actual_length, copied_length);
278
drop(state);
279
xhci_transfer
280
.on_transfer_complete(&status, actual_length as u32)
281
.map_err(Error::TransferComplete)
282
}
283
_ => {
284
// update state is already invoked. This match should not be in any
285
// other state.
286
error!("xhci trasfer state (device to host) is invalid");
287
Err(Error::BadXhciTransferState)
288
}
289
}
290
};
291
let fail_handle = self.fail_handle.clone();
292
293
usb_transfer.set_callback(move |t: BackendTransferType| match callback(t) {
294
Ok(_) => {}
295
Err(e) => {
296
error!("bulk transfer callback {:?}", e);
297
fail_handle.fail();
298
}
299
});
300
301
device.submit_transfer(
302
self.fail_handle.clone(),
303
&self.job_queue,
304
tmp_transfer,
305
usb_transfer,
306
)?;
307
}
308
}
309
Ok(())
310
}
311
}
312
313