PS2 GPIO: Added initial structure
PS2 GPIO: Added interrupt handler and pin reading/writing helper functions
PS2 GPIO: Added reading mode
PS2 GPIO: Add zephyr PS2 API support for callback and read function
PS2 GPIO: Fix reading not working with real PS2 device
PS2 GPIO: Added write with separate edge rising callback
PS2 GPIO: Made single interrupt handler
PS2 GPIO: Switch to inactive and falling handler only
PS2 GPIO: Switch back to edge falling
PS2 GPIO: Working without writing
PS2 GPIO: Enable both edges (working)
PS2 GPIO: Somewhat working write
PS2 GPIO: Write with ack working, but read always wrong stop bit
PS2 GPIO: Cleanup code
PS2 GPIO: Fix write not working by increasing clock holding time
PS2 GPIO: Adde scl clock timeout for reads
PS2 GPIO: Add scl clock timeout for writes
PS2 GPIO: Moved pin config into separate functions (not working)
PS2 GPIO: Make read work by removing some logging
PS2 GPIO: Fixed memory issue with fifo
PS2 GPIO: Kind of working sometimes except for ack bit
PS2 GPIO: Working without ack
PS2 GPIO: Fix crash: Disable resend command in read abort
PS2 GPIO: Make write work (But next read fails)
PS2 GPIO:
PS2 GPIO: I think the next read fails because we swallow the first 0 bit when reading the ack.
PS2 GPIO: Disable logging go speed up processing
PS2 GPIO: Made writing mostly work; read afterwards inconsistent
PS2 GPIO: Added post write debug log
PS2 GPIO: Improved post-write reading success rate
PS2 GPIO: Adjust timings
PS2 GPIO: Added logging of time between interrupts
PS2 GPIO: Instead of sleep() for write line inhibition, do delayable work
PS2 GPIO: Compensate for missing stop bit in read when it happens directly after write
PS2 GPIO: Adding interrupt logging
PS2 GPIO: Fix set_scl() setting sda and set_sda() setting scl
PS2 GPIO: Make interrupt logging work for sends
PS2 GPIO:
PS2 GPIO: Add interrupt logging to read functions
PS2 GPIO: Add ability to disable interrupt callback during write inhibition phase
PS2 GPIO: Rename inhibit slc duration config option
PS2 GPIO:
PS2 GPIO: Add missing inhibit setting rename
PS2 GPIO: Improve handling of spurious interrupts after writing better
PS2 GPIO:
PS2 GPIO: Adjust scl timeouts
PS2 GPIO: Added logging helper
PS2 GPIO:
PS2 GPIO: Logging helper
PS2 GPIO: Start fixing original write functions
PS2 GPIO: Cleanup read code
PS2 GPIO: Cleanup write code
PS2 GPIO: Adjust read/write scl timeouts
PS2 GPIO: Disable logger
PS2 GPIO: Add custom interrupt logging
PS2 GPIO: Enable interrupt logger
PS2 GPIO: Added separate write scl timeout for the first clock
PS2 GPIO: Fix running out of memory due to read queue
PS2 GPIO: Allow disabling of interrupt logs
PS2 GPIO: Improve logging and comments
PS2 GPIO: Removed old interrupt code
PS2 GPIO:
PS2 GPIO: interrupt stuff
PS2 GPIO:
PS2 GPIO: x
PS 2 Mouse: Add initial mouse driver for testing PS2 driver
PS2 GPIO: Add ps2 device to callback
PS2 Mouse: Added initial mouse movement cmd parsing
PS2 Mouse: Added actual mouse movement
PS2 Mouse:
PS2 Mouse: Inverted y for zmk as that is what zmk thinks is up
PS2 Mouse:
PS2 Mouse: comm
PS2 Mouse: Changed config options to match zmk mouse naming
PS2 Mouse: Added options to invert x and y mouse movements
PS2 Mouse: Rename mouse movement to mouse activity
PS2 Mouse: Added mouse button support
PS2 GPIO: Small cleanup
PS2 GPIO: Move interrupt logging around
PS2 GPIO: Added write re-try on fail
PS2 GPIO: Initial attempt at write await response (not working)
PS2 GPIO: Revert "Initial attempt at write await response (not working)"
PS2 GPIO:
PS2 GPIO: This reverts commit 6e77047ba8f8537e076f3a583d03c6222874998c.
PS2 GPIO: Added reason do abort_read()
PS2 GPIO: Disable resend on read failure for debugging
PS2 GPIO: Initialize current write byte
PS2 GPIO: Added max read retry
PS2 GPIO: Disable log interrupt
PS2 GPIO: Made timings based on ps2 protocol defines
PS2 GPIO:
PS2 GPIO: Move settings
PS2 GPIO: Added write_byte_await_response()
PS2 GPIO: Fix write retry incorrect retry number log
PS2 Mouse: Sort defines
PS2 Mouse: Improve init sequence
PS2 Mouse: Removed unneeded stream mode enabling command
PS2 GPIO: Make no response in write_byte_await_response an failure
PS2 Mouse: During init send reset command on every 4th attempt
PS2 Mouse: Add sample rate setting support
PS2 Mouse: Start use mouse events for movements
PS2 GPIO: Change most logging to debug
PS2 Mouse: Change logs from inf to dbg
PS2 Mouse: Experiment with different mouse movement methods
PS2 Mouse: Report mouse activity through timer instead of as it arrives.
PS2 Mouse: Use zmk log level instead of ps2
PS2 GPIO: Moved retry logic into separate function that also resends on error resp from device
PS2 GPIO:
PS2 GPIO: Enable retry send for ps2 interface
PS2 GPIO:
PS2 GPIO: Fix write failure error code
PS2 GPIO: Cleanup write code slightly
PS2 GPIO: Disable interrupt logging
PS2 GPIO: Use just one function for parity checking
PS2 GPIO: Small cleanup of read and write
PS2 GPIO:
PS2 GPIO: Small comment improvement
PS2 GPIO: In read_interrupt, moved data bits to a more logical position
PS2 GPIO:
PS2 GPIO: Read interrupt fix
PS2 GPIO: Call zephyr PS2 callback from a worker
PS2 GPIO: Remove unneeded gpio config flags
PS2 GPIO: Allow to enable and disable interrupt log through kconfig
PS2 Mouse: Re-enable resend request due to packet misalignment
PS2 Mouse: Parse cmd buffer using a struct instead of pointers
PS2 Mouse: Cleanup and improve documentation
PS2 Mouse: Silence error on read data queue timeout
PS2 Mouse: Improved init sequence
PS2 Mouse: Move init functions around
PS2 Mouse: Added scroll mode
PS2 Mouse: Make scroll mode configurable
PS2 Mouse: Added functions to incr and decr sampling rate
PS2 GPIO: Update blocking timeout for retry changes
PS2 GPIO: Increased timeout for write_byte_await_response()
PS2 GPIO: Added custom work queue
PS2 Mouse: Added mouse settings behavior (with queue)
PS2 Mouse: Removed queue from mouse setting behavior
PS2 Mouse:
PS2 Mouse: A queue was added to the ps2 gpio driver itself. So there is no need for it here anymore.
PS2 GPIO: Don’t log error on timed out ps2_read()
PS2 Mouse: Added reset-on-power function for trackpoints
PS2 Mouse: Add command to check if device is trackpoint
PS2 Mouse: Added send_cmd function that can send multibyte commands and read response
PS2 Mouse: Added trackpoint press to select and sensitivity setting
PS2 Mouse: Add config option for tap to select
PS2 Mouse: Fixed power-on-reset
PS2 Mouse: Add trackpoint swap xy command
PS2 Mouse: Add swap xy setting
PS2 Mouse: Added option to invert x and y directly on the trackpoint
PS2 Mouse: Added negative inertia setting
PS2 Mouse: Added upper plateau speed (value6)
PS2 Mouse: Added press-to-select threshold
PS2 Mouse: Added keybindings for TP settings
PS2 GPIO: Increased log break time
PS2 GPIO: Re-enable re-send request on read error
PS2 GPIO: Fix outdated comment
PS2 GPIO: Reduce debug logging
PS2 Mouse: Reduce logging
PS2 Mouse: Added config option to disable clicking
PS2 Mouse: Added config option for sampling rate
PS2 GPIO: Automatically adjust BT priorities when PS2 GPIO driver is enabled
PS2 Mouse: Lower default priority of NRF IRQs
PS2 Mouse: Add out of alignment detection
PS2 Mouse: Simplify out of alignment check
PS2 GPIO: Fixed deadlock on write due to wrong wokr queue priorities
PS2 GPIO: Log reason for write finish
PS2 GPIO: Increase SCL timeout
PS2 GPIO: Add pos to abort read logging
PS2 GPIO: For writes read ack bit before interrupt logging
PS2 GPIO: Fix interrupt_log_add crash
PS2 GPIO: Make interrupt_log. log in background
PS2 GPIO: Decrease priority for inhibition wait thread
PS2 GPIO: Increase timeout for first write clock
PS2 GPIO: Add argument to interrupt_log
PS2 GPIO: Improve write debug logging
PS2 Mouse: Improve misalignment detection by checking if there was prev movement
PS2 Mouse: Fix crash caused by zephyr LOG bug due to transient strings
PS2 Mouse:
PS2 Mouse: https://github.com/zephyrproject-rtos/zephyr/issues/44996
PS2 GPIO: Use main write function for 0xfe retry cmd
PS2 GPIO: Add resend callback to gpio driver
PS2 GPIO: Increase inhibition delay timer
PS2 GPIO: Properly fail write if semaphore times out
PS2 Mouse: Added resend callback
PS2 Mouse: Fix TP incr/decrease not remembering prev value
PS2 Mouse: Reset cmd buffer on misalignment
PS2 Mouse: Re-enable abort on sharp movement
PS2 Mouse: Fix mouse scrolling unintendedly if mouse mode is disabled
PS2 Mouse: Log button presses as info
PS2 GPIO: Re-enable scl interrupt on write failure
PS2 GPIO: Increase write max retry
PS2 Mouse: Make compatible with zephyr 3.2 update
PS2 GPIO: Make compatible with zephyr 3.2
PS2 GPIO: Use new zephyr gpio_*_dt apis
PS2 Mouse: Use new zephyr gpio_*_dt apis
PS2 GPIO: Prevent lockout on resend by using cb worker
PS2 GPIO: Add missing interrupt detection
PS2 GPIO: Added write error code for semaphore timeout
PS2 GPIO: Added mutex to prevent multiple threads writing at once
PS2 Mouse: Treat packet overflow as mistransmission
PS2 Mouse: If multiple mouse buttons are changed in one packet, treat it as mistransmission
PS2 Mouse: Fix value6 functions actually setting neg inertia
PS2 Mouse: Remove kconfig setting for mouse
PS2 Mouse: Reordered includes
PS2 Mouse: Switched tp sensitivity setting from float to int
PS2 Mouse: Added setting saving and restoring
PS2 Mouse: Created proper, automatic Kconfig
PS2 Mouse: Reorder includes
PS2 GPIO: Enable PS2 GPIO driver automatically if the devicetree node is present
PS2 Mouse: Rename ps2_mouse to mouse_ps2
PS2 Mouse: Fully rename ps2_mouse to mouse_ps2
PS2 Mouse: Cleanup of mouse_ps2 function naming and order
PS2 Mouse: Renamed activity cmd to packet
PS2 Mouse: Removed sampling rate from being set from keypresses
PS2 Mouse: Remove redudant decr/incr functions
PS2 Mouse: Rename array functions
PS2 Mouse: Ensure all functions have common prefix zmk_mouse_ps2
PS2 Mouse: Reduce logging
PS2 GPIO: Make sure all functions have the ps2_gpio prefix
PS2 UART: Added initial UART PS2 driver
PS2 UART: Made uart PS2 read work
PS2 UART: Refactored uart read code
PS2 UART: Added parity error checking
PS2 UART: Initial attempt at pincntrl switching
Revert "PS2 UART: Initial attempt at pincntrl switching"
This reverts commit 5538731a90031b7607f05775135385bbe14464dc.
PS2 UART: Initial version of GPIO write (not working)
PS2 UART: Kindoff working
PS2 UART: Working
PS2 UART: Small cleanup
PS2 UART: Adjust timings and improve comments
PS2 Mouse: Increase init priority to be after UART drivers
PS2 UART: Attempt to not block interrupt (not working, worker never called)
Revert "PS2 UART: Attempt to not block interrupt (not working, worker never called)"
This reverts commit 6c37f9a236137cc0ca873c6ee4afef3f69eb5708.
PS2 Mouse: Add config option to disable additional error mitigation
Auto Layer: Add automatic layer toggling on mouse movement
PS2 Mouse: Log transmission error reason
PS2 UART: Check read error in received_byte
PS2 UART: Added blocking write
Auto Layer: Fixes
Scroll Mode: Initial implementation
PS2 Mouse: Auto lint code
PS2 Mouse: Scroll mode improvements (uncommitted changes from 3 month ago)
PS2 GPIO: Apply new auto-format to ps2_gpio
PS2 UART: Apply new auto-format
PS2 GPIO: Make write mutex names unique
PS2 UART: Make write mutex names unique
PS2 GPIO: Fix zephyr 3.5 compile errors
PS2 UART: Fix zephyr 3.5 compile errors
Mouse Settings & Scroll Mode: Apply new auto-formatting
Mouse Settings: Silence zephyr 3.5 compile warnings
PS2 Mouse: Move to zephyr 3.5 input system
PS2 Mouse: Add mouse ps2 work queue
PS2 Mouse: Increase err_msg buffer size for zmk_mouse_ps2_send_cmd_resp to avoid truncating
PS2 UART: Attempt adding pinctrl states dynamically (unsuccessful)
Revert "PS2 UART: Attempt adding pinctrl states dynamically (unsuccessful)"
This reverts commit 1a2c3f6f6832a95a828675a093bef7070fbe5bac.
PS2 UART: Fix zephyr 3.5 pinctrl change error
the issue was that I was using the sleep pinctrl state, because I couldn't figure out how to create a custom state.
And in zephyr 3.2 that worked fine, but in 3.5 it disables the sleep state if CONFIG_PM_DEVICE=n.
So the state was missing.
Mouse Settings & Scroll Mode: Update mouse settings and scroll mode behaviors to new labelless behavior init method
PS2 Mouse: Disable restore of mouse PS2 settings
Mouse Auto Layer: Added toggle delay to prevent accidental activations
PS2 Mouse: Added script to generate interrupt priority overrides for zmk-config
PS2 Mouse: Don’t override interrupt priorities in all zmk builds by default
PS2 GPIO: Potentially fix SCL not being pulled down during write
PS2 UART: Potentially fix inhibition not being done correctly
PS2 Mouse: Reset device if invalid self-test result is received
PS2 UART: Fix PM_DEVICE not enabling
PS2 GPIO: Improve PS2 GPIO kconfig documentation
PS2 UART: Lower BT interrupt priorities
PS2 Mouse: Delay PS2 init sequence to give mouse time to send init data
PS2 UART: Fix first write not working
PS2 UART: Add debug write function
PS2 UART: Disable debug write function
PS2 UART: Log received bytes
PS2 UART: Use delayable instead of k_busy_wait
Revert "PS2 UART: Use delayable instead of k_busy_wait"
Realized that this is pointless, since that’s a blocking function anyways.
We need to not block in the interrupt handler rather than here.
This reverts commit eee0c35793ccccf40d72742d19cc85762423a103.
PS2 UART: Move scl interrupt into worker
PS2 UART: Add cur write pos
Revert "PS2 UART: Move scl interrupt into worker"
This reverts commit ea24c68b1af0f6e4a367622829d92f380d92a27f.
PS2 UART: Added async interrupt writing mode
PS2 UART: Ignore framing errors for 0xfa received bytes
PS2 UART: Log write and received bytes only in DBG
PS2 Mouse: Remove kconfig settings that are handled by input-config
PS2 Mouse: Moved sampling rate and press to select from kconfig to device tree
PS2 Mouse: Log when settings were set successfully
PS2 Mouse: Added devicetree settings for TP settings
Correct val6 default value
PS2 Mouse: Enable settings restore again
PS2 Mouse: Don’t restore runtime settings if device config is present
Mouse Settings: Added log and reset behavior
Adjust settings log
PS2 Mouse: Fix wrong type for tp press to select var
PS2 Mouse: Replace kconfig enable clicking with devicetree disable clicking
PS2 Mouse: Disable error mitigations by default
PS2 Mouse: Moved axis options from kconfig to devicetree
PS2 Mouse: Removed scroll mode behavior
PS2 Mouse: Move scroll mode from kconfig to devicetree
PS2 Mouse: Removed movement buffer / queue
Remove butter kconfigs
PS2 Mouse: Moved mouse_ps2 driver to module
PS2 Mouse: Convert clicking to zephyr 3.5 input system
Mouse Auto Layer: Removed it in preparation for zephyr 3.5 module
PS2 UART: Fix Linking error due to missing config options if logging is disabled
PS2 UART: Replace data queue fifo with message queue to get rid of heap use
PS2 GPIO: Replace data queue fifo with message queue to get rid of heap use
Input Listener: Added logging
Auto Layer Toggle: Added initial version
Auto Layer Toggle: Changed how config is retrieved
Auto Layer Toggle: Adjust logging
Auto Layer Toggle: Remove dependency on urob’s “Zen display & battery tweaks”
That commit adds an additional parameter to the `zmk_keymap_layer_activate` function.
Need to find a better way to make the code compatible whether urob is used or not.
5796aa0104
PS2 GPIO & UART: Apply zmk auto-formatting
PS2 UART, GPIO, Mouse: Make resend callback optional to allow compilation without zephyr fork
PS2 Mouse & Auto Layer Toggle: Add urob compatibility option
PS2 Mouse: Remove unneeded file
1779 lines
58 KiB
C
1779 lines
58 KiB
C
/*
|
|
* Copyright (c) 2019 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT zmk_input_mouse_ps2
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/devicetree.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/drivers/ps2.h>
|
|
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
|
#include <zephyr/input/input.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/settings/settings.h>
|
|
#include <zephyr/sys/util.h>
|
|
|
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|
|
|
/*
|
|
* Settings
|
|
*/
|
|
|
|
// Delay mouse init to give the mouse time to send the init sequence.
|
|
#define ZMK_MOUSE_PS2_INIT_THREAD_DELAY_MS 1000
|
|
|
|
// How often the driver try to initialize a mouse before we give up.
|
|
#define MOUSE_PS2_INIT_ATTEMPTS 10
|
|
|
|
// Mouse activity packets are at least three bytes.
|
|
// This defines how much time between bytes can pass before
|
|
// we give up on the packet and start fresh.
|
|
#define MOUSE_PS2_TIMEOUT_ACTIVITY_PACKET K_MSEC(500)
|
|
|
|
/*
|
|
* PS/2 Defines
|
|
*/
|
|
|
|
// According to the `IBM TrackPoint System Version 4.0 Engineering
|
|
// Specification`...
|
|
// "The POR shall be timed to occur 600 ms ± 20 % from the time power is
|
|
// applied to the TrackPoint controller."
|
|
#define MOUSE_PS2_POWER_ON_RESET_TIME K_MSEC(600)
|
|
|
|
// Common PS/2 Mouse commands
|
|
#define MOUSE_PS2_CMD_GET_SECONDARY_ID "\xe1"
|
|
#define MOUSE_PS2_CMD_GET_SECONDARY_ID_RESP_LEN 2
|
|
|
|
#define MOUSE_PS2_CMD_GET_DEVICE_ID "\xf2"
|
|
#define MOUSE_PS2_CMD_GET_DEVICE_ID_RESP_LEN 1
|
|
|
|
#define MOUSE_PS2_CMD_SET_SAMPLING_RATE "\xf3"
|
|
#define MOUSE_PS2_CMD_SET_SAMPLING_RATE_RESP_LEN 0
|
|
#define MOUSE_PS2_CMD_SET_SAMPLING_RATE_DEFAULT 100
|
|
|
|
#define MOUSE_PS2_CMD_ENABLE_REPORTING "\xf4"
|
|
#define MOUSE_PS2_CMD_ENABLE_REPORTING_RESP_LEN 0
|
|
|
|
#define MOUSE_PS2_CMD_DISABLE_REPORTING "\xf5"
|
|
#define MOUSE_PS2_CMD_DISABLE_REPORTING_RESP_LEN 0
|
|
|
|
#define MOUSE_PS2_CMD_RESEND "\xfe"
|
|
#define MOUSE_PS2_CMD_RESEND_RESP_LEN 0
|
|
|
|
#define MOUSE_PS2_CMD_RESET "\xff"
|
|
#define MOUSE_PS2_CMD_RESET_RESP_LEN 0
|
|
|
|
// Trackpoint Commands
|
|
// They can be found in the `IBM TrackPoint System Version 4.0 Engineering
|
|
// Specification` (YKT3Eext.pdf)...
|
|
|
|
#define MOUSE_PS2_CMD_TP_GET_CONFIG_BYTE "\xe2\x80\x2c"
|
|
#define MOUSE_PS2_CMD_TP_GET_CONFIG_BYTE_RESP_LEN 1
|
|
|
|
#define MOUSE_PS2_CMD_TP_SET_CONFIG_BYTE "\xe2\x81\x2c"
|
|
#define MOUSE_PS2_CMD_TP_SET_CONFIG_BYTE_RESP_LEN 0
|
|
|
|
#define MOUSE_PS2_ST_TP_SENSITIVITY "tp_sensitivity"
|
|
#define MOUSE_PS2_CMD_TP_GET_SENSITIVITY "\xe2\x80\x4a"
|
|
#define MOUSE_PS2_CMD_TP_GET_SENSITIVITY_RESP_LEN 1
|
|
|
|
#define MOUSE_PS2_CMD_TP_SET_SENSITIVITY "\xe2\x81\x4a"
|
|
#define MOUSE_PS2_CMD_TP_SET_SENSITIVITY_RESP_LEN 0
|
|
#define MOUSE_PS2_CMD_TP_SET_SENSITIVITY_MIN 0
|
|
#define MOUSE_PS2_CMD_TP_SET_SENSITIVITY_MAX 255
|
|
#define MOUSE_PS2_CMD_TP_SET_SENSITIVITY_DEFAULT 128
|
|
|
|
#define MOUSE_PS2_ST_TP_NEG_INERTIA "tp_neg_inertia"
|
|
#define MOUSE_PS2_CMD_TP_GET_NEG_INERTIA "\xe2\x80\x4d"
|
|
#define MOUSE_PS2_CMD_TP_GET_NEG_INERTIA_RESP_LEN 1
|
|
|
|
#define MOUSE_PS2_CMD_TP_SET_NEG_INERTIA "\xe2\x81\x4d"
|
|
#define MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_RESP_LEN 0
|
|
#define MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_MIN 0
|
|
#define MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_MAX 255
|
|
#define MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_DEFAULT 0x06
|
|
|
|
#define MOUSE_PS2_ST_TP_VALUE6 "tp_value6"
|
|
#define MOUSE_PS2_CMD_TP_GET_VALUE6_UPPER_PLATEAU_SPEED "\xe2\x80\x60"
|
|
#define MOUSE_PS2_CMD_TP_GET_VALUE6_UPPER_PLATEAU_SPEED_RESP_LEN 1
|
|
|
|
#define MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED "\xe2\x81\x60"
|
|
#define MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_RESP_LEN 0
|
|
#define MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_MIN 0
|
|
#define MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_MAX 255
|
|
#define MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_DEFAULT 0x61
|
|
|
|
#define MOUSE_PS2_ST_TP_PTS_THRESHOLD "tp_pts_threshold"
|
|
#define MOUSE_PS2_CMD_TP_GET_PTS_THRESHOLD "\xe2\x80\x5c"
|
|
#define MOUSE_PS2_CMD_TP_GET_PTS_THRESHOLD_RESP_LEN 1
|
|
|
|
#define MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD "\xe2\x81\x5c"
|
|
#define MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_RESP_LEN 0
|
|
#define MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_MIN 0
|
|
#define MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_MAX 255
|
|
#define MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_DEFAULT 0x08
|
|
|
|
// Trackpoint Config Bits
|
|
#define MOUSE_PS2_TP_CONFIG_BIT_PRESS_TO_SELECT 0x00
|
|
#define MOUSE_PS2_TP_CONFIG_BIT_RESERVED 0x01
|
|
#define MOUSE_PS2_TP_CONFIG_BIT_BUTTON2 0x02
|
|
#define MOUSE_PS2_TP_CONFIG_BIT_INVERT_X 0x03
|
|
#define MOUSE_PS2_TP_CONFIG_BIT_INVERT_Y 0x04
|
|
#define MOUSE_PS2_TP_CONFIG_BIT_INVERT_Z 0x05
|
|
#define MOUSE_PS2_TP_CONFIG_BIT_SWAP_XY 0x06
|
|
#define MOUSE_PS2_TP_CONFIG_BIT_FORCE_TRANSPARENT 0x07
|
|
|
|
// Responses
|
|
#define MOUSE_PS2_RESP_SELF_TEST_PASS 0xaa
|
|
#define MOUSE_PS2_RESP_SELF_TEST_FAIL 0xfc
|
|
|
|
/*
|
|
* ZMK Defines
|
|
*/
|
|
|
|
#define MOUSE_PS2_BUTTON_L_IDX 0
|
|
#define MOUSE_PS2_BUTTON_R_IDX 1
|
|
#define MOUSE_PS2_BUTTON_M_IDX 3
|
|
|
|
#define MOUSE_PS2_THREAD_STACK_SIZE 1024
|
|
#define MOUSE_PS2_THREAD_PRIORITY 10
|
|
|
|
/*
|
|
* Global Variables
|
|
*/
|
|
|
|
#define MOUSE_PS2_SETTINGS_SUBTREE "mouse_ps2"
|
|
|
|
typedef enum {
|
|
MOUSE_PS2_PACKET_MODE_PS2_DEFAULT,
|
|
MOUSE_PS2_PACKET_MODE_SCROLL,
|
|
} zmk_mouse_ps2_packet_mode;
|
|
|
|
struct zmk_mouse_ps2_config {
|
|
const struct device *ps2_device;
|
|
struct gpio_dt_spec rst_gpio;
|
|
|
|
bool scroll_mode;
|
|
bool disable_clicking;
|
|
int sampling_rate;
|
|
|
|
bool tp_press_to_select;
|
|
int tp_press_to_select_threshold;
|
|
int tp_sensitivity;
|
|
int tp_neg_inertia;
|
|
int tp_val6_upper_speed;
|
|
bool tp_x_invert;
|
|
bool tp_y_invert;
|
|
bool tp_xy_swap;
|
|
};
|
|
|
|
struct zmk_mouse_ps2_packet {
|
|
int16_t mov_x;
|
|
int16_t mov_y;
|
|
int8_t scroll;
|
|
bool overflow_x;
|
|
bool overflow_y;
|
|
bool button_l;
|
|
bool button_m;
|
|
bool button_r;
|
|
};
|
|
|
|
struct zmk_mouse_ps2_data {
|
|
const struct device *dev;
|
|
struct gpio_dt_spec rst_gpio; /* GPIO used for Power-On-Reset line */
|
|
|
|
K_THREAD_STACK_MEMBER(thread_stack, MOUSE_PS2_THREAD_STACK_SIZE);
|
|
struct k_thread thread;
|
|
|
|
zmk_mouse_ps2_packet_mode packet_mode;
|
|
uint8_t packet_buffer[4];
|
|
int packet_idx;
|
|
struct zmk_mouse_ps2_packet prev_packet;
|
|
struct k_work_delayable packet_buffer_timeout;
|
|
|
|
bool button_l_is_held;
|
|
bool button_m_is_held;
|
|
bool button_r_is_held;
|
|
|
|
bool activity_reporting_on;
|
|
bool is_trackpoint;
|
|
|
|
uint8_t sampling_rate;
|
|
uint8_t tp_sensitivity;
|
|
uint8_t tp_neg_inertia;
|
|
uint8_t tp_value6;
|
|
uint8_t tp_pts_threshold;
|
|
};
|
|
|
|
static const struct zmk_mouse_ps2_config zmk_mouse_ps2_config = {
|
|
.ps2_device = DEVICE_DT_GET(DT_INST_PHANDLE(0, ps2_device)),
|
|
|
|
#if DT_INST_NODE_HAS_PROP(0, rst_gpios)
|
|
.rst_gpio = GPIO_DT_SPEC_INST_GET(0, rst_gpios),
|
|
#else
|
|
.rst_gpio =
|
|
{
|
|
.port = NULL,
|
|
.pin = 0,
|
|
.dt_flags = 0,
|
|
},
|
|
#endif
|
|
|
|
.scroll_mode = DT_INST_PROP_OR(0, scroll_mode, false),
|
|
.disable_clicking = DT_INST_PROP_OR(0, disable_clicking, false),
|
|
.sampling_rate = DT_INST_PROP_OR(0, sampling_rate, MOUSE_PS2_CMD_SET_SAMPLING_RATE_DEFAULT),
|
|
.tp_press_to_select = DT_INST_PROP_OR(0, tp_press_to_select, false),
|
|
.tp_press_to_select_threshold = DT_INST_PROP_OR(0, tp_press_to_select_threshold, -1),
|
|
.tp_sensitivity = DT_INST_PROP_OR(0, tp_sensitivity, -1),
|
|
.tp_neg_inertia = DT_INST_PROP_OR(0, tp_neg_inertia, -1),
|
|
.tp_val6_upper_speed = DT_INST_PROP_OR(0, tp_val6_upper_speed, -1),
|
|
.tp_x_invert = DT_INST_PROP_OR(0, tp_x_invert, false),
|
|
.tp_y_invert = DT_INST_PROP_OR(0, tp_y_invert, false),
|
|
.tp_xy_swap = DT_INST_PROP_OR(0, tp_xy_swap, false),
|
|
};
|
|
|
|
static struct zmk_mouse_ps2_data zmk_mouse_ps2_data = {
|
|
.packet_mode = MOUSE_PS2_PACKET_MODE_PS2_DEFAULT,
|
|
.packet_idx = 0,
|
|
.prev_packet =
|
|
{
|
|
.button_l = false,
|
|
.button_r = false,
|
|
.button_m = false,
|
|
.overflow_x = 0,
|
|
.overflow_y = 0,
|
|
.mov_x = 0,
|
|
.mov_y = 0,
|
|
.scroll = 0,
|
|
},
|
|
|
|
.button_l_is_held = false,
|
|
.button_m_is_held = false,
|
|
.button_r_is_held = false,
|
|
|
|
// Data reporting is disabled on init
|
|
.activity_reporting_on = false,
|
|
.is_trackpoint = false,
|
|
|
|
// PS2 devices initialize with this rate
|
|
.sampling_rate = MOUSE_PS2_CMD_SET_SAMPLING_RATE_DEFAULT,
|
|
.tp_sensitivity = MOUSE_PS2_CMD_TP_SET_SENSITIVITY_DEFAULT,
|
|
.tp_neg_inertia = MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_DEFAULT,
|
|
.tp_value6 = MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_DEFAULT,
|
|
.tp_pts_threshold = MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_DEFAULT,
|
|
};
|
|
|
|
static int allowed_sampling_rates[] = {
|
|
10, 20, 40, 60, 80, 100, 200,
|
|
};
|
|
|
|
/*
|
|
* Function Definitions
|
|
*/
|
|
|
|
int zmk_mouse_ps2_settings_save();
|
|
|
|
/*
|
|
* Helpers
|
|
*/
|
|
|
|
#define MOUSE_PS2_GET_BIT(data, bit_pos) ((data >> bit_pos) & 0x1)
|
|
#define MOUSE_PS2_SET_BIT(data, bit_val, bit_pos) (data |= (bit_val) << bit_pos)
|
|
|
|
/*
|
|
* Mouse Activity Packet Reading
|
|
*/
|
|
|
|
void zmk_mouse_ps2_activity_process_cmd(zmk_mouse_ps2_packet_mode packet_mode, uint8_t packet_state,
|
|
uint8_t packet_x, uint8_t packet_y, uint8_t packet_extra);
|
|
void zmk_mouse_ps2_activity_abort_cmd();
|
|
void zmk_mouse_ps2_activity_move_mouse(int16_t mov_x, int16_t mov_y);
|
|
void zmk_mouse_ps2_activity_scroll(int8_t scroll_y);
|
|
void zmk_mouse_ps2_activity_click_buttons(bool button_l, bool button_m, bool button_r);
|
|
void zmk_mouse_ps2_activity_reset_packet_buffer();
|
|
struct zmk_mouse_ps2_packet
|
|
zmk_mouse_ps2_activity_parse_packet_buffer(zmk_mouse_ps2_packet_mode packet_mode,
|
|
uint8_t packet_state, uint8_t packet_x, uint8_t packet_y,
|
|
uint8_t packet_extra);
|
|
void zmk_mouse_ps2_activity_toggle_layer();
|
|
|
|
// Called by the PS/2 driver whenver the mouse sends a byte and
|
|
// reporting is enabled through `zmk_mouse_ps2_activity_reporting_enable`.
|
|
void zmk_mouse_ps2_activity_callback(const struct device *ps2_device, uint8_t byte) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
k_work_cancel_delayable(&data->packet_buffer_timeout);
|
|
|
|
// LOG_DBG("Received mouse movement data: 0x%x", byte);
|
|
|
|
data->packet_buffer[data->packet_idx] = byte;
|
|
|
|
if (data->packet_idx == 0) {
|
|
|
|
// Bit 3 of the first command byte should always be 1
|
|
// If it is not, then we are definitely out of alignment.
|
|
// So we ask the device to resend the entire 3-byte command
|
|
// again.
|
|
int alignment_bit = MOUSE_PS2_GET_BIT(byte, 3);
|
|
if (alignment_bit != 1) {
|
|
|
|
zmk_mouse_ps2_activity_abort_cmd("Bit 3 of packet is 0 instead of 1");
|
|
return;
|
|
}
|
|
} else if (data->packet_idx == 1) {
|
|
// Do nothing
|
|
} else if ((data->packet_mode == MOUSE_PS2_PACKET_MODE_PS2_DEFAULT && data->packet_idx == 2) ||
|
|
(data->packet_mode == MOUSE_PS2_PACKET_MODE_SCROLL && data->packet_idx == 3)) {
|
|
|
|
zmk_mouse_ps2_activity_process_cmd(data->packet_mode, data->packet_buffer[0],
|
|
data->packet_buffer[1], data->packet_buffer[2],
|
|
data->packet_buffer[3]);
|
|
zmk_mouse_ps2_activity_reset_packet_buffer();
|
|
return;
|
|
}
|
|
|
|
data->packet_idx += 1;
|
|
|
|
k_work_schedule(&data->packet_buffer_timeout, MOUSE_PS2_TIMEOUT_ACTIVITY_PACKET);
|
|
}
|
|
|
|
void zmk_mouse_ps2_activity_abort_cmd(char *reason) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
const struct zmk_mouse_ps2_config *config = &zmk_mouse_ps2_config;
|
|
const struct device *ps2_device = config->ps2_device;
|
|
|
|
LOG_ERR("PS/2 Mouse cmd buffer is out of aligment. Requesting resend: %s", reason);
|
|
|
|
data->packet_idx = 0;
|
|
ps2_write(ps2_device, MOUSE_PS2_CMD_RESEND[0]);
|
|
|
|
zmk_mouse_ps2_activity_reset_packet_buffer();
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_ZMK_INPUT_MOUSE_PS2_ENABLE_PS2_RESEND_CALLBACK)
|
|
|
|
// Called if the PS/2 driver encounters a transmission error and asks the
|
|
// device to resend the packet.
|
|
// The device will resend all bytes of the packet. So we need to reset our
|
|
// buffer.
|
|
void zmk_mouse_ps2_activity_resend_callback(const struct device *ps2_device) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
LOG_WRN("Mouse movement cmd had transmission error on idx=%d", data->packet_idx);
|
|
|
|
zmk_mouse_ps2_activity_reset_packet_buffer();
|
|
}
|
|
|
|
#endif /* IS_ENABLED(CONFIG_ZMK_INPUT_MOUSE_PS2_ENABLE_PS2_RESEND_CALLBACK) */
|
|
|
|
// Called if no new byte arrives within
|
|
// MOUSE_PS2_TIMEOUT_ACTIVITY_PACKET
|
|
void zmk_mouse_ps2_activity_packet_timout(struct k_work *item) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
LOG_DBG("Mouse movement cmd timed out on idx=%d", data->packet_idx);
|
|
|
|
// Reset the cmd buffer in case we are out of alignment.
|
|
// This way if the mouse ever gets out of alignment, the user
|
|
// can reset it by just not moving it for a second.
|
|
zmk_mouse_ps2_activity_reset_packet_buffer();
|
|
}
|
|
|
|
void zmk_mouse_ps2_activity_reset_packet_buffer() {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
data->packet_idx = 0;
|
|
memset(data->packet_buffer, 0x0, sizeof(data->packet_buffer));
|
|
}
|
|
|
|
void zmk_mouse_ps2_activity_process_cmd(zmk_mouse_ps2_packet_mode packet_mode, uint8_t packet_state,
|
|
uint8_t packet_x, uint8_t packet_y, uint8_t packet_extra) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
struct zmk_mouse_ps2_packet packet;
|
|
packet = zmk_mouse_ps2_activity_parse_packet_buffer(packet_mode, packet_state, packet_x,
|
|
packet_y, packet_extra);
|
|
|
|
int x_delta = abs(data->prev_packet.mov_x - packet.mov_x);
|
|
int y_delta = abs(data->prev_packet.mov_y - packet.mov_y);
|
|
|
|
LOG_DBG("Got mouse activity cmd "
|
|
"(mov_x=%d, mov_y=%d, o_x=%d, o_y=%d, scroll=%d, "
|
|
"b_l=%d, b_m=%d, b_r=%d) and ("
|
|
"x_delta=%d, y_delta=%d)",
|
|
packet.mov_x, packet.mov_y, packet.overflow_x, packet.overflow_y, packet.scroll,
|
|
packet.button_l, packet.button_m, packet.button_r, x_delta, y_delta);
|
|
|
|
#if IS_ENABLED(CONFIG_ZMK_INPUT_MOUSE_PS2_ENABLE_ERROR_MITIGATION)
|
|
if (packet.overflow_x == 1 && packet.overflow_y == 1) {
|
|
LOG_WRN("Detected overflow in both x and y. "
|
|
"Probably mistransmission. Aborting...");
|
|
|
|
zmk_mouse_ps2_activity_abort_cmd("Overflow in both x and y");
|
|
return;
|
|
}
|
|
|
|
// If the mouse exceeds the allowed threshold of movement, it's probably
|
|
// a mistransmission or misalignment.
|
|
// But we only do this check if there was prior movement that wasn't
|
|
// reset in `zmk_mouse_ps2_activity_packet_timout`.
|
|
if ((packet.mov_x != 0 && packet.mov_y != 0) && (x_delta > 150 || y_delta > 150)) {
|
|
LOG_WRN("Detected malformed packet with "
|
|
"(mov_x=%d, mov_y=%d, o_x=%d, o_y=%d, scroll=%d, "
|
|
"b_l=%d, b_m=%d, b_r=%d) and ("
|
|
"x_delta=%d, y_delta=%d)",
|
|
packet.mov_x, packet.mov_y, packet.overflow_x, packet.overflow_y, packet.scroll,
|
|
packet.button_l, packet.button_m, packet.button_r, x_delta, y_delta);
|
|
zmk_mouse_ps2_activity_abort_cmd("Exceeds movement threshold.");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
zmk_mouse_ps2_activity_move_mouse(packet.mov_x, packet.mov_y);
|
|
zmk_mouse_ps2_activity_click_buttons(packet.button_l, packet.button_m, packet.button_r);
|
|
|
|
data->prev_packet = packet;
|
|
}
|
|
|
|
struct zmk_mouse_ps2_packet
|
|
zmk_mouse_ps2_activity_parse_packet_buffer(zmk_mouse_ps2_packet_mode packet_mode,
|
|
uint8_t packet_state, uint8_t packet_x, uint8_t packet_y,
|
|
uint8_t packet_extra) {
|
|
struct zmk_mouse_ps2_packet packet;
|
|
|
|
packet.button_l = MOUSE_PS2_GET_BIT(packet_state, 0);
|
|
packet.button_r = MOUSE_PS2_GET_BIT(packet_state, 1);
|
|
packet.button_m = MOUSE_PS2_GET_BIT(packet_state, 2);
|
|
packet.overflow_x = MOUSE_PS2_GET_BIT(packet_state, 6);
|
|
packet.overflow_y = MOUSE_PS2_GET_BIT(packet_state, 7);
|
|
packet.scroll = 0;
|
|
|
|
// The coordinates are delivered as a signed 9bit integers.
|
|
// But a PS/2 packet is only 8 bits, so the most significant
|
|
// bit with the sign is stored inside the state packet.
|
|
//
|
|
// Since we are converting the uint8_t into a int16_t
|
|
// we must pad the unused most significant bits with
|
|
// the sign bit.
|
|
//
|
|
// Example:
|
|
// ↓ x sign bit
|
|
// - State: 0x18 ( 0001 1000)
|
|
// ↑ y sign bit
|
|
// - X: 0xfd ( 1111 1101) / decimal 253
|
|
// - New X: (1111 1111 1111 1101) / decimal -3
|
|
//
|
|
// - Y: 0x02 ( 0000 0010) / decimal 2
|
|
// - New Y: (0000 0000 0000 0010) / decimal 2
|
|
//
|
|
// The code below creates a signed int and is from...
|
|
// https://wiki.osdev.org/PS/2_Mouse
|
|
packet.mov_x = packet_x - ((packet_state << 4) & 0x100);
|
|
packet.mov_y = packet_y - ((packet_state << 3) & 0x100);
|
|
|
|
// If packet mode scroll or scroll+5 buttons is used,
|
|
// then the first 4 bit of the extra byte are used for the
|
|
// scroll wheel. It is a signed number with the rango of
|
|
// -8 to +7.
|
|
if (packet_mode == MOUSE_PS2_PACKET_MODE_SCROLL) {
|
|
MOUSE_PS2_SET_BIT(packet.scroll, MOUSE_PS2_GET_BIT(packet_extra, 0), 0);
|
|
MOUSE_PS2_SET_BIT(packet.scroll, MOUSE_PS2_GET_BIT(packet_extra, 1), 1);
|
|
MOUSE_PS2_SET_BIT(packet.scroll, MOUSE_PS2_GET_BIT(packet_extra, 2), 2);
|
|
packet.scroll = packet_extra - ((packet.scroll << 3) & 0x100);
|
|
}
|
|
|
|
return packet;
|
|
}
|
|
|
|
/*
|
|
* Mouse Moving and Clicking
|
|
*/
|
|
|
|
static bool zmk_mouse_ps2_is_non_zero_1d_movement(int16_t speed) { return speed != 0; }
|
|
|
|
void zmk_mouse_ps2_activity_move_mouse(int16_t mov_x, int16_t mov_y) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
int ret = 0;
|
|
|
|
bool have_x = zmk_mouse_ps2_is_non_zero_1d_movement(mov_x);
|
|
bool have_y = zmk_mouse_ps2_is_non_zero_1d_movement(mov_y);
|
|
|
|
if (have_x) {
|
|
ret = input_report_rel(data->dev, INPUT_REL_X, mov_x, !have_y, K_NO_WAIT);
|
|
}
|
|
if (have_y) {
|
|
ret = input_report_rel(data->dev, INPUT_REL_Y, mov_y, true, K_NO_WAIT);
|
|
}
|
|
}
|
|
|
|
void zmk_mouse_ps2_activity_click_buttons(bool button_l, bool button_m, bool button_r) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
const struct zmk_mouse_ps2_config *config = &zmk_mouse_ps2_config;
|
|
|
|
// TODO: Integrate this with the proper button mask instead
|
|
// of hardcoding the mouse button indeces.
|
|
// Check hid.c and zmk_hid_mouse_buttons_press() for more info.
|
|
|
|
int buttons_pressed = 0;
|
|
int buttons_released = 0;
|
|
|
|
// First we check which mouse button press states have changed
|
|
bool button_l_pressed = false;
|
|
bool button_l_released = false;
|
|
if (button_l == true && data->button_l_is_held == false) {
|
|
LOG_INF("Pressed button_l");
|
|
|
|
button_l_pressed = true;
|
|
buttons_pressed++;
|
|
} else if (button_l == false && data->button_l_is_held == true) {
|
|
LOG_INF("Releasing button_l");
|
|
|
|
button_l_released = true;
|
|
buttons_released++;
|
|
}
|
|
|
|
bool button_m_released = false;
|
|
bool button_m_pressed = false;
|
|
if (button_m == true && data->button_m_is_held == false) {
|
|
LOG_INF("Pressing button_m");
|
|
|
|
button_m_pressed = true;
|
|
buttons_pressed++;
|
|
} else if (button_m == false && data->button_m_is_held == true) {
|
|
LOG_INF("Releasing button_m");
|
|
|
|
button_m_released = true;
|
|
buttons_released++;
|
|
}
|
|
|
|
bool button_r_released = false;
|
|
bool button_r_pressed = false;
|
|
if (button_r == true && data->button_r_is_held == false) {
|
|
LOG_INF("Pressing button_r");
|
|
|
|
button_r_pressed = true;
|
|
buttons_pressed++;
|
|
} else if (button_r == false && data->button_r_is_held == true) {
|
|
LOG_INF("Releasing button_r");
|
|
|
|
button_r_released = true;
|
|
buttons_released++;
|
|
}
|
|
|
|
// Then we check if this is likely a transmission error
|
|
if (buttons_pressed > 1 || buttons_released > 1) {
|
|
LOG_WRN("Ignoring button presses: Received %d button presses "
|
|
"and %d button releases in one packet. "
|
|
"Probably tranmission error.",
|
|
buttons_pressed, buttons_released);
|
|
|
|
zmk_mouse_ps2_activity_abort_cmd("Multiple button presses");
|
|
return;
|
|
}
|
|
|
|
if (config->disable_clicking != true) {
|
|
// If it wasn't, we actually send the events.
|
|
if (buttons_pressed > 0 || buttons_released > 0) {
|
|
|
|
int buttons_need_reporting = buttons_pressed + buttons_released;
|
|
|
|
// Left button
|
|
if (button_l_pressed) {
|
|
|
|
input_report_key(data->dev, INPUT_BTN_0, 1,
|
|
buttons_need_reporting == 1 ? true : false, K_FOREVER);
|
|
data->button_l_is_held = true;
|
|
} else if (button_l_released) {
|
|
|
|
input_report_key(data->dev, INPUT_BTN_0, 0,
|
|
buttons_need_reporting == 1 ? true : false, K_FOREVER);
|
|
data->button_l_is_held = false;
|
|
}
|
|
|
|
buttons_need_reporting--;
|
|
|
|
// Right button
|
|
if (button_r_pressed) {
|
|
|
|
input_report_key(data->dev, INPUT_BTN_1, 1,
|
|
buttons_need_reporting == 1 ? true : false, K_FOREVER);
|
|
data->button_r_is_held = true;
|
|
} else if (button_r_released) {
|
|
|
|
input_report_key(data->dev, INPUT_BTN_1, 0,
|
|
buttons_need_reporting == 1 ? true : false, K_FOREVER);
|
|
data->button_r_is_held = false;
|
|
}
|
|
|
|
buttons_need_reporting--;
|
|
|
|
// Middle Button
|
|
if (button_m_pressed) {
|
|
|
|
input_report_key(data->dev, INPUT_BTN_2, 1,
|
|
buttons_need_reporting == 1 ? true : false, K_FOREVER);
|
|
data->button_m_is_held = true;
|
|
} else if (button_m_released) {
|
|
|
|
input_report_key(data->dev, INPUT_BTN_2, 0,
|
|
buttons_need_reporting == 1 ? true : false, K_FOREVER);
|
|
data->button_m_is_held = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* PS/2 Command Sending Wrapper
|
|
*/
|
|
int zmk_mouse_ps2_activity_reporting_enable();
|
|
int zmk_mouse_ps2_activity_reporting_disable();
|
|
|
|
struct zmk_mouse_ps2_send_cmd_resp {
|
|
int err;
|
|
char err_msg[80];
|
|
uint8_t resp_buffer[8];
|
|
int resp_len;
|
|
};
|
|
|
|
struct zmk_mouse_ps2_send_cmd_resp zmk_mouse_ps2_send_cmd(char *cmd, int cmd_len, uint8_t *arg,
|
|
int resp_len, bool pause_reporting) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
const struct zmk_mouse_ps2_config *config = &zmk_mouse_ps2_config;
|
|
const struct device *ps2_device = config->ps2_device;
|
|
int err = 0;
|
|
bool prev_activity_reporting_on = data->activity_reporting_on;
|
|
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = {
|
|
.err = 0,
|
|
.err_msg = "",
|
|
.resp_len = 0,
|
|
};
|
|
memset(resp.resp_buffer, 0x0, sizeof(resp.resp_buffer));
|
|
|
|
// Don't send the string termination NULL byte
|
|
int cmd_bytes = cmd_len - 1;
|
|
if (cmd_bytes < 1) {
|
|
err = 1;
|
|
snprintf(resp.err_msg, sizeof(resp.err_msg),
|
|
"Cannot send cmd with less than 1 byte length");
|
|
|
|
return resp;
|
|
}
|
|
|
|
if (resp_len > sizeof(resp.resp_buffer)) {
|
|
err = 2;
|
|
snprintf(resp.err_msg, sizeof(resp.err_msg),
|
|
"Response can't be longer than the resp_buffer (%d)", sizeof(resp.err_msg));
|
|
|
|
return resp;
|
|
}
|
|
|
|
if (pause_reporting == true && data->activity_reporting_on == true) {
|
|
LOG_DBG("Disabling mouse activity reporting...");
|
|
|
|
err = zmk_mouse_ps2_activity_reporting_disable();
|
|
if (err) {
|
|
resp.err = err;
|
|
snprintf(resp.err_msg, sizeof(resp.err_msg), "Could not disable data reporting (%d)",
|
|
err);
|
|
}
|
|
}
|
|
|
|
if (resp.err == 0) {
|
|
LOG_DBG("Sending cmd...");
|
|
|
|
for (int i = 0; i < cmd_bytes; i++) {
|
|
err = ps2_write(ps2_device, cmd[i]);
|
|
if (err) {
|
|
resp.err = err;
|
|
snprintf(resp.err_msg, sizeof(resp.err_msg), "Could not send cmd byte %d/%d (%d)",
|
|
i + 1, cmd_bytes, err);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (resp.err == 0 && arg != NULL) {
|
|
LOG_DBG("Sending arg...");
|
|
err = ps2_write(ps2_device, *arg);
|
|
if (err) {
|
|
resp.err = err;
|
|
snprintf(resp.err_msg, sizeof(resp.err_msg), "Could not send arg (%d)", err);
|
|
}
|
|
}
|
|
|
|
if (resp.err == 0 && resp_len > 0) {
|
|
LOG_DBG("Reading response...");
|
|
for (int i = 0; i < resp_len; i++) {
|
|
err = ps2_read(ps2_device, &resp.resp_buffer[i]);
|
|
if (err) {
|
|
resp.err = err;
|
|
snprintf(resp.err_msg, sizeof(resp.err_msg),
|
|
"Could not read response cmd byte %d/%d (%d)", i + 1, resp_len, err);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pause_reporting == true && prev_activity_reporting_on == true) {
|
|
LOG_DBG("Enabling mouse activity reporting...");
|
|
|
|
err = zmk_mouse_ps2_activity_reporting_enable();
|
|
if (err) {
|
|
// Don' overwrite existing error
|
|
if (resp.err == 0) {
|
|
resp.err = err;
|
|
snprintf(resp.err_msg, sizeof(resp.err_msg),
|
|
"Could not re-enable data reporting (%d)", err);
|
|
}
|
|
}
|
|
}
|
|
|
|
return resp;
|
|
}
|
|
|
|
int zmk_mouse_ps2_activity_reporting_enable() {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
const struct zmk_mouse_ps2_config *config = &zmk_mouse_ps2_config;
|
|
const struct device *ps2_device = config->ps2_device;
|
|
|
|
if (data->activity_reporting_on == true) {
|
|
return 0;
|
|
}
|
|
|
|
uint8_t cmd = MOUSE_PS2_CMD_ENABLE_REPORTING[0];
|
|
int err = ps2_write(ps2_device, cmd);
|
|
if (err) {
|
|
LOG_ERR("Could not enable data reporting: %d", err);
|
|
return err;
|
|
}
|
|
|
|
err = ps2_enable_callback(ps2_device);
|
|
if (err) {
|
|
LOG_ERR("Could not enable ps2 callback: %d", err);
|
|
return err;
|
|
}
|
|
|
|
data->activity_reporting_on = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_activity_reporting_disable() {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
const struct zmk_mouse_ps2_config *config = &zmk_mouse_ps2_config;
|
|
const struct device *ps2_device = config->ps2_device;
|
|
|
|
if (data->activity_reporting_on == false) {
|
|
return 0;
|
|
}
|
|
|
|
uint8_t cmd = MOUSE_PS2_CMD_DISABLE_REPORTING[0];
|
|
int err = ps2_write(ps2_device, cmd);
|
|
if (err) {
|
|
LOG_ERR("Could not disable data reporting: %d", err);
|
|
return err;
|
|
}
|
|
|
|
err = ps2_disable_callback(ps2_device);
|
|
if (err) {
|
|
LOG_ERR("Could not disable ps2 callback: %d", err);
|
|
return err;
|
|
}
|
|
|
|
data->activity_reporting_on = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* PS/2 Command Helpers
|
|
*/
|
|
|
|
int zmk_mouse_ps2_array_get_elem_index(int elem, int *array, size_t array_size) {
|
|
int elem_index = -1;
|
|
for (int i = 0; i < array_size; i++) {
|
|
if (array[i] == elem) {
|
|
elem_index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return elem_index;
|
|
}
|
|
|
|
int zmk_mouse_ps2_array_get_next_elem(int elem, int *array, size_t array_size) {
|
|
int elem_index = zmk_mouse_ps2_array_get_elem_index(elem, array, array_size);
|
|
if (elem_index == -1) {
|
|
return -1;
|
|
}
|
|
|
|
int next_index = elem_index + 1;
|
|
if (next_index >= array_size) {
|
|
return -1;
|
|
}
|
|
|
|
return array[next_index];
|
|
}
|
|
|
|
int zmk_mouse_ps2_array_get_prev_elem(int elem, int *array, size_t array_size) {
|
|
int elem_index = zmk_mouse_ps2_array_get_elem_index(elem, array, array_size);
|
|
if (elem_index == -1) {
|
|
return -1;
|
|
}
|
|
|
|
int prev_index = elem_index - 1;
|
|
if (prev_index < 0 || prev_index >= array_size) {
|
|
return -1;
|
|
}
|
|
|
|
return array[prev_index];
|
|
}
|
|
|
|
/*
|
|
* PS/2 Commands
|
|
*/
|
|
|
|
int zmk_mouse_ps2_reset(const struct device *ps2_device) {
|
|
struct zmk_mouse_ps2_send_cmd_resp resp =
|
|
zmk_mouse_ps2_send_cmd(MOUSE_PS2_CMD_RESET, sizeof(MOUSE_PS2_CMD_RESET), NULL,
|
|
MOUSE_PS2_CMD_RESET_RESP_LEN, false);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not send reset cmd");
|
|
}
|
|
|
|
return resp.err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_get_secondary_id(uint8_t *resp_byte_1, uint8_t *resp_byte_2) {
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_GET_SECONDARY_ID, sizeof(MOUSE_PS2_CMD_GET_SECONDARY_ID), NULL,
|
|
MOUSE_PS2_CMD_GET_SECONDARY_ID_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not get secondary id");
|
|
return resp.err;
|
|
}
|
|
|
|
*resp_byte_1 = resp.resp_buffer[0];
|
|
*resp_byte_2 = resp.resp_buffer[1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_set_sampling_rate(uint8_t sampling_rate) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
int rate_idx = zmk_mouse_ps2_array_get_elem_index(sampling_rate, allowed_sampling_rates,
|
|
sizeof(allowed_sampling_rates));
|
|
if (rate_idx == -1) {
|
|
LOG_ERR("Requested to set illegal sampling rate: %d", sampling_rate);
|
|
return -1;
|
|
}
|
|
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_SET_SAMPLING_RATE, sizeof(MOUSE_PS2_CMD_SET_SAMPLING_RATE), &sampling_rate,
|
|
MOUSE_PS2_CMD_SET_SAMPLING_RATE_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not set sample rate to %d", sampling_rate);
|
|
return resp.err;
|
|
}
|
|
|
|
data->sampling_rate = sampling_rate;
|
|
|
|
LOG_INF("Successfully set sampling rate to %d", sampling_rate);
|
|
|
|
return resp.err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_get_device_id(uint8_t *device_id) {
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_GET_DEVICE_ID, sizeof(MOUSE_PS2_CMD_GET_DEVICE_ID), NULL, 1, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not get device id");
|
|
return resp.err;
|
|
}
|
|
|
|
*device_id = resp.resp_buffer[0];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_set_packet_mode(zmk_mouse_ps2_packet_mode mode) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
if (mode == MOUSE_PS2_PACKET_MODE_PS2_DEFAULT) {
|
|
// Do nothing. Mouse devices enable this by
|
|
// default.
|
|
return 0;
|
|
}
|
|
|
|
bool prev_activity_reporting_on = data->activity_reporting_on;
|
|
zmk_mouse_ps2_activity_reporting_disable();
|
|
|
|
// Setting a mouse mode is a bit like using a cheat code
|
|
// in a video game.
|
|
// You have to send a specific sequence of sampling rates.
|
|
if (mode == MOUSE_PS2_PACKET_MODE_SCROLL) {
|
|
|
|
zmk_mouse_ps2_set_sampling_rate(200);
|
|
zmk_mouse_ps2_set_sampling_rate(100);
|
|
zmk_mouse_ps2_set_sampling_rate(80);
|
|
}
|
|
|
|
// Scroll mouse + 5 buttons mode can be enabled with the
|
|
// following sequence, but since I don't have a mouse to
|
|
// test it, I am commenting it out for now.
|
|
// else if(mode == MOUSE_PS2_PACKET_MODE_SCROLL_5_BUTTONS) {
|
|
|
|
// zmk_mouse_ps2_set_sampling_rate(200);
|
|
// zmk_mouse_ps2_set_sampling_rate(200);
|
|
// zmk_mouse_ps2_set_sampling_rate(80);
|
|
// }
|
|
|
|
uint8_t device_id;
|
|
int err = zmk_mouse_ps2_get_device_id(&device_id);
|
|
if (err) {
|
|
LOG_ERR("Could not enable packet mode %d. Failed to get device id with "
|
|
"error %d",
|
|
mode, err);
|
|
} else {
|
|
if (device_id == 0x00) {
|
|
LOG_ERR("Could not enable packet mode %d. The device does not "
|
|
"support it",
|
|
mode);
|
|
|
|
data->packet_mode = MOUSE_PS2_PACKET_MODE_PS2_DEFAULT;
|
|
err = 1;
|
|
} else if (device_id == 0x03 || device_id == 0x04) {
|
|
LOG_INF("Successfully activated packet mode %d. Mouse returned "
|
|
"device id: %d",
|
|
mode, device_id);
|
|
|
|
data->packet_mode = MOUSE_PS2_PACKET_MODE_SCROLL;
|
|
err = 0;
|
|
}
|
|
// else if(device_id == 0x04) {
|
|
// LOG_INF(
|
|
// "Successfully activated packet mode %d. Mouse returned device "
|
|
// "id: %d", mode, device_id
|
|
// );
|
|
|
|
// data->packet_mode = MOUSE_PS2_PACKET_MODE_SCROLL_5_BUTTONS;
|
|
// err = 0;
|
|
// }
|
|
else {
|
|
LOG_ERR("Could not enable packet mode %d. Received an invalid "
|
|
"device id: %d",
|
|
mode, device_id);
|
|
|
|
data->packet_mode = MOUSE_PS2_PACKET_MODE_PS2_DEFAULT;
|
|
err = 1;
|
|
}
|
|
}
|
|
|
|
// Restore sampling rate to prev value
|
|
zmk_mouse_ps2_set_sampling_rate(data->sampling_rate);
|
|
|
|
if (prev_activity_reporting_on == true) {
|
|
zmk_mouse_ps2_activity_reporting_enable();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* Trackpoint Commands
|
|
*/
|
|
|
|
bool zmk_mouse_ps2_is_device_trackpoint() {
|
|
bool ret = false;
|
|
|
|
uint8_t second_id_1, second_id_2;
|
|
int err = zmk_mouse_ps2_get_secondary_id(&second_id_1, &second_id_2);
|
|
if (err) {
|
|
// Not all devices implement this command.
|
|
ret = false;
|
|
} else {
|
|
if (second_id_1 == 0x1) {
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
LOG_DBG("Connected device is a trackpoint: %d", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_get_config_byte(uint8_t *config_byte) {
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_TP_GET_CONFIG_BYTE, sizeof(MOUSE_PS2_CMD_TP_GET_CONFIG_BYTE), NULL,
|
|
MOUSE_PS2_CMD_TP_GET_CONFIG_BYTE_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not read trackpoint config byte");
|
|
return resp.err;
|
|
}
|
|
|
|
*config_byte = resp.resp_buffer[0];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_set_config_option(int config_bit, bool enabled, char *descr) {
|
|
uint8_t config_byte;
|
|
int err = zmk_mouse_ps2_tp_get_config_byte(&config_byte);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
bool is_enabled = MOUSE_PS2_GET_BIT(config_byte, config_bit);
|
|
|
|
if (is_enabled == enabled) {
|
|
LOG_DBG("Trackpoint %s was already %s... not doing anything.", descr,
|
|
is_enabled ? "enabled" : "disabled");
|
|
return 0;
|
|
}
|
|
|
|
LOG_DBG("Setting trackpoint %s: %s", descr, enabled ? "enabled" : "disabled");
|
|
|
|
MOUSE_PS2_SET_BIT(config_byte, enabled, config_bit);
|
|
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_TP_SET_CONFIG_BYTE, sizeof(MOUSE_PS2_CMD_TP_SET_CONFIG_BYTE), &config_byte,
|
|
MOUSE_PS2_CMD_TP_SET_CONFIG_BYTE_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not set trackpoint %s to %s", descr, enabled ? "enabled" : "disabled");
|
|
return resp.err;
|
|
}
|
|
|
|
LOG_INF("Successfully set config option %s to %s", descr, enabled ? "enabled" : "disabled");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_press_to_select_set(bool enabled) {
|
|
int err = zmk_mouse_ps2_tp_set_config_option(MOUSE_PS2_TP_CONFIG_BIT_PRESS_TO_SELECT, enabled,
|
|
"Press To Select");
|
|
|
|
return err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_invert_x_set(bool enabled) {
|
|
int err =
|
|
zmk_mouse_ps2_tp_set_config_option(MOUSE_PS2_TP_CONFIG_BIT_INVERT_X, enabled, "Invert X");
|
|
|
|
return err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_invert_y_set(bool enabled) {
|
|
int err =
|
|
zmk_mouse_ps2_tp_set_config_option(MOUSE_PS2_TP_CONFIG_BIT_INVERT_Y, enabled, "Invert Y");
|
|
|
|
return err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_swap_xy_set(bool enabled) {
|
|
int err =
|
|
zmk_mouse_ps2_tp_set_config_option(MOUSE_PS2_TP_CONFIG_BIT_SWAP_XY, enabled, "Swap XY");
|
|
|
|
return err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_sensitivity_get(uint8_t *sensitivity) {
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_TP_GET_SENSITIVITY, sizeof(MOUSE_PS2_CMD_TP_GET_SENSITIVITY), NULL,
|
|
MOUSE_PS2_CMD_TP_GET_SENSITIVITY_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not get trackpoint sensitivity");
|
|
return resp.err;
|
|
}
|
|
|
|
// Convert uint8_t to float
|
|
// 0x80 (128) represents 1.0
|
|
uint8_t sensitivity_int = resp.resp_buffer[0];
|
|
*sensitivity = sensitivity_int;
|
|
|
|
LOG_DBG("Trackpoint sensitivity is %d", sensitivity_int);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_sensitivity_set(int sensitivity) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
if (sensitivity < MOUSE_PS2_CMD_TP_SET_SENSITIVITY_MIN ||
|
|
sensitivity > MOUSE_PS2_CMD_TP_SET_SENSITIVITY_MAX) {
|
|
LOG_ERR("Invalid sensitivity value %d. Min: %d; Max: %d", sensitivity,
|
|
MOUSE_PS2_CMD_TP_SET_SENSITIVITY_MIN, MOUSE_PS2_CMD_TP_SET_SENSITIVITY_MAX);
|
|
return 1;
|
|
}
|
|
|
|
uint8_t arg = sensitivity;
|
|
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_TP_SET_SENSITIVITY, sizeof(MOUSE_PS2_CMD_TP_SET_SENSITIVITY), &arg,
|
|
MOUSE_PS2_CMD_TP_SET_SENSITIVITY_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not set sensitivity to %d", sensitivity);
|
|
return resp.err;
|
|
}
|
|
|
|
data->tp_sensitivity = sensitivity;
|
|
|
|
LOG_INF("Successfully set TP sensitivity to %d", sensitivity);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_sensitivity_change(int amount) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
int new_val = data->tp_sensitivity + amount;
|
|
|
|
LOG_INF("Setting trackpoint sensitivity to %d", new_val);
|
|
int err = zmk_mouse_ps2_tp_sensitivity_set(new_val);
|
|
if (err == 0) {
|
|
|
|
zmk_mouse_ps2_settings_save();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_negative_inertia_get(uint8_t *neg_inertia) {
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_TP_GET_NEG_INERTIA, sizeof(MOUSE_PS2_CMD_TP_GET_NEG_INERTIA), NULL,
|
|
MOUSE_PS2_CMD_TP_GET_NEG_INERTIA_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not get trackpoint negative inertia");
|
|
return resp.err;
|
|
}
|
|
|
|
uint8_t neg_inertia_int = resp.resp_buffer[0];
|
|
*neg_inertia = neg_inertia_int;
|
|
|
|
LOG_DBG("Trackpoint negative inertia is %d", neg_inertia_int);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_neg_inertia_set(int neg_inertia) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
if (neg_inertia < MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_MIN ||
|
|
neg_inertia > MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_MAX) {
|
|
LOG_ERR("Invalid negative inertia value %d. Min: %d; Max: %d", neg_inertia,
|
|
MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_MIN, MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_MAX);
|
|
return 1;
|
|
}
|
|
|
|
uint8_t arg = neg_inertia;
|
|
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_TP_SET_NEG_INERTIA, sizeof(MOUSE_PS2_CMD_TP_SET_NEG_INERTIA), &arg,
|
|
MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not set negative inertia to %d", neg_inertia);
|
|
return resp.err;
|
|
}
|
|
|
|
data->tp_neg_inertia = neg_inertia;
|
|
|
|
LOG_INF("Successfully set TP negative inertia to %d", neg_inertia);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_neg_inertia_change(int amount) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
int new_val = data->tp_neg_inertia + amount;
|
|
|
|
LOG_INF("Setting negative inertia to %d", new_val);
|
|
int err = zmk_mouse_ps2_tp_neg_inertia_set(new_val);
|
|
if (err == 0) {
|
|
|
|
zmk_mouse_ps2_settings_save();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_value6_upper_plateau_speed_get(uint8_t *value6) {
|
|
struct zmk_mouse_ps2_send_cmd_resp resp =
|
|
zmk_mouse_ps2_send_cmd(MOUSE_PS2_CMD_TP_GET_VALUE6_UPPER_PLATEAU_SPEED,
|
|
sizeof(MOUSE_PS2_CMD_TP_GET_VALUE6_UPPER_PLATEAU_SPEED), NULL,
|
|
MOUSE_PS2_CMD_TP_GET_VALUE6_UPPER_PLATEAU_SPEED_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not get trackpoint value6 upper plateau speed");
|
|
return resp.err;
|
|
}
|
|
|
|
uint8_t value6_int = resp.resp_buffer[0];
|
|
*value6 = value6_int;
|
|
|
|
LOG_DBG("Trackpoint value6 upper plateau speed is %d", value6_int);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_value6_upper_plateau_speed_set(int value6) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
if (value6 < MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_MIN ||
|
|
value6 > MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_MAX) {
|
|
LOG_ERR("Invalid value6 upper plateau speed value %d. Min: %d; Max: %d", value6,
|
|
MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_MIN,
|
|
MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_MAX);
|
|
return 1;
|
|
}
|
|
|
|
uint8_t arg = value6;
|
|
|
|
struct zmk_mouse_ps2_send_cmd_resp resp =
|
|
zmk_mouse_ps2_send_cmd(MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED,
|
|
sizeof(MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED), &arg,
|
|
MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not set value6 upper plateau speed to %d", value6);
|
|
return resp.err;
|
|
}
|
|
|
|
data->tp_value6 = value6;
|
|
|
|
LOG_INF("Successfully set TP value6 upper plateau speed to %d", value6);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_value6_upper_plateau_speed_change(int amount) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
int new_val = data->tp_value6 + amount;
|
|
|
|
LOG_INF("Setting value6 upper plateau speed to %d", new_val);
|
|
int err = zmk_mouse_ps2_tp_value6_upper_plateau_speed_set(new_val);
|
|
if (err == 0) {
|
|
|
|
zmk_mouse_ps2_settings_save();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_pts_threshold_get(uint8_t *pts_threshold) {
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_TP_GET_PTS_THRESHOLD, sizeof(MOUSE_PS2_CMD_TP_GET_PTS_THRESHOLD), NULL,
|
|
MOUSE_PS2_CMD_TP_GET_PTS_THRESHOLD_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not get trackpoint press-to-select threshold");
|
|
return resp.err;
|
|
}
|
|
|
|
uint8_t pts_threshold_int = resp.resp_buffer[0];
|
|
*pts_threshold = pts_threshold_int;
|
|
|
|
LOG_DBG("Trackpoint press-to-select threshold is %d", pts_threshold_int);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_pts_threshold_set(int pts_threshold) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
if (pts_threshold < MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_MIN ||
|
|
pts_threshold > MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_MAX) {
|
|
LOG_ERR("Invalid press-to-select threshold value %d. Min: %d; Max: %d", pts_threshold,
|
|
MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_MIN, MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_MAX);
|
|
return 1;
|
|
}
|
|
|
|
uint8_t arg = pts_threshold;
|
|
|
|
struct zmk_mouse_ps2_send_cmd_resp resp = zmk_mouse_ps2_send_cmd(
|
|
MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD, sizeof(MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD), &arg,
|
|
MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_RESP_LEN, true);
|
|
if (resp.err) {
|
|
LOG_ERR("Could not set press-to-select threshold to %d", pts_threshold);
|
|
return resp.err;
|
|
}
|
|
|
|
data->tp_pts_threshold = pts_threshold;
|
|
|
|
LOG_INF("Successfully set TP press-to-select threshold to %d", pts_threshold);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_tp_pts_threshold_change(int amount) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
int new_val = data->tp_pts_threshold + amount;
|
|
|
|
LOG_INF("Setting press-to-select threshold to %d", new_val);
|
|
int err = zmk_mouse_ps2_tp_pts_threshold_set(new_val);
|
|
if (err == 0) {
|
|
|
|
zmk_mouse_ps2_settings_save();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* State Saving
|
|
*/
|
|
|
|
#if IS_ENABLED(CONFIG_SETTINGS)
|
|
|
|
struct k_work_delayable zmk_mouse_ps2_save_work;
|
|
|
|
int zmk_mouse_ps2_settings_save_setting(char *setting_name, const void *value, size_t val_len) {
|
|
char setting_path[40];
|
|
snprintf(setting_path, sizeof(setting_path), "%s/%s", MOUSE_PS2_SETTINGS_SUBTREE, setting_name);
|
|
|
|
LOG_DBG("Saving setting to `%s`", setting_path);
|
|
int err = settings_save_one(setting_path, value, val_len);
|
|
if (err) {
|
|
LOG_ERR("Could not save setting to `%s`: %d", setting_path, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int zmk_mouse_ps2_settings_reset_setting(char *setting_name) {
|
|
char setting_path[40];
|
|
snprintf(setting_path, sizeof(setting_path), "%s/%s", MOUSE_PS2_SETTINGS_SUBTREE, setting_name);
|
|
|
|
LOG_DBG("Reseting setting `%s`", setting_path);
|
|
int err = settings_delete(setting_path);
|
|
if (err) {
|
|
LOG_ERR("Could not reset setting `%s`", setting_path);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void zmk_mouse_ps2_settings_save_work(struct k_work *work) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
LOG_DBG("");
|
|
|
|
zmk_mouse_ps2_settings_save_setting(MOUSE_PS2_ST_TP_SENSITIVITY, &data->tp_sensitivity,
|
|
sizeof(data->tp_sensitivity));
|
|
zmk_mouse_ps2_settings_save_setting(MOUSE_PS2_ST_TP_NEG_INERTIA, &data->tp_neg_inertia,
|
|
sizeof(data->tp_neg_inertia));
|
|
zmk_mouse_ps2_settings_save_setting(MOUSE_PS2_ST_TP_VALUE6, &data->tp_value6,
|
|
sizeof(data->tp_value6));
|
|
zmk_mouse_ps2_settings_save_setting(MOUSE_PS2_ST_TP_PTS_THRESHOLD, &data->tp_pts_threshold,
|
|
sizeof(data->tp_pts_threshold));
|
|
}
|
|
#endif
|
|
|
|
int zmk_mouse_ps2_settings_save() {
|
|
LOG_DBG("");
|
|
|
|
#if IS_ENABLED(CONFIG_SETTINGS)
|
|
int ret =
|
|
k_work_reschedule(&zmk_mouse_ps2_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE));
|
|
return MIN(ret, 0);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int zmk_mouse_ps2_settings_reset() {
|
|
|
|
LOG_INF("Deleting runtime settings...");
|
|
zmk_mouse_ps2_settings_reset_setting(MOUSE_PS2_ST_TP_SENSITIVITY);
|
|
zmk_mouse_ps2_settings_reset_setting(MOUSE_PS2_ST_TP_NEG_INERTIA);
|
|
zmk_mouse_ps2_settings_reset_setting(MOUSE_PS2_ST_TP_VALUE6);
|
|
zmk_mouse_ps2_settings_reset_setting(MOUSE_PS2_ST_TP_PTS_THRESHOLD);
|
|
|
|
LOG_INF("Restoring default settings to TP..");
|
|
zmk_mouse_ps2_tp_sensitivity_set(MOUSE_PS2_CMD_TP_SET_SENSITIVITY_DEFAULT);
|
|
|
|
zmk_mouse_ps2_tp_neg_inertia_set(MOUSE_PS2_CMD_TP_SET_NEG_INERTIA_DEFAULT);
|
|
|
|
zmk_mouse_ps2_tp_value6_upper_plateau_speed_set(
|
|
MOUSE_PS2_CMD_TP_SET_VALUE6_UPPER_PLATEAU_SPEED_DEFAULT);
|
|
|
|
zmk_mouse_ps2_tp_pts_threshold_set(MOUSE_PS2_CMD_TP_SET_PTS_THRESHOLD_DEFAULT);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_settings_log() {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
|
|
char settings_str[250];
|
|
|
|
snprintf(settings_str, sizeof(settings_str), " \n\
|
|
&mouse_ps2_conf = { \n\
|
|
tp-sensitivity = <%d>; \n\
|
|
tp-neg-inertia = <%d>; \n\
|
|
tp-val6-upper-speed = <%d>; \n\
|
|
tp-tp-press-to-select-threshold = <%d>; \n\
|
|
}",
|
|
data->tp_sensitivity, data->tp_neg_inertia, data->tp_value6, data->tp_pts_threshold);
|
|
|
|
LOG_INF("Current settings... %s", settings_str);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// This function is called when settings are loaded from flash by
|
|
// `settings_load_subtree`.
|
|
// It's called once for each PS/2 mouse setting that has been stored.
|
|
static int zmk_mouse_ps2_settings_restore(const char *name, size_t len, settings_read_cb read_cb,
|
|
void *cb_arg) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
const struct zmk_mouse_ps2_config *config = &zmk_mouse_ps2_config;
|
|
|
|
uint8_t setting_val;
|
|
|
|
if (len != sizeof(setting_val)) {
|
|
LOG_ERR("Could not restore settings %s: Len mismatch", name);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int rc = read_cb(cb_arg, &setting_val, sizeof(setting_val));
|
|
if (rc <= 0) {
|
|
LOG_ERR("Could not restore setting %s: %d", name, rc);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (data->is_trackpoint == false) {
|
|
LOG_INF("Mouse device is not a trackpoint. Not restoring setting %s.", name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
LOG_INF("Restoring setting %s with value: %d", name, setting_val);
|
|
|
|
if (strcmp(name, MOUSE_PS2_ST_TP_SENSITIVITY) == 0) {
|
|
|
|
if (config->tp_sensitivity != -1) {
|
|
LOG_WRN("Not restoring runtime settings for %s with value %d, because deviceconfig "
|
|
"defines the setting with value %d",
|
|
name, setting_val, config->tp_sensitivity);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return zmk_mouse_ps2_tp_sensitivity_set(setting_val);
|
|
} else if (strcmp(name, MOUSE_PS2_ST_TP_NEG_INERTIA) == 0) {
|
|
if (config->tp_neg_inertia != -1) {
|
|
LOG_WRN("Not restoring runtime settings for %s with value %d, because deviceconfig "
|
|
"defines the setting with value %d",
|
|
name, setting_val, config->tp_neg_inertia);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return zmk_mouse_ps2_tp_neg_inertia_set(setting_val);
|
|
} else if (strcmp(name, MOUSE_PS2_ST_TP_VALUE6) == 0) {
|
|
if (config->tp_val6_upper_speed != -1) {
|
|
LOG_WRN("Not restoring runtime settings for %s with value %d, because deviceconfig "
|
|
"defines the setting with value %d",
|
|
name, setting_val, config->tp_val6_upper_speed);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return zmk_mouse_ps2_tp_value6_upper_plateau_speed_set(setting_val);
|
|
} else if (strcmp(name, MOUSE_PS2_ST_TP_PTS_THRESHOLD) == 0) {
|
|
if (config->tp_press_to_select_threshold != -1) {
|
|
LOG_WRN("Not restoring runtime settings for %s with value %d, because deviceconfig "
|
|
"defines the setting with value %d",
|
|
name, setting_val, config->tp_press_to_select_threshold);
|
|
|
|
return 0;
|
|
}
|
|
|
|
return zmk_mouse_ps2_tp_pts_threshold_set(setting_val);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
struct settings_handler zmk_mouse_ps2_settings_conf = {
|
|
.name = MOUSE_PS2_SETTINGS_SUBTREE,
|
|
.h_set = zmk_mouse_ps2_settings_restore,
|
|
};
|
|
|
|
int zmk_mouse_ps2_settings_init() {
|
|
#if IS_ENABLED(CONFIG_SETTINGS)
|
|
LOG_DBG("");
|
|
|
|
settings_subsys_init();
|
|
|
|
int err = settings_register(&zmk_mouse_ps2_settings_conf);
|
|
if (err) {
|
|
LOG_ERR("Failed to register the PS/2 mouse settings handler (err %d)", err);
|
|
return err;
|
|
}
|
|
|
|
k_work_init_delayable(&zmk_mouse_ps2_save_work, zmk_mouse_ps2_settings_save_work);
|
|
|
|
// This will load the settings and then call
|
|
// `zmk_mouse_ps2_settings_restore`, which will set the settings
|
|
settings_load_subtree(MOUSE_PS2_SETTINGS_SUBTREE);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Init
|
|
*/
|
|
|
|
static void zmk_mouse_ps2_init_thread(int dev_ptr, int unused);
|
|
int zmk_mouse_ps2_init_power_on_reset();
|
|
int zmk_mouse_ps2_init_wait_for_mouse(const struct device *dev);
|
|
|
|
static int zmk_mouse_ps2_init(const struct device *dev) {
|
|
LOG_DBG("Inside zmk_mouse_ps2_init");
|
|
|
|
LOG_DBG("Creating mouse_ps2 init thread.");
|
|
k_thread_create(&zmk_mouse_ps2_data.thread, zmk_mouse_ps2_data.thread_stack,
|
|
MOUSE_PS2_THREAD_STACK_SIZE, (k_thread_entry_t)zmk_mouse_ps2_init_thread,
|
|
(struct device *)dev, 0, NULL, K_PRIO_COOP(MOUSE_PS2_THREAD_PRIORITY), 0,
|
|
K_MSEC(ZMK_MOUSE_PS2_INIT_THREAD_DELAY_MS));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void zmk_mouse_ps2_init_thread(int dev_ptr, int unused) {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
int err;
|
|
|
|
data->dev = INT_TO_POINTER(dev_ptr);
|
|
|
|
const struct zmk_mouse_ps2_config *config = data->dev->config;
|
|
|
|
zmk_mouse_ps2_init_power_on_reset();
|
|
|
|
LOG_INF("Waiting for mouse to connect...");
|
|
err = zmk_mouse_ps2_init_wait_for_mouse(data->dev);
|
|
if (err) {
|
|
LOG_ERR("Could not init a mouse in %d attempts. Giving up. "
|
|
"Power cycle the mouse and reset zmk to try again.",
|
|
MOUSE_PS2_INIT_ATTEMPTS);
|
|
return;
|
|
}
|
|
|
|
if (config->sampling_rate != MOUSE_PS2_CMD_SET_SAMPLING_RATE_DEFAULT) {
|
|
|
|
LOG_INF("Setting sample rate to %d...", config->sampling_rate);
|
|
zmk_mouse_ps2_set_sampling_rate(config->sampling_rate);
|
|
if (err) {
|
|
LOG_ERR("Could not set sampling rate to %d: %d", config->sampling_rate, err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (zmk_mouse_ps2_is_device_trackpoint() == true) {
|
|
LOG_INF("Device is a trackpoint");
|
|
data->is_trackpoint = true;
|
|
|
|
if (config->tp_press_to_select) {
|
|
LOG_INF("Enabling TP press to select...");
|
|
zmk_mouse_ps2_tp_press_to_select_set(true);
|
|
}
|
|
|
|
if (config->tp_press_to_select_threshold != -1) {
|
|
LOG_INF("Setting TP press to select thereshold to %d...",
|
|
config->tp_press_to_select_threshold);
|
|
zmk_mouse_ps2_tp_pts_threshold_set(config->tp_press_to_select_threshold);
|
|
}
|
|
|
|
if (config->tp_sensitivity != -1) {
|
|
LOG_INF("Setting TP sensitivity to %d...", config->tp_sensitivity);
|
|
zmk_mouse_ps2_tp_sensitivity_set(config->tp_sensitivity);
|
|
}
|
|
|
|
if (config->tp_neg_inertia != -1) {
|
|
LOG_INF("Setting TP inertia to %d...", config->tp_neg_inertia);
|
|
zmk_mouse_ps2_tp_neg_inertia_set(config->tp_neg_inertia);
|
|
}
|
|
|
|
if (config->tp_val6_upper_speed != -1) {
|
|
LOG_INF("Setting TP value 6 upper speed plateau to %d...", config->tp_val6_upper_speed);
|
|
zmk_mouse_ps2_tp_value6_upper_plateau_speed_set(config->tp_val6_upper_speed);
|
|
}
|
|
if (config->tp_x_invert) {
|
|
LOG_INF("Inverting trackpoint x axis.");
|
|
zmk_mouse_ps2_tp_invert_x_set(true);
|
|
}
|
|
|
|
if (config->tp_y_invert) {
|
|
LOG_INF("Inverting trackpoint y axis.");
|
|
zmk_mouse_ps2_tp_invert_y_set(true);
|
|
}
|
|
|
|
if (config->tp_xy_swap) {
|
|
LOG_INF("Swapping trackpoint x and y axis.");
|
|
zmk_mouse_ps2_tp_swap_xy_set(true);
|
|
}
|
|
}
|
|
|
|
if (config->scroll_mode) {
|
|
LOG_INF("Enabling scroll mode.");
|
|
zmk_mouse_ps2_set_packet_mode(MOUSE_PS2_PACKET_MODE_SCROLL);
|
|
}
|
|
|
|
zmk_mouse_ps2_settings_init();
|
|
|
|
// Configure read callback
|
|
LOG_DBG("Configuring ps2 callback...");
|
|
#if IS_ENABLED(CONFIG_ZMK_INPUT_MOUSE_PS2_ENABLE_PS2_RESEND_CALLBACK)
|
|
|
|
err = ps2_config(config->ps2_device, &zmk_mouse_ps2_activity_callback,
|
|
&zmk_mouse_ps2_activity_resend_callback);
|
|
|
|
#else
|
|
|
|
err = ps2_config(config->ps2_device, &zmk_mouse_ps2_activity_callback);
|
|
|
|
#endif /* IS_ENABLED(CONFIG_ZMK_INPUT_MOUSE_PS2_ENABLE_PS2_RESEND_CALLBACK) */
|
|
|
|
if (err) {
|
|
LOG_ERR("Could not configure ps2 interface: %d", err);
|
|
return;
|
|
}
|
|
|
|
LOG_INF("Enabling data reporting and ps2 callback...");
|
|
err = zmk_mouse_ps2_activity_reporting_enable();
|
|
if (err) {
|
|
LOG_ERR("Could not activate ps2 callback: %d", err);
|
|
} else {
|
|
LOG_DBG("Successfully activated ps2 callback");
|
|
}
|
|
|
|
k_work_init_delayable(&data->packet_buffer_timeout, zmk_mouse_ps2_activity_packet_timout);
|
|
|
|
return;
|
|
}
|
|
|
|
// Power-On-Reset for trackpoints (and possibly other devices).
|
|
// From the `IBM TrackPoint System Version 4.0 Engineering
|
|
// Specification`...
|
|
// "The TrackPoint logic shall execute a Power On Reset (POR) when power is
|
|
// applied to the device. The POR shall be timed to occur 600 ms ± 20 % from
|
|
// the time power is applied to the TrackPoint controller. Activity on the
|
|
// clock and data lines is ignored prior to the completion of the diagnostic
|
|
// sequence. (See RESET mode of operation.)"
|
|
int zmk_mouse_ps2_init_power_on_reset() {
|
|
struct zmk_mouse_ps2_data *data = &zmk_mouse_ps2_data;
|
|
const struct zmk_mouse_ps2_config *config = &zmk_mouse_ps2_config;
|
|
|
|
// Check if the optional rst-gpios setting was set
|
|
if (config->rst_gpio.port == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
LOG_INF("Performing Power-On-Reset...");
|
|
|
|
if (data->rst_gpio.port == NULL) {
|
|
data->rst_gpio = config->rst_gpio;
|
|
|
|
// Overwrite any user-provided flags from the devicetree
|
|
data->rst_gpio.dt_flags = 0;
|
|
}
|
|
|
|
// Set reset pin low...
|
|
int err = gpio_pin_configure_dt(&data->rst_gpio, (GPIO_OUTPUT_HIGH));
|
|
if (err) {
|
|
LOG_ERR("Failed Power-On-Reset: Failed to configure RST GPIO pin to "
|
|
"output low (err %d)",
|
|
err);
|
|
return err;
|
|
}
|
|
|
|
// Wait 600ms
|
|
k_sleep(MOUSE_PS2_POWER_ON_RESET_TIME);
|
|
|
|
// Set pin high
|
|
err = gpio_pin_set_dt(&data->rst_gpio, 0);
|
|
if (err) {
|
|
LOG_ERR("Failed Power-On-Reset: Failed to set RST GPIO pin to "
|
|
"low (err %d)",
|
|
err);
|
|
return err;
|
|
}
|
|
|
|
LOG_DBG("Finished Power-On-Reset successfully...");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int zmk_mouse_ps2_init_wait_for_mouse(const struct device *dev) {
|
|
const struct zmk_mouse_ps2_config *config = dev->config;
|
|
int err;
|
|
|
|
uint8_t read_val;
|
|
|
|
for (int i = 0; i < MOUSE_PS2_INIT_ATTEMPTS; i++) {
|
|
|
|
LOG_INF("Trying to initialize mouse device (attempt %d / %d)", i + 1,
|
|
MOUSE_PS2_INIT_ATTEMPTS);
|
|
|
|
// PS/2 Devices do a self-test and send the result when they power up.
|
|
|
|
err = ps2_read(config->ps2_device, &read_val);
|
|
if (err == 0) {
|
|
if (read_val != MOUSE_PS2_RESP_SELF_TEST_PASS) {
|
|
LOG_WRN("Got invalid PS/2 self-test result: 0x%x", read_val);
|
|
|
|
LOG_INF("Trying to reset PS2 device...");
|
|
zmk_mouse_ps2_reset(config->ps2_device);
|
|
|
|
continue;
|
|
}
|
|
|
|
LOG_INF("PS/2 Device passed self-test: 0x%x", read_val);
|
|
|
|
// Read device id
|
|
LOG_INF("Reading PS/2 device id...");
|
|
err = ps2_read(config->ps2_device, &read_val);
|
|
if (err) {
|
|
LOG_WRN("Could not read PS/2 device id: %d", err);
|
|
} else {
|
|
if (read_val == 0) {
|
|
LOG_INF("Connected PS/2 device is a mouse...");
|
|
return 0;
|
|
} else {
|
|
LOG_WRN("PS/2 device is not a mouse: 0x%x", read_val);
|
|
return 1;
|
|
}
|
|
}
|
|
} else {
|
|
LOG_WRN("Could not read PS/2 device self-test result: %d. ", err);
|
|
}
|
|
|
|
// But when a zmk device is reset, it doesn't cut the power to external
|
|
// devices. So the device acts as if it was never disconnected.
|
|
// So we try sending the reset command.
|
|
if (i % 2 == 0) {
|
|
LOG_INF("Trying to reset PS2 device...");
|
|
zmk_mouse_ps2_reset(config->ps2_device);
|
|
continue;
|
|
}
|
|
|
|
k_sleep(K_SECONDS(5));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Depends on the UART and PS2 init priorities, which are 55 and 45 by default
|
|
#define ZMK_MOUSE_PS2_INIT_PRIORITY 90
|
|
|
|
DEVICE_DT_INST_DEFINE(0, &zmk_mouse_ps2_init, NULL, &zmk_mouse_ps2_data, &zmk_mouse_ps2_config,
|
|
POST_KERNEL, ZMK_MOUSE_PS2_INIT_PRIORITY, NULL);
|