1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! A bare-bones client of the USB hardware interface.
//!
//! It responds to standard device requests and can be enumerated.
use core::cell::Cell;
use super::descriptors::{
self, Buffer8, DeviceDescriptor, EndpointAddress, EndpointDescriptor, TransferDirection,
};
use super::usbc_client_ctrl::ClientCtrl;
use kernel::debug;
use kernel::hil;
use kernel::hil::usb::TransferType;
use kernel::utilities::cells::VolatileCell;
const VENDOR_ID: u16 = 0x6667;
const PRODUCT_ID: u16 = 0xabcd;
static LANGUAGES: &[u16; 1] = &[
0x0409, // English (United States)
];
static STRINGS: &[&str] = &[
"XYZ Corp.", // Manufacturer
"The Zorpinator", // Product
"Serial No. 5", // Serial number
];
/// Platform-specific packet length for the `SAM4L` USB hardware.
pub const MAX_CTRL_PACKET_SIZE_SAM4L: u8 = 8;
/// Platform-specific packet length for the `nRF52` USB hardware.
pub const MAX_CTRL_PACKET_SIZE_NRF52840: u8 = 64;
/// Platform-specific packet length for the `earlgrey` USB hardware.
pub const MAX_CTRL_PACKET_SIZE_EARLGREY: u8 = 64;
const N_ENDPOINTS: usize = 2;
pub struct Client<'a, C: 'a> {
client_ctrl: ClientCtrl<'a, 'static, C>,
// An eight-byte buffer for each endpoint
buffers: [Buffer8; N_ENDPOINTS],
// State for a debugging feature: A buffer for echoing bulk data
// from an OUT endpoint back to an IN endpoint
echo_buf: [Cell<u8>; 8], // Must be no larger than endpoint packet buffer
echo_len: Cell<usize>,
delayed_out: Cell<bool>,
}
impl<'a, C: hil::usb::UsbController<'a>> Client<'a, C> {
pub fn new(controller: &'a C, max_ctrl_packet_size: u8) -> Self {
let interfaces: &mut [descriptors::InterfaceDescriptor] =
&mut [descriptors::InterfaceDescriptor {
interface_number: 0,
alternate_setting: 0,
num_endpoints: 0, // (excluding default control endpoint)
interface_class: 0xff, // vendor_specific
interface_subclass: 0xab,
interface_protocol: 0,
string_index: 0,
}];
let endpoints: &[&[EndpointDescriptor]] = &mut [&[
EndpointDescriptor {
endpoint_address: EndpointAddress::new_const(1, TransferDirection::DeviceToHost),
transfer_type: TransferType::Bulk,
max_packet_size: 8,
interval: 0,
},
EndpointDescriptor {
endpoint_address: EndpointAddress::new_const(2, TransferDirection::HostToDevice),
transfer_type: TransferType::Bulk,
max_packet_size: 8,
interval: 0,
},
]];
let (device_descriptor_buffer, other_descriptor_buffer) =
descriptors::create_descriptor_buffers(
DeviceDescriptor {
vendor_id: VENDOR_ID,
product_id: PRODUCT_ID,
manufacturer_string: 1,
product_string: 2,
serial_number_string: 3,
max_packet_size_ep0: max_ctrl_packet_size,
..DeviceDescriptor::default()
},
descriptors::ConfigurationDescriptor::default(),
interfaces,
endpoints,
None, // No HID descriptor
None, // No CDC descriptor array
);
Client {
client_ctrl: ClientCtrl::new(
controller,
device_descriptor_buffer,
other_descriptor_buffer,
None, // No HID descriptor
None, // No report descriptor
LANGUAGES,
STRINGS,
),
buffers: Default::default(),
echo_buf: Default::default(),
echo_len: Cell::new(0),
delayed_out: Cell::new(false),
}
}
fn alert_full(&'a self) {
// Alert the controller that we now have data to send on the Bulk IN endpoint 1
self.controller().endpoint_resume_in(1);
}
fn alert_empty(&'a self) {
// In case we reported Delay before, alert the controller
// that we can now receive data on the Bulk OUT endpoint 2
if self.delayed_out.take() {
self.controller().endpoint_resume_out(2);
}
}
#[inline]
fn controller(&'a self) -> &'a C {
self.client_ctrl.controller()
}
#[inline]
fn buffer(&'a self, i: usize) -> &'a [VolatileCell<u8>; 8] {
&self.buffers[i - 1].buf
}
}
impl<'a, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for Client<'a, C> {
fn enable(&'a self) {
// Set up the default control endpoint
self.client_ctrl.enable();
// Set up a bulk-in endpoint for debugging
self.controller().endpoint_set_in_buffer(1, self.buffer(1));
self.controller().endpoint_in_enable(TransferType::Bulk, 1);
// Set up a bulk-out endpoint for debugging
self.controller().endpoint_set_out_buffer(2, self.buffer(2));
self.controller().endpoint_out_enable(TransferType::Bulk, 2);
}
fn attach(&'a self) {
self.client_ctrl.attach();
}
fn bus_reset(&'a self) {
// Should the client initiate reconfiguration here?
// For now, the hardware layer does it.
debug!("Bus reset");
// Reset the state for our pair of debugging endpoints
self.echo_len.set(0);
self.delayed_out.set(false);
}
/// Handle a Control Setup transaction
fn ctrl_setup(&'a self, endpoint: usize) -> hil::usb::CtrlSetupResult {
self.client_ctrl.ctrl_setup(endpoint)
}
/// Handle a Control In transaction
fn ctrl_in(&'a self, endpoint: usize) -> hil::usb::CtrlInResult {
self.client_ctrl.ctrl_in(endpoint)
}
/// Handle a Control Out transaction
fn ctrl_out(&'a self, endpoint: usize, packet_bytes: u32) -> hil::usb::CtrlOutResult {
self.client_ctrl.ctrl_out(endpoint, packet_bytes)
}
fn ctrl_status(&'a self, endpoint: usize) {
self.client_ctrl.ctrl_status(endpoint)
}
/// Handle the completion of a Control transfer
fn ctrl_status_complete(&'a self, endpoint: usize) {
self.client_ctrl.ctrl_status_complete(endpoint)
}
/// Handle a Bulk/Interrupt IN transaction
fn packet_in(&'a self, transfer_type: TransferType, endpoint: usize) -> hil::usb::InResult {
match transfer_type {
TransferType::Interrupt => {
debug!("interrupt_in({}) not implemented", endpoint);
hil::usb::InResult::Error
}
TransferType::Bulk => {
// Write a packet into the endpoint buffer
let packet_bytes = self.echo_len.get();
if packet_bytes > 0 {
// Copy the entire echo buffer into the packet
let packet = self.buffer(endpoint);
for i in 0..packet_bytes {
packet[i].set(self.echo_buf[i].get());
}
self.echo_len.set(0);
// We can receive more now
self.alert_empty();
hil::usb::InResult::Packet(packet_bytes)
} else {
// Nothing to send
hil::usb::InResult::Delay
}
}
TransferType::Control | TransferType::Isochronous => unreachable!(),
}
}
/// Handle a Bulk/Interrupt OUT transaction
fn packet_out(
&'a self,
transfer_type: TransferType,
endpoint: usize,
packet_bytes: u32,
) -> hil::usb::OutResult {
match transfer_type {
TransferType::Interrupt => {
debug!("interrupt_out({}) not implemented", endpoint);
hil::usb::OutResult::Error
}
TransferType::Bulk => {
// Consume a packet from the endpoint buffer
let new_len = packet_bytes as usize;
let current_len = self.echo_len.get();
let total_len = current_len + new_len;
if total_len > self.echo_buf.len() {
// The packet won't fit in our little buffer. We'll have
// to wait until it is drained
self.delayed_out.set(true);
hil::usb::OutResult::Delay
} else if new_len > 0 {
// Copy the packet into our echo buffer
let packet = self.buffer(endpoint);
for i in 0..new_len {
self.echo_buf[current_len + i].set(packet[i].get());
}
self.echo_len.set(total_len);
// We can start sending again
self.alert_full();
hil::usb::OutResult::Ok
} else {
debug!("Ignoring zero-length OUT packet");
hil::usb::OutResult::Ok
}
}
TransferType::Control | TransferType::Isochronous => unreachable!(),
}
}
fn packet_transmitted(&'a self, _endpoint: usize) {
// Nothing to do.
}
}