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