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 | |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 18 | } // namespace |
| 19 | |
| 20 | void HidFunction::Initialize() { |
| 21 | interface_ = AddInterface(); |
| 22 | in_endpoint_ = AddEndpoint(); |
| 23 | |
Brian Silverman | 4aa8304 | 2018-01-05 12:47:31 -0800 | [diff] [blame] | 24 | CreateIadDescriptor( |
| 25 | /*first_interface=*/interface_, |
| 26 | /*interface_count=*/1, |
| 27 | /*function_class=*/hid_class(), |
| 28 | /*function_subclass=*/0, |
| 29 | /*function_protocol=*/0, "HidIad"); |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 30 | |
| 31 | { |
| 32 | const auto interface_descriptor = CreateDescriptor( |
| 33 | interface_descriptor_length(), UsbDescriptorType::kInterface); |
| 34 | interface_descriptor->AddByte(interface_); // bInterfaceNumber |
| 35 | interface_descriptor->AddByte(0); // bAlternateSetting |
| 36 | interface_descriptor->AddByte(1); // bNumEndpoints |
| 37 | interface_descriptor->AddByte(hid_class()); // bInterfaceClass |
| 38 | interface_descriptor->AddByte(0); // bInterfaceSubClass |
| 39 | interface_descriptor->AddByte(0); // bInterfaceProtocol |
| 40 | interface_descriptor->AddByte(device()->AddString("Hid")); // iInterface |
| 41 | } |
| 42 | |
Brian Silverman | 587edcc | 2018-01-15 12:00:22 -0800 | [diff] [blame] | 43 | { |
| 44 | const auto hid_descriptor = hid_descriptor_list_.CreateDescriptor( |
| 45 | 9, UsbClassDescriptorType::kHidHid); |
| 46 | hid_descriptor->AddUint16(0x0110); // bcdHID |
| 47 | hid_descriptor->AddByte(0); // bCountryCode |
| 48 | hid_descriptor->AddByte(1); // bNumDescriptors |
| 49 | hid_descriptor->AddByte(static_cast<uint8_t>( |
| 50 | UsbClassDescriptorType::kHidReport)); // bDescriptorType |
| 51 | hid_descriptor->AddUint16(report_descriptor_.size()); // wDescriptorLength |
| 52 | } |
| 53 | AddPremadeDescriptor(hid_descriptor_list_); |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 54 | |
| 55 | { |
| 56 | const auto endpoint_descriptor = CreateDescriptor( |
| 57 | endpoint_descriptor_length(), UsbDescriptorType::kEndpoint); |
| 58 | endpoint_descriptor->AddByte(in_endpoint_ | |
| 59 | m_endpoint_address_in()); // bEndpointAddress |
| 60 | endpoint_descriptor->AddByte( |
| 61 | m_endpoint_attributes_interrupt()); // bmAttributes |
| 62 | endpoint_descriptor->AddUint16(in_endpoint_max_size()); // wMaxPacketSize |
Brian Silverman | 7c7170e | 2018-01-13 17:41:21 -0800 | [diff] [blame] | 63 | endpoint_descriptor->AddByte(0x8); // bInterval |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 64 | } |
| 65 | } |
| 66 | |
| 67 | UsbFunction::SetupResponse HidFunction::HandleEndpoint0SetupPacket( |
| 68 | const UsbDevice::SetupPacket &setup_packet) { |
| 69 | if (G_SETUP_REQUEST_TYPE_TYPE(setup_packet.request_type) != |
| 70 | SetupRequestType::kClass) { |
| 71 | return SetupResponse::kIgnored; |
| 72 | } |
| 73 | if (G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type) != |
| 74 | standard_setup_recipients::kInterface) { |
| 75 | return SetupResponse::kIgnored; |
| 76 | } |
| 77 | if (setup_packet.index != interface_) { |
| 78 | return SetupResponse::kIgnored; |
| 79 | } |
| 80 | const bool in = setup_packet.request_type & M_SETUP_REQUEST_TYPE_IN; |
| 81 | switch (setup_packet.request) { |
| 82 | case hid_class_requests::get_report(): |
| 83 | if (!in) { |
| 84 | return SetupResponse::kStall; |
| 85 | } |
| 86 | // If it's not requesting the only Input report, no idea what the host |
| 87 | // wants so stall. |
| 88 | if (setup_packet.value != 0x0100) { |
| 89 | return SetupResponse::kStall; |
| 90 | } |
| 91 | { |
| 92 | DisableInterrupts disable_interrupts; |
| 93 | memcpy(get_report_response_buffer_.data(), |
| 94 | report_tx_buffer_being_sent(disable_interrupts), kMaxReportSize); |
| 95 | } |
| 96 | device()->QueueEndpoint0Data( |
| 97 | reinterpret_cast<const char *>(get_report_response_buffer_.data()), |
| 98 | ::std::min<uint16_t>(setup_packet.length, report_max_size_)); |
| 99 | return SetupResponse::kHandled; |
| 100 | |
| 101 | case hid_class_requests::set_idle(): |
| 102 | // Minimum implementation to make the host stack happy. |
| 103 | if (in) { |
| 104 | return SetupResponse::kStall; |
| 105 | } |
| 106 | device()->SendEmptyEndpoint0Packet(); |
| 107 | return SetupResponse::kHandled; |
| 108 | |
| 109 | // TODO(Brian): Should we actually implement the idle stuff? |
| 110 | |
| 111 | default: |
| 112 | return SetupResponse::kStall; |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | UsbFunction::SetupResponse HidFunction::HandleGetDescriptor( |
| 117 | const UsbDevice::SetupPacket &setup_packet) { |
| 118 | const uint8_t recipient = |
| 119 | G_SETUP_REQUEST_TYPE_RECIPIENT(setup_packet.request_type); |
| 120 | if (recipient != standard_setup_recipients::kInterface) { |
| 121 | return SetupResponse::kIgnored; |
| 122 | } |
| 123 | |
| 124 | const uint8_t descriptor_type = (setup_packet.value >> 8) & 0xFF; |
| 125 | if (G_DESCRIPTOR_TYPE_TYPE(descriptor_type) != |
| 126 | standard_descriptor_type_types::kClass) { |
| 127 | return SetupResponse::kIgnored; |
| 128 | } |
| 129 | if (setup_packet.index != interface_) { |
| 130 | return SetupResponse::kIgnored; |
| 131 | } |
| 132 | |
| 133 | const uint8_t descriptor_index = setup_packet.value & 0xFF; |
| 134 | switch (descriptor_type) { |
| 135 | case static_cast<uint8_t>(UsbClassDescriptorType::kHidHid): |
| 136 | if (descriptor_index != 0) { |
| 137 | return SetupResponse::kStall; |
| 138 | } |
| 139 | device()->QueueEndpoint0Data( |
Brian Silverman | 587edcc | 2018-01-15 12:00:22 -0800 | [diff] [blame] | 140 | hid_descriptor_list_.GetData(), |
| 141 | ::std::min<int>(setup_packet.length, |
| 142 | hid_descriptor_list_.CurrentSize())); |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 143 | return SetupResponse::kHandled; |
| 144 | |
| 145 | case static_cast<uint8_t>(UsbClassDescriptorType::kHidReport): |
| 146 | if (descriptor_index != 0) { |
| 147 | return SetupResponse::kStall; |
| 148 | } |
| 149 | device()->QueueEndpoint0Data( |
Brian Silverman | 587edcc | 2018-01-15 12:00:22 -0800 | [diff] [blame] | 150 | report_descriptor_.data(), |
| 151 | ::std::min<int>(setup_packet.length, report_descriptor_.size())); |
Brian Silverman | d930f28 | 2017-11-04 23:09:12 -0400 | [diff] [blame] | 152 | return SetupResponse::kHandled; |
| 153 | |
| 154 | case static_cast<uint8_t>(UsbClassDescriptorType::kHidPhysical): |
| 155 | static constexpr char kNoPhysicalDescriptors[] = {0, 0, 0}; |
| 156 | device()->QueueEndpoint0Data( |
| 157 | kNoPhysicalDescriptors, |
| 158 | ::std::min<int>(setup_packet.length, sizeof(kNoPhysicalDescriptors))); |
| 159 | return SetupResponse::kHandled; |
| 160 | } |
| 161 | return SetupResponse::kStall; |
| 162 | } |
| 163 | |
| 164 | void HidFunction::HandleInFinished(int endpoint, BdtEntry * /*bdt_entry*/, |
| 165 | EvenOdd odd) { |
| 166 | if (endpoint == in_endpoint_) { |
| 167 | DisableInterrupts disable_interrupts; |
| 168 | if (odd != BufferStateToEmpty(tx_state_)) { |
| 169 | __builtin_trap(); |
| 170 | } |
| 171 | |
| 172 | // Copy the current one into the just-sent buffer. |
| 173 | memcpy(report_tx_buffer_being_sent(disable_interrupts), |
| 174 | report_tx_buffer_to_fill(disable_interrupts), kMaxReportSize); |
| 175 | |
| 176 | dma_memory_barrier(); |
| 177 | device()->SetBdtEntry( |
| 178 | in_endpoint_, Direction::kTx, BufferStateToFill(tx_state_), |
| 179 | {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) | |
| 180 | static_cast<uint32_t>(next_tx_toggle_), |
| 181 | report_tx_buffer_to_fill(disable_interrupts)}); |
| 182 | |
| 183 | // Advance the state to indicate we've swapped buffers. |
| 184 | tx_state_ = BufferStateAfterFill(BufferStateAfterEmpty(tx_state_)); |
| 185 | next_tx_toggle_ = Data01Inverse(next_tx_toggle_); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | void HidFunction::HandleConfigured(int endpoint) { |
| 190 | if (endpoint == in_endpoint_) { |
| 191 | device()->ConfigureEndpointFor(in_endpoint_, false, true, true); |
| 192 | DisableInterrupts disable_interrupts; |
| 193 | next_tx_toggle_ = Data01::kData0; |
| 194 | |
| 195 | EvenOdd to_fill; |
| 196 | if (BufferStateHasFull(tx_state_)) { |
| 197 | to_fill = BufferStateToEmpty(tx_state_); |
| 198 | } else { |
| 199 | to_fill = BufferStateToFill(tx_state_); |
| 200 | tx_state_ = BufferStateAfterFill(tx_state_); |
| 201 | } |
| 202 | |
| 203 | dma_memory_barrier(); |
| 204 | device()->SetBdtEntry( |
| 205 | in_endpoint_, Direction::kTx, to_fill, |
| 206 | {M_USB_BD_OWN | M_USB_BD_DTS | V_USB_BD_BC(report_max_size_) | |
| 207 | static_cast<uint32_t>(next_tx_toggle_), |
| 208 | report_tx_buffer_to_fill(disable_interrupts)}); |
| 209 | next_tx_toggle_ = Data01Inverse(next_tx_toggle_); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | } // namespace teensy |
| 214 | } // namespace frc971 |