blob: f42930bbe8a0847266d6d1a9b0d56916c2ae471e [file] [log] [blame]
Brian Silvermand930f282017-11-04 23:09:12 -04001#include "motors/usb/hid.h"
2
3namespace frc971 {
4namespace teensy {
5namespace {
6
7constexpr uint8_t hid_class() { return 0x03; }
8
9namespace hid_class_requests {
10constexpr uint8_t get_report() { return 0x01; }
11constexpr uint8_t get_idle() { return 0x02; }
12constexpr uint8_t get_protocol() { return 0x03; }
13constexpr uint8_t set_report() { return 0x09; }
14constexpr uint8_t set_idle() { return 0x0a; }
15constexpr uint8_t set_protcol() { return 0x0b; }
16} // namespace hid_class_requests
17
18// The hard-coded HID report descriptor.
19uint8_t kReportDescriptor[] = {
20 0x05, 0x01, // Usage Page (Generic Desktop),
21 0x09, 0x04, // Usage (Joystick),
22 0xA1, 0x01, // Collection (Application),
23 0x75, 0x10, // Report Size (16),
Brian Silverman7c7170e2018-01-13 17:41:21 -080024 0x95, 0x06, // Report Count (6),
Brian Silvermand930f282017-11-04 23:09:12 -040025 0x15, 0x00, // Logical Minimum (0),
26 0x26, 0xFF, 0xFF, // Logical Maximum (65535),
27 0x35, 0x00, // Physical Minimum (0),
28 0x46, 0xFF, 0xFF, // Physical Maximum (65535),
29 0x09, 0x30, // Usage (X),
30 0x09, 0x31, // Usage (Y),
31 0x09, 0x32, // Usage (Z),
Brian Silverman7c7170e2018-01-13 17:41:21 -080032 0x09, 0x33, // Usage (Rz),
33 0x09, 0x34, // Usage (?),
34 0x09, 0x35, // Usage (?),
Brian Silvermand930f282017-11-04 23:09:12 -040035 0x81, 0x02, // Input (Variable),
36 0x75, 0x01, // Report Size (1),
37 0x95, 0x10, // Report Count (16),
38 0x25, 0x01, // Logical Maximum (1),
39 0x45, 0x01, // Physical Maximum (1),
40 0x05, 0x09, // Usage Page (Button),
Brian Silverman7c7170e2018-01-13 17:41:21 -080041 0x19, 0x01, // Usage Minimum (01),
Brian Silvermand930f282017-11-04 23:09:12 -040042 0x29, 0x10, // Usage Maximum (16),
43 0x81, 0x02, // Input (Variable),
44 0xC0 // End Collection
45};
46
47// The hard-coded HID descriptor.
48uint8_t kHidDescriptor[] = {
49 9, // bLength
50 static_cast<uint8_t>(UsbClassDescriptorType::kHidHid), // bDescriptorType
51 0x10, 0x01, // bcdHID
52 0, // bCountryCode
53 1, // bNumDescriptors
54 static_cast<uint8_t>(
55 UsbClassDescriptorType::kHidReport), // bDescriptorType
56 sizeof(kReportDescriptor), // wDescriptorLength
57 0,
58};
59
60} // namespace
61
62void HidFunction::Initialize() {
63 interface_ = AddInterface();
64 in_endpoint_ = AddEndpoint();
65
Brian Silverman4aa83042018-01-05 12:47:31 -080066 CreateIadDescriptor(
67 /*first_interface=*/interface_,
68 /*interface_count=*/1,
69 /*function_class=*/hid_class(),
70 /*function_subclass=*/0,
71 /*function_protocol=*/0, "HidIad");
Brian Silvermand930f282017-11-04 23:09:12 -040072
73 {
74 const auto interface_descriptor = CreateDescriptor(
75 interface_descriptor_length(), UsbDescriptorType::kInterface);
76 interface_descriptor->AddByte(interface_); // bInterfaceNumber
77 interface_descriptor->AddByte(0); // bAlternateSetting
78 interface_descriptor->AddByte(1); // bNumEndpoints
79 interface_descriptor->AddByte(hid_class()); // bInterfaceClass
80 interface_descriptor->AddByte(0); // bInterfaceSubClass
81 interface_descriptor->AddByte(0); // bInterfaceProtocol
82 interface_descriptor->AddByte(device()->AddString("Hid")); // iInterface
83 }
84
85 AddPremadeDescriptor(kHidDescriptor, sizeof(kHidDescriptor));
86
87 {
88 const auto endpoint_descriptor = CreateDescriptor(
89 endpoint_descriptor_length(), UsbDescriptorType::kEndpoint);
90 endpoint_descriptor->AddByte(in_endpoint_ |
91 m_endpoint_address_in()); // bEndpointAddress
92 endpoint_descriptor->AddByte(
93 m_endpoint_attributes_interrupt()); // bmAttributes
94 endpoint_descriptor->AddUint16(in_endpoint_max_size()); // wMaxPacketSize
Brian Silverman7c7170e2018-01-13 17:41:21 -080095 endpoint_descriptor->AddByte(0x8); // bInterval
Brian Silvermand930f282017-11-04 23:09:12 -040096 }
97}
98
99UsbFunction::SetupResponse HidFunction::HandleEndpoint0SetupPacket(
100 const UsbDevice::SetupPacket &setup_packet) {
101 if (G_SETUP_REQUEST_TYPE_TYPE(setup_packet.request_type) !=
102 SetupRequestType::kClass) {
103 return SetupResponse::kIgnored;
104 }
105 if (G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type) !=
106 standard_setup_recipients::kInterface) {
107 return SetupResponse::kIgnored;
108 }
109 if (setup_packet.index != interface_) {
110 return SetupResponse::kIgnored;
111 }
112 const bool in = setup_packet.request_type & M_SETUP_REQUEST_TYPE_IN;
113 switch (setup_packet.request) {
114 case hid_class_requests::get_report():
115 if (!in) {
116 return SetupResponse::kStall;
117 }
118 // If it's not requesting the only Input report, no idea what the host
119 // wants so stall.
120 if (setup_packet.value != 0x0100) {
121 return SetupResponse::kStall;
122 }
123 {
124 DisableInterrupts disable_interrupts;
125 memcpy(get_report_response_buffer_.data(),
126 report_tx_buffer_being_sent(disable_interrupts), kMaxReportSize);
127 }
128 device()->QueueEndpoint0Data(
129 reinterpret_cast<const char *>(get_report_response_buffer_.data()),
130 ::std::min<uint16_t>(setup_packet.length, report_max_size_));
131 return SetupResponse::kHandled;
132
133 case hid_class_requests::set_idle():
134 // Minimum implementation to make the host stack happy.
135 if (in) {
136 return SetupResponse::kStall;
137 }
138 device()->SendEmptyEndpoint0Packet();
139 return SetupResponse::kHandled;
140
141 // TODO(Brian): Should we actually implement the idle stuff?
142
143 default:
144 return SetupResponse::kStall;
145 }
146}
147
148UsbFunction::SetupResponse HidFunction::HandleGetDescriptor(
149 const UsbDevice::SetupPacket &setup_packet) {
150 const uint8_t recipient =
151 G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type);
152 if (recipient != standard_setup_recipients::kInterface) {
153 return SetupResponse::kIgnored;
154 }
155
156 const uint8_t descriptor_type = (setup_packet.value >> 8) & 0xFF;
157 if (G_DESCRIPTOR_TYPE_TYPE(descriptor_type) !=
158 standard_descriptor_type_types::kClass) {
159 return SetupResponse::kIgnored;
160 }
161 if (setup_packet.index != interface_) {
162 return SetupResponse::kIgnored;
163 }
164
165 const uint8_t descriptor_index = setup_packet.value & 0xFF;
166 switch (descriptor_type) {
167 case static_cast<uint8_t>(UsbClassDescriptorType::kHidHid):
168 if (descriptor_index != 0) {
169 return SetupResponse::kStall;
170 }
171 device()->QueueEndpoint0Data(
172 reinterpret_cast<const char *>(kHidDescriptor),
173 ::std::min<int>(setup_packet.length, sizeof(kHidDescriptor)));
174 return SetupResponse::kHandled;
175
176 case static_cast<uint8_t>(UsbClassDescriptorType::kHidReport):
177 if (descriptor_index != 0) {
178 return SetupResponse::kStall;
179 }
180 device()->QueueEndpoint0Data(
181 reinterpret_cast<const char *>(kReportDescriptor),
182 ::std::min<int>(setup_packet.length, sizeof(kReportDescriptor)));
183 return SetupResponse::kHandled;
184
185 case static_cast<uint8_t>(UsbClassDescriptorType::kHidPhysical):
186 static constexpr char kNoPhysicalDescriptors[] = {0, 0, 0};
187 device()->QueueEndpoint0Data(
188 kNoPhysicalDescriptors,
189 ::std::min<int>(setup_packet.length, sizeof(kNoPhysicalDescriptors)));
190 return SetupResponse::kHandled;
191 }
192 return SetupResponse::kStall;
193}
194
195void HidFunction::HandleInFinished(int endpoint, BdtEntry * /*bdt_entry*/,
196 EvenOdd odd) {
197 if (endpoint == in_endpoint_) {
198 DisableInterrupts disable_interrupts;
199 if (odd != BufferStateToEmpty(tx_state_)) {
200 __builtin_trap();
201 }
202
203 // Copy the current one into the just-sent buffer.
204 memcpy(report_tx_buffer_being_sent(disable_interrupts),
205 report_tx_buffer_to_fill(disable_interrupts), kMaxReportSize);
206
207 dma_memory_barrier();
208 device()->SetBdtEntry(
209 in_endpoint_, Direction::kTx, BufferStateToFill(tx_state_),
210 {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) |
211 static_cast<uint32_t>(next_tx_toggle_),
212 report_tx_buffer_to_fill(disable_interrupts)});
213
214 // Advance the state to indicate we've swapped buffers.
215 tx_state_ = BufferStateAfterFill(BufferStateAfterEmpty(tx_state_));
216 next_tx_toggle_ = Data01Inverse(next_tx_toggle_);
217 }
218}
219
220void HidFunction::HandleConfigured(int endpoint) {
221 if (endpoint == in_endpoint_) {
222 device()->ConfigureEndpointFor(in_endpoint_, false, true, true);
223 DisableInterrupts disable_interrupts;
224 next_tx_toggle_ = Data01::kData0;
225
226 EvenOdd to_fill;
227 if (BufferStateHasFull(tx_state_)) {
228 to_fill = BufferStateToEmpty(tx_state_);
229 } else {
230 to_fill = BufferStateToFill(tx_state_);
231 tx_state_ = BufferStateAfterFill(tx_state_);
232 }
233
234 dma_memory_barrier();
235 device()->SetBdtEntry(
236 in_endpoint_, Direction::kTx, to_fill,
237 {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) |
238 static_cast<uint32_t>(next_tx_toggle_),
239 report_tx_buffer_to_fill(disable_interrupts)});
240 next_tx_toggle_ = Data01Inverse(next_tx_toggle_);
241 }
242}
243
244} // namespace teensy
245} // namespace frc971