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