capsules_extra/usb/
usbc_client.rs

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