blob: 5a668d83825f35b2ebcd4f564cc865565abe228f [file] [log] [blame]
Brian Silvermaneda63f32017-10-08 18:57:33 -04001#include "motors/usb/cdc.h"
2
Brian Silverman69f96c22017-11-01 02:54:02 -04003#include <stdint.h>
Philipp Schrader790cb542023-07-05 21:06:52 -07004#include <string.h>
Brian Silverman69f96c22017-11-01 02:54:02 -04005
6#include "motors/core/time.h"
Brian Silvermaneda63f32017-10-08 18:57:33 -04007
8#define CHECK(c) \
9 do { \
10 if (!(c)) { \
11 while (true) { \
12 for (int i = 0; i < 10000000; ++i) { \
13 GPIOC_PSOR = 1 << 5; \
14 } \
15 for (int i = 0; i < 10000000; ++i) { \
16 GPIOC_PCOR = 1 << 5; \
17 } \
18 } \
19 } \
20 } while (false)
21
Stephan Pleinesf63bde82024-01-13 15:59:33 -080022namespace frc971::teensy {
Brian Silvermaneda63f32017-10-08 18:57:33 -040023namespace {
24
25// Aka the Communications Device Class code, the Communications Class code,
26// and the Communications Interface Class code.
27constexpr uint8_t communications_class() { return 0x02; }
28constexpr uint8_t data_interface_class() { return 0x0A; }
29
30namespace cdc_descriptor_subtype {
31constexpr uint8_t header() { return 0x00; }
32constexpr uint8_t header_length() { return 5; }
33constexpr uint8_t call_management() { return 0x01; }
34constexpr uint8_t call_management_length() { return 5; }
35constexpr uint8_t abstract_control_management() { return 0x02; }
36constexpr uint8_t abstract_control_management_length() { return 4; }
37constexpr uint8_t direct_line_management() { return 0x03; }
38constexpr uint8_t telephone_ringer() { return 0x04; }
39constexpr uint8_t telephone_call_etc() { return 0x05; }
40// Can't just call this "union" because that's a keyword...
41constexpr uint8_t union_function() { return 0x06; }
42constexpr uint8_t union_length(int number_subordinates) {
43 return 4 + number_subordinates;
44}
45constexpr uint8_t country_selection() { return 0x07; }
46constexpr uint8_t telephone_operational_modes() { return 0x08; }
47constexpr uint8_t usb_terminal() { return 0x09; }
48constexpr uint8_t network_channel() { return 0x0A; }
49constexpr uint8_t protocol_unit() { return 0x0B; }
50constexpr uint8_t extension_unit() { return 0x0C; }
51constexpr uint8_t multichannel_management() { return 0x0D; }
52constexpr uint8_t capi_control() { return 0x0E; }
53constexpr uint8_t ethernet_networking() { return 0x0F; }
54constexpr uint8_t atm_networking() { return 0x10; }
55constexpr uint8_t wireless_handset_control() { return 0x11; }
56constexpr uint8_t mobile_direct_line() { return 0x12; }
57constexpr uint8_t mdlm_detail() { return 0x13; }
58constexpr uint8_t device_management() { return 0x14; }
59constexpr uint8_t obex() { return 0x15; }
60constexpr uint8_t command_set() { return 0x16; }
61constexpr uint8_t command_set_detail() { return 0x17; }
62constexpr uint8_t telephone_control() { return 0x18; }
63constexpr uint8_t obex_service() { return 0x19; }
64constexpr uint8_t ncm() { return 0x1A; }
65} // namespace cdc_descriptor_subtype
66
67namespace communications_subclass {
68constexpr uint8_t direct_line_control_model() { return 0x01; }
69constexpr uint8_t abstract_control_model() { return 0x02; }
70constexpr uint8_t telephone_control_model() { return 0x03; }
71constexpr uint8_t multichannel_control_model() { return 0x04; }
72constexpr uint8_t capi_control_model() { return 0x05; }
73constexpr uint8_t ethernet_networking_control_model() { return 0x06; }
74constexpr uint8_t atm_networking_control_model() { return 0x07; }
75constexpr uint8_t wireless_handset_control_model() { return 0x08; }
76constexpr uint8_t device_management() { return 0x09; }
77constexpr uint8_t mobile_direct_line_model() { return 0x0A; }
78constexpr uint8_t obex() { return 0x0B; }
79constexpr uint8_t ethernet_emulation_model() { return 0x0C; }
80constexpr uint8_t network_control_model() { return 0x0D; }
81} // namespace communications_subclass
82
83namespace cdc_class_requests {
84constexpr uint8_t send_encapsulated_command() { return 0x00; }
85constexpr uint8_t get_encapsulated_response() { return 0x01; }
86constexpr uint8_t set_comm_feature() { return 0x02; }
87constexpr uint8_t get_comm_feature() { return 0x03; }
88constexpr uint8_t clear_comm_feature() { return 0x04; }
89constexpr uint8_t set_aux_line_state() { return 0x10; }
90constexpr uint8_t set_hook_state() { return 0x11; }
91constexpr uint8_t pulse_setup() { return 0x12; }
92constexpr uint8_t send_pulse() { return 0x13; }
93constexpr uint8_t set_pulse_time() { return 0x14; }
94constexpr uint8_t ring_aux_jack() { return 0x15; }
95constexpr uint8_t set_line_coding() { return 0x20; }
96constexpr uint8_t get_line_coding() { return 0x21; }
97constexpr uint8_t set_control_line_state() { return 0x22; }
98constexpr uint8_t send_break() { return 0x23; }
99constexpr uint8_t set_ringer_parms() { return 0x30; }
100constexpr uint8_t get_ringer_parms() { return 0x31; }
101constexpr uint8_t set_operation_parms() { return 0x32; }
102constexpr uint8_t get_operation_parms() { return 0x33; }
103constexpr uint8_t set_line_parms() { return 0x34; }
104constexpr uint8_t get_line_parms() { return 0x35; }
105constexpr uint8_t dial_digits() { return 0x36; }
106constexpr uint8_t set_unit_parameter() { return 0x37; }
107constexpr uint8_t get_unit_parameter() { return 0x38; }
108constexpr uint8_t clear_unit_parameter() { return 0x39; }
109constexpr uint8_t get_profile() { return 0x3A; }
110} // namespace cdc_class_requests
111
112} // namespace
113
114void AcmTty::Initialize() {
115 status_interface_ = AddInterface();
116 data_interface_ = AddInterface();
117 status_endpoint_ = AddEndpoint();
118 data_tx_endpoint_ = AddEndpoint();
119 data_rx_endpoint_ = AddEndpoint();
120
Brian Silverman4aa83042018-01-05 12:47:31 -0800121 CreateIadDescriptor(
122 /*first_interface=*/status_interface_,
123 /*interface_count=*/2,
124 /*function_class=*/communications_class(),
125 /*function_subclass=*/communications_subclass::abstract_control_model(),
126 /*function_protocol=*/0, "UsbTty");
Brian Silvermaneda63f32017-10-08 18:57:33 -0400127
128 {
129 const auto interface_descriptor = CreateDescriptor(
130 interface_descriptor_length(), UsbDescriptorType::kInterface);
Philipp Schrader790cb542023-07-05 21:06:52 -0700131 interface_descriptor->AddByte(status_interface_); // bInterfaceNumber
132 interface_descriptor->AddByte(0); // bAlternateSetting
Brian Silvermaneda63f32017-10-08 18:57:33 -0400133 interface_descriptor->AddByte(1); // bNumEndpoints
134 interface_descriptor->AddByte(communications_class()); // bInterfaceClass
135 interface_descriptor->AddByte(
136 communications_subclass::
137 abstract_control_model()); // bInterfaceSubClass
138 interface_descriptor->AddByte(0); // bInterfaceProtocol
139 interface_descriptor->AddByte(
140 device()->AddString("UsbTty.status")); // iInterface
141 }
142
143 {
144 const auto cdc_header =
145 CreateDescriptor(cdc_descriptor_subtype::header_length(),
146 UsbClassDescriptorType::kInterface);
147 cdc_header->AddByte(
148 cdc_descriptor_subtype::header()); // bDescriptorSubtype
149 cdc_header->AddUint16(0x0110); // bcdCDC
150 }
151
152 {
153 const auto call_management =
154 CreateDescriptor(cdc_descriptor_subtype::call_management_length(),
155 UsbClassDescriptorType::kInterface);
156 call_management->AddByte(
157 cdc_descriptor_subtype::call_management()); // bDescriptorSubtype
158 // We don't do call management.
Philipp Schrader790cb542023-07-05 21:06:52 -0700159 call_management->AddByte(0); // bmCapabilities
Brian Silvermaneda63f32017-10-08 18:57:33 -0400160 call_management->AddByte(data_interface_); // bDataInterface
161 }
162
163 {
Brian Silvermand930f282017-11-04 23:09:12 -0400164 const auto abstract_control_management = CreateDescriptor(
165 cdc_descriptor_subtype::abstract_control_management_length(),
166 UsbClassDescriptorType::kInterface);
Brian Silvermaneda63f32017-10-08 18:57:33 -0400167 abstract_control_management->AddByte(
Brian Silvermand930f282017-11-04 23:09:12 -0400168 cdc_descriptor_subtype::
169 abstract_control_management()); // bDescriptorSubtype
Brian Silvermaneda63f32017-10-08 18:57:33 -0400170 // We support:
171 // line_coding and serial_state
172 // send_break
173 // We don't support:
174 // comm_feature
175 // network_notification
176 abstract_control_management->AddByte(6); // bmCapabilities
177 }
178
179 {
180 const auto cdc_union_descriptor =
181 CreateDescriptor(cdc_descriptor_subtype::union_length(1),
182 UsbClassDescriptorType::kInterface);
183 cdc_union_descriptor->AddByte(
184 cdc_descriptor_subtype::union_function()); // bDescriptorSubtype
185 cdc_union_descriptor->AddByte(status_interface_); // bMasterInterface
Philipp Schrader790cb542023-07-05 21:06:52 -0700186 cdc_union_descriptor->AddByte(data_interface_); // bSlaveInterface
Brian Silvermaneda63f32017-10-08 18:57:33 -0400187 }
188
189 {
190 const auto endpoint_descriptor = CreateDescriptor(
191 endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
192 endpoint_descriptor->AddByte(status_endpoint_ |
193 m_endpoint_address_in()); // bEndpointAddress
194 endpoint_descriptor->AddByte(
195 m_endpoint_attributes_interrupt()); // bmAttributes
Brian Silvermand930f282017-11-04 23:09:12 -0400196 endpoint_descriptor->AddUint16(kStatusMaxPacketSize); // wMaxPacketSize
Brian Silvermaneda63f32017-10-08 18:57:33 -0400197 // Set it to the max because we have nothing to send, so no point using bus
198 // bandwidth asking.
199 endpoint_descriptor->AddByte(255); // bInterval
200 }
201
202 {
203 const auto interface_descriptor = CreateDescriptor(
204 interface_descriptor_length(), UsbDescriptorType::kInterface);
205 interface_descriptor->AddByte(data_interface_); // bInterfaceNumber
206 interface_descriptor->AddByte(0); // bAlternateSetting
207 interface_descriptor->AddByte(2); // bNumEndpoints
208 interface_descriptor->AddByte(data_interface_class()); // bInterfaceClass
209 interface_descriptor->AddByte(0); // bInterfaceSubClass
210 interface_descriptor->AddByte(0); // bInterfaceProtocol
211 interface_descriptor->AddByte(
212 device()->AddString("UsbTty.data")); // iInterface
213 }
214
215 // Kernel seems to think tx and rx belong in the other order, but it deals
216 // with either one. Everybody's examples seem to use this order, and the
217 // kernel deals with it, so going to go with this.
218 {
219 const auto endpoint_descriptor = CreateDescriptor(
220 endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
221 endpoint_descriptor->AddByte(data_rx_endpoint_); // bEndpointAddress
222 endpoint_descriptor->AddByte(m_endpoint_attributes_bulk()); // bmAttributes
Brian Silvermand930f282017-11-04 23:09:12 -0400223 endpoint_descriptor->AddUint16(kDataMaxPacketSize); // wMaxPacketSize
Brian Silvermaneda63f32017-10-08 18:57:33 -0400224 endpoint_descriptor->AddByte(1); // bInterval
225 }
226
227 {
228 const auto endpoint_descriptor = CreateDescriptor(
229 endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
230 endpoint_descriptor->AddByte(data_tx_endpoint_ |
231 m_endpoint_address_in()); // bEndpointAddress
232 endpoint_descriptor->AddByte(m_endpoint_attributes_bulk()); // bmAttributes
Brian Silvermand930f282017-11-04 23:09:12 -0400233 endpoint_descriptor->AddUint16(kDataMaxPacketSize); // wMaxPacketSize
Brian Silvermaneda63f32017-10-08 18:57:33 -0400234 endpoint_descriptor->AddByte(1); // bInterval
235 }
236}
237
238// We deliberately don't implement SendEncapsulatedCommand and
239// GetEncapsulatedResponse because there doesn't seem to be any reason for
240// anybody to send those to us, despite them being "required".
241UsbFunction::SetupResponse AcmTty::HandleEndpoint0SetupPacket(
242 const UsbDevice::SetupPacket &setup_packet) {
243 if (G_SETUP_REQUEST_TYPE_TYPE(setup_packet.request_type) !=
244 SetupRequestType::kClass) {
245 return SetupResponse::kIgnored;
246 }
247 if (G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type) !=
248 standard_setup_recipients::kInterface) {
249 return SetupResponse::kIgnored;
250 }
251 if (setup_packet.index != status_interface_) {
252 return SetupResponse::kIgnored;
253 }
254 const bool in = setup_packet.request_type & M_SETUP_REQUEST_TYPE_IN;
255 switch (setup_packet.request) {
256 case cdc_class_requests::set_line_coding():
257 if (in || setup_packet.value != 0 ||
258 setup_packet.length != sizeof(line_coding_)) {
259 return SetupResponse::kStall;
260 }
261 next_endpoint0_out_ = NextEndpoint0Out::kLineCoding;
262 return SetupResponse::kHandled;
263
264 case cdc_class_requests::get_line_coding():
265 if (!in || setup_packet.value != 0 ||
266 setup_packet.length != sizeof(line_coding_)) {
267 return SetupResponse::kStall;
268 }
269 line_coding_to_send_ = line_coding_;
270 device()->QueueEndpoint0Data(
271 reinterpret_cast<const char *>(&line_coding_to_send_),
272 setup_packet.length);
273 return SetupResponse::kHandled;
274
275 case cdc_class_requests::set_control_line_state():
276 if (in || setup_packet.length != 0) {
277 return SetupResponse::kStall;
278 }
279 control_line_state_ = setup_packet.value;
280 device()->SendEmptyEndpoint0Packet();
281 return SetupResponse::kHandled;
282
283 case cdc_class_requests::send_break():
284 if (in || setup_packet.length != 0) {
285 return SetupResponse::kStall;
286 }
287 // TODO(Brian): setup_packet.value is the length of the break in ms.
288 // 0xFFFF means keep sending break until receiving another one.
289 device()->SendEmptyEndpoint0Packet();
290 return SetupResponse::kHandled;
291 }
292 return SetupResponse::kStall;
293}
294
295UsbFunction::SetupResponse AcmTty::HandleEndpoint0OutPacket(void *data,
296 int data_length) {
297 switch (next_endpoint0_out_) {
298 case NextEndpoint0Out::kNone:
299 return SetupResponse::kIgnored;
300
301 case NextEndpoint0Out::kLineCoding:
Brian Silverman69f96c22017-11-01 02:54:02 -0400302 next_endpoint0_out_ = NextEndpoint0Out::kNone;
Brian Silvermaneda63f32017-10-08 18:57:33 -0400303 if (data_length != sizeof(line_coding_)) {
304 return SetupResponse::kStall;
305 }
306 memcpy(&line_coding_, data, data_length);
307 device()->SendEmptyEndpoint0Packet();
Brian Silverman69f96c22017-11-01 02:54:02 -0400308 // If we're supposed to reboot, then do it.
309 if (line_coding_.rate == UINT32_C(0x97101678)) {
310 // Delay for a bit so the empty IN packet gets back to the host.
311 delay(5);
312 __asm__ __volatile__("bkpt");
313 }
Brian Silvermaneda63f32017-10-08 18:57:33 -0400314 return SetupResponse::kHandled;
315
316 default:
317 return SetupResponse::kIgnored;
318 }
319}
320
321void AcmTty::HandleOutFinished(int endpoint, BdtEntry *bdt_entry) {
322 if (endpoint == data_rx_endpoint_) {
323 const size_t data_size = G_USB_BD_BC(bdt_entry->buffer_descriptor);
324 CHECK(rx_queue_.Write(static_cast<char *>(bdt_entry->address), data_size) ==
325 data_size);
326 dma_memory_barrier();
327
328 DisableInterrupts disable_interrupts;
329 // If we don't have space to handle it, don't return the entry so we'll
330 // ask the host to wait to transmit more data.
331 if (rx_queue_.space_available() >= kDataMaxPacketSize * 2) {
332 bdt_entry->buffer_descriptor = M_USB_BD_OWN | M_USB_BD_DTS |
333 V_USB_BD_BC(kDataMaxPacketSize) |
334 static_cast<uint32_t>(next_rx_toggle_);
335 next_rx_toggle_ = Data01Inverse(next_rx_toggle_);
336 } else {
337 if (first_rx_held_ == nullptr) {
338 first_rx_held_ = bdt_entry;
339 } else {
340 CHECK(second_rx_held_ == nullptr);
341 second_rx_held_ = bdt_entry;
342 }
343 }
344 }
345}
346
347void AcmTty::HandleInFinished(int endpoint, BdtEntry * /*bdt_entry*/,
348 EvenOdd odd) {
349 if (endpoint == data_tx_endpoint_) {
350 DisableInterrupts disable_interrupts;
351 CHECK(odd == BufferStateToEmpty(tx_state_));
352 tx_state_ = BufferStateAfterEmpty(tx_state_);
353 EnqueueTxData(disable_interrupts);
354 }
355}
356
357void AcmTty::HandleConfigured(int endpoint) {
358 // TODO(Brian): Handle data already in the buffers correctly.
359 if (endpoint == status_endpoint_) {
360 device()->ConfigureEndpointFor(status_endpoint_, false, true, true);
361 } else if (endpoint == data_tx_endpoint_) {
362 device()->ConfigureEndpointFor(data_tx_endpoint_, false, true, true);
Brian Silvermaneda63f32017-10-08 18:57:33 -0400363 DisableInterrupts disable_interrupts;
Brian Silvermand930f282017-11-04 23:09:12 -0400364 next_tx_toggle_ = Data01::kData0;
Brian Silvermaneda63f32017-10-08 18:57:33 -0400365 EnqueueTxData(disable_interrupts);
366 } else if (endpoint == data_rx_endpoint_) {
367 device()->ConfigureEndpointFor(data_rx_endpoint_, true, false, true);
368 next_rx_toggle_ = Data01::kData0;
369 device()->SetBdtEntry(
370 data_rx_endpoint_, Direction::kRx, EvenOdd::kEven,
Brian Silverman64d04272017-11-26 22:26:39 -0500371 {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(rx_buffers_[0].size()),
372 rx_buffers_[0].data()});
Brian Silvermaneda63f32017-10-08 18:57:33 -0400373 device()->SetBdtEntry(
374 data_rx_endpoint_, Direction::kRx, EvenOdd::kOdd,
Brian Silverman64d04272017-11-26 22:26:39 -0500375 {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(rx_buffers_[1].size()) |
Brian Silvermaneda63f32017-10-08 18:57:33 -0400376 M_USB_BD_DATA1,
Brian Silverman64d04272017-11-26 22:26:39 -0500377 rx_buffers_[1].data()});
Brian Silvermaneda63f32017-10-08 18:57:33 -0400378 }
379}
380
381size_t AcmTty::Read(void *buffer, size_t buffer_size) {
382 const size_t r = rx_queue_.Read(static_cast<char *>(buffer), buffer_size);
383
384 DisableInterrupts disable_interrupts;
385 if (rx_queue_.space_available() >= kDataMaxPacketSize * 2) {
386 if (first_rx_held_ != nullptr) {
387 first_rx_held_->buffer_descriptor =
388 M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kDataMaxPacketSize) |
389 static_cast<uint32_t>(next_rx_toggle_);
390 next_rx_toggle_ = Data01Inverse(next_rx_toggle_);
391 }
392 if (second_rx_held_ != nullptr) {
393 second_rx_held_->buffer_descriptor =
394 M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(kDataMaxPacketSize) |
395 static_cast<uint32_t>(next_rx_toggle_);
396 next_rx_toggle_ = Data01Inverse(next_rx_toggle_);
397 }
398 first_rx_held_ = second_rx_held_ = nullptr;
399 }
400
401 return r;
402}
403
404size_t AcmTty::Write(const void *buffer, size_t buffer_size) {
405 const size_t r =
406 tx_queue_.Write(static_cast<const char *>(buffer), buffer_size);
407 DisableInterrupts disable_interrupts;
408 EnqueueTxData(disable_interrupts);
409 return r;
410}
411
412// TODO(Brian): Could this critical section be broken up per buffer we fill?
413void AcmTty::EnqueueTxData(const DisableInterrupts &) {
414 while (BufferStateHasEmpty(tx_state_) && !tx_queue_.empty()) {
415 const EvenOdd next_tx_odd = BufferStateToFill(tx_state_);
416 const size_t buffer_size =
417 tx_queue_.Read(tx_buffer_for(next_tx_odd), kDataMaxPacketSize);
418 CHECK(buffer_size > 0);
419 dma_memory_barrier();
420 device()->SetBdtEntry(
421 data_tx_endpoint_, Direction::kTx, next_tx_odd,
422 {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(buffer_size) |
423 static_cast<uint32_t>(next_tx_toggle_),
424 tx_buffer_for(next_tx_odd)});
425 tx_state_ = BufferStateAfterFill(tx_state_);
426 next_tx_toggle_ = Data01Inverse(next_tx_toggle_);
427 }
428}
429
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800430} // namespace frc971::teensy