| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_UI_INPUT_DRIVERS_PC_PS2_DEVICE_H_ |
| #define SRC_UI_INPUT_DRIVERS_PC_PS2_DEVICE_H_ |
| |
| #include <fidl/fuchsia.hardware.input/cpp/wire.h> |
| #include <fidl/fuchsia.input.report/cpp/wire.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/zx/interrupt.h> |
| |
| #include <condition_variable> |
| |
| #include <ddktl/device.h> |
| #include <ddktl/protocol/empty-protocol.h> |
| #include <ddktl/unbind-txn.h> |
| #include <hid/boot.h> |
| |
| #include "src/ui/input/drivers/pc-ps2/controller.h" |
| #include "src/ui/input/lib/input-report-reader/reader.h" |
| |
| namespace i8042 { |
| |
| struct PS2KbdInputReport { |
| size_t num_pressed_keys_3 = 0; |
| fuchsia_input::wire::Key pressed_keys_3[fuchsia_input_report::wire::kKeyboardMaxNumKeys]; |
| |
| void Reset() { num_pressed_keys_3 = 0; } |
| }; |
| |
| struct PS2MouseInputReport { |
| uint8_t buttons; |
| int8_t rel_x; |
| int8_t rel_y; |
| |
| void Reset() { |
| buttons = 0; |
| rel_x = 0; |
| rel_y = 0; |
| } |
| }; |
| |
| struct PS2InputReport { |
| zx::time event_time; |
| fuchsia_hardware_input::BootProtocol type; |
| std::variant<PS2KbdInputReport, PS2MouseInputReport> report; |
| |
| void ToFidlInputReport(fuchsia_input_report::wire::InputReport& input_report, |
| fidl::AnyArena& allocator); |
| void Reset() { |
| event_time = {}; |
| type = fuchsia_hardware_input::BootProtocol::kNone; |
| if (std::holds_alternative<PS2KbdInputReport>(report)) { |
| std::get<PS2KbdInputReport>(report).Reset(); |
| } else if (std::holds_alternative<PS2MouseInputReport>(report)) { |
| std::get<PS2MouseInputReport>(report).Reset(); |
| } |
| } |
| }; |
| |
| enum ModStatus { |
| kSet = 1, |
| kExists = 2, |
| kRollover = 3, |
| }; |
| |
| enum KeyStatus { |
| kKeyAdded = 1, |
| kKeyExists = 2, |
| kKeyRollover = 3, |
| kKeyRemoved = 4, |
| kKeyNotFound = 5, |
| }; |
| constexpr uint8_t kAck = 0xfa; |
| |
| class I8042Device; |
| using DeviceType = ddk::Device<I8042Device, ddk::Unbindable, |
| ddk::Messageable<fuchsia_input_report::InputDevice>::Mixin>; |
| class I8042Device : public DeviceType, public ddk::EmptyProtocol<ZX_PROTOCOL_INPUTREPORT> { |
| public: |
| explicit I8042Device(Controller* parent, Port port) |
| : DeviceType(parent->zxdev()), |
| controller_(parent), |
| port_(port), |
| loop_(&kAsyncLoopConfigNoAttachToCurrentThread), |
| report_({ |
| .event_time = {}, |
| .type = fuchsia_hardware_input::BootProtocol::kNone, |
| }) {} |
| |
| static zx_status_t Bind(Controller* parent, Port port); |
| zx_status_t Bind(); |
| |
| void DdkRelease() { |
| irq_thread_.join(); |
| delete this; |
| } |
| void DdkUnbind(ddk::UnbindTxn txn); |
| |
| void GetInputReportsReader(GetInputReportsReaderRequestView request, |
| GetInputReportsReaderCompleter::Sync& completer) override; |
| void GetDescriptor(GetDescriptorRequestView request, |
| GetDescriptorCompleter::Sync& completer) override; |
| void SendOutputReport(SendOutputReportRequestView request, |
| SendOutputReportCompleter::Sync& completer) override { |
| completer.ReplyError(ZX_ERR_NOT_SUPPORTED); |
| } |
| void GetFeatureReport(GetFeatureReportRequestView request, |
| GetFeatureReportCompleter::Sync& completer) override { |
| completer.ReplyError(ZX_ERR_NOT_SUPPORTED); |
| } |
| void SetFeatureReport(SetFeatureReportRequestView request, |
| SetFeatureReportCompleter::Sync& completer) override { |
| completer.ReplyError(ZX_ERR_NOT_SUPPORTED); |
| } |
| void GetInputReport(GetInputReportRequestView request, |
| GetInputReportCompleter::Sync& completer) override { |
| completer.ReplyError(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| #ifdef PS2_TEST |
| zx_status_t WaitForNextReader(zx::duration timeout) { |
| zx_status_t status = sync_completion_wait(&next_reader_wait_, timeout.get()); |
| if (status == ZX_OK) { |
| sync_completion_reset(&next_reader_wait_); |
| } |
| return status; |
| } |
| #endif |
| |
| private: |
| Controller* controller_; |
| Port port_; |
| fuchsia_hardware_input::wire::BootProtocol protocol_; |
| std::thread irq_thread_; |
| zx::interrupt irq_; |
| std::mutex unbind_lock_; |
| std::condition_variable_any unbind_ready_; |
| std::optional<ddk::UnbindTxn> unbind_ __TA_GUARDED(unbind_lock_); |
| |
| std::mutex hid_lock_; |
| input::InputReportReaderManager<PS2InputReport> input_report_readers_ __TA_GUARDED(hid_lock_); |
| #ifdef PS2_TEST |
| sync_completion_t next_reader_wait_; |
| #endif |
| async::Loop loop_; |
| |
| uint8_t last_code_ = 0; |
| PS2InputReport report_; |
| PS2KbdInputReport& keyboard_report() { |
| ZX_ASSERT(std::holds_alternative<PS2KbdInputReport>(report_.report)); |
| return std::get<PS2KbdInputReport>(report_.report); |
| } |
| PS2MouseInputReport& mouse_report() { |
| ZX_ASSERT(std::holds_alternative<PS2MouseInputReport>(report_.report)); |
| return std::get<PS2MouseInputReport>(report_.report); |
| } |
| |
| zx::status<fuchsia_hardware_input::wire::BootProtocol> Identify(); |
| void IrqThread(); |
| // Keyboard input |
| void ProcessScancode(zx::time timestamp, uint8_t code); |
| KeyStatus AddKey(fuchsia_input::wire::Key key); |
| KeyStatus RemoveKey(fuchsia_input::wire::Key key); |
| // Mouse input |
| void ProcessMouse(zx::time timestamp, uint8_t code); |
| }; |
| |
| } // namespace i8042 |
| |
| #endif // SRC_UI_INPUT_DRIVERS_PC_PS2_DEVICE_H_ |